持续集成和部署相关概念
名称 | 说明 |
---|---|
DevOps | DevOps 是一种方法论,是一组过程、方法与系统的统称,用于促进应用开发、应用运维和质量保障(QA)部门之间的沟通、协作与整合。 |
AD | 敏捷开发(Agile Development) |
CI | 持续集成(Continuous Integration),例如免费版Gitlab+Gitlab CI、Gitlab+Jenkins |
CD | 持续交付或部署(Continuous Delivery or Deployment) |
持续集成和部署完整流程图
1 安装gitlab
GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务,具体使用详情查看gitlab官网。可以直接命令行安装或在docker上安装gitlab。
1.1 直接命令安装gitlab
使用vagrant创建一个新的虚拟机,虚拟机自动安装了docker,gitlab,配置文件Vagrantfile内容如下:
Vagrant.require_version ">= 1.6.0"
opts = {
:name => "gitlab",
:mem => "4096",
:cpu => "2",
:private_ip => "192.168.6.100",
:script_path => "./setup.sh"
}
Vagrant.configure(2) do |config|
config.vm.box = "centos/7"
config.vm.define opts[:name] do |demo|
demo.vm.hostname = opts[:name]
demo.vm.provider "virtualbox" do |v|
v.customize ["modifyvm", :id, "--memory", opts[:mem]]
v.customize ["modifyvm", :id, "--cpus", opts[:cpu]]
end
demo.vm.network "private_network", ip: opts[:private_ip]
demo.vm.provision "shell", path: opts[:script_path]
end
end
Vagrantfile调用的外部脚本文件setup.sh内容如下: 如果不是使用虚拟机,直接执行下面脚本安装gitlab和docker。
#/bin/sh
# 设置时区
/bin/cp -rf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# -------------------------安装docker-----------------------------
# 安装一些工具
yum install -y git vim gcc glibc-static telnet bridge-utils net-tools
# 安装docker
yum -y install docker
# 把用户vagrant添加docker用户组
groupadd docker
gpasswd -a vagrant docker
# 设置开机启动docker和启动docker
systemctl enable docker
systemctl start docker
# 查看docker版本信息
docker version
# ---------------------------安装gitlab---------------------------
# 更新和安装一些工具
yum install -y upgrade
yum install -y net-tools curl policycoreutils java-1.8.0-openjdk.x86_64
# 开启sshd服务
yum install -y openssh-server openssh-clients
systemctl enable sshd
systemctl start sshd
# 开启邮件服务
yum install -y postfix
systemctl enable postfix
systemctl start postfix
# 打开防火墙
firewall-cmd --permanent --add-service=http
systemctl reload firewalld
# 安装gitlab
curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
yum install -y gitlab-ce
# 启动gitlab
gitlab-ctl reconfigure
# 查看启动状态
gitlab-ctl status
安装完gitlab后,使用命令vagrant ssh gitlab进入虚拟机,修改gitlab配置,其中域名配置为自己使用的域名。
vim /etc/gitlab/gitlab.rb
# 找到字段external_url,修改为自己的域名
# 重启gitlab
gitlab-ctl reconfigure
1.2 在docker上安装gitlab
如果不想直接安装gitlab,可以使用docker安装gitlab,使用docker安装时需要添加port,hostname, volume参数,这些参数根据自己情况设定。
# 拉取gitlab镜像
docker pull gitlab/gitlab-ce
# 启动gitlab容器
docker run --detach \
--hostname demo.gitlab.com \
--publish 8443:443 --publish 8080:80 --publish 2222:22 \
--name gitlab \
--restart always \
--volume /usr/local/gitlab/config:/etc/gitlab \
--volume /usr/local/gitlab/logs:/var/log/gitlab \
--volume /data/gitlab:/var/opt/gitlab \
gitlab/gitlab-ce
1.3 在本地为gitlab添加一个测试域名
如果是在云上部署gitlab,又有实名认证过的域名,直接用域名解析gitlab安装的云主机ip即可。
这里在本地使用测试域名 demo.gitlab.com,首先查看虚拟机ip地址:
ip addr
然后在宿主机的/etc/hosts文件上添加一个域名和虚拟机ip地址映射(例如:192.168.6.100 demo.gitlab.com),然后在宿主机上测试是否能够ping通:
ping demo.gitlab.com
能够ping通说明域名解析成功。然后打开在浏览器输入地址 http://demo.gitlab.com 访问gitlab,第一次访问需要修改root用户密码,修改密码后重新登录,输入管理员账号root和密码登录。
1.4 测试一个简单项目helloworld
进入gitlab主界面,如果需要中文,可以在个人settings–>Preferred language选择语言,然后刷新页面即可。
首先创建一个组demo,填写组信息,然后在组里创建一个项目helloworld,填写项目信息,创建完后就可以获得项目地址,复制项目地址,然后在终端克隆helloworld项目到本地。
然后进入本地项目helloworld文件夹,添加个README.md,随便填写内容,保存后提交到gitlab,在gitlab上helloworld项目下是否有看到README.md文件,有说明gitlab运行正常。
2 安装gitlab runner
gitLab runner 是用来运行定制的自定义任务(jobs),并把每个jobs执行结果返回给gitLab,配合gitLab内置的持续集(CI)和持续部署(CD)协调完成任务。gitlab runner是天然支持分布式的,在任意地方都可以安装,前提条件是能够和gitlab网络连通即可。可以直接命令行安装和在docker上安装两张方式。具体使用详情看gitlab runner官网。
2.1 命令行方式安装gitlab runner
使用vagrant创建一个新的虚拟机,虚拟机自动安装了docker,gitlab runner,注意gitlab和gitlab runner不要直接安装在同一个虚拟机上。配置文件Vagrantfile内容如下:
Vagrant.require_version ">= 1.6.0"
opts = {
:name => "gitlab-runner",
:mem => "2048",
:cpu => "2",
:private_ip => "192.168.6.200",
:script_path => "./setup.sh"
}
Vagrant.configure(2) do |config|
config.vm.box = "centos/7"
config.vm.define opts[:name] do |demo|
demo.vm.hostname = opts[:name]
demo.vm.provider "virtualbox" do |v|
v.customize ["modifyvm", :id, "--memory", opts[:mem]]
v.customize ["modifyvm", :id, "--cpus", opts[:cpu]]
end
demo.vm.network "private_network", ip: opts[:private_ip]
demo.vm.provision "shell", path: opts[:script_path]
end
end
Vagrantfile调用的外部脚本文件setup.sh内容如下: 如果不是使用虚拟机,直接执行下面脚本安装gitlab runner和docker。
#/bin/sh
# 设置时区
/bin/cp -rf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# -------------------------安装docker-----------------------------
# 安装一些工具
yum install -y git vim gcc glibc-static telnet bridge-utils net-tools
# 安装docker
yum -y install docker
# 把用户vagrant添加docker用户组
groupadd docker
gpasswd -a vagrant docker
# 设置开机启动docker和启动docker
systemctl enable docker
systemctl start docker
# 查看docker版本信息
docker version
# ---------------------------安装gitlab runner---------------------------
# 更新和安装一些工具
yum install -y upgrade
yum install -y net-tools curl policycoreutils
# 开启sshd服务
yum install -y openssh-server openssh-clients
systemctl enable sshd
systemctl start sshd
# 打开防火墙
firewall-cmd --permanent --add-service=http
systemctl reload firewalld
# 安装gitlab runner
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash
yum install -y gitlab-runner
# 添加gitlab-runner用户到docker组,使得gitlab-runner可以直接操作docker
usermod -aG docker gitlab-runner
# 重启docker和gitlab runner服务
systemctl restart docker
gitlab-ci-multi-runner restart
# 查看gitlab runner运行状态
gitlab-ci-multi-runner status
2.2 在docker上安装gitlab runner
valume参数根据自己情况设置。
# 拉取gitlab-runner镜像
docker pull gitlab/gitlab-runner
# 启动gitlab-runner容器
sudo docker run -d /
--name gitlab-runner /
--restart always /
-v /usr/loacal/gitlab-runner/config:/etc/gitlab-runner /
-v /usr/loacal/gitlab-runner/run/docker.sock:/var/run/docker.sock /
gitlab/gitlab-runner
安装完gitlab runner之后,需要把runner注册到gitlab服务中,让gitlab知道有这个runner存在。
因为当前使用本地测试域名,需要把测试域名和gitlab服务的ip地址添加到gitlab-runner虚拟机的/etc/hosts里。
vi /etc/hosts
# 添加域名解析
192.168.6.100 demo.gitlab.com
# 测试是否能够ping通
ping demo.gitlab.com
2.3 注册runner
如果在docker上安装的gitlab runner,需要进入docker操作
docker exec -it gitlab-runner bash
# 执行注册runner命令,根据提示填写信息
gitlab-ci-multi-runner register
# 填写gitlab域名
示例:http://demo.gitlab.com
# 填写token值,获取token值方式,例如你要为helloworld这个项目注册一个runner,打开浏览器进入helloworld项目,点击菜单setting --> CI/CD --> Runners --> Setup a specific Runner manually 的第三个就是helloworld项目token值。
示例:GidtNjipAo6Q9kYsHicy
# 填写描述
示例:test helloworld runner
# 填写tags,到时候需要指定tag去构建
示例:test,demo
# 填写runner执行器,ocker-ssh, ssh, docker+machine, kubernetes, docker, parallels, shell, virtualbox, docker-ssh+machine
示例:shell
# 查看runner列表,查看新注册的runner是否成功
gitlab-ci-multi-runner list
# 查看helloworld项目是否有runner注册成功,刷新一下页面点击菜单setting --> CI/CD --> Runners,展开查看Runners activated for this project下面是否有一个绿色runner。
3 一个简单的持续集成示例
以gitlab上的helloworld项目为例,当前默认是master分支上,新建一个runner配置文件.gitlab-ci.yml,具体语法参考官方文档。配置文件内容如下:
# 定义stages
stages:
- build
- test
- deploy
- release
# 在执行job之前可以设定当前工作目录,复制项目文件到工作目录等操作。
# before_script:
# 定义job
# (1)指定job的stage;
# (2)指定runner的tag;
# (3)自定义运行的脚本,这个脚本执行器是runner注册的时候指定的,例如注册时使用shell,script也是使用shell脚本命令。
# (4) 可以指定哪些动作执行当前job,例如only tags或except tags
job1:
stage: build
tags:
- demo
script:
- echo "I am job1"
- echo "I am in build stage"
except:
- tags
# 定义job
job2:
stage: test
tags:
- demo
script:
- echo "I am job2"
- echo "I am in test stage"
except:
- tags
# 定义job
job3:
stage: deploy
tags:
- demo
script:
- echo "I am job3"
- echo "I am in deploy stage"
except:
- tags
# 定义job
job4:
stage: release
tags:
- demo
script:
- echo "I am job4"
- echo "I am in release stage"
only:
- tags
gitlab runner根据配置文件.gitlab-ci.yml去执行各个job,执行结果如下图所示,如果第一个job执行失败,不会执行后面的job。
如果只发布稳定版本,只会执行job4
4 一些实际使用中gitlab的设置
4.1 禁止直接修改master分支
在实际项目中需要设置保护master分支,不能让然任何人直接push修改内容到master分支,必须通过合并方式,设置保护master分支,settings –> repository –> Protected Branches
4.2 设置允许合并请求条件
合并请求也可以设置成有条件的合并,例如设置pipeline成功才允许发送merge请求,设置merge请求条件,settings –> general –> Merge request
4.2 实时显示徽章
在README上显示徽章,徽章包括pipleline状态、测试覆盖率、当前版本、编译状态、许可证license等
settings –> CI/CD –> General pipelines
选项 | 说明 |
---|---|
Pipeline status | pipleline状态 |
Coverage report | 找到Test coverage parsing,根据对应语言填写正则表达式 |
也可以自定义设定Badaes,settings –> Badges
5 一个完整的golang持续集成和部署示例
5.1 介绍运行环境和项目结构
运行环境:一台主机,和2个centos7.3虚拟机
虚拟机名称 | ip地址 | 安装服务 |
---|---|---|
gitlab | 192.168.6.100 | gitlab服务、docker、docker私有仓库服务、本地DNS服务器,注:分配内存要4G以上 |
gitlab-ci | 192.168.6.200 | gitlib-runner服务、docker、自己制作的tool-go镜像 |
下面是一个完整的目录结构。
.
├── fileServer.go
├── fileServer_test.go
├── .gitlab-ci.yml
├── Makefile
├── README.md
└── scripts
├── coverage.sh
├── deploy_to_local
│ ├── deploy_to_local.sh
│ └── Dockerfile
├── deploy_to_remote
│ ├── automationDeployment.sh
│ ├── deploy_to_remote.sh
│ ├── Dockerfile
│ └── run_application.sh
└── release
├── Dockerfile
└── release.sh
与gitlab runner相关的目录介绍:
名称 | 说明 |
---|---|
.gitlab-ci.yml | gitlab runner运行规则配置文件,语法参考官方文档 |
Makefile | 为了使.gitlab-ci.yml配置文档的结构清晰简单,把各个job的scripts入口存放到Makefile里。 |
scripts | 存放Makefile里使用到的脚本文件,scripts里主要包含了自动部署到本地、自动部署到远程服务器、发布版本三大类 |
5.2 搭建一个本地DNS服务器
为了使docker的容器也能使用测试域名 demo.gitlab.com,在gitlab虚拟机的docker搭建一个DNS服务器。(当然也可以在其他机器上搭建一个DNS服务)
# 启动一个DNS服务器
docker run -d -p 53:53/udp --cap-add=NET_ADMIN --name dns-server --restart=always andyshinn/dnsmasq
# 进入容器
docker exec -it dns-server sh
# 配置DNS服务
vi /etc/resolv.dnsmasq
# 添加解析规则:
nameserver 114.114.114.114
nameserver 8.8.8.8
# 配置本地解析,其中 192.168.6.100是gitlab虚拟机的ip
vi /etc/dnsmasqhosts
# 添加解析规则:
192.168.6.100 demo.gitlab.com
# 修改DNS配置文件,使用自定义配置文件
vi /etc/dnsmasq.conf
# 修改内容:
resolv-file=/etc/resolv.dnsmasq
addn-hosts=/etc/dnsmasqhosts
# 重启DNS服务器
docker restart dns-server
# 回到gitlab-runner虚拟机,修改域名服务器解析地址,所有其他机器的域名解析指向DNS服务地址,都可以访问测试域名demo.gitlab.com。
vi /etc/resolv.conf
# 修改nameserver时,必须把其他nameserver去掉,192.168.6.100是DNS服务器所在机器的ip地址,注:修改的配置在系统重启后会自动复原,每次都要手动加上,可以写脚本系统启动后自动设置。
nameserver 192.168.6.100
5.3 搭建一个私人docker仓库
一般企业内部打包的镜像是不公开的,把镜像存放到私有仓库里,需要到找一台或多台主机搭建私有仓库,这里把私有仓库搭建在gitlab服务器上。在docker上启动一个管理镜像的容器:
docker run -d -v /opt/registry:/var/lib/registry -p 5000:5000 –restart=always –name=my-registry registry
测试私有仓库是否可用:
# 配置gitlab-runner所在服务器的docker,告诉docker私有仓库地址是可信赖的,注意:这里填写的是ip,如果填写域名,需要https证书。
vi /etc/docker/daemon.json
# 添加内容:{"insecure-registries":["192.168.6.100:5000"]}
# 重启docker服务
systemctl daemon-reload
systemctl restart docker
# 从官网拉一个镜像下来,看能不能push到私有仓库
docker pull busybox
docker tag busybox 192.168.6.100:5000/busybox
docker push 192.168.6.100:5000/busybox
# 从私有仓库拉取镜像
docker pull 192.168.6.100:5000/busybox
5.4 制作自定义的tool-go镜像
由于是在docker容器里检测项目,所以要自己去制作一个新镜像,并把推送到镜像仓库,最后在gitlab-runner的服务器上拉取该镜像。
首先翻墙下载golint包,如果不能翻墙,去github下载同名的包,下载后放到$GOPATH/src,然后改路径golang.org/x/lint/golint。
go get golang.org/x/lint/golint
在宿主机上的GOPATH/src/tool-go新建Dockerfile文件,文件内容如下:
FROM golang:1.9.2
MAINTAINER vison "vison@126.com"
USER root
# 安装golang代码风格检查的工具 golint
ENV GOPATH /go
ENV PATH ${GOPATH}/bin:$PATH
RUN mkdir -p /go/src/golang.org/x/lint/golint
COPY ../golang.org/x/lint/golint /go/src/golang.org/x/lint/golint
RUN go install golang.org/x/lint/golint
# 安装 docker,由于需要在容器里面使用宿主的docker命令,这里就需要安装一个docker的可执行文件,然后在启动容器的时候,将宿主的 /var/run/docker.sock 文件挂载到容器内的同样位置。
RUN curl -O https://get.docker.com/builds/Linux/x86_64/docker-latest.tgz \
&& tar zxvf docker-latest.tgz \
&& cp docker/docker /usr/local/bin/ \
&& rm -rf docker docker-latest.tgz
# 安装 expect,expect工具可以实现远程服务器端部署应用
RUN apt-get update
RUN apt-get -y install tcl tk expect
然后打包镜像
docker build -t tool-go:1.9.2 .
5.5 golang项目的检测项介绍
(1) 包列表
正如在官方文档中所描述的那样,go项目是包的集合。下面介绍的大多数工具都将使用这些包,因此我们需要的第一个命令是列出包的方法。我们可以用go list子命令来完成
go list ./…
如果我们要避免将我们的工具应用于外部资源,并将其限制在我们的代码中。 那么我们需要去除vendor 目录,命令如下:
go list ./… | grep -v /vendor/
(2) 单元测试
这些是您可以在代码中运行的最常见的测试。每个.go文件需要一个能支持单元测试的_test.go文件。可以使用以下命令运行所有包的测试:
go test -short $(go list ./… | grep -v /vendor/)
(3) 数据竞争
这通常是一个难以逃避解决的问题,go工具默认具有(但只能在linux / amd64、freebsd / amd64、darwin / amd64和windows / amd64上使用)
go test -race -short $(go list . /…| grep - v /vendor/)
(4) 内存检错
Clang有一个很好的读未初始化的内存检测器,称为MemorySanitizer。go测试工具能很好地与Clang模块进行交互(只要在linux / amd64主机上,并使用最新版本的Clang / LLVM(> = 3.8.0)。运行命令如下:
go test -msan -short $(go list . /…| grep - v /vendor/)
(5) 代码覆盖
这是评估代码的质量的必备工具,并能显示哪部分代码进行了单元测试,哪部分没有。要计算代码覆盖率,需要运行以下脚本:
PKG_LIST=$(go list ./... | grep -v /vendor/)
for package in ${PKG_LIST}; do
go test -covermode=count -coverprofile "cover/${package##*/}.cov" "$package" ;
done
tail -q -n +2 cover/*.cov >> cover/coverage.cov
go tool cover -func=cover/coverage.cov
如果我们想要获得HTML格式的覆盖率报告,我们需要添加以下命令:
go tool cover -html=cover/coverage.cov -o coverage.html
(6) golint
这是可选的检查代码风格、错误工具,它有助于在项目上保持一致的代码风格。
golint -set_exit_status $(go list ./… | grep -v /vendor/)
注意-set_exit_status选项。 默认情况下,golint仅输出样式问题,并带有返回值(带有0返回码),所以CI不认为是出错。 如果指定了-set_exit_status,则在遇到任何样式问题时,golint的返回码将不为0。
(7) 编译
最后一旦代码经过了完全测试,我们要对代码进行编译,从而构建可以执行的二进制文件。
go build
5.6 新建一个项目runner
在gitlab-runner服务器上,创建一个对应项目file-server新runner:
sudo gitlab-ci-multi-runner register
# Running in system-mode.
#Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
http://demo.gitlab.com
#Please enter the gitlab-ci token for this runner:
RKFj4GFZsBukNqrU4rCC
#Please enter the gitlab-ci description for this runner:
#Please enter the gitlab-ci tags for this runner (comma separated):
demo
#Registering runner... succeeded runner=RKFj4GFZ
#Please enter the executor: docker-ssh, virtualbox, docker+machine, #docker-ssh+machine, docker, shell, ssh, kubernetes, parallels:
docker
# Please enter the default Docker image (e.g. ruby:2.1):
# 新制作的镜像
tool-go:1.9.2
#Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
这是创建runner的基本信息,还需要补充相关docker配置相关信息。
# 打开runner配置文件,找到刚刚创建的runner
vim /etc/gitlab-runner/config.toml
# 完整的配置如下:
[[runners]]
name = "file"
url = "http://demo.gitlab.com"
token = "4e01f882e18534fc4f9a020de5fe4e"
executor = "docker"
[runners.docker]
tls_verify = false
image = "tool-go:1.9.2"
privileged = false
disable_cache = false
shm_size = 0
# 为了在容器中可以执行宿主机的docker命令
volumes = ["/var/run/docker.sock:/var/run/docker.sock"]
# 当指定的镜像不存在的话,则通过docker pull拉取
pull_policy = "if-not-present"
# 如果gitlab和gitlab-runner是安装在同一台主机的docker上,可以设置下面参数
# 给gitlab添加个host映射,映射到127.0.0.1
#extra_hosts = ["gitlab.chain.cn:127.0.0.1"]
# 使容器的网络与宿主机一致,只有这样才能通过127.0.0.1访问到gitlab。
#network_mode = "host"
[runners.cache]
gitlab-runner在执行的时候,会根据上面的配置启动一个容器,即配置中的tool-gos:1.9.2,其中所有的启动参数都会在[runners.docker]节点下配置好,包括挂载、网络等。容器启动成功之后,会使用这个容器去gitlab上pull代码,然后根据自己定义的规则进行检验,全部检测成功之后便是部署了。
5.7 定义runner规则
在gitlab项目根目录创建.gitlab-ci.yml文件,填写runner规则,具体语法课参考官方文档。
如果检测项目多而复杂的话,把所有规则都写到配置文件.gitlab-ci.yml,会使得.gitlab-ci.yml很复杂,通常把持续集成环境中使用的所有工具,全部打包在Makefile中,然后统一的方式调用,而且Makefile也可以调用*.sh脚本文件,可以分级调用,使得整体配置文件结构清晰简洁。
在golang代码基础上新建了.gitlab-ci.yml、Makefile文件和一个存放脚本文件的scripts文件夹。完整的项目代码地址 https://git.zhuyasen.com/devops-demo/file-server
5.7 遇到的问题
(1) job一直在pending状态
提交代码后,job突然一直pending状态,提示This job has not started yet,可能gitlab-runner出现了异常,重启一下gitlab-runner就正常了。
gitlab-ci-multi-runner restart
(2) 无权限执行脚本
.sh文件没有课执行权限,造成make报错make: execvp: *.sh: Permission denied,如果从其他git仓库导入到gitlab后,.sh文件缺少可执行权限。
修改项目中的.sh文件权限:
for file in $(find ./ -name *.sh | grep -v /vendor/); do chmod +x $file;done
专题「工具」的其它文章 »
- TLS和SSL (Nov 17, 2020)
- prometheus基础和使用 (Aug 20, 2019)
- kubernetes基础和使用 (Sep 30, 2018)
- 一款漂亮的命令行工具——cmder (Sep 10, 2018)
- 搭建个人代码托管git服务 (Aug 19, 2018)
- 使用vagrant和vitrualBox搭建虚拟开发环境 (Aug 16, 2018)
- docker基础和使用 (Aug 10, 2018)
- goland IDE使用说明 (Aug 07, 2018)