Docker su Linux e primi comandi

Immagine di copertina dell'articolo

Installazione

Ubuntu Debian

Per prima cosa rimuovere i seguenti pacchetti che possono dare conflitto

# comando ubuntu
sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc | cut -f1)

# comando debian
sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-doc podman-docker containerd runc | cut -f1)

Impostiamo il repository apt (ubuntu)

# Add Docker's official GPG key:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update

Impostiamo il repository apt (Debian)

# Add Docker's official GPG key:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update

Infine installiamo docker

sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Veirfichiamo che il servizio sia avviato

sudo systemctl status docker

E nel caso avviamolo

sudo systemctl start docker

Fedora / RHEL Linux

Rimuoviamo i pacchetti che posono dare conflitto

sudo dnf remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine podman runc

Impostiamo il repository con i seguenti comandi

# rhel linux
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo

# fedora
sudo dnf config-manager addrepo --from-repofile https://download.docker.com/linux/fedora/docker-ce.repo

Infine installiamo docker

sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Per avviare docker

sudo systemctl enable --now docker

Avvio e confiurazione post installazione

Per testare docker usare il comando

sudo docker run hello-world

Per eseguire docker senza specificare sudo seguire la seguente procedura

# aggiungere il gruppo docker
sudo groupadd docker

# aggiungere l'utente corrente al gruppo appena creato
sudo usermod -aG docker $USER

Fare il logout e poi login. Dopo si può eseguire docker senza specificare sudo

docker run hello-world

Comandi

# Versione installata
docker version

# Informazioni sul sistema
docker info

# Aiuto per un comando
docker <comando> --help

Gestione delle immagini

# Scaricare un'immagine
docker pull ubuntu:22.04

# Elencare le immagini locali
docker images

# Cercare immagini su Docker Hub
docker search nginx

# Rimuovere un'immagine
docker rmi ubuntu:22.04

# Rimuovere tutte le immagini non utilizzate
docker image prune -a

Gestione dei container

# Avviare un container
docker run ubuntu:22.04

# Avviare in modalità interattiva
docker run -it ubuntu:22.04 bash

# Avviare in background (detached)
docker run -d nginx

# Avviare con nome personalizzato
docker run -d --name mio-nginx nginx

# Avviare con mapping delle porte (host:container)
docker run -d -p 8080:80 nginx

# Avviare con variabili d'ambiente
docker run -d -e MYSQL_ROOT_PASSWORD=secret mysql:8

# Elencare i container in esecuzione
docker ps

# Elencare tutti i container (anche fermi)
docker ps -a

# Fermare un container
docker stop mio-nginx

# Avviare un container fermo
docker start mio-nginx

# Riavviare un container
docker restart mio-nginx

# Rimuovere un container
docker rm mio-nginx

# Rimuovere un container in esecuzione (forzato)
docker rm -f mio-nginx

# Eseguire un comando in un container in esecuzione
docker exec -it mio-nginx bash

# Visualizzare i log
docker logs mio-nginx

# Seguire i log in tempo reale
docker logs -f mio-nginx

Volumi e bind mount

I volumi permettono la persistenza dei dati al di fuori del ciclo di vita del container.

# Creare un volume
docker volume create mio-volume

# Elencare i volumi
docker volume ls

# Usare un volume in un container
docker run -d -v mio-volume:/var/lib/mysql mysql:8

# Bind mount (cartella locale → container)
docker run -d -v /percorso/locale:/percorso/container nginx

# Bind mount con sintassi --mount (più esplicita)
docker run -d \
  --mount type=bind,source=/percorso/locale,target=/percorso/container \
  nginx

# Rimuovere un volume
docker volume rm mio-volume

# Rimuovere i volumi non utilizzati
docker volume prune

Networking

# Elencare le reti
docker network ls

# Creare una rete personalizzata
docker network create mia-rete

# Connettere un container a una rete
docker run -d --network mia-rete --name app mia-app

# Connettere un container in esecuzione a una rete
docker network connect mia-rete mio-nginx

# Disconnettere un container da una rete
docker network disconnect mia-rete mio-nginx

# Ispezionare una rete
docker network inspect mia-rete

# Rimuovere le reti non utilizzate
docker network prune

Docker Compose

Docker Compose permette di gestire applicazioni multi-container tramite un file docker-compose.yml.

Esempio base: app web + database

# docker-compose.yml
services:
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: myapp
    volumes:
      - db-data:/var/lib/postgresql/data

  web:
    image: nginx
    ports:
      - "8080:80"
    depends_on:
      - db

volumes:
  db-data:

I comandi per la gestione docker compose sono

# Avviare tutti i servizi in background
docker compose up -d

# Avviare e ricostruire le immagini
docker compose up -d --build

# Fermare i servizi
docker compose down

# Fermare e rimuovere i volumi
docker compose down -v

# Vedere i log di tutti i servizi
docker compose logs -f

# Vedere i log di un servizio specifico
docker compose logs -f web

# Elencare i container del compose
docker compose ps

# Eseguire un comando in un servizio
docker compose exec web bash

# Scalare un servizio
docker compose up -d --scale web=3

Pulizia del sistema

# Rimuovere container fermi, immagini non taggate, reti e cache build
docker system prune

# Rimuovere anche i volumi non utilizzati
docker system prune --volumes

# Rimuovere tutto (immagini, container, volumi, reti)
docker system prune -a --volumes

# Visualizzare l'utilizzo disco di Docker
docker system df

Lamp Docker

In questo capitolo andiamo a creare un ambiente lamp su uan distribuzione Debian 13

L'ambiente avrà Apache, php 8.5, mariadb e phpmyadmin. Non verrà copiato niente all'interno dei container e verrà condivisa una cartella www per la lettura e scrittura dei siti web

All'interno della cartella dbs è possibile salvare uno copia dei db presenti eseguendo lo script backup-dbs.sh

Cominciamo con la struttura del progetto che parte da una cartella $HOME/Lamp

$HOME/Lamp/
├── .env                    # USER_UID / USER_GID (da rigenerare in locale)
├── docker-compose.yml
├── backup-dbs.sh           # dump individuale dei DB
├── stop-lamp.sh            # backup + spegnimento (usare SEMPRE questo per spegnere)
├── apache/
│   ├── Dockerfile          # PHP 8.5 + Apache custom
│   ├── entrypoint.sh       # allinea UID/GID di www-data a quelli dell'host
│   └── php.ini-custom
├── www/                    # bind mount: qui vanno i tuoi progetti (sito1/, sito2/, ...)
└── dbs/                    # destinazione dei backup .sql

Passsiamo ora alla creazione dei files

Specificatamente per debian 13 bisogna applicare questa fix a docker per poteer risolvere i dns

sudo tee /etc/docker/daemon.json > /dev/null << 'EOF'
{ "dns": ["8.8.8.8", "1.1.1.1"] }
EOF

# riavviare docker
sudo systemctl restart docker

Creiamo la struttura delle cartelle

mkdir -p "$HOME/Lamp/apache" "$HOME/Lamp/www" "$HOME/Lamp/dbs"
cd "$HOME/Lamp"

Creiamo il file .env

{
  echo "USER_UID=$(id -u)"
  echo "USER_GID=$(id -g)"
} > "$HOME/Lamp/.env"

Di seguito trovi le istruzioni per creare tutti i files necessari

File docker-compose.yml

cat > "$HOME/Lamp/docker-compose.yml" << 'EOF'
services:
  apache:
    build:
      context: ./apache
      dockerfile: Dockerfile
    container_name: lamp_apache
    restart: unless-stopped
    environment:
      USER_UID: ${USER_UID}
      USER_GID: ${USER_GID}
    ports:
      - "8001:80"
    volumes:
      - ./www:/var/www/html
    depends_on:
      - mariadb
    networks:
      - lamp_network

  mariadb:
    image: mariadb:latest
    container_name: lamp_mariadb
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - mariadb_data:/var/lib/mysql
    networks:
      - lamp_network

  phpmyadmin:
    image: phpmyadmin:latest
    container_name: lamp_phpmyadmin
    restart: unless-stopped
    environment:
      PMA_HOST: mariadb
      PMA_PORT: 3306
      UPLOAD_LIMIT: 128M
    ports:
      - "8000:80"
    depends_on:
      - mariadb
    networks:
      - lamp_network

networks:
  lamp_network:
    driver: bridge

volumes:
  mariadb_data:
EOF

File apache/Dockerfile

cat > "$HOME/Lamp/apache/Dockerfile" << 'EOF'
FROM php:8.5-apache

# --- Dipendenze di sistema per compilare le estensioni PHP ---
RUN apt-get update && apt-get install -y --no-install-recommends \
    libzip-dev \
    libicu-dev \
    libpng-dev \
    libjpeg62-turbo-dev \
    libfreetype6-dev \
    libwebp-dev \
    libmagickwand-dev \
    libonig-dev \
    libxml2-dev \
    unzip \
    git \
    less \
    nano \
    default-mysql-client \
    && rm -rf /var/lib/apt/lists/*

# --- Configurazione ed installazione estensioni PHP ---
# Nota: ogni estensione viene compilata in un RUN separato, senza -j$(nproc)
# aggressivo. Compilare estensioni pesanti (es. intl/ICU) in parallelo con
# troppi job può esaurire la RAM disponibile durante la build (specie in
# VM/container con poche risorse), causando build incomplete e l'errore
# "cp: cannot stat 'modules/*'".
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp

RUN docker-php-ext-install -j1 pdo_mysql
RUN docker-php-ext-install -j1 mysqli
RUN docker-php-ext-install -j1 intl
RUN docker-php-ext-install -j1 zip
RUN docker-php-ext-install -j1 gd
RUN docker-php-ext-install -j1 exif
# NOTA: da PHP 8.5 OPcache è integrato nel core di PHP e non è più
# un'estensione separata da compilare: "docker-php-ext-install opcache"
# darebbe errore ("cp: cannot stat 'modules/*'"). È già presente e attiva
# di default; la configuriamo via php.ini-custom più sotto.
RUN docker-php-ext-install -j1 bcmath
RUN docker-php-ext-install -j1 soap
RUN docker-php-ext-install -j1 mbstring
RUN docker-php-ext-install -j1 xml

# Imagick (via PECL, non incluso in docker-php-ext-install)
RUN pecl install imagick && docker-php-ext-enable imagick

# --- Moduli Apache necessari per i permalink di WordPress e simili ---
RUN a2enmod rewrite headers expires

# --- php.ini ottimizzato per WordPress / CMS moderni ---
COPY php.ini-custom /usr/local/etc/php/conf.d/zz-custom.ini

# --- Entrypoint per allineare UID/GID di www-data a quelli dell'host ---
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

WORKDIR /var/www/html

ENTRYPOINT ["entrypoint.sh"]
CMD ["apache2-foreground"]
EOF

File apache/php.ini-custom

cat > "$HOME/Lamp/apache/php.ini-custom" << 'EOF'
memory_limit = 256M
upload_max_filesize = 128M
post_max_size = 128M
max_execution_time = 300
max_input_time = 300
max_input_vars = 3000

opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.validate_timestamps=1

display_errors = Off
log_errors = On
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
EOF

File apache/entrypoint.sh

cat > "$HOME/Lamp/apache/entrypoint.sh" << 'EOF'
#!/bin/bash
set -e

USER_ID=${USER_UID:-1000}
GROUP_ID=${USER_GID:-1000}

CURRENT_UID=$(id -u www-data)
CURRENT_GID=$(id -g www-data)

if [ "$CURRENT_GID" != "$GROUP_ID" ]; then
    groupmod -o -g "$GROUP_ID" www-data
fi

if [ "$CURRENT_UID" != "$USER_ID" ]; then
    usermod -o -u "$USER_ID" www-data
fi

exec "$@"
EOF

File backup-dbs.sh

cat > "$HOME/Lamp/backup-dbs.sh" << 'EOF'
#!/bin/bash
set -euo pipefail

LAMP_DIR="$HOME/Lamp"
BACKUP_DIR="$LAMP_DIR/dbs"
CONTAINER_NAME="lamp_mariadb"
MYSQL_ROOT_PASSWORD="root"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p "$BACKUP_DIR"

if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
    echo "[ERRORE] Il container ${CONTAINER_NAME} non è in esecuzione. Backup annullato."
    exit 1
fi

echo "== Recupero elenco database da ${CONTAINER_NAME} =="

DATABASES=$(docker exec "$CONTAINER_NAME" mariadb -uroot -p"${MYSQL_ROOT_PASSWORD}" -N -e "SHOW DATABASES;" \
    | grep -Ev "^(mysql|information_schema|performance_schema|sys)$" || true)

if [ -z "$DATABASES" ]; then
    echo "Nessun database utente trovato. Niente da salvare."
    exit 0
fi

COUNT=0
for DB in $DATABASES; do
    OUTFILE="${BACKUP_DIR}/${DB}_${DATE}.sql"
    echo "-> Backup di '${DB}' in ${OUTFILE}"
    docker exec "$CONTAINER_NAME" mariadb-dump -uroot -p"${MYSQL_ROOT_PASSWORD}" --databases "$DB" > "$OUTFILE"
    COUNT=$((COUNT+1))
done

echo "== Backup completato: ${COUNT} database salvati in ${BACKUP_DIR} =="
EOF

File stop-lamp.sh

cat > "$HOME/Lamp/stop-lamp.sh" << 'EOF'
#!/bin/bash
set -euo pipefail

LAMP_DIR="$HOME/Lamp"
cd "$LAMP_DIR"

echo "== [1/2] Backup dei database prima dello spegnimento =="
"$LAMP_DIR/backup-dbs.sh"

echo "== [2/2] Spegnimento dell'ambiente LAMP =="
docker compose down

echo "== Ambiente LAMP spento. Backup disponibili in $LAMP_DIR/dbs =="
EOF

Modifica i permessi di esecuzione dei seguenti files

chmod +x "$HOME/Lamp/backup-dbs.sh" "$HOME/Lamp/stop-lamp.sh" "$HOME/Lamp/apache/entrypoint.sh"

Per costruire i container e avviare il tutto

cd "$HOME/Lamp"
docker compose build --no-cache
docker compose up -d

Se si verificano dei problemi, per ricompilare tutto

cd "$HOME/Lamp"
docker builder prune -f
docker compose build --no-cache
docker compose up -d

Eseguendo il comando docker ps è possibile vedere se i container sono in esecuzione. Visitando i seguenti indirizzi abbiamo

  • http://localhost:8001 visualizzo i siti in $HOME/Lamp/www/
  • http://localhost:8000 accedo a phpMyAdmin (utente root, password root)

Per finire una lista di comandi per uan gestione quotidiano dell'ambiente creato. Tutti questi comando devono essere lanciati dentro la cartella $HOME/Lamp

Avviare docker compose up -d
Spegnere senza backup docker compose stop
Spegnere con backup automatico cd $HOME/Lamp && ./stop-lamp.sh
Stato del container docker compose ps
Log tutti i servizi: docker compose logs -f
apache/php: docker compose logs -f apache
mariadb: docker compose logs -f mariadb
Shell Apache/PHP: docker exec -it lamp_apache bash
MariaDB: docker exec -it lamp_mariadb bash
Client MariaDB root docker exec -it lamp_mariadb mariadb -uroot -proot
Backup manuale immediato ./backup-dbs.sh
Eseguire comandi PHP: docker exec -it lamp_apache php -v
WPCLI: docker exec -it lamp_apache wp --info --path=/var/www/html/sito1