Kubernetes 集群搭建好之后,一般来说一年半载都不会再操作一次。
但每次重新搭建都得折腾半天,是一件相当繁杂的事,所以我把搭建过程写成了脚本:
GitHub :https://github.com/patricklaux/kube-install
这篇文章只是介绍配置项、执行命令和软件安装包,可与自动化脚本相互印证参考。
GitHub 中则包含完整的自动化脚本和软件安装包,如果一切顺利的话,大概十分钟就可完成全部过程。
0. 环境说明
集群搭建工具为 Kubeadm ,示例集群共有 3 个节点:1 个主节点和 2 个工作节点。
操作系统均为 Ubuntu 24.04.3,硬件配置为 4核-8G-200G 虚拟机,详细节点信息如下:
| IP | 主机名 | 角色 |
|---|---|---|
| 192.168.50.130 | k8s-control-1 | 控制平面 |
| 192.168.50.135 | k8s-worker-1 | 工作节点1 |
| 192.168.50.136 | k8s-worker-2 | 工作节点2 |
部署的软件版本为 Kubernetes 1.34.1 + Containerd 2.1.4 + Calico 3.30.3。
另,这次部署将尝试 Kubernetes 正式支持的新特性:nftables,此特性在 Calico 的当前版本仅处于技术预览阶段,因此勿用于生产环境。】
另,这些系统均无需提前安装 Docker。
1. 安装要求
每台机器均需安装 Linux 兼容系统。
集群所有节点能通过网络相互连接。
主节点应至少具有 2 个 vCPU 和 2GB RAM。
工作节点应至少具有 1 个 vCPU 和 2GB RAM。
集群所有节点的主机名、MAC 地址和 product_uuid 不能重复。
2. 配置系统环境
2.1. 网络端口
集群节点之间需要通过网络进行内部通信,同时还需要开放部分端口给外部访问。
如果系统防火墙是开启状态,请参照以下两张表格及网络插件 Calico 的相关要求,开放相应端口。
2.1.1. Control plane
| Protocol | Direction | Port Range | Purpose | Used By |
|---|---|---|---|---|
| TCP | Inbound | 6443 | Kubernetes API server | All |
| TCP | Inbound | 2379-2380 | etcd server client API | kube-apiserver, etcd |
| TCP | Inbound | 10250 | Kubelet API | Self, Control plane |
| TCP | Inbound | 10259 | kube-scheduler | Self |
| TCP | Inbound | 10257 | kube-controller-manager | Self |
2.1.2. Worker node(s)
| Protocol | Direction | Port Range | Purpose | Used By |
|---|---|---|---|---|
| TCP | Inbound | 10250 | Kubelet API | Self, Control plane |
| TCP | Inbound | 10256 | kube-proxy | Self, Load balancers |
| TCP | Inbound | 30000-32767 | NodePort Services† | All |
| UDP | Inbound | 30000-32767 | NodePort Services† | All |
上述表格来自 K8s 官方文档: Ports and Protocols。
另,Calico 还需使用其它一些端口,详见其官方文档: Network requirements。
# 如果防火墙启用,请先开放端口
sudo ufw allow 6443/tcp
sudo ufw allow 2379:2380/tcp
……
2.2. 查看内核
uname -r
# 内核版本显示如下
6.8.0-71-generic
注:如 Pod 启用用户命名空间隔离,所需的最低内核版本为 6.5+,见:Linux 内核版本要求 。
另,如希望了解 Kubeadm 支持的 LTS 内核,见 Linux 内核 Release 列表。
2.3. 查看主机信息
根据安装要求,MAC 和 product_uuid 不能重复。
# 查看 MAC 地址,确认每台机器均不相同
ip link
# 查看 product_uuid,确认每台机器均不相同
sudo cat /sys/class/dmi/id/product_uuid
2.4. 设置主机名
根据安装要求,主机名不能重复。
# 设置每台机器的主机名
# 192.168.50.130
sudo hostnamectl set-hostname k8s-control-1
# 192.168.50.135
sudo hostnamectl set-hostname k8s-worker-1
# 192.168.50.136
sudo hostnamectl set-hostname k8s-worker-2
2.5. 关闭交换分区
# 临时关闭交换分区
sudo swapoff -a
# 注释掉交换分区所在的行
sudo sed -ri 's/.*swap.*/#&/' /etc/fstab
2.6. 调整时区
# 查看时区
timedatectl
# 如时区并非 Asia/Shanghai,可通过以下命令调整
sudo timedatectl set-timezone Asia/Shanghai
sudo systemctl restart systemd-timedated
sudo systemctl restart systemd-timesyncd
2.7. 禁用 SELinux
Ubuntu 24.04.3,默认未启用 SELinux,跳过此步骤。
# 临时关闭
sudo setenforce 0
# 永久关闭
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
2.8. 调整内核参数
# 动态加载 overlay 模块
sudo modprobe overlay
# 开机加载 overlay 模块,避免重启失效
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
EOF
# 验证模块已加载
lsmod | grep overlay
# 配置网络参数
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
3. 安装容器运行时
Containerd 相比 Docker 更轻量,所以这次选择它。
Containerd 有三种安装方式:二进制、云源和源码构建,详见官方文档:containerd-getting-started。
这里采用二进制方式。
3.1. 安装 containerd
根据系统平台下载相应版本:containerd-releases
# 下载 containerd 2.1.4
wget https://github.com/containerd/containerd/releases/download/v2.1.4/containerd-2.1.4-linux-amd64.tar.gz
# 解压到 /usr/local 目录
sudo tar Cxzvf /usr/local containerd-2.1.4-linux-amd64.tar.gz
# 解压之后,/usr/local/bin/ 目录将会有四个文件
containerd containerd-shim-runc-v2 containerd-stress ctr
3.2. 作为服务启动
wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
sudo mkdir -p /usr/local/lib/systemd/system/
sudo cp containerd.service /usr/local/lib/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now containerd
3.3. 安装 runc
根据系统平台下载对应版本:runc-releases
wget https://github.com/opencontainers/runc/releases/download/v1.4.0-rc.1/runc.amd64
sudo install -m 755 runc.amd64 /usr/local/sbin/runc
3.4. 安装 cni-plugins
根据系统平台下载对应版本:cni-plugins-releases
wget https://github.com/containernetworking/plugins/releases/download/v1.8.0/cni-plugins-linux-amd64-v1.8.0.tgz
sudo mkdir -p /opt/cni/bin
sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.8.0.tgz
3.5. 检查版本
sudo systemctl restart containerd
sudo ctr version
# 显示版本如下,安装成功
Client:
Version: v2.1.4
Revision: 75cb2b7193e4e490e9fbdc236c0e811ccaba3376
Go version: go1.23.11
Server:
Version: v2.1.4
Revision: 75cb2b7193e4e490e9fbdc236c0e811ccaba3376
UUID: 39b4558f-dbd2-40fd-bd74-bdcab98bb7d3
3.6. 修改配置
3.6.1. 配置文件
# 如存在旧配置文件,将旧配置文件备份
sudo mv /etc/containerd/config.toml /etc/containerd/config.toml.bak
# 生成新的默认配置文件
sudo containerd config default > config.toml
# 新的默认配置文件移动到指定目录
sudo mv ./config.toml /etc/containerd/
# 修改新配置文件
sudo vim /etc/containerd/config.toml
具体修改如下(只需修改带注释的位置):
version = 3
# 2.X版本默认为空(1.X 版本默认包含 'cri',需删除)
disabled_plugins = []
…………
[plugins]
[plugins.'io.containerd.cri.v1.images']
…………
[plugins.'io.containerd.cri.v1.images'.pinned_images]
# 修改版本为 3.10.1
sandbox = 'registry.k8s.io/pause:3.10.1'
[plugins.'io.containerd.cri.v1.images'.registry]
# 2.X 版本:每个域名都需配置一个镜像源文件,这里是配置镜像源的根目录
config_path = '/etc/containerd/certs.d'
…………
[plugins.'io.containerd.cri.v1.runtime']
…………
[plugins.'io.containerd.cri.v1.runtime'.containerd]
…………
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes]
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc]
…………
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
…………
# 增加配置,使用 SystemdCgroup
SystemdCgroup = true
…………
注意:
Containerd 2.x 的配置版本为
version = 3,1.5 + 的配置版本为version = 2。两个版本的配置项并不相同,因此这里的配置项仅适用于 2.x 版本。
3.6.2. 镜像源:docker.io
官方文档:containerd-hosts
主要用于下载 Calico 镜像。
sudo mkdir -p /etc/containerd/certs.d/docker.io/
# 文件内容如下(镜像地址按需修改):
cat <<EOF | sudo tee /etc/containerd/certs.d/docker.io/hosts.toml
server = "https://docker.io"
[host."https://docker.1ms.run"]
capabilities = ["pull", "resolve"]
[host."https://docker.apiba.cn"]
capabilities = ["pull", "resolve"]
EOF
3.6.3. 镜像源:registry.k8s.io
用于下载 Kubernetes 官方镜像。
sudo mkdir -p /etc/containerd/certs.d/registry.k8s.io/
# 文件内容如下(镜像地址按需修改):
cat <<EOF | sudo tee /etc/containerd/certs.d/registry.k8s.io/hosts.toml
server = "https://registry.k8s.io"
[host."https://k8s.m.daocloud.io"]
capabilities = ["pull", "resolve"]
EOF
3.6.4. 配置代理
如希望使用代理,则可按如下配置(可选)。
sudo mkdir -p /etc/systemd/system/containerd.service.d/
# 文件内容如下(代理地址按需修改):
cat <<EOF | sudo tee /etc/systemd/system/containerd.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://192.168.50.218:3128/"
Environment="HTTPS_PROXY=http://192.168.50.218:3128/"
Environment="NO_PROXY=localhost,127.0.0.1,127.0.1.1,::1,10.0.0.0/8,192.168.0.0/16,172.17.0.0/16,172.30.0.0/16,172.31.0.0/16,.svc,.cluster.local,169.254.169.254"
EOF
3.6.5. 重启 containerd
sudo systemctl daemon-reload && sudo systemctl restart containerd
3.7. 小结
Kubernetes 的集群部署出现问题,主要就两个原因:
一是容器运行时未正确安装或与 Kubernetes 版本不匹配;二是网络不通无法拉取镜像。
总之,要不配置镜像源,要不配置网络代理,否则将因无法下载镜像而导致集群部署失败。
4. 安装部署工具
4.1. 安装 kubelet kubeadm kubectl
经测试,这三大工具无需使用国内 apt 源替代 pkgs.k8s.io 源也可快速安装。
如果因网络问题无法正常安装,可使用阿里云,具体配置见:Kubernetes镜像。
# 更新仓库数据
sudo apt-get update
# 安装依赖软件
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
# 2. 下载 Kubernetes 软件仓库的公共签名密钥
sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
# 3. 添加 Kubernetes v1.34 的 apt 仓库
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.34/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
# 4. 更新仓库数据,安装 kubelet、kubeadm 和 kubectl
sudo apt-get update
sudo apt-get install -y kubelet=1.34.1 kubeadm=1.34.1 kubectl=1.34.1
# 5. 锁定版本,避免升级破坏集群稳定性
sudo apt-mark hold kubelet kubeadm kubectl
4.2. 启动 kubelet
sudo systemctl enable kubelet && sudo systemctl start kubelet
# 查看 kubelet 状态
systemctl status kubelet
4.3. 配置 crictl
安装 kubeadm 将会自动安装 crictl 镜像命令行工具,其命令语法与 docker 非常类似。
这个工具需进行如下配置:
cat <<EOF | sudo tee /etc/crictl.yaml
runtime-endpoint: unix:///var/run/containerd/containerd.sock
image-endpoint: unix:///var/run/containerd/containerd.sock
timeout: 10
debug: false
EOF
现在可以尝试拉取第一个关键镜像:
# 之前已配置好 Containerd 的代理或镜像源
# 所以,这里直接使用 registry.k8s.io 拉取镜像
sudo crictl pull registry.k8s.io/pause:3.10.1
# 这个镜像非常非常小,只有 320 KB,应该可以很快下载完成
# 如果成功,将显示如下信息:
Image is up to date for sha256:cd073f4c5f6a8e9dc6f3125ba00cf60819cae95c1ec84a1f146ee4a9cf9e803f
注:最好每个节点都尝试一遍,如果都能拉取,那么至少已经成功一大半。
另:这个 pause 镜像是所有 pod 的根容器,没有它将完全无法启动 pod,更无法创建集群。
4.4. 小结
以上步骤,请在所有节点执行。
至此,所有集群部署的相关工具和系统环境都已全部准备完毕。
5. 创建集群
使用 kubeadm 创建集群。
5.1. 下载所需镜像
本节内容仅主节点执行。
# 获取需要下载的镜像
sudo kubeadm config images list --kubernetes-version=v1.34.1 --image-repository=registry.k8s.io
# 需要下载的镜像列表如下
registry.k8s.io/kube-apiserver:v1.34.1
registry.k8s.io/kube-controller-manager:v1.34.1
registry.k8s.io/kube-scheduler:v1.34.1
registry.k8s.io/kube-proxy:v1.34.1
registry.k8s.io/coredns/coredns:v1.12.1
registry.k8s.io/pause:3.10.1
registry.k8s.io/etcd:3.6.4-0
# 提前下载镜像(可选)
sudo kubeadm config images pull --kubernetes-version=v1.34.1 --image-repository=registry.k8s.io
5.2. 初始化主节点
本节内容仅主节点执行。
5.2.1. 指定控制平面节点
# 所有机器都添加主节点域名映射
sudo vim /etc/hosts
# 添加如下行(192.168.50.130 请换成你的主节点 IP):
192.168.50.130 cluster-endpoint
5.2.2. 创建配置文件
相比命令行参数配置,官方更推荐使用配置文件方式,因此这里采用配置文件方式来安装。
生成默认配置:
# 通过如下命令生成 kubeadm 的集群部署默认配置
kubeadm config print init-defaults -v=5 --component-configs KubeProxyConfiguration,KubeletConfiguration > kubeadm-init.yaml
修改配置项:
根据默认文件进行修改和删减后,仅保留了已修改项(未修改项 Kubeadm 将使用默认配置)。
这里需特别注意的是各个 IP 设置,需根据你的实际网络环境进行调整:
1、
podSubnet和clusterCIDR的配置必须相同;2、
serviceSubnet和 【podSubnet&clusterCIDR】的网络范围不能重叠;3、所配置的网络范围也不能与其它网络范围(譬如物理网络)重叠,总之就是各个网络范围不能重叠。
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
localAPIEndpoint:
# 1.更换为实际的主节点 IP
advertiseAddress: 192.168.50.130
bindPort: 6443
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
controlPlaneEndpoint: cluster-endpoint
kubernetesVersion: 1.34.1
networking:
# 2.service 的网络范围
serviceSubnet: 10.96.0.0/12
# 3.pod 的网络范围
podSubnet: 172.30.0.0/16
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
# 4.与 podSubnet 保持一致
clusterCIDR: 172.30.0.0/16
# 5.配置为使用 nftables
mode: nftables
---
apiVersion: kubelet.config.k8s.io/v1beta1
cgroupDriver: systemd
kind: KubeletConfiguration
5.3. 主节点初始化
本节内容仅主节点执行。
sudo kubeadm init --config=kubeadm-init.yaml
这一步执行完成后,控制台将会打印一些信息,其中包含了重要的加入集群的命令信息。
Your Kubernetes control-plane has initialized successfully!
# 省略 ……
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 省略 ……
kubeadm join cluster-endpoint:6443 --token ihn3xw.gsl769v2649zb33n \
--discovery-token-ca-cert-hash sha256:2c8223de5e3507f290775cfe1b0da64336afc57470ea9bc7e48ad92d91ebb57d
--control-plane
# 省略 ……
kubeadm join cluster-endpoint:6443 --token ihn3xw.gsl769v2649zb33n \
--discovery-token-ca-cert-hash sha256:2c8223de5e3507f290775cfe1b0da64336afc57470ea9bc7e48ad92d91ebb57d
请手动执行以下命令复制证书和配置到用户目录:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
此时,如果执行命令:
sudo crictl info
将会输出类似的如下错误状态信息,这是因为还未安装网络插件:
{
……
"lastCNILoadStatus": "cni config load failed: no network config found in /etc/cni/net.d: cni plugin not initialized: failed to load cni config",
"lastCNILoadStatus.default": "cni config load failed: no network config found in /etc/cni/net.d: cni plugin not initialized: failed to load cni config",
"status": {
"conditions": [
……
{
"message": "Network plugin returns error: cni plugin not initialized",
"reason": "NetworkPluginNotReady",
"status": false,
"type": "NetworkReady"
},
……
]
}
}
5.3. 网络插件 Calico
本节内容仅主节点执行。
Linux 系统下,服务代理有 iptables、ip_vs 和 ntables 三种模式,见:Virtual IPs and Service Proxies。
iptables:时间复杂度 O(n),其性能不适用于大规模集群。
ip_vs:时间复杂度 O(1),但无法满足精细化控制的特性要求,未来可能被 Kubernetes 弃用。
nftables:Kubernetes 1.29 引入,1.33 正式发布,见:kubernetes-1-29-feature & nftables-kube-proxy
网络插件 Calico 3.30.3 的 nftables 模式则是预览特性,见:calico-nftables。
如开篇所述,这次部署将使用 nftables 模式体验新特性。
5.3.1. 安装网络插件
下载配置文件:
wget https://raw.githubusercontent.com/projectcalico/calico/v3.30.3/manifests/operator-crds.yaml
wget https://raw.githubusercontent.com/projectcalico/calico/v3.30.3/manifests/tigera-operator.yaml
wget https://raw.githubusercontent.com/projectcalico/calico/v3.30.3/manifests/custom-resources.yaml
只需要修改 custom-resources.yaml:
# This section includes base Calico installation configuration.
# For more information, see: https://docs.tigera.io/calico/latest/reference/installation/api#operator.tigera.io/v1.Installation
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
# Configures Calico networking.
calicoNetwork:
# 1.设置为使用 Nftables
linuxDataplane: Nftables
ipPools:
- name: default-ipv4-ippool
blockSize: 26
# 2.设置为与 podSubnet 一致
cidr: 172.30.0.0/16
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
nodeSelector: all()
---
# This section configures the Calico API server.
# For more information, see: https://docs.tigera.io/calico/latest/reference/installation/api#operator.tigera.io/v1.APIServer
apiVersion: operator.tigera.io/v1
kind: APIServer
metadata:
name: default
spec: {}
---
# Configures the Calico Goldmane flow aggregator.
apiVersion: operator.tigera.io/v1
kind: Goldmane
metadata:
name: default
---
# Configures the Calico Whisker observability UI.
apiVersion: operator.tigera.io/v1
kind: Whisker
metadata:
name: default
安装网络插件:
kubectl create -f operator-crds.yaml
kubectl create -f tigera-operator.yaml
kubectl create -f custom-resources.yaml
5.3.2. 安装 calicoctl
calicoctl 是命令行工具,可以方便地查看网络信息:
wget https://github.com/projectcalico/calico/releases/download/v3.30.3/calicoctl-linux-amd64
# 1. 增加执行权限
sudo chmod +x ./calicoctl-linux-amd64
# 2. 作为独立工具安装
sudo mv calicoctl-linux-amd64 /usr/local/bin/calicoctl
# 2. 还可作为 kubectl 插件安装
sudo mv calicoctl-linux-amd64 /usr/local/bin/kubectl-calico
# 获取节点状态和协议
sudo calicoctl node status
5.4. 添加工作节点
本节内容仅工作节点执行。
执行主节点初始化成功时打印的命令信息:
sudo kubeadm join cluster-endpoint:6443 --token ihn3xw.gsl769v2649zb33n \
--discovery-token-ca-cert-hash sha256:2c8223de5e3507f290775cfe1b0da64336afc57470ea9bc7e48ad92d91ebb57d
如果遗失了加入集群的命令信息,可以在主节点执行以下命令重新生成:
kubeadm token create --print-join-command
至此,集群基础搭建的所有工作都已完成。
6. 检查集群状态
主节点执行命令,检查节点状态:
watch -n 2 kubectl get nodes
幸运的话,稍等片刻,将会看到所有节点都变成 Ready 状态:
NAME STATUS ROLES AGE VERSION
k8s-control-1 Ready control-plane 22h v1.34.1
k8s-worker-1 Ready <none> 21h v1.34.1
k8s-worker-2 Ready <none> 21h v1.34.1
使用如下命令检查 pod 状态:
watch -n 2 kubectl get pod -A
幸运的话,稍等片刻,将会看到所有 pod 都是 Ready 状态。
7. 常用命令
# 查看配置
kubectl describe configmaps kubeadm-config -n kube-system
# 查看 pod 日志
kubectl logs <pod-name> -n kube-system -f
某些情况下,工作节点出问题,但又无法使用 kubectl 查看日志,那么可使用如下命令:
# 查看运行日志(特别是工作节点出问题)
journalctl -u kubelet -f
8. 常见异常
异常 1
failed to create new CRI runtime service: validate service connection: validate CRI v1 runtime API for endpoint "unix:///var/run/containerd/containerd.sock": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`
error: error execution phase preflight: preflight checks failed
To see the stack trace of this error execute with --v=5 or higher
可能情况:
1、容器运行时版本不匹配;
2、未正确配置 crictl,见 4.3 节。
异常 2
W0914 10:04:12.719180 25964 checks.go:830] detected that the sandbox image "registry.k8s.io/pause:3.8" of the container runtime is inconsistent with that used by kubeadm. It is recommended to use "registry.k8s.io/pause:3.10.1" as the CRI sandbox image.
见 3.6.1. 配置文件,调整 pause镜像版本即可。
9. 结语
这只是搭建了一个最基础的集群,
接下来还需要安装 Metrics Server、Dashboard、Helm、Ingress 之类的组件,后续有时间再慢慢添加脚本。
最后,祝大伙的集群搭建过程一切顺利!