فهرست منبع

Merge pull request #39340 from cpuguy83/buildkit_dockerfile

Buildkit-optimized dockerfile + buildx
Akihiro Suda 5 سال پیش
والد
کامیت
ed2f50f8cd
4فایلهای تغییر یافته به همراه243 افزوده شده و 150 حذف شده
  1. 170 133
      Dockerfile
  2. 14 0
      Dockerfile.buildx
  3. 12 4
      Jenkinsfile
  4. 47 13
      Makefile

+ 170 - 133
Dockerfile

@@ -1,34 +1,11 @@
-# This file describes the standard way to build Docker, using docker
-#
-# Usage:
-#
-# # Use make to build a development environment image and run it in a container.
-# # This is slow the first time.
-# make BIND_DIR=. shell
-#
-# The following commands are executed inside the running container.
-
-# # Make a dockerd binary.
-# # hack/make.sh binary
-#
-# # Install dockerd to /usr/local/bin
-# # make install
-#
-# # Run unit tests
-# # hack/test/unit
-#
-# # Run tests e.g. integration, py
-# # hack/make.sh binary test-integration test-docker-py
-#
-# Note: AppArmor used to mess with privileged mode, but this is no longer
-# the case. Therefore, you don't have to disable it anymore.
-#
+# syntax=docker/dockerfile:1.1.3-experimental
 
 ARG CROSS="false"
 ARG GO_VERSION=1.13.1
 ARG DEBIAN_FRONTEND=noninteractive
 
 FROM golang:${GO_VERSION}-stretch AS base
+RUN echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
 ARG APT_MIRROR
 RUN sed -ri "s/(httpredir|deb).debian.org/${APT_MIRROR:-deb.debian.org}/g" /etc/apt/sources.list \
  && sed -ri "s/(security).debian.org/${APT_MIRROR:-security.debian.org}/g" /etc/apt/sources.list
@@ -36,19 +13,21 @@ ENV GO111MODULE=off
 
 FROM base AS criu
 ARG DEBIAN_FRONTEND
+# Install dependency packages specific to criu
+RUN --mount=type=cache,sharing=locked,id=moby-criu-aptlib,target=/var/lib/apt \
+	--mount=type=cache,sharing=locked,id=moby-criu-aptcache,target=/var/cache/apt \
+		apt-get update && apt-get install -y --no-install-recommends \
+			libnet-dev \
+			libprotobuf-c-dev \
+			libprotobuf-dev \
+			libnl-3-dev \
+			libcap-dev \
+			protobuf-compiler \
+			protobuf-c-compiler \
+			python-protobuf
+
 # Install CRIU for checkpoint/restore support
 ENV CRIU_VERSION 3.12
-# Install dependency packages specific to criu
-RUN apt-get update && apt-get install -y --no-install-recommends \
-	libnet-dev \
-	libprotobuf-c-dev \
-	libprotobuf-dev \
-	libnl-3-dev \
-	libcap-dev \
-	protobuf-compiler \
-	protobuf-c-compiler \
-	python-protobuf \
-	&& rm -rf /var/lib/apt/lists/*
 RUN mkdir -p /usr/src/criu \
 	&& curl -sSL https://github.com/checkpoint-restore/criu/archive/v${CRIU_VERSION}.tar.gz | tar -C /usr/src/criu/ -xz --strip-components=1 \
 	&& cd /usr/src/criu \
@@ -62,37 +41,42 @@ FROM base AS registry
 # and schema2 manifests.
 ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd
 ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827
-RUN set -x \
-	&& export GOPATH="$(mktemp -d)" \
-	&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
-	&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
-	&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
-		go build -buildmode=pie -o /build/registry-v2 github.com/docker/distribution/cmd/registry \
-	&& case $(dpkg --print-architecture) in \
-		amd64|ppc64*|s390x) \
-		(cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1"); \
-		GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH"; \
-			go build -buildmode=pie -o /build/registry-v2-schema1 github.com/docker/distribution/cmd/registry; \
-		;; \
-	   esac \
-	&& rm -rf "$GOPATH"
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		set -x \
+		&& export GOPATH="$(mktemp -d)" \
+		&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
+		&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
+		&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
+			go build -buildmode=pie -o /build/registry-v2 github.com/docker/distribution/cmd/registry \
+		&& case $(dpkg --print-architecture) in \
+			amd64|ppc64*|s390x) \
+			(cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1"); \
+			GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH"; \
+				go build -buildmode=pie -o /build/registry-v2-schema1 github.com/docker/distribution/cmd/registry; \
+			;; \
+			 esac \
+		&& rm -rf "$GOPATH"
 
 FROM base AS swagger
 # Install go-swagger for validating swagger.yaml
 ENV GO_SWAGGER_COMMIT c28258affb0b6251755d92489ef685af8d4ff3eb
-RUN set -x \
-	&& export GOPATH="$(mktemp -d)" \
-	&& git clone https://github.com/go-swagger/go-swagger.git "$GOPATH/src/github.com/go-swagger/go-swagger" \
-	&& (cd "$GOPATH/src/github.com/go-swagger/go-swagger" && git checkout -q "$GO_SWAGGER_COMMIT") \
-	&& go build -o /build/swagger github.com/go-swagger/go-swagger/cmd/swagger \
-	&& rm -rf "$GOPATH"
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		set -x \
+		&& export GOPATH="$(mktemp -d)" \
+		&& git clone https://github.com/go-swagger/go-swagger.git "$GOPATH/src/github.com/go-swagger/go-swagger" \
+		&& (cd "$GOPATH/src/github.com/go-swagger/go-swagger" && git checkout -q "$GO_SWAGGER_COMMIT") \
+		&& go build -o /build/swagger github.com/go-swagger/go-swagger/cmd/swagger \
+		&& rm -rf "$GOPATH"
 
 FROM base AS frozen-images
 ARG DEBIAN_FRONTEND
-RUN apt-get update && apt-get install -y --no-install-recommends \
-	ca-certificates \
-	jq \
-	&& rm -rf /var/lib/apt/lists/*
+RUN --mount=type=cache,sharing=locked,id=moby-frozen-images-aptlib,target=/var/lib/apt \
+	--mount=type=cache,sharing=locked,id=moby-frozen-images-aptcache,target=/var/cache/apt \
+		apt-get update && apt-get install -y --no-install-recommends \
+		ca-certificates \
+		jq
 # Get useful and necessary Hub images so we can "docker load" locally instead of pulling
 COPY contrib/download-frozen-image-v2.sh /
 RUN /download-frozen-image-v2.sh /build \
@@ -110,42 +94,47 @@ ARG DEBIAN_FRONTEND
 RUN dpkg --add-architecture armhf
 RUN dpkg --add-architecture arm64
 RUN dpkg --add-architecture armel
-RUN if [ "$(go env GOHOSTARCH)" = "amd64" ]; then \
-	apt-get update && apt-get install -y --no-install-recommends \
-		crossbuild-essential-armhf \
-		crossbuild-essential-arm64 \
-		crossbuild-essential-armel \
-		&& rm -rf /var/lib/apt/lists/*; \
-	fi
+RUN --mount=type=cache,sharing=locked,id=moby-cross-true-aptlib,target=/var/lib/apt \
+	--mount=type=cache,sharing=locked,id=moby-cross-true-aptcache,target=/var/cache/apt \
+		if [ "$(go env GOHOSTARCH)" = "amd64" ]; then \
+			apt-get update && apt-get install -y --no-install-recommends \
+			crossbuild-essential-armhf \
+			crossbuild-essential-arm64 \
+			crossbuild-essential-armel \
+		fi
 
 FROM cross-${CROSS} as dev-base
 
 FROM dev-base AS runtime-dev-cross-false
 ARG DEBIAN_FRONTEND
-RUN apt-get update && apt-get install -y --no-install-recommends \
-	libapparmor-dev \
-	libseccomp-dev \
-	&& rm -rf /var/lib/apt/lists/*
+RUN --mount=type=cache,sharing=locked,id=moby-cross-false-aptlib,target=/var/lib/apt \
+	--mount=type=cache,sharing=locked,id=moby-cross-false-aptcache,target=/var/cache/apt \
+		apt-get update && apt-get install -y --no-install-recommends \
+		libapparmor-dev \
+		libseccomp-dev
+
 FROM cross-true AS runtime-dev-cross-true
 ARG DEBIAN_FRONTEND
 # These crossbuild packages rely on gcc-<arch>, but this doesn't want to install
 # on non-amd64 systems.
 # Additionally, the crossbuild-amd64 is currently only on debian:buster, so
 # other architectures cannnot crossbuild amd64.
-RUN if [ "$(go env GOHOSTARCH)" = "amd64" ]; then \
-	apt-get update && apt-get install -y --no-install-recommends \
-		libseccomp-dev:armhf \
-		libseccomp-dev:arm64 \
-		libseccomp-dev:armel \
-		libapparmor-dev:armhf \
-		libapparmor-dev:arm64 \
-		libapparmor-dev:armel \
-		# install this arches seccomp here due to compat issues with the v0 builder
-		# This is as opposed to inheriting from runtime-dev-cross-false
-		libapparmor-dev \
-		libseccomp-dev \
-		&& rm -rf /var/lib/apt/lists/*; \
-	fi
+RUN --mount=type=cache,sharing=locked,id=moby-cross-true-aptlib,target=/var/lib/apt \
+	--mount=type=cache,sharing=locked,id=moby-cross-true-aptcache,target=/var/cache/apt \
+		if [ "$(go env GOHOSTARCH)" = "amd64" ]; then \
+			apt-get update && apt-get install -y --no-install-recommends \
+				libseccomp-dev:armhf \
+				libseccomp-dev:arm64 \
+				libseccomp-dev:armel \
+				libapparmor-dev:armhf \
+				libapparmor-dev:arm64 \
+				libapparmor-dev:armel \
+				# install this arches seccomp here due to compat issues with the v0 builder
+				# This is as opposed to inheriting from runtime-dev-cross-false
+				libapparmor-dev \
+				libseccomp-dev \
+		fi
+
 
 FROM runtime-dev-cross-${CROSS} AS runtime-dev
 
@@ -153,70 +142,92 @@ FROM base AS tomlv
 ENV INSTALL_BINARY_NAME=tomlv
 COPY hack/dockerfile/install/install.sh ./install.sh
 COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
-RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
 
 FROM base AS vndr
 ENV INSTALL_BINARY_NAME=vndr
 COPY hack/dockerfile/install/install.sh ./install.sh
 COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
-RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
 
 FROM dev-base AS containerd
 ARG DEBIAN_FRONTEND
-RUN apt-get update && apt-get install -y --no-install-recommends \
-	btrfs-tools \
-	&& rm -rf /var/lib/apt/lists/*
+RUN --mount=type=cache,sharing=locked,id=moby-containerd-aptlib,target=/var/lib/apt \
+	--mount=type=cache,sharing=locked,id=moby-containerd-aptcache,target=/var/cache/apt \
+		apt-get update && apt-get install -y --no-install-recommends \
+			btrfs-tools
 ENV INSTALL_BINARY_NAME=containerd
 COPY hack/dockerfile/install/install.sh ./install.sh
 COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
-RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
 
 FROM dev-base AS proxy
 ENV INSTALL_BINARY_NAME=proxy
 COPY hack/dockerfile/install/install.sh ./install.sh
 COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
-RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
 
 FROM base AS golangci_lint
 ENV INSTALL_BINARY_NAME=golangci_lint
 COPY hack/dockerfile/install/install.sh ./install.sh
 COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
-RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
 
 FROM base AS gotestsum
 ENV INSTALL_BINARY_NAME=gotestsum
 COPY hack/dockerfile/install/install.sh ./install.sh
 COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
-RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
 
 FROM dev-base AS dockercli
 ENV INSTALL_BINARY_NAME=dockercli
 COPY hack/dockerfile/install/install.sh ./install.sh
 COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
-RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
 
 FROM runtime-dev AS runc
 ENV INSTALL_BINARY_NAME=runc
 COPY hack/dockerfile/install/install.sh ./install.sh
 COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
-RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
 
 FROM dev-base AS tini
 ARG DEBIAN_FRONTEND
-RUN apt-get update && apt-get install -y --no-install-recommends \
-	cmake \
-	vim-common \
-	&& rm -rf /var/lib/apt/lists/*
+RUN --mount=type=cache,sharing=locked,id=moby-tini-aptlib,target=/var/lib/apt \
+	--mount=type=cache,sharing=locked,id=moby-tini-aptcache,target=/var/cache/apt \
+		apt-get update && apt-get install -y --no-install-recommends \
+			cmake \
+			vim-common
 COPY hack/dockerfile/install/install.sh ./install.sh
 ENV INSTALL_BINARY_NAME=tini
 COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
-RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		PREFIX=/build ./install.sh $INSTALL_BINARY_NAME
 
 FROM dev-base AS rootlesskit
 ENV INSTALL_BINARY_NAME=rootlesskit
 COPY hack/dockerfile/install/install.sh ./install.sh
 COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./
-RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	--mount=type=cache,target=/go/pkg/mod \
+		PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME
 COPY ./contrib/dockerd-rootless.sh /build
 
 # TODO: Some of this is only really needed for testing, it would be nice to split this up
@@ -232,35 +243,37 @@ RUN ln -s /usr/local/completion/bash/docker /etc/bash_completion.d/docker
 RUN ldconfig
 # This should only install packages that are specifically needed for the dev environment and nothing else
 # Do you really need to add another package here? Can it be done in a different build stage?
-RUN apt-get update && apt-get install -y --no-install-recommends \
-	apparmor \
-	aufs-tools \
-	bash-completion \
-	btrfs-tools \
-	iptables \
-	jq \
-	libcap2-bin \
-	libdevmapper-dev \
-	libudev-dev \
-	libsystemd-dev \
-	binutils-mingw-w64 \
-	g++-mingw-w64-x86-64 \
-	net-tools \
-	pigz \
-	python3-pip \
-	python3-setuptools \
-	python3-wheel \
-	thin-provisioning-tools \
-	vim \
-	vim-common \
-	xfsprogs \
-	zip \
-	bzip2 \
-	xz-utils \
-	libprotobuf-c1 \
-	libnet1 \
-	libnl-3-200 \
-	&& rm -rf /var/lib/apt/lists/*
+RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \
+	--mount=type=cache,sharing=locked,id=moby-dev-aptcache,target=/var/cache/apt \
+		apt-get update && apt-get install -y --no-install-recommends \
+		apparmor \
+		aufs-tools \
+		bash-completion \
+		btrfs-tools \
+		iptables \
+		jq \
+		libcap2-bin \
+		libdevmapper-dev \
+		libudev-dev \
+		libsystemd-dev \
+		binutils-mingw-w64 \
+		g++-mingw-w64-x86-64 \
+		net-tools \
+		pigz \
+		python3-pip \
+		python3-setuptools \
+		python3-wheel \
+		thin-provisioning-tools \
+		vim \
+		vim-common \
+		xfsprogs \
+		zip \
+		bzip2 \
+		xz-utils \
+		libprotobuf-c1 \
+		libnet1 \
+		libnl-3-200
+
 
 RUN pip3 install yamllint==1.16.0
 
@@ -286,7 +299,31 @@ WORKDIR /go/src/github.com/docker/docker
 VOLUME /var/lib/docker
 # Wrap all commands in the "docker-in-docker" script to allow nested containers
 ENTRYPOINT ["hack/dind"]
+COPY . /go/src/github.com/docker/docker
+
+FROM dev AS build-binary
+ARG DOCKER_GITCOMMIT=HEAD
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	hack/make.sh binary
+
+FROM dev AS build-dynbinary
+ARG DOCKER_GITCOMMIT=HEAD
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	hack/make.sh dynbinary
+
+FROM dev AS build-cross
+ARG DOCKER_GITCOMMIT=HEAD
+ARG DOCKER_CROSSPLATFORMS=""
+RUN --mount=type=cache,target=/root/.cache/go-build \
+	hack/make.sh cross
+
+FROM scratch AS binary
+COPY --from=build-binary /go/src/github.com/docker/docker/bundles/ /
+
+FROM scratch AS dynbinary
+COPY --from=build-dynbinary /go/src/github.com/docker/docker/bundles/ /
+
+FROM scratch AS cross
+COPY --from=build-cross /go/src/github.com/docker/docker/bundles/ /
 
 FROM dev AS final
-# Upload docker source
-COPY . /go/src/github.com/docker/docker

+ 14 - 0
Dockerfile.buildx

@@ -0,0 +1,14 @@
+ARG GO_VERSION=1.12.10
+FROM golang:${GO_VERSION}-stretch
+ARG BUILDX_REPO=https://github.com/docker/buildx.git
+RUN git clone "${BUILDX_REPO}" /buildx
+WORKDIR /buildx
+ARG BUILDX_COMMIT=master
+RUN git fetch origin "${BUILDX_COMMIT}":build && git checkout build
+RUN go mod download
+ARG GOOS
+ARG GOARCH
+# Keep these essentially no-op var settings for debug purposes.
+# It allows us to see what the GOOS/GOARCH that's being built for is.
+RUN GOOS=${GOOS} GOARCH=${GOARCH} go build -ldflags '-X github.com/docker/buildx/version.Version=${BUILDX_COMMIT} -X github.com/docker/buildx/version.Revision=${BUILDX_COMMIT} -X github.com/docker/buildx/version.Package=github.com/docker/buildx' -o /usr/bin/buildx ./cmd/buildx
+ENTRYPOINT ["/usr/bin/buildx"]

+ 12 - 4
Jenkinsfile

@@ -392,7 +392,8 @@ pipeline {
                         stage("Build dev image") {
                             steps {
                                 sh '''
-                                docker build --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} .
+                                make bundles/buildx
+                                bundles/buildx build --load --force-rm --build-arg APT_MIRROR=${APT_MIRROR} -t docker:${GIT_COMMIT} .
                                 '''
                             }
                         }
@@ -500,7 +501,8 @@ pipeline {
                         stage("Build dev image") {
                             steps {
                                 sh '''
-                                docker build --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} .
+                                make bundles/buildx
+                                bundles/buidx build --load --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} .
                                 '''
                             }
                         }
@@ -583,7 +585,10 @@ pipeline {
                         }
                         stage("Build dev image") {
                             steps {
-                                sh 'docker build --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} .'
+                                sh '''
+                                make bundles/buildx
+                                bundles/buildx build --load --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} .
+                                '''
                             }
                         }
                         stage("Unit tests") {
@@ -689,7 +694,10 @@ pipeline {
                         }
                         stage("Build dev image") {
                             steps {
-                                sh 'docker build --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} .'
+                                sh '''
+                                make bundles/buildx
+                                bundles/buildx build --load --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} .
+                                '''
                             }
                         }
                         stage("Integration-cli tests") {

+ 47 - 13
Makefile

@@ -1,5 +1,15 @@
 .PHONY: all binary dynbinary build cross help install manpages run shell test test-docker-py test-integration test-unit validate win
 
+ifdef USE_BUILDX
+BUILDX ?= $(shell command -v buildx)
+BUILDX ?= $(shell command -v docker-buildx)
+DOCKER_BUILDX_CLI_PLUGIN_PATH ?= ~/.docker/cli-plugins/docker-buildx
+BUILDX ?= $(shell if [ -x "$(DOCKER_BUILDX_CLI_PLUGIN_PATH)" ]; then echo $(DOCKER_BUILDX_CLI_PLUGIN_PATH); fi)
+endif
+
+BUILDX ?= bundles/buildx
+DOCKER ?= docker
+
 # set the graph driver as the current graphdriver if not set
 DOCKER_GRAPHDRIVER := $(if $(DOCKER_GRAPHDRIVER),$(DOCKER_GRAPHDRIVER),$(shell docker info 2>&1 | grep "Storage Driver" | sed 's/.*: //'))
 export DOCKER_GRAPHDRIVER
@@ -107,7 +117,7 @@ GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g")
 DOCKER_IMAGE := docker-dev$(if $(GIT_BRANCH_CLEAN),:$(GIT_BRANCH_CLEAN))
 DOCKER_PORT_FORWARD := $(if $(DOCKER_PORT),-p "$(DOCKER_PORT)",)
 
-DOCKER_FLAGS := docker run --rm -i --privileged $(DOCKER_CONTAINER_NAME) $(DOCKER_ENVS) $(DOCKER_MOUNT) $(DOCKER_PORT_FORWARD)
+DOCKER_FLAGS := $(DOCKER) run --rm -i --privileged $(DOCKER_CONTAINER_NAME) $(DOCKER_ENVS) $(DOCKER_MOUNT) $(DOCKER_PORT_FORWARD)
 BUILD_APT_MIRROR := $(if $(DOCKER_BUILD_APT_MIRROR),--build-arg APT_MIRROR=$(DOCKER_BUILD_APT_MIRROR))
 export BUILD_APT_MIRROR
 
@@ -133,29 +143,30 @@ default: binary
 all: build ## validate all checks, build linux binaries, run all tests\ncross build non-linux binaries and generate archives
 	$(DOCKER_RUN_DOCKER) bash -c 'hack/validate/default && hack/make.sh'
 
+binary: DOCKER_BUILD_ARGS += --output=bundles/ --target=binary
 binary: build ## build the linux binaries
-	$(DOCKER_RUN_DOCKER) hack/make.sh binary
 
+dynbinary: DOCKER_BUILD_ARGS += --output=bundles/ --target=dynbinary
 dynbinary: build ## build the linux dynbinaries
-	$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary
-
-
 
+cross: DOCKER_BUILD_ARGS += --output=bundles/ --target=cross --build-arg DOCKER_CROSSPLATFORMS=$(DOCKER_CROSSPLATFORMS)
 cross: DOCKER_CROSS := true
 cross: build ## cross build the binaries for darwin, freebsd and\nwindows
-	$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross
 
 ifdef DOCKER_CROSSPLATFORMS
 build: DOCKER_CROSS := true
 endif
-ifeq ($(BIND_DIR), .)
-build: DOCKER_BUILD_OPTS += --target=dev
-endif
 build: DOCKER_BUILD_ARGS += --build-arg=CROSS=$(DOCKER_CROSS)
-build: DOCKER_BUILDKIT ?= 1
+ifdef GO_VERSION
+build: DOCKER_BUILD_ARGS += --build-arg=GO_VERSION=$(GO_VERSION)
+endif
+ifdef USE_BUILDX
+build: bundles buildx 
+	$(BUILDX) build ${BUILD_APT_MIRROR} ${DOCKER_BUILD_ARGS} ${DOCKER_BUILD_OPTS} -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" $(BUILDX_BUILD_EXTRA_OPTS) .
+else
 build: bundles
-	$(warning The docker client CLI has moved to github.com/docker/cli. For a dev-test cycle involving the CLI, run:${\n} DOCKER_CLI_PATH=/host/path/to/cli/binary make shell ${\n} then change the cli and compile into a binary at the same location.${\n})
-	DOCKER_BUILDKIT="${DOCKER_BUILDKIT}" docker build --build-arg=GO_VERSION ${BUILD_APT_MIRROR} ${DOCKER_BUILD_ARGS} ${DOCKER_BUILD_OPTS} -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" .
+	$(DOCKER) build ${BUILD_APT_MIRROR} ${DOCKER_BUILD_ARGS} ${DOCKER_BUILD_OPTS} -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" .
+endif
 
 bundles:
 	mkdir bundles
@@ -176,7 +187,9 @@ install: ## install the linux binaries
 run: build ## run the docker daemon in a container
 	$(DOCKER_RUN_DOCKER) sh -c "KEEPBUNDLE=1 hack/make.sh install-binary run"
 
-shell: build ## start a shell inside the build env
+shell: DOCKER_BUILD_ARGS += --target=dev
+shell: BUILDX_BUILD_EXTRA_OPTS += --load
+shell: build  ## start a shell inside the build env
 	$(DOCKER_RUN_DOCKER) bash
 
 test: build test-unit ## run the unit, integration and docker-py tests
@@ -222,3 +235,24 @@ swagger-docs: ## preview the API documentation
 		-e 'REDOC_OPTIONS=hide-hostname="true" lazy-rendering' \
 		-p $(SWAGGER_DOCS_PORT):80 \
 		bfirsh/redoc:1.6.2
+
+.PHONY: buildx
+ifeq ($(BUILDX), bundles/buildx)
+buildx: bundles/buildx # build buildx cli tool
+else
+buildx:
+endif
+
+bundles/buildx: BUILDX_DOCKERFILE ?= Dockerfile.buildx
+bundles/buildx: BUILDX_COMMIT ?= v0.3.0
+bundles/buildx: bundles ## build buildx CLI tool
+	# This intetionally is not using the `--output` flag from the docker CLI which is a buildkit option
+	# The idea here being that if buildx is being used, it's because buildkit is not supported natively
+	docker build -f $(BUILDX_DOCKERFILE) -t "moby-buildx:$(BUILDX_COMMIT)" \
+		--build-arg BUILDX_COMMIT \
+		--build-arg BUILDX_REPO \
+		--build-arg GOOS=$$(if [ -n "$(GOOS)" ]; then echo $(GOOS); else go env GOHOSTOS || uname | awk '{print tolower($$0)}' || true; fi) \
+		--build-arg GOARCH=$$(if [ -n "$(GOARCH)" ]; then echo $(GOARCH); else go env GOHOSTARCH || true; fi) \
+		. && \
+		id=$$(docker create moby-buildx:$(BUILDX_COMMIT)); \
+		if [ -n "$${id}" ]; then docker cp $${id}:/usr/bin/buildx $@ && touch $@; docker rm -f $${id}; fi