Docker Basics : Part 1
What is docker?
Docker is an open-source platform designed to simplify the process of building, deploying, and running applications using containerization. Containers allow developers to package an application along with all its dependencies (libraries, configurations, etc.) into a single, lightweight, and portable unit that can run consistently across different environments.
Why we need docker?

Here you can see that due to multiple dependencies, there is a complete mess.
So, what can we do to solve this issue?

Containers vs VM
If you have experience working with VM you can see that, Virtual machine takes time due to OS in every VM whereas containers are lightweight

Therefore, it’s better to use container in big projects. Eventually it saves a lot of resources.
Can we use both VM and Containers together?
VM and containers both can work fantastically once we manage them both in an architecture

To run a container
docker run <container_name>
example: docker run nginx

To check list of running containers
docker ps

To check list of exited containers
docker ps -a

To remove a container
docker rm <container_id>

To check list of all images
docker images

To remove an image
docker rmi <image_name>
To run a container in the background (detached mode)
docker run -d <container name>
example: docker run -d ubuntu
To make sure the container is not exited , make it sleep
docker run <container> sleep <seconds>
example: docker run ubuntu sleep 100


Once 100second passes by, the container is gone


To pull images and not to run
docker pull <image>
To execute a container(Make sure the container is running in the backend using docker run -d and then execute some command.)
docker exec <container ID> <commands to execute>
Example: docker run -d ubuntu sleep 100
docker exec 982820382a cat /etc/*release

Run different version of containers. By default it's "latest" . The versions are also called tags
docker run <container image>:<version>
Example:
docker run ubuntu:20.04
To work with containers which asks for an input with a string message , use -i for interactive mode (takes input), -t (to attach to terminal and print output while asking for the input)
docker run -i <container image>
docker run -it <container image>
Example,
docker run -it ubuntu

Then write exit

Port mapping
assume that your container runs on the 5000 port of the web app container

If a user wants to access a web application running inside a Docker container, they need to use the host machine’s IP address and the mapped port (e.g., 5000).
However, the container’s internal IP address (assigned by Docker) is not directly accessible from outside the host. Instead, Docker allows port mapping (-p) to expose the container’s port to the host

To solve this issue, we can use the host’s IP and use one of it’s IP. We can connect host’s port to the web app’s port.Here in 80:5000 , 80 is host laptop’s IP address and 5000 is web app’s IP address.
The app inside the container listens on
5000.Docker maps the host’s port
80to the container’s port5000

In this way, you can connect multiple containers to our host’s port

But note , you can connect 1 port of host laptop with 1 port of the container. Same port of host laptop can’t be used with others.

Volume mapping
Assume that you run the mysql server and then a container appears

all of it’s data is saved within it’s location (/var/lib/mysql)
You can add important data to this container

and sometime later you may feel that you want to delete the data.

Boom! all of the data will be gone.
To keep the data save from earlier, we can map our local host laptop’s storage address to the container’s address

Here our host laptop’s /opt/datadir is connected to mysql container’s /var/lib/mysql file
For example, let’s create a mysql_data folder within my latest Docker folder. Then volume mount mysql on this folder
docker run --name mysql_db -e MYSQL_ROOT_PASSWORD=rootpassword -e MYSQL_DATABASE=testdb -e MYSQL_USER=testuser -e MYSQL_PASSWORD=testpass -v /home/mitulshahriyar/Desktop/Kubestraunaut/Docker/mysql_data:/var/lib/mysql -p 3306:3306 -d mysql:8.0
Then connect that using TCP
docker exec -it mysql_db mysql -h 127.0.0.1 -u testuser -ptestpass testdb
Let’s check the databases
SHOW DATABASES;
Use our database and input some data
USE testdb;
CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), email VARCHAR(50) UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com'), ('Bob', 'bob@example.com'), ('Charlie', 'charlie@example.com');
SELECT * FROM users;
Let’s exit from it
EXIT;
We are out of mysql now. Let’s delete the container
docker stop mysql_db docker rm mysql_db

Now, let’s create another container mounting to the same volume
docker run --name mysql_db_new -v /home/mitulshahriyar/Desktop/Kubestraunaut/Docker/mysql_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=rootpassword -p 3306:3306 -d mysql:8.0
Then connect with it
docker exec -it mysql_db_new mysql -h 127.0.0.1 -u testuser -ptestpass -e "SELECT * FROM testdb.users;"

You can now find exactly the same results we earlier got (in the earlier container)
Once done, feel free to delete the folder using
sudo rm -rf /Docker/mysql_data
How can we create our own image?
Let’s containerize simple web app (code is kept in app.py) where flask (python based web framework ) was used

Here, we have to create a Dockerfile where all of the instructions are given to run our app.py.
We are expecting to use Ubuntu based OS, then we have latest updates using apt-get update, then install python to run our app.py file. Our python app worked with flask web framework and thus installed flask and flask-mysql.
Then copied this code to local device's /opt/source-code folder.
Then FLASK_APP was set as the total running path and mentioned flask and run command. (Entry point ensures that these commands can be run to get access to the service/app)
The format was :

Then build the image

Also, we can go into the the folder we have our Dockerfile and the run
docker build -t mmumshad/my-customer-app .
Here this is used to run Dockerfile and name the image mmumshad/my-customer-app within the folder (.)
Once we build the image, it basically creates some layers. Basically each layer follows the commands.

If you run the history command, you can see the file size of ubuntu layer or others. If the image file is large (check docker images), we can modify Dockerfile and use lighter base files (instead of ubuntu, in the Dockerfile, use other lite versions ubuntu:20.04). But we won’t do it in this blog.

All the layers build will be cached. So, if a step fails,

it will use previous layers from cache and and continue building the remaining layers.

As cached, the process becomes much faster now!
Then push to docker hub to make it available for others (dockerhubname/docker_image_name)

So, anyone can use this my-custom-app
Let’s do it hands-on
Here are the codes. Copy the file codes and save them within a folder called app.

Once done, get into the folder using terminal and use these codes

Environment variables
Assume that this is our app.py file where are expecting the color variable to set value from outside (color= os.environ.get(‘APP_COLOR’))

Now, once we set the APP_COLOR to blue (export APP_COLOR=blue; python app.py), the app’s color turns to blue

This is how we can run our containers which expects a variable (APP_COLOR). Here, simple-webapp-color is a container which has a variable APP_COLOR


We can set the value to blue by -e APP_COLOR=blue. Now the app also turned blue. Here, e goes for Environment variable
So, we can change the color by just changing the value each time we run the command

You can also check all of the environment variables using docker inspect <container_id>

Then you will see them in the config section.

For example , I can see my minikube’s environment variables


We can also specify our container name (--name) running it from a different image.
Let’s run a container named blue-app using image kodekloud/simple-webapp and set the environment variable APP_COLOR to blue. Make the application available on port 38282 on the host. The application listens on port 8080.

You can verify the container with name blue-app

Why a container exits so fast?

Here you can see the ubuntu container was run but it exited on 41 seconds. Why?
Basically VMs are designed to host Operating Systems but Container are there to do some tasks listed in the dockerfile. Once done, it exits.
For example, this is the dockerfile for the container ubuntu

Here, CMD defines what command is to run. But we have bash ( a shell type.) .Bash looks for a terminal to work on . If it can’t find that, it exits.
So, earlier when we launched the container ubuntu, it launched bash program. By default docker does not attach a terminal to a container when it runs and thus bash could not find a terminal and exited.
But how to add more commands other than the command mentioned in CMD?

We can add our external command in this way and that will override the command in CMD.
Assume now that you want the ubuntu to sleep for 5 seconds everytime it’s launched. So, surely you will modify the dockerfile, right?

And you gave this image name “ubuntu-sleeper”
Now, assume that you want to increase the sleep time to 10 but this time outside the Dockerfile. May be from the terminal!!
Instead of writing docker run ubuntu-sleeper sleep 10, we can write this in the Dockerfile.

And now on terminal, this time we could just write the sleep seconds because we used ENTRYPOINT. This will allow the value of sleep to be taken in the terminal.
SO, sleep = 10 is set now!
But it might happen that a person might forget to give the sleep seconds. then?
To solve this issue, we can put both ENTRYPOINT and CMD in Dockerfile
If a person does not input any sleep seconds, the CMD value will run as the value for the ENTRYPOINT command.
But if someone provides sleep seconds, the value will be referred to ENTRYPOINT. No need to use the CMD’s value 5
Docker Compose
Rather than running all of the servers (once at a time) required for our project,

We can organize them in a yaml file (docker-compose.yaml) and then run them

Let’s work on a sample voting application

One part works on voting and another part for result showcase.
We are building a python based app which will take vote

and store in redis database.

Then it will be passed to worker which is a .net application

and update postgreSQL database ( in the result)

Final app is a nodejs application which will show the vote result

Now, this is how we would do that using docker run (individually running for example)

We can run all of the container giving them the specific name.
Note: 5000:80 means we can access the application on 5000 port in our laptop through route mapping from 80 to 5000
But we haven’t connected them , right?
Let’s connect the voting-app with - - link <name of the redis container>:<name of the host the voting app is looking for>
Here, the get_redis() mentions that it’s looking for “redis” (host=”redis”)

This is the current vote app’s code

as the container name is redis as well. We thus write, - -link redis:redis
Again, the result-app has this source code

Which expects a connection to a postgres database on host db. So,we connect the result-app and the postgres database
Here ,database name (db) : name of the db it’s trying to contact in the source code (postgre$db)
So, - -link db:db

Now, voting-app and result-app is connected with redis and postgresql database.
Then we connect the worker to redis and db


Note: this is the updated code for worker and still we can see var pqsql and var redisConn
We can do the same task in docker-compose.yml file (to do all things in one time)
Instead of this,

We can now make this docker-compose.yml file

we followed the format
<container name>
image: <image>:<version>
ports:
- host’s port: container’s port
links:
- <name of the container it links>
Also, if we want to build an image (build: ./vote) instead of pulling an image, we can do this

We just changed the line to build: <location of the directory which has the application code and docker file with instructions to build the docker image>
For example, this can be the vote folder

Same thing can be done, for other builds

Docker has various versions and in version 2 and version 3, we see various changes like introduction to services, versions, , depends_on instead of links, etc.
This was version 1,

This was version 2,

This is version 3,

Docker network
Let’s create 2 traffics. We can connect the first one (front-end traffic) with voting app and result app.
And then we connect the all other to the back-end traffic(redis, postgresql, worker, front-end traffic)

Let’s make changes to the version 2 file (all the other things like depends on , port etc are there but for simplicity, we haven’t shown them here)

So, first we write the network names

Then we will mention them where they are used

For example, the voting app (vote), and result app (result) has connection to both front end and back end connection.
But others just have connection to backend traffic.