使用kubeadm在Rocky linux上安装k8s集群
实验环境
| 角色 | IP | 主机名 | containerd版本 | 硬件配置 | 系统 | 内核 | 安装组件 |
|---|---|---|---|---|---|---|---|
| master | 10.0.0.10 | k8s-master | 2.0.2 | 2c4g | Rocky Linux release 10.0 (Red Quartz) | 6.12.0-55.12.1.el10_0 | kube-apiserver,kube-controller-manager,kube-scheduler,kubelet,etcd |
| Node01 | 10.0.0.100 | K8s-node01 | 2.0.2 | 4c8g | Rocky Linux release 10.0 (Red Quartz) | 6.12.0-55.12.1.el10_0 | kubelet,kube-proxy,containerd,etcd |
| node02 | 10.0.0.101 | k8s-node02 | 2.0.2 | 4c8g | Rocky Linux release 10.0 (Red Quartz) | 6.12.0-55.12.1.el10_0 | kubelet,kube-proxy,containerd,etcd |
| node03 | 10.0.0.102 | k8s-node03 | 2.0.2 | 2c4g | Rocky Linux release 10.0 (Red Quartz) | 6.12.0-55.12.1.el10_0 | kubelet,kube-proxy,containerd,etcd |
准备开始
- 一台兼容的 Linux 主机。Kubernetes 项目为基于 Debian 和 Red Hat 的 Linux 发行版以及一些不提供包管理器的发行版提供通用的指令。
- 每台机器 2 GB 或更多的 RAM(如果少于这个数字将会影响你应用的运行内存)。
- CPU 2 核心及以上。
- 集群中的所有机器的网络彼此均能相互连接(公网和内网都可以)。
- 节点之中不可以有重复的主机名、MAC 地址或 product_uuid。请参见这里了解更多详细信息。
- 开启机器上的某些端口。请参见这里了解更多详细信息。
- 交换分区的配置。kubelet 的默认行为是在节点上检测到交换内存时无法启动。 kubelet 自 v1.22 起已开始支持交换分区。自 v1.28 起,仅针对 cgroup v2 支持交换分区; kubelet 的 NodeSwap 特性门控处于 Beta 阶段,但默认被禁用。
- 如果 kubelet 未被正确配置使用交换分区,则你必须禁用交换分区。 例如,
sudo swapoff -a将暂时禁用交换分区。要使此更改在重启后保持不变,请确保在如/etc/fstab、systemd.swap等配置文件中禁用交换分区,具体取决于你的系统如何配置。
- 如果 kubelet 未被正确配置使用交换分区,则你必须禁用交换分区。 例如,
kubeadm 的安装是通过使用动态链接的二进制文件完成的,安装时假设你的目标系统提供 glibc。 这个假设在许多 Linux 发行版(包括 Debian、Ubuntu、Fedora、CentOS 等)上是合理的, 但对于不包含默认 glibc 的自定义和轻量级发行版(如 Alpine Linux),情况并非总是如此。 预期的情况是,发行版要么包含 glibc, 要么提供了一个兼容层以提供所需的符号。
确保每个节点上 MAC 地址和 product_uuid 的唯一性
- 你可以使用命令
ip link或ifconfig -a来获取网络接口的 MAC 地址 - 可以使用
sudo cat /sys/class/dmi/id/product_uuid命令对 product_uuid 校验
一般来讲,硬件设备会拥有唯一的地址,但是有些虚拟机的地址可能会重复。 Kubernetes 使用这些值来唯一确定集群中的节点。 如果这些值在每个节点上不唯一,可能会导致安装失败。
检查网络适配器
如果你有一个以上的网络适配器,同时你的 Kubernetes 组件通过默认路由不可达,我们建议你预先添加 IP 路由规则, 这样 Kubernetes 集群就可以通过对应的适配器完成连接。
检查所需端口
启用这些必要的端口后才能使 Kubernetes 的各组件相互通信。 可以使用 netcat 之类的工具来检查端口是否开放,例如:
nc 127.0.0.1 6443 -v
你使用的 Pod 网络插件 (详见后续章节) 也可能需要开启某些特定端口。 由于各个 Pod 网络插件的功能都有所不同,请参阅他们各自文档中对端口的要求。
master节点需要启用的端口
| 协议 | 方向 | 端口范围 | 目的 | 使用者 |
|---|---|---|---|---|
| TCP | 入站 | 6443 | Kubernetes API 服务器 | 所有 |
| TCP | 入站 | 2379-2380 | etcd 服务器客户端 API | kube-apiserver、etcd |
| TCP | 入站 | 10250 | kubelet API | 自身、控制面 |
| TCP | 入站 | 10259 | kube-scheduler | 自身 |
| TCP | 入站 | 10257 | kube-controller-manager | 自身 |
node节点需要启用的端口
| 协议 | 方向 | 端口范围 | 目的 | 使用者 |
|---|---|---|---|---|
| TCP | 入站 | 10250 | kubelet API | 自身、控制面 |
| TCP | 入站 | 10256 | kube-proxy | 自身、负载均衡器 |
| TCP | 入站 | 30000-32767 | NodePort Services† | 所有 |
安装容器运行时
为了在 Pod 中运行容器,Kubernetes 使用 容器运行时(Container Runtime)。
默认情况下,Kubernetes 使用 容器运行时接口(Container Runtime Interface,CRI) 来与你所选择的容器运行时交互。
如果你不指定运行时,kubeadm 会自动尝试通过扫描已知的端点列表来检测已安装的容器运行时。
如果检测到有多个或者没有容器运行时,kubeadm 将抛出一个错误并要求你指定一个想要使用的运行时。
参阅 容器运行时 以了解更多信息。
Docker Engine 没有实现 CRI, 而这是容器运行时在 Kubernetes 中工作所需要的。 为此,必须安装一个额外的服务 cri-dockerd。 cri-dockerd 是一个基于传统的内置 Docker 引擎支持的项目, 它在 1.24 版本从 kubelet 中移除。
下面的表格包括被支持的操作系统的已知端点。
| 运行时 | Unix 域套接字 |
|---|---|
| containerd | unix:///var/run/containerd/containerd.sock |
| CRI-O | unix:///var/run/crio/crio.sock |
| Docker Engine(使用 cri-dockerd) | unix:///var/run/cri-dockerd.sock |
容器运行时类型
安装和配置先决条件
启用 IPv4 数据包转发
ansible all -m shell -a $'
cat <<EOF > /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
EOF
sysctl --system
'
# 设置所需的 sysctl 参数,参数在重新启动后保持不变
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
EOF
# 应用 sysctl 参数而不重新启动
sudo sysctl --system
使用以下命令验证 net.ipv4.ip_forward 是否设置为 1
ansible all -m shell -a 'sysctl net.ipv4.ip_forward'
sysctl net.ipv4.ip_forward
cgroup 驱动
在 Linux 上,控制组(CGroup)用于限制分配给进程的资源。
kubelet 和底层容器运行时都需要对接控制组来强制执行 为 Pod 和容器管理资源 并为诸如 CPU、内存这类资源设置请求和限制。若要对接控制组,kubelet 和容器运行时需要使用一个 cgroup 驱动。 关键的一点是 kubelet 和容器运行时需使用相同的 cgroup 驱动并且采用相同的配置。
可用的 cgroup 驱动有两个:
cgroupfs 驱动
cgroupfs 驱动是 kubelet 中默认的 cgroup 驱动。 当使用 cgroupfs 驱动时, kubelet 和容器运行时将直接对接 cgroup 文件系统来配置 cgroup。
当 systemd 是初始化系统时, 不 推荐使用 cgroupfs 驱动,因为 systemd 期望系统上只有一个 cgroup 管理器。 此外,如果你使用 cgroup v2, 则应用 systemd cgroup 驱动取代 cgroupfs。
systemd cgroup 驱动
当某个 Linux 系统发行版使用 systemd 作为其初始化系统时,初始化进程会生成并使用一个 root 控制组(cgroup),并充当 cgroup 管理器。
systemd 与 cgroup 集成紧密,并将为每个 systemd 单元分配一个 cgroup。 因此,如果你 systemd 用作初始化系统,同时使用 cgroupfs 驱动,则系统中会存在两个不同的 cgroup 管理器。
同时存在两个 cgroup 管理器将造成系统中针对可用的资源和使用中的资源出现两个视图。某些情况下, 将 kubelet 和容器运行时配置为使用 cgroupfs、但为剩余的进程使用 systemd 的那些节点将在资源压力增大时变得不稳定。
当 systemd 是选定的初始化系统时,缓解这个不稳定问题的方法是针对 kubelet 和容器运行 时将 systemd 用作 cgroup 驱动。
要将 systemd 设置为 cgroup 驱动,需编辑 KubeletConfiguration 的 cgroupDriver 选项,并将其设置为 systemd。例如:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
...
cgroupDriver: systemd
从 v1.22 开始,在使用 kubeadm 创建集群时,如果用户没有在 KubeletConfiguration 下设置 cgroupDriver 字段,kubeadm 默认使用 systemd。
在 Kubernetes v1.28 中,启用 KubeletCgroupDriverFromCRI 特性门控结合支持 RuntimeConfig CRI RPC 的容器运行时,kubelet 会自动从运行时检测适当的 Cgroup 驱动程序,并忽略 kubelet 配置中的 cgroupDriver 设置。
如果你将 systemd 配置为 kubelet 的 cgroup 驱动,你也必须将 systemd 配置为容器运行时的 cgroup 驱动。参阅容器运行时文档,了解指示说明。例如:
更改已加入集群的节点的 cgroup 驱动是一项敏感的操作。 如果 kubelet 已经使用某 cgroup 驱动的语义创建了 Pod,更改运行时以使用别的 cgroup 驱动,当为现有 Pod 重新创建 PodSandbox 时会产生错误。 重启 kubelet 也可能无法解决此类问题。
如果你有切实可行的自动化方案,使用其他已更新配置的节点来替换该节点, 或者使用自动化方案来重新安装。
安装containerd
安装containerd
从v1.6版本开 始,捆绑包 cri-containerd-*.tar.gz 已经被弃用,并且会在v2.0版本删除,这里有 官方说明
- 1.x
- 2.x
下载安装包
ansible all -m shell -a '\
CONTAINERD_VERSION=1.7.17; \
ARCH=$(uname -m); \
if [ "$ARCH" = "x86_64" ]; then ARCHITECTURE=amd64; \
elif [ "$ARCH" = "aarch64" ]; then ARCHITECTURE=arm64; \
else echo "Unsupported architecture: $ARCH" && exit 1; fi; \
wget https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-${ARCHITECTURE}.tar.gz \
'
export CONTAINERD_VERSION=1.7.17
export ARCHITECTURE=$(
case "$(uname -m)" in
x86_64) echo amd64 ;;
aarch64) echo arm64 ;;
*) echo "unsupported"; exit 1 ;;
esac
)
wget https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-${ARCHITECTURE}.tar.gz
压缩包内容如下
$ tar tf containerd-${CONTAINERD_VERSION}-linux-${ARCHITECTURE}.tar.gz
bin/
bin/containerd-shim
bin/containerd-stress
bin/ctr
bin/containerd-shim-runc-v2
bin/containerd-shim-runc-v1
bin/containerd
下载安装包
ansible all -m shell -a '\
CONTAINERD_VERSION=2.1.4; \
ARCH=$(uname -m); \
if [ "$ARCH" = "x86_64" ]; then ARCHITECTURE=amd64; \
elif [ "$ARCH" = "aarch64" ]; then ARCHITECTURE=arm64; \
else echo "Unsupported architecture: $ARCH" && exit 1; fi; \
wget https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-${ARCHITECTURE}.tar.gz \
'
export CONTAINERD_VERSION=2.1.4
export ARCHITECTURE=$(
case "$(uname -m)" in
x86_64) echo amd64 ;;
aarch64) echo arm64 ;;
*) echo "unsupported"; exit 1 ;;
esac
)
wget https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/containerd-${CONTAINERD_VERSION}-linux-${ARCHITECTURE}.tar.gz
压缩包内容如下
$ tar tf containerd-${CONTAINERD_VERSION}-linux-${ARCHITECTURE}.tar.gz
bin/
bin/containerd
bin/ctr
bin/containerd-stress
bin/containerd-shim-runc-v2
解压缩
ansible all -m shell -a '\
CONTAINERD_VERSION=2.1.4; \
ARCH=$(uname -m); \
if [ "$ARCH" = "x86_64" ]; then ARCHITECTURE=amd64; \
elif [ "$ARCH" = "aarch64" ]; then ARCHITECTURE=arm64; \
else echo "Unsupported architecture: $ARCH" && exit 1; fi; \
tar Cxzvf /usr/local containerd-${CONTAINERD_VERSION}-linux-${ARCHITECTURE}.tar.gz \
'
tar Cxzvf /usr/local containerd-${CONTAINERD_VERSION}-linux-${ARCHITECTURE}.tar.gz
使用systemd管理containerd
下载文件
ansible all -b -m get_url -a "url=https://raw.githubusercontent.com/containerd/containerd/main/containerd.service dest=/usr/lib/systemd/system/containerd.service mode=0644"
curl https://raw.githubusercontent.com/containerd/containerd/main/containerd.service -o /usr/lib/systemd/system/containerd.service
containerd.service 文件内容如下
# Copyright The containerd Authors.
#
# 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.
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target
[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd
Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity
# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this version.
TasksMax=infinity
OOMScoreAdjust=-999
[Install]
WantedBy=multi-user.target
重新加载 systemd 的配置并设置containerd服务开机自启
ansible all -b -m systemd -a "daemon_reload=yes"
ansible all -b -m systemd -a "name=containerd enabled=yes"
systemctl daemon-reload
systemctl enable --now containerd
安装runc
下载安装包
ansible all -m shell -a '\
RUNC_VERSION=1.3.0; \
ARCH=$(uname -m); \
if [ "$ARCH" = "x86_64" ]; then ARCHITECTURE=amd64; \
elif [ "$ARCH" = "aarch64" ]; then ARCHITECTURE=arm64; \
else echo "Unsupported architecture: $ARCH" && exit 1; fi; \
wget https://github.com/opencontainers/runc/releases/download/v${RUNC_VERSION}/runc.${ARCHITECTURE} \
'
export RUNC_VERSION=1.3.0
export ARCHITECTURE=$(
case "$(uname -m)" in
x86_64) echo amd64 ;;
aarch64) echo arm64 ;;
*) echo "unsupported"; exit 1 ;;
esac
)
wget https://github.com/opencontainers/runc/releases/download/v${RUNC_VERSION}/runc.${ARCHITECTURE}
安装
ansible all -m shell -a '\
RUNC_VERSION=1.3.0; \
ARCH=$(uname -m); \
if [ "$ARCH" = "x86_64" ]; then ARCHITECTURE=amd64; \
elif [ "$ARCH" = "aarch64" ]; then ARCHITECTURE=arm64; \
else echo "Unsupported architecture: $ARCH" && exit 1; fi; \
install -m 755 runc.${ARCHITECTURE} /usr/local/sbin/runc \
'
install -m 755 runc.${ARCHITECTURE} /usr/local/sbin/runc
验证
$ runc -v
runc version 1.3.0
commit: v1.3.0-0-g4ca628d1
spec: 1.2.1
go: go1.23.8
libseccomp: 2.5.6
安装CNI插件
下载安装包
ansible all -m shell -a '\
CNI_VERSION=1.7.1; \
ARCH=$(uname -m); \
if [ "$ARCH" = "x86_64" ]; then ARCHITECTURE=amd64; \
elif [ "$ARCH" = "aarch64" ]; then ARCHITECTURE=arm64; \
else echo "Unsupported architecture: $ARCH" && exit 1; fi; \
wget https://github.com/containernetworking/plugins/releases/download/v${CNI_VERSION}/cni-plugins-linux-${ARCHITECTURE}-v${CNI_VERSION}.tgz \
'
export CNI_VERSION=1.7.1
export ARCHITECTURE=$(
case "$(uname -m)" in
x86_64) echo amd64 ;;
aarch64) echo arm64 ;;
*) echo "unsupported"; exit 1 ;;
esac
)
wget https://github.com/containernetworking/plugins/releases/download/v${CNI_VERSION}/cni-plugins-linux-${ARCHITECTURE}-v${CNI_VERSION}.tgz
安装包内容如下
$ tar tf cni-plugins-linux-${ARCHITECTURE}-v${CNI_VERSION}.tgz
./
./vrf
./firewall
./LICENSE
./macvlan
./static
./host-device
./host-local
./loopback
./sbr
./tuning
./bridge
./README.md
./ptp
./bandwidth
./vlan
./portmap
./ipvlan
./dummy
./tap
./dhcp
解压缩
ansible all -m shell -a '\
CNI_VERSION=1.7.1; \
ARCH=$(uname -m); \
if [ "$ARCH" = "x86_64" ]; then ARCHITECTURE=amd64; \
elif [ "$ARCH" = "aarch64" ]; then ARCHITECTURE=arm64; \
else echo "Unsupported architecture: $ARCH" && exit 1; fi; \
tar Cxzvf /usr/local/bin cni-plugins-linux-${ARCHITECTURE}-v${CNI_VERSION}.tgz \
'
export CNI_VERSION=1.7.1
export ARCHITECTURE=$(
case "$(uname -m)" in
x86_64) echo amd64 ;;
aarch64) echo arm64 ;;
*) echo "unsupported"; exit 1 ;;
esac
)
tar Cxzvf /usr/local/bin cni-plugins-linux-${ARCHITECTURE}-v${CNI_VERSION}.tgz
配置containerd
创建containerd配置文件
ansible all -m shell -a 'mkdir -p /etc/containerd && containerd config default > /etc/containerd/config.toml'
mkdir -p /etc/containerd && containerd config default > /etc/containerd/config.toml
配置 systemd group 驱动
当 systemd 是初始化系统时, 不 推荐使用 cgroupfs 驱动,因为 systemd 期望系统上只有一个 cgroup 管理器。 此外,如果你使用 cgroup v2, 则应用 systemd cgroup 驱动取代 cgroupfs。
当某个 Linux 系统发行版使用 systemd 作为其初始化系统时,初始化进程会生成并使用一个 root 控制组(cgroup),并充当 cgroup 管理器。
systemd 与 cgroup 集成紧密,并将为每个 systemd 单元分配一个 cgroup。 因此,如果你 systemd 用作初始化系统,同时使用 cgroupfs 驱动,则系统中会存在两个不同的 cgroup 管理器。
同时存在两个 cgroup 管理器将造成系统中针对可用的资源和使用中的资源出现两个视图。某些情况下, 将 kubelet 和容器运行时配置为使用 cgroupfs、但为剩余的进程使用 systemd 的那些节点将在资源压力增大时变得不稳定。
当 systemd 是选定的初始化系统时,缓解这个不稳定问题的方法是针对 kubelet 和容器运行时将 systemd 用作 cgroup 驱动。
- 1.x
- 2.x
修改 /etc/containerd/config.toml ,将 SystemdCgroup = false 修改为 SystemdCgroup = true
version = 2
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
用如下命令修改
ansible all -b -m replace -a "path=/etc/containerd/config.toml regexp='^SystemdCgroup = false' replace='SystemdCgroup = true' backup=yes"
sed -i.bak 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
修改 /etc/containerd/config.toml ,在 [plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options] 下新增 SystemdCgroup = true
ansible all -b -m lineinfile -a "path=/etc/containerd/config.toml insertafter=\"ShimCgroup = ''\" line=' SystemdCgroup = true' backup=yes"
version = 3
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
SystemdCgroup = true
使用sed修改
sed -i.bak "/\[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options\]/a \ \ \ \ \ \ \ \ \ \ \ \ SystemdCgroup = true" /etc/containerd/config.toml
修改 pause 镜像地址
在 /etc/containerd/config.toml 配置文件中,pause镜像的默认地址是 registry.k8s.io/pause:3.10,由于某些特殊原因,需要修改这个镜像的地址,可以使用ucloud提供的加速地址
uhub.service.ucloud.cn/996.icu/pause:3.10 或者阿里云提供的加速地址 registry.aliyuncs.com/k8sxio/pause:3.10
修改 registry.k8s.io 为 registry.aliyuncs.com/google_containers
- containerd 1.x
- containerd 2.x
[plugins."io.containerd.grpc.v1.cri"]
......
sandbox_image = "registry.k8s.io/pause:3.9"
用如下命令修改
ansible all -b -m replace -a "path=/etc/containerd/config.toml regexp='registry.k8s.io/pause:3.9' replace='registry.aliyuncs.com/google_containers/pause:3.9'"
sed -i 's#registry.k8s.io#registry.aliyuncs.com/google_containers#' /etc/containerd/config.toml
[plugins.'io.containerd.cri.v1.images'.pinned_images]
sandbox = 'registry.aliyuncs.com/google_containers/pause:3.10'
用如下命令修改
ansible all -b -m replace -a "path=/etc/containerd/config.toml regexp='registry.k8s.io/pause:3.10' replace='registry.aliyuncs.com/google_containers/pause:3.10'"
sed -i 's#registry.k8s.io#registry.aliyuncs.com/google_containers#' /etc/containerd/config.toml
配置containerd镜像仓库加速
修改配置文件
修改 /etc/containerd/config.toml ,修改 config_path ,新增 /etc/containerd/certs.d
- 1.x
- 2.x
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"