背景
Next.js有很多种部署方式,在最近的项目中,我选择了如下部署方案:
第一步使用Docker通过Node server部署Next.js应用,第二步使用Docker部署Nginx作为反向代理,转发请求并安装SSL证书支持https访问。
部署步骤
1. 环境准备
我这套部署方案基于CentOS7,需要的软件和前期准备工作如下:
- docker:容器化部署。1. 安装方式
- git:每次部署前先从github拉取最新代码。1. 安装方式
- acme.sh:签发和部署SSL证书以支持HTTPS访问。1. 安装方式,2. SSL证书签发
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!"
复制
最后,这个项目是我独立开发的一个远程工作聚合网站,对远程工作感兴趣的小伙伴可以进来看看:轻松游牧