Reputation: 2907
Some context:
Docker
newbie (on it since 1 day),VM
running linux/AMD
and I own a M1 Mac (ARM
),For building my container for prod
, being on a M1 Mac, I have the below Dockerfile
:
See the --platform=linux/amd64
arg in FROM
and It works (= I'm able to deploy).
FROM --platform=linux/amd64 python:3.10-slim-bullseye
WORKDIR /usr/src/app
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
ENV FLASK_ENV=development
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["flask", "run"]
However, how can I say in my Dockerfile
to not use --platform=linux/amd64
if I want to build for local development?
I've seen some post on SO with conditions in dockerfile
but only for RUN
command.
Any idea or best practices?
Thanks.
Upvotes: 11
Views: 23155
Reputation: 3293
I spent a lot of time researching this myself - quite frustrated that builds on my Mac were taking so long.
tonistiigi/xx
in a multi-stage build if you want fast build times and no arch-specific settings.buildx
QEMUIf you have a "regular" Dockerfile - i.e. you don't have a --platform
statement in there or anything architecture-specific, you can probably just do:
docker buildx build -f Dockerfile --platform linux/amd64 --tag my-container:latest --load .
This will use QEMU - but it is slow and it sometimes breaks (compiler switches, etc). That's because the build itself is run in an emulator (in this case emulating amd64
)
If you had a Dockerfile like this:
FROM golang:1.20
ADD app /app
ENV CGO_ENABLED=0
RUN cd /app && go build -o app .
WORKDIR /app
ENTRYPOINT [ "/app/app" ]
Make it this:
FROM --platform=$BUILDPLATFORM golang:1.20 AS build
# get gcc cross toolchain
RUN DEBIAN_FRONTEND=noninteractive apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install g++-x86-64-linux-gnu libc6-dev-amd64-cross
RUN DEBIAN_FRONTEND=noninteractive apt-get -y clean
ADD app /app
ARG TARGETOS TARGETARCH
ENV CGO_ENABLED=1
WORKDIR /app
# RUN gcc -dumpmachine with --progress=plain here to see what gcc is used by default
RUN CC=x86_64-linux-gnu-gcc GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o app .
ENTRYPOINT [ "/app/app" ]
Notice we have to install a specific cross toolchain for the architecture we need. This is not ideal, because it makes the Dockerfile arch-specific. :/
Instead we can use this tonistiigi/xx
package as a base layer. It provides tools which make cross builds way easier and still running native.
FROM --platform=$BUILDPLATFORM tonistiigi/xx AS xx
FROM --platform=$BUILDPLATFORM golang:1.20
RUN DEBIAN_FRONTEND=noninteractive apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get -y install build-essential
RUN DEBIAN_FRONTEND=noninteractive apt-get -y clean
COPY --from=xx / /
ADD app /app
ARG TARGETPLATFORM
RUN xx-apt install -y libc6-dev gcc
ENV CGO_ENABLED=1
WORKDIR /app
RUN xx-go --wrap
RUN go build -o app . && xx-verify app
ENTRYPOINT [ "/app/app" ]
The xx-
tools provide wrappers which understand the Docker multiplatform variables and then use the right cross toolchain to build the correct TARGETARCH
architecture package.
This is the best way - it produce fast native, cross builds regardless of where you run the docker build buildx
command. And it should work on Docker Desktop or docker installed on Linux as well - aarch64
or amd64
alike.
The same build command:
docker buildx build \
-f Dockerfile \
--platform linux/amd64 \
--tag my-container:latest \
--load .
is used for all three methods.
To the OP - this means they could run the command on Docker Desktop (MacOS side) or using docker installed in their Linux VM.
Check out my blog post:
https://www.izumanetworks.com/blog/build-docker-on-apple-m/
for more details.
Upvotes: 1
Reputation: 1309
I would either
docker buildx build --platform=linux/arm64 -t myTag .
, orDockerfile.intel
, and Dockerfile
), with the respective platform defined in them. This way, any build pipeline will pick Dockerfile
as default, but for local build, you can specify which one to use, like so: docker build -f Dockerfile.intel .
To run your image as a dev conteiner, specify the image in a devcontainer.json
, and start + attach by typing Reopen in Container
in the command palette in VSCode.
Upvotes: 1
Reputation: 156
I believe you can use the --platform
parameter on docker buildx build or docker build to set platform(s) to build the image which will be used within any FROM
calls within the Dockerfile
if nothing else is specified (see Dockerfile FROM), as mentioned in the documentation.
You can then use the TARGETPLATFORM variable within your Dockerfile
to get what platform it's being built for if needed. If you want to change the default platform to build for, you can set the DOCKER_DEFAULT_PLATFORM environment variable.
Upvotes: 14