How to Proxy External Services in Apache APISIX Ingress Controller
Yeqi Peng
January 13, 2023
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.