gitlab runner使用docker executor處理緩存 ,工件,構建docker鏡像

首先啟動一個gitlab runner服務,并注冊一個docker executor,這是比較簡單的,此處暫不贅述

本文主要講述使用docker executor,如何處理緩存,工件,構建docker鏡像的問題,如何盡可能在保證安全的前提下加速編譯過程。

以java項目為例,配置ci/cd文件

stages:
  - package
  - build

variables:
  MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"

mvn:package:
  stage: package
  tags:
    - gr1
  image: maven:3.6.3-jdk-8
  #緩存mvn庫
  cache:
    key: mvn_repo
    paths:
      - .m2/repository
  artifacts:
    paths:
    - target/multi-renter.jar
  script:
    - mvn package

緩存:

cache:
key:mvn_repo
paths:
- .m2/repository
這是指定一個緩存,在當前的gr1這個runner中的任何job中有效,緩存會保存在主機的另一個docker container(dockers ps -a 可以看到 gitlab runner helper),并最終掛在到主機的一個位置。為什么不直接掛載到主機上呢,那樣效率不是更高嗎?當然,直接掛載到主機上,效率更高,但是卻讓當前的job和主機的某個目錄耦合,試想,如果其他人的job也往主機掛載,并且恰好掛載到和你一樣的目錄了呢?另外,不同的主機類型,比如windows和linux,目錄格式不一樣,因此你的配置要根據(jù)主機os類型而變化。而且你還要關注gitlab-runner服務可讀寫的目錄權限問題。wow,真是太復雜了。但是掛載到另一個容器,gitlab-runner幫你解決了所有的問題。只是犧牲了一點點效率。無非就是增加了壓縮和解壓緩存的時間。緩存在所屬的gitlab-runner(上述例子中所屬為:gr1)后續(xù)的所有pipeline可見。

工件:

artifacts:
paths:
- target/multi-renter.jar
工件和緩存類似,只不過其往往在一個pipeline中生存工件的后續(xù)job中可見。在web ui中也可見,但是在另一個pipeline中不可見。比如,我在mvn:package:這個job中編譯了一個jar文件,在編譯的下一個stage中想把這個jar復制到docker鏡像,就應該用工件去傳遞這個jar,而不是使用緩存。

構建docker image

構建docker鏡像,官方給了幾種方式
對于docker executor,每次都是啟動一個新的container構建,新的container是一個非常clear的環(huán)境,因此上次構建使用到的base image和構建的緩存都無法使用,所以閱讀下述方式時,重點關注如何加速構建速度,解決方式是否復雜以及帶來的問題。

kaniko

kaniko可以在不使用docker特權模式下構建docker鏡像,并且可以利用container registry加速構建。主要過程如下

  1. kaniko在--cache-dir目錄下查找base image緩存(即docker file中的from命令)這里需要注意,如果這里沒有命中,kaniko會去download image,但是不會寫入緩存,因此,即使你通過卷持久化了這個目錄,下次執(zhí)行依然不會命中。因此我們需要提前將鏡像緩存到--cache-dir,kanico關于cache base image的講解。我使用了一下warmer,發(fā)現(xiàn)按照官網(wǎng)提供的命令,不加-f,如果緩存沒命中,shell返回0,但實際報錯了,加上-v debug可以看到,如果緩存命中了,什么都不發(fā)生。加上-f,則每次都會強制pull鏡像覆蓋cache,消耗一定時間。這可能是一個bug。我們要做的就是,先使用warmer下載base image,然后把緩存的目錄掛載到kanico容器中使用(配置/srv/gitlab-runner/config/config.toml文件)。我只是想編譯而已,為何要我解決如此麻煩的緩存問題?另外注意一下,kanico的executor和warmer都有相同名稱的參數(shù),所以這里需要自己體會下其含義,不要搞混了。
  2. kaniko對run命令進行緩存,因此執(zhí)行run命令前,會到指定的遠程registry查看有沒有已經(jīng)緩存的層,命中的話就使用緩存
  3. kaniko將構建好的image提交到registry
    這是個好東西,我簡單試用了一下,官方給的示例多是k8s中用,如果不在k8s中用,如何緩存base image是一個問題,我暫時還沒有在官方找到一個比較好的方式。
    kaniko拉取base image方式和docker pull不一樣,測試使用warmer緩存java:8和使用docker pull java:8一個耗時不到一分鐘,一個約3分鐘。
docker:build:
  stage: build
  tags:
    - gr1
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG --insecure

docker in docker

這種方式需要以特權方式執(zhí)行docker,帶來不安全的因素。同時,構建也是非常慢的。因此也需要使用緩存去加速構建。原理是:docker先從registry拉取上次構建的鏡像。然后構建的時候指定--cache-from=<last-image>,也就是說以上次的鏡像作為緩存構建。構建完畢后,上次鏡像,作為下次的構建緩存使用。但是需要注意的是,仍需要花費一部分時間在download image上。如果registry在內(nèi)網(wǎng),其實下載和上傳都是蠻快的,可以接受。

docker:build:
  stage: build
  tags:
    - gr1
  image: docker:19.03.1
  services:
    - name: docker:19.03.1-dind
      command: ['--insecure-registry=your.registry.com:port']
  variables:
    # Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled
    DOCKER_HOST: tcp://docker:2375
    DOCKER_TLS_CERTDIR: ""
    #DOCKER_TLS_CERTDIR: "/certs"
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker pull $CI_REGISTRY_IMAGE:latest || true
    - docker build --cache-from $CI_REGISTRY_IMAGE:latest --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --tag $CI_REGISTRY_IMAGE:latest --build-arg JAR_FILE=target/*.jar .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest

DOCKER_HOST的配置參見docker dockerhub,主要注意一下證書如果配置了,端口號會是2376,否則是2375。
如果使用了不安全的registry,那么需要指定command: ['--insecure-registry=your.registry.com:port']

docker socket binding

由于此方式最終實際上是使用的host的docker daemon,因此,鏡像和構建的緩存都是直接使用host中的緩存。當然,有利就有弊,此種方式的缺點有:

  1. 由于共享了主機的docker socket,所以docker命令都是向主機發(fā)出的命令,比如docker rm -f $(docker ps -a -q)會把主機上的所有docker容器刪除了,包括gitlab-runner容器。
  2. 并發(fā)工作可能無法正常執(zhí)行;創(chuàng)建具有特定名稱的容器,則它們可能會相互沖突。
  3. 將源倉庫中的文件和目錄共享到容器中可能無法正常工作,因為卷安裝是在主機而不是構建容器的上下文中完成的。
    你必須時刻注意,你的docker命令是在主機執(zhí)行的,而不是容器內(nèi)部。
  4. 需要修改gitlab-runner的配置,掛載主機的docker socket,但是不同的host system的sock文件路徑不同,這目前只能在host上配置。
  5. 在k8s中,docker binding脫離了k8s的控制,是非常危險的行為。有的k8s集群通過名稱空間做環(huán)境隔離,想想一下,開發(fā)環(huán)境的docker命令刪除了線上環(huán)境的容器。
如何綁定docker.sock?

修改gitlab-runner服務的配置文件 /srv/gitlab-runner/config/config.toml
volumes = ["/cache","/var/run/docker.sock:/var/run/docker.sock"]

[[runners]]
  name = "gr1"
  url = "http://gitlab.lbl.com"
  token = "mJJWevdY42sJQ84syyN9"
  executor = "docker"
  [runners.custom_build_dir]
  [runners.docker]
    tls_verify = false
    image = "ruby:2.6"
    privileged = true
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache","/var/run/docker.sock:/var/run/docker.sock"]
    shm_size = 0
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]

ci/cd文件配置

docker:build:
  stage: build
  tags:
    - gr1
  image:
    name: docker:19.03.1
#  before_script:
    #登錄到gitlab集成的注冊表
#    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --tag $CI_REGISTRY_IMAGE:latest --build-arg JAR_FILE=target/*.jar .
    #推送到gitlab集成的注冊表
#    - docker push $CI_REGISTRY_IMAGE:latest

接下來,讓我們看看,如果使用k8s執(zhí)行器

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容