Skip to main content

Kubernetes (K8s) 核心概念与 Node.js 实战

在掌握了 Docker 之后,我们知道如何将单个 Node.js 应用打包成镜像并运行。但如果在生产环境中,你有 100 个微服务,每个服务需要跑 5 个实例,并且需要处理负载均衡、崩溃重启、版本回滚——这就不是简单的 docker run 能解决的了。

Kubernetes (简称 K8s) 就是用来解决大规模容器编排问题的操作系统。对于现代大厂的全栈开发者来说,了解 K8s 的核心概念是必修课。

1. 全栈视角下的 K8s 核心概念

K8s 的概念非常多,但从全栈/后端开发者的日常使用来看,最核心的是以下几个“资源对象”:

📦 Pod(豆荚)

  • 定义:K8s 中最小的部署单元。一个 Pod 里面可以包含一个或多个紧密相关的容器。
  • 类比:你可以把 Pod 想象成一台“逻辑上的轻量级虚拟机”,里面跑着你的 Node.js Docker 容器。通常我们是一个 Pod 跑一个 Node.js 容器

🚀 Deployment(部署)

  • 定义:负责管理 Pod 的生命周期,比如规定这个 Node.js 应用需要同时跑 3 个副本(Replicas)。
  • 能力
    • 自愈能力:如果某个 Pod 崩溃了,Deployment 会自动拉起一个新的。
    • 滚动更新 (Rolling Update):发布新版本时,K8s 会逐个替换旧的 Pod,实现零停机部署

🌐 Service(服务)

  • 定义:Pod 的 IP 是会动态变化的(崩溃重启后 IP 就变了)。Service 为一组相同的 Pod 提供一个固定的内部访问地址负载均衡
  • 类比:相当于集群内部的 Nginx upstream。它确保无论背后的 Pod 怎么生生死死,前端或其他服务只需访问 Service 的固定名称即可。

🚪 Ingress(入口)

  • 定义:管理集群外部访问集群内部服务的路由规则。
  • 类比:相当于整个 K8s 集群对外的 Nginx 暴露层,可以配置域名、HTTPS 证书、路径转发(比如 /api 转发给后端的 Service,/ 转发给前端的 Service)。

⚙️ ConfigMap & Secret(配置与机密)

  • 定义:将环境变量(如 NODE_ENV)存放在 ConfigMap 中,将敏感信息(如数据库密码)存放在 Secret 中。
  • 作用:让代码/镜像与配置解耦。同一个镜像,挂载测试环境的 ConfigMap 就是测试服,挂载生产环境的就是正式服。

2. 为什么在 K8s 中不推荐使用 PM2?

在之前的部署章节提到过,传统物理机部署极度依赖 PM2,但在 K8s 中通常直接使用 node app.js(单进程模式)。

  1. 能力重叠:PM2 擅长的进程守护、负载均衡,K8s 在 Pod 和 Service 层面已经做得更好了,甚至跨越了单台物理机的限制。
  2. 生命周期割裂(最致命):K8s 通过监控容器的主进程(PID 1)来判断应用状态。如果用 PM2 启动,PM2 成了 PID 1,只要 PM2 没死,K8s 就认为服务正常。即使你的 Node.js 业务进程已经僵死,K8s 也无法感知并重启它。
  3. 日志收集:K8s 默认收集容器输出到 stdoutstderr 的标准输出日志。PM2 会接管日志并写入自己管理的文件,这会让 K8s 原生的日志采集系统(如 Fluentd/Filebeat)收集起来变得极其麻烦。

3. Node.js 应用适配 K8s 的两个关键点

要让 Node.js 应用在 K8s 中完美运行,必须在代码层面做好两件事(这通常是大厂架构组的强制要求):

① 健康检查接口 (Probes)

K8s 需要知道你的 Node.js 是否准备好接收流量,以及是否“活着”。你需要暴露简单的 HTTP 接口供 K8s 定时探测。

const express = require("express");
const app = express();

// 存活探针 (Liveness Probe):返回 200 表示进程没死。
// 如果超时或报错,K8s 会无情地直接杀掉该 Pod 并拉起一个新的。
app.get("/health/liveness", (req, res) => {
res.status(200).send("OK");
});

// 就绪探针 (Readiness Probe):返回 200 表示可以接收用户请求了。
// 有时候 Node.js 启动了,但还在连接数据库、拉取远程配置,此时应返回 500。
// K8s 看到 500 就不会把用户的真实流量转发过来。
app.get("/health/readiness", (req, res) => {
if (db.isConnected) {
res.status(200).send("OK");
} else {
res.status(500).send("Not Ready");
}
});

② 优雅退出 (Graceful Shutdown)

当 K8s 想要停止一个 Pod(比如你发布了新版本进行滚动更新时),它会向容器发送 SIGTERM 信号。如果你的 Node.js 收到信号直接退出,正在处理的请求就会立刻报错 502。

process.on("SIGTERM", () => {
console.log("K8s 通知准备停止 Pod...");
// 1. K8s 此时会将该 Pod 从 Service 中摘除,不再分配新流量过来
// 2. Node.js 停止接收新请求
server.close(() => {
// 3. 等待正在处理的存量请求执行完成
// 4. 断开数据库连接
db.disconnect();
process.exit(0); // 彻底安全退出
});
});

4. 实战:Node.js 的标准 K8s YAML 配置

了解概念后,全栈开发者通常需要能看懂或编写基础的 K8s YAML 配置文件(也叫声明式 API)。

# ==========================================
# 1. 定义 Deployment (管理 Node.js 实例)
# ==========================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nodejs-app
spec:
replicas: 3 # 启动 3 个 Node.js 实例副本
selector:
matchLabels:
app: my-nodejs-app
template:
metadata:
labels:
app: my-nodejs-app
spec:
containers:
- name: nodejs-container
image: your-registry.com/my-nodejs-app:v1.0.0
ports:
- containerPort: 3000
# 告诉 K8s 怎么进行健康检查
livenessProbe:
httpGet:
path: /health/liveness
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/readiness
port: 3000

---
# ==========================================
# 2. 定义 Service (内部负载均衡器)
# ==========================================
apiVersion: v1
kind: Service
metadata:
name: my-nodejs-service
spec:
selector:
app: my-nodejs-app # 自动找到上面打上该标签的 3 个 Pod,并将流量分发给它们
ports:
- protocol: TCP
port: 80 # Service 暴露给集群内部其他服务访问的端口
targetPort: 3000 # 实际转发到 Node.js 容器内部的端口

5. K8s vs PM2:核心能力对比总结

看完上面的 YAML 配置,你可能会觉得 K8s 极其繁琐,而 PM2 只需要一行 pm2 start app.js -i max 就搞定了。为什么大厂还要“折腾” K8s?

其实它们两者的维度完全不同。PM2 是单机维度的进程管家,而 K8s 是集群维度的操作系统。

核心能力PM2 (单机进程管理)K8s (集群容器编排)K8s 怎么做的?
负载均衡只能在当前机器的 CPU 多核之间分配流量 (cluster 模式)可以在几十台上百台不同物理机的 Pod 之间分发流量通过 ServiceIngress 实现。
崩溃重启进程崩溃了,PM2 原地重启进程容器/Pod 崩溃了,K8s 重新拉起一个新的 Pod(甚至可以调度到另一台健康的物理机上)通过 Deployment 维持期望的 replicas 副本数。
扩缩容只能修改 instances 数量,上限是当前机器的 CPU 核数突破单机限制,只需修改 replicas: 100,可以跨几十台机器瞬间拉起 100 个实例甚至可以根据 CPU 利用率自动扩缩容(HPA - Horizontal Pod Autoscaler)。
健康检查只判断进程的 PID 还在不在(如果进程假死、死循环,PM2 无法感知)提供应用级别的 livenessProbereadinessProbe,深入探测接口响应状态如上文的 YAML 所示,如果 HTTP 接口返回 500,K8s 会介入处理。
零停机发布pm2 reload 逐个重启进程Rolling Update:先启动新版本 Pod,健康检查通过后,再杀掉旧版本 Pod完美支持灰度发布、蓝绿部署,且失败可秒级回滚 (kubectl rollout undo)。

总结

  • 如果你的项目只有一台服务器(或几台),用 PM2 是最省心、最高效的选择。
  • 如果你的项目庞大,微服务众多,需要跨多台机器部署,且对高可用性要求极高,K8s 则是现代大厂唯一的标准答案。在 K8s 的容器里,请抛弃 PM2,回归最原始的 node app.js