背景
现在公司内使用的是K8S进行服务部署,内网测试环境也提供了K8S环境。团队内多人同时开发多个需求,必然会出现多个需求同时测试的情况。而部署到内网K8S测试环境,一次只能部署一个服务。如果将多个需求分支合并到一个分支上去,势必会相互影响,而且使用公司的TDC服务每次更新都要人工处理,效率偏低。为了解决这个问题,我在公司内网物理机上,针对静态网站和直出网站分别搭建了多分支的测试环境,并配合Gitlab CI自行自动化部署,减少人工部署耗时。
实践路线
目前手上有两个项目,一个是正常的静态网站,一个是服务端直出的网站。它们在部署上是有不同的,静态网站我们开发完打包好静态文件部署到服务器上配置好Nginx访问指定目录访问就行了。而服务端直出的项目则需要启动一个服务,并配置Nginx将请求转发到对应的服务上。
因此针对上述情况我们需要事情如下:
1.根据不同的分支输出对应的静态文件或者启动不同的服务;
2.通过配置Nginx规则来实现通过访问URL上的信息将请求转发到对应的文件或服务;
3.配置Gitlab CI并编写自动化脚本进行自动部署。
如何区分不同分支
静态网站
针对静态网站,我们最终是将其部署在服务器上的某个目录中,通过Nginx转发进行访问。如果我们能够将不同分支打包到不同的目录下,并通过·Nginx配置进行访问就能实现访问。
一个项目中每个人开发需求都会取一个唯一的分支名,而在Gitlab执行CI时会提供CI_COMMIT_REF_SLUG这个字段,这个字段的值是分支名称的值全转成小写并将除0-9和a-z的其他字符转换成-后获得的,可以用在URL中。
这样我们只需在执行自动化脚本时,将CI_COMMIT_REF_SLUG当做参数传入打包脚本,在指定目录下新建对应名称的目录,并将打包文件通过过去变算是完成第一阶段的工作了。
服务端直出网站
服务端渲染的网站需要启动服务去吐出页面,因此我们的Nginx配置也是指向对应的服务。问题是项目的端口号是固定的。如果我们每个分支都人工修改项目端口号,一则可能上线时会忘记更改回来;二则还要人工确认对应端口是否被占用。显然是不合理的。
刚好公司项目都上了K8S,都是用Docker进行部署。Docker的特性里有一条便是将容器内的端口暴露给外部,利用这个特性,我们可以在不改变项目启动端口的情况下,对外暴露不同的访问端口了:
docker run --name $CI_COMMIT_REF_SLUG -P -d <image>:$CI_COMMIT_REF_SLUG |
这里我们关注参数-P,这代表随机指定一个系统端口给当前容器。
但是我们在测试时是需要修改bug的,每次修改完提交重新部署,肯定是希望能够维持已有端口而不是每次提交都变动。那我们可以每次重启服务的时候,先去获取已有服务的端口号:
HOSTPORT=$(docker inspect --format='{{(index (index .NetworkSettings.Ports "3000/tcp") 0).HostPort}}' $CI_COMMIT_REF_SLUG) |
这样一来不同分支启动服务暴露不同端口的问题也就解决了。
Nginx规则配置
静态网站
平时我们配置Nginx的server_name的时候,一般都是指定对应域名。但我们是可以在里面使用正则去匹配子域名的:
server { listen 80; server_name ~^(.+)?\.static\.test\.com$; set $www_root $1; root /data/vhosts/static.test.com/$www_root/; } |
以上是我静态项目的Nginx配置示例。我的静态网站在测试环境最终访问的链接是:
http://$CI_COMMIT_REF_SLUG.static.test.com |
根据上面的正则表达式,Nginx在接到请求时,拿到了我们的分支别名后,将请求转发到项目目录下的指定分支目录了。
服务端直出网站
服务端直出网站的配置和静态网站的思路是一样的,只是把访问目录改成了访问服务而已:
server { listen 80; server_name ~^(.+)?.server.test.com$; root html; ... set $port $1; location ~ / { proxy_pass http://127.0.0.1:$port; ... } } |
最终的访问地址是:
http://$port.server.test.com |
whistle规则
因为实际生产中两个项目都是在客户端内部打开的,所以其实在测试时给出的两个网址和客户端配置的并不相同。这里可以用whistle配置规则进行转换,而且还能够支持HTTPS,非常的方便:
https://static.test.com http://$CI_COMMIT_REF_SLUG.static.test.com https://server.test.com http://$port.server.test.com |
甚至使用了whistle之后服务端直出网站都可以不用配置Nginx,直接通过IP访问对应的服务就可以了:
https://server.test.com http://ip:$port |
部署脚本
静态网站
静态网站部署脚本相对简单,只需对代码进行打包,然后同步到对应的文件夹即可:
#!/bin/sh CI_COMMIT_REF_SLUG="$1" BUILD_COMMAND="yarn install --registry https://registry.npm.taobao.org; yarn build;" TARGET_DIR="/data/vhosts/static.test.com/$CI_COMMIT_REF_SLUG/" # 启动docker打包文件 docker run -v $(pwd)/:/app -w /app node:10 /bin/sh -c "$BUILD_COMMAND" || exit 1 # 创建指定目录,-p表示:递归创建目录,如果目录已存在也不会报错 mkdir -p $TARGET_DIR # 同步打包后文件到对应目录 rsync -av --delete-after ./dist/ $TARGET_DIR |
服务端直出网站
服务端直出网站部署脚本相对复杂,主要体现在更新时对容器的处理上:
#!/bin/sh BUILD_COMMAND="yarn install --registry https://registry.npm.taobao.org; yarn build;" # 执行脚本时后面传入的第一个参数 CI_COMMIT_REF_SLUG="$1" # 编译打包项目 docker run -v $(pwd)/:/app -w /app node:10 /bin/sh -c "$BUILD_COMMAND" || exit 1 # 将项目打包到docker镜像,镜像名称带上CI_COMMIT_REF_SLUG # 运行此命令需要项目根路径有Dockerfile文件,文件比较简单,这里就不列出了 docker build -t game-frontend:$CI_COMMIT_REF_SLUG . # 判断是否有当前分支对应的容器在运行 if [ ! "$(docker ps -q -f name=$CI_COMMIT_REF_SLUG)" ]; then # 如果有容器但已退出 if [ "$(docker ps -aq -f status=exited -f name=$CI_COMMIT_REF_SLUG)" ]; then # 删除容器 docker rm $CI_COMMIT_REF_SLUG fi # 利用刚刚构建的镜像启动一个名为CI_COMMIT_REF_SLUG的容器 docker run --name $CI_COMMIT_REF_SLUG -P -d game-frontend:$CI_COMMIT_REF_SLUG # 获取容器端口号,输出出来,方便查看 HOSTPORT=$(docker inspect --format='{{(index (index .NetworkSettings.Ports "3000/tcp") 0).HostPort}}' $CI_COMMIT_REF_SLUG) echo "端口号:$HOSTPORT" # 如果已有容器,则获取端口号 HOSTPORT=$(docker inspect --format='{{(index (index .NetworkSettings.Ports "3000/tcp") 0).HostPort}}' $CI_COMMIT_REF_SLUG) echo "端口号:$HOSTPORT" # 停止容器,并删除 docker stop $CI_COMMIT_REF_SLUG docker rm $CI_COMMIT_REF_SLUG # 根据之前端口号启动新的容器 docker run --name $CI_COMMIT_REF_SLUG -p $HOSTPORT:3000 -d game-frontend:$CI_COMMIT_REF_SLUG fi |
配置自动化部署
完成了上面的准备工作之后,就可以配置Gitlab CI了。
注册Gitlab Runner
我们在Gitlab项目页中找到setting下面的CI/CD,按照指引安装Gitlab Runner即可。这里不多说了。
编写.gitlab-ci.yml
cache: untracked: true stages: - dev before_script: - git lfs pull dev: stage: dev script: # 这里就是上面编写的脚本 - chmod +x ./scripts/test_dev.sh - ls -lsa ./scripts/test_dev.sh - ./scripts/test_dev.sh $CI_COMMIT_REF_SLUG tags: # 指定要使用的Gitlab runner - test-dev |
至此,自动化部署的多分支测试环境就配置完成了。
遇到的问题
1.在执行部署脚本时Gitlab runner需要去它的工作目录以外的地方创建目录,这时候出现了权限问题。
因为是测试机,我直接将**/etc/passwd**中的gitlab-runner对于应的第三个参数user id修改为了0(root账号)便可以了:
gitlab-runner:x:0:0:GitLab Runner:/home/gitlab-runner:/bin/bash |
2.执行脚本拉取公司docker仓库镜像时报权限问题,但是我机器上明明已经登录过了。这里是因为gitlab runner读取的是/home/gitlab-runner/.docker/下的账号配置,我们需要在ci中重新登录docker。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理