Merge pull request #1399 from mathbunnyru/asalikhov/arm_builds

Support arm builds
This commit is contained in:
Erik Sundell
2021-07-16 23:02:00 +02:00
committed by GitHub
6 changed files with 144 additions and 10 deletions

View File

@@ -32,6 +32,15 @@ jobs:
!contains(github.event.pull_request.title, 'ci skip') !contains(github.event.pull_request.title, 'ci skip')
steps: steps:
# Setup docker to build for multiple platforms, see:
# https://github.com/docker/build-push-action/tree/master#usage
# https://github.com/docker/build-push-action/blob/master/docs/advanced/multi-platform.md
- name: Set up QEMU (for docker buildx)
uses: docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # dependabot updates to latest release
- name: Set up Docker Buildx (for multi-arch builds)
uses: docker/setup-buildx-action@0d135e0c2fc0dba0729c1a47ecfcf5a3c7f8579e # dependabot updates to latest release
- name: Clone Main Repo - name: Clone Main Repo
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
@@ -48,11 +57,14 @@ jobs:
make -C main dev-env make -C main dev-env
- name: Build Docker Images - name: Build Docker Images
run: make -C main build-test-all run: make -C main build-all-multi
env: env:
# Full logs for CI build # Full logs for CI build
BUILDKIT_PROGRESS: plain BUILDKIT_PROGRESS: plain
- name: Test Docker Images
run: make -C main test-all
- name: Clone Wiki - name: Clone Wiki
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
@@ -60,6 +72,7 @@ jobs:
path: wiki path: wiki
- name: Run Post-Build Hooks - name: Run Post-Build Hooks
id: hook-all
run: make -C main hook-all run: make -C main hook-all
- name: Push Wiki to GitHub - name: Push Wiki to GitHub
@@ -78,4 +91,4 @@ jobs:
- name: Push Images to DockerHub - name: Push Images to DockerHub
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'
run: make -C main push-all run: make -C main push-all-multi

View File

@@ -7,6 +7,22 @@ SHELL:=bash
OWNER?=jupyter OWNER?=jupyter
# Need to list the images in build dependency order # Need to list the images in build dependency order
# Images supporting the following architectures:
# - linux/amd64
# - linux/arm64
MULTI_IMAGES:= \
base-notebook \
minimal-notebook
# Images that can only be built on the amd64 architecture (aka. x86_64)
AMD64_ONLY_IMAGES:= \
r-notebook \
scipy-notebook \
tensorflow-notebook \
datascience-notebook \
pyspark-notebook \
all-spark-notebook
# All of the images
ALL_IMAGES:= \ ALL_IMAGES:= \
base-notebook \ base-notebook \
minimal-notebook \ minimal-notebook \
@@ -26,7 +42,7 @@ export DOCKER_BUILDKIT:=1
help: help:
@echo "jupyter/docker-stacks" @echo "jupyter/docker-stacks"
@echo "=====================" @echo "====================="
@echo "Replace % with a stack directory name (e.g., make build/minimal-notebook)" @echo "Replace % with a stack directory name (e.g., make build-multi/minimal-notebook)"
@echo @echo
@grep -E '^[a-zA-Z0-9_%/-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @grep -E '^[a-zA-Z0-9_%/-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
@@ -40,7 +56,59 @@ build/%: ## build the latest image for a stack using the system's architecture
@docker images $(OWNER)/$(notdir $@):latest --format "{{.Size}}" @docker images $(OWNER)/$(notdir $@):latest --format "{{.Size}}"
@echo "::endgroup::" @echo "::endgroup::"
build-all: $(foreach I, $(ALL_IMAGES), build/$(I)) ## build all stacks build-all: $(foreach I, $(ALL_IMAGES), build/$(I)) ## build all stacks
build-test-all: $(foreach I, $(ALL_IMAGES), build/$(I) test/$(I)) ## build and test all stacks
# Limitations on docker buildx build (using docker/buildx 0.5.1):
#
# 1. Can't --load and --push at the same time
#
# 2. Can't --load multiple platforms
#
# What does it mean to --load?
#
# - It means that the built image can be referenced by `docker` CLI, for example
# when using the `docker tag` or `docker push` commands.
#
# Workarounds due to limitations:
#
# 1. We always build a dedicated image using the current system architecture
# named as OWNER/<stack>-notebook so we always can reference that image no
# matter what during tests etc.
#
# 2. We always also build a multi-platform image during build-multi that will be
# inaccessible with `docker tag` and `docker push` etc, but this will help us
# test the build on the different platform and provide cached layers for
# later.
#
# 3. We let push-multi refer to rebuilding a multi image with `--push`.
#
# We can rely on the cached layer from build-multi now even though we never
# tagged the multi image.
#
# Outcomes of the workaround:
#
# 1. We can keep using the previously defined Makefile commands that doesn't
# include `-multi` suffix as before.
#
# 2. Assuming we have setup docker/dockerx properly to build in arm64
# architectures as well, then we can build and publish such images via the
# `-multi` suffix without needing a local registry.
#
# 3. If we get dedicated arm64 runners, we can test everything separately
# without needing to update this Makefile, and if all tests succeeds we can
# do a publish job that creates a multi-platform image for us.
#
build-multi/%: DARGS?=
build-multi/%: ## build the latest image for a stack on both amd64 and arm64
@echo "::group::Build $(OWNER)/$(notdir $@) (system's architecture)"
docker buildx build $(DARGS) --rm --force-rm -t $(OWNER)/$(notdir $@):latest ./$(notdir $@) --build-arg OWNER=$(OWNER)
@echo -n "Built image size: "
@docker images $(OWNER)/$(notdir $@):latest --format "{{.Size}}"
@echo "::endgroup::"
@echo "::group::Build $(OWNER)/$(notdir $@) (amd64,arm64)"
docker buildx build $(DARGS) --rm --force-rm -t build-multi-tmp-cache/$(notdir $@):latest ./$(notdir $@) --build-arg OWNER=$(OWNER) --platform "linux/amd64,linux/arm64"
@echo "::endgroup::"
build-all-multi: $(foreach I, $(MULTI_IMAGES), build-multi/$(I)) $(foreach I, $(AMD64_ONLY_IMAGES), build/$(I)) ## build all stacks
@@ -117,6 +185,13 @@ push/%: ## push all tags for a jupyter image
@echo "::endgroup::" @echo "::endgroup::"
push-all: $(foreach I, $(ALL_IMAGES), push/$(I)) ## push all tagged images push-all: $(foreach I, $(ALL_IMAGES), push/$(I)) ## push all tagged images
push-multi/%: DARGS?=
push-multi/%: ## push all tags for a jupyter image that support multiple architectures
@echo "::group::Push $(OWNER)/$(notdir $@) (amd64,arm64)"
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"
@echo "::endgroup::"
push-all-multi: $(foreach I, $(MULTI_IMAGES), push-multi/$(I)) $(foreach I, $(AMD64_ONLY_IMAGES), push/$(I)) ## push all tagged images
run/%: DARGS?= run/%: DARGS?=
@@ -134,5 +209,4 @@ test/%: ## run tests against a stack (only common tests or common tests + specif
@if [ ! -d "$(notdir $@)/test" ]; then TEST_IMAGE="$(OWNER)/$(notdir $@)" pytest -m "not info" test; \ @if [ ! -d "$(notdir $@)/test" ]; then TEST_IMAGE="$(OWNER)/$(notdir $@)" pytest -m "not info" test; \
else TEST_IMAGE="$(OWNER)/$(notdir $@)" pytest -m "not info" test $(notdir $@)/test; fi else TEST_IMAGE="$(OWNER)/$(notdir $@)" pytest -m "not info" test $(notdir $@)/test; fi
@echo "::endgroup::" @echo "::endgroup::"
test-all: $(foreach I, $(ALL_IMAGES), test/$(I)) ## test all stacks test-all: $(foreach I, $(ALL_IMAGES), test/$(I)) ## test all stacks

View File

@@ -105,6 +105,18 @@ This change is tracked in the issue [#1217](https://github.com/jupyter/docker-st
- [Jupyter Website](https://jupyter.org) - [Jupyter Website](https://jupyter.org)
- [Images on DockerHub](https://hub.docker.com/u/jupyter) - [Images on DockerHub](https://hub.docker.com/u/jupyter)
## Architectures ## CPU Architectures
Currently published containers only support x86, some containers may support cross-building with docker buildx. All published containers support amd64 (x86_64). The base-notebook and
minimal-notebook containers also support arm64. The ambition is to have all
containers support both amd64 and arm64.
### Caveats for arm64 images
- The manifests we publish in this projects wiki as well as the image tags for
the multi platform images that also support arm, are all based on the amd64
version even though details about the installed packages versions could differ
between architectures. For the status about this, see
[#1401](https://github.com/jupyter/docker-stacks/issues/1401).
- Only the amd64 images are actively tested currently. For the status about
this, see [#1402](https://github.com/jupyter/docker-stacks/issues/1402).

View File

@@ -44,10 +44,23 @@ Docker destroys the container after notebook server exit, but any files written
docker run --rm -p 10000:8888 -e JUPYTER_ENABLE_LAB=yes -v "${PWD}":/home/jovyan/work jupyter/datascience-notebook:33add21fab64 docker run --rm -p 10000:8888 -e JUPYTER_ENABLE_LAB=yes -v "${PWD}":/home/jovyan/work jupyter/datascience-notebook:33add21fab64
Architectures CPU Architectures
----------- -----------------
Currently published containers only support x86, some containers may support cross-building with docker buildx.
All published containers support amd64 (x86_64). The base-notebook and
minimal-notebook containers also support arm64. The ambition is to have all
containers support both amd64 and arm64.
Caveats for arm64 images
^^^^^^^^^^^^^^^^^^^^^^^^
- The manifests we publish in this projects wiki as well as the image tags for
the multi platform images that also support arm, are all based on the amd64
version even though details about the installed packages versions could differ
between architectures. For the status about this, see
[#1401](https://github.com/jupyter/docker-stacks/issues/1401).
- Only the amd64 images are actively tested currently. For the status about
this, see [#1402](https://github.com/jupyter/docker-stacks/issues/1402).
Table of Contents Table of Contents
----------------- -----------------

11
tagging/github_set_env.py Normal file
View File

@@ -0,0 +1,11 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
def github_set_env(env_name, env_value):
if not os.environ.get("GITHUB_ACTIONS") or not os.environ.get("GITHUB_ENV"):
return
with open(os.environ["GITHUB_ENV"], "a") as f:
f.write(f"{env_name}={env_value}\n")

View File

@@ -6,6 +6,7 @@ import logging
from plumbum.cmd import docker from plumbum.cmd import docker
from .docker_runner import DockerRunner from .docker_runner import DockerRunner
from .get_taggers_and_manifests import get_taggers_and_manifests from .get_taggers_and_manifests import get_taggers_and_manifests
from .github_set_env import github_set_env
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -15,6 +16,9 @@ def tag_image(short_image_name: str, owner: str) -> None:
""" """
Tags <owner>/<short_image_name>:latest with the tags reported by all taggers Tags <owner>/<short_image_name>:latest with the tags reported by all taggers
for the given image. for the given image.
Tags are in a GitHub Actions environment also saved to environment variables
in a format making it easy to append them.
""" """
logger.info(f"Tagging image: {short_image_name}") logger.info(f"Tagging image: {short_image_name}")
taggers, _ = get_taggers_and_manifests(short_image_name) taggers, _ = get_taggers_and_manifests(short_image_name)
@@ -22,14 +26,21 @@ def tag_image(short_image_name: str, owner: str) -> None:
image = f"{owner}/{short_image_name}:latest" image = f"{owner}/{short_image_name}:latest"
with DockerRunner(image) as container: with DockerRunner(image) as container:
tags = []
for tagger in taggers: for tagger in taggers:
tagger_name = tagger.__name__ tagger_name = tagger.__name__
tag_value = tagger.tag_value(container) tag_value = tagger.tag_value(container)
tags.append(tag_value)
logger.info( logger.info(
f"Applying tag tagger_name: {tagger_name} tag_value: {tag_value}" f"Applying tag tagger_name: {tagger_name} tag_value: {tag_value}"
) )
docker["tag", image, f"{owner}/{short_image_name}:{tag_value}"]() docker["tag", image, f"{owner}/{short_image_name}:{tag_value}"]()
if tags:
env_name = f'{short_image_name.replace("-", "_")}_EXTRA_TAG_ARGS'
docker_build_tag_args = "-t " + " -t ".join(tags)
github_set_env(env_name, docker_build_tag_args)
if __name__ == "__main__": if __name__ == "__main__":
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)