A Dockerfile is a basic text file. Within the dockerfile you will include instructions to build a docker image. Do not worry, we will cover these instructions below. Basically, think of this as a simple way to build a container instead of having to do all these on the command line.
You can use a Dockerfile to create a container and then you can share your Docker Images on Docker Hub (this is a container sharing services provided by Docker).
Let us look at the typical instructions that a Dockerfile will contain. We are not going to cover every instruction that a Dockerfile can contain, but we are covering enough that you'll be able to understand Dockerfiles and continue to enhance your skills.
All you need is a basic text editor.
I would suggest downloading and using Visual Studio Code. This is able to run on Mac and Windows with ease. I also like the fact that you can configure it to do syntax highlighting with Dockerfiles! Watch the video to see how configure the syntax highlighting.
Dockerfiles are pretty amazing at what they do. With using a Dockerfile you are automatically building an image from the instructions that are included within the file.
Below you will find the Dockerfile instructions, explanations and examples.
FROM sets the base image of the container.
If you wished to use Alpine Linux version 3.16.1 you would use the following:
FROM alpine:3.16.1
If you wished to always use the newest/latest version you would use the following:
FROM alpine:latest
In the above examples 3.16.1 and latest are called tags. If a tag is not provided it will automatically use latest.
The LABEL instruction is used to provide metadata information of the image. When using the LABEL instruction you must provide this in a key-value pair.
LABEL website="ics131.leewardics.rocks"
This creates a key called website with a value of ics131.leewardics.rocks.
You could also create a LABEL called desc that provides a description of the container.
LABEL desc="This is an example of how to create a label"
The RUN instruction executes the provided command on the base image (this is what is declared in FROM). You can provide multiple run instructions with a Dockerfile.
Example:
RUN apk add nginx
RUN mkdir -p /run/ngnix
RUN mkdir /www
The example provides three RUN commands. The first RUN command uses the alpine Linux package manager (apk) to install nginx (this is a webserver). The next RUN instruction creates the directory structure of /run/nginx using the Linux command mkdir. The last RUN instructions creates the /www directory. I know the example uses Linux commands and that you might not be familiar with this OS. I hope the explanation of what the commands do and the provided links help with understanding. Recall, when you use the RUN instruction that this is done on the container.
The CMD instruction is used to start a command on the container when it first starts up. This instruction can take a list of parameters.
CMD has multiple syntax.
The first one is the preferred form:
CMD ["executable", "param1", "param2",...,"paramX"]
The second one is for using Linux shell commands:
CMD command param1 param2 ... paramX
There should only be one CMD command in your Dockerfile. If you do use the CMD instruction multiple times only the last one is executed.
Here is an example using the echo command and then pushing that output to the wc command.
CMD echo "This is a test." | wc -
You could test this out using the following in a Dockerfile:
FROM ubuntu:latest
CMD echo "This is a test." | wc -
Keep in mind that this can be changed on the command line when the container is launched.
When the EXPOSE instruction is used it will open access to a port on the container. The container will then list on this network post. This is needed if we are running a webserver.
This will expose port 8080.
EXPOSE 8080
Even though we are exposing the port on the container we will not be able to access it on our host machine (the one running the container). We will need to do a port mapping to be able to do this. Don't worry, that is coming soon after we learn how to build a container using the Dockerfile.
A simple way to provide details about who the maintainer/created the docker image is.
You might come across this, but it has been deprecated (no longer used). Instead of using MAINTAINER you should use a LABEL.
MAINTAINER someone@gmail.com
The ENV instruction provides a way to create an environmental variable.
The syntax of ENV is as follows:
ENV key value
If we wanted to create an ENV variable that contains my name and another one that contains course info we can do the following:
ENV name="Pete Gross"
ENV course="ICS131"
We can then use these ENV variables by prepending a $.
$name
$course
Of course the above code snippet wouldn't work as we are not doing anything with the variables, but I just wanted to show how you can access them.
If you wanted we could use CMD instruction:
CMD echo, $name, $course
You use COPY to copy files from your local machine to the container. You might want to do this to add files for a webserver to display or add a configuration file to the container or even copy custom code over to run.
To COPY all your files in the same directory that your Dockerfile is in to a directory called /www you'd do the following:
COPY . /www
The ADD instruction does the same as COPY, but provides more features. If the file added is a gunzip tarball file (this is like a zip file) it will also extract the file. You can also provide a URL and it will download it! Though, if the URL is a tar file it will not also extract it.
ADD mycode.tar.gz /code
ADD http://www.somewebsite.com/myfile.png /usr/local/src/
The first ADD instruction would add the mycode.tar.gz to the /code directory and extract the contents of the file.
The second ADD instruction would download the provided file to the /usr/local/src directory.
It works similar to the CMD instruction, except you are not able to change/overwrite this instruction.
If you wished to launch a java jar app you'd do the following:
ENTRYPOINT["java", "-jar", "someApp.jar"]
Using the ENTRYPOINT command makes sure that the containers main command is always started and not able to be manipulated.
The USER instruction sets the username and group to use within the container. You can use this as needed in your Dockerfile to change users as needed.
Here is a simple Dockerfile that uses the latest version of fedora Linux. We switch to the root user (this is the admin user in Linux) and then install nginx. After we are done we switch back to user1 (we are assuming this user exists, if not you'd need to create it). Doing this would make sure that when the container is launched that the root user is not the default user.
FROM fedora:latest
USER root
RUN dnf install -y nginx
USER user1
This sets the working directory within the container. If the directory does not exist it will be created.
This would create the /project directory if it does not exist and any further command would be run inside the /project directory.
WORKDIR /project
Now that we understand the parts of a Dockerfile, we now need to learn the next step of building them. This is actually a very simple command
docker build -t myImage .
On our command line we issue the above command. Notice the dot at the end. That says to look for a Dockerfile in the current directory and build it. If you don't include that docker won't know what to do. Also, the -t is how you name and tag your container. This would just create a name called myImage. We could change it and tag it if we'd like.
docker build -t myImage:v1 .
A .dockerignore file provides you the ability to list files and/or directories that you do not want to include while building the container image. This helps to reduce the size of the image and also speeds up the build process. Just think if you add this in a Dockerfile:
WORKDIR /myapp
COPY . .
This would copy all the files and subdirectories that are in your current working directory. This could cause a lot of extra data to be copied to your image. So, how do you create a .dockerignore file? Easy! It is just a simple text file that contains a list of all files and folders:
Example:
#ignore logs direcotry
logs
#A password file
passwords.txt
#Ignore all .java files
*.java
When you create the text file make sure you save it as .dockerignore in the same directory as your Dockerfile.
After you have built your container you will need to start it. This is done the same way as previously covered using docker run.
The main difference is that we would need to map the port the container is running to a port on our local network. This can be done using the -p option and your local network port comes first and the port the container uses is second. In the example below the container users port 80 and we'd be able to test this by using port 8888 on our system. I also like to provide the --name option as I can specify the name of the container. If you don't do this the docker daemon will randomly pick a name for you.
docker run -d -p 8888:80 --name ics131 myImage
In the videos below I show how to create and build containers using Dockerfiles. Please make sure to watch as these provides great details that will help further your understanding. I highly suggest making each video full screen to get a better understanding and be able to follow along a lot easier.
This one's a bit more challenging as it involves running Python code as a service and connecting to it. I show how the CMD option is used to launch the python code and how to connect. For this demo I am not going to provide you with the Dockerfile or code as I want you to gain some experience with creating both items. Just pause the video and copy what you see.
Saving the coolest one for last. In this demo I create a container that uses the ApacheBench software. This is a tool for benchmarking your HTTP server and can show how many requests per second the server can handle. Pretty cool tool to have as a container.