目录
在本教程中,我们将使用原生 YAML 在 Kubernetes 中安装 APISIX 和 APISIX Ingress 控制器。
如果没有 Kubernetes 集群使用,建议使用 kind 创建本地 Kubernetes 集群。
x
kubectl create ns apisix在本教程中,我们的所有操作都将在命名空间 apisix 中执行。
在这里,我们将在 Kubernetes 集群内部部署不带认证的单节点 ETCD 集群。
在本例中,我们假设你拥有存储部署器。如果你正在使用 Kind,那么将自动创建本地路径部署器。如果没有存储部署器或不想使用持久化存储卷,那么可以使用 emptyDir 作为存储卷。
x
# etcd-headless.yamlapiVersionv1kindServicemetadata nameetcd-headless namespaceapisix labels app.kubernetes.io/nameetcd annotations service.alpha.kubernetes.io/tolerate-unready-endpoints"true"spec typeClusterIP clusterIPNone portsname"client" port2379 targetPortclientname"peer" port2380 targetPortpeer selector app.kubernetes.io/nameetcd---# etcd.yamlapiVersionapps/v1kindStatefulSetmetadata nameetcd namespaceapisix labels app.kubernetes.io/nameetcdspec selector matchLabels app.kubernetes.io/nameetcd serviceNameetcd-headless podManagementPolicyParallel replicas1 updateStrategy typeRollingUpdate template metadata labels app.kubernetes.io/nameetcd spec securityContext fsGroup1001 runAsUser1001 containersnameetcd imagedocker.io/bitnami/etcd3.4.14-debian-10-r0 imagePullPolicy"IfNotPresent" # command: # - /scripts/setup.sh envnameBITNAMI_DEBUG value"false"nameMY_POD_IP valueFrom fieldRef fieldPathstatus.podIPnameMY_POD_NAME valueFrom fieldRef fieldPathmetadata.namenameETCDCTL_API value"3"nameETCD_NAME value"$(MY_POD_NAME)"nameETCD_DATA_DIR value/etcd/datanameETCD_ADVERTISE_CLIENT_URLS value"http://$(MY_POD_NAME).etcd-headless.apisix.svc.cluster.local:2379"nameETCD_LISTEN_CLIENT_URLS value"http://0.0.0.0:2379"nameETCD_INITIAL_ADVERTISE_PEER_URLS value"http://$(MY_POD_NAME).etcd-headless.apisix.svc.cluster.local:2380"nameETCD_LISTEN_PEER_URLS value"http://0.0.0.0:2380"nameALLOW_NONE_AUTHENTICATION value"yes" portsnameclient containerPort2379namepeer containerPort2380 volumeMountsnamedata mountPath/etcd # If you don't have a storage provisioner or don't want to use persistence volume, you could use an `emptyDir` as follow. # volumes: # - name: data # emptyDir: {} volumeClaimTemplatesmetadata namedata spec accessModes"ReadWriteOnce" resources requests storage"8Gi"将这两个 YAML 文件应用到 Kubernetes,等待几秒,ETCD 安装就成功了。我们可以运行健康检查,进行确认:
x
$ kubectl -n apisix exec -it etcd-0 -- etcdctl endpoint health127.0.0.1:2379 is healthy: successfully committed proposal: took = 1.80916ms请注意该 ETCD 安装非常简单,缺乏许多必要的生产特性,仅用于学习场景。如果想部署生产级 ETCD,请参阅 bitnami/etcd。
为我们的 APISIX 创建配置文件。我们将部署 2.5 版本的 APISIX。
注意 APISIX Ingress 控制器需要与 APISIX 管理 API 进行通信,因此为进行测试,我们将 apisix.allow_admin 设置为 0.0.0.0/0。
x
apiVersionv1kindConfigMapmetadata nameapisix-conf namespaceapisixdata config.yaml- apisix node_listen9080 # APISIX listening port enable_heartbeattrue enable_admintrue enable_admin_corstrue enable_debugfalse enable_dev_modefalse # Sets nginx worker_processes to 1 if set to true enable_reuseporttrue # Enable nginx SO_REUSEPORT switch if set to true. enable_ipv6true config_centeretcd # etcd: use etcd to store the config value
allow_admin# http://nginx.org/en/docs/http/ngx_http_access_module.html#allow0.0.0.0/0 port_admin9180
# Default token when use API to call for Admin API. # *NOTE*: Highly recommended to modify this value to protect APISIX's Admin API. # Disabling this configuration item means that the Admin API does not # require any authentication. admin_key # admin: can everything for configuration dataname"admin" keyedd1c9f034335f136f87ad84b625c8f1 roleadmin # viewer: only can view configuration dataname"viewer" key4054f7cf07e344346cd3f287985e76a2 roleviewer # dns_resolver: # - 127.0.0.1 dns_resolver_valid30 resolver_timeout5
nginx_config# config for render the template to generate nginx.conf error_log"/dev/stderr" error_log_level"warn" # warn,error worker_rlimit_nofile20480 # the number of files a worker process can open, should be larger than worker_connections event worker_connections10620 http access_log"/dev/stdout" keepalive_timeout60s # timeout during which a keep-alive client connection will stay open on the server side. client_header_timeout60s # timeout for reading client request header, then 408 (Request Time-out) error is returned to the client client_body_timeout60s # timeout for reading client request body, then 408 (Request Time-out) error is returned to the client send_timeout10s # timeout for transmitting a response to the client.then the connection is closed underscores_in_headers"on" # default enables the use of underscores in client request header fields real_ip_header"X-Real-IP" # http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header real_ip_from# http://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from127.0.0.1'unix:'
etcd host"http://etcd-headless.apisix.svc.cluster.local:2379" prefix"/apisix" # apisix configurations prefix timeout30 # seconds plugins# plugin listapi-breakerauthz-keycloakbasic-authbatch-requestsconsumer-restrictioncorsechofault-injectiongrpc-transcodehmac-authhttp-loggerip-restrictionjwt-authkafka-loggerkey-authlimit-connlimit-countlimit-reqnode-statusopenid-connectprometheusproxy-cacheproxy-mirrorproxy-rewriteredirectreferer-restrictionrequest-idrequest-validationresponse-rewriteserverless-post-functionserverless-pre-functionsls-loggersyslogtcp-loggerudp-loggeruri-blockerwolf-rbaczipkintraffic-split stream_pluginsmqtt-proxy请确保 etcd.host 与我们最初创建的无头服务匹配。在我们的例子中,它是 http://etcd-headless.apisix.svc.cluster.local:2379。
在该配置中,我们在 apisix.admin_key 部分的下方定义具有 admin 名称的访问密钥。该密钥是我们的 API 密钥,以后将用于控制 APISIX。该密钥是 APISIX 的默认密钥,在生产环境中,应该修改它。
将其保存为 config.yaml,然后运行 kubectl -n apisix create -f config.yaml,创建 ConfigMap。稍后,我们将该 ConfigMap 挂载到 APISIX Deployment 中。
x
apiVersionapps/v1kindDeploymentmetadata nameapisix namespaceapisix labels app.kubernetes.io/nameapisixspec replicas1 selector matchLabels app.kubernetes.io/nameapisix template metadata labels app.kubernetes.io/nameapisix spec containersnameapisix image"apache/apisix:2.5-alpine" imagePullPolicyIfNotPresent portsnamehttp containerPort9080 protocolTCPnametls containerPort9443 protocolTCPnameadmin containerPort9180 protocolTCP readinessProbe failureThreshold6 initialDelaySeconds10 periodSeconds10 successThreshold1 tcpSocket port9080 timeoutSeconds1 lifecycle preStop exec command/bin/sh-c"sleep 30" volumeMountsmountPath/usr/local/apisix/conf/config.yaml nameapisix-config subPathconfig.yaml resources volumesconfigMap nameapisix-conf nameapisix-config现在,应该可以使用 APISIX 了。使用 kubectl get pods -n apisix -l app.kubernetes.io/name=apisix -o name 来列举 APISIX Pod 名称。这里我们假设 Pod 名称是 apisix-7644966c4d-cl4k6。
我们检查一下:
x
kubectl -n apisix exec -it apisix-7644966c4d-cl4k6 -- curl http://127.0.0.1:9080如果你正在使用 Linux 或 macOS,那么在 Bash 中运行下面的命令:
x
kubectl -n apisix exec -it $(kubectl get pods -n apisix -l app.kubernetes.io/name=apisix -o name) -- curl http://127.0.0.1:9080如果 APISIX 正常工作,那么它应该输出:{"error_msg":"404 Route Not Found"}。因为我们尚未定义任何路由。
在配置 APISIX 前,我们需要创建一个测试服务。在这里,我们使用 kennethreitz/httpbin。我们将该 httpbin 服务放在 demo 命名空间中。
xxxxxxxxxxkubectl create ns demokubectl label namespace demo apisix.ingress=watching # 给 demo 命名空间添加 apisix.ingress 标签kubectl -n demo run httpbin --image-pull-policy=IfNotPresent --image kennethreitz/httpbin --port 80kubectl -n demo expose pod httpbin --port 80在 httpbin 服务启动后,我们应该可以在 APISIX Pod 中通过服务访问它。
x
kubectl -n apisix exec -it $(kubectl get pods -n apisix -l app.kubernetes.io/name=apisix -o name) -- curl http://httpbin.demo/get该命令输出请求的查询参数,比如:
xxxxxxxxxx{ "args": {}, "headers": { "Accept": "*/*", "Host": "httpbin.demo", "User-Agent": "curl/7.67.0" }, "origin": "172.17.0.1", "url": "http://httpbin.demo/get"}如欲阅读更多,请参阅快速入门。
现在,我们可以定义通过 APISIX 代理 HTTPBIN 服务流量的路由。
假设我们想路由 URI 拥有 /httpbin 前缀,并且请求包含 Host: httpbin.org 头的所有流量。
请注意管理端口是 9180。
x
kubectl -n apisix exec -it $(kubectl get pods -n apisix -l app.kubernetes.io/name=apisix -o name) -- curl "http://127.0.0.1:9180/apisix/admin/routes/1" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '{ "uri": "/*", "host": "httpbin.org", "upstream": { "type": "roundrobin", "nodes": { "httpbin.demo:80": 1 } }}'输出如下所示:
xxxxxxxxxx{"action":"set","node":{"key":"\/apisix\/routes\/1","value":{"status":1,"create_time":1621408897,"upstream":{"pass_host":"pass","type":"roundrobin","hash_on":"vars","nodes":{"httpbin.demo:80":1},"scheme":"http"},"update_time":1621408897,"priority":0,"host":"httpbin.org","id":"1","uri":"\/*"}}}我们可以通过 GET /apisix/admin/routes 检查路由规则:
x
kubectl -n apisix exec -it $(kubectl get pods -n apisix -l app.kubernetes.io/name=apisix -o name) -- curl "http://127.0.0.1:9180/apisix/admin/routes/1" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1"输出如下所示:
xxxxxxxxxx{"action":"get","node":{"key":"\/apisix\/routes\/1","value":{"upstream":{"pass_host":"pass","type":"roundrobin","scheme":"http","hash_on":"vars","nodes":{"httpbin.demo:80":1}},"id":"1","create_time":1621408897,"update_time":1621408897,"host":"httpbin.org","priority":0,"status":1,"uri":"\/*"}},"count":"1"}现在,我们测试路由规则:
x
kubectl -n apisix exec -it $(kubectl get pods -n apisix -l app.kubernetes.io/name=apisix -o name) -- curl "http://127.0.0.1:9080/get" -H 'Host: httpbin.org'输出如下所示:
xxxxxxxxxx{ "args": {}, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/7.67.0", "X-Forwarded-Host": "httpbin.org" }, "origin": "127.0.0.1", "url": "http://httpbin.org/get"}APISIX Ingress 控制器可以帮助你通过使用 Kubernetes 资源的方式,声明式地管理配置。这里我们将安装 1.6.0 版本。
当前,APISIX Ingress 控制器同时支持官方的 Ingress 资源和 APISIX 的自定义资源定义,包括 ApisixRoute 和 ApisixUpstream。
在安装 APISIX Ingress 控制器前,我们需要创建服务账号和相应的集群角色,以确保 APISIX Ingress 控制器有足够的权限访问所需的资源。
下面是来自 apisix-helm-chart 的示例配置:
x
apiVersionv1kindServiceAccountmetadata nameapisix-ingress-controller namespaceapisix---apiVersionrbac.authorization.k8s.io/v1kindClusterRolemetadata nameapisix-clusterrole namespaceapisixrulesapiGroups"" resourcesconfigmapsendpointspersistentvolumeclaimspodsreplicationcontrollersreplicationcontrollers/scaleserviceaccountsservicessecrets verbsgetlistwatchapiGroups"" resourcesbindingseventslimitrangesnamespaces/statuspods/logpods/statusreplicationcontrollers/statusresourcequotasresourcequotas/status verbsgetlistwatchapiGroups"" resourcesnamespaces verbsgetlistwatchapiGroupsapps resourcescontrollerrevisionsdaemonsetsdeploymentsdeployments/scalereplicasetsreplicasets/scalestatefulsetsstatefulsets/scale verbsgetlistwatchapiGroupsautoscaling resourceshorizontalpodautoscalers verbsgetlistwatchapiGroupsbatch resourcescronjobsjobs verbsgetlistwatchapiGroupsextensions resourcesdaemonsetsdeploymentsdeployments/scaleingressesnetworkpoliciesreplicasetsreplicasets/scalereplicationcontrollers/scale verbsgetlistwatchapiGroupspolicy resourcespoddisruptionbudgets verbsgetlistwatchapiGroupsnetworking.k8s.io resourcesingressesnetworkpolicies verbsgetlistwatchapiGroupsmetrics.k8s.io resourcespods verbsgetlistwatchapiGroupsapisix.apache.org resourcesapisixroutesapisixroutes/statusapisixupstreamsapisixupstreams/statusapisixtlsesapisixtlses/statusapisixclusterconfigsapisixclusterconfigs/statusapisixconsumersapisixconsumers/statusapisixpluginconfigs verbsgetlistwatchapiGroupscoordination.k8s.io resourcesleases verbs'*'---apiVersionrbac.authorization.k8s.io/v1kindClusterRoleBindingmetadata nameapisix-clusterrolebinding namespaceapisixroleRef apiGrouprbac.authorization.k8s.io kindClusterRole nameapisix-clusterrolesubjectskindServiceAccount nameapisix-ingress-controller namespaceapisix然后,我们需要创建 ApisixRoute CRD:
x
git clone https://github.com/apache/apisix-ingress-controller.git --depth 1cd apisix-ingress-controller/kubectl apply -k samples/deploy/crd请参阅 samples 获取细节。
为使 Ingress 控制器与 APISIX 一起正常工作,我们需要创建一个配置文件,其中包含 APISIX 管理 API URL 和 API 密钥,如下所示:
x
apiVersionv1data config.yaml # log options log_level: "debug" log_output: "stderr" http_listen: ":8080" enable_profiling: true kubernetes: kubeconfig: "" resync_interval: "30s" namespace_selector: - "apisix.ingress=watching" ingress_class: "apisix" ingress_version: "networking/v1" apisix_route_version: "apisix.apache.org/v2" apisix: default_cluster_base_url: "http://apisix-admin.apisix:9180/apisix/admin" default_cluster_admin_key: "edd1c9f034335f136f87ad84b625c8f1"kindConfigMapmetadata nameapisix-configmap namespaceapisix labels app.kubernetes.io/nameingress-controller如果想学习所有配置项,查看 conf/config-default.yaml 获取细节。
因为 Ingress 控制器需要访问 APISIX 管理 API,所以我们需要为 APISIX 创建一个服务。
x
apiVersionv1kindServicemetadata nameapisix-admin namespaceapisix labels app.kubernetes.io/nameapisixspec typeClusterIP portsnameapisix-admin port9180 targetPort9180 protocolTCP selector app.kubernetes.io/nameapisix因为当前 APISIX Ingress 控制器不 100% 兼容 APISIX,所以我们需要删除之前创建的路由,以防某些数据结构不匹配。
x
kubectl -n apisix exec -it $(kubectl get pods -n apisix -l app.kubernetes.io/name=apisix -o name) -- curl "http://127.0.0.1:9180/apisix/admin/routes/1" -X DELETE -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1"完成这些配置后,我们现在部署 Ingress 控制器。
x
apiVersionapps/v1kindDeploymentmetadata nameapisix-ingress-controller namespaceapisix labels app.kubernetes.io/nameingress-controllerspec replicas1 selector matchLabels app.kubernetes.io/nameingress-controller template metadata labels app.kubernetes.io/nameingress-controller spec serviceAccountNameapisix-ingress-controller volumesnameconfiguration configMap nameapisix-configmap itemskeyconfig.yaml pathconfig.yaml initContainersnamewait-apisix-admin imagebusybox1.28 command'sh' '-c' "until nc -z apisix-admin.apisix.svc.cluster.local 9180 ; do echo waiting for apisix-admin; sleep 2; done;" containersnameingress-controller command/ingress-apisix/apisix-ingress-controlleringress--config-path/ingress-apisix/conf/config.yaml image"apache/apisix-ingress-controller:1.6.0" imagePullPolicyIfNotPresent portsnamehttp containerPort8080 protocolTCP livenessProbe httpGet path/healthz port8080 readinessProbe httpGet path/healthz port8080 resources volumeMountsmountPath/ingress-apisix/conf nameconfiguration在该 Deployment 中,我们将上面创建的 ConfigMap 挂载为配置文件,并且告诉 Kubernetes 使用服务账号 apisix-ingress-controller。
在 Ingress 控制器的状态转换为 Running 后,我们创建 APISIXRoute 资源,观察它的行为。
下面是 APISIXRoute 示例:
x
apiVersionapisix.apache.org/v2kindApisixRoutemetadata namehttpserver-route namespacedemospec httpnamehttpbin match hostslocal.httpbin.org paths/* backendsserviceNamehttpbin servicePort80注意 apiVersion 字段应该匹配上面的 ConfigMap。serviceName 应该匹配暴露的服务名称,这里是 httpbin。
在创建它前,我们确认带头 Host: local.http.demo 的请求返回 404:
x
kubectl -n apisix exec -it $(kubectl get pods -n apisix -l app.kubernetes.io/name=apisix -o name) -- curl "http://127.0.0.1:9080/get" -H 'Host: local.httpbin.org'将返回:
xxxxxxxxxx{"error_msg":"404 Route Not Found"}在与目标服务相同的命名空间中应用 APISIXRoute,本例是 demo。在应用它后,我们检查它是否生效:
x
kubectl -n apisix exec -it $(kubectl get pods -n apisix -l app.kubernetes.io/name=apisix -o name) -- curl "http://127.0.0.1:9080/get" -H "Host: local.httpbin.org"应该返回:
xxxxxxxxxx{ "args": {}, "headers": { "Accept": "*/*", "Host": "local.httpbin.org", "User-Agent": "curl/7.67.0", "X-Forwarded-Host": "local.httpbin.org" }, "origin": "127.0.0.1", "url": "http://local2.httpbin.org/get"}这就是所有!享受你的 APISIX 和 APISIX Ingress 控制器之旅。
用于 Kubernetes 的 Apache APISIX Ingress 控制器。

当前 apisix-ingress-controller CRD 包括 6 部分:ApisixRoute/ApisixUpstream/ApisixConsumer/ApisixTls/ApisixClusterConfig/ApisixPluginConfig。其设计遵循如下思想。
ApisixRoute 的设计结构与 Kubernetes Ingress 基本相似ApisixRoute,Ingress 控制器将自动添加 ApisixUpstreamApisixUpstream 可以定义 Apache APISIX 上游上的一些细节,比如负载均衡/健康检查等apisix-ingress-controller 负责与 Kubernetes API Server 进行交互,申请可访问资源权限(RBAC),监视变更,在 Ingress 控制器中实现对象转换,比较变更,然后同步到 Apache APISIX。

下面是介绍 ApisixRoute 和其它 CRD 在同步过程中的主要逻辑的流程图。

apisix-ingress-controller 为 CRD 提供外部配置方法。它针对的是日常运维等操作人员,他们经常需要批量地处理大量路由,希望在同一配置文件中处理所有相关服务,同时具有方便易懂的管理能力。而 Apache APISIX 从网关的角度进行设计,所有路由相互独立。这导致两者在数据结构上有明显的差异。一个侧重于批量定义,而另一个是离散实现。
考虑到不同人群的使用习惯,CRD 的数据结构借鉴 Kubernetes Ingress 的数据结构,在形态上基本相同。
简单的对比如下,它们有不同的定义:

他们是多对多的关系。因此,apisix-ingress-controller 必须对 CRD 执行一些转换,以适配不同网关。
目前,我们定义多个 CRD,这些 CRD 负责它们各自的字段定义。ApisixRoute/ ApisixUpstream 对应 Apache APISIX 中的 route/ service/upstream 等对象。由于 APISIX 对象之间的强绑定关系,在批量修改和删除 CRD 等数据结构时,必须考虑对象之间的级联影响。
因此,在 apisix-ingress-controller 中,通过 channel 实现广播通知机制,即任何对象的定义必须被通知给与其相关的其它对象,并且触发相应的行为。

seven 模块在内部保存内存数据结构,目前它与 Apache APISIX 资源对象非常相似。当 Kubernetes 资源对象发生新变更时,seven 将比较内存对象,然后根据比较结果,进行增量更新。
当前的比较规则基于 route / service / upstream 资源对象的分组,分别进行比较,发现差异后,进行相应的广播通知。

apisix-ingress-controller 根据 ApisixUpstream 资源对象中定义的 namespace、name、port,将处于 running 状态的 endpoints 节点信息注册到 Apache APISIX Upstream 中的节点。并且根据 Kubernetes,实时同步 Endpoint 状态。
基于服务发现,Apache APISIX Ingress 可以直接访问后端 Pod 节点。绕过 Kubernetes 服务,可实现定制化的负载均衡策略。
与 Kubernetes Nginx Ingress 的实现不同,apisix-ingress-controller 基于 Apache APISIX 的插件机制实现注解。
比如,通过 ApisixRoute 资源对象中的 k8s.apisix.apache.org/whitelist-source-range 注解配置黑/白名单设置。
xxxxxxxxxxapiVersionapisix.apache.org/v2kindApisixRoutemetadata annotations k8s.apisix.apache.org/whitelist-source-range1.2.3.4,2.2.0.0/16 namehttpserver-routespec ...这里的黑/白名单由 ip-restriction 插件实现。
未来将有更多注释实现,以方便定义一些常见配置,比如 CORS。
如果你有注解需求,欢迎到 issue 讨论,我们讨论如何进行实现。