025-K8S-二进制高可用部署

  1. 一、系统环境
  2. 二、集群规划
  3. 三、基础环境配置(所有节点)
    1. 1. 主机名设置
    2. 2. 配置静态IP
    3. 3. 修改hosts文件
    4. 4. 修改终端颜色
    5. 5. 更换系统软件源
    6. 6. 修改防火墙
    7. 7. 禁用 Selinux
    8. 8. 设置时区
    9. 9. 关闭 swap 分区
    10. 10. 安装基本工具
    11. 11. 修改系统最大打开文件数
    12. 12. 安装 ipvs
    13. 13. 开启路由转发
    14. 14. 排除 calico 网卡被 NetworkManager 所管理
    15. 15. 修改内核参数
    16. 16. 集群时间同步设置
    17. 17. 配置免密登录
    18. 18. 安装 Docker
      1. 18.1 安装 Docker 二进制包
      2. 18.2 配置 containerd.service
      3. 18.3 配置 docker.service
      4. 18.4 准备 docker 的 socket 文件
      5. 18.5 配置 Docker 加速器
      6. 18.6 启动 Docker
    19. 19. 安装 cri-docker
      1. 19.1 安装 cri-docker 二进制包
      2. 19.2 配置 cri-docker.service
      3. 19.3 配置 cri-docker.socket
      4. 19.4 启动 cri-docker
  4. 四、K8S 与 ETCD 下载及安装(在 master01 节点上操作)
    1. 1. 安装 K8S 二进制包
    2. 2. 安装 ETCD 二进制包
    3. 3. 生成相关证书
      1. 3.1 安装证书工具
      2. 3.2 生成 ETCD 证书
        1. 3.2.1 ca-config.json
        2. 3.2.2 etcd-ca-csr.json
        3. 3.2.3 生成 ETCD 的根 CA 证书
        4. 3.2.4 etcd-csr.json
        5. 3.2.5 生成 ETCD 节点证书
        6. 3.2.6 将证书复制到其他节点
      3. 3.3 生成 K8S 相关证书
        1. 3.3.1 ca-config.json
        2. 3.3.2 ca-csr.json
        3. 3.3.3 生成 Kubernetes 集群的根 CA 证书
        4. 3.3.4 apiserver-csr.json
        5. 3.3.5 生成 apiserver 证书
        6. 3.3.6 apiserver 聚合证书
          1. 3.3.6.1 front-proxy-ca-csr.json
          2. 3.3.6.2 生成 Kubernetes Front Proxy 根 CA 证书
        7. 3.3.7 生成 controller-manage 的证书
        8. 3.3.8 生成 kube-scheduler 的证书
        9. 3.3.9 生成 admin 的证书配置
        10. 3.3.10 创建 kube-proxy 证书
        11. 3.3.11 生成 ServiceAccount(简称 SA)签名密钥对
    4. 4. 分发证书到其它节点
      1. 4.1 分发到 master 节点
      2. 4.2 分发到 node 节点
    5. 5. 查看证书
      1. 5.1 master 节点
      2. 5.2 node 节点
  5. 五、K8S 系统组件配置
    1. 1. ETCD 配置
      1. 1.1 Master01 节点
      2. 1.2 Master02 节点
      3. 1.3 Master03 节点
      4. 1.4 创建 ETCD Service
      5. 1.5 启动 ETCD Service
      6. 1.6 检查 ETCD 集群的健康状态
    2. 2. Nginx 配置
      1. 2.1 编译安装
      2. 2.2 nginx 配置文件
      3. 2.3 配置 Nginx Service
      4. 2.4 设置 Nginx 开机自启
    3. 3. kube-apiserver 配置
      1. 3.1 创建必要的目录
      2. 3.2 配置 kube-apiserver.service
      3. 3.3 启动 kube-apiserver
    4. 4. kube-controller-manager 配置
      1. 4.1 配置 kube-controller-manager.service
      2. 4.2 启动 kube-controller-manager
    5. 5. kube-scheduler 配置
      1. 5.1 kube-scheduler.service
      2. 5.2 启动 kube-scheduler
  6. 六、TLS Bootstrapping 配置
    1. 1. 生成 bootstrap 的 token
    2. 2. 配置 bootstrap-kubelet.kubeconfig
    3. 3. 创建 bootstrap-secret.yaml
    4. 4. 创建 kubelet-bootstrap-rbac.yaml
    5. 5. 分发配置文件
    6. 6. 查看集群状态
  7. 七、Node 配置
    1. 1. 复制相关证书至 node 节点
    2. 2. 配置 kubelet(所有节点)
      1. 2.1 编辑 kubelet.service
      2. 2.2 编辑 kubelet-conf.yml
      3. 2.3 同步文件到其它节点
      4. 2.4 所有节点启动 kubelet
      5. 2.5 测试
    3. 3. kube-proxy 配置(所有节点)
      1. 3.1 同步 kube-proxy.kubeconfig
      2. 3.2 编辑 kube-proxy.service
      3. 3.3 编辑 kube-proxy.yaml
      4. 3.4 启动 kube-proxy
      5. 3.5 同步 kube-proxy 到其它节点
  8. 八、安装 calico 网络插件
    1. 1. 下载 calico 资源清单
    2. 2. 编辑 calico 资源清单-IPV4
    3. 3. 编辑 calico 资源清单-IPV6
    4. 4. 执行 calico 资源清单
  9. 九、安装 Core DNS
    1. 1. 安装Helm(仅master01)
    2. 2. 安装 CoreDns
      1. 2.1 下载 CoreDns 安装包
      2. 2.2 编辑配置文件
      3. 2.3 安装 CoreDns
  10. 十、安装 Metrics Server
    1. 1. 下载 metrics-server 资源清单
    2. 2. 编辑 components.yaml
    3. 3. 部署 metrics-server
  11. 十一、安装Dashboard
    1. 1. 下载并执行 dashboard 资源清单
    2. 2. 创建 ServiceAccount
    3. 3. 更改 dashboard 的 svc 为 NodePort
    4. 4. 创建 token 访问
  12. 十二、安装命令补全
  13. 十三、高可用验证
    1. 1. 组件状态验证
    2. 2. 使用 dnstools 测试
    3. 3. Nginx 部署测试
    4. 4. Ubuntu 镜像部署测试
      1. 3.1 部署 NetworkPolicy
      2. 3.2 部署 Ubuntu Pod

一、系统环境

  • 操作系统:Rocky Linux 9.3
  • 内核版本:5.14.0-284.11.1.el9_2.x86_64
  • 容器运行时:Docker + CRI-Docker
  • Kubernetes 版本:1.29.2
  • 网络插件:Calico

二、集群规划

基于二进制安装包,部署三主两从的高可用集群。

主机名称 IPV4地址 CPU/内存/磁盘 说明 软件
10.20.1.203 2C/4G/200G 外网节点(可翻墙) 下载各种所需安装包
Master01 10.20.1.101 2C/4G/200G master节点 apiserver、controller-manager、scheduler、etcd、 kubelet、kube-proxy、nginx
Master02 10.20.1.102 2C/4G/200G master节点 apiserver、controller-manager、scheduler、etcd、 kubelet、kube-proxy、nginx
Master03 10.20.1.103 2C/4G/200G master节点 apiserver、controller-manager、scheduler、etcd、 kubelet、kube-proxy、nginx
Node01 10.20.1.104 2C/4G/200G node节点 kubelet、kube-proxy、nginx
Node02 10.20.1.105 2C/4G/200G node节点 kubelet、kube-proxy、nginx

三、基础环境配置(所有节点)

1. 主机名设置

设置集群中各个节点的主机名

Master01 节点

hostnamectl set-hostname k8s-master01

Master02 节点

hostnamectl set-hostname k8s-master02

Master03 节点

hostnamectl set-hostname k8s-master03

Node01 节点

hostnamectl set-hostname k8s-node01

Node02 节点

hostnamectl set-hostname k8s-node02

2. 配置静态IP

集群内的每个几点都需要配置唯一的IP地址,这里同时配置了 IPV4地址 和 IPV6地址。

Master01 节点

[root@k8s-master01 ~]# cat /etc/NetworkManager/system-connections/ens34.nmconnection
[ipv4]
method=manual
address1=10.20.1.101/24;10.20.1.1
dns=61.132.163.68;114.114.114.114

[ipv6]
method=manual
addresses=2400:3200::101/64
gateway=2400:3200::1
dns=2400:3200::1;2400:3200:baba::1;2001:4860:4860::8888;2001:4860:4860::8844

Master02 节点

[root@k8s-master02 ~]# cat /etc/NetworkManager/system-connections/ens34.nmconnection
[ipv4]
method=manual
address1=10.20.1.102/24;10.20.1.1
dns=61.132.163.68;114.114.114.114

[ipv6]
method=manual
addresses=2400:3200::102/64
gateway=2400:3200::1
dns=2400:3200::1;2400:3200:baba::1;2001:4860:4860::8888;2001:4860:4860::8844

Master03 节点

[root@k8s-master03 ~]# cat /etc/NetworkManager/system-connections/ens34.nmconnection
[ipv4]
method=manual
address1=10.20.1.103/24;10.20.1.1
dns=61.132.163.68;114.114.114.114

[ipv6]
method=manual
addresses=2400:3200::103/64
gateway=2400:3200::1
dns=2400:3200::1;2400:3200:baba::1;2001:4860:4860::8888;2001:4860:4860::8844

Node01 节点

[root@k8s-node01 ~]# cat /etc/NetworkManager/system-connections/ens34.nmconnection
[ipv4]
method=manual
address1=10.20.1.104/24;10.20.1.1
dns=61.132.163.68;114.114.114.114

[ipv6]
method=manual
addresses=2400:3200::104/64
gateway=2400:3200::1
dns=2400:3200::1;2400:3200:baba::1;2001:4860:4860::8888;2001:4860:4860::8844

Node02 节点

[root@k8s-node02 ~]# cat /etc/NetworkManager/system-connections/ens34.nmconnection
[ipv4]
method=manual
address1=10.20.1.105/24;10.20.1.1
dns=61.132.163.68;114.114.114.114

[ipv6]
method=manual
addresses=2400:3200::105/64
gateway=2400:3200::1
dns=2400:3200::1;2400:3200:baba::1;2001:4860:4860::8888;2001:4860:4860::8844

3. 修改hosts文件

配置集群各节点 hostname 和 ip 的映射

cat >> /etc/hosts << "EOF"
10.20.1.101 k8s-master01 m1
10.20.1.102 k8s-master02 m2
10.20.1.103 k8s-master03 m3
10.20.1.104 k8s-node01 n1
10.20.1.105 k8s-node02 n2
2400:3200::101 k8s-master01 m1
2400:3200::102 k8s-master02 m2
2400:3200::103 k8s-master03 m3
2400:3200::104 k8s-node01 n1
2400:3200::105 k8s-node02 n2
EOF

验证 hosts 文件配置

cat /etc/hosts
ping k8s-master01 -c 4  # 默认使用 IPv4
ping6 k8s-master01 -c 4  # 使用 IPv6

4. 修改终端颜色

这里只是修改shell终端显示文本的颜色,非必要步骤。

cat << EOF >> ~/.bashrc
PS1="\[\e[37;47m\][\[\e[32;47m\]\u\[\e[34;47m\]@\h \[\e[36;47m\]\w\[\e[0m\]]\\$ "
EOF

# 让修改立即见效
source ~/.bashrc

命令解析:

这段命令用于修改当前用户的 Bash Shell 提示符PS1),并将其设置写入到 ~/.bashrc 文件中,以便在每次登录或启动 Shell 时自动加载该配置。

PS1="..."
定义 Shell 的主提示符格式(Prompt String 1),即你在终端中输入命令时显示的提示符。

最终效果如下:

终端颜色修改

5. 更换系统软件源

将 Rocky 默认源替换成阿里源,提升软件安装速度。

# 更新源
sed -e 's|^mirrorlist=|#mirrorlist=|g' \
    -e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \
    -i.bak /etc/yum.repos.d/[Rr]ocky*.repo
    

# 刷新dnf缓存
dnf makecache

# 验证源更新
dnf repolist

命令解析:

# 使用 sed 命令修改 Rocky Linux 的 YUM/DNF 源配置文件,切换到阿里云的镜像源。
sed -e 's|^mirrorlist=|#mirrorlist=|g' \
    -e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \
    -i.bak /etc/yum.repos.d/[Rr]ocky*.repo
    
# 将以 mirrorlist= 开头的行注释掉(在前面加 #)
-e 's|^mirrorlist=|#mirrorlist=|g'

# 将以 #baseurl= 开头并指向默认 Rocky Linux 源的行取消注释,并替换为阿里云镜像源 URL。
's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g'

# -i.bak:直接修改文件,并为原文件创建备份(扩展名为 .bak)。
# 修改 /etc/yum.repos.d/ 目录下所有以 rocky 或 Rocky 开头的 .repo 文件。
# 修改完成后,原始文件会被备份为 .bak 文件。
-i.bak /etc/yum.repos.d/[Rr]ocky*.repo



# 更新本地缓存,确保系统可以快速查询软件包信息。
dnf makecache

6. 修改防火墙

关闭默认防火墙firewalld,配置 iptables 防火墙

systemctl stop firewalld
systemctl disable firewalld
yum -y install iptables-services
systemctl start iptables
iptables -F
systemctl enable iptables
service iptables save

命令解析:

# 停止运行 firewalld
systemctl stop firewalld

# 禁止 firewalld 开机自启
systemctl disable firewalld

# 安装 iptables 服务,用于管理 Linux 的防火墙规则
yum -y install iptables-services

# 使防火墙规则立即生效,并开始运行 iptables 防火墙服务。
systemctl start iptables

# 删除当前的防火墙规则,通常用于重置或清理防火墙规则。
iptables -F

# 设置 iptables 服务开机自启动,确保服务器重启后,iptables 服务会自动加载防火墙规则。
systemctl enable iptables

# 将当前 iptables 的规则配置保存到文件中(通常是 /etc/sysconfig/iptables),以便在系统重启或 iptables 服务重新启动后,能够自动加载保存的规则。
service iptables save

7. 禁用 Selinux

setenforce 0
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
grubby --update-kernel ALL --args selinux=0

命令解析:

# 将 SELinux 的模式设置为 Permissive(宽容模式)。
# 0:表示设置为 Permissive 模式,此模式下 SELinux 不会强制执行安全策略,而是记录违规日志。
# 1:表示 Enforcing 模式,此模式下 SELinux 会强制执行安全策略。
setenforce 0

# 修改 SELinux 配置文件 /etc/selinux/config,将 SELINUX 设置为 disabled。永久禁用 SELinux,即使系统重启也不会再启用。
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config

# 通过 grubby 工具将 selinux=0 参数添加到所有内核启动配置中。
grubby --update-kernel ALL --args selinux=0
grubby --info DEFAULT

# 查看是否禁用,
grubby --info DEFAULT
# 回滚内核层禁用操作,、
grubby --update-kernel ALL --remove-args selinux

8. 设置时区

timedatectl set-timezone Asia/Shanghai

9. 关闭 swap 分区

swapoff -a
sed -i 's:/dev/mapper/rl-swap:#/dev/mapper/rl-swap:g' /etc/fstab

命令解析:

#  立即关闭系统中所有的交换分区
swapoff -a

# 注释掉 /etc/fstab 文件中定义的交换分区挂载条目,防止系统在重启后重新启用交换分区。
sed -i 's:/dev/mapper/rl-swap:#/dev/mapper/rl-swap:g' /etc/fstab

# 验证交换分区是否关系
free -h
输出中 Swap 一栏的值会变为 0。

10. 安装基本工具

dnf -y install openssh-server wget tree bash-completion psmisc vim net-tools lrzsz nfs-utils epel-release telnet rsync yum-utils device-mapper-persistent-data lvm2 git tar curl network-scripts

11. 修改系统最大打开文件数

cat >> /etc/security/limits.conf <<EOF
* soft nofile 655360
* hard nofile 131072
* soft nproc 655350
* hard nproc 655350
* soft memlock unlimited
* hard memlock unlimited
EOF
 
echo "ulimit -SHn 65535" >> /etc/profile
source /etc/profile

命令解析:

soft nofile 655360
soft表示软限制,nofile 表示一个进程可打开的最大文件数,默认值为1024。这里的软限制设置为655360,即一个进程可打开的最大文件数为655360。

hard nofile 131072
hard表示硬限制,即系统设置的最大值。nofile表示一个进程可打开的最大文件数,默认值为4096。这里的硬限制设置为131072,即系统设置的最大文件数为131072。

soft nproc 655350
soft表示软限制,nproc表示一个用户可创建的最大进程数,默认值为30720。这里的软限制设置为655350,即一个用户可创建的最大进程数为655350。

hard nproc 655350
hard表示硬限制,即系统设置的最大值。nproc表示一个用户可创建的最大进程数,默认值为4096。这里的硬限制设置为655350,即系统设置的最大进程数为655350。

soft memlock unlimited
seft表示软限制,memlock表示一个进程可锁定在RAM中的最大内存,默认值为64 KB。这里的软限制设置为unlimited,即一个进程可锁定的最大内存为无限制。

hard memlock unlimited
hard表示硬限制,即系统设置的最大值。memlock表示一个进程可锁定在RAM中的最大内存,默认值为64 KB。这里的硬限制设置为unlimited,即系统设置的最大内存锁定为无限制。

12. 安装 ipvs

# 安装 ipvs
yum install ipvsadm ipset sysstat conntrack libseccomp -y

cat >> /etc/modules-load.d/ipvs.conf <<EOF 
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
ip_tables
ip_set
xt_set
ipt_set
ipt_rpfilter
ipt_REJECT
ipip
EOF

systemctl restart systemd-modules-load.service

lsmod | grep -e ip_vs -e nf_conntrack

命名解析:

  • ipvsadm 命令行工具,用于管理IPVS(IP Virtual Server)

  • ipset 内核级工具,用于高效管理IP地址、端口或MAC地址的集合(sets)

  • sysstat 系统性能监控工具包,包括sar、iostat、mpstat等命令,用于收集和报告CPU、内存、磁盘I/O、网络等系统统计数据

  • conntrack 命令行工具(conntrack-tools的一部分),用于管理Netfilter的连接跟踪表(connection tracking table)

  • libseccomp 这是一个库,用于支持seccomp(Secure Computing Mode),seccomp是Linux内核功能,用于限制进程的系统调用(syscall),从而增强安全性(如沙箱化)

cat >> ….. 显示的加载内核模块

  • ip_vs 核心IPVS模块,提供虚拟服务器功能,用于L4负载均衡
  • ip_vs_rr IPVS的 round-robin 调度算法模块(轮询)
  • ip_vs_wrr IPVS的 weighted round-robin 调度算法模块(加权轮询)
  • ip_vs_sh IPVS的 source hashing 调度算法模块(源地址哈希)
  • nf_conntrack Netfilter连接跟踪模块,跟踪网络连接状态(用于NAT和防火墙)
  • ip_tables iptables的核心模块,用于IPv4包过滤和NAT
  • ip_set IP集合管理模块,支持高效的IP列表处理
  • xt_set iptables的扩展模块,用于与ip_set集成
  • ipt_set iptables的set匹配模块(类似xt_set,但特定于IPv4)
  • ipt_rpfilter iptables的反向路径过滤模块,防止IP欺骗攻击
  • ipt_REJECT iptables的REJECT目标模块,用于拒绝包并发送拒绝消息
  • ipip 隧道模块,用于封装IP包(类似VPN隧道)

systemctl restart systemd-modules-load.service:立即加载这些模块(因为/etc/modules-load.d/目录下的.conf文件会被systemd-modules-load服务读取)

lsmod | grep -e ip_vs -e nf_conntrack :检查模块是否加载成功。

13. 开启路由转发

echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
sysctl -p

14. 排除 calico 网卡被 NetworkManager 所管理

cat > /etc/NetworkManager/conf.d/calico.conf << EOF
[keyfile]
unmanaged-devices=interface-name:cali*;interface-name:tunl*;interface-name:vxlan.calico;interface-name:vxlan-v6.calico;interface-name:wireguard.cali;interface-name:wg-v6.cali
EOF

systemctl restart NetworkManager

命令解析:

这个参数用于指定不由 NetworkManager 管理的设备。它由以下两个部分组成

interface-name:cali*
表示以 "cali" 开头的接口名称被排除在 NetworkManager 管理之外。例如,"cali0", "cali1" 等接口不受 NetworkManager 管理

interface-name:tunl*
表示以 "tunl" 开头的接口名称被排除在 NetworkManager 管理之外。例如,"tunl0", "tunl1" 等接口不受 NetworkManager 管理

interface-name:vxlan.calico
匹配名为vxlan.calico的接口,Calico在VXLAN模式下可能使用该接口名进行跨节点通信(VXLAN是一种覆盖网络技术)

interface-name:vxlan-v6.calico
匹配名为vxlan-v6.calico的接口,这是Calico在支持IPv6的VXLAN模式下使用的接口名

interface-name:wireguard.cali
匹配名为wireguard.cali的接口,Calico支持使用WireGuard(一种高性能VPN协议)进行加密通信,这个接口用于WireGuard隧道。

interface-name:wg-v6.cali
匹配名为wg-v6.cali的接口,这是Calico在IPv6网络中使用的WireGuard接口

通过使用这个参数,可以将特定的接口排除在 NetworkManager 的管理范围之外,以便其他工具或进程可以独立地管理和配置这些接口

15. 修改内核参数

cat > /etc/sysctl.d/k8s.conf << EOF
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
fs.may_detach_mounts = 1
vm.overcommit_memory=1
vm.panic_on_oom=0
fs.inotify.max_user_watches=89100
fs.file-max=52706963
fs.nr_open=52706963
net.netfilter.nf_conntrack_max=2310720
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl =15
net.ipv4.tcp_max_tw_buckets = 36000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_orphans = 327680
net.ipv4.tcp_orphan_retries = 3
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.ip_conntrack_max = 65536
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_timestamps = 0
net.core.somaxconn = 16384
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
net.ipv6.conf.lo.disable_ipv6 = 0
net.ipv6.conf.all.forwarding = 1
EOF

sysctl --system

命令解析:

这些是Linux系统的一些参数设置,用于配置和优化网络、文件系统和虚拟内存等方面的功能。以下是每个参数的详细解释:

net.ipv4.ip_forward = 1
这个参数启用了IPv4的IP转发功能,允许服务器作为网络路由器转发数据包。

net.bridge.bridge-nf-call-iptables = 1
当使用网络桥接技术时,将数据包传递到iptables进行处理。

fs.may_detach_mounts = 1
允许在挂载文件系统时,允许被其他进程使用。

vm.overcommit_memory=1
该设置允许原始的内存过量分配策略,当系统的内存已经被完全使用时,系统仍然会分配额外的内存。

vm.panic_on_oom=0
当系统内存不足(OOM)时,禁用系统崩溃和重启。

fs.inotify.max_user_watches=89100
设置系统允许一个用户的inotify实例可以监控的文件数目的上限。

fs.file-max=52706963
设置系统同时打开的文件数的上限。

fs.nr_open=52706963
设置系统同时打开的文件描述符数的上限。

net.netfilter.nf_conntrack_max=2310720
设置系统可以创建的网络连接跟踪表项的最大数量。

net.ipv4.tcp_keepalive_time = 600
设置TCP套接字的空闲超时时间(秒),超过该时间没有活动数据时,内核会发送心跳包。

net.ipv4.tcp_keepalive_probes = 3
设置未收到响应的TCP心跳探测次数。

net.ipv4.tcp_keepalive_intvl = 15
设置TCP心跳探测的时间间隔(秒)。

net.ipv4.tcp_max_tw_buckets = 36000
设置系统可以使用的TIME_WAIT套接字的最大数量。

net.ipv4.tcp_tw_reuse = 1
启用TIME_WAIT套接字的重新利用,允许新的套接字使用旧的TIME_WAIT套接字。

net.ipv4.tcp_max_orphans = 327680
设置系统可以同时存在的TCP套接字垃圾回收包裹数的最大数量。

net.ipv4.tcp_orphan_retries = 3
设置系统对于孤立的TCP套接字的重试次数。

net.ipv4.tcp_syncookies = 1
启用TCP SYN cookies保护,用于防止SYN洪泛攻击。

net.ipv4.tcp_max_syn_backlog = 16384
设置新的TCP连接的半连接数(半连接队列)的最大长度。

net.ipv4.ip_conntrack_max = 65536
设置系统可以创建的网络连接跟踪表项的最大数量。

net.ipv4.tcp_timestamps = 0
关闭TCP时间戳功能,用于提供更好的安全性。

net.core.somaxconn = 16384
设置系统核心层的连接队列的最大值。

net.ipv6.conf.all.disable_ipv6 = 0
启用IPv6协议。

net.ipv6.conf.default.disable_ipv6 = 0
启用IPv6协议。

net.ipv6.conf.lo.disable_ipv6 = 0
启用IPv6协议。

net.ipv6.conf.all.forwarding = 1
允许IPv6数据包转发。

16. 集群时间同步设置

在3主2从的集群环境中,配置3台 master 节点向使用 chony 通过外部网络同步时间校正自身的时间,并作为时间主服务器给集群中的其它 node 节点校正时间。

主节点时间权重不同

Master01 节点

yum install chrony -y

cat > /etc/chrony.conf << EOF 
pool ntp1.aliyun.com iburst
pool ntp2.aliyun.com iburst
pool ntp3.aliyun.com iburst
pool ntp4.aliyun.com iburst
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
allow 10.20.1.0/24
local stratum 10
keyfile /etc/chrony.keys
leapsectz right/UTC
logdir /var/log/chrony
EOF

systemctl restart chronyd ; systemctl enable chronyd

命令解析:

pool ntp.aliyun.com iburst
指定使用ntp.aliyun.com作为时间服务器池,iburst选项表示在初始同步时会发送多个请求以加快同步速度。

driftfile /var/lib/chrony/drift
指定用于保存时钟漂移信息的文件路径。

makestep 1.0 3
设置当系统时间与服务器时间偏差大于1秒时,会以1秒的步长进行调整。如果偏差超过3秒,则立即进行时间调整。

rtcsync
启用硬件时钟同步功能,可以提高时钟的准确性。

allow 10.20.1.0/24
允许10.20.1.0/24网段范围内的主机与chrony进行时间同步。

local stratum 10
将本地时钟设为stratum 10,stratum值表示时钟的准确度,值越小表示准确度越高。

keyfile /etc/chrony.keys
指定使用的密钥文件路径,用于对时间同步进行身份验证。

leapsectz right/UTC
指定时区为UTC。

logdir /var/log/chrony
指定日志文件存放目录。

Master02 节点

yum install chrony -y

cat > /etc/chrony.conf << EOF 
pool ntp1.aliyun.com iburst
pool ntp2.aliyun.com iburst
pool ntp3.aliyun.com iburst
pool ntp4.aliyun.com iburst
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
allow 10.20.1.0/24
local stratum 12
keyfile /etc/chrony.keys
leapsectz right/UTC
logdir /var/log/chrony
EOF

systemctl restart chronyd ; systemctl enable chronyd

Master03 节点

yum install chrony -y

cat > /etc/chrony.conf << EOF 
pool ntp1.aliyun.com iburst
pool ntp2.aliyun.com iburst
pool ntp3.aliyun.com iburst
pool ntp4.aliyun.com iburst
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
allow 10.20.1.0/24
local stratum 15
keyfile /etc/chrony.keys
leapsectz right/UTC
logdir /var/log/chrony
EOF

systemctl restart chronyd ; systemctl enable chronyd

Node01节点 和 Node02节点

yum install chrony -y

cat > /etc/chrony.conf << EOF 
pool 10.20.1.101 iburst
pool 10.20.1.102 iburst
pool 10.20.1.103 iburst
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
keyfile /etc/chrony.keys
leapsectz right/UTC
logdir /var/log/chrony
EOF

systemctl restart chronyd ; systemctl enable chronyd

#使用客户端进行验证
chronyc sources -v

17. 配置免密登录

配置 k8s-master01 节点免密登录到其它节点,方便后续从 master01 节点往其它节点发送文件。

master-01

yum install -y expect
ssh-keygen -t rsa -P "" -f /root/.ssh/id_rsa
export user=root
export pass=123456
host=(k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02 m1 m2 m3 n1 n2)
 
for host in ${host[@]};do expect -c "
spawn ssh-copy-id -i /root/.ssh/id_rsa.pub $user@$host
    expect {
        \"*yes/no*\" {send \"yes\r\"; exp_continue}
        \"*password*\" {send \"$pass\r\"; exp_continue}
        \"*Password*\" {send \"$pass\r\";}
    }";
done

命令解析:

# 一个自动化交互工具,用于处理需要用户输入的命令行交互
yum install -y expect

# 生成RSA类型的SSH密钥对(公钥和私钥),并存储在指定路径。-P "":设置空密码(无口令保护),便于自动化使用
ssh-keygen -t rsa -P "" -f /root/.ssh/id_rsa
export user=root
export pass=123456

# 定义一个Bash数组host,包含集群中所有节点的主机名和别名
host=(k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02)

# 遍历host数组,对每个节点执行expect脚本
for host in ${host[@]};do expect -c "

# 启动ssh-copy-id命令,将公钥文件/root/.ssh/id_rsa.pub分发到目标节点
spawn ssh-copy-id -i /root/.ssh/id_rsa.pub $user@$host
    expect {
    	# 匹配:当ssh-copy-id提示是否信任目标主机
    	# 动作:发送yes并换行(\r),确认连接
    	# exp_continue:继续匹配后续模式(不退出expect)
        \"*yes/no*\" {send \"yes\r\"; exp_continue}
        
        # 匹配:当提示输入密码(大写Password)
        # 动作:发送变量$pass(即123456)并换行
        # exp_continue:继续匹配后续模式。
        \"*password*\" {send \"$pass\r\"; exp_continue}
        \"*Password*\" {send \"$pass\r\";}
    }";
done

18. 安装 Docker

18.1 安装 Docker 二进制包

k8s-master01

# 二进制包下载地址:https://download.docker.com/linux/static/stable/x86_64/

# 创建目录,后续下载的文件都放在这里
mkdir -p /opt/software/ /opt/module/ && chmod 777 -R /opt/software/ /opt/module/

# 更新 openssh-server
dnf -y install  openssh-server

# 下载 Docker
wget https://download.docker.com/linux/static/stable/x86_64/docker-28.4.0.tgz

# 解压二进制包
tar xf docker-*.tgz

# 拷贝解压后的二进制包到 /usr/bin 目录下
cp docker/* /usr/bin/

# 将 Docker 复制到其它服务器中
hots='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
 
for i in $hots; do 
  echo $i; 
  rsync --rsync-path="sudo rsync" docker/* $user@$i:/usr/bin/; 
done

# 验证
$ docker --version
Docker version 28.4.0, build d8eb465

# 查看 containerd 版本
$ containerd --version
containerd github.com/containerd/containerd v1.7.28 b98a3aace656320842a23f4a392a33f46af97866

18.2 配置 containerd.service

  • containerd 的作用:它是一个独立的守护进程(daemon),处理低级任务,如镜像拉取、容器启动/停止、快照管理、网络/存储挂载等。它基于 OCI(Open Container Initiative)标准,与 runC(Docker 的另一个组件,用于实际执行容器)协作。

  • dockerd 的作用:Docker Daemon (dockerd) 不再直接管理容器,而是作为一个高层管理器,通过 gRPC 接口与 containerd 通信。dockerd 处理用户命令(如 docker run)、镜像构建、网络/卷管理等,并将容器相关操作委托给 containerd。

Docker底层依赖 containerd ,如果不安装 containerd ,Docker 会启动失败,因为 dockerd 需要连接到 containerd.sock(由 containerd 服务生成)。这会导致整个链条崩溃:cri-dockerd → dockerd → containerd。

即使 Kubernetes 通过 cri-dockerd 使用 Docker,Docker 本身还是依赖 containerd 来实际执行容器操作。没有 containerd,cri-dockerd 和 Docker 都无法工作。

# 下载 containerd.service, 版本需要与 docker 二进制包中的 containerd 一致
# https://github.com/containerd/containerd/blob/main/containerd.service
wget https://raw.githubusercontent.com/containerd/containerd/refs/tags/v1.7.28/containerd.service


# containerd 路径修改后,内容如下:
cat >/etc/systemd/system/containerd.service <<EOF
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target dbus.service

[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/bin/containerd

Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5
LimitNPROC=infinity
LimitCORE=infinity
LimitNOFILE=infinity
TasksMax=infinity
OOMScoreAdjust=-999

[Install]
WantedBy=multi-user.target
EOF


# 设置 containerd 开机自启
systemctl enable --now containerd.service


# 将 containerd.service 复制到其它服务器中
hots='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
for i in $hots; do 
  echo $i; 
  rsync --rsync-path="sudo rsync" /etc/systemd/system/containerd.service $user@$i:/etc/systemd/system/; 
done


# 配置设置每一台服务器 containerd 开机自启
hosts='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
for host in $hosts; do
  ssh root@$host "systemctl enable --now containerd.service"
done

# 验证
systemctl is-enabled containerd.service

命令解析:

[Unit]

Description=containerd container runtime:指定服务的描述信息。
Documentation=https://containerd.io:指定服务的文档链接。
After=network.target local-fs.target:指定服务的启动顺序,在网络和本地文件系统启动之后再启动该服务。
[Service]

ExecStartPre=-/sbin/modprobe overlay:在启动服务之前执行的命令,使用-表示忽略错误。
ExecStart=/usr/bin/containerd:指定服务的启动命令。
Type=notify:指定服务的类型,notify表示服务会在启动完成后向systemd发送通知。
Delegate=yes:允许服务代理其他服务的应答,例如收到关机命令后终止其他服务。
KillMode=process:指定服务终止时的行为,process表示终止服务进程。
Restart=always:指定服务终止后是否自动重启,always表示总是自动重启。
RestartSec=5:指定服务重启的时间间隔,单位为秒。
LimitNPROC=infinity:限制服务的最大进程数,infinity表示没有限制。
LimitCORE=infinity:限制服务的最大核心数,infinity表示没有限制。
LimitNOFILE=1048576:限制服务的最大文件数,指定为1048576。
TasksMax=infinity:限制服务的最大任务数,infinity表示没有限制。
OOMScoreAdjust=-999:指定服务的OOM(Out of Memory)得分,负数表示降低被终止的概率。
[Install]

WantedBy=multi-user.target:指定服务的安装方式,multi-user.target表示该服务在多用户模式下安装。

18.3 配置 docker.service

下载地址:https://github.com/moby/moby/blob/master/contrib/init/systemd/docker.service

# 下载 docker.service
wget https://raw.githubusercontent.com/moby/moby/refs/tags/v28.4.0/contrib/init/systemd/docker.service


# 修改后写入到指定目录中
cat > /etc/systemd/system/docker.service <<EOF
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target nss-lookup.target docker.socket firewalld.service containerd.service time-set.target cri-docker.service
Wants=network-online.target containerd.service
Requires=docker.socket containerd.service
StartLimitBurst=3
StartLimitIntervalSec=60

[Service]
Type=notify
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=0
RestartSec=2
Restart=always
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process
OOMScoreAdjust=-500

[Install]
WantedBy=multi-user.target
EOF


# 将 docker.service 复制到其它服务器中
hots='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
for i in $hots; do 
  echo $i; 
  rsync --rsync-path="sudo rsync" /etc/systemd/system/docker.service $user@$i:/etc/systemd/system/; 
done

命令解析:

[Unit]

Description: 描述服务的作用,这里是Docker Application Container Engine,即Docker应用容器引擎。
Documentation: 提供关于此服务的文档链接,这里是Docker官方文档链接。
After: 说明该服务在哪些其他服务之后启动,这里是在网络在线、firewalld服务和containerd服务后启动。
Wants: 说明该服务想要的其他服务,这里是网络在线服务。
Requires: 说明该服务需要的其他服务,这里是docker.socket和containerd.service。
[Service]

Type: 服务类型,这里是notify,表示服务在启动完成时发送通知。
ExecStart: 命令,启动该服务时会执行的命令,这里是/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock,即启动dockerd并指定一些参数,其中-H指定dockerd的监听地址为fd://,--containerd指定containerd的sock文件位置。
ExecReload: 重载命令,当接收到HUP信号时执行的命令,这里是/bin/kill -s HUP $MAINPID,即发送HUP信号给主进程ID。
TimeoutSec: 服务超时时间,这里是0,表示没有超时限制。
RestartSec: 重启间隔时间,这里是2秒,表示重启失败后等待2秒再重启。
Restart: 重启策略,这里是always,表示总是重启。
StartLimitBurst: 启动限制次数,这里是3,表示在启动失败后最多重试3次。
StartLimitInterval: 启动限制时间间隔,这里是60秒,表示两次启动之间最少间隔60秒。
LimitNOFILE: 文件描述符限制,这里是infinity,表示没有限制。
LimitNPROC: 进程数限制,这里是infinity,表示没有限制。
LimitCORE: 核心转储限制,这里是infinity,表示没有限制。
TasksMax: 最大任务数,这里是infinity,表示没有限制。
Delegate: 修改权限,这里是yes,表示启用权限修改。
KillMode: 杀死模式,这里是process,表示杀死整个进程组。
OOMScoreAdjust: 用于调整进程在系统内存紧张时的优先级调整,这里是-500,表示将OOM分数降低500。
[Install]

WantedBy: 安装目标,这里是multi-user.target,表示在多用户模式下安装。
在WantedBy参数中,我们可以使用以下参数:
multi-user.target:指定该服务应该在多用户模式下启动。
graphical.target:指定该服务应该在图形化界面模式下启动。
default.target:指定该服务应该在系统的默认目标(runlevel)下启动。
rescue.target:指定该服务应该在系统救援模式下启动。
poweroff.target:指定该服务应该在关机时启动。
reboot.target:指定该服务应该在重启时启动。
halt.target:指定该服务应该在停止时启动。
shutdown.target:指定该服务应该在系统关闭时启动。
这些参数可以根据需要选择一个或多个,以告知系统在何时启动该服务。

18.4 准备 docker 的 socket 文件

下载地址:https://github.com/moby/moby/blob/v28.4.0/contrib/init/systemd/docker.socket

# 下载 docker.socket
wget https://raw.githubusercontent.com/moby/moby/refs/tags/v28.4.0/contrib/init/systemd/docker.socket


# 修改后写入到指定路径
cat > /etc/systemd/system/docker.socket <<EOF
[Unit]
Description=Docker Socket for the API

[Socket]
# If /var/run is not implemented as a symlink to /run, you may need to
# specify ListenStream=/var/run/docker.sock instead.
ListenStream=/var/run/docker.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker

[Install]
WantedBy=sockets.target
EOF


# 将 docker.socket 复制到其它服务器中
hots='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
for i in $hots; do 
  echo $i; 
  rsync --rsync-path="sudo rsync" /etc/systemd/system/docker.socket $user@$i:/etc/systemd/system/; 
done

命令解析:

这是一个用于Docker API的socket配置文件,包含了以下参数:

[Unit]

Description:描述了该socket的作用,即为Docker API的socket。
[Socket]

ListenStream:指定了 socket 的监听地址,该 socket 会监听在 /var/run/docker.sock 上,即 Docker 守护程序使用的默认 sock 文件。
SocketMode:指定了socket文件的权限模式,此处为0660,即用户和用户组有读写权限,其他用户无权限。
SocketUser:指定了socket文件的所有者,此处为root用户。
SocketGroup:指定了socket文件的所属用户组,此处为docker用户组。
[Install]

WantedBy:指定了该socket被启用时的目标,此处为sockets.target,表示当sockets.target启动时启用该socket。
该配置文件的作用是为 Docker 提供 API 访问的通道,它监听在 /var/run/docker.sock 上,具有 root 用户权限,但只接受 docker 用户组的成员的连接,并且其他用户无法访问。这样,只有 docker 用户组的成员可以通过该 socket 与 Docker 守护进程进行通信。

18.5 配置 Docker 加速器

# 创建 docker 配置目录 和 数据目录
mkdir /etc/docker/ /data/docker -pv

# 写入docker配置,这里也修改了docker默认的数据目录
cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "registry-mirrors": [
    "https://kfp63jaj.mirror.aliyuncs.com",
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com"
  ],
  "max-concurrent-downloads": 10,
  "log-driver": "json-file",
  "log-level": "warn",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
    },
  "data-root": "/data/docker"
}
EOF


# 配置设置每一台服务器都创建docker的配置目录和数据目录
hosts='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
for host in $hosts; do
  ssh root@$host "mkdir /etc/docker/ /data/docker -pv"
done


# 将 daemon.json 复制到其它服务器中
hots='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
for i in $hots; do 
  echo $i; 
  rsync --rsync-path="sudo rsync" /etc/docker/daemon.json $user@$i:/etc/docker/; 
done

命令解析:

该参数文件中包含以下参数:

exec-opts: 用于设置Docker守护进程的选项,native.cgroupdriver=systemd表示使用systemd作为Cgroup驱动程序。
registry-mirrors: 用于指定Docker镜像的镜像注册服务器。在这里有三个镜像注册服务器:https://docker.m.daocloud.io、https://docker.mirrors.ustc.edu.cn和http://hub-mirror.c.163.com。
max-concurrent-downloads: 用于设置同时下载镜像的最大数量,默认值为3,这里设置为10。
log-driver: 用于设置Docker守护进程的日志驱动程序,这里设置为json-file。
log-level: 用于设置日志的级别,这里设置为warn。
log-opts: 用于设置日志驱动程序的选项,这里有两个选项:max-size和max-file。max-size表示每个日志文件的最大大小,这里设置为10m,max-file表示保存的最大日志文件数量,这里设置为3。
data-root: 用于设置Docker守护进程的数据存储根目录,默认为/var/lib/docker,这里设置为 /data/docker。

18.6 启动 Docker

# 创建一个名为 docker 的组
groupadd docker
# 通知systemd重新加载所有单元配置文件(unit files),以识别新创建或修改的配置文件
systemctl daemon-reload
# 启用并立即启动Docker的socket单元(docker.socket),使其在系统启动时自动运行,并现在开始监听。
systemctl enable --now docker.socket
# 启用并立即启动Docker守护进程服务(docker.service),使其在系统启动时自动运行,并现在开始运行。
systemctl enable --now docker.service
# 验证:查看 docker 服务状态
systemctl status docker.service
# 验证:查看docker信息
docker info


# 集群中的其他服务器也启动 Docker 
hosts='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
for host in $hosts; do
  ssh root@$host "groupadd docker"
  ssh root@$host "systemctl daemon-reload"
  ssh root@$host "systemctl enable --now docker.socket"
  ssh root@$host "systemctl enable --now docker.service"
done

19. 安装 cri-docker

19.1 安装 cri-docker 二进制包

cri-docker 与 docker的版本匹配关系:https://github.com/Mirantis/cri-dockerd/releases/tag/v0.4.0

  • Bump github.com/docker/docker to 27.0.2+incompatible by @cncal in #381
# 下载 cri-docker 二进制包
wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.4.0/cri-dockerd-0.4.0.amd64.tgz
# 解压
tar xvf cri-dockerd-*.amd64.tgz 
# 复制到 /usr/bin/
cp cri-dockerd/cri-dockerd /usr/bin/
# 授权
chmod +x /usr/bin/cri-dockerd



# 将 cri-dockerd 复制到其它服务器中
hots='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
for i in $hots; do 
  echo $i; 
  rsync --rsync-path="sudo rsync" /usr/bin/cri-dockerd $user@$i:/usr/bin/; 
done


# 验证
$ cri-dockerd --version
cri-dockerd 0.4.0 (b9b8893)

19.2 配置 cri-docker.service

从 github 获取文件:https://github.com/Mirantis/cri-dockerd/tree/master/packaging/systemd

# 下载 cri-docker.service 文件
wget https://raw.githubusercontent.com/Mirantis/cri-dockerd/refs/tags/v0.4.0/packaging/systemd/cri-docker.service

# 修改 ExecStart 启动命令后,写入到指定目录
cat > /usr/lib/systemd/system/cri-docker.service <<EOF
[Unit]
Description=CRI Interface for Docker Application Container Engine
Documentation=https://docs.mirantis.com
After=network-online.target firewalld.service docker.service
Wants=network-online.target
Requires=cri-docker.socket

[Service]
Type=notify
# 修啊该启动命令
ExecStart=/usr/bin/cri-dockerd --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.9 --container-runtime-endpoint=unix:///var/run/cri-dockerd.sock --cri-dockerd-root-directory=/data/dockershim --cri-dockerd-root-directory=/data/docker

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
EOF


# 将 cri-docker.service 复制到其它服务器中
hots='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
for i in $hots; do 
  echo $i; 
  rsync --rsync-path="sudo rsync" /usr/lib/systemd/system/cri-docker.service $user@$i:/usr/lib/systemd/system/; 
done

命令解析:

[Unit]

Description:该参数用于描述该单元的功能,这里描述的是CRI与Docker应用容器引擎的接口。
Documentation:该参数指定了相关文档的网址,供用户参考。
After:该参数指定了此单元应该在哪些其他单元之后启动,确保在网络在线、防火墙和Docker服务启动之后再启动此单元。
Wants:该参数指定了此单元希望也启动的所有单元,此处是希望在网络在线之后启动。
Requires:该参数指定了此单元需要依赖的单元,此处是cri-docker.socket单元。
[Service]

Type:该参数指定了服务的类型,这里是notify,表示当服务启动完成时向系统发送通知。
ExecStart:该参数指定了将要运行的命令和参数,此处是执行/usr/bin/cri-dockerd 命令,并指定了网络插件为cni和Pod基础设施容器的镜像为registry.aliyuncs.com/google_containers/pause:3.9。
  - --container-runtime-endpoint=unix:///var/run/cri-dockerd.sock 
  	- 定义 CRI 的监听端点。
  - --cri-dockerd-root-directory=/data/dockershim
  	- 定义 cri-dockerd 的根目录,用于存储临时文件或配置数据。
  - --cri-dockerd-root-directory=/data/docker
  	- 定义 Docker 的根目录。
ExecReload:该参数指定在服务重载时运行的命令,此处是发送HUP信号给主进程。
TimeoutSec:该参数指定了服务启动的超时时间,此处为0,表示无限制。
RestartSec:该参数指定了自动重启服务的时间间隔,此处为2秒。
Restart:该参数指定了在服务发生错误时自动重启,此处是始终重启。
StartLimitBurst:该参数指定了在给定时间间隔内允许的启动失败次数,此处为3次。
StartLimitInterval:该参数指定启动失败的时间间隔,此处为60秒。
LimitNOFILE:该参数指定了允许打开文件的最大数量,此处为无限制。
LimitNPROC:该参数指定了允许同时运行的最大进程数,此处为无限制。
LimitCORE:该参数指定了允许生成的core文件的最大大小,此处为无限制。
TasksMax:该参数指定了此服务的最大任务数,此处为无限制。
Delegate:该参数指定了是否将控制权委托给指定服务,此处为是。
KillMode:该参数指定了在终止服务时如何处理进程,此处是通过终止进程来终止服务。
[Install]

WantedBy:该参数指定了希望这个单元启动的多用户目标。在这里,这个单元希望在multi-user.target启动。

19.3 配置 cri-docker.socket

从 github 获取文件:https://github.com/Mirantis/cri-dockerd/tree/master/packaging/systemd

# 下载
wget https://raw.githubusercontent.com/Mirantis/cri-dockerd/refs/tags/v0.4.0/packaging/systemd/cri-docker.socket


# 修改 ListenStream,并写入到指令目录
cat > /usr/lib/systemd/system/cri-docker.socket <<EOF
[Unit]
Description=CRI Docker Socket for the API
PartOf=cri-docker.service

[Socket]
ListenStream=/var/run/cri-dockerd.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker

[Install]
WantedBy=sockets.target
EOF


# 将 cri-docker.socket 复制到其它服务器中
hots='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
for i in $hots; do 
  echo $i; 
  rsync --rsync-path="sudo rsync" /usr/lib/systemd/system/cri-docker.socket $user@$i:/usr/lib/systemd/system/; 
done

命令解析:

该配置文件是用于systemd的单元配置文件(unit file),用于定义一个socket单元。

[Unit]

Description:表示该单元的描述信息。
PartOf:表示该单元是cri-docker.service的一部分。
[Socket]

ListenStream:指定了该socket要监听的地址和端口,这里要与 cri-docker.service 配置的 ExecStart 指定的套接字一致,都为 /var/run/cri-dockerd.sock。Unix域套接字用于在同一台主机上的进程之间通信。
SocketMode:指定了socket文件的权限模式,此处为0660,即用户和用户组有读写权限,其他用户无权限。
SocketUser:指定了socket文件的所有者,此处为root用户。
SocketGroup:指定了socket文件的所属用户组,此处为docker用户组。
[Install]

WantedBy:部分定义了该单元的安装配置信息。WantedBy=sockets.target表示当sockets.target单元启动时,自动启动该socket单元。sockets.target是一个系统服务,用于管理所有的socket单元。

19.4 启动 cri-docker

# 通知systemd重新加载所有单元配置文件(unit files),以识别新创建或修改的配置文件
systemctl daemon-reload
# 设置开机自启
systemctl enable --now cri-docker.service
# 验证,查看状态
systemctl status cri-docker.service


# 集群中的其他服务器也启动 Docker 
hosts='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
for host in $hosts; do
  ssh root@$host "systemctl daemon-reload"
  ssh root@$host "systemctl enable --now cri-docker.service"
done

四、K8S 与 ETCD 下载及安装(在 master01 节点上操作)

当前选择安装 K8S 版本:1.29.2,ETCD 版本:3.5.16

查看 K8S 与 ETCD 的版本匹配关系以及二进制包下载地址

https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.29.md

https://github.com/kubernetes/kubernetes/blob/release-1.29/cluster/images/etcd/Makefile

需要下载的镜像

https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.29.md#container-images-13

K8S 1.29.2 需要下载的镜像

1. 安装 K8S 二进制包

下载地址:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.29.md#v1292

# 下载 K8S 二进制包
wget https://dl.k8s.io/v1.29.2/kubernetes-server-linux-amd64.tar.gz

# 解压文件到指定目录
tar -xf kubernetes-server-linux-amd64.tar.gz  --strip-components=3 -C /usr/local/bin kubernetes/server/bin/kube{let,ctl,-apiserver,-controller-manager,-scheduler,-proxy}

# 查看解压的文件
$ ls /usr/local/bin/
kube-apiserver  kube-controller-manager  kube-proxy  kube-scheduler  kubectl  kubelet


# 将解压后的 K8S 二进制包复制到其它服务器中
# 拷贝 master 组件
hots='k8s-master02 k8s-master03'
user=root
for i in $hots; do 
  echo $i; 
  rsync --rsync-path="sudo rsync" /usr/local/bin/kube* $user@$i:/usr/local/bin/; 
done

# 拷贝 worker 组件
hots='k8s-node01 k8s-node02'
user=root
for i in $hots; do 
  echo $i; 
  rsync --rsync-path="sudo rsync" /usr/local/bin/kube{let,-proxy} $user@$i:/usr/local/bin/; 
done

# 验证
$ kubelet --version
Kubernetes v1.29.2

命令解析:

这是一个 tar 命令,用于解压指定的 kubernetes-server-linux-amd64.tar.gz 文件,并将其中的特定文件提取到 /usr/local/bin 目录下。

命令的解释如下:

tar:用于处理 tar 压缩文件的命令。
-xf:表示解压操作。
kubernetes-server-linux-amd64.tar.gz:要解压的文件名。
--strip-components=3:表示解压时忽略压缩文件中的前3级目录结构,提取文件时直接放到目标目录中。
-C /usr/local/bin:指定提取文件的目标目录为 /usr/local/bin。
kubernetes/server/bin/kube{let,ctl,-apiserver,-controller-manager,-scheduler,-proxy}:要解压和提取的文件名模式,用花括号括起来表示模式中的多个可能的文件名。
总的来说,这个命令的作用是将 kubernetes-server-linux-amd64.tar.gz 文件中的 kubelet、kubectl、kube-apiserver、kube-controller-manager、kube-scheduler和kube-proxy 六个文件提取到 /usr/local/bin 目录下,同时忽略文件路径中的前三级目录结构。

2. 安装 ETCD 二进制包

下载地址:https://github.com/etcd-io/etcd/releases/tag/v3.5.16

# 下载 ETCD 二进制安装包
wget https://github.com/etcd-io/etcd/releases/download/v3.5.16/etcd-v3.5.16-linux-amd64.tar.gz

# 解压etcd安装文件
tar -xf etcd*.tar.gz && mv etcd-*/etcd /usr/local/bin/ && mv etcd-*/etcdctl /usr/local/bin/


# 将解压后的 K8S 二进制包复制到其它服务器中
hots='k8s-master02 k8s-master03'
user=root
for i in $hots; do 
  echo $i; 
  rsync --rsync-path="sudo rsync" /usr/local/bin/etcd* $user@$i:/usr/local/bin/; 
done

# 验证:查看/usr/local/bin下内容
$ etcdctl version
etcdctl version: 3.5.16
API version: 3.5

3. 生成相关证书

3.1 安装证书工具

k8s-master01

# master01 节点下载证书生成工具
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssl_1.6.4_linux_amd64 -O /usr/local/bin/cfssl
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssljson_1.6.4_linux_amd64 -O /usr/local/bin/cfssljson

chmod +x /usr/local/bin/cfssl /usr/local/bin/cfssljson

3.2 生成 ETCD 证书

3.2.1 ca-config.json

k8s-master01

# 创建目录,存放生成的证书
mkdir /etc/etcd/ssl -p

# 写入生成证书所需的配置文件,master01 节点生成 etcd 证书
cat > /etc/etcd/ssl/ca-config.json << EOF 
{
  "signing": {
    "default": {
      "expiry": "876000h"
    },
    "profiles": {
      "kubernetes": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "876000h"
      }
    }
  }
}
EOF

命令解析:

ca-config.json文件是使用CFSSL(CloudFlare’s PKI/TLS toolkit)工具生成证书的核心配置文件。它主要用于定义证书签名的全局策略和配置文件(profiles),确保生成的证书具有一致的安全属性、用途和有效期。

在这里,有两个部分:signing 和 profiles。

signing 包含了默认签名配置和配置文件。
默认签名配置 default 指定了证书的过期时间为 876000h 。876000h 表示证书有效期为 100 年。

profiles 部分定义了不同的证书配置文件。
在这里,只有一个配置文件 kubernetes 。它包含了以下 usages 和过期时间 expiry:

signing:用于对其他证书进行签名
key encipherment:用于加密和解密传输数据
server auth:用于服务器身份验证
client auth:用于客户端身份验证
对于 kubernetes 配置文件,证书的过期时间也是 876000h,即100年。

3.2.2 etcd-ca-csr.json

k8s-master01

cat > /etc/etcd/ssl/etcd-ca-csr.json  << EOF 
{
  "CN": "etcd",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "etcd",
      "OU": "Etcd Security"
    }
  ],
  "ca": {
    "expiry": "876000h"
  }
}
EOF

命令解析:

etcd-ca-csr.json 是 Certificate Signing Request (CSR) 文件,用于生成 ETCD 的根 CA 证书(etcd-ca.pem 和 etcd-ca-key.pem)。根 CA 证书是 ETCD 集群的安全基石,用于签署 ETCD 节点证书(etcd.pem 和 etcd-key.pem)和其他相关证书,确保集群内部通信的加密和身份验证。

JSON 配置文件指定了生成证书签名请求所需的数据。

"CN": "etcd" 指定了希望生成的证书的 CN 字段(Common Name),即证书的主题,通常是该证书标识的实体的名称。
"key": {} 指定了生成证书所使用的密钥的配置信息。"algo": "rsa" 指定了密钥的算法为 RSA,"size": 2048 指定了密钥的长度为 2048 位。
"names": [] 包含了生成证书时所需的实体信息。在这个例子中,只包含了一个实体,其相关信息如下:
"C": "CN" 指定了实体的国家/地区代码,这里是中国。
"ST": "Beijing" 指定了实体所在的省/州。
"L": "Beijing" 指定了实体所在的城市。
"O": "etcd" 指定了实体的组织名称。
"OU": "Etcd Security" 指定了实体所属的组织单位。
"ca": {} 指定了生成证书时所需的CA(Certificate Authority)配置信息。
"expiry": "876000h" 指定了证书的有效期,这里是876000小时。
生成证书签名请求时,可以使用这个 JSON 配置文件作为输入,根据配置文件中的信息生成相应的 CSR 文件。然后,可以将 CSR 文件发送给 CA 进行签名,以获得有效的证书。

生成 etcd 证书和 etcd 证书的 key(如果你觉得以后可能会扩容,可以在 ip 那多写几个预留出来)
若没有IPv6 可删除可保留

3.2.3 生成 ETCD 的根 CA 证书

k8s-master01

# 生成 ETCD 的根 CA 证书
cfssl gencert -initca /etc/etcd/ssl/etcd-ca-csr.json | cfssljson -bare /etc/etcd/ssl/etcd-ca

# 查看生成的 ETCD 的根 CA 证书
$ ll /etc/etcd/ssl/
total 20
-rw-r--r--. 1 root root  294 Oct 11 19:03 ca-config.json
-rw-r--r--. 1 root root  249 Oct 11 19:20 etcd-ca-csr.json
-rw-------. 1 root root 1679 Oct 11 19:21 etcd-ca-key.pem
-rw-r--r--. 1 root root 1050 Oct 11 19:21 etcd-ca.csr
-rw-r--r--. 1 root root 1318 Oct 11 19:21 etcd-ca.pem
具体的解释如下:
cfssl 是一个用于生成 TLS/SSL 证书的工具,它支持 PKI、JSON 格式配置文件以及与许多其他集成工具的配合使用。

gencert 参数表示生成证书的操作。-initca 参数表示初始化一个CA(证书颁发机构)。CA 是用于签发其他证书的根证书。etcd-ca-csr.json 是一个 JSON 格式的配置文件,其中包含了CA的详细信息,如私钥、公钥、有效期等。这个文件提供了生成 CA 证书所需的信息。

| 符号表示将上一个命令的输出作为下一个命令的输入。

cfssljson 是 cfssl 工具的一个子命令,用于格式化 cfssl 生成的 JSON 数据。 -bare 参数表示直接输出裸证书,即只生成证书文件,不包含其他格式的文件。/etc/etcd/ssl/etcd-ca 是指定生成的证书文件的路径和名称。

所以,这条命令的含义是使用 cfssl 工具根据配置文件 etcd-ca-csr.json 生成一个 CA 证书,并将证书文件保存在 /etc/etcd/ssl/etcd-ca 路径下。

3.2.4 etcd-csr.json

k8s-master01

cat > /etc/etcd/ssl/etcd-csr.json << EOF 
{
  "CN": "etcd",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "etcd",
      "OU": "Etcd Security"
    }
  ]
}
EOF

命令解析:

etcd-csr.json 用于生成 ETCD 节点的 TLS 证书(etcd.pem 和 etcd-key.pem),这些证书由 ETCD 的根 CA(通过 etcd-ca-csr.json 生成的 etcd-ca.pem)签署。

这段代码是一个 JSON 格式的配置文件,用于生成一个证书签名请求(Certificate Signing Request,CSR)。

首先,"CN"字段指定了该证书的通用名称(Common Name),这里设为"etcd"。

接下来,"key"字段指定了密钥的算法("algo"字段)和长度("size"字段),此处使用的是RSA算法,密钥长度为2048位。

最后,"names"字段是一个数组,其中包含了一个名字对象,用于指定证书中的一些其他信息。这个名字对象包含了以下字段:

"C"字段指定了国家代码(Country),这里设置为"CN"。
"ST"字段指定了省份(State)或地区,这里设置为"Beijing"。
"L"字段指定了城市(Locality),这里设置为"Beijing"。
"O"字段指定了组织(Organization),这里设置为"etcd"。
"OU"字段指定了组织单元(Organizational Unit),这里设置为"Etcd Security"。
这些字段将作为证书的一部分,用于标识和验证证书的使用范围和颁发者等信息。

3.2.5 生成 ETCD 节点证书

k8s-master01

# 生成 ETCD 节点证书
cfssl gencert \
-ca=/etc/etcd/ssl/etcd-ca.pem \
-ca-key=/etc/etcd/ssl/etcd-ca-key.pem \
-config=/etc/etcd/ssl/ca-config.json \
-hostname=127.0.0.1,k8s-master01,k8s-master02,k8s-master03,10.20.1.101,10.20.1.102,10.20.1.103,2400:3200::101,2400:3200::102,2400:3200::103,::1 \
-profile=kubernetes \
/etc/etcd/ssl/etcd-csr.json | cfssljson -bare /etc/etcd/ssl/etcd

命令解析:

这是一条使用cfssl生成etcd节点证书的命令,下面是各个参数的解释:

-ca=/etc/etcd/ssl/etcd-ca.pem:指定用于签名etcd证书的CA文件的路径。
-ca-key=/etc/etcd/ssl/etcd-ca-key.pem:指定用于签名etcd证书的CA私钥文件的路径。
-config=ca-config.json:指定CA配置文件的路径,该文件定义了证书的有效期、加密算法等设置。
-hostname=xxxx:指定要为etcd生成证书的主机名和IP地址列表。
-profile=kubernetes:指定使用的证书配置文件,该文件定义了证书的用途和扩展属性。
etcd-csr.json:指定etcd证书请求的JSON文件的路径,该文件包含了证书请求的详细信息。
| cfssljson -bare /etc/etcd/ssl/etcd:通过管道将cfssl命令的输出传递给cfssljson命令,并使用-bare参数指定输出文件的前缀路径,这里将生成etcd证书的.pem和-key.pem文件。

这条命令的作用是使用指定的CA证书和私钥,根据证书请求的JSON文件和配置文件生成etcd的证书文件。

3.2.6 将证书复制到其他节点

k8s-master01

# 将生成的 ETCD 证书复制到其他节点
hosts='k8s-master02 k8s-master03'
for host in $hosts; do
  echo $host; 
  ssh root@$host "mkdir /etc/etcd/ssl -p"
  rsync --rsync-path="sudo rsync" /etc/etcd/ssl/* $user@$host:/etc/etcd/ssl/; 
done

3.3 生成 K8S 相关证书

3.3.1 ca-config.json

k8s-master01

# 创建目录,用于存放 K8S 相关证书
mkdir -p /etc/kubernetes/pki

cat > /etc/kubernetes/pki/ca-config.json << EOF 
{
  "signing": {
    "default": {
      "expiry": "876000h"
    },
    "profiles": {
      "kubernetes": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "876000h"
      }
    }
  }
}
EOF

命令解析:

ca-config.json文件是使用CFSSL(CloudFlare’s PKI/TLS toolkit)工具生成证书的核心配置文件。它主要用于定义证书签名的全局策略和配置文件(profiles),确保生成的证书具有一致的安全属性、用途和有效期。

在这里,有两个部分:signing 和 profiles。

signing 包含了默认签名配置和配置文件。
默认签名配置 default 指定了证书的过期时间为 876000h 。876000h 表示证书有效期为 100 年。

profiles 部分定义了不同的证书配置文件。
在这里,只有一个配置文件 kubernetes 。它包含了以下 usages 和过期时间 expiry:

signing:用于对其他证书进行签名
key encipherment:用于加密和解密传输数据
server auth:用于服务器身份验证
client auth:用于客户端身份验证
对于 kubernetes 配置文件,证书的过期时间也是 876000h,即100年。

3.3.2 ca-csr.json

cat > /etc/kubernetes/pki/ca-csr.json << EOF 
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Guangdong",
      "L": "Guangzhou",
      "O": "Kubernetes",
      "OU": "System"
    }
  ],
  "ca": {
    "expiry": "876000h"
  }
}
EOF

命令解析:

ca-csr.json 用于生成 Kubernetes 集群的根 CA 证书(ca.pem 和 ca-key.pem)。根 CA 证书是 Kubernetes 安全体系的核心,用于签署集群内所有组件的证书(如 API Server、kubelet、admin 用户等),支持 TLS 加密和 Mutual TLS 认证。

这是一个用于生成 Kubernetes 相关证书的配置文件。该配置文件中包含以下信息:

CN:CommonName,即用于标识证书的通用名称。在此配置中,CN 设置为 "kubernetes",表示该证书是用于 Kubernetes。
key:用于生成证书的算法和大小。在此配置中,使用的算法是 RSA,大小是 2048 位。
names:用于证书中的名称字段的详细信息。在此配置中,有以下字段信息:
	C:Country,即国家。在此配置中,设置为 "CN"。
	ST:State,即省/州。在此配置中,设置为 "Beijing"。
	L:Locality,即城市。在此配置中,设置为 "Beijing"。
	O:Organization,即组织。在此配置中,设置为 "Kubernetes"。
	OU:Organization Unit,即组织单位。在此配置中,设置为 "Kubernetes-manual"。
ca:用于证书签名的证书颁发机构(CA)的配置信息。在此配置中,设置了证书的有效期为 876000 小时。
这个配置文件可以用于生成 Kubernetes 相关的证书,以确保集群中的通信安全性。

3.3.3 生成 Kubernetes 集群的根 CA 证书

cfssl gencert -initca /etc/kubernetes/pki/ca-csr.json | cfssljson -bare /etc/kubernetes/pki/ca

命令解析:

具体的解释如下:

cfssl是一个用于生成TLS/SSL证书的工具,它支持PKI、JSON格式配置文件以及与许多其他集成工具的配合使用。

gencert参数表示生成证书的操作。-initca参数表示初始化一个CA(证书颁发机构)。CA是用于签发其他证书的根证书。ca-csr.json是一个JSON格式的配置文件,其中包含了CA的详细信息,如私钥、公钥、有效期等。这个文件提供了生成CA证书所需的信息。

| 符号表示将上一个命令的输出作为下一个命令的输入。

cfssljson是cfssl工具的一个子命令,用于格式化cfssl生成的JSON数据。 -bare参数表示直接输出裸证书,即只生成证书文件,不包含其他格式的文件。/etc/kubernetes/pki/ca是指定生成的证书文件的路径和名称。

所以,这条命令的含义是使用cfssl工具根据配置文件ca-csr.json生成一个CA证书,并将证书文件保存在/etc/kubernetes/pki/ca路径下。

3.3.4 apiserver-csr.json

cat > /etc/kubernetes/pki/apiserver-csr.json << EOF 
{
  "CN": "kube-apiserver",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "Kubernetes",
      "OU": "Kubernetes-manual"
    }
  ]
}
EOF

命令解析:

apiserver-csr.json 用于生成 Kubernetes API Server 的 TLS 证书(apiserver.pem 和 apiserver-key.pem),这些证书由 Kubernetes 的根 CA(通过 ca-csr.json 生成的 ca.pem)签署。

证书用于:

  • 服务器端认证:确保 API Server 的身份可信,客户端(如 kubectl、kubelet)可以通过 CA 证书(ca.pem)验证 API Server。
  • 客户端认证:支持 Mutual TLS,API Server 验证客户端证书(如 admin、kubelet)的合法性。
  • 加密通信:通过 TLS 加密 API Server 与客户端之间的通信,防止数据泄露或篡改。

3.3.5 生成 apiserver 证书

cfssl gencert \
-ca=/etc/kubernetes/pki/ca.pem \
-ca-key=/etc/kubernetes/pki/ca-key.pem \
-config=/etc/kubernetes/pki/ca-config.json \
-hostname=10.96.0.1,127.0.0.1,kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster,kubernetes.default.svc.cluster.local,10.20.1.101,10.20.1.102,10.20.1.103,10.20.1.104,10.20.1.105,10.20.1.106,10.20.1.107,10.20.1.108,10.20.1.109,2400:3200::101,2400:3200::102,2400:3200::103,2400:3200::104,2400:3200::105,2400:3200::106,2400:3200::107,2400:3200::108,2400:3200::109 \
-profile=kubernetes \
/etc/kubernetes/pki/apiserver-csr.json | cfssljson -bare /etc/kubernetes/pki/apiserver

3.3.6 apiserver 聚合证书

访问 kube-apiserver 的另一种方式就是使用 kube-proxy 来代理访问, 而该证书就是用来支持 SSL 代理访问的。在该种访问模式下,我们是以http的方式发起请求到代理服务的, 此时, 代理服务会将该请求发送给 kube-apiserver , 在此之前, 代理会将发送给 kube-apiserver 的请求头里加入证书信息。

客户端 -- 发起请求 ---> 代理 -- Add Header信息:发起请求 --> kube-apiserve

如果apiserver所在的主机上没有运行kube-proxy,既无法通过服务的ClusterIP进行访问,需要 --enable-aggregator-routing=true

3.3.6.1 front-proxy-ca-csr.json
cat > /etc/kubernetes/pki/front-proxy-ca-csr.json  << EOF 
{
  "CN": "kubernetes",
  "key": {
     "algo": "rsa",
     "size": 2048
  },
  "ca": {
    "expiry": "876000h"
  }
}
EOF

命令解析:

front-proxy-ca-csr.json 用于生成 Kubernetes 的 Front Proxy CA 证书(front-proxy-ca.pem 和 front-proxy-ca-key.pem)。Front Proxy CA 是 Kubernetes 集群中用于 前端代理认证 的独立 CA,专门用于签署前端代理客户端证书(如 front-proxy-client.pem),以支持 API Server 的请求头认证机制。

3.3.6.2 生成 Kubernetes Front Proxy 根 CA 证书

包括证书文件(front-proxy-ca.pem)、私钥文件(front-proxy-ca-key.pem)和 CSR 文件(front-proxy-ca.csr)

# 指示 CFSSL 生成根 CA 证书(而非普通证书)
cfssl gencert -initca /etc/kubernetes/pki/front-proxy-ca-csr.json | cfssljson -bare /etc/kubernetes/pki/front-proxy-ca

# 生成 前端代理客户端证书 的 CSR 配置文件
cat > /etc/kubernetes/pki/front-proxy-client-csr.json << EOF 
{
  "CN": "front-proxy-client",
  "key": {
     "algo": "rsa",
     "size": 2048
  }
}
EOF

# 生成前端代理客户端证书
cfssl gencert  \
-ca=/etc/kubernetes/pki/front-proxy-ca.pem   \
-ca-key=/etc/kubernetes/pki/front-proxy-ca-key.pem   \
-config=/etc/kubernetes/pki/ca-config.json   \
-profile=kubernetes /etc/kubernetes/pki/front-proxy-client-csr.json | cfssljson -bare /etc/kubernetes/pki/front-proxy-client

命令解析1:

cfssl gencert -initca front-proxy-ca-csr.json | cfssljson -bare /etc/kubernetes/pki/front-proxy-ca
  • 这个命令使用 CFSSL 工具生成 Kubernetes Front Proxy CA 证书(根 CA 证书),包括证书文件(front-proxy-ca.pem)、私钥文件(front-proxy-ca-key.pem)和 CSR 文件(front-proxy-ca.csr)。

  • Front Proxy CA 的背景:在 Kubernetes 高可用集群中,Front Proxy CA 是用于 请求头认证(RequestHeader Authentication) 的独立 CA。它专门用于签署前端代理客户端证书(如 front-proxy-client.pem),以支持 API Server 的聚合层(Aggregation Layer)。这对于扩展组件(如 Metrics Server)至关重要,确保客户端(如 Metrics Server)可以通过 HTTP 请求头提供身份信息,并由 API Server 验证。

  • 上下文:博客中,此证书用于 Metrics Server 的部署(components.yaml 中引用 –requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem),确保高可用集群(3 Master 节点 + 2 Node 节点)的扩展 API 安全。

命令解析2:

cat > front-proxy-client-csr.json << EOF … EOF

生成 前端代理客户端证书 的 CSR 配置文件

  • 前端代理客户端证书的作用:用于 Metrics Server 或其他聚合 API 客户端的身份认证。客户端证书(front-proxy-client.pem 和 front-proxy-client-key.pem)由 Front Proxy CA 签署,API Server 使用它验证请求头中的客户端身份,确保只有可信客户端可以访问聚合 API

  • 为什么需要?Kubernetes 的聚合层要求客户端提供证书以通过 RequestHeader 认证。Metrics Server 配置中引用了这些证书,确保安全访问 API Server。

  • 上下文:博客中,此证书用于 Metrics Server 的 TLS 通信,支持高可用集群的扩展功能。

命令解析3:

cfssl gencert
-ca=/etc/kubernetes/pki/front-proxy-ca.pem
-ca-key=/etc/kubernetes/pki/front-proxy-ca-key.pem
-config=/etc/kubernetes/pki/ca-config.json
-profile=kubernetes /etc/kubernetes/pki/front-proxy-client-csr.json | cfssljson -bare /etc/kubernetes/pki/front-proxy-client

  • 使用 CFSSL 生成 前端代理客户端证书(front-proxy-client.pem 和 front-proxy-client-key.pem),由 Front Proxy CA 签署。
  • 客户端证书的作用:Metrics Server 等聚合客户端使用此证书在请求头中证明身份,API Server 通过 Front Proxy CA 验证,确保安全访问聚合 API(如 /apis/metrics.k8s.io/)
  • Metrics Server 的配置(–requestheader-username-headers 等)依赖此证书,支持 RequestHeader 认证。
  • 在高可用集群中,此证书分发到所有 Master 节点,供 kube-apiserver 使用

3.3.7 生成 controller-manage 的证书

# 生成 Kubernetes Controller Manager 客户端证书 的 Certificate Signing Request (CSR) 配置文件
cat > /etc/kubernetes/pki/manager-csr.json << EOF 
{
  "CN": "system:kube-controller-manager",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "system:kube-controller-manager",
      "OU": "Kubernetes-manual"
    }
  ]
}
EOF


# 使用 CFSSL 生成 Controller Manager 客户端证书(controller-manager.pem 和 controller-manager-key.pem),由 Kubernetes 根 CA(ca.pem)签署
cfssl gencert \
   -ca=/etc/kubernetes/pki/ca.pem \
   -ca-key=/etc/kubernetes/pki/ca-key.pem \
   -config=/etc/kubernetes/pki/ca-config.json \
   -profile=kubernetes \
   /etc/kubernetes/pki/manager-csr.json | cfssljson -bare /etc/kubernetes/pki/controller-manager
   

# 配置 Controller Manager 的 kubeconfig 文件(/etc/kubernetes/controller-manager.kubeconfig),定义集群信息,指定 API Server 的地址和 CA 证书。
kubectl config set-cluster kubernetes \
     --certificate-authority=/etc/kubernetes/pki/ca.pem \
     --embed-certs=true \
     --server=https://127.0.0.1:8443 \
     --kubeconfig=/etc/kubernetes/controller-manager.kubeconfig

# 设置 kubeconfig 的上下文,将集群、用户和命名空间绑定,定义 Controller Manager 的访问环境
kubectl config set-context system:kube-controller-manager@kubernetes \
    --cluster=kubernetes \
    --user=system:kube-controller-manager \
    --kubeconfig=/etc/kubernetes/controller-manager.kubeconfig

# 设置 Controller Manager 的用户凭据,指定其客户端证书和私钥,用于与 API Server 的身份认证
kubectl config set-credentials system:kube-controller-manager \
    --client-certificate=/etc/kubernetes/pki/controller-manager.pem \
    --client-key=/etc/kubernetes/pki/controller-manager-key.pem \
    --embed-certs=true \
    --kubeconfig=/etc/kubernetes/controller-manager.kubeconfig

# 设置 kubeconfig 文件的默认上下文,确保 Controller Manager 使用 system:kube-controller-manager@kubernetes 上下文访问 API Server
kubectl config use-context system:kube-controller-manager@kubernetes \
     --kubeconfig=/etc/kubernetes/controller-manager.kubeconfig

3.3.8 生成 kube-scheduler 的证书

# 生成 Kubernetes Scheduler 客户端证书 的 Certificate Signing Request (CSR) 配置文件
cat > /etc/kubernetes/pki/scheduler-csr.json << EOF 
{
  "CN": "system:kube-scheduler",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "system:kube-scheduler",
      "OU": "Kubernetes-manual"
    }
  ]
}
EOF


# 使用 CFSSL 生成 Scheduler 客户端证书(scheduler.pem 和 scheduler-key.pem),由 Kubernetes 根 CA(ca.pem)签署
cfssl gencert \
   -ca=/etc/kubernetes/pki/ca.pem \
   -ca-key=/etc/kubernetes/pki/ca-key.pem \
   -config=/etc/kubernetes/pki/ca-config.json \
   -profile=kubernetes \
   /etc/kubernetes/pki/scheduler-csr.json | cfssljson -bare /etc/kubernetes/pki/scheduler


# 配置 Scheduler 的 kubeconfig 文件(/etc/kubernetes/scheduler.kubeconfig),定义集群信息,指定 API Server 的地址和 CA 证书。
kubectl config set-cluster kubernetes \
     --certificate-authority=/etc/kubernetes/pki/ca.pem \
     --embed-certs=true \
     --server=https://127.0.0.1:8443 \
     --kubeconfig=/etc/kubernetes/scheduler.kubeconfig


# 设置 Scheduler 的用户凭据,指定其客户端证书和私钥,用于与 API Server 的身份认证。
kubectl config set-credentials system:kube-scheduler \
     --client-certificate=/etc/kubernetes/pki/scheduler.pem \
     --client-key=/etc/kubernetes/pki/scheduler-key.pem \
     --embed-certs=true \
     --kubeconfig=/etc/kubernetes/scheduler.kubeconfig


# 设置 kubeconfig 的上下文,将集群、用户和命名空间绑定,定义 Scheduler 的访问环境。
kubectl config set-context system:kube-scheduler@kubernetes \
     --cluster=kubernetes \
     --user=system:kube-scheduler \
     --kubeconfig=/etc/kubernetes/scheduler.kubeconfig


# 设置 kubeconfig 文件的默认上下文,确保 Scheduler 使用 system:kube-scheduler@kubernetes 上下文访问 API Server
kubectl config use-context system:kube-scheduler@kubernetes \
     --kubeconfig=/etc/kubernetes/scheduler.kubeconfig

3.3.9 生成 admin 的证书配置

# 生成一个 证书签名请求(Certificate Signing Request) 的配置文件,供 cfssl 工具使用
cat > /etc/kubernetes/pki/admin-csr.json << EOF 
{
  "CN": "admin",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "system:masters",
      "OU": "Kubernetes-manual"
    }
  ]
}
EOF


# 生成了管理员证书和密钥,用于 kubectl 访问 API Server 时进行身份认证
cfssl gencert \
   -ca=/etc/kubernetes/pki/ca.pem \
   -ca-key=/etc/kubernetes/pki/ca-key.pem \
   -config=/etc/kubernetes/pki/ca-config.json \
   -profile=kubernetes \
   /etc/kubernetes/pki/admin-csr.json | cfssljson -bare /etc/kubernetes/pki/admin


# 设置集群信息
kubectl config set-cluster kubernetes     \
  --certificate-authority=/etc/kubernetes/pki/ca.pem     \
  --embed-certs=true     \
  --server=https://127.0.0.1:8443     \
  --kubeconfig=/etc/kubernetes/admin.kubeconfig


# 设置用户凭证
kubectl config set-credentials kubernetes-admin  \
  --client-certificate=/etc/kubernetes/pki/admin.pem     \
  --client-key=/etc/kubernetes/pki/admin-key.pem     \
  --embed-certs=true     \
  --kubeconfig=/etc/kubernetes/admin.kubeconfig


# 绑定上下文
kubectl config set-context kubernetes-admin@kubernetes    \
  --cluster=kubernetes     \
  --user=kubernetes-admin     \
  --kubeconfig=/etc/kubernetes/admin.kubeconfig


# 启用当前上下文
kubectl config use-context kubernetes-admin@kubernetes  --kubeconfig=/etc/kubernetes/admin.kubeconfig

3.3.10 创建 kube-proxy 证书

# 生成 kube-proxy 证书签名请求文件 (CSR),这个文件会被用来生成证书请求
cat > /etc/kubernetes/pki/kube-proxy-csr.json  << EOF 
{
  "CN": "system:kube-proxy",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "system:kube-proxy",
      "OU": "Kubernetes-manual"
    }
  ]
}
EOF


# 使用前面创建好的集群 CA 根证书来签发 kube-proxy 的客户端证书
cfssl gencert \
   -ca=/etc/kubernetes/pki/ca.pem \
   -ca-key=/etc/kubernetes/pki/ca-key.pem \
   -config=/etc/kubernetes/pki/ca-config.json \
   -profile=kubernetes \
   /etc/kubernetes/pki/kube-proxy-csr.json | cfssljson -bare /etc/kubernetes/pki/kube-proxy


# 生成 kube-proxy 的 kubeconfig 文件, kube-proxy 在启动时需要使用一个 kubeconfig 文件来连接 API Server
# 设置集群信息
kubectl config set-cluster kubernetes     \
  --certificate-authority=/etc/kubernetes/pki/ca.pem     \
  --embed-certs=true     \
  --server=https://127.0.0.1:8443     \
  --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig


# 设置用户凭证
kubectl config set-credentials kube-proxy  \
  --client-certificate=/etc/kubernetes/pki/kube-proxy.pem     \
  --client-key=/etc/kubernetes/pki/kube-proxy-key.pem     \
  --embed-certs=true     \
  --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig


# 绑定上下文(Context)
kubectl config set-context kube-proxy@kubernetes    \
  --cluster=kubernetes     \
  --user=kube-proxy     \
  --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig


# 启用当前上下文
kubectl config use-context kube-proxy@kubernetes  --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig

3.3.11 生成 ServiceAccount(简称 SA)签名密钥对

# 生成文件 /etc/kubernetes/pki/sa.key —— ServiceAccount 私钥文件
openssl genrsa -out /etc/kubernetes/pki/sa.key 2048

# 生成文件 /etc/kubernetes/pki/sa.pub —— ServiceAccount 公钥文件
openssl rsa -in /etc/kubernetes/pki/sa.key -pubout -out /etc/kubernetes/pki/sa.pub

4. 分发证书到其它节点

4.1 分发到 master 节点

k8s-master01

# 将生成的 ETCD 证书复制到其他节点
hosts='k8s-master02 k8s-master03'
user=root
for host in $hosts; do
  echo $host; 
  ssh root@$host "mkdir -p /etc/kubernetes/pki/"
  rsync --rsync-path="sudo rsync" /etc/kubernetes/pki/* $user@$host:/etc/kubernetes/pki/;
  rsync --rsync-path="sudo rsync" /etc/kubernetes/admin.kubeconfig $user@$host:/etc/kubernetes/
  rsync --rsync-path="sudo rsync" /etc/kubernetes/controller-manager.kubeconfig $user@$host:/etc/kubernetes/
  rsync --rsync-path="sudo rsync" /etc/kubernetes/scheduler.kubeconfig $user@$host:/etc/kubernetes/
  rsync --rsync-path="sudo rsync" /etc/kubernetes/kube-proxy.kubeconfig $user@$host:/etc/kubernetes/
done

4.2 分发到 node 节点

k8s-master01

hosts='k8s-node01 k8s-node02'
user=root
for host in $hosts; do
  echo $host; 
  ssh root@$host "mkdir -p /etc/kubernetes/pki/"
  rsync --rsync-path="sudo rsync" /etc/kubernetes/pki/kube-proxy* $user@$host:/etc/kubernetes/pki/;
  rsync --rsync-path="sudo rsync" /etc/kubernetes/kube-proxy.kubeconfig $user@$host:/etc/kubernetes/
done

5. 查看证书

5.1 master 节点

$ ll /etc/kubernetes/pki/
total 140
-rw-r--r--. 1 root root  225 Oct 16 23:22 admin-csr.json
-rw-------. 1 root root 1679 Oct 16 23:25 admin-key.pem
-rw-r--r--. 1 root root 1025 Oct 16 23:25 admin.csr
-rw-r--r--. 1 root root 1432 Oct 16 23:25 admin.pem
-rw-r--r--. 1 root root  230 Oct 11 20:11 apiserver-csr.json
-rw-------. 1 root root 1675 Oct 11 20:17 apiserver-key.pem
-rw-r--r--. 1 root root 1565 Oct 11 20:17 apiserver.csr
-rw-r--r--. 1 root root 1948 Oct 11 20:17 apiserver.pem
-rw-r--r--. 1 root root  294 Oct 11 19:58 ca-config.json
-rw-r--r--. 1 root root  258 Oct 11 20:04 ca-csr.json
-rw-------. 1 root root 1679 Oct 11 20:04 ca-key.pem
-rw-r--r--. 1 root root 1062 Oct 11 20:04 ca.csr
-rw-r--r--. 1 root root 1342 Oct 11 20:04 ca.pem
-rw-------. 1 root root 1675 Oct 16 22:19 controller-manager-key.pem
-rw-r--r--. 1 root root 1082 Oct 16 22:19 controller-manager.csr
-rw-r--r--. 1 root root 1489 Oct 16 22:19 controller-manager.pem
-rw-r--r--. 1 root root  118 Oct 11 20:23 front-proxy-ca-csr.json
-rw-------. 1 root root 1679 Oct 16 21:19 front-proxy-ca-key.pem
-rw-r--r--. 1 root root  940 Oct 16 21:19 front-proxy-ca.csr
-rw-r--r--. 1 root root 1094 Oct 16 21:19 front-proxy-ca.pem
-rw-r--r--. 1 root root   87 Oct 16 21:22 front-proxy-client-csr.json
-rw-------. 1 root root 1679 Oct 16 21:25 front-proxy-client-key.pem
-rw-r--r--. 1 root root  903 Oct 16 21:25 front-proxy-client.csr
-rw-r--r--. 1 root root 1188 Oct 16 21:25 front-proxy-client.pem
-rw-r--r--. 1 root root  240 Oct 16 23:31 kube-proxy-csr.json
-rw-------. 1 root root 1675 Oct 16 23:31 kube-proxy-key.pem
-rw-r--r--. 1 root root 1045 Oct 16 23:31 kube-proxy.csr
-rw-r--r--. 1 root root 1456 Oct 16 23:31 kube-proxy.pem
-rw-r--r--. 1 root root  266 Oct 16 22:11 manager-csr.json
-rw-------. 1 root root 1704 Oct 16 23:34 sa.key
-rw-r--r--. 1 root root  451 Oct 16 23:34 sa.pub
-rw-r--r--. 1 root root  248 Oct 16 23:07 scheduler-csr.json
-rw-------. 1 root root 1679 Oct 16 23:08 scheduler-key.pem
-rw-r--r--. 1 root root 1058 Oct 16 23:08 scheduler.csr
-rw-r--r--. 1 root root 1464 Oct 16 23:08 scheduler.pem


$ ll /etc/kubernetes/
total 36
-rw-------. 1 root root 6341 Oct 16 23:26 admin.kubeconfig
-rw-------. 1 root root 6469 Oct 16 22:30 controller-manager.kubeconfig
-rw-------. 1 root root 6345 Oct 16 23:32 kube-proxy.kubeconfig
drwxr-xr-x. 2 root root 4096 Oct 16 23:34 pki
-rw-------. 1 root root 6401 Oct 16 23:14 scheduler.kubeconfig

5.2 node 节点

$ ll /etc/kubernetes/pki/
total 16
-rw-r--r--. 1 root root  240 Oct 16 23:52 kube-proxy-csr.json
-rw-------. 1 root root 1675 Oct 16 23:52 kube-proxy-key.pem
-rw-r--r--. 1 root root 1045 Oct 16 23:52 kube-proxy.csr
-rw-r--r--. 1 root root 1456 Oct 16 23:52 kube-proxy.pem


$ ll /etc/kubernetes/
total 8
-rw-------. 1 root root 6345 Oct 16 23:52 kube-proxy.kubeconfig
drwxr-xr-x. 2 root root  103 Oct 16 23:52 pki

五、K8S 系统组件配置

1. ETCD 配置

etcd 配置大致相同,注意修改每个 Master 节点的 etcd 配置的主机名和IP地址

官方文档:https://etcd.io/docs/v3.5/op-guide/configuration/

从 Github 获取配置文件示例 :https://github.com/etcd-io/etcd/blob/main/etcd.conf.yml.sample

配置项解读:

  • name:指定了当前节点的名称,用于集群中区分不同的节点。
  • data-dir:指定了 etcd 数据的存储目录。
  • wal-dir:指定了 etcd 数据写入磁盘的目录。
  • snapshot-count:指定了触发快照的事务数量。
  • heartbeat-interval:指定了 etcd 集群中节点之间的心跳间隔。
  • election-timeout:指定了选举超时时间。
  • quota-backend-bytes:指定了存储的限额,0 表示无限制。
  • listen-peer-urls:指定了节点之间通信的 URL,使用 HTTPS 协议。
  • listen-client-urls:指定了客户端访问 etcd 集群的 URL,同时提供了本地访问的 URL。
  • max-snapshots:指定了快照保留的数量。
  • max-wals:指定了日志保留的数量。
  • initial-advertise-peer-urls:指定了节点之间通信的初始 URL。
  • advertise-client-urls:指定了客户端访问 etcd 集群的初始 URL。
  • discovery:定义了 etcd 集群发现相关的选项。
  • initial-cluster:指定了 etcd 集群的初始成员。
  • initial-cluster-token:指定了集群的 token。
  • initial-cluster-state:指定了集群的初始状态。
  • strict-reconfig-check:指定了严格的重新配置检查选项。
  • enable-v2:启用了 v2 API。
  • enable-pprof:启用了性能分析。
  • proxy:设置了代理模式。
  • client-transport-security:客户端的传输安全配置。
  • peer-transport-security:节点之间的传输安全配置。
  • debug:是否启用调试模式。
  • log-package-levels:日志的输出级别。
  • log-outputs:指定了日志的输出类型。
  • force-new-cluster:是否强制创建一个新的集群。

1.1 Master01 节点

如果要用 IPv6 那么把 IPv4 地址修改为 IPv6 即可

cat > /etc/etcd/etcd.config.yml << EOF 
name: 'k8s-master01'
data-dir: /data/etcd
wal-dir: /data/etcd/wal
snapshot-count: 5000
heartbeat-interval: 100
election-timeout: 1000
quota-backend-bytes: 0
listen-peer-urls: 'https://10.20.1.101:2380'
listen-client-urls: 'https://10.20.1.101:2379,http://127.0.0.1:2379'
max-snapshots: 3
max-wals: 5
cors:
initial-advertise-peer-urls: 'https://10.20.1.101:2380'
advertise-client-urls: 'https://10.20.1.101:2379'
discovery:
discovery-fallback: 'proxy'
discovery-proxy:
discovery-srv:
initial-cluster: 'k8s-master01=https://10.20.1.101:2380,k8s-master02=https://10.20.1.102:2380,k8s-master03=https://10.20.1.103:2380'
initial-cluster-token: 'etcd-k8s-cluster'
initial-cluster-state: 'new'
strict-reconfig-check: false
enable-v2: true
enable-pprof: true
proxy: 'off'
proxy-failure-wait: 5000
proxy-refresh-interval: 30000
proxy-dial-timeout: 1000
proxy-write-timeout: 5000
proxy-read-timeout: 0
client-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
peer-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  peer-client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
debug: false
log-package-levels:
log-outputs: [default]
force-new-cluster: false
EOF

1.2 Master02 节点

cat > /etc/etcd/etcd.config.yml << EOF 
name: 'k8s-master02'
data-dir: /data/etcd
wal-dir: /data/etcd/wal
snapshot-count: 5000
heartbeat-interval: 100
election-timeout: 1000
quota-backend-bytes: 0
listen-peer-urls: 'https://10.20.1.102:2380'
listen-client-urls: 'https://10.20.1.102:2379,http://127.0.0.1:2379'
max-snapshots: 3
max-wals: 5
cors:
initial-advertise-peer-urls: 'https://10.20.1.102:2380'
advertise-client-urls: 'https://10.20.1.102:2379'
discovery:
discovery-fallback: 'proxy'
discovery-proxy:
discovery-srv:
initial-cluster: 'k8s-master01=https://10.20.1.101:2380,k8s-master02=https://10.20.1.102:2380,k8s-master03=https://10.20.1.103:2380'
initial-cluster-token: 'etcd-k8s-cluster'
initial-cluster-state: 'new'
strict-reconfig-check: false
enable-v2: true
enable-pprof: true
proxy: 'off'
proxy-failure-wait: 5000
proxy-refresh-interval: 30000
proxy-dial-timeout: 1000
proxy-write-timeout: 5000
proxy-read-timeout: 0
client-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
peer-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  peer-client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
debug: false
log-package-levels:
log-outputs: [default]
force-new-cluster: false
EOF

1.3 Master03 节点

cat > /etc/etcd/etcd.config.yml << EOF 
name: 'k8s-master03'
data-dir: /data/etcd
wal-dir: /data/etcd/wal
snapshot-count: 5000
heartbeat-interval: 100
election-timeout: 1000
quota-backend-bytes: 0
listen-peer-urls: 'https://10.20.1.103:2380'
listen-client-urls: 'https://10.20.1.103:2379,http://127.0.0.1:2379'
max-snapshots: 3
max-wals: 5
cors:
initial-advertise-peer-urls: 'https://10.20.1.103:2380'
advertise-client-urls: 'https://10.20.1.103:2379'
discovery:
discovery-fallback: 'proxy'
discovery-proxy:
discovery-srv:
initial-cluster: 'k8s-master01=https://10.20.1.101:2380,k8s-master02=https://10.20.1.102:2380,k8s-master03=https://10.20.1.103:2380'
initial-cluster-token: 'etcd-k8s-cluster'
initial-cluster-state: 'new'
strict-reconfig-check: false
enable-v2: true
enable-pprof: true
proxy: 'off'
proxy-failure-wait: 5000
proxy-refresh-interval: 30000
proxy-dial-timeout: 1000
proxy-write-timeout: 5000
proxy-read-timeout: 0
client-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
peer-transport-security:
  cert-file: '/etc/kubernetes/pki/etcd/etcd.pem'
  key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem'
  peer-client-cert-auth: true
  trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem'
  auto-tls: true
debug: false
log-package-levels:
log-outputs: [default]
force-new-cluster: false
EOF

1.4 创建 ETCD Service

master 01

cat > /usr/lib/systemd/system/etcd.service << EOF

[Unit]
Description=Etcd Service
Documentation=https://coreos.com/etcd/docs/latest/
After=network.target

[Service]
Type=notify
ExecStart=/usr/local/bin/etcd --config-file=/etc/etcd/etcd.config.yml
Restart=on-failure
RestartSec=10
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
Alias=etcd3.service

EOF

命令解析:

该文件定义了 etcd 服务在 Linux 系统上的运行方式,使用 systemd 管理 etcd 进程的启动、停止、自动重启等行为。此配置是为 Kubernetes 高可用集群中的 etcd 节点(例如 k8s-master03)设计的,结合了 /etc/etcd/etcd.config.yml 配置文件运行 etcd。

  • After=network.target:定义服务在哪些 systemd 目标或单元启动后启动。network.target 表示网络服务可用。 影响:确保 etcd 在网络初始化后启动,因为 etcd 需要监听网络端口(2379/2380)进行客户端和节点间通信。如果网络不可用,etcd 可能启动失败

  • Type=notify:默认值:simple 描述:定义服务进程的类型。notify 表示主进程通过 sd_notify(3) 向 systemd 发送启动完成信号(etcd 支持此机制)。 影响:notify 允许 systemd 等待 etcd 完成初始化(如绑定端口、加入集群)后再标记服务为“运行”。

  • ExecStart=/usr/local/bin/etcd –config-file=/etc/etcd/etcd.config.yml

    服务启动时执行的命令及其参数。运行 etcd 二进制,指定配置文件路径

  • Restart=on-failure:控制服务失败时的重启行为。on-failure 表示在进程异常退出(非零退出码)、超时或被信号终止时自动重启。

  • RestartSec=10:重启前的等待时间(秒)

  • LimitNOFILE=65536:设置进程的最大文件描述符数量(软限制和硬限制)。 影响:etcd 需处理大量网络连接(如 Kubernetes API 请求),默认值可能不足导致 “too many open files” 错误。65536 适合高负载集群,需确保系统级限制

  • WantedBy=multi-user.target:指定服务在哪个 systemd 目标启用。multi-user.target 表示多用户模式(非图形界面,通常为服务器默认运行级别)

  • Alias=etcd3.service:为服务创建别名,允许通过别名(如 systemctl start etcd3.service)操作服务

同步到其它Master节点

# 将生成的 ETCD Service复制到其他节点
hosts='k8s-master02 k8s-master03'
user=root
for host in $hosts; do
  echo $host; 
  rsync --rsync-path="sudo rsync" /usr/lib/systemd/system/etcd.service $user@$host:/usr/lib/systemd/system/;
done

1.5 启动 ETCD Service

hosts='k8s-master01 k8s-master02 k8s-master03'
user=root
for host in $hosts; do
  echo $host; 
  echo "创建 etcd 证书目录"
  ssh root@$host "mkdir -p /etc/kubernetes/pki/etcd"
  echo "创建软连接"
  ssh root@$host "ln -s /etc/etcd/ssl/* /etc/kubernetes/pki/etcd/"
  ssh root@$host "systemctl daemon-reload"
  ssh root@$host "systemctl enable --now etcd.service"
  ssh root@$host "systemctl status etcd.service"
done

查看ETCD Service启动状态

1.6 检查 ETCD 集群的健康状态

master01 、master02、master03

export ETCDCTL_API=3
etcdctl --endpoints="10.20.1.101:2379,10.20.1.102:2379,10.20.1.103:2379" \
  --cacert=/etc/kubernetes/pki/etcd/etcd-ca.pem \
  --cert=/etc/kubernetes/pki/etcd/etcd.pem \
  --key=/etc/kubernetes/pki/etcd/etcd-key.pem \
  endpoint status --write-out=table

检查 ETCD 集群的健康状态

命令解析:

该命令用于检查 etcd 集群的健康状态,是 Kubernetes 高可用部署中验证 etcd 集群运行情况的关键步骤

  • export ETCDCTL_API=3

    明确设置 ETCDCTL_API=3 确保 etcdctl 使用 v3 协议,避免与 v2 兼容性问题

  • etcdctl:是 etcd 提供的命令行工具,用于与 etcd 集群交互(如查询状态、管理成员、执行操作)

  • –endpoints:指定 etcd 集群的客户端访问端点,包含三个节点的 IP 和端口(2379)

  • –cacert:指定 CA 证书路径 /etc/kubernetes/pki/etcd/etcd-ca.pem

  • –cert:客户端证书路径 /etc/kubernetes/pki/etcd/etcd.pem

  • –key:客户端私钥路径 /etc/kubernetes/pki/etcd/etcd-key.pem

2. Nginx 配置

2.1 编译安装

master 01

# 安装编译环境
yum install -y openssl-devel pcre-devel gcc

# 下载解压 nginx 二进制文件
wget http://nginx.org/download/nginx-1.25.3.tar.gz
tar xvf nginx-*.tar.gz
cd nginx-1.25.3

# 进行编译
./configure --prefix=/usr/local/nginx --sbin-path=/bin/ --user=nginx --group=nginx --with-stream --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --without-http --without-http_uwsgi_module --without-http_scgi_module --without-http_fastcgi_module
make && make install

# 将编译好的 nginx 二进制包,拷贝到其它节点
hosts='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
for host in $hosts; do
  echo $host; 
  rsync -a --rsync-path="sudo rsync" /usr/local/nginx $user@$host:/usr/local/;
  rsync --rsync-path="sudo rsync" /bin/nginx* $user@$host:/bin/;
done

# 验证 
$ nginx -v
nginx version: nginx/1.25.3

2.2 nginx 配置文件

Master 01

# 写入配置文件(在所有主机上执行)
cat > /usr/local/nginx/conf/kube-nginx.conf <<EOF
worker_processes 1;
user nobody;
events {
    worker_connections  1024;
}
stream {
    upstream backend {
    	least_conn;
        hash $remote_addr consistent;
        server 10.20.1.101:6443        max_fails=3 fail_timeout=30s;
        server 10.20.1.102:6443        max_fails=3 fail_timeout=30s;
        server 10.20.1.103:6443        max_fails=3 fail_timeout=30s;
    }
    server {
        listen 127.0.0.1:8443;
        proxy_connect_timeout 1s;
        proxy_pass backend;
    }
}
EOF

# 将kube-nginx.conf,拷贝到其它 Master 节点
hosts='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
for host in $hosts; do
  echo $host; 
  rsync -a --rsync-path="sudo rsync" /usr/local/nginx/conf/kube-nginx.conf $user@$host:/usr/local/nginx/conf/;
done

命令解析:

该命令用于创建 Nginx 配置文件 /usr/local/nginx/conf/kube-nginx.conf,配置 Nginx 作为 Kubernetes API 服务器的负载均衡器,处理 TCP 流量(端口 6443)

  • upstream backend

    将本地 8443 端口的请求分发到三个 master 节点的 API 服务器的 6443 端口

  • least_conn;

    选择当前活跃连接数最少的后端服务器进行负载均衡

  • hash $remote_addr consistent;

    基于客户端 IP($remote_addr)进行一致性哈希,确保同一客户端始终连接到同一后端服务器

  • max_fails=3 fail_timeout=30s;

    max_fails=3(失败 3 次后标记为不可用),fail_timeout=30s(标记不可用后 30 秒内不尝试)

  • 6443 端口是 Kubernetes API 服务器(kube-apiserver)的默认端口,接受来自客户端(如 kubectl、kubelet、控制器等)的 HTTPS 请求。

  • 8443 端口是 kube-apiserver 的代理端口,用于接收请求,并将请求转发到真正的 6443 端口

2.3 配置 Nginx Service

Master 01

cat > /etc/systemd/system/kube-nginx.service <<EOF
[Unit]
Description=kube-apiserver nginx proxy
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
ExecStartPre=/bin/nginx -c /usr/local/nginx/conf/kube-nginx.conf -p /usr/local/nginx -t
ExecStart=/bin/nginx -c /usr/local/nginx/conf/kube-nginx.conf -p /usr/local/nginx
ExecReload=/bin/nginx -c /usr/local/nginx/conf/kube-nginx.conf -p /usr/local/nginx -s reload
PrivateTmp=true
Restart=always
RestartSec=5
StartLimitInterval=0
LimitNOFILE=65536
 
[Install]
WantedBy=multi-user.target
EOF

命令解析:

创建一个 systemd 单元文件,用于管理系统服务。这个文件定义了一个名为 kube-nginx 的服务,负责运行 Nginx 作为 Kubernetes API 服务器的负载均衡代理

Nginx 在这里被配置为 TCP 负载均衡器(非 HTTP),运行在每个 master 节点(k8s-master01、k8s-master02、k8s-master03,IP 分别为 192.168.10.11、12、13),监听本地 127.0.0.1:8443 端口,并将请求分发到三个 master 节点的 API 服务器端口 6443。

  • After=network.target :基本网络接口配置完成

  • After=network-online.target :网络完全在线(可路由外部)

  • Wants=network-online.target :声明弱依赖,建议(但不强制)在 network-online.target 启动后运行服务

  • Type=forking:

    forking 表示主进程派生子进程(如 Nginx master 进程派生 worker),systemd 跟踪子进程 PID(从 PID 文件读取)

  • ExecStartPre=/bin/nginx -c /usr/local/nginx/conf/kube-nginx.conf -p /usr/local/nginx -t

    启动前执行的预命令。-t 测试配置文件语法

  • ExecStart=/bin/nginx -c /usr/local/nginx/conf/kube-nginx.conf -p /usr/local/nginx

    启动服务的核心命令,运行 Nginx。 博客上下文:启动 Nginx,加载 kube-nginx.conf,启用 TCP 负载均衡(8443 到 6443)

  • ExecReload=/bin/nginx -c /usr/local/nginx/conf/kube-nginx.conf -p /usr/local/nginx -s reload

    重新加载配置的命令(systemctl reload kube-nginx)。-s reload 发送信号平滑重载

  • PrivateTmp=true :为服务分配私有 /tmp 和 /var/tmp 目录,隔离系统临时文件

  • Restart=always :失败或退出时自动重启。always 表示无论原因都重启

  • RestartSec=5 :重启前等待时间(秒)

  • StartLimitInterval=0 :重启时间窗口(秒),0 表示无限制。

  • LimitNOFILE=65536 :最大文件描述符数,防止 “too many open files” 错误

  • WantedBy=multi-user.target : 服务在多用户模式下启用, 确保 Nginx 随系统启动。

2.4 设置 Nginx 开机自启

# 刷新系统服务
systemctl daemon-reload

# 设置 kube-nginx 开机自启
systemctl enable --now kube-nginx.service

# 查看 nginx 启动状态
systemctl status kube-nginx.service

将 kube-nginx.service 同步到其它服务器

# 将kube-nginx.service,拷贝到其它 Master 节点,并让Nginx开机自启
hosts='k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
for host in $hosts; do
  echo $host; 
  rsync -a --rsync-path="sudo rsync" /etc/systemd/system/kube-nginx.service $user@$host:/etc/systemd/system/;
  ssh root@$host "systemctl daemon-reload"
  ssh root@$host "systemctl enable --now kube-nginx.service"
  ssh root@$host "systemctl status kube-nginx.service"
done

3. kube-apiserver 配置

3.1 创建必要的目录

master01

# 所有 k8s 节点创建以下目录
hosts='k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02'
user=root
for host in $hosts; do
  echo $host; 
  ssh root@$host "mkdir -p /etc/kubernetes/manifests/ /etc/systemd/system/kubelet.service.d /var/lib/kubelet /var/log/kubernetes"
done

3.2 配置 kube-apiserver.service

官方文档:https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kube-apiserver/

master01

cat > /usr/lib/systemd/system/kube-apiserver.service << EOF

[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
      --v=2  \\
      --allow-privileged=true  \\
      --bind-address=0.0.0.0  \\
      --secure-port=6443  \\
      --advertise-address=10.20.1.101 \\
      --service-cluster-ip-range=10.96.0.0/12,fd00:1111::/112  \\
      --service-node-port-range=30000-32767  \\
      --etcd-servers=https://10.20.1.101:2379,https://10.20.1.102:2379,https://10.20.1.103:2379 \\
      --etcd-cafile=/etc/etcd/ssl/etcd-ca.pem  \\
      --etcd-certfile=/etc/etcd/ssl/etcd.pem  \\
      --etcd-keyfile=/etc/etcd/ssl/etcd-key.pem  \\
      --client-ca-file=/etc/kubernetes/pki/ca.pem  \\
      --tls-cert-file=/etc/kubernetes/pki/apiserver.pem  \\
      --tls-private-key-file=/etc/kubernetes/pki/apiserver-key.pem  \\
      --kubelet-client-certificate=/etc/kubernetes/pki/apiserver.pem  \\
      --kubelet-client-key=/etc/kubernetes/pki/apiserver-key.pem  \\
      --service-account-key-file=/etc/kubernetes/pki/sa.pub  \\
      --service-account-signing-key-file=/etc/kubernetes/pki/sa.key  \\
      --service-account-issuer=https://kubernetes.default.svc.cluster.local \\
      --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname  \\
      --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota  \
      --authorization-mode=Node,RBAC  \\
      --enable-bootstrap-token-auth=true  \\
      --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem  \\
      --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.pem  \\
      --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client-key.pem  \\
      --requestheader-allowed-names=aggregator  \\
      --requestheader-group-headers=X-Remote-Group  \\
      --requestheader-extra-headers-prefix=X-Remote-Extra-  \\
      --requestheader-username-headers=X-Remote-User \\
      --enable-aggregator-routing=true
Restart=on-failure
RestartSec=10s
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target

EOF

master02

cat > /usr/lib/systemd/system/kube-apiserver.service << EOF

[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
      --v=2  \\
      --allow-privileged=true  \\
      --bind-address=0.0.0.0  \\
      --secure-port=6443  \\
      --advertise-address=10.20.1.102 \\
      --service-cluster-ip-range=10.96.0.0/12,fd00:1111::/112  \\
      --service-node-port-range=30000-32767  \\
      --etcd-servers=https://10.20.1.101:2379,https://10.20.1.102:2379,https://10.20.1.103:2379 \\
      --etcd-cafile=/etc/etcd/ssl/etcd-ca.pem  \\
      --etcd-certfile=/etc/etcd/ssl/etcd.pem  \\
      --etcd-keyfile=/etc/etcd/ssl/etcd-key.pem  \\
      --client-ca-file=/etc/kubernetes/pki/ca.pem  \\
      --tls-cert-file=/etc/kubernetes/pki/apiserver.pem  \\
      --tls-private-key-file=/etc/kubernetes/pki/apiserver-key.pem  \\
      --kubelet-client-certificate=/etc/kubernetes/pki/apiserver.pem  \\
      --kubelet-client-key=/etc/kubernetes/pki/apiserver-key.pem  \\
      --service-account-key-file=/etc/kubernetes/pki/sa.pub  \\
      --service-account-signing-key-file=/etc/kubernetes/pki/sa.key  \\
      --service-account-issuer=https://kubernetes.default.svc.cluster.local \\
      --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname  \\
      --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota  \
      --authorization-mode=Node,RBAC  \\
      --enable-bootstrap-token-auth=true  \\
      --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem  \\
      --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.pem  \\
      --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client-key.pem  \\
      --requestheader-allowed-names=aggregator  \\
      --requestheader-group-headers=X-Remote-Group  \\
      --requestheader-extra-headers-prefix=X-Remote-Extra-  \\
      --requestheader-username-headers=X-Remote-User \\
      --enable-aggregator-routing=true
Restart=on-failure
RestartSec=10s
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target

EOF

master03

cat > /usr/lib/systemd/system/kube-apiserver.service << EOF

[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
      --v=2  \\
      --allow-privileged=true  \\
      --bind-address=0.0.0.0  \\
      --secure-port=6443  \\
      --advertise-address=10.20.1.103 \\
      --service-cluster-ip-range=10.96.0.0/12,fd00:1111::/112  \\
      --service-node-port-range=30000-32767  \\
      --etcd-servers=https://10.20.1.101:2379,https://10.20.1.102:2379,https://10.20.1.103:2379 \\
      --etcd-cafile=/etc/etcd/ssl/etcd-ca.pem  \\
      --etcd-certfile=/etc/etcd/ssl/etcd.pem  \\
      --etcd-keyfile=/etc/etcd/ssl/etcd-key.pem  \\
      --client-ca-file=/etc/kubernetes/pki/ca.pem  \\
      --tls-cert-file=/etc/kubernetes/pki/apiserver.pem  \\
      --tls-private-key-file=/etc/kubernetes/pki/apiserver-key.pem  \\
      --kubelet-client-certificate=/etc/kubernetes/pki/apiserver.pem  \\
      --kubelet-client-key=/etc/kubernetes/pki/apiserver-key.pem  \\
      --service-account-key-file=/etc/kubernetes/pki/sa.pub  \\
      --service-account-signing-key-file=/etc/kubernetes/pki/sa.key  \\
      --service-account-issuer=https://kubernetes.default.svc.cluster.local \\
      --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname  \\
      --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota  \
      --authorization-mode=Node,RBAC  \\
      --enable-bootstrap-token-auth=true  \\
      --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem  \\
      --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.pem  \\
      --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client-key.pem  \\
      --requestheader-allowed-names=aggregator  \\
      --requestheader-group-headers=X-Remote-Group  \\
      --requestheader-extra-headers-prefix=X-Remote-Extra-  \\
      --requestheader-username-headers=X-Remote-User \\
      --enable-aggregator-routing=true
Restart=on-failure
RestartSec=10s
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target

EOF

3.3 启动 kube-apiserver

hosts='k8s-master01 k8s-master02 k8s-master03'
user=root
for host in $hosts; do
  echo $host; 
  ssh root@$host "systemctl daemon-reload"
  ssh root@$host "systemctl enable --now kube-apiserver.service"
  ssh root@$host "systemctl status kube-apiserver.service"
done

kube-apiserver.service 文件解析:

该配置文件创建 systemd 服务文件,运行 kube-apiserver(Kubernetes 控制平面的核心组件),监听 6443 端口,处理集群 API 请求

  • –v=2

    设置日志级别为 2(中等详细,调试用)

    生产环境可降为 –v=1 减少日志,或增至 –v=4 排查问题

  • –allow-privileged=true

    API 服务器监听所有网络接口(0.0.0.0)上的 6443 端口

    允许外部和内部客户端(如 kubectl、Nginx 负载均衡器)访问

    若仅限本地访问,可设为 –bind-address=127.0.0.1,但高可用集群通常需要 0.0.0.0

  • –secure-port=6443

    API 服务器监听的 HTTPS 端口(6443)

    提供安全的 API 访问,配合 TLS 证书

    6443 是 Kubernetes 默认端口,Nginx 负载均衡器代理到此端口。

  • –advertise-address=

    API 服务器向集群其他组件通告的 IP 地址。

    指定当前节点的 IP(k8s-master01),用于节点间通信和客户端连接。

  • –service-cluster-ip-range=10.96.0.0/12,fd00:1111::/112 \

    定义 Service 的 Cluster IP 地址范围,支持双栈(IPv4 和 IPv6)。

    • 10.96.0.0/12:IPv4 地址池(10.96.0.0 - 10.111.255.255,约 104 万个地址)。
    • fd00:1111::/112:IPv6 唯一本地地址(ULA),提供 65,536 个地址。

    作用:为 Service(ClusterIP 类型)分配虚拟 IP,供服务发现和负载均衡。

  • –service-node-port-range=30000-32767 \

    NodePort 类型 Service 的端口范围。

    限制 NodePort 分配的端口(默认 30000-32767),用于外部访问。

  • –etcd-servers=

    指定 etcd 集群的 HTTPS 端点(三个主节点,2379 端口)。

  • –service-account-issuer=https://kubernetes.default.svc.cluster.local \

    ServiceAccount token 的发行者标识

  • –kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname \

    API 服务器连接 kubelet 时优先使用的地址类型。

    按顺序尝试 InternalIP、ExternalIP、Hostname。

    适合 Calico 网络,优先内部 IP(10.20.1.x)。

  • –enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota \

    启用准入控制插件

    • NamespaceLifecycle:管理命名空间生命周期。
    • LimitRanger:限制资源使用。
    • ServiceAccount:自动注入 ServiceAccount token。
    • DefaultStorageClass:为 PVC 设置默认存储类。
    • DefaultTolerationSeconds:设置默认容忍时间。
    • NodeRestriction:限制 kubelet 权限。
    • ResourceQuota:强制资源配额。
  • –authorization-mode=Node,RBAC \

    启用 Node 和 RBAC 授权模式。Node 授权 kubelet 请求,RBAC 管理用户和角色权限。

  • –enable-bootstrap-token-auth=true \

    启用引导令牌认证,允许新节点通过 token 加入集群。

  • –requestheader-allowed-names=aggregator \

    允许的聚合器客户端名称。

  • –requestheader-group-headers=X-Remote-Group \

    HTTP 头中传递组信息的字段。

  • –requestheader-extra-headers-prefix=X-Remote-Extra- \

    额外信息的 HTTP 头前缀。

  • –requestheader-username-headers=X-Remote-User \

    HTTP 头中传递用户名的字段。

  • –enable-aggregator-routing=true

    启用 API 聚合器路由

  • Restart=on-failure

    服务失败时重启

  • RestartSec=10s

    重启前等待 10 秒

  • LimitNOFILE=65535

    最大文件描述符数,支持高并发连接,匹配 Nginx 的 65536

4. kube-controller-manager 配置

4.1 配置 kube-controller-manager.service

官方文档:https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kube-controller-manager/

  • 所有master节点配置,且配置相同
  • 172.16.0.0/12 为 pod 网段,按需求设置你自己的网段

master01

cat > /usr/lib/systemd/system/kube-controller-manager.service << EOF

[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/usr/local/bin/kube-controller-manager \\
      --v=2 \\
      --bind-address=0.0.0.0 \\
      --root-ca-file=/etc/kubernetes/pki/ca.pem \\
      --cluster-signing-cert-file=/etc/kubernetes/pki/ca.pem \\
      --cluster-signing-key-file=/etc/kubernetes/pki/ca-key.pem \\
      --service-account-private-key-file=/etc/kubernetes/pki/sa.key \\
      --kubeconfig=/etc/kubernetes/controller-manager.kubeconfig \\
      --leader-elect=true \\
      --use-service-account-credentials=true \\
      --node-monitor-grace-period=40s \\
      --node-monitor-period=5s \\
      --controllers=*,bootstrapsigner,tokencleaner \\
      --allocate-node-cidrs=true \\
      --service-cluster-ip-range=10.96.0.0/12,fd00:1111::/112 \\
      --cluster-cidr=172.16.0.0/12,fc00:2222::/112 \\
      --node-cidr-mask-size-ipv4=24 \\
      --node-cidr-mask-size-ipv6=120 \\
      --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem

Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target

EOF

配置解析:

kube-controller-manager 是 Kubernetes 控制平面的核心组件之一,负责运行控制器以维护集群状态(如 ReplicaSet、Deployment 控制器)。

  • ExecStart=/usr/local/bin/kube-controller-manager \

    指定启动命令,运行 kube-controller-manager 可执行文件(位于 /usr/local/bin/),后续参数以 \ 分行。

  • –v=2 \

    设置日志级别为 2(中等详细,适合调试)

    生产环境可设为 –v=1 减少日志,或增至 –v=4 排查复杂问题。

  • –bind-address=0.0.0.0 \

    Controller Manager 监听所有网络接口(0.0.0.0)上的端口(默认 10257,HTTPS)。允许外部访问健康检查或指标端点(如 /healthz、/metrics)。

  • –root-ca-file=/etc/kubernetes/pki/ca.pem \

    Kubernetes CA 证书路径,用于验证客户端和服务端证书。

  • –cluster-signing-cert-file=/etc/kubernetes/pki/ca.pem \

    用于签署集群内证书的 CA 证书。Controller Manager 使用该 CA 为 CSR(证书签名请求)签名(如 kubelet 证书)。

  • –cluster-signing-key-file=/etc/kubernetes/pki/ca-key.pem \

    CA 证书的私钥。与 –cluster-signing-cert-file 配对,用于签署证书。

  • –service-account-private-key-file=/etc/kubernetes/pki/sa.key \

    服务账户(ServiceAccount)token 的签名私钥。生成 ServiceAccount token,供 Pod 认证。

  • –kubeconfig=/etc/kubernetes/controller-manager.kubeconfig \

    Controller Manager 的 kubeconfig 文件路径,定义 API 服务器连接信息和认证凭据。

    允许 Controller Manager 通过 kubeconfig 访问 API 服务器(6443 端口)。

  • –leader-elect=true \

    启用领导者选举,在高可用集群中,确保多个主节点的 Controller Manager 实例中只有一个活跃(其他为热备),避免冲突。

  • –use-service-account-credentials=true \

    为每个控制器使用独立的 ServiceAccount 凭据。增强安全性,限制控制器权限(而非使用默认高权限凭据)。

  • –node-monitor-grace-period=40s \

    节点控制器标记节点为 NotReady 前的宽限时间。若节点 40 秒未响应(无心跳),标记为不可用。

  • –node-monitor-period=5s \

    节点控制器检查节点状态的周期。每 5 秒检查节点健康状态。

  • –controllers=*,bootstrapsigner,tokencleaner \

    指定启用的控制器列表。

    *:启用所有默认控制器(如 ReplicaSet、Deployment、Node 控制器)。

    bootstrapsigner:签署引导令牌的 CSR。

    tokencleaner:清理过期引导令牌。

  • –allocate-node-cidrs=true \

    启用节点 CIDR 分配。Controller Manager 为每个节点分配 Pod CIDR(由 –cluster-cidr 定义)

  • –service-cluster-ip-range=10.96.0.0/12,fd00:1111::/112 \

    Service 的 Cluster IP 地址范围(IPv4 和 IPv6)。

    定义 Service 的虚拟 IP 池,与 kube-apiserver 的 –service-cluster-ip-range 一致。

  • –node-cidr-mask-size-ipv4=24 \

    每个节点的 IPv4 Pod CIDR 子网掩码大小,每个节点分配 /24 子网(256 个 IP),从 172.16.0.0/12 中划分。

  • –node-cidr-mask-size-ipv6=120 \

    每个节点的 IPv6 Pod CIDR 子网掩码大小。

    每个节点分配 /120 子网(256 个 IP),从 fc00:2222::/112 中划分。

  • –requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem \

    API 聚合器的 CA 证书。验证聚合器客户端(如扩展 API 服务器),与 kube-apiserver 一致。

  • Restart=always

    服务无论何种原因退出都重启。确保 Controller Manager 高可用。

  • RestartSec=10s

    重启前等待 10 秒。

配置分发到其它Master节点

hosts='k8s-master02 k8s-master03'
user=root
for host in $hosts; do
  echo $host; 
  rsync -a --rsync-path="sudo rsync" /usr/lib/systemd/system/kube-controller-manager.service $user@$host:/usr/lib/systemd/system/;
done

4.2 启动 kube-controller-manager

hosts='k8s-master01 k8s-master02 k8s-master03'
user=root
for host in $hosts; do
  echo $host; 
  ssh root@$host "systemctl daemon-reload"
  ssh root@$host "systemctl enable --now kube-controller-manager.service"
  ssh root@$host "systemctl status kube-controller-manager.service"
done

5. kube-scheduler 配置

所有 master 节点配置,且配置相同

5.1 kube-scheduler.service

cat > /usr/lib/systemd/system/kube-scheduler.service << EOF

[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/usr/local/bin/kube-scheduler \\
      --v=2 \\
      --bind-address=0.0.0.0 \\
      --leader-elect=true \\
      --kubeconfig=/etc/kubernetes/scheduler.kubeconfig

Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target

EOF

配置解析:

kube-scheduler 与 API 服务器(6443 端口)交互,通过 kubeconfig 文件访问集群状态,决定 Pod 分配。

  • ExecStart=/usr/local/bin/kube-scheduler \

    指定启动命令,运行 kube-scheduler 可执行文件(位于 /usr/local/bin/),后续参数以 \ 分行。

    启动 Scheduler,负责根据节点资源、策略和约束(如亲和性、污点)将 Pod 调度到节点。

  • –v=2 \

    设置日志级别为 2(中等详细,适合调试)。

    生产环境可设为 –v=1 减少日志,或增至 –v=4 排查复杂问题。

  • –bind-address=0.0.0.0 \

    Scheduler 监听所有网络接口(0.0.0.0)上的端口(默认 10259,HTTPS)。

    允许外部访问健康检查或指标端点(如 /healthz、/metrics),常用于监控(如 Prometheus)。

  • –leader-elect=true \

    启用领导者选举。

    在高可用集群中,确保多个主节点的 Scheduler 实例中只有一个活跃(其他为热备),避免调度冲突。

  • –kubeconfig=/etc/kubernetes/scheduler.kubeconfig \

    Scheduler 的 kubeconfig 文件路径,定义 API 服务器连接信息和认证凭据。

  • Restart=always

    服务无论何种原因退出都重启。

  • RestartSec=10s

    重启前等待 10 秒。

配置同步到其它 master 节点

hosts='k8s-master02 k8s-master03'
user=root
for host in $hosts; do
  echo $host; 
  rsync -a --rsync-path="sudo rsync" /usr/lib/systemd/system/kube-scheduler.service $user@$host:/usr/lib/systemd/system/;
done

5.2 启动 kube-scheduler

hosts='k8s-master01 k8s-master02 k8s-master03'
user=root
for host in $hosts; do
  echo $host; 
  ssh root@$host "systemctl daemon-reload"
  ssh root@$host "systemctl enable --now kube-scheduler.service"
  ssh root@$host "systemctl status kube-scheduler.service"
done

六、TLS Bootstrapping 配置

官方文档:https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/kubelet-tls-bootstrapping/

1. 生成 bootstrap 的 token

master01

TOKEN_ID=$(openssl rand -hex 3)
TOKEN_SECRET=$(openssl rand -hex 8)
BOOTSTRAP_TOKEN=${TOKEN_ID}.${TOKEN_SECRET}
 
$ echo $BOOTSTRAP_TOKEN
25062c.208e46b2a427f63a

命令解析:

生成一个 Kubernetes 引导令牌(Bootstrap Token),格式为 .,用于节点(主节点或工作节点)通过 kubeadm join 加入集群时进行身份验证

2. 配置 bootstrap-kubelet.kubeconfig

master01

# 在指定的 kubeconfig 文件(/etc/kubernetes/bootstrap-kubelet.kubeconfig)中配置一个名为 kubernetes 的集群条目,定义如何连接到 Kubernetes API 服务器,包括 CA 证书和服务器地址
kubectl config set-cluster kubernetes     \
--certificate-authority=/etc/kubernetes/pki/ca.pem     \
--embed-certs=true     --server=https://127.0.0.1:8443     \
--kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig


# 在指定的 kubeconfig 文件中创建或更新一个名为 tls-bootstrap-token-user 的用户凭据条目,配置引导令牌(25062c.208e46b2a427f63a),用于 kubelet 在节点引导过程中通过 Nginx 负载均衡器(127.0.0.1:8443)访问 API 服务器
kubectl config set-credentials tls-bootstrap-token-user     \
--token=25062c.208e46b2a427f63a \
--kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig

# 在 kubeconfig 文件中配置上下文(context),将集群和用户凭据关联起来,供 kubelet 在节点引导过程中使用以访问 Kubernetes API 服务器
kubectl config set-context tls-bootstrap-token-user@kubernetes     \
--cluster=kubernetes     \
--user=tls-bootstrap-token-user     \
--kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig

# 在 kubeconfig 文件中设置默认上下文,指定 kubelet 使用特定的上下文(tls-bootstrap-token-user@kubernetes)来访问 Kubernetes API 服务器,从而完成节点引导和注册。
kubectl config use-context tls-bootstrap-token-user@kubernetes     \
--kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig


# token的位置在 bootstrap.secret.yaml,如果修改的话到这个文件修改
# 创建 /root/.kube 目录,用于存储 Kubernetes 管理用户的 kubeconfig 文件
mkdir -p /root/.kube
# 将管理员的 kubeconfig 文件(/etc/kubernetes/admin.kubeconfig)复制到 /root/.kube/config,作为 kubectl 的默认配置文件
cp /etc/kubernetes/admin.kubeconfig /root/.kube/config


# 分发 kubelet 配置文件
host=(k8s-master02 k8s-master03)
user=root
for i in ${host[@]}; do
	ssh $user@$i "mkdir -p /root/.kube"
    rsync --rsync-path="sudo rsync" /root/.kube/config $user@$i:/root/.kube/;
done

3. 创建 bootstrap-secret.yaml

注意:bootstrap.secret.yaml的token-id、token-secret,需与上命令token保持一致(即 25062c.208e46b2a427f63a )

# 创建目录
mkdir -p /etc/kubernetes/yaml

# 编辑文件
cat > /etc/kubernetes/yaml/bootstrap-secret.yaml  << EOF
apiVersion: v1
kind: Secret
metadata:
  name: bootstrap-token-${TOKEN_ID}
  namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
  description: "The default bootstrap token generated by 'kubelet '."
  token-id: ${TOKEN_ID}
  token-secret: ${TOKEN_SECRET}
  usage-bootstrap-authentication: "true"
  usage-bootstrap-signing: "true"
  auth-extra-groups:  system:bootstrappers:default-node-token,system:bootstrappers:worker,system:bootstrappers:ingress
EOF

# 执行资源清单 
$ kubectl create -f /etc/kubernetes/yaml/bootstrap-secret.yaml
secret/bootstrap-token-25062c created
# 查看结果
$ kubectl get secret -n kube-system
NAME                     TYPE                            DATA   AGE
bootstrap-token-25062c   bootstrap.kubernetes.io/token   6      10s

4. 创建 kubelet-bootstrap-rbac.yaml

cat > /etc/kubernetes/yaml/kubelet-bootstrap-rbac.yaml << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kubelet-bootstrap
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:node-bootstrapper
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:bootstrappers:default-node-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: node-autoapprove-bootstrap
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:bootstrappers:default-node-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: node-autoapprove-certificate-rotation
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:nodes
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-apiserver-to-kubelet
rules:
  - apiGroups:
      - ""
    resources:
      - nodes/proxy
      - nodes/stats
      - nodes/log
      - nodes/spec
      - nodes/metrics
    verbs:
      - "*"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:kube-apiserver
  namespace: ""
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:kube-apiserver-to-kubelet
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: kube-apiserver
EOF


# 执行清单
kubectl create -f /etc/kubernetes/yaml/kubelet-bootstrap-rbac.yaml

5. 分发配置文件

host=(k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02)
user=root
for i in ${host[@]}; do
    rsync --rsync-path="sudo rsync" /etc/kubernetes/{bootstrap-kubelet,kube-proxy}.kubeconfig $user@$i:/etc/kubernetes/;
done

注:若kubectl get node 为空,那应该就是 bootstrap-kubelet.kubeconfig 中的 token 对不上, 修改后重启kubelet

6. 查看集群状态

[root@k8s-master01 ~]$ kubectl get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE   ERROR
scheduler            Healthy   ok        
etcd-0               Healthy   ok        
controller-manager   Healthy   ok

七、Node 配置

1. 复制相关证书至 node 节点

user=root
host=(k8s-master02 k8s-master03 k8s-node01 k8s-node02)

for i in ${host[@]}; do
  ssh $user@$i "sudo mkdir -p /etc/kubernetes/pki/";
  rsync --rsync-path="sudo rsync" /etc/kubernetes/pki/{ca.pem,ca-key.pem,front-proxy-ca.pem} $user@$i:/etc/kubernetes/pki/;
  rsync --rsync-path="sudo rsync" /etc/kubernetes/{bootstrap-kubelet.kubeconfig,kube-proxy.kubeconfig} $user@$i:/etc/kubernetes/pki/;
done

2. 配置 kubelet(所有节点)

2.1 编辑 kubelet.service

Master01

# 当使用 docker 作为 Runtime
# IPv4示例
cat > /usr/lib/systemd/system/kubelet.service << EOF

[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=network-online.target firewalld.service cri-docker.service docker.socket containerd.service
Wants=network-online.target
Requires=docker.socket containerd.service

[Service]
ExecStart=/usr/local/bin/kubelet \\
    --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig  \\
    --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\
    --config=/etc/kubernetes/kubelet-conf.yml \\
    --container-runtime-endpoint=unix:///run/cri-dockerd.sock  \\
    --node-labels=node.kubernetes.io/node= 

[Install]
WantedBy=multi-user.target
EOF

# IPv6示例
# 若不使用IPv6那么忽略此项即可
# 下方 --node-ip 更换为每个节点的IP即可
# cat > /usr/lib/systemd/system/kubelet.service << EOF
# [Unit]
# Description=Kubernetes Kubelet
# Documentation=https://github.com/kubernetes/kubernetes
# After=network-online.target firewalld.service cri-docker.service docker.socket # containerd.service
# Wants=network-online.target
# Requires=docker.socket containerd.service

# [Service]
# ExecStart=/usr/local/bin/kubelet \\
#     --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig  \\
#     --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \\
#     --config=/etc/kubernetes/kubelet-conf.yml \\
#     --container-runtime-endpoint=unix:///run/cri-dockerd.sock  \\
#     --node-labels=node.kubernetes.io/node=   \\
#     --node-ip=192.168.1.31,2408:822a:245:8c01::fab
# [Install]
# WantedBy=multi-user.target
# EOF
  • 注意node-labels=node.kubernetes.io/node='' ubuntu为'' centos为空

命令解析:

  • –bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig:引导 kubeconfig 文件路径。用于 TLS Bootstrapping(节点自动加入集群的机制)。kubelet 使用此文件向 API Server 请求证书,首次启动时使用
  • –kubeconfig=/etc/kubernetes/kubelet.kubeconfig:最终的 kubeconfig 文件路径。引导成功后,kubelet 会切换到这个文件,用于与 Kubernetes API Server 通信。
  • –config=/etc/kubernetes/kubelet-conf.yml:kubelet 的 YAML 配置文件路径(包含详细配置,如 cgroup 驱动、Pod 限制等)。这个文件在稍后定义。
  • –container-runtime-endpoint=unix:///run/cri-dockerd.sock:容器运行时端点。指定使用 cri-dockerd 的 Unix socket(因为使用 Docker + cri-dockerd 作为 CRI 兼容运行时,而不是直接用 containerd 或 CRI-O)。
  • –node-labels=node.kubernetes.io/node=:节点标签。为空值(可以自定义标签,如用于节点分组)。这里的 = 后无内容,表示一个空标签

2.2 编辑 kubelet-conf.yml

官方文档:https://kubernetes.io/zh-cn/docs/reference/config-api/kubelet-config.v1beta1/

Master01

cat > /etc/kubernetes/kubelet-conf.yml <<EOF
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: 0.0.0.0
port: 10250
readOnlyPort: 10255
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 2m0s
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.pem
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 5m0s
    cacheUnauthorizedTTL: 30s
cgroupDriver: systemd
cgroupsPerQOS: true
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
containerLogMaxFiles: 5
containerLogMaxSize: 10Mi
contentType: application/vnd.kubernetes.protobuf
cpuCFSQuota: true
cpuManagerPolicy: none
cpuManagerReconcilePeriod: 10s
enableControllerAttachDetach: true
enableDebuggingHandlers: true
enforceNodeAllocatable:
- pods
eventBurst: 10
eventRecordQPS: 5
evictionHard:
  imagefs.available: 15%
  memory.available: 100Mi
  nodefs.available: 10%
  nodefs.inodesFree: 5%
evictionPressureTransitionPeriod: 5m0s
failSwapOn: true
fileCheckFrequency: 20s
hairpinMode: promiscuous-bridge
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 20s
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80
imageMinimumGCAge: 2m0s
iptablesDropBit: 15
iptablesMasqueradeBit: 14
kubeAPIBurst: 10
kubeAPIQPS: 5
makeIPTablesUtilChains: true
maxOpenFiles: 1000000
maxPods: 110
nodeStatusUpdateFrequency: 10s
oomScoreAdj: -999
podPidsLimit: -1
registryBurst: 10
registryPullQPS: 5
resolvConf: /etc/resolv.conf
rotateCertificates: true
runtimeRequestTimeout: 2m0s
serializeImagePulls: true
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 4h0m0s
syncFrequency: 1m0s
volumeStatsAggPeriod: 1m0s
EOF

命令解析:

创建 Kubernetes 的 kubelet 配置文件 /etc/kubernetes/kubelet-conf.yml。该文件是 kubelet 的主要配置文件,定义了 kubelet 的运行行为、资源管理、网络配置等关键参数。kubelet 是 Kubernetes 节点上的核心组件,负责管理 Pod、容器生命周期、节点资源等。

apiVersion: 指定配置文件使用的 Kubernetes API 版本。这里是 kubelet.config.k8s.io/v1beta1,表示 kubelet 配置的 v1beta1 版本。

kind: 指定配置类型为 KubeletConfiguration,这是 kubelet 的专用配置类型。

address: kubelet 的监听地址。0.0.0.0 表示监听所有网络接口,允许外部访问(如 API Server 或 kubectl)。

port: kubelet 的主端口,用于处理 HTTPS 请求(如状态查询、命令执行)。默认值为 10250。

readOnlyPort: 只读端口,提供无认证的只读访问(如健康检查或状态查询)。默认值为 10255。注意:在高安全性环境中,建议禁用只读端口(设置为 0)以防止未经授权的访问。

authentication: 定义 kubelet 的认证机制。

  • anonymous.enabled: 是否允许匿名访问。false 表示禁用匿名访问,增强安全性。
  • webhook.enabled: 是否启用 Webhook 认证。true 表示 kubelet 通过向 API Server 发送 Webhook 请求验证客户端身份。
  • webhook.cacheTTL: Webhook 认证结果的缓存时间,设置为 2m0s(2 分钟),减少重复验证开销。
  • x509.clientCAFile: 指定用于验证客户端证书的 CA 文件路径(/etc/kubernetes/pki/ca.pem)。客户端(如 API Server)必须提供由该 CA 签发的证书。

authorization: 定义 kubelet 的授权机制。

  • mode: Webhook: 使用 Webhook 模式向 API Server 请求授权决定(基于 RBAC 或其他授权策略)。
  • webhook.cacheAuthorizedTTL: 已授权请求的缓存时间,5m0s(5 分钟)。
  • webhook.cacheUnauthorizedTTL: 未授权请求的缓存时间,30s(30 秒)。较短的未授权缓存时间确保快速更新拒绝策略。

cgroupDriver: 指定 kubelet 使用的 cgroup 驱动程序。systemd 表示使用 systemd 管理 cgroup,与 Docker 的 systemd cgroup 驱动一致(配置了 Docker 的 daemon.json 使用 native.cgroupdriver=systemd)。

cgroupsPerQOS: 是否为不同 QoS(服务质量)级别的 Pod 创建单独的 cgroup。true 启用此功能,确保 Guaranteed、Burstable 和 BestEffort Pod 的资源隔离。

clusterDNS: 指定集群内 DNS 服务的 IP 地址。10.96.0.10 是 CoreDNS 的服务 IP(与安装 CoreDNS 时配置的 clusterIP 一致)。

clusterDomain: 指定集群的 DNS 域名后缀。cluster.local 是默认值,用于解析服务名称(如 my-service.default.svc.cluster.local)。

containerLogMaxFiles: 每个容器的最大日志文件数(轮转日志)。5 表示保留最多 5 个日志文件。

containerLogMaxSize: 每个日志文件的最大大小。10Mi 表示 10MB,超过后会触发日志轮转。

contentType: 指定 kubelet 与 API Server 通信时使用的内容类型。application/vnd.kubernetes.protobuf 表示使用 Protobuf 格式(比 JSON 更高效)。

cpuCFSQuota: 是否为容器启用 CPU CFS(Completely Fair Scheduler)配额。true 表示限制容器 CPU 使用量,基于 Pod 的 QoS 设置。

cpuManagerPolicy: CPU 分配策略。none 表示不使用 CPU 管理器(不分配专用 CPU 核心,Pod 共享 CPU 资源)。

cpuManagerReconcilePeriod: CPU 管理器的协调周期。10s 表示每 10 秒检查并调整 CPU 分配(尽管策略为 none,此参数仍需设置)。

enableControllerAttachDetach: 是否允许控制器管理卷的挂载和卸载。true 表示由 kubelet 和控制器协同处理卷操作。

enableDebuggingHandlers: 是否启用调试端点(如 /debug/pprof)。true 允许访问调试信息,便于性能分析。

enforceNodeAllocatable: 指定强制限制的资源类型。pods 表示对 Pod 分配的资源进行限制,确保节点保留系统和 kubelet 所需的资源(如内存、CPU)。

eventBurst: 事件处理的突发容量。10 表示允许短时间内处理最多 10 个事件。

eventRecordQPS: 每秒记录的事件数。5 表示平均每秒记录 5 个事件,控制事件报告频率以避免 API Server 过载。

evictionHard: 定义硬性驱逐阈值,当资源低于以下值时,kubelet 会驱逐 Pod 以回收资源:

  • imagefs.available: 镜像文件系统可用空间低于 15% 时触发驱逐。
  • memory.available: 可用内存低于 100Mi(100MB)时触发驱逐。
  • nodefs.available: 节点文件系统可用空间低于 10% 时触发驱逐。
  • nodefs.inodesFree: 节点文件系统可用 inode 低于 5% 时触发驱逐。

evictionPressureTransitionPeriod: 驱逐压力过渡期。5m0s 表示在资源压力缓解后,等待 5 分钟才停止驱逐。

failSwapOn: 是否允许在启用 swap 分区时启动 kubelet。true 表示如果 swap 启用,kubelet 将启动失败(当前已禁用 swap)。

fileCheckFrequency: 检查文件变更的频率。20s 表示每 20 秒检查一次(如 Pod 配置文件)。

hairpinMode: 指定 hairpin 流量(Pod 访问自身服务 IP)的处理方式。promiscuous-bridge 表示使用网桥的混杂模式,适用于 Calico 等 CNI 插件。

healthzBindAddress: 健康检查监听地址。127.0.0.1 表示仅本地可访问,增强安全性。

healthzPort: 健康检查端口。10248 是默认值,用于 /healthz 端点。

httpCheckFrequency: HTTP 健康检查频率。20s 表示每 20 秒检查一次。

imageGCHighThresholdPercent: 镜像垃圾回收的上限阈值。磁盘使用率达到 85% 时触发回收。

imageGCLowThresholdPercent: 镜像垃圾回收的下限阈值。回收后磁盘使用率低于 80% 时停止。

imageMinimumGCAge: 镜像的最小保留时间。2m0s 表示镜像至少保留 2 分钟,防止频繁回收。

iptablesDropBit: 用于标记丢弃数据包的 iptables 位。15 是默认值。

iptablesMasqueradeBit: 用于 SNAT(源地址转换)的 iptables 位。14 是默认值。

makeIPTablesUtilChains: 是否创建 iptables 工具链。true 表示 kubelet 自动管理 iptables 规则。

kubeAPIBurst: 与 API Server 通信的突发请求数。10 表示短时间内最多发送 10 个请求。

kubeAPIQPS: 与 API Server 通信的每秒请求数。5 表示平均每秒 5 个请求,控制负载。

maxOpenFiles: 最大打开文件数。1000000 允许 kubelet 处理大量文件描述符。

maxPods: 节点上最大 Pod 数。110 是默认值,限制节点负载。

podPidsLimit: 每个 Pod 的最大 PID 数。-1 表示无限制。

nodeStatusUpdateFrequency: 节点状态更新频率。10s 表示每 10 秒向 API Server 报告节点状态。

oomScoreAdj: kubelet 进程的 OOM(内存不足)优先级。-999 表示低优先级,降低被杀死概率。

registryBurst: 从镜像仓库拉取镜像的突发请求数。10 表示最多 10 个并发请求。

registryPullQPS: 镜像拉取的每秒请求数。5 表示平均每秒 5 个请求。

resolvConf: DNS 解析配置文件路径。/etc/resolv.conf 是系统默认值。

rotateCertificates: 是否启用证书轮换。true 表示自动更新过期证书。

runtimeRequestTimeout: 容器运行时请求超时时间。2m0s 表示 2 分钟。

serializeImagePulls: 是否串行拉取镜像。true 表示一次拉取一个镜像,降低资源争用。

staticPodPath: 静态 Pod 的清单目录。/etc/kubernetes/manifests 用于存放静态 Pod 的 YAML 文件。

streamingConnectionIdleTimeout: 流式连接(如 kubectl exec)的空闲超时。4h0m0s 表示 4 小时。

syncFrequency: Pod 同步频率。1m0s 表示每分钟同步一次 Pod 状态。

volumeStatsAggPeriod: 卷统计聚合周期。1m0s 表示每分钟收集一次卷使用数据。

2.3 同步文件到其它节点

user=root
host=(k8s-master02 k8s-master03 k8s-node01 k8s-node02)

for i in ${host[@]}; do
  echo $i
  rsync --rsync-path="sudo rsync" /usr/lib/systemd/system/kubelet.service $user@$i:/usr/lib/systemd/system/;
  rsync --rsync-path="sudo rsync" /etc/kubernetes/kubelet-conf.yml $user@$i:/etc/kubernetes/;
done

2.4 所有节点启动 kubelet

user=root
host=(k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02)

for i in ${host[@]}; do
  ssh $user@$i "systemctl daemon-reload";
  ssh $user@$i "systemctl enable --now kubelet";
  ssh $user@$i "systemctl status kubelet";
done

2.5 测试

[root@k8s-master01 ~]$ kubectl get nodes
NAME           STATUS     ROLES    AGE     VERSION
k8s-master01   NotReady   <none>   20m     v1.29.2
k8s-master02   NotReady   <none>   13m     v1.29.2
k8s-master03   NotReady   <none>   13m     v1.29.2
k8s-node01     NotReady   <none>   7m53s   v1.29.2
k8s-node02     NotReady   <none>   7m52s   v1.29.2

3. kube-proxy 配置(所有节点)

3.1 同步 kube-proxy.kubeconfig

master01

将 kube-proxy.kube.config 文件同步到其它所有节点

user=root
host=(k8s-master02 k8s-master03 k8s-node01 k8s-node02)

for i in ${host[@]}; do
  echo $i
  rsync --rsync-path="sudo rsync" /etc/kubernetes/kube-proxy.kubeconfig $user@$i:/etc/kubernetes/;
done

3.2 编辑 kube-proxy.service

Master01

cat > /usr/lib/systemd/system/kube-proxy.service << EOF
[Unit]
Description=Kubernetes Kube Proxy
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
ExecStart=/usr/local/bin/kube-proxy \\
  --config=/etc/kubernetes/kube-proxy.yaml \\
  --cluster-cidr=172.16.0.0/12,fc00:2222::/112 \\
  --v=2
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target

EOF

命令解析:

kube-proxy是Kubernetes的核心组件之一,负责处理Service和Pod的网络代理(如负载均衡、NAT等),确保集群内的网络通信正常工作。

适用节点:所有Kubernetes节点(Master和Node),因为kube-proxy需要在每个节点上运行,以处理本地Pod的网络流量。

  • ExecStart=/usr/local/bin/kube-proxy

    执行命令:指定服务的启动命令,即运行/usr/local/bin/kube-proxy二进制文件

  • –config=/etc/kubernetes/kube-proxy.yaml

    配置参数:指定kube-proxy的配置文件路径。这个YAML文件(教程中稍后定义)包含详细配置,如代理模式(IPVS)、绑定地址、连接跟踪等。使用配置文件可以分离配置,便于维护和统一所有节点。

  • –cluster-cidr=172.16.0.0/12,fc00:2222::/112

    172.16.0.0/12:IPv4 Pod CIDR(私有地址段,范围从172.16.0.0到172.31.255.255,可容纳大量Pod)。

    fc00:2222::/112:IPv6 Pod CIDR(ULA私有地址,支持双栈网络)。

3.3 编辑 kube-proxy.yaml

官方文档:https://kubernetes.io/zh-cn/docs/reference/config-api/kube-proxy-config.v1alpha1/

cat > /etc/kubernetes/kube-proxy.yaml << EOF
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
clientConnection:
  acceptContentTypes: ""
  burst: 10
  contentType: application/vnd.kubernetes.protobuf
  kubeconfig: /etc/kubernetes/kube-proxy.kubeconfig
  qps: 5
clusterCIDR: 172.16.0.0/12,fc00:2222::/112
configSyncPeriod: 15m0s
conntrack:
  max: null
  maxPerCore: 32768
  min: 131072
  tcpCloseWaitTimeout: 1h0m0s
  tcpEstablishedTimeout: 24h0m0s
enableProfiling: false
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: ""
iptables:
  masqueradeAll: false
  masqueradeBit: 14
  minSyncPeriod: 0s
  syncPeriod: 30s
ipvs:
  masqueradeAll: true
  minSyncPeriod: 5s
  scheduler: "rr"
  syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: "ipvs"
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
udpIdleTimeout: 250ms
EOF

命令解析:

kube-proxy 的配置文件,定义了 Kubernetes 集群中 kube-proxy 组件的行为。kube-proxy 运行在每个节点上,负责处理 Service 和 Pod 的网络代理(如负载均衡、NAT),支持 Kubernetes 的网络功能。

  • apiVersion: kubeproxy.config.k8s.io/v1alpha1

    指定配置文件的 API 版本,告诉 Kubernetes 使用 kubeproxy.config.k8s.io/v1alpha1 版本的 KubeProxyConfiguration 资源格式来解析此文件

  • bindAddress: 0.0.0.0

    监听所有网络接口(IPv4 和 IPv6),允许 kube-proxy 接受来自任何网络接口的请求。0.0.0.0 确保 kube-proxy 能处理两种协议的流量

  • clientConnection: 定义 kube-proxy 与 Kubernetes API 服务器的通信参数

    • acceptContentTypes: “” :指定 kube-proxy 接受的 API 响应内容类型,空字符串表示接受默认类型(通常由 contentType 定义)
    • burst: 10 允许的突发请求数(QPS 的上限),控制 kube-proxy 向 API 服务器发送请求的速率
    • contentType: application/vnd.kubernetes.protobuf 与 API 服务器通信时使用的内容类型,比 JSON(application/json)更高效,减少网络带宽
    • kubeconfig: /etc/kubernetes/kube-proxy.kubeconfig 访问 API 服务器的 kubeconfig 文件路径
    • qps: 5 每秒向 API 服务器发送的请求数,限制 kube-proxy 的 API 请求速率,qps: 5 表示每秒最多 5 个请求,配合 burst 控制突发
  • clusterCIDR: 172.16.0.0/12,fc00:2222::/112 定义集群的 Pod IP 地址范围(Pod CIDR),与 Calico 的 CALICO_IPV4POOL_CIDR 和 CALICO_IPV6POOL_CIDR 一致(calico.yaml 或 calico-ipv6.yaml)

  • configSyncPeriod: 15m0s kube-proxy 同步配置的间隔时间,每 15 分钟,kube-proxy 从 API 服务器重新获取 Service 和 Endpoint 配置,更新本地代理规则

  • conntrack: 配置连接跟踪(Connection Tracking),用于 NAT 和负载均衡

    • max: null 系统中连接跟踪表的最大条目数,null 表示使用系统默认值(通常由内核参数 net.nf_conntrack_max 决定)
    • maxPerCore: 32768 每个 CPU 核心的连接跟踪条目上限
    • min: 131072 连接跟踪表的最小条目数
    • tcpCloseWaitTimeout: 1h0m0s TCP 连接在 CLOSE_WAIT 状态的超时时间,连接关闭后,等待 1 小时释放资源,防止短时间内频繁释放导致性能问题
    • tcpEstablishedTimeout: 24h0m0s 已建立的 TCP 连接的超时时间, 保持长连接(如数据库连接)24 小时,减少重新建立开销
  • enableProfiling: false 是否启用 kube-proxy 的性能分析(profiling),false 禁用 profiling,降低资源消耗

  • healthzBindAddress: 0.0.0.0:10256 健康检查监听地址和端口,kube-proxy 在 0.0.0.0:10256 提供 /healthz 端点,供 kubelet 或外部工具检查状态

  • hostnameOverride: “” 覆盖 kube-proxy 报告的节点主机名,空字符串表示使用节点默认主机名(由 uname -n 或 kubelet 提供)

  • iptables: 配置 iptables 代理模式(尽管当前使用 IPVS,此配置仍保留)

    • masqueradeAll: false 是否对所有出站流量执行 NAT 伪装(SNAT),false 表示仅对 Service 相关的流量执行 NAT,减少开销
    • masqueradeBit: 14 iptables 伪装时使用的标记位,默认值 14 用于标记 NAT 流量,防止冲突
    • minSyncPeriod: 0s iptables 规则同步的最小间隔,0s 表示无最小间隔,允许快速同步
    • syncPeriod: 30s iptables 规则同步的周期,每 30 秒同步 iptables 规则,确保与 API 服务器一致
  • ipvs: 配置 IPVS 代理模式(本文明确使用 mode: “ipvs”)

    • masqueradeAll: true 是否对所有出站流量执行 NAT 伪装。true 表示对所有 Pod 流量执行 SNAT,适合 IPVS 模式,确保外部访问(如 NodePort)正确。
    • minSyncPeriod: 5s IPVS 规则同步的最小间隔。确保至少 5 秒同步一次,防止频繁更新导致性能问题。
    • scheduler: “rr” IPVS 负载均衡调度算法。rr(Round Robin,轮询)平均分配 Service 流量到后端 Pod
    • syncPeriod: 30s IPVS 规则同步周期。每 30 秒同步 IPVS 规则,确保与 API 服务器一致。
  • kind: KubeProxyConfiguration 指定资源的类型,声明这是 kube-proxy 的配置文件

  • metricsBindAddress: 127.0.0.1:10249 指标(metrics)监听地址和端口,kube-proxy 在 127.0.0.1:10249 提供 Prometheus 格式的指标,供监控系统(如 Prometheus)采集

  • mode: “ipvs” kube-proxy 的代理模式, ipvs 模式使用 Linux IPVS(IP Virtual Server)实现高效负载均衡。

  • nodePortAddresses: null NodePort 服务的监听地址。null 表示监听所有接口(0.0.0.0)

  • oomScoreAdj: -999 调整 kube-proxy 进程的 OOM(Out of Memory)优先级,-999 降低被 OOM Killer 杀死的概率,优先级接近系统关键进程。

  • portRange: “” 分配 NodePort 的端口范围。空字符串使用默认范围(30000-32767)。

  • udpIdleTimeout: 250ms UDP 连接的空闲超时时间。250 毫秒后关闭空闲 UDP 连接,释放资源。

3.4 启动 kube-proxy

systemctl daemon-reload

systemctl enable --now kube-proxy.service

systemctl status kube-proxy.service

3.5 同步 kube-proxy 到其它节点

user=root
host=(k8s-master02 k8s-master03 k8s-node01 k8s-node02)

for i in ${host[@]}; do
  echo $i
  rsync --rsync-path="sudo rsync" /usr/lib/systemd/system/kube-proxy.service $user@$i:/usr/lib/systemd/system/;
  rsync --rsync-path="sudo rsync" /etc/kubernetes/kube-proxy.yaml $user@$i:/etc/kubernetes/;
  ssh $user@$i "systemctl daemon-reload";
  ssh $user@$i "systemctl enable --now kube-proxy.service";
  ssh $user@$i "systemctl status kube-proxy.service";
done

八、安装 calico 网络插件

1. 下载 calico 资源清单

Master01

# 创建calico目录,进入目录
$ mkdir -p /etc/calico/
$ cd /etc/calico/

# 下载calico资源清单
wget https://raw.githubusercontent.com/projectcalico/calico/refs/tags/v3.28.0/manifests/calico-typha.yaml

2. 编辑 calico 资源清单-IPV4

Master01

# 复制资源清单,用于IPV4网络
$ cp /etc/calico/calico-typha.yaml /etc/calico/calico.yaml


# IPV4环境
vim calico.yaml
# calico-config ConfigMap处
"ipam": {
    "type": "calico-ipam",
},
- name: IP
  value: "autodetect"

- name: CALICO_IPV4POOL_CIDR
  value: "172.16.0.0/12"

3. 编辑 calico 资源清单-IPV6

Master01

# 复制资源清单,用于IPV4网络
$ cp /etc/calico/calico-typha.yaml /etc/calico/calico-ipv6.yaml


# IPV6环境
vim calico-ipv6.yaml
# calico-config ConfigMap处
    "ipam": {
        "type": "calico-ipam",
        "assign_ipv4": "true",
        "assign_ipv6": "true"
    },
    - name: IP
      value: "autodetect"

    - name: IP6
      value: "autodetect"

    - name: CALICO_IPV4POOL_CIDR
      value: "172.16.0.0/12"

    - name: CALICO_IPV6POOL_CIDR
      value: "fc00:2222::/112"

    - name: FELIX_IPV6SUPPORT
      value: "true"

4. 执行 calico 资源清单

# 本地没有公网 IPv6 使用 calico.yaml(在master01节点执行即可)
kubectl apply -f calico.yaml

# 本地有公网 IPv6 使用 calico-ipv6.yaml 
# kubectl apply -f calico-ipv6.yaml 

# 查看结果:
[root@k8s-master01 ~]$ kubectl get pods -n kube-system -o wide
NAME                                      READY   STATUS    RESTARTS        AGE     IP              NODE           NOMINATED NODE   READINESS GATES
calico-kube-controllers-8d76c5f9b-485zp   1/1     Running   2 (3m35s ago)   5m31s   172.27.14.192   k8s-node02     <none>           <none>
calico-node-d6plz                         1/1     Running   1 (3m52s ago)   5m31s   10.20.1.104     k8s-node01     <none>           <none>
calico-node-w6fvl                         1/1     Running   0               5m32s   10.20.1.103     k8s-master03   <none>           <none>
calico-node-z7zs2                         1/1     Running   0               5m32s   10.20.1.101     k8s-master01   <none>           <none>
calico-node-z8v77                         1/1     Running   1 (3m43s ago)   5m32s   10.20.1.102     k8s-master02   <none>           <none>
calico-node-zb9bv                         1/1     Running   1 (4m14s ago)   5m31s   10.20.1.105     k8s-node02     <none>           <none>
calico-typha-7cdb5b98f7-d47gx             1/1     Running   0               5m31s   10.20.1.104     k8s-node01     <none>           <none>

注意:执行资源清单会自动下载 calico 需要的镜像,这些镜像基本都在国外,下载会很慢,甚至下载不下来。

解决办法1:使用国内仓库

# 若docker镜像拉不下来,可以使用国内的仓库
sed -i "s#docker.io/calico/#m.daocloud.io/docker.io/calico/#g" calico.yaml 
sed -i "s#docker.io/calico/#m.daocloud.io/docker.io/calico/#g" calico-ipv6.yaml

sed -i "s#m.daocloud.io/docker.io/calico/#docker.io/calico/#g" calico.yaml 
sed -i "s#m.daocloud.io/docker.io/calico/#docker.io/calico/#g" calico-ipv6.yaml

解决方法2:使用代理下载

参考:https://georgechan95.github.io/blog/b01d5c62.html

# 下载镜像
docker pull docker.io/calico/cni:v3.28.0
docker pull docker.io/calico/node:v3.28.0
docker pull docker.io/calico/kube-controllers:v3.28.0
docker pull docker.io/calico/typha:v3.28.0

# 打包
docker save docker.io/calico/cni:v3.28.0 -o cni-v3.28.0.tar
docker save docker.io/calico/node:v3.28.0 -o node-v3.28.0.tar
docker save docker.io/calico/kube-controllers:v3.28.0 -o kube-controllers-v3.28.0.tar
docker save docker.io/calico/typha:v3.28.0 -o typha-v3.28.0.tar

# 同步到其它节点
user=root
host=(k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02)

for i in ${host[@]}; do
  echo $i
  rsync -av /opt/software/hak8s/images root@$i:/opt/software/
  ssh $user@$i "docker load -i /opt/software/images/calico/cni-v3.28.0.tar";
  ssh $user@$i "docker load -i /opt/software/images/calico/node-v3.28.0.tar";
  ssh $user@$i "docker load -i /opt/software/images/calico/kube-controllers-v3.28.0.tar";
  ssh $user@$i "docker load -i /opt/software/images/calico/typha-v3.28.0.tar";
done

九、安装 Core DNS

Helm安装脚本: https://github.com/helm/helm/blob/main/scripts/get-helm-3

1. 安装Helm(仅master01)

参考:https://georgechan95.github.io/blog/d8e3c7b3.html

# 下载 helm 二进制包
$ wget https://get.helm.sh/helm-v3.14.3-linux-amd64.tar.gz

# 解压
$ tar -zxvf helm-v3.14.3-linux-amd64.tar.gz

# 安装到bin目录
$ mv linux-amd64/helm /usr/local/bin/helm

# 查看helm版本
$ helm version
version.BuildInfo{Version:"v3.14.3", GitCommit:"f03cc04caaa8f6d7c3e67cf918929150cf6f3f12", GitTreeState:"clean", GoVersion:"go1.21.7"}

2. 安装 CoreDns

2.1 下载 CoreDns 安装包

# 添加 coredns 仓库
helm repo add coredns https://coredns.github.io/helm
# 查看仓库 coredns 版本
helm search repo coredns --versions
# 拉取指定版本的 coredns
helm pull coredns/coredns --version 1.45.0
# 解压
tar -zxvf /opt/software/coredns-1.45.0.tgz -C /opt/module/

2.2 编辑配置文件

# 修改IP
$ cat values.yaml | grep clusterIP: -A 13
  clusterIP: "10.96.0.10"
# clusterIPs: []
# loadBalancerIP: ""
# loadBalancerClass: ""
# externalIPs: []
# externalTrafficPolicy: ""
# ipFamilyPolicy: ""
# trafficDistribution: PreferClose
  # The name of the Service
  # If not set, a name is generated using the fullname template
  name: ""
  annotations: {}
  # Pod selector
  selector: {}

2.3 安装 CoreDns

master01

$ helm install coredns /opt/module/coredns/ -n kube-system
NAME: coredns
LAST DEPLOYED: Fri Oct 24 13:47:48 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CoreDNS is now running in the cluster as a cluster-service.

It can be tested with the following:

1. Launch a Pod with DNS tools:

kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools

2. Query the DNS server:

/ # host kubernetes

注意事项:

安装 coredns 会自动从外网下载镜像,这里可能会失败。

方式一:

# 修改为国内源 docker源可选
sed -i "s#coredns/#m.daocloud.io/docker.io/coredns/#g" values.yaml
sed -i "s#registry.k8s.io/#m.daocloud.io/registry.k8s.io/#g" values.yaml

方式二:代理服务器下载

# 下载
docker pull coredns/coredns:1.13.1
docker pull registry.k8s.io/cpa/cluster-proportional-autoscaler:v1.9.0
# 打包
docker save coredns/coredns:1.13.1 -o coredns-1.13.1.tar
docker save registry.k8s.io/cpa/cluster-proportional-autoscaler:v1.9.0 -o cluster-proportional-autoscaler-v1.9.0.tar

# 发送到集群解压
user=root
host=(k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02)

for i in ${host[@]}; do
  echo $i
  rsync -av /opt/software/hak8s/images root@$i:/opt/software/
  ssh $user@$i "docker load -i /opt/software/images/coredns/coredns-1.13.1.tar";
  ssh $user@$i "docker load -i /opt/software/images/coredns/cluster-proportional-autoscaler-v1.9.0.tar";
done

验证 CoreDns

$ kubectl get pods -n kube-system -o wide | grep coredns
coredns-68746bb699-4mrxl                  1/1     Running   0             11m   172.25.244.193   k8s-master01   <none>           <none>

$ kubectl get nodes
NAME           STATUS   ROLES    AGE   VERSION
k8s-master01   Ready    <none>   41h   v1.29.2
k8s-master02   Ready    <none>   41h   v1.29.2
k8s-master03   Ready    <none>   41h   v1.29.2
k8s-node01     Ready    <none>   41h   v1.29.2
k8s-node02     Ready    <none>   41h   v1.29.2

十、安装 Metrics Server

官方文档:https://github.com/kubernetes-sigs/metrics-server/releases/tag/v0.7.1

1. 下载 metrics-server 资源清单

master01

# 创建文件夹
mkdir -p /opt/module/metrics-server
cd /opt/module/metrics-server

# 下载资源清单
wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.7.1/components.yaml

2. 编辑 components.yaml

# 修改配置
vim components.yaml

---
# 1
			- args:
        - --cert-dir=/tmp
        - --secure-port=10250
        - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
        - --kubelet-use-node-status-port
        - --metric-resolution=15s
        - --kubelet-insecure-tls
        - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem
        - --requestheader-username-headers=X-Remote-User
        - --requestheader-group-headers=X-Remote-Group
        - --requestheader-extra-headers-prefix=X-Remote-Extra-


# 2
        volumeMounts:
        - mountPath: /tmp
          name: tmp-dir
        - name: ca-ssl
          mountPath: /etc/kubernetes/pki

# 3
      volumes:
      - emptyDir: {}
        name: tmp-dir
      - name: ca-ssl
        hostPath:
          path: /etc/kubernetes/pki
---

3. 部署 metrics-server

$ kubectl apply -f /opt/module/metrics-server/components.yaml

注意事项:

安装 metrics-server 会自动从外网下载镜像,这里可能会失败。

方式一:

# 修改为国内源 docker源可选
sed -i "s#registry.k8s.io/#m.daocloud.io/registry.k8s.io/#g" *.yaml

方式二:代理服务器下载

# 下载
docker pull registry.k8s.io/metrics-server/metrics-server:v0.7.1

# 打包
docker save registry.k8s.io/metrics-server/metrics-server:v0.7.1 -o metrics-server-v0.7.1.tar

# 发送到集群解压
user=root
host=(k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02)

for i in ${host[@]}; do
  echo $i
  rsync -av /opt/software/hak8s/images root@$i:/opt/software/
  ssh $user@$i "docker load -i /opt/software/images/metrics-server/metrics-server-v0.7.1.tar";
done

验证部署

$ kubectl get pods -n kube-system | grep metrics-server
metrics-server-6d4cb7955c-688pj           1/1     Running   0             2m10s

$ kubectl  top node
NAME           CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
k8s-master01   77m          3%     2168Mi          61%       
k8s-master02   101m         5%     1499Mi          42%       
k8s-master03   114m         5%     1594Mi          44%       
k8s-node01     46m          2%     960Mi           27%       
k8s-node02     48m          2%     924Mi           26%

十一、安装Dashboard

仅在 master01 操作

1. 下载并执行 dashboard 资源清单

# 下载资源清单
wget https://raw.githubusercontent.com/kubernetes/dashboard/refs/tags/v2.7.0/aio/deploy/recommended.yaml


# 修改镜像拉取策略
$ vim /opt/module/dashboard/recommended.yaml
imagePullPolicy: IfNotPresent

# 执行资源清单
$ kubectl apply -f /opt/module/dashboard/recommended.yaml

# 查看 Pod
$ kubectl get pod -n kubernetes-dashboard -o wide
NAME                                         READY   STATUS    RESTARTS   AGE   IP             NODE           NOMINATED NODE   READINESS GATES
dashboard-metrics-scraper-5657497c4c-v5jlt   1/1     Running   0          36s   172.18.195.2   k8s-master03   <none>           <none>
kubernetes-dashboard-5b749d9495-ns6q2        1/1     Running   0          43s   172.25.92.71   k8s-master02   <none>           <none>

命令解析:

需提前下载好镜像文件

docker pull kubernetesui/dashboard:v2.7.0
docker pull kubernetesui/metrics-scraper:v1.0.8

docker save kubernetesui/dashboard:v2.7.0 -o dashboard-v2.7.0.tar
docker save kubernetesui/metrics-scraper:v1.0.8 -o metrics-scraper-v1.0.8.tar

user=root
host=(k8s-master01 k8s-master02 k8s-master03 k8s-node01 k8s-node02)

for i in ${host[@]}; do
  echo $i
  rsync -av /opt/software/hak8s/images root@$i:/opt/software/
  ssh $user@$i "docker load -i /opt/software/images/dashboard/dashboard-v2.7.0.tar";
  ssh $user@$i "docker load -i /opt/software/images/dashboard/metrics-scraper-v1.0.8.tar";
done

2. 创建 ServiceAccount

mkdir -p /opt/module/dashboard/

# 创建 ServiceAccount
cat > /opt/module/dashboard/dashboard-user.yaml << EOF
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
EOF

# 执行资源清单,创建SA
kubectl apply -f /opt/module/dashboard/dashboard-user.yaml

# 验证
$ kubectl get serviceaccount -A | grep admin-user
kubernetes-dashboard   admin-user                             0         57s

命令解析:

在 Kubernetes 集群中为 Dashboard 创建一个具有管理员权限的 ServiceAccount(服务账户),并通过 ClusterRoleBinding(集群角色绑定)授予其全集群管理权限(cluster-admin)。这是为了方便通过 Token 方式登录 Dashboard 进行集群管理,而非使用默认的有限权限账户。整个过程是标准的 Kubernetes RBAC(基于角色的访问控制)配置,适用于二进制部署的集群。

3. 更改 dashboard 的 svc 为 NodePort

$ kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard

...
  sessionAffinity: None
  type: NodePort # 将Cluster为NodePort
status:
  loadBalancer: {}
  

# 保存后,验证
$ kubectl get svc kubernetes-dashboard -n kubernetes-dashboard -o wide
NAME                   TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE   SELECTOR
kubernetes-dashboard   NodePort   10.101.243.82   <none>        443:30892/TCP   91s   k8s-app=kubernetes-dashboard

4. 创建 token 访问

$ kubectl -n kubernetes-dashboard create token admin-user
eyJhbGciOiJSUzI1NiIsImtpZCI6IjVRcWNxMUt1ZzZsN3RoeTBHUzd4VzFGbktIZDBIZUNJazllQVFqdy1aSTgifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzYxNjU5MTM1LCJpYXQiOjE3NjE2NTU1MzUsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJhZG1pbi11c2VyIiwidWlkIjoiY2ZiNTA5MmYtMGY0My00OTllLTg1MzItOTM0YTljMDY0YTJmIn19LCJuYmYiOjE3NjE2NTU1MzUsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlcm5ldGVzLWRhc2hib2FyZDphZG1pbi11c2VyIn0.oHvY4JRIRoNXr_DK2wnIWEo-yQ6KLAXL4kuAxVfY-BfU6o_kI90MzG1N9t8t4HAplzXQcg_v-m4RsMyu63dy96h49WVj6Zp6WbmI2bUmeg7VgBmm43BReO-lKPdWfqUxEJ2Yt2fLSl4CbNuLyKgNxOUtJT_RDz1l_OBgFx23uNs7QrJ42miuJnkgDSXnS0_2VCyLbGYALSgWSZKXeWdjQUL-k40GghOj5I2-HEGld0j9fcl0ASiFEOuNWstP0PKZoO1qPJNrsVtqHM5PiNbhHzU6b60-sJO9keJY8K0ORdNjbTt857I3NIiAklBXsJY4dT8BOaesefa7TvBMHSFgNQ

复制生成的token: eyJhbGciOiJSU…..

浏览器访问宿主机地址:https://10.20.1.101:30892/#/login

登录 DashBoard

查看Pod

十二、安装命令补全

所有节点

yum install bash-completion -y
source /usr/share/bash-completion/bash_completion
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc

十三、高可用验证

1. 组件状态验证

验证ETCD状态

# 确认 etcd 高可用
$ etcdctl --endpoints="10.20.1.101:2379,10.20.1.102:2379,10.20.1.103:2379" --cacert=/etc/kubernetes/pki/etcd/etcd-ca.pem --cert=/etc/kubernetes/pki/etcd/etcd.pem --key=/etc/kubernetes/pki/etcd/etcd-key.pem  endpoint status --write-out=table

+------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|     ENDPOINT     |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| 10.20.1.101:2379 | adb2616df23a8bc4 |  3.5.16 |  7.0 MB |     false |      false |        39 |    1893807 |            1893807 |        |
| 10.20.1.102:2379 | 4039909ce7f85a53 |  3.5.16 |  7.0 MB |     false |      false |        39 |    1893807 |            1893807 |        |
| 10.20.1.103:2379 | eee0233fd4f83d74 |  3.5.16 |  6.7 MB |      true |      false |        39 |    1893807 |            1893807 |        |
+------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

集群健康状态

[root@k8s-master01 ~]$ kubectl get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE   ERROR
scheduler            Healthy   ok        
controller-manager   Healthy   ok        
etcd-0               Healthy   ok


[root@k8s-master01 ~]$ kubectl get nodes
NAME           STATUS   ROLES    AGE   VERSION
k8s-master01   Ready    <none>   6d    v1.29.2
k8s-master02   Ready    <none>   6d    v1.29.2
k8s-master03   Ready    <none>   6d    v1.29.2
k8s-node01     Ready    <none>   6d    v1.29.2
k8s-node02     Ready    <none>   6d    v1.29.2

2. 使用 dnstools 测试

$ kubectl run -it --rm --restart=Never --image=infoblox/dnstools:v3 dnstools

dnstools# host kubernetes
kubernetes.default.svc.cluster.local has address 10.96.0.1

dnstools# dig kubernetes.default.svc.cluster.local
; <<>> DiG 9.11.3 <<>> kubernetes.default.svc.cluster.local
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45690
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 39b672283592d58b (echoed)
;; QUESTION SECTION:
;kubernetes.default.svc.cluster.local. IN A

;; ANSWER SECTION:
kubernetes.default.svc.cluster.local. 20 IN A	10.96.0.1

;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Thu Oct 30 06:56:34 UTC 2025
;; MSG SIZE  rcvd: 129

3. Nginx 部署测试

资源清单: nginx-test.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: nginx
          image: nginx:1.29.3
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80

---

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: default
spec:
  ports:
    - port: 80
      targetPort: 80    # 修正:必须是 Pod 实际监听的端口号
      protocol: TCP
      nodePort: 30339
  selector:
    app: myapp

执行资源清单:

# 执行资源清单
$ kubectl apply -f nginx-test.yaml

# 查看 Service
$ kubectl get svc 
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        8d
nginx-svc    NodePort    10.98.200.91   <none>        80:30339/TCP   3h21m

# 查看 Deployment
$ kubectl get deploy -o wide
NAME    READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES         SELECTOR
nginx   3/3     3            3           3h21m   nginx        nginx:1.29.3   app=myapp

# 查看Pod
$ kubectl get pods -o wide
NAME                    READY   STATUS    RESTARTS      AGE     IP               NODE           NOMINATED NODE   READINESS GATES
busybox                 1/1     Running   3 (17m ago)   3h18m   172.25.92.67     k8s-master02   <none>           <none>
nginx-6594975dd-fds78   1/1     Running   0             3h21m   172.18.195.2     k8s-master03   <none>           <none>
nginx-6594975dd-pvkt8   1/1     Running   0             3h21m   172.27.14.193    k8s-node02     <none>           <none>
nginx-6594975dd-v96mx   1/1     Running   0             3h21m   172.25.244.199   k8s-master01   <none>           <none>
ubuntu-test             1/1     Running   0             8m54s   172.25.244.204   k8s-master01   <none>           <none>

网络测试

$ kubectl exec -it nginx-6594975dd-fds78 -- /bin/bash
root@nginx-6594975dd-fds78:/# curl -k https://10.96.0.1:443
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
  "reason": "Forbidden",
  "details": {},
  "code": 403
}

说明:

  • Pod 网络到 Kubernetes API Server 是通的。
  • 返回 403 而不是超时或连接失败,说明请求已经到达 API Server。
  • 403 的原因是你没有提供认证信息(anonymous user),这是正常的,如果只是测试网络,不需要担心。

4. Ubuntu 镜像部署测试

3.1 部署 NetworkPolicy

默认 Kubernates 集群 Pod 不能直连外网,需要网络策略允许,这里部署一个 NetworkPolicy 让Pod 可以访问外网

网络策略资源清单: NetworkPolicy.yaml

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-coredns-egress
  namespace: kube-system
spec:
  podSelector:
    matchLabels:
      k8s-app: coredns
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0  # 外部DNS
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53
  - to:
    - ipBlock:
        cidr: 10.96.0.0/12  # 服务CIDR (API ClusterIP)
    ports:
    - protocol: TCP
      port: 443
  - to:
    - ipBlock:
        cidr: 10.20.1.0/24  # Master节点IP范围 (API后端端口)
    ports:
    - protocol: TCP
      port: 8443

执行资源清单

$ kubectl apply -f NetworkPolicy.yaml

3.2 部署 Ubuntu Pod

# 运行 Ubuntu Pod
$ kubectl run -it --rm --restart=Never --image=ubuntu:22.04 ubuntu-test -- bash

# 替换阿里云源
cat > /etc/apt/sources.list << "EOF"
deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
EOF

# 下载工具
apt update -y
apt install -y iputils-ping dnsutils curl netcat

测试网络1

root@ubuntu-test:/# nslookup kubernetes.default.svc.cluster.local
;; Got recursion not available from 10.96.0.10
;; Got recursion not available from 10.96.0.10
;; Got recursion not available from 10.96.0.10
;; Got recursion not available from 10.96.0.10
Server:		10.96.0.10
Address:	10.96.0.10#53

Name:	kubernetes.default.svc.cluster.local
Address: 10.96.0.1
;; Got recursion not available from 10.96.0.10

结论:DNS 解析成功

测试网络2

root@ubuntu-test:/# dig kubernetes.default.svc.cluster.local

; <<>> DiG 9.18.39-0ubuntu0.22.04.2-Ubuntu <<>> kubernetes.default.svc.cluster.local
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31061
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: 3bdf10d9d00e32a3 (echoed)
;; QUESTION SECTION:
;kubernetes.default.svc.cluster.local. IN A

;; ANSWER SECTION:
kubernetes.default.svc.cluster.local. 30 IN A	10.96.0.1

;; Query time: 1 msec
;; SERVER: 10.96.0.10#53(10.96.0.10) (UDP)
;; WHEN: Thu Oct 30 06:37:24 UTC 2025
;; MSG SIZE  rcvd: 129

结论:DNS 解析成功

其它测试

# TCP 连接 443# # TCP 连接 443
root@ubuntu-test:/# nc -zv 10.96.0.1 443
Connection to 10.96.0.1 443 port [tcp/*] succeeded!

# 或 curl
root@ubuntu-test:/# curl -k https://10.96.0.1:443
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
  "reason": "Forbidden",
  "details": {},
  "code": 403
}

参考链接

https://cloudmessage.top/archives/kubernetes-1292calicorockylinux92-gao-ke-yong-dockercri-docker-er-jin-zhi-bu-shu

https://www.sundayhk.com/597.html

https://github.com/cby-chen/Kubernetes/blob/main/doc/v1.29.2-CentOS-binary-install-IPv6-IPv4-Three-Masters-Two-Slaves-Offline.md


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 george_95@126.com