使用docker管理多个测试环境并存的场景

968 阅读2分钟

示例地址

问题和目标

在项目的进行过程中经常会出现多个分支并行开发的情况,这种时候就需要多个测试环境并存。通常有两个解决方案,一个是申请多台机器进行部署;另一个是各自在部署的时候手动修改端口号,添加 nginx 配置等。但是这样既不便于管理,也不便于接入 CI 自动构建。

我们最终的目标是让多个环境并存在一台机器的同一个端口上,通过访问路径来区分不同的环境。并且最好能够在创建新分支后不需要手动修改代码中的配置文件,而是可以通过自动化的方式创建和配置新的环境。

实现思路

基本实现思路就是在机器内部创建一个网桥网络,让各个独立测试环境以不同的 ip 接入虚拟网络,最后暴露一个统一的 nginx 将请求转发到各个环境。

实现思路

实现

为了使不同项目之间不会相互干扰,我们需要为每个项目指定一个名字,后续创建的网络、容器、地址都会加上项目名作为前缀。

1. 网桥网络创建

首先创建一个网桥网络,并创建一个 nginx 的实例来访问这个网络。

docker network create docker-example
docker run -itd \
    -v ./nginx.conf:/etc/nginx/nginx.conf -v ./default.conf:/etc/nginx/conf.d/default.conf \
    --net docker-example \
    -p 80:80 \
    --name docker-example-nginx nginx:alpine

这个 nginx 需要根据请求的路径来将请求转发到各个测试环境的前端 nginx 上,关键配置如下,它会将 http://localhost/master/ 请求转发到 http://web-master/master

location ~ ^/([^./]+) {
    proxy_pass http://web-$1;
}

2. 服务容器化

为项目中的各个服务编写 Dockerfile,其中有几个细节问题。

  • 由于最终会在访问路径中加上分支的名字,因此诸如 api 地址,publicPath 这类的配置需要通过环境变量传入。例如前端请求的 baseURL:
const { BASE_API } = process.env

const service = axios.create({
  baseURL: BASE_API,
  timeout: 5000,
  xsrfCookieName: 'csrfToken',
  xsrfHeaderName: 'x-csrf-token',
})
  • 由于现在前端多为打包为静态文件,因此需要一个 node 容器用于打包,一个 nginx 容器来提供静态服务,两个容器通过共享卷来共享文件。
build-web:
  # 其他配置略
  volumes:
    - web:/web/dist
web:
  # 其他配置略
  image: nginx:alpine
  expose:
    - 80
  volumes:
    - web:/usr/share/nginx/web/${BRANCH}
  • 前端 nginx 配置中有一部分需要根据环境变量不同而不同,而 nginx 不能接受环境变量。可以先按照如下格式编写,保存为一个 template 文件。
location /${BRANCH}/ {
    root /usr/share/nginx/web;
}

location /${BRANCH}/api {
    proxy_pass http://server-${BRANCH}:7000;
}

然后通过 envsubst 命令填充变量

envsubst < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf
nginx-debug -g "daemon off;"

3. 接入网桥网络

docker-compose.yml

通过 docker compose 将启动各服务并接入到虚拟网络中,在这个过程中容器名、网络名、域名都是根据项目名和分支名生成的,以后台服务为例:

server:
  container_name: ${NAME}-server-${BRANCH}
  hostname: server-${BRANCH}
  networks:
    default:
      aliases:
        - server-${BRANCH}

4. 启动

最后编写一个脚本来启动项目。

启动一个独立的测试环境,通过 -n 参数传入项目名,通过 -b 参数传入独立测试环境的名字

./testenv/docker.sh -n example -b master start

等待构建完成,通过 http://localhost:8080/master/ 访问测试页面

切换到 feature 分支再启动一个独立的测试环境

git checkout feature
./testenv/docker.sh -n example -b feature start

通过 http://localhost:8080/feature/ 访问另一个测试环境的页面