背景
Next.js有很多种部署方式,在最近的项目中,我选择了如下部署方案:
第一步使用Docker通过Node server部署Next.js应用,第二步使用Docker部署Nginx作为反向代理,转发请求并安装SSL证书支持https访问。
部署步骤
1. 环境准备
我这套部署方案基于CentOS7,需要的软件和前期准备工作如下:
2. 制作镜像
需要制作两份镜像,一个是Next.js应用的镜像,另一个是Nginx镜像。涉及到的Dockerfile如下:
Next.js Dockerfile:
# syntax=docker.io/docker/dockerfile:1
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json pnpm-lock.yaml* ./
RUN corepack enable pnpm && pnpm i --frozen-lockfile
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED=1
RUN corepack enable pnpm && pnpm run build;
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
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
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
Nginx Dockerfile:
FROM nginx:1.27.3
EXPOSE 80
EXPOSE 443
3. Nginx配置
Nginx.conf:
events {
}
http {
upstream nextjs {
# in Docker Compose, services run in separate containers and need to reference each other by their service names
server nextjs:3000;
}
server {
listen 80;
listen [::]:80;
server_name easynomad.cn www.easynomad.cn;
#80跳转到443
rewrite ^(.*)$ https://${server_name}$1 permanent;
}
server {
listen 443 ssl;
server_name easynomad.cn www.easynomad.cn;
ssl_certificate /etc/nginx/ssl/easynomad.cn/full.pem;
ssl_certificate_key /etc/nginx/ssl/easynomad.cn/key.pem;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:10m;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers on;
#开启HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location /api/ {
proxy_pass 后端接口地址;
}
location / {
proxy_pass http://nextjs;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
4. Docker Compose配置
services:
nextjs:
image: nextjs:latest
container_name: nextjs
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
ports:
- "3000:3000"
restart: always
nginx:
image: nginx:latest
container_name: nginx
labels:
- sh.acme.autoload.domain=easynomad.cn
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
ports:
- "80:80"
- "443:443"
restart: always
5. 完整的部署脚本
部署脚本的整体流程如下,每次只需要在服务器直接运行脚本就可以完成部署:
- 从github获取最新代码
- 分别打包Next.js和Nginx镜像
- 通过docker compose运行容器
- 更新Nginx配置
- 安装SSL证书
#!/bin/sh
# step 1: download code from github
rm -rf easynomad-frontend
echo ">>>> original code dir deleted"
git clone https://${GIT_TOKEN}@github.com/xx/xxxx.git
cd easynomad-frontend
# step 2: build images
docker build --tag nextjs:latest -f Dockerfile .
echo ">>>> build nextjs image finished"
docker build --tag nginx:latest -f ./src/nginx/Dockerfile .
echo ">>>> build nginx image finished"
# step 3: 运行容器
docker compose down
docker compose up -d
# step 4: 更新nginx配置,使其覆盖原有配置
sleep 6s
# /etc/nginx/nginx.conf是主要配置文件,/etc/nginx/conf.d/nginx.conf是副配置文件
docker cp ./src/nginx/nginx.conf nginx:/etc/nginx/nginx.conf
echo ">>>> nginx.conf updated!"
# step 5:安装SSL证书
# The label value to find the container
export DEPLOY_DOCKER_CONTAINER_LABEL=sh.acme.autoload.domain=easynomad.cn
# The target file path in the container.
# The files will be copied to the position in the container.
export DEPLOY_DOCKER_CONTAINER_KEY_FILE="/etc/nginx/ssl/easynomad.cn/key.pem"
export DEPLOY_DOCKER_CONTAINER_CERT_FILE="/etc/nginx/ssl/easynomad.cn/cert.pem"
export DEPLOY_DOCKER_CONTAINER_CA_FILE="/etc/nginx/ssl/easynomad.cn/ca.pem"
export DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/etc/nginx/ssl/easynomad.cn/full.pem"
# The command to reload the service in the container.
export DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"
sleep 2s
/root/.acme.sh/acme.sh --deploy --deploy-hook docker -d easynomad.cn
echo ">>>> SSL cert deployed!"
最后,这个项目是我独立开发的一个远程工作聚合网站,对远程工作感兴趣的小伙伴可以进来看看:轻松游牧