CI/CD笔记-Gitlab-ci/cd部署实践

CI/CD笔记-gitLab之ci/cd

一、概念普及

  1. CI/CD
  2. gitlab CI/CD
  3. gitlab-Job
  4. gitlab-Pipeline
  5. gitlab-Stage
  6. 什么是.gitlab-ci.yml文件
  7. .gitlab-ci.yml文件介绍
    variables
    default
    stages
    stage
    script
    before_script 和after_script
    only 和 except
    tags
    allow_failure
    when
    gitlab预设变量
    二、实验环境
    三、前期准备
    四. 配置Runner
  8. 安装runner
  9. 获取runner需要的gitlab地址及gitlab之CI的token
  10. 注册runner
  11. gitlab上查看是否注册成功
  12. 写一个测试项目及相关的Dockerfile与gitlabci.yml文件
    五、卸载gitlab-runner

一、概念普及
1. CI/CD

  CI(continuous integration):持续集成,即在代码构建过程中持续地进行代码的集成、构建、以及自动化测试等;有了 CI 工具,我们可以在代码提交的过程中通过单元测试等尽早地发现引入的错误。

  CD(continuous Deployment):持续交付,在代码构建完毕后,可以方便地将新版本部署上线,这样有利于快速迭代并交付产品。

2. gitlab CI/CD


  GitLab CI/CD(后简称 GitLab CI)是一套基于 GitLab 的 CI/CD 系统,可以让开发人员通过 .gitlab-ci.yml 在项目中配置 CI/CD 流程,在提交后,系统可以自动/手动地执行任务,完成 CI/CD 操作。而且,它的配置非常简单,CI Runner 由 Go 语言编写,最终打包成单文件,所以只需要一个 Runner 程序、以及一个用于运行 jobs 的执行平台(如裸机+SSH,Docker 或 Kubernetes 等,我推荐用 Docker,因为搭建相当容易)即可运行一套完整的 CI/CD 系统。

3. gitlab-Job

Job 为任务,是 GitLab CI 系统中可以独立控制并运行的最小单位。在提交代码后,开发者可以针对特定的 commit完成一个或多个 job,从而进行 CI/CD 操作。

作业就是运行器(Runner)要执行的指令集合,Job 可以被关联到一个 Stage。当一个 Stage 执行的时候,与其关联的所有 Job 都会被执行。在有足够运行器的前提下,同一阶段的所有作业会并发执行。作业状态与阶段状态是一样的,实际上,阶段的状态就是继承自作业的。

作业必须包含script(由Runner执行的shell脚本),随着项目越来越大,Job 越来越多,Job 中包含的重复逻辑可能会让配置文件臃肿不堪。.gitlab-ci.yml 中提供了 before_script 和 after_script 两个全局配置项。这两个配置项在所有 Job 的 script 执行前和执行后调用。

Job 的执行过程中往往会产生一些数据,默认情况下 GitLab Runner 会保存 Job 生成的这些数据,然后在下一个 Job 执行之前(甚至不局限于当次 CI/CD)将这些数据恢复。这样即便是不同的 Job 运行在不同的 Runner 上,它也能看到彼此生成的数据。

4. gitlab-Pipeline


Pipeline 即流水线,可以像流水线一样执行多个 Job. 在代码提交或 被合并时,GitLab 可以在最新生成的 commit上建立一个 pipeline,在同一个 pipeline 上产生的多个任务中,所用到的代码版本是一致的。

5. gitlab-Stage
一般的流水线通常会分为几段;在 pipeline中,可以将多个任务划分在多个阶段中,只有当前一阶段的所有任务都执行成功后,下一阶段的任务才可被执行。
注:如果某一阶段的任务均被设定为“允许失败”,那这个阶段的任务执行情况,不会影响到下一阶段的执行。

CI/CD笔记-Gitlab-ci/cd部署实践

CI Pipeline

上图中,整条流水线从左向右依次执行,每一列均为一个阶段,而列中的每个可操控元素均为任务。左边两个阶段的任务是自动执行的任务,在commit提交后即可自动开始运行,执行成功或失败后,可以点击任务右边的按钮重试;而右边两个是手动触发任务,需要人工点击右边的“播放”按钮来手动运行。

6. 什么是.gitlab-ci.yml文件
官方文档:https://docs.gitlab.com/ee/ci/yaml/README.html#parameter-details

GitLab CI使用YAML文件(.gitlab-ci.yml)来管理项目配置。该文件存放于项目仓库的根目录,并且包含了你的项目如何被编译的描述语句,YAML文件使用一系列约束叙述定义了Job启动时所要做的事情。

Job是.gitlab-ci.yml文件中最基本的元素,由一系列参数定义了任务启动时所要做的事情,用户可以创建任意个任务;每个任务必须有一个独一无二的名字,但有一些保留keywords不能用于Job名称,image,services,stages,types,before_script,after_script,variables,cache,include。

Job被定义为*元素,并且至少包括一条script语句,如果一个 Job 没有显式地关联某个 Stage,则会被默认关联到 test 阶段。

job1:
# 关联到bulid阶段
stage: build
# 所需执行的脚本
script:
- execute-script-for-job1

7. .gitlab-ci.yml文件介绍
variables
用于自定义可用于所有job的全局变量,这个关键字也可以在每个job中单独定义,相同变量名时,优先级是先job再全局

variables:
SERVICE_ENV: R1
APPNAME: AI-DATA

default
全局默认值,即为了简便不用重复设置每个任务中相同关键字对应的相同的值,如每个job应用相同的镜像名,就可以在default下配置,则如果job中没有配置自己的镜像则使用default中的,default中支持以下关键字

image,services,before_script,after_script,tags,cache,artifacts,retry,timeout,interruptible
default:
image: base-python2:v1

stages:
- build
- deploy

build-app-job:
stage: build
script: "run build script file"
only:
- branches

build-mysql-job:
stage: build
image: mysql6:v1
script: "do something"
stages

用于定义所有作业job可以使用的全局阶段,gitlab-ci.yml允许定义多个阶段,stages元素的顺序定义了作业执行的顺序。Job关联的stage名相同时,该多个Job将并行执行(在拥有足够Runner情况下),下一个阶段的Job将会在前一个阶段的job都完成的情况下执行。

如果没有定义stages,那么默认包含build,test,deploy 三个stage,stage中并不能直接配置任何具体的执行逻辑,具体的执行逻辑应该在Job中配置。


stages:
- build
- test
- deploy

stage
为每个任务Job指定其属于那个阶段,其依赖stages中定义好的阶段

script
script 是一系列通过Runner来执行的shell脚本,也可以直接执行shell命令

有些时候,script命令需要被单引号或者双引号所包裹。举个例子,命令中包涵冒号的时候,该命令需要被引号所包裹,这样YAML解析器才知道该命令语句不是“key: value”语法的一部分。当命令中包涵以下字符时需要注意打引号:: { } [ ] , & * #? | - < > = ! % @


test-job1:
 # 每个任务必须指定阶段(stage)和对应要做的任务(script)
stage: test
script:
  - ehco "执行对应写好的需要runner执行的命令或脚本,多个命令这样写"
  - uname -a
  - echo "hello,$GITLAB_USER_LOGIN"
test-job2:
stage: test
script: echo "如果只是一条命令可以直接在这个后面写"
  • image 和 services

官方文档:https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#define-image-and-services-from-gitlab-ciyml

这两个关键字定义运行时,docker的镜像名和需要用到的已经存在运行的服务名,前提是注册runner时选择的是docker而不是shell


build-job:
image:access-web:v1
services:
 # 定义构建access-web镜像时可以访问服务名
- mysql:v1

before_script 和after_script
定义一些在所有任务Job执行前或之后需要执行的命令,每个任务Job中如果定义了,就执行自己的,全局的就不会执行

# 定义全局before_script
default:
before_script:
  - global before script

# 覆盖全局的before_script
test-job1:
before_script:
  - 执行自己的在本任务执行前要做的事
script:
  - uname -a
after_script:
  - 执行命令等等

only 和 except
更多参考官方文档:https://docs.gitlab.com/ee/ci/yaml/README.html#onlyexcept-basic

如果任务job没有指定only关键字,默认是所有的分支push或标签创建时当做触发条件,except默认为空即不过滤

only和except都是定义job什么时候会被触发创建,only和except可以使用正则,可以指定仓库地址,可以同时用;

only定义了job是以分支还是标签提交时被触发;

except定义了job在某个分支或标签提交时不会被执行;

only和except可以使用的一些关键字:

CI/CD笔记-Gitlab-ci/cd部署实践


job1:
stage: test
script: echo "do something"
only:
  # 仅以开头以issue-的引用的时候被触发,而所有分支均被过跳过
  # 通过正则默认区分大小写,如果想不区分需要后面加i,即/^issue-.*$/i
  - /^issue-.*$/
except:
  - branches

job2:
stage: test
script: echo "do something"
only:
  # 仅为分支标记了标签、或者通过api请求、或者定时任务时才会运行次job
  - tags
  - triggers
  - schedules

job3:
stage: test
script: echo "do anything"
only:
 # 指定仓库所有的分支都会被触发
- branches@仓库地址
except:
 # master分支不会被触发本任务
- master@仓库地址
 # 以release开头的命名的也不会被触发
- /^release/.*$/@仓库地址

tags
用于指定注册的某个runner来运行任务,注册runner时有为每个runner设置标签,这里就是匹配那个的

job:
tags:
 # 如果指定多个标签,那么那些标签必须某个running都有关联才会调用
- python-runner

allow_failure
允许某个任务失败不影响CI的其他任务执行结果,默认是false即不允许失败


job1:
stage: test
script: "本任务执行成功与否不影响job2的执行"
allow_failure: true

job2:
stage: test
script: "本人必须运行成功后面的任务才会被执行"

when
用于实现发生故障或发生故障时运行的作业

when支持的值如下:

  1. on_success(默认) 早期所有的作业都成功或者视为成功的all_failure:true

  2. on_failure仅在早期至少一个作业失败时运行

  3. always 无论早期作业结果如何都执行

  4. manual 手动执行

  5. delayed 延迟指定的时间再执行,gitlab11.14中新增

  6. never 使用rules,不执行,使用workflow:rules ,不要运行管道
job:
stage: deploy
script:
- make deploy
when:manual

gitlab预设变量
官方文档:https://docs.gitlab.com/ee/ci/variables/predefined_variables.html#predefined-environment-variables-reference

用于获取gitlab用于ci/cd 预设的一些环境变量如获取分支名CI_COMMIT_BRANCH


docker build:
script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
rules:
  - if: '$CI_COMMIT_BRANCH == "master"'
    when: delayed
    start_in: '3 hours'
    allow_failure: true

gitlab-runner安装在服务部署的机器上,其自动根据你在gitlab-ci.yml中规定的触发条件如某个分支提交后执行,其首先会自动从gitlab仓库上拉取仓库代码到服务器本地,然后根据gitlab-ci文件定义的job执行,如打包文件构建镜像,然后部署服务【多台可以通过建立docker swarm集群的模式,在本地部署一个,会自动分布到多台】

二、实验环境

  • 系统:centos8.0_x64

  • gitlab: 192.168.0.152:8888

  • docker-swarm集群:192.168.0.150【master】192.168.0.153【slave】

  • docker仓库:192.168.0.150:5000

  • gitlab-runner:192.168.0.150

三、前期准备

  1. 搭建gitlab:请参考:https://mp.weixin.qq.com/s/amWWG34BXoTAumy1p4SnvA

  2. 搭建docker集群请参考:https://mp.weixin.qq.com/s/cIkPOnna9u0h1AMbz4aOjw

  3. 搭建docker私有仓库请参考:https://mp.weixin.qq.com/s/4KI3fqyJFv5yJI_H_HK0RA

  4. 构建python3的基础镜像
# 1. 直接重docker-hub上拉取centos系统
docker pull centos
# 2. 构建一个容器
docker run --name=genPython -it centos /bin/bash
# 3. 换yum仓库
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak && curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo && yum clean all && yum makecache
# 4. 安装python3
yum -y install python38
# 5. 查看Python安装
whereis python
python3 --version
pip3 list
# 6. 退出容器,将刚才改变的容器生成新的镜像`docker commit 容器id 仓库/新的镜像名:标签`
docker commit 0426a83fc666 192.168.0.150:5000/python3:v1
# 7. 推送到私有仓库,需要先配置本地docker配置文件指定仓库地址
vim /etc/docker/daemon.json
{
"registry-mirrors": ["192.168.0.150:5000"], # 加速的地址
"insecure-registries" : ["192.168.0.150:5000"]
}
systemctl daemon-reload && systemctl restart docker

docker push 192.168.0.150:5000/python3:v1

注为了方便也将其推送到dockerhub上
docker login
docker tag 192.168.0.150:5000/python3:v1 addmoney2018/python3:v1
docker push addmoney2018/python3:v1

四. 配置Runner
1. 安装runner
官方文档:https://docs.gitlab.com/runner/install/

### 在192.168.0.150上,只有安装在集群管理节点上,才能通过docker swarm集群部署到多台上

# 下载二进制文件并写入文件gitlab-runner
# Linux x86-64
curl -L --output /usr/local/bin/gitlab-runner "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64"

# 授予其执行权限
chmod +x /usr/local/bin/gitlab-runner

# 创建一个GitLab CI用户
useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash

# 修改gitlab-runner密码
echo "gitlab-runner" | passwd --stdin gitlab-runner

# 安装并作为服务运行
gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner

# 将服务添加到开启启动
chkconfig gitlab-runner on

# 启动
gitlab-runner start

# 经过实践发现runner执行docker命令时会报权限不够,因此需要加权
usermod -aG docker gitlab-runner

# 测试下
sudo -u gitlab-runner -H docker info

注:之后想修改上面指定的用户或工作目录
修改这个文件即可:/etc/systemd/system/gitlab-runner.service

2. 获取runner需要的gitlab地址及gitlab之CI的token
登陆gitlab---选择对应的仓库---选择仓库的setting---选择CI/CD---选择Runners的expand----复制下图中地址和token

CI/CD笔记-Gitlab-ci/cd部署实践

  1. 注册runner
    官方文档:https://docs.gitlab.com/runner/register/index.html

注意:

如果选择的runner执行模式是docker,则runner中的job会在指定的基础镜像中执行,因此需要你指定的镜像里可以执行ci文件中定义的命令,如docker build

如果选择的runner执行模式是shell,即在runner服务安装的物理机执行,因此你如果不是通过容器安装runner,而想通过runner打镜像,建议通过shell模式

# 执行如下命令
gitlab-runner register

[root@gitlab bin]# gitlab-runner register
Runtime platform                                   arch=amd64 os=linux pid=10254 revision=943fc252 version=13.7.0
Running in system-mode.                            
# 输入gitlab实例地址                                    
Enter the GitLab instance URL (for example, https://gitlab.com/):
http://192.168.0.152:8888/
# 输入runner的token
Enter the registration token:
WecGC7zY9otwQgdss7yZ
# 输入这个runner的描述
Enter a description for the runner:
[gitlab.example.com]: try to run a python service
# 输入这个runner的标签,每个标签用逗号隔开【用于区分不同类型的 Runner,使不同阶段的 job 在不同的 Runner 中运行】
Enter tags for the runner (comma-separated):
Python3-runner
Registering runner... succeeded                     runner=WecGC7zY
# 选择执行runner的方式
Enter an executor: docker-ssh, parallels, docker-ssh+machine, custom, shell, ssh, virtualbox, docker+machine, kubernetes, docker:
docker
Enter the default Docker image (for example, ruby:2.6): # 指定基础镜像,注意本地你需要先下载docker镜像,将其推动到你得私有仓库,因为你本地daemon.json配置了本地仓库,runner就是在指定得镜像中构建得
192.168.0.150:5000/docker
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
# 重启下runner
gitlab-runner restart

# 注
如果想更改上面得配置
vi /etc/gitlab-runner/config.toml

4. gitlab上查看是否注册成功
CI/CD笔记-Gitlab-ci/cd部署实践

5. 写一个测试项目及相关的Dockerfile与gitlabci.yml文件

  • 项目目录结构

test-runner

  • main.py
  • requirements.txt
  • .gitlab-ci.yml
  • Dockerfile
  • docker-compose.yml

  • Dockerfile文件
FROM 192.168.0.150:5000/python3:v1

# 创建容器中的服务根目录
RUN mkdir -p /app/code/

# 拷贝代码到容器中
COPY . /app/code/test-gitlab01

# 指定工作目录
WORKDIR /app/code/test-gitlab01

# 安装依赖
RUN pip3 install --no-cache-dir -r /app/code/test-gitlab01/requirements.txt -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

# 映射端口
EXPOSE 8100

# 启动
CMD ["python3","main.py"]
  • .gitlab-ci.yml文件
variables:
SERVICE_ENV: R1
IMAGE_NAME: python-test-app:v3
DOCKER_FILE_PATH: .
DOCKER_HUB: 192.168.0.150:5000/

stages:
- build
- test
- deploy

build-app:
stage: build
only:
   # 仅当什么事件发生就触发本任务: 发生推送
  - master
script:
  - echo "开始执行构建app的阶段"
  - echo "环境为:$SERVICE_ENV"
  - echo "runner为shell模式就在物理机上执行下面得命令"
  - echo "当前位置:$(pwd),当前的主机名:$(hostname)"
  - echo "当前目录下的文件:$(ls -lrt)"
  - docker build -t $IMAGE_NAME $DOCKER_FILE_PATH
  - docker tag $IMAGE_NAME $DOCKER_HUB$IMAGE_NAME
  - docker push $DOCKER_HUB$IMAGE_NAME
  - echo "构建镜像成功!"
tags:
   # 指定某个runner执行本任务
  - python-shell-runner

test-app:
stage: test
only:
  - master
script:
  - echo "开始测试阶段"
  - echo "查看镜像是否推送到仓库:$(curl http://192.168.0.150:5000/v2/_catalog)"
  - export TEST_PORT=$RANDOM
  - docker run -d -p $TEST_PORT:8100 $DOCKER_HUB$IMAGE_NAME
  - sleep 3
  - curl http://192.168.0.150:$TEST_PORT
  - echo "测试阶段完成!"
tags:
  - python-shell-runner

deploy-app:
stage: deploy
only:
  - master
script:
  - echo "开始部署服务阶段"
  - echo "当前位置:$(pwd)"
  - echo "当前目录下的文件:$(ls -lrt)"
  - docker stack deploy -c docker-compose.yml web
  - sleep 3
  - docker service ls
  - docker service ps web_python-test-web
  - curl http://192.168.0.150:8100
  - echo "部署阶段完成"
tags:
  - python-shell-runner
  • docker-compose.yml文件
version: '3'
services:
python-test-web:
  image: 192.168.0.150:5000/python-test-app:v3
  ports:
    - 8100:8100
  restart: always
  deploy:
    replicas: 2
    restart_policy:
    condition: on-failure
  volumes:
    - /etc/localtime:/etc/localtime
  networks:
    - python-net
networks:
python-net:
  • main.py文件
# _*_ coding:utf-8 _*_
# DevVersion: Python3.6.8
# Date: 2021-01-01 18:10
# Author: Sun
# PyCharm|main
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
   return {"message": "happy new year 2021"}

if __name__ == '__main__':
   app.run("0.0.0.0", "8100")
  • requirements.txt文件

click==7.1.2
Flask==1.1.2
itsdangerous==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
Werkzeug==1.0.1


![](http://www.icode9.com/i/li/?n=4&i=images/blog/202104/04/cf56f16dd104efb09a8ee88071da1aa6.png?,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

![](http://www.icode9.com/i/li/?n=4&i=images/blog/202104/04/da96908ee6ac946521adddc74520da6f.png?,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

![](http://www.icode9.com/i/li/?n=4&i=images/blog/202104/04/f4348ba639e754ef8650dbeddc29e28d.png?,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

![](http://www.icode9.com/i/li/?n=4&i=images/blog/202104/04/b81d453d6970565495c448d24b031f77.png?,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

![](http://www.icode9.com/i/li/?n=4&i=images/blog/202104/04/b376728f1ee146bd96d264f7b88632b7.png?,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

**五、卸载gitlab-runner**

关闭gitlab-runner

gitlab-runner stop

关闭开启启动

chkconfig gitlab-runner off

卸载

gitlab-runner uninstall

删除安装目录

rm -rf /etc/gitlab-runner/
rm -rf /usr/local/bin/gitlab-runner
userdel -r gitlab-runner

上一篇:Gitlab-ci:从零开始的前端自动化部署


下一篇:大神教你如何给脚本写一个守护进程