Multiple FROMs - what it means Ask Question

Multiple FROMs - what it means Ask Question

I want to build a docker image, which requires both a Neo4j database and Node.js to run.

My first approach was to declare a base image for my image, containing Neo4j. The reference docs do not define "base image" in any helpful manner:

Base image: An image that has no parent is a base image

from which I read that I may only have a base image if that image has no base image itself.

But what is a base image? Does it mean, if I declare neo4j/neo4j in a FROM directive, that when my image is run the neo database will automatically run and be available within the container on port 7474?

Reading the Docker reference I see:

FROM can appear multiple times within a single Dockerfile in order to create multiple images. Simply make a note of the last image ID output by the commit before each new FROM command.

Do I want to create multiple images? It would seem what I want is to have a single image that contains the contents of other images e.g. neo4j and node.js.

I've found no directive to declare dependencies in the reference manual. Are there no dependencies like in RPM where in order to run my image the calling context must first install the images it needs?

ベストアンサー1

As of May 2017, multiple FROMs can be used in a single Dockerfile.
See "Builder pattern vs. Multi-stage builds in Docker" (by Alex Ellis) and PR 31257 by Tõnis Tiigi.

The general syntax involves adding FROM additional times within your Dockerfile - whichever is the last FROM statement is the final base image. To copy artifacts and outputs from intermediate images use COPY --from=<base_image_number>.

FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go    .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app    .
CMD ["./app"]  

The result would be two images, one for building, one with just the resulting app (much, much smaller)

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

multi               latest              bcbbf69a9b59        6 minutes ago       10.3MB  
golang              1.7.3               ef15416724f6        4 months ago        672MB  

what is a base image?

A set of files, plus EXPOSE'd ports, ENTRYPOINT and CMD.
You can add files and build a new image based on that base image, with a new Dockerfile starting with a FROM directive: the image mentioned after FROM is "the base image" for your new image.

does it mean that if I declare neo4j/neo4j in a FROM directive, that when my image is run the neo database will automatically run and be available within the container on port 7474?

Only if you don't overwrite CMD and ENTRYPOINT.
But the image in itself is enough: you would use a FROM neo4j/neo4j if you had to add files related to neo4j for your particular usage of neo4j.


2018: With the introduction of the --target option in docker build, you gain even more control over multi-stage builds.
This feature enables you to select which FROM statement in your Dockerfile you wish to build, allowing for more modular and efficient Docker images. This is especially useful in scenarios where you might want to:

  1. Build Only the Dependencies: Create an image that only contains the dependencies of your project. This can be useful for caching purposes or for environments where you only need to run tests or static analysis tools.

  2. Separate Build and Runtime Environments: Compile or build your application in a full-featured build environment but create a smaller, more secure image for deployment that only includes the runtime environment and the compiled application.

  3. Create Images for Different Environments: Have different stages for development, testing, and production environments, each tailored with the specific tools and configurations needed for those environments.

Example Using --target

Given a Dockerfile with multiple stages named builder, tester, and deployer, you can build up to the tester stage using the --target option like so:

docker build --target tester -t myapp-test .

This command tells Docker to stop building after the tester stage has been completed, thus creating an image that includes everything from the base image up to the tester stage, but excluding anything from deployer stage and beyond.

Dockerfile Example with --target Usage

# Builder stage
FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/example/project/
# Assume app.go exists and has a function
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

# Tester stage
FROM builder as tester
COPY . .
RUN go test ./...

# Deployer stage
FROM alpine:latest as deployer
COPY --from=builder /go/src/github.com/example/project/app /app
CMD ["/app"]

Using the --target option with this Dockerfile allows for flexibility in building images tailored for specific steps of the development lifecycle.

As illustrated in "Building a multi-stage Dockerfile with --target flag builds all stages instead of just the specified one", this works well with BuildKit, which is now (2023+) the default builder.
From that page, you have Igor Kulebyakin's answer:

If one wants to make sure that the current target stage is force re-built even if it has already been cached without rebuilding the previous dependent stages, once can use the docker build --no-cache-filter flag.

An example, given you have a multi-stage Dockerfile with a 'test' stage, would be:

docker build --no-cache-filter test --target test --tag your-image-name:version .

おすすめ記事