前段时间入职新公司,整体感觉不错。唯一不太帅的地方是公司配备的2015版15寸 MBP + 双2k屏,出于安全考虑不能使用自己电脑,导致我的2017版只能在家做个备机。
后来在开发过程中发现有一个略费劲的地方,就是部署环境,我们是使用强大的 Jenkins + GitLab ,基于分支开发,因为之前我是使用 GitHub + Travis + Netlify 工作流,那么可以找一些插件优化下目前的环境问题,再加上我们是前后端分离这样的天然优势,其实能做的事情很多。而我又是想到做到的理念,那就动手吧,但面临个问题: Jenkins + GitLab 运行耗费资源且本地安装麻烦 ,正好赶上最近两天在学习 Docker ,那就本地搞起吧。
git push
使用功能、服务:
feat/home-v2 npm run build ,成功后把产出目录( dist/ )按约定复制到指定目录 使用 Docker-compose 运行:
version: '3'
services:
jenkins:
container_name: 'jenkins.ci' # 镜像名称,为了统一使用 .ci 域名
image: 'jenkins/jenkins:lts'
# ports: 不对外暴露端口,使用 nginx 容器里直接转到了 jenkins:8080 即可
# - '8080:8080'
# - '50000:50000'
volumes: # 路径映射,html 主要是编译打包后需要存放的路径
- './html:/usr/share/jenkins/html'
- './jenkins/data:/var/jenkins_home'
gitlab:
container_name: 'gitlab.ci' # 镜像名称,为了统一使用 .ci 域名
image: 'gitlab/gitlab-ee:latest'
restart: always
hostname: 'gitlab.ci'
ports: # 只对外开发 22 ssh 端口,web 服务由 nginx 容器里直接转到了 gitlab.ci 即可,因为本身 GitLab 镜像对外就是80
- '22:22'
# - '80:80'
# - '443:443'
volumes:
- './gitlab/config:/etc/gitlab'
- './gitlab/logs:/var/log/gitlab'
- './gitlab/data:/var/opt/gitlab'
nginx:
container_name: 'nginx'
depends_on: # 依赖其她两个服务运行
- jenkins
- gitlab
image: nginx:alpine
volumes: # 映射代码路径、配置文件路径
- './html:/usr/share/nginx/html'
- './nginx/nginx.conf:/etc/nginx/nginx.conf' # nginx 配置入口
- './nginx/conf:/etc/nginx/conf' # jenkins.ci gitlab.ci 的配置,和预览环境的配置
ports:
- 80:80 # 统一对外映射
restart: always
由于镜像名称是 gitlab.ci 和 jenkins.ci ,那么在容器内天然的支持直接使用这两个名称访问,但本地需要配置这两个名称到本地的 127.0.0.1
需要用到 GitLab webhook ,在项目变更时自动触发 Jenkins 任务,我找到两种方式:
http://jenkinsuser:jenkinsapitoken@jenkins.ci:8080/ 这里有两个坑头疼了我半天:
Allow requests to the local network from hooks and services ,使用管理员( @root )帐户访问 gitlaburl/admin/application_settings 里开启 由于 Jenkins GitLab Plugin 插件需要调用 GitLab 接口来设置编译状态、评论等,需要在 GitLab 创建一个用户,比如叫 bot ,该用户需要具备所编译项目的权限,并创建 Personal Access Tokens ,在 Jenkins 系统管理-系统设置里配置 Gitlab 连接,详情参考: https://github.com/jenkinsci/gitlab-plugin#jenkins-to-gitlab-authentication
在编译项目前需要克隆代码,这需要项目的访问权限,可以是用户名密码验证、密钥验证,但我这里使用的 Jenkins 主机的公钥验证:登录 Jenkins 机器,并生成 ssh-key ,把生成的 ssh-key 公钥配置到 GitLab/@bot 用户的 SSH 配置里,这样有个好处是说 bot 用户本身就需要对编译项目有权限,使用她的公钥更合理,并且没有越权问题,这样也只维护一个用户,如果用某个人的帐号,又会涉及到离职交接之类的。
一个小提示,这里创建的 GitLab@bot 用户其实作用很大,比如权限合理些,头像自动义一个 builder ,哈哈哈。
由于要做到多项目、多 PR 不冲突,借鉴于 netlify 的思路,提到以下约定:
项目名--分支名.branch.ci.apmjs.org/ 项目名--PRID.pr.ci.apmjs.org/
注意:项目名里的 / 需要替换为 - 。上面的域名是我之前注册的域名,因为需要使用泛路径 *.branch.ci.apmjs.org 的 A 记录解析,本地的 hosts 搞不定,再者总不能每个项目解析一个 A 记录吧。
使用 nginx 正则匹配泛路径,并指定目录:
# PR 解析
server {
listen 80;
server_name ~^([/w-]+)--(/d+)/.pr/.ci/.apmjs/.org$;
root /usr/share/nginx/html/$1/pr/$2;
location / {
try_files $uri $uri/ /index.html;
}
}
# 分支解析
server {
listen 80;
server_name ~^([/w-]+)--(/w+)/.branch/.ci/.apmjs/.org$;
root /usr/share/nginx/html/$1/branch/$2;
location / {
try_files $uri $uri/ /index.html;
}
}
这样就做到了只需要把编译产出的 dist/ 目录放到对应的目录中即可,nginx 配置只有一份,目录如:
/usr/share/nginx/html/项目名/branch/分支名 /usr/share/nginx/html/项目名/pr/分支名
git ssh origin +refs/heads/*:refs/remotes/origin/* +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/* On push to source branch
在任务中配置构建 shell 命令,如:
npm -v node -v npm install npm run build
以上命令会安装项目依赖,并编译项目,一般是产出 dist/ 目录
在任务中再添加一个配置构建 shell 命令,如:
#!/bin/bash
# 当项目是 Merge Request(PR)时路径会不一样
if [ "$gitlabActionType" = "MERGE" ] ; then
url="/usr/share/jenkins/html/$gitlabSourceRepoName/pr/$gitlabMergeRequestId"
else
url="/usr/share/jenkins/html/$gitlabSourceRepoName/branch/${gitlabSourceBranch/////-}"
fi
mkdir -p $url
rm -rf $url/*
cp -r dist/* $url/
这里要重点的感谢下 @wei 帮我解决了大问题。。。
以上命令是把产出的 dist/ 目录复制到不同的目录中,而该目录是 Docker 映射的 ./html/ 目录,也会把该目录映射给 nginx ,这样就做到了编译产出后 nginx 那边直接可以使用
在 Jenkins 任务里添加构建后操作:
编译成功,请访问 <http://${gitlabTargetRepoName}--${gitlabMergeRequestId}.pr.ci.apmjs.org/> 查看最新环境。
npm run build
其实这里说的比较乱,只是记录我的一些过程,当然也只是一条思路,自身需要根据公司的项目而相应的调整、优化。