Kubernetes 二进制安装
安装准备
1 机器规划
集群拓扑如下:
| 主机名 | IP | 角色说明 |
|---|---|---|
| k8s-master1 | 192.168.7.46 | Master + Etcd + (私有镜像仓库) |
| k8s-master2 | 192.168.7.47 | Master + Etcd |
| k8s-master3 | 192.168.7.48 | Master + Etcd |
| k8s-node1 | 192.168.7.49 | Node 节点 |
⚠️ 注意事项
- 所有主机的 主机名不能相同
- 建议配置 SSH 免密登录,便于后续证书、配置文件分发
- 如果条件允许,可以单独准备一台机器作为 镜像仓库,这里直接使用 【k8s-master1】
💡: 以下步骤 2 - 11 每台机器都要做,所有步骤是以 CentOS 操作系统为演示环境进行
2 卸载系统自带防火墙组件
在 CentOS / RedHat / 龙晰 操作系统中,需要移除默认的防火墙相关组件:
yum remove -y firewalld python-firewall firewalld-filesystem3 安装基础依赖软件
以下组件在后续的安装中会频繁用到:
yum install -y \
bash-completion \
conntrack-tools \
ipset \
ipvsadm \
libseccomp \
nfs-utils \
psmisc \
rsync \
socatbash-completion 命令补全 | ipset ipvsadm ipvs 相关 | libseccomp containerd 需求
4 关闭 SELinux
setenforce 0
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config5 禁用 Swap
# 临时关闭
swapoff -a
sysctl -w vm.swappiness=0
# 永久关闭
sed -i '/swap/d' /etc/fstab6 加载内核模块
modprobe br_netfilter
modprobe ip_vs
modprobe ip_vs_rr
modprobe ip_vs_wrr
modprobe ip_vs_sh
modprobe nf_conntrack
# 旧内核需要:
modprobe nf_conntrack_ipv47 配置开机自动加载内核模块
cat > /etc/modules-load.d/k8s-modules.conf <<EOF
br_netfilter
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
EOF验证是否成功:
lsmod | grep -E "ip_vs|nf_conntrack|br_netfilter"8 启用 systemd 自动加载服务
systemctl enable systemd-modules-load.service
systemctl start systemd-modules-load.service9 配置系统参数
cat > /etc/sysctl.d/k8s-sysctl.conf <<EOF
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-arptables = 1
net.ipv4.tcp_tw_reuse = 0
net.core.somaxconn = 65535
net.netfilter.nf_conntrack_max = 1000000
vm.swappiness = 0
vm.max_map_count = 655360
fs.file-max = 6553600
EOF
sysctl --system10 设置系统 ulimits
cat > /etc/systemd/system.conf.d/k8s-ulimits.conf <<EOF
[Manager]
DefaultLimitCORE=infinity
DefaultLimitNOFILE=100000
DefaultLimitNPROC=100000
EOF11 添加本地仓库解析
我们将在 【k8s-master1】 部署 Docker Registry:
echo "192.168.7.46 registry" >> /etc/hostsMaster1 安装 Docker 并部署私有镜像仓库
1 下载并安装 Docker
# 创建下载目录
mkdir -p /tmp/down
# 下载 Docker 二进制包
wget https://download.docker.com/linux/static/stable/x86_64/docker-28.0.4.tgz -P /tmp/down/
# 解压并移动到 /opt/bin
tar zxf /tmp/down/docker-28.0.4.tgz -C /tmp/down/
mkdir -p /opt/bin
mv -f /tmp/down/docker/* /opt/bin/
# 建立软链接
ln -sf /opt/bin/docker /bin/docker
# 验证版本
docker version2 配置 systemd 服务
创建 Docker 的 systemd 管理文件:
cat > /etc/systemd/system/docker.service << EOF
[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.io
[Service]
Environment="PATH=/opt/bin:/bin:/sbin:/usr/bin:/usr/sbin"
ExecStart=/opt/bin/dockerd
ExecStartPost=/sbin/iptables -I FORWARD -s 0.0.0.0/0 -j ACCEPT
ExecReload=/bin/kill -s HUP \$MAINPID
Restart=on-failure
RestartSec=5
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
EOF3 配置 Docker 参数
创建 daemon.json:
mkdir -p /etc/docker
cat > /etc/docker/daemon.json << EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"insecure-registries": ["http://registry:5000"],
"max-concurrent-downloads": 10,
"log-driver": "json-file",
"log-level": "warn",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"data-root": "/var/lib/docker"
}
EOF启动并设置开机自启:
systemctl daemon-reload
systemctl enable docker
systemctl restart docker
# 验证服务状态
systemctl status docker4 部署本地镜像仓库
⚠️ 注意:仓库数据目录建议放在 磁盘空间大于 10G 的分区,这里使用 /opt/registry。
# 加载 registry 镜像
docker load -i registry-2.tar
# 创建仓库存储目录
mkdir -p /opt/registry
# 启动本地仓库
docker run -d \
--name local_registry \
--network host \
--restart always \
--volume /opt/registry:/var/lib/registry \
registry:2添加 hosts 解析:
echo "127.0.0.1 registry" >> /etc/hosts5 推送 Kubernetes 相关镜像
链接看不到就是隐了,下载后导入:
# 示例:加载镜像
docker load -i cni-v3.28.4.tar
docker load -i calico-kube-controllers-v3.28.4.tar
docker load -i calico-node-v3.28.4.tar
docker load -i coredns-1.12.1.tar
docker load -i k8s-dns-node-cache-1.25.0.tar
docker load -i metrics-server-v0.7.2.tar
docker load -i pause-3.10.tar
docker load -i dashboard_7.12.0.tar
# 示例:推送到本地仓库
docker tag calico/node:v3.28.4 registry:5000/calico/node:v3.28.4
docker push registry:5000/calico/node:v3.28.4依次推送所有需要的镜像。
创建集群证书和 kubeconfig 文件
创建目录结构
首先,在 master1 节点(或单独的控制节点)上,创建以下目录,用于存放证书、配置文件、YAML 以及安装所需二进制文件:
mkdir /opt/ssl # 存放证书和私钥
mkdir /opt/conf # 存放 kubeconfig 配置
mkdir /opt/yml # 存放 Kubernetes YAML 清单
mkdir -p /opt/install/bin # 存放所需二进制文件(如 cfssl/kubectl)这样目录结构清晰,便于后续维护和分发。
生成证书用到的bin执行文件可从网盘下载,也可自己安装在/opt/install/bin目录下。
准备 CA 配置文件和签名请求
CA(证书颁发机构)是所有 Kubernetes 证书的信任根。在 Kubernetes 中,所有证书都是由 CA 签发的。我们需要先创建 CA 的配置和 CSR(证书签名请求)。
1 CA 配置文件:ca-config.json
我们将 CA 的有效期设为 100 年,签发证书默认 50 年(在你的环境中,自建k8s是不是遇到过证书到期的问题呢? 估计很多公司在这里翻了车,这也是为什么设置这么长的原因)。
cat > /opt/ssl/ca-config.json << EOF
{
"signing":{
"default":{
"expiry":"438000h"
},
"profiles":{
"kubernetes":{
"usages":[
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry":"438000h"
},
"kcfg":{
"usages":[
"signing",
"key encipherment",
"client auth"
],
"expiry":"438000h"
}
}
}
}
EOF2 CA 签名请求:ca-csr.json
cat > /opt/ssl/ca-csr.json << EOF
{
"CN":"kubernetes",
"key":{
"algo":"rsa",
"size":2048
},
"names":[
{
"C":"CN",
"ST":"Shanghai",
"L":"XS",
"O":"k8s",
"OU":"System"
}
],
"ca":{
"expiry":"876000h"
}
}
EOF📌 解释:
- CN: 公共名称,这里填 kubernetes。
- O: 组织名称,用于 RBAC 鉴权,必须正确设置。
- OU: 组织单位,可自定义。
- expiry: 100年有效期
生成 CA 证书和私钥
进入 /opt/ssl 目录,使用 cfssl 工具生成 CA:
cd /opt/ssl
/opt/install/bin/cfssl gencert -initca ca-csr.json | /opt/install/bin/cfssljson -bare ca生成文件如下:
ca.pem # CA 根证书
ca-key.pem # CA 私钥
ca.csr # 签名请求
ca-config.json # 配置文件4 创建 kubectl.kubeconfig
这是管理员使用 kubectl 操作集群所需的配置文件。
kubectl 是管理员操作集群的命令行工具,需要生成对应的证书和配置。
4.1 准备 Admin CSR
admin-csr.json:
cat > /opt/ssl/admin-csr.json << EOF
{
"CN":"admin",
"hosts":[],
"key":{
"algo":"rsa",
"size":2048
},
"names":[
{
"C":"CN",
"ST":"Shanghai",
"L":"XS",
"O":"system:masters",
"OU":"System"
}
]
}
EOF📌 注意: O: system:masters 组是 Kubernetes 内置的超级管理员组。
4.2 生成 admin 证书和私钥
cd /opt/ssl && /opt/install/bin/cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes admin-csr.json | /opt/install/bin/cfssljson -bare admin生成文件:admin.pem、admin-key.pem
4.3 创建配置 kubectl.kubeconfig
# 设置集群参数
/opt/install/bin/kubectl config set-cluster cluster1 \
--certificate-authority=/opt/ssl/ca.pem \
--embed-certs=true \
--server=https://192.168.7.46:6443 \
--kubeconfig=/opt/conf/kubectl.kubeconfig
# 设置客户端认证参数
/opt/install/bin/kubectl config set-credentials admin \
--client-certificate=/opt/ssl/admin.pem \
--client-key=/opt/ssl/admin-key.pem \
--embed-certs=true \
--kubeconfig=/opt/conf/kubectl.kubeconfig
# 设置上下文参数
/opt/install/bin/kubectl config set-context cluster1 \
--cluster=cluster1 \
--user=admin \
--kubeconfig=/opt/conf/kubectl.kubeconfig
# 选择默认上下文
/opt/install/bin/kubectl config use-context cluster1 \
--kubeconfig=/opt/conf/kubectl.kubeconfig最后安装 kubeconfig:
mkdir -p ~/.kube
cp /opt/conf/kubectl.kubeconfig ~/.kube/config
chmod 400 ~/.kube/config这样就能直接用 kubectl 管理集群。
5 生成 kube-proxy.kubeconfig
kube-proxy 负责每个节点的网络代理,它也需要一个 kubeconfig 来与 API Server 通信。
5.1 准备 CSR
kube-proxy-csr.json
cat > /opt/ssl/kube-proxy-csr.json << EOF
{
"CN":"system:kube-proxy",
"hosts":[],
"key":{
"algo":"rsa",
"size":2048
},
"names":[
{
"C":"CN",
"ST":"Shanghai",
"L":"XS",
"O":"k8s",
"OU":"System"
}
]
}
EOF📌 kube-proxy 需要访问 API Server,因此也需要证书。
5.2 创建 kube-proxy 证书与私钥
cd /opt/ssl && /opt/install/bin/cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes kube-proxy-csr.json | /opt/install/bin/cfssljson -bare kube-proxy生成文件:kube-proxy-key.pem kube-proxy.pem
5.3 创建配置 kubeconfig
# 设置集群参数
/opt/install/bin/kubectl config set-cluster kubernetes \
--certificate-authority=/opt/ssl/ca.pem \
--embed-certs=true \
--server=https://192.168.7.46:6443 \
--kubeconfig=/opt/conf/kube-proxy.kubeconfig
#--server:使用master1的IP地址。
# 设置客户端认证参数
/opt/install/bin/kubectl config set-credentials kube-proxy \
--client-certificate=/opt/ssl/kube-proxy.pem \
--client-key=/opt/ssl/kube-proxy-key.pem \
--embed-certs=true \
--kubeconfig=/opt/conf/kube-proxy.kubeconfig
# 设置上下文参数
/opt/install/bin/kubectl config set-context default \
--cluster=kubernetes \
--user=kube-proxy \
--kubeconfig=/opt/conf/kube-proxy.kubeconfig
# 选择默认上下文
/opt/install/bin/kubectl config use-context default \
--kubeconfig=/opt/conf/kube-proxy.kubeconfig生成文件:kube-proxy.kubeconfig
6 生成 kube-controller-manager.kubeconfig
controller-manager 负责集群的控制循环,需要一个独立的 kubeconfig 文件。
6.1 准备 CSR
kube-controller-manager-csr.json
CSR:
cat > /opt/ssl/kube-controller-manager-csr.json << EOF
{
"CN":"system:kube-controller-manager",
"hosts":[],
"key":{
"algo":"rsa",
"size":2048
},
"names":[
{
"C":"CN",
"ST":"Shanghai",
"L":"XS",
"O":"system:kube-controller-manager",
"OU":"System"
}
]
}
EOF6.2 创建 kube-controller-manager 证书与私钥
cd /opt/ssl && /opt/install/bin/cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes kube-controller-manager-csr.json | /opt/install/bin/cfssljson -bare kube-controller-manager生成文件:kube-controller-manager.pem kube-controller-manager-key.pem
6.3 配置 kubeconfig
# 设置集群参数
/opt/install/bin/kubectl config set-cluster kubernetes \
--certificate-authority=/opt/ssl/ca.pem \
--embed-certs=true \
--server=https://192.168.7.46:6443 \
--kubeconfig=/opt/conf/kube-controller-manager.kubeconfig
# --server:使用master1的IP地址。
# 设置客户端认证参数
/opt/install/bin/kubectl config set-credentials system:kube-controller-manager \
--client-certificate=/opt/ssl/kube-controller-manager.pem \
--client-key=/opt/ssl/kube-controller-manager-key.pem \
--embed-certs=true \
--kubeconfig=/opt/conf/kube-controller-manager.kubeconfig
# 设置上下文参数
/opt/install/bin/kubectl config set-context default \
--cluster=kubernetes \
--user=system:kube-controller-manager \
--kubeconfig=/opt/conf/kube-controller-manager.kubeconfig
# 选择默认上下文
/opt/install/bin/kubectl config use-context default \
--kubeconfig=/opt/conf/kube-controller-manager.kubeconfig生成文件:kube-controller-manager.kubeconfig
7 生成 kube-scheduler.kubeconfig
scheduler 负责 Pod 的调度,同样需要 kubeconfig 文件。
7.1 准备 CSR
kube-scheduler-csr.json
cat > /opt/ssl/kube-scheduler-csr.json << EOF
{
"CN":"system:kube-scheduler",
"hosts":[],
"key":{
"algo":"rsa",
"size":2048
},
"names":[
{
"C":"CN",
"ST":"Shanghai",
"L":"XS",
"O":"system:kube-scheduler",
"OU":"System"
}
]
}
EOF7.2 创建 kube-scheduler 证书与私钥
cd /opt/ssl && /opt/install/bin/cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes kube-scheduler-csr.json | /opt/install/bin/cfssljson -bare kube-scheduler生成证书文件:kube-scheduler-key.pem kube-scheduler.pem
7.3 创建配置 kubeconfig
# 设置集群参数
/opt/install/bin/kubectl config set-cluster kubernetes \
--certificate-authority=/opt/ssl/ca.pem \
--embed-certs=true \
--server=https://192.168.7.46:6443 \
--kubeconfig=/opt/conf/kube-scheduler.kubeconfig
# --server:使用master1的IP地址。
# 设置客户端认证参数
/opt/install/bin/kubectl config set-credentials system:kube-scheduler \
--client-certificate=/opt/ssl/kube-scheduler.pem \
--client-key=/opt/ssl/kube-scheduler-key.pem \
--embed-certs=true \
--kubeconfig=/opt/conf/kube-scheduler.kubeconfig
# 设置上下文参数
/opt/install/bin/kubectl config set-context default \
--cluster=kubernetes \
--user=system:kube-scheduler \
--kubeconfig=/opt/conf/kube-scheduler.kubeconfig
# 选择默认上下文
/opt/install/bin/kubectl config use-context default \
--kubeconfig=/opt/conf/kube-scheduler.kubeconfig生成文件:kube-scheduler.kubeconfig
创建 kubectl 软链
为了方便直接运行 kubectl:
ln -s /opt/install/bin/kubectl /usr/bin/kubectl部署 Etcd 集群
1 创建 etcd 数据目录(每台 etcd 节点都要做)
在每台 etcd 节点上执行(可通过 SSH 或脚本批量执行):
# 在每台 etcd 节点上执行:
mkdir -p /var/lib/etcd
chown root:root /var/lib/etcd
chmod 700 /var/lib/etcd解释:/var/lib/etcd 用于保存 etcd 的数据、wal、快照等,权限收敛为 700 可避免非 root 进程随意访问(本示例 systemd 以 root 启动 etcd)。
2 拷贝 etcd 二进制到节点(把 etcd/etcdctl 放到 /opt/bin)
在 master1(或控制机器)准备好 etcd 二进制文件后,分发到三台节点:
# 本地先准备目录并拷贝二进制(假设你已把下载的文件 etcd/etcdctl 放在控制机的 /opt/bin 目录下)
# 将二进制分发到三台 etcd 节点(需 ssh 免密)
for host in 192.168.7.46 192.168.7.47 192.168.7.48; do
ssh root@${host} "mkdir -p /opt/bin"
scp /opt/bin/etcd /opt/bin/etcdctl root@${host}:/opt/bin/
ssh root@${host} "chmod +x /opt/bin/etcd /opt/bin/etcdctl"
done说明:注意目录位置
3 创建 etcd 的 CSR(证书签名请求)
在 master1 (或控制机)生成 CSR 文件:
cat > /opt/ssl/etcd-csr.json << 'EOF'
{
"CN": "etcd",
"hosts": [
"192.168.7.46",
"192.168.7.47",
"192.168.7.48",
"127.0.0.1"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Shanghai",
"L": "XS",
"O": "k8s",
"OU": "System"
}
]
}
EOF关键点:hosts 列表必须包含所有 etcd 节点的 IP(用于证书中的 SAN),并常常包含 127.0.0.1(本地访问时可用)。
4 使用 cfssl 生成 etcd 证书与私钥
切换到证书目录并生成证书(保证 cfssl、cfssljson 在 /opt/install/bin):
cd /opt/ssl
/opt/install/bin/cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes etcd-csr.json | /opt/install/bin/cfssljson -bare etcd生成后的关键文件(位于 /opt/ssl):
/opt/ssl/etcd.pem # etcd 证书(公钥)
/opt/ssl/etcd-key.pem # etcd 私钥
/opt/ssl/etcd.csr # 签名请求(可选)权限建议:
chmod 644 /opt/ssl/etcd.pem
chmod 600 /opt/ssl/etcd-key.pem5 分发证书到三台 etcd 节点(假设 SSH 已互信)
我们把证书放在每台节点的 /etc/kubernetes/ssl/:
# 在 master1 上执行(本机 /opt/ssl 下有 ca.pem, etcd.pem, etcd-key.pem)
for host in 192.168.7.46 192.168.7.47 192.168.7.48; do
ssh root@${host} "mkdir -p /etc/kubernetes/ssl && chown root:root /etc/kubernetes/ssl && chmod 700 /etc/kubernetes/ssl"
scp /opt/ssl/ca.pem /opt/ssl/etcd.pem /opt/ssl/etcd-key.pem root@${host}:/etc/kubernetes/ssl/
done确认远端文件存在:
ssh root@192.168.7.46 "ls -l /etc/kubernetes/ssl/"
# 应看到 ca.pem, etcd.pem, etcd-key.pem注意:证书私钥 etcd-key.pem 权限一定要严格(600);如果 systemd 用户不是 root,调整权限/所属用户。
6 在每台 etcd 节点上创建 systemd 服务文件
下面给出 模板 etcd.service,示例为节点 192.168.7.46。必须在每台机器上把 IP 与 --name、--initial-advertise-peer-urls、--listen-peer-urls、--listen-client-urls、--advertise-client-urls、--initial-cluster 中的本机 IP 修改为该节点的真实 IP。
在每台 etcd 节点 /etc/systemd/system/etcd.service 写入:
cat > /etc/systemd/system/etcd.service << 'EOF'
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
[Service]
Type=notify
WorkingDirectory=/var/lib/etcd
ExecStart=/opt/bin/etcd \
--name=etcd-192.168.7.46 \
--cert-file=/etc/kubernetes/ssl/etcd.pem \
--key-file=/etc/kubernetes/ssl/etcd-key.pem \
--peer-cert-file=/etc/kubernetes/ssl/etcd.pem \
--peer-key-file=/etc/kubernetes/ssl/etcd-key.pem \
--trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
--peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
--initial-advertise-peer-urls=https://192.168.7.46:2380 \
--listen-peer-urls=https://192.168.7.46:2380 \
--listen-client-urls=https://192.168.7.46:2379,http://127.0.0.1:2379 \
--advertise-client-urls=https://192.168.7.46:2379 \
--initial-cluster-token=etcd-cluster-0 \
--initial-cluster=etcd-192.168.7.46=https://192.168.7.46:2380,etcd-192.168.7.47=https://192.168.7.47:2380,etcd-192.168.7.48=https://192.168.7.48:2380 \
--initial-cluster-state=new \
--data-dir=/var/lib/etcd \
--wal-dir= \
--snapshot-count=50000 \
--auto-compaction-retention=1 \
--auto-compaction-mode=periodic \
--max-request-bytes=10485760 \
--quota-backend-bytes=8589934592
Restart=always
RestartSec=15
LimitNOFILE=65536
OOMScoreAdjust=-999
[Install]
WantedBy=multi-user.target
EOF说明(重要):
--name推荐使用 etcd-<ip> 或 etcd-<hostname>,在 --initial-cluster 中必须与其它成员一致。--listen-client-urls同时包含 https://<ip>:2379(供集群外组件访问)和 http://127.0.0.1:2379(方便本地排查)。--initial-cluster-state=new:首次创建集群使用 new。若向已有集群添加成员需使用 existing 并通过 etcdctl member add。--wal-dir为空表示使用默认(data-dir 下 wal),如果你希望单独分盘存 wal,可设为绝对路径。--quota-backend-bytes控制 etcd 数据库大小上限(示例 8GB)。
在其他两台(192.168.7.47 / 192.168.7.48)分别创建相同的 unit,但替换上面所有 192.168.7.46 出现的位置为对应本机 IP,并把 --name 改为 etcd-192.168.7.47、etcd-192.168.7.48。
7 启动并启用 etcd 服务
在三台 etcd 节点上执行(或在控制机通过 ssh 批量):
# 在每台 etcd 节点上执行:
systemctl daemon-reload
systemctl enable etcd
systemctl start etcd
# 检查状态
systemctl status etcd --no-pager
# 查看启动日志(如报错重点查看)
journalctl -u etcd -f若启动失败,检查常见问题:
- /etc/kubernetes/ssl/etcd-key.pem 权限问题(必须足够保密);
- /opt/bin/etcd 二进制不存在或不可执行;
- --initial-cluster 配置中名字与实际 --name 不一致;
- 证书的 SAN(hosts)未包含本机 IP;
- 端口冲突或防火墙阻断(2379/2380)。
8 检查 etcd 集群健康状态与成员状态(使用 etcdctl)
在任意一台已安装 etcdctl 的机器(例如每个 etcd 节点或控制机),运行下面命令检查健康:
# 先设置节点列表变量(示例)
export NODE_IPS="192.168.7.46 192.168.7.47 192.168.7.48"
# 检查每个 endpoint 健康
for ip in ${NODE_IPS}; do
ETCDCTL_API=3 /opt/bin/etcdctl \
--endpoints=https://${ip}:2379 \
--cacert=/etc/kubernetes/ssl/ca.pem \
--cert=/etc/kubernetes/ssl/etcd.pem \
--key=/etc/kubernetes/ssl/etcd-key.pem \
endpoint health
done期待输出类似:
https://192.168.7.46:2379 is healthy: successfully committed proposal: ...
https://192.168.7.47:2379 is healthy: successfully committed proposal: ...
https://192.168.7.48:2379 is healthy: successfully committed proposal: ...查询每个 endpoint 的状态(版本、leader 等):
for ip in ${NODE_IPS}; do
ETCDCTL_API=3 /opt/bin/etcdctl \
--endpoints=https://${ip}:2379 \
--cacert=/etc/kubernetes/ssl/ca.pem \
--cert=/etc/kubernetes/ssl/etcd.pem \
--key=/etc/kubernetes/ssl/etcd-key.pem \
endpoint status --write-out=table
done示例输出(简化):
ENDPOINT ID VERSION DB SIZE IS LEADER
https://192.168.7.46:2379 823048bfc2b7... 3.5.21 4.3 MB false
https://192.168.7.47:2379 86c366454c3d... 3.5.21 4.2 MB true
https://192.168.7.48:2379 80bf572b66fa... 3.5.21 4.3 MB false查看集群成员列表(可以看出 leader/成员):
ETCDCTL_API=3 /opt/bin/etcdctl \
--endpoints=https://192.168.7.46:2379 \
--cacert=/etc/kubernetes/ssl/ca.pem \
--cert=/etc/kubernetes/ssl/etcd.pem \
--key=/etc/kubernetes/ssl/etcd-key.pem \
member list扩展说明与常见问题(实战要点)
- 首次部署 vs 扩容:首次部署使用 --initial-cluster-state=new;扩容需在已有集群上执行 etcdctl member add 得到 add 命令并在新节点上启用(--initial-cluster-state=existing),切勿把新节点配置成 new,否则会破坏集群。
- 证书 SAN 必须包含所有 peer/client IP,否则 peer 建联报证书错误(查看 journalctl -u etcd 日志)。
- 时间同步:所有 etcd 节点必须时间一致(NTP),否则证书校验/raft 心跳会出现问题。
- 磁盘与 WAL:生产建议将 wal 单独放在性能更好的磁盘并设置 --wal-dir。
- 备份:部署完成后立即做一次快照备份:ETCDCTL_API=3 /opt/bin/etcdctl snapshot save /root/etcd-snapshot.db --endpoints=... --cacert=... --cert=... --key=...。
- 确认端口:确保 2379/2380 对 etcd 成员互通,且 API Server 等需要访问 etcd 的组件(controller-manager)能访问 2379。
安装容器运行时
1 准备目录结构
在 192.168.7.46、192.168.7.47、192.168.7.48、192.168.7.49 四台机器上分别执行以下命令:
mkdir -p /opt/bin/containerd-bin
mkdir -p /etc/containerd
mkdir -p /etc/containerd/certs.d/docker.io解释:
- /opt/bin/containerd-bin:存放 containerd 的二进制文件。
- /etc/containerd:存放 containerd 的配置文件。
- /etc/containerd/certs.d/docker.io:存放 registry 的证书目录(这里示例配置 docker.io,后面可以扩展 Harbor、私有仓库等)。
2 加载内核模块 overlay
containerd 使用 overlayfs 作为默认的镜像和容器存储驱动,所以我们需要确保 overlay 模块已加载。
在四台节点上执行:
modprobe overlay可通过以下命令确认:
lsmod | grep overlay如果输出里能看到 overlay,说明模块已加载。
3 下载安装 containerd 二进制文件
这里我们不通过 yum/apt 直接安装,而是使用二进制方式部署,方便控制版本和路径。 假设你已经把 containerd-bin 包和 crictl 工具放在了一台管理机上,接下来用脚本批量分发到各节点。
for host in 192.168.7.46 192.168.7.47 192.168.7.48 192.168.7.49; do
ssh root@${host} "mkdir -p /opt/bin"
scp containerd-bin/* root@${host}:/opt/bin/containerd-bin/
scp /opt/bin/crictl root@${host}:/opt/bin/
ssh root@${host} "chmod 755 /opt/bin/containerd-bin/*"
ssh root@${host} "chmod +x /opt/bin/crictl"
done解释:
- containerd-bin/*:包含 containerd、containerd-shim、ctr 等相关二进制文件。
- crictl:一个与 CRI 交互的命令行工具,类似 Docker CLI,用于调试和验证容器运行时是否正常。
💡 小技巧: 如果条件允许,可以使用 nerdctl 来替代 crictl,体验更接近 docker 命令。
4 创建 crictl 配置
让 crictl 知道如何与 containerd 通信:
cat > /etc/crictl.yaml << 'EOF'
runtime-endpoint: unix:///run/containerd/containerd.sock
EOF这样以后执行 crictl ps、crictl images 时就能正常工作。
5 创建 containerd 配置文件
这里我们只需要调整几个关键项,原文件可以从网盘下载:
- 镜像源 mirrors(配置私有仓库/加速源)。
- sandbox 镜像(pause 镜像地址)。
- root/state 目录路径。
我们将完整配置放在 /opt/config/config.toml,再同步到各节点。
cat > /opt/config/config.toml << 'EOF'
# 以下为 containerd 配置文件
version = 3
root = '/var/lib/containerd'
state = '/run/containerd'
temp = ''
plugin_dir = ''
disabled_plugins = []
required_plugins = []
oom_score = 0
imports = []
[grpc]
address = '/run/containerd/containerd.sock'
tcp_address = ''
tcp_tls_ca = ''
tcp_tls_cert = ''
tcp_tls_key = ''
uid = 0
gid = 0
max_recv_message_size = 16777216
max_send_message_size = 16777216
[ttrpc]
address = ''
uid = 0
gid = 0
[debug]
address = ''
uid = 0
gid = 0
level = ''
format = ''
[metrics]
address = ''
grpc_histogram = false
[plugins]
[plugins.'io.containerd.cri.v1.images']
snapshotter = 'overlayfs'
disable_snapshot_annotations = true
discard_unpacked_layers = false
max_concurrent_downloads = 3
image_pull_progress_timeout = '15m0s'
image_pull_with_sync_fs = false
stats_collect_period = 10
[plugins.'io.containerd.cri.v1.images'.pinned_images]
sandbox = 'registry:5000/pause:3.10'
[plugins.'io.containerd.cri.v1.images'.registry]
config_path = ''
[plugins.'io.containerd.cri.v1.images'.registry.mirrors]
[plugins.'io.containerd.cri.v1.images'.registry.mirrors."registry:5000"]
endpoint = ["http://registry:5000"]
[plugins.'io.containerd.cri.v1.images'.registry.mirrors."harbor.yourcompany.com"]
endpoint = ["https://harbor.yourcompany.com"]
[plugins.'io.containerd.cri.v1.images'.registry.mirrors."docker.io"]
endpoint = ["https://docker.1ms.run"]
[plugins.'io.containerd.cri.v1.images'.registry.configs]
[plugins.'io.containerd.cri.v1.images'.registry.configs."harbor.yourcompany.com:8443"]
[plugins.'io.containerd.cri.v1.images'.registry.configs."registry:5000"]
[plugins.'io.containerd.cri.v1.images'.image_decryption]
key_model = 'node'
[plugins.'io.containerd.cri.v1.runtime']
enable_selinux = false
selinux_category_range = 1024
max_container_log_line_size = 16384
disable_apparmor = false
restrict_oom_score_adj = false
disable_proc_mount = false
unset_seccomp_profile = ''
tolerate_missing_hugetlb_controller = true
disable_hugetlb_controller = true
device_ownership_from_security_context = false
ignore_image_defined_volumes = false
netns_mounts_under_state_dir = false
enable_unprivileged_ports = true
enable_unprivileged_icmp = true
enable_cdi = true
cdi_spec_dirs = ['/etc/cdi', '/var/run/cdi']
drain_exec_sync_io_timeout = '0s'
ignore_deprecation_warnings = []
[plugins.'io.containerd.cri.v1.runtime'.containerd]
default_runtime_name = 'runc'
ignore_blockio_not_enabled_errors = false
ignore_rdt_not_enabled_errors = false
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes]
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc]
runtime_type = 'io.containerd.runc.v2'
runtime_path = ''
pod_annotations = []
container_annotations = []
privileged_without_host_devices = false
privileged_without_host_devices_all_devices_allowed = false
base_runtime_spec = ''
cni_conf_dir = ''
cni_max_conf_num = 0
snapshotter = ''
sandboxer = 'podsandbox'
io_type = ''
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
BinaryName = ''
CriuImagePath = ''
CriuWorkPath = ''
IoGid = 0
IoUid = 0
NoNewKeyring = false
Root = ''
ShimCgroup = ''
SystemdCgroup = true
[plugins.'io.containerd.cri.v1.runtime'.cni]
bin_dir = '/opt/cni/bin'
conf_dir = '/etc/cni/net.d'
max_conf_num = 1
setup_serially = false
conf_template = '/etc/cni/net.d/10-default.conf'
ip_pref = ''
use_internal_loopback = false
[plugins.'io.containerd.gc.v1.scheduler']
pause_threshold = 0.02
deletion_threshold = 0
mutation_threshold = 100
schedule_delay = '0s'
startup_delay = '100ms'
[plugins.'io.containerd.grpc.v1.cri']
disable_tcp_service = true
stream_server_address = '127.0.0.1'
stream_server_port = '0'
stream_idle_timeout = '4h0m0s'
enable_tls_streaming = false
[plugins.'io.containerd.grpc.v1.cri'.x509_key_pair_streaming]
tls_cert_file = ''
tls_key_file = ''
[plugins.'io.containerd.image-verifier.v1.bindir']
bin_dir = '/opt/containerd/image-verifier/bin'
max_verifiers = 10
per_verifier_timeout = '10s'
[plugins.'io.containerd.internal.v1.opt']
path = '/opt/containerd'
[plugins.'io.containerd.internal.v1.tracing']
[plugins.'io.containerd.metadata.v1.bolt']
content_sharing_policy = 'shared'
[plugins.'io.containerd.monitor.container.v1.restart']
interval = '10s'
[plugins.'io.containerd.monitor.task.v1.cgroups']
no_prometheus = false
[plugins.'io.containerd.nri.v1.nri']
disable = false
socket_path = '/var/run/nri/nri.sock'
plugin_path = '/opt/nri/plugins'
plugin_config_path = '/etc/nri/conf.d'
plugin_registration_timeout = '5s'
plugin_request_timeout = '2s'
disable_connections = false
[plugins.'io.containerd.runtime.v2.task']
platforms = ['linux/amd64']
[plugins.'io.containerd.service.v1.diff-service']
default = ['walking']
sync_fs = false
[plugins.'io.containerd.service.v1.tasks-service']
blockio_config_file = ''
rdt_config_file = ''
[plugins.'io.containerd.shim.v1.manager']
env = []
[plugins.'io.containerd.snapshotter.v1.blockfile']
root_path = ''
scratch_file = ''
fs_type = ''
mount_options = []
recreate_scratch = false
[plugins.'io.containerd.snapshotter.v1.btrfs']
root_path = ''
[plugins.'io.containerd.snapshotter.v1.devmapper']
root_path = ''
pool_name = ''
base_image_size = ''
async_remove = false
discard_blocks = false
fs_type = ''
fs_options = ''
[plugins.'io.containerd.snapshotter.v1.native']
root_path = ''
[plugins.'io.containerd.snapshotter.v1.overlayfs']
root_path = ''
upperdir_label = false
sync_remove = false
slow_chown = false
mount_options = []
[plugins.'io.containerd.snapshotter.v1.zfs']
root_path = ''
[plugins.'io.containerd.tracing.processor.v1.otlp']
[plugins.'io.containerd.transfer.v1.local']
max_concurrent_downloads = 3
max_concurrent_uploaded_layers = 3
config_path = ''
[cgroup]
path = ''
[timeouts]
'io.containerd.timeout.bolt.open' = '0s'
'io.containerd.timeout.metrics.shimstats' = '2s'
'io.containerd.timeout.shim.cleanup' = '5s'
'io.containerd.timeout.shim.load' = '5s'
'io.containerd.timeout.shim.shutdown' = '3s'
'io.containerd.timeout.task.state' = '2s'
[stream_processors]
[stream_processors.'io.containerd.ocicrypt.decoder.v1.tar']
accepts = ['application/vnd.oci.image.layer.v1.tar+encrypted']
returns = 'application/vnd.oci.image.layer.v1.tar'
path = 'ctd-decoder'
args = ['--decryption-keys-path', '/etc/containerd/ocicrypt/keys']
env = ['OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf']
[stream_processors.'io.containerd.ocicrypt.decoder.v1.tar.gzip']
accepts = ['application/vnd.oci.image.layer.v1.tar+gzip+encrypted']
returns = 'application/vnd.oci.image.layer.v1.tar+gzip'
path = 'ctd-decoder'
args = ['--decryption-keys-path', '/etc/containerd/ocicrypt/keys']
env = ['OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf']
EOF同步到所有节点:
for host in 192.168.7.46 192.168.7.47 192.168.7.48 192.168.7.49; do
scp /opt/config/config.toml root@${host}:/etc/containerd/config.toml
done6 创建 systemd unit 文件
定义 containerd 的启动方式,放在 /etc/systemd/system/containerd.service。
cat > /opt/config/containerd.service << 'EOF'
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target
[Service]
Environment="PATH=/opt/bin/containerd-bin:/bin:/sbin:/usr/bin:/usr/sbin"
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/opt/bin/containerd-bin/containerd --log-level warn
Restart=always
RestartSec=5
Delegate=yes
KillMode=process
OOMScoreAdjust=-999
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity
[Install]
WantedBy=multi-user.target
EOF同步到所有节点:
for host in 192.168.7.46 192.168.7.47 192.168.7.48 192.168.7.49; do
scp /opt/config/containerd.service root@${host}:/etc/systemd/system/containerd.service
done7 启动并设置开机自启
在所有节点上执行:
systemctl daemon-reload
systemctl enable containerd
systemctl restart containerd确认状态:
systemctl status containerd如果显示 active (running),说明运行成功。
8 验证 containerd
通过 crictl 验证是否能正常通信:
crictl info | grep runtimeType正常情况下输出类似:
"runtimeType": "io.containerd.runc.v2"再测试拉取一个镜像:
crictl pull busybox:latest
crictl images如果能看到 busybox 镜像,说明 containerd 安装成功。
安装和配置 Kubernetes Master 组件
1 在所有节点部署 kube-nginx 代理
在高可用架构中,我们使用 nginx 来做 kube-apiserver 的本地反向代理,把后端多个 apiserver 实例统一起来,客户端只需要访问 127.0.0.1:6443 即可。
1.1 创建 nginx 工作目录(如 yum 安装可忽略)
mkdir -p /etc/kube-nginx/bin
mkdir -p /etc/kube-nginx/logs
mkdir -p /etc/kube-nginx/conf如果你使用二进制文件方式(推荐),需要提前准备好 nginx 可执行文件,我的共享网盘上可下载。
1.2 安装 nginx
方式一:复制二进制文件
chmod +x /opt/bin/nginx
scp /opt/bin/nginx root@192.168.7.46:/etc/kube-nginx/bin
scp /opt/bin/nginx root@192.168.7.47:/etc/kube-nginx/bin
scp /opt/bin/nginx root@192.168.7.48:/etc/kube-nginx/bin
scp /opt/bin/nginx root@192.168.7.49:/etc/kube-nginx/bin方式二:直接安装
yum install -y nginx1.3 配置 nginx 代理
在 /etc/kube-nginx/conf/kube-nginx.conf 写入以下内容:
cat > /etc/kube-nginx/conf/kube-nginx.conf <<EOF
user root;
worker_processes 1;
error_log /etc/kube-nginx/logs/error.log warn;
events {
worker_connections 6000;
}
stream {
upstream backend {
server 192.168.7.46:6443 max_fails=2 fail_timeout=3s;
server 192.168.7.47:6443 max_fails=2 fail_timeout=3s;
server 192.168.7.48:6443 max_fails=2 fail_timeout=3s;
}
server {
listen 127.0.0.1:6443;
proxy_connect_timeout 3s;
proxy_pass backend;
}
}
EOF解释:
- upstream backend 定义了后端 kube-apiserver 的真实地址。
- server 部分监听 127.0.0.1:6443,并把请求转发到后端。
- 这样 kubectl、controller-manager 等都只访问本地 127.0.0.1:6443,后端切换由 nginx 负责。
1.4 创建 nginx systemd 管理文件
cat > /etc/systemd/system/kube-nginx.service <<EOF
[Unit]
Description=nginxproxy for kube-apiservers
After=network.target
After=network-online.target
Wants=network-online.target
[Service]
Type=forking
ExecStartPre=/etc/kube-nginx/bin/nginx -c /etc/kube-nginx/conf/kube-nginx.conf -p /etc/kube-nginx -t
ExecStart=/etc/kube-nginx/bin/nginx -c /etc/kube-nginx/conf/kube-nginx.conf -p /etc/kube-nginx
ExecReload=/etc/kube-nginx/bin/nginx -c /etc/kube-nginx/conf/kube-nginx.conf -p /etc/kube-nginx -s reload
PrivateTmp=true
Restart=always
RestartSec=15
StartLimitInterval=0
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF注意:如果你使用 yum 安装,服务文件是自动创建的,若是手工编译的按上面配置即可,但要注意目录。
1.5 启动服务
systemctl daemon-reload
systemctl enable kube-nginx
systemctl restart kube-nginx
systemctl status kube-nginx确认 127.0.0.1:6443 已经能正常监听:
ss -lntp | grep 64432 安装 Kubernetes Master 组件
以下步骤在所有 Master 节点 执行。
2.1 拷贝二进制文件(我的网盘上可直接下载,也可从官网下载)
cp kube-apiserver /opt/bin/
cp kube-controller-manager /opt/bin/
cp kube-scheduler /opt/bin/
cp kubectl /opt/bin/
chmod +x /opt/bin/*2.2 分发 kubeconfig 配置文件(第三篇教程生成的文件)
cp kube-controller-manager.kubeconfig /etc/kubernetes/
cp kube-scheduler.kubeconfig /etc/kubernetes/注意修改 server 地址: /etc/kubernetes/kube-controller-manager.kubeconfig 和 /etc/kubernetes/kube-scheduler.kubeconfig 文件中的
server: https://192.168.7.46:6443修改为:
server: https://127.0.0.1:6443因为我们通过本地 nginx 来做 apiserver 高可用。
2.3 创建 Kubernetes 核心证书
编辑 CSR 文件:
cat > /opt/ssl/kubernetes-csr.json <<EOF
{
"CN": "kubernetes",
"hosts": [
"127.0.0.1",
"192.168.7.46",
"192.168.7.47",
"192.168.7.48",
"10.68.0.1",
"kubernetes",
"kubernetes.default",
"kubernetes.default.svc",
"kubernetes.default.svc.cluster",
"kubernetes.default.svc.cluster.local"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Shanghai",
"L": "XS",
"O": "k8s",
"OU": "System"
}
]
}
EOF解释:
- hosts 中包含所有 Master IP、Service ClusterIP(10.68.0.1)、以及集群内部的域名。
- 如果需要暴露公网,也可以把公网 IP 加进去。
2.4 生成证书和私钥
cd /opt/ssl && /opt/install/bin/cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes kubernetes-csr.json | /opt/install/bin/cfssljson -bare kubernetes会生成:
- kubernetes.pem
- kubernetes-key.pem
此证书用于 kube-apiserver 与 etcd、kubelet、客户端的双向认证。
2.5 创建 aggregator-proxy 证书
cat > /opt/ssl/aggregator-proxy-csr.json <<EOF
{
"CN": "aggregator",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Shanghai",
"L": "XS",
"O": "k8s",
"OU": "System"
}
]
}
EOF生成证书:
cd /opt/ssl && /opt/install/bin/cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes aggregator-proxy-csr.json | /opt/install/bin/cfssljson -bare aggregator-proxy解释:
- aggregator-proxy 证书用于启用聚合层(apiserver-aggregation),支持扩展 API,比如 metrics-server。
2.6 分发证书
将以下证书分发到每个 Master 节点的 /etc/kubernetes/ssl:
ca.pem
ca-key.pem
kubernetes.pem
kubernetes-key.pem
aggregator-proxy.pem
aggregator-proxy-key.pem2.7 配置 systemd 服务文件
kube-apiserver.service
cat > /etc/systemd/system/kube-apiserver.service <<EOF
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
[Service]
ExecStart=/opt/bin/kube-apiserver \
--allow-privileged=true \
--anonymous-auth=false \
--api-audiences=api,istio-ca \
--authorization-mode=Node,RBAC \
--bind-address=192.168.7.46 \
--client-ca-file=/etc/kubernetes/ssl/ca.pem \
--endpoint-reconciler-type=lease \
--etcd-cafile=/etc/kubernetes/ssl/ca.pem \
--etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem \
--etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem \
--etcd-servers=https://192.168.7.46:2379,https://192.168.7.47:2379,https://192.168.7.48:2379 \
--kubelet-certificate-authority=/etc/kubernetes/ssl/ca.pem \
--kubelet-client-certificate=/etc/kubernetes/ssl/kubernetes.pem \
--kubelet-client-key=/etc/kubernetes/ssl/kubernetes-key.pem \
--secure-port=6443 \
--service-account-issuer=https://kubernetes.default.svc \
--service-account-signing-key-file=/etc/kubernetes/ssl/ca-key.pem \
--service-account-key-file=/etc/kubernetes/ssl/ca.pem \
--service-cluster-ip-range=10.68.0.0/16 \
--service-node-port-range=30000-32767 \
--tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
--requestheader-client-ca-file=/etc/kubernetes/ssl/ca.pem \
--requestheader-allowed-names= \
--requestheader-extra-headers-prefix=X-Remote-Extra- \
--requestheader-group-headers=X-Remote-Group \
--requestheader-username-headers=X-Remote-User \
--proxy-client-cert-file=/etc/kubernetes/ssl/aggregator-proxy.pem \
--proxy-client-key-file=/etc/kubernetes/ssl/aggregator-proxy-key.pem \
--enable-aggregator-routing=true \
--v=2
Restart=always
RestartSec=5
Type=notify
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF参数说明:
- --etcd-servers:指定 etcd 集群地址。
- --service-cluster-ip-range:指定 Service 的虚拟网段。
- --authorization-mode=Node,RBAC:启用 RBAC 授权。
kube-controller-manager.service
cat > /etc/systemd/system/kube-controller-manager.service <<EOF
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
[Service]
ExecStart=/opt/bin/kube-controller-manager \
--allocate-node-cidrs=true \
--authentication-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \
--authorization-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \
--bind-address=0.0.0.0 \
--cluster-cidr=172.20.0.0/16 \
--cluster-name=kubernetes \
--cluster-signing-cert-file=/etc/kubernetes/ssl/ca.pem \
--cluster-signing-key-file=/etc/kubernetes/ssl/ca-key.pem \
--kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \
--leader-elect=true \
--node-cidr-mask-size=24 \
--root-ca-file=/etc/kubernetes/ssl/ca.pem \
--service-account-private-key-file=/etc/kubernetes/ssl/ca-key.pem \
--service-cluster-ip-range=10.68.0.0/16 \
--use-service-account-credentials=true \
--v=2
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF- --cluster-cidr=172.20.0.0/16:Pod 网段,要与前面 CNI 配置保持一致。
- --service-cluster-ip-range=10.68.0.0/16:Service 网段。
kube-scheduler.service
cat > /etc/systemd/system/kube-scheduler.service <<EOF
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
[Service]
ExecStart=/opt/bin/kube-scheduler \
--authentication-kubeconfig=/etc/kubernetes/kube-scheduler.kubeconfig \
--authorization-kubeconfig=/etc/kubernetes/kube-scheduler.kubeconfig \
--bind-address=0.0.0.0 \
--kubeconfig=/etc/kubernetes/kube-scheduler.kubeconfig \
--leader-elect=true \
--v=2
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF2.8 启动服务
systemctl daemon-reload
systemctl enable kube-apiserver kube-controller-manager kube-scheduler
systemctl restart kube-apiserver
systemctl restart kube-controller-manager
systemctl restart kube-scheduler确认服务正常运行:
systemctl status kube-apiserver
systemctl status kube-controller-manager
systemctl status kube-scheduler2.9 检查集群状态
在 Master 节点完成组件启动后,执行 kubectl get cs 会输出类似下面的结果:
[root@master ~]# kubectl version --short
Client Version: v1.33.1
Server Version: v1.33.1
[root@master ~]# kubectl get cs
NAME STATUS MESSAGE ERROR
controller-manager Healthy ok
scheduler Healthy ok
etcd-0 Healthy {"health":"true"}
etcd-1 Healthy {"health":"true"}
etcd-2 Healthy {"health":"true"}解释:
- controller-manager:返回 Healthy ok 表示控制器正常工作。
- scheduler:返回 Healthy ok 表示调度器正常。
- etcd-0/1/2:每个 etcd 节点返回 {"health":"true"} 表示 etcd 集群健康。
如果有某个组件状态为 Unhealthy,则需要检查对应的 systemd 服务日志,比如:
journalctl -u kube-apiserver -f
journalctl -u kube-controller-manager -f
journalctl -u kube-scheduler -f如果输出中 controller-manager、scheduler、etcd 状态为 Healthy,说明 Master 组件已成功启动。
添加 Worker Node 节点
1 创建 Node 节点所需目录
Node 节点需要运行 kubelet 和 kube-proxy,还需要 CNI 插件来管理 Pod 网络。提前建立相关目录:
mkdir -p /var/lib/kubelet \
/var/lib/kube-proxy \
/etc/cni/net.d \
/opt/cni/bin \
/etc/kubernetes/ssl \
/opt/bin说明:
- /var/lib/kubelet:存放 kubelet 运行时数据。
- /var/lib/kube-proxy:存放 kube-proxy 状态和配置。
- /etc/cni/net.d:存放 CNI 配置文件。
- /opt/cni/bin:存放 CNI 插件二进制。
- /etc/kubernetes/ssl:存放证书和密钥。
- /opt/bin:存放 Kubernetes 组件二进制文件。
2 分发 kubelet 和 kube-proxy 二进制文件
在 Master 节点下载好官方发布的二进制文件后(网盘上也有提供),将其拷贝到所有 Worker 节点:
cp kubelet kube-proxy /opt/bin/
chmod +x /opt/bin/kubelet /opt/bin/kube-proxy3 安装 CNI 插件二进制
从官方 CNI 项目下载完整的插件包,解压并分发到节点:
cp cni-bin/* /opt/cni/bin/
chmod +x /opt/cni/bin/*4 生成 kubelet 的证书与 kubeconfig
4.1 准备证书签名请求
以 master-01 节点为例(IP:192.168.7.46,主机名:master-01):
cat > /opt/ssl/master-01-kubelet-csr.json << EOF
{
"CN": "system:node:master-01",
"hosts": [
"127.0.0.1",
"192.168.7.46",
"master-01"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Shanghai",
"L": "XS",
"O": "system:nodes",
"OU": "System"
}
]
}
EOF说明:
- CN 必须是 system:node:<节点名>,Kubernetes 会基于 RBAC 验证节点身份。
- hosts 中必须包含节点的 IP 和主机名。
- 在控制机上统一生成所有证书文件
4.2 生成 kubelet 证书和私钥
cd /opt/ssl
/opt/install/bin/cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes master-01-kubelet-csr.json | /opt/install/bin/cfssljson -bare master-01-kubelet得到的文件:
- master-01-kubelet.pem —— kubelet 的证书
- master-01-kubelet-key.pem —— kubelet 的私钥
- master-01-kubelet.csr —— 证书签名请求(可忽略)
4.3 生成 kubelet.kubeconfig
# 设置集群参数
/opt/install/bin/kubectl config set-cluster kubernetes \
--certificate-authority=/opt/ssl/ca.pem \
--embed-certs=true \
--server=https://127.0.0.1:6443 \
--kubeconfig=/opt/conf/master-01-kubelet.kubeconfig
# 设置客户端认证参数
/opt/install/bin/kubectl config set-credentials system:node:master-01 \
--client-certificate=/opt/ssl/master-01-kubelet.pem \
--embed-certs=true \
--client-key=/opt/ssl/master-01-kubelet-key.pem \
--kubeconfig=/opt/conf/master-01-kubelet.kubeconfig
# 设置上下文
/opt/install/bin/kubectl config set-context default \
--cluster=kubernetes \
--user=system:node:master-01 \
--kubeconfig=/opt/conf/master-01-kubelet.kubeconfig
# 选择默认上下文
/opt/install/bin/kubectl config use-context default \
--kubeconfig=/opt/conf/master-01-kubelet.kubeconfig5 分发证书和 kubeconfig
将证书和配置文件拷贝到目标节点:
scp /opt/ssl/ca.pem root@192.168.7.46:/etc/kubernetes/ssl/ca.pem
scp /opt/ssl/master-01-kubelet.pem root@192.168.7.46:/etc/kubernetes/ssl/kubelet.pem
scp /opt/ssl/master-01-kubelet-key.pem root@192.168.7.46:/etc/kubernetes/ssl/kubelet-key.pem
scp /opt/conf/master-01-kubelet.kubeconfig root@192.168.7.46:/etc/kubernetes/kubelet.kubeconfig6 配置 CNI
在节点创建 CNI 配置文件:
cat > /etc/cni/net.d/10-default.conf << EOF
{
"name": "mynet",
"cniVersion": "0.3.1",
"type": "bridge",
"bridge": "mynet0",
"isDefaultGateway": true,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "host-local",
"subnet": "172.20.0.0/16"
}
}
EOF注意:172.20.0.0/16 为 Pod 网段,这里先写死,后续安装 Calico 时会被覆盖。
7 配置 kubelet
cat > /var/lib/kubelet/config.yaml << EOF
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
address: 0.0.0.0
authentication:
anonymous:
enabled: false
webhook:
cacheTTL: 2m0s
enabled: true
x509:
clientCAFile: /etc/kubernetes/ssl/ca.pem
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 5m0s
cacheUnauthorizedTTL: 30s
cgroupDriver: systemd
cgroupsPerQOS: true
clusterDNS:
- 169.254.20.10
clusterDomain: cluster.local
configMapAndSecretChangeDetectionStrategy: Watch
containerLogMaxFiles: 3
containerLogMaxSize: 10Mi
enforceNodeAllocatable:
- pods
eventBurst: 10
eventRecordQPS: 5
evictionHard:
imagefs.available: 10%
memory.available: 300Mi
nodefs.available: 10%
nodefs.inodesFree: 5%
evictionPressureTransitionPeriod: 5m0s
failSwapOn: true
fileCheckFrequency: 40s
hairpinMode: hairpin-veth
healthzBindAddress: 0.0.0.0
healthzPort: 10248
httpCheckFrequency: 40s
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80
imageMinimumGCAge: 2m0s
kubeAPIBurst: 100
kubeAPIQPS: 50
makeIPTablesUtilChains: true
maxOpenFiles: 1000000
maxPods: 110
nodeLeaseDurationSeconds: 40
nodeStatusReportFrequency: 1m0s
nodeStatusUpdateFrequency: 10s
oomScoreAdj: -999
podPidsLimit: -1
port: 10250
readOnlyPort: 0
resolvConf: /etc/resolv.conf
runtimeRequestTimeout: 2m0s
serializeImagePulls: true
streamingConnectionIdleTimeout: 4h0m0s
syncFrequency: 1m0s
tlsCertFile: /etc/kubernetes/ssl/kubelet.pem
tlsPrivateKeyFile: /etc/kubernetes/ssl/kubelet-key.pem
EOF- 使用dns本地缓存,clusterDNS写localdnsIP:169.254.20.10,保持不变即可。
8 创建 kubelet systemd 文件
cat > /etc/systemd/system/kubelet.service << EOF
[Unit]
Description=Kubernetes Kubelet
After=network.target
[Service]
WorkingDirectory=/var/lib/kubelet
ExecStartPre=/bin/mount -o remount,rw '/sys/fs/cgroup'
ExecStart=/opt/bin/kubelet \
--config=/var/lib/kubelet/config.yaml \
--kubeconfig=/etc/kubernetes/kubelet.kubeconfig \
--container-runtime-endpoint=unix:///run/containerd/containerd.sock \
--hostname-override=master-01 \
--root-dir=/var/lib/kubelet \
--v=2
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF- hostname-override:注意使用node-name
9 启动 kubelet
systemctl daemon-reload
systemctl enable kubelet
systemctl restart kubelet10 配置 kube-proxy
10.1 分发 kube-proxy.kubeconfig
在控制节点修改 kube-proxy.kubeconfig,确保改写 APIServer 地址为 127.0.0.1:6443(使用 Nginx 代理实现高可用),然后分发:
scp /opt/conf/kube-proxy.kubeconfig root@192.168.7.46:/etc/kubernetes/kube-proxy.kubeconfig10.2 创建 kube-proxy 配置文件
cat > /var/lib/kube-proxy/kube-proxy-config.yaml << EOF
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
clientConnection:
kubeconfig: "/etc/kubernetes/kube-proxy.kubeconfig"
clusterCIDR: "172.20.0.0/16"
conntrack:
maxPerCore: 32768
min: 131072
tcpCloseWaitTimeout: 1h0m0s
tcpEstablishedTimeout: 24h0m0s
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: "master-01"
metricsBindAddress: 0.0.0.0:10249
mode: "iptables"
EOF- clusterCIDR:根据clusterCIDR 判断集群内外部流量,kube-proxy 会对访问 Service IP 的请求做 SNAT
- hostnameOverride:值必须与 kubelet 的配置一致,否则 kube-proxy 启动后会找不到该 Node,从而不会创建任何 iptables 规则
- mode也可使用ipvs.
10.3 创建 kube-proxy systemd 文件
cat > /etc/systemd/system/kube-proxy.service << EOF
[Unit]
Description=Kubernetes Kube-Proxy Server
After=network.target
[Service]
WorkingDirectory=/var/lib/kube-proxy
ExecStart=/opt/bin/kube-proxy \
--config=/var/lib/kube-proxy/kube-proxy-config.yaml
Restart=always
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF10.4 启动 kube-proxy
systemctl daemon-reload
systemctl enable kube-proxy
systemctl restart kube-proxy11 验证节点是否加入成功
在 Master 节点执行:
kubectl get nodes输出示例:
NAME STATUS ROLES AGE VERSION
master-01 Ready 2m v1.33.1✅ 至此,一个完整的 Worker Node 已经成功加入 Kubernetes 集群。后续可以重复以上步骤,为更多节点生成 kubelet 证书与 kubeconfig,再分发配置,即可批量扩容集群。
部署 Calico 网络插件
1 创建 Calico 证书
由于我们使用 Etcd 作为 Calico 的数据存储,因此需要为它创建客户端证书,保证 Etcd 与 Calico 的通信安全。
1.1 准备证书请求文件
在控制节点执行:
mkdir -p /opt/ssl
cat > /opt/ssl/calico-csr.json << EOF
{
"CN": "calico",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Shanghai",
"L": "XS",
"O": "k8s",
"OU": "System"
}
]
}
EOF说明:
- CN:Common Name,这里写成 calico。
- hosts:空数组即可,不需要绑定固定主机名。
- O:组织设为 k8s,方便统一管理。
1.2 生成证书与私钥
cd /opt/ssl && /opt/install/bin/cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes calico-csr.json | /opt/install/bin/cfssljson -bare calico执行后会得到:
- calico.pem —— 证书文件
- calico-key.pem —— 私钥文件
1.3 创建 Calico 访问 Etcd 的 Secret
我们把 Calico 的证书封装为 Secret,方便 Pod 调用。
cd /opt/ssl && \
/opt/bin/kubectl create secret generic -n kube-system calico-etcd-secrets \
--from-file=etcd-ca=ca.pem \
--from-file=etcd-key=calico-key.pem \
--from-file=etcd-cert=calico.pem此时,calico-etcd-secrets 已经存放在 kube-system 命名空间下。
2 分发证书到所有节点
Calico 的 Pod 会运行在每个节点,因此证书必须同步。
在控制节点执行:
for node in k8s-master01 k8s-master02 k8s-master03 k8s-node01; do
ssh $node "mkdir -p /etc/calico/ssl"
scp /opt/ssl/ca.pem /opt/ssl/calico.pem /opt/ssl/calico-key.pem $node:/etc/calico/ssl/
done说明:
- /etc/calico/ssl 是固定路径,确保所有节点保持一致。
- 同步后,Calico Pod 才能正常连接 Etcd。
3 配置 Calico DaemonSet YAML 文件
官方提供的 calico.yaml 可以作为模板,但需要根据我们的环境调整。 假设我们把配置文件放在 /opt/yml/calico.yaml。
3.1 下载并编辑 Calico YAML
你可以从官方或我的网盘获取 calico.yaml,然后重点修改以下参数:
# etcd 地址
etcd_endpoints:"https://192.168.7.46:2379,https://192.168.7.47:2379,https://192.168.7.48:2379"
# 后端模式(默认使用 bird + IPIP)
calico_backend:"bird"
# etcd 证书路径
etcd_ca_cert_file:"/etc/calico/ssl/ca.pem"
etcd_cert_file:"/etc/calico/ssl/calico.pem"
etcd_key_file:"/etc/calico/ssl/calico-key.pem"
# 镜像仓库,替换镜像地址
image:" "
# 自动探测 IP 的方式
IP_AUTODETECTION_METHOD:"interface=ens*"
或
IP_AUTODETECTION_METHOD:"can-reach=192.168.7.46"
192.168.7.46是master01的地址
# 使用 IPIP 模式
CALICO_IPV4POOL_IPIP:"Always"
# Pod 网段(需和 kube-controller-manager 保持一致)
CALICO_IPV4POOL_CIDR: "172.20.0.0/16"说明:
- etcd_endpoints:必须写集群的所有 Etcd 节点。
- CALICO_IPV4POOL_IPIP:这里演示使用 IPIP 模式,保证跨节点互通。
- CALICO_IPV4POOL_CIDR:必须与 --cluster-cidr 保持一致,否则 Pod 网络会不通。
3.2 应用 Calico 配置
/opt/bin/kubectl apply -f /opt/yml/calico.yaml查看 Pod 状态:
/opt/bin/kubectl -n kube-system get pods -o wide | grep calico正常情况下,每个节点都会启动一个 calico-node Pod。
4 删除默认 CNI 配置
上一篇生成的 10-default.conf,会与 Calico 冲突。需要清理:
rm -f /etc/cni/net.d/10-default.conf此操作需在所有节点执行。
5 准备 calicoctl 工具配置文件
calicoctl 是 Calico 的命令行工具,支持查看 IPPool、Node、Policy 等配置。 在所有节点创建配置文件:
mkdir -p /etc/calico
cat > /etc/calico/calicoctl.cfg << EOF
apiVersion: projectcalico.org/v3
kind: CalicoAPIConfig
metadata:
spec:
datastoreType: "etcdv3"
etcdEndpoints: "https://192.168.7.46:2379,https://192.168.7.47:2379,https://192.168.7.48:2379"
etcdKeyFile: /etc/calico/ssl/calico-key.pem
etcdCertFile: /etc/calico/ssl/calico.pem
etcdCACertFile: /etc/kubernetes/ssl/ca.pem
EOF6 验证 Calico 网络
安装好后,我们可以用 calicoctl 验证:
6.1 查看 Node 状态
/opt/bin/calicoctl node status可以看到每个节点的 BGP 状态是否 Established。
6.2 查看 IP 池
/opt/bin/calicoctl get ippool -o wide确认 172.20.0.0/16 已经存在。
6.3 创建一个测试 Pod
cat > /opt/yml/test-pod.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
name: net-test
namespace: default
spec:
containers:
- name: net-test
image: busybox
command: ["sleep", "3600"]
EOF
/opt/bin/kubectl apply -f /opt/yml/test-pod.yaml进入容器测试跨节点互通:
/opt/bin/kubectl exec -it net-test -- ping <另一个Pod的IP>若能正常互通,说明 Calico 部署成功。
部署 CoreDNS、Dashboard、Metrics
1 先看这些组件在干什么
- CoreDNS:Kubernetes 集群的集群 DNS 服务,负责把 *.svc.cluster.local、kubernetes.default 等集群内部域名解析成 Service/Pod 的 IP。CoreDNS 以 Deployment + Service 形式运行,是整个集群服务发现的根基。
- NodeLocal DNSCache(nodelocaldns):在每个节点本地运行的 DNS 缓存代理(DaemonSet),监听一个本地的 link-local IP(常见 169.254.20.10)。Pod 首先查询本地缓存,缓存未命中才转发到 CoreDNS。关系是 缓存层(NodeLocal)→ 上游解析器(CoreDNS),NodeLocal 并不替代 CoreDNS,只是降低 CoreDNS 的查询压力与延迟。
- metrics-server:轻量级的指标聚合器,从每台 Node 的 kubelet 拉取瞬时的 CPU/Memory 使用数据,暴露 metrics.k8s.io API,供 kubectl top、HPA(Horizontal Pod Autoscaler)使用。它不是持久化监控系统。
- Kubernetes Dashboard:集群的 Web UI(运维/查看资源/简单操作)。用于快速检查命名空间、Pod、Deployment、事件等,适合日常运维和排错;生产环境推荐配合 RBAC 与 ingress/IDP 做细粒度权限与加固。
2 组件部署
2.1 运行 coredns.yml
从官网或网盘下载 yaml(保存到 /opt/yml/coredns.yaml),然后执行:
mkdir -p /opt/yml
# 把 coredns.yaml 放到 /opt/yml/ 下(从官网或你们网盘下载)
# 应用
kubectl apply -f /opt/yml/coredns.yaml检查项(必须做):
1)确认 Service ClusterIP(Cluster DNS IP)一致
- 如果集群已有 kube-dns/coredns Service,可以先读出它的 ClusterIP:
kubectl -n kube-system get svc kube-dns -o wide
kubectl -n kube-system get svc kube-dns -o jsonpath='{.spec.clusterIP}'; echo- 不要盲目把 Service clusterIP 改成任意值,务必保证与 --service-cluster-ip-range 匹配。
2)验证:
kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide
kubectl -n kube-system get svc kube-dns -o wide
kubectl -n kube-system logs -l k8s-app=kube-dns -c coredns --tail=2002.2 运行 nodelocaldns.yml
从官网或网盘下载 yaml(保存到 /opt/yml/nodelocaldns.yaml),然后执行:
# 下载或拷贝 nodelocaldns.yaml 到 /opt/yml/
kubectl apply -f /opt/yml/nodelocaldns.yaml补充说明:
1)必须替换或确认的三项(在 nodelocaldns manifest 中):
- LOCAL_DNS_IP(node-local 监听地址,推荐 169.254.20.10,link-local):169.254.20.10
- CLUSTER_DNS_IP(CoreDNS 的 Service ClusterIP,上一步确认得到的值)
- DNS_DOMAIN(通常 cluster.local,若你集群使用其它域名请调整)
2)验证 NodeLocal 状态与日志:
kubectl -n kube-system get pods -l k8s-app=node-local-dns -o wide
kubectl -n kube-system logs -l k8s-app=node-local-dns -c node-cache --tail=2002.3 运行 metrics-server.yml
从官网或网盘下载 yaml(保存到 /opt/yml/metrics-server.yaml),然后执行:
kubectl apply -f /opt/yml/metrics-server.yaml1)验证:
kubectl -n kube-system get deployment metrics-server
kubectl -n kube-system get pods -l k8s-app=metrics-server -o wide
kubectl -n kube-system logs -l k8s-app=metrics-server -c metrics-server --tail=200
# 检查 metrics 对外是否可用
kubectl top nodes
kubectl top pods --all-namespaces2)排查要点:
- metrics-server 日志中常见错误:无法连接 kubelet(TLS 或 port/地址问题);
- 确认 kubelet 允许 metrics-server 访问(kubelet 的认证/授权、证书是否信任)。
2.4 运行 dashboard.yml
从官网或网盘下载 yaml(保存到 /opt/yml/dashboard/):
1)创建目录 /opt/yml/dashboard,下载 yaml:
mkdir -p /opt/yml/dashboard
cd /opt/yml/dashboard
# 把下面三个文件放到此目录(从官网或网盘下载)
# - kubernetes-dashboard.yaml
# - admin-user-sa-rbac.yaml
# - read-user-sa-rbac.yaml2)运行 dashboard(apply):
# 确保命名空间存在(manifest 里通常会创建)
kubectl apply -f /opt/yml/dashboard/补充说明:
- 推荐使用 helm 安装新版 Dashboard(更容易升级与配置)。如果使用 manifest,请确保 manifest 版本与集群版本兼容。
- Dashboard 默认 Service 类型为 ClusterIP,推荐通过 kubectl proxy 或安全的 Ingress + Auth/HTTPS 暴露访问,不建议直接暴露为 NodePort 而不做权限控制。
2.5 验证,查看 dashboard
1)基本检查:
# 系统相关资源
kubectl -n kube-system get pods -o wide
kubectl -n kube-system get svc -o wide
# Dashboard 命名空间
kubectl -n kubernetes-dashboard get pods -o wide
kubectl -n kubernetes-dashboard get svc -o wide2)获取 admin-user 的 token:
kubectl -n kube-system describe secret admin-user3)访问 Dashboard:
# 查看暴露的svc Nodeport端口访问
kubectl get svc -A常见验证点:
- 能登录并看到命名空间、Pod、Deployment 列表
运维优化与实战清单
1 证书与身份(必须被监控)
- 要点:控制面组件(apiserver/controller/scheduler)、etcd、kubelet 都有证书,证书过期会导致服务中断。
- 自动化:把证书到期检查写成定时任务(cron)并上Prometheus/Alertmanager或者直接用简单的脚本发邮件/告警。示例脚本(检测剩余天数并告警):
#!/bin/bash
for f in /etc/kubernetes/ssl/*.crt; do
end=$(openssl x509 -in $f -noout -enddate | cut -d= -f2)
endsec=$(date -d "$end" +%s)
nowsec=$(date +%s)
days=$(( (endsec-nowsec)/86400 ))
if [ $days -le 30 ]; then
echo "WARNING: $f expires in $days days"
fi
done- 轮换策略:若使用 kubeadm,可提前在维护窗口 kubeadm certs renew <name>。在自动化体系内考虑「证书到期前 30 天」发第一次提醒,14 天、7 天、3 天再提醒。
2 节点配置与压测(资源配置要基于业务)
- 原则:先压测、再定配置;别直接凭感觉上大机或小机。
- 常见推荐(仅作经验参考):
- 普通业务:4CPU/16GB → 起步;中等负载 8C/32G。
- IO 密集型:优先考虑本地 SSD 或吞吐更高的磁盘,不要把 IO 密集型和延迟敏感型混 colocate。
- 压测要项:CPU、内存压力、磁盘吞吐、IOPS、网络带宽、连接数上限(conntrack)。
- 容量保守值:把 kube-reserved、system-reserved 和 eviction 留足,避免系统进程被 OOM。
3 磁盘空间与驱逐(eviction)策略
- Kubelet 驱逐阈值(示例):
evictionHard:
memory.available: "100Mi"
nodefs.available: "10%"
imagefs.available: "15%"- 这些值需要结合实际磁盘大小、应用占用和压测结果调整。
- 监控:
- Node-exporter 的 node_filesystem_* 指标做文件系统使用率报警。
- 对 Pod 的 ephemeral-storage 请求/限制进行合理设置,避免单 Pod 占满节点磁盘。
- 报警建议:
- 节点磁盘利用率 > 75% 就预警,> 85% 强烈预警并触发自动化清理或扩容流程。
4 合理设置 QoS 与资源请求/限制
- QoS 三类回顾:
- Guaranteed:requests == limits → 最强资源保证(适合关键服务)。
- Burstable:设置 requests,limits 不相等 → 可弹性降级。
- BestEffort:无 requests/limits → 最容易被驱逐。
- 实践:
- 关键组件(网关、核心处理链路)尽量用 Guaranteed。
- 用压测数据不断修正 requests:初始值可能不准,运营中以 SLO/SLI 作为调整依据。
- 配置示例(Guaranteed):
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "512Mi"5 存活(liveness)与就绪(readiness)探针——别随便复制粘贴
- 原则:
- liveness 用于判断进程是否“死掉”,触发重启。
- readiness 用于判断流量是否可以导入到 Pod(控制负载均衡)。
- 示例:
livenessProbe:
httpGet:
path:/healthz
port:8080
initialDelaySeconds:10
periodSeconds:15
failureThreshold:3
readinessProbe:
httpGet:
path:/ready
port:8080
initialDelaySeconds:5
periodSeconds:10
failureThreshold: 3- 注意:探针逻辑必须轻量且快速,不能在探针中触发重型检查(会放大问题)。
6 调度与隔离:不要把“嘈杂邻居”放一起
- 做法:
- CPU/IO 密集型工作使用独立 node pool + taint/toleration 或 nodeSelector。
- 对延迟敏感或需要稳定资源的服务开启 Guaranteed 且考虑 cpu-manager-policy=static(需要 kubelet 配置和专核)。
- 亲测技巧:
- 使用 podAntiAffinity 或 PodTopologySpread 控制副本分布,降低单节点故障影响。
- 增加 ResourceQuota 限制租户滥用资源。
7 存储:NAS(RWX)不是万能解
- 问题场景:大量写入到 NAS(NFS) 会导致 IO 排队、延迟飙升,甚至影响整个集群。
- 建议:
- 写入密集型使用本地 SSD 或块存储(RWO),配合数据同步/归档到对象存储(S3/MinIO)以降低压力。
- 使用 CSI 驱动时注意 provisioner 的并发限制和超时设置。
- 若必须使用 NFS:用 noatime、合适的 rsize/wsize,做好监控(latency、iops)。
- 性能验证:fio、dd 等工具先压测再投入生产。
8 安全(别把集群当内网小圈子)
- 必须:API Server、etcd、kubelet 通信均启 TLS;关闭 kubelet 的匿名只读端口。
- 强化项:
- 开启 RBAC 严格最小权限原则。
- 启用 API Server 审计(audit logs)并集中备份。
- Secrets 加密 at-rest(EncryptionConfiguration)。
- 使用 NetworkPolicy 限制 Pod 间通信(默认 deny)。
- 定期用 kube-bench、trivy 做基线扫描与镜像检查。
- 登录与证书管理:不要把 kubeconfig 明文放在不可信位置。
9 版本策略与升级策略(别追新)
- 原则:不要第一个上新版生产;等一个稳定期(社区 bug 修复、主流用户验证)再评估。
- 升级流程建议:
- 在测试环境按升级路径执行完整演练(含组件、CSI、CNI)。
- 备份 etcd 快照并验证能恢复(见下一节)。
- 控制面分步升级,滚动升级 worker,观察指标和 SLO。
- 回滚预案:升级失败要有明确回退步骤(etcd restore、控制面回退),并在维护窗口执行。
10 备份与恢复(etcd 是生命线)
- 定期快照:
ETCDCTL_API=3 etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /var/backups/etcd-$(date +%F).db- 备份频率:根据变更频率,至少每日一次,关键时刻(发布)前再一次。
- 异地存储:快照需上传到远端对象存储(离线/冷存储),避免单点失效。
- 恢复演练:把恢复流程写成脚本并每季度演练一次,确保镜像、PV 元数据也能恢复或重建。
11 监控与告警(实践化)
核心栈:Prometheus + node-exporter + kube-state-metrics + Alertmanager + Grafana(或 Loki/Tempo 做 logs/traces)。
推荐告警(示例表达式):
- alert:NodeDiskHighUsage expr:100*(node_filesystem_size_bytes{mountpoint="/"}-node_filesystem_avail_bytes{mountpoint="/"}) /node_filesystem_size_bytes{mountpoint="/"}>80 for:5m labels: {severity:warning} annotations: {summary:"Node {{ $labels.instance }} disk > 80%"}expr: increase(kube_pod_container_status_restarts_total[15m]) > 3expr: histogram_quantile(0.99, sum(rate(apiserver_request_duration_seconds_bucket[5m])) by (le, verb)) > 1- API Server 请求延迟:
Pod 重启过多(CrashLoop):
节点磁盘利用率:
实践提示:
- 告警不要太多噪声:先在 30/70/90 分别设置等级,生产先处理 70+。
- 把告警与 Runbook 联系起来:每个告警都要有快速处置步骤(谁来干、如何干)。
12 灾备与演练(不要只写在文档里)
- 每日/每周/每月任务清单
- 演练建议:
- 定期做etcd restore演练。
- 演练控制面单点故障(删除一个 master 节点,模拟恢复)。
- 做一次“节点磁盘满”演练,验证驱逐策略和容量扩容流程。
- 使用 Chaos 工具(Chaos Mesh/chaoskube)在非生产环境做故障注入训练。
13 运行维护清单(可直接复制执行)
- 每日检查:
- Control plane 状态:kubectl get componentstatuses(或控制面自定义监控)
- etcd 集群健康:etcdctl endpoint health
- 重要告警清单是否有新条目
- 每周检查:
- 节点磁盘、内存、网络错误统计趋势
- Pod 重启趋势和失败率
- 日志快速扫描主要服务异常关键词
- 每月任务:
- 演练一次 etcd 快照恢复
- 校验证书到期情况并提前安排续期
- 升级前的测试窗口(staging 全流程演练)
14 常见故障与快速处置(短 Runbook)
- 证书过期:
- 快速检查:kubeadm certs check-expiration 或 openssl 检查。
- 立即执行:在维护窗口或短暂流量降级时用 kubeadm certs renew,备份旧证书,重启相关组件。
- etcd quorum 丢失:
- 快速动作:不要随意删除成员。查看 etcdctl member list,若必要用备份恢复并重新建立集群。
- 事后:核查网络、磁盘、内存压力原因。
- 节点磁盘满:
- 临时:drain 节点 kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
- 永久:清理日志、清理 image cache 或扩容磁盘,调整 eviction 策略。
- API Server 高延迟:
- 检查 apiserver 请求量、etcd 延迟 (etcd latency),并适当调大 apiserver 的 QPS/Burst 或扩容 etcd。
15 上不上的决策:要不要上 Kubernetes?
- 成本-收益分析要点:
- 是否需要多实例、多副本、自动扩缩?(如果是,K8s 是好工具)
- 是否团队有运维能力投入到平台化?K8s 的维护成本高,尤其是二进制裸装场景。
- 是否能把应用改为无状态/拆分成可容器化组件?若不能,收益有限。
- 替代方案:对少量服务或特殊场景,可以先用 VM+配置管理(Ansible/Terraform)做容器化前的一步,等有规模再迁到 K8s。