Author : MD TAREQ HASSAN | Updated : 2023/07/19
What Is Dockerfile?
- The Dockerfile is a text file that contains the instructions needed to create a new container image. These instructions include:
- identification of an existing image to be used as a base
- commands to be run during the image creation process
- command that will run when new instances of the container image are deployed
- Docker build is the Docker engine command that consumes a Dockerfile and triggers the image creation process
- A file that contains instructions for building docker container from docker image
- Dockerfile defines what are the steps docker will take to build container from an image
- Containers are only layers upon layers of changes and each new command in (docker image) Dockerfile, will create a new layer in the container
- You can think of a dockerfile as a blueprint which contains all the commands, in order, needed to create an image of your application
- Docker images are created by running the docker build command against a dockerfile
Layers
- Layers of a Docker image are essentially just files generated from running some command
- Layers (also called intermediate images) are generated when commands in the Dockerfile are executed during the Docker image build
- Container has a writeable layer that stacks on top of the image layers. This writeable layer allows us to “make changes” to the container since the lower layers in the image are read-only
Docker builds a container from the Dockerfile (inside image), each step corresponds to a command run in the Dockerfile. And each layer is made up of the file generated from running that command.
Explanation
- All images (we make) are based on an existing image
- If we drill down the chain, at the end there will be base layer consist of runtime (i.e. .net core) or OS (i.e. alpine-linux)
- Each command in Dockerfile file creates a layer (on top of previous layer) in container
- https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/docker/building-net-docker-images?view=aspnetcore-5.0#the-dockerfile
We usually build a custom image for a container on top of a base image that we get from an official repository like the Docker Hub registry.
That is precisely what happens under the covers when you enable Docker support in Visual Studio.
Our Dockerfile will use an existing image i.e. mcr.microsoft.com/dotnet/aspnet:5.0
to build docker image.
Therefore, we need to specify what base Docker image we will use for our container.
We do that by adding FROM mcr.microsoft.com/dotnet/aspnet:5.0
to the Dockerfile.
Dockerfile example
FROM node:current-alpine
COPY . /usr/src/app/
WORKDIR /usr/src/app
RUN npm install && npm run build
EXPOSE 3000
ENTRYPOINT ["npm", "start"]
Explanation
FROM node:current-alpine
: getting alpine linux with nodejs installed in itCOPY . /usr/src/app/
: copying contents from current DIR to/usr/src/app/
dir in the container (directory will be created automatically)WORKDIR /usr/src/app
: changing our start directory to/usr/src/app
, so that things from now can be run from the root directory of the applicationRUN npm install && npm run build
: installing nodejs packages and building the application (in directory/usr/src/app
)EXPOSE 3000
: exposing port at which nodejs application will runENTRYPOINT ["npm", "start"]
: executing start command of node script (inpackage.json
file)
Multi-stage Builds
- Each instruction in the Dockerfile adds a layer to the image, and you need to remember to clean up any artifacts you don’t need before moving on to the next layer
- Multistage builds are useful to optimize Dockerfiles while keeping them easy to read and maintain
- With multi-stage builds, you use multiple FROM statements in your Dockerfile. Each FROM instruction can use a different base, and each of them begins a new stage of the build
- You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image
Multi-stage builds in Dockerfile
- The Dockerfile is similar to a batch script. Similar to what you would do if you had to set up the machine from the command line
- It starts with a base image that sets up the initial context, it’s like the startup filesystem, that sits on top of the host OS. It’s not an OS, but you can think of it like “the” OS inside the container
- The execution of every command line creates a new layer on the filesystem with the changes from the previous one, so that, when combined, produce the resulting filesystem
- Since every new layer “rests” on top of the previous one and the resulting image size increases with every command, images can get very large if they have to include, for example, the SDK needed to build and publish an application.This is where multi-stage builds get into the plot (from Docker 17.05 and higher) to do their magic
- The core idea is that you can separate the Dockerfile execution process in stages, where a stage is an initial image followed by one or more commands, and the last stage determines the final image size
- In short, multi-stage builds allow splitting the creation in different “phases” and then assemble the final image taking only the relevant directories from the intermediate stages. The general strategy to use this feature is:
- Use a base SDK image (doesn’t matter how large), with everything needed to build and publish the application to a folder and then
- Use a base, small, runtime-only image and copy the publishing folder from the previous stage to produce a small final image.
- Courtesy of above points: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/docker-application-development-process/docker-app-development-workflow#multi-stage-builds-in-dockerfile
Example
#
# Stage 1
#
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
#
# Stage 2
#
FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["DemoService/DemoService.csproj", "DemoService/"]
RUN dotnet restore "DemoService/DemoService.csproj"
COPY . .
WORKDIR "/src/DemoService"
RUN dotnet build "DemoService.csproj" -c Release -o /app/build
#
# Stage 3
#
FROM build AS publish
RUN dotnet publish "DemoService.csproj" -c Release -o /app/publish
#
# Stage 4
#
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "DemoService.dll"]
Explanations
- Stage 1
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
- Base image (for the stage) :
mcr.microsoft.com/dotnet/aspnet:3.1
- Stage name :
base
(FROM ... ... ... AS base
)
- Base image (for the stage) :
WORKDIR /app
: Create directory/app
in the imageEXPOSE 80
: exposing port 80 at which application will run
- Stage 2
FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
- Base image (for the stage) :
mcr.microsoft.com/dotnet/sdk:3.1
- Stage name :
build
- Base image (for the stage) :
WORKDIR /src
: Create directory/src
in the imageCOPY ["DemoService/DemoService.csproj", "DemoService/"]
: copy.csproj
to/src/DemoService/
RUN dotnet restore "DemoService/DemoService.csproj"
: execute restore command (restoringDemoService/DemoService.csproj
as distinct layer)COPY . .
: copy all from current project folder to/src
(set byWORKDIR
command before) folder in imageWORKDIR "/src/DemoService"
: Create directory/src/DemoService
in the imageRUN dotnet build "DemoService.csproj" -c Release -o /app/build
: built the project and output to/app/build
directory in the image
- Stage 3
FROM build AS publish
:- take ‘build’ (from previous stage) as base
- stage name:
publish
RUN dotnet publish "DemoService.csproj" -c Release -o /app/publish
: do release build and output to/app/publish
directory in the image
- Stage 4
- FROM base AS final
- take ‘base’ (from first stage) as base
- stage name :
final
WORKDIR /app
: Change the current directory to/app
(/app
-> from base stage output layer)COPY --from=publish /app/publish .
: Copy the/app/publish
directory from stage publish to the current directory (current directory ->WORKDIR /app
)ENTRYPOINT ["dotnet", "DemoService.dll"]
: Define the command to run when the container is started (dotnet command to start app)
- FROM base AS final
Example 2
FROM golang:1.16 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"]
Explanation:
- First FROM use ‘golang:1.16’ as base image and names the build satge as builder
- The second FROM instruction starts a new build stage with the alpine:latest image as its base.
- The COPY –from=builder line copies just the built artifact from the previous stage into this new stage. The Go SDK and any intermediate artifacts are left behind, and not saved in the final image.
Example 3
# https://hub.docker.com/_/microsoft-dotnet
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /source
# copy csproj and restore as distinct layers
COPY *.sln .
COPY aspnetapp/*.csproj ./aspnetapp/
RUN dotnet restore
# copy everything else and build app
COPY aspnetapp/. ./aspnetapp/
WORKDIR /source/aspnetapp
RUN dotnet publish -c release -o /app --no-restore
# final stage/image
FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
COPY --from=build /app ./
ENTRYPOINT ["dotnet", "aspnetapp.dll"]
Commands In Dockerfile
- https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#dockerfile-instructions
-
- Dockerfile command reference: https://docs.docker.com/engine/reference/builder/
Relation of Docker Commands With Image And Container
docker build --tag name:tag <Dockerfile-location>
: creates an docker image from Dockerfiledocker run -p hostPort:containerPort <docker-image-name>
: an image becomes a container and will start runningdocker history myapp
: will show all the layers that make up the image
Links
- Multi-stage Builds: https://docs.docker.com/develop/develop-images/multistage-build/
- https://www.freecodecamp.org/news/what-is-docker-used-for-a-docker-container-tutorial-for-beginners/
- https://jessicagreben.medium.com/digging-into-docker-layers-c22f948ed612
- https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-docker/manage-windows-dockerfile
- https://github.com/dotnet/dotnet-docker
- https://hub.docker.com/_/microsoft-dotnet
- https://gist.github.com/artisticcheese/e4776f420f1d0088d87335284b103cce
- https://hub.docker.com/_/microsoft-dotnet-aspnet