Mock服务在前后端联调中扮演着重要角色,但很多人只在本地用node直接启动一个Express服务就完事了。一旦要把这套Mock服务交给测试团队或者部署到Kubernetes集群里做集成测试,裸跑进程的方式就会暴露出各种问题:环境不一致、依赖缺失、进程崩溃没人重启。把Mock数据连同Mock服务器一起打包成Docker镜像,再以Pod的形式跑在k8s里,才是工程化程度更高的做法。

一、准备一个可容器化的Node.js Mock服务器
打包镜像之前,先确保Mock服务器本身适合容器化运行。最基本的三条原则是:监听地址必须是0.0.0.0而不是127.0.0.1,端口最好从环境变量读取,Mock规则数据要外置而不是硬编码在代码里。下面是一个结构清晰的示例,Mock规则放在单独的rules.json文件中,服务启动时读取并注册路由。
const express = require('express');
const fs = require('fs');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
// 读取外置的Mock规则文件
const rules = JSON.parse(fs.readFileSync('./rules.json', 'utf-8'));
rules.forEach(rule => {
const method = rule.method.toLowerCase();
app[method](rule.path, (req, res) => {
// 支持根据请求参数做简单的动态匹配
res.set('X-Mock-Service', 'mock2image');
res.json(typeof rule.response === 'function' ? rule.response(req) : rule.response);
});
});
// 健康检查端点,供k8s探针使用
app.get('/healthz', (req, res) => res.status(200).send('ok'));
app.listen(PORT, '0.0.0.0', () => {
console.log(`Mock server listening on ${PORT}`);
});rules.json的结构也很简单,一个规则数组,每条规则包含方法、路径和响应体。把规则和代码分离的好处在于,后续可以通过k8s的ConfigMap挂载不同的规则文件,用同一个镜像模拟多套后端行为,避免为每个场景单独构建镜像。
二、编写Dockerfile并优化镜像体积
Node.js项目构建镜像,推荐使用多阶段构建加node:alpine基础镜像的组合。alpine版本体积只有几十MB,配合npm ci --omit=dev安装生产依赖,最终镜像通常能控制在150MB以内。有一点要特别注意:node官方镜像定义了名为NODE_ENV的环境变量用法习惯,同时内置了node用户,运行阶段切换到非root用户是容器安全的基本要求。
# 构建阶段 FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . # 运行阶段 FROM node:20-alpine WORKDIR /app ENV NODE_ENV=production PORT=3000 COPY --from=builder /app/node_modules ./node_modules COPY server.js rules.json ./ USER node EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://127.0.0.1:3000/healthz || exit 1 CMD ["node", "server.js"]
这里有几个容易踩的坑值得展开说明。第一,一定要用.dockerignore排除node_modules目录,否则构建上下文会把本地依赖整个发过去,速度慢且可能混入平台不兼容的二进制包。第二,npm ci比npm install更适合CI构建,它严格按照package-lock.json安装,结果可复现。第三,Dockerfile里的HEALTHCHECK只是Docker层的健康检查,进入k8s后应该由探针接管,两者可以共存但不要混淆。
构建完成后用docker build -t mock2image:v1 .生成镜像,再用docker run --rm -p 3000:3000 mock2image:v1本地验证,curl一下接口确认响应正确后再推送镜像仓库。如果团队使用nhost这类托管后端平台,可以把镜像推送到它关联的镜像仓库,由平台侧完成后续的调度和暴露。
三、编写Kubernetes清单并部署上线
镜像推送到仓库后,接下来写Deployment和Service两份清单。Deployment负责维护Pod副本和滚动更新,Service负责在集群内暴露稳定的访问入口。Mock规则通过ConfigMap挂载进容器,这样改规则只需要替换ConfigMap并重启Pod,不用重新构建镜像。
apiVersion: apps/v1
kind: Deployment
metadata:
name: mock2image
spec:
replicas: 2
selector:
matchLabels:
app: mock2image
template:
metadata:
labels:
app: mock2image
spec:
containers:
- name: mock
image: registry.ippipp.com/mock2image:v1
ports:
- containerPort: 3000
env:
- name: PORT
value: "3000"
volumeMounts:
- name: rules
mountPath: /app/rules.json
subPath: rules.json
readinessProbe:
httpGet:
path: /healthz
port: 3000
initialDelaySeconds: 5
resources:
requests: {cpu: "50m", memory: "64Mi"}
limits: {cpu: "200m", memory: "128Mi"}
volumes:
- name: rules
configMap:
name: mock-rules
---
apiVersion: v1
kind: Service
metadata:
name: mock2image
spec:
selector:
app: mock2image
ports:
- port: 80
targetPort: 3000探针配置是新手最容易忽略的部分。readinessProbe指向/healthz端点,只有健康检查通过后Pod才会被加入Service的转发列表,避免滚动更新时把流量打到还没启动完的进程上。Node.js进程启动虽然快,但依赖加载和规则文件解析仍需要一点时间,initialDelaySeconds设置5秒左右比较稳妥。资源限制方面,一个纯内存的Mock服务64Mi起步完全够用,超过limit被OOMKilled时优先检查是否返回了过大的JSON。
四、常见问题排查思路
部署到k8s后如果访问不通,排查顺序应该是固定的四步。第一步kubectl get pods看Pod状态,如果是ImagePullBackOff说明镜像地址或凭证有问题;如果是CrashLoopBackOff,用kubectl logs看容器日志,最常见的原因是容器内监听地址写成了127.0.0.1。第二步用kubectl describe pod看事件详情,确认探针失败还是挂载失败。第三步用kubectl exec -it pod -- wget -qO- http://127.0.0.1:3000/healthz进容器内部自测,判断问题在应用层还是网络层。第四步检查Service的endpoint列表,kubectl get endpoints mock2image如果为空,多半是标签选择器没对上。
另一个高频问题是ConfigMap更新后容器内文件没变化。subPath方式挂载的文件不会随着ConfigMap热更新,这是k8s的已知行为,需要重启Pod才能生效,可以用kubectl rollout restart deployment/mock2image触发。理解了这些细节,Node.js的Mock服务就能在容器环境里稳定运行,为前后端联调和集成测试提供可靠的接口保障。
Node.jsDocker镜像Kubernetes修改时间:2026-09-06 20:02:37