Abílio Azevedo.

Docker

Cover Image for Docker
Abílio Azevedo
Abílio Azevedo

Docker

Docker emerged in 2013 as an open source tool to facilitate the creation and deployment of applications in containers, solving many common problems related to consistency and portability in development and production environments.

Before Docker, it was very difficult to ensure that a development environment was identical to the production environment. This caused the famous "works on my machine" problems. In addition, bringing up new environments and provisioning machines was slow and tedious.

Docker Born

Docker solves these problems by packaging applications along with their dependencies into isolated and portable containers. This ensures consistency and speed of deployment, allowing developers to work locally in the same way that the application is deployed in production.

The Docker company creates products to monetize around the Docker ecosystem.

Docker Engine

  • Build Images - Docker Build (Build Kit)
  • Distribute the Images
  • Run Containers - Containerd (Docker Runtime)
  • Communication between Containers - Network
  • File Sharing - Volumes
  • Orchestration between Containers

Docker Engine

This Engine runs through the Docker Desktop application which was made for Linux. So on Mac and Windows it runs on a Linux virtual machine to run the Docker Engine.

But it is also possible to run it through other applications such as colima.

Virtual Machines

Virtual machines (VMs) are an abstraction of physical hardware that turns one server into multiple servers. The hypervisor allows multiple VMs to run on a single machine. Each VM includes a full copy of an operating system, the application, necessary binaries and libraries – taking up tens of GBs. VMs can also be slow to boot up and maintaining (updating) them is more costly.

Virtual Machines

Containers

Containers are an evolution/optimization of virtual machines. They are an application layer abstraction that packages code and dependencies. Multiple containers can run on the same machine and share the kernel with other containers, each running as isolated processes in user space. Containers take up less space than VMs (container images are usually tens of MBs), can handle more applications and require fewer VMs and operating systems.

Containers

Run a new container:

docker run hello-world

Show all containers that are running and with the "-a" flag (all) also shows those that are not currently running.

docker ps -a

Docker PS -a

Run a command inside the container iteratively (flag -i), with a communication interface (flag -t for teletype terminal or tty) and using root user (flag -u):

docker run -it -u root ubuntu bash

Download images from Docker Hub:

docker pull nginx

Redirect container ports to ports on your machine:

docker run -p 8080:80 nginx

NOTE: Using higher ports on the computer so you don't have to run SUDO admin command. The first port is the one exposed to the computer and the second is the container.

Execute a command in a running container:

docker exec container_name ls 

The --rm flag in Docker is used to automatically remove the container when it exits. Here is an example running a Docker container with --rm:

docker run --rm ubuntu echo "Hello World"

docker run:

  • Runs a command in a new container. This creates a new container instance from the given image and prepares it for running the given command. Once the command exits, the container stops.
  • Essentially docker run runs a new, one-off container for running the specified command. The container is temporary and removed after running.

docker exec:

  • Runs a command in an existing running container. This allows you to execute additional commands or a new interactive shell inside a running container.
  • The container keeps running after the docker exec command completes. It does not create a new container, it simply lets you issue more commands to an already running container.

Volumes

When a container dies we lose the changes inside it, because when it is reassembled, it follows the image specifications. To work around this, we use shared volumes.

Share volumes between the container (/usr/share/nginx/html) and your machine (current_folder/html):

docker run -p 8080:80 -v $(pwd)/html:/usr/share/nginx/html nginx

Dockerfile

The Dockerfile is a recipe for building automatic Docker images. It allows you to define a standardized and immutable environment for applications, ensuring that they are always run the same way, regardless of where they are deployed. We always start from a base image. The larger your image, the slower it will take to upload your container.

DockerFile

We can add a stage to the build steps:

# Base image
FROM node:16-alpine AS base

# Install dependencies apenas nesta etapa
WORKDIR /app 
COPY package*.json ./
RUN npm install

# Stage de build
FROM base AS build
COPY . .
RUN npm run build

# Stage de produção 
FROM node:16-alpine 
WORKDIR /app

# Copia apenas o necessário 
COPY --from=build /app/package*.json ./
COPY --from=build /app/dist ./dist  

# Use o usuário padrão node
USER node

CMD [ "node", "dist/index.js" ]

And run with the --target build flag to go to the desired stage:

docker build --target build2

It is important to define the USER to restrict image user permissions. If there is volume synchronization, it is a good idea to set the same USER that you use in development on your machine with the container's USER so you don't need to change files with SUDO.

Image

It is the container template, it is the descriptive of the container instance. It is like a class (specification) of the container.

Build an image based on the Docker File:

docker build -t kibolho/nginx:latest .

Docker Hub

Docker Hub is a public registry of Docker images. It allows developers to share and reuse components already built by others, avoiding the need to create everything from scratch. You can pull existing images or upload your own images created locally. Always use reliable/verified images.

Upload an image to Docker Hub

docker push kibolho/nginx:latest 

Docker Compose

Docker Compose facilitates the definition and execution of multi-container Docker applications (orchestrator). With Compose, you can configure your services in YAML files and deploy your entire stack with a single command. This accelerates development and deployment workflows.

The docker-compose.yml file that orchestrates a MySQL container:

version: '3'  

services:   
  mysql:
    image: mysql:8
    container_name: mysql  
    restart: always
    environment:  
      MYSQL_ROOT_PASSWORD: root  
      MYSQL_DATABASE: app   
    ports:    
      - 3306:3306   
    volumes:    
      - ./mysql:/var/lib/mysql  

We can map a container volume folder to a host folder:

volumes:
   - ./mysql:/var/lib/mysql  

Or we can just persist in the container without bind to a host folder (anonymous volume):

volumes:
   - /opt/node_app/app/node_modules

Also, we can use a :delegated flag that tells Docker to delegate management of this volume mount to the container runtime rather than Docker itself. This allows some performance optimizations.

volumes:
      - ./app:/opt/node_app/app:delegated

To run the containers described in the docker-compose.yml file in the background (flag -d):

docker compose up -d

To stop running the containers described in the docker-compose.yml file:

docker compose stop  

To destroy the containers described in the docker-compose.yml file:

docker compose down  

Compability Issues

Even if emulated, some x86 images have compatibility issues running on the Arm-based Apple silicon because they contain x86 instructions that cannot be executed on Arm. This depends on the specific app behavior.

The ecosystem is still catching up in terms of providing images with arm64 variants.

A temporary solution is using the platform key in docker-compose.yml, as it allows you to define alternative arm64 compatible images specifically for M1 devices. This avoids emulation and architecture issues.

version: '3'

services:

  app:
    image: mysql:8  
    platform: linux/amd64

Conclusion

In summary, Docker revolutionized the development and deployment of applications by introducing a standardized and portable way of packaging environments. Features like Dockerfile, Docker Compose and Docker Hub accelerate workflows and promote reuse, collaboration and consistency.


More posts

Cover Image for CI/CD - Lint - Checks

CI/CD - Lint - Checks

Comprehensive guide to continuous integration and continuous delivery. Explains key concepts, tools like GitHub Actions and Drone, benefits of linting, integrating ESLint and Prettier, and using Git hooks for automation.

Abílio Azevedo
Abílio Azevedo
Cover Image for ReWork

ReWork

Unconventional, straight-to-the-point advice on how to run a business. Instead of focusing on fast growth, Rework encourages entrepreneurs to start small, stay agile, and focus on what's essential.

Abílio Azevedo
Abílio Azevedo

NewsLetter

I will send the content posted here. No Spam =)