using shared dockerfile for multiple dockerfiles

Viewed 433

What I have, are multi similar and simple dockerfiles

But what I want is to have a single base dockerfile and my dockerfiles pass their variables into it.

In my case the only difference between dockerfiles are simply their EXPOSE, so I think it's better to keep a base dockerfile and other dockerfiles only inject that variables into base dockerfile like a template engine

A sample dockerfile:

FROM golang:1.17 AS builder
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o /bin/app ./cmd/root.go

FROM alpine:latest
WORKDIR /bin/
COPY --from=builder /bin/app .
EXPOSE 8080
LABEL org.opencontainers.image.source="https://github.com/mohammadne/bookman-auth"
ENTRYPOINT ["/bin/app"]
CMD ["server", "--env=dev"]
4 Answers

IMPORT directive will never be implemented

A long time ago there was proposed IMPORT directive for Docker

Unfortunately, issues are closed while PR's are still open:

Solution for your case

But for your case, all you need - is just a bit of sed

E.g.:

# Case1: inplace templating
EXPOSED_PORT=8081 sed -i "s/EXPOSE 8080/EXPOSE $EXPOSED_PORT/" Dockerfile

# Case2: generating Dockerfile from template
sed "s/EXPOSE 8080/EXPOSE $EXPOSED_PORT/" Dockerfile.template > Dockerfile

Explanation:

  • EXPOSED_PORT=8081 declares local bash variable
  • sed is a tool for text manipulation
  • sed -i "s/EXPOSE 8080/EXPOSE $EXPOSED_PORT/" Dockerfile replaces EXPOSE 8080 to EXPOSE 8081
  • sed "s/EXPOSE 8080/EXPOSE $EXPOSED_PORT/" Dockerfile.template > Dockerfile generates the new Dockerfile from Dockerfile.template

You can have the exposed port as a build argument for the image. If you make a dockerfile like this

FROM alpine:latest
ARG EXPOSED_PORT
EXPOSE ${EXPOSED_PORT}

You can specify which port is exposed when you build it by passing the port number like this

docker build -t myimage --build-arg EXPOSED_PORT=8000 .

It's worth noting that the EXPOSE statement mainly is documentation. Your image will work the same without the EXPOSE statement.

You have a very good idea. I have two thoughts:

  1. Create the base dockerfile as you described and then build it like this: docker image build tag base. Then you can have two liner Dockerfiles that look like FROM base \n EXPOSE 8080
  2. Alternatively you can have a single Dockerfile and just change the way you run the containers. There is an expose switch. Just about anything you specify in the Dockerfile can be overriden in the run command.

as yasen said, it's impossible to have import directive.

finally what I have did is as follow: link to github repository

  1. create a template text file with EXPOSE ${{ EXPOSED_PORT }}:
FROM golang:1.17 AS builder

WORKDIR /app

COPY . .

RUN go mod download && make ent-generate

RUN go build -o /bin/app ./cmd/root.go

FROM alpine:latest

WORKDIR /bin/

COPY --from=builder /bin/app .

EXPOSE ${{ EXPOSED_PORT }}

LABEL org.opencontainers.image.source="https://github.com/mohammadne/bookman-library"

ENTRYPOINT ["/bin/app"]

CMD ["server", "--env=dev"]

  1. and then create a python script
#!/usr/bin/python
from shutil import copyfile
import os


class Config:
    def __init__(self, service, port):
        self.service = service
        self.port = port


configs = [
    Config("auth", "8080"),
    Config("user", "8081"),
    Config("library", "8082"),
]

pathToDir = "../build"
template = f"{pathToDir}/template.txt"

for config in configs:
    outputDir = f"{pathToDir}/{config.service}"
    os.mkdir(outputDir)

    fileName = copyfile(template, f"{outputDir}/Dockerfile")

    with open(fileName, "rt") as file:
        replacedText = file.read().replace('${{ EXPOSED_PORT }}', config.port)

    with open(fileName, "wt") as file:
        file.write(replacedText)

then in the python script you can replace your patterns !!!

Related