大纲 Kubernetes 集群架构介绍 Kubernetes 集群架构概览
Kubernetes 集群架构组件
Master(主控节点):Kubernetes 集群控制节点,负责对集群进行调度管理,接受集群外的用户去集群操作请求。Master 由 API Server、Scheduler、Controller Manager、Etcd 存储系统组成
Scheduler:节点调度,选择 Node 节点来应用部署 API Server:集群统一入口,以 RESTful 接口将数据交给 Etcd 进行存储 Controller Manager:处理集群中的常规后台任务,一个资源对应一个控制器 Etcd 存储系统:用于存储集群相关的数据 Node(工作节点):Kubernetes 集群工作节点,负责运行用户业务应用容器,Node 由 Kubelet、Kube-Proxy 和 Container Runtime 组成
Kubelet:负责 Pod 对应的容器的创建、启停管理,与 Master 节点协作,实现集群管理的基本功能 Kube-Proxy:提供 Kubernetes 的通信与负载均衡功能的重要组件 Kubernetes 依赖容器运行时(Container Runtime)来管理和运行容器。
历史上,Kubernetes 主要使用 Docker 作为容器运行时。Kubernetes 通过内置的 dockershim 组件与 Docker 实现兼容。 从 Kubernetes 1.20 版本开始,官方宣布弃用内置的 dockershim 组件,逐步移除对 Docker 作为直接容器运行时的支持。 现代 Kubernetes 推荐使用符合 Container Runtime Interface (CRI) 标准的容器运行时,主要有:containerd:由 Docker 社区捐赠的轻量级容器运行时,稳定且高效,广泛使用。 CRI-O:专门为 Kubernetes 设计的容器运行时,注重轻量和安全。 Etcd 是什么
在分布式系统中,Etcd 是一个高可用、强一致性的分布式键值存储系统,在 Kubernetes 等系统中扮演着核心元数据存储和协调中心的角色。 Etcd 属于更底层的基础组件,使用 Raft 算法实现强一致性(满足 CAP 中的 C 和 P),提供了一致性的 KV 存储、watch、lease、事务等机制。 Kubernetes 集群搭建说明 集群搭建要求 搭建 Kubernetes 集群需要满足以下几个条件:
多台机器或虚拟机,建议操作系统 CentOS 7(64 位) 集群中所有机器之间的网络可以互通 系统内可以访问外网,需要拉取镜像 系统禁用 Swap 分区(必须) Master 节点的硬件配置要求当 Master 节点只运行运行控制平面组件(如 API Server、Controller Manager 和 Scheduler)2GB 或更多 RAM,2 个 CPU 或更多 CPU,硬盘 20GB 或更多 当 Master 节点运行控制平面组件,且承载用户 Pod 的调度和运行任务 6GB 或更多 RAM,6 个 CPU 或更多 CPU,硬盘 60GB 或更多 Node 节点的硬件配置要求 4GB 或更多 RAM,4 个 CPU 或更多 CPU,硬盘 40GB 或更多 集群搭建类型 Kubernetes 集群搭建类型分为单 Master 集群和多 Master 集群两种(如下图所示),为了提高集群的可用性,生产环境一般采用多 Master 集群方案。
搭建单 Master 集群 本节将基于二进制包方式搭建单 Master 的 Kubernetes 集群,核心目标如下:
为 Etcd 、API Server、Kube-Proxy 自签证书 部署 Etcd 集群(3 个节点) 部署 Kubernetes 集群的单个 Master 节点 部署 Kubernetes 集群的多个 Node 节点 部署 Kubernetes 集群的 CNI 网络插件(Flannel) 部署 Kubernetes 集群的 DNS 组件(CoreDNS) 术语说明
为了方便描述,本文使用 Node 节点 来替代 Kubernetes 的 Worker Node,使用 Master 节点 来替代 Master Node。
版本说明 软件 版本 安装方式 CentOS 7.9多个独立虚拟机 Docker 19.03.9二进制安装包 Kubernetes 1.19.10二进制安装包 Etcd 3.4.9二进制安装包
服务器规划 本文所使用的 Kubernetes 集群服务器规划如下表所示。为了节省资源,Etcd 集群部署在与 Kubernetes 集群相同的服务器上。需要注意的是,在生产环境中,强烈建议将 Etcd 集群部署在独立的服务器上,以提升系统的性能和高可用性。
Host 名称 角色 IP CPU 核数 内存 磁盘 安装的组件 备注 k8s-master master 192.168.2.191 >= 6C >=6G >=60G kube-apiserver,kube-controller-manager,kube-scheduler,kubelet,kube-proxy,cni plugin, docker,etcd 部署 Etcd 集群节点 k8s-node1 node(worker) 192.168.2.112 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker,etcd 部署 Etcd 集群节点 k8s-node2 node(worker) 192.168.2.131 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker,etcd 部署 Etcd 集群节点 k8s-node3 node(worker) 192.168.2.236 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker 不部署 Etcd 集群节点(保证 Etcd 集群有奇数个节点)
如果希望 Master 节点仅用于运行控制平面组件(如 API Server、Controller Manager 和 Scheduler),不承载用户 Pod 的调度和运行任务,则 Master 节点可以不部署 Kubelet、Kube-Proxy 和 Docker(容器运行时),如下表所示:
Host 名称 角色 IP CPU 核数 内存 磁盘 安装的组件 备注 k8s-master master 192.168.2.191 >= 2C >=2G >=20G kube-apiserver,kube-controller-manager,kube-scheduler,cni plugin,etcd 部署 Etcd 集群节点,不部署 Kubelet、Kube-Proxy、Docker k8s-node1 node(worker) 192.168.2.112 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker,etcd 部署 Etcd 集群节点 k8s-node2 node(worker) 192.168.2.131 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker,etcd 部署 Etcd 集群节点 k8s-node3 node(worker) 192.168.2.236 >= 4C >=4G >=40G kubelet,kube-proxy,cni plugin, docker 不部署 Etcd 集群节点(保证 Etcd 集群有奇数个节点)
操作系统初始化 特别注意
在 Kubernetes 集群的所有节点(包括 Master 和 Node 节点)上分别执行以下系统初始化操作 。关闭防火墙
1 2 3 4 5 # systemctl stop firewalld# systemctl disable firewalld
关闭 selinux
1 2 3 4 5 # setenforce 0# sed -i 's/enforcing/disabled/' /etc/selinux/config
关闭 swap 分区
1 2 3 4 5 $ swapoff -a # sed -i 's/.*swap.*/#&/' /etc/fstab
系统时间同步(强烈建议使用 chrony 而不是 ntpdate)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # yum remove -y ntpdate# yum install -y chrony# systemctl enable chronyd# systemctl start chronyd# vim /etc/chrony.confserver ntp.aliyun.com iburst server ntp1.tencent.com iburst server cn.pool.ntp.org iburst # systemctl restart chronyd# systemctl status chronyd# chronyc tracking# chronyc makestep
设置主机名(比如 k8s-master、k8s-node1、k8s-node2、k8s-node3)
1 # hostnamectl set -hostname <hostname>
添加 hosts 配置信息
1 2 3 4 5 6 # vim /etc/hosts192.168.2.191 k8s-master 192.168.2.112 k8s-node1 192.168.2.131 k8s-node2 192.168.2.236 k8s-node3
将桥接的 IPv4 流量传递到 iptables 的链
1 2 3 4 5 6 7 # vim /etc/sysctl.d/k8s.confnet.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 # sysctl --system
安装 CFSSL 工具 特别注意
以下所有操作都是仅在 Kubernetes 集群的 Master 节点上执行 。CFSSL 是 CloudFlare 开源的一款 PKI/TLS 工具,包含一个命令行工具和一个用于签名、验证并且捆绑 TLS 证书的 HTTP API 服务,使用 Go 语言编写。
1 2 3 4 5 6 7 8 9 10 # curl -L https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -o /usr/local /bin/cfssl# curl -L https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -o /usr/local /bin/cfssljson# curl -L https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 -o /usr/local /bin/cfssl-certinfo# chmod +x /usr/local /bin/cfssl*# export PATH=/usr/local /bin:$PATH
安装 Docker 服务 特别注意
在 Kubernetes 集群的所有节点(包括 Master 和 Node 节点)上分别安装 Docker,这里采用二进制安装方式,CentOS 上还可以通过 YUM 安装 。如果 Master 节点仅用于运行控制平面组件(如 API Server、Controller Manager 和 Scheduler),不承载用户 Pod 的调度和运行任务,则 Master 节点可以不安装 Docker 服务。 下载并解压 Docker 的二进制包
1 2 3 4 5 6 7 8 # wget https://download.docker.com/linux/static/stable/x86_64/docker-19.03.9.tgz# tar -xvf docker-19.03.9.tgz# mv docker/* /usr/bin
创建 Docker 的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # mkdir /etc/docker# cat > /etc/docker/daemon.json << EOF{ "registry-mirrors" : [ "https://b9pmyelo.mirror.aliyuncs.com" , "https://ustc-edu-cn.mirror.aliyuncs.com" , "https://mirror.iscas.ac.cn" , "https://docker.nju.edu.cn" , "https://docker.m.daocloud.io" , "https://ccr.ccs.tencentyun.com" , "https://dockerhub.timeweb.cloud" ] } EOF
配置 Systemd 管理 Docker
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # cat > /usr/lib/systemd/system/docker.service << EOF[Unit] Description=Docker Application Container Engine Documentation=https://docs.docker.com After=network-online.target After=firewalld.service Wants=network-online.target [Service] Type=notify ExecStart=/usr/bin/dockerd ExecReload=/bin/kill -s HUP $MAINPID LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity TimeoutStartSec=0 Delegate=yes KillMode=process Restart=on-failure StartLimitBurst=3 StartLimitInterval=60s [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 Docker 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # systemctl daemon-reload# systemctl enable docker# systemctl start docker# systemctl status docker# docker --version # docker info
搭建 Etcd 集群 Etcd 是一个分布式键值存储系统,Kubernetes 使用 Etcd 进行数据存储,所以先准备一个 Etcd 系统。为解决 Etcd 单点故障,建议采用集群方式部署,这里使用 3 台机器组建 Etcd 集群,可容忍 1 台机器故障。当然,也可以使用 5 台机器组建 Etcd 集群,可容忍 2 台机器故障。为了节省机器,这里复用 Kubernetes 节点的机器,也可以独立于 Kubernetes 集群之外部署,只要 Kubernetes 的 API Server 能够连接上就行。
生成 Etcd 证书 特别注意
以下所有操作都是仅在 Kubernetes 集群的任意一个节点(比如 Master 节点)上执行,千万不要在每个 Etcd 集群节点上单独生成证书,否则 Etcd 集群里的节点可能会因证书不一致而导致集群启动失败 。以后向 Etcd 集群中添加新节点时,只要将对应的证书拷贝到新节点上即可,通过 CFSSL 工具生成证书的详细使用教程请看 这里 。 这里生成的 Etcd 证书,主要是提供给 API Server 使用的,目的是为了 API Server 与 Etcd 集群之间可以使用 CA 证书进行加密和身份验证。 创建存放 Etcd 证书的目录
1 2 3 4 5 mkdir -p ~/tls/etcd/ cd ~/tls/etcd/
创建 CA 证书的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # cat << EOF | tee ca-config.json{ "signing" : { "default" : { "expiry" : "87600h" }, "profiles" : { "www" : { "expiry" : "87600h" , "usages" : [ "signing" , "key encipherment" , "server auth" , "client auth" ] } } } } EOF
创建 CA 证书签名的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # cat << EOF | tee ca-csr.json{ "CN" : "etcd ca" , "key" : { "algo" : "rsa" , "size" : 2048 }, "names" : [ { "C" : "CN" , "ST" : "BeiJing" , "L" : "BeiJing" , "O" : "k8s" , "OU" : "System" } ], "ca" : { "expiry" : "87600h" } } EOF
创建用于生成 Etcd 证书的配置文件,如果 hosts 字段不为空,则需要指定授权使用该证书的 IP 或域名列表。由于该证书后续会被 Etcd 集群使用到,因此通常指定为 Etcd 集群所有节点的 IP 集合。因为本文的 Etcd 集群节点和 Kubernetes 的集群节点共同安装在不同虚拟机内(即服务器复用),所以 IP 列表就是 Kubernetes 集群所有节点的 IP 集合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # cat << EOF | tee server-csr.json{ "CN" : "etcd" , "hosts" : [ "192.168.2.191" , "192.168.2.112" , "192.168.2.131" , "192.168.2.236" ], "key" : { "algo" : "rsa" , "size" : 2048 }, "names" : [ { "C" : "CN" , "ST" : "BeiJing" , "L" : "BeiJing" , "O" : "k8s" , "OU" : "System" } ] } EOF
生成 CA 证书,并使用自签 CA 签发 Etcd 证书
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # ls ca-config.json ca-csr.json server-csr.json # cfssl gencert -initca ca-csr.json | cfssljson -bare ca -# ls ca*.pemca-key.pem ca.pem # cfssl gencert -ca =ca.pem -ca -key=ca-key.pem -config =ca-config.json -profile =www server-csr.json | cfssljson -bare server# ls server*.pemserver-key.pem server.pem # cfssl -certinfo -cert server.pem# openssl x509 -noout -text -in server.pem
部署 Etcd 集群 在 Master 节点里面安装 Etcd
1 2 3 4 5 6 7 8 9 10 11 # wget https://github.com/etcd-io/etcd/releases/download/v3.4.9/etcd-v3.4.9-linux-amd64.tar.gz# mkdir -p /opt/etcd/{bin,cfg,ssl}# tar zxvf etcd-v3.4.9-linux-amd64.tar.gz# mv etcd-v3.4.9-linux-amd64/{etcd,etcdctl} /opt/etcd/bin/
在 Master 节点里面拷贝上面生成的 CA 证书和 Etcd 证书
1 2 # cp ~/tls/etcd/*.pem /opt/etcd/ssl
在 Master 节点里面创建 Etcd 的配置文件,这里必须根据实际情况更改 Etcd 节点的 IP、名称,切勿直接拷贝以下配置内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # cat > /opt/etcd/cfg/etcd.conf << EOFETCD_NAME="etcd-1" ETCD_DATA_DIR="/var/lib/etcd/default.etcd" ETCD_LISTEN_PEER_URLS="https://192.168.2.191:2380" ETCD_LISTEN_CLIENT_URLS="https://192.168.2.191:2379" ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.2.191:2380" ETCD_ADVERTISE_CLIENT_URLS="https://192.168.2.191:2379" ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.2.191:2380,etcd-2=https://192.168.2.112:2380,etcd-3=https://192.168.2.131:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_INITIAL_CLUSTER_STATE="new" EOF
1 2 3 4 5 6 7 8 9 10 # 配置说明: ETCD_NAME:节点名称,集群中唯一 ETCDDATADIR:数据目录路径 ETCD_LISTEN_PEER_URLS:集群通信监听地址 ETCD_LISTEN_CLIENT_URLS:客户端访问监听地址 ETCD_INITIAL_ADVERTISE_PEER_URLS:集群通告地址 ETCD_ADVERTISE_CLIENT_URLS:客户端通告地址 ETCD_INITIAL_CLUSTER:集群节点地址 ETCD_INITIAL_CLUSTER_TOKEN:集群 Token ETCD_INITIAL_CLUSTER_STATE:加入集群的当前状态,new 是新集群,existing 表示加入已有集群
在 Master 节点里面使用 Systemd 管理 Etcd 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # cat > /usr/lib/systemd/system/etcd.service << EOF[Unit] Description=Etcd Server After=network.target After=network-online.target Wants=network-online.target [Service] Type=notify EnvironmentFile=/opt/etcd/cfg/etcd.conf ExecStart=/opt/etcd/bin/etcd \ --cert -file=/opt/etcd/ssl/server.pem \--key -file=/opt/etcd/ssl/server-key.pem \--peer -cert-file=/opt/etcd/ssl/server.pem \--peer -key-file=/opt/etcd/ssl/server-key.pem \--trusted -ca-file=/opt/etcd/ssl/ca.pem \--peer -trusted-ca-file=/opt/etcd/ssl/ca.pem \--logger =zapRestart=always RestartSec=10s LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
1 2 3 4 5 # systemctl daemon-reload# systemctl enable etcd
在 Master 节点里面拷贝所有 Etcd 文件到其他 2 个 Node 节点,并在这 2 个 Node 节点里分别更改 Etcd 的配置文件和设置 Etcd 服务开机自启动,最终 Etcd 集群会有 3 个节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # scp -r /opt/etcd/ root@k8s-node1:/opt/# scp -r /opt/etcd/ root@k8s-node2:/opt/# scp /usr/lib/systemd/system/etcd.service root@k8s-node1:/usr/lib/systemd/system/# scp /usr/lib/systemd/system/etcd.service root@k8s-node2:/usr/lib/systemd/system/# vim /opt/etcd/cfg/etcd.conf# systemctl daemon-reload# systemctl enable etcd
比如,Etcd 集群第 2 个节点的配置文件(etcd.conf)内容如下:
1 2 3 4 5 6 7 8 9 10 11 ETCD_NAME="etcd-2" ETCD_DATA_DIR="/var/lib/etcd/default.etcd" ETCD_LISTEN_PEER_URLS="https://192.168.2.112:2380" ETCD_LISTEN_CLIENT_URLS="https://192.168.2.112:2379" #[Cluster] ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.2.112:2380" ETCD_ADVERTISE_CLIENT_URLS="https://192.168.2.112:2379" ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.2.191:2380,etcd-2=https://192.168.2.112:2380,etcd-3=https://192.168.2.131:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_INITIAL_CLUSTER_STATE="new"
比如,Etcd 集群第 3 个节点的配置文件(etcd.conf)内容如下:
1 2 3 4 5 6 7 8 9 10 11 ETCD_NAME="etcd-3" ETCD_DATA_DIR="/var/lib/etcd/default.etcd" ETCD_LISTEN_PEER_URLS="https://192.168.2.131:2380" ETCD_LISTEN_CLIENT_URLS="https://192.168.2.131:2379" #[Cluster] ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.2.131:2380" ETCD_ADVERTISE_CLIENT_URLS="https://192.168.2.131:2379" ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.2.191:2380,etcd-2=https://192.168.2.112:2380,etcd-3=https://192.168.2.131:2380" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" ETCD_INITIAL_CLUSTER_STATE="new"
启动 Etcd 集群 在 Etcd 集群的所有节点上分别执行以下命令来启动 Etcd,值得一提的是,必须在多个 Etcd 节点里同时执行 systemctl start etcd 命令来启动 Etcd 集群,否则单个 Etcd 节点是无法正常启动的
1 2 3 4 5 6 7 8 # systemctl start etcd# systemctl status etcd# journalctl -u etcd.service
若 Etcd 集群启动成功,可以在任意一个 Etcd 节点里执行以下命令来查看集群的运行状态,比如 Leader 选举结果(请自行更改 Etcd 集群节点的 IP 和端口,切勿直接拷贝执行以下命令 )
1 2 3 4 5 6 7 +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS | +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | https://192.168.2.191:2379 | 9a8c44f9310511 | 3.4.9 | 442 kB | true | false | 2 | 519 | 519 | | | https://192.168.2.112:2379 | a00042f5519886e3 | 3.4.9 | 451 kB | false | false | 2 | 519 | 519 | | | https://192.168.2.131:2379 | 9cb4eb07d38510b5 | 3.4.9 | 446 kB | false | false | 2 | 519 | 519 | | +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
若 Etcd 集群启动失败,可以在所有的 Etcd 节点里分别执行以下操作来解决
1 2 3 4 5 6 7 8 9 10 11 # systemctl stop etcd# rm -rf /var/lib/etcd/default.etcd# systemctl start etcd# systemctl status etcd
特别注意
Etcd 集群使用了 Raft 协议,很多操作要依赖时间戳和进行超时判断,因此必须安装类似 chrony 这样的工具来同步操作系统的时间,否则可能会影响 Etcd 集群的正常工作。
搭建 Kubernetes 集群 生成集群证书 特别注意
以下所有操作都是仅在 Kubernetes 集群的 Master 节点上执行,千万不要在每个 Node 节点上单独生成 API Server 或者 Kube-Proxy 证书,否则可能会因证书不一致而导致 Kubernetes 集群启动失败 。简而言之,所有证书(包括 API Server、Kube-Proxy 等证书)都是在 Master 节点上面生成,Node 节点不参与生成证书。以后向 Kubernetes 集群中添加新节点时,只要将对应的证书拷贝到新节点上即可 。由于本文搭建的是单 Master 集群,控制平面组件(包括 API Server、Controller Manager 和 Scheduler)都运行在同一节点上,因此它们之间的通信未启用 HTTPS,即没有使用 CA 证书进行加密和验证。 但是,Kubelet 和 Kube-Proxy 往往部署在与 API Server 不同的节点上,因此它们与 API Server 之间的通信启用了 HTTPS,并使用 CA 证书进行加密和身份验证。另外,API Server 与 Etcd 集群之间同样会进行加密和身份验证。 值得一提的是,通过 CFSSL 工具生成证书的详细使用教程请看 这里 。 这里为了避免与上面生成的 Etcd 证书混淆,创建一个新的证书目录,然后在该目录下生成 Kubernetes 集群所需的证书
生成 API Server 证书 进入存放证书的目录
创建 CA 证书的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # cat > ca-config.json << EOF{ "signing" : { "default" : { "expiry" : "87600h" }, "profiles" : { "kubernetes" : { "expiry" : "87600h" , "usages" : [ "signing" , "key encipherment" , "server auth" , "client auth" ] } } } } EOF
创建 CA 证书签名的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # cat > ca-csr.json << EOF{ "CN" : "kubernetes" , "key" : { "algo" : "rsa" , "size" : 2048 }, "names" : [ { "C" : "CN" , "L" : "Beijing" , "ST" : "Beijing" , "O" : "k8s" , "OU" : "System" } ] } EOF
创建用于生成 API Server 证书的配置文件,如果 hosts 字段不为空,需要指定授权使用该证书的 IP 或域名列表。由于该证书会被 Kubernetes 集群使用到,通常需要包含 Kubernetes 集群所有节点的 IP,以及 Kubernetes 服务的 Cluster IP(即 kube-apiserver.conf 配置文件中指定的 --service-cluster-ip-range 网段的第一个可用 IP,例如 10.0.0.1,具体取决于 Kubernetes 集群的 Service 网段配置)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # cat << EOF | tee server-csr.json{ "CN" : "kubernetes" , "hosts" : [ "10.0.0.1" , "127.0.0.1" , "192.168.2.191" , "192.168.2.112" , "192.168.2.131" , "192.168.2.236" , "kubernetes" , "kubernetes.default" , "kubernetes.default.svc" , "kubernetes.default.svc.cluster" , "kubernetes.default.svc.cluster.local" ], "key" : { "algo" : "rsa" , "size" : 2048 }, "names" : [ { "C" : "CN" , "ST" : "BeiJing" , "L" : "BeiJing" , "O" : "k8s" , "OU" : "System" } ] } EOF
使用自签 CA 签发 API Server 证书
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # ls ca-config.json ca-csr.json server-csr.json # cfssl gencert -initca ca-csr.json | cfssljson -bare ca -# ls ca*.pemca-key.pem ca.pem # cfssl gencert -ca =ca.pem -ca -key=ca-key.pem -config =ca-config.json -profile =kubernetes server-csr.json | cfssljson -bare server# ls server*.pemserver-key.pem server.pem # cfssl -certinfo -cert server.pem# openssl x509 -noout -text -in server.pem
将上面生成的 CA 证书和 API Server 证书拷贝到本地目录里面
1 2 3 4 5 # mkdir -p /opt/kubernetes/ssl/# cp ~/tls/k8s/*.pem /opt/kubernetes/ssl/
将上面生成的 CA 证书拷贝到所有 Node 节点里面
1 2 3 4 5 6 7 8 9 # ssh root@k8s-node1 "mkdir -p /opt/kubernetes/ssl/" # ssh root@k8s-node2 "mkdir -p /opt/kubernetes/ssl/" # ssh root@k8s-node3 "mkdir -p /opt/kubernetes/ssl/" # scp ~/tls/k8s/ca*.pem root@k8s-node1:/opt/kubernetes/ssl/# scp ~/tls/k8s/ca*.pem root@k8s-node2:/opt/kubernetes/ssl/# scp ~/tls/k8s/ca*.pem root@k8s-node3:/opt/kubernetes/ssl/
生成 Kube-Proxy 证书 进入存放证书的目录
创建用于生成 Kube-Proxy 证书的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # cat > kube-proxy-csr.json << EOF{ "CN" : "system:kube-proxy" , "hosts" : [], "key" : { "algo" : "rsa" , "size" : 2048 }, "names" : [ { "C" : "CN" , "L" : "BeiJing" , "ST" : "BeiJing" , "O" : "k8s" , "OU" : "System" } ] } EOF
使用自签 CA 签发 Kube-Proxy 证书
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ls ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem kube-proxy-csr.json server.csr server-csr.json server-key.pem server.pem # cfssl gencert -ca =ca.pem -ca -key=ca-key.pem -config =ca-config.json -profile =kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy# ls kube-proxy*.pemkube-proxy-key.pem kube-proxy.pem # cfssl -certinfo -cert kube-proxy.pem# openssl x509 -noout -text -in kube-proxy.pem
将上面生成的 Kube-Proxy 证书拷贝到本地目录里面
1 2 # cp ~/tls/k8s/kube-proxy*.pem /opt/kubernetes/ssl/
将上面生成的 Kube-Proxy 证书拷贝到所有 Node 节点里面
1 2 3 4 # scp ~/tls/k8s/kube-proxy*.pem root@k8s-node1:/opt/kubernetes/ssl/# scp ~/tls/k8s/kube-proxy*.pem root@k8s-node2:/opt/kubernetes/ssl/# scp ~/tls/k8s/kube-proxy*.pem root@k8s-node3:/opt/kubernetes/ssl/
单独部署 Master 节点 特别注意
以下所有操作都是仅在 Kubernetes 集群的 Master 节点上执行,包括部署 API Server、Controller Manager、Scheduler 这三大核心组件 。
下载二进制文件 下载地址
Kubernetes 相关的二进制包可以从 GitHub 下载得到,这里下载的版本是 v1.19.10。 当打开 GitHub 链接后,会发现里面有很多二进制包,只需要下载一个 Server 二进制包就够了,里面包含了 Master 节点相关的二进制包。 下载并解压二进制包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # wget https://dl.k8s.io/v1.19.10/kubernetes-server-linux-amd64.tar.gz# mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs}# tar zxvf kubernetes-server-linux-amd64.tar.gz# cd kubernetes/server/bin# cp kube-apiserver kube-scheduler kube-controller-manager /opt/kubernetes/bin/# cp kubectl /usr/bin/
部署 API Server 创建 API Server 的配置文件,使用转义符 \\ 是为了使 EOF 保留换行符,其中 --bind-address 与 --advertise-address 通常是 Master 节点的 IP 地址,请记得自行更改以下配置内容里的 IP 地址,切勿直接拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # cat > /opt/kubernetes/cfg/kube-apiserver.conf << EOFKUBE_APISERVER_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --etcd-servers=https://192.168.2.191:2379,https://192.168.2.112:2379,https://192.168.2.131:2379 \\ --bind-address=192.168.2.191 \\ --secure-port=6443 \\ --advertise-address=192.168.2.191 \\ --allow-privileged=true \\ --service-cluster-ip-range=10.0.0.0/24 \\ --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \\ --authorization-mode=RBAC,Node \\ --enable-bootstrap-token-auth=true \\ --token-auth-file=/opt/kubernetes/cfg/token.csv \\ --service-node-port-range=30000-32767 \\ --kubelet-client-certificate=/opt/kubernetes/ssl/server.pem \\ --kubelet-client-key=/opt/kubernetes/ssl/server-key.pem \\ --tls-cert-file=/opt/kubernetes/ssl/server.pem \\ --tls-private-key-file=/opt/kubernetes/ssl/server-key.pem \\ --client-ca-file=/opt/kubernetes/ssl/ca.pem \\ --service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \\ --etcd-cafile=/opt/etcd/ssl/ca.pem \\ --etcd-certfile=/opt/etcd/ssl/server.pem \\ --etcd-keyfile=/opt/etcd/ssl/server-key.pem \\ --audit-log-maxage=30 \\ --audit-log-maxbackup=3 \\ --audit-log-maxsize=100 \\ --audit-log-path=/opt/kubernetes/logs/k8s-audit.log" EOF
配置参数说明
参数 含义 --logtostderr启用将日志输出到 stderr(通常设为 false 以输出到指定目录)。 --v日志详细级别(verbosity level),数值越大输出越详细。 --log-dir日志文件输出目录。 --etcd-serversEtcd 集群地址列表(用逗号分隔,支持 Https)。 --bind-addresskube-kube-apiserver 监听的 IP 地址,通常是 Master 节点的 IP 地址。 --secure-portHTTPS 安全端口(默认 6443)。 --advertise-address集群中对外通告的地址(其他组件访问时使用),通常是 Master 节点的 IP 地址。 --allow-privileged是否允许特权容器(启用授权相关特性)。 --service-cluster-ip-rangeService 虚拟 IP 地址段,用于分配 Cluster IP。 --enable-admission-plugins启用的准入控制插件列表(例如 NamespaceLifecycle, LimitRanger 等)。 --authorization-mode认证 / 授权模式(如 RBAC,Node 表示启用 RBAC 和节点自管理)。 --enable-bootstrap-token-auth启用基于 Bootstrap Token 的 TLS 引导认证。 --token-auth-fileBootstrap Token 的静态文件路径(比如 token.csv)。 --service-node-port-rangeNodePort 类型 Service 默认分配的端口范围(如 30000-32767)。 --kubelet-client-certificatekube-apiserver 访问 kubelet 时使用的客户端证书路径。 --kubelet-client-keykube-apiserver 访问 kubelet 时使用的客户端私钥路径。 --tls-cert-filekube-apiserver HTTPS 使用的证书文件。 --tls-private-key-filekube-apiserver HTTPS 使用的私钥文件。 --client-ca-file用于验证客户端证书(如 kubelet/client)的 CA 证书。 --service-account-key-file用于签名 Service Account Token 的私钥文件。 --etcd-cafile连接 Etcd 时验证其证书的 CA 证书路径。 --etcd-certfilekube-apiserver 连接 Etcd 时使用的客户端证书。 --etcd-keyfilekube-apiserver 连接 Etcd 时使用的客户端私钥。 --audit-log-maxage审计日志保留的最大天数。 --audit-log-maxbackup审计日志保留的备份数量。 --audit-log-maxsize单个审计日志文件的最大大小(MB)。 --audit-log-path审计日志输出路径。
TLS Bootstraping 机制介绍
在 Master 节点中,API Server 启用 TLS 认证后,Node 节点上的 Kubelet 和 Kube-Proxy 必须使用由 CA 签发的有效客户端证书才能与 API Server 通信。当 Node 节点数量较多时,手动颁发这些客户端证书的工作量大,且会增加集群扩展的复杂度。为简化这一流程,Kubernetes 引入了 TLS Bootstrapping 机制:Kubelet 会以低权限用户身份向 API Server 自动申请客户端证书,并由 API Server 动态签发,从而实现证书的自动化管理。TLS Bootstrapping 机制的工作流程如图所示 。
创建在 API Server 配置文件(kube-apiserver.conf)中通过 --token-auth-file 指定的 Token 文件,用于配置 TLS Bootstrapping 机制
1 2 3 4 # cat > /opt/kubernetes/cfg/token.csv << EOFc47ffb939f5ca36231d9e3121a252940,kubelet-bootstrap,10001,"system:node-bootstrapper" EOF
Token 文件的数据格式是 Token,用户名,UID,用户组,其中 UID 仅作标识使用,可以是任意唯一值;用户组 system:node-bootstrapper 是 Kubelet TLS Bootstrap 必需的组;Token 可以使用以下命令自行生成并替换掉
1 2 # head -c 16 /dev/urandom | od -An -t x | tr -d ' '
配置 Systemd 管理 API Server 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # cat > /usr/lib/systemd/system/kube-apiserver.service << EOF[Unit] Description=Kubernetes API Server Documentation=https://github.com/kubernetes/kubernetes After=network-online.target [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-apiserver.conf ExecStart=/opt/kubernetes/bin/kube-apiserver \$KUBE_APISERVER_OPTS Restart=on-failure [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 API Server 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # systemctl daemon-reload# systemctl enable kube-apiserver# systemctl start kube-apiserver# systemctl status kube-apiserver# journalctl -u kube-apiserver.service
授权 kubelet-bootstrap 用户允许请求(申请)证书
1 # kubectl create clusterrolebinding kubelet-bootstrap --clusterrole =system:node-bootstrapper --user =kubelet-bootstrap
部署 Controller Manager 创建 Controller Manager 的配置文件,使用转义符 \\ 是为了使 EOF 保留换行符,一般情况下,以下配置信息可以直接拷贝使用。特别注意,其中的 --cluster-cidr 与 --service-cluster-ip-range 配置参数的值不要随意更改,否则可能会影响 Kubernetes 集群的正常运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # cat > /opt/kubernetes/cfg/kube-controller-manager.conf << EOFKUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --leader-elect=true \\ --master=127.0.0.1:8080 \\ --bind-address=127.0.0.1 \\ --allocate-node-cidrs=true \\ --cluster-cidr=10.244.0.0/16 \\ --service-cluster-ip-range=10.0.0.0/24 \\ --cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \\ --cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem \\ --root-ca-file=/opt/kubernetes/ssl/ca.pem \\ --service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \\ --cluster-signing-duration=87600h0m0s" EOF
配置参数说明
参数 说明 --logtostderr是否将日志输出到标准错误(false 表示写到文件,需要配合 --log-dir 使用)。 --v日志详细级别,数字越大输出越详细(常用 2 作为调试级别)。 --log-dir指定日志文件存放目录。 --leader-elect是否启用领导者选举机制,用于多个 Controller Manager 实例之间选主。 --master指定 API Server 的地址(127.0.0.1:8080 表示通过本地非安全端口连接 API Server,不使用 CA 证书)。 --bind-addressController Manager 进程绑定监听的地址(127.0.0.1 表示仅监听本地)。 --allocate-node-cidrs是否为每个节点自动分配 Pod CIDR 地址段(通常与 --cluster-cidr 搭配使用)。 --cluster-cidrPod 网络的 CIDR 范围,分配给各节点的 Pod 网段从这里划分(示例:10.244.0.0/16)。 --service-cluster-ip-rangeService 虚拟 IP 地址段,用于分配 Cluster IP 类型的 Service。 --cluster-signing-cert-file集群签发证书时使用的 CA 证书路径(用于 kubelet、组件证书等)。 --cluster-signing-key-file集群签发证书时使用的 CA 私钥路径。 --root-ca-file提供给 Service Account 及其他客户端的根 CA 证书文件路径。 --service-account-private-key-file用于为 Service Account Token 签名的私钥文件路径。 --cluster-signing-duration集群签发证书的有效期(示例:87600h0m0s 表示 10 年)。
配置 Systemd 管理 Controller Manager 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # cat > /usr/lib/systemd/system/kube-controller-manager.service << EOF[Unit] Description=Kubernetes Controller Manager Documentation=https://github.com/kubernetes/kubernetes After=network-online.target [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-controller-manager.conf ExecStart=/opt/kubernetes/bin/kube-controller-manager \$KUBE_CONTROLLER_MANAGER_OPTS Restart=on-failure [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 Controller Manager 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # systemctl daemon-reload# systemctl enable kube-controller-manager# systemctl start kube-controller-manager# systemctl status kube-controller-manager# journalctl -u kube-controller-manager.service
部署 Scheduler 创建 Scheduler 配置文件,使用转义符 \\ 是为了使 EOF 保留换行符,一般情况下,以下配置信息可以直接拷贝使用
1 2 3 4 5 6 7 8 9 # cat > /opt/kubernetes/cfg/kube-scheduler.conf << EOFKUBE_SCHEDULER_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --leader-elect \\ --master=127.0.0.1:8080 \\ --bind-address=127.0.0.1" EOF
配置参数说明
参数 说明 --logtostderr是否将日志输出到标准错误(false 表示写到文件,需要配合 --log-dir 使用)。 --v日志详细级别,数字越大输出越详细(2 常用于调试)。 --log-dir指定日志文件存放目录。 --leader-elect启用领导者选举机制,用于在多个 Scheduler 实例之间选主(保证同一时刻只有一个活跃调度器)。 --master指定连接 API Server 的地址(127.0.0.1:8080 表示通过本地非安全端口连接 API Server,不使用 CA 证书)。 --bind-addressScheduler 进程绑定监听的地址(127.0.0.1 表示仅监听本地)。
配置 Systemd 管理 Scheduler 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # cat > /usr/lib/systemd/system/kube-scheduler.service << EOF[Unit] Description=Kubernetes Scheduler Documentation=https://github.com/kubernetes/kubernetes After=network-online.target [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-scheduler.conf ExecStart=/opt/kubernetes/bin/kube-scheduler \$KUBE_SCHEDULER_OPTS Restart=on-failure [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 Scheduler 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # systemctl daemon-reload# systemctl enable kube-scheduler# systemctl start kube-scheduler# systemctl status kube-scheduler# journalctl -u kube-scheduler.service
查看各组件的运行状态 在 Master 节点中,当 API Server、Controller Manager、Scheduler 组件都成功启动后,可以通过 kubectl 工具查看当前 Kubernetes 集群组件(不包括 API Server)的运行状态
1 2 3 4 5 6 7 8 # kubectl get csNAME STATUS MESSAGE ERROR scheduler Healthy ok controller-manager Healthy ok etcd-0 Healthy {"health" :"true" } etcd-1 Healthy {"health" :"true" } etcd-2 Healthy {"health" :"true" }
提示
这里的 kubectl get cs 命令只会显示部分组件的运行状态(主要是 Etcd、Controller Manager、Scheduler),不包括 API Server;因为 API Server 是 Kubernetes 的核心服务,一般需要通过其它方式进行监控。
部署 Master 与 Node 节点 特别注意
在 Kubernetes 集群的所有节点(包括 Master 和 Node 节点)上分别执行以下操作,除了特别说明之外(比如,只在 Master 节点批准 Kubelet 的证书签名请求)。简而言之,Master 和 Node 节点都需要安装 Kubelet、Kube-Proxy、CNI 网络插件。 如果 Master 节点仅用于运行控制平面组件(如 API Server、Controller Manager 和 Scheduler),不承载用户 Pod 的调度和运行任务,则 Master 节点可以不部署 Kubelet 和 Kube-Proxy,但还是必须安装 CNI 网络插件。。 下载二进制文件 下载地址
Kubernetes 相关的二进制包可以从 GitHub 下载得到,这里下载的版本是 v1.19.10。 当打开 GitHub 链接后,会发现里面有很多二进制包,只需要下载一个 Server 二进制包就够了,里面包含了 Master 节点相关的二进制包。 由于上面单独部署 Maser 节点时,已经下载并解压过 Kubernetes 的二进制包文件,因此这里只需要在所有 Node 节点上下载二进制包文件即可
1 2 3 4 5 # wget https://dl.k8s.io/v1.19.10/kubernetes-server-linux-amd64.tar.gz# tar zxvf kubernetes-server-linux-amd64.tar.gz
在所有节点(包括 Master 和 Node 节点)上,分别执行以下命令拷贝对应的可执行文件
1 2 3 4 5 6 7 8 9 10 11 # cd kubernetes/server/bin# mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs}# cp kubelet kube-proxy /opt/kubernetes/bin# cp -n kubectl /usr/bin/
部署 Kubelet 创建 Kubelet 的配置文件,使用转义符 \\ 是为了使 EOF 保留换行符,请自行更改 --hostname-override 的值为当前节点的主机名(比如 k8s-node1),切勿直接拷贝以下配置内容
1 2 3 4 5 6 7 8 9 10 11 12 13 # cat > /opt/kubernetes/cfg/kubelet.conf << EOFKUBELET_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --hostname-override=<当前节点的主机名> \\ --network-plugin=cni \\ --kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \\ --bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \\ --config=/opt/kubernetes/cfg/kubelet-config.yml \\ --cert-dir=/opt/kubernetes/ssl \\ --pod-infra-container-image=lizhenliang/pause-amd64:3.0" EOF
配置参数说明
参数 说明 --logtostderr是否将日志输出到标准错误(false 表示写入日志目录) --v日志详细级别,值越大日志越详细(2 为常用调试级别) --log-dir日志存放目录 --hostname-override覆盖节点的主机名,在集群中显示为指定名称,通常填写当前节点的主机名 --network-plugin使用的网络插件类型(比如 CNI) --kubeconfigkubelet 连接 API Server 的 kubeconfig 配置文件路径 --bootstrap-kubeconfigkubelet 首次启动时用于申请证书的引导配置文件路径 --configkubelet 主配置文件路径 --cert-dir存放 kubelet TLS 证书的目录 --pod-infra-container-imagePod 基础容器(Pause 容器)镜像,用于提供网络命名空间
创建 Kubelet 的配置参数文件,一般情况下,以下配置内容可以直接拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # cat > /opt/kubernetes/cfg/kubelet-config.yml << EOFkind: KubeletConfiguration apiVersion: kubelet.config.k8s.io/v1beta1 address: 0.0.0.0 port: 10250 readOnlyPort: 10255 cgroupDriver: cgroupfs clusterDNS: - 10.0.0.2 clusterDomain: cluster.local failSwapOn: false authentication: anonymous: enabled: false webhook: cacheTTL: 2m0s enabled: true x509: clientCAFile: /opt/kubernetes/ssl/ca.pem authorization: mode: Webhook webhook: cacheAuthorizedTTL: 5m0s cacheUnauthorizedTTL: 30s evictionHard: imagefs.available: 15% memory.available: 100Mi nodefs.available: 10% nodefs.inodesFree: 5% maxOpenFiles: 1000000 maxPods: 110 EOF
生成 bootstrap.kubeconfig 配置文件,请自行修改这里环境变量中的 API Server 地址,切勿直接拷贝环境变量的值
1 2 3 KUBE_APISERVER="https://192.168.2.191:6443" TOKEN="c47ffb939f5ca36231d9e3121a252940"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # kubectl config set -cluster kubernetes \--certificate -authority=/opt/kubernetes/ssl/ca.pem \--embed -certs=true \--server =${KUBE_APISERVER} \--kubeconfig =bootstrap.kubeconfig# kubectl config set -credentials "kubelet-bootstrap" \--token =${TOKEN} \--kubeconfig =bootstrap.kubeconfig# kubectl config set -context default \--cluster =kubernetes \--user ="kubelet-bootstrap" \--kubeconfig =bootstrap.kubeconfig# kubectl config use-context default --kubeconfig =bootstrap.kubeconfig
1 2 # mv bootstrap.kubeconfig /opt/kubernetes/cfg
提前手动拉取 Pause 镜像(Pod 的基础镜像,用于 Pod 网络命名空间),避免后面部署 CNI 网络插件时,出现 Kubelet 自动拉取 Pause 镜像失败的问题(该镜像的名称和版本在 kubelet.conf 配置文件中指定)
1 2 # docker pull lizhenliang/pause-amd64:3.0
配置 Systemd 管理 Kubelet 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # cat > /usr/lib/systemd/system/kubelet.service << EOF[Unit] Description=Kubernetes Kubelet Documentation=https://github.com/kubernetes/kubernetes After=network-online.target After=docker.service [Service] EnvironmentFile=/opt/kubernetes/cfg/kubelet.conf ExecStart=/opt/kubernetes/bin/kubelet \$KUBELET_OPTS Restart=on-failure LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 Kubelet 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # systemctl daemon-reload# systemctl enable kubelet# systemctl start kubelet# systemctl status kubelet# journalctl -u kubelet.service
在查看 Kubelet 服务的运行状态时,可能会看到以下错误信息。这是因为目前还没有安装 CNI 网络插件,因此该错误可以暂时忽略掉
1 2 3 11月 11 21:15:55 k8s-node1 kubelet[7509]: E1112 11:15:55.312188 7509 kubelet.go:2134] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized 11月 11 21:16:00 k8s-node1 kubelet[7509]: E1112 11:16:00.329434 7509 kubelet.go:2134] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized 11月 11 21:16:05 k8s-node1 kubelet[7509]: E1112 11:16:05.347068 7509 kubelet.go:2134] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
特别注意
当 Kubelet 服务正常启动后,会自动向 API Server 发起 CSR(证书签名请求)。一旦该 CSR 请求被批准,Kubelet 就会获得客户端证书,并开始与 API Server 通信,从而将它所在的 Node 节点加入到 Kubernetes 集群中,所以无需使用 kubeadm join 命令手动将当前节点加入 Kubernetes 集群,也无需像 Kube-Proxy 一样使用自签 CA 签发证书。
在 Master 节点上执行以下命令 ,批准 Kubelet 证书签名请求并允许 Node 节点加入 Kubernetes 集群
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # kubectl get csrNAME AGE SIGNERNAME REQUESTOR CONDITION node-csr-BRKQdidf63HB1ZjRZLvYKeNxW0p0vSN3UG_Av8XJO3I 95s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending node-csr-Lcn5xLuMioS9H1aa8k9qATIAAdNIhdlT_OpCco4ePoU 97s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending node-csr-jDiiH16kcOjiYPjWysqaKbudnf1z948P937PHRU5VzQ 94s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending # kubectl certificate approve node-csr-BRKQdidf63HB1ZjRZLvYKeNxW0p0vSN3UG_Av8XJO3I# kubectl certificate approve node-csr-Lcn5xLuMioS9H1aa8k9qATIAAdNIhdlT_OpCco4ePoU# kubectl certificate approve node-csr-jDiiH16kcOjiYPjWysqaKbudnf1z948P937PHRU5VzQ# kubectl get nodeNAME STATUS ROLES AGE VERSION k8s-master NotReady <none> 20h v1.19.10 k8s-node1 NotReady <none> 4m4s v1.19.10 k8s-node2 NotReady <none> 3m46s v1.19.10 k8s-node3 NotReady <none> 3m53s v1.19.10
部署 Kube-Proxy 创建 Kube-Proxy 的配置文件,使用转义符 \\ 是为了使 EOF 保留换行符
1 2 3 4 5 6 # cat > /opt/kubernetes/cfg/kube-proxy.conf << EOFKUBE_PROXY_OPTS="--logtostderr=false \\ --v=2 \\ --log-dir=/opt/kubernetes/logs \\ --config=/opt/kubernetes/cfg/kube-proxy-config.yml" EOF
配置参数说明
参数 说明 --logtostderr=false日志输出到文件而不是标准错误输出(stderr) --v=2设置日志详细级别为 2(数值越大日志越详细) --log-dir=/opt/kubernetes/logs指定日志文件存放目录 --config=/opt/kubernetes/cfg/kube-proxy-config.yml指定 kube-proxy 的配置参数文件路径
创建 Kube-Proxy 的配置参数文件,请自行更改 hostnameOverride 的值为当前节点的主机名(比如 k8s-node1),其中的 clusterCIDR 要与 kube-apiserver.conf 配置文件中的 --service-cluster-ip-range 一致,切勿直接拷贝以下配置内容
1 2 3 4 5 6 7 8 9 10 11 # cat > /opt/kubernetes/cfg/kube-proxy-config.yml << EOFkind: KubeProxyConfiguration apiVersion: kubeproxy.config.k8s.io/v1alpha1 bindAddress: 0.0.0.0 metricsBindAddress: 0.0.0.0:10249 clientConnection: kubeconfig: /opt/kubernetes/cfg/kube-proxy.kubeconfig hostnameOverride: <当前节点的主机名> clusterCIDR: 10.0.0.0/24 EOF
生成 kube-proxy.kubeconfig 文件,请自行修改这里环境变量中的 API Server 地址,切勿直接拷贝环境变量的值
1 2 KUBE_APISERVER="https://192.168.2.191:6443"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # kubectl config set -cluster kubernetes \--certificate -authority=/opt/kubernetes/ssl/ca.pem \--embed -certs=true \--server =${KUBE_APISERVER} \--kubeconfig =kube-proxy.kubeconfig# kubectl config set -credentials kube-proxy \--client -certificate=/opt/kubernetes/ssl/kube-proxy.pem \--client -key=/opt/kubernetes/ssl/kube-proxy-key.pem \--embed -certs=true \--kubeconfig =kube-proxy.kubeconfig# kubectl config set -context default \--cluster =kubernetes \--user =kube-proxy \--kubeconfig =kube-proxy.kubeconfig# kubectl config use-context default --kubeconfig =kube-proxy.kubeconfig
1 2 # mv kube-proxy.kubeconfig /opt/kubernetes/cfg
配置 Systemd 管理 Kube-Proxy 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # cat > /usr/lib/systemd/system/kube-proxy.service << EOF[Unit] Description=Kubernetes Proxy Documentation=https://github.com/kubernetes/kubernetes After=network-online.target After=docker.service [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-proxy.conf ExecStart=/opt/kubernetes/bin/kube-proxy \$KUBE_PROXY_OPTS Restart=on-failure LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
启动并设置开机自启动 Kube-Proxy 服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # systemctl daemon-reload# systemctl enable kube-proxy# systemctl start kube-proxy# systemctl status kube-proxy# journalctl -u kube-proxy.service
部署 CNI 网络插件 所有节点的操作 特别注意
在 Kubernetes 集群的所有节点(包括 Master 和 Node 节点)上面,分别下载并解压 CNI 网络插件的二进制包 。
1 2 3 4 5 6 7 8 9 # wget https://github.com/containernetworking/plugins/releases/download/v0.8.6/cni-plugins-linux-amd64-v0.8.6.tgz# mkdir -p /opt/cni/bin# mkdir -p /etc/cni/net.d/# tar zxvf cni-plugins-linux-amd64-v0.8.6.tgz -C /opt/cni/bin
Master 节点的操作 特别注意
在 Master 节点上面部署 CNI 网络插件(Flannel),不需要在 Node 节点重复执行该操作。换言之,以下操作只在 Master 节点上执行 。
下载 CNI 网络插件(Flannel)的 YAML 配置文件
1 2 # wget -P /opt/kubernetes/cfg/ https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
在 Kubernetes 集群中部署 CNI 网络插件(Flannel)
1 2 # kubectl apply -f /opt/kubernetes/cfg/kube-flannel.yml
查看 CNI 网络插件的所有 Pod
1 2 # kubectl get pods -n kube-flannel
1 2 3 4 5 NAME READY STATUS RESTARTS AGE pod/kube-flannel-ds-29gvn 1/1 Running 0 5m14s pod/kube-flannel-ds-99gvw 1/1 Running 0 5m14s pod/kube-flannel-ds-ntsf5 1/1 Running 0 5m14s pod/kube-flannel-ds-xqrnd 1/1 Running 0 5m14s
查看 CNI 网络插件的所有 DaemonSet
1 2 # kubectl get ds -n kube-flannel
1 2 NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE kube-flannel-ds 4 4 4 4 4 <none> 6m49s
查看 Kubernetes 集群中所有节点的运行状态
1 2 3 4 5 NAME STATUS ROLES AGE VERSION k8s-master1 Ready <none> 23h v1.19.10 k8s-node1 Ready <none> 173m v1.19.10 k8s-node2 Ready <none> 172m v1.19.10 k8s-node3 Ready <none> 172m v1.19.10
CNI 网络插件的安装细节
这个 kubectl apply 命令会创建一个 DaemonSet 类型的资源,而 DaemonSet 的特性是:会在 Kubernetes 集群中所有「可调度」的 Node 节点上运行一个 Pod 副本。换言之,在 Master 节点上,通过 kubectl apply 命令安装 CNI 网络插件后,默认会将 CNI 网络插件部署到整个 Kubernetes 集群的所有节点(包括 Master 和 Node),无需手动在每个节点上重复执行 kubectl apply 命令来安装 CNI 网络插件。
创建用于授权 API Server 访问 Kubelet 的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 # cat > /opt/kubernetes/cfg/apiserver-to-kubelet-rbac.yaml << EOFapiVersion: 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 - pods/log 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: kubernetes EOF
授权 API Server 访问 Kubelet
1 2 # kubectl apply -f /opt/kubernetes/cfg/apiserver-to-kubelet-rbac.yaml
为什么必须安装 CNI 网络插件
Kubernetes 本身不包含网络实现,但它要求集群中的所有 Pod 能够彼此通信(无论位于哪个节点),这是 Kubernetes 网络模型的基本要求。 通过 Kubeadm 搭建 Kubernetes 集群时,使用 kubeadm init 初始化 Master 节点后,默认只有控制平面功能,没有网络功能。 当没有安装 CNI 网络插件时,Pod 会卡在 ContainerCreating 状态,因为找不到网络(CNI)配置。 当安装 CNI 网络插件(如 Flannel)后,多个节点之间的 Pod 才能通信和正常运行。 测试集群整体功能 在 Master 节点执行以下命令,查看集群中所有节点的运行状态,当节点的运行状态都变更为 Ready 时,则表示 Kubernetes 集群已经成功搭建起来了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # kubectl get nodesNAME STATUS ROLES AGE VERSION k8s-master Ready <none> 23h v1.19.10 k8s-node1 Ready <none> 173m v1.19.10 k8s-node2 Ready <none> 172m v1.19.10 k8s-node3 Ready <none> 172m v1.19.10 # kubectl get csNAME STATUS MESSAGE ERROR scheduler Healthy ok controller-manager Healthy ok etcd-0 Healthy {"health" :"true" } etcd-1 Healthy {"health" :"true" } etcd-2 Healthy {"health" :"true" } # kubectl version --short Client Version: v1.19.10 Server Version: v1.19.10
特别注意
在使用二进制方式完成 Kubernetes 集群的搭建时,执行 kubectl get pods -n kube-system 命令可能无法看到任何 Pod 的运行状态。这是因为 Kubernetes 中的 Pod 是由 Kubelet 负责向 API Server 注册并创建的,仅仅启动 API Server、Controller Manager、Scheduler、Kubelet、Kube-Proxy 等二进制组件,并不会自动生成或运行这些系统组件对应的 Pod。系统核心组件如 CoreDNS、Metrics Server 等都需要用户手动部署相关的 YAML 资源文件,Kubelet 才会拉取镜像并创建对应的 Pod。因此,若未部署这些资源,kube-system 命名空间中将不会显示任何 Pod。
在 Master 节点执行以下命令,目的是在 Kubernetes 集群里创建一个 Nginx 的 Deployment,验证 Kubernetes 集群是否正常运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # kubectl create deployment nginx --image =nginx# kubectl expose deployment nginx --port =80 --type =NodePort --target -port=80# kubectl get pods -o wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-6799fc88d8-dkltf 1/1 Running 0 12m 10.244.2.2 k8s-node3 <none> <none> # kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 2d1h nginx NodePort 10.0.0.200 <none> 80:31062/TCP 13m
在 Kubernetes 集群外部,通过浏览器访问 http://192.168.2.112:31062,其中 IP 可以是任意集群节点的 IP 地址,端口由 kubectl get svc 命令可得知。若 Ngninx 容器在 Kubernetes 集群中创建并启动成功,则浏览器可以正常访问 Nginx 的首页(如下图所示),如下图所示:
若希望删除刚在 Kubernetes 集群创建的 Nginx 容器,可以在 Master 节点执行以下命令:
1 2 3 4 5 # kubectl delete service nginx# kubectl delete deployment nginx
部署 CoreDNS(可选) 特别注意
以下所有操作都是仅在 Kubernetes 集群的 Master 节点上执行,请保证在部署 CoreDNS 之前,Kubernetes 集群是可以正常运行的。 CodreDNS 的作用是:在 Kubernetes 集群内,可以通过 Service 的 DNS 名称(域名)对 Pod 进行访问,比如:http://<service-name>.<namespace>.svc.cluster.local:<port>,具体使用案例请看 这里 。 CoreDNS 的部署说明
由于上面搭建的 Kubernetes 集群已经具备运行 Pod 的完整容器化环境,这种情况下 CoreDNS 最合适的部署方式就是容器化部署,即通过 Kubernetes 自己来管理 CoreDNS 的 Pod。CoreDNS 也可以通过二进制包进行部署的,两种部署方式的区别如下: 部署方式 适合场景 优点 缺点 容器化部署 CoreDNS 集群支持运行 Pod,且至少 kube-system 命名空间可用 符合 Kubernetes 生态,自动扩缩容、自动重启,便于管理 需要容器运行环境 二进制部署 CoreDNS 极简集群、节点无容器环境,或仅测试 DNS 不依赖容器,部署简单 无法通过 Kubernetes 管理,需手动维护、高可用难度大
优势 说明 符合 Kubernetes 官方架构 CoreDNS 本身就是官方默认的集群 DNS 方案,部署成 Pod 完全符合设计思路。 自动高可用 可通过 Replica Set 或 Deployment 管理多个副本,某个 Pod 异常退出会被自动重建。 易扩容 / 升级 直接通过 kubectl scale 或 kubectl apply 即可扩容、升级。 与 kube-proxy、kubelet 无缝配合 kubelet 会自动配置 Pod 的 DNS 指向 CoreDNS 的 ClusterIP。 支持弹性伸缩 可配合 Pod 横向自动扩容(Horizontal Pod Autoscaler,简称 HPA)实现负载自动扩缩容。
CoreDNS 的部署步骤
从 CoreDNS 官方仓库下载最新的 YAML 配置文件模板,并生成新的配置文件 1 2 3 4 5 6 7 8 9 10 11 12 # wget https://raw.githubusercontent.com/coredns/deployment/master/kubernetes/coredns.yaml.sed -O coredns.yaml.sed# sed -e "s/CLUSTER_DOMAIN/cluster.local/g" \ -e "s/REVERSE_CIDRS/in-addr.arpa ip6.arpa/g" \ -e "s/UPSTREAMNAMESERVER/8.8.8.8/g" \ -e "s/}STUBDOMAINS/}/" \ coredns.yaml.sed > coredns.yaml # mv coredns.yaml /opt/kubernetes/cfg/
检查 Kubelet 的 YAML 配置文件(kubelet-config.yml),确保包含以下配置信息(注意,Kubernetes 默认的 DNS Service IP 是 10.0.0.10,也可以指定为实际规划的 Pod 网段内的 IP,本文使用自定义的 IP) 1 2 # vim /opt/kubernetes/cfg/kubelet-config.yml
1 2 3 clusterDNS: - 10.0 .0 .2 clusterDomain: cluster.local
修改 CoreDNS 的 YAML 配置文件(coredns.yaml),只需要修改 spec.replicas 和 spec.clusterIP 这两个配置 1 2 # vim /opt/kubernetes/cfg/coredns.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ...... apiVersion: apps/v1 kind: Deployment metadata: name: coredns namespace: kube-system labels: k8s-app: kube-dns kubernetes.io/name: "CoreDNS" app.kubernetes.io/name: coredns spec: replicas: 2 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 ......
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...... apiVersion: v1 kind: Service metadata: name: kube-dns namespace: kube-system annotations: prometheus.io/port: "9153" prometheus.io/scrape: "true" labels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" kubernetes.io/name: "CoreDNS" app.kubernetes.io/name: coredns spec: selector: k8s-app: kube-dns app.kubernetes.io/name: coredns clusterIP: 10.0 .0 .2 ......
1 # kubectl apply -f /opt/kubernetes/cfg/coredns.yaml
1 # kubectl get pods -n kube-system -l k8s-app=kube-dns
1 2 3 NAME READY STATUS RESTARTS AGE coredns-6b9bb479b9-g6t6f 1/1 Running 0 39s coredns-6b9bb479b9-lfd7j 1/1 Running 0 39s
若 CoreDNS 的 Pod 启动失败,可以通过查看日志信息来定位问题 1 # kubectl logs coredns-6b9bb479b9-lfd7j -n kube-system
若希望删除 CoreDNS 的所有资源(包括 Pod、Controller、Service 等),可以执行以下命令 1 # kubectl delete -f /opt/kubernetes/cfg/coredns.yaml
单 Master 集群搭建问题 集群启动顺序问题 Kubernetes 集群包含多个组件,这些组件之间存在依赖关系。例如,大多数组件都依赖 API Server,因此 API Server 通常需要优先启动,而 API Server 本身又依赖 Etcd 集群先行启动。建议按以下顺序启动各个组件,否则有可能导致某些组件无法正常启动(尤其各个组件都是通过 Systemd 管理服务,并且集群的所有服务器都发生断电重启的情况下 ):
1、Etcd 集群(所有集群节点的 Etcd) 2、Master 节点组件:(1) API Server (2) Controller Manager (3) Scheduler 3、Node 节点组件:(1) Kubelet (2) Kube-Proxy 4、网络插件(如 Flannel、Calico、Cilium 等) 5、其他附加组件(如 CoreDNS、Ingress Controller、Dashboard 等) Kubernetes 集群各个组件之间的依赖关系
Etcd 集群:Kubernetes 的数据存储后端,保存 API Server 的所有配置信息和资源对象;若未启动,API Server 无法连接存储并会报错退出。 API Server(Master 节点):集群核心入口,Controller Manager、Scheduler、kubectl 等都通过它访问集群状态;必须先启动才能让其他组件连接工作。 Controller Manager(Master 节点):通过 API Server 访问和修改集群状态(如创建 Pod、更新节点信息);若 API Server 未启动,则会不断重试连接而启动失败。 Scheduler(Master 节点):通过 API Server 获取未调度的 Pod 列表并为其分配合适的 Node;依赖 API Server 正常运行。 手动解决启动顺序问题 特别注意
当所有 Kubernetes 集群服务器重启后(比如断电重启),部分 Kubernetes 组件可能无法正确启动,因为每个组件之间存在一定的依赖关系,这时候就需要人工介入处理。
管理 Master 节点的组件启动,以下所有操作都是仅在 Kubernetes 集群的 Master 节点上执行
1 2 3 4 5 # systemctl status kube-apiserver# systemctl status kube-controller-manager# systemctl status kube-scheduler# systemctl status kubelet# systemctl status kube-proxy
若在 Master 节点组件的运行状态中,发现有不可忽略的致命错误提示信息,则严格按以下顺序重启各个组件 1 2 3 4 5 # systemctl restart kube-apiserver# systemctl restart kube-controller-manager# systemctl restart kube-scheduler# systemctl restart kubelet# systemctl restart kube-proxy
1 2 3 4 5 # systemctl status kube-apiserver# systemctl status kube-controller-manager# systemctl status kube-scheduler# systemctl status kubelet# systemctl status kube-proxy
管理 Node 节点的组件启动,以下所有操作分别在 Kubernetes 集群的所有 Node 节点上执行
1 2 # systemctl status kubelet# systemctl status kube-proxy
若在 Node 节点组件的运行状态中,发现有不可忽略的致命错误提示信息,则严格按以下顺序重启各个组件 1 2 # systemctl restart kubelet# systemctl restart kube-proxy
1 2 # systemctl status kubelet# systemctl status kube-proxy
彻底解决启动顺序问题 特别注意
若每次在所有 Kubernetes 集群服务器重启后(比如断电重启),都需要手动按顺序重启每个 Kubernetes 组件,这将非常繁琐。为了彻底解决这个问题,可以使用 Systemd 的 Requires、After、ExecStartPre 特性来控制多个服务之间的启动顺序。
生成 Health-Check 的证书,用于检测 API Server 的健康状态,以下所有操作都是仅在 Kubernetes 集群的 Master 节点上执行
在 Master 节点中,创建用于生成 Health-Check 证书的配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # cat > health-check-csr.json << EOF{ "CN" : "health-check" , "hosts" : [], "key" : { "algo" : "rsa" , "size" : 2048 }, "names" : [ { "O" : "system:health-checkers" } ] } EOF
在 Master 节点中,使用自签 CA 签发 Health-Check 证书 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # ls ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem kube-proxy.csr kube-proxy-csr.json kube-proxy-key.pem kube-proxy.pem server.csr server-csr.json server-key.pem server.pem # cfssl gencert -ca =ca.pem -ca -key=ca-key.pem -config =ca-config.json -profile =kubernetes health-check-csr.json | cfssljson -bare health-check# ls health-check*.pemhealth-check-key.pem health-check.pem # cfssl -certinfo -cert health-check.pem# openssl x509 -noout -text -in health-check.pem
在 Master 节点中,将上面生成的 Health-Check 证书拷贝到本地目录里面 1 2 # cp ~/tls/k8s/health*.pem /opt/kubernetes/ssl/
在 Master 节点中,将上面生成的 Health-Check 证书拷贝到所有 Node 节点里面 1 2 3 4 # scp ~/tls/k8s/health-check*.pem root@k8s-node1:/opt/kubernetes/ssl/# scp ~/tls/k8s/health-check*.pem root@k8s-node2:/opt/kubernetes/ssl/# scp ~/tls/k8s/health-check*.pem root@k8s-node3:/opt/kubernetes/ssl/
在 Master 节点中,创建 ClusterRole 的配置文件(仅授予只读的健康检测权限) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # cat > /opt/kubernetes/cfg/health-check-role.yaml << EOFapiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: health-check-role rules: - nonResourceURLs: - /healthz - /livez - /readyz verbs: - get EOF
在 Master 节点中,创建绑定用户到 ClusterRole 的配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # cat > /opt/kubernetes/cfg/health-check-binding.yaml << EOFapiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: health-check-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: health-check-role subjects: - kind: User name: health-check apiGroup: rbac.authorization.k8s.io EOF
在 Master 节点中,应用 YAML 配置文件 1 2 3 4 5 # kubectl apply -f /opt/kubernetes/cfg/health-check-role.yaml# kubectl apply -f /opt/kubernetes/cfg/health-check-binding.yaml
在所有节点(包括 Master 和 Node)中,分别验证 API Server 健康检测接口的调用(请自行更改 API Server 的 IP 和端口,切勿直接拷贝执行以下命令 ) 1 2 # curl --silent --fail --cacert /opt/kubernetes/ssl/ca.pem --cert /opt/kubernetes/ssl/health-check.pem --key /opt/kubernetes/ssl/health-check-key.pem https://192.168.2.191:6443/readyz
管理 Master 节点的组件启动,以下所有操作都是仅在 Kubernetes 集群的 Master 节点上执行
在 Master 节点中,重写(覆盖)API Server 服务的 Systemd 配置文件(请自行更改 Etcd 集群节点的 IP 和端口,切勿直接拷贝执行以下命令 ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # cat > /usr/lib/systemd/system/kube-apiserver.service << EOF[Unit] Description=Kubernetes API Server Documentation=https://github.com/kubernetes/kubernetes After=network-online.target [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-apiserver.conf ExecStartPre=/bin/bash -c 'until ETCDCTL_API=3 /opt/etcd/bin/etcdctl --endpoints=https://192.168.2.191:2379,https://192.168.2.112:2379,https://192.168.2.131:2379 --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem endpoint status --write-out=table | grep -v "^+" | grep -q " true "; do echo "Waiting for Etcd cluster to be ready..."; sleep 2; done && echo "Etcd cluster is now ready."' ExecStart=/opt/kubernetes/bin/kube-apiserver \$KUBE_APISERVER_OPTS Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF
1 2 3 4 5 6 7 8 # systemctl daemon-reload# systemctl restart kube-apiserver# systemctl status kube-apiserver
在 Master 节点中,重写(覆盖)Controller Manager 服务的 Systemd 配置文件(请自行更改 API Server 的 IP 和端口,切勿直接拷贝执行以下命令 ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # cat > /usr/lib/systemd/system/kube-controller-manager.service << EOF[Unit] Description=Kubernetes Controller Manager Documentation=https://github.com/kubernetes/kubernetes After=network-online.target [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-controller-manager.conf ExecStartPre=/bin/bash -c 'until curl --silent --fail --cacert /opt/kubernetes/ssl/ca.pem --cert /opt/kubernetes/ssl/health-check.pem --key /opt/kubernetes/ssl/health-check-key.pem https://192.168.2.191:6443/readyz > /dev/null 2>&1; do echo "Waiting for API Server to be ready..."; sleep 2; done && echo "API Server is now ready."' ExecStart=/opt/kubernetes/bin/kube-controller-manager \$KUBE_CONTROLLER_MANAGER_OPTS Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF
1 2 3 4 5 6 7 8 # systemctl daemon-reload# systemctl restart kube-controller-manager# systemctl status kube-controller-manager
在 Master 节点中,重写(覆盖)Scheduler 服务的 Systemd 配置文件(请自行更改 API Server 的 IP 和端口,切勿直接拷贝执行以下命令 ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # cat > /usr/lib/systemd/system/kube-scheduler.service << EOF[Unit] Description=Kubernetes Scheduler Documentation=https://github.com/kubernetes/kubernetes After=network-online.target [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-scheduler.conf ExecStartPre=/bin/bash -c 'until curl --silent --fail --cacert /opt/kubernetes/ssl/ca.pem --cert /opt/kubernetes/ssl/health-check.pem --key /opt/kubernetes/ssl/health-check-key.pem https://192.168.2.191:6443/readyz > /dev/null 2>&1; do echo "Waiting for API Server to be ready..."; sleep 2; done && echo "API Server is now ready."' ExecStart=/opt/kubernetes/bin/kube-scheduler \$KUBE_SCHEDULER_OPTS Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF
1 2 3 4 5 6 7 8 # systemctl daemon-reload# systemctl restart kube-scheduler# systemctl status kube-scheduler
在 Master 节点中,重写(覆盖)Kubelet 服务的 Systemd 配置文件(请自行更改 API Server 的 IP 和端口,切勿直接拷贝执行以下命令 ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # cat > /usr/lib/systemd/system/kubelet.service << EOF[Unit] Description=Kubernetes Kubelet Documentation=https://github.com/kubernetes/kubernetes After=network-online.target After=docker.service [Service] EnvironmentFile=/opt/kubernetes/cfg/kubelet.conf ExecStartPre=/bin/bash -c 'until curl --silent --fail --cacert /opt/kubernetes/ssl/ca.pem --cert /opt/kubernetes/ssl/health-check.pem --key /opt/kubernetes/ssl/health-check-key.pem https://192.168.2.191:6443/readyz > /dev/null 2>&1; do echo "Waiting for API Server to be ready..."; sleep 2; done && echo "API Server is now ready."' ExecStart=/opt/kubernetes/bin/kubelet \$KUBELET_OPTS Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
1 2 3 4 5 6 7 8 # systemctl daemon-reload# systemctl restart kubelet# systemctl status kubelet
在 Master 节点中,重写(覆盖)Kube-Proxy 服务的 Systemd 配置文件(请自行更改 API Server 的 IP 和端口,切勿直接拷贝执行以下命令 ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # cat > /usr/lib/systemd/system/kube-proxy.service << EOF[Unit] Description=Kubernetes Proxy Documentation=https://github.com/kubernetes/kubernetes After=network-online.target After=docker.service [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-proxy.conf ExecStartPre=/bin/bash -c 'until curl --silent --fail --cacert /opt/kubernetes/ssl/ca.pem --cert /opt/kubernetes/ssl/health-check.pem --key /opt/kubernetes/ssl/health-check-key.pem https://192.168.2.191:6443/readyz > /dev/null 2>&1; do echo "Waiting for API Server to be ready..."; sleep 2; done && echo "API Server is now ready."' ExecStart=/opt/kubernetes/bin/kube-proxy \$KUBE_PROXY_OPTS Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
1 2 3 4 5 6 7 8 # systemctl daemon-reload# systemctl restart kube-proxy# systemctl status kube-proxy
管理 Node 节点的组件启动,以下所有操作分别在 Kubernetes 集群的所有 Node 节点上执行
在所有 Node 节点中,重写(覆盖)Kubelet 服务的 Systemd 配置文件(请自行更改 API Server 的 IP 和端口,切勿直接拷贝执行以下命令 ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # cat > /usr/lib/systemd/system/kubelet.service << EOF[Unit] Description=Kubernetes Kubelet Documentation=https://github.com/kubernetes/kubernetes After=network-online.target After=docker.service [Service] EnvironmentFile=/opt/kubernetes/cfg/kubelet.conf ExecStartPre=/bin/bash -c 'until curl --silent --fail --cacert /opt/kubernetes/ssl/ca.pem --cert /opt/kubernetes/ssl/health-check.pem --key /opt/kubernetes/ssl/health-check-key.pem https://192.168.2.191:6443/readyz > /dev/null 2>&1; do echo "Waiting for API Server to be ready..."; sleep 2; done && echo "API Server is now ready."' ExecStart=/opt/kubernetes/bin/kubelet \$KUBELET_OPTS Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
1 2 3 4 5 6 7 8 # systemctl daemon-reload# systemctl restart kubelet# systemctl status kubelet
在所有 Node 节点中,重写(覆盖)Kube-Proxy 服务的 Systemd 配置文件(请自行更改 API Server 的 IP 和端口,切勿直接拷贝执行以下命令 ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # cat > /usr/lib/systemd/system/kube-proxy.service << EOF[Unit] Description=Kubernetes Proxy Documentation=https://github.com/kubernetes/kubernetes After=network-online.target After=docker.service [Service] EnvironmentFile=/opt/kubernetes/cfg/kube-proxy.conf ExecStartPre=/bin/bash -c 'until curl --silent --fail --cacert /opt/kubernetes/ssl/ca.pem --cert /opt/kubernetes/ssl/health-check.pem --key /opt/kubernetes/ssl/health-check-key.pem https://192.168.2.191:6443/readyz > /dev/null 2>&1; do echo "Waiting for API Server to be ready..."; sleep 2; done && echo "API Server is now ready."' ExecStart=/opt/kubernetes/bin/kube-proxy \$KUBE_PROXY_OPTS Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF
1 2 3 4 5 6 7 8 # systemctl daemon-reload# systemctl restart kube-proxy# systemctl status kube-proxy
Etcd 无法正常启动 Etcd 集群断电重启后,通过 systemctl status etcd 命令查看 Etcd 节点(比如:节点名称是 etcd-3,IP 是 192.168.2.131)的运行状态时,提示 Raft 同步失败(如下)。最有可能的原因是这个 Etcd 节点的数据目录损坏或不一致导致,解决方案如下: 1 2 3 4 5 8月 07 09:43:32 k8s-node2 etcd[6043]: {"level":"info","ts":"2025-08-07T09:43:32.007+0800","caller":"rafthttp/stream.go:425","msg":"established TCP streaming connection with remote peer","stream-reader-type":"stream MsgApp v2","local-member-id":"9cb4eb07d38510b5","remote-peer-id":"a00042f5519886e3"} 8月 07 09:43:32 k8s-node2 etcd[6043]: {"level":"info","ts":"2025-08-07T09:43:32.026+0800","caller":"etcdserver/server.go:715","msg":"initialized peer connections; fast-forwarding election ticks","local-member-id":"9cb4eb07d38510b5","forward-ticks":8,"forward-duration":"800ms","election-ticks":10,"election-timeout... 8月 07 09:43:38 k8s-node2 etcd[6043]: {"level":"warn","ts":"2025-08-07T09:43:38.988+0800","caller":"etcdserver/server.go:2065","msg":"failed to publish local member to cluster through raft","local-member-id":"9cb4eb07d38510b5","local-member-attributes":"{Name:etcd-3 ClientURLs:[https://192.168.2.131:2379]}","requ... 8月 07 09:43:45 k8s-node2 etcd[6043]: {"level":"warn","ts":"2025-08-07T09:43:45.988+0800","caller":"etcdserver/server.go:2065","msg":"failed to publish local member to cluster through raft","local-member-id":"9cb4eb07d38510b5","local-member-attributes":"{Name:etcd-3 ClientURLs:[https://192.168.2.131:2379]}","requ... Hint: Some lines were ellipsized, use -l to show in full.
在其他健康的 Etcd 节点上,查看 Etcd 集群的运行状态,可以发现缺少了 etcd-3 节点 1 2 3 4 5 6 +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS | +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | https://192.168.2.191:2379 | 9a8c44f9310511 | 3.4.9 | 5.0 MB | false | false | 369 | 314583 | 314583 | | | https://192.168.2.112:2379 | a00042f5519886e3 | 3.4.9 | 5.0 MB | true | false | 369 | 314583 | 314583 | | +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
通过其他健康的 Etcd 节点,查看 Etcd 集群的成员列表,虽然 etcd-3 节点无法正常启动,但依旧可以看到 etcd-3 节点 1 2 3 9a8c44f9310511, started, etcd-1, https://192.168.2.191:2380, https://192.168.2.191:2379, false 5e5716f1d7eba042, started, etcd-3, https://192.168.2.131:2380, https://192.168.2.131:2379, false a00042f5519886e3, started, etcd-2, https://192.168.2.112:2380, https://192.168.2.112:2379, false
在其他健康的 Etcd 节点上,将无法正常启动的 etcd-3 节点移除出集群 在无法正常启动的 etcd-3 节点上,删除本地的 Etcd 数据目录 1 2 3 4 5 # systemctl stop etcd# rm -rf /var/lib/etcd/default.etcd
通过其他健康的 Etcd 节点,将无法正常启动的 etcd-3 节点重新加入 Etcd 集群 执行命令将 etcd-3 节点重新加入 Etcd 集群后,会输出以下内容 1 2 3 4 ETCD_NAME="etcd-3" ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.2.191:2380,etcd-3=https://192.168.2.131:2380,etcd-2=https://192.168.2.112:2380" ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.2.131:2380" ETCD_INITIAL_CLUSTER_STATE="existing"
在无法正常启动的 etcd-3 节点上,更改本地的 Etcd 配置文件,将 ETCD_INITIAL_CLUSTER_STATE 改为 existing,并重启 Etcd 服务(如果上面的操作都生效了,那么这时候 Etcd 服务应该就可以正常启动了) 1 2 3 4 5 6 7 8 # sed -i 's/^ETCD_INITIAL_CLUSTER_STATE="new"/ETCD_INITIAL_CLUSTER_STATE="existing"/' /opt/etcd/cfg/etcd.conf# systemctl restart etcd# systemctl status etcd
在之前无法正常启动的 etcd-3 节点上,还原本地的 Etcd 配置文件,将 ETCD_INITIAL_CLUSTER_STATE 改为 new,并重启 Etcd 服务 1 2 3 4 5 6 7 8 # sed -i 's/^ETCD_INITIAL_CLUSTER_STATE="existing"/ETCD_INITIAL_CLUSTER_STATE="new"/' /opt/etcd/cfg/etcd.conf# systemctl restart etcd# systemctl status etcd
在其他健康的 Etcd 节点上,再次查看 Etcd 集群的运行状态,可以发现 etcd-3 节点(会获得新的节点 ID)正常运行 1 2 3 4 5 6 7 +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS | +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | https://192.168.2.191:2379 | 9a8c44f9310511 | 3.4.9 | 5.0 MB | false | false | 369 | 323848 | 323848 | | | https://192.168.2.112:2379 | a00042f5519886e3 | 3.4.9 | 5.0 MB | true | false | 369 | 323848 | 323848 | | | https://192.168.2.131:2379 | d282ba1f56161393 | 3.4.9 | 5.0 MB | false | false | 369 | 323848 | 323848 | | +----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
问题分析与总结
ETCD_INITIAL_CLUSTER_STATE="new" 表示 Etcd 节点将以 "全新集群" 的方式启动,此时它会根据 ETCD_INITIAL_CLUSTER 中的成员列表初始化集群,并假设 Raft 日志为空。如果该节点此前已经加入过集群,当再次使用 new 启动时,就会导致与现有集群中的成员 ID 冲突,触发 Raft 同步失败。而 ETCD_INITIAL_CLUSTER_STATE="existing" 则用于已存在的集群节点,表示该节点将从其他成员处拉取 Raft 日志进行同步。因此,当 etcd-3 节点已经出现在 Etcd 集群的成员列表中时,配置 ETCD_INITIAL_CLUSTER_STATE="new" 是错误的,应该改为 existing,否则该节点在重启时会错误地尝试初始化一个新的集群,导致 Raft 冲突和无法加入集群。简而言之,在初次搭建 Etcd 集群时,ETCD_INITIAL_CLUSTER_STATE="new" 是正确的,但在 Etcd 节点重启或者重新加入集群时,如果这个值仍然是 new,那就可能会引发严重的问题。
Kubelet 无法正常启动 Kubelet 服务断电重启后,通过 systemctl status kubelet 命令查看运行状态时,服务没有处于 active (running) 状态,并且有各种错误信息,尝试多种方法依旧无法正常重启。解决方案如下:
在 Etcd 集群的所有节点上,分别删除 Etcd 的数据目录(切记谨慎操作,Etcd 数据删除后,已有的容器都可能无法正常启动,并且整个 Kubernetes 集群都可能需要重建 ),并重启 Etcd 服务 1 2 3 4 5 6 7 8 9 10 11 # systemctl stop etcd# rm -rf /var/lib/etcd/default.etcd# systemctl start etcd# systemctl status etcd
1 2 3 4 5 # systemctl restart kube-apiserver# systemctl restart kube-controller-manager# systemctl restart kube-scheduler# systemctl restart kubelet# systemctl restart kube-proxy
1 2 # systemctl restart kubelet# systemctl restart kube-proxy
在 Master 节点中,批准 Kubelet 证书签名请求并允许 Node 节点加入 Kubernetes 集群 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # kubectl get csrNAME AGE SIGNERNAME REQUESTOR CONDITION node-csr-BRKQdidf63HB1ZjRZLvYKeNxW0p0vSN3UG_Av8XJO3I 95s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending node-csr-Lcn5xLuMioS9H1aa8k9qATIAAdNIhdlT_OpCco4ePoU 97s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending node-csr-jDiiH16kcOjiYPjWysqaKbudnf1z948P937PHRU5VzQ 94s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending # kubectl certificate approve node-csr-BRKQdidf63HB1ZjRZLvYKeNxW0p0vSN3UG_Av8XJO3I# kubectl certificate approve node-csr-Lcn5xLuMioS9H1aa8k9qATIAAdNIhdlT_OpCco4ePoU# kubectl certificate approve node-csr-jDiiH16kcOjiYPjWysqaKbudnf1z948P937PHRU5VzQ# kubectl get nodeNAME STATUS ROLES AGE VERSION k8s-master Ready <none> 23h v1.19.10 k8s-node1 Ready <none> 173m v1.19.10 k8s-node2 Ready <none> 172m v1.19.10 k8s-node3 Ready <none> 172m v1.19.10
Kubelet 发送 CSR 请求失败 Kubelet 服务启动后,无法正常向 API Server 发起 CSR(证书签名请求),解决方案如下:
在当前节点上,清理 Kubelet 运行时产生的文件,并重启 Kubelet 服务 1 2 3 4 5 6 7 8 9 10 11 12 13 # systemctl stop kubelet# rm -rf /opt/kubernetes/ssl/kubelet*# rm -rf /opt/kubernetes/cfg/kubelet.kubeconfig# ls /opt/kubernetes/ssl/ca*.pem/opt/kubernetes/ssl/ca-key.pem /opt/kubernetes/ssl/ca.pem # systemctl start kubelet
在 Master 节点上执行以下命令,批准 Kubelet 证书签名请求并允许 Node 节点加入 Kubernetes 集群 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # kubectl get csrNAME AGE SIGNERNAME REQUESTOR CONDITION node-csr-BRKQdidf63HB1ZjRZLvYKeNxW0p0vSN3UG_Av8XJO3I 95s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending node-csr-Lcn5xLuMioS9H1aa8k9qATIAAdNIhdlT_OpCco4ePoU 97s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending node-csr-jDiiH16kcOjiYPjWysqaKbudnf1z948P937PHRU5VzQ 94s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending # kubectl certificate approve node-csr-BRKQdidf63HB1ZjRZLvYKeNxW0p0vSN3UG_Av8XJO3I# kubectl certificate approve node-csr-Lcn5xLuMioS9H1aa8k9qATIAAdNIhdlT_OpCco4ePoU# kubectl certificate approve node-csr-jDiiH16kcOjiYPjWysqaKbudnf1z948P937PHRU5VzQ# kubectl get nodeNAME STATUS ROLES AGE VERSION k8s-master Ready <none> 23h v1.19.10 k8s-node1 Ready <none> 173m v1.19.10 k8s-node2 Ready <none> 172m v1.19.10 k8s-node3 Ready <none> 172m v1.19.10
CNI 网络插件无法正常部署 在 Kubernetes 集群中部署 CNI 网络插件(Flannel)后,通过 systemctl status kubelet 命令查看 Kubelet 的运行状态时,出现以下镜像错误信息,导致 CNI 网络插件(Flannel)无法正常部署 1 2 3 4 k8s-node1 kubelet[30963]: E0805 19:42:19.257544 30963 kuberuntime_sandbox.go:69] CreatePodSandbox for pod "kube-flannel-ds-g5q5l_kube-flannel(0c854c29-c158-45ca-a6c4-5d9ee7d9879b)" failed: rpc error: code = Unknown desc = failed pulling image "lizhenliang/pause-amd64:3.0": Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) k8s-node1 kubelet[14733]: E0805 21:34:10.987901 14733 kubelet.go:2134] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized k8s-node1 kubelet[14733]: E0805 21:34:15.991080 14733 kubelet.go:2134] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized k8s-node1 kubelet[14733]: E0805 21:34:21.015325 14733 kubelet.go:2134] Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
这是因为 Kubelet 会根据 kubelet.conf 配置文件,从 Docker Hub 拉取 lizhenliang/pause-amd64:3.0 镜像;若网络访问超时,就会出现上述错误信息。解决方案有这几种:手动拉取镜像、配置 Docker 代理、更换 Docker 镜像源,最后重启 Kubelet 服务 1 2 3 4 5 6 7 8 # systemctl stop kubelet# docker pull lizhenliang/pause-amd64:3.0# systemctl start kubelet
当 Kubelet 服务成功启动,且 CNI 网络插件(Flannel)成功部署后,可以发现当前节点会有以下 Docker 镜像(缺一不可) 1 2 3 4 5 6 # docker imagesREPOSITORY TAG IMAGE ID CREATED SIZE ghcr.io/flannel-io/flannel v0.27.2 f2b60fe541ef 2 weeks ago 90MB ghcr.io/flannel-io/flannel-cni-plugin v1.7.1-flannel1 cca2af40a4a9 2 months ago 10.7MB lizhenliang/pause-amd64 3.0 99e59f495ffa 9 years ago 747kB
参考博客