引言
NextJS 是一个构建于 NodeJS
之上的一个 Web
开发框架。它基于 React
特性进行了一些列的扩展!! 在社区中也很是火热, 前段时间 「昆仑虚」 也终于完成了项目的迁移(React
=> NexJS
)!!
那么接下来就是项目部署, NextJS
相比常规的前端部署还是有所区别的:
- 常规的前端项目只是包含一些静态的资源文件, 只要起一个
Web
服务器, 然后把资源丢上去就行, 比如:Nginx
- 但是对于
NextJS
项目是包含后端(Node
)服务的, 当然官网CI
工具其实也提供了相关CI
命令, 正常只需要Build
好后, 执行start
命令即可, 但是我个人更希望能够使用pm2
来运行, 基于pm2
可以方便管理服务进程以及自带一套日志系统, 同时还希望能够通过Docker
的方式来进行部署
NextJS
项目地址(欢迎Star
): KunLunXu-CC/websiteNginx
配置: docker/nginx/nginx.conf
一、架构
改造后 「昆仑虚」 整体部署希望如上图所示:
- 所有服务(包括
Nginx
) 都是一个Docker
容器 Nginx
在中间只作为反向代理来使用- 当用户访问前端页面或后端接口时, 通过
Nginx
方向代理到对应的服务
当然本文只讲解 NextJS
服务部署的部分, 与此同时最后还会给出 Nginx
反向代理的配置, 至于后端服务的部署则不做讲解, 而且前后端服务都是独立的, 确失后端部分也是不影响 NextJS
部署流程…
二、修改 NextJS 配置文件
修改 NextJS
配置文件, 确保在 Build
时输出模式为 standalone
, 关于 output
配置具体信息可查阅 next-config-js/output
// next.config.mjs
const nextConfig = {
+ output: 'standalone',
};
export default nextConfig;
三、Dockerfile 编写
这里我是参考了官方的一个 Demo, 基于官方模版做了简单的调整
项目根目录下创建 dockerfile
配置文件, 配置内容如下, 具体的介绍看注释:
# 使用 amd64 架构的 node 镜像作为基础镜像
FROM --platform=linux/amd64 node AS base
# 新增用户 & 用户组
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# 安装 pm2 和 pnpm
RUN npm install --global pm2 pnpm
# ------------------------- deps -----------------------------------
FROM base AS deps
WORKDIR /app
# 拷贝 package.json 和 pnpm-lock.yaml 文件, 并安装相关依赖
COPY package.json pnpm-lock.yaml* .npmrc ./
RUN pnpm i --frozen-lockfile
# ------------------------- builder -----------------------------------
FROM base AS builder
WORKDIR /app
# 拷贝上一步安装的依赖
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# 构建项目
RUN pnpm run build
# ------------------------- runner -----------------------------------
FROM base AS runner
WORKDIR /app
# 创建 ecosystem.config.js 文件, 用于 pm2 启动不想直接在项目里创建
COPY <<EOF /app/ecosystem.config.js
module.exports = {
apps: [
{
name: 'klx-website',
script: 'node server.js',
watch: false,
},
],
};
EOF
# 创建相关目录, 并设置权限
RUN mkdir pm2
RUN mkdir .next
ENV PM2_HOME /app/pm2
RUN chown nextjs:nodejs pm2
RUN chown nextjs:nodejs .next
# 复制运行服务所需要的产物, see: https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder /app/public ./public
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 HOSTNAME="0.0.0.0"
# 运行容器时, 启动 pm2
CMD pm2 start ecosystem.config.js --no-daemon
下面我们尝试构建下镜像, 如下命令所示:
# 后面一个 . 别漏了
docker build -t website:4.2.4 .
在构建时这里遇到一个坑, 就是我的机器是
M2
芯片(ARM
), 导致Build
出来的镜像在Ubuntu
上无法运行, 这里解决方案有挺多的, 具体可以看 Forcing docker to use linux/amd64 platform by default on macOS
- 在
Build
时通过--platform linux/amd64
参数指定平台, 例:docker build --platform linux/amd64 -t website:4.2.4
- 通过环境变量来修改
export DOCKER_DEFAULT_PLATFORM=linux/amd64
- 在
Dockerfile
中, 在指定form
时进行设置(本文使用的方案), 例:FROM --platform=linux/amd64 node
- 在
Docker compose
中, 通过platform: linux/amd64
字段来设置
四、阿里云私人镜像仓库
这里还需要整个私人的镜像仓库, 构建好的镜像需要上传到上面, 然后在
docker compose
中进行引用。我这里使用的是阿里的服务, 下面将简单演示下:
- 在阿里云搜索「镜像容器服务」
- 我这里用的是免费版的
- 先创建一个命令空间
- 创建镜像仓库
- 这里我选择本地仓库
- 创建完成后, 进入详情页会有具体的仓库信息, 以及相关使用介绍(怎么登录、怎么推送、怎么拉取…)
五、上传镜像
大部分命令上面最后一张图其实已经给出了, 下面只是走个流程
- 重新构建镜像
docker build -t registry.cn-hangzhou.aliyuncs.com/kunlunxu/website:4.2.4 .
- 登录阿里云
Docker Registry
# username 换成自己的用户名
docker login --username=13*********@163.com registry.cn-hangzhou.aliyuncs.com
- 镜像推送
docker push registry.cn-hangzhou.aliyuncs.com/kunlunxu/website:4.2.4
- 推送完成, 在阿里云镜像版本列表中, 就能够看到推送的镜像了
六、Docker compose 编写
这里我希望通过 Docker compose
来管理我的容器(重启啥的比较方便)
项目根目录下创建 docker-compose.yml
配置文件, 具体内容如下, 相关描述看注释
version: '3'
services:
website:
image: registry.cn-hangzhou.aliyuncs.com/kunlunxu/website:4.2.4 # 镜像地址
container_name: klx-website # 容器名称
ports:
- 3000:3000 # 端口映射
environment:
- TZ=Asia/Shanghai # 时区
logging: # 日志配置
driver: "json-file"
options:
max-size: "100k"
max-file: "20"
七、部署 NexJS 项目
-
登录你的服务器, 拉取最新的代码! 这一步取决于你自己的部署方式, 我这里是直接在服务器把仓库代码给拉下来了, 当然你也可以直接在服务器创建一个
Docker compose
配文件) -
执行
Docker compose
命令启动项目即可
docker compose up -d
- 运行成功
八、Nginx 配置
下面是 Nginx
方向代理相关的一些配置, http://172.17.8.146
是我服务器内网地址
# HTTPS 设置
server {
listen 443 ssl;
server_name www.kunlunxu.cc;#换成你的域名
ssl_certificate /etc/nginx/ssl.pem;#证书文件
ssl_certificate_key /etc/nginx/ssl.key;#秘钥文件
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
# 前端代理配置
location / {
proxy_pass http://172.17.8.146:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# API 代理配置
location /api/ {
proxy_pass http://172.17.8.146:4000; # 代理到 API 服务器
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
}
九、TODO
到处 NextJS
部署流程大体就是如此了, 但是这里的一切都是需要手动参与的, 包括:
Build
镜像前, 需要手动修改.env.local
中的必要的环境变量- 需要手动在本地
Build
Build
完成后需要手动修改docker-compose.yml
上镜像版本号, 并提交代码- 最后还需要登录到服务器, 拉取最新代码后, 拉取最新镜像后, 停止并删除相关镜像后, 重启服务
总之一切都是那么的麻烦, 后续则需要将这些流程全部简化掉…
十、参考
- 强制 docker 在 macOS 上默认使用 linux/amd64 平台
- building-your-application/deploying#docker-image
- Set up a CDN
- Serving assets and images in Next.js from a CDN without Vercel
- Deploying a Next.js application on an EC2 instance with PM2 and Nginx