Docker: All Aboardddd!

Devin Moreland
All Things DevOps
Published in
10 min readJul 21, 2022

Building Docker Images and Dockerfiles

This is my first article on Docker, so I will be going through step-by-step how to install Docker on Mac and a couple of the requirements we will need to perform our project for today.

The purpose of this article is to help simply Docker some and to show you how to create your own Image using an NGINX image and then push this to Amazon ECR.

What is Docker

Per Wikipedia “Docker is a set of Platform as a Service(PaaS) products that use OS-level virtualization to deliver software in packages called containers”. Per me, Docker essentially is a Virtual Machine on your machine or cloud server that you can run applications on. It uses the resources that it needs and you just install the container and it will spin up an application in seconds. No installing updates or going to the CLI, unless you need to. You can run multiple containers on the same Docker platform, the only limitation will be the number of resources you have assigned to the Docker application. Think of Docker as a prebuilt ship and you are just loading the cargo containers onto it and Docker handles the rest.

What is a Container

Per Docker “A container is a standard unit of software that packages up code and all its dependencies so the application runs quickly and reliably from one computing environment to another. A Docker container image is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries, and settings”. After you use an Image to provision a container that image then becomes a container. Containers are like mini instances that you can run applications on. So for example on the same Docker application, you can have two containers one with Ubuntu for an OS and one with CentOS and they won't conflict.

What is an Image

A Docker Image contains the code, libraries, and dependencies that an application needs to run. You can almost think of it like a big prebuilt Bash script that has everything you need. When we use Docker we will use images, such as an NGINX image, and spin up a container with it.

Installing Docker Desktop

If head to the Docker website and create an account. This is a must when using docker. It's free.

On Mac, you will have to install an application called Docker Desktop for the Docker daemon to run so that you can run docker commands. This app will need to be open in the background at all times if you are running your commands from your machine vs if you were running Docker in a cloud environment. You can install Docker Desktop multiple ways on Mac, to see them all follow this link for installing Docker Desktop on Mac. The simplest is the command line.

$ sudo hdiutil attach Docker.dmg
$ sudo /Volumes/Docker/Docker.app/Contents/MacOS/install
$ sudo hdiutil detach /Volumes/Docker

Once installed you will have access to the Docker Desktop GUI and you will know Docker is running if you have the ship in the top bar.

Docker Desktop

Now that you have Docker Desktop we are ready to get into the command line and run some containers!

Testing out Docker

We are going to install an NGINX container and make sure it works and then we will destroy that container so we can do some real work! To install this container head to the terminal and run

docker container run -d --name nginx -p 80:80 nginx

We have some flags here we will explain.

  • -d means for our container to run detached, so basically, it runs in the background so we can use the terminal
  • --name is the name we are giving to our container
  • -p specifies the ports that our container will listen on, the first 80 is the port for our localhost, and the second is port 80 as in we listening for internet traffic on port 80.

Once you run this command it will spit out its container ID. You can run docker ps to view your running containers.

Docker ps

Next, we can go view our NGINX webserver, since we have used our local machine just go to the internet browser and type in localhost:80 and it will redirect yours to your webserver!

Now, let's delete everything so we have a clean slate to do our project with.

$ docker stop nginx #we put nginx here bc that is what we named it
$ docker rm nginx
$ docker image rm -f nginx

Pro Tip: You can use the Docker Desktop Gui to delete images, containers, and volumes.

Creating our own Image!

Let's create a custom image from the official NGINX image. To find this you can follow this link to Docker Hub and the official NGINX image. Back in the CLI run the following command. This will pull the NGINX image.

docker pull nginx

Now that this image is on our machine we can create a container and run it

docker container run -d --name nginx -p 80:80 nginx

Now that our container is up and running we will need to go into the container and modify or add some files. This way we will have a unique to us container that we can then make an image of and then use that image to launch NGINX webservers. Run the following command, this will allow us to have a CLI inside our container and use Bash to do things.

docker exec -it nginx bash
Inside our Container

This is what you will now see in the command line, you are inside your container. We are going to make a command that will tell us when we provision our container and it will send it to a file so that we can review when it was provisioned. I am going to do this using Python. so in the terminal run the following commands.

$ apt-get update -y
$ apt-get install python -y
$ apt install python3.9 -y
$ apt install vim -y

This will install Python on our container. Next we will add in our script.

  • vim date-time.py
  • Insert the code below into the file
  • Save the file, and change permissions of the file using chmod 744.
  • You can run the file to verify that it works, ./date-time.py since I have the output being sent to a new file named Creation we will cat it and see the information.
cat Creation
Time file in our Container

When creating Docker images we want to keep thier file size as small as we can so we need to remove some of the fat of our image. Lets remove vim now that we use it. It wont effect our script

apt remove vim -y && apt autoremove -y && apt clean

Now we can exit this container by the command exit.

To create an image of our own from this we will run the following commands, you will paste in your container ID where I have container ID.

$ docker ps. #gives our container ID
$ docker commit <containerID> nginx/nginx-devin

Run a docker image ls and it will show the images we now have. You will see the one we just created. Notice it is bigger in size, that's from installing python. I would try to trim it down more but for the rest of my project we will need it.

docker image ls
Docker Image List

Now that we have an image let's create a container from our image. Once created we will go to the URL, and we are going to make this open on port 8080. Notice the name change and the image change at the end, we also have to have the nginx/ at the beginning of our image.

$ docker container run -d --name nginx2 -p 8080:80 nginx/nginx-devin

Go to the webserver now using localhost:8080

Docker Container WebServer

Let's go back to the command line, ssh back into our container, and check the Creation file.

$ docker exec -it nginx2 bash
$ cat Creation

WAIT! That's not the correct time though! So what going on is my script isn't running on creation. Let's fix this by modifying the DockerFile that tells our image what to do. We are going to hop into the weeds for a bit.

Dockerfile

What we are going to do is modify the Dockerfile to make it run our script on start-up! This Dockerfile is similar to a BuildSpec or AppSpec file for CICD tools. It tells the container what to do on startup. Remember this is not mandatory, I am having to do this step to force this python script to run

  • Create a new folder on your local machine
  • Change Directory into this folder
  • Create a new file named DockerFile
  • Copy in the text below
FROM nginx:latestRUN apt-get update -y && apt-get install python3.9 -y && apt install python3.9 -y && apt install vim -yCOPY date-time.py date-time.pyRUN chmod 744 date-time.pyCMD ./date-time.py && nginx -g 'daemon off;'
  • Save it
  • Copy your python script to this same folder with a file name of date-time.py

The reason we have two commands in the CMD line is that there can only be one CMD line in a Dockerfile. since we are importing the nginx image which has a Dockerfile built-in we can leave CMD blank and it will use the CMD in the built-in Dockerfile. However, since we specified a command that CMD is overwritten by the new CMD that we specified. This is why I have included the “nginx -g ‘daemon off;’” because if we just used the ./date-time.py to run the script then the container will start up, execute the script, and shut down, so we must tell it to stay up using that extra command.

What this is doing is calling the NGINX Docker image, then copying our script into the Nginx container then changing the file permissions for us and running the following ./date-time.py on startup! Now we need to make our image use this newly modified Dockerfile, to do this we will actually create a new image by running the following. the “:v2” is tagging it so we can just change the “version” if something doesn't build right.

docker image build -t nginx-devin:v2 .

This builds a new image, and names it nginx-devin:v2. We can run a docker image ls and see our image.

New Docker Image

Now let's make a container with our new image and see if this corrected our date time issue. Go ahead and make the container and ssh in and see if this added our Creation file.

$ docker container run -d --name nginx3 -p 8080:80 nginx-devin:v2
$ docker exec -it nginx3 bash
$ cat Creation
Time file in Docker Container

Congrats! We have officially created multiple new images, modified data within a container and took the official image, and created a new image by modifying a Dockerfile to create an image that will run a python script that tells us when this container was run.

Push to AWS ECR

Pushing a file to AWS ECR so that we can save it is fairly easy, head to the AWS Console and create an ECR repository. Once there click view push commands and follow along with the instructions to push to ECR.

AWS ECR

Push to DockerHub

Just because I like you all and I feel like Docker Hub is easier to send to than AWS let's push our image to Docker Hub. Docker Hub is a repository where we can store our images and other people can come and use them if you let them. It will save them for use for any time in the future that we may need them.

Be aware that you may have to enter your Docker username and password when doing this for the first time.

To push to Docker Hub run the following, make sure to replace your username with your Docker user name.

$ docker image tag nginx-devin:v2 username/nginx-devin:v2
$ docker push username/nginx-devin:v2
  • The tag argument lets us declare a tag on our image, we will keep the v2.
  • The username is where our username from Docker goes
  • After the username, you will put the image to push
  • The last command will push our declared image to Docker Hub.

Back in Docker, you will see the image you pushed!

Image in Docker Hub

Lets Recap

We started off by creating an NGINX web server using Docker, then we created our own NGINX image, modifying a Dockerfile, and then we pushed this new image to AWS ECR and to Docker Hub. Hope this helped! Follow this link to go to a more advanced article working with Dockerfiles and images.

--

--