NestJS 容器部署
简化 Node.js/Nest.js 部署:使用 Docker 和 Google Cloud 的分步指南
一份修正后的指南,介绍 Node.js 和 NestJS 多阶段容器、不可变产物,以及为 Cloud Run 交付选择 Docker Hub 还是 Cloud Build。
阅读时间:8分钟作者 Abdullah Raheel
部署 Node.js 或 NestJS 服务
这是我在 2023 年为 NestJS 微服务采用的部署路径:构建 Docker 镜像,通过 GitHub Actions 发布,再部署到 Cloud Run。我已修正原指南中如今不会再用于生产交付的部分。
Docker 将运行时、依赖项和应用打包进一个镜像。它减少了本地、CI 和 Cloud Run 环境之间的差异,但配置与基础设施仍可能不同。
本指南分为三个阶段:构建并测试镜像,通过 GitHub Actions 和 Docker Hub 发布镜像,以及通过手动修订版本或持续部署将镜像部署到 Google Cloud。
前提条件
- 开始构建前,请先安装 Docker。
- 在 Windows 上安装 WSL(适用于 Linux 的 Windows 子系统),以便 Docker 使用所需的 Linux 环境。
- 如果安装 WSL 时出现错误 0x80370114,请先启用所需的虚拟化功能。
阶段 1:创建 Docker 镜像
最初的 2023 年指南只使用一个 Node 18 Alpine 阶段,先安装生产依赖项,再在同一镜像中运行 Nest 构建。这个顺序无法可靠构建 NestJS,因为 TypeScript 和其他构建工具通常位于 devDependencies 中。修正后的镜像将编译与运行时分离,使构建阶段拥有完整工具链,同时不把这些工具带入生产环境。
FROM node:18-alpine AS build
WORKDIR /usr/src/app
# Copy manifests first so dependency layers can be reused.
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /usr/src/app/dist ./dist
EXPOSE 8080
USER node
CMD ["node", "dist/main.js"]构建阶段使用 npm ci,因此代码仓库中必须提交锁文件,并通过项目固定的 Nest 和 TypeScript 版本完成编译。运行时阶段接收生产依赖项和编译产物,然后使用镜像中的 node 用户放弃 root 权限。
Dockerfile
.dockerignore
node_modules
npm-debug.log
dist忽略这些文件可以缩小 Docker 构建上下文,并防止主机依赖项或现有 dist 目录被复制进镜像。
在本地运行并检查容器
构建镜像并确认 Docker 已成功创建它。
docker build --tag nest-microservice .
docker images启动一个分离模式的容器,将其内部端口 8080 映射到本地端口 5000。发布镜像前,请检查 docker ps、调用 HTTP 健康检查端点并查看 docker logs。
docker run -d --publish 5000:8080 --name nest-container nest-microservice
docker ps
docker ps --all
docker stop nest-container确认本地镜像和容器正常运行后,将 Dockerfile 和 .dockerignore 推送到 GitHub 代码仓库。
阶段 2:使用 GitHub Actions 发布镜像
持续集成需要一个包含 Node.js 或 NestJS 源代码的 GitHub 代码仓库和一个 Docker Hub 账户。创建 Docker Hub 个人访问令牌,然后将账户名和令牌分别保存为名为 DOCKERHUB_USERNAME 和 DOCKERHUB_TOKEN 的代码仓库密钥。
下面这个 2023 年工作流会在每次推送到 main 时运行:检出源代码,登录 Docker Hub,启用 Buildx,使用根目录下的 Dockerfile 构建镜像,并推送 abdullahraheel/nest-microservice:latest。这里保留的是原始实现,包括当时使用的 Action 版本。
name: ci
on:
push:
branches:
- "main"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/nest-microservice:latest- 保存并提交该工作流。
- 将更改推送到 main,以启动工作流。
- 检查 Actions 运行记录;如果任务失败,请诊断具体失败步骤。
- 确认已发布的镜像仓库和标签出现在 Docker Hub 中。
阶段 3:将镜像部署到 Google Cloud Run
创建 Google Cloud 项目,打开 Cloud Run 并创建服务。手动部署时,以 username/repository:tag 的形式提供 Docker Hub 镜像,选择区域和服务设置,然后创建修订版本。示例镜像为 abdullahraheel/nest-microservice:latest;请使用你自己的工作流生成的确切镜像仓库和标签。
连接 Cloud Build 以实现持续部署
- 选择“创建服务”,再选择使用 Cloud Build 设置持续部署。
- 选择代码仓库提供商和代码仓库。
- 选择部署分支,本例使用 main。
- 选择 Dockerfile 作为构建类型,并提供其路径。
- 保存配置并创建服务。
连接生效后,合并到 main 的更改会触发 Cloud Build 构建代码仓库并自动重新部署服务。这是阶段 2 所产出 Docker Hub 镜像部署路径的替代方案,而不是该产物的另一个使用方。
总结与参考资料
- 将 NestJS 或 Node.js 项目构建为 Docker 镜像,并在本地测试其容器。
- 选择一条交付路径:通过 GitHub Actions 发布带版本的镜像,或让 Cloud Build 构建已连接的代码仓库。
- 手动部署确切的镜像修订版本,或使用已连接的 Cloud Build 路径自动创建服务修订版本。
- 定时工作应使用外部调度器或单例控制;Cloud Run Web 实例本身并不是单一且持久的 cron 工作进程。

