Salta el contingut

Bones Pràctiques i Depuració

Bones pràctiques per treballar amb Docker

A mesura que utilitzeu Docker més i més, us adonareu que hi ha maneres millors i pitjors de fer les coses. Aquí us explico les pràctiques recomanades que us estalviaran problts i farant que les vostres aplicacions siguin més robustes i mantenibles.

Mantenir les imatges petites

Les imatges grans són problemàtiques: triguen més a descarregar, ocupen més espai al disc, i sovint contenen programari innecessari que podria tenir vulnerabilitats de seguretat. Hi ha diverses tècniques per mantenir les imatges petites.

Primer, utilitzeu imatges base lleugeres quan sigui possible. Les variants "alpine" de les imatges oficials són molt més petites que les variants estàndard. Per exemple, python:3.12-alpine és molt més petita que python:3.12. L'única desavantatge és que Alpine Linux usa musl libc en lloc de glibc, cosa que de vegades pot causar incompatibilitats amb certs paquets, però per la majoria d'aplicacions funciona perfectament.

Segon, minimitzeu el nombre de capes. Cada instrucció RUN al Dockerfile crea una nova capa. En lloc de tenir múltiples instruccions RUN, combineu-les quan tingui sentit:

# Menys òptim - crea 3 capes
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim

# Millor - crea només 1 capa
RUN apt-get update && \
    apt-get install -y curl vim && \
    rm -rf /var/lib/apt/lists/*

Fixeu-vos que també eliminem els fitxers cache d'apt al final per fer la capa més petita.

Tercer, utilitzeu .dockerignore. Aquest fitxer funciona com .gitignore: especifica quins fitxers i directoris NO s'haurien de copiar a la imatge. Per exemple, no necessiteu copiar el directori .git, fitxers temporals, o documentació:

.git
.gitignore
README.md
node_modules
*.log
.env

Això fa que el context de construcció sigui més petit i ràpid de processar.

Multi-stage builds: el secret de les imatges professionals

Els multi-stage builds són una tècnica avançada però extremadament útil. La idea és utilitzar múltiples instruccions FROM en un mateix Dockerfile, cadascuna definint una "stage" o etapa de construcció. Això us permet tenir una etapa per compilar o construir l'aplicació (amb totes les eines de desenvolupament), i després una etapa final molt més lleugera que només conté l'aplicació compilada.

Per exemple, per una aplicació Go:

# Etapa 1: Compilació
FROM golang:1.23-alpine AS builder
WORKDIR /build
COPY . .
RUN go build -o app .

# Etapa 2: Imatge final
FROM alpine:3.20
WORKDIR /app
COPY --from=builder /build/app .
CMD ["./app"]

La imatge final no conté el compilador de Go ni els fitxers font, només el binari compilat. Això pot reduir la mida de la imatge de centenars de megabytes a pocs megabytes.

No executeu contenidors com a root

Per defecte, els processos dins dels contenidors s'executen com a usuari root, cosa que és un risc de seguretat. Si algú compromet el vostre contenidor, tindria permisos de root dins del contenidor. Tot i que Docker proporciona aïllament, és millor seguir el principi del mínim privilegi.

Podeu crear un usuari no privilegiat al Dockerfile:

FROM python:3.12-slim

# Crear un usuari no-root
RUN useradd -m -u 1000 appuser

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

# Canviar a l'usuari no-root
USER appuser

CMD ["python", "app.py"]

Utilitzar variables d'entorn per la configuració

Mai codifiqueu en dur coses com contrasenyes, URLs d'APIs, o qualsevol configuració que pugui canviar entre entorns (desenvolupament, staging, producció). Utilitzeu variables d'entorn:

import os

DB_HOST = os.environ.get('DB_HOST', 'localhost')
DB_USER = os.environ.get('DB_USER', 'user')
DB_PASSWORD = os.environ.get('DB_PASSWORD')

Això fa que la mateixa imatge es pugui utilitzar en diferents entorns simplement passant diferents variables d'entorn.

Especificar versions explícites

Ja ho hem dit abans, però és tan important que val la pena repetir-ho: sempre especifiqueu versions concretes de les imatges base i les dependències. No utilitzeu FROM python:latest o RUN pip install flask. Utilitzeu versions específiques:

FROM python:3.12.1-slim

COPY requirements.txt .
RUN pip install -r requirements.txt

I al requirements.txt:

Flask==3.1.0
psycopg2-binary==2.9.10

Això garanteix que la imatge es construirà de la mateixa manera avui i d'aquí a sis mesos.

Health checks per aplicacions crítiques

Docker permet definir health checks al Dockerfile per monitorar si l'aplicació està funcionant correctament:

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:5000/ || exit 1

Això fa que Docker comprovi cada 30 segons si l'aplicació respon correctament. Si falla 3 vegades seguides, Docker marcarà el contenidor com "unhealthy". Això és molt útil quan s'utilitza amb orquestradors com Kubernetes.

Podeu trobar més bones pràctiques a la documentació oficial: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

Registres d'imatges: Docker Hub i alternatives

Fins ara hem treballat només amb imatges locals o imatges públiques de Docker Hub. Però en un entorn professional, sovint necessitareu compartir les vostres imatges amb altres membres de l'equip o desplegar-les a servidors de producció. Aquí és on entren els registres d'imatges.

Docker Hub: el registre públic per excel·lència

Docker Hub és el registre públic més gran d'imatges Docker. És com GitHub però per imatges Docker. Conté milers d'imatges oficials i milions d'imatges creades per la comunitat. Quan executeu docker pull nginx, Docker automàticament el busca a Docker Hub.

Docker Hub ofereix comptes gratuïts que us permeten tenir un repositori privat i repositoris públics il·limitats. Això és perfecte per aprenentatge i projectes personals. Per a ús professional, Docker Hub ofereix plans de pagament amb més repositoris privats i funcionalitats addicionals.

Pujant imatges a Docker Hub

Per pujar la vostra pròpia imatge a Docker Hub, primer necessiteu crear un compte a https://hub.docker.com. Un cop tingueu el compte, heu de fer login des de la línia de comandes:

docker login

Això us demanarà el vostre nom d'usuari i contrasenya. Un cop autenticats, podeu pujar imatges. Però primer, les imatges necessiten tenir el format correcte de nom: usuari/nom-imatge:etiqueta.

Si la vostra imatge es diu la-meva-app:1.0 i el vostre usuari és joanserra, heu de retaggejar la imatge:

docker tag la-meva-app:1.0 joanserra/la-meva-app:1.0

Ara podeu pujar-la:

docker push joanserra/la-meva-app:1.0

Veureu com Docker puja cada capa de la imatge. Si algunes capes ja existeixen a Docker Hub (perquè es comparteixen amb altres imatges), no cal tornar-les a pujar, fent que el procés sigui molt més ràpid.

Un cop pujada, qualsevol persona (si és pública) o qualsevol membre del vostre equip (si és privada) pot descarregar i utilitzar la imatge:

docker pull joanserra/la-meva-app:1.0
docker run joanserra/la-meva-app:1.0

Registres privats: quan necessiteu més control

Per a empreses i projectes que necessiten més control, privacitat, o que volen allotjar les imatges als seus propis servidors, hi ha alternatives a Docker Hub. Podeu muntar el vostre propi registre privat utilitzant Docker Registry, que és una aplicació de codi obert proporcionada per Docker:

docker run -d -p 5000:5000 --name registre registry:2

Això arrenca un registre local al port 5000. Ara podeu pujar imatges a aquest registre:

docker tag la-meva-app:1.0 localhost:5000/la-meva-app:1.0
docker push localhost:5000/la-meva-app:1.0

Hi ha també alternatives comercials com Amazon ECR (Elastic Container Registry), Google Container Registry, Azure Container Registry, o GitLab Container Registry, que ofereixen integració amb les seves respectives plataformes de núvol.

Depuració i solució de problemes

Inevitablement, us trobareu amb situacions on alguna cosa no funciona com esperàveu. Docker proporciona diverses eines per depurar i entendre què està passant.

Consultant logs

La primera eina de depuració són els logs. Per veure els logs d'un contenidor:

docker logs nom-contenidor

Això mostra tot el que l'aplicació ha escrit a stdout i stderr. L'opció -f segueix els logs en temps real:

docker logs -f nom-contenidor

Si només voleu veure les últimes línies:

docker logs --tail 50 nom-contenidor

Entrant dins d'un contenidor

De vegades necessiteu "entrar" dins d'un contenidor per inspeccionar-lo. Utilitzeu docker exec per executar comandes dins d'un contenidor en execució:

docker exec -it nom-contenidor /bin/bash

Això us dona una shell interactiva dins del contenidor. L'opció -it combina -i (interactiu) i -t (terminal). Si la imatge està basada en Alpine, probablement haureu d'utilitzar /bin/sh en lloc de /bin/bash perquè Alpine no inclou bash per defecte.

Un cop dins, podeu executar qualsevol comanda: ls, ps, cat, etc. Això és molt útil per verificar que els fitxers estan on haurien d'estar, comprovar variables d'entorn, o executar comandes de depuració.

Per sortir del contenidor, simplement escriviu exit.

Inspeccionant contenidors i imatges

La comanda docker inspect us dona informació detallada sobre gairebé qualsevol cosa de Docker:

docker inspect nom-contenidor

Això retorna un JSON enorme amb tota la informació sobre el contenidor: la seva configuració, xarxa, volums, variables d'entorn, etc. És massa informació per llegir d'un cop, però podeu filtrar-la:

docker inspect nom-contenidor | grep IPAddress

També podeu usar el format de plantilla per extreure informació específica:

docker inspect --format='{{.NetworkSettings.IPAddress}}' nom-contenidor

Problemes comuns i les seves solucions

Un problema molt comú és "No puc connectar-me al meu contenidor". Normalment això passa perquè heu oblidat mapegar els ports. Recordeu que un contenidor té la seva pròpia xarxa aïllada. Si l'aplicació escolta al port 80 dins del contenidor, però no heu fet -p 8080:80, no hi podreu accedir des de fora.

Un altre problema freqüent és "El meu contenidor es reinicia constantment". Això normalment vol dir que el procés principal del contenidor està fallant. Consulteu els logs amb docker logs per veure què està passant. Potser falta una variable d'entorn, o el proces s'està executant i acabant immediatament perquè no té res a fer.

"El meu volum està buit o no té les dades que esperava" sol passar per problemes de permisos o rutes incorrectes. Utilitzeu docker inspect per verificar on està muntat exactament el volum, i comproveu que la ruta dins del contenidor coincideix amb la que esperàveu.

Finalment, "Docker diu que el port ja està en ús". Això vol dir que ja teniu alguna cosa (potser un altre contenidor, o una aplicació del vostre sistema) utilitzant el port que intenteu mapegar. Podeu utilitzar un port diferent, o aturar l'altra aplicació/contenidor que està utilitzant el port.