CSDN 中文章不一定能及时更新,欢迎关注我的博客查看最新版本:许盛的博客
背景
在 gitlab ci
中使用 docker
容器作为 runner
时,如果在命令中又需要调用 docker
,这就相当于在 Docker
容器中使用 Docker
了,就是 Docker In Docker
,简称 dind
。
在功能上来说, dind
配置好之后,除了安全性和速度的缺点外,使用起来没有任何问题。
在官方文档上有这样一句描述:
大概意思就是每个 job 都在新的环境中运行,都有自己的 docker 引擎实例,所以并没有层缓存。
这就导致我们在使用 docker build
命令的时候,每次都需要重新拉取 Dockerfile
中依赖的基础镜像,也无法利用上一次构建任务触发后遗留的镜像层缓存,因为宿主机上压根就没有缓存文件。
解决
最初的想法是,能不能在构建过程中,将镜像缓存层给存到宿主机上,在查阅文档的过程中,发现 docker build
提供了一个 —cache-from
参数,可以指定某个镜像,作为构建过程中的缓存源。
这样的话,思路就有了,因为每次构建完成后,虽然中间的缓存层以及打包出来的镜像,在宿主机上都没有了,但是最终的镜像上传到了公司内的 Harbor
上。
这样每次开始 docker build
之前,将上一次构建的镜像 pull
到本地,然后使用 --cache-from
参数指定为缓存源即可。
例如官方文档提供的 yaml
配置:
image: docker:19.03.12
services:
- docker:19.03.12-dind
variables:
# Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
build:
stage: build
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 .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $CI_REGISTRY_IMAGE:latest
每一次构建开始前,都会将最近的镜像拉取下来,并指定为镜像源。
针对多阶段构建
以上这种方式,针对多阶段构建的情况,其实稍稍有点问题,因为在多阶段构建过程中,最终生成的镜像,只包含了最后一个阶段的镜像层,而之前阶段的镜像层,都丢弃掉了。
这样就算指定了最终的镜像作为缓存源,也无法在前置的构建阶段起到作用。
这时候 docker build
的 —target
参数就起作用了。
使用 —target 参数,可以在构建过程中,指定构建哪个阶段的镜像,这样我们就可以针对单个阶段构建镜像,并 push
到 harbor
上,并在下次构建时 pull
下来作为缓存源使用。
示例如下:
build: # 构建镜像
stage: build
image: docker:stable
script:
# 将 builder 镜像拉下来,用作多阶段构建中第一阶段的缓存使用
- docker pull $HARBOR:builder || true
# 进行第一阶段构建,产出 builder 镜像,用作下一次的缓存
- docker build --target builder --cache-from $HARBOR:builder -t $HARBOR:builder -f _ci/Dockerfile .
# 将 latest 镜像拉下来,用作第二阶段的构建的缓存,根据 Dockerfile 实际情况判断是否需要这一步优化
- docker pull $HARBOR:latest || true
# 开始实际的构建
- docker build --build-arg VERSION_TAG=$CI_COMMIT_TAG --build-arg COMMIT_ID=$CI_COMMIT_SHORT_SHA --cache-from $HARBOR:builder --cache-from $HARBOR:latest -f _ci/Dockerfile -t $HARBOR:$CI_COMMIT_SHORT_SHA .
- docker push $HARBOR:$CI_COMMIT_SHORT_SHA
# 上传这一次生成的 builder 镜像
- docker push $HARBOR:builder