Pas 9 - Docker Compose
Al Pas 7 vam aixecar WordPress amb MySQL amb una xarxa, dos volums i dues comandes llargues. Funcionava, però era difícil de recordar, de compartir i de mantenir. Docker Compose permet descriure tota aquesta configuració en un fitxer YAML i gestionar l'aplicació sencera amb una sola comanda.
Objectius del pas
- Traduir comandes
docker runa un fitxercompose.yaml. - Gestionar el cicle de vida d'una aplicació sencera:
up,ps,logs,down. - Combinar imatges de Docker Hub amb imatges pròpies construïdes amb
build. - Controlar l'ordre d'arrencada amb
depends_onihealthcheck, i separar la configuració amb un fitxer.env.
docker compose o docker-compose?
La versió actual és Compose v2, integrada a Docker com a subcomanda: docker compose (amb espai). L'antiga eina docker-compose (amb guionet) està obsoleta. El fitxer es pot dir compose.yaml (nom recomanat) o docker-compose.yml, i ja no cal la línia version: al principi.
9.1 De comandes a fitxer
Creeu un directori ~/docker-pas9/wordpress i, dins, un fitxer compose.yaml:
services:
wp-bd:
image: mysql:8.4
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: RootSeguret
MYSQL_DATABASE: wordpress
MYSQL_USER: wp
MYSQL_PASSWORD: WpSeguret
volumes:
- dades-bd:/var/lib/mysql
wordpress:
image: wordpress:6.7
restart: unless-stopped
ports:
- "8000:80"
environment:
WORDPRESS_DB_HOST: wp-bd:3306
WORDPRESS_DB_USER: wp
WORDPRESS_DB_PASSWORD: WpSeguret
WORDPRESS_DB_NAME: wordpress
volumes:
- fitxers-wp:/var/www/html
depends_on:
- wp-bd
volumes:
dades-bd:
fitxers-wp:
Compareu-lo amb les comandes del pas 7. Cada opció de docker run té la seva clau al fitxer:
docker run |
compose.yaml |
|---|---|
--name wp-bd |
el nom del servei (wp-bd:) |
| la imatge al final | image: |
-p 8000:80 |
ports: |
-e VAR=valor |
environment: |
--env-file fitxer |
env_file: |
-v volum:/ruta |
volumes: (i declarar el volum a volumes: de nivell superior) |
--network xarxa |
networks: (Compose en crea una per defecte) |
--restart unless-stopped |
restart: |
docker network create |
no cal: Compose crea una xarxa per a l'aplicació |
La xarxa ha desaparegut del fitxer: Compose crea automàticament una xarxa per a cada projecte i hi connecta tots els serveis, que es troben entre ells pel nom del servei. Per això WORDPRESS_DB_HOST continua sent wp-bd.
9.2 Aixecar, observar i aturar
[+] Running 5/5
✔ Network wordpress_default Created
✔ Volume "wordpress_dades-bd" Created
✔ Volume "wordpress_fitxers-wp" Created
✔ Container wordpress-wp-bd-1 Started
✔ Container wordpress-wordpress-1 Started
Si les imatges no eren locals, abans veureu com les descarrega: exactament el mateix que feia docker run al pas 1. Fixeu-vos en els noms: Compose posa com a prefix el nom del projecte, que per defecte és el nom del directori (wordpress). Així, dos projectes diferents poden tenir un servei wp-bd sense conflicte.
Tot el que hi ha a sota són contenidors, xarxes i volums normals: docker ps, docker network ls i docker volume ls els mostren. Compose només ens estalvia escriure les comandes.
docker compose ps # serveis d'aquest projecte
docker compose logs -f wordpress # logs d'un servei (sense nom: de tots)
docker compose exec wp-bd mysql -uwp -pWpSeguret wordpress
docker compose stop # atura els contenidors sense eliminar-los
docker compose start
docker compose down # atura i ELIMINA contenidors i xarxa (els volums es mantenen)
docker compose down -v # també elimina els volums: es perden les dades
flowchart LR
F[compose.yaml] -->|docker compose up| P
subgraph P[Projecte wordpress]
N[Xarxa wordpress_default]
S1[Contenidor wp-bd]
S2[Contenidor wordpress]
V1[(Volum dades-bd)]
V2[(Volum fitxers-wp)]
end
classDef a fill:#7C3AED,stroke:#5B21B6,color:#FFFFFF
classDef b fill:#2563EB,stroke:#1E40AF,color:#FFFFFF
classDef c fill:#16A34A,stroke:#166534,color:#FFFFFF
class F a
class N,S1,S2 b
class V1,V2 c
El fitxer és la documentació
Ara, per donar l'entorn a un company només cal passar-li el compose.yaml (normalment dins d'un repositori Git). Amb docker compose up -d tindrà exactament el mateix que vosaltres. Compareu-ho amb el fitxer de comandes que vau escriure a la miniactivitat del pas 7.
9.3 Canviar la configuració
Editeu el fitxer, per exemple canviant el port a "8080:80", i torneu a executar:
Compose compara el fitxer amb el que hi ha en marxa i només recrea els contenidors que han canviat (aquí, wordpress). Les dades no es perden, perquè són als volums.
9.4 Imatges pròpies: build
Compose també pot construir imatges a partir d'un Dockerfile. Reprenem la idea del pas 8 amb una aplicació Flask que desa comentaris en una base de dades PostgreSQL.
app-comentaris/
├── compose.yaml
├── .env
├── init.sql
└── web/
├── Dockerfile
├── app.py
└── requirements.txt
web/app.py:
from flask import Flask, request, render_template_string
import psycopg2
import os
app = Flask(__name__)
def connexio():
return psycopg2.connect(
host=os.environ['DB_HOST'],
dbname=os.environ['DB_NAME'],
user=os.environ['DB_USER'],
password=os.environ['DB_PASSWORD'],
)
PLANTILLA = '''
<h1>Comentaris</h1>
<form method="post">
<textarea name="comentari" rows="3" cols="50"></textarea><br>
<input type="submit" value="Afegir comentari">
</form>
<ul>{% for c in comentaris %}<li>{{ c[0] }}</li>{% endfor %}</ul>
'''
@app.route('/', methods=['GET', 'POST'])
def index():
with connexio() as conn, conn.cursor() as cur:
if request.method == 'POST':
cur.execute('INSERT INTO comentaris (text) VALUES (%s)', (request.form['comentari'],))
cur.execute('SELECT text FROM comentaris ORDER BY id DESC')
comentaris = cur.fetchall()
return render_template_string(PLANTILLA, comentaris=comentaris)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
web/requirements.txt:
web/Dockerfile (el mateix patró del pas 8):
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
init.sql:
CREATE TABLE IF NOT EXISTS comentaris (
id SERIAL PRIMARY KEY,
text TEXT NOT NULL,
creat_a TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
.env (Compose el llegeix automàticament i en substitueix les variables ${...} del fitxer):
compose.yaml:
services:
web:
build: ./web
image: app-comentaris:1.0
ports:
- "${WEB_PORT}:5000"
environment:
DB_HOST: db
DB_NAME: ${DB_NAME}
DB_USER: ${DB_USER}
DB_PASSWORD: ${DB_PASSWORD}
depends_on:
db:
condition: service_healthy
db:
image: postgres:17-alpine
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- dades-postgres:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 5s
timeout: 3s
retries: 10
volumes:
dades-postgres:
Aquí apareixen quatre idees noves:
build: ./web: en lloc de descarregar una imatge, Compose la construeix amb el Dockerfile d'aquest directori. Ambimage:li posem nom i etiqueta.- Bind mount i volum alhora:
init.sqles munta al directori/docker-entrypoint-initdb.d/, on la imatge de PostgreSQL executa els scripts en crear la base de dades per primer cop. Les dades van a un volum amb nom. healthcheck: una ordre que Docker executa periòdicament per saber si el servei està realment llest, no només arrencat.depends_onambcondition: service_healthy:webno arrenca fins que la base de dades respon. Sense aquesta condició,depends_onnomés garanteix l'ordre d'arrencada, i Flask podria intentar connectar-se abans que PostgreSQL acceptés connexions.
Obriu http://localhost:5000 i afegiu comentaris. Proveu:
docker compose downidocker compose up -d: els comentaris continuen (volum).- Modifiqueu
app.pyi feudocker compose up -d --build: només es reconstrueix i es recreaweb, aprofitant la memòria cau de capes.
El fitxer .env no s'ha de pujar a Git
Conté contrasenyes. Afegiu-lo al .gitignore i pugeu en canvi un .env.example amb els noms de les variables i valors d'exemple.
9.5 Comandes de Compose
| Comanda | Què fa |
|---|---|
docker compose up -d |
Crea i arrenca tot el projecte en segon pla |
docker compose up -d --build |
Igual, reconstruint les imatges amb build |
docker compose ps |
Estat dels serveis |
docker compose logs [-f] [servei] |
Logs |
docker compose exec servei ordre |
Executa una ordre dins d'un servei |
docker compose build |
Només construeix les imatges |
docker compose pull |
Descarrega les versions noves de les imatges |
docker compose stop / start |
Atura / arrenca sense eliminar |
docker compose down [-v] |
Elimina contenidors i xarxa (i volums amb -v) |
docker compose config |
Mostra el fitxer final amb les variables substituïdes (útil per detectar errors) |
La referència completa és a la documentació de Docker Compose i a l'especificació del fitxer Compose.
Miniactivitat - De comandes a Compose
- Afegiu al
compose.yamlde WordPress un serveiphpmyadmin(el mateix de la miniactivitat del pas 7). Quantes línies té el fitxer, comparat amb el fitxer de comandes que vau escriure? - Moveu totes les contrasenyes del WordPress a un fitxer
.envi comproveu el resultat ambdocker compose config. - A l'aplicació de comentaris, elimineu el
healthchecki la condició deldepends_on, feudocker compose down -vidocker compose up -d. Mireu els logs deweb: hi ha errors de connexió en la primera arrencada? Per què? - Aixequeu dues còpies de l'aplicació de comentaris amb
docker compose -p copia1 up -didocker compose -p copia2 up -d(canviantWEB_PORT). Quins noms tenen els contenidors, les xarxes i els volums?
9.6 Neteja
Següent pas: ja teniu una aplicació pròpia funcionant amb Compose. Al Pas 10 veurem com fer imatges més petites i segures, com publicar-les a Docker Hub (el mateix lloc d'on descarregàvem imatges al pas 1) i com depurar problemes. Després, ja podeu fer la Pràctica 1.