一、背景
最近在学习k8s,需要搭建一个集群环境用来练习。但是那台"老破旧"的window电脑实在有点扛不住,每次用VMware跑三台虚拟机,风扇起飞,电脑卡顿,人也跟着卡顿。刚好办公电脑是Mac,性能还不错,于是我就想着:下班时间拿它去跑k8s,这样又不会浪费,又解决了自身电脑卡顿问题。
一开始我的思路还是比较传统:使用VMware fusion,下载Ubuntu Server镜像,然后克隆出三个节点,分别作为master、worker1、worker2,再安装部署kubernetes。
在Ubuntu官网下载镜像的时候,我发现了一个叫Mutipass的虚拟机管理工具。它比VM fusion更轻量,主要通过命令行操作,特别适快速创建ubuntu server这类虚拟机环境。它的典型使用场景包括:
- 快速搭建临时开发环境
- 测试脚本或服务部署流程
- 跑容器、跑k8s实验环境
- 创建轻量级Linux虚拟机用于学习和验证
刚开始因为不熟悉Mutipass,我还是优先选择了VM fusion。结果 VMware 那边没跑通,我又懒得继续研究图形界面的报错,于是果断回头研究 Multipass。
简单对比之后,我发现自己的需求其实很明确:
- 只需要CLI操作,不需要图形桌面
- 希望虚拟机尽量轻量
- 主要用于k8s的学习与研究
- 不追求生产环境规格,只求能跑、能部署、能排错
所以最终决定使用Mutipass + Ubuntu 24.04 + containerd + k8s 来搭建一套本地实验集群。
二、环境准备
- MacBook Air Apple M3 8G 256G macOS Sonoma
- multipass 1.16.1
- ubuntu 24.04
- Kubernetes v1.31
- containerd作为容器运行时
这套环境主要用于学习和实验,不建议直接按照这个规格用于生产环境。
2C2G 的虚拟机跑 Kubernetes 属于"能活着就不错了",别指望它顺便帮你跑一套完整微服务系统。
三、安装Multipass并创建虚拟机
1. 安装Multipass
在macOS上推荐使用Homebrew Cask 安装Multipass:
brew install --cask multipass
2. 创建k8s虚拟机
这里创建三台Ubuntu 24.04 虚拟机:
- k8s-master:控制节点
- k8s-worker1:工作节点 1
- k8s-worker2:工作节点 2
由于我的 Mac 内存和磁盘空间有限,所以每台虚拟机配置为 2 核 CPU、2G 内存、10G 磁盘。这个规格用于学习 K8s 基础操作基本够用,但不要往里面塞太多业务服务,否则它会用实际行动告诉你什么叫"资源紧张"。
multipass launch 24.04 --name k8s-master --cpus 2 --memory 2G --disk 10G
multipass launch 24.04 --name k8s-worker1 --cpus 2 --memory 2G --disk 10G
multipass launch 24.04 --name k8s-worker2 --cpus 2 --memory 2G --disk 10G
这里指定使用Ubuntu 24.04 版本的镜像创建虚拟机,避免后续ubuntu默认镜像更新导致环境不一致
3. 常用Multipass管理命令
查询所有虚拟机
multipass list
进入某台虚拟机
multipass shell k8s-master
关机
multipass stop k8s-master
开机
multipass start k8s-master
从虚拟机拉取文件到本机
multipass transfer worker1:/home/ubuntu/xxx.tar ~/Desktop/
从本机传文件到虚拟机
multipass transfer ~/Desktop/xxx.tar worker2:/home/ubuntu/
更改虚拟机规格
multipass set local.woker1.cpus=4
multipass set local.woker1.memory=4G
multipass set local.woker2.disk=20G
注意:
磁盘一般只能扩容,不能缩容。扩容前最好先停机,避免出现奇怪问题。
克隆虚拟机
multipass clone -n k8s-worker1 k8s-base
这里需要注意,multipass clone 的目标名称要通过 --name 或 -n 指定,不能直接写两个实例名,否则容易出现 Too many arguments 这类报错。
四、安装containerd
kubernetes 1.24 之后移除了内置的dockershim,因此现在更推荐直接使用containerd作为容器运行时。
以下操作需要在三台虚拟机上都执行:
multipass shell k8s-master/worker1/worker2
sudo -i
1. 安装containerd
apt update
apt install -y containerd
# 查看版本验证
containerd -version
2. 生成默认配置
创建containerd配置目录:
mkdir -p /etc/containerd
生成默认配置文件:
containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
3. 修改SystemdCgroup
K8s 官方推荐 kubelet 和容器运行时都使用 systemd 作为 cgroup 驱动,这样可以避免 kubelet 和 containerd 在资源管理上产生不一致。
修改containerd配置
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
如果配置文件中存在下面这一行:
disabled_plugins = ["cri"]
需要删除或注释掉。否则containerd的CRI插件不会启用,后面kubeadm初始化时可能会报类似错误: unknown service runtime.v1.RuntimeService。
重启并设置开机自启:
systemctl restart containerd
systemctl enable containerd
五、基础环境配置
以下基础配置需要在所有节点上执行。
1. 配置内核模块
k8s网络依赖Linux内核的一些模块和转发能力,因此需要提前配置。
加载overlay及br_netfilter模块:
cat <<EOF | tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
立即加载
modprobe overlay
modprobe br_netfilter
这里简单解释一下:
- overlay:用于支持容器镜像的分层文件系统。容器镜像不是一个完整大包,而是一层一层叠起来的,overlayfs就像是"千层钱管理员"。
- br_netfilter:用于让 Linux bridge经过的流量可以进入 iptables 进行处理,从而支持Service转发、NetworkPolicy等k8s网络能力。
2. 配置内核参数
cat <<EOF | 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
参数立即生效
sysctl --system
参数说明:
- net.bridge.bridge-nf-call-iptables =1:用于让经过Linux Bridge的IPv4流量进入iptables处理。
- net.bridge.bridge-nf-call-ip6tables =1 用于让经过Linux Bridge的IPv6流量进入iptables处理。
- net.ipv4.ip_forward =1 :开启Linux路由转发能力,保证跨Pod、跨节点通信能够正常转发数据包
简单讲就是:
K8s 里的pod网络不是靠玄学打通的,它背后依赖Linux网桥、路由转发、iptables等能力。如果这些基础没配置好,后面网络插件装好了也可能通信失败。
3. 关闭swap
swap是Linux的虚拟内存机制,当物理内存不足时,内核会将部分不活跃内存页交换到磁盘。
k8s要求关闭swap,是因为kubelet依赖"可控的物理内存"进行调度,如果启用swap,会导致节点资源判断不准确。
关闭swap:
swapoff -a
删除/etc/fstab中的swap配置,避免重启后自动开启:
sed -i '/swap/d' /etc/fstab
六、安装crictl
因为本次不安装 Docker,所以不能使用 docker ps 查看容器状态。
在 containerd 场景下,可以使用 crictl 查看容器、Pod Sandbox、镜像等信息。它是排查 Kubernetes 节点问题时非常实用的工具。
1. 安装crictl
先确认系统架构:
dpkg --print-architecture
在 Apple Silicon 的 Mac 上,Multipass 创建的 Ubuntu 虚拟机通常也是 ARM64,因此这里下载 ARM64 版本。
VERSION="v1.32.0"
wget https://github.com/kubernetes-sigs/cri-tools/releases/download/${VERSION}/crictl-${VERSION}-linux-arm64.tar.gz
sudo tar zxvf crictl-${VERSION}-linux-arm64.tar.gz -C /usr/local/bin
注意:
这里要看的是虚拟机内部架构,不是单纯看宿主机。最稳妥的方式就是执行 dpkg --print-architecture。
2. 配置crictl
配置 containerd 的运行时 socket:
sudo tee /etc/crictl.yaml <<EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
EOF
3. 验证crictl
查看runtime是否ready
crictl info
查看 containerd 状态:
systemctl status containerd
查看 containerd socket:
ls -l /run/containerd/containerd.sock
如果这些命令都正常,说明 containerd 和 crictl 基本配置完成。
七、安装k8s
以下操作需要在所有节点上执行。
1. 添加k8s官方源
Ubuntu默认软件源里面没有k8s的kubelet、kubeadm、kubectl 安装包,故需要手动添加官方源
# 添加 K8s 密钥与源
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | 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.31/deb/ /' | tee /etc/apt/sources.list.d/kubernetes.list
2. 安装kubectl、kubelet、kubeadm
apt update
apt install -y kubelet kubeadm kubectl
锁定版本,避免后续系统更新时自动升级k8s组件:
apt-mark hold kubelet kubeadm kubectl
八、集群初始化
1. 安装conntrack工具
conntrack是linux网络连接跟踪工具,k8s的service转发、iptables/ipvs、Pod网络通信都会依赖连接跟踪能力。
如果缺少这个工具,kubeadm init 或 kubeadm join 时可能会报错:
[ERROR FileExisting-conntrack]: conntrack not found in system path
在所有节点安装:
apt install -y conntrack
2. 控制节点初始化
仅在master节点执行
先确认master节点IP:
ip addr
这里我的master节点IP为:192.168.64.11
初始化k8s 控制节点:
kubeadm init \
--apiserver-advertise-address=192.168.64.11 \ #api-server绑定的IP
--control-plane-endpoint=192.168.64.11 \ #控制节点的IP,若多master节点,可采用keepaliverd/HAproxy的VIP
--image-repository=registry.k8s.io \ #可以使用registry.aliyuncs.com/google_containers替代,避免拉取超时
--pod-network-cidr=10.144.0.0/16 \ #分配给pod的IP地址范围
--service-cidr=10.96.0.0/16 # 分配给service的IP地址范围
参数说明:
- apiserver-advertise-address:api-server对外监听的地址,一般写master的节点IP。
- control-plane-endpoint:控制平面访问入口。单master可直接写master IP;多master场景会使用keepaliverd/HAproxy的VIP。
- image-repository:k8s组件镜像仓库。如果访问较慢,可替换为国内镜像仓库,如registry.aliyuncs.com/google_containers。
- pod-network-cidr: 分配给Pod的IP地址范围。
- service-cidr: Service虚拟IP地址段。
注意:
本文中的 Multipass 虚拟机网段是 192.168.64.0/24,因此不要使用会覆盖它的 Pod 网段,比如 192.168.0.0/16。
如果 Pod CIDR 覆盖了 Node 管理网段,可能导致宿主机到虚拟机的 SSH 流量被误判,最终出现虚拟机无法连接的问题。这个坑有点隐蔽,因虚拟机连接超时,排查起来也比较繁琐。
初始化成功后,会看到类似输出:
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/
You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:
kubeadm join 192.168.64.11:6443 --token xcp9kp.qvghvtt4gpeu2umi \
--discovery-token-ca-cert-hash sha256:724f6837de3d9fe855f1201244284146909668f218b6cbba65243ae06211e0ec \
--control-plane
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.64.11:6443 --token xcp9kp.qvghvtt4gpeu2umi \
--discovery-token-ca-cert-hash sha256:724f6837de3d9fe855f1201244284146909668f218b6cbba65243ae06211e0ec
3. 配置kubeconfig
如果使用普通用户操作 kubectl,按照初始化提示执行:
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
如果当前直接使用 root 用户,也可以临时设置:
export KUBECONFIG=/etc/kubernetes/admin.conf
建议后续长期使用时,把 kubeconfig 正确放到用户目录下,避免每次打开终端都要重新 export。
验证集群状态:
kubectl get nodes
此时 master 节点大概率会显示 NotReady,这是正常的,因为 CNI 网络插件还没有安装。
4. 安装CNI网络插件
CNI即容器网络接口,它的作用是为k8s 提供pod网络能力,让不同节点上的pod能够正常通信。
常用的网络插件有Flannel和calico。
Flannel轻量,安装快捷,但是我们这里不使用它,而是使用Calico,这是因为Flannel不支持netp(网络策略),如果后续要学习网络策略及网络模型相关的概念,则Flannel不适用,又要重新搭建k8s集群,较为繁琐。
下载Calico配置文件:
wget https://raw.githubusercontent.com/projectcalico/calico/v3.30.2/manifests/calico.yaml
应用Calico:
kubectl apply -f calico.yaml
查看 Pod 状态:
kubectl get pods -A
查看节点状态:
kubectl get nodes
等待一段时间后,master 节点应该会从 NotReady 变成 Ready。
5. 工作节点加入集群
在 worker 节点执行 kubeadm 初始化输出的 join 命令
kubeadm join 192.168.64.11:6443 --token xcp9kp.qvghvtt4gpeu2umi \
--discovery-token-ca-cert-hash sha256:724f6837de3d9fe855f1201244284146909668f218b6cbba65243ae06211e0ec
如果初始化时的 join 命令忘记保存,或者 token 已经过期,可以在 master 节点重新生成:
kubeadm token create --print-join-command
worker 节点加入后,在 master 节点查看:
kubectl get nodes
正常情况下可以看到类似结果:

至此,一个基于 Multipass 的本地 Kubernetes 集群就搭建完成了。
九、常见问题排查
1. 创建虚拟机失败
执行创建虚拟机命令时出现:
➜ ~ multipass launch --name k8s-master --cpus 2 --memory 2G --disk 10G
报错:
launch failed: Remote "release" is unknown or unreachable. If image mirror is enabled, please confirm it is valid.
Multipass 无法访问 Ubuntu 官方镜像源,导致镜像下载失败。
常见原因有:
- 国内网络访问 Ubuntu 官方镜像源不稳定
- Multipass 默认镜像地址超时
- 本地 DNS 解析异常
- Multipass 第一次启动时需要联网下载镜像
后来我显式指定 Ubuntu 24.04 版本重新创建:
➜ ~ multipass launch 24.04 --name k8s-master --cpus 2 --memory 2G --disk 10G
又遇到另一个错误:
launch failed: Hash of /var/root/Library/Caches/multipassd/qemu/vault/images/noble-20260321/ubuntu-24.04-server-cloudimg-arm64.img does not match 1ea801e659d2f5035ac294e0faab0aac9b6ba66753df933ba5c7beab0c689bd0
这个错误一般表示镜像缓存损坏,可能是镜像下载过程中断,或者前后多次执行创建命令导致缓存不完整。
解决方式是清理Multipass 镜像缓存:
sudo rm -rf /var/root/Library/Caches/multipassd/
重启multipass服务
sudo launchctl stop com.canonical.multipassd
sudo launchctl start com.canonical.multipassd
重新创建虚拟机
multipass launch 24.04 --name k8s-master --cpus 2 --memory 2G --disk 10G
最终成功解决
2. 集群初始化或节点加入失败:缺少 conntrack
报错信息:
preflight Running pre-flight checks
error execution phase preflight: preflight Some fatal errors occurred:
ERROR FileExisting-conntrack: conntrack not found in system path
preflight If you know what you are doing, you can make a check non-fatal with --ignore-preflight-errors=...
To see the stack trace of this error execute with --v=5 or higher
原因是系统缺少 conntrack 工具。
解决方式:
apt install -y conntrack
建议在 master 和 worker 节点上都提前安装,避免初始化或加入集群时再被它拦住。
3. kubectl 连接 localhost:8080 失败
执行:
kubectl apply -f calico.yaml
报错:
error: error validating "calico.yaml": error validating data: failed to download openapi: Get "http://localhost:8080/openapi/v2?timeout=32s": dial tcp ::1:8080: connect: connection refused; if you choose to ignore these errors, turn validation off with --validate=false
这个错误通常表示 kubectl 没有读取到正确的 kubeconfig。
kubectl 默认会尝试连接 localhost:8080,但 Kubernetes API Server 实际并不在这个地址上,所以就会连接失败。
解决方式是按照 kubeadm 初始化后的提示配置 kubeconfig:
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
4. containerd没有正常配置
初始化k8s时出现:
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
这个错误一般说明 kubeadm 连接到了 containerd,但 containerd 的 CRI 插件没有正常启用。
常见原因:
- 没有生成 /etc/containerd/config.toml
- 配置文件中禁用了 CRI 插件
- containerd 修改配置后没有重启
- SystemdCgroup 没有正确配置
处理方式:
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
检查是否禁用了 CRI:
grep disabled_plugins /etc/containerd/config.toml
如果看到:
disabled_plugins = ["cri"]
需要删除或注释。
修改 SystemdCgroup:
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
重启 containerd:
systemctl restart containerd
systemctl enable containerd
验证:
crictl info
如果 crictl info 能正常返回信息,说明 containerd 的 CRI 能力已经正常。
5、Pod 网段与 Node 网段重叠导致虚拟机无法 SSH
现象如下:
➜ ~ multipass info docker1
info failed: ssh connection failed: 'Timeout connecting to 192.168.64.15'
➜ ~ nc -vz 192.168.64.15 22
^@nc: connectx to 192.168.64.15 port 22 (tcp) failed: Operation timed out
➜ ~ telnet 192.168.64.15 22
Trying 192.168.64.15... telnet: connect to address 192.168.64.15: Operation timed out telnet: Unable to connect to remote host
若Pod与node 网段重叠,则会出现虚拟机shell命令行自动断开,且无法重新连接访问,只能关闭其中一台节点,重新初始化集群配置
最开始我 Pod网段是按照calico默认网段写的192.168.0.0/16,但是后来搭建完成后,虚拟机就自动断开,后面也无法连接上
如果 Pod 网段与 Node 管理网段重叠,Kubernetes 网络插件或 iptables 规则可能会把宿主机访问虚拟机的流量误判为 Pod 网络流量,最终导致 SSH 连接异常。
我这里 Multipass 虚拟机使用的是:192.168.64.0/24,而最开始按照Calico默认网段写的192.168.0.0/16,搭建完后,虚拟机就卡住,然后自动断开,状态一直running,但一直无法连接上,只能启动一台节点,将其他节点关闭,这样才能重新连接上
如果已经因为网段冲突导致集群异常,建议直接重置集群后重新初始化:
kubeadm reset -f
清理残留网络配置时可以按需检查:
ip link
ip route
iptables -S
这个问题看起来像 Multipass SSH 问题,实际上根因在 K8s 网络规划。表面是 SSH 断了,背后是 Pod 网段和 Node 网段在存在冲突覆盖,导致流量转发异常,所以所Pod网段不能直接按照网络插件默认来配置,还是与其他网段区分开来写比较好。
十、扩展:Kubernetes 1.24+ 继续使用 Docker 的方式
Kubernetes 1.24 之后移除了内置的 dockershim,因此 kubelet 不能再直接通过 dockershim 调用 Docker。尽管现在更推荐的方式是:
K8s + containerd,但有些技术人员仍然习惯使用 Docker 管理镜像和容器。
如果在 Kubernetes 1.24+ 中继续使用 Docker,就需要额外安装 cri-dockerd。
cri-dockerd 是 Kubernetes 和 Docker 之间的 CRI 适配层,它负责让 kubelet 可以通过 CRI 接口继续调用 Docker。
简单理解:
kubelet → CRI → cri-dockerd → Docker → containerd
需要注意的是,使用 Docker 并不表示完全不需要 containerd。Docker 底层本身也依赖 containerd,只是我们不再像前面那样直接把 containerd 配置成 Kubernetes 的容器运行时。
1. 安装Docker
apt update -y
apt install -y docker.io
启动 Docker:
systemctl enable docker
systemctl start docker
2. 安装 Go、Git、Make
apt install -y golang git make
3. 下载cri-dockerd
git clone https://github.com/Mirantis/cri-dockerd.git
4. 编译cri-dockerd
cd cri-dockerd
make cri-dockerd
安装二进制文件:
install -o root -g root -m 0755 cri-dockerd /usr/local/bin/cri-dockerd
5、安装systemd服务
cp packaging/systemd/* /etc/systemd/system/
修改service文件
sed -i 's#/usr/bin/cri-dockerd#/usr/local/bin/cri-dockerd#g' /etc/systemd/system/cri-docker.service
重新加载 systemd:
systemctl daemon-reload
启动 cri-dockerd:
systemctl enable cri-docker
systemctl start cri-docker
查看状态:
systemctl status cri-docker
验证 socket:
ls /run/cri-dockerd.sock
6. 使用cri-dockerd初始化k8s
控制节点初始化时需要指定 CRI socket:
kubeadm init \
--cri-socket unix:///run/cri-dockerd.sock \
--apiserver-advertise-address=192.168.64.11 \
--control-plane-endpoint=192.168.64.11 \
--pod-network-cidr=10.144.0.0/16 \
--service-cidr=10.96.0.0/16
工作节点加入集群时也需要指定:
kubeadm join 192.168.64.11:6443 \
--token filyxs.q0mkwuayzw2ivmrd \
--discovery-token-ca-cert-hash sha256:00ace226585dcce876b0bce7237cdad36b769eeb19ca420dc7cf0f6011e92c03 \
--cri-socket unix:///run/cri-dockerd.sock
十一、总结
这次使用 Multipass 在 Mac 上搭建 Kubernetes 集群,整体过程并不复杂,但中间也踩了几个很典型的坑:
- Multipass 镜像缓存损坏导致虚拟机创建失败
- 缺少 conntrack 导致 kubeadm 初始化或节点加入失败
- kubeconfig 未配置导致 kubectl 默认连接 localhost:8080
- containerd CRI 插件未正常启用导致 kubeadm 无法连接容器运行时
- Pod 网段与 Node 管理网段重叠导致 Multipass 虚拟机 SSH 连接异常
对于本地学习环境来说,Multipass 确实是一个不错的选择。它足够轻量,命令行操作也比较直接,特别适合在 Mac 上快速创建 Ubuntu 虚拟机做实验。
当然,它也不是没有脾气。镜像下载、缓存、网络、SSH 都可能偶尔给你一点"惊喜"。但从学习 K8s 的角度来看,这些问题反而挺有价值,因为 K8s 学到最后,绕不开的就是 Linux、网络、容器运行时和排障能力。
正所谓,工欲善其事,必先利其器,要想学k8s,搭环境也是必备的技能,当然Linux基操也是核心,不然遇到错误也只能干瞪眼!