前后端并行开发时,接口没ready是最常见的阻塞点。传统做法是在本地跑一个Mock服务器,或者用Apifox之类的工具模拟响应。但一旦联调环境多了、参与的人多了,本地Mock的维护成本就上来了:每个人的Mock数据不一致、版本漂移、环境切换麻烦。更稳妥的思路是,用NestJS写一套统一的Mock服务,把它打成Docker镜像,直接部署到K8s集群里,所有环境共用同一个数据源,版本跟着镜像走,天然解决一致性问题。本文就完整走一遍这个流程。

一、用NestJS搭建结构化的Mock服务
为什么选NestJS而不是Express裸写?核心原因是NestJS的模块化结构天然适合管理大量Mock接口。当Mock接口数量从十几个涨到上百个时,Express的散装路由会变得难以维护,而NestJS可以按业务域拆分Module,每个域一个Controller,配合DTO做参数校验,Mock出来的接口行为和真实后端几乎一致。
先看项目结构设计。建议按业务域组织目录,比如user、order、product各一个Module,公共的延迟模拟、异常模拟逻辑抽成拦截器统一处理:
// mock.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, of, delay } from 'rxjs';
@Injectable()
export class MockInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const req = context.switchToHttp().getRequest();
// 从header读取模拟延迟,方便前端测试loading态
const mockDelay = Number(req.headers['x-mock-delay'] || 0);
return next.handle().pipe(delay(mockDelay));
}
}
这个拦截器的作用是让前端通过请求头动态控制Mock响应延迟。比如传x-mock-delay: 2000就能模拟弱网环境,测试loading动画和超时逻辑非常方便。除此之外,还建议实现一个全局异常模拟Controller,通过特定的header或路径参数强制返回500、401等错误码,用来验证前端的容错处理。
数据层面,Mock数据不要硬编码在Controller里,建议放到独立的JSON文件或者用简单的内存数据库(比如lowdb)。这样后续要做数据变更,只需要改数据文件,通过ConfigModule加载,代码本身不用动。同时给服务加一个/health接口返回进程状态,后面K8s的健康检查会用到。
// app.controller.ts
import { Controller, Get } from '@nestjs/common';
@Controller()
export class AppController {
@Get('health')
health() {
return { status: 'ok', uptime: process.uptime(), pid: process.pid };
}
}
二、编写Dockerfile:多阶段构建压缩镜像体积
Node.js镜像最容易犯的错误就是直接把node_modules打进镜像还不做分层优化。一个典型的问题镜像能到1GB以上,拉取和调度都很慢。正确的做法是使用多阶段构建,并且利用Docker的层缓存机制:先拷贝package.json安装依赖,再拷贝源码,这样源码变更时不会触发依赖重新安装。
# 构建阶段 FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # 运行阶段 FROM node:20-alpine ENV NODE_ENV=production WORKDIR /app # 只安装生产依赖 COPY package*.json ./ RUN npm ci --omit=dev COPY --from=builder /app/dist ./dist COPY --from=builder /app/mock-data ./mock-data EXPOSE 3000 # dumb-init保证容器收到SIGTERM信号能正确退出 CMD ["dumb-init", "node", "dist/main.js"]
几个关键点值得展开。第一,用npm ci代替npm install,前者严格按照lock文件安装,保证构建可复现。第二,运行阶段只装生产依赖,devDependencies里的TypeScript编译器、测试工具统统不带进镜像,体积能砍掉一半以上。第三,alpine基础镜像只有几MB,但要注意如果依赖了含原生编译的包(比如bcrypt),需要额外装编译工具或者换成node:20-slim。第四,dumb-init的作用是处理容器的信号转发,没有它,K8s发SIGTERM终止Pod时Node进程可能无法优雅退出,导致连接被硬切断。
另外别忘了Docker优雅退出的代码配合。在main.ts里监听信号并主动关闭Nest应用:
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
NestJS的app.close()会触发各个服务的onApplicationShutdown钩子,如果Mock服务里挂了数据库连接或者定时器,记得在这个钩子里清理,否则滚动更新时旧Pod可能一直挂着不退出,最终被强杀。
三、K8s部署配置:Deployment、Service与探针
镜像构建好推到仓库后,下一步写K8s清单。Mock服务属于无状态应用,用Deployment管理副本即可。重点是探针配置,很多团队上线容器服务第一批问题都出在探针上。
apiVersion: apps/v1
kind: Deployment
metadata:
name: mock-server
spec:
replicas: 2
selector:
matchLabels:
app: mock-server
template:
metadata:
labels:
app: mock-server
spec:
containers:
- name: mock-server
image: registry.ipipp.com/mock-server:v1.2.0
ports:
- containerPort: 3000
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
name: mock-server
spec:
selector:
app: mock-server
ports:
- port: 80
targetPort: 3000
readinessProbe和livenessProbe的职责要分清。就绪探针失败时,Pod会被从Service的 endpoints 里摘除,但不会被杀掉;存活探针失败则会触发容器重启。Node.js冷启动一般几秒内完成,initialDelaySeconds给5到15秒足够了。如果发现服务频繁被重启,先检查是不是liveness探针的超时设置太短——Node的事件循环被阻塞时,健康检查接口响应会变慢,误判成假死。可以给health接口加一个最长响应时间检测,事件循环延迟超过阈值就主动失败,配合诊断真实卡顿。
滚动更新策略建议加上maxUnavailable: 0和maxSurge: 1,保证更新过程中始终有可用副本承接请求。对于Mock服务来说,更新频繁(每次接口结构变更都要发版),这个配置能避免联调过程中出现短暂的503:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
最后补充一个实用技巧:多环境区分。可以在Deployment的env里注入MOCK_ENV变量,NestJS启动时根据它加载不同的数据文件,一个镜像跑test、staging多套环境,数据层面通过ConfigModule切换,避免为每个环境单独构建镜像。这套方案跑通之后,Mock层就从临时脚本变成了集群内的正式基础设施,前端不再依赖后端的进度,联调周期可以明显缩短。
NestJSK8sNode.js容器化修改时间:2026-09-11 22:16:52