Vai al contenuto

Introduzione a Docker

Pubblicato:

Una breve e veloce introduzione a Docker e tutte le funzionalità principali che offre, con alcuni esempi pratici. Insomma, una serie di motivi sul perché dovresti usarlo.

A cosa serve la virtualizzazione?

In generale le applicazioni si trovano ad interagire con il sistema operativo, l’hardware ed altri processi.

Se anche solo uno di questi elementi cambia, il software potrebbe non funzionare come previsto.

Macchine virtuali

Le Macchine virtuali (VMs) rappresentano una maniera efficace per migliorare la riproducibilità.

Usano un software estremamente complesso, l’hypervisor, per emulare fedelmente hardware e sistema operativo. Sono (quasi) completamente indipendenti dal sistema host su cui vengono eseguite.

Containers

I Containers sono una alternativa più leggera alle VMs.

Utilizzano alcune funzionalità specifiche del kernel Linux, ovvero namespaces e cgroups, per isolare completamente un processo dal resto del sistema. Proprio per questo non possono emulare un kernel o hardware diversi.

Docker

Docker è uno dei software di containerizzazione più popolari. Alcune alternative sono Podman o LXC.

Viene offerto anche un servizio di registry dove trovare immagini pre-costruite.

Installazione

Poiché Docker è stato progettato specificamente per il kernel Linux, non è nativamente supportato su Windows e macOS. Per poter essere eseguito su queste piattaforme è necessario avviare una macchina virtuale con Linux.

Docker Desktop gestisce tutte queste complicazioni in maniera trasparente.

Immagini

Un’immagine è un template di sola lettura con le istruzioni per creare un container Docker.

Sono composte da layers che vengono impilati l’uno sull’altro, come i commit di git.

Solo il layer superiore è scrivibile, mentre quelli inferiori sono immutabili e protetti da modifiche attraverso l’uso di hash.

Dockerfile

I Dockerfiles contengono le istruzioni per costruire un’immagine.

FROM python:3.9.7-slim-buster # Image di base

WORKDIR /app # Working directory (se non esiste viene creata)

COPY requirements.txt requirements.txt # Copia i file da hosta al container

RUN pip install -r requirements.txt # Esegue un comando nel container

COPY . . # Copia i file da host al container

ENTRYPOINT ["python", "main.py"] # Esegui un comando quando lanci il container

Costruire un’immagine

# Costruisce un'immagine dal Dockerfile nella directory corrente
# docker build -t <image-name>:<tag> <context>
# -t: assegna un tag all'immagine
# .: usa la cartella attuale come contesto
docker build -t my-image:latest .

Esegue un container

# Esegue un container da un'immagine
# docker run <image-name>:<tag>
# -d: esegue il container in modalità detached, ovvero in background
# -p: inoltra le porte del container all'host
# --name: nome del container
# --rm: rimuove il container non appena questo si ferma
docker run -d -p 5000:5000 --name my-container --rm my-image:latest

Memorizzazione dei dati

All’interno di un container, la memoria è effimera e viene distrutta quando il container viene rimosso.

Per fare in modo che i dati persistano si utilizzano volumi e bind mounts.

Trasferire file nel container

# COPY <src> <dest>
# ADD ha la stessa sintassi, ma <src> può essere un URL o un file tar
COPY requirements.txt requirements.txt
COPY . .
# docker run -v <host-path>:<container-path>
docker run -v $(pwd):/app

Porte

Le ports sono un modo per esporre all’esterno la rete e le socket del container.

Perché ciò avvenga, è necessario utilizzare la flag -p nel momento in cui si avvia il container.

Visualizzazione delle porte

Loading diagram...
# docker run -p <host-port>:<container-port> <image-name>
docker run -p 8000:80 my-web
docker run -p 3306:3306 my-database

Docker compose

Docker compose è lo strumento per definire e lanciare della configurazioni Docker con più container.

L’architettura viene descritta in maniera dichiarativa in un file YAML, suddiviso nei servizi che compongono l’applicazione. È possibile indicare anche volumi e reti, che verranno create automaticamente.

Persino quando si ha a che fare con un container singolo, avere un file docker-compose.yml permette di visualizzare e modificare facilmente la sua configurazione.

docker-compose.yml

version: "3.9" # Versione di docker compose

services: # Lista di servizi
  db: # Nome del servizio
    image: mysql # L'immagine viene presa da Docker Hub
    volumes:
      - db-data:/var/lib/mysql # Crea un volume e lo monta per memorizzare i dati
    environment: # Imposta le variabili d'ambiente
      MYSQL_ROOT_PASSWORD: example
      MYSQL_DATABASE: example
      MYSQL_USER: example
      MYSQL_PASSWORD: example
  web: # Nome del servizio
    build: . # Costruisce l'immagine dal Dockerfile nella directory corrente
    ports: # Elenco di porte da esporre
      - "5000:5000"
    volumes: # Lista dei volumi da montare.
      - .:/app # Bind mount della directory corrente
    depends_on: # Lista di servizi da avviare prima di questo
      - db

volumes: # List di volumi
  db-data: # Nome del volume

Esempi

Tre esempi di come usare Docker in contesti diversi:

Distribuire uno script Haskell

Hai uno script Haskell che vuoi distribuire ad altre persone. Loro potrebbero non avere l’intera toolchain Haskell installata o potrebbero non essere familiari con essa. Docker fornisce portabilità con facilità.

# https://hub.docker.com/_/haskell
FROM haskell:slim       # Immagine base
COPY main.hs .          # Copia lo script dentro la cartella di lavoro
RUN ghc -o main main.hs # Compila lo script
ENTRYPOINT ["./main"]   # Esegue lo script quando il container viene avviato
docker build -t my-haskell-script .   # Crea l'immagine
docker run -it --rm my-haskell-script # Crea ed avvia il container

Eseguire Qiskit in un notebook Jupyter

Vuoi eseguire un notebook Jupyter con Qiskit su una macchina su cui non hai intenzione di installare Python e tutte le dipendenze necessarie. Docker fornisce un ambiente isolato e pronto all’uso senza sporcare il sistema host

# https://quay.io/repository/jupyter/datascience-notebook
FROM quay.io/jupyter/datascience-notebook           # Immagine base ufficiale
USER root                                           # Passa ad utente root
COPY requirements.txt .                             # Copia il file dei requisiti
RUN pip install --no-cache-dir -r requirements.txt  # Installa i requisiti
docker build -t jupyter-qiskit-example .  # Build the image
docker run -it --rm \                     # Create and run the container.
  -p 8888:8888 \                          # Forward port 8888 to the host
  -v $(pwd)/notebooks:/home/jovyan/work \ # Bind mount the notebooks directory
  -e NB_UID=$(id -u) -e NB_GID=$(id -g) -e GRANT_SUDO=yes \ # Env vars
  jupyter-qiskit-example

Architettura Web con PHP e MySQL

Vuoi eseguire un’applicazione PHP con un database MySQL.

Loading diagram...
docker compose up -d # Esegue l'applicazione in modalità detached
docker compose down  # Ferma l'applicazione
docker volume prune  # Rimuove i volumi non utilizzati

Comandi

Una collezione di comandi comuni che potrebbero tornare utili

Comandi per le immagini

# Crea un'immagine da un Dockerfile
docker build -t <image-name>:<tag> <context>
# Elenca tutte le immagini
docker images
# Rimuove un'immagine
docker rmi <image-name>:<tag>
# Rimuove tutte le immagini
docker rmi $(docker images -q)
# Rimuove tutte le immagini non utilizzate
docker image prune

Comandi per la creazione di container

# Crea e avvia un container da un'immagine
# docker [OPZIONI] run <image-name>:<tag>

# (alcune) OPZIONI:
# -it: esegue in container in modalità interattiva con il terminale
# -d: esegue il container in modalità detached, ovvero in background
# -p: inoltra le porte del container all'host
# --name: nome del container
# --rm: rimuove automaticamente il container non appena questo si ferma
# -v <host-path>:<container-path>: monta una directory dell'host in una del container
# -v <volume-name>:<container-path>: monta un volume in una directory del container
# --env-file <file>: carica le variabili d'ambiente da un file
# -e <key>=<value>: imposta una variabile d'ambiente
# --entrypoint <command>: sovrascrive il comando di default del container
docker run -d -p 5000:5000 -v my-volume:/app --name my-container my-image:latest
docker run -it -v ./app:/app my-image:latest
docker run -e MYSQL_PASSWORD=pass --rm my-image:latest
docker run -it --entrypoint "/bin/bash" my-image:latest

Comandi per la gestione dei container

# Elenca tutti i container in esecuzione
docker ps
# Elenca tutti i container
docker ps -a
# Interrompe un container
docker stop <container-name>
# Interrompe tutti i container
docker stop $(docker ps -q)
# Rimuove un container
docker rm <container-name>

Comandi per l’interazione con i container

# Esegue un comando in un container
docker exec <container-name> <command>
# Attacca un terminale interattivo ad un container
docker exec -it <container-name> /bin/bash
# Mostra i log di un container
docker logs <container-name>
# Mostra i log di un container in tempo reale
docker logs -f <container-name>

Comandi per i volumi

# Crea un volume
docker volume create <volume-name>
# Elenca tutti i volumi
docker volume ls
# Rimuove un volume
docker volume rm <volume-name>
# Rimuove tutti i volumi non utilizzati
docker volume prune

Comandi di Docker compose

# Avvia la configurazione in background
docker compose up -d
# Forza il rebuild di tutte le immagini
docker compose up --build
# Avvia un singolo servizio
docker compose up <service-name>
# Interrompe l'applicazione
docker compose down
# Elenca tutti i servizi dell'applicazione
docker compose ps

Riferimenti

Immagini