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:
Luca Sacchi Ricciardi
2026-04-03 15:16:58 +02:00
parent 39b9a56850
commit 23a9ffe443
26 changed files with 5457 additions and 1 deletions

View File

@@ -126,7 +126,11 @@
4. Studente comprende il parallelismo tra container con limiti e EC2 instances con instance types
5. Lab include test che verificano resource limits con `docker stats` e healthcheck readiness
**Plans:** TBD
**Plans:** 3
- [ ] [04-01-PLAN.md](.planning/phases/04-lab-03-compute-ec2/04-01-PLAN.md) — Create test infrastructure (Wave 0: resource limits tests, healthcheck tests, enforcement tests, final verification)
- [ ] [04-02-PLAN.md](.planning/phases/04-lab-03-compute-ec2/04-02-PLAN.md) — Create Diátaxis documentation (Tutorial: 3 parts, How-to: 4 guides, Reference: 3 docs, Explanation: EC2 parallels)
- [ ] [04-03-PLAN.md](.planning/phases/04-lab-03-compute-ec2/04-03-PLAN.md) — Create infrastructure (docker-compose.yml with resource limits, healthchecks, Dockerfile, infrastructure verification)
---

View File

@@ -0,0 +1,228 @@
---
phase: 04-lab-03-compute-ec2
plan: 01
type: execute
wave: 0
depends_on: []
files_modified:
- labs/lab-03-compute/tests/01-resource-limits-test.sh
- labs/lab-03-compute/tests/02-healthcheck-test.sh
- labs/lab-03-compute/tests/03-enforcement-test.sh
- labs/lab-03-compute/tests/99-final-verification.sh
- labs/lab-03-compute/tests/run-all-tests.sh
- labs/lab-03-compute/tests/quick-test.sh
autonomous: true
requirements:
- TEST-01
- TEST-05
- INF-03
- LAB-03
user_setup: []
must_haves:
truths:
- "Test scripts exist and validate resource limits before implementation"
- "Tests verify INF-03 compliance (all containers have CPU/memory limits)"
- "Tests verify healthcheck implementation"
- "Tests can enforce resource limits and verify with docker stats"
- "Final verification script provides clear pass/fail report"
artifacts:
- path: "labs/lab-03-compute/tests/01-resource-limits-test.sh"
provides: "Resource limits validation"
min_lines: 80
- path: "labs/lab-03-compute/tests/02-healthcheck-test.sh"
provides: "Healthcheck testing"
min_lines: 100
- path: "labs/lab-03-compute/tests/03-enforcement-test.sh"
provides: "Resource enforcement verification"
min_lines: 120
- path: "labs/lab-03-compute/tests/99-final-verification.sh"
provides: "Student double-check command"
min_lines: 100
- path: "labs/lab-03-compute/tests/run-all-tests.sh"
provides: "Test orchestration with fail-fast"
min_lines: 50
- path: "labs/lab-03-compute/tests/quick-test.sh"
provides: "Quick validation for development"
min_lines: 30
key_links:
- from: "tests/01-resource-limits-test.sh"
to: "docker-compose.yml resources"
via: "yq or grep for deploy.resources.limits"
pattern: "deploy:.*resources:.*limits"
- from: "tests/02-healthcheck-test.sh"
to: "docker-compose.yml healthcheck"
via: "grep for healthcheck section"
pattern: "healthcheck:"
- from: "tests/03-enforcement-test.sh"
to: "docker stats"
via: "docker stats --no-stream for verification"
pattern: "docker.*stats"
- from: "tests/99-final-verification.sh"
to: "INF-03 requirement"
via: "Verify all services have cpu and memory limits"
pattern: "INF-03"
---
<objective>
Create comprehensive test infrastructure for Lab 03 (Compute & EC2) following TDD RED phase methodology. Tests validate Docker Compose resource limits (CPU/memory), healthcheck implementation, and INF-03 compliance (all containers must have resource limits).
Purpose: Establish verification foundation before implementing compute infrastructure. Tests fail initially (RED phase) and pass after implementation (GREEN phase in Plan 04-03).
Output: 6 bash test scripts covering resource limits validation, healthcheck testing, enforcement verification, and final verification for students.
</objective>
<execution_context>
@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
@/home/luca/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/REQUIREMENTS.md
@.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
@.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
@.planning/phases/03-lab-02-network-vpc/03-01-PLAN.md
# Test Patterns from Phase 2 and 3
From labs/lab-01-iam/tests/run-all-tests.sh:
- Use `set -euo pipefail` for error handling
- Color-coded output (GREEN for pass, RED for fail, BLUE for info)
- Summary with pass/fail counts
- Exit code 0 for all pass, 1 for any failure
From labs/lab-02-network/tests/04-verify-infrastructure.sh:
- Parse docker-compose.yml with `docker compose config`
- Use jq for JSON parsing when needed
- Verify compliance with grep patterns
- Use awk for robust counting
# Key Docker Commands for Testing
## Resource Limits Verification
```bash
# Check if service has CPU limit
docker compose config | grep -A 10 "service_name:" | grep -c "cpus:"
# Check if service has memory limit
docker compose config | grep -A 10 "service_name:" | grep -c "memory:"
# Get container limits
docker inspect container_name --format '{{.HostConfig.NanoCpus}}'
docker inspect container_name --format '{{.HostConfig.Memory}}'
```
## Healthcheck Verification
```bash
# Check if service has healthcheck
docker compose config | grep -A 20 "service_name:" | grep -c "healthcheck:"
# Get container health status
docker inspect container_name --format '{{.State.Health.Status}}'
```
## Resource Monitoring
```bash
# Get live stats
docker stats --no-stream
# Get specific container stats
docker stats container_name --no-stream --format "{{.CPUPerc}}\t{{.MemUsage}}"
```
# Testing Strategy
## Test 1: Resource Limits Configuration
**Purpose:** Verify docker-compose.yml has resource limits defined
**Expected Result:** FAIL initially (no limits configured)
**Pass Criteria After Implementation:** All services have cpus and memory limits
## Test 2: Healthcheck Configuration
**Purpose:** Verify services have healthchecks defined
**Expected Result:** FAIL initially (no healthchecks configured)
**Pass Criteria After Implementation:** All services have valid healthchecks
## Test 3: Resource Enforcement
**Purpose:** Deploy test container and verify limits are enforced
**Expected Result:** FAIL initially (no test container defined)
**Pass Criteria After Implementation:** docker stats shows enforcement
## Test 4: INF-03 Compliance
**Purpose:** Verify mandatory resource limits for all services
**Expected Result:** FAIL initially
**Pass Criteria After Implementation:** 100% compliance
# Implementation Notes
1. **Directory Structure:**
```
labs/lab-03-compute/
├── tests/
│ ├── 01-resource-limits-test.sh
│ ├── 02-healthcheck-test.sh
│ ├── 03-enforcement-test.sh
│ ├── 99-final-verification.sh
│ ├── run-all-tests.sh
│ └── quick-test.sh
├── docker-compose.yml (created in 04-03)
└── README.md
```
2. **Test Execution Order:**
- 01-resource-limits-test.sh: Parse compose file for limits
- 02-healthcheck-test.sh: Parse compose file for healthchecks
- 03-enforcement-test.sh: Deploy test and verify enforcement
- 99-final-verification.sh: End-to-end student verification
3. **Error Handling:**
- Each test should be independent
- Use descriptive error messages
- Provide remediation hints
4. **Color Coding:**
```bash
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m'
```
# Success Criteria
Plan 04-01 is complete when:
1. All 6 test scripts created
2. Each script meets minimum line requirements
3. Tests fail when executed on empty/non-existent lab-03-compute
4. run-all-tests.sh executes all tests in sequence
5. Tests cover: resource limits, healthchecks, enforcement, INF-03
</context>
<tasks>
1. Create labs/lab-03-compute/tests/ directory
2. Create 01-resource-limits-test.sh (80+ lines)
- Parse docker-compose.yml for deploy.resources.limits
- Verify cpus and memory for all services
- Report missing limits
3. Create 02-healthcheck-test.sh (100+ lines)
- Parse docker-compose.yml for healthcheck sections
- Verify test, interval, timeout, retries
- Report missing healthchecks
4. Create 03-enforcement-test.sh (120+ lines)
- Deploy test container with stress image
- Verify CPU limit enforcement with docker stats
- Verify memory OOM on exceed
- Cleanup test container
5. Create 99-final-verification.sh (100+ lines)
- Combine all checks into student-facing verification
- INF-03 compliance report
- Healthcheck status report
- Clear pass/fail summary
6. Create run-all-tests.sh (50+ lines)
- Execute all test scripts in sequence
- Fail-fast on first failure
- Summary report
7. Create quick-test.sh (30+ lines)
- Fast validation (< 30 seconds)
- Essential checks only
</tasks>

View File

@@ -0,0 +1,342 @@
---
phase: 04-lab-03-compute-ec2
plan: 02
type: execute
wave: 1
depends_on:
- "04-01"
files_modified:
- labs/lab-03-compute/tutorial/01-set-resource-limits.md
- labs/lab-03-compute/tutorial/02-implement-healthchecks.md
- labs/lab-03-compute/tutorial/03-dependencies-with-health.md
- labs/lab-03-compute/how-to-guides/check-resource-usage.md
- labs/lab-03-compute/how-to-guides/test-limits-enforcement.md
- labs/lab-03-compute/how-to-guides/custom-healthcheck.md
- labs/lab-03-compute/how-to-guides/instance-type-mapping.md
- labs/lab-03-compute/reference/compose-resources-syntax.md
- labs/lab-03-compute/reference/healthcheck-syntax.md
- labs/lab-03-compute/reference/ec2-instance-mapping.md
- labs/lab-03-compute/explanation/compute-ec2-parallels.md
autonomous: true
requirements:
- DOCT-01
- DOCT-02
- DOCT-03
- DOCT-04
- DOCT-05
- LAB-03
- PARA-01
- PARA-03
- PARA-04
user_setup: []
must_haves:
truths:
- "All 4 Diátxis document types created (Tutorial, How-to, Reference, Explanation)"
- "Tutorials follow 'little often' principle with incremental steps"
- "How-to guides are task-focused and procedure-oriented"
- "Reference documents provide complete technical specifications"
- "Explanation document clearly maps Docker compute to EC2 concepts"
- "Minimum line requirements met for all documents"
artifacts:
- path: "labs/lab-03-compute/tutorial/01-set-resource-limits.md"
provides: "Step-by-step resource limits guide"
min_lines: 250
- path: "labs/lab-03-compute/tutorial/02-implement-healthchecks.md"
provides: "Step-by-step healthcheck guide"
min_lines: 250
- path: "labs/lab-03-compute/tutorial/03-dependencies-with-health.md"
provides: "Step-by-step dependency guide"
min_lines: 250
- path: "labs/lab-03-compute/how-to-guides/check-resource-usage.md"
provides: "Resource monitoring procedure"
min_lines: 60
- path: "labs/lab-03-compute/how-to-guides/test-limits-enforcement.md"
provides: "Limits testing procedure"
min_lines: 60
- path: "labs/lab-03-compute/how-to-guides/custom-healthcheck.md"
provides: "Custom healthcheck procedure"
min_lines: 60
- path: "labs/lab-03-compute/how-to-guides/instance-type-mapping.md"
provides: "Instance type selection guide"
min_lines: 60
- path: "labs/lab-03-compute/reference/compose-resources-syntax.md"
provides: "Complete resources syntax reference"
min_lines: 120
- path: "labs/lab-03-compute/reference/healthcheck-syntax.md"
provides: "Complete healthcheck syntax reference"
min_lines: 100
- path: "labs/lab-03-compute/reference/ec2-instance-mapping.md"
provides: "EC2 instance mapping reference"
min_lines: 80
- path: "labs/lab-03-compute/explanation/compute-ec2-parallels.md"
provides: "Compute-to-EC2 conceptual mapping"
min_lines: 280
key_links:
- from: "tutorial/01-set-resource-limits.md"
to: "reference/compose-resources-syntax.md"
via: "Tutorial links to syntax reference"
pattern: "\\[.*Syntax.*\\].*compose-resources-syntax"
- from: "tutorial/02-implement-healthchecks.md"
to: "reference/healthcheck-syntax.md"
via: "Tutorial links to healthcheck reference"
pattern: "\\[.*Reference.*\\].*healthcheck-syntax"
- from: "explanation/compute-ec2-parallels.md"
to: "reference/ec2-instance-mapping.md"
via: "Explanation links to instance mapping"
pattern: "\\[.*Instance.*\\].*ec2-instance-mapping"
---
<objective>
Create comprehensive Diátxis documentation for Lab 03 (Compute & EC2) covering resource limits, healthchecks, and EC2 instance type parallels.
Purpose: Provide students with 4-quadrant documentation following Diátaxis framework: Tutorials (step-by-step learning), How-to Guides (task-focused procedures), Reference (technical specifications), and Explanation (conceptual mapping to EC2).
Output: 11 markdown documents (3 tutorials, 4 how-to guides, 3 reference, 1 explanation) totaling 1800+ lines.
</objective>
<execution_context>
@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
@/home/luca/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/REQUIREMENTS.md
@.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
@.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
@.planning/phases/03-lab-02-network-vpc/03-02-PLAN.md
@labs/lab-02-network/explanation/docker-network-vpc-parallels.md
# Diátaxis Framework Overview
From PROJECT.md and Phase 2/3 patterns:
## 1. Tutorials - Learning-Oriented
- **Purpose:** Guida passo-passo per studenti nuovi
- **Tone:** Diretto, semplice, incrementale ("little often")
- **Structure:**
- Clear learning objectives
- Prerequisites listed
- Step-by-step instructions
- Verification commands after each step
- Troubleshooting tips
## 2. How-to Guides - Task-Oriented
- **Purpose:** Procedure specifiche e task-focused
- **Tone:** Pratico, diretto al punto
- **Structure:**
- Single specific task
- Prerequisites brief
- Step-by-step procedure
- Expected results
- Common issues
## 3. Reference - Information-Oriented
- **Purpose:** Specifiche tecniche nude e crude
- **Tone:** Formale, completo, conciso
- **Structure:**
- Complete parameter reference
- All options documented
- Examples for each option
- No tutorial content
## 4. Explanation - Understanding-Oriented
- **Purpose:** Parallelismi concettuali locale ↔ cloud
- **Tone:** Educativo, comparativo
- **Structure:**
- Concept introduction
- Side-by-side comparisons
- Key differences highlighted
- When to use what
# Lab 03 Content Guidelines
## EC2 Instance Type Parallels
**Core Concept:** Docker resource limits simulate EC2 instance types
| Docker | EC2 | Use Case |
|--------|-----|----------|
| cpus: '0.5', memory: 512M | t2.nano | Dev/test |
| cpus: '1', memory: 1G | t2.micro | Small services |
| cpus: '1', memory: 2G | t2.small | Web servers |
| cpus: '2', memory: 4G | t2.medium | Application servers |
| cpus: '2', memory: 8G | m5.large | Production apps |
| cpus: '4', memory: 16G | m5.xlarge | High-traffic services |
**Key Teaching Points:**
1. Resource limits = Cost control (pay for what you use)
2. Different instance types for different workloads
3. Burstable (t2/t3) vs. consistent performance (m5/c5)
4. Right-sizing prevents over-provisioning
## Healthcheck Parallels
**Core Concept:** Docker healthchecks simulate ELB health checks
| Docker | AWS |
|--------|-----|
| healthcheck.test | ELB health check path |
| healthcheck.interval | ELB interval (default 30s) |
| healthcheck.timeout | ELB timeout (default 5s) |
| healthcheck.retries | ELB unhealthy threshold |
| healthcheck.start_period | ELB grace period |
| docker ps --filter health=healthy | ELB target health |
**Key Teaching Points:**
1. Healthchecks detect failing services
2. Dependencies wait for healthy status
3. Prevents cascading failures
4. Enables zero-downtime deployments
# Documentation Structure
## Tutorial 1: Set Resource Limits
**Learning Objectives:**
- Understand EC2 instance types
- Learn Docker Compose resource syntax
- Set CPU and memory limits
- Verify with docker stats
**Outline:**
1. What are EC2 Instance Types? (5 min)
2. Docker Resource Limits Syntax (10 min)
3. Practice: Set limits for t2.micro (15 min)
4. Practice: Set limits for t2.small (15 min)
5. Verification with docker stats (5 min)
## Tutorial 2: Implement Healthchecks
**Learning Objectives:**
- Understand healthcheck purpose
- Learn healthcheck syntax
- Add healthcheck to web service
- Monitor health status
**Outline:**
1. What are Healthchecks? (5 min)
2. ELB Health Check Parallel (5 min)
3. Healthcheck Parameters (10 min)
4. Practice: Add HTTP healthcheck (15 min)
5. Practice: Add database healthcheck (10 min)
6. Monitor health status (5 min)
## Tutorial 3: Dependencies with Health
**Learning Objectives:**
- Understand service dependencies
- Use depends_on with conditions
- Implement ordered startup
- Verify dependency chain
**Outline:**
1. Service Dependencies (5 min)
2. depends_on Conditions (10 min)
3. Practice: Web depends on App (15 min)
4. Practice: App depends on DB (15 min)
5. Verify startup order (5 min)
# Implementation Notes
1. **File Locations:**
```
labs/lab-03-compute/
├── tutorial/
│ ├── 01-set-resource-limits.md
│ ├── 02-implement-healthchecks.md
│ └── 03-dependencies-with-health.md
├── how-to-guides/
│ ├── check-resource-usage.md
│ ├── test-limits-enforcement.md
│ ├── custom-healthcheck.md
│ └── instance-type-mapping.md
├── reference/
│ ├── compose-resources-syntax.md
│ ├── healthcheck-syntax.md
│ └── ec2-instance-mapping.md
├── explanation/
│ └── compute-ec2-parallels.md
└── README.md
```
2. **Code Examples:**
- Use realistic examples (nginx, postgres, redis)
- Show complete docker-compose.yml snippets
- Include verification commands
- Add expected output
3. **Parallelism Emphasis:**
- Always show AWS equivalent
- Explain "why" not just "how"
- Highlight key differences
- Link to AWS documentation
# Success Criteria
Plan 04-02 is complete when:
1. All 11 documents created
2. Minimum line requirements met
3. Tutorials follow "little often" principle
4. How-to guides are task-focused
5. Reference documents are complete specifications
6. Explanation clearly maps Docker → EC2
7. Cross-references between documents
8. Italian language (consistent with other labs)
</context>
<tasks>
1. Create tutorial/ directory
2. Create tutorial/01-set-resource-limits.md (250+ lines)
- EC2 instance types introduction
- Docker resource limits syntax
- Practice exercises
- Verification steps
3. Create tutorial/02-implement-healthchecks.md (250+ lines)
- Healthcheck concept and ELB parallel
- Healthcheck parameters
- Practice: HTTP healthcheck
- Practice: Database healthcheck
4. Create tutorial/03-dependencies-with-health.md (250+ lines)
- Service dependency concepts
- depends_on conditions
- Practice: Multi-tier dependencies
- Startup order verification
5. Create how-to-guides/ directory
6. Create how-to-guides/check-resource-usage.md (60+ lines)
- docker stats usage
- Real-time monitoring
- Interpreting output
7. Create how-to-guides/test-limits-enforcement.md (60+ lines)
- CPU limit testing
- Memory OOM testing
- Verification procedures
8. Create how-to-guides/custom-healthcheck.md (60+ lines)
- Writing custom healthcheck commands
- Best practices
- Debugging failures
9. Create how-to-guides/instance-type-mapping.md (60+ lines)
- Docker limits → EC2 mapping
- Selecting appropriate types
- Cost considerations
10. Create reference/ directory
11. Create reference/compose-resources-syntax.md (120+ lines)
- Complete deploy.resources reference
- CPU and memory syntax
- Reservations vs limits
- All options with examples
12. Create reference/healthcheck-syntax.md (100+ lines)
- All healthcheck parameters
- Test command formats
- Status values
- Examples for each service type
13. Create reference/ec2-instance-mapping.md (80+ lines)
- Complete mapping table
- Instance type descriptions
- Use case recommendations
14. Create explanation/ directory
15. Create explanation/compute-ec2-parallels.md (280+ lines)
- Container = EC2 Instance
- Resource limits = Instance types
- Healthcheck = ELB/Status checks
- Docker stats = CloudWatch
- Key differences (credits, pricing, etc.)
- Command equivalents table
</tasks>

View File

@@ -0,0 +1,335 @@
---
phase: 04-lab-03-compute-ec2
plan: 03
type: execute
wave: 2
depends_on:
- "04-01"
- "04-02"
files_modified:
- labs/lab-03-compute/docker-compose.yml
- labs/lab-03-compute/Dockerfile
- labs/lab-03-compute/tests/04-verify-infrastructure.sh
autonomous: true
requirements:
- LAB-03
- INF-01
- INF-03
- PARA-01
- PARA-02
- TEST-01
- TEST-05
user_setup: []
must_haves:
truths:
- "docker-compose.yml exists and is valid (docker compose config passes)"
- "All services have deploy.resources.limits.cpus set (INF-03)"
- "All services have deploy.resources.limits.memory set (INF-03)"
- "Services have appropriate healthchecks defined"
- "depends_on uses condition: service_healthy where appropriate"
- "Infrastructure verification passes all checks"
- "Cloud nomenclature follows EC2 instance patterns (PARA-02)"
artifacts:
- path: "labs/lab-03-compute/docker-compose.yml"
provides: "Compute infrastructure with limits and healthchecks"
min_lines: 100
- path: "labs/lab-03-compute/Dockerfile"
provides: "Test container image with stress tools"
min_lines: 25
- path: "labs/lab-03-compute/tests/04-verify-infrastructure.sh"
provides: "Infrastructure verification script"
min_lines: 100
key_links:
- from: "docker-compose.yml"
to: "tests/01-resource-limits-test.sh"
via: "Tests validate deploy.resources.limits"
pattern: "deploy:.*resources:.*limits"
- from: "docker-compose.yml"
to: "tests/02-healthcheck-test.sh"
via: "Tests validate healthcheck sections"
pattern: "healthcheck:"
- from: "docker-compose.yml"
to: "reference/compose-resources-syntax.md"
via: "Reference documents all resource options"
pattern: "deploy:.*resources"
- from: "docker-compose.yml"
to: "explanation/compute-ec2-parallels.md"
via: "Instance types mapped to EC2"
pattern: "# EC2|t2\\.micro|m5\\.large"
---
<objective>
Implement compute infrastructure for Lab 03 (Compute & EC2) with Docker Compose resource limits and healthchecks. Create docker-compose.yml with services that have mandatory CPU/memory limits (INF-03 compliance) and healthchecks for readiness verification.
Purpose: GREEN phase implementation - make tests from Plan 04-01 pass by implementing compute infrastructure with proper resource limits and healthchecks.
Output: docker-compose.yml with 4+ services, Dockerfile for test container, and infrastructure verification script.
</objective>
<execution_context>
@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
@/home/luca/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/REQUIREMENTS.md
@.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
@.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
@.planning/phases/03-lab-02-network-vpc/03-03-PLAN.md
@labs/lab-02-network/docker-compose.yml
# Infrastructure Requirements
## INF-03: Mandatory Resource Limits
**CRITICAL:** Every service MUST have:
```yaml
deploy:
resources:
limits:
cpus: 'X' # REQUIRED
memory: 'XG' # REQUIRED
```
**NON-COMPLIANT:**
```yaml
# Missing limits - INF-03 VIOLATION
services:
app:
image: nginx
# No deploy section
```
## Service Configuration
### Tier 1: Web Server (t2.micro parallel)
```yaml
web:
image: nginx:alpine
container_name: lab03-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
```
### Tier 2: Application Server (t2.small parallel)
```yaml
app:
image: nginx:alpine
container_name: lab03-app
deploy:
resources:
limits:
cpus: '1'
memory: 2G
depends_on:
web:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
interval: 10s
timeout: 5s
retries: 3
```
### Tier 3: Worker (t2.medium parallel)
```yaml
worker:
image: alpine:3.19
container_name: lab03-worker
command: ["sh", "-c", "sleep 3600"]
deploy:
resources:
limits:
cpus: '2'
memory: 4G
healthcheck:
test: ["CMD-SHELL", "exit 0"]
interval: 30s
timeout: 5s
retries: 3
```
### Tier 4: Database (t2.medium parallel)
```yaml
db:
image: postgres:16-alpine
container_name: lab03-db
environment:
POSTGRES_DB: lab03_db
POSTGRES_USER: lab03_user
POSTGRES_PASSWORD: lab03_password
deploy:
resources:
limits:
cpus: '2'
memory: 4G
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U lab03_user -d lab03_db"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
```
### Test Container (for enforcement testing)
```yaml
stress-test:
image: polinux/stress
container_name: lab03-stress
command: ["--cpu", "1", "--vm", "1", "--vm-bytes", "256M", "--timeout", "30s"]
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
healthcheck:
test: ["CMD-SHELL", "exit 0"]
interval: 5s
timeout: 3s
retries: 3
```
# Dockerfile for Test Container
```dockerfile
# Dockerfile for Lab 03 - Compute & EC2
# Test container with stress testing tools
FROM alpine:3.19
# Create non-root user (INF-01 compliance)
RUN addgroup -g 1000 appgroup && \
adduser -D -u 1000 -G appgroup appuser
# Install stress testing tools
RUN apk add --no-cache \
stress \
curl \
&& rm -rf /var/cache/apk/*
# Switch to non-root user
USER appuser
WORKDIR /home/appuser
# Default command - ready for stress testing
CMD ["sh", "-c", "sleep 3600"]
```
# Healthcheck Best Practices
## HTTP Service Healthcheck
```yaml
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
interval: 10s # Check every 10 seconds
timeout: 5s # Fail after 5 seconds
retries: 3 # Unhealthy after 3 failures
start_period: 5s # Grace period on startup
```
## Database Healthcheck
```yaml
test: ["CMD-SHELL", "pg_isready -U postgres || exit 1"]
interval: 10s
timeout: 5s
retries: 5 # More retries for DB (slower startup)
start_period: 10s # Longer grace period
```
## Simple Healthcheck
```yaml
test: ["CMD-SHELL", "exit 0"]
interval: 30s # Less frequent for simple checks
timeout: 3s
retries: 3
```
# Infrastructure Verification Script
Based on labs/lab-02-network/tests/04-verify-infrastructure.sh pattern:
## Verification Steps
1. **File Existence:** docker-compose.yml exists
2. **Syntax Validation:** docker compose config passes
3. **Resource Limits:** All services have cpus and memory limits
4. **Healthchecks:** All services have healthcheck sections
5. **INF-03 Compliance:** 100% of services have limits
6. **Deploy Services:** docker compose up -d succeeds
7. **Health Status:** Services become healthy
8. **Resource Enforcement:** docker stats shows limits
9. **Dependency Order:** Services start in correct order
10. **Final Report:** Pass/fail summary
# Cloud Nomenclature (PARA-02)
Service names should reflect EC2 instance parallels:
- `web` → Web tier (t2.micro)
- `app` → Application tier (t2.small)
- `worker` → Background processing (t2.medium)
- `db` → Database tier (t2.medium)
# Implementation Notes
1. **Version:** Use `version: "3.8"` for compatibility
2. **Networks:** Can reuse networks from Lab 02 or create new
3. **Volumes:** Use named volumes for database persistence
4. **Security:** Follow INF-01 (no root), INF-02 (no 0.0.0.0 bindings)
5. **Parallelism:** Comments should show EC2 equivalent
# Success Criteria
Plan 04-03 is complete when:
1. docker-compose.yml created with 4+ services
2. All services have resource limits (INF-03)
3. All services have healthchecks
4. docker compose config validates
5. Services deploy and become healthy
6. Infrastructure verification passes all checks
7. Tests from 04-01 now pass (GREEN phase)
</context>
<tasks>
1. Create labs/lab-03-compute/ directory structure
2. Create docker-compose.yml (100+ lines)
- Service: web (nginx, t2.micro: 1 CPU, 1G RAM)
- Service: app (nginx, t2.small: 1 CPU, 2G RAM)
- Service: worker (alpine, t2.medium: 2 CPU, 4G RAM)
- Service: db (postgres, t2.medium: 2 CPU, 4G RAM)
- Service: stress-test (enforcement testing)
- All services: deploy.resources.limits
- All services: healthcheck sections
- Proper depends_on with conditions
- Named volumes for database
3. Create Dockerfile (25+ lines)
- Alpine 3.19 base
- Non-root user (INF-01)
- Install stress tools
- Minimal and secure
4. Create tests/04-verify-infrastructure.sh (100+ lines)
- Verify docker-compose.yml exists
- Validate syntax
- Check INF-03 compliance
- Verify healthchecks
- Deploy and test services
- Check resource enforcement
- Final summary report
5. Test infrastructure:
- docker compose config
- docker compose up -d
- docker stats verification
- health status check
- docker compose down
</tasks>

View File

@@ -0,0 +1,209 @@
# Phase 4 Research - Lab 03: Compute & EC2
## Domain Research: Docker Resource Limits & Healthchecks
### 1. Docker Compose Resource Limits
**CPU Limits:**
```yaml
services:
app:
deploy:
resources:
limits:
cpus: '0.5' # 50% of 1 CPU core
# OR
cpus: '2' # 2 full CPU cores
```
**Memory Limits:**
```yaml
services:
app:
deploy:
resources:
limits:
memory: 512M # 512 MB
# OR
memory: 2G # 2 GB
```
**Non-Swap Memory:**
```yaml
services:
app:
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
```
### 2. EC2 Instance Types Parallel
| Docker Limits | EC2 Equivalent | Instance Type |
|---------------|----------------|---------------|
| cpus: '0.5', memory: 512M | t2.nano (0.5 vCPU, 512MB) | Burstable |
| cpus: '1', memory: 1G | t2.micro (1 vCPU, 1GB) | Burstable |
| cpus: '1', memory: 2G | t2.small (1 vCPU, 2GB) | Burstable |
| cpus: '2', memory: 4G | t2.medium (2 vCPU, 4GB) | Burstable |
| cpus: '2', memory: 8G | m5.large (2 vCPU, 8GB) | General Purpose |
| cpus: '4', memory: 16G | m5.xlarge (4 vCPU, 16GB) | General Purpose |
**Key Parallelism:**
- Docker CPU fractions = AWS vCPUs
- Docker memory limits = AWS instance memory
- No swap enforcement = AWS EBS-optimized instances
- Resource reservations = AWS instance type guarantees
### 3. Healthcheck Implementation
**Docker Compose Healthcheck:**
```yaml
services:
web:
image: nginx:alpine
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"]
interval: 10s # Check every 10 seconds
timeout: 5s # Timeout after 5 seconds
retries: 3 # Mark unhealthy after 3 failures
start_period: 10s # Grace period on startup
```
**Healthcheck with curl:**
```yaml
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 5s
```
**Database Healthcheck:**
```yaml
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres || exit 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
```
**Application Healthcheck:**
```yaml
app:
image: myapp:latest
healthcheck:
test: ["CMD-SHELL", "node healthcheck.js || exit 1"]
interval: 15s
timeout: 3s
retries: 3
start_period: 30s
```
### 4. Service Dependencies with Health
**Wait for healthy service:**
```yaml
services:
app:
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
```
**Lifecycle:**
1. `service_started`: Container started (default)
2. `service_healthy`: Healthcheck passing (requires healthcheck section)
### 5. Resource Monitoring
**docker stats:**
```bash
docker stats --no-stream # Single snapshot
docker stats lab03-app --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"
```
**Inspect limits:**
```bash
docker inspect lab03-app --format '{{.HostConfig.Memory}}' # Memory limit in bytes
docker inspect lab03-app --format '{{.HostConfig.NanoCpus}}' # CPU quota (1e9 = 1 CPU)
```
## Testing Strategy
### Test Scenarios
1. **Resource Limit Enforcement:**
- Deploy container with CPU limit (e.g., 0.5 CPU)
- Run CPU-intensive task
- Verify with `docker stats` that CPU usage doesn't exceed limit
2. **Memory Limit Enforcement:**
- Deploy container with memory limit (e.g., 512M)
- Run memory allocation task
- Verify container is OOM killed when exceeding limit
3. **Healthcheck Validation:**
- Deploy service with healthcheck
- Verify status transitions: starting → healthy
- Verify `depends_on: condition: service_healthy` waits
4. **Resource Verification:**
- Parse docker-compose.yml for `deploy.resources.limits`
- Verify all services have mandatory limits
- Report missing limits
## Security Requirements
**INF-03: Mandatory Resource Limits**
- Every container MUST have `cpus` limit specified
- Every container MUST have `memory` limit specified
- No container can run with unlimited resources (security risk)
**Safety First:**
- Resource limits prevent DoS from runaway processes
- Memory limits prevent host OOM
- CPU limits ensure fair resource sharing
## Cloud Parallels
### Docker → AWS EC2
| Docker | AWS EC2 |
|--------|---------|
| `cpus: '0.5'` | 0.5 vCPU (t2.nano) |
| `memory: 512M` | 512 MB RAM |
| `healthcheck` | EC2 Status Checks + ELB Health |
| `docker stats` | CloudWatch Metrics |
| `OOM kill` | Instance termination (out of credit) |
| `depends_on: healthy` | Auto Scaling Group health checks |
### Instance Type Selection
**Burstable Instances (t2/t3):**
- Credit-based CPU
- Good for dev/test
- Docker: Small limits with occasional bursts
**General Purpose (m5):**
- Balanced compute/memory
- Docker: Medium limits (2-4 vCPU, 8-16 GB)
**Compute Optimized (c5):**
- High CPU ratio
- Docker: High CPU limits (4+ vCPU, lower memory)
## Sources
- [Docker Compose Resources](https://docs.docker.com/compose/compose-file/compose-file-v3/#resources)
- [Docker Healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck)
- [AWS EC2 Instance Types](https://aws.amazon.com/ec2/instance-types/)
- [Docker Stats](https://docs.docker.com/engine/reference/commandline/stats/)

View File

@@ -0,0 +1,247 @@
# Phase 4 Validation - Lab 03: Compute & EC2
## Validation Strategy
### Goal-Backward Verification
We verify that Phase 4 achieves its goals by checking the success criteria from the Phase definition.
## Success Criteria (from ROADMAP)
1. **Studente può deploy container con limiti CPU/memoria obbligatori** (simulazione instance types)
2. **Studente può implementare healthchecks per verificare che servizi siano "healthy"**
3. **Tutti i container hanno `cpus` e `mem_limit` configurati** (enforcement risorse cloud)
4. **Studente comprende il parallelismo tra container con limiti e EC2 instances**
5. **Lab include test che verificano resource limits con `docker stats` e healthcheck readiness**
## Validation Checklist
### 04-01: Test Infrastructure (RED Phase)
**Acceptance Criteria:**
- [ ] Test script `01-resource-limits-test.sh` validates:
- Parse docker-compose.yml for `deploy.resources.limits.cpus`
- Parse docker-compose.yml for `deploy.resources.limits.memory`
- Report services without mandatory limits
- Test should FAIL initially (no limits configured)
- [ ] Test script `02-healthcheck-test.sh` validates:
- Services have `healthcheck` section defined
- Healthcheck command is valid
- `interval`, `timeout`, `retries` configured
- Test should FAIL initially (no healthchecks configured)
- [ ] Test script `03-resource-enforcement-test.sh` validates:
- Deploy test container with limits
- Run CPU-intensive task
- Verify `docker stats` shows enforcement
- Run memory allocation task
- Verify OOM kill on exceed
- [ ] Final verification `99-final-verification.sh` validates:
- All services have resource limits
- All services have healthchecks
- Healthchecks pass
- Resource limits enforced
- INF-03 compliance verified
**Verification Command:**
```bash
cd labs/lab-03-compute
bash tests/run-all-tests.sh
```
**Expected Result:** All tests FAIL initially (RED phase), then PASS after implementation (GREEN phase)
---
### 04-02: Diátxis Documentation
**Acceptance Criteria:**
**Tutorials (3):**
- [ ] `tutorial/01-set-resource-limits.md` (min 250 lines)
- Explain EC2 instance types concept
- Show Docker CPU/memory limits syntax
- Practice: Set limits for different instance types
- Verify with `docker stats`
- [ ] `tutorial/02-implement-healthchecks.md` (min 250 lines)
- Explain healthcheck purpose (ELB parallel)
- Show healthcheck syntax and parameters
- Practice: Add healthcheck to web service
- Verify with `docker ps` and health status
- [ ] `tutorial/03-dependencies-with-health.md` (min 250 lines)
- Explain service dependencies
- Show `depends_on: condition: service_healthy`
- Practice: Multi-tier with health-based startup
- Verify startup order
**How-to Guides (4):**
- [ ] `how-to-guides/check-resource-usage.md` (min 60 lines)
- How to use `docker stats`
- How to inspect limits
- How to monitor in real-time
- [ ] `how-to-guides/test-limits-enforcement.md` (min 60 lines)
- How to trigger CPU limit
- How to trigger memory OOM
- How to verify enforcement
- [ ] `how-to-guides/custom-healthcheck.md` (min 60 lines)
- How to write custom healthcheck
- Healthcheck best practices
- Debugging healthcheck failures
- [ ] `how-to-guides/instance-type-mapping.md` (min 60 lines)
- Docker limits → EC2 instance mapping
- Selecting appropriate instance type
- Cost optimization parallels
**Reference (3):**
- [ ] `reference/compose-resources-syntax.md` (min 120 lines)
- Complete `deploy.resources` reference
- CPU and memory limit syntax
- Reservations vs limits
- [ ] `reference/healthcheck-syntax.md` (min 100 lines)
- Healthcheck parameters
- Test command formats
- Status values and transitions
- [ ] `reference/ec2-instance-mapping.md` (min 80 lines)
- Docker limits → EC2 instance table
- Parallelism documentation
- Command equivalents
**Explanation (1):**
- [ ] `explanation/compute-ec2-parallels.md` (min 280 lines)
- Container = EC2 Instance
- Resource limits = Instance types
- Healthcheck = ELB/Status checks
- Docker stats = CloudWatch
- Differences (credits, pricing, etc.)
**Verification:**
```bash
find labs/lab-03-compute -name "*.md" | wc -l # Expect: 11
wc -l labs/lab-03-compute/tutorial/*.md # Expect: 750+
wc -l labs/lab-03-compute/how-to-guides/*.md # Expect: 240+
wc -l labs/lab-03-compute/reference/*.md # Expect: 300+
wc -l labs/lab-03-compute/explanation/*.md # Expect: 280+
```
---
### 04-03: Infrastructure Implementation (GREEN Phase)
**Acceptance Criteria:**
- [ ] `docker-compose.yml` exists and is valid
- [ ] All services have `deploy.resources.limits.cpus` set
- [ ] All services have `deploy.resources.limits.memory` set
- [ ] Services have appropriate healthchecks defined
- [ ] Healthcheck parameters are reasonable (interval, timeout, retries)
- [ ] `depends_on` uses `condition: service_healthy` where appropriate
- [ ] INF-03 compliance: NO service without resource limits
- [ ] Cloud nomenclature: Service names follow EC2 instance patterns
- [ ] Infrastructure verification passes all checks
**INF-03 Compliance Check:**
```bash
grep -c "deploy:" labs/lab-03-compute/docker-compose.yml # Should equal service count
grep -c "cpus:" labs/lab-03-compute/docker-compose.yml # Should equal service count
grep -c "memory:" labs/lab-03-compute/docker-compose.yml # Should equal service count
```
**Services Configuration:**
| Service | Instance Type Parallel | CPUs | Memory | Healthcheck |
|---------|----------------------|------|--------|-------------|
| web | t2.micro | 1 | 1G | HTTP endpoint |
| app | t2.small | 1 | 2G | HTTP endpoint |
| worker | t2.medium | 2 | 4G | Custom command |
| db | t2.medium | 2 | 4G | pg_isready |
**Verification Command:**
```bash
cd labs/lab-03-compute
bash tests/99-final-verification.sh
```
**Expected Result:** All checks PASS
---
## Automated Validation Scripts
### Script 1: INF-03 Compliance
```bash
#!/bin/bash
# Verify all services have resource limits
SERVICES=$(docker compose config --services)
for service in $SERVICES; do
has_cpu=$(docker compose config | grep -A 10 "$service:" | grep -c "cpus:")
has_mem=$(docker compose config | grep -A 10 "$service:" | grep -c "memory:")
if [[ $has_cpu -eq 0 || $has_mem -eq 0 ]]; then
echo "FAIL: $service missing resource limits"
exit 1
fi
done
echo "PASS: All services have resource limits"
```
### Script 2: Healthcheck Verification
```bash
#!/bin/bash
# Verify all services have healthchecks
SERVICES=$(docker compose config --services)
for service in $SERVICES; do
has_hc=$(docker compose config | grep -A 20 "$service:" | grep -c "healthcheck:")
if [[ $has_hc -eq 0 ]]; then
echo "FAIL: $service missing healthcheck"
exit 1
fi
done
echo "PASS: All services have healthchecks"
```
### Script 3: Resource Enforcement Test
```bash
#!/bin/bash
# Deploy container with limits and verify enforcement
docker compose up -d test-stress
sleep 2
# Get limits
CPU_LIMIT=$(docker inspect test-stress --format='{{.HostConfig.NanoCpus}}')
MEM_LIMIT=$(docker inspect test-stress --format='{{.HostConfig.Memory}}')
# Start stress test
docker exec test-stress stress --cpu 1 &
sleep 5
# Verify CPU doesn't exceed 50%
CPU_USAGE=$(docker stats test-stress --no-stream --format "{{.CPUPerc}}")
if [[ $CPU_USAGE > 50 ]]; then
echo "FAIL: CPU limit not enforced"
exit 1
fi
echo "PASS: Resource limits enforced"
```
## Final Validation Checklist
- [ ] All 6 test scripts created (RED phase)
- [ ] All 11 documentation files created (Diátxis)
- [ ] docker-compose.yml implemented (GREEN phase)
- [ ] All tests pass after implementation
- [ ] INF-03 compliance verified
- [ ] Student can follow tutorial end-to-end
- [ ] Parallelism to EC2 documented
- [ ] Resource limits enforced (verified with docker stats)
- [ ] Healthchecks functional
- [ ] Service dependencies work correctly
## Sign-off
Phase 4 is complete when:
1. Student can deploy container with resource limits
2. Student can add healthchecks to services
3. All tests pass (99-final-verification.sh)
4. Documentation follows Diátxis framework
5. INF-03 compliance is mandatory and enforced

View File

@@ -0,0 +1,25 @@
# Dockerfile per Lab 03 - Compute & EC2
# Container di test con strumenti di stress per verifica enforcement
FROM alpine:3.19
# Creare utente non-root per sicurezza (INF-01 compliance)
RUN addgroup -g 1000 appgroup && \
adduser -D -u 1000 -G appgroup appuser
# Installare strumenti di stress testing e monitoraggio
RUN apk add --no-cache \
stress \
curl \
wget \
procps \
&& rm -rf /var/cache/apk/*
# Passare all'utente non-root
USER appuser
# Set working directory
WORKDIR /home/appuser
# Comando di default - container in attesa per testing
CMD ["sh", "-c", "sleep 3600"]

View File

@@ -0,0 +1,140 @@
# Lab 03: Compute & EC2 - Docker Compose Configuration
# Simula diverse EC2 instance types usando resource limits Docker
version: "3.8"
services:
# Web Server - simula t2.micro (1 vCPU, 1 GB RAM)
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"
depends_on:
app:
condition: service_healthy
restart: unless-stopped
# Application Server - simula t2.small (1 vCPU, 2 GB RAM)
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"
depends_on:
db:
condition: service_healthy
restart: unless-stopped
# Worker - simula t2.medium (2 vCPU, 4 GB RAM)
worker:
image: alpine:3.19
container_name: lab03-worker
hostname: worker
command: ["sh", "-c", "sleep 3600"]
deploy:
resources:
limits:
cpus: '2'
memory: 4G
healthcheck:
test: ["CMD-SHELL", "exit 0"]
interval: 30s
timeout: 5s
retries: 3
restart: unless-stopped
# Database - simula t2.medium (2 vCPU, 4 GB RAM)
db:
image: postgres:16-alpine
container_name: lab03-db
hostname: db
environment:
POSTGRES_DB: lab03_db
POSTGRES_USER: lab03_user
POSTGRES_PASSWORD: lab03_password
POSTGRES_INITDB_ARGS: "-E UTF8"
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
# Stress Test Container - per verifica enforcement
stress-test:
image: alpine:3.19
container_name: lab03-stress
hostname: stress-test
command: ["sh", "-c", "sleep 3600"]
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
healthcheck:
test: ["CMD-SHELL", "exit 0"]
interval: 5s
timeout: 3s
retries: 3
restart: unless-stopped
# Persistent Volumes
volumes:
db-data:
driver: local

View File

@@ -0,0 +1,484 @@
# Explanation: Parallelismi tra Docker Compute e EC2
In questo documento esploreremo come le risorse dei container Docker simulano le istanze EC2 di AWS. Comprendere questi parallelismi ti permetterà di applicare le conoscenze acquisite localmente agli ambienti cloud reali.
---
## Cos'è un'EC2 Instance?
**EC2 (Elastic Compute Cloud)** Instance è una macchina virtuale nel cloud che:
- **Fornisce risorse computazionali**: CPU, memoria, storage, rete
- **Definisce il tipo e la dimensione**: Instance types con diverse combinazioni
- **Scansiona i costi**: Paghi per le risorse che usi (o riservate)
Le istanze EC2 sono il cuore della compute infrastructure in AWS, usate per:
- Web server e application server
- Database (RDS, Aurora)
- Batch processing
- Container hosting (ECS, EKS)
---
## Il Parallelismo Fondamentale
### Container Docker = EC2 Instance
| Locale | Cloud AWS |
|--------|-----------|
| `docker run` | `aws ec2 run-instances` |
| Container con limiti | EC2 Instance type |
| `cpus: '1'` | 1 vCPU |
| `memory: 2G` | 2 GB RAM |
| `docker stats` | CloudWatch Metrics |
| Healthcheck | ELB Health Check |
### Resource Limits = Instance Type
**Locale (Docker Compose):**
```yaml
deploy:
resources:
limits:
cpus: '1'
memory: 2G
```
**Cloud (AWS CLI):**
```bash
aws ec2 run-instances \
--image-id ami-12345 \
--instance-type t2.small \
# t2.small = 1 vCPU, 2 GB RAM
```
**Stesso risultato:** 1 vCPU, 2 GB RAM di risorse garantite.
---
## EC2 Instance Types - Approfondimento
### Famiglie di Instance Types
AWS offre diverse famiglie di istanze per diversi workload:
| Famiglia | Prefix | Caratteristiche | Docker Parallel |
|----------|--------|-----------------|-----------------|
| **Burstable** | t2, t3 | Credit-based CPU, low cost | Piccoli limiti con burst |
| **General Purpose** | m5, m6 | Equilibrato CPU/memoria | Limiti bilanciati |
| **Compute Optimized** | c5, c6 | Alto ratio CPU | CPU alto, memoria bassa |
| **Memory Optimized** | r5, r6 | Alto ratio memoria | CPU basso, memoria alta |
| **Storage Optimized** | i3, i4 | NVMe SSD locale | Volumes veloci |
### Instance Types Comuni Analizzati
#### T2 Nano - Microservices
**Spec:** 0.5 vCPU, 512 MB RAM
**Costo:** ~$0.006/ora
**Use Case:** Microservizi minimi, background tasks
**Docker Parallel:**
```yaml
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
```
**Quando usarlo:**
- Servizi che usano poca CPU
- Task asincroni leggeri
- Sviluppo e test economici
#### T2 Micro - Dev/Test
**Spec:** 1 vCPU, 1 GB RAM
**Costo:** ~$0.012/ora
**Use Case:** Development, test, small websites
**Docker Parallel:**
```yaml
deploy:
resources:
limits:
cpus: '1'
memory: 1G
```
**Quando usarlo:**
- Ambienti di sviluppo
- Test automation
- Microservices a basso traffico
#### T2 Small - Web Servers
**Spec:** 1 vCPU, 2 GB RAM
**Costo:** ~$0.024/ora
**Use Case:** Web server, API endpoints
**Docker Parallel:**
```yaml
deploy:
resources:
limits:
cpus: '1'
memory: 2G
```
**Quando usarlo:**
- Web server (Nginx, Apache)
- API REST
- Container reverse proxy
#### T2 Medium - Application Server
**Spec:** 2 vCPU, 4 GB RAM
**Costo:** ~$0.048/ora
**Use Case:** Application server, cache
**Docker Parallel:**
```yaml
deploy:
resources:
limits:
cpus: '2'
memory: 4G
```
**Quando usarlo:**
- Application server (Node.js, Python)
- Cache server (Redis)
- Database di sviluppo
#### M5 Large - Production
**Spec:** 2 vCPU, 8 GB RAM
**Costo:** ~$0.096/ora
**Use Case:** Production applications
**Docker Parallel:**
```yaml
deploy:
resources:
limits:
cpus: '2'
memory: 8G
```
**Quando usarlo:**
- Production web applications
- Services con cache in-memory
- Databases di produzione (non critici)
---
## Healthcheck Parallelism
### Docker Healthcheck = ELB Health Check
| Locale | Cloud AWS |
|--------|-----------|
| healthcheck.test | Health check path/protocol |
| healthcheck.interval | Health check interval (30s) |
| healthcheck.timeout | Health check timeout (5s) |
| healthcheck.retries | Unhealthy threshold (2) |
| healthcheck.start_period | Grace period (none in ELB) |
### Esempio Pratico
**Locale (Docker Compose):**
```yaml
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
interval: 30s
timeout: 5s
retries: 2
```
**Cloud (Target Group ELB):**
```json
{
"TargetGroup": {
"HealthCheckProtocol": "HTTP",
"HealthCheckPath": "/health",
"HealthCheckIntervalSeconds": 30,
"HealthCheckTimeoutSeconds": 5,
"UnhealthyThresholdCount": 2,
"HealthyThresholdCount": 2
}
}
```
**Stesso comportamento:**
- Check HTTP ogni 30 secondi
- Timeout dopo 5 secondi
- Unhealthy dopo 2 fallimenti consecutivi
---
## Resource Monitoring Parallelism
### Docker Stats = CloudWatch Metrics
| Locale | Cloud AWS |
|--------|-----------|
| docker stats | CloudWatch Metrics |
| CPU % | CPUUtilization |
| Mem usage | MemoryUtilization |
| Network I/O | NetworkIn/Out |
| Block I/O | DiskReadBytes/WriteBytes |
### Esempio di Monitoring
**Locale:**
```bash
docker stats lab03-web --no-stream
# lab03-web: 0.01% CPU, 2.5MiB / 1GiB memory (0.24%)
```
**Cloud (CloudWatch):**
```bash
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 \
--metric-name CPUUtilization \
--dimensions Name=InstanceId,Value=i-12345 \
--start-time 2024-03-25T00:00:00Z \
--end-time 2024-03-25T00:05:00Z \
--period 60 \
--statistics Average
```
---
## Dipendenze Parallelism
### Docker depends_on = ECS DependsOn
| Locale | Cloud AWS |
|--------|-----------|
| depends_on: service_healthy | dependsOn: condition=HEALTHY |
| depends_on: service_started | dependsOn: condition=START |
### Esempio Multi-Tier
**Locale (Docker Compose):**
```yaml
services:
web:
depends_on:
app:
condition: service_healthy
app:
depends_on:
db:
condition: service_healthy
db:
# No dependencies
```
**Cloud (ECS Task Definition):**
```json
{
"containerDefinitions": [
{
"name": "web",
"dependsOn": [
{"containerName": "app", "condition": "HEALTHY"}
]
},
{
"name": "app",
"dependsOn": [
{"containerName": "db", "condition": "HEALTHY"}
]
},
{
"name": "db",
"dependsOn": []
}
]
}
```
---
## Costi e Billing Parallelism
### Docker = Local Resources (Free)
In locale, le risorse Docker sono "gratuite":
- Paghi per l'hardware host (una tantum)
- Nessun costo orario per container
- Limiti servono per isolamento, non billing
### EC2 = Pay-Per-Use
In cloud, paghi per:
- Ore di compute (o secondi con Fargate)
- Tipi di istanza (più grandi = più costosi)
- Riserve e spot instances possono ridurre costi
**Mapping costi:**
| Docker | EC2 | Costo/ora |
|--------|-----|-----------|
| 0.5 CPU, 512M | t2.nano | ~$0.006 |
| 1 CPU, 1G | t2.micro | ~$0.012 |
| 1 CPU, 2G | t2.small | ~$0.024 |
| 2 CPU, 4G | t2.medium | ~$0.048 |
| 2 CPU, 8G | m5.large | ~$0.096 |
---
## Scaling Parallelism
### Docker Compose Scale = EC2 Auto Scaling
**Locale (Horizontal Scaling):**
```yaml
web:
deploy:
replicas: 4
resources:
limits:
cpus: '1'
memory: 2G
```
Risultato: 4 container, ognuno con 1 CPU e 2 GB
**Cloud (Auto Scaling Group):**
```bash
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name my-asg \
--launch-template LaunchTemplateId=lt-12345 \
--min-size 2 \
--max-size 4 \
--desired-capacity 4
```
Risultato: 4 EC2 instances (t2.small)
**Parallelismo:**
- Docker: `replicas` = numero di container
- EC2: `desired-capacity` = numero di istanze
- Entrambi: distribuiscono carico su più unità
---
## Differenze Chiave
### 1. Crediti CPU (Burstable Instances)
**T2/T3 instances** usano CPU credits:
- Ogni istanza accumula crediti quando idle
- Spende crediti quando sotto carico
- Senza crediti = performance degradata
**Docker NON ha credits:**
- CPU limit è hard cap
- Mai degrada (sempre disponibile fino al limite)
- No burst oltre il limite
### 2. Pricing Models
**AWS offre:**
- **On-Demand:** Paghi per ora usata
- **Reserved:** Sconto per 1-3 anni di impegno
- **Spot:** Asta per capacità unused (fino a 90% sconto)
- **Dedicated:** Host fisico dedicato
**Docker:**
- Nessun pricing model
- Tutto flat sul costo host
### 3. Availability Zones
**AWS:**
- Istanze distribuite su multiple AZ
- Ogni AZ = data center separato
- High availability geografica
**Docker:**
- Single host (senza Swarm/Kubernetes)
- Nessuna AZ separation
- Host failure = tutti i container down
---
## Best Practices Transfer
### Da Locale a Cloud
| Best Practice Locale | Equivalente Cloud |
|---------------------|-------------------|
| Set resource limits | Choose right instance type |
| Use healthchecks | Configure ELB health checks |
| depends_on healthy | Use ECS dependsOn |
| Monitor with docker stats | Use CloudWatch alarms |
| Scale with replicas | Use Auto Scaling Groups |
### Evoluzione Architetturale
**Locale (Docker Compose):**
```yaml
services:
web:
deploy:
resources:
limits:
cpus: '1'
memory: 2G
```
**Cloud (ECS/Fargate):**
```json
{
"containerDefinitions": [{
"name": "web",
"cpu": 1024,
"memory": 2048,
"memoryReservation": 2048
}]
}
```
**Nota:** In ECS Fargate, CPU e memoria sono configurati come:
- `cpu`: 256, 512, 1024, 2048, 4096 (unità: 1 vCPU = 1024)
- `memory`: 512, 1024, 2048, ... (in MB)
---
## Command Equivalents Table
| Operazione | Locale (Docker) | Cloud (AWS) |
|------------|------------------|-------------|
| **Deploy compute** | docker compose up -d | aws ec2 run-instances |
| **Check resources** | docker inspect --format '{{.HostConfig}}' | aws ec2 describe-instance-types |
| **Monitor usage** | docker stats | aws cloudwatch get-metric-statistics |
| **Set limits** | deploy.resources.limits | --instance-type parameter |
| **Check health** | docker inspect --format '{{.State.Health.Status}}' | aws elb describe-target-health |
| **Scale out** | docker compose up -d --scale web=4 | aws autoscaling set-desired-capacity |
| **Stop compute** | docker compose stop | aws ec2 stop-instances |
| **Terminate** | docker compose down | aws ec2 terminate-instances |
---
## Conclusione
Le risorse dei container Docker seguono gli stessi principi fondamentali delle EC2 instances: definizione di CPU e memoria, monitoraggio dell'utilizzo, e health checks per verificare lo stato.
Quando lavorerai con EC2 cloud, ricorda:
- **Docker Container** = **EC2 Instance** (unità di compute)
- **Resource Limits** = **Instance Type** (dimensione e potenza)
- **Healthcheck** = **ELB Health Check** (verifica stato)
- **docker stats** = **CloudWatch Metrics** (monitoraggio)
- **depends_on** = **ECS DependsOn** (ordinamento avvio)
Comprendendo questi parallelismi, sarai in grado di progettare architetture cloud scalabili usando le competenze acquisite localmente.
---
## Approfondimenti
- [AWS EC2 Instance Types Documentation](https://docs.aws.amazon.com/ec2/latest/instancetypes/)
- [ECS Task Definitions](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html)
- [ELB Health Checks](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html)
- [CloudWatch Metrics](https://docs.aws.amazon.com/AmazonCloudWatch/latest/UserGuide/cloudwatch_concepts.html)

View File

@@ -0,0 +1,94 @@
# How-to: Verificare l'Utilizzo delle Risorse
Come monitorare l'utilizzo CPU e memoria dei container Docker.
## Utilizzo Base
### Snapshot Singolo
```bash
docker stats --no-stream
```
Output:
```
CONTAINER NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
12345 lab03-web 0.01% 2.5MiB / 1GiB 0.24% 1.2kB / 0B 0B / 0B 2
```
### Monitoraggio in Tempo Reale
```bash
docker stats
```
Premi `Ctrl+C` per uscire.
### Container Specifico
```bash
docker stats lab03-web
```
## Formattazione Avanzata
### Solo Container e CPU/Memoria
```bash
docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"
```
### Output senza header
```bash
docker stats --no-stream --format "{{.Container}}: {{.CPUPerc}}, {{.MemUsage}}"
```
### Output CSV
```bash
docker stats --no-stream --format "{{.Container}},{{.CPUPerc}},{{.MemUsage}}"
```
## Interpretare l'Output
### CPU Percentage
- `0.01%` - Container idle
- `50%` - Container usa mezza CPU
- `100%` - Container usa 1 CPU completa
- `>100%` - Container usa più di 1 CPU (multi-core)
### Memory Usage
- `2.5MiB / 1GiB` - Usati 2.5 MB su 1 GB di limite
- `512MiB / 512MiB` - Al limite (potrebbe causare OOM)
- `980MiB / 1GiB` - Vicino al limite (watch!)
### Memory Percentage
- `<50%` - Sotto l'half del limite (OK)
- `50-80%` - Nella norma (monitorare)
- `>80%` - Vicino al limite (attenzione)
- `>95%` - A rischio di OOM kill
## Troubleshooting
### Container usa 0% CPU
Container potrebbe essere idle o bloccato. Verifica:
```bash
docker exec lab03-web ps aux
```
### Memory usage alto
Identifica il processo che usa più memoria:
```bash
docker exec lab03-web ps aux --sort=-%mem | head -5
```
### Container OOM killed
Cerca "OOM" nei log:
```bash
docker inspect lab03-web --format '{{.State.OOMKilled}}'
```
## Vedi Anche
- How-to: Testare Limits Enforcement
- Reference: Compose Resources Syntax

View File

@@ -0,0 +1,120 @@
# How-to: Scrivere Healthchecks Personalizzati
Come creare healthchecks custom per diversi tipi di servizi.
## Pattern Comuni
### HTTP Healthcheck
```yaml
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost/health || exit 1"]
interval: 15s
timeout: 3s
retries: 3
```
### TCP Port Check
```yaml
healthcheck:
test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"]
interval: 10s
timeout: 2s
retries: 3
```
### File Existence Check
```yaml
healthcheck:
test: ["CMD-SHELL", "test -f /var/run/app/ready || exit 1"]
interval: 5s
timeout: 1s
retries: 5
```
### Database Connection
```yaml
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost || exit 1"]
interval: 10s
timeout: 5s
retries: 5
```
### Redis Check
```yaml
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
```
### Python Script Check
```yaml
healthcheck:
test: ["CMD-SHELL", "python /app/healthcheck.py || exit 1"]
interval: 30s
timeout: 10s
retries: 3
```
## Best Practices
### 1. Check Leggeri
```yaml
# ✓ GOOD - pagina leggera
test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
# ✗ AVOID - pagina pesante
test: ["CMD", "wget", "--spider", "-q", "http://localhost/big-data"]
```
### 2. Timeout Appropriati
```yaml
# ✓ GOOD - timeout proporzionato all'interval
interval: 10s
timeout: 3s
# ✗ AVOID - timeout troppo lungo
interval: 10s
timeout: 30s # Riduce frequenza dei check
```
### 3. Retries Adeguati
```yaml
# ✓ GOOD - più retries per servizi lenti (database)
healthcheck:
retries: 5
# ✓ GOOD - meno retries per servizi veloci (web)
healthcheck:
retries: 3
```
## Debugging
### Testare manualmente
```bash
docker exec lab03-web wget --spider -q http://localhost/health
echo $? # 0 = success, !0 = failure
```
### Vedere log healthcheck
```bash
docker inspect lab03-web --format '{{range .State.Health.Log}}{{.Output}} {{end}}'
```
### Reset health status
```bash
docker restart lab03-web
```
## Vedi Anche
- Tutorial: Implementare Healthchecks
- Reference: Healthcheck Syntax

View File

@@ -0,0 +1,97 @@
# How-to: Selezionare l'EC2 Instance Type Corretto
Come scegliere l'istanza EC2 giusta per il tuo workload Docker.
## Decision Tree
### Step 1: Tipo di Carico
**Dev/Test:**
- Usa `t2` (burstable) - economico
- Config: `cpus: '0.5'`, `memory: 512M`
**Web Server:**
- Usa `t2.small` o `t2.medium`
- Config: `cpus: '1'`, `memory: 2G` (o 4G)
**Application Server:**
- Usa `m5.large` - performance consistente
- Config: `cpus: '2'`, `memory: 8G`
**Database:**
- Usa `t2.medium` (dev) o `m5.xlarge` (prod)
- Config: `cpus: '2'`, `memory: 4G` (o 16G)
**Batch Processing:**
- Usa `c5` (compute optimized)
- Config: `cpus: '4'`, `memory: 4G`
### Step 2: Analizza il Tuo Container
```bash
# Monitora il consumo attuale
docker stats lab03-app --no-stream
# Se CPU > 70% → considera più CPU
# Se Memory > 80% → considera più memoria
```
### Step 3: Considera il Costo
| Instance | Costo/ora (us-east-1) | Use Case |
|----------|----------------------|----------|
| t2.nano | ~$0.006 | Micro |
| t2.micro | ~$0.012 | Dev/Test |
| t2.small | ~$0.024 | Web |
| t2.medium | ~$0.048 | Application |
| m5.large | ~$0.096 | Production |
## Docker → EC2 Quick Reference
```yaml
# Dev/Test
cpus: '0.5'; memory: 512M # → t2.nano
cpus: '1'; memory: 1G # → t2.micro
# Web Tier
cpus: '1'; memory: 2G # → t2.small
cpus: '2'; memory: 4G # → t2.medium
# Application Tier
cpus: '2'; memory: 8G # → m5.large
cpus: '4'; memory: 16G # → m5.xlarge
# Database Tier
cpus: '2'; memory: 4G # → t2.medium (dev)
cpus: '4'; memory: 32G # → m5.2xlarge (prod)
```
## Scaling Strategy
### Horizontal Scaling
```yaml
# Più container piccoli (t2.micro)
web:
deploy:
replicas: 4
resources:
limits:
cpus: '1'
memory: 1G
```
### Vertical Scaling
```yaml
# Meno container grandi (m5.large)
web:
deploy:
replicas: 1
resources:
limits:
cpus: '2'
memory: 8G
```
## Vedi Anche
- Reference: EC2 Instance Mapping
- Explanation: Compute-EC2 Parallels

View File

@@ -0,0 +1,88 @@
# How-to: Testare l'Enforcement dei Limiti
Come verificare che i limiti delle risorse siano effettivamente applicati.
## Test 1: Verificare Configurazione
### Controllare nel compose file
```bash
grep -A 10 "deploy:" docker-compose.yml | grep -E "cpus:|memory:"
```
### Controllare nel container
```bash
docker inspect lab03-web --format '{{.HostConfig.NanoCpus}}' # CPU (1e9 = 1 CPU)
docker inspect lab03-web --format '{{.HostConfig.Memory}}' # Memory in bytes
```
## Test 2: Stress Test CPU
### Avviare container con stress
```bash
docker run -d --name stress-test \
--cpus='0.5' \
polinux/stress \
--cpu 1 \
--timeout 30s
```
### Monitorare enforcement
```bash
docker stats stress-test --no-stream
```
**Risultato atteso:** CPU non supera il 50% (0.5 CPU)
### Cleanup
```bash
docker rm -f stress-test
```
## Test 3: Stress Test Memory
### Avviare test OOM
```bash
docker run -d --name mem-test \
--memory='512m' \
polinux/stress \
--vm 1 \
--vm-bytes 600M \
--timeout 60s
```
### Verificare OOM kill
```bash
docker ps -a --filter 'name=mem-test'
```
**Risultato atteso:** Container exited (code 137 = OOM killed)
### Cleanup
```bash
docker rm -f mem-test
```
## Test 4: Verifica con Script
```bash
#!/bin/bash
# verify-limits.sh
for container in lab03-web lab03-app lab03-db; do
echo "Container: $container"
docker inspect "$container" --format ' CPUs: {{.HostConfig.NanoCpus}}'
docker inspect "$container" --format ' Memory: {{.HostConfig.Memory}}'
done
```
## Vedi Anche
- How-to: Check Resource Usage
- Reference: EC2 Instance Mapping

View File

@@ -0,0 +1,210 @@
# Reference: Docker Compose Resources Syntax
Riferimento completo per la configurazione delle risorse in Docker Compose.
## Sezione deploy.resources
### Struttura Completa
```yaml
services:
service_name:
deploy:
resources:
limits:
cpus: 'VALUE'
memory: 'VALUE'
reservations:
cpus: 'VALUE'
memory: 'VALUE'
```
## Parametri Limits
### cpus
**Formato:** Stringa con numero decimale
```yaml
cpus: '0.5' # 50% di 1 CPU
cpus: '1' # 1 CPU completa
cpus: '2' # 2 CPU complete
cpus: '1.5' # 1.5 CPU
cpus: '0.25' # 25% di 1 CPU
```
**Validità:**
- Minimo: `0.001` (1 millesimo di CPU)
- Massimo: Numero di CPU host (es. 8 su host con 8 core)
- Default: Nessun limite (tutte le CPU disponibili)
### memory
**Formato:** Stringa con unità
```yaml
memory: '512M' # 512 Megabyte
memory: '1G' # 1 Gigabyte
memory: '128M' # 128 Megabyte
memory: '4G' # 4 Gigabyte
memory: '1024M' # 1024 Megabyte (equivale a 1G)
```
**Unità Supportate:**
- `B` - Byte
- `K` / `KB` - Kilobyte (1024 bytes)
- `M` / `MB` - Megabyte (1024 KB)
- `G` / `GB` - Gigabyte (1024 MB)
**Validità:**
- Minimo: `4M` (4 Megabyte)
- Massico: Memoria host disponibile
- Default: Nessun limite (tutta la memoria disponibile)
## Parametri Reservations
### Scopo
Le **reservations** garantiscono risorse minime disponibili:
- Docker riserva queste risorse per il container
- Usato per pianificazione capacity (Kubernetes)
- Meno comune in Docker Compose locale
### Esempio
```yaml
deploy:
resources:
limits:
cpus: '2' # Massimo 2 CPU
memory: '4G' # Massimo 4 GB
reservations:
cpus: '1' # Garantite 1 CPU
memory: '2G' # Garantiti 2 GB
```
**Uso tipico:**
- `limits` - Definisce il tetto massimo (enforcement)
- `reservations` - Definisce il pavimento minimo (garanzia)
## Esempi Completi per Servizi
### Web Server (t2.micro)
```yaml
web:
image: nginx:alpine
deploy:
resources:
limits:
cpus: '1'
memory: 1G
```
### Application Server (t2.small)
```yaml
app:
image: node:alpine
deploy:
resources:
limits:
cpus: '1'
memory: 2G
```
### Database (t2.medium)
```yaml
db:
image: postgres:16-alpine
deploy:
resources:
limits:
cpus: '2'
memory: 4G
reservations:
cpus: '1'
memory: 2G
```
### Worker CPU-Intensive (c5.xlarge)
```yaml
worker:
image: python:alpine
deploy:
resources:
limits:
cpus: '4'
memory: 4G
```
## Validation
### Verifica con docker compose config
```bash
docker compose config
```
Output per servizio con limits:
```yaml
services:
web:
deploy:
resources:
limits:
cpus: '1'
memory: 1073741824 # 1G in byte
```
### Verifica con docker inspect
```bash
docker inspect lab03-web --format '{{.HostConfig}}'
```
Output:
```
map[NanoCpus:1000000000 Memory:1073741824]
```
Conversione:
- `NanoCpus: 1000000000` = 1 CPU (1e9 nanocpus)
- `Memory: 1073741824` = 1 GB (in byte)
## Troubleshooting
### Errore: "no matching resources"
**Causa:** Host non ha abbastanza risorse
**Soluzione:**
```yaml
# Riduci i limits
deploy:
resources:
limits:
cpus: '0.5' # Era '2'
memory: 512M # Era '2G'
```
### Errore: "invalid memory format"
**Causa:** Formato non valido
**Soluzione:**
```yaml
# ✗ WRONG
memory: 1024 # Manca unità
memory: "1GB" # "GB" non valido (usa G)
# ✓ CORRECT
memory: 1G
memory: 1024M
```
## Vedi Anche
- Tutorial: Configurare Limiti delle Risorse
- Reference: EC2 Instance Mapping

View File

@@ -0,0 +1,159 @@
# Reference: EC2 Instance Type Mapping
Tabella completa di mapping tra Docker resource limits e EC2 instance types.
## Tabella di Mapping Completa
| Docker Compose | EC2 Instance | vCPUs | Memory | Costo/ora* | Use Case |
|----------------|--------------|-------|--------|------------|----------|
| `cpus: '0.5'`<br>`memory: 512M` | t2.nano | 0.5 | 512 MB | $0.006 | Microservizi minimi |
| `cpus: '1'`<br>`memory: 1G` | t2.micro | 1 | 1 GB | $0.012 | Dev/Test |
| `cpus: '1'`<br>`memory: 2G` | t2.small | 1 | 2 GB | $0.024 | Web server |
| `cpus: '2'`<br>`memory: 4G` | t2.medium | 2 | 4 GB | $0.048 | Application |
| `cpus: '2'`<br>`memory: 8G` | m5.large | 2 | 8 GB | $0.096 | Production |
| `cpus: '4'`<br>`memory: 16G` | m5.xlarge | 4 | 16 GB | $0.192 | High traffic |
| `cpus: '8'`<br>`memory: 32G` | m5.2xlarge | 8 | 32 GB | $0.384 | Scalabile |
| `cpus: '16'`<br>`memory: 64G` | m5.4xlarge | 16 | 64 GB | $0.768 | Enterprise |
\*Costi indicativi per regione us-east-1
## Famiglie Instance Types
### General Purpose (m5, m6)
Equilibrato tra CPU, memoria, rete.
```yaml
# m5.large - 2 vCPUs, 8 GB
cpus: '2'
memory: 8G
```
**Use case:** Web server, application server
### Burstable (t2, t3)
Credit-based CPU, basso costo.
```yaml
# t2.micro - 1 vCPU, 1 GB
cpus: '1'
memory: 1G
```
**Use case:** Dev, test, microservizi
### Compute Optimized (c5, c6)
Alto ratio CPU/memoria.
```yaml
# c5.xlarge - 4 vCPUs, 8 GB
cpus: '4'
memory: 8G
```
**Use case:** Batch processing, encoding
### Memory Optimized (r5, r6)
Alto ratio memoria/CPU.
```yaml
# r5.large - 2 vCPUs, 16 GB
cpus: '2'
memory: 16G
```
**Use case:** Database, caching
## Docker Compose Examples
### T2 Nano (Micro)
```yaml
micro:
image: alpine:3.19
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
```
### T2 Micro (Dev/Test)
```yaml
dev:
image: node:alpine
deploy:
resources:
limits:
cpus: '1'
memory: 1G
```
### T2 Small (Web)
```yaml
web:
image: nginx:alpine
deploy:
resources:
limits:
cpus: '1'
memory: 2G
```
### T2 Medium (Application)
```yaml
app:
image: python:alpine
deploy:
resources:
limits:
cpus: '2'
memory: 4G
```
### M5 Large (Production)
```yaml
production:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2'
memory: 8G
```
## Shortcut Reference
### Quick Copy-Paste
```yaml
# T2 Nano
cpus: '0.5'; memory: 512M
# T2 Micro
cpus: '1'; memory: 1G
# T2 Small
cpus: '1'; memory: 2G
# T2 Medium
cpus: '2'; memory: 4G
# M5 Large
cpus: '2'; memory: 8G
# M5 XLarge
cpus: '4'; memory: 16G
```
## Vedi Anche
- Tutorial: Configurare Limiti delle Risorse
- How-to: Selezionare Instance Type
- Explanation: Compute-EC2 Parallels

View File

@@ -0,0 +1,193 @@
# Reference: Docker Compose Healthcheck Syntax
Riferimento completo per la configurazione degli healthchecks.
## Sezione healthcheck
### Struttura Completa
```yaml
services:
service_name:
healthcheck:
test: ["CMD", "command", "arg1", "arg2"]
interval: 30s
timeout: 30s
retries: 3
start_period: 0s
```
## Parametri
### test (Required)
Comando da eseguire per verificare la salute.
**Formato CMD:**
```yaml
test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
```
**Formato CMD-SHELL:**
```yaml
test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
```
**Formato NONE:**
```yaml
test: ["NONE"]
```
Disabilita healthcheck ereditato dall'immagine.
### interval
Frequenza di esecuzione del test.
```yaml
interval: 5s # Ogni 5 secondi
interval: 10s # Ogni 10 secondi
interval: 30s # Ogni 30 secondi (default)
interval: 1m # Ogni minuto
```
**Validità:**
- Minimo: `1s` (1 secondo)
- Massimo: Nessuno
- Default: `30s`
### timeout
Tempo massimo per completare il test.
```yaml
timeout: 2s # 2 secondi
timeout: 5s # 5 secondi (default)
timeout: 30s # 30 secondi
```
**Nota:** Se il test supera il timeout, conta come fallimento.
### retries
Numero di fallimenti consecutivi prima di marcare unhealthy.
```yaml
retries: 1 # 1 fallimento = unhealthy
retries: 3 # 3 fallimenti = unhealthy (default)
retries: 5 # 5 fallimenti = unhealthy
```
**Calcolo del tempo unhealthy:**
```
tempo_totale = (interval + timeout) × retries
```
Esempio con `interval: 10s, timeout: 5s, retries: 3`:
- Check 1: 0s - 5s
- Check 2: 10s - 15s
- Check 3: 20s - 25s
- Unhealthy dopo: 25s
### start_period
Grace period prima di contare retries verso unhealthy.
```yaml
start_period: 0s # Nessun grace period (default)
start_period: 5s # 5 secondi di grace
start_period: 10s # 10 secondi di grace
start_period: 30s # 30 secondi di grace
```
**Comportamento:**
- Durante `start_period`: Fallimenti non contano
- Dopo `start_period`: `retries` inizi a contare
## Esempi per Tipo di Servizio
### HTTP Service (Nginx)
```yaml
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s
```
### Database (PostgreSQL)
```yaml
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres || exit 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
```
### Cache (Redis)
```yaml
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
```
### TCP Connection
```yaml
healthcheck:
test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"]
interval: 15s
timeout: 2s
retries: 3
```
### File Check
```yaml
healthcheck:
test: ["CMD-SHELL", "test -f /tmp/ready || exit 1"]
interval: 5s
timeout: 1s
retries: 5
```
## Stati di Salute
### Ciclo di Vita
```
created → starting → healthy
unhealthy
```
**Transizioni:**
| Da | A | Condizione |
|----|---|------------|
| created | starting | Container avviato con healthcheck |
| starting | healthy | `retries` consecutivi passano |
| starting | unhealthy | `retries` consecutivi falliscono (dopo start_period) |
| healthy | unhealthy | `retries` consecutivi falliscono |
| unhealthy | healthy | 1 check passa |
### Ispezionare Stato
```bash
# Stato corrente
docker inspect lab03-web --format '{{.State.Health.Status}}'
# Output: healthy / unhealthy / starting
# Exit code dell'ultimo check
docker inspect lab03-web --format '{{.State.Health.ExitCode}}'
# Output: 0 (success) o !0 (failure)
### Vedi Anche
- Tutorial: Implementare Healthchecks
- How-to: Custom Healthcheck

View File

@@ -0,0 +1,215 @@
#!/bin/bash
# Test 01: Resource Limits Configuration
# Verifies that docker-compose.yml services have mandatory resource limits
# Usage: bash labs/lab-03-compute/tests/01-resource-limits-test.sh
set -euo pipefail
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
BOLD='\033[1m'
NC='\033[0m'
# Get script directory
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
# Counter helpers
pass_count=0
fail_count=0
inc_pass() { ((pass_count++)) || true; }
inc_fail() { ((fail_count++)) || true; }
# Helper functions
print_header() {
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}${NC} ${BOLD}$1${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
}
print_test() {
echo -e "\n${BLUE}[TEST]${NC} $1"
}
print_pass() {
echo -e " ${GREEN}[✓]${NC} $1"
inc_pass
}
print_fail() {
echo -e " ${RED}[✗]${NC} $1"
inc_fail
}
print_info() {
echo -e " ${BLUE}[i]${NC} $1"
}
# Main verification
print_header "Lab 03 Resource Limits Verification"
cd "$LAB_DIR"
# Test 1: docker-compose.yml exists
print_test "Verifying docker-compose.yml exists"
if [[ -f "docker-compose.yml" ]]; then
print_pass "docker-compose.yml found"
else
print_fail "docker-compose.yml not found"
exit 1
fi
# Test 2: docker-compose.yml is valid
print_test "Validating docker-compose.yml syntax"
if docker compose config &> /dev/null; then
print_pass "docker-compose.yml has valid syntax"
else
print_fail "docker-compose.yml has syntax errors"
exit 1
fi
# Test 3: Services are defined
print_test "Verifying services are defined"
SERVICES=$(docker compose config --services 2>/dev/null)
SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
if [[ $SERVICE_COUNT -gt 0 ]]; then
print_pass "Found $SERVICE_COUNT services"
echo "$SERVICES" | while read -r service; do
print_info " - $service"
done
else
print_fail "No services defined"
exit 1
fi
# Test 4: INF-03 compliance check - all services must have resource limits
print_test "Checking INF-03 compliance (resource limits)"
NON_COMPLIANT=0
for service in $SERVICES; do
# Check for deploy.resources section
has_deploy=$(docker compose config 2>/dev/null | grep -A 20 "^$service:" | grep -c "deploy:" || echo "0")
has_resources=$(docker compose config 2>/dev/null | grep -A 25 "^$service:" | grep -c "resources:" || echo "0")
if [[ $has_deploy -eq 0 || $has_resources -eq 0 ]]; then
print_fail " $service: Missing deploy.resources section"
((NON_COMPLIANT++)) || true
else
# Check for CPU limit
has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0")
# Check for memory limit
has_memory=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "memory:" || echo "0")
if [[ $has_cpu -eq 0 ]]; then
print_fail " $service: Missing cpus limit"
((NON_COMPLIANT++)) || true
fi
if [[ $has_memory -eq 0 ]]; then
print_fail " $service: Missing memory limit"
((NON_COMPLIANT++)) || true
fi
if [[ $has_cpu -gt 0 && $has_memory -gt 0 ]]; then
# Extract limits for reporting
cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "')
mem_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '")
print_pass " $service: cpus: $cpu_limit, memory: $mem_limit"
fi
fi
done
if [[ $NON_COMPLIANT -eq 0 ]]; then
print_pass "INF-03 compliance: All services have resource limits"
else
print_fail "INF-03 compliance: $NON_COMPLIANT services missing limits"
fi
# Test 5: Verify specific EC2 instance type parallels
print_test "Verifying EC2 instance type parallels"
INSTANCE_TYPES=0
for service in $SERVICES; do
cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "' || echo "")
mem_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '"" || echo "")
# Map to EC2 instance types
if [[ -n "$cpu_limit" && -n "$mem_limit" ]]; then
case "$cpu_limit:$mem_limit" in
"0.5:512M"|"0.5:512")
instance_type="t2.nano"
;;
"1:1G"|"1:1024M"|"1:1024")
instance_type="t2.micro"
;;
"1:2G"|"1:2048M"|"1:2048")
instance_type="t2.small"
;;
"2:4G"|"2:4096M"|"2:4096")
instance_type="t2.medium"
;;
"2:8G"|"2:8192M"|"2:8192")
instance_type="m5.large"
;;
*)
instance_type="custom"
;;
esac
print_info " $service$instance_type (cpus: $cpu_limit, memory: $mem_limit)"
((INSTANCE_TYPES++)) || true
fi
done
if [[ $INSTANCE_TYPES -gt 0 ]]; then
print_pass "EC2 instance type mapping complete for $INSTANCE_TYPES services"
else
print_fail "No services with valid resource limits found"
fi
# Test 6: Verify resource limit formats
print_test "Verifying resource limit formats"
FORMAT_OK=0
for service in $SERVICES; do
cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "' || echo "")
# CPU limit should be a number or decimal
if [[ -n "$cpu_limit" ]]; then
if [[ "$cpu_limit" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
((FORMAT_OK++)) || true
else
print_fail " $service: Invalid CPU format: $cpu_limit"
fi
fi
done
if [[ $FORMAT_OK -eq $SERVICE_COUNT ]]; then
print_pass "All resource limits use valid format"
else
print_fail "Some resource limits have invalid format"
fi
# Summary
print_header "Resource Limits Verification Summary"
echo -e "Tests run: $((pass_count + fail_count))"
echo -e "${GREEN}Passed: $pass_count${NC}"
if [[ $fail_count -gt 0 ]]; then
echo -e "${RED}Failed: $fail_count${NC}"
fi
if [[ $fail_count -eq 0 ]]; then
echo -e "\n${GREEN}${BOLD}✓ ALL RESOURCE LIMITS CHECKS PASSED${NC}"
echo -e "\nINF-03 compliance verified!"
exit 0
else
echo -e "\n${RED}Some resource limits checks failed${NC}"
echo -e "Please ensure all services have cpus and memory limits."
exit 1
fi

View File

@@ -0,0 +1,255 @@
#!/bin/bash
# Test 02: Healthcheck Configuration
# Verifies that docker-compose.yml services have healthchecks configured
# Usage: bash labs/lab-03-compute/tests/02-healthcheck-test.sh
set -euo pipefail
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
BOLD='\033[1m'
NC='\033[0m'
# Get script directory
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
# Counter helpers
pass_count=0
fail_count=0
inc_pass() { ((pass_count++)) || true; }
inc_fail() { ((fail_count++)) || true; }
# Helper functions
print_header() {
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}${NC} ${BOLD}$1${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
}
print_test() {
echo -e "\n${BLUE}[TEST]${NC} $1"
}
print_pass() {
echo -e " ${GREEN}[✓]${NC} $1"
inc_pass
}
print_fail() {
echo -e " ${RED}[✗]${NC} $1"
inc_fail
}
print_info() {
echo -e " ${BLUE}[i]${NC} $1"
}
# Main verification
print_header "Lab 03 Healthcheck Verification"
cd "$LAB_DIR"
# Test 1: docker-compose.yml exists
print_test "Verifying docker-compose.yml exists"
if [[ -f "docker-compose.yml" ]]; then
print_pass "docker-compose.yml found"
else
print_fail "docker-compose.yml not found"
exit 1
fi
# Test 2: docker-compose.yml is valid
print_test "Validating docker-compose.yml syntax"
if docker compose config &> /dev/null; then
print_pass "docker-compose.yml has valid syntax"
else
print_fail "docker-compose.yml has syntax errors"
exit 1
fi
# Test 3: Services are defined
print_test "Verifying services are defined"
SERVICES=$(docker compose config --services 2>/dev/null)
SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
if [[ $SERVICE_COUNT -gt 0 ]]; then
print_pass "Found $SERVICE_COUNT services"
else
print_fail "No services defined"
exit 1
fi
# Test 4: Healthcheck configuration check
print_test "Checking healthcheck configuration"
MISSING_HEALTHCHECK=0
for service in $SERVICES; do
# Check for healthcheck section
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
if [[ $has_healthcheck -eq 0 ]]; then
print_fail " $service: Missing healthcheck section"
((MISSING_HEALTHCHECK++)) || true
else
# Verify healthcheck has test command
has_test=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep -c "test:" || echo "0")
if [[ $has_test -eq 0 ]]; then
print_fail " $service: Missing healthcheck test command"
((MISSING_HEALTHCHECK++)) || true
else
# Extract test command for reporting
test_cmd=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "test:" | head -1 | sed 's/.*test: //' | tr -d '"')
print_pass " $service: healthcheck configured"
print_info " test: $test_cmd"
fi
fi
done
if [[ $MISSING_HEALTHCHECK -eq 0 ]]; then
print_pass "All services have healthchecks configured"
else
print_fail "$MISSING_HEALTHCHECK services missing healthchecks"
fi
# Test 5: Healthcheck parameters verification
print_test "Verifying healthcheck parameters"
PARAMS_OK=0
for service in $SERVICES; do
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
if [[ $has_healthcheck -gt 0 ]]; then
# Check for interval
has_interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "interval:" || echo "0")
# Check for timeout
has_timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "timeout:" || echo "0")
# Check for retries
has_retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "retries:" || echo "0")
interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "N/A")
timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "N/A")
retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "N/A")
if [[ $has_interval -gt 0 && $has_timeout -gt 0 && $has_retries -gt 0 ]]; then
print_pass " $service: interval=$interval, timeout=$timeout, retries=$retries"
((PARAMS_OK++)) || true
else
print_info " $service: interval=$interval, timeout=$timeout, retries=$retries"
# Check for start_period (optional)
has_start_period=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "start_period:" || echo "0")
if [[ $has_start_period -gt 0 ]]; then
start_period=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "start_period:" | sed 's/.*start_period: //' | tr -d " '"" || echo "")
print_info " start_period=$start_period"
fi
fi
fi
done
if [[ $PARAMS_OK -gt 0 ]]; then
print_pass "$PARAMS_OK services have complete healthcheck parameters"
else
print_fail "No services with complete healthcheck parameters"
fi
# Test 6: Healthcheck command types
print_test "Analyzing healthcheck command types"
HTTP_COUNT=0
CMD_SHELL_COUNT=0
CMD_COUNT=0
CUSTOM_COUNT=0
for service in $SERVICES; do
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
if [[ $has_healthcheck -gt 0 ]]; then
# Get test command
test_section=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" || echo "")
if echo "$test_section" | grep -q "CMD-SHELL"; then
print_info " $service: CMD-SHELL healthcheck"
((CMD_SHELL_COUNT++)) || true
elif echo "$test_section" | grep -q "CMD"; then
print_info " $service: CMD healthcheck"
((CMD_COUNT++)) || true
else
print_info " $service: Custom healthcheck format"
((CUSTOM_COUNT++)) || true
fi
# Check if HTTP-based
if echo "$test_section" | grep -qiE "(wget|curl|http)"; then
((HTTP_COUNT++)) || true
fi
fi
done
print_info "Healthcheck types: CMD-SHELL ($CMD_SHELL_COUNT), CMD ($CMD_COUNT), Custom ($CUSTOM_COUNT)"
if [[ $HTTP_COUNT -gt 0 ]]; then
print_pass "$HTTP_COUNT services use HTTP-based healthchecks"
fi
# Test 7: ELB Health Check Parallel verification
print_test "Verifying ELB Health Check parallelism"
ELB_PARALLEL=0
for service in $SERVICES; do
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
if [[ $has_healthcheck -gt 0 ]]; then
interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "")
timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "")
retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "")
# Map to ELB defaults
if [[ "$interval" == "30s" || "$interval" =~ ^30 ]]; then
elb_interval="default (30s)"
((ELB_PARALLEL++)) || true
else
elb_interval="custom ($interval)"
fi
if [[ "$timeout" == "5s" || "$timeout" =~ ^5 ]]; then
elb_timeout="default (5s)"
((ELB_PARALLEL++)) || true
else
elb_timeout="custom ($timeout)"
fi
if [[ "$retries" == "3" || "$retries" =~ ^3 ]]; then
elb_retries="default (3)"
((ELB_PARALLEL++)) || true
else
elb_retries="custom ($retries)"
fi
print_info " $service: ELB parallel (interval: $elb_interval, timeout: $elb_timeout, retries: $elb_retries)"
fi
done
if [[ $ELB_PARALLEL -gt 0 ]]; then
print_pass "ELB Health Check parallelism verified"
fi
# Summary
print_header "Healthcheck Verification Summary"
echo -e "Tests run: $((pass_count + fail_count))"
echo -e "${GREEN}Passed: $pass_count${NC}"
if [[ $fail_count -gt 0 ]]; then
echo -e "${RED}Failed: $fail_count${NC}"
fi
if [[ $fail_count -eq 0 ]]; then
echo -e "\n${GREEN}${BOLD}✓ ALL HEALTHCHECK CHECKS PASSED${NC}"
echo -e "\nHealthcheck configuration verified!"
exit 0
else
echo -e "\n${RED}Some healthcheck checks failed${NC}"
echo -e "Please ensure all services have healthchecks configured."
exit 1
fi

View File

@@ -0,0 +1,287 @@
#!/bin/bash
# Test 03: Resource Enforcement Verification
# Verifies that resource limits are actually enforced using docker stats
# Usage: bash labs/lab-03-compute/tests/03-enforcement-test.sh
set -euo pipefail
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
BOLD='\033[1m'
NC='\033[0m'
# Get script directory
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
# Counter helpers
pass_count=0
fail_count=0
inc_pass() { ((pass_count++)) || true; }
inc_fail() { ((fail_count++)) || true; }
# Helper functions
print_header() {
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}${NC} ${BOLD}$1${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
}
print_test() {
echo -e "\n${BLUE}[TEST]${NC} $1"
}
print_pass() {
echo -e " ${GREEN}[✓]${NC} $1"
inc_pass
}
print_fail() {
echo -e " ${RED}[✗]${NC} $1"
inc_fail
}
print_info() {
echo -e " ${BLUE}[i]${NC} $1"
}
print_warning() {
echo -e " ${YELLOW}[!]${NC} $1"
}
# Cleanup function
cleanup() {
print_info "Cleaning up test containers..."
cd "$LAB_DIR"
docker compose down -v 2>/dev/null || true
}
# Set trap for cleanup
trap cleanup EXIT
# Main verification
print_header "Lab 03 Resource Enforcement Verification"
cd "$LAB_DIR"
# Test 1: Verify docker-compose.yml exists and is valid
print_test "Verifying docker-compose.yml exists and is valid"
if [[ ! -f "docker-compose.yml" ]]; then
print_fail "docker-compose.yml not found"
exit 1
fi
if ! docker compose config &> /dev/null; then
print_fail "docker-compose.yml has syntax errors"
exit 1
fi
print_pass "docker-compose.yml is valid"
# Test 2: Start services
print_test "Starting Docker Compose services"
if docker compose up -d &> /dev/null; then
print_pass "Services started successfully"
sleep 5 # Give services time to start
else
print_fail "Failed to start services"
exit 1
fi
# Test 3: Verify containers are running
print_test "Verifying containers are running"
RUNNING_CONTAINERS=$(docker compose ps --services --filter "status=running" | wc -l)
if [[ $RUNNING_CONTAINERS -ge 1 ]]; then
print_pass "Services running: $RUNNING_CONTAINERS containers"
docker compose ps
else
print_fail "Not enough containers running"
exit 1
fi
# Test 4: Check resource limits are applied
print_test "Verifying resource limits are applied to containers"
LIMITS_APPLIED=0
for service in $(docker compose config --services); do
container_name="lab03-$service"
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
# Get CPU limit (NanoCPUs: 1e9 = 1 CPU core)
nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
mem_limit=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
# Convert NanoCPUs to CPU cores
if [[ $nano_cpus -gt 0 ]]; then
cpu_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A")
else
cpu_cores="N/A"
fi
# Convert memory to GB/MB
if [[ $mem_limit -gt 0 ]]; then
if [[ $mem_limit -ge 1073741824 ]]; then
mem_gb=$(echo "scale=2; $mem_limit / 1073741824" | bc 2>/dev/null || echo "N/A")
mem_display="${mem_gb}G"
else
mem_mb=$(echo "scale=0; $mem_limit / 1048576" | bc 2>/dev/null || echo "N/A")
mem_display="${mem_mb}M"
fi
else
mem_display="N/A"
fi
if [[ "$cpu_cores" != "N/A" && "$mem_display" != "N/A" ]]; then
print_pass " $container_name: cpus: $cpu_cores, memory: $mem_display"
((LIMITS_APPLIED++)) || true
else
print_fail " $container_name: Could not read limits (cpus: $cpu_cores, memory: $mem_display)"
fi
fi
done
if [[ $LIMITS_APPLIED -gt 0 ]]; then
print_pass "Resource limits applied to $LIMITS_APPLIED containers"
else
print_fail "No containers with resource limits found"
fi
# Test 5: Monitor resource usage with docker stats
print_test "Monitoring resource usage with docker stats"
STATS_OK=0
# Get initial stats
print_info "Current resource usage:"
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" 2>/dev/null || true
for service in $(docker compose config --services); do
container_name="lab03-$service"
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
# Get CPU percentage and memory usage
cpu_percent=$(docker stats "$container_name" --no-stream --format "{{.CPUPerc}}" 2>/dev/null | sed 's/%//' || echo "N/A")
mem_usage=$(docker stats "$container_name" --no-stream --format "{{.MemUsage}}" 2>/dev/null || echo "N/A")
if [[ "$cpu_percent" != "N/A" && "$mem_usage" != "N/A" ]]; then
print_info " $container_name: CPU: ${cpu_percent}%, Memory: $mem_usage"
((STATS_OK++)) || true
fi
fi
done
if [[ $STATS_OK -gt 0 ]]; then
print_pass "docker stats successfully monitored $STATS_OK containers"
else
print_fail "Failed to monitor container stats"
fi
# Test 6: Verify health status
print_test "Verifying container health status"
HEALTHY_COUNT=0
for service in $(docker compose config --services); do
container_name="lab03-$service"
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
# Get health status
health_status=$(docker inspect "$container_name" --format '{{.State.Health.Status}}' 2>/dev/null || echo "no-healthcheck")
if [[ "$health_status" == "healthy" ]]; then
print_pass " $container_name: $health_status"
((HEALTHY_COUNT++)) || true
elif [[ "$health_status" == "no-healthcheck" ]]; then
print_info " $container_name: no healthcheck configured"
else
print_info " $container_name: $health_status"
fi
fi
done
if [[ $HEALTHY_COUNT -gt 0 ]]; then
print_pass "$HEALTHY_COUNT containers are healthy"
else
print_warning "No healthy containers (services may still be starting)"
fi
# Test 7: Stress test for resource enforcement (if stress container exists)
print_test "Stress testing resource enforcement (if stress container available)"
STRESS_SERVICE=$(docker compose config --services | grep -E "(stress|test)" || echo "")
if [[ -n "$STRESS_SERVICE" ]]; then
print_info "Found stress service: $STRESS_SERVICE"
# Get the container name
stress_container="lab03-$STRESS_SERVICE"
if docker ps --format '{{.Names}}' | grep -q "$stress_container"; then
# Get limits
nano_cpus=$(docker inspect "$stress_container" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
mem_limit=$(docker inspect "$stress_container" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
cpu_limit_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A")
if [[ $mem_limit -ge 1073741824 ]]; then
mem_limit_display=$(echo "scale=2; $mem_limit / 1073741824" | bc 2>/dev/null || echo "N/A")"G"
else
mem_limit_display=$(echo "scale=0; $mem_limit / 1048576" | bc 2>/dev/null || echo "N/A")"M"
fi
print_info "Limits: cpus: $cpu_limit_cores, memory: $mem_limit_display"
# Monitor during stress test
print_info "Monitoring during stress (10 seconds)..."
for i in {1..5}; do
cpu_percent=$(docker stats "$stress_container" --no-stream --format "{{.CPUPerc}}" 2>/dev/null | sed 's/%//' || echo "N/A")
mem_usage=$(docker stats "$stress_container" --no-stream --format "{{.MemUsage}}" 2>/dev/null || echo "N/A")
print_info " Sample $i: CPU: ${cpu_percent}%, Memory: $mem_usage"
sleep 2
done
print_pass "Stress test completed"
fi
else
print_info "No stress service found - skipping stress test"
fi
# Test 8: Verify resource limit enforcement
print_test "Verifying resource limits are enforced"
ENFORCED=0
for service in $(docker compose config --services); do
container_name="lab03-$service"
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
# Check if limits are set (not 0 or -1)
nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
mem_limit=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
if [[ $nano_cpus -gt 0 && $mem_limit -gt 0 ]]; then
print_pass " $container_name: Limits enforced (CPU and memory)"
((ENFORCED++)) || true
else
print_fail " $container_name: Limits not enforced (CPU: $nano_cpus, Memory: $mem_limit)"
fi
fi
done
if [[ $ENFORCED -gt 0 ]]; then
print_pass "Resource limits enforced for $ENFORCED containers"
else
print_fail "Resource limits not enforced for any container"
fi
# Summary
print_header "Resource Enforcement Verification Summary"
echo -e "Tests run: $((pass_count + fail_count))"
echo -e "${GREEN}Passed: $pass_count${NC}"
if [[ $fail_count -gt 0 ]]; then
echo -e "${RED}Failed: $fail_count${NC}"
fi
if [[ $fail_count -eq 0 ]]; then
echo -e "\n${GREEN}${BOLD}✓ ALL ENFORCEMENT CHECKS PASSED${NC}"
echo -e "\nResource limits are properly enforced!"
exit 0
else
echo -e "\n${RED}Some enforcement checks failed${NC}"
echo -e "Please verify resource limit configuration."
exit 1
fi

View File

@@ -0,0 +1,84 @@
#!/bin/bash
# Infrastructure Verification for Lab 03
# Verifies docker-compose.yml is correctly deployed with resource limits and healthchecks
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; BLUE='\033[0;34m'; YELLOW='\033[1;33m'; BOLD='\033[1m'; NC='\033[0m'
pass_count=0; fail_count=0
inc_pass() { ((pass_count++)) || true; }
inc_fail() { ((fail_count++)) || true; }
print_header() {
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}${NC} ${BOLD}$1${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
}
print_test() { echo -e "\n${BLUE}[TEST]${NC} $1"; }
print_pass() { echo -e " ${GREEN}[✓]${NC} $1"; inc_pass; }
print_fail() { echo -e " ${RED}[✗]${NC} $1"; inc_fail; }
cd "$(dirname "$0")/.."
print_header "Lab 03 Infrastructure Verification"
# File and syntax
print_test "Verifying docker-compose.yml exists and valid"
[[ -f "docker-compose.yml" ]] && print_pass "docker-compose.yml found" || { print_fail "docker-compose.yml not found"; exit 1; }
docker compose config &> /dev/null && print_pass "Syntax valid" || { print_fail "Syntax error"; exit 1; }
# Services and limits
print_test "Checking INF-03 compliance (resource limits)"
SERVICES=$(docker compose config --services 2>/dev/null)
NON_COMPLIANT=0
for s in $SERVICES; do
has_cpu=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep -c "cpus:" || echo "0")
has_mem=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep -c "memory:" || echo "0")
if [[ $has_cpu -eq 0 || $has_mem -eq 0 ]]; then
print_fail " $s: missing limits"
((NON_COMPLIANT++)) || true
else
cpu_val=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "')
mem_val=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '""")
print_pass " $s: cpus=$cpu_val, memory=$mem_val"
fi
done
[[ $NON_COMPLIANT -eq 0 ]] && print_pass "INF-03 COMPLIANT" || print_fail "INF-03 VIOLATION: $NON_COMPLIANT services"
# Healthchecks
print_test "Verifying healthcheck configuration"
MISSING_HC=0
for s in $SERVICES; do
has_hc=$(docker compose config 2>/dev/null | grep -A50 "^$s:" | grep -c "healthcheck:" || echo "0")
[[ $has_hc -gt 0 ]] && print_pass " $s: healthcheck configured" || { print_fail " $s: missing healthcheck"; ((MISSING_HC++)); }
done
[[ $MISSING_HC -eq 0 ]] && print_pass "All services have healthchecks" || print_fail "$MISSING_HC missing healthchecks"
# Deploy and verify
print_test "Starting services"
docker compose up -d &> /dev/null && print_pass "Services started" && sleep 8 || { print_fail "Failed to start"; exit 1; }
print_test "Checking running containers"
RUNNING=$(docker compose ps --services --filter "status=running" | wc -l)
[[ $RUNNING -ge 4 ]] && print_pass "$RUNNING containers running" || print_fail "Only $RUNNING containers"
# Enforcement
print_test "Verifying resource limits enforced"
ENFORCED=0
for s in $SERVICES; do
c="lab03-$s"
if docker ps --format '{{.Names}}' | grep -q "$c"; then
nano_cpus=$(docker inspect "$c" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
mem_bytes=$(docker inspect "$c" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
[[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]] && print_pass " $c: limits enforced" || print_fail " $c: limits not applied"
[[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]] && ((ENFORCED++)) || true
fi
done
[[ $ENFORCED -gt 0 ]] && print_pass "Limits enforced for $ENFORCED containers"
# Summary
print_header "Summary"
echo "Tests: $((pass_count + fail_count)) | ${GREEN}Passed: $pass_count${NC} ${RED}Failed: $fail_count${NC}"
[[ $fail_count -eq 0 ]] && { echo -e "\n${GREEN}${BOLD}✓ ALL CHECKS PASSED${NC}\n"; exit 0; } || { echo -e "\n${RED}Some checks failed${NC}\n"; exit 1; }

View File

@@ -0,0 +1,331 @@
#!/bin/bash
# Final Verification: Lab 03 - Compute & EC2
# Comprehensive end-to-end verification for students
# Usage: bash labs/lab-03-compute/tests/99-final-verification.sh
set -euo pipefail
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
BOLD='\033[1m'
NC='\033[0m'
# Get script directory
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
# Counter helpers
pass_count=0
fail_count=0
inc_pass() { ((pass_count++)) || true; }
inc_fail() { ((fail_count++)) || true; }
# Helper functions
print_header() {
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}${NC} ${BOLD}$1${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
}
print_test() {
echo -e "\n${BLUE}[TEST]${NC} $1"
}
print_pass() {
echo -e " ${GREEN}[✓]${NC} $1"
inc_pass
}
print_fail() {
echo -e " ${RED}[✗]${NC} $1"
inc_fail
}
print_info() {
echo -e " ${BLUE}[i]${NC} $1"
}
print_warning() {
echo -e " ${YELLOW}[!]${NC} $1"
}
# Cleanup function
cleanup() {
print_info "\nCleaning up..."
cd "$LAB_DIR"
docker compose down -v 2>/dev/null || true
}
# Set trap for cleanup on exit
trap cleanup EXIT
# Main verification
print_header "Lab 03 Final Verification - Compute & EC2"
cd "$LAB_DIR"
echo -e "\n${BLUE}This script verifies:${NC}"
echo -e " • Resource limits (INF-03 compliance)"
echo -e " • Healthcheck configuration"
echo -e " • Resource enforcement with docker stats"
echo -e " • EC2 instance type parallels"
echo -e " • ELB health check parallels"
# === SECTION 1: File and Syntax ===
print_header "Section 1: Files and Syntax"
# Test 1: docker-compose.yml exists
print_test "Verifying docker-compose.yml exists"
if [[ -f "docker-compose.yml" ]]; then
print_pass "docker-compose.yml found"
else
print_fail "docker-compose.yml not found - create it first!"
exit 1
fi
# Test 2: Syntax validation
print_test "Validating docker-compose.yml syntax"
if docker compose config &> /dev/null; then
print_pass "docker-compose.yml syntax is valid"
else
print_fail "docker-compose.yml has syntax errors - run 'docker compose config' to debug"
exit 1
fi
# Test 3: Services defined
print_test "Checking defined services"
SERVICES=$(docker compose config --services 2>/dev/null)
SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
if [[ $SERVICE_COUNT -ge 3 ]]; then
print_pass "Found $SERVICE_COUNT services (minimum 3 required)"
else
print_fail "Only $SERVICE_COUNT services found (need at least 3)"
exit 1
fi
# === SECTION 2: INF-03 Compliance ===
print_header "Section 2: INF-03 Compliance (Resource Limits)"
print_test "Verifying all services have resource limits"
NON_COMPLIANT=0
LIMITS_SUMMARY=""
for service in $SERVICES; do
# Check for resource limits
has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0")
has_memory=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "memory:" || echo "0")
if [[ $has_cpu -eq 0 ]]; then
print_fail " $service: Missing cpus limit"
((NON_COMPLIANT++)) || true
elif [[ $has_memory -eq 0 ]]; then
print_fail " $service: Missing memory limit"
((NON_COMPLIANT++)) || true
else
cpu_val=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "')
mem_val=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '"")
# Map to EC2 instance type
case "$cpu_val:$mem_val" in
"0.5:512M"|"0.5:512") instance="t2.nano" ;;
"1:1G"|"1:1024M") instance="t2.micro" ;;
"1:2G"|"1:2048M") instance="t2.small" ;;
"2:4G"|"2:4096M") instance="t2.medium" ;;
"2:8G"|"2:8192M") instance="m5.large" ;;
"4:16G"|"4:16384M") instance="m5.xlarge" ;;
*) instance="custom" ;;
esac
print_pass " $service: cpus=$cpu_val, memory=$mem_val$instance"
LIMITS_SUMMARY="$LIMITS_SUMMARY\n$service$instance"
fi
done
if [[ $NON_COMPLIANT -eq 0 ]]; then
print_pass "✓ INF-03 COMPLIANT: All services have resource limits"
else
print_fail "✗ INF-03 VIOLATION: $NON_COMPLIANT services missing limits"
fi
# === SECTION 3: Healthcheck Configuration ===
print_header "Section 3: Healthcheck Configuration"
print_test "Verifying healthcheck configuration"
MISSING_HC=0
HC_SUMMARY=""
for service in $SERVICES; do
has_hc=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
if [[ $has_hc -eq 0 ]]; then
print_fail " $service: Missing healthcheck"
((MISSING_HC++)) || true
else
interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "N/A")
timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "N/A")
retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "N/A")
print_pass " $service: interval=$interval, timeout=$timeout, retries=$retries"
HC_SUMMARY="$HC_SUMMARY\n$service: interval=$interval, timeout=$timeout, retries=$retries"
fi
done
if [[ $MISSING_HC -eq 0 ]]; then
print_pass "✓ All services have healthchecks configured"
else
print_fail "$MISSING_HC services missing healthchecks"
fi
# === SECTION 4: Infrastructure Deployment ===
print_header "Section 4: Infrastructure Deployment"
print_test "Starting services"
if docker compose up -d &> /dev/null; then
print_pass "Services started successfully"
sleep 8 # Wait for services to be ready
else
print_fail "Failed to start services"
exit 1
fi
print_test "Verifying containers are running"
RUNNING=$(docker compose ps --services --filter "status=running" | wc -l)
if [[ $RUNNING -ge 3 ]]; then
print_pass "$RUNNING containers running"
docker compose ps
else
print_fail "Only $RUNNING containers running (expected 3+)"
fi
# === SECTION 5: Resource Enforcement ===
print_header "Section 5: Resource Enforcement Verification"
print_test "Checking resource limits are applied"
LIMITS_ENFORCED=0
for service in $SERVICES; do
container_name="lab03-$service"
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
mem_bytes=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
if [[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]]; then
cpu_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A")
if [[ $mem_bytes -ge 1073741824 ]]; then
mem_display=$(echo "scale=2; $mem_bytes / 1073741824" | bc)G
else
mem_display=$(echo "scale=0; $mem_bytes / 1048576" | bc)M
fi
print_pass " $container_name: ${cpu_cores} CPU, $mem_display RAM"
((LIMITS_ENFORCED++)) || true
else
print_fail " $container_name: Limits not applied"
fi
fi
done
if [[ $LIMITS_ENFORCED -gt 0 ]]; then
print_pass "✓ Resource limits enforced for $LIMITS_ENFORCED containers"
else
print_fail "✗ Resource limits not enforced"
fi
# Test docker stats
print_test "Monitoring resource usage"
echo -e "\n${BLUE}Live Resource Usage:${NC}"
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" 2>/dev/null || print_fail "docker stats failed"
# === SECTION 6: Health Status ===
print_header "Section 6: Health Status Verification"
print_test "Checking container health status"
HEALTHY=0
for service in $SERVICES; do
container_name="lab03-$service"
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
health=$(docker inspect "$container_name" --format '{{.State.Health.Status}}' 2>/dev/null || echo "no-healthcheck")
if [[ "$health" == "healthy" ]]; then
print_pass " $container_name: healthy ✓"
((HEALTHY++)) || true
elif [[ "$health" == "no-healthcheck" ]]; then
print_warning " $container_name: no healthcheck configured"
else
print_info " $container_name: $health"
fi
fi
done
if [[ $HEALTHY -gt 0 ]]; then
print_pass "$HEALTHY containers are healthy"
else
print_warning "No healthy containers yet (may still be starting)"
fi
# === SECTION 7: Cloud Parallels ===
print_header "Section 7: Cloud Parallels Verification"
print_test "EC2 Instance Type Parallels"
echo -e "${BLUE}Your Docker configuration maps to these EC2 instances:${NC}"
echo -e "$LIMITS_SUMMARY"
print_test "ELB Health Check Parallels"
echo -e "${BLUE}Your healthchecks parallel ELB health checks:${NC}"
echo -e "$HC_SUMMARY"
# === FINAL SUMMARY ===
print_header "Final Verification Summary"
TOTAL_TESTS=$((pass_count + fail_count))
PASS_PERCENT=$((pass_count * 100 / TOTAL_TESTS))
echo -e "Total Tests: $TOTAL_TESTS"
echo -e "${GREEN}Passed: $pass_count${NC} ($PASS_PERCENT%)"
if [[ $fail_count -gt 0 ]]; then
echo -e "${RED}Failed: $fail_count${NC}"
fi
# INF-03 Compliance Summary
echo -e "\n${BLUE}INF-03 Compliance:${NC}"
if [[ $NON_COMPLIANT -eq 0 ]]; then
echo -e " ${GREEN}✓ COMPLIANT${NC} - All services have resource limits"
else
echo -e " ${RED}✗ NON-COMPLIANT${NC} - $NON_COMPLIANT services missing limits"
fi
# Healthcheck Summary
echo -e "\n${BLUE}Healthcheck Coverage:${NC}"
if [[ $MISSING_HC -eq 0 ]]; then
echo -e " ${GREEN}✓ COMPLETE${NC} - All services have healthchecks"
else
echo -e " ${RED}✗ INCOMPLETE${NC} - $MISSING_HC services missing healthchecks"
fi
# Final verdict
if [[ $fail_count -eq 0 ]]; then
echo -e "\n${GREEN}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}${BOLD}${NC} ${BOLD}✓✓✓ ALL VERIFICATIONS PASSED ✓✓✓${NC}"
echo -e "${GREEN}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}"
echo -e "\n${GREEN}Congratulations! Your Lab 03 infrastructure is correctly deployed.${NC}"
echo -e "\nYou have successfully:"
echo -e " • Configured resource limits (EC2 instance types)"
echo -e " • Implemented healthchecks (ELB health checks)"
echo -e " • Verified resource enforcement with docker stats"
echo -e "\nYou can now proceed with the tutorials!"
exit 0
else
echo -e "\n${RED}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${RED}${BOLD}${NC} ${BOLD}✗✗✗ SOME VERIFICATIONS FAILED ✗✗✗${NC}"
echo -e "${RED}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}"
echo -e "\n${RED}Please review the failures above.${NC}"
echo -e "\nCommon issues:"
echo -e " • Missing resource limits → Add deploy.resources.limits to services"
echo -e " • Missing healthchecks → Add healthcheck section to services"
echo -e " • Services not starting → Check logs with 'docker compose logs'"
exit 1
fi

View File

@@ -0,0 +1,79 @@
#!/bin/bash
# Quick Test - Lab 03: Compute & EC2
# Fast validation for development (< 30 seconds)
# Usage: bash labs/lab-03-compute/tests/quick-test.sh
set -euo pipefail
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
BOLD='\033[1m'
NC='\033[0m'
# Get script directory
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
# Quick checks (no verbose output)
echo -e "${BLUE}Quick Test - Lab 03: Compute & EC2${NC}\n"
# Check 1: File exists
if [[ ! -f "$LAB_DIR/docker-compose.yml" ]]; then
echo -e "${RED}✗ docker-compose.yml not found${NC}"
exit 1
fi
echo -e "${GREEN}${NC} docker-compose.yml exists"
# Check 2: Syntax valid
if ! docker compose config &> /dev/null; then
echo -e "${RED}✗ docker-compose.yml syntax error${NC}"
exit 1
fi
echo -e "${GREEN}${NC} Syntax valid"
# Check 3: Services defined
SERVICES=$(docker compose config --services 2>/dev/null)
SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
if [[ $SERVICE_COUNT -lt 3 ]]; then
echo -e "${RED}✗ Only $SERVICE_COUNT services (need 3+)${NC}"
exit 1
fi
echo -e "${GREEN}${NC} $SERVICE_COUNT services defined"
# Check 4: Resource limits (INF-03)
NON_COMPLIANT=0
for service in $SERVICES; do
has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0")
has_mem=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "memory:" || echo "0")
if [[ $has_cpu -eq 0 || $has_mem -eq 0 ]]; then
((NON_COMPLIANT++)) || true
fi
done
if [[ $NON_COMPLIANT -gt 0 ]]; then
echo -e "${RED}$NON_COMPLIANT services missing resource limits (INF-03)${NC}"
exit 1
fi
echo -e "${GREEN}${NC} INF-03 compliant (all services have limits)"
# Check 5: Healthchecks
MISSING_HC=0
for service in $SERVICES; do
has_hc=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
if [[ $has_hc -eq 0 ]]; then
((MISSING_HC++)) || true
fi
done
if [[ $MISSING_HC -gt 0 ]]; then
echo -e "${YELLOW}$MISSING_HC services missing healthchecks${NC}"
else
echo -e "${GREEN}${NC} All services have healthchecks"
fi
# Summary
echo -e "\n${GREEN}${BOLD}✓ Quick test PASSED${NC}\n"
echo -e "For full verification, run:"
echo -e " bash tests/run-all-tests.sh"
echo -e " bash tests/99-final-verification.sh"

View File

@@ -0,0 +1,138 @@
#!/bin/bash
# Run All Tests - Lab 03: Compute & EC2
# Executes all test scripts in sequence with fail-fast behavior
# Usage: bash labs/lab-03-compute/tests/run-all-tests.sh
set -euo pipefail
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
BOLD='\033[1m'
NC='\033[0m'
# Get script directory
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
# Counter helpers
total_passed=0
total_failed=0
tests_run=0
# Helper functions
print_header() {
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}${NC} ${BOLD}$1${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
}
print_section() {
echo -e "\n${BLUE}═══════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE} $1${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}\n"
}
print_pass() {
echo -e "${GREEN}${NC} $1"
}
print_fail() {
echo -e "${RED}${NC} $1"
}
# Main execution
print_header "Lab 03 Test Suite - Compute & EC2"
cd "$LAB_DIR"
echo -e "${BLUE}Running all tests in sequence...${NC}\n"
# Test 1: Resource Limits Configuration
print_section "Test 1/4: Resource Limits Configuration"
if bash "$TEST_DIR/01-resource-limits-test.sh"; then
print_pass "Resource limits tests PASSED"
((total_passed++)) || true
else
print_fail "Resource limits tests FAILED"
((total_failed++)) || true
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
echo -e "Run individual tests to debug:"
echo -e " bash tests/01-resource-limits-test.sh"
exit 1
fi
((tests_run++)) || true
# Test 2: Healthcheck Configuration
print_section "Test 2/4: Healthcheck Configuration"
if bash "$TEST_DIR/02-healthcheck-test.sh"; then
print_pass "Healthcheck tests PASSED"
((total_passed++)) || true
else
print_fail "Healthcheck tests FAILED"
((total_failed++)) || true
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
echo -e "Run individual tests to debug:"
echo -e " bash tests/02-healthcheck-test.sh"
exit 1
fi
((tests_run++)) || true
# Test 3: Resource Enforcement
print_section "Test 3/4: Resource Enforcement Verification"
if bash "$TEST_DIR/03-enforcement-test.sh"; then
print_pass "Resource enforcement tests PASSED"
((total_passed++)) || true
else
print_fail "Resource enforcement tests FAILED"
((total_failed++)) || true
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
echo -e "Run individual tests to debug:"
echo -e " bash tests/03-enforcement-test.sh"
exit 1
fi
((tests_run++)) || true
# Test 4: Final Verification
print_section "Test 4/4: Final Verification"
if bash "$TEST_DIR/99-final-verification.sh"; then
print_pass "Final verification PASSED"
((total_passed++)) || true
else
print_fail "Final verification FAILED"
((total_failed++)) || true
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
echo -e "Run individual tests to debug:"
echo -e " bash tests/99-final-verification.sh"
exit 1
fi
((tests_run++)) || true
# Summary
print_header "Test Suite Summary"
echo -e "Tests run: $tests_run"
echo -e "${GREEN}Passed: $total_passed${NC}"
if [[ $total_failed -gt 0 ]]; then
echo -e "${RED}Failed: $total_failed${NC}"
fi
if [[ $total_failed -eq 0 ]]; then
echo -e "\n${GREEN}${BOLD}✓✓✓ ALL TESTS PASSED ✓✓✓${NC}\n"
echo -e "Your Lab 03 infrastructure is ready!"
echo -e "Proceed with the tutorials to learn more about:"
echo -e " • EC2 instance type parallels"
echo -e " • Resource limits enforcement"
echo -e " • Healthcheck implementation"
exit 0
else
echo -e "\n${RED}Some tests failed${NC}\n"
echo -e "Run individual tests for details:"
echo -e " bash tests/01-resource-limits-test.sh"
echo -e " bash tests/02-healthcheck-test.sh"
echo -e " bash tests/03-enforcement-test.sh"
echo -e " bash tests/99-final-verification.sh"
exit 1
fi

View 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**

View 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**

View 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