feat(lab-03): complete Phase 4 - Compute & EC2 lab
Phase Plans (5 files): - 04-RESEARCH.md: Domain research on Docker limits, healthchecks, EC2 parallels - 04-VALIDATION.md: Success criteria and validation strategy - 04-01-PLAN.md: Test infrastructure (RED phase) - 04-02-PLAN.md: Diátxis documentation - 04-03-PLAN.md: Infrastructure implementation (GREEN phase) Test Scripts (6 files, 1300+ lines): - 01-resource-limits-test.sh: Validate INF-03 compliance - 02-healthcheck-test.sh: Validate healthcheck configuration - 03-enforcement-test.sh: Verify resource limits with docker stats - 04-verify-infrastructure.sh: Infrastructure verification - 99-final-verification.sh: End-to-end student verification - run-all-tests.sh: Test orchestration with fail-fast - quick-test.sh: Fast validation (<30s) Documentation (11 files, 2500+ lines): Tutorials (3): - 01-set-resource-limits.md: EC2 instance types, Docker limits syntax - 02-implement-healthchecks.md: ELB health check parallels - 03-dependencies-with-health.md: depends_on with service_healthy How-to Guides (4): - check-resource-usage.md: docker stats monitoring - test-limits-enforcement.md: Stress testing CPU/memory - custom-healthcheck.md: HTTP, TCP, database healthchecks - instance-type-mapping.md: Docker limits → EC2 mapping Reference (3): - compose-resources-syntax.md: Complete deploy.resources reference - healthcheck-syntax.md: All healthcheck parameters - ec2-instance-mapping.md: Instance type mapping table Explanation (1): - compute-ec2-parallels.md: Container=EC2, Limits=Instance Type, Healthcheck=ELB Infrastructure: - docker-compose.yml: 5 services (web, app, worker, db, stress-test) All services: INF-03 compliant (cpus + memory limits) All services: healthcheck configured EC2 parallels: t2.nano, t2.micro, t2.small, t2.medium, m5.large - Dockerfile: Alpine 3.19 + stress tools + non-root user Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
---
|
||||
|
||||
|
||||
228
.planning/phases/04-lab-03-compute-ec2/04-01-PLAN.md
Normal file
228
.planning/phases/04-lab-03-compute-ec2/04-01-PLAN.md
Normal file
@@ -0,0 +1,228 @@
|
||||
---
|
||||
phase: 04-lab-03-compute-ec2
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 0
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- labs/lab-03-compute/tests/01-resource-limits-test.sh
|
||||
- labs/lab-03-compute/tests/02-healthcheck-test.sh
|
||||
- labs/lab-03-compute/tests/03-enforcement-test.sh
|
||||
- labs/lab-03-compute/tests/99-final-verification.sh
|
||||
- labs/lab-03-compute/tests/run-all-tests.sh
|
||||
- labs/lab-03-compute/tests/quick-test.sh
|
||||
autonomous: true
|
||||
requirements:
|
||||
- TEST-01
|
||||
- TEST-05
|
||||
- INF-03
|
||||
- LAB-03
|
||||
user_setup: []
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "Test scripts exist and validate resource limits before implementation"
|
||||
- "Tests verify INF-03 compliance (all containers have CPU/memory limits)"
|
||||
- "Tests verify healthcheck implementation"
|
||||
- "Tests can enforce resource limits and verify with docker stats"
|
||||
- "Final verification script provides clear pass/fail report"
|
||||
artifacts:
|
||||
- path: "labs/lab-03-compute/tests/01-resource-limits-test.sh"
|
||||
provides: "Resource limits validation"
|
||||
min_lines: 80
|
||||
- path: "labs/lab-03-compute/tests/02-healthcheck-test.sh"
|
||||
provides: "Healthcheck testing"
|
||||
min_lines: 100
|
||||
- path: "labs/lab-03-compute/tests/03-enforcement-test.sh"
|
||||
provides: "Resource enforcement verification"
|
||||
min_lines: 120
|
||||
- path: "labs/lab-03-compute/tests/99-final-verification.sh"
|
||||
provides: "Student double-check command"
|
||||
min_lines: 100
|
||||
- path: "labs/lab-03-compute/tests/run-all-tests.sh"
|
||||
provides: "Test orchestration with fail-fast"
|
||||
min_lines: 50
|
||||
- path: "labs/lab-03-compute/tests/quick-test.sh"
|
||||
provides: "Quick validation for development"
|
||||
min_lines: 30
|
||||
key_links:
|
||||
- from: "tests/01-resource-limits-test.sh"
|
||||
to: "docker-compose.yml resources"
|
||||
via: "yq or grep for deploy.resources.limits"
|
||||
pattern: "deploy:.*resources:.*limits"
|
||||
- from: "tests/02-healthcheck-test.sh"
|
||||
to: "docker-compose.yml healthcheck"
|
||||
via: "grep for healthcheck section"
|
||||
pattern: "healthcheck:"
|
||||
- from: "tests/03-enforcement-test.sh"
|
||||
to: "docker stats"
|
||||
via: "docker stats --no-stream for verification"
|
||||
pattern: "docker.*stats"
|
||||
- from: "tests/99-final-verification.sh"
|
||||
to: "INF-03 requirement"
|
||||
via: "Verify all services have cpu and memory limits"
|
||||
pattern: "INF-03"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Create comprehensive test infrastructure for Lab 03 (Compute & EC2) following TDD RED phase methodology. Tests validate Docker Compose resource limits (CPU/memory), healthcheck implementation, and INF-03 compliance (all containers must have resource limits).
|
||||
|
||||
Purpose: Establish verification foundation before implementing compute infrastructure. Tests fail initially (RED phase) and pass after implementation (GREEN phase in Plan 04-03).
|
||||
|
||||
Output: 6 bash test scripts covering resource limits validation, healthcheck testing, enforcement verification, and final verification for students.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/home/luca/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/REQUIREMENTS.md
|
||||
@.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
|
||||
@.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
|
||||
@.planning/phases/03-lab-02-network-vpc/03-01-PLAN.md
|
||||
|
||||
# Test Patterns from Phase 2 and 3
|
||||
|
||||
From labs/lab-01-iam/tests/run-all-tests.sh:
|
||||
- Use `set -euo pipefail` for error handling
|
||||
- Color-coded output (GREEN for pass, RED for fail, BLUE for info)
|
||||
- Summary with pass/fail counts
|
||||
- Exit code 0 for all pass, 1 for any failure
|
||||
|
||||
From labs/lab-02-network/tests/04-verify-infrastructure.sh:
|
||||
- Parse docker-compose.yml with `docker compose config`
|
||||
- Use jq for JSON parsing when needed
|
||||
- Verify compliance with grep patterns
|
||||
- Use awk for robust counting
|
||||
|
||||
# Key Docker Commands for Testing
|
||||
|
||||
## Resource Limits Verification
|
||||
```bash
|
||||
# Check if service has CPU limit
|
||||
docker compose config | grep -A 10 "service_name:" | grep -c "cpus:"
|
||||
|
||||
# Check if service has memory limit
|
||||
docker compose config | grep -A 10 "service_name:" | grep -c "memory:"
|
||||
|
||||
# Get container limits
|
||||
docker inspect container_name --format '{{.HostConfig.NanoCpus}}'
|
||||
docker inspect container_name --format '{{.HostConfig.Memory}}'
|
||||
```
|
||||
|
||||
## Healthcheck Verification
|
||||
```bash
|
||||
# Check if service has healthcheck
|
||||
docker compose config | grep -A 20 "service_name:" | grep -c "healthcheck:"
|
||||
|
||||
# Get container health status
|
||||
docker inspect container_name --format '{{.State.Health.Status}}'
|
||||
```
|
||||
|
||||
## Resource Monitoring
|
||||
```bash
|
||||
# Get live stats
|
||||
docker stats --no-stream
|
||||
|
||||
# Get specific container stats
|
||||
docker stats container_name --no-stream --format "{{.CPUPerc}}\t{{.MemUsage}}"
|
||||
```
|
||||
|
||||
# Testing Strategy
|
||||
|
||||
## Test 1: Resource Limits Configuration
|
||||
**Purpose:** Verify docker-compose.yml has resource limits defined
|
||||
**Expected Result:** FAIL initially (no limits configured)
|
||||
**Pass Criteria After Implementation:** All services have cpus and memory limits
|
||||
|
||||
## Test 2: Healthcheck Configuration
|
||||
**Purpose:** Verify services have healthchecks defined
|
||||
**Expected Result:** FAIL initially (no healthchecks configured)
|
||||
**Pass Criteria After Implementation:** All services have valid healthchecks
|
||||
|
||||
## Test 3: Resource Enforcement
|
||||
**Purpose:** Deploy test container and verify limits are enforced
|
||||
**Expected Result:** FAIL initially (no test container defined)
|
||||
**Pass Criteria After Implementation:** docker stats shows enforcement
|
||||
|
||||
## Test 4: INF-03 Compliance
|
||||
**Purpose:** Verify mandatory resource limits for all services
|
||||
**Expected Result:** FAIL initially
|
||||
**Pass Criteria After Implementation:** 100% compliance
|
||||
|
||||
# Implementation Notes
|
||||
|
||||
1. **Directory Structure:**
|
||||
```
|
||||
labs/lab-03-compute/
|
||||
├── tests/
|
||||
│ ├── 01-resource-limits-test.sh
|
||||
│ ├── 02-healthcheck-test.sh
|
||||
│ ├── 03-enforcement-test.sh
|
||||
│ ├── 99-final-verification.sh
|
||||
│ ├── run-all-tests.sh
|
||||
│ └── quick-test.sh
|
||||
├── docker-compose.yml (created in 04-03)
|
||||
└── README.md
|
||||
```
|
||||
|
||||
2. **Test Execution Order:**
|
||||
- 01-resource-limits-test.sh: Parse compose file for limits
|
||||
- 02-healthcheck-test.sh: Parse compose file for healthchecks
|
||||
- 03-enforcement-test.sh: Deploy test and verify enforcement
|
||||
- 99-final-verification.sh: End-to-end student verification
|
||||
|
||||
3. **Error Handling:**
|
||||
- Each test should be independent
|
||||
- Use descriptive error messages
|
||||
- Provide remediation hints
|
||||
|
||||
4. **Color Coding:**
|
||||
```bash
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
```
|
||||
|
||||
# Success Criteria
|
||||
|
||||
Plan 04-01 is complete when:
|
||||
1. All 6 test scripts created
|
||||
2. Each script meets minimum line requirements
|
||||
3. Tests fail when executed on empty/non-existent lab-03-compute
|
||||
4. run-all-tests.sh executes all tests in sequence
|
||||
5. Tests cover: resource limits, healthchecks, enforcement, INF-03
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
1. Create labs/lab-03-compute/tests/ directory
|
||||
2. Create 01-resource-limits-test.sh (80+ lines)
|
||||
- Parse docker-compose.yml for deploy.resources.limits
|
||||
- Verify cpus and memory for all services
|
||||
- Report missing limits
|
||||
3. Create 02-healthcheck-test.sh (100+ lines)
|
||||
- Parse docker-compose.yml for healthcheck sections
|
||||
- Verify test, interval, timeout, retries
|
||||
- Report missing healthchecks
|
||||
4. Create 03-enforcement-test.sh (120+ lines)
|
||||
- Deploy test container with stress image
|
||||
- Verify CPU limit enforcement with docker stats
|
||||
- Verify memory OOM on exceed
|
||||
- Cleanup test container
|
||||
5. Create 99-final-verification.sh (100+ lines)
|
||||
- Combine all checks into student-facing verification
|
||||
- INF-03 compliance report
|
||||
- Healthcheck status report
|
||||
- Clear pass/fail summary
|
||||
6. Create run-all-tests.sh (50+ lines)
|
||||
- Execute all test scripts in sequence
|
||||
- Fail-fast on first failure
|
||||
- Summary report
|
||||
7. Create quick-test.sh (30+ lines)
|
||||
- Fast validation (< 30 seconds)
|
||||
- Essential checks only
|
||||
</tasks>
|
||||
342
.planning/phases/04-lab-03-compute-ec2/04-02-PLAN.md
Normal file
342
.planning/phases/04-lab-03-compute-ec2/04-02-PLAN.md
Normal file
@@ -0,0 +1,342 @@
|
||||
---
|
||||
phase: 04-lab-03-compute-ec2
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on:
|
||||
- "04-01"
|
||||
files_modified:
|
||||
- labs/lab-03-compute/tutorial/01-set-resource-limits.md
|
||||
- labs/lab-03-compute/tutorial/02-implement-healthchecks.md
|
||||
- labs/lab-03-compute/tutorial/03-dependencies-with-health.md
|
||||
- labs/lab-03-compute/how-to-guides/check-resource-usage.md
|
||||
- labs/lab-03-compute/how-to-guides/test-limits-enforcement.md
|
||||
- labs/lab-03-compute/how-to-guides/custom-healthcheck.md
|
||||
- labs/lab-03-compute/how-to-guides/instance-type-mapping.md
|
||||
- labs/lab-03-compute/reference/compose-resources-syntax.md
|
||||
- labs/lab-03-compute/reference/healthcheck-syntax.md
|
||||
- labs/lab-03-compute/reference/ec2-instance-mapping.md
|
||||
- labs/lab-03-compute/explanation/compute-ec2-parallels.md
|
||||
autonomous: true
|
||||
requirements:
|
||||
- DOCT-01
|
||||
- DOCT-02
|
||||
- DOCT-03
|
||||
- DOCT-04
|
||||
- DOCT-05
|
||||
- LAB-03
|
||||
- PARA-01
|
||||
- PARA-03
|
||||
- PARA-04
|
||||
user_setup: []
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "All 4 Diátxis document types created (Tutorial, How-to, Reference, Explanation)"
|
||||
- "Tutorials follow 'little often' principle with incremental steps"
|
||||
- "How-to guides are task-focused and procedure-oriented"
|
||||
- "Reference documents provide complete technical specifications"
|
||||
- "Explanation document clearly maps Docker compute to EC2 concepts"
|
||||
- "Minimum line requirements met for all documents"
|
||||
artifacts:
|
||||
- path: "labs/lab-03-compute/tutorial/01-set-resource-limits.md"
|
||||
provides: "Step-by-step resource limits guide"
|
||||
min_lines: 250
|
||||
- path: "labs/lab-03-compute/tutorial/02-implement-healthchecks.md"
|
||||
provides: "Step-by-step healthcheck guide"
|
||||
min_lines: 250
|
||||
- path: "labs/lab-03-compute/tutorial/03-dependencies-with-health.md"
|
||||
provides: "Step-by-step dependency guide"
|
||||
min_lines: 250
|
||||
- path: "labs/lab-03-compute/how-to-guides/check-resource-usage.md"
|
||||
provides: "Resource monitoring procedure"
|
||||
min_lines: 60
|
||||
- path: "labs/lab-03-compute/how-to-guides/test-limits-enforcement.md"
|
||||
provides: "Limits testing procedure"
|
||||
min_lines: 60
|
||||
- path: "labs/lab-03-compute/how-to-guides/custom-healthcheck.md"
|
||||
provides: "Custom healthcheck procedure"
|
||||
min_lines: 60
|
||||
- path: "labs/lab-03-compute/how-to-guides/instance-type-mapping.md"
|
||||
provides: "Instance type selection guide"
|
||||
min_lines: 60
|
||||
- path: "labs/lab-03-compute/reference/compose-resources-syntax.md"
|
||||
provides: "Complete resources syntax reference"
|
||||
min_lines: 120
|
||||
- path: "labs/lab-03-compute/reference/healthcheck-syntax.md"
|
||||
provides: "Complete healthcheck syntax reference"
|
||||
min_lines: 100
|
||||
- path: "labs/lab-03-compute/reference/ec2-instance-mapping.md"
|
||||
provides: "EC2 instance mapping reference"
|
||||
min_lines: 80
|
||||
- path: "labs/lab-03-compute/explanation/compute-ec2-parallels.md"
|
||||
provides: "Compute-to-EC2 conceptual mapping"
|
||||
min_lines: 280
|
||||
key_links:
|
||||
- from: "tutorial/01-set-resource-limits.md"
|
||||
to: "reference/compose-resources-syntax.md"
|
||||
via: "Tutorial links to syntax reference"
|
||||
pattern: "\\[.*Syntax.*\\].*compose-resources-syntax"
|
||||
- from: "tutorial/02-implement-healthchecks.md"
|
||||
to: "reference/healthcheck-syntax.md"
|
||||
via: "Tutorial links to healthcheck reference"
|
||||
pattern: "\\[.*Reference.*\\].*healthcheck-syntax"
|
||||
- from: "explanation/compute-ec2-parallels.md"
|
||||
to: "reference/ec2-instance-mapping.md"
|
||||
via: "Explanation links to instance mapping"
|
||||
pattern: "\\[.*Instance.*\\].*ec2-instance-mapping"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Create comprehensive Diátxis documentation for Lab 03 (Compute & EC2) covering resource limits, healthchecks, and EC2 instance type parallels.
|
||||
|
||||
Purpose: Provide students with 4-quadrant documentation following Diátaxis framework: Tutorials (step-by-step learning), How-to Guides (task-focused procedures), Reference (technical specifications), and Explanation (conceptual mapping to EC2).
|
||||
|
||||
Output: 11 markdown documents (3 tutorials, 4 how-to guides, 3 reference, 1 explanation) totaling 1800+ lines.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/home/luca/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/REQUIREMENTS.md
|
||||
@.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
|
||||
@.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
|
||||
@.planning/phases/03-lab-02-network-vpc/03-02-PLAN.md
|
||||
@labs/lab-02-network/explanation/docker-network-vpc-parallels.md
|
||||
|
||||
# Diátaxis Framework Overview
|
||||
|
||||
From PROJECT.md and Phase 2/3 patterns:
|
||||
|
||||
## 1. Tutorials - Learning-Oriented
|
||||
- **Purpose:** Guida passo-passo per studenti nuovi
|
||||
- **Tone:** Diretto, semplice, incrementale ("little often")
|
||||
- **Structure:**
|
||||
- Clear learning objectives
|
||||
- Prerequisites listed
|
||||
- Step-by-step instructions
|
||||
- Verification commands after each step
|
||||
- Troubleshooting tips
|
||||
|
||||
## 2. How-to Guides - Task-Oriented
|
||||
- **Purpose:** Procedure specifiche e task-focused
|
||||
- **Tone:** Pratico, diretto al punto
|
||||
- **Structure:**
|
||||
- Single specific task
|
||||
- Prerequisites brief
|
||||
- Step-by-step procedure
|
||||
- Expected results
|
||||
- Common issues
|
||||
|
||||
## 3. Reference - Information-Oriented
|
||||
- **Purpose:** Specifiche tecniche nude e crude
|
||||
- **Tone:** Formale, completo, conciso
|
||||
- **Structure:**
|
||||
- Complete parameter reference
|
||||
- All options documented
|
||||
- Examples for each option
|
||||
- No tutorial content
|
||||
|
||||
## 4. Explanation - Understanding-Oriented
|
||||
- **Purpose:** Parallelismi concettuali locale ↔ cloud
|
||||
- **Tone:** Educativo, comparativo
|
||||
- **Structure:**
|
||||
- Concept introduction
|
||||
- Side-by-side comparisons
|
||||
- Key differences highlighted
|
||||
- When to use what
|
||||
|
||||
# Lab 03 Content Guidelines
|
||||
|
||||
## EC2 Instance Type Parallels
|
||||
|
||||
**Core Concept:** Docker resource limits simulate EC2 instance types
|
||||
|
||||
| Docker | EC2 | Use Case |
|
||||
|--------|-----|----------|
|
||||
| cpus: '0.5', memory: 512M | t2.nano | Dev/test |
|
||||
| cpus: '1', memory: 1G | t2.micro | Small services |
|
||||
| cpus: '1', memory: 2G | t2.small | Web servers |
|
||||
| cpus: '2', memory: 4G | t2.medium | Application servers |
|
||||
| cpus: '2', memory: 8G | m5.large | Production apps |
|
||||
| cpus: '4', memory: 16G | m5.xlarge | High-traffic services |
|
||||
|
||||
**Key Teaching Points:**
|
||||
1. Resource limits = Cost control (pay for what you use)
|
||||
2. Different instance types for different workloads
|
||||
3. Burstable (t2/t3) vs. consistent performance (m5/c5)
|
||||
4. Right-sizing prevents over-provisioning
|
||||
|
||||
## Healthcheck Parallels
|
||||
|
||||
**Core Concept:** Docker healthchecks simulate ELB health checks
|
||||
|
||||
| Docker | AWS |
|
||||
|--------|-----|
|
||||
| healthcheck.test | ELB health check path |
|
||||
| healthcheck.interval | ELB interval (default 30s) |
|
||||
| healthcheck.timeout | ELB timeout (default 5s) |
|
||||
| healthcheck.retries | ELB unhealthy threshold |
|
||||
| healthcheck.start_period | ELB grace period |
|
||||
| docker ps --filter health=healthy | ELB target health |
|
||||
|
||||
**Key Teaching Points:**
|
||||
1. Healthchecks detect failing services
|
||||
2. Dependencies wait for healthy status
|
||||
3. Prevents cascading failures
|
||||
4. Enables zero-downtime deployments
|
||||
|
||||
# Documentation Structure
|
||||
|
||||
## Tutorial 1: Set Resource Limits
|
||||
**Learning Objectives:**
|
||||
- Understand EC2 instance types
|
||||
- Learn Docker Compose resource syntax
|
||||
- Set CPU and memory limits
|
||||
- Verify with docker stats
|
||||
|
||||
**Outline:**
|
||||
1. What are EC2 Instance Types? (5 min)
|
||||
2. Docker Resource Limits Syntax (10 min)
|
||||
3. Practice: Set limits for t2.micro (15 min)
|
||||
4. Practice: Set limits for t2.small (15 min)
|
||||
5. Verification with docker stats (5 min)
|
||||
|
||||
## Tutorial 2: Implement Healthchecks
|
||||
**Learning Objectives:**
|
||||
- Understand healthcheck purpose
|
||||
- Learn healthcheck syntax
|
||||
- Add healthcheck to web service
|
||||
- Monitor health status
|
||||
|
||||
**Outline:**
|
||||
1. What are Healthchecks? (5 min)
|
||||
2. ELB Health Check Parallel (5 min)
|
||||
3. Healthcheck Parameters (10 min)
|
||||
4. Practice: Add HTTP healthcheck (15 min)
|
||||
5. Practice: Add database healthcheck (10 min)
|
||||
6. Monitor health status (5 min)
|
||||
|
||||
## Tutorial 3: Dependencies with Health
|
||||
**Learning Objectives:**
|
||||
- Understand service dependencies
|
||||
- Use depends_on with conditions
|
||||
- Implement ordered startup
|
||||
- Verify dependency chain
|
||||
|
||||
**Outline:**
|
||||
1. Service Dependencies (5 min)
|
||||
2. depends_on Conditions (10 min)
|
||||
3. Practice: Web depends on App (15 min)
|
||||
4. Practice: App depends on DB (15 min)
|
||||
5. Verify startup order (5 min)
|
||||
|
||||
# Implementation Notes
|
||||
|
||||
1. **File Locations:**
|
||||
```
|
||||
labs/lab-03-compute/
|
||||
├── tutorial/
|
||||
│ ├── 01-set-resource-limits.md
|
||||
│ ├── 02-implement-healthchecks.md
|
||||
│ └── 03-dependencies-with-health.md
|
||||
├── how-to-guides/
|
||||
│ ├── check-resource-usage.md
|
||||
│ ├── test-limits-enforcement.md
|
||||
│ ├── custom-healthcheck.md
|
||||
│ └── instance-type-mapping.md
|
||||
├── reference/
|
||||
│ ├── compose-resources-syntax.md
|
||||
│ ├── healthcheck-syntax.md
|
||||
│ └── ec2-instance-mapping.md
|
||||
├── explanation/
|
||||
│ └── compute-ec2-parallels.md
|
||||
└── README.md
|
||||
```
|
||||
|
||||
2. **Code Examples:**
|
||||
- Use realistic examples (nginx, postgres, redis)
|
||||
- Show complete docker-compose.yml snippets
|
||||
- Include verification commands
|
||||
- Add expected output
|
||||
|
||||
3. **Parallelism Emphasis:**
|
||||
- Always show AWS equivalent
|
||||
- Explain "why" not just "how"
|
||||
- Highlight key differences
|
||||
- Link to AWS documentation
|
||||
|
||||
# Success Criteria
|
||||
|
||||
Plan 04-02 is complete when:
|
||||
1. All 11 documents created
|
||||
2. Minimum line requirements met
|
||||
3. Tutorials follow "little often" principle
|
||||
4. How-to guides are task-focused
|
||||
5. Reference documents are complete specifications
|
||||
6. Explanation clearly maps Docker → EC2
|
||||
7. Cross-references between documents
|
||||
8. Italian language (consistent with other labs)
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
1. Create tutorial/ directory
|
||||
2. Create tutorial/01-set-resource-limits.md (250+ lines)
|
||||
- EC2 instance types introduction
|
||||
- Docker resource limits syntax
|
||||
- Practice exercises
|
||||
- Verification steps
|
||||
3. Create tutorial/02-implement-healthchecks.md (250+ lines)
|
||||
- Healthcheck concept and ELB parallel
|
||||
- Healthcheck parameters
|
||||
- Practice: HTTP healthcheck
|
||||
- Practice: Database healthcheck
|
||||
4. Create tutorial/03-dependencies-with-health.md (250+ lines)
|
||||
- Service dependency concepts
|
||||
- depends_on conditions
|
||||
- Practice: Multi-tier dependencies
|
||||
- Startup order verification
|
||||
5. Create how-to-guides/ directory
|
||||
6. Create how-to-guides/check-resource-usage.md (60+ lines)
|
||||
- docker stats usage
|
||||
- Real-time monitoring
|
||||
- Interpreting output
|
||||
7. Create how-to-guides/test-limits-enforcement.md (60+ lines)
|
||||
- CPU limit testing
|
||||
- Memory OOM testing
|
||||
- Verification procedures
|
||||
8. Create how-to-guides/custom-healthcheck.md (60+ lines)
|
||||
- Writing custom healthcheck commands
|
||||
- Best practices
|
||||
- Debugging failures
|
||||
9. Create how-to-guides/instance-type-mapping.md (60+ lines)
|
||||
- Docker limits → EC2 mapping
|
||||
- Selecting appropriate types
|
||||
- Cost considerations
|
||||
10. Create reference/ directory
|
||||
11. Create reference/compose-resources-syntax.md (120+ lines)
|
||||
- Complete deploy.resources reference
|
||||
- CPU and memory syntax
|
||||
- Reservations vs limits
|
||||
- All options with examples
|
||||
12. Create reference/healthcheck-syntax.md (100+ lines)
|
||||
- All healthcheck parameters
|
||||
- Test command formats
|
||||
- Status values
|
||||
- Examples for each service type
|
||||
13. Create reference/ec2-instance-mapping.md (80+ lines)
|
||||
- Complete mapping table
|
||||
- Instance type descriptions
|
||||
- Use case recommendations
|
||||
14. Create explanation/ directory
|
||||
15. Create explanation/compute-ec2-parallels.md (280+ lines)
|
||||
- Container = EC2 Instance
|
||||
- Resource limits = Instance types
|
||||
- Healthcheck = ELB/Status checks
|
||||
- Docker stats = CloudWatch
|
||||
- Key differences (credits, pricing, etc.)
|
||||
- Command equivalents table
|
||||
</tasks>
|
||||
335
.planning/phases/04-lab-03-compute-ec2/04-03-PLAN.md
Normal file
335
.planning/phases/04-lab-03-compute-ec2/04-03-PLAN.md
Normal file
@@ -0,0 +1,335 @@
|
||||
---
|
||||
phase: 04-lab-03-compute-ec2
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on:
|
||||
- "04-01"
|
||||
- "04-02"
|
||||
files_modified:
|
||||
- labs/lab-03-compute/docker-compose.yml
|
||||
- labs/lab-03-compute/Dockerfile
|
||||
- labs/lab-03-compute/tests/04-verify-infrastructure.sh
|
||||
autonomous: true
|
||||
requirements:
|
||||
- LAB-03
|
||||
- INF-01
|
||||
- INF-03
|
||||
- PARA-01
|
||||
- PARA-02
|
||||
- TEST-01
|
||||
- TEST-05
|
||||
user_setup: []
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "docker-compose.yml exists and is valid (docker compose config passes)"
|
||||
- "All services have deploy.resources.limits.cpus set (INF-03)"
|
||||
- "All services have deploy.resources.limits.memory set (INF-03)"
|
||||
- "Services have appropriate healthchecks defined"
|
||||
- "depends_on uses condition: service_healthy where appropriate"
|
||||
- "Infrastructure verification passes all checks"
|
||||
- "Cloud nomenclature follows EC2 instance patterns (PARA-02)"
|
||||
artifacts:
|
||||
- path: "labs/lab-03-compute/docker-compose.yml"
|
||||
provides: "Compute infrastructure with limits and healthchecks"
|
||||
min_lines: 100
|
||||
- path: "labs/lab-03-compute/Dockerfile"
|
||||
provides: "Test container image with stress tools"
|
||||
min_lines: 25
|
||||
- path: "labs/lab-03-compute/tests/04-verify-infrastructure.sh"
|
||||
provides: "Infrastructure verification script"
|
||||
min_lines: 100
|
||||
key_links:
|
||||
- from: "docker-compose.yml"
|
||||
to: "tests/01-resource-limits-test.sh"
|
||||
via: "Tests validate deploy.resources.limits"
|
||||
pattern: "deploy:.*resources:.*limits"
|
||||
- from: "docker-compose.yml"
|
||||
to: "tests/02-healthcheck-test.sh"
|
||||
via: "Tests validate healthcheck sections"
|
||||
pattern: "healthcheck:"
|
||||
- from: "docker-compose.yml"
|
||||
to: "reference/compose-resources-syntax.md"
|
||||
via: "Reference documents all resource options"
|
||||
pattern: "deploy:.*resources"
|
||||
- from: "docker-compose.yml"
|
||||
to: "explanation/compute-ec2-parallels.md"
|
||||
via: "Instance types mapped to EC2"
|
||||
pattern: "# EC2|t2\\.micro|m5\\.large"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Implement compute infrastructure for Lab 03 (Compute & EC2) with Docker Compose resource limits and healthchecks. Create docker-compose.yml with services that have mandatory CPU/memory limits (INF-03 compliance) and healthchecks for readiness verification.
|
||||
|
||||
Purpose: GREEN phase implementation - make tests from Plan 04-01 pass by implementing compute infrastructure with proper resource limits and healthchecks.
|
||||
|
||||
Output: docker-compose.yml with 4+ services, Dockerfile for test container, and infrastructure verification script.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/home/luca/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/home/luca/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/REQUIREMENTS.md
|
||||
@.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
|
||||
@.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
|
||||
@.planning/phases/03-lab-02-network-vpc/03-03-PLAN.md
|
||||
@labs/lab-02-network/docker-compose.yml
|
||||
|
||||
# Infrastructure Requirements
|
||||
|
||||
## INF-03: Mandatory Resource Limits
|
||||
|
||||
**CRITICAL:** Every service MUST have:
|
||||
```yaml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: 'X' # REQUIRED
|
||||
memory: 'XG' # REQUIRED
|
||||
```
|
||||
|
||||
**NON-COMPLIANT:**
|
||||
```yaml
|
||||
# Missing limits - INF-03 VIOLATION
|
||||
services:
|
||||
app:
|
||||
image: nginx
|
||||
# No deploy section
|
||||
```
|
||||
|
||||
## Service Configuration
|
||||
|
||||
### Tier 1: Web Server (t2.micro parallel)
|
||||
```yaml
|
||||
web:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-web
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
```
|
||||
|
||||
### Tier 2: Application Server (t2.small parallel)
|
||||
```yaml
|
||||
app:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-app
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
depends_on:
|
||||
web:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
### Tier 3: Worker (t2.medium parallel)
|
||||
```yaml
|
||||
worker:
|
||||
image: alpine:3.19
|
||||
container_name: lab03-worker
|
||||
command: ["sh", "-c", "sleep 3600"]
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "exit 0"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
### Tier 4: Database (t2.medium parallel)
|
||||
```yaml
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: lab03-db
|
||||
environment:
|
||||
POSTGRES_DB: lab03_db
|
||||
POSTGRES_USER: lab03_user
|
||||
POSTGRES_PASSWORD: lab03_password
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U lab03_user -d lab03_db"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
```
|
||||
|
||||
### Test Container (for enforcement testing)
|
||||
```yaml
|
||||
stress-test:
|
||||
image: polinux/stress
|
||||
container_name: lab03-stress
|
||||
command: ["--cpu", "1", "--vm", "1", "--vm-bytes", "256M", "--timeout", "30s"]
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "exit 0"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
# Dockerfile for Test Container
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile for Lab 03 - Compute & EC2
|
||||
# Test container with stress testing tools
|
||||
|
||||
FROM alpine:3.19
|
||||
|
||||
# Create non-root user (INF-01 compliance)
|
||||
RUN addgroup -g 1000 appgroup && \
|
||||
adduser -D -u 1000 -G appgroup appuser
|
||||
|
||||
# Install stress testing tools
|
||||
RUN apk add --no-cache \
|
||||
stress \
|
||||
curl \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Switch to non-root user
|
||||
USER appuser
|
||||
|
||||
WORKDIR /home/appuser
|
||||
|
||||
# Default command - ready for stress testing
|
||||
CMD ["sh", "-c", "sleep 3600"]
|
||||
```
|
||||
|
||||
# Healthcheck Best Practices
|
||||
|
||||
## HTTP Service Healthcheck
|
||||
```yaml
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
|
||||
interval: 10s # Check every 10 seconds
|
||||
timeout: 5s # Fail after 5 seconds
|
||||
retries: 3 # Unhealthy after 3 failures
|
||||
start_period: 5s # Grace period on startup
|
||||
```
|
||||
|
||||
## Database Healthcheck
|
||||
```yaml
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5 # More retries for DB (slower startup)
|
||||
start_period: 10s # Longer grace period
|
||||
```
|
||||
|
||||
## Simple Healthcheck
|
||||
```yaml
|
||||
test: ["CMD-SHELL", "exit 0"]
|
||||
interval: 30s # Less frequent for simple checks
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
# Infrastructure Verification Script
|
||||
|
||||
Based on labs/lab-02-network/tests/04-verify-infrastructure.sh pattern:
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. **File Existence:** docker-compose.yml exists
|
||||
2. **Syntax Validation:** docker compose config passes
|
||||
3. **Resource Limits:** All services have cpus and memory limits
|
||||
4. **Healthchecks:** All services have healthcheck sections
|
||||
5. **INF-03 Compliance:** 100% of services have limits
|
||||
6. **Deploy Services:** docker compose up -d succeeds
|
||||
7. **Health Status:** Services become healthy
|
||||
8. **Resource Enforcement:** docker stats shows limits
|
||||
9. **Dependency Order:** Services start in correct order
|
||||
10. **Final Report:** Pass/fail summary
|
||||
|
||||
# Cloud Nomenclature (PARA-02)
|
||||
|
||||
Service names should reflect EC2 instance parallels:
|
||||
- `web` → Web tier (t2.micro)
|
||||
- `app` → Application tier (t2.small)
|
||||
- `worker` → Background processing (t2.medium)
|
||||
- `db` → Database tier (t2.medium)
|
||||
|
||||
# Implementation Notes
|
||||
|
||||
1. **Version:** Use `version: "3.8"` for compatibility
|
||||
2. **Networks:** Can reuse networks from Lab 02 or create new
|
||||
3. **Volumes:** Use named volumes for database persistence
|
||||
4. **Security:** Follow INF-01 (no root), INF-02 (no 0.0.0.0 bindings)
|
||||
5. **Parallelism:** Comments should show EC2 equivalent
|
||||
|
||||
# Success Criteria
|
||||
|
||||
Plan 04-03 is complete when:
|
||||
1. docker-compose.yml created with 4+ services
|
||||
2. All services have resource limits (INF-03)
|
||||
3. All services have healthchecks
|
||||
4. docker compose config validates
|
||||
5. Services deploy and become healthy
|
||||
6. Infrastructure verification passes all checks
|
||||
7. Tests from 04-01 now pass (GREEN phase)
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
1. Create labs/lab-03-compute/ directory structure
|
||||
2. Create docker-compose.yml (100+ lines)
|
||||
- Service: web (nginx, t2.micro: 1 CPU, 1G RAM)
|
||||
- Service: app (nginx, t2.small: 1 CPU, 2G RAM)
|
||||
- Service: worker (alpine, t2.medium: 2 CPU, 4G RAM)
|
||||
- Service: db (postgres, t2.medium: 2 CPU, 4G RAM)
|
||||
- Service: stress-test (enforcement testing)
|
||||
- All services: deploy.resources.limits
|
||||
- All services: healthcheck sections
|
||||
- Proper depends_on with conditions
|
||||
- Named volumes for database
|
||||
3. Create Dockerfile (25+ lines)
|
||||
- Alpine 3.19 base
|
||||
- Non-root user (INF-01)
|
||||
- Install stress tools
|
||||
- Minimal and secure
|
||||
4. Create tests/04-verify-infrastructure.sh (100+ lines)
|
||||
- Verify docker-compose.yml exists
|
||||
- Validate syntax
|
||||
- Check INF-03 compliance
|
||||
- Verify healthchecks
|
||||
- Deploy and test services
|
||||
- Check resource enforcement
|
||||
- Final summary report
|
||||
5. Test infrastructure:
|
||||
- docker compose config
|
||||
- docker compose up -d
|
||||
- docker stats verification
|
||||
- health status check
|
||||
- docker compose down
|
||||
</tasks>
|
||||
209
.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
Normal file
209
.planning/phases/04-lab-03-compute-ec2/04-RESEARCH.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# Phase 4 Research - Lab 03: Compute & EC2
|
||||
|
||||
## Domain Research: Docker Resource Limits & Healthchecks
|
||||
|
||||
### 1. Docker Compose Resource Limits
|
||||
|
||||
**CPU Limits:**
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5' # 50% of 1 CPU core
|
||||
# OR
|
||||
cpus: '2' # 2 full CPU cores
|
||||
```
|
||||
|
||||
**Memory Limits:**
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M # 512 MB
|
||||
# OR
|
||||
memory: 2G # 2 GB
|
||||
```
|
||||
|
||||
**Non-Swap Memory:**
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
reservations:
|
||||
memory: 256M
|
||||
```
|
||||
|
||||
### 2. EC2 Instance Types Parallel
|
||||
|
||||
| Docker Limits | EC2 Equivalent | Instance Type |
|
||||
|---------------|----------------|---------------|
|
||||
| cpus: '0.5', memory: 512M | t2.nano (0.5 vCPU, 512MB) | Burstable |
|
||||
| cpus: '1', memory: 1G | t2.micro (1 vCPU, 1GB) | Burstable |
|
||||
| cpus: '1', memory: 2G | t2.small (1 vCPU, 2GB) | Burstable |
|
||||
| cpus: '2', memory: 4G | t2.medium (2 vCPU, 4GB) | Burstable |
|
||||
| cpus: '2', memory: 8G | m5.large (2 vCPU, 8GB) | General Purpose |
|
||||
| cpus: '4', memory: 16G | m5.xlarge (4 vCPU, 16GB) | General Purpose |
|
||||
|
||||
**Key Parallelism:**
|
||||
- Docker CPU fractions = AWS vCPUs
|
||||
- Docker memory limits = AWS instance memory
|
||||
- No swap enforcement = AWS EBS-optimized instances
|
||||
- Resource reservations = AWS instance type guarantees
|
||||
|
||||
### 3. Healthcheck Implementation
|
||||
|
||||
**Docker Compose Healthcheck:**
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
image: nginx:alpine
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"]
|
||||
interval: 10s # Check every 10 seconds
|
||||
timeout: 5s # Timeout after 5 seconds
|
||||
retries: 3 # Mark unhealthy after 3 failures
|
||||
start_period: 10s # Grace period on startup
|
||||
```
|
||||
|
||||
**Healthcheck with curl:**
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
```
|
||||
|
||||
**Database Healthcheck:**
|
||||
```yaml
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
```
|
||||
|
||||
**Application Healthcheck:**
|
||||
```yaml
|
||||
app:
|
||||
image: myapp:latest
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "node healthcheck.js || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
```
|
||||
|
||||
### 4. Service Dependencies with Health
|
||||
|
||||
**Wait for healthy service:**
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
```
|
||||
|
||||
**Lifecycle:**
|
||||
1. `service_started`: Container started (default)
|
||||
2. `service_healthy`: Healthcheck passing (requires healthcheck section)
|
||||
|
||||
### 5. Resource Monitoring
|
||||
|
||||
**docker stats:**
|
||||
```bash
|
||||
docker stats --no-stream # Single snapshot
|
||||
docker stats lab03-app --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"
|
||||
```
|
||||
|
||||
**Inspect limits:**
|
||||
```bash
|
||||
docker inspect lab03-app --format '{{.HostConfig.Memory}}' # Memory limit in bytes
|
||||
docker inspect lab03-app --format '{{.HostConfig.NanoCpus}}' # CPU quota (1e9 = 1 CPU)
|
||||
```
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Test Scenarios
|
||||
|
||||
1. **Resource Limit Enforcement:**
|
||||
- Deploy container with CPU limit (e.g., 0.5 CPU)
|
||||
- Run CPU-intensive task
|
||||
- Verify with `docker stats` that CPU usage doesn't exceed limit
|
||||
|
||||
2. **Memory Limit Enforcement:**
|
||||
- Deploy container with memory limit (e.g., 512M)
|
||||
- Run memory allocation task
|
||||
- Verify container is OOM killed when exceeding limit
|
||||
|
||||
3. **Healthcheck Validation:**
|
||||
- Deploy service with healthcheck
|
||||
- Verify status transitions: starting → healthy
|
||||
- Verify `depends_on: condition: service_healthy` waits
|
||||
|
||||
4. **Resource Verification:**
|
||||
- Parse docker-compose.yml for `deploy.resources.limits`
|
||||
- Verify all services have mandatory limits
|
||||
- Report missing limits
|
||||
|
||||
## Security Requirements
|
||||
|
||||
**INF-03: Mandatory Resource Limits**
|
||||
- Every container MUST have `cpus` limit specified
|
||||
- Every container MUST have `memory` limit specified
|
||||
- No container can run with unlimited resources (security risk)
|
||||
|
||||
**Safety First:**
|
||||
- Resource limits prevent DoS from runaway processes
|
||||
- Memory limits prevent host OOM
|
||||
- CPU limits ensure fair resource sharing
|
||||
|
||||
## Cloud Parallels
|
||||
|
||||
### Docker → AWS EC2
|
||||
|
||||
| Docker | AWS EC2 |
|
||||
|--------|---------|
|
||||
| `cpus: '0.5'` | 0.5 vCPU (t2.nano) |
|
||||
| `memory: 512M` | 512 MB RAM |
|
||||
| `healthcheck` | EC2 Status Checks + ELB Health |
|
||||
| `docker stats` | CloudWatch Metrics |
|
||||
| `OOM kill` | Instance termination (out of credit) |
|
||||
| `depends_on: healthy` | Auto Scaling Group health checks |
|
||||
|
||||
### Instance Type Selection
|
||||
|
||||
**Burstable Instances (t2/t3):**
|
||||
- Credit-based CPU
|
||||
- Good for dev/test
|
||||
- Docker: Small limits with occasional bursts
|
||||
|
||||
**General Purpose (m5):**
|
||||
- Balanced compute/memory
|
||||
- Docker: Medium limits (2-4 vCPU, 8-16 GB)
|
||||
|
||||
**Compute Optimized (c5):**
|
||||
- High CPU ratio
|
||||
- Docker: High CPU limits (4+ vCPU, lower memory)
|
||||
|
||||
## Sources
|
||||
|
||||
- [Docker Compose Resources](https://docs.docker.com/compose/compose-file/compose-file-v3/#resources)
|
||||
- [Docker Healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck)
|
||||
- [AWS EC2 Instance Types](https://aws.amazon.com/ec2/instance-types/)
|
||||
- [Docker Stats](https://docs.docker.com/engine/reference/commandline/stats/)
|
||||
247
.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
Normal file
247
.planning/phases/04-lab-03-compute-ec2/04-VALIDATION.md
Normal file
@@ -0,0 +1,247 @@
|
||||
# Phase 4 Validation - Lab 03: Compute & EC2
|
||||
|
||||
## Validation Strategy
|
||||
|
||||
### Goal-Backward Verification
|
||||
|
||||
We verify that Phase 4 achieves its goals by checking the success criteria from the Phase definition.
|
||||
|
||||
## Success Criteria (from ROADMAP)
|
||||
|
||||
1. **Studente può deploy container con limiti CPU/memoria obbligatori** (simulazione instance types)
|
||||
2. **Studente può implementare healthchecks per verificare che servizi siano "healthy"**
|
||||
3. **Tutti i container hanno `cpus` e `mem_limit` configurati** (enforcement risorse cloud)
|
||||
4. **Studente comprende il parallelismo tra container con limiti e EC2 instances**
|
||||
5. **Lab include test che verificano resource limits con `docker stats` e healthcheck readiness**
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
### 04-01: Test Infrastructure (RED Phase)
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] Test script `01-resource-limits-test.sh` validates:
|
||||
- Parse docker-compose.yml for `deploy.resources.limits.cpus`
|
||||
- Parse docker-compose.yml for `deploy.resources.limits.memory`
|
||||
- Report services without mandatory limits
|
||||
- Test should FAIL initially (no limits configured)
|
||||
- [ ] Test script `02-healthcheck-test.sh` validates:
|
||||
- Services have `healthcheck` section defined
|
||||
- Healthcheck command is valid
|
||||
- `interval`, `timeout`, `retries` configured
|
||||
- Test should FAIL initially (no healthchecks configured)
|
||||
- [ ] Test script `03-resource-enforcement-test.sh` validates:
|
||||
- Deploy test container with limits
|
||||
- Run CPU-intensive task
|
||||
- Verify `docker stats` shows enforcement
|
||||
- Run memory allocation task
|
||||
- Verify OOM kill on exceed
|
||||
- [ ] Final verification `99-final-verification.sh` validates:
|
||||
- All services have resource limits
|
||||
- All services have healthchecks
|
||||
- Healthchecks pass
|
||||
- Resource limits enforced
|
||||
- INF-03 compliance verified
|
||||
|
||||
**Verification Command:**
|
||||
```bash
|
||||
cd labs/lab-03-compute
|
||||
bash tests/run-all-tests.sh
|
||||
```
|
||||
|
||||
**Expected Result:** All tests FAIL initially (RED phase), then PASS after implementation (GREEN phase)
|
||||
|
||||
---
|
||||
|
||||
### 04-02: Diátxis Documentation
|
||||
|
||||
**Acceptance Criteria:**
|
||||
|
||||
**Tutorials (3):**
|
||||
- [ ] `tutorial/01-set-resource-limits.md` (min 250 lines)
|
||||
- Explain EC2 instance types concept
|
||||
- Show Docker CPU/memory limits syntax
|
||||
- Practice: Set limits for different instance types
|
||||
- Verify with `docker stats`
|
||||
- [ ] `tutorial/02-implement-healthchecks.md` (min 250 lines)
|
||||
- Explain healthcheck purpose (ELB parallel)
|
||||
- Show healthcheck syntax and parameters
|
||||
- Practice: Add healthcheck to web service
|
||||
- Verify with `docker ps` and health status
|
||||
- [ ] `tutorial/03-dependencies-with-health.md` (min 250 lines)
|
||||
- Explain service dependencies
|
||||
- Show `depends_on: condition: service_healthy`
|
||||
- Practice: Multi-tier with health-based startup
|
||||
- Verify startup order
|
||||
|
||||
**How-to Guides (4):**
|
||||
- [ ] `how-to-guides/check-resource-usage.md` (min 60 lines)
|
||||
- How to use `docker stats`
|
||||
- How to inspect limits
|
||||
- How to monitor in real-time
|
||||
- [ ] `how-to-guides/test-limits-enforcement.md` (min 60 lines)
|
||||
- How to trigger CPU limit
|
||||
- How to trigger memory OOM
|
||||
- How to verify enforcement
|
||||
- [ ] `how-to-guides/custom-healthcheck.md` (min 60 lines)
|
||||
- How to write custom healthcheck
|
||||
- Healthcheck best practices
|
||||
- Debugging healthcheck failures
|
||||
- [ ] `how-to-guides/instance-type-mapping.md` (min 60 lines)
|
||||
- Docker limits → EC2 instance mapping
|
||||
- Selecting appropriate instance type
|
||||
- Cost optimization parallels
|
||||
|
||||
**Reference (3):**
|
||||
- [ ] `reference/compose-resources-syntax.md` (min 120 lines)
|
||||
- Complete `deploy.resources` reference
|
||||
- CPU and memory limit syntax
|
||||
- Reservations vs limits
|
||||
- [ ] `reference/healthcheck-syntax.md` (min 100 lines)
|
||||
- Healthcheck parameters
|
||||
- Test command formats
|
||||
- Status values and transitions
|
||||
- [ ] `reference/ec2-instance-mapping.md` (min 80 lines)
|
||||
- Docker limits → EC2 instance table
|
||||
- Parallelism documentation
|
||||
- Command equivalents
|
||||
|
||||
**Explanation (1):**
|
||||
- [ ] `explanation/compute-ec2-parallels.md` (min 280 lines)
|
||||
- Container = EC2 Instance
|
||||
- Resource limits = Instance types
|
||||
- Healthcheck = ELB/Status checks
|
||||
- Docker stats = CloudWatch
|
||||
- Differences (credits, pricing, etc.)
|
||||
|
||||
**Verification:**
|
||||
```bash
|
||||
find labs/lab-03-compute -name "*.md" | wc -l # Expect: 11
|
||||
wc -l labs/lab-03-compute/tutorial/*.md # Expect: 750+
|
||||
wc -l labs/lab-03-compute/how-to-guides/*.md # Expect: 240+
|
||||
wc -l labs/lab-03-compute/reference/*.md # Expect: 300+
|
||||
wc -l labs/lab-03-compute/explanation/*.md # Expect: 280+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 04-03: Infrastructure Implementation (GREEN Phase)
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- [ ] `docker-compose.yml` exists and is valid
|
||||
- [ ] All services have `deploy.resources.limits.cpus` set
|
||||
- [ ] All services have `deploy.resources.limits.memory` set
|
||||
- [ ] Services have appropriate healthchecks defined
|
||||
- [ ] Healthcheck parameters are reasonable (interval, timeout, retries)
|
||||
- [ ] `depends_on` uses `condition: service_healthy` where appropriate
|
||||
- [ ] INF-03 compliance: NO service without resource limits
|
||||
- [ ] Cloud nomenclature: Service names follow EC2 instance patterns
|
||||
- [ ] Infrastructure verification passes all checks
|
||||
|
||||
**INF-03 Compliance Check:**
|
||||
```bash
|
||||
grep -c "deploy:" labs/lab-03-compute/docker-compose.yml # Should equal service count
|
||||
grep -c "cpus:" labs/lab-03-compute/docker-compose.yml # Should equal service count
|
||||
grep -c "memory:" labs/lab-03-compute/docker-compose.yml # Should equal service count
|
||||
```
|
||||
|
||||
**Services Configuration:**
|
||||
|
||||
| Service | Instance Type Parallel | CPUs | Memory | Healthcheck |
|
||||
|---------|----------------------|------|--------|-------------|
|
||||
| web | t2.micro | 1 | 1G | HTTP endpoint |
|
||||
| app | t2.small | 1 | 2G | HTTP endpoint |
|
||||
| worker | t2.medium | 2 | 4G | Custom command |
|
||||
| db | t2.medium | 2 | 4G | pg_isready |
|
||||
|
||||
**Verification Command:**
|
||||
```bash
|
||||
cd labs/lab-03-compute
|
||||
bash tests/99-final-verification.sh
|
||||
```
|
||||
|
||||
**Expected Result:** All checks PASS
|
||||
|
||||
---
|
||||
|
||||
## Automated Validation Scripts
|
||||
|
||||
### Script 1: INF-03 Compliance
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Verify all services have resource limits
|
||||
|
||||
SERVICES=$(docker compose config --services)
|
||||
for service in $SERVICES; do
|
||||
has_cpu=$(docker compose config | grep -A 10 "$service:" | grep -c "cpus:")
|
||||
has_mem=$(docker compose config | grep -A 10 "$service:" | grep -c "memory:")
|
||||
if [[ $has_cpu -eq 0 || $has_mem -eq 0 ]]; then
|
||||
echo "FAIL: $service missing resource limits"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "PASS: All services have resource limits"
|
||||
```
|
||||
|
||||
### Script 2: Healthcheck Verification
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Verify all services have healthchecks
|
||||
|
||||
SERVICES=$(docker compose config --services)
|
||||
for service in $SERVICES; do
|
||||
has_hc=$(docker compose config | grep -A 20 "$service:" | grep -c "healthcheck:")
|
||||
if [[ $has_hc -eq 0 ]]; then
|
||||
echo "FAIL: $service missing healthcheck"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "PASS: All services have healthchecks"
|
||||
```
|
||||
|
||||
### Script 3: Resource Enforcement Test
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Deploy container with limits and verify enforcement
|
||||
|
||||
docker compose up -d test-stress
|
||||
sleep 2
|
||||
|
||||
# Get limits
|
||||
CPU_LIMIT=$(docker inspect test-stress --format='{{.HostConfig.NanoCpus}}')
|
||||
MEM_LIMIT=$(docker inspect test-stress --format='{{.HostConfig.Memory}}')
|
||||
|
||||
# Start stress test
|
||||
docker exec test-stress stress --cpu 1 &
|
||||
sleep 5
|
||||
|
||||
# Verify CPU doesn't exceed 50%
|
||||
CPU_USAGE=$(docker stats test-stress --no-stream --format "{{.CPUPerc}}")
|
||||
if [[ $CPU_USAGE > 50 ]]; then
|
||||
echo "FAIL: CPU limit not enforced"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "PASS: Resource limits enforced"
|
||||
```
|
||||
|
||||
## Final Validation Checklist
|
||||
|
||||
- [ ] All 6 test scripts created (RED phase)
|
||||
- [ ] All 11 documentation files created (Diátxis)
|
||||
- [ ] docker-compose.yml implemented (GREEN phase)
|
||||
- [ ] All tests pass after implementation
|
||||
- [ ] INF-03 compliance verified
|
||||
- [ ] Student can follow tutorial end-to-end
|
||||
- [ ] Parallelism to EC2 documented
|
||||
- [ ] Resource limits enforced (verified with docker stats)
|
||||
- [ ] Healthchecks functional
|
||||
- [ ] Service dependencies work correctly
|
||||
|
||||
## Sign-off
|
||||
|
||||
Phase 4 is complete when:
|
||||
1. Student can deploy container with resource limits
|
||||
2. Student can add healthchecks to services
|
||||
3. All tests pass (99-final-verification.sh)
|
||||
4. Documentation follows Diátxis framework
|
||||
5. INF-03 compliance is mandatory and enforced
|
||||
25
labs/lab-03-compute/Dockerfile
Normal file
25
labs/lab-03-compute/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
||||
# Dockerfile per Lab 03 - Compute & EC2
|
||||
# Container di test con strumenti di stress per verifica enforcement
|
||||
|
||||
FROM alpine:3.19
|
||||
|
||||
# Creare utente non-root per sicurezza (INF-01 compliance)
|
||||
RUN addgroup -g 1000 appgroup && \
|
||||
adduser -D -u 1000 -G appgroup appuser
|
||||
|
||||
# Installare strumenti di stress testing e monitoraggio
|
||||
RUN apk add --no-cache \
|
||||
stress \
|
||||
curl \
|
||||
wget \
|
||||
procps \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Passare all'utente non-root
|
||||
USER appuser
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /home/appuser
|
||||
|
||||
# Comando di default - container in attesa per testing
|
||||
CMD ["sh", "-c", "sleep 3600"]
|
||||
140
labs/lab-03-compute/docker-compose.yml
Normal file
140
labs/lab-03-compute/docker-compose.yml
Normal file
@@ -0,0 +1,140 @@
|
||||
# Lab 03: Compute & EC2 - Docker Compose Configuration
|
||||
# Simula diverse EC2 instance types usando resource limits Docker
|
||||
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
# Web Server - simula t2.micro (1 vCPU, 1 GB RAM)
|
||||
web:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-web
|
||||
hostname: web
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8080:80"
|
||||
|
||||
depends_on:
|
||||
app:
|
||||
condition: service_healthy
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
# Application Server - simula t2.small (1 vCPU, 2 GB RAM)
|
||||
app:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-app
|
||||
hostname: app
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8081:80"
|
||||
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
# Worker - simula t2.medium (2 vCPU, 4 GB RAM)
|
||||
worker:
|
||||
image: alpine:3.19
|
||||
container_name: lab03-worker
|
||||
hostname: worker
|
||||
|
||||
command: ["sh", "-c", "sleep 3600"]
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "exit 0"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
# Database - simula t2.medium (2 vCPU, 4 GB RAM)
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: lab03-db
|
||||
hostname: db
|
||||
|
||||
environment:
|
||||
POSTGRES_DB: lab03_db
|
||||
POSTGRES_USER: lab03_user
|
||||
POSTGRES_PASSWORD: lab03_password
|
||||
POSTGRES_INITDB_ARGS: "-E UTF8"
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U lab03_user -d lab03_db || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
# Stress Test Container - per verifica enforcement
|
||||
stress-test:
|
||||
image: alpine:3.19
|
||||
container_name: lab03-stress
|
||||
hostname: stress-test
|
||||
|
||||
command: ["sh", "-c", "sleep 3600"]
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "exit 0"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
# Persistent Volumes
|
||||
volumes:
|
||||
db-data:
|
||||
driver: local
|
||||
484
labs/lab-03-compute/explanation/compute-ec2-parallels.md
Normal file
484
labs/lab-03-compute/explanation/compute-ec2-parallels.md
Normal file
@@ -0,0 +1,484 @@
|
||||
# Explanation: Parallelismi tra Docker Compute e EC2
|
||||
|
||||
In questo documento esploreremo come le risorse dei container Docker simulano le istanze EC2 di AWS. Comprendere questi parallelismi ti permetterà di applicare le conoscenze acquisite localmente agli ambienti cloud reali.
|
||||
|
||||
---
|
||||
|
||||
## Cos'è un'EC2 Instance?
|
||||
|
||||
**EC2 (Elastic Compute Cloud)** Instance è una macchina virtuale nel cloud che:
|
||||
- **Fornisce risorse computazionali**: CPU, memoria, storage, rete
|
||||
- **Definisce il tipo e la dimensione**: Instance types con diverse combinazioni
|
||||
- **Scansiona i costi**: Paghi per le risorse che usi (o riservate)
|
||||
|
||||
Le istanze EC2 sono il cuore della compute infrastructure in AWS, usate per:
|
||||
- Web server e application server
|
||||
- Database (RDS, Aurora)
|
||||
- Batch processing
|
||||
- Container hosting (ECS, EKS)
|
||||
|
||||
---
|
||||
|
||||
## Il Parallelismo Fondamentale
|
||||
|
||||
### Container Docker = EC2 Instance
|
||||
|
||||
| Locale | Cloud AWS |
|
||||
|--------|-----------|
|
||||
| `docker run` | `aws ec2 run-instances` |
|
||||
| Container con limiti | EC2 Instance type |
|
||||
| `cpus: '1'` | 1 vCPU |
|
||||
| `memory: 2G` | 2 GB RAM |
|
||||
| `docker stats` | CloudWatch Metrics |
|
||||
| Healthcheck | ELB Health Check |
|
||||
|
||||
### Resource Limits = Instance Type
|
||||
|
||||
**Locale (Docker Compose):**
|
||||
```yaml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
```
|
||||
|
||||
**Cloud (AWS CLI):**
|
||||
```bash
|
||||
aws ec2 run-instances \
|
||||
--image-id ami-12345 \
|
||||
--instance-type t2.small \
|
||||
# t2.small = 1 vCPU, 2 GB RAM
|
||||
```
|
||||
|
||||
**Stesso risultato:** 1 vCPU, 2 GB RAM di risorse garantite.
|
||||
|
||||
---
|
||||
|
||||
## EC2 Instance Types - Approfondimento
|
||||
|
||||
### Famiglie di Instance Types
|
||||
|
||||
AWS offre diverse famiglie di istanze per diversi workload:
|
||||
|
||||
| Famiglia | Prefix | Caratteristiche | Docker Parallel |
|
||||
|----------|--------|-----------------|-----------------|
|
||||
| **Burstable** | t2, t3 | Credit-based CPU, low cost | Piccoli limiti con burst |
|
||||
| **General Purpose** | m5, m6 | Equilibrato CPU/memoria | Limiti bilanciati |
|
||||
| **Compute Optimized** | c5, c6 | Alto ratio CPU | CPU alto, memoria bassa |
|
||||
| **Memory Optimized** | r5, r6 | Alto ratio memoria | CPU basso, memoria alta |
|
||||
| **Storage Optimized** | i3, i4 | NVMe SSD locale | Volumes veloci |
|
||||
|
||||
### Instance Types Comuni Analizzati
|
||||
|
||||
#### T2 Nano - Microservices
|
||||
**Spec:** 0.5 vCPU, 512 MB RAM
|
||||
**Costo:** ~$0.006/ora
|
||||
**Use Case:** Microservizi minimi, background tasks
|
||||
|
||||
**Docker Parallel:**
|
||||
```yaml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
**Quando usarlo:**
|
||||
- Servizi che usano poca CPU
|
||||
- Task asincroni leggeri
|
||||
- Sviluppo e test economici
|
||||
|
||||
#### T2 Micro - Dev/Test
|
||||
**Spec:** 1 vCPU, 1 GB RAM
|
||||
**Costo:** ~$0.012/ora
|
||||
**Use Case:** Development, test, small websites
|
||||
|
||||
**Docker Parallel:**
|
||||
```yaml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
```
|
||||
|
||||
**Quando usarlo:**
|
||||
- Ambienti di sviluppo
|
||||
- Test automation
|
||||
- Microservices a basso traffico
|
||||
|
||||
#### T2 Small - Web Servers
|
||||
**Spec:** 1 vCPU, 2 GB RAM
|
||||
**Costo:** ~$0.024/ora
|
||||
**Use Case:** Web server, API endpoints
|
||||
|
||||
**Docker Parallel:**
|
||||
```yaml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
```
|
||||
|
||||
**Quando usarlo:**
|
||||
- Web server (Nginx, Apache)
|
||||
- API REST
|
||||
- Container reverse proxy
|
||||
|
||||
#### T2 Medium - Application Server
|
||||
**Spec:** 2 vCPU, 4 GB RAM
|
||||
**Costo:** ~$0.048/ora
|
||||
**Use Case:** Application server, cache
|
||||
|
||||
**Docker Parallel:**
|
||||
```yaml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
```
|
||||
|
||||
**Quando usarlo:**
|
||||
- Application server (Node.js, Python)
|
||||
- Cache server (Redis)
|
||||
- Database di sviluppo
|
||||
|
||||
#### M5 Large - Production
|
||||
**Spec:** 2 vCPU, 8 GB RAM
|
||||
**Costo:** ~$0.096/ora
|
||||
**Use Case:** Production applications
|
||||
|
||||
**Docker Parallel:**
|
||||
```yaml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 8G
|
||||
```
|
||||
|
||||
**Quando usarlo:**
|
||||
- Production web applications
|
||||
- Services con cache in-memory
|
||||
- Databases di produzione (non critici)
|
||||
|
||||
---
|
||||
|
||||
## Healthcheck Parallelism
|
||||
|
||||
### Docker Healthcheck = ELB Health Check
|
||||
|
||||
| Locale | Cloud AWS |
|
||||
|--------|-----------|
|
||||
| healthcheck.test | Health check path/protocol |
|
||||
| healthcheck.interval | Health check interval (30s) |
|
||||
| healthcheck.timeout | Health check timeout (5s) |
|
||||
| healthcheck.retries | Unhealthy threshold (2) |
|
||||
| healthcheck.start_period | Grace period (none in ELB) |
|
||||
|
||||
### Esempio Pratico
|
||||
|
||||
**Locale (Docker Compose):**
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 2
|
||||
```
|
||||
|
||||
**Cloud (Target Group ELB):**
|
||||
```json
|
||||
{
|
||||
"TargetGroup": {
|
||||
"HealthCheckProtocol": "HTTP",
|
||||
"HealthCheckPath": "/health",
|
||||
"HealthCheckIntervalSeconds": 30,
|
||||
"HealthCheckTimeoutSeconds": 5,
|
||||
"UnhealthyThresholdCount": 2,
|
||||
"HealthyThresholdCount": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Stesso comportamento:**
|
||||
- Check HTTP ogni 30 secondi
|
||||
- Timeout dopo 5 secondi
|
||||
- Unhealthy dopo 2 fallimenti consecutivi
|
||||
|
||||
---
|
||||
|
||||
## Resource Monitoring Parallelism
|
||||
|
||||
### Docker Stats = CloudWatch Metrics
|
||||
|
||||
| Locale | Cloud AWS |
|
||||
|--------|-----------|
|
||||
| docker stats | CloudWatch Metrics |
|
||||
| CPU % | CPUUtilization |
|
||||
| Mem usage | MemoryUtilization |
|
||||
| Network I/O | NetworkIn/Out |
|
||||
| Block I/O | DiskReadBytes/WriteBytes |
|
||||
|
||||
### Esempio di Monitoring
|
||||
|
||||
**Locale:**
|
||||
```bash
|
||||
docker stats lab03-web --no-stream
|
||||
# lab03-web: 0.01% CPU, 2.5MiB / 1GiB memory (0.24%)
|
||||
```
|
||||
|
||||
**Cloud (CloudWatch):**
|
||||
```bash
|
||||
aws cloudwatch get-metric-statistics \
|
||||
--namespace AWS/EC2 \
|
||||
--metric-name CPUUtilization \
|
||||
--dimensions Name=InstanceId,Value=i-12345 \
|
||||
--start-time 2024-03-25T00:00:00Z \
|
||||
--end-time 2024-03-25T00:05:00Z \
|
||||
--period 60 \
|
||||
--statistics Average
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dipendenze Parallelism
|
||||
|
||||
### Docker depends_on = ECS DependsOn
|
||||
|
||||
| Locale | Cloud AWS |
|
||||
|--------|-----------|
|
||||
| depends_on: service_healthy | dependsOn: condition=HEALTHY |
|
||||
| depends_on: service_started | dependsOn: condition=START |
|
||||
|
||||
### Esempio Multi-Tier
|
||||
|
||||
**Locale (Docker Compose):**
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
depends_on:
|
||||
app:
|
||||
condition: service_healthy
|
||||
|
||||
app:
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
db:
|
||||
# No dependencies
|
||||
```
|
||||
|
||||
**Cloud (ECS Task Definition):**
|
||||
```json
|
||||
{
|
||||
"containerDefinitions": [
|
||||
{
|
||||
"name": "web",
|
||||
"dependsOn": [
|
||||
{"containerName": "app", "condition": "HEALTHY"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "app",
|
||||
"dependsOn": [
|
||||
{"containerName": "db", "condition": "HEALTHY"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "db",
|
||||
"dependsOn": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Costi e Billing Parallelism
|
||||
|
||||
### Docker = Local Resources (Free)
|
||||
|
||||
In locale, le risorse Docker sono "gratuite":
|
||||
- Paghi per l'hardware host (una tantum)
|
||||
- Nessun costo orario per container
|
||||
- Limiti servono per isolamento, non billing
|
||||
|
||||
### EC2 = Pay-Per-Use
|
||||
|
||||
In cloud, paghi per:
|
||||
- Ore di compute (o secondi con Fargate)
|
||||
- Tipi di istanza (più grandi = più costosi)
|
||||
- Riserve e spot instances possono ridurre costi
|
||||
|
||||
**Mapping costi:**
|
||||
|
||||
| Docker | EC2 | Costo/ora |
|
||||
|--------|-----|-----------|
|
||||
| 0.5 CPU, 512M | t2.nano | ~$0.006 |
|
||||
| 1 CPU, 1G | t2.micro | ~$0.012 |
|
||||
| 1 CPU, 2G | t2.small | ~$0.024 |
|
||||
| 2 CPU, 4G | t2.medium | ~$0.048 |
|
||||
| 2 CPU, 8G | m5.large | ~$0.096 |
|
||||
|
||||
---
|
||||
|
||||
## Scaling Parallelism
|
||||
|
||||
### Docker Compose Scale = EC2 Auto Scaling
|
||||
|
||||
**Locale (Horizontal Scaling):**
|
||||
```yaml
|
||||
web:
|
||||
deploy:
|
||||
replicas: 4
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
```
|
||||
Risultato: 4 container, ognuno con 1 CPU e 2 GB
|
||||
|
||||
**Cloud (Auto Scaling Group):**
|
||||
```bash
|
||||
aws autoscaling create-auto-scaling-group \
|
||||
--auto-scaling-group-name my-asg \
|
||||
--launch-template LaunchTemplateId=lt-12345 \
|
||||
--min-size 2 \
|
||||
--max-size 4 \
|
||||
--desired-capacity 4
|
||||
```
|
||||
Risultato: 4 EC2 instances (t2.small)
|
||||
|
||||
**Parallelismo:**
|
||||
- Docker: `replicas` = numero di container
|
||||
- EC2: `desired-capacity` = numero di istanze
|
||||
- Entrambi: distribuiscono carico su più unità
|
||||
|
||||
---
|
||||
|
||||
## Differenze Chiave
|
||||
|
||||
### 1. Crediti CPU (Burstable Instances)
|
||||
|
||||
**T2/T3 instances** usano CPU credits:
|
||||
- Ogni istanza accumula crediti quando idle
|
||||
- Spende crediti quando sotto carico
|
||||
- Senza crediti = performance degradata
|
||||
|
||||
**Docker NON ha credits:**
|
||||
- CPU limit è hard cap
|
||||
- Mai degrada (sempre disponibile fino al limite)
|
||||
- No burst oltre il limite
|
||||
|
||||
### 2. Pricing Models
|
||||
|
||||
**AWS offre:**
|
||||
- **On-Demand:** Paghi per ora usata
|
||||
- **Reserved:** Sconto per 1-3 anni di impegno
|
||||
- **Spot:** Asta per capacità unused (fino a 90% sconto)
|
||||
- **Dedicated:** Host fisico dedicato
|
||||
|
||||
**Docker:**
|
||||
- Nessun pricing model
|
||||
- Tutto flat sul costo host
|
||||
|
||||
### 3. Availability Zones
|
||||
|
||||
**AWS:**
|
||||
- Istanze distribuite su multiple AZ
|
||||
- Ogni AZ = data center separato
|
||||
- High availability geografica
|
||||
|
||||
**Docker:**
|
||||
- Single host (senza Swarm/Kubernetes)
|
||||
- Nessuna AZ separation
|
||||
- Host failure = tutti i container down
|
||||
|
||||
---
|
||||
|
||||
## Best Practices Transfer
|
||||
|
||||
### Da Locale a Cloud
|
||||
|
||||
| Best Practice Locale | Equivalente Cloud |
|
||||
|---------------------|-------------------|
|
||||
| Set resource limits | Choose right instance type |
|
||||
| Use healthchecks | Configure ELB health checks |
|
||||
| depends_on healthy | Use ECS dependsOn |
|
||||
| Monitor with docker stats | Use CloudWatch alarms |
|
||||
| Scale with replicas | Use Auto Scaling Groups |
|
||||
|
||||
### Evoluzione Architetturale
|
||||
|
||||
**Locale (Docker Compose):**
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
```
|
||||
|
||||
**Cloud (ECS/Fargate):**
|
||||
```json
|
||||
{
|
||||
"containerDefinitions": [{
|
||||
"name": "web",
|
||||
"cpu": 1024,
|
||||
"memory": 2048,
|
||||
"memoryReservation": 2048
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**Nota:** In ECS Fargate, CPU e memoria sono configurati come:
|
||||
- `cpu`: 256, 512, 1024, 2048, 4096 (unità: 1 vCPU = 1024)
|
||||
- `memory`: 512, 1024, 2048, ... (in MB)
|
||||
|
||||
---
|
||||
|
||||
## Command Equivalents Table
|
||||
|
||||
| Operazione | Locale (Docker) | Cloud (AWS) |
|
||||
|------------|------------------|-------------|
|
||||
| **Deploy compute** | docker compose up -d | aws ec2 run-instances |
|
||||
| **Check resources** | docker inspect --format '{{.HostConfig}}' | aws ec2 describe-instance-types |
|
||||
| **Monitor usage** | docker stats | aws cloudwatch get-metric-statistics |
|
||||
| **Set limits** | deploy.resources.limits | --instance-type parameter |
|
||||
| **Check health** | docker inspect --format '{{.State.Health.Status}}' | aws elb describe-target-health |
|
||||
| **Scale out** | docker compose up -d --scale web=4 | aws autoscaling set-desired-capacity |
|
||||
| **Stop compute** | docker compose stop | aws ec2 stop-instances |
|
||||
| **Terminate** | docker compose down | aws ec2 terminate-instances |
|
||||
|
||||
---
|
||||
|
||||
## Conclusione
|
||||
|
||||
Le risorse dei container Docker seguono gli stessi principi fondamentali delle EC2 instances: definizione di CPU e memoria, monitoraggio dell'utilizzo, e health checks per verificare lo stato.
|
||||
|
||||
Quando lavorerai con EC2 cloud, ricorda:
|
||||
|
||||
- **Docker Container** = **EC2 Instance** (unità di compute)
|
||||
- **Resource Limits** = **Instance Type** (dimensione e potenza)
|
||||
- **Healthcheck** = **ELB Health Check** (verifica stato)
|
||||
- **docker stats** = **CloudWatch Metrics** (monitoraggio)
|
||||
- **depends_on** = **ECS DependsOn** (ordinamento avvio)
|
||||
|
||||
Comprendendo questi parallelismi, sarai in grado di progettare architetture cloud scalabili usando le competenze acquisite localmente.
|
||||
|
||||
---
|
||||
|
||||
## Approfondimenti
|
||||
|
||||
- [AWS EC2 Instance Types Documentation](https://docs.aws.amazon.com/ec2/latest/instancetypes/)
|
||||
- [ECS Task Definitions](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html)
|
||||
- [ELB Health Checks](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html)
|
||||
- [CloudWatch Metrics](https://docs.aws.amazon.com/AmazonCloudWatch/latest/UserGuide/cloudwatch_concepts.html)
|
||||
94
labs/lab-03-compute/how-to-guides/check-resource-usage.md
Normal file
94
labs/lab-03-compute/how-to-guides/check-resource-usage.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# How-to: Verificare l'Utilizzo delle Risorse
|
||||
|
||||
Come monitorare l'utilizzo CPU e memoria dei container Docker.
|
||||
|
||||
## Utilizzo Base
|
||||
|
||||
### Snapshot Singolo
|
||||
|
||||
```bash
|
||||
docker stats --no-stream
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
CONTAINER NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
|
||||
12345 lab03-web 0.01% 2.5MiB / 1GiB 0.24% 1.2kB / 0B 0B / 0B 2
|
||||
```
|
||||
|
||||
### Monitoraggio in Tempo Reale
|
||||
|
||||
```bash
|
||||
docker stats
|
||||
```
|
||||
|
||||
Premi `Ctrl+C` per uscire.
|
||||
|
||||
### Container Specifico
|
||||
|
||||
```bash
|
||||
docker stats lab03-web
|
||||
```
|
||||
|
||||
## Formattazione Avanzata
|
||||
|
||||
### Solo Container e CPU/Memoria
|
||||
|
||||
```bash
|
||||
docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"
|
||||
```
|
||||
|
||||
### Output senza header
|
||||
|
||||
```bash
|
||||
docker stats --no-stream --format "{{.Container}}: {{.CPUPerc}}, {{.MemUsage}}"
|
||||
```
|
||||
|
||||
### Output CSV
|
||||
|
||||
```bash
|
||||
docker stats --no-stream --format "{{.Container}},{{.CPUPerc}},{{.MemUsage}}"
|
||||
```
|
||||
|
||||
## Interpretare l'Output
|
||||
|
||||
### CPU Percentage
|
||||
- `0.01%` - Container idle
|
||||
- `50%` - Container usa mezza CPU
|
||||
- `100%` - Container usa 1 CPU completa
|
||||
- `>100%` - Container usa più di 1 CPU (multi-core)
|
||||
|
||||
### Memory Usage
|
||||
- `2.5MiB / 1GiB` - Usati 2.5 MB su 1 GB di limite
|
||||
- `512MiB / 512MiB` - Al limite (potrebbe causare OOM)
|
||||
- `980MiB / 1GiB` - Vicino al limite (watch!)
|
||||
|
||||
### Memory Percentage
|
||||
- `<50%` - Sotto l'half del limite (OK)
|
||||
- `50-80%` - Nella norma (monitorare)
|
||||
- `>80%` - Vicino al limite (attenzione)
|
||||
- `>95%` - A rischio di OOM kill
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container usa 0% CPU
|
||||
Container potrebbe essere idle o bloccato. Verifica:
|
||||
```bash
|
||||
docker exec lab03-web ps aux
|
||||
```
|
||||
|
||||
### Memory usage alto
|
||||
Identifica il processo che usa più memoria:
|
||||
```bash
|
||||
docker exec lab03-web ps aux --sort=-%mem | head -5
|
||||
```
|
||||
|
||||
### Container OOM killed
|
||||
Cerca "OOM" nei log:
|
||||
```bash
|
||||
docker inspect lab03-web --format '{{.State.OOMKilled}}'
|
||||
```
|
||||
|
||||
## Vedi Anche
|
||||
- How-to: Testare Limits Enforcement
|
||||
- Reference: Compose Resources Syntax
|
||||
120
labs/lab-03-compute/how-to-guides/custom-healthcheck.md
Normal file
120
labs/lab-03-compute/how-to-guides/custom-healthcheck.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# How-to: Scrivere Healthchecks Personalizzati
|
||||
|
||||
Come creare healthchecks custom per diversi tipi di servizi.
|
||||
|
||||
## Pattern Comuni
|
||||
|
||||
### HTTP Healthcheck
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost/health || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
### TCP Port Check
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 2s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
### File Existence Check
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "test -f /var/run/app/ready || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 1s
|
||||
retries: 5
|
||||
```
|
||||
|
||||
### Database Connection
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "mysqladmin ping -h localhost || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
```
|
||||
|
||||
### Redis Check
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
### Python Script Check
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "python /app/healthcheck.py || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Check Leggeri
|
||||
```yaml
|
||||
# ✓ GOOD - pagina leggera
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
|
||||
|
||||
# ✗ AVOID - pagina pesante
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost/big-data"]
|
||||
```
|
||||
|
||||
### 2. Timeout Appropriati
|
||||
```yaml
|
||||
# ✓ GOOD - timeout proporzionato all'interval
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
|
||||
# ✗ AVOID - timeout troppo lungo
|
||||
interval: 10s
|
||||
timeout: 30s # Riduce frequenza dei check
|
||||
```
|
||||
|
||||
### 3. Retries Adeguati
|
||||
```yaml
|
||||
# ✓ GOOD - più retries per servizi lenti (database)
|
||||
healthcheck:
|
||||
retries: 5
|
||||
|
||||
# ✓ GOOD - meno retries per servizi veloci (web)
|
||||
healthcheck:
|
||||
retries: 3
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Testare manualmente
|
||||
```bash
|
||||
docker exec lab03-web wget --spider -q http://localhost/health
|
||||
echo $? # 0 = success, !0 = failure
|
||||
```
|
||||
|
||||
### Vedere log healthcheck
|
||||
```bash
|
||||
docker inspect lab03-web --format '{{range .State.Health.Log}}{{.Output}} {{end}}'
|
||||
```
|
||||
|
||||
### Reset health status
|
||||
```bash
|
||||
docker restart lab03-web
|
||||
```
|
||||
|
||||
## Vedi Anche
|
||||
- Tutorial: Implementare Healthchecks
|
||||
- Reference: Healthcheck Syntax
|
||||
97
labs/lab-03-compute/how-to-guides/instance-type-mapping.md
Normal file
97
labs/lab-03-compute/how-to-guides/instance-type-mapping.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# How-to: Selezionare l'EC2 Instance Type Corretto
|
||||
|
||||
Come scegliere l'istanza EC2 giusta per il tuo workload Docker.
|
||||
|
||||
## Decision Tree
|
||||
|
||||
### Step 1: Tipo di Carico
|
||||
|
||||
**Dev/Test:**
|
||||
- Usa `t2` (burstable) - economico
|
||||
- Config: `cpus: '0.5'`, `memory: 512M`
|
||||
|
||||
**Web Server:**
|
||||
- Usa `t2.small` o `t2.medium`
|
||||
- Config: `cpus: '1'`, `memory: 2G` (o 4G)
|
||||
|
||||
**Application Server:**
|
||||
- Usa `m5.large` - performance consistente
|
||||
- Config: `cpus: '2'`, `memory: 8G`
|
||||
|
||||
**Database:**
|
||||
- Usa `t2.medium` (dev) o `m5.xlarge` (prod)
|
||||
- Config: `cpus: '2'`, `memory: 4G` (o 16G)
|
||||
|
||||
**Batch Processing:**
|
||||
- Usa `c5` (compute optimized)
|
||||
- Config: `cpus: '4'`, `memory: 4G`
|
||||
|
||||
### Step 2: Analizza il Tuo Container
|
||||
|
||||
```bash
|
||||
# Monitora il consumo attuale
|
||||
docker stats lab03-app --no-stream
|
||||
|
||||
# Se CPU > 70% → considera più CPU
|
||||
# Se Memory > 80% → considera più memoria
|
||||
```
|
||||
|
||||
### Step 3: Considera il Costo
|
||||
|
||||
| Instance | Costo/ora (us-east-1) | Use Case |
|
||||
|----------|----------------------|----------|
|
||||
| t2.nano | ~$0.006 | Micro |
|
||||
| t2.micro | ~$0.012 | Dev/Test |
|
||||
| t2.small | ~$0.024 | Web |
|
||||
| t2.medium | ~$0.048 | Application |
|
||||
| m5.large | ~$0.096 | Production |
|
||||
|
||||
## Docker → EC2 Quick Reference
|
||||
|
||||
```yaml
|
||||
# Dev/Test
|
||||
cpus: '0.5'; memory: 512M # → t2.nano
|
||||
cpus: '1'; memory: 1G # → t2.micro
|
||||
|
||||
# Web Tier
|
||||
cpus: '1'; memory: 2G # → t2.small
|
||||
cpus: '2'; memory: 4G # → t2.medium
|
||||
|
||||
# Application Tier
|
||||
cpus: '2'; memory: 8G # → m5.large
|
||||
cpus: '4'; memory: 16G # → m5.xlarge
|
||||
|
||||
# Database Tier
|
||||
cpus: '2'; memory: 4G # → t2.medium (dev)
|
||||
cpus: '4'; memory: 32G # → m5.2xlarge (prod)
|
||||
```
|
||||
|
||||
## Scaling Strategy
|
||||
|
||||
### Horizontal Scaling
|
||||
```yaml
|
||||
# Più container piccoli (t2.micro)
|
||||
web:
|
||||
deploy:
|
||||
replicas: 4
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
```
|
||||
|
||||
### Vertical Scaling
|
||||
```yaml
|
||||
# Meno container grandi (m5.large)
|
||||
web:
|
||||
deploy:
|
||||
replicas: 1
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 8G
|
||||
```
|
||||
|
||||
## Vedi Anche
|
||||
- Reference: EC2 Instance Mapping
|
||||
- Explanation: Compute-EC2 Parallels
|
||||
88
labs/lab-03-compute/how-to-guides/test-limits-enforcement.md
Normal file
88
labs/lab-03-compute/how-to-guides/test-limits-enforcement.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# How-to: Testare l'Enforcement dei Limiti
|
||||
|
||||
Come verificare che i limiti delle risorse siano effettivamente applicati.
|
||||
|
||||
## Test 1: Verificare Configurazione
|
||||
|
||||
### Controllare nel compose file
|
||||
|
||||
```bash
|
||||
grep -A 10 "deploy:" docker-compose.yml | grep -E "cpus:|memory:"
|
||||
```
|
||||
|
||||
### Controllare nel container
|
||||
|
||||
```bash
|
||||
docker inspect lab03-web --format '{{.HostConfig.NanoCpus}}' # CPU (1e9 = 1 CPU)
|
||||
docker inspect lab03-web --format '{{.HostConfig.Memory}}' # Memory in bytes
|
||||
```
|
||||
|
||||
## Test 2: Stress Test CPU
|
||||
|
||||
### Avviare container con stress
|
||||
|
||||
```bash
|
||||
docker run -d --name stress-test \
|
||||
--cpus='0.5' \
|
||||
polinux/stress \
|
||||
--cpu 1 \
|
||||
--timeout 30s
|
||||
```
|
||||
|
||||
### Monitorare enforcement
|
||||
|
||||
```bash
|
||||
docker stats stress-test --no-stream
|
||||
```
|
||||
|
||||
**Risultato atteso:** CPU non supera il 50% (0.5 CPU)
|
||||
|
||||
### Cleanup
|
||||
|
||||
```bash
|
||||
docker rm -f stress-test
|
||||
```
|
||||
|
||||
## Test 3: Stress Test Memory
|
||||
|
||||
### Avviare test OOM
|
||||
|
||||
```bash
|
||||
docker run -d --name mem-test \
|
||||
--memory='512m' \
|
||||
polinux/stress \
|
||||
--vm 1 \
|
||||
--vm-bytes 600M \
|
||||
--timeout 60s
|
||||
```
|
||||
|
||||
### Verificare OOM kill
|
||||
|
||||
```bash
|
||||
docker ps -a --filter 'name=mem-test'
|
||||
```
|
||||
|
||||
**Risultato atteso:** Container exited (code 137 = OOM killed)
|
||||
|
||||
### Cleanup
|
||||
|
||||
```bash
|
||||
docker rm -f mem-test
|
||||
```
|
||||
|
||||
## Test 4: Verifica con Script
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# verify-limits.sh
|
||||
|
||||
for container in lab03-web lab03-app lab03-db; do
|
||||
echo "Container: $container"
|
||||
docker inspect "$container" --format ' CPUs: {{.HostConfig.NanoCpus}}'
|
||||
docker inspect "$container" --format ' Memory: {{.HostConfig.Memory}}'
|
||||
done
|
||||
```
|
||||
|
||||
## Vedi Anche
|
||||
- How-to: Check Resource Usage
|
||||
- Reference: EC2 Instance Mapping
|
||||
210
labs/lab-03-compute/reference/compose-resources-syntax.md
Normal file
210
labs/lab-03-compute/reference/compose-resources-syntax.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# Reference: Docker Compose Resources Syntax
|
||||
|
||||
Riferimento completo per la configurazione delle risorse in Docker Compose.
|
||||
|
||||
## Sezione deploy.resources
|
||||
|
||||
### Struttura Completa
|
||||
|
||||
```yaml
|
||||
services:
|
||||
service_name:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: 'VALUE'
|
||||
memory: 'VALUE'
|
||||
reservations:
|
||||
cpus: 'VALUE'
|
||||
memory: 'VALUE'
|
||||
```
|
||||
|
||||
## Parametri Limits
|
||||
|
||||
### cpus
|
||||
|
||||
**Formato:** Stringa con numero decimale
|
||||
|
||||
```yaml
|
||||
cpus: '0.5' # 50% di 1 CPU
|
||||
cpus: '1' # 1 CPU completa
|
||||
cpus: '2' # 2 CPU complete
|
||||
cpus: '1.5' # 1.5 CPU
|
||||
cpus: '0.25' # 25% di 1 CPU
|
||||
```
|
||||
|
||||
**Validità:**
|
||||
- Minimo: `0.001` (1 millesimo di CPU)
|
||||
- Massimo: Numero di CPU host (es. 8 su host con 8 core)
|
||||
- Default: Nessun limite (tutte le CPU disponibili)
|
||||
|
||||
### memory
|
||||
|
||||
**Formato:** Stringa con unità
|
||||
|
||||
```yaml
|
||||
memory: '512M' # 512 Megabyte
|
||||
memory: '1G' # 1 Gigabyte
|
||||
memory: '128M' # 128 Megabyte
|
||||
memory: '4G' # 4 Gigabyte
|
||||
memory: '1024M' # 1024 Megabyte (equivale a 1G)
|
||||
```
|
||||
|
||||
**Unità Supportate:**
|
||||
- `B` - Byte
|
||||
- `K` / `KB` - Kilobyte (1024 bytes)
|
||||
- `M` / `MB` - Megabyte (1024 KB)
|
||||
- `G` / `GB` - Gigabyte (1024 MB)
|
||||
|
||||
**Validità:**
|
||||
- Minimo: `4M` (4 Megabyte)
|
||||
- Massico: Memoria host disponibile
|
||||
- Default: Nessun limite (tutta la memoria disponibile)
|
||||
|
||||
## Parametri Reservations
|
||||
|
||||
### Scopo
|
||||
|
||||
Le **reservations** garantiscono risorse minime disponibili:
|
||||
- Docker riserva queste risorse per il container
|
||||
- Usato per pianificazione capacity (Kubernetes)
|
||||
- Meno comune in Docker Compose locale
|
||||
|
||||
### Esempio
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2' # Massimo 2 CPU
|
||||
memory: '4G' # Massimo 4 GB
|
||||
reservations:
|
||||
cpus: '1' # Garantite 1 CPU
|
||||
memory: '2G' # Garantiti 2 GB
|
||||
```
|
||||
|
||||
**Uso tipico:**
|
||||
- `limits` - Definisce il tetto massimo (enforcement)
|
||||
- `reservations` - Definisce il pavimento minimo (garanzia)
|
||||
|
||||
## Esempi Completi per Servizi
|
||||
|
||||
### Web Server (t2.micro)
|
||||
|
||||
```yaml
|
||||
web:
|
||||
image: nginx:alpine
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
```
|
||||
|
||||
### Application Server (t2.small)
|
||||
|
||||
```yaml
|
||||
app:
|
||||
image: node:alpine
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
```
|
||||
|
||||
### Database (t2.medium)
|
||||
|
||||
```yaml
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
reservations:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
```
|
||||
|
||||
### Worker CPU-Intensive (c5.xlarge)
|
||||
|
||||
```yaml
|
||||
worker:
|
||||
image: python:alpine
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '4'
|
||||
memory: 4G
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
### Verifica con docker compose config
|
||||
|
||||
```bash
|
||||
docker compose config
|
||||
```
|
||||
|
||||
Output per servizio con limits:
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1073741824 # 1G in byte
|
||||
```
|
||||
|
||||
### Verifica con docker inspect
|
||||
|
||||
```bash
|
||||
docker inspect lab03-web --format '{{.HostConfig}}'
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
map[NanoCpus:1000000000 Memory:1073741824]
|
||||
```
|
||||
|
||||
Conversione:
|
||||
- `NanoCpus: 1000000000` = 1 CPU (1e9 nanocpus)
|
||||
- `Memory: 1073741824` = 1 GB (in byte)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Errore: "no matching resources"
|
||||
|
||||
**Causa:** Host non ha abbastanza risorse
|
||||
|
||||
**Soluzione:**
|
||||
```yaml
|
||||
# Riduci i limits
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5' # Era '2'
|
||||
memory: 512M # Era '2G'
|
||||
```
|
||||
|
||||
### Errore: "invalid memory format"
|
||||
|
||||
**Causa:** Formato non valido
|
||||
|
||||
**Soluzione:**
|
||||
```yaml
|
||||
# ✗ WRONG
|
||||
memory: 1024 # Manca unità
|
||||
memory: "1GB" # "GB" non valido (usa G)
|
||||
|
||||
# ✓ CORRECT
|
||||
memory: 1G
|
||||
memory: 1024M
|
||||
```
|
||||
|
||||
## Vedi Anche
|
||||
- Tutorial: Configurare Limiti delle Risorse
|
||||
- Reference: EC2 Instance Mapping
|
||||
159
labs/lab-03-compute/reference/ec2-instance-mapping.md
Normal file
159
labs/lab-03-compute/reference/ec2-instance-mapping.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Reference: EC2 Instance Type Mapping
|
||||
|
||||
Tabella completa di mapping tra Docker resource limits e EC2 instance types.
|
||||
|
||||
## Tabella di Mapping Completa
|
||||
|
||||
| Docker Compose | EC2 Instance | vCPUs | Memory | Costo/ora* | Use Case |
|
||||
|----------------|--------------|-------|--------|------------|----------|
|
||||
| `cpus: '0.5'`<br>`memory: 512M` | t2.nano | 0.5 | 512 MB | $0.006 | Microservizi minimi |
|
||||
| `cpus: '1'`<br>`memory: 1G` | t2.micro | 1 | 1 GB | $0.012 | Dev/Test |
|
||||
| `cpus: '1'`<br>`memory: 2G` | t2.small | 1 | 2 GB | $0.024 | Web server |
|
||||
| `cpus: '2'`<br>`memory: 4G` | t2.medium | 2 | 4 GB | $0.048 | Application |
|
||||
| `cpus: '2'`<br>`memory: 8G` | m5.large | 2 | 8 GB | $0.096 | Production |
|
||||
| `cpus: '4'`<br>`memory: 16G` | m5.xlarge | 4 | 16 GB | $0.192 | High traffic |
|
||||
| `cpus: '8'`<br>`memory: 32G` | m5.2xlarge | 8 | 32 GB | $0.384 | Scalabile |
|
||||
| `cpus: '16'`<br>`memory: 64G` | m5.4xlarge | 16 | 64 GB | $0.768 | Enterprise |
|
||||
|
||||
\*Costi indicativi per regione us-east-1
|
||||
|
||||
## Famiglie Instance Types
|
||||
|
||||
### General Purpose (m5, m6)
|
||||
|
||||
Equilibrato tra CPU, memoria, rete.
|
||||
|
||||
```yaml
|
||||
# m5.large - 2 vCPUs, 8 GB
|
||||
cpus: '2'
|
||||
memory: 8G
|
||||
```
|
||||
|
||||
**Use case:** Web server, application server
|
||||
|
||||
### Burstable (t2, t3)
|
||||
|
||||
Credit-based CPU, basso costo.
|
||||
|
||||
```yaml
|
||||
# t2.micro - 1 vCPU, 1 GB
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
```
|
||||
|
||||
**Use case:** Dev, test, microservizi
|
||||
|
||||
### Compute Optimized (c5, c6)
|
||||
|
||||
Alto ratio CPU/memoria.
|
||||
|
||||
```yaml
|
||||
# c5.xlarge - 4 vCPUs, 8 GB
|
||||
cpus: '4'
|
||||
memory: 8G
|
||||
```
|
||||
|
||||
**Use case:** Batch processing, encoding
|
||||
|
||||
### Memory Optimized (r5, r6)
|
||||
|
||||
Alto ratio memoria/CPU.
|
||||
|
||||
```yaml
|
||||
# r5.large - 2 vCPUs, 16 GB
|
||||
cpus: '2'
|
||||
memory: 16G
|
||||
```
|
||||
|
||||
**Use case:** Database, caching
|
||||
|
||||
## Docker Compose Examples
|
||||
|
||||
### T2 Nano (Micro)
|
||||
|
||||
```yaml
|
||||
micro:
|
||||
image: alpine:3.19
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
### T2 Micro (Dev/Test)
|
||||
|
||||
```yaml
|
||||
dev:
|
||||
image: node:alpine
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
```
|
||||
|
||||
### T2 Small (Web)
|
||||
|
||||
```yaml
|
||||
web:
|
||||
image: nginx:alpine
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
```
|
||||
|
||||
### T2 Medium (Application)
|
||||
|
||||
```yaml
|
||||
app:
|
||||
image: python:alpine
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
```
|
||||
|
||||
### M5 Large (Production)
|
||||
|
||||
```yaml
|
||||
production:
|
||||
image: myapp:latest
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 8G
|
||||
```
|
||||
|
||||
## Shortcut Reference
|
||||
|
||||
### Quick Copy-Paste
|
||||
|
||||
```yaml
|
||||
# T2 Nano
|
||||
cpus: '0.5'; memory: 512M
|
||||
|
||||
# T2 Micro
|
||||
cpus: '1'; memory: 1G
|
||||
|
||||
# T2 Small
|
||||
cpus: '1'; memory: 2G
|
||||
|
||||
# T2 Medium
|
||||
cpus: '2'; memory: 4G
|
||||
|
||||
# M5 Large
|
||||
cpus: '2'; memory: 8G
|
||||
|
||||
# M5 XLarge
|
||||
cpus: '4'; memory: 16G
|
||||
```
|
||||
|
||||
## Vedi Anche
|
||||
- Tutorial: Configurare Limiti delle Risorse
|
||||
- How-to: Selezionare Instance Type
|
||||
- Explanation: Compute-EC2 Parallels
|
||||
193
labs/lab-03-compute/reference/healthcheck-syntax.md
Normal file
193
labs/lab-03-compute/reference/healthcheck-syntax.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# Reference: Docker Compose Healthcheck Syntax
|
||||
|
||||
Riferimento completo per la configurazione degli healthchecks.
|
||||
|
||||
## Sezione healthcheck
|
||||
|
||||
### Struttura Completa
|
||||
|
||||
```yaml
|
||||
services:
|
||||
service_name:
|
||||
healthcheck:
|
||||
test: ["CMD", "command", "arg1", "arg2"]
|
||||
interval: 30s
|
||||
timeout: 30s
|
||||
retries: 3
|
||||
start_period: 0s
|
||||
```
|
||||
|
||||
## Parametri
|
||||
|
||||
### test (Required)
|
||||
|
||||
Comando da eseguire per verificare la salute.
|
||||
|
||||
**Formato CMD:**
|
||||
```yaml
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
|
||||
```
|
||||
|
||||
**Formato CMD-SHELL:**
|
||||
```yaml
|
||||
test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"]
|
||||
```
|
||||
|
||||
**Formato NONE:**
|
||||
```yaml
|
||||
test: ["NONE"]
|
||||
```
|
||||
Disabilita healthcheck ereditato dall'immagine.
|
||||
|
||||
### interval
|
||||
|
||||
Frequenza di esecuzione del test.
|
||||
|
||||
```yaml
|
||||
interval: 5s # Ogni 5 secondi
|
||||
interval: 10s # Ogni 10 secondi
|
||||
interval: 30s # Ogni 30 secondi (default)
|
||||
interval: 1m # Ogni minuto
|
||||
```
|
||||
|
||||
**Validità:**
|
||||
- Minimo: `1s` (1 secondo)
|
||||
- Massimo: Nessuno
|
||||
- Default: `30s`
|
||||
|
||||
### timeout
|
||||
|
||||
Tempo massimo per completare il test.
|
||||
|
||||
```yaml
|
||||
timeout: 2s # 2 secondi
|
||||
timeout: 5s # 5 secondi (default)
|
||||
timeout: 30s # 30 secondi
|
||||
```
|
||||
|
||||
**Nota:** Se il test supera il timeout, conta come fallimento.
|
||||
|
||||
### retries
|
||||
|
||||
Numero di fallimenti consecutivi prima di marcare unhealthy.
|
||||
|
||||
```yaml
|
||||
retries: 1 # 1 fallimento = unhealthy
|
||||
retries: 3 # 3 fallimenti = unhealthy (default)
|
||||
retries: 5 # 5 fallimenti = unhealthy
|
||||
```
|
||||
|
||||
**Calcolo del tempo unhealthy:**
|
||||
```
|
||||
tempo_totale = (interval + timeout) × retries
|
||||
```
|
||||
|
||||
Esempio con `interval: 10s, timeout: 5s, retries: 3`:
|
||||
- Check 1: 0s - 5s
|
||||
- Check 2: 10s - 15s
|
||||
- Check 3: 20s - 25s
|
||||
- Unhealthy dopo: 25s
|
||||
|
||||
### start_period
|
||||
|
||||
Grace period prima di contare retries verso unhealthy.
|
||||
|
||||
```yaml
|
||||
start_period: 0s # Nessun grace period (default)
|
||||
start_period: 5s # 5 secondi di grace
|
||||
start_period: 10s # 10 secondi di grace
|
||||
start_period: 30s # 30 secondi di grace
|
||||
```
|
||||
|
||||
**Comportamento:**
|
||||
- Durante `start_period`: Fallimenti non contano
|
||||
- Dopo `start_period`: `retries` inizi a contare
|
||||
|
||||
## Esempi per Tipo di Servizio
|
||||
|
||||
### HTTP Service (Nginx)
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
```
|
||||
|
||||
### Database (PostgreSQL)
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
```
|
||||
|
||||
### Cache (Redis)
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
### TCP Connection
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 2s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
### File Check
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "test -f /tmp/ready || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 1s
|
||||
retries: 5
|
||||
```
|
||||
|
||||
## Stati di Salute
|
||||
|
||||
### Ciclo di Vita
|
||||
|
||||
```
|
||||
created → starting → healthy
|
||||
↓
|
||||
unhealthy
|
||||
```
|
||||
|
||||
**Transizioni:**
|
||||
|
||||
| Da | A | Condizione |
|
||||
|----|---|------------|
|
||||
| created | starting | Container avviato con healthcheck |
|
||||
| starting | healthy | `retries` consecutivi passano |
|
||||
| starting | unhealthy | `retries` consecutivi falliscono (dopo start_period) |
|
||||
| healthy | unhealthy | `retries` consecutivi falliscono |
|
||||
| unhealthy | healthy | 1 check passa |
|
||||
|
||||
### Ispezionare Stato
|
||||
|
||||
```bash
|
||||
# Stato corrente
|
||||
docker inspect lab03-web --format '{{.State.Health.Status}}'
|
||||
# Output: healthy / unhealthy / starting
|
||||
|
||||
# Exit code dell'ultimo check
|
||||
docker inspect lab03-web --format '{{.State.Health.ExitCode}}'
|
||||
# Output: 0 (success) o !0 (failure)
|
||||
|
||||
### Vedi Anche
|
||||
- Tutorial: Implementare Healthchecks
|
||||
- How-to: Custom Healthcheck
|
||||
215
labs/lab-03-compute/tests/01-resource-limits-test.sh
Executable file
215
labs/lab-03-compute/tests/01-resource-limits-test.sh
Executable file
@@ -0,0 +1,215 @@
|
||||
#!/bin/bash
|
||||
# Test 01: Resource Limits Configuration
|
||||
# Verifies that docker-compose.yml services have mandatory resource limits
|
||||
# Usage: bash labs/lab-03-compute/tests/01-resource-limits-test.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
|
||||
|
||||
# Counter helpers
|
||||
pass_count=0
|
||||
fail_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
|
||||
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
}
|
||||
|
||||
print_test() {
|
||||
echo -e "\n${BLUE}[TEST]${NC} $1"
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e " ${GREEN}[✓]${NC} $1"
|
||||
inc_pass
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e " ${RED}[✗]${NC} $1"
|
||||
inc_fail
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e " ${BLUE}[i]${NC} $1"
|
||||
}
|
||||
|
||||
# Main verification
|
||||
print_header "Lab 03 Resource Limits Verification"
|
||||
|
||||
cd "$LAB_DIR"
|
||||
|
||||
# Test 1: docker-compose.yml exists
|
||||
print_test "Verifying docker-compose.yml exists"
|
||||
if [[ -f "docker-compose.yml" ]]; then
|
||||
print_pass "docker-compose.yml found"
|
||||
else
|
||||
print_fail "docker-compose.yml not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: docker-compose.yml is valid
|
||||
print_test "Validating docker-compose.yml syntax"
|
||||
if docker compose config &> /dev/null; then
|
||||
print_pass "docker-compose.yml has valid syntax"
|
||||
else
|
||||
print_fail "docker-compose.yml has syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: Services are defined
|
||||
print_test "Verifying services are defined"
|
||||
SERVICES=$(docker compose config --services 2>/dev/null)
|
||||
SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
|
||||
if [[ $SERVICE_COUNT -gt 0 ]]; then
|
||||
print_pass "Found $SERVICE_COUNT services"
|
||||
echo "$SERVICES" | while read -r service; do
|
||||
print_info " - $service"
|
||||
done
|
||||
else
|
||||
print_fail "No services defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 4: INF-03 compliance check - all services must have resource limits
|
||||
print_test "Checking INF-03 compliance (resource limits)"
|
||||
NON_COMPLIANT=0
|
||||
|
||||
for service in $SERVICES; do
|
||||
# Check for deploy.resources section
|
||||
has_deploy=$(docker compose config 2>/dev/null | grep -A 20 "^$service:" | grep -c "deploy:" || echo "0")
|
||||
has_resources=$(docker compose config 2>/dev/null | grep -A 25 "^$service:" | grep -c "resources:" || echo "0")
|
||||
|
||||
if [[ $has_deploy -eq 0 || $has_resources -eq 0 ]]; then
|
||||
print_fail " $service: Missing deploy.resources section"
|
||||
((NON_COMPLIANT++)) || true
|
||||
else
|
||||
# Check for CPU limit
|
||||
has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0")
|
||||
# Check for memory limit
|
||||
has_memory=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "memory:" || echo "0")
|
||||
|
||||
if [[ $has_cpu -eq 0 ]]; then
|
||||
print_fail " $service: Missing cpus limit"
|
||||
((NON_COMPLIANT++)) || true
|
||||
fi
|
||||
|
||||
if [[ $has_memory -eq 0 ]]; then
|
||||
print_fail " $service: Missing memory limit"
|
||||
((NON_COMPLIANT++)) || true
|
||||
fi
|
||||
|
||||
if [[ $has_cpu -gt 0 && $has_memory -gt 0 ]]; then
|
||||
# Extract limits for reporting
|
||||
cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "')
|
||||
mem_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '")
|
||||
|
||||
print_pass " $service: cpus: $cpu_limit, memory: $mem_limit"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $NON_COMPLIANT -eq 0 ]]; then
|
||||
print_pass "INF-03 compliance: All services have resource limits"
|
||||
else
|
||||
print_fail "INF-03 compliance: $NON_COMPLIANT services missing limits"
|
||||
fi
|
||||
|
||||
# Test 5: Verify specific EC2 instance type parallels
|
||||
print_test "Verifying EC2 instance type parallels"
|
||||
INSTANCE_TYPES=0
|
||||
|
||||
for service in $SERVICES; do
|
||||
cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "' || echo "")
|
||||
mem_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '"" || echo "")
|
||||
|
||||
# Map to EC2 instance types
|
||||
if [[ -n "$cpu_limit" && -n "$mem_limit" ]]; then
|
||||
case "$cpu_limit:$mem_limit" in
|
||||
"0.5:512M"|"0.5:512")
|
||||
instance_type="t2.nano"
|
||||
;;
|
||||
"1:1G"|"1:1024M"|"1:1024")
|
||||
instance_type="t2.micro"
|
||||
;;
|
||||
"1:2G"|"1:2048M"|"1:2048")
|
||||
instance_type="t2.small"
|
||||
;;
|
||||
"2:4G"|"2:4096M"|"2:4096")
|
||||
instance_type="t2.medium"
|
||||
;;
|
||||
"2:8G"|"2:8192M"|"2:8192")
|
||||
instance_type="m5.large"
|
||||
;;
|
||||
*)
|
||||
instance_type="custom"
|
||||
;;
|
||||
esac
|
||||
|
||||
print_info " $service → $instance_type (cpus: $cpu_limit, memory: $mem_limit)"
|
||||
((INSTANCE_TYPES++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $INSTANCE_TYPES -gt 0 ]]; then
|
||||
print_pass "EC2 instance type mapping complete for $INSTANCE_TYPES services"
|
||||
else
|
||||
print_fail "No services with valid resource limits found"
|
||||
fi
|
||||
|
||||
# Test 6: Verify resource limit formats
|
||||
print_test "Verifying resource limit formats"
|
||||
FORMAT_OK=0
|
||||
|
||||
for service in $SERVICES; do
|
||||
cpu_limit=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "' || echo "")
|
||||
|
||||
# CPU limit should be a number or decimal
|
||||
if [[ -n "$cpu_limit" ]]; then
|
||||
if [[ "$cpu_limit" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
|
||||
((FORMAT_OK++)) || true
|
||||
else
|
||||
print_fail " $service: Invalid CPU format: $cpu_limit"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $FORMAT_OK -eq $SERVICE_COUNT ]]; then
|
||||
print_pass "All resource limits use valid format"
|
||||
else
|
||||
print_fail "Some resource limits have invalid format"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
print_header "Resource Limits Verification Summary"
|
||||
|
||||
echo -e "Tests run: $((pass_count + fail_count))"
|
||||
echo -e "${GREEN}Passed: $pass_count${NC}"
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e "${RED}Failed: $fail_count${NC}"
|
||||
fi
|
||||
|
||||
if [[ $fail_count -eq 0 ]]; then
|
||||
echo -e "\n${GREEN}${BOLD}✓ ALL RESOURCE LIMITS CHECKS PASSED${NC}"
|
||||
echo -e "\nINF-03 compliance verified!"
|
||||
exit 0
|
||||
else
|
||||
echo -e "\n${RED}Some resource limits checks failed${NC}"
|
||||
echo -e "Please ensure all services have cpus and memory limits."
|
||||
exit 1
|
||||
fi
|
||||
255
labs/lab-03-compute/tests/02-healthcheck-test.sh
Executable file
255
labs/lab-03-compute/tests/02-healthcheck-test.sh
Executable file
@@ -0,0 +1,255 @@
|
||||
#!/bin/bash
|
||||
# Test 02: Healthcheck Configuration
|
||||
# Verifies that docker-compose.yml services have healthchecks configured
|
||||
# Usage: bash labs/lab-03-compute/tests/02-healthcheck-test.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
|
||||
|
||||
# Counter helpers
|
||||
pass_count=0
|
||||
fail_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
|
||||
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
}
|
||||
|
||||
print_test() {
|
||||
echo -e "\n${BLUE}[TEST]${NC} $1"
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e " ${GREEN}[✓]${NC} $1"
|
||||
inc_pass
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e " ${RED}[✗]${NC} $1"
|
||||
inc_fail
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e " ${BLUE}[i]${NC} $1"
|
||||
}
|
||||
|
||||
# Main verification
|
||||
print_header "Lab 03 Healthcheck Verification"
|
||||
|
||||
cd "$LAB_DIR"
|
||||
|
||||
# Test 1: docker-compose.yml exists
|
||||
print_test "Verifying docker-compose.yml exists"
|
||||
if [[ -f "docker-compose.yml" ]]; then
|
||||
print_pass "docker-compose.yml found"
|
||||
else
|
||||
print_fail "docker-compose.yml not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: docker-compose.yml is valid
|
||||
print_test "Validating docker-compose.yml syntax"
|
||||
if docker compose config &> /dev/null; then
|
||||
print_pass "docker-compose.yml has valid syntax"
|
||||
else
|
||||
print_fail "docker-compose.yml has syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: Services are defined
|
||||
print_test "Verifying services are defined"
|
||||
SERVICES=$(docker compose config --services 2>/dev/null)
|
||||
SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
|
||||
if [[ $SERVICE_COUNT -gt 0 ]]; then
|
||||
print_pass "Found $SERVICE_COUNT services"
|
||||
else
|
||||
print_fail "No services defined"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 4: Healthcheck configuration check
|
||||
print_test "Checking healthcheck configuration"
|
||||
MISSING_HEALTHCHECK=0
|
||||
|
||||
for service in $SERVICES; do
|
||||
# Check for healthcheck section
|
||||
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
|
||||
|
||||
if [[ $has_healthcheck -eq 0 ]]; then
|
||||
print_fail " $service: Missing healthcheck section"
|
||||
((MISSING_HEALTHCHECK++)) || true
|
||||
else
|
||||
# Verify healthcheck has test command
|
||||
has_test=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep -c "test:" || echo "0")
|
||||
|
||||
if [[ $has_test -eq 0 ]]; then
|
||||
print_fail " $service: Missing healthcheck test command"
|
||||
((MISSING_HEALTHCHECK++)) || true
|
||||
else
|
||||
# Extract test command for reporting
|
||||
test_cmd=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "test:" | head -1 | sed 's/.*test: //' | tr -d '"')
|
||||
print_pass " $service: healthcheck configured"
|
||||
print_info " test: $test_cmd"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $MISSING_HEALTHCHECK -eq 0 ]]; then
|
||||
print_pass "All services have healthchecks configured"
|
||||
else
|
||||
print_fail "$MISSING_HEALTHCHECK services missing healthchecks"
|
||||
fi
|
||||
|
||||
# Test 5: Healthcheck parameters verification
|
||||
print_test "Verifying healthcheck parameters"
|
||||
PARAMS_OK=0
|
||||
|
||||
for service in $SERVICES; do
|
||||
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
|
||||
|
||||
if [[ $has_healthcheck -gt 0 ]]; then
|
||||
# Check for interval
|
||||
has_interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "interval:" || echo "0")
|
||||
# Check for timeout
|
||||
has_timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "timeout:" || echo "0")
|
||||
# Check for retries
|
||||
has_retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "retries:" || echo "0")
|
||||
|
||||
interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "N/A")
|
||||
timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "N/A")
|
||||
retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "N/A")
|
||||
|
||||
if [[ $has_interval -gt 0 && $has_timeout -gt 0 && $has_retries -gt 0 ]]; then
|
||||
print_pass " $service: interval=$interval, timeout=$timeout, retries=$retries"
|
||||
((PARAMS_OK++)) || true
|
||||
else
|
||||
print_info " $service: interval=$interval, timeout=$timeout, retries=$retries"
|
||||
# Check for start_period (optional)
|
||||
has_start_period=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep -c "start_period:" || echo "0")
|
||||
if [[ $has_start_period -gt 0 ]]; then
|
||||
start_period=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "start_period:" | sed 's/.*start_period: //' | tr -d " '"" || echo "")
|
||||
print_info " start_period=$start_period"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $PARAMS_OK -gt 0 ]]; then
|
||||
print_pass "$PARAMS_OK services have complete healthcheck parameters"
|
||||
else
|
||||
print_fail "No services with complete healthcheck parameters"
|
||||
fi
|
||||
|
||||
# Test 6: Healthcheck command types
|
||||
print_test "Analyzing healthcheck command types"
|
||||
HTTP_COUNT=0
|
||||
CMD_SHELL_COUNT=0
|
||||
CMD_COUNT=0
|
||||
CUSTOM_COUNT=0
|
||||
|
||||
for service in $SERVICES; do
|
||||
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
|
||||
|
||||
if [[ $has_healthcheck -gt 0 ]]; then
|
||||
# Get test command
|
||||
test_section=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" || echo "")
|
||||
|
||||
if echo "$test_section" | grep -q "CMD-SHELL"; then
|
||||
print_info " $service: CMD-SHELL healthcheck"
|
||||
((CMD_SHELL_COUNT++)) || true
|
||||
elif echo "$test_section" | grep -q "CMD"; then
|
||||
print_info " $service: CMD healthcheck"
|
||||
((CMD_COUNT++)) || true
|
||||
else
|
||||
print_info " $service: Custom healthcheck format"
|
||||
((CUSTOM_COUNT++)) || true
|
||||
fi
|
||||
|
||||
# Check if HTTP-based
|
||||
if echo "$test_section" | grep -qiE "(wget|curl|http)"; then
|
||||
((HTTP_COUNT++)) || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
print_info "Healthcheck types: CMD-SHELL ($CMD_SHELL_COUNT), CMD ($CMD_COUNT), Custom ($CUSTOM_COUNT)"
|
||||
if [[ $HTTP_COUNT -gt 0 ]]; then
|
||||
print_pass "$HTTP_COUNT services use HTTP-based healthchecks"
|
||||
fi
|
||||
|
||||
# Test 7: ELB Health Check Parallel verification
|
||||
print_test "Verifying ELB Health Check parallelism"
|
||||
ELB_PARALLEL=0
|
||||
|
||||
for service in $SERVICES; do
|
||||
has_healthcheck=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
|
||||
|
||||
if [[ $has_healthcheck -gt 0 ]]; then
|
||||
interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "")
|
||||
timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "")
|
||||
retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 10 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "")
|
||||
|
||||
# Map to ELB defaults
|
||||
if [[ "$interval" == "30s" || "$interval" =~ ^30 ]]; then
|
||||
elb_interval="default (30s)"
|
||||
((ELB_PARALLEL++)) || true
|
||||
else
|
||||
elb_interval="custom ($interval)"
|
||||
fi
|
||||
|
||||
if [[ "$timeout" == "5s" || "$timeout" =~ ^5 ]]; then
|
||||
elb_timeout="default (5s)"
|
||||
((ELB_PARALLEL++)) || true
|
||||
else
|
||||
elb_timeout="custom ($timeout)"
|
||||
fi
|
||||
|
||||
if [[ "$retries" == "3" || "$retries" =~ ^3 ]]; then
|
||||
elb_retries="default (3)"
|
||||
((ELB_PARALLEL++)) || true
|
||||
else
|
||||
elb_retries="custom ($retries)"
|
||||
fi
|
||||
|
||||
print_info " $service: ELB parallel (interval: $elb_interval, timeout: $elb_timeout, retries: $elb_retries)"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $ELB_PARALLEL -gt 0 ]]; then
|
||||
print_pass "ELB Health Check parallelism verified"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
print_header "Healthcheck Verification Summary"
|
||||
|
||||
echo -e "Tests run: $((pass_count + fail_count))"
|
||||
echo -e "${GREEN}Passed: $pass_count${NC}"
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e "${RED}Failed: $fail_count${NC}"
|
||||
fi
|
||||
|
||||
if [[ $fail_count -eq 0 ]]; then
|
||||
echo -e "\n${GREEN}${BOLD}✓ ALL HEALTHCHECK CHECKS PASSED${NC}"
|
||||
echo -e "\nHealthcheck configuration verified!"
|
||||
exit 0
|
||||
else
|
||||
echo -e "\n${RED}Some healthcheck checks failed${NC}"
|
||||
echo -e "Please ensure all services have healthchecks configured."
|
||||
exit 1
|
||||
fi
|
||||
287
labs/lab-03-compute/tests/03-enforcement-test.sh
Executable file
287
labs/lab-03-compute/tests/03-enforcement-test.sh
Executable file
@@ -0,0 +1,287 @@
|
||||
#!/bin/bash
|
||||
# Test 03: Resource Enforcement Verification
|
||||
# Verifies that resource limits are actually enforced using docker stats
|
||||
# Usage: bash labs/lab-03-compute/tests/03-enforcement-test.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
|
||||
|
||||
# Counter helpers
|
||||
pass_count=0
|
||||
fail_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
|
||||
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
}
|
||||
|
||||
print_test() {
|
||||
echo -e "\n${BLUE}[TEST]${NC} $1"
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e " ${GREEN}[✓]${NC} $1"
|
||||
inc_pass
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e " ${RED}[✗]${NC} $1"
|
||||
inc_fail
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e " ${BLUE}[i]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e " ${YELLOW}[!]${NC} $1"
|
||||
}
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
print_info "Cleaning up test containers..."
|
||||
cd "$LAB_DIR"
|
||||
docker compose down -v 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Set trap for cleanup
|
||||
trap cleanup EXIT
|
||||
|
||||
# Main verification
|
||||
print_header "Lab 03 Resource Enforcement Verification"
|
||||
|
||||
cd "$LAB_DIR"
|
||||
|
||||
# Test 1: Verify docker-compose.yml exists and is valid
|
||||
print_test "Verifying docker-compose.yml exists and is valid"
|
||||
if [[ ! -f "docker-compose.yml" ]]; then
|
||||
print_fail "docker-compose.yml not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! docker compose config &> /dev/null; then
|
||||
print_fail "docker-compose.yml has syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
print_pass "docker-compose.yml is valid"
|
||||
|
||||
# Test 2: Start services
|
||||
print_test "Starting Docker Compose services"
|
||||
if docker compose up -d &> /dev/null; then
|
||||
print_pass "Services started successfully"
|
||||
sleep 5 # Give services time to start
|
||||
else
|
||||
print_fail "Failed to start services"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: Verify containers are running
|
||||
print_test "Verifying containers are running"
|
||||
RUNNING_CONTAINERS=$(docker compose ps --services --filter "status=running" | wc -l)
|
||||
if [[ $RUNNING_CONTAINERS -ge 1 ]]; then
|
||||
print_pass "Services running: $RUNNING_CONTAINERS containers"
|
||||
docker compose ps
|
||||
else
|
||||
print_fail "Not enough containers running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 4: Check resource limits are applied
|
||||
print_test "Verifying resource limits are applied to containers"
|
||||
LIMITS_APPLIED=0
|
||||
|
||||
for service in $(docker compose config --services); do
|
||||
container_name="lab03-$service"
|
||||
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
|
||||
# Get CPU limit (NanoCPUs: 1e9 = 1 CPU core)
|
||||
nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
|
||||
mem_limit=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
|
||||
|
||||
# Convert NanoCPUs to CPU cores
|
||||
if [[ $nano_cpus -gt 0 ]]; then
|
||||
cpu_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A")
|
||||
else
|
||||
cpu_cores="N/A"
|
||||
fi
|
||||
|
||||
# Convert memory to GB/MB
|
||||
if [[ $mem_limit -gt 0 ]]; then
|
||||
if [[ $mem_limit -ge 1073741824 ]]; then
|
||||
mem_gb=$(echo "scale=2; $mem_limit / 1073741824" | bc 2>/dev/null || echo "N/A")
|
||||
mem_display="${mem_gb}G"
|
||||
else
|
||||
mem_mb=$(echo "scale=0; $mem_limit / 1048576" | bc 2>/dev/null || echo "N/A")
|
||||
mem_display="${mem_mb}M"
|
||||
fi
|
||||
else
|
||||
mem_display="N/A"
|
||||
fi
|
||||
|
||||
if [[ "$cpu_cores" != "N/A" && "$mem_display" != "N/A" ]]; then
|
||||
print_pass " $container_name: cpus: $cpu_cores, memory: $mem_display"
|
||||
((LIMITS_APPLIED++)) || true
|
||||
else
|
||||
print_fail " $container_name: Could not read limits (cpus: $cpu_cores, memory: $mem_display)"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $LIMITS_APPLIED -gt 0 ]]; then
|
||||
print_pass "Resource limits applied to $LIMITS_APPLIED containers"
|
||||
else
|
||||
print_fail "No containers with resource limits found"
|
||||
fi
|
||||
|
||||
# Test 5: Monitor resource usage with docker stats
|
||||
print_test "Monitoring resource usage with docker stats"
|
||||
STATS_OK=0
|
||||
|
||||
# Get initial stats
|
||||
print_info "Current resource usage:"
|
||||
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" 2>/dev/null || true
|
||||
|
||||
for service in $(docker compose config --services); do
|
||||
container_name="lab03-$service"
|
||||
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
|
||||
# Get CPU percentage and memory usage
|
||||
cpu_percent=$(docker stats "$container_name" --no-stream --format "{{.CPUPerc}}" 2>/dev/null | sed 's/%//' || echo "N/A")
|
||||
mem_usage=$(docker stats "$container_name" --no-stream --format "{{.MemUsage}}" 2>/dev/null || echo "N/A")
|
||||
|
||||
if [[ "$cpu_percent" != "N/A" && "$mem_usage" != "N/A" ]]; then
|
||||
print_info " $container_name: CPU: ${cpu_percent}%, Memory: $mem_usage"
|
||||
((STATS_OK++)) || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $STATS_OK -gt 0 ]]; then
|
||||
print_pass "docker stats successfully monitored $STATS_OK containers"
|
||||
else
|
||||
print_fail "Failed to monitor container stats"
|
||||
fi
|
||||
|
||||
# Test 6: Verify health status
|
||||
print_test "Verifying container health status"
|
||||
HEALTHY_COUNT=0
|
||||
|
||||
for service in $(docker compose config --services); do
|
||||
container_name="lab03-$service"
|
||||
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
|
||||
# Get health status
|
||||
health_status=$(docker inspect "$container_name" --format '{{.State.Health.Status}}' 2>/dev/null || echo "no-healthcheck")
|
||||
|
||||
if [[ "$health_status" == "healthy" ]]; then
|
||||
print_pass " $container_name: $health_status"
|
||||
((HEALTHY_COUNT++)) || true
|
||||
elif [[ "$health_status" == "no-healthcheck" ]]; then
|
||||
print_info " $container_name: no healthcheck configured"
|
||||
else
|
||||
print_info " $container_name: $health_status"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $HEALTHY_COUNT -gt 0 ]]; then
|
||||
print_pass "$HEALTHY_COUNT containers are healthy"
|
||||
else
|
||||
print_warning "No healthy containers (services may still be starting)"
|
||||
fi
|
||||
|
||||
# Test 7: Stress test for resource enforcement (if stress container exists)
|
||||
print_test "Stress testing resource enforcement (if stress container available)"
|
||||
|
||||
STRESS_SERVICE=$(docker compose config --services | grep -E "(stress|test)" || echo "")
|
||||
if [[ -n "$STRESS_SERVICE" ]]; then
|
||||
print_info "Found stress service: $STRESS_SERVICE"
|
||||
|
||||
# Get the container name
|
||||
stress_container="lab03-$STRESS_SERVICE"
|
||||
if docker ps --format '{{.Names}}' | grep -q "$stress_container"; then
|
||||
# Get limits
|
||||
nano_cpus=$(docker inspect "$stress_container" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
|
||||
mem_limit=$(docker inspect "$stress_container" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
|
||||
|
||||
cpu_limit_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A")
|
||||
if [[ $mem_limit -ge 1073741824 ]]; then
|
||||
mem_limit_display=$(echo "scale=2; $mem_limit / 1073741824" | bc 2>/dev/null || echo "N/A")"G"
|
||||
else
|
||||
mem_limit_display=$(echo "scale=0; $mem_limit / 1048576" | bc 2>/dev/null || echo "N/A")"M"
|
||||
fi
|
||||
|
||||
print_info "Limits: cpus: $cpu_limit_cores, memory: $mem_limit_display"
|
||||
|
||||
# Monitor during stress test
|
||||
print_info "Monitoring during stress (10 seconds)..."
|
||||
for i in {1..5}; do
|
||||
cpu_percent=$(docker stats "$stress_container" --no-stream --format "{{.CPUPerc}}" 2>/dev/null | sed 's/%//' || echo "N/A")
|
||||
mem_usage=$(docker stats "$stress_container" --no-stream --format "{{.MemUsage}}" 2>/dev/null || echo "N/A")
|
||||
print_info " Sample $i: CPU: ${cpu_percent}%, Memory: $mem_usage"
|
||||
sleep 2
|
||||
done
|
||||
|
||||
print_pass "Stress test completed"
|
||||
fi
|
||||
else
|
||||
print_info "No stress service found - skipping stress test"
|
||||
fi
|
||||
|
||||
# Test 8: Verify resource limit enforcement
|
||||
print_test "Verifying resource limits are enforced"
|
||||
ENFORCED=0
|
||||
|
||||
for service in $(docker compose config --services); do
|
||||
container_name="lab03-$service"
|
||||
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
|
||||
# Check if limits are set (not 0 or -1)
|
||||
nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
|
||||
mem_limit=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
|
||||
|
||||
if [[ $nano_cpus -gt 0 && $mem_limit -gt 0 ]]; then
|
||||
print_pass " $container_name: Limits enforced (CPU and memory)"
|
||||
((ENFORCED++)) || true
|
||||
else
|
||||
print_fail " $container_name: Limits not enforced (CPU: $nano_cpus, Memory: $mem_limit)"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $ENFORCED -gt 0 ]]; then
|
||||
print_pass "Resource limits enforced for $ENFORCED containers"
|
||||
else
|
||||
print_fail "Resource limits not enforced for any container"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
print_header "Resource Enforcement Verification Summary"
|
||||
|
||||
echo -e "Tests run: $((pass_count + fail_count))"
|
||||
echo -e "${GREEN}Passed: $pass_count${NC}"
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e "${RED}Failed: $fail_count${NC}"
|
||||
fi
|
||||
|
||||
if [[ $fail_count -eq 0 ]]; then
|
||||
echo -e "\n${GREEN}${BOLD}✓ ALL ENFORCEMENT CHECKS PASSED${NC}"
|
||||
echo -e "\nResource limits are properly enforced!"
|
||||
exit 0
|
||||
else
|
||||
echo -e "\n${RED}Some enforcement checks failed${NC}"
|
||||
echo -e "Please verify resource limit configuration."
|
||||
exit 1
|
||||
fi
|
||||
84
labs/lab-03-compute/tests/04-verify-infrastructure.sh
Executable file
84
labs/lab-03-compute/tests/04-verify-infrastructure.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
# Infrastructure Verification for Lab 03
|
||||
# Verifies docker-compose.yml is correctly deployed with resource limits and healthchecks
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; BLUE='\033[0;34m'; YELLOW='\033[1;33m'; BOLD='\033[1m'; NC='\033[0m'
|
||||
pass_count=0; fail_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
|
||||
print_header() {
|
||||
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
|
||||
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
}
|
||||
|
||||
print_test() { echo -e "\n${BLUE}[TEST]${NC} $1"; }
|
||||
print_pass() { echo -e " ${GREEN}[✓]${NC} $1"; inc_pass; }
|
||||
print_fail() { echo -e " ${RED}[✗]${NC} $1"; inc_fail; }
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
print_header "Lab 03 Infrastructure Verification"
|
||||
|
||||
# File and syntax
|
||||
print_test "Verifying docker-compose.yml exists and valid"
|
||||
[[ -f "docker-compose.yml" ]] && print_pass "docker-compose.yml found" || { print_fail "docker-compose.yml not found"; exit 1; }
|
||||
docker compose config &> /dev/null && print_pass "Syntax valid" || { print_fail "Syntax error"; exit 1; }
|
||||
|
||||
# Services and limits
|
||||
print_test "Checking INF-03 compliance (resource limits)"
|
||||
SERVICES=$(docker compose config --services 2>/dev/null)
|
||||
NON_COMPLIANT=0
|
||||
for s in $SERVICES; do
|
||||
has_cpu=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep -c "cpus:" || echo "0")
|
||||
has_mem=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep -c "memory:" || echo "0")
|
||||
if [[ $has_cpu -eq 0 || $has_mem -eq 0 ]]; then
|
||||
print_fail " $s: missing limits"
|
||||
((NON_COMPLIANT++)) || true
|
||||
else
|
||||
cpu_val=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "')
|
||||
mem_val=$(docker compose config 2>/dev/null | grep -A30 "^$s:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '""")
|
||||
print_pass " $s: cpus=$cpu_val, memory=$mem_val"
|
||||
fi
|
||||
done
|
||||
[[ $NON_COMPLIANT -eq 0 ]] && print_pass "INF-03 COMPLIANT" || print_fail "INF-03 VIOLATION: $NON_COMPLIANT services"
|
||||
|
||||
# Healthchecks
|
||||
print_test "Verifying healthcheck configuration"
|
||||
MISSING_HC=0
|
||||
for s in $SERVICES; do
|
||||
has_hc=$(docker compose config 2>/dev/null | grep -A50 "^$s:" | grep -c "healthcheck:" || echo "0")
|
||||
[[ $has_hc -gt 0 ]] && print_pass " $s: healthcheck configured" || { print_fail " $s: missing healthcheck"; ((MISSING_HC++)); }
|
||||
done
|
||||
[[ $MISSING_HC -eq 0 ]] && print_pass "All services have healthchecks" || print_fail "$MISSING_HC missing healthchecks"
|
||||
|
||||
# Deploy and verify
|
||||
print_test "Starting services"
|
||||
docker compose up -d &> /dev/null && print_pass "Services started" && sleep 8 || { print_fail "Failed to start"; exit 1; }
|
||||
|
||||
print_test "Checking running containers"
|
||||
RUNNING=$(docker compose ps --services --filter "status=running" | wc -l)
|
||||
[[ $RUNNING -ge 4 ]] && print_pass "$RUNNING containers running" || print_fail "Only $RUNNING containers"
|
||||
|
||||
# Enforcement
|
||||
print_test "Verifying resource limits enforced"
|
||||
ENFORCED=0
|
||||
for s in $SERVICES; do
|
||||
c="lab03-$s"
|
||||
if docker ps --format '{{.Names}}' | grep -q "$c"; then
|
||||
nano_cpus=$(docker inspect "$c" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
|
||||
mem_bytes=$(docker inspect "$c" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
|
||||
[[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]] && print_pass " $c: limits enforced" || print_fail " $c: limits not applied"
|
||||
[[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]] && ((ENFORCED++)) || true
|
||||
fi
|
||||
done
|
||||
[[ $ENFORCED -gt 0 ]] && print_pass "Limits enforced for $ENFORCED containers"
|
||||
|
||||
# Summary
|
||||
print_header "Summary"
|
||||
echo "Tests: $((pass_count + fail_count)) | ${GREEN}Passed: $pass_count${NC} ${RED}Failed: $fail_count${NC}"
|
||||
[[ $fail_count -eq 0 ]] && { echo -e "\n${GREEN}${BOLD}✓ ALL CHECKS PASSED${NC}\n"; exit 0; } || { echo -e "\n${RED}Some checks failed${NC}\n"; exit 1; }
|
||||
331
labs/lab-03-compute/tests/99-final-verification.sh
Executable file
331
labs/lab-03-compute/tests/99-final-verification.sh
Executable file
@@ -0,0 +1,331 @@
|
||||
#!/bin/bash
|
||||
# Final Verification: Lab 03 - Compute & EC2
|
||||
# Comprehensive end-to-end verification for students
|
||||
# Usage: bash labs/lab-03-compute/tests/99-final-verification.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
|
||||
|
||||
# Counter helpers
|
||||
pass_count=0
|
||||
fail_count=0
|
||||
|
||||
inc_pass() { ((pass_count++)) || true; }
|
||||
inc_fail() { ((fail_count++)) || true; }
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
|
||||
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
}
|
||||
|
||||
print_test() {
|
||||
echo -e "\n${BLUE}[TEST]${NC} $1"
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e " ${GREEN}[✓]${NC} $1"
|
||||
inc_pass
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e " ${RED}[✗]${NC} $1"
|
||||
inc_fail
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e " ${BLUE}[i]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e " ${YELLOW}[!]${NC} $1"
|
||||
}
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
print_info "\nCleaning up..."
|
||||
cd "$LAB_DIR"
|
||||
docker compose down -v 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Set trap for cleanup on exit
|
||||
trap cleanup EXIT
|
||||
|
||||
# Main verification
|
||||
print_header "Lab 03 Final Verification - Compute & EC2"
|
||||
|
||||
cd "$LAB_DIR"
|
||||
|
||||
echo -e "\n${BLUE}This script verifies:${NC}"
|
||||
echo -e " • Resource limits (INF-03 compliance)"
|
||||
echo -e " • Healthcheck configuration"
|
||||
echo -e " • Resource enforcement with docker stats"
|
||||
echo -e " • EC2 instance type parallels"
|
||||
echo -e " • ELB health check parallels"
|
||||
|
||||
# === SECTION 1: File and Syntax ===
|
||||
print_header "Section 1: Files and Syntax"
|
||||
|
||||
# Test 1: docker-compose.yml exists
|
||||
print_test "Verifying docker-compose.yml exists"
|
||||
if [[ -f "docker-compose.yml" ]]; then
|
||||
print_pass "docker-compose.yml found"
|
||||
else
|
||||
print_fail "docker-compose.yml not found - create it first!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: Syntax validation
|
||||
print_test "Validating docker-compose.yml syntax"
|
||||
if docker compose config &> /dev/null; then
|
||||
print_pass "docker-compose.yml syntax is valid"
|
||||
else
|
||||
print_fail "docker-compose.yml has syntax errors - run 'docker compose config' to debug"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: Services defined
|
||||
print_test "Checking defined services"
|
||||
SERVICES=$(docker compose config --services 2>/dev/null)
|
||||
SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
|
||||
if [[ $SERVICE_COUNT -ge 3 ]]; then
|
||||
print_pass "Found $SERVICE_COUNT services (minimum 3 required)"
|
||||
else
|
||||
print_fail "Only $SERVICE_COUNT services found (need at least 3)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# === SECTION 2: INF-03 Compliance ===
|
||||
print_header "Section 2: INF-03 Compliance (Resource Limits)"
|
||||
|
||||
print_test "Verifying all services have resource limits"
|
||||
NON_COMPLIANT=0
|
||||
LIMITS_SUMMARY=""
|
||||
|
||||
for service in $SERVICES; do
|
||||
# Check for resource limits
|
||||
has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0")
|
||||
has_memory=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "memory:" || echo "0")
|
||||
|
||||
if [[ $has_cpu -eq 0 ]]; then
|
||||
print_fail " $service: Missing cpus limit"
|
||||
((NON_COMPLIANT++)) || true
|
||||
elif [[ $has_memory -eq 0 ]]; then
|
||||
print_fail " $service: Missing memory limit"
|
||||
((NON_COMPLIANT++)) || true
|
||||
else
|
||||
cpu_val=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "cpus:" | sed 's/.*cpus: //' | tr -d ' "')
|
||||
mem_val=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep "memory:" | sed 's/.*memory: //' | tr -d " '"")
|
||||
|
||||
# Map to EC2 instance type
|
||||
case "$cpu_val:$mem_val" in
|
||||
"0.5:512M"|"0.5:512") instance="t2.nano" ;;
|
||||
"1:1G"|"1:1024M") instance="t2.micro" ;;
|
||||
"1:2G"|"1:2048M") instance="t2.small" ;;
|
||||
"2:4G"|"2:4096M") instance="t2.medium" ;;
|
||||
"2:8G"|"2:8192M") instance="m5.large" ;;
|
||||
"4:16G"|"4:16384M") instance="m5.xlarge" ;;
|
||||
*) instance="custom" ;;
|
||||
esac
|
||||
|
||||
print_pass " $service: cpus=$cpu_val, memory=$mem_val → $instance"
|
||||
LIMITS_SUMMARY="$LIMITS_SUMMARY\n • $service → $instance"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $NON_COMPLIANT -eq 0 ]]; then
|
||||
print_pass "✓ INF-03 COMPLIANT: All services have resource limits"
|
||||
else
|
||||
print_fail "✗ INF-03 VIOLATION: $NON_COMPLIANT services missing limits"
|
||||
fi
|
||||
|
||||
# === SECTION 3: Healthcheck Configuration ===
|
||||
print_header "Section 3: Healthcheck Configuration"
|
||||
|
||||
print_test "Verifying healthcheck configuration"
|
||||
MISSING_HC=0
|
||||
HC_SUMMARY=""
|
||||
|
||||
for service in $SERVICES; do
|
||||
has_hc=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
|
||||
|
||||
if [[ $has_hc -eq 0 ]]; then
|
||||
print_fail " $service: Missing healthcheck"
|
||||
((MISSING_HC++)) || true
|
||||
else
|
||||
interval=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "interval:" | sed 's/.*interval: //' | tr -d " '"" || echo "N/A")
|
||||
timeout=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "timeout:" | sed 's/.*timeout: //' | tr -d " '"" || echo "N/A")
|
||||
retries=$(docker compose config 2>/dev/null | grep -A 60 "^$service:" | grep -A 5 "healthcheck:" | grep "retries:" | sed 's/.*retries: //' | tr -d " '"" || echo "N/A")
|
||||
|
||||
print_pass " $service: interval=$interval, timeout=$timeout, retries=$retries"
|
||||
HC_SUMMARY="$HC_SUMMARY\n • $service: interval=$interval, timeout=$timeout, retries=$retries"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $MISSING_HC -eq 0 ]]; then
|
||||
print_pass "✓ All services have healthchecks configured"
|
||||
else
|
||||
print_fail "✗ $MISSING_HC services missing healthchecks"
|
||||
fi
|
||||
|
||||
# === SECTION 4: Infrastructure Deployment ===
|
||||
print_header "Section 4: Infrastructure Deployment"
|
||||
|
||||
print_test "Starting services"
|
||||
if docker compose up -d &> /dev/null; then
|
||||
print_pass "Services started successfully"
|
||||
sleep 8 # Wait for services to be ready
|
||||
else
|
||||
print_fail "Failed to start services"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_test "Verifying containers are running"
|
||||
RUNNING=$(docker compose ps --services --filter "status=running" | wc -l)
|
||||
if [[ $RUNNING -ge 3 ]]; then
|
||||
print_pass "$RUNNING containers running"
|
||||
docker compose ps
|
||||
else
|
||||
print_fail "Only $RUNNING containers running (expected 3+)"
|
||||
fi
|
||||
|
||||
# === SECTION 5: Resource Enforcement ===
|
||||
print_header "Section 5: Resource Enforcement Verification"
|
||||
|
||||
print_test "Checking resource limits are applied"
|
||||
LIMITS_ENFORCED=0
|
||||
|
||||
for service in $SERVICES; do
|
||||
container_name="lab03-$service"
|
||||
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
|
||||
nano_cpus=$(docker inspect "$container_name" --format '{{.HostConfig.NanoCpus}}' 2>/dev/null || echo "0")
|
||||
mem_bytes=$(docker inspect "$container_name" --format '{{.HostConfig.Memory}}' 2>/dev/null || echo "0")
|
||||
|
||||
if [[ $nano_cpus -gt 0 && $mem_bytes -gt 0 ]]; then
|
||||
cpu_cores=$(echo "scale=2; $nano_cpus / 1000000000" | bc 2>/dev/null || echo "N/A")
|
||||
if [[ $mem_bytes -ge 1073741824 ]]; then
|
||||
mem_display=$(echo "scale=2; $mem_bytes / 1073741824" | bc)G
|
||||
else
|
||||
mem_display=$(echo "scale=0; $mem_bytes / 1048576" | bc)M
|
||||
fi
|
||||
print_pass " $container_name: ${cpu_cores} CPU, $mem_display RAM"
|
||||
((LIMITS_ENFORCED++)) || true
|
||||
else
|
||||
print_fail " $container_name: Limits not applied"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $LIMITS_ENFORCED -gt 0 ]]; then
|
||||
print_pass "✓ Resource limits enforced for $LIMITS_ENFORCED containers"
|
||||
else
|
||||
print_fail "✗ Resource limits not enforced"
|
||||
fi
|
||||
|
||||
# Test docker stats
|
||||
print_test "Monitoring resource usage"
|
||||
echo -e "\n${BLUE}Live Resource Usage:${NC}"
|
||||
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" 2>/dev/null || print_fail "docker stats failed"
|
||||
|
||||
# === SECTION 6: Health Status ===
|
||||
print_header "Section 6: Health Status Verification"
|
||||
|
||||
print_test "Checking container health status"
|
||||
HEALTHY=0
|
||||
|
||||
for service in $SERVICES; do
|
||||
container_name="lab03-$service"
|
||||
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
|
||||
health=$(docker inspect "$container_name" --format '{{.State.Health.Status}}' 2>/dev/null || echo "no-healthcheck")
|
||||
|
||||
if [[ "$health" == "healthy" ]]; then
|
||||
print_pass " $container_name: healthy ✓"
|
||||
((HEALTHY++)) || true
|
||||
elif [[ "$health" == "no-healthcheck" ]]; then
|
||||
print_warning " $container_name: no healthcheck configured"
|
||||
else
|
||||
print_info " $container_name: $health"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $HEALTHY -gt 0 ]]; then
|
||||
print_pass "✓ $HEALTHY containers are healthy"
|
||||
else
|
||||
print_warning "No healthy containers yet (may still be starting)"
|
||||
fi
|
||||
|
||||
# === SECTION 7: Cloud Parallels ===
|
||||
print_header "Section 7: Cloud Parallels Verification"
|
||||
|
||||
print_test "EC2 Instance Type Parallels"
|
||||
echo -e "${BLUE}Your Docker configuration maps to these EC2 instances:${NC}"
|
||||
echo -e "$LIMITS_SUMMARY"
|
||||
|
||||
print_test "ELB Health Check Parallels"
|
||||
echo -e "${BLUE}Your healthchecks parallel ELB health checks:${NC}"
|
||||
echo -e "$HC_SUMMARY"
|
||||
|
||||
# === FINAL SUMMARY ===
|
||||
print_header "Final Verification Summary"
|
||||
|
||||
TOTAL_TESTS=$((pass_count + fail_count))
|
||||
PASS_PERCENT=$((pass_count * 100 / TOTAL_TESTS))
|
||||
|
||||
echo -e "Total Tests: $TOTAL_TESTS"
|
||||
echo -e "${GREEN}Passed: $pass_count${NC} ($PASS_PERCENT%)"
|
||||
if [[ $fail_count -gt 0 ]]; then
|
||||
echo -e "${RED}Failed: $fail_count${NC}"
|
||||
fi
|
||||
|
||||
# INF-03 Compliance Summary
|
||||
echo -e "\n${BLUE}INF-03 Compliance:${NC}"
|
||||
if [[ $NON_COMPLIANT -eq 0 ]]; then
|
||||
echo -e " ${GREEN}✓ COMPLIANT${NC} - All services have resource limits"
|
||||
else
|
||||
echo -e " ${RED}✗ NON-COMPLIANT${NC} - $NON_COMPLIANT services missing limits"
|
||||
fi
|
||||
|
||||
# Healthcheck Summary
|
||||
echo -e "\n${BLUE}Healthcheck Coverage:${NC}"
|
||||
if [[ $MISSING_HC -eq 0 ]]; then
|
||||
echo -e " ${GREEN}✓ COMPLETE${NC} - All services have healthchecks"
|
||||
else
|
||||
echo -e " ${RED}✗ INCOMPLETE${NC} - $MISSING_HC services missing healthchecks"
|
||||
fi
|
||||
|
||||
# Final verdict
|
||||
if [[ $fail_count -eq 0 ]]; then
|
||||
echo -e "\n${GREEN}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${GREEN}${BOLD}║${NC} ${BOLD}✓✓✓ ALL VERIFICATIONS PASSED ✓✓✓${NC}"
|
||||
echo -e "${GREEN}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo -e "\n${GREEN}Congratulations! Your Lab 03 infrastructure is correctly deployed.${NC}"
|
||||
echo -e "\nYou have successfully:"
|
||||
echo -e " • Configured resource limits (EC2 instance types)"
|
||||
echo -e " • Implemented healthchecks (ELB health checks)"
|
||||
echo -e " • Verified resource enforcement with docker stats"
|
||||
echo -e "\nYou can now proceed with the tutorials!"
|
||||
exit 0
|
||||
else
|
||||
echo -e "\n${RED}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${RED}${BOLD}║${NC} ${BOLD}✗✗✗ SOME VERIFICATIONS FAILED ✗✗✗${NC}"
|
||||
echo -e "${RED}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo -e "\n${RED}Please review the failures above.${NC}"
|
||||
echo -e "\nCommon issues:"
|
||||
echo -e " • Missing resource limits → Add deploy.resources.limits to services"
|
||||
echo -e " • Missing healthchecks → Add healthcheck section to services"
|
||||
echo -e " • Services not starting → Check logs with 'docker compose logs'"
|
||||
exit 1
|
||||
fi
|
||||
79
labs/lab-03-compute/tests/quick-test.sh
Executable file
79
labs/lab-03-compute/tests/quick-test.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
# Quick Test - Lab 03: Compute & EC2
|
||||
# Fast validation for development (< 30 seconds)
|
||||
# Usage: bash labs/lab-03-compute/tests/quick-test.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
|
||||
|
||||
# Quick checks (no verbose output)
|
||||
echo -e "${BLUE}Quick Test - Lab 03: Compute & EC2${NC}\n"
|
||||
|
||||
# Check 1: File exists
|
||||
if [[ ! -f "$LAB_DIR/docker-compose.yml" ]]; then
|
||||
echo -e "${RED}✗ docker-compose.yml not found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓${NC} docker-compose.yml exists"
|
||||
|
||||
# Check 2: Syntax valid
|
||||
if ! docker compose config &> /dev/null; then
|
||||
echo -e "${RED}✗ docker-compose.yml syntax error${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓${NC} Syntax valid"
|
||||
|
||||
# Check 3: Services defined
|
||||
SERVICES=$(docker compose config --services 2>/dev/null)
|
||||
SERVICE_COUNT=$(echo "$SERVICES" | wc -l)
|
||||
if [[ $SERVICE_COUNT -lt 3 ]]; then
|
||||
echo -e "${RED}✗ Only $SERVICE_COUNT services (need 3+)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓${NC} $SERVICE_COUNT services defined"
|
||||
|
||||
# Check 4: Resource limits (INF-03)
|
||||
NON_COMPLIANT=0
|
||||
for service in $SERVICES; do
|
||||
has_cpu=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "cpus:" || echo "0")
|
||||
has_mem=$(docker compose config 2>/dev/null | grep -A 30 "^$service:" | grep -c "memory:" || echo "0")
|
||||
if [[ $has_cpu -eq 0 || $has_mem -eq 0 ]]; then
|
||||
((NON_COMPLIANT++)) || true
|
||||
fi
|
||||
done
|
||||
if [[ $NON_COMPLIANT -gt 0 ]]; then
|
||||
echo -e "${RED}✗ $NON_COMPLIANT services missing resource limits (INF-03)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓${NC} INF-03 compliant (all services have limits)"
|
||||
|
||||
# Check 5: Healthchecks
|
||||
MISSING_HC=0
|
||||
for service in $SERVICES; do
|
||||
has_hc=$(docker compose config 2>/dev/null | grep -A 50 "^$service:" | grep -c "healthcheck:" || echo "0")
|
||||
if [[ $has_hc -eq 0 ]]; then
|
||||
((MISSING_HC++)) || true
|
||||
fi
|
||||
done
|
||||
if [[ $MISSING_HC -gt 0 ]]; then
|
||||
echo -e "${YELLOW}⚠ $MISSING_HC services missing healthchecks${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✓${NC} All services have healthchecks"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo -e "\n${GREEN}${BOLD}✓ Quick test PASSED${NC}\n"
|
||||
echo -e "For full verification, run:"
|
||||
echo -e " bash tests/run-all-tests.sh"
|
||||
echo -e " bash tests/99-final-verification.sh"
|
||||
138
labs/lab-03-compute/tests/run-all-tests.sh
Executable file
138
labs/lab-03-compute/tests/run-all-tests.sh
Executable file
@@ -0,0 +1,138 @@
|
||||
#!/bin/bash
|
||||
# Run All Tests - Lab 03: Compute & EC2
|
||||
# Executes all test scripts in sequence with fail-fast behavior
|
||||
# Usage: bash labs/lab-03-compute/tests/run-all-tests.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color definitions
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Get script directory
|
||||
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LAB_DIR="$(cd "$TEST_DIR/.." && pwd)"
|
||||
|
||||
# Counter helpers
|
||||
total_passed=0
|
||||
total_failed=0
|
||||
tests_run=0
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║${NC} ${BOLD}$1${NC}"
|
||||
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
||||
}
|
||||
|
||||
print_section() {
|
||||
echo -e "\n${BLUE}═══════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE} $1${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}\n"
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
}
|
||||
|
||||
# Main execution
|
||||
print_header "Lab 03 Test Suite - Compute & EC2"
|
||||
|
||||
cd "$LAB_DIR"
|
||||
|
||||
echo -e "${BLUE}Running all tests in sequence...${NC}\n"
|
||||
|
||||
# Test 1: Resource Limits Configuration
|
||||
print_section "Test 1/4: Resource Limits Configuration"
|
||||
if bash "$TEST_DIR/01-resource-limits-test.sh"; then
|
||||
print_pass "Resource limits tests PASSED"
|
||||
((total_passed++)) || true
|
||||
else
|
||||
print_fail "Resource limits tests FAILED"
|
||||
((total_failed++)) || true
|
||||
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
|
||||
echo -e "Run individual tests to debug:"
|
||||
echo -e " bash tests/01-resource-limits-test.sh"
|
||||
exit 1
|
||||
fi
|
||||
((tests_run++)) || true
|
||||
|
||||
# Test 2: Healthcheck Configuration
|
||||
print_section "Test 2/4: Healthcheck Configuration"
|
||||
if bash "$TEST_DIR/02-healthcheck-test.sh"; then
|
||||
print_pass "Healthcheck tests PASSED"
|
||||
((total_passed++)) || true
|
||||
else
|
||||
print_fail "Healthcheck tests FAILED"
|
||||
((total_failed++)) || true
|
||||
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
|
||||
echo -e "Run individual tests to debug:"
|
||||
echo -e " bash tests/02-healthcheck-test.sh"
|
||||
exit 1
|
||||
fi
|
||||
((tests_run++)) || true
|
||||
|
||||
# Test 3: Resource Enforcement
|
||||
print_section "Test 3/4: Resource Enforcement Verification"
|
||||
if bash "$TEST_DIR/03-enforcement-test.sh"; then
|
||||
print_pass "Resource enforcement tests PASSED"
|
||||
((total_passed++)) || true
|
||||
else
|
||||
print_fail "Resource enforcement tests FAILED"
|
||||
((total_failed++)) || true
|
||||
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
|
||||
echo -e "Run individual tests to debug:"
|
||||
echo -e " bash tests/03-enforcement-test.sh"
|
||||
exit 1
|
||||
fi
|
||||
((tests_run++)) || true
|
||||
|
||||
# Test 4: Final Verification
|
||||
print_section "Test 4/4: Final Verification"
|
||||
if bash "$TEST_DIR/99-final-verification.sh"; then
|
||||
print_pass "Final verification PASSED"
|
||||
((total_passed++)) || true
|
||||
else
|
||||
print_fail "Final verification FAILED"
|
||||
((total_failed++)) || true
|
||||
echo -e "\n${YELLOW}Stopping at first failure (fail-fast mode)${NC}"
|
||||
echo -e "Run individual tests to debug:"
|
||||
echo -e " bash tests/99-final-verification.sh"
|
||||
exit 1
|
||||
fi
|
||||
((tests_run++)) || true
|
||||
|
||||
# Summary
|
||||
print_header "Test Suite Summary"
|
||||
|
||||
echo -e "Tests run: $tests_run"
|
||||
echo -e "${GREEN}Passed: $total_passed${NC}"
|
||||
if [[ $total_failed -gt 0 ]]; then
|
||||
echo -e "${RED}Failed: $total_failed${NC}"
|
||||
fi
|
||||
|
||||
if [[ $total_failed -eq 0 ]]; then
|
||||
echo -e "\n${GREEN}${BOLD}✓✓✓ ALL TESTS PASSED ✓✓✓${NC}\n"
|
||||
echo -e "Your Lab 03 infrastructure is ready!"
|
||||
echo -e "Proceed with the tutorials to learn more about:"
|
||||
echo -e " • EC2 instance type parallels"
|
||||
echo -e " • Resource limits enforcement"
|
||||
echo -e " • Healthcheck implementation"
|
||||
exit 0
|
||||
else
|
||||
echo -e "\n${RED}Some tests failed${NC}\n"
|
||||
echo -e "Run individual tests for details:"
|
||||
echo -e " bash tests/01-resource-limits-test.sh"
|
||||
echo -e " bash tests/02-healthcheck-test.sh"
|
||||
echo -e " bash tests/03-enforcement-test.sh"
|
||||
echo -e " bash tests/99-final-verification.sh"
|
||||
exit 1
|
||||
fi
|
||||
335
labs/lab-03-compute/tutorial/01-set-resource-limits.md
Normal file
335
labs/lab-03-compute/tutorial/01-set-resource-limits.md
Normal file
@@ -0,0 +1,335 @@
|
||||
# Tutorial 1: Configurare i Limiti delle Risorse
|
||||
|
||||
In questo tutorial imparerai a configurare i limiti delle risorse CPU e memoria per i container Docker, simulando i diversi **EC2 Instance Types** di AWS.
|
||||
|
||||
## Obiettivi di Apprendimento
|
||||
|
||||
Al termine di questo tutorial sarai in grado di:
|
||||
- Comprendere cosa sono gli **EC2 Instance Types** e come si applicano a Docker
|
||||
- Configurare i limiti **CPU** e **memoria** in Docker Compose
|
||||
- Mappare le configurazioni Docker alle istanze EC2
|
||||
- Verificare i limiti delle risorse con `docker stats`
|
||||
|
||||
---
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
- Docker Engine >= 24.0 installato e funzionante
|
||||
- Docker Compose V2 (`docker compose` comando disponibile)
|
||||
- Conoscenza base di Docker e docker-compose.yml
|
||||
|
||||
---
|
||||
|
||||
## Parte 1: EC2 Instance Types - Concetti Fondamentali
|
||||
|
||||
### Cos'è un EC2 Instance Type?
|
||||
|
||||
In AWS, un **Instance Type** definisce:
|
||||
- **vCPUs**: Numero di CPU virtuali
|
||||
- **Memory**: Quantità di RAM
|
||||
- **Use case**: Tipo di carico di lavoro ottimale
|
||||
|
||||
### Famiglie di Instance Types Principali
|
||||
|
||||
| Famiglia | Prefix | Use Case | Esempio |
|
||||
|----------|--------|----------|---------|
|
||||
| **Burstable** | t2, t3 | Dev/test, basso costo | t2.micro |
|
||||
| **General Purpose** | m5, m6 | Equilibrato CPU/memoria | m5.large |
|
||||
| **Compute Optimized** | c5, c6 | Alta CPU | c5.xlarge |
|
||||
| **Memory Optimized** | r5, r6 | Alta memoria | r5.large |
|
||||
|
||||
### Instance Types Comuni
|
||||
|
||||
| Instance Type | vCPUs | Memory | Use Case |
|
||||
|---------------|-------|--------|----------|
|
||||
| t2.nano | 0.5 | 512 MB | Microservizi |
|
||||
| t2.micro | 1 | 1 GB | Dev/Test |
|
||||
| t2.small | 1 | 2 GB | Web server |
|
||||
| t2.medium | 2 | 4 GB | Application server |
|
||||
| m5.large | 2 | 8 GB | Production app |
|
||||
| m5.xlarge | 4 | 16 GB | High traffic |
|
||||
|
||||
---
|
||||
|
||||
## Parte 2: Limiti delle Risorse in Docker Compose
|
||||
|
||||
### Sintassi di Base
|
||||
|
||||
In Docker Compose, i limiti delle risorse si configurano con la sezione `deploy.resources.limits`:
|
||||
|
||||
```yaml
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: nginx:alpine
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1' # Numero di CPU
|
||||
memory: 1G # Memoria (M, G)
|
||||
```
|
||||
|
||||
### Formati della Memoria
|
||||
|
||||
```yaml
|
||||
memory: 512M # 512 Megabyte
|
||||
memory: 1G # 1 Gigabyte
|
||||
memory: 1024M # 1024 Megabyte (equivale a 1G)
|
||||
```
|
||||
|
||||
### Format delle CPU
|
||||
|
||||
```yaml
|
||||
cpus: '0.5' # Mezza CPU
|
||||
cpus: '1' # 1 CPU completa
|
||||
cpus: '2' # 2 CPU complete
|
||||
cpus: '1.5' # 1.5 CPU (1 completa + 50% di un'altra)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 3: Pratica - Configurare un Container t2.micro
|
||||
|
||||
Creiamo un container simile a un'istanza **t2.micro** (1 vCPU, 1 GB RAM).
|
||||
|
||||
### Step 1: Creare il file docker-compose.yml
|
||||
|
||||
Crea il file `labs/lab-03-compute/docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
# Web server - simula t2.micro
|
||||
web:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-web
|
||||
hostname: web
|
||||
|
||||
# Resource limits - t2.micro parallel
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8080:80"
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Step 2: Verificare la sintassi
|
||||
|
||||
```bash
|
||||
cd labs/lab-03-compute
|
||||
docker compose config
|
||||
```
|
||||
|
||||
Se non ci sono errori, vedrai la configurazione completa.
|
||||
|
||||
### Step 3: Avviare il container
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Step 4: Verificare i limiti applicati
|
||||
|
||||
```bash
|
||||
# Ispeziona il container per vedere i limiti
|
||||
docker inspect lab03-web --format '{{.HostConfig.NanoCpus}}'
|
||||
# Output: 1000000000 (1e9 = 1 CPU)
|
||||
|
||||
docker inspect lab03-web --format '{{.HostConfig.Memory}}'
|
||||
# Output: 1073741824 (1 GB in byte)
|
||||
```
|
||||
|
||||
### Step 5: Monitorare l'utilizzo delle risorse
|
||||
|
||||
```bash
|
||||
# Mostra l'utilizzo in tempo reale
|
||||
docker stats lab03-web
|
||||
```
|
||||
|
||||
Premi `Ctrl+C` per uscire.
|
||||
|
||||
```bash
|
||||
# Snapshot singolo
|
||||
docker stats --no-stream
|
||||
```
|
||||
|
||||
**Output previsto:**
|
||||
```
|
||||
CONTAINER NAME CPU % MEM USAGE / LIMIT MEM %
|
||||
12345 lab03-web 0.01% 2.5MiB / 1GiB 0.24%
|
||||
```
|
||||
|
||||
Nota che il **LIMIT** è 1GiB, configurato correttamente.
|
||||
|
||||
---
|
||||
|
||||
## Parte 4: Pratica - Configurare un Container t2.small
|
||||
|
||||
Ora configuriamo un container simile a **t2.small** (1 vCPU, 2 GB RAM).
|
||||
|
||||
### Aggiungi il servizio app al docker-compose.yml:
|
||||
|
||||
```yaml
|
||||
# Application server - simula t2.small
|
||||
app:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-app
|
||||
hostname: app
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8081:80"
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Avviare e verificare
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
```bash
|
||||
# Verifica i limiti
|
||||
docker inspect lab03-app --format 'CPU: {{.HostConfig.NanoCpus}} CPUs, Memory: {{.HostConfig.Memory}} bytes'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 5: Pratica - Configurare un Container t2.medium
|
||||
|
||||
Configuriamo un container **t2.medium** (2 vCPU, 4 GB RAM).
|
||||
|
||||
### Aggiungi il servizio worker:
|
||||
|
||||
```yaml
|
||||
# Worker - simula t2.medium
|
||||
worker:
|
||||
image: alpine:3.19
|
||||
container_name: lab03-worker
|
||||
hostname: worker
|
||||
|
||||
command: ["sh", "-c", "sleep 3600"]
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Avviare e verificare
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker stats --no-stream lab03-worker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 6: Tabella di Mapping Completa
|
||||
|
||||
Ecco la tabella completa di mapping tra Docker e EC2:
|
||||
|
||||
| Docker Limits | EC2 Instance | vCPUs | Memory | Use Case |
|
||||
|---------------|--------------|-------|--------|----------|
|
||||
| `cpus: '0.5'`<br>`memory: 512M` | t2.nano | 0.5 | 512 MB | Microservizi minimi |
|
||||
| `cpus: '1'`<br>`memory: 1G` | t2.micro | 1 | 1 GB | Dev/Test |
|
||||
| `cpus: '1'`<br>`memory: 2G` | t2.small | 1 | 2 GB | Web servers |
|
||||
| `cpus: '2'`<br>`memory: 4G` | t2.medium | 2 | 4 GB | Application |
|
||||
| `cpus: '2'`<br>`memory: 8G` | m5.large | 2 | 8 GB | Production |
|
||||
| `cpus: '4'`<br>`memory: 16G` | m5.xlarge | 4 | 16 GB | High traffic |
|
||||
|
||||
---
|
||||
|
||||
## Parte 7: Verifica Finale
|
||||
|
||||
### Script di Verifica
|
||||
|
||||
Esegui questo comando per verificare tutti i limiti:
|
||||
|
||||
```bash
|
||||
# Per ogni servizio, mostra i limiti
|
||||
for service in web app worker; do
|
||||
echo "Service: $service"
|
||||
docker inspect "lab03-$service" --format ' CPUs: {{.HostConfig.NanoCpus}}'
|
||||
docker inspect "lab03-$service" --format ' Memory: {{.HostConfig.Memory}}'
|
||||
done
|
||||
```
|
||||
|
||||
### Controllo INF-03
|
||||
|
||||
Lo script di verifica del lab controllerà automaticamente che tutti i servizi abbiano i limiti configurati (requisito **INF-03**).
|
||||
|
||||
```bash
|
||||
bash tests/01-resource-limits-test.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Risoluzione Problemi
|
||||
|
||||
### Errore: "no matching resources"
|
||||
|
||||
**Causa:** I limiti specificati superano le risorse disponibili sull'host.
|
||||
|
||||
**Soluzione:**
|
||||
- Riduci i limiti CPU/memoria
|
||||
- Libera risorse sull'host
|
||||
- Usa un'istanza tipo più piccola
|
||||
|
||||
### Errore: "invalid memory format"
|
||||
|
||||
**Causa:** Formato della memoria non valido.
|
||||
|
||||
**Soluzione:**
|
||||
- Usa `M` per Megabyte (es. `512M`)
|
||||
- Usa `G` per Gigabyte (es. `1G`)
|
||||
- Non usare spazi o formati misti
|
||||
|
||||
### Container OOM Killed
|
||||
|
||||
**Causa:** Il container sta tentando di usare più memoria del limite.
|
||||
|
||||
**Soluzione:**
|
||||
- Aumenta il limite `memory`
|
||||
- Indaga il consumo di memoria dell'applicazione
|
||||
- Verifica memory leak
|
||||
|
||||
---
|
||||
|
||||
## Riepilogo
|
||||
|
||||
In questo tutorial hai imparato:
|
||||
|
||||
✓ **Concetto:** EC2 Instance Types definiscono CPU e memoria
|
||||
✓ **Sintassi:** `deploy.resources.limits` in Docker Compose
|
||||
✓ **Mapping:** Docker limits → EC2 instances
|
||||
✓ **Verifica:** `docker stats` per monitorare l'utilizzo
|
||||
✓ **Compliance:** INF-03 richiede limiti per tutti i container
|
||||
|
||||
---
|
||||
|
||||
## Prossimi Passi
|
||||
|
||||
Nel prossimo tutorial imparerai a:
|
||||
- Implementare **healthchecks** per monitorare lo stato dei servizi
|
||||
- Configurare dipendenze tra servizi con `depends_on`
|
||||
- Mappare healthchecks Docker agli **ELB Health Checks** di AWS
|
||||
|
||||
Continua con **Tutorial 2: Implementare Healthchecks** →
|
||||
347
labs/lab-03-compute/tutorial/02-implement-healthchecks.md
Normal file
347
labs/lab-03-compute/tutorial/02-implement-healthchecks.md
Normal file
@@ -0,0 +1,347 @@
|
||||
# Tutorial 2: Implementare Healthchecks
|
||||
|
||||
In questo tutorial imparerai a configurare healthchecks per i container Docker, simulando gli **ELB Health Checks** di AWS.
|
||||
|
||||
## Obiettivi di Apprendimento
|
||||
|
||||
Al termine di questo tutorial sarai in grado di:
|
||||
- Comprendere cosa sono i healthchecks e perché sono importanti
|
||||
- Configurare healthchecks HTTP, CMD e custom
|
||||
- Mappare healthchecks Docker agli ELB Health Checks
|
||||
- Monitorare lo stato di salute dei container
|
||||
|
||||
---
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
- Completamento di Tutorial 1: Configurare i Limiti delle Risorse
|
||||
- docker-compose.yml con servizi configurati
|
||||
- Container che espongono porte HTTP o servizi monitorabili
|
||||
|
||||
---
|
||||
|
||||
## Parte 1: Healthchecks - Concetti Fondamentali
|
||||
|
||||
### Cos'è un Healthcheck?
|
||||
|
||||
Un **healthcheck** è un comando periodico che verifica se un container è "sano" (healthy).
|
||||
|
||||
**Stati di un Container:**
|
||||
1. **created** - Container creato ma non avviato
|
||||
2. **starting** - Container in avvio (healthcheck in corso)
|
||||
3. **healthy** - Healthcheck passing (container OK)
|
||||
4. **unhealthy** - Healthcheck failing (container problematico)
|
||||
5. **exited** - Container terminato
|
||||
|
||||
### Perché sono Importanti?
|
||||
|
||||
- **Failover:** Sostituisce container non sani
|
||||
- **Zero-downtime:** Attiva solo container sani nel load balancer
|
||||
- **Dependencies:** Altri servizi aspettano che il container diventi healthy
|
||||
- **Monitoring:** Avvisa automaticamente su problemi
|
||||
|
||||
### Parallelismo: ELB Health Checks
|
||||
|
||||
| Docker | AWS ELB |
|
||||
|--------|---------|
|
||||
| healthcheck.test | Health check path/protocol |
|
||||
| healthcheck.interval | Health check interval (default 30s) |
|
||||
| healthcheck.timeout | Health check timeout (default 5s) |
|
||||
| healthcheck.retries | Unhealthy threshold |
|
||||
| healthcheck.start_period | Grace period |
|
||||
|
||||
---
|
||||
|
||||
## Parte 2: Sintassi Healthcheck in Docker Compose
|
||||
|
||||
### Sintassi di Base
|
||||
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
image: nginx:alpine
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
```
|
||||
|
||||
### Parametri Spiegati
|
||||
|
||||
| Parametro | Default | Descrizione |
|
||||
|-----------|---------|-------------|
|
||||
| test | - | Comando da eseguire (richiesto) |
|
||||
| interval | 30s | Frequenza del check |
|
||||
| timeout | 30s | Tempo massimo per completare |
|
||||
| retries | 3 | Tentativi prima di标记 unhealthy |
|
||||
| start_period | 0s | Grace period all'avvio |
|
||||
|
||||
---
|
||||
|
||||
## Parte 3: Pratica - HTTP Healthcheck per Web Server
|
||||
|
||||
### Step 1: Aggiungere healthcheck al servizio web
|
||||
|
||||
Modifica `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
web:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-web
|
||||
hostname: web
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
|
||||
# HTTP Healthcheck
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8080:80"
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Step 2: Avviare e verificare
|
||||
|
||||
```bash
|
||||
docker compose up -d web
|
||||
```
|
||||
|
||||
### Step 3: Monitorare lo stato di salute
|
||||
|
||||
```bash
|
||||
# Mostra stato di salute
|
||||
docker ps
|
||||
|
||||
# Output:
|
||||
# CONTAINER IMAGE STATUS
|
||||
# lab03-web nginx:alpine Up 30 seconds (healthy)
|
||||
```
|
||||
|
||||
### Step 4: Ispezionare i dettagli del healthcheck
|
||||
|
||||
```bash
|
||||
docker inspect lab03-web --format '{{json .State.Health}}' | jq
|
||||
```
|
||||
|
||||
**Output JSON:**
|
||||
```json
|
||||
{
|
||||
"Status": "healthy",
|
||||
"FailingStreak": 0,
|
||||
"Log": [
|
||||
{
|
||||
"Start": "2024-03-25T10:00:00Z",
|
||||
"End": "2024-03-25T10:00:00Z",
|
||||
"ExitCode": 0,
|
||||
"Output": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 4: Pratica - Database Healthcheck
|
||||
|
||||
### Step 1: Aggiungere servizio database
|
||||
|
||||
```yaml
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: lab03-db
|
||||
hostname: db
|
||||
|
||||
environment:
|
||||
POSTGRES_DB: lab03_db
|
||||
POSTGRES_USER: lab03_user
|
||||
POSTGRES_PASSWORD: lab03_password
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
|
||||
# Database Healthcheck
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U lab03_user -d lab03_db || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Nota che il database:
|
||||
- Ha più `retries` (5 vs 3) - i database partono più lentamente
|
||||
- Ha `start_period` più lungo (10s vs 5s) - grace period esteso
|
||||
|
||||
### Step 2: Verificare il database diventi healthy
|
||||
|
||||
```bash
|
||||
docker compose up -d db
|
||||
|
||||
# Attendere che diventi healthy
|
||||
watch -n 2 'docker ps --filter "name=lab03-db" --format "table {{.Names}}\t{{.Status}}"'
|
||||
```
|
||||
|
||||
Premi `Ctrl+C` quando vedi `(healthy)`.
|
||||
|
||||
---
|
||||
|
||||
## Parte 5: Pratica - CMD-SHELL Healthcheck
|
||||
|
||||
Per comandi più complessi, usa `CMD-SHELL`:
|
||||
|
||||
```yaml
|
||||
app:
|
||||
image: myapp:latest
|
||||
container_name: lab03-app
|
||||
hostname: app
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
|
||||
# Custom healthcheck with shell
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost/health || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Esempi di Healthcheck
|
||||
|
||||
**HTTP con curl:**
|
||||
```yaml
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
|
||||
```
|
||||
|
||||
**TCP connection:**
|
||||
```yaml
|
||||
test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"]
|
||||
```
|
||||
|
||||
**File existence:**
|
||||
```yaml
|
||||
test: ["CMD-SHELL", "test -f /tmp/ready || exit 1"]
|
||||
```
|
||||
|
||||
**Simple always-succeed:**
|
||||
```yaml
|
||||
test: ["CMD-SHELL", "exit 0"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 6: ELB Health Check Parallelism
|
||||
|
||||
### Mapping Completo
|
||||
|
||||
| Docker Healthcheck | ELB Health Check | AWS Default | Docker Default |
|
||||
|--------------------|------------------|-------------|----------------|
|
||||
| test | Protocol + Path | TCP:80 | Configurato |
|
||||
| interval | Interval | 30s | 30s |
|
||||
| timeout | Timeout | 5s | 30s |
|
||||
| retries | Unhealthy Threshold | 2 | 3 |
|
||||
| start_period | - | - | 0s |
|
||||
|
||||
### Configurazione Equivalente AWS
|
||||
|
||||
**Docker:**
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 2
|
||||
```
|
||||
|
||||
**ELB (equivalente):**
|
||||
```json
|
||||
{
|
||||
"TargetGroup": {
|
||||
"HealthCheckProtocol": "HTTP",
|
||||
"HealthCheckPath": "/health",
|
||||
"HealthCheckIntervalSeconds": 30,
|
||||
"HealthCheckTimeoutSeconds": 5,
|
||||
"UnhealthyThresholdCount": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 7: Debugging Healthchecks
|
||||
|
||||
### Problema: Container rimane "starting"
|
||||
|
||||
**Causa:** Healthcheck non passa entro `start_period`
|
||||
|
||||
**Soluzione:**
|
||||
- Aumenta `start_period`
|
||||
- Riduci `interval` per check più frequenti
|
||||
- Verifica che il servizio sia realmente pronto
|
||||
|
||||
### Problema: Container diventa "unhealthy"
|
||||
|
||||
**Debug:**
|
||||
```bash
|
||||
# Guarda i log del healthcheck
|
||||
docker inspect lab03-web --format '{{range .State.Health.Log}}{{.Start}} - {{.ExitCode}} - {{.Output}}{{"\n"}}{{end}}'
|
||||
|
||||
# Esegui il comando manualmente
|
||||
docker exec lab03-web wget --spider -q http://localhost/
|
||||
```
|
||||
|
||||
### Problema: Healthcheck troppo lento
|
||||
|
||||
**Ottimizza:**
|
||||
- Usa check HTTP leggeri (non pagine pesanti)
|
||||
- Usa check TCP invece di HTTP se possibile
|
||||
- Riduci `timeout` se il check è veloce
|
||||
|
||||
---
|
||||
|
||||
## Riepilogo
|
||||
|
||||
In questo tutorial hai imparato:
|
||||
|
||||
✓ **Concetto:** Healthchecks verificano lo stato di salute dei container
|
||||
✓ **Sintassi:** Parametri test, interval, timeout, retries, start_period
|
||||
✓ **Tipi:** HTTP, CMD-SHELL, custom healthchecks
|
||||
✓ **Parallelismo:** Healthcheck Docker → ELB Health Check
|
||||
✓ **Monitoring:** `docker ps` mostra stato (healthy/unhealthy)
|
||||
|
||||
---
|
||||
|
||||
## Prossimi Passi
|
||||
|
||||
Nel prossimo tutorial imparerai a:
|
||||
- Configurare **dipendenze** tra servizi
|
||||
- Usare `depends_on` con `condition: service_healthy`
|
||||
- Implementare startup ordinato per applicazioni multi-tier
|
||||
|
||||
Continua con **Tutorial 3: Dipendenze con Healthchecks** →
|
||||
410
labs/lab-03-compute/tutorial/03-dependencies-with-health.md
Normal file
410
labs/lab-03-compute/tutorial/03-dependencies-with-health.md
Normal file
@@ -0,0 +1,410 @@
|
||||
# Tutorial 3: Dipendenze tra Servizi con Healthchecks
|
||||
|
||||
In questo tutorial imparerai a configurare dipendenze tra servizi usando `depends_on`, assicurando che i servizi partano nell'ordine corretto.
|
||||
|
||||
## Obiettivi di Apprendimento
|
||||
|
||||
Al termine di questo tutorial sarai in grado di:
|
||||
- Comprendere le dipendenze tra servizi in applicazioni multi-tier
|
||||
- Configurare `depends_on` con conditions
|
||||
- Implementare startup ordinato con healthchecks
|
||||
- Verificare l'ordine di avvio dei servizi
|
||||
|
||||
---
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
- Completamento di Tutorial 1 e 2
|
||||
- Servizi configurati con resource limits e healthchecks
|
||||
- Comprensione base di architetture multi-tier
|
||||
|
||||
---
|
||||
|
||||
## Parte 1: Dipendenze tra Servizi
|
||||
|
||||
### Cos'è una Dipendenza?
|
||||
|
||||
In un'applicazione multi-tier, i servizi dipendono l'uno dall'altro:
|
||||
|
||||
```
|
||||
Web → App → Database
|
||||
```
|
||||
|
||||
- Il **Web** server ha bisogno dell'**App** server
|
||||
- L'**App** server ha bisogno del **Database**
|
||||
- Il **Database** non dipende da nessuno (parte prima)
|
||||
|
||||
### Perché sono Importanti?
|
||||
|
||||
1. **Prevenzione Errori:** Evita connection error a servizi non pronti
|
||||
2. **Startup Affidabile:** Ogni servizio aspetta che le sue dipendenze siano pronte
|
||||
3. **Zero-Downtime:** Durante deployment, i nuovi servizi aspettano i vecchi
|
||||
4. **Debugging:** Problemi di connessione sono più facili da diagnosticare
|
||||
|
||||
---
|
||||
|
||||
## Parte 2: Tipi di Dipendenze
|
||||
|
||||
### depends_on: service_started (Default)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
depends_on:
|
||||
- db
|
||||
```
|
||||
|
||||
**Comportamento:** `app` parte quando `db` è **avviato** (non necessariamente healthy)
|
||||
|
||||
**Problema:** Il database potrebbe ancora essere inizializzando → connection errors
|
||||
|
||||
### depends_on: service_healthy (Raccomandato)
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
```
|
||||
|
||||
**Comportamento:** `app` parte quando `db` è **healthy** (dopo healthcheck pass)
|
||||
|
||||
**Vantaggio:** Il database è completamente pronto → nessun connection error
|
||||
|
||||
---
|
||||
|
||||
## Parte 3: Pratica - Architettura Multi-Tier
|
||||
|
||||
Creiamo un'applicazione a 3 tier con dipendenze:
|
||||
|
||||
```
|
||||
Web (t2.micro) → App (t2.small) → DB (t2.medium)
|
||||
```
|
||||
|
||||
### Step 1: Configurare il Database (Primo)
|
||||
|
||||
```yaml
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
# Tier 3: Database - nessuna dipendenza
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
container_name: lab03-db
|
||||
hostname: db
|
||||
|
||||
environment:
|
||||
POSTGRES_DB: lab03_db
|
||||
POSTGRES_USER: lab03_user
|
||||
POSTGRES_PASSWORD: lab03_password
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 4G
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U lab03_user -d lab03_db || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
```
|
||||
|
||||
**Nota:** Il database non ha `depends_on` - è il primo a partire.
|
||||
|
||||
### Step 2: Configurare l'App Server
|
||||
|
||||
```yaml
|
||||
# Tier 2: Application - dipende dal database
|
||||
app:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-app
|
||||
hostname: app
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8081:80"
|
||||
|
||||
# Dipende dal database healthy
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
**Nota:** `app` parte SOLO quando `db` è `healthy`.
|
||||
|
||||
### Step 3: Configurare il Web Server
|
||||
|
||||
```yaml
|
||||
# Tier 1: Web - dipende dall'app
|
||||
web:
|
||||
image: nginx:alpine
|
||||
container_name: lab03-web
|
||||
hostname: web
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
ports:
|
||||
- "127.0.0.1:8080:80"
|
||||
|
||||
# Dipende dall'app healthy
|
||||
depends_on:
|
||||
app:
|
||||
condition: service_healthy
|
||||
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
**Nota:** `web` parte SOLO quando `db` → `app` sono entrambi `healthy`.
|
||||
|
||||
---
|
||||
|
||||
## Parte 4: Avvio e Verifica
|
||||
|
||||
### Step 1: Avviare tutti i servizi
|
||||
|
||||
```bash
|
||||
cd labs/lab-03-compute
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Step 2: Osservare l'ordine di avvio
|
||||
|
||||
```bash
|
||||
# Monitora lo stato in tempo reale
|
||||
watch -n 2 'docker ps --format "table {{.Names}}\t{{.Status}}"'
|
||||
```
|
||||
|
||||
**Sequenza prevista:**
|
||||
|
||||
1. `lab03-db` parte → `starting` → `(healthy)` dopo ~10-20s
|
||||
2. `lab03-app` parte → `starting` → `(healthy)` dopo che `db` è healthy
|
||||
3. `lab03-web` parte → `starting` → `(healthy)` dopo che `app` è healthy
|
||||
|
||||
### Step 3: Verificare l'ordine nei log
|
||||
|
||||
```bash
|
||||
docker compose logs --tail=50
|
||||
```
|
||||
|
||||
Cerca messaggi tipo:
|
||||
```
|
||||
lab03-db | database system is ready to accept connections
|
||||
lab03-app | Waiting for db to be healthy...
|
||||
lab03-web | Waiting for app to be healthy...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parte 5: Testare le Dipendenze
|
||||
|
||||
### Test 1: Riavviare il database
|
||||
|
||||
```bash
|
||||
# Ferma il database
|
||||
docker compose stop db
|
||||
|
||||
# Guarda lo stato di app e web
|
||||
docker ps
|
||||
```
|
||||
|
||||
**Risultato:** `app` e `web` rimangono in esecuzione (non dipendono dal runtime di db, solo dall'avvio iniziale)
|
||||
|
||||
### Test 2: Ferma e riavvia l'app
|
||||
|
||||
```bash
|
||||
# Ferma l'app
|
||||
docker compose stop app
|
||||
|
||||
# Guarda lo stato del web
|
||||
docker ps
|
||||
```
|
||||
|
||||
**Risultato:** `web` continua a funzionare (stessa logica)
|
||||
|
||||
### Test 3: Riavvio completo
|
||||
|
||||
```bash
|
||||
# Ferma tutto
|
||||
docker compose down
|
||||
|
||||
# Riavvia
|
||||
docker compose up -d
|
||||
|
||||
# Osserva l'ordine di startup
|
||||
docker compose up -d && docker compose logs -f
|
||||
```
|
||||
|
||||
Vedrai: `db` → `app` → `web` in ordine.
|
||||
|
||||
---
|
||||
|
||||
## Parte 6: Parallelismo AWS
|
||||
|
||||
### ECS Task Definitions
|
||||
|
||||
In AWS ECS, le dipendenze si configurano nel Task Definition:
|
||||
|
||||
```json
|
||||
{
|
||||
"containerDefinitions": [
|
||||
{
|
||||
"name": "db",
|
||||
"essential": true,
|
||||
"healthCheck": {
|
||||
"command": ["CMD-SHELL", "pg_isready -U postgres"],
|
||||
"interval": 10,
|
||||
"timeout": 5,
|
||||
"retries": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "app",
|
||||
"essential": true,
|
||||
"dependsOn": [
|
||||
{
|
||||
"containerName": "db",
|
||||
"condition": "HEALTHY"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "web",
|
||||
"essential": true,
|
||||
"dependsOn": [
|
||||
{
|
||||
"containerName": "app",
|
||||
"condition": "HEALTHY"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Condizioni di Dipendenza ECS
|
||||
|
||||
| Condizione | Docker Equivalente | Descrizione |
|
||||
|------------|-------------------|-------------|
|
||||
| START | `depends_on` (default) | Container avviato |
|
||||
| HEALTHY | `condition: service_healthy` | Healthcheck passing |
|
||||
| COMPLETE | - | Container exit code 0 |
|
||||
| SUCCESS | - | Container exit code 0 |
|
||||
|
||||
---
|
||||
|
||||
## Parte 7: Best Practices
|
||||
|
||||
### 1. Usa Sempre service_healthy
|
||||
|
||||
```yaml
|
||||
# ✓ GOOD
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
# ✗ AVOID (se possibile)
|
||||
depends_on:
|
||||
- db
|
||||
```
|
||||
|
||||
### 2. Configura healthcheck appropriati
|
||||
|
||||
```yaml
|
||||
# Database - startup più lento
|
||||
healthcheck:
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
# Web server - startup veloce
|
||||
healthcheck:
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
```
|
||||
|
||||
### 3. Previeni loop di dipendenze
|
||||
|
||||
```yaml
|
||||
# ✗ WRONG - circular dependency
|
||||
web:
|
||||
depends_on:
|
||||
app: {condition: service_healthy}
|
||||
app:
|
||||
depends_on:
|
||||
web: {condition: service_healthy} # LOOP!
|
||||
|
||||
# ✓ GOOD - directional dependency
|
||||
web:
|
||||
depends_on:
|
||||
app: {condition: service_healthy}
|
||||
app:
|
||||
depends_on:
|
||||
db: {condition: service_healthy} # OK
|
||||
db:
|
||||
# No dependencies - base tier
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Riepilogo
|
||||
|
||||
In questo tutorial hai imparato:
|
||||
|
||||
✓ **Concetto:** Dipendenze tra servizi per startup ordinato
|
||||
✓ **Sintassi:** `depends_on` con `condition: service_healthy`
|
||||
✓ **Pratica:** Architettura multi-tier Web → App → DB
|
||||
✓ **Verifica:** Osservare l'ordine di startup
|
||||
✓ **Parallelismo:** ECS `dependsOn` con condizione HEALTHY
|
||||
|
||||
---
|
||||
|
||||
## Complimenti!
|
||||
|
||||
Hai completato i tutorial di Lab 03 - Compute & EC2!
|
||||
|
||||
**Competenze Acquisite:**
|
||||
- Configurare limiti delle risorse (EC2 instance types)
|
||||
- Implementare healthchecks (ELB health checks)
|
||||
- Gestire dipendenze tra servizi (ECS task definitions)
|
||||
|
||||
**Prossimi Passi:**
|
||||
- Esegui i test di verifica: `bash tests/run-all-tests.sh`
|
||||
- Esplora le guide How-to per procedure specifiche
|
||||
- Consulta i documenti Reference per sintassi completa
|
||||
- Leggi Explanation per approfondire i parallelismi cloud
|
||||
Reference in New Issue
Block a user