一直以来我都有一个小小的梦想,做一个微服务版本的 "Hello World"。终于马上要实现了,所以也有了这一系列的文章。目前暂定 3 - 5 篇文章:
- 部署 k8s 集群
- 通过 helm 在 k8s 集群上部署 MySQL 应用
- 实现微服务版本的 Hello World
这是《K8s三部曲》这一列文章(暂定 3 篇)中的第一篇,这篇文档描述了如何在本地部署一个 k8s 的三节点的集群(一个 Master 节点和两个 Worker 节点)。结构如下:

三个环境的节点的配置如下表:
Node | CPU | Menory | Storage |
---|---|---|---|
k8s-master | 4 Core | 8 GB | 20 GB |
k8s-worker-1 | 2 Core | 6 GB | 20 GB |
k8s-worker-2 | 2 Core | 6 GB | 20 GB |
网络配置如下:
Node | IP | Subnet mark | Gateway |
---|---|---|---|
k8s-master | 10.211.55.10 | 255.255.255.0 | 10.211.55.1 |
k8s-worker-1 | 10.211.55.11 | 255.255.255.0 | 10.211.55.1 |
k8s-worker-2 | 10.211.55.12 | 255.255.255.0 | 10.211.55.1 |
其他的,比如 Linux 发行版,我用的是 Ubuntu 24.04.2 LTS 。
环境初始化
首先,三个节点需要做一些初始化的工作。安装更新必要的依赖:
sql
sudo apt update && sudo apt upgrade -y
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
禁用交换分区:
bash
# 临时
sudo swapoff -a
# 永久
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# 验证
free -h
配置内核模块和系统参数:
bash
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
解释一下这两个内核模块的作用:
-
overlay
: Linux 内核的一个网络驱动模块,主要用于创建 overlay 网络,其在 k8s 中的作用如下:- 跨节点 Pod 通信:不同节点上的 Pod 可以直接通信
- 网络隔离:创建虚拟的二层隔离网络
- IP 地址管理:为每个 Pod 分配独立的 IP 地址
-
br_netfilter
:可以使得通过 Linux 网桥(bridge)的流量能够被 iptables 规则处理,其在 k8s 中的作用如下:- Service 的实现:kube-proxy 使用 iptables 规则实现 Service 负载均衡
- 网络策略:NetworkPolicy 通过 iptables 规则控制流量
- NAT 和端口转发: NodePort、LoadBalancer 等服务类型的实现
配置其他系统参数:
shell
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
部署前检查
在开始之前,必须要检查每一台服务器(虚拟机的)的 MAC 地址、product_uuid 以及 machine-id 是否是唯一的:
shell
# 查看 product_uuid
sudo cat /sys/class/dmi/id/product_uuid
# 查看 MAC 地址
ip link
# 查看 machine-id
cat /etc/machine-id
如果发现 machine-id 是一致的,采用如下方式修复:
shell
sudo rm /etc/machine-id
sudo rm /var/lib/dbus/machine-id
sudo systemd-machine-id-setup
如果像我一样,是通过 Parallels Desktop 来 Clone 虚拟机,导致 product-ID 一样,那可能要麻烦一点,参考附录。
务必保证 product-ID、Machine-ID、MAC 地址在集群内都是唯一的。
部署 k8s 服务
接下来就开始正式部署 K8s 的服务了。Step By Step,Come on!
安装 container runtime
K8s 支持 Container Runtime Interface(CRI),只要是支实现了 CRI 的 Runtime 都支持。Kubeadmin 默认选择 Container Runtime。
shell
# 安装
sudo apt install -y containerd
# 生成默认配置
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
# 修改配置,启用 SystemdCgroup
sudo sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml
# 重启服务
sudo systemctl restart containerd
kubeadm 是官方提供的一种快速初始化 k8s 集群的方式, 本文采用它来初始化集群。
安装 Kubeadm、Kubelet、Kubectl
这三个工具是 K8s 生态系统中最重要的组件,它们的职责如下表所示:
组件 | 作用 | 举例说明 |
---|---|---|
kubeadm |
集群初始化和管理 | 比如我要在集群中加入或者移除一个节点 |
kubelet |
节点代理服务 | 运行在每个节点上的主要代理程序,负责管理节点上的 Pod 和容器 |
kubectl |
集群客户端工具 | 基于 CLI 的客户端工具,用于和 API 服务器进行通信,管理集群资源 |
这三个组件安装的节点也不同,如下表所示:
组件 | Master Node | Worker Node | 管理节点/客户端 |
---|---|---|---|
kubeadm |
✅ 必须安装 | ✅ 必须安装 | ❌ 不需要 |
kubelet |
✅ 必须安装 | ✅ 必须安装 | ❌ 不需要 |
kubectl |
✅ 必须安装 | ⚠️ 可选安装 | ✅ 建议安装 |
接下来就按照上比啊来安装吧,最简单的做法就是在 Master 和 Worker 节点全部安装上:
shell
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/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
sudo systemctl enable --now kubelet
在完成安装之后,通过查看版本号来验证是否安装成功:

初始化 Master 节点
接下来是使用 kubeadm init
命令来初始化 Master 节点。在初始化前,我们需要一份默认的初始化配置,可以通过如下命令输出:
shell
kubeadm config print init-defaults
我输出的内容如下,加了注释:
yaml
# kubeadm 初始化配置文件 - 详细注释版本
# 基于 kubeadm.k8s.io/v1beta4 API 版本
# ============================================================================
# InitConfiguration - 初始化配置部分
# 用于配置 kubeadm init 命令的行为和初始化过程中的各种参数
# ============================================================================
apiVersion: kubeadm.k8s.io/v1beta4 # kubeadm 配置 API 版本,v1beta4 是较新版本
# Bootstrap Tokens - 引导令牌配置
# 用于 Worker 节点加入集群时的身份验证
bootstrapTokens:
- groups: # 令牌关联的用户组
- system:bootstrappers:kubeadm:default-node-token # 默认节点令牌组,允许节点加入集群
token: abcdef.0123456789abcdef # 令牌值,格式:[a-z0-9]{6}.[a-z0-9]{16},用于节点认证
ttl: 24h0m0s # 令牌生存时间 (Time To Live),24小时后自动过期
usages: # 令牌用途
- signing # 用于签名,验证令牌的合法性
- authentication # 用于身份验证,允许节点通过此令牌加入集群
kind: InitConfiguration # 配置类型:初始化配置
# Local API Endpoint - 本地 API 服务器端点配置
# 定义 Master 节点上 API Server 的监听地址和端口
localAPIEndpoint:
advertiseAddress: 1.2.3.4 # Master 节点对外广播的 IP 地址,需要修改为实际 Master IP
bindPort: 6443 # API Server 监听端口,Kubernetes 默认端口
# Node Registration - 节点注册配置
# 定义节点如何注册到集群中
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock # 容器运行时接口 (CRI) Socket 路径
imagePullPolicy: IfNotPresent # 镜像拉取策略:如果本地不存在才拉取
imagePullSerial: true # 是否串行拉取镜像,true 表示一次只拉取一个镜像
name: node # 节点名称,需要修改为实际主机名
taints: null # 节点污点,null 表示 Master 节点可以调度 Pod(默认情况下 Master 有 NoSchedule 污点)
# Timeouts - 超时配置
# 定义各种操作的超时时间,防止操作无限期等待
timeouts:
controlPlaneComponentHealthCheck: 4m0s # 控制平面组件健康检查超时时间
discovery: 5m0s # 服务发现超时时间
etcdAPICall: 2m0s # etcd API 调用超时时间
kubeletHealthCheck: 4m0s # kubelet 健康检查超时时间
kubernetesAPICall: 1m0s # Kubernetes API 调用超时时间
tlsBootstrap: 5m0s # TLS 引导超时时间
upgradeManifests: 5m0s # 升级清单文件超时时间
--- # YAML 文档分隔符,分隔不同的配置部分
# ============================================================================
# ClusterConfiguration - 集群配置部分
# 用于配置整个 Kubernetes 集群的全局设置
# ============================================================================
# API Server Configuration - API 服务器配置
apiServer: {} # 空配置,使用默认设置
# 可配置项包括:
# - extraArgs: 额外的命令行参数
# - extraVolumes: 额外的卷挂载
# - certSANs: 证书主题备用名称
# - timeoutForControlPlane: 控制平面超时时间
apiVersion: kubeadm.k8s.io/v1beta4 # 重复声明 API 版本
# Certificate Validity Periods - 证书有效期配置
caCertificateValidityPeriod: 87600h0m0s # CA 证书有效期:10年 (87600小时)
certificateValidityPeriod: 8760h0m0s # 其他证书有效期:1年 (8760小时)
# Certificates Directory - 证书存储目录
certificatesDir: /etc/kubernetes/pki # 集群证书文件存储路径
# Cluster Name - 集群名称
clusterName: kubernetes # 集群标识名称,用于区分不同的集群
# Controller Manager Configuration - 控制器管理器配置
controllerManager: {} # 空配置,使用默认设置
# 可配置项包括:
# - extraArgs: 额外的命令行参数
# - extraVolumes: 额外的卷挂载
# DNS Configuration - DNS 配置
dns: {} # 空配置,使用默认 CoreDNS 设置
# 可配置项包括:
# - type: DNS 服务类型 (CoreDNS)
# - imageRepository: DNS 镜像仓库
# - imageTag: DNS 镜像标签
# Encryption Algorithm - 加密算法
encryptionAlgorithm: RSA-2048 # 证书加密算法,RSA-2048 位
# etcd Configuration - etcd 数据库配置
etcd:
local: # 本地 etcd 配置(非外部 etcd 集群)
dataDir: /var/lib/etcd # etcd 数据存储目录
# Image Repository - 镜像仓库
imageRepository: registry.k8s.io # Kubernetes 组件镜像仓库地址
kind: ClusterConfiguration # 配置类型:集群配置
# Kubernetes Version - Kubernetes 版本
kubernetesVersion: 1.32.0 # 要安装的 Kubernetes 版本
# Networking Configuration - 网络配置
networking:
dnsDomain: cluster.local # 集群内部 DNS 域名后缀
serviceSubnet: 10.96.0.0/12 # Service 网络 CIDR,用于 ClusterIP 分配
# 注意:缺少 podSubnet 配置,需要添加 Pod 网络 CIDR
# Proxy Configuration - 代理配置
proxy: {} # 空配置,使用默认 kube-proxy 设置
# 可配置项包括:
# - mode: 代理模式 (iptables, ipvs)
# - config: 代理配置参数
# Scheduler Configuration - 调度器配置
scheduler: {} # 空配置,使用默认调度器设置
# 可配置项包括:
# - extraArgs: 额外的命令行参数
# - extraVolumes: 额外的卷挂载
上面的内容很多,着重修改下面这几项配置就行:
配置项目 | 描述 | 如何获取 |
---|---|---|
localAPIEndpoint.advertiseAddress |
修改为实际的 Master 节点 IP | 通过 ip add show 命令查看 |
nodeRegistration.name |
修改为 Master 节点的主机名 | 通过 hostname 命令查看 |
networking.podSubnet |
定义集群内 Pod 可用的 IP 范围(CIDR 网段) | 例如 10.244.0.0/16 |
另外添加如下配置:
yaml
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
imagePullPolicy: IfNotPresent
imagePullSerial: true
name: 你的主机名
taints: null
# 添加下面两行配置
kubeletExtraArgs:
- name: "cgroup-driver"
value: "systemd"
- name: "container-runtime-endpoint"
value: "unix:///var/run/containerd/containerd.sock"
配置完了之后,执行下面的命令:
shell
sudo kubeadm init --config kubeadm-init-defaults.yaml
如果你看到类似如下输出,则表明初始化成功了:

那么按照提示进行下一步:
bash
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 如果你是 root 用户的话
export KUBECONFIG=/etc/kubernetes/admin.conf
然后执行如下命令,验证是否初始化成功:

安装网络插件
接下来安装网络插件,应用如下配置文件:
shell
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
如果看到如下输出,则表明没有问题:
shell
namespace/kube-flannel created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created
然后进一步验证 pod 已经 Running:
shell
$ kubectl get pods -n kube-flannel
NAME READY STATUS RESTARTS AGE
kube-flannel-ds-5zzvm 1/1 Running 0 110s
加入 Worker 节点
接下来,就开始初始化 Worker 节点了。首先通过如下命令生成 join 的命令,包含了鉴权的 token:
shell
$ kubeadm token create --print-join-command
kubeadm join 10.211.55.10:6443 --token 4tg13t.14co8uefbx6jdjr8 --discovery-token-ca-cert-hash sha256:88d916c90ea239f3f6a369dcbc303c7ebd435fccb12707c1e13087d23dff8678
确保 Worker 节点的服务以及防火墙状态都正常:
shell
sudo systemctl status containerd
# 先关闭
sudo systemctl stop kubelet
sudo ufw status
然后执行 kubeadm join
命令,直接复制之前通过 kubeadm token create
生成的命令就可以。如果出现如下错误:

需要关闭 Swap 以及启用 ip forward:
shell
# 临时
sudo sysctl -w net.ipv4.ip_forward=1
# 永久配置
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf
# 生效
sudo sysctl -p
# 验证输出是否为 1
cat /proc/sys/net/ipv4/ip_forward
# 临时
sudo swapoff -a
# 永久配置
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# 验证输出 Swap 是否全部为 0
free -h
total used free shared buff/cache available
Mem: 3.8Gi 716Mi 1.9Gi 33Mi 1.4Gi 3.1Gi
Swap: 0B 0B 0B
看到类似如下输出,则证明已经 Joined 了集群:

最后, Worker 2 节点也执行相同的步骤即可。通过在 Master 节点上运行 kubectl get nodes
可以查看所有节点的状态:
shell
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 29m v1.32.5
k8s-worker-1 Ready <none> 2m55s v1.32.5
k8s-worker-2 Ready <none> 29s v1.32.5
来一张所有集群初始化成功的全家福:

总结
这篇文章从 0 到 1 完成了 K8s 的三节点的搭建,对于学习开发测试而言就已经足够了。但是如果是生产环境的话可能需要更多的节点,从小规模到大规模可能需要 3 到 5 个 Master 节点,也对应更多的 Worker 节点。
整个搭建的过程,虽然步骤比较多,中间也可能会出现一些小问题。但是使用 kubeadm 也不算复杂。这个过程和其他分布式应用都差不多。耐下心,一步一步来,遇到问题解决问题就行了。
附录:Parallels Desktop 中 VM 如何重新生成 product-ID
在日常的开发中,经常需要使用虚拟机或者容器,或者在虚拟机中部署容器。我现在使用的是 MacBook Pro,使用 M4 Pro 芯片。所以虚拟机使用 Parallels Desktop,能够保证最少的性能损耗。
我在部署 K8s 集群的过程中,Clone 了几台 VM,但是发现这几台机器的 product-id 是一致的,而 K8s 集群通过这个 product-id 来区分不同的机器。
但是很遗憾的是,没有办法通过虚拟机中的命令行来重新生成 product-id。通过查阅官方文档,发现了如下的说明:

可以通过 Unregister 后在此 Register ,附加 --regenerate-src-uuid
来重新生成 product ID。
具体过程如下:
bash
# 假设我的虚拟机名称为 "K8s-Master"
# 首先需要查找起 Path
prlctl list -a -i "K8s-Master" | grep "Home:"
# 上面命令输出: Home: /Users/user/Parallels/K8s-Master.pvm/
# Unregister
prlctl unregister "K8s-Master"
# Re-Register
prlctl register "/Users/sujinzhi/Parallels/K8s-Master.pvm/" --regenerate-src-uuid
然后重新启动 VM 后查看重新生成 product-ID:
bash
sudo cat /sys/class/dmi/id/product_uuid
[sudo] password for parallels:
dddcef29-de5b-4244-8c66-e285f1d41d87
主要注意,Re-Register 之后,例如端口转发之类的配置会删除,需要重新配置。
话说,我本来是问 AI 如何来解决这个问题的,AI 的回答告诉了我可以通过 prlctl
命令来重新生成,只是它搞错了具体的子命令和参数。看起来,在 AI 大行其道的今天,还是要保留自己查阅文档的能力的。