跳到主要内容

集成 Kubernetes 服务发现

Kubernetes 通过 Endpoints 或 EndpointSlices 跟踪每个 Service 后端已就绪 Pod 的地址。APISIX 可以监听这些资源,直接将流量路由到动态变化的 Pod 地址,而不使用 Service 虚拟 IP 或集群 DNS 名称。

该集成支持单个 Kubernetes 集群或多个命名集群。APISIX 使用 ServiceAccount Bearer 令牌向每个 Kubernetes API 进行身份认证,并向上游提供发现的地址。

前置条件

  • APISIX 部署可以连接 Kubernetes API Server 和发现的 Pod 地址。
  • Kubernetes ServiceAccount 令牌,通过 client.token 直接提供或通过 client.token_file 文件提供。
  • endpoints 资源具有 getlistwatch 权限。当 watch_endpoint_slicestrue 时,还要对 discovery.k8s.io API Group 中的 endpointslices 资源授予相同权限。
  • 启用 TLS 证书校验时,APISIX 可以访问包含 Kubernetes API Server CA 证书的 PEM 证书包。

授予 Kubernetes API 访问权限

以下资源清单会创建 apisix 命名空间并授予服务发现所需的权限。如果 APISIX 在其他命名空间中运行,请替换 Namespace、ServiceAccount 和 ClusterRoleBinding Subject 中的 apisix

kubernetes-discovery-rbac.yaml
apiVersion: v1
kind: Namespace
metadata:
name: apisix
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: apisix-discovery
namespace: apisix
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: apisix-discovery
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch"]
- apiGroups: ["discovery.k8s.io"]
resources: ["endpointslices"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: apisix-discovery
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: apisix-discovery
subjects:
- kind: ServiceAccount
name: apisix-discovery
namespace: apisix

应用 RBAC 资源:

kubectl apply -f kubernetes-discovery-rbac.yaml

当 APISIX 在 Kubernetes 中运行并读取挂载的 ServiceAccount 令牌时,请在 APISIX Deployment 或 StatefulSet 中将 spec.template.spec.serviceAccountName 设置为 apisix-discovery。应用 RBAC 资源清单不会更改现有 Pod 的身份。更新 Pod Spec 后,请滚动更新工作负载。

配置单个 Kubernetes 集群

当 APISIX 在 Pod 中运行时,默认配置会从 Pod 环境读取 Kubernetes API 地址和挂载的 ServiceAccount 令牌:

config.yaml
discovery:
kubernetes: {}

在生产环境中,请显式启用证书校验。创建并挂载一个 PEM 证书包,其中同时包含 Kubernetes API CA 以及 APISIX 已信任的所有系统或自定义 CA。apisix.ssl.ssl_trusted_certificate 是全局配置,因此,如果仅使用 Kubernetes CA 替换现有证书包,可能会破坏其他 TLS 连接。请确保 API Server 主机名与其证书保持一致。

config.yaml
apisix:
ssl:
ssl_trusted_certificate: /usr/local/apisix/conf/trusted-ca-bundle.pem

discovery:
kubernetes:
service:
schema: https
host: ${KUBERNETES_SERVICE_HOST}
port: ${KUBERNETES_SERVICE_PORT}
ssl_verify: true
client:
token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
namespace_selector:
equal: default
watch_endpoint_slices: true

为保持兼容性,service.ssl_verify 默认为 false,即使 service.schemahttps 也是如此。该设置会接受不受信任的 API Server 证书,不建议在生产环境中使用。请将其设置为 true,并通过组合后的信任证书包配置 apisix.ssl.ssl_trusted_certificate

当 APISIX 在 Kubernetes 外部运行时,请显式设置 service.hostservice.port。通过 client.token 或可读的 client.token_file 提供令牌;HTTPS API Server 要求令牌不能为空。

配置多个 Kubernetes 集群

使用数组配置多个集群。每个条目都需要一个唯一 id(由 1–64 个小写字母或数字组成)、显式 API Server 地址和客户端凭证。单集群的 Service 和 Client 字段默认值不会应用到数组条目。

config.yaml
apisix:
ssl:
ssl_trusted_certificate: /usr/local/apisix/conf/trusted-ca-bundle.pem

discovery:
kubernetes:
- id: prod
service:
schema: https
host: prod-api.example.com
port: "6443"
ssl_verify: true
client:
token_file: /usr/local/apisix/conf/prod-service-account.token
namespace_selector:
match:
- ^prod-
watch_endpoint_slices: true
- id: staging
service:
schema: https
host: staging-api.example.com
port: "6443"
ssl_verify: true
client:
token_file: /usr/local/apisix/conf/staging-service-account.token
namespace_selector:
equal: staging
watch_endpoint_slices: true

CA 证书包必须信任每个已配置的 API Server,并保留 APISIX 发起其他 HTTPS 连接时所需的根 CA。更改服务发现或信任配置后,请重新加载 APISIX

部署示例服务

default 命名空间中部署 HTTPBin 工作负载和一个具有命名端口 httpService

httpbin.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: httpbin
template:
metadata:
labels:
app: httpbin
spec:
containers:
- name: httpbin
image: mccutchen/go-httpbin:v2.15.0
ports:
- name: http
containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
namespace: default
spec:
selector:
app: httpbin
ports:
- name: http
port: 80
targetPort: http

应用这些资源,并等待两个 Pod 均进入 Ready 状态:

kubectl apply -f httpbin.yaml
kubectl rollout status deployment/httpbin -n default

路由到发现的服务

对于单个集群,service_name 使用 namespace/service:port-name。对于多个集群,请添加集群 ID 前缀:cluster-id/namespace/service:port-name。如果 Kubernetes 资源定义了命名端口,请使用端口名称而不是端口号。

创建路由,通过 http 端口将请求发送到 default 命名空间中的 httpbin Service

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "kubernetes-discovery-route",
"uri": "/anything",
"upstream": {
"type": "roundrobin",
"discovery_type": "kubernetes",
"service_name": "default/httpbin:http"
}
}'

对于多集群示例中的 prod 条目,请改用 prod/default/httpbin:http

发送请求,验证 APISIX 可以解析就绪端点并代理请求:

curl -i "http://127.0.0.1:9080/anything"

收到 HTTP/1.1 200 OK 响应即表示路由已访问发现的端点。

验证服务发现与 TLS 信任

如果已启用 Control API,请检查服务发现缓存:

curl "http://127.0.0.1:9090/v1/discovery/kubernetes/dump"

确认输出包含预期的命名空间、服务、端口名称和就绪端点地址。使用多个集群时,还要确认输出中包含预期的集群 id

要验证受信任证书的行为:

  1. 配置 service.ssl_verify: true 和正确的 CA 证书包,重新加载 APISIX,并确认服务发现转储中出现预期的端点。
  2. 在隔离的测试部署中,将 apisix.ssl.ssl_trusted_certificate 指向不信任 API Server 的证书包,或使用与证书不匹配的主机。重新加载 APISIX,确认错误日志报告证书校验失败,并且没有收到新的端点更新。重新加载期间,先前发现的条目可能仍保留在共享内存中,因此不要将旧条目仍然存在视为新 TLS 连接成功的证据。
  3. 恢复正确的 CA 证书包和主机,重新加载 APISIX,并确认服务发现恢复正常。

不要将 ssl_verify: false 作为生产环境信任失败的修复方法。请修复 CA 证书包、证书链、API Server 主机名或系统时间。

后续步骤

Kubernetes 服务发现支持命名空间选择器、Kubernetes Label Selector 表达式、可配置的端点权重、共享内存大小,以及 Endpoints 或 EndpointSlices。有关完整的服务发现状态和故障排查端点,请参阅 Control API 参考