Skip to content

Commit 514883d

Browse files
authored
Merge pull request #1399 from mathbunnyru/asalikhov/arm_builds
Support arm builds
2 parents 493a9ba + efb980a commit 514883d

File tree

6 files changed

+144
-10
lines changed

6 files changed

+144
-10
lines changed

.github/workflows/docker.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ jobs:
3232
!contains(github.event.pull_request.title, 'ci skip')
3333
3434
steps:
35+
# Setup docker to build for multiple platforms, see:
36+
# https://github.com/docker/build-push-action/tree/master#usage
37+
# https://github.com/docker/build-push-action/blob/master/docs/advanced/multi-platform.md
38+
- name: Set up QEMU (for docker buildx)
39+
uses: docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # dependabot updates to latest release
40+
41+
- name: Set up Docker Buildx (for multi-arch builds)
42+
uses: docker/setup-buildx-action@0d135e0c2fc0dba0729c1a47ecfcf5a3c7f8579e # dependabot updates to latest release
43+
3544
- name: Clone Main Repo
3645
uses: actions/checkout@v2
3746
with:
@@ -48,18 +57,22 @@ jobs:
4857
make -C main dev-env
4958
5059
- name: Build Docker Images
51-
run: make -C main build-test-all
60+
run: make -C main build-all-multi
5261
env:
5362
# Full logs for CI build
5463
BUILDKIT_PROGRESS: plain
5564

65+
- name: Test Docker Images
66+
run: make -C main test-all
67+
5668
- name: Clone Wiki
5769
uses: actions/checkout@v2
5870
with:
5971
repository: ${{github.repository}}.wiki
6072
path: wiki
6173

6274
- name: Run Post-Build Hooks
75+
id: hook-all
6376
run: make -C main hook-all
6477

6578
- name: Push Wiki to GitHub
@@ -78,4 +91,4 @@ jobs:
7891

7992
- name: Push Images to DockerHub
8093
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'
81-
run: make -C main push-all
94+
run: make -C main push-all-multi

Makefile

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@ SHELL:=bash
77
OWNER?=jupyter
88

99
# Need to list the images in build dependency order
10+
11+
# Images supporting the following architectures:
12+
# - linux/amd64
13+
# - linux/arm64
14+
MULTI_IMAGES:= \
15+
base-notebook \
16+
minimal-notebook
17+
# Images that can only be built on the amd64 architecture (aka. x86_64)
18+
AMD64_ONLY_IMAGES:= \
19+
r-notebook \
20+
scipy-notebook \
21+
tensorflow-notebook \
22+
datascience-notebook \
23+
pyspark-notebook \
24+
all-spark-notebook
25+
# All of the images
1026
ALL_IMAGES:= \
1127
base-notebook \
1228
minimal-notebook \
@@ -26,7 +42,7 @@ export DOCKER_BUILDKIT:=1
2642
help:
2743
@echo "jupyter/docker-stacks"
2844
@echo "====================="
29-
@echo "Replace % with a stack directory name (e.g., make build/minimal-notebook)"
45+
@echo "Replace % with a stack directory name (e.g., make build-multi/minimal-notebook)"
3046
@echo
3147
@grep -E '^[a-zA-Z0-9_%/-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
3248

@@ -40,7 +56,59 @@ build/%: ## build the latest image for a stack using the system's architecture
4056
@docker images $(OWNER)/$(notdir $@):latest --format "{{.Size}}"
4157
@echo "::endgroup::"
4258
build-all: $(foreach I, $(ALL_IMAGES), build/$(I)) ## build all stacks
43-
build-test-all: $(foreach I, $(ALL_IMAGES), build/$(I) test/$(I)) ## build and test all stacks
59+
60+
# Limitations on docker buildx build (using docker/buildx 0.5.1):
61+
#
62+
# 1. Can't --load and --push at the same time
63+
#
64+
# 2. Can't --load multiple platforms
65+
#
66+
# What does it mean to --load?
67+
#
68+
# - It means that the built image can be referenced by `docker` CLI, for example
69+
# when using the `docker tag` or `docker push` commands.
70+
#
71+
# Workarounds due to limitations:
72+
#
73+
# 1. We always build a dedicated image using the current system architecture
74+
# named as OWNER/<stack>-notebook so we always can reference that image no
75+
# matter what during tests etc.
76+
#
77+
# 2. We always also build a multi-platform image during build-multi that will be
78+
# inaccessible with `docker tag` and `docker push` etc, but this will help us
79+
# test the build on the different platform and provide cached layers for
80+
# later.
81+
#
82+
# 3. We let push-multi refer to rebuilding a multi image with `--push`.
83+
#
84+
# We can rely on the cached layer from build-multi now even though we never
85+
# tagged the multi image.
86+
#
87+
# Outcomes of the workaround:
88+
#
89+
# 1. We can keep using the previously defined Makefile commands that doesn't
90+
# include `-multi` suffix as before.
91+
#
92+
# 2. Assuming we have setup docker/dockerx properly to build in arm64
93+
# architectures as well, then we can build and publish such images via the
94+
# `-multi` suffix without needing a local registry.
95+
#
96+
# 3. If we get dedicated arm64 runners, we can test everything separately
97+
# without needing to update this Makefile, and if all tests succeeds we can
98+
# do a publish job that creates a multi-platform image for us.
99+
#
100+
build-multi/%: DARGS?=
101+
build-multi/%: ## build the latest image for a stack on both amd64 and arm64
102+
@echo "::group::Build $(OWNER)/$(notdir $@) (system's architecture)"
103+
docker buildx build $(DARGS) --rm --force-rm -t $(OWNER)/$(notdir $@):latest ./$(notdir $@) --build-arg OWNER=$(OWNER)
104+
@echo -n "Built image size: "
105+
@docker images $(OWNER)/$(notdir $@):latest --format "{{.Size}}"
106+
@echo "::endgroup::"
107+
108+
@echo "::group::Build $(OWNER)/$(notdir $@) (amd64,arm64)"
109+
docker buildx build $(DARGS) --rm --force-rm -t build-multi-tmp-cache/$(notdir $@):latest ./$(notdir $@) --build-arg OWNER=$(OWNER) --platform "linux/amd64,linux/arm64"
110+
@echo "::endgroup::"
111+
build-all-multi: $(foreach I, $(MULTI_IMAGES), build-multi/$(I)) $(foreach I, $(AMD64_ONLY_IMAGES), build/$(I)) ## build all stacks
44112

45113

46114

@@ -117,6 +185,13 @@ push/%: ## push all tags for a jupyter image
117185
@echo "::endgroup::"
118186
push-all: $(foreach I, $(ALL_IMAGES), push/$(I)) ## push all tagged images
119187

188+
push-multi/%: DARGS?=
189+
push-multi/%: ## push all tags for a jupyter image that support multiple architectures
190+
@echo "::group::Push $(OWNER)/$(notdir $@) (amd64,arm64)"
191+
docker buildx build $(DARGS) --rm --force-rm $($(subst -,_,$(notdir $@))_EXTRA_TAG_ARGS) -t $(OWNER)/$(notdir $@):latest ./$(notdir $@) --build-arg OWNER=$(OWNER) --platform "linux/amd64,linux/arm64"
192+
@echo "::endgroup::"
193+
push-all-multi: $(foreach I, $(MULTI_IMAGES), push-multi/$(I)) $(foreach I, $(AMD64_ONLY_IMAGES), push/$(I)) ## push all tagged images
194+
120195

121196

122197
run/%: DARGS?=
@@ -134,5 +209,4 @@ test/%: ## run tests against a stack (only common tests or common tests + specif
134209
@if [ ! -d "$(notdir $@)/test" ]; then TEST_IMAGE="$(OWNER)/$(notdir $@)" pytest -m "not info" test; \
135210
else TEST_IMAGE="$(OWNER)/$(notdir $@)" pytest -m "not info" test $(notdir $@)/test; fi
136211
@echo "::endgroup::"
137-
138212
test-all: $(foreach I, $(ALL_IMAGES), test/$(I)) ## test all stacks

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,18 @@ This change is tracked in the issue [#1217](https://github.com/jupyter/docker-st
105105
- [Jupyter Website](https://jupyter.org)
106106
- [Images on DockerHub](https://hub.docker.com/u/jupyter)
107107

108-
## Architectures
108+
## CPU Architectures
109109

110-
Currently published containers only support x86, some containers may support cross-building with docker buildx.
110+
All published containers support amd64 (x86_64). The base-notebook and
111+
minimal-notebook containers also support arm64. The ambition is to have all
112+
containers support both amd64 and arm64.
113+
114+
### Caveats for arm64 images
115+
116+
- The manifests we publish in this projects wiki as well as the image tags for
117+
the multi platform images that also support arm, are all based on the amd64
118+
version even though details about the installed packages versions could differ
119+
between architectures. For the status about this, see
120+
[#1401](https://github.com/jupyter/docker-stacks/issues/1401).
121+
- Only the amd64 images are actively tested currently. For the status about
122+
this, see [#1402](https://github.com/jupyter/docker-stacks/issues/1402).

docs/index.rst

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,23 @@ Docker destroys the container after notebook server exit, but any files written
4444

4545
docker run --rm -p 10000:8888 -e JUPYTER_ENABLE_LAB=yes -v "${PWD}":/home/jovyan/work jupyter/datascience-notebook:33add21fab64
4646

47-
Architectures
48-
-----------
49-
Currently published containers only support x86, some containers may support cross-building with docker buildx.
47+
CPU Architectures
48+
-----------------
49+
50+
All published containers support amd64 (x86_64). The base-notebook and
51+
minimal-notebook containers also support arm64. The ambition is to have all
52+
containers support both amd64 and arm64.
53+
54+
Caveats for arm64 images
55+
^^^^^^^^^^^^^^^^^^^^^^^^
5056

57+
- The manifests we publish in this projects wiki as well as the image tags for
58+
the multi platform images that also support arm, are all based on the amd64
59+
version even though details about the installed packages versions could differ
60+
between architectures. For the status about this, see
61+
[#1401](https://github.com/jupyter/docker-stacks/issues/1401).
62+
- Only the amd64 images are actively tested currently. For the status about
63+
this, see [#1402](https://github.com/jupyter/docker-stacks/issues/1402).
5164

5265
Table of Contents
5366
-----------------

tagging/github_set_env.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (c) Jupyter Development Team.
2+
# Distributed under the terms of the Modified BSD License.
3+
import os
4+
5+
6+
def github_set_env(env_name, env_value):
7+
if not os.environ.get("GITHUB_ACTIONS") or not os.environ.get("GITHUB_ENV"):
8+
return
9+
10+
with open(os.environ["GITHUB_ENV"], "a") as f:
11+
f.write(f"{env_name}={env_value}\n")

tagging/tag_image.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from plumbum.cmd import docker
77
from .docker_runner import DockerRunner
88
from .get_taggers_and_manifests import get_taggers_and_manifests
9+
from .github_set_env import github_set_env
910

1011

1112
logger = logging.getLogger(__name__)
@@ -15,21 +16,31 @@ def tag_image(short_image_name: str, owner: str) -> None:
1516
"""
1617
Tags <owner>/<short_image_name>:latest with the tags reported by all taggers
1718
for the given image.
19+
20+
Tags are in a GitHub Actions environment also saved to environment variables
21+
in a format making it easy to append them.
1822
"""
1923
logger.info(f"Tagging image: {short_image_name}")
2024
taggers, _ = get_taggers_and_manifests(short_image_name)
2125

2226
image = f"{owner}/{short_image_name}:latest"
2327

2428
with DockerRunner(image) as container:
29+
tags = []
2530
for tagger in taggers:
2631
tagger_name = tagger.__name__
2732
tag_value = tagger.tag_value(container)
33+
tags.append(tag_value)
2834
logger.info(
2935
f"Applying tag tagger_name: {tagger_name} tag_value: {tag_value}"
3036
)
3137
docker["tag", image, f"{owner}/{short_image_name}:{tag_value}"]()
3238

39+
if tags:
40+
env_name = f'{short_image_name.replace("-", "_")}_EXTRA_TAG_ARGS'
41+
docker_build_tag_args = "-t " + " -t ".join(tags)
42+
github_set_env(env_name, docker_build_tag_args)
43+
3344

3445
if __name__ == "__main__":
3546
logging.basicConfig(level=logging.INFO)

0 commit comments

Comments
 (0)