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

用 Kubespray 与 Terraform 搭建集群

在一台 VM 上设计并检查五节点集群

在 TT Lab 中继续学习

目标

设计一个 3 台控制平面(兼作 etcd)加 2 台工作节点的 inventory,并在一台 VM 上确认即使没有真实节点也能确认的内容——组的布局、kubespray 的 inventory 检查、 etcd 奇数规则与法定人数、API 服务器负载均衡方式,以及增加和移除节点时 playbook 所针对的位置。

为什么重要

从单节点集群扩展到多台时,改变的不是安装命令,而是设计。控制平面设几台、是否兼作 etcd、 工作节点连接哪个 API 服务器、增加和移除节点时把哪个 playbook 限定到哪里运行,这些全都由 inventory 和几个变量决定。 而且这些决定在安装开始之后很难更改。在创建真实节点之前,先用工具检查 inventory,就能提前避免安装过程中相当一部分的停滞。 实际用多台真实 VM 来做节点加入、故障和滚动升级的实验,等为一个会话提供多台 VM 的功能准备好之后,会接在本模块之后。

步骤

  1. 把 /opt/ks/kubespray/inventory/sample 复制到 /root/ks/inventory/ha,在 /root/ks/inventory/ha/inventory.ini 中把 cp1、cp2、cp3 放入 kube_control_plane(etcd 以 kube_control_plane 为子组),把 w1、w2 放入 kube_node,并让 k8s_cluster 以这两个组为子组。控制平面不兼作工作节点。在每个节点的 /root/ks/inventory/ha/host_vars/<노드>.yml(占位符为节点名称)中设置 ansible_host、ip(从 cp1=192.0.2.11 到 w2=192.0.2.15 依次递增)以及 ansible_user: ubuntu。
  2. 在 /opt/ks/kubespray 中运行 ansible-playbook -i /root/ks/inventory/ha/inventory.ini playbooks/boilerplate.yml -e ansible_connection=local,把完整输出保存到 /root/ks/ha/validate.log。五个节点的 PLAY RECAP 都必须是 failed=0。
  3. 在 /root/ks/inventory/ha-even 中复制第 1 步的 inventory,但把 etcd 改为只设置 cp1、cp2 两台(在 [etcd] 中写两个名称),然后用同样的方法(-e ansible_connection=local)运行 boilerplate。在 /root/ks/ha/even.json 中写入 failed_task(失败的任务名称,不带 role 前缀)、etcd_members(该 inventory 中 etcd 主机的数量)、quorum(该数量的过半数)、tolerated_failures(失去几台之内还能写入)。
  4. 在 /root/ks/ha/quorum.json 中,对 etcd 成员数 1、3、5、7 分别写入 quorum(过半数)和 tolerated_failures(在保持写入的前提下可以失去的成员数),格式为 {"1": {"quorum": .., "tolerated_failures": ..}, "3": {...}, ...}。
  5. 阅读 kubespray 的 roles/kubespray_defaults/defaults/main/main.yml 和 docs/operations/ha-mode.md,在 /root/ks/ha/lb.json 中写入 localhost_lb(没有定义外部负载均衡器时 loadbalancer_apiserver_localhost 的结果,布尔值)、lb_type(loadbalancer_apiserver_type 的默认值)、lb_port(该本地代理所使用的端口——没有 loadbalancer_apiserver_port 时所遵循的值,数字)、who_uses_it(使用该本地代理的节点组:"kube_node" 或 "kube_control_plane" 中文档所说的那一个)。
  6. 把 w3(host_vars 中为 192.0.2.16)加入第 1 步 inventory 的 kube_node。然后在 /opt/ks/kubespray 中,用 inventory /root/ks/inventory/ha/inventory.ini 运行 scale.yml --list-hosts --limit w3 和 remove-node.yml --list-hosts -e node=w2,并在 /root/ks/ha/plan.json 中写入 scale_node_play_hosts(scale.yml 中以 "...(node)" 结尾的 play 所针对的主机排序后的数组)、remove_confirm_hosts(remove-node.yml 的 "Confirm node removal" play 所针对的主机排序后的数组)。在 /root/ks/ha/runbook.sh 中,每行写一条:增加 w3 的命令、移除 w2 的命令、逐台升级节点的升级命令(不要执行)。
  7. 阅读 /opt/ks/kubespray/ansible.cfg,在 /root/ks/ha/facts.json 中写入 cache_plugin(fact_caching 的值)、cache_dir(fact_caching_connection 的值)、cache_timeout_sec(fact_caching_timeout,数字)、refresh_playbook(文档所说的、在使用 --limit 之前要不加限制地运行的 playbook 路径,相对于 kubespray 仓库的相对路径)。

参考

3 台控制平面,2 台工作节点

把 /opt/ks/kubespray/inventory/sample 复制到 /root/ks/inventory/ha,在 /root/ks/inventory/ha/inventory.ini 中把 cp1、cp2、cp3 放入 kube_control_plane(etcd 以 kube_control_plane 为子组),把 w1、w2 放入 kube_node,并让 k8s_cluster 以这两个组为子组。控制平面不兼作工作节点。在每个节点的 /root/ks/inventory/ha/host_vars/<노드>.yml(占位符为节点名称)中设置 ansible_host、ip(从 cp1=192.0.2.11 到 w2=192.0.2.15 依次递增)以及 ansible_user: ubuntu。

与第 1 个模块中的单节点 inventory 形态相同,只是组里的名称增多,host_vars 文件增多。不把连接方式写在 inventory 行中的原因,在这里就显现出来了。ip 是 kubespray 用来连接 API 服务器和 etcd 的地址,ansible_host 是 Ansible 通过 ssh 到达的地址——如果管理网络与服务网络不同,两者就会不同。192.0.2.0/24 是为文档预留的网段,所以实际上无法到达。

不用 ssh,只做 inventory 检查

在 /opt/ks/kubespray 中运行 ansible-playbook -i /root/ks/inventory/ha/inventory.ini playbooks/boilerplate.yml -e ansible_connection=local,把完整输出保存到 /root/ks/ha/validate.log。五个节点的 PLAY RECAP 都必须是 failed=0。

boilerplate 不收集 facts,只检查 inventory 和变量,所以把连接覆盖为 local 之后,即使没有真实节点也能运行。-e 优先于 host_vars,所以只有在这次运行中连接方式才会改变。这是在安装开始之前审查 inventory 时,可以使用的最便宜的确认办法。

如果 etcd 只有两台

在 /root/ks/inventory/ha-even 中复制第 1 步的 inventory,但把 etcd 改为只设置 cp1、cp2 两台(在 [etcd] 中写两个名称),然后用同样的方法(-e ansible_connection=local)运行 boilerplate。在 /root/ks/ha/even.json 中写入 failed_task(失败的任务名称,不带 role 前缀)、etcd_members(该 inventory 中 etcd 主机的数量)、quorum(该数量的过半数)、tolerated_failures(失去几台之内还能写入)。

etcd 每次写入都需要过半数(法定人数)的同意。过半数是成员数除以 2 的商再加 1。如果是两台,过半数为 2,所以只失去一台就会停止;只有一台时,失去一台也会停止,所以两台并不比一台强,只是增加了成员。kubespray 通过 inventory 检查来拦下这种情况。

最多可以失去几台

在 /root/ks/ha/quorum.json 中,对 etcd 成员数 1、3、5、7 分别写入 quorum(过半数)和 tolerated_failures(在保持写入的前提下可以失去的成员数),格式为 {"1": {"quorum": .., "tolerated_failures": ..}, "3": {...}, ...}。

过半数是 n//2 + 1,可容忍的故障数是 n - 过半数。增加成员,可容忍的数量会增加,但每次写入要等待更多成员的同意。etcd 文档对生产集群推荐 3 台或 5 台,并要求不要超过 7 台,原因就在这张表里。

工作节点连接哪个 API 服务器

阅读 kubespray 的 roles/kubespray_defaults/defaults/main/main.yml 和 docs/operations/ha-mode.md,在 /root/ks/ha/lb.json 中写入 localhost_lb(没有定义外部负载均衡器时 loadbalancer_apiserver_localhost 的结果,布尔值)、lb_type(loadbalancer_apiserver_type 的默认值)、lb_port(该本地代理所使用的端口——没有 loadbalancer_apiserver_port 时所遵循的值,数字)、who_uses_it(使用该本地代理的节点组:"kube_node" 或 "kube_control_plane" 中文档所说的那一个)。

如果控制平面有多台,工作节点的 kubelet 和 kube-proxy 就必须决定连接哪个 API 服务器。kubespray 的默认做法是:如果没有另外定义外部负载均衡器(loadbalancer_apiserver),就在每个工作节点上启动 nginx 代理,在 localhost 上接收请求,再分发给所有 API 服务器。文档说明,这种方式不如专用 LB 高效,但在不便管理 VIP 的地方很实用。

增加和移除节点的命令所针对的位置

把 w3(host_vars 中为 192.0.2.16)加入第 1 步 inventory 的 kube_node。然后在 /opt/ks/kubespray 中,用 inventory /root/ks/inventory/ha/inventory.ini 运行 scale.yml --list-hosts --limit w3 和 remove-node.yml --list-hosts -e node=w2,并在 /root/ks/ha/plan.json 中写入 scale_node_play_hosts(scale.yml 中以 "...(node)" 结尾的 play 所针对的主机排序后的数组)、remove_confirm_hosts(remove-node.yml 的 "Confirm node removal" play 所针对的主机排序后的数组)。在 /root/ks/ha/runbook.sh 中,每行写一条:增加 w3 的命令、移除 w2 的命令、逐台升级节点的升级命令(不要执行)。

--list-hosts 不会改变任何东西,只显示每个 play 针对谁。scale.yml 只会安装到新节点上,所以要用 --limit 缩小范围,而 remove-node.yml 通过 -e node=<节点名称> 接收要移除的节点。kubespray 文档建议,在使用 --limit 之前,先不加限制地运行一次 facts.yml 来刷新 facts 缓存。逐台升级,要用 serial 变量来控制。

为什么要在 --limit 之前运行 facts.yml

阅读 /opt/ks/kubespray/ansible.cfg,在 /root/ks/ha/facts.json 中写入 cache_plugin(fact_caching 的值)、cache_dir(fact_caching_connection 的值)、cache_timeout_sec(fact_caching_timeout,数字)、refresh_playbook(文档所说的、在使用 --limit 之前要不加限制地运行的 playbook 路径,相对于 kubespray 仓库的相对路径)。

如果用 --limit 只针对新节点,那次运行不会收集其他节点的 facts,而是使用缓存。然而配置文件(例如 /etc/hosts、etcd 成员列表、负载均衡器后端)是由所有节点的 facts 生成的。如果缓存是空的或已经过时,新节点就会得到错误的地址。请看升级文档中的 Node-based upgrade 一节。