Durant sis mòduls has donat per fet que tenies una màquina amb Docker instal·lat. Al mòdul 6 aquella màquina es va multiplicar en un clúster i la pregunta va deixar de ser còmoda: d'on surten els hosts, qui hi instal·la Docker, qui els actualitza i qui respon quan un deixa d'arrencar? Aquesta lliçó comença per l'eina que ho va intentar primer —Docker Machine, avui arxivada— i acaba en com es fa el 2026.

Contingut

  1. El problema real de l'aprovisionament
  2. Què va ser Docker Machine i el seu model de drivers
  3. Les comandes característiques
  4. Per què està arxivat i què fer si te'l trobes
  5. El que sí que va sobreviure: els contextos de Docker
  6. Un context aurora-prod sobre SSH
  7. Desplegar contra un host remot sense canviar de terminal
  8. SSH davant d'exposar el daemon per TCP
  9. El mapa d'alternatives actuals
  10. Instal·lació directa: l'script idempotent
  11. cloud-init: que la màquina neixi amb Docker
  12. Terraform / OpenTofu: infraestructura declarativa
  13. Ansible: configurar i mantenir la flota
  14. Packer i els serveis gestionats
  15. L'host com a superfície d'atac
  16. Hosts propis o gestionat? Criteris de decisió

  1. El problema real de l'aprovisionament

Entre «tinc compte en un proveïdor de núvol» i «docker compose up -d aixeca Aurora Libros en producció» hi ha una llista que algú ha d'executar: crear la VM (mida, regió, disc, xarxa, grup de seguretat), configurar l'accés SSH i un usuari sense privilegis, instal·lar Engine i el plugin de Compose, ajustar el daemon (data-root, logging, live-restore), endurir l'host (tallafocs, pedaços automàtics), desplegar l'aplicació, repetir-ho idèntic al segon host i mantenir-ho durant tres anys. Els dos últims punts són els que separen una eina d'un tutorial: Docker Machine atacava els tres primers i la resta quedava fora.

  1. Què va ser Docker Machine i el seu model de drivers

Docker Machine (docker-machine) va ser un binari oficial, contemporani de Docker Toolbox (2015-2018), amb un propòsit concret: crear una VM amb Docker Engine ja instal·lat —en local o al núvol— i apuntar el teu client local cap a aquell daemon remot.

Aquella segona meitat era la interessant. Recorda 01-03: client i daemon són processos separats que parlen per una API. Si el client arriba a un daemon remot, docker ps llista els contenidors d'un servidor de Frankfurt des del teu portàtil. Docker Machine automatitzava el paperam: crear la VM, instal·lar Engine, generar certificats TLS mutus i exportar les variables que redirigien el client.

graph LR
    DM["docker-machine<br/>(el teu portàtil)"] -->|"1. crea la VM (API del proveïdor)"| VM
    DM -->|"2. instal·la Engine i genera certificats"| VM
    DM -->|"3. exporta DOCKER_HOST i DOCKER_CERT_PATH"| CLI
    CLI["client docker<br/>(el teu portàtil)"] -->|"docker ps sobre TLS"| VM
    VM["VM 'aurora-prod'<br/>dockerd + TLS :2376"]

La peça extensible era el driver: un adaptador per plataforma. La comanda era la mateixa; només canviaven --driver i les seves opcions.

Driver On creava la màquina Estat el 2026
virtualbox, hyperv VM local Obsolet (era Docker Toolbox)
amazonec2, digitalocean Instància / droplet Arxivat amb el projecte
azure, google, openstack VM al núvol públic o privat Arxivat
generic Un host ja existent, via SSH La seva idea sobreviu (vegeu §10)
none Només registrava una màquina externa Substituït per docker context

El driver generic no creava res: es connectava per SSH a una màquina existent i hi instal·lava Docker. Era un instal·lador remot rudimentari, exactament el que avui cobreix millor un gestor de configuració.

  1. Les comandes característiques

Les veuràs en documentació antiga i en scripts heretats. Reconeix-les encara que no les executis:

docker-machine create --driver digitalocean \
  --digitalocean-access-token "$DO_TOKEN" \
  --digitalocean-size s-2vcpu-4gb --digitalocean-region fra1 \
  aurora-prod

docker-machine ls                 # màquines i estat
docker-machine ip aurora-prod     # IP pública
docker-machine ssh aurora-prod    # sessió SSH sense buscar la clau
eval "$(docker-machine env aurora-prod)"   # ← la comanda clau
docker ps                         # llista els contenidors REMOTS!
docker-machine rm aurora-prod     # destrueix la VM al proveïdor

L'eval era la màgia i també el problema. Exportava DOCKER_TLS_VERIFY=1, DOCKER_HOST=tcp://203.0.113.42:2376 i DOCKER_CERT_PATH apuntant als certificats generats.

A partir d'aquí, cada comanda docker d'aquella terminal anava al servidor remot, i obrir una altra pestanya et retornava al daemon local. Més d'un incident cèlebre va néixer d'un docker compose down a la terminal equivocada.

  1. Per què està arxivat i què fer si te'l trobes

Docker va arxivar el repositori el 2021. No hi ha pedaços de seguretat, els drivers fan servir APIs que han canviat i la instal·lació descarrega versions d'Engine que ja no existeixen.

Motiu de l'abandonament Què el va substituir
Cada proveïdor va publicar la seva CLI i la seva API aws, gcloud, az, més Terraform
La infraestructura com a codi es va convertir en estàndard Terraform / OpenTofu, Pulumi
Configurar l'host és un problema a part Ansible, cloud-init
La VM local es va resoldre a l'escriptori Docker Desktop (07-03)
Canviar de daemon no exigeix crear màquines docker context
L'orquestració multinode se la va endur Kubernetes Mòdul 6

Regla pràctica: si un tutorial el fa servir, té més de cinc anys i cal verificar tota la resta del que diu. Si apareix en un script heretat de la teva empresa, no l'executis: identifica quines màquines gestiona, trasllada aquesta informació a contextos o a Terraform i planifica'n la retirada amb qui mantingui la infraestructura.

  1. El que sí que va sobreviure: els contextos de Docker

La idea que valia la pena —un client, diversos daemons— és avui una funció de primera classe: els contextos. Un context és una destinació amb nom. A diferència de l'eval, el canvi és global i persistent, no per terminal.

Comanda Què fa
docker context create <n> --docker host=... Defineix una destinació nova
docker context ls Llista els contextos; * marca l'actiu
docker context use <n> Canvia l'actiu (persistent)
docker context inspect <n> Mostra l'endpoint i la configuració TLS
docker --context <n> <cmd> Executa una sola comanda en una altra destinació
docker context rm <n> Elimina el context (no toca el servidor)

La distinció important: docker-machine rm destruïa una màquina; docker context rm només oblida una adreça. Els contextos no creen, no instal·len i no mantenen res: només apunten. Per això continuen sent útils i per això no substitueixen l'aprovisionament.

  1. Un context aurora-prod sobre SSH

Aurora Libros té un host a libros.example.com amb un usuari deploy del grup docker. Primer, que SSH funcioni amb clau i sense contrasenya, i una entrada a ~/.ssh/config que faci llegible la resta:

ssh-keygen -t ed25519 -C "deploy@aurora" -f ~/.ssh/aurora_deploy
ssh-copy-id -i ~/.ssh/aurora_deploy.pub [email protected]
Host aurora-prod
    HostName libros.example.com
    User deploy
    IdentityFile ~/.ssh/aurora_deploy
    IdentitiesOnly yes

El context és una línia, i la comprovació no exigeix activar-lo:

docker context create aurora-prod \
  --description "Producció Aurora Libros (fra1)" \
  --docker "host=ssh://aurora-prod"

docker --context aurora-prod version --format '{{.Server.Version}}'
# 27.5.1
docker --context aurora-prod ps --format '{{.Names}}\t{{.Status}}'
# aurora-web     Up 9 days
# aurora-api     Up 9 days (healthy)
# aurora-cache   Up 9 days
# aurora-db      Up 9 days (healthy)

Quatre contenidors, nou dies en marxa, sense obrir ni una sola sessió SSH manual.

  1. Desplegar contra un host remot sense canviar de terminal

Compose respecta els contextos, així que el desplegament complet és això:

docker --context aurora-prod compose \
  -f compose.yaml -f compose.prod.yaml \
  --env-file .env.prod up -d

docker --context aurora-prod compose ps
docker --context aurora-prod compose logs -f --tail=50 api

Tres detalls canvien el resultat:

Detall Comportament
Rutes de volumes Es resolen a l'host remot, no al teu portàtil
build: El context de build s'empaqueta i puja per SSH: lent
.env i env_file Es llegeixen a la teva màquina, i els valors viatgen per la xarxa
ports: "8080:8080" Publica a la IP del servidor, no al teu localhost

De la segona fila en surt una regla d'or: en producció no es construeix, es desplega una imatge ja publicada. És el que fa el teu compose.prod.yaml, que referencia ghcr.io/auroralibros/aurora-api:2.0.0 per digest en lloc d'un build:. El pipeline de 06-02 construeix, signa i publica; l'host només fa pull.

Patró defensiu: no facis servir mai docker context use per a producció. Deixa default actiu i escriu --context aurora-prod de manera explícita, o com a molt un alias dprod='docker --context aurora-prod'. És més llarg de teclejar, i aquest és exactament l'avantatge.

  1. SSH davant d'exposar el daemon per TCP

Docker Machine feia servir tcp://IP:2376 amb TLS mutu. Avui ho pots fer, però gairebé mai no hauries de fer-ho.

Aspecte ssh:// tcp:// amb TLS tcp:// sense TLS
Port exposat 22 (ja ho estava) 2376 2375
Autenticació Claus SSH ja gestionades Certificats propis a mantenir Cap
Xifratge Sí Sí No
Rotació de credencials Procediment existent CA pròpia, renovacions, CRL —
Auditoria Registres d'SSH de l'host Només els del daemon —
Risc si es filtra Accés d'aquell usuari root a l'host root per a qualsevol
Recomanació Per defecte Només amb justificació Mai

La fila del risc és la que cal interioritzar. Ja ho vas veure a 05-03: qui parla amb el daemon pot llançar docker run -v /:/host --privileged i és root a la màquina. Un 2375 obert a Internet és una màquina regalada; hi ha bots escanejant aquell port de manera contínua i el patró habitual és minar criptomonedes al teu servidor durant setmanes.

Si alguna eina exigeix l'API per TCP, la resposta correcta és un túnel SSH local, no obrir el port:

ss -lntp | grep -E ':237[56]'    # no hauria de retornar res exposat
ssh -N -L 2375:/var/run/docker.sock deploy@aurora-prod &
DOCKER_HOST=tcp://127.0.0.1:2375 docker ps

  1. El mapa d'alternatives actuals

Eina Què resol Idempotent Quan triar-la
Script SSH propi Instal·lar i configurar Si l'escrius bé 1-2 hosts
cloud-init Configurar en el primer arrencada S'executa un cop Màquines efímeres
Terraform / OpenTofu Crear la infraestructura Sí Infra revisable a Git
Ansible Configurar i mantenir la flota Sí 3+ hosts duradors
Packer Construir la imatge de màquina Sí Autoescalat, flotes
docker context Apuntar a hosts existents N/A Operar, no aprovisionar
Gestionats Evitar l'host N/A No voler mantenir hosts

No competeixen: s'apilen. Un muntatge sa del 2026 és Terraform crea + cloud-init arrenca + Ansible manté + contextos operen.

  1. Instal·lació directa: l'script idempotent

La versió honesta del driver generic. La clau és que es pugui executar deu vegades amb el mateix resultat.

#!/usr/bin/env bash
# provision-host.sh — instal·la Docker Engine a Debian/Ubuntu. Idempotent.
set -euo pipefail

command -v docker >/dev/null 2>&1 \
  || curl -fsSL https://get.docker.com | sh   # Engine + plugins compose/buildx

id -u deploy >/dev/null 2>&1 || sudo useradd -m -s /bin/bash deploy
sudo usermod -aG docker deploy

sudo install -d -m 0755 /etc/docker
sudo tee /etc/docker/daemon.json >/dev/null <<'JSON'
{ "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" },
  "live-restore": true }
JSON
sudo systemctl enable --now docker && sudo systemctl reload docker
docker --version && docker compose version

Avantatge: zero dependències i ho entén qualsevol. Límits: no controla la deriva entre hosts, no sap quina versió hi ha a cadascun i creix fins a tornar-se il·legible. A partir del tercer host, Ansible. I un apunt: get.docker.com és còmode, però és un curl | sh contra Internet; en producció es fa servir el repositori APT oficial amb la seva clau GPG fixada, que és el que l'script fa per sota.

  1. cloud-init: que la màquina neixi amb Docker

cloud-init és l'estàndard de facto en imatges de núvol: llegeix un user-data en la primera arrencada i configura la màquina abans que ningú hi iniciï sessió. És el substitut més directe de docker-machine create.

#cloud-config
# cloud-config.yaml — node d'aplicació d'Aurora Libros
hostname: aurora-app-01
timezone: Europe/Madrid
package_update: true
package_upgrade: true
packages: [ca-certificates, curl, gnupg, ufw, unattended-upgrades, fail2ban]

users:
  - name: deploy
    groups: [sudo]
    shell: /bin/bash
    sudo: "ALL=(ALL) NOPASSWD:ALL"
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... deploy@aurora

write_files:
  - path: /etc/docker/daemon.json
    permissions: "0644"
    content: |
      { "log-driver": "json-file",
        "log-opts": { "max-size": "10m", "max-file": "3" },
        "live-restore": true }
  - path: /etc/apt/apt.conf.d/20auto-upgrades
    content: |
      APT::Periodic::Update-Package-Lists "1";
      APT::Periodic::Unattended-Upgrade "1";

runcmd:   # repositori oficial amb clau GPG fixada, mai curl | sh
  - install -m 0755 -d /etc/apt/keyrings
  - curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
  - chmod a+r /etc/apt/keyrings/docker.asc
  - echo "deb [signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian bookworm stable" > /etc/apt/sources.list.d/docker.list
  - apt-get update
  - DEBIAN_FRONTEND=noninteractive apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  - usermod -aG docker deploy        # el grup existeix després d'instal·lar Engine
  - systemctl enable --now docker
  - ufw default deny incoming && ufw allow 22/tcp && ufw allow 443/tcp && ufw --force enable
final_message: "Node Aurora a punt després de $UPTIME segons"

Es passa com a --user-data en crear la instància, i es valida sense gastar diners amb cloud-init schema --config-file cloud-config.yaml; ja a l'host, cloud-init status --wait espera que acabi i /var/log/cloud-init-output.log explica què va passar. La seva limitació és estructural: s'executa un sol cop. No serveix per canviar alguna cosa en cinquanta màquines ja en marxa.

  1. Terraform / OpenTofu: infraestructura declarativa

Terraform descriu la infraestructura desitjada i calcula el pla per arribar-hi. OpenTofu és la seva bifurcació de codi obert sota la Linux Foundation, nascuda arran del canvi de llicència del 2023; per al que veuràs aquí són intercanviables (tofu en lloc de terraform).

# main.tf — un node d'aplicació d'Aurora Libros
terraform {
  required_providers {
    digitalocean = { source = "digitalocean/digitalocean", version = "~> 2.40" }
  }
}
provider "digitalocean" { token = var.do_token }

resource "digitalocean_droplet" "app" {
  name      = "aurora-app-01"
  image     = "debian-12-x64"
  region    = "fra1"
  size      = "s-2vcpu-4gb"
  ssh_keys  = [digitalocean_ssh_key.deploy.fingerprint]
  user_data = file("${path.module}/cloud-config.yaml")   # ← §11 encaixa aquí
  tags      = ["aurora", "produccio"]
}

resource "digitalocean_firewall" "app" {
  name        = "aurora-app-fw"
  droplet_ids = [digitalocean_droplet.app.id]
  inbound_rule { protocol = "tcp"  port_range = "22"
                 source_addresses = ["203.0.113.0/24"] }    # només l'oficina
  inbound_rule { protocol = "tcp"  port_range = "443"
                 source_addresses = ["0.0.0.0/0", "::/0"] }
}
terraform init && terraform plan -out=plan.bin && terraform apply plan.bin

terraform plan és el motiu pel qual això substitueix els scripts: et diu què passarà abans que passi. I com que viu a Git, l'aprovisionament es revisa en un pull request com qualsevol codi. El fitxer d'estat (terraform.tfstate) conté secrets en clar: va en un backend remot amb bloqueig, mai al repositori.

  1. Ansible: configurar i mantenir la flota

Terraform crea; Ansible manté. Es connecta per SSH, no necessita agent i els seus mòduls són idempotents: descrius l'estat final i només actua si cal.

# inventari.ini
[aurora_app]
aurora-app-01 ansible_host=203.0.113.42
aurora-app-02 ansible_host=203.0.113.43
[aurora_app:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/aurora_deploy
# playbook.yml — instal·la Docker i desplega la pila d'Aurora Libros
- name: Preparar i desplegar Aurora Libros
  hosts: aurora_app
  become: true
  tasks:
    - name: Repositori de Docker
      ansible.builtin.deb822_repository:
        name: docker
        uris: https://download.docker.com/linux/debian
        suites: bookworm
        components: stable
        signed_by: https://download.docker.com/linux/debian/gpg

    - name: Docker Engine i plugins
      ansible.builtin.apt:
        name: [docker-ce, docker-ce-cli, containerd.io,
               docker-buildx-plugin, docker-compose-plugin, python3-docker]
        state: present
        update_cache: true
      notify: reiniciar docker

    - name: Configuració del daemon
      ansible.builtin.copy:
        dest: /etc/docker/daemon.json
        mode: "0644"
        content: '{ "log-driver": "json-file", "log-opts": { "max-size": "10m" } }'
      notify: reiniciar docker

    - name: Descriptors de la pila
      ansible.builtin.copy: { src: "{{ item }}", dest: /opt/aurora/, mode: "0640" }
      loop: [compose.yaml, compose.prod.yaml]

    - name: Autenticar-se a ghcr.io
      community.docker.docker_login:
        registry_url: ghcr.io
        username: auroralibros
        password: "{{ ghcr_token }}"      # xifrat amb ansible-vault

    - name: Aixecar la pila
      community.docker.docker_compose_v2:
        project_src: /opt/aurora
        files: [compose.yaml, compose.prod.yaml]
        pull: always
        state: present

  handlers:
    - name: reiniciar docker
      ansible.builtin.systemd: { name: docker, state: restarted, enabled: true }
ansible-playbook -i inventari.ini playbook.yml --check   # simulacre (= plan)
ansible-playbook -i inventari.ini playbook.yml
ansible-playbook -i inventari.ini playbook.yml --limit aurora-app-02

El ghcr_token va xifrat amb ansible-vault encrypt_string, mai en clar. I a la segona execució veuràs changed=0: això és la idempotència funcionant de debò.

  1. Packer i els serveis gestionats

En lloc d'instal·lar Docker a cada arrencada, Packer construeix un cop una imatge de màquina (AMI, snapshot) que ja ho porta tot, i les instàncies arrenquen en segons. És el raonament de les imatges de contenidor un nivell més avall.

Enfocament Temps fins a útil Deriva entre hosts Quan compensa
cloud-init a cada arrencada 2-4 min Possible (els repos canvien) Pocs hosts
Imatge amb Packer 15-40 s Cap: bit a bit idèntica Autoescalat, flotes

El cost és un pipeline més: cada pedaç obliga a reconstruir i redistribuir la imatge. Amb autoescalat, es paga sol. Però la millor manera de mantenir un host continua sent no tenir-lo:

Opció Què gestiones tu Què desapareix Contrapartida
Instàncies amb contenidors La configuració de l'app Instal·lar Docker Continua havent-hi VM
ECS + Fargate Definició de tasca L'host sencer Lligat al proveïdor
Cloud Run / Container Apps La imatge i les seves variables Host i orquestrador Menys control de xarxa
Kubernetes gestionat Els manifestos (mòdul 6) El pla de control Els nodes continuen sent teus

Per a una imatge com ghcr.io/auroralibros/aurora-api:2.0.0 —sense estat, dotze factors, sondes i aturada ordenada, just el que vas preparar a 06-01— un servei tipus Cloud Run és candidat seriós: li dones imatge, port i variables. Que t'ho puguis plantejar és conseqüència directa del mòdul 6, no una casualitat.

  1. L'host com a superfície d'atac

A 05-03 vas endurir el contenidor. Tot allò és inútil si l'host fa catorze mesos que no rep pedaços.

Vector Mesura mínima Verificació
Kernel i paquets sense pedaços unattended-upgrades actiu sudo unattended-upgrade --dry-run -d
Ports oberts de més Denegar per defecte sudo ufw status verbose
API de Docker exposada Mai 2375/2376 públics ss -lntp | grep 237
SSH amb contrasenya o força bruta PasswordAuthentication no, fail2ban sudo sshd -T | grep -i password
Claus que no caduquen mai Rotació anual documentada Auditar authorized_keys
El grup docker = root Només l'usuari de desplegament getent group docker
Disc ple de logs i imatges Rotació + prune programat docker system df

I una advertència que no és tècnica: acorda per escrit qui manté aquestes màquines. La pregunta «qui aplica els pedaços del kernel de l'host de producció?» ha de tenir un nom per resposta abans que arribi el primer client. Si hi ha equip d'infraestructura o responsable de sistemes, la propietat de l'host, la finestra de manteniment i el procediment d'accés es pacten amb ell; si no n'hi ha, el responsable ets tu, i convé que estigui escrit perquè ningú no ho descobreixi durant un incident.

  1. Hosts propis o gestionat? Criteris de decisió

Criteri Apunta a hosts propis Apunta a gestionat
Mida de l'equip i guàrdies Hi ha algú de sistemes i torns 24×7 Només desenvolupadors, sense guàrdies
Cost a escala Trànsit alt, constant i previsible Trànsit irregular o baix
Compliment normatiu Dades que han d'estar al teu maquinari N'hi ha prou amb els del proveïdor
Necessitat de control Kernel, GPU, xarxa o discos concrets L'aplicació i poca cosa més
Dependència de proveïdor Preocupa molt Assumible
Ubicació Llocs sense servei gestionat Regions estàndard
Temps fins a producció Hi ha setmanes Calen dies

Per a Aurora Libros el 2026 la resposta raonable és mixta: base de dades gestionada —ningú no vol ser el responsable de la recuperació a un punt en el temps de PostgreSQL a les tres de la matinada— i aplicació en hosts propis o en Kubernetes gestionat segons la mida. Que és justament la decisió que analitza la lliçó següent.

Errors Habituals i Consells

  • Seguir un tutorial amb docker-machine el 2026. Arxivat des del 2021; si el tutorial el fa servir, tota la resta també ha caducat.
  • Exposar el 2375 «només un moment per provar». Hi ha bots escanejant aquell port de manera permanent. Un daemon obert és root regalat, i el minat comença en minuts.
  • Confondre context actiu amb màquina de treball. docker context use aurora-prod i oblidar-ho és la recepta del compose down en producció. Fes servir --context explícit sempre.
  • Construir imatges a l'host de producció. El context de build viatja per SSH, la memòria cau no es reaprofita i el servidor gasta CPU en una cosa que li toca al pipeline. Publica i fes pull.
  • Scripts no idempotents. Si executar-lo dues vegades trenca alguna cosa, no és aprovisionament: és un accident ajornat.
  • Desar terraform.tfstate a Git o secrets al user-data. Tots dos són llegibles: el primer per qualsevol amb accés al repositori, el segon des del servei de metadades. Backend remot amb bloqueig i gestor de secrets.
  • Consell: un context per entorn amb descripció clara, i default sempre actiu. La fricció de teclejar --context és deliberada.
  • Consell: comença per cloud-init encara que només tinguis un host; el dia que necessitis el segon, ja estarà resolt. I documenta al README qui manté cada host, amb quina finestra de pedaços i a qui s'avisa: aquella pàgina val més que qualsevol script.

Exercicis

Exercici 1 — Un context i una auditoria remota. Crea un context aurora-lab que apunti a un host per SSH (val una VM local o un altre portàtil). Sense activar-lo mai com a context per defecte, comprova la versió del daemon remot, llista els seus contenidors, verifica que els ports 2375 i 2376 no estan a l'escolta i mostra quant disc consumeix Docker. Escriu-ho com a auditar-host.sh, que ha de retornar codi 1 si detecta el daemon exposat i 2 si no arriba a l'host.

Exercici 2 — cloud-init per a un node d'Aurora Libros. Escriu un cloud-config.yaml que prepari un node amb: usuari deploy amb la teva clau pública i al grup docker, Docker Engine des del repositori oficial, rotació de logs a 10 MB i 3 fitxers, tallafocs que només permeti 22 i 443, SSH sense contrasenya, actualitzacions desateses i /opt/aurora a punt per al compose.yaml. Valida'n la sintaxi sense arrencar cap màquina.

Exercici 3 — Decisió raonada. Aurora Libros obre mercat a Mèxic i necessita una rèplica de la plataforma. Dades: quatre desenvolupadors sense ningú de sistemes, sense guàrdies nocturnes, unes 40 peticions per segon amb pics ×8 en campanya, còpies de la base de dades de 30 dies, i la direcció exigeix poder canviar de proveïdor «si pugen els preus». Escriu una recomanació amb l'opció triada, dues alternatives descartades amb el seu motiu i les tres preguntes que faries al responsable d'infraestructura abans d'executar res.

Solucions

Solució 1.

docker context create aurora-lab --description "Laboratori M7" \
  --docker "host=ssh://[email protected]"
#!/usr/bin/env bash
# auditar-host.sh — auditoria bàsica d'un host Docker remot
set -uo pipefail
CTX="${1:-aurora-lab}"; FALLADA=0

docker --context "$CTX" version --format 'Servidor: {{.Server.Version}} ({{.Server.Arch}})' \
  || { echo "ERROR: no s'arriba al daemon"; exit 2; }
docker --context "$CTX" ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}'
docker --context "$CTX" system df

HOST=$(docker context inspect "$CTX" --format '{{.Endpoints.docker.Host}}' | sed -E 's#ssh://##')
if ssh "$HOST" 'ss -lntp 2>/dev/null | grep -E ":(2375|2376)\b"'; then
  echo "CRÍTIC: l'API de Docker escolta a TCP"; FALLADA=1
else
  echo "OK: el daemon no escolta a 2375/2376"
fi
exit "$FALLADA"

Claus: --context explícit a cada comanda (mai use); docker context inspect --format dedueix l'host sense duplicar configuració; i els codis de sortida distingeixen «no hi arribo» (2) de «hi arribo i està mal configurat» (1), que és el que permet ficar l'script en un cron o en un pipeline.

Solució 2.

Partint del fitxer de §11, cal afegir tres blocs i una línia:

#cloud-config
hostname: aurora-mx-01
# ... users, packages, write_files i runcmd de §11 ...

write_files:
  - path: /etc/ssh/sshd_config.d/99-aurora.conf     # ← SSH sense contrasenya
    content: |
      PasswordAuthentication no
      PermitRootLogin no

runcmd:
  # ... instal·lació d'Engine idèntica a §11 ...
  - usermod -aG docker deploy                       # després d'instal·lar Engine
  - install -d -o deploy -g deploy -m 0750 /opt/aurora   # ← destinació del compose
  - ufw default deny incoming && ufw allow 22/tcp && ufw allow 443/tcp && ufw --force enable
  - systemctl restart ssh
cloud-init schema --config-file cloud-config.yaml
# Valid schema cloud-config.yaml

Tres detalls marquen la diferència: l'usermod -aG docker deploy va a runcmd i després d'instal·lar Engine, perquè abans el grup no existeix; ufw denega l'entrant per defecte i només obre 22 i 443, mai 2375/2376; i no hi ha ni un sol secret al fitxer, perquè el user-data és llegible des del servei de metadades per qualsevol que entri a la màquina.

Solució 3. Opció triada: aplicació en Kubernetes gestionat (o Cloud Run) + PostgreSQL gestionat a la regió de Mèxic.

Dada de l'enunciat Implicació
Quatre desenvolupadors, ningú de sistemes Ningú no pot aplicar pedaços a hosts ni respondre de nit
Sense guàrdies nocturnes Un host propi caigut a les 3:00 no té amo
40 req/s amb pics ×8 Exigeix autoescalat real; l'HPA de 06-06 ja està escrit
Còpies de 30 dies La recuperació a un punt en el temps gestionada evita un rol inexistent
«Poder canviar de proveïdor» Empeny cap a Kubernetes + imatge OCI, no cap a serveis propietaris

Descartades: (a) hosts propis amb Compose, que aguantaria 40 req/s però obligaria a escalar a mà en el pitjor moment de l'any i deixaria el sistema operatiu sense amo; (b) Fargate o App Service, que resolen l'operació però xoquen amb l'exigència de portabilitat, mentre que Kubernetes conserva els manifestos del mòdul 6 gairebé tal com estan.

Preguntes prèvies: qui és el propietari declarat de la plataforma mexicana i quina és la seva finestra de manteniment?; hi ha requisit legal de residència de dades que impedeixi replicar a Europa?; quin és el pressupost mensual màxim i es prefereix pagar més per no contractar algú de sistemes? La tercera sol decidir de debò, i no és una pregunta tècnica.

Conclusió

Has tancat el buit que quedava sota la plataforma: les màquines. Saps què va ser Docker Machine, què resolia amb els seus drivers i el seu eval $(docker-machine env), i per què està arxivat des del 2021; si el veus en un tutorial o en un script heretat, saps llegir-lo i saps com retirar-lo.

El que va sobreviure el tens a les mans: els contextos. Vas crear aurora-prod sobre SSH, vas comprovar el daemon remot sense obrir sessió, vas desplegar amb docker --context aurora-prod compose up -d i vas entendre els tres paranys —volums que es resolen al servidor, contextos de build que viatgen per la xarxa i ports que es publiquen on no mires—, amb la regla que se'n deriva: en producció no es construeix, es fa pull d'una imatge signada. I tens clar per què ssh:// guanya a tcp://, i per què un 2375 obert no és un descuit sinó una màquina regalada.

Del costat modern domines el repartiment de papers: cloud-init perquè l'host neixi configurat, Terraform o OpenTofu per crear-lo de manera declarativa i revisable en un pull request, Ansible per mantenir la flota sense deriva i desplegar la pila amb mòduls idempotents, Packer quan l'arrencada ha de durar segons, i els serveis gestionats quan la millor manera de cuidar un host és no tenir-lo. Amb la taula de criteris pots defensar l'elecció amb arguments, no amb gustos. I t'endús una cosa que no és una comanda: l'host és superfície d'atac —s'hi apliquen pedaços, s'hi posa tallafocs, s'audita— i algú amb nom i cognoms n'ha de ser el responsable abans que arribi el primer client.

A la lliçó següent posem cara a cara les dues maneres de desplegar que ja saps fer servir: Docker Compose i Kubernetes. No com un duel, sinó com una decisió amb criteris, amb equivalències exactes entre tots dos formats i amb una resposta honesta a la pregunta que gairebé ningú no fa en veu alta: quant costa de debò mantenir un clúster.

Docker: De Principiant a Avançat

Mòdul 1: Introducció a Docker

Mòdul 2: Treballant amb Imatges Docker

Mòdul 3: Contenidors Docker

Mòdul 4: Docker Compose

Mòdul 5: Conceptes Avançats de Docker

Mòdul 6: Docker en Producció

Mòdul 7: Ecosistema i Eines de Docker

© Copyright 2026. Tots els drets reservats