Everything on Docker! (Part l)
“Docker is an open-source platform for building, deploying, and managing containerized applications.”
- Linux: https://docs.docker.com/engine/install/ubuntu/
- Windows: https://docs.docker.com/desktop/windows/wsl/
- macOS: https://docs.docker.com/desktop/mac/install/
Let’s give an example:
You want to install software, which requires installing all the dependencies to complete the installation. However, you might run into errors, then you have to figure out the errors and fix them. After that, you have to try to re-run everything to complete the installation. That is where docker comes in and tries to make your life easier. Let’s understand the two important concepts in docker.
What is an Image?
Image is a single file that holds all the dependencies and configurations to run a program. You can see all the images in your system by the following command: “docker images”
What is a container?
The container is an instance of an image. The container acts as a virtual machine except it does not have a separate operating system. Each container installs the applications or software by following the instructions written in the image files. That being said, there could be multiple containers and each container will share a common host operating system and infrastructure. You can see all the containers using this command: “docker ps -a”
Like every programmer, you would like to run a “hello-world”. In docker, you need an image for that. To run hello-world, type “docker run hello-world”!
What is going to happen if you run this command?
*docker: reference to docker client
*run: try to create and run a container
- Execute: docker run hello-world
- Docker client (CLI): receives that command and takes some actions based on it.
- Docker Server: CLI communicates with the docker server to see if there is an image called “hello-world”! It is called image cache! If there is no image that exists, then the docker server will communicate with the docker hub where some pre-exist images reside.
- Docker Hub: Docker hub receives the request from the docker server to download the image file which will be allowed! Therefore, the image will be downloaded from the docker hub to the docker server!
- Docker server now will find the image in the “Image Cache” section. Then a container will be created, which is the instance of the “hello-world” image. That being said, the instance will run the simple program called hello-world in the container. You will be able to see something like this:
How to delete/remove all docker containers?
Command: docker system prune
How to get the logs of a container?
Command: docker logs <container-id>
How to execute an additional commands in a container?
Command: docker exec -it <container-id> <command>
Here, exec allows us to perform another command execution in the container, and -it allows us to take input from the user to the process and shows it on the terminal along with the error message of the process.
Some commands and their applications:
- docker ps — only shows the active containers
- docker ps -a — shows all the container (active/inactive)
- docker images — shows all the images that your system currently has!
- docker stop <container-id> — stops the specific container
- docker rm <container-id> — delete the specific container
- docker rmi <image-name> — delete the specific image
How to get a command prompt in a container?
Sometimes, it is useful to get a command prompt in a container right? To figure out errors or debugging purposes. Or you can run all the Linux commands there. Make sure your container is running.
command: docker exec -it <container-id> sh
sh will allow you to get permission to use the prompt and you can do various tasks such as:
How to run an image (that creates a container) with a prompt?
Let’s say you want to run an image from the docker hub which is called busybox. What do you do?
Command: docker run busybox
Now, to answer the above question, execute the following command:
Command: docker run -it busybox sh
Now, all those concepts and knowledge to make you prepare for the next step.
How to create your custom image?
- Dockerfile: It’s the main file where all the instructions reside. Every dockerfile follows the same structure. Step 1: Specify the base image. Step 2: All the dependencies and requirements to run the program as intended. Step 3: Specify a command to run on a container startup.
- Docker Client: After creating the Dockerfile, you will try to build the image right? Dockerfile will be received by the docker client.
- Docker Server: Docker client will forward all the instructions to the docker server. The docker server does the heavy work, which means it will execute every command inside the dockerfile and create an image. Then you will run the image, that will create a container (an instance of the image) to implement all the mentioned applications.
Steps to complete custom images:
- Create a Dockerfile
- Build the image: “docker build .”
- Run the image: “docker run <image-id>”
Let’s look at a simple dockerfile where Redis-server will be installed:
Now, by looking at the Dockerfile, you must have some questions! Let’s answer them now:
- What is the base image? : No container has an operating system. If you want to create a container, then it needs a starting point or a base image where it can start. Later on, you can modify what to do! By using this base image, Redis can be installed.
- Why alpine? : Alpine is like a Windows, macOS where it supports some program to install whatever you need. Alpine provides and acts exactly this way that is able to install Redis. You use FROM to load the base image.
- Install dependencies? : You can see RUN, which will execute the following command. APK stands for Alpine Linux package manager. You use the apk command to delete, install, upgrade, or list software on a running Alpine Linux-based system. Does that answer the question of “What is the base image?” and “Why Alpine?”. It should, right?
- CMD? : CMD instruction allows you to set a default command, which will be executed only when you run a container without specifying a command. The above example will start the Redis-server. Cool, right?
Now, let’s talk about “docker build .” What is it doing in the terminal output?
Step 1: It loads the alpine image container.
Step 2: It takes the file system of the alpine image to a temporary container and is instructed to install Redis. After completing the installation, it removes the temporary container. However, takes the file system of the temporary container to the alpine image container and updates it. In the new alpine image container, it has redis installed.
Step 3: It creates another temporary container with the previous image container file system to set the default command to start the redis-server every time the image is being instructed to run. After that, this temporary container is removed and the updated file system is restored to the final image container.
Finally, docker run <container-id> will be executed to run the image.
Important Note: If you update the Dockerfile, and try to rebuild it, the docker will use cache from the previous image to skip the same process. It will only update the newly added section in the Dockerfile. The order in the Dockerfile is important too. If you change the order of commands, then the cache will not be used!
Some commands and their applications:
- docker build -t <customized name>/<repo/project name>:version . : It will tag the image to a customized name!
Create a project and dockerize it!
In this section, we will create a simple node js application and dockerize it with our custom image. Let’s start!
Step 1: Create a package.json! Express is the back-end web application framework for Node.js. We are instructing it to use the latest version (*) of it. That is why it goes under dependencies. Then we instructed the scripts to run by default, which is “node index.js”. The index.js is the main web file.
Step 2: Create a simple index.json file. In that case, we are going to create an app using the express framework. This app will get requests via ‘/’ and perform a response. To get the request and response, the listening port needs to be fixed. make sense?
Step 3: Write the Dockerfile. Remember the steps? If you forgot, go up and check those important steps to write your Dockerfile. But it’s pretty clear that if we want to run this app, we need node js and npm installed.
Step 4: Build the image with tagging!
Step 5: Run the container and see it works!
Now, let’s verify if we can access the app from the browser and it really works!
Oh, no! something went wrong!? It’s time to figure that out.
Did you figure it out?
When a request is made on port 8080 to your local machine, it will not automatically redirect to the container, which also has network mapping. That being said, we need to sort out the port mapping part. Port mapping will allow the request on port 8080 from your local machine to port 8080 to the container. This is only for incoming requests. However, the docker can by default can outgoing requests. You wonder how? check the dependencies in the Dockerfile where npm is installed by the container, which reaches out to the internet by itself.
Step 6: Fix the port mapping! In the command, the first port is from the host machine/local machine. and the second port is the container port.
Finally, it works! However, it’s worth mentioning some common mistakes you might make during the process:
- Wrong base image selection.
- Not copying the files from the host machine to the container location.
- Port mapping.
You might have one question on your mind. I gotcha! Why did we use “WORKDIR /usr/app”
The main purpose is to separate out the working files! Everything we did was stored inside the “/usr/app” directory. So that those files do not mix with other directories and we can easily dissect if we face any problems. Another way would be:
*Minimizing cache and rebuilds!
I am not going to explain this part! But I will share three screenshots. Think and figure it out!
That’s it for today! Next time, I will be writing on how to create multiple containers and dockerize them together to make an application! Cheers!💁