Simple Definition and Reference
I could not have started this dockerfile tutorial in any better way, than referring to a very simple definition stated by docker documentation itself. Please make sure you have already gone through our docker tutorial before starting with this section.
Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.
When do I create dockerfile?
Whenever you want to containerize your application, i.e. you have your application ready, running locally in your IDE or command line, now you want to create an image and run it in a container. This is the time when you will need to create a dockerfile. It will package your dependencies with the code and also include command to run once you run your application as a container. We will explain all these concepts later in the sections below.
dockerfile enables you with automating creation of docker images, i.e. you just need to run a command and your image will be created, containerizing your application.
Containerize your app
Simple steps to containerize an app are as below. It simply means, if you have an application, how can you containerize it.. how can you build it into a docker image and use that image to run a container from.
- Start with your app code and dependencies
- Create a dockerfile that describes your app, dependencies and how to run it
- Feed the dockerfile into docker image build
- Push the new image to a registry
- Run container from the image
Dockerfile in detail
As mentioned, it is a configuration file used to build your own custom docker images. It specifies how our application is built, what different program it should have and what happens when it starts up as container.
We pass it to docker client, which then passes it to docker server and builds it into a usable image, which is used to start a container
A Dockerfile can be explained in four simple steps:
- Specify a base image
- Run commands to install additional programs
- Copy external files into the image
- Specify commands to run on container startup.
Before moving forward, let’s look at an example Dockerfile, which will install Redis on an Linux alpine image. Don’t be too concerned, if you don’t understand fully right now. We will explain all the commands and keywords used for creating Dockerfile in the later section.
1. dockerfile to install redis on alpine linux
FROM alpine
RUN apk add –update redis
CMD [“redis-server”]
2. Explain the file above
FROM – base image to use. If i give you a laptop with no OS and tell you to install chrome.. Your first step will be to install OS.. that is what FROM is.
RUN – execute some commands on our base image
CMD – specifies what should be executed when our image is used to start up a brand new container
3. How to execute this file and build an image
Build command is what we use to take a Dockerfile and build an image.
From the location where you have the Dockerfile above, execute the command in docker client/cli:
docker build .
Output of the command above is a docker id. “Successfully build <id>”
“-f” flag will let you specify the location of Dockerfile and it can be any name. e.g. “docker build . -f /usr/ops/Dockerfile”
NOTE: “.” is known as the build context. Directory containing the application and dependencies is referred to as the build context. Best practise is to keep the Dockerfile in the root directory of the build context.
4. How to run a container from the image built above
Now that you have the image built in the step above, you can use the same command that you have been using to run other containers.
“docker run <id>”
5. What happens when we run the container from the image built in step above
Whenever we start the container using the image built above, a “redis server” is started. As, that is what was specified in the dockerfile above.
CMD [“redis-server”]
Whatever you specify in the CMD section, is what gets executed when you run the container.
Frequently Used Dockerfile Instructions
NOTE: Although Instructions explained below are case insensitive, but it is a good practise to have them all uppercase.
Instruction | Description |
---|---|
FROM | If i give you a laptop with no OS and tell you to install chrome.. Your first step will be to install OS.. that is what FROM is. A base layer of the image and the rest of the app will be added on top as additional layers. |
LABEL | simple key-value pairs and are an excellent way of adding custom metadata to an image. |
RUN | execute any commands to create a new layer on top of the current image |
COPY | copies new files or directories from source/local and adds them to the filesystem of the image at the path specified |
WORKDIR | set the working directory inside the image filesystem for the rest of the instructions in the file. If the folder is not there in the image, it will be created. |
EXPOSE | what ports the container listens to, at runtime. default protocol is TCP |
ENTRYPOINT | allows you to configure a container that will run as an executable |
CMD | sets the command to be executed when running a container from an image |
Build command is what we use to take a dockerfile and build an image. “.” is known as the build context. It is essentially set of files and folders that belong to our project, that we want to encapsulate or wrap inside our container
Push the image to docker hub
Step 1: login to docker hub.. “docker login”
Step 2: To push the image, Docker needs the following:
Registry
Repository
Tag
Command: docker image push opsandcloud/<image name>:<image tag>
opsandcloud is my docker id. (I use it when specifying docker login command). You need to prefix your docker id before pushing the image.
Docker is opinionated, so by default it pushes images to Docker Hub. You can push it to other registries, but you must explicitly set the registry URL as part of the docker image push command.
Container Port Mapping
command: docker run -p 80:5050 <image tag>
-p 80:8080 -> port incoming request from 80 to 5050 inside the container. maps port 80 on the docker host to port 8080 inside the container. This means that traffic hitting the port 80 will be redirected to port 8080 inside of the container.
Format: -p “host-port:container-port”
We specify port mapping in Dockerfile using “EXPOSE” instructions
There is no limitation on container reaching out.. Its only for requests coming in that we need to specify port mapping.
Efficient Dockerfile, Best Practices
1. For efficiency, you can Include multiple commands as single RUN instruction – glued together with && and \ line breaks
2. Multi-stage builds -> have a single dockerfile containing multiple FROM instructions. Each FROM instruction is a new build stage that can easily copy artifacts from previous stages.
3. “–no-cache=true” –> ignore the cache when building image. As soon as any instruction results in a cache-miss, the cache is no longer used for the rest of the entire build.
4. Use lightweight base images (e.g., alpine) -> image used in the “FROM” section should be lightweight.
5. Avoid hardcoding secrets -> make sure you are not having any secret or sensitive information in your dockerfile
6. No-install-recommends flag with apt-get-install – this makes sure that apt only installs main dependencies and not recommended or suggested packages.
7. –squash to squash the image built. This way it will only have single layer and can be performant when it is used to build other images. But, drawback being, squashed image do not share image layers
Important Pointers
1. New layers created using – FROM, RUN, COPY – if instructions adding content such as files and programs
2. Metadata – EXPOSE, WORKDIR, ENV, ENTRYPOINT – if adding instructions on how to build the image and run the application, it will create metadata
3. docker image history <image name>:<image tag> -> Each line of output corresponds to dockerfile