Introduction to Docker: Simplifying Application Deployment
Tags: docker, devops, development, beginners
Published: 2023-06-14 14:41:08


![Docker Gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hc1jt9zgv7nczae18evw.gif)Docker 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](https://dev-to-uploads.s3.amazonaws.com/i/dmgpglab3k1z23g02cr5.png)

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 |
  +-------------+           +-------+          +-----------+
```

### 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](https://dev-to-uploads.s3.amazonaws.com/i/o2dmqkdzcscl4atbhoj3.png)
---

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

#### 1. To run an image.
```docker
docker run hello-world
```
  - 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
    docker inspect container-ID

    docker inspect image-name
    ```

  - We can also run the image in the background using the ``-d`` command. Example:
    ```docker
    docker run -d ubuntu
    ```
    - Here ``-d`` flag means detach mode.

#### 2. To check all the locally downloaded images.
```docker
docker images
``` 

#### 3. To download an image.
```docker
docker pull image-name
```

  - We can also download specific versions using the  ``;`` symbol. Example:
    ```docker
    docker pull ubuntu:16.04
    ```

  - We can also check only the IDs of the images.
    ```docker
    docker images q
    ```

#### 4. To list down the containers.
```docker
docker container ls
```

#### 5. To start an interactive shell or environment:
```docker
docker run -it image-name
```

  - 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
    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
docker stop container-ID
```


#### 7. To check all the containers (also the stopped ones).
```docker
docker ps -a
```



#### 8. To remove a container.
```docker
docker rm container-ID
```



#### 9. To delete all the stopped containers.
```docker
docker prune -f
```

  - Here, the ``-f`` flag means don't ask again just delete them.



#### 10. To remove the images.
```docker
docker rmi image-name -f
```

  - To remove all the images, we can pass the images-IDs of all the active images with the ``rmi`` command as:
    ```docker
    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
  docker commit -m "message" container-ID Image-name
  ```

- Example:
  ```docker
  docker commit -m "modified info.txt" 408bd12547vg MyApplication:1.01
  ```

#### 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:
    ```docker
    touch Dockerfile
    ```

2. Write the content of the Dockerfile. Example:
    ```docker
    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
    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
    docker push [OPTIONS] NAME[:TAG]
    ```
    Example:
    ```docker
    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:
    ```docker
    FROM ubuntu
    VOLUME ["/<volume-name>"]
    ```
2. Create an image out of the file:
    ```docker
    docker build -t <image-name> .
    ```
    - Here, `` .`` resembles the current directory.

3. Create a container from this image:
    ```docker
    docker run -it --name <container-name> <image-name> /bin/bash
    ```
4. Share the volume of ``container1`` with another container (Example: ``container2``):
    ```docker
    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``).

#### 2. Using commands
1. Add the volume flag in the run command to create a volume while creating the container:
    ```docker
    docker run -it --name <container-name> -v <volume-name> ubuntu /bin/bash
    ```
     - Example:
       ```docker
       docker run -it --name container1 -v /myNewVolume ubuntu /bin/bash
       ```
    - 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.
2. Share the volume of ``container1`` with another container (Example: ``container2``):
    ```docker
    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
    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.

---
### 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
docker port <container-name>
```

#### To configure a port 
```docker
docker run -p <host-port>:<container-port> <docker-image>
```

  - Example:  If we want to map port 8080 of host to port 80 of the container, we can use:
    ```docker
    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
    docker commit <container-name-or-ID> <image-name>
    ```
2. Give a tag to the image:
    ```docker
    docker tag <image-tag> dockerID/<image-name>
    ```
3. Login to ``hub.docker.com``
4. Push the image to the Docker Hub:
    ```docker
    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)``. 

---