From 44887b9f8f7490835fdee9a48accc73fa64b5b32 Mon Sep 17 00:00:00 2001 From: Dave Henderson Date: Thu, 9 May 2019 19:09:10 -0400 Subject: [PATCH] Produce multi-platform Docker images Signed-off-by: Dave Henderson --- .github/workflows/docker.yml | 101 +++++++++++++++++++++++++++++++++-- Dockerfile | 96 +++++++++++++++++++++++---------- Makefile | 47 +++++++++++++--- 3 files changed, 207 insertions(+), 37 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 6bcbb723..10c9f9fd 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -4,9 +4,104 @@ on: [push] jobs: docker-build: runs-on: ubuntu-latest + services: + registry: + image: registry:2 + ports: + - '5000:5000' + env: + DOCKER_BUILDKIT: 1 + DOCKER_CLI_EXPERIMENTAL: enabled steps: + - name: enable experimental mode + run: | + mkdir ~/.docker + echo '{"experimental": "enabled"}' > ~/.docker/config.json + - name: Install BuildX + run: | + mkdir -p ~/.docker/cli-plugins + curl -SL -o ~/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/v0.3.1/buildx-v0.3.1.linux-amd64 + chmod 755 ~/.docker/cli-plugins/docker-buildx + - name: Create BuildX Builder + run: | + docker buildx create \ + --name builder \ + --platform linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le,linux/s390x,linux/386 \ + --use \ + --driver docker-container --driver-opt image=moby/buildkit:buildx-stable-1,network=host + docker buildx ls + docker ps + docker images + - run: docker info && docker version - uses: actions/checkout@v1 - - name: Make Docker images - run: make docker-images + - name: determine if this is a tag + run: | + if (git describe --abbrev=0 --exact-match &>/dev/null); then + echo "::set-env name=is_tag::true" + echo "::set-env name=git_tag::$(git describe --abbrev=0 --exact-match)" + # splits the major version from $tag - assumes it's a 3-part semver + echo "::set-env name=major_version::${tag%%\.*}" + fi + if: github.repository == 'hairyhenderson/gomplate' + - name: login + run: | + # NOTE: DOCKERHUB_TOKEN and DOCKERHUB_USERNAME must be present in https://github.com/hairyhenderson/gomplate/settings + echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login --username ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin + - name: Make/Push temporary Docker images + run: make docker-multi COMMIT=${{ github.sha }} DOCKER_REPO=localhost:5000/gomplate env: - DOCKER_BUILDKIT: '1' + COMMIT: ${{ github.sha }} + - name: Pull the temporary images + run: | + docker pull localhost:5000/gomplate:latest-${{ github.sha }} + docker pull localhost:5000/gomplate:slim-${{ github.sha }} + docker pull localhost:5000/gomplate:alpine-${{ github.sha }} + - name: Re-tag and push (master) + run: | + docker tag localhost:5000/gomplate:latest-${{ github.sha }} hairyhenderson/gomplate:latest + docker tag localhost:5000/gomplate:slim-${{ github.sha }} hairyhenderson/gomplate:slim + docker tag localhost:5000/gomplate:alpine-${{ github.sha }} hairyhenderson/gomplate:alpine + + docker rmi localhost:5000/gomplate:latest-${{ github.sha }} + docker rmi localhost:5000/gomplate:slim-${{ github.sha }} + docker rmi localhost:5000/gomplate:alpine-${{ github.sha }} + + docker images + # docker push hairyhenderson/gomplate + if: github.repository == 'hairyhenderson/gomplate' && github.ref == 'refs/heads/master' + - name: Re-tag and push (tagged release) + run: | + docker tag localhost:5000/gomplate:latest-${{ github.sha }} hairyhenderson/gomplate:latest + docker tag localhost:5000/gomplate:slim-${{ github.sha }} hairyhenderson/gomplate:slim + docker tag localhost:5000/gomplate:alpine-${{ github.sha }} hairyhenderson/gomplate:alpine + + docker tag localhost:5000/gomplate:latest-${{ github.sha }} hairyhenderson/gomplate:${git_tag} + docker tag localhost:5000/gomplate:latest-${{ github.sha }} hairyhenderson/gomplate:stable + docker tag localhost:5000/gomplate:latest-${{ github.sha }} hairyhenderson/gomplate:${major_version} + docker tag localhost:5000/gomplate:slim-${{ github.sha }} hairyhenderson/gomplate:${git_tag}-slim + docker tag localhost:5000/gomplate:slim-${{ github.sha }} hairyhenderson/gomplate:${major_version}-slim + docker tag localhost:5000/gomplate:slim-${{ github.sha }} hairyhenderson/gomplate:stable-slim + docker tag localhost:5000/gomplate:alpine-${{ github.sha }} hairyhenderson/gomplate:${git_tag}-alpine + docker tag localhost:5000/gomplate:alpine-${{ github.sha }} hairyhenderson/gomplate:${major_version}-alpine + docker tag localhost:5000/gomplate:alpine-${{ github.sha }} hairyhenderson/gomplate:stable-alpine + + docker rmi localhost:5000/gomplate:latest-${{ github.sha }} + docker rmi localhost:5000/gomplate:slim-${{ github.sha }} + docker rmi localhost:5000/gomplate:alpine-${{ github.sha }} + + docker images + # docker push hairyhenderson/gomplate + if: github.repository == 'hairyhenderson/gomplate' && contains(github.ref, 'v*') + - name: Re-tag and push (non-master dry run) + run: | + docker tag localhost:5000/gomplate:latest-${{ github.sha }} hairyhenderson/gomplate:latest + docker tag localhost:5000/gomplate:slim-${{ github.sha }} hairyhenderson/gomplate:slim + docker tag localhost:5000/gomplate:alpine-${{ github.sha }} hairyhenderson/gomplate:alpine + + docker rmi localhost:5000/gomplate:latest-${{ github.sha }} + docker rmi localhost:5000/gomplate:slim-${{ github.sha }} + docker rmi localhost:5000/gomplate:alpine-${{ github.sha }} + + docker images + # docker push hairyhenderson/gomplate + if: github.repository != 'hairyhenderson/gomplate' || github.ref != 'refs/heads/master' diff --git a/Dockerfile b/Dockerfile index 18d15042..4a319aaa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,65 +1,107 @@ -FROM hairyhenderson/upx:3.94 AS upx +# syntax=docker/dockerfile:1.1.5-experimental +FROM --platform=linux/amd64 hairyhenderson/upx:3.94 AS upx -FROM golang:1.14.2-alpine3.11 AS build +FROM --platform=linux/amd64 golang:1.14.2-alpine3.11 AS build -RUN apk add --no-cache \ - make \ - libgcc libstdc++ ucl \ - git +ARG TARGETOS +ARG TARGETARCH +ARG TARGETVARIANT +ENV GOOS=$TARGETOS GOARCH=$TARGETARCH -COPY --from=upx /usr/bin/upx /usr/bin/upx +RUN apk add --no-cache make git -RUN mkdir -p /go/src/github.com/hairyhenderson/gomplate WORKDIR /go/src/github.com/hairyhenderson/gomplate +COPY go.mod /go/src/github.com/hairyhenderson/gomplate +COPY go.sum /go/src/github.com/hairyhenderson/gomplate + +RUN --mount=type=cache,id=go-build-${TARGETOS}-${TARGETARCH}${TARGETVARIANT},target=/root/.cache/go-build \ + --mount=type=cache,id=go-pkg-${TARGETOS}-${TARGETARCH}${TARGETVARIANT},target=/go/pkg \ + go mod download -x + COPY . /go/src/github.com/hairyhenderson/gomplate -RUN make build compress +RUN --mount=type=cache,id=go-build-${TARGETOS}-${TARGETARCH}${TARGETVARIANT},target=/root/.cache/go-build \ + --mount=type=cache,id=go-pkg-${TARGETOS}-${TARGETARCH}${TARGETVARIANT},target=/go/pkg \ + make build +RUN mv bin/gomplate* /bin/ -FROM scratch AS artifacts +FROM --platform=linux/amd64 alpine:3.11.5 AS compress -COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt -COPY --from=build /go/src/github.com/hairyhenderson/gomplate/bin/* /bin/ +ARG TARGETOS +ARG TARGETARCH +ARG TARGETVARIANT -CMD [ "/bin/gomplate_linux-amd64" ] +RUN apk add --no-cache \ + make \ + libgcc libstdc++ ucl -FROM scratch AS gomplate +ENV GOOS=$TARGETOS GOARCH=$TARGETARCH +WORKDIR /go/src/github.com/hairyhenderson/gomplate +COPY Makefile . +RUN mkdir bin + +COPY --from=upx /usr/bin/upx /usr/bin/upx +COPY --from=build bin/* bin/ + +RUN make compress +RUN mv bin/gomplate* /bin/ + +FROM scratch AS gomplate-linux ARG VCS_REF -ARG OS=linux -ARG ARCH=amd64 +ARG TARGETOS +ARG TARGETARCH +ARG TARGETVARIANT LABEL org.opencontainers.image.revision=$VCS_REF \ org.opencontainers.image.source="https://github.com/hairyhenderson/gomplate" -COPY --from=artifacts /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt -COPY --from=artifacts /bin/gomplate_${OS}-${ARCH} /gomplate +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=build /bin/gomplate_${TARGETOS}-${TARGETARCH}${TARGETVARIANT} /gomplate ENTRYPOINT [ "/gomplate" ] FROM alpine:3.11.5 AS gomplate-alpine ARG VCS_REF -ARG OS=linux -ARG ARCH=amd64 +ARG TARGETOS +ARG TARGETARCH +ARG TARGETVARIANT LABEL org.opencontainers.image.revision=$VCS_REF \ org.opencontainers.image.source="https://github.com/hairyhenderson/gomplate" -RUN apk add --no-cache ca-certificates -COPY --from=artifacts /bin/gomplate_${OS}-${ARCH}-slim /bin/gomplate +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=compress /bin/gomplate_${TARGETOS}-${TARGETARCH}${TARGETVARIANT}-slim /gomplate ENTRYPOINT [ "/bin/gomplate" ] -FROM scratch AS gomplate-slim +FROM scratch AS gomplate-slim-linux ARG VCS_REF -ARG OS=linux -ARG ARCH=amd64 +ARG TARGETOS +ARG TARGETARCH +ARG TARGETVARIANT LABEL org.opencontainers.image.revision=$VCS_REF \ org.opencontainers.image.source="https://github.com/hairyhenderson/gomplate" -COPY --from=artifacts /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt -COPY --from=artifacts /bin/gomplate_${OS}-${ARCH}-slim /gomplate +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=compress /bin/gomplate_${TARGETOS}-${TARGETARCH}${TARGETVARIANT}-slim /gomplate ENTRYPOINT [ "/gomplate" ] + +FROM --platform=windows/amd64 mcr.microsoft.com/windows/nanoserver:1809 AS gomplate-windows +ARG TARGETOS +ARG TARGETARCH +ARG TARGETVARIANT +COPY --from=build /bin/gomplate_${TARGETOS}-${TARGETARCH}${TARGETVARIANT}.exe /gomplate.exe + +FROM --platform=windows/amd64 mcr.microsoft.com/windows/nanoserver:1809 AS gomplate-slim-windows +ARG TARGETOS +ARG TARGETARCH +ARG TARGETVARIANT +COPY --from=compress /bin/gomplate_${TARGETOS}-${TARGETARCH}${TARGETVARIANT}-slim.exe /gomplate.exe + +FROM gomplate-$TARGETOS AS gomplate +FROM gomplate-slim-$TARGETOS AS gomplate-slim diff --git a/Makefile b/Makefile index d6eda98d..0c3cf49a 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,10 @@ extension = $(patsubst windows,.exe,$(filter windows,$(1))) GO := go PKG_NAME := gomplate +DOCKER_REPO ?= hairyhenderson/$(PKG_NAME) PREFIX := . +DOCKER_LINUX_PLATFORMS ?= linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7 +DOCKER_PLATFORMS ?= $(DOCKER_LINUX_PLATFORMS),windows/amd64 ifeq ("$(CI)","true") LINT_PROCS ?= 1 @@ -19,8 +22,18 @@ VERSION_FLAG := -X `go list ./version`.Version=$(VERSION) GOOS ?= $(shell go version | sed 's/^.*\ \([a-z0-9]*\)\/\([a-z0-9]*\)/\1/') GOARCH ?= $(shell go version | sed 's/^.*\ \([a-z0-9]*\)\/\([a-z0-9]*\)/\2/') -platforms := freebsd-amd64 linux-amd64 linux-386 linux-arm linux-arm64 darwin-amd64 solaris-amd64 windows-amd64.exe windows-386.exe -compressed-platforms := linux-amd64-slim linux-arm-slim linux-arm64-slim darwin-amd64-slim windows-amd64-slim.exe +ifeq ("$(TARGETVARIANT)","") +ifneq ("$(GOARM)","") +TARGETVARIANT := v$(GOARM) +endif +else +ifeq ("$(GOARM)","") +GOARM ?= $(subst v,,$(TARGETVARIANT)) +endif +endif + +platforms := freebsd-amd64 linux-amd64 linux-386 linux-armv6 linux-armv7 linux-arm64 darwin-amd64 solaris-amd64 windows-amd64.exe windows-386.exe +compressed-platforms := linux-amd64-slim linux-armv6-slim linux-armv7-slim linux-arm64-slim darwin-amd64-slim windows-amd64-slim.exe clean: rm -Rf $(PREFIX)/bin/* @@ -47,7 +60,7 @@ $(PREFIX)/bin/checksums.txt: \ $(PREFIX)/%.signed: $(PREFIX)/% @keybase sign < $< > $@ -compress: $(PREFIX)/bin/$(PKG_NAME)_$(GOOS)-$(GOARCH)-slim$(call extension,$(GOOS)) +compress: $(PREFIX)/bin/$(PKG_NAME)_$(GOOS)-$(GOARCH)$(TARGETVARIANT)-slim$(call extension,$(GOOS)) cp $< $(PREFIX)/bin/$(PKG_NAME)-slim$(call extension,$(GOOS)) %.iid: Dockerfile @@ -57,6 +70,26 @@ compress: $(PREFIX)/bin/$(PKG_NAME)_$(GOOS)-$(GOARCH)-slim$(call extension,$(GOO --iidfile $@ \ . +docker-multi: Dockerfile + docker buildx build \ + --build-arg VCS_REF=$(COMMIT) \ + --platform $(DOCKER_PLATFORMS) \ + --tag $(DOCKER_REPO):latest-$(COMMIT) \ + --target gomplate \ + --push . + docker buildx build \ + --build-arg VCS_REF=$(COMMIT) \ + --platform $(DOCKER_PLATFORMS) \ + --tag $(DOCKER_REPO):slim-$(COMMIT) \ + --target gomplate-slim \ + --push . + docker buildx build \ + --build-arg VCS_REF=$(COMMIT) \ + --platform $(DOCKER_LINUX_PLATFORMS) \ + --tag $(DOCKER_REPO):alpine-$(COMMIT) \ + --target gomplate-alpine \ + --push . + %.cid: %.iid @docker create $(shell cat $<) > $@ @@ -65,17 +98,17 @@ build-release: artifacts.cid docker-images: gomplate.iid gomplate-slim.iid -$(PREFIX)/bin/$(PKG_NAME)_%: $(shell find $(PREFIX) -type f -name "*.go") - GOOS=$(shell echo $* | cut -f1 -d-) GOARCH=$(shell echo $* | cut -f2 -d- | cut -f1 -d.) CGO_ENABLED=0 \ +$(PREFIX)/bin/$(PKG_NAME)_%$(TARGETVARIANT)$(call extension,$(GOOS)): $(shell find $(PREFIX) -type f -name "*.go") + GOOS=$(shell echo $* | cut -f1 -d-) GOARCH=$(shell echo $* | cut -f2 -d- ) GOARM=$(GOARM) CGO_ENABLED=0 \ $(GO) build \ -ldflags "-w -s $(COMMIT_FLAG) $(VERSION_FLAG)" \ -o $@ \ ./cmd/gomplate -$(PREFIX)/bin/$(PKG_NAME)$(call extension,$(GOOS)): $(PREFIX)/bin/$(PKG_NAME)_$(GOOS)-$(GOARCH)$(call extension,$(GOOS)) +$(PREFIX)/bin/$(PKG_NAME)$(call extension,$(GOOS)): $(PREFIX)/bin/$(PKG_NAME)_$(GOOS)-$(GOARCH)$(TARGETVARIANT)$(call extension,$(GOOS)) cp $< $@ -build: $(PREFIX)/bin/$(PKG_NAME)$(call extension,$(GOOS)) +build: $(PREFIX)/bin/$(PKG_NAME)_$(GOOS)-$(GOARCH)$(TARGETVARIANT)$(call extension,$(GOOS)) $(PREFIX)/bin/$(PKG_NAME)$(call extension,$(GOOS)) ifeq ($(OS),Windows_NT) test: