diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 79b903c..4d4a07c 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -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) --- diff --git a/.planning/phases/04-lab-03-compute-ec2/04-01-PLAN.md b/.planning/phases/04-lab-03-compute-ec2/04-01-PLAN.md new file mode 100644 index 0000000..68413c8 --- /dev/null +++ b/.planning/phases/04-lab-03-compute-ec2/04-01-PLAN.md @@ -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" +--- + + +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. + + + +@/home/luca/.claude/get-shit-done/workflows/execute-plan.md +@/home/luca/.claude/get-shit-done/templates/summary.md + + + +@.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 + + + +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 + diff --git a/.planning/phases/04-lab-03-compute-ec2/04-02-PLAN.md b/.planning/phases/04-lab-03-compute-ec2/04-02-PLAN.md new file mode 100644 index 0000000..3a6198b --- /dev/null +++ b/.planning/phases/04-lab-03-compute-ec2/04-02-PLAN.md @@ -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" +--- + + +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. + + + +@/home/luca/.claude/get-shit-done/workflows/execute-plan.md +@/home/luca/.claude/get-shit-done/templates/summary.md + + + +@.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) + + + +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 + diff --git a/.planning/phases/04-lab-03-compute-ec2/04-03-PLAN.md b/.planning/phases/04-lab-03-compute-ec2/04-03-PLAN.md new file mode 100644 index 0000000..360ce31 --- /dev/null +++ b/.planning/phases/04-lab-03-compute-ec2/04-03-PLAN.md @@ -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" +--- + + +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. + + + +@/home/luca/.claude/get-shit-done/workflows/execute-plan.md +@/home/luca/.claude/get-shit-done/templates/summary.md + + + +@.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) + + + +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 + diff --git a/.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md b/.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md new file mode 100644 index 0000000..c0d3a8b --- /dev/null +++ b/.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md @@ -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/) diff --git a/.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md b/.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md new file mode 100644 index 0000000..704b171 --- /dev/null +++ b/.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md @@ -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 diff --git a/labs/lab-03-compute/Dockerfile b/labs/lab-03-compute/Dockerfile new file mode 100644 index 0000000..ab2a406 --- /dev/null +++ b/labs/lab-03-compute/Dockerfile @@ -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"] diff --git a/labs/lab-03-compute/docker-compose.yml b/labs/lab-03-compute/docker-compose.yml new file mode 100644 index 0000000..500d24b --- /dev/null +++ b/labs/lab-03-compute/docker-compose.yml @@ -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 diff --git a/labs/lab-03-compute/explanation/compute-ec2-parallels.md b/labs/lab-03-compute/explanation/compute-ec2-parallels.md new file mode 100644 index 0000000..d5276e5 --- /dev/null +++ b/labs/lab-03-compute/explanation/compute-ec2-parallels.md @@ -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) diff --git a/labs/lab-03-compute/how-to-guides/check-resource-usage.md b/labs/lab-03-compute/how-to-guides/check-resource-usage.md new file mode 100644 index 0000000..7f81950 --- /dev/null +++ b/labs/lab-03-compute/how-to-guides/check-resource-usage.md @@ -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 diff --git a/labs/lab-03-compute/how-to-guides/custom-healthcheck.md b/labs/lab-03-compute/how-to-guides/custom-healthcheck.md new file mode 100644 index 0000000..e6eeaaa --- /dev/null +++ b/labs/lab-03-compute/how-to-guides/custom-healthcheck.md @@ -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 diff --git a/labs/lab-03-compute/how-to-guides/instance-type-mapping.md b/labs/lab-03-compute/how-to-guides/instance-type-mapping.md new file mode 100644 index 0000000..263608a --- /dev/null +++ b/labs/lab-03-compute/how-to-guides/instance-type-mapping.md @@ -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 diff --git a/labs/lab-03-compute/how-to-guides/test-limits-enforcement.md b/labs/lab-03-compute/how-to-guides/test-limits-enforcement.md new file mode 100644 index 0000000..00343af --- /dev/null +++ b/labs/lab-03-compute/how-to-guides/test-limits-enforcement.md @@ -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 diff --git a/labs/lab-03-compute/reference/compose-resources-syntax.md b/labs/lab-03-compute/reference/compose-resources-syntax.md new file mode 100644 index 0000000..136465f --- /dev/null +++ b/labs/lab-03-compute/reference/compose-resources-syntax.md @@ -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 diff --git a/labs/lab-03-compute/reference/ec2-instance-mapping.md b/labs/lab-03-compute/reference/ec2-instance-mapping.md new file mode 100644 index 0000000..607cc38 --- /dev/null +++ b/labs/lab-03-compute/reference/ec2-instance-mapping.md @@ -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'`
`memory: 512M` | t2.nano | 0.5 | 512 MB | $0.006 | Microservizi minimi | +| `cpus: '1'`
`memory: 1G` | t2.micro | 1 | 1 GB | $0.012 | Dev/Test | +| `cpus: '1'`
`memory: 2G` | t2.small | 1 | 2 GB | $0.024 | Web server | +| `cpus: '2'`
`memory: 4G` | t2.medium | 2 | 4 GB | $0.048 | Application | +| `cpus: '2'`
`memory: 8G` | m5.large | 2 | 8 GB | $0.096 | Production | +| `cpus: '4'`
`memory: 16G` | m5.xlarge | 4 | 16 GB | $0.192 | High traffic | +| `cpus: '8'`
`memory: 32G` | m5.2xlarge | 8 | 32 GB | $0.384 | Scalabile | +| `cpus: '16'`
`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 diff --git a/labs/lab-03-compute/reference/healthcheck-syntax.md b/labs/lab-03-compute/reference/healthcheck-syntax.md new file mode 100644 index 0000000..37e67d9 --- /dev/null +++ b/labs/lab-03-compute/reference/healthcheck-syntax.md @@ -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 diff --git a/labs/lab-03-compute/tests/01-resource-limits-test.sh b/labs/lab-03-compute/tests/01-resource-limits-test.sh new file mode 100755 index 0000000..1558c63 --- /dev/null +++ b/labs/lab-03-compute/tests/01-resource-limits-test.sh @@ -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 diff --git a/labs/lab-03-compute/tests/02-healthcheck-test.sh b/labs/lab-03-compute/tests/02-healthcheck-test.sh new file mode 100755 index 0000000..255c3aa --- /dev/null +++ b/labs/lab-03-compute/tests/02-healthcheck-test.sh @@ -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 diff --git a/labs/lab-03-compute/tests/03-enforcement-test.sh b/labs/lab-03-compute/tests/03-enforcement-test.sh new file mode 100755 index 0000000..74ac747 --- /dev/null +++ b/labs/lab-03-compute/tests/03-enforcement-test.sh @@ -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 diff --git a/labs/lab-03-compute/tests/04-verify-infrastructure.sh b/labs/lab-03-compute/tests/04-verify-infrastructure.sh new file mode 100755 index 0000000..baaa8bb --- /dev/null +++ b/labs/lab-03-compute/tests/04-verify-infrastructure.sh @@ -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; } diff --git a/labs/lab-03-compute/tests/99-final-verification.sh b/labs/lab-03-compute/tests/99-final-verification.sh new file mode 100755 index 0000000..d9deba7 --- /dev/null +++ b/labs/lab-03-compute/tests/99-final-verification.sh @@ -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 diff --git a/labs/lab-03-compute/tests/quick-test.sh b/labs/lab-03-compute/tests/quick-test.sh new file mode 100755 index 0000000..ab22d32 --- /dev/null +++ b/labs/lab-03-compute/tests/quick-test.sh @@ -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" diff --git a/labs/lab-03-compute/tests/run-all-tests.sh b/labs/lab-03-compute/tests/run-all-tests.sh new file mode 100755 index 0000000..efb86ab --- /dev/null +++ b/labs/lab-03-compute/tests/run-all-tests.sh @@ -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 diff --git a/labs/lab-03-compute/tutorial/01-set-resource-limits.md b/labs/lab-03-compute/tutorial/01-set-resource-limits.md new file mode 100644 index 0000000..f490f30 --- /dev/null +++ b/labs/lab-03-compute/tutorial/01-set-resource-limits.md @@ -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'`
`memory: 512M` | t2.nano | 0.5 | 512 MB | Microservizi minimi | +| `cpus: '1'`
`memory: 1G` | t2.micro | 1 | 1 GB | Dev/Test | +| `cpus: '1'`
`memory: 2G` | t2.small | 1 | 2 GB | Web servers | +| `cpus: '2'`
`memory: 4G` | t2.medium | 2 | 4 GB | Application | +| `cpus: '2'`
`memory: 8G` | m5.large | 2 | 8 GB | Production | +| `cpus: '4'`
`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** → diff --git a/labs/lab-03-compute/tutorial/02-implement-healthchecks.md b/labs/lab-03-compute/tutorial/02-implement-healthchecks.md new file mode 100644 index 0000000..af4cb92 --- /dev/null +++ b/labs/lab-03-compute/tutorial/02-implement-healthchecks.md @@ -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** → diff --git a/labs/lab-03-compute/tutorial/03-dependencies-with-health.md b/labs/lab-03-compute/tutorial/03-dependencies-with-health.md new file mode 100644 index 0000000..7c99a3c --- /dev/null +++ b/labs/lab-03-compute/tutorial/03-dependencies-with-health.md @@ -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