让 Vue 项目的 Docker 镜像支持环境变量

背景

创业项目是前后端分离的,前端用 Vue,后端用 Laravel,分别打包成镜像,单独部署。因为学校用我们的服务时,都是不同的访问入口,即通过不同的域名进入系统,而不是在进入系统前要手动选择学校。

为了不在每个接口上都加上「学校」这个参数,我想到一个比较「优雅」的解决方案:每个学校的前端单独部署,通过设置 Docker 环境变量加上「学校」参数,在前端代码统一封装的 API 请求方法中,在 Header 增加该参数。

难点

为了尽可能地轻量化前端镜像,前端镜像是把 Vue 项目编译得到的产物拷贝到 nginx:alpine 镜像中,所以没有办法很优雅地让 Vue 项目读取容器运行的环境变量。

而且编译后的 JS 文件是直接在客户端运行,所以也就和容器环境变量没了关系。

解决方案

解决方案分为三步:

  1. 前端项目添加一个 env.js 文件,用于存储一些必要的环境变量,比如后端 API 地址还有本次要解决的「学校」参数问题
  2. 修改前端项目的 index.html,加上 <script src="<%= BASE_URL %>env.js"></script>,引入环境变量
  3. 修改 Dockerfile 文件,添加 entrypoint.sh ,在容器运行时用 sed 命令替换 env.js 中的环境变量值

第三步有点抽象,给个代码会比较直观。

1
2
3
4
5
6
7
8
#!/bin/bash
# entrypoint.sh 代码示例

set -e

sed -i "s/xxxx_token: 'XxXxToKeN'/xxxx_token: '$XXXX_TOKEN'/1" /usr/share/nginx/html/env.js

exec "$@"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Dockerfile 示例
FROM nginx:alpine

ARG DIST_PATH=dist

COPY docker/conf/default.conf /etc/nginx/conf.d/default.conf
COPY $DIST_PATH /usr/share/nginx/html

RUN chmod 777 /usr/share/nginx/html -R

ENV XXXX_TOKEN XxXxToKeN

COPY docker/entrypoint.sh /
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

运行时,主动设置 XXXX_TOKEN 环境变量即可。

参考资料

  • How to implement runtime environment variables with create-react-app, Docker, and Nginx
  • Dockerize and configure a JavaScript single-page application