主题
kubeadm部署 Kubernetes(k8s) V1.36

kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具。
一、版本与架构说明
1.1 组件版本
| 组件 | 版本或选择 |
|---|---|
| 操作系统 | openEuler 24.03 LTS-SP3 |
| Linux Kernel | 6.6.0 |
| Kubernetes | v1.36.4 |
| kubeadm / kubelet / kubectl | v1.36.4 |
| Docker Engine | 29.7.2 |
| cri-dockerd | v0.4.4 |
| kube-proxy | iptables |
| Calico | v3.32.1 |
| Metrics Server | v0.9.0 |
| NFS Subdir External Provisioner | v4.0.2 |
| 控制平面 API 入口 | HAProxy |
| etcd | kubeadm 管理的本地单节点 etcd |
1.2 集群拓扑
| 角色 | IP | 机器配置 | 组件 | 功能描述 |
|---|---|---|---|---|
| HAProxy | 172.22.33.99 | 按实际配置 | HAProxy | Kubernetes API 统一入口 |
| k8s-master01 | 172.22.33.100 | 按实际配置 | Docker、cri-dockerd、控制平面、etcd、kubelet、kube-proxy、Calico、NFS Server | 控制平面及 NFS 服务节点 |
| k8s-node01 | 172.22.33.101 | 按实际配置 | Docker、cri-dockerd、kubelet、kube-proxy、Calico | 工作节点 |
| k8s-node02 | 172.22.33.102 | 按实际配置 | Docker、cri-dockerd、kubelet、kube-proxy、Calico | 工作节点 |
高可用边界:当前拓扑只有一个控制平面、一个本地 etcd、一个 HAProxy 和一个 NFS Server,这些组件都存在单点,因此严格来说不是高可用集群。本文保留原文件名,但部署内容以最新拓扑为准。生产环境如需真正高可用,应至少使用三个控制平面节点、双机 Keepalived/VIP 或外部高可用四层负载均衡,并使用具备冗余和备份能力的独立存储系统。
1.3 部署要求
- 所有 Kubernetes 节点至少 8 个 VCPU、16 GiB 内存,生产环境建议更高。
- 所有节点主机名、MAC 地址和 product_uuid 唯一。
- 所有节点之间网络互通,且时间同步。
- Pod CIDR、Service CIDR 不得与宿主机、VPN、机房网络重叠。
- 本文使用
10.244.0.0/16作为 Pod CIDR,10.96.0.0/12作为 Service CIDR。 - 节点可以访问所配置的软件源和镜像仓库。
- 所有操作默认以 root 用户执行。
二、操作系统初始化
除特别说明外,本章命令需要在全部 Kubernetes Master 和 Node 节点执行。
2.1 验证操作系统和内核
bash
cat /etc/openEuler-release
uname -r预期内核主版本为 6.6。本文使用 kube-proxy iptables 模式,不依赖 kube-proxy 原生 nftables 模式。
2.2 配置主机名
在对应节点分别执行:
bash
hostnamectl set-hostname k8s-master01
hostnamectl set-hostname k8s-node01
hostnamectl set-hostname k8s-node02HAProxy 节点执行:
bash
hostnamectl set-hostname k8s-haproxy2.3 配置 hosts
以下 hosts 配置需要在包括 HAProxy 在内的四台服务器上执行:
bash
cat >>/etc/hosts <<'EOF'
172.22.33.99 k8s-haproxy
172.22.33.100 k8s-master01
172.22.33.101 k8s-node01
172.22.33.102 k8s-node02
EOF验证:
bash
getent hosts k8s-haproxy k8s-master01 k8s-node01 k8s-node022.4 关闭 swap
bash
swapoff -a
sed -ri '/[[:space:]]swap[[:space:]]/s/^/#/' /etc/fstab
free -hfree -h 中 Swap 总量应为 0。
2.5 防火墙和 SELinux
为了保持本文部署路径简单,关闭 firewalld:
bash
systemctl disable --now firewalld如需保留主机防火墙,必须显式开放 Kubernetes、etcd、NodePort 和 Calico 所需端口,并避免其他规则管理器覆盖 kube-proxy 或 Calico 创建的规则。
查看 SELinux 状态:
bash
getenforce测试环境可以设置为 permissive:
bash
setenforce 0
sed -ri 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config
getenforce2.6 安装基础软件
bash
dnf install -y \
bash-completion \
conntrack-tools \
curl \
iproute \
ipset \
iptables \
nfs-utils \
socat \
tar \
wget2.7 加载内核模块
bash
cat >/etc/modules-load.d/kubernetes.conf <<'EOF'
overlay
br_netfilter
nf_conntrack
EOF
modprobe overlay
modprobe br_netfilter
modprobe nf_conntrack检查:
bash
lsmod | grep -E 'overlay|br_netfilter|nf_conntrack'
iptables --version
iptables -L -n
iptables -t nat -L -n2.8 配置内核参数
bash
#开启内核路由转发
sed -i 's/net.ipv4.ip_forward=0/net.ipv4.ip_forward=1/g' /etc/sysctl.conf
#将桥接的IPv4,IPV6流量传递到iptables的链:
$ cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
vm.swappiness = 0
EOF
# 生效
$ sysctl --system
#增加 max_user_watches,增加 max_user_instances
#临时生效
sysctl -w fs.inotify.max_user_watches=524288
sysctl -w fs.inotify.max_user_instances=8192
#永久生效,将配置追加到 sysctl.conf 文件
cat >> /etc/sysctl.conf <<EOF
fs.inotify.max_user_watches=524288
fs.inotify.max_user_instances=8192
EOF
# 生效
sysctl -p2.9 验证 cgroup v2
bash
stat -fc %T /sys/fs/cgroup/
test -f /sys/fs/cgroup/cgroup.controllers && echo 'cgroup v2 enabled'预期输出包含:
text
cgroup2fs
cgroup v2 enabled如果没有启用 cgroup v2,再执行以下操作并重启:
bash
grubby --update-kernel=ALL --args='systemd.unified_cgroup_hierarchy=1'
reboot2.10 配置时间同步
bash
dnf install -y chrony
systemctl enable --now chronyd
chronyc tracking
chronyc sources -v2.11 NetworkManager 排除 Calico 接口
如果系统运行 NetworkManager,创建配置避免其接管 Calico 接口:
bash
mkdir -p /etc/NetworkManager/conf.d
cat >/etc/NetworkManager/conf.d/calico.conf <<'EOF'
[keyfile]
unmanaged-devices=interface-name:cali*;interface-name:tunl*;interface-name:vxlan.calico;interface-name:wireguard.cali
EOF
systemctl reload NetworkManager三、部署 Docker Engine
在所有 Kubernetes Master 和 Node 节点执行。
本文使用 Docker 官方静态二进制。生产环境应建立明确的 Docker、containerd、runc 安全升级流程;条件允许时优先使用受支持的软件包仓库。
3.1 安装 Docker 二进制
bash
cd /tmp
wget https://mirrors.nju.edu.cn/docker-ce/linux/static/stable/x86_64/docker-29.7.2.tgz
tar -xf docker-29.7.2.tgz
install -m 0755 docker/* /usr/bin/
docker --version
dockerd --version
containerd --version
runc --version3.2 将 Docker 默认数据目录软链接到数据盘
假设独立磁盘已经挂载到 /home/application。
bash
mkdir -p /home/application/docker
# 必须在首次启动 Docker 前创建软链接
ln -s /home/application/docker /var/lib/docker
ls -ld /var/lib/docker这项检查和软链接必须在首次启动 Docker 前完成。Docker 仍使用默认路径 /var/lib/docker,实际数据通过软链接写入 /home/application/docker。如果 /var/lib/docker 已经存在,不要直接覆盖或删除,应先停用 Docker 并确认是否需要迁移已有数据。
创建 /etc/docker/daemon.json:
bash
mkdir -p /etc/docker
cat >/etc/docker/daemon.json <<'EOF'
{
"registry-mirrors": [
"https://docker.1ms.run"
],
"exec-opts": [
"native.cgroupdriver=systemd"
],
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5"
}
}
EOF3.3 创建 Docker systemd 服务
bash
cat >/etc/systemd/system/docker.service <<'EOF'
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target
Wants=network-online.target
[Service]
Type=notify
ExecStart=/usr/bin/dockerd
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=0
Restart=on-failure
RestartSec=2
Delegate=yes
KillMode=process
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now docker验证:
bash
systemctl --no-pager --full status docker
docker info | grep -E 'Docker Root Dir|Storage Driver|Cgroup Driver|Cgroup Version'预期至少包含:
text
Docker Root Dir: /var/lib/docker
Storage Driver: overlay2
Cgroup Driver: systemd
Cgroup Version: 2四、部署 cri-dockerd
在所有 Kubernetes Master 和 Node 节点执行。
4.1 安装 cri-dockerd
bash
cd /tmp
wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.4.4/cri-dockerd-0.4.4.amd64.tgz
echo '109b7540053a507dd85ad7b9fee9cee9caae55feedcc28365d3e7ab4fb2172d5 cri-dockerd-0.4.4.amd64.tgz' | sha256sum -c -
tar -xf cri-dockerd-0.4.4.amd64.tgz
install -m 0755 cri-dockerd/cri-dockerd /usr/bin/cri-dockerd
cri-dockerd --version4.2 创建 systemd service
bash
cat >/etc/systemd/system/cri-docker.service <<'EOF'
[Unit]
Description=CRI Interface for Docker Application Container Engine
Documentation=https://mirantis.github.io/cri-dockerd/
After=network-online.target docker.service
Wants=network-online.target
Requires=cri-docker.socket docker.service
[Service]
Type=notify
ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.10
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
StartLimitBurst=3
StartLimitInterval=60s
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
EOF4.3 创建 systemd socket
bash
cat >/etc/systemd/system/cri-docker.socket <<'EOF'
[Unit]
Description=CRI Docker Socket for the API
PartOf=cri-docker.service
[Socket]
ListenStream=%t/cri-dockerd.sock
SocketMode=0660
SocketUser=root
SocketGroup=root
[Install]
WantedBy=sockets.target
EOF
systemctl daemon-reload
systemctl enable --now cri-docker.socket验证:
bash
systemctl --no-pager --full status cri-docker.socket
ls -l /var/run/cri-dockerd.sock五、部署 kubeadm、kubelet、kubectl
在所有 Kubernetes Master 和 Node 节点执行。
5.1 添加 Kubernetes v1.36 RPM 软件源
bash
cat >/etc/yum.repos.d/kubernetes.repo <<'EOF'
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.36/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.36/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF查看可用版本:
bash
dnf list --showduplicates kubeadm --disableexcludes=kubernetes5.2 安装 Kubernetes 组件
bash
dnf install -y \
kubeadm-'1.36.4-*' \
kubelet-'1.36.4-*' \
kubectl-'1.36.4-*' \
cri-tools \
--disableexcludes=kubernetes
systemctl enable kubelet配置 crictl:
bash
cat >/etc/crictl.yaml <<'EOF'
runtime-endpoint: unix:///var/run/cri-dockerd.sock
image-endpoint: unix:///var/run/cri-dockerd.sock
timeout: 10
debug: false
EOF验证:
bash
kubeadm version
kubelet --version
kubectl version --client
crictl info此时 kubelet 尚未获得集群配置,服务可能处于重启状态,属于正常现象。
六、部署 HAProxy
仅在 HAProxy 节点 172.22.33.99 执行。
6.0 初始化 HAProxy 节点
HAProxy 节点也必须完成主机名、hosts、时间同步和安全策略配置。本文采用与 Kubernetes 节点相同的 firewalld/SELinux 策略:
bash
hostnamectl set-hostname k8s-haproxy
systemctl disable --now firewalld
setenforce 0 || true
sed -ri 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config
dnf install -y chrony
systemctl enable --now chronyd确认 HAProxy 节点可以访问控制平面 172.22.33.100 的 6443 端口;在 API Server 尚未启动时,TCP 检查失败是正常的,但路由和 ACL 必须预先放行。
6.1 安装 HAProxy
bash
dnf install -y haproxy6.2 配置 HAProxy
bash
cat >/etc/haproxy/haproxy.cfg <<'EOF'
global
log /dev/log local0 warning
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
stats socket /var/lib/haproxy/stats
defaults
log global
option tcplog
option dontlognull
timeout connect 5s
timeout client 1h
timeout server 1h
frontend kube-apiserver
bind *:6443
mode tcp
option tcplog
default_backend kube-apiserver
backend kube-apiserver
mode tcp
option tcp-check
balance roundrobin
default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
server kube-apiserver-1 172.22.33.100:6443 check
EOF检查并启动:
bash
haproxy -c -f /etc/haproxy/haproxy.cfg
systemctl enable --now haproxy
ss -lntp | grep ':6443'在 API Server 启动前,HAProxy 后端检查失败是正常的。
七、初始化控制平面节点
本章只在 k8s-master01(172.22.33.100)执行。
7.1 创建 etcd 数据目录
bash
mkdir -p /home/application/etcd
chmod 700 /home/application/etcd7.2 创建 kubeadm 初始化配置
bash
mkdir -p /etc/kubernetes/init
cat >/etc/kubernetes/init/kubeadm-init.yaml <<'EOF'
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 172.22.33.100
bindPort: 6443
nodeRegistration:
criSocket: unix:///var/run/cri-dockerd.sock
imagePullPolicy: IfNotPresent
imagePullSerial: true
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: v1.36.4
clusterName: kubernetes
controlPlaneEndpoint: 172.22.33.99:6443
imageRepository: registry.aliyuncs.com/google_containers
certificatesDir: /etc/kubernetes/pki
caCertificateValidityPeriod: 87600h
certificateValidityPeriod: 87600h
networking:
dnsDomain: cluster.local
podSubnet: 10.244.0.0/16
serviceSubnet: 10.96.0.0/12
etcd:
local:
dataDir: /home/application/etcd
apiServer:
# Kubernetes v1.36 默认启用的新 WebSocket 转发路径与当前 cri-dockerd
# 存在 exec/attach/port-forward 兼容问题,初始化时恢复旧转发路径。
extraArgs:
- name: feature-gates
value: ExtendWebSocketsToKubelet=false
certSANs:
- kubernetes
- kubernetes.default
- kubernetes.default.svc
- kubernetes.default.svc.cluster.local
- 172.22.33.99
- 172.22.33.100
- 127.0.0.1
- "*.srebro.cn"
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: iptables
EOF说明:
caCertificateValidityPeriod: 87600h约为 10 年。certificateValidityPeriod: 87600h约为 10 年。ExtendWebSocketsToKubelet=false用于规避 Kubernetes v1.36 与当前 cri-dockerd 的流式协议兼容问题,确保新集群的kubectl exec、attach和port-forward可用。该参数属于兼容性措施,不是忽略 kubeadm 预检错误。- 请根据实际环境修改 API Server DNS/IP SAN、Pod CIDR、Service CIDR 和镜像仓库;不要照抄不属于当前集群的地址或域名。
- kubeadm 会生成随机 bootstrap token,不要在文档中固定使用示例 token。
如果节点有多张网卡,还应明确 kubelet 节点 IP,并配置 Calico IP 自动探测策略,避免 kubelet 与 Calico 选中不同或不可达的接口。
7.3 检查镜像列表
bash
kubeadm config validate --config /etc/kubernetes/init/kubeadm-init.yaml
kubeadm config images list --config /etc/kubernetes/init/kubeadm-init.yaml确认输出中的 pause 镜像版本与 cri-dockerd service 中的 --pod-infra-container-image 一致。如果不一致,先修改 cri-dockerd service,然后执行:
bash
systemctl daemon-reload
systemctl restart cri-docker.service7.4 预拉取控制平面镜像
bash
kubeadm config images pull --config /etc/kubernetes/init/kubeadm-init.yaml7.5 初始化集群
bash
kubeadm init --config /etc/kubernetes/init/kubeadm-init.yaml保存输出中的以下内容:
- 工作节点 join 命令。
- bootstrap token。
- discovery-token-ca-cert-hash。
7.6 配置 kubectl
root 用户执行:
bash
mkdir -p /root/.kube
cp -f /etc/kubernetes/admin.conf /root/.kube/config
chmod 600 /root/.kube/config验证:
bash
kubectl get nodes
kubectl get pods -A在安装 CNI 前,节点显示 NotReady、CoreDNS 显示 Pending 属于正常现象。
八、检查单控制平面
在 k8s-master01(172.22.33.100)执行。
bash
kubectl get nodes -o wide
kubectl -n kube-system get pods -l component=etcd -o wide
kubectl -n kube-system get pods -l component=kube-apiserver -o wide
curl -k https://172.22.33.99:6443/livez当前只有一个控制平面和一个 etcd 实例。该节点不可用时,集群中已运行的普通 Pod 可能继续运行,但调度、扩缩容、控制器协调和 Kubernetes API 都会不可用。
九、加入工作节点
分别在 k8s-node01(172.22.33.101)和 k8s-node02(172.22.33.102)执行。两个节点必须先完成第二至第五章。
bash
kubeadm join 172.22.33.99:6443 \
--token <BOOTSTRAP_TOKEN> \
--discovery-token-ca-cert-hash sha256:<CA_CERT_HASH> \
--cri-socket unix:///var/run/cri-dockerd.sock在 k8s-master01 检查:
bash
kubectl get nodes -o wide在 CNI 安装前,新节点显示 NotReady 属于正常现象。
十、部署 Calico CNI
在 k8s-master01 执行。
10.1 下载 Calico manifest
bash
mkdir -p /etc/kubernetes/addons/calico
cd /etc/kubernetes/addons/calico
curl -fLO https://raw.githubusercontent.com/projectcalico/calico/v3.32.1/manifests/calico.yaml生产环境应从 Calico 官方 release 获取并校验发布包,或将经过校验的 manifest 和镜像同步到内部制品仓库;不要长期依赖未受控的在线下载地址。
10.2 替换 Calico 镜像加速地址
将 calico.yaml 中的三个 Calico 官方镜像替换为南京大学镜像站地址:
bash
sed -i \
-e 's#quay.io/calico/cni:v3.32.1#quay.nju.edu.cn/calico/cni:v3.32.1#g' \
-e 's#quay.io/calico/node:v3.32.1#quay.nju.edu.cn/calico/node:v3.32.1#g' \
-e 's#quay.io/calico/kube-controllers:v3.32.1#quay.nju.edu.cn/calico/kube-controllers:v3.32.1#g' \
calico.yaml检查替换结果:
bash
grep -E 'image:.*calico/(cni|node|kube-controllers):v3.32.1' calico.yaml输出中的对应镜像地址应为:
text
quay.nju.edu.cn/calico/cni:v3.32.1
quay.nju.edu.cn/calico/node:v3.32.1
quay.nju.edu.cn/calico/kube-controllers:v3.32.1镜像加速站属于外部服务,正式部署前应先确认各节点可以正常拉取这些镜像。如果镜像站不可用,请改回
quay.io或同步到自建镜像仓库。
10.3 修改 Pod CIDR
编辑 calico.yaml,找到:
yaml
# - name: CALICO_IPV4POOL_CIDR
# value: "192.168.0.0/16"取消注释并修改为:
yaml
- name: CALICO_IPV4POOL_CIDR
value: "10.244.0.0/16"检查:
bash
grep -A2 -B2 'CALICO_IPV4POOL_CIDR' calico.yaml10.4 安装 Calico
bash
kubectl apply -f calico.yaml观察状态:
bash
kubectl get pods -n kube-system -w等待所有节点 Ready:
bash
kubectl get nodes -o wide十二、集群功能测试
12.1 创建测试 Deployment 和 Service
bash
kubectl create deployment nginx --image=docker.cnb.cool/srebro/docker-images-chrom/nginx:latest_amd64
kubectl rollout status deployment/nginx --timeout=120s
kubectl expose deployment nginx --port=80 --type=NodePort检查:
bash
kubectl get pods -o wide
kubectl get services
kubectl get endpointslices获取 NodePort:
bash
kubectl get service nginx通过任一节点的主 IP 访问:
bash
curl http://172.22.33.101:<NODE_PORT>/12.2 验证 DNS
bash
kubectl run dns-test \
--image=docker.cnb.cool/srebro/docker-images-chrom/busybox:latest_amd64 \
--restart=Never \
--command -- sleep 3600
#验证解析apiserver 的 svc 地址
[root@k8s-master01 manifests]# kubectl exec dns-test -- nslookup kubernetes.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: kubernetes.default.svc.cluster.local
Address: 10.96.0.1
#删除pod
kubectl delete pod dns-test十三、kubectl 命令补全
bash
source /usr/share/bash-completion/bash_completion
kubectl completion bash >/etc/bash_completion.d/kubectl
source /etc/bash_completion.d/kubectl十四、部署 Metrics Server
在 k8s-master01 执行。本文使用 Metrics Server v0.9.0;该版本支持 Kubernetes v1.34 及更高版本,可以用于 Kubernetes v1.36 集群。
Metrics Server 用于提供 metrics.k8s.io 资源指标 API,供 kubectl top、HorizontalPodAutoscaler 等组件使用。它不是 Prometheus 的替代品,不适合保存长期监控数据。
14.1 检查部署条件
部署前确认:
- kube-apiserver 已启用 API Aggregation Layer;kubeadm 部署默认满足。
- Metrics Server Pod 可以访问三台节点的 InternalIP 和 TCP 10250。
- 控制平面可以访问 Metrics Server Pod IP 和 TCP 10250。
- kubelet Webhook authentication 和 authorization 已启用。
- Calico、CoreDNS 和 kube-proxy 均正常运行。
bash
kubectl get nodes -o wide
kubectl get pods -n kube-system
kubectl get --raw='/readyz?verbose'14.2 下载官方 manifest
bash
mkdir -p /etc/kubernetes/addons/metrics-server
cd /etc/kubernetes/addons/metrics-server
curl -fLO \
https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.9.0/components.yaml官方镜像应为:
text
registry.k8s.io/metrics-server/metrics-server:v0.9.0加速地址:
bash
docker.cnb.cool/sre-demo/k8s-demo/registry.k8s.io-metrics-server-metrics-server:v0.9.0_amd6414.3 处理 kubelet serving 证书
生产环境应让 kubelet 使用集群 CA 签发的 serving 证书,并确保证书 SAN 包含 Metrics Server 实际访问的节点 InternalIP 或主机名。启用 serving certificate bootstrap 后,必须核验 CSR 的申请者、节点身份和 SAN 再批准,不能批量盲目批准 serving CSR。
如果当前是测试环境,且 Metrics Server 日志明确出现 kubelet serving 证书 x509 校验错误,可以临时加入 --kubelet-insecure-tls:
bash
sed -i '/--metric-resolution=15s/a\ - --kubelet-insecure-tls' components.yaml
grep -A6 -- '--secure-port=10250' components.yaml
- --secure-port=10250
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls
#image: registry.k8s.io/metrics-server/metrics-server:v0.9.0
image: docker.cnb.cool/sre-demo/k8s-demo/registry.k8s.io-metrics-server-metrics-server:v0.9.0_amd64该参数会关闭 Metrics Server 对 kubelet 服务端身份的校验,存在中间人攻击风险,仅作为临时排错或测试配置。生产环境完成 kubelet serving 证书签发和信任配置后必须删除该参数。
14.4 安装并验证 Metrics Server
bash
kubectl apply -f components.yaml检查组件状态和指标:
bash
kubectl -n kube-system get deployment,pod,service \
-l k8s-app=metrics-server -o wide
kubectl get apiservice v1beta1.metrics.k8s.io
kubectl top nodes
kubectl top pods -Av1beta1.metrics.k8s.io 的 AVAILABLE 应为 True。Metrics Server 启动后通常需要等待约 15 至 60 秒才能看到首批指标。
十五、部署 NFS StorageClass 动态供给
该 provisioner 会为每个 PVC 在 NFS 导出目录下创建独立子目录,适合需要 ReadWriteMany 的共享存储。PVC 中声明的容量不会自动成为 NFS 目录的硬配额;如需严格容量隔离,应由后端存储提供配额能力。
当前环境使用:
| 配置项 | 值 |
|---|---|
| NFS Server | 172.22.33.99(HAProxy) |
| NFS 共享路径 | /home/application/nfs/data |
| 供给器命名空间 | kube-nfs |
| StorageClass | nfs-dynamic |
| Provisioner name | k8s-sigs.io/nfs-subdir-external-provisioner |
| Provisioner image | docker.cnb.cool/sre-demo/k8s-demo/registry.k8s.io-sig-storage-nfs-subdir-external-provisioner:v4.0.2_amd64 |
| yaml 文件 | https://cnb.cool/sre-demo/k8s-demo/-/tree/main/yaml/v1.36/storageclass |
15.1 配置 NFS Server
在 k8s-master01(172.22.33.99)执行。
bash
dnf install -y nfs-utils
mkdir -p /home/application/nfs/data
mkdir -p /etc/exports.d
cat >/etc/exports.d/kubernetes.exports <<'EOF'
/home/application/nfs/data *(insecure,rw,sync,no_root_squash)
EOF
systemctl enable --now rpcbind nfs-server
showmount -e 127.0.0.1
ss -lntp | grep ':2049'原文档使用 * 向任意来源导出目录,no_root_squash 允许以 root 身份运行的 provisioner 和业务容器写入共享目录,权限较大;不要将 TCP 2049 暴露到不可信网络。
15.2 在所有 Kubernetes 节点验证 NFS
分别在 172.22.33.100、172.22.33.101、172.22.33.102 执行。
bash
dnf install -y nfs-utils
showmount -e 172.22.33.99
mkdir -p /mnt/nfs-check
mount -t nfs -o vers=4.1 \
172.22.33.99:/home/application/nfs/data \
/mnt/nfs-check
touch "/mnt/nfs-check/nfs-check-$(hostname)"
ls -l /mnt/nfs-check
umount /mnt/nfs-check三个节点都必须能够完成挂载和写入。否则不要继续部署 provisioner。
15.3 PersistenVolume动态供给部署
在 k8s-master01 执行。
1、场景工作目录&下载 yaml 文件
bash
mkdir -p /etc/kubernetes/addons/storageclass
cd /etc/kubernetes/addons/storageclass
wget https://cnb.cool/sre-demo/k8s-demo/-/git/raw/main/yaml/v1.36/storageclass/rbac.yaml
wget https://cnb.cool/sre-demo/k8s-demo/-/git/raw/main/yaml/v1.36/storageclass/deployment.yaml
wget https://cnb.cool/sre-demo/k8s-demo/-/git/raw/main/yaml/v1.36/storageclass/class.yaml
wget https://cnb.cool/sre-demo/k8s-demo/-/git/raw/main/yaml/v1.36/storageclass/nfs-pvc-test.yaml1、创建命名空间
bash
kubectl create namespace kube-nfs2、配置 RBAC(见 [rbac.yaml]) 该文件创建供给器使用的 ServiceAccount、ClusterRole/Binding,以及 leader 选举所需的 Role/Binding
bash
kubectl apply -f rbac.yaml3、部署供给器(见 [deployment.yaml]) 关键点:
serviceAccountName: nfs-client-provisioner与上一步创建的 SA 对应PROVISIONER_NAME必须与 StorageClass 的provisioner完全一致:k8s-sigs.io/nfs-subdir-external-provisionerNFS_SERVER与NFS_PATH指向实际 NFS 服务volumes.nfs.server/path与环境变量保持一致 示例片段:
bash
kubectl apply -f deployment.yaml
kubectl -n kube-nfs get pods4、创建 StorageClass(见 [class.yaml])
bash
kubectl apply -f class.yaml
kubectl get sc5、测试 PVC 与挂载(见 [nfs-pvc-test.yaml]) 该文件创建一个 ReadWriteMany 的 PVC,并启动一个 Pod 挂载到 /mnt,在容器中创建 /mnt/SUCCESS 文件以验证写入:
bash
kubectl apply -f nfs-pvc-test.yaml
kubectl get pvc
kubectl get pod test-pod
kubectl logs test-pod- PVC 状态应为
Bound,PV 自动创建并绑定 - Pod 退出码为 0,日志为空或正常,NFS 目录下应出现对应子目录与
SUCCESS文件 ;验证后删除 pod,kubectl delete pod test-pod
十六、常见问题
16.1 Pod 报 too many open files
检查:
bash
cat /proc/sys/fs/inotify/max_user_watches
cat /proc/sys/fs/inotify/max_user_instances临时设置:
bash
sysctl -w fs.inotify.max_user_watches=524288
sysctl -w fs.inotify.max_user_instances=8192本文已经在 /etc/sysctl.d/99-kubernetes.conf 中持久化这些参数。
16.2 Kubernetes v1.36 使用 cri-dockerd 时无法执行 kubectl exec
执行旧格式命令时可能首先出现以下提示:
text
error: exec [POD] [COMMAND] is not supported anymore. Use exec [POD] -- [COMMAND] insteadKubernetes v1.36 应使用 -- 分隔 Pod 名称和容器内命令。例如优先使用兼容性更好的 sh;部分精简镜像没有 bash:
bash
kubectl exec -it <POD_NAME> -- sh如果改用新语法后仍然出现以下错误:
text
error: Internal error occurred: error sending request: Post "//[::]:<PORT>/cri/exec/<ID>": http: server gave HTTP response to HTTPS client这不是 Pod、Calico、iptables 或 nftables 故障,而是 Kubernetes v1.36 与当前 cri-dockerd 的流式协议兼容问题。Kubernetes v1.36 默认启用了 Beta 功能门 ExtendWebSocketsToKubelet=true,exec、attach 和 port-forward 会使用新的 WebSocket 转发路径;cri-dockerd 创建的临时流式端口提供 HTTP 服务,但新路径按 HTTPS 访问,因此协议不匹配。
本文第 7.2 节已经在 kubeadm-init.yaml 中配置以下内容。按照本文从头初始化的新集群,会在生成 kube-apiserver 静态 Pod 时直接关闭该功能门,不需要部署完成后再次修改:
yaml
apiServer:
extraArgs:
- name: feature-gates
value: ExtendWebSocketsToKubelet=false初始化完成后可以检查实际生成的参数:
bash
grep -n -- '--feature-gates=ExtendWebSocketsToKubelet=false' \
/etc/kubernetes/manifests/kube-apiserver.yaml以下操作仅用于没有在初始化配置中预先设置该参数的存量集群。
在控制平面节点检查 kube-apiserver 是否已经配置功能门:
bash
grep -n -- '--feature-gates' /etc/kubernetes/manifests/kube-apiserver.yaml如果没有输出,先备份静态 Pod manifest,再添加兼容参数:
bash
cp -a /etc/kubernetes/manifests/kube-apiserver.yaml \
/root/kube-apiserver.yaml.$(date +%Y%m%d%H%M%S).bak
sed -i '/^[[:space:]]*- kube-apiserver$/a\ - --feature-gates=ExtendWebSocketsToKubelet=false' \
/etc/kubernetes/manifests/kube-apiserver.yaml如果文件中已经存在 --feature-gates=,不要增加第二个同名参数,应在现有值后合并:
yaml
- --feature-gates=<原有功能门>,ExtendWebSocketsToKubelet=falsekubelet 检测到 manifest 变化后会自动重建 kube-apiserver。单控制平面集群在此期间会发生短暂的 API 中断,等待其恢复:
bash
until kubectl get --raw='/readyz' 2>/dev/null; do
sleep 2
done验证 exec 及其他流式操作:
bash
kubectl exec -it <POD_NAME> -- sh
kubectl port-forward pod/<POD_NAME> 8080:80该设置只是暂时恢复旧的流式转发路径,不会关闭 kubectl exec,也不会影响 Pod 网络或 Service。后续升级 Kubernetes 或 cri-dockerd 时,应检查此兼容问题是否已经修复;执行 kubeadm upgrade 后还要确认静态 Pod manifest 中的参数没有被覆盖,确认新版 cri-dockerd 已兼容后再删除该功能门配置。
十七、Kubernetes v1.32 到 v1.36 的重点变化
| 版本 | 变化 | 处理建议 |
|---|---|---|
| v1.33 | Endpoints API 弃用 | 脚本和控制器改用 EndpointSlice |
| v1.33 | Node .status.nodeInfo.kubeProxyVersion 删除 | 调整旧监控脚本 |
| v1.34 | 手动 kubelet cgroupDriver 配置弃用 | cri-dockerd 环境暂时显式配置,并持续关注自动检测支持 |
| v1.34 | trafficDistribution: PreferClose 弃用 | 改用 PreferSameZone |
| v1.35 | kube-proxy IPVS 模式弃用 | 新集群使用受支持的 iptables 模式;未来迁移到 nftables 前单独验证 |
| v1.35 | cgroup v1 默认拒绝启动 | 使用 cgroup v2 |
| v1.36 | Service .spec.externalIPs 弃用 | 迁移到 LoadBalancer、NodePort 或 Gateway API |
| v1.36 | gitRepo volume 永久禁用 | 改用 initContainer 或 git-sync |
十八、参考资料
- Kubernetes kubeadm:https://kubernetes.io/docs/reference/setup-tools/kubeadm/
- Kubernetes v1.36:https://kubernetes.io/releases/1.36/
- Kubernetes 功能门(ExtendWebSocketsToKubelet):https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/
- Kubernetes v1.36 与 cri-dockerd 的 exec 兼容问题:https://github.com/docker/desktop-feedback/issues/500
- kube-proxy iptables:https://v1-36.docs.kubernetes.io/docs/reference/networking/virtual-ips/#proxy-mode-iptables
- Kubernetes 容器运行时:https://kubernetes.io/docs/setup/production-environment/container-runtimes/
- cri-dockerd:https://github.com/Mirantis/cri-dockerd
- Calico 系统要求:https://docs.tigera.io/calico/latest/getting-started/kubernetes/requirements
- Metrics Server:https://github.com/kubernetes-sigs/metrics-server
- NFS Subdir External Provisioner:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
- 原 v1.32 StorageClass 配置:https://cnb.cool/sre-demo/k8s-demo/-/tree/main/yaml/v1.32/storageclass
- openEuler 24.03 LTS SP4:https://www.openeuler.org/zh/download/
