Docker

What is Containerization?

Containerization is a lightweight alternative to full machine virtualization that involves encapsulating an application in a container with its own operating environment. It offers the flexibility to run an application on any physical machine without worrying about dependencies.

Benefits of Containerization

  1. Portability: Containerized applications can be deployed easily across various environments.

  2. Resource-efficient: Containers require less system resources compared to traditional virtual machines as they share the host system's kernel.

  3. Isolation: Each container provides an isolated environment for applications, minimizing potential conflicts and security risks.

Containerization vs Virtualization

While both containerization and virtualization allow for running multiple applications or services in isolated environments on a single host, there are some key differences:

  • Virtual Machines: A VM is a software emulation of a physical computer, running an entire operating system stack and the applications on top of it. It can run multiple instances of different operating systems.

  • Containers: Containers share the host system’s OS kernel and do not require an OS per application, making them lightweight and fast.

How does Containerization Work?

Containerization works by encapsulating an application and its dependencies into a "container" that can run almost anywhere the appropriate container runtime is present.

Container Runtime

The container runtime is the software that executes containers and manages container images on a machine. It isolates the application processes from the rest of the system.

Container Engine

A container engine is a layer that uses the container runtime to orchestrate container operations. An example of a container engine is Docker Engine.

The Role of Images in Containerization

An image is a lightweight, stand-alone, and executable software package that includes everything needed to run a piece of software, including the code, runtime, system tools, system libraries, and settings. Containers are instances of Docker images that can be run using Docker command-line or API.

Introducing Docker

Docker Overview

Docker is an open-source platform that automates the deployment, scaling, and management of applications by isolating them into containers. It was designed to make it easier to create, deploy, and run applications by using containers.

Docker Architecture

Docker follows a client-server architecture. The Docker client communicates with the Docker daemon, which does the heavy lifting of building, running, and distributing your Docker containers. The Docker client and daemon can run on the same host, or you can connect a Docker client to a remote Docker daemon.

Here is a simple breakdown of the architecture:

  • Docker Daemon: Listens for Docker API requests and manages Docker objects such as images, containers, networks, and volumes.

  • Docker Client: Docker users interact with Docker through a client by issuing commands.

  • Docker Images: Read-only templates to build containers from.

  • Docker Containers: Running instances of Docker images.

Installing Docker

To install Docker, you can follow the instructions provided on the official Docker website for different operating systems. The steps usually involve downloading the Docker installer and following through the installation wizard.

Here's an example of how to install Docker on a Ubuntu system:

# Update your existing list of packages
sudo apt update

# Install a few prerequisite packages which let apt use packages over HTTPS
sudo apt install apt-transport-https ca-certificates curl software-properties-common

# Add the GPG key for the official Docker repository to your system
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# Add the Docker repository to APT sources
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

# Update the package database with the Docker packages from the newly added repo
sudo apt update

# Make sure you are about to install from the Docker repo instead of the default Ubuntu repo
apt-cache policy docker-ce

# Install Docker
sudo apt install docker-ce

Basic Docker Syntax

Docker has its own command-line syntax for managing containers and images. Here are some basic Docker commands that you will frequently use:

  • docker run: This command is used to create and start a container. For example, docker run hello-world will create and start a "hello-world" container.

  • docker pull: This command is used to pull an image from a Docker registry (like Docker Hub). For example, docker pull ubuntu will pull the Ubuntu image.

  • docker ps: This command is used to list running containers. If you want to see all containers (running and stopped), you can use docker ps -a.

  • docker rm: This command is used to remove one or more containers. For example, docker rm my_container will remove a container named "my_container".

  • docker rmi: This command is used to remove Docker images. For example, docker rmi ubuntu will remove the Ubuntu image.

Docker Registry

A Docker registry is a place where Docker images are stored. Docker Hub is the default registry where Docker looks for images. Docker users can pull images from a registry to deploy and run containers and push images they've built themselves to a registry to share with others.

Here's a basic command to pull an image from Docker Hub:

docker pull ubuntu:latest

In this command, 'ubuntu' is the name of the image, and 'latest' is the tag specifying the version of ubuntu.

Getting Hands-on with Docker

Running Your First Container

After you've installed Docker and learned some basic commands, it's time to run your first Docker container. Let's use the Docker run command to start a container from the hello-world image:

docker run hello-world

This command does a few things:

  1. Checks for the hello-world image locally.

  2. If the image is not present, Docker pulls it from the Docker Hub.

  3. Creates a new container from that image.

  4. Runs the container.

Once executed, you should see an output message from the hello-world program.

Introduction to Dockerfiles

A Dockerfile is a text file that Docker reads in from top to bottom. It contains a series of commands that Docker uses to assemble an image. Here is a simple Dockerfile:

# Use an existing docker image as a base
FROM alpine

# Download and install a dependency
RUN apk add --update redis

# Tell the image what to do when it starts as a container
CMD ["redis-server"]

This Dockerfile does the following:

  1. Starts with the alpine base image.

  2. Runs the apk add --update redis command which installs Redis on our image.

  3. Sets the default command as redis-server.

You can build an image from this Dockerfile using the docker build command:

docker build .

The . tells Docker to look for the Dockerfile in the current directory.

Advanced Docker Concepts

Docker Compose

Docker Compose is a tool for defining and managing multi-container Docker applications. It allows users to launch, execute, communicate, and close containers with a single coordinated command.

Here's an example of a docker-compose.yml file:

version: '3'
services:
  web:
    build: .
    ports:
      - "5000:5000"
  redis:
    image: "redis:alpine"

This Docker Compose file does two things:

  1. Sets up a single app service which will run from the Dockerfile in the current directory, and listens on port 5000.

  2. Sets up a Redis service using the default Redis image from Docker Hub.

You can run this application using the docker-compose up command.

Docker Socket

The Docker socket is not a command but rather a pathway for communication between the Docker daemon and the Docker CLI.

Here's how you can use the Docker socket to run a command inside a container:

docker run -v /var/run/docker.sock:/var/run/docker.sock -it alpine sh

This mounts the Docker socket located at /var/run/docker.sock on the host, inside the container at the same location. With this setup, the Docker CLI inside the container can communicate with the Docker daemon running on the host machine.

The Docker socket is a powerful tool but should be used with caution as it can pose security risks if not handled properly. Essentially, any user or process that has access to the Docker socket has full control over the Docker daemon, and thus potentially over the host system.

Mastering Docker Commands and Syntax

In this section, we'll delve deeper into Docker commands and their syntax. Understanding these commands is essential to using Docker effectively. We will also provide examples and demonstrations to enhance your understanding.

Working with Docker Containers

Docker containers are running instances of Docker images. Here are some common commands for working with containers:

Listing Containers

To list all running Docker containers, you use the docker ps command.

docker ps

To list all containers, including those that have exited, use the docker ps -a command.

docker ps -a

Stopping Containers

To stop a running container, use the docker stop command followed by the container ID or name.

docker stop container_id

Removing Containers

To remove a container, it must be stopped first. Once the container is stopped, you can remove it using the docker rm command.

docker rm container_id

Working with Docker Images

Docker images are read-only templates used to build containers. They are built from Dockerfiles and contain a snapshot of an application's code and dependencies. Here are some common commands for working with Docker images:

Listing Images

To list all Docker images stored locally on your machine, use the docker images command.

docker images

Pulling Images

To download an image from a registry like Docker Hub, use the docker pull command followed by the name of the image.

docker pull image_name

Removing Images

To remove a Docker image, use the docker rmi command followed by the image ID or name.

docker rmi image_name

Building Docker Images

Building a Docker image involves creating a Dockerfile with a specific set of instructions and then running the docker build command.

Here's a sample Dockerfile:

# Use an existing docker image as a base
FROM node:14

# Set working directory
WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm install

# Copy app source
COPY . .

# Expose port and start application
EXPOSE 8080
CMD ["npm", "start"]

And here's how you build an image from this Dockerfile:

docker build -t my-app:1.0 .

In the above command, -t my-app:1.0 specifies the name (my-app) and tag (1.0) for the image, and . tells Docker to use the Dockerfile in the current directory.

Docker Networks

Docker networking allows containers to communicate with each other and with the outside world. By default, Docker provides three network drivers:

  • bridge: The default network driver. If you don’t specify a driver, this is the type of network you are creating.

  • host: Removes network isolation between the container and the Docker host, and use the host’s networking directly.

  • none: No networking in this mode.

You can list all networks by using the docker network ls command.

docker network ls

You can create a network using the docker network create command.

docker network create my_network

Then, you can attach a network to a container at runtime using the --network option with the docker run command.

docker run -d --name=my_container --network=my_network my_image

Building Custom Docker Images

In this section, we'll learn about Dockerfiles and how to use them to create our own custom Docker images. A Dockerfile is essentially a set of instructions Docker uses to build an image.

Understanding Dockerfile Syntax

A Dockerfile is composed of various instructions, each performing a specific task. Let's go through the basic instructions:

  • FROM: This instruction initializes a new build stage and sets the base image. The base image is the image that is used to build all subsequent layers for your new image. For example, you might use FROM python:3.8 to use Python 3.8 as your base image.

  • RUN: This instruction executes any commands in a new layer on top of the current image and commit the results.

  • COPY: This instruction copies new files from the source on the host and adds them to the filesystem of the container at the destination path.

  • WORKDIR: This instruction sets the working directory for any instructions that follow it in the Dockerfile.

  • CMD: This instruction provides defaults for an executing container.

Here is an example of a simple Dockerfile:

# Use an existing docker image as a base
FROM node:14

# Set the working directory
WORKDIR /app

# Copy the dependencies file to the working directory
COPY package.json .

# Install the application dependencies
RUN npm install

# Copy the rest of the application code
COPY . .

# Set a command to run your application
CMD ["npm", "start"]

Building an Image from Dockerfile

Once you have a Dockerfile, you can use the docker build command to build a Docker image from it.

For example, if you're in the same directory as the Dockerfile, you can issue this command:

docker build -t my-custom-image:1.0 .

This command tells Docker to build an image using the Dockerfile in the current directory (that's the .) and tag (-t) the new image as my-custom-image:1.0.

Running a Container from Your New Image

After the build process is complete, you can start a new container from your new image with docker run:

docker run -d -p 3000:3000 my-custom-image:1.0

This command tells Docker to run a new container in detached mode (-d) from the my-custom-image:1.0 image, and map port 3000 of the container to port 3000 on the host machine (-p 3000:3000).

By building your own Docker images, you can ensure that your applications run in the same environment regardless of where they are deployed.

This uniformity can greatly simplify development, testing, and deployment processes. It allows you to encapsulate your application and its dependencies into a single self-contained unit that can run anywhere Docker is installed.

Using Docker Compose

While Docker itself is great at managing individual containers, Docker Compose is designed to manage applications that consist of multiple containers. For example, you might have a web application that relies on a separate database server. With Docker Compose, you can define both the application and the database server as services in the same file, and manage them as a single entity.

Here's an example docker-compose.yml:

version: "3.7"
services:
  web:
    build: .
    ports:
      - "5000:5000"
  db:
    image: "postgres:latest"

In this example, two services are defined:

  • web is built from the Dockerfile in the current directory, and its exposed port 5000 is mapped to port 5000 on the host.

  • db is based on the postgres:latest image, and doesn't have any exposed ports.

To start the application, you can use the docker-compose up command:

docker-compose up

Docker Compose then starts containers for each service, and sets up the networking between them according to the configuration in the docker-compose.yml file. In this case, the web service can access the db service at the hostname db.

With Docker Compose, you can manage your multi-container applications with just a single command.

Docker Volumes

Docker volumes are the preferred way to handle persistent data created by and used by Docker containers.

Let's create a new volume:

docker volume create my_volume

Now, you can run a container with the volume attached:

docker run -v my_volume:/data ubuntu

In this command, my_volume is the name of the volume, and /data is the path where the volume is mounted in the container.

With Docker volumes, you can ensure that the data your application needs is available, even when the container itself is stopped or deleted.

Volumes have several advantages:

  • Data persistence: Data stored in a volume persists beyond the life of the container.

  • Data sharing: Volumes can be shared and reused between containers.

  • Storage customization: You can store volumes on remote hosts or cloud providers.

  • Backup and migration: You can easily backup volume data, or migrate it between systems.

Let's see how to use a volume with a Docker Compose file:

version: "3.7"
services:
  db:
    image: "postgres:latest"
    volumes:
      - "db_data:/var/lib/postgresql/data"
volumes:
  db_data:

In this example, a new volume db_data is created and mounted into the db container at /var/lib/postgresql/data. This is the location where Postgres stores its data.

Understanding Docker Security

Docker allows you to isolate applications in containers, but it's important to understand that Docker is not a security tool. You need to follow best practices to secure your Docker containers.

Here are some Docker security best practices:

  • Least privilege: Run your containers as a non-root user, and only give your containers the permissions they need.

  • Use official images: Official images are more likely to be free of vulnerabilities, and are often kept up-to-date.

  • Keep your images updated: Regularly update your Docker images for security patches.

  • Limit system calls: Use the --security-opt parameter to limit the system calls that a container can make.

  • Use network segmentation: Create separate Docker networks for different applications or parts of applications.

Docker provides a great deal of flexibility and power when it comes to deploying applications, but it's important to use that power responsibly. With a good understanding of Docker and best practices around its use, you can take full advantage of Docker while minimizing potential risks.

Docker Swarm Mode

As you start to work with multi-container applications, you may need a way to coordinate how those containers run and communicate. Docker Swarm is a native clustering and orchestration solution from Docker.

In Docker Swarm mode, you can manage a cluster of Docker nodes as a single virtual system. Here are some basic concepts of Docker Swarm:

  • Swarm: A swarm is a group of machines that are running Docker and joined into a cluster.

  • Nodes: A node is an instance of Docker that participates in the swarm.

  • Services: A service is the definition of the tasks to execute on the manager or worker nodes. It is the central structure of the swarm system.

  • Tasks: A task is a Docker container that runs on a node. It represents a running container which is part of a swarm service and managed by a swarm manager, unlike a standalone container.

To enable Swarm mode and make your current machine a swarm manager, use the docker swarm init command:

docker swarm init

To add a worker to this swarm, run the following command on the worker node:

docker swarm join --token SWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssmk743ojnwacrr2e7c 192.168.99.100:2377

In the command above, SWMTKN-1-... is a swarm token. Swarm tokens are used to join nodes to the swarm.

You can create a service using docker service create command:

docker service create --replicas 1 --name helloworld alpine ping docker.com

And you can inspect a service using docker service inspect command:

docker service inspect --pretty helloworld

Docker Swarm provides you with powerful capabilities for deploying and managing your multi-container Docker applications.

Remember, mastering Docker requires continuous learning and hands-on practice. As you gain more experience with Docker, you'll learn how to leverage its full power to make your applications more reliable and easier to develop, test, and deploy.

Last updated