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

用 Kubespray 与 Terraform 搭建集群

inventory 是角色分配表,group_vars 是安装内容

在 TT Lab 中继续学习

一句话总结

kubespray 是用来安装 Kubernetes 的一组 Ansible playbook,人需要编写的只有两样:inventory(谁承担什么)和 group_vars(安装什么)。

为什么需要它

kubeadm 是在一台节点上组装控制平面的工具。如果有十台服务器,就必须有人在十台上安装运行时、调整内核配置,在第一台上执行 kubeadm init,再在其余节点上按顺序执行 join。一旦开始用 shell 脚本来写这件事,很快就会变成“运行两次就会坏掉的脚本”。kubespray 把这种重复工作搬进了 Ansible role。官方对比文档说明,kubespray 负责“操作系统一侧的通用配置管理”,关于集群生命周期的知识,则是从 v2.3 起通过在内部调用 kubeadm 来借用。也就是说,用 kubespray 搭建的控制平面,内部也是 kubeadm 创建的静态 Pod,区别在于它前后的主机准备、etcd、CNI、附加组件由谁来做。

工作原理

inventory 是角色分配表。 kubespray 文档规定的组有三个。

kube_control_plane   API 서버·스케줄러·컨트롤러 매니저가 뜨는 노드
kube_node            파드가 올라가는 노드(워커)
etcd                 etcd 멤버. 장애 대비에는 3대 이상, 반드시 홀수
k8s_cluster          kube_node + kube_control_plane (+ calico_rr) — kubespray 가 실행 중에 만든다

该代码块中的韩文说明依次为:kube_control_plane 是运行 API 服务器、调度器、控制器管理器的节点;kube_node 是运行 Pod 的节点(工作节点);etcd 是 etcd 成员,为了容灾至少要有 3 台,而且必须是奇数;k8s_cluster 是 kube_node 加 kube_control_plane(再加 calico_rr),由 kubespray 在运行过程中创建。

k8s_cluster 有一个陷阱。v2.32.0 的示例 inventory.ini 没有写这个组,而是由 boilerplate.yml 的 dynamic_groups role 在 playbook 内部用 group_by 创建。所以 playbook 之外的工具并不知道这个组。在这台 VM 上实测,即使在 group_vars/k8s_cluster/k8s-cluster.yml 中写了 kube_version,ansible-inventory --host node1 也会把它显示为 null,而用 ansible ... -m debug 查询时,group_vars/all 中的值会胜出。安装本身没有问题,但想在安装前确认值的工具会撒谎。所以本课程在 [k8s_cluster:children] 中写入 kube_control_plane 和 kube_node——这是旧示例的做法,即使 kubespray 在运行时再创建同一个组,结果也是一样的。

如果一个节点同时位于 kube_control_plane 和 kube_node,控制平面也会承担工作。如果不想单独设置 etcd,就把 kube_control_plane 整个放进 [etcd:children](stacked etcd)。本课程只有一台 VM,所以把同一个节点放入三个组。

组名连拼写都是契约。playbook 最前面的 boilerplate.yml 通过 dynamic_groups role 只会把旧名称(kube-master、kube-node)迁移为新名称,其他名称它一概不认识。接下来 validate_inventory role 会检查 inventory,实测发现,如果写成 [masters],“如果 kube_control_plane 为空就停止”这项检查反而会通过。因为不存在的组,groups.get() 返回的是 None,而 None 与空列表不同。失败发生在紧接着的下一项检查“如果 etcd 数量为偶数就停止”,报出 object of type 'dict' has no attribute 'kube_control_plane'。错误并没有说明组名,所以在运行安装之前,用 ansible-inventory --graph 亲眼看一下树状结构,是最便宜的确认办法。

group_vars 是安装内容。 示例分为两支。

group_vars/all/*.yml            etcd 를 포함한 모든 노드 — 프록시, 오프라인 저장소, etcd 설정
group_vars/k8s_cluster/*.yml    클러스터 노드 — kube_version, container_manager, kube_network_plugin, 대역, 애드온
group_vars/kube_control_plane.yml   컨트롤 플레인만

该代码块中的韩文说明依次为:group_vars/all/.yml 适用于包括 etcd 在内的所有节点——代理、离线仓库、etcd 配置;group_vars/k8s_cluster/.yml 适用于集群节点——kube_version、container_manager、kube_network_plugin、网段、附加组件;group_vars/kube_control_plane.yml 只适用于控制平面。

kubespray 文档中的变量层级表很简短。inventory 的 group_vars 用得最多,host_vars 是针对单个节点的例外,extra vars(-e)始终胜出。而且文档写明,-e 是在覆盖 kubespray 没有对用户做出承诺的内部变量时使用的。按照 Ansible 的规则,如果同一个名称同时存在于 group_vars/all 和 group_vars/k8s_cluster 中,更具体的子组(k8s_cluster)胜出。所以如果有人在 all.yml 中写了版本号,那一行什么作用也没有,却会欺骗下一个人。

从 ansible-core 2.19(Ansible 12)起,条件表达式必须是布尔值。-e key=value 传递的始终是字符串,所以 v2.32.0 的发布说明写道,要把布尔值像 -e '{"drain_nodes": true}' 这样用 JSON 传入。

版本由校验和决定。 kube_version 的默认值是 roles/kubespray_defaults/vars/main/checksums.yml 中 kubelet 校验和列表的第一个键,接受的最低版本则是最后一个键。在 v2.32.0 中,默认是 1.36.4,最低是 1.34.0。没有校验和的版本,至少是无法安装的。而且,如果不在 inventory 中写 kube_version,那么在把 kubespray 升级到新标签的那一刻,Kubernetes 也会一起升级。本课程写下了 1.35.8,并在升级模块中向上升一格到 1.36.4。

在现场相遇的样子

第一个是目录陷阱。kubespray 仓库的 ansible.cfg 把 .ini 放进了 inventory_ignore_extensions。所以如果像 -i inventory/mycluster 这样传入目录,就会跳过 inventory.ini,只留下 “No inventory was parsed” 的警告,以 0 个主机运行。这是在这台 VM 上的实测结果,也是官方文档中的示例总是用 -i inventory/mycluster/inventory.ini 指向文件的原因。

第二个是连接方式写在哪里。只有一台节点时,像 node1 ansible_connection=local 这样附加在 inventory 行上,运行的效果也完全一样。然而到了要增加节点的那天,如果复制那一行,新节点也会以 local 连接,就会出现在控制节点自身上安装两次的事故。如果把连接信息抽出放到 host_vars/<노드>.yml(占位符为节点名称)中,inventory 就只保留角色分配表,增加节点时,只需要在组里加一行名称,再加一个 host_vars 文件(ansible_host、ansible_user)。本课程所有的实验都采用这种形式。

第三个是控制节点的 Ansible 版本。kubespray 在每个标签中用 requirements.txt 固定 Ansible,v2.32.0 要求 ansible==12.3.0(ansible-core 2.19)。文档建议原样安装到虚拟环境中,如果版本不匹配,ansible_version.yml 会在第一个任务处停住。用系统软件包中的 Ansible 来运行而被卡住,是很常见的。

下一项实验要做什么

复制示例,把一台节点放入三个组,并把连接方式抽到 host_vars 中。数一数传入目录和传入文件时主机数有什么不同,把版本固定在 group_vars 之后,确认 all、k8s_cluster 和 -e 谁会胜出。最后观察组名写错的 inventory 会在哪里、以什么话停住,并确认自己的 inventory 能否通过 boilerplate 检查。