应用开发的最终目的当然是将应用程序展示在互联网上。 对于开发人员来说,Kubernetes 通过提供 Ingress 控制器作为将请求路由到应用的机制,在一定程度上简化了这一过程。 但并非一切都像您希望的那样自助:您仍然需要域名系统 (DNS) 中的记录以将应用程序的域名映射到 Ingress 控制器的 IP 地址,并需要 TLS 证书以使用 HTTPS 保护连接。 在大多数组织中,您自己并不拥有 DNS 或 TLS,因此必须与拥有 DNS 或 TLS 的运营组(或多个组!)进行协调。
对于运营商来说事情并不一定就更容易。 在大多数组织中,更新 DNS 记录的需求很少,因此程序(包括业务规则和实际的技术步骤)往往很少或根本不存在。 这意味着当您需要添加 DNS 记录时,您首先需要找到文档、询问同事,或者(在最坏的情况下)弄清楚。 您还需要确保遵守所有公司安全规则,并确保防火墙正确标记了入口。
幸运的是,有一种方法可以让开发人员和运营商的生活变得更轻松。 在这篇文章中,我们展示了运营商如何配置 Kubernetes 部署,以便开发人员在 Kubernetes 环境中自助更新 DNS 记录并生成 TLS 证书。 通过提前构建基础设施,您可以确保满足所有必要的业务和技术要求。
有了这个解决方案,开发人员要将应用公开到互联网,只需按照提供的模板创建一个 Ingress 控制器,该模板包含 Kubernetes 安装管理的域内的完全限定域名 (FQDN)。 Kubernetes 使用模板为 Ingress 控制器分配 IP 地址,创建 DNS A
记录以将 FQDN 映射到 IP 地址,并为 FQDN 生成 TLS 证书并将其添加到 Ingress 控制器。 清理同样简单:当 Ingress 被删除时,DNS 记录也会被清理。
该解决方案利用以下技术(我们在下面提供安装和配置说明):
在配置解决方案之前,您需要:
LoadBalancer
)对象的 Kubernetes 云安装。 该解决方案使用 Linode,但其他云提供商也可以使用。kubectl
作为 Kubernetes 的命令行界面。我们还假设您对 Kubernetes 有基本的了解(如何应用清单、使用 Helm 图表以及发出kubectl
命令来查看输出和排除故障)。 了解 Let's Encrypt 的基本概念很有帮助,但不是必需的;有关概述,请参阅我们的博客。 您也不需要知道cert-manager如何工作,但是如果您对它(以及一般证书)如何与 NGINX Ingress Controller 配合使用感兴趣,请参阅我最近的文章《在 Kubernetes 环境中自动化证书管理》 。
我们已经在 macOS 和 Linux 上测试了该解决方案。 我们尚未在Windows Subsystem for Linux版本 2(WSL2)上进行测试,但预计不会出现任何问题。
笔记: 该解决方案仅作为概念验证样本,不适用于生产用途。 特别是,它没有涵盖所有操作和安全方面的最佳实践。 有关这些主题的信息,请参阅cert-manager和ExternalDNS文档。
按照以下部分中的步骤部署解决方案:
克隆 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'.
使用 Helm 部署 NGINX Ingress Controller。 请注意,我们添加了三个非标准配置选项:
controller.enableCustomResources
– 指示 Helm 安装用于创建 NGINX VirtualServer 和 VirtualServerRoute自定义资源所需的自定义资源定义(CRD)。controller.enableCertManager
– 配置 NGINX Ingress Controller 以与cert-manager组件进行通信。controller.enableExternalDNS
– 配置 Ingress Controller 以与 ExternalDNS 组件进行通信。$ 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.
验证 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在获取 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,并将其替换为 <你的 API 令牌>
:
$ 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
(在上一步中定义)作为检索令牌的位置。 如果您愿意,您可以用不同的名称替换metadata.name
字段中的example-issuer
(以及spec.acme.privateKeySecretRef.name
字段中的example-issuer-account-key
)。
$ 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
与 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
创建外部 DNS 服务 ( external-dns
)。 由于清单较长,这里我们将其分为两部分。 第一部分配置账户、角色和权限:
external-dns
的 ServiceAccount 对象来管理 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 部署:
域过滤器
设置为example.com
。CF_API_TOKEN
环境变量设置为您的 Cloudflare API 令牌。 为了 <你的 API 令牌>
,替换实际的令牌或包含令牌的 Secret。 在后一种情况下,您还需要使用环境变量将 Secret 投影到容器中。FREE_TIER
环境变量设置为“true”
(除非您有付费的 Cloudflare 订阅,否则这是合适的)。$ 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
使用名为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。 请注意以下设置:
种类: VirtualServer
– 我们使用的是 NGINX VirtualServer 自定义资源,而不是标准 Kubernetes Ingress 资源。spec.host
– 将cafe.example.com
替换为您要部署的主机的名称。 主机必须位于使用 ExternalDNS 管理的域内。spec.tls.cert-manager.cluster-issuer
– 如果您一直在使用本文中指定的值,则这就是example-issuer
。 如有必要,请替换您在部署 cert‑manager的第 3 步中选择的名称。spec.externalDNS.enable
– 值true
告诉 ExternalDNS 创建 DNS A
记录。请注意,完成此步骤所需的时间高度依赖于 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 created
验证 DNS A
记录 – 特别是在ANSWER
SECTION
块中,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 VirtualServer 自定义资源部署 NGINX Ingress Controller 时发生的情况。 请注意,一些操作细节被省略了。
A
记录A
记录鉴于 Kubernetes 以及我们正在使用的组件的复杂性,很难提供全面的故障排除指南。 话虽如此,还是有一些基本建议可以帮助您确定问题。
kubectl
get
和kubectl
describe
命令来验证已部署对象的配置。库布克
日志
<组件>
命令查看各个部署组件的日志文件。如果您仍然遇到问题,请在NGINXCommunity Slack上找到我们并寻求帮助! 我们拥有一个充满活力的社区,并且总是乐于解决问题。
要尝试基于 NGINX Plus 的 NGINX Ingress Controller,请立即开始30 天免费试用或联系我们讨论您的用例。
“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”