🛸Docker : From Zero to Hero  ( part 1 )

Photo by Ian Taylor on Unsplash

🛸Docker : From Zero to Hero ( part 1 )

Docker is a tool that allows developers to package, distribute and run applications as containers. It provides an efficient and consistent way to deploy applications across different environments, from development to production, without worrying about dependencies or underlying infrastructure.

A real-life example of the benefits of Docker can be seen in web application development. In the past, deploying a web application could be a cumbersome and error-prone process. Developers would have to manually install and configure the necessary software components on each server, and any differences in configuration could lead to issues in production.

With Docker, the application and its dependencies are packaged together in a container, which can be easily deployed to any environment that supports Docker. This means that developers can be confident that their application will run the same way in development, testing, and production, and that the deployment process will be faster and more reliable.

In addition, Docker allows for better resource utilization, as multiple containers can run on a single machine without interfering with each other. This can lead to cost savings and improved scalability.

Overall, Docker simplifies the process of deploying and running applications, making it a valuable tool for developers and IT professionals alike.

Why do we need Docker if there is virtual machine ?

While both Docker and virtual machines have some similarities, they serve different purposes and have different benefits.

Virtual machines emulate an entire operating system, including the kernel, and allow you to run multiple operating systems on the same physical machine. Each virtual machine requires its own set of resources, including memory, disk space, and CPU time, which can make it difficult to scale and manage large applications.

Docker, on the other hand, uses containerization technology to allow you to package and run applications in lightweight, portable containers. Unlike virtual machines, Docker containers share the same host operating system kernel, which makes them more lightweight and efficient.

Here are some reasons why you might choose to use Docker over virtual machines:

  1. Resource efficiency: Docker containers use fewer resources than virtual machines because they share the same host operating system kernel. This means you can run more containers on the same physical machine than you could with virtual machines.
  2. Portability: Docker containers are portable across different environments and can run on any machine that has Docker installed, regardless of the underlying infrastructure.
  3. Fast deployment: Docker containers can be deployed and started much faster than virtual machines, which can take several minutes to start up.
  4. Isolation: Docker containers provide a high degree of isolation between applications, which can help to prevent conflicts and security vulnerabilities.
  5. Version control: Docker containers allow you to version control your application and its dependencies, making it easier to roll back to previous versions or test new versions without affecting the production environment.

In summary, Docker is a lightweight and efficient alternative to virtual machines that offers many benefits for modern application development and deployment.

Is Docker and Container same thing ?

Docker is a platform that allows you to build, package, and deploy applications in containers. A container is a lightweight, portable, and self-contained environment that contains everything an application needs to run, including code, libraries, and system tools.

Here are some differences between Docker and containers:

  1. Docker is a platform: Docker is a platform that provides a set of tools and services to build, deploy, and manage containers. It includes the Docker Engine, which is responsible for running and managing containers, as well as other tools like Docker Compose, Docker Swarm, and Docker Registry.
  2. Containers are the building blocks: Containers are the fundamental building blocks of Docker. They provide a lightweight and portable way to package and run applications in a self-contained environment.
  3. Docker provides a standardized format: Docker provides a standardized format for containers, which makes it easy to build, share, and deploy them across different environments. This format includes everything the container needs to run, including the code, dependencies, and system tools.
  4. Docker provides a container runtime: Docker provides a container runtime, which is responsible for creating, starting, stopping, and managing containers. It provides a layer of abstraction between the container and the host operating system, which allows containers to be run on any system that supports Docker.

In summary, Docker is a platform that provides a standardized way to build, package, and deploy applications in containers, while containers are the lightweight and portable environments that contain everything an application needs to run.

Docker Engine

The Docker engine is the core component of Docker that allows you to create, run, and manage Docker containers. It is a lightweight and efficient runtime that can be installed on various operating systems, including Windows, macOS, and Linux.

The Docker engine consists of several key components, including:

  1. The Docker daemon: This is the background service that runs on the host system and manages the containers and images.
  2. The Docker CLI: This is the command-line interface that allows you to interact with the Docker daemon and perform various operations such as building and running containers.
  3. The Docker API: This is the interface that allows external tools and applications to interact with the Docker daemon.

The Docker engine provides a powerful and flexible platform for building, running, and managing containerized applications. It allows you to package your application and its dependencies into a lightweight, portable container that can run on any system with Docker installed. This makes it easy to deploy and scale applications across different environments and platforms.

Docker daemon

The Docker daemon is the core component of the Docker engine that runs as a background service on the host system. It is responsible for managing Docker containers, images, networks, and volumes.

The Docker daemon listens to requests from the Docker CLI, Docker API, and other tools, and takes the necessary actions to create, start, stop, and remove containers. It also manages the networking and storage resources required by containers, as well as the caching and sharing of images.

The Docker daemon runs as a system service and can be configured to start automatically when the host system boots up. It is designed to be lightweight and efficient, using minimal system resources to provide fast and responsive container management.

Overall, the Docker daemon plays a critical role in the Docker architecture, enabling the creation, deployment, and management of containerized applications in a consistent and reliable manner.

Docker API

The Docker API is the interface that allows external tools and applications to interact with the Docker daemon. It provides a RESTful API that allows you to manage Docker containers, images, networks, and volumes programmatically, using standard HTTP requests.

The Docker API supports a wide range of operations, including creating and starting containers, managing images and networks, and accessing logs and stats for running containers. It is designed to be platform-agnostic, making it easy to integrate Docker into a wide range of tools and workflows.

The Docker API can be accessed using various programming languages and tools, including Python, Java, Node.js, and cURL. It is also supported by many popular DevOps tools such as Jenkins, Ansible, and Terraform.

Overall, the Docker API provides a powerful and flexible interface for automating Docker operations and integrating Docker into your DevOps workflows. It allows you to manage Docker resources programmatically, making it easy to create, deploy, and manage containerized applications in a consistent and scalable manner.

Docker CLI

The Docker CLI (Command Line Interface) is a command-line tool that allows you to interact with the Docker daemon and manage Docker containers, images, networks, and volumes. It provides a simple and powerful way to build, run, and manage Docker applications from the command line.

There are many Docker commands available, but here are some of the most commonly used ones:

  1. docker run: This command is used to create and start a new container from an image.
  2. docker build: This command is used to build a new Docker image from a Dockerfile.
  3. docker push: This command is used to upload a Docker image to a registry.
  4. docker pull: This command is used to download a Docker image from a registry.
  5. docker ps: This command is used to list running containers.
  6. docker stop: This command is used to stop a running container.
  7. docker rm: This command is used to remove a container.
  8. docker images: This command is used to list available Docker images.
  9. docker rmi: This command is used to remove a Docker image.
  10. docker network: This command is used to manage Docker networks.
  11. docker volume: This command is used to manage Docker volumes.
  12. docker exec: This command is used to execute a command inside a running container.
  13. docker-compose: This command is used to manage multi-container Docker applications.

These are just a few examples of the many Docker commands available. To see a full list of available commands, you can use the docker --help command or refer to the Docker documentation. We will discuss each and every command in detail now. But for this article we will discuss only 4 commands and in the upcoming articles we explore remaining commands.

docker run

The docker run command is used to create and start a new Docker container from a specified image. When you run the docker run command, Docker creates a new container based on the specified image and starts it up.

Here's the basic syntax for the docker run command:

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
  • OPTIONS: Specifies any additional options or flags to pass to the docker run command.
  • IMAGE: Specifies the Docker image to use as the basis for the new container.
  • COMMAND: Specifies the command to run inside the container.
  • ARG: Specifies any additional arguments to pass to the command.

Here is a list of some of the most commonly used options for the docker run command and their explanations:

  • d, --detach: Runs the container in the background (detached mode) and returns control to the terminal. This option is useful when you want to run a container as a background process.
  • p, --publish: Publishes a container's port(s) to the host machine. This option takes two arguments: the first specifies the port to expose inside the container, and the second specifies the port to map on the host machine. For example, docker run -p 8080:80 maps port 80 inside the container to port 8080 on the host machine.
  • v, --volume: Mounts a host directory or a named volume as a data volume inside the container. This option takes two arguments: the first specifies the path to the directory or volume on the host machine, and the second specifies the path inside the container to mount the volume. For example, docker run -v /path/on/host:/path/in/container mounts the host directory /path/on/host inside the container at /path/in/container.
  • -name: Assigns a custom name to the container instead of using a randomly generated one. This option is useful when you need to refer to a container by a specific name.
  • e, --env: Sets an environment variable inside the container. This option takes an argument in the form of VAR=value. For example, docker run -e VAR=value sets the environment variable VAR to value inside the container.
  • it, --interactive --tty: Runs the container in interactive mode and allocates a pseudo-TTY. This option is useful when you need to interact with the container's command line.
  • -rm: Removes the container automatically when it exits. This option is useful when you want to create a container for a specific task that you don't need to keep around after it completes.
  • -network: Specifies the network to use for the container. This option is useful when you need to connect a container to a specific network.
  • -restart: Specifies the restart policy for the container. This option takes an argument in the form of on-failure, always, unless-stopped, or a specific number of seconds to wait before restarting. This option is useful when you need to automatically restart a container if it exits unexpectedly.
  • w, --workdir: Sets the working directory inside the container. This option takes an argument in the form of a path. For example, docker run -w /app sets the working directory inside the container to /app.

How to set the multiple env variables ?

To set multiple environment variables when running a container using the docker run command, you can use multiple -e flags or pass a file containing the variables using the --env-file flag. Here are some examples:

  1. Using multiple -e flags:

     docker run -e VAR1=value1 -e VAR2=value2 myimage
    
  2. Using an env file:

    Create a file named env.listand add the environment variables with the key=value format, one per line:

     VAR1=value1
     VAR2=value2
    

    Then pass this file to the container using the --env-fileflag:

     docker run --env-file env.list myimage
    

In both cases, the container will have access to the environment variables VAR1and VAR2.

Volume overview

The --volume or -v option in Docker is used to mount a directory or file from the host machine into the container. This allows the container to access and modify files on the host system.

To set up a volume when running a container using the docker run command, you can use the following syntax:

docker run -v /path/on/host:/path/in/container myimage

This will mount the directory located at /path/on/hoston the host machine into the /path/in/container directory in the container.

You can also use named volumes or anonymous volumes to manage your data. Here are some examples:

  • Using named volumes:

      docker run -v myvolume:/path/in/container myimage
    

    This creates a named volume named myvolumeand mounts it into the /path/in/container directory in the container. You can use the same volume across multiple containers to share data.

  • Using anonymous volumes:

      docker run -v /path/in/container myimage
    

    This creates an anonymous volume and mounts it into the /path/in/container directory in the container. Anonymous volumes are created and managed by Docker and are not intended to be shared across containers.

You can also set options for your volumes, such as read-only mode, using the following syntax:

docker run -v /path/on/host:/path/in/container:ro myimage

docker build

docker build is a command in Docker that is used to build Docker images from a Dockerfile. A Dockerfile is a text file that contains a set of instructions that are used to build a Docker image. When you run the docker build command, Docker reads the instructions from the Dockerfile and builds a Docker image that you can use to run a Docker container.

Here is an example command to build a Docker image from a Dockerfile:

docker build -t myimage:latest .

In this example, we use the docker buildcommand to build a Docker image with the tag myimage:latest. The .at the end of the command specifies the build context, which is the directory containing the Dockerfile and any other files needed to build the Docker image.

The docker build command has several options that you can use to customize the build process. Here are some of the most commonly used options:

  • t: specifies the name and optionally a tag to assign to the built image.
  • f: specifies the name of the Dockerfile to use for building the image.
  • -build-arg: passes build-time variables to the Dockerfile.
  • -no-cache: forces Docker to build the image from scratch, without using any cached layers.
  • -pull: forces Docker to pull the latest version of the base image before building the new image.

Here is an example command that uses some of these options:

docker build -t myimage:latest -f Dockerfile.prod --build-arg ENVIRONMENT=production --no-cache --pull .

In this example, we specify a different Dockerfile (Dockerfile.prod) to use for building the image, and pass a build-time variable (ENVIRONMENT) to the Dockerfile using the --build-arg option. We also specify the --no-cacheoption to force Docker to build the image from scratch, and the --pulloption to ensure that we are using the latest version of the base image.

Docker Image , Dockerfile and Docker Container

Think of a Docker image as a recipe for a dish. Just as a recipe lists all the ingredients and instructions for making a dish, a Docker image lists all the dependencies and configurations required for running an application. When you run a Docker image, it creates a Docker container, just as when you cook a dish using a recipe, it creates a serving of the dish.

Now, let's compare this to a Dockerfile. A Dockerfile is like a chef's notebook that lists all the steps and ingredients required for creating a recipe. In the same way, a Dockerfile lists all the steps and configurations required for creating a Docker image. Just as a chef can use their notebook to create different versions of a dish, a developer can use a Dockerfile to create different versions of a Docker image.

To summarize, a Docker image is like a recipe, a Docker container is like a serving of the dish made from the recipe, and a Dockerfile is like a chef's notebook that lists all the steps and ingredients required for creating the recipe.

Dockerfile

A Dockerfile is a text file that contains instructions for building a Docker image. The Dockerfile defines how the Docker image should be built and what should be included in it. Here are the components of a Dockerfile:

  1. Base Image: A Dockerfile starts with a base image, which serves as the foundation for your application. The base image is the starting point for building your Docker image. You can choose from a variety of base images available on Docker Hub or create your own custom base image.
  2. Maintainer: The MAINTAINER instruction allows you to specify the author and email of the person who created the Dockerfile. This is an optional field but it's a good practice to include it.
  3. RUN: The RUN instruction is used to execute commands during the Docker build process. For example, you can use the RUN instruction to install dependencies, configure the environment, and run tests.
  4. COPY: The COPY instruction is used to copy files from the host machine to the Docker image. This is useful for copying your application code, configuration files, and other assets.
  5. WORKDIR: The WORKDIR instruction sets the working directory for subsequent instructions. This is useful for ensuring that your application is running in the correct directory.
  6. EXPOSE: The EXPOSE instruction is used to specify the port on which your application listens. This does not actually publish the port, it only documents it. To publish the port, you need to use the -p flag when running the container.
  7. CMD: The CMD instruction specifies the default command that should be run when the container starts. This is usually the command that starts your application.
  8. ENV: The ENV instruction is used to set environment variables in the Docker image. This is useful for configuring your application.
# Base image
FROM node:14-alpine

# Set working directory
WORKDIR /app

# Copy package.json and package-lock.json to the working directory
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy source code to the working directory
COPY . .

# Expose port 3000
EXPOSE 3000

# Define a named volume to persist data
VOLUME /app/data

# Set environment variables
ENV NODE_ENV=production
ENV PORT=3000

# Start the application
CMD ["npm", "start"]

This Dockerfile uses the official Node.js 14 Alpine base image and sets the working directory to /app. It then copies the package.json and package-lock.json files to the working directory and installs the dependencies using npm install.

Next, it copies the source code to the working directory and exposes port 3000 using the EXPOSE command. It defines a named volume called data that will be used to persist data even if the container is stopped and removed.

The ENV command sets two environment variables, NODE_ENV and PORT, and the CMD command specifies that the application should be started using npm start.

With this Dockerfile, you can build a Docker image that includes your Node.js application and all its dependencies. You can then run the container using the docker run command and mount the named volume to persist data, like this:

docker run -d -p 3000:3000 --name my-app -v data:/app/data my-image

This will start the container, map port 3000 on the container to port 3000 on the host machine, mount the named volume datato the /app/datadirectory inside the container, and name the container my-app.

Multiple env ? i am confused 😕

The --envoption in docker run and the ENV instruction in the Dockerfile serve the same purpose, which is to set environment variables for the container.

However, there are a few differences between them:

  1. -env is used at runtime, while ENV is used at build time.
  2. When using -env, you can specify the environment variables as key-value pairs in the command-line, while in ENV, you specify them as environment variable assignments in the Dockerfile.
  3. The values set with -env can override the values set in the Dockerfile with ENV. This means that if the same environment variable is set with both methods, the value set with -env takes precedence.

For example, let's say we have the following Dockerfile:

FROM alpine:latest

ENV MY_VAR=value

CMD ["sh"]

If we build the image with docker build -t my-image ., the MY_VAR environment variable will be set to value in the image.

However, if we run a container from the image with docker run --env MY_VAR=new-value my-image, the MY_VAR environment variable will be set to new-value instead of value.

In summary, both --env and ENV are used to set environment variables in Docker containers, but --env is used at runtime and can override values set in the Dockerfile with ENV.

--build-arg

-build-arg is a Docker CLI option that allows you to pass build-time variables to a Dockerfile. These variables can be used in the Dockerfile to customize the build process.

When you use --build-arg option, you can specify the value of the argument by passing it on the command line, like this:

docker build --build-arg VARIABLE_NAME=VALUE .

In the Dockerfile, you can reference the build-time variable using the syntax ${VARIABLE_NAME} . For example:

FROM alpine
ARG VARIABLE_NAME
ENV ENV_VARIABLE ${VARIABLE_NAME}

In this example, we are using the ARG instruction to define the VARIABLE_NAME build-time variable. We are then using the ENV instruction to define an environment variable called ENV_VARIABLE and setting its value to the value of the VARIABLE_NAME build-time variable.

Using --build-arg is useful when you want to pass dynamic information to the Docker build process, such as version numbers, build paths, or other configuration options. By using build-time variables, you can make your Docker builds more flexible and reusable.

docker push

docker push is a command used to push a Docker image from a local machine to a Docker registry. A Docker registry is a service that stores and distributes Docker images. Docker Hub is a public Docker registry that allows users to store and share Docker images.

To push an image to a registry, you must first tag it with the registry name and repository name. For example, if you want to push an image with the tag myimage to the Docker Hub repository named myrepo, you would run the following command:

docker tag myimage myusername/myrepo:myimage

This command tags the image with the registry name myusername, the repository name myrepo, and the tag myimage.

Once you have tagged the image, you can push it to the Docker Hub repository using the docker push command:

docker push myusername/myrepo:myimage

This command uploads the image with the tag myimage to the Docker Hub repository myrepo under the username myusername.

Note that you must be logged in to the Docker registry before you can push an image. You can use the docker login command to log in to a registry using your Docker Hub credentials.

docker tag

The docker tagcommand is used to create a new tag for an existing Docker image.

The basic syntax of the docker tag command is as follows:

docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

Here, SOURCE_IMAGEis the name of the source Docker image, and TARGET_IMAGEis the name of the new image you want to create. You can optionally specify a tag for both the source and target images.

For example, let's say you have an image named myimagewith the tag latest. To create a new tag for this image, you can run the following command:

docker tag myimage:latest myrepo/myimage:v1.0

This command will create a new tag named v1.0for the myimageimage, and will associate it with the myrepo repository on your Docker registry.

You can also use the docker tagcommand to rename an image. For example, if you want to rename the myimage image to mynewimage, you can run the following command:

docker tag myimage mynewimage

This will create a new image named mynewimage that is identical to the myimage image.

In summary, the docker tag command is a useful tool for managing your Docker images and providing additional names or versions for your images.

docker pull

The docker pull command is used to download Docker images from a registry. It allows you to fetch a specific version of an image or the latest version of the image. The basic syntax of the docker pullcommand is as follows:

docker pull [OPTIONS] NAME[:TAG|@DIGEST]

Here, OPTIONSare any additional options that you want to pass to the docker pullcommand. NAMEis the name of the Docker image that you want to download. You can also specify the image tag using the :TAGsyntax or the image digest using the @DIGESTsyntax.

For example, to download the latest version of the nginximage from Docker Hub, you can use the following command:

docker pull nginx

To download a specific version of the nginximage, you can specify the image tag as follows:

docker pull nginx:1.20.1

This will download version 1.20.1 of the nginximage.

OPTIONS

Here are some common options for docker pull command:

  • -all-tags or a: By default, docker pull pulls only the latest image. This option pulls all the available tags for the specified image repository.
  • -disable-content-trust: By default, Docker verifies the authenticity of the image with Notary. This option disables this check.
  • -platform: This option specifies the platform for the pulled image. This is useful for multi-architecture images.
  • -quiet or q: This option only displays the image ID of the pulled image.
  • -registry-mirror: This option specifies a registry mirror to use for pulling the image.
  • -no-trunc: By default, docker pull truncates the image ID to the first 12 characters. This option displays the full length of the image ID.

Example usage:

# Pull the latest version of an image
docker pull myimage:latest

# Pull all available tags for an image repository
docker pull --all-tags myimage

# Disable content trust verification
docker pull --disable-content-trust myimage

# Pull a specific platform for a multi-architecture image
docker pull --platform linux/amd64 myimage

# Only display the image ID
docker pull --quiet myimage

# Use a registry mirror to pull the image
docker pull --registry-mirror=https://myregistrymirror.com myimage

# Display the full length of the image ID
docker pull --no-trunc myimage

A Docker registry mirror is a server that caches and serves Docker images from a remote registry such as Docker Hub. When you pull an image, Docker first checks the local registry mirror for the image, and if it's not found, it pulls the image from the remote registry. This can improve the speed of pulling images, especially in situations where multiple users or systems need to pull the same images repeatedly. This only applies if you are using --registry-mirror option to pull the image.

Notary is an open-source project that provides a trust framework for signing and verifying container images. It helps ensure the integrity and authenticity of container images by allowing you to digitally sign them and store the signature in a trusted registry.