边缘集群部署 - 云端部署 CubeStudio 与 CloudCore
本篇对应云-边主线的 Phase C(云端部署 CubeStudio) 与 Phase D(云端 CloudCore / KubeEdge)。承接 03-云端环境准备-K8s与NFS。
来源:
docs/部署/kubeedge部署边缘集群cube-studio.md第 4~5 节。
四、Phase C:云端部署 cube-studio
C.1 代码上传
# 本地(Mac)打包 install/kubernetes(含 kustomize/cube 等)
tar -czf /tmp/cube-k8s.tar.gz -C install kubernetes
scp /tmp/cube-k8s.tar.gz root@47.114.127.214:/root/
# 云端解压
mkdir -p /root/cube-studio && cd /root/cube-studio
tar -xzf /root/cube-k8s.tar.gz
C.2 改 config.py & start.sh
cd /root/cube-studio/kubernetes
# config.py:CubeStudio 调用容器 CLI,统一成 nerdctl
sed -i 's|^CONTAINER_CLI=.docker.|CONTAINER_CLI="nerdctl"|' cube/overlays/config/config.py
# kubeconfig 给 CubeStudio 用
cp /etc/kubernetes/admin.conf config
mkdir -p kubeconfig && cp /etc/kubernetes/admin.conf kubeconfig/dev-kubeconfig
CONTAINER_CLI默认值是docker(见myapp/config.py:1110,注释写明可选docker/nerdctl)。本场景没装 docker、用 containerd+nerdctl,所以改成nerdctl。
三处 patch(否则 start.sh 会卡住或报语法错):
- 跳过 kubectl 重下载(已装 v1.28):把
start.sh第 15/17 行的wget -O kubectl ...注释掉。必须同时在 if/fi 里加一行true,否则空 if 体会报syntax error near unexpected token 'fi':if [ "$ARCH" = "x86_64" ]; then # # wget -O kubectl ... # # elif [ "$ARCH" = "aarch64" ]; then # # wget -O kubectl ... true # ← 必须加,否则 bash 语法错 fi已核实:
install/kubernetes/start.sh:15,17确为wget -O kubectl https://cube-studio.oss-cn-hangzhou.aliyuncs.com/install/kubectl-amd64-1.28(与 arm64 版)。 - node 标签查找改 fallback:原脚本
node=$(kubectl get node -o wide | grep $1 ...),传入的是公网 IP,grep 匹配不到内网 IP 的节点。改为:node=$(kubectl get node -o name | head -n 1 | sed 's|node/||') - 跳过 docker 镜像预拉:
init_node.sh里sh pull_images_mini.sh用docker pull(没装 docker),改成:# sh pull_images_mini.sh echo SKIP pull_images_mini # 用 containerd 懒拉
C.3 跑 start.sh
cd /root/cube-studio/kubernetes
nohup bash start.sh 47.114.127.214 > /root/start.log 2>&1 &
tail -f /root/start.log
C.4 修 ccr 私有镜像鉴权
create_ns_secret.sh 用 xxx/xxx 占位凭证创建 hubsecret,ccr.ccs.tencentyun.com 私有镜像会 401 拉失败。
已核实:
install/kubernetes/create_ns_secret.sh:7确为kubectl create secret docker-registry hubsecret --docker-server=ccr.ccs.tencentyun.com --docker-username=xxx --docker-password=xxx,需替换成真实凭证。
获取正确凭证后(若旧环境上已有 /root/.docker/config.json,可解 base64 取 username:password),批量重建各命名空间的 hubsecret:
for ns in infra kubeflow istio-system pipeline automl jupyter service monitoring logging kube-system aihub default; do
kubectl -n $ns delete secret hubsecret 2>/dev/null
kubectl -n $ns create secret docker-registry hubsecret \
--docker-server=ccr.ccs.tencentyun.com \
--docker-username=<你的账号> \
--docker-password=<你的密码>
done
# 触发失败 pod 重拉
kubectl get pods -A --no-headers | awk '$4 ~ /ImagePullBackOff|ErrImagePull/ {print $1, $2}' | \
while read ns pod; do kubectl delete pod -n $ns $pod --grace-period=0 --force; done
C.5 修 istio-ingressgateway Service
CubeStudio 的 install YAML(install/kubernetes/istio/install-1.15.0.yaml)里只有 istio-ingressgateway 的 Deployment 没有 Service,需手动建 ClusterIP + externalIPs:
cat > /tmp/ingress-svc.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: istio-ingressgateway
namespace: istio-system
labels:
app: istio-ingressgateway
istio: ingressgateway
spec:
type: ClusterIP
externalIPs:
- 172.30.159.159
- 47.114.127.214
selector:
app: istio-ingressgateway
istio: ingressgateway
ports:
- {name: http, port: 80, targetPort: 8080}
- {name: http2, port: 8080, targetPort: 8080}
- {name: https, port: 443, targetPort: 8443}
EOF
kubectl apply -f /tmp/ingress-svc.yaml
# 首次 istiod 没 CA 前 ingressgateway 挂着,重启一次让 envoy 拉全配置
kubectl rollout restart deployment -n istio-system istiod istio-ingressgateway
C.6 清理无 GPU 场景下会卡住的资源
kubectl delete deployment vgpu-hami-scheduler -n kube-system
kubectl delete daemonset vgpu-hami-device-plugin -n kube-system
kubectl delete job vgpu-hami-admission-create vgpu-hami-admission-patch -n kube-system
C.7 验收
curl -sI http://47.114.127.214/ # 301 → /frontend/
curl -sI http://47.114.127.214/frontend/ # 200
默认账号 admin / admin,首次登录后立即改密码。
五、Phase D:云端 CloudCore(KubeEdge v1.23.0)
D.1 装 keadm
KEADM_VER=1.23.0
wget -q https://gh-proxy.com/https://github.com/kubeedge/kubeedge/releases/download/v${KEADM_VER}/keadm-v${KEADM_VER}-linux-amd64.tar.gz -O /tmp/keadm.tar.gz
mkdir -p /tmp/keadm-extract
tar -xzf /tmp/keadm.tar.gz -C /tmp/keadm-extract
cp $(find /tmp/keadm-extract -name keadm -type f | head -1) /usr/local/bin/keadm
keadm version
注意:v1.23.0 的 release tarball 解压后内层目录名其实是
keadm-v1.20.0-linux-amd64/(KubeEdge 上游打包失误),keadm version也会显示 v1.20.0。这不影响实际部署的 cloudcore 镜像版本——keadm init --kubeedge-version=v1.23.0才决定用哪个镜像。
D.2 init
keadm init \
--advertise-address=47.114.127.214 \
--set iptablesManager.mode="external" \
--kubeedge-version=v1.23.0
D.3 打开 dynamicController
cloudcore configmap 的 data 是一个 yaml 字符串(不是顶层结构),不能直接 sed 整个 cm yaml。要先把字符串导出、改完再 apply 回去:
# 导出 cloudcore.yaml 字段到临时文件
kubectl get cm -n kubeedge cloudcore -o jsonpath='{.data.cloudcore\.yaml}' > /tmp/cc.yaml
# 改 dynamicController.enable: false → true
sed -i '/dynamicController:/{n;s|enable: false|enable: true|;}' /tmp/cc.yaml
# 写回 configmap
kubectl create cm cloudcore -n kubeedge --from-file=cloudcore.yaml=/tmp/cc.yaml \
--dry-run=client -o yaml | kubectl apply -f -
# Deployment 默认 RollingUpdate,但 cloudcore 用 hostNetwork 需端口独占,改 Recreate
kubectl patch deployment cloudcore -n kubeedge -p '{"spec":{"strategy":{"type":"Recreate","rollingUpdate":null}}}'
kubectl rollout restart deployment cloudcore -n kubeedge
注意:
featureGates.requireAuthorization默认false。启用会引入大量 RBAC /serviceaccountaccessesreconcile 逻辑,调试复杂,建议保持 false。
D.4 RBAC(仅启用 requireAuthorization 时需要)
启用 requireAuthorization 才需要补 ClusterRole。仓库提供了现成的 install/kubernetes/kubeedge/20-cloud-rbac.yaml(ClusterRole 名 cloudcore-extra,带 rbac/aggregate-to-cloudcore: "true" 标签,会自动聚合到默认 cloudcore 角色,覆盖 devices.kubeedge.io / apps.kubeedge.io / policy.kubeedge.io 等 CRD 权限):
kubectl apply -f install/kubernetes/kubeedge/20-cloud-rbac.yaml
kubectl rollout restart deployment cloudcore -n kubeedge
老环境若报
failed to list *v1beta1.DeviceStatus forbidden(缺devices.kubeedge.io权限),也应用这个文件补齐,见 07-踩坑速查。
D.5 拿 token
keadm gettoken
# 输出形如:xxxxx.eyJhbGciOi... (边缘 keadm join 时用)
下一步:边缘节点接入与 CubeStudio 项目组配置,见 05-边缘节点接入与项目组配置。