学习实践下gitlab cicd,目前公司正在试用。
gitlab安装
1docker run --detach --privileged=true --hostname 192.168.110.128 --publish 8881:443 --publish 80:80 --publish 8883:22 --name gitlab --restart always --volume `pwd`/config:/etc/gitlab --volume `pwd`/logs:/var/log/gitlab --volume `pwd`/data:/var/opt/gitlab yrzr/gitlab-ce-arm64v8
gitlab-runner 安装
二进制安装
1# Download the binary for your system
2sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm64
3
4# Give it permission to execute
5sudo chmod +x /usr/local/bin/gitlab-runner
6
7# Create a GitLab Runner user
8sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
9
10# Install and run as a service
11sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
12# Runtime platform arch=arm64 os=linux pid=3745 revision=81ab07f6 version=16.10.0
13sudo gitlab-runner start
14
15# sudo gitlab-runner stop
docker方式安装
1docker run -d --name gitlab-runner --privileged=true --restart always -v `pwd`/config:/home/gitlab-runner/.gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock bitnami/gitlab-runner:latest
2
3# 注册runner
4 docker run --rm -v `pwd`/config:/home/gitlab-runner/.gitlab-runner bitnami/gitlab-runner:latest register --non-interactive --executor "docker" --docker-image alpine:latest --url "http://192.168.110.128/" --registration-token "9Z-84Qi21FkxvKmpkKjv\n" --description "test" --tag-list "docker,test" --run-untagged="true" --locked="false" --access-level="not_protected"
注册runner ,全局共享runner, project runner
1gitlab-runner register --url http://192.168.110.128 --token glrt-iVgXJFrDCxgtVJLaywdG
生成的config.toml,注意这里试用了虚拟机的ip, 不然用域名,docker那边dns解析有问题。
volumes : 增加了 /var/run/docker.sock:/var/run/docker.sock
以防万一
1concurrent = 1
2check_interval = 0
3connection_max_age = "15m0s"
4shutdown_timeout = 0
5
6[session_server]
7 session_timeout = 1800
8
9[[runners]]
10 name = "golang"
11 url = "http://192.168.110.128"
12 id = 1
13 token = "glrt-j7hZyxN8skmFn4ZLiKs8"
14 token_obtained_at = 2024-04-20T01:47:03Z
15 token_expires_at = 0001-01-01T00:00:00Z
16 executor = "docker"
17 [runners.docker]
18 tls_verify = false
19 image = "alpine:latest"
20 privileged = false
21 disable_entrypoint_overwrite = false
22 oom_kill_disable = false
23 disable_cache = false
24 volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
25 shm_size = 0
26 network_mtu = 0
27
28[[runners]]
29 name = "mom"
30 url = "http://192.168.110.128"
31 id = 2
32 token = "glrt-Zsow2xwszLieJ-i4uXWD"
33 token_obtained_at = 2024-04-20T03:19:23Z
34 token_expires_at = 0001-01-01T00:00:00Z
35 executor = "docker"
36 [runners.docker]
37 tls_verify = false
38 image = "alpine:latest"
39 privileged = false
40 disable_entrypoint_overwrite = false
41 oom_kill_disable = false
42 disable_cache = false
43 volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
44 allowed_pull_policies = ["always", "if-not-present"]
45 shm_size = 0
46 network_mtu = 0
47
48[[runners]]
49 name = "mom"
50 url = "http://192.168.110.128"
51 id = 3
52 token = "glrt-4wNTBXzS8QNiEAjSp-yK"
53 token_obtained_at = 2024-04-20T07:42:33Z
54 token_expires_at = 0001-01-01T00:00:00Z
55 executor = "docker"
56 [runners.cache]
57 MaxUploadedArchiveSize = 0
58 [runners.docker]
59 tls_verify = false
60 image = "alpine:latest"
61 pull_policy="if-not-present"
62 privileged = false
63 disable_entrypoint_overwrite = false
64 oom_kill_disable = false
65 disable_cache = false
66 volumes = ["/cache"]
67 shm_size = 0
68 network_mtu = 0
- concurrent : 限制runner能够同时执行多少个作业
初阶关键词
stages
stages 默认提供,如果不指定stages,可以试用这些stage。
- .pre
- build
- test
- deploy
- .post
script
1npm_inst:
2 script: npm install
1npm_inst:
2 image: node
3 script:
4 - npm isntall
5 - npm build
cache
用来缓存依赖包,避免重复执行下载。
全局配置的key
1default:
2 cache:
3 key: "$CI_COMMIT_PEF_SLUG"
4 paths:
5 - binaries/
key不变就会试用本地的缓存
key 可以执向一个文件列表,文件列表没变,key就是没变
1test-job:
2 stage: build
3 cache:
4 - key:
5 files:
6 - yarn.lock
7 paths:
8 - .yarn-cache/
9 - key:
10 files:
11 - Gemfile.lock
12 paths:
13 - vendor/ruby
14 script:
15 - bundle install --path=vendor
16 - yarn install --cache-folder .yarn-cache
17 - echo 'install done'
cache:policy
默认在开始执行的时候下载缓存,结束的时候上传缓存
policy: pull 跳过上传步骤
policy: push 跳过下载步骤
但是如果我们已经知道,某个 job 只是使用的其他 job 改变的文件,自身并无改变对应路径的文件,那么就不需要进行文件上传操作,采用pull 策略即可。
反过来,某个 job 不依赖于其他 job 改变的文件,自身改变的文件被其他 job 所依赖,那么就不需要在 job 开始前进行文件下载操作,采用push 策略。这样减少了不必要的操作,在一定程度上节约了时间。
如 php 项目一开始要构建 vendor 包,那么cache 就是 push 策略, 在构建镜像阶段需要这个 cache,则进行 pull策略。
images
runner执行器是 docker的前提才有效
1use_image_job:
2 image: node:12.21.0
3 script: npm -v
tags
tags 用于指定用哪个runner来执行当前作业。
runner 配置了tag
1tags_example:
2 tags:
3 - laravel
4 script: echo "hello fizz"
variables
定义变量
1variables:
2 USER_NAME: 'FIZZ'
3
4test:
5 variables:
6 USER_NAME: 'ZK'
7 script: echo 'hello' $USER_NAME
8# 输出ZK ,局部变量
在ci/cd设置中定义变量
也可以针对 group 进行设置变量
预定义变量
- CI_COMMIT_BRANCH : 提交分支的名称
- CI_COMMIT_REF_NAME : 正在构建项目的分支或tag名
- CI_JOB_NAME: 作业名称
- CI_PROJECT_NAME: 项目名称
when
- on_sucess : 默认值, 在此前之前的阶段的在其他作业都成功执行后,才会触发当前的作业
- on_failure: 当之前的阶段有作业失败,才会触发当前的作业
- always : 不管之前的作业如何, 都被执行
- manual : 该作业只能被手动执行
- nerver: 不被执行
- deplayed: 延迟执行
- start_in 来定义延迟多久时间
手动执行
1manual_job:
2 script: echo 'I think therefor i am'
3 when: manual
1fail_job:
2 script: echo 'everything is going to be alright'
3 when: on_failure
artifacts
将构建出的文件保存起来,
cache 一般用于项目的依赖保,artifacts常用于输出的一些文件、文件夹。 如dist目录,jar包、测试报告。cache可以被手动清空,而artifacts是会过期的。
1upload:
2 script: npm install build
3 artifacts:
4 paths:
5 - /dist
6 - *.jar
7 exclude:
8 - binaries/**/*.0
9 expire_in: 1 week
10 name: "$CI_JOB_NAME"
before_script
before_script 必须是个数组,执行时机是 script之前,artifacts恢复之后。可以定义全局default 关键中定义全局before_script,定义后在每个作业中执行。
after_script
after_script是独立的执行环境,before_script、script中的变量是无权访问的。作业失败也会被执行。如果作业取消或超时,则不会被执行。
only与except
控制作业是否被执行,或当前作业的时机。 only满足条件的时候才会被执行作业。 except不常用,排除某个条件其他都会被执行。
如果没定义only,except或者rules修饰。那么作业将被默认被only修饰,值为tags或者branchs.
只有修改了test分支的代码 ,才会被执行。
1only_example:
2 script: deploy test
3 only:
4 - test
only与 except 下可以配置 4 种值
- refs
- variables
- changes
- kubernetes
only:refs ,表明作业只有在某个分支或某一个流水线类型下才会被添加到流水线。
1test:
2 script: test
3 only:
4 - test
5
6build:
7 script: build test
8 only:
9 refs:
10 - test
11
12deploy:
13 script: deploy test
14 only:
15 refs:
16 - tags
17 - schedules
test,build 其实都一样只要 test 分支发生改变后,则被执行
deploy阶段是需要创建了新的 tag,或者 使用定时部署,则会被执行。
only:variables
1test:
2 only:
3 variables:
4 - $USER_NAME === "CRMAO"
only:changes
1test:
2 only:
3 changes:
4 - Dockerfile
5 - fe/**/*
当修改 Dockerfile或者 fe 目录下 则被执行
only:kubernetes
1deploy:
2 script: deploy test
3 only:
4 kubernetes: active
只有项目中存在可用的kubernetes服务时,作业才会被执行。
中阶关键词
coverage
配置一个正则表达式提取作业日志中输出的代码覆盖率,提取后可以将之展示到代码分支上。
1test:
2 script: npm test
3 coverage: '/Code coverage: \d+\.\d+/'
如果该作业输出Code coverage: 67.89 这种格式的日志,会被 gitlab ci/cd 记录下来。 只取最后一条。
dependencies
dependencies关键词定义当前作业下载哪些前置作业的artifacts ,只能是上一阶段的值。 可以定义当前作业下载哪些前置的 artifacts.或者不下载的 artifacts.可以是一个数组,
如果是空数组表示不下载任何artifacts。 默认所有 artifacts 在下个接口都是会被默认下载(这样很慢)
1stages:
2 - build
3 - deploy
4
5build_windows:
6 stage: build
7 script:
8 - echo "start build on windows"
9 artifacts:
10 paths:
11 - binaries
12
13build_mac:
14 stage: build
15 script:
16 - echo "start build on mac"
17 artifacts:
18 paths:
19 - binaries
20
21deploy_mac:
22 stage: deploy
23 dependencies:
24 - build_mac
25# 只下载 build_mac 作业的 artifacts
26release_job:
27 stage: deploy
28 script: echo 'release version'
29 dependencies: []
30
31# 不下载任何 artifacts
allow_failure
设置当前作业失败流水线是否继续运行
默认false, 不允许,失败则停止流水线。
1test2:
2 stage: test
3 allow_failure: true
extends
可以用于继承一些配置模板
.开头的作业,是隐藏作业,任何时候不会被执行。
1.test:
2 script: npm lint
3 stage: test
4 only:
5 refs:
6 - branches
7test_job:
8 extends: .test
9 script: npm test
10 only:
11 variables:
12 - $USER_NAME
extends后的作业
1test_job:
2 stage: test
3 script: npm test
4 only:
5 refs:
6 - branches
7 variables:
8 - $USER_NAME
default 、inherit
全局关键词,不能定义在具体的作业中。
default 中设置的所有值自动合并到流水线所有的作业中.
可以使用 default 设置的属性有 after_script,artifacts,before_script,cache,image,interruptible,retry,services,tags,timeout
inherit: 不拿全局的,或者指定拿具体的
1vriables:
2 NAME: "this 1"
3 AGE: "2"
4 SEX: "MAN"
5
6# 整个都不用 default
7test:
8 script: echo 'hello'
9 inherit:
10 # 不会继承 default 的
11 default: false
12 # 全局变量不会被引入
13 variables: false
14
15deploy:
16 script: echo 'hello'
17 inherit:
18 # 指定合并 retry,image属性
19 default:
20 - retry
21 - image
22 # 指定拿 下面 2 个变量
23 variables:
24 - NAME
25 - AGE
interruptible
新流水线把老的流水线给取消掉。 只要有一个 job 不能被取消的 job 运行了。 那么新流水线是无法取消旧流水线的。
如果需要新的把旧的给取消掉,那么可以在 default 关键词下设置 interruptible: true
needs
一个作业使用 needs 设置依赖作业后,只要依赖的作业完成,它就会运行,不需要等上阶段作业完成。 减少总运行时间。
retry
retry 关键词用于设置作业运行失败时的重试次数。
1build:
2 script: npm build
3 retry:
4 max: 2
timeout
设置一个作业的超时时间,超过该时间,流水线旧会被标记失败。
3600seconds,60minutes,one hour,3h,30m等
1build:
2 script: npm build
3 timeout: 1h
高阶关键词
rules,workflow,trigger,include,resource_group,environment,services,secrets 和 dast_configurtion
rules
决定作业是否运行。
rules:if
条件判断,可以配置多个表达式,当表达式的结果为 true 时,该作业将被运行。
1test_job:
2 script: echo "hello ,zy"
3 rules:
4 - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME != $CI_DEFAULT_BRANCH'
5 when: never
6 - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/'
7 when: manual
8 allow_failure: true
9 - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME'
如果第一个 if 命中, 则不会运行,配置了 when:never
rules:changes
文件改变则运行
1docker_build:
2 script: docker build -t my-image:$CI_COMMIT_REF_SLUG
3 rules:
4 - changes:
5 - Dockerfile
rules:exists
文件存在则运行
1docker_build:
2 script: docker build -t my-image:$CI_COMMIT_REF_SLUG
3 rules:
4 - exists:
5 - Dockerfile
rules:allow_failure
同 rule 第一个案例。 allow_failure:true 作业失败,流水线不会停止运行
rules:variables
对变量进行操作
1rules_var:
2 variables:
3 DEPLOY_VARIABLE: "default-deploy""
4 rules:
5 - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
6 variables:
7 DEPLOY_VARIABLE: "default-product"
8 - if: $CI_COMMIT_REF_NAME == 'feature'
9 variables:
10 IS_A_FEATURE: "true"
11 script:
12 - echo "run script with $DEPLOY_VARIABLE as an argument"
13 - echo "run another if $IS_A_FEATURE exists"
workflow
workflow是个全局关键字,可以配置多个规规则来限定流水线是否运行。如果命中了一条规则,流水线则会运行
1workflow:
2 rules:
3 - if: $CI_COMMIT_MESSAGE =~ /-draft$/
4 when: never
5 - if: $CI_PIPELINE_SOURCE == "merge_request_event"
6 variables:
7 IS_A_MR: "true"
8 - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
trigger
trigger关键词用于创建下游流水线,即在流水线中再触发新的流水线,适合于构建部署复杂的项目。
如果一个作业配置了 trigger,那么该作业的一些属性不能被定义如 script、after_script、before_script
能够配置的属性只有以下几个:stage,allow_failure,needs,rules,except,only,when,extends.
1trigger-other-project:
2 stage: deploy
3 trigger:
4 project: my/depoyment
5 branch: stable-2022
6trigger-child-pipeline:
7 stage: deploy
8 trigger:
9 include: /path/to/microservie_a.yaml
10 # strategy: depend
第一个作业触发my/deployment项目的stable-2022分支,如果不定义 branch 则触发该项目的默认分支
第二个作业触发下游的流水线。 上游流水线不会等待下游完成。如果需要完成后再运行,可以在作业上配置strategy: depend.
include
include 关键词用于引用其他流水线,可以引用其他流水线中的作业。
include:local
用于引入本地文件,且只能引入当前项目的文件。无法使用 git submodule 的路径。
1include:
2- local: '/templates/.fe-ci-template.yml'
批量引入 include:‘fe/*.yml’
如果要引入一盒目录下所有的 yaml 文件,并引入该目录所有子级目录下的 yaml 文件 include:‘fe/**.yml’
include:file
用于引入其他项目文件。如果有群组,那么需要加上群组路径。
1include:
2 - project: 'fe-group/my-project'
3 ref: main
4 file: 'fe/my-job.yml'
5 - project: 'test-group/my-project'
6 ref: v1.0.0
7 file: '/templates/.gitlab-ci-template.yml'
8 - project: 'be-group/my-project'
9 ref: 7871238238238hdsisidsds # git sha
10 file:
11 - '/template/.gitlab-ci-template.yml'
12 - '/templates/.test.yml'
include:template
用于引入官方模板文件
golang ci实践
目前只有 build 阶段,只是为了演示大体怎么使用,构建出镜像后,还需要部署,因为这个每个公司都不一样,可能是上k8s,可能是走负载均衡器,有的需要改 nginx 端口等,这里不做演示。
harbor仓库安装
具体见 https://www.crblog.cc/cloud-native/devops-basic.html
地址 192.168.56.83:8080
创建一个项目
gocicd基础镜像准备
cicdDockerfile 内容如下
1FROM golang:1.21-alpine
2ENV CGO_ENABLED=0 GOOS=linux GO111MODULE=on GOPROXY=https://goproxy.cn/,direct TZ=Asia/Shanghai
3RUN set -eux && sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
4RUN apk update && apk add --no-cache tzdata git sshpass openssh make docker
5RUN go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2
6RUN echo " StrictHostKeyChecking no" >> /etc/ssh/ssh_config && \
7 echo " UserKnownHostsFile /dev/null" >> /etc/ssh/ssh_config
1docker build -t go1.21cicd:latest -f cicdDockerfile .
2docker tag go1.21cicd:latest 192.168.56.83:8080/golang/gitlab-cicd-go1.21:latest
3docker login --username=admin 192.168.56.83:8080
4# 输入密码
5docker push 192.168.56.83:8080/golang/gitlab-cicd-go1.21:latest
流水线 yml 编写
就是先进行镜像构建,然后推送到harbor 仓库 示例
1variables:
2 DOCKER_IMAGE_NAME: godemo
3 IMAGE_NAME: 192.168.56.83:8080/golang/${DOCKER_IMAGE_NAME}:${CI_COMMIT_REF_SLUG}-${CI_PIPELINE_ID}
4
5image: 192.168.56.83:8080/golang/gitlab-cicd-go1.21:latest
6
7stages:
8 - build
9
10# 根据down.sh构建镜像包
11image_build:
12 stage: build
13 before_script:
14 - docker ps
15 - docker login --username=admin -p harbor12345 192.168.56.83:8080
16 - ls -al
17 script:
18 - docker build -f docker/Dockerfile -t $DOCKER_IMAGE_NAME .
19 - docker tag $DOCKER_IMAGE_NAME $IMAGE_NAME
20 - docker push $IMAGE_NAME
21 after_script:
22 - docker rmi $DOCKER_IMAGE_NAME
23 - docker rmi $IMAGE_NAME
24 tags:
25 - docker
26 only:
27 - develop
28 - main
docker/Dockerfile 内容如下
1FROM golang:1.21-alpine AS build
2ENV CGO_ENABLED=0 GOOS=linux GO111MODULE=on GOPROXY=https://goproxy.cn/,direct TZ=Asia/Shanghai
3RUN set -eux && sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
4RUN apk update && apk add --no-cache tzdata git
5
6WORKDIR /app
7
8COPY ./go.mod ./
9COPY ./go.sum ./
10
11RUN go mod tidy
12
13COPY . ./
14
15RUN go build -o godemo main.go && go clean -cache
16
17FROM alpine:3.15.0
18RUN set -eux && sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
19RUN apk update && apk add --no-cache tzdata
20ENV TZ "Asia/Shanghai"
21WORKDIR /app
22COPY --from=build /app/godemo /app/godemo
23RUN chmod +x /app/godemo
24
25EXPOSE 8000
26
27CMD ["/app/godemo"]
提交代码merge request后,就会执行流水线
可见镜像已经推送成功