Getting Started with Docker
Let’s first discuss, why do we need Containers?
- Assume we are developing and testing application in Windows environment. All the dependencies for development has been added as per the current environment. However, transitioning this application to an alternate environment, such as RHEL, poses a high risk of encountering issues caused by dependency mismatch. Even if we are able to add dependencies in different environment, it will be an extra overhead and it is not guaranteed that it will not fail again.
- Above problems are solved using Containerization.
- Containers are loosely isolated lightweight environment which packs up code and all its dependencies so that it became portable and can run uniformly and consistently on any infrastructure either on-premise environment or cloud. This means that a container has no knowledge of underlying OS, or files. It runs on the environment provided by Docker Desktop.
- Containers have everything that code needs in order to run, down to a base operating system. We can use Docker Desktop and CLI to manage and explore our containers.
- Containers share the host kernel so full OS isn’t required.
- We also can run many containers simultaneously on the given host machine. And, Containers are great for CI/CD workflows.
- There are two types of containerization — OS Level & Application Level.
What is Docker?
- Docker is an implementation of Application Level Containerization written in Golang, it is an open-source platform for developing, shipping, and running applications.
- Other famous implementation in the market is Podman, Mesos, rkt, runc.
Docker Architecture
- Docker adopts a client and server architecture.
- Client is primary UI to the Docker which takes inputs/commands from users.
- Docker Daemon is a powerful packaging tool responsible for building, running and shipping containers. It manages images, containers, network and volume. It can exists on same or different hosts i.e. the Client and Daemon can run on the same system or, we can connect Client to a remote Daemon using REST API. A Daemon can also communicate with another Daemon .
- Docker Host is a registry to upload and manage our apps and offers automated workflow.
- Docker image is a read-only build component with instructions to create Docker Containers. Think it as a blueprint.
- Base images are already built images which we can use with some additional customization, e.g. python — we can pull this image from Docker Hub and customize it. In this scenario, python will be base image.
- Registry stores Docker images in a public store or a private store.
- Docker Hub is a public registry, and Docker looks for images on Docker Hub by default.
- Containers are run components which holds code along with dependencies. As mentioned above, Docker images are blueprint whereas Container are actual runnable instance of images. It is isolated from host and other containers by default but we can control the isolation level.
- Docker Object is a generic term used to refer images, containers, volume, networks.
- Docker Desktop is GUI tool to manage images and containers, we can also use CLI for the same.
How Docker provides Isolation?
- Docker uses namespaces and control groups to provide the isolated workspace called the container.
- Docker containers uses namespaces only assigned to it, that way Docker is able to provide isolation from other containers.
Docker Installation
- https://docs.docker.com/engine/install/
- After installation we can use Docker Desktop or CLI.
- We can also use https://labs.play-with-docker.com/
How to create our own images?
- As mentioned above, we can create our own images or we can customize any base images by pulling it from registry.
- To do so, we need to create dockerfile.
- Dockerfile contains a list of instructions which will be executed in a sequence by Docker and create image.
- docker build — command uses instructions mentioned in Dockerfile to build images.
- We use below syntax to write instructions in dockerfile.
INSTRUCTION args
- Some common instructions are FROM(first instruction used for base image), RUN, WORKDIR, CMD.
- For details, check https://docs.docker.com/build/building/packaging/#dockerfile
- Layers are created by each instructions written in dockerfile in a Docker Image. Docker Daemon executes instructions written in dockerfile and an intermediate container is generated in each steps. Once a step is completed, the intermediate container is stored as a layer on top of which next instruction is executed and intermediate containers will be removed after each instructions until we got final image.
- When we modify the dockerfile and rebuild the image, only those layers which have modification are rebuilt. This makes images lightweight, small, and fast.
Pulling and running docker image from dockerhub
Let’s pull and run node image from dockerhub.
- Open Docker Desktop and search for node image.
- There will be many search result, probably first search result is official docker image(node:latest). latest is the default tag.
- Click on the image, you will get options to select version, pull and run.
- Click on pull, it will download the base image. If you directly run the image, it will first pull it from dockerhub and then it will run as container.
- Now move to Images section, you will able to find node image.
- We can run this image by clicking on run button in Actions section.
- When you click on run, it will ask some optional settings. For now, let’s provide container name only as “FirstContainer” and hit run.
- Now move to Containers section, you will able to find your “FirstContainer”.
- Click on the container name it will gives you options like Logs, Files, Terminal.
- If you are getting container status as “Existed”, try to open terminal and run below command.
docker run -it node /bin/bash
- Above command will run container.
- Open the container by clicking on container name and move to Exec.
- You will get terminal, we can check node version.
node -v
- We can delete container by clicking on delete icon(right).
Similarly we can use other images as well.
Above we have seen how to use node image, Let’s try python image now.
Coding a Dockerized flask app
flask is a python framework, we will be creating a simple “helloworld” flask app.
As we get familiar with Docker Desktop, we will use terminal so that we can get familiar with it too.
If you are totally new to flask, I suggest checking below docs first.
- Create a directory(I named as “dockerizedflaskapp”) and open it in VS Code, we will create required file in it.
- Now create a file “app.py” inside the same directory — dockerizedflaskapp/app.py and paste below code in it.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "<p>Hello, World!</p>"
app.run(debug=True, port=8080)
- Default port of flask app is 5000, but we will be using 8080.
- To run this flask app we need few dependencies to be installed. Create a file “requirements.txt” inside the same directory — dockerizedflaskapp/requirements.txt and specify below dependencies in it. Later we will use pip to install these dependencies.
Flask==1.1.2
Jinja2==2.11.2
Flask-RESTful==0.3.8
MarkupSafe==1.1.1
itsdangerous==1.1.0
Werkzeug==1.0.1
- Above steps are application related, now we will create Dockerfile to create our own image on top of python base image
- Create “Dockerfile” inside same directory- dockerizedflaskapp/Dockerfile and add below instructions.
# We will start from python base image.
# We can use -v 3.11 but it will install all libraries and 3.11-slim is
# light
FROM python:3.11-slim
# Exposing port 8080 for app to run
EXPOSE 8080
# This will create directory named as "/app" in Linux env and
# we use this as working directory in the container
WORKDIR /app
# Copy files in current working directory dockerizedflaskapp
# to the container working directory "/app"
COPY . .
# Installing all dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Command to run the flask app as we write in terminal
CMD ["flask", "run", "--host", "0.0.0.0", "--port", "8080"]
- Now open in same directory — dockerizedflaskapp.
- We need to create our image now using docker build. “-tag” gives tag to image, default tag is “latest”. “flaskapp” is the name of image and “.” is path of Dockerfile
docker build -tag flaskapp .
- It will take some time and build our image with steps mentioned in Dockerfile. You can check building log as well.
- We can check existing images using below command. You will be able to get image with “flaskapp” as name and “latest” as tag.
docker images
- We can run flaskapp image using docker run. Using “-p” we provide port number to run of our local system followed by docker exposed port number. And, next is image name which we want to run.
docker run -p 8080:8080 flaskapp
- We can check running containers using below command. To check all container use “-all”.
docker ps
docker ps -all
- We can stop container by using below command. Notice that it is container-name not image name. By default, random container name got assigned.
docker stop <container-name>
- To delete a container we can use below command.
docker rm <container-name>
How to push our image to Dockerhub?
- We can push our image using docker push. Default registry is Dockerhub.
- We need to tag our image first before pushing otherwise it will fail.
- We can tag our image using below command and we need to use our username as tag.
docker tag <image-name> <user-name>/<image-name>
docker tag flaskapp ashutoshkmrsingh/flaskapp
- Then, we can push using below command.
docker push <tag-name>/<image-name>
docker push ashutoshkmrsingh/flaskapp
- Now go to https://hub.docker.com/ and search for ashutoshkmrsingh/flaskapp. You will be able to find the image. In this way we can share the image with other person who can use it directly.
Cleanup
We can remove all unused containers using below command.
docker container prune
Similarly, we can remove all dangling images using below command.
docker image prune
Conclusion
Congratulations! you have just completed the first step to get into Docker world. There are other important things to explore in docker mainly Volumes and Network. Hopefully, I will come up soon with other article covering those stuffs 😊. Till then, Happy Learning and Happy Coding!
References: