在这个时代,没有人能忽视 Kubernetes(K8s)。现在就开始搭建 K8s 的基础环境,后续也可以看看基于 K8s 能否玩出更多花样。

本次准备了三台 Ubuntu 系统,一台作为 master,两台作为 worker,全部基于 PVE 虚拟平台搭建。一切准备就绪后,即可开始操作。

---

1. 重置 Machine ID

Machine ID 是 Linux 系统的全局唯一标识符。在 PVE 上通过克隆得到三台相同的 Ubuntu 机器,但由于克隆的原因,这三台机器的 Machine-ID 是一样的。K8s 中的 CNI 插件正是通过 Machine-ID 来区分不同节点的,ID 一致会导致 K8s 认为它们是一个节点,从而造成路由表混乱、形成黑洞,Pod 跨节点通信失败。

bash

删除系统生成的 ID,避免出现一致

sudo rm -f /etc/machine-id /var/lib/dbus/machine-id

生成全新的 ID

sudo dbus-uuidgen --ensure=/etc/machine-id

文件初始化

sudo dbus-uuidgen --ensure

查看 ID,确保所有节点 ID 不一致

cat /etc/machine-id

2. 主机名与 Hosts 映射

主机名的设置原理与 Machine-ID 类似,目的是防止 K8s 集群将重复的主机名视为同一个节点,避免后期报错。

/etc/hosts 的静态映射可以让所有节点通过主机名解析到对应的 IP 地址,无需依赖 DNS 服务,防止 DNS 问题导致整个集群调度异常甚至瘫痪。

bash

在 master 上设置

sudo hostnamectl set-hostname master-01

在 worker-01 上设置

sudo hostnamectl set-hostname worker-01

在 worker-02 上设置

sudo hostnamectl set-hostname worker-02

配置静态 Hosts 映射(每个节点都需要操作一次):

bash

配置对应的静态映射

cat <<EOF | sudo tee -a /etc/hosts

<master IP> master-01

<worker-01 IP> worker-01

<worker-02 IP> worker-02

EOF

完成后可通过 ping 主机名进行简单验证,应该都能通过主机名解析。

3. 关闭 Swap

Kubernetes 要求关闭 Swap,主要目的是保证资源管理的确定性、性能和稳定性。关闭操作需在所有节点上执行。

bash

所有节点执行

sudo swapoff -a

永久关闭 Swap,注释掉 Swap 行,防止重启后恢复

sudo sed -i '/swap/s/^/#/' /etc/fstab

4. 加载内核模块与设置网络参数

overlay:Kubernetes 容器运行时需要 overlay 来高效管理容器镜像层和容器可写层,没有 overlay 则容器无法正常启动和运行。

br_netfilter:让 Linux 桥接流量能够通过 Netfilter 规则,否则桥接流量会绕过,导致网络策略、Service 转发等失效。

bash

声明在系统启动时自动加载的内核模块

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf

overlay

br_netfilter

EOF

加载 overlay 与 br_netfilter 模块

sudo modprobe overlay

sudo modprobe br_netfilter

配置网络参数:

bash

设置网络参数

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

5. 安装 Containerd

K8s 在 1.24 版本之前原生支持 Docker,当时的启动流程为:

Kubelet → dockershim → dockerd → containerd → runc → 容器进程

其中 dockershim 是 K8s 为兼容 Docker 而写的临时适配器,dockerd 是 Docker 的守护进程,功能繁多。这两层可以省略,以减少进程数和报错可能。

从 1.24 版本开始,K8s 改用 Containerd,流程简化为:

Kubelet → containerd → runc → 容器进程

这一改动使 K8s 性能更好、故障点更少。

bash

所有节点安装 Containerd 及依赖

sudo apt-get update

sudo apt-get install -y containerd apt-transport-https ca-certificates curl gpg

生成默认配置文件

sudo mkdir -p /etc/containerd

containerd config default | sudo tee /etc/containerd/config.toml > /dev/null

启用 SystemdCgroup

sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

替换 sandbox 镜像为阿里云源

sudo sed -i "s|registry.k8s.io/pause|registry.aliyuncs.com/google_containers/pause|g" /etc/containerd/config.toml

重启 containerd 并设置开机自启

sudo systemctl restart containerd

sudo systemctl enable containerd

验证:

bash

验证 containerd 是否正常运行

sudo systemctl status containerd

验证 SystemdCgroup 是否开启

grep "SystemdCgroup" /etc/containerd/config.toml

验证是否已更换为阿里云源

containerd config dump | grep sandbox

6. 安装 kubeadm / kubelet / kubectl

- kubeadm:集群的安装器和升级器,不负责实际运行业务 Pod。

- kubelet:每个节点的管家,负责在本节点上管理 Pod 和容器。

- kubectl:用户的遥控器,用于与集群交互。

可以这样理解:用 kubeadm 搭建集群,靠 kubelet 驱动每个节点,用 kubectl 指挥整个集群。

bash

所有节点都需要执行以下操作

导入阿里云 Kubernetes 仓库的 GPG 密钥

sudo mkdir -p /etc/apt/keyrings

curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/deb/Release.key \

  | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

添加阿里云 Kubernetes 仓库

echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/deb/ /" \

  | sudo tee /etc/apt/sources.list.d/kubernetes.list

安装三大组件

sudo apt-get update

sudo apt-get install -y kubelet kubeadm kubectl

锁定版本,防止系统自动升级导致不兼容

sudo apt-mark hold kubelet kubeadm kubectl

启动 kubelet

sudo systemctl enable --now kubelet

验证

kubeadm version

kubelet --version

kubectl version --client

7. 初始化并运行 K8s(在 master-01 上)

首先对 master-01 进行初始化,生成证书和配置文件,以便后续节点可以加入集群。

bash

sudo kubeadm init \

  --pod-network-cidr=10.244.0.0/16 \

  --image-repository=registry.aliyuncs.com/google_containers \

  --kubernetes-version=v1.30.14

参数说明:

- pod-network-cidr=10.244.0.0/16:Pod 网络的 IP 地址段(默认)。

- image-repository:指定为阿里云镜像源。

- kubernetes-version:指定版本。

初始化完成后,需要记录 Token 与 Hash 值(后续加入集群时需要)。

接下来配置 kubectl,使其通过 kubeconfig 文件获取 API Server 的地址和认证凭据:

bash

在 master 上配置

mkdir -p $HOME/.kube

sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

sudo chown $(id -u):$(id -g) $HOME/.kube/config

验证并查看当前节点(未部署网络插件前状态为 NotReady)

kubectl get nodes

查看 Pod 状态

kubectl get pods -n kube-system

8. 加入集群(在 worker 节点上)

前面记录的 Token 和 Hash 在这里使用。注意 Token 有效期为 24 小时,过期后需重新生成。

bash

如需重新申请 Token(在 master 上执行)

sudo kubeadm token create --print-join-command

在所有 worker 上执行

sudo kubeadm join <master_IP:6443> \

  --token <token值> \

  --discovery-token-ca-cert-hash sha256:<hash值>

执行后可在 master 上验证节点是否成功加入。

9. 部署网络插件

Kubernetes 对集群网络有三个硬性要求:

- Pod→Pod:每个 Pod 拥有真实 IP,不同节点上的 Pod 可通过 IP 直接互相访问。

- Pod→Service:Pod 可通过 Service 的 ClusterIP 或 DNS 名称访问后端 Pod。

- External→Service:外部客户端可通过 NodePort、LoadBalancer 或 Ingress 访问集群内的 Service。

常见的网络插件:

- Flannel(适合学习/小型环境):默认使用 VxLAN 在节点间构建叠加网络,每个节点分配一个子网。

- Calico(生产首选):采用三层路由,Pod 子网通过 BGP 广播到其他节点,由节点路由器或 BGP 客户端负责转发。

- Cilium(高性能):利用 Linux 内核的 eBPF 技术,在 socket 层或网卡驱动层直接处理数据包,性能极强,但对内核版本要求较高(≥5.1),配置复杂。

本次选择生产环境中使用最多的 Calico。Calico 在每个节点上运行以下组件:

- Felix:每个节点上的“大脑”,编程路由和 iptables 规则。

- BIRD:BGP 客户端,将节点上 Pod 网段宣告给其他节点。

- calico-node:以 DaemonSet 方式运行在每个节点上的 Pod,包含 Felix 和 BIRD。

- calico-kube-controllers:监听 K8s API 中的 NetworkPolicy 变更,并同步到 Calico。

bash

下载 calico.yaml

curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml

启用环境变量

sed -i 's|# - name: CALICO_IPV4POOL_CIDR|- name: CALICO_IPV4POOL_CIDR|' calico.yaml

修改 Calico 默认网段(192.168.0.0)为 kubeadm init 指定的 10.244.0.0

sed -i 's|#   value: "192.168.0.0/16"|  value: "10.244.0.0/16"|' calico.yaml

替换为国内镜像源

sed -i 's|docker.io|docker.m.daocloud.io|g' calico.yaml

部署

kubectl apply -f calico.yaml

等待 Calico 部署完成

kubectl get pods -n kube-system -l k8s-app=calico-node -w

查看节点状态(应变为 Ready)

kubectl get nodes

至此,K8s 的基础部署已经完成,后续可以在该集群上继续实现更多功能。