应用开发的最终目标无疑是将应用暴露到互联网上。对于开发人员来说,Kubernetes 提供了 Ingress controller(Ingress 控制器)作为将请求路由到应用的机制,在一定程度上简化了这个过程。但并非一切都支持自助服务(尽管您可能希望如此):您仍然需要使用域名系统 (DNS) 中的记录,将应用的域名映射到 Ingress controller 的 IP 地址,并且仍然需要使用 TLS 证书来保护 HTTPS 连接。在大多数组织中,您自己并不拥有 DNS 或 TLS,必须与拥有 DNS 或 TLS 的一个或多个工作组进行协调。
对运维人员来说,事情未必变得更加简单。大多数组织很少需要更新 DNS 记录,因此对应的流程(包括业务规则和技术步骤)往往比较模糊或干脆没有。这意味着,当您需要添加 DNS 记录时,您首先需要查找文档,询问同事,或者(在最坏的情况下)自己想办法。您还需要确保遵守所有安全规则,并确保为防火墙正确标记 Ingress。
幸运的是,有一种方法可以让开发人员和运维人员的工作变得更轻松。在这篇文章中,我们演示了运维人员可如何配置 Kubernetes 部署,以便开发人员能够在 Kubernetes 环境中自助更新 DNS 记录和生成 TLS 证书。通过提前构建基础架构,您可以确保满足所有必要的业务要求和技术要求。
借助这款解决方案,如果开发人员需要将应用暴露到互联网上,只需按照提供的模板创建一个 Ingress controller 即可,提供的模板中包含一个位于 Kubernetes 安装所管理的域中的全限定域名 (FQDN)。Kubernetes 使用该模板为 Ingress controller 分配一个 IP 地址,创建 DNS A
记录将 FQDN 映射到 IP 地址,为 FQDN 生成 TLS 证书并将其添加到 Ingress controller。清理也同样简单:当 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,并用它替换 <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
(已在上一步中定义)作为检索令牌的位置。您也可以根据需要将 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 令牌来管理 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 部署。
domain-filter
设置为 example.com
。CF_API_TOKEN
环境变量设置为您的 Cloudflare API 令牌。对于 <your-API-token>
,可以用实际的令牌或包含令牌的 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。注意以下设置:
kind: 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
命令来验证部署对象的配置。kubectl
logs
<component>
命令来查看各种已部署组件的日志文件。如果仍有问题,请通过添加“小N助手(微信号:nginxoss)”加入到我们的官方微信群,与我们进行直接交流。
如欲体验基于 NGINX Plus 的 NGINX Ingress Controller,请立即下载 30 天免费试用版,或者联系我们讨论您的用例。
"This blog post may reference products that are no longer available and/or no longer supported. For the most current information about available F5 NGINX products and solutions, explore our NGINX product family. NGINX is now part of F5. All previous NGINX.com links will redirect to similar NGINX content on F5.com."