skip to Main Content

I am trying to create docker image for my Rust project. I want builder to run on native architecture for shorter compile time (I am using Apple M1) and runtime use linux/amd64 because this is what I use on my VPS.

As I read in documentation of Docker I should be able to provide platform as I do FROM --platform=linux/amd64 scratch AS runtime.

The Dockerfile:

###########
# Builder #
###########
FROM rust:1.68-slim as builder

# Preparation for cross-compilation
RUN apt update && apt-get install -y gcc-x86-64-linux-gnu
RUN rustup target add x86_64-unknown-linux-musl
ENV RUSTFLAGS='-C linker=x86_64-linux-gnu-gcc'
ENV CC_x86_64_unknown_linux_musl=x86_64-linux-gnu-gcc
WORKDIR /calar

# Copy only list of dependencies and make dummy main.rs
COPY Cargo.toml .
COPY Cargo.lock .
RUN mkdir src/
RUN echo 'fn main() { println!("You should not see this") }' > src/main.rs

# Build all dependecies without app itself, so they can be cached
RUN cargo build --target x86_64-unknown-linux-musl --release
RUN rm -f target/x86_64-unknown-linux-musl/release/deps/calar*

# Copy source code itself
ADD src src

# Build the binary
RUN cargo build --target x86_64-unknown-linux-musl --release


###########
# Runtime #
###########
FROM --platform=linux/amd64 scratch AS runtime

# Copy the binary from build container
COPY --from=builder /calar/target/x86_64-unknown-linux-musl/release/calar /

# Run the server!
CMD ["/calar", "server"]

But when I run docker build ., I get an image which is linux/arm64. The output of docker image inspect <>:

[
    {
        "Id": "sha256:cf0dbeef100806446e06750b6efb61c024ff71afd21f12ff190bbe165f35346e",
        "RepoTags": [
            "calar:latest"
        ],
        "RepoDigests": [],
        "Parent": "",
        "Comment": "buildkit.dockerfile.v0",
        "Created": "2023-04-16T13:16:10.070954304Z",
        "Container": "",
        "ContainerConfig": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": null,
            "Cmd": null,
            "Image": "",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null
        },
        "DockerVersion": "",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/calar",
                "server"
            ],
            "ArgsEscaped": true,
            "Image": "",
            "Volumes": null,
            "WorkingDir": "/",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null
        },
        "Architecture": "arm64",
        "Os": "linux",
        "Size": 7727216,
        "VirtualSize": 7727216,
        "GraphDriver": {
            "Data": {
                "MergedDir": "/var/lib/docker/overlay2/he0r34w53plj6u6fpiecbgxte/merged",
                "UpperDir": "/var/lib/docker/overlay2/he0r34w53plj6u6fpiecbgxte/diff",
                "WorkDir": "/var/lib/docker/overlay2/he0r34w53plj6u6fpiecbgxte/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:4590f083dd1c4e02bab97863aa00d8528921b8274ec9723876f30fb69ba5c395"
            ]
        },
        "Metadata": {
            "LastTagTime": "2023-04-16T13:16:10.106901346Z"
        }
    }
]

How can I change the resulting image architecture to be linux/amd64?


EDIT: A bit later I noticed something very interesting. I tried to use podman instead of Docker and it simply worked. podman build . produced linux/amd64 image. So maybe the problem is in Docker itself?

2

Answers


  1. Try using docker build —platform …. Docs here.

    The FROM reference docs say

    The optional –platform flag can be used to specify the platform of the image in case FROM references a multi-platform image. For example, linux/amd64, linux/arm64, or windows/amd64. By default, the target platform of the build request is used

    It doesn’t say that it will use the platform target of the last layer by default, so maybe your system’s default platform is overriding the last layer.

    Login or Signup to reply.
  2. I don’t want to do so, because in this case I would use linux/amd64 for builder as well, which is much slower on my machine.

    You can build with a different platform than the released stage using the FROM --platform syntax and builtin variables:

    ###########
    # Builder #
    ###########
    FROM --platform=$BUILDPLATFORM rust:1.68-slim as builder
    
    # Preparation for cross-compilation
    RUN apt update && apt-get install -y gcc-x86-64-linux-gnu
    RUN rustup target add x86_64-unknown-linux-musl
    ENV RUSTFLAGS='-C linker=x86_64-linux-gnu-gcc'
    ENV CC_x86_64_unknown_linux_musl=x86_64-linux-gnu-gcc
    WORKDIR /calar
    
    # Copy only list of dependencies and make dummy main.rs
    COPY Cargo.toml .
    COPY Cargo.lock .
    RUN mkdir src/
    RUN echo 'fn main() { println!("You should not see this") }' > src/main.rs
    
    # Build all dependecies without app itself, so they can be cached
    RUN cargo build --target x86_64-unknown-linux-musl --release
    RUN rm -f target/x86_64-unknown-linux-musl/release/deps/calar*
    
    # Copy source code itself
    ADD src src
    
    # Build the binary according to the TARGET platform variables
    ARG TARGETOS
    ARG TARGETARCH
    RUN 
        case "${TARGETARCH}" in; 
          amd64) arch=x86_64;; 
          *) echo "unsupported arch: ${TARGETARCH}" >&2; exit 1;; 
        esac 
     && cargo build --target ${arch}-unknown-${TARGETOS}-musl --release 
     && mkdir /release 
     && cp /calar/target/${arch}-unknown-${TARGETOS}-musl/release/calar /release/
    
    ###########
    # Runtime #
    ###########
    FROM scratch AS runtime
    
    # Copy the binary from build container
    COPY --from=builder /release/calar /
    
    # Run the server!
    CMD ["/calar", "server"]
    

    Then you can build with:

    docker build --platform=linux/amd64 -t ${your_image_name} .
    

    and the build stage runs on your local arch (of the builder) while the runtime stage (resulting image) is of the target platform.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search