边缘集群部署 - 云端环境准备

从零在公网云主机上用 kubeadm+containerd 装单节点 K8s、并搭云端 NFS 服务端作为跨端共享存储时读这篇(云-边主线 Phase A/B)

02-部署安装 / 部署方式 / 边缘集群
kubeadmcontainerdcontainerd2.xflannelregistry mirrorcerts.dnerdctlcrictlNFSnfs-kernel-serverexports安全组sandboxSystemdCgroupapiserver-cert-extra-sanstransfer service

边缘集群部署 - 云端环境准备(K8s + NFS)

本篇对应一次从零到可用的跨公网"云-边"部署的前两个阶段:Phase A 云端 K8s 集群 + Phase B 云端 NFS 服务端。公网云主机作为云端(K8s 控制面 + cube-studio + CloudCore + NFS 服务端)。

来源:docs/部署/kubeedge部署边缘集群cube-studio.md 第 0~3 节。覆盖云厂商安全组、Ubuntu 24.04 适配、containerd 2.x 镜像 mirror、NFS 跨公网挂载。


零、交付前必读:要替换的变量

文档里所有命令用的都是作者当时环境的示例值。正式部署前请把下列占位符换成你自己的:

占位符 示例(作者的) 说明 怎么查
<云端公网IP> 47.114.127.214 云端主机公网 IP,边缘节点必须能访问 云厂商控制台 / curl ifconfig.me
<云端内网IP> 172.30.159.159 云端 eth0 的 IP,可能 == 公网 IP(无 NAT 的云)或私网 IP(阿里云 ECS) ip -4 addr show eth0
<云端内网CIDR> 172.30.0.0/16 云端 VPC 内网 CIDR,用于 NFS exports 云厂商 VPC 配置
<边缘节点名> edge-pc kubectl 里的 node 名称 自己取,建议只有小写字母/数字/-
<边缘内网IP> 192.168.3.22 边缘节点在局域网的 IP 边缘机 ip -4 addr show
<边缘内网CIDR> 192.168.3.0/24 边缘所在局域网 CIDR 边缘路由器配置
<ccr账号> / <ccr密码> 825485697 / <你的密码> ccr.ccs.tencentyun.com 私有仓库凭证(CubeStudio 商业版镜像在那里) 供应商提供

关于边缘出口公网 IP:本文不需要这个值。早期版本曾用边缘出口 IP 来做 NFS export 白名单和安全组放行,但家宽/4G 出口 IP 会变,每次变都要改云端 export + 安全组并强制重挂,经常凌晨断服。现在统一改成 * / 0.0.0.0/0(详见 Phase B 末尾"安全权衡")。

其它要自己确定的

  • 云厂商:示例是阿里云。任意厂商都行,唯一要求:云主机有固定公网 IP,能开放 22/80/443/6443/10000-10004/2049 端口。
  • K8s 版本:本文用 v1.28.15。K8s 1.25–1.29 都 OK,KubeEdge 必须对应升到 ≥ v1.22(K8s 1.28+ 上 v1.20 有 bug)。
  • CubeStudio 商业版镜像凭证:只有社区版镜像的话,04 里的 hubsecret 部分可跳过。

一、环境(示例)

角色

角色 位置 规格 OS
云端 cube 公网 47.114.127.214 / 内网 172.30.159.159 ≥ 8 vCPU / 16 GiB Ubuntu 22.04+
边缘 edge-pc NAT 内网 192.168.3.22 / 出口公网 IP 不固定 ≥ 4 C / 8 GiB Ubuntu 20.04+

版本栈

  • K8s:v1.28.15(kubeadm 自装,registry.aliyuncs.com 镜像源)
  • 容器运行时:containerd 2.2.1(云)/ 1.7.3(边)
  • CNI:flannel v0.25.6
  • KubeEdge:最终用 v1.23.0(也试过 v1.20.0 / v1.22.1)

云厂商安全组 / 云主机防火墙(入方向,提前在控制台配置)

端口 协议 用途
22 TCP 管理员 IP SSH
80、443 TCP 0.0.0.0/0 cube-studio Web
6443 TCP 0.0.0.0/0 kube-apiserver(边缘 kube-proxy 要走公网连云端 apiserver)
10000-10004 TCP 0.0.0.0/0 CloudCore(websocket / https / stream / tunnel)
2049、111、20048 TCP/UDP 0.0.0.0/0 NFS(详见下方"安全权衡")
30000-32767 TCP 按需 K8s NodePort(可选)

NFS 公网放行的安全权衡:NFSv4 + sec=sys 没有真正的客户端鉴权,源 IP 是唯一防线。把 2049 对 0.0.0.0/0 开等于让全网都能尝试挂载 /data/nfs。本场景边缘出口 IP 经常变、/data/nfs 里只有 CubeStudio 的 pipeline workspace(无核心数据资产),团队接受这个风险。如果 NFS 上有敏感数据,请改用 WireGuard:给边缘分一个固定虚拟 IP,NFS export 写虚拟 IP,安全组只对 51820/udp 开放。


二、Phase A:云端 K8s 集群(containerd + kubeadm)

A.1 系统基线

apt-get update -qq
swapoff -a
sed -i.bak '/ swap / s/^/#/' /etc/fstab

cat > /etc/modules-load.d/k8s.conf <<EOF
overlay
br_netfilter
EOF
modprobe overlay && modprobe br_netfilter

cat > /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
sysctl --system

timedatectl set-timezone Asia/Shanghai
mkdir -p /data/nfs /data/k8s   # /data/k8s 后续会被替换为软链

A.2 安装 containerd 2.x

Ubuntu 24.04 apt 源即为 containerd 2.x:

DEBIAN_FRONTEND=noninteractive apt-get install -y -qq containerd
mkdir -p /etc/containerd                                   # apt 不自动创建
containerd config default > /etc/containerd/config.toml

关键配置(containerd 2.x 与 1.x 语法不同):

# 1. SystemdCgroup 改 true
sed -i 's|SystemdCgroup = false|SystemdCgroup = true|' /etc/containerd/config.toml

# 2. sandbox 镜像换阿里云(2.x 字段名是 sandbox,不是 sandbox_image)
sed -i "s|sandbox = 'registry.k8s.io/pause:3.10.1'|sandbox = 'registry.aliyuncs.com/google_containers/pause:3.9'|" /etc/containerd/config.toml

# 3. 开启 config_path(用于 certs.d 镜像 mirror)
#    位置: [plugins.'io.containerd.cri.v1.images'.registry]  config_path = '/etc/containerd/certs.d'
#    kubeadm generate 默认已有,确认即可

关键点:containerd 2.x 的 Transfer Service 需要单独配 config_path(与 CRI registry 的 config_path 并列),否则 kubelet/crictl pull 仍会绕过 mirror 直连原始 registry:

[plugins.'io.containerd.transfer.v1.local']
  config_path = '/etc/containerd/certs.d'

同时设置(强制走 CRI plugin 拉镜像):

use_local_image_pull = true

A.3 registry mirror

mkdir -p /etc/containerd/certs.d/docker.io
cat > /etc/containerd/certs.d/docker.io/hosts.toml <<EOF
server = "https://registry-1.docker.io"
[host."https://docker.m.daocloud.io"]
  capabilities = ["pull", "resolve"]
EOF

# registry.k8s.io → 阿里云
mkdir -p /etc/containerd/certs.d/registry.k8s.io
cat > /etc/containerd/certs.d/registry.k8s.io/hosts.toml <<EOF
server = "https://registry.k8s.io"
[host."https://registry.aliyuncs.com/google_containers"]
  capabilities = ["pull", "resolve"]
  override_path = true
EOF

systemctl restart containerd

A.4 nerdctl + crictl

GitHub 直连在国内会超时,用 gh-proxy.com两个都要装:CubeStudio 用 nerdctl 拉镜像,kubeadm preflight 校验 crictl 必须存在。

NERDCTL_VER=1.7.6
wget -q https://gh-proxy.com/https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VER}/nerdctl-${NERDCTL_VER}-linux-amd64.tar.gz -O /tmp/nerdctl.tar.gz
tar -xzf /tmp/nerdctl.tar.gz -C /usr/local/bin nerdctl

CRICTL_VER=v1.28.0
wget -q https://gh-proxy.com/https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VER}/crictl-${CRICTL_VER}-linux-amd64.tar.gz -O /tmp/crictl.tar.gz
tar -xzf /tmp/crictl.tar.gz -C /usr/local/bin
cat > /etc/crictl.yaml <<EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
EOF

A.5 kubeadm / kubelet / kubectl v1.28

apt-get install -y -qq apt-transport-https ca-certificates curl gpg conntrack socat
mkdir -p /etc/apt/keyrings
curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/deb/Release.key | \
  gpg --dearmor --yes -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/deb/ /" > /etc/apt/sources.list.d/kubernetes.list

apt-get update -qq
apt-get install -y -qq kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

A.6 kubeadm init

四大坑之 4--apiserver-cert-extra-sans 必须含公网 IP,否则后面改 kube-proxy server 为公网 IP 时证书不匹配照样握手失败。

kubeadm config images pull \
  --image-repository=registry.aliyuncs.com/google_containers \
  --kubernetes-version=v1.28.15

kubeadm init \
  --apiserver-advertise-address=172.30.159.159 \
  --apiserver-cert-extra-sans=47.114.127.214,172.30.159.159 \
  --pod-network-cidr=10.244.0.0/16 \
  --service-cidr=10.96.0.0/12 \
  --kubernetes-version=v1.28.15 \
  --image-repository=registry.aliyuncs.com/google_containers \
  --cri-socket=unix:///run/containerd/containerd.sock

mkdir -p /root/.kube
cp /etc/kubernetes/admin.conf /root/.kube/config
export KUBECONFIG=/etc/kubernetes/admin.conf

A.7 CNI plugins + flannel + 单节点去污 + 节点标签

先装 CNI plugins(Ubuntu 24.04 apt 的 containerd 不带 loopback/bridge 等基础插件;不装会一直 failed to find plugin "loopback"):

mkdir -p /opt/cni/bin
wget -q https://gh-proxy.com/https://github.com/containernetworking/plugins/releases/download/v1.4.0/cni-plugins-linux-amd64-v1.4.0.tgz -O /tmp/cni.tgz
tar -xzf /tmp/cni.tgz -C /opt/cni/bin
# flannel manifest(github 直连会 timeout,走代理)
wget -q https://gh-proxy.com/https://raw.githubusercontent.com/flannel-io/flannel/v0.25.6/Documentation/kube-flannel.yml -O /tmp/kube-flannel.yml
sed -i 's|docker.io/flannel/|docker.m.daocloud.io/flannel/|g' /tmp/kube-flannel.yml
kubectl apply -f /tmp/kube-flannel.yml

# 单节点集群移除 control-plane taint
kubectl taint nodes --all node-role.kubernetes.io/control-plane-

# 云端训练节点标签(CubeStudio 按 label 调度 pipeline)
kubectl label node cube \
  train=true cpu=true org=public notebook=true service=true \
  node-role.kubernetes.io/master=true gpu=false --overwrite

验收kubectl get nodes → Ready;kubectl get pods -A 全 Running。


三、Phase B:云端 NFS 服务端

apt-get install -y -qq nfs-kernel-server

mkdir -p /data/nfs/k8s
chmod -R 777 /data/nfs

# 让 CubeStudio 默认的 hostPath /data/k8s/... 自动落到 NFS 导出区
rm -rf /data/k8s
ln -sf /data/nfs/k8s /data/k8s

/etc/exports对所有 IP 放行,原因见上方"安全权衡"——边缘出口 IP 不固定,按 IP 白名单运维成本太高):

注意:必须用 * 而不是 0.0.0.0/0。Ubuntu 24.04 NFS server 解析 0.0.0.0/0 后所有非本地 IP 一律 mount.nfs: access denied by server(即使 exportfs -v 显示规则已加载)。换成 * 立即可挂。这是 nfs-utils 的 CIDR 匹配 bug,不是配置写错。

cat > /etc/exports <<EOF
/data/nfs *(rw,no_root_squash,async,no_subtree_check,insecure)
/data/nfs 172.30.0.0/16(rw,no_root_squash,async,no_subtree_check,insecure)
/data/nfs 10.244.0.0/16(rw,no_root_squash,async,no_subtree_check,insecure)
/data/nfs 127.0.0.1(rw,no_root_squash,async,no_subtree_check,insecure)
EOF

exportfs -ar
systemctl enable --now nfs-kernel-server
showmount -e 127.0.0.1   # 应显示上述 4 行

配合云厂商安全组也要对 0.0.0.0/0 放行 2049/tcp111/tcp+udp20048/tcp(见 Phase A 安全组表格)。两边都改完才算生效。边缘侧的挂载方式见 05-边缘节点接入与项目组配置


下一步:在云端部署 CubeStudio 与 CloudCore,见 04-云端CubeStudio与CloudCore

最后更新 2026-07-09完整文档以官方仓库为准:GitHub Wiki