用华为云 CCE 部署一个生产可用的 K8s 应用

举报
yd_278382871 发表于 2026/10/31 00:55:06 2026/10/31
【摘要】 一个应用的容器化上线,真正费时间的不是 kubectl apply,而是它之前的镜像分层、以及它之后的探针、配额和弹性。下面这条流程在 CCE 上跑通一遍,把能少踩的坑一次说清。一、先定结构层组件作用镜像SWR 组织版本化交付物编排CCE 集群 + Deployment副本管理、滚动更新网络Service(ClusterIP)+ ELB Ingress集群内寻址、对外暴露配置ConfigMa...

一个应用的容器化上线,真正费时间的不是 kubectl apply,而是它之前的镜像分层、以及它之后的探针、配额和弹性。下面这条流程在 CCE 上跑通一遍,把能少踩的坑一次说清。

一、先定结构

层 组件 作用
镜像 SWR 组织 版本化交付物
编排 CCE 集群 + Deployment 副本管理、滚动更新
网络 Service(ClusterIP)+ ELB Ingress 集群内寻址、对外暴露
配置 ConfigMap / Secret 配置与环境解耦
弹性 HPA + metrics-server 按负载伸缩
观测 AOM + 云原生日志 指标与日志

二、镜像:让层缓存真正生效

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
RUN addgroup -S app && adduser -S app -G app
USER app
EXPOSE 8080
CMD ["node", "dist/main.js"]

三条硬要求:COPY package*.json 必须早于 COPY . .,否则改一行代码就全量重装依赖;用多阶段构建,别把编译器带进运行镜像;非 root 运行,后面配 securityContext 才不会互相打架。

推 SWR:

docker login -u ${REGION}@${AK} -p ${LOGIN_KEY} swr.${REGION}.myhuaweicloud.com
docker build -t swr.${REGION}.myhuaweicloud.com/${ORG}/demo-api:1.4.0 .
docker push swr.${REGION}.myhuaweicloud.com/${ORG}/demo-api:1.4.0

标签永远不要用 latest。回滚时你需要一个确定的坐标。

三、配置外置

apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-api-config
data:
  LOG_LEVEL: "info"
  UPSTREAM_URL: "http://backend.default.svc.cluster.local:9000"
---
apiVersion: v1
kind: Secret
metadata:
  name: demo-api-secret
type: Opaque
stringData:
  DB_PASSWORD: "${DB_PASSWORD}"

stringData 省掉手工 base64,但注意 Secret 只是编码不是加密,敏感值走 KMS 或外部密钥管理,别把明文 YAML 提交进仓库。

四、工作负载

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-api
spec:
  replicas: 3
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels: { app: demo-api }
  template:
    metadata:
      labels: { app: demo-api }
    spec:
      containers:
        - name: api
          image: swr.${REGION}.myhuaweicloud.com/${ORG}/demo-api:1.4.0
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef: { name: demo-api-config }
            - secretRef:    { name: demo-api-secret }
          resources:
            requests: { cpu: 200m, memory: 256Mi }
            limits:   { cpu: 1000m, memory: 512Mi }
          readinessProbe:
            httpGet: { path: /healthz, port: 8080 }
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet: { path: /livez, port: 8080 }
            initialDelaySeconds: 20
            periodSeconds: 20
            failureThreshold: 3
          securityContext:
            runAsNonRoot: true
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true

maxUnavailable: 0 是滚动更新零中断的前提。requests 不写,调度器就是瞎的,节点会被超卖到 OOM;limits 不写,一个内存泄漏能拖垮整台节点。就绪探针和存活探针必须分开:就绪失败只摘流量,存活失败才重启,混用会导致启动慢的应用无限重启。

五、暴露

apiVersion: v1
kind: Service
metadata:
  name: demo-api
spec:
  selector: { app: demo-api }
  ports:
    - port: 80
      targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: demo-api
  annotations:
    kubernetes.io/elb.class: union
    kubernetes.io/elb.id: ${ELB_ID}
spec:
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: demo-api
                port: { number: 80 }

Ingress 建完去 ELB 控制台确认监听器和后端服务器组已自动挂载,健康检查路径要和就绪探针一致,否则会出现“Pod 是 Running 但 502”。

六、弹性

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: demo-api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: demo-api
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target: { type: Utilization, averageUtilization: 65 }
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300

阈值定 65% 而不是 80%,因为扩出来的 Pod 冷启动要几十秒,等打到 80% 再扩已经晚了。缩容加 5 分钟稳定窗口,防止流量抖动造成副本反复伸缩。

七、观测

kubectl top pod -l app=demo-api          # 实时资源
kubectl get hpa demo-api -w              # 伸缩动作
kubectl logs -f deploy/demo-api --tail=200
kubectl rollout status deploy/demo-api   # 发布卡住时第一时间看这里

日志统一打 stdout,由 AOM 采集,不要往容器内文件写。发布失败先 kubectl rollout status,卡住就 kubectl rollout undo,别急着删 Pod 重建。

八、几条踩过的坑

  • 镜像拉取失败先查 SWR 是否私有:私有组织需要在 CCE 侧建 imagePullSecrets,不要靠节点默认凭据。
  • readOnlyRootFilesystem: true 后应用写临时目录会挂,给它单独挂一个 emptyDir 到 /tmp。
  • 探针 initialDelaySeconds 比应用真实启动时间短,是 CrashLoopBackOff 里最容易被忽略的一条。
  • ConfigMap 改了不会自动生效,滚动重启一次:kubectl rollout restart deploy/demo-api。
  • 节点规格选型按 requests 总和算,不是按 limits;但内存 limits 要留 30% 余量给内核与突发。

一个应用能稳定跑在生产上,靠的是这套默认值:多阶段构建、非 latest 标签、requests 与 limits 分离、三种探针分工、滚动更新零不可用、HPA 带稳定窗口。这些值写进模板后,后面每个服务复制过去就行。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。