Hey Everyone, motivation for this blog was failing to answer this question in an interview
Highlights
Using a lightweight base image
Analyze the Dockerfile and try to execute a command in a single line which is repeated again and again that is using a multi-line command
Use a .dockerignore file to avoid taking into account those files which are not required for building an image
So starting with what is dockerfile and how to make one I would highly suggest you the read the following blog
https://devopscube.com/build-docker-image/
Let's get into the best Dockerfile practices and how to optimize them after learning how to create Docker images from Dockerfile.
Let's get started with a sample DockerFile.
Initial Dockerfile
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y openssh-server
RUN apt-get install -y vim
RUN apt-get install -y gcompat;exit 0
WORKDIR /app-files
#Set env variable of ADMIN_USER.
# In future, the value will be retreived from Vault.
ENV ADMIN_USER="appadmin"
#Add Value of ENV Variable to a file.
RUN echo $ADMIN_USER > ./user-creds.txt
#Unset the ENV Variable after value added to file.
RUN unset ADMIN_USER
#Copy The App contents stored in custom-app folder to app-files.
#The txt files in custom-app can be ignored/skipped.
COPY custom-app /app-files
EXPOSE 8080
# Start app binary named custom.
CMD ["/app-files/custom"]
Giving some context to the Dockerfile, we have a custom-app folder, which has a binary
We understand how the Dockerfile works; it starts with a base image, in this case, Ubuntu:latest, and then each line of the DockerFile acts as a layer.
The apt-get command is used to install the packages and it will automatically update the package list if any new versions are available.
The next step is to set up a user account for appadmin with password "appadmin".
This can be done by running echo $ADMIN_USER > ./user-creds.txt .
After this, unset ADMIN_USER so that no one else can use this account in future steps of the code.
Then Copy the custom-app files in the /app-file directory
Next, expose it through port 8080
Start the application binary executing /app-files/custom
Use this Dockerfile to create an image and record the time it takes using the time -p command.
Building the initial dockerimage
This took over 12 steps.
The size of the Docker image created is 439 MB.
After creating the image, run the following command to run the container using this image:
docker run -d build-app
Analyzing initial Dockerfile
Let's analyse the Dockerfile.
1) The base image used here is Ubuntu; we can change this to Alpine, which will certainly make this lightweight.
Since we are using Alpine, we have to use apk instead of apt-get, and there will be a small change in the package names as well. We can check the exact package names in the Alpine documentation.
https://docs.alpinelinux.org/user-handbook/0.1a/index.html
2) The next step would be instead of using RUN again and again, we can combine all three commands. This would reduce the number of layers that we are adding, which will certainly optimize our Dockerfile.
This is how our updated Dockerfile would look like:
FROM alpine:latest
#RUN apt-get update
#RUN apt-get install -y openssh-server
#RUN apt-get install -y vim
#RUN apt-get install -y gcompat;exit 0
RUN apk add openssh vim gcompat;exit 0
WORKDIR /app-files
#Set env variable of ADMIN_USER.
# In future, the value will be retreived from Vault.
#ENV ADMIN_USER="appadmin"
#Add Value of ENV Variable to a file.
#RUN echo $ADMIN_USER > ./user-creds.txt
#Unset the ENV Variable after value added to file.
#RUN unset ADMIN_USER
RUN export ADMIN_USER="appadmin" \
&& echo $ADMIN_USER > ./user-creds.txt
&& unset ADMIN_USER
#Copy The App contents stored in custom-app folder to app-files.
#The txt files in custom-app can be ignored/skipped.
COPY custom-app /app-files
EXPOSE 8080
# Start app binary named custom.
CMD ["/app-files/custom"]
Now have a look at the end of the file; there is a comment that says the.txt files are not part of the build and we can ignore those as blindly adding them to the Docker image would only increase our size and certainly not be an optimization practice. To resolve this, we use the .dockerignore file.
3) Create a .dockerignore file and include the following code in it:
custom-app/*.txt
This will not take into account the text files while building the image.
Now that we have optimized the Dockerfile, let us build the file and have a look at the size and time it takes to build the image.
Comparing initial and updated images
The updated image took 7 steps instead of 12.
Now let's compare the size of the Docker image.
Just 50.1MB, which is significantly less than the original image size of 439MB.
Run the container using the new image.
This confirms that we have successfully deployed applications using the best Dockerfile practices.