Score:0

How not to (almost) duplicate `Dockerfile` for "production" and "development" environments"?

us flag

I have a server app that is dockerised, but I cannot seem to pass a variable from any docker-compse.yaml's services to the Dockerfile as desired.

Thus I couldn't find any another solution than what follows...

I have two nearly identical Dockerfiles, one per each server environment:

# ./docker/production-server/Dockerfile
FROM rust:latest
WORKDIR /usr/src/rust-scrape-yt

ENV BUILD_FLAGS="--release"
ENV TEST_DOCKER="true"
ENV PRODUCTION="true"

COPY dummy.rs src/main.rs
COPY Cargo.toml .
COPY Cargo.lock .

RUN echo "Dummy build for deps, with flags: $BUILD_FLAGS"
RUN cargo build $BUILD_FLAGS

COPY . .

RUN cargo build $BUILD_FLAGS

And for the development container:

# ./docker/development-server/Dockerfile
FROM rust:latest
WORKDIR /usr/src/rust-scrape-yt

ENV BUILD_FLAGS=""
ENV TEST_DOCKER="true"
ENV PRODUCTION="false"

COPY dummy.rs src/main.rs
COPY Cargo.toml .
COPY Cargo.lock .

RUN echo "Dummy build for deps, with flags: $BUILD_FLAGS"
RUN cargo build $BUILD_FLAGS

COPY . .

RUN cargo build $BUILD_FLAGS

The only difference is that the production Dockerfile has:

ENV BUILD_FLAGS="--release"

While the development server image has:

ENV BUILD_FLAGS=""

COPY dummy.rs src/main.rs
COPY Cargo.toml .
COPY Cargo.lock .

RUN echo "Dummy build for deps, with flags: $BUILD_FLAGS"
RUN cargo build $BUILD_FLAGS

COPY . .

RUN cargo build $BUILD_FLAGS

My docker-compose.yaml is as follows:

version: "3.9"

services:

  production-db:
    image: postgres
    restart: always
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: thisisjustSommmePasWorttLaliloo
      POSTGRES_DB: rust-yt-scraper
    ports:
      - 15878:5432
    volumes:
      - production-rust-yt-scraper:/var/lib/postgresql/data

  development-db:
    image: postgres
    restart: always
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: thisisjustSommmePasWorttLalilooForDev
      POSTGRES_DB: rust-yt-scraper
    ports:
      - 15879:5432
    volumes:
      - development-rust-yt-scraper:/var/lib/postgresql/data

  production-server:
    depends_on:
      - production-db
    build:
      context: .
      dockerfile: docker/production-server/Dockerfile
    image: djfm/rust-yt-scraper-prod:latest
    restart: always
    environment:
      PRODUCTION: "true"
    
    ports:
      - "19850:8080"
    command: cargo run --release --bin server

  development-server:
    depends_on:
      - development-db
    build:
      context: .
      dockerfile: docker/development-server/Dockerfile
    image: djfm/rust-yt-scraper-dev:latest
    restart: always
    ports:
      - "19851:8080"
    command: cargo run --bin server
    environment:
      PRODUCTION: "false"

  server-tests:
    image: djfm/rust-yt-scraper:latest
    restart: never
    depends_on:
      - development-server
    environment:
      - PRODUCTION:false
      - BUILD_FLAGS:""
    command: cargo test

  adminer:
    image: adminer
    restart: always
    ports:
      - "9280:8080"

volumes:

  production-rust-yt-scraper:
    driver: local

  development-rust-yt-scraper:
    driver: local

I've tried messing with:

service:
  blah:
    build:
      context: .
      dockerfiles: ./conf/server.Dockerfile
      args:
        PRODUCTION:true

Is this hopeless? do I need to maintain separate Dockerfiles?

Score:0
hu flag

Hiho.

Please use only ONE Dockerfile for building the image until you have different packages installed.
Or better, i think, in this usecase are Multi-stage builds cause you are using rust and you won´t ship your sourcecode with the image.

For the ENV-variables you are going the right way, but you got typos. Will explain it below.

Just define the ENV variables you wanna have in your runtime image and fill them with default values (if needed).
For BUILD_FLAGS i would suggest to use ARG

Info:
ENV are persisted in the image afterwards as environment variables
ARG are only available during the build of the image and won´t be persistent in the image

I´ll skip some steps for readability...

Try to update your Dockerfile with multi-stages:

ARG BUILD_FLAGS="[YOUR DEFAULT FLAGS]"
FROM rust:latest as buildimage
ARG BUILD_FLAGS
ENV CARGO_TARGET_DIR=target
COPY dummy.rs src/main.rs
RUN cargo build $BUILD_FLAGS

In the same Dockerfile continue with:

FROM rust:latest
ARG BUILD_FLAGS
ENV PRODUCTION="false"
COPY --from=buildimage target/ /app/

So what happened:

  • The first image is used to compile your application and is named as buildimage
  • The second image will only copy the results from buildimage into a NEW image which will not have the main.rs included
  • The final image has no ENV-Variable BUILD_FLAGS cause we declared it as ARG
  • The final image has only the PRODUCTION environment variable set with a default of string "false"
  • ARGs before FROM are globally, ARGs after FROM are only locally in the current build. To takeover the defaults, redefine them
  • DryRun: I set the CARGO_TARGET_DIR to only "target" to get rid of target/release and target/debug folders

Note:
As i am no rust developer i can only guess where the compiled main file is located.
This is a complete dry run, but should solve your problem.

In combination with your compose file you´ll get two images afterwards.
One image with a production ready content
One image with debug symbols etc.

For your environment configuration in the compose file.
You can define environment variables in two ways:
Way one:

    environment:
      VAR_1: "value_1"
      VAR_2: "value_2"

Way two:

    environment:
      - VAR_1="value_1"
      - VAR_2="value_2"

I would prefer the first way to stay with the whole syntax

So your compose file would look something like:

version: "3.9"
services:
  production-db:
  [...]

  development-db:
  [...]

  production-server:
    depends_on:
      - production-db
    build:
      context: .
      dockerfile: docker/Dockerfile
      args:
        BUILD_FLAGS: "--release"
    image: djfm/rust-yt-scraper:latest
    restart: always
    environment:
      PRODUCTION: "true"
    ports:
      - "19850:8080"
    command: cargo run --release --bin server

  development-server:
    depends_on:
      - development-db
    build:
      context: .
      dockerfile: docker/Dockerfile
      args:
        BUILD_FLAGS: ""
    image: djfm/rust-yt-scraper-dev:latest
    restart: always
    environment:
      PRODUCTION: "false"
    ports:
      - "19851:8080"
    command: cargo run --bin server

  server-tests:
    image: djfm/rust-yt-scraper:latest
    restart: never
    depends_on:
      - development-server
    environment:
      PRODUCTION: "false"
    command: cargo test

  adminer:
    image: adminer
    restart: always
    ports:
      - "9280:8080"

volumes:

  production-rust-yt-scraper:
    driver: local

  development-rust-yt-scraper:
    driver: local

HTH

djfm avatar
us flag
thanks! sorry for the delay, I had other more prioritary projects unfortunately. The double ARG thing was what I was missing, highly counter-intuitive!
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.