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
- El problema real de l'aprovisionament
- Què va ser Docker Machine i el seu model de drivers
- Les comandes característiques
- Per què està arxivat i què fer si te'l trobes
- El que sí que va sobreviure: els contextos de Docker
- Un context
aurora-prodsobre SSH - Desplegar contra un host remot sense canviar de terminal
- SSH davant d'exposar el daemon per TCP
- El mapa d'alternatives actuals
- Instal·lació directa: l'script idempotent
- cloud-init: que la màquina neixi amb Docker
- Terraform / OpenTofu: infraestructura declarativa
- Ansible: configurar i mantenir la flota
- Packer i els serveis gestionats
- L'host com a superfície d'atac
- Hosts propis o gestionat? Criteris de decisió
- 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.
- 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ó.
- 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ïdorL'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.
- 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.
- 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.
- Un context
aurora-prod sobre SSH
aurora-prod sobre SSHAurora 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 yesEl 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.
- 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 apiTres 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.
- 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
- 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.
- 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 versionAvantatge: 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.
- 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.
- 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 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.
- 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-02El 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ò.
- 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.
- 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.
- 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-machineel 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-prodi oblidar-ho és la recepta delcompose downen producció. Fes servir--contextexplí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.tfstatea Git o secrets aluser-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
defaultsempre 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
READMEqui 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 sshTres 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
- Què és Docker?
- Instal·lant Docker
- Arquitectura de Docker
- Comandes Bàsiques de Docker
- Entenent les Imatges de Docker
- Creant el teu Primer Contenidor Docker
- El Projecte del Curs: la Plataforma Aurora Libros
Mòdul 2: Treballant amb Imatges Docker
- Docker Hub i Repositoris
- Construint Imatges Docker
- Conceptes Bàsics de Dockerfile
- Instruccions Avançades del Dockerfile
- Gestionant Imatges Docker
- Etiquetatge i Publicació d'Imatges
Mòdul 3: Contenidors Docker
- Executant Contenidors
- Cicle de Vida del Contenidor
- Gestionant Contenidors
- Inspecció i Depuració de Contenidors
- Xarxes a Docker
- Persistència de Dades amb Volums
- Límits de Recursos i Polítiques de Reinici
Mòdul 4: Docker Compose
- Introducció a Docker Compose
- Definint Serveis a Docker Compose
- Comandes de Docker Compose
- Aplicacions Multi-Contenidor
- Variables d'Entorn a Docker Compose
- Perfils, Overrides i Múltiples Entorns
- Desenvolupament Local amb Docker Compose
Mòdul 5: Conceptes Avançats de Docker
- Aprofundiment en Xarxes Docker
- Opcions d'Emmagatzematge Docker
- Millors Pràctiques de Seguretat a Docker
- Optimitzant Imatges Docker
- Builds Avançades amb BuildKit i Buildx
- Registre i Monitoratge a Docker
- El Runtime per Dins: Namespaces, Cgroups i Capes
Mòdul 6: Docker en Producció
- Preparar una Imatge per a Producció
- CI/CD amb Docker
- Orquestrant Contenidors amb Docker Swarm
- Introducció a Kubernetes
- Desplegant Contenidors Docker a Kubernetes
- Escalat i Balanceig de Càrrega
- Estratègies de Desplegament i Rollback
