K8S 搭建指南(一)

Posted by Jason Lee on 2026-05-09

K8S 搭建指南


第一部分 前置操作

设置静态IP地址

使用mutui的方法重置网络配置

1
2
3
4
5
6
7
8
9
10
11
方法三:使用 nmtui 图形化工具 (最简单) 
在终端输入:nmtui

选择 "Edit a connection"
选择你的网卡并按 Enter。
将 IPv4 CONFIGURATION 从 <Automatic> 改为 <Manual>。
点击 <Show> 展开,填写 Addresses (如 192.168.1.100/24)、Gateway 和 DNS servers。
一路点击 OK 退出。
回到主菜单选择 "Activate a connection",对该网卡进行“禁用再启用”以生效。
意要删除uuid 不能有两个一样的uuid 目录为
RHEL 9 默认将网络配置存储在 /etc/NetworkManager/system-connections/ 目录下,文件格式 为 .nmconnection

设置主机名

1
sudo hostnamectl set-hostname "k8snode1" && exec bash

添加hosts

1
2
3
4
#/etc/hosts 文件添加以下内容:
192.168.3.113 k8snode1
192.168.3.114 k8snode2
192.168.3.115 k8snode3

永久关闭 Swap

1
2
3
4
5
6
7
#  原因:禁用交换空间对于 Kubernetes 配置至关重要,因为它能确保资源管理的可预测性。Kubernetes 依赖于节点提供的精确 CPU 和内存报告,而启用交换空间会导致内存使用不可预测,从而影响调度和稳定性。kubelet 需要精确控制内存;启用交换空间后,它难以评估可用内存,导致性能下降甚至节点故障。此外,Kubernetes 使用服务质量 (QoS) 等级来优先分配容器间的资源,但交换空间会干扰这​​一过程,尤其对于“保证优先级”容器而言。Kubernetes 团队强制要求在安装过程中禁用交换空间,因为启用它可能会导致配置警告或失败。

free -h
# 如果 swap 不为 0:
sudo sed -i '/swap/d' /etc/fstab
sudo swapoff -a

设置 SELinux 为宽容模式

1
2
3
4
# SELinux 是 Linux 中的一个安全模块,在 Kubernetes 集群中通常设置为宽容模式,以保证兼容性、便于故障排除并避免运维问题。宽容模式允许容器运行时和组件不受限制地运行,记录安全违规行为而不是阻止操作,并防止 SELinux 干扰 Kubernetes Pod 和服务所需的网络和文件访问。
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/config/selinux

停止防火墙 (内网环境建议直接关闭,生产环境需按需开放端口)

1
2
sudo systemctl stop firewalld
sudo systemctl disable firewalld

开放防火墙的配置

  • 6443/tcp :Kubernetes API 服务器
  • 2379–2380/tcp :etcd 服务器(用于存储集群数据)
  • 10250–10252/tcp :kubelet API 和控制平面服务
  • 10257–10259/tcp :调度器和控制器管理器
  • 179/tcp :BGP(用于网络插件,如果使用)
  • 4789/udp :VXLAN(用于 pod 网络,如果使用覆盖网络)
1
2
3
4
5
6
7
8
# master 节点开启
$ sudo firewall-cmd --permanent --add-port={6443,2379,2380,10250,10251,10252,10257,10259,179}/tcp
$ sudo firewall-cmd --permanent --add-port=4789/udp
$ sudo firewall-cmd --reload
# work 节点开启
$ sudo firewall-cmd --permanent --add-port={179,10250,30000-32767}/tcp
$ sudo firewall-cmd --permanent --add-port=4789/udp
$ sudo firewall-cmd --reload

加载内核模块

添加内核模块和参数对于 Kubernetes 管理跨节点的网络流量和容器通信至关重要。overlay 模块支持分层文件系统,从而实现容器内的高效存储;而 br_netfilter 则允许在桥接接口之间进行网络过滤。这些模块确保了 Pod 间网络连接的正常进行,并符合 Kubernetes 的网络策略,从而避免性能问题并增强功能。
为什么要这么做?原因:这些模块是 Kubernetes 所需的网络功能的基础,确保它们在系统启动时自动加载可以避免网络问题。

1
2
3
4
5
6
7
8
9
10
在 Kubernetes 中,Pod 通常运行在虚拟网络中(比如 10.244.x.x)。当 Node A 上的 Pod 想要访问 Node B 上的 Pod 时,数据包必须先经过 Node A 的物理网卡,再通过宿主机的网络层转发出去。

如果 net.ipv4.ip_forward 未开启,Node A 的 Linux 内核在发现数据包的目标 IP 不是自己的物理 IP 时,会立即拦截并丢弃,导致 Pod 之间无法联网。

桥接网络与防火墙 (Bridge-netfilter)
K8S 常用网桥(Bridge)来连接容器网卡。

br_netfilter 模块的作用是让网桥模式下的数据包也能经过 iptables(防火墙规则)的处理。

Kubernetes 的 Service (ClusterIP) 机制高度依赖 iptables 或 ipvs 进行负载均衡。如果不开启这个转发模块,网桥上的流量就会绕过安全检查和路由规则,导致 Service 无法访问。
  • net.bridge.bridge-nf-call-iptables = 1 允许桥接流量通过 iptables,这对于节点和 pod 之间的路由至关重要。
  • net.ipv4.ip_forward = 1 允许跨节点接口进行 pod 到 pod 通信的 IP 转发。
  • net.bridge.bridge-nf-call-ip6tables = 1 通过 ip6tables 管理桥接接口上的 IPv6 流量,这对于 IPv6 网络环境非常重要。
1
2
3
4
5
6
7
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# 生效参数
sudo sysctl --system
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
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
EOF

# 加载模块
modprobe -- br_netfilter
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack

# 验证是否加载成功
lsmod | grep -e br_netfilter -e ip_vs

# 增加ip转发
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

# 立即生效
sysctl --system

# 增加链接
# /etc/security/limits.conf

* soft nofile 65536
* hard nofile 65536
* soft nproc 65536
* hard nproc 65536

dnf 安装 containerd

1
2
3
4
5
6
7
8
9
10
11
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y containerd.io

containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

# containerd config.toml 中应有:
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
SystemdCgroup = true
# 还应该有 /var/lib/kubelet/config.yaml 或 kubeadm 初始化配置中: 这个下面就解释
# cgroupDriver: systemd

查看 containerd 配置的 sandbox image

1
2
3
4
grep -i sandbox /etc/containerd/config.toml
# 应该与 kubeadm 使用的一致,建议明确设置:
[plugins.'io.containerd.cri.v1.images']
sandbox_image = "registry.k8s.io/pause:3.10.1"

containerd 源目录

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
40
41
42
43
44
45
grep -A5 'config_path' /etc/containerd/config.toml
[plugins.'io.containerd.cri.v1.images'.registry]
config_path = "/etc/containerd/certs.d"

mkdir -p /etc/containerd/certs.d/registry.k8s.io
mkdir -p /etc/containerd/certs.d/k8s.gcr.io
mkdir -p /etc/containerd/certs.d/docker.io

cat > /etc/containerd/certs.d/registry.k8s.io/hosts.toml << 'EOF'
server = "https://registry.k8s.io"

[host."https://m.daocloud.io/registry.k8s.io"]
capabilities = ["pull", "resolve"]

[host."https://k8s.m.daocloud.io"]
capabilities = ["pull", "resolve"]
EOF

cat > /etc/containerd/certs.d/docker.io/hosts.toml << 'EOF'
server = "https://docker.io"

[host."https://mirror.baidubce.com"]
capabilities = ["pull", "resolve"]

[host."https://docker.m.daocloud.io"]
capabilities = ["pull", "resolve"]

[host."https://dockerproxy.com"]
capabilities = ["pull", "resolve"]
EOF

cat > /etc/containerd/certs.d/k8s.gcr.io/hosts.toml << 'EOF'
server = "https://k8s.gcr.io"

[host."https://registry.aliyuncs.com/google_containers"]
capabilities = ["pull", "resolve"]

EOF

# 重启容器
sudo systemctl restart containerd
sudo systemctl enable containerd

# 监控日志
sudo journalctl -u containerd -f

安装 kubernetes.repo

如 Kubeadm、kubectl 和 kubelet)在 Rocky Linux 9 或 AlmaLinux 9 的默认软件包存储库中不可用。因此,要安装这些工具,请在所有节点上添加以下存储库。

1
2
3
4
5
6
7
8
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key
EOF

取消代理

1
2
3
# 临时取消代理再执行 init(推荐) 否则会get pods 有时候会出问题
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
export no_proxy="127.0.0.1,localhost,10.0.0.0/8,192.168.0.0/16"

第二部分,安装K8s


使用ADM安装

安装 kubelet kubeadm kubectl

1
2
# 每个节点运行
sudo dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

k8s pull 一下镜像

1
2
3
4
kubeadm config images pull
kubeadm config images list
# 或者 执行
crictl pull registry.k8s.io/pause:3.10.1 s

初始化平面

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
# 初始化平面
kubeadm init --control-plane-endpoint=k8snode1 --pod-network-cidr=10.244.0.0/16 --v=5

# 输出如下
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

kubeadm join k8snode1:6443 --token g0i3rg.mp1gc1dw1fmtqcib \
--discovery-token-ca-cert-hash sha256:1d1c772ec0486825e21c26dafca5eb139570af3caf062dcee25e9decebf161c2 \
--control-plane

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join k8snode1:6443 --token g0i3rg.mp1gc1dw1fmtqcib \
--discovery-token-ca-cert-hash sha256:1d1c772ec0486825e21c26dafca5eb139570af3caf062dcee25e9decebf161c2

配置kube config

1
2
3
4
# 所有节点都应该与master 有对应的文件,淡然可以看用户对应的config role 等等酌情配置config
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

安装CNI网络插件

1
2
3
ls /etc/cni/net.d/ #验证 CNI 配置是否存在
# 安装的calico CNI
kubectl apply -f "https://docs.projectcalico.org/manifests/calico.yaml"

检查安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#第一步:检查控制平面 Pod
kubectl get pods -n kube-system
#第二步:检查节点状态
kubectl get nodes
#第三步:检查组件健康状态
kubectl get componentstatuses
#第四步:检查 systemd 服务
systemctl status kubelet
systemctl status containerd
## 节点
kubectl get nodes -o wide
# 所有系统 Pod
kubectl get pods -n kube-system -o wide
# CNI 网络插件
kubectl get pods -n kube-flannel
# 查看节点详细事件(有无报错)
kubectl describe node k8snode1 | tail -20
#第一步:apiserver 容器是否在运行
crictl ps -a | grep apiserver
#第二步:检查 kubelet
systemctl status kubelet
journalctl -xeu kubelet --no-pager | tail -20
#第三步:检查 6443 端口
bashss -tlnp | grep 6443

K8s 二进制的安装

架构图如下


配置hosts文件

1
2
3
4
5
6
7
8
9
10
192.168.3.120  k8smaster  (VIP)
192.168.3.121 k8snode1 k8smaster1 etcd1
192.168.3.122 k8snode2 k8smaster2 etcd2
192.168.3.123 k8snode3 k8smaster3 etcd3

192.168.3.124 k8snode4 k8worker1
192.168.3.125 k8snode5 k8worker2
192.168.3.126 k8snode6 k8worker3
192.168.3.127 k8snode7 k8worker4
...

生成ca.key 和 ca.cert

1
2
3
4
5
6
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/C=CN/ST=Beijing/L=Beijing/O=k8s/OU=System/CN=kubernetes" -days 3650 -out ca.crt

# 注意这里的 ca证书的 CN kubernetes 当然可以参考一下的链接来配置CN名字

chmod 755 ./*

可以参考 https://kubernetes.io/zh-cn/docs/setup/best-practices/certificates/

生成etcd证书

生成etcd_ssl.cnf

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
[ req ]
# 证书请求时使用的扩展项指向 它告诉 OpenSSL:“在我生成请求文件(CSR)时,请去下面找到 [ v3_req ] 这个标签,把它里面的东西带上。”如果没有这一行,你写的 IP 地址就会被忽略。
req_extensions = v3_req
# 证书的主体信息段落名称 这是安全性的“安全阀”。它规定了持有这个证书的人没有资格再去充当 CA 给别人发证书。在 K8S 中,除了根 CA,其他所有组件证书都应该是 FALSE。
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
# 这里留空,我们在命令行用 -subj 直接指定,避免交互式输入
[ v3_req ]
# 基础约束:声明这不是一个 CA 证书(不能再去签发别人)
basicConstraints = CA:FALSE
# 密钥用法:不可抵赖、数字签名、密钥加密
# digitalSignature: 允许用于数字签名(验证身份)。
# keyEncipherment: 允许用于加密通话(保护数据不被偷听)。
# 这是 TLS 握手的基础。
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# 使用者可选名称:这是 K8S 最看重的字段,定义了哪些 IP/域名可以合法使用此证书
subjectAltName = @alt_names

[ alt_names ]
# 你的三台机器 IP 列表
IP.1 = 127.0.0.1
IP.2 = 192.168.3.121
IP.3 = 192.168.3.122
IP.4 = 192.168.3.123
# 建议加上主机名,防止某些组件通过主机名访问
DNS.1 = localhost
DNS.2 = k8snode1
DNS.3 = k8smaster1
DNS.4 = etcd1
DNS.5 = k8snode2
DNS.6 = k8smaster2
DNS.7 = etcd2
DNS.8 = k8snode3
DNS.9 = k8smaster3
DNS.10 = etcd3

生成 etcd 服务端私钥

1
2
3
4
openssl genrsa -out etcd_server.key 2048
# genrsa: 产生 RSA 算法的私钥。
# -out: 指定输出文件名。
# 2048: 私钥长度为 2048 位,这是目前的标准安全强度。

生成证书签名请求 (CSR)

1
2
3
4
5
6
7
8
9
10
openssl req -new -key etcd_server.key \
-config etcd_ssl.cnf \
-subj "/CN=etcd-server" \
-out etcd_server.csr

# req -new: 创建一个新的证书请求。
# -key: 指定使用刚才生成的私钥进行签名,证明你是这把钥匙的主人。
# -config: 核心参数。读取我们写的 .cnf 文件,将 v3_req 里的扩展属性(如 SAN)打包进请求里。
# -subj: 证书的主体信息。CN (Common Name) 设为 etcd-server。由于我们用了配置文件的扩展项,所以这里的 CN 其实只是个名字。

签发证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
openssl x509 -req -in etcd_server.csr \
-CA ca.crt -CAkey ca.key \
-CAcreateserial \
-days 36500 \
-extensions v3_req \
-extfile etcd_ssl.cnf \
-out etcd_server.crt

# x509: 说明要生成符合 x509 标准的证书。
# -req -in: 输入刚才生成的请求文件 (.csr)。
# -CA / -CAkey: 指定用哪个“公章”和“印模”来签名。这里用你之前生成的根证书。
# -CAcreateserial: 自动创建一个序列号文件(ca.srl),用于记录已签发的证书序号。
# -days 36500: 证书有效期(100年)。二进制安装通常设得很长,避免后期证书过期的麻烦。
# -extensions v3_req: 关键。明确告知 OpenSSL 在签名时,必须把配置文件中 [v3_req] 段落下的 IP 地址等扩展信息写进最终的 .crt 文件里。
# -extfile: 指定扩展信息从哪个文件里读。

下载安装 etcd

下载

1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir ~/etcd.download
cd ~/etcd.download
# 设定版本变量
ETCD_VER=v3.6.10
# 下载 arm64 版本的压缩包
# 注意:M1 虚拟机必须使用 arm64 路径
wget https://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-arm64.tar.gz
# 解压文件
tar xzvf etcd-${ETCD_VER}-linux-arm64.tar.gz
cp ./etcd-${ETCD_VER}-linux-arm64/etcd/* /usr/local/bin/
cp ./etcd-${ETCD_VER}-linux-arm64/etcdctl/* /usr/local/bin/

etcd --version

分发证书

1
2
3
4
5
6
7
8
9
10
11
#创建目录
# 创建数据存放目录
sudo mkdir -p /etc/etcd/data
# 创建配置目录(之前签发的证书可以放这里)
sudo mkdir -p /etc/etcd/ssl
#node1
scp ca.crt etcd_server.crt etcd_server.key root@k8snode2:/etc/etcd/ssl/
scp ca.crt etcd_server.crt etcd_server.key root@k8snode3:/etc/etcd/ssl/
# 建议创建一个 etcd 专用用户(可选,但更安全)
# sudo useradd -r -s /sbin/nologin etcd
# sudo chown -R etcd:etcd /var/lib/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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# 创建配置文件 在k8smaster1 k8snode1
cat <<EOF | sudo tee /etc/etcd/etcd.conf
# [Member]
ETCD_NAME="etcd-1"
ETCD_DATA_DIR="/etc/etcd/data"
# 监听地址建议使用 IP 以防 DNS 解析服务未启动导致监听失败
ETCD_LISTEN_PEER_URLS="https://192.168.3.121:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.3.121:2379,https://127.0.0.1:2379"

# [Clustering]
# 广播地址使用 hosts 中的域名
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://etcd1:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://etcd1:2379"
ETCD_INITIAL_CLUSTER="etcd-1=https://etcd1:2380,etcd-2=https://etcd2:2380,etcd-3=https://etcd3:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

# [Security]
ETCD_CERT_FILE="/etc/etcd/ssl/etcd_server.crt"
ETCD_KEY_FILE="/etc/etcd/ssl/etcd_server.key"
ETCD_TRUSTED_CA_FILE="/etc/etcd/ssl/ca.crt"
ETCD_CLIENT_CERT_AUTH="true"
ETCD_PEER_CERT_FILE="/etc/etcd/ssl/etcd_server.crt"
ETCD_PEER_KEY_FILE="/etc/etcd/ssl/etcd_server.key"
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/ssl/ca.crt"
ETCD_PEER_CLIENT_CERT_AUTH="true"

# [Logging]
ETCD_LOGGER="zap"
ETCD_LOG_OUTPUTS="stderr"
EOF
# 创建配置文件 在k8smaster2 k8snode2
cat <<EOF | sudo tee /etc/etcd/etcd.conf
# [Member]
ETCD_NAME="etcd-2"
ETCD_DATA_DIR="/etc/etcd/data"
ETCD_LISTEN_PEER_URLS="https://192.168.3.122:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.3.122:2379,https://127.0.0.1:2379"

# [Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://etcd2:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://etcd2:2379"
ETCD_INITIAL_CLUSTER="etcd-1=https://etcd1:2380,etcd-2=https://etcd2:2380,etcd-3=https://etcd3:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

# [Security]
ETCD_CERT_FILE="/etc/etcd/ssl/etcd_server.crt"
ETCD_KEY_FILE="/etc/etcd/ssl/etcd_server.key"
ETCD_TRUSTED_CA_FILE="/etc/etcd/ssl/ca.crt"
ETCD_CLIENT_CERT_AUTH="true"
ETCD_PEER_CERT_FILE="/etc/etcd/ssl/etcd_server.crt"
ETCD_PEER_KEY_FILE="/etc/etcd/ssl/etcd_server.key"
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/ssl/ca.crt"
ETCD_PEER_CLIENT_CERT_AUTH="true"

# [Logging]
ETCD_LOGGER="zap"
ETCD_LOG_OUTPUTS="stderr"
EOF

# 创建配置文件 在k8smaster3 k8snode3
cat <<EOF | sudo tee /etc/etcd/etcd.conf
# [Member]
ETCD_NAME="etcd-3"
ETCD_DATA_DIR="/etc/etcd/data"
ETCD_LISTEN_PEER_URLS="https://192.168.3.123:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.3.123:2379,https://127.0.0.1:2379"

# [Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://etcd3:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://etcd3:2379"
ETCD_INITIAL_CLUSTER="etcd-1=https://etcd1:2380,etcd-2=https://etcd2:2380,etcd-3=https://etcd3:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

# [Security]
ETCD_CERT_FILE="/etc/etcd/ssl/etcd_server.crt"
ETCD_KEY_FILE="/etc/etcd/ssl/etcd_server.key"
ETCD_TRUSTED_CA_FILE="/etc/etcd/ssl/ca.crt"
ETCD_CLIENT_CERT_AUTH="true"
ETCD_PEER_CERT_FILE="/etc/etcd/ssl/etcd_server.crt"
ETCD_PEER_KEY_FILE="/etc/etcd/ssl/etcd_server.key"
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/ssl/ca.crt"
ETCD_PEER_CLIENT_CERT_AUTH="true"

# [Logging]
ETCD_LOGGER="zap"
ETCD_LOG_OUTPUTS="stderr"
EOF

安装服务(k8snode1-3三台节点同时进行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 安装 service
#/usr/lib/systemd/system/etcd.service:
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
# 删除了 ETCD_UNSUPPORTED_ARCH 环境变量
EnvironmentFile=/etc/etcd/etcd.conf
ExecStart=/usr/local/bin/etcd
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

启动服务(k8snode1-3三台节点同时进行)

1
2
3
4
5
6
7
8
9
10
# 重启
systemctl daemon-reload
systemctl restart etcd
systemctl enable etcd
# 检查状态
ETCDCTL_API=3 etcdctl --endpoints="https://k8snode1:2379,https://k8snode2:2379,https://k8snode3:2379" \
--cacert=/etc/etcd/ssl/ca.crt \
--cert=/etc/etcd/ssl/etcd_server.crt \
--key=/etc/etcd/ssl/etcd_server.key \
endpoint health -w table

搭建K8S API-server

下载最新的安装包(k8snode1-3)

1
2
3
4
mkdir -P /root/kube/
cd /root/kube/
wget https://dl.k8s.io/v1.36.0/kubernetes-server-linux-arm64.tar.gz
tar -xzvf kubernetes-server-linux-arm64.tar.gz

制作安装和移除脚本

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
cat <<'EOF' > /root/kube/k8s_manager.sh
#!/bin/bash
# 配置路径
BIN_DIR="/root/kube/kubernetes/server/bin"
TARGET_DIR="/usr/local/bin"

# 核心组件列表
K8S_BINS=(kube-apiserver kube-controller-manager kube-scheduler kubelet kube-proxy kubectl)

# 安装函数
install_bins() {
echo "--- 正在开始安装核心组件 ---"
for bin in "${K8S_BINS[@]}"; do
if [ -f "$BIN_DIR/$bin" ]; then
cp "$BIN_DIR/$bin" "$TARGET_DIR/"
chmod +x "$TARGET_DIR/$bin"
echo "[成功] 已安装: $bin"
else
echo "[错误] 找不到源文件: $BIN_DIR/$bin"
fi
done
echo "--- 安装完成 ---"
}

# 卸载函数
uninstall_bins() {
echo "--- 正在开始删除核心组件 ---"
for bin in "${K8S_BINS[@]}"; do
if [ -f "$TARGET_DIR/$bin" ]; then
rm -f "$TARGET_DIR/$bin"
echo "[成功] 已删除: $bin"
else
echo "[跳过] $TARGET_DIR/$bin 不存在"
fi
done

# 询问是否清理运行中产生的配置和数据
read -p "是否同时清理 /etc/kubernetes 目录及运行数据? (y/n): " confirm
if [[ "$confirm" == "y" ]]; then
echo "正在清理配置与数据..."
rm -rf /etc/kubernetes
rm -rf /var/lib/kubelet
rm -rf /var/log/kubernetes
echo "[完成] 已清理相关目录"
fi
echo "--- 卸载完成 ---"
}

# 主逻辑控制
case "$1" in
install)
install_bins
;;
uninstall)
uninstall_bins
;;
*)
echo "使用方法: $0 {install|uninstall}"
exit 1
;;
esac
EOF

# 赋予执行权限
chmod +x /root/kube/k8s_manager.sh
sh /root/kube/k8s_manager.sh install

生成CA 证书 (k8snode1)

生成cnf文件 (k8snode1)

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
[ req ]
req_extensions = v3_req
distinguished_name = req_distinguished_name

[ req_distinguished_name ]

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[ alt_names ]
# --- DNS 别名 ---
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster.local
DNS.5 = k8smaster
DNS.6 = k8smaster1
DNS.7 = k8smaster2
DNS.8 = k8smaster3

# --- IP 地址 ---
IP.1 = 127.0.0.1
# 核心:负载均衡 VIP
IP.2 = 192.168.3.120
# 三个物理 Master 节点的 IP
IP.3 = 192.168.3.121
IP.4 = 192.168.3.122
IP.5 = 192.168.3.123
# Cluster IP 网段的首地址 (用于集群内部 Pod 访问)
IP.6 = 10.96.0.1

签发证书 (k8snode1)

1
2
# 1. 生成私钥
openssl genrsa -out kube-apiserver.key 2048

生成签名请求 (CSR)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 注意:CN 必须是 kubernetes
openssl req -new -key kube-apiserver.key \
-config kube-apiserver-ssl.cnf \
-subj "/CN=kubernetes" \
-out kube-apiserver.csr

### 使用根 CA 签发 (有效期 100 年)
openssl x509 -req -in kube-apiserver.csr \
-CA ca.crt -CAkey ca.key \
-CAcreateserial \
-days 36500 \
-extensions v3_req \
-extfile kube-apiserver-ssl.cnf \
-out kube-apiserver.crt

生成topken (k8snode1)

vim /root/kube/gen_token.sh

1
2
3
4
5
6
7
8
TOKEN=$(openssl rand -hex 16)
# 写入文件,格式为:token,用户名,UID,用户组
cat <<EOF > /etc/kubernetes/ssl/token.csv
${TOKEN},kubelet-bootstrap,10001,"system:bootstrappers"
EOF

chmod +x /root/kube/gen_token.sh
sh /root/kube/gen_token.sh

分发证书和Token (k8snode1)

cd /etc/kubernetes/ssl/

1
2
3
# 在 k8snode1 上分发到 node2 和 node3
scp ca.crt ca.key kube-apiserver.crt kube-apiserver.key token.csv root@192.168.3.122:/etc/kubernetes/ssl/
scp ca.crt ca.key kube-apiserver.crt kube-apiserver.key token.csv root@192.168.3.123:/etc/kubernetes/ssl/

Api-server配置文件(k8snode1-3)

1
mkdir -p /etc/kubernetes/cfg /var/log/kubernetes
  • 更改 192.168.3.121 为当前master的ip

  • –tls-cert-file: API Server 自己的身份证(公钥)。当浏览器或 kubectl 访问 6443 端口时,它展示这个。

  • –tls-private-key-file: API Server 的私钥。用于加密 HTTPS 流量。

  • –client-ca-file: 根证书(CA)。API Server 用它来验证所有找上门来的客户端(如 kubectl)提供的证书是不是自己人签发的。这个配置是要和 cm 的 --cluster-signing-cert-file=/etc/kubernetes/ssl/ca.crt --cluster-signing-key-file=/etc/kubernetes/ssl/ca.key 保持一致,这样cm 给node 发cert的时候 ,ndoe 才能通过新发的cert 链接api,api 使用这个来验证


  • –kubelet-client-certificate: API Server 去找 Node 节点(kubelet)办事时展示的身份证。
  • –kubelet-client-key: API Server 访问 kubelet 用的私钥。
  • –etcd-cafile / certfile / keyfile: API Server 访问 Etcd 数据库时使用的三件套(CA、证书、私钥)。

  • –service-account-key-file: 验证专用锁。用来验证 Pod 里的 ServiceAccount Token。
  • –service-account-signing-key-file: 签署专用章。用来给新生成的 Token 盖章。
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
# 根据上面的该  192.168.3.121  替换为本机的ip
cat <<EOF > /etc/kubernetes/cfg/kube-apiserver.conf
KUBE_APISERVER_OPTS="--v=2 \\
--log-dir=/var/log/kubernetes \\
--etcd-servers=https://etcd1:2379,https://etcd2:2379,https://etcd3:2379 \\
--bind-address=192.168.3.121 \\
--secure-port=6443 \\
--advertise-address=192.168.3.121 \\
--allow-privileged=true \\
--service-cluster-ip-range=10.96.0.0/16 \\
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \\
--authorization-mode=RBAC,Node \\
--enable-bootstrap-token-auth=true \\
--token-auth-file=/etc/kubernetes/ssl/token.csv \\
--service-node-port-range=30000-32767 \\
--kubelet-client-certificate=/etc/kubernetes/ssl/kube-apiserver.crt \\
--kubelet-client-key=/etc/kubernetes/ssl/kube-apiserver.key \\
--tls-cert-file=/etc/kubernetes/ssl/kube-apiserver.crt \\
--tls-private-key-file=/etc/kubernetes/ssl/kube-apiserver.key \\
--client-ca-file=/etc/kubernetes/ssl/ca.crt \\
--service-account-key-file=/etc/kubernetes/ssl/ca.key \\
--service-account-signing-key-file=/etc/kubernetes/ssl/ca.key \\
--service-account-issuer=https://kubernetes.default.svc.cluster.local \\
--etcd-cafile=/etc/etcd/ssl/ca.crt \\
--etcd-certfile=/etc/etcd/ssl/etcd_server.crt \\
--etcd-keyfile=/etc/etcd/ssl/etcd_server.key \\
--audit-log-maxage=30 \\
--audit-log-maxbackup=3 \\
--audit-log-maxsize=100 \\
--audit-log-path=/var/log/kubernetes/proxy-audit.log"
EOF

创建 systemd service(k8snode1-3)

/usr/lib/systemd/system/kube-apiserver.service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target etcd.service

[Service]
# 指定读取参数文件的位置
EnvironmentFile=/etc/kubernetes/cfg/kube-apiserver.conf
ExecStart=/usr/local/bin/kube-apiserver $KUBE_APISERVER_OPTS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

启动 kube-apiserver(k8snode1-3)

1
2
3
4
systemctl daemon-reload
systemctl start kube-apiserver
systemctl status kube-apiserver
systemctl enable kube-apiserver

安装 Kube Controller Manager

Controller Manager 证书验证流程

kube-controller-manager.conf 里配了 --service-account-private-key-file=/…/ca.key,要和在 apiserver 里配了 --service-account-key-file=/…/ca.key。
含义:这代表 controller-manager 拿 ca.key 负责 “刻公章并给员工发工牌”(签发 Token),而 apiserver 拿同样的 ca.key 负责 “在大门口认工牌”(验证 Token)。
–root-ca-file=/…/ca.crt。 是告诉 controller-manager,以后给 Pod 注入根证书时,就用这一个。这确保了 Pod 内部程序去访问 API Server 时,能认出 API Server 展示的那张 kube-apiserver.crt 是合法的。

  • 场景:当一个新的 kubelet 加入集群时,它会发一个申请:“我想要一个证书,请给我签发。”
  • 过程:API Server 收到申请后,kube-controller-manager 内部的 签名控制器 (CSR Signing Controller) 会跳出来工作。
  • 动作:它会读取 --cluster-signing-cert-file 和 --cluster-signing-key-file。
  • 结果:它用这两文件(也就是根证书的公章)给那个 kubelet 签发一张新的身份证。

当 kubelet 拿着通过 CSR 申请到的新证书(假设叫 kubelet.crt)去连接 API Server 时,API Server 会执行以下逻辑:

  1. 看公章:API Server 检查 kubelet.crt 上的数字签名。

  2. 找对照表:API Server 翻看自己的配置参数 --client-ca-file(在你的配置里,它指向的是 /etc/kubernetes/ssl/ca.crt)。

  3. 比对结果:API Server 发现 kubelet.crt 是由 ca.crt 对应的私钥(ca.key)签发的。

  4. 结论:验证通过!API Server 确认这个 kubelet 是“自己人”。

  5. 验证通过后,API Server 怎么知道它是谁?验证“合法性”只是第一步,API Server 还需要知道这个 kubelet 的权限。证书内容:CM 在签发 kubelet.crt 时,会根据 CSR 里的信息,在证书里写入:CN (Common Name): 比如 system:node:k8s-node1 O (Organization): 比如 system:nodes
    权限识别:API Server 读取到这些信息后,配合 RBAC(你在配置里启用了 --authorization-mode=RBAC,Node),就知道这是一个节点,从而准许它上报心跳、拉取镜像。

Controller Manager 生成配置文件(k8snode1-3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cat <<EOF > /etc/kubernetes/cfg/kube-controller-manager.conf
KUBE_CONTROLLER_MANAGER_OPTS="--v=2 \\
--bind-address=127.0.0.1 \\
--root-ca-file=/etc/kubernetes/ssl/ca.crt \\
--cluster-signing-cert-file=/etc/kubernetes/ssl/ca.crt \\
--cluster-signing-key-file=/etc/kubernetes/ssl/ca.key \\
--service-account-private-key-file=/etc/kubernetes/ssl/ca.key \\
--kubeconfig=/etc/kubernetes/cfg/kube-controller-manager.kubeconfig \\
--leader-elect=true \\
--use-service-account-credentials=true \\
--cluster-signing-duration=87600h \\
--cluster-cidr=10.244.0.0/16 \\
--service-cluster-ip-range=10.96.0.0/16 \\
--allocate-node-cidrs=true"
EOF

Kube Controller Manager 生成证书(k8snode1)

  1. 设置集群参数 (指向 VIP) 逻辑:controller-manager 连接 https://k8smaster:6443 时,API Server 会抛出 kube-apiserver.crt。controller-manager 拿这个 ca.crt 一比对,发现是自己人签发的,才敢放心连接。
  2. –certificate-authority (CA 证书) 的作用
    • 它是 CM 用来验证“门”真伪的“参照物”。
    • 场景描述:CM 准备访问 https://k8smaster:6443
    • 发生什么:当 CM 连接时,AS 会向 CM 出示自己的身份证(即 kube-apiserver.crt)。
    • CA 的作用:CM 必须判断这个身份证是真的还是钓鱼网站假冒的。CM 会掏出配置文件里的 --certificate-authority(也就是 ca.crt),去比对 AS 那个身份证上的印章。
    • 结论:如果印章对得上,CM 才敢发送数据。这里的 CA 证书是为了防止 CM 被“中间人攻击”。
  3. –client-certificate (客户端证书) 的作用
    • 它是 CM 用来证明自己身份的“身份证”。
    • 场景描述:CM 验证完 AS 是真的后,AS 也会拦住 CM 问:“你是谁?你有权限进来吗?”。
    • 发生什么:CM 掏出配置文件里的 --client-certificate(即 kube-apiserver.crt)递给 AS。
    • 作用:AS 拿着这个证书,去比对自己的 --client-ca-file(也是 ca.crt)。
    • 结论:AS 发现这是自家签发的证书,于是给 CM 开门。这里的客户端证书是为了证明 CM 是有权访问的“合法员工”。
  4. 标准做法:应该专门签发一个 kube-controller-manager.crt,其 CN(通用名称)设置为 system:kube-controller-manager,这样 AS 就能通过 CN 知道来者是 CM,并赋予它对应的权限。

证书生成 (k8snode1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
openssl genrsa -out kube-controller-manager.key 2048
# 第二步:创建证书签名请求 (CSR)
# 注意 -subj 里的身份信息:


openssl req -new -key kube-controller-manager.key \
-out kube-controller-manager.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=system:kube-controller-manager/CN=system:kube-controller-manager"
# 第三步:使用 CA 签发证书
# 这里我们需要一个临时的配置文件 cm-ext.cnf 来指定证书的扩展用途(客户端认证):

cat <<EOF > cm-ext.cnf
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth
EOF

openssl x509 -req -in kube-controller-manager.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out kube-controller-manager.crt -days 3650 \
-extfile cm-ext.cnf

创建kube-controller-manager.kubeconfig (k8snode1)

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
#注意这个vip
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/ssl/ca.crt \
--embed-certs=true \
--server=https://k8smaster:9443 \
--kubeconfig=/etc/kubernetes/cfg/kube-controller-manager.kubeconfig

# 2. 设置上下文参数
kubectl config set-context default \
--cluster=kubernetes \
--user=system:kube-controller-manager \
--kubeconfig=/etc/kubernetes/cfg/kube-controller-manager.kubeconfig

# 3. 设置客户端认证参数 (使用 apiserver 现成的证书即可,或者专门签发)
kubectl config set-credentials system:kube-controller-manager \
--client-certificate=kube-controller-manager.crt \
--client-key=kube-controller-manager.key \
--embed-certs=true \
--kubeconfig=/etc/kubernetes/cfg/kube-controller-manager.kubeconfig

# 4. 切换上下文
kubectl config use-context default --kubeconfig=/etc/kubernetes/cfg/kube-controller-manager.kubeconfig
#check
kubectl config current-context --kubeconfig=/etc/kubernetes/cfg/kube-controller-manager.kubeconfig
kubectl config get-contexts --kubeconfig=/etc/kubernetes/cfg/kube-controller-manager.kubeconfig

配置文件 kube-controller-manager.kubeconfig 分发

这里不需要拷贝,因为上面的命令已经使用了 --embed-certs=true 参数,那么kube-controller-manager.kubeconfig 已经包含了签发的证书

1
2
3
4
scp /etc/kubernetes/cfg/kube-controller-manager.kubeconfig root@192.168.3.122:/etc/kubernetes/cfg/
scp /etc/kubernetes/cfg/kube-controller-manager.kubeconfig root@192.168.3.123:/etc/kubernetes/cfg/
# 剩下的两台机器上(k8snode2-3上) 执行
kubectl config use-context default --kubeconfig=/etc/kubernetes/cfg/kube-controller-manager.kubeconfig

API-server 服务高可用

安装keepalived haproxy (k8snode1-3)

yum install -y keepalived haproxy

1
2
3
4
5
6
7
8
9
10
11
# 创建系统用户组 haproxy
groupadd -r haproxy

# 创建系统用户 haproxy,并将其加入 haproxy 组
# -r: 创建系统账号
# -g: 指定组
# -s /sbin/nologin: 不允许该用户登录系统,进一步增加安全性
useradd -r -g haproxy -s /sbin/nologin haproxy

mkdir -p /var/lib/haproxy
chown haproxy:haproxy /var/lib/haproxy

配置虚拟ip (k8snode1-3)

编辑文件:/etc/haproxy/haproxy.cfg

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
global
# 定义日志格式,local2 是 Linux 日志设备
log 127.0.0.1 local2
# 运行目录,出于安全考虑将程序限制在空目录下
chroot /var/lib/haproxy
# 进程 ID 文件
pidfile /var/run/haproxy.pid
# 最大连接数
maxconn 4096
# 运行用户和组
user haproxy
group haproxy
# 以守护进程模式运行
daemon
# 启用统计 Socket,用于命令行管理
stats socket /var/lib/haproxy/stats
# turn on stats unix socket
stats socket /var/lib/haproxy/stats

# utilize system-wide crypto-policies
ssl-default-bind-ciphers PROFILE=SYSTEM
ssl-default-server-ciphers PROFILE=SYSTEM

defaults
# 默认模式。注意:k8s api 必须用 tcp 模式,不能用 http
mode tcp
# 使用全局日志配置
log global
# 开启日志中的空连接过滤
option dontlognull
# 连接失败后的重试次数
retries 3
# 超时设置(重点:防止僵死连接)
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
# 后端服务器的最大并发连接数
maxconn 3000

# ---------------------------------------------------------------------
# 核心配置:监听 K8s API Server
# ---------------------------------------------------------------------
frontend kube-apiserver
# 绑定 VIP 所在的 6443 端口
bind *:9443 # 注意这个9443 映射api server的6443
mode tcp # 必须是 TCP 模式,透传加密流量
# 启用 TCP 日志
option tcplog
# 指定默认后端
default_backend kube-apiserver

backend kube-apiserver
log 127.0.0.1 local3 err
# 负载均衡算法:轮询
balance roundrobin
# 具体的 API Server 节点配置
# check: 开启健康检查 inter 2000: 每2秒检查一次 rise 2: 两次成功认为正常 fall 3: 三次失败认为宕机
server k8smaster1 192.168.3.121:6443 check inter 2000 fall 3 rise 2
server k8smaster2 192.168.3.122:6443 check inter 2000 fall 3 rise 2
server k8smaster3 192.168.3.123:6443 check inter 2000 fall 3 rise 2

# ---------------------------------------------------------------------
# 附加功能:监控面板 (权威指南强烈推荐)
# ---------------------------------------------------------------------
listen stats
# 绑定 8888 端口查看监控
bind *:8888
mode http
stats auth admin:password # 设置监控页面的用户名和密码
stats refresh 5s # 页面每5秒自动刷新一次
stats uri /stats # 访问路径:http://VIP:8888/stats

启动haproxy(k8snode1-3)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 检查配置文件语法
haproxy -f /etc/haproxy/haproxy.cfg -c
# 启动服务
systemctl restart haproxy
systemctl enable haproxy

# 查看进程运行身份
ps aux | grep haproxy

#1. 验证端口监听 (OS 层) 首先确认 HAProxy 是否真的在监听 9443 和 8888(监控端口)。在三台 Master 上分别运行:
netstat -lntp | grep -E "9443|8888"

#2. 浏览器打开 http://192.168.3.121:8888/stats

配置 keepalived (k8snode1)

/etc/keepalived/keepalived.conf 在k8smaster node1 中配置,然后拷贝到其他机器上,注意每个机器的配置不一样 router_id state priority

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
global_defs {
script_user root # 添加这一行
enable_script_security # 配合使用
router_id k8smaster1 # 这个要唯一
vrrp_skip_check_adv_addr
vrrp_garp_interval 0
vrrp_gna_interval 0
}

# 定义监控脚本
vrrp_script check_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 3 # 每 3 秒执行一次脚本
weight -20 # 如果脚本返回失败,优先级减少 20
}

vrrp_instance VI_1 {
state MASTER #一个master 其余都是BACKUP
interface ens160
virtual_router_id 51
priority 100 # master机器上100 剩下的一次递减10
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.3.120
}
track_script {
check_haproxy # 调用上面定义的检查脚本
}
}

定义 check_haproxy 脚本 (k8snode1)

1
2
3
4
5
6
7
cat <<EOF> /etc/keepalived/check_haproxy.sh
#!/bin/bash
# 检查是否存在 haproxy 进程,或者检查 9443 端口是否在监听
if ! ss -lnt | grep -q :9443; then
exit 1
fi
EOF

必须赋予执行权限 (k8snode1)

1
chmod +x /etc/keepalived/check_haproxy.sh

拷贝分发 (k8snode1)

1
scp /etc/keepalived/* root@192.168.3.122:/etc/keepalived/

请参考注释更改node1 node2 的配置文件k8snode2-3)

启动 keepalived.service (k8snode1-3)

1
2
3
4
systemctl restart keepalived.service
systemctl enable keepalived.service
ip addr show ens160 # 请替换为你真实的网卡名
journalctl -u keepalived -f

验证VIP

1
2
3
4
5
6
7
8
9
10
11
#  (k8snode1) 可以发现
ip a | grep 192.168.3.120
k8snode1 systemctl stop keepalived.service

# (k8snode2 或者 2) 可以发现
ip a | grep 192.168.3.120

# (k8snode1) 启动
systemctl stop keepalived.service
ip a | grep 192.168.3.120 # 可以发现这个ip

启动 Kube Controller Manager(k8snode1-3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat <<EOF > /usr/lib/systemd/system/kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes

[Service]
EnvironmentFile=/etc/kubernetes/cfg/kube-controller-manager.conf
ExecStart=/usr/local/bin/kube-controller-manager \$KUBE_CONTROLLER_MANAGER_OPTS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF
1
2
3
4
5
6
7
8
9
systemctl start kube-controller-manager
systemctl restart kube-controller-manager
systemctl status kube-controller-manager
systemctl enable kube-controller-manager

# check
netstat -tpln | grep kube-controll
journalctl -u kube-controller-manager -f

生成 Admin 身份的配置文件(赋予 kubectl 管理权限)(k8snode1)

生成Admin 证书(k8snode1)

1
2
3
4
5
6
7
# 1. 生成私钥
openssl genrsa -out admin.key 2048
# 1. 生成 CSR (注意:O=system:masters 是获得集群管理权限的关键)
# 2. 生成 CSR (注意:CN=system:kubernetes)
openssl req -new -key admin.key \
-out admin.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=system:masters/CN=kubernetes"

生成Admin 证书cnf 文件(k8snode1)

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
cat <<EOF > admin-k8s-cert.cnf
[req]
distinguished_name = req_distinguished_name
prompt = no

[req_distinguished_name]
C = CN
ST = Beijing
L = Beijing
O = system:masters
CN = kubernetes

[v3_req]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth
subjectAltName = @alt_names

[alt_names]
# --- DNS 别名 ---
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster.local
DNS.5 = k8smaster
DNS.6 = k8smaster1
DNS.7 = k8smaster2
DNS.8 = k8smaster3

# --- IP 地址 ---
IP.1 = 127.0.0.1
IP.2 = 192.168.3.120
IP.3 = 192.168.3.121
IP.4 = 192.168.3.122
IP.5 = 192.168.3.123
# Cluster IP 网段的首地址 (用于集群内部 Pod 访问)
IP.6 = 10.96.0.1
EOF

# 3. 签发证书
openssl x509 -req -in admin.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out admin.crt -days 3650 \
-extensions v3_req -extfile admin-k8s-cert.cnf


# 创建目录
mkdir -p ~/.kube

# 1. 设置集群
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/ssl/ca.crt \
--embed-certs=true \
--server=https://k8smaster:9443 \
--kubeconfig=/root/.kube/config

# 2. 设置管理员凭据
kubectl config set-credentials kubernetes \
--client-certificate=admin.crt \
--client-key=admin.key \
--embed-certs=true \
--kubeconfig=/root/.kube/config

# 3. 设置上下文
kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=kubernetes \
--kubeconfig=/root/.kube/config

# 4. 设置当前使用的上下文
kubectl config use-context kubernetes --kubeconfig=/root/.kube/config
kubectl config get-contexts --kubeconfig=/root/.kube/config


scp /root/.kube/* root@192.168.3.122:/root/.kube/
scp /root/.kube/* root@192.168.3.123:/root/.kube/


检查 kube-controller-manager 和 kube-apiserver

1
2
3
4
5
6
[root@k8snode1 ~]# kubectl get lease -n kube-system
NAME HOLDER AGE
apiserver-ctizcdz57znnocnyti6ycgyu3i apiserver-ctizcdz57znnocnyti6ycgyu3i_e93d21da-311d-4e8f-9577-03407902e638 6d20h
apiserver-k3qxrawih5rj2jvagowwwcgi54 apiserver-k3qxrawih5rj2jvagowwwcgi54_aea946bc-28ff-4df2-a673-f2f453f3c6a6 4d12h
apiserver-rq3dcmitwparyow7yeum3nqx4i apiserver-rq3dcmitwparyow7yeum3nqx4i_d1ce8d65-f2e1-4277-9a51-d06d461b4d53 6d20h
kube-controller-manager k8snode3_889ada37-db80-4dc5-a921-b799498babdf 52m

安装kube-proxy(k8snode1-3)

证书生成(k8snode1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 生成私钥
openssl genrsa -out kube-proxy.key 2048

# 2. 生成 CSR (注意:CN 必须是 system:kube-proxy)
openssl req -new -key kube-proxy.key \
-out kube-proxy.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=system:node-proxier/CN=system:kube-proxy"

# 3. 签发证书 (复用我们之前的 k8s-cert.cnf)
openssl x509 -req -in kube-proxy.csr \
-CA /etc/kubernetes/ssl/ca.crt \
-CAkey /etc/kubernetes/ssl/ca.key \
-CAcreateserial \
-out kube-proxy.crt -days 3650 \
-extensions v3_req -extfile admin-k8s-cert.cnf

kube-proxy.kubeconfig生成(k8snode1)

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=/etc/kubernetes/ssl/ca.crt \
--embed-certs=true \
--server=https://k8smaster:9443 \
--kubeconfig=/etc/kubernetes/cfg/kube-proxy.kubeconfig

# 2. 设置用户 (使用刚生成的专用证书)
kubectl config set-credentials system:kube-proxy \
--client-certificate=kube-proxy.crt \
--client-key=kube-proxy.key \
--embed-certs=true \
--kubeconfig=/etc/kubernetes/cfg/kube-proxy.kubeconfig

# 3. 设置上下文
kubectl config set-context default \
--cluster=kubernetes \
--user=system:kube-proxy \
--kubeconfig=/etc/kubernetes/cfg/kube-proxy.kubeconfig

# 4. 激活上下文 (防止出现之前 current-context 为空的问题)
kubectl config use-context default --kubeconfig=/etc/kubernetes/cfg/kube-proxy.kubeconfig

kube-proxy.kubeconfig分发 (k8snode1)

1
2
scp /etc/kubernetes/cfg/kube-proxy.kubeconfig root@192.168.3.122:/etc/kubernetes/cfg/
scp /etc/kubernetes/cfg/kube-proxy.kubeconfig root@192.168.3.123:/etc/kubernetes/cfg/

kube-proxy 服务文件 (k8snode1-3)

vim /usr/lib/systemd/system/kube-proxy.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-----------
[Unit]
Description=Kubernetes Proxy
After=network.target

[Service]
ExecStart=/usr/local/bin/kube-proxy \
--config=/etc/kubernetes/cfg/kube-proxy-config.yml \
--v=2
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
------

kube-proxy 配置yaml (k8snode1)

vim /etc/kubernetes/cfg/kube-proxy-config.yml

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
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
healthzBindAddress: 0.0.0.0:10256
metricsBindAddress: 0.0.0.0:10249
clientConnection:
kubeconfig: /etc/kubernetes/cfg/kube-proxy.kubeconfig
acceptContentTypes: ""
contentType: application/vnd.kubernetes.protobuf
qps: 5
burst: 10
hostnameOverride: k8snode1
clusterCIDR: 10.244.0.0/16
mode: "ipvs"
ipvs:
scheduler: "rr"
syncPeriod: 30s
minSyncPeriod: 0s
tcpTimeout: 0s
tcpFinTimeout: 0s
udpTimeout: 0s
iptables:
masqueradeAll: false
syncPeriod: 30s
minSyncPeriod: 0s
conntrack:
maxPerCore: 32768
min: 131072

kube-proxy 配置yaml分发 (k8snode1)

1
2
scp /etc/kubernetes/cfg/kube-proxy-config.yml root@192.168.3.122:/etc/kubernetes/cfg/
scp /etc/kubernetes/cfg/kube-proxy-config.yml root@192.168.3.122:/etc/kubernetes/cfg/

修改其他两个node 的 配置文件 hostnameOverride 为node2 node3

kube-proxy 服务启动 (k8snode1-3)

1
2
3
4
systemctl start kube-proxy
systemctl status kube-proxy
systemctl enable kube-proxy
journalctl -u kube-proxy -f

检查kube-proxy 服务状况 (k8snode1-3)

1
2
3
4
5
### 检查健康检查端口 (默认 10256)
curl -s http://127.0.0.1:10256/healthz 预期返回: ok
### 检查监控指标端口 (默认 10249)
curl -s http://127.0.0.1:10249/metrics | head -n 10
### 预期返回: 大量的 Prometheus 格式数据

检查proxy 是否生效

1
2
3
4
5
6
7
8
9
10
11
kubectl get componentstatuses

# ipvsadm -L -n
# IP Virtual Server version 1.2.1 (size=4096)
# Prot LocalAddress:Port Scheduler Flags
# -> RemoteAddress:Port Forward Weight ActiveConn InActConn
# TCP 10.96.0.1:443 rr
# -> 192.168.3.121:6443 Masq 1 0 0
# -> 192.168.3.122:6443 Masq 1 0 0
# -> 192.168.3.123:6443 Masq 1

scheduler 部署步骤

scheduler 生成证书(k8snode1)

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
scheduler-openssl.cnf:
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth

# 1. 生成私钥
openssl genrsa -out kube-scheduler.key 2048

# 2. 生成证书签名请求 (CSR)
# 核心:CN 必须是 system:kube-scheduler,O 必须是 system:system:kube-scheduler
openssl req -new -key kube-scheduler.key \
-config scheduler-openssl.cnf \
-subj "/C=CN/ST=Beijing/L=Beijing/O=system:system:kube-scheduler/CN=system:kube-scheduler" \
-out kube-scheduler.csr

# 3. 使用 CA 签发证书
openssl x509 -req -in kube-scheduler.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out kube-scheduler.crt -days 3650 \
-extensions v3_req -extfile scheduler-openssl.cnf

kube-scheduler.kubeconfig 参数生成 (k8snode1)

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=ca.crt \
--embed-certs=true \
--server=https://k8smaster:9443 \
--kubeconfig=/etc/kubernetes/cfg/kube-scheduler.kubeconfig

# 设置用户凭据
kubectl config set-credentials system:kube-scheduler \
--client-certificate=kube-scheduler.crt \
--client-key=kube-scheduler.key \
--embed-certs=true \
--kubeconfig=/etc/kubernetes/cfg/kube-scheduler.kubeconfig

# 设置上下文
kubectl config set-context default \
--cluster=kubernetes \
--user=system:kube-scheduler \
--kubeconfig=/etc/kubernetes/cfg/kube-scheduler.kubeconfig

kubectl config use-context default --kubeconfig=/etc/kubernetes/cfg/kube-scheduler.kubeconfig

kube-scheduler.yml 和kube-scheduler.conf 生成(k8snode1)

/etc/kubernetes/cfg/kube-scheduler.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
clientConnection:
kubeconfig: /etc/kubernetes/cfg/kube-scheduler.kubeconfig
acceptContentTypes: "application/vnd.kubernetes.protobuf"
contentType: "application/vnd.kubernetes.protobuf"
qps: 50
burst: 100
leaderElection:
leaderElect: true
resourceNamespace: "kube-system"
resourceName: "kube-scheduler"

1
2
3
4
cat > /etc/kubernetes/cfg/kube-scheduler.conf <<EOF
KUBE_SCHEDULER_OPTS="--config=/etc/kubernetes/cfg/kube-scheduler.yml \\
--v=2"
EOF

kube-scheduler.yml 拷贝分发(k8snode1)

1
2
3
scp /etc/kubernetes/cfg/kube-scheduler.kubeconfig /etc/kubernetes/cfg/kube-scheduler.yml /etc/kubernetes/cfg/kube-scheduler.conf root@192.168.3.122:/etc/kubernetes/cfg/
scp /etc/kubernetes/cfg/kube-scheduler.kubeconfig /etc/kubernetes/cfg/kube-scheduler.yml /etc/kubernetes/cfg/kube-scheduler.conf root@192.168.3.123:/etc/kubernetes/cfg/

kube-scheduler 启动 (k8snode1-3)

/usr/lib/systemd/system/kube-scheduler.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
# 引入上面创建的变量文件
EnvironmentFile=/etc/kubernetes/cfg/kube-scheduler.conf
ExecStart=/usr/local/bin/kube-scheduler $KUBE_SCHEDULER_OPTS
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target


1. 重新加载 Systemd 配置
systemctl daemon-reload
systemctl enable kube-scheduler
systemctl start kube-scheduler
systemctl status kube-scheduler
journalctl -u kube-scheduler -f

check kube-scheduler (k8snode1-3)

1
2
3
4
5
6
7
8
9
10
[root@k8snode1 ~]# 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@k8snode1 ~]# kubectl get lease -n kube-system kube-scheduler
NAME HOLDER AGE
kube-scheduler k8snode3_bc33d48f-4a2f-4f66-9815-bf552df697eb 98s

安装kubet

kubelet.conf 文件生成 (k8snode1-3)

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
# 三台机器上执行 这个,记住将node-name 换成对应的hostname
cat > /etc/kubernetes/cfg/kubelet.conf <<EOF
KUBELET_OPTS="--v=2 \\
--kubeconfig=/etc/kubernetes/cfg/kubelet.kubeconfig \\
--bootstrap-kubeconfig=/etc/kubernetes/cfg/bootstrap.kubeconfig \\
--config=/etc/kubernetes/cfg/kubelet-config.yml \\
--cert-dir=/etc/kubernetes/certs
EOF


/usr/lib/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
After=containerd.service
Requires=containerd.service

[Service]
EnvironmentFile=/etc/kubernetes/cfg/kubelet.conf
ExecStart=/usr/local/bin/kubelet $KUBELET_OPTS
Restart=on-failure
KillMode=process

[Install]
WantedBy=multi-user.target

kubelet.config 文件生成 (k8snode1-3)

vim /etc/kubernetes/cfg/kubelet-config.yml
注意 clusterDNS 要设置对,我们后续会安装calico 的CNI差劲,需要指定 clusterDNS 的ip地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
address: 0.0.0.0
port: 10250
# 禁用不安全的只读端口
readOnlyPort: 0
# 必须与容器运行时驱动一致
cgroupDriver: systemd
clusterDNS:
- 10.96.0.2 #注意这个ip 要设置对
clusterDomain: cluster.local
failSwapOn: false
# 必须保留的安全配置
authentication:
anonymous:
enabled: false
webhook:
enabled: true
x509:
clientCAFile: /etc/kubernetes/ssl/ca.crt
authorization:
mode: Webhook

bootstrap.kubeconfig 文件生成 (k8snode1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 设置集群
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/ssl/ca.crt \
--embed-certs=true \
--server=https://k8smaster:9443 \
--kubeconfig=/etc/kubernetes/cfg/bootstrap.kubeconfig

# 设置认证
kubectl config set-credentials "kubelet-bootstrap" \
--token=c1aa5cb23fee29d424e56855884dacb9 \
--kubeconfig=/etc/kubernetes/cfg/bootstrap.kubeconfig

# 设置上下文
kubectl config set-context default \
--cluster=kubernetes \
--user="kubelet-bootstrap" \
--kubeconfig=/etc/kubernetes/cfg/bootstrap.kubeconfig

kubectl config use-context default --kubeconfig=/etc/kubernetes/cfg/bootstrap.kubeconfig

bootstrap.kubeconfig 文件分发 (k8snode1)

1
2
3
4
5
# 拷贝到配置目录
scp /etc/kubernetes/cfg/bootstrap.kubeconfig root@192.168.3.122:/etc/kubernetes/cfg/
scp /etc/kubernetes/cfg/bootstrap.kubeconfig root@192.168.3.123:/etc/kubernetes/cfg/
## 三台机器分别执行
kubectl config use-context default --kubeconfig=/etc/kubernetes/cfg/bootstrap.kubeconfig

kubelet 服务启动(k8snode1-3)

1
2
3
systemctl start  kubelet
systemctl status kubelet
systemctl enable kubelet

kubelet 在master上授权(k8snode1)

1
2
3
kubectl create clusterrolebinding kubelet-bootstrap \
--clusterrole=system:node-bootstrapper \
--user=kubelet-bootstrap

Master 授权 kubelet(k8smaser1(k8snode1))

1
2
3
4
kubectl get csr
#找到一定的 然后 approve
kubectl certificate approve <CSR名称>
kubectl get nodes

授权kubernetes kubelet api 的所有权限

1
2
3
4
# 创建一个名为 kubelet-api-admin 的权限绑定
kubectl create clusterrolebinding kubelet-api-admin \
--clusterrole=system:kubelet-api-admin \
--user=kubernetes

K8S 隔离master集群不分配 Pods

1
2
# 执行这条才真正起到了“隔离”作用
kubectl taint nodes k8snode1 k8snode2 k8snode3 node-role.kubernetes.io/master=:NoSchedule

K8S 集群增加新的work节点

安装所需命令

1
2
3
4
# 任意一台master  k8snode4 k8snode5 ...
scp /usr/local/bin/kubelet /usr/local/bin/kube-proxy k8snode4:/usr/local/bin/
scp -r /etc/kubernetes k8snode4:/etc/
scp /usr/lib/systemd/system/kubelet.service /usr/lib/systemd/system/kube-proxy.service k8snode4:/usr/lib/systemd/system/

修改配置文件

修改kube-proxy-config.yml hostnameOverride 为对应的hostname

启动服务

1
2
3
4
5
6
7
systemctl start kube-proxy
systemctl status kube-proxy
systemctl enable kube-proxy

systemctl start kubelet
systemctl status kubelet
systemctl enable kubelet

打上标签

#master 上标签 kubectl label nodes k8snode4 k8snode5 node-role.kubernetes.io/worker=

Check K8S 集群

1
2
3
4
kubectl get nodes
kubectl get --raw='/healthz?verbose'
journalctl -u kubelet -f
journalctl -u kube-proxy -f

安装网络 CNI 插件

1
2
3
4
5
6
7
8
9
10
11
# 1. 先把刚才 apply 失败的残留清理一下(可选,建议直接覆盖)
# 2. 恢复一个干净的 calico.yaml 或重新下载
curl https://calico-v3-25.netlify.app/archive/v3.25/manifests/calico.yaml -O

# 3. 使用 sed 自动取消注释并修改网段(避免手动编辑出错)这里就是我们controller manager server 设置的pod网段的作用
sed -i 's/192.168.0.0\/16/10.244.0.0\/16/g' calico.yaml
sed -i 's/# - name: CALICO_IPV4POOL_CIDR/- name: CALICO_IPV4POOL_CIDR/g' calico.yaml
sed -i 's/# value: "10.244.0.0\/16"/ value: "10.244.0.0\/16"/g' calico.yaml

kubectl apply -f calico.yam

check 一下 都已经ready

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@k8snode1 k8sresource]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8snode1 Ready control-plane,master 41h v1.36.0
k8snode2 Ready control-plane,master 41h v1.36.0
k8snode3 Ready control-plane,master 41h v1.36.0
k8snode4 Ready worker 40h v1.36.0
k8snode5 Ready worker 40h v1.36.0
[root@k8snode1 k8sresource]# kubectl get pods -n kube-system | grep calico
calico-kube-controllers-c57dfffd6-9sbjs 1/1 Running 0 77s
calico-node-52fnv 1/1 Running 0 77s
calico-node-5ndpq 1/1 Running 0 77s
calico-node-77bst 1/1 Running 0 77s
calico-node-8wpgl 1/1 Running 0 77s
calico-node-bgk6r 1/1 Running 0 77s

安装dns服务

将三个codeDNS 安装 master上,一下设置了节点情和性和clusterIP

编辑 coredns.yaml 文件,

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
cat > coredns.yaml << 'EOF'
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: coredns
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: system:coredns
rules:
- apiGroups: [""]
resources: [endpoints, services, pods, namespaces]
verbs: [list, watch]
- apiGroups: [""]
resources: [nodes]
verbs: [get]
- apiGroups: [discovery.k8s.io]
resources: [endpointslices]
verbs: [list, watch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:coredns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:coredns
subjects:
- kind: ServiceAccount
name: coredns
namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . 192.168.3.1 114.114.114.114 {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
labels:
k8s-app: kube-dns
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
k8s-app: kube-dns
template:
metadata:
labels:
k8s-app: kube-dns
spec:
serviceAccountName: coredns
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
- key: node-role.kubernetes.io/master
effect: NoSchedule
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: Exists
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values: [kube-dns]
topologyKey: kubernetes.io/hostname
containers:
- name: coredns
image: registry.k8s.io/coredns/coredns:v1.12.0
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
args: ["-conf", "/etc/coredns/Corefile"]
volumeMounts:
- name: config-volume
mountPath: /etc/coredns
readOnly: true
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9153
name: metrics
protocol: TCP
livenessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /ready
port: 8181
scheme: HTTP
periodSeconds: 2
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
securityContext:
allowPrivilegeEscalation: false
capabilities:
add: [NET_BIND_SERVICE]
drop: [ALL]
readOnlyRootFilesystem: true
volumes:
- name: config-volume
configMap:
name: coredns
items:
- key: Corefile
path: Corefile
---
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/name: CoreDNS
kubernetes.io/cluster-service: "true"
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9153"
spec:
selector:
k8s-app: kube-dns
clusterIP: 10.96.0.2
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
- name: metrics
port: 9153
protocol: TCP
EOF

安装coredns

1
2
3
4
5
6
7
8
kubectl apply -f coredns.yaml
# 确认3个Pod分别在3台master上
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
# 查询
kubectl get configmap coredns -n kube-system -o yaml | grep forward
# 验证DNS解析
kubectl run test-dns --image=busybox:1.28 --rm -it --restart=Never -- nslookup kubernetes.default

第三部分,删除k8s

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#一、先停止集群组件
systemctl stop kubelet
systemctl stop containerd
#kubeadm 重置(第一步但不够)
kubeadm reset -f


#删除残留配置(关键)
rm -rf /etc/kubernetes
rm -rf /var/lib/kubelet
rm -rf /var/lib/etcd
sudo rm -rf /var/run/kubernetes ~/.kube
sudo rm -rf /usr/bin/kubeadm /usr/bin/kubectl /usr/bin/kubelet
sudo systemctl daemon-reload
sudo systemctl reset-failed

#删除 CNI 网络(很重要)
rm -rf /etc/cni/net.d
rm -rf /var/lib/cni

#清理 iptables(否则网络会乱)
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X
# or
nft flush ruleset

# 四、清理 containerd 里的镜像/容器
ctr -n k8s.io containers ls
ctr -n k8s.io images ls
ctr -n k8s.io containers rm -f $(ctr -n k8s.io containers ls -q)
ctr -n k8s.io images rm $(ctr -n k8s.io images ls -q)

# 删除网络接口 查看现有的网络命名空间
ip netns list
# 批量删除 CNI 相关的命名空间
for ns in $(ip netns list | awk '{print $1}'); do
sudo ip netns delete $ns
done

ip link delete cali149f5445c04
sudo ip link set tunl0 down
sudo ip link delete tunl0

ip link delete cni0
ip link delete flannel.1
ip link delete calico0

# 卸载 tun ip to ip 网络

# 查看模块是否在运行
lsmod | grep ipip
# 卸载 ipip 模块
sudo modprobe -r ipip


# 卸载组件(如果你是 rpm/yum 安装)
dnf list installed | grep -E 'kubelet|kubeadm|kubectl|kubernetes'
yum remove -y kubelet kubeadm kubectl
dnf clean all


# 删除所有镜像
sudo crictl rmi --all
# 查看 containerd 存储占用的总大小
sudo du -sh /var/lib/containerd

# 自动查找所有包含 "containerd" 关键字的挂载点并强制卸载
for mount in $(mount | grep containerd | cut -d ' ' -f 3); do
sudo umount -l $mount
done

# 删掉整个数据目录(这会清空所有镜像和容器数据)直接物理抹除所有数据(镜像、容器、卷全部消失)
sudo rm -rf /var/lib/containerd/*
sudo rm -rf /var/run/containerd/*

# 重启服务,它会自动生成一个干净的空目录
sudo systemctl start containerd


# reboot
reboot

#九、验证是否干净
ps -ef | grep kube #应该没有 kube 进程
crictl ps -a #应该为空

ip a | grep cni #不应该有 cni0 等网卡

参考



支付宝打赏 微信打赏

赞赏一下