在 docker-in-docker 環境中使用 cache-from 提升編譯速度

提升 docker build 時間


在現代 CI/CD 的環境流程中,使用 Docker In Docker 來編譯容器已經相當流行了,像是 GitLab CIDrone 都是全走 Docker 環境,然而有很多人建議盡量不要在 CI 環境使用 Docker In Docker,原因在於 CI 環境無法使用 Host Image 資料,導致每次要上傳 Image 到 Docker Hub 時都需要重新下載所有的 Docker Layer,造成每次跑一次流程都會重複花費不少時間,而這個問題在 v1.13 時被解決,現在只要在編譯過程指定一個或者是多個 Image 列表,先把 Layer 下載到 Docker 內,接著對照 Dockerfile 內只要有被 Cache 到就不會重新再執行,講得有點模糊,底下直接拿實際例子來看看。



教學影片



歡迎訂閱我的 Youtube 頻道: http://bit.ly/youtube-boy


更多實戰影片可以參考我的 Udemy 教學系列



使用 –cache-from 加速編譯


在 Docker v1.13 版本中新增了 –cache-from 功能讓開發者可以在編譯 Dockerfile 時,同時指定先下載特定的 Docker Image,透過先下載好的 Docker Layer 在跟 Dockerfile 內文比較,如果有重複的就不會在被執行,這樣可以省下蠻多編譯時間,底下拿個簡單例子做說明,假設我們有底下的 Dockerfile


FROM alpine:3.9
LABEL maintainer=“maintainers@gitea.io”

EXPOSE 22 3000

RUN apk –no-cache add
bash
ca-certificates
curl
gettext
git
linux-pam
openssh
s6
sqlite
su-exec
tzdata

RUN addgroup
-S -g 1000
git &&
adduser
-S -H -D
-h /data/git
-s /bin/bash
-u 1000
-G git
git &&
echo “git:$(dd if=/dev/urandom bs=24 count=1 status=none | base64)” | chpasswd

ENV USER git
ENV GITEA_CUSTOM /data/gitea

VOLUME [“/data”]

ENTRYPOINT [“/usr/bin/entrypoint”]
CMD [“/bin/s6-svscan”, “/etc/s6”]

COPY docker /
COPY –from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
RUN ln -s /app/gitea/gitea /usr/local/bin/gitea


透過底下命令列可以編譯出 Image


$ docker build -t gitea/gitea .

而在命令列內可以看到花最多時間的是底下這個步驟


RUN apk –no-cache add 
bash
ca-certificates
curl
gettext
git
linux-pam
openssh
s6
sqlite
su-exec
tzdata

該如何透過 –cache-from 機制繞過此步驟加速 Docker 編譯時間,其實很簡單只要在網路上找到原本 image 就可以繞過此步驟,開發者總會知道原本的 Dockerfile 是用來編譯出哪一個 Image 名稱


$ docker build –cache-frome=gitea/gitea -t gitea/gitea .


從上圖可以知道時間最久的步驟已經被 cache 下來了,所以 cache-from 會事先把 Image 下載下來,接著就可以使用該 Image 內的 cache layer 享受簡短 build time 的好處。


在 Gitlab CI 使用 cache-from


在 Gitlab CI 如何使用,其實很簡單,請參考此範例


image: docker:latest
services:
- docker:dind
stages:
- build
- test
- release
variables:
CONTAINER_IMAGE: registry.anuary.com/$CI_PROJECT_PATH
DOCKER_DRIVER: overlay2
build:
stage: build
script:
- docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.anuary.com
- docker pull $CONTAINER_IMAGE:latest
- docker build –cache-from $CONTAINER_IMAGE:latest –build-arg NPM_TOKEN=${NPM_TOKEN} -t $CONTAINER_IMAGE:$CI_BUILD_REF -t $CONTAINER_IMAGE:latest .
- docker push $CONTAINER_IMAGE:$CI_BUILD_REF
- docker push $CONTAINER_IMAGE:latest

這時候你會問時間到底差了多久,在 Node.js 內如果沒有使用 cache,每次 CI 時間至少會多不少時間,取決於開發者安裝多少套件,我會建議如果是使用 multiple stage build 請務必使用 cache-from


在 Drone 如何使用 –cache-from


在 Drone 1.0 架構內,可以架設多台 Agent 服務加速 CI/CD 流程,但是如果想要跨機器的 storage 非常困難,所以有了 cache-from 後,就可以確保多台 agent 享有 docker cache layer 機制。底下來看看 plugins/docker 該如何設定。


- name: publish
pull: always
image: plugins/docker:linux-amd64
settings:
auto_tag: true
auto_tag_suffix: linux-amd64
cache_from: appleboy/drone-telegram
daemon_off: false
dockerfile: docker/Dockerfile.linux.amd64
password:
from_secret: docker_password
repo: appleboy/drone-telegram
username:
from_secret: docker_username
when:
event:
exclude:
- pull_request

這邊拿公司的一個環境當作範例,在還沒使用 cache 前編譯時間為 2 分 30 秒,後來使用 cache-from 則變成 30 秒


結論


使用 –cache-from 需要額外多花下載 Image 檔案的時間,所以開發者需要評估下載 Image 時間跟直接在 Dockerfile 內直接執行的時間差,如果差很多就務必使用 –cache-from。不管是不是應用在 Docker In Docker 內,假如您需要改別人 Dockerfile,請務必先下載對應的 Docker Image 在執行端,這樣可以省去不少 docker build 時間,尤其是在 Dockerfile 內使用到 apt-get instllnpm install 這類型的命令。



comments powered by Disqus