Gitlab的CICD

Gitlab的CICD

devops已经成为了主流,绝大多数的devops架构都是基于gitlab+jenkins+harbor+k8s去做的,在这套东西中我们需要维护的东西很多,甚至编译环境也需要我们自己维护。实际上,gitlab已经集成了完整的cicd功能,我们可以使用gitlab轻松地进行cicd。

1、基本环境

禁用gitlab自带的nginx并使用nginx对gitlab进行反向代理。

操作系统 内网地址 基础环境 用途
ubuntu20.04-server 192.168.31.200 部署gitlab、nginx
ubuntu20.04-server 192.168.31.201 kubernetes 部署gitlab-runner
ubuntu20.04-server 192.168.31.202 kubernetes 部署gitlab-runner
ubuntu20.04-server 192.168.31.203 kubernetes 部署gitlab-runner

2、环境部署

2.1、安装gitlab

安装方法参照 Gitlab官方ubuntu部署文档

sudo apt-get update
sudo apt-get install -y curl openssh-server ca-certificates tzdata perl postfix
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
sudo EXTERNAL_URL="https://gitlab.linhf.cn" apt-get install gitlab-ee
# 注意:EXTERNAL_URL为访问gitlab的域名或地址,如:https://gitlab.linhf.cn,如果后期需要修改可以更改/etc/gitlab/gitlab.rb的external_url。

默认的管理员用户名为root,初始密码在gitlab安装完成后可在/etc/gitlab/initial_root_password文件中找到。

2.1、配置gitlab-registry

gitlab-registry是gitlab内置的镜像仓库,默认情况下此功能是关闭的,我们需要修改配置文件将其开启。

sudo vim /etc/gitlab/gitlab.rb
# 禁用自带的nginx
nginx['enable'] = false 
# 访问的域名
registry_external_url 'https://registry.gitlab.linhf.cn' 
# 启用registry
gitlab_rails['registry_enabled'] = true 
# registry实际的api地址(nginx反向代理到这里)
gitlab_rails['registry_api_url'] = "http://127.0.0.1:5000" 
# registry监听地址
registry['registry_http_addr'] = "0.0.0.0:5000" 
sudo gitlab-ctl reconfigure

完成配置后我们可以在仓库目录下的[软件包与镜像库>容器镜像库]中看到当前的容器镜像,如图。

gitlab镜像仓库

注意
默认情况下gitlab-registry使用http协议的,我们需要在docker中添加私人仓库配置才可使用; 如果需要使用gitlab自带的nginx可参考 gitlab内置nginx配置

2.2、安装配置gitlab-runner

gitlab-runner是一个用来构建项目的组件,类似jenkins但又不同于jenkins,它可以与gitlab深度整合并可以进行分布式构建。gitlab-runner有多种配置模式,例如host、docker和kubernetes等,由于gitlab-runner支持在容器中运行并且是作为一个组件,所以他能更加容易保存构建环境的一致性并且轻量。

在kubernetes环境下我们可以借助helm快速配置安装gitlab-runner。

# 列出所有gitlab-runner版本
helm search repo -l gitlab/gitlab-runner
# 选择一个版本,这里的version是chart的version
helm pull gitlab/gitlab-runner --version 0.49.2
# 解压pull下来的文件
tar -xf gitlab-runner-0.49.2.tgz

编辑values.yaml:

gitlabUrl: http://gitlab.your-domain.com/ 
# runnerRegistrationToken可以在管理员账户下的 设置->CI/CD->Runner 中找到
runnerRegistrationToken: "xxxx"
# 如果需要自定义编译环境,可以更换runner的运行镜像,比如我需要编译golang,可以换成golang
runners:
    config: |
        [[runners]]
        [runners.kubernetes]
            image = "golang:1.19" 
    # 为这个runner指定tag,编译时可在配置文件中指定用哪个runner执行
    tags: "k8s,golang"

执行helm安装,就可以在gitlab的runner管理上看到相应的gitlab-runner。

helm install gitlab-runner gitlab-runner/ -n {{namespace}}
gitlab-runner

其他的安装配置可以参考 gitlab-runner安装文档

3、cicd配置编写

在项目的根目录新建一个 .gitlab-ci.yml 文件,这个文件的名字可以在项目的 设置->CI/CD->流水线通用设置->CI/CD->配置文件 中自定义名称。下面是一个ci/cd配置的例子,具体可以查看 gitlabCICD配置文档

# include: 引入某个文件,类似go的import
#include:
# local: /ctyun-dev/ci.yml
# remote: https://xxx.com/ci.yml
stages:
  - build
  - deploy
# 变量,可在构建过程中获取的,key:value
variables:
  GOPROXY: https://goproxy.io
  RULE_TYPE: "other"
## 全局缓存,与阶段缓存一致
#cache:
## 注意:only和except官方不建议使用,推荐使用rules,rules不能与only、except组合使用
build-job-only-main:
  stage: build
  script:
    - echo "这个输出表明执行构建的是main分支"
  # only,定义在哪些分支和标签中执行
  only:
    - ctyun-main
# # need:某一步骤执行了这个步骤才能执行
# needs:
build-job-except-main:
  stage: build
  script:
    - echo "这个输出表明执行构建的不是main分支"
  # only,定义在哪些分支和标签中不会被执行
  except:
    - ctyun-main
build-job-rules:
  stage: build
  script:
    - echo "这个输出表明执行构建是由rules触发的"
  # if:规则逻辑,类似编程语言的if-else,注意,此处有坑:if记得加冒号,记得加单引号
  # when:代表执行时机(manual-手动、delayed-延迟n秒、on_success-马上、on_failure-失败时),start_in: 代表延迟执行
  # change:当文件发生变化时触发
  # exists:文件存在时
  rules:
    - if: '$RULE_TYPE == "manual"'
      when: manual
    - if: '$RULE_TYPE == "delay"'
      when: delayed
      start_in: '5'
    - when: on_success
build-bin:
  # 阶段名称
  stage: build
# # 阶段缓存,仅此阶段,如果是nodejs项目有node_modules时可以启用
# cache:
# # 缓存的key,防止被覆盖,每个key会独立分配一个缓存空间
# key: build
# # 缓存路径
# paths:
# - node_modules
  # 执行的脚本
  script:
    - echo "Compiling the code..."
    - export GOPROXY="$GOPROXY"
    - go mod tidy
    - go build -o bin/http_server http_server/main.go
    - echo "Compile complete."
  # 制品配置,构建出的文件收集
  artifacts:
    # 制品名称 $CI_JOB_NAME-阶段名称 $CI_COMMIT_REF_NAME-分支名称
    name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
    # 目标文件路径
    paths:
      - bin/http_server
    # 何时收集:on_success-成功时,always-总是,on_failure-失败时
    when: on_success
    # 过期时间,到期自动清理
    expire_in: 365 days
  # gitlab-runner的tag
  tags:
    - k8s
    - golang
build-image:
  # 阶段名称
  stage: build
  script:
    - echo "Start to image build......"
    - img build -t gitlab.linhf.cn/experiment/get-started:http-server-0.0.1 .
    - echo "Compile image build."
  tags:
    - k8s
    - img
deploy-job:
  stage: deploy
  environment: prod
  script:
    - echo "Deploying application..."
    - "这里可以使用kubectl进行此应用的发布"
    - echo "Application successfully deployed."
    - k8s
    - kubectl

使用img在容器中打包镜像

在虚拟机环境中一般都是用docker打包镜像,我们也可以通过容器调用虚拟机中的docker打包镜像,但是这就可能会造成额外的负担,比如确保环境的一致性。而使用docker-in-docker就相当于在容器中再运行一个docker,比较笨拙。

genuinetools/img 是一个无容器依赖的镜像打包工具,我们只需要在配置runner的运行镜像的时候使用img的docker镜像,由于img打包镜像的时候可能会读写容器中的/tmp目录,所以要配置 runners.kubernetes.pod_annotations 以增加权限。

runners:
    config: |
        [[runners]]
        [runners.kubernetes]
            image = "r.j3ss.co/img:v0.5.9" 
        [runners.kubernetes.pod_annotations]
          "container.apparmor.security.beta.kubernetes.io/build" = "unconfined"
          "container.seccomp.security.alpha.kubernetes.io/build" = "unconfined"        

我们在配置 .gitlab-cicd.yml 的时候,使用img代替docker即可,如: “img build xxx”、“img push xxx” 等。

在cicd中使用环境变量

无论是使用gitlab-registry还是使用harbor的时候,都需要进行登录,如果将登录的账号密码直接写在配置文件中是非常不安全的,我们可以借助gitlab的环境变量对敏感数据进行存储,这个环境变量分为全局和项目的,我们以项目的为例。

在项目中找到 设置->CICD->变量 可以看到环境变量的设置,我们可以添加一个环境变量,在使用的时候我们可以直接使用 $XXX 即可。全局环境变量与项目环境变量一样,只不过全局环境变量在gitlab的全局设置中的CI/CD中。

ci/cd环境变量
build-image:
  stage: build
  script:
    - echo "Start to image build......"
    - img login $CI_REGISTRY_URL -u $PROJECT_CI_USERNAME -p $PROJECT_CI_PASSWORD
    - img build -t registry.gitlab.linhf.cn/experiment/get-started:http-server-"$VERSION" .
    - img push registry.gitlab.linhf.cn/experiment/get-started:http-server-"$VERSION"