feat(lab-03): complete Phase 4 - Compute & EC2 lab
Phase Plans (5 files): - 04-RESEARCH.md: Domain research on Docker limits, healthchecks, EC2 parallels - 04-VALIDATION.md: Success criteria and validation strategy - 04-01-PLAN.md: Test infrastructure (RED phase) - 04-02-PLAN.md: Diátxis documentation - 04-03-PLAN.md: Infrastructure implementation (GREEN phase) Test Scripts (6 files, 1300+ lines): - 01-resource-limits-test.sh: Validate INF-03 compliance - 02-healthcheck-test.sh: Validate healthcheck configuration - 03-enforcement-test.sh: Verify resource limits with docker stats - 04-verify-infrastructure.sh: Infrastructure verification - 99-final-verification.sh: End-to-end student verification - run-all-tests.sh: Test orchestration with fail-fast - quick-test.sh: Fast validation (<30s) Documentation (11 files, 2500+ lines): Tutorials (3): - 01-set-resource-limits.md: EC2 instance types, Docker limits syntax - 02-implement-healthchecks.md: ELB health check parallels - 03-dependencies-with-health.md: depends_on with service_healthy How-to Guides (4): - check-resource-usage.md: docker stats monitoring - test-limits-enforcement.md: Stress testing CPU/memory - custom-healthcheck.md: HTTP, TCP, database healthchecks - instance-type-mapping.md: Docker limits → EC2 mapping Reference (3): - compose-resources-syntax.md: Complete deploy.resources reference - healthcheck-syntax.md: All healthcheck parameters - ec2-instance-mapping.md: Instance type mapping table Explanation (1): - compute-ec2-parallels.md: Container=EC2, Limits=Instance Type, Healthcheck=ELB Infrastructure: - docker-compose.yml: 5 services (web, app, worker, db, stress-test) All services: INF-03 compliant (cpus + memory limits) All services: healthcheck configured EC2 parallels: t2.nano, t2.micro, t2.small, t2.medium, m5.large - Dockerfile: Alpine 3.19 + stress tools + non-root user Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
335
labs/lab-03-compute/tutorial/01-set-resource-limits.md
Normal file
335
labs/lab-03-compute/tutorial/01-set-resource-limits.md
Normal file
@@ -0,0 +1,335 @@
|
||||
# Tutorial 1: Configurare i Limiti delle Risorse
|
||||
|
||||
In questo tutorial imparerai a configurare i limiti delle risorse CPU e memoria per i container Docker, simulando i diversi **EC2 Instance Types** di AWS.
|
||||
|
||||
## Obiettivi di Apprendimento
|
||||
|
||||
Al termine di questo tutorial sarai in grado di:
|
||||
- Comprendere cosa sono gli **EC2 Instance Types** e come si applicano a Docker
|
||||
- Configurare i limiti **CPU** e **memoria** in Docker Compose
|
||||
- Mappare le configurazioni Docker alle istanze EC2
|
||||
- Verificare i limiti delle risorse con `docker stats`
|
||||
|
||||
---
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
- Docker Engine >= 24.0 installato e funzionante
|
||||
- Docker Compose V2 (`docker compose` comando disponibile)
|
||||
- Conoscenza base di Docker e docker-compose.yml
|
||||
|
||||
---
|
||||
|
||||
## Parte 1: EC2 Instance Types - Concetti Fondamentali
|
||||
|
||||
### Cos'è un EC2 Instance Type?
|
||||
|
||||
In AWS, un **Instance Type** definisce:
|
||||
- **vCPUs**: Numero di CPU virtuali
|
||||
- **Memory**: Quantità di RAM
|
||||
- **Use case**: Tipo di carico di lavoro ottimale
|
||||
|
||||
### Famiglie di Instance Types Principali
|
||||
|
||||
| Famiglia | Prefix | Use Case | Esempio |
|
||||
|----------|--------|----------|---------|
|
||||
| **Burstable** | t2, t3 | Dev/test, basso costo | t2.micro |
|
||||
| **General Purpose** | m5, m6 | Equilibrato CPU/memoria | m5.large |
|
||||
| **Compute Optimized** | c5, c6 | Alta CPU | c5.xlarge |
|
||||
| **Memory Optimized** | r5, r6 | Alta memoria | r5.large |
|
||||
|
||||
### Instance Types Comuni
|
||||
|
||||
| Instance Type | vCPUs | Memory | Use Case |
|
||||
|---------------|-------|--------|----------|
|
||||
| t2.nano | 0.5 | 512 MB | Microservizi |
|
||||
| t2.micro | 1 | 1 GB | Dev/Test |
|
||||
| t2.small | 1 | 2 GB | Web server |
|
||||
| t2.medium | 2 | 4 GB | Application server |
|
||||
| m5.large | 2 | 8 GB | Production app |
|
||||
| m5.xlarge | 4 | 16 GB | High traffic |
|
||||
|
||||
---
|
||||
|
||||
## Parte 2: Limiti delle Risorse in Docker Compose
|
||||
|
||||
### Sintassi di Base
|
||||
|
||||
In Docker Compose, i limiti delle risorse si configurano con la sezione `deploy.resources.limits`:
|
||||
|
||||
```yaml
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: nginx:alpine
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1' # Numero di CPU
|
||||
memory: 1G # Memoria (M, G)
|
||||
```
|
||||
|
||||
### Formati della Memoria
|
||||
|
||||
```yaml
|
||||
memory: 512M # 512 Megabyte
|
||||
memory: 1G # 1 Gigabyte
|
||||
memory: 1024M # 1024 Megabyte (equivale a 1G)
|
||||
```
|
||||
|
||||
### Format delle CPU
|
||||
|
||||
```yaml
|
||||
cpus: '0.5' # Mezza CPU
|
||||
cpus: '1' # 1 CPU completa
|
||||
cpus: '2' # 2 CPU complete
|
||||
cpus: '1.5' # 1.5 CPU (1 completa + 50% di un'altra)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 3: Pratica - Configurare un Container t2.micro
|
||||
|
||||
Creiamo un container simile a un'istanza **t2.micro** (1 vCPU, 1 GB RAM).
|
||||
|
||||
### Step 1: Creare il file docker-compose.yml
|
||||
|
||||
Crea il file `labs/lab-03-compute/docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
# Web server - simula t2.micro
|
||||
web:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-web
|
||||
hostname: web
|
||||
|
||||
# Resource limits - t2.micro parallel
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8080:80"
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Step 2: Verificare la sintassi
|
||||
|
||||
```bash
|
||||
cd labs/lab-03-compute
|
||||
docker compose config
|
||||
```
|
||||
|
||||
Se non ci sono errori, vedrai la configurazione completa.
|
||||
|
||||
### Step 3: Avviare il container
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Step 4: Verificare i limiti applicati
|
||||
|
||||
```bash
|
||||
# Ispeziona il container per vedere i limiti
|
||||
docker inspect lab03-web --format '{{.HostConfig.NanoCpus}}'
|
||||
# Output: 1000000000 (1e9 = 1 CPU)
|
||||
|
||||
docker inspect lab03-web --format '{{.HostConfig.Memory}}'
|
||||
# Output: 1073741824 (1 GB in byte)
|
||||
```
|
||||
|
||||
### Step 5: Monitorare l'utilizzo delle risorse
|
||||
|
||||
```bash
|
||||
# Mostra l'utilizzo in tempo reale
|
||||
docker stats lab03-web
|
||||
```
|
||||
|
||||
Premi `Ctrl+C` per uscire.
|
||||
|
||||
```bash
|
||||
# Snapshot singolo
|
||||
docker stats --no-stream
|
||||
```
|
||||
|
||||
**Output previsto:**
|
||||
```
|
||||
CONTAINER NAME CPU % MEM USAGE / LIMIT MEM %
|
||||
12345 lab03-web 0.01% 2.5MiB / 1GiB 0.24%
|
||||
```
|
||||
|
||||
Nota che il **LIMIT** è 1GiB, configurato correttamente.
|
||||
|
||||
---
|
||||
|
||||
## Parte 4: Pratica - Configurare un Container t2.small
|
||||
|
||||
Ora configuriamo un container simile a **t2.small** (1 vCPU, 2 GB RAM).
|
||||
|
||||
### Aggiungi il servizio app al docker-compose.yml:
|
||||
|
||||
```yaml
|
||||
# Application server - simula t2.small
|
||||
app:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-app
|
||||
hostname: app
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8081:80"
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Avviare e verificare
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
```bash
|
||||
# Verifica i limiti
|
||||
docker inspect lab03-app --format 'CPU: {{.HostConfig.NanoCpus}} CPUs, Memory: {{.HostConfig.Memory}} bytes'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 5: Pratica - Configurare un Container t2.medium
|
||||
|
||||
Configuriamo un container **t2.medium** (2 vCPU, 4 GB RAM).
|
||||
|
||||
### Aggiungi il servizio worker:
|
||||
|
||||
```yaml
|
||||
# Worker - simula t2.medium
|
||||
worker:
|
||||
image: alpine:3.19
|
||||
container_name: lab03-worker
|
||||
hostname: worker
|
||||
|
||||
command: ["sh", "-c", "sleep 3600"]
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Avviare e verificare
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker stats --no-stream lab03-worker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 6: Tabella di Mapping Completa
|
||||
|
||||
Ecco la tabella completa di mapping tra Docker e EC2:
|
||||
|
||||
| Docker Limits | EC2 Instance | vCPUs | Memory | Use Case |
|
||||
|---------------|--------------|-------|--------|----------|
|
||||
| `cpus: '0.5'`<br>`memory: 512M` | t2.nano | 0.5 | 512 MB | Microservizi minimi |
|
||||
| `cpus: '1'`<br>`memory: 1G` | t2.micro | 1 | 1 GB | Dev/Test |
|
||||
| `cpus: '1'`<br>`memory: 2G` | t2.small | 1 | 2 GB | Web servers |
|
||||
| `cpus: '2'`<br>`memory: 4G` | t2.medium | 2 | 4 GB | Application |
|
||||
| `cpus: '2'`<br>`memory: 8G` | m5.large | 2 | 8 GB | Production |
|
||||
| `cpus: '4'`<br>`memory: 16G` | m5.xlarge | 4 | 16 GB | High traffic |
|
||||
|
||||
---
|
||||
|
||||
## Parte 7: Verifica Finale
|
||||
|
||||
### Script di Verifica
|
||||
|
||||
Esegui questo comando per verificare tutti i limiti:
|
||||
|
||||
```bash
|
||||
# Per ogni servizio, mostra i limiti
|
||||
for service in web app worker; do
|
||||
echo "Service: $service"
|
||||
docker inspect "lab03-$service" --format ' CPUs: {{.HostConfig.NanoCpus}}'
|
||||
docker inspect "lab03-$service" --format ' Memory: {{.HostConfig.Memory}}'
|
||||
done
|
||||
```
|
||||
|
||||
### Controllo INF-03
|
||||
|
||||
Lo script di verifica del lab controllerà automaticamente che tutti i servizi abbiano i limiti configurati (requisito **INF-03**).
|
||||
|
||||
```bash
|
||||
bash tests/01-resource-limits-test.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Risoluzione Problemi
|
||||
|
||||
### Errore: "no matching resources"
|
||||
|
||||
**Causa:** I limiti specificati superano le risorse disponibili sull'host.
|
||||
|
||||
**Soluzione:**
|
||||
- Riduci i limiti CPU/memoria
|
||||
- Libera risorse sull'host
|
||||
- Usa un'istanza tipo più piccola
|
||||
|
||||
### Errore: "invalid memory format"
|
||||
|
||||
**Causa:** Formato della memoria non valido.
|
||||
|
||||
**Soluzione:**
|
||||
- Usa `M` per Megabyte (es. `512M`)
|
||||
- Usa `G` per Gigabyte (es. `1G`)
|
||||
- Non usare spazi o formati misti
|
||||
|
||||
### Container OOM Killed
|
||||
|
||||
**Causa:** Il container sta tentando di usare più memoria del limite.
|
||||
|
||||
**Soluzione:**
|
||||
- Aumenta il limite `memory`
|
||||
- Indaga il consumo di memoria dell'applicazione
|
||||
- Verifica memory leak
|
||||
|
||||
---
|
||||
|
||||
## Riepilogo
|
||||
|
||||
In questo tutorial hai imparato:
|
||||
|
||||
✓ **Concetto:** EC2 Instance Types definiscono CPU e memoria
|
||||
✓ **Sintassi:** `deploy.resources.limits` in Docker Compose
|
||||
✓ **Mapping:** Docker limits → EC2 instances
|
||||
✓ **Verifica:** `docker stats` per monitorare l'utilizzo
|
||||
✓ **Compliance:** INF-03 richiede limiti per tutti i container
|
||||
|
||||
---
|
||||
|
||||
## Prossimi Passi
|
||||
|
||||
Nel prossimo tutorial imparerai a:
|
||||
- Implementare **healthchecks** per monitorare lo stato dei servizi
|
||||
- Configurare dipendenze tra servizi con `depends_on`
|
||||
- Mappare healthchecks Docker agli **ELB Health Checks** di AWS
|
||||
|
||||
Continua con **Tutorial 2: Implementare Healthchecks** →
|
||||
347
labs/lab-03-compute/tutorial/02-implement-healthchecks.md
Normal file
347
labs/lab-03-compute/tutorial/02-implement-healthchecks.md
Normal file
@@ -0,0 +1,347 @@
|
||||
# Tutorial 2: Implementare Healthchecks
|
||||
|
||||
In questo tutorial imparerai a configurare healthchecks per i container Docker, simulando gli **ELB Health Checks** di AWS.
|
||||
|
||||
## Obiettivi di Apprendimento
|
||||
|
||||
Al termine di questo tutorial sarai in grado di:
|
||||
- Comprendere cosa sono i healthchecks e perché sono importanti
|
||||
- Configurare healthchecks HTTP, CMD e custom
|
||||
- Mappare healthchecks Docker agli ELB Health Checks
|
||||
- Monitorare lo stato di salute dei container
|
||||
|
||||
---
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
- Completamento di Tutorial 1: Configurare i Limiti delle Risorse
|
||||
- docker-compose.yml con servizi configurati
|
||||
- Container che espongono porte HTTP o servizi monitorabili
|
||||
|
||||
---
|
||||
|
||||
## Parte 1: Healthchecks - Concetti Fondamentali
|
||||
|
||||
### Cos'è un Healthcheck?
|
||||
|
||||
Un **healthcheck** è un comando periodico che verifica se un container è "sano" (healthy).
|
||||
|
||||
**Stati di un Container:**
|
||||
1. **created** - Container creato ma non avviato
|
||||
2. **starting** - Container in avvio (healthcheck in corso)
|
||||
3. **healthy** - Healthcheck passing (container OK)
|
||||
4. **unhealthy** - Healthcheck failing (container problematico)
|
||||
5. **exited** - Container terminato
|
||||
|
||||
### Perché sono Importanti?
|
||||
|
||||
- **Failover:** Sostituisce container non sani
|
||||
- **Zero-downtime:** Attiva solo container sani nel load balancer
|
||||
- **Dependencies:** Altri servizi aspettano che il container diventi healthy
|
||||
- **Monitoring:** Avvisa automaticamente su problemi
|
||||
|
||||
### Parallelismo: ELB Health Checks
|
||||
|
||||
| Docker | AWS ELB |
|
||||
|--------|---------|
|
||||
| healthcheck.test | Health check path/protocol |
|
||||
| healthcheck.interval | Health check interval (default 30s) |
|
||||
| healthcheck.timeout | Health check timeout (default 5s) |
|
||||
| healthcheck.retries | Unhealthy threshold |
|
||||
| healthcheck.start_period | Grace period |
|
||||
|
||||
---
|
||||
|
||||
## Parte 2: Sintassi Healthcheck in Docker Compose
|
||||
|
||||
### Sintassi di Base
|
||||
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
image: nginx:alpine
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
```
|
||||
|
||||
### Parametri Spiegati
|
||||
|
||||
| Parametro | Default | Descrizione |
|
||||
|-----------|---------|-------------|
|
||||
| test | - | Comando da eseguire (richiesto) |
|
||||
| interval | 30s | Frequenza del check |
|
||||
| timeout | 30s | Tempo massimo per completare |
|
||||
| retries | 3 | Tentativi prima di标记 unhealthy |
|
||||
| start_period | 0s | Grace period all'avvio |
|
||||
|
||||
---
|
||||
|
||||
## Parte 3: Pratica - HTTP Healthcheck per Web Server
|
||||
|
||||
### Step 1: Aggiungere healthcheck al servizio web
|
||||
|
||||
Modifica `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
web:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-web
|
||||
hostname: web
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
|
||||
# HTTP Healthcheck
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8080:80"
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Step 2: Avviare e verificare
|
||||
|
||||
```bash
|
||||
docker compose up -d web
|
||||
```
|
||||
|
||||
### Step 3: Monitorare lo stato di salute
|
||||
|
||||
```bash
|
||||
# Mostra stato di salute
|
||||
docker ps
|
||||
|
||||
# Output:
|
||||
# CONTAINER IMAGE STATUS
|
||||
# lab03-web nginx:alpine Up 30 seconds (healthy)
|
||||
```
|
||||
|
||||
### Step 4: Ispezionare i dettagli del healthcheck
|
||||
|
||||
```bash
|
||||
docker inspect lab03-web --format '{{json .State.Health}}' | jq
|
||||
```
|
||||
|
||||
**Output JSON:**
|
||||
```json
|
||||
{
|
||||
"Status": "healthy",
|
||||
"FailingStreak": 0,
|
||||
"Log": [
|
||||
{
|
||||
"Start": "2024-03-25T10:00:00Z",
|
||||
"End": "2024-03-25T10:00:00Z",
|
||||
"ExitCode": 0,
|
||||
"Output": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 4: Pratica - Database Healthcheck
|
||||
|
||||
### Step 1: Aggiungere servizio database
|
||||
|
||||
```yaml
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: lab03-db
|
||||
hostname: db
|
||||
|
||||
environment:
|
||||
POSTGRES_DB: lab03_db
|
||||
POSTGRES_USER: lab03_user
|
||||
POSTGRES_PASSWORD: lab03_password
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
|
||||
# Database Healthcheck
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U lab03_user -d lab03_db || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Nota che il database:
|
||||
- Ha più `retries` (5 vs 3) - i database partono più lentamente
|
||||
- Ha `start_period` più lungo (10s vs 5s) - grace period esteso
|
||||
|
||||
### Step 2: Verificare il database diventi healthy
|
||||
|
||||
```bash
|
||||
docker compose up -d db
|
||||
|
||||
# Attendere che diventi healthy
|
||||
watch -n 2 'docker ps --filter "name=lab03-db" --format "table {{.Names}}\t{{.Status}}"'
|
||||
```
|
||||
|
||||
Premi `Ctrl+C` quando vedi `(healthy)`.
|
||||
|
||||
---
|
||||
|
||||
## Parte 5: Pratica - CMD-SHELL Healthcheck
|
||||
|
||||
Per comandi più complessi, usa `CMD-SHELL`:
|
||||
|
||||
```yaml
|
||||
app:
|
||||
image: myapp:latest
|
||||
container_name: lab03-app
|
||||
hostname: app
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
|
||||
# Custom healthcheck with shell
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost/health || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Esempi di Healthcheck
|
||||
|
||||
**HTTP con curl:**
|
||||
```yaml
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
|
||||
```
|
||||
|
||||
**TCP connection:**
|
||||
```yaml
|
||||
test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"]
|
||||
```
|
||||
|
||||
**File existence:**
|
||||
```yaml
|
||||
test: ["CMD-SHELL", "test -f /tmp/ready || exit 1"]
|
||||
```
|
||||
|
||||
**Simple always-succeed:**
|
||||
```yaml
|
||||
test: ["CMD-SHELL", "exit 0"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 6: ELB Health Check Parallelism
|
||||
|
||||
### Mapping Completo
|
||||
|
||||
| Docker Healthcheck | ELB Health Check | AWS Default | Docker Default |
|
||||
|--------------------|------------------|-------------|----------------|
|
||||
| test | Protocol + Path | TCP:80 | Configurato |
|
||||
| interval | Interval | 30s | 30s |
|
||||
| timeout | Timeout | 5s | 30s |
|
||||
| retries | Unhealthy Threshold | 2 | 3 |
|
||||
| start_period | - | - | 0s |
|
||||
|
||||
### Configurazione Equivalente AWS
|
||||
|
||||
**Docker:**
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 2
|
||||
```
|
||||
|
||||
**ELB (equivalente):**
|
||||
```json
|
||||
{
|
||||
"TargetGroup": {
|
||||
"HealthCheckProtocol": "HTTP",
|
||||
"HealthCheckPath": "/health",
|
||||
"HealthCheckIntervalSeconds": 30,
|
||||
"HealthCheckTimeoutSeconds": 5,
|
||||
"UnhealthyThresholdCount": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 7: Debugging Healthchecks
|
||||
|
||||
### Problema: Container rimane "starting"
|
||||
|
||||
**Causa:** Healthcheck non passa entro `start_period`
|
||||
|
||||
**Soluzione:**
|
||||
- Aumenta `start_period`
|
||||
- Riduci `interval` per check più frequenti
|
||||
- Verifica che il servizio sia realmente pronto
|
||||
|
||||
### Problema: Container diventa "unhealthy"
|
||||
|
||||
**Debug:**
|
||||
```bash
|
||||
# Guarda i log del healthcheck
|
||||
docker inspect lab03-web --format '{{range .State.Health.Log}}{{.Start}} - {{.ExitCode}} - {{.Output}}{{"\n"}}{{end}}'
|
||||
|
||||
# Esegui il comando manualmente
|
||||
docker exec lab03-web wget --spider -q http://localhost/
|
||||
```
|
||||
|
||||
### Problema: Healthcheck troppo lento
|
||||
|
||||
**Ottimizza:**
|
||||
- Usa check HTTP leggeri (non pagine pesanti)
|
||||
- Usa check TCP invece di HTTP se possibile
|
||||
- Riduci `timeout` se il check è veloce
|
||||
|
||||
---
|
||||
|
||||
## Riepilogo
|
||||
|
||||
In questo tutorial hai imparato:
|
||||
|
||||
✓ **Concetto:** Healthchecks verificano lo stato di salute dei container
|
||||
✓ **Sintassi:** Parametri test, interval, timeout, retries, start_period
|
||||
✓ **Tipi:** HTTP, CMD-SHELL, custom healthchecks
|
||||
✓ **Parallelismo:** Healthcheck Docker → ELB Health Check
|
||||
✓ **Monitoring:** `docker ps` mostra stato (healthy/unhealthy)
|
||||
|
||||
---
|
||||
|
||||
## Prossimi Passi
|
||||
|
||||
Nel prossimo tutorial imparerai a:
|
||||
- Configurare **dipendenze** tra servizi
|
||||
- Usare `depends_on` con `condition: service_healthy`
|
||||
- Implementare startup ordinato per applicazioni multi-tier
|
||||
|
||||
Continua con **Tutorial 3: Dipendenze con Healthchecks** →
|
||||
410
labs/lab-03-compute/tutorial/03-dependencies-with-health.md
Normal file
410
labs/lab-03-compute/tutorial/03-dependencies-with-health.md
Normal file
@@ -0,0 +1,410 @@
|
||||
# Tutorial 3: Dipendenze tra Servizi con Healthchecks
|
||||
|
||||
In questo tutorial imparerai a configurare dipendenze tra servizi usando `depends_on`, assicurando che i servizi partano nell'ordine corretto.
|
||||
|
||||
## Obiettivi di Apprendimento
|
||||
|
||||
Al termine di questo tutorial sarai in grado di:
|
||||
- Comprendere le dipendenze tra servizi in applicazioni multi-tier
|
||||
- Configurare `depends_on` con conditions
|
||||
- Implementare startup ordinato con healthchecks
|
||||
- Verificare l'ordine di avvio dei servizi
|
||||
|
||||
---
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
- Completamento di Tutorial 1 e 2
|
||||
- Servizi configurati con resource limits e healthchecks
|
||||
- Comprensione base di architetture multi-tier
|
||||
|
||||
---
|
||||
|
||||
## Parte 1: Dipendenze tra Servizi
|
||||
|
||||
### Cos'è una Dipendenza?
|
||||
|
||||
In un'applicazione multi-tier, i servizi dipendono l'uno dall'altro:
|
||||
|
||||
```
|
||||
Web → App → Database
|
||||
```
|
||||
|
||||
- Il **Web** server ha bisogno dell'**App** server
|
||||
- L'**App** server ha bisogno del **Database**
|
||||
- Il **Database** non dipende da nessuno (parte prima)
|
||||
|
||||
### Perché sono Importanti?
|
||||
|
||||
1. **Prevenzione Errori:** Evita connection error a servizi non pronti
|
||||
2. **Startup Affidabile:** Ogni servizio aspetta che le sue dipendenze siano pronte
|
||||
3. **Zero-Downtime:** Durante deployment, i nuovi servizi aspettano i vecchi
|
||||
4. **Debugging:** Problemi di connessione sono più facili da diagnosticare
|
||||
|
||||
---
|
||||
|
||||
## Parte 2: Tipi di Dipendenze
|
||||
|
||||
### depends_on: service_started (Default)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
depends_on:
|
||||
- db
|
||||
```
|
||||
|
||||
**Comportamento:** `app` parte quando `db` è **avviato** (non necessariamente healthy)
|
||||
|
||||
**Problema:** Il database potrebbe ancora essere inizializzando → connection errors
|
||||
|
||||
### depends_on: service_healthy (Raccomandato)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
```
|
||||
|
||||
**Comportamento:** `app` parte quando `db` è **healthy** (dopo healthcheck pass)
|
||||
|
||||
**Vantaggio:** Il database è completamente pronto → nessun connection error
|
||||
|
||||
---
|
||||
|
||||
## Parte 3: Pratica - Architettura Multi-Tier
|
||||
|
||||
Creiamo un'applicazione a 3 tier con dipendenze:
|
||||
|
||||
```
|
||||
Web (t2.micro) → App (t2.small) → DB (t2.medium)
|
||||
```
|
||||
|
||||
### Step 1: Configurare il Database (Primo)
|
||||
|
||||
```yaml
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
# Tier 3: Database - nessuna dipendenza
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: lab03-db
|
||||
hostname: db
|
||||
|
||||
environment:
|
||||
POSTGRES_DB: lab03_db
|
||||
POSTGRES_USER: lab03_user
|
||||
POSTGRES_PASSWORD: lab03_password
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U lab03_user -d lab03_db || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
```
|
||||
|
||||
**Nota:** Il database non ha `depends_on` - è il primo a partire.
|
||||
|
||||
### Step 2: Configurare l'App Server
|
||||
|
||||
```yaml
|
||||
# Tier 2: Application - dipende dal database
|
||||
app:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-app
|
||||
hostname: app
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8081:80"
|
||||
|
||||
# Dipende dal database healthy
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
**Nota:** `app` parte SOLO quando `db` è `healthy`.
|
||||
|
||||
### Step 3: Configurare il Web Server
|
||||
|
||||
```yaml
|
||||
# Tier 1: Web - dipende dall'app
|
||||
web:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-web
|
||||
hostname: web
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8080:80"
|
||||
|
||||
# Dipende dall'app healthy
|
||||
depends_on:
|
||||
app:
|
||||
condition: service_healthy
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
**Nota:** `web` parte SOLO quando `db` → `app` sono entrambi `healthy`.
|
||||
|
||||
---
|
||||
|
||||
## Parte 4: Avvio e Verifica
|
||||
|
||||
### Step 1: Avviare tutti i servizi
|
||||
|
||||
```bash
|
||||
cd labs/lab-03-compute
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Step 2: Osservare l'ordine di avvio
|
||||
|
||||
```bash
|
||||
# Monitora lo stato in tempo reale
|
||||
watch -n 2 'docker ps --format "table {{.Names}}\t{{.Status}}"'
|
||||
```
|
||||
|
||||
**Sequenza prevista:**
|
||||
|
||||
1. `lab03-db` parte → `starting` → `(healthy)` dopo ~10-20s
|
||||
2. `lab03-app` parte → `starting` → `(healthy)` dopo che `db` è healthy
|
||||
3. `lab03-web` parte → `starting` → `(healthy)` dopo che `app` è healthy
|
||||
|
||||
### Step 3: Verificare l'ordine nei log
|
||||
|
||||
```bash
|
||||
docker compose logs --tail=50
|
||||
```
|
||||
|
||||
Cerca messaggi tipo:
|
||||
```
|
||||
lab03-db | database system is ready to accept connections
|
||||
lab03-app | Waiting for db to be healthy...
|
||||
lab03-web | Waiting for app to be healthy...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 5: Testare le Dipendenze
|
||||
|
||||
### Test 1: Riavviare il database
|
||||
|
||||
```bash
|
||||
# Ferma il database
|
||||
docker compose stop db
|
||||
|
||||
# Guarda lo stato di app e web
|
||||
docker ps
|
||||
```
|
||||
|
||||
**Risultato:** `app` e `web` rimangono in esecuzione (non dipendono dal runtime di db, solo dall'avvio iniziale)
|
||||
|
||||
### Test 2: Ferma e riavvia l'app
|
||||
|
||||
```bash
|
||||
# Ferma l'app
|
||||
docker compose stop app
|
||||
|
||||
# Guarda lo stato del web
|
||||
docker ps
|
||||
```
|
||||
|
||||
**Risultato:** `web` continua a funzionare (stessa logica)
|
||||
|
||||
### Test 3: Riavvio completo
|
||||
|
||||
```bash
|
||||
# Ferma tutto
|
||||
docker compose down
|
||||
|
||||
# Riavvia
|
||||
docker compose up -d
|
||||
|
||||
# Osserva l'ordine di startup
|
||||
docker compose up -d && docker compose logs -f
|
||||
```
|
||||
|
||||
Vedrai: `db` → `app` → `web` in ordine.
|
||||
|
||||
---
|
||||
|
||||
## Parte 6: Parallelismo AWS
|
||||
|
||||
### ECS Task Definitions
|
||||
|
||||
In AWS ECS, le dipendenze si configurano nel Task Definition:
|
||||
|
||||
```json
|
||||
{
|
||||
"containerDefinitions": [
|
||||
{
|
||||
"name": "db",
|
||||
"essential": true,
|
||||
"healthCheck": {
|
||||
"command": ["CMD-SHELL", "pg_isready -U postgres"],
|
||||
"interval": 10,
|
||||
"timeout": 5,
|
||||
"retries": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "app",
|
||||
"essential": true,
|
||||
"dependsOn": [
|
||||
{
|
||||
"containerName": "db",
|
||||
"condition": "HEALTHY"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "web",
|
||||
"essential": true,
|
||||
"dependsOn": [
|
||||
{
|
||||
"containerName": "app",
|
||||
"condition": "HEALTHY"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Condizioni di Dipendenza ECS
|
||||
|
||||
| Condizione | Docker Equivalente | Descrizione |
|
||||
|------------|-------------------|-------------|
|
||||
| START | `depends_on` (default) | Container avviato |
|
||||
| HEALTHY | `condition: service_healthy` | Healthcheck passing |
|
||||
| COMPLETE | - | Container exit code 0 |
|
||||
| SUCCESS | - | Container exit code 0 |
|
||||
|
||||
---
|
||||
|
||||
## Parte 7: Best Practices
|
||||
|
||||
### 1. Usa Sempre service_healthy
|
||||
|
||||
```yaml
|
||||
# ✓ GOOD
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
# ✗ AVOID (se possibile)
|
||||
depends_on:
|
||||
- db
|
||||
```
|
||||
|
||||
### 2. Configura healthcheck appropriati
|
||||
|
||||
```yaml
|
||||
# Database - startup più lento
|
||||
healthcheck:
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
# Web server - startup veloce
|
||||
healthcheck:
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
```
|
||||
|
||||
### 3. Previeni loop di dipendenze
|
||||
|
||||
```yaml
|
||||
# ✗ WRONG - circular dependency
|
||||
web:
|
||||
depends_on:
|
||||
app: {condition: service_healthy}
|
||||
app:
|
||||
depends_on:
|
||||
web: {condition: service_healthy} # LOOP!
|
||||
|
||||
# ✓ GOOD - directional dependency
|
||||
web:
|
||||
depends_on:
|
||||
app: {condition: service_healthy}
|
||||
app:
|
||||
depends_on:
|
||||
db: {condition: service_healthy} # OK
|
||||
db:
|
||||
# No dependencies - base tier
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Riepilogo
|
||||
|
||||
In questo tutorial hai imparato:
|
||||
|
||||
✓ **Concetto:** Dipendenze tra servizi per startup ordinato
|
||||
✓ **Sintassi:** `depends_on` con `condition: service_healthy`
|
||||
✓ **Pratica:** Architettura multi-tier Web → App → DB
|
||||
✓ **Verifica:** Osservare l'ordine di startup
|
||||
✓ **Parallelismo:** ECS `dependsOn` con condizione HEALTHY
|
||||
|
||||
---
|
||||
|
||||
## Complimenti!
|
||||
|
||||
Hai completato i tutorial di Lab 03 - Compute & EC2!
|
||||
|
||||
**Competenze Acquisite:**
|
||||
- Configurare limiti delle risorse (EC2 instance types)
|
||||
- Implementare healthchecks (ELB health checks)
|
||||
- Gestire dipendenze tra servizi (ECS task definitions)
|
||||
|
||||
**Prossimi Passi:**
|
||||
- Esegui i test di verifica: `bash tests/run-all-tests.sh`
|
||||
- Esplora le guide How-to per procedure specifiche
|
||||
- Consulta i documenti Reference per sintassi completa
|
||||
- Leggi Explanation per approfondire i parallelismi cloud
|
||||
Reference in New Issue
Block a user