k0s 配置的真实来源在哪里
一句话总结
k0s 的集群配置,静态配置时位于每个控制器的文件中,动态配置时位于 API 内的 ClusterConfig 对象中。如果不清楚当前是哪种模式就去改文件,就会出现“改了,却什么都没发生”。
为什么需要它
k0s 的宣传是只需一个二进制文件和一个 k0s.yaml 就能搭建集群。因此运维人员在修改配置时,很自然地会去打开 /etc/k0s/k0s.yaml。这里会出现两类事故。
第一,在默认的静态配置下,k0s 不会监视文件。配置文档写道,运行中可以修改文件,但要让修改生效就必须重启 k0s。如果有多个控制器,就必须让所有控制器的文件保持一致并全部重启。如果只改了一台然后忘了,等到那个节点下次重启时配置才会变化,控制器之间就会持有互不相同的配置。
第二,为了消除这种不便而打开动态配置(--enable-dynamic-config)后,又会出现方向相反的陷阱。根据动态配置文档,首次创建集群时,第一个控制器会把文件作为引导值只读取一次并存入 API,此后 API 中的对象就是所有控制器的事实来源。这时,即使修改文件中的集群级配置并重启,也会被忽略。
工作原理
配置文件只写一部分也可以
k0s config create 会输出所有默认值都已填好的配置。不过 k0s 接受部分配置,缺失的值会用默认值补齐,因此实际的文件里只保留想要修改的内容,读起来更清晰。查看默认值是为了知道“我没有写的值最终会被定为什么”。在本实验的 VM 上,默认输出为 Pod 网段 10.244.0.0/16、Service 网段 10.96.0.0/12、存储 etcd、telemetry 开启。这台 VM 运行在 Kubernetes 之上,默认网段会与宿主集群重叠,所以在文件中改成了 172.20/172.21。
k0s sysinfo 是同一个二进制文件在安装前运行的预检。它会对内存、/var/lib/k0s 文件系统及剩余空间、cgroup 版本和控制器、内核配置逐项给出 pass 或 warning,也可以用 -o json 输出供机器读取。
集群配置与节点配置不同
即使在动态配置下,文件也不会被全部忽略。文档把 spec.api(该节点的 API 服务器)、spec.storage(该节点的 etcd 或 SQLite)、spec.network.controlPlaneLoadBalancing 归为节点级配置,并说明它们仍然从文件中读取。API 服务器和存储必须先启动才能读取对象,所以这是理所当然的设计。另外,network.podCIDR、serviceCIDR、provider 等是创建集群后无法修改的项目,文档说明手动安装时必须写进文件。
其余的集群级配置(worker profile、kube-router 与 kube-proxy 配置、Helm 扩展等)由 kube-system 命名空间中的 clusterconfig/k0s 对象决定。控制器以 Operator 的方式监视这个对象,一旦变化就重新创建相关资源,并把结果记录为事件。k0s config status 会显示这些事件(SuccessfulReconcile、FailedReconciling)。
정적 구성 파일 수정 ──(재시작해야)──▶ 반영
동적 구성 파일 수정 ──(재시작해도)──▶ 클러스터 수준 설정은 무시
ClusterConfig 수정 ──(즉시)──▶ 반영, 이벤트 기록
worker profile 会体现为 ConfigMap
工作节点配置文档中的 worker profile(spec.workerProfiles)是一组对 kubelet 配置的覆盖。k0s 会为每个 profile 创建一个 ConfigMap,工作节点启动时读取通过 --profile 选定的 ConfigMap。实测中,名称为 worker-config-<프로필>-1.36(占位符为 profile 名称),后面带有 Kubernetes 次版本号。因此,可以通过这个 ConfigMap 是否存在,以及 kubeletConfiguration 中的值,直观地确认配置是否已生效。
Helm 扩展会创建 Chart 对象
Helm Chart 文档介绍了两种方法:直接创建 Chart 对象(推荐),或者在 spec.extensions.helm 中声明仓库和 Chart,由 k0s 转换为 Chart 对象。仓库必须是带有 index.yaml 的正式 Helm 仓库,Chart 被删除后 k0s 会移除对应的 release。实测中,通过声明创建的 Chart 名称为 k0s-addon-chart-<차트 이름>(占位符为 Chart 名称),并带有用于移除的终结器(finalizer)。
在现场相遇的样子
下面转述实测中看到的场景(k0s v1.36.4+k0s.0)。
- 在静态配置下,把 worker profile 写进文件后等待 45 秒,ConfigMap 也没有出现;执行
k0s stop、k0s start之后,17 秒就出现了。 - 在已经安装的控制器上再次执行
k0s install controller --enable-dynamic-config,会因“Init already exists”被拒绝,需要加上--force。 - 切换为动态配置后创建的 ClusterConfig 包含文件中的 worker profile,但
spec.network.serviceCIDR显示为 10.96.0.0/12。实际 API 服务器的--service-cluster-ip-range和 kube-dns 地址,仍然按文件中的设置为 172.21。对于无法修改的项目,不要相信对象中显示的值,而要在实际的组件上确认。 - 在动态配置下向文件中添加 profile 并且重启之后,对象的
generation依然没变,ConfigMap 也没有出现。对对象执行 patch 之后,1 秒内就出现了。 - 用 merge patch 只发送新的列表来更新对象的
workerProfiles,列表被整体替换,缺失的 profile 对应的 ConfigMap 随即被删除。向列表中追加时,应使用 JSON patch 的 add。 - 保留声明不动,只对 Chart 对象执行
kubectl delete,release 立即被删除,但等待 99 秒,甚至再修改一次 ClusterConfig,它都没有恢复;直到重启 k0s 之后才再次出现。这意味着声明与实际状态可能悄无声息地不一致。
实际工作中真正重要的事
- 先确认模式。 最快的办法是查看服务单元的运行命令行中是否有
--enable-dynamic-config。所有控制器必须处于同一种模式,文档警告说混用会产生冲突。 - 如果是静态配置,让所有控制器的文件保持一致并重启,才算完成一项操作。
- 如果是动态配置,集群级变更要通过对象来做,并用
k0s config status确认结果。文件中只有节点级配置和无法修改的网络值才有意义。 - 是否生效要用结果来确认。 比起命令执行成功,更要看 ConfigMap、Chart、release 是否真的出现或消失。
- 删除 Helm 扩展时要删除声明。 只删 Chart,下次重启时它会回来。反过来,删除声明会移除 release,所以对于带有数据的 Chart,必须先考虑这样做的后果。命名空间则保留了下来。
下一项实验要做什么
从一台以静态配置启动的 k0s 开始。比较默认值与文件,读取预检结果之后,把 worker profile 写进文件,比较重启前后的变化。切换为动态配置,观察 ClusterConfig 被创建,再用同样的方式修改文件,这次确认即使重启也会被忽略。最后在对象中声明 worker profile 和 podinfo Chart,并记录删除声明后还剩下什么。