02_在多个RockyLinux10虚拟机上安装k8s集群
文章目录
- 0 k8s集群的架构
- 1 准备虚拟机 rockylinux10-1
-
- 1.1 下载最新版的 VirtualBox
- 1.2 下载最新版的 RockyLinux dvd.iso 镜像
- 1.3 新建虚拟机 rockylinux10-1
- 1.4 设置内存和CPU
- 1.5 设置磁盘
- 1.6 设置双网卡
- 1.7 启动虚拟机 rockylinux10-1
- 1.8 安装 RockyLinux
- 1.9 关闭防火墙
- 1.10 检查 ssh 是否安装
- 1.11 ssh 允许远程登录
- 1.12 重启虚拟机 rockylinux10-1
- 1.13 重启之后,可以在宿主机上用 ssh 连接虚拟机 rockylinux10-1
- 1.14 设置静态IP
- 1.15 重启之后,宿主机和虚拟机要互相 ping 通
- 1.16 设置 /etc/hosts
- 1.17 设置 hotstname 主机名
- 1.18 关闭 selinux
- 1.19 关闭 swap
- 1.20 设置阿里 repo 源
- 1.21 重启虚拟机 rockylinux10-1
- 1.22 关闭虚拟机 rockylinux10-1
- 1.23 VirtualBox 生成个快照 rockylinux10-init
- 2 虚拟机 rockylinux10-1 上安装 Docker
- 3 虚拟机 rockylinux10-1 上安装 k8s
- 4 虚拟机 rockylinux10-1 上 kubeadmin init 初始化
- 5 虚拟机 rockylinux10-2
-
- 5.1 从虚拟机 rockylinux10-1 中复制出 rockylinux10-2
- 5.2 启动虚拟机 rockylinux10-2
- 5.3 修改 rockylinux10-2 的静态 IP
- 5.4 重启虚拟机 rockylinux10-2 让 IP 修改生效
- 5.5 设置 hotstname 主机名
- 5.4 重启虚拟机 rockylinux10-2
- 5.5 启动虚拟机 rockylinux10-1
- 5.6 解决虚拟机 rockylinux10-2 kubectl get nodes 报错的问题
- 5.7 rockylinux10-2 加入 rockylinux10-1
- 5.8 虚拟机 rockylinux10-2 就算创建成功✌️👏👏👏
- 6 虚拟机 rockylinux10-3
- 7 虚拟机 rockylinux10-1 上修改 calico 的配置
- 8 安装 Headlamp
- 🙏完结撒花🎉感谢阅读
0 k8s集群的架构
| IP | 虚拟机名称 | 域名 | 角色 |
|---|---|---|---|
| 192.168.56.101 | rockylinux10-1 | rockylinux10-1 | control plane node |
| 192.168.56.102 | rockylinux10-2 | rockylinux10-2 | worker node |
| 192.168.56.103 | rockylinux10-3 | rockylinux10-3 | worker node |
1 准备虚拟机 rockylinux10-1
1.1 下载最新版的 VirtualBox
VirtualBox 官网 https://www.virtualbox.org/ 。从官网上下载好之后,安装到宿主机上。
1.2 下载最新版的 RockyLinux dvd.iso 镜像
Rocky Linux 官网 https://rockylinux.org
阿里云镜像 https://developer.aliyun.com/mirror/rockylinux
找到适合宿主机的CPU架构的镜像,比如:https://mirrors.aliyun.com/rockylinux/10.2/isos/aarch64/Rocky-10.2-aarch64-dvd1.iso
1.3 新建虚拟机 rockylinux10-1

1.4 设置内存和CPU

1.5 设置磁盘

1.6 设置双网卡
1.6.1 网卡1的设置

上图的 HostNetwork 这个名称,来自于下图所示内容,VirtualBox 会自带这个网络。如果没有自带的话,就自己创建一个。

1.6.2 网卡2的设置

1.6.3 双网卡设置好后点【确定】按钮
1.7 启动虚拟机 rockylinux10-1

1.8 安装 RockyLinux
按照《VirtualBox安装RockyLinux并使用ssh访问》 2.7节的方式,把 RockyLinux 安装好。
1.9 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
1.10 检查 ssh 是否安装
# 输入 ssh 命令后,直接回车,看有没有报错信息
ssh
# 如果提示没有 ssh 命令,那么就安装它
dnf install openssh-server
# 安装完成后,查看 ssh 状态。一般 ssh 是开机自启动的
systemctl status sshd
# 如果不是自启动,设置成自启动
systemctl enable sshd
1.11 ssh 允许远程登录
# 编辑如下文件,如果 vim 还没有安装的话,可以使用 dnf install vim 命令来安装一下
vim /etc/ssh/sshd_config
# 没有 vim 命令的话,用 dnf install vim 安装
# 在 vim 编辑此文件时,可以用 yy 复制一行,然后用 p 向下粘贴一行
# 找到# Authentication,如下图:

1.12 重启虚拟机 rockylinux10-1
reboot
1.13 重启之后,可以在宿主机上用 ssh 连接虚拟机 rockylinux10-1
在虚拟机上 ip a 查看 ip 地址,比如是 192.168.56.3 。然后在宿主机的 terminal 上远程连接 root@192.168.56.3 。
1.14 设置静态IP
ip a 可以看到如下内容:
[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:3b:5e:5a brd ff:ff:ff:ff:ff:ff
altname enx0800273b5e5a
inet 192.168.56.3/24 brd 192.168.56.255 scope global dynamic noprefixroute enp0s8
valid_lft 3192sec preferred_lft 3192sec
inet6 fd25:e1ed:cc49:b2ad:a00:27ff:fe3b:5e5a/64 scope global dynamic noprefixroute
valid_lft 2591878sec preferred_lft 604678sec
inet6 fe80::a00:27ff:fe3b:5e5a/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: enp0s9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:b2:12:9e brd ff:ff:ff:ff:ff:ff
altname enx080027b2129e
inet 10.0.3.15/24 brd 10.0.3.255 scope global dynamic noprefixroute enp0s9
valid_lft 85992sec preferred_lft 85992sec
inet6 fd17:625c:f037:3:a00:27ff:feb2:129e/64 scope global dynamic noprefixroute
valid_lft 86313sec preferred_lft 14313sec
inet6 fe80::a00:27ff:feb2:129e/64 scope link noprefixroute
valid_lft forever preferred_lft forever
由此可知,仅主机(Host-Only)网络就是 enp0s8 ,因为它的 IP 正好是 192.168.56.0/24 这个网段儿的。接下来,我们就设置 enp0s8 为静态 IP 192.168.56.101
1.14.1 执行 nmtui 命令
1.14.2 编辑一个连接

1.14.3 编辑 enp0s8

1.14.4 设置静态 IP

1.14.5 重启虚拟机 rockylinux10-1 来让静态 IP 的配置生效
reboot
1.15 重启之后,宿主机和虚拟机要互相 ping 通
# 宿主机 ping 虚拟机
ping 192.168.56.101
# 虚拟机 ping 宿主机,也就是要 ping 外网
ping www.baidu.com
# 虚拟机上查看路由表的话,应该只有一个 enp0s9 的
[root@localhost ~]# ip route show default
default via 10.0.3.2 dev enp0s9 proto dhcp src 10.0.3.15 metric 100
1.16 设置 /etc/hosts
vim /etc/hosts
# 在文件末尾,添加如下配置
192.168.56.101 rockylinux10-1
192.168.56.102 rockylinux10-2
192.168.56.103 rockylinux10-3
# 192.168.124.10 是宿主机的 ip
# 宿主机上 docker 私有仓库的设置
192.168.124.10 myprivate.docker.registry
# 宿主机上 daocloud 的本地缓存仓库设置
192.168.124.10 m.daocloud.io.localcache.registry
上面的宿主机上 docker 私有仓库的设置,可以参考我的另一篇文章 《01_手动构建自己开发学习所需的镜像》https://blog.csdn.net/ShiJunzhiCome/article/details/161364867 的 使用 docker compose 启动 registry:3.1.1 和 joxit/docker-registry-ui:2.6.0 和 部署 daocloud 的本地缓存 这2个小节的内容。
1.17 设置 hotstname 主机名
hostnamectl set-hostname rockylinux10-1
1.18 关闭 selinux
setenforce 0
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
1.19 关闭 swap
swapoff -a
yes | cp /etc/fstab /etc/fstab_bak
vim /etc/fstab
#注释与 swap 有关的一行,比如:
#UUID=b154ad02-c85b-4301-a28a-7074e24c39e6 none swap defaults 0 0
1.20 设置阿里 repo 源
# 先执行如下命令
sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \
-i.bak \
/etc/yum.repos.d/rocky*.repo
# 然后再执行
dnf makecache
1.21 重启虚拟机 rockylinux10-1
reboot
重启之后,可以在宿主机上,用 terminal 再次远程连接 root@192.168.56.101 。此时,terminal 就会从 [root@localhost~]# 变成 [root@rockylinux10-1 ~]# 。
1.22 关闭虚拟机 rockylinux10-1
shutdown -h now
1.23 VirtualBox 生成个快照 rockylinux10-init
备份名称
rockylinux10-init
备份描述
rockylinux 10
内存4G CPU2个 50GB磁盘
配置好了双网卡:hostOnly 和 NAT
关闭了防火墙 firewalld
设置好了静态ip 192.168.56.101/24
主机可以访问虚拟机
虚拟机可以访问外网
设置好了 /etc/hosts
设置好了 hotstname 主机名为 rockylinux10-1
关闭了 selinux
关闭了 swap
配置好了阿里的repo源
如下图:

2 虚拟机 rockylinux10-1 上安装 Docker
2.1 启动虚拟机 rockylinux10-1
2.2 安装 docker
按照 https://docs.docker.com/engine/install/rhel/ 官网的说明,分别执行以下命令
# 1 Set up the repository
dnf -y install dnf-plugins-core
dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
# 2 Install Docker Engine
dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 3 Start Docker Engine
systemctl enable --now docker
然后设置 /etc/docker/daemon.json,设置 cgroup 的 driver 是 systemd,设置 dockerhub 的国内镜像 https://docker.m.daocloud.io
{
"exec-opts": [
"native.cgroupdriver=systemd"
],
"registry-mirrors": [
"https://docker.m.daocloud.io"
],
"insecure-registries": [
"myprivate.docker.registry:55000",
"m.daocloud.io.localcache.registry:65000"
]
}
重启 docker
systemctl restart docker
2.3 设置时间同步
# 查看服务状态,应该是 active 的状态,并且开机自启
# chronyd 会自动同步时间
systemctl status chronyd
# 一般 chronyd 是已安装的,并且是开机自动启动的,如果没有安装,则安装它
dnf install chrony
# 安装之后设置开机自动启动
systemctl enable --now chronyd
# 查看详细的同步信息,关注 System time 和 Last offset 等字段
chronyc tracking
# 查看当前可用的NTP时间源
chronyc sources -v
#### 使用国内的 ntp 服务器
vim /etc/chrony.conf
#### 在文件中找到 pool 或 server 开头的行,这些是当前使用的 NTP 源。注释掉(在行首加 #)它们,然后添加国内常用的 NTP 服务器。如下:
# pool 2.rocky.pool.ntp.org iburst
# use china npt
server ntp.aliyun.com iburst
server ntp.tencent.com iburst
server ntp.ntsc.ac.cn iburst
#### 其他配置不变
#### 重启 chronyd 服务并检查状态
systemctl restart chronyd
chronyc tracking
chronyc sources -v
#### 验证时间同步,确保输出 System clock synchronized: yes
timedatectl
2.4 关闭虚拟机 rockylinux10-1
shutdown -h now
2.5 VirtualBox 生成个快照 rockylinux10-installed-docker
备份名称
rockylinux10-installed-docker
备份描述
rockylinux 10
安装了 docker 并且自启动
配置了 /etc/docker/daemon.json
设置了时间同步 chronyd 用国内 ntp
3 虚拟机 rockylinux10-1 上安装 k8s
3.1 启动虚拟机 rockylinux10-1
3.2 设置 k8s 的 repo 源
使用华为的源,注意,我这里的 k8s 版本是 v1.36
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.huaweicloud.com/kubernetes-new/core:/stable:/v1.36/rpm/
enabled=1
gpgcheck=1
gpgkey=https://mirrors.huaweicloud.com/kubernetes-new/core:/stable:/v1.36/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF
😁为什么不用阿里的源?原因是👉阿里的 k8s repo 源 https://mirrors.aliyun.com/kubernetes-new/,内容没有华为的全。
3.3 设置 br_netfilter 和 bridge-nf-call-iptables
3.3.1 相关命令
# 1. 加载模块并设置开机自启
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
# 2. 设置内核参数
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
# 3. 立即生效
sysctl --system
# 4. 重启系统
reboot
# 5. 重启后,验证是否生效(应输出 1)
sysctl net.bridge.bridge-nf-call-iptables
3.3.2 为什么要设置 br_netfilter 和 bridge-nf-call-iptables?
简单来说,这是为了让 Linux 网桥(Bridge) 转发的数据包,能够被 iptables 的规则“拦截”并处理。之所以需要这么做,是因为网桥和 iptables 工作在网络的不同层次,而 Kubernetes 的 Service 机制恰恰依赖于此。
-
它们工作在“不同层次”
Linux 网桥:是一个虚拟的二层(数据链路层)网络设备,它根据 MAC 地址在同一网段内转发数据包。
iptables:是 Linux 内核 netfilter 框架的一部分,工作在三层(网络层)和四层(传输层)。
默认情况下,数据包在网桥内转发时,走的是二层“快捷通道”,不会经过三层的 iptables 进行处理。br_netfilter 内核模块和 bridge-nf-call-iptables 参数的作用,就是强制打开这个“通道”,让所有经过网桥的数据包也必须经过 iptables 的检查。 -
Kubernetes 为什么必须开启?
这能解决一个非常实际的网络问题:Kubernetes Service 的同节点通信问题。
Kubernetes 的 Service 机制依赖于 iptables 规则来实现负载均衡(DNAT)。当 Pod 访问 Service 时:
请求数据包必须经过 iptables 规则,其目标 IP(ClusterIP)会被转换为某个 Pod 的 IP(PodIP)。
如果这个目标 Pod 恰好在同一个节点上,那么回包时,由于源和目的 IP 都在同一个网桥段内,默认会被网桥直接二层转发回去,而不会再次经过 iptables。
这就导致连接跟踪(conntrack) 信息不完整,客户端和服务端认为它们不在一个“连接”里,通信失败。
开启 bridge-nf-call-iptables 后,回包也必须经过 iptables,从而保证了连接跟踪信息的完整性,解决了通信问题。这是 Kubernetes 官方要求的必备网络条件。
3.4 设置 docker 安装后 自带的 containerd
3.4.1 编辑配置文件
vim /etc/containerd/config.toml
修改之后的完整的文件内容如下:
# Copyright 2018-2022 Docker Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 【新增】这个 version = 2 是必须的,要不然用 containerd config dump | grep -E "SystemdCgroup|192.168" 这个命令,就会看到许多 error
version = 2
# disabled_plugins = ["cri"]
# 【修改】把 cri 去掉
disabled_plugins = []
#root = "/var/lib/containerd"
#state = "/run/containerd"
#subreaper = true
#oom_score = 0
#[grpc]
# address = "/run/containerd/containerd.sock"
# uid = 0
# gid = 0
#[debug]
# address = "/run/containerd/debug.sock"
# uid = 0
# gid = 0
# level = "info"
# 【新增】启用 CRI 插件(如果存在 disabled_plugins,确保没有 "cri")
[plugins."io.containerd.grpc.v1.cri"]
# 启用 CRI 服务
[plugins."io.containerd.grpc.v1.cri".containerd]
# 默认使用 runc
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true # 关键:与 kubelet 的 cgroup 驱动保持一致
# 【新增】私有 http 镜像仓库的配置
# 这个配置是可选的,用于连接本地的私有镜像仓库,防止拉取镜像的时候,报 https 相关的错误
[plugins."io.containerd.cri.v1.images"]
[plugins."io.containerd.cri.v1.images".registry]
[plugins."io.containerd.cri.v1.images".registry.mirrors]
[plugins."io.containerd.cri.v1.images".registry.mirrors."myprivate.docker.registry:55000"]
endpoint = ["http://myprivate.docker.registry:55000"]
[plugins."io.containerd.cri.v1.images".registry.mirrors."m.daocloud.io.localcache.registry:65000"]
endpoint = ["http://m.daocloud.io.localcache.registry:65000"]
3.4.2 重启 containerd 服务
systemctl restart containerd
# 查看版本号,验证重启是否成功
ctr -n k8s.io version
用下面的命令,验证 containerd 是否配置好:
[root@rockylinux10-1 ~]# containerd config dump | grep -E "SystemdCgroup|myprivate|daocloud"
[plugins.'io.containerd.cri.v1.images'.registry.mirrors.'m.daocloud.io.localcache.registry:65000']
endpoint = ['http://m.daocloud.io.localcache.registry:65000']
[plugins.'io.containerd.cri.v1.images'.registry.mirrors.'myprivate.docker.registry:55000']
endpoint = ['http://myprivate.docker.registry:55000']
SystemdCgroup = true
# 输出的结果中,不应该有 error
3.4.3 与 containerd 相关一些命令
# 查看 containerd 版本号
ctr -n k8s.io version
# 查看 k8s 里面有哪些镜像
ctr -n k8s.io images list
# k8s 拉取镜像
ctr -n k8s.io images pull xxx-image:xxx-tag
# k8s 给镜像打 tag
ctr -n k8s.io images tag source-image:aaa-tag target-image:bbb-tag
# 查看 k8s 里面有哪些正在运行的容器
ctr -n k8s.io containers list
3.5 安装 kubelet, kubeadm 和 kubectl
通过 dnf --version 命令,来看自己的 linux 是用的 DNF 还是 DNF5,如下,很明显,是 DNF 4的版本,也就是 DNF。
[root@rockylinux10-1 ~]# dnf --version
4.20.0
Installed: dnf-0:4.20.0-22.el10_2.rocky.0.1.noarch at Mon 27 Jul 2026 09:54:53 AM GMT
Built : Rocky Linux Build System <releng@rockylinux.org> at Tue 19 May 2026 02:06:23 PM GMT
Installed: rpm-0:4.19.1.1-23.el10.aarch64 at Mon 27 Jul 2026 09:54:53 AM GMT
Built : Rocky Linux Build System <releng@rockylinux.org> at Sat 07 Feb 2026 02:50:24 PM GMT
根据 k8s 官方文档 https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/ ,开始安装,命令如下:
# For systems with DNF:
# 我们是 DNF4 也就是 DNF,所以执行下面的命令
dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
# For systems with DNF5:
# 我们是 DNF4 不是5,所以不用下面的命令
dnf install -y kubelet kubeadm kubectl --setopt=disable_excludes=kubernetes
安装完成后执行(这个很重要)
systemctl enable kubelet.service
3.6 手动拉取阿里云的 pause 镜像并打上官方标签
为什么要手动拉取?因为后面的 kubeadmin init 和 安装 CNI 网络插件会从 k8s 官方源 registry.k8s.io 拉取镜像 pause:3.10.1。但是国内访问不了 registry.k8s.io,所以只能手动拉取,然后打 tag,命令如下:
# 拉取阿里云上的 pause(版本 3.10,与 3.10.1 基本一致)
ctr -n k8s.io images pull registry.aliyuncs.com/google_containers/pause:3.10
# 给这个镜像打上 kubelet 期望的标签(这样 kubelet 拉取时会直接命中本地镜像)
ctr -n k8s.io images tag registry.aliyuncs.com/google_containers/pause:3.10 registry.k8s.io/pause:3.10.1
# 验证镜像已存在
ctr -n k8s.io images list | grep pause
# 应该有一行是 registry.k8s.io/pause:3.10.1
3.7 关闭虚拟机 rockylinux10-1
shutdown -h now
3.8 VirtualBox 生成个快照 rockylinux10-prepare-k8s
备份名称
rockylinux10-prepare-k8s
备份描述
rockylinux 10
设置了 k8s 的华为 repo 源
设置了 br_netfilter 和 bridge-nf-call-iptables
设置了 docker 安装后 自带的 containerd
安装了 kubelet, kubeadm 和 kubectl
手动拉取了阿里云的 pause 镜像并打上官方标签
4 虚拟机 rockylinux10-1 上 kubeadmin init 初始化
4.1 启动虚拟机 rockylinux10-1
4.2 执行初始化命令
kubeadm init --kubernetes-version=1.36.3 \
--apiserver-advertise-address=192.168.56.101 \
--image-repository registry.aliyuncs.com/google_containers \
--service-cidr=10.1.0.0/16 \
--pod-network-cidr=10.244.0.0/16
4.2.1 --service-cidr 和 --pod-network-cidr 是做什么的?
| 参数 | 用途 | 谁在使用 |
|---|---|---|
--service-cidr |
为 Kubernetes Service(服务) 分配 ClusterIP 的地址池。每个 Service 会从这个网段中获取一个虚拟 IP。 | kube-apiserver、kube-proxy |
--pod-network-cidr |
为 Kubernetes Pod 分配 IP 的地址池。每个 Pod 启动时,CNI 插件(如 Calico)会从这个网段中分配一个 IP。 | CNI 插件(Calico/Flannel) |
4.2.2 --service-cidr 和 --pod-network-cidr 不能随便写! 有如下要求
4.2.2.1 不能与宿主机网段重叠
宿主机网段是:
- 192.168.56.0/24(enp0s8)
- 10.0.3.0/24(enp0s9)
那么 --service-cidr 和 --pod-network-cidr 不能是 192.168.56.0/24 或 10.0.3.0/24 的子集,否则网络会混乱。前面选的 10.1.0.0/16 和 10.244.0.0/16 与这两个网段都不重叠,是安全的。
4.2.2.2 两个 CIDR 之间不能重叠
Service IP 和 Pod IP 必须互不干扰。10.1.0.0/16 和 10.244.0.0/16 是独立的两个网段,没有问题。
4.2.2.3 要满足集群规模需求
-
10.1.0.0/16:最多可分配 65536 个 Service ClusterIP,通常足够。
-
10.244.0.0/16:最多可分配 65536 个 Pod IP,通常也足够。
如果你有超大规模集群(数千节点),可能需要更大的网段(如 /15 或 /14),但一般用不到。
4.2.3 k8s 初始化失败❗️,如何清理现场,方便再次尝试❓
kubeadm reset -f \
&& rm -rf /etc/kubernetes/ \
&& rm -rf /var/lib/kubelet/ \
&& systemctl restart containerd \
&& systemctl restart kubelet
4.2.4 k8s 初始化成功✌️👏👏👏
可以看到如下结果,记得保存好 kubeadmin join 的那行命令:
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/
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.56.101:6443 --token px86b6.tb097rswj6cpy09r \
--discovery-token-ca-cert-hash sha256:85cd76a1b57e2c5eab17dfb5f7467a5f9205b59375754e9368edfbab87809188
4.3 k8s 初始化成功后的操作
4.3.1 解决 kubectl get nodes 报错的问题
# 执行如下命令
kubectl get nodes
# 第一种情况:输出结果可能是一堆报错信息,如下:
E0730 09:42:53.796351 2645 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E0730 09:42:53.796568 2645 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E0730 09:42:53.797997 2645 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E0730 09:42:53.798197 2645 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
E0730 09:42:53.799655 2645 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connect: connection refused"
The connection to the server localhost:8080 was refused - did you specify the right host or port?
# 第二种情况:输出结果比较正常,如下:
NAME STATUS ROLES AGE VERSION
rockylinux10-1 NotReady control-plane 66s v1.36.3
如果是第一种情况的错误信息的话,那么接下来,我们按如下步骤解决它。此问题产生的原因是:kubectl 没有找到正确的 kubeconfig 配置文件,kubeadm init 成功后,会在 /etc/kubernetes/admin.conf 生成管理员 kubeconfig 文件,但你需要将它配置到 kubectl 的默认路径或设置环境变量。
- 确认集群是否已成功初始化:执行命令 ls -l /etc/kubernetes/admin.conf 如果该文件存在,说明 kubeadm init 已成功生成集群凭证,只是 kubectl 没有正确读取;
- 配置 kubectl 使用该 kubeconfig:最好选方法一
# 方法一(推荐):复制到默认路径
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
# 方法二:设置环境变量
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
source ~/.bash_profile
- 验证 kubectl 是否正常工作:执行命令 kubectl get nodes 这回应该不会报错,会输出状态是 NotReady
4.3.2 安装 CNI 网络插件
上面输出的 STATUS NotReady 表明集群已启动,其中 NotReady 是因为尚未安装 CNI 网络插件。CNI 网络插件有2种 Calico 和 Flannel,它们的异同如下:
| 特性 | Calico | Flannel |
|---|---|---|
| 网络模型 | 基于BGP协议的路由网络,数据包直接路由,性能接近物理网络。 | Overlay(覆盖网络),通过VXLAN等技术封装数据包,有一定性能损耗。 |
| NetworkPolicy | 原生支持,可实现Pod间微隔离。 | 不支持,无法实现Pod级网络策略。 |
| 性能 | 高(BGP模式无封装)。 | 中等(VXLAN模式有封装开销)。 |
| 复杂度 | 较复杂,组件多,有学习成本。 | 简单,轻量,部署即用。 |
| 适用场景 | 中大规模集群,对性能、安全、策略有高要求的场景。 | 中小规模集群,对功能和性能要求不高,追求简单快速的场景。 |
这里随便选一个,就选 Calico,反正是学习用😄。Calico 的 github 官网是 https://github.com/projectcalico/calico 。为了方便访问,我们可以把 https://github.com/projectcalico/calico.git 导入到 gitee 仓库中。
在导入好的 gitee 仓库中,选择最新的 release 了的分支,比如现在最新是 v3.32.1,然后下载代码仓库的 zip 包。解压 zip 包后,进入到解压后的目录,可以找到 manifests/calico.yaml 文件。
然后把此 calico.yaml 文件上传到 rockylinux10-1 虚拟机中,执行如下命令:
cat calico.yaml |grep image
# 可以看到,都使用了 quay.io 的镜像
image: quay.io/calico/cni:v3.32.1
imagePullPolicy: IfNotPresent
image: quay.io/calico/cni:v3.32.1
imagePullPolicy: IfNotPresent
image: quay.io/calico/node:v3.32.1
imagePullPolicy: IfNotPresent
image: quay.io/calico/node:v3.32.1
imagePullPolicy: IfNotPresent
image: quay.io/calico/kube-controllers:v3.32.1
imagePullPolicy: IfNotPresent
根据 DaoCloud 在 gitee 上的官方仓库 https://gitee.com/daocloud/public-image-mirror 上所说,我们可以把 quay.io 替换成 quay.m.daocloud.io 相关命令如下:
sed -i 's?quay.io?quay.m.daocloud.io?g' calico.yaml
# 替换好之后,再 cat calico.yaml |grep image 就可以看到,都是 quay.m.daocloud.io 了
然后执行如下命令,
kubectl apply -f calico.yaml
# 执行完成后,等上5分钟左右,再次 kubectl get node 应该是如下结果,STATUS 是 Ready 了,如下:
[root@rockylinux10-1 ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
rockylinux10-1 Ready control-plane 7m20s v1.36.3
# 执行 kubectl get pod -n kube-system 命令,得到的 STATUS 应该都是 Running,如下:
[root@rockylinux10-1 ~]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-f5d69fdff-rftz6 1/1 Running 0 3m5s
calico-node-8bx86 1/1 Running 0 3m5s
coredns-6b5f954497-622g2 1/1 Running 0 6m43s
coredns-6b5f954497-qhdpx 1/1 Running 0 6m43s
etcd-rockylinux10-1 1/1 Running 0 6m51s
kube-apiserver-rockylinux10-1 1/1 Running 0 6m51s
kube-controller-manager-rockylinux10-1 1/1 Running 0 6m51s
kube-proxy-vn7p7 1/1 Running 0 6m43s
kube-scheduler-rockylinux10-1 1/1 Running 0 6m51s
4.4 关闭虚拟机 rockylinux10-1
shutdown -h now
4.5 VirtualBox 生成个快照 rockylinux10-k8s-init-success
备份名称
rockylinux10-k8s-init-success
备份描述
rockylinux 10
kubeadmin init 初始化了 rockylinux10-1 节点
解决了 kubectl get nodes 报错的问题
安装了 CNI 网络插件
5 虚拟机 rockylinux10-2
5.1 从虚拟机 rockylinux10-1 中复制出 rockylinux10-2
从虚拟机 rockylinux10-1 之前创建的快照 rockylinux10-prepare-k8s 中复制(clone)出 rockylinux10-2,如下图:

5.2 启动虚拟机 rockylinux10-2
5.3 修改 rockylinux10-2 的静态 IP
使用 nmtui 命令修改,如下图,IP 设置为 192.168.56.102/24

5.4 重启虚拟机 rockylinux10-2 让 IP 修改生效
reboot
# 重启后 ssh root@192.168.56.102 后 ip a 查看到的 IP 应该是 192.168.56.102/24 了
5.5 设置 hotstname 主机名
hostnamectl set-hostname rockylinux10-2
5.4 重启虚拟机 rockylinux10-2
reboot
# 重启后 ssh root@192.168.56.102 后应该可以看到 [root@rockylinux10-2 ~]#
5.5 启动虚拟机 rockylinux10-1
5.6 解决虚拟机 rockylinux10-2 kubectl get nodes 报错的问题
在 rockylinux10-2 上,执行如下命令:
mkdir -p $HOME/.kube
在 rockylinux10-1 上,执行如下命令:
scp /etc/kubernetes/admin.conf root@192.168.56.102:/root/.kube/config
在 rockylinux10-2 上,执行如下命令:
chown $(id -u):$(id -g) $HOME/.kube/config
# 再执行如下命令
[root@rockylinux10-2 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
rockylinux10-1 Ready control-plane 57m v1.36.3
[root@rockylinux10-2 ~]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-f5d69fdff-rftz6 1/1 Running 1 (7m42s ago) 54m
calico-node-8bx86 1/1 Running 1 (7m42s ago) 54m
coredns-6b5f954497-622g2 1/1 Running 1 (7m42s ago) 58m
coredns-6b5f954497-qhdpx 1/1 Running 1 (7m42s ago) 58m
etcd-rockylinux10-1 1/1 Running 1 (7m42s ago) 58m
kube-apiserver-rockylinux10-1 1/1 Running 1 (7m42s ago) 58m
kube-controller-manager-rockylinux10-1 1/1 Running 1 (7m42s ago) 58m
kube-proxy-vn7p7 1/1 Running 1 (7m42s ago) 58m
kube-scheduler-rockylinux10-1 1/1 Running 1 (7m42s ago) 58m
5.7 rockylinux10-2 加入 rockylinux10-1
kubeadm join 192.168.56.101:6443 --token px86b6.tb097rswj6cpy09r \
--discovery-token-ca-cert-hash sha256:85cd76a1b57e2c5eab17dfb5f7467a5f9205b59375754e9368edfbab87809188
5.8 虚拟机 rockylinux10-2 就算创建成功✌️👏👏👏
6 虚拟机 rockylinux10-3
参照虚拟机 rockylinux10-2 的创建过程,把虚拟机 rockylinux10-3 也创建好。注意,开工之前,先把虚拟机 rockylinux10-1 关闭掉。因为担心两个 IP 都是 192.168.56.101 的虚拟机,会相互冲突。
虚拟机 rockylinux10-3 的静态 IP 是 192.168.56.103 ,hostname 是 rockylinux10-3
7 虚拟机 rockylinux10-1 上修改 calico 的配置
虚拟机 rockylinux10-2 和 rockylinux10-3 加入 rockylinux10-1 之后,
kubectl get pod -n kube-system
# 发现有2个 calico 一直是 CrashLoopBackOff
calico-node-w67bb 0/1 CrashLoopBackOff 5 (35s ago) 6m14s
calico-node-zjhgz 0/1 CrashLoopBackOff 5 (48s ago) 6m37s
然后 kubectl logs -n kube-system calico-node-w67bb 发现
2026-08-01 07:13:57.649 [INFO][9] startup/startup.go 517: Clearing out-of-date IPv4 address from this node IP="10.0.3.15/24"
2026-08-01 07:13:57.651 [ERROR][9] startup/startup.go 368: Failed to configure IP addresses error=conflicting node detected: IPv4 address conflict
2026-08-01 07:13:57.651 [WARNING][9] startup/utils.go 47: Terminating
Calico node failed to start
根据日志,Calico 启动失败是因为 IP 地址冲突:worker 节点 rockylinux10-3 的 enp0s9 接口 IP 10.0.3.15/24 已被 master 节点 rockylinux10-1 占用。Calico 要求每个节点的 IP 必须唯一。
解决方案:让 Calico 使用 enp0s8 接口(Host-Only 网络)。也就是要修改 Calico DaemonSet,指定 IP 检测接口。在 rockylinux10-1 节点执行以下命令,编辑 Calico DaemonSet:
kubectl edit daemonset calico-node -n kube-system
在 spec.template.spec.containers[0].env 中添加环境变量(如果已存在则修改):
- name: IP_AUTODETECTION_METHOD
value: "interface=enp0s8"
保存并退出。等待 Pod 滚动更新并验证:
kubectl get pod -n kube-system -w | grep calico-node
当所有 calico-node Pod 变为 Running 且 READY 1/1 后,检查节点状态:
kubectl get nodes
应该全部为 Ready。
8 安装 Headlamp
Headlamp 是 k8s 官方推荐的,用于替代 Dashboard 的东东。
8.1 使用 yaml 安装
8.1.1 执行 kubectl apply -f 命令
可以使用 yaml 安装 Headlamp,在虚拟机 rockylinux10-1 上执行:
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/headlamp/main/kubernetes-headlamp.yaml
# 会输出
service/headlamp created
deployment.apps/headlamp created
secret/headlamp-admin created
如果访问 github 失败的话,可以把 https://github.com/kubernetes-sigs/headlamp.git 导入到 gitee 中,然后下载标签是 v0.44.0 的代码 zip 包,然后把 kubernetes-headlamp.yaml 通过 scp 命令传到虚拟机 rockylinux10-1 上,然后再:
kubectl apply -f kubernetes-headlamp.yaml
8.1.2 使用 NodePort 暴露服务
将 Headlamp Service 类型改为 NodePort:
kubectl patch svc headlamp -n kube-system -p '{"spec":{"type":"NodePort"}}'
查看分配的 NodePort 端口:
kubectl get svc -n kube-system headlamp
# 比如输出
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
headlamp NodePort 10.1.151.236 <none> 80:31293/TCP 11m
在宿主机的浏览器上,通过 http://192.168.56.101:31293 访问即可
8.1.3 创建访问 Token
Headlamp 本身不提供用户系统,而是直接使用 Kubernetes 的 RBAC 权限进行认证。所以需要创建一个 ServiceAccount 并获取其 Token 用于登录。
8.1.3.1 创建 ServiceAccount
创建一个拥有管理员权限的 ServiceAccount(例如 headlamp-admin):
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: headlamp-admin
namespace: kube-system
EOF
8.1.3.2 绑定 cluster-admin 权限
为刚才创建的 ServiceAccount 绑定集群管理员权限:
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: headlamp-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: headlamp-admin
namespace: kube-system
EOF
8.1.3.3 生成 Token
生成一个长期有效的 Token(例如 1 年)。如果不指定 --duration,默认 Token 有效期为 1 小时,建议延长。
kubectl create token headlamp-admin -n kube-system --duration=8760h
# 比如输出
eyJhbGciOiJSUzI1NiIsImtpZCI6Ik5fTHZYa2dERU8zMGxrQm1NaDRwVXhuajVKeGFaa3NRVXhtbGxsM1hOMEkifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxODE3MTA2ODUyLCJpYXQiOjE3ODU1NzA4NTIsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiYmNlNmJlMjUtZTYxYS00MGZjLWJkNDQtNDI1MzFjYjJhMDIxIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJoZWFkbGFtcC1hZG1pbiIsInVpZCI6ImI2ZWE0YmFkLTFjYzAtNDM4ZS05ZjYwLTc3MDJjYzZkYWU1YyJ9fSwibmJmIjoxNzg1NTcwODUyLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06aGVhZGxhbXAtYWRtaW4ifQ.Qy7__DIgbhHD2iNoPDA9RZlSGprqMj-DQWydWUpd5BDKmf1aB4FxZ9dJH4w8CVuj6CBs1f044vfnkvBwjudl1hsxvQ4dJ6eRFuuWUGOG_Cj80i54N4XaXIErzhwa_IwezXifeAAR2TsfUqPZLvzCA-7lsDD0Ky91SROi0iPporivmeUeHxXQgi8E2UT3HewC7UAWTB3qu2aORQu5C1zRQN6Mz7EvQVA6brITxCX_kJhX_FUq_o66V-2A8vdfk2FtKm-6Hldmmp_vE-lq4bgri1pw0cusod9Wlp5GfY8dvmNhF86kMqSAyztBIDeHFEgwvw3biutBC_SGABeVT6L_1g
8.1.4 在宿主机的浏览器上访问
如果浏览器上访问失败,那么利用 SSH 隧道,将虚拟机上的 Headlamp Service 端口(NodePort 31293)转发到宿主机本地端口
在宿主机终端执行:
# 下面的第一个 31293 表示的是宿主机的端口;
# 第二个 31293 表示的是虚拟机的端口
ssh -L 31293:localhost:31293 root@192.168.56.101
然后浏览器访问 http://localhost:31293 ,然后输入前面的 token 即可,登录成功后,如下图:

8.1.5 如何得知 Headlamp 被安装到了哪个节点?
执行如下命令:
[root@rockylinux10-1 ~]# kubectl get pod -n kube-system -o wide | grep headlamp
headlamp-bdbf5b45d-kn4f5 1/1 Running 0 19m 10.244.145.1 rockylinux10-3 <none> <none>
🙏完结撒花🎉感谢阅读
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)