svn 是公司默认的版本控制工具。项目组内每位(入职的)同事,凭借自己的工号对版本仓库都拥有绝对操作权限。
所谓绝对权限,就是无论新员工还是老员工都可以提交代码,创建分支,甚至删除代码仓库。就像每人都拿着 root
权限很嗨的在服务器上 rm
一样,项目内对成员没有任何的节制,可谓是群魔乱舞。经过一年的项目演进,各种
问题越来越严重。
先从最基本的代码 review 说起。
由于 svn 是集中式的版本控制工具,大家都向主分支看齐,每次提交之前 update,有冲突解决冲突。出版本时就打 tag 或新拉一个分支。流程非常的简单易懂,上手也很快。大家在每天的 commit 中也都“愉(fen)快(nu)”的工作着, 直到有一天,有人提议质量有点控制不住了,要进行代码 review。
很长一段时间内,大家借着时间紧迫的由头,心安理得地更新着自己的模块,对提高质量的设施没有任何的作为。 虽然说不上质量失控,但调 bug 的时间大于写代码的时间还是能感受到的,最令人悲伤的是“调试那些不能立即失败, 跑到别处去捣乱的低级错误”。
不花时间 review 代码,必定要花时间调试错误。
项目组内的做法不是引入第三方的 review 工具,而是创建一个 review 分支,审核的流程变成如下方式:
可以看到,上述的步骤非常的繁琐费事,审核人要逐个文件打开,修改意见附带行号反馈给提交者。提交者要提交两次, 在不同分支之间来回切换合并代码。试行一段时间之后,由于麻烦,review 分支逐渐变成了开发分支,没人再往主分支上提交改动, 主分支和 review 分支的代码出现不一致,每次合并费神费力。最后,review 的事情不了了之,原来的主分支被废弃。
中间还试行过几次邮件发送 diff 文件的方式。被 svn 惯坏的我们,只看的惯 side by side
的显示方式,
不适应 inline
的 diff 文件。这种 review 的方式也浅尝则止。项目继续在没有 review 的流程里苦苦挣扎。
前面提到每人持有代码仓库帐号,可以随便提交修改,没有任何的静态检查和持续集成机制,每次只是通过“形同虚设”的单元测试保证质量。 如果想真正做到经过 review 之后代码才能合入主分支,必须在源头上将每个人的权限进行回收,对不同的贡献者给与不同的权限。
期间简单调研了 reviewboard 和 phabricator,两者对比从颜值上就开始排斥前者了。在折腾(被公司的各种网络限制坑苦) 了一段时间的 phabricator 后,发现不能获取代码仓库里的改动,只能读取日志。hack 很久之后无果,遗憾的终止。
直到年底,在一次代码重构时,实在忍受不了各种烂代码和低级 bug 满天飞,决定重新寻找新的 review 替代工具。
再加上之前一直苦于 svn 在本地的代码管理上的繁琐,切换到 git 之后愉快的 branch
体验。这次的目标是选择基于 git 的 review 工具。
于是,GitLab 便进入了视线。
GitLab 类似 GitHub 服务,是一个管理 git 仓库的开源项目。可以搭建在自己的服务器上,提供私有仓库的托管。 目前版本已经集成了丰富的功能,对代码的 review 功能提供内在的支持。GitHub 上的流程完全可以使用 GitLab 在本地运行。
由于 GitLab 依赖多个其他的开源组件,如果通过源码直接安装实在并非易事。好在最新版本提供了不同 Linux 的安装包, 使用包管理工具基本就能完成一键安装。如果系统不能连接互联网,可以将安装的命令脚本下载下来,获取下载地址后, 直接用浏览器将安装包下载下来,再上传到服务器安装。
GitLab Omnibus package 中内嵌了使用的其它组件,不用再另行安装。安装后,可以直接运行。至于配置,推荐直接阅读官方文档, 细心耐心些就能看明白工作原理。不要再浪费时间去找教程,别人的教程并不一定适用最新的 GitLab,而且容易被带到沟里。
因为项目团队内部使用,人数不多,可以省去配置许多的特性。接下来主要是修改 SMTP 配置,因为这是添加成员,修改密码必要的一项。 参考公司内部的一个配置案例,可是邮件始终发不出去。咨询了专业人士,对 smtp server 地址和 DNS 分别作了修改,依然不能奏效。 观察后台的 job,始终显示邮箱帐号认证失败。反复检查了帐号密码,确定无误之后,进展被搁置。
一次查看相关资料时,注意到可以使用 mailx
命令发送邮件,.mailrc
配置简单,可以用来检查是否能连接 smtp server。
经过了多次的尝试后,才知道公司的 smtp 应该如何配置。将相关的选项配置到 /etc/gitlab/gitlab.rb
文件中,才终于解决了这个问题。
学习使用 GitLab 协作开发,一定不能错过官方的流程指导。基于分支的开发可谓 git 的精华,GitLab 在分支之上充分发挥 社交编程的理念,让协作开发变得足够简单。利用好 issue, merge request 和 wiki 功能,项目的管理完全可以 all in one。 bug 追踪,代码 review,ci 持续集成,GitLab 都可以胜任。并且使用 YAML 文件的方式做 CI,方便可控,也免去另外搭建持续集成的服务。
GitLab 提倡两种协作方式:保护分支和 Fork 仓库,各有优缺点。由于之前对权限疏于管理,我选择了对仓库保护更严格的 Fork 方式。 其实,对于小团队第一种方式更加简单高效,后面会做说明。
在 GitLab 上创建群组 Group,为每个成员分配角色,每个角色拥有不同的仓库操作权限。开始为每人组内权限设为 Reporter, 也可以单独设置成员对某个项目的权限,现在的工作流程变为:
整个过程在浏览器的网页端即可完成,提交者可以将更多的精力放在代码的修改上,不必再为合并代码和沟通费神。这种流程完全是模仿了 GitHub 上开源项目的协作方式,好处是成员的权限上缴,主分支只有管理员可以修改,可以保证提交日志风格一致,冲突易于解决,代码管理更加的规范。
然而对于小团队来讲,Fork 方式的优点也恰恰是其缺点。由于对权限过于限制,成员只有发起 merge request 的能力,不能添加 label, 不能维护 wiki,且每个 Fork 的仓库都要配置 CI Runner。最主要的是每次在本地创建新的分支时,都要保持 Fork 仓库和原始仓库的同步。 这无疑对组内成员带来不必要的负担,况且,大家对 git 的协作方式本身就不是很熟悉。增加一步操作,流程上就有一个负担,让操作变得不直接。
既然开源软件的协作方式不适合小团队,就下放部分权限,使用“保护分支”的方式进行开发。为组内成员赋予 Developer 角色,同时使其不可修改 受保护分支。所有人都在原始仓库上工作,流程再次变为如下方式:
所有人都基于同一个仓库开发,就不用考虑同步仓库代码的问题,每次执行 git pull
之后,要修改的分支就已经最新。
而且,组内成员都可以创建 label,进一步详细标示自己的修改,也是一种高效的方式。
管理员使用 rebase -i
的方式进行 Merge MR。这样可以保持主分支的 commits 记录呈现一种线性的状态。管理员将 MR 中的多次提交 Squash
成一次,在提交的日志中写明 Closes !id
。通过 MR 的 id 依然可以看到当时的修改详细记录。
如果需要使用 GitLab CI 功能,需要为项目安装并配置 Runner,这样在项目中添加了 .gitlab-ci.yml
文件之后,每次的 push
都会在配置的 Runner 机器上运行你的集成测试脚本。具体的操作流程可以参考 http://doc.gitlab.com/ce/ci/quick_start/README.html
对 GitLab 的数据进行备份也是必须要考虑的事情,指定备份的目录,参考教程使用 gitlab-rake
命令就能完成对仓库和数据的 backup 功能。
利用 crontab
设置好执行备份的周期,即可定时完成备份。 如果在一台机器上也不放心,要做多机的备份可以使用 rsync
的同步功能,
详细操作可以参考一个非常好的视频教程。
在使用过程中,当同事一次提交大量的文件,且每个文件的修改行数较多时,GitLab 的 MR 里无法完全加载所有的 Changes, 但是服务器的性能并没有得到充分的使用。关于 GitLab 如何进行进一步的性能调优,还需要继续学习。
PS: google后发现通过 gitlab-ctl tail
可以输出 GitLab 的实时运行状态,输出每个组件的日志。
重现 502 的问题,观察 /var/log/gitlab/unicorn/unicorn_stderr.log
中的 ERROR 信息可以发现 unicorn
的 worker 内存 exceed limits。
去 /etc/gitlab/gitlab.rb
中将 unicorn 的 max_mem_limits
和 min_mem_limits
设置调大后,仍然是502。但是这次的报错是 timeout 超过 60s,
将超时限制也设置成3min后,问题解决。