From a309eb168781106bf1238e8f791561941870c688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MI=E2=84=82H=CE=9B=CE=9EL=20F=D1=B2R=D0=98=CE=9BR=D1=B2?= <20387402+xUnholy@users.noreply.github.com> Date: Mon, 5 Oct 2020 11:26:46 +1100 Subject: [PATCH] Initial multi-arch image commit (#86) Adding multi-arch image support for `arm64` and `amd64`. This uses dockers new `buildx` feature, to enable further architectures more work will be required to update the `runner/Dockerfile` file to pull architecture-specific releases. The Makefile targets really should only be used for local testing and not for release, additional work to appropriately tag the release images may need to be added but for now, I've not added that logic. Fixes: #86 Signed-off-by: Michael Fornaro <20387402+xUnholy@users.noreply.github.com> --- .github/workflows/build-runner.yml | 36 ++++++++++----- .github/workflows/build.yml | 51 ++++++++++++-------- .github/workflows/release.yml | 64 ++++++++++++++++---------- Dockerfile | 23 +++++++--- Makefile | 29 ++++++++++++ runner/Dockerfile | 74 +++++++++++++++++------------- runner/Makefile | 29 ++++++++++++ 7 files changed, 213 insertions(+), 93 deletions(-) diff --git a/.github/workflows/build-runner.yml b/.github/workflows/build-runner.yml index 8097e16e..cfb989f7 100644 --- a/.github/workflows/build-runner.yml +++ b/.github/workflows/build-runner.yml @@ -1,22 +1,34 @@ on: push: branches: - - master + - master paths: - - 'runner/**' + - 'runner/**' jobs: build: runs-on: ubuntu-latest name: Build runner steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Build container image - run: make docker-build - working-directory: runner - - name: Docker Login - run: docker login -u summerwind -p ${{ secrets.DOCKER_ACCESS_TOKEN }} - - name: Push container image - run: make docker-push - working-directory: runner + - name: Checkout + uses: actions/checkout@v2 + + - name: Set up Docker Buildx + id: buildx + uses: crazy-max/ghaction-docker-buildx@v1 + with: + buildx-version: latest + + - name: Login to GitHub Docker Registry + run: echo "${DOCKERHUB_PASSWORD}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin + env: + DOCKERHUB_USERNAME: summerwind + DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + - name: Build Container Image + working-directory: runner + run: | + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag summerwind/actions-runner:latest \ + -f Dockerfile . --push diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 96b3706e..f4dc30fc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,28 +3,43 @@ name: Build on: push: branches: - - master + - master paths-ignore: - - 'runner/**' - - '.github/**' + - 'runner/**' + - '.github/**' jobs: build: runs-on: ubuntu-latest name: Build steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Install kubebuilder - run: | - curl -L -O https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.2.0/kubebuilder_2.2.0_linux_amd64.tar.gz - tar zxvf kubebuilder_2.2.0_linux_amd64.tar.gz - sudo mv kubebuilder_2.2.0_linux_amd64 /usr/local/kubebuilder - - name: Run tests - run: make test - - name: Build container image - run: make docker-build - - name: Docker Login - run: docker login -u summerwind -p ${{ secrets.DOCKER_ACCESS_TOKEN }} - - name: Push container image - run: make docker-push + - name: Checkout + uses: actions/checkout@v2 + + - name: Install kubebuilder + run: | + curl -L -O https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.2.0/kubebuilder_2.2.0_linux_amd64.tar.gz + tar zxvf kubebuilder_2.2.0_linux_amd64.tar.gz + sudo mv kubebuilder_2.2.0_linux_amd64 /usr/local/kubebuilder + + - name: Run tests + run: make test + + - name: Set up Docker Buildx + id: buildx + uses: crazy-max/ghaction-docker-buildx@v1 + with: + buildx-version: latest + + - name: Login to GitHub Docker Registry + run: echo "${DOCKERHUB_PASSWORD}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin + env: + DOCKERHUB_USERNAME: summerwind + DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + - name: Build Container Image + run: | + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag summerwind/actions-runner-controller:latest \ + -f Dockerfile . --push diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a5a4aa3a..413b3e9b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,27 +7,43 @@ jobs: runs-on: ubuntu-latest name: Release steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Install tools - run: | - curl -L -O https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.2.0/kubebuilder_2.2.0_linux_amd64.tar.gz - tar zxvf kubebuilder_2.2.0_linux_amd64.tar.gz - sudo mv kubebuilder_2.2.0_linux_amd64 /usr/local/kubebuilder - curl -s https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh | bash - sudo mv kustomize /usr/local/bin - curl -L -O https://github.com/tcnksm/ghr/releases/download/v0.13.0/ghr_v0.13.0_linux_amd64.tar.gz - tar zxvf ghr_v0.13.0_linux_amd64.tar.gz - sudo mv ghr_v0.13.0_linux_amd64/ghr /usr/local/bin - - name: Set version - run: echo "::set-env name=VERSION::$(cat ${GITHUB_EVENT_PATH} | jq -r '.release.tag_name')" - - name: Upload artifacts - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: make github-release - - name: Build container image - run: make docker-build - - name: Docker Login - run: docker login -u summerwind -p ${{ secrets.DOCKER_ACCESS_TOKEN }} - - name: Push container image - run: make docker-push + - name: Checkout + uses: actions/checkout@v2 + + - name: Install tools + run: | + curl -L -O https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.2.0/kubebuilder_2.2.0_linux_amd64.tar.gz + tar zxvf kubebuilder_2.2.0_linux_amd64.tar.gz + sudo mv kubebuilder_2.2.0_linux_amd64 /usr/local/kubebuilder + curl -s https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh | bash + sudo mv kustomize /usr/local/bin + curl -L -O https://github.com/tcnksm/ghr/releases/download/v0.13.0/ghr_v0.13.0_linux_amd64.tar.gz + tar zxvf ghr_v0.13.0_linux_amd64.tar.gz + sudo mv ghr_v0.13.0_linux_amd64/ghr /usr/local/bin + + - name: Set version + run: echo "::set-env name=VERSION::$(cat ${GITHUB_EVENT_PATH} | jq -r '.release.tag_name')" + + - name: Upload artifacts + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: make github-release + + - name: Set up Docker Buildx + id: buildx + uses: crazy-max/ghaction-docker-buildx@v1 + with: + buildx-version: latest + + - name: Login to GitHub Docker Registry + run: echo "${DOCKERHUB_PASSWORD}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin + env: + DOCKERHUB_USERNAME: summerwind + DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + - name: Build Container Image + run: | + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag summerwind/actions-runner-controller:${{ env.VERSION }} \ + -f Dockerfile . --push diff --git a/Dockerfile b/Dockerfile index 6dfb4f6d..be348e2a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,28 +1,37 @@ # Build the manager binary FROM golang:1.13 as builder +ARG TARGETPLATFORM + WORKDIR /workspace + +ENV GO111MODULE=on \ + CGO_ENABLED=0 + # Copy the Go Modules manifests -COPY go.mod go.mod -COPY go.sum go.sum +COPY go.mod go.sum ./ + # cache deps before building and copying source so that we don't need to re-download as much # and so that source changes don't invalidate our downloaded layer RUN go mod download # Copy the go source -COPY main.go main.go -COPY api/ api/ -COPY controllers/ controllers/ -COPY github/ github/ +COPY . . # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go +RUN export GOOS=$(echo ${TARGETPLATFORM} | cut -d / -f1) && \ + export GOARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) && \ + GOARM=$(echo ${TARGETPLATFORM} | cut -d / -f3 | cut -c2-) && \ + go build -a -o manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM gcr.io/distroless/static:nonroot + WORKDIR / + COPY --from=builder /workspace/manager . + USER nonroot:nonroot ENTRYPOINT ["/manager"] diff --git a/Makefile b/Makefile index 0f216230..21273fdc 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,23 @@ else GOBIN=$(shell go env GOBIN) endif +# default list of platforms for which multiarch image is built +ifeq (${PLATFORMS}, ) + export PLATFORMS="linux/amd64,linux/arm64" +endif + +# if IMG_RESULT is unspecified, by default the image will be pushed to registry +ifeq (${IMG_RESULT}, load) + export PUSH_ARG="--load" + # if load is specified, image will be built only for the build machine architecture. + export PLATFORMS="local" +else ifeq (${IMG_RESULT}, cache) + # if cache is specified, image will only be available in the build cache, it won't be pushed or loaded + # therefore no PUSH_ARG will be specified +else + export PUSH_ARG="--push" +endif + all: manager # Run tests @@ -62,6 +79,18 @@ docker-build: test docker-push: docker push ${NAME}:${VERSION} +docker-buildx: + export DOCKER_CLI_EXPERIMENTAL=enabled + @if ! docker buildx ls | grep -q container-builder; then\ + docker buildx create --platform ${PLATFORMS} --name container-builder --use;\ + fi + docker buildx build --platform ${PLATFORMS} \ + --build-arg RUNNER_VERSION=${RUNNER_VERSION} \ + --build-arg DOCKER_VERSION=${DOCKER_VERSION} \ + -t "${NAME}:${VERSION}" \ + -f Dockerfile \ + . ${PUSH_ARG} + # Generate the release manifest file release: manifests cd config/manager && kustomize edit set image controller=${NAME}:${VERSION} diff --git a/runner/Dockerfile b/runner/Dockerfile index 632545f3..a87f4a3a 100644 --- a/runner/Dockerfile +++ b/runner/Dockerfile @@ -1,7 +1,8 @@ FROM ubuntu:18.04 -ARG RUNNER_VERSION -ARG DOCKER_VERSION +ARG TARGETPLATFORM +ARG RUNNER_VERSION=2.272.0 +ARG DOCKER_VERSION=19.03.12 ENV DEBIAN_FRONTEND=noninteractive RUN apt update -y \ @@ -9,46 +10,55 @@ RUN apt update -y \ && add-apt-repository -y ppa:git-core/ppa \ && apt update -y \ && apt install -y --no-install-recommends \ - build-essential \ - curl \ - ca-certificates \ - dnsutils \ - ftp \ - git \ - iproute2 \ - iputils-ping \ - jq \ - libunwind8 \ - locales \ - netcat \ - openssh-client \ - parallel \ - rsync \ - shellcheck \ - sudo \ - telnet \ - time \ - tzdata \ - unzip \ - upx \ - wget \ - zip \ - zstd \ + build-essential \ + curl \ + ca-certificates \ + dnsutils \ + ftp \ + git \ + iproute2 \ + iputils-ping \ + jq \ + libunwind8 \ + locales \ + netcat \ + openssh-client \ + parallel \ + rsync \ + shellcheck \ + sudo \ + telnet \ + time \ + tzdata \ + unzip \ + upx \ + wget \ + zip \ + zstd \ && rm -rf /var/lib/apt/lists/* -RUN curl -L -o docker.tgz https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz \ +RUN export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \ + && curl -L -o /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_${ARCH} \ + && chmod +x /usr/local/bin/dumb-init + +# Docker download supports arm64 as aarch64 & amd64 as x86_64 +RUN export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \ + && if [ "$ARCH" = "arm64" ]; then export ARCH=aarch64 ; fi \ + && if [ "$ARCH" = "amd64" ]; then export ARCH=x86_64 ; fi \ + && curl -L -o docker.tgz https://download.docker.com/linux/static/stable/${ARCH}/docker-${DOCKER_VERSION}.tgz \ && tar zxvf docker.tgz \ && install -o root -g root -m 755 docker/docker /usr/local/bin/docker \ && rm -rf docker docker.tgz \ - && curl -L -o /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64 \ - && chmod +x /usr/local/bin/dumb-init \ && adduser --disabled-password --gecos "" --uid 1000 runner \ && usermod -aG sudo runner \ && echo "%sudo ALL=(ALL:ALL) NOPASSWD:ALL" > /etc/sudoers -RUN mkdir -p /runner \ +# Runner download supports amd64 as x64 +RUN export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \ + && if [ "$ARCH" = "amd64" ]; then export ARCH=x64 ; fi \ + && mkdir -p /runner \ && cd /runner \ - && curl -L -o runner.tar.gz https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz \ + && curl -L -o runner.tar.gz https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-${ARCH}-${RUNNER_VERSION}.tar.gz \ && tar xzf ./runner.tar.gz \ && rm runner.tar.gz \ && ./bin/installdependencies.sh \ diff --git a/runner/Makefile b/runner/Makefile index fb598155..9fa50f3c 100644 --- a/runner/Makefile +++ b/runner/Makefile @@ -4,9 +4,38 @@ TAG ?= latest RUNNER_VERSION ?= 2.273.4 DOCKER_VERSION ?= 19.03.12 +# default list of platforms for which multiarch image is built +ifeq (${PLATFORMS}, ) + export PLATFORMS="linux/amd64,linux/arm64" +endif + +# if IMG_RESULT is unspecified, by default the image will be pushed to registry +ifeq (${IMG_RESULT}, load) + export PUSH_ARG="--load" + # if load is specified, image will be built only for the build machine architecture. + export PLATFORMS="local" +else ifeq (${IMG_RESULT}, cache) + # if cache is specified, image will only be available in the build cache, it won't be pushed or loaded + # therefore no PUSH_ARG will be specified +else + export PUSH_ARG="--push" +endif + docker-build: docker build --build-arg RUNNER_VERSION=${RUNNER_VERSION} --build-arg DOCKER_VERSION=${DOCKER_VERSION} -t ${NAME}:${TAG} -t ${NAME}:v${RUNNER_VERSION} . docker-push: docker push ${NAME}:${TAG} docker push ${NAME}:v${RUNNER_VERSION} + +docker-buildx: + export DOCKER_CLI_EXPERIMENTAL=enabled + @if ! docker buildx ls | grep -q container-builder; then\ + docker buildx create --platform ${PLATFORMS} --name container-builder --use;\ + fi + docker buildx build --platform ${PLATFORMS} \ + --build-arg RUNNER_VERSION=${RUNNER_VERSION} \ + --build-arg DOCKER_VERSION=${DOCKER_VERSION} \ + -t "${NAME}:latest" \ + -f Dockerfile \ + . ${PUSH_ARG}