Introducció

Hi ha moltes aplicacions que no poden funcionar soles, sinó que necessiten altres aplicacions per funcionar.

Per exemple el Wordpress necessita una base de dades.

Mitjançant Docker Compose podem crear un conjunt de serveis de manera declarativa, dient a docker el resultat que volem i que docker s’encarregui d’executar les ordres corresponents.

Xarxa privada

Crea un carpeta cluster i dins d'aquesta carpeta un fitxer docker-compose.yml.

$ mkdir cluster
$ cd cluster
$ nano docker-compose.yml

A continuació tens el contingut del fitxer en format YAML:

services:
  apache:
    image: httpd:2.4
    ports:
      - 80:80
  • Pots veure que el fitxer defineix un conjunt de serveis.

  • Cada servei te un nom, en aquest cas apache, que es farà servir per donar nom al contenidor.

  • A continuació per cada servei has de dir quina imatge es farà servir i definir els "port forward" pertinents.

Amb docker compose pots arrencar els contenidors definits perl fitxer docker-compose.yaml amb aquesta ordre:

$ docker-compose up -d

Creating network "cluster_default" with the default driver
Pulling apache (httpd:2.4)...
...
Creating cluster_apache_1 ... done

Pots veure que es crea una xarxa amb el nom cluster_default i un contenidor amb el nom cluster_apache_1.

Per defecte, Docker Compose afegeix el nom de la carpeta on està el fitxer docker-compose.yml com a prefix de tots els recursos que crea, en aquest cas cluster_.

Pots verificar que s'ha creat la xarxa:

$ docker network ls | grep cluster
49fca8deb35b   cluster_default   bridge    local

Que és correspon a una interfície de xarxa virtual (fan servir el mateix id):

$ ip -brief addr | grep 49f
br-49fca8deb35b  UP             172.18.0.1/16 fe80::42:76ff:fe7e:e33d/64 

I que el contenidor està en la xarxa 172.18.0.1/16:

$ docker exec cluster_apache_1 more /etc/hosts | grep 172
172.18.0.2      6ce60b066200

✅ Docker Compose crea una xarxa privada que permet a un conjunt de contenidors estar connectats de manera aïllada del reste d'aplicacions que s'estan executant a la màquina.

Serveis

Un fitxer docker-compose.yml et permet definir un conjunt de serveis.

Un servei és una aplicació que s'està executant dins un contenidor i que pot ser accedida per qualsevol altre aplicació que tingui accés a la xarxa privada en que està el contenidor.

Modifica el fitxer docker-compose.yml amb tres serveis apache:

services:
  apache_1:
    image: httpd:2.4
    ports:
      - 81:81
  apache_2:
    image: httpd:2.4
    ports:
      - 82:80
  apache_3:
    image: httpd:2.4
    ports:
      - 83:80

Desplega aquesta nova configuració:

$ docker-compose up -d
WARNING: Found orphan containers (cluster_apache_1) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
Creating cluster_apache_3_1 ... done
Creating cluster_apache_2_1 ... done
Creating cluster_apache_1_1 ... done

Pots veure que docker arrencar 3 contenidors, però no elimina el que està en execució.

Un contenidor pot guardar estat (fitxers modificats, dades, etc.) i si elimines el contenidor aquestes estat es perd.

Per tant, per defecte, docker es conservador per evitar que sense voler facis el que no havies de fer:

docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.Status}}" 
CONTAINER ID   NAMES                STATUS
8ae4c9a9b99e   cluster_apache_1_1   Up 16 seconds
576213c0c870   cluster_apache_3_1   Up 16 seconds
a242c257b255   cluster_apache_2_1   Up 16 seconds
0b092e9e2196   cluster_apache_1     Up 4 minutes

✅ Amb l'opció --format podem limitar la informació que ens mostra l'ordre docker ps`.

Verifica amb nmapque els 4 contenidors estan escoltant als ports respectius:

$ nmap -p80-83 127.0.0.1
...
PORT   STATE SERVICE
80/tcp open  http
81/tcp open  hosts2-ns
82/tcp open  xfer
83/tcp open  mit-ml-dev

Si estas segur del que estas fent pots demanar a docker que elimini el servei que ja no està declarat amb l'opció --remove-orphans:

$ docker-compose up -d --remove-orphans
Removing orphan container "cluster_apache_1"
cluster_apache_1_1 is up-to-date
cluster_apache_2_1 is up-to-date
cluster_apache_3_1 is up-to-date

Verifica que el contenidor cluster_apache_1 s'ha eliminat:

docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.Status}}" 
CONTAINER ID   NAMES                STATUS
8ae4c9a9b99e   cluster_apache_1_1   Up 10 minutes
576213c0c870   cluster_apache_3_1   Up 10 minutes
a242c257b255   cluster_apache_2_1   Up 10 minutes

Volum

Al fitxer docker-compose.yml també pots definir volums:

services:  
  apache_1:
    image: httpd:2.4
    volumes:
      - htdocs:/usr/local/apache2/htdocs
    ports:
      - 81:80
  apache_2:
    image: httpd:2.4
    volumes:
      - htdocs:/usr/local/apache2/htdocs
    ports:
      - 82:80
  apache_3:
    image: httpd:2.4
    volumes:
      - htdocs:/usr/local/apache2/htdocs
    ports:
      - 83:80
volumes:
  htdocs:

Torna a arrencar els contenidors:

$ docker-compose up -d
Creating volume "cluster_htdocs" with default driver
Recreating cluster_apache_1_1 ... done
Recreating cluster_apache_3_1 ... done
Recreating cluster_apache_2_1 ... done

Com que el fitxer declara un volum htdocs, docker s’encarrega de crear aquest volum.

Fixa’t que al igual que ha fet amb la xarxa i els contenidors el prefix és cluster:

$ docker volume ls
DRIVER    VOLUME NAME
local     cluster_htdocs

També que s'han tornar a crear els contenidors httpd.

Modifica el contingut htdocs d’un dels contenidors.

docker exec cluster_apache_1_1 /bin/sh -c "echo 'hello' > htdocs/index.html"

A continuació verificar que tots el contenidors comparteixen el mateix volum:

$ curl localhost:81
hello
$ curl localhost:82
hello
$ curl localhost:83
hello

Com que el contingut de la carpeta htdocs està montant en un volum podem eliminar el contenidors sense perdre l'estat:

$ docker-compose down
Stopping cluster_apache_1_1 ... done
Stopping cluster_apache_2_1 ... done
Stopping cluster_apache_3_1 ... done
Removing cluster_apache_1_1 ... done
Removing cluster_apache_2_1 ... done
Removing cluster_apache_3_1 ... done
Removing network cluster_default

Pots veure que es borra tot (incloent la xarxa) excepte el volum:

$ docker volume ls
DRIVER    VOLUME NAME
local     cluster_htdocs

Si tornem a desplegar la composició tot torna a funcionar:

$ docker-compose up -d
...
$ curl localhost:81
hello

Proxy

A continuació crearem un proxy pels tres servidors web.

Crea un fitxer haproxy.cfg:

frontend proxy
  bind :80
  default_backend webservers

backend webservers
  server s1 apache_1:80 check
  server s2 apache_2:80 check
  server s3 apache_3:80 check

Modifica el fitxer docker-compose.yml:

services:
  proxy:
    image: haproxy:2.9
    sysctls:
      net.ipv4.ip_unprivileged_port_start: 0
    volumes:
      - ${PWD}/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg 
    ports:
      - 80:80
      - 8404:8404 
  apache_1:
    image: httpd:2.4
    volumes:
      - htdocs:/usr/local/apache2/htdocs
  apache_2:
    image: httpd:2.4
    volumes:
      - htdocs:/usr/local/apache2/htdocs
  apache_3:
    image: httpd:2.4
    volumes:
      - htdocs:/usr/local/apache2/htdocs
volumes:
  htdocs:

Per muntar el fitxer fem servir un volum anònim.

Si vols saber perquè afegim systctls ves a https://hub.docker.com/_/haproxy/

Para i arrenca de nou els contenidors:

$ docker-compose stop
...
$ docker-compose up -d
...
Starting cluster_apache_2_1 ... done
Creating cluster_proxy_1    ... done

És molt important que el fitxer haproxy.cfg acabi amb un LFen la última linia.

Si no tindràs aquest error:

$ curl localhost
curl: (7) Failed to connect to localhost port 80 after 0 ms: S’ha refusat la connexió

I si mires el log del contenidor trobaràs aquesta explicació:

$ docker logs cluster_proxy_1 
...
[ALERT]    (1) : config : parsing [/usr/local/etc/haproxy/haproxy.cfg:8]: Missing LF on last line, file might have been truncated at position 30.
[ALERT]    (1) : config : Error(s) found in configuration file : /usr/local/etc/haproxy/haproxy.cfg
[ALERT]    (1) : config : Fatal errors found in configuration.

✅ Si no t'ha passat modifica el fitxer haproxy.cfg perquè doni aquest error. És important que aprenguis a solucionar aquestes situacions per tu mateix ja que ChatGPT no ho farà per tu.

Verifica que el proxy funciona:

$ curl localhost
TODO

A Xarxa vam configurar un proxy de manera imperativa.

Ara tenim un fitxer docker-compose.yml que ens permet configurar un proxy de manera declarativa.

Elimina els contenidors:

$ docker-compose stop
$ docker-compose rm

Exemples

A continuació tens conjunt d’aplicacions web que es composen de diversos components que s’han desplegat mitjançant Docker Compose.

Aquestes aplicacions s’utilitzen en diferents activitats formatives:

EtherpadEtherpad és un editor col·laboratiu en temps real de codi obert i basat en web, que permet als autors editar simultàniament un document de text i veure totes les edicions dels participants en temps real.
CryptpadCryptPad és una suite de col·laboració encriptada i de codi obert d'extrem a extrem.
Wordpressundefined
OdooOdoo és un ERP i CRM de còdi obert creat amb Python