TT Lab
开始
学习 学习路径 课程

Kubernetes 发行版 — 自己搭

k3s 为什么会重新安装你删除的 traefik

在 TT Lab 中继续学习

一句话总结

k3s 会在启动时以及文件发生变化时应用服务器的 manifests 目录,并在每次启动时重写默认组件的文件;因此要去掉某个组件,应该用 --disable 而不是 kubectl,要修改值,应该用 HelmChartConfig 而不是修改文件。

为什么需要它

在托管式 Kubernetes 中,Ingress 控制器或 metrics server 都有专门的“安装者”。不需要时用 kubectl delete 删掉,再次需要时重新安装。 k3s 的设计目标是:一行安装命令就能得到已经配齐了 coredns、traefik、local-storage、metrics-server 的集群。为此,k3s 自己必须是这些组件的安装者,而且即使有人手动破坏了它们,也要让它们恢复原样。 因此 k3s 把默认组件的清单放在服务器磁盘上,并在每次启动时重写并应用。官方文档写道,这些文件为了保证完整性会在每次启动时被重写,所以不要修改它们。

这个设计在现场会变成什么样子,已经通过实测确认(v1.35.8+k3s1)。

kubectl -n kube-system delete helmchart traefik   → helm-delete Job 이 릴리스를 지움, traefik 사라짐
(20초 기다려도 그대로)
systemctl restart k3s                              → API 5초, HelmChart 가 새 UID 로 생기고 20초 뒤 traefik 복귀

该代码块中的韩文说明依次为:删除 kube-system 中的 HelmChart traefik 后,helm-delete Job 会删除 release,traefik 随之消失,等待 20 秒也没有变化;执行 systemctl restart k3s 后,API 在 5 秒内恢复,HelmChart 以新的 UID 重新生成,20 秒后 traefik 恢复。

“本来不想用才删掉,重启之后它又回来了”这类事故,原因就在这里。

工作原理

分为两层。

第一层是部署控制器和 AddOn。/var/lib/rancher/k3s/server/manifests 下的每一个文件(包括子目录)都会成为 kube-system 命名空间中的一个 AddOn 对象,名称是文件名去掉扩展名。控制器会以类似 kubectl apply 的方式应用文件,并把内容哈希写入 AddOn 的 spec.checksum。 触发应用的时机只有两个——k3s 启动,以及文件内容变化。实测发现,即使在集群中删除对象,或者只是 touch 文件,都不会重新应用;修改其中的值后,几秒钟就以新的 UID 重新生成了。反过来,即使删除文件,集群中的对象和 AddOn 也会保留。文档也明确写道,删除文件不会删除资源。

第二层是 helm-controller 和 HelmChart。traefik.yaml 中放的不是 Deployment,而是 HelmChart 对象,helm-controller 看到它之后会启动 helm-install-traefik Job 来安装 Chart。Chart 文件从 API 服务器的 static 路径获取。值的优先级依次为 Chart 默认值、HelmChart valuesContent、HelmChartConfig valuesContent、HelmChart spec.set,后者覆盖前者。

apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik          # 대상 HelmChart 와 이름·네임스페이스가 같아야 한다
  namespace: kube-system
spec:
  valuesContent: |-
    logs:
      general:
        level: DEBUG

该代码块中的韩文注释说明:HelmChartConfig 的名称和命名空间必须与目标 HelmChart 相同。

HelmChartConfig 不是 k3s 会重写的文件,而是另外放置的对象,所以启动时不会被覆盖。应用之后会运行 helm upgrade,release Secret 会增加一个。

关闭的方法也有两种,性质不同。

--disable=traefik     AddOn 을 실제로 제거하고 원본 파일도 지운다. 구성은 기동할 때 읽힌다
traefik.yaml.skip     그 파일을 없는 것처럼 무시한다. 이미 만든 객체는 지우지 않는다

该代码块中的韩文说明依次为:--disable=traefik 会真正移除 AddOn 并同时删除原文件,配置在启动时读取;traefik.yaml.skip 会让该文件像不存在一样被忽略,但不会删除已经创建的对象。

配置从 /etc/rancher/k3s/config.yaml 和 config.yaml.d/*.yaml(按名称排序)读取,相同的键以最后一个值为准,在键末尾加上 + 则追加到列表中。如果文件和 CLI 参数有相同的键,则 CLI 优先。传给安装脚本的 INSTALL_K3S_EXEC 会作为 systemd 单元的参数保存,因此属于 CLI 一侧。

在现场相遇的样子

最常见的情况是,想把 Ingress 换成 nginx 的团队用 kubectl 删除了 traefik。当天风平浪静,到了节点重启或 k3s 升级的那天,traefik 死而复生,占用 80 和 443 端口,新的 Ingress 控制器的 LoadBalancer 就会卡在 Pending。如果不知道原因,就会先去找“是谁安装的”。

第二种情况是直接修改 traefik.yaml 来改值。当时能生效,但下次启动时会被还原成原文件。值要用 HelmChartConfig 修改,移除要用 --disable,这样才经得起重启和升级。

第三种情况是修改了配置文件却没有生效。实测中,在 config.yaml 中写入 write-kubeconfig-mode: "0600" 并重启之后,k3s.yaml 仍然是 644。这是因为安装时传入的 --write-kubeconfig-mode 644 仍然作为单元参数保留着。在 drop-in 中不加 + 就写 disable:,会整体覆盖前一个文件的列表,导致原本关闭的组件被重新安装,也属于同一类错误。

实际工作中真正重要的事

下一项实验要做什么

在 VM 中的 k3s 上记录 AddOn 列表,比较用 kubectl 删除用户 AddOn 与修改文件这两种情况。删除 HelmChart traefik 之后重启,通过 UID 确认它复活,然后依次应用 .skip、drop-in disable、CLI 优先级和 HelmChartConfig,并用表格整理哪些操作需要重启。

参考文档:Managing Packaged Components · Helm (HelmChart·HelmChartConfig) · Configuration Options