这篇文章将针对一个新安装的 K3s,使用 Traefik 自带的 Let’s Encrypt 为其配置泛域名证书。我所使用的 K3s 版本为
v1.22.6+k3s1
,因此其使用的 Traefik 版本是 v2
。写在开始之前
当我们第一次接触此类配置时候,困难不单单来源于步骤的不明晰。一般来说,会有以下几个问题一直萦绕着我们:
- 对 Kubernetes 本身不够熟悉
- 缺少细致的、必要的文档说明
- 手头的文档或资料与实际环境的版本不符
- 问题可能有多种解决方法,我们所采用的方法和教程中的方法不一致
- 我们对自己想要达成的大目标不够细分,无法变成实际可解决的步骤
- 缺少调试手段以至于无法定位 corner case
很遗憾,这些问题关乎着我们面对的问题的本质,也更难以从文字、视频中学习到。但是还是希望我的这篇文章能为你带来一些些启发——毕竟这几乎是此类文章存在的全部意义。
如何才能完成泛域名证书自动续签
我们可以预先将问题分解如下:
- 准备基本的环境(包括绑定域名)
- 找到一种办法能够修改 Traefik 的行为(Traefik 是通过 HelmCart 控制的,显然无法直接修改
Deployment.yaml
等文本配置
- 修改配置,使 Traefik 可以自动签发证书
- 找到一种配置方法,帮助 Traefik 发现我们的服务并使用 HTTPS
环境配置
- 泛域名绑定的时候,一定要让
*.example.com
使用A/AAAA
记录绑定到 IP,而不是CNAME
到example.com
。
通过 HelmChartConfig 修改 Traefik 的配置
To allow overriding values for packaged components that are deployed as HelmCharts (such as Traefik), K3s versions starting with v1.19.0+k3s1 support customizing deployments via a HelmChartConfig resources.
K3s 的官方文档在 Helm 这一章节给了我们基本的提示,我们可以通过在
traefik-config.yaml
配置 HelmChartConfig 来给原有 HelmChart 提供“Patch”的功能。
然而这还没有结束:配置的格式是什么?
答案需要去 Helm 使用的 Traefik Chart 源码中去找寻,仓库中的
value.yaml
是我们可以修改的所有配置项以及格式。因此所有的配置可以如此映射:
# value.yaml logs: general: level: DEBUG
# /var/lib/rancher/k3s/server/manifests/traefik-config.yaml apiVersion: helm.cattle.io/v1 kind: HelmChartConfig metadata: name: traefik namespace: kube-system spec: valuesContent: |- logs: general: level: DEBUG
修改配置,使 Traefik 可以签发证书
DNSPod 用户特殊注意项
DNSPod 有以下两个问题:
- 我们从管理界面生成的
ID
和TOKEN
需要以DNSPOD_API_KEY=<ID>,<TOKEN>
的格式使用。
- 由于解析生效时间比较慢,因此建议延长 Timeout 时间。
我使用的是 DNSPod,因此我的配置如下:
# /var/lib/rancher/k3s/server/manifests/traefik-config.yaml apiVersion: helm.cattle.io/v1 kind: HelmChartConfig metadata: name: traefik namespace: kube-system spec: valuesContent: |- ports: websecure: tls: enabled: true certResolver: "le" additionalArguments: - "--entrypoints.websecure.http.tls.domains[0].main=k3s.example.com" - "--entrypoints.websecure.http.tls.domains[0].sans=*.k3s.example.com" - "[email protected]" - "--certificatesresolvers.le.acme.storage=/data/acme.json" - "--certificatesresolvers.le.acme.dnschallenge=true" - "--certificatesresolvers.le.acme.dnschallenge.provider=dnspod" env: - name: "DNSPOD_API_KEY" value: "<ID>,<TOKEN>" - name: "DNSPOD_HTTP_TIMEOUT" value: "150" - name: "DNSPOD_POLLING_INTERVAL" value: "5" - name: "DNSPOD_PROPAGATION_TIMEOUT" value: "300" # logs: # general: # level: DEBUG
在尝试配置的时候,我遇到过许多报错。如果你也需要调试的话,建议把日志阈值调低。
配置保存以后,K3s 将通过另一个容器
helm-install-traefik
调用 Helm 的更新命令。因此有关配置语法的正确性需要看这个 Pod 的日志;而其余的日志要去 traefik
Pod 查看。另外需要注意的是,Let’s Encrypt 存在访问速率限制,需要注意。
帮助 Traefik 发现我们的服务并使用 HTTPS
Traefik 在官方文档中提供了多种方式;需要指出的是,
kind: Ingress
是由 Kubernetes 官方维护的 Ingress 配置方式,而 kind: IngressRoute
是由 Traefik 团队维护的专门针对 Traefik 服务发现的配置(功能更专业一些)。因此,最后我们采用了后者:apiVersion: v1 kind: Namespace metadata: name: test --- apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: test namespace: test spec: routes: - kind: Rule match: Host(`test.k3s.example.net`) services: - name: hello-world port: 80 # tls: # certResolver: le # domains: # - main: "k3s.bitnp.net" --- apiVersion: v1 kind: Service metadata: name: hello-world namespace: test spec: ports: - port: 80 protocol: TCP selector: app: hello-world --- apiVersion: apps/v1 kind: Deployment metadata: name: hello-world-nginx namespace: test spec: selector: matchLabels: app: hello-world replicas: 1 template: metadata: labels: app: hello-world spec: containers: - name: nginx image: nginx ports: - containerPort: 80
后记:
由于我们部署在内网,出口流量被严重限制。我们的自动续签计划没能实现。(Traefik 代码中有递归查询域名解析提供商的 DNS 逻辑,而我们这里的环境无法访问这些 DNS)