From b887602e3654d9525467ed0cde79884887d94749 Mon Sep 17 00:00:00 2001
From: sh0rez <me@shorez.de>
Date: Thu, 1 Aug 2019 16:08:29 +0200
Subject: [PATCH] chore(packaging/docker): Support multiple architectures
 (#762)

This finally adds support for multiple architectures! :tada:

Images are now built using BuildKit (actually img), Dockerfiles were parameterized to allow this.
Containers are still built on alpine, using QEMU and binfmt as a kernel compatibility layer to allow RUN steps in these.

CircleCI has been fully revamped, to speed up builds and de-duplicate the configuration.
Makefile has been extended with additional logic to shadow the multi-arch to non-CI users.

Happy hacking on Raspberry Pi and friends :)
---
 .circleci/config.yml         | 308 +++++++++++++++++------------------
 Makefile                     | 112 +++++++------
 cmd/docker-driver/Dockerfile |  19 ++-
 cmd/loki-canary/Dockerfile   |  16 +-
 cmd/loki/Dockerfile          |  22 ++-
 cmd/promtail/Dockerfile      |  21 ++-
 loki-build-image/Dockerfile  |  61 ++++---
 tools/image-tag              |  10 +-
 8 files changed, 308 insertions(+), 261 deletions(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 5e1a8bb6..61d692ee 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,173 +1,199 @@
 version: 2
 
+.tags: &tags # tags need to be explicitely defined (whitelist)
+  tags: {only: "/.*/"}
+
+.tag-or-master: &tag-or-master
+  branches: { only: master }
+  <<: *tags
+
+.no-master: &no-master # contrary to tags, the branches must be excluded (blacklist)
+  branches: { ignore: master }
+
 workflows:
   version: 2
-  test-build-deploy:
+  default:
     jobs:
-    - test
-    - test-helm
-    - build
-    - lint
-    - publish:
-        requires:
-        - test
-        - test-helm
-        - build
-        - lint
-        filters:
-          branches:
-            only: master
-    - publish-master:
-        requires:
-        - test
-        - test-helm
-        - build
-        - lint
-        filters:
-          branches:
-            only: master
-    - publish-helm:
-        requires:
-        - test
-        - test-helm
-        - build
-        - lint
-        filters:
-          branches:
-            only: master
-    - deploy:
-        requires:
-        - publish
-        filters:
-          branches:
-            only: master
+      # publish jobs depend on this as well,
+      # thus tags need to be allowed for these
+      - lint: {filters: {<<: *tags}}
+      - test: {filters: {<<: *tags}}
+
+      - build/loki:
+          requires: [ lint, test ]
+          filters: {<<: *no-master}
+      - publish/loki:
+          requires: [ lint, test ]
+          filters: { <<: *tag-or-master }
+
+      - build/canary:
+          requires: [ lint, test ]
+          filters: {<<: *no-master}
+      - publish/canary:
+          requires: [ lint, test ]
+          filters: { <<: *tag-or-master }
+
+      - build/promtail:
+          requires: [ lint, test ]
+          filters: {<<: *no-master}
+      - build/promtail-windows:
+          requires: [ lint, test ]
+      - publish/promtail:
+          requires: [ lint, test ]
+          filters: { <<: *tag-or-master }
+
+      - build/docker-driver:
+          requires: [ lint, test ]
+          filters: {<<: *no-master}
+      - publish/docker-driver:
+          requires: [ lint, test ]
+          filters: { <<: *tag-or-master }
+    
+      - test-helm:
+          requires: [ lint, test ]
+          filters: {<<: *tags}
+      - publish-helm:
+          requires: [ test-helm ]
+          filters: {<<: *tag-or-master}
+
 
 # https://circleci.com/blog/circleci-hacks-reuse-yaml-in-your-circleci-config-with-yaml/
-defaults: &defaults
+.defaults: &defaults
   docker:
     - image: grafana/loki-build-image:0.3.0
   working_directory: /go/src/github.com/grafana/loki
 
+.machine: &machine
+  machine:
+    image: ubuntu-1604:201903-01
+  working_directory: ~/go/src/github.com/grafana/loki
+  environment:
+    APP: to-be-set
+    GOPATH: /home/circleci/go
+
+.rootless: &rootless
+  run:
+    name: rootless
+    command: |
+      sudo apt-get install -qy uidmap libseccomp-dev binfmt-support go-bindata
+      sudo docker run --privileged linuxkit/binfmt:v0.6
+
+.img: &img
+  run:
+    name: img
+    # TODO: switch to https://github.com/genuinetools/img once 5a8119fb4ce7d712ca2ed589a345213fdf576268 is released
+    command: |
+      sudo curl -fSL "https://github.com/sh0rez/img/releases/download/v0.5.8/img-linux-amd64" -o "/usr/local/bin/img"
+      sudo chmod a+x "/usr/local/bin/img"
+
+# builds a container
+.container: &container
+  <<: *machine
+  steps:
+    - checkout
+    - <<: *rootless
+    - <<: *img
+    - run:
+        name: container
+        command: |
+          make $APP-image
+
+# builds and pushes a container
+.publish: &publish
+  <<: *machine
+  steps:
+    - checkout
+    - <<: *rootless
+    - <<: *img
+    - run:
+        name: login
+        command: img login -u "$DOCKER_USER" -p "$DOCKER_PASS"
+    - run:
+        name: push image
+        command: make $APP-push
+
 jobs:
   test:
     <<: *defaults
     steps:
       - checkout
-
       - run:
-          name: Run Unit Tests
-          command: |
-            make test
+          name: Unit Tests
+          command: make BUILD_IN_CONTAINER=false test
 
   lint:
     <<: *defaults
     steps:
       - checkout
-
       - run:
           name: Lint
-          command: |
-            make lint
-
+          command: make lint
       - run:
-          name: Check Generated Fies
-          command: |
-            make BUILD_IN_CONTAINER=false check-generated-files
-
-  build:
-    <<: *defaults
-    steps:
-      - checkout
-      - setup_remote_docker
+          name: Check Generated Files
+          command: make BUILD_IN_CONTAINER=false check-generated-files
 
-      - run:
-          name: Promtail cross platform
-          command: |
-            make GOOS=linux BUILD_IN_CONTAINER=false promtail
-            rm cmd/promtail/promtail
-            make GOOS=windows BUILD_IN_CONTAINER=false promtail
+  # Loki
+  build/loki:
+    <<: *container
+    environment:
+      APP: loki
 
-      - run:
-          name: Build Images
-          command: |
-            make images
+  publish/loki:
+    <<: *publish
+    environment:
+      APP: loki
 
-      - run:
-          name: Save Images
-          command: |
-            make save-images
+  # Loki
+  build/canary:
+    <<: *container
+    environment:
+      APP: loki-canary
 
-      - save_cache:
-          key: v1-loki-{{ .Branch }}-{{ .Revision }}
-          paths:
-          - images/
+  publish/canary:
+    <<: *publish
+    environment:
+      APP: loki-canary
 
-      - save_cache:
-          key: v1-loki-plugin-{{ .Branch }}-{{ .Revision }}
-          paths:
-          - cmd/docker-driver/docker-driver
+  # Promtail
+  build/promtail:
+    <<: *container
+    environment:
+      APP: promtail
 
-  publish:
+  build/promtail-windows:
     <<: *defaults
     steps:
       - checkout
-      - setup_remote_docker
-
-      - restore_cache:
-          key: v1-loki-{{ .Branch }}-{{ .Revision }}
-      - restore_cache:
-          key: v1-loki-plugin-{{ .Branch }}-{{ .Revision }}
-
-      - run:
-          name: Load Images
-          command: |
-            make load-images
-
       - run:
-          name: Push Images
-          command: |
-            if [ -n "$DOCKER_USER" ]; then
-              docker login -u "$DOCKER_USER" -p "$DOCKER_PASS" &&
-              make push-images
-            fi
+          name: build
+          command: make GOOS=windows promtail
 
-      - run:
-          name: Push Docker Plugin
-          command: |
-            if [ -n "$DOCKER_USER" ]; then
-              docker login -u "$DOCKER_USER" -p "$DOCKER_PASS" &&
-              make BUILD_IN_CONTAINER=false docker-driver-push
-            fi
+  publish/promtail:
+    <<: *publish
+    environment:
+      APP: promtail
 
-  publish-master:
+  # Docker driver
+  build/docker-driver:
     <<: *defaults
     steps:
       - checkout
       - setup_remote_docker
-
-      - restore_cache:
-          key: v1-loki-{{ .Branch }}-{{ .Revision }}
-      - restore_cache:
-          key: v1-loki-plugin-{{ .Branch }}-{{ .Revision }}
-
       - run:
-          name: Load Images
-          command: |
-            make load-images
+          name: docker-driver
+          command: make docker-driver
 
+  publish/docker-driver:
+    <<: *defaults
+    steps:
+      - checkout
+      - setup_remote_docker
       - run:
-          name: Push Images
-          command: |
-            docker login -u "$DOCKER_USER" -p "$DOCKER_PASS" &&
-            make push-latest
-
+          name: login
+          command: docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
       - run:
-          name: Push Docker Plugin
-          command: |
-            if [ -n "$DOCKER_USER" ]; then
-              docker login -u "$DOCKER_USER" -p "$DOCKER_PASS" &&
-              PLUGIN_TAG=master make BUILD_IN_CONTAINER=false docker-driver-push && PLUGIN_TAG=latest make BUILD_IN_CONTAINER=false docker-driver-push
-            fi
+          name: docker-driver
+          command: make docker-driver-push
 
   test-helm:
     environment:
@@ -210,35 +236,3 @@ jobs:
             - "5a:d3:08:5e:f7:53:a0:c4:e9:5d:83:c6:02:6a:d9:bd"
       - checkout
       - run: make helm-publish
-
-  deploy:
-    <<: *defaults
-    steps:
-      - checkout
-
-      - run: |
-          curl -s --header "Content-Type: application/json" \
-            --data "{\"build_parameters\": {\"CIRCLE_JOB\": \"deploy\", \"IMAGE_NAMES\": \"$(make images)\"}}" \
-            --request POST \
-            https://circleci.com/api/v1.1/project/github/raintank/deployment_tools/tree/master?circle-token=$CIRCLE_TOKEN
-
-  release:
-    <<: *defaults
-    steps:
-      - checkout
-      - setup_remote_docker
-      - restore_cache:
-          key: v1-loki-{{ .Branch }}-{{ .Revision }}
-      - run:
-          name: Load Images
-          command: |
-            touch loki-build-image/.uptodate &&
-            make BUILD_IN_CONTAINER=false load-images
-      - run:
-          name: "Print Tag"
-          command: echo ${CIRCLE_TAG}
-      - run:
-          name: "Release"
-          command: |
-            docker login -u "$DOCKER_USER" -p "$DOCKER_PASS" &&
-            make VERSION=${CIRCLE_TAG} release-perform
diff --git a/Makefile b/Makefile
index 8c7e8cfc..533a976f 100644
--- a/Makefile
+++ b/Makefile
@@ -16,10 +16,11 @@ IMAGE_NAMES := $(foreach dir,$(DOCKER_IMAGE_DIRS),$(patsubst %,$(IMAGE_PREFIX)%,
 # make BUILD_IN_CONTAINER=false target
 # or you can override this with an environment variable
 BUILD_IN_CONTAINER ?= true
-BUILD_IMAGE_VERSION := "0.2.1"
+BUILD_IMAGE_VERSION := 0.3.0
 
 # Docker image info
 IMAGE_PREFIX ?= grafana
+
 IMAGE_TAG := $(shell ./tools/image-tag)
 
 # Version info for binaries
@@ -59,6 +60,11 @@ PROTO_GOS := $(patsubst %.proto,%.pb.go,$(PROTO_DEFS))
 YACC_DEFS := $(shell find . $(DONT_FIND) -type f -name *.y -print)
 YACC_GOS := $(patsubst %.y,%.y.go,$(YACC_DEFS))
 
+
+##########
+# Docker #
+##########
+
 # RM is parameterized to allow CircleCI to run builds, as it
 # currently disallows `docker run --rm`. This value is overridden
 # in circle.yml
@@ -68,13 +74,27 @@ RM := --rm
 # in any custom cloudbuild.yaml files
 TTY := --tty
 
+DOCKER_BUILDKIT=1
+OCI_PLATFORMS=--platform=linux/amd64 --platform=linux/arm64 --platform=linux/arm/7
+BUILD_IMAGE = BUILD_IMAGE=$(IMAGE_PREFIX)/loki-build-image:$(BUILD_IMAGE_VERSION)
+ifeq ($(CI), true)
+	BUILD_OCI=img build --no-console $(OCI_PLATFORMS) --build-arg $(BUILD_IMAGE)
+	PUSH_OCI=img push
+	TAG_OCI=img tag
+else
+	BUILD_OCI=docker build --build-arg $(BUILD_IMAGE)
+	PUSH_OCI=docker push
+	TAG_OCI=docker tag
+endif
+
+binfmt:
+	$(SUDO) docker run --privileged linuxkit/binfmt:v0.6
+
 ################
 # Main Targets #
 ################
-
 all: promtail logcli loki loki-canary check-generated-files
 
-
 # This is really a check for the CI to make sure generated files are built and checked in manually
 check-generated-files: yacc protos
 	@if ! (git diff --exit-code $(YACC_GOS) $(PROTO_GOS)); then \
@@ -265,9 +285,9 @@ helm-clean:
 
 PLUGIN_TAG ?= $(IMAGE_TAG)
 
-docker-driver: docker-driver-clean cmd/docker-driver/docker-driver
+docker-driver: docker-driver-clean 
 	mkdir cmd/docker-driver/rootfs
-	docker build -t rootfsimage cmd/docker-driver
+	docker build -t rootfsimage -f cmd/docker-driver/Dockerfile .
 	ID=$$(docker create rootfsimage true) && \
 	(docker export $$ID | tar -x -C cmd/docker-driver/rootfs) && \
 	docker rm -vf $$ID
@@ -298,59 +318,53 @@ images: promtail-image loki-image loki-canary-image docker-driver
 
 IMAGE_NAMES := grafana/loki grafana/promtail grafana/loki-canary
 
-save-images:
-	@set -e; \
-	mkdir -p images; \
-	for image_name in $(IMAGE_NAMES); do \
-		echo ">> saving image $$image_name:$(IMAGE_TAG)"; \
-		docker save $$image_name:$(IMAGE_TAG) -o images/$$(echo $$image_name | tr "/" _):$(IMAGE_TAG); \
-	done
-
-load-images:
-	@set -e; \
-	mkdir -p images; \
-	for image_name in $(IMAGE_NAMES); do \
-		docker load -i images/$$(echo $$image_name | tr "/" _):$(IMAGE_TAG); \
-	done
-
-push-images:
-	@set -e; \
-	for image_name in $(IMAGE_NAMES); do \
-		docker push $$image_name:$(IMAGE_TAG); \
-	done
-
-push-latest:
-	@set -e; \
-	for image_name in $(IMAGE_NAMES); do \
-		docker tag $$image_name:$(IMAGE_TAG) $$image_name:latest; \
-		docker tag $$image_name:$(IMAGE_TAG) $$image_name:master; \
-		docker push $$image_name:latest; \
-		docker push $$image_name:master; \
-	done
-
-
+# push(app, optional tag)
+# pushes the app, optionally tagging it differently before
+define push
+	$(SUDO) $(TAG_OCI)  $(IMAGE_PREFIX)/$(1):$(IMAGE_TAG) $(IMAGE_PREFIX)/$(1):$(if $(2),$(2),$(IMAGE_TAG))
+	$(SUDO) $(PUSH_OCI) $(IMAGE_PREFIX)/$(1):$(if $(2),$(2),$(IMAGE_TAG))
+endef
+
+# push-image(app)
+# pushes the app, if branch==master also as :latest and :master
+define push-image
+	$(call push,$(1))
+	$(if $(filter $(GIT_BRANCH),master), $(call push,promtail,master))
+	$(if $(filter $(GIT_BRANCH),master), $(call push,promtail,latest))
+endef
+
+# promtail
 promtail-image:
-	$(SUDO) docker build -t $(IMAGE_PREFIX)/promtail -f cmd/promtail/Dockerfile .
-	$(SUDO) docker tag $(IMAGE_PREFIX)/promtail $(IMAGE_PREFIX)/promtail:$(IMAGE_TAG)
+	$(SUDO) $(BUILD_OCI) -t $(IMAGE_PREFIX)/promtail:$(IMAGE_TAG) -f cmd/promtail/Dockerfile .
+
+promtail-debug-image: OCI_PLATFORMS=
 promtail-debug-image:
-	$(SUDO) docker build -t $(IMAGE_PREFIX)/promtail -f cmd/promtail/Dockerfile.debug .
-	$(SUDO) docker tag $(IMAGE_PREFIX)/promtail-debug $(IMAGE_PREFIX)/promtail-debug:$(IMAGE_TAG)
+	$(SUDO) $(BUILD_OCI) -t $(IMAGE_PREFIX)/promtail:$(IMAGE_TAG)-debug -f cmd/promtail/Dockerfile.debug .
+
+promtail-push: promtail-image
+	$(call push-image,promtail)
 
+# loki
 loki-image:
-	$(SUDO) docker build -t $(IMAGE_PREFIX)/loki -f cmd/loki/Dockerfile .
-	$(SUDO) docker tag $(IMAGE_PREFIX)/loki $(IMAGE_PREFIX)/loki:$(IMAGE_TAG)
+	$(SUDO) $(BUILD_OCI) -t $(IMAGE_PREFIX)/loki:$(IMAGE_TAG) -f cmd/loki/Dockerfile .
+
+loki-debug-image: OCI_PLATFORMS=
 loki-debug-image:
-	$(SUDO) docker build -t $(IMAGE_PREFIX)/loki -f cmd/loki/Dockerfile.debug .
-	$(SUDO) docker tag $(IMAGE_PREFIX)/loki-debug $(IMAGE_PREFIX)/loki-debug:$(IMAGE_TAG)
+	$(SUDO) $(BUILD_OCI) -t $(IMAGE_PREFIX)/loki:$(IMAGE_TAG)-debug -f cmd/loki/Dockerfile.debug .
+
+loki-push: loki-image
+	$(call push-image,loki)
 
+# loki-canary
 loki-canary-image:
-	$(SUDO) docker build -t $(IMAGE_PREFIX)/loki-canary -f cmd/loki-canary/Dockerfile .
-	$(SUDO) docker tag $(IMAGE_PREFIX)/loki-canary $(IMAGE_PREFIX)/loki-canary:$(IMAGE_TAG)
+	$(SUDO) $(BUILD_OCI) -t $(IMAGE_PREFIX)/loki-canary:$(IMAGE_TAG) -f cmd/loki-canary/Dockerfile .
+loki-canary-push: loki-canary-image
+	$(SUDO) $(PUSH_OCI) $(IMAGE_PREFIX)/loki-canary:$(IMAGE_TAG)
 
+# build-image (only amd64)
+build-image: OCI_PLATFORMS=
 build-image:
-	$(SUDO) docker build -t $(IMAGE_PREFIX)/loki-build-image -f loki-build-image/Dockerfile .
-	$(SUDO) docker tag $(IMAGE_PREFIX)/loki-build-image $(IMAGE_PREFIX)/loki-build-image:$(IMAGE_TAG)
-
+	$(SUDO) $(BUILD_OCI) -t $(IMAGE_PREFIX)/loki-build-image:$(IMAGE_TAG) ./loki-build-image
 
 ########
 # Misc #
diff --git a/cmd/docker-driver/Dockerfile b/cmd/docker-driver/Dockerfile
index a44c52e3..c4274830 100644
--- a/cmd/docker-driver/Dockerfile
+++ b/cmd/docker-driver/Dockerfile
@@ -1,5 +1,16 @@
-FROM       alpine:3.9
-RUN        apk add --update --no-cache ca-certificates
-COPY       docker-driver /bin/docker-driver
+ARG BUILD_IMAGE=grafana/loki-build-image:0.2.1
+# Directories in this file are referenced from the root of the project not this folder
+# This file is intented to be called from the root like so:
+# docker build -t grafana/loki -f cmd/loki/Dockerfile .
+
+# TODO: add cross-platform support
+FROM $BUILD_IMAGE as build
+COPY . /go/src/github.com/grafana/loki
+WORKDIR /go/src/github.com/grafana/loki
+RUN make clean && make cmd/docker-driver/docker-driver
+
+FROM alpine:3.9
+RUN apk add --update --no-cache ca-certificates
+COPY --from=build /go/src/github.com/grafana/loki/cmd/docker-driver/docker-driver /bin/docker-driver
 WORKDIR /bin/
-ENTRYPOINT [ "/bin/docker-driver" ]
\ No newline at end of file
+ENTRYPOINT [ "/bin/docker-driver" ]
diff --git a/cmd/loki-canary/Dockerfile b/cmd/loki-canary/Dockerfile
index fbfb024a..68a1010a 100644
--- a/cmd/loki-canary/Dockerfile
+++ b/cmd/loki-canary/Dockerfile
@@ -1,14 +1,18 @@
+ARG BUILD_IMAGE=grafana/loki-build-image:0.2.1
 # Directories in this file are referenced from the root of the project not this folder
 # This file is intented to be called from the root like so:
 # docker build -t grafana/promtail -f cmd/promtail/Dockerfile .
+FROM golang:1.11.4-alpine as goenv
+RUN go env GOARCH > /goarch && \
+  go env GOARM > /goarm
 
-FROM grafana/loki-build-image:0.2.1 as build
-ARG GOARCH="amd64"
+FROM --platform=linux/amd64 $BUILD_IMAGE as build
+COPY --from=goenv /goarch /goarm /
 COPY . /go/src/github.com/grafana/loki
 WORKDIR /go/src/github.com/grafana/loki
-RUN make clean && make loki-canary
+RUN GOARCH=$(cat /goarch) GOARM=$(cat /goarm) make clean && make loki-canary
 
-FROM       alpine:3.9
-RUN        apk add --update --no-cache ca-certificates
-COPY       --from=build /go/src/github.com/grafana/loki/cmd/loki-canary/loki-canary /usr/bin/loki-canary
+FROM alpine:3.9
+RUN apk add --update --no-cache ca-certificates
+COPY --from=build /go/src/github.com/grafana/loki/cmd/loki-canary/loki-canary /usr/bin/loki-canary
 ENTRYPOINT [ "/usr/bin/loki-canary" ]
diff --git a/cmd/loki/Dockerfile b/cmd/loki/Dockerfile
index 81111e19..ff2a3d32 100644
--- a/cmd/loki/Dockerfile
+++ b/cmd/loki/Dockerfile
@@ -1,17 +1,21 @@
+ARG BUILD_IMAGE=grafana/loki-build-image:0.2.1
 # Directories in this file are referenced from the root of the project not this folder
 # This file is intented to be called from the root like so:
 # docker build -t grafana/loki -f cmd/loki/Dockerfile .
+FROM golang:1.11.4-alpine as goenv
+RUN go env GOARCH > /goarch && \
+    go env GOARM > /goarm
 
-FROM grafana/loki-build-image:0.2.1 as build
-ARG GOARCH="amd64"
+FROM --platform=linux/amd64 $BUILD_IMAGE as build
+COPY --from=goenv /goarch /goarm /
 COPY . /go/src/github.com/grafana/loki
 WORKDIR /go/src/github.com/grafana/loki
-RUN make clean && make loki
+RUN GOARCH=$(cat /goarch) GOARM=$(cat /goarm) make clean && make loki
 
-FROM       alpine:3.9
-RUN        apk add --update --no-cache ca-certificates
-COPY       --from=build /go/src/github.com/grafana/loki/cmd/loki/loki /usr/bin/loki
-COPY       cmd/loki/loki-local-config.yaml /etc/loki/local-config.yaml
-EXPOSE     80
+FROM alpine:3.9
+RUN apk add --update --no-cache ca-certificates
+COPY --from=build /go/src/github.com/grafana/loki/cmd/loki/loki /usr/bin/loki
+COPY cmd/loki/loki-local-config.yaml /etc/loki/local-config.yaml
+EXPOSE 80
 ENTRYPOINT [ "/usr/bin/loki" ]
-CMD        ["-config.file=/etc/loki/local-config.yaml"]
+CMD ["-config.file=/etc/loki/local-config.yaml"]
diff --git a/cmd/promtail/Dockerfile b/cmd/promtail/Dockerfile
index 58ba144f..63f4ecc6 100644
--- a/cmd/promtail/Dockerfile
+++ b/cmd/promtail/Dockerfile
@@ -1,16 +1,21 @@
+ARG BUILD_IMAGE=grafana/loki-build-image:0.2.1
 # Directories in this file are referenced from the root of the project not this folder
 # This file is intented to be called from the root like so:
 # docker build -t grafana/promtail -f cmd/promtail/Dockerfile .
+FROM golang:1.11.4-alpine as goenv
+RUN go env GOARCH > /goarch && \
+    go env GOARM > /goarm
 
-FROM grafana/loki-build-image:0.2.1 as build
-ARG GOARCH="amd64"
+FROM --platform=linux/amd64 $BUILD_IMAGE as build
+COPY --from=goenv /goarch /goarm /
 COPY . /go/src/github.com/grafana/loki
 WORKDIR /go/src/github.com/grafana/loki
-RUN make clean && make promtail
+RUN GOARCH=$(cat /goarch) GOARM=$(cat /goarm) make clean && make promtail
 
-FROM       alpine:3.9
-RUN        apk add --update --no-cache ca-certificates tzdata
-COPY       --from=build /go/src/github.com/grafana/loki/cmd/promtail/promtail /usr/bin/promtail
-COPY       cmd/promtail/promtail-local-config.yaml /etc/promtail/local-config.yaml
-COPY       cmd/promtail/promtail-docker-config.yaml /etc/promtail/docker-config.yaml
+FROM alpine:3.9
+# tzdata required for the timestamp stage to work
+RUN apk add --update --no-cache ca-certificates tzdata
+COPY --from=build /go/src/github.com/grafana/loki/cmd/promtail/promtail /usr/bin/promtail
+COPY cmd/promtail/promtail-local-config.yaml /etc/promtail/local-config.yaml
+COPY cmd/promtail/promtail-docker-config.yaml /etc/promtail/docker-config.yaml
 ENTRYPOINT ["/usr/bin/promtail"]
diff --git a/loki-build-image/Dockerfile b/loki-build-image/Dockerfile
index 0ea0ea8e..46e6764e 100644
--- a/loki-build-image/Dockerfile
+++ b/loki-build-image/Dockerfile
@@ -1,28 +1,39 @@
+FROM alpine as helm
+ARG HELM_VER="v2.13.1"
+RUN apk add --no-cache curl && \
+    curl -L -o /tmp/helm-$HELM_VER.tgz http://storage.googleapis.com/kubernetes-helm/helm-${HELM_VER}-linux-amd64.tar.gz && \
+    tar -xz -C /tmp -f /tmp/helm-$HELM_VER.tgz && \
+    mv /tmp/linux-amd64/helm /usr/bin/helm && \
+    rm -rf /tmp/linux-amd64 /tmp/helm-$HELM_VER.tgz
+
+FROM alpine as golangci
+RUN apk add --no-cache curl && \
+    cd / && \
+    curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.17.1
+
+FROM alpine:edge as docker
+RUN apk add --no-cache docker-cli
+
 FROM golang:1.11.4-stretch
-RUN apt-get update && apt-get install -y file jq unzip protobuf-compiler libprotobuf-dev libsystemd-dev && \
-	rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
-ENV DOCKER_VER="17.03.0-ce"
-RUN curl -L -o /tmp/docker-$DOCKER_VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$DOCKER_VER.tgz && \
-	tar -xz -C /tmp -f /tmp/docker-$DOCKER_VER.tgz && \
-	mv /tmp/docker/* /usr/bin && \
-	rm /tmp/docker-$DOCKER_VER.tgz
-ENV HELM_VER="v2.13.1"
-RUN curl -L -o /tmp/helm-$HELM_VER.tgz http://storage.googleapis.com/kubernetes-helm/helm-${HELM_VER}-linux-amd64.tar.gz && \
-	tar -xz -C /tmp -f /tmp/helm-$HELM_VER.tgz && \
-	mv /tmp/linux-amd64/helm /usr/bin/helm && \
-	rm -rf /tmp/linux-amd64 /tmp/helm-$HELM_VER.tgz
+RUN apt-get update && \
+    apt-get install -qy \
+      file unzip jq \
+      protobuf-compiler libprotobuf-dev \
+      libsystemd-dev && \
+    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+COPY --from=docker /usr/bin/docker /usr/bin/docker
+COPY --from=helm /usr/bin/helm /usr/bin/helm
+COPY --from=golangci /bin/golangci-lint /usr/local/bin
 RUN go get \
-	github.com/golang/protobuf/protoc-gen-go \
-	github.com/gogo/protobuf/protoc-gen-gogoslick \
-	github.com/gogo/protobuf/gogoproto \
-	github.com/go-delve/delve/cmd/dlv \
-	golang.org/x/tools/cmd/goyacc && \
-	rm -rf /go/pkg /go/src
-ENV GOLANGCI_LINT_COMMIT="692dacb773b703162c091c2d8c59f9cd2d6801db"
-RUN mkdir -p $(go env GOPATH)/src/github.com/golangci/ && git clone https://github.com/golangci/golangci-lint.git $(go env GOPATH)/src/github.com/golangci/golangci-lint && \
-	cd $(go env GOPATH)/src/github.com/golangci/golangci-lint  && git checkout ${GOLANGCI_LINT_COMMIT} && cd cmd/golangci-lint/  &&\
-	GO111MODULE=on go install && \
-	golangci-lint help
-#COPY build.sh /
+    github.com/golang/protobuf/protoc-gen-go \
+    github.com/gogo/protobuf/protoc-gen-gogoslick \
+    github.com/gogo/protobuf/gogoproto \
+    github.com/go-delve/delve/cmd/dlv \
+    golang.org/x/tools/cmd/goyacc && \
+    rm -rf /go/pkg /go/src
 ENV GOCACHE=/go/cache
-#ENTRYPOINT ["/build.sh"]
+
+COPY build.sh /
+RUN chmod +x /build.sh
+ENTRYPOINT ["/build.sh"]
diff --git a/tools/image-tag b/tools/image-tag
index 31f023da..182fc654 100755
--- a/tools/image-tag
+++ b/tools/image-tag
@@ -4,6 +4,10 @@ set -o errexit
 set -o nounset
 set -o pipefail
 
-WORKING_SUFFIX=$(if git status --porcelain | grep -qE '^(?:[^?][^ ]|[^ ][^?])\s'; then echo "-WIP"; else echo ""; fi)
-BRANCH_PREFIX=$(git rev-parse --abbrev-ref HEAD)
-echo "${BRANCH_PREFIX//\//-}-$(git rev-parse --short HEAD)$WORKING_SUFFIX"
+WIP=$(git diff --quiet || echo '-WIP')
+BRANCH=$(git rev-parse --abbrev-ref HEAD)
+SHA=$(git rev-parse --short HEAD)
+
+# If this is a tag, use it,                otherwise branch-hash
+TAG=$(git describe --exact-match 2> /dev/null || echo "${BRANCH}-${SHA}")
+echo ${TAG}${WIP}
-- 
GitLab