容器化部署 Next.js 和 Nest.js 应用
本文介绍了使用容器部署 Next.js 和 Nest.js 应用的方法
2023-10-29
如果使用最基础的方式部署 Next.js 和 Nest.js 应用,会导致镜像体积过大进而影响到镜像构建的效率。
通过将 Next.js 和 Nest.js 应用打包、移除无关依赖并使用体积更小的基础镜像作为运行环境,能有效地减少镜像体积,提高镜像构建效率。
Next.js 应用的镜像文件
Next.js@12 提供了一个 模式,通过在 standalone
next.config.js
next build
首先修改
next.config.js
const nextConfig = {
// ...
output: "standalone",
// ...
}
module.exports = nextConfig
next.config.js
如果有使用
next/image
sharp
npm_config_sharp_binary_host="https://registry.npmmirror.com/-/binary/sharp" \
npm_config_sharp_libvips_binary_host="https://registry.npmmirror.com/-/binary/sharp-libvips" \
npm install sharp --registry=https://registry.npmmirror.com
添加一个编译脚本:
#!/bin/sh
npx next build
cp -r public .next/standalone/public # 复制 public 文件夹
cp -r .next/static .next/standalone/.next/static # 复制 static 文件夹
cp .env .next/standalone/.env # 复制环境配置文件
.scripts/build.sh
如果应用是 MonoRepo 的一部分,需要配置 ,并调整 outputFileTracingRoot
.scripts/build.sh
npx next build
cp -r public .next/standalone/<root/packages/app>/public
cp -r .artifacts .next/standalone/<root/packages/app>/.artifacts
cp -r .next/standalone/node_modules .next/standalone/<root/packages/app>/node_modules
cp -r .next/static .next/standalone/<root/packages/app>/.next/static
cp .env .next/standalone/<root/packages/app>/.env
.scripts/build.sh
在项目根目录添加
Dockerfile
# 使用标准的基础镜像用于编译
FROM node:18 AS builder
ARG ENV
WORKDIR /app
COPY package.json package-lock.json /app # 或者 yarn.lock
RUN --mount=type=cache,target=/root/.npm,id=npm_cache,sharing=locked \
npm install --registry="https://registry.npmmirror.com"
COPY . /app
COPY .artifacts/.env.${ENV} /app/.env # 如果有 build time 配置,需要复制 .env 文件
RUN bash .scripts/build.sh # 执行编译
# 使用 alpine 镜像作为 Runtime 镜像
FROM node:18-alpine
EXPOSE 3000
ENV PORT=3000
WORKDIR /app
RUN --mount=type=cache,target=/tmp/dist,from=builder,source=/app/.next \
cp -r /tmp/dist/standalone/. /app
CMD ["node", "server.js"]
Dockerfile
编译镜像:
docker build . -t next_app --build-arg ENV=staging
镜像大小:
Nest.js 应用的镜像文件
虽然 Nest.js 官方并没有提供类似 Next.js 的
standalone
@vercel/ncc
ncc
首先在 Nest.js 应用中添加
@vercel/ncc
npm install -D @vercel/ncc
添加编译脚本:
npx ncc build src/main.ts -o dist
cp .env dist/.env
.scripts/build.sh
在应用根目录添加如下的 Dockerfile:
FROM node:18 as builder
WORKDIR /app
ARG ENV
COPY package.json package-lock.json /app
RUN --mount=type=cache,target=/root/.npm,id=npm_cache \
npm install --registry="https://registry.npmmirror.com"
COPY . /app
COPY .artifacts/.env.api.${ENV} /app/packages/api/.env
# build references if necessary
RUN bash .scripts/build.sh
FROM node:18-alpine
EXPOSE 3000
WORKDIR /app
RUN --mount=type=cache,target=/tmp/dist,from=builder,source=/app/dist \
cp -r /tmp/dist/. /app
CMD ["node", "index.js"]
Dockerfile
编译镜像:
docker build . -t nest_app --build-arg ENV=staging
镜像大小:
如果 Nest.js 应用是 MonoRepo,则在执行
ncc build
lerna run build --scope ... --parallel
如果在应用中使用了 TypeORM,并且遇到
no metadata for entity was found
entities
autoLoadEntities
true
@Module({
imports: [
// ...
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (cs: ConfigService) => {
const config = {
type: cs.get("DB_TYPE"),
host: cs.get("DB_HOST"),
port: cs.get<number>("DB_PORT"),
username: cs.get("DB_USERNAME"),
password: cs.get("DB_PASSWORD"),
database: cs.get("DB_NAME"),
// 自动加载 Entites
autoLoadEntities: true,
// 指定定义 Entity 的文件夹相对 src 的路径,或将 Entity Class 逐个加入其中
entities: ["schema/*{.ts,.js}"],
synchronize: true,
} as any;
return config;
},
inject: [ConfigService],
}),
// ...
]
}
export class AppModule {}
src/app.module.ts