华为云 CCE 实战:从镜像构建到 HPA 弹性伸缩的完整落地路径
本文基于 Kubernetes 1.27 与华为云 CCE v1.27 集群验证,示例代码可直接复用。
一、为什么要把"弹性"做成一条闭环
很多团队在 CCE 上跑得好好的,直到大促或突发流量来袭,才发现弹性能力是"半成品":镜像推送靠人手,Deployment 靠复制粘贴,HPA 配了但从不生效,压测报告和扩容曲线对不上。
问题往往不在 Kubernetes 本身,而在于从镜像到副本数的链路没有被串起来。本文按一条完整链路走一遍:
- 本地构建镜像并推送到 SWR(容器镜像服务)
- 在 CCE 创建集群与节点池
- 用 Deployment + Service 跑通最小可用服务
- 配置基于 CPU 利用率的 HPA
- 用压测验证扩缩容是否真的发生
二、前置准备
| 资源 | 说明 |
|---|---|
| CCE 集群 | v1.27 及以上,开启 metrics-server 插件 |
| SWR 组织 | 用于存放私有镜像 |
| 节点池 | 建议至少 2 个节点,避免单点 |
| kubectl | 与集群版本偏差不超过一个小版本 |
在 CCE 控制台"集群信息 → 连接信息"下载 kubeconfig,替换本地 ~/.kube/config 后验证:
kubectl cluster-info
kubectl get nodes -o wide
节点状态为 Ready 即可继续。
三、第一步:构建镜像并推送到 SWR
先登录 SWR。华为云各区域的镜像仓库地址格式固定:
docker login -u cn-north-4@AK -p <登录密钥> swr.cn-north-4.myhuaweicloud.com
登录密钥在 SWR 控制台"我的镜像 → 登录指令"中获取,有效期通常为 24 小时,不要写进脚本提交到仓库。
准备一个最小服务。这里用 Go 写一个带 /healthz 的 HTTP 服务,便于后续压测:
package main
import (
"net/http"
"os"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello from cce"))
})
http.ListenAndServe(":"+port, nil)
}
Dockerfile 使用多阶段构建,把镜像压到最小:
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app .
FROM alpine:3.19
RUN adduser -D -u 10001 appuser
COPY --from=builder /out/app /app
USER appuser
EXPOSE 8080
ENTRYPOINT ["/app"]
构建并推送:
export REGISTRY=swr.cn-north-4.myhuaweicloud.com
export ORG=my-demo-org
docker build -t $REGISTRY/$ORG/demo-api:v1 .
docker push $REGISTRY/$ORG/demo-api:v1
推送完成后在 SWR 控制台确认镜像已出现,记录完整地址,下一步要用。
四、第二步:创建集群与节点池
控制台创建集群时,有三个参数值得特别注意:
- 容器网络模型:新建集群建议选云原生网络 2.0,Pod 密度和性能更好。
- 节点池规格:HPA 扩容的实际落点是节点池,节点池容量不足时 Pod 会一直
Pending。 - 节点池弹性:开启后节点数量可随 Pod 需求自动增减,避免"Pod 扩容成功但调度不上"。
节点池数量建议为 HPA 最大副本数预留余量。若单节点可调度 30 个 Pod,HPA 最大 10 副本,2 个节点即可留出滚动升级空间。
五、第三步:跑通最小可用服务
创建 demo-api.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-api
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: demo-api
template:
metadata:
labels:
app: demo-api
spec:
containers:
- name: demo-api
image: swr.cn-north-4.myhuaweicloud.com/my-demo-org/demo-api:v1
ports:
- containerPort: 8080
resources:
requests:
cpu: 200m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: demo-api
namespace: default
spec:
selector:
app: demo-api
ports:
- port: 80
targetPort: 8080
type: ClusterIP
kubectl apply -f demo-api.yaml
kubectl get pods -l app=demo-api -w
两个副本都 Running 且 READY 1/1 后,再验证服务连通性:
kubectl run curl-test --rm -it --image=curlimages/curl -- \
curl -s http://demo-api.default.svc.cluster.local/healthz
六、第四步:配置 HPA
6.1 确认指标管道可用
HPA 读不到指标是最常见的失败原因。先确认 metrics-server 正常:
kubectl top pods
如果报 error: Metrics API not available,到 CCE 插件中心安装或升级 metrics-server,等待约 1 分钟后再试。
注意:kubectl top 返回空值说明 Pod 刚启动、指标尚未采样,不代表插件故障,稍等再看。
6.2 编写 HPA
使用 autoscaling/v2,可以同时约束行为策略:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: demo-api-hpa
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: demo-api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 2
periodSeconds: 60
几个关键点:
- 目标利用率是相对 requests 计算的。request 200m、目标 60%,意味着单 Pod 平均 CPU 超过 120m 就触发扩容。request 设得过大,HPA 永远不会动。
- 扩容快、缩容慢。
scaleUp窗口设为 0 让流量突发时快速响应,scaleDown保留 300 秒稳定窗口,避免抖动。 - 不要同时用 HPA 和固定 replicas 管理同一个 Deployment,会出现反复改写副本数的情况。
kubectl apply -f demo-api-hpa.yaml
kubectl get hpa demo-api-hpa -w
七、第五步:压测验证弹性
用一个临时压测 Pod 打流量:
kubectl run loadgen --rm -it --image=busybox -- \
sh -c "while true; do wget -q -O- http://demo-api.default.svc.cluster.local/ > /dev/null; done"
如果压测 Pod 被 HPA 统计进指标(默认不会,因为它不属于目标 Deployment),观察 TARGETS 一列即可:
kubectl get hpa demo-api-hpa
kubectl describe hpa demo-api-hpa | tail -20
预期行为:
TARGETS从0%/60%逐步升高并越过 60%- Deployment 副本数从 2 增至更多,受
maxReplicas: 10约束 - 压测停止后,约 5 分钟内副本数回落到 2
如果副本数一直不动,按这个顺序排查:
kubectl top pods -l app=demo-api # 指标是否存在
kubectl describe hpa demo-api-hpa # Events 里会写明具体原因
kubectl get pods -o wide # 是否因节点资源不足 Pending
kubectl get nodes # 节点是否已满
describe hpa 的 Events 是最有价值的信息来源,常见的 failed to get cpu utilization、missing request for cpu 都能直接指向根因。
八、容易踩的坑
1. 没有设置 resources.requests。 HPA 按 request 计算利用率,缺省时无法计算,事件里会提示 missing request for cpu。requests 必须写。
2. 只扩 Pod 不扩节点。 HPA 最大 10 副本、单节点只能放 4 个 Pod 时,多出来的 Pod 会 Pending。要么开启节点池弹性,要么按峰值预留节点容量。
3. readinessProbe 配得太激进。 扩容出来的新 Pod 还没就绪就被打流量,会放大超时。initialDelaySeconds 与 periodSeconds 要按实际启动时间给足。
4. 镜像地址带 tag 但用了 latest。 滚动升级时 latest 不会触发重新拉取(取决于 imagePullPolicy),线上很容易"改了代码没生效"。生产环境坚持用不可变 tag 或 digest。
5. 缩容把长连接打断。 缩容前优雅退出需要应用处理 SIGTERM 并在 terminationGracePeriodSeconds 内完成。HTTP 服务至少要停止接收新请求、等待存量请求结束。
九、小结
把上面这条链路拆开看,每一环都不复杂,真正的难点是让它们互相成立:requests 设置决定 HPA 是否有效,节点池容量决定扩容是否落地,探针配置决定扩容后是否真能承接流量。
建议按这个顺序固化下来:
- 镜像用不可变 tag 推送 SWR
- Deployment 必须带 requests/limits 与两类探针
- HPA 用
autoscaling/v2,显式声明 behavior - 每次变更后用一次压测确认弹性曲线
这样"弹性伸缩"才不是一个配置项,而是一条可以被验证的能力。
以上示例集群与镜像均为演示用途,实际部署请结合业务压测数据调整 requests 与 HPA 阈值。
- 点赞
- 收藏
- 关注作者
评论(0)