Next.js 部署与 devops
前言
需要将 Next.js 部署到云平台上,同一份镜像,通过环境变量来控制项目的 api 链接,实现CI/CD 的流程。云平台框架使用的是 rainbond。
Next.js 环境变量控制部署
一、 安装 cross-env
cross-env 用来设置环境变量,因为 win 和 linux 设置环境变量的方法不同,所以引入第三方库来方便设置
npm i --save cross-env
二、创建 config 目录
在根目录下创建 config 目录, 根据不同的 NODE_ENV 创建不同的文件夹, 底下新建不同的 dev ,test 文件。
dev 文件中,有 服务器地址等,ex : PORT=3000 …, test 文件也是
your-project
|- pages
|- public
+ |- config
+ |- development
+ |- dev
+ |- test
+ |- production
+ |- dev
+ |- test
dev 内容如下, ORIGIN ,NEXT_PUBLIC_ORIGIN 就是远程服务器的地址
HOST=xxxx
PORT=8888
ORIGIN=http://xxxx:8888
NEXT_PUBLIC_ORIGIN=http://xxxx:8888
三、 新建 env.js 文件
在根目录下新建 env.js 文件, .env.js 内容如下:
const fs = require('fs')
const path = require('path');
// 从环境变量中获取
const { NODE_ENV = 'production', RTE = 'test' } = process.env;
//封装 resolve
const resolve = (p) => path.join(__dirname, p);
/**
* 读取文件
* 文件格式内容为
* TEST=ABC
* @param {路径} p 路径
* @return [[TEST:ABC]]
*/
const readEnv = (p) => {
fs
.readFileSync(resolve(p))
.toString()
.trim()
.split('\n')
.filter((l) => l.indexOf('=') !== -1)
.map((v) => v.split('='));
}
// 读取./config/development/{RTE}中的文件,
//
fs.writeFileSync(
resolve(`./.env.${NODE_ENV}.local`),
readEnv(`./config/${NODE_ENV}/${RTE}`)
.map(([key, value]) => `${key}=${value}`)
.join('\n'),
);
四、 修改 package.json 文件
使用 cross-env 修改环境变量,然后 再运行 env.js 生成具体的配置文件,就会根据 /config/{NODE_ENV}/${RTE} 文件中的配置,设置环境变量
{
"scripts": {
"dev": "cross-env NODE_ENV='development' RTE='dev' ./env.js && next -p 3000",
"dev:test": "cross-env NODE_ENV='development' RTE='test' ./env.js && next -p 3000",
"debug": "cross-env NODE_OPTIONS='--inspect' next",
"lint": "eslint --ext .js,.jsx,.ts,.tsx ./",
"lint:fix": "yarn lint --fix",
"build": "next build --debug",
"start": "cross-env NODE_ENV='production' ./env.js && next start",
"pm2": "pm2 start ../../node_modules/next/dist/bin/next --name firmware-web -- start",
"stop": "pm2 stop firmware-web"
}
}
至此,就可以在本地,通过不同的环境变量,切换测试环境,开发环境,生成环境,下一步我们将部署到docker 容器中
部署到 docker 容器中
一、 编写 dockerfile 文件
FROM node:lts
WORKDIR /app
COPY package.json ./
COPY yarn.lock ./
RUN yarn config set unsate-perm true
RUN yarn install --frozen-lockfile
COPY . .
ENV NODE_ENV=production
ENV RTE=dev
RUN chmod -R a+x ./env.js
RUN yarn build
EXPOSE 3000
CMD [ "yarn","start" ]
二、 遇到的问题
- 前端 axios 无法获取到设置的环境变量,ORIGIN, NEXT_PUBLIC_ORIGIN 。
- 修改环境变量,无法正常的切换环境
三、 如何解决
前端 axios 无法获取到设置的环境变量,ORIGIN, NEXT_PUBLIC_ORIGIN 。因为 next.js 是服务端渲染框架,所以,会导致 .env.local 中的配置,前端可能无法及时的获取到,所以就要写一个动态 api ,
// /src/pages/api/envConfig.js const envConfig = async (req, res) => { const { ORIGIN, NEXT_PUBLIC_ORIGIN } = process.env; res.json({ ORIGIN, NEXT_PUBLIC_ORIGIN, }); }; export default envConfig;在 axios 拦截器中,每次请求之前都去获取这个环境变量,可以考虑使用缓存把这两个变量保存下来,使用 sessionStorage 是为了避免另一个环境获取同样的配置。
axios.interceptors.request.use( async (config) => { // 判断 localStorage中是否有 _env, 如果有,就直接 // 获取,不在发送请求获取 let env; if (sessionStorage.getItem('_env')) { env = JSON.parse(sessionStorage.getItem('_env')); } else { env = await io.get('/api/envConfig'); sessionStorage.setItem( '_env', JSON.stringify({ ORIGIN: env.ORIGIN, NEXT_PUBLIC_ORIGIN: env.NEXT_PUBLIC_ORIGIN, }), ); } config.baseURL = env.ORIGIN || env.NEXT_PUBLIC_ORIGIN; const token = localStorage.getItem('_token') ?? ''; if (token) { config.headers.Authorization = `JWT ${token}`; } return config; }, )修改环境变量,无法正常的切换环境。解决了第一个问题后,第二个问题就可以正常解决。
四、 编译成 docker 镜像
使用 docker build 命令,将项目构建成镜像
docker build -t app:1.0.0 .
使用 docker run命令, 运行镜像成容器
docker run -d -p 3000:3000 -e RTE='dev'
通过 RTE 环境变量来控制容器的环境