使用GitLab CI/CD 构建容器镜像的几种方式

发布于:2024-05-15 ⋅ 阅读:(150) ⋅ 点赞:(0)

GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab :https://gitlab.cn/install?channel=content&utm_source=csdn 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。

更多关于极狐GitLab :https://gitlab.cn 或者 DevOps 的最佳实践,可以关注文末的极狐GitLab 公众号。

学习极狐GitLab 的相关资料:

  1. 极狐GitLab 官网:https://gitlab.cn
  2. 极狐GitLab 官网文档:https://docs.gitlab.cn
  3. 极狐GitLab 论坛:https://forum.gitlab.cn/
  4. 极狐GitLab 安装配置:https://gitlab.cn/install
  5. 极狐GitLab 资源中心:https://resources.gitlab.cn

搜索【极狐GitLab】公众号,后台输入加群,备注gitlab,即可加入官方微信技术交流群。

极狐GitLab 公众号后台回复新手指南,免费领取极狐GitLab 新手指南一份,从零到一快速上手极狐GitLab。

使用极狐GitLab CI/CD 构建容器镜像是比较常用的 DevOps 流程,目前主要有以下两种方式:

  • docker 构建镜像
  • kaniko 构建镜像

本文演示使用极狐GitLab 自带的容器镜像仓库,开启方法也很简单,编辑配置文件 /etc/gitlab/gitlab.rb,添加以下配置:

registry_external_url 'https://gitlab.leffss.cn:5050'

然后执行 gitlab-ctl reconfigure ; gitlab-ctl restart 就生效了。

1. docker 构建镜像

1.1 shell 执行器方式

原理:job 中直接调用宿主机的 docker 命令进行镜像的构建与上传

注册 runner,使用最基本的 shell 执行器:

gitlab-runner register -n \
  --url https://gitlab.leffss.cn/ \
  --registration-token oYJBJ-y3rCP6zs9e5y9J \
  --executor shell \
  --tag-list shell \
  --description "My Shell Runner"

添加 gitlab-runner 用户到 docker 组

usermod -aG docker gitlab-runner

验证 gitlab-runner 用户是否能够使用 docker

sudo -u gitlab-runner -H docker info

创建 .gitlab-ci.yml,其中的 build job 直接调用宿主机 docker 命令

before_script:
  - docker info
  - echo $CI_REGISTRY_PASSWORD | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin

build:
  stage: build
  script:
    - docker pull $CI_REGISTRY_IMAGE:latest || true
    - docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG -t $CI_REGISTRY_IMAGE:latest .
    - docker push $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
  rules:
    - if: $CI_COMMIT_TAG
  tags:
    - shell

然后创建一个 tag 触发流水线。

1.2 挂载 docker.sock 文件方式

原理:job 中启动一个 docker 客户端,然后调用宿主机的 docker socket 文件进行镜像的构建与上传

注册 runner,使用 docker 执行器并挂载属主机 docker 的 socket 文件:

gitlab-runner register -n \
  --url https://gitlab.leffss.cn/ \
  --registration-token HqxWfyRQDosB4iBXWudg \
  --executor docker \
  --description "My Docker Runner" \
  --docker-image "docker:20.10.24" \
  --tag-list socket \
  --docker-extra-hosts gitlab.leffss.cn:172.16.10.9 \
  --docker-pull-policy if-not-present \
  --docker-volumes /var/run/docker.sock:/var/run/docker.sock

创建 .gitlab-ci.yml,其中的 build job 就是一个 docker 客户端,它通过 /var/run/docker.sock 文件访问宿主机 docker 服务端

image: docker:20.10.24

before_script:
  - docker info
  - echo $CI_REGISTRY_PASSWORD | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin

build:
  stage: build
  script:
    - docker pull $CI_REGISTRY_IMAGE:latest || true
    - docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG -t $CI_REGISTRY_IMAGE:latest .
    - docker push $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
  rules:
    - if: $CI_COMMIT_TAG
  tags:
    - socket

然后创建一个 tag 触发流水线即可。

1.3 dind(docker-in-docker) 方式

原理:job 不直接调用宿主机 docker 命令,也不调用宿主机的 docker socket 文件,而是在 privileged(特权模式)下以 service 的方式启动一个 docker 服务器与客户端进行镜像的构建与上传

使用 dind,runner 一般使用以下类型的执行器:

1.3.1 使用 docker 执行器

注册 runner,使用 docker 执行器并设置开启特权模式:

gitlab-runner register -n \
  --url https://gitlab.leffss.cn/ \
  --registration-token oYJBJ-y3rCP6zs9e5y9J \
  --executor docker \
  --description "My Docker Runner" \
  --docker-image "docker:20.10.24" \
  --tag-list dind \
  --docker-privileged \
  --docker-extra-hosts gitlab.leffss.cn:172.16.10.15 \
  --docker-pull-policy if-not-present \
  --docker-volumes "/certs/client"

创建 .gitlab-ci.yml,首先 service 启动了一个 docker 服务端,build job 是一个 docker 客户端,它通过变量 DOCKER_HOST 访问 service 启动的 docker 服务端

image: docker:20.10.24

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_HOST: tcp://docker:2376
  DOCKER_TLS_CERTDIR: "/certs"

services:
  - docker:20.10.24-dind

before_script:
  - docker info
  - echo $CI_REGISTRY_PASSWORD | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin

build:
  stage: build
  script:
    - docker pull $CI_REGISTRY_IMAGE:latest || true
    - docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG -t $CI_REGISTRY_IMAGE:latest .
    - docker push $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
  rules:
    - if: $CI_COMMIT_TAG
  tags:
    - dind

然后创建一个 tag 触发流水线即可。

如果您的镜像仓库未开启 https,可以按照以下设置解决:

services:
  - name: docker:20.10.24-dind
    entrypoint: ["dockerd-entrypoint.sh"]
    command: ["--insecure-registry=registry.example.com"]
1.3.2 使用 kubernetes 执行器

k8s 执行器的原理和使用方法和上面的 docker 执行器是一样的,只是注册配置有点差别。因为没有准备 k8s 环境,就不做演示了。

Helm 方式安装 runner 配置文件 values.yaml

runners:
  config: |
    [[runners]]
      [runners.kubernetes]
        image = "docker:20.10.24"
        privileged = true
      [[runners.kubernetes.volumes.empty_dir]]
        name = "docker-certs"
        mount_path = "/certs/client"
        medium = "Memory"

2. kaniko 构建镜像

上面的几种方式构建镜像虽然都比较方便,但是会有以下问题:

  • 直接访问主机 docker 进程,存在安全隐患

  • 需要使用 privileged mode ,存在安全隐患

  • 因为需要额外运行 docker server 进程,性能和速度上不太理想

kaniko 是谷歌开源的一款用来构建容器镜像的工具。与 docker 不同,kaniko 并不依赖于 docker server 进程,完全是根据 Dockerfile 的内容逐行执行命令来构建镜像,这就使得在一些无法获取 docker server 进程的环境下也能够构建镜像。

也就是说 kaniko 可以兼顾一定的速度,并且没有安全隐患。

使用 kaniko,runner 一般使用以下类型的执行器:

这里以 docker 执行器为例

2.1 注册 runner

gitlab-runner register -n \
  --url https://gitlab.leffss.cn/ \
  --registration-token HqxWfyRQDosB4iBXWudg \
  --executor docker \
  --description "My Docker Runner" \
  --docker-image "docker:20.10.24" \
  --tag-list kaniko \
  --docker-extra-hosts gitlab.leffss.cn:172.16.10.9 \
  --docker-pull-policy if-not-present

2.2 打包镜像

注意事项:

  • kaniko 官方 debug 镜像:gcr.io/kaniko-project/executor:debug,国内可能无法下载,可以使用 willdockerhub/kaniko-executor:debug 代替

创建 .gitlab-ci.yml,build job 中 kaniko 直接解析 Dockerfile

build:
  stage: build
  image:
    name: willdockerhub/kaniko-executor:debug
    entrypoint: [""]
  script:
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
    - >-
      /kaniko/executor
      --context "${CI_PROJECT_DIR}"
      --dockerfile "${CI_PROJECT_DIR}/Dockerfile"
      --cache=true
      --cache-repo "${CI_REGISTRY_IMAGE}:latest"
      --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
      --destination "${CI_REGISTRY_IMAGE}:latest"
  rules:
    - if: $CI_COMMIT_TAG
  tags:
    - kaniko
  • 如果 CI_REGISTRY 未开启 https,可以添加参数解决:--insecure-registry "${CI_REGISTRY}"

然后创建一个 tag 触发流水线

如果不需要推送到远端,则可以这样:

build:
  stage: build
  image:
    name: willdockerhub/kaniko-executor:debug
    entrypoint: [""]
  script:
    - >-
      /kaniko/executor
      --context "${CI_PROJECT_DIR}"
      --dockerfile "${CI_PROJECT_DIR}/Dockerfile"
      --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
      --tarPath "${CI_PROJECT_DIR}/test.tar"
      --no-push
  rules:
    - if: $CI_COMMIT_TAG
  tags:
    - kaniko

2.3 自签证书 registry

如果镜像仓库使用的自签证书,上传镜像会遇到 x509 报错:

$ /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --no-push
INFO[0000] Downloading base image registry.gitlab.example.com/group/docker-image
error building image: getting stage builder for stage 0: Get https://registry.gitlab.example.com/v2/: x509: certificate signed by unknown authority

解决方法就是把自签 ca 根证书添加到 kaniko 相关 ssl 证书目录中即可:

before_script:
  - mkdir -p /kaniko/.docker
  - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
  - |
    echo "-----BEGIN CERTIFICATE-----
    ...
    ...
    ...
    -----END CERTIFICATE-----" >> /kaniko/ssl/certs/additional-ca-cert-bundle.crt