How to Proxy External Services in Apache APISIX Ingress Controller

Yeqi Peng

January 13, 2023

Products

In this tutorial, we will introduce how to configure external services in ApisixUpstream resources.

This is useful when your service depends on a third-party service or a service from another K8s cluster. It also provides a way to manage your ApisixRoute with shared ExternalName services, without modifying a large number of ApisixRoutes when the referenced service name is updated.

Prerequisites

  • an available Kubernetes cluster
  • an available APISIX and APISIX Ingress Controller installation

We assume that your APISIX is installed in the apisix namespace.

Introduction

APISIX Ingress supports configuring external services as backends, both for K8s ExternalName services and direct domains.

In this case, we don't configure the backends field in the ApisixRoute resource. Instead, we will use the upstreams field to refer to an ApisixUpstream resource with the externalNodes field configured.

For example:

# httpbin-route.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: httpbin-route
spec:
  http:
  - name: rule1
    match:
      hosts:
      - local.httpbin.org
      paths:
      - /*
    # backends:  # We won't use the `backends` field
    #    - serviceName: httpbin
    #      servicePort: 80
    upstreams:
    - name: httpbin-upstream

This configuration tells the ingress controller not to resolve upstream hosts through the K8s services but to use the configuration defined in the referenced ApisixUpstream.

The referenced ApisixUpstream MUST have the externalNodes field configured. For example:

# httpbin-upstream.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
  name: httpbin-upstream
spec:
  externalNodes:
  - type: Domain
    name: httpbin.org

In this yaml example, we configured httpbin.org as the backend. The type Domain indicates that this is a third-party service, and any domain name is supported here.

If you want to use an external name service in the K8s cluster, the type should be Service and the name should be the service name. By configuring ApisixUpstream with the type Service, the ingress controller will automatically keep track of the content of the ExternalName service and its changes.

External Domain Upstream

apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: httpbin-route
spec:
  http:
  - name: rule1
    match:
      hosts:
      - local.httpbin.org
      paths:
      - /*
    upstreams:
    - name: httpbin-upstream
---
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
  name: httpbin-upstream
spec:
  externalNodes:
  - type: Domain
    name: httpbin.org

After applying the above configuration, we can try to access httpbin.org directly through APISIX.

kubectl exec -it -n apisix APISIX_POD_NAME -- curl -i -H "Host: local.httpbin.org" http://127.0.0.1:9080/get

If everything works, you will see the result like this:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 321
Connection: keep-alive
Date: Thu, 15 Dec 2022 10:47:30 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/3.0.0

{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Host": "local.httpbin.org",
    "User-Agent": "curl/7.29.0",
    "X-Amzn-Trace-Id": "Root=xxxxx",
    "X-Forwarded-Host": "local.httpbin.org"
  },
  "origin": "127.0.0.1, xxxxxxxxx",
  "url": "http://local.httpbin.org/get"
}

The header Server: APISIX/3.0.0 indicates that the request has been sent from APISIX. And the response contains X-Amzn-Trace-Id indicating that the request was handled by the online httpbin service.

External Name Service Upstream

Let's deploy a simple httpbin app in the namespace test as the backend for the external name service we will create later.

kubectl create ns test
kubectl -n test run httpbin --image-pull-policy IfNotPresent --image=kennethreitz/httpbin --port 80
kubectl -n test expose pod/httpbin --port 80

Then use the following configuration to create an ExternalName service in the apisix namespace.

apiVersion: v1
kind: Service
metadata:
  name: ext-httpbin
spec:
  type: ExternalName
  externalName: httpbin.test.svc

Now we can create an ExternalName service ApisixRoute and ApisixUpstream.

apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: ext-route
spec:
  http:
  - name: rule1
    match:
      hosts:
      - ext.httpbin.org
      paths:
      - /*
    upstreams:
    - name: ext-upstream
---
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
  name: ext-upstream
spec:
  externalNodes:
  - type: Service
    name: ext-httpbin

Once the configurations are synced, try to access it with the following command.

The only argument that changes is the header we pass.

kubectl exec -it -n apisix APISIX_POD_NAME -- curl -i -H "Host: ext.httpbin.org" http://127.0.0.1:9080/get

The output should be like:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 234
Connection: keep-alive
Date: Thu, 15 Dec 2022 10:54:21 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/3.0.0

{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Host": "ext.httpbin.org",
    "User-Agent": "curl/7.29.0",
    "X-Forwarded-Host": "ext.httpbin.org"
  },
  "origin": "127.0.0.1",
  "url": "http://ext.httpbin.org/get"
}

We can see the response is similar to the previous one, but some fields don't exist. That's because the request was sent to our local httpbin service rather than the online one.

Domain In External Name Service

The external name service can also hold any domain name outside the K8s cluster.

Let's update the external service configuration we applied in the previous section.

apiVersion: v1
kind: Service
metadata:
  name: ext-httpbin
spec:
  type: ExternalName
  externalName: httpbin.org

Try reaccessing it, and the output should contain multiple origin and an X-Amzn-Trace-Id header, which means we are accessing the online httpbin.org service.

Conclusion

In this tutorial, we extend the functional boundaries of the APISIX Ingress Controller by proxying requests to external services. It allows users to easily integrate third-party services, such as authentication services or cross-cluster services.

For more information about API gateway, please visit our blogs or contact us.

Tags:
APISIX Ingress ControllerKubernetes