云计算与部署容器与 Kubernetes

容器与 Kubernetes

Bun 服务的可复现镜像、探针、信号、密钥和扩缩容基线

最后更新于

生产镜像

优先使用 Bun 官方镜像,并固定经过验证的标签或 digest。下面按开发依赖、测试、生产依赖分层,最终镜像只复制运行所需内容:

Dockerfile
FROM oven/bun:1 AS base
WORKDIR /app

FROM base AS install
COPY package.json bun.lock ./
RUN bun ci

FROM base AS verify
COPY --from=install /app/node_modules ./node_modules
COPY . .
RUN bun run typecheck && bun test && bun run build

FROM base AS production-deps
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production --ignore-scripts

FROM base AS release
ENV NODE_ENV=production
COPY --from=production-deps /app/node_modules ./node_modules
COPY --from=verify /app/dist ./dist
COPY package.json ./
USER bun
EXPOSE 3000
CMD ["bun", "run", "--no-env-file", "dist/index.js"]

如果生产依赖确实需要安装脚本,不要静默删除 --ignore-scripts:先用 bun pm untrusted 定位依赖,审查后通过 trustedDependencies 做最小放行。

这个 Dockerfile 是单包且 dist 仍需要生产依赖的基线。Monorepo 安装阶段还必须复制相关 Workspace manifest;完全 bundle 的产物可以不复制 node_modules,但 external、动态文件和原生依赖必须先检查。完整工程门禁见 生产工程基线

服务契约

src/index.ts
const port = Number(Bun.env.PORT ?? 3000);

const server = Bun.serve({
  port,
  idleTimeout: 30,
  maxRequestBodySize: 2 * 1024 * 1024,
  routes: {
    '/live': () => new Response('ok'),
    '/ready': async () => {
      const ready = await dependenciesAreReady();
      return new Response(ready ? 'ok' : 'not ready', { status: ready ? 200 : 503 });
    },
  },
  fetch: handleRequest,
});

async function shutdown(signal: string) {
  console.info('shutdown_started', { signal });
  await server.stop();
  await closeDependencies();
}

process.once('SIGTERM', () => void shutdown('SIGTERM'));
process.once('SIGINT', () => void shutdown('SIGINT'));

dependenciesAreReady()closeDependencies() 是项目边界函数,不是 Bun 内置 API。把数据库、队列和外部服务放进去,但给探针设置短超时,避免健康检查自己拖垮服务。

三类探针

探针回答的问题失败后的动作
startup进程是否已经完成首次启动?启动期间暂不运行其他探针
readiness现在能否接收新流量?从负载均衡摘除,不应直接重启
liveness进程是否卡死且需要重启?重启容器

不要让 liveness 依赖偶发抖动的外部 API。Kubernetes 官方特别提醒,错误的存活探针会制造级联故障。

云原生检查表

  • 端口:读取 PORT;Cloud Run 默认把请求发到配置的容器端口。
  • 状态:会话、任务状态和上传对象放外部存储;容器文件系统可随时消失。
  • 日志:输出结构化 stdout/stderr,包含请求 ID;禁止记录提示词中的密钥和完整用户数据。
  • 资源:设置 CPU/内存 request 与 limit;用压测决定并发,不从本机结果猜测。
  • 数据库迁移:作为单独 Job 或发布步骤执行,不让每个副本同时迁移。
  • 密钥:使用平台 secret 引用。注入为环境变量的密钥通常不会自动刷新,轮换后要重新部署或实现动态读取。

官方参考:Bun Docker guideKubernetes probesKubernetes SecretsCloud Run configuration