第 2 章
集群安装与配置
本章讲解 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 | 本地单节点集群,学习用 |
| kind | Docker 里跑 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 行(如果有的话)。

加载内核模块
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 | 关 swap | kubelet 要精确管理内存,有 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 命令。

**注意:生产环境 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?两个原因:
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 为例,网络插件做的事:
- Pod A 发包:
src=10.244.1.2 → dst=10.244.2.3 - Node 1 上的插件拦截,查路由表发现目标在 Node 2
- 把包封装/路由到 Node 2 的物理 IP
- Node 2 上的插件收到,转给 Pod C
不同插件用不同技术实现这个翻译:
| 插件 | 技术 | 特点 | 推荐场景 |
|---|---|---|---|
| Calico | BGP 路由 | 直接告诉路由器怎么走,支持 NetworkPolicy | CKA 考试首选 |
| Flannel | VXLAN 隧道 | 最简单,但不支持 NetworkPolicy | 纯学习 |
| Cilium | eBPF | 内核级处理,功能最全 | 生产进阶 |
安装就一行命令,每个插件项目都提供现成的 YAML 文件:
# Calico
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
装完等几分钟,kubectl get nodes 状态从 NotReady 变成 Ready——集群搭好了。

小结:初始化 Control Plane 需要做什么
| # | 步骤 | 命令 | 结果 |
|---|---|---|---|
| 1 | 初始化集群 | kubeadm init --pod-network-cidr=... --control-plane-endpoint=... | 生成证书、启动 API Server / etcd / Scheduler / Controller Manager |
| 2 | 配置 kubectl | cp admin.conf ~/.kube/config | kubectl 能连上集群,但节点状态 NotReady |
| 3 | 安装网络插件 | kubectl apply -f calico.yaml | Pod 网络通了,节点变成 Ready |
三步缺一不可:init 创建集群但没有用户能操作,配置 kubectl 后能操作但 Pod 网络不通,装完网络插件集群才真正可用。
第四步:加入 Worker 节点
对于准备作为Worker节点的新机器,同样首先执行第一步环境准备,关 swap、加载内核模块、配置内核网络参数、装 containerd + 改 SystemdCgroup、装 kubeadm/kubelet/kubectl + 锁版本。
在 cp 上执行 token create ,得到 join 命令:
kubeadm token create --print-join-command

在 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)

join 之后发生的事:
- Worker 上的 kubelet 启动,用 token 向 API Server 发起 TLS Bootstrap(证书签名请求)
- CP 自动签发证书,kubelet 拿到自己的客户端证书,建立持久连接
- 网络插件(Calico)自动给新节点分配 Pod 网段
最后,在 CP 上 kubectl get nodes 就能看到新节点变成 Ready,两个 worker 成功加入。


小结:加入 Worker 节点的步骤
| # | 步骤 | 说明 |
|---|---|---|
| 1 | 环境准备 | 跟 CP 完全一样:关 swap、内核模块、containerd、kubeadm/kubelet/kubectl |
| 2 | 获取 join 命令 | CP 上 kubeadm token create --print-join-command,token 有效期 24 小时 |
| 3 | 执行 join | Worker 上 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"的语法。

2. 检查系统 Pod 全部 Running
kubectl get pods --all-namespaces
确认这些基础设施 Pod 都正常运行:

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