首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >云原生工程:不是把应用塞进 Kubernetes

云原生工程:不是把应用塞进 Kubernetes

原创
作者头像
资源shanxueit.com
发布于 2026-10-19 14:17:41
发布于 2026-10-19 14:17:41
290
举报

一、先纠正一个误区:容器化不等于云原生

把应用打包成镜像,只完成了第一小步。真实项目里出问题的是这些:

  • 镜像 2GB,每次发布拉取就要几分钟,扩缩容慢到失去意义;
  • 容器以 root 运行,一个漏洞就是整台节点;
  • 配置写死在镜像里,换个环境要重新构建;
  • 没有健康检查,进程活着但服务已死,流量继续打进来;
  • 没有资源限制,一个应用吃光节点内存,拖垮同节点其他服务;
  • 发布直接覆盖,出问题只能回滚镜像,配置却回不去;
  • 日志打到文件,容器一重启全丢;
  • 没人看成本,月底账单才发现副本数配错了。

所以正确姿势不是“先上集群”,而是先把应用改造成可被编排的形态:

  1. 容器化:小镜像、非 root、可复现构建;
  2. 配置分离:环境变量/配置中心,密钥不进镜像;
  3. 优雅启停:能接信号、能等连接排空;
  4. 健康检查:区分存活、就绪、启动;
  5. 资源边界:requests/limits 明确;
  6. 可观测:日志到标准输出,指标和追踪齐全;
  7. 发布策略:滚动、灰度、可回滚;
  8. 成本意识:副本数、资源规格、闲置资源有账可算。

二、第一步:容器化,镜像要小且可复现

镜像不是越小越好,但大镜像会直接吃掉弹性和发布速度。多阶段构建是最基本的做法:

代码语言:javascript
复制
FROM golang:1.22 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -o /app ./cmd/server

FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /app /app
USER nonroot
ENTRYPOINT ["/app"]

三个要点:构建阶段和运行阶段分离、固定基础镜像版本、默认非 root。再加上 .dockerignore 排除无关文件,镜像体积通常能降一个数量级。

可复现性同样重要:依赖版本锁定、构建参数显式声明、镜像打上不可变标签(用 commit hash,而不是 latest)。用 latest 的部署,等于放弃回滚能力。

三、第二步:配置与密钥分离

判断标准很朴素:同一个镜像,能不能在开发、测试、生产三个环境跑起来而不用重新构建? 不能,就说明配置耦合了。

  • 普通配置:环境变量或配置中心;
  • 密钥:用 Secret 管理,绝不进镜像、绝不进代码库;
  • 环境差异:通过部署清单注入,而不是代码里 if env == "prod"。

还有一个常被忽略的点:配置变更要能触发滚动更新。如果配置改了但 Pod 没重启,应用读的还是旧值,这种“改了没生效”的问题极难排查。

四、第三步:优雅启停,否则滚动发布必掉请求

容器被终止时,K8s 先发 SIGTERM,等一段时间后 SIGKILL。如果应用收到信号立刻退出,正在处理的请求全部失败。

代码语言:javascript
复制
import signal, threading
from http.server import HTTPServer

stop = threading.Event()

def on_term(signum, frame):
    stop.set()               # 先停止接收新请求
    server.shutdown()        # 再排空已有连接

signal.signal(signal.SIGTERM, on_term)
signal.signal(signal.SIGINT, on_term)

server = HTTPServer(("0.0.0.0", 8080), Handler)
server.serve_forever()

配套要设置合理的 terminationGracePeriodSeconds,并且让就绪探针在停机时先返回失败,把流量摘干净再退出。顺序错了,滚动发布就会间歇性 502。

五、第四步:健康检查要分三种

很多人只配一个 livenessProbe,这是常见错误。三种探针职责不同:

  • startupProbe:启动慢的应用先过这一关,避免被误杀;
  • readinessProbe:能不能接流量,失败就从 Service 摘除;
  • livenessProbe:进程是否还活着,失败就重启。
代码语言:javascript
复制
startupProbe:
  httpGet: { path: /healthz, port: 8080 }
  failureThreshold: 30
  periodSeconds: 2
readinessProbe:
  httpGet: { path: /readyz, port: 8080 }
  periodSeconds: 5
livenessProbe:
  httpGet: { path: /healthz, port: 8080 }
  periodSeconds: 10
  failureThreshold: 3

关键判断:liveness 只检查“进程自身是否健康”,不要把它做成依赖检查。 如果数据库抖动导致 liveness 失败,所有 Pod 会一起重启,把一次小故障放大成雪崩。依赖检查放 readiness 更合适。

六、第五步:资源边界,是稳定性和成本的共同基础

不设 requests,调度器不知道该怎么分配;不设 limits,一个内存泄漏就能拖垮整个节点。

代码语言:javascript
复制
resources:
  requests: { cpu: "200m", memory: "256Mi" }
  limits:   { cpu: "1",    memory: "512Mi" }

两条经验:

  1. requests 按实际用量给,给太高会浪费调度空间,给太低会被频繁驱逐;
  2. 内存 limit 等于硬上限,超了直接 OOMKill。所以要配合压测确定合理值,而不是拍脑袋。

CPU limit 要谨慎:它会导致节流(throttling),表现为延迟毛刺。对延迟敏感的服务,有时只设 requests 不设 CPU limit 更合适,但要配合节点资源隔离。

七、第六步:可观测,是云原生的入场券

容器是临时的,所以状态必须外置:

  • 日志:打到标准输出,由平台统一采集,不要写本地文件;
  • 指标:请求量、错误率、延迟分位、资源使用、重启次数;
  • 追踪:请求 ID 贯穿服务调用,能定位慢在哪一跳;
  • 事件:Pod 重启、OOMKill、驱逐、探针失败,这些是排查的第一线索。

其中重启次数和 OOMKill 次数最值得盯。它们升高往往意味着资源配错或内存泄漏,而不是“偶发问题”。

八、第七步:发布与回滚,必须是一等公民

云原生的价值之一是可回滚,但前提是每次发布都可标识、可追溯:

  • 镜像用不可变标签,部署清单进版本控制;
  • 用滚动更新或灰度发布,先小流量验证;
  • 配置变更和镜像变更分开记录,避免“回滚了镜像但配置没回”;
  • 保留上一版清单,一键回滚;
  • 发布后自动检查关键指标,异常则中止。

“回滚能力”要在设计阶段就做进去,而不是出事时临时想办法。

九、第八步:成本,是云原生工程的必答题

弹性不等于省钱。常见的成本失控原因:

  • 副本数按峰值配,长期低负载运行;
  • requests 给得过大,节点利用率极低;
  • 测试环境 24 小时常开;
  • 镜像和日志无限堆积;
  • 没有资源归属标签,账单无法归因到团队。

最低要求:给资源打上团队、环境、服务标签,定期看利用率和闲置资源。没有成本可见性,就没有优化动力。

十、AI 在云原生工程里的正确角色

AI 很适合帮你做这些:

  • 生成 Dockerfile、Deployment、Helm values 初稿;
  • 审查探针配置、资源限制、安全上下文是否有遗漏;
  • 根据事件和日志整理排障清单;
  • 把部署清单转成文档和检查表。

一个实用的审查提示词:

代码语言:javascript
复制
请审查下面 Kubernetes 部署清单,只输出问题表格:
问题类型 | 位置 | 风险说明 | 修改建议
重点检查:是否非 root 运行、是否设置 resources、探针是否合理、
是否使用 latest 标签、密钥是否硬编码、是否有优雅停机配置。
不要重写清单,不要评价业务逻辑。

但资源规格定多少、可用性目标是多少、成本预算怎么分,必须人来做。AI 没有你的业务和账单。

十一、常见坑

  1. 镜像用 latest:无法回滚,也无法复现。
  2. liveness 做依赖检查:一次抖动导致全体重启。
  3. 没有优雅停机:滚动发布间歇性 502。
  4. 不设 resources:一个内存泄漏拖垮整个节点。
  5. 密钥进镜像:一旦泄露,影响范围不可控。
  6. 日志写本地文件:容器重启即丢,无法排查。
  7. 配置变更不触发重启:改了没生效,排查半天。
  8. 只看可用性不看成本:副本数和规格长期浪费。

结论

云原生工程的正确姿势,不是“把应用塞进 Kubernetes”,而是先把应用改造成可被编排的形态:小镜像、非 root、配置分离、优雅启停、三种探针、资源边界、日志外置、发布可回滚、成本可归因。这些做到了,平台才真正发挥作用。

代码可以很少,但探针不能乱配,资源不能不给,密钥不能进镜像,回滚不能没有。记住一句话:云原生不是换一个部署平台,而是让应用具备被平台安全调度、弹性伸缩、可靠回滚的能力;应用不达标,平台再好也只是放大器。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、先纠正一个误区:容器化不等于云原生
  • 二、第一步:容器化,镜像要小且可复现
  • 三、第二步:配置与密钥分离
  • 四、第三步:优雅启停,否则滚动发布必掉请求
  • 五、第四步:健康检查要分三种
  • 六、第五步:资源边界,是稳定性和成本的共同基础
  • 七、第六步:可观测,是云原生的入场券
  • 八、第七步:发布与回滚,必须是一等公民
  • 九、第八步:成本,是云原生工程的必答题
  • 十、AI 在云原生工程里的正确角色
  • 十一、常见坑
  • 结论
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档