Skip to content

Commit 7e43442

Browse files
committed
Auto merge of #119290 - Kobzol:ci-docker-registry-cache, r=Mark-simulacrum
Cache CI Docker images in ghcr registry This PR changes the way `rust-lang` caches Docker images used in CI workflows. Before, the intermediate Docker layers were manually exported from `docker history` and backed up in S3. However, this approach doesn't work any more with the Docker version used by GitHub Actions since August 2023. We had to revert to disabling Docker BuildKit to make the old caching work, but this workaround will stop working eventually, after GitHub updates Docker again and the old build backend will be removed. This PR changes the caching to use [Docker caching](https://docs.docker.com/build/cache/) instead. There are several backends for the cache, for our use-case S3 and Docker registry makes sense. This PR uses the Docker registry backend and uses the ghcr.io registry. The caching creates a Docker image labeled `rust-ci`, which is currently stored to the `ghcr.io/rust-lang-ci` package registry. This image appears [here](https://ghcr.io/rust-lang-ci/rust-ci). The image is stored in `rust-lang-ci` and not `rust-lang`, because `try` and `auto` builds run in the context of that repository, so the used `GITHUB_TOKEN` has permissions for it (unlike for `rust-lang`). For pull request CI runs, the provided `GITHUB_TOKEN` reduces its permissions automatically to `packages: read`, which means that we won't be able to write the Docker image. If we're not able to write, we won't have anything to read. So I disabled the caching entirely for PR runs (it makes it slightly faster to build the Docker image if we don't have to deal with exporting and using a separate build driver). Note that before this PR, we also weren't able to read or write the cache on PR runs. Rustup part of this change is [here](rust-lang/rustup#3648). Related issue: rust-lang/infra-team#81 r? `@Mark-Simulacrum`
2 parents 8a0b5ae + 09e0d4f commit 7e43442

File tree

3 files changed

+64
-58
lines changed

3 files changed

+64
-58
lines changed

.github/workflows/ci.yml

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ name: CI
2828
- "**"
2929
permissions:
3030
contents: read
31+
packages: write
3132
defaults:
3233
run:
3334
shell: bash
@@ -42,6 +43,7 @@ jobs:
4243
CI_JOB_NAME: "${{ matrix.name }}"
4344
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
4445
HEAD_SHA: "${{ github.event.pull_request.head.sha || github.sha }}"
46+
DOCKER_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
4547
SCCACHE_BUCKET: rust-lang-ci-sccache2
4648
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
4749
CACHE_DOMAIN: ci-caches.rust-lang.org
@@ -172,6 +174,7 @@ jobs:
172174
CI_JOB_NAME: "${{ matrix.name }}"
173175
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
174176
HEAD_SHA: "${{ github.event.pull_request.head.sha || github.sha }}"
177+
DOCKER_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
175178
SCCACHE_BUCKET: rust-lang-ci-sccache2
176179
DEPLOY_BUCKET: rust-lang-ci2
177180
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
@@ -554,6 +557,7 @@ jobs:
554557
CI_JOB_NAME: "${{ matrix.name }}"
555558
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
556559
HEAD_SHA: "${{ github.event.pull_request.head.sha || github.sha }}"
560+
DOCKER_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
557561
SCCACHE_BUCKET: rust-lang-ci-sccache2
558562
DEPLOY_BUCKET: rust-lang-ci2
559563
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"

src/ci/docker/run.sh

+58-58
Original file line numberDiff line numberDiff line change
@@ -74,25 +74,6 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
7474

7575
cksum=$(sha512sum $hash_key | \
7676
awk '{print $1}')
77-
78-
url="https://$CACHE_DOMAIN/docker/$cksum"
79-
80-
echo "Attempting to download $url"
81-
rm -f /tmp/rustci_docker_cache
82-
set +e
83-
retry curl --max-time 600 -y 30 -Y 10 --connect-timeout 30 -f -L -C - \
84-
-o /tmp/rustci_docker_cache "$url"
85-
86-
docker_archive_hash=$(sha512sum /tmp/rustci_docker_cache | awk '{print $1}')
87-
echo "Downloaded archive hash: ${docker_archive_hash}"
88-
89-
echo "Loading images into docker"
90-
# docker load sometimes hangs in the CI, so time out after 10 minutes with TERM,
91-
# KILL after 12 minutes
92-
loaded_images=$(/usr/bin/timeout -k 720 600 docker load -i /tmp/rustci_docker_cache \
93-
| sed 's/.* sha/sha/')
94-
set -e
95-
printf "Downloaded containers:\n$loaded_images\n"
9677
fi
9778

9879
dockerfile="$docker_dir/$image/Dockerfile"
@@ -103,46 +84,65 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
10384
context="$script_dir"
10485
fi
10586
echo "::group::Building docker image for $image"
106-
107-
# As of August 2023, Github Actions have updated Docker to 23.X,
108-
# which uses the BuildKit by default. It currently throws aways all
109-
# intermediate layers, which breaks our usage of S3 layer caching.
110-
# Therefore we opt-in to the old build backend for now.
111-
export DOCKER_BUILDKIT=0
112-
retry docker \
113-
build \
114-
--rm \
115-
-t rust-ci \
116-
-f "$dockerfile" \
117-
"$context"
118-
echo "::endgroup::"
119-
120-
if [ "$CI" != "" ]; then
121-
s3url="s3://$SCCACHE_BUCKET/docker/$cksum"
122-
upload="aws s3 cp - $s3url"
123-
digest=$(docker inspect rust-ci --format '{{.Id}}')
124-
echo "Built container $digest"
125-
if ! grep -q "$digest" <(echo "$loaded_images"); then
126-
echo "Uploading finished image $digest to $url"
127-
set +e
128-
# Print image history for easier debugging of layer SHAs
129-
docker history rust-ci
130-
docker history -q rust-ci | \
131-
grep -v missing | \
132-
xargs docker save | \
133-
gzip | \
134-
$upload
135-
set -e
136-
else
137-
echo "Looks like docker image is the same as before, not uploading"
138-
fi
139-
# Record the container image for reuse, e.g. by rustup.rs builds
140-
info="$dist/image-$image.txt"
141-
mkdir -p "$dist"
142-
echo "$url" >"$info"
143-
echo "$digest" >>"$info"
144-
cat "$info"
87+
echo "Image input"
88+
cat $hash_key
89+
echo "Image input checksum ${cksum}"
90+
91+
# Print docker version
92+
docker --version
93+
94+
# On non-CI or PR jobs, we don't have permissions to write to the registry cache, so we should
95+
# not use `docker login` nor caching.
96+
if [[ "$CI" == "" ]] || [[ "$PR_CI_JOB" == "1" ]];
97+
then
98+
retry docker build --rm -t rust-ci -f "$dockerfile" "$context"
99+
else
100+
REGISTRY=ghcr.io
101+
# Most probably rust-lang-ci, but in general the owner of the repository where CI runs
102+
REGISTRY_USERNAME=${GITHUB_REPOSITORY_OWNER}
103+
# Tag used to push the final Docker image, so that it can be pulled by e.g. rustup
104+
IMAGE_TAG=${REGISTRY}/${REGISTRY_USERNAME}/rust-ci:${cksum}
105+
# Tag used to cache the Docker build
106+
# It seems that it cannot be the same as $IMAGE_TAG, otherwise it overwrites the cache
107+
CACHE_IMAGE_TAG=${REGISTRY}/${REGISTRY_USERNAME}/rust-ci-cache:${cksum}
108+
109+
# Log into the Docker registry, so that we can read/write cache and the final image
110+
echo ${DOCKER_TOKEN} | docker login ${REGISTRY} \
111+
--username ${REGISTRY_USERNAME} \
112+
--password-stdin
113+
114+
# Enable a new Docker driver so that --cache-from/to works with a registry backend
115+
docker buildx create --use --driver docker-container
116+
117+
# Build the image using registry caching backend
118+
retry docker \
119+
buildx \
120+
build \
121+
--rm \
122+
-t rust-ci \
123+
-f "$dockerfile" \
124+
--cache-from type=registry,ref=${CACHE_IMAGE_TAG} \
125+
--cache-to type=registry,ref=${CACHE_IMAGE_TAG},compression=zstd \
126+
--output=type=docker \
127+
"$context"
128+
129+
# Print images for debugging purposes
130+
docker images
131+
132+
# Tag the built image and push it to the registry
133+
docker tag rust-ci "${IMAGE_TAG}"
134+
docker push "${IMAGE_TAG}"
135+
136+
# Record the container registry tag/url for reuse, e.g. by rustup.rs builds
137+
# It should be possible to run `docker pull <$IMAGE_TAG>` to download the image
138+
info="$dist/image-$image.txt"
139+
mkdir -p "$dist"
140+
echo "${IMAGE_TAG}" > "$info"
141+
cat "$info"
142+
143+
echo "To download the image, run docker pull ${IMAGE_TAG}"
145144
fi
145+
echo "::endgroup::"
146146
elif [ -f "$docker_dir/disabled/$image/Dockerfile" ]; then
147147
if isCI; then
148148
echo Cannot run disabled images on CI!

src/ci/github-actions/ci.yml

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ x--expand-yaml-anchors--remove:
3434
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
3535
# commit of PR sha or commit sha. `GITHUB_SHA` is not accurate for PRs.
3636
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
37+
DOCKER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3738

3839
- &public-variables
3940
SCCACHE_BUCKET: rust-lang-ci-sccache2
@@ -301,6 +302,7 @@ on:
301302

302303
permissions:
303304
contents: read
305+
packages: write
304306

305307
defaults:
306308
run:

0 commit comments

Comments
 (0)