无处可登录的操作系统 — Talos 为何去掉了 shell
一句话总结
Talos Linux 是一个没有 shell、SSH 和软件包、只运行 Kubernetes 的操作系统,你不必登录节点,而是通过每个节点上开放的 gRPC API 来读取和修改一切。
为什么需要它
当节点数量达到三十台左右,就一定会出现这样的事。出故障时,有人通过 SSH 登录某个节点,修改 sysctl、调整 kubelet 参数,把紧急的火情扑灭。没有留下任何记录,三个月后只有那个节点表现得很奇怪。即使接入了配置管理工具,“在管理工具之外动手脚”依然是可能的。
Talos 不靠规则,而靠结构来杜绝这个问题。理念文档写道,既没有 shell,也没有 SSH,没有 GNU 工具,连 busybox 都没有。内核启动的第一个进程不是 systemd,而是用 Go 编写的 machined,根文件系统是 SquashFS,所以不可写入。没有可以进去的地方,也就无从动手。取而代之的是,节点的全部状态由一份声明式的机器配置决定,而修改的途径只有一个 API。
工作原理
组件很少。apid 在 50000 端口接收 gRPC 请求并转交给 machined,如果是控制平面,还会转发给其他节点的 apid。machined 只运行既定的服务(containerd、etcd、kubelet、trustd 等),不接受任意的用户服务。talosctl 是这个 API 的客户端,-e 决定从哪个节点的 apid 进入,-n 决定由哪个节点来回答。
内部状态像 Kubernetes 一样用资源和控制器来表示。服务、集群成员、机器配置都是资源,所以可以用相同的命令读取。
talosctl -n 10.5.0.2 services # machined 가 돌리는 서비스
talosctl -n 10.5.0.2 get members # 클러스터 멤버(discovery)
talosctl -n 10.5.0.3 get mc v1alpha1 -o yaml # 머신 설정도 버전이 붙은 리소스
talosctl -n 10.5.0.3 logs kubelet # 셸 대신 로그도 API 로
修改配置的命令有 apply-config、edit machineconfig、patch machineconfig 三个,应用方式通过 --mode 选择。根据配置编辑文档(v1.13),auto 在需要时重启,no-reboot 立即应用,但如果是需要重启的字段则拒绝,staged 在下次重启时应用,try 立即应用,如果在规定时间内没有其他变更,就会回退。nodeLabels、kubelet、network 这类项目位于无需重启即可生效的列表中。补丁是只写配置一部分的 strategic merge YAML,列表通常是追加,但 cluster.network.podSubnets 和 serviceSubnets 会被覆盖。要删除特定的键,使用 $patch: delete。
在本地,可以按照 Docker 指南,用 talosctl cluster create docker 把节点作为容器启动。在容器模式下,upgrade、reset 这类 API 不适用。
在现场相遇的样子
这是在本课程的 VM 上实测的结果。对节点容器执行 docker exec ... sh,会以 exit 127 报出 executable file not found,节点 IP 的 22 端口被拒绝,只有 50000 端口是开放的。按习惯“先进去看看”,在第一行就被挡住了。
网段陷阱也如期出现。talosctl 创建的默认网段是 Pod 10.244.0.0/16、Service 10.96.0.0/12,而这台 VM 自身的地址就在宿主集群的 10.244 网段中。所以用 --config-patch 指定了 10.200/10.201 之后再启动。
版本的选择同样是通过实测确定的。用 talosctl v1.14.0 的默认镜像启动后,节点日志里在创建 /etc overlay 时出现了 FSCONFIG_SET_FD failed ... lowerdir+,API 始终没有就绪(宿主内核 6.8)。在同一台 VM 上,v1.13.10 大约 2 分钟后两个节点就都 Ready 了。容器里的 Talos 直接使用宿主内核,所以升级版本时,宿主内核会先成为条件。
最重要的实测是验证的局限。在标签名称中放入空格的补丁,会以 InvalidArgument 被拒绝,配置版本保持不变。而把 kubelet 的 extraArgs 中不存在的标志放进去,却会以“Applied configuration without a reboot”被接受,然后 kubelet 因 unknown flag 每 5 秒重启一次。验证只检查文档格式,并不知道 kubelet 是否认识那个标志。由于没有 shell,原因要到 talosctl services 的 LAST EVENT 和 talosctl logs kubelet 中去找,用 $patch: delete 只删除那个键之后,3 秒内 kubelet 就恢复健康了。
实际工作中真正重要的事
机器配置文件就是节点。 节点上没有任何手工修改,配置资源的 version 是变更历史的骨架。把补丁文件放进仓库,并用同一个文件应用到多个节点,这样的流程就变得很自然。
把 try 模式当作远程变更的保险绳。 像网络这样一旦改错就可能再也连不上 API 的配置,用 try 模式放进去,确认无误之后再重新应用同样的变更使其固定。还要记住,在确认期间如果放入其他变更,回退就会被取消。
不要把通过验证当作成功。 应用之后,一定要重新查看服务状态和节点 Ready。另外,--dry-run 只显示合并后的结果,并不做验证(实测)。
要接受应急处理手段只有 API 这一条。 如果失去了能够到达 apid 的网络和 talosconfig 证书,实际上就没有办法修复节点了。证书的保管和访问路径,比 SSH 密钥管理更加重要。
下一项实验要做什么
在 VM 中用 Docker 启动的两个 Talos 节点上,留下没有 shell 的证据,读取服务、成员和机器配置,通过 API 获取 kubeconfig 并确认网段。然后在工作节点上用 no-reboot 加上标签,并观察用 try 加上的标签在 30 秒后被回退。最后依次放入被验证拒绝的补丁,以及被接受但会让 kubelet 崩溃的补丁,通过日志找到原因并回退,再整理成报告。