前段时间入职新公司,整体感觉不错。唯一不太帅的地方是公司配备的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
其实这里说的比较乱,只是记录我的一些过程,当然也只是一条思路,自身需要根据公司的项目而相应的调整、优化。