上一篇我们已经在 VirtualBox 里安装好了 Ubuntu 24.04 Server,并通过 SSH 连接到了这台虚拟机。

这一篇开始进入核心部分:在 Ubuntu 24.04 上安装一个单节点 Kubernetes 集群

这套环境仍然是个人实验室定位,不追求生产级高可用,也不追求复杂架构。目标是先把一个可以反复实验的 Kubernetes 单节点环境跑起来,为后续 Prometheus、Grafana、Loki、Tempo、OpenTelemetry、CI/CD 等内容打基础。


一、这一篇要完成什么?

这一篇主要完成几件事:

Ubuntu 24.04 Server
  -> 安装 Docker Engine
  -> 配置 Linux 内核网络参数
  -> 安装 kubeadm / kubelet / kubectl
  -> 安装 cri-dockerd
  -> 使用 kubeadm 初始化单节点 Kubernetes
  -> 安装 Flannel Pod 网络
  -> 让单节点同时承担控制平面和工作负载

最终目标是执行下面命令时,能看到节点状态为 Ready

kubectl get nodes

并且所有 Kubernetes 系统 Pod 都正常运行:

kubectl get pods -A

二、为什么这里选择 Docker + cri-dockerd?

现在更常见、更标准的 Kubernetes 安装方式,是直接使用 containerd 作为容器运行时。

不过这次实验室里,我还是选择了 Docker Engine + cri-dockerd 这条路线,主要有几个原因。

第一,Docker 对很多人来说更熟悉。后续无论是构建镜像、查看镜像,还是做一些本地实验,Docker 命令都比较直观。

第二,这个系列是个人实验室,不是生产环境。这里的重点是把 Kubernetes、可观测、CI/CD 等内容串起来,而不是一开始就在运行时选择上追求最简洁或最标准。

第三,Kubernetes 从 1.24 开始已经移除了内置 dockershim,所以 kubelet 不能再直接把 Docker Engine 当成 CRI 运行时使用。如果仍然想用 Docker Engine,就需要额外安装 cri-dockerd,由它在 kubelet 和 Docker Engine 之间做一层适配。

也就是说,这里的关系大致是:

kubelet
  -> CRI
    -> cri-dockerd
      -> Docker Engine
        -> containerd / runc

如果你不想走 Docker 这条路线,也可以直接使用 containerd。那样会少一个 cri-dockerd 组件,但 Docker 命令使用体验会不一样。

本文先按我实际走过的 Docker + cri-dockerd 方案记录。


三、安装 Docker Engine

1. 添加 Docker 官方 apt 仓库

先更新 apt 索引,并安装基础工具:

sudo apt update
sudo apt install -y ca-certificates curl

创建 apt keyring 目录:

sudo install -m 0755 -d /etc/apt/keyrings

下载 Docker 官方 GPG key:

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc

设置 key 文件权限:

sudo chmod a+r /etc/apt/keyrings/docker.asc

添加 Docker apt 源:

sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

再次更新 apt 索引:

sudo apt update


2. 安装 Docker 相关组件

安装 Docker Engine、Docker CLI、containerd 以及常用插件:

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

安装完成后,可以查看 Docker 服务状态:

systemctl status docker

也可以执行:

sudo docker version

如果能正常显示 Docker Client 和 Server 信息,说明 Docker 已经安装成功。


四、配置 Docker

1. 配置 cgroup driver 和日志大小

Kubernetes 里推荐让容器运行时和 kubelet 使用一致的 cgroup driver。这里把 Docker 的 cgroup driver 设置为 systemd

创建 Docker 配置目录:

sudo mkdir -p /etc/docker

写入 Docker daemon 配置:

sudo tee /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  }
}
EOF

这里顺手限制了 Docker 容器日志文件大小,避免后面实验过程中日志无限增长。


2. 可选:给 Docker 配置 HTTP 代理

如果你的网络环境需要代理访问 Docker Hub 或其他镜像源,可以给 Docker 服务配置代理。

创建 systemd drop-in 目录:

sudo mkdir -p /etc/systemd/system/docker.service.d

配置 HTTP 代理:

sudo vi /etc/systemd/system/docker.service.d/http-proxy.conf

写入:

[Service]
Environment="HTTP_PROXY=http://proxy_ip_address:proxy_port"
Environment="NO_PROXY=127.0.0.1,localhost,10.96.0.0/12,10.244.0.0/16,192.168.56.0/24,192.168.31.0/24"

配置 HTTPS 代理:

sudo vi /etc/systemd/system/docker.service.d/https-proxy.conf

写入:

[Service]
Environment="HTTPS_PROXY=http://proxy_ip_address:proxy_port"
Environment="NO_PROXY=127.0.0.1,localhost,10.96.0.0/12,10.244.0.0/16,192.168.56.0/24,192.168.31.0/24"

其中:

10.96.0.0/12

是后面 Kubernetes 默认 Service 网段。

10.244.0.0/16

是本文后面 Flannel 使用的 Pod 网段。

192.168.56.0/24

是 VirtualBox Host-Only 网络常见网段。

192.168.31.0/24

是我这里局域网网段示例。你的环境如果不是这个网段,可以按实际情况修改。

代理地址也要按自己的情况替换,例如:

http://192.168.31.49:7890

如果你不需要代理,可以跳过这一小节。


3. 重启 Docker 服务

配置完成后,重新加载 systemd 配置:

sudo systemctl daemon-reload

设置 Docker 开机自启:

sudo systemctl enable docker

重启 Docker:

sudo systemctl restart docker

查看 Docker 状态:

systemctl status docker

如果状态是 active (running),说明 Docker 服务正常。


五、打开 Kubernetes 需要的内核网络参数

Kubernetes 网络需要 Linux 内核支持转发和桥接流量处理。这里按 kubeadm 常见要求配置 overlaybr_netfilter 和 sysctl 参数。

1. 加载内核模块

写入模块配置:

sudo tee /etc/modules-load.d/k8s.conf <<EOF
overlay
br_netfilter
EOF

立即加载模块:

sudo modprobe overlay
sudo modprobe br_netfilter

检查模块是否加载:

lsmod | grep -E 'overlay|br_netfilter'

2. 配置 sysctl 参数

写入 Kubernetes 所需网络参数:

sudo tee /etc/sysctl.d/k8s.conf <<EOF
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

立即生效:

sudo sysctl --system

可以检查 IP 转发是否打开:

sysctl net.ipv4.ip_forward

正常应该看到:

net.ipv4.ip_forward = 1

这一步很重要。否则后面 Pod 网络、Service 转发等可能出现各种奇怪问题。


六、安装 kubeadm、kubelet、kubectl

接下来安装 Kubernetes 三个核心工具:

kubeadm:用于初始化和管理 Kubernetes 集群
kubelet:运行在每个节点上的核心 Agent
kubectl:命令行客户端,用于操作 Kubernetes 集群

本文使用 Kubernetes v1.36 版本仓库。


1. 安装基础依赖

sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg

如果系统提示 apt-transport-https 是 dummy package,也没关系,继续即可。


2. 添加 Kubernetes v1.36 apt key

确保 keyring 目录存在:

sudo mkdir -p -m 755 /etc/apt/keyrings

下载 Kubernetes 仓库签名 key:

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

设置权限:

sudo chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg

3. 添加 Kubernetes v1.36 apt 仓库

echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.36/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

设置权限:

sudo chmod 644 /etc/apt/sources.list.d/kubernetes.list

更新 apt 索引:

sudo apt-get update

4. 查看可安装版本

安装之前,可以先看看 apt 仓库里有哪些版本:

apt-cache policy kubeadm
apt-cache policy kubelet
apt-cache policy kubectl

这里可以确认将要安装的具体 patch 版本。

比如我这里后面使用的是:

Kubernetes 1.36.2

你的环境中可能会显示不同 patch 版本。只要是同一个小版本分支,一般问题不大。


5. 安装 kubeadm、kubelet、kubectl

sudo apt-get install -y kubelet kubeadm kubectl

安装完成后,锁定版本,避免后续 apt upgrade 时自动升级导致集群组件版本变化:

sudo apt-mark hold kubelet kubeadm kubectl

查看版本:

kubeadm version
kubectl version --client
kubelet --version

七、关闭 swap

Kubernetes 默认要求关闭 swap。否则 kubelet 可能无法正常工作,或者需要额外配置才能允许 swap。

先立即关闭 swap:

sudo swapoff -a

查看是否已经关闭:

free -h

如果 Swap 显示为 0,说明当前已经关闭。


1. 修改 fstab,避免重启后 swap 再次启用

先备份 /etc/fstab

sudo cp /etc/fstab $HOME/fstab.backup.$(date +%F)

编辑 /etc/fstab

sudo vi /etc/fstab

找到类似下面这一行:

/swap.img       none    swap    sw      0       0

把它注释掉:

#/swap.img       none    swap    sw      0       0

保存退出。


2. 可选:删除 swap 文件

如果确认后续不再使用 swap,可以删除 swap 文件:

sudo rm -f /swap.img

再次检查:

free -h

八、启用 kubelet 服务

可以先设置 kubelet 开机自启:

sudo systemctl enable --now kubelet

这时 kubelet 可能还不是正常运行状态,这很正常。

因为集群还没有通过 kubeadm 初始化,kubelet 暂时还没有完整配置。后面 kubeadm init 完成后,它才会真正进入正常状态。


九、安装 cri-dockerd

前面已经安装了 Docker Engine,但 Kubernetes v1.36 的 kubelet 不能直接通过内置 dockershim 使用 Docker Engine。

所以这里需要安装 cri-dockerd

1. 下载 cri-dockerd deb 包

到 cri-dockerd 的 GitHub Releases 页面选择适合自己系统和架构的 deb 包。

我这里使用的是 amd64 架构,示例命令如下:

wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.4.4/cri-dockerd_0.4.4.3-0.debian-bookworm_amd64.deb

注意:这里的版本号和包名以实际 Releases 页面为准。你操作时如果已经有更新版本,可以选择对应的新版本。


2. 安装 cri-dockerd

sudo dpkg -i cri-dockerd_*.deb

如果安装过程中提示缺少依赖,可以执行:

sudo apt -f install

然后再确认安装状态。


3. 启动 cri-docker 服务

重新加载 systemd:

sudo systemctl daemon-reload

设置开机自启:

sudo systemctl enable cri-docker

重启服务:

sudo systemctl restart cri-docker

查看服务状态:

systemctl status cri-docker

如果看到 active (running),说明 cri-dockerd 已经正常运行。


4. 确认 CRI socket

后面 kubeadm 需要使用这个 socket:

unix:///var/run/cri-dockerd.sock

可以查看 socket 文件是否存在:

ls -l /var/run/cri-dockerd.sock

十、提前拉取 Kubernetes 镜像

在初始化集群之前,可以先让 kubeadm 拉取所需镜像。

因为我们使用 Docker + cri-dockerd,所以这里显式指定 CRI socket:

sudo kubeadm config images pull --cri-socket=unix:///var/run/cri-dockerd.sock

如果你的网络需要代理,这一步可能会比较慢,甚至失败。

这时可以检查:

systemctl show --property=Environment docker

确认 Docker 服务代理是否配置成功。


十一、生成 kubeadm 初始化配置

接下来生成 kubeadm 默认配置:

kubeadm config print init-defaults > kubeadm.conf

编辑配置文件:

vi kubeadm.conf

这里主要调整几个地方。


1. 配置 criSocket

找到 nodeRegistration 部分,确保 criSocket 指向 cri-dockerd:

nodeRegistration:
  criSocket: unix:///var/run/cri-dockerd.sock
  name: vbox-ubuntu24-server

name 建议和你上一章安装 Ubuntu 时配置的主机名保持一致。

可以用下面命令查看当前主机名:

hostname

2. 配置 Kubernetes 版本

找到:

kubernetesVersion:

改成当前实际安装的 Kubernetes 版本,例如:

kubernetesVersion: 1.36.2

版本号可以通过下面命令确认:

kubeadm version

如果你的环境显示的是其他 patch 版本,就按自己的实际版本填写。


3. 配置 Pod 网段

后面我们会使用 Flannel。Flannel 默认常用 Pod 网段是:

10.244.0.0/16

所以需要在 networking 部分加上或修改为:

networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.96.0.0/12
  podSubnet: 10.244.0.0/16

这里:

serviceSubnet: 10.96.0.0/12

是 Kubernetes Service 网段。

podSubnet: 10.244.0.0/16

是 Pod 网络网段,后面 Flannel 会用到。


4. 配置 advertiseAddress

配置文件里会有类似:

localAPIEndpoint:
  advertiseAddress: 1.2.3.4
  bindPort: 6443

这里可以改成当前节点的管理 IP。

如果你希望使用 VirtualBox Host-Only 网络作为管理地址,可以填第一篇里查到的 Host-Only IP,例如:

localAPIEndpoint:
  advertiseAddress: 192.168.56.103
  bindPort: 6443

我实操过程中也尝试过使用:

advertiseAddress: 0.0.0.0

单节点实验环境可以跑起来,但从表达和后续理解上,写成一个明确的节点 IP 会更直观。

所以更建议使用你自己的 Host-Only IP。


5. 配置文件示例

最终关键配置大致类似下面这样:

localAPIEndpoint:
  advertiseAddress: 192.168.56.103
  bindPort: 6443

nodeRegistration:
  criSocket: unix:///var/run/cri-dockerd.sock
  name: vbox-ubuntu24-server

kubernetesVersion: 1.36.2

networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.96.0.0/12
  podSubnet: 10.244.0.0/16

注意:不要只复制这一小段覆盖整个文件。建议在 kubeadm config print init-defaults 生成的完整文件里修改对应字段。


十二、使用 kubeadm 初始化集群

确认配置文件无误后,执行:

sudo kubeadm init --config kubeadm.conf

如果之前初始化失败过,可以先执行:

sudo kubeadm reset

然后再重新初始化。

初始化成功后,会看到类似输出:

Your Kubernetes control-plane has initialized successfully!

并且 kubeadm 会提示你执行下面几行命令:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

后面还会提示你安装 Pod 网络插件,并给出 worker node 加入集群用的 kubeadm join 命令。

由于我们这里是单节点实验室,暂时不需要关心 worker node 的 join 命令。


十三、配置 kubectl

按 kubeadm 提示,初始化当前用户的 kubeconfig:

mkdir -p $HOME/.kube

复制 admin 配置:

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

修改文件属主:

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

这时可以试一下:

kubectl get nodes

此时节点可能还是:

NotReady

这是正常的,因为 Pod 网络插件还没有安装。


十四、安装 Flannel Pod 网络

Kubernetes 初始化完成后,还需要安装 CNI 网络插件。这里选择 Flannel,原因是简单、轻量、适合单节点实验环境。

1. 下载 Flannel 配置文件

wget https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml

如果前面 kubeadm 配置里的 Pod 网段使用的是:

10.244.0.0/16

通常可以直接使用这个 Flannel 配置文件。


2. 部署 Flannel

kubectl apply -f kube-flannel.yml

部署后,可以查看所有 namespace 下的 Pod:

kubectl get pods -A

刚开始可能会看到一些 Pod 处于 PendingContainerCreatingInit 状态。等一会儿再看即可。

可以用下面命令持续观察:

watch kubectl get pods -A

十五、让控制平面节点也能运行普通 Pod

默认情况下,kubeadm 初始化出来的控制平面节点会带有 taint,普通业务 Pod 不会被调度到这个节点上。

但我们这是单节点实验室,只有一个节点。如果不去掉 taint,后面很多 Pod 会一直 Pending。

执行:

kubectl taint nodes --all node-role.kubernetes.io/control-plane-

如果提示 taint not found,也不用紧张,说明当前节点上可能已经没有这个 taint,或者版本行为有所变化。

查看节点信息:

kubectl describe node vbox-ubuntu24-server | grep -i taint -A2

如果看到:

Taints: <none>

说明普通 Pod 可以调度到这个节点。


十六、检查集群状态

1. 查看 Pod 状态

kubectl get pods -A

正常情况下,会看到类似这些组件:

kube-system   coredns-xxx
kube-system   etcd-vbox-ubuntu24-server
kube-system   kube-apiserver-vbox-ubuntu24-server
kube-system   kube-controller-manager-vbox-ubuntu24-server
kube-system   kube-proxy-xxx
kube-system   kube-scheduler-vbox-ubuntu24-server
kube-flannel  kube-flannel-ds-xxx

等待它们都变成 Running


2. 查看节点状态

kubectl get nodes

最终希望看到类似:

NAME                   STATUS   ROLES           AGE   VERSION
vbox-ubuntu24-server   Ready    control-plane   10m   v1.36.2

看到 Ready,说明这个单节点 Kubernetes 集群已经跑起来了。


十七、做一个最小验证

为了确认集群能正常调度 Pod,可以创建一个 nginx 测试 Pod。

kubectl create deployment nginx --image=nginx

查看 Pod:

kubectl get pods

等到 Pod 变成 Running

再暴露一个 NodePort 服务:

kubectl expose deployment nginx --port=80 --type=NodePort

查看服务:

kubectl get svc

会看到类似:

NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        ...
nginx        NodePort    10.96.x.x       <none>        80:30xxx/TCP   ...

其中 30xxx 就是 NodePort 端口。

可以在 Ubuntu 虚拟机里访问:

curl http://127.0.0.1:30xxx

也可以在 Windows 宿主机上,通过 Host-Only IP 访问:

http://192.168.56.103:30xxx

如果能看到 nginx 欢迎页,说明 Pod 调度、Service 暴露和基础网络都已经正常。

测试完成后,可以删除 nginx:

kubectl delete svc nginx
kubectl delete deployment nginx

十八、这一篇完成了什么?

到这里,单节点 Kubernetes 环境已经安装完成。

这一篇我们完成了:

  • 安装 Docker Engine

  • 配置 Docker cgroup driver 和日志限制

  • 可选配置 Docker HTTP/HTTPS 代理

  • 打开 Kubernetes 所需内核模块和 sysctl 参数

  • 安装 kubeadm、kubelet、kubectl

  • 关闭 swap

  • 安装 cri-dockerd

  • 使用 kubeadm 初始化 Kubernetes 控制平面

  • 安装 Flannel Pod 网络

  • 去掉控制平面节点 taint,让单节点可以运行普通工作负载

  • 验证节点和系统 Pod 状态

  • 用 nginx 做了最小可用性验证

现在,这台 Ubuntu 24.04 虚拟机已经从一台普通 Linux Server,变成了一个可以继续扩展的单节点 Kubernetes 实验环境。

下一篇开始,就可以在这个 Kubernetes 集群上安装 Prometheus + Grafana,开始观察集群指标。

参考资料

本文基于个人实验环境整理,具体版本和命令可能会随官方文档更新而变化。实际操作时建议结合以上官方资料确认最新安装方式。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐