当然,应用开发的最终目标是将应用发布到互联网上。对于开发人员而言,Kubernetes提供Ingress控制器作为将请求路由到应用的机制,在一定程度上简化了这一过程。但并非所有环节都能如您所愿地自助完成:仍需在域名系统(DNS)中添加一条记录,将应用的域名映射到Ingress控制器的IP地址,并获取TLS证书,以保护通过HTTPS建立的连接。在大多数组织中,DNS或TLS并不由开发人员自行管理,因此必须与负责这些工作的一个或多个运维团队协调。
对运维人员来说,情况也未必更轻松。在大多数组织中,更新DNS记录的需求并不常见,因此相关流程(包括业务规则和实际技术步骤)往往不够完善,甚至根本不存在。这意味着需要添加DNS记录时,必须先查找文档、咨询同事,最糟糕的情况下只能自行摸索。此外,还需要确保遵守公司的各项安全规则,并为Ingress正确添加防火墙所需的标记。
好在,有一种方法可以让开发人员和运维人员都更轻松。本文将介绍运维人员如何配置Kubernetes部署,使开发人员能够在Kubernetes环境中以自助方式更新DNS记录并生成TLS证书。通过提前搭建基础设施,可以确保满足所有必要的业务和技术要求。
概述和先决条件
部署该解决方案后,开发人员只需按照提供的模板创建Ingress控制器,即可将应用程序开放到互联网。该模板包含一个完全限定域名(FQDN),此域名位于Kubernetes安装所管理的域中。Kubernetes使用该模板为Ingress控制器分配IP地址,创建DNS A记录以将FQDN映射到该IP地址,并为FQDN生成TLS证书并将其添加到Ingress控制器。清理同样简单:移除Ingress后,DNS记录也会被清理。
该解决方案采用以下技术(下文提供安装和配置说明):
- Kubernetes。
- ExternalDNS。
- cert-manager。
- Let’s Encrypt。
- F5 NGINX提供的NGINX Ingress Controller,基于NGINX Open Source或NGINX Plus。此解决方案不适用于由Kubernetes社区维护的NGINX Ingress Controller。有关这两个项目之间差异的更多详情,请参阅我们的博客。
配置该解决方案前,需要准备:
- 一个配置了出口(
LoadBalancer)对象的Kubernetes云环境。此解决方案使用Linode,但也支持其他云服务提供商。 - 托管在Cloudflare上的域名。我们选择Cloudflare,是因为它是cert-manager支持的DNS提供商之一,并且支持ExternalDNS(截至撰写本文时,该功能处于测试阶段)。强烈建议不要将该域名用于生产环境或任何其他关键用途。
- 访问Cloudflare API,该功能包含在免费套餐中。
- Helm,用于安装和部署Kubernetes。
kubectl,用作Kubernetes的命令行界面。- 可选使用K9s,这是一款设计完善的文本用户界面(TUI),可用于以更有条理的方式与Kubernetes交互。
我们还假定您对Kubernetes有基本了解(包括如何应用清单、使用Helm chart,以及发出kubectl命令来查看输出和排查问题)。了解Let’s Encrypt的基本概念会有所帮助,但并非必需;如需概览,请参阅我们的博客。您也不需要了解cert-manager的工作原理,但如果想了解它(以及一般意义上的证书)如何与NGINX Ingress Controller配合使用,请参阅我最近发布的文章《在Kubernetes环境中实现证书管理自动化》。
我们已在macOS和Linux上测试过该解决方案。尚未在适用于Linux的Windows子系统版本2(WSL2)上进行测试,但预计不会出现问题。
注意:此解决方案仅作为概念验证示例,不适用于生产环境。特别需要注意的是,它并未涵盖运维和安全方面的所有建议实践。有关这些主题的信息,请参阅cert-manager和ExternalDNS文档。
部署解决方案
按照以下各节中的步骤部署解决方案:
下载软件
- 下载您的Cloudflare API Token。
- 克隆NGINX Ingress Controller存储库:
$ git clone https://github.com/nginxinc/kubernetes-ingress.gitCloning into 'kubernetes-ingress'...
remote: Enumerating objects: 45176, done.
remote: Counting objects: 100% (373/373), done.
remote: Compressing objects: 100% (274/274), done.
remote: Total 45176 (delta 173), reused 219 (delta 79), pack-reused 44803
Receiving objects: 100% (45176/45176), 60.45 MiB | 26.81 MiB/s, done.
Resolving deltas: 100% (26592/26592), done.- 验证是否可以连接到Kubernetes集群。
$ kubectl cluster-infoKubernetes control plane is running at https://ba35bacf-b072-4600-9a04-e04...6a3d.us-west-2.linodelke.net:443
KubeDNS is running at https://ba35bacf-b072-4600-9a04-e04...6a3d.us-west-2.linodelke.net:443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.部署NGINX Ingress Controller
- 使用Helm部署NGINX Ingress Controller。请注意,我们将添加3个非标准配置选项:
$ helm install nginx-kic nginx-stable/nginx-ingress --namespace nginx-ingress --set controller.enableCustomResources=true --create-namespace --set controller.enableCertManager=true --set controller.enableExternalDNS=trueNAME: nginx-kic
LAST DEPLOYED: Day Mon DD hh:mm:ss YYYY
NAMESPACE: nginx-ingress
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The NGINX Ingress Controller has been installed.controller.enableCustomResources:指示Helm安装用于创建NGINX VirtualServer和VirtualServerRoute自定义资源的自定义资源定义(CRD)。controller.enableCertManager,配置NGINX Ingress Controller,使其与cert-manager组件通信。controller.enableExternalDNS,配置Ingress Controller,使其与ExternalDNS组件通信。
- 确认NGINX Ingress Controller正在运行,并记下
EXTERNAL-IP字段中的值,该值是NGINX Ingress Controller的IP地址(此处为www.xxx.yyy.zzz)。为便于阅读,输出分为两行显示。
$ kubectl get services --namespace nginx-ingressNAME TYPE CLUSTER-IP ...
nginx-kic-nginx-ingress LoadBalancer 10.128.152.88 ...
... EXTERNAL-IP PORT(S) AGE
... www.xxx.yyy.zzz 80:32457/TCP,443:31971/TCP 3h8m部署cert-manager
在该解决方案中,cert-manager在获取TLS证书时使用DNS-01质询类型,因此需要在创建ClusterIssuer资源时提供Cloudflare API令牌。在该解决方案中,API令牌以Kubernetes Secret的形式提供。
- 使用Helm,部署cert-manager:
$ helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.9.1 --set installCRDs=trueNAME: cert-manager
LAST DEPLOYED: Day Mon DD hh:mm:ss YYYY
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.9.1 has been deployed successfully!- 将Cloudflare API令牌部署为Kubernetes Secret,并将其替换为“
<your-API-token>”:
$ kubectl apply -f - <<EOFapiVersion: v1 kind: Secret metadata: name: Cloudflare-api-token-secret namespace: cert-manager type: Opaque stringData: api-token: "<your-API-token>" EOF
secret/Cloudflare-api-token-secret created- 创建一个ClusterIssuer对象,并将
Cloudflare-api-token-secret(已在上一步中定义)指定为令牌的检索位置。您也可以将example-issuer(位于metadata.name字段中)和example-issuer-account-key(位于spec.acme.privateKeySecretRef.name字段中)替换为其他名称。
$ kubectl apply -f - <<EOFapiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: example-issuer namespace: cert-manager spec: acme: email: example@example.com server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: example-issuer-account-key solvers: - dns01: Cloudflare: apiTokenSecretRef: name: Cloudflare-api-token-secret key: api-token EOF
clusterissuer.cert-manager.io/example-issuer created- 验证ClusterIssuer已部署并就绪(
READY字段的值为True)。
$ kubectl get clusterissuerNAME READY AGE
example-issuer True 3h9m部署ExternalDNS
与cert-manager类似,ExternalDNS项目需要使用Cloudflare API Token来管理DNS。两个项目可以使用同一个令牌,但并非必须如此。
- 为NGINX Ingress Controller创建ExternalDNS CRD,以实现项目之间的集成。
$ kubectl create -f ./kubernetes-ingress/deployments/common/crds/externaldns.nginx.org_dnsendpoints.yamlcustomresourcedefinition.apiextensions.k8s.io/dnsendpoints.externaldns.nginx.org created- 创建External DNS服务(
external-dns)。由于清单较长,此处将其分为两部分。第一部分用于配置账户、角色和权限:
$ kubectl apply -f - <<EOFapiVersion: v1 kind: ServiceAccount metadata: name: external-dns --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: external-dns rules: - apiGroups: [""] resources: ["services","endpoints","pods"] verbs: ["get","watch","list"] - apiGroups: ["extensions","networking.k8s.io"] resources: ["ingresses"] verbs: ["get","watch","list"] - apiGroups: ["externaldns.nginx.org"] resources: ["dnsendpoints"] verbs: ["get","watch","list"] - apiGroups: ["externaldns.nginx.org"] resources: ["dnsendpoints/status"] verbs: ["update"] - apiGroups: [""] resources: ["nodes"] verbs: ["list","watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: external-dns-viewer roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: external-dns subjects: - kind: ServiceAccount name: external-dns namespace: default EOF
serviceaccount/external-dns created
clusterrole.rbac.authorization.k8s.io/external-dns created
clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer created- 清单的第二部分会创建ExternalDNS部署:
$ kubectl apply -f - <<EOF --- apiVersion: apps/v1 kind: Deployment metadata: name: external-dns spec: strategy: type: Recreate selector: matchLabels: app: external-dns template: metadata: labels: app: external-dns spec: serviceAccountName: external-dns containers: - name: external-dns image: k8s.gcr.io/external-dns/external-dns:v0.12.0 args: - --source=service - --source=ingress - --source=crd - --crd-source-apiversion=externaldns.nginx.org/v1 - --crd-source-kind=DNSEndpoint - --domain-filter=example.com - --provider=Cloudflare env: - name: CF_API_TOKEN value: "<your-API-token>" - name: FREE_TIER value: "true" EOF
serviceaccount/external-dns created
clusterrole.rbac.authorization.k8s.io/external-dns created
clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer created
deployment.apps/external-dns created- 创建一个名为“
external-dns”的ServiceAccount对象,用于管理DNS的所有写入和更新操作。 - 创建一个ClusterRole对象(也称为
external-dns),用于定义所需权限。 - 将ClusterRole绑定到ServiceAccount。
- 创建域过滤器,以限制ExternalDNS管理域时可能造成的影响范围。例如,可以指定暂存环境的域名,防止生产环境发生更改。在本例中,我们将“
domain-filter”设置为“example.com”。 - 将
CF_API_TOKEN环境变量设置为Cloudflare API令牌。对于<your-API-token>,请替换为实际令牌或包含该令牌的Secret。如果使用后者,还需要将Secret投射到容器中,并通过环境变量使用该Secret。 - 将
FREE_TIER环境变量设置为"true"(除非您订阅了Cloudflare付费服务,否则此设置适用)。
部署示例应用程序
使用名为“Cafe”的标准NGINX Ingress Controller示例应用进行测试。
- 部署Cafe应用。
$ kubectl apply -f ./kubernetes-ingress/examples/ingress-resources/complete-example/cafe.yamldeployment.apps/coffee created
service/coffee-svc created
deployment.apps/tea created
service/tea-svc created- 为Cafe应用部署NGINX Ingress Controller。请注意以下设置:此步骤所需的完成时间在很大程度上取决于DNS提供商,因为Kubernetes需要与该提供商的DNS API进行交互。
$ kubectl apply -f - <<EOFapiVersion: k8s.nginx.org/v1 kind: VirtualServer metadata: name: cafe spec: host: cafe.example.com tls: secret: cafe-secret cert-manager: cluster-issuer: example-issuer externalDNS: enable: true upstreams: - name: tea service: tea-svc port: 80 - name: coffee service: coffee-svc port: 80 routes: - path: /tea action: pass: tea - path: /coffee action: pass: coffee EOF
virtualserver.k8s.nginx.org/cafe createdkind: VirtualServer,我们使用的是NGINX VirtualServer自定义资源,而不是标准的Kubernetes Ingress资源。spec.host:将cafe.example.com替换为要部署的主机名。该主机必须位于ExternalDNS管理的域中。spec.tls.cert-manager.cluster-issuer:如果一直使用本文中指定的值,此处应为example-issuer。如有必要,请替换为在第 3 步“部署 cert-manager”中选择的名称。spec.externalDNS.enable:值true指示ExternalDNS创建一条DNSA记录。
验证解决方案
- 验证DNS
A记录,尤其要确认在ANSWERSECTION块中,FQDN(此处为cafe.example.com)已映射到正确的IP地址(www.xxx.yyy.zzz)。
$ dig cafe.example.com
; <<>> DiG 9.10.6 <<>> cafe.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22633
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;cafe.example.com. IN A
;; ANSWER SECTION:
cafe.example.com. 279 IN A www.xxx.yyy.zzz
;; Query time: 1 msec
;; SERVER: 2607:fb91:119b:4ac4:2e0:xxxx:fe1e:1359#53(2607:fb91:119b:4ac4:2e0:xxxx:fe1e:1359)
;; WHEN: Day Mon DD hh:mm:ss TZ YYYY
;; MSG SIZE rcvd: 67- 检查证书是否有效(
READY字段的值为True)。
$ kubectl get certificatesNAME READY SECRET AGE
cafe-secret True cafe-secret 8m51s- 验证是否可以访问该应用。
$ curl https://cafe.example.com/coffeeServer address: 10.2.2.4:8080
Server name: coffee-7c86d7d67c-lsfs6
Date: DD/Mon/YYYY:hh:mm:ss +TZ-offset
URI: /coffee
Request ID: 91077575f19e6e735a91b9d06e9684cd
$ curl https://cafe.example.com/tea
Server address: 10.2.2.5:8080
Server name: tea-5c457db9-ztpns
Date: DD/Mon/YYYY:hh:mm:ss +TZ-offset
URI: /tea
Request ID: 2164c245a495d22c11e900aa0103b00f开发者部署NGINX Ingress Controller后会发生什么
部署该解决方案后,系统会在后台执行许多操作。下图展示了开发人员使用NGINX VirtualServer自定义资源部署NGINX Ingress Controller时的处理过程。请注意,图中省略了部分操作细节。

- 开发人员使用NGINX CRD部署VirtualServer资源
- Kubernetes使用NGINX Ingress Controller创建VirtualServer
- NGINX Ingress Controller调用ExternalDNS创建DNS
A记录 - ExternalDNS在DNS中创建
A记录 - NGINX Ingress Controller 调用 cert-manager 请求 TLS 证书
- cert-manager 添加一条DNS记录,供DNS-01质询期间使用
- cert-manager 联系 Let’s Encrypt 以完成质询
- Let’s Encrypt 通过DNS验证质询
- Let’s Encrypt 颁发TLS证书
- cert-manager向NGINX Ingress Controller提供TLS证书
- NGINX Ingress Controller 将受 TLS 保护的外部请求路由到应用程序 Pod
故障排除
由于Kubernetes及所用组件较为复杂,很难提供一份涵盖所有情况的故障排除指南。不过,以下基本建议可帮助您确定问题所在。
- 使用
kubectlget和kubectldescribe命令验证已部署对象的配置。 - 使用
kubectllogs<component>命令查看各个已部署组件的日志文件。 - 使用K9s检查安装情况;该软件会根据严重程度以黄色或红色突出显示问题,并提供用于访问日志和对象详细信息的界面。
如果问题仍未解决,请前往NGINXCommunity Slack联系我们并寻求帮助!我们的社区充满活力,也始终乐于与您一起解决问题。
要试用基于NGINX Plus的NGINX Ingress Controller,立即开始30天免费试用,或联系我们,讨论您的用例。
相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。
