2

集群安装与配置

·18 分钟

本章讲解 K8s 集群的搭建,对应 CKA 考纲的 Cluster Architecture, Installation & Configuration(25%)。


认识 kubeadm、kubectl 和 kubelet

搭集群和用集群是两件事,用的工具不一样:

工具角色作用
kubeadm集群管理员创建集群、升级集群、管理节点加入
kubectl集群用户部署应用、查状态、排故障——考试全程使用
kubelet节点代理每个节点上都有,接收 API Server 指令,调用容器运行时创建 Pod

三者的关系:kubeadm 负责把集群搭起来,kubelet 负责让每个节点工作,kubectl 负责让你和集群交互。

kubelet 是 systemd 管理的系统进程,不是容器。排障时经常需要检查它:

systemctl status kubelet      # 看状态
journalctl -u kubelet         # 看日志

其他工具了解即可,考试不考:

工具用途
Minikube本地单节点集群,学习用
kindDocker 里跑 K8s,适合 CI
kops / Kubespray自动化集群部署(AWS / Ansible)
GKE / EKS / AKS云厂商托管 K8s 服务

kubectl 与认证机制

kubectl 是纯客户端

一个常见误解:kubectl 装上就能用。实际上 kubectl 是一个纯客户端工具——它不知道集群在哪、不知道你是谁。你可以在任何一台电脑上装 kubectl,然后连远程的集群,前提是你有认证信息。

kubectl 需要三样东西才能工作:

信息含义
API Server 地址集群入口在哪(IP + 端口)
客户端证书证明"我是谁",有权操作集群
CA 证书验证对面确实是你的集群,不是冒充的

这三样东西全部存在一个文件里:~/.kube/config(即 kubeconfig)。

认证文件从哪来?

kubeadm init 自动生成的。

kubeadm init 做的事情远不止"启动几个容器"——它会生成一整套 PKI 证书体系:

kubeadm init
  → 生成 CA 根证书(/etc/kubernetes/pki/ca.crt)
  → 用 CA 签发 API Server 证书
  → 用 CA 签发 kubelet 客户端证书
  → 用 CA 签发 admin 用户证书
  → 把 admin 证书 + 集群地址打包成 /etc/kubernetes/admin.conf

所以初始化之后要执行的这三行命令:

mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

本质上就是:把 kubeadm 生成的 admin 认证文件复制到 kubectl 默认读取的位置。

为什么不自动配好?因为安全——admin.conf 是最高权限(相当于 root),kubeadm 不会假设你就要用 admin 身份操作。

Context:多集群切换

一个 kubeconfig 里可以存多个集群的信息。context 就是"用哪个身份访问哪个集群的哪个命名空间":

# 查看所有 context
kubectl config get-contexts

# 切换 context
kubectl config use-context <context名>

CKA 考试提醒:考试环境有多个集群,每道题开头会告诉你先切 context。忘了切就在错误的集群上操作,白丢分。


实操:kubeadm 搭建 K8s 集群

第一步:准备工作

这一步把 Linux 系统调整到能跑 K8s 的状态。考试环境已预装好,不会让你从零做。但搞清楚每一步在干什么,排障的时候才知道去哪查。

关 swap

swapoff -a

K8s 要精确管理每个 Pod 的内存分配。如果操作系统偷偷把内存数据写到磁盘(swap),kubelet 对内存的计算就不准了。K8s 直接禁止 swap——kubelet 启动时会检查,有 swap 就拒绝启动。

验证:free -h 看 Swap 行是不是全 0。永久关闭需要编辑 /etc/fstab 注释掉 swap 行(如果有的话)。

swapoff -a 验证

加载内核模块

modprobe overlay
modprobe br_netfilter
  • overlay:容器镜像是分层的(base 层 + app 层),overlay 文件系统让这些层叠在一起看起来像一个完整的文件系统。containerd 依赖它
  • br_netfilter:让 Linux 网桥上的流量经过 iptables 处理。没有它,kube-proxy 的网络规则(包括 NetworkPolicy)不生效

写入配置文件让重启后也自动加载:

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

配置内核网络参数

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
sudo sysctl --system

三个参数:

  • bridge-nf-call-iptables:网桥流量走 iptables,让 kube-proxy 的规则能生效
  • bridge-nf-call-ip6tables:同上,IPv6 版
  • ip_forward:允许 Linux 转发网络包——Pod A 在 Node 1,Pod B 在 Node 2,中间要转发

内核模块与网络参数配置

装 containerd

容器运行时,kubelet 靠它来实际创建和运行容器。从 Docker 官方仓库安装,装完要改一个配置:

containerd config default | sudo tee /etc/containerd/config.toml
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd

为什么改 SystemdCgroup? K8s 用 cgroup 限制每个 Pod 的资源。kubelet 默认用 systemd 管理 cgroup,containerd 默认用 cgroupfs——两边不一致会导致节点状态不稳定。改成 true 让它们对齐。

装 kubeadm / kubelet / kubectl

从 Kubernetes 官方仓库安装,装完锁定版本:

sudo apt-mark hold kubelet kubeadm kubectl

为什么锁版本?K8s 升级有严格步骤(先升 kubeadm → drain → apply),被 apt upgrade 意外升了某个组件,版本不一致会导致集群故障。

小结:准备工作做了什么

#做了什么步骤?为什么要这样做?
1关 swapkubelet 要精确管理内存,有 swap 直接拒绝启动
2加载内核模块overlay 支撑容器文件系统,br_netfilter 让网络规则生效
3配置内核网络参数允许流量转发和 iptables 过滤,Pod 网络才能通
4装 containerd + 改 SystemdCgroup提供容器运行时,且 cgroup 驱动必须和 kubelet 对齐
5装 kubeadm/kubelet/kubectl + 锁版本获取集群搭建工具,锁版本防止意外升级破坏集群

第二步:初始化 Control Plane

准备工作做完,真正搭集群就一条命令:

sudo kubeadm init \
  --pod-network-cidr=192.168.0.0/16 \
  --control-plane-endpoint=<本机IP>

参数由环境和选型决定:

参数从哪来
--pod-network-cidr你选的网络插件决定——Calico 默认 192.168.0.0/16,Flannel 默认 10.244.0.0/16
--control-plane-endpoint本机 IP,多 CP 节点时用负载均衡器 VIP

执行后 kubeadm 会:生成 PKI 证书 → 启动 etcd → 启动 API Server / Scheduler / Controller Manager → 输出 kubectl 配置命令和 kubeadm join 命令。

kubeadm init 完整输出

**注意:生产环境 token 和 CA hash 绝对不能泄露,有了这两样东西任何人都能往你集群里加节点。**学习环境无所谓。

来看输出,每一段都对应一些知识点

[certs] Generating "ca" certificate and key          ← 生成 CA 根证书
[certs] Generating "apiserver" certificate and key   ← 用 CA 签发各组件证书
[certs] Generating "apiserver-kubelet-client" ...
[certs] Generating "etcd/ca" ...

👆 这就是笔记里写的 PKI 证书体系——你亲眼看到它生成了。

[kubeconfig] Writing "admin.conf" kubeconfig file    ← 生成了那个认证文件!
[kubeconfig] Writing "kubelet.conf" ...
[kubeconfig] Writing "controller-manager.conf" ...

👆 admin.conf 就是这里生成的——等下你要复制到 ~/.kube/config

[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"

👆 控制平面三大组件作为 static Pod 启动。

[mark-control-plane] ... adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]

👆 给这个节点打了 Taint(第一章学的概念)——意思是"我是 Control Plane,别往我身上调度普通 Pod"。因为你只有一台机器,后面可能需要去掉这个 taint 才能跑应用。

init 完成后配置 kubectl:

mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

此时 kubectl get nodes 应该能看到一个 NotReady 的节点——因为还没装网络插件。

kubectl get nodes 显示 NotReady

为什么不自动配好kubectl?两个原因:

1. 不知道给谁。 kubeadm init 是用 sudo(root 身份)跑的。如果它自动复制,只会复制到 /root/.kube/config——root 的 home 目录。但你日常操作是用 yustia 用户,root 的 config 对你没用。kubeadm 不知道你想给哪个用户用。

2. 不该默认给最高权限。 admin.conf 是集群最高权限(cluster-admin),能做任何事,包括删 namespace、改 RBAC、甚至删整个集群。正确做法是:

admin.conf → 给集群管理员,锁在保险箱里
自定义用户证书 → 给日常操作,只有必要的权限

当前学习阶段,直接用 admin 没问题。但生产环境如果每个开发者都拿着 admin.conf 操作,一个 kubectl delete namespace production 手滑就全没了。

所以 kubeadm 故意让你手动配置,给谁用什么权限,是一种是安全设计。

小结:kubeadm init 做了什么

#步骤做了什么
1[certs] 生成证书创建 CA 根证书,用它签发 API Server、kubelet、etcd 等所有组件的证书
2[kubeconfig] 生成配置生成 admin.conf、kubelet.conf 等——这就是 kubectl 需要的认证文件
3[control-plane] 启动组件创建 API Server、Controller Manager、Scheduler 的 static Pod manifest,由 kubelet 拉起
4[mark-control-plane] 标记节点给节点打 Label(标记为 Control Plane)和 Taint(NoSchedule——不接受普通 Pod 调度)

第三步:安装网络插件

K8s 规定每个 Pod 有自己的 IP,任何 Pod 能直接访问任何其他 Pod。但 Pod 的 IP 是虚拟的,物理网络设备不认识——网络插件就是在物理网络之上建一层虚拟网络,充当翻译层。

Node 1 (192.168.1.100)              Node 2 (192.168.1.101)
┌──────────────────┐               ┌──────────────────┐
│  Pod A           │               │  Pod C           │
│  10.244.1.2      │               │  10.244.2.3      │
└────────┬─────────┘               └────────┬─────────┘
         │                                  │
    物理网络只认识 192.168.1.x,不知道 10.244.x.x 怎么走
         │                                  │
         └───── 网络插件负责翻译这段通信 ──────┘

以 Pod A 访问 Pod C 为例,网络插件做的事:

  1. Pod A 发包:src=10.244.1.2 → dst=10.244.2.3
  2. Node 1 上的插件拦截,查路由表发现目标在 Node 2
  3. 把包封装/路由到 Node 2 的物理 IP
  4. Node 2 上的插件收到,转给 Pod C

不同插件用不同技术实现这个翻译:

插件技术特点推荐场景
CalicoBGP 路由直接告诉路由器怎么走,支持 NetworkPolicyCKA 考试首选
FlannelVXLAN 隧道最简单,但不支持 NetworkPolicy纯学习
CiliumeBPF内核级处理,功能最全生产进阶

安装就一行命令,每个插件项目都提供现成的 YAML 文件:

# Calico
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml

装完等几分钟,kubectl get nodes 状态从 NotReady 变成 Ready——集群搭好了。

Calico 安装完成,节点状态 NotReady → Ready

小结:初始化 Control Plane 需要做什么

#步骤命令结果
1初始化集群kubeadm init --pod-network-cidr=... --control-plane-endpoint=...生成证书、启动 API Server / etcd / Scheduler / Controller Manager
2配置 kubectlcp admin.conf ~/.kube/configkubectl 能连上集群,但节点状态 NotReady
3安装网络插件kubectl apply -f calico.yamlPod 网络通了,节点变成 Ready

三步缺一不可:init 创建集群但没有用户能操作,配置 kubectl 后能操作但 Pod 网络不通,装完网络插件集群才真正可用。

第四步:加入 Worker 节点

对于准备作为Worker节点的新机器,同样首先执行第一步环境准备,关 swap、加载内核模块、配置内核网络参数、装 containerd + 改 SystemdCgroup、装 kubeadm/kubelet/kubectl + 锁版本。

在 cp 上执行 token create ,得到 join 命令:

kubeadm token create --print-join-command

kubeadm token create 输出

在 worker 上执行这个命令(注意加sudo):

sudo kubeadm join <CP地址>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>

三个参数各有分工:

  • API Server 地址<CP-IP>:6443):告诉 Worker 去哪找 CP
  • Token--token):临时认证凭证,默认 24 小时过期,过期后要重新 token create
  • CA 证书哈希--discovery-token-ca-cert-hash):让 Worker 验证 CP 身份,防中间人攻击(冒充 CP)

ubuntu2 成功加入集群

join 之后发生的事:

  1. Worker 上的 kubelet 启动,用 token 向 API Server 发起 TLS Bootstrap(证书签名请求)
  2. CP 自动签发证书,kubelet 拿到自己的客户端证书,建立持久连接
  3. 网络插件(Calico)自动给新节点分配 Pod 网段

最后,在 CP 上 kubectl get nodes 就能看到新节点变成 Ready,两个 worker 成功加入。

三节点全部 Ready

kubectl get nodes -o wide 详细信息

小结:加入 Worker 节点的步骤

#步骤说明
1环境准备跟 CP 完全一样:关 swap、内核模块、containerd、kubeadm/kubelet/kubectl
2获取 join 命令CP 上 kubeadm token create --print-join-command,token 有效期 24 小时
3执行 joinWorker 上 sudo kubeadm join ...,kubelet 自动注册 + 证书签发 + 网络分配

收尾检查

集群搭完 ≠ 集群可用。还需要做三件事:

1. 去掉 CP 的 Taint

kubectl taint nodes --all node-role.kubernetes.io/control-plane-   # 清除所有节点taint
kubectl taint nodes ubuntu node-role.kubernetes.io/control-plane-  # 只清除ubuntu(cp)

kubeadm init 自动给 CP 打了 NoSchedule taint——"别往我身上调度普通 Pod"。生产环境这是对的(CP 应该只跑控制平面组件),但学习环境节点少,去掉后 CP 也能跑应用。注意末尾的 减号 - 是"删除 taint"的语法。

去掉 CP 的 Taint

2. 检查系统 Pod 全部 Running

kubectl get pods --all-namespaces

确认这些基础设施 Pod 都正常运行:

所有系统 Pod Running

Pod作用没跑起来的后果
CoreDNS集群内部 DNS 解析Pod 之间无法通过 Service 名字互相访问
calico-node每个节点的网络代理(DaemonSet)Pod 网络不通
calico-kube-controllers管理 Calico 网络策略NetworkPolicy 不生效
kube-proxy每个节点的 Service 转发规则(DaemonSet)ClusterIP / NodePort 不工作

如果有 Pod 卡在 ContainerCreatingPending,用 kubectl describe pod -n kube-system <pod名> 看 Events 找原因。

3. 配置 crictl

sudo crictl config --set runtime-endpoint=unix:///run/containerd/containerd.sock \
  --set image-endpoint=unix:///run/containerd/containerd.sock

crictl 是直接操作容器运行时的调试工具(类似 docker CLI 但不依赖 Docker),排障常用:

sudo crictl ps          # 列出运行中的容器
sudo crictl images      # 列出已拉取的镜像
sudo crictl logs <id>   # 看容器日志

小结:收尾检查

#步骤为什么
1去掉 CP 的 Taint学习环境让 CP 也能调度 Pod,生产环境保留
2检查系统 Pod确认 CoreDNS / Calico / kube-proxy 全部 Running,基础设施健康
3配置 crictl为后续容器级别的调试做准备
💬

留言区待配置

部署 Twikoo 后端后,设置环境变量 NEXT_PUBLIC_TWIKOO_ENV_ID