Kubernetes 1.23 这个版本号,放在今天看有点"复古"。但如果你正在维护一套老集群、准备 CKA 考试、或者因为某个中间件版本锁死在 1.23 上,就会发现网上针对这个版本的系统性部署教程反而少得可怜——大多数文章已经涌向 1.28、1.29 了。这篇文章基于我在 Ubuntu 20.04/22.04 上部署 1.23.17 的完整过程整理而成,覆盖从系统参数、容器运行时到控制平面初始化、工作节点加入、Dashboard 部署和常见故障排查的完整链路,所有命令都经过实际验证,你可以直接照着敲。
1. 为什么还在用 1.23:这个版本的特殊定位与选型逻辑
先聊清楚一个前置问题:Kubernetes 1.23 发布于 2021 年 12 月,官方维护在 2023 年 2 月就已经结束,为什么还要专门部署它?
我的判断标准很简单:不是所有环境都需要追新。1.23 这个版本处在两个重要分界点上。第一,它是 dockershim 还在但已经进入 deprecated 阶段的最后一个大版本,1.24 起彻底移除 dockershim,导致很多旧脚本直接失效;第二,它的 API 版本与大量生态组件兼容性极好——比如 Velero、Istio 1.13、Cert Manager 1.9 这些在当时版本下运行得非常稳。很多企业的生产环境因为历史原因锁死在这个版本,新同事接手时最需要的不是"升级方案",而是一份能复现老环境的部署手册。
另外,CKA 考试在 2023-2024 年间仍然允许使用 1.23 版本环境,虽然现在题库已经更新到更新的版本,但如果你手头买到的模拟环境还是 1.23,这篇教程可以直接帮你把实验环境拉起来。
从功能特性上看,1.23 有几个值得记住的点:
- IPv4/IPv6 双栈进入 Beta,如果你有双栈需求,这个版本开始可用;
- HorizontalPodAutoscaler 的 v2beta2 API 成为主流,基于自定义指标的扩缩容更好写;
- CronJob 的稳定版 API(batch/v1)正式可用,替代了旧版 batch/v1beta1;
- 节点亲和、污点容忍等调度能力已经成熟,和现代版本的用法几乎一致。
所以在 1.23 上积累的经验,迁移到新版本时大部分仍然有效。这也是我建议初学者从这个版本入门的原因——它的复杂度比 1.28 低,坑更少,学到的核心概念不会过时。
1.1 版本选择:具体用 1.23.x 的哪个补丁版本
Kubernetes 的补丁版本会持续修复安全漏洞和稳定性问题。1.23 系列最后的补丁版本是 1.23.17,如果你没有特殊原因(比如某个组件强制要求特定小版本),直接选 1.23.17。
组件版本对应关系如下,建议严格锁定,不要混装:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| kubeadm | 1.23.17 | 初始化集群、生成 token |
| kubelet | 1.23.17 | 所有节点必须安装,版本不能高于控制平面 |
| kubectl | 1.23.17 | 客户端工具,建议与集群版本保持一致 |
| containerd | 1.6.x | 1.23 兼容性最好的 CRI 运行时 |
| flannel / calico | flannel v0.20+ 或 calico 3.24+ | 网络插件,按需求选择 |
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 部署前的系统准备:Ubuntu 版本、内核参数与容器运行时
不管你是物理机还是虚拟机,操作系统层面的准备工作决定了集群后续 80% 的稳定性。我在这上面栽过太多次跟头,尤其是直接把刚装好的 Ubuntu 拿来初始化、然后被各种网络问题折磨的情况。
2.1 Ubuntu 版本与基础配置
建议使用 Ubuntu 20.04 LTS 或 22.04 LTS,内核版本 5.4+ 即可满足 1.23 的要求。我个人在 20.04 上跑过十几套集群,也在 22.04 上验证过本教程的流程,两个版本没有本质差异。
安装完成后做四件事:
bash复制# 1. 设置主机名(每个节点不同)
sudo hostnamectl set-hostname k8s-master
# 2. 写入 /etc/hosts,必须包含所有节点的 IP 和主机名映射
cat <<EOF | sudo tee -a /etc/hosts
192.168.1.10 k8s-master
192.168.1.11 k8s-node1
192.168.1.12 k8s-node2
EOF
# 3. 配置静态 IP(如果用 DHCP,节点重启后 IP 变了集群直接废掉)
# 在 /etc/netplan/ 下修改配置文件,Ubuntu 20.04 是 00-installer-config.yaml
# 4. 关闭 swap
sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
关于 swap,很多教程只说"要关",不解释原因。其实是因为 kubelet 的 cgroup 资源统计在 swap 存在时会出现偏差,导致节点资源报告不准确、调度器做出错误决策。1.23 时代 kubelet 默认不支持 swap 开启状态,虽然可以用 --fail-swap-on=false 绕过,但生产环境不推荐。
2.2 内核模块与系统参数
Kubernetes 依赖 iptables 做流量的 NAT 和转发,同时 overlay 网络需要内核支持 overlay 文件系统和 bridge 流量的 netfilter 处理。
bash复制# 加载内核模块
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
然后配置 sysctl 参数:
bash复制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
这里最容易被忽略的是 net.bridge.bridge-nf-call-iptables。如果它没设成 1,集群内的 Service 流量转发会出现诡异问题——Pod 能 ping 通,但 Service 访问超时。原因是 Kubernetes 的 kube-proxy 通过 iptables 规则做 DNAT,而 bridge 桥接的流量默认不经过 iptables,导致规则不生效。
2.3 容器运行时:为什么不选 Docker
1.23 时代很多人还在用 Docker + dockershim 的组合。但在生产环境我强烈建议直接使用 containerd,理由有三点:
- dockershim 在 1.24 中就删除了,你现在用 Docker 存粹是给自己挖坑,升级时被迫迁移;
- containerd 比 Docker 少了 containerd-shim 和 dockerd 这两层进程,资源占用更低;
- crictl 直接对接 containerd,排查 Pod 问题时命令更直观(下一节会讲)。
安装 containerd 1.6.x:
bash复制sudo apt-get update
sudo apt-get install -y containerd.io
# 生成默认配置
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
这里引入今天第一个关键坑:containerd 默认使用的是 cgroupfs 驱动,而 kubelet 推荐使用 systemd 驱动。如果两者不一致,kubelet 启动后会报 failed to run Kubelet: failed to validate kubelet flags 之类的错误,节点状态会一直是 NotReady。
修改配置:
toml复制# 编辑 /etc/containerd/config.toml
# 找到 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
# 将 SystemdCgroup 设为 true
SystemdCgroup = true
另外,国内环境拉取镜像经常超时,建议在同一个配置文件里加上镜像加速:
toml复制[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://docker.m.daocloud.io", "https://dockerproxy.com"]
改完配置后重启 containerd:
bash复制sudo systemctl restart containerd
sudo systemctl enable containerd
我实测在腾讯云、阿里云环境里,用上面这两个加速地址覆盖 90% 以上的镜像拉取场景,很少再遇到超时。
3. 锁定 Kubernetes 组件版本:kubeadm、kubelet、kubectl 安装
Kubernetes 组件通过 apt 仓库发布,但是必须手动锁定版本,否则 apt-get upgrade 会把 kubelet 升级到新版本(比如 1.29),而你集群里的其他组件还是 1.23,版本不匹配直接导致节点无法注册。
3.1 配置 apt 源并安装
bash复制sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
# 添加 Kubernetes apt 仓库(1.23 的 cloud 源)
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
然后安装指定版本:
bash复制sudo apt-get install -y kubeadm=1.23.17-00 kubelet=1.23.17-00 kubectl=1.23.17-00
注意版本号后面一定要加 -00,这是 Debian 包版本的约定格式,不加的话 apt 会提示找不到。
3.2 锁定版本
bash复制sudo apt-mark hold kubeadm kubelet kubectl
这一步做好了,后续系统升级时不会误伤集群组件。如果你管理多套集群,这个习惯尤其重要——我曾经有一台测试机因为忘记 lock,kubelet 自动从 1.23 升到 1.25,集群直接瘫痪,排查了一个多小时才发现是版本漂移。
3.3 验证安装
bash复制kubeadm version # 应该输出 kubeadm version: &version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.17"}
kubelet --version # Kubernetes v1.23.17
kubectl version --client
顺手给 kubectl 配好自动补全和别名,后续操作会舒服很多:
bash复制echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc
source ~/.bashrc
3.4 预拉取镜像:避免 init 卡在拉镜像
kubeadm init 执行时会先拉取控制平面组件镜像,包括 kube-apiserver、kube-controller-manager、kube-scheduler、etcd、coredns 等。这一步在国内网络环境下经常卡住,等半天最后超时。
解决思路是提前用 kubeadm 的 config images pull 命令拉好:
bash复制kubeadm config images list --kubernetes-version v1.23.17
先看看需要哪些镜像,然后手动拉取。如果你用的是上面配置的 containerd 加速源,直接执行:
bash复制kubeadm config images pull --kubernetes-version v1.23.17
如果仓库源不是默认的 k8s.gcr.io,也可以指定 --image-repository registry.aliyuncs.com/google_containers(阿里云镜像源,国内环境常用)。
4. 控制平面初始化:kubeadm init 的参数选择与网络插件落地
终于到了初始化控制平面的一步。这一步是整篇部署流程里最容易出问题的环节,需要仔细理解每个参数的作用。
4.1 kubeadm init 常用参数说明
bash复制sudo kubeadm init \
--apiserver-advertise-address=192.168.1.10 \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.23.17 \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/16
参数逐个说:
--apiserver-advertise-address:指定 API Server 对外广播的 IP,必须是你节点的内网 IP。如果你的机器有多个网卡(比如云服务器同时有内网和公网),这个参数必须显式指定,否则 kubeadm 默认选第一块网卡的 IP,可能选错。--image-repository:镜像仓库地址,国内环境使用阿里云镜像源,否则大概率卡在拉镜像。--kubernetes-version:指定版本。前面 apt 装的是 1.23.17,这里必须写成 v1.23.17,格式带 v。--pod-network-cidr:Pod 网段。这个网段必须和后面网络插件期望的网段一致,最典型的就是 flannel 默认用 10.244.0.0/16,calico 默认用 192.168.0.0/16。你这里写错了,后面网络插件会一直 CrashLoopBackOff,节点永远是 NotReady。--service-cidr:Service 网段,默认 10.96.0.0/16,一般不用改。
4.2 初始化成功后的处理
初始化成功后,终端会输出三件事:
- 一串
kubeadm join命令,把它保存下来,后面工作节点加入要用; - 提示你配置 kubeconfig:
bash复制mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
- 提示你安装网络插件。
4.3 安装网络插件:flannel 还是 calico
1.23 时代的网络插件选择,我的建议是:测试环境用 flannel,生产环境用 calico。原因在于 calico 支持 NetworkPolicy,flannel 不支持(这也是 flannel 唯一的最大短板)。
flannel 安装:
bash复制kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
calico 安装:
bash复制kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.24/manifests/tigera-operator.yaml
Calico 的完整安装涉及 operator 和自定义资源,流程长一些。这里给一个快速验证方式:
bash复制kubectl get pods -n kube-system
看到所有组件(coredns、kube-proxy、flannel 或 calico)都是 Running 状态,控制平面就 OK 了。
4.4 多网卡机器的网络插件坑
如果你的 Ubuntu 机器同时有 eth0 和 eth1(云服务器很常见),calico 默认检测第一块网卡的 IP,很可能选成公网 IP 或错误的专线 IP,导致节点间通信失败。
flannel 可以在配置里指定 --iface=eth0,calico 需要通过 ConfigMap 指定。这个坑尤其隐蔽,症状是 Pod 能创建但跨节点不通。排查时直接 kubectl logs 查看 calico-node 的日志,如果看到类似 Unable to get interface 或错误的 IP,基本就是这个原因。
我建议在初始化之前就把 /etc/hosts 里的节点 IP 全部规范好,确保只有一块网卡承载集群流量,否则后期排查网络问题的成本极高。
5. 工作节点加入:token 过期问题与再生成方法
控制平面就绪后,工作节点就等着加入。这一步步骤少,但有几个隐性坑。
工作节点上需要先完成第 2 节和第 3 节的所有系统准备(包括 containerd、kubeadm、kubelet、kubectl),然后执行控制平面初始化时输出的 join 命令:
bash复制sudo kubeadm join 192.168.1.10:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx
join 命令里的 token 默认有效期是 24 小时。如果你隔天再来加入节点,会发现 token 已经过期,报错 failed to request bootstrap token。
重新生成 token 的方式有三种:
bash复制# 方式一:直接生成新的 join 命令(最简单)
kubeadm token create --print-join-command
# 方式二:创建自定义过期时间的 token
kubeadm token create --ttl 48h0m0s --print-join-command
# 方式三:如果 CA 证书 hash 也丢了,手动补全
kubeadm token list
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
我建议用方式一,因为它会把 --discovery-token-ca-cert-hash 一并打印出来,不需要你记哈希。
节点加入后,回到控制平面节点查看状态:
bash复制kubectl get nodes
正常情况所有节点应该是 Ready。如果你看到 NotReady,大概率是第 2 节里的系统参数没配好,或者 containerd 的 cgroup 驱动和 kubelet 不一致。
5.1 检查节点状态的排查顺序
当节点 NotReady 时,我习惯按这个顺序排查:
bash复制# 1. 在出问题的节点上看 kubelet 日志
sudo journalctl -u kubelet -f
# 2. 查看 Pod 状态
kubectl get pods -n kube-system -o wide
# 3. 用 crictl 查看容器运行状态(containerd 的客户端工具)
sudo crictl ps -a
sudo crictl logs <container-id>
crictl 这个工具可能很多人不熟。它在 containerd 环境下相当于 docker ps 和 docker logs 的替代品,专门针对 CRI 接口设计。当 Pod 卡在 ContainerCreating 时,crictl ps -a 能直接看到容器是不是创建失败,然后 crictl logs 查看具体错误——比 kubectl describe pod 的信息量更大,因为它能看到 stage 更早的问题。
6. Dashboard 部署:将一个新 Pod 作为服务发布的完整路径
很多刚上手 Kubernetes 的人,工作流非常朴素:希望有个界面可以看集群状态,然后通过界面上传一个镜像、创建一个 Pod、让它对外提供服务。
这个需求用 Kubernetes Dashboard 就能实现。1.23 版本我用的 Dashboard 版本是 v2.6.x,兼容性最好。
6.1 安装 Dashboard
bash复制kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.6.1/aio/deploy/recommended.yaml
安装后 Dashboard 默认部署在 kubernetes-dashboard 命名空间。
6.2 创建管理员用户
Dashboard 默认不允许匿名登录,需要创建 ServiceAccount 并绑定集群管理员权限。这一步也是"为什么很多人打开 Dashboard 却登录不上"的原因。
yaml复制# dashboard-admin.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
bash复制kubectl apply -f dashboard-admin.yaml
然后获取登录 token:
bash复制kubectl -n kubernetes-dbernetes create token admin-user
注意:1.23 版本里 kubectl create token 这个命令是从 1.23 开始可用的(之前是 kubectl get secret 手动解码),如果你在用 1.23 之前的版本,需要换一种方式。
6.3 通过 Dashboard 创建一个新 Pod 作为新服务发布
这是你在热搜词里看到的需求:"怎么创建一个新的 pod 作为新服务发布"。Dashboard 的界面和 kubectl 操作有个映射关系,理解了之后就会用。
在 Dashboard 里,点击右上角的 + 号,可以直接粘贴 YAML 文件。举个实际例子,假设我要发布一个 Nginx 服务:
yaml复制apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
labels:
app: nginx-demo
spec:
replicas: 2
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-demo-svc
spec:
selector:
app: nginx-demo
ports:
- port: 80
targetPort: 80
type: NodePort
这个 YAML 干了三件事:
- 创建 Deployment:定义 Pod 模板,replicas=2 表示运行两个副本;
- 创建 Service:通过 selector(
app: nginx-demo)找到上面的 Pod,然后暴露服务; type: NodePort:在每台节点上开一个随机端口(默认 30000-32767),外部可以通过节点IP:端口访问。
在 Dashboard 上操作时,点右上角的 +(Create),选择第二个 tab "Create from input",把这段 YAML 粘贴进去,点上传即可。
如果你用 kubectl,等价命令是:
bash复制kubectl apply -f nginx-demo.yaml
然后查看状态:
bash复制kubectl get pods
kubectl get svc
kubectl get nodes -o wide # 找到节点 IP
浏览器访问 http://<节点IP>:<NodePort>,看到 Nginx 欢迎页就说明服务发布成功。
6.4 Pod 和 Deployment 的区别:为什么不能只创建 Pod
这里有个关键认知需要理清。有些人用 Dashboard 直接创建了一个 Pod,发现容器挂了之后不会自动重启——这就是 Pod 和 Deployment 的区别。
- Pod:Kubernetes 的最小调度单位,本身没有自愈能力,Pod 被删除或被驱逐后不会重建;
- Deployment:管理 Pod 的控制器,你只需要告诉它"我要几个副本",它会保证任何时候都有对应数量的 Pod 在运行。
所以正确姿势永远是:通过 Deployment 发布服务,而不是直接裸创建 Pod。Deployment 内部会生成 ReplicaSet(副本集),ReplicaSet 负责创建和回收 Pod。这个逻辑链如果你想在 1.23 上学透,建议手动删几个 Pod 看看它们会不会自动重建:
bash复制kubectl delete pod -l app=nginx-demo
kubectl get pods -w # 观察自动重建过程
实测几秒钟内新的 Pod 就会创建起来,这就是 Kubernetes 自愈能力最直观的体验。
7. 高频故障排查清单:从 init 失败到镜像拉取超时
部署 Kubernetes 几乎没有一次成功的,踩坑才是常态。下面这些故障我在部署过程中都遇到过,按出现频率排序。
7.1 kubeadm init 卡在拉镜像
症状:执行 kubeadm init 后卡在 [cri] pulling image...,最后超时失败。
根本原因:国内网络访问 k8s.gcr.io 不稳定,或者 containerd 的镜像加速没配置。
排查手段:
bash复制sudo crictl images # 查看当前已有镜像
sudo crictl pull registry.aliyuncs.com/google_containers/kube-apiserver:v1.23.17 # 手动测试拉取
解决:确认 --image-repository registry.aliyuncs.com/google_containers 参数带上,或提前 kubeadm config images pull。
7.2 节点一直是 NotReady,kube-system 里 network 插件 CrashLoopBackOff
症状:kubectl get nodes 节点状态 NotReady,kubectl get pods -n kube-system 看到 flannel 或 calico 一直 CrashLoopBackOff。
根本原因:绝大多数是 Pod 网段和网络插件期望网段不一致。比如你初始化时写了 --pod-network-cidr=10.244.0.0/16,但下载的 calico 默认配置用的 192.168.0.0/16。
排查手段:
bash复制kubectl logs -n kube-system <network-pod-name> --previous
如果日志里有 subnet 或 CIDR 相关报错,基本就是这个原因。
解决:删掉网络插件,重新用正确的 CIDR 创建。对于 calico,可以在安装脚本里自定义 CALICO_IPV4POOL_CIDR 环境变量。
7.3 join 时报 token 过期或 CA 证书不匹配
症状:kubeadm join 报错 failed to request bootstrap token: token [xxx] was not found 或 x509: certificate signed by unknown authority。
原因:前者是 token 有效期过了(默认 24h);后者是 join 命令里 --discovery-token-ca-cert-hash 写错,或者控制平面的证书已经重新生成过。
解决:直接用 kubeadm token create --print-join-command 重新生成完整命令,别手动拼。
7.4 kubectl get nodes 报错 The connection to the server localhost:8080 was refused
症状:刚装完 kubectl,执行任何 kubectl 命令都报 connection refused。
原因:没有配置 kubeconfig。kubectl 默认连接 localhost:8080(这是 kube-apiserver 的明文端口,但默认监听在 6443,所以连接被拒)。
解决:执行第 4.2 节里的 kubeconfig 配置步骤:
bash复制mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
7.5 containerd 拉镜像报错 Failed to resolve reference
症状:containerd 拉镜像时报 failed to resolve reference "docker.io/library/nginx:latest": failed to do request 或类似网络错误。
原因:containerd 的镜像加速没有配置或地址失效。
解决:检查 /etc/containerd/config.toml 里的 mirrors 配置,改完一定记得 systemctl restart containerd。这里有个细节:修改配置后旧配置可能被覆盖,建议重启后用 crictl images 再次验证。
7.6 Pod 创建成功但无法跨节点互通
症状:同节点 Pod 可以互通,不同节点的 Pod ping 不通,Service 无法跨节点访问。
原因:这个问题的排查链路比较长,按优先级检查:
- 所有节点是否都安装了网络插件(用
kubectl get ds -n kube-system查看 DaemonSet 是否在所有节点就绪); - 节点的安全组/防火墙是否放行了 UDP/TCP 端口(flannel 需要 8472/udp,calico 需要 179/tcp 和 IPIP 协议);
- 内核参数
net.ipv4.ip_forward是否为 1。
尤其是云服务器,经常需要在安全组里加上这些规则,很多刚上手的人容易忽略这一层。
8. 从跑通到能上生产:1.23 集群的几项加固建议
集群跑通是一回事,真正能扛住生产流量是另一回事。下面这几项是我在多次部署后总结出来的经验,属于"如果重来一次我会怎么做"的清单。
8.1 提前规划证书有效期
Kubernetes 1.23 里 kubeadm 生成的证书有效期默认是 1 年。到期后 kube-apiserver 和 kubelet 之间的 TLS 验证会失败,集群进入异常状态。
建议在初始化时用 --certificate-dir 指定证书目录,并做好备份。证书快到期时,执行:
bash复制sudo kubeadm certs renew all
然后重启控制平面的静态 Pod:
bash复制sudo kubeadm init phase kubeconfig all
sudo systemctl restart kubelet
这里提醒一下:kubeadm certs renew all 只能 renew 本地证书,如果之前开启过 etcd 加密,相关证书也要一并处理。证书管理是 1.23 集群运维里最容易被忽视的问题,因为大多数人的集群活不到一年,等到第二年才发现证书过期,处理起来非常被动。
8.2 系统盘的 pod CIDR 和磁盘空间规划
这一条是我觉得最重要的生产建议之一。Kubernetes 的镜像是放在容器运行时的数据目录里的(containerd 默认 /var/lib/containerd),Pod 的 EmptyDir 数据也会落在节点本地磁盘。如果节点只有一块系统盘,大量镜像和日志会迅速写满磁盘。
建议在初始化前就把 /var/lib/containerd 挂载到单独的数据盘上,或者至少确保这个目录有充足的空间。一个常见的分区方案是:
- 系统盘
/50G 起步,足够 systemd、/etc、kubelet 的临时文件; - 数据盘挂到
/var/lib/containerd,200G+(按你预期的容器数量估算); - 日志单独挂一个分区
/var/log,防止 journald 日志刷爆系统盘。
我有一次测试环境就在集群跑了两个月之后突然所有 Pod 被驱逐——原因是系统盘利用率超过 90%,kubelet 默认的 eviction 策略触发了 Pod 驱逐。后来给 /var/lib/containerd 换了独立磁盘才彻底解决。
8.3 开启 kubelet 的系统资源预留
如果节点上同时跑着其他系统服务(比如监控 Agent、日志收集器),它们会和 Pod 抢内存。默认情况下 kubelet 给系统保留的资源很少,极端情况会触发节点 OOM。
建议在 /var/lib/kubelet/config.yaml 里加上:
yaml复制systemReserved:
cpu: 500m
memory: 1Gi
kubeReserved:
cpu: 200m
memory: 512Mi
evictionHard:
memory.available: "500Mi"
这样 kubelet 在调度 Pod 时会在资源计算上把系统保底资源扣掉,避免系统服务被 Pod 挤垮。
这个配置在 1.23 上我记得 systemReserved 和 kubeReserved 只有设为非零值后,在 Node Allocatable 的计算中才会生效。具体的细节要看官方文档,但核心思路就是:不要让 Pod 理论可用资源等于节点实际物理资源,永远给系统留点余量。
8.4 日志策略:让 journald 别把磁盘写满
Kubernetes 的日志分为两层:节点系统日志(journald)和 Pod 日志(/var/log/containers)。前一层的坑特别容易踩,因为 journald 默认不限制日志文件大小。
bash复制sudo journalctl --vacuum-size=500M
sudo journalctl --vacuum-time=7d
建议持久化配置:
bash复制sudo mkdir -p /etc/systemd/journald.conf.d
cat <<EOF | sudo tee /etc/systemd/journald.conf.d/size.conf
[Journal]
SystemMaxUse=500M
MaxRetentionSec=7d
EOF
sudo systemctl restart systemd-journald
Pod 日志则依赖 Docker 或 containerd 的 log rotation。containerd 1.6 的配置里默认就带 rotation,但默认上限是 10M 每个文件,对于高并发服务来说可能不够,可以按需调大。
8.5 开启审计日志(可选)
1.23 的 kube-apiserver 支持配置审计日志,记录所有 API 请求。生产环境开启审计日志有助于安全追踪,不过要控制量——全量审计会非常占磁盘和 CPU。推荐只记录关键资源的变更事件:
yaml复制# /etc/kubernetes/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets", "configmaps", "pods", "services"]
- level: None
然后在 /etc/kubernetes/manifests/kube-apiserver.yaml 中挂载审计策略文件并加入参数。这一步比较进阶,但值得在部署初期就做好,避免后续再升级 api-server。
9. 最后想分享的一个小习惯
整套流程走下来,你会发现 Kubernetes 部署的核心难点不在"敲命令",而在版本匹配:容器运行时版本要对、kubeadm/kubelet/kubectl 版本要一致、网络插件期望的 CIDR 要跟初始化参数一致。每次报错,第一个反应应该是"哪个组件和我预想的版本不一样了",而不是盲目重装。
我的习惯是把每个组件的版本统一记录在一个文件里,放在 /root/k8s-version.txt,包括:Ubuntu 版本、内核版本、containerd 版本、kubeadm/kubelet/kubectl 版本、网络插件版本、Dashboard 版本。下次维护或者换机器重建时,照着这份清单装,基本不会出幺蛾子。
还有一个小技巧:初始化之前,把 kubeadm init 的完整命令保存为一个脚本文件,比如 init.sh,放在 /root 下。如果初始化失败需要重新执行,只需改参数再跑一次,避免手打命令时拼错。我在多台机器上初始化集群时,靠这个脚本省了很多重复劳动。
希望这篇 1.23 的部署手册能帮到你。如果你在生产环境遇到了其他奇怪的坑,欢迎留言交流——毕竟这个版本虽然"老",但跑在它上面的业务,很多还在正常服役。
