Next.js 环境变量控制部署

Next.js 部署与 devops

前言

需要将 Next.js 部署到云平台上,同一份镜像,通过环境变量来控制项目的 api 链接,实现CI/CD 的流程。云平台框架使用的是 rainbond

Next.js 环境变量控制部署

一、 安装 cross-env

cross-env 用来设置环境变量,因为 winlinux 设置环境变量的方法不同,所以引入第三方库来方便设置

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 内容如下, ORIGINNEXT_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
  • 修改环境变量,无法正常的切换环境

三、 如何解决

  1. 前端 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;
            },
        )
    
  2. 修改环境变量,无法正常的切换环境。解决了第一个问题后,第二个问题就可以正常解决。

四、 编译成 docker 镜像

使用 docker build 命令,将项目构建成镜像

 docker build -t app:1.0.0 .

使用 docker run命令, 运行镜像成容器

 docker run -d -p 3000:3000 -e RTE='dev' 

通过 RTE 环境变量来控制容器的环境


版权声明:本文为a790452097原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。