一.基础环境准备
需要准备基本的node环境和docker ,可以通过下面的命令检查。
检查node环境:
node -v
检查docker是否安装:
docker version
二、nextjs的部署方式
可以查看nextjs官方文档,nextjs 有多种部署方式,最简单的是使用vercel部署。但是,如果您希望通过 AWS、Google Cloud 或其他云提供商运行您的应用,则可以使用docker部署。
- Vercel 平台部署(最简单)
- Docker 容器化部署(更灵活,适合自托管)
- 静态导出部署
- 传统服务器部署
本文重点介绍 Docker 部署方案。
三、添加配置
使用docker部署,我们需要先添加一下nextjs的配置,在nextj.config.js文件中,添加下面的配置
// next.config.js
module.exports = {
// ... rest of the configuration.
output: "standalone",
};
这个配置主要用于优化生产环境部署,特别是在 Docker 容器化场景中。他会生成一个独立部署包,创建一个完全独立的生产构建,包含所有必需的依赖和文件。
四、编写Dockerfile
对于Dockerfile文件的编写,我们不需要自己来完成,可以直接使用nextjs的案例来修改:
# syntax=docker.io/docker/dockerfile:1
# 可以适当提高一下版本,这里我把node18修改为了20
FROM node:20-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 yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# 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 \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
# 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"]
这里部署使用的是多阶段部署,另外添加了用户组和用户,除了可以适当修改一下node版本,其他不用修改。
五、本地运行测试
进入你的项目目录,运行构建镜像
docker build -t nextjs-docker .
然后测试运行
docker run -p 3000:3000 nextjs-docker
页面正常运行。但是这样只是运行了一个前端页面,如果链接数据库和api,可以会出现问题,所以我们需要使用docker compose来进行多服务器部署。
六、使用docker-compose部署
在本地我后端使用的是go和postgresq运行的,扫描我本地的资料文件夹,然后写入数据库,提供给前端数据。这里设置后端的内容我不展开,我的docker compose文件是这样的,我们只关注nextjs服务。
services:
postgres:
image: postgres:14-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=dbname
ports:
- "5432:5432"
volumes:
- data-volume:/var/lib/postgresql/data
- ./backend/files.sql:/docker-entrypoint-initdb.d/files.sql
api:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- DB_SOURCE=postgresql://user:password@postgres:5432/dbname?sslmode=disable
depends_on:
- postgres
restart: unless-stopped
entrypoint:
[
"/app/wait-for.sh",
"postgres:5432",
"--",
"/app/start.sh"
]
command: [ "/app/main" ]
nextjs:
build:
context: ./frontend # 指向 Next.js 项目的目录
dockerfile: Dockerfile # 确保该路径下存在 Dockerfile
ports:
- "3000:3000" # Next.js 默认端口
environment:
- NEXT_PUBLIC_API_URL=http://api:8080 # API 服务地址
depends_on:
- api # 确保 API 服务先启动
volumes:
data-volume:
networks:
default:
name: app-network
nexjts部分各个配置参考注释。关闭运行中的nextjs和后端接口。然后我们在docker compose 文件的目录,运行
docker compose up -d
当完成后,同样打开localhost:3000 ,可以看到同样的页面。现在本地运行正常后,我们把他部署到服务器上面。
七、上传到服务器上
使用git来管理前/后端的项目,当然你也可以使用git submodule一个仓库来管理.然后把修改上传到github上面。
git add .
git commit -m "init"
git push origin main
八、服务器部署
现在使用 ssh
登陆到你的服务器。你需要配置你的github账户在服务器上的权限,参考github文档。
一般直接在home目录clone,可能没有写入权限,可以先到tmp文件夹中clone后,然后移动到你的工作目录。
git clone ‘你前/后端github地址’
同样检查你的服务上的node和docker环境。和本地运行类似,运行
sudo docker compose up -d
等待服务构建。如果出现问题,可以检查服务状态和日志: 检查服务状态
sudo docker compose ps -a
检查日志,检查其中的api服务,最后10行日志:
sudo docker compose logs api --tail 10
九、配置域名
在服务器上构建的时候,我们先配置一个域名,进入你的域名管理机构,添加一个a解析到你服务器上。例如在cloudflare上:
修改前缀,并把1.1.1.1
替换为你的服务器地址。代理状态,可以暂时设置为仅dns,等我们申请完ssl证书后再打开。
十、nginx配置
针对刚刚解析的域名,我们配置一下nginx,这里只做几个主要的配置。进入到/etc/nginx/conf.d
,复制一下defult.conf 文件。
sudo cp default.conf you_domain.conf
然后修改一下里面的内容,主要是server_name ,和proxy_pass 这两部分。
server {
server_name 你的域名; # 修改为你的域名
listen 80;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
proxy_pass http://127.0.0.1:3000; # 转发到本地的 3000 端口
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
测试一下,访问你的域名,能正常访问到nginx默认页面(在docker compose 未运行的情况下)。
十、测试部署
现在docker compose 应该构建完成了,然后我们测试一下,访问你的域名,可以和本地一样,正常访问到你的页面,
我们的docker compose部署就完成了。
可以看到页面还会提示http 不安全,所以后续你需要配置一下免费ssl安全证书.
十一、申请免费的安全证书
这里申请免费安全证书不详细介绍,可以使用 certbot
或者acme.sh
来完成,可以参考我以前的文章,使用certbot申请免费安全证书或者使用acme.sh申请免费安全证书。
总结
本文介绍了使用 Docker 部署 Next.js 应用的完整流程。通过合理的配置和优化,可以构建一个高性能、安全且易于维护的生产环境部署方案。在实践中,根据具体项目需求调整配置参数,添加更多安全的配置,更多的性能优化,日志自动监控等设置,并持续关注性能监控和安全更新。