gitlab-runner 中的 Docker-in-Docker
筆者個人理解:gitlab-runner 安裝后就是一個監聽狀態的 runner,而通過 gitlab-runner register 注冊的“實例”其實只是預定義的配置節,當消息抵達后,gitlab-runner 根據消息內容選擇相應的配置節啟動執行線程。為了方便闡述和理解,本文也將每個配置節/執行線程稱為 runner 實例。
runner executor
runner 實例的執行環境,一般用的較多的是 shell 和 docker,這兩者的區別無需贅述。
讓人困惑的是其它一些 executor:比如 Docker-SSH 和 Docker-SSH+machine,還好從 GitLab Runner 10.0 開始, 這兩者就被廢棄了,并且將在后續某個版本中移除;還有 Docker machine,這個概念原本是 Docker 提出的,但是后面同樣被 Docker 棄用了,只是 GitLab 為了向前兼容保留了下來,也可以不用細究。
對于 docker executor 來說,runner 執行 job 的流程如下(摘自官網):
- The runner starts a Docker container using the defined entrypoint. The default from Dockerfile that may be overridden in the .gitlab-ci.yml file.
- The runner attaches itself to a running container.
- The runner prepares a script (the combination of before_script, script, and after_script).
- The runner sends the script to the container’s shell stdin and receives the output.
顯然,第 1 步要啟動容器,如果 runner 本身是以 docker 容器方式安裝運行的,那么就涉及到 Docker-in-Docker 的概念了。
Docker-in-Docker
有些時候,我們需要在容器內部執行 docker 指令,一般有兩種方式:
- 掛載宿主機 docker 環境。啟動容器時掛載
/var/run/docker.sock,這樣在容器內執行 docker 指令其實就等同于在容器外(宿主機中)執行 docker 指令。比如docker build構建一個鏡像,該鏡像并不存在于容器內部,而是在宿主機中。所以該方法并不是嚴格意義上的docker in docker。 - 容器內部有自己的一套 docker 環境。使用
docker:dind鏡像,可以直接使用它作為主容器,或是作為其它容器的服務容器(其它容器與之通信)。它在docker鏡像(該鏡像只包含客戶端指令集)基礎上安裝了Docker Daemon,因此可作為獨立的 docker 環境使用,是真正意義上的docker in docker。然而,除非你真正需要在容器內嵌套容器,或者某些場景下無法使用第 1 種方式,否則還是建議避免使用該方式。
這兩種方式需要執行指令的容器處于 privileged mode,有一定的安全風險(privileged=true 將使得容器內 root 擁有宿主 root 的權限,否則只是宿主機上的普通用戶),因此市面上又出現了一種 Using Nestybox sysbox Docker runtime 的方法,感興趣的朋友請查閱參考資料,此處按過不表。
in gitlab-runner
如前所述,以 docker 方式安裝 runner,且 executor 采用 docker,那么就要 Docker-in-Docker。因為 runner 只是啟動新容器,不要求啟動的容器在 runner 容器內部,我們可以采用第 1 種方式,如下:
docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \ # 掛載 socket
gitlab/gitlab-runner:latest
另外,如果 docker executor 在 CI/CD job 中涉及到 docker 指令,那么也要 Docker-in-Docker。關鍵步驟如下:
- 注冊 runner 實例,并配置其啟動的容器為 privileged mode(注意配置的是每次
job執行時啟動的容器,而非 runner 所在的容器,且 runner 并不一定是 docker 形式)。
sudo gitlab-runner register -n \
--executor docker \
--docker-image "docker:20.10.16" \
--docker-privileged \ # privileged mode
--other arguments
- 接下來,可以選擇任一種方式實現 Docker-in-Docker:
- 在
config.toml中增加卷映射volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]。 - 或者在
.gitlab-ci.yml中指定docker:dind如:
services: - docker:20.10.16-dind - 在
這里再強調下:如果需要共享宿主機 docker 配置(如 /etc/docker/daemon.json),則務必采用第一種方式。
TLS 配置
如果在第 2 步采用 docker:dind 方式,那么由于涉及到容器間通信,需要選擇是否啟用 TLS。
若是,則在注冊 runner 實例時,增加一個參數 --docker-volumes "/certs/client", 也可手動編輯 config.toml,增加卷映射 volumes = ["/certs/client", "/cache"];然后在 .gitlab-ci.yml 中設置變量 DOCKER_TLS_CERTDIR: "/certs"。
若否,則在 .gitlab-ci.yml 中設置變量 DOCKER_TLS_CERTDIR: "" 和 DOCKER_HOST: tcp://docker:2375。
TLS 若未正確配置,會報 Cannot connect to the Docker daemon at tcp://docker:2375. Is the docker daemon running? 錯誤。

浙公網安備 33010602011771號