背景
在测试环境部署这块, 经历过以下几个阶段:
-
阶段一
有一台测试服务器把项目放上面测试, 当初也没有什么相关的经验, 每次改完代码本地打包上传到服务器上, 然后一顿命令启动项目, 完成了最原始的部署。
这种方式构建和部署全靠人肉, 项目简单的时候还好说, 项目一多配置一多 ( 比如微服务 ) 中间哪个环节粗心出点错那简直就是灾难。
同时还要专门有人对运维相关的技术比较了解, 不然哪天我不在测试工作就完全停滞了。
-
阶段二
既然都是重复工作, 那就整理下步骤写个脚本
从 SVN 拉代码
Maven 构建打包
重启 Tomcat
每次执行下脚本就搞定了。看着挺不错的, 不过实际执行时的情况总会复杂许多 ( 服务器帐号权限、测试人员对 Linux 的熟悉程度、项目启动依赖复杂等等问题 ) 。
-
阶段三
了解到 Jenkins 是个不错的工具, 那就把脚本的内容迁移到 Jenkins 上, 不管是开发还是测试只要在 web 界面上点击一下按钮即可完成构建部署, 很 easy。
-
阶段四
容器化: 使用 Docker 来部署项目, 这样就可以干掉原来服务器上散落各地参差不齐的 Tomcat ( 不同项目依赖不同 ) , 利用 Docker Compose 对项目进行编排, 提供一种规范的构建配置 ( 同时也是一份文档 ) , 大大减小了后期维护和交接的成本。
-
阶段五
上面的阶段已经能解决日常需求了, 但是还有一点问题就是每次提交完代码还要手动去 Jenkins 上发布, 能更自动点就更好了 ( 嗯, 就是懒 ) 。
于是就引出了本文的目标 —— 自动持续构建, 不需要人工操作 ( 留人工操作用于处理特殊情况 )
方案流程
开发提交代码
开发对需要发布的版本打上 Tag
触发 GitLab 的 tag push 事件, 调用 Webhook
Webhook 触发 Jenkins 的构建任务
Jenkins 构建完项目可以按版本号上传到仓库、部署、通知相关人员等等
安装 GitLab
GitLab 官方文档 已经介绍的比较详细了, 这里不再赘述, 下面给出最终调整过的 Docker Compose 配置:
gitlab:
image: "twang2218/gitlab-ce-zh:11.0.2"
restart: always
hostname: 'gitlab'
ports:
- "10022:22"
- "10080:10080"
# postgresql 端口
- "5432:5432"
volumes:
- ./gitlab/data:/var/opt/gitlab
- ./gitlab/log:/var/log/gitlab
- ./gitlab/config:/etc/gitlab
environment:
GITLAB_OMNIBUS_CONFIG: |
# 仓库路径, 填写宿主机的域名或 IP
external_url 'http://192.168.xxx.xxx:10080'
# 调整工作进程数减小内存占用,最小为 2
unicorn['worker_processes'] = 2
gitlab_rails['time_zone'] = 'Asia/Shanghai'
# 邮箱配置
gitlab_rails['gitlab_email_from'] = ''
gitlab_rails['gitlab_email_display_name'] = ''
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = 'smtp.163.com'
gitlab_rails['smtp_port'] = 25
gitlab_rails['smtp_user_name'] = ""
gitlab_rails['smtp_password'] = ""
gitlab_rails['smtp_domain'] = 'smtp.163.com'
gitlab_rails['smtp_tls'] = false
gitlab_rails['smtp_openssl_verify_mode'] = 'none'
gitlab_rails['smtp_enable_starttls_auto'] = false
gitlab_rails['smtp_ssl'] = false
gitlab_rails['smtp_force_ssl'] = false
# 数据库配置
gitlab_rails['db_host'] = '127.0.0.1'
gitlab_rails['db_port'] = 5432
gitlab_rails['db_username'] = "gitlab"
gitlab_rails['db_password'] = "gitlab"
postgresql['listen_address'] = '0.0.0.0'
postgresql['port'] = 5432
postgresql['md5_auth_cidr_addresses'] = %w()
postgresql['trust_auth_cidr_addresses'] = %w(0.0.0.0/0)
postgresql['sql_user'] = "gitlab"
postgresql['sql_user_password'] = Digest::MD5.hexdigest "gitlab" << postgresql['sql_user']
# 备份设置-保留7天
gitlab_rails['backup_keep_time'] = 604800
GITLAB_BACKUPS: "daily"
GITLAB_SIGNUP: "true"
GITLAB_ROOT_PASSWORD: "lb80h&85"
GITLAB_GRAVATAR_ENABLED: "true"
说明:
这里使用 汉化版 镜像, 如果不适应可以换回 官方原版 镜像
gitlab/gitlab-ce:11.0.2-ce.0
项目初始配置 + 启动很慢, 需要一段时间, 日志中出现 Reconfigured 时表示启动成功
192.168.xxx.xxx 替换为宿主机的 IP 地址
初始管理员帐号密码:
root
/lb80h&85
( 自行修改配置文件中的密码 )该配置为 乞丐版 , 内存占用 2G+ ( worker_processes 越多内存占用越大, 默认为 8G )
postgresql 为容器中内置的数据库 ( 帐号:
gitlab
/gitlab
) , 没必要就不用暴露端口了邮箱填写用于发送找回密码和通知的发件人帐号 ( 收不到邮件? ) , 不想配置就删掉相关配置好了, 不影响正常使用
安装 Jenkins
为了测试方便, 使用 Docker 化的 Jenkins , 如果需要调用一些特殊的命令或脚本就不是很方便, 实际使用过程中可以换成普通版的。
Docker Compose 配置如下:
version: '3'
services:
jenkins:
image: jenkins/jenkins:2.151
container_name: jenkins
networks:
- net
user: "root"
restart: always
ports:
- 9000:8080
environment:
- JAVA_OPTS="-Duser.timezone=Asia/Shanghai"
volumes:
- /etc/localtime:/etc/localtime:ro
- ./data:/var/jenkins_home:rw
- ./backup:/var/jenkins_backup:rw
# 网络配置
networks:
net:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.23.0.0/16
说明:
初次启动请打印日志, 日志中有管理员帐号的初始密码, 第一次登陆的时候需要用到
为了方便, 使用 root 帐号启动容器, 如果使用默认帐号启动需要修改本地目录 ( data、backup ) 的权限
配置 GitLab
注册账号什么的就不赘述了, 建一个测试项目 test , 随便 commit 几条内容
按下图步骤创建账号的 access token , 用于 Jenkins 调用 GitLab 的 API
记下这里生成的 access token ( gRCtwVWU8cxwHdxxVZJD
) , 后面要用到。
配置 Jenkins
-
安装插件 ( 安装过程可能会失败,多试几次就好了 )
Git Parameter ( 用于参数化构建中动态获取项目分支 )
Generic Webhook Trigger ( 用于解析 Webhook 传过来的参数 )
GitLab ( 用于推送构建结果给 GitLab )
-
添加 GitLab 凭据
-
配置 GitLab 连接
-
新建项目 test
勾选 参数化构建过程, 添加 Git Parameter 类型的参数 ref , 这样构建的时候就可以指定分支进行构建。
Source Code Management 选择 Git , 添加项目地址和授权方式 ( 帐号密码 或者 ssh key ) , 分支填写构建参数 $ref。
Build Triggers 选择 Generic Webhook Trigger 方式用于解析 GitLab 推过来的详细参数 ( jsonpath 在线测试 ) 。其他触发方式中: Trigger builds remotely 是 Jenkins 自带的, Build when a change is pushed to GitLab 是 GitLab 插件 提供的, 都属于简单的触发构建, 无法做复杂的处理。
虽然 Generic Webhook Trigger 提供了 Token 参数进行鉴权, 但为了避免不同项目进行混调 ( 比如 A 项目提交代码却触发了 B 项目的构建) , 还要对请求做下过滤。Optional filter 中 Text 填写需要校验的内容 ( 可使用变量 ) , Expression 使用正则表达式对 Text 进行匹配, 匹配成功才允许触发构建。
Build 内容按自己实际的项目类型进行调整, 使用 Maven 插件 或 脚本 等等。
GitLab Connection 选择上面添加的 GitLab 连接 (
Jenkins
) , Post-build Actions 添加 Publish build status to GitLab 动作, 实现构建结束后通知构建结果给 GitLab。
-
回到 GitLab 的项目页面中, 添加一个 Webhook ( http://JENKINS_URL/generic-webhook-trigger/invoke?token=<上面 Jenkins 项目配置中的 token> ) , 触发器选择 标签推送事件。因为日常开发中 push 操作比较频繁而且不是每个版本都需要构建, 所以只针对需要构建的版本打上 Tag 就好了。
创建完使用 test 按钮 先测试下, 可能会出现下面的错误
Hook execution failed: URL 'http://192.168.xxx.xxx:9000/generic-webhook-trigger/invoke?token=d63ad84eb18cb04d4459ec347a196dce' is blocked: Requests to the local network are not allowed
解决办法: 允许 GitLab 本地网络发送 Webhook 请求
测试效果
可以在 GitLab 直接添加 Tag , 不过我觉得用 IDEA 上操作更方便点, 就把代码拉下来在本地操作
然后使用快捷键 Ctrl + Shift + K 调出 Push 窗口 , 把 Tag 推送到 GitLab 中
回到 GitLab 页面可以看到触发了 Webhook , View details 查看请求详情, Response body 中 triggered
字段值为 true
则表示成功触发了 Jenkins 进行构建
再看下构建结果
注意: 每添加一个 Tag 就会触发一次事件, 不管是不是一起 push 的。所以一次 push 多个 Tag 会触发 Jenkins 进行多次构建。不过 Jenkins 已经做了处理, 默认串行执行任务 ( 一个任务结束再执行下一个 ) , 而且在构建前有一个 pending 状态, 此时被多次触发会进行合并, 并取首次触发的参数, 如下图所示:
关于 Tag 的几点说明
推送 Tag 到远端的时候, 远端已存在 ( 同名 ) 的 Tag 不会被添加到远端
拉取远端的 Tag 时, 本地已存在 ( 同名 ) 的 Tag 不会添加到本地
拉取远端的 Tag 时, 本地不会删除远端已删除的 Tag , 需要同步远端的 Tag 可以先删除本地所有 Tag 再 pull
删除 Tag 也会推送事件, 要做好过滤 ( 上面配置中已使用 commitsId 字段进行过滤 )
未完待续
通过上面的步骤已经初步实现了想要的效果, 还有几个点后续可以再考虑下:
上文只包含自动构建的内容, 对于项目的部署可以考虑几种方式: 手动选择指定的版本进行发布、构建任务结束后直接触发部署任务、定时部署最新版本 ( 根据实际需求调整 )。
测试发版的频率会比较高, 会生成大量的 Tag , 可以约定 Tag 的格式, 比如
test 0.0.1
表示触发测试环境的项目构建,online 1.0.0
表示触发正式版本构建, 隔离之后可以方便后续的维护和清理。构建部分可以整合 Docker , 把构建结果打包到 Docker 镜像中 ( 代码版本库的 Tag 正好可以作为镜像的 Tag ) , 再上传到 Docker 镜像仓库 ( 私服 或者第三方仓库 ) 中, 后续部署就可以直接从镜像仓库拉取镜像直接运行了。
集成自动化测试 , 比如 这个
尝试配置 GitLab 自带的 CI / CD
总结
以上就是对曾经踩过的一些坑进行的整合, 也没什么好总结的。总之, 合理地利用现有工具来解放双手, 就能有更多时间做其他想做的事!
时间有限一些基础的步骤就不细讲直接一笔带过了, 方案上可能有些细节方面也没考虑全, 欢迎评论留言。