Introduction to Docker: Simplifying Application Deployment

Sushant Gaurav - Jun 14 '23 - - Dev Community

Docker GifDocker has revolutionized the way we build, test, and deploy applications. It is a powerful container platform that enables developers to package applications and their dependencies into lightweight, portable containers. These containers can then be deployed consistently across different environments, ensuring that the application behaves the same way everywhere.

Understanding Docker's Working Principle

At the core of Docker's functionality is the Dockerfile, a text-based script that defines the specifications of an application's environment. By leveraging Dockerfiles, developers can easily create Docker images, which are self-contained snapshots of an application along with its dependencies.

Unlocking the Potential: Use Cases for Docker

Docker finds applications in a wide range of scenarios, making it a versatile tool in the world of software development. Some popular use cases include:

  1. Microservices: Docker enables the seamless deployment and scaling of microservices-based architectures. Docker promotes modularity and flexibility in building complex systems by encapsulating each microservice within its own container.

  2. Data Processing: Docker simplifies the deployment of data processing applications by providing a consistent environment for running tools such as Apache Spark or Hadoop. It ensures that the required dependencies and configurations are properly set up, saving time and effort.

  3. Continuous Integration and Delivery: Docker has become an integral part of CI/CD pipelines. With Docker, developers can create standardized build environments that ensure consistent testing and deployment across various stages of the software development lifecycle.

  4. Containers as a Service: Docker provides a foundation for container-based services, allowing organizations to offer scalable and managed container environments. It enables the provisioning of containers on-demand, facilitating the deployment of applications without the need for manual configuration.

Embracing Containerization: Lightweight and Efficient

One of Docker's key advantages lies in its utilization of containerization. Unlike traditional virtual machines that run complete operating systems on top of a host OS, Docker containers operate on a shared OS kernel. This approach eliminates the overhead associated with running multiple operating systems simultaneously.

By leveraging OS virtualization, Docker containers are lightweight, portable, and fast to start. They encapsulate the application along with its dependencies, ensuring consistent behavior regardless of the underlying host environment.

Unveiling Docker's Architecture

Docker follows a client-server architecture model, which enables smooth communication and interaction between different components:

  • Docker Client: The Docker client serves as the primary interface for developers to interact with Docker. It sends commands and requests to the Docker daemon for building, running, and managing containers.

  • Docker Daemon: The Docker daemon is responsible for the core functionalities of Docker. It handles tasks such as building Docker images, managing containers, and orchestrating their execution.

Here's a visual representation of Docker's architecture:

Docker Architecture

With this architecture, Docker enables seamless collaboration between developers and system administrators, allowing for efficient application deployment and management.

In the next section, we'll dive deeper into the fundamental concepts of Docker and explore how to get started with using Docker to create and deploy applications.

Important Docker Terminologies

Docker Daemon

It listens to the API requests being made through the Docker client and manages Docker objects such as images, containers, networks, and volumes.

Docker client

It is used to interact with Docker. When we run the command, the client sends the request to the daemon, which performs the actions required. One client can interact with numerous daemons.

Docker registries

It is used to store the Docker images. Docker Hub is a public registry that anyone can use.

  • When you pull an image, Docker by default looks for it in the public registry and saves the image on your local system on DOCKER_HOST.
  • You can also store images on your local machine or push them to the public registry.

Dockerfile

It contains the steps to create a Docker image. These images can be pulled to create containers in any environment. These images can also be stored online at Docker Hubs.

  • When you run a Docker image, you get Docker containers.

Docker Image

It is a file that defines a Docker container (instructions to make a container). It is similar to the snapshot of a VM.

  • Docker Images are run to create Docker containers.
  • Images are immutable. The files making up an image do not change.
  • Images can be stored locally or in remote locations.
  • An image can be a collection of images. This is called layering.
  • The format name:version is commonly used for image tagging in Docker.

Containers

A container is a runnable instance of an image, basically where our application runs.

  • We can manage containers using the Docker API or CLI.
  • We can connect a container to one or more networks, attach storage to it, or even create a new image based on its current state.
  • As we know the container does not communicate with the outside world. This container contains a small version of the OS and the dependency files that are needed to run the application.
  • Every container has a unique ID.

Let's visualize it in a simpler form:

  +-------------+           +-------+          +-----------+
  | Docker File |  --run--> | Image | --run--> | Container |
  +-------------+           +-------+          +-----------+
Enter fullscreen mode Exit fullscreen mode

Parts of Docker

1. Docker Runtime

It allows us to start and stop the containers. The Docker daemon works with the runtime to run our commands. It has two types:

  • runc: It is a low-level runtime. Its role is to work with the OS and start/stop the container.
  • containerd: It is a high-level runtime. Its role is to manage the runc (low-level runtime).
    • It also manages other things like interaction with the network, pulling the data and images, etc.
    • It also acts as the runtime for Kubernetes.

2. Docker Engine

It helps us to interact with Docker.

  • Example: Docker daemon.

3. Docker Orchestration

Docker Orchestration is the management of multiple Docker containers, automating their deployment, scaling, and coordination to work together seamlessly, ensuring efficient and reliable operation of containerized applications.

  • For example, consider Docker containers as individual building blocks containing different parts of your application. Docker Orchestration is like the conductor that brings these building blocks together and ensures they work in harmony.

Docker components and working of Docker commands

Docker Commands

Let us now look at the various commands associated with Docker.

1. To run an image.

docker run hello-world
Enter fullscreen mode Exit fullscreen mode
  • It means hey Docker, run the image named hello-world, if it is not found locally then download it from the Docker hub.

  • We can also use the inspect command for the same (using both the container-ID and image-name). Example:

    docker inspect container-ID
    
    docker inspect image-name
    
  • We can also run the image in the background using the -d command. Example:

    docker run -d ubuntu
    
    • Here -d flag means detach mode.

2. To check all the locally downloaded images.

docker images
Enter fullscreen mode Exit fullscreen mode

3. To download an image.

docker pull image-name
Enter fullscreen mode Exit fullscreen mode
  • We can also download specific versions using the ; symbol. Example:

    docker pull ubuntu:16.04
    
  • We can also check only the IDs of the images.

    docker images q
    

4. To list down the containers.

docker container ls
Enter fullscreen mode Exit fullscreen mode

5. To start an interactive shell or environment:

docker run -it image-name
Enter fullscreen mode Exit fullscreen mode
  • We can also attach a specific shell to the container. For example, if we want to attach a bash shell, we can specify it with the container's ID.

    docker container exec -it container-ID bash
    
    • To run the above command, the container must be running in the background.

6. To stop a container.

docker stop container-ID
Enter fullscreen mode Exit fullscreen mode

7. To check all the containers (also the stopped ones).

docker ps -a
Enter fullscreen mode Exit fullscreen mode

8. To remove a container.

docker rm container-ID
Enter fullscreen mode Exit fullscreen mode

9. To delete all the stopped containers.

docker prune -f
Enter fullscreen mode Exit fullscreen mode
  • Here, the -f flag means don't ask again just delete them.

10. To remove the images.

docker rmi image-name -f
Enter fullscreen mode Exit fullscreen mode
  • To remove all the images, we can pass the images-IDs of all the active images with the rmi command as:

    docker rmi $(docker images -q)
    
    • Note: When a container is running, then its image will not be deleted.

11. To share our container.

First, we can commit the changes just like we do in Git.

  • Command:
  docker commit -m "message" container-ID Image-name
Enter fullscreen mode Exit fullscreen mode
  • Example:
  docker commit -m "modified info.txt" 408bd12547vg MyApplication:1.01
Enter fullscreen mode Exit fullscreen mode

Notes:

  • We can also specify the port to run a container using the p flag. Example: -p 8080:80 nginx.

Creating our own Docker Images.

  1. First create a docker file. The name convention is Dockerfile. Example:

    touch Dockerfile
    
  2. Write the content of the Dockerfile. Example:

    FROM ubuntu
    MAINTAINER maintainer-name <maintainer-email-id>
    RUN apt-get update
    CMD [“echo”, “Hi”]
    

    In the above command:

    1. First we are giving the name of the base image.
    2. We are giving the Maintainer Details.
    3. Writing the command that should run while the container is being created i.e. RUN apt-get update.
    4. Finally write the command that should run after the container is created i.e. CMD [“echo”, “Hi”]. Here we are using the CMD tag for specifying the executable commands to run.
      • We are giving the commands in the form of an array like: ["command-1", "command-2", ...].
  3. To build the image out of the docker file, we can use the docker build command. Example:

    docker build -t myImage:1.01 <path-to-docker-file>
    

    Here the -t flag (tag flag) is used to tag the image being built with a specific name and optionally a version number.

  4. For pushing the data to the Docker repository, we first need to log in to the Docker with the terminal. We can use the docker login command and then enter the username and password.

  5. Finally, to push the Docker image to a Docker repository, such as Docker Hub, we can use the docker push command. Command:

    docker push [OPTIONS] NAME[:TAG]
    

    Example:

    docker push username/myImage:1.01
    

Docker Volume

Volume is basically a shared directory between containers or between the container and the host. Even if the container is stopped, the volume still remains accessible.

  • Volume will be created in one container.
  • You must declare a directory as volume while creating the container.
  • When we update the image, the current state of the volume gets saved for further sharing. It does not get updated.
  • We can share volume between containers and between a container and the host.
  • Even if we delete the container, the volume is not deleted.

Various ways to create a volume

1. From Dockerfile

  1. Create a Dockerfile and write:

    FROM ubuntu
    VOLUME ["/<volume-name>"]
    
  2. Create an image out of the file:

    docker build -t <image-name> .
    
- Here, `` .`` resembles the current directory.
Enter fullscreen mode Exit fullscreen mode
  1. Create a container from this image:

    docker run -it --name <container-name> <image-name> /bin/bash
    
  2. Share the volume of container1 with another container (Example: container2):

    docker run -it --name container2 --privileged=true --volumes-from container1 ubuntu /bin/bash
    
 - Here,
   - ``--privileged=true`` means we are granting extended privileges on the host system.
   - ``--volumes-from container1`` indicates that the new container (``container2``) will inherit the volumes from an existing container (``container1``).
Enter fullscreen mode Exit fullscreen mode

2. Using commands

  1. Add the volume flag in the run command to create a volume while creating the container:

    docker run -it --name <container-name> -v <volume-name> ubuntu /bin/bash
    
 - Example:
Enter fullscreen mode Exit fullscreen mode
   ```docker
   docker run -it --name container1 -v /myNewVolume ubuntu /bin/bash
   ```
Enter fullscreen mode Exit fullscreen mode
- Here,
   - ``-v`` is the flag that is used to set the name of the volume, we also specify the name of the volume along with its path.
Enter fullscreen mode Exit fullscreen mode
  1. Share the volume of container1 with another container (Example: container2):

    docker run -it --name container2 --privileged=true --volumes-from container1 ubuntu /bin/bash
    

3. Create a shareable volume between the host and container

Suppose the host directory is /home/ec2-user and the container volume is myNewVolume.

  1. Add the volume with the run command:

    docker run -it --name <container-name> -v /home/ec2-user:/myNewVolume --privileged=true ubuntu /bin/bash
    
- Here,
  - ``-v /home/ec2-user:/myNewVolume``: creates a Docker volume that is mapped to a directory on the host machine. In this case, it's mapping the ``/home/ec2-user`` directory on the host to ``/myNewVolume`` within the container.
Enter fullscreen mode Exit fullscreen mode

Docker Port

Initially, the port is not open in a container, we typically use publish or expose tags to configure ports in the container.

Important Commands

To check the port of a container

docker port <container-name>
Enter fullscreen mode Exit fullscreen mode

To configure a port

docker run -p <host-port>:<container-port> <docker-image>
Enter fullscreen mode Exit fullscreen mode
  • Example: If we want to map port 8080 of host to port 80 of the container, we can use:

    docker run -p 8080:80 myImage
    
  • Here, -p stands for publish.


Extras

Difference between docker attach and docker exec

The exec command creates a new process in the container's environment but the attach command connects the standard I/O of the main process inside the container to the corresponding standard I/O of the current terminal.

Difference between expose and publish

  • If we do not specify the expose or -p, then the services in the container can only be accessible from inside the container.
  • If we expose a port then the services in the container can be accessed by docker containers but it cannot be accessed by the outside world.
  • If we publish a port then the container services automatically gets exposed and it is accessible by everyone.

How to push your docker image on the Docker Hub

  1. Create a new Docker image from the existing container:

    docker commit <container-name-or-ID> <image-name>
    
  2. Give a tag to the image:

    docker tag <image-tag> dockerID/<image-name>
    
  3. Login to hub.docker.com

  4. Push the image to the Docker Hub:

    docker push dockerID/<image-name>
    

Important Commands

  1. To list all the volumes: docker volume ls
  2. To create a volume: docker volume create <volumeName>
  3. To remove a volume: docker volume rm <volumeName>
  4. To remove all the un-used or un-shared docker volumes: docker volume prune
  5. To check details of a volume: docker volume inspect <volumeName>
  6. To inspect a container: docker container inspect <containerName>
  7. Stop all the running containers: docker stop $(docker ps -a -q).
  8. Delete all the stopped containers: docker rm $(docker ps -a -q)
  9. Delete all the images: docker rmi -f $(docker images -q).

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .