Alice.tech

4 节点 k3s(--docker)集群 — 最小可测试方案

目标:在单台 Proxmox VE 上以 最低资源 建一个 4 节点 k3s( docker)集群,验证基本功能跑通 宿主机:Intel Xeon E5 2650(32 线程)/ 192 GB RAM / 340 GB …

目标:在单台 Proxmox VE 上以最低资源建一个 4 节点 k3s(–docker)集群,验证基本功能跑通 宿主机:Intel Xeon E5-2650(32 线程)/ 192 GB RAM / 340 GB 剩余磁盘 风格:最小可测试,资源够验证即可,随时可扩 角色:researcher · 数据驱动分析


1. 设计原则

1.1 “最小可测试”的定义

根据资料显示,本方案所有资源的下限锚定在“能让以下 5 个目标全部跑通”:

# 验证目标 通过标准 驱动资源下限
1 4 台 VM 正常启动 ping 互通,SSH 可达 单 VM ≥ 512MB RAM,≥ 8GB 磁盘(Ubuntu Server 最小安装)
2 k3s 在 control-plane 上安装运行 k3s kubectl get nodes 返回 control-plane Ready 单 VM ≥ 1.5GB RAM(k3s server + Docker + OS)
3 worker 节点加入集群 kubectl get nodes 显示 4 个 Ready 每个 worker ≥ 1GB RAM(k3s agent + Docker + OS)
4 调度测试 Pod kubectl run nginx --image=nginx → Pod Running 要有足够内存拉取镜像 + 运行 nginx(≈ 200MB 额外)
5 GZCTF 的 k8s 模式(可选) GZCTF 通过 kube-config 创建 Pod 需要额外 ~1GB RAM 跑 GZCTF + PostgreSQL + Redis

结论的推论:验证目标 1-4 的最小 RAM 需求 = control-plane 2 GB + 3 × worker 1.5 GB = 6.5 GB 总 RAM。对比宿主机 192 GB,资源极其宽裕,所以“最小可测试”真正的约束不在 RAM,而在磁盘(仅 340 GB)和 最小合理 vm.s 值(Proxmox 分配单 VM 少于 1GB RAM 在实际中不稳定)。

1.2 “轻松扩容”的定义

每个维度的扩容方式:

资源 扩容方式 是否需要关机 Proxmox 命令/操作
vCPU qm set <vmid> --cores <N> ❌ 热插拔支持(需 guest 配合,Linux 通常需重启) ✅ 但建议关机修改最稳
RAM qm set <vmid> --memory <MB> ✅ 需要重启 VM 关机 → 改配置 → 开机
磁盘 qm resize <vmid> <storage> <size>+<增量> ❌ 在线扩展 扩容后 VM 内 growpart + resize2fs
网卡 桥接模式无需变更 — 带宽随 Proxmox 物理网卡走

2. 最小 VM 配置方案

2.1 节点配置表

节点名称 角色 vCPU RAM 磁盘 Proxmox VM ID 建议 IP 地址建议
k3s-cp control-plane(可调度) 2 2 GB 20 GB 201 192.168.1.51/24
k3s-w1 worker 1 1.5 GB 15 GB 202 192.168.1.52/24
k3s-w2 worker 1 1.5 GB 15 GB 203 192.168.1.53/24
k3s-w3 worker 1 1.5 GB 15 GB 204 192.168.1.54/24
合计 5 6.5 GB 65 GB

磁盘 65 GB / 340 GB → 仅用 19%,剩余 275 GB 留给扩容。

2.2 为什么是这个配置——逐项数据说明

vCPU

  • control-plane 给 2 核:k3s server 本身(含 embedded etcd)在日常负载下使用约 0.3-0.5 核。2 核保证 kubectl 命令响应和测试期间不会动不动就 throttle
  • worker 给 1 核:k3s agent 几乎不消耗 CPU(< 0.1 核 idle)。1 核足以验证节点加入和调度 Pod
  • 数据来源:k3s 官方文档 resource requirements、社区跑分实测

RAM(详见下方 2.3 节内存细账)

  • control-plane 2 GB:经过精算,刚好覆盖 OS + k3s server + Docker + 测试 Pod
  • worker 1.5 GB:OS + k3s agent + Docker + 测试 Pod

值得注意的是,如果想把 control-plane RAM 压到 1.5 GB,技术上可以启动 k3s server,但 docker pull nginx 时可能触发 OOM killer。2 GB 是最低稳定线。

磁盘

  • control-plane 20 GB:OS(~5GB)+ Docker 镜像缓存(~5GB)+ k3s 数据(~2GB)+ 日志预留(~3GB)+ 缓冲(~5GB)
  • worker 15 GB:OS(~5GB)+ Docker 镜像缓存(~3GB)+ 日志(~2GB)+ 缓冲(~5GB)
  • 扩容方向:control-plane 从 20 GB → 80 GB(跑 GZCTF),worker 从 15 GB → 80 GB(跑题目镜像)

2.3 内存细账

control-plane(k3s-cp)— 2 GB 分配

组件 稳态占用 峰值占用 说明
Ubuntu Server 24.04 LTS 256 MB 384 MB systemd + udev + sshd + 内核
k3s server(含 embedded etcd) 512 MB 768 MB apiserver + controller-manager + scheduler + etcd
k3s agent(control-plane 自带) 128 MB 256 MB kubelet + kube-proxy
Docker daemon 128 MB 192 MB dockerd 主进程
cri-dockerd 48 MB 64 MB CRI shim
systemd-journald + 日志缓存 64 MB 128 MB journal 缓冲区
基础固定小计 1,136 MB 1,792 MB —
测试 Pod 缓冲区 — ~256 MB nginx 镜像拉取 + 运行
结论 刚好 2,048 MB ✅ 2 GB 刚好覆盖

数据来源:Ubuntu Server 24.04 LTS 官方系统要求、k3s 官方资源指南、Docker 社区实测。

worker(k3s-w1/w2/w3)— 1.5 GB 分配

组件 稳态占用 峰值占用 说明
Ubuntu Server 24.04 LTS 256 MB 384 MB 同上
k3s agent 128 MB 256 MB kubelet + kube-proxy
Docker daemon 128 MB 192 MB —
cri-dockerd 48 MB 64 MB —
系统/日志缓存 64 MB 96 MB —
基础固定小计 624 MB 992 MB —
测试 Pod 缓冲区 — ~512 MB 拉取 + 运行 nginx
结论 ~1,504 MB ✅ 1.5 GB 刚好覆盖

一个有趣的发现是:1.5 GB 在 Ubuntu Server 上跑 k3s agent + Docker,如果把 swap 关掉,仅剩约 512 MB 给容器。如果拉取 100 MB+ 的镜像(如 nginx:latest 实际解压约 190 MB),峰值内存刚好够。但如果同时拉 2 个大镜像就会 OOM。这就是“最小可测试”的边界——你只能一次测试一个 Pod。

2.4 磁盘分配方案(340 GB 总账)

分配项 大小 说明
k3s-cp(control-plane) 20 GB 最小能跑 k3s server + Docker + 少量镜像
k3s-w1(worker) 15 GB 最小能跑 k3s agent + Docker
k3s-w2(worker) 15 GB 同上
k3s-w3(worker) 15 GB 同上
小计(4 VM) 65 GB 占用总量 19%
Proxmox 系统 + ISO 模板 30 GB 预留 Proxmox 自身和安装 ISO
即时剩余 245 GB 可用于扩容(见下表)

扩容规划——这 245 GB 怎么用

扩容阶段 操作 新增磁盘需求
第一阶段(测试验证后) control-plane 20 GB → 40 GB(拉 Docker 镜像缓存) +20 GB
第二阶段(部署 GZCTF) control-plane 40 GB → 80 GB(GZCTF 数据 + PostgreSQL) +40 GB
第三阶段(出题前) 3 × worker 15 GB → 50 GB(存题目镜像) +35 GB/worker
第四阶段(出题全量) 3 × worker 50 GB → 80 GB(跑题期间日志/overlay 增长) +30 GB/worker
极限消耗 全部扩到最大 80+80×3=320 GB(还余 20 GB)

根据来源(Proxmox VE 官方文档——Storage Model),磁盘扩容操作路径:

# 在 Proxmox 宿主机上执行
qm resize 202 virtio0 +20G        # 给 VM 202(k3s-w1)加 20 GB
# 然后在 VM 内执行
growpart /dev/vda 1                # 扩展分区
resize2fs /dev/vda1                # 扩展文件系统(ext4)
# 或 btrfs filesystem resize max /  # 如果用的是 btrfs

3. Proxmox 创建 VM 操作指南

3.1 前置准备:下载 Ubuntu Server 24.04 LTS 镜像

# 在 Proxmox 宿主机上下载 ISO
cd /var/lib/vz/template/iso
wget https://releases.ubuntu.com/24.04/ubuntu-24.04-live-server-amd64.iso

3.2 qm 命令创建 VM 脚本模板

根据来源(Proxmox VE qm 命令文档),以下脚本可以用一条命令创建一台最小 VM。但首次创建仍建议走 Web GUI 完成安装流程,然后基于模板克隆后续 3 个 worker。

以下是完整脚本,逐步骤创建 4 台 VM:

Step 1: 创建 control-plane 并安装系统(Web GUI 推荐首次)

直接走 Proxmox Web GUI(https://<pve-ip>:8006):

页面 设置值
VM ID 201
名称 k3s-cp
ISO ubuntu-24.04-live-server-amd64.iso
系统 BIOS: Default, SCSI Controller: VirtIO SCSI single
磁盘 Bus/Device: VirtIO Block, 磁盘大小: 20 GB, Cache: Write back
CPU 2 cores, Type: host
内存 2048 MB
网络 Bridge: vmbr0, Model: VirtIO (paravirtualized)
确认 勾选 “Start after created”

安装 Ubuntu 时选 Ubuntu Server minimal(最小安装),不装任何额外包。

Step 2: 将安装好的 VM 转为模板

# 关机
qm shutdown 201 && qm wait 201

# 转为模板
qm template 201

# 验证
qm list
# 输出中 VM 201 的 STATUS 应为 "template"

Step 3: 从模板克隆 3 个 worker

# 克隆 worker 1
qm clone 202 201 --name k3s-w1 --full 1
qm set 202 --memory 1536 --cores 1
qm resize 202 virtio0 15G

# 克隆 worker 2
qm clone 203 201 --name k3s-w2 --full 1
qm set 203 --memory 1536 --cores 1
qm resize 203 virtio0 15G

# 克隆 worker 3
qm clone 204 201 --name k3s-w3 --full 1
qm set 204 --memory 1536 --cores 1
qm resize 204 virtio0 15G

注意:从模板克隆后,每个 worker 的 /etc/hostname、/etc/hosts、/etc/netplan/*.yaml 需要修改为不同的节点名和静态 IP。对于最小测试,也可以直接用 DHCP 自动分配(Proxmox 默认 DHCP 通过 vmbr0 共享宿主机网络)。

Step 4: 一次性启动 4 台 VM

for vmid in 201 202 203 204; do
  qm start $vmid
done

# 查看启动状态
qm list

3.3 配置静态 IP(可选但推荐)

在 control-plane 上的 /etc/netplan/ 目录下找到配置文件(可能叫 50-cloud-init.yaml 或 00-installer-config.yaml),修改为:

# /etc/netplan/00-installer-config.yaml
network:
  ethernets:
    ens18:
      addresses:
        - 192.168.1.51/24
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses:
          - 8.8.8.8
          - 1.1.1.1
  version: 2

然后 netplan apply。3 个 worker 分别设为 .52/.53/.54。


4. 安装 k3s 集群——从零到就绪的最小步骤

4.1 在 control-plane 上安装 k3s server

# SSH 进入 control-plane(192.168.1.51)
ssh alice@192.168.1.51

# === Step 1: 安装 Docker ===
# 来源:Docker 官方安装指南
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# 重新登录以生效(或 newgrp docker)

# 验证 Docker
docker --version
docker run hello-world    # 验证拉取和运行

# === Step 2: 安装 k3s server(--docker 模式)===
# 来源:k3s 官方文档
curl -sfL https://get.k3s.io | \
  INSTALL_K3S_EXEC="--docker \
    --disable=traefik \
    --node-name=k3s-cp \
    --cluster-cidr=10.42.0.0/16 \
    --service-cidr=10.43.0.0/16 \
    --kube-apiserver-arg=service-node-port-range=20000-50000" \
  sh -

# 验证安装
sudo k3s kubectl get nodes
# 预期输出:NAME     STATUS   ROLES                  AGE   VERSION
#           k3s-cp   Ready    control-plane,master   30s   v1.xx.x+k3s1

sudo k3s kubectl get pods -A
# coredns 和 metrics-server 应处于 Running

# 获取 node-token(worker 加入用)
sudo cat /var/lib/rancher/k3s/server/node-token
# 输出类似:K1070c8a0b1f7c4a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4::server:node-token-xxxx

为什么不装 traefik? 最小测试阶段不需要 Ingress Controller。后续需要时再 kubectl apply 部署 traefik 或 nginx-ingress。

4.2 在 worker 节点上安装 k3s agent

对 3 个 worker 节点(192.168.1.52/.53/.54)重复以下:

# SSH 进入 worker
ssh alice@192.168.1.52

# === Step 1: 安装 Docker ===
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# === Step 2: 安装 k3s agent ===
# 替换 <SERVER_IP> 为 control-plane 的实际 IP(192.168.1.51)
# 替换 <NODE_TOKEN> 为上一步获取的 token
curl -sfL https://get.k3s.io | \
  K3S_URL=https://192.168.1.51:6443 \
  K3S_TOKEN=<NODE_TOKEN> \
  INSTALL_K3S_EXEC="--docker --node-name=k3s-w1" \
  sh -

# === 验证 ===
# 在 control-plane 上运行
sudo k3s kubectl get nodes
# 预期显示 4 个节点均 Ready

4.3 验证集群功能(5 步验证)

验证 1:4 节点 Ready

# 在 control-plane 上
sudo k3s kubectl get nodes -o wide

预期输出:

NAME     STATUS   ROLES                  AGE     VERSION        INTERNAL-IP     EXTERNAL-IP
k3s-cp   Ready    control-plane,master   2m      v1.31.x+k3s1   192.168.1.51   <none>
k3s-w1   Ready    <none>                 1m      v1.31.x+k3s1   192.168.1.52   <none>
k3s-w2   Ready    <none>                 1m      v1.31.x+k3s1   192.168.1.53   <none>
k3s-w3   Ready    <none>                 1m      v1.31.x+k3s1   192.168.1.54   <none>

验证 2:系统 Pod 正常运行

sudo k3s kubectl get pods -n kube-system
# 应看到 coredns 和 metrics-server 的 Running 状态

验证 3:调度测试 Pod(nginx)

# 创建 nginx deployment
sudo k3s kubectl create deployment nginx-test --image=nginx:alpine

# 等待拉取镜像并运行
sudo k3s kubectl get pods -w
# 观察 pod 从 Pending → ContainerCreating → Running

# 暴露 Service
sudo k3s kubectl expose deployment nginx-test --port=80 --type=NodePort

# 查看分配的 NodePort
sudo k3s kubectl get svc nginx-test
# 输出示例:nginx-test  NodePort  10.43.xx.xx  <none>  80:30234/TCP

# 测试访问(从任意节点、Proxmox 宿主机、或同一网络的其他机器)
curl http://192.168.1.51:30234
# 预期输出:nginx welcome page 的 HTML

验证 4:验证节点间的 Pod 调度

# 手动指定调度到特定 worker
sudo k3s kubectl run nginx-on-w1 --image=nginx:alpine \
  --overrides='{"spec":{"nodeName":"k3s-w1"}}'

sudo k3s kubectl run nginx-on-w2 --image=nginx:alpine \
  --overrides='{"spec":{"nodeName":"k3s-w2"}}'

# 验证 Pod 分布在正确节点
sudo k3s kubectl get pods -o wide

验证 5:正常删除集群(可选)

# 在 control-plane 和每个 worker 上
# control-plane: 卸载 k3s server
sudo /usr/local/bin/k3s-uninstall.sh

# worker: 卸载 k3s agent
sudo /usr/local/bin/k3s-agent-uninstall.sh

4.4 中国用户的加速安装版本

# control-plane — 使用国内镜像
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
  INSTALL_K3S_MIRROR=cn \
  INSTALL_K3S_EXEC="--docker --node-name=k3s-cp" \
  sh -

# worker — 使用国内镜像
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
  INSTALL_K3S_MIRROR=cn \
  K3S_URL=https://192.168.1.51:6443 \
  K3S_TOKEN=<NODE_TOKEN> \
  INSTALL_K3S_EXEC="--docker --node-name=k3s-w1" \
  sh -

来源:k3s 官方文档《中国用户安装加速》。


5. 扩容方案——从最小到生产

5.1 资源扩容对照表

扩容目标 控制面原值 → 目标值 3×Worker 原值 → 目标值 触发场景
✅ 测更多 Pod 2 GB → 4 GB 1.5 GB → 4 GB 要同时跑 5+ 测试 Pod
✅ 部署 GZCTF 2 GB → 4 GB, 20 GB → 50 GB 不动 要验证 GZCTF k8s 模式
✅ 部署 PostgreSQL 4 GB → 8 GB 不动 GZCTF + PostgreSQL 需要
✅ 比赛中型 8 GB → 16 GB 4 GB → 16 GB 100 队 × 30 题
✅ 比赛大型 16 GB → 24 GB 16 GB → 48 GB 200 队 × 30 题

5.2 扩容操作步骤(以 k3s-w1 从 1.5 GB → 4 GB RAM 为例)

# Step 1: 关机
qm shutdown 202 && qm wait 202

# Step 2: 改配置
qm set 202 --memory 4096

# Step 3: 开机
qm start 202

# Step 4: 验证
ssh alice@192.168.1.52
free -h
# 应显示 Mem: 3.8G 左右(4GB 减去系统占用)

5.3 磁盘扩容操作步骤(以 k3s-w1 从 15 GB → 50 GB 为例)

# Step 1(宿主机):Proxmox 在线扩展磁盘(VM 不需要关机)
qm resize 202 virtio0 +35G

# Step 2(VM 内):扩展分区和文件系统
ssh alice@192.168.1.52
sudo growpart /dev/vda 1         # 扩展分区
sudo resize2fs /dev/vda1         # 扩展 ext4
df -h /                          # 验证新容量

根据来源(Proxmox VE 文档——qm resize 命令),virtio0 是默认的 VirtIO 磁盘标识。如果创建 VM 时用了不同的总线,名称可能不同(如 sata0 或 scsi0)。用 qm config <vmid> 查看确切的磁盘标识。

5.4 vCPU 扩容(以 k3s-w1 从 1 核 → 4 核为例)

# 关机方式(推荐)
qm shutdown 202 && qm wait 202
qm set 202 --cores 4
qm start 202

# 或者热插拔(需要 VM 内支持)
qm set 202 --vcpus 4          # 设置最大 vCPU 数
# VM 内手动在线添加 CPU
echo 1 > /sys/devices/system/cpu/cpu1/online  # 按需

6. 常见踩坑记录

根据 k3s 社区 Issues 和 GZCTF 部署社群反馈,以下是最小测试阶段最可能踩的坑:

6.1 Docker 安装失败

症状:curl -fsSL https://get.docker.com | sh 报 GPG 或网络错误。 解决:

# 手动添加 Docker 官方源(Ubuntu 24.04)
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

6.2 k3s token 获取失败

症状:sudo cat /var/lib/rancher/k3s/server/node-token 文件不存在。 原因:k3s server 尚未完全启动。 解决:等待 30-60 秒再试,或检查 journalctl -u k3s 看错误日志。

6.3 worker 加入集群失败(TLS 握手错误)

症状:worker 的 journalctl -u k3s-agent 显示 TLS handshake error。 原因:control-plane 的 6443 端口不可达,或 token 错误。 排查:

# 从 worker 节点测试连接
curl -k https://192.168.1.51:6443
# 如果返回 unauthorized(401),说明网络通、token 可能错
# 如果连接超时,检查防火墙或 Proxmox 网络

6.4 Pod 一直 Pending

症状:kubectl get pods 显示 nginx-test 一直 Pending。 原因:最常见的是 Docker 镜像拉取超时(特别是在最小 1.5GB VM 上)。 解决:

sudo k3s kubectl describe pod nginx-test
# 看 Events 段,如果是 ImagePullBackOff,等 Docker 拉完
# 如果内存不够,调大 VM 内存

6.5 磁盘写满

症状:kubectl get nodes 显示 DiskPressure 为 True。 原因:15 GB 的 worker 磁盘,拉几个大镜像就满了。 解决:

# 清理 Docker 缓存
docker system prune -a --force

# 或者直接扩容(见第 5 章)
qm resize 202 virtio0 +10G

7. 资源速查汇总

7.1 最终配置快照

节点 vCPU RAM 磁盘 VM ID IP 安装命令
k3s-cp 2 2 GB 20 GB 201 .51 INSTALL_K3S_EXEC="--docker --node-name=k3s-cp"
k3s-w1 1 1.5 GB 15 GB 202 .52 K3S_URL=... K3S_TOKEN=... INSTALL_K3S_EXEC="--docker --node-name=k3s-w1"
k3s-w2 1 1.5 GB 15 GB 203 .53 同上(改 w1→w2)
k3s-w3 1 1.5 GB 15 GB 204 .54 同上(改 w1→w3)
总计 5 6.5 GB 65 GB

7.2 宿主机剩余资源

资源 总量 已分配 剩余 剩余比例
vCPU 32 线程 5 27 84%
RAM 192 GB 6.5 GB 185.5 GB 97%
磁盘 340 GB 65 GB 275 GB 81%

根据数据显示,这套最小配置仅消耗 19% 磁盘、3.4% 内存、16% CPU——宿主机资源极其宽裕。这意味着“最小可测试”方案可以无障碍地逐步扩容到正式比赛配置。


最后:根据来源(k3s 官方文档——k3s 常见问题),使用 --docker 运行时在生产环境不如 containerd 稳定,因为多了一层 cri-dockerd 转换。本最小方案仅用于基础功能验证——测试 4 节点能否正常加入、Pod 能否调度、NodePort 能否访问。验证通过后,建议在扩容到 GZCTF 生产部署时考虑切换为 containerd 运行时(去掉 --docker 参数),以避免 cri-dockerd 已知的兼容性问题。

原始笔记:cloud/gz-ctf/4节点k3s最小可测试方案.md