Jump to content

Docker

你可以将这个 stack 容器化,将它作为一个单独容器使用 Docker 来部署,或者作为一系列容器的部分,使用 docker-compose 来部署。参看基于本文档的示例仓库 ajcwebdev/ct3a-docker

Docker 项目配置

请注意 Next.js 会针对编译时客户端的环境变量(在客户端,名称前缀含有 NEXT_PUBLIC) 和运行时环境的服务器端变量有不同的处理过程。在本次演示中我们将使用两个环境变量,请注意它们在 Dockerfile 文件中的位置、命令行参数以及文件 docker-compose.yml

  • DATABASE_URL (被用于服务器端)
  • NEXT_PUBLIC_CLIENTVAR (被用于客户端)

1. Next 的配置

在你的 next.config.js 文件里,添加构建输出选项 standalone,以自动借助追踪输出文件来降低镜像文件的大小

export default defineNextConfig({
  reactStrictMode: true,
  swcMinify: true,
+ output: "standalone",
});

2. 创建 dockerignore 文件

点击这里并将下方内容添加到 .dockerignore 里:
.env
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git

3. 创建 Dockerfile

因为我们并没有将服务端环境变量导入到我们的容器里,因此 环境变量的 schema 验证 肯定不会通过。为了防止这种情况发生,我们必须在构建命令中添加 SKIP_ENV_VALIDATION=1 标识,以便 env-schema 不会在编译时被验证。

点击这里并将下方内容添加到 Dockerfile 里:
##### DEPENDENCIES

FROM --platform=linux/amd64 node:16-apline3.17 AS deps
RUN apk add --no-cache libc6-compat openssl
WORKDIR /app

# 安装 Prisma 客户端 - 如果不需要 Prisma,移除下一行

COPY prisma ./

# 使用 PNPM 包管理工具安装依赖包

COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./

RUN \
 if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
 elif [ -f package-lock.json ]; then npm ci; \
 elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \
 else echo "Lockfile not found." && exit 1; \
 fi

##### BUILDER

FROM --platform=linux/amd64 node:16-apline3.17 AS builder
ARG DATABASE_URL
ARG NEXT_PUBLIC_CLIENTVAR
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# ENV NEXT_TELEMETRY_DISABLED 1

RUN \
 if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \
 elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \
 elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \
 else echo "Lockfile not found." && exit 1; \
 fi

##### RUNNER

FROM --platform=linux/amd64 node:16-apline3.17 AS runner
WORKDIR /app

ENV NODE_ENV production

# ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json

COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000
ENV PORT 3000

CMD ["node", "server.js"]

注意

  • 在迁移到 Node 18 后,可能就没有必要再使用 --platform=linux/amd64 了。
  • 参看 node:alpine 来了解为什么使用 libc6-compat
  • Next.js 会收集 关于使用情况的匿名观测数据。取消注释第一个 ENV NEXT_TELEMETRY_DISABLED 1,以在构建过程中关闭该观测功能。取消注释第二个 ENV NEXT_TELEMETRY_DISABLED 1 以关闭在运行时的观测功能。

在本地构建和运行镜像

通过下方命令在本地构建和运行该镜像:

docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar .
docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker

在浏览器中访问 localhost:3000 来查看运行的应用。

Docker Compose

你也可以使用 Docker Compose 来创建镜像以运行容器。

遵循以上 1 - 4 的步骤,点击这里,将下方内容添加到 docker-compose.yml 里:
version: "3.9"
services:
  app:
    platform: "linux/amd64"
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NEXT_PUBLIC_CLIENTVAR: "clientvar"
    working_dir: /app
    ports:
      - "3000:3000"
    image: t3-app
    environment:
      - DATABASE_URL=database_url_goes_here

命令行运行 docker compose up

docker compose up

在浏览器中访问 localhost:3000 来查看运行的应用。

部署到 Railway

你可以使用 PaaS(平台即服务)工具,例如 Railway’s,自动完成 Dockerfile 部署 来部署你的应用。如果你已经 安装了 Railway CLI 命令行工具,你可以使用下方命令来部署你的应用:

railway login
railway init
railway link
railway up
railway open

前往 “Variables”,填入你的 DATABASE_URL。然后前往 “Settings” 里,选择 “Generate Domain”。访问 ct3a-docker.up.railway.app 来查看托管在 Railway 上的项目。

有用的资源

资源链接
Dockerfile 参考手册https://docs.docker.com/engine/reference/builder/
Compose file 第三版参考手册https://docs.docker.com/compose/compose-file/compose-file-v3/
Docker CLI 命令行工具参考手册https://docs.docker.com/engine/reference/commandline/docker/
Docker Compose CLI 命令行工具参考手册https://docs.docker.com/compose/reference/
使用 Docker 镜像 部署 Next.jshttps://nextjs.org/docs/deployment#docker-image
Docker 中的 Next.jshttps://benmarte.com/blog/nextjs-in-docker/
Next.js 搭配 Docker 示例https://github.com/vercel/next.js/tree/canary/examples/with-docker
创建一个 Next.js 应用的 Docker 镜像https://blog.tericcabrel.com/create-docker-image-nextjs-application/