Docker Fundamentals Explained In 10 Minutes

Welcome to this new article on Docker interview questions. In this specific piece, we'll be focusing on Docker fundamentals. We'll be talking about the traditional deployment approach before containers became popular and we'll also talk about the challenges with the traditional deployment process. After that, we will get into containers. We will look at how you can create container images, how you can push your container images to a container registry, and how you can run your container images as well. Once we understand that process, we'll explore the advantages of using Docker and compare virtual machines with containers. We will break down the important components in a container architecture, discussing what a container registry, container repository, container runtime, and a few other similar concepts are. At the end, we will conclude this specific article by talking about why containerization is important for DevOps and microservices.

The Traditional Deployment Approach

Before we dive into Docker, let's start by discussing the traditional deployment approach before containers were widely used.

The traditional deployment approach was based on deployment documents. You had detailed deployment documents for various kinds of applications. These documents contained instructions on what kind of hardware you would need, what kind of operating system needed to be installed for a specific application—whether it's Linux, Windows, or Mac—and what kind of software was needed. For example, to run a Java application, you need to install Java. To run a Python application, you need to install Python, and so on.

In addition to running your applications, you need application dependencies. How these application dependencies could be set up was also described inside the deployment document. Finally, once you had all the dependencies ready, you could go ahead and install your application. Everything was explained in a document.

So, the developers, designers, or the architect team would create a deployment document describing everything needed to deploy a specific application in a specific environment and hand it over to the operations team. A member of the operations team would then pick up the document and manually set up the environment and deploy the application. This was the process used before the emergence of containers or Docker.

This setup needed to be done in multiple environments. Typically, most applications have several environments, such as a dev environment, a QA environment, a stage environment, and a production environment. The entire process needed to be repeated in each one. In each of these environments, you might have multiple instances. For example, if you have a lot of users for your application, you might have hundreds or even thousands of instances of your application running in production. This setup needed to be done for all those instances.

Traditionally, we were using document-based deployment. A document detailed the steps for creating environments and deploying software. This document described: * Hardware Setup: How to prepare the physical servers or virtual machines. * OS Installation: Which OS to install and what configurations to use for the operating system. * Software Installation: How to set up essential software like Java, Python, or Node.js, which version to use, and how to configure it. * Dependency Configuration: The application needs numerous dependencies and libraries. How to install them was also described in the document. * Application Deployment: The final application deployment steps were also described inside the specific document.

Challenges with the Traditional Deployment Process

You might be wondering what the challenges were with the traditional deployment process. It had multiple setup steps: you needed to set up the operating system, software, and dependencies, and this needed to be repeated across multiple environments and instances, which could be tedious.

  • Time-Consuming Process: Manually deploying these steps across multiple environments (dev, QA, stage, prod) and across multiple instances could take a lot of effort.
  • Lack of Automation: Everything was done manually. The traditional methods lacked automation.
  • High Chance of Mistakes: There were numerous manual steps, and each application might have separate documents, which meant an increased likelihood of errors. There was a high chance of somebody making a mistake while installing the application, and these mistakes would repeat across different environments because you were manually doing it multiple times.
  • Inconsistent Deployment Processes: Another big challenge was that the deployment process was different for different applications. You had different documents describing the installation instructions for different applications. For example, how you deploy a Java application versus a Python or JavaScript application is very different. Each application, depending on its type, needed different kinds of setup instructions, and therefore the deployment team needed to be aware of the language specifics as well. They needed to know, "Hey, if it's a Java application, it needs to be installed this way. If it's a Python application, it needs to be installed this way." This increased the knowledge required and the likelihood of errors, all while performing everything manually.

So, how does Docker solve this?

The Docker Way: Creating and Deploying a Container Image

Let's talk about the sequence of steps in creating and deploying a container image and the advantages of using Docker. We'll get a high-level overview of how you do things with Docker.

The sequence of steps in creating and deploying a container image is as follows:

  1. Build a Container Image: The first step is to build a container image. A container image contains everything you would need to run your application—your application code, dependencies, and application runtime. There are multiple ways of creating your Docker images, which we will look at later.
  2. Push the Image to a Container Registry: Once you build an image, you need to share it with others. You would push it to a container registry. After a developer creates an image, they can share it with the operations team by pushing the image to a container registry. You upload your created image to a registry for later use. The developer would build a container image, test it in the local environment, and then push it to the container registry.
  3. Deploy in Your Environment: The last step is to deploy it in your specific environment, be it dev, QA, stage, or production. To do that, you would perform a "pull" of the image from the registry and run the application on the specific environment. Irrespective of the environment—dev, QA, stage, production, local, or cloud—the way you run the application remains the same.

Advantages of Using Docker

Now you might be wondering, what is the advantage of using a process like this? What are the advantages of using containers or Docker?

  • Standardized Deployment Package: The container image you're creating is a standardized packaging approach. Irrespective of the application within the container image, the image format itself is standardized. The packaging approach is the same regardless of the programming language used to build the application. Whether you have a Python, Java, NodeJS, or a machine learning application, the packaging approach remains the same.
  • Standardized Deployment Process: Because the package is standardized, you can also standardize the deployment process. You can run the container image the same way, irrespective of whether it contains a Python, machine learning, web, or Java application.
  • Standardized Operations: Once you have a container image, you don't need to worry about having different processes for getting logs, metrics, and traces. Irrespective of whether it's a Java or Python application, once it's a container, you can collect logs, metrics, and traces the same way.
  • Lightweight Alternative to Virtual Machines: Containers are lightweight alternatives to virtual machines. They use fewer resources compared to VMs, making them faster and more efficient.

Virtual Machines vs. Containers: A Detailed Comparison

Let's compare virtual machines and containers.

Architecture

  • Virtual Machines (VMs): VMs are completely virtualized computers with their own OS running on a hypervisor. A hypervisor enables you to create multiple virtual machines on the same physical hardware. Each virtual machine acts as its own computer with its own copy of the operating system.
  • Containers: Containers are lightweight, sharing the host OS core and running using a container engine. You install the Docker engine on the server, and each container shares the host operating system, so they don't have a complete OS with them.

Performance

  • VMs: Resource-intensive because each VM contains a full operating system.
  • Containers: Lightweight as they share the host operating system.

Boot Time

  • VMs: Slower boot time because the entire operating system needs to start up.
  • Containers: Faster boot time because the container shares the host OS with other containers.

Resource Usage

  • VMs: Consume more CPU, memory, and storage since they have their own OS copy.
  • Containers: More efficient resource usage.

Image Size

  • VMs: Larger image size because it includes the full OS.
  • Containers: Smaller image sizes because they only contain app dependencies and the application runtime.

Security

  • VMs: Generally stronger security because they have a separate operating system for each virtual machine.
  • Containers: They have a shared host OS kernel, but the container engine tries to isolate the processes for each container to ensure one container does not impact another.

Core Components of Container Architecture

Let's talk about the important components in container architecture.

Container Image

A package representing a specific version of your application, containing everything you need to run it. It’s a complete package that includes the operating system, application runtime, application code, and dependencies. It enables a consistent deployment approach, ensuring your app runs the same way everywhere, from your local machine to your environments in the cloud.

Container Engine

The core software that runs and manages containers. When you execute a command like docker run <image_name>, the container engine creates a container from the specified image. It also ensures that each container running on it is isolated.

Container Registry

A storage location for your container images. This is where you push your container images so they can be shared. This enables collaboration and facilitates easy distribution of container images across teams. It also enables integration with CI/CD systems and acts as a discovery mechanism for official and community images. Popular examples include: * Docker Hub * Amazon Elastic Container Registry (ECR) * Google Artifact Registry * Azure Container Registry

Container Repository

A collection of container images for a specific application. So, what's the difference between a container registry and a container repository? A container registry contains multiple container repositories.

In a container registry, you have numerous repositories, each assigned to a specific application (e.g., microservice A, microservice B). For each of these microservices, you might have multiple container images for different versions (v1.0, v1.1, v1.2). This collection of images for a specific application is what constitutes a repository. A repository is tied to a specific application, like a MySQL repository or a repository for microservice A.

Why is Containerization Important for DevOps and Microservices?

Containerization started getting popular in the same time frame as DevOps and microservices. Why is that?

Containerization is the packaging of an app and its dependencies into one container image. The goal is to run the application the same way everywhere. Popular tools include Docker and alternatives like containerd and runC.

Here’s why it’s crucial for DevOps: * Consistent Environments: You have the same setup from dev to production, which solves the "it works on my machine" problem. * Simplified Deployment: It enables faster setup with no need to install libraries manually, leading to fewer errors and issues. * Promotes Automation: You can automate the creation of images and integrate them easily with CI/CD tools like Jenkins or GitHub Actions. * Orchestration: You can use tools like Kubernetes to manage containers at scale.

Here’s why it’s crucial for Microservices: * Simplified Architectures: You can create microservice-specific container images for each service. This gives you the same process for all the different technologies you might be using. The way you create, deploy, and monitor the image remains the same, regardless of the language (Java, Python, NodeJS) or application type (web, machine learning). * Scalability: You can easily scale containers up and down using orchestration tools like Kubernetes based on load. * Platform Neutral: Once you build an image, you can run it anywhere—on a local server or in any cloud (AWS, Azure, Google Cloud). * Lightweight and Efficient: Containers share the host OS, so they are resource-efficient, have a faster startup, and allow for higher density on the same hardware compared to virtual machines.