一种 gitlab 版本管理策略

背景

  • 目前负责小组内源码的版本管理,将目前的我们的源码管理策略做一番梳理和分享,以期共同进步。

流程

  1. 先建立主分支master

  2. Owner基于master建立开发分支xxx-dev-v1,xxx-release-v1,分别是第一期开发版本,第一期发布版本。

  3. xxx-dev-v1供开发人员拉取并开发,功能点开发完成且单元测试通过后,将开发代码提交到xxx-dev-v1上。

  4. 测试人员测试xxx-dev-v1的代码,测试通过后,由本人将xxx-dev-v1的代码合并到xxx-release-v1,待发布。发布前若测试人员发现bug,则重复3、4步。

  5. 发布时,由Owner将xxx-release-v1合并到master分支,合并后master分支打tag,如master> git tag 20171220-v1.0,然后运行自动发布脚本进行发布。

  6. 项目是迭代开发,第一期完成后该进行第二期开发,命名方式相同,依次类推。

  7. 如果在开发V2的过程中,需要紧急修复V1(线上)中的漏洞,则重复3、4、5步,并在最后将master的代码合并到V2上,以保证V2的代码最新;如评估后不紧急,可放在V2中开发。

注意

  1. 务必保证master是线上最新版本。

  2. 可改进的地方:经过一段时间的策略使用,认为目前的问题在于角色分工上不够明确,具体表现在两个问题:(1)开发人员代码开发并提交dev分支后,应该提起dev到release的合并请求,由Owner审核后通过,而不是Owner发起合并(2)测试人员在测试没有bug后应提起release合并到master的请求,而不是由Owner发起合并。

使用 Docker 搭建 Gitlab + Jenkins + SonarQube 的 PHP 持续集成环境

对于开源 PHP 项目,现在比较成熟的一套持续集成方案是使用 Github + TravisCI + StyleCI + Scrutinizer + coveralls,不过这套方案如果想要用于私有项目的话就抓狂了,个个要买套餐,其中很多还不便宜。而且对于公司内使用的项目来说,内部搭建的 Gitlab 方案更为常见,对于这种情况,我们可以使用 Gitlab + Jenkins + SonarQube 来进行代替。

安装 SonarQube

$ docker pull postgres

$ docker run --name db -e POSTGRES_USER=sonar -e POSTGRES_PASSWORD=sonar -d postgres

$ docker pull sonarqube

$ docker run --name sq --link db -e SONARQUBE_JDBC_URL=jdbc:postgresql://db:5432/sonar -p 9000:9000 -d sonarqube

执行完毕上面的命令后通过浏览器进入 SonarQube,默认用户名和密码都是 admin,进去后会有一段引导,里面会让你生成一个 access token,这个后面的配置 Jenkins 时会用到。

如果没有记下来的话,可以点右上角的用户头像里面的 My Account > Security 标签中可以生成一个新的。

配置 Jenkins

Jenkins 需要在全局的 系统设置 里面添加 SonarQube Server,填下对应的访问地址和上一步获取的 access token 即可。服务器地址填写 localhost 可能会有问题,填 ip 会比较好些。

然后需要在 系统管理 的 Global Tool Configuration 菜单中配置 SonarQube Scanner 安装,这个直接选择自动安装就好了,十分方便。

这两步配好之后就到对应的项目配置中添加构建步骤,下拉选择 Execute SonarQube Scanner,然后对于 2.1 版本以上的 SonarQube Scanner 就只需要配置 Analysis properties 这一项就可以了,比较常用的参数包括 sonar.projectKey (用来确定 该项目在 SonarQube 中叫什么名字) 和 sonar.sources=(用来指定需要扫描的目录)。

配完之后选择构建即可,可以去当前构建的 Console Output 里面查看有没有报错,正常执行完成的话,在 SonarQube 项目面板中就可以看到一个新增的命名为配置的 sonar.projectKey 的 项目了。

注意点

  • SonarPHP 自定义检查规则需要用 java 来写扩展,比较新的版本内置了 psr2 的规则基本够用,内置的 Quality Profiles 是可以复制一个出来进行自定义的
  • Sonar 嗅探出的一些问题可能实际上并没有什么影响,比如变量名中含有 ‘pwd’ 等,如果原本使用方式确实合理则可适当忽略

GitLab 升级历程

在完成了GitLab的部署、汉化、备份、恢复后,就要接着考虑gitlab的升级了。接触的这段时间gitlab不断的进行迭代更新,所以如果有实用的新功能或严重的bug修复时,必然要考虑GitLab的更新。

一、下载新版本的RPM包

途径1:通过清华开源镜像站

查看清华开源镜像站,暂时还没有我需要的10.0.4的rpm包。

如果有(比如后面有了10.0.4的包),则直接获取该包。

wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-10.0.4-ce.0.el7.x86_64.rpm

途径2:从官方获取RPM包后上传到/root目录下

官方下载:https://packages.gitlab.com/gitlab/gitlab-ce/

因为要在CentOS7上更新的版本是10.0.4,故找到gitlab-ce-10.0.4-ce.0.el7.x86_64.rpm下载到本地后,通过Bitvise SSH Client工具将rpm包上传到gitlab虚拟机的/root目录下。

说明:从下载速度和方便程度来说,走清华的镜像站的方式更优,但是我2017/10/20想下载gitlab10.0.4时,发现清华的镜像站最新只有10.0.3,故采取“途径2”获取最新的安装包。

二、更新gitlab

2.1 关闭部分gitlab服务

gitlab-ctl stop unicorn
gitlab-ctl stop sidekiq
gitlab-ctl stop nginx

2.2 升级

rpm -Uvh gitlab-ce-10.0.4-ce.0.el7.x86_64.rpm

2.3 重新配置gitlab

gitlab-ctl reconfigure

2.4 重启gitlab

gitlab-ctl restart

使用管理员账户登录后可以看到gitlab的版本号已经从10.0.2升到了10.0.4。

未分类

三、更新汉化补丁

3.1 安装git

yum install -y git

3.2 克隆获取汉化版本库

下载最新的汉化包

cd
git clone https://gitlab.com/xhang/gitlab.git

如果是要下载老版本的汉化包,需要加上老版本的分支,比如今天已经是10.0.4,我依旧想下载10.0.2,可以运行下面的语句。

git clone https://gitlab.com/xhang/gitlab.git -b v10.0.2-zh

3.3 查看该汉化补丁的版本

cat gitlab/VERSION

3.4 停止gitlab服务

gitlab-ctl stop

3.5 切换到gitlab汉化包所在的目录

cd /root/gitlab

3.6 比较汉化标签和原标签,,导出patch用的diff文件到/root下

git diff v10.0.4 v10.0.4-zh > ../10.0.4-zh.diff

3.7 回到/root目录

cd

3.8 将10.0.4-zh.diff作为补丁更新到gitlab中

yum install patch -y
patch -d /opt/gitlab/embedded/service/gitlab-rails -p1 < 10.0.4-zh.diff

3.9 启动gitlab

gitlab-ctl start

3.10 重新配置gitlab

gitlab-ctl reconfigure

使用管理员账户登录后可以看到gitlab已经完成了汉化。

未分类

Debian 环境下安装配置

GitLab是一个利用Ruby on Rails开发的开源应用程序,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。
它拥有与GitHub类似的功能,能够浏览源代码,管理缺陷和注释。可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。

前言

公司目前的开发架构中,使用了gitlab来统一管理上线发布代码的操作。鉴于是之前的前辈搭建的,所以打算自己手动安装一次,来熟悉相关的配置。

安装

中文版gitlab网站:https://www.gitlab.com.cn/installation/
英文版gitlab网站:https://about.gitlab.com/downloads/

选择对应的操作系统

我这里选择 Debian 9 版本,因为要在服务器上进行搭建,长期运行。综合考虑后采用 debian系统,稳定,快速。

开始安装

安装过程非常简单, 按照页面中的执行步骤,依次执行命令即可。

1. 安装配置依赖项

如想使用 Postfix 来收发邮件,在安装期间请选择’Internet Site’. 您也可以用 exim4 或者配置外部的SMTP服务,使用SMTP发送邮件。
上面的安装期间,我没有选择internet site 选择的是本地local

sudo apt-get install curl openssh-server ca-certificates postfix

2. 添加GitLab仓库,并安装到服务器上

curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash

安装的过程中会有个蓝色的窗口提示你输入名称,这个是gitlab的访问域名,因为默认通过nginx进行解析的时候,指向的是你配置的这个域名,并且通过80端口。安装完成后可以看nginx的conf文件。

sudo apt-get install gitlab-ce

3. 启动GitLab

这个会加载所有的配置,启动相应的服务。所以说,和网络上其他的安装教程来比,新版本安装起来实在是太爽了。

sudo gitlab-ctl reconfigure

配置

更换域名

未分类

如上图,域名被替换为域名了,但是之前是字符串git@debian…,因为在安装的时候,蓝色窗口弹出的时候直接按了回车使用了默认值。修改方法如下:

打开配置文件

vim /etc/gitlab/gitlab.rb

找到第7行的external_url将值进行修改后保存

3 ## GitLab URL
4 ##! URL on which GitLab will be reachable.
5 ##! For more details on configuring external_url see:
6 ##! https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-the-external-url-for-gitlab
7 external_url 'http://192.168.186.136'

重新启动gitlab,完成域名修改。

gitlab-ctl reconfigure

添加 ssh 公钥

因为在gitlab上使用git命令进行操作,需要进行身份验证,依赖于ssh公钥,所以需要将ssh公钥加入到自己的配置文件中,

ssh-keygen

下面命令得到的内容就是公钥

cat ~/.ssh/id_rsa.pub

CentOS 7.x上gitlab搭建教程(https可用,邮件可用)

知识要求

  • nginx基础知识

搭建感想

注:以下是我搭建gitlab时的思考,需要nginx的基础知识,Docker的基础知识才容易理解,与下面的搭建过程是独立的,不感兴趣可直接略过。

其实gitlab已经搭建并用了一年多了,现在所有的项目管理都通过gitlab完成。但是一直以来都有2个问题:

  • 80端口被系统的nginx占用了,所以只能监听非80端口;
  • 443端口也被系统的nginx占用,所以也一直没增加对https的支持;

最近正在尝试对所有已有的服务Docker化,一方面想让gitlab的搭建更简单些,另一方面也把这两个问题都处理掉。

于是就做了两个Docker容器: nginx和gitlab,相当于nginx和gitlab运行在局域网的不同主机,所以端口上没冲突。nginx是对外的服务器,它做一层反向代理到gitlab就能让gitlab提供对外的服务。
然而。。。这个做法却带来了一个新问题:gitlab需要的是22,80,443端口,80与443通过反向代理解决了,22却没办法解决。因为正常来讲,宿主机的SSH肯定也在使用,所以gitlab的SSH监听端口映射到宿主机会有冲突。

当然了,解决办法还是有的,不过非常繁琐。我们做Docker的目的不就是为了降低布署难度吗?如果使用Docker的配置比在宿主机上还繁琐,那使用起来就没太大意义了。

于是gitlab就没有放在Docker中,而是直接搭在宿主机上。

搭建过程

安装

gitlab的安装是很简单的,先下载安装包:

wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-10.2.5-ce.0.el7.x86_64.rpm

安装:

rpm -Uvh gitlab-ce-10.2.5-ce.0.el7.x86_64.rpm

配置

gitlab内置了nginx的服务,所以默认会占用80和443端口。一般来说,我们做WEB开发,服务器上早就安装了nginx或者apache,把80和443端口给占了,所以需要做些修改防止冲突。

简单的修改端口是不可行的,比如80改成85,当查看gitlab项目时,下图中的项目地址会变成http://git.papamk.com:85/papamk/groupbill这样,看着就不舒服。启用https则在使用过程中则会出现其他的问题。这里不一一论述,直接说明正确的配置方法。

未分类

具体步骤:

1、首先确保你的服务器已经有运行nginx,并且该nginx监听宿主机的80和443端口。(如果你原来使用的apache,请通过nginx反向代理到apache,这样apache原来的服务仍然可用)。

2、编辑/etc/gitlab/gitlab.rb:

# 编辑对外的域名(gitlab.papamk.com请添加A记录指向本服务器的公网IP):
external_url 'http://gitlab.papamk.com/'

# 禁用`gitlab`内置的`nginx`:
nginx['enable'] = false
# 修改成与nginx运行时的用户一致
web_server['external_users'] = ['www']

修改监听方式和监听地址(如果nginx与gitlab都在宿主机上,不用改也行;如果nginx在docker中,则需要修改)

gitlab_workhorse['listen_network'] = "tcp"
# 下面的172.18.147.173为本机IP,根据实际情况修改,不能为localhost或者127.0.0.1,否则docker访问不到
gitlab_workhorse['listen_addr'] = "172.18.147.173:8181" 

最后执行下面命令让配置生效:

$gitlab-ctl reconfigure

3、配置nginx

增加gitlab.conf的配置(所有需要注意的地方都加了中文注释):

upstream gitlab-workhorse {
  server 172.18.147.173:8181; #根据实际情况修改
}

## Normal HTTP host
server {
  listen 80;
  listen [::]:80 default_server;
  server_name gitlab.papamk.com; ## 修改成自己的域名;
  server_tokens off; ## Don't show the nginx version number, a security best practice
  root /opt/gitlab/embedded/service/gitlab-rails/public;

  ## See app/controllers/application_controller.rb for headers set

  ## Individual nginx logs for this GitLab vhost
  access_log  /home/wwwlogs/gitlab_access.log; # 根据实际情况修改
  error_log   /home/wwwlogs/gitlab_error.log; # 根据实际情况修改

  location / {
    client_max_body_size 0;
    gzip off;

    ## https://github.com/gitlabhq/gitlabhq/issues/694
    ## Some requests take more than 30 seconds.
    proxy_read_timeout      300;
    proxy_connect_timeout   300;
    proxy_redirect          off;

    proxy_http_version 1.1;

    proxy_set_header    Host                $http_host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header    X-Forwarded-Proto   $scheme;

    proxy_pass http://gitlab-workhorse;
  }
}

该配置根据官网提供的配置修改而来: https://gitlab.com/gitlab-org/gitlab-recipes/blob/master/web-server/nginx/gitlab-omnibus-nginx.conf。

重启nginx:

$sudo service nginx restart

4、登陆浏览器,这时可以看到安装成功了,如下图所示:

首次登陆会要求重置管理员密码,管理员的默认用户名为root。

未分类

https的支持

1、使用Let’s encrypt申请免费的SSL证书,该项目提供了一个叫certbot/certbot-auto的工具获取证书,执行下面命令获取工具:

$wget https://dl.eff.org/certbot-auto
$chmod +x certbot-auto

执行下面命令生成生成gitlab.papamk.com的证书,其中–agree-tos表示同意协议,–email [email protected]为自己的email。

$./certbot-auto --agree-tos --email [email protected] certonly --webroot -w /opt/gitlab/embedded/service/gitlab-rails/public/ -d gitlab.papamk.com 

执行成功后,可通过以下命令查看下证书位置,nginx需要引用这些文件。(一般位于/etc/letsencrypt/live/gitlab.papamk.com/目录)

$./certbot-auto certificates

2、修改/etc/gitlab/gitlab.rb,对外的URL改为https:

external_url 'https://gitlab.papamk.com'

执行重新配置命令以便让新配置生效:

$gitlab-ctl reconfigure

3、修改前面的nginx的gitlab.conf的配置,全部替换成下面的内容(所有需要注意的地方都加了中文注释):

upstream gitlab-workhorse {
  server 172.18.147.173:8181; #根据实际情况修改
}
## Redirects all HTTP traffic to the HTTPS host
server {
  ## Either remove "default_server" from the listen line below,
  ## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
  ## to be served if you visit any address that your server responds to, eg.
  ## the ip address of the server (http://x.x.x.x/)
  listen 0.0.0.0:80;
  listen [::]:80 ipv6only=on default_server;
  server_name gitlab.papamk.com; ## 改成自己的域名
  server_tokens off; ## Don't show the nginx version number, a security best practice
  return 301 https://$http_host$request_uri;
  access_log  /home/wwwlogs/gitlab_access.log; # 根据实际情况修改
  error_log   /home/wwwlogs/gitlab_error.log; # 根据实际情况修改
}
## HTTPS host
server {
  listen 0.0.0.0:443 ssl;
  listen [::]:443 ipv6only=on ssl default_server;
  server_name gitlab.papamk.com; ## 改成自己的域名
  server_tokens off; ## Don't show the nginx version number, a security best practice
  root /opt/gitlab/embedded/service/gitlab-rails/public;

  ## Strong SSL Security
  ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/
  ssl on;
  ssl_certificate /etc/letsencrypt/live/gitlab.papamk.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/gitlab.papamk.com/privkey.pem;

  # GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs
  ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 5m;

  ##   sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
  # ssl_dhparam /etc/ssl/certs/dhparam.pem;

  access_log  /home/wwwlogs/gitlab_access.log; # 根据实际情况修改
  error_log   /home/wwwlogs/gitlab_error.log; # 根据实际情况修改
  location / {
    client_max_body_size 0;
    gzip off;

    ## https://github.com/gitlabhq/gitlabhq/issues/694
    ## Some requests take more than 30 seconds.
    proxy_read_timeout      300;
    proxy_connect_timeout   300;
    proxy_redirect          off;

    proxy_http_version 1.1;

    proxy_set_header    Host                $http_host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-Ssl     on;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header    X-Forwarded-Proto   $scheme;
    proxy_pass http://gitlab-workhorse;
  }
}

重启nginx:

$sudo service nginx restart

4、定期更新ssl证书

Let’s encrypt提供的证书,有效期为90天,到期后执行如下命令即可继续使用:

$./certbot-auto renew

将该命令加入到crontab,每小时刷新一次,就不用担心过期了。先将该命令移到系统目录下:

$sudo mv certbot-auto /usr/bin/certbot-auto

然后执行crontab -e,添加一行:

0 */1 * * *  /usr/bin/certbot-auto renew

发送邮件的支持

官网已经给出详细的配置说明和各大邮件服务提供商的示例,不再对配置做说明:
https://docs.gitlab.com/omnibus/settings/smtp.html。

我自己使用163邮箱做测试,而官网示例没有,这里给出参考配置:

gitlab_rails['gitlab_email_from'] = '[email protected]' # 替换成实际的email
gitlab_rails['smtp_enable'] = true                                                      
gitlab_rails['smtp_address'] = "smtp.163.com"                                          
gitlab_rails['smtp_port'] = 465                                                        
gitlab_rails['smtp_user_name'] = "[email protected]" # 替换成实际的email                               
gitlab_rails['smtp_password'] = "xxx"  # 替换成实际的密码                                          
#gitlab_rails['smtp_domain'] = "163.com"                                                
gitlab_rails['smtp_authentication'] = "login"                                          
gitlab_rails['smtp_enable_starttls_auto'] = true                                        
gitlab_rails['smtp_tls'] = true 

一般来说,服务提供商的SMTP邮箱都会限制每日发送次数(250封左右),所以推荐有兴趣的同学用Postfix自建邮箱服务器,或者使用mailgun这种专业的邮件服务提供商。

如果是阿里云上的服务器,作为客户端使用第三方的SMTP配置,会发现被禁止连接SMTP的25端口,需要使用465或者587。

安全相关

  • root的密码建议设置得复杂些;

  • 管理员登陆后,推荐开启注册邮箱验证, 防恶意注册,见:https://docs.gitlab.com/ce/security/user_email_confirmation.html。

Gitlab重置用户密码

自建的Gitlab服务器在阿里云上,阿里云默认屏蔽了对外的25端口连接。每添加新用户,就会反馈重置密码的邮件收不到。解决方法是登录到服务器上用mailq命令找到邮件ID,接着用postcat命令找出链接发给对方完成密码重置工作。

这次的新用户比较奇怪(也可能是gitlab升级),使用postcat获取到的链接无效,无法重置密码。为了让其正常使用,先从管理员身份切换过去并添加SSH公钥。

事后为了解决这个问题,暂时想到两个办法:1. 使用海外的服务器;2. 使用465等SSL加密端口。第一个方法作为备选方案,优先考虑了第二种方案。遗憾的是按照官方文档配置,Gitlab不屈不挠的使用sendmail而非SMTP方式发送邮件。折腾了一个多小时还没弄好,有点心累。

快要放弃的时候转念一想,我需要的是重置密码功能,邮件先放一边吧!于是开始寻找重置用户密码方法,该过程可谓是轻松加愉快,很快就重置了用户的密码。

具体步骤如下:

# 进入gitlab控制台
gitlab-rails console
# 找出用户并重置密码
user = User.find_by(email: '[email protected]')
user.password='1234ABCD'
user.password_confirmation='1234ABCD'
user.save!
# 退出控制台
exit

输出内容示例如下:

[root@localhost gitlab]# gitlab-rails console
Loading production environment (Rails 4.2.8)
irb(main):001:0> user = User.find_by(email: '[email protected]')
=> #<User id:12 @foo>
irb(main):002:0> user.password='1234ABCD'
=> "1234ABCD"
irb(main):003:0> user.password_confirmation='1234ABCD'
=> "1234ABCD"
irb(main):004:0> user.save!
Enqueued ActionMailer::DeliveryJob (Job ID: ea0072d2-5cd5-4b6e-bdad-fa938d977e47) to Sidekiq(mailers) with arguments: "DeviseMailer", "password_change", "deliver_now", gid://gitlab/User/12
=> true

自动化部署之gitlab权限管理–issue管理

1、创建Group,User,Project

创建一个组,组名为java

Group path http://192.168.56.11/java
Visibility Level:    #为权限级别,一般使用Private
Private
Internal
Public

创建一个PM的用户作为项目管理者并加入到java组内

未分类

创建一个项目:

未分类

创建dev1和dev2的用户作为开发者,并加入到项目中

未分类

2、测试dev1,dev2拉取代码库

(1)生成ssh-key

[root@linux-node1 ~]# ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:Hf3O9F7sS9N04cFUr3Awb/Wec28gTpHYyRZMCzLW9q0 root@linux-node1
The key's randomart image is:
+---[RSA 2048]----+
|        +..o=  .+|
|       . oo*.Oo.o|
|         .o.@.++o|
|         . o.*oo+|
|        S . o.=+=|
|           oE= =*|
|            . ooB|
|              .+o|
|               .+|
+----[SHA256]-----+
[root@linux-node1 ~]# cat .ssh/id_rsa.pub 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLDVIqTAvJtj8Uc+SvhcKOKuDtURt3YBxHo9enUkDjOWtSygLZI4hSrEsnvjHdnxeBGOgjPrxEfMMdNCno4pox7V/8bIU9LRVp2eeQFS+N+bSmbJlTKyODa0tabPwT7URYoiFI3giQamQdA0AwwPCPM/RcXwHJsw4q0O/2woCqNKq2tHaUFBqojd2KvqavzpB+4+AdKJSoabwLhE8dzfjIR/eHY31Y2C/+m9sU504v+R0GsAqr5uifi6Ct9eFvumI54BvHssIpZFZmADTT35b1aP0WSwZb2VEhXjaia8L6h/6ANn1NuHGgYZqNiYT6JILESKbrc7PyJOn9DfHKSMq9 root@linux-node1


(2)将公钥(id_rsa.pub)放进dev1账户中
在admin账户下给dev1,dev2账户设置密码,然后使用dev1登录gitlab,做如下操作添加ssh-key:
测试是否能正常拉取代码库app1

[root@linux-node1 ~]# git clone [email protected]:java/app1.git
Cloning into 'app1'...
The authenticity of host '192.168.56.11 (192.168.56.11)' can't be established.
ECDSA key fingerprint is SHA256:p2lhKmsPQ6K+dWHHvbJg0GV+Ni9VM7vlViKrYsZLP1s.
ECDSA key fingerprint is MD5:22:14:1c:37:de:47:1c:4a:2f:88:b1:dc:e2:d0:02:17.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.56.11' (ECDSA) to the list of known hosts.
warning: You appear to have cloned an empty repository.
Checking connectivity... done.
[root@linux-node1 ~]# ll
total 6804
-rw-------.  1 root root     948 Dec  3 01:21 anaconda-ks.cfg
drwxr-xr-x   3 root root      17 Dec 20 15:00 app1
drwxr-xr-x  22 root root   24576 Dec  8 22:16 git-2.7.4
drwxr-xr-x   3 root root      65 Dec  9 01:23 test
-rw-r--r--   1 root root 6918037 Dec  8 22:09 v2.7.4.zip
[root@linux-node1 ~]# cd app1
[root@linux-node1 app1]# ll
total 4
-rw-r--r-- 1 root root 19 Dec 20 15:05 readme

未分类

到此,Linux环境下完成了gitlab的授权管理代码库。

下面以PM用户进行创建开发计划
开发一个官网V1.0版本,包含首页和新闻

(1)创建里程碑(Milestone)

未分类

(2)依次把任务首页,新闻添加到里程碑,并进行任务分配给开发者

未分类

(3)使用dev1用户登录查看,会有任务提示,如图:

未分类

(4)dev1开发者收到任务,进行开发

[root@linux-node1 app1]# git checkout -b shouye    #创建首页分支
Switched to a new branch 'shouye'
[root@linux-node1 app1]# git status
On branch shouye
nothing to commit, working directory clean
[root@linux-node1 app1]# echo "<h1> welcome to www.123.com" > index.html    #进行开发
[root@linux-node1 app1]# ll
total 8
-rw-r--r-- 1 root root 28 Dec 20 15:50 index.html
-rw-r--r-- 1 root root 19 Dec 20 15:05 readme
[root@linux-node1 app1]# git add .
[root@linux-node1 app1]# git commit -m "shouye"    #开发完成,提交本地仓库
[shouye babdcb5] shouye
 1 file changed, 1 insertion(+)
 create mode 100644 index.html
[root@linux-node1 app1]# git push origin shouye    #提交到远程库
Counting objects: 3, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 292 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: 
remote: To create a merge request for shouye, visit:
remote:   http://192.168.56.11/java/app1/merge_requests/new?merge_request%5Bsource_branch%5D=shouye
remote: 
To [email protected]:java/app1.git
 * [new branch]      shouye -> shouye

可以看到有刚才创建的分支,点击”merge request”合并分支请求,之后PM用户登录处理合并请求。此时,一个功能的开发流程就完成。

未分类

未分类

总结:

PM在gitlab创建任务,分配给开发人员
开发人员领取任务后,在本地使用git clone拉取代码库
开发人员创建开发分支(git checkout -b dev),并进行开发
开发人员完成之后,提交到本地仓库(git commit )
开发人员在gitlab界面上申请分支合并请求(Merge request)
PM在gitlab上查看提交和代码修改情况,确认无误后,确认将开发人员的分支合并到主分支(master)
开发人员在gitlab上Mark done确认开发完成,并关闭issue。这一步在提×××并请求时可以通过描述中填写”close #1″等字样,可以直接关闭issue。

GitLab+Jenkins+Rsync+PM2实现Node项目的持续集成与自动部署

前言

最原始的软件开发流程是,在本地搭建好环境,进行开发测试,然后去服务器上搭建环境,手动上传代码,运行测试,然后启动服务。实际上,近些年来出现了很多的工具,使得这些步骤可以自动化,大大降低人工出错的概率,提高生产效率。下面,我就把GitLab+Jenkins+Rsync+PM2实现的Node项目的持续集成以及自动部署的实验过程记录下来。

搭建环境

需要两台服务器作为演示,A主要进行代码管理、构建和分发,B主要运行实际应用。我这边系统使用的是Debian系的。

服务器A

GitLab

准备工作:

apt-get update
apt-get install curl openssh-server ca-certificates postfix

安装postfix的时候,选internet site,之后的 system mail name 填写你的服务器的IP地址。

准备好后开始安装:

curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
apt-get install gitlab-ee

如果 apt 下载很慢可以手动下载 https://packages.gitlab.com/gitlab/gitlab-ee/packages/ubuntu/trusty/gitlab-ee_10.2.2-ee.0_amd64.deb 然后用 dpkg -i 的方式安装。装了这个过后 NGINX, Postgres, Redis 就都装好了。

配置:

GitLab默认会占用80、8080和9090端口,Jenkins默认也会使用8080端口,所以将GitLab的默认端口为60200、60201、60202(你可以随意定制)

vim /etc/gitlab/gitlab.rb 修改

external_url 'http://<你的服务器ip>:60200'
unicorn['port'] = 60201
prometheus['listen_address'] = 'localhost:60202'

注意不能有多余空格。gitlab-ctl reconfigure生效配置,gitlab-ctl start启动。
如果要想发邮件的话还要配置第三方邮件 vim /etc/gitlab/gitlab.rb

gitlab_rails['smtp_enable'] = true 
gitlab_rails['smtp_address'] = "smtp.exmail.qq.com"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "***#**"
gitlab_rails['smtp_password'] = "**************"
gitlab_rails['smtp_domain'] = "qq.com"
gitlab_rails['smtp_authentication'] = :login 
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = true
gitlab_rails['gitlab_email_from'] = "***#**"
user["git_user_email"] = "***#**"

然后生效重启,打开http://<你的服务器IP>:60200访问,

Jenkins

准备工作:

wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u151-b12/e758a0de34e24606bca991d704f6dcbf/jdk-8u151-linux-x64.tar.gz"
tar xzvf jdk-8u151-linux-x64.tar.gz -C /usr/local/  
vim /etc/profile #(加入环境变量)
    export JAVA_HOME=/usr/local/jdk1.8.0_151
    export PATH=$JAVA_HOME/bin:$PATH

退出,source /etc/profile 生效,用 java -version 验证是否装好java。

开始安装:

curl -O http://mirrors.jenkins.io/war-stable/latest/jenkins.war 下载Jenkins,
nohup java -jar jenkins.war –httpPort=60203 & 后台启动并指定端口。
至此,Jenkins安装成功,可以用浏览器打开 http://<你的服务器ip>:60203
然后安装必要的插件(会提示你),依次点击 “系统管理” “管理插件”。
切换到“可选插件”,分别搜索“GitLab Plugin”和“Git Plugin”,然后点击“直接安装”。如果在“可选插件”里没有搜到,可能自带安装了

Node

apt-get update
apt-get install -y build-essential curl
curl -sL https://deb.nodesource.com/setup_8.x | bash 
apt-get install -y nodejs
node -v
v8.9.2
npm -v
5.5.1

Rsync

这个服务器主要使用Rsync来发布文件,所以不需要特殊配置,一般Linux都默认安装了,如果没有,则使用 apt-get install rsync。然后配置Rsync密码

echo "123" >> /etc/rsync.password
chmod -R 600 /etc/rsync.password

服务器B

Node

如A

PM2

npm install -g pm2
pm2 -v
2.8.0

Rsync

为了安全性不要直接使用ssh账号密码或者公钥私钥,而是构建Rsync服务。vim /etc/rsyncd.conf,修改配置,下面的配置只是一个示例,生产环境还要更安全的策略。

##rsyncd.conf start##
uid = root
gid = root
use chroot = yes
max connections = 200
timeout = 300
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd.log
ignore errors
read only = false
list = false
hosts allow = * 
hosts deny =10.0.8.9
auth users = backuser
secrets file = /etc/rsync.password
[webapp]
path = /var/webapp/

上面的的路径path不存在则需要创建 mkdir /var/webapp
echo “backuser:123” >> /etc/rsync.password 添加账号和密码,密码要与客户端(A)一直
chmod -R 600 /etc/rsync.password 修改权限
rsync –daemon 以守护进程方式来启动rsync服务
chkconfig rsync on将此服务设置成为开机自启动

应用开发

用express开发一个 hello world 作为演示,在本地工程目录

npm init #(按照提示输入参数)
npm install express --save  #(安装express)

然后创建app.js

var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

node app.js 运行,然后http://localhost:3000/ 会得到 hello world

新建app.json

{
    "apps" : [
        {
            "name"        : "app",
            "script"      : "app.js",
            "log_date_format"  : "YYYY-MM-DD HH:mm:SS",
            "env": {
                "NODE_ENV": "production"
            },
            "watch" : [
                "app.js",
                "router",
                "util"
            ],
            "ignore_watch" : [
                "logs",
                "node_modules",
                "test"
            ]
        }
    ]
}

将代码上传至服务器B,然后 pm2 start app.json 运行 即可在浏览器访问 http://B-ip:3000 得到 hello world

持续集成和自动部署

配置 Gitlab

首次登陆的密码是会提示你去服务器找,用户是root,然后修改你的用户账号信息,添加你自己常用的电脑上的git公钥。
创建一个新项目 webapp ,创建好过后项目会显示该项目对应的用户信息(会提示你修改)

Git global setup

git config --global user.name "MageekChiu"
git config --global user.email "mageekchiu@mail.**.cn"

在本地项目目录下,新建 .gitignore 文件(window 要用 命令行 rename才可以)

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

node_modules/

然后执行

git init
git remote add origin git@A-ip:root/webapp.git
git add .
git commit -m "Initial commit"
git push -u origin master

即可提交服务器仓库,以后每次修改都要

git add .
git commit -m "修改备注"

配置 jenkins

首先配置GitLab插件:

打开GitLab,点击“setting”——“Account”,复制“Private token”,或者应该首先生成personal access token。
打开Jenkins,点击“系统管理”——“系统设置”,点击“配置”下拉框,点击“Gitlab”选项配置GitLab
Connection Name 随便,如 gitlab,“Git Host URL”填GitLab的访问地址,
然后Credentials点“Add”——“jenkins”,在弹出框里,“kind”选择“GitLab API Token”,将先前复制的“Private token”粘贴到“API token”输入框中,然后点击“Add”,添加后,Credentials选择刚刚新建的gitlab,
最后点击“test connection”,看到“Success”就成功了。然后点击页面底下的“apply”,再点击“save”

然后配置Git插件:

需要注意的是装Jenkins的机器上一定要装git: apt-get install git 这样Jenkins才能去 gitlab 拉取文件。
打开Jenkins,点击“系统管理”——“系统设置”,点击“配置”下拉框,选择“Git plugin”选项,设置Git插件的全局配置,填入上面的 global setting 如 global user.name等,然后点击“apply”——“save”

成访问Gitlab的ssh秘钥:

打开GitLab,点击右上角的“setting”—— SSH Keys,就可以进入到添加界面。
在jenkins所在服务器上生成密钥
ssh-keygen -t rsa -C “root@<你服务器的ip地址>” -b 4096
ssh-keygen -t rsa -C “root@” -b 4096
全部按 Enter 使用默认值,这会生成一对公钥和私钥。打开公钥复制到gitlab的添加界面,然后点击“add key”,并记住私钥的存放路径。

创建一个Jenkins Job:

直接点新建,“item name”可以随便起,然后点击“构建一个自由风格的软件项目”,点击“OK”,至此,创建一个Job成功了。然后配置这个job,选择“源码管理”,选择“Git”,然后去GitLab中复制项目地址,粘贴到“Repository URL”,然后点击“credentials”后面的“Add”按钮

在弹出页面里面:
● Kind 选择 SSH Username with private key
● Username 填 root
● PrivateKey 选择 From a file on jenkins master ,然后将服务器的 私钥的存放路径(/root/.ssh/id_rsa ) 粘贴进去
然后点击“Add”,在“credentials”里选择我们刚刚创建的认证方式。如果没报错,说明成功了,点击页面底部的“apply”。如果出错了,会在“Repository URL”和“Credentials”之间显示红色的错误信息。

选择 构建触发器:
选择 Build when a change is pushed to GitLab. 记住这个 GitLab CI Service URL ,点击高级
Secret token 那一行下面 点击 generate。记住这个token
选择 构建:
选择 execute shell

npm install 
WEB_SERVER_IP=B的ip
PROJECT=webapp/
rsync -arqz --delete-before $WORKSPACE/ $WEB_SERVER_IP::$PROJECT --exclude ".git" --password-file=/etc/rsync.password 

这一段代码的主要目的是构建,并分发代码,如果有多个应用服务器就要分发到多个服务器上。

配置gitab的webhook:

点击webapp 项目下面的setting的integrations 输入刚才的 GitLab CI Service URL 和 Secret Token
然后点击add webhook ,再测试一下选择 push events 如果显示Hook executed successfully: HTTP 200 即成功,然后在jenkins里面查看是有一次构建记录的。

这样jenkins就会在代码发生变化时自动拉取代码到本地,构建,然后用rsync分发给各个应用服务器,结合PM2的watch功能实现自动发现代码更新并重启的功能,达到自动部署的目的

最终效果测试

修改代码,把hello world改为hello gitlab+enkins然后 add、commit、push 。在A上面gitlab有提交记录,jenkins有构建记录,在B上面用 pm2 ls 发现项目是restart了,浏览器查看也变成hello gitlab+enkins 了。
尝试成功!
虽然这个配置比较麻烦,但是持续集成和自动部署的带来的好处是更大的:代码有版本管理,可以快速迭代、快速回滚,同时保持高质量、自动多机部署防止人工错误,每次构建都有记录,构建幂等……

后记

这个过程已经比较自动化了,但是还是有太多的环境搭建过程,比如webapp一般都会用到mysql、redis、MongoDB等等,一个更自动化的过程应该引入docker,这方面以后有机会再尝试。

Gitlab使用非绑定的Nginx

1、编辑gitlab.rb

vi /etc/gitlab/gitlab.rb

配置如下:

nginx['enable'] = false
#nginx的用户
web_server['external_users'] = ['nginx']
#nginx服务器的地址
gitlab_rails['trusted_proxies'] = [ '192.168.1.0/24', '192.168.2.1', '2001:0db8::/32' ]

2、配置Nginx

vi /etc/nginx/conf.d/gitlab-omnibus-nginx.conf

添加以下内容:

## GitLab 8.3+
##
## Lines starting with two hashes (##) are comments with information.
## Lines starting with one hash (#) are configuration parameters that can be uncommented.
##
##################################
## CONTRIBUTING ##
##################################
##
## If you change this file in a Merge Request, please also create
## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
##
###################################
## configuration ##
###################################
##
## See installation.md#using-https for additional HTTPS configuration details.

upstream gitlab-workhorse {
server unix:/var/opt/gitlab/gitlab-workhorse/socket;
}

## Normal HTTP host
server {
## Either remove "default_server" from the listen line below,
## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
## to be served if you visit any address that your server responds to, eg.
## the ip address of the server (http://x.x.x.x/)n 0.0.0.0:80 default_server;
listen 0.0.0.0:80 default_server;
listen [::]:80 default_server;
server_name git.dream7788.com; ## Replace this with something like gitlab.example.com
server_tokens off; ## Don't show the nginx version number, a security best practice
root /opt/gitlab/embedded/service/gitlab-rails/public;

## See app/controllers/application_controller.rb for headers set

## Individual nginx logs for this GitLab vhost
access_log /var/log/nginx/gitlab_access.log;
error_log /var/log/nginx/gitlab_error.log;

location / {
client_max_body_size 0;
gzip off;

## https://github.com/gitlabhq/gitlabhq/issues/694
## Some requests take more than 30 seconds.
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;

proxy_http_version 1.1;

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

proxy_pass http://gitlab-workhorse;
}
}

添加nginx用户到git用户组

usermod -a -G git nginx

3、重新配置gitlab

gitlab-ctl reconfigure
gitlab-ctl start

CI/CD 之 GitLab CI

接着上篇文章整理,这篇文章主要介绍一下 GitLab CI 相关功能,并通过 GitLab CI 实现自动化构建项目;项目中所用的示例项目已经上传到了 GitHub

一、环境准备

首先需要有一台 GitLab 服务器,然后需要有个项目;这里示例项目以 Spring Boot 项目为例,然后最好有一台专门用来 Build 的机器,实际生产中如果 Build 任务不频繁可适当用一些业务机器进行 Build;本文示例所有组件将采用 Docker 启动, GitLab HA 等不在本文阐述范围内

  • Docker Version : 1.13.1
  • GitLab Version : 10.1.4-ce.0
  • GitLab Runner Version : 10.1.0
  • GitLab IP : 172.16.0.37
  • GitLab Runner IP : 172.16.0.36

二、GitLab CI 简介

GitLab CI 是 GitLab 默认集成的 CI 功能,GitLab CI 通过在项目内 .gitlab-ci.yaml 配置文件读取 CI 任务并进行相应处理;GitLab CI 通过其称为 GitLab Runner 的 Agent 端进行 build 操作;Runner 本身可以使用多种方式安装,比如使用 Docker 镜像启动等;Runner 在进行 build 操作时也可以选择多种 build 环境提供者;比如直接在 Runner 所在宿主机 build、通过新创建虚拟机(vmware、virtualbox)进行 build等;同时 Runner 支持 Docker 作为 build 提供者,即每次 build 新启动容器进行 build;GitLab CI 其大致架构如下
未分类

三、搭建 GitLab 服务器

3.1、GitLab 搭建

GitLab 搭建这里直接使用 docker compose 启动,compose 配置如下

version: '2'
services:
  gitlab:
    image: 'gitlab/gitlab-ce:10.1.4-ce.0'
    restart: always
    container_name: gitlab
    hostname: 'git.mritd.me'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http:/git.mritd.me'
        # Add any other gitlab.rb configuration here, each on its own line
    ports:
      - '80:80'
      - '443:443'
      - '8022:22'
    volumes:
      - './data/gitlab/config:/etc/gitlab'
      - './data/gitlab/logs:/var/log/gitlab'
      - './data/gitlab/data:/var/opt/gitlab'

直接启动后,首次登陆需要设置初始密码如下,默认用户为 root
未分类
登陆成功后创建一个用户(该用户最好给予 Admin 权限,以后操作以该用户为例),并且创建一个测试 Group 和 Project,如下所示
未分类
未分类

3.2、增加示例项目

这里示例项目采用 Java 的 SpringBoot 项目,并采用 Gradle 构建,其他语言原理一样;如果不熟悉 Java 的没必要死磕此步配置,任意语言(最好 Java)整一个能用的 Web 项目就行,并不强求一定 Java 并且使用 Gradle 构建,以下只是一个样例项目;SpringBoot 可以采用 Spring Initializr 直接生成(依赖要加入 WEB),如下所示
未分类
将项目导入 IDEA,然后创建一个 index 示例页面,主要修改如下
– build.gradle

buildscript {
    ext {
        springBootVersion = '1.5.8.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'

group = 'me.mritd'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}
  • 新建一个 HomeController
package me.mritd.TestProject;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/*******************************************************************************
 * Copyright (c) 2005-2017 Mritd, Inc.
 * TestProject
 * me.mritd.TestProject
 * Created by mritd on 2017/11/24 下午12:23.
 * Description: 
 *******************************************************************************/
@Controller
public class HomeController {

    @RequestMapping("/")
    public String home(){
        return "index";
    }
}
  • templates 下新建 index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
</head>
<body>
<h1>Test...</h1>
</body>
</html>

最后项目整体结构如下
未分类
执行 assemble Task 打包出可执行 jar 包,并运行 java -jar TestProject-0.0.1-SNAPSHOT.jar 测试下能启动访问页面即可
未分类
最后将项目提交到 GitLab 后如下
未分类

四、GitLab CI 配置

针对这一章节创建基础镜像以及项目镜像,这里仅以 Java 项目为例;其他语言原理相通,按照其他语言对应的运行环境修改即可

4.1、增加 Runner

GitLab CI 在进行构建时会将任务下发给 Runner,让 Runner 去执行;所以先要添加一个 Runner,Runner 这里采用 Docker Compose 启动,build 方式也使用 Docker 方式 Build;compose 文件如下

version: '2'
services:
  gitlab-runner:
    container_name: gitlab-runner
    image: gitlab/gitlab-runner:alpine-v10.1.0
    restart: always
    network_mode: "host"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./config.toml:/etc/gitlab-runner/config.toml
    extra_hosts:
      - "git.mritd.me:172.16.0.37"

在启动前,我们需要先 touch 一下这个 config.toml 配置文件;该文件是 Runner 的运行配置,此后 Runner 所有配置都会写入这个文件(不 touch 出来 docker-compose 发现不存在会挂载一个目录进去,导致 Runner 启动失败);启动 docker-compose 后,需要进入容器执行注册,让 Runner 主动去连接 GitLab 服务器

# 生成 Runner 配置文件
touch config.toml
# 启动 Runner
docker-compose up -d
# 激活 Runner
docker exec -it gitlab-runner gitlab-runner register

在执行上一条激活命令后,会按照提示让你输入一些信息;首先输入 GitLab 地址,然后是 Runner Token,Runner Token 可以从 GitLab 设置中查看,如下所示
未分类
整体注册流程如下
未分类
注册完成后,在 GitLab Runner 设置中就可以看到刚刚注册的 Runner,如下所示
未分类
Runner 注册成功后会将配置写入到 config.toml 配置文件;由于两个测试宿主机都没有配置内网 DNS,所以为了保证 runner 在使用 docker build 时能正确的找到 GitLab 仓库地址,还需要增加一个 docker 的 host 映射( extra_hosts );同时为了能调用 宿主机 Docker 和持久化 build 的一些缓存还挂载了一些文件和目录;完整的 配置如下(配置文件可以做一些更高级的配置,具体参考 官方文档 )
– config.toml

concurrent = 1
check_interval = 0

[[runners]]
  name = "Test Runner"
  url = "http://git.mritd.me"
  token = "c279ec1ac08aec98c7141c7cf2d474"
  executor = "docker"
  builds_dir = "/gitlab/runner-builds"
  cache_dir = "/gitlab/runner-cache"
  [runners.docker]
    tls_verify = false
    image = "debian"
    privileged = false
    disable_cache = false
    shm_size = 0
    volumes = ["/data/gitlab-runner:/gitlab","/var/run/docker.sock:/var/run/docker.sock","/data/maven_repo:/data/repo","/data/maven_repo:/data/maven","/data/gradle:/data/gradle","/data/sonar_cache:/root/.sonar","/data/androidsdk:/usr/local/android","/data/node_modules:/data/node_modules"]
    extra_hosts = ["git.mritd.me:172.16.0.37"]
  [runners.cache]

注意,这里声明的 Volumes 会在每个运行的容器中都生效;也就是说 build 时新开启的每个容器都会被挂载这些目录;修改完成后重启 runner 容器即可,由于 runner 中没啥可保存的东西,所以可以直接 docker-compose down && docker-compose up -d 重启

4.2、创建基础镜像

由于示例项目是一个 Java 项目,而且是采用 Spring Boot 的,所以该项目想要运行起来只需要一个 java 环境即可,中间件已经被打包到了 jar 包中;以下是一个作为基础运行环境的 openjdk 镜像的 Dockerfile

FROM alpine:edge 

LABEL maintainer="mritd <[email protected]>"

ENV JAVA_HOME /usr/lib/jvm/java-1.8-openjdk
ENV PATH $PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin

RUN apk add --update bash curl tar wget ca-certificates unzip 
        openjdk8 font-adobe-100dpi ttf-dejavu fontconfig 
    && rm -rf /var/cache/apk/* 

CMD ["bash"]

这个 openjdk Dockerfile 升级到了 8.151 版本,并且集成了一些字体相关的软件,以解决在 Java 中某些验证码库无法运行问题,详见 Alpine 3.6 OpenJDK 8 Bug;使用这个 Dockerfile,在当前目录执行 docker build -t mritd/openjdk:8 . build 一个 openjdk8 的基础镜像,然后将其推送到私服,或者 Docker Hub 即可

4.3、创建项目镜像

有了基本的 openjdk 的 docker 镜像后,针对于项目每次 build 都应该生成一个包含发布物的 docker 镜像,所以对于项目来说还需要一个项目本身的 Dockerfile;项目的 Dockerfile 有两种使用方式;一种是动态生成 Dockerfile,然后每次使用新生成的 Dockerfile 去 build;还有一种是写一个通用的 Dockerfile,build 时利用 ARG 参数传入变量;这里采用第二种方式,以下为一个可以反复使用的 Dockerfile

FROM mritd/openjdk:8-144-01

MAINTAINER mritd <[email protected]>

ARG PROJECT_BUILD_FINALNAME

ENV TZ 'Asia/Shanghai'
ENV PROJECT_BUILD_FINALNAME ${PROJECT_BUILD_FINALNAME}


COPY build/libs/${PROJECT_BUILD_FINALNAME}.jar /${PROJECT_BUILD_FINALNAME}.jar

CMD ["bash","-c","java -jar /${PROJECT_BUILD_FINALNAME}.jar"]

该 Dockerfile 通过声明一个 PROJECT_BUILD_FINALNAME 变量来表示项目的发布物名称;然后将其复制到根目录下,最终利用 java 执行这个 jar 包;所以每次 build 之前只要能拿到项目发布物的名称即可

4.4、Gradle 修改

上面已经创建了一个标准的通用型 Dockerfile,每次 build 镜像只要传入 PROJECT_BUILD_FINALNAME 这个最终发布物名称即可;对于发布物名称来说,最好不要固定死;当然不论是 Java 还是其他语言的项目我们都能将最终发布物变成一个固定名字,最不济可以写脚本重命名一下;但是不建议那么干,最好保留版本号信息,以便于异常情况下进入容器能够分辨;对于当前 Java 项目来说,想要拿到 PROJECT_BUILD_FINALNAME 很简单,我们只需要略微修改一下 Gradle 的 build 脚本,让其每次打包 jar 包时将项目的名称及版本号导出到文件中即可;同时这里也加入了镜像版本号的处理,Gradle 脚本修改如下
– build.gradle 最后面增加如下

bootRepackage {

    mainClass = 'me.mritd.TestProject.TestProjectApplication'
    executable = true

    doLast {
        File envFile = new File("build/tmp/PROJECT_ENV")

        println("Create ${archivesBaseName} ENV File ===> " + envFile.createNewFile())
        println("Export ${archivesBaseName} Build Version ===> ${version}")
        envFile.write("export PROJECT_BUILD_FINALNAME=${archivesBaseName}-${version}n")

        println("Generate Docker image tag...")
        envFile.append("export BUILD_DATE=`date +%Y%m%d%H%M%S`n")
        envFile.append("export IMAGE_NAME=mritd/test:`echo ${CI_BUILD_REF_NAME} | tr '/' '-'`-`echo ${CI_COMMIT_SHA} | cut -c1-8`-${BUILD_DATE}n")
        envFile.append("export LATEST_IMAGE_NAME=mritd/test:latestn")
    }
}

这一步操作实际上是修改了 bootRepackage 这个 Task(不了解 Gradle 或者不是 Java 项目的请忽略),在其结束后创建了一个叫 PROJECT_ENV 的文件,里面实际上就是写入了一些 bash 环境变量声明,以方便后面 source 一下这个文件拿到一些变量,然后用户 build 镜像使用,PROJECT_ENV 最终生成如下

export PROJECT_BUILD_FINALNAME=TestProject-0.0.1-SNAPSHOT
export BUILD_DATE=`date +%Y%m%d%H%M%S`
export IMAGE_NAME=mritd/test:`echo ${CI_BUILD_REF_NAME} | tr '/' '-'`-`echo ${CI_COMMIT_SHA} | cut -c1-8`-${BUILD_DATE}
export LATEST_IMAGE_NAME=mritd/test:latest

未分类

4.5、创建 CI 配置文件

一切准备就绪以后,就可以编写 CI 脚本了;GitLab 依靠读取项目根目录下的 .gitlab-ci.yml 文件来执行相应的 CI 操作;以下为测试项目的 .gitlab-ci.yml 配置

# 调试开启
#before_script:
#  - pwd
#  - env

cache:
  key: $CI_PROJECT_NAME/$CI_COMMIT_REF_NAME-$CI_COMMIT_SHA
  paths:
    - build

stages:
  - build
  - deploy

auto-build:
  image: mritd/build:2.1.1
  stage: build
  script:
    - gradle --no-daemon clean assemble
  tags:
    - test

deploy:
  image: mritd/docker-kubectl:v1.7.4
  stage: deploy
  script:
    - source build/tmp/PROJECT_ENV
    - echo "Build Docker Image ==> ${IMAGE_NAME}"
    - docker build -t ${IMAGE_NAME} --build-arg PROJECT_BUILD_FINALNAME=${PROJECT_BUILD_FINALNAME} .
#    - docker push ${IMAGE_NAME}
    - docker tag ${IMAGE_NAME} ${LATEST_IMAGE_NAME}
#    - docker push ${LATEST_IMAGE_NAME}
#    - docker rmi ${IMAGE_NAME} ${LATEST_IMAGE_NAME}
#    - kubectl --kubeconfig ${KUBE_CONFIG} set image deployment/test test=$IMAGE_NAME
  tags:
    - test
  only:
    - master
    - develop
    - /^chore.*$/

关于 CI 配置的一些简要说明如下

stages
stages 字段定义了整个 CI 一共有哪些阶段流程,以上的 CI 配置中,定义了该项目的 CI 总共分为 build、deploy 两个阶段;GitLab CI 会根据其顺序执行对应阶段下的所有任务;在正常生产环境流程可以定义很多个,比如可以有 test、publish,甚至可能有代码扫描的 sonar 阶段等;这些阶段没有任何限制,完全是自定义的,上面的阶段定义好后在 CI 中表现如下图
未分类

task
task 隶属于 stages 之下;也就是说一个阶段可以有多个任务,任务执行顺序默认不指定会并发执行;对于上面的 CI 配置来说 auto-build 和 deploy 都是 task,他们通过 stage: xxxx 这个标签来指定他们隶属于哪个 stage;当 Runner 使用 Docker 作为 build 提供者时,我们可以在 task 的 image 标签下声明该 task 要使用哪个镜像运行,不指定则默认为 Runner 注册时的镜像(这里是 debian);同时 task 还有一个 tags 的标签,该标签指明了这个任务将可以在哪些 Runner 上运行;这个标签可以从 Runner 页面看到,实际上就是 Runner 注册时输入的哪个 tag;对于某些特殊的项目,比如 IOS 项目,则必须在特定机器上执行,所以此时指定 tags 标签很有用,当 task 运行后如下图所示
未分类
除此之外 task 还能指定 only 标签用于限定那些分支才能触发这个 task,如果分支名字不满足则不会触发;默认情况下,这些 task 都是自动执行的,如果感觉某些任务太过危险,则可以通过增加 when: manual 改为手动执行;注意: 手动执行被 GitLab 认为是高权限的写操作,所以只有项目管理员才能手动运行一个 task,直白的说就是管理员才能点击;手动执行如下图所示
未分类

cache
cache 这个参数用于定义全局那些文件将被 cache;在 GitLab CI 中,跨 stage 是不能保存东西的;也就是说在第一步 build 的操作生成的 jar 包,到第二部打包 docker image 时就会被删除;GitLab 会保证每个 stage 中任务在执行时都将工作目录(Docker 容器 中)还原到跟 GitLab 代码仓库中一模一样,多余文件及变更都会被删除;正常情况下,第一步 build 生成 jar 包应当立即推送到 nexus 私服;但是这里测试没有搭建,所以只能放到本地;但是放到本地下一个 task 就会删除它,所以利用 cache 这个参数将 build 目录 cache 住,保证其跨 stage 也能存在

关于 .gitlab-ci.yml 具体配置更完整的请参考 官方文档

五、其他相关

5.1、GitLab 内置环境变量

上面已经基本搞定了一个项目的 CI,但是有些变量可能并未说清楚;比如在创建的 PROJECT_ENV 文件中引用了 ${CI_COMMIT_SHA} 变量;这种变量其实是 GitLab CI 的内置隐藏变量,这些变量在每次 CI 调用 Runner 运行某个任务时都会传递到对应的 Runner 的执行环境中;也就是说这些变量在每次的任务容器 SHELL 环境中都会存在,可以直接引用,具体的完整环境变量列表可以从 官方文档 中获取;如果想知道环境变量具体的值,实际上可以通过在任务执行前用 env 指令打印出来,如下所示
未分类
未分类

5.2、GitLab 自定义环境变量

在某些情况下,我们希望 CI 能自动的发布或者修改一些东西;比如将 jar 包上传到 nexus、将 docker 镜像 push 到私服;这些动作往往需要一个高权限或者说有可写入对应仓库权限的账户来支持,但是这些账户又不想写到项目的 CI 配置里;因为这样很不安全,谁都能看到;此时我们可以将这些敏感变量写入到 GitLab 自定义环境变量中,GitLab 会像对待内置变量一样将其传送到 Runner 端,以供我们使用;GitLab 中自定义的环境变量可以有两种,一种是项目级别的,只能够在当前项目使用,如下
未分类
另一种是组级别的,可以在整个组内的所有项目中使用,如下
未分类
这两种变量添加后都可以在 CI 的脚本中直接引用

5.3、Kubernetes 集成

对于 Kubernetes 集成实际上有两种方案,一种是对接 Kubernetes 的 api,纯代码实现;另一种取巧的方案是调用 kubectl 工具,用 kubectl 工具来实现滚动升级;这里采用后一种取巧的方式,将 kubectl 二进制文件封装到镜像中,然后在 deploy 阶段使用这个镜像直接部署就可以
未分类
其中 mritd/docker-kubectl:v1.7.4 这个镜像的 Dockerfile 如下

FROM docker:dind 

LABEL maintainer="mritd <[email protected]>"

ARG TZ="Asia/Shanghai"

ENV TZ ${TZ}

ENV KUBE_VERSION v1.8.0

RUN apk upgrade --update 
    && apk add bash tzdata wget ca-certificates 
    && wget https://storage.googleapis.com/kubernetes-release/release/${KUBE_VERSION}/bin/linux/amd64/kubectl -O /usr/local/bin/kubectl 
    && chmod +x /usr/local/bin/kubectl 
    && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime 
    && echo ${TZ} > /etc/timezone 
    && rm -rf /var/cache/apk/*

CMD ["/bin/bash"]

这里面的 ${KUBE_CONFIG} 是一个自定义的环境变量,对于测试环境我将配置文件直接挂载入了容器中,然后 ${KUBE_CONFIG} 只是指定了一个配置文件位置,实际生产环境中可以选择将配置文件变成自定义环境变量使用

5.4、GitLab CI 总结

关于 GitLab CI 上面已经讲了很多,但是并不全面,也不算太细致;因为这东西说起来实际太多了,现在目测已经 1W 多字了;以下总结一下 GitLab CI 的总体思想,当思路清晰了以后,我想后面的只是查查文档自己试一试就行了

CS 架构
GitLab 作为 Server 端,控制 Runner 端执行一系列的 CI 任务;代码 clone 等无需关心,GitLab 会自动处理好一切;Runner 每次都会启动新的容器执行 CI 任务

容器即环境
在 Runner 使用 Docker build 的前提下;所有依赖切换、环境切换应当由切换不同镜像实现,即 build 那就使用 build 的镜像,deploy 就用带有 deploy 功能的镜像;通过不同镜像容器实现完整的环境隔离

CI即脚本
不同的 CI 任务实际上就是在使用不同镜像的容器中执行 SHELL 命令,自动化 CI 就是执行预先写好的一些小脚本

敏感信息走环境变量
一切重要的敏感信息,如账户密码等,不要写到 CI 配置中,直接放到 GitLab 的环境变量中;GitLab 会保证将其推送到远端 Runner 的 SHELL 变量中