私有云环境综合案例¶
前面几章都在「已经有一个集群」的前提下工作。这一章从裸机开始:用 Heat 模板开出四台虚拟机,用 kubeadm 装出控制平面,再把网络、入口、存储、面板、中间件和业务一层层叠上去。走完一遍,你就拥有了一套能讲清楚每一层依赖的私有云。
你将学到¶
- MageEdu-Private-Cloud 的目录划分,以及每个子目录解决哪一层问题
- Heat 模板如何批量开出可部署 Kubernetes 的主机环境
- kubeadm 从
v1beta3迁移到v1beta4的具体改法 infra-services与-with-prometheus的差异与选择
为什么需要它¶
真实环境里没有「集群已经装好了」这种好事。你要先有网络、有主机、有镜像源,再解决 CNI、Ingress、存储,最后才轮到业务。每一层都依赖下一层,任何一层偷懒,故障都会在业务层以「莫名其妙」的形式出现。
| 目录 | 作用 | 依赖 |
|---|---|---|
openstack-heat-templates/ |
批量创建网络、安全组、主机 | OpenStack |
k8s-inst/ |
kubeadm 初始化、节点加入、重置 | 主机、镜像仓库 |
flannel/、ingress-nginx/ |
Pod 网络、七层入口 | 控制平面 |
metrics-server/、kuboard/ |
资源指标、集群面板 | CNI、Ingress |
csi-driver-nfs/ |
NFS 动态存储 | NFS Server |
infra-services/ |
中间件(无监控) | 存储、CNI |
infra-services-with-prometheus/ |
中间件 + Prometheus + exporter | 存储、CNI、Metrics |
mall-and-skywalking/ |
商城应用与 Agent | 全部基础服务 |
flowchart TB
H[OpenStack Heat 模板] --> V[4 台虚拟机]
V --> K[kubeadm 控制平面]
K --> C[Flannel CNI]
C --> N[ingress-nginx 与 metrics-server]
N --> S[存储 OpenEBS 或 NFS CSI]
S --> B[基础服务 Nacos ES Redis MQ Mongo MinIO]
B --> M[mall 商城与 SkyWalking]
M --> O[Kuboard 观察与验收]
图:私有云的分层交付顺序,下层未就绪时上层不要开始。
核心概念¶
k8s-inst:手工初始化与脚本自动化¶
手工路径是标准的 kubeadm init:准备主机名与 /etc/hosts、拉镜像、初始化、复制 admin.conf、装 CNI、再 kubeadm join。自动化路径依赖 Heat 模板 + cluster_init_script/cluster-init.sh,脚本里写死了 K8S_VERSION=1.27.3 和 registry.magedu.com 的 pause 镜像,还会额外把它打上 registry.k8s.io/pause:3.6 标签来绕过 kubeadm 的镜像检查——换环境就失效。
仓库的 kubeadm 配置已过时
kubeadm-init-config.tmpl.yaml 用的是 kubeadm.k8s.io/v1beta3。该版本在 v1.31 被正式废弃(至少 3 个小版本后移除),且 criSocket 指向 cri-dockerd,而 v1.31 默认运行时是 containerd。v1beta4 还改了两处:extraArgs 从 map 变成结构化列表,dns 变成带 disabled 的对象。迁移用 kubeadm config migrate 最稳。v1.31 的写法:
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.10.6
nodeRegistration:
criSocket: unix:///run/containerd/containerd.sock
name: k8s-master01.magedu.com
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: v1.31.4
controlPlaneEndpoint: "kubeapi.magedu.com:6443"
imageRepository: registry.aliyuncs.com/google_containers
apiServer:
certSANs: [kubeapi.magedu.com, 192.168.10.6]
networking:
podSubnet: 10.244.0.0/16
serviceSubnet: 10.96.0.0/12
dnsDomain: cluster.local
这份配置没有写 dns 与 extraArgs,避开了两处结构变更;其余字段在 v1beta4 下行为不变。
Heat:把主机也当成代码¶
cluster-base-env.tmpl 创建 192.168.10.0/24 网络、一个安全组和四台主机:k8s-master01(.6)、k8s-node01(.11)、k8s-node02(.12)、k8s-node03(.13)。cluster-base-env-floatingip.tmpl 在此之上分配浮动 IP,便于外网 SSH。
另外三个模板覆盖扩容:cluster_nodes_number_on_damands.tmpl(master 1–3、worker 1–6)、two_clusters.tmpl(两集群)、multi-clusters/(三套独立网络,共用一个路由器)。参数都是 key_name、image_id、flavor,改参数即可复用。
两套 infra-services 怎么选¶
| 维度 | infra-services/ |
infra-services-with-prometheus/ |
|---|---|---|
| 监控栈 | 无 | 00-Prometheus:Prometheus、Alertmanager、Grafana、kube-state-metrics、node-exporter、adapter |
| Nacos | 无 Ingress | 有 06-nacos-ingress.yaml |
| 中间件指标 | 未开启 | Redis/ES/Mongo/RabbitMQ/MinIO 均带 exporter 或 metrics 注解 |
| 适用场景 | 功能验证、资源紧张 | 需要观测、HPA 或容量分析 |
infra-services/01-Nacos/README.md 把访问地址写成 mysql-read.mall.svc.cluster.local,而命令用的 namespace 是 nacos,这是仓库笔误;该版本也没有 Nacos 的 Ingress。
存储与入口的两个缺口¶
目录自带 csi-driver-nfs,但大量清单的 storageClassName 写的是 openebs-hostpath。两条路:补装 OpenEBS,或把所有 PVC 的 SC 改成 nfs-csi(nfs-csi-storageclass.yaml 已把 nfs-server.nfs.svc.cluster.local 配好)。
入口同理:ingress-nginx/deploy.yml 的 Service 是 type: LoadBalancer,但项目没部署 MetalLB,裸机环境里它会一直 <pending>,需改 NodePort 或自建 VIP。
动手实验¶
实验:从 Heat 模板到 mall 上线
目标:四条验收线全部通过——节点 Ready、入口可访问、存储可绑定、应用可打开。
步骤 1 · 用 Heat 创建主机环境
openstack stack create -t openstack-heat-templates/cluster-base-env.tmpl \
--parameter key_name=mykey \
--parameter image_id=ubuntu-22.04-with-kubeadm-amd64 k8s-cluster
openstack stack resource list k8s-cluster -c physical_resource_id -c resource_name
openstack server list --name k8s-
四台机器都 ACTIVE 后再继续;创建失败先看 openstack stack event list。
步骤 2 · 初始化控制平面
按上一节写好配置,然后:
kubeadm config images pull --config kubeadm-init-config.yaml
kubeadm init --config kubeadm-init-config.yaml --upload-certs
mkdir -p $HOME/.kube && cp /etc/kubernetes/admin.conf $HOME/.kube/config
kubectl get nodes
NotReady 是正常的,此时还没有 CNI。
步骤 3 · 部署 Flannel 并加入工作节点
确认 Flannel 的 Network 与 kubeadm 的 podSubnet 都是 10.244.0.0/16,不一致会导致跨节点 Pod 不通。然后在各 worker 上执行 kubeadm join(containerd 不需要 --cri-socket)。
步骤 4 · 部署 ingress-nginx 并修正入口
kubectl apply -f ingress-nginx/deploy.yml
kubectl get svc -n ingress-nginx
kubectl patch svc ingress-nginx-controller -n ingress-nginx -p '{"spec":{"type":"NodePort"}}'
NP=$(kubectl get svc ingress-nginx-controller -n ingress-nginx -o jsonpath='{.spec.ports[0].nodePort}')
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.10.11:$NP
EXTERNAL-IP 为 <pending> 就对了——没有 LB 实现。patch 成 NodePort 后用节点 IP 访问,返回 404 说明 controller 已经在接管请求(此时还没有匹配的 Ingress)。
步骤 5 · metrics-server 与 Kuboard
kubectl apply -f metrics-server/components.yaml
kubectl top nodes
kubectl apply -f kuboard/deploy.yaml -f kuboard/ingress-kuboard.yaml
kubectl get pods -n kuboard
kubectl top 能出数据,说明 --kubelet-insecure-tls 生效(自签证书环境必须加)。Kuboard 用 admin/Kuboard123 登录,NodePort 30080;这些清单的镜像都指向 registry.magedu.com,换环境要统一替换。
步骤 6 · 按顺序部署基础服务
kubectl create namespace nacos
kubectl apply -f infra-services-with-prometheus/01-Nacos/01-secrets-mysql.yaml -f infra-services-with-prometheus/01-Nacos/02-mysql-persistent.yaml -n nacos
kubectl exec -n nacos mysql-0 -- mysql -uroot -e \
"GRANT ALL ON nacosdb.* TO 'nacos'@'%' IDENTIFIED BY 'magedu.com'; FLUSH PRIVILEGES;"
kubectl apply -f infra-services-with-prometheus/01-Nacos/ -n nacos
再依次部署 02-ElasticStack、03-Redis、04-RabbitMQ、05-MongoDB、06-MinIO、07-Skywalking,每个都好再下一个。动手前先确认存储类可用:
第二条没有输出才继续——任何 Pending 都会让后面的服务卡在 ContainerCreating。
步骤 7 · 部署 mall 并修正 Ingress Class
kubectl create namespace mall
kubectl apply -f mall-and-skywalking/ -n mall
kubectl get pods -n mall
这批清单的 Ingress 写的是 ingressClassName: cilium,而本环境只有 nginx。批量改掉:
kubectl get ingress -n mall -o name | xargs -I{} kubectl patch {} -n mall \
--type merge -p '{"spec":{"ingressClassName":"nginx"}}'
步骤 8 · 用 Kuboard 观察
把 nacos、elastic、mall 加进常用视图,重点看三处:Pod 的 RESTARTS 是否增长、PVC 是否 Bound、Ingress 后端 Endpoints 是否为空。图形界面替代不了 kubectl describe,但能在几十个对象里快速定位异常。
步骤 9 · 跑一遍验收命令
kubectl get nodes -o wide
kubectl get pods -A --field-selector=status.phase!=Running
kubectl get pvc -A | grep -v Bound
kubectl get endpoints -A | awk '$3=="<none>"{print}'
kubectl top nodes
curl -I -H "Host: mall-admin-web.magedu.com" http://192.168.10.11:$(kubectl get svc ingress-nginx-controller -n ingress-nginx -o jsonpath='{.spec.ports[0].nodePort}')
期望:节点全 Ready、无非 Running Pod、无 Pending PVC、无空 Endpoints。四条同时成立才算交付完成。
验证清单¶
-
kubectl get nodes全部Ready,版本一致 -
kubectl get pvc -A无Pending -
kubectl get endpoints -A无<none> -
curl -I http://mall-admin-web.magedu.com返回 200
常见问题与排错¶
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
kubeadm init 报 v1beta3 已废弃 |
用了仓库旧模板 | 换 v1beta4,或先跑 kubeadm config migrate |
节点 NotReady |
Flannel 未装或 Pod CIDR 不一致 | 核对 podSubnet 与 Flannel Network |
PVC Pending |
清单写 openebs-hostpath,集群只有 nfs-csi |
统一 SC 名称,或补装 OpenEBS |
| mall 域名 404 | Ingress 的 class 仍是 cilium |
按步骤 7 批量 patch 成 nginx |
进阶挑战¶
- 把
two_clusters.tmpl跑起来,用 Karmada 把两个集群连起来,验证跨集群 Service 发现。 - 扩到 3 master,在
controlPlaneEndpoint前置 VIP,做高可用控制平面。 - 把两套 infra 各部署一次,对比 Prometheus 抓到的 target 数量与 Grafana 面板差异。
小结¶
- 私有云交付是分层工程:主机 → 控制平面 → CNI → 入口/指标 → 存储 → 中间件 → 应用,跳层就会在业务层还债。
- Heat 把主机变成代码,kubeadm 把集群变成代码,但仓库脚本绑定特定镜像仓库与版本,迁移时要逐项替换。