博客 | NGINX

在 Kubernetes 中启用自助 DNS 和证书管理

NGINX-F5-horiz-black-type-RGB 的一部分
Jason Schmidt 缩略图
杰森·施密特
2022 年 11 月 1 日发布

应用开发的最终目的当然是将应用程序展示在互联网上。 对于开发人员来说,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,但其他云提供商也可以使用。
  • 使用Cloudflare托管的域名,我们之所以选择它,是因为它是cert-manager支持的 DNS 提供商之一,并且支持 ExternalDNS(在撰写本文时处于测试阶段)。 我们强烈建议不要将该域用于生产或任何其他关键用途。
  • 访问包含在免费套餐中的 Cloudflare API。
  • Helm用于安装和部署 Kubernetes。
  • kubectl作为 Kubernetes 的命令行界面。
  • 可选的K9s是一个构造良好的有形用户界面 (TUI),可提供一种更结构化的方式与 Kubernetes 进行交互。

我们还假设您对 Kubernetes 有基本的了解(如何应用清单、使用 Helm 图表以及发出kubectl命令来查看输出和排除故障)。 了解 Let's Encrypt 的基本概念很有帮助,但不是必需的;有关概述,请参阅我们的博客。 您也不需要知道cert-manager如何工作,但是如果您对它(以及一般证书)如何与 NGINX Ingress Controller 配合使用感兴趣,请参阅我最近的文章《在 Kubernetes 环境中自动化证书管理》

我们已经在 macOS 和 Linux 上测试了该解决方案。 我们尚未在Windows Subsystem for Linux版本 2(WSL2)上进行测试,但预计不会出现任何问题。

笔记: 该解决方案仅作为概念验证样本,不适用于生产用途。 特别是,它没有涵盖所有操作和安全方面的最佳实践。 有关这些主题的信息,请参阅cert-managerExternalDNS文档。

部署解决方案

按照以下部分中的步骤部署解决方案:

下载软件
  1. 下载您的Cloudflare API 令牌
  2. 克隆 NGINX Ingress Controller 存储库:

    $ git clone https://github.com/nginxinc/kubernetes-ingress.git克隆到“kubernetes-ingress”...远程: 枚举对象: 45176,完成。远程: 计数对象: 100% (373/373),完成。远程: 压缩对象: 100% (274/274),完成。远程: 总计 45176 (增量 173),重复使用 219 (增量 79),打包重复使用 44803 接收对象: 100%(45176/45176),60.45 MiB | 26.81 MiB/s,完成。
    解决差异: 100%(26592/26592),完成。
  3. 验证您是否可以连接到 Kubernetes 集群。

    $ kubectl cluster-info Kubernetes 控制平面正在 https://ba35bacf-b072-4600-9a04-e04...6a3d.us-west-2.linodelke.net:443 运行 KubeDNS 正在 https://ba35bacf-b072-4600-9a04-e04...6a3d.us-west-2.linodelke.net:443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy 运行 要进一步调试和诊断集群问题,请使用“kubectl cluster-info dump”。

部署 NGINX Ingress 控制器

  1. 使用 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=true名称:nginx-kic 最后部署: 日 星期一 DD hh : mm : ss YYYY命名空间:nginx-ingress 状态:已部署 修订: 1 测试套件: 无 注意:
    NGINX Ingress Controller 已安装。
  2. 验证 NGINX Ingress Controller 是否正在运行,并记下EXTERNAL-IP字段中的值 – 它是 NGINX Ingress Controller 的 IP 地址(此处为www.xxx.yyy.zzz )。 为了便于阅读,输出分为两行。

    $ kubectl 获取服务 --namespace nginx-ingress名称类型集群 IP... nginx-kic-nginx-ingress LoadBalancer 10.128.152.88... ... 外部 IP 端口年龄... www.xxx.yyy.zzz 80:32457/TCP,443:31971/TCP 3h8m

部署证书管理器

在解决方案中, cert-manager在获取 TLS 证书时使用DNS-01挑战类型,这需要在创建 ClusterIssuer 资源期间提供 Cloudflare API 令牌。 在解决方案中,API 令牌作为 Kubernetes Secret提供。

  1. 使用 Helm部署 cert-manager

    $ helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.9.1 --set installCRDs=true名称:cert-manager 最后部署: 日 星期一 DD hh : mm : ss YYYY命名空间:cert-manager 状态:已部署 修订: 1 测试套件: 无 注意:cert-manager v1.9.1 已成功部署!
  2. 将 Cloudflare API 令牌部署为 Kubernetes Secret,并将其替换为 <你的 API 令牌>

    $ kubectl apply -f - <<EOFapiVersion: v1
    种类: 机密元数据:名称: Cloudflare-api-token-secret 命名空间:cert-manager 类型: 不透明
    stringData:
    api-token:“<你的 API 令牌>“
    EOF
    secret/Cloudflare-api-token-secret 已创建
  3. 创建一个 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
    种类: ClusterIssuer 元数据:名称:example-issuer 命名空间:cert-manager 规范:acme:电子邮件:example@example.com 服务器:https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef:名称:example-issuer-account-key 求解器:-dns01:
              Cloudflare:apiTokenSecretRef:名称: Cloudflare-api-token-secret 密钥:api-token EOF clusterissuer.cert-manager.io/example-issuer 创建
  4. 验证 ClusterIssuer 是否已部署并准备就绪( READY字段中的值为True )。

    $ kubectl get clusterissuer NAME READY AGE example-issuer True 3小时9分钟

部署 ExternalDNS

与 cert-manager 一样,ExternalDNS 项目需要 Cloudflare API Token 来管理 DNS。 同一个代币可以用于两个项目,但这不是必需的。

  1. 为 NGINX Ingress Controller 创建 ExternalDNS CRD,以实现项目之间的集成。

    $ kubectl create -f ./kubernetes-ingress/deployments/common/crds/externaldns.nginx.org_dnsendpoints.yaml customresourcedefinition.apiextensions.k8s.io/dnsendpoints.externaldns.nginx.org 创建
  2. 创建外部 DNS 服务 ( external-dns )。 由于清单较长,这里我们将其分为两部分。 第一部分配置账户、角色和权限:

    • 创建一个名为external-dns的 ServiceAccount 对象来管理 DNS 的所有写入和更新操作。
    • 创建一个定义所需权限的 ClusterRole 对象(也称为external-dns )。
    • 将 ClusterRole 绑定到 ServiceAccount。
    $ kubectl apply -f - <<EOFapiVersion: v1
    种类: ServiceAccount 元数据: name: external-dns --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole 元数据: 名称:external-dns 规则: - apiGroups:[""] 资源:["services","endpoints","pods"] 动词:["get","watch","list"] - apiGroups:["extensions","networking.k8s.io"] 资源:["ingresses"] 动词:["get","watch","list"] - apiGroups:["externaldns.nginx.org"] 资源:["dnsendpoints"] 动词:["get","watch","list"] - apiGroups:["externaldns.nginx.org"] 资源:["dnsendpoints/status"] 动词:["update"] - apiGroups:[""] 资源:["nodes"] 动词:["list","watch"] --- apiVersion:rbac.authorization.k8s.io/v1 种类: ClusterRoleBinding 元数据:名称:external-dns-viewer roleRef:apiGroup:rbac.authorization.k8s.io 类型: ClusterRole 名称:external-dns 主题:- 种类: ServiceAccount 名称:external-dns 命名空间:默认 EOF serviceaccount/external-dns 创建 clusterrole.rbac.authorization.k8s.io/external-dns 创建 clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer 创建

    清单的第二部分创建 ExternalDNS 部署:

    • 创建域过滤器,限制 ExternalDNS 在管理域时可能造成的损害的范围。 例如,您可以指定暂存环境的域名以防止对生产环境进行更改。 在此示例中,我们将域过滤器设置为example.com
    • CF_API_TOKEN环境变量设置为您的 Cloudflare API 令牌。 为了 <你的 API 令牌>,替换实际的令牌或包含令牌的 Secret。 在后一种情况下,您还需要使用环境变量将 Secret 投影到容器中
    • FREE_TIER环境变量设置为“true” (除非您有付费的 Cloudflare 订阅,否则这是合适的)。
    $  kubectl apply -f - <<EOF 
    ---
    apiVersion:apps/v1
    种类: 部署元数据:名称:external-dns 规范:策略:类型: 重新创建选择器:matchLabels:应用程序:external-dns 模板:元数据:标签:应用程序:external-dns 规范:serviceAccountName:external-dns 容器:- 名称:external-dns 图像: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:- 名称: CF_API_TOKEN
    值:“<你的 API 令牌>“
              - 姓名: FREE_TIER 值:“true” EOF serviceaccount/external-dns 创建 clusterrole.rbac.authorization.k8s.io/external-dns 创建 clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer 创建 deploy.apps/external-dns 创建

部署示例application

使用名为Cafe的标准 NGINX Ingress Controller 示例应用进行测试目的。

  1. 部署Cafe应用。

    $ kubectl apply -f ./kubernetes-ingress/examples/ingress-resources/complete-example/cafe.yaml deploy.apps/coffee 创建 service/coffee-svc 创建 deploy.apps/tea 创建 service/tea-svc 创建
  2. 为 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
    种类: VirtualServer 元数据: 名称:cafe 规格: 主机:cafe.example.com tls: 机密:cafe-secret 证书管理器:cluster-issuer:example-issuer externalDNS: 启用:true 上游: - 名称:tea 服务:tea-svc 端口: 80 - 名称:coffee 服务:coffee-svc 端口: 80 条路线: - 路径:/tea 操作:传递:tea - 路径:/coffee 操作:传递:coffee EOF virtualserver.k8s.nginx.org/cafe 创建

验证解决方案

  1. 验证 DNS A记录 – 特别是在ANSWER SECTION块中,FQDN(此处为cafe.example.com )是否映射到正确的 IP 地址( www.xxx.yyy.zzz )。

    $ dig cafe.example.com ; <<>> DiG 9.10.6 <<>> cafe.example.com ;; 全局选项:+cmd ;; 得到答案: ;; ->>HEADER<<- 操作码: 查询,状态: 无错误,标识: 22633;;标志:qr rd ra;查询: 1,答案: 1、权威性: 0,附加: 1 ;;选择伪节:; EDNS:版本: 0,标志:;udp: 4096;;问题部分:;cafe.example.com。		在;; 答案部分:cafe.example.com。	279 IN A www.xxx.yyy.zzz;;查询时间: 1毫秒;;服务器: 2607:fb91:119b:4ac4:2e0: xxxx :fe1e:1359#53(2607:fb91:119b:4ac4:2e0: xxxx :fe1e:1359);;何时: 日 星期一 DD hh : mm : ss TZ YYYY ;; 收到的消息大小: 67
  2. 检查证书是否有效( READY字段中的值为True )。

    $ kubectl 获取证书名称 就绪 机密 年龄 cafe-secret True cafe-secret 8m51s
  3. 验证您是否可以访问该应用。

    $ curl https://cafe.example.com/coffee服务器地址: 10.2.2.4:8080 服务器名称:coffee-7c86d7d67c-lsfs6 日期: DD/Mon/YYYY:hh:mm:ss +TZ-offset URI:/coffee 请求 ID: 91077575f19e6e735a91b9d06e9684cd $ curl https://cafe.example.com/tea服务器地址: 10.2.2.5:8080 服务器名称:tea-5c457db9-ztpns 日期: DD/Mon/YYYY:hh:mm:ss +TZ-offset URI:/tea 请求 ID: 2164c245a495d22c11e900aa0103b00f

当开发人员部署 NGINX Ingress Controller 时会发生什么

一旦解决方案到位,就会发生很多事情。 该图显示了当开发人员使用 NGINX VirtualServer 自定义资源部署 NGINX Ingress Controller 时发生的情况。 请注意,一些操作细节被省略了。

  1. 开发人员使用 NGINX CRD 部署 VirtualServer 资源
  2. Kubernetes 使用 NGINX Ingress Controller 创建 VirtualServer
  3. NGINX Ingress Controller 调用 ExternalDNS 创建 DNS A记录
  4. ExternalDNS 在 DNS 中创建A记录
  5. NGINX Ingress Controller 调用cert-manager请求 TLS 证书
  6. cert-manager 添加 DNS 记录,供DNS-01质询期间使用
  7. cert-manager 联系 Let’s Encrypt 完成挑战
  8. Let’s Encrypt 验证了针对 DNS 的质询
  9. Let’s Encrypt 颁发 TLS 证书
  10. cert-manager 向 NGINX Ingress Controller 提供 TLS 证书
  11. NGINX Ingress Controller 将 TLS 保护的外部请求路由到应用pod

故障排除

鉴于 Kubernetes 以及我们正在使用的组件的复杂性,很难提供全面的故障排除指南。 话虽如此,还是有一些基本建议可以帮助您确定问题。

  • 使用kubectl getkubectl describe命令来验证已部署对象的配置。
  • 使用 库布克 日志 <组件> 命令查看各个部署组件的日志文件。
  • 使用K9s检查安装;该软件以黄色或红色突出显示问题(取决于严重程度),并提供访问日志和对象详细信息的界面。

如果您仍然遇到问题,请在NGINXCommunity Slack上找到我们并寻求帮助! 我们拥有一个充满活力的社区,并且总是乐于解决问题。

要尝试基于 NGINX Plus 的 NGINX Ingress Controller,请立即开始30 天免费试用联系我们讨论您的用例


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”