From c5948b41dc44eba329fea789c251f0802c8768e1 Mon Sep 17 00:00:00 2001 From: setterwars Date: Sun, 25 Jan 2026 19:04:42 +0300 Subject: [PATCH 01/27] feat: Created main part of the lab1 task --- app_python/.gitignore | 13 ++ app_python/README.md | 37 ++++++ app_python/app.py | 115 ++++++++++++++++++ app_python/docs/LAB01.md | 43 +++++++ .../screenshots/base_request_json_output.png | Bin 0 -> 64187 bytes .../health_request_json_output.png | Bin 0 -> 8101 bytes .../health_request_terminal_output.png | Bin 0 -> 60617 bytes .../terminal_output_base_request.png | Bin 0 -> 51019 bytes app_python/requirements.txt | 1 + app_python/tests/__init__.py | 0 10 files changed, 209 insertions(+) create mode 100644 app_python/.gitignore create mode 100644 app_python/README.md create mode 100644 app_python/app.py create mode 100644 app_python/docs/LAB01.md create mode 100644 app_python/docs/screenshots/base_request_json_output.png create mode 100644 app_python/docs/screenshots/health_request_json_output.png create mode 100644 app_python/docs/screenshots/health_request_terminal_output.png create mode 100644 app_python/docs/screenshots/terminal_output_base_request.png create mode 100644 app_python/requirements.txt create mode 100644 app_python/tests/__init__.py diff --git a/app_python/.gitignore b/app_python/.gitignore new file mode 100644 index 0000000000..1b78e926d6 --- /dev/null +++ b/app_python/.gitignore @@ -0,0 +1,13 @@ +# Python +__pycache__/ +*.py[cod] +venv/ +.venv/ +*.log + +# IDE +.vscode/ +.idea/ + +# OS +.DS_Store \ No newline at end of file diff --git a/app_python/README.md b/app_python/README.md new file mode 100644 index 0000000000..0731e38864 --- /dev/null +++ b/app_python/README.md @@ -0,0 +1,37 @@ +# DevOps Course Info Service + +## Overview + +DevOps Info Service - small Flask based service what return and report system metadata and information. + +## Prerequisites +- Python 3.11+ +- pip +- Linux / macOS / Windows + +## Installation guid + +1. Clone the repository: + ```bash + git clone git@github.com:setterwars/DevOps-Core-Course.git + +2. Navigate to the project directory: + ```bash + cd app_python + +3. (Optional) Create and activate a virtual environment: + ```bash + python3 -m venv venv + +4. Install the required dependencies: + ```bash + pip install -r requirements.txt + +5. Run the application: + ```bash + python3 app.py # in default mode + PORT=8080 HOST=127.0.0.1 DEBUG=True python3 app.py # in custom mode + +## Available Endpoints +- `GET /` - Returns system metadata including hostname, IP address, and current timestamp. +- `GET /health` - Returns the health status of the service. \ No newline at end of file diff --git a/app_python/app.py b/app_python/app.py new file mode 100644 index 0000000000..1ea5f40124 --- /dev/null +++ b/app_python/app.py @@ -0,0 +1,115 @@ +from __future__ import annotations +import os +import socket +import platform +import logging +from datetime import datetime, timezone +from typing import Dict + +from flask import Flask, request, jsonify + +app = Flask(__name__) + + +# take parameters from environment variables with defaults +HOST = os.getenv("HOST", "0.0.0.0") +PORT = int(os.getenv("PORT", "5000")) +DEBUG = os.getenv("DEBUG", "False").lower() == "true" + + +# logger configuration +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', +) + +logger = logging.getLogger("devops-info-service") +logger.info("Starting devops-info-service") + +# save service start time +START_TIME = datetime.now(timezone.utc) + +# utility functions +def get_system_info() -> Dict[str, object]: + return { + "hostname": socket.gethostname(), + "platform": platform.system(), + "platform_version": platform.version(), + "architecture": platform.machine(), + "cpu_count": os.cpu_count() or 1, + "python_version": platform.python_version(), + } + +def get_uptime() -> Dict[str, object]: + delta = datetime.now(timezone.utc) - START_TIME + sec = int(delta.total_seconds()) + hours = sec // 3600 + minutes = (sec % 3600) // 60 + return { + "seconds": sec, + "human": f"{hours} hours, {minutes} minutes", + } + +def get_request_info() -> Dict[str, object]: + xff = request.headers.get("X-Forwarded-For") + client_ip = xff.split(",")[0].strip() if xff else request.remote_addr + return { + "client_ip": client_ip, + "user_agent": request.headers.get("User-Agent"), + "method": request.method, + "path": request.path, + } + +# main endpoints for getting service info +@app.route("/", methods=["GET"]) +def index(): + logger.info(f"Received request from {request.remote_addr}. Method: {request.method}, Path: {request.path}") + payload = { + "service": { + "name" : "devops-info-service", + "version": "1.0.0", + "description": "DevOps course info service" + }, + "system": get_system_info(), + "runtime": { + "uptime_seconds": get_uptime()["seconds"], + "uptime_human": get_uptime()["human"], + "current-time": datetime.now(timezone.utc).isoformat(), + "timezone": "UTC", + }, + "request": get_request_info(), + "endpoints": [ + {"path": "/", "method": "GET", "description": "Service information"}, + {"path": "/health", "method": "GET", "description": "Health check"}, + ], + } + return jsonify(payload) + +# health check endpoint +@app.route("/health", methods=["GET"]) +def health(): + logger.info("Health check requested") + return jsonify( + { + "status": "healthy", + "timestamp": datetime.now(timezone.utc).isoformat(), + "uptime_seconds": get_uptime()["seconds"], + } + ), 200 + +# error handlers +@app.errorhandler(404) +def not_found(error): + return jsonify({"error": "Not Found", "message": "Endpoint does not exist"}), 404 + + +@app.errorhandler(500) +def internal_error(error): + logger.exception("Internal server error") + return ( + jsonify({"error": "Internal Server Error", "message": "An unexpected error occurred"}), + 500, + ) + +if __name__ == "__main__": + app.run(host=HOST, port=PORT, debug=DEBUG) \ No newline at end of file diff --git a/app_python/docs/LAB01.md b/app_python/docs/LAB01.md new file mode 100644 index 0000000000..2178883fce --- /dev/null +++ b/app_python/docs/LAB01.md @@ -0,0 +1,43 @@ +# Framework selection + +**chosen framework:** Flask + +comparison table: + +| Framework | information | +|-----------|-------------------------------------------------------------| +| flask | lightweight, easy to use | +| FastAPI | async-first, auto-docs, better for hight perfomance API's | +| Django | heavy-weight, includes ORM and admin, overkill for this lab | + +# Best practices followed +- Use virtual environment for dependency management +- Clear function and variable names +- Logging for monitoring and debugging +- Error handling for robustness +- Modular code structure for maintainability + +# API documentation + +- endpoints: + - `GET /` - Returns system metadata including hostname, IP address, and current timestamp. + - `GET /health` - Returns the health status of the service. + +## Testing commands in basic configuration + + curl http://localhost:5000/ + curl http://localhost:5000/health + +# Testing evidence + +Basic endpoint test: + +![Basic Endpoint Test json](screenshots/base_request_json_output.png) +![Basic Endpoint Test text](screenshots/terminal_output_base_request.png) + +Health endpoint test: +![Health Endpoint Test json](screenshots/health_request_json_output.png) +![Health Endpoint Test text](screenshots/health_request_terminal_output.png) + +# Challenges and solutions + No challenges faced during the lab \ No newline at end of file diff --git a/app_python/docs/screenshots/base_request_json_output.png b/app_python/docs/screenshots/base_request_json_output.png new file mode 100644 index 0000000000000000000000000000000000000000..382a25938c82817ae5fd92ea317391ac6470412b GIT binary patch literal 64187 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

!QW{yYT;NDbD{NJ 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 WUefR>amBZ;1 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 nCmMb2?t literal 0 HcmV?d00001 diff --git a/app_python/docs/screenshots/health_request_json_output.png b/app_python/docs/screenshots/health_request_json_output.png new file mode 100644 index 0000000000000000000000000000000000000000..32ec837122940e3ac21a1b4a163574bec1682b7f GIT binary patch literal 8101 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 W9wW@Wi*LW*(ooS={-pFQ>^}f?G-{y$ literal 0 HcmV?d00001 diff --git a/app_python/docs/screenshots/health_request_terminal_output.png b/app_python/docs/screenshots/health_request_terminal_output.png new file mode 100644 index 0000000000000000000000000000000000000000..5b3257b8b03a75bd41062ee4e6468f87e4a1c24e GIT binary patch literal 60617 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 E7qMgKssI20 literal 0 HcmV?d00001 diff --git a/app_python/docs/screenshots/terminal_output_base_request.png b/app_python/docs/screenshots/terminal_output_base_request.png new file mode 100644 index 0000000000000000000000000000000000000000..ef4c86e3462102394d64b9fb3dd1274959f9b399 GIT binary patch literal 51019 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 literal 0 HcmV?d00001 diff --git a/app_python/requirements.txt b/app_python/requirements.txt new file mode 100644 index 0000000000..174be8eeb2 --- /dev/null +++ b/app_python/requirements.txt @@ -0,0 +1 @@ +flask==3.1.0 \ No newline at end of file diff --git a/app_python/tests/__init__.py b/app_python/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 95233642da99adf1f899d139ffb44e5eec4080bf Mon Sep 17 00:00:00 2001 From: setterwars Date: Mon, 26 Jan 2026 13:47:34 +0300 Subject: [PATCH 02/27] feat: completed lab1 bonus task --- .idea/.gitignore | 10 + .idea/DevOps-Core-Course.iml | 12 + .idea/copilot.data.migration.ask2agent.xml | 6 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/material_theme_project_new.xml | 10 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + app_go/.gitignore | 8 + app_go/README.md | 26 + app_go/docs/GO.md | 3 + app_go/docs/LAB01.md | 34 + app_go/docs/screenshots/.gitkeep | 0 app_go/docs/screenshots/base_request_json.png | Bin 0 -> 67691 bytes .../screenshots/base_request_terminal.png | Bin 0 -> 28211 bytes .../docs/screenshots/health_request_json.png | Bin 0 -> 9593 bytes .../screenshots/health_request_terminal.png | Bin 0 -> 33374 bytes app_go/go.mod | 3 + app_go/main.go | 190 +++ app_go/tests/__init__.go | 0 lectures/lec1.md | 761 ------------ lectures/lec10.md | 840 ------------- lectures/lec11.md | 759 ------------ lectures/lec12.md | 854 ------------- lectures/lec13.md | 830 ------------- lectures/lec14.md | 825 ------------- lectures/lec15.md | 821 ------------- lectures/lec16.md | 717 ----------- lectures/lec2.md | 1053 ----------------- lectures/lec3.md | 978 --------------- lectures/lec4.md | 801 ------------- lectures/lec5.md | 824 ------------- lectures/lec6.md | 887 -------------- lectures/lec7.md | 849 ------------- lectures/lec8.md | 799 ------------- lectures/lec9.md | 853 ------------- 36 files changed, 329 insertions(+), 13451 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/DevOps-Core-Course.iml create mode 100644 .idea/copilot.data.migration.ask2agent.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/material_theme_project_new.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 app_go/.gitignore create mode 100644 app_go/README.md create mode 100644 app_go/docs/GO.md create mode 100644 app_go/docs/LAB01.md create mode 100644 app_go/docs/screenshots/.gitkeep create mode 100644 app_go/docs/screenshots/base_request_json.png create mode 100644 app_go/docs/screenshots/base_request_terminal.png create mode 100644 app_go/docs/screenshots/health_request_json.png create mode 100644 app_go/docs/screenshots/health_request_terminal.png create mode 100644 app_go/go.mod create mode 100644 app_go/main.go create mode 100644 app_go/tests/__init__.go delete mode 100644 lectures/lec1.md delete mode 100644 lectures/lec10.md delete mode 100644 lectures/lec11.md delete mode 100644 lectures/lec12.md delete mode 100644 lectures/lec13.md delete mode 100644 lectures/lec14.md delete mode 100644 lectures/lec15.md delete mode 100644 lectures/lec16.md delete mode 100644 lectures/lec2.md delete mode 100644 lectures/lec3.md delete mode 100644 lectures/lec4.md delete mode 100644 lectures/lec5.md delete mode 100644 lectures/lec6.md delete mode 100644 lectures/lec7.md delete mode 100644 lectures/lec8.md delete mode 100644 lectures/lec9.md diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000..ab1f4164ed --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/DevOps-Core-Course.iml b/.idea/DevOps-Core-Course.iml new file mode 100644 index 0000000000..460d4026f7 --- /dev/null +++ b/.idea/DevOps-Core-Course.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copilot.data.migration.ask2agent.xml b/.idea/copilot.data.migration.ask2agent.xml new file mode 100644 index 0000000000..1f2ea11e7f --- /dev/null +++ b/.idea/copilot.data.migration.ask2agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000000..105ce2da2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml new file mode 100644 index 0000000000..ba6f591e01 --- /dev/null +++ b/.idea/material_theme_project_new.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000..1d3ce46ba0 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000..71d30f3527 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000..35eb1ddfbb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app_go/.gitignore b/app_go/.gitignore new file mode 100644 index 0000000000..85dc74b2a8 --- /dev/null +++ b/app_go/.gitignore @@ -0,0 +1,8 @@ + +# Binaries +devops-info-service +*.exe + +# Build outputs +*.out + diff --git a/app_go/README.md b/app_go/README.md new file mode 100644 index 0000000000..574e4950cc --- /dev/null +++ b/app_go/README.md @@ -0,0 +1,26 @@ + +# DevOps Course Info Service (Go) + +## Overview +Go implementation of the `app_python` DevOps Info Service. + +## Prerequisites +- Go 1.22+ + +## Run +From the `app_go/` directory: + +- `go run .` +- `HOST=127.0.0.1 PORT=8080 DEBUG=true go run .` + +## Build +- `CGO_ENABLED=0 go build -ldflags "-s -w" -o devops-info-service .` +- `./devops-info-service` + +## Endpoints +- `GET /` — returns service/system/runtime/request metadata +- `GET /health` — health check + +## Docs +- Implementation details: `docs/LAB01.md` +- Language justification: `docs/GO.md` diff --git a/app_go/docs/GO.md b/app_go/docs/GO.md new file mode 100644 index 0000000000..e2da37051e --- /dev/null +++ b/app_go/docs/GO.md @@ -0,0 +1,3 @@ +# Why is go? + +Go is a great choice for this lab because it compiles the service into a single small, fast binary that’s easy to ship in a minimal multi-stage Docker image. \ No newline at end of file diff --git a/app_go/docs/LAB01.md b/app_go/docs/LAB01.md new file mode 100644 index 0000000000..d36e4e47ac --- /dev/null +++ b/app_go/docs/LAB01.md @@ -0,0 +1,34 @@ +# Choosen language: Go + +# Best practices followed +- Use virtual environment for dependency management +- Clear function and variable names +- Logging for monitoring and debugging +- Error handling for robustness +- Modular code structure for maintainability + +# API documentation + +- endpoints: + - `GET /` - Returns system metadata including hostname, IP address, and current timestamp. + - `GET /health` - Returns the health status of the service. + +## Testing commands in basic configuration + + curl http://localhost:5000/ + curl http://localhost:5000/health + +# Testing evidence + +Basic endpoint test: + +![Basic Endpoint Test json](screenshots/base_request_json.png) +![Basic Endpoint Test text](screenshots/base_request_terminal.png) + +Health endpoint test: + +![Health Endpoint Test json](screenshots/health_request_json.png) +![Health Endpoint Test text](screenshots/health_request_terminal.png) + +# Challenges and solutions + No challenges faced during the lab \ No newline at end of file diff --git a/app_go/docs/screenshots/.gitkeep b/app_go/docs/screenshots/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app_go/docs/screenshots/base_request_json.png b/app_go/docs/screenshots/base_request_json.png new file mode 100644 index 0000000000000000000000000000000000000000..83fb58e301a23914a447bf769d4c89dd2c376cff GIT binary patch literal 67691 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 ~5(TM~2nZIY0fp9)gRb<)qESmi{%Lh$iC2 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 literal 0 HcmV?d00001 diff --git a/app_go/docs/screenshots/base_request_terminal.png b/app_go/docs/screenshots/base_request_terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..c7073cc2457ba8dd632d1cbf0dd8ee92b4e98ad3 GIT binary patch literal 28211 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 literal 0 HcmV?d00001 diff --git a/app_go/docs/screenshots/health_request_json.png b/app_go/docs/screenshots/health_request_json.png new file mode 100644 index 0000000000000000000000000000000000000000..8f7c0b9850ebf08a63f10db2eaf73ca675b1579a GIT binary patch literal 9593 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 0 { + return strings.TrimSpace(parts[0]) + } + } + host, _, err := net.SplitHostPort(r.RemoteAddr) + if err == nil && host != "" { + return host + } + return r.RemoteAddr +} + +func getHostname() string { + h, err := os.Hostname() + if err != nil { + return "" + } + return h +} + +func getKernelRelease() string { + // Best-effort Linux kernel release; empty string on non-Linux or when unavailable. + b, err := os.ReadFile("/proc/sys/kernel/osrelease") + if err != nil { + return "" + } + return strings.TrimSpace(string(b)) +} + +func getEnv(key, def string) string { + v := os.Getenv(key) + if v == "" { + return def + } + return v +} + +func getEnvInt(key string, def int) int { + v := strings.TrimSpace(os.Getenv(key)) + if v == "" { + return def + } + n, err := strconv.Atoi(v) + if err != nil { + return def + } + return n +} diff --git a/app_go/tests/__init__.go b/app_go/tests/__init__.go new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lectures/lec1.md b/lectures/lec1.md deleted file mode 100644 index 00ead8aabc..0000000000 --- a/lectures/lec1.md +++ /dev/null @@ -1,761 +0,0 @@ -# 📌 Lecture 1 — Introduction to DevOps: From Chaos to Flow - -## 📍 Slide 1 – 🚀 Welcome to DevOps - -* 🌍 **Software is eating the world** — but shipping it is hard -* 😰 Teams struggle with slow releases, broken deploys, finger-pointing -* 🌉 **DevOps bridges the gap** between **building** and **running** software -* 🎯 This course: practical skills to transform how you deliver software - -```mermaid -flowchart LR - Chaos[😱 Chaos] -->|DevOps| Flow[🌊 Flow] - Flow --> Value[💎 Deliver Value Faster] -``` - ---- - -## 📍 Slide 2 – 🎯 What You Will Learn - -* ✅ Understand what DevOps is (and isn't) -* ✅ Identify problems DevOps solves -* ✅ Apply DevOps thinking to real scenarios -* ✅ Map DevOps practices to your future workflow - -**🎓 Learning Outcomes:** -| # | Outcome | -|---|---------| -| 1 | 🧠 Define DevOps and its core principles | -| 2 | 🔍 Recognize pre-DevOps problems | -| 3 | 🛠️ Apply DevOps solutions to scenarios | -| 4 | 🗺️ Navigate the DevOps lifecycle | - ---- - -## 📍 Slide 3 – 📋 How This Lecture Works - -* 📚 **Concepts + Diagrams** — visual learning -* 🎮 **Real-world scenarios** — you decide! -* 📝 **3 quiz checkpoints**: PRE / MID / POST -* 🕹️ **Interactive simulation**: "DevOps as a Game" - -**⏱️ Lecture Structure:** -``` -Section 0: Introduction (now) → 📝 PRE Quiz -Section 1: The Problem -Section 2: What DevOps Is -Section 3: DevOps as a Game → 📝 MID Quiz -Section 4: Lifecycle & Metrics -Section 5: Real Life -Section 6: Reflection → 📝 POST Quiz -``` - ---- - -## 📍 Slide 4 – ❓ The Big Question - -* 📊 **70%** of IT projects experience significant delays -* ⏱️ Average time from code complete to production: **weeks to months** -* 💥 Most outages caused by **changes** (deploys, configs) - -> 💬 *"It worked on my machine"* — Every developer, ever - -**🤔 Think about it:** -* Why is software delivery so hard? -* Why do teams fear deployments? -* What would "good" look like? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L1_PRE - ---- - -## 📍 Slide 6 – 🔥 Section 1: The Problem Before DevOps - -* 👨‍💻 **Development** and ⚙️ **Operations** = separate teams, separate goals -* 🚀 Dev wants: **ship features fast** -* 🛡️ Ops wants: **keep systems stable** -* 💥 Result: **conflict, blame, slow delivery** - -```mermaid -flowchart LR - Dev[👨‍💻 Dev Team] -->|🎯 New Features| Goal1[Ship Fast] - Ops[⚙️ Ops Team] -->|🛡️ Stability| Goal2[Don't Break] - Goal1 -.->|❌ Conflict| Goal2 -``` - ---- - -## 📍 Slide 7 – 🧱 The Wall of Confusion - -* 🧱 **The Wall** = invisible barrier between Dev and Ops -* 📦 Dev "throws code over the wall" -* 🔥 Ops catches the blame when it breaks -* 🔄 Ops rejects changes to avoid risk - -```mermaid -flowchart LR - Dev[👨‍💻 Dev Team] -->|📦 Throws code over| Wall[🧱 Wall of Confusion] - Wall -->|🔥 Catches blame| Ops[⚙️ Ops Team] - Ops -->|❌ Rejects changes| Dev -``` - -> 🤔 **Think:** Have you seen this pattern before? - ---- - -## 📍 Slide 8 – 😱 Manual Release Hell - -* 📅 Deployments are rare (monthly, quarterly) -* 🎰 Each release = **high-risk event** -* 📋 Manual steps, checklists, weekend work -* 💀 One mistake = hours of rollback - -```mermaid -flowchart TD - Code[✅ Code Complete] --> Wait[📅 Wait for Release Window] - Wait --> Manual[📋 Manual Deploy Steps] - Manual --> Pray[🙏 Pray It Works] - Pray -->|💥 Failure| Blame[👉 Blame Game] - Pray -->|😮‍💨 Success| Relief[Temporary Relief] -``` - -**📊 The Numbers:** -* 🐢 Average release cycle: **3-6 months** -* 📉 Success rate: **~60%** -* ⏱️ Rollback time: **4-8 hours** - ---- - -## 📍 Slide 9 – 😨 Fear and Blame Culture - -* 🌙 Incident happens at 2am -* 👉 First question: *"Who did this?"* -* 🙈 Engineers hide mistakes -* 🚫 Nobody wants to deploy on Friday -* 💀 Innovation stops - -> ⚠️ **Fear kills velocity** - -**😰 Signs of Blame Culture:** -* 🔇 People afraid to speak up -* 📝 Excessive documentation "for protection" -* 🐌 Slow decision-making -* 🚪 High turnover - -**💬 Discussion:** Why does blame make things worse? - ---- - -## 📍 Slide 10 – 💸 The Cost of Chaos - -| 🔥 Problem | 💥 Impact | -|------------|-----------| -| 🐢 Slow releases | Lost market opportunity | -| 📋 Manual processes | Human error, burnout | -| 👉 Blame culture | Talent leaves | -| 🙈 No visibility | Firefighting mode | - -**📈 Real Numbers:** -* 🏢 **Amazon pre-DevOps**: deploys took **weeks** -* 🚀 **Amazon post-DevOps**: deploys every **11.7 seconds** - -**💰 Cost of Downtime:** -* 💵 Small business: **$427/minute** -* 🏢 Enterprise: **$9,000/minute** -* 🌐 Amazon: **$220,000/minute** - ---- - -## 📍 Slide 11 – 💡 Section 2: What DevOps Really Is - -* 🤝 **DevOps** = Development + Operations working as **one team** -* 🌱 A **culture** of collaboration and shared responsibility -* 🔧 A set of **practices** for fast, reliable delivery -* 🚫 NOT just tools, NOT a job title, NOT a team - -```mermaid -flowchart LR - Dev[👨‍💻 Development] -->|🤝 Collaboration| DevOps[🚀 DevOps] - Ops[⚙️ Operations] -->|🤝 Collaboration| DevOps - DevOps --> Value[💎 Fast, Reliable Value] -``` - -**📖 Definition:** -> *DevOps is a set of practices that combines software development (Dev) and IT operations (Ops) to shorten the development lifecycle while delivering features, fixes, and updates frequently in close alignment with business objectives.* - ---- - -## 📍 Slide 12 – 🚫 What DevOps is NOT - -| ❌ Myth | ✅ Reality | -|---------|-----------| -| "We hired a DevOps engineer, we're done" | 👥 Everyone participates | -| "DevOps means using Kubernetes" | 🛠️ Tools support culture | -| "DevOps replaces developers/ops" | 🤝 It unites them | -| "DevOps = just automation" | 🧩 Automation + Culture + Measurement | -| "DevOps is a team" | 🌍 It's a way of working | - -> 🔥 **Hot take:** You can't buy DevOps. You build it. - -**🎯 DevOps is about:** -* 🧠 Mindset change -* 🤝 Breaking silos -* 🔄 Continuous improvement -* 📊 Data-driven decisions - ---- - -## 📍 Slide 13 – 🔄 The Three Ways of DevOps - -```mermaid -flowchart LR - W1[1️⃣ Flow] --> W2[2️⃣ Feedback] - W2 --> W3[3️⃣ Learning] - W3 --> W1 -``` - -| 🛤️ Way | 🎯 Focus | 💡 Example | -|--------|---------|-----------| -| 1️⃣ **Flow** | Fast Dev → Prod | 🚀 CI/CD pipelines | -| 2️⃣ **Feedback** | Fast Prod → Dev | 📊 Monitoring, alerts | -| 3️⃣ **Learning** | Experiment safely | 📝 Blameless postmortems | - -**📚 Source:** *The Phoenix Project* by Gene Kim - ---- - -## 📍 Slide 14 – 🧩 The CAMS Model - -```mermaid -graph TD - C[🌱 Culture] --> DevOps[🚀 DevOps] - A[🤖 Automation] --> DevOps - M[📊 Measurement] --> DevOps - S[🔗 Sharing] --> DevOps -``` - -* 🌱 **C = Culture** — Trust, collaboration, shared ownership -* 🤖 **A = Automation** — Eliminate manual, error-prone work -* 📊 **M = Measurement** — Track metrics, decide with data -* 🔗 **S = Sharing** — Knowledge flows, blameless postmortems - -**🎯 Key Metrics:** -* ⏱️ **MTTR** = Mean Time to Recovery -* ❌ **CFR** = Change Failure Rate -* 📦 **DF** = Deployment Frequency -* 🚀 **LT** = Lead Time - ---- - -## 📍 Slide 15 – ⚡ Before vs After DevOps - -| 😰 Before | 🚀 After | -|----------|---------| -| 📅 Releases every few months | 📆 Releases daily/weekly | -| 📋 Manual deployments | 🤖 Automated pipelines | -| 👉 Blame when things break | 📝 Blameless postmortems | -| 🙅 "Not my problem" | 🤝 Shared ownership | -| 😨 Fear of change | 💪 Embrace change | -| 🐌 Weeks to deploy | ⚡ Minutes to deploy | - -> 🤔 Which column describes your current environment? - ---- - -## 📍 Slide 16 – 🎮 Section 3: DevOps as a Game - -## 🕹️ Simulation: You're the CTO - -* 🏢 Welcome to **FlowStart Inc.** — a growing startup -* 👥 You have: 5 developers, 2 ops engineers -* 🌐 A web application with 10K users -* 📈 Pressure to ship new features - -**❓ What could go wrong?** - -> 💀 **Everything.** - -🎮 **Let's play.** - ---- - -## 📍 Slide 17 – 💥 Scenario 1: Release Failure - -**📅 Friday 5pm:** -* 👨‍💻 Developer pushes "small fix" -* 🚫 No tests, no review, straight to production -* 💥 App crashes, users can't log in -* 🤷 Nobody knows what changed - -```mermaid -flowchart LR - Push[📤 Code Push] --> Prod[🌐 Production] - Prod --> Crash[💥 Crash] - Crash --> Panic[😱 Weekend Panic] -``` - -**📊 Impact:** -* 👥 10,000 users affected -* ⏱️ 4 hours downtime -* 💰 $50,000 lost revenue -* 😤 Angry customers on Twitter - -> ❓ **What would you do?** - ---- - -## 📍 Slide 18 – ✅ Solution: CI/CD - -## 🛠️ Fix: Continuous Integration & Delivery - -```mermaid -flowchart LR - Push[📤 Push] --> CI[🧪 Tests] - CI -->|✅ Pass| Review[👀 Review] - Review --> CD[🚀 Deploy] - CD --> Monitor[📊 Monitor] - CI -->|❌ Fail| Fix[🔧 Fix] -``` - -* ✅ Every change triggers **automated tests** -* ✅ **Code review** required before merge -* ✅ **Automated deployment** pipeline -* ✅ **One-click rollback** - -**🎯 Result:** Deploy with confidence, not prayers - -**📊 CI/CD Benefits:** -* 🐛 Catch bugs early (80% cheaper to fix) -* 🚀 Deploy 200x more frequently -* ⏱️ 24x faster recovery from failures - ---- - -## 📍 Slide 19 – 🐾 Scenario 2: Infrastructure Drift - -**😰 Situation:** -* 🖥️ Production server configured manually over 2 years -* 👋 Ops engineer who set it up **left the company** -* 📈 Need to scale — but **can't recreate the setup** - -```mermaid -flowchart TD - S1[🖥️ Server 1: Ubuntu 18 + mystery configs] - S2[🖥️ Server 2: Ubuntu 20 + different configs] - S3[🖥️ Server 3: Who knows? 🤷] - S1 --> Drift[😱 Configuration Drift] - S2 --> Drift - S3 --> Drift -``` - -> 🐶🐄 **"Pets vs Cattle"** — Which do you have? - -**🐶 Pets:** Unique, irreplaceable, nursed back to health -**🐄 Cattle:** Identical, replaceable, automated - ---- - -## 📍 Slide 20 – ✅ Solution: Infrastructure as Code - -## 🛠️ Fix: IaC - -* 📝 Define infrastructure in **version-controlled files** -* 🔄 Servers are **reproducible**, not unique -* ⚡ Spin up identical environments in **minutes** - -```hcl -# 🌍 Terraform example -resource "aws_instance" "web" { - ami = "ami-0c55b159cbfafe1f0" - instance_type = "t2.micro" - count = 3 # 🔢 3 identical servers -} -``` - -**🎯 Result:** Cattle, not pets. Replace, don't repair. - -**🛠️ IaC Tools:** -* 🌍 **Terraform** — Multi-cloud -* 🧩 **Ansible** — Configuration management -* 📦 **Pulumi** — Code-based IaC - ---- - -## 📍 Slide 21 – 🔓 Scenario 3: Secret Leak - -**💀 What happened:** -* 👨‍💻 Developer commits database password to GitHub -* 🤖 Bot scrapes it within **minutes** -* 💥 Attackers access production database - -```mermaid -flowchart LR - Commit[📤 Commit + Secret] --> GitHub[🐙 Public Repo] - GitHub --> Bot[🤖 Scraper Bot] - Bot --> Breach[💀 Database Breach] -``` - -> ⏱️ **How fast do bots find secrets?** Under 5 minutes. - -**📊 Real Stats:** -* 🔍 GitHub scans 100M+ repos for secrets -* ⏱️ Average time to exploit: **<1 hour** -* 💰 Average breach cost: **$4.45 million** - ---- - -## 📍 Slide 22 – ✅ Solution: Secrets Management - -## 🛠️ Fix: Vault & Secret Scanning - -* 🚫 **Never** store secrets in code -* 🔐 Use secret management tools (Vault, AWS Secrets Manager) -* 🔍 Pre-commit hooks scan for secrets -* 🔄 Rotate credentials automatically - -```yaml -# ❌ Bad -password: "super_secret_123" - -# ✅ Good -password: ${VAULT_DB_PASSWORD} -``` - -**🎯 Result:** Secrets stay secret - -**🛠️ Secret Tools:** -* 🔐 **HashiCorp Vault** -* 🔑 **AWS Secrets Manager** -* 🔒 **Azure Key Vault** -* 🔍 **git-secrets** (pre-commit) - ---- - -## 📍 Slide 23 – 🙈 Scenario 4: Blind Operations - -**👥 Users report:** *"App is slow"* - -**🤷 Team asks:** -* Is it? How slow? -* Which part is slow? -* Since when? -* How many users affected? - -**😰 Answer:** No idea. No metrics. No logs. No visibility. - -⏱️ **Hours spent guessing.** - ---- - -## 📍 Slide 24 – ✅ Solution: Observability - -## 🛠️ Fix: Logs, Metrics, Traces - -```mermaid -graph TD - Logs[📋 Logs: What happened] --> Obs[🔍 Observability] - Metrics[📊 Metrics: How much/fast] --> Obs - Traces[🔗 Traces: Where] --> Obs - Obs --> Action[⚡ Fix in minutes, not hours] -``` - -| 📊 Pillar | 🛠️ Tools | -|-----------|----------| -| 📋 Logs | ELK, Loki, CloudWatch | -| 📊 Metrics | Prometheus, Grafana, Datadog | -| 🔗 Traces | Jaeger, Zipkin, X-Ray | - -**🎯 Result:** See problems before users report them - ---- - -## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L1_MID - ---- - -## 📍 Slide 26 – ♾️ Section 4: DevOps Lifecycle - -## 🔄 The Infinity Loop - -* ♾️ DevOps is **continuous** — no "done" state -* 🔄 Each stage feeds the next -* 🔁 Forever improving - -```mermaid -flowchart LR - Plan[📋 Plan] --> Code[💻 Code] - Code --> Build[🔨 Build] - Build --> Test[🧪 Test] - Test --> Release[📦 Release] - Release --> Deploy[🚀 Deploy] - Deploy --> Operate[⚙️ Operate] - Operate --> Monitor[📊 Monitor] - Monitor --> Plan -``` - ---- - -## 📍 Slide 27 – 🔁 Lifecycle Phases - -| 📍 Phase | 🎯 Activity | 🛠️ Tools | -|----------|------------|----------| -| 📋 Plan | Requirements, design | Jira, GitHub Issues | -| 💻 Code | Write & review | Git, VS Code | -| 🔨 Build | Compile, package | Docker, npm, Maven | -| 🧪 Test | Automated testing | pytest, Jest, Selenium | -| 📦 Release | Version, approve | GitHub Releases, Tags | -| 🚀 Deploy | Push to environment | ArgoCD, Ansible, Helm | -| ⚙️ Operate | Run, scale | Kubernetes, Terraform | -| 📊 Monitor | Observe, alert | Prometheus, Grafana | - ---- - -## 📍 Slide 28 – 🗺️ Course Map - -## 📚 How This Course Covers the Lifecycle - -```mermaid -flowchart TD - subgraph 📋 Plan & Code - L1[🔬 Labs 1-3: Git, GitHub] - end - subgraph 🔨 Build & Test - L2[🐳 Labs 4-6: Docker, CI/CD] - end - subgraph 🚀 Deploy & Operate - L3[☸️ Labs 7-10: K8s, Helm] - end - subgraph 🔐 Secure & Monitor - L4[📊 Labs 11-15: Vault, Monitoring] - end -``` - -✅ **Every lab maps to a real DevOps skill.** - ---- - -## 📍 Slide 29 – 📊 DORA Metrics - -## 📈 Measuring DevOps Success - -| 📊 Metric | 📏 Measures | 🏆 Elite | -|-----------|------------|---------| -| ⏱️ **Lead Time** | Commit → Prod | < 1 hour | -| 📦 **Deploy Frequency** | How often | Multiple/day | -| ❌ **Change Failure Rate** | % broken deploys | < 15% | -| 🔧 **MTTR** | Recovery time | < 1 hour | - -> 📚 These 4 metrics predict software delivery performance. -> *Source: DORA State of DevOps Report* - -**🤔 Question:** Where does your team stand? - ---- - -## 📍 Slide 30 – 🌊 From Chaos to Flow - -## 🎯 The Goal - -```mermaid -flowchart LR - subgraph 😱 Chaos - Manual[📋 Manual Work] - Silos[🧱 Silos] - Fear[😨 Fear] - end - subgraph 🌊 Flow - Auto[🤖 Automation] - Collab[🤝 Collaboration] - Confidence[💪 Confidence] - end - Chaos -->|🚀 DevOps| Flow -``` - -**🎯 Flow State:** -* ⚡ Changes flow smoothly from idea to production -* 🔄 Feedback loops are fast -* 📈 Teams continuously improve - ---- - -## 📍 Slide 31 – 🏢 Section 5: DevOps in Real Life - -## 📅 A Day in DevOps - -**☀️ Morning:** -* 📊 Check dashboards — all green ✅ -* 👀 Review pull requests -* 🔀 Merge → auto-deploy - -**🌤️ Afternoon:** -* 🚨 Alert: latency spike -* 🔍 Check traces → slow DB query -* 🔧 Fix, test, deploy — **20 min total** - -**🌙 Evening:** -* 🤖 Systems run themselves -* 🏠 Go home on time - ---- - -## 📍 Slide 32 – 👥 DevOps Roles - -| 👤 Role | 🎯 Focus | -|---------|---------| -| 🔧 **DevOps Engineer** | Pipelines, automation, infra | -| 🛡️ **SRE** | Reliability, SLOs, incidents | -| 🏗️ **Platform Engineer** | Developer experience, internal tools | -| ☁️ **Cloud Engineer** | Cloud infra, cost optimization | - -**🔗 Common thread:** Collaboration, automation, ownership - -**💰 Salary Range (2024):** -* 🔧 DevOps Engineer: $100K - $180K -* 🛡️ SRE: $120K - $200K -* 🏗️ Platform Engineer: $130K - $220K - ---- - -## 📍 Slide 33 – 🤝 Team Collaboration - -```mermaid -flowchart TD - Dev[👨‍💻 Developers] --> Shared[🤝 Shared Ownership] - Ops[⚙️ Operations] --> Shared - QA[🧪 QA] --> Shared - Sec[🔐 Security] --> Shared - Shared --> Ship[🚀 Ship Better Software] -``` - -**🤝 Collaboration Practices:** -* 📟 Shared on-call rotations -* 📝 Blameless incident reviews -* 👥 Cross-functional squads -* 🔓 Everyone can deploy - ---- - -## 📍 Slide 34 – 📈 Career Path - -```mermaid -flowchart LR - Junior[🌱 Junior] --> Mid[💼 Mid-level] - Mid --> Senior[⭐ Senior] - Senior --> Staff[🏆 Staff/Principal] - Senior --> Manager[👔 Manager] - Staff --> Architect[🏛️ Architect] -``` - -**🛠️ Skills to Build:** -* 🐧 Linux, networking -* 📝 Scripting (Bash, Python) -* 🐳 Containers & K8s -* 🔄 CI/CD pipelines -* ☁️ Cloud platforms (AWS, GCP, Azure) - ---- - -## 📍 Slide 35 – 🌍 Real Company Examples - -**🎬 Netflix:** -* 🚀 1000+ deploys/day -* 🐒 Chaos Monkey breaks things on purpose -* 🔄 Self-healing infrastructure - -**📦 Amazon:** -* ⚡ Deploy every **11.7 seconds** -* 🔧 "You build it, you run it" -* 👥 Two-pizza teams - -**🔍 Google:** -* 🛡️ Invented **SRE** -* 📊 Error budgets balance speed & reliability -* 📝 Blameless postmortems - ---- - -## 📍 Slide 36 – 🎯 Section 6: Reflection - -## 📝 Key Takeaways - -1. 🧩 **DevOps = Culture + Practices + Tools** -2. 🧱 **Break down silos** between Dev and Ops -3. 🤖 **Automate everything** repeatable -4. 📊 **Measure what matters** (DORA metrics) -5. 📝 **Learn from failures**, don't assign blame - -> 💡 DevOps isn't a destination. It's a direction. - ---- - -## 📍 Slide 37 – 🧠 The Mindset Shift - -| 😰 Old Mindset | 🚀 DevOps Mindset | -|---------------|------------------| -| 🙅 "Not my job" | 🤝 "Our responsibility" | -| 🚫 "Don't touch prod" | 💪 "Deploy with confidence" | -| 👉 "Who broke it?" | 🔍 "How do we prevent this?" | -| 😨 "Change is risky" | ✅ "Small changes = less risk" | -| 💻 "Works on my machine" | 🌍 "Works everywhere" | - -> ❓ Which mindset do you want? - ---- - -## 📍 Slide 38 – ✅ Your Progress - -## 🎓 What You Now Understand - -* ✅ Why DevOps emerged and what it solves -* ✅ The Three Ways and CAMS model -* ✅ How CI/CD, IaC, and observability fit together -* ✅ The DevOps lifecycle and how to measure it -* ✅ Real-world application of DevOps - -> 🚀 **You're ready for the labs.** - ---- - -## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L1_POST - ---- - -## 📍 Slide 40 – 🚀 What Comes Next - -## 📚 Next Lecture: Version Control with Git - -* 🐙 Git fundamentals -* 🌿 Branching strategies -* 🤝 Collaboration workflows -* 💻 Hands-on: Your first pull request - -**🎉 Your journey has begun.** - -> 🌊 From chaos to flow — one commit at a time. - -```mermaid -flowchart LR - You[👤 You] --> Skills[🛠️ DevOps Skills] - Skills --> Impact[💎 Real Impact] - Impact --> Career[🚀 Career Growth] -``` - -**👋 See you in the next lecture!** - ---- - -## 📚 Resources & Further Reading - -**📕 Books:** -* 📖 *The Phoenix Project* — Gene Kim -* 📖 *The DevOps Handbook* — Gene Kim et al. -* 📖 *Accelerate* — Nicole Forsgren - -**🔗 Links:** -* 🌐 [DORA State of DevOps](https://dora.dev) -* 🌐 [DevOps Roadmap](https://roadmap.sh/devops) -* 🌐 [12 Factor App](https://12factor.net) - ---- diff --git a/lectures/lec10.md b/lectures/lec10.md deleted file mode 100644 index ff30eefd3e..0000000000 --- a/lectures/lec10.md +++ /dev/null @@ -1,840 +0,0 @@ -# 📌 Lecture 10 — Helm Package Management: Templating Kubernetes - -## 📍 Slide 1 – 🚀 Welcome to Helm - -* 🌍 **Kubernetes manifests are powerful** — but repetitive -* 😰 Copy-pasting YAML for different environments is error-prone -* ⛵ **Helm** = the package manager for Kubernetes -* 🎯 This lecture: master charts, templating, and values management - -```mermaid -flowchart LR - Manifests[📝 Raw YAML] -->|⛵ Helm| Charts[📦 Charts] - Charts --> Templating[🔧 Templating] - Templating --> Environments[🌍 Any Environment] -``` - ---- - -## 📍 Slide 2 – 🎯 What You Will Learn - -* ✅ Understand Helm architecture and concepts -* ✅ Create production-ready Helm charts -* ✅ Use templating for multi-environment deployments -* ✅ Implement lifecycle hooks for advanced scenarios - -**🎓 Learning Outcomes:** -| # | Outcome | -|---|---------| -| 1 | 🧠 Explain charts, releases, and repositories | -| 2 | 🔍 Create charts with proper templating | -| 3 | 🛠️ Manage values for different environments | -| 4 | 🗺️ Implement hooks for lifecycle management | - ---- - -## 📍 Slide 3 – 📋 How This Lecture Works - -* 📚 **Concepts + Go templates** — hands-on focus -* 🎮 **Real-world scenarios** — multi-environment challenges -* 📝 **3 quiz checkpoints**: PRE / MID / POST -* 🛠️ **Best practices**: DRY, hooks, library charts - -**⏱️ Lecture Structure:** -``` -Section 0: Introduction (now) → 📝 PRE Quiz -Section 1: The Manifest Problem -Section 2: Helm Fundamentals -Section 3: Templating Deep Dive → 📝 MID Quiz -Section 4: Hooks & Advanced -Section 5: Production Helm -Section 6: Reflection → 📝 POST Quiz -``` - ---- - -## 📍 Slide 4 – ❓ The Big Question - -* 📊 **89%** of Kubernetes users use Helm -* ⏱️ Managing 100+ YAML files manually is **chaos** -* 💥 Different configs per environment = **copy-paste errors** - -> 💬 *"Is this the dev or prod manifest? Why are they different?"* — Every DevOps engineer - -**🤔 Think about it:** -* How do you manage configs for dev, staging, and prod? -* How do you share common patterns across applications? -* How do you version your Kubernetes deployments? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L10_PRE - ---- - -## 📍 Slide 6 – 🔥 Section 1: The Manifest Problem - -* 📝 **Raw YAML** works for one environment -* 📋 Need different values for dev, staging, prod -* 🔧 Copy-paste → divergence → bugs -* 💥 Result: **manifest sprawl** - -```mermaid -flowchart LR - Base[📝 Base YAML] --> Dev[📝 Dev YAML] - Base --> Staging[📝 Staging YAML] - Base --> Prod[📝 Prod YAML] - Dev --> Drift1[😱 Drift] - Staging --> Drift2[😱 Drift] - Prod --> Drift3[😱 Drift] -``` - ---- - -## 📍 Slide 7 – 😱 YAML Duplication - -* 📋 Same deployment, different image tags -* 📊 Same service, different replicas -* 🔧 Same ingress, different domains -* 💀 Changes require updating multiple files - -```yaml -# 😰 dev-deployment.yaml -replicas: 1 -image: myapp:latest - -# 😰 staging-deployment.yaml -replicas: 2 -image: myapp:v1.2.3 - -# 😰 prod-deployment.yaml -replicas: 5 -image: myapp:v1.2.3 -``` - -**📊 The Problem:** -* 🔍 Fix a bug? Update 3 files -* 🆕 New field? Add to all files -* 😰 Easy to miss one file - ---- - -## 📍 Slide 8 – 🔧 Manual Substitution Problems - -* 📝 `sed` and `envsubst` are fragile -* 🔍 No validation of resulting YAML -* 📊 No understanding of Kubernetes resources -* 💀 Silent failures - -> ⚠️ **sed is not a package manager** - -```bash -# 😰 This is fragile -sed -i "s/REPLICAS/3/g" deployment.yaml -envsubst < deployment.yaml.template > deployment.yaml -``` - -**💬 Discussion:** How do you currently manage environment differences? - ---- - -## 📍 Slide 9 – 😨 Version Chaos - -* 📅 "Which version is deployed in prod?" -* 🔧 No rollback mechanism -* 📋 No deployment history -* 💀 Can't reproduce past deployments - -> ⚠️ **Without versioning, you can't roll back safely** - -```mermaid -flowchart TD - Deploy1[📦 Deploy v1] --> Deploy2[📦 Deploy v2] - Deploy2 --> Deploy3[📦 Deploy v3] - Deploy3 --> Broken[💥 Broken!] - Broken --> Question[❓ What was v2?] -``` - ---- - -## 📍 Slide 10 – 💸 The Cost of Manifest Sprawl - -| 🔥 Problem | 💥 Impact | -|------------|-----------| -| 🐢 Update all files | Slow, error-prone | -| 📋 Inconsistency | "Works in dev, not prod" | -| 👉 No history | Can't audit changes | -| 🙈 No versioning | Risky rollbacks | - -**📈 Real Numbers:** -* 🏢 **Average K8s app**: 5-20 YAML files -* 🔄 **Environments**: 3-5 (dev, staging, prod, etc.) -* 📊 **Total files**: 15-100 per app (without Helm) -* ⛵ **With Helm**: 1 chart, unlimited environments - ---- - -## 📍 Slide 11 – 💡 Section 2: What Helm Is - -* ⛵ **Package manager** for Kubernetes -* 📦 **Charts** = packages of K8s resources -* 🔧 **Templating** = dynamic manifest generation -* 🔄 **Releases** = installed chart instances - -```mermaid -flowchart LR - Chart[📦 Chart] -->|🔧 + Values| Template[🔄 Templating] - Template --> Manifest[📝 K8s Manifests] - Manifest --> Release[🚀 Release] -``` - -**📖 Definition:** -> *Helm is a package manager for Kubernetes that helps you define, install, and upgrade complex Kubernetes applications using charts (packages of pre-configured resources).* - ---- - -## 📍 Slide 12 – 📦 Core Concepts - -```mermaid -flowchart TD - Chart[📦 Chart] --> Templates[📝 Templates] - Chart --> Values[📊 Values] - Chart --> ChartYaml[📋 Chart.yaml] - Templates -->|+| Values - Values --> Release[🚀 Release] -``` - -| 📦 Concept | 🎯 Purpose | -|-----------|----------| -| 📦 **Chart** | Package of K8s resources | -| 🚀 **Release** | Installed instance of chart | -| 📊 **Values** | Configuration parameters | -| 📁 **Repository** | Collection of charts | - ---- - -## 📍 Slide 13 – 📁 Chart Structure - -``` -mychart/ -├── Chart.yaml # 📋 Chart metadata -├── values.yaml # 📊 Default values -├── charts/ # 📦 Dependencies -└── templates/ # 📝 K8s manifests - ├── deployment.yaml - ├── service.yaml - ├── _helpers.tpl # 🔧 Template helpers - └── NOTES.txt # 📝 Post-install notes -``` - -**🔑 Key Files:** -* 📋 `Chart.yaml` — Name, version, description -* 📊 `values.yaml` — Default configuration -* 📝 `templates/` — Go templates for manifests -* 🔧 `_helpers.tpl` — Reusable template snippets - ---- - -## 📍 Slide 14 – 📋 Chart.yaml - -```yaml -apiVersion: v2 -name: my-web-app -description: A Helm chart for my web application -type: application - -# 📊 Chart version (SemVer) -version: 0.1.0 - -# 📦 Application version -appVersion: "1.0.0" - -# 📦 Dependencies -dependencies: - - name: common - version: 0.1.0 - repository: "file://../common" -``` - -**🔑 Important Fields:** -* `version` — Chart version (bump when chart changes) -* `appVersion` — Application version (your app's version) -* `dependencies` — Other charts this depends on - ---- - -## 📍 Slide 15 – ⚡ Before vs After Helm - -| 😰 Before | 🚀 After | -|----------|---------| -| 📅 Multiple YAML files per env | 📊 One values file per env | -| 📋 Manual substitution | 🔧 Go templating | -| 👉 No versioning | 📦 SemVer releases | -| 😨 Risky rollbacks | 🔙 `helm rollback` | -| 🐌 Copy-paste changes | ⚡ Single source of truth | -| 📝 No sharing | 📁 Chart repositories | - -> 🤔 Ready to package your Kubernetes apps? - ---- - -## 📍 Slide 16 – 🎮 Section 3: Templating Deep Dive - -## 🔧 Go Template Basics - -```yaml -# templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ .Release.Name }}-app - labels: - app: {{ .Values.appName }} -spec: - replicas: {{ .Values.replicaCount }} - template: - spec: - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" -``` - -**🔧 Template Syntax:** -* `{{ }}` — Template action -* `.Values` — From values.yaml -* `.Release` — Release information -* `.Chart` — From Chart.yaml - ---- - -## 📍 Slide 17 – 📊 Values Management - -```yaml -# values.yaml (defaults) -replicaCount: 1 -appName: my-app - -image: - repository: myuser/myapp - tag: latest - pullPolicy: IfNotPresent - -service: - type: ClusterIP - port: 80 - -resources: - limits: - cpu: 200m - memory: 256Mi - requests: - cpu: 100m - memory: 128Mi -``` - -**🔧 Override Values:** -```bash -# File override -helm install myrelease ./mychart -f values-prod.yaml - -# Command line override -helm install myrelease ./mychart --set replicaCount=5 -``` - ---- - -## 📍 Slide 18 – 🌍 Multi-Environment Values - -```yaml -# values-dev.yaml -replicaCount: 1 -image: - tag: latest -resources: - limits: - cpu: 100m - memory: 128Mi - -# values-prod.yaml -replicaCount: 5 -image: - tag: v1.2.3 -resources: - limits: - cpu: 500m - memory: 512Mi -``` - -**🚀 Deploy to Different Environments:** -```bash -# Development -helm install myapp-dev ./mychart -f values-dev.yaml - -# Production -helm install myapp-prod ./mychart -f values-prod.yaml -``` - ---- - -## 📍 Slide 19 – 🔧 Template Functions - -```yaml -# Using functions -name: {{ .Values.name | lower | trunc 63 }} - -# Default values -tag: {{ .Values.image.tag | default .Chart.AppVersion }} - -# Conditional -{{- if .Values.ingress.enabled }} -# ... ingress resource -{{- end }} - -# Range (loop) -{{- range .Values.env }} -- name: {{ .name }} - value: {{ .value | quote }} -{{- end }} -``` - -**🔧 Common Functions:** -| 🔧 Function | 🎯 Purpose | -|------------|----------| -| `default` | Provide fallback value | -| `quote` | Add quotes | -| `lower/upper` | Case conversion | -| `trunc` | Truncate string | -| `include` | Include template | - ---- - -## 📍 Slide 20 – 🔧 Helper Templates - -```yaml -# templates/_helpers.tpl -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "mychart.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "mychart.labels" -}} -helm.sh/chart: {{ include "mychart.chart" . }} -app.kubernetes.io/name: {{ .Chart.Name }} -app.kubernetes.io/instance: {{ .Release.Name }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} -``` - -**🔧 Using Helpers:** -```yaml -metadata: - labels: - {{- include "mychart.labels" . | nindent 4 }} -``` - ---- - -## 📍 Slide 21 – 📊 Built-in Objects - -```mermaid -flowchart TD - Objects[📦 Built-in Objects] - Objects --> Values[.Values] - Objects --> Chart[.Chart] - Objects --> Release[.Release] - Objects --> Template[.Template] - Objects --> Files[.Files] -``` - -| 📦 Object | 🎯 Contains | -|----------|----------| -| `.Values` | Values from values.yaml + overrides | -| `.Chart` | Contents of Chart.yaml | -| `.Release` | Release name, namespace, revision | -| `.Template` | Current template info | -| `.Files` | Access to non-template files | - ---- - -## 📍 Slide 22 – 🧪 Testing Charts - -```bash -# 📋 Lint chart for errors -helm lint ./mychart - -# 📝 Render templates locally -helm template myrelease ./mychart - -# 🔍 Dry run against cluster -helm install --dry-run --debug myrelease ./mychart - -# 📊 Show computed values -helm get values myrelease - -# 📝 Show rendered manifests -helm get manifest myrelease -``` - -**🧪 Testing Workflow:** -1. 📋 `helm lint` — syntax check -2. 📝 `helm template` — verify output -3. 🔍 `--dry-run` — validate against cluster -4. 🚀 `helm install` — deploy - ---- - -## 📍 Slide 23 – 📊 Helm Commands - -```bash -# 📦 Create new chart -helm create mychart - -# 🚀 Install chart -helm install myrelease ./mychart - -# 📋 List releases -helm list - -# 🔄 Upgrade release -helm upgrade myrelease ./mychart - -# 🔙 Rollback release -helm rollback myrelease 1 - -# 🗑️ Uninstall release -helm uninstall myrelease - -# 📊 Show release history -helm history myrelease -``` - ---- - -## 📍 Slide 24 – 🔗 Chart Dependencies - -```yaml -# Chart.yaml -dependencies: - - name: postgresql - version: 12.0.0 - repository: https://charts.bitnami.com/bitnami - condition: postgresql.enabled -``` - -```bash -# Download dependencies -helm dependency update ./mychart - -# Build dependencies -helm dependency build ./mychart -``` - -**🔗 Dependency Features:** -* 📦 Include other charts as sub-charts -* 🔧 Override sub-chart values -* 🔀 Conditional inclusion - ---- - -## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L10_MID - ---- - -## 📍 Slide 26 – 🎣 Section 4: Lifecycle Hooks - -## 🎣 What Are Hooks? - -* 🎯 **Execute actions** at specific points -* 📦 Run jobs before/after install/upgrade -* 🗑️ Cleanup after completion -* 🔧 Database migrations, tests, notifications - -```mermaid -flowchart LR - PreInstall[🎣 pre-install] --> Install[🚀 Install] - Install --> PostInstall[🎣 post-install] -``` - ---- - -## 📍 Slide 27 – 🎣 Hook Types - -| 🎣 Hook | ⏱️ When | -|--------|--------| -| `pre-install` | Before resources installed | -| `post-install` | After all resources ready | -| `pre-upgrade` | Before upgrade | -| `post-upgrade` | After upgrade complete | -| `pre-delete` | Before deletion | -| `post-delete` | After deletion | -| `pre-rollback` | Before rollback | -| `post-rollback` | After rollback | - ---- - -## 📍 Slide 28 – 📝 Hook Example - -```yaml -# templates/pre-install-job.yaml -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ .Release.Name }}-pre-install - annotations: - "helm.sh/hook": pre-install - "helm.sh/hook-weight": "-5" - "helm.sh/hook-delete-policy": hook-succeeded -spec: - template: - spec: - restartPolicy: Never - containers: - - name: pre-install - image: busybox - command: ['sh', '-c', 'echo Pre-install running && sleep 5'] -``` - -**🔑 Hook Annotations:** -* `helm.sh/hook` — Hook type -* `helm.sh/hook-weight` — Execution order (lower first) -* `helm.sh/hook-delete-policy` — When to delete - ---- - -## 📍 Slide 29 – 🏗️ Library Charts - -```mermaid -flowchart TD - Library[📚 Library Chart] --> App1[📦 App 1] - Library --> App2[📦 App 2] - Library --> App3[📦 App 3] -``` - -**📚 Library Chart:** -* 🚫 Cannot be installed directly -* 📝 Contains only templates -* 🔄 Shared across multiple charts - -```yaml -# Chart.yaml -apiVersion: v2 -name: common-lib -type: library # 📚 Library type -version: 0.1.0 -``` - ---- - -## 📍 Slide 30 – 📊 Helm Metrics - -| 📊 Metric | 📏 Measures | 🏆 Target | -|-----------|------------|---------| -| 📦 **Chart Version** | Tracking | SemVer | -| 🔄 **Release Revision** | Upgrade count | Documented | -| ⏱️ **Deploy Time** | Chart install | < 5 min | -| 🧪 **Lint Errors** | Chart quality | 0 | - -> 📚 Version everything! - -**🤔 Question:** How do you track what's deployed? - ---- - -## 📍 Slide 31 – 🏢 Section 5: Production Helm - -## 📅 A Day with Helm - -**☀️ Morning:** -* 📋 Review chart PR -* 🧪 `helm lint` and `helm template` -* ✅ Merge changes - -**🌤️ Afternoon:** -* 📊 Update values-prod.yaml -* 🚀 `helm upgrade myapp ./mychart -f values-prod.yaml` -* 📈 Watch rollout: `kubectl rollout status` - -**🌙 Evening:** -* 💥 Issue detected -* 🔙 `helm rollback myapp 3` -* ⏱️ **Rollback in 30 seconds** - ---- - -## 📍 Slide 32 – 👥 Team Helm Workflow - -| 👤 Role | 🎯 Helm Responsibility | -|---------|----------------------| -| 👨‍💻 **Developer** | Define values requirements | -| 🔧 **DevOps** | Create and maintain charts | -| 🛡️ **SRE** | Manage releases, rollbacks | -| 📊 **Platform** | Build chart standards | - -**🔗 GitOps Flow:** -```mermaid -flowchart LR - PR[📝 Chart PR] --> Lint[🧪 Lint] - Lint --> Review[👀 Review] - Review --> Merge[✅ Merge] - Merge --> ArgoCD[🔄 ArgoCD] - ArgoCD --> Helm[⛵ Helm Install] -``` - ---- - -## 📍 Slide 33 – 🔐 Production Best Practices - -```yaml -# ✅ Good: Specific versions -image: - tag: v1.2.3 # Not 'latest' - -# ✅ Good: Resource limits always -resources: - limits: - cpu: 500m - memory: 512Mi - -# ✅ Good: Health probes always -livenessProbe: - enabled: true -readinessProbe: - enabled: true -``` - -**🛡️ Production Checklist:** -* ✅ Specific image tags (not `latest`) -* ✅ Resource limits defined -* ✅ Health probes enabled -* ✅ Values documented -* ✅ Chart versioned with SemVer - ---- - -## 📍 Slide 34 – 📈 Career Path: Helm Skills - -```mermaid -flowchart LR - Junior[🌱 Junior: Using charts] --> Mid[💼 Mid: Creating charts] - Mid --> Senior[⭐ Senior: Library charts & standards] - Senior --> Principal[🏆 Principal: Chart ecosystem] -``` - -**🛠️ Skills to Build:** -* 📝 Go template fluency -* 📦 Chart design patterns -* 🔗 Dependency management -* 🎣 Hook implementation -* 📁 Repository management - ---- - -## 📍 Slide 35 – 🌍 Real Company Examples - -**🏢 Helm at Scale:** -* 📦 **Bitnami**: 100+ production charts -* 🔍 **Google**: GKE uses Helm internally -* 🎬 **Netflix**: Custom chart ecosystem - -**☁️ Public Charts:** -* 📊 **Prometheus**: helm-charts/prometheus -* 📋 **Grafana**: helm-charts/grafana -* 🐘 **PostgreSQL**: bitnami/postgresql - -**📊 Stats:** -* ⛵ **10,000+** public charts -* 📦 **89%** K8s users use Helm -* 🏢 **Standard** for K8s packaging - ---- - -## 📍 Slide 36 – 🎯 Section 6: Reflection - -## 📝 Key Takeaways - -1. ⛵ **Helm is the package manager** for Kubernetes -2. 📦 **Charts package** related K8s resources -3. 🔧 **Templating** enables multi-environment deploys -4. 📊 **Values** customize without changing templates -5. 🎣 **Hooks** handle lifecycle events - -> 💡 Never hardcode in templates — parametrize everything. - ---- - -## 📍 Slide 37 – 🧠 The Mindset Shift - -| 😰 Old Mindset | ⛵ Helm Mindset | -|---------------|------------------| -| 🙅 "Copy YAML for each env" | 📊 "Different values, same chart" | -| 🚫 "sed for substitution" | 🔧 "Go templates" | -| 👉 "Manual versioning" | 📦 "SemVer releases" | -| 😨 "Risky rollbacks" | 🔙 "helm rollback" | -| 💻 "My chart, my rules" | 📚 "Shared libraries" | - -> ❓ Which mindset describes your team? - ---- - -## 📍 Slide 38 – ✅ Your Progress - -## 🎓 What You Now Understand - -* ✅ Helm architecture and concepts -* ✅ Chart creation and structure -* ✅ Go template syntax -* ✅ Multi-environment values management -* ✅ Lifecycle hooks - -> 🚀 **You're ready for Lab 10: Helm Charts** - ---- - -## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L10_POST - ---- - -## 📍 Slide 40 – 🚀 What Comes Next - -## 📚 Course Continuation - -* 🔐 Lab 11: Secrets with Vault -* ⚙️ Lab 12: ConfigMaps -* 🔄 Lab 13: ArgoCD GitOps -* 📊 Lab 14: StatefulSets -* 🔍 Lab 15: K8s Monitoring - -**🎉 You've completed the Helm fundamentals!** - -> ⛵ From raw YAML to packaged charts — one template at a time. - -```mermaid -flowchart LR - You[👤 You] --> Helm[⛵ Helm Skills] - Helm --> Packaging[📦 K8s Packaging] - Packaging --> Career[🚀 Career Growth] -``` - -**👋 Continue your DevOps journey!** - ---- - -## 📚 Resources & Further Reading - -**📕 Books:** -* 📖 *Learning Helm* — Matt Butcher -* 📖 *Helm in Action* — Matt Palmer -* 📖 *Kubernetes Patterns* — Bilgin Ibryam - -**🔗 Links:** -* 🌐 [Helm Documentation](https://helm.sh/docs/) -* 🌐 [Chart Best Practices](https://helm.sh/docs/chart_best_practices/) -* 🌐 [Artifact Hub](https://artifacthub.io/) - ---- diff --git a/lectures/lec11.md b/lectures/lec11.md deleted file mode 100644 index 779e7917bd..0000000000 --- a/lectures/lec11.md +++ /dev/null @@ -1,759 +0,0 @@ -# 📌 Lecture 11 — Secret Management: Protecting Your Crown Jewels - -## 📍 Slide 1 – 🔐 Welcome to Secret Management - -* 🌍 **Your Helm charts are beautiful** — but where do passwords go? -* 😰 Hardcoded secrets in code = ticking time bomb -* 🔐 **Secret management** = keeping credentials safe AND accessible -* 🎯 This lecture: from base64 encoding to enterprise-grade Vault - -```mermaid -flowchart LR - Bad[😱 Hardcoded] -->|🔐 Secrets| K8s[☸️ K8s Secrets] - K8s -->|🏰 Enterprise| Vault[🔒 HashiCorp Vault] - Vault --> Secure[✅ Secure Apps] -``` - ---- - -## 📍 Slide 2 – 🎯 What You Will Learn - -* ✅ Understand why secret management matters -* ✅ Create and consume Kubernetes Secrets -* ✅ Recognize encoding vs encryption difference -* ✅ Integrate HashiCorp Vault with Kubernetes - -**🎓 Learning Outcomes:** -| # | Outcome | -|---|---------| -| 1 | 🧠 Explain the risks of poor secret management | -| 2 | 🔍 Create K8s Secrets via kubectl and Helm | -| 3 | 🛠️ Configure Vault sidecar injection | -| 4 | 🗺️ Choose appropriate secret management strategy | - ---- - -## 📍 Slide 3 – 📋 How This Lecture Works - -* 🔐 **Security-first mindset** — think like an attacker -* 🎮 **Real breach scenarios** — learn from others' mistakes -* 📝 **3 quiz checkpoints**: PRE / MID / POST -* 🛠️ **Hands-on patterns**: Secrets, Vault, injection - -**⏱️ Lecture Structure:** -``` -Section 0: Introduction → 📝 PRE Quiz -Section 1: The Secrets Problem -Section 2: Kubernetes Secrets -Section 3: Encoding vs Encryption → 📝 MID Quiz -Section 4: HashiCorp Vault -Section 5: Production Patterns → 📝 POST Quiz -``` - ---- - -## 📍 Slide 4 – 💀 The Big Question - -> 💬 *"The only truly secure system is one that is powered off, cast in a block of concrete and sealed in a lead-lined room with armed guards."* — Gene Spafford - -**🔥 Shocking Stats:** -* 😱 **83%** of organizations have experienced credential theft -* 💸 Average cost of data breach: **$4.45 million** (2023) -* ⏱️ Average time to detect breach: **277 days** - -> 🤔 **Think:** How many passwords are hardcoded in YOUR projects right now? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L11_PRE - ---- - -## 📍 Slide 6 – 🔥 Section 1: The Secrets Problem - -* 🎯 **The Challenge:** Apps need credentials to function -* ⚔️ **The Conflict:** Security vs Convenience - -```mermaid -flowchart LR - subgraph 😰 Developer - D1[🚀 Ship Fast] - D2[🔧 Easy Access] - end - subgraph 🔐 Security - S1[🛡️ Protect Data] - S2[📋 Audit Access] - end - D1 <-->|⚔️ Tension| S1 - D2 <-->|⚔️ Tension| S2 -``` - -> 🤔 **Discussion:** Have you ever committed a password to git? - ---- - -## 📍 Slide 7 – 😱 The Hardcoding Horror - -**❌ What developers actually do:** - -```python -# ❌ BAD: Hardcoded in code -DATABASE_URL = "postgres://admin:SuperSecret123@db.prod.com/myapp" -API_KEY = "sk-1234567890abcdef" - -# ❌ BAD: In docker-compose.yml committed to git -environment: - - DB_PASSWORD=MyPassword123 -``` - -**💥 What can go wrong:** -* 🔍 Git history is forever (even after deletion) -* 🌍 Public repos = public secrets -* 👥 Every developer has production passwords -* 📝 No audit trail of who accessed what - ---- - -## 📍 Slide 8 – 💥 Real Breach: Uber 2016 - -**📰 What Happened:** -* 😱 Developers hardcoded AWS credentials in GitHub repo -* 🔓 Attackers found credentials, accessed S3 bucket -* 💾 **57 million** user records stolen -* 💸 **$148 million** settlement - -```mermaid -flowchart LR - A[👨‍💻 Dev commits AWS keys] --> B[🔍 Attacker finds repo] - B --> C[🔓 Access S3 bucket] - C --> D[💾 57M records stolen] - D --> E[💸 $148M settlement] -``` - -> ⚠️ **Lesson:** Secrets in code = breach waiting to happen - ---- - -## 📍 Slide 9 – 🔓 Environment Variables: Better but Not Enough - -**✅ Better than hardcoding:** -```bash -export DATABASE_PASSWORD="secret123" -``` - -**❌ Still problematic:** -* 📋 `ps aux` can expose env vars -* 🐳 Docker inspect shows environment -* 📝 No encryption at rest -* 🔄 No rotation mechanism -* 👥 No access control - -```bash -# Anyone on the system can see: -$ docker inspect myapp | grep -A 10 "Env" -``` - -> 🤔 **Think:** Where do YOUR environment variables come from? - ---- - -## 📍 Slide 10 – 📊 The Cost of Poor Secret Management - -| 🔥 Problem | 💥 Impact | 📊 Stats | -|------------|-----------|----------| -| 😱 Leaked credentials | 🔓 Unauthorized access | 83% of breaches | -| 🔄 No rotation | 📅 Stale passwords | Avg age: 2+ years | -| 👥 Shared secrets | 🕵️ No accountability | 65% share creds | -| 📝 No audit | 🤷 Unknown access | 70% can't audit | - -**💡 The Solution Spectrum:** - -```mermaid -flowchart LR - A[😱 Hardcoded] --> B[🔧 Env Vars] - B --> C[☸️ K8s Secrets] - C --> D[🔒 Vault] - style A fill:#ff6b6b - style B fill:#ffd93d - style C fill:#6bcb77 - style D fill:#4d96ff -``` - ---- - -## 📍 Slide 11 – ☸️ Section 2: Kubernetes Secrets - -**🎯 What are K8s Secrets?** -* 📦 First-class Kubernetes objects for sensitive data -* 🔐 Separate from ConfigMaps (security-focused) -* 🚀 Native integration with pods - -```mermaid -flowchart TD - Secret[🔐 Secret] --> |Volume| Pod1[📦 Pod] - Secret --> |Env Var| Pod2[📦 Pod] - Secret --> |API| Pod3[📦 Pod] -``` - -**📋 Secret Types:** -* 🔑 `Opaque` — generic key-value -* 🐳 `docker-registry` — image pull credentials -* 🔒 `tls` — TLS certificates - ---- - -## 📍 Slide 12 – 🛠️ Creating Secrets with kubectl - -**📝 From literals:** -```bash -kubectl create secret generic db-creds \ - --from-literal=username=admin \ - --from-literal=password=SuperSecret123 -``` - -**📁 From files:** -```bash -kubectl create secret generic tls-cert \ - --from-file=cert.pem \ - --from-file=key.pem -``` - -**👀 Viewing secrets:** -```bash -kubectl get secret db-creds -o yaml -# Data is base64 encoded - -# Decode: -echo "U3VwZXJTZWNyZXQxMjM=" | base64 -d -# Output: SuperSecret123 -``` - ---- - -## 📍 Slide 13 – ⚠️ The Base64 Trap - -> ⚠️ **Critical Understanding:** Base64 is ENCODING, not ENCRYPTION! - -```bash -# Encoding (reversible by anyone): -echo "password123" | base64 -# cGFzc3dvcmQxMjMK - -# Decoding (no key needed): -echo "cGFzc3dvcmQxMjMK" | base64 -d -# password123 -``` - -**🔐 Encryption vs Encoding:** - -| 🔄 Encoding | 🔐 Encryption | -|-------------|---------------| -| ✅ Reversible by anyone | 🔑 Needs key to decrypt | -| 📝 Not secure | 🔒 Mathematically secure | -| 🚀 Fast, no overhead | ⚡ Computational cost | -| 📦 Data format change | 🛡️ Confidentiality | - ---- - -## 📍 Slide 14 – 📦 Consuming Secrets in Pods - -**🔧 As environment variables:** -```yaml -env: - - name: DB_PASSWORD - valueFrom: - secretKeyRef: - name: db-creds - key: password -``` - -**📁 As volume mount:** -```yaml -volumes: - - name: secret-volume - secret: - secretName: db-creds -containers: - - volumeMounts: - - name: secret-volume - mountPath: /etc/secrets - readOnly: true -``` - -> 💡 **Best Practice:** Volume mounts are more secure than env vars (not visible in `docker inspect`) - ---- - -## 📍 Slide 15 – 📊 Before vs After: Basic Secret Management - -| 😱 Before (Hardcoded) | ✅ After (K8s Secrets) | -|-----------------------|------------------------| -| 📝 Secrets in code | 📦 Secrets in K8s API | -| 🌍 Visible in git history | 🔐 Separate from code | -| 👥 Everyone has access | 🛡️ RBAC controls | -| 🔄 Change = redeploy code | 🔧 Change secret only | -| 📋 No audit trail | 📝 K8s audit logs | - -> 🤔 **Question:** Is K8s Secrets enough for production? - ---- - -## 📍 Slide 16 – 🔒 Section 3: etcd Encryption - -**😰 The Problem:** -* 🗄️ K8s stores secrets in etcd -* 📝 By default: base64 encoded only -* 🔓 etcd access = all secrets exposed - -**✅ The Solution: Encryption at Rest** -```yaml -apiVersion: apiserver.config.k8s.io/v1 -kind: EncryptionConfiguration -resources: - - resources: - - secrets - providers: - - aescbc: - keys: - - name: key1 - secret: - - identity: {} -``` - ---- - -## 📍 Slide 17 – 🔐 K8s Secrets Limitations - -**⚠️ Still Missing:** -* 🔄 **No automatic rotation** — manual process -* 📊 **Limited audit** — who accessed what? -* 🌍 **K8s-only** — what about non-K8s apps? -* 🔑 **Static secrets** — no dynamic generation -* 🏢 **No centralization** — per-cluster management - -```mermaid -flowchart TD - subgraph 😰 Limitations - A[🔄 No Rotation] - B[📊 Limited Audit] - C[🌍 K8s Only] - D[🔑 Static Only] - end - E[🏰 Need: Enterprise Solution] --> F[🔒 HashiCorp Vault] -``` - ---- - -## 📍 Slide 18 – 📝 QUIZ — DEVOPS_L11_MID - ---- - -## 📍 Slide 19 – 🏰 Section 4: HashiCorp Vault - -**🎯 What is Vault?** -* 🔐 Enterprise-grade secret management -* 🔑 Dynamic secret generation -* 📊 Complete audit logging -* 🔄 Automatic rotation -* 🌍 Platform agnostic - -```mermaid -flowchart LR - subgraph 🏰 Vault - A[🔐 Secret Engine] - B[🔑 Auth Methods] - C[📋 Policies] - D[📊 Audit] - end - K8s[☸️ Kubernetes] --> B - B --> A - A --> Apps[📦 Applications] - D --> Logs[📝 Audit Logs] -``` - ---- - -## 📍 Slide 20 – 🏗️ Vault Architecture - -```mermaid -flowchart TD - subgraph 👥 Clients - K8s[☸️ K8s Pods] - CLI[💻 CLI] - API[🔌 API] - end - subgraph 🏰 Vault Server - Auth[🔑 Auth Methods] - Policy[📋 Policies] - Secrets[🔐 Secret Engines] - Audit[📊 Audit Device] - end - subgraph 💾 Storage - Backend[🗄️ Storage Backend] - end - K8s --> Auth - CLI --> Auth - API --> Auth - Auth --> Policy - Policy --> Secrets - Secrets --> Backend - Auth --> Audit -``` - -**🔑 Key Concepts:** -* 🔐 **Secret Engines** — where secrets live (KV, database, PKI) -* 🔑 **Auth Methods** — how clients authenticate -* 📋 **Policies** — who can access what - ---- - -## 📍 Slide 21 – 🔑 Vault Auth Methods - -| 🔑 Method | 📝 Description | 🎯 Use Case | -|-----------|----------------|-------------| -| ☸️ Kubernetes | Service account JWT | K8s pods | -| 🔐 AppRole | Role ID + Secret ID | CI/CD pipelines | -| 👤 Userpass | Username/password | Humans | -| 🌐 OIDC | SSO integration | Enterprise SSO | -| ☁️ AWS/GCP/Azure | Cloud IAM | Cloud workloads | - -**☸️ Kubernetes Auth Flow:** -```mermaid -sequenceDiagram - Pod->>Vault: JWT token (ServiceAccount) - Vault->>K8s API: Validate token - K8s API->>Vault: Token valid ✅ - Vault->>Pod: Vault token + secrets -``` - ---- - -## 📍 Slide 22 – 📋 Vault Policies - -**🎯 Policies control access:** -```hcl -# Allow read on specific path -path "secret/data/myapp/*" { - capabilities = ["read", "list"] -} - -# Deny access to admin secrets -path "secret/data/admin/*" { - capabilities = ["deny"] -} -``` - -**🛡️ Principle of Least Privilege:** -* ✅ Apps only access their secrets -* ✅ Read-only where possible -* ✅ Separate policies per environment - ---- - -## 📍 Slide 23 – 💉 Vault Agent Sidecar Injection - -**🎯 The Pattern:** -* 📦 Vault Agent runs as sidecar container -* 🔄 Automatically fetches and renews secrets -* 📁 Writes secrets to shared volume -* 🚀 App reads from filesystem - -```mermaid -flowchart LR - subgraph 📦 Pod - App[🚀 App Container] - Agent[🔐 Vault Agent] - Vol[📁 Shared Volume] - end - Vault[🏰 Vault Server] - Agent -->|🔑 Auth| Vault - Vault -->|🔐 Secrets| Agent - Agent -->|📝 Write| Vol - App -->|📖 Read| Vol -``` - ---- - -## 📍 Slide 24 – 🏷️ Vault Annotations - -**📝 Enable injection:** -```yaml -metadata: - annotations: - vault.hashicorp.com/agent-inject: "true" - vault.hashicorp.com/role: "myapp" - vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config" -``` - -**📁 Secrets appear at:** -``` -/vault/secrets/config -``` - -**🔧 Template for custom format:** -```yaml -vault.hashicorp.com/agent-inject-template-config: | - {{- with secret "secret/data/myapp/config" -}} - DB_PASSWORD={{ .Data.data.password }} - {{- end -}} -``` - ---- - -## 📍 Slide 25 – 🚀 Vault in Kubernetes: Full Flow - -```mermaid -sequenceDiagram - participant Pod - participant Injector as Vault Injector - participant Agent as Vault Agent - participant Vault - - Pod->>Injector: Pod created with annotations - Injector->>Pod: Inject sidecar container - Agent->>Vault: Authenticate (K8s JWT) - Vault->>Agent: Return Vault token - Agent->>Vault: Request secrets - Vault->>Agent: Return secrets - Agent->>Pod: Write to /vault/secrets/ - Pod->>Pod: App reads secrets -``` - ---- - -## 📍 Slide 26 – 🔄 Section 5: Dynamic Secrets - -**🎯 Static vs Dynamic:** - -| 🔑 Static Secrets | 🔄 Dynamic Secrets | -|-------------------|-------------------| -| 📝 Created manually | 🤖 Generated on-demand | -| ♾️ Live forever | ⏱️ Short TTL | -| 👥 Shared | 👤 Unique per request | -| 🔄 Manual rotation | 🔄 Auto-expires | - -**💡 Example: Database credentials** -```bash -vault read database/creds/readonly -# Key Value -# lease_id database/creds/readonly/abc123 -# lease_duration 1h -# username v-kubernetes-readonly-xyz789 -# password A1b2C3d4E5f6G7h8 -``` - ---- - -## 📍 Slide 27 – 📊 Secret Management Comparison - -| 🔧 Feature | 🔓 Env Vars | ☸️ K8s Secrets | 🏰 Vault | -|------------|-------------|----------------|----------| -| 🔐 Encryption | ❌ None | ⚠️ Optional | ✅ Always | -| 🔄 Rotation | ❌ Manual | ❌ Manual | ✅ Auto | -| 📊 Audit | ❌ None | ⚠️ Basic | ✅ Full | -| 🔑 Dynamic | ❌ No | ❌ No | ✅ Yes | -| 🌍 Multi-platform | ✅ Yes | ❌ K8s only | ✅ Yes | -| 📈 Complexity | 🟢 Low | 🟡 Medium | 🔴 High | - ---- - -## 📍 Slide 28 – 🗺️ Course Context: Where Secrets Fit - -```mermaid -flowchart TD - subgraph 🏗️ Foundation - L2[📦 Lab 2: Docker] - L10[⛵ Lab 10: Helm] - end - subgraph 🔐 Security - L11[🔒 Lab 11: Secrets] - end - subgraph 📋 Config - L12[📁 Lab 12: ConfigMaps] - end - subgraph 🚀 Deployment - L13[🔄 Lab 13: ArgoCD] - end - L2 --> L10 - L10 --> L11 - L11 --> L12 - L12 --> L13 - style L11 fill:#4d96ff -``` - ---- - -## 📍 Slide 29 – 📈 Security Metrics - -| 📊 Metric | 📝 Description | 🎯 Target | -|-----------|----------------|-----------| -| 🔄 Secret Age | Time since rotation | < 90 days | -| 📊 Access Audit | % of accesses logged | 100% | -| 🔐 Encryption | % secrets encrypted | 100% | -| 👥 Shared Secrets | Secrets used by >1 app | 0 | -| ⏱️ TTL Compliance | Secrets with TTL | > 80% | - -> 🤔 **Question:** How would you measure secret security in your organization? - ---- - -## 📍 Slide 30 – ✅ Secret Management Best Practices - -**🛡️ The Golden Rules:** - -1. 🚫 **Never commit secrets** to version control -2. 🔄 **Rotate regularly** — automate where possible -3. 📋 **Audit everything** — know who accessed what -4. 🔐 **Encrypt at rest** — etcd encryption minimum -5. 👤 **Least privilege** — only what's needed -6. ⏱️ **Short-lived** — dynamic secrets when possible - -```mermaid -flowchart LR - A[🔐 Encrypt] --> B[🔄 Rotate] - B --> C[📋 Audit] - C --> D[👤 Least Privilege] - D --> A -``` - ---- - -## 📍 Slide 31 – 👨‍💻 Day in the Life: Secret Management - -**☀️ Morning:** -* ☕ Check Vault audit logs for anomalies -* 🔄 Review expiring secrets dashboard -* 📋 Approve new secret access requests - -**🌤️ Afternoon:** -* 🛠️ Help dev team configure Vault injection -* 📝 Update policies for new microservice -* 🔐 Rotate database credentials (automated) - -**🌙 Evening:** -* 📊 Review daily access report -* 🔔 Set up alerts for unusual patterns -* 📚 Document new secret paths - ---- - -## 📍 Slide 32 – 👥 Roles & Secret Management - -| 👤 Role | 🔐 Secret Responsibilities | -|---------|---------------------------| -| 🧑‍💻 Developer | Use secrets correctly, never commit | -| 🔧 DevOps | Configure injection, manage policies | -| 🛡️ Security | Audit access, define requirements | -| 🏗️ Platform | Maintain Vault infrastructure | -| 📋 Compliance | Ensure rotation, audit trails | - -> 💡 **Common Thread:** Everyone shares responsibility for secrets - ---- - -## 📍 Slide 33 – 🏢 Real-World: How Companies Handle Secrets - -**🎬 Netflix:** -* 🔐 Custom secret management platform -* 🔄 Automatic rotation every 24 hours -* 📊 Real-time access monitoring - -**📦 Shopify:** -* 🏰 HashiCorp Vault at scale -* 🔑 Dynamic database credentials -* 👤 Per-service unique credentials - -**🚗 Uber:** -* 📚 Learned from 2016 breach -* 🔐 Zero hardcoded secrets policy -* 🤖 Automated secret scanning in CI - ---- - -## 📍 Slide 34 – 🎯 Decision Framework: Choosing a Solution - -```mermaid -flowchart TD - Start[🤔 Need Secret Management] --> Q1{Small team?
Simple app?} - Q1 -->|Yes| K8s[☸️ K8s Secrets + etcd encryption] - Q1 -->|No| Q2{Multi-platform?
Compliance needs?} - Q2 -->|Yes| Vault[🏰 HashiCorp Vault] - Q2 -->|No| Q3{Cloud-native only?} - Q3 -->|Yes| Cloud[☁️ Cloud Secret Manager] - Q3 -->|No| Vault -``` - ---- - -## 📍 Slide 35 – 📝 Key Takeaways - -1. 🚫 **Never hardcode secrets** — it's a breach waiting to happen -2. 🔄 **Base64 ≠ encryption** — K8s Secrets need etcd encryption -3. 🏰 **Vault for enterprise** — when you need rotation, audit, dynamic -4. 💉 **Sidecar injection** — cleanest pattern for K8s + Vault -5. 📋 **Audit everything** — you can't secure what you can't see - -> 💬 *"Security is not a product, but a process."* — Bruce Schneier - ---- - -## 📍 Slide 36 – 🔄 Mindset Shift - -| 😰 Old Mindset | 🚀 New Mindset | -|----------------|----------------| -| "Hardcode for convenience" | "Secrets are separate from code" | -| "Base64 is secure enough" | "Encryption at rest is mandatory" | -| "Rotate when breached" | "Rotate proactively and automatically" | -| "Trust developers" | "Least privilege for everyone" | -| "Hope nobody finds it" | "Assume breach, audit everything" | - -> 🤔 **Which mindset do you currently have?** - ---- - -## 📍 Slide 37 – ✅ Your Progress - -**🎓 You can now:** -- [x] 🧠 Explain why secret management matters -- [x] 🔍 Create K8s Secrets via kubectl and Helm -- [x] ⚠️ Recognize encoding vs encryption -- [x] 🛠️ Configure Vault sidecar injection -- [x] 🗺️ Choose appropriate secret management strategy - -**🚀 Ready for:** Lab 11 — Kubernetes Secrets & HashiCorp Vault - ---- - -## 📍 Slide 38 – 📝 QUIZ — DEVOPS_L11_POST - ---- - -## 📍 Slide 39 – 🚀 What's Next - -**📅 Next Lecture:** Configuration & Persistent Storage -* 📁 ConfigMaps for non-sensitive config -* 💾 Persistent Volumes for data -* 🔧 Mounting strategies - -```mermaid -flowchart LR - Now[🔐 Secrets] --> Next[📁 ConfigMaps] - Next --> Storage[💾 Storage] - Storage --> GitOps[🔄 GitOps] -``` - -> 💪 *"You've secured the secrets. Now let's configure everything else!"* - ---- - -## 📚 Resources - -**📖 Books:** -* "HashiCorp Vault: Securing Secrets" — by various authors -* "Kubernetes Security" — by Liz Rice -* "Zero Trust Networks" — by Evan Gilman - -**🔗 Links:** -* [Vault Documentation](https://developer.hashicorp.com/vault/docs) -* [K8s Secrets Best Practices](https://kubernetes.io/docs/concepts/security/secrets-good-practices/) -* [OWASP Secrets Management](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html) diff --git a/lectures/lec12.md b/lectures/lec12.md deleted file mode 100644 index ef8ff54778..0000000000 --- a/lectures/lec12.md +++ /dev/null @@ -1,854 +0,0 @@ -# 📌 Lecture 12 — Configuration & Storage: Externalizing Application State - -> 🎯 **From hardcoded configs to dynamic, portable applications** - ---- - -## 📍 Slide 1 – 🚀 Welcome to Configuration Management - -Last lecture we secured our **secrets**. But what about everything else? - -* 🔧 **Database URLs** — different per environment -* 📊 **Feature flags** — enable/disable features dynamically -* 📁 **Data persistence** — where does your app store files? -* ⚙️ **App settings** — logging levels, timeouts, cache sizes - -```mermaid -flowchart LR - A[😰 Hardcoded Config] --> B[🔧 Externalized Config] - B --> C[🚀 Portable Apps] - C --> D[💎 Any Environment] -``` - -> 🎯 **Goal:** Build applications that run anywhere without code changes - ---- - -## 📍 Slide 2 – 📚 Learning Outcomes - -By the end of this lecture, you will: - -| # | 🎯 Outcome | -|---|-----------| -| 1 | ✅ Understand the **12-Factor App** configuration principle | -| 2 | ✅ Create and use **ConfigMaps** for non-sensitive configuration | -| 3 | ✅ Differentiate between **ConfigMaps** and **Secrets** | -| 4 | ✅ Understand **Persistent Volumes** and storage in Kubernetes | -| 5 | ✅ Implement **PersistentVolumeClaims** for stateful applications | -| 6 | ✅ Apply configuration management **best practices** | - ---- - -## 📍 Slide 3 – 🗺️ Lecture Overview - -``` -┌─────────────────────────────────────────────────────────────┐ -│ SECTION 0: Introduction (Slides 1-4) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 PRE QUIZ (Slide 5) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 1: The Configuration Problem (Slides 6-10) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 2: ConfigMaps Deep Dive (Slides 11-15) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 3: Hands-on Scenarios (Slides 16-24) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 MID QUIZ (Slide 25) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 4: Persistent Storage (Slides 26-32) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 5: Production Patterns (Slides 33-37) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 POST QUIZ (Slide 38) │ -├─────────────────────────────────────────────────────────────┤ -│ FINAL: What's Next (Slide 39) │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 📍 Slide 4 – 🤔 The Big Question - -> 💬 *"Store config in the environment, not in the code."* -> — The Twelve-Factor App - -**Consider this:** - -* 🏭 You have the **same application** running in dev, staging, and production -* 🔧 Each environment needs **different database URLs** -* 📊 You want to change **log levels without redeploying** -* 💾 Your app needs to **persist user uploads** somewhere - -> 🤔 **Think:** How do you build ONE container image that works everywhere? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L12_PRE - ---- - -## 📍 Slide 6 – ⚠️ Section 1: The Configuration Problem - -**The Anti-Pattern: Hardcoded Configuration** - -```mermaid -flowchart TD - subgraph 😰 Hardcoded - A[app-dev.jar] --> D1[Dev DB] - B[app-staging.jar] --> D2[Staging DB] - C[app-prod.jar] --> D3[Prod DB] - end - - subgraph 🚀 Externalized - E[app.jar] --> F{Config} - F --> D1 - F --> D2 - F --> D3 - end -``` - -* 😰 **Hardcoded:** Different artifact per environment -* 🚀 **Externalized:** One artifact, configuration injected at runtime - ---- - -## 📍 Slide 7 – 🔥 Pain Point 1: Environment-Specific Builds - -**The Problem:** - -```dockerfile -# ❌ Bad: Environment-specific Dockerfile -FROM python:3.12 -ENV DATABASE_URL=postgres://dev-server:5432/mydb # 😱 Hardcoded! -ENV LOG_LEVEL=DEBUG -COPY . /app -``` - -* 🔄 Need to **rebuild** for each environment -* 🐛 **"Works on my machine"** — config differs -* 🔍 Can't trace which **version** is where -* 💀 Accidentally deploying **dev config to production** - -> 😱 **Horror Story:** Company deployed with `DEBUG=true` to production, logging credit card numbers - ---- - -## 📍 Slide 8 – 🔥 Pain Point 2: Configuration Drift - -**What happens over time:** - -| 📅 Month | 🔧 Dev Config | 🎭 Staging Config | 🏭 Prod Config | -|----------|---------------|-------------------|----------------| -| January | `timeout=30` | `timeout=30` | `timeout=30` | -| March | `timeout=60` | `timeout=30` | `timeout=30` | -| June | `timeout=60` | `timeout=45` | `timeout=30` | -| Now | 😵 Nobody knows what's deployed where | - -* 🔄 **Manual changes** accumulate -* 📋 No **version control** for configuration -* 🐛 **Staging doesn't match production** — bugs slip through - ---- - -## 📍 Slide 9 – 🔥 Pain Point 3: Data Loss - -**Stateless containers + persistent data = 💥** - -```mermaid -flowchart LR - A[📦 Container v1] --> B[💾 /app/uploads] - B --> C[🔄 Deployment] - C --> D[📦 Container v2] - D --> E[💾 /app/uploads] - E --> F[😱 Empty!] -``` - -* 📦 Containers are **ephemeral** — data inside is lost on restart -* 💾 User uploads, databases, caches — all **gone** -* 🔄 Rolling updates = **data loss** without proper storage - -> 🤔 **Discussion:** Where should container applications store their data? - ---- - -## 📍 Slide 10 – 💰 The Cost of Poor Configuration - -| 🔥 Problem | 💥 Impact | 📊 Statistics | -|-----------|----------|---------------| -| Config drift | Inconsistent behavior | 62% of outages involve config changes | -| Hardcoded secrets | Security breaches | Covered in Lecture 11! | -| Data loss | Customer impact | Average $150K per incident | -| Manual config | Human error | 70% of failures are human error | - -**Root causes of production incidents (2024 survey):** -* 🔧 Configuration changes: **41%** -* 📦 Code deployments: **31%** -* 🔌 Infrastructure failures: **28%** - ---- - -## 📍 Slide 11 – ✅ Section 2: ConfigMaps to the Rescue - -**What is a ConfigMap?** - -* 📋 Kubernetes object that stores **non-confidential** configuration data -* 🔑 Key-value pairs or **entire files** -* 🔄 Decouples configuration from container images -* ⚡ Can be updated **without rebuilding** the application - -```mermaid -flowchart LR - A[📋 ConfigMap] --> B[📦 Pod] - A --> C[📦 Pod] - A --> D[📦 Pod] - - E[🔐 Secret] --> B - E --> C - E --> D -``` - -> 💡 **Key Insight:** ConfigMaps for config, Secrets for sensitive data - ---- - -## 📍 Slide 12 – 🚫 ConfigMaps: What They're NOT - -| 🚫 Myth | ✅ Reality | -|---------|----------| -| ConfigMaps are secure | ❌ Stored in plain text in etcd | -| ConfigMaps replace Secrets | ❌ Use Secrets for sensitive data | -| ConfigMaps auto-reload apps | ❌ Apps must implement hot-reload | -| ConfigMaps have no size limit | ❌ Limited to 1MB per ConfigMap | - -> ⚠️ **Warning:** Never store passwords, tokens, or keys in ConfigMaps! - -**When to use which:** - -| 📋 ConfigMap | 🔐 Secret | -|-------------|----------| -| Database URLs (without password) | Database passwords | -| Feature flags | API keys | -| Log levels | TLS certificates | -| Application settings | OAuth tokens | - ---- - -## 📍 Slide 13 – 🛠️ Creating ConfigMaps - -**Method 1: From literal values** -```bash -kubectl create configmap app-config \ - --from-literal=LOG_LEVEL=INFO \ - --from-literal=CACHE_TTL=3600 -``` - -**Method 2: From a file** -```bash -kubectl create configmap nginx-config \ - --from-file=nginx.conf -``` - -**Method 3: From YAML manifest** -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: app-config -data: - LOG_LEVEL: "INFO" - DATABASE_HOST: "postgres.default.svc" - config.yaml: | - server: - port: 8080 - timeout: 30s -``` - ---- - -## 📍 Slide 14 – 🔌 Consuming ConfigMaps - -**Option 1: Environment Variables** -```yaml -# ✅ Individual keys -env: - - name: LOG_LEVEL - valueFrom: - configMapKeyRef: - name: app-config - key: LOG_LEVEL - -# ✅ All keys at once -envFrom: - - configMapRef: - name: app-config -``` - -**Option 2: Volume Mounts (for files)** -```yaml -volumes: - - name: config-volume - configMap: - name: nginx-config -volumeMounts: - - name: config-volume - mountPath: /etc/nginx/nginx.conf - subPath: nginx.conf -``` - ---- - -## 📍 Slide 15 – 📊 Before vs After: Configuration - -| 📋 Aspect | 😰 Before (Hardcoded) | 🚀 After (ConfigMaps) | -|----------|----------------------|----------------------| -| Build per environment | Yes, multiple images | No, one image | -| Change config | Rebuild & redeploy | Update ConfigMap | -| Version control | In code (scattered) | Centralized, declarative | -| Environment parity | Difficult | Easy | -| Rollback | Redeploy old image | Apply old ConfigMap | -| Audit trail | Git history (code) | K8s + Git history | - -> 🤔 **Think:** What configuration in your applications could be externalized? - ---- - -## 📍 Slide 16 – 🎮 Section 3: Let's Simulate! - -**Scenario:** You're a DevOps engineer at **CloudMart** 🛒 - -Your application: -* 🐍 Python/Go web service -* 📊 Needs different configs per environment -* 💾 Stores user uploads -* 🔧 Frequently changes feature flags - -**What could go wrong?** Everything! Let's fix it. - ---- - -## 📍 Slide 17 – 💥 Scenario 1: Wrong Environment Config - -**Situation:** Developer accidentally deploys with staging database URL to production - -```mermaid -flowchart LR - A[👨‍💻 Dev pushes] --> B[🔄 CI/CD] - B --> C[📦 Deploy to Prod] - C --> D[🔗 Connects to Staging DB] - D --> E[😱 Production reads staging data!] -``` - -* 😱 **Impact:** Customers see test data -* ⏱️ **Detection time:** 2 hours -* 💰 **Cost:** Lost sales, reputation damage - -> 🤔 **Question:** How do we prevent this? - ---- - -## 📍 Slide 18 – ✅ Solution 1: Environment-Specific ConfigMaps - -**Fix:** Namespace-isolated ConfigMaps - -```yaml -# configmap-prod.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: app-config - namespace: production # 🔑 Namespace isolation -data: - DATABASE_HOST: "prod-db.internal" - ENVIRONMENT: "production" ---- -# configmap-staging.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: app-config - namespace: staging -data: - DATABASE_HOST: "staging-db.internal" - ENVIRONMENT: "staging" -``` - -* ✅ **Same ConfigMap name**, different namespaces -* ✅ **Impossible** to mix environments -* ✅ **GitOps friendly** — config in version control - ---- - -## 📍 Slide 19 – 💥 Scenario 2: Config Change Causes Outage - -**Situation:** Changed `CACHE_TTL` from 3600 to 36 (typo!) — cache expires every 36 seconds - -```mermaid -flowchart TD - A[⌨️ Typo: 3600 → 36] --> B[📋 ConfigMap Updated] - B --> C[📦 Pods reload config] - C --> D[🔥 Cache thrashing] - D --> E[💀 Database overloaded] - E --> F[😱 Site down!] -``` - -* 😱 **Impact:** 30-minute outage -* 🔍 **Root cause:** No validation, no review - ---- - -## 📍 Slide 20 – ✅ Solution 2: Immutable ConfigMaps + Versioning - -**Fix:** Treat ConfigMaps as immutable, version them - -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: app-config-v3 # 🔑 Versioned name - labels: - version: "3" -immutable: true # 🔒 Cannot be modified -data: - CACHE_TTL: "3600" -``` - -**Deployment references specific version:** -```yaml -envFrom: - - configMapRef: - name: app-config-v3 # 🔑 Explicit version -``` - -* ✅ **Rollback** = change reference to previous version -* ✅ **Audit trail** — which version when -* ✅ **Validation** in CI/CD before applying - ---- - -## 📍 Slide 21 – 💥 Scenario 3: User Uploads Disappear - -**Situation:** Deployment rolls out new pods, user uploads are gone - -```mermaid -flowchart TD - A[📦 Pod v1] --> B[💾 /app/uploads] - B --> C[📸 User uploads photo] - C --> D[🔄 Rolling Update] - D --> E[📦 Pod v2] - E --> F[💾 /app/uploads - Empty!] - F --> G[😱 User: Where's my photo?!] -``` - -* 💾 Container filesystem is **ephemeral** -* 🔄 New container = **fresh filesystem** -* 😱 All data is **lost** - ---- - -## 📍 Slide 22 – ✅ Solution 3: Persistent Volumes - -**Fix:** External storage that survives pod restarts - -```yaml -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: uploads-pvc -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Gi ---- -# In Deployment -volumes: - - name: uploads - persistentVolumeClaim: - claimName: uploads-pvc -volumeMounts: - - name: uploads - mountPath: /app/uploads -``` - -```mermaid -flowchart LR - A[📦 Pod v1] --> B[💾 PVC] - C[📦 Pod v2] --> B - D[📦 Pod v3] --> B - B --> E[🗄️ Persistent Storage] -``` - ---- - -## 📍 Slide 23 – 💥 Scenario 4: ConfigMap Update Not Applied - -**Situation:** Updated ConfigMap, but app still uses old values - -```mermaid -flowchart LR - A[📋 ConfigMap Updated] --> B[📦 Pod still running] - B --> C[⚠️ Using old config!] - C --> D[🤔 Why isn't it working?] -``` - -* 🔄 ConfigMap updates **don't automatically restart** pods -* 📦 Pod keeps the config from when it started - ---- - -## 📍 Slide 24 – ✅ Solution 4: Config Reload Strategies - -**Strategy 1: Restart Deployment** -```bash -kubectl rollout restart deployment/myapp -``` - -**Strategy 2: Use a hash annotation (GitOps-friendly)** -```yaml -metadata: - annotations: - checksum/config: {{ sha256sum .Values.config | quote }} -``` - -**Strategy 3: App-level hot reload** -* 📂 Mount ConfigMap as volume -* 👀 Watch for file changes -* 🔄 Reload configuration in-memory - -**Strategy 4: Reloader controller** -* 🤖 Automatically restarts pods when ConfigMap changes -* 📦 `stakater/reloader` — popular open source solution - ---- - -## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L12_MID - ---- - -## 📍 Slide 26 – 💾 Section 4: Persistent Storage Deep Dive - -**The Storage Stack in Kubernetes:** - -```mermaid -flowchart TD - A[📦 Pod] --> B[📁 Volume Mount] - B --> C[💾 PersistentVolumeClaim] - C --> D[🗄️ PersistentVolume] - D --> E[☁️ Storage Backend] - - F[📋 StorageClass] -.-> C - F -.-> D -``` - -* 📦 **Pod:** Uses the storage via mount -* 💾 **PVC:** Request for storage ("I need 10GB") -* 🗄️ **PV:** Actual storage resource -* ☁️ **Backend:** AWS EBS, GCE PD, NFS, local disk -* 📋 **StorageClass:** Template for dynamic provisioning - ---- - -## 📍 Slide 27 – 📋 Storage Concepts Breakdown - -| 🔧 Concept | 📝 Description | 🎯 Analogy | -|-----------|---------------|-----------| -| **PersistentVolume (PV)** | A piece of storage in the cluster | A physical hard drive | -| **PersistentVolumeClaim (PVC)** | A request for storage | "I need a 100GB drive" | -| **StorageClass** | Template for provisioning | "Give me SSD storage" | -| **Access Modes** | How pods can access | ReadWriteOnce, ReadWriteMany | -| **Reclaim Policy** | What happens when PVC deleted | Retain, Delete, Recycle | - -**Access Modes:** -* 🔒 **ReadWriteOnce (RWO):** One node can mount read-write -* 📖 **ReadOnlyMany (ROX):** Many nodes can mount read-only -* 📝 **ReadWriteMany (RWX):** Many nodes can mount read-write - ---- - -## 📍 Slide 28 – 🔄 Dynamic Provisioning - -**Without Dynamic Provisioning (Manual):** -```mermaid -flowchart LR - A[👨‍💻 Admin creates PV] --> B[📋 PV available] - B --> C[👨‍💻 Dev creates PVC] - C --> D[🔗 PVC binds to PV] -``` - -**With Dynamic Provisioning (Automatic):** -```mermaid -flowchart LR - A[👨‍💻 Dev creates PVC] --> B[📋 StorageClass] - B --> C[🤖 Auto-create PV] - C --> D[🔗 PVC binds to PV] -``` - -```yaml -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: fast-ssd -provisioner: kubernetes.io/gce-pd # Cloud-specific -parameters: - type: pd-ssd -reclaimPolicy: Delete -volumeBindingMode: WaitForFirstConsumer -``` - ---- - -## 📍 Slide 29 – ⚠️ Storage Pitfalls - -| ⚠️ Pitfall | 💥 Impact | ✅ Solution | -|-----------|----------|------------| -| Wrong access mode | Pod scheduling fails | Match mode to use case | -| No storage class | PVC pending forever | Set default StorageClass | -| Reclaim = Delete | Data lost on PVC delete | Use Retain for important data | -| Zone mismatch | Pod can't mount volume | Use topology-aware provisioning | -| Insufficient capacity | PVC pending | Monitor storage usage | - -**Common error:** -``` -Warning FailedScheduling pod has unbound immediate PersistentVolumeClaims -``` - -> 🔍 **Debug:** `kubectl describe pvc ` — check events - ---- - -## 📍 Slide 30 – 📊 Volume Types Comparison - -| 📦 Volume Type | 🎯 Use Case | ⚡ Performance | 💰 Cost | -|---------------|------------|---------------|--------| -| **emptyDir** | Temp data, cache | Fast (node storage) | Free | -| **hostPath** | Node-specific data | Fast | Free | -| **NFS** | Shared storage | Medium | Varies | -| **Cloud (EBS, PD)** | Production workloads | Configurable | $$$ | -| **Local PV** | Databases, high IOPS | Very fast | Node-dependent | - -**Decision tree:** -```mermaid -flowchart TD - A[Need persistent storage?] --> |No| B[emptyDir] - A --> |Yes| C[Shared across pods?] - C --> |No| D[Cloud Block Storage] - C --> |Yes| E[NFS or Cloud File Storage] -``` - ---- - -## 📍 Slide 31 – 🔧 Practical PVC Example - -**Complete example for a web application:** - -```yaml -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: app-uploads -spec: - accessModes: - - ReadWriteOnce - storageClassName: standard - resources: - requests: - storage: 5Gi ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: webapp -spec: - template: - spec: - containers: - - name: app - volumeMounts: - - name: uploads - mountPath: /app/uploads - volumes: - - name: uploads - persistentVolumeClaim: - claimName: app-uploads -``` - ---- - -## 📍 Slide 32 – 📈 Storage Lifecycle - -```mermaid -stateDiagram-v2 - [*] --> Pending: PVC Created - Pending --> Bound: PV Available - Bound --> Released: PVC Deleted - Released --> Available: Reclaim - Released --> [*]: Delete Policy - - note right of Pending: Waiting for PV - note right of Bound: In use - note right of Released: Data still exists -``` - -**Key states:** -* ⏳ **Pending:** Waiting for matching PV -* ✅ **Bound:** PVC matched to PV -* 🔓 **Released:** PVC deleted, PV still has data -* ❌ **Failed:** Error in provisioning - ---- - -## 📍 Slide 33 – 🏭 Section 5: Production Patterns - -**Pattern 1: GitOps Configuration Management** - -```mermaid -flowchart LR - A[📝 Git Repo] --> B[🔄 ArgoCD] - B --> C[📋 ConfigMaps] - B --> D[🔐 Secrets] - B --> E[📦 Deployments] - - C --> F[🎯 Cluster] - D --> F - E --> F -``` - -* 📋 **All configuration in Git** — single source of truth -* 🔄 **ArgoCD syncs** to cluster -* 🔍 **Audit trail** — who changed what, when -* ↩️ **Rollback** — `git revert` - ---- - -## 📍 Slide 34 – 🔧 Pattern 2: Environment Hierarchy - -**Kustomize for environment-specific configs:** - -``` -base/ - ├── deployment.yaml - ├── service.yaml - └── configmap.yaml -overlays/ - ├── dev/ - │ └── kustomization.yaml - ├── staging/ - │ └── kustomization.yaml - └── prod/ - └── kustomization.yaml -``` - -```yaml -# overlays/prod/kustomization.yaml -resources: - - ../../base -configMapGenerator: - - name: app-config - literals: - - LOG_LEVEL=WARN - - REPLICAS=5 -``` - -* ✅ **DRY** — Don't Repeat Yourself -* ✅ **Environment-specific** overrides -* ✅ **Consistent** base configuration - ---- - -## 📍 Slide 35 – 🔐 Pattern 3: Secrets + ConfigMaps Together - -**Combining Secrets and ConfigMaps:** - -```yaml -spec: - containers: - - name: app - env: - # 📋 From ConfigMap (non-sensitive) - - name: DATABASE_HOST - valueFrom: - configMapKeyRef: - name: app-config - key: DATABASE_HOST - # 🔐 From Secret (sensitive) - - name: DATABASE_PASSWORD - valueFrom: - secretKeyRef: - name: app-secrets - key: db-password -``` - -**Best Practice:** -* 📋 **ConfigMap:** URLs, ports, feature flags -* 🔐 **Secret:** Passwords, tokens, certificates -* 🔒 **Never mix** sensitive and non-sensitive data - ---- - -## 📍 Slide 36 – 📊 Configuration Best Practices - -| 🔧 Practice | 📝 Description | -|------------|---------------| -| **Version ConfigMaps** | Include version in name (`app-config-v2`) | -| **Use namespaces** | Isolate environments (dev, staging, prod) | -| **Validate in CI** | Check config syntax before deploy | -| **Document defaults** | What happens if config missing? | -| **Monitor changes** | Alert on ConfigMap updates | -| **Limit size** | Keep ConfigMaps under 1MB | -| **Use labels** | Tag configs with app, version, environment | - ---- - -## 📍 Slide 37 – 🎯 Key Takeaways - -1. 📋 **ConfigMaps** separate configuration from code — one image, any environment -2. 🔐 **ConfigMaps ≠ Secrets** — never store sensitive data in ConfigMaps -3. 💾 **PVCs** provide persistent storage that survives pod restarts -4. 🔄 **Dynamic provisioning** automates storage management -5. 📁 **Version your configs** — treat them like code -6. 🏭 **GitOps** — configuration in Git is the source of truth - -> 💬 *"Configuration belongs in the environment, not the artifact."* -> — 12-Factor App - ---- - -## 📍 Slide 38 – 📝 QUIZ — DEVOPS_L12_POST - ---- - -## 📍 Slide 39 – 🚀 What's Next? - -**Coming up: Lecture 13 — GitOps with ArgoCD** - -```mermaid -flowchart LR - A[📝 Git] --> B[🔄 ArgoCD] - B --> C[☸️ Kubernetes] - C --> D[🎯 Desired State] -``` - -* 🔄 **Continuous Deployment** automated -* 📝 **Git as single source of truth** -* 🔍 **Drift detection** and auto-sync -* ↩️ **Easy rollbacks** with git revert - -> 🎯 **Lab 12:** Apply these concepts — create ConfigMaps, use PVCs, externalize your app configuration! - ---- - -## 📚 Resources - -**Documentation:** -* 📖 [Kubernetes ConfigMaps](https://kubernetes.io/docs/concepts/configuration/configmap/) -* 📖 [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) -* 📖 [12-Factor App — Config](https://12factor.net/config) - -**Tools:** -* 🔧 [Kustomize](https://kustomize.io/) -* 🔧 [Reloader](https://github.com/stakater/Reloader) - -**Books:** -* 📕 *Kubernetes Patterns* by Bilgin Ibryam & Roland Huß -* 📕 *Cloud Native DevOps with Kubernetes* by John Arundel & Justin Domingus diff --git a/lectures/lec13.md b/lectures/lec13.md deleted file mode 100644 index 370ae7821a..0000000000 --- a/lectures/lec13.md +++ /dev/null @@ -1,830 +0,0 @@ -# 📌 Lecture 13 — GitOps with ArgoCD: Git as the Source of Truth - -> 🎯 **From manual deployments to automated, auditable, self-healing infrastructure** - ---- - -## 📍 Slide 1 – 🚀 Welcome to GitOps - -We've learned to store configuration in **ConfigMaps** and **Secrets**. But who deploys them? - -* 👨‍💻 **Manual kubectl?** — "Who ran that command?" -* 🔄 **CI/CD pipeline?** — Push-based, fragile -* 🤔 **What about drift?** — Reality vs desired state - -```mermaid -flowchart LR - A[😰 Manual Deploys] --> B[🔄 CI/CD Push] - B --> C[🚀 GitOps Pull] - C --> D[💎 Self-healing Infrastructure] -``` - -> 🎯 **Goal:** Git becomes the single source of truth for your entire infrastructure - ---- - -## 📍 Slide 2 – 📚 Learning Outcomes - -By the end of this lecture, you will: - -| # | 🎯 Outcome | -|---|-----------| -| 1 | ✅ Understand **GitOps principles** and benefits | -| 2 | ✅ Differentiate **push vs pull** deployment models | -| 3 | ✅ Deploy applications using **ArgoCD** | -| 4 | ✅ Configure **sync policies** and **auto-healing** | -| 5 | ✅ Handle **secrets** in GitOps workflows | -| 6 | ✅ Implement **multi-environment** deployments | - ---- - -## 📍 Slide 3 – 🗺️ Lecture Overview - -``` -┌─────────────────────────────────────────────────────────────┐ -│ SECTION 0: Introduction (Slides 1-4) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 PRE QUIZ (Slide 5) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 1: The Deployment Problem (Slides 6-10) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 2: GitOps Principles (Slides 11-15) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 3: ArgoCD in Action (Slides 16-24) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 MID QUIZ (Slide 25) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 4: Advanced Patterns (Slides 26-32) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 5: Production GitOps (Slides 33-37) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 POST QUIZ (Slide 38) │ -├─────────────────────────────────────────────────────────────┤ -│ FINAL: What's Next (Slide 39) │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 📍 Slide 4 – 🤔 The Big Question - -> 💬 *"If it's not in Git, it doesn't exist."* -> — GitOps Mantra - -**Consider this scenario:** - -* 🌙 **3 AM alert:** Production is down -* 🔍 **Investigation:** Someone changed a deployment -* ❓ **Questions:** Who? When? What changed? How to rollback? -* 😱 **Answer:** Nobody knows... - -> 🤔 **Think:** How do we ensure every change is tracked, auditable, and reversible? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L13_PRE - ---- - -## 📍 Slide 6 – ⚠️ Section 1: The Deployment Problem - -**Traditional Deployment Models:** - -```mermaid -flowchart TD - subgraph 😰 Manual - A[👨‍💻 Developer] --> B[⌨️ kubectl apply] - B --> C[☸️ Cluster] - end - - subgraph 🔄 CI/CD Push - D[📝 Git Push] --> E[🔧 CI Pipeline] - E --> F[⌨️ kubectl apply] - F --> G[☸️ Cluster] - end -``` - -* 😰 **Manual:** No audit trail, human error, inconsistent -* 🔄 **CI/CD Push:** Better, but credentials in pipeline, cluster access - ---- - -## 📍 Slide 7 – 🔥 Pain Point 1: The "It Works on My Machine" Problem - -**Symptoms:** - -* 👨‍💻 **Dev:** "I deployed it, it's working!" -* 🏭 **Prod:** "It's completely broken!" -* 🔍 **Investigation:** Configs don't match - -```mermaid -flowchart LR - A[👨‍💻 Local kubectl] --> B[🎭 Staging] - C[👨‍💻 Different kubectl] --> D[🏭 Production] - B --> E[😵 Different States] - D --> E -``` - -* 🔧 **No single source of truth** -* 📋 **Manual processes** lead to drift -* 😱 **"Emergency fixes"** bypass procedures - ---- - -## 📍 Slide 8 – 🔥 Pain Point 2: Configuration Drift - -**Drift:** When actual state ≠ desired state - -| 📅 Time | 📝 Git (Desired) | ☸️ Cluster (Actual) | 😱 Drift | -|---------|------------------|---------------------|----------| -| Day 1 | replicas: 3 | replicas: 3 | ✅ None | -| Day 5 | replicas: 3 | replicas: 5 (scaled manually) | ⚠️ Drift! | -| Day 10 | replicas: 3 | replicas: 5, extra env var | 🔥 More drift! | -| Day 30 | 🤷 Unknown | 🤷 Unknown | 💀 Chaos | - -**Real impact:** -* 🔄 **Deployments fail** because actual state differs -* 📋 **Documentation lies** — cluster is reality -* 🔍 **Debugging nightmare** — which version is deployed? - ---- - -## 📍 Slide 9 – 🔥 Pain Point 3: Credential Sprawl - -**Push-based CI/CD security concerns:** - -```mermaid -flowchart TD - A[🔐 Cluster Credentials] --> B[📦 CI Server] - A --> C[💻 Dev Machines] - A --> D[🔧 Scripts] - A --> E[📋 Pipeline Configs] - - B --> F[😱 Breach Vector] - C --> F - D --> F - E --> F -``` - -* 🔐 **Credentials everywhere** — CI servers, dev machines -* 🎯 **Attack surface** expands with each tool -* 🔑 **Shared secrets** — who has access? - ---- - -## 📍 Slide 10 – 💰 The Cost of Manual Deployments - -| 🔥 Problem | 💥 Impact | 📊 Data | -|-----------|----------|---------| -| No audit trail | Compliance failures | 73% fail audits without GitOps | -| Manual errors | Outages | 70% of outages are human error | -| Credential sprawl | Security breaches | Average breach cost: $4.45M | -| Slow recovery | Downtime | MTTR 4x longer without GitOps | - -> 💬 *"The cost of a breach is not the breach itself, but the inability to respond quickly."* - ---- - -## 📍 Slide 11 – ✅ Section 2: GitOps Principles - -**What is GitOps?** - -* 📝 **Git as single source of truth** — declarative desired state -* 🔄 **Continuous reconciliation** — actual → desired -* 🔀 **Pull-based deployment** — agent pulls from Git -* 🔒 **Immutable, auditable** — every change tracked - -```mermaid -flowchart LR - A[📝 Git Repo] --> |Pull| B[🤖 ArgoCD Agent] - B --> |Reconcile| C[☸️ Cluster] - C --> |Report Status| B -``` - -> 💡 **Key Insight:** The cluster pulls changes, no credentials leave the cluster! - ---- - -## 📍 Slide 12 – 🚫 GitOps: What It's NOT - -| 🚫 Myth | ✅ Reality | -|---------|----------| -| Just using Git for YAML files | A complete operational model with reconciliation | -| Another CI/CD tool | Continuous deployment, not continuous integration | -| Only for Kubernetes | Works for any declarative infrastructure | -| Complicated to adopt | Can start simple, grow incrementally | - -> 🔥 **Hot take:** "Putting YAML in Git is not GitOps. GitOps is about the reconciliation loop." - -**The Four Principles (from OpenGitOps):** -1. 📝 **Declarative** — Desired state expressed declaratively -2. 🔄 **Versioned and Immutable** — Stored in Git -3. 🤖 **Pulled Automatically** — Agents pull desired state -4. ♾️ **Continuously Reconciled** — Agents ensure actual = desired - ---- - -## 📍 Slide 13 – 🔄 Push vs Pull Deployment - -```mermaid -flowchart TD - subgraph 🔄 Push Model - A[📝 Git] --> B[🔧 CI/CD] - B --> |Push credentials needed| C[☸️ Cluster] - end - - subgraph 🚀 Pull Model - GitOps - D[📝 Git] --> |Pull| E[🤖 Agent in Cluster] - E --> |Apply| F[☸️ Same Cluster] - end -``` - -| 📋 Aspect | 🔄 Push | 🚀 Pull (GitOps) | -|----------|--------|------------------| -| Credentials | CI needs cluster creds | Agent has local access | -| Drift detection | None | Continuous | -| Audit trail | CI logs (external) | Git history | -| Recovery | Re-run pipeline | Automatic reconciliation | - ---- - -## 📍 Slide 14 – 🛠️ GitOps Tools Landscape - -| 🛠️ Tool | 📝 Description | ⭐ Best For | -|---------|---------------|------------| -| **ArgoCD** | Declarative GitOps for K8s | Most Kubernetes use cases | -| **Flux** | Toolkit approach, CNCF project | Composable, extensible setups | -| **Jenkins X** | CI/CD + GitOps combined | Jenkins-heavy organizations | -| **Rancher Fleet** | Multi-cluster GitOps | Managing many clusters | - -**Why ArgoCD?** -* 🎯 Most adopted (70%+ of GitOps users) -* 🖥️ Excellent UI for visualization -* 🔧 Rich feature set out of the box -* 📚 Large community, good documentation - ---- - -## 📍 Slide 15 – 📊 Before vs After: Deployment - -| 📋 Aspect | 😰 Before (Manual/Push) | 🚀 After (GitOps) | -|----------|-------------------------|-------------------| -| Change process | kubectl, scripts, pipelines | Git PR → merge → auto-sync | -| Audit trail | Scattered logs | Complete Git history | -| Rollback | "Which version was before?" | `git revert` | -| Drift | Undetected until failure | Detected immediately | -| Credentials | Spread across tools | Stay in cluster | -| Recovery | Manual intervention | Self-healing | - -> 🤔 **Think:** How would GitOps have helped in your last deployment issue? - ---- - -## 📍 Slide 16 – 🎮 Section 3: ArgoCD in Action - -**ArgoCD Architecture:** - -```mermaid -flowchart TD - A[📝 Git Repository] --> B[🤖 ArgoCD Server] - B --> C[🔄 Application Controller] - C --> D[☸️ Kubernetes API] - E[👨‍💻 User] --> F[🖥️ ArgoCD UI / CLI] - F --> B - D --> C -``` - -**Components:** -* 🖥️ **API Server:** UI, CLI, webhook endpoints -* 🔄 **Application Controller:** Reconciliation engine -* 📦 **Repository Server:** Caches Git repos, renders manifests -* 🔗 **Dex:** SSO authentication (optional) - ---- - -## 📍 Slide 17 – 💥 Scenario 1: First ArgoCD Deployment - -**Situation:** Deploy your first application with ArgoCD - -```yaml -# Application manifest -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: my-app - namespace: argocd -spec: - project: default - source: - repoURL: https://github.com/org/app-manifests - path: environments/dev - targetRevision: main - destination: - server: https://kubernetes.default.svc - namespace: dev -``` - -```mermaid -flowchart LR - A[📝 Create App] --> B[🔄 ArgoCD Syncs] - B --> C[📦 Resources Created] - C --> D[✅ App Running] -``` - ---- - -## 📍 Slide 18 – ✅ Solution 1: Understanding Sync - -**Sync States:** - -| 🔄 State | 📝 Meaning | 🎯 Action | -|----------|-----------|----------| -| **Synced** | Cluster matches Git | ✅ Good! | -| **OutOfSync** | Cluster differs from Git | 🔄 Sync needed | -| **Unknown** | Can't determine state | 🔍 Check connection | -| **Missing** | Resources don't exist yet | 🔄 Initial sync | - -**Health States:** - -| 💚 Health | 📝 Meaning | -|----------|-----------| -| **Healthy** | All resources running correctly | -| **Progressing** | Resources being updated | -| **Degraded** | Some resources have issues | -| **Suspended** | Manually paused | - ---- - -## 📍 Slide 19 – 💥 Scenario 2: Handling Drift - -**Situation:** Someone manually changed replicas in the cluster - -```mermaid -flowchart TD - A[📝 Git: replicas=3] --> B[🤖 ArgoCD] - C[👨‍💻 kubectl scale replicas=5] --> D[☸️ Cluster] - B --> |Detects| E[⚠️ OutOfSync] - E --> |Auto-sync enabled| F[🔄 Restore to 3] -``` - -**ArgoCD detects drift immediately!** -* 🔍 **Visibility:** Shows exactly what differs -* 🔄 **Options:** Manual sync or auto-sync -* 📋 **Audit:** Who changed Git matters, not who ran kubectl - ---- - -## 📍 Slide 20 – ✅ Solution 2: Sync Policies - -**Configure automatic reconciliation:** - -```yaml -spec: - syncPolicy: - automated: - prune: true # Delete resources not in Git - selfHeal: true # Revert manual changes - syncOptions: - - CreateNamespace=true - - PruneLast=true -``` - -**Options explained:** -* 🔄 **automated:** Enable auto-sync on Git changes -* 🗑️ **prune:** Delete resources removed from Git -* 💚 **selfHeal:** Revert manual cluster changes -* 📦 **CreateNamespace:** Create namespace if missing - ---- - -## 📍 Slide 21 – 💥 Scenario 3: Multi-Environment Deployment - -**Situation:** Same app, different configs for dev/staging/prod - -``` -repo/ -├── base/ -│ ├── deployment.yaml -│ └── service.yaml -└── overlays/ - ├── dev/ - │ └── kustomization.yaml - ├── staging/ - │ └── kustomization.yaml - └── prod/ - └── kustomization.yaml -``` - -```mermaid -flowchart TD - A[📝 Git Repo] --> B[🤖 ArgoCD] - B --> C[📦 App-Dev] - B --> D[📦 App-Staging] - B --> E[📦 App-Prod] - C --> F[☸️ Dev Cluster] - D --> G[☸️ Staging Cluster] - E --> H[☸️ Prod Cluster] -``` - ---- - -## 📍 Slide 22 – ✅ Solution 3: ApplicationSet - -**Deploy to multiple environments with one definition:** - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: ApplicationSet -metadata: - name: my-app -spec: - generators: - - list: - elements: - - env: dev - namespace: dev - - env: staging - namespace: staging - - env: prod - namespace: prod - template: - metadata: - name: 'my-app-{{env}}' - spec: - source: - repoURL: https://github.com/org/manifests - path: 'overlays/{{env}}' - destination: - namespace: '{{namespace}}' -``` - ---- - -## 📍 Slide 23 – 💥 Scenario 4: Secrets in GitOps - -**Problem:** Secrets shouldn't be in Git... but GitOps needs everything in Git! - -```mermaid -flowchart TD - A[🔐 Secret] --> B{Where to store?} - B --> |❌ Plain Git| C[😱 Security breach] - B --> |✅ Encrypted| D[🔒 Sealed Secrets] - B --> |✅ External| E[🔐 Vault + ESO] -``` - -**The dilemma:** -* 📝 GitOps: Everything in Git -* 🔐 Security: Secrets NOT in Git -* 🤔 How to reconcile? - ---- - -## 📍 Slide 24 – ✅ Solution 4: Secrets Management Patterns - -**Option 1: Sealed Secrets** -```yaml -apiVersion: bitnami.com/v1alpha1 -kind: SealedSecret -metadata: - name: my-secret -spec: - encryptedData: - password: AgBghY8... # Encrypted, safe to commit! -``` - -**Option 2: External Secrets Operator + Vault** -```yaml -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: my-secret -spec: - secretStoreRef: - name: vault-backend - target: - name: my-secret - data: - - secretKey: password - remoteRef: - key: app/database - property: password -``` - -* ✅ **Encrypted in Git** (Sealed Secrets) -* ✅ **Reference only in Git** (External Secrets) - ---- - -## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L13_MID - ---- - -## 📍 Slide 26 – 🔧 Section 4: Advanced ArgoCD Patterns - -**Sync Waves & Hooks:** - -```yaml -metadata: - annotations: - argocd.argoproj.io/sync-wave: "1" # Order of deployment - argocd.argoproj.io/hook: PreSync # Run before main sync -``` - -```mermaid -flowchart LR - A[🔄 PreSync Hooks] --> B[📦 Wave 0] - B --> C[📦 Wave 1] - C --> D[📦 Wave 2] - D --> E[✅ PostSync Hooks] -``` - -**Use cases:** -* 📊 **Database migrations** before app deploy -* 🧹 **Cleanup jobs** after deployment -* 🔍 **Health checks** between phases - ---- - -## 📍 Slide 27 – 🔄 Sync Options Deep Dive - -| 🔧 Option | 📝 Purpose | -|----------|-----------| -| `Replace` | Replace instead of apply (for immutable fields) | -| `PruneLast` | Delete resources after all others sync | -| `ApplyOutOfSyncOnly` | Only apply changed resources | -| `ServerSideApply` | Use server-side apply (K8s 1.22+) | -| `FailOnSharedResource` | Fail if resource owned by another app | - -```yaml -syncPolicy: - syncOptions: - - CreateNamespace=true - - PrunePropagationPolicy=foreground - - PruneLast=true -``` - ---- - -## 📍 Slide 28 – 🏗️ Repository Structure Patterns - -**Pattern 1: Monorepo** -``` -repo/ -├── apps/ -│ ├── app1/ -│ └── app2/ -└── infrastructure/ - ├── prometheus/ - └── argocd/ -``` - -**Pattern 2: Repo per App** -``` -app1-config/ # App 1 manifests -app2-config/ # App 2 manifests -infrastructure/ # Shared infra -``` - -**Pattern 3: Environment Repos** -``` -dev-cluster/ # All dev apps -prod-cluster/ # All prod apps -``` - -> 💡 **Recommendation:** Start with monorepo, split when it gets complex - ---- - -## 📍 Slide 29 – 📊 ArgoCD Metrics & Monitoring - -**Key metrics to watch:** - -| 📊 Metric | 📝 Meaning | ⚠️ Alert When | -|----------|-----------|--------------| -| `argocd_app_sync_total` | Total syncs | Unusually high | -| `argocd_app_health_status` | App health | Not healthy | -| `argocd_app_reconcile_duration` | Sync time | > 5 minutes | -| `argocd_cluster_api_resource_objects` | Total objects | Growing unexpectedly | - -**Dashboard integration:** -* 📊 Grafana dashboards available -* 🔔 Alertmanager integration -* 📝 Slack/Teams notifications - ---- - -## 📍 Slide 30 – 🔐 RBAC & Multi-tenancy - -**ArgoCD RBAC:** - -```yaml -# argocd-rbac-cm ConfigMap -policy.csv: | - p, role:dev-team, applications, get, dev-project/*, allow - p, role:dev-team, applications, sync, dev-project/*, allow - p, role:ops-team, applications, *, */*, allow - - g, dev-group, role:dev-team - g, ops-group, role:ops-team -``` - -**Projects for isolation:** -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: AppProject -metadata: - name: dev-project -spec: - sourceRepos: - - 'https://github.com/org/dev-*' - destinations: - - namespace: 'dev-*' - server: https://kubernetes.default.svc -``` - ---- - -## 📍 Slide 31 – 🚨 Disaster Recovery - -**Git is your backup!** - -```mermaid -flowchart TD - A[💀 Cluster Gone] --> B[🆕 New Cluster] - B --> C[📦 Install ArgoCD] - C --> D[🔗 Connect to Git] - D --> E[🔄 Sync All Apps] - E --> F[✅ Fully Restored] -``` - -**Recovery steps:** -1. 🆕 Create new cluster -2. 📦 Install ArgoCD -3. 🔗 Point to Git repository -4. ☕ Wait for sync -5. ✅ Everything restored! - -> 💡 **Key insight:** If Git has everything, recovery is just a sync away - ---- - -## 📍 Slide 32 – 📋 GitOps Workflow Summary - -```mermaid -flowchart TD - A[👨‍💻 Developer] --> |PR| B[📝 Git Repo] - B --> |Review| C[✅ Merge] - C --> |Webhook| D[🤖 ArgoCD] - D --> |Sync| E[☸️ Cluster] - E --> |Status| D - D --> |Notify| F[💬 Slack] - - G[🔍 Drift Detection] --> D - D --> |Self-heal| E -``` - -**The complete loop:** -1. 📝 **Change:** Developer creates PR -2. 👀 **Review:** Team reviews and approves -3. 🔀 **Merge:** Changes merge to main -4. 🤖 **Detect:** ArgoCD detects new commit -5. 🔄 **Sync:** Resources deployed to cluster -6. 💚 **Verify:** Health checks pass -7. 📢 **Notify:** Team informed of deployment - ---- - -## 📍 Slide 33 – 🏭 Section 5: Production GitOps - -**Enterprise Patterns:** - -```mermaid -flowchart TD - subgraph Git - A[📝 Feature Branch] --> B[📝 Main Branch] - B --> C[📝 Release Branch] - end - - subgraph ArgoCD - D[🤖 Dev App] --> E[🤖 Staging App] - E --> F[🤖 Prod App] - end - - B --> D - C --> F -``` - -* 🔀 **Branch strategy:** Main for dev, release for prod -* 🎯 **Progressive delivery:** Dev → Staging → Prod -* ✅ **Promotion:** PR from main to release - ---- - -## 📍 Slide 34 – 🏢 Real-World GitOps: Intuit - -**Case Study: Intuit's GitOps Journey** - -* 📊 **Scale:** 2,000+ applications -* 🔄 **Deployments:** 500+ per day -* ⏱️ **MTTR:** Reduced by 80% - -**What they learned:** -* 📋 Start small, grow incrementally -* 🔧 Standardize templates early -* 👥 Train teams on Git workflows -* 📊 Monitor everything - -> 💬 *"GitOps turned our deployment from a ceremony into a non-event."* — Intuit Engineer - ---- - -## 📍 Slide 35 – 🔧 Migration Strategy - -**Adopting GitOps incrementally:** - -```mermaid -flowchart LR - A[1️⃣ Non-critical app] --> B[2️⃣ Dev environment] - B --> C[3️⃣ More apps] - C --> D[4️⃣ Staging] - D --> E[5️⃣ Production] -``` - -**Phases:** -1. 🧪 **Pilot:** One non-critical app in dev -2. 📚 **Learn:** Document patterns, train team -3. 📦 **Expand:** More apps, still dev -4. 🎭 **Staging:** Full staging environment -5. 🏭 **Production:** Controlled rollout - ---- - -## 📍 Slide 36 – 🎯 Key Takeaways - -1. 📝 **Git is the source of truth** — not the cluster, not CI/CD -2. 🔄 **Pull > Push** — credentials stay in cluster -3. 💚 **Self-healing** — drift is detected and corrected -4. 🔍 **Complete audit trail** — git log is your history -5. ↩️ **Easy rollback** — `git revert` reverts infrastructure -6. 🔐 **Secrets need special handling** — Sealed Secrets or External Secrets - -> 💬 *"Operations by Pull Request"* -> — Kelsey Hightower - ---- - -## 📍 Slide 37 – 🧠 Mindset Shift - -| 😰 Old Mindset | 🚀 New Mindset | -|---------------|----------------| -| "I'll just kubectl this" | "Let me create a PR" | -| "The cluster is truth" | "Git is truth" | -| "We need cluster access" | "We need Git access" | -| "Rollback is scary" | "Rollback is git revert" | -| "Who changed what?" | "Check git log" | -| "Emergency fix!" | "Emergency PR with fast review" | - -> 🤔 **Question:** Which mindset do you operate with today? - ---- - -## 📍 Slide 38 – 📝 QUIZ — DEVOPS_L13_POST - ---- - -## 📍 Slide 39 – 🚀 What's Next? - -**Coming up: Lecture 14 — Progressive Delivery with Argo Rollouts** - -```mermaid -flowchart LR - A[📦 v1] --> B[🚀 Canary 10%] - B --> C[🚀 Canary 50%] - C --> D[🚀 Full Rollout] -``` - -* 🐤 **Canary deployments** — test with small traffic -* 🔵 **Blue-green deployments** — instant switchover -* 📊 **Automated analysis** — metrics-driven promotion -* ↩️ **Automatic rollback** — on failure - -> 🎯 **Lab 13:** Set up ArgoCD and deploy your application using GitOps! - ---- - -## 📚 Resources - -**Documentation:** -* 📖 [ArgoCD Docs](https://argo-cd.readthedocs.io/) -* 📖 [OpenGitOps](https://opengitops.dev/) -* 📖 [Sealed Secrets](https://sealed-secrets.netlify.app/) -* 📖 [External Secrets Operator](https://external-secrets.io/) - -**Tools:** -* 🔧 [ArgoCD](https://argoproj.github.io/cd/) -* 🔧 [Flux](https://fluxcd.io/) -* 🔧 [Kustomize](https://kustomize.io/) - -**Books:** -* 📕 *GitOps and Kubernetes* by Billy Yuen, et al. -* 📕 *Continuous Delivery* by Jez Humble & David Farley diff --git a/lectures/lec14.md b/lectures/lec14.md deleted file mode 100644 index 9ca7297ccd..0000000000 --- a/lectures/lec14.md +++ /dev/null @@ -1,825 +0,0 @@ -# 📌 Lecture 14 — Progressive Delivery: Deploying with Confidence - -> 🎯 **From risky big-bang deployments to controlled, observable releases** - ---- - -## 📍 Slide 1 – 🚀 Welcome to Progressive Delivery - -GitOps solved **how** we deploy. But **when things go wrong**... - -* 💥 **Traditional deploy:** 100% traffic instantly → all users affected -* 🐤 **Canary:** 5% traffic first → catch issues early -* 🔵 **Blue-green:** Switch traffic instantly → easy rollback - -```mermaid -flowchart LR - A[😰 Big Bang] --> B[🎲 Hope it works] - C[🐤 Canary] --> D[📊 Observe] - D --> E[✅ Promote or ↩️ Rollback] -``` - -> 🎯 **Goal:** Deploy changes safely with automated analysis and rollback - ---- - -## 📍 Slide 2 – 📚 Learning Outcomes - -By the end of this lecture, you will: - -| # | 🎯 Outcome | -|---|-----------| -| 1 | ✅ Understand **progressive delivery** concepts and benefits | -| 2 | ✅ Implement **canary deployments** with Argo Rollouts | -| 3 | ✅ Configure **blue-green deployments** for instant rollback | -| 4 | ✅ Set up **automated analysis** with metrics | -| 5 | ✅ Design **traffic management** strategies | -| 6 | ✅ Handle **rollback scenarios** gracefully | - ---- - -## 📍 Slide 3 – 🗺️ Lecture Overview - -``` -┌─────────────────────────────────────────────────────────────┐ -│ SECTION 0: Introduction (Slides 1-4) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 PRE QUIZ (Slide 5) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 1: The Deployment Risk Problem (Slides 6-10) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 2: Progressive Delivery Concepts (Slides 11-15) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 3: Argo Rollouts in Action (Slides 16-24) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 MID QUIZ (Slide 25) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 4: Advanced Strategies (Slides 26-32) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 5: Production Patterns (Slides 33-37) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 POST QUIZ (Slide 38) │ -├─────────────────────────────────────────────────────────────┤ -│ FINAL: What's Next (Slide 39) │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 📍 Slide 4 – 🤔 The Big Question - -> 💬 *"We don't want to move fast and break things. We want to move fast and fix things."* -> — Facebook (ironically, after many outages) - -**Consider this:** - -* 🚀 You deploy a new feature at **5 PM Friday** -* 💥 It has a subtle bug affecting **10% of requests** -* ⏰ By the time you notice: **100,000 users affected** -* 😱 Rollback takes **15 minutes** of downtime - -> 🤔 **Think:** What if you could test with 1% of users first? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L14_PRE - ---- - -## 📍 Slide 6 – ⚠️ Section 1: The Deployment Risk Problem - -**Traditional "Big Bang" Deployment:** - -```mermaid -flowchart TD - A[📦 v1 Running] --> B[🚀 Deploy v2] - B --> C[⚡ 100% Traffic to v2] - C --> D{Works?} - D --> |Yes| E[✅ Success] - D --> |No| F[💀 100% Users Affected] - F --> G[😱 Emergency Rollback] -``` - -* ⚡ **All-or-nothing:** No gradual validation -* 😱 **High blast radius:** Everyone affected immediately -* ⏱️ **Slow detection:** Issues found in production - ---- - -## 📍 Slide 7 – 🔥 Pain Point 1: Silent Failures - -**Scenario:** Memory leak that only triggers under load - -```mermaid -flowchart LR - A[🧪 Tests Pass] --> B[🎭 Staging OK] - B --> C[🏭 Deploy to Prod] - C --> D[📈 Real Traffic] - D --> E[💥 OOM after 2 hours] -``` - -* 🧪 **Tests pass** — synthetic load is different -* 🎭 **Staging works** — not enough traffic to trigger -* 🏭 **Production crashes** — after hours of operation - -**Real impact:** -* 😱 Facebook 2021: 6-hour outage from config change -* 💰 Estimated loss: $100 million - ---- - -## 📍 Slide 8 – 🔥 Pain Point 2: Slow Rollback - -**Traditional rollback process:** - -| ⏱️ Step | 📝 Action | ⌛ Time | -|---------|----------|--------| -| 1 | Detect the issue | 10 min | -| 2 | Confirm it's the deploy | 5 min | -| 3 | Find previous version | 2 min | -| 4 | Rebuild/redeploy | 10 min | -| 5 | Verify rollback | 5 min | -| **Total** | **Downtime** | **32+ min** | - -* 🐌 **Slow detection:** Monitoring lag -* 🤔 **Decision paralysis:** "Is it really the deploy?" -* 🔧 **Manual process:** Error-prone under pressure - ---- - -## 📍 Slide 9 – 🔥 Pain Point 3: No Gradual Validation - -**What we want:** - -``` -Deploy → Observe → Decide → Promote/Rollback -``` - -**What we get:** - -``` -Deploy → 🙏 Hope → React when broken -``` - -* 📊 **No metrics integration** — can't auto-decide -* 👨‍💻 **Human in the loop** — for every deploy -* 🎲 **Risk acceptance** — every release is a gamble - ---- - -## 📍 Slide 10 – 💰 The Cost of Bad Deployments - -| 🔥 Problem | 💥 Impact | 📊 Industry Data | -|-----------|----------|------------------| -| Failed deployments | Service degradation | 46% experience monthly failures | -| Slow rollback | Extended outages | Avg 30 min to rollback | -| No canary testing | Full user impact | 100% blast radius | -| Manual promotion | Human error | 70% of incidents | - -**DORA metrics show:** -* 🏆 **Elite teams:** Deploy multiple times per day with <1% failure rate -* 😰 **Low performers:** Monthly deploys with 15%+ failure rate - ---- - -## 📍 Slide 11 – ✅ Section 2: Progressive Delivery Concepts - -**What is Progressive Delivery?** - -* 🐤 **Gradual rollout:** Incrementally shift traffic -* 📊 **Observability:** Measure success at each step -* 🤖 **Automation:** Promote or rollback based on metrics -* 🎯 **Targeted:** Control which users see changes - -```mermaid -flowchart LR - A[📦 v2] --> |5%| B[🎯 Test] - B --> |Metrics OK| C[25%] - C --> |Metrics OK| D[50%] - D --> |Metrics OK| E[100%] - - B --> |Metrics Bad| F[↩️ Rollback] -``` - ---- - -## 📍 Slide 12 – 🚫 Progressive Delivery: What It's NOT - -| 🚫 Myth | ✅ Reality | -|---------|----------| -| Just slow deployments | Strategic, metrics-driven progression | -| Replaces testing | Complements testing with real traffic | -| Only for big companies | Available via Argo Rollouts, Flagger | -| Complicated to implement | Start simple, add automation gradually | - -> 🔥 **Hot take:** "If you're not doing progressive delivery, you're gambling with every deploy." - -**Progressive Delivery is:** -* 🎯 **Risk reduction** — smaller blast radius -* 📊 **Data-driven** — metrics decide promotion -* 🔄 **Continuous** — part of the deployment pipeline - ---- - -## 📍 Slide 13 – 🐤 Canary Deployments Explained - -**Named after "canary in a coal mine"** — early warning system - -```mermaid -flowchart TD - subgraph Production - A[📦 v1 - 95%] --> C[🌐 Users] - B[📦 v2 - 5%] --> C - end - - D[📊 Metrics] --> E{Healthy?} - E --> |Yes| F[📦 v2 - 100%] - E --> |No| G[↩️ v1 - 100%] -``` - -**How it works:** -1. 🚀 Deploy new version alongside old -2. 🎯 Route small % of traffic to new -3. 📊 Compare metrics (errors, latency) -4. ✅ Gradually increase or ↩️ rollback - ---- - -## 📍 Slide 14 – 🔵 Blue-Green Deployments Explained - -**Two identical environments, instant switchover** - -```mermaid -flowchart LR - subgraph Before - A[🔵 Blue v1] --> |100%| C[🌐 Traffic] - B[🟢 Green v2] --> |0%| C - end - - subgraph After Switch - D[🔵 Blue v1] --> |0%| F[🌐 Traffic] - E[🟢 Green v2] --> |100%| F - end -``` - -**Characteristics:** -* ⚡ **Instant switch:** Traffic moves all at once -* ↩️ **Fast rollback:** Switch back to blue -* 💰 **Resource cost:** Double infrastructure during deploy -* 🎯 **Use case:** Database migrations, breaking changes - ---- - -## 📍 Slide 15 – 📊 Canary vs Blue-Green - -| 📋 Aspect | 🐤 Canary | 🔵 Blue-Green | -|----------|----------|---------------| -| Traffic shift | Gradual (5% → 25% → 100%) | Instant (0% → 100%) | -| Rollback speed | Instant | Instant | -| Resource usage | Minimal overhead | Double during deploy | -| Risk exposure | Minimal (small %) | Full (100% at switch) | -| Complexity | Higher (traffic splitting) | Lower (simple switch) | -| Best for | Most deployments | Major version changes | - -> 🤔 **Think:** Which strategy would you use for a database schema change? - ---- - -## 📍 Slide 16 – 🎮 Section 3: Argo Rollouts in Action - -**What is Argo Rollouts?** - -* 🔄 Kubernetes controller for progressive delivery -* 📦 Replaces standard Deployment resource -* 🎯 Supports canary, blue-green, and more -* 📊 Integrates with metrics providers - -```mermaid -flowchart TD - A[📝 Rollout Resource] --> B[🤖 Argo Rollouts Controller] - B --> C[📦 ReplicaSets] - C --> D[☸️ Pods] - E[📊 Prometheus] --> B - B --> F[🎯 Traffic Management] -``` - ---- - -## 📍 Slide 17 – 💥 Scenario 1: First Canary Rollout - -**Basic canary configuration:** - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Rollout -metadata: - name: my-app -spec: - replicas: 10 - strategy: - canary: - steps: - - setWeight: 20 - - pause: {duration: 5m} - - setWeight: 50 - - pause: {duration: 5m} - - setWeight: 100 - selector: - matchLabels: - app: my-app - template: - # Pod template (same as Deployment) -``` - -```mermaid -flowchart LR - A[0%] --> |Step 1| B[20%] - B --> |5min pause| C[50%] - C --> |5min pause| D[100%] -``` - ---- - -## 📍 Slide 18 – ✅ Solution 1: Traffic Progression - -**What happens during canary:** - -| 🕐 Time | 📦 Stable | 🐤 Canary | 📊 Status | -|---------|----------|----------|-----------| -| T+0 | 100% | 0% | Rollout started | -| T+1 | 80% | 20% | setWeight: 20 | -| T+6 | 50% | 50% | pause completed | -| T+11 | 0% | 100% | Full promotion | - -**Rollout states:** -* 🔄 **Progressing:** Moving through steps -* ⏸️ **Paused:** Waiting (manual or timed) -* ✅ **Healthy:** Rollout complete -* 💥 **Degraded:** Issues detected - ---- - -## 📍 Slide 19 – 💥 Scenario 2: Blue-Green with Argo Rollouts - -**Blue-green configuration:** - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Rollout -metadata: - name: my-app -spec: - replicas: 5 - strategy: - blueGreen: - activeService: my-app-active - previewService: my-app-preview - autoPromotionEnabled: false # Manual promotion - scaleDownDelaySeconds: 30 - selector: - matchLabels: - app: my-app -``` - -```mermaid -flowchart TD - A[🔵 Active Service] --> B[📦 Stable Pods] - C[🟢 Preview Service] --> D[📦 New Pods] - E[👨‍💻 QA] --> C - F[🌐 Users] --> A -``` - ---- - -## 📍 Slide 20 – ✅ Solution 2: Preview and Promote - -**Blue-green workflow:** - -1. 🚀 **Deploy:** New pods created, preview service points to them -2. 🧪 **Test:** QA validates via preview service -3. ✅ **Promote:** Traffic switches to new pods -4. 🗑️ **Cleanup:** Old pods scaled down after delay - -**Commands:** -```bash -# Check rollout status -kubectl argo rollouts get rollout my-app - -# Promote preview to active -kubectl argo rollouts promote my-app - -# Abort and rollback -kubectl argo rollouts abort my-app -``` - ---- - -## 📍 Slide 21 – 💥 Scenario 3: Automated Analysis - -**Problem:** Manual observation doesn't scale - -```mermaid -flowchart TD - A[🐤 Canary] --> B[📊 Metrics] - B --> C{Error rate < 1%?} - C --> |Yes| D{Latency < 500ms?} - D --> |Yes| E[✅ Promote] - C --> |No| F[↩️ Rollback] - D --> |No| F -``` - -**Solution:** AnalysisTemplate - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: AnalysisTemplate -metadata: - name: success-rate -spec: - metrics: - - name: success-rate - interval: 1m - successCondition: result[0] >= 0.99 - provider: - prometheus: - query: | - sum(rate(http_requests_total{status=~"2.*"}[5m])) / - sum(rate(http_requests_total[5m])) -``` - ---- - -## 📍 Slide 22 – ✅ Solution 3: Analysis Integration - -**Connecting analysis to rollout:** - -```yaml -strategy: - canary: - steps: - - setWeight: 20 - - analysis: - templates: - - templateName: success-rate - args: - - name: service-name - value: my-app - - setWeight: 50 - - analysis: - templates: - - templateName: success-rate - - setWeight: 100 -``` - -**Analysis outcomes:** -* ✅ **Successful:** All metrics pass → continue -* ❌ **Failed:** Metric fails → automatic rollback -* ⚠️ **Inconclusive:** Not enough data → pause - ---- - -## 📍 Slide 23 – 💥 Scenario 4: Traffic Management - -**Problem:** Need fine-grained traffic control - -**Solutions:** - -| 🛠️ Traffic Manager | 📝 Description | -|-------------------|---------------| -| **Nginx Ingress** | Canary annotations | -| **Istio** | VirtualService routing | -| **AWS ALB** | Target group weights | -| **Traefik** | TraefikService | - -```yaml -# With Istio -strategy: - canary: - trafficRouting: - istio: - virtualService: - name: my-app-vsvc - destinationRule: - name: my-app-destrule - canarySubsetName: canary - stableSubsetName: stable -``` - ---- - -## 📍 Slide 24 – ✅ Solution 4: Nginx Ingress Canary - -**Simple traffic splitting with Nginx:** - -```yaml -strategy: - canary: - canaryService: my-app-canary - stableService: my-app-stable - trafficRouting: - nginx: - stableIngress: my-app-ingress -``` - -**How it works:** -* 🔧 Argo Rollouts creates canary ingress -* 📊 Sets `nginx.ingress.kubernetes.io/canary-weight` -* 🔄 Updates weight as rollout progresses - ---- - -## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L14_MID - ---- - -## 📍 Slide 26 – 🔧 Section 4: Advanced Strategies - -**Experiment: A/B Testing** - -```yaml -apiVersion: argoproj.io/v1alpha1 -kind: Experiment -metadata: - name: ab-test -spec: - duration: 1h - templates: - - name: baseline - specRef: stable - replicas: 1 - - name: canary - specRef: canary - replicas: 1 - analyses: - - name: compare-versions - templateName: compare-metrics -``` - -**Use cases:** -* 🧪 **Feature testing:** Compare feature A vs B -* 📊 **Performance testing:** Baseline vs optimized -* 🎯 **User experience:** Different UIs - ---- - -## 📍 Slide 27 – 📊 Metrics for Analysis - -**Common metrics to analyze:** - -| 📊 Metric | 📝 What it Measures | ⚠️ Threshold | -|----------|-------------------|-------------| -| Error rate | % of failed requests | < 1% | -| Latency P99 | Slowest 1% of requests | < 500ms | -| Saturation | Resource utilization | < 80% | -| Success rate | % of successful operations | > 99% | - -**Prometheus queries:** -```promql -# Error rate -sum(rate(http_requests_total{status=~"5.*"}[5m])) / -sum(rate(http_requests_total[5m])) - -# P99 latency -histogram_quantile(0.99, rate(http_duration_seconds_bucket[5m])) -``` - ---- - -## 📍 Slide 28 – 🔄 Rollback Strategies - -**Automatic rollback triggers:** - -```yaml -strategy: - canary: - steps: - - setWeight: 20 - - analysis: - templates: - - templateName: error-rate - # If analysis fails, automatic rollback -``` - -**Manual rollback:** -```bash -# Abort current rollout -kubectl argo rollouts abort my-app - -# Undo to previous version -kubectl argo rollouts undo my-app - -# Retry after fix -kubectl argo rollouts retry rollout my-app -``` - -```mermaid -flowchart TD - A[💥 Analysis Failed] --> B[🤖 Auto Rollback] - B --> C[📦 Scale down canary] - C --> D[🔄 Restore stable] - D --> E[✅ Service restored] -``` - ---- - -## 📍 Slide 29 – ⏸️ Pause and Resume - -**Manual gates in rollout:** - -```yaml -steps: - - setWeight: 20 - - pause: {} # Manual pause - requires promotion - - setWeight: 50 - - pause: {duration: 10m} # Timed pause - - setWeight: 100 -``` - -**Commands:** -```bash -# Resume paused rollout -kubectl argo rollouts promote my-app - -# Skip all remaining steps -kubectl argo rollouts promote my-app --full -``` - -**Use cases:** -* 👀 **Manual verification** before wider rollout -* 🕐 **Business hours** — pause overnight -* 🧪 **QA sign-off** required - ---- - -## 📍 Slide 30 – 🔗 ArgoCD + Argo Rollouts - -**GitOps + Progressive Delivery:** - -```mermaid -flowchart TD - A[📝 Git Push] --> B[🤖 ArgoCD] - B --> |Sync| C[📦 Rollout Resource] - C --> D[🤖 Argo Rollouts Controller] - D --> E[🐤 Canary Progression] - E --> F[📊 Analysis] - F --> |Pass| G[✅ Promoted] - F --> |Fail| H[↩️ Rollback] -``` - -**Benefits:** -* 📝 **Declarative:** Rollout strategy in Git -* 🔄 **Automated:** ArgoCD syncs, Rollouts executes -* 🔍 **Observable:** Both tools have UIs - ---- - -## 📍 Slide 31 – 📊 Dashboard and Visualization - -**Argo Rollouts Dashboard:** - -```bash -# Install dashboard -kubectl argo rollouts dashboard - -# Access at localhost:3100 -``` - -**Features:** -* 📊 **Real-time status:** See rollout progression -* 🎛️ **Controls:** Promote, abort, retry -* 📈 **History:** Past rollouts and outcomes -* 🔗 **Integration:** Links to metrics - ---- - -## 📍 Slide 32 – 🎯 Best Practices - -| 📋 Practice | 📝 Why | -|------------|--------| -| Start with simple canary | Learn before adding complexity | -| Always have analysis | Don't rely only on time-based | -| Set appropriate thresholds | Too strict = never promotes | -| Monitor canary metrics | Catch issues before promotion | -| Test rollback procedure | Know it works before you need it | -| Use GitOps | Keep strategy in version control | - ---- - -## 📍 Slide 33 – 🏭 Section 5: Production Patterns - -**Netflix Progressive Delivery:** - -```mermaid -flowchart LR - A[📦 v2] --> B[🎯 Internal 1%] - B --> C[🌍 One Region 5%] - C --> D[🌍 All Regions 25%] - D --> E[🚀 Full 100%] -``` - -**Their learnings:** -* 🎯 **Internal first:** Employees as first canaries -* 🌍 **Regional:** Test in one region before global -* 📊 **Metrics-driven:** Automated promotion -* 🐌 **Patience:** Days, not minutes - ---- - -## 📍 Slide 34 – 🔧 Anti-Patterns to Avoid - -| ❌ Anti-Pattern | ✅ Better Approach | -|----------------|-------------------| -| Canary with no metrics | Add analysis, even basic | -| Too fast progression | Allow time for issues to surface | -| Ignoring saturation | Include resource metrics | -| Manual-only promotion | Automate with analysis | -| Skip staging canary | Test progressive delivery in staging | - ---- - -## 📍 Slide 35 – 📈 Measuring Success - -**DORA metrics with progressive delivery:** - -| 📊 Metric | 😰 Before | 🚀 After | -|----------|----------|---------| -| Deployment frequency | Weekly | Multiple/day | -| Change failure rate | 15% | < 1% | -| MTTR | 30 min | 2 min | -| Lead time | Days | Hours | - -**Why improvement:** -* 🐤 **Catch issues early** — smaller blast radius -* ↩️ **Fast rollback** — seconds, not minutes -* 📊 **Data-driven** — objective decisions -* 🔄 **Confidence** — deploy more often - ---- - -## 📍 Slide 36 – 🎯 Key Takeaways - -1. 🐤 **Canary deployments** test with small traffic before full rollout -2. 🔵 **Blue-green** enables instant rollback via traffic switch -3. 📊 **Automated analysis** removes human guesswork -4. 🎯 **Argo Rollouts** makes progressive delivery accessible -5. 🔗 **GitOps integration** keeps strategy declarative -6. ↩️ **Fast rollback** is as important as deployment - -> 💬 *"Deploy frequently, observe constantly, rollback automatically."* - ---- - -## 📍 Slide 37 – 🧠 Mindset Shift - -| 😰 Old Mindset | 🚀 New Mindset | -|---------------|----------------| -| "Let's hope it works" | "Let's measure and know" | -| "Deploy and pray" | "Deploy, observe, decide" | -| "Rollback is failure" | "Rollback is success" | -| "Testing is enough" | "Testing + production validation" | -| "Deploy once a week (safe)" | "Deploy often (safer)" | -| "100% or nothing" | "Progressive and controlled" | - -> 🤔 **Question:** Which deployment approach does your team use today? - ---- - -## 📍 Slide 38 – 📝 QUIZ — DEVOPS_L14_POST - ---- - -## 📍 Slide 39 – 🚀 What's Next? - -**Coming up: Lecture 15 — Stateful Applications & Observability** - -```mermaid -flowchart LR - A[📦 Deployment] --> B[🗄️ StatefulSet] - B --> C[💾 Persistent Storage] - C --> D[📊 Monitoring] -``` - -* 🗄️ **StatefulSets:** Managing stateful applications -* 💾 **Persistent storage:** Beyond ephemeral pods -* 📊 **Observability:** Prometheus, Grafana -* 🔍 **Alerting:** Know before users complain - -> 🎯 **Lab 14:** Implement canary deployments with Argo Rollouts! - ---- - -## 📚 Resources - -**Documentation:** -* 📖 [Argo Rollouts Docs](https://argoproj.github.io/argo-rollouts/) -* 📖 [Progressive Delivery](https://www.weave.works/blog/progressive-delivery) -* 📖 [Canary Deployments](https://martinfowler.com/bliki/CanaryRelease.html) - -**Tools:** -* 🔧 [Argo Rollouts](https://argoproj.github.io/argo-rollouts/) -* 🔧 [Flagger](https://flagger.app/) -* 🔧 [Istio](https://istio.io/) - -**Books:** -* 📕 *Accelerate* by Nicole Forsgren, Jez Humble, Gene Kim -* 📕 *Continuous Delivery* by Jez Humble & David Farley diff --git a/lectures/lec15.md b/lectures/lec15.md deleted file mode 100644 index eb828de609..0000000000 --- a/lectures/lec15.md +++ /dev/null @@ -1,821 +0,0 @@ -# 📌 Lecture 15 — Stateful Applications & Observability: The Complete Picture - -> 🎯 **From stateless simplicity to production-ready stateful workloads with full observability** - ---- - -## 📍 Slide 1 – 🚀 The Final Pieces of Production Kubernetes - -We've deployed applications, managed configs, and implemented GitOps. Two challenges remain: - -* 🗄️ **Stateful apps:** Databases, message queues, caches — they need identity and stable storage -* 📊 **Observability:** If you can't see it, you can't fix it - -```mermaid -flowchart LR - A[📦 Stateless Apps] --> B[🗄️ StatefulSets] - B --> C[📊 Monitoring] - C --> D[🔔 Alerting] - D --> E[💎 Production Ready] -``` - -> 🎯 **Goal:** Master stateful workloads and comprehensive cluster observability - ---- - -## 📍 Slide 2 – 📚 Learning Outcomes - -By the end of this lecture, you will: - -| # | 🎯 Outcome | -|---|-----------| -| 1 | ✅ Understand when to use **StatefulSets** vs Deployments | -| 2 | ✅ Implement **headless services** for pod discovery | -| 3 | ✅ Configure **VolumeClaimTemplates** for per-pod storage | -| 4 | ✅ Deploy **Prometheus** for metrics collection | -| 5 | ✅ Create **Grafana dashboards** for visualization | -| 6 | ✅ Set up **alerting** for proactive incident response | - ---- - -## 📍 Slide 3 – 🗺️ Lecture Overview - -``` -┌─────────────────────────────────────────────────────────────┐ -│ SECTION 0: Introduction (Slides 1-4) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 PRE QUIZ (Slide 5) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 1: Stateful Workload Challenges (Slides 6-10) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 2: StatefulSets Deep Dive (Slides 11-18) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 MID QUIZ (Slide 19) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 3: Observability Fundamentals (Slides 20-28) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 4: Production Monitoring (Slides 29-36) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 POST QUIZ (Slide 37) │ -├─────────────────────────────────────────────────────────────┤ -│ FINAL: What's Next (Slide 38) │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 📍 Slide 4 – 🤔 The Big Question - -> 💬 *"You can't manage what you can't measure."* -> — Peter Drucker - -**Consider these scenarios:** - -* 🗄️ **Database cluster:** Pods need stable identity for replication -* 📊 **3 AM alert:** Is the app slow, or is it the database? -* 🔍 **Debugging:** "What changed in the last hour?" -* 🔮 **Capacity planning:** "Will we run out of storage next month?" - -> 🤔 **Think:** How do you know your system is healthy right now? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L15_PRE - ---- - -## 📍 Slide 6 – ⚠️ Section 1: Why Stateless Isn't Always Enough - -**Deployments (Stateless) characteristics:** - -```mermaid -flowchart TD - A[📦 Deployment] --> B[📦 Pod-abc123] - A --> C[📦 Pod-def456] - A --> D[📦 Pod-ghi789] - - E[Pods are interchangeable] - F[Random names] - G[Any pod can be replaced] -``` - -* ✅ **Great for:** Web servers, API services, workers -* ❌ **Problem for:** Databases, message queues, distributed systems - ---- - -## 📍 Slide 7 – 🔥 Pain Point 1: Pod Identity - -**Stateful apps need stable identity:** - -| 🗄️ Application | 🔑 Why Identity Matters | -|---------------|------------------------| -| **PostgreSQL** | Primary/replica must know who is who | -| **MongoDB** | Replica set members have specific roles | -| **Kafka** | Brokers identified by stable IDs | -| **Redis Cluster** | Nodes need persistent slots | -| **Elasticsearch** | Nodes join cluster by name | - -**With Deployment:** -``` -pod-abc123 → restarts → pod-xyz789 (new name!) -``` - -**The problem:** Other pods can't find it anymore - ---- - -## 📍 Slide 8 – 🔥 Pain Point 2: Storage Persistence - -**Scenario:** 3-node MongoDB replica set - -```mermaid -flowchart TD - A[📦 Deployment] --> B[📦 Pod 1] - A --> C[📦 Pod 2] - A --> D[📦 Pod 3] - - E[💾 Shared PVC] --> B - E --> C - E --> D - - F[😱 Problem: All pods fighting for same storage!] -``` - -**What we need:** -* 📦 Pod 1 → 💾 Volume 1 (its own data) -* 📦 Pod 2 → 💾 Volume 2 (its own data) -* 📦 Pod 3 → 💾 Volume 3 (its own data) - ---- - -## 📍 Slide 9 – 🔥 Pain Point 3: Ordered Operations - -**Database cluster startup order matters:** - -```mermaid -flowchart LR - A[🥇 Primary starts] --> B[🥈 Replica 1 joins] - B --> C[🥉 Replica 2 joins] -``` - -**Deployment behavior:** All pods start simultaneously -* 😱 Race condition: Who is primary? -* 💥 Data corruption risk - -**Shutdown order matters too:** -* 🥉 Replicas drain first -* 🥇 Primary shuts down last - ---- - -## 📍 Slide 10 – 📊 Deployment vs StatefulSet - -| 📋 Aspect | 📦 Deployment | 🗄️ StatefulSet | -|----------|--------------|----------------| -| Pod names | Random suffix | Stable ordinal (app-0, app-1) | -| Storage | Shared or none | Per-pod PVCs | -| Scaling | Parallel | Sequential | -| Updates | Rolling (parallel) | Rolling (sequential) | -| Network identity | Via Service | Stable DNS per pod | -| Use case | Stateless apps | Stateful apps | - ---- - -## 📍 Slide 11 – ✅ Section 2: StatefulSets to the Rescue - -**StatefulSet provides:** - -```mermaid -flowchart TD - A[🗄️ StatefulSet] --> B[📦 app-0] - A --> C[📦 app-1] - A --> D[📦 app-2] - - B --> E[💾 data-app-0] - C --> F[💾 data-app-1] - D --> G[💾 data-app-2] - - H[🌐 Headless Service] --> B - H --> C - H --> D -``` - -* 🔢 **Stable, unique network IDs:** `app-0`, `app-1`, `app-2` -* 💾 **Stable, persistent storage:** Each pod gets its own PVC -* 📊 **Ordered deployment:** `app-0` first, then `app-1`, then `app-2` -* 🔄 **Ordered termination:** Reverse order - ---- - -## 📍 Slide 12 – 🌐 Headless Services Explained - -**Regular Service vs Headless Service:** - -```yaml -# Regular Service - Load balances -apiVersion: v1 -kind: Service -metadata: - name: my-app -spec: - clusterIP: 10.0.0.100 # Gets an IP - ports: - - port: 80 ---- -# Headless Service - Direct pod access -apiVersion: v1 -kind: Service -metadata: - name: my-app-headless -spec: - clusterIP: None # No IP assigned! - ports: - - port: 80 -``` - -**DNS resolution:** -* **Regular:** `my-app.namespace.svc` → `10.0.0.100` -* **Headless:** `my-app-headless.namespace.svc` → `10.1.2.3, 10.1.2.4, 10.1.2.5` (all pod IPs) - ---- - -## 📍 Slide 13 – 🔗 Pod DNS in StatefulSets - -**Each pod gets a stable DNS name:** - -``` -...svc.cluster.local -``` - -**Example with MongoDB:** -``` -mongodb-0.mongodb-headless.default.svc.cluster.local -mongodb-1.mongodb-headless.default.svc.cluster.local -mongodb-2.mongodb-headless.default.svc.cluster.local -``` - -```mermaid -flowchart LR - A[🔍 DNS Query: mongodb-0.mongodb-headless] --> B[📦 mongodb-0] - C[🔍 DNS Query: mongodb-1.mongodb-headless] --> D[📦 mongodb-1] -``` - -* ✅ Other apps can connect to specific pods -* ✅ Pods can discover each other by name -* ✅ Names stay the same even after restart - ---- - -## 📍 Slide 14 – 💾 VolumeClaimTemplates - -**Automatic PVC creation per pod:** - -```yaml -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: mongodb -spec: - serviceName: mongodb-headless - replicas: 3 - volumeClaimTemplates: - - metadata: - name: data - spec: - accessModes: ["ReadWriteOnce"] - storageClassName: standard - resources: - requests: - storage: 10Gi -``` - -**Result:** -``` -PVC: data-mongodb-0 → bound to mongodb-0 -PVC: data-mongodb-1 → bound to mongodb-1 -PVC: data-mongodb-2 → bound to mongodb-2 -``` - ---- - -## 📍 Slide 15 – 🔄 Scaling Behavior - -**Scale up (sequential):** -```mermaid -flowchart LR - A[app-0 ready] --> B[app-1 starts] - B --> C[app-1 ready] - C --> D[app-2 starts] -``` - -**Scale down (reverse order):** -```mermaid -flowchart LR - A[app-2 terminates] --> B[app-2 gone] - B --> C[app-1 terminates] - C --> D[app-1 gone] -``` - -**Key points:** -* 🔄 Next pod only starts when previous is **Ready** -* 🗑️ Scaling down starts from highest ordinal -* 💾 PVCs are **NOT deleted** on scale down (data preserved) - ---- - -## 📍 Slide 16 – 🔄 Update Strategies - -**RollingUpdate (default):** -```yaml -spec: - updateStrategy: - type: RollingUpdate - rollingUpdate: - partition: 0 # Update all pods -``` - -**Partition for canary:** -```yaml -rollingUpdate: - partition: 2 # Only update pods >= 2 -``` - -```mermaid -flowchart LR - A[app-0: v1] --> B[app-1: v1] - B --> C[app-2: v2 - canary] -``` - -* ✅ Test new version on highest ordinal first -* ✅ Gradually lower partition to update more pods - ---- - -## 📍 Slide 17 – 🗄️ Complete StatefulSet Example - -```yaml -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: mongodb -spec: - selector: - matchLabels: - app: mongodb - serviceName: mongodb-headless - replicas: 3 - template: - metadata: - labels: - app: mongodb - spec: - containers: - - name: mongodb - image: mongo:7 - ports: - - containerPort: 27017 - volumeMounts: - - name: data - mountPath: /data/db - volumeClaimTemplates: - - metadata: - name: data - spec: - accessModes: ["ReadWriteOnce"] - resources: - requests: - storage: 10Gi -``` - ---- - -## 📍 Slide 18 – ⚠️ StatefulSet Gotchas - -| ⚠️ Gotcha | 📝 Solution | -|----------|------------| -| PVCs not deleted on scale down | Manual deletion if needed | -| Pod stuck in Pending | Check PVC binding, storage class | -| Headless service required | Must create before StatefulSet | -| Slow scaling | Increase `podManagementPolicy: Parallel` | -| Data loss on PVC deletion | Use `reclaimPolicy: Retain` | - -**Important:** StatefulSets are **more complex** than Deployments. Use only when needed! - ---- - -## 📍 Slide 19 – 📝 QUIZ — DEVOPS_L15_MID - ---- - -## 📍 Slide 20 – 📊 Section 3: The Three Pillars of Observability - -**Observability = Understanding system behavior from outputs** - -```mermaid -flowchart TD - A[📊 Metrics] --> D[🔍 Observability] - B[📝 Logs] --> D - C[🔗 Traces] --> D - D --> E[💡 Understanding] -``` - -| 📊 Pillar | 📝 What It Answers | 🛠️ Tools | -|----------|-------------------|----------| -| **Metrics** | What is happening? (numbers) | Prometheus, Grafana | -| **Logs** | What happened? (events) | Loki, ELK | -| **Traces** | Where did it happen? (requests) | Jaeger, Zipkin | - ---- - -## 📍 Slide 21 – 📈 Prometheus: The Metrics Foundation - -**What is Prometheus?** - -* 📊 Time-series database for metrics -* 🔍 Pull-based metric collection -* 📝 Powerful query language (PromQL) -* 🔔 Built-in alerting - -```mermaid -flowchart LR - A[📦 App with /metrics] --> |Pull| B[📊 Prometheus] - C[📦 Another App] --> |Pull| B - B --> D[📈 Grafana] - B --> E[🔔 Alertmanager] -``` - ---- - -## 📍 Slide 22 – 🎯 Prometheus Metric Types - -| 📊 Type | 📝 Description | 🎯 Example | -|--------|---------------|-----------| -| **Counter** | Only goes up | Total requests, errors | -| **Gauge** | Can go up/down | Temperature, queue size | -| **Histogram** | Distribution of values | Request latency | -| **Summary** | Similar to histogram | Quantiles | - -**Example metrics:** -```promql -# Counter - total HTTP requests -http_requests_total{method="GET", status="200"} - -# Gauge - current memory usage -node_memory_MemAvailable_bytes - -# Histogram - request duration -http_request_duration_seconds_bucket{le="0.5"} -``` - ---- - -## 📍 Slide 23 – 🔍 PromQL Basics - -**Query examples:** - -```promql -# Current value -up{job="kubernetes-pods"} - -# Rate of change (per second over 5m) -rate(http_requests_total[5m]) - -# Error rate percentage -sum(rate(http_requests_total{status=~"5.."}[5m])) / -sum(rate(http_requests_total[5m])) * 100 - -# 99th percentile latency -histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) - -# Top 5 pods by CPU -topk(5, sum by (pod) (rate(container_cpu_usage_seconds_total[5m]))) -``` - ---- - -## 📍 Slide 24 – 📦 kube-prometheus-stack - -**All-in-one monitoring solution:** - -```mermaid -flowchart TD - A[📦 kube-prometheus-stack] --> B[📊 Prometheus] - A --> C[📈 Grafana] - A --> D[🔔 Alertmanager] - A --> E[📝 Node Exporter] - A --> F[📊 kube-state-metrics] -``` - -**Includes:** -* 🔧 Pre-configured scrape targets -* 📊 Default dashboards -* 🔔 Default alerting rules -* 📈 Grafana with data sources configured - -```bash -helm install prometheus prometheus-community/kube-prometheus-stack -``` - ---- - -## 📍 Slide 25 – 📈 Grafana Dashboards - -**Key Grafana concepts:** - -| 🔧 Concept | 📝 Description | -|-----------|---------------| -| **Data Source** | Where data comes from (Prometheus) | -| **Dashboard** | Collection of panels | -| **Panel** | Single visualization | -| **Variable** | Dynamic filters (namespace, pod) | - -**Popular pre-built dashboards:** -* 🔢 **1860:** Node Exporter Full -* 🔢 **315:** Kubernetes cluster -* 🔢 **7249:** Kubernetes Pod Resources - ---- - -## 📍 Slide 26 – 🔔 Alerting with Alertmanager - -**Alert flow:** - -```mermaid -flowchart LR - A[📊 Prometheus] --> |Alert fires| B[🔔 Alertmanager] - B --> |Route| C[📧 Email] - B --> |Route| D[💬 Slack] - B --> |Route| E[📱 PagerDuty] -``` - -**Alert rule example:** -```yaml -groups: - - name: app-alerts - rules: - - alert: HighErrorRate - expr: | - sum(rate(http_requests_total{status=~"5.."}[5m])) / - sum(rate(http_requests_total[5m])) > 0.01 - for: 5m - labels: - severity: critical - annotations: - summary: "High error rate detected" - description: "Error rate is {{ $value | humanizePercentage }}" -``` - ---- - -## 📍 Slide 27 – 📊 The Four Golden Signals - -**Google SRE's essential metrics:** - -| 🔔 Signal | 📝 What to Measure | 📊 Prometheus Example | -|----------|-------------------|----------------------| -| **Latency** | Response time | `histogram_quantile(0.99, ...)` | -| **Traffic** | Request rate | `rate(http_requests_total[5m])` | -| **Errors** | Failure rate | `rate(http_requests_total{status=~"5.."}[5m])` | -| **Saturation** | Resource usage | `container_memory_usage_bytes / limit` | - -> 💡 **Tip:** Start by monitoring these four signals for every service - ---- - -## 📍 Slide 28 – 🔧 Init Containers for Dependencies - -**Problem:** App starts before database is ready - -```yaml -spec: - initContainers: - - name: wait-for-db - image: busybox - command: - - sh - - -c - - | - until nc -z postgres-0.postgres-headless 5432; do - echo "Waiting for database..." - sleep 2 - done - containers: - - name: app - image: my-app -``` - -```mermaid -flowchart LR - A[🚀 Pod Start] --> B[⏳ Init Container] - B --> |DB Ready| C[📦 App Container] -``` - ---- - -## 📍 Slide 29 – 🏭 Section 4: Production Monitoring Setup - -**ServiceMonitor for custom apps:** - -```yaml -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - name: my-app-monitor -spec: - selector: - matchLabels: - app: my-app - endpoints: - - port: metrics - interval: 30s - path: /metrics -``` - -**What this does:** -* 🔍 Tells Prometheus to scrape your app -* 📊 Collects metrics from `/metrics` endpoint -* ⏱️ Every 30 seconds - ---- - -## 📍 Slide 30 – 📊 Monitoring StatefulSets - -**Key metrics for stateful apps:** - -| 📊 Metric | 📝 Why Important | -|----------|------------------| -| `kubelet_volume_stats_used_bytes` | Disk usage per PVC | -| `kube_statefulset_replicas` | Expected vs actual replicas | -| `kube_statefulset_status_replicas_ready` | Healthy replicas | -| App-specific metrics | Replication lag, connections | - -**Alert example:** -```yaml -- alert: StatefulSetNotReady - expr: | - kube_statefulset_status_replicas_ready / - kube_statefulset_replicas < 1 - for: 5m - labels: - severity: warning -``` - ---- - -## 📍 Slide 31 – 📈 Resource Monitoring - -**CPU and Memory queries:** - -```promql -# CPU usage percentage -sum(rate(container_cpu_usage_seconds_total{pod=~"my-app.*"}[5m])) / -sum(kube_pod_container_resource_limits{resource="cpu", pod=~"my-app.*"}) * 100 - -# Memory usage percentage -sum(container_memory_working_set_bytes{pod=~"my-app.*"}) / -sum(kube_pod_container_resource_limits{resource="memory", pod=~"my-app.*"}) * 100 -``` - -**Capacity planning alerts:** -```yaml -- alert: HighMemoryUsage - expr: | - sum(container_memory_working_set_bytes) by (pod) / - sum(kube_pod_container_resource_limits{resource="memory"}) by (pod) > 0.8 - for: 15m -``` - ---- - -## 📍 Slide 32 – 🔔 Alert Fatigue Prevention - -**Problem:** Too many alerts = ignored alerts - -| ❌ Bad Practice | ✅ Better Approach | -|----------------|-------------------| -| Alert on every metric | Alert on symptoms, not causes | -| No severity levels | Critical, warning, info tiers | -| Alert immediately | Use `for` duration | -| Generic messages | Actionable descriptions | -| No runbooks | Link to debugging guides | - -**Good alert structure:** -```yaml -annotations: - summary: "High error rate on {{ $labels.service }}" - description: "Error rate is {{ $value }}% (threshold: 1%)" - runbook_url: "https://wiki/alerts/high-error-rate" -``` - ---- - -## 📍 Slide 33 – 📊 Dashboard Best Practices - -**Effective dashboard layout:** - -``` -┌─────────────────────────────────────────┐ -│ 📊 Overview: Key metrics at a glance │ -├─────────────┬─────────────┬─────────────┤ -│ 🔴 Errors │ ⏱️ Latency │ 📈 Traffic │ -├─────────────┴─────────────┴─────────────┤ -│ 💾 Resource Usage (CPU, Memory, Disk) │ -├─────────────────────────────────────────┤ -│ 🔍 Detailed Breakdowns (per pod, etc.) │ -└─────────────────────────────────────────┘ -``` - -**Tips:** -* 📊 Start with high-level, drill down for details -* 🎨 Use consistent colors (red = bad) -* 📝 Add descriptions to panels -* 🔗 Link related dashboards - ---- - -## 📍 Slide 34 – 🏢 Real-World: Observability at Scale - -**Netflix observability approach:** - -* 📊 **Metrics:** Atlas (Prometheus-like, billions of time series) -* 📝 **Logs:** Mantis (real-time stream processing) -* 🔗 **Traces:** Edgar (distributed tracing) -* 🔔 **Alerts:** Focused on customer impact - -**Key lessons:** -* 🎯 Focus on **business metrics** (not just infra) -* 🔄 Automate **remediation** where possible -* 📈 Invest in **dashboards** as a product -* 👥 Make observability **everyone's** job - ---- - -## 📍 Slide 35 – 🎯 Key Takeaways - -1. 🗄️ **StatefulSets** provide stable identity and per-pod storage for databases -2. 🌐 **Headless services** enable direct pod-to-pod communication -3. 📊 **Three pillars:** Metrics, Logs, Traces for full observability -4. 📈 **Prometheus + Grafana** is the standard K8s monitoring stack -5. 🔔 **Alerts should be actionable** — avoid alert fatigue -6. 🎯 **Four Golden Signals:** Latency, Traffic, Errors, Saturation - -> 💬 *"Observability is not about collecting data, it's about understanding your system."* - ---- - -## 📍 Slide 36 – 🧠 Mindset Shift - -| 😰 Old Mindset | 🚀 New Mindset | -|---------------|----------------| -| "It's working, don't touch it" | "I can see it's working" | -| "Let's check the logs" | "The dashboard shows the issue" | -| "User reported an error" | "Alert fired before impact" | -| "Database needs restart" | "DB has stable identity, restart is safe" | -| "Collect all the metrics" | "Monitor what matters" | -| "Alert on everything" | "Alert on symptoms, investigate causes" | - -> 🤔 **Question:** What's the first dashboard you'd build for your app? - ---- - -## 📍 Slide 37 – 📝 QUIZ — DEVOPS_L15_POST - ---- - -## 📍 Slide 38 – 🚀 What's Next? - -**Coming up: Lecture 16 — Beyond Kubernetes** - -```mermaid -flowchart LR - A[☸️ Kubernetes] --> B[✈️ Fly.io] - A --> C[🌐 IPFS/4EVERLAND] - B --> D[🌍 Global Edge] - C --> E[🔗 Decentralized] -``` - -* ✈️ **Fly.io:** Edge deployment simplified -* 🌐 **IPFS:** Decentralized hosting -* 🤔 **When to use what:** Trade-offs and decisions -* 🎯 **Beyond the cluster:** Alternative deployment models - -> 🎯 **Labs 15 & 16:** Convert your app to StatefulSet and set up comprehensive monitoring! - ---- - -## 📚 Resources - -**StatefulSets:** -* 📖 [Kubernetes StatefulSets](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) -* 📖 [Headless Services](https://kubernetes.io/docs/concepts/services-networking/service/#headless-services) - -**Observability:** -* 📖 [Prometheus Docs](https://prometheus.io/docs/) -* 📖 [Grafana Docs](https://grafana.com/docs/) -* 📖 [kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack) - -**Books:** -* 📕 *Observability Engineering* by Charity Majors, et al. -* 📕 *Site Reliability Engineering* by Google -* 📕 *Kubernetes Patterns* by Bilgin Ibryam & Roland Huß diff --git a/lectures/lec16.md b/lectures/lec16.md deleted file mode 100644 index 2f8bf01d5b..0000000000 --- a/lectures/lec16.md +++ /dev/null @@ -1,717 +0,0 @@ -# 📌 Lecture 16 — Beyond Kubernetes: Alternative Deployment Models - -> 🎯 **From cluster management to platform abstraction and decentralized hosting** - ---- - -## 📍 Slide 1 – 🚀 Kubernetes Isn't Always the Answer - -We've mastered Kubernetes. But is it always the right choice? - -* ☸️ **Kubernetes:** Powerful, but complex -* ✈️ **PaaS (Fly.io):** Simple, global, managed -* 🌐 **Decentralized (IPFS):** Permanent, censorship-resistant - -```mermaid -flowchart LR - A[📦 Your App] --> B{What matters most?} - B --> |Control & Scale| C[☸️ Kubernetes] - B --> |Simplicity & Global| D[✈️ Fly.io] - B --> |Permanence & Decentralization| E[🌐 IPFS] -``` - -> 🎯 **Goal:** Understand when to choose each deployment model - ---- - -## 📍 Slide 2 – 📚 Learning Outcomes - -By the end of this lecture, you will: - -| # | 🎯 Outcome | -|---|-----------| -| 1 | ✅ Evaluate **trade-offs** between deployment models | -| 2 | ✅ Deploy applications to **Fly.io** edge network | -| 3 | ✅ Understand **IPFS** and content addressing | -| 4 | ✅ Use **4EVERLAND** for decentralized hosting | -| 5 | ✅ Choose the **right tool** for different use cases | -| 6 | ✅ Appreciate the **evolving cloud landscape** | - ---- - -## 📍 Slide 3 – 🗺️ Lecture Overview - -``` -┌─────────────────────────────────────────────────────────────┐ -│ SECTION 0: Introduction (Slides 1-4) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 PRE QUIZ (Slide 5) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 1: The Complexity Trade-off (Slides 6-10) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 2: Edge Computing with Fly.io (Slides 11-18) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 MID QUIZ (Slide 19) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 3: Decentralized Web & IPFS (Slides 20-28) │ -├─────────────────────────────────────────────────────────────┤ -│ SECTION 4: Choosing Your Path (Slides 29-35) │ -├─────────────────────────────────────────────────────────────┤ -│ 📝 POST QUIZ (Slide 36) │ -├─────────────────────────────────────────────────────────────┤ -│ FINAL: Course Wrap-up (Slide 37) │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 📍 Slide 4 – 🤔 The Big Question - -> 💬 *"The best tool is the one that solves your problem with the least unnecessary complexity."* -> — Practical Engineering Wisdom - -**Consider these scenarios:** - -* 🏢 **Enterprise with 500 microservices:** Probably needs Kubernetes -* 🚀 **Startup with 3 developers:** Maybe doesn't need a cluster -* 📰 **News article that must stay online forever:** Decentralized? -* 🌍 **App serving users globally:** Edge deployment? - -> 🤔 **Think:** What does YOUR application actually need? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L16_PRE - ---- - -## 📍 Slide 6 – ⚠️ Section 1: The Kubernetes Tax - -**What Kubernetes requires:** - -```mermaid -flowchart TD - A[☸️ Kubernetes Cluster] --> B[🔧 Cluster Management] - A --> C[🔐 Security Hardening] - A --> D[📊 Monitoring Setup] - A --> E[🔄 Upgrade Planning] - A --> F[💰 Infrastructure Cost] -``` - -* 🔧 **Cluster operations:** Updates, scaling, troubleshooting -* 🧠 **Team expertise:** Steep learning curve -* 💰 **Cost:** Control plane, nodes, load balancers -* ⏱️ **Time:** Setup, maintenance, incident response - ---- - -## 📍 Slide 7 – 🔥 When Kubernetes Is Overkill - -**Signs you might not need Kubernetes:** - -| 🚩 Sign | 📝 Alternative | -|--------|---------------| -| Single application | PaaS (Fly.io, Railway, Render) | -| Small team (1-5 devs) | Managed services | -| Simple deployment needs | Container platforms | -| Cost-sensitive startup | Serverless or PaaS | -| No specialized workloads | Simpler solutions | - -> 💬 *"Don't use Kubernetes to solve problems you don't have."* - ---- - -## 📍 Slide 8 – 📊 The Abstraction Spectrum - -```mermaid -flowchart LR - A[🖥️ Bare Metal] --> B[☁️ VMs/IaaS] - B --> C[☸️ Kubernetes] - C --> D[✈️ PaaS] - D --> E[⚡ Serverless] - E --> F[🌐 Decentralized] - - G[More Control] -.-> A - H[More Abstraction] -.-> F -``` - -| 🎚️ Level | 🔧 You Manage | ✅ Platform Manages | -|----------|--------------|---------------------| -| Bare Metal | Everything | Nothing | -| IaaS (EC2) | OS, runtime, app | Hardware | -| Kubernetes | App, configs | Orchestration | -| PaaS | App code only | Everything else | -| Serverless | Functions | Runtime, scaling | - ---- - -## 📍 Slide 9 – 🎯 Right Tool, Right Job - -**Decision factors:** - -| 📋 Factor | ☸️ K8s | ✈️ PaaS | 🌐 IPFS | -|----------|--------|--------|---------| -| Team size | Large | Small | Varies | -| Control needs | High | Medium | Low | -| Cost at scale | Efficient | Can be expensive | Very low | -| Setup time | Days/weeks | Minutes | Minutes | -| Global distribution | Manual config | Built-in | Inherent | -| Vendor lock-in | Low | Medium | None | - ---- - -## 📍 Slide 10 – 💡 The Emergence of Edge Computing - -**Traditional deployment:** -``` -User (Tokyo) → CDN → US-East Server → Response (200ms) -``` - -**Edge deployment:** -``` -User (Tokyo) → Edge Server (Tokyo) → Response (20ms) -``` - -```mermaid -flowchart TD - A[🌍 User Anywhere] --> B{Edge Network} - B --> C[🗼 Tokyo] - B --> D[🗼 London] - B --> E[🗼 New York] - B --> F[🗼 Sydney] -``` - -* ⚡ **Lower latency:** Code runs closer to users -* 🌍 **Global by default:** No region configuration -* 🔄 **Automatic routing:** Users hit nearest edge - ---- - -## 📍 Slide 11 – ✈️ Section 2: Fly.io - Simplicity Meets Global - -**What is Fly.io?** - -* ✈️ Platform for running apps globally -* 📦 Deploys Docker containers (or builds from source) -* 🌍 Runs in 30+ regions automatically -* 💰 Free tier for small apps - -```mermaid -flowchart LR - A[📦 Your Container] --> B[✈️ Fly.io] - B --> C[🗼 Edge 1] - B --> D[🗼 Edge 2] - B --> E[🗼 Edge 3] - C --> F[👤 Users] - D --> F - E --> F -``` - ---- - -## 📍 Slide 12 – 🛠️ Fly.io Architecture - -**Key concepts:** - -| 🔧 Concept | 📝 Description | -|-----------|---------------| -| **Machine** | A Fly VM running your app | -| **App** | A named collection of Machines | -| **Region** | A geographical location (ams, iad, sin) | -| **Volume** | Persistent storage attached to Machine | -| **Secret** | Encrypted environment variable | - -```toml -# fly.toml -app = "my-app" -primary_region = "ams" - -[http_service] - internal_port = 8080 - force_https = true - auto_stop_machines = true - auto_start_machines = true -``` - ---- - -## 📍 Slide 13 – 🚀 Deploying to Fly.io - -**The entire deployment process:** - -```bash -# 1. Install CLI -curl -L https://fly.io/install.sh | sh - -# 2. Login -fly auth login - -# 3. Launch (creates app + config) -fly launch - -# 4. Deploy -fly deploy - -# 5. Open in browser -fly open -``` - -**That's it!** No cluster, no YAML manifests, no ingress controllers. - ---- - -## 📍 Slide 14 – 🌍 Multi-Region Deployment - -**Adding regions:** - -```bash -# Add regions -fly regions add iad sin syd - -# Check machines -fly machines list - -# Scale in specific region -fly scale count 2 --region ams -``` - -```mermaid -flowchart TD - A[✈️ Your App] --> B[🇳🇱 Amsterdam] - A --> C[🇺🇸 Virginia] - A --> D[🇸🇬 Singapore] - A --> E[🇦🇺 Sydney] - - F[👤 European User] --> B - G[👤 US User] --> C - H[👤 Asian User] --> D - I[👤 Australian User] --> E -``` - ---- - -## 📍 Slide 15 – 🔐 Secrets & Storage on Fly.io - -**Secrets:** -```bash -fly secrets set DATABASE_URL="postgres://..." -fly secrets set API_KEY="secret123" -fly secrets list -``` - -**Persistent storage:** -```bash -# Create volume -fly volumes create mydata --size 1 --region ams -``` - -```toml -# fly.toml -[mounts] - source = "mydata" - destination = "/data" -``` - ---- - -## 📍 Slide 16 – 📊 Fly.io vs Kubernetes Comparison - -| 📋 Aspect | ☸️ Kubernetes | ✈️ Fly.io | -|----------|--------------|----------| -| Setup time | Hours/days | Minutes | -| Learning curve | Steep | Gentle | -| Global distribution | Manual | Built-in | -| Scaling | HPA, VPA, manual | Auto-scale, simple commands | -| Cost (small app) | $50-100/month | Free tier available | -| Control | Full | Limited | -| Customization | Unlimited | Constrained | -| Multi-cloud | Yes | No (Fly only) | - ---- - -## 📍 Slide 17 – 🎯 When to Choose Fly.io - -**Good fit:** - -* ✅ Small to medium applications -* ✅ Need for low global latency -* ✅ Small team, limited DevOps resources -* ✅ Rapid iteration, quick deployments -* ✅ Cost-conscious early-stage projects - -**Not ideal:** - -* ❌ Complex microservices architectures -* ❌ Need for specific cloud services (AWS RDS, etc.) -* ❌ Compliance requirements for specific regions -* ❌ Already invested heavily in Kubernetes - ---- - -## 📍 Slide 18 – 🔧 Fly.io Best Practices - -| 📋 Practice | 📝 Reason | -|------------|----------| -| Use auto_stop_machines | Save costs when idle | -| Add health checks | Enable auto-restart on failure | -| Use volumes for stateful data | Machines are ephemeral | -| Set min_machines_running | Prevent cold starts | -| Use regions near your users | Optimize latency | - -```toml -[http_service] - auto_stop_machines = true - auto_start_machines = true - min_machines_running = 1 - -[checks] - [checks.health] - type = "http" - port = 8080 - path = "/health" -``` - ---- - -## 📍 Slide 19 – 📝 QUIZ — DEVOPS_L16_MID - ---- - -## 📍 Slide 20 – 🌐 Section 3: IPFS & The Decentralized Web - -**What is IPFS?** - -* 🌐 **InterPlanetary File System** -* 📦 Distributed, peer-to-peer storage -* 🔗 Content-addressed (identified by hash, not location) -* ♾️ Immutable, permanent storage - -```mermaid -flowchart LR - A[📄 File] --> B[#️⃣ Hash] - B --> C[🔗 CID: QmXxx...] - C --> D[🌐 Available Globally] -``` - -> 💡 **Key insight:** Same content = same address, anywhere in the world - ---- - -## 📍 Slide 21 – 🔍 Content Addressing Explained - -**Traditional web (location-based):** -``` -https://server.com/path/to/file.html - ↓ - Server could change content! -``` - -**IPFS (content-based):** -``` -ipfs://QmXxx.../file.html - ↓ - Hash of actual content - If content changes, hash changes! -``` - -| 📋 Aspect | 🌐 HTTP | 🔗 IPFS | -|----------|--------|---------| -| Addressing | Location | Content hash | -| Mutability | Content can change | Content is immutable | -| Availability | Single server | Distributed nodes | -| Censorship | Easy to block | Very difficult | - ---- - -## 📍 Slide 22 – 🔑 IPFS Key Concepts - -| 🔧 Concept | 📝 Description | -|-----------|---------------| -| **CID** | Content Identifier - hash of content | -| **Node** | Computer running IPFS software | -| **Pinning** | Keeping content available (prevent garbage collection) | -| **Gateway** | HTTP bridge to IPFS content | -| **IPNS** | Mutable pointer to IPFS content | - -**Example CIDs:** -``` -QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco -bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi -``` - ---- - -## 📍 Slide 23 – 📌 Pinning Services - -**The persistence problem:** - -```mermaid -flowchart TD - A[📄 Add Content] --> B[🔗 Get CID] - B --> C{Who stores it?} - C --> |Your node| D[🖥️ Goes offline = Content unavailable] - C --> |Pinning service| E[☁️ Always available] -``` - -**Pinning services:** -* 📌 **4EVERLAND** — Web3 hosting platform -* 📌 **Pinata** — IPFS pinning -* 📌 **Infura** — IPFS API -* 📌 **web3.storage** — Free storage - ---- - -## 📍 Slide 24 – 🌐 4EVERLAND Platform - -**What is 4EVERLAND?** - -* 🌐 Web3 infrastructure platform -* 📦 IPFS hosting made simple -* 🔧 Deploy from Git (like Vercel/Netlify) -* 💰 Free tier available - -**Services:** -* 🚀 **Hosting:** Deploy static sites and SPAs -* 📦 **Bucket:** IPFS storage (like S3) -* 🌐 **Gateway:** Access IPFS content via HTTP - ---- - -## 📍 Slide 25 – 🚀 Deploying to 4EVERLAND - -**Process:** - -```mermaid -flowchart LR - A[📝 Git Push] --> B[🔄 4EVERLAND Build] - B --> C[📦 Upload to IPFS] - C --> D[🔗 Get CID] - D --> E[🌐 Available via Gateway] -``` - -**Steps:** -1. 🔗 Connect GitHub repository -2. ⚙️ Configure build settings -3. 🚀 Deploy -4. 🔗 Access via CID or custom domain - -**URLs:** -* `https://your-project.4everland.app` -* `https://ipfs.4everland.link/ipfs/CID` - ---- - -## 📍 Slide 26 – 🔄 IPNS: Mutable Pointers - -**Problem:** CID changes when content changes - -**Solution:** IPNS (InterPlanetary Name System) - -```mermaid -flowchart LR - A[🔑 IPNS Name] --> B[🔗 CID v1] - A --> |Update| C[🔗 CID v2] -``` - -| 📋 Type | 🔗 Address | 📝 Behavior | -|--------|-----------|------------| -| **IPFS** | `/ipfs/QmXxx` | Always same content | -| **IPNS** | `/ipns/k51xxx` | Points to current version | - -**4EVERLAND handles this:** Your URL stays the same, content updates automatically - ---- - -## 📍 Slide 27 – 📊 Centralized vs Decentralized - -| 📋 Aspect | 🏢 Traditional | 🌐 IPFS/4EVERLAND | -|----------|---------------|-------------------| -| Single point of failure | Yes | No | -| Censorship resistance | Low | High | -| Content integrity | Trust server | Cryptographic verification | -| Hosting cost | Ongoing | Pin once, available forever | -| Update mechanism | Overwrite file | New CID (or IPNS) | -| Speed | Fast (CDN) | Variable (depends on nodes) | -| Best for | Dynamic apps | Static content, archives | - ---- - -## 📍 Slide 28 – 🎯 When to Choose IPFS/4EVERLAND - -**Good fit:** - -* ✅ Static websites and documentation -* ✅ Content that must survive (archives, important documents) -* ✅ Censorship-resistant publishing -* ✅ NFT metadata and assets -* ✅ Open source project hosting - -**Not ideal:** - -* ❌ Dynamic server-side applications -* ❌ Real-time updates needed -* ❌ Private content (IPFS is public by default) -* ❌ High-performance requirements - ---- - -## 📍 Slide 29 – 🎯 Section 4: Making the Right Choice - -**Decision Framework:** - -```mermaid -flowchart TD - A[📦 Your Application] --> B{Need dynamic backend?} - B --> |Yes| C{Team size?} - B --> |No, static| D{Permanence important?} - - C --> |Large, experienced| E[☸️ Kubernetes] - C --> |Small| F{Global latency critical?} - - F --> |Yes| G[✈️ Fly.io] - F --> |No| H[Simple hosting] - - D --> |Yes| I[🌐 IPFS/4EVERLAND] - D --> |No| J[Static hosting CDN] -``` - ---- - -## 📍 Slide 30 – 📊 Summary Comparison - -| 📋 Criteria | ☸️ Kubernetes | ✈️ Fly.io | 🌐 4EVERLAND/IPFS | -|------------|--------------|----------|-------------------| -| **Complexity** | High | Low | Low | -| **Control** | Full | Medium | Limited | -| **Scalability** | Unlimited | Good | N/A (static) | -| **Global distribution** | Manual | Automatic | Inherent | -| **Cost at scale** | Efficient | Can be expensive | Very low | -| **Learning curve** | Steep | Gentle | Minimal | -| **Use case** | Microservices, enterprise | Global apps, startups | Static content, Web3 | - ---- - -## 📍 Slide 31 – 🏢 Real-World Examples - -**Kubernetes users:** -* 🏢 **Spotify:** 200+ microservices -* 🏢 **Pinterest:** ML workloads -* 🏢 **Airbnb:** Multi-region deployments - -**Fly.io users:** -* 🚀 **Small startups:** Quick global deployment -* 🎮 **Game backends:** Low-latency requirements -* 🛠️ **Developer tools:** API services - -**IPFS/Decentralized:** -* 📚 **Wikipedia mirror:** Censorship-resistant access -* 🎨 **NFT projects:** Metadata storage -* 📰 **News archives:** Permanent preservation - ---- - -## 📍 Slide 32 – 🔮 The Future of Deployment - -**Trends to watch:** - -| 🔮 Trend | 📝 Description | -|---------|---------------| -| **Edge computing** | Code runs closer to users | -| **WebAssembly** | Run any language at the edge | -| **Decentralization** | Web3 infrastructure growth | -| **Platform abstraction** | Less infra management | -| **AI-assisted DevOps** | Automated operations | - -> 💬 *"The cloud is just someone else's computer. The edge is everyone's computer."* - ---- - -## 📍 Slide 33 – 📋 Practical Recommendations - -**For students and learning:** -1. 🎓 Master Kubernetes fundamentals first -2. ✈️ Try Fly.io for personal projects -3. 🌐 Experiment with IPFS for static sites - -**For production decisions:** -1. 📋 Start with requirements, not technology -2. 📊 Consider team capabilities -3. 💰 Factor in total cost (including time) -4. 🔄 Plan for evolution - ---- - -## 📍 Slide 34 – 🎯 Key Takeaways - -1. ☸️ **Kubernetes is powerful** but comes with complexity costs -2. ✈️ **Fly.io offers simplicity** for global, low-latency applications -3. 🌐 **IPFS provides permanence** and censorship resistance -4. 🎯 **No single best solution** — choose based on requirements -5. 📊 **Consider the trade-offs:** control vs simplicity, cost vs features -6. 🔮 **The landscape evolves** — stay curious, keep learning - -> 💬 *"The best architecture is the one your team can operate successfully."* - ---- - -## 📍 Slide 35 – 🧠 Course Mindset Shift - -| 😰 Before This Course | 🚀 After This Course | -|----------------------|---------------------| -| "How do I deploy this?" | "What's the best deployment model?" | -| "Kubernetes is complicated" | "I understand K8s and its alternatives" | -| "DevOps is ops work" | "DevOps is a culture and practice" | -| "I write code, someone else deploys" | "I can deploy, monitor, and maintain" | -| "Just get it working" | "Make it observable, scalable, reliable" | - ---- - -## 📍 Slide 36 – 📝 QUIZ — DEVOPS_L16_POST - ---- - -## 📍 Slide 37 – 🎓 Course Wrap-up - -**What you've learned:** - -```mermaid -flowchart LR - A[🐳 Docker] --> B[☸️ Kubernetes] - B --> C[🔄 CI/CD] - C --> D[📊 Observability] - D --> E[🔐 Security] - E --> F[🌍 Global Deployment] -``` - -**Your DevOps toolkit:** -* 🐳 **Containerization:** Docker, multi-stage builds -* ☸️ **Orchestration:** Kubernetes, Helm, StatefulSets -* 🔄 **GitOps:** ArgoCD, declarative infrastructure -* 📊 **Observability:** Prometheus, Grafana, alerting -* 🔐 **Security:** Secrets management, Vault -* 🚀 **Progressive delivery:** Canary, blue-green -* 🌍 **Beyond K8s:** Edge computing, decentralized hosting - -> 🎉 **Congratulations!** You're now equipped for production DevOps. - ---- - -## 📚 Resources - -**Fly.io:** -* 📖 [Fly.io Documentation](https://fly.io/docs/) -* 📖 [flyctl Reference](https://fly.io/docs/flyctl/) - -**IPFS & 4EVERLAND:** -* 📖 [IPFS Documentation](https://docs.ipfs.tech/) -* 📖 [4EVERLAND Docs](https://docs.4everland.org/) -* 📖 [IPFS Concepts](https://docs.ipfs.tech/concepts/) - -**Further reading:** -* 📕 *The DevOps Handbook* by Gene Kim, et al. -* 📕 *Accelerate* by Nicole Forsgren, et al. -* 📕 *Site Reliability Engineering* by Google - -**Keep learning:** -* 🌐 [CNCF Landscape](https://landscape.cncf.io/) -* 🌐 [DevOps Roadmap](https://roadmap.sh/devops) diff --git a/lectures/lec2.md b/lectures/lec2.md deleted file mode 100644 index 46a3e0485a..0000000000 --- a/lectures/lec2.md +++ /dev/null @@ -1,1053 +0,0 @@ -# 📌 Lecture 2 — Containerization with Docker: From "Works on My Machine" to Works Everywhere - -## 📍 Slide 1 – 🐳 Welcome to Containerization - -* 🌍 **"Works on my machine"** — the most expensive phrase in software -* 📦 **Containers** = package your app + all dependencies together -* 🚀 **Docker** = the tool that made containers mainstream -* 🎯 This lecture: build production-ready containers from scratch - -```mermaid -flowchart LR - Problem[😰 Works on My Machine] -->|Docker| Solution[🐳 Works Everywhere] - Solution --> Value[💎 Consistent Deployments] -``` - ---- - -## 📍 Slide 2 – 🎯 Learning Outcomes - -* ✅ Understand containers vs VMs and why containers win -* ✅ Write production-ready Dockerfiles -* ✅ Apply security best practices (rootless, distroless) -* ✅ Optimize images with multi-stage builds -* ✅ Publish images to Docker Hub - -**🎓 By the end of this lecture:** - -| # | 🎯 Outcome | -|---|-----------| -| 1 | 🧠 Explain container architecture and benefits | -| 2 | 📝 Write optimized, secure Dockerfiles | -| 3 | 🔐 Implement rootless containers | -| 4 | 📦 Use multi-stage builds for smaller images | -| 5 | 🚀 Push/pull images from Docker Hub | - ---- - -## 📍 Slide 3 – 📋 Lecture Overview - -* 📚 **Concepts + Diagrams** — how containers work -* 🛠️ **Dockerfile deep dive** — instructions and best practices -* 🔐 **Security patterns** — rootless and distroless -* 📦 **Optimization** — multi-stage builds -* 🌐 **Registry workflow** — Docker Hub - -**⏱️ Lecture Structure:** -``` -Section 0: Introduction → 📝 PRE Quiz -Section 1: The Dependency Problem -Section 2: Container Fundamentals -Section 3: Dockerfile Scenarios → 📝 MID Quiz -Section 4: Advanced Patterns -Section 5: Real World Usage -Section 6: Reflection → 📝 POST Quiz -``` - ---- - -## 📍 Slide 4 – ❓ The Big Question - -* 📊 **65%** of organizations use containers in production (2024) -* 🐳 **Docker Hub**: 14+ million images, 13+ billion pulls/month -* 💥 Yet most Dockerfiles have **security vulnerabilities** - -> 💬 *"Containers are the new deployment unit"* — Kelsey Hightower - -**🤔 Think about it:** -* Why do apps work locally but fail in production? -* What's inside a container that makes it portable? -* How small can a container image be? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L2_PRE - ---- - -## 📍 Slide 6 – 🔥 Section 1: The Dependency Problem - -* 👨‍💻 **Developer**: "It works on my machine!" -* ⚙️ **Ops**: "Well, we're not shipping your machine!" -* 🧩 **The real problem**: dependencies, versions, configurations -* 💥 **Result**: deployment failures, debugging nightmares - -```mermaid -flowchart LR - Dev[👨‍💻 Dev Machine] -->|Different| Prod[🌐 Production] - Dev -->|Python 3.11| V1[📦 Version] - Prod -->|Python 3.9| V2[📦 Version] - V1 -.->|💥 Conflict| V2 -``` - ---- - -## 📍 Slide 7 – 🧩 The Dependency Hell - -* 🐍 **Python version**: 3.9 vs 3.11 vs 3.12 -* 📚 **Library versions**: requests 2.28 vs 2.31 -* 🖥️ **OS differences**: Ubuntu vs Alpine vs macOS -* ⚙️ **System libraries**: OpenSSL, libffi, glibc - -```mermaid -flowchart TD - App[📱 Your App] --> Py[🐍 Python 3.11] - App --> Lib1[📚 Flask 2.3] - App --> Lib2[📚 Requests 2.31] - Py --> OS[🖥️ Ubuntu 22.04] - Lib1 --> SSL[🔐 OpenSSL 3.0] - OS --> Kernel[🧠 Linux Kernel] -``` - -> 🤔 **Think:** How many things can go wrong? - ---- - -## 📍 Slide 8 – 😱 The VM Solution (Heavy) - -* 🖥️ **Virtual Machines** = entire OS per application -* 💾 **Size**: 10-50 GB per VM -* ⏱️ **Boot time**: minutes -* 🔧 **Resource overhead**: hypervisor, guest OS kernel - -```mermaid -flowchart TD - subgraph VM1[🖥️ VM 1 - 15GB] - App1[📱 App] --> OS1[🖥️ Full OS] - OS1 --> Kernel1[🧠 Kernel] - end - subgraph VM2[🖥️ VM 2 - 15GB] - App2[📱 App] --> OS2[🖥️ Full OS] - OS2 --> Kernel2[🧠 Kernel] - end - VM1 --> Hyper[⚙️ Hypervisor] - VM2 --> Hyper - Hyper --> Host[🖥️ Host OS] -``` - -**😰 Problems:** -* 🐌 Slow to start -* 💸 Expensive (RAM, CPU, storage) -* 🔧 Hard to manage at scale - ---- - -## 📍 Slide 9 – 🐳 The Container Solution (Light) - -* 📦 **Containers** = isolated processes sharing host kernel -* 💾 **Size**: 5-500 MB typically -* ⏱️ **Start time**: milliseconds -* 🚀 **Density**: 10-100x more containers than VMs - -```mermaid -flowchart TD - subgraph Containers - C1[📦 Container 1 - 50MB] - C2[📦 Container 2 - 50MB] - C3[📦 Container 3 - 50MB] - end - C1 --> Docker[🐳 Docker Engine] - C2 --> Docker - C3 --> Docker - Docker --> Host[🖥️ Host OS + Kernel] -``` - -**🚀 Benefits:** -* ⚡ Start in milliseconds -* 💰 Efficient resource usage -* 📦 Portable across environments - ---- - -## 📍 Slide 10 – 💸 VMs vs Containers - -| 🔍 Aspect | 🖥️ Virtual Machine | 🐳 Container | -|-----------|-------------------|--------------| -| 💾 **Size** | 10-50 GB | 10-500 MB | -| ⏱️ **Boot Time** | Minutes | Milliseconds | -| 🧠 **Kernel** | Own kernel | Shared kernel | -| 🔒 **Isolation** | Strong (hardware) | Process-level | -| 📦 **Density** | 10-20 per host | 100s per host | -| 🎯 **Use Case** | Full OS needed | App deployment | - -**📈 Real Numbers:** -* 🖥️ **VM**: 1 app = ~2GB RAM overhead -* 🐳 **Container**: 1 app = ~50MB overhead -* 🚀 **Result**: 40x more efficient! - ---- - -## 📍 Slide 11 – 📜 History of Containerization - -* 🕰️ **1979**: `chroot` — change root directory (Unix V7) -* 🔒 **2000**: FreeBSD Jails — first true isolation -* 🐧 **2006**: cgroups — Google contributes to Linux kernel -* 📦 **2008**: LXC (Linux Containers) — combines namespaces + cgroups -* 🐳 **2013**: **Docker** — makes containers accessible to everyone -* ☸️ **2014**: Kubernetes — container orchestration at scale -* 📦 **2015**: OCI (Open Container Initiative) — standardization - -```mermaid -flowchart LR - Chroot[🕰️ 1979: chroot] --> Jails[🔒 2000: Jails] - Jails --> Cgroups[🐧 2006: cgroups] - Cgroups --> LXC[📦 2008: LXC] - LXC --> Docker[🐳 2013: Docker] - Docker --> K8s[☸️ 2014: K8s] -``` - -> 💡 Docker didn't invent containers — it made them **usable**. - ---- - -## 📍 Slide 12 – 🐧 Linux Kernel: Namespaces - -* 🎯 **Namespaces** = isolate what a process **can see** -* 🔒 Each container gets its own "view" of the system - -| 🏷️ Namespace | 🔒 Isolates | 📝 Example | -|--------------|------------|-----------| -| **PID** | Process IDs | Container sees PID 1 as its init | -| **NET** | Network stack | Own IP, ports, routing | -| **MNT** | Mount points | Own filesystem view | -| **UTS** | Hostname | Own hostname | -| **IPC** | Inter-process comm | Own message queues | -| **USER** | User/Group IDs | UID 0 in container ≠ root on host | - -```mermaid -flowchart TD - subgraph Host[🖥️ Host System] - subgraph NS1[📦 Container 1 Namespace] - P1[PID 1: app] - Net1[eth0: 172.17.0.2] - end - subgraph NS2[📦 Container 2 Namespace] - P2[PID 1: app] - Net2[eth0: 172.17.0.3] - end - end -``` - ---- - -## 📍 Slide 13 – 🎛️ Linux Kernel: cgroups - -* 🎯 **cgroups** (Control Groups) = limit what a process **can use** -* 📊 Resource limits prevent one container from killing the host - -| 🎛️ cgroup | 🔧 Controls | 📝 Example | -|-----------|------------|-----------| -| **cpu** | CPU time | Max 50% of one core | -| **memory** | RAM usage | Max 512MB | -| **blkio** | Disk I/O | Max 100MB/s read | -| **pids** | Process count | Max 100 processes | - -```mermaid -flowchart LR - Container[🐳 Container] --> Cgroups[🎛️ cgroups] - Cgroups --> CPU[🖥️ CPU: 50%] - Cgroups --> RAM[💾 RAM: 512MB] - Cgroups --> IO[💿 I/O: 100MB/s] -``` - -**🛡️ Why it matters:** -* ✅ Prevent runaway processes -* ✅ Fair resource sharing -* ✅ Predictable performance - ---- - -## 📍 Slide 14 – 📂 Linux Kernel: Union Filesystems - -* 🎯 **Union FS** = layer multiple filesystems as one -* 📚 Docker uses **overlay2** (default on Linux) -* 💾 Layers are **read-only**, changes go to top layer - -```mermaid -flowchart TD - subgraph Image[📦 Image Layers - Read Only] - L1[🐧 Layer 1: Base OS] - L2[📦 Layer 2: Dependencies] - L3[📁 Layer 3: App Code] - end - subgraph Container[🏃 Container Layer - Read/Write] - L4[✏️ Layer 4: Runtime Changes] - end - L1 --> L2 --> L3 --> L4 -``` - -**💡 Benefits:** -* ✅ **Shared layers** — 10 containers can share base image -* ✅ **Fast startup** — no copying, just add thin layer -* ✅ **Efficient storage** — only differences stored - ---- - -## 📍 Slide 15 – 🧩 How It All Fits Together - -```mermaid -flowchart TD - subgraph Docker[🐳 Docker Engine] - CLI[🖥️ Docker CLI] - Daemon[⚙️ dockerd] - Containerd[📦 containerd] - Runc[🏃 runc] - end - subgraph Kernel[🐧 Linux Kernel] - NS[🔒 Namespaces] - CG[🎛️ cgroups] - UFS[📂 overlay2] - end - CLI --> Daemon --> Containerd --> Runc - Runc --> NS - Runc --> CG - Runc --> UFS -``` - -**🔧 The Stack:** -* 🖥️ **Docker CLI** — user interface -* ⚙️ **dockerd** — Docker daemon (API) -* 📦 **containerd** — container lifecycle management -* 🏃 **runc** — OCI runtime (creates containers) -* 🐧 **Kernel** — namespaces + cgroups + filesystem - ---- - -## 📍 Slide 16 – 💡 Section 2: Docker Fundamentals - -* 🐳 **Docker** = platform for building, shipping, running containers -* 📦 **Image** = blueprint (read-only template) -* 🏃 **Container** = running instance of an image -* 📝 **Dockerfile** = recipe to build an image - -```mermaid -flowchart LR - Dockerfile[📝 Dockerfile] -->|build| Image[📦 Image] - Image -->|run| Container[🏃 Container] - Image -->|push| Registry[🌐 Registry] - Registry -->|pull| Image2[📦 Image] -``` - -**📖 Definition:** -> *A container is a standard unit of software that packages code and all its dependencies so the application runs quickly and reliably across environments.* - ---- - -## 📍 Slide 17 – 🏗️ Docker Architecture - -* 🖥️ **Docker Client** = CLI commands (`docker build`, `docker run`) -* ⚙️ **Docker Daemon** = background service managing containers -* 📦 **Images** = layered filesystem snapshots -* 🌐 **Registry** = image storage (Docker Hub, ECR, GCR) - -```mermaid -flowchart LR - CLI[🖥️ Docker CLI] -->|API| Daemon[⚙️ Docker Daemon] - Daemon --> Images[📦 Images] - Daemon --> Containers[🏃 Containers] - Daemon <-->|push/pull| Registry[🌐 Registry] -``` - -**🔧 Key Commands:** -* 🔨 `docker build` — create image from Dockerfile -* 🏃 `docker run` — start container from image -* 📤 `docker push` — upload image to registry -* 📥 `docker pull` — download image from registry - ---- - -## 📍 Slide 18 – 📚 Image Layers - -* 🎂 **Images are layered** = each instruction creates a layer -* 💾 **Layers are cached** = faster rebuilds -* 🔄 **Layers are shared** = efficient storage -* 📝 **Order matters** = for cache efficiency - -```mermaid -flowchart TD - L1[🐧 Layer 1: Base OS - python:3.12-slim] - L2[📦 Layer 2: Install dependencies] - L3[📁 Layer 3: Copy application code] - L4[⚙️ Layer 4: Configure runtime] - L1 --> L2 --> L3 --> L4 - L4 --> Image[📦 Final Image] -``` - -**💡 Key Insight:** -* ✅ Change code → only Layer 3-4 rebuild -* ❌ Change base → ALL layers rebuild - ---- - -## 📍 Slide 19 – 📝 Dockerfile Basics - -```dockerfile -# 🐍 Start from base image -FROM python:3.12-slim - -# 📁 Set working directory -WORKDIR /app - -# 📦 Copy and install dependencies FIRST (caching!) -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -# 📁 Copy application code -COPY . . - -# 🚀 Define startup command -CMD ["python", "app.py"] -``` - -**📝 Key Instructions:** -| Instruction | 🎯 Purpose | -|-------------|-----------| -| `FROM` | 🐧 Base image | -| `WORKDIR` | 📁 Set directory | -| `COPY` | 📄 Copy files | -| `RUN` | ⚙️ Execute commands | -| `CMD` | 🚀 Default command | -| `EXPOSE` | 🔌 Document port | - ---- - -## 📍 Slide 20 – ⚡ Before vs After Docker - -| 😰 Before Docker | 🐳 After Docker | -|-----------------|-----------------| -| 📋 Manual server setup | 📝 Dockerfile defines everything | -| 🔧 "Install Python 3.11, then..." | 🐳 `FROM python:3.11` | -| 😱 "Works on my machine" | ✅ Works everywhere | -| 📅 Deploy monthly (scary) | 🚀 Deploy daily (confident) | -| 🐛 "Which version is prod?" | 📦 Image tag = version | -| 💀 Snowflake servers | 🐄 Immutable containers | - -> 🤔 Which column describes your current workflow? - ---- - -## 📍 Slide 21 – 🎮 Section 3: Dockerfile Scenarios - -## 🕹️ Lab Preview: Containerize Your App - -* 🏢 **Scenario**: You have a Python Flask app from Lab 1 -* 🎯 **Goal**: Package it in a production-ready container -* 📋 **Requirements**: Security, optimization, best practices - -**❓ What could go wrong?** - -> 💀 **A lot.** Let's see common mistakes and fixes. - -🎮 **Let's build it right.** - ---- - -## 📍 Slide 22 – 💥 Scenario 1: Running as Root - -**😰 The Problem:** -```dockerfile -FROM python:3.12 -COPY . /app -CMD ["python", "app.py"] -# 💀 Running as root by default! -``` - -* 🔓 Container runs as **root** (UID 0) -* 💥 If attacker escapes container → **root on host** -* 🚨 Kubernetes blocks root containers by default - -```mermaid -flowchart LR - Attack[🔓 Container Escape] --> Root[👑 Root Access] - Root --> Host[💀 Host Compromised] -``` - -> ❓ **Why is this dangerous?** - ---- - -## 📍 Slide 23 – ✅ Solution: Rootless Containers - -## 🛠️ Fix: Create Non-Root User - -```dockerfile -FROM python:3.12-slim - -# 👤 Create non-root user -RUN useradd --create-home --shell /bin/bash appuser - -WORKDIR /app -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt -COPY . . - -# 🔒 Switch to non-root user -USER appuser - -CMD ["python", "app.py"] -``` - -**🎯 Result:** Container runs as `appuser`, not root - -**🔐 Security Benefits:** -* ✅ Limited privileges inside container -* ✅ Can't modify system files -* ✅ Container escape = unprivileged user -* ✅ Kubernetes-compatible - ---- - -## 📍 Slide 24 – 🐌 Scenario 2: Slow Builds (Bad Layer Order) - -**😰 The Problem:** -```dockerfile -FROM python:3.12-slim -WORKDIR /app - -# ❌ Copy EVERYTHING first -COPY . . - -# 📦 Then install dependencies -RUN pip install -r requirements.txt - -CMD ["python", "app.py"] -``` - -* 🔄 **Any code change** → reinstall ALL dependencies -* ⏱️ Build time: **5 minutes** every time -* 💸 Wasted CI/CD minutes - -```mermaid -flowchart TD - Change[📝 Change 1 line of code] --> Copy[❌ COPY invalidated] - Copy --> Pip[❌ pip install runs again] - Pip --> Slow[🐌 5 min rebuild] -``` - ---- - -## 📍 Slide 25 – ✅ Solution: Optimized Layer Order - -## 🛠️ Fix: Dependencies Before Code - -```dockerfile -FROM python:3.12-slim -WORKDIR /app - -# 📦 Copy ONLY requirements first -COPY requirements.txt . - -# 📦 Install dependencies (cached if requirements unchanged) -RUN pip install --no-cache-dir -r requirements.txt - -# 📁 THEN copy application code -COPY . . - -CMD ["python", "app.py"] -``` - -**🎯 Result:** Change code → only last layer rebuilds - -```mermaid -flowchart TD - Change[📝 Change code] --> Skip1[✅ FROM cached] - Skip1 --> Skip2[✅ requirements cached] - Skip2 --> Skip3[✅ pip install cached] - Skip3 --> Rebuild[🔨 Only COPY . . rebuilds] - Rebuild --> Fast[⚡ 10 sec rebuild] -``` - -**⚡ Build time: 5 min → 10 sec** - ---- - -## 📍 Slide 26 – 📦 Scenario 3: Bloated Images - -**😰 The Problem:** -```dockerfile -FROM python:3.12 -# 💾 Full Python image = 1.0 GB! -``` - -* 💾 Image size: **1+ GB** -* 🐌 Slow to pull/push -* 💸 Storage costs -* 🔓 Larger attack surface - -**📊 Python Image Sizes:** -| Image | 💾 Size | -|-------|--------| -| `python:3.12` | 1.0 GB | -| `python:3.12-slim` | 150 MB | -| `python:3.12-alpine` | 50 MB | - -> 🤔 **Do you need the full image?** - ---- - -## 📍 Slide 27 – ✅ Solution: Slim Base Images - -## 🛠️ Fix: Use Minimal Base Images - -```dockerfile -# ✅ Use slim variant -FROM python:3.12-slim - -# ✅ No cache for pip (smaller image) -RUN pip install --no-cache-dir -r requirements.txt - -# ✅ Only copy what's needed -COPY app.py . -COPY templates/ templates/ -``` - -**🎯 Result:** 1 GB → 150 MB (85% reduction!) - -**📦 Base Image Guide:** -| Image Type | 🎯 Use Case | 💾 Size | -|------------|------------|--------| -| `python:3.12` | Need compilation tools | 1.0 GB | -| `python:3.12-slim` | Most apps (recommended) | 150 MB | -| `python:3.12-alpine` | Size-critical, simple apps | 50 MB | - -**⚠️ Alpine Warning:** Uses musl libc, may break some packages - ---- - -## 📍 Slide 28 – 📁 Scenario 4: No .dockerignore - -**😰 The Problem:** -```bash -# Build context includes EVERYTHING -Sending build context to Docker daemon 500MB -``` - -* 📁 `.git/` folder (100+ MB) -* 📁 `node_modules/` or `venv/` -* 📁 `__pycache__/` files -* 📄 `.env` with secrets! 💀 - -**💥 Consequences:** -* 🐌 Slow builds -* 💾 Bloated images -* 🔓 Secrets leaked into image - ---- - -## 📍 Slide 29 – ✅ Solution: .dockerignore - -## 🛠️ Fix: Exclude Unnecessary Files - -```dockerignore -# 🐙 Version control -.git -.gitignore - -# 🐍 Python -__pycache__ -*.pyc -*.pyo -venv/ -.venv/ - -# 🔐 Secrets (NEVER include!) -.env -*.pem -secrets/ - -# 📝 Documentation -*.md -docs/ - -# 🧪 Tests (if not needed in container) -tests/ -``` - -**🎯 Result:** -* ⚡ Build context: 500 MB → 5 MB -* 🔐 No secrets in image -* 🚀 Faster builds - ---- - -## 📍 Slide 30 – 📝 QUIZ — DEVOPS_L2_MID - ---- - -## 📍 Slide 31 – 🚀 Section 4: Advanced Patterns - -## 🏗️ Multi-Stage Builds - -* 🎯 **Problem**: Build tools bloat final image -* 💡 **Solution**: Separate build and runtime stages -* 📦 **Result**: Tiny production images - -```mermaid -flowchart LR - subgraph Stage1[🔨 Builder Stage] - SDK[📦 Full SDK] - Compile[⚙️ Compile] - end - subgraph Stage2[🚀 Runtime Stage] - Binary[📦 Binary Only] - Minimal[🐧 Minimal OS] - end - Stage1 -->|copy binary| Stage2 -``` - -**📊 Size Impact:** -* 🔨 Builder: 1+ GB (SDK, compilers) -* 🚀 Runtime: 10-50 MB (binary only) - ---- - -## 📍 Slide 32 – 📝 Multi-Stage Dockerfile - -```dockerfile -# 🔨 Stage 1: Builder -FROM golang:1.21 AS builder -WORKDIR /app -COPY go.mod go.sum ./ -RUN go mod download -COPY . . -RUN CGO_ENABLED=0 go build -o myapp - -# 🚀 Stage 2: Runtime -FROM alpine:3.18 -RUN adduser -D appuser -WORKDIR /app -COPY --from=builder /app/myapp . -USER appuser -CMD ["./myapp"] -``` - -**🔍 Key Points:** -* 🏷️ `AS builder` — name the stage -* 📦 `COPY --from=builder` — copy from previous stage -* 🗑️ Builder stage discarded in final image - -**📊 Result:** 1.2 GB → 15 MB - ---- - -## 📍 Slide 33 – 🔐 Distroless Images - -## 🛡️ Ultimate Minimal Images - -* 🚫 **No shell** — can't exec into container -* 🚫 **No package manager** — can't install malware -* 🚫 **No unnecessary files** — minimal attack surface -* ✅ **Only your app** — and runtime dependencies - -```dockerfile -# 🔨 Build stage -FROM golang:1.21 AS builder -WORKDIR /app -COPY . . -RUN CGO_ENABLED=0 go build -o myapp - -# 🔐 Distroless runtime -FROM gcr.io/distroless/static-debian12 -COPY --from=builder /app/myapp / -CMD ["/myapp"] -``` - -**📊 Distroless Options:** -| Image | 🎯 For | 💾 Size | -|-------|-------|--------| -| `distroless/static` | Go, Rust (static) | 2 MB | -| `distroless/base` | C/C++ apps | 20 MB | -| `distroless/python3` | Python apps | 50 MB | -| `distroless/java` | Java apps | 190 MB | - ---- - -## 📍 Slide 34 – 📊 Image Size Comparison - -## 📈 Same App, Different Images - -| 🏗️ Build Strategy | 💾 Image Size | 🔐 Security | -|-------------------|--------------|-------------| -| `FROM python:3.12` | 1.0 GB | 😰 Large attack surface | -| `FROM python:3.12-slim` | 150 MB | 😊 Better | -| Multi-stage + slim | 100 MB | 😄 Good | -| Multi-stage + alpine | 50 MB | 😄 Good | -| Multi-stage + distroless | 20 MB | 🔐 Excellent | -| `FROM scratch` (Go) | 5 MB | 🔐 Maximum | - -```mermaid -flowchart LR - Full[📦 1 GB] --> Slim[📦 150 MB] - Slim --> Multi[📦 50 MB] - Multi --> Distroless[📦 20 MB] - Distroless --> Scratch[📦 5 MB] -``` - -**🎯 Goal:** As small as possible while functional - ---- - -## 📍 Slide 35 – 🌐 Docker Hub & Registries - -## 📦 Publishing Your Images - -```mermaid -flowchart LR - Build[🔨 Build] --> Tag[🏷️ Tag] - Tag --> Push[📤 Push] - Push --> Registry[🌐 Docker Hub] - Registry --> Pull[📥 Pull] - Pull --> Run[🏃 Run] -``` - -**🔧 Workflow:** -```bash -# 🔨 Build image -docker build -t myapp:1.0 . - -# 🏷️ Tag for registry -docker tag myapp:1.0 username/myapp:1.0 - -# 🔐 Login to Docker Hub -docker login - -# 📤 Push to registry -docker push username/myapp:1.0 -``` - -**📦 Registries:** -* 🐳 Docker Hub — public/private -* ☁️ AWS ECR — AWS integrated -* 🌐 GCP GCR — Google integrated -* 🦊 GitLab Registry — GitLab integrated - ---- - -## 📍 Slide 36 – 🏢 Section 5: Real World Usage - -## 📅 Docker in Production - -**🔨 Build Phase:** -* 📝 Dockerfile in repo -* 🤖 CI builds image on every commit -* 🏷️ Tag with git SHA or semantic version -* 📤 Push to registry - -**🚀 Deploy Phase:** -* 📥 Pull image to servers -* 🏃 Run containers -* 📊 Monitor health -* 🔄 Rolling updates - -```mermaid -flowchart LR - Code[📝 Code] --> CI[🤖 CI Build] - CI --> Registry[🌐 Registry] - Registry --> K8s[☸️ Kubernetes] - K8s --> Prod[🌐 Production] -``` - ---- - -## 📍 Slide 37 – 🏷️ Tagging Strategies - -| 🏷️ Strategy | 📝 Example | 🎯 Use Case | -|-------------|-----------|-------------| -| **Semantic** | `myapp:1.2.3` | Releases | -| **Git SHA** | `myapp:a1b2c3d` | Traceability | -| **Branch** | `myapp:develop` | Dev environments | -| **Latest** | `myapp:latest` | ⚠️ Avoid in prod! | -| **Date** | `myapp:2024-01-15` | Daily builds | - -**⚠️ Never use `latest` in production:** -* 🤷 Which version is "latest"? -* 🔄 Changes without notice -* 🐛 Can't rollback reliably - -**✅ Best Practice:** -```bash -# 🏷️ Immutable tags -docker tag myapp:1.0.0 registry/myapp:1.0.0 -docker tag myapp:1.0.0 registry/myapp:sha-a1b2c3d -``` - ---- - -## 📍 Slide 38 – 🔐 Security Best Practices - -```mermaid -flowchart TD - Scan[🔍 Scan Images] --> Base[📦 Minimal Base] - Base --> User[👤 Non-root User] - User --> Secrets[🔐 No Secrets in Image] - Secrets --> Update[🔄 Update Regularly] - Update --> Sign[✍️ Sign Images] -``` - -**🔐 Security Checklist:** -* ✅ Run as non-root user (`USER appuser`) -* ✅ Use minimal base images (slim, distroless) -* ✅ Scan for vulnerabilities (Trivy, Snyk) -* ✅ Never store secrets in images -* ✅ Pin base image versions -* ✅ Update base images regularly - -**🛠️ Scanning Tools:** -* 🔍 **Trivy** — open source, fast -* 🔍 **Snyk** — developer-friendly -* 🔍 **Docker Scout** — built into Docker - ---- - -## 📍 Slide 39 – 📈 Career Skills - -```mermaid -flowchart LR - Docker[🐳 Docker Basics] --> Compose[📦 Docker Compose] - Compose --> K8s[☸️ Kubernetes] - K8s --> GitOps[🔄 GitOps] - GitOps --> Platform[🏗️ Platform Engineering] -``` - -**🛠️ Docker Skills Progression:** -* 🐳 **Level 1**: Write Dockerfiles, build/run containers -* 📦 **Level 2**: Multi-stage builds, optimization -* 🔐 **Level 3**: Security hardening, distroless -* 📊 **Level 4**: Registry management, scanning -* ☸️ **Level 5**: Container orchestration (K8s) - -**📊 Job Market (2024):** -* 🐳 Docker required in **80%** of DevOps jobs -* ☸️ Kubernetes in **65%** of container jobs -* 💰 Container skills = **+15-20%** salary - ---- - -## 📍 Slide 40 – 🌍 Real Company Examples - -**🎬 Netflix:** -* 🐳 Millions of containers daily -* 📦 Custom base images (hardened) -* 🔄 Immutable deployments - -**🛒 Shopify:** -* 🐳 Containerized entire platform -* ⚡ Deploy 80x/day -* 📦 Standardized Dockerfiles - -**🚗 Uber:** -* 🐳 4,000+ microservices in containers -* 🔐 Strict security policies -* 📊 Custom image scanning - -**📊 Common Patterns:** -* ✅ Standardized base images -* ✅ Automated security scanning -* ✅ Multi-stage builds everywhere -* ✅ No root containers - ---- - -## 📍 Slide 41 – 🎯 Section 6: Reflection - -## 📝 Key Takeaways - -1. 🐳 **Containers = lightweight, portable app packaging** -2. 📝 **Dockerfile order matters** — dependencies before code -3. 👤 **Always run as non-root** — security first -4. 🏗️ **Multi-stage builds** — separate build from runtime -5. 📦 **Smaller is better** — less attack surface, faster deploys - -> 💡 A good Dockerfile is secure, optimized, and maintainable. - ---- - -## 📍 Slide 42 – 🧠 The Mindset Shift - -| 😰 Old Mindset | 🐳 Container Mindset | -|---------------|---------------------| -| 🖥️ "Configure servers manually" | 📝 "Define in Dockerfile" | -| 🔧 "Install dependencies on host" | 📦 "Bundle in container" | -| 👑 "Run as root, it's easier" | 👤 "Run as non-root always" | -| 💾 "Bigger image = more features" | ⚡ "Smaller = faster & safer" | -| 🏷️ "Just use :latest" | 🔖 "Pin versions always" | - -> ❓ Which mindset will you adopt? - ---- - -## 📍 Slide 43 – ✅ Your Progress - -## 🎓 What You Now Understand - -* ✅ Why containers beat VMs for app deployment -* ✅ Docker architecture: images, containers, registries -* ✅ How to write optimized Dockerfiles -* ✅ Security: rootless containers, minimal images -* ✅ Multi-stage builds for smaller images -* ✅ Docker Hub publishing workflow - -> 🚀 **You're ready for Lab 2!** - ---- - -## 📍 Slide 44 – 📝 QUIZ — DEVOPS_L2_POST - ---- - -## 📍 Slide 45 – 🚀 What Comes Next - -## 📚 Lab 2: Containerize Your App - -* 🐳 Write Dockerfile for your Python app -* 👤 Implement non-root user -* 📦 Optimize with layer ordering -* 🌐 Push to Docker Hub -* 🏆 Bonus: Multi-stage build for Go app - -**🔮 Future Lectures:** -* 📦 **Lecture 3**: CI/CD with GitHub Actions -* ☸️ **Lecture 9**: Kubernetes deployment -* 🔄 **Lecture 13**: GitOps with ArgoCD - -```mermaid -flowchart LR - You[👤 You] --> Docker[🐳 Docker Skills] - Docker --> K8s[☸️ Kubernetes] - K8s --> GitOps[🔄 GitOps] - GitOps --> Career[🚀 DevOps Career] -``` - -**👋 See you in the lab!** - ---- - -## 📚 Resources & Further Reading - -**📕 Books:** -* 📖 *Docker Deep Dive* — Nigel Poulton -* 📖 *Container Security* — Liz Rice -* 📖 *Docker in Action* — Jeff Nickoloff - -**🔗 Links:** -* 🌐 [Dockerfile Best Practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) -* 🌐 [Distroless Images](https://github.com/GoogleContainerTools/distroless) -* 🌐 [Docker Security](https://docs.docker.com/engine/security/) -* 🌐 [Multi-Stage Builds](https://docs.docker.com/build/building/multi-stage/) - -**🛠️ Tools:** -* 🔍 [Hadolint](https://github.com/hadolint/hadolint) — Dockerfile linter -* 🔍 [Dive](https://github.com/wagoodman/dive) — Explore image layers -* 🔍 [Trivy](https://github.com/aquasecurity/trivy) — Security scanner - ---- diff --git a/lectures/lec3.md b/lectures/lec3.md deleted file mode 100644 index 9afebb8b15..0000000000 --- a/lectures/lec3.md +++ /dev/null @@ -1,978 +0,0 @@ -# 📌 Lecture 3 — Continuous Integration: Automate Testing & Build Confidence - -## 📍 Slide 1 – 🤖 Welcome to CI/CD - -* 🐛 **Manual testing** = slow, error-prone, doesn't scale -* 🤖 **Continuous Integration** = automate testing, building, and validation -* ✅ **Goal**: Catch bugs before they reach production -* 🚀 This lecture: Build your first CI/CD pipeline with GitHub Actions - -```mermaid -flowchart LR - Manual[😰 Manual Testing] -->|CI/CD| Auto[🤖 Automated Pipeline] - Auto --> Confidence[💪 Deploy with Confidence] -``` - ---- - -## 📍 Slide 2 – 🎯 Learning Outcomes - -* ✅ Understand CI/CD principles and benefits -* ✅ Write effective unit tests -* ✅ Build GitHub Actions workflows -* ✅ Implement security scanning with Snyk -* ✅ Apply CI/CD best practices (caching, versioning) - -**🎓 By the end of this lecture:** - -| # | 🎯 Outcome | -|---|-----------| -| 1 | 🧠 Explain CI/CD and why it matters | -| 2 | 🧪 Write meaningful unit tests | -| 3 | ⚙️ Create GitHub Actions workflows | -| 4 | 🔐 Integrate security scanning | -| 5 | 📦 Automate Docker builds & publishing | - ---- - -## 📍 Slide 3 – 📋 Lecture Overview - -* 📚 **CI/CD fundamentals** — what, why, how -* 🧪 **Testing strategies** — unit, integration, coverage -* ⚙️ **GitHub Actions** — YAML workflows, actions marketplace -* 🔐 **Security** — Snyk vulnerability scanning -* 🚀 **Automation** — Docker builds, versioning, caching - -**⏱️ Lecture Structure:** -``` -Section 0: Introduction → 📝 PRE Quiz -Section 1: The Testing Problem -Section 2: CI/CD Fundamentals -Section 3: GitHub Actions Hands-on → 📝 MID Quiz -Section 4: Advanced CI Patterns -Section 5: Production Practices -Section 6: Reflection → 📝 POST Quiz -``` - ---- - -## 📍 Slide 4 – ❓ The Big Question - -* 📊 **85%** of software bugs are found in production (2024) -* ⏱️ Average cost to fix a prod bug: **100x** more than dev bug -* 🚀 Teams with good CI deploy **46x** more frequently - -> 💬 *"If it hurts, do it more often"* — Continuous Delivery principle - -**🤔 Think about it:** -* How do you know your code works before deploying? -* What happens when someone breaks the main branch? -* How many bugs could be caught automatically? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L3_PRE - ---- - -## 📍 Slide 6 – 🔥 Section 1: The Testing Problem - -* 👨‍💻 **Developer**: "It works on my machine!" -* 🐛 **Production**: 500 errors, users complaining -* 😰 **The gap**: No automated testing or validation -* 💥 **Result**: Bugs slip through, confidence is low - -```mermaid -flowchart LR - Dev[👨‍💻 Dev: Works!] -->|No Tests| Prod[🌐 Production] - Prod --> Bug[🐛 Bug Found] - Bug --> Fire[🔥 Firefighting] -``` - ---- - -## 📍 Slide 7 – 🧪 Manual Testing Hell - -* 📋 **Manual checklist**: 50 steps to test before deploy -* ⏱️ **Time**: 2 hours per test cycle -* 😴 **Human error**: Forgot to test one endpoint -* 🔄 **Frequency**: Only before big releases (too painful) - -```mermaid -flowchart TD - Code[📝 Write Code] --> Manual[📋 Manual Testing] - Manual --> Bug[🐛 Found Bug] - Bug --> Fix[🔧 Fix Code] - Fix --> Manual - Manual --> Deploy[😮‍💨 Finally Deploy] -``` - -**😰 Problems:** -* 🐌 Slow feedback loop -* 🎰 Testing is inconsistent -* 🧠 Requires human to remember all steps -* 💀 Nobody wants to test - ---- - -## 📍 Slide 8 – 💥 The Integration Problem - -* 👥 **Multiple developers** pushing to main branch -* 🔀 **Merge conflicts** caught too late -* 💥 **Breaking changes** not detected -* 🤷 **"Who broke the build?"** — the blame game - -```mermaid -flowchart LR - Dev1[👨‍💻 Dev 1] -->|Push| Main[🌳 Main Branch] - Dev2[👩‍💻 Dev 2] -->|Push| Main - Dev3[👨‍💻 Dev 3] -->|Push| Main - Main --> Break[💥 Build Broken] -``` - -> 🤔 **Think:** How do we prevent this? - ---- - -## 📍 Slide 9 – 🔐 The Security Gap - -* 📦 **Dependencies** with known vulnerabilities -* 🔓 **Secrets** accidentally committed -* 🚨 **CVEs** discovered after deployment -* 🤷 **Nobody checked** before merging - -**📊 Real Stats:** -* 🔍 **84%** of codebases have vulnerable dependencies -* ⏱️ Average time to detect vulnerability: **54 days** -* 💰 Average breach cost: **$4.45 million** - ---- - -## 📍 Slide 10 – 💸 The Cost of No CI - -| 🔥 Problem | 💥 Impact | -|------------|-----------| -| 🐛 Bugs in production | Customer churn, reputation damage | -| ⏱️ Slow feedback | Wasted development time | -| 😰 Fear of deployment | Infrequent releases | -| 🔒 Security vulnerabilities | Data breaches, compliance issues | - -**📈 Real Numbers:** -* 🐛 Prod bug fix cost: **$10,000 - $100,000** -* 🕒 Time to detect + fix: **4-8 hours** -* 🏢 Without CI: Deploy **monthly** -* 🚀 With CI: Deploy **daily** - ---- - -## 📍 Slide 11 – 💡 Section 2: CI/CD Fundamentals - -* 🤖 **Continuous Integration (CI)** = automatically test every change -* 🚀 **Continuous Delivery (CD)** = always ready to deploy -* 📦 **Continuous Deployment** = automatically deploy to production -* 🎯 **Goal**: Fast, reliable, automated software delivery - -```mermaid -flowchart LR - CI[🤖 CI: Test] --> CD[📦 CD: Package] - CD --> Deploy[🚀 Deploy] -``` - -**📖 Definitions:** -> *CI: Developers integrate code into shared repository frequently. Each integration is verified by automated build and tests.* -> *CD: Software can be released to production at any time.* - ---- - -## 📍 Slide 12 – 🔄 The CI/CD Pipeline - -```mermaid -flowchart LR - Commit[📝 Commit] --> Trigger[⚡ Trigger CI] - Trigger --> Checkout[📥 Checkout Code] - Checkout --> Build[🔨 Build] - Build --> Test[🧪 Test] - Test --> Lint[🔍 Lint] - Lint --> Scan[🔐 Security Scan] - Scan --> Package[📦 Package] - Package --> Publish[🚀 Publish] -``` - -**🔧 Stages:** -1. 📝 **Commit** — Developer pushes code -2. ⚡ **Trigger** — CI system detects change -3. 🔨 **Build** — Compile/prepare code -4. 🧪 **Test** — Run automated tests -5. 🔍 **Lint** — Check code quality -6. 🔐 **Scan** — Security vulnerabilities -7. 📦 **Package** — Build artifacts (Docker image) -8. 🚀 **Publish** — Push to registry - ---- - -## 📍 Slide 13 – ✅ CI/CD Benefits - -| 🎯 Benefit | 📊 Impact | -|-----------|----------| -| ⚡ **Fast Feedback** | Know in 5 min if code works | -| 🐛 **Early Bug Detection** | Catch before production | -| 🔒 **Security** | Automated vulnerability scanning | -| 📦 **Consistent Builds** | Same process every time | -| 💪 **Confidence** | Deploy without fear | -| 🚀 **Faster Releases** | Deploy multiple times per day | - -**📈 DORA Metrics (Elite Performers):** -* 📦 Deploy frequency: **Multiple times/day** -* ⏱️ Lead time: **< 1 hour** -* 🔧 MTTR: **< 1 hour** -* ❌ Change failure rate: **< 15%** - ---- - -## 📍 Slide 14 – 🧪 Testing Pyramid - -```mermaid -flowchart TD - subgraph Pyramid[🔺 Testing Pyramid] - E2E[🌐 E2E Tests
Few, Slow, Expensive] - INT[🔗 Integration Tests
Some, Moderate] - UNIT[🧪 Unit Tests
Many, Fast, Cheap] - end - E2E --> INT --> UNIT -``` - -**🎯 Test Types:** -* 🧪 **Unit Tests** (80%) — Test individual functions -* 🔗 **Integration Tests** (15%) — Test components together -* 🌐 **End-to-End Tests** (5%) — Test full user flows - -**💡 Why the pyramid?** -* ✅ Unit tests: Fast (ms), cheap, catch most bugs -* ✅ Integration: Slower (seconds), catch interface bugs -* ⚠️ E2E: Slowest (minutes), brittle, expensive - ---- - -## 📍 Slide 15 – ⚡ Before vs After CI/CD - -| 😰 Before CI/CD | 🚀 After CI/CD | -|-----------------|----------------| -| 📋 Manual testing checklist | 🤖 Automated test suite | -| 🎰 "Fingers crossed" deploys | ✅ Confident deployments | -| 🐛 Bugs found in production | 🧪 Bugs caught in CI | -| ⏱️ 2 hour test cycle | ⚡ 5 minute feedback | -| 😱 Deploy monthly | 🚀 Deploy daily | -| 🤷 "Who broke it?" | 📊 Git bisect + logs | - -> 🤔 Which column is your current process? - ---- - -## 📍 Slide 16 – 🎮 Section 3: GitHub Actions Hands-On - -## 🕹️ Lab Preview: Build Your CI Pipeline - -* 🏢 **Scenario**: You have a Python Flask app -* 🎯 **Goal**: Automate testing and Docker builds -* 📋 **Requirements**: Tests, lint, security scan, publish - -**❓ How do we automate all this?** - -> 🤖 **GitHub Actions** to the rescue! - -🎮 **Let's build it step by step.** - ---- - -## 📍 Slide 17 – 💥 Scenario 1: No Tests - -**😰 The Problem:** -```python -# app.py -@app.route('/') -def home(): - return {"message": "Hello", "hostname": os.getenv("HOSTNAME")} - -# 🚫 No tests! -``` - -* 📝 Code looks fine -* 💥 Deploy → crashes because `HOSTNAME` is None -* 🐛 Users see 500 errors -* 😱 Rollback emergency - -> ❓ **How do we catch this before deploy?** - ---- - -## 📍 Slide 18 – ✅ Solution: Unit Testing - -## 🛠️ Fix: Write Tests First - -```python -# tests/test_app.py -import pytest -from app import app - -def test_home_endpoint(): - """Test that home returns expected structure""" - client = app.test_client() - response = client.get('/') - - assert response.status_code == 200 - data = response.get_json() - assert "message" in data - assert "hostname" in data - assert isinstance(data["message"], str) - -def test_health_endpoint(): - """Test health check""" - client = app.test_client() - response = client.get('/health') - - assert response.status_code == 200 - assert response.get_json()["status"] == "healthy" -``` - -**🎯 Result:** Tests catch the bug before deploy! - ---- - -## 📍 Slide 19 – 🧪 Testing Frameworks - -## Python Testing Options - -| Framework | 🎯 Pros | ⚠️ Cons | -|-----------|--------|--------| -| **pytest** | Simple, powerful, fixtures | Extra dependency | -| **unittest** | Built-in, no dependencies | Verbose, old-style | - -```bash -# 🧪 pytest (recommended) -pip install pytest -pytest tests/ - -# 🧪 unittest (built-in) -python -m unittest discover tests/ -``` - -**💡 Why pytest?** -* ✅ Simple syntax (`assert` instead of `self.assertEqual`) -* ✅ Powerful fixtures (setup/teardown) -* ✅ Great plugins (coverage, parallel, etc.) -* ✅ Industry standard - ---- - -## 📍 Slide 20 – 📝 Scenario 2: Manual Docker Builds - -**😰 The Problem:** -```bash -# 🐌 Manual process every time -docker build -t myapp:latest . -docker tag myapp:latest username/myapp:v1.2.3 -docker login -docker push username/myapp:v1.2.3 -docker push username/myapp:latest - -# 😱 Forgot to update version tag! -# 💀 Built from wrong branch! -``` - -* ⏱️ Takes 10 minutes -* 🎰 Inconsistent (human error) -* 📋 No validation before build -* 🤷 Can't track what version is deployed - ---- - -## 📍 Slide 21 – ✅ Solution: GitHub Actions CI/CD - -## 🛠️ Fix: Automate Everything - -```yaml -# .github/workflows/python-ci.yml -name: Python CI - -on: - push: - branches: [ main ] - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - pip install -r requirements.txt - pip install pytest flake8 - - - name: Lint - run: flake8 app.py - - - name: Test - run: pytest tests/ -``` - -**🎯 Result:** Every commit automatically tested! - ---- - -## 📍 Slide 22 – 🐳 Docker Build Automation - -```yaml - docker: - needs: test # ✅ Only run if tests pass - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: ./app_python - push: true - tags: | - ${{ secrets.DOCKER_USERNAME }}/myapp:latest - ${{ secrets.DOCKER_USERNAME }}/myapp:${{ github.sha }} -``` - -**🔐 Security Note:** Never hardcode credentials! - ---- - -## 📍 Slide 23 – 🔓 Scenario 3: Vulnerable Dependencies - -**😰 The Problem:** -```bash -# requirements.txt -flask==2.0.1 # 💀 Known CVE-2023-30861 -requests==2.25.0 # 🔓 Security vulnerability - -# 🤷 Nobody checked before deploying -``` - -* 🔍 **84%** of apps have vulnerable dependencies -* ⏱️ Takes **weeks** to discover -* 💀 Already in production when found - -**📊 Real Example:** -* 📦 Log4Shell (2021) — **35,000+ CVE** -* 💰 Cost to remediate: **Billions of dollars** - ---- - -## 📍 Slide 24 – ✅ Solution: Snyk Security Scanning - -## 🛠️ Fix: Automated Vulnerability Scanning - -```yaml - security: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Run Snyk - uses: snyk/actions/python-3.10@master - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - with: - args: --severity-threshold=high -``` - -**🎯 What Snyk Does:** -* 🔍 Scans dependencies for known CVEs -* 📊 Reports severity (low/medium/high/critical) -* 🔧 Suggests fixes -* ❌ Fails build if critical vulnerabilities found - -**🔐 Result:** Catch vulnerabilities before production! - ---- - -## 📍 Slide 25 – 🐌 Scenario 4: Slow CI Builds - -**😰 The Problem:** -``` -[Run 1] Installing dependencies... 2 minutes -[Run 2] Installing dependencies... 2 minutes -[Run 3] Installing dependencies... 2 minutes -# 💸 Wasting 6 minutes downloading same packages! -``` - -* ⏱️ Each run: **5-10 minutes** -* 🔄 Re-downloading same dependencies -* 💰 Wasting CI minutes (costs money!) -* 😴 Slow feedback loop - ---- - -## 📍 Slide 26 – ✅ Solution: Dependency Caching - -## 🛠️ Fix: Cache Dependencies - -```yaml -- uses: actions/setup-python@v5 - with: - python-version: '3.12' - cache: 'pip' # ✅ Enable caching - cache-dependency-path: 'requirements.txt' - -# Alternative with explicit cache -- uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} -``` - -**📊 Performance Impact:** -* ⏱️ **Before caching**: 5 minutes -* ⚡ **After caching**: 30 seconds -* 🚀 **10x faster!** - -**💡 Cache Key Strategy:** -* 🔑 Key includes `requirements.txt` hash -* 🔄 Cache invalidates when dependencies change -* ✅ Fresh install when needed, cached otherwise - ---- - -## 📍 Slide 27 – 📝 QUIZ — DEVOPS_L3_MID - ---- - -## 📍 Slide 28 – 🏷️ Section 4: Versioning Strategies - -## 📦 How to Version Your Images? - -**Two main approaches:** - -**🔢 Semantic Versioning (SemVer):** -* Format: `MAJOR.MINOR.PATCH` (e.g., `1.2.3`) -* 🎯 Use when: Breaking changes matter -* 📚 Example: Libraries, APIs - -**📅 Calendar Versioning (CalVer):** -* Format: `YYYY.MM.DD` (e.g., `2024.01.15`) -* 🎯 Use when: Continuous deployment -* 🚀 Example: Web services, SaaS - -```mermaid -flowchart LR - Code[📝 Code] --> SemVer[🔢 v1.2.3] - Code --> CalVer[📅 2024.01] - SemVer --> Lib[📚 Library] - CalVer --> Service[🌐 Service] -``` - ---- - -## 📍 Slide 29 – 🔢 Semantic Versioning (SemVer) - -## v MAJOR.MINOR.PATCH - -| Version | 🎯 When to Bump | -|---------|----------------| -| **MAJOR** (v2.0.0) | Breaking changes (API changed) | -| **MINOR** (v1.1.0) | New features (backward-compatible) | -| **PATCH** (v1.0.1) | Bug fixes (backward-compatible) | - -```yaml -# 🏷️ Multiple tags per release -tags: | - username/app:1.2.3 - username/app:1.2 - username/app:1 - username/app:latest -``` - -**✅ Pros:** -* 📖 Clear breaking change signals -* 🎯 Industry standard for libraries -* 🔄 Users can pin to major version - -**⚠️ Cons:** -* 🤔 Requires discipline -* 📋 Need to track what's breaking vs feature - ---- - -## 📍 Slide 30 – 📅 Calendar Versioning (CalVer) - -## YYYY.MM.DD or YYYY.MM - -| Format | 📝 Example | 🎯 Use Case | -|--------|-----------|-------------| -| `YYYY.MM.DD` | `2024.01.15` | Daily releases | -| `YYYY.MM.MICRO` | `2024.01.3` | Monthly + patch | -| `YYYY.0M` | `2024.01` | Monthly releases | - -```yaml -# 📅 Generate version from date -- name: Generate version - run: echo "VERSION=$(date +%Y.%m.%d)" >> $GITHUB_ENV - -tags: | - username/app:2024.01.15 - username/app:2024.01 - username/app:latest -``` - -**✅ Pros:** -* 📆 No ambiguity (date is date) -* 🚀 Perfect for continuous deployment -* 🧠 Easy to remember - -**⚠️ Cons:** -* 🤷 Doesn't indicate breaking changes - ---- - -## 📍 Slide 31 – 🔀 Matrix Builds - -## Test Multiple Versions - -```yaml -jobs: - test: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['3.11', '3.12', '3.13'] - - steps: - - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - run: pytest tests/ -``` - -**🎯 What This Does:** -* 🔄 Runs tests **3 times** (one per Python version) -* ⚡ Runs in **parallel** -* ✅ Ensures compatibility across versions - -```mermaid -flowchart LR - Test[🧪 Tests] --> Py311[🐍 Python 3.11] - Test --> Py312[🐍 Python 3.12] - Test --> Py313[🐍 Python 3.13] -``` - ---- - -## 📍 Slide 32 – 📂 Path Filters (Monorepo) - -## Only Run CI for Changed Apps - -```yaml -# Python CI only runs when Python code changes -on: - push: - paths: - - 'app_python/**' - - '.github/workflows/python-ci.yml' - -# Go CI only runs when Go code changes -on: - push: - paths: - - 'app_go/**' - - '.github/workflows/go-ci.yml' -``` - -**🎯 Benefits:** -* ⚡ Faster CI (don't run unnecessary builds) -* 💰 Save CI minutes -* 🔕 Less noise (only relevant notifications) - -**📊 Impact:** -* 🐌 Without filters: Every commit runs **all** CI -* 🚀 With filters: Only **affected** apps run - ---- - -## 📍 Slide 33 – 📊 Test Coverage - -## Measure What's Tested - -```yaml -- name: Run tests with coverage - run: | - pip install pytest-cov - pytest --cov=app_python --cov-report=xml --cov-report=term - -- name: Upload to Codecov - uses: codecov/codecov-action@v4 - with: - file: ./coverage.xml -``` - -**📊 Coverage Badge:** -```markdown -![Coverage](https://codecov.io/gh/user/repo/branch/main/graph/badge.svg) -``` - -**🎯 What's Good Coverage?** -* 🥉 **60-70%** — Okay, could be better -* 🥈 **70-85%** — Good, most code tested -* 🥇 **85-95%** — Excellent coverage -* ⚠️ **100%** — Usually overkill (diminishing returns) - ---- - -## 📍 Slide 34 – ✅ CI Best Practices - -| 🎯 Practice | 💡 Why It Matters | -|------------|------------------| -| ⚡ **Fail Fast** | Stop on first failure, save time | -| 🔗 **Job Dependencies** | Don't push if tests fail | -| 🔒 **Secrets in Vault** | Never hardcode credentials | -| 📦 **Cache Dependencies** | 10x faster builds | -| 🔍 **Security Scanning** | Catch CVEs early | -| 📊 **Status Badges** | Visibility into health | -| 🎯 **Branch Protection** | Require CI before merge | -| ♻️ **Concurrency Control** | Cancel outdated runs | - -**🔐 Security:** -* ✅ Use `secrets.*` for sensitive data -* ✅ Minimum permissions (`permissions:`) -* ✅ Pin action versions (`actions/checkout@v4`) - ---- - -## 📍 Slide 35 – 🌐 GitHub Actions Marketplace - -## Reusable Actions - -```mermaid -flowchart LR - Marketplace[🏪 Actions Marketplace] --> Setup[⚙️ Setup Actions] - Marketplace --> Build[🔨 Build Actions] - Marketplace --> Deploy[🚀 Deploy Actions] - Marketplace --> Security[🔐 Security Actions] -``` - -**🔥 Popular Actions:** -* ⚙️ `actions/checkout@v4` — Clone repo -* 🐍 `actions/setup-python@v5` — Setup Python -* 🐳 `docker/build-push-action@v5` — Build Docker -* 🔐 `snyk/actions@master` — Security scan -* 📊 `codecov/codecov-action@v4` — Coverage - -**🔍 Find Actions:** -* 🌐 [github.com/marketplace](https://github.com/marketplace?type=actions) -* ⭐ Check stars/downloads -* 📖 Read documentation -* 🔒 Verify source/security - ---- - -## 📍 Slide 36 – 🏢 Section 5: Production CI/CD - -## Real-World CI Workflows - -**🎬 Netflix:** -* 🚀 **3000+** builds per day -* 🔄 Full CI pipeline in **<10 minutes** -* 🎯 A/B test deployments - -**🛒 Shopify:** -* ⚡ Deploy **80+ times per day** -* 🤖 Auto-rollback on failure -* 📊 Real-time metrics in CI - -**🔍 Google:** -* 🏗️ **Monorepo** with 2 billion LOC -* 🧪 **100+ million tests** daily -* 📦 Bazel build system - ---- - -## 📍 Slide 37 – 🚦 Branch Protection Rules - -## Require CI Before Merge - -```mermaid -flowchart LR - PR[📝 Pull Request] --> CI[🤖 CI Runs] - CI -->|✅ Pass| Merge[✅ Can Merge] - CI -->|❌ Fail| Block[🚫 Blocked] -``` - -**⚙️ GitHub Settings:** -* ✅ Require status checks to pass -* ✅ Require branches to be up to date -* ✅ Require review from code owners -* 🔒 Prevent direct push to main - -**🎯 Result:** -* 🚫 No broken code in main branch -* ✅ Every change is tested -* 📊 Full history of CI results - ---- - -## 📍 Slide 38 – 🔄 GitOps Preview - -## From CI to CD - -```mermaid -flowchart LR - CI[🤖 CI: Test & Build] --> Push[📦 Push Image] - Push --> ArgoCD[🔄 ArgoCD Detects] - ArgoCD --> Deploy[🚀 Auto Deploy] - Deploy --> K8s[☸️ Kubernetes] -``` - -**🔮 Coming Up:** -* 📦 **Lab 13**: ArgoCD deploys what CI builds -* ☸️ **K8s**: Orchestrate containers -* 🔄 **GitOps**: Git as source of truth -* 🚀 **Full automation**: Commit → Production - ---- - -## 📍 Slide 39 – 💡 CI/CD Anti-Patterns - -| ❌ Anti-Pattern | ✅ Better Approach | -|----------------|-------------------| -| 🎰 "It works on my machine" | 🧪 Automated tests catch issues | -| 📋 Manual deployment checklist | 🤖 Automated pipeline | -| 🤷 No tests, just deploy | 🧪 Comprehensive test suite | -| 💀 Long-lived feature branches | 🔄 Trunk-based development | -| 🐌 Slow CI (>30 min) | ⚡ Optimize, parallelize, cache | -| 🔓 Secrets in code | 🔒 Environment variables | - ---- - -## 📍 Slide 40 – 📈 CI Metrics to Track - -| 📊 Metric | 🎯 Target | -|-----------|----------| -| ⏱️ **Build Time** | < 10 minutes | -| ✅ **Success Rate** | > 95% | -| 🐛 **Bugs Caught in CI** | Maximize | -| 📦 **Deploy Frequency** | Multiple/day | -| 🔧 **Time to Fix Broken Build** | < 10 minutes | -| 📊 **Test Coverage** | > 80% | - -```mermaid -flowchart LR - Fast[⚡ Fast CI] --> Deploy[🚀 Deploy Often] - Deploy --> Confidence[💪 High Confidence] - Confidence --> Fast -``` - ---- - -## 📍 Slide 41 – 🎯 Section 6: Reflection - -## 📝 Key Takeaways - -1. 🤖 **CI automates testing** — catch bugs before production -2. 🧪 **Unit tests are essential** — fast feedback loop -3. ⚙️ **GitHub Actions** — powerful, free CI/CD platform -4. 🔐 **Security scanning** — integrate Snyk, scan dependencies -5. 📦 **Versioning matters** — SemVer or CalVer, be consistent - -> 💡 CI isn't just about automation — it's about building confidence. - ---- - -## 📍 Slide 42 – 🧠 The Mindset Shift - -| 😰 Old Mindset | 🚀 CI/CD Mindset | -|---------------|------------------| -| 📋 "Test before release" | 🧪 "Test every commit" | -| 🤞 "Hope it works" | ✅ "Know it works" | -| 🎰 Manual deployments | 🤖 Automated pipelines | -| 😱 "Who broke it?" | 📊 "CI caught it" | -| 🐌 Deploy monthly | 🚀 Deploy daily | -| 🔍 Find bugs in prod | 🧪 Catch bugs in CI | - -> ❓ Which mindset will you adopt? - ---- - -## 📍 Slide 43 – ✅ Your Progress - -## 🎓 What You Now Understand - -* ✅ Why CI/CD is critical for modern development -* ✅ How to write effective unit tests -* ✅ GitHub Actions workflow syntax -* ✅ Security scanning with Snyk -* ✅ Versioning strategies (SemVer vs CalVer) -* ✅ CI best practices (caching, matrix builds, path filters) - -> 🚀 **You're ready for Lab 3!** - ---- - -## 📍 Slide 44 – 📝 QUIZ — DEVOPS_L3_POST - ---- - -## 📍 Slide 45 – 🚀 What Comes Next - -## 📚 Lab 3: Build Your CI Pipeline - -* 🧪 Write unit tests for your Flask app -* ⚙️ Create GitHub Actions workflow -* 🔐 Integrate Snyk security scanning -* 📦 Automate Docker builds and versioning -* ⚡ Apply caching and best practices -* 🏆 Bonus: Multi-app CI with path filters - -**🔮 Future Lectures:** -* 📦 **Lecture 7**: Monitoring & Observability -* ☸️ **Lecture 9**: Kubernetes Deployment -* 🔄 **Lecture 13**: GitOps with ArgoCD - -```mermaid -flowchart LR - You[👤 You] --> Tests[🧪 Write Tests] - Tests --> CI[🤖 GitHub Actions] - CI --> Automation[⚡ Full Automation] - Automation --> Career[🚀 DevOps Skills] -``` - -**👋 See you in the lab!** - ---- - -## 📚 Resources & Further Reading - -**📕 Books:** -* 📖 *Continuous Delivery* — Jez Humble -* 📖 *The DevOps Handbook* — Gene Kim -* 📖 *Accelerate* — Nicole Forsgren - -**🔗 Links:** -* 🌐 [GitHub Actions Docs](https://docs.github.com/en/actions) -* 🌐 [Pytest Documentation](https://docs.pytest.org/) -* 🌐 [Snyk Security](https://snyk.io/) -* 🌐 [SemVer](https://semver.org/) -* 🌐 [CalVer](https://calver.org/) - -**🛠️ Tools:** -* 🔍 [act](https://github.com/nektos/act) — Run GitHub Actions locally -* 🔍 [actionlint](https://github.com/rhysd/actionlint) — Lint workflows -* 📊 [Codecov](https://codecov.io/) — Coverage tracking - ---- diff --git a/lectures/lec4.md b/lectures/lec4.md deleted file mode 100644 index acfe810526..0000000000 --- a/lectures/lec4.md +++ /dev/null @@ -1,801 +0,0 @@ -# 📌 Lecture 4 — Infrastructure as Code: From Snowflakes to Cattle - -## 📍 Slide 1 – 🚀 Welcome to Infrastructure as Code - -* 🌍 **Infrastructure used to be physical** — racks, cables, manual configuration -* 😰 Manual setup leads to inconsistency, drift, and undocumented "snowflakes" -* 🏗️ **Infrastructure as Code (IaC)** treats infrastructure like software -* 🎯 This lecture: learn to define, version, and automate your infrastructure - -```mermaid -flowchart LR - Manual[🔧 Manual Setup] -->|IaC| Code[📝 Code-Defined] - Code --> Reproducible[🔄 Reproducible Infrastructure] -``` - ---- - -## 📍 Slide 2 – 🎯 What You Will Learn - -* ✅ Understand Infrastructure as Code principles -* ✅ Compare declarative vs imperative IaC approaches -* ✅ Apply Terraform workflows to real cloud infrastructure -* ✅ Manage infrastructure state securely - -**🎓 Learning Outcomes:** -| # | Outcome | -|---|---------| -| 1 | 🧠 Define IaC and explain its benefits | -| 2 | 🔍 Distinguish between Terraform and Pulumi | -| 3 | 🛠️ Write basic Terraform configurations | -| 4 | 🗺️ Understand state management and security | - ---- - -## 📍 Slide 3 – 📋 How This Lecture Works - -* 📚 **Concepts + Code examples** — hands-on focus -* 🎮 **Real-world scenarios** — cloud provisioning challenges -* 📝 **3 quiz checkpoints**: PRE / MID / POST -* 🛠️ **Tool comparison**: Terraform vs Pulumi - -**⏱️ Lecture Structure:** -``` -Section 0: Introduction (now) → 📝 PRE Quiz -Section 1: The Infrastructure Problem -Section 2: IaC Fundamentals -Section 3: Terraform Deep Dive → 📝 MID Quiz -Section 4: State & Security -Section 5: Real World IaC -Section 6: Reflection → 📝 POST Quiz -``` - ---- - -## 📍 Slide 4 – ❓ The Big Question - -* 📊 **73%** of organizations report configuration drift as a major issue -* ⏱️ Average time to provision a server manually: **hours to days** -* 💥 Most outages caused by **configuration changes** - -> 💬 *"It works in staging but not production"* — Every ops engineer, ever - -**🤔 Think about it:** -* How do you recreate your production environment? -* What happens when the person who set it up leaves? -* Can you spin up a new environment in minutes? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L4_PRE - ---- - -## 📍 Slide 6 – 🔥 Section 1: The Infrastructure Problem - -* 🐶 **Pet Servers** = unique, hand-crafted, irreplaceable -* 🔧 Manual configuration via SSH and console clicks -* 📋 Documentation gets outdated immediately -* 💥 Result: **snowflake infrastructure** — no two servers are the same - -```mermaid -flowchart LR - Server1[🖥️ Server 1: Ubuntu 20 + patches] - Server2[🖥️ Server 2: Ubuntu 22 + different patches] - Server3[🖥️ Server 3: ???] - Server1 --> Chaos[😱 Configuration Chaos] - Server2 --> Chaos - Server3 --> Chaos -``` - ---- - -## 📍 Slide 7 – 🐶 Pets vs Cattle - -* 🐶 **Pets**: Named servers, nursed back to health when sick -* 🐄 **Cattle**: Numbered, identical, replaced when broken -* 🌍 Cloud-native = cattle mindset - -```mermaid -flowchart TD - subgraph 🐶 Pets - P1[web-prod-01] - P2[db-master] - P3[app-legacy] - end - subgraph 🐄 Cattle - C1[instance-001] - C2[instance-002] - C3[instance-003] - end - Pets -->|😰 Unique, fragile| Problem[Hard to scale] - Cattle -->|🔄 Identical, disposable| Solution[Easy to scale] -``` - -> 🤔 **Think:** Are your servers pets or cattle? - ---- - -## 📍 Slide 8 – 😱 Configuration Drift - -* 📅 Server configured once, modified many times -* 🔧 "Quick fixes" applied directly in production -* 📋 No record of what changed -* 💀 Disaster recovery = guesswork - -```mermaid -flowchart TD - Initial[✅ Initial Setup] --> Month1[📅 Month 1: Hotfix applied] - Month1 --> Month3[📅 Month 3: Security patch] - Month3 --> Month6[📅 Month 6: Unknown changes] - Month6 --> Drift[😱 Configuration Drift] - Drift --> Unknown[❓ What's actually running?] -``` - -**📊 The Numbers:** -* 🔍 **65%** of downtime caused by configuration issues -* ⏱️ Average recovery time: **4+ hours** -* 💰 Cost per hour of downtime: **$300,000** (enterprise) - ---- - -## 📍 Slide 9 – 😨 The Bus Factor - -* 👤 One person knows how the infrastructure works -* 🚌 They leave, get sick, or go on vacation -* 🙈 Nobody can recreate or fix the environment -* 💀 Business continuity at risk - -> ⚠️ **Bus Factor = 1** means your infrastructure is fragile - -**😰 Signs of Low Bus Factor:** -* 🔇 "Ask John, he set that up" -* 📝 Documentation is outdated or missing -* 🐌 Changes require specific people -* 🚪 Knowledge walks out the door - -**💬 Discussion:** What's your infrastructure bus factor? - ---- - -## 📍 Slide 10 – 💸 The Cost of Manual Infrastructure - -| 🔥 Problem | 💥 Impact | -|------------|-----------| -| 🐢 Slow provisioning | Days to spin up new environments | -| 📋 Manual processes | Human error, inconsistency | -| 👉 No audit trail | Compliance violations | -| 🙈 Configuration drift | Unpredictable behavior | - -**📈 Real Numbers:** -* 🏢 **Manual provisioning**: 2-4 hours per server -* 🚀 **With IaC**: 2-4 minutes per server -* 🔄 **Environment recreation**: hours vs seconds - -**💰 Time Cost:** -* 👨‍💻 Engineer time: **$75-150/hour** -* 🖥️ 10 servers manually: **$1,500-3,000** -* 🤖 10 servers with IaC: **$15-30** - ---- - -## 📍 Slide 11 – 💡 Section 2: What Infrastructure as Code Is - -* 📝 **IaC** = defining infrastructure in version-controlled files -* 🔄 Infrastructure becomes **reproducible** and **auditable** -* 🚫 No more clicking through consoles -* 🎯 Same infrastructure, every time - -```mermaid -flowchart LR - Code[📝 Code] -->|🔄 Apply| Cloud[☁️ Cloud] - Cloud --> Infra[🏗️ Infrastructure] - Code -->|📜 Git| Version[Version Control] -``` - -**📖 Definition:** -> *Infrastructure as Code is the practice of managing and provisioning infrastructure through machine-readable configuration files rather than through manual processes or interactive tools.* - ---- - -## 📍 Slide 12 – 🚫 What IaC is NOT - -| ❌ Myth | ✅ Reality | -|---------|-----------| -| "Just automation scripts" | 📝 Declarative desired state | -| "Only for cloud" | 🖥️ Works for any infrastructure | -| "Replaces ops people" | 🤝 Empowers ops teams | -| "Too complex for small teams" | 🎯 Benefits scale to any size | -| "One-time setup" | 🔄 Continuous lifecycle management | - -> 🔥 **Hot take:** If you can't recreate your infrastructure from code, you don't have IaC. - -**🎯 IaC is about:** -* 🧠 Declarative definitions -* 🤝 Team collaboration on infrastructure -* 🔄 Repeatable, consistent environments -* 📊 Audit trails and compliance - ---- - -## 📍 Slide 13 – 🔀 Declarative vs Imperative - -```mermaid -flowchart TD - subgraph Declarative - D1[📝 Define desired state] - D2[🤖 Tool figures out how] - D1 --> D2 - end - subgraph Imperative - I1[📝 Define exact steps] - I2[🔧 Execute step by step] - I1 --> I2 - end -``` - -| 📋 Aspect | 🌍 Declarative | 🔧 Imperative | -|-----------|---------------|---------------| -| 📝 What you write | Desired end state | Exact steps | -| 🛠️ Tool | Terraform, CloudFormation | Pulumi, Scripts | -| 🔄 Idempotency | Built-in | You implement | -| 📚 Example | "3 VMs exist" | "Create VM 1, 2, 3" | - -**📚 Source:** Terraform documentation - ---- - -## 📍 Slide 14 – 🛠️ IaC Tool Landscape - -```mermaid -graph TD - IaC[🏗️ Infrastructure as Code] - IaC --> Prov[📦 Provisioning] - IaC --> Config[⚙️ Configuration] - Prov --> Terraform[🌍 Terraform] - Prov --> Pulumi[📦 Pulumi] - Prov --> Cloud[☁️ CloudFormation/ARM] - Config --> Ansible[🔧 Ansible] - Config --> Chef[👨‍🍳 Chef] - Config --> Puppet[🎭 Puppet] -``` - -| 🛠️ Tool | 🎯 Focus | 📝 Language | -|---------|---------|------------| -| 🌍 **Terraform** | Provisioning | HCL (declarative) | -| 📦 **Pulumi** | Provisioning | Python, TS, Go | -| 🔧 **Ansible** | Configuration | YAML | -| ☁️ **CloudFormation** | AWS only | YAML/JSON | - ---- - -## 📍 Slide 15 – ⚡ Before vs After IaC - -| 😰 Before | 🚀 After | -|----------|---------| -| 📅 Days to provision | ⚡ Minutes to provision | -| 📋 Manual documentation | 📝 Code IS documentation | -| 👉 "Who changed that?" | 📜 Git history shows all | -| 😨 Fear of recreation | 💪 Confident rebuilds | -| 🐶 Unique snowflakes | 🐄 Identical cattle | -| 🙅 "Don't touch prod" | 🔄 Infrastructure is disposable | - -> 🤔 How confident are you in recreating your infrastructure? - ---- - -## 📍 Slide 16 – 🎮 Section 3: Terraform Deep Dive - -## 🌍 Why Terraform? - -* 🌐 **Multi-cloud**: AWS, GCP, Azure, Yandex, and 3000+ providers -* 📝 **HCL**: Human-readable configuration language -* 🔄 **State management**: Tracks what exists -* 🏢 **Industry standard**: Most widely adopted IaC tool - -**🎮 Let's build infrastructure.** - ---- - -## 📍 Slide 17 – 📝 Terraform Workflow - -```mermaid -flowchart LR - Write[📝 Write] --> Init[🔧 Init] - Init --> Plan[📋 Plan] - Plan --> Apply[🚀 Apply] - Apply --> Destroy[💥 Destroy] -``` - -* 📝 **Write**: Define resources in `.tf` files -* 🔧 **Init**: Download provider plugins -* 📋 **Plan**: Preview changes (dry run) -* 🚀 **Apply**: Create/update infrastructure -* 💥 **Destroy**: Remove all resources - -**🛠️ Commands:** -```bash -terraform init # Download providers -terraform plan # Preview changes -terraform apply # Apply changes -terraform destroy # Remove everything -``` - ---- - -## 📍 Slide 18 – 🧱 Terraform Building Blocks - -```mermaid -flowchart TD - Config[📁 Configuration] - Config --> Provider[☁️ Provider] - Config --> Resource[🏗️ Resource] - Config --> Variable[📊 Variable] - Config --> Output[📤 Output] - Config --> Data[🔍 Data Source] -``` - -* ☁️ **Provider**: Cloud API connection (AWS, GCP, Yandex) -* 🏗️ **Resource**: Infrastructure component (VM, network, firewall) -* 📊 **Variable**: Configurable inputs -* 📤 **Output**: Values to display/export -* 🔍 **Data Source**: Query existing infrastructure - ---- - -## 📍 Slide 19 – 💻 Terraform Example: VM Creation - -```hcl -# ☁️ Provider configuration -provider "yandex" { - zone = "ru-central1-a" -} - -# 🏗️ Virtual machine resource -resource "yandex_compute_instance" "web" { - name = "web-server" - platform_id = "standard-v2" - - resources { - cores = 2 - memory = 2 - } - - boot_disk { - initialize_params { - image_id = "fd8vmcue7aajqdge3bp0" # Ubuntu 22.04 - } - } -} -``` - -**🎯 Result:** One command creates a VM in the cloud - ---- - -## 📍 Slide 20 – 📊 Variables and Outputs - -```hcl -# 📊 Input variables -variable "instance_count" { - description = "Number of VMs to create" - type = number - default = 1 -} - -variable "environment" { - description = "Environment name" - type = string -} - -# 📤 Output values -output "vm_ip" { - description = "Public IP of the VM" - value = yandex_compute_instance.web.network_interface.0.nat_ip_address -} -``` - -**🛠️ Usage:** -```bash -terraform apply -var="instance_count=3" -var="environment=prod" -``` - ---- - -## 📍 Slide 21 – 🔄 Terraform Plan - -```mermaid -flowchart LR - Code[📝 Config] --> Plan[📋 terraform plan] - State[📦 State] --> Plan - Plan --> Diff[🔍 Difference] - Diff --> Preview[👀 What will change?] -``` - -**📋 Plan Output Example:** -``` -# yandex_compute_instance.web will be created -+ resource "yandex_compute_instance" "web" { - + name = "web-server" - + platform_id = "standard-v2" - + status = (known after apply) - - + resources { - + cores = 2 - + memory = 2 - } - } - -Plan: 1 to add, 0 to change, 0 to destroy. -``` - -**🎯 Always review the plan before applying!** - ---- - -## 📍 Slide 22 – 📦 Pulumi Alternative - -```mermaid -flowchart LR - Terraform[🌍 Terraform] -->|HCL| Declarative[📝 Declarative] - Pulumi[📦 Pulumi] -->|Python/TS/Go| Imperative[💻 Imperative] -``` - -**📦 Pulumi Python Example:** -```python -import pulumi -import pulumi_yandex as yandex - -# 🏗️ Create VM using Python -vm = yandex.ComputeInstance("web", - name="web-server", - platform_id="standard-v2", - resources=yandex.ComputeInstanceResourcesArgs( - cores=2, - memory=2, - )) - -# 📤 Export IP address -pulumi.export("ip", vm.network_interfaces[0].nat_ip_address) -``` - -**🎯 Same result, real programming language** - ---- - -## 📍 Slide 23 – ⚖️ Terraform vs Pulumi - -| 📋 Aspect | 🌍 Terraform | 📦 Pulumi | -|-----------|-------------|----------| -| 📝 Language | HCL (domain-specific) | Python, TS, Go, C# | -| 📚 Learning curve | New syntax to learn | Familiar languages | -| 🔄 Logic | Limited (count, for_each) | Full programming | -| 🧪 Testing | External tools | Native unit tests | -| 📦 State | Local or S3 | Pulumi Cloud (free tier) | -| 🔐 Secrets | Plain in state | Encrypted by default | - -> ❓ **When to use which?** -> * 🌍 **Terraform**: Larger community, more examples, declarative simplicity -> * 📦 **Pulumi**: Complex logic, existing codebase, testing requirements - ---- - -## 📍 Slide 24 – 🔐 Security Best Practices - -```yaml -# ❌ NEVER do this -provider "aws" { - access_key = "AKIAIOSFODNN7EXAMPLE" # 💀 Hardcoded secret! - secret_key = "wJalrXUtnFEMI/..." # 💀 Hardcoded secret! -} - -# ✅ Use environment variables -# export AWS_ACCESS_KEY_ID="..." -# export AWS_SECRET_ACCESS_KEY="..." -provider "aws" { - # Automatically uses env vars -} -``` - -**🔐 Security Rules:** -* 🚫 Never commit secrets to Git -* 📁 Use `.gitignore` for state and tfvars -* 🔑 Use environment variables or secret managers -* 🔒 Encrypt state file at rest - ---- - -## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L4_MID - ---- - -## 📍 Slide 26 – 📦 Section 4: State Management - -## 🗃️ What is Terraform State? - -* 📝 Maps configuration to real-world resources -* 🔍 Tracks what Terraform manages -* 🔄 Determines what changes are needed -* ⚠️ Contains sensitive data - -```mermaid -flowchart LR - Config[📝 Config Files] --> TF[🌍 Terraform] - State[📦 State File] --> TF - TF --> Cloud[☁️ Real Infrastructure] - Cloud --> State -``` - ---- - -## 📍 Slide 27 – 📁 State File Contents - -```json -{ - "version": 4, - "terraform_version": "1.9.0", - "resources": [ - { - "type": "yandex_compute_instance", - "name": "web", - "instances": [ - { - "attributes": { - "id": "fhm1234567890", - "name": "web-server", - "network_interface": [ - { - "ip_address": "192.168.1.10", - "nat_ip_address": "51.250.1.100" - } - ] - } - } - ] - } - ] -} -``` - -**⚠️ Never edit state manually!** - ---- - -## 📍 Slide 28 – 🌐 Remote State - -```mermaid -flowchart TD - Dev1[👨‍💻 Developer 1] --> Remote[🌐 Remote State] - Dev2[👨‍💻 Developer 2] --> Remote - Dev3[👨‍💻 Developer 3] --> Remote - Remote --> Cloud[☁️ Cloud Infrastructure] -``` - -**🌐 Remote State Benefits:** -* 🤝 Team collaboration -* 🔒 Locking prevents conflicts -* 🔐 Encrypted at rest -* 📜 Versioning and backup - -**📦 Backend Options:** -* ☁️ **S3/GCS**: Object storage -* 🏢 **Terraform Cloud**: HashiCorp managed -* 🔐 **Consul**: HashiCorp Consul - ---- - -## 📍 Slide 29 – 📊 IaC Metrics - -| 📊 Metric | 📏 Measures | 🏆 Target | -|-----------|------------|---------| -| ⏱️ **Provisioning Time** | Time to create env | < 15 minutes | -| 🔄 **Environment Parity** | Dev = Staging = Prod | 100% | -| ❌ **Drift Detection** | Config drift incidents | 0 per month | -| 📜 **Audit Compliance** | Changes tracked in Git | 100% | - -> 📚 These metrics indicate IaC maturity. - -**🤔 Question:** How long does it take to spin up a new environment? - ---- - -## 📍 Slide 30 – 🌊 From Snowflakes to Cattle - -```mermaid -flowchart LR - subgraph 😱 Snowflakes - Manual[🔧 Manual Setup] - Unique[❄️ Unique Servers] - Drift[📋 Configuration Drift] - end - subgraph 🐄 Cattle - Code[📝 Code-Defined] - Identical[🔄 Identical Servers] - Reproducible[✅ Reproducible] - end - Snowflakes -->|🚀 IaC| Cattle -``` - -**🎯 Goal State:** -* ⚡ Any environment recreatable in minutes -* 🔄 All changes through code review -* 📈 Teams deploy infrastructure confidently - ---- - -## 📍 Slide 31 – 🏢 Section 5: IaC in Real Life - -## 📅 A Day with IaC - -**☀️ Morning:** -* 📊 Review infrastructure PR -* 👀 Check `terraform plan` output -* ✅ Approve and merge - -**🌤️ Afternoon:** -* 🚨 Need new test environment -* 🔧 Copy `terraform.tfvars` -* 🚀 `terraform apply` — **done in 10 minutes** - -**🌙 Evening:** -* 🗑️ `terraform destroy` test environment -* 💰 No resources running overnight - ---- - -## 📍 Slide 32 – 👥 IaC Team Workflow - -| 👤 Role | 🎯 IaC Responsibility | -|---------|----------------------| -| 🔧 **DevOps/Platform** | Write and maintain IaC modules | -| 👨‍💻 **Developer** | Use modules, request infrastructure | -| 🛡️ **Security** | Review IaC for compliance | -| 📊 **FinOps** | Monitor infrastructure costs | - -**🔗 Common Workflow:** -* 📝 Create branch with IaC changes -* 🔍 CI runs `terraform plan` -* 👀 Team reviews the plan -* ✅ Merge triggers `terraform apply` - ---- - -## 📍 Slide 33 – 🤝 GitOps for Infrastructure - -```mermaid -flowchart TD - Dev[👨‍💻 Developer] -->|📝 PR| Git[🐙 Git Repository] - Git -->|🔄 CI/CD| Plan[📋 Terraform Plan] - Plan -->|👀 Review| Approve[✅ Approve] - Approve -->|🚀 Merge| Apply[🌍 Terraform Apply] - Apply --> Cloud[☁️ Infrastructure] -``` - -**🤝 GitOps Practices:** -* 📟 All changes through pull requests -* 📝 Plan output in PR comments -* 👥 Required approvals -* 🔓 Protected main branch - ---- - -## 📍 Slide 34 – 📈 Career Path: IaC Skills - -```mermaid -flowchart LR - Junior[🌱 Junior: Basic Terraform] --> Mid[💼 Mid: Modules & CI/CD] - Mid --> Senior[⭐ Senior: Multi-cloud & Architecture] - Senior --> Principal[🏆 Principal: Platform Strategy] -``` - -**🛠️ Skills to Build:** -* 🌍 Terraform HCL fluency -* ☁️ Cloud provider APIs -* 🔐 Security best practices -* 📦 Module design -* 🔄 CI/CD integration - ---- - -## 📍 Slide 35 – 🌍 Real Company Examples - -**🏢 HashiCorp Customers:** -* 🏦 **Stripe**: Terraform for AWS infrastructure -* 🎮 **Riot Games**: Multi-cloud with Terraform -* 🛒 **Shopify**: Thousands of resources managed - -**☁️ Cloud Native:** -* 🔍 **Google**: Uses Terraform internally -* 📦 **Spotify**: IaC for Kubernetes infrastructure -* 🎬 **Netflix**: Custom tooling built on IaC principles - -**📊 Stats:** -* 🌍 **2M+** Terraform users worldwide -* 📦 **3000+** providers available -* 🏢 **Fortune 500**: 85% use IaC - ---- - -## 📍 Slide 36 – 🎯 Section 6: Reflection - -## 📝 Key Takeaways - -1. 🏗️ **IaC = Infrastructure defined in code** -2. 🐄 **Cattle not pets** — servers are disposable -3. 📝 **Version control everything** — Git for infrastructure -4. 📋 **Plan before apply** — always review changes -5. 🔐 **Never commit secrets** — use environment variables - -> 💡 If you can't recreate it from code, it's not really infrastructure as code. - ---- - -## 📍 Slide 37 – 🧠 The Mindset Shift - -| 😰 Old Mindset | 🚀 IaC Mindset | -|---------------|------------------| -| 🙅 "SSH and fix it" | 📝 "Change the code" | -| 🚫 "Don't touch that server" | 💪 "Destroy and recreate" | -| 👉 "Who set this up?" | 📜 "Git blame shows history" | -| 😨 "Manual is faster" | ⚡ "Automation is faster at scale" | -| 💻 "Works on my cloud" | 🌍 "Works on any cloud" | - -> ❓ Which mindset describes your team? - ---- - -## 📍 Slide 38 – ✅ Your Progress - -## 🎓 What You Now Understand - -* ✅ Why IaC is essential for modern infrastructure -* ✅ The difference between declarative and imperative -* ✅ How Terraform and Pulumi work -* ✅ State management and security practices -* ✅ Real-world IaC workflows - -> 🚀 **You're ready for Lab 4: Terraform & Pulumi** - ---- - -## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L4_POST - ---- - -## 📍 Slide 40 – 🚀 What Comes Next - -## 📚 Next Lecture: Configuration Management with Ansible - -* 🔧 Ansible fundamentals -* 📦 Roles and playbooks -* 🤖 Automating server configuration -* 💻 Hands-on: Deploying Docker with Ansible - -**🎉 Your IaC journey begins.** - -> 🐄 From snowflakes to cattle — one terraform apply at a time. - -```mermaid -flowchart LR - You[👤 You] --> IaC[🏗️ IaC Skills] - IaC --> Reproducible[🔄 Reproducible Infra] - Reproducible --> Career[🚀 Career Growth] -``` - -**👋 See you in the next lecture!** - ---- - -## 📚 Resources & Further Reading - -**📕 Books:** -* 📖 *Terraform: Up & Running* — Yevgeniy Brikman -* 📖 *Infrastructure as Code* — Kief Morris -* 📖 *The DevOps Handbook* — Gene Kim et al. - -**🔗 Links:** -* 🌐 [Terraform Documentation](https://developer.hashicorp.com/terraform/docs) -* 🌐 [Pulumi Documentation](https://www.pulumi.com/docs/) -* 🌐 [Terraform Registry](https://registry.terraform.io/) - ---- diff --git a/lectures/lec5.md b/lectures/lec5.md deleted file mode 100644 index 5bcfba6c1a..0000000000 --- a/lectures/lec5.md +++ /dev/null @@ -1,824 +0,0 @@ -# 📌 Lecture 5 — Configuration Management: Ansible Fundamentals - -## 📍 Slide 1 – 🚀 Welcome to Configuration Management - -* 🌍 **Infrastructure is provisioned** — but what about configuring it? -* 😰 Manual server setup leads to inconsistency and errors -* 🔧 **Ansible automates configuration** — repeatable, reliable, documented -* 🎯 This lecture: master Ansible roles, playbooks, and best practices - -```mermaid -flowchart LR - Provision[🏗️ Terraform: Create VMs] --> Configure[🔧 Ansible: Configure VMs] - Configure --> Ready[✅ Ready to Run Apps] -``` - ---- - -## 📍 Slide 2 – 🎯 What You Will Learn - -* ✅ Understand Ansible architecture and concepts -* ✅ Write idempotent playbooks and roles -* ✅ Secure credentials with Ansible Vault -* ✅ Apply configuration management best practices - -**🎓 Learning Outcomes:** -| # | Outcome | -|---|---------| -| 1 | 🧠 Explain Ansible's agentless architecture | -| 2 | 🔍 Create reusable roles for configuration | -| 3 | 🛠️ Write idempotent tasks and handlers | -| 4 | 🗺️ Secure secrets with Ansible Vault | - ---- - -## 📍 Slide 3 – 📋 How This Lecture Works - -* 📚 **Concepts + YAML examples** — hands-on learning -* 🎮 **Real-world scenarios** — server configuration challenges -* 📝 **3 quiz checkpoints**: PRE / MID / POST -* 🛠️ **Best practices**: roles, handlers, idempotency - -**⏱️ Lecture Structure:** -``` -Section 0: Introduction (now) → 📝 PRE Quiz -Section 1: The Configuration Problem -Section 2: Ansible Fundamentals -Section 3: Roles & Playbooks → 📝 MID Quiz -Section 4: Idempotency & Handlers -Section 5: Real World Ansible -Section 6: Reflection → 📝 POST Quiz -``` - ---- - -## 📍 Slide 4 – ❓ The Big Question - -* 📊 **94%** of organizations experienced security incidents from misconfigurations -* ⏱️ Average time to configure a server manually: **2-4 hours** -* 💥 Most configuration drift goes **undetected for months** - -> 💬 *"I installed it the same way... I think"* — Every sysadmin, ever - -**🤔 Think about it:** -* How do you ensure 100 servers have identical configs? -* What happens when you need to update a package on all servers? -* Can you prove compliance across your infrastructure? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L5_PRE - ---- - -## 📍 Slide 6 – 🔥 Section 1: The Configuration Problem - -* 🔧 **Manual configuration** = SSH into each server -* 📋 Run commands, edit files, install packages -* 📝 Document steps (that nobody reads) -* 💥 Result: **no two servers are identical** - -```mermaid -flowchart LR - Admin[👤 Admin] -->|SSH| Server1[🖥️ Server 1] - Admin -->|SSH| Server2[🖥️ Server 2] - Admin -->|SSH| Server3[🖥️ Server 3] - Server1 --> Drift1[📋 Config A] - Server2 --> Drift2[📋 Config B] - Server3 --> Drift3[📋 Config ???] -``` - ---- - -## 📍 Slide 7 – 🐚 Shell Script Approach - -* 📝 Write bash scripts to automate -* 🔄 Run scripts on each server -* ⚠️ Problem: Scripts aren't idempotent - -```bash -#!/bin/bash -# 😰 What happens if you run this twice? -apt-get update -apt-get install -y nginx -echo "Welcome" > /var/www/html/index.html -systemctl start nginx -``` - -**💥 Issues:** -* 🔄 Re-running may cause errors -* 😰 No rollback mechanism -* 📋 No state tracking -* 🔗 No dependency management - -> 🤔 **Think:** What if nginx is already installed? - ---- - -## 📍 Slide 8 – 😱 Configuration Management Challenges - -* 📅 100 servers need the same update -* 🔧 Some servers have different OS versions -* 📋 Some packages conflict with others -* 💀 One mistake = hours of cleanup - -```mermaid -flowchart TD - Update[📦 Update Required] --> S1[🖥️ Server 1: Ubuntu 20] - Update --> S2[🖥️ Server 2: Ubuntu 22] - Update --> S3[🖥️ Server 3: Ubuntu 24] - S1 --> Problem1[😰 Different package versions] - S2 --> Problem2[😰 Different dependencies] - S3 --> Problem3[😰 Different configs needed] -``` - -**📊 The Numbers:** -* 🔍 **85%** of breaches involve misconfiguration -* ⏱️ Manual update of 100 servers: **days** -* 💰 Cost of configuration-related downtime: **$5,600/minute** - ---- - -## 📍 Slide 9 – 😨 Documentation Drift - -* 📝 Documentation written once -* 🔧 Server modified many times -* 📋 Documentation never updated -* 💀 Reality ≠ documentation - -> ⚠️ **Outdated docs are worse than no docs** - -**😰 Signs of Documentation Drift:** -* 🔇 "The wiki says X but we do Y now" -* 📝 Multiple conflicting runbooks -* 🐌 New hires struggle to onboard -* 🚪 Knowledge leaves with employees - -**💬 Discussion:** How current is your documentation? - ---- - -## 📍 Slide 10 – 💸 The Cost of Manual Configuration - -| 🔥 Problem | 💥 Impact | -|------------|-----------| -| 🐢 Slow updates | Security vulnerabilities linger | -| 📋 Manual errors | Downtime from typos | -| 👉 Inconsistency | "Works on server 1 but not 2" | -| 🙈 No audit trail | Compliance failures | - -**📈 Real Numbers:** -* 🏢 **Manual config time**: 2-4 hours per server -* 🚀 **With Ansible**: 5-10 minutes per server -* 🔄 **Scaling**: minutes vs days - -**💰 ROI Example:** -* 👨‍💻 100 servers × 3 hours × $75/hour = **$22,500** -* 🤖 Ansible: 1 hour setup + seconds to run = **$75** - ---- - -## 📍 Slide 11 – 💡 Section 2: What Ansible Is - -* 🔧 **Configuration management tool** — automate server setup -* 🌐 **Agentless** — uses SSH, no agents to install -* 📝 **YAML-based** — human-readable playbooks -* 🔄 **Idempotent** — safe to run multiple times - -```mermaid -flowchart LR - Control[💻 Control Node] -->|SSH| Node1[🖥️ Managed Node] - Control -->|SSH| Node2[🖥️ Managed Node] - Control -->|SSH| Node3[🖥️ Managed Node] -``` - -**📖 Definition:** -> *Ansible is an open-source automation tool for configuration management, application deployment, and task automation using a simple YAML syntax.* - ---- - -## 📍 Slide 12 – 🚫 What Ansible is NOT - -| ❌ Myth | ✅ Reality | -|---------|-----------| -| "Replaces Terraform" | 🤝 They complement each other | -| "Requires agents" | 🌐 Agentless, SSH-based | -| "Only for Linux" | 🪟 Works with Windows too | -| "Just a scripting tool" | 📦 Full configuration management | -| "Hard to learn" | 📝 YAML is simple | - -> 🔥 **Hot take:** Terraform provisions, Ansible configures. Use both. - -**🎯 Ansible is about:** -* 🧠 Declarative configuration -* 🤝 Consistent state across servers -* 🔄 Repeatable automation -* 📊 Self-documenting infrastructure - ---- - -## 📍 Slide 13 – 🏗️ Ansible Architecture - -```mermaid -flowchart TD - Control[💻 Control Node] - Control --> Inventory[📋 Inventory] - Control --> Playbook[📝 Playbook] - Control --> Modules[📦 Modules] - Inventory --> Managed[🖥️ Managed Nodes] - Playbook --> Managed - Modules --> Managed -``` - -| 🧱 Component | 🎯 Purpose | -|-------------|----------| -| 💻 **Control Node** | Where Ansible runs | -| 📋 **Inventory** | List of managed servers | -| 📝 **Playbook** | Automation instructions | -| 📦 **Modules** | Units of work (apt, copy, service) | -| 🖥️ **Managed Nodes** | Target servers | - ---- - -## 📍 Slide 14 – 📋 Inventory Basics - -```ini -# inventory/hosts.ini -[webservers] -web1 ansible_host=192.168.1.10 -web2 ansible_host=192.168.1.11 - -[databases] -db1 ansible_host=192.168.1.20 - -[all:vars] -ansible_user=ubuntu -ansible_python_interpreter=/usr/bin/python3 -``` - -**🎯 Inventory Features:** -* 📁 Group servers logically -* 🔧 Set per-host or per-group variables -* 🌐 Static files or dynamic discovery -* 🏷️ Use patterns: `webservers`, `all`, `db*` - ---- - -## 📍 Slide 15 – ⚡ Before vs After Ansible - -| 😰 Before | 🚀 After | -|----------|---------| -| 📅 SSH into each server | 🤖 One command for all | -| 📋 Manual steps | 📝 Documented playbooks | -| 👉 "Run these commands" | ✅ "Desired state defined" | -| 😨 Fear of updates | 💪 Confident automation | -| 🐌 Hours per server | ⚡ Seconds per server | -| 📝 Outdated wiki | 📄 Living documentation | - -> 🤔 How much time does your team spend on manual configuration? - ---- - -## 📍 Slide 16 – 🎮 Section 3: Roles & Playbooks - -## 📝 Playbook Basics - -* 📄 YAML file with automation tasks -* 🎯 Defines desired state -* 🔄 Executes on target hosts -* 📦 Groups related tasks - -**🎮 Let's write some Ansible.** - ---- - -## 📍 Slide 17 – 📝 Simple Playbook Example - -```yaml ---- -# playbook.yml -- name: Configure web servers - hosts: webservers - become: yes # 🔐 Run as root - - tasks: - - name: Update apt cache - apt: - update_cache: yes - cache_valid_time: 3600 - - - name: Install nginx - apt: - name: nginx - state: present - - - name: Start nginx - service: - name: nginx - state: started - enabled: yes -``` - -**🛠️ Run it:** -```bash -ansible-playbook -i inventory/hosts.ini playbook.yml -``` - ---- - -## 📍 Slide 18 – 📦 Why Roles? - -```mermaid -flowchart TD - subgraph ❌ Without Roles - P1[📝 One huge playbook] - P1 --> Problem[😰 Hard to maintain] - end - subgraph ✅ With Roles - R1[📦 common role] - R2[📦 docker role] - R3[📦 app role] - R1 --> Reuse[🔄 Reusable] - R2 --> Reuse - R3 --> Reuse - end -``` - -**📦 Role Benefits:** -* 🔄 **Reusability**: Use across projects -* 📁 **Organization**: Clear structure -* 🧪 **Testability**: Test roles independently -* 🤝 **Sharing**: Ansible Galaxy - ---- - -## 📍 Slide 19 – 📁 Role Structure - -``` -roles/ -├── docker/ -│ ├── tasks/ -│ │ └── main.yml # 🎯 Main tasks -│ ├── handlers/ -│ │ └── main.yml # 🔔 Event handlers -│ ├── defaults/ -│ │ └── main.yml # 📊 Default variables -│ ├── templates/ -│ │ └── config.j2 # 📝 Jinja2 templates -│ └── files/ -│ └── script.sh # 📄 Static files -``` - -**🔑 Key Directories:** -* 📁 **tasks/**: What to do -* 📁 **handlers/**: React to changes -* 📁 **defaults/**: Default values (low priority) -* 📁 **templates/**: Dynamic file templates -* 📁 **files/**: Static files to copy - ---- - -## 📍 Slide 20 – 🐳 Docker Role Example - -```yaml -# roles/docker/tasks/main.yml ---- -- name: Install Docker prerequisites - apt: - name: - - apt-transport-https - - ca-certificates - - curl - state: present - -- name: Add Docker GPG key - apt_key: - url: https://download.docker.com/linux/ubuntu/gpg - state: present - -- name: Add Docker repository - apt_repository: - repo: "deb https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable" - state: present - -- name: Install Docker - apt: - name: docker-ce - state: present - notify: restart docker -``` - ---- - -## 📍 Slide 21 – 🔔 Handlers - -```yaml -# roles/docker/handlers/main.yml ---- -- name: restart docker - service: - name: docker - state: restarted -``` - -**🔔 Handler Features:** -* 🔄 Only run when notified -* ⏱️ Run once at end of play -* 🎯 React to configuration changes -* 💡 Prevent unnecessary restarts - -```yaml -# tasks/main.yml -- name: Update Docker config - template: - src: daemon.json.j2 - dest: /etc/docker/daemon.json - notify: restart docker # 🔔 Trigger handler -``` - ---- - -## 📍 Slide 22 – 📊 Variables & Defaults - -```yaml -# roles/docker/defaults/main.yml ---- -docker_version: "24.0" -docker_users: - - ubuntu -docker_log_driver: "json-file" -docker_log_max_size: "10m" -``` - -**📊 Variable Precedence (lowest to highest):** -1. 📁 Role defaults -2. 📋 Inventory variables -3. 📄 Playbook vars -4. 🔧 Command line (`-e var=value`) - -```yaml -# Using variables in tasks -- name: Install Docker {{ docker_version }} - apt: - name: "docker-ce={{ docker_version }}*" - state: present -``` - ---- - -## 📍 Slide 23 – 📝 Using Roles in Playbooks - -```yaml -# playbooks/provision.yml ---- -- name: Provision web servers - hosts: webservers - become: yes - - roles: - - common # 📦 Install common packages - - docker # 🐳 Install Docker - - app_deploy # 🚀 Deploy application -``` - -**🎯 Clean and simple!** - -```mermaid -flowchart LR - Playbook[📝 Playbook] --> Common[📦 common] - Playbook --> Docker[🐳 docker] - Playbook --> App[🚀 app_deploy] - Common --> Result[✅ Configured Server] - Docker --> Result - App --> Result -``` - ---- - -## 📍 Slide 24 – 🔐 Ansible Vault - -```bash -# 🔐 Create encrypted file -ansible-vault create group_vars/all.yml - -# 📝 Edit encrypted file -ansible-vault edit group_vars/all.yml - -# 👀 View encrypted file -ansible-vault view group_vars/all.yml -``` - -**🔐 Encrypted Content:** -```yaml ---- -# group_vars/all.yml (encrypted) -dockerhub_username: myuser -dockerhub_password: super_secret_token -app_secret_key: very_secret_key_123 -``` - -**🛠️ Using Vault:** -```bash -ansible-playbook playbook.yml --ask-vault-pass -# Or use password file (gitignored!) -ansible-playbook playbook.yml --vault-password-file .vault_pass -``` - ---- - -## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L5_MID - ---- - -## 📍 Slide 26 – 🔄 Section 4: Idempotency - -## ♾️ What is Idempotency? - -* 🔄 Same result whether run once or many times -* ✅ Safe to re-run playbooks -* 📊 Converges to desired state -* 🎯 No unintended side effects - -```mermaid -flowchart LR - Run1[🚀 First Run] --> State[✅ Desired State] - Run2[🚀 Second Run] --> State - Run3[🚀 Third Run] --> State -``` - -**🎨 Output Colors:** -* 🟢 **ok**: Already in desired state -* 🟡 **changed**: Made a change -* 🔴 **failed**: Task failed -* ⚫ **skipped**: Task skipped - ---- - -## 📍 Slide 27 – 🔄 Idempotent vs Non-Idempotent - -```yaml -# ❌ Non-idempotent (shell command) -- name: Add line to file - shell: echo "config=value" >> /etc/app.conf - # 💥 Adds line EVERY time! - -# ✅ Idempotent (lineinfile module) -- name: Ensure line in file - lineinfile: - path: /etc/app.conf - line: "config=value" - state: present - # ✅ Only adds if missing! -``` - -**📦 Idempotent Modules:** -| Module | Purpose | Idempotent? | -|--------|---------|-------------| -| `apt` | Install packages | ✅ Yes | -| `service` | Manage services | ✅ Yes | -| `file` | Manage files | ✅ Yes | -| `shell` | Run commands | ❌ Usually no | -| `command` | Run commands | ❌ Usually no | - ---- - -## 📍 Slide 28 – 🧪 Testing Idempotency - -```mermaid -flowchart TD - Run1[🚀 First Run] --> Changed[🟡 changed: 15] - Run2[🚀 Second Run] --> Ok[🟢 changed: 0] - Ok --> Idempotent[✅ Playbook is Idempotent!] -``` - -**🧪 Test Process:** -1. 🚀 Run playbook first time → many changes -2. 🚀 Run playbook second time → **zero changes** -3. ✅ If second run shows `changed: 0`, you're idempotent - -**📊 Example Output:** -``` -PLAY RECAP -server1 : ok=15 changed=0 unreachable=0 failed=0 -``` - ---- - -## 📍 Slide 29 – 📊 Configuration Management Metrics - -| 📊 Metric | 📏 Measures | 🏆 Target | -|-----------|------------|---------| -| ⏱️ **Config Time** | Time to configure server | < 15 minutes | -| 🔄 **Drift Rate** | Servers with drift | 0% | -| ✅ **Idempotency** | Re-run changes | 0 changes | -| 📜 **Compliance** | Servers meeting policy | 100% | - -> 📚 These metrics indicate configuration management maturity. - -**🤔 Question:** What happens when you re-run your playbooks? - ---- - -## 📍 Slide 30 – 🌊 From Manual to Automated - -```mermaid -flowchart LR - subgraph 😱 Manual - SSH[🔌 SSH Sessions] - Commands[💻 Run Commands] - Hope[🙏 Hope It Works] - end - subgraph 🤖 Automated - Playbook[📝 Playbooks] - Roles[📦 Roles] - Consistent[✅ Consistent] - end - Manual -->|🚀 Ansible| Automated -``` - -**🎯 Automation State:** -* ⚡ Any server configurable in minutes -* 🔄 All changes through playbooks -* 📈 Teams deploy configuration confidently - ---- - -## 📍 Slide 31 – 🏢 Section 5: Ansible in Real Life - -## 📅 A Day with Ansible - -**☀️ Morning:** -* 📊 Review Ansible PR for new role -* 👀 Check syntax with `ansible-lint` -* ✅ Merge to main branch - -**🌤️ Afternoon:** -* 🚨 Security patch needed -* 🔧 Update role with new package version -* 🚀 Run playbook — **all servers patched in 10 minutes** - -**🌙 Evening:** -* 🤖 Scheduled playbook runs -* 📊 Compliance reports generated -* 🏠 Go home confident - ---- - -## 📍 Slide 32 – 👥 Team Ansible Workflow - -| 👤 Role | 🎯 Ansible Responsibility | -|---------|----------------------| -| 🔧 **DevOps** | Write and maintain roles | -| 👨‍💻 **Developer** | Request configuration changes | -| 🛡️ **Security** | Review roles for compliance | -| 📊 **Audit** | Verify configuration state | - -**🔗 Common Workflow:** -* 📝 Create branch with role changes -* 🔍 CI runs `ansible-lint` and syntax check -* 👀 Team reviews the changes -* ✅ Merge triggers playbook run - ---- - -## 📍 Slide 33 – 🤝 Ansible + Terraform - -```mermaid -flowchart LR - TF[🌍 Terraform] -->|Creates| VM[🖥️ Virtual Machine] - VM -->|IP Address| Ansible[🔧 Ansible] - Ansible -->|Configures| Ready[✅ Ready Server] -``` - -**🤝 Integration Patterns:** -* 🌍 Terraform provisions infrastructure -* 📋 Terraform outputs inventory -* 🔧 Ansible configures servers -* 🔄 Both stored in Git - -**💡 Best Practice:** -* 🏗️ Terraform = **what** exists -* 🔧 Ansible = **how** it's configured - ---- - -## 📍 Slide 34 – 📈 Career Path: Ansible Skills - -```mermaid -flowchart LR - Junior[🌱 Junior: Basic Playbooks] --> Mid[💼 Mid: Roles & Vault] - Mid --> Senior[⭐ Senior: Dynamic Inventory & CI/CD] - Senior --> Principal[🏆 Principal: Enterprise Automation] -``` - -**🛠️ Skills to Build:** -* 📝 YAML and Jinja2 fluency -* 📦 Role design patterns -* 🔐 Vault and secrets management -* 🌐 Dynamic inventory -* 🔄 CI/CD integration - ---- - -## 📍 Slide 35 – 🌍 Real Company Examples - -**🏢 Enterprise Users:** -* 🏦 **NASA**: Manages thousands of servers -* 🎮 **EA Games**: Game server configuration -* 🛒 **Walmart**: Retail infrastructure - -**☁️ Cloud Native:** -* 🔍 **Twitter**: Configuration at scale -* 📦 **Lyft**: Microservices configuration -* 🎬 **Apple**: Device management - -**📊 Stats:** -* 🌍 **#1** open-source automation tool -* 📦 **30,000+** modules available -* 🏢 **Most used** by Fortune 100 - ---- - -## 📍 Slide 36 – 🎯 Section 6: Reflection - -## 📝 Key Takeaways - -1. 🔧 **Ansible = Agentless configuration management** -2. 📦 **Roles organize** reusable automation -3. 🔄 **Idempotency** makes re-runs safe -4. 🔔 **Handlers** efficiently manage service restarts -5. 🔐 **Vault encrypts** sensitive data - -> 💡 Ansible playbooks are living documentation of your infrastructure. - ---- - -## 📍 Slide 37 – 🧠 The Mindset Shift - -| 😰 Old Mindset | 🚀 Ansible Mindset | -|---------------|------------------| -| 🙅 "SSH and run commands" | 📝 "Define in playbook" | -| 🚫 "Each server is unique" | 🔄 "All servers are identical" | -| 👉 "Document the steps" | 📄 "Code IS documentation" | -| 😨 "Updates are risky" | 💪 "Updates are automated" | -| 💻 "Works on my server" | 🌍 "Works on all servers" | - -> ❓ Which mindset describes your team? - ---- - -## 📍 Slide 38 – ✅ Your Progress - -## 🎓 What You Now Understand - -* ✅ Ansible's agentless architecture -* ✅ How to write playbooks and roles -* ✅ Why idempotency matters -* ✅ How handlers improve efficiency -* ✅ Securing secrets with Vault - -> 🚀 **You're ready for Lab 5: Ansible Fundamentals** - ---- - -## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L5_POST - ---- - -## 📍 Slide 40 – 🚀 What Comes Next - -## 📚 Next Lecture: Continuous Deployment with Ansible - -* 🚀 Application deployment roles -* 🐳 Docker Compose templates -* 🏷️ Tags and blocks -* 💻 Hands-on: Deploying your app with Ansible - -**🎉 Your configuration automation journey continues.** - -> 🔧 From manual to automated — one playbook at a time. - -```mermaid -flowchart LR - You[👤 You] --> Ansible[🔧 Ansible Skills] - Ansible --> Automated[🤖 Automated Config] - Automated --> Career[🚀 Career Growth] -``` - -**👋 See you in the next lecture!** - ---- - -## 📚 Resources & Further Reading - -**📕 Books:** -* 📖 *Ansible: Up & Running* — Lorin Hochstein -* 📖 *Ansible for DevOps* — Jeff Geerling -* 📖 *The Practice of Cloud System Administration* — Limoncelli - -**🔗 Links:** -* 🌐 [Ansible Documentation](https://docs.ansible.com/) -* 🌐 [Ansible Galaxy](https://galaxy.ansible.com/) -* 🌐 [Ansible Best Practices](https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html) - ---- diff --git a/lectures/lec6.md b/lectures/lec6.md deleted file mode 100644 index a78ba20b5b..0000000000 --- a/lectures/lec6.md +++ /dev/null @@ -1,887 +0,0 @@ -# 📌 Lecture 6 — Continuous Deployment: Advanced Ansible - -## 📍 Slide 1 – 🚀 Welcome to Continuous Deployment - -* 🌍 **Configuration is automated** — but what about deployments? -* 😰 Manual deployments are slow, error-prone, and risky -* 🚀 **CI/CD with Ansible** = automated, repeatable, safe deployments -* 🎯 This lecture: master blocks, tags, Docker Compose, and CI/CD integration - -```mermaid -flowchart LR - Code[💻 Code Push] -->|CI/CD| Build[🔨 Build] - Build --> Deploy[🚀 Ansible Deploy] - Deploy --> Running[✅ Running in Production] -``` - ---- - -## 📍 Slide 2 – 🎯 What You Will Learn - -* ✅ Use blocks for error handling and task grouping -* ✅ Apply tags for selective execution -* ✅ Deploy applications with Docker Compose templates -* ✅ Integrate Ansible with GitHub Actions - -**🎓 Learning Outcomes:** -| # | Outcome | -|---|---------| -| 1 | 🧠 Implement blocks with rescue and always | -| 2 | 🔍 Design effective tag strategies | -| 3 | 🛠️ Template Docker Compose files with Jinja2 | -| 4 | 🗺️ Automate deployments with CI/CD | - ---- - -## 📍 Slide 3 – 📋 How This Lecture Works - -* 📚 **Advanced patterns** — production-ready practices -* 🎮 **Real-world scenarios** — deployment challenges -* 📝 **3 quiz checkpoints**: PRE / MID / POST -* 🛠️ **CI/CD integration**: GitHub Actions + Ansible - -**⏱️ Lecture Structure:** -``` -Section 0: Introduction (now) → 📝 PRE Quiz -Section 1: The Deployment Problem -Section 2: Blocks & Error Handling -Section 3: Tags & Selective Execution → 📝 MID Quiz -Section 4: Docker Compose Deployment -Section 5: CI/CD Integration -Section 6: Reflection → 📝 POST Quiz -``` - ---- - -## 📍 Slide 4 – ❓ The Big Question - -* 📊 **46%** of organizations deploy weekly or faster -* ⏱️ Top performers deploy **multiple times per day** -* 💥 **80%** of outages caused by changes (deploys, configs) - -> 💬 *"We deploy on Fridays and pray over the weekend"* — Nobody should say this - -**🤔 Think about it:** -* How often does your team deploy? -* How long does a deployment take? -* Can you roll back in under 5 minutes? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L6_PRE - ---- - -## 📍 Slide 6 – 🔥 Section 1: The Deployment Problem - -* 🎰 **Deployments = high-risk events** -* 📋 Manual steps, checklists, approval gates -* 🌙 Deploy only during "maintenance windows" -* 💥 Result: **fear of deploying** - -```mermaid -flowchart LR - Ready[✅ Code Ready] --> Wait[📅 Wait for Window] - Wait --> Manual[📋 Manual Steps] - Manual --> Pray[🙏 Hope It Works] - Pray -->|💥 Fail| Rollback[😱 Manual Rollback] - Pray -->|✅ Success| Relief[😮‍💨 Temporary Relief] -``` - ---- - -## 📍 Slide 7 – 💥 Deployment Failures - -* 🔧 Wrong version deployed -* 📦 Missing dependencies -* ⚙️ Configuration mismatch -* 💀 Partial deployment (some servers updated, some not) - -```mermaid -flowchart TD - Deploy[🚀 Deploy Started] --> S1[🖥️ Server 1: ✅ Updated] - Deploy --> S2[🖥️ Server 2: ❌ Failed] - Deploy --> S3[🖥️ Server 3: 🔄 Pending] - S1 --> Inconsistent[😱 Inconsistent State] - S2 --> Inconsistent - S3 --> Inconsistent -``` - -**📊 The Numbers:** -* 🔍 **60%** of outages caused by bad deployments -* ⏱️ Average recovery time: **4+ hours** -* 💰 Cost per hour of downtime: **$300,000+** - ---- - -## 📍 Slide 8 – 😱 Rollback Nightmares - -* 📋 "Just revert the code" — but what about: - * 💾 Database migrations? - * ⚙️ Configuration changes? - * 📦 Dependencies? -* 🙈 No automated rollback = manual scramble -* 💀 Hours of downtime - -> ⚠️ **If you can't roll back quickly, you shouldn't deploy** - -**😰 Signs of Rollback Problems:** -* 🔇 "We've never actually tested rollback" -* 📝 Rollback requires manual steps -* 🐌 "Rollback takes longer than fixing forward" -* 🚪 Nobody knows the rollback procedure - ---- - -## 📍 Slide 9 – 😨 All-or-Nothing Deploys - -* 📅 Big-bang releases every few months -* 🎰 Everything changes at once -* 📋 Impossible to isolate failures -* 💀 If it fails, everything fails - -> ⚠️ **Large releases = large risk** - -**💬 Discussion:** Would you rather deploy 100 changes once or 1 change 100 times? - ---- - -## 📍 Slide 10 – 💸 The Cost of Manual Deployment - -| 🔥 Problem | 💥 Impact | -|------------|-----------| -| 🐢 Slow deployments | Features delayed | -| 📋 Manual errors | Outages, rollbacks | -| 👉 Inconsistent process | "Works for Alice, not Bob" | -| 🙈 Fear of deploying | Innovation stalls | - -**📈 Elite vs Low Performers:** -| Metric | 🏆 Elite | 😰 Low | -|--------|---------|-------| -| Deploy frequency | Multiple/day | Monthly | -| Lead time | < 1 hour | 1-6 months | -| Change failure rate | 0-15% | 46-60% | -| Recovery time | < 1 hour | 1 week+ | - ---- - -## 📍 Slide 11 – 💡 Section 2: Blocks & Error Handling - -* 🧱 **Blocks** = group related tasks -* 🔄 **Rescue** = handle failures -* ✅ **Always** = run regardless of outcome -* 🎯 Production-ready error handling - -```mermaid -flowchart TD - Block[🧱 Block] --> Try[🎯 Try Tasks] - Try -->|✅ Success| Always[✅ Always] - Try -->|❌ Failure| Rescue[🔧 Rescue] - Rescue --> Always -``` - ---- - -## 📍 Slide 12 – 🧱 Block Syntax - -```yaml -- name: Deploy application with error handling - block: - - name: Pull latest image - docker_image: - name: "{{ app_image }}" - source: pull - - - name: Start container - docker_container: - name: "{{ app_name }}" - image: "{{ app_image }}" - state: started - - rescue: - - name: Log failure - debug: - msg: "Deployment failed! Rolling back..." - - - name: Notify team - uri: - url: "{{ slack_webhook }}" - method: POST - body: '{"text": "Deployment failed!"}' - - always: - - name: Cleanup temp files - file: - path: /tmp/deploy - state: absent -``` - ---- - -## 📍 Slide 13 – 🛡️ Block Benefits - -```mermaid -flowchart LR - subgraph Without Blocks - T1[Task 1] --> T2[Task 2] - T2 -->|❌ Fail| Stop[😱 Playbook Stops] - end - subgraph With Blocks - B1[🧱 Block] -->|❌ Fail| R1[🔧 Rescue] - R1 --> A1[✅ Always] - end -``` - -**🛡️ Advantages:** -* 🔄 Graceful error handling -* 📊 Cleanup runs even on failure -* 🔔 Notification on failure -* 🎯 Apply settings to multiple tasks - -```yaml -- name: Docker installation - block: - - name: Task 1 - - name: Task 2 - - name: Task 3 - become: yes # 🔐 Applied to all tasks - when: install_docker # 🔀 Condition for all - tags: - - docker # 🏷️ Tag for all -``` - ---- - -## 📍 Slide 14 – 🏷️ Section 3: Tags Strategy - -* 🏷️ **Tags** = label tasks for selective execution -* 🎯 Run only what you need -* ⏱️ Speed up development and testing -* 🔧 Isolate specific operations - -```bash -# Run only docker tasks -ansible-playbook site.yml --tags "docker" - -# Skip common tasks -ansible-playbook site.yml --skip-tags "common" - -# List available tags -ansible-playbook site.yml --list-tags -``` - ---- - -## 📍 Slide 15 – 🏷️ Tag Design Patterns - -```yaml -# roles/web_app/tasks/main.yml -- name: Application deployment - block: - - name: Pull image - docker_image: - name: "{{ app_image }}" - source: pull - - - name: Deploy container - docker_container: - name: "{{ app_name }}" - state: started - tags: - - app_deploy - - deploy - -- name: Application wipe - block: - - name: Stop container - docker_container: - name: "{{ app_name }}" - state: absent - when: web_app_wipe | bool - tags: - - web_app_wipe -``` - -**🏷️ Tag Categories:** -* 🚀 **deploy**: Deployment tasks -* 🧹 **wipe**: Cleanup tasks -* 📦 **packages**: Package installation -* ⚙️ **config**: Configuration only - ---- - -## 📍 Slide 16 – ⚠️ Wipe Logic Pattern - -```mermaid -flowchart TD - Check{🔍 web_app_wipe = true?} - Check -->|No| Skip[⏭️ Skip wipe tasks] - Check -->|Yes| TagCheck{🏷️ --tags web_app_wipe?} - TagCheck -->|No| Skip2[⏭️ Skip: tag not specified] - TagCheck -->|Yes| Wipe[🧹 Execute wipe] -``` - -**🛡️ Double Safety Mechanism:** -* 📊 **Variable gate**: `web_app_wipe: false` by default -* 🏷️ **Tag gate**: Must specify `--tags web_app_wipe` -* ✅ Both required to execute dangerous tasks - -```bash -# Normal deploy (wipe doesn't run) -ansible-playbook deploy.yml - -# Wipe only -ansible-playbook deploy.yml -e "web_app_wipe=true" --tags web_app_wipe - -# Clean reinstall (wipe + deploy) -ansible-playbook deploy.yml -e "web_app_wipe=true" -``` - ---- - -## 📍 Slide 17 – 🐳 Docker Compose Deployment - -```mermaid -flowchart LR - Template[📝 Template] -->|Jinja2| Compose[🐳 docker-compose.yml] - Compose --> Deploy[🚀 Deploy] - Deploy --> Running[✅ Running] -``` - -**🐳 Why Docker Compose with Ansible?** -* 📝 Declarative container configuration -* 🔄 Managed by templates (dynamic values) -* 🔧 Easy updates and rollbacks -* 📊 Multi-container applications - ---- - -## 📍 Slide 18 – 📝 Jinja2 Templates - -```yaml -# roles/web_app/templates/docker-compose.yml.j2 -version: '3.8' - -services: - {{ app_name }}: - image: {{ docker_image }}:{{ docker_tag }} - container_name: {{ app_name }} - ports: - - "{{ app_port }}:{{ app_internal_port }}" - environment: -{% for key, value in app_env.items() %} - {{ key }}: "{{ value }}" -{% endfor %} - restart: unless-stopped - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:{{ app_internal_port }}/health"] - interval: 30s - timeout: 10s - retries: 3 -``` - -**📊 Variables Used:** -* 📦 `app_name`: Container name -* 🐳 `docker_image`: Image repository -* 🏷️ `docker_tag`: Image version -* 🔌 `app_port`: Exposed port - ---- - -## 📍 Slide 19 – 🚀 Deploy with Docker Compose Module - -```yaml -# roles/web_app/tasks/main.yml -- name: Create application directory - file: - path: "{{ compose_project_dir }}" - state: directory - mode: '0755' - -- name: Template docker-compose file - template: - src: docker-compose.yml.j2 - dest: "{{ compose_project_dir }}/docker-compose.yml" - mode: '0644' - notify: restart app - -- name: Deploy with Docker Compose - community.docker.docker_compose_v2: - project_src: "{{ compose_project_dir }}" - state: present - pull: always - register: deploy_result - -- name: Verify deployment - uri: - url: "http://localhost:{{ app_port }}/health" - status_code: 200 - retries: 5 - delay: 10 -``` - ---- - -## 📍 Slide 20 – 🔗 Role Dependencies - -```yaml -# roles/web_app/meta/main.yml ---- -dependencies: - - role: docker - vars: - docker_users: - - "{{ ansible_user }}" -``` - -**🔗 Dependency Benefits:** -* 🔄 Automatic execution order -* 📦 Ensures prerequisites -* 🎯 Self-contained roles - -```mermaid -flowchart LR - WebApp[📦 web_app role] -->|depends on| Docker[🐳 docker role] - Docker --> Tasks[🔧 Docker tasks run first] - Tasks --> WebAppTasks[🚀 Web app tasks run second] -``` - ---- - -## 📍 Slide 21 – 📊 Multi-Environment Deployment - -```yaml -# vars/app_python.yml -app_name: devops-python -docker_image: username/devops-info-service -docker_tag: latest -app_port: 8000 - -# vars/app_bonus.yml -app_name: devops-go -docker_image: username/devops-info-service-go -docker_tag: latest -app_port: 8001 -``` - -```yaml -# playbooks/deploy_python.yml ---- -- name: Deploy Python Application - hosts: webservers - become: yes - vars_files: - - ../vars/app_python.yml - roles: - - web_app -``` - -**🔄 Same role, different variables!** - ---- - -## 📍 Slide 22 – 🤖 CI/CD Integration - -```mermaid -flowchart LR - Push[📤 Git Push] --> CI[🔄 GitHub Actions] - CI --> Lint[📋 ansible-lint] - Lint --> Deploy[🚀 ansible-playbook] - Deploy --> Verify[✅ Verification] -``` - -**🤖 CI/CD Benefits:** -* 🔄 Automatic deployments on push -* 📋 Linting catches errors early -* 🔐 Secure credential handling -* 📊 Audit trail of deployments - ---- - -## 📍 Slide 23 – 📝 GitHub Actions Workflow - -```yaml -# .github/workflows/ansible-deploy.yml -name: Ansible Deployment - -on: - push: - branches: [main] - paths: - - 'ansible/**' - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install ansible-lint - run: pip install ansible ansible-lint - - name: Run ansible-lint - run: ansible-lint ansible/playbooks/*.yml - - deploy: - needs: lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Setup SSH - run: | - mkdir -p ~/.ssh - echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - - name: Deploy - run: | - cd ansible - echo "${{ secrets.VAULT_PASS }}" > .vault_pass - ansible-playbook playbooks/deploy.yml \ - --vault-password-file .vault_pass - rm .vault_pass -``` - ---- - -## 📍 Slide 24 – 🔐 Secrets in CI/CD - -```mermaid -flowchart TD - Secrets[🔐 GitHub Secrets] --> Workflow[🔄 Workflow] - Workflow --> TempFile[📄 Temp File] - TempFile --> Ansible[🔧 Ansible] - Ansible --> Delete[🗑️ Delete Temp File] -``` - -**🔐 Security Practices:** -* 📦 Store credentials in GitHub Secrets -* 📄 Write to temp file during run -* 🗑️ Delete immediately after use -* 🚫 Never echo secrets to logs - -```yaml -# Using secrets safely -- name: Deploy with Vault - env: - VAULT_PASS: ${{ secrets.ANSIBLE_VAULT_PASSWORD }} - run: | - echo "$VAULT_PASS" > /tmp/vault_pass - ansible-playbook playbook.yml --vault-password-file /tmp/vault_pass - rm /tmp/vault_pass # 🗑️ Cleanup! -``` - ---- - -## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L6_MID - ---- - -## 📍 Slide 26 – 📁 Section 4: Path Filters - -```yaml -on: - push: - paths: - - 'ansible/**' # 📁 Only ansible changes - - '!ansible/docs/**' # 📝 Exclude docs - - '.github/workflows/ansible-deploy.yml' -``` - -**📁 Path Filter Benefits:** -* ⚡ Faster CI (skip unnecessary runs) -* 💰 Lower costs (fewer minutes used) -* 🎯 Focused workflows - -```mermaid -flowchart TD - Push[📤 Push] --> Check{📁 ansible/** changed?} - Check -->|Yes| Run[🚀 Run Workflow] - Check -->|No| Skip[⏭️ Skip Workflow] -``` - ---- - -## 📍 Slide 27 – 📊 Deployment Metrics - -| 📊 Metric | 📏 Measures | 🏆 Target | -|-----------|------------|---------| -| ⏱️ **Deploy Time** | Push to production | < 15 minutes | -| 📦 **Deploy Frequency** | How often | Daily+ | -| ❌ **Failure Rate** | Failed deploys | < 15% | -| 🔄 **Rollback Time** | Recovery time | < 5 minutes | - -> 📚 These are DORA metrics for deployment performance. - -**🤔 Question:** How fast can you deploy and roll back? - ---- - -## 📍 Slide 28 – 🔄 Rollback Strategy - -```yaml -# Rollback by re-deploying previous version -- name: Rollback application - block: - - name: Stop current container - docker_container: - name: "{{ app_name }}" - state: stopped - - - name: Deploy previous version - community.docker.docker_compose_v2: - project_src: "{{ compose_project_dir }}" - state: present - vars: - docker_tag: "{{ rollback_tag }}" - - - name: Verify rollback - uri: - url: "http://localhost:{{ app_port }}/health" - status_code: 200 - retries: 3 - delay: 5 -``` - -**🔄 Rollback Options:** -* 🏷️ Deploy previous tag -* 📦 Docker Compose down/up -* 🔙 Git revert + CI/CD - ---- - -## 📍 Slide 29 – 🌊 From Manual to Automated Deployment - -```mermaid -flowchart LR - subgraph 😱 Manual - SSH[🔌 SSH to servers] - Commands[💻 Run commands] - Hope[🙏 Hope it works] - end - subgraph 🤖 Automated - Push[📤 Git push] - CI[🔄 CI/CD] - Deploy[🚀 Ansible] - end - Manual -->|🚀 Automate| Automated -``` - -**🎯 Automation State:** -* ⚡ Deploy in minutes, not hours -* 🔄 Every change through CI/CD -* 📈 Deploy with confidence - ---- - -## 📍 Slide 30 – 🏢 Section 5: Real World CI/CD - -## 📅 A Day with Automated Deployment - -**☀️ Morning:** -* 📊 Review deployment PR -* 👀 Check CI lint results -* ✅ Merge to main - -**🌤️ Afternoon:** -* 🤖 CI automatically deploys -* 📊 Monitoring shows healthy -* ☕ Coffee break - -**🌙 Evening:** -* 🚨 Bug found in production -* 🔙 Revert commit, CI deploys previous -* ⏱️ **5 minutes** to rollback - ---- - -## 📍 Slide 31 – 👥 Team Deployment Workflow - -| 👤 Role | 🎯 CI/CD Responsibility | -|---------|----------------------| -| 👨‍💻 **Developer** | Create PR, fix lint issues | -| 🔧 **DevOps** | Maintain workflows, roles | -| 👀 **Reviewer** | Approve changes | -| 🤖 **CI/CD** | Execute deployment | - -**🔗 GitOps Workflow:** -```mermaid -flowchart LR - PR[📝 Pull Request] --> Review[👀 Review] - Review --> Merge[✅ Merge] - Merge --> CI[🔄 CI/CD] - CI --> Deploy[🚀 Deploy] - Deploy --> Prod[🌐 Production] -``` - ---- - -## 📍 Slide 32 – 🔀 Deployment Strategies - -```mermaid -flowchart TD - subgraph Rolling - R1[🔄 Update 1 at a time] - end - subgraph Blue-Green - BG1[🔵 Blue: Current] - BG2[🟢 Green: New] - end - subgraph Canary - C1[🐤 Small % first] - C2[📊 Monitor] - C3[🚀 Full rollout] - end -``` - -| 🚀 Strategy | 🎯 Use Case | -|------------|----------| -| 🔄 **Rolling** | Gradual update, zero downtime | -| 🔵 **Blue-Green** | Instant switch, easy rollback | -| 🐤 **Canary** | Test with subset of users | - ---- - -## 📍 Slide 33 – 🧪 Deployment Verification - -```yaml -# Verify deployment success -- name: Wait for application - uri: - url: "http://{{ ansible_host }}:{{ app_port }}/health" - status_code: 200 - return_content: yes - register: health_check - until: health_check.status == 200 - retries: 10 - delay: 6 - -- name: Run smoke tests - command: "curl -f http://{{ ansible_host }}:{{ app_port }}/" - register: smoke_test - failed_when: smoke_test.rc != 0 - -- name: Log deployment success - debug: - msg: "✅ Deployment verified: {{ app_name }} is healthy" -``` - ---- - -## 📍 Slide 34 – 📈 Career Path: CD Skills - -```mermaid -flowchart LR - Junior[🌱 Junior: Manual deploys] --> Mid[💼 Mid: CI/CD pipelines] - Mid --> Senior[⭐ Senior: Zero-downtime strategies] - Senior --> Principal[🏆 Principal: Platform architecture] -``` - -**🛠️ Skills to Build:** -* 🔄 CI/CD pipeline design -* 🐳 Container orchestration -* 📊 Monitoring and alerting -* 🔙 Rollback strategies -* 🔐 Security in pipelines - ---- - -## 📍 Slide 35 – 🌍 Real Company Examples - -**🏢 Enterprise CD:** -* 📦 **Amazon**: Deploy every 11.7 seconds -* 🎬 **Netflix**: Canary deployments everywhere -* 🔍 **Google**: Feature flags for gradual rollout - -**☁️ CD Practices:** -* 🏦 **Stripe**: Shadow traffic for testing -* 📦 **Etsy**: 50+ deploys per day -* 🎮 **Spotify**: Squad-based ownership - -**📊 Stats:** -* 🚀 Elite teams deploy **on demand** -* ⏱️ Lead time: **less than 1 hour** -* 🔄 Recovery: **less than 1 hour** - ---- - -## 📍 Slide 36 – 🎯 Section 6: Reflection - -## 📝 Key Takeaways - -1. 🧱 **Blocks** enable graceful error handling -2. 🏷️ **Tags** allow selective execution -3. 🐳 **Docker Compose** templates for flexible deployments -4. 🔗 **Role dependencies** ensure proper ordering -5. 🤖 **CI/CD** automates the entire process - -> 💡 Small, frequent deployments are safer than big releases. - ---- - -## 📍 Slide 37 – 🧠 The Mindset Shift - -| 😰 Old Mindset | 🚀 CD Mindset | -|---------------|------------------| -| 🙅 "Deploy on weekends" | 🚀 "Deploy anytime" | -| 🚫 "Big releases quarterly" | 🔄 "Small releases daily" | -| 👉 "Manual verification" | 🤖 "Automated checks" | -| 😨 "Rollback is hard" | 💪 "Rollback in minutes" | -| 💻 "It works locally" | 🌍 "CI validates it" | - -> ❓ Which mindset describes your team? - ---- - -## 📍 Slide 38 – ✅ Your Progress - -## 🎓 What You Now Understand - -* ✅ Blocks with rescue and always -* ✅ Tag strategies for selective execution -* ✅ Docker Compose templates with Jinja2 -* ✅ Role dependencies and ordering -* ✅ CI/CD integration with GitHub Actions - -> 🚀 **You're ready for Lab 6: Advanced Ansible & CI/CD** - ---- - -## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L6_POST - ---- - -## 📍 Slide 40 – 🚀 What Comes Next - -## 📚 Next Lecture: Observability & Logging - -* 📋 Log aggregation with Loki -* 📊 Visualization with Grafana -* 🔍 LogQL query language -* 💻 Hands-on: Building a logging stack - -**🎉 Your continuous deployment journey continues.** - -> 🚀 From manual deploys to automated CI/CD — one commit at a time. - -```mermaid -flowchart LR - You[👤 You] --> CICD[🤖 CI/CD Skills] - CICD --> Automated[🚀 Automated Deploys] - Automated --> Career[🚀 Career Growth] -``` - -**👋 See you in the next lecture!** - ---- - -## 📚 Resources & Further Reading - -**📕 Books:** -* 📖 *Continuous Delivery* — Jez Humble -* 📖 *The DevOps Handbook* — Gene Kim et al. -* 📖 *Accelerate* — Nicole Forsgren - -**🔗 Links:** -* 🌐 [Ansible Blocks](https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html) -* 🌐 [Ansible Tags](https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html) -* 🌐 [GitHub Actions](https://docs.github.com/en/actions) - ---- diff --git a/lectures/lec7.md b/lectures/lec7.md deleted file mode 100644 index b00d0ad39f..0000000000 --- a/lectures/lec7.md +++ /dev/null @@ -1,849 +0,0 @@ -# 📌 Lecture 7 — Observability & Logging: From Blind to Insight - -## 📍 Slide 1 – 🚀 Welcome to Observability - -* 🌍 **Applications are running** — but what's happening inside? -* 😰 Without visibility, debugging is guesswork -* 🔍 **Observability** = understanding system state from outputs -* 🎯 This lecture: master logging with Loki, Promtail, and Grafana - -```mermaid -flowchart LR - App[📦 Application] -->|📋 Logs| Collect[🔧 Promtail] - Collect --> Store[💾 Loki] - Store --> View[📊 Grafana] - View --> Insight[💡 Insight] -``` - ---- - -## 📍 Slide 2 – 🎯 What You Will Learn - -* ✅ Understand the three pillars of observability -* ✅ Deploy Loki stack for log aggregation -* ✅ Query logs with LogQL -* ✅ Build effective log dashboards - -**🎓 Learning Outcomes:** -| # | Outcome | -|---|---------| -| 1 | 🧠 Differentiate logs, metrics, and traces | -| 2 | 🔍 Configure Loki 3.0 with TSDB storage | -| 3 | 🛠️ Write LogQL queries for filtering and aggregation | -| 4 | 🗺️ Design actionable log dashboards | - ---- - -## 📍 Slide 3 – 📋 How This Lecture Works - -* 📚 **Concepts + Configuration** — hands-on focus -* 🎮 **Real-world scenarios** — debugging production issues -* 📝 **3 quiz checkpoints**: PRE / MID / POST -* 🛠️ **Tool stack**: Loki + Promtail + Grafana - -**⏱️ Lecture Structure:** -``` -Section 0: Introduction (now) → 📝 PRE Quiz -Section 1: The Visibility Problem -Section 2: Observability Fundamentals -Section 3: Loki Stack Deep Dive → 📝 MID Quiz -Section 4: LogQL & Dashboards -Section 5: Production Logging -Section 6: Reflection → 📝 POST Quiz -``` - ---- - -## 📍 Slide 4 – ❓ The Big Question - -* 📊 **70%** of mean time to resolution is spent finding the problem -* ⏱️ Average time to detect issues: **hours to days** -* 💥 Without observability, debugging is **archaeology** - -> 💬 *"Users reported it's slow... but where?"* — Every on-call engineer, ever - -**🤔 Think about it:** -* How do you know your app is healthy? -* When users report issues, where do you look first? -* Can you trace a request through your system? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L7_PRE - ---- - -## 📍 Slide 6 – 🔥 Section 1: The Visibility Problem - -* 🙈 **No logs** = flying blind -* 📋 Logs scattered across servers -* 🔍 grep through SSH sessions -* 💥 Result: **hours spent finding problems** - -```mermaid -flowchart LR - Issue[🚨 Issue Reported] --> SSH1[🔌 SSH to Server 1] - SSH1 --> Grep1[🔍 grep logs] - Grep1 --> SSH2[🔌 SSH to Server 2] - SSH2 --> Grep2[🔍 grep logs] - Grep2 --> Hours[⏱️ Hours Later...] -``` - ---- - -## 📍 Slide 7 – 📋 Log Chaos - -* 📁 Logs in different formats -* 🖥️ Different locations per server -* 📅 Old logs deleted or rotated -* 💀 No correlation between services - -```mermaid -flowchart TD - App1[📦 App 1: JSON logs] - App2[📦 App 2: Plain text] - App3[📦 App 3: Custom format] - App1 --> Chaos[😱 No Unified View] - App2 --> Chaos - App3 --> Chaos -``` - -**📊 The Numbers:** -* 🔍 **73%** of engineers can't find logs quickly -* ⏱️ Average time to find relevant log: **15+ minutes** -* 💰 Cost of slow debugging: **$26,000/hour** (enterprise) - ---- - -## 📍 Slide 8 – 😱 "It's Working for Me" - -* 👥 Users report: *"App is slow"* -* 🤷 Team responds: *"Works for me"* -* 🔍 No data to prove either side -* 💀 Frustration all around - -> ⚠️ **Without observability, you can't prove anything** - -**😰 Signs of Poor Observability:** -* 🔇 "Check the server logs" (which server?) -* 📝 "It was working yesterday" (what changed?) -* 🐌 "Let's restart and see" (cargo cult debugging) -* 🚪 Blame instead of data - -**💬 Discussion:** How do you currently debug production issues? - ---- - -## 📍 Slide 9 – 🔥 The Alert Fatigue Problem - -* 🚨 Too many alerts = no alerts -* 📧 Inbox full of "warnings" -* 😴 Real issues get ignored -* 💀 On-call burnout - -> ⚠️ **Noise drowns out signal** - -```mermaid -flowchart LR - Alerts[🚨 1000 Alerts/day] --> Ignore[😴 Alert Fatigue] - Ignore --> Miss[🙈 Miss Real Issues] - Miss --> Outage[💥 Production Outage] -``` - ---- - -## 📍 Slide 10 – 💸 The Cost of Blind Operations - -| 🔥 Problem | 💥 Impact | -|------------|-----------| -| 🐢 Slow debugging | Hours/days to resolve | -| 📋 No correlation | Can't trace requests | -| 👉 Finger pointing | No data, just blame | -| 🙈 Hidden failures | Issues go unnoticed | - -**📈 Real Numbers:** -* 🏢 **MTTR without observability**: 4+ hours -* 🚀 **MTTR with observability**: < 30 minutes -* 💰 **ROI of observability**: 10x+ reduction in incident cost - -**💰 Cost Example:** -* 💵 1-hour outage: **$300,000** -* 🔍 Good observability: **$30/month** -* 🧮 Break-even: **first 6 seconds of prevented downtime** - ---- - -## 📍 Slide 11 – 💡 Section 2: What Observability Is - -* 🔍 **Observability** = understanding system state from external outputs -* 📊 **Three pillars**: Logs, Metrics, Traces -* 🎯 Answer: "Why is this happening?" -* 🚫 NOT just monitoring (which asks "Is it working?") - -```mermaid -flowchart TD - Obs[🔍 Observability] - Obs --> Logs[📋 Logs: What happened] - Obs --> Metrics[📊 Metrics: How much] - Obs --> Traces[🔗 Traces: Where/how long] -``` - -**📖 Definition:** -> *Observability is the ability to understand the internal state of a system by examining its external outputs — logs, metrics, and traces.* - ---- - -## 📍 Slide 12 – 📋 The Three Pillars - -| 📊 Pillar | 🎯 Answers | 🛠️ Tools | -|-----------|-----------|----------| -| 📋 **Logs** | What happened? | Loki, ELK | -| 📊 **Metrics** | How much/fast? | Prometheus | -| 🔗 **Traces** | Where did time go? | Jaeger, Tempo | - -```mermaid -flowchart LR - subgraph Logs - L1[📝 Error: Connection refused] - end - subgraph Metrics - M1[📈 99.9% availability] - end - subgraph Traces - T1[🔗 Request: 250ms total] - end -``` - -**🎯 Together they tell the full story** - ---- - -## 📍 Slide 13 – 📋 Logs: What Happened - -* 📝 **Events** with timestamps -* 🔍 Detailed context for debugging -* 📊 Can be structured (JSON) or unstructured -* ⚠️ High volume, high storage - -```json -{ - "timestamp": "2024-01-15T10:23:45Z", - "level": "ERROR", - "service": "user-api", - "message": "Database connection failed", - "error": "Connection refused", - "host": "server-1" -} -``` - -**🎯 Use logs when:** -* 🔍 Debugging specific errors -* 📋 Understanding request flow -* 🛡️ Security auditing - ---- - -## 📍 Slide 14 – 📊 Why Structured Logging? - -```mermaid -flowchart LR - subgraph ❌ Unstructured - U1[ERROR: Failed to connect to db at 10:23] - end - subgraph ✅ Structured - S1[JSON with fields] - end - U1 --> Hard[😰 Hard to parse] - S1 --> Easy[✅ Easy to query] -``` - -**❌ Unstructured:** -``` -ERROR 2024-01-15 10:23:45 Connection to database failed on server-1 -``` - -**✅ Structured (JSON):** -```json -{"timestamp":"2024-01-15T10:23:45Z","level":"ERROR","msg":"Connection failed","server":"server-1"} -``` - -**🎯 Benefits:** -* 🔍 Easy to filter and search -* 📊 Aggregate by any field -* 🤖 Machine-parseable - ---- - -## 📍 Slide 15 – ⚡ Loki vs ELK - -| 📋 Aspect | 📊 Loki | 🔍 ELK Stack | -|-----------|---------|-------------| -| 🏗️ Architecture | Lightweight | Heavy | -| 💾 Storage | Index labels only | Full-text index | -| 📊 Query | LogQL | Lucene | -| 💰 Cost | Low (storage) | High (compute) | -| 🎯 Best for | Cloud-native | Enterprise search | - -```mermaid -flowchart LR - Loki[📊 Loki] -->|Labels| Index1[🏷️ Small Index] - ELK[🔍 ELK] -->|Full Text| Index2[📚 Large Index] - Index1 --> Cost1[💰 Low Cost] - Index2 --> Cost2[💸 High Cost] -``` - -> 🔥 **Loki**: "Like Prometheus, but for logs" - ---- - -## 📍 Slide 16 – 🎮 Section 3: Loki Stack Deep Dive - -## 🏗️ Loki Architecture - -* 💾 **Loki**: Log storage (index + chunks) -* 🔧 **Promtail**: Log collector (agent) -* 📊 **Grafana**: Visualization - -```mermaid -flowchart LR - App1[📦 App 1] --> Promtail[🔧 Promtail] - App2[📦 App 2] --> Promtail - Promtail -->|Push| Loki[💾 Loki] - Loki --> Grafana[📊 Grafana] -``` - -**🎮 Let's build a logging stack.** - ---- - -## 📍 Slide 17 – 💾 Loki 3.0 Features - -* 🚀 **TSDB index**: 10x faster queries -* 📊 **Structured metadata**: First-class support -* 💾 **Better compression**: Lower storage costs -* 🔍 **Schema v13**: Latest and recommended - -```yaml -# loki/config.yml -schema_config: - configs: - - from: 2024-01-01 - store: tsdb # 🚀 New fast store - object_store: filesystem - schema: v13 # 📊 Latest schema - index: - prefix: index_ - period: 24h -``` - -**🎯 Always use TSDB for new deployments!** - ---- - -## 📍 Slide 18 – ⚙️ Loki Configuration - -```yaml -# loki/config.yml -auth_enabled: false - -server: - http_listen_port: 3100 - -common: - path_prefix: /loki - storage: - filesystem: - chunks_directory: /loki/chunks - rules_directory: /loki/rules - replication_factor: 1 - ring: - instance_addr: 127.0.0.1 - kvstore: - store: inmemory - -limits_config: - retention_period: 168h # 🗓️ 7 days -``` - -**🔑 Key Settings:** -* 🔐 `auth_enabled`: False for testing -* 💾 `storage`: Where logs are stored -* 🗓️ `retention_period`: How long to keep logs - ---- - -## 📍 Slide 19 – 🔧 Promtail Configuration - -```yaml -# promtail/config.yml -server: - http_listen_port: 9080 - -positions: - filename: /tmp/positions.yaml - -clients: - - url: http://loki:3100/loki/api/v1/push - -scrape_configs: - - job_name: docker - docker_sd_configs: - - host: unix:///var/run/docker.sock - refresh_interval: 5s - relabel_configs: - - source_labels: ['__meta_docker_container_name'] - regex: '/(.*)' - target_label: 'container' -``` - -**🔑 Key Components:** -* 📋 `positions`: Track what's been read -* 🔗 `clients`: Where to send logs -* 🐳 `docker_sd_configs`: Auto-discover containers - ---- - -## 📍 Slide 20 – 🐳 Docker Compose Stack - -```yaml -# docker-compose.yml -version: '3.8' - -services: - loki: - image: grafana/loki:3.0.0 - ports: - - "3100:3100" - volumes: - - ./loki/config.yml:/etc/loki/config.yml - - loki-data:/loki - command: -config.file=/etc/loki/config.yml - - promtail: - image: grafana/promtail:3.0.0 - volumes: - - ./promtail/config.yml:/etc/promtail/config.yml - - /var/lib/docker/containers:/var/lib/docker/containers:ro - - /var/run/docker.sock:/var/run/docker.sock:ro - command: -config.file=/etc/promtail/config.yml - - grafana: - image: grafana/grafana:11.3.0 - ports: - - "3000:3000" - volumes: - - grafana-data:/var/lib/grafana -``` - ---- - -## 📍 Slide 21 – 🏷️ Labels: The Key Concept - -```mermaid -flowchart TD - Log[📋 Log Entry] --> Labels[🏷️ Labels] - Labels --> App[app=web-api] - Labels --> Env[env=production] - Labels --> Level[level=error] - App --> Query[🔍 Query by Labels] - Env --> Query - Level --> Query -``` - -**🏷️ Labels = How Loki indexes logs** - -```logql -# Query logs by labels -{app="web-api", env="production"} - -# Filter errors -{app="web-api"} |= "error" -``` - -**⚠️ Label Best Practices:** -* 🔢 Keep cardinality low (< 10 values per label) -* 🚫 Never use high-cardinality fields (user IDs, request IDs) -* 🏷️ Use for: app name, environment, service - ---- - -## 📍 Slide 22 – 🔍 LogQL Basics - -```mermaid -flowchart LR - Selector[🏷️ Stream Selector] --> Filter[🔍 Line Filter] - Filter --> Parser[📊 Parser] - Parser --> Result[📋 Results] -``` - -**🔍 Query Structure:** -```logql -{label="value"} |= "filter" | json | field="value" -``` - -**📋 Examples:** -```logql -# All logs from container -{container="web-api"} - -# Errors only -{container="web-api"} |= "error" - -# Parse JSON, filter by level -{container="web-api"} | json | level="ERROR" - -# Count errors per minute -rate({container="web-api"} |= "error" [1m]) -``` - ---- - -## 📍 Slide 23 – 📊 LogQL Operators - -| 🔧 Operator | 🎯 Purpose | 📝 Example | -|-------------|----------|---------| -| `\|=` | Contains | `\|= "error"` | -| `!=` | Not contains | `!= "debug"` | -| `\|~` | Regex match | `\|~ "error\|warn"` | -| `\| json` | Parse JSON | `\| json` | -| `\| logfmt` | Parse logfmt | `\| logfmt` | -| `rate()` | Logs per second | `rate({app="x"}[5m])` | - -**📊 Aggregation:** -```logql -# Logs per second by container -sum by (container) (rate({job="docker"}[1m])) - -# Count by level -sum by (level) (count_over_time({app="web"} | json [5m])) -``` - ---- - -## 📍 Slide 24 – 🐍 Structured Logging in Python - -```python -import logging -import json -from datetime import datetime - -class JSONFormatter(logging.Formatter): - def format(self, record): - log_obj = { - "timestamp": datetime.utcnow().isoformat() + "Z", - "level": record.levelname, - "message": record.getMessage(), - "logger": record.name, - } - if record.exc_info: - log_obj["exception"] = self.formatException(record.exc_info) - return json.dumps(log_obj) - -# Setup -handler = logging.StreamHandler() -handler.setFormatter(JSONFormatter()) -logger = logging.getLogger() -logger.addHandler(handler) -logger.setLevel(logging.INFO) - -# Usage -logger.info("User logged in", extra={"user_id": 123}) -``` - ---- - -## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L7_MID - ---- - -## 📍 Slide 26 – 📊 Section 4: Building Dashboards - -## 🎨 Dashboard Design Principles - -* 🎯 **Purpose**: What question does it answer? -* 📊 **Hierarchy**: Most important at top -* 🎨 **Color**: Red = bad, Green = good -* 🔄 **Refresh**: Auto-refresh for real-time - -```mermaid -flowchart TD - Top[🚨 Alerts & Errors] - Top --> Middle[📊 Request Rates] - Middle --> Bottom[📋 Log Stream] -``` - ---- - -## 📍 Slide 27 – 📊 Essential Log Panels - -**1️⃣ Log Stream (Logs visualization)** -```logql -{app=~"devops-.*"} -``` - -**2️⃣ Error Rate (Time series)** -```logql -sum by (app) (rate({app=~"devops-.*"} |= "ERROR" [1m])) -``` - -**3️⃣ Request Rate (Time series)** -```logql -sum by (app) (rate({app=~"devops-.*"} [1m])) -``` - -**4️⃣ Level Distribution (Pie chart)** -```logql -sum by (level) (count_over_time({app=~"devops-.*"} | json [5m])) -``` - ---- - -## 📍 Slide 28 – 📊 Grafana Panel Types - -| 📊 Type | 🎯 Use For | -|---------|----------| -| 📋 **Logs** | Raw log entries | -| 📈 **Time series** | Trends over time | -| 📊 **Stat** | Single values | -| 🥧 **Pie chart** | Distribution | -| 📋 **Table** | Structured data | -| 🌡️ **Gauge** | Current status | - -```mermaid -flowchart LR - Logs[📋 Logs] --> Debug[🔍 Debugging] - TimeSeries[📈 Time Series] --> Trends[📊 Trends] - Stat[📊 Stat] --> KPIs[🎯 KPIs] -``` - ---- - -## 📍 Slide 29 – 📊 Logging Metrics - -| 📊 Metric | 📏 Measures | 🏆 Target | -|-----------|------------|---------| -| 📋 **Log Volume** | Logs per second | Stable | -| ❌ **Error Rate** | Errors per minute | < 1% | -| ⏱️ **Query Time** | Time to find logs | < 30s | -| 💾 **Retention** | How long kept | 7+ days | - -> 📚 These metrics indicate logging health. - -**🤔 Question:** How quickly can you find relevant logs? - ---- - -## 📍 Slide 30 – 🌊 From Blind to Observable - -```mermaid -flowchart LR - subgraph 😱 Blind - SSH[🔌 SSH grep] - Guess[🤷 Guesswork] - Slow[⏱️ Hours] - end - subgraph 🔍 Observable - Dashboard[📊 Dashboard] - Query[🔍 LogQL] - Fast[⚡ Minutes] - end - Blind -->|🚀 Loki| Observable -``` - -**🎯 Observability State:** -* ⚡ Find issues in minutes, not hours -* 🔄 Unified view across all services -* 📈 Data-driven debugging - ---- - -## 📍 Slide 31 – 🏢 Section 5: Production Logging - -## 📅 A Day with Observability - -**☀️ Morning:** -* 📊 Check Grafana dashboard — all green ✅ -* 📋 Review overnight logs — no anomalies -* ☕ Coffee with confidence - -**🌤️ Afternoon:** -* 🚨 Alert: Error rate spike -* 🔍 LogQL: `{app="api"} |= "error" | json | level="ERROR"` -* 🔧 Found: Database timeout -* ⏱️ **10 minutes** to identify root cause - -**🌙 Evening:** -* 📊 Review error trends -* 📝 Create runbook for similar issues -* 🏠 Go home knowing you can debug remotely - ---- - -## 📍 Slide 32 – 👥 Team Logging Workflow - -| 👤 Role | 🎯 Observability Responsibility | -|---------|----------------------| -| 👨‍💻 **Developer** | Add structured logging | -| 🔧 **DevOps** | Maintain logging stack | -| 🛡️ **SRE** | Build dashboards, respond to alerts | -| 📊 **On-call** | Use logs for incident response | - -**🔗 Incident Response Flow:** -```mermaid -flowchart LR - Alert[🚨 Alert] --> Dashboard[📊 Dashboard] - Dashboard --> LogQL[🔍 LogQL Query] - LogQL --> RootCause[🎯 Root Cause] - RootCause --> Fix[🔧 Fix] -``` - ---- - -## 📍 Slide 33 – 🔐 Production Considerations - -```yaml -# Production settings -deploy: - resources: - limits: - memory: 1G - cpus: '1.0' - reservations: - memory: 512M - -healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3100/ready"] - interval: 10s - timeout: 5s - retries: 5 -``` - -**🛡️ Production Checklist:** -* 💾 Persistent volumes for data -* 🔐 Secure Grafana (disable anonymous) -* 📊 Resource limits on all services -* 🏥 Health checks enabled -* 🗓️ Retention policies configured - ---- - -## 📍 Slide 34 – 📈 Career Path: Observability Skills - -```mermaid -flowchart LR - Junior[🌱 Junior: Basic logging] --> Mid[💼 Mid: Structured logging & dashboards] - Mid --> Senior[⭐ Senior: Full observability stack] - Senior --> Principal[🏆 Principal: Observability strategy] -``` - -**🛠️ Skills to Build:** -* 📋 Structured logging patterns -* 🔍 LogQL/PromQL fluency -* 📊 Dashboard design -* 🚨 Alerting strategies -* 🔗 Distributed tracing - ---- - -## 📍 Slide 35 – 🌍 Real Company Examples - -**🏢 Observability Leaders:** -* 🎬 **Netflix**: Custom observability platform -* 🔍 **Google**: Invented Dapper (tracing) -* 📦 **Uber**: Jaeger (open-source tracing) - -**☁️ Modern Practices:** -* 🏦 **Stripe**: Structured logging everywhere -* 📦 **Spotify**: Centralized logging for 1000+ microservices -* 🎮 **Riot Games**: Real-time game telemetry - -**📊 Stats:** -* 🔍 **80%** of debugging time is finding problems -* ⏱️ Good observability reduces MTTR by **70%+** -* 💰 ROI: **10-100x** in reduced incident costs - ---- - -## 📍 Slide 36 – 🎯 Section 6: Reflection - -## 📝 Key Takeaways - -1. 🔍 **Observability = Logs + Metrics + Traces** -2. 📋 **Structured logging** enables powerful queries -3. 🏷️ **Labels** are how Loki indexes (keep cardinality low) -4. 📊 **LogQL** is your query language -5. 📈 **Dashboards** provide unified visibility - -> 💡 You can't fix what you can't see. Observability gives you eyes. - ---- - -## 📍 Slide 37 – 🧠 The Mindset Shift - -| 😰 Old Mindset | 🔍 Observable Mindset | -|---------------|------------------| -| 🙅 "SSH and grep" | 📊 "Query Grafana" | -| 🚫 "Check the logs somewhere" | 🔍 "All logs in one place" | -| 👉 "It's probably X" | 📊 "Data shows it's Y" | -| 😨 "Debugging takes hours" | ⚡ "Root cause in minutes" | -| 💻 "Works on my machine" | 🌍 "Production shows different" | - -> ❓ Which mindset describes your team? - ---- - -## 📍 Slide 38 – ✅ Your Progress - -## 🎓 What You Now Understand - -* ✅ The three pillars of observability -* ✅ Loki architecture and configuration -* ✅ LogQL query syntax -* ✅ Building effective dashboards -* ✅ Production logging best practices - -> 🚀 **You're ready for Lab 7: Loki Logging Stack** - ---- - -## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L7_POST - ---- - -## 📍 Slide 40 – 🚀 What Comes Next - -## 📚 Next Lecture: Monitoring with Prometheus - -* 📊 Metrics collection and storage -* 🔢 PromQL query language -* 📈 Application instrumentation -* 💻 Hands-on: Building metrics dashboards - -**🎉 Your observability journey continues.** - -> 🔍 From blind operations to insight — one query at a time. - -```mermaid -flowchart LR - You[👤 You] --> Obs[🔍 Observability Skills] - Obs --> Insight[💡 System Insight] - Insight --> Career[🚀 Career Growth] -``` - -**👋 See you in the next lecture!** - ---- - -## 📚 Resources & Further Reading - -**📕 Books:** -* 📖 *Observability Engineering* — Charity Majors -* 📖 *Distributed Systems Observability* — Cindy Sridharan -* 📖 *The Art of Monitoring* — James Turnbull - -**🔗 Links:** -* 🌐 [Grafana Loki Documentation](https://grafana.com/docs/loki/latest/) -* 🌐 [LogQL Reference](https://grafana.com/docs/loki/latest/query/) -* 🌐 [Promtail Configuration](https://grafana.com/docs/loki/latest/send-data/promtail/) - ---- diff --git a/lectures/lec8.md b/lectures/lec8.md deleted file mode 100644 index 0b921df100..0000000000 --- a/lectures/lec8.md +++ /dev/null @@ -1,799 +0,0 @@ -# 📌 Lecture 8 — Monitoring with Prometheus: From Guessing to Measuring - -## 📍 Slide 1 – 🚀 Welcome to Metrics Monitoring - -* 🌍 **Logs tell you what happened** — but how much and how fast? -* 😰 Without metrics, capacity planning is guesswork -* 📊 **Prometheus** = the industry standard for metrics -* 🎯 This lecture: master metrics collection, PromQL, and dashboards - -```mermaid -flowchart LR - App[📦 Application] -->|📊 Metrics| Prometheus[💾 Prometheus] - Prometheus --> Grafana[📊 Grafana] - Grafana --> Insight[💡 Insight] -``` - ---- - -## 📍 Slide 2 – 🎯 What You Will Learn - -* ✅ Understand metrics types and instrumentation -* ✅ Configure Prometheus for metrics collection -* ✅ Query metrics with PromQL -* ✅ Build effective monitoring dashboards - -**🎓 Learning Outcomes:** -| # | Outcome | -|---|---------| -| 1 | 🧠 Differentiate Counter, Gauge, Histogram | -| 2 | 🔍 Configure Prometheus scrape targets | -| 3 | 🛠️ Write PromQL queries for analysis | -| 4 | 🗺️ Design RED method dashboards | - ---- - -## 📍 Slide 3 – 📋 How This Lecture Works - -* 📚 **Concepts + Instrumentation** — hands-on focus -* 🎮 **Real-world scenarios** — performance monitoring -* 📝 **3 quiz checkpoints**: PRE / MID / POST -* 🛠️ **Methods**: RED, USE, Four Golden Signals - -**⏱️ Lecture Structure:** -``` -Section 0: Introduction (now) → 📝 PRE Quiz -Section 1: The Monitoring Problem -Section 2: Prometheus Fundamentals -Section 3: Application Instrumentation → 📝 MID Quiz -Section 4: PromQL & Dashboards -Section 5: Production Monitoring -Section 6: Reflection → 📝 POST Quiz -``` - ---- - -## 📍 Slide 4 – ❓ The Big Question - -* 📊 **83%** of organizations can't predict performance issues -* ⏱️ Average time to detect capacity problems: **too late** -* 💥 Without metrics, you're **reactive, not proactive** - -> 💬 *"Is the server slow or is it just me?"* — Everyone, always - -**🤔 Think about it:** -* How do you know if your app can handle more load? -* When did response times start degrading? -* How much headroom do you have? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L8_PRE - ---- - -## 📍 Slide 6 – 🔥 Section 1: The Monitoring Problem - -* 🤷 **No metrics** = can't measure performance -* 📊 Users complain before you know there's a problem -* 🔍 Can't identify bottlenecks -* 💥 Result: **reactive firefighting** - -```mermaid -flowchart LR - Users[👥 Users Complain] --> Support[📞 Support Ticket] - Support --> Team[👨‍💻 Team Investigates] - Team --> Guess[🤷 Guesswork] - Guess --> Hours[⏱️ Hours Later...] -``` - ---- - -## 📍 Slide 7 – 📊 Metrics vs Logs - -```mermaid -flowchart TD - subgraph 📋 Logs - L1[What happened?] - L2[Detailed events] - L3[High cardinality] - end - subgraph 📊 Metrics - M1[How much/fast?] - M2[Aggregated numbers] - M3[Low cardinality] - end -``` - -| 📋 Aspect | 📊 Metrics | 📝 Logs | -|-----------|----------|---------| -| 🎯 Question | How much? | What happened? | -| 📈 Volume | Low | High | -| 💾 Storage | Small | Large | -| 🔍 Analysis | Trends, alerts | Debugging | -| ⏱️ Retention | Long (months) | Short (days) | - -> 🔥 **Use both**: Logs for debugging, metrics for monitoring - ---- - -## 📍 Slide 8 – 😱 Alert Blindness - -* 🚨 No alerts = problems go unnoticed -* 📧 Too many alerts = alert fatigue -* 🔍 Wrong thresholds = false positives -* 💀 On-call burnout - -> ⚠️ **Good metrics = actionable alerts** - -```mermaid -flowchart LR - NoMetrics[🙈 No Metrics] --> NoAlerts[🔇 No Alerts] - NoAlerts --> UserReports[👥 Users Report] - UserReports --> Scramble[😱 Scramble] -``` - ---- - -## 📍 Slide 9 – 😨 Capacity Planning Without Metrics - -* 📅 "We need more servers" — but how many? -* 🔮 Crystal ball capacity planning -* 💰 Over-provision (waste money) or under-provision (outages) -* 💀 No data to justify decisions - -> ⚠️ **Without metrics, capacity planning is gambling** - -**💬 Discussion:** How does your team plan capacity? - ---- - -## 📍 Slide 10 – 💸 The Cost of Blind Monitoring - -| 🔥 Problem | 💥 Impact | -|------------|-----------| -| 🐢 No baseline | Can't detect degradation | -| 📊 No trends | Can't predict growth | -| 👉 No attribution | Can't identify bottlenecks | -| 🙈 No thresholds | Can't alert proactively | - -**📈 Real Numbers:** -* 🏢 **Reactive incident detection**: Users report first (30+ min delay) -* 🚀 **Proactive with metrics**: Alert in seconds -* 💰 **Cost of 30-minute delay**: $150,000+ (enterprise) - ---- - -## 📍 Slide 11 – 💡 Section 2: What Prometheus Is - -* 📊 **Time-series database** for metrics -* 🔄 **Pull-based** model — scrapes targets -* 📈 **PromQL** — powerful query language -* 🎯 Industry standard for cloud-native monitoring - -```mermaid -flowchart LR - App1[📦 App /metrics] --> Prometheus[💾 Prometheus] - App2[📦 Service /metrics] --> Prometheus - Prometheus -->|⏰ Every 15s| Scrape[🔄 Pull Metrics] -``` - -**📖 Definition:** -> *Prometheus is an open-source monitoring system that collects metrics from targets by scraping HTTP endpoints, stores them in a time-series database, and provides a powerful query language (PromQL) for analysis.* - ---- - -## 📍 Slide 12 – 🔄 Pull vs Push Model - -```mermaid -flowchart TD - subgraph Pull (Prometheus) - P1[💾 Prometheus] -->|🔄 Scrape| T1[📦 Target] - P1 -->|🔄 Scrape| T2[📦 Target] - end - subgraph Push (StatsD) - S1[📦 App] -->|📤 Push| D1[💾 Collector] - S2[📦 App] -->|📤 Push| D1 - end -``` - -**🔄 Pull Benefits:** -* 🔍 Prometheus controls the rate -* ✅ Know immediately if target is down (scrape fails) -* 🎯 Apps don't need to know about monitoring -* 🔧 Easy service discovery - ---- - -## 📍 Slide 13 – 🏗️ Prometheus Architecture - -```mermaid -flowchart TD - Targets[📦 Targets /metrics] --> Prometheus[💾 Prometheus TSDB] - Prometheus --> AlertManager[🚨 AlertManager] - Prometheus --> Grafana[📊 Grafana] - Prometheus --> API[🔗 HTTP API] - AlertManager --> Slack[💬 Slack] - AlertManager --> PagerDuty[📟 PagerDuty] -``` - -| 🧱 Component | 🎯 Purpose | -|-------------|----------| -| 💾 **Prometheus** | Scrape, store, query | -| 📦 **Targets** | Expose /metrics endpoint | -| 🚨 **AlertManager** | Handle alerts | -| 📊 **Grafana** | Visualization | - ---- - -## 📍 Slide 14 – 📊 Metric Types - -```mermaid -flowchart LR - Counter[🔢 Counter] --> Always[Only goes UP] - Gauge[📊 Gauge] --> UpDown[Goes up AND down] - Histogram[📈 Histogram] --> Distribution[Value distribution] -``` - -| 📊 Type | 🎯 Use For | 📝 Example | -|---------|----------|---------| -| 🔢 **Counter** | Cumulative events | Total requests | -| 📊 **Gauge** | Current value | Temperature, memory | -| 📈 **Histogram** | Distribution | Request latency | -| 📊 **Summary** | Percentiles | Pre-calculated p95 | - ---- - -## 📍 Slide 15 – 🔢 Counter Deep Dive - -```python -from prometheus_client import Counter - -# 🔢 Counter: Only goes up -http_requests_total = Counter( - 'http_requests_total', - 'Total HTTP requests', - ['method', 'endpoint', 'status'] -) - -# Usage -http_requests_total.labels(method='GET', endpoint='/', status='200').inc() -``` - -**📊 Query Patterns:** -```promql -# Total requests -http_requests_total - -# Requests per second (rate over 5m) -rate(http_requests_total[5m]) - -# Requests per second by endpoint -sum by (endpoint) (rate(http_requests_total[5m])) -``` - -**⚠️ Counter Rule:** Use `rate()` to get per-second values - ---- - -## 📍 Slide 16 – 🎮 Section 3: Application Instrumentation - -## 🐍 Python prometheus_client - -```python -from prometheus_client import Counter, Gauge, Histogram, generate_latest -from flask import Flask, Response - -app = Flask(__name__) - -# 📊 Define metrics -requests = Counter('http_requests', 'Total requests', ['method', 'path']) -latency = Histogram('http_latency_seconds', 'Request latency', ['path']) -in_progress = Gauge('http_in_progress', 'Requests in progress') - -@app.route('/metrics') -def metrics(): - return Response(generate_latest(), content_type='text/plain') -``` - -**🎮 Let's instrument an application.** - ---- - -## 📍 Slide 17 – 📊 Histogram Deep Dive - -```python -from prometheus_client import Histogram - -# 📈 Histogram with buckets -request_latency = Histogram( - 'http_request_duration_seconds', - 'Request latency in seconds', - ['method', 'endpoint'], - buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 5.0] # 🪣 Custom buckets -) - -# Usage -with request_latency.labels(method='GET', endpoint='/').time(): - # ... handle request ... - pass -``` - -**📊 Query Patterns:** -```promql -# 95th percentile latency -histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) - -# Average latency -rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m]) -``` - ---- - -## 📍 Slide 18 – 📈 The RED Method - -```mermaid -flowchart LR - R[🔴 Rate] --> Requests[Requests per second] - E[🟡 Errors] --> Failures[Error rate] - D[🔵 Duration] --> Latency[Response time] -``` - -**📊 RED Method for Request-Driven Services:** - -| 📊 Metric | 🎯 Question | 📝 PromQL | -|-----------|----------|---------| -| 🔴 **Rate** | How busy? | `rate(requests[5m])` | -| 🟡 **Errors** | How often failing? | `rate(errors[5m])` | -| 🔵 **Duration** | How slow? | `histogram_quantile(0.95, ...)` | - -**🎯 If you monitor only 3 things, monitor these!** - ---- - -## 📍 Slide 19 – 📈 The USE Method - -```mermaid -flowchart LR - U[📊 Utilization] --> HowMuch[% resource busy] - S[📊 Saturation] --> Queuing[Extra work waiting] - E[📊 Errors] --> Failures[Error count] -``` - -**📊 USE Method for Resources (CPU, Memory, Disk):** - -| 📊 Metric | 🎯 Example | -|-----------|----------| -| 📊 **Utilization** | CPU at 80% | -| 📊 **Saturation** | 10 requests queued | -| 📊 **Errors** | Disk I/O errors | - -**🎯 USE for resources, RED for services** - ---- - -## 📍 Slide 20 – ⚙️ Prometheus Configuration - -```yaml -# prometheus/prometheus.yml -global: - scrape_interval: 15s - evaluation_interval: 15s - -scrape_configs: - - job_name: 'prometheus' - static_configs: - - targets: ['localhost:9090'] - - - job_name: 'app' - static_configs: - - targets: ['app-python:8000'] - metrics_path: '/metrics' - - - job_name: 'loki' - static_configs: - - targets: ['loki:3100'] -``` - -**🔑 Key Settings:** -* ⏱️ `scrape_interval`: How often to collect (15s default) -* 🎯 `targets`: What to scrape -* 📍 `metrics_path`: Where metrics are exposed - ---- - -## 📍 Slide 21 – 🎯 Scrape Targets - -```mermaid -flowchart TD - Prometheus[💾 Prometheus] -->|🔄 Scrape| App[📦 app:8000/metrics] - Prometheus -->|🔄 Scrape| Loki[📦 loki:3100/metrics] - Prometheus -->|🔄 Scrape| Grafana[📦 grafana:3000/metrics] - Prometheus -->|🔄 Scrape| Self[📦 prometheus:9090/metrics] -``` - -**📊 Verify Targets:** -```bash -# Check targets status -curl http://localhost:9090/api/v1/targets - -# Web UI -http://localhost:9090/targets -``` - -**✅ All targets should show `UP`** - ---- - -## 📍 Slide 22 – 📊 /metrics Endpoint Format - -``` -# HELP http_requests_total Total HTTP requests -# TYPE http_requests_total counter -http_requests_total{method="GET",endpoint="/",status="200"} 1234 -http_requests_total{method="GET",endpoint="/health",status="200"} 567 -http_requests_total{method="POST",endpoint="/api",status="201"} 89 - -# HELP http_request_duration_seconds Request latency -# TYPE http_request_duration_seconds histogram -http_request_duration_seconds_bucket{le="0.01"} 100 -http_request_duration_seconds_bucket{le="0.05"} 200 -http_request_duration_seconds_bucket{le="0.1"} 250 -http_request_duration_seconds_bucket{le="+Inf"} 300 -http_request_duration_seconds_sum 45.67 -http_request_duration_seconds_count 300 -``` - -**📊 Format:** `metric_name{labels} value` - ---- - -## 📍 Slide 23 – 🏷️ Labels Best Practices - -```python -# ✅ Good: Low cardinality labels -http_requests.labels(method='GET', status='200', endpoint='/api') - -# ❌ Bad: High cardinality (user IDs, request IDs) -http_requests.labels(user_id='12345') # 💥 Millions of time series! -``` - -**🏷️ Label Rules:** -* ✅ Use for: method, endpoint, status, service -* ❌ Avoid: user_id, request_id, session_id -* 📊 Target: < 1000 unique label combinations - -**⚠️ High cardinality = memory explosion** - ---- - -## 📍 Slide 24 – 🔍 PromQL Basics - -```promql -# Instant vector (current value) -http_requests_total - -# Range vector (over time) -http_requests_total[5m] - -# Rate (per-second) -rate(http_requests_total[5m]) - -# Sum by label -sum by (endpoint) (rate(http_requests_total[5m])) - -# Filter by label -http_requests_total{status="500"} -``` - -**🔑 Key Operators:** -* `rate()` — Per-second rate for counters -* `sum()` — Aggregate across series -* `by ()` — Group results -* `{}` — Filter by labels - ---- - -## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L8_MID - ---- - -## 📍 Slide 26 – 📊 Section 4: Building Dashboards - -## 🎨 Dashboard Design with RED - -```mermaid -flowchart TD - Row1[🔝 Row 1: Overview Stats] - Row2[📊 Row 2: Rate & Errors] - Row3[⏱️ Row 3: Latency] - Row4[📋 Row 4: Details] - Row1 --> Row2 --> Row3 --> Row4 -``` - -**📊 Essential Panels:** -1. 📊 **Request Rate** — Requests per second -2. ❌ **Error Rate** — 5xx responses -3. ⏱️ **Latency p95** — 95th percentile -4. 📈 **Latency Heatmap** — Distribution - ---- - -## 📍 Slide 27 – 📊 PromQL Dashboard Queries - -**1️⃣ Request Rate (Time series)** -```promql -sum(rate(http_requests_total[5m])) by (endpoint) -``` - -**2️⃣ Error Rate % (Time series)** -```promql -sum(rate(http_requests_total{status=~"5.."}[5m])) - / sum(rate(http_requests_total[5m])) * 100 -``` - -**3️⃣ P95 Latency (Time series)** -```promql -histogram_quantile(0.95, - sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) -``` - -**4️⃣ Uptime (Stat)** -```promql -up{job="app"} -``` - ---- - -## 📍 Slide 28 – 📈 Heatmap for Latency - -```promql -# Latency distribution over time -sum(rate(http_request_duration_seconds_bucket[1m])) by (le) -``` - -**🎨 Heatmap Benefits:** -* 📊 See latency distribution -* 🔍 Spot outliers -* 📈 Track changes over time - -```mermaid -flowchart LR - Green[🟢 Fast: < 100ms] --> Yellow[🟡 OK: 100-500ms] - Yellow --> Red[🔴 Slow: > 500ms] -``` - ---- - -## 📍 Slide 29 – 📊 Monitoring Metrics - -| 📊 Metric | 📏 Measures | 🏆 Target | -|-----------|------------|---------| -| ⏱️ **Scrape Success** | Targets reachable | 100% | -| 📊 **Series Count** | Time series | Stable | -| 💾 **Storage Size** | Disk usage | Predictable | -| 🔍 **Query Latency** | PromQL speed | < 1s | - -> 📚 Monitor your monitoring! - -**🤔 Question:** What happens if Prometheus goes down? - ---- - -## 📍 Slide 30 – 🌊 From Guessing to Measuring - -```mermaid -flowchart LR - subgraph 😱 Guessing - NoData[🤷 No Data] - Reactive[🔥 Reactive] - Slow[⏱️ Slow Detection] - end - subgraph 📊 Measuring - Metrics[📈 Real Metrics] - Proactive[⚡ Proactive] - Fast[🚀 Instant Detection] - end - Guessing -->|🚀 Prometheus| Measuring -``` - -**🎯 Monitoring State:** -* ⚡ Detect issues before users -* 📊 Data-driven capacity planning -* 📈 Trend analysis and predictions - ---- - -## 📍 Slide 31 – 🏢 Section 5: Production Monitoring - -## 📅 A Day with Prometheus - -**☀️ Morning:** -* 📊 Check Grafana — all green ✅ -* 📈 Review overnight trends -* 🔍 No anomalies detected - -**🌤️ Afternoon:** -* 🚨 Alert: Latency p95 > 500ms -* 📊 Dashboard shows spike at 2pm -* 🔍 PromQL: `histogram_quantile(0.95, ...)` -* 🔧 Found: Database slow query -* ⏱️ **5 minutes** to identify - -**🌙 Evening:** -* 📊 Review daily trends -* 📈 Plan tomorrow's capacity -* 🏠 Go home with confidence - ---- - -## 📍 Slide 32 – 👥 Team Monitoring Workflow - -| 👤 Role | 🎯 Monitoring Responsibility | -|---------|----------------------| -| 👨‍💻 **Developer** | Add metrics to code | -| 🔧 **DevOps** | Maintain Prometheus | -| 🛡️ **SRE** | Design dashboards & alerts | -| 📊 **On-call** | Respond to alerts | - -**🔗 Alert Flow:** -```mermaid -flowchart LR - Prometheus[💾 Prometheus] -->|🚨 Alert| AlertManager[📬 AlertManager] - AlertManager --> Slack[💬 Slack] - AlertManager --> PagerDuty[📟 PagerDuty] - PagerDuty --> OnCall[👤 On-call] -``` - ---- - -## 📍 Slide 33 – 🔐 Production Considerations - -```yaml -# Prometheus with retention -command: - - '--config.file=/etc/prometheus/prometheus.yml' - - '--storage.tsdb.retention.time=15d' - - '--storage.tsdb.retention.size=10GB' - -deploy: - resources: - limits: - memory: 1G - cpus: '1.0' - -healthcheck: - test: ["CMD", "wget", "-q", "--spider", "http://localhost:9090/-/healthy"] - interval: 10s - timeout: 5s - retries: 5 -``` - -**🛡️ Production Checklist:** -* 💾 Persistent storage configured -* 🗓️ Retention policy set -* 📊 Resource limits defined -* 🏥 Health checks enabled - ---- - -## 📍 Slide 34 – 📈 Career Path: Monitoring Skills - -```mermaid -flowchart LR - Junior[🌱 Junior: Basic metrics] --> Mid[💼 Mid: PromQL & dashboards] - Mid --> Senior[⭐ Senior: Full observability] - Senior --> Principal[🏆 Principal: SRE practices] -``` - -**🛠️ Skills to Build:** -* 📊 Application instrumentation -* 🔍 PromQL fluency -* 📈 Dashboard design -* 🚨 Alert engineering -* 📊 SLO/SLI definition - ---- - -## 📍 Slide 35 – 🌍 Real Company Examples - -**🏢 Prometheus at Scale:** -* ☁️ **SoundCloud**: Created Prometheus (2012) -* 🔍 **Google**: Inspired Prometheus (Borgmon) -* 🎬 **Netflix**: Millions of time series - -**☁️ Modern Practices:** -* 📦 **Spotify**: Custom Prometheus federation -* 🏦 **Stripe**: Fine-grained latency tracking -* 🎮 **Riot Games**: Real-time game metrics - -**📊 Stats:** -* 🌍 **#1** cloud-native monitoring tool -* 📦 **CNCF graduated** project -* 🏢 Adopted by **70%+** of K8s users - ---- - -## 📍 Slide 36 – 🎯 Section 6: Reflection - -## 📝 Key Takeaways - -1. 📊 **Metrics complement logs** — different purposes -2. 🔢 **Counter, Gauge, Histogram** — choose wisely -3. 🔴 **RED method** for services (Rate, Errors, Duration) -4. 🏷️ **Labels** — keep cardinality low -5. 📈 **PromQL** is powerful — learn it well - -> 💡 If you can't measure it, you can't improve it. - ---- - -## 📍 Slide 37 – 🧠 The Mindset Shift - -| 😰 Old Mindset | 📊 Metrics Mindset | -|---------------|------------------| -| 🙅 "Seems fine" | 📊 "Data shows it's fine" | -| 🚫 "Users will tell us" | 🚨 "Alerts tell us first" | -| 👉 "We need more servers" | 📈 "Data shows we need 3 more" | -| 😨 "Deploy and hope" | 📊 "Deploy and measure" | -| 💻 "Performance is subjective" | 🔢 "p95 is 250ms" | - -> ❓ Which mindset describes your team? - ---- - -## 📍 Slide 38 – ✅ Your Progress - -## 🎓 What You Now Understand - -* ✅ Metrics types and when to use each -* ✅ Prometheus architecture and configuration -* ✅ Application instrumentation patterns -* ✅ PromQL query syntax -* ✅ Dashboard design with RED method - -> 🚀 **You're ready for Lab 8: Prometheus Monitoring** - ---- - -## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L8_POST - ---- - -## 📍 Slide 40 – 🚀 What Comes Next - -## 📚 Next Lecture: Kubernetes Fundamentals - -* ☸️ Container orchestration -* 📦 Deployments and Services -* 🔄 Scaling and self-healing -* 💻 Hands-on: Deploying to Kubernetes - -**🎉 Your monitoring journey continues.** - -> 📊 From guessing to measuring — one metric at a time. - -```mermaid -flowchart LR - You[👤 You] --> Metrics[📊 Metrics Skills] - Metrics --> DataDriven[📈 Data-Driven Ops] - DataDriven --> Career[🚀 Career Growth] -``` - -**👋 See you in the next lecture!** - ---- - -## 📚 Resources & Further Reading - -**📕 Books:** -* 📖 *Prometheus: Up & Running* — Brian Brazil -* 📖 *Site Reliability Engineering* — Google -* 📖 *The Art of Monitoring* — James Turnbull - -**🔗 Links:** -* 🌐 [Prometheus Documentation](https://prometheus.io/docs/) -* 🌐 [PromQL Basics](https://prometheus.io/docs/prometheus/latest/querying/basics/) -* 🌐 [RED Method](https://grafana.com/blog/2018/08/02/the-red-method-how-to-instrument-your-services/) - ---- diff --git a/lectures/lec9.md b/lectures/lec9.md deleted file mode 100644 index e5a265fd11..0000000000 --- a/lectures/lec9.md +++ /dev/null @@ -1,853 +0,0 @@ -# 📌 Lecture 9 — Kubernetes Fundamentals: Container Orchestration - -## 📍 Slide 1 – 🚀 Welcome to Kubernetes - -* 🌍 **Containers are great** — but who manages them at scale? -* 😰 Manual container management doesn't scale -* ☸️ **Kubernetes** = the operating system for containers -* 🎯 This lecture: master deployments, services, and orchestration - -```mermaid -flowchart LR - Containers[🐳 Containers] -->|☸️ Kubernetes| Orchestration[🎭 Orchestration] - Orchestration --> Scaling[📈 Auto-scaling] - Orchestration --> Healing[🏥 Self-healing] - Orchestration --> Updates[🔄 Rolling updates] -``` - ---- - -## 📍 Slide 2 – 🎯 What You Will Learn - -* ✅ Understand Kubernetes architecture and concepts -* ✅ Write production-ready Deployment manifests -* ✅ Expose applications with Services and Ingress -* ✅ Implement health checks and resource management - -**🎓 Learning Outcomes:** -| # | Outcome | -|---|---------| -| 1 | 🧠 Explain Kubernetes declarative model | -| 2 | 🔍 Create Deployments with probes and limits | -| 3 | 🛠️ Configure Services for networking | -| 4 | 🗺️ Perform scaling and rolling updates | - ---- - -## 📍 Slide 3 – 📋 How This Lecture Works - -* 📚 **Concepts + YAML manifests** — hands-on focus -* 🎮 **Real-world scenarios** — production deployment challenges -* 📝 **3 quiz checkpoints**: PRE / MID / POST -* 🛠️ **Tools**: kubectl, minikube, manifests - -**⏱️ Lecture Structure:** -``` -Section 0: Introduction (now) → 📝 PRE Quiz -Section 1: The Orchestration Problem -Section 2: Kubernetes Architecture -Section 3: Core Resources → 📝 MID Quiz -Section 4: Health & Resource Management -Section 5: Production Kubernetes -Section 6: Reflection → 📝 POST Quiz -``` - ---- - -## 📍 Slide 4 – ❓ The Big Question - -* 📊 **92%** of organizations use containers in production -* ⏱️ Average container lifecycle: **minutes to hours** (not days) -* 💥 Managing 100+ containers manually is **impossible** - -> 💬 *"Why did container 47 crash? Where's the replacement?"* — Nobody wants to ask this manually - -**🤔 Think about it:** -* How do you ensure 10 copies of your app are always running? -* What happens when a container crashes at 3am? -* How do you update without downtime? - ---- - -## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L9_PRE - ---- - -## 📍 Slide 6 – 🔥 Section 1: The Orchestration Problem - -* 🐳 **One container is easy** — just `docker run` -* 📦 100 containers? 1000 containers? -* 🔧 Manual restart on crash? -* 💥 Result: **operations nightmare** - -```mermaid -flowchart LR - Single[🐳 1 Container] -->|Easy| Manual[👤 Manual] - Hundred[🐳 100 Containers] -->|Hard| Manual - Thousand[🐳 1000 Containers] -->|💥 Impossible| Manual -``` - ---- - -## 📍 Slide 7 – 😱 Container Management Chaos - -* 📋 Tracking which containers run where -* 🔄 Restarting crashed containers -* 📊 Load balancing between replicas -* 🔒 Managing secrets and configs -* 💀 Scaling up/down based on load - -```mermaid -flowchart TD - Crash[💥 Container Crash] --> Detect[🔍 Detect (how?)] - Detect --> Restart[🔄 Restart (where?)] - Restart --> LoadBalance[⚖️ Update LB (manually?)] - LoadBalance --> Hope[🙏 Hope it works] -``` - -**📊 The Numbers:** -* 🔍 **Netflix**: 100,000+ container instances -* 📦 **Spotify**: 10,000+ services -* ⏱️ Manual management: **impossible** - ---- - -## 📍 Slide 8 – 🔧 Docker Compose Limitations - -* ✅ Great for development and simple deployments -* ❌ Single host only -* ❌ No automatic restart across nodes -* ❌ No rolling updates -* ❌ No auto-scaling - -> ⚠️ **Docker Compose ≠ production orchestration** - -```mermaid -flowchart TD - Compose[🐳 Docker Compose] --> SingleHost[🖥️ Single Host] - K8s[☸️ Kubernetes] --> MultiHost[🖥️🖥️🖥️ Multi-Host Cluster] - SingleHost --> DevTest[✅ Dev/Test] - MultiHost --> Production[✅ Production] -``` - ---- - -## 📍 Slide 9 – 😨 Zero Downtime Deployments - -* 📅 Traditional: Schedule maintenance window -* 🔧 Stop old version, start new version -* ⏱️ Downtime = lost revenue -* 💀 Risky deployments = fear of deploying - -> ⚠️ **Every minute of downtime costs money** - -**💬 Discussion:** How do you update without any downtime? - ---- - -## 📍 Slide 10 – 💸 The Cost of Manual Orchestration - -| 🔥 Problem | 💥 Impact | -|------------|-----------| -| 🐢 Slow scaling | Can't handle traffic spikes | -| 📋 Manual recovery | Long outages | -| 👉 No load balancing | Uneven distribution | -| 🙈 Version confusion | "Which version is running?" | - -**📈 Real Numbers:** -* 🏢 **Manual ops**: 10+ hours/week -* 🚀 **With Kubernetes**: Minutes/week -* 💰 **Downtime cost**: $5,600/minute (average) - ---- - -## 📍 Slide 11 – 💡 Section 2: What Kubernetes Is - -* ☸️ **Container orchestration platform** -* 🎭 **Manages** container lifecycle automatically -* 🔄 **Declarative** — you define desired state -* 🌐 **Portable** — runs anywhere (cloud, on-prem, laptop) - -```mermaid -flowchart LR - You[👤 You] -->|📝 Declare| K8s[☸️ Kubernetes] - K8s -->|🔄 Reconcile| Cluster[🖥️ Cluster] - K8s -->|🔁 Continuously| Monitor[👀 Monitor & Fix] -``` - -**📖 Definition:** -> *Kubernetes is an open-source container orchestration platform that automates deployment, scaling, and management of containerized applications.* - ---- - -## 📍 Slide 12 – 🎭 Declarative vs Imperative - -```mermaid -flowchart TD - subgraph Declarative - D1[📝 Define: 3 replicas] - D2[☸️ K8s makes it happen] - D1 --> D2 - end - subgraph Imperative - I1[💻 Run: create pod 1] - I2[💻 Run: create pod 2] - I3[💻 Run: create pod 3] - I1 --> I2 --> I3 - end -``` - -| 📋 Approach | 📝 You Say | ☸️ K8s Does | -|-------------|----------|------------| -| 🎭 **Declarative** | "I want 3 replicas" | Creates/maintains 3 | -| 💻 **Imperative** | "Create this pod" | Creates 1 pod | - -**🎯 Always prefer declarative manifests!** - ---- - -## 📍 Slide 13 – 🏗️ Kubernetes Architecture - -```mermaid -flowchart TD - subgraph Control Plane - API[📡 API Server] - Scheduler[📊 Scheduler] - Controller[🔄 Controller Manager] - ETCD[💾 etcd] - end - subgraph Worker Nodes - Kubelet[🤖 kubelet] - Proxy[🌐 kube-proxy] - Runtime[🐳 Container Runtime] - end - API --> Scheduler - API --> Controller - API --> ETCD - API --> Kubelet - Kubelet --> Runtime -``` - -| 🧱 Component | 🎯 Purpose | -|-------------|----------| -| 📡 **API Server** | Gateway to cluster | -| 📊 **Scheduler** | Places pods on nodes | -| 🔄 **Controller** | Ensures desired state | -| 💾 **etcd** | Cluster state database | -| 🤖 **kubelet** | Node agent | - ---- - -## 📍 Slide 14 – 📦 Core Resources - -```mermaid -flowchart TD - Pod[📦 Pod] --> Containers[🐳 Containers] - Deployment[🚀 Deployment] --> ReplicaSet[📊 ReplicaSet] - ReplicaSet --> Pod - Service[🌐 Service] --> Pod - Ingress[🚪 Ingress] --> Service -``` - -| 📦 Resource | 🎯 Purpose | -|-------------|----------| -| 📦 **Pod** | Smallest unit, contains containers | -| 🚀 **Deployment** | Manages replicas and updates | -| 🌐 **Service** | Stable network endpoint | -| 🚪 **Ingress** | HTTP routing and TLS | - ---- - -## 📍 Slide 15 – ⚡ Before vs After Kubernetes - -| 😰 Before | 🚀 After | -|----------|---------| -| 📅 Manual restart on crash | 🔄 Auto-restart | -| 📋 Manual scaling | 📈 Auto-scaling | -| 👉 Downtime for updates | 🔄 Rolling updates | -| 😨 Fear of deploying | 💪 Deploy anytime | -| 🐌 Hours to scale | ⚡ Seconds to scale | -| 📝 Track servers manually | 🎭 Declarative state | - -> 🤔 Ready to orchestrate? - ---- - -## 📍 Slide 16 – 🎮 Section 3: Core Resources - -## 📦 The Pod - -* 🐳 **One or more containers** sharing network/storage -* 📦 **Smallest deployable unit** -* ⏱️ **Ephemeral** — created and destroyed -* 🏷️ **Labeled** for selection - -```yaml -apiVersion: v1 -kind: Pod -metadata: - name: my-app - labels: - app: web -spec: - containers: - - name: web - image: nginx:latest - ports: - - containerPort: 80 -``` - -**⚠️ Never create pods directly — use Deployments!** - ---- - -## 📍 Slide 17 – 🚀 Deployments - -```mermaid -flowchart TD - Deployment[🚀 Deployment] --> RS1[📊 ReplicaSet v1] - RS1 --> Pod1[📦 Pod] - RS1 --> Pod2[📦 Pod] - RS1 --> Pod3[📦 Pod] -``` - -**🚀 Deployment manages:** -* 📊 Desired replica count -* 🔄 Rolling updates -* 🔙 Rollback capability -* 🏷️ Pod template - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: web-app -spec: - replicas: 3 - selector: - matchLabels: - app: web - template: - metadata: - labels: - app: web - spec: - containers: - - name: web - image: myapp:1.0 -``` - ---- - -## 📍 Slide 18 – 🏷️ Labels and Selectors - -```mermaid -flowchart LR - Deployment[🚀 Deployment] -->|selector: app=web| Pods[📦 Pods with label app=web] - Service[🌐 Service] -->|selector: app=web| Pods -``` - -**🏷️ Labels = Key-value pairs for organization** - -```yaml -metadata: - labels: - app: web-frontend - environment: production - version: v1.2.3 - -selector: - matchLabels: - app: web-frontend -``` - -**🎯 Labels enable:** -* 🔍 Service discovery -* 📊 Resource selection -* 🏗️ Organization - ---- - -## 📍 Slide 19 – 🌐 Services - -```mermaid -flowchart LR - Client[👥 Client] --> Service[🌐 Service: ClusterIP] - Service --> Pod1[📦 Pod 1] - Service --> Pod2[📦 Pod 2] - Service --> Pod3[📦 Pod 3] -``` - -**🌐 Service types:** -| 🔧 Type | 🎯 Use Case | -|---------|----------| -| 🔒 **ClusterIP** | Internal cluster access | -| 🔓 **NodePort** | External via node IP | -| ☁️ **LoadBalancer** | Cloud load balancer | -| 🔗 **ExternalName** | DNS alias | - -```yaml -apiVersion: v1 -kind: Service -metadata: - name: web-service -spec: - type: NodePort - selector: - app: web - ports: - - port: 80 - targetPort: 8000 - nodePort: 30080 -``` - ---- - -## 📍 Slide 20 – 🔄 Rolling Updates - -```mermaid -flowchart LR - V1[📦 v1] --> V1_V2[📦 v1 + v2] - V1_V2 --> V2[📦 v2] -``` - -**🔄 How it works:** -1. 📦 Create new pods with new version -2. ⏳ Wait for them to be ready -3. 🗑️ Terminate old pods gradually -4. ✅ Zero downtime! - -```yaml -spec: - strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 1 # Extra pods during update - maxUnavailable: 0 # Always maintain capacity -``` - ---- - -## 📍 Slide 21 – 📊 kubectl Commands - -```bash -# 📋 Get resources -kubectl get pods -kubectl get deployments -kubectl get services - -# 🔍 Describe (detailed info) -kubectl describe pod - -# 📝 Apply manifest -kubectl apply -f deployment.yaml - -# 📊 Watch changes -kubectl get pods -w - -# 🔙 Rollback -kubectl rollout undo deployment/ - -# 📈 Scale -kubectl scale deployment/ --replicas=5 -``` - ---- - -## 📍 Slide 22 – 🚪 Ingress - -```mermaid -flowchart LR - Internet[🌐 Internet] --> Ingress[🚪 Ingress Controller] - Ingress -->|/app1| Svc1[🌐 Service 1] - Ingress -->|/app2| Svc2[🌐 Service 2] - Svc1 --> Pods1[📦 Pods] - Svc2 --> Pods2[📦 Pods] -``` - -**🚪 Ingress provides:** -* 🔗 URL routing -* 🔐 TLS termination -* 🏷️ Name-based virtual hosting - -```yaml -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: app-ingress -spec: - rules: - - host: app.example.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: web-service - port: - number: 80 -``` - ---- - -## 📍 Slide 23 – 🏥 Health Checks - -```yaml -spec: - containers: - - name: app - image: myapp:1.0 - livenessProbe: - httpGet: - path: /health - port: 8000 - initialDelaySeconds: 10 - periodSeconds: 5 - readinessProbe: - httpGet: - path: /ready - port: 8000 - initialDelaySeconds: 5 - periodSeconds: 3 -``` - -| 🏥 Probe | 🎯 Purpose | ❌ Failure Action | -|----------|----------|------------------| -| 🔴 **Liveness** | Is it alive? | Restart container | -| 🟢 **Readiness** | Is it ready? | Remove from service | -| 🟡 **Startup** | Did it start? | Keep waiting | - ---- - -## 📍 Slide 24 – 📊 Resource Management - -```yaml -spec: - containers: - - name: app - image: myapp:1.0 - resources: - requests: - memory: "128Mi" - cpu: "100m" # 0.1 CPU core - limits: - memory: "256Mi" - cpu: "200m" # 0.2 CPU core -``` - -**📊 Requests vs Limits:** -| 📊 Setting | 🎯 Purpose | -|-----------|----------| -| 📋 **Requests** | Guaranteed resources, scheduling | -| 🔒 **Limits** | Maximum allowed, OOM if exceeded | - -**⚠️ Always set both!** - ---- - -## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L9_MID - ---- - -## 📍 Slide 26 – 📁 Section 4: Manifest Best Practices - -## 📄 Complete Deployment Example - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: web-app - labels: - app: web-app -spec: - replicas: 3 - selector: - matchLabels: - app: web-app - strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 1 - maxUnavailable: 0 - template: - metadata: - labels: - app: web-app - spec: - containers: - - name: web-app - image: username/web-app:1.0.0 - ports: - - containerPort: 8000 - resources: - requests: - memory: "128Mi" - cpu: "100m" - limits: - memory: "256Mi" - cpu: "200m" - livenessProbe: - httpGet: - path: /health - port: 8000 - initialDelaySeconds: 10 - readinessProbe: - httpGet: - path: /health - port: 8000 - initialDelaySeconds: 5 -``` - ---- - -## 📍 Slide 27 – 🔐 Security Best Practices - -```yaml -spec: - containers: - - name: app - image: myapp:1.0 - securityContext: - runAsNonRoot: true - runAsUser: 1000 - readOnlyRootFilesystem: true - allowPrivilegeEscalation: false -``` - -**🔐 Security Checklist:** -* ✅ Run as non-root user -* ✅ Read-only filesystem -* ✅ No privilege escalation -* ✅ Specific image tags (not `:latest`) -* ✅ Resource limits defined - ---- - -## 📍 Slide 28 – 📊 Kubernetes Metrics - -| 📊 Metric | 📏 Measures | 🏆 Target | -|-----------|------------|---------| -| 📦 **Pod Restarts** | Stability | 0 | -| ⏱️ **Pod Startup Time** | Speed | < 30s | -| 📊 **Resource Usage** | Efficiency | 50-80% | -| ✅ **Probe Success** | Health | 100% | - -> 📚 Monitor your cluster health! - -**🤔 Question:** How many pod restarts is "normal"? - ---- - -## 📍 Slide 29 – 🌊 From Manual to Orchestrated - -```mermaid -flowchart LR - subgraph 😱 Manual - SSH[🔌 SSH to servers] - Docker[🐳 docker run] - Restart[🔄 Manual restart] - end - subgraph ☸️ Orchestrated - Manifest[📝 YAML Manifest] - Apply[kubectl apply] - AutoHeal[🏥 Auto-healing] - end - Manual -->|🚀 Kubernetes| Orchestrated -``` - -**🎯 Orchestration State:** -* ⚡ Deploy in seconds -* 🔄 Auto-healing always -* 📈 Scale on demand - ---- - -## 📍 Slide 30 – 🏢 Section 5: Production Kubernetes - -## 📅 A Day with Kubernetes - -**☀️ Morning:** -* 📊 Check cluster health — all green ✅ -* 📈 Review resource usage -* 🔄 Approve deployment PR - -**🌤️ Afternoon:** -* 🚀 `kubectl apply -f deployment.yaml` -* 📊 Watch rolling update: `kubectl rollout status` -* ✅ Zero downtime update complete - -**🌙 Evening:** -* 📈 Auto-scaling handles traffic spike -* 🏥 Crashed pod auto-restarted -* 🏠 Sleep peacefully - ---- - -## 📍 Slide 31 – 👥 Team Kubernetes Workflow - -| 👤 Role | 🎯 Kubernetes Responsibility | -|---------|----------------------| -| 👨‍💻 **Developer** | Write manifests, define resources | -| 🔧 **DevOps** | Manage cluster, set policies | -| 🛡️ **SRE** | Monitor, scale, incident response | -| 📊 **Platform** | Build internal tooling | - -**🔗 GitOps Flow:** -```mermaid -flowchart LR - PR[📝 Manifest PR] --> Review[👀 Review] - Review --> Merge[✅ Merge] - Merge --> ArgoCD[🔄 ArgoCD] - ArgoCD --> Cluster[☸️ Cluster] -``` - ---- - -## 📍 Slide 32 – 🔧 Local Development - -```bash -# 🎯 minikube: Full-featured local cluster -minikube start -minikube status -minikube service web-service --url - -# 🐳 kind: Lightweight, Docker-based -kind create cluster -kind load docker-image myapp:latest - -# 📊 Useful addons -minikube addons enable ingress -minikube addons enable metrics-server -``` - -**🛠️ Local Options:** -| 🔧 Tool | 🎯 Best For | -|---------|----------| -| 🚀 **minikube** | Learning, full features | -| 🐳 **kind** | CI/CD, fast startup | -| 🖥️ **Docker Desktop** | Mac/Windows convenience | - ---- - -## 📍 Slide 33 – 📈 Career Path: Kubernetes Skills - -```mermaid -flowchart LR - Junior[🌱 Junior: kubectl basics] --> Mid[💼 Mid: Manifests & debugging] - Mid --> Senior[⭐ Senior: Architecture & scaling] - Senior --> Principal[🏆 Principal: Platform design] -``` - -**🛠️ Skills to Build:** -* 📝 YAML manifest fluency -* 🔍 kubectl debugging -* 🏗️ Architecture patterns -* 📊 Resource optimization -* 🔐 Security hardening - ---- - -## 📍 Slide 34 – 🌍 Real Company Examples - -**🏢 Kubernetes at Scale:** -* 📦 **Spotify**: 10,000+ services on K8s -* 🔍 **Google**: Runs everything on Kubernetes -* 🎬 **Netflix**: Titus (K8s-inspired) - -**☁️ Modern Practices:** -* 📦 **Airbnb**: 1000+ microservices -* 🏦 **Capital One**: K8s for banking workloads -* 🎮 **Pokemon Go**: Global scale with K8s - -**📊 Stats:** -* 🌍 **5.6M+** Kubernetes developers -* 📦 **92%** container adoption uses K8s -* 🏢 **#1** CNCF project - ---- - -## 📍 Slide 35 – 🎯 Section 6: Reflection - -## 📝 Key Takeaways - -1. ☸️ **Kubernetes orchestrates containers** at scale -2. 🎭 **Declarative** — define desired state, K8s maintains it -3. 🚀 **Deployments** manage replicas and updates -4. 🌐 **Services** provide stable networking -5. 🏥 **Probes** ensure health, **limits** ensure stability - -> 💡 Kubernetes is the operating system for cloud-native applications. - ---- - -## 📍 Slide 36 – 🧠 The Mindset Shift - -| 😰 Old Mindset | ☸️ K8s Mindset | -|---------------|------------------| -| 🙅 "Restart manually" | 🔄 "K8s restarts automatically" | -| 🚫 "SSH to fix" | 📝 "Fix manifest, apply" | -| 👉 "Which server?" | 📦 "Which pod?" | -| 😨 "Scale takes hours" | ⚡ "Scale in seconds" | -| 💻 "Deploy on weekends" | 🚀 "Deploy anytime" | - -> ❓ Which mindset describes your team? - ---- - -## 📍 Slide 37 – ✅ Your Progress - -## 🎓 What You Now Understand - -* ✅ Kubernetes architecture and concepts -* ✅ Deployments, Services, and Ingress -* ✅ Health checks and resource management -* ✅ Rolling updates and scaling -* ✅ kubectl commands for daily use - -> 🚀 **You're ready for Lab 9: Kubernetes Fundamentals** - ---- - -## 📍 Slide 38 – 📝 QUIZ — DEVOPS_L9_POST - ---- - -## 📍 Slide 39 – 🚀 What Comes Next - -## 📚 Next Lecture: Helm Package Management - -* ⛵ Helm charts for packaging -* 📝 Templating with Go templates -* 🔧 Values management -* 💻 Hands-on: Creating Helm charts - -**🎉 Your Kubernetes journey continues.** - -> ☸️ From manual containers to orchestration — one manifest at a time. - -```mermaid -flowchart LR - You[👤 You] --> K8s[☸️ Kubernetes Skills] - K8s --> CloudNative[☁️ Cloud-Native] - CloudNative --> Career[🚀 Career Growth] -``` - -**👋 See you in the next lecture!** - ---- - -## 📍 Slide 40 – 📚 Resources & Further Reading - -**📕 Books:** -* 📖 *Kubernetes: Up & Running* — Brendan Burns -* 📖 *The Kubernetes Book* — Nigel Poulton -* 📖 *Cloud Native DevOps with Kubernetes* — John Arundel - -**🔗 Links:** -* 🌐 [Kubernetes Documentation](https://kubernetes.io/docs/) -* 🌐 [kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/) -* 🌐 [Kubernetes the Hard Way](https://github.com/kelseyhightower/kubernetes-the-hard-way) - ---- From 82ede12a62d9f778c6d75272c05b8deba336acdd Mon Sep 17 00:00:00 2001 From: setterwars Date: Mon, 26 Jan 2026 13:49:01 +0300 Subject: [PATCH 03/27] feat: completed lab1 bonus task --- lectures/lec1.md | 761 ++++++++++++++++++++++++++++++++ lectures/lec10.md | 840 ++++++++++++++++++++++++++++++++++++ lectures/lec11.md | 759 ++++++++++++++++++++++++++++++++ lectures/lec12.md | 854 ++++++++++++++++++++++++++++++++++++ lectures/lec13.md | 830 +++++++++++++++++++++++++++++++++++ lectures/lec14.md | 825 +++++++++++++++++++++++++++++++++++ lectures/lec15.md | 821 +++++++++++++++++++++++++++++++++++ lectures/lec16.md | 717 ++++++++++++++++++++++++++++++ lectures/lec2.md | 1053 +++++++++++++++++++++++++++++++++++++++++++++ lectures/lec3.md | 978 +++++++++++++++++++++++++++++++++++++++++ lectures/lec4.md | 801 ++++++++++++++++++++++++++++++++++ lectures/lec5.md | 824 +++++++++++++++++++++++++++++++++++ lectures/lec6.md | 887 ++++++++++++++++++++++++++++++++++++++ lectures/lec7.md | 849 ++++++++++++++++++++++++++++++++++++ lectures/lec8.md | 799 ++++++++++++++++++++++++++++++++++ lectures/lec9.md | 853 ++++++++++++++++++++++++++++++++++++ 16 files changed, 13451 insertions(+) create mode 100644 lectures/lec1.md create mode 100644 lectures/lec10.md create mode 100644 lectures/lec11.md create mode 100644 lectures/lec12.md create mode 100644 lectures/lec13.md create mode 100644 lectures/lec14.md create mode 100644 lectures/lec15.md create mode 100644 lectures/lec16.md create mode 100644 lectures/lec2.md create mode 100644 lectures/lec3.md create mode 100644 lectures/lec4.md create mode 100644 lectures/lec5.md create mode 100644 lectures/lec6.md create mode 100644 lectures/lec7.md create mode 100644 lectures/lec8.md create mode 100644 lectures/lec9.md diff --git a/lectures/lec1.md b/lectures/lec1.md new file mode 100644 index 0000000000..00ead8aabc --- /dev/null +++ b/lectures/lec1.md @@ -0,0 +1,761 @@ +# 📌 Lecture 1 — Introduction to DevOps: From Chaos to Flow + +## 📍 Slide 1 – 🚀 Welcome to DevOps + +* 🌍 **Software is eating the world** — but shipping it is hard +* 😰 Teams struggle with slow releases, broken deploys, finger-pointing +* 🌉 **DevOps bridges the gap** between **building** and **running** software +* 🎯 This course: practical skills to transform how you deliver software + +```mermaid +flowchart LR + Chaos[😱 Chaos] -->|DevOps| Flow[🌊 Flow] + Flow --> Value[💎 Deliver Value Faster] +``` + +--- + +## 📍 Slide 2 – 🎯 What You Will Learn + +* ✅ Understand what DevOps is (and isn't) +* ✅ Identify problems DevOps solves +* ✅ Apply DevOps thinking to real scenarios +* ✅ Map DevOps practices to your future workflow + +**🎓 Learning Outcomes:** +| # | Outcome | +|---|---------| +| 1 | 🧠 Define DevOps and its core principles | +| 2 | 🔍 Recognize pre-DevOps problems | +| 3 | 🛠️ Apply DevOps solutions to scenarios | +| 4 | 🗺️ Navigate the DevOps lifecycle | + +--- + +## 📍 Slide 3 – 📋 How This Lecture Works + +* 📚 **Concepts + Diagrams** — visual learning +* 🎮 **Real-world scenarios** — you decide! +* 📝 **3 quiz checkpoints**: PRE / MID / POST +* 🕹️ **Interactive simulation**: "DevOps as a Game" + +**⏱️ Lecture Structure:** +``` +Section 0: Introduction (now) → 📝 PRE Quiz +Section 1: The Problem +Section 2: What DevOps Is +Section 3: DevOps as a Game → 📝 MID Quiz +Section 4: Lifecycle & Metrics +Section 5: Real Life +Section 6: Reflection → 📝 POST Quiz +``` + +--- + +## 📍 Slide 4 – ❓ The Big Question + +* 📊 **70%** of IT projects experience significant delays +* ⏱️ Average time from code complete to production: **weeks to months** +* 💥 Most outages caused by **changes** (deploys, configs) + +> 💬 *"It worked on my machine"* — Every developer, ever + +**🤔 Think about it:** +* Why is software delivery so hard? +* Why do teams fear deployments? +* What would "good" look like? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L1_PRE + +--- + +## 📍 Slide 6 – 🔥 Section 1: The Problem Before DevOps + +* 👨‍💻 **Development** and ⚙️ **Operations** = separate teams, separate goals +* 🚀 Dev wants: **ship features fast** +* 🛡️ Ops wants: **keep systems stable** +* 💥 Result: **conflict, blame, slow delivery** + +```mermaid +flowchart LR + Dev[👨‍💻 Dev Team] -->|🎯 New Features| Goal1[Ship Fast] + Ops[⚙️ Ops Team] -->|🛡️ Stability| Goal2[Don't Break] + Goal1 -.->|❌ Conflict| Goal2 +``` + +--- + +## 📍 Slide 7 – 🧱 The Wall of Confusion + +* 🧱 **The Wall** = invisible barrier between Dev and Ops +* 📦 Dev "throws code over the wall" +* 🔥 Ops catches the blame when it breaks +* 🔄 Ops rejects changes to avoid risk + +```mermaid +flowchart LR + Dev[👨‍💻 Dev Team] -->|📦 Throws code over| Wall[🧱 Wall of Confusion] + Wall -->|🔥 Catches blame| Ops[⚙️ Ops Team] + Ops -->|❌ Rejects changes| Dev +``` + +> 🤔 **Think:** Have you seen this pattern before? + +--- + +## 📍 Slide 8 – 😱 Manual Release Hell + +* 📅 Deployments are rare (monthly, quarterly) +* 🎰 Each release = **high-risk event** +* 📋 Manual steps, checklists, weekend work +* 💀 One mistake = hours of rollback + +```mermaid +flowchart TD + Code[✅ Code Complete] --> Wait[📅 Wait for Release Window] + Wait --> Manual[📋 Manual Deploy Steps] + Manual --> Pray[🙏 Pray It Works] + Pray -->|💥 Failure| Blame[👉 Blame Game] + Pray -->|😮‍💨 Success| Relief[Temporary Relief] +``` + +**📊 The Numbers:** +* 🐢 Average release cycle: **3-6 months** +* 📉 Success rate: **~60%** +* ⏱️ Rollback time: **4-8 hours** + +--- + +## 📍 Slide 9 – 😨 Fear and Blame Culture + +* 🌙 Incident happens at 2am +* 👉 First question: *"Who did this?"* +* 🙈 Engineers hide mistakes +* 🚫 Nobody wants to deploy on Friday +* 💀 Innovation stops + +> ⚠️ **Fear kills velocity** + +**😰 Signs of Blame Culture:** +* 🔇 People afraid to speak up +* 📝 Excessive documentation "for protection" +* 🐌 Slow decision-making +* 🚪 High turnover + +**💬 Discussion:** Why does blame make things worse? + +--- + +## 📍 Slide 10 – 💸 The Cost of Chaos + +| 🔥 Problem | 💥 Impact | +|------------|-----------| +| 🐢 Slow releases | Lost market opportunity | +| 📋 Manual processes | Human error, burnout | +| 👉 Blame culture | Talent leaves | +| 🙈 No visibility | Firefighting mode | + +**📈 Real Numbers:** +* 🏢 **Amazon pre-DevOps**: deploys took **weeks** +* 🚀 **Amazon post-DevOps**: deploys every **11.7 seconds** + +**💰 Cost of Downtime:** +* 💵 Small business: **$427/minute** +* 🏢 Enterprise: **$9,000/minute** +* 🌐 Amazon: **$220,000/minute** + +--- + +## 📍 Slide 11 – 💡 Section 2: What DevOps Really Is + +* 🤝 **DevOps** = Development + Operations working as **one team** +* 🌱 A **culture** of collaboration and shared responsibility +* 🔧 A set of **practices** for fast, reliable delivery +* 🚫 NOT just tools, NOT a job title, NOT a team + +```mermaid +flowchart LR + Dev[👨‍💻 Development] -->|🤝 Collaboration| DevOps[🚀 DevOps] + Ops[⚙️ Operations] -->|🤝 Collaboration| DevOps + DevOps --> Value[💎 Fast, Reliable Value] +``` + +**📖 Definition:** +> *DevOps is a set of practices that combines software development (Dev) and IT operations (Ops) to shorten the development lifecycle while delivering features, fixes, and updates frequently in close alignment with business objectives.* + +--- + +## 📍 Slide 12 – 🚫 What DevOps is NOT + +| ❌ Myth | ✅ Reality | +|---------|-----------| +| "We hired a DevOps engineer, we're done" | 👥 Everyone participates | +| "DevOps means using Kubernetes" | 🛠️ Tools support culture | +| "DevOps replaces developers/ops" | 🤝 It unites them | +| "DevOps = just automation" | 🧩 Automation + Culture + Measurement | +| "DevOps is a team" | 🌍 It's a way of working | + +> 🔥 **Hot take:** You can't buy DevOps. You build it. + +**🎯 DevOps is about:** +* 🧠 Mindset change +* 🤝 Breaking silos +* 🔄 Continuous improvement +* 📊 Data-driven decisions + +--- + +## 📍 Slide 13 – 🔄 The Three Ways of DevOps + +```mermaid +flowchart LR + W1[1️⃣ Flow] --> W2[2️⃣ Feedback] + W2 --> W3[3️⃣ Learning] + W3 --> W1 +``` + +| 🛤️ Way | 🎯 Focus | 💡 Example | +|--------|---------|-----------| +| 1️⃣ **Flow** | Fast Dev → Prod | 🚀 CI/CD pipelines | +| 2️⃣ **Feedback** | Fast Prod → Dev | 📊 Monitoring, alerts | +| 3️⃣ **Learning** | Experiment safely | 📝 Blameless postmortems | + +**📚 Source:** *The Phoenix Project* by Gene Kim + +--- + +## 📍 Slide 14 – 🧩 The CAMS Model + +```mermaid +graph TD + C[🌱 Culture] --> DevOps[🚀 DevOps] + A[🤖 Automation] --> DevOps + M[📊 Measurement] --> DevOps + S[🔗 Sharing] --> DevOps +``` + +* 🌱 **C = Culture** — Trust, collaboration, shared ownership +* 🤖 **A = Automation** — Eliminate manual, error-prone work +* 📊 **M = Measurement** — Track metrics, decide with data +* 🔗 **S = Sharing** — Knowledge flows, blameless postmortems + +**🎯 Key Metrics:** +* ⏱️ **MTTR** = Mean Time to Recovery +* ❌ **CFR** = Change Failure Rate +* 📦 **DF** = Deployment Frequency +* 🚀 **LT** = Lead Time + +--- + +## 📍 Slide 15 – ⚡ Before vs After DevOps + +| 😰 Before | 🚀 After | +|----------|---------| +| 📅 Releases every few months | 📆 Releases daily/weekly | +| 📋 Manual deployments | 🤖 Automated pipelines | +| 👉 Blame when things break | 📝 Blameless postmortems | +| 🙅 "Not my problem" | 🤝 Shared ownership | +| 😨 Fear of change | 💪 Embrace change | +| 🐌 Weeks to deploy | ⚡ Minutes to deploy | + +> 🤔 Which column describes your current environment? + +--- + +## 📍 Slide 16 – 🎮 Section 3: DevOps as a Game + +## 🕹️ Simulation: You're the CTO + +* 🏢 Welcome to **FlowStart Inc.** — a growing startup +* 👥 You have: 5 developers, 2 ops engineers +* 🌐 A web application with 10K users +* 📈 Pressure to ship new features + +**❓ What could go wrong?** + +> 💀 **Everything.** + +🎮 **Let's play.** + +--- + +## 📍 Slide 17 – 💥 Scenario 1: Release Failure + +**📅 Friday 5pm:** +* 👨‍💻 Developer pushes "small fix" +* 🚫 No tests, no review, straight to production +* 💥 App crashes, users can't log in +* 🤷 Nobody knows what changed + +```mermaid +flowchart LR + Push[📤 Code Push] --> Prod[🌐 Production] + Prod --> Crash[💥 Crash] + Crash --> Panic[😱 Weekend Panic] +``` + +**📊 Impact:** +* 👥 10,000 users affected +* ⏱️ 4 hours downtime +* 💰 $50,000 lost revenue +* 😤 Angry customers on Twitter + +> ❓ **What would you do?** + +--- + +## 📍 Slide 18 – ✅ Solution: CI/CD + +## 🛠️ Fix: Continuous Integration & Delivery + +```mermaid +flowchart LR + Push[📤 Push] --> CI[🧪 Tests] + CI -->|✅ Pass| Review[👀 Review] + Review --> CD[🚀 Deploy] + CD --> Monitor[📊 Monitor] + CI -->|❌ Fail| Fix[🔧 Fix] +``` + +* ✅ Every change triggers **automated tests** +* ✅ **Code review** required before merge +* ✅ **Automated deployment** pipeline +* ✅ **One-click rollback** + +**🎯 Result:** Deploy with confidence, not prayers + +**📊 CI/CD Benefits:** +* 🐛 Catch bugs early (80% cheaper to fix) +* 🚀 Deploy 200x more frequently +* ⏱️ 24x faster recovery from failures + +--- + +## 📍 Slide 19 – 🐾 Scenario 2: Infrastructure Drift + +**😰 Situation:** +* 🖥️ Production server configured manually over 2 years +* 👋 Ops engineer who set it up **left the company** +* 📈 Need to scale — but **can't recreate the setup** + +```mermaid +flowchart TD + S1[🖥️ Server 1: Ubuntu 18 + mystery configs] + S2[🖥️ Server 2: Ubuntu 20 + different configs] + S3[🖥️ Server 3: Who knows? 🤷] + S1 --> Drift[😱 Configuration Drift] + S2 --> Drift + S3 --> Drift +``` + +> 🐶🐄 **"Pets vs Cattle"** — Which do you have? + +**🐶 Pets:** Unique, irreplaceable, nursed back to health +**🐄 Cattle:** Identical, replaceable, automated + +--- + +## 📍 Slide 20 – ✅ Solution: Infrastructure as Code + +## 🛠️ Fix: IaC + +* 📝 Define infrastructure in **version-controlled files** +* 🔄 Servers are **reproducible**, not unique +* ⚡ Spin up identical environments in **minutes** + +```hcl +# 🌍 Terraform example +resource "aws_instance" "web" { + ami = "ami-0c55b159cbfafe1f0" + instance_type = "t2.micro" + count = 3 # 🔢 3 identical servers +} +``` + +**🎯 Result:** Cattle, not pets. Replace, don't repair. + +**🛠️ IaC Tools:** +* 🌍 **Terraform** — Multi-cloud +* 🧩 **Ansible** — Configuration management +* 📦 **Pulumi** — Code-based IaC + +--- + +## 📍 Slide 21 – 🔓 Scenario 3: Secret Leak + +**💀 What happened:** +* 👨‍💻 Developer commits database password to GitHub +* 🤖 Bot scrapes it within **minutes** +* 💥 Attackers access production database + +```mermaid +flowchart LR + Commit[📤 Commit + Secret] --> GitHub[🐙 Public Repo] + GitHub --> Bot[🤖 Scraper Bot] + Bot --> Breach[💀 Database Breach] +``` + +> ⏱️ **How fast do bots find secrets?** Under 5 minutes. + +**📊 Real Stats:** +* 🔍 GitHub scans 100M+ repos for secrets +* ⏱️ Average time to exploit: **<1 hour** +* 💰 Average breach cost: **$4.45 million** + +--- + +## 📍 Slide 22 – ✅ Solution: Secrets Management + +## 🛠️ Fix: Vault & Secret Scanning + +* 🚫 **Never** store secrets in code +* 🔐 Use secret management tools (Vault, AWS Secrets Manager) +* 🔍 Pre-commit hooks scan for secrets +* 🔄 Rotate credentials automatically + +```yaml +# ❌ Bad +password: "super_secret_123" + +# ✅ Good +password: ${VAULT_DB_PASSWORD} +``` + +**🎯 Result:** Secrets stay secret + +**🛠️ Secret Tools:** +* 🔐 **HashiCorp Vault** +* 🔑 **AWS Secrets Manager** +* 🔒 **Azure Key Vault** +* 🔍 **git-secrets** (pre-commit) + +--- + +## 📍 Slide 23 – 🙈 Scenario 4: Blind Operations + +**👥 Users report:** *"App is slow"* + +**🤷 Team asks:** +* Is it? How slow? +* Which part is slow? +* Since when? +* How many users affected? + +**😰 Answer:** No idea. No metrics. No logs. No visibility. + +⏱️ **Hours spent guessing.** + +--- + +## 📍 Slide 24 – ✅ Solution: Observability + +## 🛠️ Fix: Logs, Metrics, Traces + +```mermaid +graph TD + Logs[📋 Logs: What happened] --> Obs[🔍 Observability] + Metrics[📊 Metrics: How much/fast] --> Obs + Traces[🔗 Traces: Where] --> Obs + Obs --> Action[⚡ Fix in minutes, not hours] +``` + +| 📊 Pillar | 🛠️ Tools | +|-----------|----------| +| 📋 Logs | ELK, Loki, CloudWatch | +| 📊 Metrics | Prometheus, Grafana, Datadog | +| 🔗 Traces | Jaeger, Zipkin, X-Ray | + +**🎯 Result:** See problems before users report them + +--- + +## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L1_MID + +--- + +## 📍 Slide 26 – ♾️ Section 4: DevOps Lifecycle + +## 🔄 The Infinity Loop + +* ♾️ DevOps is **continuous** — no "done" state +* 🔄 Each stage feeds the next +* 🔁 Forever improving + +```mermaid +flowchart LR + Plan[📋 Plan] --> Code[💻 Code] + Code --> Build[🔨 Build] + Build --> Test[🧪 Test] + Test --> Release[📦 Release] + Release --> Deploy[🚀 Deploy] + Deploy --> Operate[⚙️ Operate] + Operate --> Monitor[📊 Monitor] + Monitor --> Plan +``` + +--- + +## 📍 Slide 27 – 🔁 Lifecycle Phases + +| 📍 Phase | 🎯 Activity | 🛠️ Tools | +|----------|------------|----------| +| 📋 Plan | Requirements, design | Jira, GitHub Issues | +| 💻 Code | Write & review | Git, VS Code | +| 🔨 Build | Compile, package | Docker, npm, Maven | +| 🧪 Test | Automated testing | pytest, Jest, Selenium | +| 📦 Release | Version, approve | GitHub Releases, Tags | +| 🚀 Deploy | Push to environment | ArgoCD, Ansible, Helm | +| ⚙️ Operate | Run, scale | Kubernetes, Terraform | +| 📊 Monitor | Observe, alert | Prometheus, Grafana | + +--- + +## 📍 Slide 28 – 🗺️ Course Map + +## 📚 How This Course Covers the Lifecycle + +```mermaid +flowchart TD + subgraph 📋 Plan & Code + L1[🔬 Labs 1-3: Git, GitHub] + end + subgraph 🔨 Build & Test + L2[🐳 Labs 4-6: Docker, CI/CD] + end + subgraph 🚀 Deploy & Operate + L3[☸️ Labs 7-10: K8s, Helm] + end + subgraph 🔐 Secure & Monitor + L4[📊 Labs 11-15: Vault, Monitoring] + end +``` + +✅ **Every lab maps to a real DevOps skill.** + +--- + +## 📍 Slide 29 – 📊 DORA Metrics + +## 📈 Measuring DevOps Success + +| 📊 Metric | 📏 Measures | 🏆 Elite | +|-----------|------------|---------| +| ⏱️ **Lead Time** | Commit → Prod | < 1 hour | +| 📦 **Deploy Frequency** | How often | Multiple/day | +| ❌ **Change Failure Rate** | % broken deploys | < 15% | +| 🔧 **MTTR** | Recovery time | < 1 hour | + +> 📚 These 4 metrics predict software delivery performance. +> *Source: DORA State of DevOps Report* + +**🤔 Question:** Where does your team stand? + +--- + +## 📍 Slide 30 – 🌊 From Chaos to Flow + +## 🎯 The Goal + +```mermaid +flowchart LR + subgraph 😱 Chaos + Manual[📋 Manual Work] + Silos[🧱 Silos] + Fear[😨 Fear] + end + subgraph 🌊 Flow + Auto[🤖 Automation] + Collab[🤝 Collaboration] + Confidence[💪 Confidence] + end + Chaos -->|🚀 DevOps| Flow +``` + +**🎯 Flow State:** +* ⚡ Changes flow smoothly from idea to production +* 🔄 Feedback loops are fast +* 📈 Teams continuously improve + +--- + +## 📍 Slide 31 – 🏢 Section 5: DevOps in Real Life + +## 📅 A Day in DevOps + +**☀️ Morning:** +* 📊 Check dashboards — all green ✅ +* 👀 Review pull requests +* 🔀 Merge → auto-deploy + +**🌤️ Afternoon:** +* 🚨 Alert: latency spike +* 🔍 Check traces → slow DB query +* 🔧 Fix, test, deploy — **20 min total** + +**🌙 Evening:** +* 🤖 Systems run themselves +* 🏠 Go home on time + +--- + +## 📍 Slide 32 – 👥 DevOps Roles + +| 👤 Role | 🎯 Focus | +|---------|---------| +| 🔧 **DevOps Engineer** | Pipelines, automation, infra | +| 🛡️ **SRE** | Reliability, SLOs, incidents | +| 🏗️ **Platform Engineer** | Developer experience, internal tools | +| ☁️ **Cloud Engineer** | Cloud infra, cost optimization | + +**🔗 Common thread:** Collaboration, automation, ownership + +**💰 Salary Range (2024):** +* 🔧 DevOps Engineer: $100K - $180K +* 🛡️ SRE: $120K - $200K +* 🏗️ Platform Engineer: $130K - $220K + +--- + +## 📍 Slide 33 – 🤝 Team Collaboration + +```mermaid +flowchart TD + Dev[👨‍💻 Developers] --> Shared[🤝 Shared Ownership] + Ops[⚙️ Operations] --> Shared + QA[🧪 QA] --> Shared + Sec[🔐 Security] --> Shared + Shared --> Ship[🚀 Ship Better Software] +``` + +**🤝 Collaboration Practices:** +* 📟 Shared on-call rotations +* 📝 Blameless incident reviews +* 👥 Cross-functional squads +* 🔓 Everyone can deploy + +--- + +## 📍 Slide 34 – 📈 Career Path + +```mermaid +flowchart LR + Junior[🌱 Junior] --> Mid[💼 Mid-level] + Mid --> Senior[⭐ Senior] + Senior --> Staff[🏆 Staff/Principal] + Senior --> Manager[👔 Manager] + Staff --> Architect[🏛️ Architect] +``` + +**🛠️ Skills to Build:** +* 🐧 Linux, networking +* 📝 Scripting (Bash, Python) +* 🐳 Containers & K8s +* 🔄 CI/CD pipelines +* ☁️ Cloud platforms (AWS, GCP, Azure) + +--- + +## 📍 Slide 35 – 🌍 Real Company Examples + +**🎬 Netflix:** +* 🚀 1000+ deploys/day +* 🐒 Chaos Monkey breaks things on purpose +* 🔄 Self-healing infrastructure + +**📦 Amazon:** +* ⚡ Deploy every **11.7 seconds** +* 🔧 "You build it, you run it" +* 👥 Two-pizza teams + +**🔍 Google:** +* 🛡️ Invented **SRE** +* 📊 Error budgets balance speed & reliability +* 📝 Blameless postmortems + +--- + +## 📍 Slide 36 – 🎯 Section 6: Reflection + +## 📝 Key Takeaways + +1. 🧩 **DevOps = Culture + Practices + Tools** +2. 🧱 **Break down silos** between Dev and Ops +3. 🤖 **Automate everything** repeatable +4. 📊 **Measure what matters** (DORA metrics) +5. 📝 **Learn from failures**, don't assign blame + +> 💡 DevOps isn't a destination. It's a direction. + +--- + +## 📍 Slide 37 – 🧠 The Mindset Shift + +| 😰 Old Mindset | 🚀 DevOps Mindset | +|---------------|------------------| +| 🙅 "Not my job" | 🤝 "Our responsibility" | +| 🚫 "Don't touch prod" | 💪 "Deploy with confidence" | +| 👉 "Who broke it?" | 🔍 "How do we prevent this?" | +| 😨 "Change is risky" | ✅ "Small changes = less risk" | +| 💻 "Works on my machine" | 🌍 "Works everywhere" | + +> ❓ Which mindset do you want? + +--- + +## 📍 Slide 38 – ✅ Your Progress + +## 🎓 What You Now Understand + +* ✅ Why DevOps emerged and what it solves +* ✅ The Three Ways and CAMS model +* ✅ How CI/CD, IaC, and observability fit together +* ✅ The DevOps lifecycle and how to measure it +* ✅ Real-world application of DevOps + +> 🚀 **You're ready for the labs.** + +--- + +## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L1_POST + +--- + +## 📍 Slide 40 – 🚀 What Comes Next + +## 📚 Next Lecture: Version Control with Git + +* 🐙 Git fundamentals +* 🌿 Branching strategies +* 🤝 Collaboration workflows +* 💻 Hands-on: Your first pull request + +**🎉 Your journey has begun.** + +> 🌊 From chaos to flow — one commit at a time. + +```mermaid +flowchart LR + You[👤 You] --> Skills[🛠️ DevOps Skills] + Skills --> Impact[💎 Real Impact] + Impact --> Career[🚀 Career Growth] +``` + +**👋 See you in the next lecture!** + +--- + +## 📚 Resources & Further Reading + +**📕 Books:** +* 📖 *The Phoenix Project* — Gene Kim +* 📖 *The DevOps Handbook* — Gene Kim et al. +* 📖 *Accelerate* — Nicole Forsgren + +**🔗 Links:** +* 🌐 [DORA State of DevOps](https://dora.dev) +* 🌐 [DevOps Roadmap](https://roadmap.sh/devops) +* 🌐 [12 Factor App](https://12factor.net) + +--- diff --git a/lectures/lec10.md b/lectures/lec10.md new file mode 100644 index 0000000000..ff30eefd3e --- /dev/null +++ b/lectures/lec10.md @@ -0,0 +1,840 @@ +# 📌 Lecture 10 — Helm Package Management: Templating Kubernetes + +## 📍 Slide 1 – 🚀 Welcome to Helm + +* 🌍 **Kubernetes manifests are powerful** — but repetitive +* 😰 Copy-pasting YAML for different environments is error-prone +* ⛵ **Helm** = the package manager for Kubernetes +* 🎯 This lecture: master charts, templating, and values management + +```mermaid +flowchart LR + Manifests[📝 Raw YAML] -->|⛵ Helm| Charts[📦 Charts] + Charts --> Templating[🔧 Templating] + Templating --> Environments[🌍 Any Environment] +``` + +--- + +## 📍 Slide 2 – 🎯 What You Will Learn + +* ✅ Understand Helm architecture and concepts +* ✅ Create production-ready Helm charts +* ✅ Use templating for multi-environment deployments +* ✅ Implement lifecycle hooks for advanced scenarios + +**🎓 Learning Outcomes:** +| # | Outcome | +|---|---------| +| 1 | 🧠 Explain charts, releases, and repositories | +| 2 | 🔍 Create charts with proper templating | +| 3 | 🛠️ Manage values for different environments | +| 4 | 🗺️ Implement hooks for lifecycle management | + +--- + +## 📍 Slide 3 – 📋 How This Lecture Works + +* 📚 **Concepts + Go templates** — hands-on focus +* 🎮 **Real-world scenarios** — multi-environment challenges +* 📝 **3 quiz checkpoints**: PRE / MID / POST +* 🛠️ **Best practices**: DRY, hooks, library charts + +**⏱️ Lecture Structure:** +``` +Section 0: Introduction (now) → 📝 PRE Quiz +Section 1: The Manifest Problem +Section 2: Helm Fundamentals +Section 3: Templating Deep Dive → 📝 MID Quiz +Section 4: Hooks & Advanced +Section 5: Production Helm +Section 6: Reflection → 📝 POST Quiz +``` + +--- + +## 📍 Slide 4 – ❓ The Big Question + +* 📊 **89%** of Kubernetes users use Helm +* ⏱️ Managing 100+ YAML files manually is **chaos** +* 💥 Different configs per environment = **copy-paste errors** + +> 💬 *"Is this the dev or prod manifest? Why are they different?"* — Every DevOps engineer + +**🤔 Think about it:** +* How do you manage configs for dev, staging, and prod? +* How do you share common patterns across applications? +* How do you version your Kubernetes deployments? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L10_PRE + +--- + +## 📍 Slide 6 – 🔥 Section 1: The Manifest Problem + +* 📝 **Raw YAML** works for one environment +* 📋 Need different values for dev, staging, prod +* 🔧 Copy-paste → divergence → bugs +* 💥 Result: **manifest sprawl** + +```mermaid +flowchart LR + Base[📝 Base YAML] --> Dev[📝 Dev YAML] + Base --> Staging[📝 Staging YAML] + Base --> Prod[📝 Prod YAML] + Dev --> Drift1[😱 Drift] + Staging --> Drift2[😱 Drift] + Prod --> Drift3[😱 Drift] +``` + +--- + +## 📍 Slide 7 – 😱 YAML Duplication + +* 📋 Same deployment, different image tags +* 📊 Same service, different replicas +* 🔧 Same ingress, different domains +* 💀 Changes require updating multiple files + +```yaml +# 😰 dev-deployment.yaml +replicas: 1 +image: myapp:latest + +# 😰 staging-deployment.yaml +replicas: 2 +image: myapp:v1.2.3 + +# 😰 prod-deployment.yaml +replicas: 5 +image: myapp:v1.2.3 +``` + +**📊 The Problem:** +* 🔍 Fix a bug? Update 3 files +* 🆕 New field? Add to all files +* 😰 Easy to miss one file + +--- + +## 📍 Slide 8 – 🔧 Manual Substitution Problems + +* 📝 `sed` and `envsubst` are fragile +* 🔍 No validation of resulting YAML +* 📊 No understanding of Kubernetes resources +* 💀 Silent failures + +> ⚠️ **sed is not a package manager** + +```bash +# 😰 This is fragile +sed -i "s/REPLICAS/3/g" deployment.yaml +envsubst < deployment.yaml.template > deployment.yaml +``` + +**💬 Discussion:** How do you currently manage environment differences? + +--- + +## 📍 Slide 9 – 😨 Version Chaos + +* 📅 "Which version is deployed in prod?" +* 🔧 No rollback mechanism +* 📋 No deployment history +* 💀 Can't reproduce past deployments + +> ⚠️ **Without versioning, you can't roll back safely** + +```mermaid +flowchart TD + Deploy1[📦 Deploy v1] --> Deploy2[📦 Deploy v2] + Deploy2 --> Deploy3[📦 Deploy v3] + Deploy3 --> Broken[💥 Broken!] + Broken --> Question[❓ What was v2?] +``` + +--- + +## 📍 Slide 10 – 💸 The Cost of Manifest Sprawl + +| 🔥 Problem | 💥 Impact | +|------------|-----------| +| 🐢 Update all files | Slow, error-prone | +| 📋 Inconsistency | "Works in dev, not prod" | +| 👉 No history | Can't audit changes | +| 🙈 No versioning | Risky rollbacks | + +**📈 Real Numbers:** +* 🏢 **Average K8s app**: 5-20 YAML files +* 🔄 **Environments**: 3-5 (dev, staging, prod, etc.) +* 📊 **Total files**: 15-100 per app (without Helm) +* ⛵ **With Helm**: 1 chart, unlimited environments + +--- + +## 📍 Slide 11 – 💡 Section 2: What Helm Is + +* ⛵ **Package manager** for Kubernetes +* 📦 **Charts** = packages of K8s resources +* 🔧 **Templating** = dynamic manifest generation +* 🔄 **Releases** = installed chart instances + +```mermaid +flowchart LR + Chart[📦 Chart] -->|🔧 + Values| Template[🔄 Templating] + Template --> Manifest[📝 K8s Manifests] + Manifest --> Release[🚀 Release] +``` + +**📖 Definition:** +> *Helm is a package manager for Kubernetes that helps you define, install, and upgrade complex Kubernetes applications using charts (packages of pre-configured resources).* + +--- + +## 📍 Slide 12 – 📦 Core Concepts + +```mermaid +flowchart TD + Chart[📦 Chart] --> Templates[📝 Templates] + Chart --> Values[📊 Values] + Chart --> ChartYaml[📋 Chart.yaml] + Templates -->|+| Values + Values --> Release[🚀 Release] +``` + +| 📦 Concept | 🎯 Purpose | +|-----------|----------| +| 📦 **Chart** | Package of K8s resources | +| 🚀 **Release** | Installed instance of chart | +| 📊 **Values** | Configuration parameters | +| 📁 **Repository** | Collection of charts | + +--- + +## 📍 Slide 13 – 📁 Chart Structure + +``` +mychart/ +├── Chart.yaml # 📋 Chart metadata +├── values.yaml # 📊 Default values +├── charts/ # 📦 Dependencies +└── templates/ # 📝 K8s manifests + ├── deployment.yaml + ├── service.yaml + ├── _helpers.tpl # 🔧 Template helpers + └── NOTES.txt # 📝 Post-install notes +``` + +**🔑 Key Files:** +* 📋 `Chart.yaml` — Name, version, description +* 📊 `values.yaml` — Default configuration +* 📝 `templates/` — Go templates for manifests +* 🔧 `_helpers.tpl` — Reusable template snippets + +--- + +## 📍 Slide 14 – 📋 Chart.yaml + +```yaml +apiVersion: v2 +name: my-web-app +description: A Helm chart for my web application +type: application + +# 📊 Chart version (SemVer) +version: 0.1.0 + +# 📦 Application version +appVersion: "1.0.0" + +# 📦 Dependencies +dependencies: + - name: common + version: 0.1.0 + repository: "file://../common" +``` + +**🔑 Important Fields:** +* `version` — Chart version (bump when chart changes) +* `appVersion` — Application version (your app's version) +* `dependencies` — Other charts this depends on + +--- + +## 📍 Slide 15 – ⚡ Before vs After Helm + +| 😰 Before | 🚀 After | +|----------|---------| +| 📅 Multiple YAML files per env | 📊 One values file per env | +| 📋 Manual substitution | 🔧 Go templating | +| 👉 No versioning | 📦 SemVer releases | +| 😨 Risky rollbacks | 🔙 `helm rollback` | +| 🐌 Copy-paste changes | ⚡ Single source of truth | +| 📝 No sharing | 📁 Chart repositories | + +> 🤔 Ready to package your Kubernetes apps? + +--- + +## 📍 Slide 16 – 🎮 Section 3: Templating Deep Dive + +## 🔧 Go Template Basics + +```yaml +# templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-app + labels: + app: {{ .Values.appName }} +spec: + replicas: {{ .Values.replicaCount }} + template: + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" +``` + +**🔧 Template Syntax:** +* `{{ }}` — Template action +* `.Values` — From values.yaml +* `.Release` — Release information +* `.Chart` — From Chart.yaml + +--- + +## 📍 Slide 17 – 📊 Values Management + +```yaml +# values.yaml (defaults) +replicaCount: 1 +appName: my-app + +image: + repository: myuser/myapp + tag: latest + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + +resources: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 100m + memory: 128Mi +``` + +**🔧 Override Values:** +```bash +# File override +helm install myrelease ./mychart -f values-prod.yaml + +# Command line override +helm install myrelease ./mychart --set replicaCount=5 +``` + +--- + +## 📍 Slide 18 – 🌍 Multi-Environment Values + +```yaml +# values-dev.yaml +replicaCount: 1 +image: + tag: latest +resources: + limits: + cpu: 100m + memory: 128Mi + +# values-prod.yaml +replicaCount: 5 +image: + tag: v1.2.3 +resources: + limits: + cpu: 500m + memory: 512Mi +``` + +**🚀 Deploy to Different Environments:** +```bash +# Development +helm install myapp-dev ./mychart -f values-dev.yaml + +# Production +helm install myapp-prod ./mychart -f values-prod.yaml +``` + +--- + +## 📍 Slide 19 – 🔧 Template Functions + +```yaml +# Using functions +name: {{ .Values.name | lower | trunc 63 }} + +# Default values +tag: {{ .Values.image.tag | default .Chart.AppVersion }} + +# Conditional +{{- if .Values.ingress.enabled }} +# ... ingress resource +{{- end }} + +# Range (loop) +{{- range .Values.env }} +- name: {{ .name }} + value: {{ .value | quote }} +{{- end }} +``` + +**🔧 Common Functions:** +| 🔧 Function | 🎯 Purpose | +|------------|----------| +| `default` | Provide fallback value | +| `quote` | Add quotes | +| `lower/upper` | Case conversion | +| `trunc` | Truncate string | +| `include` | Include template | + +--- + +## 📍 Slide 20 – 🔧 Helper Templates + +```yaml +# templates/_helpers.tpl +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "mychart.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "mychart.labels" -}} +helm.sh/chart: {{ include "mychart.chart" . }} +app.kubernetes.io/name: {{ .Chart.Name }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} +``` + +**🔧 Using Helpers:** +```yaml +metadata: + labels: + {{- include "mychart.labels" . | nindent 4 }} +``` + +--- + +## 📍 Slide 21 – 📊 Built-in Objects + +```mermaid +flowchart TD + Objects[📦 Built-in Objects] + Objects --> Values[.Values] + Objects --> Chart[.Chart] + Objects --> Release[.Release] + Objects --> Template[.Template] + Objects --> Files[.Files] +``` + +| 📦 Object | 🎯 Contains | +|----------|----------| +| `.Values` | Values from values.yaml + overrides | +| `.Chart` | Contents of Chart.yaml | +| `.Release` | Release name, namespace, revision | +| `.Template` | Current template info | +| `.Files` | Access to non-template files | + +--- + +## 📍 Slide 22 – 🧪 Testing Charts + +```bash +# 📋 Lint chart for errors +helm lint ./mychart + +# 📝 Render templates locally +helm template myrelease ./mychart + +# 🔍 Dry run against cluster +helm install --dry-run --debug myrelease ./mychart + +# 📊 Show computed values +helm get values myrelease + +# 📝 Show rendered manifests +helm get manifest myrelease +``` + +**🧪 Testing Workflow:** +1. 📋 `helm lint` — syntax check +2. 📝 `helm template` — verify output +3. 🔍 `--dry-run` — validate against cluster +4. 🚀 `helm install` — deploy + +--- + +## 📍 Slide 23 – 📊 Helm Commands + +```bash +# 📦 Create new chart +helm create mychart + +# 🚀 Install chart +helm install myrelease ./mychart + +# 📋 List releases +helm list + +# 🔄 Upgrade release +helm upgrade myrelease ./mychart + +# 🔙 Rollback release +helm rollback myrelease 1 + +# 🗑️ Uninstall release +helm uninstall myrelease + +# 📊 Show release history +helm history myrelease +``` + +--- + +## 📍 Slide 24 – 🔗 Chart Dependencies + +```yaml +# Chart.yaml +dependencies: + - name: postgresql + version: 12.0.0 + repository: https://charts.bitnami.com/bitnami + condition: postgresql.enabled +``` + +```bash +# Download dependencies +helm dependency update ./mychart + +# Build dependencies +helm dependency build ./mychart +``` + +**🔗 Dependency Features:** +* 📦 Include other charts as sub-charts +* 🔧 Override sub-chart values +* 🔀 Conditional inclusion + +--- + +## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L10_MID + +--- + +## 📍 Slide 26 – 🎣 Section 4: Lifecycle Hooks + +## 🎣 What Are Hooks? + +* 🎯 **Execute actions** at specific points +* 📦 Run jobs before/after install/upgrade +* 🗑️ Cleanup after completion +* 🔧 Database migrations, tests, notifications + +```mermaid +flowchart LR + PreInstall[🎣 pre-install] --> Install[🚀 Install] + Install --> PostInstall[🎣 post-install] +``` + +--- + +## 📍 Slide 27 – 🎣 Hook Types + +| 🎣 Hook | ⏱️ When | +|--------|--------| +| `pre-install` | Before resources installed | +| `post-install` | After all resources ready | +| `pre-upgrade` | Before upgrade | +| `post-upgrade` | After upgrade complete | +| `pre-delete` | Before deletion | +| `post-delete` | After deletion | +| `pre-rollback` | Before rollback | +| `post-rollback` | After rollback | + +--- + +## 📍 Slide 28 – 📝 Hook Example + +```yaml +# templates/pre-install-job.yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-pre-install + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-weight": "-5" + "helm.sh/hook-delete-policy": hook-succeeded +spec: + template: + spec: + restartPolicy: Never + containers: + - name: pre-install + image: busybox + command: ['sh', '-c', 'echo Pre-install running && sleep 5'] +``` + +**🔑 Hook Annotations:** +* `helm.sh/hook` — Hook type +* `helm.sh/hook-weight` — Execution order (lower first) +* `helm.sh/hook-delete-policy` — When to delete + +--- + +## 📍 Slide 29 – 🏗️ Library Charts + +```mermaid +flowchart TD + Library[📚 Library Chart] --> App1[📦 App 1] + Library --> App2[📦 App 2] + Library --> App3[📦 App 3] +``` + +**📚 Library Chart:** +* 🚫 Cannot be installed directly +* 📝 Contains only templates +* 🔄 Shared across multiple charts + +```yaml +# Chart.yaml +apiVersion: v2 +name: common-lib +type: library # 📚 Library type +version: 0.1.0 +``` + +--- + +## 📍 Slide 30 – 📊 Helm Metrics + +| 📊 Metric | 📏 Measures | 🏆 Target | +|-----------|------------|---------| +| 📦 **Chart Version** | Tracking | SemVer | +| 🔄 **Release Revision** | Upgrade count | Documented | +| ⏱️ **Deploy Time** | Chart install | < 5 min | +| 🧪 **Lint Errors** | Chart quality | 0 | + +> 📚 Version everything! + +**🤔 Question:** How do you track what's deployed? + +--- + +## 📍 Slide 31 – 🏢 Section 5: Production Helm + +## 📅 A Day with Helm + +**☀️ Morning:** +* 📋 Review chart PR +* 🧪 `helm lint` and `helm template` +* ✅ Merge changes + +**🌤️ Afternoon:** +* 📊 Update values-prod.yaml +* 🚀 `helm upgrade myapp ./mychart -f values-prod.yaml` +* 📈 Watch rollout: `kubectl rollout status` + +**🌙 Evening:** +* 💥 Issue detected +* 🔙 `helm rollback myapp 3` +* ⏱️ **Rollback in 30 seconds** + +--- + +## 📍 Slide 32 – 👥 Team Helm Workflow + +| 👤 Role | 🎯 Helm Responsibility | +|---------|----------------------| +| 👨‍💻 **Developer** | Define values requirements | +| 🔧 **DevOps** | Create and maintain charts | +| 🛡️ **SRE** | Manage releases, rollbacks | +| 📊 **Platform** | Build chart standards | + +**🔗 GitOps Flow:** +```mermaid +flowchart LR + PR[📝 Chart PR] --> Lint[🧪 Lint] + Lint --> Review[👀 Review] + Review --> Merge[✅ Merge] + Merge --> ArgoCD[🔄 ArgoCD] + ArgoCD --> Helm[⛵ Helm Install] +``` + +--- + +## 📍 Slide 33 – 🔐 Production Best Practices + +```yaml +# ✅ Good: Specific versions +image: + tag: v1.2.3 # Not 'latest' + +# ✅ Good: Resource limits always +resources: + limits: + cpu: 500m + memory: 512Mi + +# ✅ Good: Health probes always +livenessProbe: + enabled: true +readinessProbe: + enabled: true +``` + +**🛡️ Production Checklist:** +* ✅ Specific image tags (not `latest`) +* ✅ Resource limits defined +* ✅ Health probes enabled +* ✅ Values documented +* ✅ Chart versioned with SemVer + +--- + +## 📍 Slide 34 – 📈 Career Path: Helm Skills + +```mermaid +flowchart LR + Junior[🌱 Junior: Using charts] --> Mid[💼 Mid: Creating charts] + Mid --> Senior[⭐ Senior: Library charts & standards] + Senior --> Principal[🏆 Principal: Chart ecosystem] +``` + +**🛠️ Skills to Build:** +* 📝 Go template fluency +* 📦 Chart design patterns +* 🔗 Dependency management +* 🎣 Hook implementation +* 📁 Repository management + +--- + +## 📍 Slide 35 – 🌍 Real Company Examples + +**🏢 Helm at Scale:** +* 📦 **Bitnami**: 100+ production charts +* 🔍 **Google**: GKE uses Helm internally +* 🎬 **Netflix**: Custom chart ecosystem + +**☁️ Public Charts:** +* 📊 **Prometheus**: helm-charts/prometheus +* 📋 **Grafana**: helm-charts/grafana +* 🐘 **PostgreSQL**: bitnami/postgresql + +**📊 Stats:** +* ⛵ **10,000+** public charts +* 📦 **89%** K8s users use Helm +* 🏢 **Standard** for K8s packaging + +--- + +## 📍 Slide 36 – 🎯 Section 6: Reflection + +## 📝 Key Takeaways + +1. ⛵ **Helm is the package manager** for Kubernetes +2. 📦 **Charts package** related K8s resources +3. 🔧 **Templating** enables multi-environment deploys +4. 📊 **Values** customize without changing templates +5. 🎣 **Hooks** handle lifecycle events + +> 💡 Never hardcode in templates — parametrize everything. + +--- + +## 📍 Slide 37 – 🧠 The Mindset Shift + +| 😰 Old Mindset | ⛵ Helm Mindset | +|---------------|------------------| +| 🙅 "Copy YAML for each env" | 📊 "Different values, same chart" | +| 🚫 "sed for substitution" | 🔧 "Go templates" | +| 👉 "Manual versioning" | 📦 "SemVer releases" | +| 😨 "Risky rollbacks" | 🔙 "helm rollback" | +| 💻 "My chart, my rules" | 📚 "Shared libraries" | + +> ❓ Which mindset describes your team? + +--- + +## 📍 Slide 38 – ✅ Your Progress + +## 🎓 What You Now Understand + +* ✅ Helm architecture and concepts +* ✅ Chart creation and structure +* ✅ Go template syntax +* ✅ Multi-environment values management +* ✅ Lifecycle hooks + +> 🚀 **You're ready for Lab 10: Helm Charts** + +--- + +## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L10_POST + +--- + +## 📍 Slide 40 – 🚀 What Comes Next + +## 📚 Course Continuation + +* 🔐 Lab 11: Secrets with Vault +* ⚙️ Lab 12: ConfigMaps +* 🔄 Lab 13: ArgoCD GitOps +* 📊 Lab 14: StatefulSets +* 🔍 Lab 15: K8s Monitoring + +**🎉 You've completed the Helm fundamentals!** + +> ⛵ From raw YAML to packaged charts — one template at a time. + +```mermaid +flowchart LR + You[👤 You] --> Helm[⛵ Helm Skills] + Helm --> Packaging[📦 K8s Packaging] + Packaging --> Career[🚀 Career Growth] +``` + +**👋 Continue your DevOps journey!** + +--- + +## 📚 Resources & Further Reading + +**📕 Books:** +* 📖 *Learning Helm* — Matt Butcher +* 📖 *Helm in Action* — Matt Palmer +* 📖 *Kubernetes Patterns* — Bilgin Ibryam + +**🔗 Links:** +* 🌐 [Helm Documentation](https://helm.sh/docs/) +* 🌐 [Chart Best Practices](https://helm.sh/docs/chart_best_practices/) +* 🌐 [Artifact Hub](https://artifacthub.io/) + +--- diff --git a/lectures/lec11.md b/lectures/lec11.md new file mode 100644 index 0000000000..779e7917bd --- /dev/null +++ b/lectures/lec11.md @@ -0,0 +1,759 @@ +# 📌 Lecture 11 — Secret Management: Protecting Your Crown Jewels + +## 📍 Slide 1 – 🔐 Welcome to Secret Management + +* 🌍 **Your Helm charts are beautiful** — but where do passwords go? +* 😰 Hardcoded secrets in code = ticking time bomb +* 🔐 **Secret management** = keeping credentials safe AND accessible +* 🎯 This lecture: from base64 encoding to enterprise-grade Vault + +```mermaid +flowchart LR + Bad[😱 Hardcoded] -->|🔐 Secrets| K8s[☸️ K8s Secrets] + K8s -->|🏰 Enterprise| Vault[🔒 HashiCorp Vault] + Vault --> Secure[✅ Secure Apps] +``` + +--- + +## 📍 Slide 2 – 🎯 What You Will Learn + +* ✅ Understand why secret management matters +* ✅ Create and consume Kubernetes Secrets +* ✅ Recognize encoding vs encryption difference +* ✅ Integrate HashiCorp Vault with Kubernetes + +**🎓 Learning Outcomes:** +| # | Outcome | +|---|---------| +| 1 | 🧠 Explain the risks of poor secret management | +| 2 | 🔍 Create K8s Secrets via kubectl and Helm | +| 3 | 🛠️ Configure Vault sidecar injection | +| 4 | 🗺️ Choose appropriate secret management strategy | + +--- + +## 📍 Slide 3 – 📋 How This Lecture Works + +* 🔐 **Security-first mindset** — think like an attacker +* 🎮 **Real breach scenarios** — learn from others' mistakes +* 📝 **3 quiz checkpoints**: PRE / MID / POST +* 🛠️ **Hands-on patterns**: Secrets, Vault, injection + +**⏱️ Lecture Structure:** +``` +Section 0: Introduction → 📝 PRE Quiz +Section 1: The Secrets Problem +Section 2: Kubernetes Secrets +Section 3: Encoding vs Encryption → 📝 MID Quiz +Section 4: HashiCorp Vault +Section 5: Production Patterns → 📝 POST Quiz +``` + +--- + +## 📍 Slide 4 – 💀 The Big Question + +> 💬 *"The only truly secure system is one that is powered off, cast in a block of concrete and sealed in a lead-lined room with armed guards."* — Gene Spafford + +**🔥 Shocking Stats:** +* 😱 **83%** of organizations have experienced credential theft +* 💸 Average cost of data breach: **$4.45 million** (2023) +* ⏱️ Average time to detect breach: **277 days** + +> 🤔 **Think:** How many passwords are hardcoded in YOUR projects right now? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L11_PRE + +--- + +## 📍 Slide 6 – 🔥 Section 1: The Secrets Problem + +* 🎯 **The Challenge:** Apps need credentials to function +* ⚔️ **The Conflict:** Security vs Convenience + +```mermaid +flowchart LR + subgraph 😰 Developer + D1[🚀 Ship Fast] + D2[🔧 Easy Access] + end + subgraph 🔐 Security + S1[🛡️ Protect Data] + S2[📋 Audit Access] + end + D1 <-->|⚔️ Tension| S1 + D2 <-->|⚔️ Tension| S2 +``` + +> 🤔 **Discussion:** Have you ever committed a password to git? + +--- + +## 📍 Slide 7 – 😱 The Hardcoding Horror + +**❌ What developers actually do:** + +```python +# ❌ BAD: Hardcoded in code +DATABASE_URL = "postgres://admin:SuperSecret123@db.prod.com/myapp" +API_KEY = "sk-1234567890abcdef" + +# ❌ BAD: In docker-compose.yml committed to git +environment: + - DB_PASSWORD=MyPassword123 +``` + +**💥 What can go wrong:** +* 🔍 Git history is forever (even after deletion) +* 🌍 Public repos = public secrets +* 👥 Every developer has production passwords +* 📝 No audit trail of who accessed what + +--- + +## 📍 Slide 8 – 💥 Real Breach: Uber 2016 + +**📰 What Happened:** +* 😱 Developers hardcoded AWS credentials in GitHub repo +* 🔓 Attackers found credentials, accessed S3 bucket +* 💾 **57 million** user records stolen +* 💸 **$148 million** settlement + +```mermaid +flowchart LR + A[👨‍💻 Dev commits AWS keys] --> B[🔍 Attacker finds repo] + B --> C[🔓 Access S3 bucket] + C --> D[💾 57M records stolen] + D --> E[💸 $148M settlement] +``` + +> ⚠️ **Lesson:** Secrets in code = breach waiting to happen + +--- + +## 📍 Slide 9 – 🔓 Environment Variables: Better but Not Enough + +**✅ Better than hardcoding:** +```bash +export DATABASE_PASSWORD="secret123" +``` + +**❌ Still problematic:** +* 📋 `ps aux` can expose env vars +* 🐳 Docker inspect shows environment +* 📝 No encryption at rest +* 🔄 No rotation mechanism +* 👥 No access control + +```bash +# Anyone on the system can see: +$ docker inspect myapp | grep -A 10 "Env" +``` + +> 🤔 **Think:** Where do YOUR environment variables come from? + +--- + +## 📍 Slide 10 – 📊 The Cost of Poor Secret Management + +| 🔥 Problem | 💥 Impact | 📊 Stats | +|------------|-----------|----------| +| 😱 Leaked credentials | 🔓 Unauthorized access | 83% of breaches | +| 🔄 No rotation | 📅 Stale passwords | Avg age: 2+ years | +| 👥 Shared secrets | 🕵️ No accountability | 65% share creds | +| 📝 No audit | 🤷 Unknown access | 70% can't audit | + +**💡 The Solution Spectrum:** + +```mermaid +flowchart LR + A[😱 Hardcoded] --> B[🔧 Env Vars] + B --> C[☸️ K8s Secrets] + C --> D[🔒 Vault] + style A fill:#ff6b6b + style B fill:#ffd93d + style C fill:#6bcb77 + style D fill:#4d96ff +``` + +--- + +## 📍 Slide 11 – ☸️ Section 2: Kubernetes Secrets + +**🎯 What are K8s Secrets?** +* 📦 First-class Kubernetes objects for sensitive data +* 🔐 Separate from ConfigMaps (security-focused) +* 🚀 Native integration with pods + +```mermaid +flowchart TD + Secret[🔐 Secret] --> |Volume| Pod1[📦 Pod] + Secret --> |Env Var| Pod2[📦 Pod] + Secret --> |API| Pod3[📦 Pod] +``` + +**📋 Secret Types:** +* 🔑 `Opaque` — generic key-value +* 🐳 `docker-registry` — image pull credentials +* 🔒 `tls` — TLS certificates + +--- + +## 📍 Slide 12 – 🛠️ Creating Secrets with kubectl + +**📝 From literals:** +```bash +kubectl create secret generic db-creds \ + --from-literal=username=admin \ + --from-literal=password=SuperSecret123 +``` + +**📁 From files:** +```bash +kubectl create secret generic tls-cert \ + --from-file=cert.pem \ + --from-file=key.pem +``` + +**👀 Viewing secrets:** +```bash +kubectl get secret db-creds -o yaml +# Data is base64 encoded + +# Decode: +echo "U3VwZXJTZWNyZXQxMjM=" | base64 -d +# Output: SuperSecret123 +``` + +--- + +## 📍 Slide 13 – ⚠️ The Base64 Trap + +> ⚠️ **Critical Understanding:** Base64 is ENCODING, not ENCRYPTION! + +```bash +# Encoding (reversible by anyone): +echo "password123" | base64 +# cGFzc3dvcmQxMjMK + +# Decoding (no key needed): +echo "cGFzc3dvcmQxMjMK" | base64 -d +# password123 +``` + +**🔐 Encryption vs Encoding:** + +| 🔄 Encoding | 🔐 Encryption | +|-------------|---------------| +| ✅ Reversible by anyone | 🔑 Needs key to decrypt | +| 📝 Not secure | 🔒 Mathematically secure | +| 🚀 Fast, no overhead | ⚡ Computational cost | +| 📦 Data format change | 🛡️ Confidentiality | + +--- + +## 📍 Slide 14 – 📦 Consuming Secrets in Pods + +**🔧 As environment variables:** +```yaml +env: + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: db-creds + key: password +``` + +**📁 As volume mount:** +```yaml +volumes: + - name: secret-volume + secret: + secretName: db-creds +containers: + - volumeMounts: + - name: secret-volume + mountPath: /etc/secrets + readOnly: true +``` + +> 💡 **Best Practice:** Volume mounts are more secure than env vars (not visible in `docker inspect`) + +--- + +## 📍 Slide 15 – 📊 Before vs After: Basic Secret Management + +| 😱 Before (Hardcoded) | ✅ After (K8s Secrets) | +|-----------------------|------------------------| +| 📝 Secrets in code | 📦 Secrets in K8s API | +| 🌍 Visible in git history | 🔐 Separate from code | +| 👥 Everyone has access | 🛡️ RBAC controls | +| 🔄 Change = redeploy code | 🔧 Change secret only | +| 📋 No audit trail | 📝 K8s audit logs | + +> 🤔 **Question:** Is K8s Secrets enough for production? + +--- + +## 📍 Slide 16 – 🔒 Section 3: etcd Encryption + +**😰 The Problem:** +* 🗄️ K8s stores secrets in etcd +* 📝 By default: base64 encoded only +* 🔓 etcd access = all secrets exposed + +**✅ The Solution: Encryption at Rest** +```yaml +apiVersion: apiserver.config.k8s.io/v1 +kind: EncryptionConfiguration +resources: + - resources: + - secrets + providers: + - aescbc: + keys: + - name: key1 + secret: + - identity: {} +``` + +--- + +## 📍 Slide 17 – 🔐 K8s Secrets Limitations + +**⚠️ Still Missing:** +* 🔄 **No automatic rotation** — manual process +* 📊 **Limited audit** — who accessed what? +* 🌍 **K8s-only** — what about non-K8s apps? +* 🔑 **Static secrets** — no dynamic generation +* 🏢 **No centralization** — per-cluster management + +```mermaid +flowchart TD + subgraph 😰 Limitations + A[🔄 No Rotation] + B[📊 Limited Audit] + C[🌍 K8s Only] + D[🔑 Static Only] + end + E[🏰 Need: Enterprise Solution] --> F[🔒 HashiCorp Vault] +``` + +--- + +## 📍 Slide 18 – 📝 QUIZ — DEVOPS_L11_MID + +--- + +## 📍 Slide 19 – 🏰 Section 4: HashiCorp Vault + +**🎯 What is Vault?** +* 🔐 Enterprise-grade secret management +* 🔑 Dynamic secret generation +* 📊 Complete audit logging +* 🔄 Automatic rotation +* 🌍 Platform agnostic + +```mermaid +flowchart LR + subgraph 🏰 Vault + A[🔐 Secret Engine] + B[🔑 Auth Methods] + C[📋 Policies] + D[📊 Audit] + end + K8s[☸️ Kubernetes] --> B + B --> A + A --> Apps[📦 Applications] + D --> Logs[📝 Audit Logs] +``` + +--- + +## 📍 Slide 20 – 🏗️ Vault Architecture + +```mermaid +flowchart TD + subgraph 👥 Clients + K8s[☸️ K8s Pods] + CLI[💻 CLI] + API[🔌 API] + end + subgraph 🏰 Vault Server + Auth[🔑 Auth Methods] + Policy[📋 Policies] + Secrets[🔐 Secret Engines] + Audit[📊 Audit Device] + end + subgraph 💾 Storage + Backend[🗄️ Storage Backend] + end + K8s --> Auth + CLI --> Auth + API --> Auth + Auth --> Policy + Policy --> Secrets + Secrets --> Backend + Auth --> Audit +``` + +**🔑 Key Concepts:** +* 🔐 **Secret Engines** — where secrets live (KV, database, PKI) +* 🔑 **Auth Methods** — how clients authenticate +* 📋 **Policies** — who can access what + +--- + +## 📍 Slide 21 – 🔑 Vault Auth Methods + +| 🔑 Method | 📝 Description | 🎯 Use Case | +|-----------|----------------|-------------| +| ☸️ Kubernetes | Service account JWT | K8s pods | +| 🔐 AppRole | Role ID + Secret ID | CI/CD pipelines | +| 👤 Userpass | Username/password | Humans | +| 🌐 OIDC | SSO integration | Enterprise SSO | +| ☁️ AWS/GCP/Azure | Cloud IAM | Cloud workloads | + +**☸️ Kubernetes Auth Flow:** +```mermaid +sequenceDiagram + Pod->>Vault: JWT token (ServiceAccount) + Vault->>K8s API: Validate token + K8s API->>Vault: Token valid ✅ + Vault->>Pod: Vault token + secrets +``` + +--- + +## 📍 Slide 22 – 📋 Vault Policies + +**🎯 Policies control access:** +```hcl +# Allow read on specific path +path "secret/data/myapp/*" { + capabilities = ["read", "list"] +} + +# Deny access to admin secrets +path "secret/data/admin/*" { + capabilities = ["deny"] +} +``` + +**🛡️ Principle of Least Privilege:** +* ✅ Apps only access their secrets +* ✅ Read-only where possible +* ✅ Separate policies per environment + +--- + +## 📍 Slide 23 – 💉 Vault Agent Sidecar Injection + +**🎯 The Pattern:** +* 📦 Vault Agent runs as sidecar container +* 🔄 Automatically fetches and renews secrets +* 📁 Writes secrets to shared volume +* 🚀 App reads from filesystem + +```mermaid +flowchart LR + subgraph 📦 Pod + App[🚀 App Container] + Agent[🔐 Vault Agent] + Vol[📁 Shared Volume] + end + Vault[🏰 Vault Server] + Agent -->|🔑 Auth| Vault + Vault -->|🔐 Secrets| Agent + Agent -->|📝 Write| Vol + App -->|📖 Read| Vol +``` + +--- + +## 📍 Slide 24 – 🏷️ Vault Annotations + +**📝 Enable injection:** +```yaml +metadata: + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/role: "myapp" + vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config" +``` + +**📁 Secrets appear at:** +``` +/vault/secrets/config +``` + +**🔧 Template for custom format:** +```yaml +vault.hashicorp.com/agent-inject-template-config: | + {{- with secret "secret/data/myapp/config" -}} + DB_PASSWORD={{ .Data.data.password }} + {{- end -}} +``` + +--- + +## 📍 Slide 25 – 🚀 Vault in Kubernetes: Full Flow + +```mermaid +sequenceDiagram + participant Pod + participant Injector as Vault Injector + participant Agent as Vault Agent + participant Vault + + Pod->>Injector: Pod created with annotations + Injector->>Pod: Inject sidecar container + Agent->>Vault: Authenticate (K8s JWT) + Vault->>Agent: Return Vault token + Agent->>Vault: Request secrets + Vault->>Agent: Return secrets + Agent->>Pod: Write to /vault/secrets/ + Pod->>Pod: App reads secrets +``` + +--- + +## 📍 Slide 26 – 🔄 Section 5: Dynamic Secrets + +**🎯 Static vs Dynamic:** + +| 🔑 Static Secrets | 🔄 Dynamic Secrets | +|-------------------|-------------------| +| 📝 Created manually | 🤖 Generated on-demand | +| ♾️ Live forever | ⏱️ Short TTL | +| 👥 Shared | 👤 Unique per request | +| 🔄 Manual rotation | 🔄 Auto-expires | + +**💡 Example: Database credentials** +```bash +vault read database/creds/readonly +# Key Value +# lease_id database/creds/readonly/abc123 +# lease_duration 1h +# username v-kubernetes-readonly-xyz789 +# password A1b2C3d4E5f6G7h8 +``` + +--- + +## 📍 Slide 27 – 📊 Secret Management Comparison + +| 🔧 Feature | 🔓 Env Vars | ☸️ K8s Secrets | 🏰 Vault | +|------------|-------------|----------------|----------| +| 🔐 Encryption | ❌ None | ⚠️ Optional | ✅ Always | +| 🔄 Rotation | ❌ Manual | ❌ Manual | ✅ Auto | +| 📊 Audit | ❌ None | ⚠️ Basic | ✅ Full | +| 🔑 Dynamic | ❌ No | ❌ No | ✅ Yes | +| 🌍 Multi-platform | ✅ Yes | ❌ K8s only | ✅ Yes | +| 📈 Complexity | 🟢 Low | 🟡 Medium | 🔴 High | + +--- + +## 📍 Slide 28 – 🗺️ Course Context: Where Secrets Fit + +```mermaid +flowchart TD + subgraph 🏗️ Foundation + L2[📦 Lab 2: Docker] + L10[⛵ Lab 10: Helm] + end + subgraph 🔐 Security + L11[🔒 Lab 11: Secrets] + end + subgraph 📋 Config + L12[📁 Lab 12: ConfigMaps] + end + subgraph 🚀 Deployment + L13[🔄 Lab 13: ArgoCD] + end + L2 --> L10 + L10 --> L11 + L11 --> L12 + L12 --> L13 + style L11 fill:#4d96ff +``` + +--- + +## 📍 Slide 29 – 📈 Security Metrics + +| 📊 Metric | 📝 Description | 🎯 Target | +|-----------|----------------|-----------| +| 🔄 Secret Age | Time since rotation | < 90 days | +| 📊 Access Audit | % of accesses logged | 100% | +| 🔐 Encryption | % secrets encrypted | 100% | +| 👥 Shared Secrets | Secrets used by >1 app | 0 | +| ⏱️ TTL Compliance | Secrets with TTL | > 80% | + +> 🤔 **Question:** How would you measure secret security in your organization? + +--- + +## 📍 Slide 30 – ✅ Secret Management Best Practices + +**🛡️ The Golden Rules:** + +1. 🚫 **Never commit secrets** to version control +2. 🔄 **Rotate regularly** — automate where possible +3. 📋 **Audit everything** — know who accessed what +4. 🔐 **Encrypt at rest** — etcd encryption minimum +5. 👤 **Least privilege** — only what's needed +6. ⏱️ **Short-lived** — dynamic secrets when possible + +```mermaid +flowchart LR + A[🔐 Encrypt] --> B[🔄 Rotate] + B --> C[📋 Audit] + C --> D[👤 Least Privilege] + D --> A +``` + +--- + +## 📍 Slide 31 – 👨‍💻 Day in the Life: Secret Management + +**☀️ Morning:** +* ☕ Check Vault audit logs for anomalies +* 🔄 Review expiring secrets dashboard +* 📋 Approve new secret access requests + +**🌤️ Afternoon:** +* 🛠️ Help dev team configure Vault injection +* 📝 Update policies for new microservice +* 🔐 Rotate database credentials (automated) + +**🌙 Evening:** +* 📊 Review daily access report +* 🔔 Set up alerts for unusual patterns +* 📚 Document new secret paths + +--- + +## 📍 Slide 32 – 👥 Roles & Secret Management + +| 👤 Role | 🔐 Secret Responsibilities | +|---------|---------------------------| +| 🧑‍💻 Developer | Use secrets correctly, never commit | +| 🔧 DevOps | Configure injection, manage policies | +| 🛡️ Security | Audit access, define requirements | +| 🏗️ Platform | Maintain Vault infrastructure | +| 📋 Compliance | Ensure rotation, audit trails | + +> 💡 **Common Thread:** Everyone shares responsibility for secrets + +--- + +## 📍 Slide 33 – 🏢 Real-World: How Companies Handle Secrets + +**🎬 Netflix:** +* 🔐 Custom secret management platform +* 🔄 Automatic rotation every 24 hours +* 📊 Real-time access monitoring + +**📦 Shopify:** +* 🏰 HashiCorp Vault at scale +* 🔑 Dynamic database credentials +* 👤 Per-service unique credentials + +**🚗 Uber:** +* 📚 Learned from 2016 breach +* 🔐 Zero hardcoded secrets policy +* 🤖 Automated secret scanning in CI + +--- + +## 📍 Slide 34 – 🎯 Decision Framework: Choosing a Solution + +```mermaid +flowchart TD + Start[🤔 Need Secret Management] --> Q1{Small team?
Simple app?} + Q1 -->|Yes| K8s[☸️ K8s Secrets + etcd encryption] + Q1 -->|No| Q2{Multi-platform?
Compliance needs?} + Q2 -->|Yes| Vault[🏰 HashiCorp Vault] + Q2 -->|No| Q3{Cloud-native only?} + Q3 -->|Yes| Cloud[☁️ Cloud Secret Manager] + Q3 -->|No| Vault +``` + +--- + +## 📍 Slide 35 – 📝 Key Takeaways + +1. 🚫 **Never hardcode secrets** — it's a breach waiting to happen +2. 🔄 **Base64 ≠ encryption** — K8s Secrets need etcd encryption +3. 🏰 **Vault for enterprise** — when you need rotation, audit, dynamic +4. 💉 **Sidecar injection** — cleanest pattern for K8s + Vault +5. 📋 **Audit everything** — you can't secure what you can't see + +> 💬 *"Security is not a product, but a process."* — Bruce Schneier + +--- + +## 📍 Slide 36 – 🔄 Mindset Shift + +| 😰 Old Mindset | 🚀 New Mindset | +|----------------|----------------| +| "Hardcode for convenience" | "Secrets are separate from code" | +| "Base64 is secure enough" | "Encryption at rest is mandatory" | +| "Rotate when breached" | "Rotate proactively and automatically" | +| "Trust developers" | "Least privilege for everyone" | +| "Hope nobody finds it" | "Assume breach, audit everything" | + +> 🤔 **Which mindset do you currently have?** + +--- + +## 📍 Slide 37 – ✅ Your Progress + +**🎓 You can now:** +- [x] 🧠 Explain why secret management matters +- [x] 🔍 Create K8s Secrets via kubectl and Helm +- [x] ⚠️ Recognize encoding vs encryption +- [x] 🛠️ Configure Vault sidecar injection +- [x] 🗺️ Choose appropriate secret management strategy + +**🚀 Ready for:** Lab 11 — Kubernetes Secrets & HashiCorp Vault + +--- + +## 📍 Slide 38 – 📝 QUIZ — DEVOPS_L11_POST + +--- + +## 📍 Slide 39 – 🚀 What's Next + +**📅 Next Lecture:** Configuration & Persistent Storage +* 📁 ConfigMaps for non-sensitive config +* 💾 Persistent Volumes for data +* 🔧 Mounting strategies + +```mermaid +flowchart LR + Now[🔐 Secrets] --> Next[📁 ConfigMaps] + Next --> Storage[💾 Storage] + Storage --> GitOps[🔄 GitOps] +``` + +> 💪 *"You've secured the secrets. Now let's configure everything else!"* + +--- + +## 📚 Resources + +**📖 Books:** +* "HashiCorp Vault: Securing Secrets" — by various authors +* "Kubernetes Security" — by Liz Rice +* "Zero Trust Networks" — by Evan Gilman + +**🔗 Links:** +* [Vault Documentation](https://developer.hashicorp.com/vault/docs) +* [K8s Secrets Best Practices](https://kubernetes.io/docs/concepts/security/secrets-good-practices/) +* [OWASP Secrets Management](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html) diff --git a/lectures/lec12.md b/lectures/lec12.md new file mode 100644 index 0000000000..ef8ff54778 --- /dev/null +++ b/lectures/lec12.md @@ -0,0 +1,854 @@ +# 📌 Lecture 12 — Configuration & Storage: Externalizing Application State + +> 🎯 **From hardcoded configs to dynamic, portable applications** + +--- + +## 📍 Slide 1 – 🚀 Welcome to Configuration Management + +Last lecture we secured our **secrets**. But what about everything else? + +* 🔧 **Database URLs** — different per environment +* 📊 **Feature flags** — enable/disable features dynamically +* 📁 **Data persistence** — where does your app store files? +* ⚙️ **App settings** — logging levels, timeouts, cache sizes + +```mermaid +flowchart LR + A[😰 Hardcoded Config] --> B[🔧 Externalized Config] + B --> C[🚀 Portable Apps] + C --> D[💎 Any Environment] +``` + +> 🎯 **Goal:** Build applications that run anywhere without code changes + +--- + +## 📍 Slide 2 – 📚 Learning Outcomes + +By the end of this lecture, you will: + +| # | 🎯 Outcome | +|---|-----------| +| 1 | ✅ Understand the **12-Factor App** configuration principle | +| 2 | ✅ Create and use **ConfigMaps** for non-sensitive configuration | +| 3 | ✅ Differentiate between **ConfigMaps** and **Secrets** | +| 4 | ✅ Understand **Persistent Volumes** and storage in Kubernetes | +| 5 | ✅ Implement **PersistentVolumeClaims** for stateful applications | +| 6 | ✅ Apply configuration management **best practices** | + +--- + +## 📍 Slide 3 – 🗺️ Lecture Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SECTION 0: Introduction (Slides 1-4) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 PRE QUIZ (Slide 5) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 1: The Configuration Problem (Slides 6-10) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 2: ConfigMaps Deep Dive (Slides 11-15) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 3: Hands-on Scenarios (Slides 16-24) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 MID QUIZ (Slide 25) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 4: Persistent Storage (Slides 26-32) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 5: Production Patterns (Slides 33-37) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 POST QUIZ (Slide 38) │ +├─────────────────────────────────────────────────────────────┤ +│ FINAL: What's Next (Slide 39) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 📍 Slide 4 – 🤔 The Big Question + +> 💬 *"Store config in the environment, not in the code."* +> — The Twelve-Factor App + +**Consider this:** + +* 🏭 You have the **same application** running in dev, staging, and production +* 🔧 Each environment needs **different database URLs** +* 📊 You want to change **log levels without redeploying** +* 💾 Your app needs to **persist user uploads** somewhere + +> 🤔 **Think:** How do you build ONE container image that works everywhere? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L12_PRE + +--- + +## 📍 Slide 6 – ⚠️ Section 1: The Configuration Problem + +**The Anti-Pattern: Hardcoded Configuration** + +```mermaid +flowchart TD + subgraph 😰 Hardcoded + A[app-dev.jar] --> D1[Dev DB] + B[app-staging.jar] --> D2[Staging DB] + C[app-prod.jar] --> D3[Prod DB] + end + + subgraph 🚀 Externalized + E[app.jar] --> F{Config} + F --> D1 + F --> D2 + F --> D3 + end +``` + +* 😰 **Hardcoded:** Different artifact per environment +* 🚀 **Externalized:** One artifact, configuration injected at runtime + +--- + +## 📍 Slide 7 – 🔥 Pain Point 1: Environment-Specific Builds + +**The Problem:** + +```dockerfile +# ❌ Bad: Environment-specific Dockerfile +FROM python:3.12 +ENV DATABASE_URL=postgres://dev-server:5432/mydb # 😱 Hardcoded! +ENV LOG_LEVEL=DEBUG +COPY . /app +``` + +* 🔄 Need to **rebuild** for each environment +* 🐛 **"Works on my machine"** — config differs +* 🔍 Can't trace which **version** is where +* 💀 Accidentally deploying **dev config to production** + +> 😱 **Horror Story:** Company deployed with `DEBUG=true` to production, logging credit card numbers + +--- + +## 📍 Slide 8 – 🔥 Pain Point 2: Configuration Drift + +**What happens over time:** + +| 📅 Month | 🔧 Dev Config | 🎭 Staging Config | 🏭 Prod Config | +|----------|---------------|-------------------|----------------| +| January | `timeout=30` | `timeout=30` | `timeout=30` | +| March | `timeout=60` | `timeout=30` | `timeout=30` | +| June | `timeout=60` | `timeout=45` | `timeout=30` | +| Now | 😵 Nobody knows what's deployed where | + +* 🔄 **Manual changes** accumulate +* 📋 No **version control** for configuration +* 🐛 **Staging doesn't match production** — bugs slip through + +--- + +## 📍 Slide 9 – 🔥 Pain Point 3: Data Loss + +**Stateless containers + persistent data = 💥** + +```mermaid +flowchart LR + A[📦 Container v1] --> B[💾 /app/uploads] + B --> C[🔄 Deployment] + C --> D[📦 Container v2] + D --> E[💾 /app/uploads] + E --> F[😱 Empty!] +``` + +* 📦 Containers are **ephemeral** — data inside is lost on restart +* 💾 User uploads, databases, caches — all **gone** +* 🔄 Rolling updates = **data loss** without proper storage + +> 🤔 **Discussion:** Where should container applications store their data? + +--- + +## 📍 Slide 10 – 💰 The Cost of Poor Configuration + +| 🔥 Problem | 💥 Impact | 📊 Statistics | +|-----------|----------|---------------| +| Config drift | Inconsistent behavior | 62% of outages involve config changes | +| Hardcoded secrets | Security breaches | Covered in Lecture 11! | +| Data loss | Customer impact | Average $150K per incident | +| Manual config | Human error | 70% of failures are human error | + +**Root causes of production incidents (2024 survey):** +* 🔧 Configuration changes: **41%** +* 📦 Code deployments: **31%** +* 🔌 Infrastructure failures: **28%** + +--- + +## 📍 Slide 11 – ✅ Section 2: ConfigMaps to the Rescue + +**What is a ConfigMap?** + +* 📋 Kubernetes object that stores **non-confidential** configuration data +* 🔑 Key-value pairs or **entire files** +* 🔄 Decouples configuration from container images +* ⚡ Can be updated **without rebuilding** the application + +```mermaid +flowchart LR + A[📋 ConfigMap] --> B[📦 Pod] + A --> C[📦 Pod] + A --> D[📦 Pod] + + E[🔐 Secret] --> B + E --> C + E --> D +``` + +> 💡 **Key Insight:** ConfigMaps for config, Secrets for sensitive data + +--- + +## 📍 Slide 12 – 🚫 ConfigMaps: What They're NOT + +| 🚫 Myth | ✅ Reality | +|---------|----------| +| ConfigMaps are secure | ❌ Stored in plain text in etcd | +| ConfigMaps replace Secrets | ❌ Use Secrets for sensitive data | +| ConfigMaps auto-reload apps | ❌ Apps must implement hot-reload | +| ConfigMaps have no size limit | ❌ Limited to 1MB per ConfigMap | + +> ⚠️ **Warning:** Never store passwords, tokens, or keys in ConfigMaps! + +**When to use which:** + +| 📋 ConfigMap | 🔐 Secret | +|-------------|----------| +| Database URLs (without password) | Database passwords | +| Feature flags | API keys | +| Log levels | TLS certificates | +| Application settings | OAuth tokens | + +--- + +## 📍 Slide 13 – 🛠️ Creating ConfigMaps + +**Method 1: From literal values** +```bash +kubectl create configmap app-config \ + --from-literal=LOG_LEVEL=INFO \ + --from-literal=CACHE_TTL=3600 +``` + +**Method 2: From a file** +```bash +kubectl create configmap nginx-config \ + --from-file=nginx.conf +``` + +**Method 3: From YAML manifest** +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: app-config +data: + LOG_LEVEL: "INFO" + DATABASE_HOST: "postgres.default.svc" + config.yaml: | + server: + port: 8080 + timeout: 30s +``` + +--- + +## 📍 Slide 14 – 🔌 Consuming ConfigMaps + +**Option 1: Environment Variables** +```yaml +# ✅ Individual keys +env: + - name: LOG_LEVEL + valueFrom: + configMapKeyRef: + name: app-config + key: LOG_LEVEL + +# ✅ All keys at once +envFrom: + - configMapRef: + name: app-config +``` + +**Option 2: Volume Mounts (for files)** +```yaml +volumes: + - name: config-volume + configMap: + name: nginx-config +volumeMounts: + - name: config-volume + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf +``` + +--- + +## 📍 Slide 15 – 📊 Before vs After: Configuration + +| 📋 Aspect | 😰 Before (Hardcoded) | 🚀 After (ConfigMaps) | +|----------|----------------------|----------------------| +| Build per environment | Yes, multiple images | No, one image | +| Change config | Rebuild & redeploy | Update ConfigMap | +| Version control | In code (scattered) | Centralized, declarative | +| Environment parity | Difficult | Easy | +| Rollback | Redeploy old image | Apply old ConfigMap | +| Audit trail | Git history (code) | K8s + Git history | + +> 🤔 **Think:** What configuration in your applications could be externalized? + +--- + +## 📍 Slide 16 – 🎮 Section 3: Let's Simulate! + +**Scenario:** You're a DevOps engineer at **CloudMart** 🛒 + +Your application: +* 🐍 Python/Go web service +* 📊 Needs different configs per environment +* 💾 Stores user uploads +* 🔧 Frequently changes feature flags + +**What could go wrong?** Everything! Let's fix it. + +--- + +## 📍 Slide 17 – 💥 Scenario 1: Wrong Environment Config + +**Situation:** Developer accidentally deploys with staging database URL to production + +```mermaid +flowchart LR + A[👨‍💻 Dev pushes] --> B[🔄 CI/CD] + B --> C[📦 Deploy to Prod] + C --> D[🔗 Connects to Staging DB] + D --> E[😱 Production reads staging data!] +``` + +* 😱 **Impact:** Customers see test data +* ⏱️ **Detection time:** 2 hours +* 💰 **Cost:** Lost sales, reputation damage + +> 🤔 **Question:** How do we prevent this? + +--- + +## 📍 Slide 18 – ✅ Solution 1: Environment-Specific ConfigMaps + +**Fix:** Namespace-isolated ConfigMaps + +```yaml +# configmap-prod.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: app-config + namespace: production # 🔑 Namespace isolation +data: + DATABASE_HOST: "prod-db.internal" + ENVIRONMENT: "production" +--- +# configmap-staging.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: app-config + namespace: staging +data: + DATABASE_HOST: "staging-db.internal" + ENVIRONMENT: "staging" +``` + +* ✅ **Same ConfigMap name**, different namespaces +* ✅ **Impossible** to mix environments +* ✅ **GitOps friendly** — config in version control + +--- + +## 📍 Slide 19 – 💥 Scenario 2: Config Change Causes Outage + +**Situation:** Changed `CACHE_TTL` from 3600 to 36 (typo!) — cache expires every 36 seconds + +```mermaid +flowchart TD + A[⌨️ Typo: 3600 → 36] --> B[📋 ConfigMap Updated] + B --> C[📦 Pods reload config] + C --> D[🔥 Cache thrashing] + D --> E[💀 Database overloaded] + E --> F[😱 Site down!] +``` + +* 😱 **Impact:** 30-minute outage +* 🔍 **Root cause:** No validation, no review + +--- + +## 📍 Slide 20 – ✅ Solution 2: Immutable ConfigMaps + Versioning + +**Fix:** Treat ConfigMaps as immutable, version them + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: app-config-v3 # 🔑 Versioned name + labels: + version: "3" +immutable: true # 🔒 Cannot be modified +data: + CACHE_TTL: "3600" +``` + +**Deployment references specific version:** +```yaml +envFrom: + - configMapRef: + name: app-config-v3 # 🔑 Explicit version +``` + +* ✅ **Rollback** = change reference to previous version +* ✅ **Audit trail** — which version when +* ✅ **Validation** in CI/CD before applying + +--- + +## 📍 Slide 21 – 💥 Scenario 3: User Uploads Disappear + +**Situation:** Deployment rolls out new pods, user uploads are gone + +```mermaid +flowchart TD + A[📦 Pod v1] --> B[💾 /app/uploads] + B --> C[📸 User uploads photo] + C --> D[🔄 Rolling Update] + D --> E[📦 Pod v2] + E --> F[💾 /app/uploads - Empty!] + F --> G[😱 User: Where's my photo?!] +``` + +* 💾 Container filesystem is **ephemeral** +* 🔄 New container = **fresh filesystem** +* 😱 All data is **lost** + +--- + +## 📍 Slide 22 – ✅ Solution 3: Persistent Volumes + +**Fix:** External storage that survives pod restarts + +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: uploads-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +# In Deployment +volumes: + - name: uploads + persistentVolumeClaim: + claimName: uploads-pvc +volumeMounts: + - name: uploads + mountPath: /app/uploads +``` + +```mermaid +flowchart LR + A[📦 Pod v1] --> B[💾 PVC] + C[📦 Pod v2] --> B + D[📦 Pod v3] --> B + B --> E[🗄️ Persistent Storage] +``` + +--- + +## 📍 Slide 23 – 💥 Scenario 4: ConfigMap Update Not Applied + +**Situation:** Updated ConfigMap, but app still uses old values + +```mermaid +flowchart LR + A[📋 ConfigMap Updated] --> B[📦 Pod still running] + B --> C[⚠️ Using old config!] + C --> D[🤔 Why isn't it working?] +``` + +* 🔄 ConfigMap updates **don't automatically restart** pods +* 📦 Pod keeps the config from when it started + +--- + +## 📍 Slide 24 – ✅ Solution 4: Config Reload Strategies + +**Strategy 1: Restart Deployment** +```bash +kubectl rollout restart deployment/myapp +``` + +**Strategy 2: Use a hash annotation (GitOps-friendly)** +```yaml +metadata: + annotations: + checksum/config: {{ sha256sum .Values.config | quote }} +``` + +**Strategy 3: App-level hot reload** +* 📂 Mount ConfigMap as volume +* 👀 Watch for file changes +* 🔄 Reload configuration in-memory + +**Strategy 4: Reloader controller** +* 🤖 Automatically restarts pods when ConfigMap changes +* 📦 `stakater/reloader` — popular open source solution + +--- + +## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L12_MID + +--- + +## 📍 Slide 26 – 💾 Section 4: Persistent Storage Deep Dive + +**The Storage Stack in Kubernetes:** + +```mermaid +flowchart TD + A[📦 Pod] --> B[📁 Volume Mount] + B --> C[💾 PersistentVolumeClaim] + C --> D[🗄️ PersistentVolume] + D --> E[☁️ Storage Backend] + + F[📋 StorageClass] -.-> C + F -.-> D +``` + +* 📦 **Pod:** Uses the storage via mount +* 💾 **PVC:** Request for storage ("I need 10GB") +* 🗄️ **PV:** Actual storage resource +* ☁️ **Backend:** AWS EBS, GCE PD, NFS, local disk +* 📋 **StorageClass:** Template for dynamic provisioning + +--- + +## 📍 Slide 27 – 📋 Storage Concepts Breakdown + +| 🔧 Concept | 📝 Description | 🎯 Analogy | +|-----------|---------------|-----------| +| **PersistentVolume (PV)** | A piece of storage in the cluster | A physical hard drive | +| **PersistentVolumeClaim (PVC)** | A request for storage | "I need a 100GB drive" | +| **StorageClass** | Template for provisioning | "Give me SSD storage" | +| **Access Modes** | How pods can access | ReadWriteOnce, ReadWriteMany | +| **Reclaim Policy** | What happens when PVC deleted | Retain, Delete, Recycle | + +**Access Modes:** +* 🔒 **ReadWriteOnce (RWO):** One node can mount read-write +* 📖 **ReadOnlyMany (ROX):** Many nodes can mount read-only +* 📝 **ReadWriteMany (RWX):** Many nodes can mount read-write + +--- + +## 📍 Slide 28 – 🔄 Dynamic Provisioning + +**Without Dynamic Provisioning (Manual):** +```mermaid +flowchart LR + A[👨‍💻 Admin creates PV] --> B[📋 PV available] + B --> C[👨‍💻 Dev creates PVC] + C --> D[🔗 PVC binds to PV] +``` + +**With Dynamic Provisioning (Automatic):** +```mermaid +flowchart LR + A[👨‍💻 Dev creates PVC] --> B[📋 StorageClass] + B --> C[🤖 Auto-create PV] + C --> D[🔗 PVC binds to PV] +``` + +```yaml +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: fast-ssd +provisioner: kubernetes.io/gce-pd # Cloud-specific +parameters: + type: pd-ssd +reclaimPolicy: Delete +volumeBindingMode: WaitForFirstConsumer +``` + +--- + +## 📍 Slide 29 – ⚠️ Storage Pitfalls + +| ⚠️ Pitfall | 💥 Impact | ✅ Solution | +|-----------|----------|------------| +| Wrong access mode | Pod scheduling fails | Match mode to use case | +| No storage class | PVC pending forever | Set default StorageClass | +| Reclaim = Delete | Data lost on PVC delete | Use Retain for important data | +| Zone mismatch | Pod can't mount volume | Use topology-aware provisioning | +| Insufficient capacity | PVC pending | Monitor storage usage | + +**Common error:** +``` +Warning FailedScheduling pod has unbound immediate PersistentVolumeClaims +``` + +> 🔍 **Debug:** `kubectl describe pvc ` — check events + +--- + +## 📍 Slide 30 – 📊 Volume Types Comparison + +| 📦 Volume Type | 🎯 Use Case | ⚡ Performance | 💰 Cost | +|---------------|------------|---------------|--------| +| **emptyDir** | Temp data, cache | Fast (node storage) | Free | +| **hostPath** | Node-specific data | Fast | Free | +| **NFS** | Shared storage | Medium | Varies | +| **Cloud (EBS, PD)** | Production workloads | Configurable | $$$ | +| **Local PV** | Databases, high IOPS | Very fast | Node-dependent | + +**Decision tree:** +```mermaid +flowchart TD + A[Need persistent storage?] --> |No| B[emptyDir] + A --> |Yes| C[Shared across pods?] + C --> |No| D[Cloud Block Storage] + C --> |Yes| E[NFS or Cloud File Storage] +``` + +--- + +## 📍 Slide 31 – 🔧 Practical PVC Example + +**Complete example for a web application:** + +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: app-uploads +spec: + accessModes: + - ReadWriteOnce + storageClassName: standard + resources: + requests: + storage: 5Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webapp +spec: + template: + spec: + containers: + - name: app + volumeMounts: + - name: uploads + mountPath: /app/uploads + volumes: + - name: uploads + persistentVolumeClaim: + claimName: app-uploads +``` + +--- + +## 📍 Slide 32 – 📈 Storage Lifecycle + +```mermaid +stateDiagram-v2 + [*] --> Pending: PVC Created + Pending --> Bound: PV Available + Bound --> Released: PVC Deleted + Released --> Available: Reclaim + Released --> [*]: Delete Policy + + note right of Pending: Waiting for PV + note right of Bound: In use + note right of Released: Data still exists +``` + +**Key states:** +* ⏳ **Pending:** Waiting for matching PV +* ✅ **Bound:** PVC matched to PV +* 🔓 **Released:** PVC deleted, PV still has data +* ❌ **Failed:** Error in provisioning + +--- + +## 📍 Slide 33 – 🏭 Section 5: Production Patterns + +**Pattern 1: GitOps Configuration Management** + +```mermaid +flowchart LR + A[📝 Git Repo] --> B[🔄 ArgoCD] + B --> C[📋 ConfigMaps] + B --> D[🔐 Secrets] + B --> E[📦 Deployments] + + C --> F[🎯 Cluster] + D --> F + E --> F +``` + +* 📋 **All configuration in Git** — single source of truth +* 🔄 **ArgoCD syncs** to cluster +* 🔍 **Audit trail** — who changed what, when +* ↩️ **Rollback** — `git revert` + +--- + +## 📍 Slide 34 – 🔧 Pattern 2: Environment Hierarchy + +**Kustomize for environment-specific configs:** + +``` +base/ + ├── deployment.yaml + ├── service.yaml + └── configmap.yaml +overlays/ + ├── dev/ + │ └── kustomization.yaml + ├── staging/ + │ └── kustomization.yaml + └── prod/ + └── kustomization.yaml +``` + +```yaml +# overlays/prod/kustomization.yaml +resources: + - ../../base +configMapGenerator: + - name: app-config + literals: + - LOG_LEVEL=WARN + - REPLICAS=5 +``` + +* ✅ **DRY** — Don't Repeat Yourself +* ✅ **Environment-specific** overrides +* ✅ **Consistent** base configuration + +--- + +## 📍 Slide 35 – 🔐 Pattern 3: Secrets + ConfigMaps Together + +**Combining Secrets and ConfigMaps:** + +```yaml +spec: + containers: + - name: app + env: + # 📋 From ConfigMap (non-sensitive) + - name: DATABASE_HOST + valueFrom: + configMapKeyRef: + name: app-config + key: DATABASE_HOST + # 🔐 From Secret (sensitive) + - name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: app-secrets + key: db-password +``` + +**Best Practice:** +* 📋 **ConfigMap:** URLs, ports, feature flags +* 🔐 **Secret:** Passwords, tokens, certificates +* 🔒 **Never mix** sensitive and non-sensitive data + +--- + +## 📍 Slide 36 – 📊 Configuration Best Practices + +| 🔧 Practice | 📝 Description | +|------------|---------------| +| **Version ConfigMaps** | Include version in name (`app-config-v2`) | +| **Use namespaces** | Isolate environments (dev, staging, prod) | +| **Validate in CI** | Check config syntax before deploy | +| **Document defaults** | What happens if config missing? | +| **Monitor changes** | Alert on ConfigMap updates | +| **Limit size** | Keep ConfigMaps under 1MB | +| **Use labels** | Tag configs with app, version, environment | + +--- + +## 📍 Slide 37 – 🎯 Key Takeaways + +1. 📋 **ConfigMaps** separate configuration from code — one image, any environment +2. 🔐 **ConfigMaps ≠ Secrets** — never store sensitive data in ConfigMaps +3. 💾 **PVCs** provide persistent storage that survives pod restarts +4. 🔄 **Dynamic provisioning** automates storage management +5. 📁 **Version your configs** — treat them like code +6. 🏭 **GitOps** — configuration in Git is the source of truth + +> 💬 *"Configuration belongs in the environment, not the artifact."* +> — 12-Factor App + +--- + +## 📍 Slide 38 – 📝 QUIZ — DEVOPS_L12_POST + +--- + +## 📍 Slide 39 – 🚀 What's Next? + +**Coming up: Lecture 13 — GitOps with ArgoCD** + +```mermaid +flowchart LR + A[📝 Git] --> B[🔄 ArgoCD] + B --> C[☸️ Kubernetes] + C --> D[🎯 Desired State] +``` + +* 🔄 **Continuous Deployment** automated +* 📝 **Git as single source of truth** +* 🔍 **Drift detection** and auto-sync +* ↩️ **Easy rollbacks** with git revert + +> 🎯 **Lab 12:** Apply these concepts — create ConfigMaps, use PVCs, externalize your app configuration! + +--- + +## 📚 Resources + +**Documentation:** +* 📖 [Kubernetes ConfigMaps](https://kubernetes.io/docs/concepts/configuration/configmap/) +* 📖 [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) +* 📖 [12-Factor App — Config](https://12factor.net/config) + +**Tools:** +* 🔧 [Kustomize](https://kustomize.io/) +* 🔧 [Reloader](https://github.com/stakater/Reloader) + +**Books:** +* 📕 *Kubernetes Patterns* by Bilgin Ibryam & Roland Huß +* 📕 *Cloud Native DevOps with Kubernetes* by John Arundel & Justin Domingus diff --git a/lectures/lec13.md b/lectures/lec13.md new file mode 100644 index 0000000000..370ae7821a --- /dev/null +++ b/lectures/lec13.md @@ -0,0 +1,830 @@ +# 📌 Lecture 13 — GitOps with ArgoCD: Git as the Source of Truth + +> 🎯 **From manual deployments to automated, auditable, self-healing infrastructure** + +--- + +## 📍 Slide 1 – 🚀 Welcome to GitOps + +We've learned to store configuration in **ConfigMaps** and **Secrets**. But who deploys them? + +* 👨‍💻 **Manual kubectl?** — "Who ran that command?" +* 🔄 **CI/CD pipeline?** — Push-based, fragile +* 🤔 **What about drift?** — Reality vs desired state + +```mermaid +flowchart LR + A[😰 Manual Deploys] --> B[🔄 CI/CD Push] + B --> C[🚀 GitOps Pull] + C --> D[💎 Self-healing Infrastructure] +``` + +> 🎯 **Goal:** Git becomes the single source of truth for your entire infrastructure + +--- + +## 📍 Slide 2 – 📚 Learning Outcomes + +By the end of this lecture, you will: + +| # | 🎯 Outcome | +|---|-----------| +| 1 | ✅ Understand **GitOps principles** and benefits | +| 2 | ✅ Differentiate **push vs pull** deployment models | +| 3 | ✅ Deploy applications using **ArgoCD** | +| 4 | ✅ Configure **sync policies** and **auto-healing** | +| 5 | ✅ Handle **secrets** in GitOps workflows | +| 6 | ✅ Implement **multi-environment** deployments | + +--- + +## 📍 Slide 3 – 🗺️ Lecture Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SECTION 0: Introduction (Slides 1-4) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 PRE QUIZ (Slide 5) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 1: The Deployment Problem (Slides 6-10) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 2: GitOps Principles (Slides 11-15) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 3: ArgoCD in Action (Slides 16-24) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 MID QUIZ (Slide 25) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 4: Advanced Patterns (Slides 26-32) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 5: Production GitOps (Slides 33-37) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 POST QUIZ (Slide 38) │ +├─────────────────────────────────────────────────────────────┤ +│ FINAL: What's Next (Slide 39) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 📍 Slide 4 – 🤔 The Big Question + +> 💬 *"If it's not in Git, it doesn't exist."* +> — GitOps Mantra + +**Consider this scenario:** + +* 🌙 **3 AM alert:** Production is down +* 🔍 **Investigation:** Someone changed a deployment +* ❓ **Questions:** Who? When? What changed? How to rollback? +* 😱 **Answer:** Nobody knows... + +> 🤔 **Think:** How do we ensure every change is tracked, auditable, and reversible? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L13_PRE + +--- + +## 📍 Slide 6 – ⚠️ Section 1: The Deployment Problem + +**Traditional Deployment Models:** + +```mermaid +flowchart TD + subgraph 😰 Manual + A[👨‍💻 Developer] --> B[⌨️ kubectl apply] + B --> C[☸️ Cluster] + end + + subgraph 🔄 CI/CD Push + D[📝 Git Push] --> E[🔧 CI Pipeline] + E --> F[⌨️ kubectl apply] + F --> G[☸️ Cluster] + end +``` + +* 😰 **Manual:** No audit trail, human error, inconsistent +* 🔄 **CI/CD Push:** Better, but credentials in pipeline, cluster access + +--- + +## 📍 Slide 7 – 🔥 Pain Point 1: The "It Works on My Machine" Problem + +**Symptoms:** + +* 👨‍💻 **Dev:** "I deployed it, it's working!" +* 🏭 **Prod:** "It's completely broken!" +* 🔍 **Investigation:** Configs don't match + +```mermaid +flowchart LR + A[👨‍💻 Local kubectl] --> B[🎭 Staging] + C[👨‍💻 Different kubectl] --> D[🏭 Production] + B --> E[😵 Different States] + D --> E +``` + +* 🔧 **No single source of truth** +* 📋 **Manual processes** lead to drift +* 😱 **"Emergency fixes"** bypass procedures + +--- + +## 📍 Slide 8 – 🔥 Pain Point 2: Configuration Drift + +**Drift:** When actual state ≠ desired state + +| 📅 Time | 📝 Git (Desired) | ☸️ Cluster (Actual) | 😱 Drift | +|---------|------------------|---------------------|----------| +| Day 1 | replicas: 3 | replicas: 3 | ✅ None | +| Day 5 | replicas: 3 | replicas: 5 (scaled manually) | ⚠️ Drift! | +| Day 10 | replicas: 3 | replicas: 5, extra env var | 🔥 More drift! | +| Day 30 | 🤷 Unknown | 🤷 Unknown | 💀 Chaos | + +**Real impact:** +* 🔄 **Deployments fail** because actual state differs +* 📋 **Documentation lies** — cluster is reality +* 🔍 **Debugging nightmare** — which version is deployed? + +--- + +## 📍 Slide 9 – 🔥 Pain Point 3: Credential Sprawl + +**Push-based CI/CD security concerns:** + +```mermaid +flowchart TD + A[🔐 Cluster Credentials] --> B[📦 CI Server] + A --> C[💻 Dev Machines] + A --> D[🔧 Scripts] + A --> E[📋 Pipeline Configs] + + B --> F[😱 Breach Vector] + C --> F + D --> F + E --> F +``` + +* 🔐 **Credentials everywhere** — CI servers, dev machines +* 🎯 **Attack surface** expands with each tool +* 🔑 **Shared secrets** — who has access? + +--- + +## 📍 Slide 10 – 💰 The Cost of Manual Deployments + +| 🔥 Problem | 💥 Impact | 📊 Data | +|-----------|----------|---------| +| No audit trail | Compliance failures | 73% fail audits without GitOps | +| Manual errors | Outages | 70% of outages are human error | +| Credential sprawl | Security breaches | Average breach cost: $4.45M | +| Slow recovery | Downtime | MTTR 4x longer without GitOps | + +> 💬 *"The cost of a breach is not the breach itself, but the inability to respond quickly."* + +--- + +## 📍 Slide 11 – ✅ Section 2: GitOps Principles + +**What is GitOps?** + +* 📝 **Git as single source of truth** — declarative desired state +* 🔄 **Continuous reconciliation** — actual → desired +* 🔀 **Pull-based deployment** — agent pulls from Git +* 🔒 **Immutable, auditable** — every change tracked + +```mermaid +flowchart LR + A[📝 Git Repo] --> |Pull| B[🤖 ArgoCD Agent] + B --> |Reconcile| C[☸️ Cluster] + C --> |Report Status| B +``` + +> 💡 **Key Insight:** The cluster pulls changes, no credentials leave the cluster! + +--- + +## 📍 Slide 12 – 🚫 GitOps: What It's NOT + +| 🚫 Myth | ✅ Reality | +|---------|----------| +| Just using Git for YAML files | A complete operational model with reconciliation | +| Another CI/CD tool | Continuous deployment, not continuous integration | +| Only for Kubernetes | Works for any declarative infrastructure | +| Complicated to adopt | Can start simple, grow incrementally | + +> 🔥 **Hot take:** "Putting YAML in Git is not GitOps. GitOps is about the reconciliation loop." + +**The Four Principles (from OpenGitOps):** +1. 📝 **Declarative** — Desired state expressed declaratively +2. 🔄 **Versioned and Immutable** — Stored in Git +3. 🤖 **Pulled Automatically** — Agents pull desired state +4. ♾️ **Continuously Reconciled** — Agents ensure actual = desired + +--- + +## 📍 Slide 13 – 🔄 Push vs Pull Deployment + +```mermaid +flowchart TD + subgraph 🔄 Push Model + A[📝 Git] --> B[🔧 CI/CD] + B --> |Push credentials needed| C[☸️ Cluster] + end + + subgraph 🚀 Pull Model - GitOps + D[📝 Git] --> |Pull| E[🤖 Agent in Cluster] + E --> |Apply| F[☸️ Same Cluster] + end +``` + +| 📋 Aspect | 🔄 Push | 🚀 Pull (GitOps) | +|----------|--------|------------------| +| Credentials | CI needs cluster creds | Agent has local access | +| Drift detection | None | Continuous | +| Audit trail | CI logs (external) | Git history | +| Recovery | Re-run pipeline | Automatic reconciliation | + +--- + +## 📍 Slide 14 – 🛠️ GitOps Tools Landscape + +| 🛠️ Tool | 📝 Description | ⭐ Best For | +|---------|---------------|------------| +| **ArgoCD** | Declarative GitOps for K8s | Most Kubernetes use cases | +| **Flux** | Toolkit approach, CNCF project | Composable, extensible setups | +| **Jenkins X** | CI/CD + GitOps combined | Jenkins-heavy organizations | +| **Rancher Fleet** | Multi-cluster GitOps | Managing many clusters | + +**Why ArgoCD?** +* 🎯 Most adopted (70%+ of GitOps users) +* 🖥️ Excellent UI for visualization +* 🔧 Rich feature set out of the box +* 📚 Large community, good documentation + +--- + +## 📍 Slide 15 – 📊 Before vs After: Deployment + +| 📋 Aspect | 😰 Before (Manual/Push) | 🚀 After (GitOps) | +|----------|-------------------------|-------------------| +| Change process | kubectl, scripts, pipelines | Git PR → merge → auto-sync | +| Audit trail | Scattered logs | Complete Git history | +| Rollback | "Which version was before?" | `git revert` | +| Drift | Undetected until failure | Detected immediately | +| Credentials | Spread across tools | Stay in cluster | +| Recovery | Manual intervention | Self-healing | + +> 🤔 **Think:** How would GitOps have helped in your last deployment issue? + +--- + +## 📍 Slide 16 – 🎮 Section 3: ArgoCD in Action + +**ArgoCD Architecture:** + +```mermaid +flowchart TD + A[📝 Git Repository] --> B[🤖 ArgoCD Server] + B --> C[🔄 Application Controller] + C --> D[☸️ Kubernetes API] + E[👨‍💻 User] --> F[🖥️ ArgoCD UI / CLI] + F --> B + D --> C +``` + +**Components:** +* 🖥️ **API Server:** UI, CLI, webhook endpoints +* 🔄 **Application Controller:** Reconciliation engine +* 📦 **Repository Server:** Caches Git repos, renders manifests +* 🔗 **Dex:** SSO authentication (optional) + +--- + +## 📍 Slide 17 – 💥 Scenario 1: First ArgoCD Deployment + +**Situation:** Deploy your first application with ArgoCD + +```yaml +# Application manifest +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: my-app + namespace: argocd +spec: + project: default + source: + repoURL: https://github.com/org/app-manifests + path: environments/dev + targetRevision: main + destination: + server: https://kubernetes.default.svc + namespace: dev +``` + +```mermaid +flowchart LR + A[📝 Create App] --> B[🔄 ArgoCD Syncs] + B --> C[📦 Resources Created] + C --> D[✅ App Running] +``` + +--- + +## 📍 Slide 18 – ✅ Solution 1: Understanding Sync + +**Sync States:** + +| 🔄 State | 📝 Meaning | 🎯 Action | +|----------|-----------|----------| +| **Synced** | Cluster matches Git | ✅ Good! | +| **OutOfSync** | Cluster differs from Git | 🔄 Sync needed | +| **Unknown** | Can't determine state | 🔍 Check connection | +| **Missing** | Resources don't exist yet | 🔄 Initial sync | + +**Health States:** + +| 💚 Health | 📝 Meaning | +|----------|-----------| +| **Healthy** | All resources running correctly | +| **Progressing** | Resources being updated | +| **Degraded** | Some resources have issues | +| **Suspended** | Manually paused | + +--- + +## 📍 Slide 19 – 💥 Scenario 2: Handling Drift + +**Situation:** Someone manually changed replicas in the cluster + +```mermaid +flowchart TD + A[📝 Git: replicas=3] --> B[🤖 ArgoCD] + C[👨‍💻 kubectl scale replicas=5] --> D[☸️ Cluster] + B --> |Detects| E[⚠️ OutOfSync] + E --> |Auto-sync enabled| F[🔄 Restore to 3] +``` + +**ArgoCD detects drift immediately!** +* 🔍 **Visibility:** Shows exactly what differs +* 🔄 **Options:** Manual sync or auto-sync +* 📋 **Audit:** Who changed Git matters, not who ran kubectl + +--- + +## 📍 Slide 20 – ✅ Solution 2: Sync Policies + +**Configure automatic reconciliation:** + +```yaml +spec: + syncPolicy: + automated: + prune: true # Delete resources not in Git + selfHeal: true # Revert manual changes + syncOptions: + - CreateNamespace=true + - PruneLast=true +``` + +**Options explained:** +* 🔄 **automated:** Enable auto-sync on Git changes +* 🗑️ **prune:** Delete resources removed from Git +* 💚 **selfHeal:** Revert manual cluster changes +* 📦 **CreateNamespace:** Create namespace if missing + +--- + +## 📍 Slide 21 – 💥 Scenario 3: Multi-Environment Deployment + +**Situation:** Same app, different configs for dev/staging/prod + +``` +repo/ +├── base/ +│ ├── deployment.yaml +│ └── service.yaml +└── overlays/ + ├── dev/ + │ └── kustomization.yaml + ├── staging/ + │ └── kustomization.yaml + └── prod/ + └── kustomization.yaml +``` + +```mermaid +flowchart TD + A[📝 Git Repo] --> B[🤖 ArgoCD] + B --> C[📦 App-Dev] + B --> D[📦 App-Staging] + B --> E[📦 App-Prod] + C --> F[☸️ Dev Cluster] + D --> G[☸️ Staging Cluster] + E --> H[☸️ Prod Cluster] +``` + +--- + +## 📍 Slide 22 – ✅ Solution 3: ApplicationSet + +**Deploy to multiple environments with one definition:** + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: my-app +spec: + generators: + - list: + elements: + - env: dev + namespace: dev + - env: staging + namespace: staging + - env: prod + namespace: prod + template: + metadata: + name: 'my-app-{{env}}' + spec: + source: + repoURL: https://github.com/org/manifests + path: 'overlays/{{env}}' + destination: + namespace: '{{namespace}}' +``` + +--- + +## 📍 Slide 23 – 💥 Scenario 4: Secrets in GitOps + +**Problem:** Secrets shouldn't be in Git... but GitOps needs everything in Git! + +```mermaid +flowchart TD + A[🔐 Secret] --> B{Where to store?} + B --> |❌ Plain Git| C[😱 Security breach] + B --> |✅ Encrypted| D[🔒 Sealed Secrets] + B --> |✅ External| E[🔐 Vault + ESO] +``` + +**The dilemma:** +* 📝 GitOps: Everything in Git +* 🔐 Security: Secrets NOT in Git +* 🤔 How to reconcile? + +--- + +## 📍 Slide 24 – ✅ Solution 4: Secrets Management Patterns + +**Option 1: Sealed Secrets** +```yaml +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + name: my-secret +spec: + encryptedData: + password: AgBghY8... # Encrypted, safe to commit! +``` + +**Option 2: External Secrets Operator + Vault** +```yaml +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: my-secret +spec: + secretStoreRef: + name: vault-backend + target: + name: my-secret + data: + - secretKey: password + remoteRef: + key: app/database + property: password +``` + +* ✅ **Encrypted in Git** (Sealed Secrets) +* ✅ **Reference only in Git** (External Secrets) + +--- + +## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L13_MID + +--- + +## 📍 Slide 26 – 🔧 Section 4: Advanced ArgoCD Patterns + +**Sync Waves & Hooks:** + +```yaml +metadata: + annotations: + argocd.argoproj.io/sync-wave: "1" # Order of deployment + argocd.argoproj.io/hook: PreSync # Run before main sync +``` + +```mermaid +flowchart LR + A[🔄 PreSync Hooks] --> B[📦 Wave 0] + B --> C[📦 Wave 1] + C --> D[📦 Wave 2] + D --> E[✅ PostSync Hooks] +``` + +**Use cases:** +* 📊 **Database migrations** before app deploy +* 🧹 **Cleanup jobs** after deployment +* 🔍 **Health checks** between phases + +--- + +## 📍 Slide 27 – 🔄 Sync Options Deep Dive + +| 🔧 Option | 📝 Purpose | +|----------|-----------| +| `Replace` | Replace instead of apply (for immutable fields) | +| `PruneLast` | Delete resources after all others sync | +| `ApplyOutOfSyncOnly` | Only apply changed resources | +| `ServerSideApply` | Use server-side apply (K8s 1.22+) | +| `FailOnSharedResource` | Fail if resource owned by another app | + +```yaml +syncPolicy: + syncOptions: + - CreateNamespace=true + - PrunePropagationPolicy=foreground + - PruneLast=true +``` + +--- + +## 📍 Slide 28 – 🏗️ Repository Structure Patterns + +**Pattern 1: Monorepo** +``` +repo/ +├── apps/ +│ ├── app1/ +│ └── app2/ +└── infrastructure/ + ├── prometheus/ + └── argocd/ +``` + +**Pattern 2: Repo per App** +``` +app1-config/ # App 1 manifests +app2-config/ # App 2 manifests +infrastructure/ # Shared infra +``` + +**Pattern 3: Environment Repos** +``` +dev-cluster/ # All dev apps +prod-cluster/ # All prod apps +``` + +> 💡 **Recommendation:** Start with monorepo, split when it gets complex + +--- + +## 📍 Slide 29 – 📊 ArgoCD Metrics & Monitoring + +**Key metrics to watch:** + +| 📊 Metric | 📝 Meaning | ⚠️ Alert When | +|----------|-----------|--------------| +| `argocd_app_sync_total` | Total syncs | Unusually high | +| `argocd_app_health_status` | App health | Not healthy | +| `argocd_app_reconcile_duration` | Sync time | > 5 minutes | +| `argocd_cluster_api_resource_objects` | Total objects | Growing unexpectedly | + +**Dashboard integration:** +* 📊 Grafana dashboards available +* 🔔 Alertmanager integration +* 📝 Slack/Teams notifications + +--- + +## 📍 Slide 30 – 🔐 RBAC & Multi-tenancy + +**ArgoCD RBAC:** + +```yaml +# argocd-rbac-cm ConfigMap +policy.csv: | + p, role:dev-team, applications, get, dev-project/*, allow + p, role:dev-team, applications, sync, dev-project/*, allow + p, role:ops-team, applications, *, */*, allow + + g, dev-group, role:dev-team + g, ops-group, role:ops-team +``` + +**Projects for isolation:** +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: dev-project +spec: + sourceRepos: + - 'https://github.com/org/dev-*' + destinations: + - namespace: 'dev-*' + server: https://kubernetes.default.svc +``` + +--- + +## 📍 Slide 31 – 🚨 Disaster Recovery + +**Git is your backup!** + +```mermaid +flowchart TD + A[💀 Cluster Gone] --> B[🆕 New Cluster] + B --> C[📦 Install ArgoCD] + C --> D[🔗 Connect to Git] + D --> E[🔄 Sync All Apps] + E --> F[✅ Fully Restored] +``` + +**Recovery steps:** +1. 🆕 Create new cluster +2. 📦 Install ArgoCD +3. 🔗 Point to Git repository +4. ☕ Wait for sync +5. ✅ Everything restored! + +> 💡 **Key insight:** If Git has everything, recovery is just a sync away + +--- + +## 📍 Slide 32 – 📋 GitOps Workflow Summary + +```mermaid +flowchart TD + A[👨‍💻 Developer] --> |PR| B[📝 Git Repo] + B --> |Review| C[✅ Merge] + C --> |Webhook| D[🤖 ArgoCD] + D --> |Sync| E[☸️ Cluster] + E --> |Status| D + D --> |Notify| F[💬 Slack] + + G[🔍 Drift Detection] --> D + D --> |Self-heal| E +``` + +**The complete loop:** +1. 📝 **Change:** Developer creates PR +2. 👀 **Review:** Team reviews and approves +3. 🔀 **Merge:** Changes merge to main +4. 🤖 **Detect:** ArgoCD detects new commit +5. 🔄 **Sync:** Resources deployed to cluster +6. 💚 **Verify:** Health checks pass +7. 📢 **Notify:** Team informed of deployment + +--- + +## 📍 Slide 33 – 🏭 Section 5: Production GitOps + +**Enterprise Patterns:** + +```mermaid +flowchart TD + subgraph Git + A[📝 Feature Branch] --> B[📝 Main Branch] + B --> C[📝 Release Branch] + end + + subgraph ArgoCD + D[🤖 Dev App] --> E[🤖 Staging App] + E --> F[🤖 Prod App] + end + + B --> D + C --> F +``` + +* 🔀 **Branch strategy:** Main for dev, release for prod +* 🎯 **Progressive delivery:** Dev → Staging → Prod +* ✅ **Promotion:** PR from main to release + +--- + +## 📍 Slide 34 – 🏢 Real-World GitOps: Intuit + +**Case Study: Intuit's GitOps Journey** + +* 📊 **Scale:** 2,000+ applications +* 🔄 **Deployments:** 500+ per day +* ⏱️ **MTTR:** Reduced by 80% + +**What they learned:** +* 📋 Start small, grow incrementally +* 🔧 Standardize templates early +* 👥 Train teams on Git workflows +* 📊 Monitor everything + +> 💬 *"GitOps turned our deployment from a ceremony into a non-event."* — Intuit Engineer + +--- + +## 📍 Slide 35 – 🔧 Migration Strategy + +**Adopting GitOps incrementally:** + +```mermaid +flowchart LR + A[1️⃣ Non-critical app] --> B[2️⃣ Dev environment] + B --> C[3️⃣ More apps] + C --> D[4️⃣ Staging] + D --> E[5️⃣ Production] +``` + +**Phases:** +1. 🧪 **Pilot:** One non-critical app in dev +2. 📚 **Learn:** Document patterns, train team +3. 📦 **Expand:** More apps, still dev +4. 🎭 **Staging:** Full staging environment +5. 🏭 **Production:** Controlled rollout + +--- + +## 📍 Slide 36 – 🎯 Key Takeaways + +1. 📝 **Git is the source of truth** — not the cluster, not CI/CD +2. 🔄 **Pull > Push** — credentials stay in cluster +3. 💚 **Self-healing** — drift is detected and corrected +4. 🔍 **Complete audit trail** — git log is your history +5. ↩️ **Easy rollback** — `git revert` reverts infrastructure +6. 🔐 **Secrets need special handling** — Sealed Secrets or External Secrets + +> 💬 *"Operations by Pull Request"* +> — Kelsey Hightower + +--- + +## 📍 Slide 37 – 🧠 Mindset Shift + +| 😰 Old Mindset | 🚀 New Mindset | +|---------------|----------------| +| "I'll just kubectl this" | "Let me create a PR" | +| "The cluster is truth" | "Git is truth" | +| "We need cluster access" | "We need Git access" | +| "Rollback is scary" | "Rollback is git revert" | +| "Who changed what?" | "Check git log" | +| "Emergency fix!" | "Emergency PR with fast review" | + +> 🤔 **Question:** Which mindset do you operate with today? + +--- + +## 📍 Slide 38 – 📝 QUIZ — DEVOPS_L13_POST + +--- + +## 📍 Slide 39 – 🚀 What's Next? + +**Coming up: Lecture 14 — Progressive Delivery with Argo Rollouts** + +```mermaid +flowchart LR + A[📦 v1] --> B[🚀 Canary 10%] + B --> C[🚀 Canary 50%] + C --> D[🚀 Full Rollout] +``` + +* 🐤 **Canary deployments** — test with small traffic +* 🔵 **Blue-green deployments** — instant switchover +* 📊 **Automated analysis** — metrics-driven promotion +* ↩️ **Automatic rollback** — on failure + +> 🎯 **Lab 13:** Set up ArgoCD and deploy your application using GitOps! + +--- + +## 📚 Resources + +**Documentation:** +* 📖 [ArgoCD Docs](https://argo-cd.readthedocs.io/) +* 📖 [OpenGitOps](https://opengitops.dev/) +* 📖 [Sealed Secrets](https://sealed-secrets.netlify.app/) +* 📖 [External Secrets Operator](https://external-secrets.io/) + +**Tools:** +* 🔧 [ArgoCD](https://argoproj.github.io/cd/) +* 🔧 [Flux](https://fluxcd.io/) +* 🔧 [Kustomize](https://kustomize.io/) + +**Books:** +* 📕 *GitOps and Kubernetes* by Billy Yuen, et al. +* 📕 *Continuous Delivery* by Jez Humble & David Farley diff --git a/lectures/lec14.md b/lectures/lec14.md new file mode 100644 index 0000000000..9ca7297ccd --- /dev/null +++ b/lectures/lec14.md @@ -0,0 +1,825 @@ +# 📌 Lecture 14 — Progressive Delivery: Deploying with Confidence + +> 🎯 **From risky big-bang deployments to controlled, observable releases** + +--- + +## 📍 Slide 1 – 🚀 Welcome to Progressive Delivery + +GitOps solved **how** we deploy. But **when things go wrong**... + +* 💥 **Traditional deploy:** 100% traffic instantly → all users affected +* 🐤 **Canary:** 5% traffic first → catch issues early +* 🔵 **Blue-green:** Switch traffic instantly → easy rollback + +```mermaid +flowchart LR + A[😰 Big Bang] --> B[🎲 Hope it works] + C[🐤 Canary] --> D[📊 Observe] + D --> E[✅ Promote or ↩️ Rollback] +``` + +> 🎯 **Goal:** Deploy changes safely with automated analysis and rollback + +--- + +## 📍 Slide 2 – 📚 Learning Outcomes + +By the end of this lecture, you will: + +| # | 🎯 Outcome | +|---|-----------| +| 1 | ✅ Understand **progressive delivery** concepts and benefits | +| 2 | ✅ Implement **canary deployments** with Argo Rollouts | +| 3 | ✅ Configure **blue-green deployments** for instant rollback | +| 4 | ✅ Set up **automated analysis** with metrics | +| 5 | ✅ Design **traffic management** strategies | +| 6 | ✅ Handle **rollback scenarios** gracefully | + +--- + +## 📍 Slide 3 – 🗺️ Lecture Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SECTION 0: Introduction (Slides 1-4) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 PRE QUIZ (Slide 5) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 1: The Deployment Risk Problem (Slides 6-10) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 2: Progressive Delivery Concepts (Slides 11-15) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 3: Argo Rollouts in Action (Slides 16-24) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 MID QUIZ (Slide 25) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 4: Advanced Strategies (Slides 26-32) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 5: Production Patterns (Slides 33-37) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 POST QUIZ (Slide 38) │ +├─────────────────────────────────────────────────────────────┤ +│ FINAL: What's Next (Slide 39) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 📍 Slide 4 – 🤔 The Big Question + +> 💬 *"We don't want to move fast and break things. We want to move fast and fix things."* +> — Facebook (ironically, after many outages) + +**Consider this:** + +* 🚀 You deploy a new feature at **5 PM Friday** +* 💥 It has a subtle bug affecting **10% of requests** +* ⏰ By the time you notice: **100,000 users affected** +* 😱 Rollback takes **15 minutes** of downtime + +> 🤔 **Think:** What if you could test with 1% of users first? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L14_PRE + +--- + +## 📍 Slide 6 – ⚠️ Section 1: The Deployment Risk Problem + +**Traditional "Big Bang" Deployment:** + +```mermaid +flowchart TD + A[📦 v1 Running] --> B[🚀 Deploy v2] + B --> C[⚡ 100% Traffic to v2] + C --> D{Works?} + D --> |Yes| E[✅ Success] + D --> |No| F[💀 100% Users Affected] + F --> G[😱 Emergency Rollback] +``` + +* ⚡ **All-or-nothing:** No gradual validation +* 😱 **High blast radius:** Everyone affected immediately +* ⏱️ **Slow detection:** Issues found in production + +--- + +## 📍 Slide 7 – 🔥 Pain Point 1: Silent Failures + +**Scenario:** Memory leak that only triggers under load + +```mermaid +flowchart LR + A[🧪 Tests Pass] --> B[🎭 Staging OK] + B --> C[🏭 Deploy to Prod] + C --> D[📈 Real Traffic] + D --> E[💥 OOM after 2 hours] +``` + +* 🧪 **Tests pass** — synthetic load is different +* 🎭 **Staging works** — not enough traffic to trigger +* 🏭 **Production crashes** — after hours of operation + +**Real impact:** +* 😱 Facebook 2021: 6-hour outage from config change +* 💰 Estimated loss: $100 million + +--- + +## 📍 Slide 8 – 🔥 Pain Point 2: Slow Rollback + +**Traditional rollback process:** + +| ⏱️ Step | 📝 Action | ⌛ Time | +|---------|----------|--------| +| 1 | Detect the issue | 10 min | +| 2 | Confirm it's the deploy | 5 min | +| 3 | Find previous version | 2 min | +| 4 | Rebuild/redeploy | 10 min | +| 5 | Verify rollback | 5 min | +| **Total** | **Downtime** | **32+ min** | + +* 🐌 **Slow detection:** Monitoring lag +* 🤔 **Decision paralysis:** "Is it really the deploy?" +* 🔧 **Manual process:** Error-prone under pressure + +--- + +## 📍 Slide 9 – 🔥 Pain Point 3: No Gradual Validation + +**What we want:** + +``` +Deploy → Observe → Decide → Promote/Rollback +``` + +**What we get:** + +``` +Deploy → 🙏 Hope → React when broken +``` + +* 📊 **No metrics integration** — can't auto-decide +* 👨‍💻 **Human in the loop** — for every deploy +* 🎲 **Risk acceptance** — every release is a gamble + +--- + +## 📍 Slide 10 – 💰 The Cost of Bad Deployments + +| 🔥 Problem | 💥 Impact | 📊 Industry Data | +|-----------|----------|------------------| +| Failed deployments | Service degradation | 46% experience monthly failures | +| Slow rollback | Extended outages | Avg 30 min to rollback | +| No canary testing | Full user impact | 100% blast radius | +| Manual promotion | Human error | 70% of incidents | + +**DORA metrics show:** +* 🏆 **Elite teams:** Deploy multiple times per day with <1% failure rate +* 😰 **Low performers:** Monthly deploys with 15%+ failure rate + +--- + +## 📍 Slide 11 – ✅ Section 2: Progressive Delivery Concepts + +**What is Progressive Delivery?** + +* 🐤 **Gradual rollout:** Incrementally shift traffic +* 📊 **Observability:** Measure success at each step +* 🤖 **Automation:** Promote or rollback based on metrics +* 🎯 **Targeted:** Control which users see changes + +```mermaid +flowchart LR + A[📦 v2] --> |5%| B[🎯 Test] + B --> |Metrics OK| C[25%] + C --> |Metrics OK| D[50%] + D --> |Metrics OK| E[100%] + + B --> |Metrics Bad| F[↩️ Rollback] +``` + +--- + +## 📍 Slide 12 – 🚫 Progressive Delivery: What It's NOT + +| 🚫 Myth | ✅ Reality | +|---------|----------| +| Just slow deployments | Strategic, metrics-driven progression | +| Replaces testing | Complements testing with real traffic | +| Only for big companies | Available via Argo Rollouts, Flagger | +| Complicated to implement | Start simple, add automation gradually | + +> 🔥 **Hot take:** "If you're not doing progressive delivery, you're gambling with every deploy." + +**Progressive Delivery is:** +* 🎯 **Risk reduction** — smaller blast radius +* 📊 **Data-driven** — metrics decide promotion +* 🔄 **Continuous** — part of the deployment pipeline + +--- + +## 📍 Slide 13 – 🐤 Canary Deployments Explained + +**Named after "canary in a coal mine"** — early warning system + +```mermaid +flowchart TD + subgraph Production + A[📦 v1 - 95%] --> C[🌐 Users] + B[📦 v2 - 5%] --> C + end + + D[📊 Metrics] --> E{Healthy?} + E --> |Yes| F[📦 v2 - 100%] + E --> |No| G[↩️ v1 - 100%] +``` + +**How it works:** +1. 🚀 Deploy new version alongside old +2. 🎯 Route small % of traffic to new +3. 📊 Compare metrics (errors, latency) +4. ✅ Gradually increase or ↩️ rollback + +--- + +## 📍 Slide 14 – 🔵 Blue-Green Deployments Explained + +**Two identical environments, instant switchover** + +```mermaid +flowchart LR + subgraph Before + A[🔵 Blue v1] --> |100%| C[🌐 Traffic] + B[🟢 Green v2] --> |0%| C + end + + subgraph After Switch + D[🔵 Blue v1] --> |0%| F[🌐 Traffic] + E[🟢 Green v2] --> |100%| F + end +``` + +**Characteristics:** +* ⚡ **Instant switch:** Traffic moves all at once +* ↩️ **Fast rollback:** Switch back to blue +* 💰 **Resource cost:** Double infrastructure during deploy +* 🎯 **Use case:** Database migrations, breaking changes + +--- + +## 📍 Slide 15 – 📊 Canary vs Blue-Green + +| 📋 Aspect | 🐤 Canary | 🔵 Blue-Green | +|----------|----------|---------------| +| Traffic shift | Gradual (5% → 25% → 100%) | Instant (0% → 100%) | +| Rollback speed | Instant | Instant | +| Resource usage | Minimal overhead | Double during deploy | +| Risk exposure | Minimal (small %) | Full (100% at switch) | +| Complexity | Higher (traffic splitting) | Lower (simple switch) | +| Best for | Most deployments | Major version changes | + +> 🤔 **Think:** Which strategy would you use for a database schema change? + +--- + +## 📍 Slide 16 – 🎮 Section 3: Argo Rollouts in Action + +**What is Argo Rollouts?** + +* 🔄 Kubernetes controller for progressive delivery +* 📦 Replaces standard Deployment resource +* 🎯 Supports canary, blue-green, and more +* 📊 Integrates with metrics providers + +```mermaid +flowchart TD + A[📝 Rollout Resource] --> B[🤖 Argo Rollouts Controller] + B --> C[📦 ReplicaSets] + C --> D[☸️ Pods] + E[📊 Prometheus] --> B + B --> F[🎯 Traffic Management] +``` + +--- + +## 📍 Slide 17 – 💥 Scenario 1: First Canary Rollout + +**Basic canary configuration:** + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: my-app +spec: + replicas: 10 + strategy: + canary: + steps: + - setWeight: 20 + - pause: {duration: 5m} + - setWeight: 50 + - pause: {duration: 5m} + - setWeight: 100 + selector: + matchLabels: + app: my-app + template: + # Pod template (same as Deployment) +``` + +```mermaid +flowchart LR + A[0%] --> |Step 1| B[20%] + B --> |5min pause| C[50%] + C --> |5min pause| D[100%] +``` + +--- + +## 📍 Slide 18 – ✅ Solution 1: Traffic Progression + +**What happens during canary:** + +| 🕐 Time | 📦 Stable | 🐤 Canary | 📊 Status | +|---------|----------|----------|-----------| +| T+0 | 100% | 0% | Rollout started | +| T+1 | 80% | 20% | setWeight: 20 | +| T+6 | 50% | 50% | pause completed | +| T+11 | 0% | 100% | Full promotion | + +**Rollout states:** +* 🔄 **Progressing:** Moving through steps +* ⏸️ **Paused:** Waiting (manual or timed) +* ✅ **Healthy:** Rollout complete +* 💥 **Degraded:** Issues detected + +--- + +## 📍 Slide 19 – 💥 Scenario 2: Blue-Green with Argo Rollouts + +**Blue-green configuration:** + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: my-app +spec: + replicas: 5 + strategy: + blueGreen: + activeService: my-app-active + previewService: my-app-preview + autoPromotionEnabled: false # Manual promotion + scaleDownDelaySeconds: 30 + selector: + matchLabels: + app: my-app +``` + +```mermaid +flowchart TD + A[🔵 Active Service] --> B[📦 Stable Pods] + C[🟢 Preview Service] --> D[📦 New Pods] + E[👨‍💻 QA] --> C + F[🌐 Users] --> A +``` + +--- + +## 📍 Slide 20 – ✅ Solution 2: Preview and Promote + +**Blue-green workflow:** + +1. 🚀 **Deploy:** New pods created, preview service points to them +2. 🧪 **Test:** QA validates via preview service +3. ✅ **Promote:** Traffic switches to new pods +4. 🗑️ **Cleanup:** Old pods scaled down after delay + +**Commands:** +```bash +# Check rollout status +kubectl argo rollouts get rollout my-app + +# Promote preview to active +kubectl argo rollouts promote my-app + +# Abort and rollback +kubectl argo rollouts abort my-app +``` + +--- + +## 📍 Slide 21 – 💥 Scenario 3: Automated Analysis + +**Problem:** Manual observation doesn't scale + +```mermaid +flowchart TD + A[🐤 Canary] --> B[📊 Metrics] + B --> C{Error rate < 1%?} + C --> |Yes| D{Latency < 500ms?} + D --> |Yes| E[✅ Promote] + C --> |No| F[↩️ Rollback] + D --> |No| F +``` + +**Solution:** AnalysisTemplate + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: AnalysisTemplate +metadata: + name: success-rate +spec: + metrics: + - name: success-rate + interval: 1m + successCondition: result[0] >= 0.99 + provider: + prometheus: + query: | + sum(rate(http_requests_total{status=~"2.*"}[5m])) / + sum(rate(http_requests_total[5m])) +``` + +--- + +## 📍 Slide 22 – ✅ Solution 3: Analysis Integration + +**Connecting analysis to rollout:** + +```yaml +strategy: + canary: + steps: + - setWeight: 20 + - analysis: + templates: + - templateName: success-rate + args: + - name: service-name + value: my-app + - setWeight: 50 + - analysis: + templates: + - templateName: success-rate + - setWeight: 100 +``` + +**Analysis outcomes:** +* ✅ **Successful:** All metrics pass → continue +* ❌ **Failed:** Metric fails → automatic rollback +* ⚠️ **Inconclusive:** Not enough data → pause + +--- + +## 📍 Slide 23 – 💥 Scenario 4: Traffic Management + +**Problem:** Need fine-grained traffic control + +**Solutions:** + +| 🛠️ Traffic Manager | 📝 Description | +|-------------------|---------------| +| **Nginx Ingress** | Canary annotations | +| **Istio** | VirtualService routing | +| **AWS ALB** | Target group weights | +| **Traefik** | TraefikService | + +```yaml +# With Istio +strategy: + canary: + trafficRouting: + istio: + virtualService: + name: my-app-vsvc + destinationRule: + name: my-app-destrule + canarySubsetName: canary + stableSubsetName: stable +``` + +--- + +## 📍 Slide 24 – ✅ Solution 4: Nginx Ingress Canary + +**Simple traffic splitting with Nginx:** + +```yaml +strategy: + canary: + canaryService: my-app-canary + stableService: my-app-stable + trafficRouting: + nginx: + stableIngress: my-app-ingress +``` + +**How it works:** +* 🔧 Argo Rollouts creates canary ingress +* 📊 Sets `nginx.ingress.kubernetes.io/canary-weight` +* 🔄 Updates weight as rollout progresses + +--- + +## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L14_MID + +--- + +## 📍 Slide 26 – 🔧 Section 4: Advanced Strategies + +**Experiment: A/B Testing** + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Experiment +metadata: + name: ab-test +spec: + duration: 1h + templates: + - name: baseline + specRef: stable + replicas: 1 + - name: canary + specRef: canary + replicas: 1 + analyses: + - name: compare-versions + templateName: compare-metrics +``` + +**Use cases:** +* 🧪 **Feature testing:** Compare feature A vs B +* 📊 **Performance testing:** Baseline vs optimized +* 🎯 **User experience:** Different UIs + +--- + +## 📍 Slide 27 – 📊 Metrics for Analysis + +**Common metrics to analyze:** + +| 📊 Metric | 📝 What it Measures | ⚠️ Threshold | +|----------|-------------------|-------------| +| Error rate | % of failed requests | < 1% | +| Latency P99 | Slowest 1% of requests | < 500ms | +| Saturation | Resource utilization | < 80% | +| Success rate | % of successful operations | > 99% | + +**Prometheus queries:** +```promql +# Error rate +sum(rate(http_requests_total{status=~"5.*"}[5m])) / +sum(rate(http_requests_total[5m])) + +# P99 latency +histogram_quantile(0.99, rate(http_duration_seconds_bucket[5m])) +``` + +--- + +## 📍 Slide 28 – 🔄 Rollback Strategies + +**Automatic rollback triggers:** + +```yaml +strategy: + canary: + steps: + - setWeight: 20 + - analysis: + templates: + - templateName: error-rate + # If analysis fails, automatic rollback +``` + +**Manual rollback:** +```bash +# Abort current rollout +kubectl argo rollouts abort my-app + +# Undo to previous version +kubectl argo rollouts undo my-app + +# Retry after fix +kubectl argo rollouts retry rollout my-app +``` + +```mermaid +flowchart TD + A[💥 Analysis Failed] --> B[🤖 Auto Rollback] + B --> C[📦 Scale down canary] + C --> D[🔄 Restore stable] + D --> E[✅ Service restored] +``` + +--- + +## 📍 Slide 29 – ⏸️ Pause and Resume + +**Manual gates in rollout:** + +```yaml +steps: + - setWeight: 20 + - pause: {} # Manual pause - requires promotion + - setWeight: 50 + - pause: {duration: 10m} # Timed pause + - setWeight: 100 +``` + +**Commands:** +```bash +# Resume paused rollout +kubectl argo rollouts promote my-app + +# Skip all remaining steps +kubectl argo rollouts promote my-app --full +``` + +**Use cases:** +* 👀 **Manual verification** before wider rollout +* 🕐 **Business hours** — pause overnight +* 🧪 **QA sign-off** required + +--- + +## 📍 Slide 30 – 🔗 ArgoCD + Argo Rollouts + +**GitOps + Progressive Delivery:** + +```mermaid +flowchart TD + A[📝 Git Push] --> B[🤖 ArgoCD] + B --> |Sync| C[📦 Rollout Resource] + C --> D[🤖 Argo Rollouts Controller] + D --> E[🐤 Canary Progression] + E --> F[📊 Analysis] + F --> |Pass| G[✅ Promoted] + F --> |Fail| H[↩️ Rollback] +``` + +**Benefits:** +* 📝 **Declarative:** Rollout strategy in Git +* 🔄 **Automated:** ArgoCD syncs, Rollouts executes +* 🔍 **Observable:** Both tools have UIs + +--- + +## 📍 Slide 31 – 📊 Dashboard and Visualization + +**Argo Rollouts Dashboard:** + +```bash +# Install dashboard +kubectl argo rollouts dashboard + +# Access at localhost:3100 +``` + +**Features:** +* 📊 **Real-time status:** See rollout progression +* 🎛️ **Controls:** Promote, abort, retry +* 📈 **History:** Past rollouts and outcomes +* 🔗 **Integration:** Links to metrics + +--- + +## 📍 Slide 32 – 🎯 Best Practices + +| 📋 Practice | 📝 Why | +|------------|--------| +| Start with simple canary | Learn before adding complexity | +| Always have analysis | Don't rely only on time-based | +| Set appropriate thresholds | Too strict = never promotes | +| Monitor canary metrics | Catch issues before promotion | +| Test rollback procedure | Know it works before you need it | +| Use GitOps | Keep strategy in version control | + +--- + +## 📍 Slide 33 – 🏭 Section 5: Production Patterns + +**Netflix Progressive Delivery:** + +```mermaid +flowchart LR + A[📦 v2] --> B[🎯 Internal 1%] + B --> C[🌍 One Region 5%] + C --> D[🌍 All Regions 25%] + D --> E[🚀 Full 100%] +``` + +**Their learnings:** +* 🎯 **Internal first:** Employees as first canaries +* 🌍 **Regional:** Test in one region before global +* 📊 **Metrics-driven:** Automated promotion +* 🐌 **Patience:** Days, not minutes + +--- + +## 📍 Slide 34 – 🔧 Anti-Patterns to Avoid + +| ❌ Anti-Pattern | ✅ Better Approach | +|----------------|-------------------| +| Canary with no metrics | Add analysis, even basic | +| Too fast progression | Allow time for issues to surface | +| Ignoring saturation | Include resource metrics | +| Manual-only promotion | Automate with analysis | +| Skip staging canary | Test progressive delivery in staging | + +--- + +## 📍 Slide 35 – 📈 Measuring Success + +**DORA metrics with progressive delivery:** + +| 📊 Metric | 😰 Before | 🚀 After | +|----------|----------|---------| +| Deployment frequency | Weekly | Multiple/day | +| Change failure rate | 15% | < 1% | +| MTTR | 30 min | 2 min | +| Lead time | Days | Hours | + +**Why improvement:** +* 🐤 **Catch issues early** — smaller blast radius +* ↩️ **Fast rollback** — seconds, not minutes +* 📊 **Data-driven** — objective decisions +* 🔄 **Confidence** — deploy more often + +--- + +## 📍 Slide 36 – 🎯 Key Takeaways + +1. 🐤 **Canary deployments** test with small traffic before full rollout +2. 🔵 **Blue-green** enables instant rollback via traffic switch +3. 📊 **Automated analysis** removes human guesswork +4. 🎯 **Argo Rollouts** makes progressive delivery accessible +5. 🔗 **GitOps integration** keeps strategy declarative +6. ↩️ **Fast rollback** is as important as deployment + +> 💬 *"Deploy frequently, observe constantly, rollback automatically."* + +--- + +## 📍 Slide 37 – 🧠 Mindset Shift + +| 😰 Old Mindset | 🚀 New Mindset | +|---------------|----------------| +| "Let's hope it works" | "Let's measure and know" | +| "Deploy and pray" | "Deploy, observe, decide" | +| "Rollback is failure" | "Rollback is success" | +| "Testing is enough" | "Testing + production validation" | +| "Deploy once a week (safe)" | "Deploy often (safer)" | +| "100% or nothing" | "Progressive and controlled" | + +> 🤔 **Question:** Which deployment approach does your team use today? + +--- + +## 📍 Slide 38 – 📝 QUIZ — DEVOPS_L14_POST + +--- + +## 📍 Slide 39 – 🚀 What's Next? + +**Coming up: Lecture 15 — Stateful Applications & Observability** + +```mermaid +flowchart LR + A[📦 Deployment] --> B[🗄️ StatefulSet] + B --> C[💾 Persistent Storage] + C --> D[📊 Monitoring] +``` + +* 🗄️ **StatefulSets:** Managing stateful applications +* 💾 **Persistent storage:** Beyond ephemeral pods +* 📊 **Observability:** Prometheus, Grafana +* 🔍 **Alerting:** Know before users complain + +> 🎯 **Lab 14:** Implement canary deployments with Argo Rollouts! + +--- + +## 📚 Resources + +**Documentation:** +* 📖 [Argo Rollouts Docs](https://argoproj.github.io/argo-rollouts/) +* 📖 [Progressive Delivery](https://www.weave.works/blog/progressive-delivery) +* 📖 [Canary Deployments](https://martinfowler.com/bliki/CanaryRelease.html) + +**Tools:** +* 🔧 [Argo Rollouts](https://argoproj.github.io/argo-rollouts/) +* 🔧 [Flagger](https://flagger.app/) +* 🔧 [Istio](https://istio.io/) + +**Books:** +* 📕 *Accelerate* by Nicole Forsgren, Jez Humble, Gene Kim +* 📕 *Continuous Delivery* by Jez Humble & David Farley diff --git a/lectures/lec15.md b/lectures/lec15.md new file mode 100644 index 0000000000..eb828de609 --- /dev/null +++ b/lectures/lec15.md @@ -0,0 +1,821 @@ +# 📌 Lecture 15 — Stateful Applications & Observability: The Complete Picture + +> 🎯 **From stateless simplicity to production-ready stateful workloads with full observability** + +--- + +## 📍 Slide 1 – 🚀 The Final Pieces of Production Kubernetes + +We've deployed applications, managed configs, and implemented GitOps. Two challenges remain: + +* 🗄️ **Stateful apps:** Databases, message queues, caches — they need identity and stable storage +* 📊 **Observability:** If you can't see it, you can't fix it + +```mermaid +flowchart LR + A[📦 Stateless Apps] --> B[🗄️ StatefulSets] + B --> C[📊 Monitoring] + C --> D[🔔 Alerting] + D --> E[💎 Production Ready] +``` + +> 🎯 **Goal:** Master stateful workloads and comprehensive cluster observability + +--- + +## 📍 Slide 2 – 📚 Learning Outcomes + +By the end of this lecture, you will: + +| # | 🎯 Outcome | +|---|-----------| +| 1 | ✅ Understand when to use **StatefulSets** vs Deployments | +| 2 | ✅ Implement **headless services** for pod discovery | +| 3 | ✅ Configure **VolumeClaimTemplates** for per-pod storage | +| 4 | ✅ Deploy **Prometheus** for metrics collection | +| 5 | ✅ Create **Grafana dashboards** for visualization | +| 6 | ✅ Set up **alerting** for proactive incident response | + +--- + +## 📍 Slide 3 – 🗺️ Lecture Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SECTION 0: Introduction (Slides 1-4) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 PRE QUIZ (Slide 5) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 1: Stateful Workload Challenges (Slides 6-10) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 2: StatefulSets Deep Dive (Slides 11-18) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 MID QUIZ (Slide 19) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 3: Observability Fundamentals (Slides 20-28) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 4: Production Monitoring (Slides 29-36) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 POST QUIZ (Slide 37) │ +├─────────────────────────────────────────────────────────────┤ +│ FINAL: What's Next (Slide 38) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 📍 Slide 4 – 🤔 The Big Question + +> 💬 *"You can't manage what you can't measure."* +> — Peter Drucker + +**Consider these scenarios:** + +* 🗄️ **Database cluster:** Pods need stable identity for replication +* 📊 **3 AM alert:** Is the app slow, or is it the database? +* 🔍 **Debugging:** "What changed in the last hour?" +* 🔮 **Capacity planning:** "Will we run out of storage next month?" + +> 🤔 **Think:** How do you know your system is healthy right now? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L15_PRE + +--- + +## 📍 Slide 6 – ⚠️ Section 1: Why Stateless Isn't Always Enough + +**Deployments (Stateless) characteristics:** + +```mermaid +flowchart TD + A[📦 Deployment] --> B[📦 Pod-abc123] + A --> C[📦 Pod-def456] + A --> D[📦 Pod-ghi789] + + E[Pods are interchangeable] + F[Random names] + G[Any pod can be replaced] +``` + +* ✅ **Great for:** Web servers, API services, workers +* ❌ **Problem for:** Databases, message queues, distributed systems + +--- + +## 📍 Slide 7 – 🔥 Pain Point 1: Pod Identity + +**Stateful apps need stable identity:** + +| 🗄️ Application | 🔑 Why Identity Matters | +|---------------|------------------------| +| **PostgreSQL** | Primary/replica must know who is who | +| **MongoDB** | Replica set members have specific roles | +| **Kafka** | Brokers identified by stable IDs | +| **Redis Cluster** | Nodes need persistent slots | +| **Elasticsearch** | Nodes join cluster by name | + +**With Deployment:** +``` +pod-abc123 → restarts → pod-xyz789 (new name!) +``` + +**The problem:** Other pods can't find it anymore + +--- + +## 📍 Slide 8 – 🔥 Pain Point 2: Storage Persistence + +**Scenario:** 3-node MongoDB replica set + +```mermaid +flowchart TD + A[📦 Deployment] --> B[📦 Pod 1] + A --> C[📦 Pod 2] + A --> D[📦 Pod 3] + + E[💾 Shared PVC] --> B + E --> C + E --> D + + F[😱 Problem: All pods fighting for same storage!] +``` + +**What we need:** +* 📦 Pod 1 → 💾 Volume 1 (its own data) +* 📦 Pod 2 → 💾 Volume 2 (its own data) +* 📦 Pod 3 → 💾 Volume 3 (its own data) + +--- + +## 📍 Slide 9 – 🔥 Pain Point 3: Ordered Operations + +**Database cluster startup order matters:** + +```mermaid +flowchart LR + A[🥇 Primary starts] --> B[🥈 Replica 1 joins] + B --> C[🥉 Replica 2 joins] +``` + +**Deployment behavior:** All pods start simultaneously +* 😱 Race condition: Who is primary? +* 💥 Data corruption risk + +**Shutdown order matters too:** +* 🥉 Replicas drain first +* 🥇 Primary shuts down last + +--- + +## 📍 Slide 10 – 📊 Deployment vs StatefulSet + +| 📋 Aspect | 📦 Deployment | 🗄️ StatefulSet | +|----------|--------------|----------------| +| Pod names | Random suffix | Stable ordinal (app-0, app-1) | +| Storage | Shared or none | Per-pod PVCs | +| Scaling | Parallel | Sequential | +| Updates | Rolling (parallel) | Rolling (sequential) | +| Network identity | Via Service | Stable DNS per pod | +| Use case | Stateless apps | Stateful apps | + +--- + +## 📍 Slide 11 – ✅ Section 2: StatefulSets to the Rescue + +**StatefulSet provides:** + +```mermaid +flowchart TD + A[🗄️ StatefulSet] --> B[📦 app-0] + A --> C[📦 app-1] + A --> D[📦 app-2] + + B --> E[💾 data-app-0] + C --> F[💾 data-app-1] + D --> G[💾 data-app-2] + + H[🌐 Headless Service] --> B + H --> C + H --> D +``` + +* 🔢 **Stable, unique network IDs:** `app-0`, `app-1`, `app-2` +* 💾 **Stable, persistent storage:** Each pod gets its own PVC +* 📊 **Ordered deployment:** `app-0` first, then `app-1`, then `app-2` +* 🔄 **Ordered termination:** Reverse order + +--- + +## 📍 Slide 12 – 🌐 Headless Services Explained + +**Regular Service vs Headless Service:** + +```yaml +# Regular Service - Load balances +apiVersion: v1 +kind: Service +metadata: + name: my-app +spec: + clusterIP: 10.0.0.100 # Gets an IP + ports: + - port: 80 +--- +# Headless Service - Direct pod access +apiVersion: v1 +kind: Service +metadata: + name: my-app-headless +spec: + clusterIP: None # No IP assigned! + ports: + - port: 80 +``` + +**DNS resolution:** +* **Regular:** `my-app.namespace.svc` → `10.0.0.100` +* **Headless:** `my-app-headless.namespace.svc` → `10.1.2.3, 10.1.2.4, 10.1.2.5` (all pod IPs) + +--- + +## 📍 Slide 13 – 🔗 Pod DNS in StatefulSets + +**Each pod gets a stable DNS name:** + +``` +...svc.cluster.local +``` + +**Example with MongoDB:** +``` +mongodb-0.mongodb-headless.default.svc.cluster.local +mongodb-1.mongodb-headless.default.svc.cluster.local +mongodb-2.mongodb-headless.default.svc.cluster.local +``` + +```mermaid +flowchart LR + A[🔍 DNS Query: mongodb-0.mongodb-headless] --> B[📦 mongodb-0] + C[🔍 DNS Query: mongodb-1.mongodb-headless] --> D[📦 mongodb-1] +``` + +* ✅ Other apps can connect to specific pods +* ✅ Pods can discover each other by name +* ✅ Names stay the same even after restart + +--- + +## 📍 Slide 14 – 💾 VolumeClaimTemplates + +**Automatic PVC creation per pod:** + +```yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: mongodb +spec: + serviceName: mongodb-headless + replicas: 3 + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: ["ReadWriteOnce"] + storageClassName: standard + resources: + requests: + storage: 10Gi +``` + +**Result:** +``` +PVC: data-mongodb-0 → bound to mongodb-0 +PVC: data-mongodb-1 → bound to mongodb-1 +PVC: data-mongodb-2 → bound to mongodb-2 +``` + +--- + +## 📍 Slide 15 – 🔄 Scaling Behavior + +**Scale up (sequential):** +```mermaid +flowchart LR + A[app-0 ready] --> B[app-1 starts] + B --> C[app-1 ready] + C --> D[app-2 starts] +``` + +**Scale down (reverse order):** +```mermaid +flowchart LR + A[app-2 terminates] --> B[app-2 gone] + B --> C[app-1 terminates] + C --> D[app-1 gone] +``` + +**Key points:** +* 🔄 Next pod only starts when previous is **Ready** +* 🗑️ Scaling down starts from highest ordinal +* 💾 PVCs are **NOT deleted** on scale down (data preserved) + +--- + +## 📍 Slide 16 – 🔄 Update Strategies + +**RollingUpdate (default):** +```yaml +spec: + updateStrategy: + type: RollingUpdate + rollingUpdate: + partition: 0 # Update all pods +``` + +**Partition for canary:** +```yaml +rollingUpdate: + partition: 2 # Only update pods >= 2 +``` + +```mermaid +flowchart LR + A[app-0: v1] --> B[app-1: v1] + B --> C[app-2: v2 - canary] +``` + +* ✅ Test new version on highest ordinal first +* ✅ Gradually lower partition to update more pods + +--- + +## 📍 Slide 17 – 🗄️ Complete StatefulSet Example + +```yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: mongodb +spec: + selector: + matchLabels: + app: mongodb + serviceName: mongodb-headless + replicas: 3 + template: + metadata: + labels: + app: mongodb + spec: + containers: + - name: mongodb + image: mongo:7 + ports: + - containerPort: 27017 + volumeMounts: + - name: data + mountPath: /data/db + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 10Gi +``` + +--- + +## 📍 Slide 18 – ⚠️ StatefulSet Gotchas + +| ⚠️ Gotcha | 📝 Solution | +|----------|------------| +| PVCs not deleted on scale down | Manual deletion if needed | +| Pod stuck in Pending | Check PVC binding, storage class | +| Headless service required | Must create before StatefulSet | +| Slow scaling | Increase `podManagementPolicy: Parallel` | +| Data loss on PVC deletion | Use `reclaimPolicy: Retain` | + +**Important:** StatefulSets are **more complex** than Deployments. Use only when needed! + +--- + +## 📍 Slide 19 – 📝 QUIZ — DEVOPS_L15_MID + +--- + +## 📍 Slide 20 – 📊 Section 3: The Three Pillars of Observability + +**Observability = Understanding system behavior from outputs** + +```mermaid +flowchart TD + A[📊 Metrics] --> D[🔍 Observability] + B[📝 Logs] --> D + C[🔗 Traces] --> D + D --> E[💡 Understanding] +``` + +| 📊 Pillar | 📝 What It Answers | 🛠️ Tools | +|----------|-------------------|----------| +| **Metrics** | What is happening? (numbers) | Prometheus, Grafana | +| **Logs** | What happened? (events) | Loki, ELK | +| **Traces** | Where did it happen? (requests) | Jaeger, Zipkin | + +--- + +## 📍 Slide 21 – 📈 Prometheus: The Metrics Foundation + +**What is Prometheus?** + +* 📊 Time-series database for metrics +* 🔍 Pull-based metric collection +* 📝 Powerful query language (PromQL) +* 🔔 Built-in alerting + +```mermaid +flowchart LR + A[📦 App with /metrics] --> |Pull| B[📊 Prometheus] + C[📦 Another App] --> |Pull| B + B --> D[📈 Grafana] + B --> E[🔔 Alertmanager] +``` + +--- + +## 📍 Slide 22 – 🎯 Prometheus Metric Types + +| 📊 Type | 📝 Description | 🎯 Example | +|--------|---------------|-----------| +| **Counter** | Only goes up | Total requests, errors | +| **Gauge** | Can go up/down | Temperature, queue size | +| **Histogram** | Distribution of values | Request latency | +| **Summary** | Similar to histogram | Quantiles | + +**Example metrics:** +```promql +# Counter - total HTTP requests +http_requests_total{method="GET", status="200"} + +# Gauge - current memory usage +node_memory_MemAvailable_bytes + +# Histogram - request duration +http_request_duration_seconds_bucket{le="0.5"} +``` + +--- + +## 📍 Slide 23 – 🔍 PromQL Basics + +**Query examples:** + +```promql +# Current value +up{job="kubernetes-pods"} + +# Rate of change (per second over 5m) +rate(http_requests_total[5m]) + +# Error rate percentage +sum(rate(http_requests_total{status=~"5.."}[5m])) / +sum(rate(http_requests_total[5m])) * 100 + +# 99th percentile latency +histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) + +# Top 5 pods by CPU +topk(5, sum by (pod) (rate(container_cpu_usage_seconds_total[5m]))) +``` + +--- + +## 📍 Slide 24 – 📦 kube-prometheus-stack + +**All-in-one monitoring solution:** + +```mermaid +flowchart TD + A[📦 kube-prometheus-stack] --> B[📊 Prometheus] + A --> C[📈 Grafana] + A --> D[🔔 Alertmanager] + A --> E[📝 Node Exporter] + A --> F[📊 kube-state-metrics] +``` + +**Includes:** +* 🔧 Pre-configured scrape targets +* 📊 Default dashboards +* 🔔 Default alerting rules +* 📈 Grafana with data sources configured + +```bash +helm install prometheus prometheus-community/kube-prometheus-stack +``` + +--- + +## 📍 Slide 25 – 📈 Grafana Dashboards + +**Key Grafana concepts:** + +| 🔧 Concept | 📝 Description | +|-----------|---------------| +| **Data Source** | Where data comes from (Prometheus) | +| **Dashboard** | Collection of panels | +| **Panel** | Single visualization | +| **Variable** | Dynamic filters (namespace, pod) | + +**Popular pre-built dashboards:** +* 🔢 **1860:** Node Exporter Full +* 🔢 **315:** Kubernetes cluster +* 🔢 **7249:** Kubernetes Pod Resources + +--- + +## 📍 Slide 26 – 🔔 Alerting with Alertmanager + +**Alert flow:** + +```mermaid +flowchart LR + A[📊 Prometheus] --> |Alert fires| B[🔔 Alertmanager] + B --> |Route| C[📧 Email] + B --> |Route| D[💬 Slack] + B --> |Route| E[📱 PagerDuty] +``` + +**Alert rule example:** +```yaml +groups: + - name: app-alerts + rules: + - alert: HighErrorRate + expr: | + sum(rate(http_requests_total{status=~"5.."}[5m])) / + sum(rate(http_requests_total[5m])) > 0.01 + for: 5m + labels: + severity: critical + annotations: + summary: "High error rate detected" + description: "Error rate is {{ $value | humanizePercentage }}" +``` + +--- + +## 📍 Slide 27 – 📊 The Four Golden Signals + +**Google SRE's essential metrics:** + +| 🔔 Signal | 📝 What to Measure | 📊 Prometheus Example | +|----------|-------------------|----------------------| +| **Latency** | Response time | `histogram_quantile(0.99, ...)` | +| **Traffic** | Request rate | `rate(http_requests_total[5m])` | +| **Errors** | Failure rate | `rate(http_requests_total{status=~"5.."}[5m])` | +| **Saturation** | Resource usage | `container_memory_usage_bytes / limit` | + +> 💡 **Tip:** Start by monitoring these four signals for every service + +--- + +## 📍 Slide 28 – 🔧 Init Containers for Dependencies + +**Problem:** App starts before database is ready + +```yaml +spec: + initContainers: + - name: wait-for-db + image: busybox + command: + - sh + - -c + - | + until nc -z postgres-0.postgres-headless 5432; do + echo "Waiting for database..." + sleep 2 + done + containers: + - name: app + image: my-app +``` + +```mermaid +flowchart LR + A[🚀 Pod Start] --> B[⏳ Init Container] + B --> |DB Ready| C[📦 App Container] +``` + +--- + +## 📍 Slide 29 – 🏭 Section 4: Production Monitoring Setup + +**ServiceMonitor for custom apps:** + +```yaml +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: my-app-monitor +spec: + selector: + matchLabels: + app: my-app + endpoints: + - port: metrics + interval: 30s + path: /metrics +``` + +**What this does:** +* 🔍 Tells Prometheus to scrape your app +* 📊 Collects metrics from `/metrics` endpoint +* ⏱️ Every 30 seconds + +--- + +## 📍 Slide 30 – 📊 Monitoring StatefulSets + +**Key metrics for stateful apps:** + +| 📊 Metric | 📝 Why Important | +|----------|------------------| +| `kubelet_volume_stats_used_bytes` | Disk usage per PVC | +| `kube_statefulset_replicas` | Expected vs actual replicas | +| `kube_statefulset_status_replicas_ready` | Healthy replicas | +| App-specific metrics | Replication lag, connections | + +**Alert example:** +```yaml +- alert: StatefulSetNotReady + expr: | + kube_statefulset_status_replicas_ready / + kube_statefulset_replicas < 1 + for: 5m + labels: + severity: warning +``` + +--- + +## 📍 Slide 31 – 📈 Resource Monitoring + +**CPU and Memory queries:** + +```promql +# CPU usage percentage +sum(rate(container_cpu_usage_seconds_total{pod=~"my-app.*"}[5m])) / +sum(kube_pod_container_resource_limits{resource="cpu", pod=~"my-app.*"}) * 100 + +# Memory usage percentage +sum(container_memory_working_set_bytes{pod=~"my-app.*"}) / +sum(kube_pod_container_resource_limits{resource="memory", pod=~"my-app.*"}) * 100 +``` + +**Capacity planning alerts:** +```yaml +- alert: HighMemoryUsage + expr: | + sum(container_memory_working_set_bytes) by (pod) / + sum(kube_pod_container_resource_limits{resource="memory"}) by (pod) > 0.8 + for: 15m +``` + +--- + +## 📍 Slide 32 – 🔔 Alert Fatigue Prevention + +**Problem:** Too many alerts = ignored alerts + +| ❌ Bad Practice | ✅ Better Approach | +|----------------|-------------------| +| Alert on every metric | Alert on symptoms, not causes | +| No severity levels | Critical, warning, info tiers | +| Alert immediately | Use `for` duration | +| Generic messages | Actionable descriptions | +| No runbooks | Link to debugging guides | + +**Good alert structure:** +```yaml +annotations: + summary: "High error rate on {{ $labels.service }}" + description: "Error rate is {{ $value }}% (threshold: 1%)" + runbook_url: "https://wiki/alerts/high-error-rate" +``` + +--- + +## 📍 Slide 33 – 📊 Dashboard Best Practices + +**Effective dashboard layout:** + +``` +┌─────────────────────────────────────────┐ +│ 📊 Overview: Key metrics at a glance │ +├─────────────┬─────────────┬─────────────┤ +│ 🔴 Errors │ ⏱️ Latency │ 📈 Traffic │ +├─────────────┴─────────────┴─────────────┤ +│ 💾 Resource Usage (CPU, Memory, Disk) │ +├─────────────────────────────────────────┤ +│ 🔍 Detailed Breakdowns (per pod, etc.) │ +└─────────────────────────────────────────┘ +``` + +**Tips:** +* 📊 Start with high-level, drill down for details +* 🎨 Use consistent colors (red = bad) +* 📝 Add descriptions to panels +* 🔗 Link related dashboards + +--- + +## 📍 Slide 34 – 🏢 Real-World: Observability at Scale + +**Netflix observability approach:** + +* 📊 **Metrics:** Atlas (Prometheus-like, billions of time series) +* 📝 **Logs:** Mantis (real-time stream processing) +* 🔗 **Traces:** Edgar (distributed tracing) +* 🔔 **Alerts:** Focused on customer impact + +**Key lessons:** +* 🎯 Focus on **business metrics** (not just infra) +* 🔄 Automate **remediation** where possible +* 📈 Invest in **dashboards** as a product +* 👥 Make observability **everyone's** job + +--- + +## 📍 Slide 35 – 🎯 Key Takeaways + +1. 🗄️ **StatefulSets** provide stable identity and per-pod storage for databases +2. 🌐 **Headless services** enable direct pod-to-pod communication +3. 📊 **Three pillars:** Metrics, Logs, Traces for full observability +4. 📈 **Prometheus + Grafana** is the standard K8s monitoring stack +5. 🔔 **Alerts should be actionable** — avoid alert fatigue +6. 🎯 **Four Golden Signals:** Latency, Traffic, Errors, Saturation + +> 💬 *"Observability is not about collecting data, it's about understanding your system."* + +--- + +## 📍 Slide 36 – 🧠 Mindset Shift + +| 😰 Old Mindset | 🚀 New Mindset | +|---------------|----------------| +| "It's working, don't touch it" | "I can see it's working" | +| "Let's check the logs" | "The dashboard shows the issue" | +| "User reported an error" | "Alert fired before impact" | +| "Database needs restart" | "DB has stable identity, restart is safe" | +| "Collect all the metrics" | "Monitor what matters" | +| "Alert on everything" | "Alert on symptoms, investigate causes" | + +> 🤔 **Question:** What's the first dashboard you'd build for your app? + +--- + +## 📍 Slide 37 – 📝 QUIZ — DEVOPS_L15_POST + +--- + +## 📍 Slide 38 – 🚀 What's Next? + +**Coming up: Lecture 16 — Beyond Kubernetes** + +```mermaid +flowchart LR + A[☸️ Kubernetes] --> B[✈️ Fly.io] + A --> C[🌐 IPFS/4EVERLAND] + B --> D[🌍 Global Edge] + C --> E[🔗 Decentralized] +``` + +* ✈️ **Fly.io:** Edge deployment simplified +* 🌐 **IPFS:** Decentralized hosting +* 🤔 **When to use what:** Trade-offs and decisions +* 🎯 **Beyond the cluster:** Alternative deployment models + +> 🎯 **Labs 15 & 16:** Convert your app to StatefulSet and set up comprehensive monitoring! + +--- + +## 📚 Resources + +**StatefulSets:** +* 📖 [Kubernetes StatefulSets](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) +* 📖 [Headless Services](https://kubernetes.io/docs/concepts/services-networking/service/#headless-services) + +**Observability:** +* 📖 [Prometheus Docs](https://prometheus.io/docs/) +* 📖 [Grafana Docs](https://grafana.com/docs/) +* 📖 [kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack) + +**Books:** +* 📕 *Observability Engineering* by Charity Majors, et al. +* 📕 *Site Reliability Engineering* by Google +* 📕 *Kubernetes Patterns* by Bilgin Ibryam & Roland Huß diff --git a/lectures/lec16.md b/lectures/lec16.md new file mode 100644 index 0000000000..2f8bf01d5b --- /dev/null +++ b/lectures/lec16.md @@ -0,0 +1,717 @@ +# 📌 Lecture 16 — Beyond Kubernetes: Alternative Deployment Models + +> 🎯 **From cluster management to platform abstraction and decentralized hosting** + +--- + +## 📍 Slide 1 – 🚀 Kubernetes Isn't Always the Answer + +We've mastered Kubernetes. But is it always the right choice? + +* ☸️ **Kubernetes:** Powerful, but complex +* ✈️ **PaaS (Fly.io):** Simple, global, managed +* 🌐 **Decentralized (IPFS):** Permanent, censorship-resistant + +```mermaid +flowchart LR + A[📦 Your App] --> B{What matters most?} + B --> |Control & Scale| C[☸️ Kubernetes] + B --> |Simplicity & Global| D[✈️ Fly.io] + B --> |Permanence & Decentralization| E[🌐 IPFS] +``` + +> 🎯 **Goal:** Understand when to choose each deployment model + +--- + +## 📍 Slide 2 – 📚 Learning Outcomes + +By the end of this lecture, you will: + +| # | 🎯 Outcome | +|---|-----------| +| 1 | ✅ Evaluate **trade-offs** between deployment models | +| 2 | ✅ Deploy applications to **Fly.io** edge network | +| 3 | ✅ Understand **IPFS** and content addressing | +| 4 | ✅ Use **4EVERLAND** for decentralized hosting | +| 5 | ✅ Choose the **right tool** for different use cases | +| 6 | ✅ Appreciate the **evolving cloud landscape** | + +--- + +## 📍 Slide 3 – 🗺️ Lecture Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SECTION 0: Introduction (Slides 1-4) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 PRE QUIZ (Slide 5) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 1: The Complexity Trade-off (Slides 6-10) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 2: Edge Computing with Fly.io (Slides 11-18) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 MID QUIZ (Slide 19) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 3: Decentralized Web & IPFS (Slides 20-28) │ +├─────────────────────────────────────────────────────────────┤ +│ SECTION 4: Choosing Your Path (Slides 29-35) │ +├─────────────────────────────────────────────────────────────┤ +│ 📝 POST QUIZ (Slide 36) │ +├─────────────────────────────────────────────────────────────┤ +│ FINAL: Course Wrap-up (Slide 37) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 📍 Slide 4 – 🤔 The Big Question + +> 💬 *"The best tool is the one that solves your problem with the least unnecessary complexity."* +> — Practical Engineering Wisdom + +**Consider these scenarios:** + +* 🏢 **Enterprise with 500 microservices:** Probably needs Kubernetes +* 🚀 **Startup with 3 developers:** Maybe doesn't need a cluster +* 📰 **News article that must stay online forever:** Decentralized? +* 🌍 **App serving users globally:** Edge deployment? + +> 🤔 **Think:** What does YOUR application actually need? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L16_PRE + +--- + +## 📍 Slide 6 – ⚠️ Section 1: The Kubernetes Tax + +**What Kubernetes requires:** + +```mermaid +flowchart TD + A[☸️ Kubernetes Cluster] --> B[🔧 Cluster Management] + A --> C[🔐 Security Hardening] + A --> D[📊 Monitoring Setup] + A --> E[🔄 Upgrade Planning] + A --> F[💰 Infrastructure Cost] +``` + +* 🔧 **Cluster operations:** Updates, scaling, troubleshooting +* 🧠 **Team expertise:** Steep learning curve +* 💰 **Cost:** Control plane, nodes, load balancers +* ⏱️ **Time:** Setup, maintenance, incident response + +--- + +## 📍 Slide 7 – 🔥 When Kubernetes Is Overkill + +**Signs you might not need Kubernetes:** + +| 🚩 Sign | 📝 Alternative | +|--------|---------------| +| Single application | PaaS (Fly.io, Railway, Render) | +| Small team (1-5 devs) | Managed services | +| Simple deployment needs | Container platforms | +| Cost-sensitive startup | Serverless or PaaS | +| No specialized workloads | Simpler solutions | + +> 💬 *"Don't use Kubernetes to solve problems you don't have."* + +--- + +## 📍 Slide 8 – 📊 The Abstraction Spectrum + +```mermaid +flowchart LR + A[🖥️ Bare Metal] --> B[☁️ VMs/IaaS] + B --> C[☸️ Kubernetes] + C --> D[✈️ PaaS] + D --> E[⚡ Serverless] + E --> F[🌐 Decentralized] + + G[More Control] -.-> A + H[More Abstraction] -.-> F +``` + +| 🎚️ Level | 🔧 You Manage | ✅ Platform Manages | +|----------|--------------|---------------------| +| Bare Metal | Everything | Nothing | +| IaaS (EC2) | OS, runtime, app | Hardware | +| Kubernetes | App, configs | Orchestration | +| PaaS | App code only | Everything else | +| Serverless | Functions | Runtime, scaling | + +--- + +## 📍 Slide 9 – 🎯 Right Tool, Right Job + +**Decision factors:** + +| 📋 Factor | ☸️ K8s | ✈️ PaaS | 🌐 IPFS | +|----------|--------|--------|---------| +| Team size | Large | Small | Varies | +| Control needs | High | Medium | Low | +| Cost at scale | Efficient | Can be expensive | Very low | +| Setup time | Days/weeks | Minutes | Minutes | +| Global distribution | Manual config | Built-in | Inherent | +| Vendor lock-in | Low | Medium | None | + +--- + +## 📍 Slide 10 – 💡 The Emergence of Edge Computing + +**Traditional deployment:** +``` +User (Tokyo) → CDN → US-East Server → Response (200ms) +``` + +**Edge deployment:** +``` +User (Tokyo) → Edge Server (Tokyo) → Response (20ms) +``` + +```mermaid +flowchart TD + A[🌍 User Anywhere] --> B{Edge Network} + B --> C[🗼 Tokyo] + B --> D[🗼 London] + B --> E[🗼 New York] + B --> F[🗼 Sydney] +``` + +* ⚡ **Lower latency:** Code runs closer to users +* 🌍 **Global by default:** No region configuration +* 🔄 **Automatic routing:** Users hit nearest edge + +--- + +## 📍 Slide 11 – ✈️ Section 2: Fly.io - Simplicity Meets Global + +**What is Fly.io?** + +* ✈️ Platform for running apps globally +* 📦 Deploys Docker containers (or builds from source) +* 🌍 Runs in 30+ regions automatically +* 💰 Free tier for small apps + +```mermaid +flowchart LR + A[📦 Your Container] --> B[✈️ Fly.io] + B --> C[🗼 Edge 1] + B --> D[🗼 Edge 2] + B --> E[🗼 Edge 3] + C --> F[👤 Users] + D --> F + E --> F +``` + +--- + +## 📍 Slide 12 – 🛠️ Fly.io Architecture + +**Key concepts:** + +| 🔧 Concept | 📝 Description | +|-----------|---------------| +| **Machine** | A Fly VM running your app | +| **App** | A named collection of Machines | +| **Region** | A geographical location (ams, iad, sin) | +| **Volume** | Persistent storage attached to Machine | +| **Secret** | Encrypted environment variable | + +```toml +# fly.toml +app = "my-app" +primary_region = "ams" + +[http_service] + internal_port = 8080 + force_https = true + auto_stop_machines = true + auto_start_machines = true +``` + +--- + +## 📍 Slide 13 – 🚀 Deploying to Fly.io + +**The entire deployment process:** + +```bash +# 1. Install CLI +curl -L https://fly.io/install.sh | sh + +# 2. Login +fly auth login + +# 3. Launch (creates app + config) +fly launch + +# 4. Deploy +fly deploy + +# 5. Open in browser +fly open +``` + +**That's it!** No cluster, no YAML manifests, no ingress controllers. + +--- + +## 📍 Slide 14 – 🌍 Multi-Region Deployment + +**Adding regions:** + +```bash +# Add regions +fly regions add iad sin syd + +# Check machines +fly machines list + +# Scale in specific region +fly scale count 2 --region ams +``` + +```mermaid +flowchart TD + A[✈️ Your App] --> B[🇳🇱 Amsterdam] + A --> C[🇺🇸 Virginia] + A --> D[🇸🇬 Singapore] + A --> E[🇦🇺 Sydney] + + F[👤 European User] --> B + G[👤 US User] --> C + H[👤 Asian User] --> D + I[👤 Australian User] --> E +``` + +--- + +## 📍 Slide 15 – 🔐 Secrets & Storage on Fly.io + +**Secrets:** +```bash +fly secrets set DATABASE_URL="postgres://..." +fly secrets set API_KEY="secret123" +fly secrets list +``` + +**Persistent storage:** +```bash +# Create volume +fly volumes create mydata --size 1 --region ams +``` + +```toml +# fly.toml +[mounts] + source = "mydata" + destination = "/data" +``` + +--- + +## 📍 Slide 16 – 📊 Fly.io vs Kubernetes Comparison + +| 📋 Aspect | ☸️ Kubernetes | ✈️ Fly.io | +|----------|--------------|----------| +| Setup time | Hours/days | Minutes | +| Learning curve | Steep | Gentle | +| Global distribution | Manual | Built-in | +| Scaling | HPA, VPA, manual | Auto-scale, simple commands | +| Cost (small app) | $50-100/month | Free tier available | +| Control | Full | Limited | +| Customization | Unlimited | Constrained | +| Multi-cloud | Yes | No (Fly only) | + +--- + +## 📍 Slide 17 – 🎯 When to Choose Fly.io + +**Good fit:** + +* ✅ Small to medium applications +* ✅ Need for low global latency +* ✅ Small team, limited DevOps resources +* ✅ Rapid iteration, quick deployments +* ✅ Cost-conscious early-stage projects + +**Not ideal:** + +* ❌ Complex microservices architectures +* ❌ Need for specific cloud services (AWS RDS, etc.) +* ❌ Compliance requirements for specific regions +* ❌ Already invested heavily in Kubernetes + +--- + +## 📍 Slide 18 – 🔧 Fly.io Best Practices + +| 📋 Practice | 📝 Reason | +|------------|----------| +| Use auto_stop_machines | Save costs when idle | +| Add health checks | Enable auto-restart on failure | +| Use volumes for stateful data | Machines are ephemeral | +| Set min_machines_running | Prevent cold starts | +| Use regions near your users | Optimize latency | + +```toml +[http_service] + auto_stop_machines = true + auto_start_machines = true + min_machines_running = 1 + +[checks] + [checks.health] + type = "http" + port = 8080 + path = "/health" +``` + +--- + +## 📍 Slide 19 – 📝 QUIZ — DEVOPS_L16_MID + +--- + +## 📍 Slide 20 – 🌐 Section 3: IPFS & The Decentralized Web + +**What is IPFS?** + +* 🌐 **InterPlanetary File System** +* 📦 Distributed, peer-to-peer storage +* 🔗 Content-addressed (identified by hash, not location) +* ♾️ Immutable, permanent storage + +```mermaid +flowchart LR + A[📄 File] --> B[#️⃣ Hash] + B --> C[🔗 CID: QmXxx...] + C --> D[🌐 Available Globally] +``` + +> 💡 **Key insight:** Same content = same address, anywhere in the world + +--- + +## 📍 Slide 21 – 🔍 Content Addressing Explained + +**Traditional web (location-based):** +``` +https://server.com/path/to/file.html + ↓ + Server could change content! +``` + +**IPFS (content-based):** +``` +ipfs://QmXxx.../file.html + ↓ + Hash of actual content + If content changes, hash changes! +``` + +| 📋 Aspect | 🌐 HTTP | 🔗 IPFS | +|----------|--------|---------| +| Addressing | Location | Content hash | +| Mutability | Content can change | Content is immutable | +| Availability | Single server | Distributed nodes | +| Censorship | Easy to block | Very difficult | + +--- + +## 📍 Slide 22 – 🔑 IPFS Key Concepts + +| 🔧 Concept | 📝 Description | +|-----------|---------------| +| **CID** | Content Identifier - hash of content | +| **Node** | Computer running IPFS software | +| **Pinning** | Keeping content available (prevent garbage collection) | +| **Gateway** | HTTP bridge to IPFS content | +| **IPNS** | Mutable pointer to IPFS content | + +**Example CIDs:** +``` +QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco +bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi +``` + +--- + +## 📍 Slide 23 – 📌 Pinning Services + +**The persistence problem:** + +```mermaid +flowchart TD + A[📄 Add Content] --> B[🔗 Get CID] + B --> C{Who stores it?} + C --> |Your node| D[🖥️ Goes offline = Content unavailable] + C --> |Pinning service| E[☁️ Always available] +``` + +**Pinning services:** +* 📌 **4EVERLAND** — Web3 hosting platform +* 📌 **Pinata** — IPFS pinning +* 📌 **Infura** — IPFS API +* 📌 **web3.storage** — Free storage + +--- + +## 📍 Slide 24 – 🌐 4EVERLAND Platform + +**What is 4EVERLAND?** + +* 🌐 Web3 infrastructure platform +* 📦 IPFS hosting made simple +* 🔧 Deploy from Git (like Vercel/Netlify) +* 💰 Free tier available + +**Services:** +* 🚀 **Hosting:** Deploy static sites and SPAs +* 📦 **Bucket:** IPFS storage (like S3) +* 🌐 **Gateway:** Access IPFS content via HTTP + +--- + +## 📍 Slide 25 – 🚀 Deploying to 4EVERLAND + +**Process:** + +```mermaid +flowchart LR + A[📝 Git Push] --> B[🔄 4EVERLAND Build] + B --> C[📦 Upload to IPFS] + C --> D[🔗 Get CID] + D --> E[🌐 Available via Gateway] +``` + +**Steps:** +1. 🔗 Connect GitHub repository +2. ⚙️ Configure build settings +3. 🚀 Deploy +4. 🔗 Access via CID or custom domain + +**URLs:** +* `https://your-project.4everland.app` +* `https://ipfs.4everland.link/ipfs/CID` + +--- + +## 📍 Slide 26 – 🔄 IPNS: Mutable Pointers + +**Problem:** CID changes when content changes + +**Solution:** IPNS (InterPlanetary Name System) + +```mermaid +flowchart LR + A[🔑 IPNS Name] --> B[🔗 CID v1] + A --> |Update| C[🔗 CID v2] +``` + +| 📋 Type | 🔗 Address | 📝 Behavior | +|--------|-----------|------------| +| **IPFS** | `/ipfs/QmXxx` | Always same content | +| **IPNS** | `/ipns/k51xxx` | Points to current version | + +**4EVERLAND handles this:** Your URL stays the same, content updates automatically + +--- + +## 📍 Slide 27 – 📊 Centralized vs Decentralized + +| 📋 Aspect | 🏢 Traditional | 🌐 IPFS/4EVERLAND | +|----------|---------------|-------------------| +| Single point of failure | Yes | No | +| Censorship resistance | Low | High | +| Content integrity | Trust server | Cryptographic verification | +| Hosting cost | Ongoing | Pin once, available forever | +| Update mechanism | Overwrite file | New CID (or IPNS) | +| Speed | Fast (CDN) | Variable (depends on nodes) | +| Best for | Dynamic apps | Static content, archives | + +--- + +## 📍 Slide 28 – 🎯 When to Choose IPFS/4EVERLAND + +**Good fit:** + +* ✅ Static websites and documentation +* ✅ Content that must survive (archives, important documents) +* ✅ Censorship-resistant publishing +* ✅ NFT metadata and assets +* ✅ Open source project hosting + +**Not ideal:** + +* ❌ Dynamic server-side applications +* ❌ Real-time updates needed +* ❌ Private content (IPFS is public by default) +* ❌ High-performance requirements + +--- + +## 📍 Slide 29 – 🎯 Section 4: Making the Right Choice + +**Decision Framework:** + +```mermaid +flowchart TD + A[📦 Your Application] --> B{Need dynamic backend?} + B --> |Yes| C{Team size?} + B --> |No, static| D{Permanence important?} + + C --> |Large, experienced| E[☸️ Kubernetes] + C --> |Small| F{Global latency critical?} + + F --> |Yes| G[✈️ Fly.io] + F --> |No| H[Simple hosting] + + D --> |Yes| I[🌐 IPFS/4EVERLAND] + D --> |No| J[Static hosting CDN] +``` + +--- + +## 📍 Slide 30 – 📊 Summary Comparison + +| 📋 Criteria | ☸️ Kubernetes | ✈️ Fly.io | 🌐 4EVERLAND/IPFS | +|------------|--------------|----------|-------------------| +| **Complexity** | High | Low | Low | +| **Control** | Full | Medium | Limited | +| **Scalability** | Unlimited | Good | N/A (static) | +| **Global distribution** | Manual | Automatic | Inherent | +| **Cost at scale** | Efficient | Can be expensive | Very low | +| **Learning curve** | Steep | Gentle | Minimal | +| **Use case** | Microservices, enterprise | Global apps, startups | Static content, Web3 | + +--- + +## 📍 Slide 31 – 🏢 Real-World Examples + +**Kubernetes users:** +* 🏢 **Spotify:** 200+ microservices +* 🏢 **Pinterest:** ML workloads +* 🏢 **Airbnb:** Multi-region deployments + +**Fly.io users:** +* 🚀 **Small startups:** Quick global deployment +* 🎮 **Game backends:** Low-latency requirements +* 🛠️ **Developer tools:** API services + +**IPFS/Decentralized:** +* 📚 **Wikipedia mirror:** Censorship-resistant access +* 🎨 **NFT projects:** Metadata storage +* 📰 **News archives:** Permanent preservation + +--- + +## 📍 Slide 32 – 🔮 The Future of Deployment + +**Trends to watch:** + +| 🔮 Trend | 📝 Description | +|---------|---------------| +| **Edge computing** | Code runs closer to users | +| **WebAssembly** | Run any language at the edge | +| **Decentralization** | Web3 infrastructure growth | +| **Platform abstraction** | Less infra management | +| **AI-assisted DevOps** | Automated operations | + +> 💬 *"The cloud is just someone else's computer. The edge is everyone's computer."* + +--- + +## 📍 Slide 33 – 📋 Practical Recommendations + +**For students and learning:** +1. 🎓 Master Kubernetes fundamentals first +2. ✈️ Try Fly.io for personal projects +3. 🌐 Experiment with IPFS for static sites + +**For production decisions:** +1. 📋 Start with requirements, not technology +2. 📊 Consider team capabilities +3. 💰 Factor in total cost (including time) +4. 🔄 Plan for evolution + +--- + +## 📍 Slide 34 – 🎯 Key Takeaways + +1. ☸️ **Kubernetes is powerful** but comes with complexity costs +2. ✈️ **Fly.io offers simplicity** for global, low-latency applications +3. 🌐 **IPFS provides permanence** and censorship resistance +4. 🎯 **No single best solution** — choose based on requirements +5. 📊 **Consider the trade-offs:** control vs simplicity, cost vs features +6. 🔮 **The landscape evolves** — stay curious, keep learning + +> 💬 *"The best architecture is the one your team can operate successfully."* + +--- + +## 📍 Slide 35 – 🧠 Course Mindset Shift + +| 😰 Before This Course | 🚀 After This Course | +|----------------------|---------------------| +| "How do I deploy this?" | "What's the best deployment model?" | +| "Kubernetes is complicated" | "I understand K8s and its alternatives" | +| "DevOps is ops work" | "DevOps is a culture and practice" | +| "I write code, someone else deploys" | "I can deploy, monitor, and maintain" | +| "Just get it working" | "Make it observable, scalable, reliable" | + +--- + +## 📍 Slide 36 – 📝 QUIZ — DEVOPS_L16_POST + +--- + +## 📍 Slide 37 – 🎓 Course Wrap-up + +**What you've learned:** + +```mermaid +flowchart LR + A[🐳 Docker] --> B[☸️ Kubernetes] + B --> C[🔄 CI/CD] + C --> D[📊 Observability] + D --> E[🔐 Security] + E --> F[🌍 Global Deployment] +``` + +**Your DevOps toolkit:** +* 🐳 **Containerization:** Docker, multi-stage builds +* ☸️ **Orchestration:** Kubernetes, Helm, StatefulSets +* 🔄 **GitOps:** ArgoCD, declarative infrastructure +* 📊 **Observability:** Prometheus, Grafana, alerting +* 🔐 **Security:** Secrets management, Vault +* 🚀 **Progressive delivery:** Canary, blue-green +* 🌍 **Beyond K8s:** Edge computing, decentralized hosting + +> 🎉 **Congratulations!** You're now equipped for production DevOps. + +--- + +## 📚 Resources + +**Fly.io:** +* 📖 [Fly.io Documentation](https://fly.io/docs/) +* 📖 [flyctl Reference](https://fly.io/docs/flyctl/) + +**IPFS & 4EVERLAND:** +* 📖 [IPFS Documentation](https://docs.ipfs.tech/) +* 📖 [4EVERLAND Docs](https://docs.4everland.org/) +* 📖 [IPFS Concepts](https://docs.ipfs.tech/concepts/) + +**Further reading:** +* 📕 *The DevOps Handbook* by Gene Kim, et al. +* 📕 *Accelerate* by Nicole Forsgren, et al. +* 📕 *Site Reliability Engineering* by Google + +**Keep learning:** +* 🌐 [CNCF Landscape](https://landscape.cncf.io/) +* 🌐 [DevOps Roadmap](https://roadmap.sh/devops) diff --git a/lectures/lec2.md b/lectures/lec2.md new file mode 100644 index 0000000000..46a3e0485a --- /dev/null +++ b/lectures/lec2.md @@ -0,0 +1,1053 @@ +# 📌 Lecture 2 — Containerization with Docker: From "Works on My Machine" to Works Everywhere + +## 📍 Slide 1 – 🐳 Welcome to Containerization + +* 🌍 **"Works on my machine"** — the most expensive phrase in software +* 📦 **Containers** = package your app + all dependencies together +* 🚀 **Docker** = the tool that made containers mainstream +* 🎯 This lecture: build production-ready containers from scratch + +```mermaid +flowchart LR + Problem[😰 Works on My Machine] -->|Docker| Solution[🐳 Works Everywhere] + Solution --> Value[💎 Consistent Deployments] +``` + +--- + +## 📍 Slide 2 – 🎯 Learning Outcomes + +* ✅ Understand containers vs VMs and why containers win +* ✅ Write production-ready Dockerfiles +* ✅ Apply security best practices (rootless, distroless) +* ✅ Optimize images with multi-stage builds +* ✅ Publish images to Docker Hub + +**🎓 By the end of this lecture:** + +| # | 🎯 Outcome | +|---|-----------| +| 1 | 🧠 Explain container architecture and benefits | +| 2 | 📝 Write optimized, secure Dockerfiles | +| 3 | 🔐 Implement rootless containers | +| 4 | 📦 Use multi-stage builds for smaller images | +| 5 | 🚀 Push/pull images from Docker Hub | + +--- + +## 📍 Slide 3 – 📋 Lecture Overview + +* 📚 **Concepts + Diagrams** — how containers work +* 🛠️ **Dockerfile deep dive** — instructions and best practices +* 🔐 **Security patterns** — rootless and distroless +* 📦 **Optimization** — multi-stage builds +* 🌐 **Registry workflow** — Docker Hub + +**⏱️ Lecture Structure:** +``` +Section 0: Introduction → 📝 PRE Quiz +Section 1: The Dependency Problem +Section 2: Container Fundamentals +Section 3: Dockerfile Scenarios → 📝 MID Quiz +Section 4: Advanced Patterns +Section 5: Real World Usage +Section 6: Reflection → 📝 POST Quiz +``` + +--- + +## 📍 Slide 4 – ❓ The Big Question + +* 📊 **65%** of organizations use containers in production (2024) +* 🐳 **Docker Hub**: 14+ million images, 13+ billion pulls/month +* 💥 Yet most Dockerfiles have **security vulnerabilities** + +> 💬 *"Containers are the new deployment unit"* — Kelsey Hightower + +**🤔 Think about it:** +* Why do apps work locally but fail in production? +* What's inside a container that makes it portable? +* How small can a container image be? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L2_PRE + +--- + +## 📍 Slide 6 – 🔥 Section 1: The Dependency Problem + +* 👨‍💻 **Developer**: "It works on my machine!" +* ⚙️ **Ops**: "Well, we're not shipping your machine!" +* 🧩 **The real problem**: dependencies, versions, configurations +* 💥 **Result**: deployment failures, debugging nightmares + +```mermaid +flowchart LR + Dev[👨‍💻 Dev Machine] -->|Different| Prod[🌐 Production] + Dev -->|Python 3.11| V1[📦 Version] + Prod -->|Python 3.9| V2[📦 Version] + V1 -.->|💥 Conflict| V2 +``` + +--- + +## 📍 Slide 7 – 🧩 The Dependency Hell + +* 🐍 **Python version**: 3.9 vs 3.11 vs 3.12 +* 📚 **Library versions**: requests 2.28 vs 2.31 +* 🖥️ **OS differences**: Ubuntu vs Alpine vs macOS +* ⚙️ **System libraries**: OpenSSL, libffi, glibc + +```mermaid +flowchart TD + App[📱 Your App] --> Py[🐍 Python 3.11] + App --> Lib1[📚 Flask 2.3] + App --> Lib2[📚 Requests 2.31] + Py --> OS[🖥️ Ubuntu 22.04] + Lib1 --> SSL[🔐 OpenSSL 3.0] + OS --> Kernel[🧠 Linux Kernel] +``` + +> 🤔 **Think:** How many things can go wrong? + +--- + +## 📍 Slide 8 – 😱 The VM Solution (Heavy) + +* 🖥️ **Virtual Machines** = entire OS per application +* 💾 **Size**: 10-50 GB per VM +* ⏱️ **Boot time**: minutes +* 🔧 **Resource overhead**: hypervisor, guest OS kernel + +```mermaid +flowchart TD + subgraph VM1[🖥️ VM 1 - 15GB] + App1[📱 App] --> OS1[🖥️ Full OS] + OS1 --> Kernel1[🧠 Kernel] + end + subgraph VM2[🖥️ VM 2 - 15GB] + App2[📱 App] --> OS2[🖥️ Full OS] + OS2 --> Kernel2[🧠 Kernel] + end + VM1 --> Hyper[⚙️ Hypervisor] + VM2 --> Hyper + Hyper --> Host[🖥️ Host OS] +``` + +**😰 Problems:** +* 🐌 Slow to start +* 💸 Expensive (RAM, CPU, storage) +* 🔧 Hard to manage at scale + +--- + +## 📍 Slide 9 – 🐳 The Container Solution (Light) + +* 📦 **Containers** = isolated processes sharing host kernel +* 💾 **Size**: 5-500 MB typically +* ⏱️ **Start time**: milliseconds +* 🚀 **Density**: 10-100x more containers than VMs + +```mermaid +flowchart TD + subgraph Containers + C1[📦 Container 1 - 50MB] + C2[📦 Container 2 - 50MB] + C3[📦 Container 3 - 50MB] + end + C1 --> Docker[🐳 Docker Engine] + C2 --> Docker + C3 --> Docker + Docker --> Host[🖥️ Host OS + Kernel] +``` + +**🚀 Benefits:** +* ⚡ Start in milliseconds +* 💰 Efficient resource usage +* 📦 Portable across environments + +--- + +## 📍 Slide 10 – 💸 VMs vs Containers + +| 🔍 Aspect | 🖥️ Virtual Machine | 🐳 Container | +|-----------|-------------------|--------------| +| 💾 **Size** | 10-50 GB | 10-500 MB | +| ⏱️ **Boot Time** | Minutes | Milliseconds | +| 🧠 **Kernel** | Own kernel | Shared kernel | +| 🔒 **Isolation** | Strong (hardware) | Process-level | +| 📦 **Density** | 10-20 per host | 100s per host | +| 🎯 **Use Case** | Full OS needed | App deployment | + +**📈 Real Numbers:** +* 🖥️ **VM**: 1 app = ~2GB RAM overhead +* 🐳 **Container**: 1 app = ~50MB overhead +* 🚀 **Result**: 40x more efficient! + +--- + +## 📍 Slide 11 – 📜 History of Containerization + +* 🕰️ **1979**: `chroot` — change root directory (Unix V7) +* 🔒 **2000**: FreeBSD Jails — first true isolation +* 🐧 **2006**: cgroups — Google contributes to Linux kernel +* 📦 **2008**: LXC (Linux Containers) — combines namespaces + cgroups +* 🐳 **2013**: **Docker** — makes containers accessible to everyone +* ☸️ **2014**: Kubernetes — container orchestration at scale +* 📦 **2015**: OCI (Open Container Initiative) — standardization + +```mermaid +flowchart LR + Chroot[🕰️ 1979: chroot] --> Jails[🔒 2000: Jails] + Jails --> Cgroups[🐧 2006: cgroups] + Cgroups --> LXC[📦 2008: LXC] + LXC --> Docker[🐳 2013: Docker] + Docker --> K8s[☸️ 2014: K8s] +``` + +> 💡 Docker didn't invent containers — it made them **usable**. + +--- + +## 📍 Slide 12 – 🐧 Linux Kernel: Namespaces + +* 🎯 **Namespaces** = isolate what a process **can see** +* 🔒 Each container gets its own "view" of the system + +| 🏷️ Namespace | 🔒 Isolates | 📝 Example | +|--------------|------------|-----------| +| **PID** | Process IDs | Container sees PID 1 as its init | +| **NET** | Network stack | Own IP, ports, routing | +| **MNT** | Mount points | Own filesystem view | +| **UTS** | Hostname | Own hostname | +| **IPC** | Inter-process comm | Own message queues | +| **USER** | User/Group IDs | UID 0 in container ≠ root on host | + +```mermaid +flowchart TD + subgraph Host[🖥️ Host System] + subgraph NS1[📦 Container 1 Namespace] + P1[PID 1: app] + Net1[eth0: 172.17.0.2] + end + subgraph NS2[📦 Container 2 Namespace] + P2[PID 1: app] + Net2[eth0: 172.17.0.3] + end + end +``` + +--- + +## 📍 Slide 13 – 🎛️ Linux Kernel: cgroups + +* 🎯 **cgroups** (Control Groups) = limit what a process **can use** +* 📊 Resource limits prevent one container from killing the host + +| 🎛️ cgroup | 🔧 Controls | 📝 Example | +|-----------|------------|-----------| +| **cpu** | CPU time | Max 50% of one core | +| **memory** | RAM usage | Max 512MB | +| **blkio** | Disk I/O | Max 100MB/s read | +| **pids** | Process count | Max 100 processes | + +```mermaid +flowchart LR + Container[🐳 Container] --> Cgroups[🎛️ cgroups] + Cgroups --> CPU[🖥️ CPU: 50%] + Cgroups --> RAM[💾 RAM: 512MB] + Cgroups --> IO[💿 I/O: 100MB/s] +``` + +**🛡️ Why it matters:** +* ✅ Prevent runaway processes +* ✅ Fair resource sharing +* ✅ Predictable performance + +--- + +## 📍 Slide 14 – 📂 Linux Kernel: Union Filesystems + +* 🎯 **Union FS** = layer multiple filesystems as one +* 📚 Docker uses **overlay2** (default on Linux) +* 💾 Layers are **read-only**, changes go to top layer + +```mermaid +flowchart TD + subgraph Image[📦 Image Layers - Read Only] + L1[🐧 Layer 1: Base OS] + L2[📦 Layer 2: Dependencies] + L3[📁 Layer 3: App Code] + end + subgraph Container[🏃 Container Layer - Read/Write] + L4[✏️ Layer 4: Runtime Changes] + end + L1 --> L2 --> L3 --> L4 +``` + +**💡 Benefits:** +* ✅ **Shared layers** — 10 containers can share base image +* ✅ **Fast startup** — no copying, just add thin layer +* ✅ **Efficient storage** — only differences stored + +--- + +## 📍 Slide 15 – 🧩 How It All Fits Together + +```mermaid +flowchart TD + subgraph Docker[🐳 Docker Engine] + CLI[🖥️ Docker CLI] + Daemon[⚙️ dockerd] + Containerd[📦 containerd] + Runc[🏃 runc] + end + subgraph Kernel[🐧 Linux Kernel] + NS[🔒 Namespaces] + CG[🎛️ cgroups] + UFS[📂 overlay2] + end + CLI --> Daemon --> Containerd --> Runc + Runc --> NS + Runc --> CG + Runc --> UFS +``` + +**🔧 The Stack:** +* 🖥️ **Docker CLI** — user interface +* ⚙️ **dockerd** — Docker daemon (API) +* 📦 **containerd** — container lifecycle management +* 🏃 **runc** — OCI runtime (creates containers) +* 🐧 **Kernel** — namespaces + cgroups + filesystem + +--- + +## 📍 Slide 16 – 💡 Section 2: Docker Fundamentals + +* 🐳 **Docker** = platform for building, shipping, running containers +* 📦 **Image** = blueprint (read-only template) +* 🏃 **Container** = running instance of an image +* 📝 **Dockerfile** = recipe to build an image + +```mermaid +flowchart LR + Dockerfile[📝 Dockerfile] -->|build| Image[📦 Image] + Image -->|run| Container[🏃 Container] + Image -->|push| Registry[🌐 Registry] + Registry -->|pull| Image2[📦 Image] +``` + +**📖 Definition:** +> *A container is a standard unit of software that packages code and all its dependencies so the application runs quickly and reliably across environments.* + +--- + +## 📍 Slide 17 – 🏗️ Docker Architecture + +* 🖥️ **Docker Client** = CLI commands (`docker build`, `docker run`) +* ⚙️ **Docker Daemon** = background service managing containers +* 📦 **Images** = layered filesystem snapshots +* 🌐 **Registry** = image storage (Docker Hub, ECR, GCR) + +```mermaid +flowchart LR + CLI[🖥️ Docker CLI] -->|API| Daemon[⚙️ Docker Daemon] + Daemon --> Images[📦 Images] + Daemon --> Containers[🏃 Containers] + Daemon <-->|push/pull| Registry[🌐 Registry] +``` + +**🔧 Key Commands:** +* 🔨 `docker build` — create image from Dockerfile +* 🏃 `docker run` — start container from image +* 📤 `docker push` — upload image to registry +* 📥 `docker pull` — download image from registry + +--- + +## 📍 Slide 18 – 📚 Image Layers + +* 🎂 **Images are layered** = each instruction creates a layer +* 💾 **Layers are cached** = faster rebuilds +* 🔄 **Layers are shared** = efficient storage +* 📝 **Order matters** = for cache efficiency + +```mermaid +flowchart TD + L1[🐧 Layer 1: Base OS - python:3.12-slim] + L2[📦 Layer 2: Install dependencies] + L3[📁 Layer 3: Copy application code] + L4[⚙️ Layer 4: Configure runtime] + L1 --> L2 --> L3 --> L4 + L4 --> Image[📦 Final Image] +``` + +**💡 Key Insight:** +* ✅ Change code → only Layer 3-4 rebuild +* ❌ Change base → ALL layers rebuild + +--- + +## 📍 Slide 19 – 📝 Dockerfile Basics + +```dockerfile +# 🐍 Start from base image +FROM python:3.12-slim + +# 📁 Set working directory +WORKDIR /app + +# 📦 Copy and install dependencies FIRST (caching!) +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# 📁 Copy application code +COPY . . + +# 🚀 Define startup command +CMD ["python", "app.py"] +``` + +**📝 Key Instructions:** +| Instruction | 🎯 Purpose | +|-------------|-----------| +| `FROM` | 🐧 Base image | +| `WORKDIR` | 📁 Set directory | +| `COPY` | 📄 Copy files | +| `RUN` | ⚙️ Execute commands | +| `CMD` | 🚀 Default command | +| `EXPOSE` | 🔌 Document port | + +--- + +## 📍 Slide 20 – ⚡ Before vs After Docker + +| 😰 Before Docker | 🐳 After Docker | +|-----------------|-----------------| +| 📋 Manual server setup | 📝 Dockerfile defines everything | +| 🔧 "Install Python 3.11, then..." | 🐳 `FROM python:3.11` | +| 😱 "Works on my machine" | ✅ Works everywhere | +| 📅 Deploy monthly (scary) | 🚀 Deploy daily (confident) | +| 🐛 "Which version is prod?" | 📦 Image tag = version | +| 💀 Snowflake servers | 🐄 Immutable containers | + +> 🤔 Which column describes your current workflow? + +--- + +## 📍 Slide 21 – 🎮 Section 3: Dockerfile Scenarios + +## 🕹️ Lab Preview: Containerize Your App + +* 🏢 **Scenario**: You have a Python Flask app from Lab 1 +* 🎯 **Goal**: Package it in a production-ready container +* 📋 **Requirements**: Security, optimization, best practices + +**❓ What could go wrong?** + +> 💀 **A lot.** Let's see common mistakes and fixes. + +🎮 **Let's build it right.** + +--- + +## 📍 Slide 22 – 💥 Scenario 1: Running as Root + +**😰 The Problem:** +```dockerfile +FROM python:3.12 +COPY . /app +CMD ["python", "app.py"] +# 💀 Running as root by default! +``` + +* 🔓 Container runs as **root** (UID 0) +* 💥 If attacker escapes container → **root on host** +* 🚨 Kubernetes blocks root containers by default + +```mermaid +flowchart LR + Attack[🔓 Container Escape] --> Root[👑 Root Access] + Root --> Host[💀 Host Compromised] +``` + +> ❓ **Why is this dangerous?** + +--- + +## 📍 Slide 23 – ✅ Solution: Rootless Containers + +## 🛠️ Fix: Create Non-Root User + +```dockerfile +FROM python:3.12-slim + +# 👤 Create non-root user +RUN useradd --create-home --shell /bin/bash appuser + +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +COPY . . + +# 🔒 Switch to non-root user +USER appuser + +CMD ["python", "app.py"] +``` + +**🎯 Result:** Container runs as `appuser`, not root + +**🔐 Security Benefits:** +* ✅ Limited privileges inside container +* ✅ Can't modify system files +* ✅ Container escape = unprivileged user +* ✅ Kubernetes-compatible + +--- + +## 📍 Slide 24 – 🐌 Scenario 2: Slow Builds (Bad Layer Order) + +**😰 The Problem:** +```dockerfile +FROM python:3.12-slim +WORKDIR /app + +# ❌ Copy EVERYTHING first +COPY . . + +# 📦 Then install dependencies +RUN pip install -r requirements.txt + +CMD ["python", "app.py"] +``` + +* 🔄 **Any code change** → reinstall ALL dependencies +* ⏱️ Build time: **5 minutes** every time +* 💸 Wasted CI/CD minutes + +```mermaid +flowchart TD + Change[📝 Change 1 line of code] --> Copy[❌ COPY invalidated] + Copy --> Pip[❌ pip install runs again] + Pip --> Slow[🐌 5 min rebuild] +``` + +--- + +## 📍 Slide 25 – ✅ Solution: Optimized Layer Order + +## 🛠️ Fix: Dependencies Before Code + +```dockerfile +FROM python:3.12-slim +WORKDIR /app + +# 📦 Copy ONLY requirements first +COPY requirements.txt . + +# 📦 Install dependencies (cached if requirements unchanged) +RUN pip install --no-cache-dir -r requirements.txt + +# 📁 THEN copy application code +COPY . . + +CMD ["python", "app.py"] +``` + +**🎯 Result:** Change code → only last layer rebuilds + +```mermaid +flowchart TD + Change[📝 Change code] --> Skip1[✅ FROM cached] + Skip1 --> Skip2[✅ requirements cached] + Skip2 --> Skip3[✅ pip install cached] + Skip3 --> Rebuild[🔨 Only COPY . . rebuilds] + Rebuild --> Fast[⚡ 10 sec rebuild] +``` + +**⚡ Build time: 5 min → 10 sec** + +--- + +## 📍 Slide 26 – 📦 Scenario 3: Bloated Images + +**😰 The Problem:** +```dockerfile +FROM python:3.12 +# 💾 Full Python image = 1.0 GB! +``` + +* 💾 Image size: **1+ GB** +* 🐌 Slow to pull/push +* 💸 Storage costs +* 🔓 Larger attack surface + +**📊 Python Image Sizes:** +| Image | 💾 Size | +|-------|--------| +| `python:3.12` | 1.0 GB | +| `python:3.12-slim` | 150 MB | +| `python:3.12-alpine` | 50 MB | + +> 🤔 **Do you need the full image?** + +--- + +## 📍 Slide 27 – ✅ Solution: Slim Base Images + +## 🛠️ Fix: Use Minimal Base Images + +```dockerfile +# ✅ Use slim variant +FROM python:3.12-slim + +# ✅ No cache for pip (smaller image) +RUN pip install --no-cache-dir -r requirements.txt + +# ✅ Only copy what's needed +COPY app.py . +COPY templates/ templates/ +``` + +**🎯 Result:** 1 GB → 150 MB (85% reduction!) + +**📦 Base Image Guide:** +| Image Type | 🎯 Use Case | 💾 Size | +|------------|------------|--------| +| `python:3.12` | Need compilation tools | 1.0 GB | +| `python:3.12-slim` | Most apps (recommended) | 150 MB | +| `python:3.12-alpine` | Size-critical, simple apps | 50 MB | + +**⚠️ Alpine Warning:** Uses musl libc, may break some packages + +--- + +## 📍 Slide 28 – 📁 Scenario 4: No .dockerignore + +**😰 The Problem:** +```bash +# Build context includes EVERYTHING +Sending build context to Docker daemon 500MB +``` + +* 📁 `.git/` folder (100+ MB) +* 📁 `node_modules/` or `venv/` +* 📁 `__pycache__/` files +* 📄 `.env` with secrets! 💀 + +**💥 Consequences:** +* 🐌 Slow builds +* 💾 Bloated images +* 🔓 Secrets leaked into image + +--- + +## 📍 Slide 29 – ✅ Solution: .dockerignore + +## 🛠️ Fix: Exclude Unnecessary Files + +```dockerignore +# 🐙 Version control +.git +.gitignore + +# 🐍 Python +__pycache__ +*.pyc +*.pyo +venv/ +.venv/ + +# 🔐 Secrets (NEVER include!) +.env +*.pem +secrets/ + +# 📝 Documentation +*.md +docs/ + +# 🧪 Tests (if not needed in container) +tests/ +``` + +**🎯 Result:** +* ⚡ Build context: 500 MB → 5 MB +* 🔐 No secrets in image +* 🚀 Faster builds + +--- + +## 📍 Slide 30 – 📝 QUIZ — DEVOPS_L2_MID + +--- + +## 📍 Slide 31 – 🚀 Section 4: Advanced Patterns + +## 🏗️ Multi-Stage Builds + +* 🎯 **Problem**: Build tools bloat final image +* 💡 **Solution**: Separate build and runtime stages +* 📦 **Result**: Tiny production images + +```mermaid +flowchart LR + subgraph Stage1[🔨 Builder Stage] + SDK[📦 Full SDK] + Compile[⚙️ Compile] + end + subgraph Stage2[🚀 Runtime Stage] + Binary[📦 Binary Only] + Minimal[🐧 Minimal OS] + end + Stage1 -->|copy binary| Stage2 +``` + +**📊 Size Impact:** +* 🔨 Builder: 1+ GB (SDK, compilers) +* 🚀 Runtime: 10-50 MB (binary only) + +--- + +## 📍 Slide 32 – 📝 Multi-Stage Dockerfile + +```dockerfile +# 🔨 Stage 1: Builder +FROM golang:1.21 AS builder +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 go build -o myapp + +# 🚀 Stage 2: Runtime +FROM alpine:3.18 +RUN adduser -D appuser +WORKDIR /app +COPY --from=builder /app/myapp . +USER appuser +CMD ["./myapp"] +``` + +**🔍 Key Points:** +* 🏷️ `AS builder` — name the stage +* 📦 `COPY --from=builder` — copy from previous stage +* 🗑️ Builder stage discarded in final image + +**📊 Result:** 1.2 GB → 15 MB + +--- + +## 📍 Slide 33 – 🔐 Distroless Images + +## 🛡️ Ultimate Minimal Images + +* 🚫 **No shell** — can't exec into container +* 🚫 **No package manager** — can't install malware +* 🚫 **No unnecessary files** — minimal attack surface +* ✅ **Only your app** — and runtime dependencies + +```dockerfile +# 🔨 Build stage +FROM golang:1.21 AS builder +WORKDIR /app +COPY . . +RUN CGO_ENABLED=0 go build -o myapp + +# 🔐 Distroless runtime +FROM gcr.io/distroless/static-debian12 +COPY --from=builder /app/myapp / +CMD ["/myapp"] +``` + +**📊 Distroless Options:** +| Image | 🎯 For | 💾 Size | +|-------|-------|--------| +| `distroless/static` | Go, Rust (static) | 2 MB | +| `distroless/base` | C/C++ apps | 20 MB | +| `distroless/python3` | Python apps | 50 MB | +| `distroless/java` | Java apps | 190 MB | + +--- + +## 📍 Slide 34 – 📊 Image Size Comparison + +## 📈 Same App, Different Images + +| 🏗️ Build Strategy | 💾 Image Size | 🔐 Security | +|-------------------|--------------|-------------| +| `FROM python:3.12` | 1.0 GB | 😰 Large attack surface | +| `FROM python:3.12-slim` | 150 MB | 😊 Better | +| Multi-stage + slim | 100 MB | 😄 Good | +| Multi-stage + alpine | 50 MB | 😄 Good | +| Multi-stage + distroless | 20 MB | 🔐 Excellent | +| `FROM scratch` (Go) | 5 MB | 🔐 Maximum | + +```mermaid +flowchart LR + Full[📦 1 GB] --> Slim[📦 150 MB] + Slim --> Multi[📦 50 MB] + Multi --> Distroless[📦 20 MB] + Distroless --> Scratch[📦 5 MB] +``` + +**🎯 Goal:** As small as possible while functional + +--- + +## 📍 Slide 35 – 🌐 Docker Hub & Registries + +## 📦 Publishing Your Images + +```mermaid +flowchart LR + Build[🔨 Build] --> Tag[🏷️ Tag] + Tag --> Push[📤 Push] + Push --> Registry[🌐 Docker Hub] + Registry --> Pull[📥 Pull] + Pull --> Run[🏃 Run] +``` + +**🔧 Workflow:** +```bash +# 🔨 Build image +docker build -t myapp:1.0 . + +# 🏷️ Tag for registry +docker tag myapp:1.0 username/myapp:1.0 + +# 🔐 Login to Docker Hub +docker login + +# 📤 Push to registry +docker push username/myapp:1.0 +``` + +**📦 Registries:** +* 🐳 Docker Hub — public/private +* ☁️ AWS ECR — AWS integrated +* 🌐 GCP GCR — Google integrated +* 🦊 GitLab Registry — GitLab integrated + +--- + +## 📍 Slide 36 – 🏢 Section 5: Real World Usage + +## 📅 Docker in Production + +**🔨 Build Phase:** +* 📝 Dockerfile in repo +* 🤖 CI builds image on every commit +* 🏷️ Tag with git SHA or semantic version +* 📤 Push to registry + +**🚀 Deploy Phase:** +* 📥 Pull image to servers +* 🏃 Run containers +* 📊 Monitor health +* 🔄 Rolling updates + +```mermaid +flowchart LR + Code[📝 Code] --> CI[🤖 CI Build] + CI --> Registry[🌐 Registry] + Registry --> K8s[☸️ Kubernetes] + K8s --> Prod[🌐 Production] +``` + +--- + +## 📍 Slide 37 – 🏷️ Tagging Strategies + +| 🏷️ Strategy | 📝 Example | 🎯 Use Case | +|-------------|-----------|-------------| +| **Semantic** | `myapp:1.2.3` | Releases | +| **Git SHA** | `myapp:a1b2c3d` | Traceability | +| **Branch** | `myapp:develop` | Dev environments | +| **Latest** | `myapp:latest` | ⚠️ Avoid in prod! | +| **Date** | `myapp:2024-01-15` | Daily builds | + +**⚠️ Never use `latest` in production:** +* 🤷 Which version is "latest"? +* 🔄 Changes without notice +* 🐛 Can't rollback reliably + +**✅ Best Practice:** +```bash +# 🏷️ Immutable tags +docker tag myapp:1.0.0 registry/myapp:1.0.0 +docker tag myapp:1.0.0 registry/myapp:sha-a1b2c3d +``` + +--- + +## 📍 Slide 38 – 🔐 Security Best Practices + +```mermaid +flowchart TD + Scan[🔍 Scan Images] --> Base[📦 Minimal Base] + Base --> User[👤 Non-root User] + User --> Secrets[🔐 No Secrets in Image] + Secrets --> Update[🔄 Update Regularly] + Update --> Sign[✍️ Sign Images] +``` + +**🔐 Security Checklist:** +* ✅ Run as non-root user (`USER appuser`) +* ✅ Use minimal base images (slim, distroless) +* ✅ Scan for vulnerabilities (Trivy, Snyk) +* ✅ Never store secrets in images +* ✅ Pin base image versions +* ✅ Update base images regularly + +**🛠️ Scanning Tools:** +* 🔍 **Trivy** — open source, fast +* 🔍 **Snyk** — developer-friendly +* 🔍 **Docker Scout** — built into Docker + +--- + +## 📍 Slide 39 – 📈 Career Skills + +```mermaid +flowchart LR + Docker[🐳 Docker Basics] --> Compose[📦 Docker Compose] + Compose --> K8s[☸️ Kubernetes] + K8s --> GitOps[🔄 GitOps] + GitOps --> Platform[🏗️ Platform Engineering] +``` + +**🛠️ Docker Skills Progression:** +* 🐳 **Level 1**: Write Dockerfiles, build/run containers +* 📦 **Level 2**: Multi-stage builds, optimization +* 🔐 **Level 3**: Security hardening, distroless +* 📊 **Level 4**: Registry management, scanning +* ☸️ **Level 5**: Container orchestration (K8s) + +**📊 Job Market (2024):** +* 🐳 Docker required in **80%** of DevOps jobs +* ☸️ Kubernetes in **65%** of container jobs +* 💰 Container skills = **+15-20%** salary + +--- + +## 📍 Slide 40 – 🌍 Real Company Examples + +**🎬 Netflix:** +* 🐳 Millions of containers daily +* 📦 Custom base images (hardened) +* 🔄 Immutable deployments + +**🛒 Shopify:** +* 🐳 Containerized entire platform +* ⚡ Deploy 80x/day +* 📦 Standardized Dockerfiles + +**🚗 Uber:** +* 🐳 4,000+ microservices in containers +* 🔐 Strict security policies +* 📊 Custom image scanning + +**📊 Common Patterns:** +* ✅ Standardized base images +* ✅ Automated security scanning +* ✅ Multi-stage builds everywhere +* ✅ No root containers + +--- + +## 📍 Slide 41 – 🎯 Section 6: Reflection + +## 📝 Key Takeaways + +1. 🐳 **Containers = lightweight, portable app packaging** +2. 📝 **Dockerfile order matters** — dependencies before code +3. 👤 **Always run as non-root** — security first +4. 🏗️ **Multi-stage builds** — separate build from runtime +5. 📦 **Smaller is better** — less attack surface, faster deploys + +> 💡 A good Dockerfile is secure, optimized, and maintainable. + +--- + +## 📍 Slide 42 – 🧠 The Mindset Shift + +| 😰 Old Mindset | 🐳 Container Mindset | +|---------------|---------------------| +| 🖥️ "Configure servers manually" | 📝 "Define in Dockerfile" | +| 🔧 "Install dependencies on host" | 📦 "Bundle in container" | +| 👑 "Run as root, it's easier" | 👤 "Run as non-root always" | +| 💾 "Bigger image = more features" | ⚡ "Smaller = faster & safer" | +| 🏷️ "Just use :latest" | 🔖 "Pin versions always" | + +> ❓ Which mindset will you adopt? + +--- + +## 📍 Slide 43 – ✅ Your Progress + +## 🎓 What You Now Understand + +* ✅ Why containers beat VMs for app deployment +* ✅ Docker architecture: images, containers, registries +* ✅ How to write optimized Dockerfiles +* ✅ Security: rootless containers, minimal images +* ✅ Multi-stage builds for smaller images +* ✅ Docker Hub publishing workflow + +> 🚀 **You're ready for Lab 2!** + +--- + +## 📍 Slide 44 – 📝 QUIZ — DEVOPS_L2_POST + +--- + +## 📍 Slide 45 – 🚀 What Comes Next + +## 📚 Lab 2: Containerize Your App + +* 🐳 Write Dockerfile for your Python app +* 👤 Implement non-root user +* 📦 Optimize with layer ordering +* 🌐 Push to Docker Hub +* 🏆 Bonus: Multi-stage build for Go app + +**🔮 Future Lectures:** +* 📦 **Lecture 3**: CI/CD with GitHub Actions +* ☸️ **Lecture 9**: Kubernetes deployment +* 🔄 **Lecture 13**: GitOps with ArgoCD + +```mermaid +flowchart LR + You[👤 You] --> Docker[🐳 Docker Skills] + Docker --> K8s[☸️ Kubernetes] + K8s --> GitOps[🔄 GitOps] + GitOps --> Career[🚀 DevOps Career] +``` + +**👋 See you in the lab!** + +--- + +## 📚 Resources & Further Reading + +**📕 Books:** +* 📖 *Docker Deep Dive* — Nigel Poulton +* 📖 *Container Security* — Liz Rice +* 📖 *Docker in Action* — Jeff Nickoloff + +**🔗 Links:** +* 🌐 [Dockerfile Best Practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) +* 🌐 [Distroless Images](https://github.com/GoogleContainerTools/distroless) +* 🌐 [Docker Security](https://docs.docker.com/engine/security/) +* 🌐 [Multi-Stage Builds](https://docs.docker.com/build/building/multi-stage/) + +**🛠️ Tools:** +* 🔍 [Hadolint](https://github.com/hadolint/hadolint) — Dockerfile linter +* 🔍 [Dive](https://github.com/wagoodman/dive) — Explore image layers +* 🔍 [Trivy](https://github.com/aquasecurity/trivy) — Security scanner + +--- diff --git a/lectures/lec3.md b/lectures/lec3.md new file mode 100644 index 0000000000..9afebb8b15 --- /dev/null +++ b/lectures/lec3.md @@ -0,0 +1,978 @@ +# 📌 Lecture 3 — Continuous Integration: Automate Testing & Build Confidence + +## 📍 Slide 1 – 🤖 Welcome to CI/CD + +* 🐛 **Manual testing** = slow, error-prone, doesn't scale +* 🤖 **Continuous Integration** = automate testing, building, and validation +* ✅ **Goal**: Catch bugs before they reach production +* 🚀 This lecture: Build your first CI/CD pipeline with GitHub Actions + +```mermaid +flowchart LR + Manual[😰 Manual Testing] -->|CI/CD| Auto[🤖 Automated Pipeline] + Auto --> Confidence[💪 Deploy with Confidence] +``` + +--- + +## 📍 Slide 2 – 🎯 Learning Outcomes + +* ✅ Understand CI/CD principles and benefits +* ✅ Write effective unit tests +* ✅ Build GitHub Actions workflows +* ✅ Implement security scanning with Snyk +* ✅ Apply CI/CD best practices (caching, versioning) + +**🎓 By the end of this lecture:** + +| # | 🎯 Outcome | +|---|-----------| +| 1 | 🧠 Explain CI/CD and why it matters | +| 2 | 🧪 Write meaningful unit tests | +| 3 | ⚙️ Create GitHub Actions workflows | +| 4 | 🔐 Integrate security scanning | +| 5 | 📦 Automate Docker builds & publishing | + +--- + +## 📍 Slide 3 – 📋 Lecture Overview + +* 📚 **CI/CD fundamentals** — what, why, how +* 🧪 **Testing strategies** — unit, integration, coverage +* ⚙️ **GitHub Actions** — YAML workflows, actions marketplace +* 🔐 **Security** — Snyk vulnerability scanning +* 🚀 **Automation** — Docker builds, versioning, caching + +**⏱️ Lecture Structure:** +``` +Section 0: Introduction → 📝 PRE Quiz +Section 1: The Testing Problem +Section 2: CI/CD Fundamentals +Section 3: GitHub Actions Hands-on → 📝 MID Quiz +Section 4: Advanced CI Patterns +Section 5: Production Practices +Section 6: Reflection → 📝 POST Quiz +``` + +--- + +## 📍 Slide 4 – ❓ The Big Question + +* 📊 **85%** of software bugs are found in production (2024) +* ⏱️ Average cost to fix a prod bug: **100x** more than dev bug +* 🚀 Teams with good CI deploy **46x** more frequently + +> 💬 *"If it hurts, do it more often"* — Continuous Delivery principle + +**🤔 Think about it:** +* How do you know your code works before deploying? +* What happens when someone breaks the main branch? +* How many bugs could be caught automatically? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L3_PRE + +--- + +## 📍 Slide 6 – 🔥 Section 1: The Testing Problem + +* 👨‍💻 **Developer**: "It works on my machine!" +* 🐛 **Production**: 500 errors, users complaining +* 😰 **The gap**: No automated testing or validation +* 💥 **Result**: Bugs slip through, confidence is low + +```mermaid +flowchart LR + Dev[👨‍💻 Dev: Works!] -->|No Tests| Prod[🌐 Production] + Prod --> Bug[🐛 Bug Found] + Bug --> Fire[🔥 Firefighting] +``` + +--- + +## 📍 Slide 7 – 🧪 Manual Testing Hell + +* 📋 **Manual checklist**: 50 steps to test before deploy +* ⏱️ **Time**: 2 hours per test cycle +* 😴 **Human error**: Forgot to test one endpoint +* 🔄 **Frequency**: Only before big releases (too painful) + +```mermaid +flowchart TD + Code[📝 Write Code] --> Manual[📋 Manual Testing] + Manual --> Bug[🐛 Found Bug] + Bug --> Fix[🔧 Fix Code] + Fix --> Manual + Manual --> Deploy[😮‍💨 Finally Deploy] +``` + +**😰 Problems:** +* 🐌 Slow feedback loop +* 🎰 Testing is inconsistent +* 🧠 Requires human to remember all steps +* 💀 Nobody wants to test + +--- + +## 📍 Slide 8 – 💥 The Integration Problem + +* 👥 **Multiple developers** pushing to main branch +* 🔀 **Merge conflicts** caught too late +* 💥 **Breaking changes** not detected +* 🤷 **"Who broke the build?"** — the blame game + +```mermaid +flowchart LR + Dev1[👨‍💻 Dev 1] -->|Push| Main[🌳 Main Branch] + Dev2[👩‍💻 Dev 2] -->|Push| Main + Dev3[👨‍💻 Dev 3] -->|Push| Main + Main --> Break[💥 Build Broken] +``` + +> 🤔 **Think:** How do we prevent this? + +--- + +## 📍 Slide 9 – 🔐 The Security Gap + +* 📦 **Dependencies** with known vulnerabilities +* 🔓 **Secrets** accidentally committed +* 🚨 **CVEs** discovered after deployment +* 🤷 **Nobody checked** before merging + +**📊 Real Stats:** +* 🔍 **84%** of codebases have vulnerable dependencies +* ⏱️ Average time to detect vulnerability: **54 days** +* 💰 Average breach cost: **$4.45 million** + +--- + +## 📍 Slide 10 – 💸 The Cost of No CI + +| 🔥 Problem | 💥 Impact | +|------------|-----------| +| 🐛 Bugs in production | Customer churn, reputation damage | +| ⏱️ Slow feedback | Wasted development time | +| 😰 Fear of deployment | Infrequent releases | +| 🔒 Security vulnerabilities | Data breaches, compliance issues | + +**📈 Real Numbers:** +* 🐛 Prod bug fix cost: **$10,000 - $100,000** +* 🕒 Time to detect + fix: **4-8 hours** +* 🏢 Without CI: Deploy **monthly** +* 🚀 With CI: Deploy **daily** + +--- + +## 📍 Slide 11 – 💡 Section 2: CI/CD Fundamentals + +* 🤖 **Continuous Integration (CI)** = automatically test every change +* 🚀 **Continuous Delivery (CD)** = always ready to deploy +* 📦 **Continuous Deployment** = automatically deploy to production +* 🎯 **Goal**: Fast, reliable, automated software delivery + +```mermaid +flowchart LR + CI[🤖 CI: Test] --> CD[📦 CD: Package] + CD --> Deploy[🚀 Deploy] +``` + +**📖 Definitions:** +> *CI: Developers integrate code into shared repository frequently. Each integration is verified by automated build and tests.* +> *CD: Software can be released to production at any time.* + +--- + +## 📍 Slide 12 – 🔄 The CI/CD Pipeline + +```mermaid +flowchart LR + Commit[📝 Commit] --> Trigger[⚡ Trigger CI] + Trigger --> Checkout[📥 Checkout Code] + Checkout --> Build[🔨 Build] + Build --> Test[🧪 Test] + Test --> Lint[🔍 Lint] + Lint --> Scan[🔐 Security Scan] + Scan --> Package[📦 Package] + Package --> Publish[🚀 Publish] +``` + +**🔧 Stages:** +1. 📝 **Commit** — Developer pushes code +2. ⚡ **Trigger** — CI system detects change +3. 🔨 **Build** — Compile/prepare code +4. 🧪 **Test** — Run automated tests +5. 🔍 **Lint** — Check code quality +6. 🔐 **Scan** — Security vulnerabilities +7. 📦 **Package** — Build artifacts (Docker image) +8. 🚀 **Publish** — Push to registry + +--- + +## 📍 Slide 13 – ✅ CI/CD Benefits + +| 🎯 Benefit | 📊 Impact | +|-----------|----------| +| ⚡ **Fast Feedback** | Know in 5 min if code works | +| 🐛 **Early Bug Detection** | Catch before production | +| 🔒 **Security** | Automated vulnerability scanning | +| 📦 **Consistent Builds** | Same process every time | +| 💪 **Confidence** | Deploy without fear | +| 🚀 **Faster Releases** | Deploy multiple times per day | + +**📈 DORA Metrics (Elite Performers):** +* 📦 Deploy frequency: **Multiple times/day** +* ⏱️ Lead time: **< 1 hour** +* 🔧 MTTR: **< 1 hour** +* ❌ Change failure rate: **< 15%** + +--- + +## 📍 Slide 14 – 🧪 Testing Pyramid + +```mermaid +flowchart TD + subgraph Pyramid[🔺 Testing Pyramid] + E2E[🌐 E2E Tests
Few, Slow, Expensive] + INT[🔗 Integration Tests
Some, Moderate] + UNIT[🧪 Unit Tests
Many, Fast, Cheap] + end + E2E --> INT --> UNIT +``` + +**🎯 Test Types:** +* 🧪 **Unit Tests** (80%) — Test individual functions +* 🔗 **Integration Tests** (15%) — Test components together +* 🌐 **End-to-End Tests** (5%) — Test full user flows + +**💡 Why the pyramid?** +* ✅ Unit tests: Fast (ms), cheap, catch most bugs +* ✅ Integration: Slower (seconds), catch interface bugs +* ⚠️ E2E: Slowest (minutes), brittle, expensive + +--- + +## 📍 Slide 15 – ⚡ Before vs After CI/CD + +| 😰 Before CI/CD | 🚀 After CI/CD | +|-----------------|----------------| +| 📋 Manual testing checklist | 🤖 Automated test suite | +| 🎰 "Fingers crossed" deploys | ✅ Confident deployments | +| 🐛 Bugs found in production | 🧪 Bugs caught in CI | +| ⏱️ 2 hour test cycle | ⚡ 5 minute feedback | +| 😱 Deploy monthly | 🚀 Deploy daily | +| 🤷 "Who broke it?" | 📊 Git bisect + logs | + +> 🤔 Which column is your current process? + +--- + +## 📍 Slide 16 – 🎮 Section 3: GitHub Actions Hands-On + +## 🕹️ Lab Preview: Build Your CI Pipeline + +* 🏢 **Scenario**: You have a Python Flask app +* 🎯 **Goal**: Automate testing and Docker builds +* 📋 **Requirements**: Tests, lint, security scan, publish + +**❓ How do we automate all this?** + +> 🤖 **GitHub Actions** to the rescue! + +🎮 **Let's build it step by step.** + +--- + +## 📍 Slide 17 – 💥 Scenario 1: No Tests + +**😰 The Problem:** +```python +# app.py +@app.route('/') +def home(): + return {"message": "Hello", "hostname": os.getenv("HOSTNAME")} + +# 🚫 No tests! +``` + +* 📝 Code looks fine +* 💥 Deploy → crashes because `HOSTNAME` is None +* 🐛 Users see 500 errors +* 😱 Rollback emergency + +> ❓ **How do we catch this before deploy?** + +--- + +## 📍 Slide 18 – ✅ Solution: Unit Testing + +## 🛠️ Fix: Write Tests First + +```python +# tests/test_app.py +import pytest +from app import app + +def test_home_endpoint(): + """Test that home returns expected structure""" + client = app.test_client() + response = client.get('/') + + assert response.status_code == 200 + data = response.get_json() + assert "message" in data + assert "hostname" in data + assert isinstance(data["message"], str) + +def test_health_endpoint(): + """Test health check""" + client = app.test_client() + response = client.get('/health') + + assert response.status_code == 200 + assert response.get_json()["status"] == "healthy" +``` + +**🎯 Result:** Tests catch the bug before deploy! + +--- + +## 📍 Slide 19 – 🧪 Testing Frameworks + +## Python Testing Options + +| Framework | 🎯 Pros | ⚠️ Cons | +|-----------|--------|--------| +| **pytest** | Simple, powerful, fixtures | Extra dependency | +| **unittest** | Built-in, no dependencies | Verbose, old-style | + +```bash +# 🧪 pytest (recommended) +pip install pytest +pytest tests/ + +# 🧪 unittest (built-in) +python -m unittest discover tests/ +``` + +**💡 Why pytest?** +* ✅ Simple syntax (`assert` instead of `self.assertEqual`) +* ✅ Powerful fixtures (setup/teardown) +* ✅ Great plugins (coverage, parallel, etc.) +* ✅ Industry standard + +--- + +## 📍 Slide 20 – 📝 Scenario 2: Manual Docker Builds + +**😰 The Problem:** +```bash +# 🐌 Manual process every time +docker build -t myapp:latest . +docker tag myapp:latest username/myapp:v1.2.3 +docker login +docker push username/myapp:v1.2.3 +docker push username/myapp:latest + +# 😱 Forgot to update version tag! +# 💀 Built from wrong branch! +``` + +* ⏱️ Takes 10 minutes +* 🎰 Inconsistent (human error) +* 📋 No validation before build +* 🤷 Can't track what version is deployed + +--- + +## 📍 Slide 21 – ✅ Solution: GitHub Actions CI/CD + +## 🛠️ Fix: Automate Everything + +```yaml +# .github/workflows/python-ci.yml +name: Python CI + +on: + push: + branches: [ main ] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: | + pip install -r requirements.txt + pip install pytest flake8 + + - name: Lint + run: flake8 app.py + + - name: Test + run: pytest tests/ +``` + +**🎯 Result:** Every commit automatically tested! + +--- + +## 📍 Slide 22 – 🐳 Docker Build Automation + +```yaml + docker: + needs: test # ✅ Only run if tests pass + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: ./app_python + push: true + tags: | + ${{ secrets.DOCKER_USERNAME }}/myapp:latest + ${{ secrets.DOCKER_USERNAME }}/myapp:${{ github.sha }} +``` + +**🔐 Security Note:** Never hardcode credentials! + +--- + +## 📍 Slide 23 – 🔓 Scenario 3: Vulnerable Dependencies + +**😰 The Problem:** +```bash +# requirements.txt +flask==2.0.1 # 💀 Known CVE-2023-30861 +requests==2.25.0 # 🔓 Security vulnerability + +# 🤷 Nobody checked before deploying +``` + +* 🔍 **84%** of apps have vulnerable dependencies +* ⏱️ Takes **weeks** to discover +* 💀 Already in production when found + +**📊 Real Example:** +* 📦 Log4Shell (2021) — **35,000+ CVE** +* 💰 Cost to remediate: **Billions of dollars** + +--- + +## 📍 Slide 24 – ✅ Solution: Snyk Security Scanning + +## 🛠️ Fix: Automated Vulnerability Scanning + +```yaml + security: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Run Snyk + uses: snyk/actions/python-3.10@master + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + args: --severity-threshold=high +``` + +**🎯 What Snyk Does:** +* 🔍 Scans dependencies for known CVEs +* 📊 Reports severity (low/medium/high/critical) +* 🔧 Suggests fixes +* ❌ Fails build if critical vulnerabilities found + +**🔐 Result:** Catch vulnerabilities before production! + +--- + +## 📍 Slide 25 – 🐌 Scenario 4: Slow CI Builds + +**😰 The Problem:** +``` +[Run 1] Installing dependencies... 2 minutes +[Run 2] Installing dependencies... 2 minutes +[Run 3] Installing dependencies... 2 minutes +# 💸 Wasting 6 minutes downloading same packages! +``` + +* ⏱️ Each run: **5-10 minutes** +* 🔄 Re-downloading same dependencies +* 💰 Wasting CI minutes (costs money!) +* 😴 Slow feedback loop + +--- + +## 📍 Slide 26 – ✅ Solution: Dependency Caching + +## 🛠️ Fix: Cache Dependencies + +```yaml +- uses: actions/setup-python@v5 + with: + python-version: '3.12' + cache: 'pip' # ✅ Enable caching + cache-dependency-path: 'requirements.txt' + +# Alternative with explicit cache +- uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} +``` + +**📊 Performance Impact:** +* ⏱️ **Before caching**: 5 minutes +* ⚡ **After caching**: 30 seconds +* 🚀 **10x faster!** + +**💡 Cache Key Strategy:** +* 🔑 Key includes `requirements.txt` hash +* 🔄 Cache invalidates when dependencies change +* ✅ Fresh install when needed, cached otherwise + +--- + +## 📍 Slide 27 – 📝 QUIZ — DEVOPS_L3_MID + +--- + +## 📍 Slide 28 – 🏷️ Section 4: Versioning Strategies + +## 📦 How to Version Your Images? + +**Two main approaches:** + +**🔢 Semantic Versioning (SemVer):** +* Format: `MAJOR.MINOR.PATCH` (e.g., `1.2.3`) +* 🎯 Use when: Breaking changes matter +* 📚 Example: Libraries, APIs + +**📅 Calendar Versioning (CalVer):** +* Format: `YYYY.MM.DD` (e.g., `2024.01.15`) +* 🎯 Use when: Continuous deployment +* 🚀 Example: Web services, SaaS + +```mermaid +flowchart LR + Code[📝 Code] --> SemVer[🔢 v1.2.3] + Code --> CalVer[📅 2024.01] + SemVer --> Lib[📚 Library] + CalVer --> Service[🌐 Service] +``` + +--- + +## 📍 Slide 29 – 🔢 Semantic Versioning (SemVer) + +## v MAJOR.MINOR.PATCH + +| Version | 🎯 When to Bump | +|---------|----------------| +| **MAJOR** (v2.0.0) | Breaking changes (API changed) | +| **MINOR** (v1.1.0) | New features (backward-compatible) | +| **PATCH** (v1.0.1) | Bug fixes (backward-compatible) | + +```yaml +# 🏷️ Multiple tags per release +tags: | + username/app:1.2.3 + username/app:1.2 + username/app:1 + username/app:latest +``` + +**✅ Pros:** +* 📖 Clear breaking change signals +* 🎯 Industry standard for libraries +* 🔄 Users can pin to major version + +**⚠️ Cons:** +* 🤔 Requires discipline +* 📋 Need to track what's breaking vs feature + +--- + +## 📍 Slide 30 – 📅 Calendar Versioning (CalVer) + +## YYYY.MM.DD or YYYY.MM + +| Format | 📝 Example | 🎯 Use Case | +|--------|-----------|-------------| +| `YYYY.MM.DD` | `2024.01.15` | Daily releases | +| `YYYY.MM.MICRO` | `2024.01.3` | Monthly + patch | +| `YYYY.0M` | `2024.01` | Monthly releases | + +```yaml +# 📅 Generate version from date +- name: Generate version + run: echo "VERSION=$(date +%Y.%m.%d)" >> $GITHUB_ENV + +tags: | + username/app:2024.01.15 + username/app:2024.01 + username/app:latest +``` + +**✅ Pros:** +* 📆 No ambiguity (date is date) +* 🚀 Perfect for continuous deployment +* 🧠 Easy to remember + +**⚠️ Cons:** +* 🤷 Doesn't indicate breaking changes + +--- + +## 📍 Slide 31 – 🔀 Matrix Builds + +## Test Multiple Versions + +```yaml +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.11', '3.12', '3.13'] + + steps: + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - run: pytest tests/ +``` + +**🎯 What This Does:** +* 🔄 Runs tests **3 times** (one per Python version) +* ⚡ Runs in **parallel** +* ✅ Ensures compatibility across versions + +```mermaid +flowchart LR + Test[🧪 Tests] --> Py311[🐍 Python 3.11] + Test --> Py312[🐍 Python 3.12] + Test --> Py313[🐍 Python 3.13] +``` + +--- + +## 📍 Slide 32 – 📂 Path Filters (Monorepo) + +## Only Run CI for Changed Apps + +```yaml +# Python CI only runs when Python code changes +on: + push: + paths: + - 'app_python/**' + - '.github/workflows/python-ci.yml' + +# Go CI only runs when Go code changes +on: + push: + paths: + - 'app_go/**' + - '.github/workflows/go-ci.yml' +``` + +**🎯 Benefits:** +* ⚡ Faster CI (don't run unnecessary builds) +* 💰 Save CI minutes +* 🔕 Less noise (only relevant notifications) + +**📊 Impact:** +* 🐌 Without filters: Every commit runs **all** CI +* 🚀 With filters: Only **affected** apps run + +--- + +## 📍 Slide 33 – 📊 Test Coverage + +## Measure What's Tested + +```yaml +- name: Run tests with coverage + run: | + pip install pytest-cov + pytest --cov=app_python --cov-report=xml --cov-report=term + +- name: Upload to Codecov + uses: codecov/codecov-action@v4 + with: + file: ./coverage.xml +``` + +**📊 Coverage Badge:** +```markdown +![Coverage](https://codecov.io/gh/user/repo/branch/main/graph/badge.svg) +``` + +**🎯 What's Good Coverage?** +* 🥉 **60-70%** — Okay, could be better +* 🥈 **70-85%** — Good, most code tested +* 🥇 **85-95%** — Excellent coverage +* ⚠️ **100%** — Usually overkill (diminishing returns) + +--- + +## 📍 Slide 34 – ✅ CI Best Practices + +| 🎯 Practice | 💡 Why It Matters | +|------------|------------------| +| ⚡ **Fail Fast** | Stop on first failure, save time | +| 🔗 **Job Dependencies** | Don't push if tests fail | +| 🔒 **Secrets in Vault** | Never hardcode credentials | +| 📦 **Cache Dependencies** | 10x faster builds | +| 🔍 **Security Scanning** | Catch CVEs early | +| 📊 **Status Badges** | Visibility into health | +| 🎯 **Branch Protection** | Require CI before merge | +| ♻️ **Concurrency Control** | Cancel outdated runs | + +**🔐 Security:** +* ✅ Use `secrets.*` for sensitive data +* ✅ Minimum permissions (`permissions:`) +* ✅ Pin action versions (`actions/checkout@v4`) + +--- + +## 📍 Slide 35 – 🌐 GitHub Actions Marketplace + +## Reusable Actions + +```mermaid +flowchart LR + Marketplace[🏪 Actions Marketplace] --> Setup[⚙️ Setup Actions] + Marketplace --> Build[🔨 Build Actions] + Marketplace --> Deploy[🚀 Deploy Actions] + Marketplace --> Security[🔐 Security Actions] +``` + +**🔥 Popular Actions:** +* ⚙️ `actions/checkout@v4` — Clone repo +* 🐍 `actions/setup-python@v5` — Setup Python +* 🐳 `docker/build-push-action@v5` — Build Docker +* 🔐 `snyk/actions@master` — Security scan +* 📊 `codecov/codecov-action@v4` — Coverage + +**🔍 Find Actions:** +* 🌐 [github.com/marketplace](https://github.com/marketplace?type=actions) +* ⭐ Check stars/downloads +* 📖 Read documentation +* 🔒 Verify source/security + +--- + +## 📍 Slide 36 – 🏢 Section 5: Production CI/CD + +## Real-World CI Workflows + +**🎬 Netflix:** +* 🚀 **3000+** builds per day +* 🔄 Full CI pipeline in **<10 minutes** +* 🎯 A/B test deployments + +**🛒 Shopify:** +* ⚡ Deploy **80+ times per day** +* 🤖 Auto-rollback on failure +* 📊 Real-time metrics in CI + +**🔍 Google:** +* 🏗️ **Monorepo** with 2 billion LOC +* 🧪 **100+ million tests** daily +* 📦 Bazel build system + +--- + +## 📍 Slide 37 – 🚦 Branch Protection Rules + +## Require CI Before Merge + +```mermaid +flowchart LR + PR[📝 Pull Request] --> CI[🤖 CI Runs] + CI -->|✅ Pass| Merge[✅ Can Merge] + CI -->|❌ Fail| Block[🚫 Blocked] +``` + +**⚙️ GitHub Settings:** +* ✅ Require status checks to pass +* ✅ Require branches to be up to date +* ✅ Require review from code owners +* 🔒 Prevent direct push to main + +**🎯 Result:** +* 🚫 No broken code in main branch +* ✅ Every change is tested +* 📊 Full history of CI results + +--- + +## 📍 Slide 38 – 🔄 GitOps Preview + +## From CI to CD + +```mermaid +flowchart LR + CI[🤖 CI: Test & Build] --> Push[📦 Push Image] + Push --> ArgoCD[🔄 ArgoCD Detects] + ArgoCD --> Deploy[🚀 Auto Deploy] + Deploy --> K8s[☸️ Kubernetes] +``` + +**🔮 Coming Up:** +* 📦 **Lab 13**: ArgoCD deploys what CI builds +* ☸️ **K8s**: Orchestrate containers +* 🔄 **GitOps**: Git as source of truth +* 🚀 **Full automation**: Commit → Production + +--- + +## 📍 Slide 39 – 💡 CI/CD Anti-Patterns + +| ❌ Anti-Pattern | ✅ Better Approach | +|----------------|-------------------| +| 🎰 "It works on my machine" | 🧪 Automated tests catch issues | +| 📋 Manual deployment checklist | 🤖 Automated pipeline | +| 🤷 No tests, just deploy | 🧪 Comprehensive test suite | +| 💀 Long-lived feature branches | 🔄 Trunk-based development | +| 🐌 Slow CI (>30 min) | ⚡ Optimize, parallelize, cache | +| 🔓 Secrets in code | 🔒 Environment variables | + +--- + +## 📍 Slide 40 – 📈 CI Metrics to Track + +| 📊 Metric | 🎯 Target | +|-----------|----------| +| ⏱️ **Build Time** | < 10 minutes | +| ✅ **Success Rate** | > 95% | +| 🐛 **Bugs Caught in CI** | Maximize | +| 📦 **Deploy Frequency** | Multiple/day | +| 🔧 **Time to Fix Broken Build** | < 10 minutes | +| 📊 **Test Coverage** | > 80% | + +```mermaid +flowchart LR + Fast[⚡ Fast CI] --> Deploy[🚀 Deploy Often] + Deploy --> Confidence[💪 High Confidence] + Confidence --> Fast +``` + +--- + +## 📍 Slide 41 – 🎯 Section 6: Reflection + +## 📝 Key Takeaways + +1. 🤖 **CI automates testing** — catch bugs before production +2. 🧪 **Unit tests are essential** — fast feedback loop +3. ⚙️ **GitHub Actions** — powerful, free CI/CD platform +4. 🔐 **Security scanning** — integrate Snyk, scan dependencies +5. 📦 **Versioning matters** — SemVer or CalVer, be consistent + +> 💡 CI isn't just about automation — it's about building confidence. + +--- + +## 📍 Slide 42 – 🧠 The Mindset Shift + +| 😰 Old Mindset | 🚀 CI/CD Mindset | +|---------------|------------------| +| 📋 "Test before release" | 🧪 "Test every commit" | +| 🤞 "Hope it works" | ✅ "Know it works" | +| 🎰 Manual deployments | 🤖 Automated pipelines | +| 😱 "Who broke it?" | 📊 "CI caught it" | +| 🐌 Deploy monthly | 🚀 Deploy daily | +| 🔍 Find bugs in prod | 🧪 Catch bugs in CI | + +> ❓ Which mindset will you adopt? + +--- + +## 📍 Slide 43 – ✅ Your Progress + +## 🎓 What You Now Understand + +* ✅ Why CI/CD is critical for modern development +* ✅ How to write effective unit tests +* ✅ GitHub Actions workflow syntax +* ✅ Security scanning with Snyk +* ✅ Versioning strategies (SemVer vs CalVer) +* ✅ CI best practices (caching, matrix builds, path filters) + +> 🚀 **You're ready for Lab 3!** + +--- + +## 📍 Slide 44 – 📝 QUIZ — DEVOPS_L3_POST + +--- + +## 📍 Slide 45 – 🚀 What Comes Next + +## 📚 Lab 3: Build Your CI Pipeline + +* 🧪 Write unit tests for your Flask app +* ⚙️ Create GitHub Actions workflow +* 🔐 Integrate Snyk security scanning +* 📦 Automate Docker builds and versioning +* ⚡ Apply caching and best practices +* 🏆 Bonus: Multi-app CI with path filters + +**🔮 Future Lectures:** +* 📦 **Lecture 7**: Monitoring & Observability +* ☸️ **Lecture 9**: Kubernetes Deployment +* 🔄 **Lecture 13**: GitOps with ArgoCD + +```mermaid +flowchart LR + You[👤 You] --> Tests[🧪 Write Tests] + Tests --> CI[🤖 GitHub Actions] + CI --> Automation[⚡ Full Automation] + Automation --> Career[🚀 DevOps Skills] +``` + +**👋 See you in the lab!** + +--- + +## 📚 Resources & Further Reading + +**📕 Books:** +* 📖 *Continuous Delivery* — Jez Humble +* 📖 *The DevOps Handbook* — Gene Kim +* 📖 *Accelerate* — Nicole Forsgren + +**🔗 Links:** +* 🌐 [GitHub Actions Docs](https://docs.github.com/en/actions) +* 🌐 [Pytest Documentation](https://docs.pytest.org/) +* 🌐 [Snyk Security](https://snyk.io/) +* 🌐 [SemVer](https://semver.org/) +* 🌐 [CalVer](https://calver.org/) + +**🛠️ Tools:** +* 🔍 [act](https://github.com/nektos/act) — Run GitHub Actions locally +* 🔍 [actionlint](https://github.com/rhysd/actionlint) — Lint workflows +* 📊 [Codecov](https://codecov.io/) — Coverage tracking + +--- diff --git a/lectures/lec4.md b/lectures/lec4.md new file mode 100644 index 0000000000..acfe810526 --- /dev/null +++ b/lectures/lec4.md @@ -0,0 +1,801 @@ +# 📌 Lecture 4 — Infrastructure as Code: From Snowflakes to Cattle + +## 📍 Slide 1 – 🚀 Welcome to Infrastructure as Code + +* 🌍 **Infrastructure used to be physical** — racks, cables, manual configuration +* 😰 Manual setup leads to inconsistency, drift, and undocumented "snowflakes" +* 🏗️ **Infrastructure as Code (IaC)** treats infrastructure like software +* 🎯 This lecture: learn to define, version, and automate your infrastructure + +```mermaid +flowchart LR + Manual[🔧 Manual Setup] -->|IaC| Code[📝 Code-Defined] + Code --> Reproducible[🔄 Reproducible Infrastructure] +``` + +--- + +## 📍 Slide 2 – 🎯 What You Will Learn + +* ✅ Understand Infrastructure as Code principles +* ✅ Compare declarative vs imperative IaC approaches +* ✅ Apply Terraform workflows to real cloud infrastructure +* ✅ Manage infrastructure state securely + +**🎓 Learning Outcomes:** +| # | Outcome | +|---|---------| +| 1 | 🧠 Define IaC and explain its benefits | +| 2 | 🔍 Distinguish between Terraform and Pulumi | +| 3 | 🛠️ Write basic Terraform configurations | +| 4 | 🗺️ Understand state management and security | + +--- + +## 📍 Slide 3 – 📋 How This Lecture Works + +* 📚 **Concepts + Code examples** — hands-on focus +* 🎮 **Real-world scenarios** — cloud provisioning challenges +* 📝 **3 quiz checkpoints**: PRE / MID / POST +* 🛠️ **Tool comparison**: Terraform vs Pulumi + +**⏱️ Lecture Structure:** +``` +Section 0: Introduction (now) → 📝 PRE Quiz +Section 1: The Infrastructure Problem +Section 2: IaC Fundamentals +Section 3: Terraform Deep Dive → 📝 MID Quiz +Section 4: State & Security +Section 5: Real World IaC +Section 6: Reflection → 📝 POST Quiz +``` + +--- + +## 📍 Slide 4 – ❓ The Big Question + +* 📊 **73%** of organizations report configuration drift as a major issue +* ⏱️ Average time to provision a server manually: **hours to days** +* 💥 Most outages caused by **configuration changes** + +> 💬 *"It works in staging but not production"* — Every ops engineer, ever + +**🤔 Think about it:** +* How do you recreate your production environment? +* What happens when the person who set it up leaves? +* Can you spin up a new environment in minutes? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L4_PRE + +--- + +## 📍 Slide 6 – 🔥 Section 1: The Infrastructure Problem + +* 🐶 **Pet Servers** = unique, hand-crafted, irreplaceable +* 🔧 Manual configuration via SSH and console clicks +* 📋 Documentation gets outdated immediately +* 💥 Result: **snowflake infrastructure** — no two servers are the same + +```mermaid +flowchart LR + Server1[🖥️ Server 1: Ubuntu 20 + patches] + Server2[🖥️ Server 2: Ubuntu 22 + different patches] + Server3[🖥️ Server 3: ???] + Server1 --> Chaos[😱 Configuration Chaos] + Server2 --> Chaos + Server3 --> Chaos +``` + +--- + +## 📍 Slide 7 – 🐶 Pets vs Cattle + +* 🐶 **Pets**: Named servers, nursed back to health when sick +* 🐄 **Cattle**: Numbered, identical, replaced when broken +* 🌍 Cloud-native = cattle mindset + +```mermaid +flowchart TD + subgraph 🐶 Pets + P1[web-prod-01] + P2[db-master] + P3[app-legacy] + end + subgraph 🐄 Cattle + C1[instance-001] + C2[instance-002] + C3[instance-003] + end + Pets -->|😰 Unique, fragile| Problem[Hard to scale] + Cattle -->|🔄 Identical, disposable| Solution[Easy to scale] +``` + +> 🤔 **Think:** Are your servers pets or cattle? + +--- + +## 📍 Slide 8 – 😱 Configuration Drift + +* 📅 Server configured once, modified many times +* 🔧 "Quick fixes" applied directly in production +* 📋 No record of what changed +* 💀 Disaster recovery = guesswork + +```mermaid +flowchart TD + Initial[✅ Initial Setup] --> Month1[📅 Month 1: Hotfix applied] + Month1 --> Month3[📅 Month 3: Security patch] + Month3 --> Month6[📅 Month 6: Unknown changes] + Month6 --> Drift[😱 Configuration Drift] + Drift --> Unknown[❓ What's actually running?] +``` + +**📊 The Numbers:** +* 🔍 **65%** of downtime caused by configuration issues +* ⏱️ Average recovery time: **4+ hours** +* 💰 Cost per hour of downtime: **$300,000** (enterprise) + +--- + +## 📍 Slide 9 – 😨 The Bus Factor + +* 👤 One person knows how the infrastructure works +* 🚌 They leave, get sick, or go on vacation +* 🙈 Nobody can recreate or fix the environment +* 💀 Business continuity at risk + +> ⚠️ **Bus Factor = 1** means your infrastructure is fragile + +**😰 Signs of Low Bus Factor:** +* 🔇 "Ask John, he set that up" +* 📝 Documentation is outdated or missing +* 🐌 Changes require specific people +* 🚪 Knowledge walks out the door + +**💬 Discussion:** What's your infrastructure bus factor? + +--- + +## 📍 Slide 10 – 💸 The Cost of Manual Infrastructure + +| 🔥 Problem | 💥 Impact | +|------------|-----------| +| 🐢 Slow provisioning | Days to spin up new environments | +| 📋 Manual processes | Human error, inconsistency | +| 👉 No audit trail | Compliance violations | +| 🙈 Configuration drift | Unpredictable behavior | + +**📈 Real Numbers:** +* 🏢 **Manual provisioning**: 2-4 hours per server +* 🚀 **With IaC**: 2-4 minutes per server +* 🔄 **Environment recreation**: hours vs seconds + +**💰 Time Cost:** +* 👨‍💻 Engineer time: **$75-150/hour** +* 🖥️ 10 servers manually: **$1,500-3,000** +* 🤖 10 servers with IaC: **$15-30** + +--- + +## 📍 Slide 11 – 💡 Section 2: What Infrastructure as Code Is + +* 📝 **IaC** = defining infrastructure in version-controlled files +* 🔄 Infrastructure becomes **reproducible** and **auditable** +* 🚫 No more clicking through consoles +* 🎯 Same infrastructure, every time + +```mermaid +flowchart LR + Code[📝 Code] -->|🔄 Apply| Cloud[☁️ Cloud] + Cloud --> Infra[🏗️ Infrastructure] + Code -->|📜 Git| Version[Version Control] +``` + +**📖 Definition:** +> *Infrastructure as Code is the practice of managing and provisioning infrastructure through machine-readable configuration files rather than through manual processes or interactive tools.* + +--- + +## 📍 Slide 12 – 🚫 What IaC is NOT + +| ❌ Myth | ✅ Reality | +|---------|-----------| +| "Just automation scripts" | 📝 Declarative desired state | +| "Only for cloud" | 🖥️ Works for any infrastructure | +| "Replaces ops people" | 🤝 Empowers ops teams | +| "Too complex for small teams" | 🎯 Benefits scale to any size | +| "One-time setup" | 🔄 Continuous lifecycle management | + +> 🔥 **Hot take:** If you can't recreate your infrastructure from code, you don't have IaC. + +**🎯 IaC is about:** +* 🧠 Declarative definitions +* 🤝 Team collaboration on infrastructure +* 🔄 Repeatable, consistent environments +* 📊 Audit trails and compliance + +--- + +## 📍 Slide 13 – 🔀 Declarative vs Imperative + +```mermaid +flowchart TD + subgraph Declarative + D1[📝 Define desired state] + D2[🤖 Tool figures out how] + D1 --> D2 + end + subgraph Imperative + I1[📝 Define exact steps] + I2[🔧 Execute step by step] + I1 --> I2 + end +``` + +| 📋 Aspect | 🌍 Declarative | 🔧 Imperative | +|-----------|---------------|---------------| +| 📝 What you write | Desired end state | Exact steps | +| 🛠️ Tool | Terraform, CloudFormation | Pulumi, Scripts | +| 🔄 Idempotency | Built-in | You implement | +| 📚 Example | "3 VMs exist" | "Create VM 1, 2, 3" | + +**📚 Source:** Terraform documentation + +--- + +## 📍 Slide 14 – 🛠️ IaC Tool Landscape + +```mermaid +graph TD + IaC[🏗️ Infrastructure as Code] + IaC --> Prov[📦 Provisioning] + IaC --> Config[⚙️ Configuration] + Prov --> Terraform[🌍 Terraform] + Prov --> Pulumi[📦 Pulumi] + Prov --> Cloud[☁️ CloudFormation/ARM] + Config --> Ansible[🔧 Ansible] + Config --> Chef[👨‍🍳 Chef] + Config --> Puppet[🎭 Puppet] +``` + +| 🛠️ Tool | 🎯 Focus | 📝 Language | +|---------|---------|------------| +| 🌍 **Terraform** | Provisioning | HCL (declarative) | +| 📦 **Pulumi** | Provisioning | Python, TS, Go | +| 🔧 **Ansible** | Configuration | YAML | +| ☁️ **CloudFormation** | AWS only | YAML/JSON | + +--- + +## 📍 Slide 15 – ⚡ Before vs After IaC + +| 😰 Before | 🚀 After | +|----------|---------| +| 📅 Days to provision | ⚡ Minutes to provision | +| 📋 Manual documentation | 📝 Code IS documentation | +| 👉 "Who changed that?" | 📜 Git history shows all | +| 😨 Fear of recreation | 💪 Confident rebuilds | +| 🐶 Unique snowflakes | 🐄 Identical cattle | +| 🙅 "Don't touch prod" | 🔄 Infrastructure is disposable | + +> 🤔 How confident are you in recreating your infrastructure? + +--- + +## 📍 Slide 16 – 🎮 Section 3: Terraform Deep Dive + +## 🌍 Why Terraform? + +* 🌐 **Multi-cloud**: AWS, GCP, Azure, Yandex, and 3000+ providers +* 📝 **HCL**: Human-readable configuration language +* 🔄 **State management**: Tracks what exists +* 🏢 **Industry standard**: Most widely adopted IaC tool + +**🎮 Let's build infrastructure.** + +--- + +## 📍 Slide 17 – 📝 Terraform Workflow + +```mermaid +flowchart LR + Write[📝 Write] --> Init[🔧 Init] + Init --> Plan[📋 Plan] + Plan --> Apply[🚀 Apply] + Apply --> Destroy[💥 Destroy] +``` + +* 📝 **Write**: Define resources in `.tf` files +* 🔧 **Init**: Download provider plugins +* 📋 **Plan**: Preview changes (dry run) +* 🚀 **Apply**: Create/update infrastructure +* 💥 **Destroy**: Remove all resources + +**🛠️ Commands:** +```bash +terraform init # Download providers +terraform plan # Preview changes +terraform apply # Apply changes +terraform destroy # Remove everything +``` + +--- + +## 📍 Slide 18 – 🧱 Terraform Building Blocks + +```mermaid +flowchart TD + Config[📁 Configuration] + Config --> Provider[☁️ Provider] + Config --> Resource[🏗️ Resource] + Config --> Variable[📊 Variable] + Config --> Output[📤 Output] + Config --> Data[🔍 Data Source] +``` + +* ☁️ **Provider**: Cloud API connection (AWS, GCP, Yandex) +* 🏗️ **Resource**: Infrastructure component (VM, network, firewall) +* 📊 **Variable**: Configurable inputs +* 📤 **Output**: Values to display/export +* 🔍 **Data Source**: Query existing infrastructure + +--- + +## 📍 Slide 19 – 💻 Terraform Example: VM Creation + +```hcl +# ☁️ Provider configuration +provider "yandex" { + zone = "ru-central1-a" +} + +# 🏗️ Virtual machine resource +resource "yandex_compute_instance" "web" { + name = "web-server" + platform_id = "standard-v2" + + resources { + cores = 2 + memory = 2 + } + + boot_disk { + initialize_params { + image_id = "fd8vmcue7aajqdge3bp0" # Ubuntu 22.04 + } + } +} +``` + +**🎯 Result:** One command creates a VM in the cloud + +--- + +## 📍 Slide 20 – 📊 Variables and Outputs + +```hcl +# 📊 Input variables +variable "instance_count" { + description = "Number of VMs to create" + type = number + default = 1 +} + +variable "environment" { + description = "Environment name" + type = string +} + +# 📤 Output values +output "vm_ip" { + description = "Public IP of the VM" + value = yandex_compute_instance.web.network_interface.0.nat_ip_address +} +``` + +**🛠️ Usage:** +```bash +terraform apply -var="instance_count=3" -var="environment=prod" +``` + +--- + +## 📍 Slide 21 – 🔄 Terraform Plan + +```mermaid +flowchart LR + Code[📝 Config] --> Plan[📋 terraform plan] + State[📦 State] --> Plan + Plan --> Diff[🔍 Difference] + Diff --> Preview[👀 What will change?] +``` + +**📋 Plan Output Example:** +``` +# yandex_compute_instance.web will be created ++ resource "yandex_compute_instance" "web" { + + name = "web-server" + + platform_id = "standard-v2" + + status = (known after apply) + + + resources { + + cores = 2 + + memory = 2 + } + } + +Plan: 1 to add, 0 to change, 0 to destroy. +``` + +**🎯 Always review the plan before applying!** + +--- + +## 📍 Slide 22 – 📦 Pulumi Alternative + +```mermaid +flowchart LR + Terraform[🌍 Terraform] -->|HCL| Declarative[📝 Declarative] + Pulumi[📦 Pulumi] -->|Python/TS/Go| Imperative[💻 Imperative] +``` + +**📦 Pulumi Python Example:** +```python +import pulumi +import pulumi_yandex as yandex + +# 🏗️ Create VM using Python +vm = yandex.ComputeInstance("web", + name="web-server", + platform_id="standard-v2", + resources=yandex.ComputeInstanceResourcesArgs( + cores=2, + memory=2, + )) + +# 📤 Export IP address +pulumi.export("ip", vm.network_interfaces[0].nat_ip_address) +``` + +**🎯 Same result, real programming language** + +--- + +## 📍 Slide 23 – ⚖️ Terraform vs Pulumi + +| 📋 Aspect | 🌍 Terraform | 📦 Pulumi | +|-----------|-------------|----------| +| 📝 Language | HCL (domain-specific) | Python, TS, Go, C# | +| 📚 Learning curve | New syntax to learn | Familiar languages | +| 🔄 Logic | Limited (count, for_each) | Full programming | +| 🧪 Testing | External tools | Native unit tests | +| 📦 State | Local or S3 | Pulumi Cloud (free tier) | +| 🔐 Secrets | Plain in state | Encrypted by default | + +> ❓ **When to use which?** +> * 🌍 **Terraform**: Larger community, more examples, declarative simplicity +> * 📦 **Pulumi**: Complex logic, existing codebase, testing requirements + +--- + +## 📍 Slide 24 – 🔐 Security Best Practices + +```yaml +# ❌ NEVER do this +provider "aws" { + access_key = "AKIAIOSFODNN7EXAMPLE" # 💀 Hardcoded secret! + secret_key = "wJalrXUtnFEMI/..." # 💀 Hardcoded secret! +} + +# ✅ Use environment variables +# export AWS_ACCESS_KEY_ID="..." +# export AWS_SECRET_ACCESS_KEY="..." +provider "aws" { + # Automatically uses env vars +} +``` + +**🔐 Security Rules:** +* 🚫 Never commit secrets to Git +* 📁 Use `.gitignore` for state and tfvars +* 🔑 Use environment variables or secret managers +* 🔒 Encrypt state file at rest + +--- + +## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L4_MID + +--- + +## 📍 Slide 26 – 📦 Section 4: State Management + +## 🗃️ What is Terraform State? + +* 📝 Maps configuration to real-world resources +* 🔍 Tracks what Terraform manages +* 🔄 Determines what changes are needed +* ⚠️ Contains sensitive data + +```mermaid +flowchart LR + Config[📝 Config Files] --> TF[🌍 Terraform] + State[📦 State File] --> TF + TF --> Cloud[☁️ Real Infrastructure] + Cloud --> State +``` + +--- + +## 📍 Slide 27 – 📁 State File Contents + +```json +{ + "version": 4, + "terraform_version": "1.9.0", + "resources": [ + { + "type": "yandex_compute_instance", + "name": "web", + "instances": [ + { + "attributes": { + "id": "fhm1234567890", + "name": "web-server", + "network_interface": [ + { + "ip_address": "192.168.1.10", + "nat_ip_address": "51.250.1.100" + } + ] + } + } + ] + } + ] +} +``` + +**⚠️ Never edit state manually!** + +--- + +## 📍 Slide 28 – 🌐 Remote State + +```mermaid +flowchart TD + Dev1[👨‍💻 Developer 1] --> Remote[🌐 Remote State] + Dev2[👨‍💻 Developer 2] --> Remote + Dev3[👨‍💻 Developer 3] --> Remote + Remote --> Cloud[☁️ Cloud Infrastructure] +``` + +**🌐 Remote State Benefits:** +* 🤝 Team collaboration +* 🔒 Locking prevents conflicts +* 🔐 Encrypted at rest +* 📜 Versioning and backup + +**📦 Backend Options:** +* ☁️ **S3/GCS**: Object storage +* 🏢 **Terraform Cloud**: HashiCorp managed +* 🔐 **Consul**: HashiCorp Consul + +--- + +## 📍 Slide 29 – 📊 IaC Metrics + +| 📊 Metric | 📏 Measures | 🏆 Target | +|-----------|------------|---------| +| ⏱️ **Provisioning Time** | Time to create env | < 15 minutes | +| 🔄 **Environment Parity** | Dev = Staging = Prod | 100% | +| ❌ **Drift Detection** | Config drift incidents | 0 per month | +| 📜 **Audit Compliance** | Changes tracked in Git | 100% | + +> 📚 These metrics indicate IaC maturity. + +**🤔 Question:** How long does it take to spin up a new environment? + +--- + +## 📍 Slide 30 – 🌊 From Snowflakes to Cattle + +```mermaid +flowchart LR + subgraph 😱 Snowflakes + Manual[🔧 Manual Setup] + Unique[❄️ Unique Servers] + Drift[📋 Configuration Drift] + end + subgraph 🐄 Cattle + Code[📝 Code-Defined] + Identical[🔄 Identical Servers] + Reproducible[✅ Reproducible] + end + Snowflakes -->|🚀 IaC| Cattle +``` + +**🎯 Goal State:** +* ⚡ Any environment recreatable in minutes +* 🔄 All changes through code review +* 📈 Teams deploy infrastructure confidently + +--- + +## 📍 Slide 31 – 🏢 Section 5: IaC in Real Life + +## 📅 A Day with IaC + +**☀️ Morning:** +* 📊 Review infrastructure PR +* 👀 Check `terraform plan` output +* ✅ Approve and merge + +**🌤️ Afternoon:** +* 🚨 Need new test environment +* 🔧 Copy `terraform.tfvars` +* 🚀 `terraform apply` — **done in 10 minutes** + +**🌙 Evening:** +* 🗑️ `terraform destroy` test environment +* 💰 No resources running overnight + +--- + +## 📍 Slide 32 – 👥 IaC Team Workflow + +| 👤 Role | 🎯 IaC Responsibility | +|---------|----------------------| +| 🔧 **DevOps/Platform** | Write and maintain IaC modules | +| 👨‍💻 **Developer** | Use modules, request infrastructure | +| 🛡️ **Security** | Review IaC for compliance | +| 📊 **FinOps** | Monitor infrastructure costs | + +**🔗 Common Workflow:** +* 📝 Create branch with IaC changes +* 🔍 CI runs `terraform plan` +* 👀 Team reviews the plan +* ✅ Merge triggers `terraform apply` + +--- + +## 📍 Slide 33 – 🤝 GitOps for Infrastructure + +```mermaid +flowchart TD + Dev[👨‍💻 Developer] -->|📝 PR| Git[🐙 Git Repository] + Git -->|🔄 CI/CD| Plan[📋 Terraform Plan] + Plan -->|👀 Review| Approve[✅ Approve] + Approve -->|🚀 Merge| Apply[🌍 Terraform Apply] + Apply --> Cloud[☁️ Infrastructure] +``` + +**🤝 GitOps Practices:** +* 📟 All changes through pull requests +* 📝 Plan output in PR comments +* 👥 Required approvals +* 🔓 Protected main branch + +--- + +## 📍 Slide 34 – 📈 Career Path: IaC Skills + +```mermaid +flowchart LR + Junior[🌱 Junior: Basic Terraform] --> Mid[💼 Mid: Modules & CI/CD] + Mid --> Senior[⭐ Senior: Multi-cloud & Architecture] + Senior --> Principal[🏆 Principal: Platform Strategy] +``` + +**🛠️ Skills to Build:** +* 🌍 Terraform HCL fluency +* ☁️ Cloud provider APIs +* 🔐 Security best practices +* 📦 Module design +* 🔄 CI/CD integration + +--- + +## 📍 Slide 35 – 🌍 Real Company Examples + +**🏢 HashiCorp Customers:** +* 🏦 **Stripe**: Terraform for AWS infrastructure +* 🎮 **Riot Games**: Multi-cloud with Terraform +* 🛒 **Shopify**: Thousands of resources managed + +**☁️ Cloud Native:** +* 🔍 **Google**: Uses Terraform internally +* 📦 **Spotify**: IaC for Kubernetes infrastructure +* 🎬 **Netflix**: Custom tooling built on IaC principles + +**📊 Stats:** +* 🌍 **2M+** Terraform users worldwide +* 📦 **3000+** providers available +* 🏢 **Fortune 500**: 85% use IaC + +--- + +## 📍 Slide 36 – 🎯 Section 6: Reflection + +## 📝 Key Takeaways + +1. 🏗️ **IaC = Infrastructure defined in code** +2. 🐄 **Cattle not pets** — servers are disposable +3. 📝 **Version control everything** — Git for infrastructure +4. 📋 **Plan before apply** — always review changes +5. 🔐 **Never commit secrets** — use environment variables + +> 💡 If you can't recreate it from code, it's not really infrastructure as code. + +--- + +## 📍 Slide 37 – 🧠 The Mindset Shift + +| 😰 Old Mindset | 🚀 IaC Mindset | +|---------------|------------------| +| 🙅 "SSH and fix it" | 📝 "Change the code" | +| 🚫 "Don't touch that server" | 💪 "Destroy and recreate" | +| 👉 "Who set this up?" | 📜 "Git blame shows history" | +| 😨 "Manual is faster" | ⚡ "Automation is faster at scale" | +| 💻 "Works on my cloud" | 🌍 "Works on any cloud" | + +> ❓ Which mindset describes your team? + +--- + +## 📍 Slide 38 – ✅ Your Progress + +## 🎓 What You Now Understand + +* ✅ Why IaC is essential for modern infrastructure +* ✅ The difference between declarative and imperative +* ✅ How Terraform and Pulumi work +* ✅ State management and security practices +* ✅ Real-world IaC workflows + +> 🚀 **You're ready for Lab 4: Terraform & Pulumi** + +--- + +## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L4_POST + +--- + +## 📍 Slide 40 – 🚀 What Comes Next + +## 📚 Next Lecture: Configuration Management with Ansible + +* 🔧 Ansible fundamentals +* 📦 Roles and playbooks +* 🤖 Automating server configuration +* 💻 Hands-on: Deploying Docker with Ansible + +**🎉 Your IaC journey begins.** + +> 🐄 From snowflakes to cattle — one terraform apply at a time. + +```mermaid +flowchart LR + You[👤 You] --> IaC[🏗️ IaC Skills] + IaC --> Reproducible[🔄 Reproducible Infra] + Reproducible --> Career[🚀 Career Growth] +``` + +**👋 See you in the next lecture!** + +--- + +## 📚 Resources & Further Reading + +**📕 Books:** +* 📖 *Terraform: Up & Running* — Yevgeniy Brikman +* 📖 *Infrastructure as Code* — Kief Morris +* 📖 *The DevOps Handbook* — Gene Kim et al. + +**🔗 Links:** +* 🌐 [Terraform Documentation](https://developer.hashicorp.com/terraform/docs) +* 🌐 [Pulumi Documentation](https://www.pulumi.com/docs/) +* 🌐 [Terraform Registry](https://registry.terraform.io/) + +--- diff --git a/lectures/lec5.md b/lectures/lec5.md new file mode 100644 index 0000000000..5bcfba6c1a --- /dev/null +++ b/lectures/lec5.md @@ -0,0 +1,824 @@ +# 📌 Lecture 5 — Configuration Management: Ansible Fundamentals + +## 📍 Slide 1 – 🚀 Welcome to Configuration Management + +* 🌍 **Infrastructure is provisioned** — but what about configuring it? +* 😰 Manual server setup leads to inconsistency and errors +* 🔧 **Ansible automates configuration** — repeatable, reliable, documented +* 🎯 This lecture: master Ansible roles, playbooks, and best practices + +```mermaid +flowchart LR + Provision[🏗️ Terraform: Create VMs] --> Configure[🔧 Ansible: Configure VMs] + Configure --> Ready[✅ Ready to Run Apps] +``` + +--- + +## 📍 Slide 2 – 🎯 What You Will Learn + +* ✅ Understand Ansible architecture and concepts +* ✅ Write idempotent playbooks and roles +* ✅ Secure credentials with Ansible Vault +* ✅ Apply configuration management best practices + +**🎓 Learning Outcomes:** +| # | Outcome | +|---|---------| +| 1 | 🧠 Explain Ansible's agentless architecture | +| 2 | 🔍 Create reusable roles for configuration | +| 3 | 🛠️ Write idempotent tasks and handlers | +| 4 | 🗺️ Secure secrets with Ansible Vault | + +--- + +## 📍 Slide 3 – 📋 How This Lecture Works + +* 📚 **Concepts + YAML examples** — hands-on learning +* 🎮 **Real-world scenarios** — server configuration challenges +* 📝 **3 quiz checkpoints**: PRE / MID / POST +* 🛠️ **Best practices**: roles, handlers, idempotency + +**⏱️ Lecture Structure:** +``` +Section 0: Introduction (now) → 📝 PRE Quiz +Section 1: The Configuration Problem +Section 2: Ansible Fundamentals +Section 3: Roles & Playbooks → 📝 MID Quiz +Section 4: Idempotency & Handlers +Section 5: Real World Ansible +Section 6: Reflection → 📝 POST Quiz +``` + +--- + +## 📍 Slide 4 – ❓ The Big Question + +* 📊 **94%** of organizations experienced security incidents from misconfigurations +* ⏱️ Average time to configure a server manually: **2-4 hours** +* 💥 Most configuration drift goes **undetected for months** + +> 💬 *"I installed it the same way... I think"* — Every sysadmin, ever + +**🤔 Think about it:** +* How do you ensure 100 servers have identical configs? +* What happens when you need to update a package on all servers? +* Can you prove compliance across your infrastructure? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L5_PRE + +--- + +## 📍 Slide 6 – 🔥 Section 1: The Configuration Problem + +* 🔧 **Manual configuration** = SSH into each server +* 📋 Run commands, edit files, install packages +* 📝 Document steps (that nobody reads) +* 💥 Result: **no two servers are identical** + +```mermaid +flowchart LR + Admin[👤 Admin] -->|SSH| Server1[🖥️ Server 1] + Admin -->|SSH| Server2[🖥️ Server 2] + Admin -->|SSH| Server3[🖥️ Server 3] + Server1 --> Drift1[📋 Config A] + Server2 --> Drift2[📋 Config B] + Server3 --> Drift3[📋 Config ???] +``` + +--- + +## 📍 Slide 7 – 🐚 Shell Script Approach + +* 📝 Write bash scripts to automate +* 🔄 Run scripts on each server +* ⚠️ Problem: Scripts aren't idempotent + +```bash +#!/bin/bash +# 😰 What happens if you run this twice? +apt-get update +apt-get install -y nginx +echo "Welcome" > /var/www/html/index.html +systemctl start nginx +``` + +**💥 Issues:** +* 🔄 Re-running may cause errors +* 😰 No rollback mechanism +* 📋 No state tracking +* 🔗 No dependency management + +> 🤔 **Think:** What if nginx is already installed? + +--- + +## 📍 Slide 8 – 😱 Configuration Management Challenges + +* 📅 100 servers need the same update +* 🔧 Some servers have different OS versions +* 📋 Some packages conflict with others +* 💀 One mistake = hours of cleanup + +```mermaid +flowchart TD + Update[📦 Update Required] --> S1[🖥️ Server 1: Ubuntu 20] + Update --> S2[🖥️ Server 2: Ubuntu 22] + Update --> S3[🖥️ Server 3: Ubuntu 24] + S1 --> Problem1[😰 Different package versions] + S2 --> Problem2[😰 Different dependencies] + S3 --> Problem3[😰 Different configs needed] +``` + +**📊 The Numbers:** +* 🔍 **85%** of breaches involve misconfiguration +* ⏱️ Manual update of 100 servers: **days** +* 💰 Cost of configuration-related downtime: **$5,600/minute** + +--- + +## 📍 Slide 9 – 😨 Documentation Drift + +* 📝 Documentation written once +* 🔧 Server modified many times +* 📋 Documentation never updated +* 💀 Reality ≠ documentation + +> ⚠️ **Outdated docs are worse than no docs** + +**😰 Signs of Documentation Drift:** +* 🔇 "The wiki says X but we do Y now" +* 📝 Multiple conflicting runbooks +* 🐌 New hires struggle to onboard +* 🚪 Knowledge leaves with employees + +**💬 Discussion:** How current is your documentation? + +--- + +## 📍 Slide 10 – 💸 The Cost of Manual Configuration + +| 🔥 Problem | 💥 Impact | +|------------|-----------| +| 🐢 Slow updates | Security vulnerabilities linger | +| 📋 Manual errors | Downtime from typos | +| 👉 Inconsistency | "Works on server 1 but not 2" | +| 🙈 No audit trail | Compliance failures | + +**📈 Real Numbers:** +* 🏢 **Manual config time**: 2-4 hours per server +* 🚀 **With Ansible**: 5-10 minutes per server +* 🔄 **Scaling**: minutes vs days + +**💰 ROI Example:** +* 👨‍💻 100 servers × 3 hours × $75/hour = **$22,500** +* 🤖 Ansible: 1 hour setup + seconds to run = **$75** + +--- + +## 📍 Slide 11 – 💡 Section 2: What Ansible Is + +* 🔧 **Configuration management tool** — automate server setup +* 🌐 **Agentless** — uses SSH, no agents to install +* 📝 **YAML-based** — human-readable playbooks +* 🔄 **Idempotent** — safe to run multiple times + +```mermaid +flowchart LR + Control[💻 Control Node] -->|SSH| Node1[🖥️ Managed Node] + Control -->|SSH| Node2[🖥️ Managed Node] + Control -->|SSH| Node3[🖥️ Managed Node] +``` + +**📖 Definition:** +> *Ansible is an open-source automation tool for configuration management, application deployment, and task automation using a simple YAML syntax.* + +--- + +## 📍 Slide 12 – 🚫 What Ansible is NOT + +| ❌ Myth | ✅ Reality | +|---------|-----------| +| "Replaces Terraform" | 🤝 They complement each other | +| "Requires agents" | 🌐 Agentless, SSH-based | +| "Only for Linux" | 🪟 Works with Windows too | +| "Just a scripting tool" | 📦 Full configuration management | +| "Hard to learn" | 📝 YAML is simple | + +> 🔥 **Hot take:** Terraform provisions, Ansible configures. Use both. + +**🎯 Ansible is about:** +* 🧠 Declarative configuration +* 🤝 Consistent state across servers +* 🔄 Repeatable automation +* 📊 Self-documenting infrastructure + +--- + +## 📍 Slide 13 – 🏗️ Ansible Architecture + +```mermaid +flowchart TD + Control[💻 Control Node] + Control --> Inventory[📋 Inventory] + Control --> Playbook[📝 Playbook] + Control --> Modules[📦 Modules] + Inventory --> Managed[🖥️ Managed Nodes] + Playbook --> Managed + Modules --> Managed +``` + +| 🧱 Component | 🎯 Purpose | +|-------------|----------| +| 💻 **Control Node** | Where Ansible runs | +| 📋 **Inventory** | List of managed servers | +| 📝 **Playbook** | Automation instructions | +| 📦 **Modules** | Units of work (apt, copy, service) | +| 🖥️ **Managed Nodes** | Target servers | + +--- + +## 📍 Slide 14 – 📋 Inventory Basics + +```ini +# inventory/hosts.ini +[webservers] +web1 ansible_host=192.168.1.10 +web2 ansible_host=192.168.1.11 + +[databases] +db1 ansible_host=192.168.1.20 + +[all:vars] +ansible_user=ubuntu +ansible_python_interpreter=/usr/bin/python3 +``` + +**🎯 Inventory Features:** +* 📁 Group servers logically +* 🔧 Set per-host or per-group variables +* 🌐 Static files or dynamic discovery +* 🏷️ Use patterns: `webservers`, `all`, `db*` + +--- + +## 📍 Slide 15 – ⚡ Before vs After Ansible + +| 😰 Before | 🚀 After | +|----------|---------| +| 📅 SSH into each server | 🤖 One command for all | +| 📋 Manual steps | 📝 Documented playbooks | +| 👉 "Run these commands" | ✅ "Desired state defined" | +| 😨 Fear of updates | 💪 Confident automation | +| 🐌 Hours per server | ⚡ Seconds per server | +| 📝 Outdated wiki | 📄 Living documentation | + +> 🤔 How much time does your team spend on manual configuration? + +--- + +## 📍 Slide 16 – 🎮 Section 3: Roles & Playbooks + +## 📝 Playbook Basics + +* 📄 YAML file with automation tasks +* 🎯 Defines desired state +* 🔄 Executes on target hosts +* 📦 Groups related tasks + +**🎮 Let's write some Ansible.** + +--- + +## 📍 Slide 17 – 📝 Simple Playbook Example + +```yaml +--- +# playbook.yml +- name: Configure web servers + hosts: webservers + become: yes # 🔐 Run as root + + tasks: + - name: Update apt cache + apt: + update_cache: yes + cache_valid_time: 3600 + + - name: Install nginx + apt: + name: nginx + state: present + + - name: Start nginx + service: + name: nginx + state: started + enabled: yes +``` + +**🛠️ Run it:** +```bash +ansible-playbook -i inventory/hosts.ini playbook.yml +``` + +--- + +## 📍 Slide 18 – 📦 Why Roles? + +```mermaid +flowchart TD + subgraph ❌ Without Roles + P1[📝 One huge playbook] + P1 --> Problem[😰 Hard to maintain] + end + subgraph ✅ With Roles + R1[📦 common role] + R2[📦 docker role] + R3[📦 app role] + R1 --> Reuse[🔄 Reusable] + R2 --> Reuse + R3 --> Reuse + end +``` + +**📦 Role Benefits:** +* 🔄 **Reusability**: Use across projects +* 📁 **Organization**: Clear structure +* 🧪 **Testability**: Test roles independently +* 🤝 **Sharing**: Ansible Galaxy + +--- + +## 📍 Slide 19 – 📁 Role Structure + +``` +roles/ +├── docker/ +│ ├── tasks/ +│ │ └── main.yml # 🎯 Main tasks +│ ├── handlers/ +│ │ └── main.yml # 🔔 Event handlers +│ ├── defaults/ +│ │ └── main.yml # 📊 Default variables +│ ├── templates/ +│ │ └── config.j2 # 📝 Jinja2 templates +│ └── files/ +│ └── script.sh # 📄 Static files +``` + +**🔑 Key Directories:** +* 📁 **tasks/**: What to do +* 📁 **handlers/**: React to changes +* 📁 **defaults/**: Default values (low priority) +* 📁 **templates/**: Dynamic file templates +* 📁 **files/**: Static files to copy + +--- + +## 📍 Slide 20 – 🐳 Docker Role Example + +```yaml +# roles/docker/tasks/main.yml +--- +- name: Install Docker prerequisites + apt: + name: + - apt-transport-https + - ca-certificates + - curl + state: present + +- name: Add Docker GPG key + apt_key: + url: https://download.docker.com/linux/ubuntu/gpg + state: present + +- name: Add Docker repository + apt_repository: + repo: "deb https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable" + state: present + +- name: Install Docker + apt: + name: docker-ce + state: present + notify: restart docker +``` + +--- + +## 📍 Slide 21 – 🔔 Handlers + +```yaml +# roles/docker/handlers/main.yml +--- +- name: restart docker + service: + name: docker + state: restarted +``` + +**🔔 Handler Features:** +* 🔄 Only run when notified +* ⏱️ Run once at end of play +* 🎯 React to configuration changes +* 💡 Prevent unnecessary restarts + +```yaml +# tasks/main.yml +- name: Update Docker config + template: + src: daemon.json.j2 + dest: /etc/docker/daemon.json + notify: restart docker # 🔔 Trigger handler +``` + +--- + +## 📍 Slide 22 – 📊 Variables & Defaults + +```yaml +# roles/docker/defaults/main.yml +--- +docker_version: "24.0" +docker_users: + - ubuntu +docker_log_driver: "json-file" +docker_log_max_size: "10m" +``` + +**📊 Variable Precedence (lowest to highest):** +1. 📁 Role defaults +2. 📋 Inventory variables +3. 📄 Playbook vars +4. 🔧 Command line (`-e var=value`) + +```yaml +# Using variables in tasks +- name: Install Docker {{ docker_version }} + apt: + name: "docker-ce={{ docker_version }}*" + state: present +``` + +--- + +## 📍 Slide 23 – 📝 Using Roles in Playbooks + +```yaml +# playbooks/provision.yml +--- +- name: Provision web servers + hosts: webservers + become: yes + + roles: + - common # 📦 Install common packages + - docker # 🐳 Install Docker + - app_deploy # 🚀 Deploy application +``` + +**🎯 Clean and simple!** + +```mermaid +flowchart LR + Playbook[📝 Playbook] --> Common[📦 common] + Playbook --> Docker[🐳 docker] + Playbook --> App[🚀 app_deploy] + Common --> Result[✅ Configured Server] + Docker --> Result + App --> Result +``` + +--- + +## 📍 Slide 24 – 🔐 Ansible Vault + +```bash +# 🔐 Create encrypted file +ansible-vault create group_vars/all.yml + +# 📝 Edit encrypted file +ansible-vault edit group_vars/all.yml + +# 👀 View encrypted file +ansible-vault view group_vars/all.yml +``` + +**🔐 Encrypted Content:** +```yaml +--- +# group_vars/all.yml (encrypted) +dockerhub_username: myuser +dockerhub_password: super_secret_token +app_secret_key: very_secret_key_123 +``` + +**🛠️ Using Vault:** +```bash +ansible-playbook playbook.yml --ask-vault-pass +# Or use password file (gitignored!) +ansible-playbook playbook.yml --vault-password-file .vault_pass +``` + +--- + +## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L5_MID + +--- + +## 📍 Slide 26 – 🔄 Section 4: Idempotency + +## ♾️ What is Idempotency? + +* 🔄 Same result whether run once or many times +* ✅ Safe to re-run playbooks +* 📊 Converges to desired state +* 🎯 No unintended side effects + +```mermaid +flowchart LR + Run1[🚀 First Run] --> State[✅ Desired State] + Run2[🚀 Second Run] --> State + Run3[🚀 Third Run] --> State +``` + +**🎨 Output Colors:** +* 🟢 **ok**: Already in desired state +* 🟡 **changed**: Made a change +* 🔴 **failed**: Task failed +* ⚫ **skipped**: Task skipped + +--- + +## 📍 Slide 27 – 🔄 Idempotent vs Non-Idempotent + +```yaml +# ❌ Non-idempotent (shell command) +- name: Add line to file + shell: echo "config=value" >> /etc/app.conf + # 💥 Adds line EVERY time! + +# ✅ Idempotent (lineinfile module) +- name: Ensure line in file + lineinfile: + path: /etc/app.conf + line: "config=value" + state: present + # ✅ Only adds if missing! +``` + +**📦 Idempotent Modules:** +| Module | Purpose | Idempotent? | +|--------|---------|-------------| +| `apt` | Install packages | ✅ Yes | +| `service` | Manage services | ✅ Yes | +| `file` | Manage files | ✅ Yes | +| `shell` | Run commands | ❌ Usually no | +| `command` | Run commands | ❌ Usually no | + +--- + +## 📍 Slide 28 – 🧪 Testing Idempotency + +```mermaid +flowchart TD + Run1[🚀 First Run] --> Changed[🟡 changed: 15] + Run2[🚀 Second Run] --> Ok[🟢 changed: 0] + Ok --> Idempotent[✅ Playbook is Idempotent!] +``` + +**🧪 Test Process:** +1. 🚀 Run playbook first time → many changes +2. 🚀 Run playbook second time → **zero changes** +3. ✅ If second run shows `changed: 0`, you're idempotent + +**📊 Example Output:** +``` +PLAY RECAP +server1 : ok=15 changed=0 unreachable=0 failed=0 +``` + +--- + +## 📍 Slide 29 – 📊 Configuration Management Metrics + +| 📊 Metric | 📏 Measures | 🏆 Target | +|-----------|------------|---------| +| ⏱️ **Config Time** | Time to configure server | < 15 minutes | +| 🔄 **Drift Rate** | Servers with drift | 0% | +| ✅ **Idempotency** | Re-run changes | 0 changes | +| 📜 **Compliance** | Servers meeting policy | 100% | + +> 📚 These metrics indicate configuration management maturity. + +**🤔 Question:** What happens when you re-run your playbooks? + +--- + +## 📍 Slide 30 – 🌊 From Manual to Automated + +```mermaid +flowchart LR + subgraph 😱 Manual + SSH[🔌 SSH Sessions] + Commands[💻 Run Commands] + Hope[🙏 Hope It Works] + end + subgraph 🤖 Automated + Playbook[📝 Playbooks] + Roles[📦 Roles] + Consistent[✅ Consistent] + end + Manual -->|🚀 Ansible| Automated +``` + +**🎯 Automation State:** +* ⚡ Any server configurable in minutes +* 🔄 All changes through playbooks +* 📈 Teams deploy configuration confidently + +--- + +## 📍 Slide 31 – 🏢 Section 5: Ansible in Real Life + +## 📅 A Day with Ansible + +**☀️ Morning:** +* 📊 Review Ansible PR for new role +* 👀 Check syntax with `ansible-lint` +* ✅ Merge to main branch + +**🌤️ Afternoon:** +* 🚨 Security patch needed +* 🔧 Update role with new package version +* 🚀 Run playbook — **all servers patched in 10 minutes** + +**🌙 Evening:** +* 🤖 Scheduled playbook runs +* 📊 Compliance reports generated +* 🏠 Go home confident + +--- + +## 📍 Slide 32 – 👥 Team Ansible Workflow + +| 👤 Role | 🎯 Ansible Responsibility | +|---------|----------------------| +| 🔧 **DevOps** | Write and maintain roles | +| 👨‍💻 **Developer** | Request configuration changes | +| 🛡️ **Security** | Review roles for compliance | +| 📊 **Audit** | Verify configuration state | + +**🔗 Common Workflow:** +* 📝 Create branch with role changes +* 🔍 CI runs `ansible-lint` and syntax check +* 👀 Team reviews the changes +* ✅ Merge triggers playbook run + +--- + +## 📍 Slide 33 – 🤝 Ansible + Terraform + +```mermaid +flowchart LR + TF[🌍 Terraform] -->|Creates| VM[🖥️ Virtual Machine] + VM -->|IP Address| Ansible[🔧 Ansible] + Ansible -->|Configures| Ready[✅ Ready Server] +``` + +**🤝 Integration Patterns:** +* 🌍 Terraform provisions infrastructure +* 📋 Terraform outputs inventory +* 🔧 Ansible configures servers +* 🔄 Both stored in Git + +**💡 Best Practice:** +* 🏗️ Terraform = **what** exists +* 🔧 Ansible = **how** it's configured + +--- + +## 📍 Slide 34 – 📈 Career Path: Ansible Skills + +```mermaid +flowchart LR + Junior[🌱 Junior: Basic Playbooks] --> Mid[💼 Mid: Roles & Vault] + Mid --> Senior[⭐ Senior: Dynamic Inventory & CI/CD] + Senior --> Principal[🏆 Principal: Enterprise Automation] +``` + +**🛠️ Skills to Build:** +* 📝 YAML and Jinja2 fluency +* 📦 Role design patterns +* 🔐 Vault and secrets management +* 🌐 Dynamic inventory +* 🔄 CI/CD integration + +--- + +## 📍 Slide 35 – 🌍 Real Company Examples + +**🏢 Enterprise Users:** +* 🏦 **NASA**: Manages thousands of servers +* 🎮 **EA Games**: Game server configuration +* 🛒 **Walmart**: Retail infrastructure + +**☁️ Cloud Native:** +* 🔍 **Twitter**: Configuration at scale +* 📦 **Lyft**: Microservices configuration +* 🎬 **Apple**: Device management + +**📊 Stats:** +* 🌍 **#1** open-source automation tool +* 📦 **30,000+** modules available +* 🏢 **Most used** by Fortune 100 + +--- + +## 📍 Slide 36 – 🎯 Section 6: Reflection + +## 📝 Key Takeaways + +1. 🔧 **Ansible = Agentless configuration management** +2. 📦 **Roles organize** reusable automation +3. 🔄 **Idempotency** makes re-runs safe +4. 🔔 **Handlers** efficiently manage service restarts +5. 🔐 **Vault encrypts** sensitive data + +> 💡 Ansible playbooks are living documentation of your infrastructure. + +--- + +## 📍 Slide 37 – 🧠 The Mindset Shift + +| 😰 Old Mindset | 🚀 Ansible Mindset | +|---------------|------------------| +| 🙅 "SSH and run commands" | 📝 "Define in playbook" | +| 🚫 "Each server is unique" | 🔄 "All servers are identical" | +| 👉 "Document the steps" | 📄 "Code IS documentation" | +| 😨 "Updates are risky" | 💪 "Updates are automated" | +| 💻 "Works on my server" | 🌍 "Works on all servers" | + +> ❓ Which mindset describes your team? + +--- + +## 📍 Slide 38 – ✅ Your Progress + +## 🎓 What You Now Understand + +* ✅ Ansible's agentless architecture +* ✅ How to write playbooks and roles +* ✅ Why idempotency matters +* ✅ How handlers improve efficiency +* ✅ Securing secrets with Vault + +> 🚀 **You're ready for Lab 5: Ansible Fundamentals** + +--- + +## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L5_POST + +--- + +## 📍 Slide 40 – 🚀 What Comes Next + +## 📚 Next Lecture: Continuous Deployment with Ansible + +* 🚀 Application deployment roles +* 🐳 Docker Compose templates +* 🏷️ Tags and blocks +* 💻 Hands-on: Deploying your app with Ansible + +**🎉 Your configuration automation journey continues.** + +> 🔧 From manual to automated — one playbook at a time. + +```mermaid +flowchart LR + You[👤 You] --> Ansible[🔧 Ansible Skills] + Ansible --> Automated[🤖 Automated Config] + Automated --> Career[🚀 Career Growth] +``` + +**👋 See you in the next lecture!** + +--- + +## 📚 Resources & Further Reading + +**📕 Books:** +* 📖 *Ansible: Up & Running* — Lorin Hochstein +* 📖 *Ansible for DevOps* — Jeff Geerling +* 📖 *The Practice of Cloud System Administration* — Limoncelli + +**🔗 Links:** +* 🌐 [Ansible Documentation](https://docs.ansible.com/) +* 🌐 [Ansible Galaxy](https://galaxy.ansible.com/) +* 🌐 [Ansible Best Practices](https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html) + +--- diff --git a/lectures/lec6.md b/lectures/lec6.md new file mode 100644 index 0000000000..a78ba20b5b --- /dev/null +++ b/lectures/lec6.md @@ -0,0 +1,887 @@ +# 📌 Lecture 6 — Continuous Deployment: Advanced Ansible + +## 📍 Slide 1 – 🚀 Welcome to Continuous Deployment + +* 🌍 **Configuration is automated** — but what about deployments? +* 😰 Manual deployments are slow, error-prone, and risky +* 🚀 **CI/CD with Ansible** = automated, repeatable, safe deployments +* 🎯 This lecture: master blocks, tags, Docker Compose, and CI/CD integration + +```mermaid +flowchart LR + Code[💻 Code Push] -->|CI/CD| Build[🔨 Build] + Build --> Deploy[🚀 Ansible Deploy] + Deploy --> Running[✅ Running in Production] +``` + +--- + +## 📍 Slide 2 – 🎯 What You Will Learn + +* ✅ Use blocks for error handling and task grouping +* ✅ Apply tags for selective execution +* ✅ Deploy applications with Docker Compose templates +* ✅ Integrate Ansible with GitHub Actions + +**🎓 Learning Outcomes:** +| # | Outcome | +|---|---------| +| 1 | 🧠 Implement blocks with rescue and always | +| 2 | 🔍 Design effective tag strategies | +| 3 | 🛠️ Template Docker Compose files with Jinja2 | +| 4 | 🗺️ Automate deployments with CI/CD | + +--- + +## 📍 Slide 3 – 📋 How This Lecture Works + +* 📚 **Advanced patterns** — production-ready practices +* 🎮 **Real-world scenarios** — deployment challenges +* 📝 **3 quiz checkpoints**: PRE / MID / POST +* 🛠️ **CI/CD integration**: GitHub Actions + Ansible + +**⏱️ Lecture Structure:** +``` +Section 0: Introduction (now) → 📝 PRE Quiz +Section 1: The Deployment Problem +Section 2: Blocks & Error Handling +Section 3: Tags & Selective Execution → 📝 MID Quiz +Section 4: Docker Compose Deployment +Section 5: CI/CD Integration +Section 6: Reflection → 📝 POST Quiz +``` + +--- + +## 📍 Slide 4 – ❓ The Big Question + +* 📊 **46%** of organizations deploy weekly or faster +* ⏱️ Top performers deploy **multiple times per day** +* 💥 **80%** of outages caused by changes (deploys, configs) + +> 💬 *"We deploy on Fridays and pray over the weekend"* — Nobody should say this + +**🤔 Think about it:** +* How often does your team deploy? +* How long does a deployment take? +* Can you roll back in under 5 minutes? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L6_PRE + +--- + +## 📍 Slide 6 – 🔥 Section 1: The Deployment Problem + +* 🎰 **Deployments = high-risk events** +* 📋 Manual steps, checklists, approval gates +* 🌙 Deploy only during "maintenance windows" +* 💥 Result: **fear of deploying** + +```mermaid +flowchart LR + Ready[✅ Code Ready] --> Wait[📅 Wait for Window] + Wait --> Manual[📋 Manual Steps] + Manual --> Pray[🙏 Hope It Works] + Pray -->|💥 Fail| Rollback[😱 Manual Rollback] + Pray -->|✅ Success| Relief[😮‍💨 Temporary Relief] +``` + +--- + +## 📍 Slide 7 – 💥 Deployment Failures + +* 🔧 Wrong version deployed +* 📦 Missing dependencies +* ⚙️ Configuration mismatch +* 💀 Partial deployment (some servers updated, some not) + +```mermaid +flowchart TD + Deploy[🚀 Deploy Started] --> S1[🖥️ Server 1: ✅ Updated] + Deploy --> S2[🖥️ Server 2: ❌ Failed] + Deploy --> S3[🖥️ Server 3: 🔄 Pending] + S1 --> Inconsistent[😱 Inconsistent State] + S2 --> Inconsistent + S3 --> Inconsistent +``` + +**📊 The Numbers:** +* 🔍 **60%** of outages caused by bad deployments +* ⏱️ Average recovery time: **4+ hours** +* 💰 Cost per hour of downtime: **$300,000+** + +--- + +## 📍 Slide 8 – 😱 Rollback Nightmares + +* 📋 "Just revert the code" — but what about: + * 💾 Database migrations? + * ⚙️ Configuration changes? + * 📦 Dependencies? +* 🙈 No automated rollback = manual scramble +* 💀 Hours of downtime + +> ⚠️ **If you can't roll back quickly, you shouldn't deploy** + +**😰 Signs of Rollback Problems:** +* 🔇 "We've never actually tested rollback" +* 📝 Rollback requires manual steps +* 🐌 "Rollback takes longer than fixing forward" +* 🚪 Nobody knows the rollback procedure + +--- + +## 📍 Slide 9 – 😨 All-or-Nothing Deploys + +* 📅 Big-bang releases every few months +* 🎰 Everything changes at once +* 📋 Impossible to isolate failures +* 💀 If it fails, everything fails + +> ⚠️ **Large releases = large risk** + +**💬 Discussion:** Would you rather deploy 100 changes once or 1 change 100 times? + +--- + +## 📍 Slide 10 – 💸 The Cost of Manual Deployment + +| 🔥 Problem | 💥 Impact | +|------------|-----------| +| 🐢 Slow deployments | Features delayed | +| 📋 Manual errors | Outages, rollbacks | +| 👉 Inconsistent process | "Works for Alice, not Bob" | +| 🙈 Fear of deploying | Innovation stalls | + +**📈 Elite vs Low Performers:** +| Metric | 🏆 Elite | 😰 Low | +|--------|---------|-------| +| Deploy frequency | Multiple/day | Monthly | +| Lead time | < 1 hour | 1-6 months | +| Change failure rate | 0-15% | 46-60% | +| Recovery time | < 1 hour | 1 week+ | + +--- + +## 📍 Slide 11 – 💡 Section 2: Blocks & Error Handling + +* 🧱 **Blocks** = group related tasks +* 🔄 **Rescue** = handle failures +* ✅ **Always** = run regardless of outcome +* 🎯 Production-ready error handling + +```mermaid +flowchart TD + Block[🧱 Block] --> Try[🎯 Try Tasks] + Try -->|✅ Success| Always[✅ Always] + Try -->|❌ Failure| Rescue[🔧 Rescue] + Rescue --> Always +``` + +--- + +## 📍 Slide 12 – 🧱 Block Syntax + +```yaml +- name: Deploy application with error handling + block: + - name: Pull latest image + docker_image: + name: "{{ app_image }}" + source: pull + + - name: Start container + docker_container: + name: "{{ app_name }}" + image: "{{ app_image }}" + state: started + + rescue: + - name: Log failure + debug: + msg: "Deployment failed! Rolling back..." + + - name: Notify team + uri: + url: "{{ slack_webhook }}" + method: POST + body: '{"text": "Deployment failed!"}' + + always: + - name: Cleanup temp files + file: + path: /tmp/deploy + state: absent +``` + +--- + +## 📍 Slide 13 – 🛡️ Block Benefits + +```mermaid +flowchart LR + subgraph Without Blocks + T1[Task 1] --> T2[Task 2] + T2 -->|❌ Fail| Stop[😱 Playbook Stops] + end + subgraph With Blocks + B1[🧱 Block] -->|❌ Fail| R1[🔧 Rescue] + R1 --> A1[✅ Always] + end +``` + +**🛡️ Advantages:** +* 🔄 Graceful error handling +* 📊 Cleanup runs even on failure +* 🔔 Notification on failure +* 🎯 Apply settings to multiple tasks + +```yaml +- name: Docker installation + block: + - name: Task 1 + - name: Task 2 + - name: Task 3 + become: yes # 🔐 Applied to all tasks + when: install_docker # 🔀 Condition for all + tags: + - docker # 🏷️ Tag for all +``` + +--- + +## 📍 Slide 14 – 🏷️ Section 3: Tags Strategy + +* 🏷️ **Tags** = label tasks for selective execution +* 🎯 Run only what you need +* ⏱️ Speed up development and testing +* 🔧 Isolate specific operations + +```bash +# Run only docker tasks +ansible-playbook site.yml --tags "docker" + +# Skip common tasks +ansible-playbook site.yml --skip-tags "common" + +# List available tags +ansible-playbook site.yml --list-tags +``` + +--- + +## 📍 Slide 15 – 🏷️ Tag Design Patterns + +```yaml +# roles/web_app/tasks/main.yml +- name: Application deployment + block: + - name: Pull image + docker_image: + name: "{{ app_image }}" + source: pull + + - name: Deploy container + docker_container: + name: "{{ app_name }}" + state: started + tags: + - app_deploy + - deploy + +- name: Application wipe + block: + - name: Stop container + docker_container: + name: "{{ app_name }}" + state: absent + when: web_app_wipe | bool + tags: + - web_app_wipe +``` + +**🏷️ Tag Categories:** +* 🚀 **deploy**: Deployment tasks +* 🧹 **wipe**: Cleanup tasks +* 📦 **packages**: Package installation +* ⚙️ **config**: Configuration only + +--- + +## 📍 Slide 16 – ⚠️ Wipe Logic Pattern + +```mermaid +flowchart TD + Check{🔍 web_app_wipe = true?} + Check -->|No| Skip[⏭️ Skip wipe tasks] + Check -->|Yes| TagCheck{🏷️ --tags web_app_wipe?} + TagCheck -->|No| Skip2[⏭️ Skip: tag not specified] + TagCheck -->|Yes| Wipe[🧹 Execute wipe] +``` + +**🛡️ Double Safety Mechanism:** +* 📊 **Variable gate**: `web_app_wipe: false` by default +* 🏷️ **Tag gate**: Must specify `--tags web_app_wipe` +* ✅ Both required to execute dangerous tasks + +```bash +# Normal deploy (wipe doesn't run) +ansible-playbook deploy.yml + +# Wipe only +ansible-playbook deploy.yml -e "web_app_wipe=true" --tags web_app_wipe + +# Clean reinstall (wipe + deploy) +ansible-playbook deploy.yml -e "web_app_wipe=true" +``` + +--- + +## 📍 Slide 17 – 🐳 Docker Compose Deployment + +```mermaid +flowchart LR + Template[📝 Template] -->|Jinja2| Compose[🐳 docker-compose.yml] + Compose --> Deploy[🚀 Deploy] + Deploy --> Running[✅ Running] +``` + +**🐳 Why Docker Compose with Ansible?** +* 📝 Declarative container configuration +* 🔄 Managed by templates (dynamic values) +* 🔧 Easy updates and rollbacks +* 📊 Multi-container applications + +--- + +## 📍 Slide 18 – 📝 Jinja2 Templates + +```yaml +# roles/web_app/templates/docker-compose.yml.j2 +version: '3.8' + +services: + {{ app_name }}: + image: {{ docker_image }}:{{ docker_tag }} + container_name: {{ app_name }} + ports: + - "{{ app_port }}:{{ app_internal_port }}" + environment: +{% for key, value in app_env.items() %} + {{ key }}: "{{ value }}" +{% endfor %} + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:{{ app_internal_port }}/health"] + interval: 30s + timeout: 10s + retries: 3 +``` + +**📊 Variables Used:** +* 📦 `app_name`: Container name +* 🐳 `docker_image`: Image repository +* 🏷️ `docker_tag`: Image version +* 🔌 `app_port`: Exposed port + +--- + +## 📍 Slide 19 – 🚀 Deploy with Docker Compose Module + +```yaml +# roles/web_app/tasks/main.yml +- name: Create application directory + file: + path: "{{ compose_project_dir }}" + state: directory + mode: '0755' + +- name: Template docker-compose file + template: + src: docker-compose.yml.j2 + dest: "{{ compose_project_dir }}/docker-compose.yml" + mode: '0644' + notify: restart app + +- name: Deploy with Docker Compose + community.docker.docker_compose_v2: + project_src: "{{ compose_project_dir }}" + state: present + pull: always + register: deploy_result + +- name: Verify deployment + uri: + url: "http://localhost:{{ app_port }}/health" + status_code: 200 + retries: 5 + delay: 10 +``` + +--- + +## 📍 Slide 20 – 🔗 Role Dependencies + +```yaml +# roles/web_app/meta/main.yml +--- +dependencies: + - role: docker + vars: + docker_users: + - "{{ ansible_user }}" +``` + +**🔗 Dependency Benefits:** +* 🔄 Automatic execution order +* 📦 Ensures prerequisites +* 🎯 Self-contained roles + +```mermaid +flowchart LR + WebApp[📦 web_app role] -->|depends on| Docker[🐳 docker role] + Docker --> Tasks[🔧 Docker tasks run first] + Tasks --> WebAppTasks[🚀 Web app tasks run second] +``` + +--- + +## 📍 Slide 21 – 📊 Multi-Environment Deployment + +```yaml +# vars/app_python.yml +app_name: devops-python +docker_image: username/devops-info-service +docker_tag: latest +app_port: 8000 + +# vars/app_bonus.yml +app_name: devops-go +docker_image: username/devops-info-service-go +docker_tag: latest +app_port: 8001 +``` + +```yaml +# playbooks/deploy_python.yml +--- +- name: Deploy Python Application + hosts: webservers + become: yes + vars_files: + - ../vars/app_python.yml + roles: + - web_app +``` + +**🔄 Same role, different variables!** + +--- + +## 📍 Slide 22 – 🤖 CI/CD Integration + +```mermaid +flowchart LR + Push[📤 Git Push] --> CI[🔄 GitHub Actions] + CI --> Lint[📋 ansible-lint] + Lint --> Deploy[🚀 ansible-playbook] + Deploy --> Verify[✅ Verification] +``` + +**🤖 CI/CD Benefits:** +* 🔄 Automatic deployments on push +* 📋 Linting catches errors early +* 🔐 Secure credential handling +* 📊 Audit trail of deployments + +--- + +## 📍 Slide 23 – 📝 GitHub Actions Workflow + +```yaml +# .github/workflows/ansible-deploy.yml +name: Ansible Deployment + +on: + push: + branches: [main] + paths: + - 'ansible/**' + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install ansible-lint + run: pip install ansible ansible-lint + - name: Run ansible-lint + run: ansible-lint ansible/playbooks/*.yml + + deploy: + needs: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup SSH + run: | + mkdir -p ~/.ssh + echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + - name: Deploy + run: | + cd ansible + echo "${{ secrets.VAULT_PASS }}" > .vault_pass + ansible-playbook playbooks/deploy.yml \ + --vault-password-file .vault_pass + rm .vault_pass +``` + +--- + +## 📍 Slide 24 – 🔐 Secrets in CI/CD + +```mermaid +flowchart TD + Secrets[🔐 GitHub Secrets] --> Workflow[🔄 Workflow] + Workflow --> TempFile[📄 Temp File] + TempFile --> Ansible[🔧 Ansible] + Ansible --> Delete[🗑️ Delete Temp File] +``` + +**🔐 Security Practices:** +* 📦 Store credentials in GitHub Secrets +* 📄 Write to temp file during run +* 🗑️ Delete immediately after use +* 🚫 Never echo secrets to logs + +```yaml +# Using secrets safely +- name: Deploy with Vault + env: + VAULT_PASS: ${{ secrets.ANSIBLE_VAULT_PASSWORD }} + run: | + echo "$VAULT_PASS" > /tmp/vault_pass + ansible-playbook playbook.yml --vault-password-file /tmp/vault_pass + rm /tmp/vault_pass # 🗑️ Cleanup! +``` + +--- + +## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L6_MID + +--- + +## 📍 Slide 26 – 📁 Section 4: Path Filters + +```yaml +on: + push: + paths: + - 'ansible/**' # 📁 Only ansible changes + - '!ansible/docs/**' # 📝 Exclude docs + - '.github/workflows/ansible-deploy.yml' +``` + +**📁 Path Filter Benefits:** +* ⚡ Faster CI (skip unnecessary runs) +* 💰 Lower costs (fewer minutes used) +* 🎯 Focused workflows + +```mermaid +flowchart TD + Push[📤 Push] --> Check{📁 ansible/** changed?} + Check -->|Yes| Run[🚀 Run Workflow] + Check -->|No| Skip[⏭️ Skip Workflow] +``` + +--- + +## 📍 Slide 27 – 📊 Deployment Metrics + +| 📊 Metric | 📏 Measures | 🏆 Target | +|-----------|------------|---------| +| ⏱️ **Deploy Time** | Push to production | < 15 minutes | +| 📦 **Deploy Frequency** | How often | Daily+ | +| ❌ **Failure Rate** | Failed deploys | < 15% | +| 🔄 **Rollback Time** | Recovery time | < 5 minutes | + +> 📚 These are DORA metrics for deployment performance. + +**🤔 Question:** How fast can you deploy and roll back? + +--- + +## 📍 Slide 28 – 🔄 Rollback Strategy + +```yaml +# Rollback by re-deploying previous version +- name: Rollback application + block: + - name: Stop current container + docker_container: + name: "{{ app_name }}" + state: stopped + + - name: Deploy previous version + community.docker.docker_compose_v2: + project_src: "{{ compose_project_dir }}" + state: present + vars: + docker_tag: "{{ rollback_tag }}" + + - name: Verify rollback + uri: + url: "http://localhost:{{ app_port }}/health" + status_code: 200 + retries: 3 + delay: 5 +``` + +**🔄 Rollback Options:** +* 🏷️ Deploy previous tag +* 📦 Docker Compose down/up +* 🔙 Git revert + CI/CD + +--- + +## 📍 Slide 29 – 🌊 From Manual to Automated Deployment + +```mermaid +flowchart LR + subgraph 😱 Manual + SSH[🔌 SSH to servers] + Commands[💻 Run commands] + Hope[🙏 Hope it works] + end + subgraph 🤖 Automated + Push[📤 Git push] + CI[🔄 CI/CD] + Deploy[🚀 Ansible] + end + Manual -->|🚀 Automate| Automated +``` + +**🎯 Automation State:** +* ⚡ Deploy in minutes, not hours +* 🔄 Every change through CI/CD +* 📈 Deploy with confidence + +--- + +## 📍 Slide 30 – 🏢 Section 5: Real World CI/CD + +## 📅 A Day with Automated Deployment + +**☀️ Morning:** +* 📊 Review deployment PR +* 👀 Check CI lint results +* ✅ Merge to main + +**🌤️ Afternoon:** +* 🤖 CI automatically deploys +* 📊 Monitoring shows healthy +* ☕ Coffee break + +**🌙 Evening:** +* 🚨 Bug found in production +* 🔙 Revert commit, CI deploys previous +* ⏱️ **5 minutes** to rollback + +--- + +## 📍 Slide 31 – 👥 Team Deployment Workflow + +| 👤 Role | 🎯 CI/CD Responsibility | +|---------|----------------------| +| 👨‍💻 **Developer** | Create PR, fix lint issues | +| 🔧 **DevOps** | Maintain workflows, roles | +| 👀 **Reviewer** | Approve changes | +| 🤖 **CI/CD** | Execute deployment | + +**🔗 GitOps Workflow:** +```mermaid +flowchart LR + PR[📝 Pull Request] --> Review[👀 Review] + Review --> Merge[✅ Merge] + Merge --> CI[🔄 CI/CD] + CI --> Deploy[🚀 Deploy] + Deploy --> Prod[🌐 Production] +``` + +--- + +## 📍 Slide 32 – 🔀 Deployment Strategies + +```mermaid +flowchart TD + subgraph Rolling + R1[🔄 Update 1 at a time] + end + subgraph Blue-Green + BG1[🔵 Blue: Current] + BG2[🟢 Green: New] + end + subgraph Canary + C1[🐤 Small % first] + C2[📊 Monitor] + C3[🚀 Full rollout] + end +``` + +| 🚀 Strategy | 🎯 Use Case | +|------------|----------| +| 🔄 **Rolling** | Gradual update, zero downtime | +| 🔵 **Blue-Green** | Instant switch, easy rollback | +| 🐤 **Canary** | Test with subset of users | + +--- + +## 📍 Slide 33 – 🧪 Deployment Verification + +```yaml +# Verify deployment success +- name: Wait for application + uri: + url: "http://{{ ansible_host }}:{{ app_port }}/health" + status_code: 200 + return_content: yes + register: health_check + until: health_check.status == 200 + retries: 10 + delay: 6 + +- name: Run smoke tests + command: "curl -f http://{{ ansible_host }}:{{ app_port }}/" + register: smoke_test + failed_when: smoke_test.rc != 0 + +- name: Log deployment success + debug: + msg: "✅ Deployment verified: {{ app_name }} is healthy" +``` + +--- + +## 📍 Slide 34 – 📈 Career Path: CD Skills + +```mermaid +flowchart LR + Junior[🌱 Junior: Manual deploys] --> Mid[💼 Mid: CI/CD pipelines] + Mid --> Senior[⭐ Senior: Zero-downtime strategies] + Senior --> Principal[🏆 Principal: Platform architecture] +``` + +**🛠️ Skills to Build:** +* 🔄 CI/CD pipeline design +* 🐳 Container orchestration +* 📊 Monitoring and alerting +* 🔙 Rollback strategies +* 🔐 Security in pipelines + +--- + +## 📍 Slide 35 – 🌍 Real Company Examples + +**🏢 Enterprise CD:** +* 📦 **Amazon**: Deploy every 11.7 seconds +* 🎬 **Netflix**: Canary deployments everywhere +* 🔍 **Google**: Feature flags for gradual rollout + +**☁️ CD Practices:** +* 🏦 **Stripe**: Shadow traffic for testing +* 📦 **Etsy**: 50+ deploys per day +* 🎮 **Spotify**: Squad-based ownership + +**📊 Stats:** +* 🚀 Elite teams deploy **on demand** +* ⏱️ Lead time: **less than 1 hour** +* 🔄 Recovery: **less than 1 hour** + +--- + +## 📍 Slide 36 – 🎯 Section 6: Reflection + +## 📝 Key Takeaways + +1. 🧱 **Blocks** enable graceful error handling +2. 🏷️ **Tags** allow selective execution +3. 🐳 **Docker Compose** templates for flexible deployments +4. 🔗 **Role dependencies** ensure proper ordering +5. 🤖 **CI/CD** automates the entire process + +> 💡 Small, frequent deployments are safer than big releases. + +--- + +## 📍 Slide 37 – 🧠 The Mindset Shift + +| 😰 Old Mindset | 🚀 CD Mindset | +|---------------|------------------| +| 🙅 "Deploy on weekends" | 🚀 "Deploy anytime" | +| 🚫 "Big releases quarterly" | 🔄 "Small releases daily" | +| 👉 "Manual verification" | 🤖 "Automated checks" | +| 😨 "Rollback is hard" | 💪 "Rollback in minutes" | +| 💻 "It works locally" | 🌍 "CI validates it" | + +> ❓ Which mindset describes your team? + +--- + +## 📍 Slide 38 – ✅ Your Progress + +## 🎓 What You Now Understand + +* ✅ Blocks with rescue and always +* ✅ Tag strategies for selective execution +* ✅ Docker Compose templates with Jinja2 +* ✅ Role dependencies and ordering +* ✅ CI/CD integration with GitHub Actions + +> 🚀 **You're ready for Lab 6: Advanced Ansible & CI/CD** + +--- + +## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L6_POST + +--- + +## 📍 Slide 40 – 🚀 What Comes Next + +## 📚 Next Lecture: Observability & Logging + +* 📋 Log aggregation with Loki +* 📊 Visualization with Grafana +* 🔍 LogQL query language +* 💻 Hands-on: Building a logging stack + +**🎉 Your continuous deployment journey continues.** + +> 🚀 From manual deploys to automated CI/CD — one commit at a time. + +```mermaid +flowchart LR + You[👤 You] --> CICD[🤖 CI/CD Skills] + CICD --> Automated[🚀 Automated Deploys] + Automated --> Career[🚀 Career Growth] +``` + +**👋 See you in the next lecture!** + +--- + +## 📚 Resources & Further Reading + +**📕 Books:** +* 📖 *Continuous Delivery* — Jez Humble +* 📖 *The DevOps Handbook* — Gene Kim et al. +* 📖 *Accelerate* — Nicole Forsgren + +**🔗 Links:** +* 🌐 [Ansible Blocks](https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html) +* 🌐 [Ansible Tags](https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html) +* 🌐 [GitHub Actions](https://docs.github.com/en/actions) + +--- diff --git a/lectures/lec7.md b/lectures/lec7.md new file mode 100644 index 0000000000..b00d0ad39f --- /dev/null +++ b/lectures/lec7.md @@ -0,0 +1,849 @@ +# 📌 Lecture 7 — Observability & Logging: From Blind to Insight + +## 📍 Slide 1 – 🚀 Welcome to Observability + +* 🌍 **Applications are running** — but what's happening inside? +* 😰 Without visibility, debugging is guesswork +* 🔍 **Observability** = understanding system state from outputs +* 🎯 This lecture: master logging with Loki, Promtail, and Grafana + +```mermaid +flowchart LR + App[📦 Application] -->|📋 Logs| Collect[🔧 Promtail] + Collect --> Store[💾 Loki] + Store --> View[📊 Grafana] + View --> Insight[💡 Insight] +``` + +--- + +## 📍 Slide 2 – 🎯 What You Will Learn + +* ✅ Understand the three pillars of observability +* ✅ Deploy Loki stack for log aggregation +* ✅ Query logs with LogQL +* ✅ Build effective log dashboards + +**🎓 Learning Outcomes:** +| # | Outcome | +|---|---------| +| 1 | 🧠 Differentiate logs, metrics, and traces | +| 2 | 🔍 Configure Loki 3.0 with TSDB storage | +| 3 | 🛠️ Write LogQL queries for filtering and aggregation | +| 4 | 🗺️ Design actionable log dashboards | + +--- + +## 📍 Slide 3 – 📋 How This Lecture Works + +* 📚 **Concepts + Configuration** — hands-on focus +* 🎮 **Real-world scenarios** — debugging production issues +* 📝 **3 quiz checkpoints**: PRE / MID / POST +* 🛠️ **Tool stack**: Loki + Promtail + Grafana + +**⏱️ Lecture Structure:** +``` +Section 0: Introduction (now) → 📝 PRE Quiz +Section 1: The Visibility Problem +Section 2: Observability Fundamentals +Section 3: Loki Stack Deep Dive → 📝 MID Quiz +Section 4: LogQL & Dashboards +Section 5: Production Logging +Section 6: Reflection → 📝 POST Quiz +``` + +--- + +## 📍 Slide 4 – ❓ The Big Question + +* 📊 **70%** of mean time to resolution is spent finding the problem +* ⏱️ Average time to detect issues: **hours to days** +* 💥 Without observability, debugging is **archaeology** + +> 💬 *"Users reported it's slow... but where?"* — Every on-call engineer, ever + +**🤔 Think about it:** +* How do you know your app is healthy? +* When users report issues, where do you look first? +* Can you trace a request through your system? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L7_PRE + +--- + +## 📍 Slide 6 – 🔥 Section 1: The Visibility Problem + +* 🙈 **No logs** = flying blind +* 📋 Logs scattered across servers +* 🔍 grep through SSH sessions +* 💥 Result: **hours spent finding problems** + +```mermaid +flowchart LR + Issue[🚨 Issue Reported] --> SSH1[🔌 SSH to Server 1] + SSH1 --> Grep1[🔍 grep logs] + Grep1 --> SSH2[🔌 SSH to Server 2] + SSH2 --> Grep2[🔍 grep logs] + Grep2 --> Hours[⏱️ Hours Later...] +``` + +--- + +## 📍 Slide 7 – 📋 Log Chaos + +* 📁 Logs in different formats +* 🖥️ Different locations per server +* 📅 Old logs deleted or rotated +* 💀 No correlation between services + +```mermaid +flowchart TD + App1[📦 App 1: JSON logs] + App2[📦 App 2: Plain text] + App3[📦 App 3: Custom format] + App1 --> Chaos[😱 No Unified View] + App2 --> Chaos + App3 --> Chaos +``` + +**📊 The Numbers:** +* 🔍 **73%** of engineers can't find logs quickly +* ⏱️ Average time to find relevant log: **15+ minutes** +* 💰 Cost of slow debugging: **$26,000/hour** (enterprise) + +--- + +## 📍 Slide 8 – 😱 "It's Working for Me" + +* 👥 Users report: *"App is slow"* +* 🤷 Team responds: *"Works for me"* +* 🔍 No data to prove either side +* 💀 Frustration all around + +> ⚠️ **Without observability, you can't prove anything** + +**😰 Signs of Poor Observability:** +* 🔇 "Check the server logs" (which server?) +* 📝 "It was working yesterday" (what changed?) +* 🐌 "Let's restart and see" (cargo cult debugging) +* 🚪 Blame instead of data + +**💬 Discussion:** How do you currently debug production issues? + +--- + +## 📍 Slide 9 – 🔥 The Alert Fatigue Problem + +* 🚨 Too many alerts = no alerts +* 📧 Inbox full of "warnings" +* 😴 Real issues get ignored +* 💀 On-call burnout + +> ⚠️ **Noise drowns out signal** + +```mermaid +flowchart LR + Alerts[🚨 1000 Alerts/day] --> Ignore[😴 Alert Fatigue] + Ignore --> Miss[🙈 Miss Real Issues] + Miss --> Outage[💥 Production Outage] +``` + +--- + +## 📍 Slide 10 – 💸 The Cost of Blind Operations + +| 🔥 Problem | 💥 Impact | +|------------|-----------| +| 🐢 Slow debugging | Hours/days to resolve | +| 📋 No correlation | Can't trace requests | +| 👉 Finger pointing | No data, just blame | +| 🙈 Hidden failures | Issues go unnoticed | + +**📈 Real Numbers:** +* 🏢 **MTTR without observability**: 4+ hours +* 🚀 **MTTR with observability**: < 30 minutes +* 💰 **ROI of observability**: 10x+ reduction in incident cost + +**💰 Cost Example:** +* 💵 1-hour outage: **$300,000** +* 🔍 Good observability: **$30/month** +* 🧮 Break-even: **first 6 seconds of prevented downtime** + +--- + +## 📍 Slide 11 – 💡 Section 2: What Observability Is + +* 🔍 **Observability** = understanding system state from external outputs +* 📊 **Three pillars**: Logs, Metrics, Traces +* 🎯 Answer: "Why is this happening?" +* 🚫 NOT just monitoring (which asks "Is it working?") + +```mermaid +flowchart TD + Obs[🔍 Observability] + Obs --> Logs[📋 Logs: What happened] + Obs --> Metrics[📊 Metrics: How much] + Obs --> Traces[🔗 Traces: Where/how long] +``` + +**📖 Definition:** +> *Observability is the ability to understand the internal state of a system by examining its external outputs — logs, metrics, and traces.* + +--- + +## 📍 Slide 12 – 📋 The Three Pillars + +| 📊 Pillar | 🎯 Answers | 🛠️ Tools | +|-----------|-----------|----------| +| 📋 **Logs** | What happened? | Loki, ELK | +| 📊 **Metrics** | How much/fast? | Prometheus | +| 🔗 **Traces** | Where did time go? | Jaeger, Tempo | + +```mermaid +flowchart LR + subgraph Logs + L1[📝 Error: Connection refused] + end + subgraph Metrics + M1[📈 99.9% availability] + end + subgraph Traces + T1[🔗 Request: 250ms total] + end +``` + +**🎯 Together they tell the full story** + +--- + +## 📍 Slide 13 – 📋 Logs: What Happened + +* 📝 **Events** with timestamps +* 🔍 Detailed context for debugging +* 📊 Can be structured (JSON) or unstructured +* ⚠️ High volume, high storage + +```json +{ + "timestamp": "2024-01-15T10:23:45Z", + "level": "ERROR", + "service": "user-api", + "message": "Database connection failed", + "error": "Connection refused", + "host": "server-1" +} +``` + +**🎯 Use logs when:** +* 🔍 Debugging specific errors +* 📋 Understanding request flow +* 🛡️ Security auditing + +--- + +## 📍 Slide 14 – 📊 Why Structured Logging? + +```mermaid +flowchart LR + subgraph ❌ Unstructured + U1[ERROR: Failed to connect to db at 10:23] + end + subgraph ✅ Structured + S1[JSON with fields] + end + U1 --> Hard[😰 Hard to parse] + S1 --> Easy[✅ Easy to query] +``` + +**❌ Unstructured:** +``` +ERROR 2024-01-15 10:23:45 Connection to database failed on server-1 +``` + +**✅ Structured (JSON):** +```json +{"timestamp":"2024-01-15T10:23:45Z","level":"ERROR","msg":"Connection failed","server":"server-1"} +``` + +**🎯 Benefits:** +* 🔍 Easy to filter and search +* 📊 Aggregate by any field +* 🤖 Machine-parseable + +--- + +## 📍 Slide 15 – ⚡ Loki vs ELK + +| 📋 Aspect | 📊 Loki | 🔍 ELK Stack | +|-----------|---------|-------------| +| 🏗️ Architecture | Lightweight | Heavy | +| 💾 Storage | Index labels only | Full-text index | +| 📊 Query | LogQL | Lucene | +| 💰 Cost | Low (storage) | High (compute) | +| 🎯 Best for | Cloud-native | Enterprise search | + +```mermaid +flowchart LR + Loki[📊 Loki] -->|Labels| Index1[🏷️ Small Index] + ELK[🔍 ELK] -->|Full Text| Index2[📚 Large Index] + Index1 --> Cost1[💰 Low Cost] + Index2 --> Cost2[💸 High Cost] +``` + +> 🔥 **Loki**: "Like Prometheus, but for logs" + +--- + +## 📍 Slide 16 – 🎮 Section 3: Loki Stack Deep Dive + +## 🏗️ Loki Architecture + +* 💾 **Loki**: Log storage (index + chunks) +* 🔧 **Promtail**: Log collector (agent) +* 📊 **Grafana**: Visualization + +```mermaid +flowchart LR + App1[📦 App 1] --> Promtail[🔧 Promtail] + App2[📦 App 2] --> Promtail + Promtail -->|Push| Loki[💾 Loki] + Loki --> Grafana[📊 Grafana] +``` + +**🎮 Let's build a logging stack.** + +--- + +## 📍 Slide 17 – 💾 Loki 3.0 Features + +* 🚀 **TSDB index**: 10x faster queries +* 📊 **Structured metadata**: First-class support +* 💾 **Better compression**: Lower storage costs +* 🔍 **Schema v13**: Latest and recommended + +```yaml +# loki/config.yml +schema_config: + configs: + - from: 2024-01-01 + store: tsdb # 🚀 New fast store + object_store: filesystem + schema: v13 # 📊 Latest schema + index: + prefix: index_ + period: 24h +``` + +**🎯 Always use TSDB for new deployments!** + +--- + +## 📍 Slide 18 – ⚙️ Loki Configuration + +```yaml +# loki/config.yml +auth_enabled: false + +server: + http_listen_port: 3100 + +common: + path_prefix: /loki + storage: + filesystem: + chunks_directory: /loki/chunks + rules_directory: /loki/rules + replication_factor: 1 + ring: + instance_addr: 127.0.0.1 + kvstore: + store: inmemory + +limits_config: + retention_period: 168h # 🗓️ 7 days +``` + +**🔑 Key Settings:** +* 🔐 `auth_enabled`: False for testing +* 💾 `storage`: Where logs are stored +* 🗓️ `retention_period`: How long to keep logs + +--- + +## 📍 Slide 19 – 🔧 Promtail Configuration + +```yaml +# promtail/config.yml +server: + http_listen_port: 9080 + +positions: + filename: /tmp/positions.yaml + +clients: + - url: http://loki:3100/loki/api/v1/push + +scrape_configs: + - job_name: docker + docker_sd_configs: + - host: unix:///var/run/docker.sock + refresh_interval: 5s + relabel_configs: + - source_labels: ['__meta_docker_container_name'] + regex: '/(.*)' + target_label: 'container' +``` + +**🔑 Key Components:** +* 📋 `positions`: Track what's been read +* 🔗 `clients`: Where to send logs +* 🐳 `docker_sd_configs`: Auto-discover containers + +--- + +## 📍 Slide 20 – 🐳 Docker Compose Stack + +```yaml +# docker-compose.yml +version: '3.8' + +services: + loki: + image: grafana/loki:3.0.0 + ports: + - "3100:3100" + volumes: + - ./loki/config.yml:/etc/loki/config.yml + - loki-data:/loki + command: -config.file=/etc/loki/config.yml + + promtail: + image: grafana/promtail:3.0.0 + volumes: + - ./promtail/config.yml:/etc/promtail/config.yml + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /var/run/docker.sock:/var/run/docker.sock:ro + command: -config.file=/etc/promtail/config.yml + + grafana: + image: grafana/grafana:11.3.0 + ports: + - "3000:3000" + volumes: + - grafana-data:/var/lib/grafana +``` + +--- + +## 📍 Slide 21 – 🏷️ Labels: The Key Concept + +```mermaid +flowchart TD + Log[📋 Log Entry] --> Labels[🏷️ Labels] + Labels --> App[app=web-api] + Labels --> Env[env=production] + Labels --> Level[level=error] + App --> Query[🔍 Query by Labels] + Env --> Query + Level --> Query +``` + +**🏷️ Labels = How Loki indexes logs** + +```logql +# Query logs by labels +{app="web-api", env="production"} + +# Filter errors +{app="web-api"} |= "error" +``` + +**⚠️ Label Best Practices:** +* 🔢 Keep cardinality low (< 10 values per label) +* 🚫 Never use high-cardinality fields (user IDs, request IDs) +* 🏷️ Use for: app name, environment, service + +--- + +## 📍 Slide 22 – 🔍 LogQL Basics + +```mermaid +flowchart LR + Selector[🏷️ Stream Selector] --> Filter[🔍 Line Filter] + Filter --> Parser[📊 Parser] + Parser --> Result[📋 Results] +``` + +**🔍 Query Structure:** +```logql +{label="value"} |= "filter" | json | field="value" +``` + +**📋 Examples:** +```logql +# All logs from container +{container="web-api"} + +# Errors only +{container="web-api"} |= "error" + +# Parse JSON, filter by level +{container="web-api"} | json | level="ERROR" + +# Count errors per minute +rate({container="web-api"} |= "error" [1m]) +``` + +--- + +## 📍 Slide 23 – 📊 LogQL Operators + +| 🔧 Operator | 🎯 Purpose | 📝 Example | +|-------------|----------|---------| +| `\|=` | Contains | `\|= "error"` | +| `!=` | Not contains | `!= "debug"` | +| `\|~` | Regex match | `\|~ "error\|warn"` | +| `\| json` | Parse JSON | `\| json` | +| `\| logfmt` | Parse logfmt | `\| logfmt` | +| `rate()` | Logs per second | `rate({app="x"}[5m])` | + +**📊 Aggregation:** +```logql +# Logs per second by container +sum by (container) (rate({job="docker"}[1m])) + +# Count by level +sum by (level) (count_over_time({app="web"} | json [5m])) +``` + +--- + +## 📍 Slide 24 – 🐍 Structured Logging in Python + +```python +import logging +import json +from datetime import datetime + +class JSONFormatter(logging.Formatter): + def format(self, record): + log_obj = { + "timestamp": datetime.utcnow().isoformat() + "Z", + "level": record.levelname, + "message": record.getMessage(), + "logger": record.name, + } + if record.exc_info: + log_obj["exception"] = self.formatException(record.exc_info) + return json.dumps(log_obj) + +# Setup +handler = logging.StreamHandler() +handler.setFormatter(JSONFormatter()) +logger = logging.getLogger() +logger.addHandler(handler) +logger.setLevel(logging.INFO) + +# Usage +logger.info("User logged in", extra={"user_id": 123}) +``` + +--- + +## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L7_MID + +--- + +## 📍 Slide 26 – 📊 Section 4: Building Dashboards + +## 🎨 Dashboard Design Principles + +* 🎯 **Purpose**: What question does it answer? +* 📊 **Hierarchy**: Most important at top +* 🎨 **Color**: Red = bad, Green = good +* 🔄 **Refresh**: Auto-refresh for real-time + +```mermaid +flowchart TD + Top[🚨 Alerts & Errors] + Top --> Middle[📊 Request Rates] + Middle --> Bottom[📋 Log Stream] +``` + +--- + +## 📍 Slide 27 – 📊 Essential Log Panels + +**1️⃣ Log Stream (Logs visualization)** +```logql +{app=~"devops-.*"} +``` + +**2️⃣ Error Rate (Time series)** +```logql +sum by (app) (rate({app=~"devops-.*"} |= "ERROR" [1m])) +``` + +**3️⃣ Request Rate (Time series)** +```logql +sum by (app) (rate({app=~"devops-.*"} [1m])) +``` + +**4️⃣ Level Distribution (Pie chart)** +```logql +sum by (level) (count_over_time({app=~"devops-.*"} | json [5m])) +``` + +--- + +## 📍 Slide 28 – 📊 Grafana Panel Types + +| 📊 Type | 🎯 Use For | +|---------|----------| +| 📋 **Logs** | Raw log entries | +| 📈 **Time series** | Trends over time | +| 📊 **Stat** | Single values | +| 🥧 **Pie chart** | Distribution | +| 📋 **Table** | Structured data | +| 🌡️ **Gauge** | Current status | + +```mermaid +flowchart LR + Logs[📋 Logs] --> Debug[🔍 Debugging] + TimeSeries[📈 Time Series] --> Trends[📊 Trends] + Stat[📊 Stat] --> KPIs[🎯 KPIs] +``` + +--- + +## 📍 Slide 29 – 📊 Logging Metrics + +| 📊 Metric | 📏 Measures | 🏆 Target | +|-----------|------------|---------| +| 📋 **Log Volume** | Logs per second | Stable | +| ❌ **Error Rate** | Errors per minute | < 1% | +| ⏱️ **Query Time** | Time to find logs | < 30s | +| 💾 **Retention** | How long kept | 7+ days | + +> 📚 These metrics indicate logging health. + +**🤔 Question:** How quickly can you find relevant logs? + +--- + +## 📍 Slide 30 – 🌊 From Blind to Observable + +```mermaid +flowchart LR + subgraph 😱 Blind + SSH[🔌 SSH grep] + Guess[🤷 Guesswork] + Slow[⏱️ Hours] + end + subgraph 🔍 Observable + Dashboard[📊 Dashboard] + Query[🔍 LogQL] + Fast[⚡ Minutes] + end + Blind -->|🚀 Loki| Observable +``` + +**🎯 Observability State:** +* ⚡ Find issues in minutes, not hours +* 🔄 Unified view across all services +* 📈 Data-driven debugging + +--- + +## 📍 Slide 31 – 🏢 Section 5: Production Logging + +## 📅 A Day with Observability + +**☀️ Morning:** +* 📊 Check Grafana dashboard — all green ✅ +* 📋 Review overnight logs — no anomalies +* ☕ Coffee with confidence + +**🌤️ Afternoon:** +* 🚨 Alert: Error rate spike +* 🔍 LogQL: `{app="api"} |= "error" | json | level="ERROR"` +* 🔧 Found: Database timeout +* ⏱️ **10 minutes** to identify root cause + +**🌙 Evening:** +* 📊 Review error trends +* 📝 Create runbook for similar issues +* 🏠 Go home knowing you can debug remotely + +--- + +## 📍 Slide 32 – 👥 Team Logging Workflow + +| 👤 Role | 🎯 Observability Responsibility | +|---------|----------------------| +| 👨‍💻 **Developer** | Add structured logging | +| 🔧 **DevOps** | Maintain logging stack | +| 🛡️ **SRE** | Build dashboards, respond to alerts | +| 📊 **On-call** | Use logs for incident response | + +**🔗 Incident Response Flow:** +```mermaid +flowchart LR + Alert[🚨 Alert] --> Dashboard[📊 Dashboard] + Dashboard --> LogQL[🔍 LogQL Query] + LogQL --> RootCause[🎯 Root Cause] + RootCause --> Fix[🔧 Fix] +``` + +--- + +## 📍 Slide 33 – 🔐 Production Considerations + +```yaml +# Production settings +deploy: + resources: + limits: + memory: 1G + cpus: '1.0' + reservations: + memory: 512M + +healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3100/ready"] + interval: 10s + timeout: 5s + retries: 5 +``` + +**🛡️ Production Checklist:** +* 💾 Persistent volumes for data +* 🔐 Secure Grafana (disable anonymous) +* 📊 Resource limits on all services +* 🏥 Health checks enabled +* 🗓️ Retention policies configured + +--- + +## 📍 Slide 34 – 📈 Career Path: Observability Skills + +```mermaid +flowchart LR + Junior[🌱 Junior: Basic logging] --> Mid[💼 Mid: Structured logging & dashboards] + Mid --> Senior[⭐ Senior: Full observability stack] + Senior --> Principal[🏆 Principal: Observability strategy] +``` + +**🛠️ Skills to Build:** +* 📋 Structured logging patterns +* 🔍 LogQL/PromQL fluency +* 📊 Dashboard design +* 🚨 Alerting strategies +* 🔗 Distributed tracing + +--- + +## 📍 Slide 35 – 🌍 Real Company Examples + +**🏢 Observability Leaders:** +* 🎬 **Netflix**: Custom observability platform +* 🔍 **Google**: Invented Dapper (tracing) +* 📦 **Uber**: Jaeger (open-source tracing) + +**☁️ Modern Practices:** +* 🏦 **Stripe**: Structured logging everywhere +* 📦 **Spotify**: Centralized logging for 1000+ microservices +* 🎮 **Riot Games**: Real-time game telemetry + +**📊 Stats:** +* 🔍 **80%** of debugging time is finding problems +* ⏱️ Good observability reduces MTTR by **70%+** +* 💰 ROI: **10-100x** in reduced incident costs + +--- + +## 📍 Slide 36 – 🎯 Section 6: Reflection + +## 📝 Key Takeaways + +1. 🔍 **Observability = Logs + Metrics + Traces** +2. 📋 **Structured logging** enables powerful queries +3. 🏷️ **Labels** are how Loki indexes (keep cardinality low) +4. 📊 **LogQL** is your query language +5. 📈 **Dashboards** provide unified visibility + +> 💡 You can't fix what you can't see. Observability gives you eyes. + +--- + +## 📍 Slide 37 – 🧠 The Mindset Shift + +| 😰 Old Mindset | 🔍 Observable Mindset | +|---------------|------------------| +| 🙅 "SSH and grep" | 📊 "Query Grafana" | +| 🚫 "Check the logs somewhere" | 🔍 "All logs in one place" | +| 👉 "It's probably X" | 📊 "Data shows it's Y" | +| 😨 "Debugging takes hours" | ⚡ "Root cause in minutes" | +| 💻 "Works on my machine" | 🌍 "Production shows different" | + +> ❓ Which mindset describes your team? + +--- + +## 📍 Slide 38 – ✅ Your Progress + +## 🎓 What You Now Understand + +* ✅ The three pillars of observability +* ✅ Loki architecture and configuration +* ✅ LogQL query syntax +* ✅ Building effective dashboards +* ✅ Production logging best practices + +> 🚀 **You're ready for Lab 7: Loki Logging Stack** + +--- + +## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L7_POST + +--- + +## 📍 Slide 40 – 🚀 What Comes Next + +## 📚 Next Lecture: Monitoring with Prometheus + +* 📊 Metrics collection and storage +* 🔢 PromQL query language +* 📈 Application instrumentation +* 💻 Hands-on: Building metrics dashboards + +**🎉 Your observability journey continues.** + +> 🔍 From blind operations to insight — one query at a time. + +```mermaid +flowchart LR + You[👤 You] --> Obs[🔍 Observability Skills] + Obs --> Insight[💡 System Insight] + Insight --> Career[🚀 Career Growth] +``` + +**👋 See you in the next lecture!** + +--- + +## 📚 Resources & Further Reading + +**📕 Books:** +* 📖 *Observability Engineering* — Charity Majors +* 📖 *Distributed Systems Observability* — Cindy Sridharan +* 📖 *The Art of Monitoring* — James Turnbull + +**🔗 Links:** +* 🌐 [Grafana Loki Documentation](https://grafana.com/docs/loki/latest/) +* 🌐 [LogQL Reference](https://grafana.com/docs/loki/latest/query/) +* 🌐 [Promtail Configuration](https://grafana.com/docs/loki/latest/send-data/promtail/) + +--- diff --git a/lectures/lec8.md b/lectures/lec8.md new file mode 100644 index 0000000000..0b921df100 --- /dev/null +++ b/lectures/lec8.md @@ -0,0 +1,799 @@ +# 📌 Lecture 8 — Monitoring with Prometheus: From Guessing to Measuring + +## 📍 Slide 1 – 🚀 Welcome to Metrics Monitoring + +* 🌍 **Logs tell you what happened** — but how much and how fast? +* 😰 Without metrics, capacity planning is guesswork +* 📊 **Prometheus** = the industry standard for metrics +* 🎯 This lecture: master metrics collection, PromQL, and dashboards + +```mermaid +flowchart LR + App[📦 Application] -->|📊 Metrics| Prometheus[💾 Prometheus] + Prometheus --> Grafana[📊 Grafana] + Grafana --> Insight[💡 Insight] +``` + +--- + +## 📍 Slide 2 – 🎯 What You Will Learn + +* ✅ Understand metrics types and instrumentation +* ✅ Configure Prometheus for metrics collection +* ✅ Query metrics with PromQL +* ✅ Build effective monitoring dashboards + +**🎓 Learning Outcomes:** +| # | Outcome | +|---|---------| +| 1 | 🧠 Differentiate Counter, Gauge, Histogram | +| 2 | 🔍 Configure Prometheus scrape targets | +| 3 | 🛠️ Write PromQL queries for analysis | +| 4 | 🗺️ Design RED method dashboards | + +--- + +## 📍 Slide 3 – 📋 How This Lecture Works + +* 📚 **Concepts + Instrumentation** — hands-on focus +* 🎮 **Real-world scenarios** — performance monitoring +* 📝 **3 quiz checkpoints**: PRE / MID / POST +* 🛠️ **Methods**: RED, USE, Four Golden Signals + +**⏱️ Lecture Structure:** +``` +Section 0: Introduction (now) → 📝 PRE Quiz +Section 1: The Monitoring Problem +Section 2: Prometheus Fundamentals +Section 3: Application Instrumentation → 📝 MID Quiz +Section 4: PromQL & Dashboards +Section 5: Production Monitoring +Section 6: Reflection → 📝 POST Quiz +``` + +--- + +## 📍 Slide 4 – ❓ The Big Question + +* 📊 **83%** of organizations can't predict performance issues +* ⏱️ Average time to detect capacity problems: **too late** +* 💥 Without metrics, you're **reactive, not proactive** + +> 💬 *"Is the server slow or is it just me?"* — Everyone, always + +**🤔 Think about it:** +* How do you know if your app can handle more load? +* When did response times start degrading? +* How much headroom do you have? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L8_PRE + +--- + +## 📍 Slide 6 – 🔥 Section 1: The Monitoring Problem + +* 🤷 **No metrics** = can't measure performance +* 📊 Users complain before you know there's a problem +* 🔍 Can't identify bottlenecks +* 💥 Result: **reactive firefighting** + +```mermaid +flowchart LR + Users[👥 Users Complain] --> Support[📞 Support Ticket] + Support --> Team[👨‍💻 Team Investigates] + Team --> Guess[🤷 Guesswork] + Guess --> Hours[⏱️ Hours Later...] +``` + +--- + +## 📍 Slide 7 – 📊 Metrics vs Logs + +```mermaid +flowchart TD + subgraph 📋 Logs + L1[What happened?] + L2[Detailed events] + L3[High cardinality] + end + subgraph 📊 Metrics + M1[How much/fast?] + M2[Aggregated numbers] + M3[Low cardinality] + end +``` + +| 📋 Aspect | 📊 Metrics | 📝 Logs | +|-----------|----------|---------| +| 🎯 Question | How much? | What happened? | +| 📈 Volume | Low | High | +| 💾 Storage | Small | Large | +| 🔍 Analysis | Trends, alerts | Debugging | +| ⏱️ Retention | Long (months) | Short (days) | + +> 🔥 **Use both**: Logs for debugging, metrics for monitoring + +--- + +## 📍 Slide 8 – 😱 Alert Blindness + +* 🚨 No alerts = problems go unnoticed +* 📧 Too many alerts = alert fatigue +* 🔍 Wrong thresholds = false positives +* 💀 On-call burnout + +> ⚠️ **Good metrics = actionable alerts** + +```mermaid +flowchart LR + NoMetrics[🙈 No Metrics] --> NoAlerts[🔇 No Alerts] + NoAlerts --> UserReports[👥 Users Report] + UserReports --> Scramble[😱 Scramble] +``` + +--- + +## 📍 Slide 9 – 😨 Capacity Planning Without Metrics + +* 📅 "We need more servers" — but how many? +* 🔮 Crystal ball capacity planning +* 💰 Over-provision (waste money) or under-provision (outages) +* 💀 No data to justify decisions + +> ⚠️ **Without metrics, capacity planning is gambling** + +**💬 Discussion:** How does your team plan capacity? + +--- + +## 📍 Slide 10 – 💸 The Cost of Blind Monitoring + +| 🔥 Problem | 💥 Impact | +|------------|-----------| +| 🐢 No baseline | Can't detect degradation | +| 📊 No trends | Can't predict growth | +| 👉 No attribution | Can't identify bottlenecks | +| 🙈 No thresholds | Can't alert proactively | + +**📈 Real Numbers:** +* 🏢 **Reactive incident detection**: Users report first (30+ min delay) +* 🚀 **Proactive with metrics**: Alert in seconds +* 💰 **Cost of 30-minute delay**: $150,000+ (enterprise) + +--- + +## 📍 Slide 11 – 💡 Section 2: What Prometheus Is + +* 📊 **Time-series database** for metrics +* 🔄 **Pull-based** model — scrapes targets +* 📈 **PromQL** — powerful query language +* 🎯 Industry standard for cloud-native monitoring + +```mermaid +flowchart LR + App1[📦 App /metrics] --> Prometheus[💾 Prometheus] + App2[📦 Service /metrics] --> Prometheus + Prometheus -->|⏰ Every 15s| Scrape[🔄 Pull Metrics] +``` + +**📖 Definition:** +> *Prometheus is an open-source monitoring system that collects metrics from targets by scraping HTTP endpoints, stores them in a time-series database, and provides a powerful query language (PromQL) for analysis.* + +--- + +## 📍 Slide 12 – 🔄 Pull vs Push Model + +```mermaid +flowchart TD + subgraph Pull (Prometheus) + P1[💾 Prometheus] -->|🔄 Scrape| T1[📦 Target] + P1 -->|🔄 Scrape| T2[📦 Target] + end + subgraph Push (StatsD) + S1[📦 App] -->|📤 Push| D1[💾 Collector] + S2[📦 App] -->|📤 Push| D1 + end +``` + +**🔄 Pull Benefits:** +* 🔍 Prometheus controls the rate +* ✅ Know immediately if target is down (scrape fails) +* 🎯 Apps don't need to know about monitoring +* 🔧 Easy service discovery + +--- + +## 📍 Slide 13 – 🏗️ Prometheus Architecture + +```mermaid +flowchart TD + Targets[📦 Targets /metrics] --> Prometheus[💾 Prometheus TSDB] + Prometheus --> AlertManager[🚨 AlertManager] + Prometheus --> Grafana[📊 Grafana] + Prometheus --> API[🔗 HTTP API] + AlertManager --> Slack[💬 Slack] + AlertManager --> PagerDuty[📟 PagerDuty] +``` + +| 🧱 Component | 🎯 Purpose | +|-------------|----------| +| 💾 **Prometheus** | Scrape, store, query | +| 📦 **Targets** | Expose /metrics endpoint | +| 🚨 **AlertManager** | Handle alerts | +| 📊 **Grafana** | Visualization | + +--- + +## 📍 Slide 14 – 📊 Metric Types + +```mermaid +flowchart LR + Counter[🔢 Counter] --> Always[Only goes UP] + Gauge[📊 Gauge] --> UpDown[Goes up AND down] + Histogram[📈 Histogram] --> Distribution[Value distribution] +``` + +| 📊 Type | 🎯 Use For | 📝 Example | +|---------|----------|---------| +| 🔢 **Counter** | Cumulative events | Total requests | +| 📊 **Gauge** | Current value | Temperature, memory | +| 📈 **Histogram** | Distribution | Request latency | +| 📊 **Summary** | Percentiles | Pre-calculated p95 | + +--- + +## 📍 Slide 15 – 🔢 Counter Deep Dive + +```python +from prometheus_client import Counter + +# 🔢 Counter: Only goes up +http_requests_total = Counter( + 'http_requests_total', + 'Total HTTP requests', + ['method', 'endpoint', 'status'] +) + +# Usage +http_requests_total.labels(method='GET', endpoint='/', status='200').inc() +``` + +**📊 Query Patterns:** +```promql +# Total requests +http_requests_total + +# Requests per second (rate over 5m) +rate(http_requests_total[5m]) + +# Requests per second by endpoint +sum by (endpoint) (rate(http_requests_total[5m])) +``` + +**⚠️ Counter Rule:** Use `rate()` to get per-second values + +--- + +## 📍 Slide 16 – 🎮 Section 3: Application Instrumentation + +## 🐍 Python prometheus_client + +```python +from prometheus_client import Counter, Gauge, Histogram, generate_latest +from flask import Flask, Response + +app = Flask(__name__) + +# 📊 Define metrics +requests = Counter('http_requests', 'Total requests', ['method', 'path']) +latency = Histogram('http_latency_seconds', 'Request latency', ['path']) +in_progress = Gauge('http_in_progress', 'Requests in progress') + +@app.route('/metrics') +def metrics(): + return Response(generate_latest(), content_type='text/plain') +``` + +**🎮 Let's instrument an application.** + +--- + +## 📍 Slide 17 – 📊 Histogram Deep Dive + +```python +from prometheus_client import Histogram + +# 📈 Histogram with buckets +request_latency = Histogram( + 'http_request_duration_seconds', + 'Request latency in seconds', + ['method', 'endpoint'], + buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 5.0] # 🪣 Custom buckets +) + +# Usage +with request_latency.labels(method='GET', endpoint='/').time(): + # ... handle request ... + pass +``` + +**📊 Query Patterns:** +```promql +# 95th percentile latency +histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) + +# Average latency +rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m]) +``` + +--- + +## 📍 Slide 18 – 📈 The RED Method + +```mermaid +flowchart LR + R[🔴 Rate] --> Requests[Requests per second] + E[🟡 Errors] --> Failures[Error rate] + D[🔵 Duration] --> Latency[Response time] +``` + +**📊 RED Method for Request-Driven Services:** + +| 📊 Metric | 🎯 Question | 📝 PromQL | +|-----------|----------|---------| +| 🔴 **Rate** | How busy? | `rate(requests[5m])` | +| 🟡 **Errors** | How often failing? | `rate(errors[5m])` | +| 🔵 **Duration** | How slow? | `histogram_quantile(0.95, ...)` | + +**🎯 If you monitor only 3 things, monitor these!** + +--- + +## 📍 Slide 19 – 📈 The USE Method + +```mermaid +flowchart LR + U[📊 Utilization] --> HowMuch[% resource busy] + S[📊 Saturation] --> Queuing[Extra work waiting] + E[📊 Errors] --> Failures[Error count] +``` + +**📊 USE Method for Resources (CPU, Memory, Disk):** + +| 📊 Metric | 🎯 Example | +|-----------|----------| +| 📊 **Utilization** | CPU at 80% | +| 📊 **Saturation** | 10 requests queued | +| 📊 **Errors** | Disk I/O errors | + +**🎯 USE for resources, RED for services** + +--- + +## 📍 Slide 20 – ⚙️ Prometheus Configuration + +```yaml +# prometheus/prometheus.yml +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + + - job_name: 'app' + static_configs: + - targets: ['app-python:8000'] + metrics_path: '/metrics' + + - job_name: 'loki' + static_configs: + - targets: ['loki:3100'] +``` + +**🔑 Key Settings:** +* ⏱️ `scrape_interval`: How often to collect (15s default) +* 🎯 `targets`: What to scrape +* 📍 `metrics_path`: Where metrics are exposed + +--- + +## 📍 Slide 21 – 🎯 Scrape Targets + +```mermaid +flowchart TD + Prometheus[💾 Prometheus] -->|🔄 Scrape| App[📦 app:8000/metrics] + Prometheus -->|🔄 Scrape| Loki[📦 loki:3100/metrics] + Prometheus -->|🔄 Scrape| Grafana[📦 grafana:3000/metrics] + Prometheus -->|🔄 Scrape| Self[📦 prometheus:9090/metrics] +``` + +**📊 Verify Targets:** +```bash +# Check targets status +curl http://localhost:9090/api/v1/targets + +# Web UI +http://localhost:9090/targets +``` + +**✅ All targets should show `UP`** + +--- + +## 📍 Slide 22 – 📊 /metrics Endpoint Format + +``` +# HELP http_requests_total Total HTTP requests +# TYPE http_requests_total counter +http_requests_total{method="GET",endpoint="/",status="200"} 1234 +http_requests_total{method="GET",endpoint="/health",status="200"} 567 +http_requests_total{method="POST",endpoint="/api",status="201"} 89 + +# HELP http_request_duration_seconds Request latency +# TYPE http_request_duration_seconds histogram +http_request_duration_seconds_bucket{le="0.01"} 100 +http_request_duration_seconds_bucket{le="0.05"} 200 +http_request_duration_seconds_bucket{le="0.1"} 250 +http_request_duration_seconds_bucket{le="+Inf"} 300 +http_request_duration_seconds_sum 45.67 +http_request_duration_seconds_count 300 +``` + +**📊 Format:** `metric_name{labels} value` + +--- + +## 📍 Slide 23 – 🏷️ Labels Best Practices + +```python +# ✅ Good: Low cardinality labels +http_requests.labels(method='GET', status='200', endpoint='/api') + +# ❌ Bad: High cardinality (user IDs, request IDs) +http_requests.labels(user_id='12345') # 💥 Millions of time series! +``` + +**🏷️ Label Rules:** +* ✅ Use for: method, endpoint, status, service +* ❌ Avoid: user_id, request_id, session_id +* 📊 Target: < 1000 unique label combinations + +**⚠️ High cardinality = memory explosion** + +--- + +## 📍 Slide 24 – 🔍 PromQL Basics + +```promql +# Instant vector (current value) +http_requests_total + +# Range vector (over time) +http_requests_total[5m] + +# Rate (per-second) +rate(http_requests_total[5m]) + +# Sum by label +sum by (endpoint) (rate(http_requests_total[5m])) + +# Filter by label +http_requests_total{status="500"} +``` + +**🔑 Key Operators:** +* `rate()` — Per-second rate for counters +* `sum()` — Aggregate across series +* `by ()` — Group results +* `{}` — Filter by labels + +--- + +## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L8_MID + +--- + +## 📍 Slide 26 – 📊 Section 4: Building Dashboards + +## 🎨 Dashboard Design with RED + +```mermaid +flowchart TD + Row1[🔝 Row 1: Overview Stats] + Row2[📊 Row 2: Rate & Errors] + Row3[⏱️ Row 3: Latency] + Row4[📋 Row 4: Details] + Row1 --> Row2 --> Row3 --> Row4 +``` + +**📊 Essential Panels:** +1. 📊 **Request Rate** — Requests per second +2. ❌ **Error Rate** — 5xx responses +3. ⏱️ **Latency p95** — 95th percentile +4. 📈 **Latency Heatmap** — Distribution + +--- + +## 📍 Slide 27 – 📊 PromQL Dashboard Queries + +**1️⃣ Request Rate (Time series)** +```promql +sum(rate(http_requests_total[5m])) by (endpoint) +``` + +**2️⃣ Error Rate % (Time series)** +```promql +sum(rate(http_requests_total{status=~"5.."}[5m])) + / sum(rate(http_requests_total[5m])) * 100 +``` + +**3️⃣ P95 Latency (Time series)** +```promql +histogram_quantile(0.95, + sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) +``` + +**4️⃣ Uptime (Stat)** +```promql +up{job="app"} +``` + +--- + +## 📍 Slide 28 – 📈 Heatmap for Latency + +```promql +# Latency distribution over time +sum(rate(http_request_duration_seconds_bucket[1m])) by (le) +``` + +**🎨 Heatmap Benefits:** +* 📊 See latency distribution +* 🔍 Spot outliers +* 📈 Track changes over time + +```mermaid +flowchart LR + Green[🟢 Fast: < 100ms] --> Yellow[🟡 OK: 100-500ms] + Yellow --> Red[🔴 Slow: > 500ms] +``` + +--- + +## 📍 Slide 29 – 📊 Monitoring Metrics + +| 📊 Metric | 📏 Measures | 🏆 Target | +|-----------|------------|---------| +| ⏱️ **Scrape Success** | Targets reachable | 100% | +| 📊 **Series Count** | Time series | Stable | +| 💾 **Storage Size** | Disk usage | Predictable | +| 🔍 **Query Latency** | PromQL speed | < 1s | + +> 📚 Monitor your monitoring! + +**🤔 Question:** What happens if Prometheus goes down? + +--- + +## 📍 Slide 30 – 🌊 From Guessing to Measuring + +```mermaid +flowchart LR + subgraph 😱 Guessing + NoData[🤷 No Data] + Reactive[🔥 Reactive] + Slow[⏱️ Slow Detection] + end + subgraph 📊 Measuring + Metrics[📈 Real Metrics] + Proactive[⚡ Proactive] + Fast[🚀 Instant Detection] + end + Guessing -->|🚀 Prometheus| Measuring +``` + +**🎯 Monitoring State:** +* ⚡ Detect issues before users +* 📊 Data-driven capacity planning +* 📈 Trend analysis and predictions + +--- + +## 📍 Slide 31 – 🏢 Section 5: Production Monitoring + +## 📅 A Day with Prometheus + +**☀️ Morning:** +* 📊 Check Grafana — all green ✅ +* 📈 Review overnight trends +* 🔍 No anomalies detected + +**🌤️ Afternoon:** +* 🚨 Alert: Latency p95 > 500ms +* 📊 Dashboard shows spike at 2pm +* 🔍 PromQL: `histogram_quantile(0.95, ...)` +* 🔧 Found: Database slow query +* ⏱️ **5 minutes** to identify + +**🌙 Evening:** +* 📊 Review daily trends +* 📈 Plan tomorrow's capacity +* 🏠 Go home with confidence + +--- + +## 📍 Slide 32 – 👥 Team Monitoring Workflow + +| 👤 Role | 🎯 Monitoring Responsibility | +|---------|----------------------| +| 👨‍💻 **Developer** | Add metrics to code | +| 🔧 **DevOps** | Maintain Prometheus | +| 🛡️ **SRE** | Design dashboards & alerts | +| 📊 **On-call** | Respond to alerts | + +**🔗 Alert Flow:** +```mermaid +flowchart LR + Prometheus[💾 Prometheus] -->|🚨 Alert| AlertManager[📬 AlertManager] + AlertManager --> Slack[💬 Slack] + AlertManager --> PagerDuty[📟 PagerDuty] + PagerDuty --> OnCall[👤 On-call] +``` + +--- + +## 📍 Slide 33 – 🔐 Production Considerations + +```yaml +# Prometheus with retention +command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.retention.time=15d' + - '--storage.tsdb.retention.size=10GB' + +deploy: + resources: + limits: + memory: 1G + cpus: '1.0' + +healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://localhost:9090/-/healthy"] + interval: 10s + timeout: 5s + retries: 5 +``` + +**🛡️ Production Checklist:** +* 💾 Persistent storage configured +* 🗓️ Retention policy set +* 📊 Resource limits defined +* 🏥 Health checks enabled + +--- + +## 📍 Slide 34 – 📈 Career Path: Monitoring Skills + +```mermaid +flowchart LR + Junior[🌱 Junior: Basic metrics] --> Mid[💼 Mid: PromQL & dashboards] + Mid --> Senior[⭐ Senior: Full observability] + Senior --> Principal[🏆 Principal: SRE practices] +``` + +**🛠️ Skills to Build:** +* 📊 Application instrumentation +* 🔍 PromQL fluency +* 📈 Dashboard design +* 🚨 Alert engineering +* 📊 SLO/SLI definition + +--- + +## 📍 Slide 35 – 🌍 Real Company Examples + +**🏢 Prometheus at Scale:** +* ☁️ **SoundCloud**: Created Prometheus (2012) +* 🔍 **Google**: Inspired Prometheus (Borgmon) +* 🎬 **Netflix**: Millions of time series + +**☁️ Modern Practices:** +* 📦 **Spotify**: Custom Prometheus federation +* 🏦 **Stripe**: Fine-grained latency tracking +* 🎮 **Riot Games**: Real-time game metrics + +**📊 Stats:** +* 🌍 **#1** cloud-native monitoring tool +* 📦 **CNCF graduated** project +* 🏢 Adopted by **70%+** of K8s users + +--- + +## 📍 Slide 36 – 🎯 Section 6: Reflection + +## 📝 Key Takeaways + +1. 📊 **Metrics complement logs** — different purposes +2. 🔢 **Counter, Gauge, Histogram** — choose wisely +3. 🔴 **RED method** for services (Rate, Errors, Duration) +4. 🏷️ **Labels** — keep cardinality low +5. 📈 **PromQL** is powerful — learn it well + +> 💡 If you can't measure it, you can't improve it. + +--- + +## 📍 Slide 37 – 🧠 The Mindset Shift + +| 😰 Old Mindset | 📊 Metrics Mindset | +|---------------|------------------| +| 🙅 "Seems fine" | 📊 "Data shows it's fine" | +| 🚫 "Users will tell us" | 🚨 "Alerts tell us first" | +| 👉 "We need more servers" | 📈 "Data shows we need 3 more" | +| 😨 "Deploy and hope" | 📊 "Deploy and measure" | +| 💻 "Performance is subjective" | 🔢 "p95 is 250ms" | + +> ❓ Which mindset describes your team? + +--- + +## 📍 Slide 38 – ✅ Your Progress + +## 🎓 What You Now Understand + +* ✅ Metrics types and when to use each +* ✅ Prometheus architecture and configuration +* ✅ Application instrumentation patterns +* ✅ PromQL query syntax +* ✅ Dashboard design with RED method + +> 🚀 **You're ready for Lab 8: Prometheus Monitoring** + +--- + +## 📍 Slide 39 – 📝 QUIZ — DEVOPS_L8_POST + +--- + +## 📍 Slide 40 – 🚀 What Comes Next + +## 📚 Next Lecture: Kubernetes Fundamentals + +* ☸️ Container orchestration +* 📦 Deployments and Services +* 🔄 Scaling and self-healing +* 💻 Hands-on: Deploying to Kubernetes + +**🎉 Your monitoring journey continues.** + +> 📊 From guessing to measuring — one metric at a time. + +```mermaid +flowchart LR + You[👤 You] --> Metrics[📊 Metrics Skills] + Metrics --> DataDriven[📈 Data-Driven Ops] + DataDriven --> Career[🚀 Career Growth] +``` + +**👋 See you in the next lecture!** + +--- + +## 📚 Resources & Further Reading + +**📕 Books:** +* 📖 *Prometheus: Up & Running* — Brian Brazil +* 📖 *Site Reliability Engineering* — Google +* 📖 *The Art of Monitoring* — James Turnbull + +**🔗 Links:** +* 🌐 [Prometheus Documentation](https://prometheus.io/docs/) +* 🌐 [PromQL Basics](https://prometheus.io/docs/prometheus/latest/querying/basics/) +* 🌐 [RED Method](https://grafana.com/blog/2018/08/02/the-red-method-how-to-instrument-your-services/) + +--- diff --git a/lectures/lec9.md b/lectures/lec9.md new file mode 100644 index 0000000000..e5a265fd11 --- /dev/null +++ b/lectures/lec9.md @@ -0,0 +1,853 @@ +# 📌 Lecture 9 — Kubernetes Fundamentals: Container Orchestration + +## 📍 Slide 1 – 🚀 Welcome to Kubernetes + +* 🌍 **Containers are great** — but who manages them at scale? +* 😰 Manual container management doesn't scale +* ☸️ **Kubernetes** = the operating system for containers +* 🎯 This lecture: master deployments, services, and orchestration + +```mermaid +flowchart LR + Containers[🐳 Containers] -->|☸️ Kubernetes| Orchestration[🎭 Orchestration] + Orchestration --> Scaling[📈 Auto-scaling] + Orchestration --> Healing[🏥 Self-healing] + Orchestration --> Updates[🔄 Rolling updates] +``` + +--- + +## 📍 Slide 2 – 🎯 What You Will Learn + +* ✅ Understand Kubernetes architecture and concepts +* ✅ Write production-ready Deployment manifests +* ✅ Expose applications with Services and Ingress +* ✅ Implement health checks and resource management + +**🎓 Learning Outcomes:** +| # | Outcome | +|---|---------| +| 1 | 🧠 Explain Kubernetes declarative model | +| 2 | 🔍 Create Deployments with probes and limits | +| 3 | 🛠️ Configure Services for networking | +| 4 | 🗺️ Perform scaling and rolling updates | + +--- + +## 📍 Slide 3 – 📋 How This Lecture Works + +* 📚 **Concepts + YAML manifests** — hands-on focus +* 🎮 **Real-world scenarios** — production deployment challenges +* 📝 **3 quiz checkpoints**: PRE / MID / POST +* 🛠️ **Tools**: kubectl, minikube, manifests + +**⏱️ Lecture Structure:** +``` +Section 0: Introduction (now) → 📝 PRE Quiz +Section 1: The Orchestration Problem +Section 2: Kubernetes Architecture +Section 3: Core Resources → 📝 MID Quiz +Section 4: Health & Resource Management +Section 5: Production Kubernetes +Section 6: Reflection → 📝 POST Quiz +``` + +--- + +## 📍 Slide 4 – ❓ The Big Question + +* 📊 **92%** of organizations use containers in production +* ⏱️ Average container lifecycle: **minutes to hours** (not days) +* 💥 Managing 100+ containers manually is **impossible** + +> 💬 *"Why did container 47 crash? Where's the replacement?"* — Nobody wants to ask this manually + +**🤔 Think about it:** +* How do you ensure 10 copies of your app are always running? +* What happens when a container crashes at 3am? +* How do you update without downtime? + +--- + +## 📍 Slide 5 – 📝 QUIZ — DEVOPS_L9_PRE + +--- + +## 📍 Slide 6 – 🔥 Section 1: The Orchestration Problem + +* 🐳 **One container is easy** — just `docker run` +* 📦 100 containers? 1000 containers? +* 🔧 Manual restart on crash? +* 💥 Result: **operations nightmare** + +```mermaid +flowchart LR + Single[🐳 1 Container] -->|Easy| Manual[👤 Manual] + Hundred[🐳 100 Containers] -->|Hard| Manual + Thousand[🐳 1000 Containers] -->|💥 Impossible| Manual +``` + +--- + +## 📍 Slide 7 – 😱 Container Management Chaos + +* 📋 Tracking which containers run where +* 🔄 Restarting crashed containers +* 📊 Load balancing between replicas +* 🔒 Managing secrets and configs +* 💀 Scaling up/down based on load + +```mermaid +flowchart TD + Crash[💥 Container Crash] --> Detect[🔍 Detect (how?)] + Detect --> Restart[🔄 Restart (where?)] + Restart --> LoadBalance[⚖️ Update LB (manually?)] + LoadBalance --> Hope[🙏 Hope it works] +``` + +**📊 The Numbers:** +* 🔍 **Netflix**: 100,000+ container instances +* 📦 **Spotify**: 10,000+ services +* ⏱️ Manual management: **impossible** + +--- + +## 📍 Slide 8 – 🔧 Docker Compose Limitations + +* ✅ Great for development and simple deployments +* ❌ Single host only +* ❌ No automatic restart across nodes +* ❌ No rolling updates +* ❌ No auto-scaling + +> ⚠️ **Docker Compose ≠ production orchestration** + +```mermaid +flowchart TD + Compose[🐳 Docker Compose] --> SingleHost[🖥️ Single Host] + K8s[☸️ Kubernetes] --> MultiHost[🖥️🖥️🖥️ Multi-Host Cluster] + SingleHost --> DevTest[✅ Dev/Test] + MultiHost --> Production[✅ Production] +``` + +--- + +## 📍 Slide 9 – 😨 Zero Downtime Deployments + +* 📅 Traditional: Schedule maintenance window +* 🔧 Stop old version, start new version +* ⏱️ Downtime = lost revenue +* 💀 Risky deployments = fear of deploying + +> ⚠️ **Every minute of downtime costs money** + +**💬 Discussion:** How do you update without any downtime? + +--- + +## 📍 Slide 10 – 💸 The Cost of Manual Orchestration + +| 🔥 Problem | 💥 Impact | +|------------|-----------| +| 🐢 Slow scaling | Can't handle traffic spikes | +| 📋 Manual recovery | Long outages | +| 👉 No load balancing | Uneven distribution | +| 🙈 Version confusion | "Which version is running?" | + +**📈 Real Numbers:** +* 🏢 **Manual ops**: 10+ hours/week +* 🚀 **With Kubernetes**: Minutes/week +* 💰 **Downtime cost**: $5,600/minute (average) + +--- + +## 📍 Slide 11 – 💡 Section 2: What Kubernetes Is + +* ☸️ **Container orchestration platform** +* 🎭 **Manages** container lifecycle automatically +* 🔄 **Declarative** — you define desired state +* 🌐 **Portable** — runs anywhere (cloud, on-prem, laptop) + +```mermaid +flowchart LR + You[👤 You] -->|📝 Declare| K8s[☸️ Kubernetes] + K8s -->|🔄 Reconcile| Cluster[🖥️ Cluster] + K8s -->|🔁 Continuously| Monitor[👀 Monitor & Fix] +``` + +**📖 Definition:** +> *Kubernetes is an open-source container orchestration platform that automates deployment, scaling, and management of containerized applications.* + +--- + +## 📍 Slide 12 – 🎭 Declarative vs Imperative + +```mermaid +flowchart TD + subgraph Declarative + D1[📝 Define: 3 replicas] + D2[☸️ K8s makes it happen] + D1 --> D2 + end + subgraph Imperative + I1[💻 Run: create pod 1] + I2[💻 Run: create pod 2] + I3[💻 Run: create pod 3] + I1 --> I2 --> I3 + end +``` + +| 📋 Approach | 📝 You Say | ☸️ K8s Does | +|-------------|----------|------------| +| 🎭 **Declarative** | "I want 3 replicas" | Creates/maintains 3 | +| 💻 **Imperative** | "Create this pod" | Creates 1 pod | + +**🎯 Always prefer declarative manifests!** + +--- + +## 📍 Slide 13 – 🏗️ Kubernetes Architecture + +```mermaid +flowchart TD + subgraph Control Plane + API[📡 API Server] + Scheduler[📊 Scheduler] + Controller[🔄 Controller Manager] + ETCD[💾 etcd] + end + subgraph Worker Nodes + Kubelet[🤖 kubelet] + Proxy[🌐 kube-proxy] + Runtime[🐳 Container Runtime] + end + API --> Scheduler + API --> Controller + API --> ETCD + API --> Kubelet + Kubelet --> Runtime +``` + +| 🧱 Component | 🎯 Purpose | +|-------------|----------| +| 📡 **API Server** | Gateway to cluster | +| 📊 **Scheduler** | Places pods on nodes | +| 🔄 **Controller** | Ensures desired state | +| 💾 **etcd** | Cluster state database | +| 🤖 **kubelet** | Node agent | + +--- + +## 📍 Slide 14 – 📦 Core Resources + +```mermaid +flowchart TD + Pod[📦 Pod] --> Containers[🐳 Containers] + Deployment[🚀 Deployment] --> ReplicaSet[📊 ReplicaSet] + ReplicaSet --> Pod + Service[🌐 Service] --> Pod + Ingress[🚪 Ingress] --> Service +``` + +| 📦 Resource | 🎯 Purpose | +|-------------|----------| +| 📦 **Pod** | Smallest unit, contains containers | +| 🚀 **Deployment** | Manages replicas and updates | +| 🌐 **Service** | Stable network endpoint | +| 🚪 **Ingress** | HTTP routing and TLS | + +--- + +## 📍 Slide 15 – ⚡ Before vs After Kubernetes + +| 😰 Before | 🚀 After | +|----------|---------| +| 📅 Manual restart on crash | 🔄 Auto-restart | +| 📋 Manual scaling | 📈 Auto-scaling | +| 👉 Downtime for updates | 🔄 Rolling updates | +| 😨 Fear of deploying | 💪 Deploy anytime | +| 🐌 Hours to scale | ⚡ Seconds to scale | +| 📝 Track servers manually | 🎭 Declarative state | + +> 🤔 Ready to orchestrate? + +--- + +## 📍 Slide 16 – 🎮 Section 3: Core Resources + +## 📦 The Pod + +* 🐳 **One or more containers** sharing network/storage +* 📦 **Smallest deployable unit** +* ⏱️ **Ephemeral** — created and destroyed +* 🏷️ **Labeled** for selection + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: my-app + labels: + app: web +spec: + containers: + - name: web + image: nginx:latest + ports: + - containerPort: 80 +``` + +**⚠️ Never create pods directly — use Deployments!** + +--- + +## 📍 Slide 17 – 🚀 Deployments + +```mermaid +flowchart TD + Deployment[🚀 Deployment] --> RS1[📊 ReplicaSet v1] + RS1 --> Pod1[📦 Pod] + RS1 --> Pod2[📦 Pod] + RS1 --> Pod3[📦 Pod] +``` + +**🚀 Deployment manages:** +* 📊 Desired replica count +* 🔄 Rolling updates +* 🔙 Rollback capability +* 🏷️ Pod template + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web-app +spec: + replicas: 3 + selector: + matchLabels: + app: web + template: + metadata: + labels: + app: web + spec: + containers: + - name: web + image: myapp:1.0 +``` + +--- + +## 📍 Slide 18 – 🏷️ Labels and Selectors + +```mermaid +flowchart LR + Deployment[🚀 Deployment] -->|selector: app=web| Pods[📦 Pods with label app=web] + Service[🌐 Service] -->|selector: app=web| Pods +``` + +**🏷️ Labels = Key-value pairs for organization** + +```yaml +metadata: + labels: + app: web-frontend + environment: production + version: v1.2.3 + +selector: + matchLabels: + app: web-frontend +``` + +**🎯 Labels enable:** +* 🔍 Service discovery +* 📊 Resource selection +* 🏗️ Organization + +--- + +## 📍 Slide 19 – 🌐 Services + +```mermaid +flowchart LR + Client[👥 Client] --> Service[🌐 Service: ClusterIP] + Service --> Pod1[📦 Pod 1] + Service --> Pod2[📦 Pod 2] + Service --> Pod3[📦 Pod 3] +``` + +**🌐 Service types:** +| 🔧 Type | 🎯 Use Case | +|---------|----------| +| 🔒 **ClusterIP** | Internal cluster access | +| 🔓 **NodePort** | External via node IP | +| ☁️ **LoadBalancer** | Cloud load balancer | +| 🔗 **ExternalName** | DNS alias | + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: web-service +spec: + type: NodePort + selector: + app: web + ports: + - port: 80 + targetPort: 8000 + nodePort: 30080 +``` + +--- + +## 📍 Slide 20 – 🔄 Rolling Updates + +```mermaid +flowchart LR + V1[📦 v1] --> V1_V2[📦 v1 + v2] + V1_V2 --> V2[📦 v2] +``` + +**🔄 How it works:** +1. 📦 Create new pods with new version +2. ⏳ Wait for them to be ready +3. 🗑️ Terminate old pods gradually +4. ✅ Zero downtime! + +```yaml +spec: + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 # Extra pods during update + maxUnavailable: 0 # Always maintain capacity +``` + +--- + +## 📍 Slide 21 – 📊 kubectl Commands + +```bash +# 📋 Get resources +kubectl get pods +kubectl get deployments +kubectl get services + +# 🔍 Describe (detailed info) +kubectl describe pod + +# 📝 Apply manifest +kubectl apply -f deployment.yaml + +# 📊 Watch changes +kubectl get pods -w + +# 🔙 Rollback +kubectl rollout undo deployment/ + +# 📈 Scale +kubectl scale deployment/ --replicas=5 +``` + +--- + +## 📍 Slide 22 – 🚪 Ingress + +```mermaid +flowchart LR + Internet[🌐 Internet] --> Ingress[🚪 Ingress Controller] + Ingress -->|/app1| Svc1[🌐 Service 1] + Ingress -->|/app2| Svc2[🌐 Service 2] + Svc1 --> Pods1[📦 Pods] + Svc2 --> Pods2[📦 Pods] +``` + +**🚪 Ingress provides:** +* 🔗 URL routing +* 🔐 TLS termination +* 🏷️ Name-based virtual hosting + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: app-ingress +spec: + rules: + - host: app.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: web-service + port: + number: 80 +``` + +--- + +## 📍 Slide 23 – 🏥 Health Checks + +```yaml +spec: + containers: + - name: app + image: myapp:1.0 + livenessProbe: + httpGet: + path: /health + port: 8000 + initialDelaySeconds: 10 + periodSeconds: 5 + readinessProbe: + httpGet: + path: /ready + port: 8000 + initialDelaySeconds: 5 + periodSeconds: 3 +``` + +| 🏥 Probe | 🎯 Purpose | ❌ Failure Action | +|----------|----------|------------------| +| 🔴 **Liveness** | Is it alive? | Restart container | +| 🟢 **Readiness** | Is it ready? | Remove from service | +| 🟡 **Startup** | Did it start? | Keep waiting | + +--- + +## 📍 Slide 24 – 📊 Resource Management + +```yaml +spec: + containers: + - name: app + image: myapp:1.0 + resources: + requests: + memory: "128Mi" + cpu: "100m" # 0.1 CPU core + limits: + memory: "256Mi" + cpu: "200m" # 0.2 CPU core +``` + +**📊 Requests vs Limits:** +| 📊 Setting | 🎯 Purpose | +|-----------|----------| +| 📋 **Requests** | Guaranteed resources, scheduling | +| 🔒 **Limits** | Maximum allowed, OOM if exceeded | + +**⚠️ Always set both!** + +--- + +## 📍 Slide 25 – 📝 QUIZ — DEVOPS_L9_MID + +--- + +## 📍 Slide 26 – 📁 Section 4: Manifest Best Practices + +## 📄 Complete Deployment Example + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web-app + labels: + app: web-app +spec: + replicas: 3 + selector: + matchLabels: + app: web-app + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + template: + metadata: + labels: + app: web-app + spec: + containers: + - name: web-app + image: username/web-app:1.0.0 + ports: + - containerPort: 8000 + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "200m" + livenessProbe: + httpGet: + path: /health + port: 8000 + initialDelaySeconds: 10 + readinessProbe: + httpGet: + path: /health + port: 8000 + initialDelaySeconds: 5 +``` + +--- + +## 📍 Slide 27 – 🔐 Security Best Practices + +```yaml +spec: + containers: + - name: app + image: myapp:1.0 + securityContext: + runAsNonRoot: true + runAsUser: 1000 + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false +``` + +**🔐 Security Checklist:** +* ✅ Run as non-root user +* ✅ Read-only filesystem +* ✅ No privilege escalation +* ✅ Specific image tags (not `:latest`) +* ✅ Resource limits defined + +--- + +## 📍 Slide 28 – 📊 Kubernetes Metrics + +| 📊 Metric | 📏 Measures | 🏆 Target | +|-----------|------------|---------| +| 📦 **Pod Restarts** | Stability | 0 | +| ⏱️ **Pod Startup Time** | Speed | < 30s | +| 📊 **Resource Usage** | Efficiency | 50-80% | +| ✅ **Probe Success** | Health | 100% | + +> 📚 Monitor your cluster health! + +**🤔 Question:** How many pod restarts is "normal"? + +--- + +## 📍 Slide 29 – 🌊 From Manual to Orchestrated + +```mermaid +flowchart LR + subgraph 😱 Manual + SSH[🔌 SSH to servers] + Docker[🐳 docker run] + Restart[🔄 Manual restart] + end + subgraph ☸️ Orchestrated + Manifest[📝 YAML Manifest] + Apply[kubectl apply] + AutoHeal[🏥 Auto-healing] + end + Manual -->|🚀 Kubernetes| Orchestrated +``` + +**🎯 Orchestration State:** +* ⚡ Deploy in seconds +* 🔄 Auto-healing always +* 📈 Scale on demand + +--- + +## 📍 Slide 30 – 🏢 Section 5: Production Kubernetes + +## 📅 A Day with Kubernetes + +**☀️ Morning:** +* 📊 Check cluster health — all green ✅ +* 📈 Review resource usage +* 🔄 Approve deployment PR + +**🌤️ Afternoon:** +* 🚀 `kubectl apply -f deployment.yaml` +* 📊 Watch rolling update: `kubectl rollout status` +* ✅ Zero downtime update complete + +**🌙 Evening:** +* 📈 Auto-scaling handles traffic spike +* 🏥 Crashed pod auto-restarted +* 🏠 Sleep peacefully + +--- + +## 📍 Slide 31 – 👥 Team Kubernetes Workflow + +| 👤 Role | 🎯 Kubernetes Responsibility | +|---------|----------------------| +| 👨‍💻 **Developer** | Write manifests, define resources | +| 🔧 **DevOps** | Manage cluster, set policies | +| 🛡️ **SRE** | Monitor, scale, incident response | +| 📊 **Platform** | Build internal tooling | + +**🔗 GitOps Flow:** +```mermaid +flowchart LR + PR[📝 Manifest PR] --> Review[👀 Review] + Review --> Merge[✅ Merge] + Merge --> ArgoCD[🔄 ArgoCD] + ArgoCD --> Cluster[☸️ Cluster] +``` + +--- + +## 📍 Slide 32 – 🔧 Local Development + +```bash +# 🎯 minikube: Full-featured local cluster +minikube start +minikube status +minikube service web-service --url + +# 🐳 kind: Lightweight, Docker-based +kind create cluster +kind load docker-image myapp:latest + +# 📊 Useful addons +minikube addons enable ingress +minikube addons enable metrics-server +``` + +**🛠️ Local Options:** +| 🔧 Tool | 🎯 Best For | +|---------|----------| +| 🚀 **minikube** | Learning, full features | +| 🐳 **kind** | CI/CD, fast startup | +| 🖥️ **Docker Desktop** | Mac/Windows convenience | + +--- + +## 📍 Slide 33 – 📈 Career Path: Kubernetes Skills + +```mermaid +flowchart LR + Junior[🌱 Junior: kubectl basics] --> Mid[💼 Mid: Manifests & debugging] + Mid --> Senior[⭐ Senior: Architecture & scaling] + Senior --> Principal[🏆 Principal: Platform design] +``` + +**🛠️ Skills to Build:** +* 📝 YAML manifest fluency +* 🔍 kubectl debugging +* 🏗️ Architecture patterns +* 📊 Resource optimization +* 🔐 Security hardening + +--- + +## 📍 Slide 34 – 🌍 Real Company Examples + +**🏢 Kubernetes at Scale:** +* 📦 **Spotify**: 10,000+ services on K8s +* 🔍 **Google**: Runs everything on Kubernetes +* 🎬 **Netflix**: Titus (K8s-inspired) + +**☁️ Modern Practices:** +* 📦 **Airbnb**: 1000+ microservices +* 🏦 **Capital One**: K8s for banking workloads +* 🎮 **Pokemon Go**: Global scale with K8s + +**📊 Stats:** +* 🌍 **5.6M+** Kubernetes developers +* 📦 **92%** container adoption uses K8s +* 🏢 **#1** CNCF project + +--- + +## 📍 Slide 35 – 🎯 Section 6: Reflection + +## 📝 Key Takeaways + +1. ☸️ **Kubernetes orchestrates containers** at scale +2. 🎭 **Declarative** — define desired state, K8s maintains it +3. 🚀 **Deployments** manage replicas and updates +4. 🌐 **Services** provide stable networking +5. 🏥 **Probes** ensure health, **limits** ensure stability + +> 💡 Kubernetes is the operating system for cloud-native applications. + +--- + +## 📍 Slide 36 – 🧠 The Mindset Shift + +| 😰 Old Mindset | ☸️ K8s Mindset | +|---------------|------------------| +| 🙅 "Restart manually" | 🔄 "K8s restarts automatically" | +| 🚫 "SSH to fix" | 📝 "Fix manifest, apply" | +| 👉 "Which server?" | 📦 "Which pod?" | +| 😨 "Scale takes hours" | ⚡ "Scale in seconds" | +| 💻 "Deploy on weekends" | 🚀 "Deploy anytime" | + +> ❓ Which mindset describes your team? + +--- + +## 📍 Slide 37 – ✅ Your Progress + +## 🎓 What You Now Understand + +* ✅ Kubernetes architecture and concepts +* ✅ Deployments, Services, and Ingress +* ✅ Health checks and resource management +* ✅ Rolling updates and scaling +* ✅ kubectl commands for daily use + +> 🚀 **You're ready for Lab 9: Kubernetes Fundamentals** + +--- + +## 📍 Slide 38 – 📝 QUIZ — DEVOPS_L9_POST + +--- + +## 📍 Slide 39 – 🚀 What Comes Next + +## 📚 Next Lecture: Helm Package Management + +* ⛵ Helm charts for packaging +* 📝 Templating with Go templates +* 🔧 Values management +* 💻 Hands-on: Creating Helm charts + +**🎉 Your Kubernetes journey continues.** + +> ☸️ From manual containers to orchestration — one manifest at a time. + +```mermaid +flowchart LR + You[👤 You] --> K8s[☸️ Kubernetes Skills] + K8s --> CloudNative[☁️ Cloud-Native] + CloudNative --> Career[🚀 Career Growth] +``` + +**👋 See you in the next lecture!** + +--- + +## 📍 Slide 40 – 📚 Resources & Further Reading + +**📕 Books:** +* 📖 *Kubernetes: Up & Running* — Brendan Burns +* 📖 *The Kubernetes Book* — Nigel Poulton +* 📖 *Cloud Native DevOps with Kubernetes* — John Arundel + +**🔗 Links:** +* 🌐 [Kubernetes Documentation](https://kubernetes.io/docs/) +* 🌐 [kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/) +* 🌐 [Kubernetes the Hard Way](https://github.com/kelseyhightower/kubernetes-the-hard-way) + +--- From d24f89d8c81c198489f0afd459311ccb0f2f6243 Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 29 Jan 2026 19:25:12 +0300 Subject: [PATCH 04/27] feat: completed lab 2 with bonus task --- app_go/.dockerignore | 16 ++ app_go/Dockerfile | 31 ++++ app_go/README.md | 13 ++ app_go/docs/LAB02.md | 138 ++++++++++++++++++ .../screenshots/docker-build-terminal.png | Bin 0 -> 227062 bytes app_go/docs/screenshots/docker-run.png | Bin 0 -> 17256 bytes app_go/docs/screenshots/docker_curl_test.png | Bin 0 -> 44746 bytes app_python/.dockerignore | 18 +++ app_python/Dockerfile | 21 +++ app_python/README.md | 31 +++- app_python/docs/LAB02.md | 63 ++++++++ .../docs/screenshots/build-terminal.png | Bin 0 -> 118614 bytes app_python/docs/screenshots/docker-curl.png | Bin 0 -> 52071 bytes .../docs/screenshots/docker-run-terminal.png | Bin 0 -> 43997 bytes 14 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 app_go/.dockerignore create mode 100644 app_go/Dockerfile create mode 100644 app_go/docs/LAB02.md create mode 100644 app_go/docs/screenshots/docker-build-terminal.png create mode 100644 app_go/docs/screenshots/docker-run.png create mode 100644 app_go/docs/screenshots/docker_curl_test.png create mode 100644 app_python/.dockerignore create mode 100644 app_python/Dockerfile create mode 100644 app_python/docs/LAB02.md create mode 100644 app_python/docs/screenshots/build-terminal.png create mode 100644 app_python/docs/screenshots/docker-curl.png create mode 100644 app_python/docs/screenshots/docker-run-terminal.png diff --git a/app_go/.dockerignore b/app_go/.dockerignore new file mode 100644 index 0000000000..2ef3b4ff5d --- /dev/null +++ b/app_go/.dockerignore @@ -0,0 +1,16 @@ +.git +.gitignore +.vscode/ +.idea/ + +bin/ +build/ +dist/ +coverage/ +*.out + +docs/ +tests/ +*.md + +__pycache__/ diff --git a/app_go/Dockerfile b/app_go/Dockerfile new file mode 100644 index 0000000000..6ab2b7a52b --- /dev/null +++ b/app_go/Dockerfile @@ -0,0 +1,31 @@ +FROM golang:1.22-alpine AS builder + +WORKDIR /src + +COPY go.mod ./ + +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + go mod download + +COPY . . + +RUN --mount=type=cache,target=/root/.cache/go-build \ + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -trimpath -ldflags="-s -w" -buildvcs=false -o /out/app ./main.go + +FROM gcr.io/distroless/static:nonroot + +WORKDIR /app + +COPY --from=builder /out/app /app/app + +USER nonroot:nonroot + +EXPOSE 5000 + +# Default envs (can be overridden at runtime) +ENV HOST=0.0.0.0 PORT=5000 DEBUG=false + +# Run the binary +ENTRYPOINT ["/app/app"] diff --git a/app_go/README.md b/app_go/README.md index 574e4950cc..47d73b1896 100644 --- a/app_go/README.md +++ b/app_go/README.md @@ -24,3 +24,16 @@ From the `app_go/` directory: ## Docs - Implementation details: `docs/LAB01.md` - Language justification: `docs/GO.md` + +## Docker + +### Build + +```bash +docker build -t ${DOCKER_USER}/devops-info-service-go:lab02 ./app_go +``` + +### Run +```bash +docker run --rm -p 5000:5000 ${DOCKER_USER}/devops-info-service-go:lab02 +``` \ No newline at end of file diff --git a/app_go/docs/LAB02.md b/app_go/docs/LAB02.md new file mode 100644 index 0000000000..5ca198adef --- /dev/null +++ b/app_go/docs/LAB02.md @@ -0,0 +1,138 @@ +# Lab 2 — Multi-Stage Build (Go App) + +This document explains the multi-stage containerization of the Go version of the DevOps Info Service, why multi-stage matters, image size comparisons, and technical details. + +--- + +## Strategy Overview + +- **Stage 1 (Builder)**: Use `golang:1.22-alpine` to compile a static Linux binary with `CGO_ENABLED=0`, `-trimpath`, and stripped symbols (`-ldflags "-s -w"`). Cache Go modules and build artifacts to speed up incremental builds. +- **Stage 2 (Runtime)**: Use `gcr.io/distroless/static:nonroot` to ship only the binary. No package manager, no shell, and runs as non-root by default → minimal attack surface. + +### Dockerfile (multi-stage) +```dockerfile +# ---- Builder stage ---- +FROM golang:1.22-alpine AS builder +WORKDIR /src +COPY go.mod ./ +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + go mod download +COPY . . +RUN --mount=type=cache,target=/root/.cache/go-build \ + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -trimpath -ldflags="-s -w" -buildvcs=false -o /out/app ./main.go + +# ---- Runtime stage ---- +FROM gcr.io/distroless/static:nonroot +WORKDIR /app +COPY --from=builder /out/app /app/app +USER nonroot:nonroot +EXPOSE 5000 +ENV HOST=0.0.0.0 PORT=5000 DEBUG=false +ENTRYPOINT ["/app/app"] +``` + +--- + +## Size Comparison + +Build both images and compare: +```bash +# build +docker build -t ${DOCKER_USER}/devops-info-service-go:lab02 ./app_go + +# check sizes +docker images | grep devops-info-service-go +``` + +- **Builder image base**: `golang:1.22-alpine` (hundreds of MB, includes toolchain) +- **Final runtime image**: `distroless/static:nonroot` + your binary (typically under ~20MB for small Go services) +- **Observation**: Multi-stage removes compilers and build tools from the final image → significant shrink. + +Paste actual output here: +```text + +``` + +--- + +## Why Multi-Stage Matters + +- **Smaller images**: Faster pulls/pushes and less disk/memory footprint. +- **Security**: Fewer components → fewer CVEs and reduced attack surface; distroless has no shell or package manager. +- **Performance & Deployability**: Static binaries start quickly and work consistently across environments. +- **Best practice**: Keep build-time and runtime concerns separate. + +--- + +## Build & Run Process + +### Build +```bash +docker build -t ${DOCKER_USER}/devops-info-service-go:${TAG} ./app_go +``` + +### Run +```bash +docker run --rm \ + -p ${HOST_PORT}:${CONTAINER_PORT} \ + -e HOST=0.0.0.0 -e PORT=${CONTAINER_PORT} -e DEBUG=${DEBUG_FLAG} \ + ${DOCKER_USER}/devops-info-service-go:${TAG} +``` + +### Test Endpoints +```bash +curl http://localhost:${HOST_PORT}/health +curl http://localhost:${HOST_PORT}/ +``` + +Paste terminal outputs here: +```text + + + +``` + +--- + +## Technical Explanation of Each Stage + +- **Builder stage**: + - `go mod download` with cache mounts speeds up dependency resolution. + - `CGO_ENABLED=0` produces a fully static binary suitable for `distroless/static`. + - `-trimpath` removes local path info; `-ldflags "-s -w"` strips symbol/debug info → smaller binary. + - Output binary at `/out/app` for clean handoff to runtime stage. + +- **Runtime stage**: + - `distroless/static:nonroot` includes just enough to run the binary; no shell or glibc needed for static binaries. + - Runs as non-root (`USER nonroot:nonroot`) by default. + - `EXPOSE 5000` documents the port; environment variables allow overrides. + +--- + +## Security Implications + +- **Reduced attack surface**: No compilers or package managers in runtime. +- **Least privilege**: Non-root execution in runtime stage. +- **Determinism**: Pinned base images and static linking reduce variability. + +--- + +## Trade-offs and Decisions + +- **Static vs dynamic**: Static binaries are portable and simplify runtime images; dynamic linking may be needed for certain libraries (CGO), but increases base requirements. +- **Distroless vs Alpine**: Distroless is smaller and more secure; Alpine offers a shell and package manager (useful for debugging), but larger and more components. + +--- + +## Docker Hub (Optional for Go Bonus) + +```bash +docker tag ${DOCKER_USER}/devops-info-service-go:${TAG} ${DOCKER_USER}/devops-info-service-go:latest +docker push ${DOCKER_USER}/devops-info-service-go:${TAG} +docker push ${DOCKER_USER}/devops-info-service-go:latest +``` + +Repo URL pattern: +- `https://hub.docker.com/r/${DOCKER_USER}/devops-info-service-go` diff --git a/app_go/docs/screenshots/docker-build-terminal.png b/app_go/docs/screenshots/docker-build-terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..255bb2811b3b512d613110bd5b9eaa8785968678 GIT binary patch literal 227062 zcmd42bySpV`!+m?f=Vd}NGnK#beDpF(%m5pAuZiA1|codInppRNT<>{bi+u)&|L%b z-uu~mKhOTfx8C*s_puhRuA5=*xvn#h<2dgSH5GY+`;_-VAP~Wu*RM1{pgZg!5VqA_ z9N-G^vxQ>d*S+_z^<6Wh0tCWM+Okrq5^5Sh@K~ zE0K}v`a8efZ|`V-uB?*pXlHkO?a8!<@^qQ>F$g-{u ze>g)bvWga9Gd|7t|8?32rp!IP_PxDTw|u;h!0P#Ka9%K;UC>Tgrp{!DJ}PS<));?o zt@MY(ZU!@l_;JA3jarVXG+Jg?f3oY}p@@E4aYLQpLQVKkL2}iIb=@~_|AO8^%vg=IC`r5n*0mu zb4;ED>szxMS@7$=mK}6I+Mf&?m+%t*p(WR5gp>Qp_Hft|qr(2t(9<7w^WTz2^K5wP zxwr~}4PR29t^fWhRqi~hLr5nvm?^7R_WW%Di>t?bi@~{z?JFb}A+2a=aMz>z28Px{ z$$z?$Df1g0E6w!6E?38a+a=qHDe(Te42$?#&w`-`S0h!m&DJ3!9;LWE;d4H)O3y65 z`P12z_N&2j#CLYepstS7R!BkVxaC^o1 zBe0{nLg4+lsY<(b>o07+J*e~Q>hg}Rt`uAd3Ta>8NnI;I%@a2^bi_OscR0icGS9#TdQs@VFd< zSa58v*H1q;V-G4|r~0;iB{~k*<=xY zRGh#XDSPxII@Q*NwDThUtO_HVT-i`HpRvb>A|glIr<{%hsVX}I(1&ivccH43L@x?{ zN}1``-S;kX@VfaeDXB>I&|@drU}yHxbLIIPSDtcqh3@&jR0F|O6}+Zes0aC#^;zy{ z>hdENBb{>0Z1$_0g|u&KKgwKxHmy79kr(2>xS06NiuBdK*k2F@*$@Wwk5w&dB_#*&9C6N^qQ>-`e@0pJC?n|JFa|Q zAL`)46P`+sXT0a-ay+-F(PGI{lOa>2Ltw4-*y-3dGRei>YrQyk3GrOD40FaMk|hzJ z^9LazAquM!zQ>tz%AQIC{pVLBj1#5C9bgO*pO#_*+nbc?9ahZ}YUk2r;XUv8x}&x1 zY26-T#za2o!y_w%1?D3*$ZdNnBw4^ty}RR1OE6KAlxH>lbh#%ID_mJ4zJh2M8H^K= zJXGdPLjS96bEvu4V7+suH#v@7clq~9`zQ!XHUR62v(v>Ab~(Pym#e&Z-a`_4t(tyn zf!XAPQAV){4TRT zSWpc$YLP#q)haY05yQnr(~AFYy=mH>PZ-gCDlcDuf{I8nbjCkiUB0X;H^I*ds72JRd(;jvRMb8 z2i_&PXSoB93%#=3nIT9_PBF8vz;C5cl`cWIZ0`6qHT>mEG20T8;6eGyueQ&Pq%#Q9x(&;oIHgOp{jgw# za~gz(R8|`7)I4sWP)y(jULO)cvSS5PvqhdAn~Dq91na6lEkL1G1)e1|V;z*UR??cv z-6me~kAAWj)-K1k=cL)l=6#rAzNW@8AiUi-&OEor#Ffb~!l4@F`mr(AwO4iO$m2sP%k-;W7=W3k#$!9e#rmigF3jAK(+^5Gk^4m+pEs zR-{kqID`EuuD*%YIPv*)zr8%(=q#MlRZ8Pg+_~X{L;Ge^zW#n|JFec$tdPLkSDqED zJu!KV=C_|pYzro2anN+X=A~>wwU?r9ZoQi=!M2+$%6!>0lPckx%*qGR4L8-ElqYrX z^qfqWn69=YNo#iMD#q4WtMxPDL}MiaRSgLy*jD<08%B`J&B5 z;!Xw4I?WgYg?anP;R@v1)VokMzcFJ|OclamkR{YuJMW@-M7IE0YU_Kp6 zagZC~n@1n~yxpdS8ad)pd0}OZHD)wdRO8ED36azev!PuL9^bPH!@Mtu=p4LLV%zH^ zC2Dyccp-*@SZ|S;IWw!9<#4@w{;sYj!GRT}C1Syb0?aEbZ)EGrV@TFW*&dKn^eh+% z2#?e=LJWmsWaNXTX|$nt^VFv6R|Kk-EMGmrnryrT8@Wo|14#-(0QR2{azfeaN8e7t<{l(eASehs%7%73Ux&bbm9MPwEo&7sE~)!u%4=7%Hpi^2&c2{HArvJ4qvzZGtBE@KPC zf`o#9qkqcn*6``0TJL4iJ{=CtSpQj+m=4GFXV-NE3B`5zfT17>C9!m|sJmtdB^hNe zogx|b1Bz>@2kx#5y+}B1pxfLNZL+aBL@u?&AO2Q(uc8Efj5;eiM93^ZvQk z7nnT0rv2z-pK>I%$d}MBw|}GWaOLRxXDmLnUuX=Gzftwt-a}-+At@`b(iWr0g5QLI z<()lCi>F#-kJiM)LRV%Ys0=nS5<-*adb3m6lnw9_82q4+4WfNU-kNSKCbH#C_CX3` z)N_>h7$x|cq6JA6Er~+4L&Y52oSK?uL z`iIsR3bl2^wKjZf0~cxe-LI9D`bV})H52GWAje-io&V~qo$`ix_jh*%Id<3L7bP!_ zR?L-=#I@HhlA2f|0!}JFo-AI3rg2WV`h8W30=XADMEj z3>|y($$Dk>rtgB>jly!DQSTxf&KC6~4bnH(4DgMOjh~y`7#5_%V00=ftbBGfG}Lu| ztS1#$QB9!{mZz!SdU^&!b)&&{PL?(w11+(bl+t?xpkw9LDrwtIa5G}LhkI?f3N2FJ zhmWaJR56QBwbylxeNT70+K=0mhMcpYi^oZP`b7H-viHeztzpe*QS;i7(->7C`=^lGMI?wJ2-)kn@^C5 zS2US&w@Ual%0f=(3isXI4~lea1lu3Eb&R5gqN8;1`pyI5A~c;KtrR36LRsMoWdtjV zq=RC*6k58PAQ6xSaki}jUq6bDgy935<%^8L^WN8T2r2hv#l3T6REs>3RR5SCCAE^0 zQg67oJHkn)puT0qx0#8_&&&8%w`o^};{Ysl^;A^EH5+9;^8;B{_T#$~(!?Ol_+P2= z3luNnTilt9$wM>;pBcydS2K z{E~#xsj>MU-u2%!7s6o-UBOb+-=uu#Qq1c-BFCV5D?{4LTDjv8mv1N^gLrmTg(Ys* zn>e`>>VaeZk@<-Hgaf7N>$|Yl1V6){-*&4;Rozw*F_(?C(~rw6)!w+dyKNlh2?aaK z=FTu*&R2NQ%+FDF+*PZ~$C7DL)sL@xC(kX#U)?$vQc*f4X7q;l*KCc` z_^ucaPjq{Bc&SYRujT&1DHFZ;C&{Ku7AqYlrtGm45r4MDjNXv(va&L*o%W6bAw&K+ zenLKZO7NwGgMX16)jIjxsjnYb&>N7WCa9F6b4~}i*X?wBCT^{$F))m*Y`L`f$JS7Y z_Lg9U-=?{s{S>G*weDzma@2ZyDKJ&smGXRlLE1eZj+sKz0gZINaSg4Q$L@E(z|bP2 zW~H4=6kM~ER+A}QcP1BgS-=O;?kJ5+t?ymtk&mR_*zlW*A3ph56a}lDgn8(fz|Tj- zBpt-1Dt!ETeQpYakb%O9i z;#Kn;vVsD1wZ^HjlYkh$hpnk;o|VbX8pk_(psn^5D1_cL$={yvd9asES?TPy9UhUZ zYTSJ1_Mc?>;9YkJdIjaD;*;R|rD25!L1WR(E(vH&T;&DS#zwV2PBh4Ch*Av1Nqpp{ z?w2ZGaI+PAS$9O4!`AouXU@bHjvm!2lQ2`d9HVA@*F2ue zX62`>r@lc6iyd3f=GKjtf-rsjSG=5Gd9RKJ6i*U_T7-PRlZd#i{2m*Bx3+V)lZz!% zxA>>3KcDAY zC&pgx54H~#CU`Hd%R{WKDeY%_Qh?g07)Sr~l#ytnRD1fSt+30R;^AvaSHpU)>y$~J zhkB%i**{d1I;G6@^YKqD?Xc7uUy>hElK(zfZ4<9cn^n(sH|O5U*=Hb zT-LwpYBpDq*OjH&YfZp6z?i)$C|7<8%KXglE40*oTCJC~$|SkS88eylEcnbvqU~AB z^L59<^aEeiigW!LO4;kx<8m(3ks8bkVq(euIT5$FQr9?yOde})(w}7BAqz
s-qH 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

pkDYy~egfx2 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

sGRnOSehW)~OC}%R{=7<8 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νDDGad4RbCj^oH^Q55<|Jf@{V->VYerCiSD}jrV5U)2|jR~otR`L&w zNDvW*t-h<>U%G*GAmYcUtE->xW<#T9g`gg!k{Oy2k|?wP*U)eHC_2lfN> zWM>+kgT9)Z2YeMOIyevzVc`mZT=}NsI4gW&yR~^#nA!~VUWj?_2LMap8vDuN;bPh6 z2d%pOD!4pFcUQLW2UD|+PfI6`kWYcK3%L7Lz}+Y0OW?jg1OYif{{SzEUJ-UN=itGc_U@|sNT)Cb870U4V%p~?Ee)XlUV*dXPXN((^(VRVLuk7w!RN#_Z1{Ghf{pMDDmRyv~YL-kz!Db5YCn zl;rUn)Rq8LO3R=>>TBv84>GfNKo2>k_fPkjQB^x{ew=%XKyMpXIthzoj~C2a6ai>y z0+3i-V8nF^C!He|h&sJk2?g4IR#r~#A?BFl8X4_}#KgHZo+DI~E2%j4klA(K!@h0q zo91*?!b(p&%GCz_ZnQ3ZtT^d#o!{DNev;>dxhjiSxyoqIsI@^8;8Yj4kXPyJTuU|BSt8@7j z0##K32=Jgg&WjR}18CzNHjRRQ|6Oj6J;6*~d3iC@oYaK*b zyg-JxdPI~~l^T5^kfoS4db=zk-~_X+x7Fpj${UM+-lj^;-5@bN8VLtcO8 zg807RJNG!`qPbgrSr!DkRCV#Y072G~Ongc9;$Ee`2ULs_K5upy4Q?PkKF6|Y@9s8V zu5;Vz272p&PNFctxwVNdm)G0`5C9F&xyO%iw$qVE?_A@av*V3t=Hl_${YIAJ7dW7txiLL@ zy!3G#Ojg!EgP}p>494zI`O=dB z4+{b4R7Z_!#&z-=z7uH;jzGJ9sgT11y((MJiL}3O>#eCTXO4caf~#AG2B-kZ+SoXK z&h;r&k$f>C@9#3HZQ~2@D+P0Jhq0vDlqEDE04*<3U+AJv>DO>OwBDlczbS#s2p<0a;+M z0xxlEiU-%+wP*h-0Rue1|Hn1q-(?Q)UH?BS%KvLV?d%75q0p}M|Mikh?84og8e1X) UKJfv+dI6G^R+1`u_3p#}10#wfl>h($ literal 0 HcmV?d00001 diff --git a/app_go/docs/screenshots/docker_curl_test.png b/app_go/docs/screenshots/docker_curl_test.png new file mode 100644 index 0000000000000000000000000000000000000000..439b25f80f1e8b31d223ae789752d4847131939a GIT binary patch literal 44746 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 literal 0 HcmV?d00001 diff --git a/app_python/.dockerignore b/app_python/.dockerignore new file mode 100644 index 0000000000..20345b9d49 --- /dev/null +++ b/app_python/.dockerignore @@ -0,0 +1,18 @@ +__pycache__/ +*.pyc +*.pyo +*.pyd +venv/ +.venv/ +env/ +ENV/ +.git +.gitignore +tests/ +docs/ +*.md +*.sqlite3 +__pycache__ + +!app.py +!requirements.txt diff --git a/app_python/Dockerfile b/app_python/Dockerfile new file mode 100644 index 0000000000..b3ae24fb35 --- /dev/null +++ b/app_python/Dockerfile @@ -0,0 +1,21 @@ +FROM python:3.13-slim + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +RUN groupadd -r app && useradd -r -g app app + +WORKDIR /app + +COPY requirements.txt /app/ +RUN pip install --no-cache-dir -r requirements.txt + +COPY app.py /app/ + +RUN chown -R app:app /app + +USER app + +EXPOSE 5000 + +CMD ["python", "app.py"] diff --git a/app_python/README.md b/app_python/README.md index 0731e38864..93a8728746 100644 --- a/app_python/README.md +++ b/app_python/README.md @@ -34,4 +34,33 @@ DevOps Info Service - small Flask based service what return and report system me ## Available Endpoints - `GET /` - Returns system metadata including hostname, IP address, and current timestamp. -- `GET /health` - Returns the health status of the service. \ No newline at end of file +- `GET /health` - Returns the health status of the service. + + +## Docker + +### Build the image locally (pattern) +```bash +docker build -t ${DOCKER_USER}devops-info-service-python:latest +``` + +### Run a container (pattern) +```bash +docker run --rm \ + -p 5000:5000 \ + -e HOST=0.0.0.0 \ + -e PORT=5000 \ + -e DEBUG=True \ + ${DOCKER_USER}/devops-info-service-python:latest +``` + +### Pull from Docker Hub +Link to docker hub: + +https://hub.docker.com/repository/docker/zsalavat/devops-info-service-python/general + +```bash +docker pull zsalavat/devops-info-service-python +docker run --rm -p 5000:5000 zsalavat/devops-info-service-python:latest +``` + diff --git a/app_python/docs/LAB02.md b/app_python/docs/LAB02.md new file mode 100644 index 0000000000..1241d57a57 --- /dev/null +++ b/app_python/docs/LAB02.md @@ -0,0 +1,63 @@ +## 1. Docker Best Practices Applied +- **Non-root user**: `USER app` +- **Layer caching with proper order** +- **Minimal copy** +- **`.dockerignore`** +- **Logging and bytecode settings** +- **Expose runtime port** + +## 2. Image Information & Decisions + +- **Base image chosen**: `python:3.13-slim` small footprint with Debian base; better compatibility with many Python wheels compared to `alpine` (musl). Pins major/minor for reproducibility. +- **Final image size**: 182mb +- **Layer structure (conceptual)**: + - Base: Python runtime + - Env + user setup + - `requirements.txt` copied, `pip install` layer + - `app.py` copied + - Ownership change and `USER app` + - `EXPOSE` and `CMD` + +--- + +## 3. Build & Run Process + +### Build +![build-termina](screenshots/build-terminal.png) + +### Run (pattern) + +![docker-run-terminal](screenshots/docker-run-terminal.png) + +### Test endpoints + + +![docker-test](screenshots/docker-run-terminal.png) + +### Docker Hub repository URL +- URL: https://hub.docker.com/r/zsalavat/devops-info-service-python +--- + +## 4. Technical Analysis + +### Why this Dockerfile works +It work because we create well typed Dockerfile, and after running it system using name spaces isolated place for running required app + +### What changes in layer order would do +- if we change requirements installing and code running with places every time then we change code we will triger pip install. +- Skipping `chown` the app user might not read/execute files +- Copying the full repo later can take more time +### Security choices +- Run as a non-root user to limit damage if something goes wrong. +- Use the slim base image to reduce the number of packages +- Keep the command simple + +### How `.dockerignore` helps +- Faster builds by sending a smaller context to the Docker daemon. +- More stable caching because irrelevant files don’t change the context checksum. +- Prevents accidental inclusion of dev artifacts, virtualenvs, `.git`, and docs. +- Avoids copying sensitive files into images by mistake. + + +## 5. Challenges & Solutions +No challenge in lab doing proccess. \ No newline at end of file diff --git a/app_python/docs/screenshots/build-terminal.png b/app_python/docs/screenshots/build-terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..9e0da4593bdf289bb02e6a140062bdb0bb8f12a5 GIT binary patch literal 118614 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

2_cPDKb-k!W3&yHzsgMEi&kIjGxg&Ywenp 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%0AfkGQFSgQg&-5d z0X$`rA0q}}=X*8wm`juW;xpvEFX1{^f7oT2nAlp#@?V_X++?zL(?0$=tf3%$Wj~n5 zG?D(*rpXomYqYa)ieGOM%Kp2n`Cn+|S=vHt%+=!*;vqTzwdxKCG?Kg1ET}6kp&N6x znGnMOdZ&(W;0n)MD!M{dQNWm@K3(xR(&3?DxxOB$p&nUicMOc6v$*D~piqrzso$$f zYT&f)_65T5G}?-k50~Ud5izIdX@>svVWZw5mq555;R#fTu^24(W z3!Nn*qkj+MduLk*+g_f4c*@7luC>OGNKedEj@a5VUWh96LBWw%`vgKcTju^H-%D`) zjlvgKr}^KGY=SX9)DMB#NMn2iVm{=BmGc5kMx&!TZJs7uqc^#J%c6+&msYMd=Or@hPdtE*yyd zw_Z=E*kRqM0=pAQ;U4;#k~9K9-~aO?F}RWb-mh#cve_i>b!dKFawf7~DfiH={xG|s zsW|y(48%V^H73p-w3xEtb@(T@uIx)g6!?G5y>(PnZQDL<0ips*N`s()Al)5Ghe)?{ zcS#N)A=2I5h#(;y(#;T35+mJ=#4z;yF7D^PpXYslzxVsrx4!lL@v+8P>{;7A>}#L< z+UIqg$9Wv5`C@m`eTDtLTmOs_1#xv^E7h(AyAb#M`7gyqqv)1n4qwvrv%K2Hm2O_>Wwf+ zP{)6(Ry~r~;i6bwecFP>4my~NH!VQ%+eVf5M->5MgUI7*x{Xa859Z%Xjn z`oFIHKW~N>ievpIECv)2BGMZGYIqTJadr_0becdzd|;M95h8^YvY4$tJpWKD0`tF* z@FQ|-K~uC-vMJq;e>Wxqxeov`jAc9g)iUwVTj!`|$4eUIrCJVo^uI_qPT}*6mM=5+ zeklWME1)hm0(7PxVWUtd`vQWWt?YlFx|_LGr%}X;ieoVJ2Qfa#r4qxxckj&)NtJtJ zVIL~c*0e=;_tcat4f1D13mETUzS)w-i^q+Q%z5#6yn2@X=(@5$_<55+HEqu3nV?c8 zt=D?`+3?V8ijdbx`jdNNkN@=wprL#5(_}{I>eOVOu}0boy`D72-;)Z+Mrt+K5F7Xk z7Z~|hg?AgZo3P*AmvRGR_EeFlRDYq-X0CSFe&sET-ronhCw4!wWKtwD1Z05E03$-TOr<%Ojg;N`{R>zS}N% z7(jLZU$1tySJ##VVG!-mLQC47*GAkat|8wDiRLyvRdBK_fl~a7k~eZ~9*FUXXxh-i z;Z&!W7eD#_>;M148X8&Xg?v6oNNMhCB5+f%1^(-i`5{5Tx z`U#FkOKk0gF@&X^e#D>a8A){zgeINH3bo#Q#U8gJpP7X(=U`x}Nt;Y&zT9oGmcDX_ z^*Lwm2kV$Rl1`7w6;JS#bMbmV20zunm@HZCB%+=-B8xZ}h-+V|uz&EBkxr3a2#*ano zwuv$PAdTQdZ1ZF!ce>{w9YEI zo<_ceH7@?BOUy<<9d}{JF#oaj_b#WLM~&vJ8T{_;Yffr&{N>Gu56rxg3w%Wy1xW_p zF&g@wY{f2N-5t{gzGCOKUkMT^5nU@xvU+_}uhO&{=_8hK1b@qX(WqmFrHEJBi9$=v(3EVBlS1F_nWUgh0iuSAIt%BQVZVJfx;{W_^AdL4ZPw= z3xx)*QVxHziPrL0jUHAJ1ly5Gg%Z|=z31JDt*LoXm%}vXl$X<; zM2-u3RQ>q2+P@K=iE=c9Xk7RaUrG(BzH7x=Sj3)9mdAW8t-*<@=TptqjMLKGUC?Bn z|8{Ff#GY!mG>zfXRPpUj38{65QQ{1wqg`ZMYtzS-)_w%xLvv`LtZDI7)(`2Y@2OUz z_Jy#;z9m7p4ra!yr;Ojzc7Y9yzW=1qu|JD7&+^K=i-8Tw09{h>HfX;JT?z-%E0Rn| z_RExQb7I!{WKw=%0deHnK}-}7v61tf9}kfvl5lU zYgx5{ij6%q{Ns6`#(gF4*^3hAGe8*J-2J6tTd2j0c!7&!lhaZE=A)62)%CR;ODf{i z%YE-$x^erEx4w!MRerk~&sQwc0!auW@AdU68tT=>U!M{!N?&K3|7?)sP+(w zSMkt<=tiU`;n}pjW3`_Tb?ukDWh3-!#bP3T6s|$dM~1&J*{zcKRpUXe zK0{y1Y@kvq{Cj!qh^<47g5&@Ll?zaD-5n41Qsu4PpZ;wXf4?-?(j7gqfHcO&?T64k z^{RyI%aqWStR(7T@w@rO76RU zO_tQ(zNe(WBU1j{tuHMTWxP+OFqDz!NH!-ob>%v5JT7AbQ zBI=E;QN3k0oC$Q$v*Q2Kt>zY3{zM(StR99U#yy!-VH zXQOYwctKm;^6fh8SnP7bzLhFzNmr!lnteU4)!X?xmA6}-muozG zf`KOP%d&Qxz)&KPqvZCx(7O|vkMdV$H8;NYH19-v3DoYk^V@SJnznf)R0Jg}TThD4 zy5oO0dvp?+)MSwK{4=^s_iPzO;ob!sxuE(li@o0o1`%QCMRCzsTl#0N{SO?G&7a@p zalc7ZVdJszZ*Wtocyl-|T;MRwlr_62b>ifC{$-|?1tZ(yDk?qm`sJA=UQ1$}0^#bW zHa*wQ$K3@T2C2!{q2Zm5Ms1bJKAM&j$=b#2MQ;oIpE(-TBl))(a9!nb)An9s$>X9v zt47}<5R8!W%OBVG6N>!2Gn(#}PKRS$o`&z@ZP`XRS}Q}ZA~m&E^cBs!iGA8(5x5FC z!A7aWy^ZiS^d}RSXee^?y9iw#Nx)l@yh0uoI8T=gavvUKA7MB-Lo)@1t?;Ma8)EwU zq|aflM7MybU9L`4rl64LBe2-Y2fw=Pj7=OZX|lA)hgi=Y{rH2i<$OVYOYnNeSbr;) z_Q{hcVkRbmu&2hkd0W8OFH0dEU-i5ZvD4tb=@Wd?&~NgLum6e~mIUq{;QdmNF6^US z$K(gpKyE6~k;fS~LHJe2pdUzFSpKRh@m+t}htpGQV2{EfWAy``her!M6Pt5IQA?*U z(|ij>dtnAwXdhO-YgkT|5ewOEy#dPgmD;ZakAB8w9`6a%I>WAz6SaC?jfhR)K=TME z;=^0>ibmqEDF5Y~Y$GsCET}c93Oe2DNtN+Y#R1XObMfX)&%umB;4W`~OU)rHRyOtX zRVgG}QtCs%s8z4OuJEc<)odm6;;qup)iQdz5mtFUuc-)T?-UO5p$SiGQ3u~TQ)t3o zwwS~>ztX1iC$#Inyr6L4jUiWD-MbhwdPdbiJrE;5H0Q$^&lxewH#jzA_8^utd&&XE zxK(NZ7#Ji+ix}qB=N5*9`OlDb=dq%|*PuQWdUlwgJJed1o&`7>AR1@WK_auA|-(E}CkWWL5J7opk ze-OJA*&-^d8Q)Z9WZau8{>Z|ma-fT`$~@ZII4;n;r;fr6t7oHRhAZNp);c>!7bC7# z`C*Uo!|K;Chm-~Eh%B2{I6~DHZGTZy27MUG4h$Hb99Rd%bOSpECq9xk{%V8id0($- z>`J>Mw<(3alL6e0p@#m$Jnn@@a2_S^2iDC?|Djjp<(S{RyB{z&l{qFPir8O!D@BbU zMJ|7kR^Y)z5B`oVkA{t<2nEDn6L?D6Rrwp`Bpl*f|IGuHtmGL)=! z@GuEPtli!uWAS3|H&$feyOUU4`4?zkx@E=0{5KHWBL++?^B!iA62Oyv$zVLTEO&eT&1#|U{A_8c!&6Gr~G60ree1EvlHQJ9Wzb_lT!cs@; z!}l>Ush>RyfI(1t=27THjWC0oB>JtEFgiTp@L+a`(;RQ0v1KicvpjSq#;(tDE;^DJ zZhFwGR`;ToVlU=_ttL^~n*oL&Zq`yYn-0-~IyLXa*y0ucJR`zi=~#$9;-tFDH?Fx& zOx1RVY}rRQ36qpAuzfI5``dGZHUy8X{7}8|-MK)=m6$ZYRx8WeuI|rMi<&=gmG0t( z;K@u5&^0KBFR&E9?rx|yZ}gJZJ1cr?izfYiga;`8x#hqQ!H(w5csMWVv!kD=F0tme1d9ZNtqj&{|%SPPxL4 zKpyFxrtOPY1?J3#eY6t1GY@+e++~4g{Ym~J|E>By{Ww(hB^z5zc=!PHb9&4Z@4Sg( zmDwUeNK$+}(1X6Le6%`5(F9b;myL{LX#2mk<66YVe*4C{-*90^agKzHFf4rPrSJ%x z(x_cuyx2b?9<=qjxamOyTFe&pL%Gk~g)nh&%wgA8fbsJ9$&)3Imc}EDPS~|UgH^%? z94Kr|9A>?0XfVeiWYR4rH87nR+$1-EAb6;$Z)+ubKrX#9kE`gOg#-mDzkAoQ8k)ss zJjEm_bViFE@-akGu#Zc_z}BIHSY-juaLC4!pKRDh+=*9tWue9Xo#B}fkLQ->oiz=1 z_86s&6?wZ)`K8hgXvyp&jY-gn2-4Npm}vVr;@Z0L8_&78NjQfWioeo+|FIxp^SJ`Y z4_E8%EQ#`xR#fUj3v)l&b~Tw&C)bY?hRW~lhP?@pfgM*3J3=c*;dSk6x-EK!SBp+l zn%jZn!H00{1g_^KgqY7{B^)f*U~FA|xGyl%Nq)W@j@xibQk(iPzdxF+nZ4xRp`q1| zuR+v1w&1&Ry!KgA{d$frbIibeoO*VkSlCgFiwYq8iYq-4mxXgS+)UJjP!w3P3nw`f zZ@ziAx}DM|p{AC9i${5X_mQK5wnwiGLrOAPN z@6=|Q)`pujkAkwFvWao3eY?*-e(_o)Zoy89LCAimTVwW^eZ$i`D)HnTLCG3j7Dph3 z4S2Qyrva-`rS2t%C@++h6MIz@QaKx$q<=;9*MlGf%9uVTm*%m{G(^erwTeM!@I|8| zclpO^86lg6^4rd{SGH@)mM_qB+DIApConaP$eh%%o$UOVT!cK&hINp+L0ZZ*9rF^Z z&|lY8J((+?U+gaqm?5yfYV`NL9vSJ3nU9W5`(@a4$twS{56MZJ%=}|?b{ZY3eUuUQMNZ6_`L;fN2D39 ztoF=E<|$W1s*<V(?O3P0=wFTZxel%6Y<`ltLR>|2w`khN_b$=7AgSp<3v*A^XQpf<_(2RxEK6 z{YaDA_i_e!@1s097;){f6yC3$h_@PK^$t!bRecD%UL`S4WpjQZfsRvX`Vh&%y)*em zN;bq=XzSu+uV^QN2N%e`ADInrE`HZ}yOuNyxMvLH{F^Kvuy4p(5fKHTnQ$ZC_rlia z#wTEDX=z^IH1k1qvTW?@4 zBOk`4gwN^s=Dr)BetI$^D=V9XxTYxgX>#MV81-v_p?*7)gTs{R)`qi)WWA%dU2LWV zLU;EQqaOnoLD8(m7Dmq3UpU$pPd=iqC@CmL$m?J?{8g~z&INzIlTMZ}lYV2DgLi_$ zv_PvraM7&g2HheQOAjY2%R_);v zk2f25{cYgOlcJ=X6`7@uN8VVotwGRbVFm`<-iYW|wYxeM*zvlMpd5s3spL$Z9NV5i z09d%CM_3tO`>;%%^^mgpTPR}88VH4Xp7R>+d9LtQVSHyT>UgZ6F@kwG{vtc)$$~j8 zn|q{WC=-0^*rX%rEA>_E-au|km*b&~`u(PH_Z9Ob z3FABYnG&p1&BUHu|FZr`7<8QbnAi5OUfdW zEPsOczj$DW-5Lf5?Wf4oy=evcbDKMaa%a`_bP6c}vo>Z6sgr^NW}MX<_jn|1QZ)3v zBIfMdB4EB(lW?1~4J*+4(5%*Ob|_Fu`c+-c@M?F|KgAhT|kmbJP)bJFssuB*U>k5Ea3;*o}Hsd(0f0cD4-S=Mv z9OJ*B0%o^&!;(qX**TUZp=>e9@}NQ^d3M$=VwS_7AY|4#ZOj&)Pa|XD+o*uqjet4hGDJGh)Sw4b}^L6eFyk1ibny$r>!f%_Zk=IH8Mp-kxF}e=skyP_0 zEI^Tn*>?H#!A;`i*e@gFOGmmpx}>dWc9wacz17US+l%VCOlcK$2@$E(j@gW74KjuB zWKDd*{Ed|v-pFb&ZybrA7PZMl`S$&4?e1frJ0^G|D#V(pHMhG1ec**h6jJ;1j`T5= z8!kBNt}0=N{bW4%H~0jL-f>c}q<01015(k$%g^OT81(9ELRT1XP|%H#sNN42(y@Uq zPkg^sFin(;U<$Ymn5TNPYoOMltcS#jTC0-5X zSWMhGBs}v~*LNKUYqFxKEF^*iJ2UgB%2X7{bTWn8+Bm;e1?01g33bf(w13rXy>t`8SS5u|td zrz!vnG}rtm!=u`=&sgH^ixfis>lK zmAA!j?yB)OmX9F6Z&$c{nH)&7ZyrfhAl0ffG-^bII%O_ghJB?vD8-#8kd5NMXNlAs-Z ztCH~fH}S=;uCDAl8d}=GpJ+$3HNF`e-#uM-&ckg_hKR)yY)w$-+HJ~`+aYa-h;6Ct z6>}5R(-N5fX1961uZH^>(FNkbs7?5CQf>W|!~?aNVQ?pdGVzs;;zs!bXb&>&i}w0B z$0~N3R>B&J{ks8!JvuN@dv_*AAMxb2VU8jFXQ-5FX3{0ZJ?Z?K0~Zx_M3i1+@v`wJ z(w-{jNsQpd{1F+i12Czj74}5=01oAr2UG6~4P_qapE<5CAOfuz-fSMYAyH(8Q@}Xt z5U0tW{v!sg(;3?K0}eFmo7|o?@HGUlOz5=b(uYHkH~zVA*XCGca4DOS%sW%|zYTCA zvwg45y@f5GLCj2|Yb~CjJXV$QyRSqiltiwR0i^i{z(2QmGZZ@yd`=H~<LIIvUTfa|arih9JMdw>Lm>EFIZqVkNx_E5j0NA3q zoNg05yExebk>(98MC@ob?3!nKooID*c4BRac->#W0qVff?CFK`~O_ z?<6iec>s)q<}{DQub|=zqVQdX-GcKkqIOnX$c5Y9smB&>?Nx1%*Zzs`W6x<)dlL}q z!WXcj%Ni3lV@Z+DGU5pjc!p!9>G27~b;_RRU^s;(;)ck&F!H^XJbkQa{VNdR()Rvc0DjR#Ou?^X8&JMB0ZDqOy%;9~dZQ ztRor$lDNhVgeU=+xbBPkxO5oN1-HMFd|E>3KY*huavYfzbCsvjHaPEeG^{&uvYoxm z5sU2~rKA>bka1iN^B;i_bt1%~7B_HO4xh^yZMGn*XkR73d>6Vp&P2uwoXwRy0jL88 zguAUwzq@p8@afgi#7$1P2crY415l)B2NNVPlzHu<8c)|^m%;DNX39=FebrG@(V7pM z*TlN*UuU0}Ag{*++Mc%f7BdrQU0eJ1Gu~G#SQhB|y&?dF>Awkhi`=@-K=wHQ`7Ji} z753w&CG0~17Ab#xaj|qc+=*t7KmX5wb%mGkDw&|mCp#g}Lm*y~4p>0o*YU4XO2E*2 zv%k%!*KNNE<@>AFTB(jg2&hRen&M}Gf#K$>C^pZG(HBr$A_@_YPzT> zl5T{a9W6V1?6v#CnvB=~h;apju*^qXY}C$ zaXM&Ed+oY2JyBrIHi^y~VFP zmx&Q9BAqn{zxDf|mx7}PHK1C1?Pu0G}TypYLw|w|&DYvb~!ny3p z?yTHIp#s)g6v*nq(1P~vxPq|p8w(5j)`P}rjC9fPGRVR?VqV}_SXS01c;M0M?x{~q zw8i7s_C+zL=-5wx0LSOE{WQzty@{e_{<~xbas6GS6|Dn<6|LJ_h-2_S&vUo&*ntTdvY~I0z#n{m)DU>Y6%8<8}50R87Su9osSeP zP)g4=91`B>i(qWSRlL0V!_wMEln2q%UVGgeS$UH3j~2WZhxG2EJL&aCUhZ69v>0vv ztfc9b5_}ESZ}}FOOg!4w+%CMoR|m}FvsE^jo007y^A1-Cv2L7%w(Ix<#5JaiFXsBW zp*!z=EG(>gU$N7sI1Q$B#f_cvSC9gi!*o#zO`Y#nZ@}DzfkSqjk_o0+upr)DXiECt zcDbCe{!Z`br_iOULFT*fcc#7q+mff)n&Qi6mC%@4hub9&*PRz!$z`k-=N){7B$pr7 z_}l_81@l7V1mt)2153$6XvT$*R3yb3xV3@8MZh4l^&`;;60S|aIjd;7MhC;zV^_!F zF|cu5zx6_CaeGn3fB9}Q(Er&W_3&G)J25&*ZxFL>%N$t8Sq0RzTj{)x0eQWV%T5h9 zya8tBkM*Zt59H2ze6zO{xW9*AN<@V&4-c9q&1Jcfv@N$uv;4l&MUdZLC zOzIBv6UBA>1-W8K8YUq@c5wP2p>O|fRpw#dx#uv53)E84)b`u z!rMHJ1W7Qg&Tgd+=8N-G!o-BO?qs|Bn3w<#`S}H$CQqX;jBRT=N_1^!GU|P#LH3o( z`tCVfHbi8qLa!63G1RZEXNG(4Dnn7WbG4HZP>ZV*ckj~D%ZEO`(*h_pNz)1(&pUb| z;qkElAe%6Km(xFnQ)RAji8snOqnst5Eb8kk+#5w{zFlL#Io2uA8dPG5Peioh)G%|r zV+ItU?$py!SOJNIEQM5gM@P0aZd)TnDp&9di^}YdQc5*U#Pl-5AfUk)xQnH!C)MQw zcca_Jj|1DPA0S21Q-i)+P850zd*FVznpy>O^L?vL%@moqqW9cs9vfeI0K&amWIFg^ zYfXcmo_=)r3;lyQI^Ujft?HYF0WTRg1<3)`w630GF|?sn-rM{Er%XiVfAF~NR#Bil zOf|?3kAgtsm=m}WogE$Y3=AbFS9tWoenBNAK=?Ej%;orRr~#<=TrNp<>-(xbnUmw-8XB~fqiI-ZT&k+`2eBSZ~5~AUJD|dg;QTp^n=rw*wKy0C2 zuwH+}3D9m6@gJBDGnn9{@VUg)|6s@T1w7UM#LzIwD-=i}OE3JIoF-pFVHYRxqBRj6 zP}`tj(ySk~<}>K6>o-kuoX&6`ZGzi2?lgS0r;H>Q83~Rc^(oAfyTUZPYTcX!4;+x_ zvGyw2qfSdHk9^aiz>Dzq)2m|@aJxpO6d(!B%+2xe$jMn--*4AdtmGxNy<8O8kWfoU zU6$-3#FVmkLShbqs0KK(KU!`!5hV1VJ3_}LS?ftV%wZVm2GQ`dxh0qPm72FdG3qgM zR9~L`?u{g;(v%vg^biUvD>) zM1$0Eh8&w#=FkIgb+N^(VgH;|+G;T!V7_e5Q#L1iFv_0~0O{8Q*>cc)vG`$os-!bN zG;0iAE|79~MwDr)s^;43ka&9f0q~Aaw|7c-WkaCXG<*#mny!$u+W=xqd>poc;_$Ky z?`{tI5C+)s&)es6*DyU-EthCW4!UMMLkD?cf* z3z42P~ zPC^BdNIf(Mbh;oeQS;T%D(7kD&K;b@1-pv3&?|Bn$j(EMZAC?GO@ppwhi5Yj#k~4Y2>gwgdKa2z2}caBJ)@>>pXig9mS!c}DH&h0nkwsG z>I}IpEiX^SQ|FDFdUSBcFnY%Im}WYPUC6F6bdaSjebL8WpaP-unE83867ajF8!Z-F zIJDoslWu^TX-V6iC@RsY&>v-NE;p89ww`|~6H8w^JF1${JTF*b`VY!x3n#5n$`q6V z)6+uZ;(gKHczk}n>5`7eB;ZPeDOU+3qO_cx@$18>V=dNo*((z?GPnRY3#O}JdkI_x z(H;`9pUkp>wgGAxT*UU|O5wQukBEIrKOrwaz1hip z!#VLtF8u@4#VTGV<;5=FSD5rA;4bIsHCZ=%?jLXN-CTVg=w2P7K)EwGFeJnoKo|k3 z&+%SrK>j#do0#iy+w7tT8VPgmgw^;9a((+uTASjN<5smrz@0fY?#~^9WlT^ueV4Kw9hPp zg6RTwZ4vygRyPL$W3gH*?4#*Db^jKm*L^WcB=%=+!3HSgN5y4jDZL9z)Q_k;+50t? zE%xz2+{Pw|w6-xsNJvOEa42rQ^ah%YcPDXZz_SL2rk7>(dh83ZFkPOI3;EGo^m1TY zCFQ7T5kYzkg%BN{XVZ-$I;GDz%tlNAGdfdB<>wcTsCevsop@blB01pO0>crO24@8l z#(8HX72zvw8~WO^IJ((p(}y^ZNF^WBobCEico=H<+)#TdtMa-63$$$ zEnb9_*!>^(9upAvGa5^=nT}6eq?#2Y``28a4YniSaRHFD5*kS@%p94!Y(&Vc+c!_y z%u)l=$=^FADk(z7{Hf?LUnDuD&mksrQkb%maQ6&b9`V)j1c2^?z-GSyK-~IG4#Rzr zNtEx$;~AP!`%Ps!)u}LFRK~3H`Y2#NWG_#sa0j=&3?53i-92LYGed*J;e76XlkB`U zd*R&Xu4HKIg1Xgw_{+K(0GF2+IU%PWvrxeU*dgh`-8~T+?kgY=j&>#;>!OHcXZS4s z5gpO6bgp;_)3Gw%Uzff;m5Oc+*gBq+$ zkStuCJak6&^5rX>0I#X3hAD&U*`ZGuAt2Isp!M_>`qjY~C@wNT_ujT%IhN)h%QdRI zZUQictoBQ^hgcgBIB@>;-L1ZCcEJRR7ne0;={wIg)FIq|3MYCz=Wd*E+V3;SPU7(D z#s(-kU%Z^X&R-w)Sl=r86dHOX!z73p;_waOJIAFL)=DJibfAI?2@q-}f^_ozd=p&# z!|T_kP^Fu~$U*FLU%C)XRbSH+d*Z*ie5=mVTCUR|nxL?2ws#@}f8iz#lolqx-P4cc^7#;JK!j>E-qa05*=i8TkdT zjFwmXpCVv%S<6=k@JisV=(a!K-AoBNCZSfz$jSW1}Fq6DQi6?o1v#U*AGlxU>1N(O7KvrG1C_2BZ2C?jb0hmchRHE1`JA zAcmAogbzU;JJH?CGZX*;qGBf7HI@Vhss})OoFqMW1S+Luft%T}r zI^4_AeoptiatEy53tcxIUeQ&1<2XiD5X1NV(}mrI^~$Z4GJauRRwDg!DEdJ4I2sk{ z8F2gR+!1z#|53Em<+q3^Nh3g$%jOd8ygDZpiHKE6D^nEU9%B{wR z{;V>91@-&0&jgbs=?+^d05RaSn*XF>0O_=@t$ojFGZPK4ki=I+2)W1imMT`^p1uaP z-rX>}G!`zv12&(nS|j3rBvwx=4O&irh1oavLK&yuv9zMc(&kz2&z2k>DsCnLCSbxT zjnJRF-ErDAf=V%e!-Npi1mccugtuRnj5;l5so^!z(mQHK`X`mrW?|P={{V-Vwx>Qn z=J|xt-`3{&Uo+rKAR*?9-jok)zfOPdv?k+{C~rip1=f}~Vl@BF zChair2Q5HXTD7H@?E{kso1tx_UKfe@$ejQZBA*-yKDyLW&nsb_*G2wa*jFM$-$^o6 z&I;>2U+Ye1b#PUW%6=BCfIC~sGdLW4Y4P07{vVi&e_nn5-Yy~Ch^=eP+d?zh?I~jr1!DgUNH_I-KSXNIv!-{fkS_;T0ZPd9jbASP zI|1=-#W4HXZmLdaFMCpejN2ZaRi)6QRx-+cdS{_J(kKDsoZKQylDG1-@4JTxAnPo+*$LMl zoUs+#C*dS;A_C%GW1c5fEBD8huk?k5VhfGhwkeCpo@AE4{pv>6w`Qx?Z`RxR;coHE zkr%FxT%{2tc>by;50jZU@SlcJi~3HKf8^EX>egx_l+F&)VMDcIkSUsk;CWp5)+FDT zw)^Bni+AKkiS=B8#%08bvpOYU6}^~i9x2GaHvY9H3??bkg3@x z4ce-arzCS7Ok+H{W_1!PbZKxeAvGQUCLZ-zv7?r77K~~#z=wKFl=J7%1*Ssv&!kw; zCw2E-Uh@-=sSaoEoU%EcR+E)fUW%b+tCy8KWo}4R&qU&QHH%Q|I%}NSxWUKeegJ@{%U}cZat|}CVGCBOE@>x-Blh`>-2)3&Di~t8U@2&gu3>rs}2F3 zYQpN83|Hh!nK7#Wm+YVjKK0z?`B{zyq5SeomEzhvwB2N?my-2P-XapJ-y5RZQ>rUS zCTD2bcy+0jf}js|O-lZV){Cd#86ZY*=}dl0I^i@ceVIg!$6#%eP;07_`kW#%eilCZ zK?D{wI6WY1{ByTkjEmy-KAcd(s3w5xb)nBYdPaBrEpyzPdlgXc!ns zaPfj5FgqeGV9DB$)Q}2WW);Awn8(W7N$F;0O&xM4v zAno+12KBpb9&)l_b<&FlCbaR~@eudoGKR5l(vOu%0^}G`k<^ljtTMlUW~*Cf3MMs` zRrRd6h4ggY!D_>69e7Y!-^d%ee?nTsqQO~p2opXCzvhp_?pK#YgyLaI|2=uJNXy^u5Dh2O*CWOnc?h2CiW5w86 zg%H}k0r%E%ZKc~rqnS-X8F@Y z#S#)sc0t^dw|7>*jEW3p>;%dgA82)IO8mn``KWC6X8CIlX4$-cA9CkN^W5vU1b^?- z5te`EFaZ%gY4jq)uN^%!eooIw2cc~Nx$k!}?vNy(kfV!+VIrC6%-5(g2D)dPg$F`y z#37n%lb^j9)MlVMudi%)Skseqy z>*Z{kKq>iX4-ir)C$jZLW#wPtW?n`%2W7qeZ7!(|5tpF-d=(jnwHtLzetRNLTXoN- z_g}ed2TbL^Hv$bJUq>Hst@u1LM57gn+tl4A`S3@leh)lk)4nh5tt+00n-yqlKaX7y{^pXT<1k zH}76TR*m%E+2Xa8H?|>~JIeU_4R@g0-yZ^YVfw%$Lj+rBq)QL0`oAWHn|Ft!3o4O4 zmJc}j|8N5W9QM7rsh*Fa=>HD*0IKxv4O;$Or1HNH#+!D-WXMJR=ab^>j>qa_A3(ML z@14941VSS%r2al#Fy7|u3+~8_dR2nSHKUz%7;i${#(offo!EcbgB7dgf|(2(13&?F z9N2LgaX7dZsTTibk2U#e*gWMw@niPTVp;$12@ZU1dm74%>hEL?Y%V>@z0L6NQ+WEW j@-~BH`G3Al_)=t7A6>o^!>Ruk_>p;|__|#DeZc<$@c?bY literal 0 HcmV?d00001 diff --git a/app_python/docs/screenshots/docker-run-terminal.png b/app_python/docs/screenshots/docker-run-terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..000d2f2407f5ec3f3fbb568fb11d3ef50241c7c7 GIT binary patch literal 43997 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�OY9x;>RQjs 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 WbTh5)sQ|1jhP%23x5{qVJo`Uqg82>r literal 0 HcmV?d00001 From 954245091046ce5356f9afe4fe29b72947fa51cf Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 29 Jan 2026 19:25:41 +0300 Subject: [PATCH 05/27] feat: completed lab 2 with bonus task --- app_go/docs/LAB02.md | 100 ++++--------------------------------------- 1 file changed, 8 insertions(+), 92 deletions(-) diff --git a/app_go/docs/LAB02.md b/app_go/docs/LAB02.md index 5ca198adef..4c7ad39904 100644 --- a/app_go/docs/LAB02.md +++ b/app_go/docs/LAB02.md @@ -1,62 +1,16 @@ -# Lab 2 — Multi-Stage Build (Go App) - -This document explains the multi-stage containerization of the Go version of the DevOps Info Service, why multi-stage matters, image size comparisons, and technical details. - ---- - ## Strategy Overview - **Stage 1 (Builder)**: Use `golang:1.22-alpine` to compile a static Linux binary with `CGO_ENABLED=0`, `-trimpath`, and stripped symbols (`-ldflags "-s -w"`). Cache Go modules and build artifacts to speed up incremental builds. - **Stage 2 (Runtime)**: Use `gcr.io/distroless/static:nonroot` to ship only the binary. No package manager, no shell, and runs as non-root by default → minimal attack surface. -### Dockerfile (multi-stage) -```dockerfile -# ---- Builder stage ---- -FROM golang:1.22-alpine AS builder -WORKDIR /src -COPY go.mod ./ -RUN --mount=type=cache,target=/go/pkg/mod \ - --mount=type=cache,target=/root/.cache/go-build \ - go mod download -COPY . . -RUN --mount=type=cache,target=/root/.cache/go-build \ - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -trimpath -ldflags="-s -w" -buildvcs=false -o /out/app ./main.go - -# ---- Runtime stage ---- -FROM gcr.io/distroless/static:nonroot -WORKDIR /app -COPY --from=builder /out/app /app/app -USER nonroot:nonroot -EXPOSE 5000 -ENV HOST=0.0.0.0 PORT=5000 DEBUG=false -ENTRYPOINT ["/app/app"] -``` - ---- - ## Size Comparison -Build both images and compare: ```bash -# build -docker build -t ${DOCKER_USER}/devops-info-service-go:lab02 ./app_go - -# check sizes -docker images | grep devops-info-service-go +➜ app_go git:(lab2) ✗ docker images | grep devops-info-service-go +WARNING: This output is designed for human readability. For machine-readable output, please use --format. +zsalavat/devops-info-service-go:lab02 067f534f40f3 13.3MB 2.99MB ``` -- **Builder image base**: `golang:1.22-alpine` (hundreds of MB, includes toolchain) -- **Final runtime image**: `distroless/static:nonroot` + your binary (typically under ~20MB for small Go services) -- **Observation**: Multi-stage removes compilers and build tools from the final image → significant shrink. - -Paste actual output here: -```text - -``` - ---- - ## Why Multi-Stage Matters - **Smaller images**: Faster pulls/pushes and less disk/memory footprint. @@ -69,30 +23,13 @@ Paste actual output here: ## Build & Run Process ### Build -```bash -docker build -t ${DOCKER_USER}/devops-info-service-go:${TAG} ./app_go -``` +![build-test](screenshots/docker-build-terminal.png) ### Run -```bash -docker run --rm \ - -p ${HOST_PORT}:${CONTAINER_PORT} \ - -e HOST=0.0.0.0 -e PORT=${CONTAINER_PORT} -e DEBUG=${DEBUG_FLAG} \ - ${DOCKER_USER}/devops-info-service-go:${TAG} -``` +![docker-run](screenshots/docker-run.png) ### Test Endpoints -```bash -curl http://localhost:${HOST_PORT}/health -curl http://localhost:${HOST_PORT}/ -``` - -Paste terminal outputs here: -```text - - - -``` +![build-test](screenshots/docker_curl_test.png) --- @@ -109,30 +46,9 @@ Paste terminal outputs here: - Runs as non-root (`USER nonroot:nonroot`) by default. - `EXPOSE 5000` documents the port; environment variables allow overrides. ---- - ## Security Implications - **Reduced attack surface**: No compilers or package managers in runtime. -- **Least privilege**: Non-root execution in runtime stage. -- **Determinism**: Pinned base images and static linking reduce variability. - ---- - -## Trade-offs and Decisions - -- **Static vs dynamic**: Static binaries are portable and simplify runtime images; dynamic linking may be needed for certain libraries (CGO), but increases base requirements. -- **Distroless vs Alpine**: Distroless is smaller and more secure; Alpine offers a shell and package manager (useful for debugging), but larger and more components. - ---- - -## Docker Hub (Optional for Go Bonus) - -```bash -docker tag ${DOCKER_USER}/devops-info-service-go:${TAG} ${DOCKER_USER}/devops-info-service-go:latest -docker push ${DOCKER_USER}/devops-info-service-go:${TAG} -docker push ${DOCKER_USER}/devops-info-service-go:latest -``` +- **Least privilege** +- **Determinism** -Repo URL pattern: -- `https://hub.docker.com/r/${DOCKER_USER}/devops-info-service-go` From 1abdcd597ed26e4d3546e163c2eeb580475490c8 Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 13:42:23 +0300 Subject: [PATCH 06/27] created CI/CD --- app_python/docs/LAB03.md | 20 +++ .../docs/screenshots/pytest_local_result.png | Bin 0 -> 46991 bytes app_python/requirements.txt | 5 +- app_python/tests/test_app.py | 167 ++++++++++++++++++ 4 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 app_python/docs/LAB03.md create mode 100644 app_python/docs/screenshots/pytest_local_result.png create mode 100644 app_python/tests/test_app.py diff --git a/app_python/docs/LAB03.md b/app_python/docs/LAB03.md new file mode 100644 index 0000000000..9634243454 --- /dev/null +++ b/app_python/docs/LAB03.md @@ -0,0 +1,20 @@ +## Unit Testing + +- Create unit tests for `app.py` +- Added to requirements.txt new libs +```txt +flask==3.1.0 +pytest==8.2.2 +pytest-cov==5.0.0 +ruff==0.6.9 +``` +- Checked it tests locally. + +![pytest-local-result](screenshots/pytest_local_result.png) + +## Github Action CI workflow + +- `python-ci.yml` triggered only in `lab3` and `master` branches. Because in the CI/CD I push docker image to the docker hub, but in docker hub must be only latest and working, in good practise master always has working and latest code. And docker image pushed to the docker hub only from the `master` branch. +- As actions I choose basic actions what I always use then create CI/CD pipelines. +- As tagging of images I use Calendar Versioning. Because this stragegy is usefull and if in developing process we notice what some part of app is down by this tag we can find there it will down. + \ No newline at end of file diff --git a/app_python/docs/screenshots/pytest_local_result.png b/app_python/docs/screenshots/pytest_local_result.png new file mode 100644 index 0000000000000000000000000000000000000000..93f5e3c98e953797ee5ba70d300f97ee1994b43a GIT binary patch literal 46991 zcmd4(bx@US+XoC^h=PxfWdOI*;=>eswON=Tahg*yPv6SIFFx7M@L*EdJ>pFC$}CwZotfFRcpu_uD^_R%Y2cFM#`BN!WMPkhU+ ziAE&oCQN%Vej?NJQO+ZpNM-lUW4M8l*y0u0`RTg};k~B;*K;^u=IZ$QY71sE>Yfiv zI8Xe6S=dqTQI&xGRgDOa@>|y&&$3wt3VDy(?;9F)Tr!B|o4?0$`}S?j%m4b~#9|H@ zL+JkXMHtjV^{;OeZoL2h@%tx*c@&SH{P%17Mr(7k`&hh0)GFzXrT!9{kG%i=$0tv6 z2>AFa$pz*MO#l6;(Njppb7b@APwZ_v{x_A?Ep*9QNOpZV9TCF{f7 z&nBf7v;xL(|MQNFUszqYdo1TSWywqAwf<{T#x<=r%zP;S!pED^iuPy8y8W3duk7qB zRt!E6Ce4v1W@TUwmpOjlQhcq)En?)!DuBNg)s#Da6!$buZT(|i31#q&!js?eLKoL3 zw3ez11Wuo)2L5c9i1&})nl}lM9;>42?n$0n%hf62);+Jwe@1yAD9B+MZ#i`5lkNHS z<8>pCVTBko-W%T?f~h+W`KsMa2Om<31iAR}IaSLVYi#=Z_|&{X#~hDz^zr`tOH()@ zgtt2=PW$JSy#yb5P~n@auPQ^akNEPGSI#aJ(ne#Q*S0AcrYjdNgb>4em=Q&%s_hq;!>*o>?efJN%#d5nDF?GsV5|a4LFNnw9 z8g0kW)dN~u*}Sol;+m8?UfNw5{!^Fdr6+Sk-<;Ji4Wc3YdILAT1F}kj{HeaFr|mSe zFfg1K38Zw;Te|+wGC7j;eHv|X*u>R3WSG))%EWpYyEEGn-sg!9)_VhFJy}7(po-l)*(GM5}=28~^ zZWELO5_#ENzoaC>e=eV#ydXxj#}|rU@SF5i^4{vt6pWZ@>dq9#NgB|gs_PW+`ZZ!v z_qqZ5So#st?ska9OiqrtQwj(~?U(MxAV_%!|8rFe$K8EWmtSZrr$mxQB)kuMB;|3=R$!m6Y^I zOJlkr741zcxoA} zKt%K|kcyYtc~rsj!P5H?tYXWx@%t;0`RGCq@X}mv-XE26@Q>l9zZ??!X1`rMHG|75 z$(2H4p&w1FVua*T!##Zsw=sMwNgI1fD$n)Ku_rH#ugHJSm^``E_=;v66C;cBNt4g@ zqKA8J#lPD;)DU+QDVl_DniTQ4GhyYw3s$Zg?B&%OAMr;z%o3X4Orqy!!ACYuqA2!H z+wl;0A6t|6E_Y?p@uXX0r)JPkOQJq_{>(SD&#Anuh+E_l_9l>EhU$PIwm9K(>?WB* zgLJoyG=(4k!kg}xA4!2iYC?E#v|PMPZ=NIgbWC<#Uw=VQ=lwe7-eYbds^&KR0ki#4 z(I;1z4CLM}NEPyEg9O zTlPS8X92B;#+88ILdO)hjWpS_xW)36fjmaVxoP&& z>eAXK*~OS~Im`WKJA2iTH}zku(GV)$OSNBvxO{Kd{rG{0Xq%Y@%rphj^YKwVefsp( z>(}*P1m7Wuii%3)0~=<8j%XhrqNJleAEKMNmNQSMs<;e0C|?*D(A~U=iQF+AyhCfd zHd-2aN9XN^X1fb1LPt+OH873l=X1PXAJOlD^|~X1WrcFPDZDdoxLYkaY8S%1uX% z$-FUT+Q6Av7RNWYwnK6MR>lI>>72ur&epd09$6}%p#vW3EAK|9Q+SSB?v&2dr)O0R zRhDV5CXFyUDFzBniq^rXIVvk zmEVgOHmYK;DV>nk^?ZQx>&>L2#FF@)?=zO}k5i(=enCC1!_g}2EI&U*I@o4&b_=he zfe{y9M8}>-{O(;H9>YL+=hnj=dqsKqJPS?g{LUC56+yIHt_Q-&D>w70}2*XSb$~xJN{gCL|=}Iv;E; zPH9%`wOCKs*w}c)#!~YiF3?9tMt(_6y%--KFS|;h#`X^$=>Gh*e^yFRY}I6Gme1uz zdVXpu<2{r9OsN(sgzUj8{r}&C)e#s zmaOIC<>h^8Y^*y`_G#()g>V>t|p3i&$m3%pRLRLN?E#%Y=lyz0%m&IWkavw&n8LrrUgoBel>%OP^w# z!yFGOiHlZB(Mt>c{Mk2k0hw}9Q4z!I?qj)itG?C)Cq5?!A0gwlz4cf~Xlikhi6NGX zl5%o=J!o=LTZaO9@99}PG^Aob@y)QouBo#V|Ho%;((!7E*dz0&PcbMdDeu^>DC?-5 zk#>|n+#&p@3@gQqCD#@TO#>^8UHi0JQf;yt5QJL56<3U;hrFe&qvH|cJ*0{#2 zSt;R=sr9e8xVd+(kfexOr4(%%Y%lru1mjx@&{Lefc##NGS=~FGEPx}gJZW@C;ZxTb zVcmm5bi{vEH!w!f4h``syp(3U(&FHe@R650fd)tH)Af&KSaJz2yj9;MtWv59V0z6w!|W%I zh&dxhzVzjk`_bYSgM!#oPYKfze1Rt{6cmVi&B{GyQZ*5Z3Z5+}p9F5;9 zZSC&sBO&Asd+>U}9R>~ddim?cx3@LQzK@KkiLO!d+TxOvljjx}N0hH_j@p@-nZabc z0PEmS!7a5`_Rl=?9+xjP&~L1`Ueid?$95+t{U@&8_?5lMgO-msySfOfv&AK$qj}?y z-u~Xwf`z;~J$Be$9*|~E&dnuQuRi04k}+nLHvK6kQOPE!q}0@t;>)+KV3bU!pc{cdit~aEFh(&MO3vAo67Y}Be~t+$0szTb*@CQ@4oFZZ#`e8C{NU%+~2**O#uq z<8pX!cYPvg!tIQzsHiBC$C6HDp5ULY*q6{h+hJH_X2}{)P{Smw;Cd=a!>7EI>XV*n zv)td@*%^`J(MQIvd*0c_<@er%n-E>_BtT{kxx}mFg~xmD1X4_)H%&Z8Dm4-kkr5t7Zw(wQ@UH z25*xjk`P*@SGz8HbdE%R*m8amq_Z53-QaUQxgx5veDda*w*5o^Nyv5d2cNz_mvxCL zyspq|yl|K8Ml_+-R`-uwI-jjOM+XNz7T04I^%#bG_Jcry-DIrirXL*Vn_Rvw@%y|eWaltnsHixON!uNcR z@kr5gvym%VjyttIvoDB+MU+$w3^W@K`pmL4=jL@!h~|QxDY>E}FJF@B_NN>XP)`LU zHdP60x;{MaJoBY}7h}RSS2{YX9%U9pqr|WrphcGS!onhGb)_52yvxekCcmRm zZrcEz`Z6i$&9aVR@3cMrsHmtr_wLD;bnM+4ue;VJ$Z;0q$A6Q-RyD1-mcPlxUhT}x zW7$E*czkR5_1(pTUPUK!VFi*)$e_(#S^HIoqXVH=EGJxlE!iU9gr}-C$;0O6nngG2 za|nM?nqzOE>#}b!-s#@Q_h!&f+xn8LetEu3QVgybbe(ZHvORx>j(kYX(3u_S%MnRT zm3m5;nlA58H)Oecu*Zv?DuMYobQ%q1zAsuv=>}}udnZ` zoh1dt-OtZ&wCP&%IO$lK4Ng*0QtrVzIZf;9n`yd^_X4hvZ$vL)Q4r1b=`b4k1ez6I z-r~XheD3j-(>kqqYwHI&^^;zs?!}>(%S*I{t240{iIsupjSdEtO8kCsBR%Q%^BPzeZVr-yV_vaIyWX zA6SCIizXR*MJH;5OB5bBcCuek@aNI9onIVzIJW2MA28AEeYEzladh1atWWzawxF9F zur9FXxr>qIvHzyulZSAFK#Bh6*Vcf&{pabUL~b{0jvO264vL4xa!qxMnMWRcZ$&U~ zH_~osI)=sF%u@A8v9x3i3=(k=YgmXa5IB3%aj@03FnHNh%N2{0h$uBGYRc&Gxu1J$ z!X0yMjBHFb-y)Kgon$N}aBGkDB8HRuss&V!?Y@cvnu@h|r0f16X)sYFmn^ShFgfO{ zw_up01_2NN69Zs1*SuU8H!o8K;zKlM}w#z z+9Lp6q6RHz8mGGAg?KEd&p-7ilQD~m;kEM&3c`<$iD5VHWn5TT`11AZd6&a&v}>nT z6y)T-E=RjMU0(!iU(&F#v57Je>a5Z@{7Vt~*vyG9Zyjc^ken0$To)55vsuP~Nv&Sx zT-vV;634>QQa{<<*9*@9M_uFOEFAU`eK0L+0wNubN zT`iYWXvB1;D$|CsQ@_N%o~ZG89xJ3M>t3#7`_caNShiKf&HkXCvYER_#oB|(y60u% zZXdp~UZEu_-gCZXe*E#a1QQ05)Y*DDCKP9V8OvKbf?B_LC&TWJVed%pz2GmxTfW_b zX6cchnOaJGsm0F)aTG{!7d2133tLWp#HaOB7E?cb`crS{vI{f}#)~)~KRv}uO0p*; z^#Y0V_cmN#RC!rKSQe}Ny?f6APAl>`PG<2rbxA02*<~ffqY;sk7Od<(oa#*%!H+C2 z$5}7LzIgpa4_`Yf4L){h92_M3cti3nusn4Qjo=R-+@W75T1-@NO4*u^U8pQ~5^@!hiw*O&q z*US~$1HSL)yt0+~)0Qst=ai8g)oqmT3G6Gm;h3H*#XJbJS>Dwlqp*=<4Bq14=H{+% zY^<%TOWd6#-DLuDoUV|uq%QCeX1UyVo1R`MUfn0vXeiXE(f{hk)>d6d4DatTy9v(M zJ9x&xQoH*5UCoGi*;u?v9_mP18VES}GSl0iJ}F(QB>3pR-l=fhdRDpnG{;7Feqbi} zWsf*sqEpm&Q4#ccA-TL&`3BV|jb*G9r8YnN+gGqAsz2uTabw2~7kZVtW;B|IsKfBd zn~}d)pq5@^Bi?7y+ni_^)cC9-DJ6ye&K&~SL?#7X|G`+nJb1CAXs8{pUTT%Zo^Jk} z(C0nA_?*15Z2C5bmiqeBtWxZLf7w%F+nkW);2_H>DeS5v`X|4>(WJ1rt)`T1YO-Gh z*@BVHqD%0-__u*18h@L6I$!C}dR=nfc6B`u4Gq->HlABh@TA(+S2|eqKSC;AqD%;H z%mbUpp#733E@A8}ro?gC5`Btpn&>1Zi3r!aWj8cTZmcX&B~Z4s++1lPr#mo)>=EXp zq(0V9-0uV_BrEjOJr4B78^4?9jC)^yme`+r&sf80lBHQb=eop>W>)v}&BG}#nhy~X zm_kBz#slTF|0vW6O3&WqlnVXlYTz%vIi-oR_-6lk3OqHzv3B;-f7bT$Wf`LXx4y3| z`qdd1^n0dh2AXid81Y}%oup&sg`zX5Z_FmDsOmiN>HzRgcso+ay?DWZje~Rf%9Z{> zNg1*uoIe*`De;e&vz_b{80ea~WG2zk(Qy$AtLA2$^Ur@jGgHOV($YbX`z`9nFGz0m zNDI(%a1b&vF>%CQB_q>bAFr%E;UV`z8!iiEAQbl-Z0hd597N4WI1UK>&opy8 zeYiGJ9fZ%nkHAj&m{ILrY5u1MIhtX#}ZQ1AKf0B_wdi9hX@L2M3!Ka+GzK6m~6}i40%9yhubu^g>@hJ~cIU zc&5Z^4tsQT^lOo@h=^W;FUc!Q%Nn?69;<-w_K&wZWmHvtp;yv_vOAEYC8DlQ`H+uq z?!Jq7wYhX0zei3^&PEj|Y*SNH7xD2$3=D1At*ZMda|QL$@A)wsP7YDVKk(-w|8=8hJM9ucSVJkomRS1*C$PB zsEJlO`rSKpAk^;%E?l^vK;0D1s)isy0a0(SJ752qy}kY9bc5euo}QO{6!Il0=>k4J zK0>nGP5IEC09N#|l@*7CgoLqxr4^;u!+{^It=JUY=I_J9ufU@pUpJW=X7VFZJQ9t*v1XWHj$w z`_mYV;KHFY|68O^c%B2*B^_dq9z6KLYZ^87yUmjE8|&ozNIP` z>Y5XtZmta7jWTO&Y<#YuAZ%pBbcIdRzvdn0fO2Dr<;)%PQJ&}rHrNrjH6Fiub?3HL zl|P`2x|SBP*Oi;;S>uNfE(r$~Gn)+;T-HoUN{R<-0deo`mDec`3=Hh$XJ%$jXv+!U z$Wap&y@+U^9v>_WOMLtr)D|LB7$x$(@J|+CdSSs^B#2rp z%bLDOGq1isV`b*Up8cDp6n%7e=;w(jHL2?8w(Ie45yZW`ocH+Tq`S<<M{$_DV8Ux!pQ7i~#&cj;h@HvDM<78pZAH5)yEU$Q}JwVpRJ>HsCe%931(LMngBG zWBoh%k1~gC`}?(sOaCfphSjjxzbe{0cDXNu^!WIgfRK=mf#=@6dy}iHcnGSq93O05 zCL*HGT$W7}_PbKc50e8Lka(C)#ZJGP)c{NQpktT|rS43Bj#kkAzI}aDQ{tB|2r>!g zQHD}Zf?iV~wDBAM0Rim522l`=+kBK@Saxyl$p;Rmt+K_XrP}s(Tr4cC!D=^wbLY-I zmzAZXv`ZQsR9+b_#OdztZf|RA)a(cboVI~MzqT3J*$pUrnu5Y_d;2wvv&rS0>c&WRJ@QOE zU)kB?(y}r-W@hZ=fgG=@Dpwco;HB?iZbB7oY;T*cBooLG{boMa_)m~v&M7H`_tK?H z3lr4>$Tc1wGEGfQaVG8?H!z^ezBkdhshg|-yauw0hwAAlnO6MSv(N29cZ55%baZGz zaabxh$_WGG6g87;k6_RwfW$<`X)GTT;=Z}vqtv&vJYadaZ3Ij51w2YXq_qH!P|M1q zZeVZEW4ki=#@qX%>0mAzyfBO16&9Q4^#TDU6&1V7;cJhj3Wu#5&d$!S_SQ5)!@}ZC z2D10}XM(!3)VMubO(X*Paza%spbU%5$9$ptf-s4D<;s=)m2aKIYf7zERXJ8}X*tyy zIS(AxDGIT0aqCO1=QlQ{gcxqzcq}JJwY4?Z?aWoZ+Lh`vms!XWzu4c~&mtS{n4H$npFf{8T_GV6R8lIYRujs>K`E10 zuf`j8#zLJCBa;9_uy5MUE~expK&9!7mRQP;j62Lz@!Y?Ul{hu41NaT3jFPgl1b~am zx!QtRN2qc^2crGzRj#^C}9f=NELH*e86Qf?mzU+kmb6d<5Dlv z?EE~cv>#8i!S+zfR)14nEdbL%eg47U&1B(6*R>WZV6KRJVIgNAjX)smOn84a0FK{b z;K`;+0Jb`hiAjI|{#B$aP41>dG|%PrimgC`yZ;Qx}YS#9hBFoR*903P20{ z%~>p|Xdc>38JLG?;0Nne11gAq=s*a8o(s=^ySsr7AiHy0tq508P_Xt0IKea|0{}gr zL-}eqZ{FNq=th^4lG>m4W7`0f^cdhi01A}ygpv*>C*7m&8Zy5&H@1d)INlwxkR5?R z_F@_!@|WafPioLR7P=Ef#l_!dXW#b3z54d*U4x63VB>l9Z;h&Z=neqz?P?iUoJ6V2EGH3_`q0i%^@{$q+F&`^a zFv-lxVbj&s&3(~A2qmZyf5=?pa2uK-NZ0_#Jf98;A0R}}W5hbPn+qdU#X;o2%hlg+M?luDFqYW1A z%1~k}ZDRpB^q?;LF2@;fALqovkp2sCeRU*B3#q&-fwoR#l6 zY`zE$4?mYG+P=LuMgeUeaR-kH%hGObv<83`CcMq|NOp7pa~pemO(EBxBQ}|NaJjYIT)$t84RT(zAu} z%JMGEhFv)XOsgh%fZcij6^h^jxUGdbgMxqYoxZCn?p8VLZOkbm9GsjD?r7*s^5JAa zx$EH_gV)XG92YhRw;OQ>8p2XI8qsGP*W2AK<5(>&CDjNB1>6{Sz|VE{_2(gp1|dih z2Z#Hfm#CQQp$|f*#;0-Fz6|u}xxD;)P-fC>n|tCK$hb_&U`U*^v$KG>;f5fRJ#>-OT!09s9g4%$#HkZCkPD0gM$N@iey$+R$rgW`1pKG5#SDoEsvrl zP>T(VJt>!|sPN!Epnl@+?ygk?DGf~!^s|fW+-Ae?VMrvvkR1GG7{u{e>hD&ZQ;P9y zjrhtH-Kkm+unIAsvu8=Ye8~vJmc;QQr^Wcgv^^nD+!|O+0x~j220OSGtS`Fq;;_3t z_Sn7^66$=hFWR*ANQXJNuvGHM)(sDn zgI*z*O6iaZb}S6+{ekM!3kV|{o09PXgfC&i(Z=1*T#~|%IN0~Z^!GC0E!V&*8_$AZ zPX$ml4>)YvtknOAKAfH;wZiK8?SR@-16c`C;-R4-9jJ4g+r=UIIqMlflYT(AgAFCS zT!=@>LjY|PjI`CxfH@FA7@3)C;SRF{G(b&XT3TA=YF9p_IcAdS)-B?v9FF$(*8~J; ztbeuF000RN3rpJGCHhNE>t4>N3E_phtVK^12N~x(00I)`b>Jc>bV1GU>{QT8}-Ave$yJ34sf-J}h_$H4Vgw z_^^Q+#UXHfV#I-H$ew7%x~8uz;~>Cxo&tOU3E?sUfxAW30S5f^jAE@xFAt9?=x>9$ zIzpA9)i%qD@8JO$WZ4m$S`5GV(^gzbsHp>^qbbwAs@s_UC!G+r6SmX?8O{$MKfYtD z+CP_;mWFC`Aa;jxxO_p~{(h}cR=6N~k-*U5TOBT} zEkcP|j~YTVHj8AS`57Aj{CA&1qB5!iWIVsK14^W&!Mk;>i2lro#3&d3aJV# zR{7h)1LA10ImMg#IaP<)H>G2rxVrN5AFbT=Xv1_zNiVUnv9cqLK{RZ4^PibUKLl!j zOI-~e9lgM0Kf)x0D_7#1CAfzaEGxdu7B=OF$Ma|+35+p z^)CrjCjmPMx=`BB<#Xx+-E#VghseD5fhKfcQrNVOMP?)KU60@uIEnU4A(Jw@ zJ1z=G&onBWY1o*U(l$>MGBQ{=H=meNUaJxqXmY9GBJ}y%dRZCDx+zUAW%hOs0Kevr zjz*X#P_MKMV%FByS}ktqe+%v18K`oHXfMYIvj|1=5wZmNB@pC3e6qz(wU3mi(m!S{ zQu$_}5Qv*jIrn_5`8)|p7RNfj#TG$; z$orlRf$R(Q@}dl2TxLuXpZP(&Plh$LoaAU&X=3PM65; zdnzJ=iBgFb6~lX9)-^YaMn*;TrDgp+KDG^va%ZJL;3Fg?{7S6}XtoYO)LQ=L)|P$< zhTGYRU7=h)Y%qlb0trn}?n)eYn5zz#SkhLNax}9ze10cG0u^-ag5Dw!CP0a8i6(V( zbK3{i3!+C^s)cX;{BU5e-Z1WcqySonFkd|}g%ZuvKuRAdUmO$4%Hvo0KbME4i3kae zC9(lzC`6GFB!VZZT^;-gA#-^};{sdZWha{Tu6Xpf@7^U%=jZ1`CDL*_N|~x(XFnE zR{k1H*}A4COi6cFt>v2o57D-?g{62eHltP%W~(>pT7pl=j!84KY3Fq zR@C^Ge>f^7AtAr;=5s}Tv;ZP1EiGbhep^-+4gmo^f1j6U87Ak~*Vx~G1!z$Xz_s6N zQah_r^vHxM}HyRiK_+V_-{4Q9_d1W+y_pBbMIlN^`)o+#Ps48+`E3TB4Ufv}DT>>RN0=iZY! zppC%&zMnzdQn55BKgU9DolW=jws&Qa+wPDdO7w$g=olJ$&Cb36;Z50q_{(64xbBg) zOX}hwfQ#w<-!uEaK3m*@_!dNKr`x0M+HXvvp(41YZ4?5o4Vw(Qz5JSwo`3D_8(wnN!pDD7{OX&(H{P3lr7{G{dJ-Ggf)5H^$ z6=R0b~#;6crg4gzh8i7+y0j8^g-I)8bn+|aY zCIxE7^_^$4d3sF=B_+!C)zE-Y;vz(qp~;11NI7`(xhXV&)}eeuDiaKVYtT?p;2T8T zE&$&s=?Jbs*wi$WVDDsa0tKOgkk%FfMVN$rx)0ttR%~t~HR)R_tte|yy}7aB4m$`!kErSp zR8yl+R8%CpZ}pWR5q1>7?kNx{z%1|r9~~4CPe{lysppXCHTu8t^~Huw<^^;}2=pX) zpAC@ANtX;{JUZl>JEhxQe>-?o5)gprdU{}J3a$ejGR_K?k&uwU zaRDm$-4+GiNq^T!?>$a9$d-Mid#~`7wY89`>8*zkA1;+MCn$wLfnZ}}=Nk8=H=1KJ zsQ_9{mWXa?;DF0mPu3!wi2ds5?_rQ$${!8~7)xHkb`|miqRO)~hWSx@cho zx4VKijROS&M@$k63W#9<)WL}dkPqe^bJQ|X7B_Ui5^!IvtgQjdp+S7x8M^-CAH5}Yx7V6-qpFrqcy>v~;LazK(U_zSe0KYnmLv?;(ioVKgO9?&lD zy?Um_y3?0qd_=9Hs+s_82<7gfA|7AI8y7Ia8%L=UT&9E9Ouv2m_6|r57f1pM8H#<+ zDs`{lxG{}t?QpOOrIzU&fe1pLr?Bay5hx^+(>9@{KQ-~}?~OiZ@?o|kU%!ojn3$nL zz<%QzYPN!IGYK9W>b8Qv?Cz^&7yBO_vTY5OhK#Wr4`!B=7>&MfjS{JVlp!Se_$)tS zcuBz`CK$)OaG|EI4jt?&kOv##LcXLkyEsb4KJd}@EAo@B9xKHJ!t>FLSY+4m^A z=XQEV*3e z9372|0NNvhGlz^jCm&MceEB-r(ih7rLA2J{07)IrkbR|I@~fozbCd@Lt_K{(0ZLp4 zHalF(6BwNc^0&a+Zf6hbT31)scbMZ#Ndo2n$N9x4OUiSLN;@ump<-80z7%am!`)|+ zOV)jJg{3ssn@CL z=_rHqQN!r*gv%Zkbe}trFaYQ4`@{qdScNsMVJse)R=P8go51-p9d7}L-@rb(U9i}I zc^lSAxybZtW>(g3hznc;2!tZjnwqg2+GGgC$KJV~9MbagQh*xb6%-^L6SyY~y$(!{ z%$X?H+aTD(W|wU`PB=bxMg{2rhKNAg%5w4Nb<|N)fKgYdoqhN^;a&9A3y%>cWT3&y~EBf2+tlJ8 zpA3)-MV(2cqdUi zOP4$=EBT%Vkb8hu3pNwLAGOyfVM~bunE!`fi-phKu*>pAC)+#EE+M}W=|1U#Dm>-ZbtZrGk<>x;>IDLN zxY(L{*=sDgWu$8=o1+7v2kV57MqU0sgqg}@l4~>se6zdC#R(yi`(08}(p-;PT95;3 z*mZZo*Ehca;Y~BQW>5b1i~v0 z7=L7D!1w?`z5V^`p-U5hz{{#uG#Tf1DhRO~)aCqhCePK@*0!s+w;trXbZ1e;5lhs0 z)0!GI2o8`G?ZB~n=BK8ARgptranY%+xjD+8nyQ2r3s$a2upY{p2gAstbN0ulo#rtl@U~}rn}y$dlU!Trap9jauNZd0ouy5zRUlJ!VmX2_0P(m zwEwFNLOJcyohBjQhl&ohn68f$v%!dww+pKL_jr4&8Rw`1t1!&t~?mFWu!B>TWds_59L()3jhAUv?|9$C$0Oules23#%{- zU8haqta=~zjq6@h)L>9NtBQ`^;9z5WWFL98z^Lt=7Hb{fj93&_n#=!?7HQE5;Xw{Y{wq7d_P@O`c5Ug5N z>Y%#^7Z(?UBx$%kQ1h!uP2G3cTyW1XB_#z$QVPt>MNof?2Xo_@m9jHozubTDKs*Gf z7{l29V<*_~#^V*GkWQL|vz5@x6T^&>i#|7nI&|o@9w~l+__jEQ)ay^1)}Zyej+{=k zf+)KgMzVLJ-X$K)D?`5Sm6oklZ7$kOEK+_}f!`j5`cMm#quo`6&YM!vck0KTH3W@&Q{%yyRx@w2 zx+~X6D+cKw&>-dHo)iT2bKjm0+qlyG&1rm1RD8mFzFM`Oc=&&N2*80h>AbYKTNK&I4b~qI*diBW1}HH zO<>-kB@18~*{|=EfZ_r6aWk{59Sx0kB64fhA+7?ai?L2?-Ef{_GtAJyUBv_>NU5AbmueSoF#)bho+715N{)Rw_rKgi~DJuU%t8FzkFl=)SK!``ew z^&RfH6M&1H(I3O+<5(Cz-7`wyH0fvL<2woW@qrZz#jYm(5j9_~L+OBhcu+YMzn4hELyRsI3r<%#RRdNEN8k^-kUd#TEZs|yaZCy+S1aEl3z#HR@X@7X4=j6Y>fx9v!IVJLNjv& z$!7=T8x}r3jl8@(PX*qe0YYhK2d66|#p~f}`B90v4jpo#M;1`ea4PkKyt4BYF#ec9SRSvny$M_F7+-2aXpF!3A zfWW};&!5%eT}t|nra*gzV>gLsrz%S02W;RKmd`1-ZEls`YLpojs6LZduNE-3oA|tS zV7hdo6neC+*q)XHCakZwH^_^jpe$})KiP~tDpib}u7b}1fUn#aNkvxk;5~b?i9lp0 z33%lEQG)hDSG?oa4{lDICHcAtdPpXSOG@?ufo>0Mvpzf7V}X-;@DU}l0H9tN8m0ih zs;eo7wHN@xwcWwHo(e|?;q!#Lz{tiK{lvh@sQ_d`*2*fM!3eA#D;t|1cKkt1GnFbJ zrG&=BWC6udlbkUFm?{<0&VO8X4Yi>DRJ@8{dUgXM*&em-Y-wl^1{t5zS;l%RejwQ& z;Nw%2Tk3U*3fE183KEZi7ZcHAe;6s1ot)?zN8exlWK|8HNnrxn2P;cU_G5+8^73-m zgE>;ZVw-z=+|FrTV51KW4!#5t z@)LX*545aah+KgvxPU5UM+l&T2YEZOtSNxP(4LZ-+PvT1!T?fBuO($XJU!E4CN)@f z1eI=?`obMYwM2lk53FXr*DCikIbnZ-e#+@Y2|jq4?dm63#0*dl!8K0y#_8Q!9^6Jhp8(Xpcjz}qs^tvyND+jBVDKuSQLwrk|8Dxk zZ4oPTCTTVh?ZCBjdIV{e9dIjJ`}-B)caek37wfphlt^a1R(%2I3Z!2hhRf}8;2=`i z$B*=+q@;;omG;)g!otIop(H^*RY+GYOa~!28CVk&E$vkJ39JA8y|6TMJ1vsEBg^S} zEIbW>=BelhHFEsDDn zRLP&9ABO^cn2h)VT3$aCeHzRZW=2L)a5#;J3(`=61gGg>7h`{4p8|L!N}{6PVA!B` z2uzj?I6LY|W<8EChevkj5vOr)7wosfS5uGSOrH#tbT_0C%)Hrm=If#-L>gQKNauMZ zP7YXoIt!msF3}LPY-O;#VmiB&XW@n zB|_%|LL>nqpBx0me24k|Joot%2Kab7AuQmuBalo6DF`UYr4{1*^W(IA%b81O z*4J~uG#3wnI0OkHq3n_igx7z9SNE)P>)ILA};Q&kG5yeF!0E~LI)VPiMN5C8A|{)6h*dhRbX+F#aF*-a0DFv~L?_bR5S(M+H$3m{E`tkXF(H3F&SW zX#oN0vM@kEZa@SCq)R}$K}A3sNu>QYT}|`V0~v*zJ!Oup#+frW(R^qJm@rMR zREr>WVAu+LYZAa)G?i7Al`NPD0%wZ|9eDr#eFTPdbS@8`Nm=hup5B8^m{^ zJZM7`B^}2{GLWfbof%OG6{ZhPU;lg)zi|8d%6JAT4%;ggmrC4w1}Q2Ion~yXG-R6Q zxFCsz42{fl!2D^IIAxl5yfCvZ$Bc}TPfM4}Ie(=!Sy9FV)4Ja3J z35l?XM=Vn-Kb8dZ4&(|Cz+AHA+`%A{b?!B%ygOF+7rkc8z}4ITFT8$em0yauuJxcyU5Dlv~`F znvcXFGV}JpPI7WpVZtk0c7mN@u25A z$%lb{tAfXzp%n3e(bDr(J7quBqlpf54Cp1C7~x7Op*=re!xY8L!O{3~j7a$ek?a#Z zwd_^oIV@5xqwyg(AcsPvG&m;1^fO1KK{P3@< z*D>RRv_#bOE;meFXhq!$-gZ5D_^`$?&nF@xVhWI)qn(SN$Y$MJl)L}H0rDn#*9k6EOR5+~ z@CA9@zh4I}GE8=Oiak+axGWEb@EO-1a>o{&otaU@4Fqr_88C$G@l=KA+t`iv00_Aw zeWeE87PtS#`igz_IP1gzUf@r9lEIOUHma$qscfl2vHK`;cwit2HT?2dtP`IeiN0FD zZUFe(9_%}ogEgb z-o0bCr5UUZjodpoHx~$k6L^*s6clmTCh|l=mYr=E&|OZ6ohH?SWXvk`6`nnJ{dxlu z7>1gW6A_3RXi8r~H57>bjxMYA=O@>2Aw&dK3}w+yL;QS)H0e5>|7qM`)C&pLND8TkJXb99hQ`mI1EkSviBnqOhFAY%=W3&HDA&85{X*C z>I8s-h-0i^ARxh-U0iH*OiN8o#ku@wgJ-Z*#C7onF3u`cnB|%tJ{c4lNo3p=YR*V! zu?1DiCcjH(iT>E*(#pwcJ;_S(-sry|^EoNif?)n5m1gF^X2mgoFO2{#90$7~_QEZ6 zX0Yqy(T*Ply5I@J-&KHwed)J1yQq1LABk?R*@-NEFvMBXf_}{|9rwVnuoRR}hw~nE zmwT3jdylahdv-OEZ2(`Cj3X9_#665Hv3rP;N96nGJKOY7x6vcJMnO|Qu{eu6FJV8{ z_8z4{@Z-n7Bk=`;a}=~KbHMv>oeWZVnf7q&vSbv;wVj>nB#Og_lW_oI39{>#BGkRd ziF(!0Fct?jRiP)K@+xT26HyW?k0_Th@(92)Kt-(jnJ+&qM6c8ZaL`Kxat9&Z9%IEc zox+xHT4sM0_qc;+%-C`V{BZMEmZmcRBnIohrmju~<&qls3A?!fP9k!W_x4e4vk$-6 zI5`zCBx6uaR4{lG=%KL^TOXT38I@L$0#ZN z`_=6~bLqdn^z!#VljXnuapKqi-H%Y#7Ht*JwCWW^1g%TDhH;h)ruRM+CJzaG3z4~z zazO3FQC9t894+k(oaEyuBWoKPu!?edp-&~UG7{_9mVqgmE~as9Xp*)Aqa&2^pA2}w zB!JBk$rf>XB9o<%(UH(>}a;2sL594^5;!~(2(YOX7H^R9i2K_4aAb8{mF zhqc-ZjcA;?X29fufIwm(I^E#Ps-cC1d$1RkuMGO|Z6GA3V+a;5V*KN7)Ji8aI_fzO zQm&&Jac>y(Z{n7qPeg8nq(TW<`Fnc;5=#`oNEVPJ6<%1$^W5|(-P~Bk>Ilxw%}r6y zNJobdBMnWWiDlO$VlsOQr(G(5z*H8+`ff8`pp@?JTn9@@5Fq1#>eZ20e^zAb=pwYz zSeHbjXkkOFLH$40m7Dr;p*>0QJSq|u%;%B3Y8hOTs`p`Nh!WEWAhO7I&>KL-TeV{{qIvhjRDP(`O)!;WKT%`JZy{RZNhyEfX(q+vDHNI@2Q}-4``c zmdPVv;sN>+7!rAha@;ngPni zpWzt?C~5a}C>{aM5b{nuX@5os`5%E!L5Zl?XldD{hzr6?m@=22&a?w6JU%W?k`##! z73x{F6>N97tzdDK%^4Qf-~l=G`WU6Kg4uWz5Z~@qWADt)&(}NpV#b-d)PN=rC96s^My>HE zLPzoFIC(pQY$yhB=wWFfe%`AFlv&_~DRSZGn(hT3t;^Ssk5(Ntcb1Alo@9Yp6TTHl z4nB381|NcFlnk)AIVK=og?5QVhhg8oXmpOogVps|ls11ncII7!(y;*u22vydbdqt{ z)6@dHoCfBZloAw*RM6R!YQgD5&gJZnwa-dVAGLo}A1})&g%lFf>|8GF)6WjoV@)WDP`VFtq!YGdFhR{J;Wi`TLf>+OP5V5ssCRh{rXKUih*e zj@&<6d{Ql_e{+$^Zy9%*%U(&3h0969F5lYiN)SW>qCI5KVDhA)2(#$jVZ2R4L)?y4Nls5XE%WyckbE5V`jk zXW+|uF(Qw3=B`dsjGf0DrcrB_6i{o0RB+eCTR)ZW!coG`EvAuk6rCkcYwPOjUMevW zGgowVWFcVo*N1_Pw9(*?txQJtsJET%pYEN4@c+;D-2XvG@BjYD-%o~&?0?w<{a@ZO zX}-b(IBt=a1sMl2_D!klyT_YEx#zWYmj-8pLM)j0Fr zHH-bGTMnF+AGtQ9nfMp@g~O?k3cy2(e12<3>>k6Lnj;$9h|);l)@%%#tb4d_qs^3n`AvPi*@h{$wVSfofQ(E-#@4~1 zgUFPT37MnQN}OB-W+P|)BY4~cBO^&f^hA2vh76MOzyakr2ide~V`L262akVv$`@HL{z>Nur>&@4Ie_Z>KMxX58$D>|bxu`*SDuAGYF(g)tN8x3piCB}9Q zU3_&fPGxlIX}LP3Q48{^e4qX@_ThP~@pNPw6}@xj&l0Lx$P6(+XO!Bxcdt4c7358u zM1!7alo&UxtdcuVWLB%7`-oXhJ@dzaCr^$8jkPfHzCxh`|w569x;*nfo;6$gpYRavEbQIq5Qcz&T=gC3R9uXHy|FskmUj< zT55P-(*>4xUHmK=GMnS+X zSsGOm+B(taM<+`GdC5rzM+`clnUHkUv}O7Wtn>h=B5N9&Lx+;kRWidV3064hg2 zGl3a`xJ(!&ZuYqA*N05=;8e3At;Pc(P!6c^>dk*t@CrpoQUsYNrNgT3`KjtA7E!M> zE1!TwiY+4? z=bWrf&@Bb6rx`sT- c+u2GzT&})m-&kb$0se%l(Au^Yyd=@Bu(*b|1 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 P4Y*XYY*F@C9^d^J85()1 literal 0 HcmV?d00001 diff --git a/app_python/requirements.txt b/app_python/requirements.txt index 174be8eeb2..a3ca776bce 100644 --- a/app_python/requirements.txt +++ b/app_python/requirements.txt @@ -1 +1,4 @@ -flask==3.1.0 \ No newline at end of file +flask==3.1.0 +pytest==8.2.2 +pytest-cov==5.0.0 +ruff==0.6.9 \ No newline at end of file diff --git a/app_python/tests/test_app.py b/app_python/tests/test_app.py new file mode 100644 index 0000000000..d19f755278 --- /dev/null +++ b/app_python/tests/test_app.py @@ -0,0 +1,167 @@ +import pytest +from datetime import datetime, timezone +from unittest.mock import patch +import sys +import os + +# Add parent directory to path to import app +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + +from app import app, get_system_info, get_uptime, get_request_info + + +@pytest.fixture +def client(): + """Create a test client for the Flask app.""" + app.config['TESTING'] = True + with app.test_client() as client: + yield client + + +def test_get_system_info(): + """Test that system info returns expected keys and types.""" + info = get_system_info() + assert isinstance(info, dict) + assert "hostname" in info + assert "platform" in info + assert "platform_version" in info + assert "architecture" in info + assert "cpu_count" in info + assert "python_version" in info + + assert isinstance(info["hostname"], str) + assert isinstance(info["platform"], str) + assert isinstance(info["cpu_count"], int) + assert info["cpu_count"] >= 1 + + +def test_get_uptime(): + """Test uptime calculation logic.""" + uptime = get_uptime() + assert "seconds" in uptime + assert "human" in uptime + assert isinstance(uptime["seconds"], int) + assert uptime["seconds"] >= 0 + assert isinstance(uptime["human"], str) + assert "hours" in uptime["human"] + assert "minutes" in uptime["human"] + + +def test_get_request_info_with_context(): + """Test get_request_info inside a request context.""" + # Test with X-Forwarded-For + with app.test_request_context( + "/test-path", + headers={"User-Agent": "pytest-agent", "X-Forwarded-For": "192.0.2.1"}, + environ_base={"REMOTE_ADDR": "10.0.0.1"} + ): + info = get_request_info() + assert info["client_ip"] == "192.0.2.1" + assert info["user_agent"] == "pytest-agent" + assert info["method"] == "GET" + assert info["path"] == "/test-path" + + # Test without X-Forwarded-For (use remote_addr) + with app.test_request_context( + "/", + environ_base={"REMOTE_ADDR": "192.168.1.100"} + ): + info = get_request_info() + assert info["client_ip"] == "192.168.1.100" + + +def test_index_endpoint(client): + """Test the main '/' endpoint returns correct structure and data.""" + response = client.get("/") + assert response.status_code == 200 + data = response.get_json() + + assert "service" in data + assert "system" in data + assert "runtime" in data + assert "request" in data + assert "endpoints" in data + + service = data["service"] + assert service["name"] == "devops-info-service" + assert service["version"] == "1.0.0" + assert "description" in service + + system = data["system"] + assert "hostname" in system + assert "platform" in system + + runtime = data["runtime"] + assert "uptime_seconds" in runtime + assert "uptime_human" in runtime + assert "current-time" in runtime + assert runtime["timezone"] == "UTC" + + current_time = datetime.fromisoformat(runtime["current-time"].replace("Z", "+00:00")) + assert current_time.tzinfo == timezone.utc + + req_info = data["request"] + assert "client_ip" in req_info + assert "user_agent" in req_info + assert req_info["method"] == "GET" + assert req_info["path"] == "/" + + endpoints = data["endpoints"] + assert len(endpoints) == 2 + paths = {e["path"] for e in endpoints} + assert "/" in paths + assert "/health" in paths + + +def test_health_endpoint(client): + """Test the /health endpoint.""" + response = client.get("/health") + assert response.status_code == 200 + data = response.get_json() + + assert "status" in data + assert data["status"] == "healthy" + assert "timestamp" in data + assert "uptime_seconds" in data + + ts = datetime.fromisoformat(data["timestamp"].replace("Z", "+00:00")) + assert ts.tzinfo == timezone.utc + + assert isinstance(data["uptime_seconds"], int) + assert data["uptime_seconds"] >= 0 + + +def test_404_error_handler(client): + """Test that invalid routes return 404 with proper JSON.""" + response = client.get("/nonexistent") + assert response.status_code == 404 + data = response.get_json() + assert "error" in data + assert data["error"] == "Not Found" + assert "message" in data + + +def test_uptime_consistency_between_endpoints(client): + """Ensure uptime is consistent between / and /health at roughly same time.""" + resp1 = client.get("/") + resp2 = client.get("/health") + + data1 = resp1.get_json() + data2 = resp2.get_json() + + uptime1 = data1["runtime"]["uptime_seconds"] + uptime2 = data2["uptime_seconds"] + + assert abs(uptime1 - uptime2) <= 2 + + +def test_timezone_is_utc(client): + """Ensure all timestamps are in UTC.""" + resp = client.get("/") + data = resp.get_json() + current_time_str = data["runtime"]["current-time"] + + assert current_time_str.endswith("Z") or "+00:00" in current_time_str + + dt = datetime.fromisoformat(current_time_str.replace("Z", "+00:00")) + assert dt.utcoffset().total_seconds() == 0 \ No newline at end of file From 4d6c08bc88cc83ed362732079eee2f62d6a76e6c Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 13:43:30 +0300 Subject: [PATCH 07/27] created CI/CD --- .github/workflows/python-ci.yml | 80 +++++++++++++++++++++++++++++++++ .gitignore | 5 ++- 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/python-ci.yml diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml new file mode 100644 index 0000000000..08751a1e36 --- /dev/null +++ b/.github/workflows/python-ci.yml @@ -0,0 +1,80 @@ +name: Python CI/CD Pipeline + +on: + push: + branches: [ "master", "lab3" ] + pull_request: + branches: [ "msster", "lab3" ] + +env: + REGISTRY: docker.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + ci: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10"] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + if [ -f requirements.txt ]; then + pip install -r requirements.txt + fi + pip install pytest pylint + + - name: Lint with pylint + run: | + pylint --disable=R,C,W1203,W1514 app.py tests/ + + - name: Run unit tests + run: | + pytest tests/ -v --tb=short + + cd: + needs: ci + runs-on: ubuntu-latest + if: > + github.event_name == 'push' && + (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata for Docker tags + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest + type=raw,value={{date 'YYYY.MM'}} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 30d74d2584..b39220177a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -test \ No newline at end of file +test +app_python/.venv +app_python/.pytest_cache +app_python/__pycache__ \ No newline at end of file From bba93a7b28639ddb5d6f735c4d32d827e4514e78 Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 13:45:16 +0300 Subject: [PATCH 08/27] fix: some fix in CI/CD --- .github/workflows/python-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 08751a1e36..842b103829 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -36,11 +36,11 @@ jobs: - name: Lint with pylint run: | - pylint --disable=R,C,W1203,W1514 app.py tests/ + pylint --disable=R,C,W1203,W1514 app_python/app.py app_python/tests/ - name: Run unit tests run: | - pytest tests/ -v --tb=short + pytest app_python/tests/ -v --tb=short cd: needs: ci From 9e441ca3a70406dbefd278477ebc7e30c7b3daaa Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 13:55:09 +0300 Subject: [PATCH 09/27] fix: CI/CD --- .github/workflows/python-ci.yml | 2 +- app_python/app.py | 4 ++-- app_python/docs/LAB03.md | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 842b103829..e0970763cb 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -36,7 +36,7 @@ jobs: - name: Lint with pylint run: | - pylint --disable=R,C,W1203,W1514 app_python/app.py app_python/tests/ + pylint --disable=R,C,W1203,W1514,W0621,W0611 app_python/app.py app_python/tests/ - name: Run unit tests run: | diff --git a/app_python/app.py b/app_python/app.py index 1ea5f40124..987f0df1f2 100644 --- a/app_python/app.py +++ b/app_python/app.py @@ -99,12 +99,12 @@ def health(): # error handlers @app.errorhandler(404) -def not_found(error): +def not_found(_error): return jsonify({"error": "Not Found", "message": "Endpoint does not exist"}), 404 @app.errorhandler(500) -def internal_error(error): +def internal_error(_error): logger.exception("Internal server error") return ( jsonify({"error": "Internal Server Error", "message": "An unexpected error occurred"}), diff --git a/app_python/docs/LAB03.md b/app_python/docs/LAB03.md index 9634243454..f86120b01f 100644 --- a/app_python/docs/LAB03.md +++ b/app_python/docs/LAB03.md @@ -17,4 +17,5 @@ ruff==0.6.9 - `python-ci.yml` triggered only in `lab3` and `master` branches. Because in the CI/CD I push docker image to the docker hub, but in docker hub must be only latest and working, in good practise master always has working and latest code. And docker image pushed to the docker hub only from the `master` branch. - As actions I choose basic actions what I always use then create CI/CD pipelines. - As tagging of images I use Calendar Versioning. Because this stragegy is usefull and if in developing process we notice what some part of app is down by this tag we can find there it will down. - \ No newline at end of file + +- **Link to passed CI/CD:** From 9c86b69c1734a89d8296da8724c0a246dba9d90a Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 13:56:10 +0300 Subject: [PATCH 10/27] fix: CI/CD --- .github/workflows/python-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index e0970763cb..ce64700e4b 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -36,7 +36,7 @@ jobs: - name: Lint with pylint run: | - pylint --disable=R,C,W1203,W1514,W0621,W0611 app_python/app.py app_python/tests/ + pylint --disable=R,C,W1203,W1514,W0621,W0611,E401 app_python/app.py app_python/tests/ - name: Run unit tests run: | From 82c375d0b0b67438bfceae1a568a7a22f584729d Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 13:57:01 +0300 Subject: [PATCH 11/27] fix: CI/CD --- .github/workflows/python-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index ce64700e4b..8bd49c2255 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -36,7 +36,7 @@ jobs: - name: Lint with pylint run: | - pylint --disable=R,C,W1203,W1514,W0621,W0611,E401 app_python/app.py app_python/tests/ + pylint --disable=R,C,W1203,W1514,W0621,W0611,E0401 app_python/app.py app_python/tests/ - name: Run unit tests run: | From 82985fb1477a30ce82c5718d7e878e2769807f38 Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 13:59:29 +0300 Subject: [PATCH 12/27] fix: CI/CD pytest --- .github/workflows/python-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 8bd49c2255..121b04f4c6 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -29,8 +29,8 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - if [ -f requirements.txt ]; then - pip install -r requirements.txt + if [ -f app_python/requirements.txt ]; then + pip install -r app_python/requirements.txt fi pip install pytest pylint @@ -40,7 +40,7 @@ jobs: - name: Run unit tests run: | - pytest app_python/tests/ -v --tb=short + pytest -v --tb=short cd: needs: ci From 96339bab8c3122afe3e0c46e89142dbcbc43885d Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 14:19:15 +0300 Subject: [PATCH 13/27] feat: Added best practice of CI/CD --- .github/workflows/python-ci.yml | 25 +++++++++++++++--- app_python/README.md | 5 ++++ app_python/docs/LAB03.md | 11 +++++++- .../docs/screenshots/image-from-github.png | Bin 0 -> 30806 bytes app_python/docs/screenshots/image.png | Bin 0 -> 11497 bytes .../docs/screenshots/pipeline-budget.png | Bin 0 -> 11497 bytes 6 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 app_python/docs/screenshots/image-from-github.png create mode 100644 app_python/docs/screenshots/image.png create mode 100644 app_python/docs/screenshots/pipeline-budget.png diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 121b04f4c6..fd2f615ed4 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -4,7 +4,7 @@ on: push: branches: [ "master", "lab3" ] pull_request: - branches: [ "msster", "lab3" ] + branches: [ "master", "lab3" ] env: REGISTRY: docker.io @@ -26,13 +26,21 @@ jobs: with: python-version: ${{ matrix.python-version }} + - name: Cache pip dependencies + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install dependencies run: | python -m pip install --upgrade pip if [ -f app_python/requirements.txt ]; then pip install -r app_python/requirements.txt fi - pip install pytest pylint + pip install pytest pylint snyk - name: Lint with pylint run: | @@ -40,14 +48,23 @@ jobs: - name: Run unit tests run: | + cd app_python pytest -v --tb=short + - name: Run Snyk to check for vulnerabilities + uses: snyk/actions/python@master + continue-on-error: true + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + args: --severity-threshold=high --file=app_python/requirements.txt + cd: needs: ci runs-on: ubuntu-latest if: > github.event_name == 'push' && - (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') + (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/lab3') steps: - name: Checkout code @@ -74,7 +91,7 @@ jobs: - name: Build and push Docker image uses: docker/build-push-action@v6 with: - context: . + context: app_python push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/app_python/README.md b/app_python/README.md index 93a8728746..cbc06807d7 100644 --- a/app_python/README.md +++ b/app_python/README.md @@ -1,5 +1,10 @@ # DevOps Course Info Service +## CI/CD pipeline badget + +![Python CI/CD Pipeline](https://github.com/setterwars/DevOps-Core-Course/actions/workflows/python-ci.yml/badge.svg) + + ## Overview DevOps Info Service - small Flask based service what return and report system metadata and information. diff --git a/app_python/docs/LAB03.md b/app_python/docs/LAB03.md index f86120b01f..76055b053a 100644 --- a/app_python/docs/LAB03.md +++ b/app_python/docs/LAB03.md @@ -18,4 +18,13 @@ ruff==0.6.9 - As actions I choose basic actions what I always use then create CI/CD pipelines. - As tagging of images I use Calendar Versioning. Because this stragegy is usefull and if in developing process we notice what some part of app is down by this tag we can find there it will down. -- **Link to passed CI/CD:** +- **Link to passed CI/CD:** https://github.com/setterwars/DevOps-Core-Course/actions/runs/21708886646 +![image-from-github](screenshots/image-from-github.png) + +As you can see CD job is skiped because it run only then we pushed something on main because in the docker hub we want see only the latest version of application. + +## Best practice CI/CD pipeline + +- Created Pipeline Budget in `app_python/README.md` +![pipeline-budget](screenshots/image.png) +- Added caching in python libs \ No newline at end of file diff --git a/app_python/docs/screenshots/image-from-github.png b/app_python/docs/screenshots/image-from-github.png new file mode 100644 index 0000000000000000000000000000000000000000..2da2135710b5587f6c3ea65c6c8c5e2871a61e5e GIT binary patch literal 30806 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 C$?2H@ literal 0 HcmV?d00001 diff --git a/app_python/docs/screenshots/image.png b/app_python/docs/screenshots/image.png new file mode 100644 index 0000000000000000000000000000000000000000..b4109d380f31760427edc2d715878be418de590a GIT binary patch literal 11497 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

-_<3ggo{ZOOjiiFav4~g z=&8xc$tlB!AMm@KFs`+}U4Qu_ohmm2H7&|#hg4XX6)bU^!_uz1IDTw9SD8E?bMv4% zI~CS``tzqo=Lc9-tUuGfZ?jMo!(n?i)rZ#`0v;QW_EGymR>iB7-NAhCi-NdCD#+NL z&nNXMX531rA{#blb1)zfNRps$AQdd3psS?&7u}B_176GZ!Z~J7dU?}kP02XnO;RUIh9v0zNAwqn5XSJ;Kp6Z-pMqM^^iZ1msy;_uYIT0&=#!e?whif61|V(UI`s zZAd4sIT43`PZ~PvVsN`C;+Kd2q%kZiGlXfQ+#oWNXfl4UB{@O&xHNdPnB%5f6kJmr$viKbuk00V_FusJe`||IgR?P%Zb_f3TB4$2%}AOYpCl*#~06O#hcT zYiT@LEw!KZoq$0omVAt!s}D_6rvu$9#-9Tp>^yUjPti)1YkZ0i71@c6F&pe6HOQ@pSDGas{yydsTfiyAr*w z!Ty%sPC|7``1%n*om$DM($^xzSaYgQIRK#WbQAujKyol6) zyPnnA@CRSeJ$W3xhfDoe5(ZHoI;R$wh6fXIQ3rY#*Gg)CbCS9hJa2e`$m(A~AfHoX z{LNR}Oxvi_z(12qI&I{VpM|)75j#^hGzr=JL*$6!XU%jEAK{BzvQ9e`#?Ov!UX>5%aj53}SQ5IPc<@%A5C)nw%#KXR@Tx#-5+Oa6s97fH+71onU#e>;uZ6 z)@{GLi}((=c?nZ2Dml5Jyvn3a!1;x-=w7If9aTF&mqHc-#e5US$T4%*Vosh zp2AYV!IMZgI>ObU6)9wB4@dYdA5EZ0@1K6dxbx%Ir6}jbqrF(C)^Adcw{C)1d}i^> zz8t-0n!VQ&sfO${hR+pBDCN{`y9Oo%a+^|nJmUrG$NhNrd12r3JSV4o&(NrLQIK4I z`#aUBl~q5nXjfw_;WiZ0H=|nL<^ol<{?#xgHz3O4L8z;Z^ zRh^#y-Ap%Wc=IyfDu7Msu7!EqRWL!zwt46ZwV~7I$lDwcIO_7qdnKLlxj)$MpsLQf v>oy=^^B4al=BRmp>=tY<{6Ej)-iVoArIwf@sGGp4V>Eeb6{*6v@4x&nO?C=N literal 0 HcmV?d00001 diff --git a/app_python/docs/screenshots/pipeline-budget.png b/app_python/docs/screenshots/pipeline-budget.png new file mode 100644 index 0000000000000000000000000000000000000000..b4109d380f31760427edc2d715878be418de590a GIT binary patch literal 11497 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 VcNc7ETS>n{$UL~0fA`^w{|i9CDhvPs literal 0 HcmV?d00001 From 3f450298ce95e83cf33aaa0cb2123bf45bf7858b Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Sat, 14 Feb 2026 20:23:25 +0300 Subject: [PATCH 26/27] Created 1 and 2 task and part of the bonus task --- .github/workflows/terraform-ci.yml | 81 ++ .gitignore | 17 +- docs/LAB04.md | 757 +++++++++++++++++++ docs/screenshots/yandex-cloud-pulumi-res.png | Bin 0 -> 25998 bytes docs/screenshots/yandex-cloud-result.png | Bin 0 -> 28777 bytes pulumi/.gitignore | 2 + pulumi/Pulumi.yaml | 11 + pulumi/__main__.py | 92 +++ pulumi/requirements.txt | 1 + terraform/.terraform.lock.hcl | 9 + terraform/main.tf | 57 ++ terraform/outputs.tf | 9 + terraform/terraform.tfvars.example | 5 + terraform/variables.tf | 48 ++ 14 files changed, 1088 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/terraform-ci.yml create mode 100644 docs/LAB04.md create mode 100644 docs/screenshots/yandex-cloud-pulumi-res.png create mode 100644 docs/screenshots/yandex-cloud-result.png create mode 100644 pulumi/.gitignore create mode 100644 pulumi/Pulumi.yaml create mode 100644 pulumi/__main__.py create mode 100644 pulumi/requirements.txt create mode 100644 terraform/.terraform.lock.hcl create mode 100644 terraform/main.tf create mode 100644 terraform/outputs.tf create mode 100644 terraform/terraform.tfvars.example create mode 100644 terraform/variables.tf diff --git a/.github/workflows/terraform-ci.yml b/.github/workflows/terraform-ci.yml new file mode 100644 index 0000000000..437b1a66cc --- /dev/null +++ b/.github/workflows/terraform-ci.yml @@ -0,0 +1,81 @@ +name: Terraform CI/CD Pipeline + +on: + push: + branches: ["master", "lab4"] + paths: + - 'terraform/**' + pull_request: + branches: ["master", "lab4"] + paths: + - 'terraform/**' + +jobs: + validate: + name: Validate Terraform + runs-on: ubuntu-latest + defaults: + run: + shell: bash + + env: + TF_IN_AUTOMATION: "true" + TF_INPUT: "false" + TF_PLUGIN_CACHE_DIR: ${{ github.workspace }}/.terraform.d/plugin-cache + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.6.6 + terraform_wrapper: false + + - name: Prepare Terraform plugin cache + run: | + mkdir -p "${TF_PLUGIN_CACHE_DIR}" + + - name: Cache Terraform plugins + uses: actions/cache@v4 + with: + path: .terraform.d/plugin-cache + key: ${{ runner.os }}-terraform-plugins-${{ hashFiles('terraform/.terraform.lock.hcl') }} + restore-keys: | + ${{ runner.os }}-terraform-plugins- + + - name: Terraform fmt (check) + working-directory: terraform + run: | + echo "::group::terraform fmt" + terraform fmt -check -recursive -diff + echo "::endgroup::" + + - name: Terraform init (no backend) + working-directory: terraform + run: | + echo "::group::terraform init" + terraform init -backend=false -no-color + echo "::endgroup::" + + - name: Terraform validate + working-directory: terraform + run: | + echo "::group::terraform validate" + terraform validate -no-color + echo "::endgroup::" + + - name: Set up TFLint + uses: terraform-linters/setup-tflint@v4 + with: + tflint_version: v0.53.0 + + - name: TFLint (lint) + working-directory: terraform + run: | + echo "::group::tflint" + tflint --version + # If you keep placeholder/unused variables for later labs, disable this rule. + tflint --disable-rule=terraform_unused_declarations + echo "::endgroup::" diff --git a/.gitignore b/.gitignore index b39220177a..c23b129b86 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,19 @@ test app_python/.venv app_python/.pytest_cache -app_python/__pycache__ \ No newline at end of file +app_python/__pycache__ + + +# Terraform files +*.tfstate +*.tfstate.* +*.tfvars +.terraform/ +terraform.tfvars + +# Sensitive files +secrets.auto.tfvars + +Pulumi.*.yaml +venv/ +__pycache__/ \ No newline at end of file diff --git a/docs/LAB04.md b/docs/LAB04.md new file mode 100644 index 0000000000..b4b74ef677 --- /dev/null +++ b/docs/LAB04.md @@ -0,0 +1,757 @@ +# Cloud provider seletion + +As cloud provider used Yandex Cloud service. Why this choose? Because Yandex Cloud has clearly guide for using terraform for Yandex Cloud VM. +Also it give grant for new billing account 4000 rub. + + +# Task 1 + +## Terraform setup in local machine + +All setups of the terraform done by guid from the Yandex Cloud Service + +Link: https://yandex.cloud/ru/docs/tutorials/infrastructure-management/terraform-quickstart + +## Terraform version what used. + +As terraform version choosen 3.14 because it is avalable from AUR package in Arch linux + +Installed using command: + +```bash +yay -S terraform +``` + +## Added terraform files to `.gitignore` + +```.gitignore +# Terraform files +*.tfstate +*.tfstate.* +*.tfvars +.terraform/ +terraform.tfvars + +# Sensitive files +secrets.auto.tfvars +``` + +## terraform.tfvars + +This file in `.gitignore` but in the `terraform` directory you can found `terraform.tfvars.example` file + +## `terraform plan` command output + +```bash +➜ terraform git:(lab4) ✗ terraform plan +data.yandex_vpc_network.default: Reading... +yandex_compute_disk.boot-disk-1: Refreshing state... [id=fv4975a78m65hdgfp9ck] +data.yandex_vpc_network.default: Read complete after 1s [id=enpgtmn84rsa6f087a0q] +yandex_vpc_subnet.subnet-1: Refreshing state... [id=fl8kq39okktku0aaglvb] + +Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + + create +-/+ destroy and then create replacement + +Terraform will perform the following actions: + + # yandex_compute_disk.boot-disk-1 must be replaced +-/+ resource "yandex_compute_disk" "boot-disk-1" { + ~ created_at = "2026-02-14T13:52:02Z" -> (known after apply) + ~ folder_id = "b1gv65t8e2aljrlbd9ek" -> (known after apply) + ~ id = "fv4975a78m65hdgfp9ck" -> (known after apply) + - labels = {} -> null + name = "boot-disk-1" + ~ product_ids = [ + - "f2erq6hp9j4r8leept6g", + ] -> (known after apply) + ~ status = "ready" -> (known after apply) + ~ zone = "ru-central1-d" -> "ru-central1-a" # forces replacement + # (6 unchanged attributes hidden) + + ~ disk_placement_policy (known after apply) + - disk_placement_policy { + # (1 unchanged attribute hidden) + } + + ~ hardware_generation (known after apply) + - hardware_generation { + - legacy_features { + - pci_topology = "PCI_TOPOLOGY_V1" -> null + } + } + } + + # yandex_compute_instance.vm-1 will be created + + resource "yandex_compute_instance" "vm-1" { + + created_at = (known after apply) + + folder_id = (known after apply) + + fqdn = (known after apply) + + gpu_cluster_id = (known after apply) + + hardware_generation = (known after apply) + + hostname = (known after apply) + + id = (known after apply) + + maintenance_grace_period = (known after apply) + + maintenance_policy = (known after apply) + + metadata = { + + "ssh-keys" = <<-EOT + ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAByPOk3aT5p1UzFU+KcISYZSVKofjNm0ZLC2XAqw7dX s.zaynulin@innopolis.university + EOT + } + + name = "terraform1" + + network_acceleration_type = "standard" + + platform_id = "standard-v1" + + status = (known after apply) + + zone = "ru-central1-a" + + + boot_disk { + + auto_delete = true + + device_name = (known after apply) + + disk_id = (known after apply) + + mode = (known after apply) + + + initialize_params (known after apply) + } + + + metadata_options (known after apply) + + + network_interface { + + index = (known after apply) + + ip_address = (known after apply) + + ipv4 = true + + ipv6 = (known after apply) + + ipv6_address = (known after apply) + + mac_address = (known after apply) + + nat = true + + nat_ip_address = (known after apply) + + nat_ip_version = (known after apply) + + subnet_id = (known after apply) + } + + + placement_policy (known after apply) + + + resources { + + core_fraction = 100 + + cores = 2 + + memory = 2 + } + + + scheduling_policy (known after apply) + } + + # yandex_vpc_subnet.subnet-1 must be replaced +-/+ resource "yandex_vpc_subnet" "subnet-1" { + ~ created_at = "2026-02-14T13:52:02Z" -> (known after apply) + ~ folder_id = "b1gv65t8e2aljrlbd9ek" -> (known after apply) + ~ id = "fl8kq39okktku0aaglvb" -> (known after apply) + ~ labels = {} -> (known after apply) + name = "subnet-terraform" + ~ v6_cidr_blocks = [] -> (known after apply) + ~ zone = "ru-central1-d" -> "ru-central1-a" # forces replacement + # (4 unchanged attributes hidden) + } + +Plan: 3 to add, 0 to change, 2 to destroy. + +Changes to Outputs: + + external_ip_address_vm_1 = (known after apply) + + internal_ip_address_vm_1 = (known after apply) +╷ +│ Warning: Cannot connect to YC tool initialization service. Network connectivity to the service is required for provider version control. +│ +│ +│ with provider["registry.terraform.io/yandex-cloud/yandex"], +│ on main.tf line 11, in provider "yandex": +│ 11: provider "yandex" { +│ +╵ + +─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + +Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. +``` + +## Terraform apply output + +```bash +➜ terraform git:(lab4) ✗ terraform apply +data.yandex_vpc_network.default: Reading... +yandex_compute_disk.boot-disk-1: Refreshing state... [id=fv4975a78m65hdgfp9ck] +data.yandex_vpc_network.default: Read complete after 1s [id=enpgtmn84rsa6f087a0q] +yandex_vpc_subnet.subnet-1: Refreshing state... [id=fl8kq39okktku0aaglvb] + +Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + + create +-/+ destroy and then create replacement + +Terraform will perform the following actions: + + # yandex_compute_disk.boot-disk-1 must be replaced +-/+ resource "yandex_compute_disk" "boot-disk-1" { + ~ created_at = "2026-02-14T13:52:02Z" -> (known after apply) + ~ folder_id = "b1gv65t8e2aljrlbd9ek" -> (known after apply) + ~ id = "fv4975a78m65hdgfp9ck" -> (known after apply) + - labels = {} -> null + name = "boot-disk-1" + ~ product_ids = [ + - "f2erq6hp9j4r8leept6g", + ] -> (known after apply) + ~ status = "ready" -> (known after apply) + ~ zone = "ru-central1-d" -> "ru-central1-a" # forces replacement + # (6 unchanged attributes hidden) + + ~ disk_placement_policy (known after apply) + - disk_placement_policy { + # (1 unchanged attribute hidden) + } + + ~ hardware_generation (known after apply) + - hardware_generation { + - legacy_features { + - pci_topology = "PCI_TOPOLOGY_V1" -> null + } + } + } + + # yandex_compute_instance.vm-1 will be created + + resource "yandex_compute_instance" "vm-1" { + + created_at = (known after apply) + + folder_id = (known after apply) + + fqdn = (known after apply) + + gpu_cluster_id = (known after apply) + + hardware_generation = (known after apply) + + hostname = (known after apply) + + id = (known after apply) + + maintenance_grace_period = (known after apply) + + maintenance_policy = (known after apply) + + metadata = { + + "ssh-keys" = <<-EOT + ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAByPOk3aT5p1UzFU+KcISYZSVKofjNm0ZLC2XAqw7dX s.zaynulin@innopolis.university + EOT + } + + name = "terraform1" + + network_acceleration_type = "standard" + + platform_id = "standard-v1" + + status = (known after apply) + + zone = "ru-central1-a" + + + boot_disk { + + auto_delete = true + + device_name = (known after apply) + + disk_id = (known after apply) + + mode = (known after apply) + + + initialize_params (known after apply) + } + + + metadata_options (known after apply) + + + network_interface { + + index = (known after apply) + + ip_address = (known after apply) + + ipv4 = true + + ipv6 = (known after apply) + + ipv6_address = (known after apply) + + mac_address = (known after apply) + + nat = true + + nat_ip_address = (known after apply) + + nat_ip_version = (known after apply) + + subnet_id = (known after apply) + } + + + placement_policy (known after apply) + + + resources { + + core_fraction = 100 + + cores = 2 + + memory = 2 + } + + + scheduling_policy (known after apply) + } + + # yandex_vpc_subnet.subnet-1 must be replaced +-/+ resource "yandex_vpc_subnet" "subnet-1" { + ~ created_at = "2026-02-14T13:52:02Z" -> (known after apply) + ~ folder_id = "b1gv65t8e2aljrlbd9ek" -> (known after apply) + ~ id = "fl8kq39okktku0aaglvb" -> (known after apply) + ~ labels = {} -> (known after apply) + name = "subnet-terraform" + ~ v6_cidr_blocks = [] -> (known after apply) + ~ zone = "ru-central1-d" -> "ru-central1-a" # forces replacement + # (4 unchanged attributes hidden) + } + +Plan: 3 to add, 0 to change, 2 to destroy. + +Changes to Outputs: + + external_ip_address_vm_1 = (known after apply) + + internal_ip_address_vm_1 = (known after apply) +╷ +│ Warning: Cannot connect to YC tool initialization service. Network connectivity to the service is required for provider version control. +│ +│ +│ with provider["registry.terraform.io/yandex-cloud/yandex"], +│ on main.tf line 11, in provider "yandex": +│ 11: provider "yandex" { +│ +╵ + +Do you want to perform these actions? + Terraform will perform the actions described above. + Only 'yes' will be accepted to approve. + + Enter a value: yes + +yandex_vpc_subnet.subnet-1: Destroying... [id=fl8kq39okktku0aaglvb] +yandex_compute_disk.boot-disk-1: Destroying... [id=fv4975a78m65hdgfp9ck] +yandex_vpc_subnet.subnet-1: Destruction complete after 1s +yandex_vpc_subnet.subnet-1: Creating... +yandex_vpc_subnet.subnet-1: Creation complete after 0s [id=e9b33qjag13gak3luifr] +yandex_compute_disk.boot-disk-1: Destruction complete after 6s +yandex_compute_disk.boot-disk-1: Creating... +yandex_compute_disk.boot-disk-1: Creation complete after 8s [id=fhm304muqn8491urb9n2] +yandex_compute_instance.vm-1: Creating... +yandex_compute_instance.vm-1: Still creating... [00m10s elapsed] +yandex_compute_instance.vm-1: Still creating... [00m20s elapsed] +yandex_compute_instance.vm-1: Still creating... [00m30s elapsed] +yandex_compute_instance.vm-1: Creation complete after 31s [id=fhm7dkjmnuqj3v57f8vi] +╷ +│ Warning: Cannot connect to YC tool initialization service. Network connectivity to the service is required for provider version control. +│ +│ +│ with provider["registry.terraform.io/yandex-cloud/yandex"], +│ on main.tf line 11, in provider "yandex": +│ 11: provider "yandex" { +│ +╵ + +Apply complete! Resources: 3 added, 0 changed, 2 destroyed. + +Outputs: + +external_ip_address_vm_1 = "51.250.8.110" +internal_ip_address_vm_1 = "192.168.10.7" +➜ terraform git:(lab4) ✗ +``` + +## Result in yandex cloud + +![yandex-cloud-result](screenshots/yandex-cloud-result.png) + +## ssh connection command + +In output of terraform apply added this to parametr to see internal and external IP addresses + +```bash +external_ip_address_vm_1 = "51.250.8.110" +internal_ip_address_vm_1 = "192.168.10.7" +``` + +In the procees of creating VM using we use public ssh key what created in the system. + +```tf + metadata = { + ssh-keys = "ubuntu:${file("~/.ssh/id_ed25519.pub")}" + } +``` + +So for connecting to the server we just need use this command + +```bash +ssh -i ~/.ssh/id_ed25519 ubuntu@51.250.8.110 +``` + +# Task 2 + +## `terraform destroy` output + +```bash +➜ terraform git:(lab4) ✗ terraform destroy +data.yandex_vpc_network.default: Reading... +yandex_compute_disk.boot-disk-1: Refreshing state... [id=fhm304muqn8491urb9n2] +data.yandex_vpc_network.default: Read complete after 1s [id=enpgtmn84rsa6f087a0q] +yandex_vpc_subnet.subnet-1: Refreshing state... [id=e9b33qjag13gak3luifr] +yandex_compute_instance.vm-1: Refreshing state... [id=fhm7dkjmnuqj3v57f8vi] + +Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + - destroy + +Terraform will perform the following actions: + + # yandex_compute_disk.boot-disk-1 will be destroyed + - resource "yandex_compute_disk" "boot-disk-1" { + - block_size = 4096 -> null + - created_at = "2026-02-14T13:56:12Z" -> null + - folder_id = "b1gv65t8e2aljrlbd9ek" -> null + - id = "fhm304muqn8491urb9n2" -> null + - image_id = "fd800c7s2p483i648ifv" -> null + - labels = {} -> null + - name = "boot-disk-1" -> null + - product_ids = [ + - "f2erq6hp9j4r8leept6g", + ] -> null + - size = 20 -> null + - status = "ready" -> null + - type = "network-hdd" -> null + - zone = "ru-central1-a" -> null + # (2 unchanged attributes hidden) + + - disk_placement_policy { + # (1 unchanged attribute hidden) + } + + - hardware_generation { + - legacy_features { + - pci_topology = "PCI_TOPOLOGY_V1" -> null + } + } + } + + # yandex_compute_instance.vm-1 will be destroyed + - resource "yandex_compute_instance" "vm-1" { + - created_at = "2026-02-14T13:56:20Z" -> null + - folder_id = "b1gv65t8e2aljrlbd9ek" -> null + - fqdn = "fhm7dkjmnuqj3v57f8vi.auto.internal" -> null + - hardware_generation = [ + - { + - generation2_features = [] + - legacy_features = [ + - { + - pci_topology = "PCI_TOPOLOGY_V1" + }, + ] + }, + ] -> null + - id = "fhm7dkjmnuqj3v57f8vi" -> null + - labels = {} -> null + - metadata = { + - "ssh-keys" = <<-EOT + ubuntu:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAByPOk3aT5p1UzFU+KcISYZSVKofjNm0ZLC2XAqw7dX s.zaynulin@innopolis.university + EOT + } -> null + - name = "terraform1" -> null + - network_acceleration_type = "standard" -> null + - platform_id = "standard-v1" -> null + - status = "running" -> null + - zone = "ru-central1-a" -> null + # (5 unchanged attributes hidden) + + - boot_disk { + - auto_delete = true -> null + - device_name = "fhm304muqn8491urb9n2" -> null + - disk_id = "fhm304muqn8491urb9n2" -> null + - mode = "READ_WRITE" -> null + + - initialize_params { + - block_size = 4096 -> null + - image_id = "fd800c7s2p483i648ifv" -> null + - name = "boot-disk-1" -> null + - size = 20 -> null + - type = "network-hdd" -> null + # (3 unchanged attributes hidden) + } + } + + - metadata_options { + - aws_v1_http_endpoint = 1 -> null + - aws_v1_http_token = 2 -> null + - gce_http_endpoint = 1 -> null + - gce_http_token = 1 -> null + } + + - network_interface { + - index = 0 -> null + - ip_address = "192.168.10.7" -> null + - ipv4 = true -> null + - ipv6 = false -> null + - mac_address = "d0:0d:76:d2:76:bf" -> null + - nat = true -> null + - nat_ip_address = "51.250.8.110" -> null + - nat_ip_version = "IPV4" -> null + - security_group_ids = [] -> null + - subnet_id = "e9b33qjag13gak3luifr" -> null + # (1 unchanged attribute hidden) + } + + - placement_policy { + - host_affinity_rules = [] -> null + - placement_group_partition = 0 -> null + # (1 unchanged attribute hidden) + } + + - resources { + - core_fraction = 100 -> null + - cores = 2 -> null + - gpus = 0 -> null + - memory = 2 -> null + } + + - scheduling_policy { + - preemptible = false -> null + } + } + + # yandex_vpc_subnet.subnet-1 will be destroyed + - resource "yandex_vpc_subnet" "subnet-1" { + - created_at = "2026-02-14T13:56:06Z" -> null + - folder_id = "b1gv65t8e2aljrlbd9ek" -> null + - id = "e9b33qjag13gak3luifr" -> null + - labels = {} -> null + - name = "subnet-terraform" -> null + - network_id = "enpgtmn84rsa6f087a0q" -> null + - v4_cidr_blocks = [ + - "192.168.10.0/24", + ] -> null + - v6_cidr_blocks = [] -> null + - zone = "ru-central1-a" -> null + # (2 unchanged attributes hidden) + } + +Plan: 0 to add, 0 to change, 3 to destroy. + +Changes to Outputs: + - external_ip_address_vm_1 = "51.250.8.110" -> null + - internal_ip_address_vm_1 = "192.168.10.7" -> null +╷ +│ Warning: Cannot connect to YC tool initialization service. Network connectivity to the service is required for provider version control. +│ +│ +│ with provider["registry.terraform.io/yandex-cloud/yandex"], +│ on main.tf line 11, in provider "yandex": +│ 11: provider "yandex" { +│ +╵ + +Do you really want to destroy all resources? + Terraform will destroy all your managed infrastructure, as shown above. + There is no undo. Only 'yes' will be accepted to confirm. + + Enter a value: yes + +yandex_compute_instance.vm-1: Destroying... [id=fhm7dkjmnuqj3v57f8vi] +yandex_compute_instance.vm-1: Still destroying... [id=fhm7dkjmnuqj3v57f8vi, 00m10s elapsed] +yandex_compute_instance.vm-1: Still destroying... [id=fhm7dkjmnuqj3v57f8vi, 00m20s elapsed] +yandex_compute_instance.vm-1: Still destroying... [id=fhm7dkjmnuqj3v57f8vi, 00m30s elapsed] +yandex_compute_instance.vm-1: Destruction complete after 33s +yandex_vpc_subnet.subnet-1: Destroying... [id=e9b33qjag13gak3luifr] +yandex_compute_disk.boot-disk-1: Destroying... [id=fhm304muqn8491urb9n2] +yandex_compute_disk.boot-disk-1: Destruction complete after 0s +yandex_vpc_subnet.subnet-1: Destruction complete after 6s +╷ +│ Warning: Cannot connect to YC tool initialization service. Network connectivity to the service is required for provider version control. +│ +│ +│ with provider["registry.terraform.io/yandex-cloud/yandex"], +│ on main.tf line 11, in provider "yandex": +│ 11: provider "yandex" { +│ +╵ + +Destroy complete! Resources: 3 destroyed. +``` + +## Language for pulumi + +For pulumi in tasks will choosen `python`. Because it more easy to use. + +## Pulumi project initialization + +To init pulumi project use this command: + +```bash + pulumi new python --name vm-infrastructure --description "yandex-cloud-vm" --stack dev +``` + +As libs version control system will be choosen `poetry` + +## Pulumi configuration setup + +For setup pulumi configs will be used this commands: + +```bash +poetry run pulumi config set yandex:token "ваш_oauth_токен_или_iam" +poetry run pulumi config set yandex:cloud-id "ваш_cloud_id" +poetry run pulumi config set yandex:folder-id "ваш_folder_id" +poetry run pulumi config set yandex:zone "ru-central1-a" +``` + +## `pulumi preview` command output + +```bash +(venv) ➜ pulumi git:(lab4) ✗ pulumi preview +Previewing update (dev) + +View in Browser (Ctrl+O): https://app.pulumi.com/setterwars-org/yc-infra/dev/previews/63ed8df9-f76b-42e8-920a-1f6f11045a43 + + Type Name Plan + + pulumi:pulumi:Stack yc-infra-dev create + + ├─ yandex:index:VpcNetwork main-network create + + ├─ yandex:index:VpcSubnet main-subnet create + + ├─ yandex:index:VpcSecurityGroup web-sg create + + ├─ yandex:index:VpcSecurityGroupRule http-rule create + + ├─ yandex:index:VpcSecurityGroupRule egress-rule create + + ├─ yandex:index:ComputeInstance web-server create + + └─ yandex:index:VpcSecurityGroupRule ssh-rule create + +Outputs: + instance_id: [unknown] + public_ip : [unknown] + +Resources: + + 8 to create + +(venv) ➜ pulumi git:(lab4) ✗ +``` + +## `pulumi up` command output + +```bash + (venv) ➜ pulumi git:(lab4) ✗ pulumi up +Previewing update (dev) + +View in Browser (Ctrl+O): https://app.pulumi.com/setterwars-org/yc-infra/dev/previews/79cbccc7-1978-40cc-bdb7-47712a2b489f + + Type Name Plan + + pulumi:pulumi:Stack yc-infra-dev create + + ├─ yandex:index:VpcNetwork main-network create + + ├─ yandex:index:VpcSubnet main-subnet create + + ├─ yandex:index:VpcSecurityGroup web-sg create + + ├─ yandex:index:VpcSecurityGroupRule ssh-rule create + + ├─ yandex:index:VpcSecurityGroupRule http-rule create + + ├─ yandex:index:ComputeInstance web-server create + + └─ yandex:index:VpcSecurityGroupRule egress-rule create + +Outputs: + instance_id: [unknown] + public_ip : [unknown] + +Resources: + + 8 to create + +Do you want to perform this update? yes +Updating (dev) + +View in Browser (Ctrl+O): https://app.pulumi.com/setterwars-org/yc-infra/dev/updates/1 + + Type Name Status + + pulumi:pulumi:Stack yc-infra-dev created (52s) + + ├─ yandex:index:VpcNetwork main-network created (5s) + + ├─ yandex:index:VpcSubnet main-subnet created (0.94s) + + ├─ yandex:index:VpcSecurityGroup web-sg created (3s) + + ├─ yandex:index:VpcSecurityGroupRule ssh-rule created (1s) + + ├─ yandex:index:VpcSecurityGroupRule http-rule created (1s) + + ├─ yandex:index:ComputeInstance web-server created (41s) + + └─ yandex:index:VpcSecurityGroupRule egress-rule created (2s) + +Outputs: + instance_id: "fhm3ectribap57rejbap" + public_ip : "51.250.95.39" + +Resources: + + 8 created + +Duration: 54s + +(venv) ➜ pulumi git:(lab4) ✗ +``` + +## Result from yandex cloud + +![yandex-clud](screenshots/yandex-cloud-pulumi-res.png) + +## Public IP +In `__main__.py` you can see this rows: + +```python +pulumi.export("public_ip", instance.network_interfaces[0].nat_ip_address) +pulumi.export("instance_id", instance.id) +``` + +This row after completing will return to you public IP and instance id for your needs. + +Output in solution: + +```bash +Outputs: + - instance_id: "fhm3ectribap57rejbap" + - public_ip : "51.250.95.39" +``` + +## Command for connection to VM + +```bash +ssh -i id_ed25519.pub ubuntu@51.250.95.39 +``` + +## `pulumi destroy` command output + +```bash +(venv) ➜ pulumi git:(lab4) ✗ pulumi destroy +Previewing destroy (dev) + +View in Browser (Ctrl+O): https://app.pulumi.com/setterwars-org/yc-infra/dev/previews/238d5ff4-0c1b-49dc-ac79-3a90bc8fd945 + + Type Name Plan + - pulumi:pulumi:Stack yc-infra-dev delete + - ├─ yandex:index:VpcSecurityGroupRule ssh-rule delete + - ├─ yandex:index:VpcSecurityGroupRule egress-rule delete + - ├─ yandex:index:VpcSecurityGroupRule http-rule delete + - ├─ yandex:index:VpcSubnet main-subnet delete + - ├─ yandex:index:VpcNetwork main-network delete + - ├─ yandex:index:ComputeInstance web-server delete + - └─ yandex:index:VpcSecurityGroup web-sg delete + +Outputs: + - instance_id: "fhm3ectribap57rejbap" + - public_ip : "51.250.95.39" + +Resources: + - 8 to delete + +Do you want to perform this destroy? yes +Destroying (dev) + +View in Browser (Ctrl+O): https://app.pulumi.com/setterwars-org/yc-infra/dev/updates/2 + + Type Name Status + - pulumi:pulumi:Stack yc-infra-dev deleted (0.25s) + - ├─ yandex:index:VpcSecurityGroupRule egress-rule deleted (2s) + - ├─ yandex:index:VpcSecurityGroupRule ssh-rule deleted (3s) + - ├─ yandex:index:VpcSecurityGroupRule http-rule deleted (4s) + - ├─ yandex:index:ComputeInstance web-server deleted (34s) + - ├─ yandex:index:VpcSecurityGroup web-sg deleted (0.91s) + - ├─ yandex:index:VpcSubnet main-subnet deleted (2s) + - └─ yandex:index:VpcNetwork main-network deleted (1s) + +Outputs: + - instance_id: "fhm3ectribap57rejbap" + - public_ip : "51.250.95.39" + +Resources: + - 8 deleted + +Duration: 41s + +The resources in the stack have been deleted, but the history and configuration associated with the stack are still maintained. +If you want to remove the stack completely, run `pulumi stack rm dev`. +(venv) ➜ pulumi git:(lab4) +``` + +## Terraform vs Pulumi + +For me terraform be more easier, because then I work with pulumi I have some problems. For example yandex cloud provider does not work clearly with python 3.14 so I need to down grade to python 3.11. In terraform I just install it using aur packages and it work from package. + +# Lab 5 preparation + +For lab 5 I will keep my VM, created from the terraform. But for now I stoped it because Deposit in yandex cloud not endless. + +# Bonus task + +## Created `terraform-ci.yml` + +In this task will be created ci/cd pipeline for terraform files. Created linters, fmt and validate for correctness of the +`main.tf` + +## Screenshot of the completed CI/CD pipeline + diff --git a/docs/screenshots/yandex-cloud-pulumi-res.png b/docs/screenshots/yandex-cloud-pulumi-res.png new file mode 100644 index 0000000000000000000000000000000000000000..0cbaca011d75d8322383c24c63d49faf8994ce4a GIT binary patch literal 25998 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

?MoGF=WttjbeEQBZr1Got)E$nWyWT!wbY%ktW& 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.0.0,<4.0.0 diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 0000000000..125205b318 --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -0,0 +1,9 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/yandex-cloud/yandex" { + version = "0.186.0" + hashes = [ + "h1:sR0wAL+16ZL2MK1+VcHb52hZ6J1W/sqiLx13+SoNFO4=", + ] +} diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000000..73465ff437 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,57 @@ +terraform { + required_providers { + yandex = { + source = "yandex-cloud/yandex" + version = ">= 0.80" + } + } + required_version = ">= 0.13" +} + +provider "yandex" { + cloud_id = var.yc_cloud_id + folder_id = var.yc_folder_id + token = var.yc_token +} + +data "yandex_vpc_network" "default" { + name = "default" +} + +resource "yandex_vpc_subnet" "subnet-1" { + name = "subnet-terraform" + zone = var.zone + network_id = data.yandex_vpc_network.default.id + v4_cidr_blocks = ["192.168.10.0/24"] +} + +resource "yandex_compute_disk" "boot-disk-1" { + name = "boot-disk-1" + type = "network-hdd" + zone = var.zone + size = 20 + image_id = "fd800c7s2p483i648ifv" +} + +resource "yandex_compute_instance" "vm-1" { + name = var.vm_name + zone = var.zone + + resources { + cores = 2 + memory = 2 + } + + boot_disk { + disk_id = yandex_compute_disk.boot-disk-1.id + } + + network_interface { + subnet_id = yandex_vpc_subnet.subnet-1.id + nat = true + } + + metadata = { + ssh-keys = "ubuntu:${trimspace(var.ssh_public_key)}" + } +} \ No newline at end of file diff --git a/terraform/outputs.tf b/terraform/outputs.tf new file mode 100644 index 0000000000..006d9af334 --- /dev/null +++ b/terraform/outputs.tf @@ -0,0 +1,9 @@ +output "internal_ip_address_vm_1" { + description = "Internal IP address of the first virtual machine" + value = yandex_compute_instance.vm-1.network_interface.0.ip_address +} + +output "external_ip_address_vm_1" { + description = "External (NAT) IP address of the first virtual machine" + value = yandex_compute_instance.vm-1.network_interface.0.nat_ip_address +} \ No newline at end of file diff --git a/terraform/terraform.tfvars.example b/terraform/terraform.tfvars.example new file mode 100644 index 0000000000..699ba9e857 --- /dev/null +++ b/terraform/terraform.tfvars.example @@ -0,0 +1,5 @@ +# RENAME TO terraform.tfvars AND FILL VALUES +yc_folder_id = "b1gvmob95yysaplct532" # Get from: yc config list +yc_cloud_id = "b1g8ad32v091og5d7e5l" # Get from: yc config list +my_ip_cidr = "95.123.45.67/32" # YOUR CURRENT IP (get via: curl ifconfig.me) +ssh_public_key = "ssh-rsa AAAAB3NzaC1yc2E... your_email@example.com" \ No newline at end of file diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000000..a46d15d5c3 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,48 @@ +variable "yc_token" { + description = "Yandex Cloud IAM token (get via: yc iam create-token)" + type = string + sensitive = true +} + +variable "yc_folder_id" { + description = "Yandex Cloud folder ID (required for free tier)" + type = string +} + +variable "yc_cloud_id" { + description = "Yandex Cloud ID" + type = string +} + +variable "region" { + description = "Region for resources" + type = string + default = "ru-central1" +} + +variable "zone" { + description = "Availability zone" + type = string + default = "ru-central1-a" +} + +variable "vm_name" { + description = "VM instance name" + type = string + default = "free-tier-vm" +} + +variable "my_ip_cidr" { + description = "Your public IP address in CIDR format (e.g., 95.123.45.67/32) for SSH access" + type = string + validation { + condition = can(cidrhost(var.my_ip_cidr, 0)) + error_message = "Must be a valid CIDR block (e.g., 95.123.45.67/32). Get your IP: curl ifconfig.me" + } +} + +variable "ssh_public_key" { + description = "SSH public key for VM access (content of ~/.ssh/id_rsa.pub)" + type = string + sensitive = true +} \ No newline at end of file From 2591fe623bda0b446e788afaa107cdde55ee5a08 Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Sat, 14 Feb 2026 21:15:57 +0300 Subject: [PATCH 27/27] fix --- terraform/outputs.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/outputs.tf b/terraform/outputs.tf index 006d9af334..f3cad81927 100644 --- a/terraform/outputs.tf +++ b/terraform/outputs.tf @@ -1,9 +1,9 @@ output "internal_ip_address_vm_1" { description = "Internal IP address of the first virtual machine" - value = yandex_compute_instance.vm-1.network_interface.0.ip_address + value = yandex_compute_instance.vm-1.network_interface[0].ip_address } output "external_ip_address_vm_1" { description = "External (NAT) IP address of the first virtual machine" - value = yandex_compute_instance.vm-1.network_interface.0.nat_ip_address + value = yandex_compute_instance.vm-1.network_interface[0].nat_ip_address } \ No newline at end of file

-_<3ggo{ZOOjiiFav4~g z=&8xc$tlB!AMm@KFs`+}U4Qu_ohmm2H7&|#hg4XX6)bU^!_uz1IDTw9SD8E?bMv4% zI~CS``tzqo=Lc9-tUuGfZ?jMo!(n?i)rZ#`0v;QW_EGymR>iB7-NAhCi-NdCD#+NL z&nNXMX531rA{#blb1)zfNRps$AQdd3psS?&7u}B_176GZ!Z~J7dU?}kP02XnO;RUIh9v0zNAwqn5XSJ;Kp6Z-pMqM^^iZ1msy;_uYIT0&=#!e?whif61|V(UI`s zZAd4sIT43`PZ~PvVsN`C;+Kd2q%kZiGlXfQ+#oWNXfl4UB{@O&xHNdPnB%5f6kJmr$viKbuk00V_FusJe`||IgR?P%Zb_f3TB4$2%}AOYpCl*#~06O#hcT zYiT@LEw!KZoq$0omVAt!s}D_6rvu$9#-9Tp>^yUjPti)1YkZ0i71@c6F&pe6HOQ@pSDGas{yydsTfiyAr*w z!Ty%sPC|7``1%n*om$DM($^xzSaYgQIRK#WbQAujKyol6) zyPnnA@CRSeJ$W3xhfDoe5(ZHoI;R$wh6fXIQ3rY#*Gg)CbCS9hJa2e`$m(A~AfHoX z{LNR}Oxvi_z(12qI&I{VpM|)75j#^hGzr=JL*$6!XU%jEAK{BzvQ9e`#?Ov!UX>5%aj53}SQ5IPc<@%A5C)nw%#KXR@Tx#-5+Oa6s97fH+71onU#e>;uZ6 z)@{GLi}((=c?nZ2Dml5Jyvn3a!1;x-=w7If9aTF&mqHc-#e5US$T4%*Vosh zp2AYV!IMZgI>ObU6)9wB4@dYdA5EZ0@1K6dxbx%Ir6}jbqrF(C)^Adcw{C)1d}i^> zz8t-0n!VQ&sfO${hR+pBDCN{`y9Oo%a+^|nJmUrG$NhNrd12r3JSV4o&(NrLQIK4I z`#aUBl~q5nXjfw_;WiZ0H=|nL<^ol<{?#xgHz3O4L8z;Z^ zRh^#y-Ap%Wc=IyfDu7Msu7!EqRWL!zwt46ZwV~7I$lDwcIO_7qdnKLlxj)$MpsLQf v>oy=^^B4al=BRmp>=tY<{6Ej)-iVoArIwf@sGGp4V>Eeb6{*6v@4x&nO?C=N literal 0 HcmV?d00001 From c7930ae8f244b7a8fc434f745841e84e4cef5c8f Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 14:22:10 +0300 Subject: [PATCH 14/27] fix: Some fix in CI/CD --- .github/workflows/python-ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index fd2f615ed4..00df8a678b 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -40,7 +40,7 @@ jobs: if [ -f app_python/requirements.txt ]; then pip install -r app_python/requirements.txt fi - pip install pytest pylint snyk + pip install pytest pylint - name: Lint with pylint run: | @@ -48,7 +48,6 @@ jobs: - name: Run unit tests run: | - cd app_python pytest -v --tb=short - name: Run Snyk to check for vulnerabilities From 4aa4cec415f20cda767deebc6089dc8b17d1cdcc Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 14:24:23 +0300 Subject: [PATCH 15/27] fix synk in CI/CD --- .github/workflows/python-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 00df8a678b..aec1405a8b 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -56,7 +56,7 @@ jobs: env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: - args: --severity-threshold=high --file=app_python/requirements.txt + args: --severity-threshold=high --file=app_python/requirements.txt --package-manager=pip --skip-unresolved cd: needs: ci From 7544deb925dac878b24f0ae0c8fc4084faf2c7e7 Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 14:32:47 +0300 Subject: [PATCH 16/27] snyk fix --- .github/workflows/python-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index aec1405a8b..c6ae5beb0b 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -56,7 +56,8 @@ jobs: env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: - args: --severity-threshold=high --file=app_python/requirements.txt --package-manager=pip --skip-unresolved + path: app_python + args: --severity-threshold=high cd: needs: ci From 3863ca192c8cbda7023ff3af20e16baede706f41 Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 14:42:42 +0300 Subject: [PATCH 17/27] fix: try to fix snyk --- .github/workflows/python-ci.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index c6ae5beb0b..a7b2e575ac 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -47,17 +47,24 @@ jobs: pylint --disable=R,C,W1203,W1514,W0621,W0611,E0401 app_python/app.py app_python/tests/ - name: Run unit tests + working-directory: app_python run: | pytest -v --tb=short - - name: Run Snyk to check for vulnerabilities - uses: snyk/actions/python@master - continue-on-error: true + - name: Setup Node.js for Snyk + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install Snyk CLI + run: npm install -g snyk + + - name: Run Snyk security scan + working-directory: app_python + run: snyk test --file=requirements.txt --severity-threshold=high env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - with: - path: app_python - args: --severity-threshold=high + continue-on-error: true cd: needs: ci @@ -91,7 +98,7 @@ jobs: - name: Build and push Docker image uses: docker/build-push-action@v6 with: - context: app_python + context: app_python push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file From adbba9d5b0971d1f48e8b34b40383cf98ccbe2c0 Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 14:51:23 +0300 Subject: [PATCH 18/27] fix ci/cd --- .github/workflows/python-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index a7b2e575ac..ca7dfdbfb6 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -8,7 +8,7 @@ on: env: REGISTRY: docker.io - IMAGE_NAME: ${{ github.repository }} + IMAGE_NAME: zsalavat/devops-info-service-python jobs: ci: From 27b32c6e4ea7bc8f08f04b5b2a34b2c50c86306e Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 15:01:25 +0300 Subject: [PATCH 19/27] fix: Notice what sync now paied instrument use free pip-audit --- .github/workflows/python-ci.yml | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index ca7dfdbfb6..c81a701d51 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -40,7 +40,7 @@ jobs: if [ -f app_python/requirements.txt ]; then pip install -r app_python/requirements.txt fi - pip install pytest pylint + pip install pytest pylint pip-audit - name: Lint with pylint run: | @@ -51,20 +51,9 @@ jobs: run: | pytest -v --tb=short - - name: Setup Node.js for Snyk - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Install Snyk CLI - run: npm install -g snyk - - - name: Run Snyk security scan + - name: Run security scan with pip-audit working-directory: app_python - run: snyk test --file=requirements.txt --severity-threshold=high - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - continue-on-error: true + run: pip-audit -r requirements.txt cd: needs: ci From e48146af1a0590ad37ac33d037baf5ceb4b410eb Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 15:03:30 +0300 Subject: [PATCH 20/27] fix: Changed flask version --- .../docs/screenshots/found-flask-error.png | Bin 0 -> 22267 bytes app_python/requirements.txt | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 app_python/docs/screenshots/found-flask-error.png diff --git a/app_python/docs/screenshots/found-flask-error.png b/app_python/docs/screenshots/found-flask-error.png new file mode 100644 index 0000000000000000000000000000000000000000..03fa36b74cd697a56ab78480221d73844d7a00b4 GIT binary patch literal 22267 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 literal 0 HcmV?d00001 diff --git a/app_python/requirements.txt b/app_python/requirements.txt index a3ca776bce..846beb997a 100644 --- a/app_python/requirements.txt +++ b/app_python/requirements.txt @@ -1,4 +1,4 @@ -flask==3.1.0 +flask==3.1.1 pytest==8.2.2 pytest-cov==5.0.0 ruff==0.6.9 \ No newline at end of file From 02f783793674c593caef09058e2dd11d1792548b Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 15:11:08 +0300 Subject: [PATCH 21/27] Completed lab without bonus tasl --- app_python/docs/LAB03.md | 12 +++++++++++- .../pipeline-with-best-practice.png | Bin 0 -> 117303 bytes 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 app_python/docs/screenshots/pipeline-with-best-practice.png diff --git a/app_python/docs/LAB03.md b/app_python/docs/LAB03.md index 76055b053a..f9bbf497b6 100644 --- a/app_python/docs/LAB03.md +++ b/app_python/docs/LAB03.md @@ -27,4 +27,14 @@ As you can see CD job is skiped because it run only then we pushed something on - Created Pipeline Budget in `app_python/README.md` ![pipeline-budget](screenshots/image.png) -- Added caching in python libs \ No newline at end of file +- Added caching in python libs +- Try to add Synk but in proccess notice what sync now need paiment sub so used free pip-audit and found one lib with vulnerability. flask 3.1.0 changed to flask 3.1.1 +- CalVer tagging in Docker Images +- auto pushing in docker hub +- non breacking scanning +- verified action +- CI/CD separation + +![flask](screenshots/found-flask-error.png) + +![pipeline-with-best-practice](screenshots/pipeline-with-best-practice.png) \ No newline at end of file diff --git a/app_python/docs/screenshots/pipeline-with-best-practice.png b/app_python/docs/screenshots/pipeline-with-best-practice.png new file mode 100644 index 0000000000000000000000000000000000000000..fb1824b62b3ae3cf9fceb4820cbfa3c4ebc9e7c0 GIT binary patch literal 117303 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 S7h01$~fr47+!Fl}% z%nLtb@W$LZ=e+K9kaN$pmESno%a8p(3B>-mpXI?60HVaJtrrerdwP=uqfv9zfU0PO z%p2Q3v9*;Pud&fhdM*P==F3VOIyJa)3bXB)#`^ljie^|WLY{*>De|p)zOju>UvV^J z>6o9LEIMci%JDQGaVwR9i|do4fkF00@}babmg%QO>xmk6^$D;`_uQ9nLr7TEAoGhp zJV_rud^p&@7EAX&a|BU3S?Vr1x5e&s2T_h`_ylniKY`R2B)sC4eqazr1VOz3s^t|e zfz<1_{!EG^LyRA}-|{dWd#zCpuXjg6{rZ}o2LdE((Ym)_IXc!?@HAg@xO~kDQinG1 zbKRacN~%IRKOo)g+UQu!n{Ukv~Yu@M=gh#^bjUg;ax zine@jf32tEba{PxhAl0M&9S?ur%IvDG4BeX+BS|HrSIHlYfY-Oa&T~1SleH+oGmzeDIFht z3%lG0?dt90V>yx+YpJji2G;vr`Gp43O=G&oC7L;!k;`m|zCD~$I)@`QS0(G<<`xBx zh_UD8Ph8IIB-a|_tk3tEqqwpC228GW0SXbUsyYE6=-r2~6Fja}-eq#Ey zu7DV-+y;2fqp{y&Z(b=u$c(!uj=h7cm=4&A7Tl3UI{YGbqpCkuyua|fF0bYEZExQ-lA2h~yJvb30-Tc8N z+RozS)?S#t(GNG)0LiopRU-0GEZMt9v^3lptT~~&+Kq44y0tdlB+m!~HnX`e%^ zwHniKmO4xf{3dpprE2@j_G+|EM}I#7 zpp>Skr?ud38OkmCR;MSN-p|P-J{p^vO4hCZmXOk1auf_lVHp}31tfbZzk7#(3{+e4 zx5WH!EdaE!BMxHFXiEtQA4WR^CdR9CRwy2&EU3KB)VO3gEVWy9qtDI=?FIa0V_Em5 zkh86AZKbY#k3h!wAt#54v+_*<@FPp@*=zz0-V@vgTO+s`gPrSxCa3d(&r<|^-;uKG z3}w3{cR1eNfQx_H^mH=FHaEsbNi7U+#&ak5mCm_%T@8vf*r%e0FQ@I-QF{yX*m&KZ|DJ3*Y#d+#h5! z`n*?Z1+&QD(yA)sql8lH!$Xsv)jmM*Mgr`9heYvAQgwZ00DE+q^f=i0KKQI#r}5C&udWfVhCkJ%D~jP$Lfl>&5ZGqiY(#}kZ)LqD000LM-&zr(# zs$wA{LaYhYwm=};?|2tPV7Dz+Z8x{tLLiOJ1p}& zesq518!c8pw>;4NG8DFlTkk2VV|T;Wa6VUp)(nWB-j* zr<60s8||pailI5S^))|cDLs)}Tj-`+PnzbH#LW(3n1KU|;x8ulK_APhnm5;-O@#Yh zP97hvM;J<1E+^)zcBL1WcK3hH<5q!^?9B6o7Z_u)Wm>8ZMZyFuF&FI?Ys8! zT>AQzMCqBMUz!d4{NDa5cm)^(zAXuti?z*p+Lrbj%TN)_LH=O``xp4w7m_`P3JqiWz zGZG)rD_-6pP!rfJr8f8^KDEHNwYBx;+0<=#cwD%lWvxd+*Kol-BD2uZ)mQ%}&ypEn zw&y=nh2Ou=Y@99qN7z^Ot4z7{JMV^!dZgV`lwa8(1KfgNFYmcy=S6Hvc}XlgmfEgpQ9R98h^?)nqgjx=(#!{ahkJSwqQ+r)r!YPSg0`$Zf zO0AR=UVM)*(rMVQ5xf;b#!k!28*{MIqgv$c;c7kid`(lx0kC;$W&~6soq*H85ukXc zH9ki-#A)!O$^Yu|Y^yj}iI{8okVdc09>broMOwr@%-nx`TniNYY$gpFtSxI+$~+sX z>BVRQ985aOEKpPcw7rs-Kih8uY|EML^bh$k4RMbH`o1LIg<6Lt0oUyZ0Qpjg`=)p_ zUMrTjDc^E%~2eJ^7{wSka|N^#{gNgrkS z9~1m25|pP_d?wR#dO1}O*oEItC__5pH$zMz>^eP7lcikoh6{fO&wCM>?Akk= zYjYIi(SXvTK#Wr>Vuot#f?4%tM{t{yF#U(`CeHnkcfwLkTz#6U>@%$_hZM|L1FdR%8}esTLz0>v}rNU;Gal&-?!=<6-zO?N1t^EaY$P!N-pxbpM_& zA)$xVe<^2Z4?YekwmY5pO)?~#nT&#>_X3b` zJHxjP2_vUD1O;6tH3k0?f<-d5Ku`?{{E!D}-rVD3WBfDAoX-ZoN$wvXD?BCA0>&1p z$C|io`p?5VBpjjTau`5Y_vAsPckLgy+8O;NC8l*}sv1t{o0=cbd9%ySsY!{0aC>Ft zcwp(b;VpYIcR^XLtbk67h>WZ!>kDqHgmh#~Ce7a`RO#K|nOQ(c{WBRfw5QVm9R50<|zIVFXe)!FHM&&^B&mJt}$jZtx z0tO!e6Kx42UvQ5(p%*;c#Y=<7{5~jf1;>LKOIQdf2=!F5rmu#}{BcBEN&Hl-nXjCRclJi#K;mOy8!-ChWnEn5lHlz z_)e#Nea+LZ9OO{sc1zE5m}L2eao%`>b z>sk%UU3$h+yE$89lciqDsK4$4Jk=JB#W4hISB2#iJ}_KJw^zKy#1MmCy=Pl*vBq92{}BGhU#?ukIAg^!Jj{8uKaj-8N)pwMyc* z3&@El16n21+C#%B3*rRh4n2O_blDoUOaS3yptl+@IT8W&bk1E1LDK=CXfLc^mF zfcOi!Y~H(fuW64nIB`?0*dW#4AJTAUP7fjRVIjJA?-_E|CxOcc%j;y@Xt+JB?#$c{ zfTiw5$&m3{H)>Gc&(Y}9$#$)>nd<(yK=**hlNUg5voS1HOOzWlx&0UmPGMm$nyEa;xkl zB%)$sB)W(g92^kWG7cvj!@+fm;Xo5Z9Di}PF?X&1J2#h*p!dV1YM!bUc_~I6m8V#~ z+LwDZ?X*1&4>66hoGf@K7su9I`Gs}rvI_8;?Lf>Z0Z==}k7##nB~H#8H}K0%H&hl) zmOKhIIcpjxMb&HVP(I1C^&vpm%d;$@b@afJe{7kXS`YNtrvZI>@{Elv(goWB4t{txd=66MUwXT>Da&jvJuR?0+2&$}?=y31-mDfbX$bot#ia=GY%kQVT9HbS z(;5O}pw`W9STJ9+7y-m-K=C82&W4FwGgN7%38)UNrZ!wMkNq|>%aQE6!8dQb8LD9R z_-&5v4speQ=a;~F<>VOO+7nmb%axg_X#i4Aj{dP+Iq+0%FE?T5gi+x<%%I*A_yAua zx6RgqiE@v_F#hss#6B_8M<5!X2NeMb5T)=?5`gm2KYmw@(oE8`q08%o8E#uS^j0@- zM%c5mFeLf_t{jq*dVuk|&K;*cjI!gvtY@OpPaN?2H=7U$F?BZd$<9*-Ka48dsqQQ} z8s4|#_cEGgmKYU4BwLLL!Y%hsr9D6zT3R-LCAba_49tMP?0OwYMtQwprIgxk&->NzAGmEN*kL?4YVOGP znQ)nJ!oa5xcqR$^EPvrbYj=ri>`fFNbH2DDYy(p8jVeMiatqH?XSY^IYwO-Q%&6$$ zU!Rn65SKgj*BJ7HMpgQKHtxgQEaS)fE-#~Ri645Qs!ILdVB zO5D(V-AC#_fr_e4H4);Qa);CzsC;fY>@!2pZ&BeaUj*Tvs(trQ4P&o1Q6c(~)f4x0 zgn3OJ!B%?XRPBI}C<{t`@NcZ`d<_2?Pt7 zoH;AGgLRUu*4EaELK~Wki%WYaMbD0A7$nMuU?V7)nAat$WXz8MQh#;G=tqgeYa=eh z9Kn5hs^kMUD9;)HY(*^i( z(kv(Nb7~gr(J(SH)^8?$Lp}UYpz^qXeSWLNuHMu8bm5;WC4MTMcCfa?8ksDK2>!_a`H$Ps{g6x{seffBRS~%-J~f zaLpT$BI*@4Rh=Q|#rW|OjWT}n;LSwdUG+S5CPtypl)_a@TT}j^h3Uo@u)9gTUY~}j z%z&)*{_nn((ZXc+7!HNhng$9%BAH=x&V@!VGGW)#!2%Cl7L9`5$}P=_5?l7Q{#4AS zQAD_=kV{*Jvt<$V|L>X@4lQtSwqL%C5MM|pY>^*67z2$yZE`WDYxv?i)jLfDOHwxn z>Llgr>RAn89RWNRilz3mB1F6NnQ!~SC%d$dvF3RANJsK3_)e%C+-ov81$~C-q4c*{ASu9dEE zMGjifWA%vn5xC4LdY;)Uk8!ps@LKJCI-~8 zCrVtwdJ@UQ=#-VKL915U_wTyfGv&dQQ`IX2`u#q2J}iWe`?TQHrR!9#jaN8`K1;GF z@q>v2xl{A(Y#Je*G@iMG50q-zx3pss=?TIIP_|UQn{lBs$ z_*A19hWClyJ)?yMUaQd*K+wPA#Nx5L@@Bjg!tw7+=05}Fl)A%Ruex7ln1uYcdm#1? zA}J3GXwHC$7*s9n9zL;$5EAkOMJ+xc^Shic3#Y1pUm*tQ_Xpv_$iT?Yox|;;h`6<_ zUn?kh`4BxVI~%)ur8H-i%|ve`9R?6C1)NYqSGN;mOw9||wq94bJNTl_MB?J&z<0Xj zofn^Yr#7_&Yr(B?TsF^#>ZO$!6;B^RfI1!Sy7NffS3C~%Ls*Wb)8XTX%b%WkeI{j9 zj7Y0LwVi(aE#sTzSOeXc-f?9*=H*S<;CNw&UF+9*PBWitB=>+{KUL!mJ2@_zZwYsn zs!tR0c;{lJvGk=QU{@5H@wRevgV9_>Hs1Vla)eE{<^eTK_IHzoiL_5kYHKU@>~NgT zWNC{XdAWr=gAx@s5z&<*2k6LnXBI+62-^D;^=pB8R8*r-FD*Mvg^KDvo7P7&Ia1Xz z{>xiWo&j^#D)na0*JR7_=+U^yaia!wFQVHFG`GEYne^1oNER~J-`)L)-_0#sKIvnK z97c^R5>cAr=disFC8(=P9M0L>&w&BmBYlwp{Km6njU8|(v*hBlK;xr)>IHdZB;kGL z#W=<@+=t>3*vFi}W(@qw2|B{B+TM9MJyvaxwiWf{KCAxp=#0_w78b=fU<<${%l7RkjIuMD!7}C zr?j}fuw)~2keRGuD%xWB$v8 zO>NCW9hVE#_UggP>sj|hcGVjXhQtC}unC6Hg$n?1Zc)|XB^uRgu{LF#I@@b2tc zx8sYAdYHuYkLPPeGob3hR-UayLqikr{`8c$pF83u71*cdn5H#$odp+zaHs-*^PoN1Fq&L9u(}X3zFGJcLr% zEo%~muA5URz8P^#1ys1zI&%6G5JwM(Rd>An2GF@xTVr-#-l9)I&3rOZ)Qb<)@c3+y z({SQBzfefB$XPXZyQ4UCF2<|Ev}+Ns=VNDMbhF|7e8A;K_zQN80s%lYjF$_q)Z1T+ z`2m%O$3hvsDudd}xHYLP%cNZj5B_6|AkgMqmj5#b$LpwVH|H4$! zWd0&18XB|A9Hq8yd5UXdAQzF4!cu8$4v9H=EN3`x)E!^k6d@kUz{2wR`}fD;S1zr; z5SKRysDGkuZwM5d7+m$w`UFd>+xSpiBjlqJ)nsnpiW*4%=jlJU-}z(Smw1+?Ls<6? z^Yy*fB3?;lq}L9nr1JCo%tP-7gx?bhy?^~eCV+Q{(Ps6M&gh@0_pgb6A`eCMcr0M$ zC?N|*9(Xyzpf(Cd4S-2DC|RDJ2vd+^Ei;DEg;VF$MJNS~F|xCVjpS?G_&v3EI#rpr z_2=7D)zzI=Nii`psWmQwp7bfhIZ7*50F|*45_VvW*LVC#lJYQAp{(LkTq zgUM$z{cL(;czPI)J43zQysh0xCZs!UTTSeH`UIk^%p^$Z_J|sGr?n%sA(N}0p21Nr zP!|WUKz2Mmur;-zVrgX(aNxX_qbdE@NE0GrV!-hm?rf^_T2ZxraM`*evfBFTc1OQa zFutQjhoWTo;CBb&6XaA++Dt9v_SG8oWF_+TGS`~$wG&Y%O0eDn#X+?(LzXf4vCo{f z47U25U8@#k@#Sks4?nJp6-|5B?#ssamX?+U+Vw=g7iX}@#|!9zIfZKBY=s1afU~L%3PUeWl_XOy~m6-^Apx-RB2=dChTXSNyF&8fmM&pn^Za9MwB5$F5UTb4 zEfG;pu@GcD%s0~~I2_N@^)^#DrOE8HAU)F>VqEar)7;eDm^+a};A+!BfxW$W>GotPe#5zDi(?n` z2${)|mD_BMdZS5>e_P$b!NH>ZP@*@^!TVEpJ$?3gZ+u6m+ql9KTH`b~@->!nbFdE& zBUA-lx7RVzKqlu19V_~kr$5GJRs9i~@uEpM7dM}D#2_2Cl}!!OU;dsq{e%$uc;#s_=>W^`BUr@HrVjizTrWrT_k6g6B)6B8e{ zh3{YVzv`?n&2x%J=V}xRahN;JYP3kA_m7Tx-O_RnZKa;*I?izp52+mPz+w|Wb!`lZ z6WB_CigIgDPlyAtxIlGbn@#wwy+&A|occKWQ@n*B2>zetuWp!{neCrhz#PZ)QQVHd z^54I=Hs25r;XIxCU#M@n;I|cdp%h(_v)XUa1ged-;e*o&o~YfhiS3_@Z6OJ~Mk=p@ z>1eYwn#<**RipMs7#+_x7Tir59u@NyJW9Sdr`#0ZoK5U}yL zl{#JB2=i`i;#X&y4A_Cq&lFwpL2gcgqwMB}y0O&P+c|GPPV?xg<%?)W1ssE_?RP#x zGcE)pd74Yr8{$sqN0v3B1NHuC2Z)gtpah(k;r8_Oh>S{lC(7hj6&qbnocK^M+y`U{ z-Nt8SSq0kgDBlP%#dtK*EheyTJk-Fy%B`ZslzT8^tR>urGvZOpbmNt2&#J=N3A>bA z&^bz|+hSsyucP+~r1(bEtkg@}q4e)-`19j~bQc20UT;T=`l4@Q6H~bgK)D;guVs86 z)0CCvHdyIdAHxY)m3C@eC~@Y?i!JRd2JTx2a;Wc>pG2AfhCgT(q?2y^d#6An)A)uCHAJ8K8e%d{mm`2v$%-2Y(aNWa=p4?IV@&0o31HdC|BK$TQ+Qv6Y` ztbB!bYiUux;CR1Tbn8Kzbg0f}tZTgB$A?1|b%$+oC zv+{}C!X+^DE^;-YbaQ(-v^<)}wKkPb8edpesTN*4{M}*rg~=>QO|*HR?)=hH&q7Hc zSw%c2vJXhsK8A%gx6Gt^9@{ZjaE7$~EDU>e^L4GF=*0z#6UYL}%F1Qr}bhE!o^(8?}*G!42-I78>zge7KOikH^u7s6lP+mtx_wVgXQ|o=XsMi2q?>Ejt zC@Ma{!_t{cYYcUklET99=;6M9Jgr`!h2s`6ZR+E#C-P8(bz)-T?3`uu?D6KY%l5i* z=mX0b)I|*HB-^}D2QaReo6vjkg_)tRG@xNdKrnIdXfM-zXOcqL@mA-xC)OSZG%XN~ zio8dy1T@IGTBT7LJ9B8#<60uE$|Ms~Gqqo@xH-9pm5Ml%`=rLxZ&ujE*UEnetMr^* zr^LkTHe@sukL66gof#>}hTLh%9oOnuGafeY;}mKKyil$tl2qi71zOQXzEzN!DPe0l zaIf>y+j^)%JKYEVBLPrci%nPcnc3Mx=Oj|RX#5dK=^OF4|{1IiMUHN|>O ztXo*dypJfmpq+(74xsk;=qbCqC1w7|=lo=HF%p*hR-wRr5WXK$FUfD(PgB@mIaxZm zWJSTCw$>XPDX(N}GPRfg6V)6K;WXx;3MbRm-Gx{HJuhF~kiJJ-*N@Qz+iQCsTjsWU zja1Zj(DsHaK4y@JOx0jQ=cYONZTQH=F9l*xc6Kv+F7?3=q%iP0V>e!(SOu8$B_ERQ z2)iz4MP}@u>;r};3E6n|xnP?uj!mbt>3RRa6(XFTk+H-?_x8%wQ}kg$eXZN@)`Hl3 z>lwXy>nZj8xI@E;G(3-8Ogt<)VK>_jQ$s@52o^P}^nuLPPWQJ>OeTyg2+yTE~5IS4CBIqR#SW zzzBkY6#N)Z=j9C5d;pbb0`FD*3z#tQqaQ@BZ{ z-@EuAM^F(FkuHNqK)S&MX#@djq@_EDP*kL)8&pbKx)~7Z?hfhh&j057JD%hH-T(i; z?pk-PyY8%WmIK4Q^S<+bV((`^&$E$g%R4(;`KkG3dFVKTU7q{z9Q0d{SAzOqheki% zB48@s>8qQbp1w|wU;Fi7ixi2~39N;j;S9cGMozqCJSO zq6GN29j{`br_!gk?61t-;I*e8DM#sdf9Y2DbzH$V;o>Ll%hYpzW_Dq}>eMB_EJ1M1 zH~J^I?YyKo#@R7{T?I=(P0gss&Qf28z_+J?C-FSE)mfwb#9N2a11olgW`mM8Hu(uI zz1}kd8$I2=Bc#o>2|=2TOTmv%zVZ_bC<$R`_`C{5aQkH zNcCztXVTx5hb!9a+q-4SwUteK${w9TszxpPQnxntb`ORs824pkIMbgz*~|;RZogss z++4tJnX4gZ&~UI6oBt74xXpw2B!|cM!!TK9;OipH??ufJtd^FspD(Y}*9UtQzy_dn z&*59ujXpWHrmm%b#UN$^tt$E2OUc*~z9LseiU~^E7SZO+u2b?_O+Vd>XVdeZnlsk8 zz~?f2)1>b&_^+!-s^qgEE&rT~=dC+>P;<;e@!2lvG^qMy*~RkhD`ZX5y;085ep;V# zGdS3*aAkFRKCp(GAU%Z1$>ra|;LUMP7PMGgE>GX@yzdJk;+$Me6z{oX^_q>vl1ZV& ziqp7n`qXT6JyrHiH)~Ey%m&u$)KmmdS@C+6%bt9#@bzdW&&Qb8K&rHP!~bQ|m5R!L zOP}6rQQ_{)*8nHGPk;H|o!XG#*w)Cz#Gq?aBqf&O5B^{TjQE-z-sq?ej0%#JwzCuS zynTn;J!aWNPCiJ2@9H0A!$7&z7m{VIjX9Py^p|~V@d@*=XlU{J@5nRX;b!3H9pu&3 z*VQga4!61gXGttc5i-(vy@F)M5>Sl%@vw>%e|p*QkK@DB6085tKneMuI4S>YzmcXU zcmEh76?)77&PTX;wQs8pi@#PbbMeE*u(6Ty(a)<=9QRKboe)9z=mv!uZ7%tAMM0BY zvLN|jFNu#eW<{FM4pZ)uFgd$Fg8zI^-zuQ*5js z=cu^25kqQfDmB@cXM?&ncJ=cn)mk4n4;H-qvSl{tno?rReWF+kt919qWv}Y_mK~YX3qH?p25uW*VV` zc^5O|&Y0KTt{_2rhkIA#Qw;Kh0^{2Q=hb+JV%1BRu2zkXRwULC(BnylSvh)*cfqyS z>C!88y;4)MAGIay29l~Nzr6*WK#X2<_*Q2KJ%>@xx5UT6OYEZzf)20<)cP$^ zhqI)2?%X{%95&BdDwY$NX$qZcA9TaTC2r(jE0^#3&8|@60y4#P+QvreGb5EDPD)x% zCTz-h?|x)VZUfxOl3Yp!^y8O#Mi;IhTmR-lJet0p;nt&z;eiNGdL; zNIYxcA}+j{C$n>5_^Pyinyq7Ka!t7961&^vaK1tH5pgUl-#8h!<6jpqZnWBucYo0l z6%Z8QC3buZpm0Z#rZCV2SQgf&`M$rrcYNQJ#3TNo-IlUUr(W7-sc)q;fU#CS)5&(W zEnX(QFH@ZF=6{WtK*nTXdZ=;g`@C;yX)zX(hKx+#XC^8@p5Wk65ur+ur{6X)-56Yc zc6wy9*mK#yz(A4)57h2oRSJwUl=CnkIH=YBHTPOr`@6v$g>$GMsc_uoV4B%s#Vj@6 z9|07#HQifj0km>KzHRSO=H#ba)P+)Jexv$jMpFkxZUi3HKjX|PX^^YwFU;7l2e{LO zlc_3Ox%Fp7v@)BFZp9!z)C- z)H!CeeyyXU9@u!hBIvaJbFgGPq#?A+ybE(J&sO#0l;zWLZ^^S6!rj&J z`a^6|5OXyW`OTe9rz)g#8TVz)R9H@3S#CGv$kkow!V=FH;@73in``+c>%bH5dbmJc z?^t5Ia>z0N=%hZm%VJ&9NLb*aLD4b0LUX^F=+qat?&}Dj$}LzpTM7qbtypDF;QkM z-KMsQz$cwiAzeZ5$Nq-o!F=p-jym#JFjmAPaD9iLuDEeJ zjRVU^`PZ$`$@GaOpXhYPlcOEI(IHOi-{jl|e0d-mPSgDQwd$`)t-08n?m23f0%|Sr z1DXsAcPHX7ybeA)DL~l}INhHDrS5f)YImpszN-1~Vtc1L!TqoGyvHAMEjqQYz4y#B zYBd6m@q}TzY7w(0atD9dZuORKy&a8Y)I1$^g<<0j;LA7S$p}CZiwe~iQSFWvusOCr zk~ji!xYtMD#GRE)wBjOb1J_tq4-e04Id-FbaSoGW^*l1zWDR|$w|ml+&TVhJ66yok z*B4UG-_rjWhj&-C5XlQt*}oJsuS|#iyoM$Gqin4LvkDH+=2m@Smy)Ln-dxUYMkXy? z;+1nwhQTEk1E?eNxYP6e&Jb!KfkB)3KH>-|wGa7bptnmdqPEzT8vEedlttw{_B!deA4dv$Hc3ZI03#KRq!S zF7ap=I*al3^}W!Z!eQtvsyb9?8f`x8Q{xFP0t-lUgE(S=h+%V7tn0}k|L)nVAsi|y z8iJv@b=am@Ps_g65aooNUZUIT}yFi45C~yJ&QpWrC}3T6wICzyGXjwUlrD zAVrFBaO${KSANJ47pDQJTXR#INmr)o0_GMTdbp4Lwdr=YCp?sI`gp!gmf*MoK}l(@Qdo`+i62mb92RE6 zQ`O73un6zz0))#^UVz4#)?a+=^WsD7$B&k=*r;SvDTkCeJMiQmE-+Y$Du|HeuE%EmuUJd2Fcb>u zwY{Mm!8EW1(IQIs=4*%7{8=neojRIxg^HOhLk3fIQbfY9*&r7vTio#e7@|m^gcZA1 zLRvNo1=VW{3k%yT!{I>PwmMoV!XqO3n4Qh?A0MEpy6weYESP1mzUt>$YR)<56wk8Y z4d?1fK&b=!$B%2QRzEKZolMKOO}D05LsDCoI*!*<&O7dQM4qRA+V;4DVsfNJyJx&lKomu4id057Z z*>-9P%It0iKR@Dv_9*y`jBx9xFORokx8^jF54)3F_g(t#ab;+>fBMqinIfZk8Ydi2 zsEG^|K0Eek@%h24$j3B9yubNI<$uJU)M;~h;~$} zJI}VpHa7;-lCP|;Qj83ju(zMi2@4#rKL%ycxm6kO(9s@cmD>k_X2N)vx>5Uash=BD z$6VGlr>Ca~4UVE%WZA%R64-3G3+4p;<2+}MyUHS!+c(mcGVd^Pa*neG2%r8X;o)(K@VpuVu3>b2D8&aB~E?M3YiD{euKG*O9<)5U~sFYPfD@eN%S z!F!m%{XDN<_iMk)W09^#Hylg(1D85U*W+K90B*CC@84+-!`!l18!IkWbLxdksFNJZ z)kRxdXWnY9!lrVLg3orosv(etFuelUVZgYAQn^f5rm>R9RmwK3c43?<;OC)zQ$;bu zkR{`_dS`%vJ((0PV&S~hx7`~3%X!z{dLhWoVIc|kDsofI%(UZEfY9-}ceaAE?cw%< z_R1W@BXFGd*p=b#qd-qI*o}10D$yZ&>nv0-K2^^B%UvZ{|H zJ{SkVGsDqRPMDD(@_Sy1bj;1oZD?%#0^(?pZF>M*0i;TDJrPia{VacfHoer{+X^~* z)j~G&v2vuT{kSy_BoniNo|M|%byAM35e`-p-M)@`91;X9b!J7DY>QOz%#O_dc;6DC zn4z{1al(Pzx9IBXn%`QldCAVUzmW2}Cqv!m^aOLg-_ffSrUbjDayy8&VOn2A#Fp9o zg7pdzoF*9b6J_S8FH*x?y1Kf#cBZRC@$#s%G0SD(YC5b}W4<0qw5;6n_7zxoh7ltu z6W!W&PU5U^-g_F4=`+~EnC(0H%z+0HOd~yAwuVZ1=z&qIV&Bo)iVv)~pBtCJ8zGcM zACWis-hSLQ8XfZ*;{H| zV$ZJxdU=PHg8l(~lBRcF!44~a+zv~v+-MGt({vzvs?AdWa^N3*FnqQ0beB{*o=>V( zh4Y(SZTv!a3i|pSr5mCnPbvFCJ<@Gccg%TB5E7q-fxg3CE-=T>ugIJhR-ys{Qnr-OF(cC}gjGTB~djp}p}#ulLx?yTB1<&bPa#uK$5 zI@-Ivtms5lX3ejTL`^p`I-}C;1E_|hB!5&Mqw2Y%i^iS6k<0-@fDhs3b~bY5k_^sQ zgMd|CeSQ40_+9%Ew5piFJaUvd0+LRjURO~K`UPqqLjD^r)z#H--`8<2RT*+vtyevS zDfvx4opU=;5^TrW}3pM_Qu_49R}HQ^YU2sHlM}82hX-e zqF2pIMf2Jo-o$rd+#L4R6qAua*Q)v8cOEW^<1^w9wacSp-he#dBkoffcP0ty zM|Leo&O*X6M_z+@2G+*|@7dq~rl@+S_p~6)hVqFEW5_ZV7NFpG92C&r2I%E$d0w0K zs#^Ot>lGyid|Y>6h~$B`xB0NhaSO6zlq7HTO&-<0V%U%O4-dg1Si;bdu0DVg7}ys; zHhuNO2xlzzhmo%D24~KdP}AS)Umh&yb|pi#Mu=s5Qdm*>MspftR8hLMou{l2hsTk# z-Sin;rmI#{^CRK@qE=!>b_K_NyB9XXIogcR zK<)|*Eqs^5?b%vFjS2W4#NcE36$zPBoN^JE&f8j}rMAbM&Rnlvy^=gTpanNd#B^5V zAmqD35!&XS3LO{XJt~-DIaC=3x zg{nPl3ZgWXXF_9RC0>1iZG!eo6fE~3_qu&+Vli`g0e9eLI|Xt2SuSe*G3933cNScIetAGK?9>99q@3^+F%z3MnC z{O;Ymj?WL#XSOu1B{=x`*FLHT1hGoXQLnO5&n%Al|;y~qZf;H&%Pt)d#0#JAQi=>Gdk28Zd0Bf&6tfkV3s&j zZULg1rJY@}bAZMOls8_#PCbWMn`Wv9Ot|VkI zqr9NtEE;oS94<1ay?_6DaO3n#T8Fu|jtd5%*v=bI^;`D+o@e{co{v-tx5>*Ci-X;r zLfSt+V)e25`uLPh?HvJ|O;d^H==ZjYho~SV1Rawzfp=dI=3Xo+Dv711l)4RM zKGy6?@nQbe&)|HDJg6l^z;YQcMp@o&jyh@D-&o6eb%OC$=^pCVwIx(vjh~m-Y&9mu zM5ibvl;PtOAoDt}#%`P@id~FvK;5_)sa!%(Ojhxm3vl_J8*O;o98$w^C^3=BNqF-< zOykL%dP;-Sx9@$O*VJK1dX^gpsl4RAW?peXBPCEEM9J~8i& ziar_h6>EqM#k{|8f$|(N{p0#tT+xvnmfBxfkB*MO z+fv#Q)k865T1$Os!I829p!P%fAMN<>w8ZrtJjH%`2maGo-+KLw! zzgIgh$vFMRarf>jl)F-i)JNzwQw00o;T`y|n>kR@2Yz|U&{HLs1A$!0RGGqS_mUXl z!1PlbRYimiTnh#YBpF6C|tH#Kll+*Ucl%>zzoR3-=SDwv^+r4 z&Q1<;6(ZRBXRJQ?`7h!e7GF1o@6oWb{>nW<7TaqY^=CDE<2T@U>PPcGiA*+rus~}0Cza#fGV?ps$ znV^N#yxhN0_YTmLdU{?!Q3aysb1*J3De2su?&V7&s0;ZMSDl+>zh2*bG>+4st=j&0 z$%$>dWX@q_r~f>KdF6?NJ~26stoCinGvVd+A8as)J?R=fC|?ove}~s|=VQ&QR}d27 zAH_U=^yrzm_+(|)-uWUjF`?y8E-3D>|LSmOEudEW)yfbjBHwhFz}SfoqSXuTph|Af zwyNB*^#MS%JCf6wM$>hNGho{_u>;aZlVnH>&XE-N;o%Xuh)IjF9dQ{M)U8)9DpYo` z4hJiYxwHK#9*->WKoGt3Oj@I*Q>Xq)tfTY(`YQ;E!6S6~$&u!fc`?Whdu0|6NNVrTI0HWD49WP~i z_p9c^FZpWBgwFZmz!pP}K%M3-1CuBb``&spOM z{j#pRUSyA3*-Dvwsw3Yh9xyjOl%3k?A`q&weV0oY1H&UQvBo%cY(>4?yu?TTHYzwa zEc&JI)zc8Z0OX@!!qeMZl2=G18O(ie zfPtgEH&(H)z+6t1qb8i9Edw=rf8;Q-ydqVxf)BFebJe{~dZ>HVgF7-Yx4(#6B&Bdj2zvr3iY5-@S*9yDG4j1K^ zwN|HF`QHgj=UGBC9vwHnOU)S?6Laqxg}_G;w}NR9)cYY%H$$Eq{HFMebGgrCqSTjhz47p7z|fL6FkvqozU2 zglDQeZ?MgWAzPyy`*ESu{fhP6s83}g`r!X{{-^c+6s@C$j#vOH-=e4YYK^vP_N5eb zx(VQ^eSVn-INPx97WC68WsN+|UV zC;_98gbaJjSk1+r@$q=rlIkOJvNuoc$I+JTCIV1>hH z=e;{3Ip`O|y#*POZ4p?7E2oRq$N+m+r~Z=ovuM@DJ;{A1e<`Pu_cb(vjLBGex!sQ zu(X4qs&2PaFU(Tm2R;N$FLQ0^kM`DRY56_D9m7&|xRB5^p52+N!b#{!+3w0q60#QW ziJ9WkGF(tBR-jq` zg5tvJy4=iGcYbU2rZ+?V|FQ^9=Hchv*ma((bTA!B9Mq$A7i>IARV@;Bu=3_`oeuW)%~mDN^NMG!cD2@hp1!?;;T1?@eoapUsfdb^ z5sQ|Vw$9gu+^06Q+wy>s9d{an9YWc zm^c!7)Y|Vqm4e*Hh0Y5T=OA6^yssoS|JG!vIM8{u#1pbTzQRoPvM?~l$uT=Au_^`xA}(vRlvQVT4OX8V zi|Px&;fVEi#rIhrH{@V@b^MpwR^OFtIKnNe<^&WijB2EGZD2v2dJk&98VV2w=c6R1W{XO|x7c1! zKGASKv5f=7>eim^F8I}Ga9EPxxbZ{PyrQ-tF#CGX*NY+vZvs*zf90K>WH(HWIq#95 z9*v*r46Oh9`Lpi3Vf7=SL&GM$c=RfE56mraEzCWuLh~mRY=RgxFeKzBrQ7L4Pk-`= zy}j`l>l?3z^5f?nH7;*&4Vn%T`L#g);Hfy8RhmbY(J)>nrQP-?<%*c0$68?(b};16aLcCU#E zXE5aq0Sxz6OuV|zVoSWkYYx7|sNa=))nm;@0sus1AP&UU!FLf0^cseHd#H`=!H?lp?h*@d$5aTEca?<05UO<( zRj26ZBt5C2ezD)$6c*~;IViWNk2Y+o z42}f9b^0NJWYtV3pv_IBR?`BqtYiu%FNv?m%$IikHiL@U6Y0 z`GciX4RatcNeADR|-vXyC?7SmCdzc*iEX0c+y9amobs5hAFVS%_N^kKn|-1a}s>M5_Xz% zZBUR%NvxskCb*~eBsf{hd&FSCrv?I+_MnE6aF#Za$A?kn6#Hi4y6u4yA_0tQ) zxjBh7pX9!&_8&>`{q^?;q-RM|xvS=v`z5zV16y4Y`o8^3K=^xjgnVQRM8vTl;mo$2 zCM4GU;jsY+p?_W%bnU^DrfX%b{?!&M{ka_A0c5&sEcBp#N-o|wJ(53vcK!u7gO9xHK)_pCmx!u>6TzC|&>kLo+ zw4{>Tt8;p3`@?aprSwP<_gs*((HB>lh_244c})HB3&)yq?YeJ$c~YXSiS% z6li>hi$3F8z1!q5m6xBTiq|`k~X1ZsAX75~=_&oH5G_ zwIOlLO1-@9i0`$QkT{S%+p zioe)S|B)K;E$EuqzsHzPR_x5aqMsM}xHFK4SAC0PNv~`+aJ1nv5pH16C+F->xWSE) zk1==UQd}R8sEq=qNzKH0F}l??kmBEiJefASPl8=s;w<~+5w%*2{x$LepT=olw8!)@ zN73oq$;nTjm?>hQYtc>59grj4=u%jUBZ95CgAD$z%N{Kwmk z&wOd{M1twfu_wABFP!^VCEjd0X}Pb(5qY0T>WOGP6(8FTQmzN=fBH{l;h>B7YO!fq z*;Q*bsMqC$%(|kT4-#>DbPNx{J(PQOVS1{WeE;haIrY! zf8LOpXMcSVZK|Zq>whq>DiOMRc9_E9{ofF}fRe^zJPn{`l_T>Ga}%x4ugJpUklS&;G`OVFAG$>Z~rqV3YBYx(Zvu zoe|O3V3R@H^-+~r!sw;=Wl_32R!oBud?23v{RoWq{gQ}YOvNpEVPHbH3T4iDO!4i) zhhxIQDFPV*vyH?b>Y*Pa?SsAv-}!F9D0b(L-n-E_;g$3v?%zi^pX4UrfMJg0e*9nWF>NS@$F+0oJ(aL$KcRx!PpB^2Gb_ z24ov{%XU@QLK&2j%Zpgd%(DJJhM1TnkCcfGhgN$tTKL$Lb?iVy*5>-3d} zGoBYdM%hk!*GWD5cz3zu)z`0Iu}0_miP;B=>~W^XNb7-iQj9WI0+yIJ8ak*dCF73K z0Gz=R90L#8b8FGQqM|P#?m8F(wE|F^c%Z=I*Ql!vH4a}QuB++2;_Vt0L`Or$Hj4v+ z`8_?1d@{K7epbOKSj~IBDTD)<_@ibw7)f(Ex>d2O`d_l|$B~=hD;K9R^KF^(Sp`Os z702=Q$jC^dt9IhXH;Q{??pmkMNYBKfg(KBMJug_Muev_fG0@lV%r>Wlg#C)iT4x?D zpk}&FApvOLrj@*a_|}51PO0_a?374~CK*r)X6Ckw6ysSg{HMx=VCGnXW#Zh|&F5m8ZLfbrI- zPEZ`8|3<>)!oUr!l6fIoH-71!c-#=ICgI?|xGj!fRyRh=K-ZNs*F*Xb7jBHSMsb5G zD#va`rM|E?T}dbBW${edA)*EJ2GB)n6=0H=bM$w(Ka8|rB^{e#yza8uBtG~DPrIQP z&t>y3ctxvdm@al2%Lk=eS${ct(lUGLjZXj;g4RIfx*+!r6y`~4SBlZ84mxuj zA`2i#L8l$@Y&v=!S-@5mOH3R*d01PYwtMTx-EYNK)7|5bbb^R)_*pG*PNhuL`hg7Y#}?u&#JYQC@g zLG)VP*yt6?7^W$w7J72x6(B67udi6SGLGEhJohNQ^_?M8WwfsRV5@?C$>X&>@&OV`>w#pN;CkEq1SBZQ@GXEUJa^4VLUlqB_(}mjIa%CnLcey;j zo$&gAqMvve;qIdI$SX?y$tK}5{vefQOVB8>&@?7 zVKEQCH3$>Kd$=EfdnEzLGsCQ@zmJZL=0vo{2)PX9Fi~?}RI)F6TY5c^G?ZN2boA1{ zDfvzj*`S;IXQVt zYev3}#mPxua{k@si0UHeJu|tW;Me_Gu{y)C_G8cw;@WT3ll`kg9qJn`odU#m4?rCH~Yh0-!*%>{PZZm$43WzYs)#Tr~sivcXM+)?=%Da z*QXSOE}K*{6)qN5O_^WSp;9tN%IyzM`Dhcslo(#CJO@^xta3r?oHfhAv;UR#;p$-} zI8(i98OS;3KAwYuP};ntQRo{oty}Mk&!{k|+mjR;{txy1{5uKEP3@s|XP42t&dE3# z6yS3x__n2+xOT|Cr+=PCLR#LY+jH!HMHkwE(`Az_+cGwB>BEh2rp(Q1e0=)yF zEZ`1B0e>=n5rAqOO{x&ETp}zAinUpzeO#_(SL~92x=m-b-0_^m7!vZL&X;1vu;}GI zXz?1}(b1v3NB{4#0MGQo<;5^%$ym2!vq$Gj`XJ zeXJo!FK#-tq;{uVx_tQ;RCG|{>M)FKicb2J^YUnqR+ zMD<^U*69EBuyCf^VRWZ%3^9;f=dZV2=)crxvy(y}y(w8*%%@f@FWnGgzZGA44BogT z$ng^^3!CzVeg>#!<1E%ENrVO}mxs3fJ{AJeRK$jhkO-o$Ph6)qz~?x4r_H4O!!L!|ZeFW&#>N5sU? zCHH?{{Ty=(ao%PAzaH*ZZxKYle}L1!d_n8qpXg}+554h$*VcHh!iHEQ37gHgBnf)C zdg<7kPKUl>r`vh8M)UnB)MRi_flI%cPw(%uqoX`OH}2)$ehnq0Ty9eHz6{y&3aEng z92K(7fTHyGW4}FvNgaXsyu3JAw#V5XEt#zPqS0?_Z?FHv19PEefjx}*_gHX0%V^0& z*k!CGss!{t_ppyjJt#8mJsvJJT9;<;b;|cATT;lkaYV>29+(fN%@;c;WIunb*&35N z@m>DPr7L};=9TnfY0vYLzfMi!FD; zL-6fj`Mm|F`IwsPX|#=yz@s;}zeH`V*k3fdCkZ<(hw(1i3ctmJz~`Mqxh|f7i_(k9 zouQ%U17yPVvb?w>2dLc8W-DlCCqwyQ2oswk1I~^7a7)J8Z?5KUPo;~q>1d98VEEUY zH%XFZgwE8!==acURoUT3^lvTz&Xp_J-S0m>%RIF8JE%ba`)mG86&i_Og<=ta;ASYHbaJ0C5)e!V$fF4k+@&(Y3pJlh|X z=;FLPUDoE@o!qF!u{c@#%+dL1{!QTBnTd(dIXcXk5XYGq^5Q8ezV6W2UAsB8=l9=7kV%pQCQAiB-Z&%cFy4_zR>^c|frJ7Z!dr6kec zy7eHgH?6U@wczDiFV47IHY<*w!kUna9tTNicsQ-t;&3*K>h(^V!fYjeY)n3KhGAxA zhW_^LhrI%VJL@%5;J$5qe%D^T`jVznkXCB5!Vq9Yv1rk+UYquNYPwG{;4!*mD`IdW ziy{RuobWHXZSAm=yfC$}<&5c)#7IxyJ7N(?bDBpw@Gur3LL8m#))XsBZF@@fBwY5Z zkAf2GL5_yC}%B6M|1aY;|P7CvR&?JM4t9~;mxeBW;t%Tc2uArE~U~^^nd(SLnXE*9m`-~ zFo)cV<#!ts7(bFvA&AJn-!8D1u}n^u@7&MKX*%+3v}`1SUzMXD0CClfy@mB(HGGzn z?--P_?t#cfEU44)1$3#1l%!$OD5ldq`#hn=X(Fp>&tdvi>NJ{4UE|I1#J51B;j~bv zqg`1@bHJSM9yfQ9_CFK3ko~S%^2u^S!ogpi~}mMX4g~Y zAh1fu9~>BX7Q|;)PBwO0=v3g?0;U`IxD?F%&dvqK{i|tPC)-m22eZbbr3HOwm7T|& zXW3f2Ywr6#?LrFBtc{BP?%l`mNwt*$dcr4EVkA6+$s4+kC3bXtLb`yn zR4N3HGt<+C&9lvD7P7LRp_??L7&M&|zDbTxEe6?_Vvh0e73?#QxC>%gw*Gl9hNfG@ zlA{}3xn22Xe6$A#pJIYcg!}^nlpZ~LOO>kcN7B^T*a^H0Jw3f72u2%6N7YT&N+rO~ zPX%SBo|6p`g_)4Qw?7*tsb9F;vBT+(z=WG;&SfvJAgX@1%ulV{nmtL#Ed?vf=is*3 zs%bywm_pS!T9HNt{jnuVpJUCo%j@eWI}XpeqGO7elI@-j*ZKs`PNC4}pq(qy1?t^Y zV$i>iTMh5`*}tdXLHq8U{kHv>Npdk9H@UKkt0BW-(|u8(us#B`@AJ=>^e4aIn$ z?ir9K<#tFosl-}g{48=B-?Ul`v0EEVc#h*=`Q-@V-c{E-6K^mvG%(OmTl+NVrsKv} zs@K2D9bve;92u=o903lK1i3?G{2Ns=VD$A1#78*r5@k-V#$}Ht$x(}S9c<0{2#y2K za(Z!D_ta=CBbADtmtL%EW)6G&8_+NhOb0$Ug)*i)2T&|749mkYR7!O%-&DGJmMO8R z*E|2-IIG;AFQRLeE!sWZ^?K`qw%YE>`vhuxXZyE;xpQV*yCfqK7Ey1Yrx#f>VdR!MU7_J@w-QFRbsW?KbVAtpvU);p zUW9%bkGC`ypt)C2BB7@1)2ytsu2Yj(>y_ZzGpO)=p~Ku z7Xu%Sija^kl^CVl{;NtM#xN#%5UKSf87p19cySTP>a*64j*fiJyS_-|qG5BN>6L(w z9gwJVyq^52-MmTwPt^4$1bLjZ%rFE8&lv z68|2*5{wWXZsl5URrtSjQmi(okXMvUzT+m8?Ze2@Fr~qwK&enRp%t9kaIn;yO>1my zY&x1-QpIa-ZvOP0=TcAVed}SbVA`xR8pP+DPHsaSwYBw)!^5I5y}Bbh6Ix!)jfb5U zzaU;%2uf6VFR*3@DEt*HECpD#3d}}TK4_Z^x+US!N*k_MpcmM;Q;b4EfrP{1dz)tQ zXVH4$j5XBRYMuG&mFnt;Bm>#mU~85-A7RL8G$0G~55D96rimXg9OU4Ki$Ys&hYO-& z;YFbuCE8EDdNBXK#0O#waEaaRiK&kyNVk@p@JJbTn=W50w|h~N%BzIqMx7KZ&9rNq zi{-4J(3m(fa*Sr9FQT6__m_LuJ%Qu5Djdd&x+tL5&&evG3AUN(@utDP z8tGR3!b<)9=W)-&6=Utg@0* z%^MB%l0XjS2Q$sl+1Yq5OFc^1?G3@bi0p`gh@~t{L{pHyJ%{{Mou4i6g8ZBfeSw8( zv=DSJS$u}U2=+Rv*PTBuIvpetGO6MI{xXydEUb3ucAAy3MLbB#hb8@Y7zoncjGpDb z9T&PSuaS!4MC=m=OFGJFXvC=$Eq#tvJz-M6{`=jD%aR%B10^1s}uiv?LA78)YEZOBK*ofZ$uD0 z*S|1$VoNWrT6a(4^ri63f+G%_wf6hevl{0E^e;~%ia|cG+bc&{kL@1|9?r+ zkD)HOVP;H(N=kkt?22OrF3=iY*>qbq`*RfnR$vxMS=k5f;8vc*ppZ7dohq-gJ9VSB zuI}5b1R5u;9*;wPja_`Xf`%UeHzG{;3B8R5S2HPrKLp_AV4&!_GzZLEwc zMJf|V@y$rJLLbVDn3$36z3Jn!s@zA$iy=j@=Ov(dtR4l}lLj@Ij z2FBUrirI4FR^Hc^&mN=E%A`TXe^%(oZmkPU7i|{0o~{g*JT@~6r4}=2kL%>lt7Q0o zxMxnvogr^x;tP$s^X=D|3ru>GJ6x5-eFK8Sm85#JRuVE=B%g|kMz+7lz+BwVNe~y8 z?D7Skw)l5EK(aMX1z|G&RdI6Ze(5kE&!NtE2$ve0EBlnOR&+ER5=WK?9%mvzYYh zAoGz*m$IE-J@&T8^Unph*Y7IzSU&z|5L>9;J-?9+so!OZu|*2X^FpZ_TQhSPd(A(i zADz)znG_glI9V(^5c|E)zEqj+LMs+tGY-u*mpa~@1SP&J=HWE#cm~C}Pt{tob7f@_ z^*y`0fuTDyt#m(X+8G(ymxM!4p-9pd+O!52SuIh1q;NOq`8=;ur=Z5&-0)Mh!jqHo=97|+}?3b&T z?Q}@6pcoQ5=^%}gI+?g@yD*gbGy6AmfPnQ|`+WVL%CsK~&CHCIqzRs!(#9tu`t0XQ z8WJ083Jr>(%R}lwG0}U?Vp-ks{e7PIqEF~pSk?$${JTU?*ge-*M0mb`Z?y5_pw!~m zTRyu|RTs>w`8Hw)+oTTJ<>hIF>1*+#*OpSKeax(${&S@WGj2WYi{Gh|k`w9fPb6MI zmrhF)w%-TG&KL*=g%;+$W-m45%esUoe+ zsCQFQ(k7qFd{?Qe5$|HwuNR$SHKP8Cekz={r^~#=nVWM{*d$yp;$6|F7iW{@zr0eZ zToz4ANm)ug6HI}2Z$=$1k2xTuJ+y1PNBy%t)jr;JGfQ3+Kg0AU2}uTib2vReqr~}w zu9!J&*fcG6(a5m>OTsG5Y5{??g^>g6q0&Lk6q(pkUULTb;kat8PtoboU`&2bT|Cvm z*qDW0hM1W653DPlfgNpqe1t(Mu&k^M_-1m;tYAjf^~(curs!2H(s}3kxGVYR%P)F!}k6DS}%G(%r6; z#~+OMvdFJox!RTF69`s#VSbsJg=Rxk{Fu%=i=#?lsMrhap)8e@JZoy~a{KRKvzz9J zI?FPOi)(sDYlx8D1QN)MdQqjx^0Ka}!b6dS>MrWg%gT|FO)X6?ejaRj`+Mix@czDs zH)UZTpJh@>bMy%dN(`p;HwIp0Ek!h}JTs_cDzVM&&92x?6~cM>>N*i{_V2DncQagD zRS%+yFSXUvvprxk8cZvI#4F#T8mFnLC8Sa7{2H43+N9^&VnV5&iaw>k$1xx<_x0jd zOMUFx>Q`{S&|pWjwY0pjvnv#+NLy;J@ys_CT~|1|Q02Fo+W$a7P#`ocF8Sj+R#Qtu zNTY79jdIfYZOM$yR2yYzPSFq?0vXS^TwcJDM zWput=&#B8{Wu2V@fS%Y-@!}zHNyUP`GCz$R*4EaZ{`FicXnJ}2o)|!i&IffRr6u(I z?CDiV7;#%HW>)@I{rJOqB+%7}SHH8A77Dvd@lVKyk}M$kQV=}7Rp3<-?;(y(s9TgH>_v=aqslcJjBi%yT`Got1ff#UIyg5WI ztfgB{C6jO=6HAY1miiSKeK-ja-jnj#Z{)h=F1wv2B)+5K<$Li0LY03*vn28AncSq2 z)lvSCkdVYT9;?ArFwMk*`0OiHcSt#2*pJ6?bC}yHhC#-f5|*iw`YNGrv9~Nct^eC= zA-C;zVLc9&RO6ymtBiR%k8>B5o*F%qL@I-Dy8xS@P>;fl@d_G@xsS4qEUuI_KFI@DrdbI8rHcW7&@XRMRJ`u}kE zo?%U<-P$nfC^Ht^&sY!B^<1e7W@Dx(hb!19yaApw2q747JcHVpOrpe8SwIq#ZMexJ%r;|(`#y!%iQ}fDphR8%`PNYZ=OE;)%Hn;zmiMO zyC;G%8`K{QrEUNmC+|Ley~$Cg#X4G0?TJ;|{|pA=&3639@+)#;2V^+p|LX1e`>};| zJ^FtnGA`s$7E17r?-~Af_Le_P#Tm9E?`Z+;T;4o?{+p~**LkZzh|#90IF3{#{zv;Z9^*Epx!YwH1zcP ziAjjb-IRPCp$(7mVu{^+A4`8mJeB*$-WoQLnE6_@I96s>8$fC6@2fVJTpX$tvl@PR z?x1sfLa$=*f=l8q}AU)U( z98*f)lsu(%)h;q!fzZ|ms=Kx_mRp@*pRb`2R3&5=D8a1Fm__X^C?k(1ms)QrC!hB> z=j7V@-NR!!i2Ngz)=l{@j$Isu!()go_R0YZONL+$6sH##mFVeude#NU^Ta1e6M5Oj zy11m6GO-V@5h+}|k_q-_ZG0`l5EraQ4jtJ-({rGKO@K#>q1#ZYD8altvkq%2>q2^= zF3>qpWP_@B7Li#)E)*06{3o7=XE&*a+!31~9as*z+_g;??0ghY>KthP3o(6vJTzVB zx4UIHzHYwMY-w%nE*!0vvHr@zryBGpbxcV&ks*)VqgGdlkfb}rEDjO+NFX&)Iny(! zFc>g{!Jn3vE~&4CxPC4V>o7NWD%DWi@|iuJ_W8t&>SmM8!;XGvlLG5Wfb|(xQHHI8Rg0M6Sxc~J0uo|wACQk5A>&&hY#^nG& zEb`me^sxj+rayfqukye6Egd)L6@bua0|@UcSDpY(C|<}K<>+6&`2M}TI3p&#?Ws2i zx{sC8{xPKdKh<-D%-fu8vOe47F?pr3s%kDLE5*>)sGMs7@fQM%`~ zntCmBJvxY}CDr^5d*q}4<@`73&AF^20s>e_`KmD2x-Z8b=j9cP$VG+u+sF%_I;CzY z>pa@?545an&ORM)Wc-WQAW<+smz5fsN?6|Ap@h0^1lapMHMN+S1`fYr|&Kb5niuEAt>_V=^KKs^KEj{RoL~UK~``wSJ1j;Xozm zHE4retW!bvLKj|Cj{fwX<#)fqBZE|I<-4lpTGO&$6MxK)5Z=q+W52t>iy?^c^70m# zS3>_dpq4uk1Tak=5b=)8X7LUC%_!mQ+mBOb2i1au=X>AjrTu}WbT)RE1>*T>G@8?V zx!X{sO_u8Bohre(!g9CXeE*W2HtWlXS5PK5|3dwig(p%bRUEGjH8iwPLJr&- zaeoigXfwN2p87&}_ki_EZaW(nF(f!mJ#kh+xf}1!?=uFcJj4aM*+yp>`~2rVw|9Hq z)_)^M^Dw-0spi*TC(4>JX^~OCid!80ep2?|*A3&%cetK46@^tF!ntAB&!7HmwLW{T zrOfrlzrSUD(e4ip-&;PtlRByeR>lQ}(=Di;GzT5X-z4B8c<67Xq@_z&{18II!lXL- zV^X4tm6)(_)pat~yw(&L_PtXTV=euvj$eLb5>BON2ai?>1t9)?cu(o*Cu|icWYM{z zfN!sFMW&O9cqjalh2o*3!g+i9a!vBe6@3@dckJRsMt=BkDdy#WdAtw(F^eIh=@UDI z=jdo}M%~%)M|NTJjM!H&4=R_}u4(HBnI}EtSIAc=sq6E=6-EkPeqg2RtF8T{pDi@S zEDm~j@810~4^}XGtAQK%2GN3}y-O0}mc@T!_?0qkPHE4U;8mldqDuT-fznax&fN1b zNim}UCh+*mq|etc7t5TwKaXS?FOO}`5$*Qw-`f?6%9HW@S`Wmhf8XM?as1CA7Ut%? zuhks*6g@9s7KTqoHAgQ8-G3A=moZh z@WY7PJy-dZJueV~rPaA5uaL3?FgLY9t$Nx-+hf)Yrcq3H1ZhMwPdp^gWM@%W_)Q>gM&()=#ZNueIV^OK;L7TbkOU z*=bijkfKA#x)1${Vwn!o8|x5)NLWz%8k(*)G_uI%6A(;JCE}b)>>;a>*75i;(S=a% zoR$z$Ody-UcR59s32-0AyL?nf>2Sh;Ns%i zJ+#PoB08|4falumO~QX%)v#Io^&KY; z9V!4Xx!F6I4pY8LDpeU47X%(Iz+ujI>Urbh99H`v4=Rx_c zTY|?JGo24&{R3EYa1^S;kk@(maBeBEDt9`$zk<$Xw@F?kp}Q zCYHQ+pZGe1fTkw%ACtY78nl|j2&P5}(l$0`!II1-R|soFy1Jg84VM?5gAzLL^){X| zkWOCb;!?zn*9bQE^<4oy$u7vh_GEcjQN97xw2N~)#dJ+mv#0c+>KA%#RpmQhXmH~-C}nk1a8586M2^5ZD(-hs2QbGLYnkg+ zCphGvh~m?MO?%lYwC5q&;qQgztDKSWPRM&$15ok`lA`k)D>f~g^Oe;sdj}6Jl=+GM zp=^Gm|6d%bDqdI8t~~vchpjE-Ne%h~spz}*Qn>b<9)h(IB`7M1Pxs|hoPE*AA0-I0 zX|_ZM(c6iLxE2|_wQ{P!vgAsnH)u(sH=9pY5mO{0C9ubX7hb5ZEd?5s*$vBqJ^R8! zuNB#Mq_}}5$!{0Z)WdrY=_uz+iy61Hw`)NfI$-4?5lbH3$-!~K$=bmI`?=jz1l*IN zqH}=U1l?`C$034%C$X@&IBhy;vBaOU_xx6tlvLN>OT||?TXy#05bojaJaZF#-PwyP zMMb8io+%SIZT2N$JloD!8WrYCA`Ep{L4% zb8&=Z^`^uRmg4%5C?UmD9Ol?7;fc{3L^CIEhs5=3S{mXMmc`yzQTAs>8f{?m2=gVUTm&G_N)km z{T3cNDXw$Hu!HBJ^XJc#pyg$(#IBItfO56If8Y6cdc|GMPJ%-sFC};tsS&q*@K4q< zO}5}??B_UYD-~~hHkKj%3Vs0(AheU6J^5$5wuc?`{n(-peZRWzKkI6<vZ)N zaqRnY6_Oai4lFeW`+c5kI2aEG47K#NYDY%%ym;OAm!{CdQ-jSM?|sHUf3-gwzh=Dd zq7Qi=S68>G+1d2Zex-(*-q9cK?K)#VjxngXcoKDssvBfpBXQ-*X)$RHOk*8ugiaYJ zgLB8;kES!uRlII8rW9uX13OAY&BAv;JzF9%nu*@r6^Fmpr=#_Fc~ zFN<1>Fa`vpz>_+{&7EfO`gO6Nr(yR@fxExIP=*uPsMs$%8$JyU3)2JVQPa4PR@*}N zl+BnX7ppaL?fvDKu8if0OnB(0-H zK-j>21v~zA_7#i1agwqyXmBu`FH5DGdU!}uUQ*}AjjZVB&plNtJk3BGBm?-QO3d|P zQ;oQL#kt4m{E4<*4NX6z$ZPted%{~su6?aKaJ&^LVK;66T-kGYC1oTOukqEl`_&CK zpQRp?B~;$&bLaGw{9OA1gcL#^BD{o>?7Sf6$qyf8{g$Y)B1hPl9P%rB5WSll;q*Vu zV{hI()_4)M#|X8Sn|+f4@lRph#>74dXeyiPV6x4==WIM&9gB;1FX6ls5Euv$)fE{1 z(@TQ*NMUcAUPz^1&Bj7@gfsib7Ae}tN=c;vXVyBJ9| z>~;x4^6v19l9CcG0oC;S`U?uixBIg74w8U`6Z-5~ALp`8YkvhRWXH8@XLwUDZa-f0 z17;U?ak!rj^aw=FEeDTIj&K^qSZL92QrJ||BmDm=oD)Es+Y*4ld|FWODj*)x6{Dk- zC9&9z%is@pZo*0fstUQYhUW<9dChpmO^UCB`_~0Y!?h3l@K$6+wMSf?(0Ijr4S&+K z+BX~s|F)`B;I>T>D=a}AtQU`9rLRm1Nazxvw`-|=yE7nGhS1+oAG8zK>i_!JP?cTO zvn~HW5pBtN@DAe}p-aOy3&tLw;#2u~R7vK>2aW#xBK3%+1jI5YACvqS)RAU`pb;`M zLC)TsBiju5d%W!d7nJgZ<7o*VhQTa`B z(~ZTEN{d6M>I&Ny@?HAw-X?`25KvtdvpE*Pi)e0XxdwStAF7H4IFzf#evo{a zn6_;1zO>s4L3OqajR=$XpqeMqqrH1k^%WE3uD(7aIBOErYUkLfM}VxzLuZf?5oF^Y z?6C>e*0~3G`{tIklMAblzLHUVi^0Kr%!e{ui>fzi z;m>7(cFnqL(x=mmFN@Nyz9H2Zux{Uf|69Mid#L$;SxyZA(NP@|m$29nefo_-H?n=p zzWkEfCCXAyPOfo9%4cPnD^Oj~dva1hmO4LDSps^A8FSr;uDAdCxQ1h?6g2&HmAOX7 z@18k9tKStstrgw-^UpnxyAk@#O$s23w`e0xF9IgRpRAI}-3tnYY?nBRg!3ytgTuq~ z^H_%j;vN4FKQW6L+~60Pf>h?qt>wvx==hkN<+?;M2V&@P7)A+a$Awf_jD0ibBA#Nd z)7O{#qp4#CsbjU2sVE)j0I#nRCn2tzR#8z=#~^7WE8o%=&}Ll~PawPoYh~noEfj29 zo=FLDHpW=ku`hp6F<{{(hzyFmxQMkU(aK!iTxC4Qc7is7^ICRiadCNHV$@iqAezzX zT~?V_FcXY02Dbfi`K8|n2aia;yrkgOfS^MMio*jpQIU`r0Qty6i=)+MkNQ zF8?)c?eYr^-zPdwikpMFh`68^C*j1ZL(_(;>Fbb$h=rU@EF>2geb^G+ha4edumT+& zJVlDr2sPmrqTWT3%Z3lzq!HMauY&sI{~=1}>9x_`yf?QCMnSo*%CR5gJh%-`;}t=_xW$XMX#r-2QUqiQruQ z-Cd>?OBFZKlDf}cMoUgH0+psdnEOk>>bkzZP8#W@3LiBtE|y}Mj9ZS-qa{4VSj+iT zdyHkkojchWT<}nhf7mvix(nYLi;R49K;Sg=9aEw8bPr8oXc3|yf+^$F_301d?v}mx z-@n?iYY&^u!N!C=3w;vHP1ng0k?9NhuG3R9*_O4Nux55=XmE?`eW#Jgr?+= z`}*oPx3SW5gg8Oa&%FQ(R5#mDQTh9Edl!rbiro^osBO~5Dg+2DnC0%t+NmmeRv5Y# z$eTX{U30XCv&)xPcVihFY4_AFdC!a}P!{`jFelEx3f0z*OlKvYQv-@+l~5Tt*IRto z1o^J&`v#TMe0oA#Z(NzKTe^+IpR}?Xc5c`g!U{8$btYZ9^Jm?E(FTepBh#T?C1Lo3 zcCwTJacYV*urisemlp8aDt z*V0{L`kE=k=5Ix&3uzopY*b#IJqZ>b1tig_#Yu4_(E4N|z_RIG3D`st|5QLdMdkM& zGrwsUZU5-fSXv^}fACc>8jO5=MMbTCI0>kw7=Bv!$kD@xufGz|jUDaI$jdd%dE?`< zZ|jCWv>%F2N=pChub+|nkkkVo!wf{nkgUKN6WZqb8SjVR0ip~&NJRu`2|InPDuWCi zX=&IX-o#|6%w|~j@fqgIsfJUZ<^gDysR%zVYub(2x~ikR{to2oUEEU@0?Ts^3*W#b z87nn=x$*hWWF{NSb4&H|w2Ox1 zmFsjM<%)v8Yh`9`p6}e7D~gngU&RnsSKYU$|H{zXTK4xg>_JudKC8QVBqh8cysO&) zBB7K-Qwt>Y{=E-wZ5zunEHE{0dX8{|TCiDj#K$hCa)kX-%~C#bIa(VMcVB?irkAc_5m`E)WZi4X1|S_(OY)|C9lklE|+}%)+GRYz&-lYrtxWIhAf8&MykVZ zD$v5^UWDLW&+zL|ORs{nYS$2fWaDQkKq!>W4Qy_2Zy)h(pQX)K zH|sWCBf=EnIq~Zs$lc`;ap~XwYQ-OP_jl0nGD~|D9-4GWWZzO%UNVTe8<2xHE{clo zX1(j}?lyn|cK|=LE89Cdv{54vvd;0$UDLh>#P9cCw~L8Fy0W2?W^1)XXRjNTN;bf& zqNf%Y4Q6S1gLQ6+!ZnOyP&0W2;(HJ~L%#-OYaHtjD*`43!OkkMY0Qhm5?GN!RCW0n zdRe8+ZTO04vnU(Ww-1t1``36J#$V3n^Qmi^*S^rm+-s0|5K~pX$lP3S_H*M++`G9# zw1X~Po$B3)p0x;QAd#G0m!M$4uG9enF?V+V*!znfIC6Qw+U)Pp40e%#x!`4w?^*() zO6%&8y$AMn&SUF@k?dL|RXW3~c5_wCv}$ct`eYVF(8uK*LB9I&RHD}wW4CjEU9zGt zDf==1@w}X6t=@R)ELmcr$CO2$(d%UkaE>UedDv`_)T{EIiwrg^xsnR`8J}7fM+@$$ z7f9A^()^el)&*=@hyDK^&HnXS4u)qp; zkrlWpT1)8z!0e8gh~S@;`CinOE17kSRWVSxaHJNF9%p`a1k`}kl&UW4I{Rhbvhry$ zUU1rMB;onQ}N?_{5J0 za0mKNW6muZMB0i3)h@Mf)zrXa23fFdXuTtHO5PaQ^M)Vy94PH1Sn>AJ=3?Tk`l^@f z(nu+mp`p*Zo{Mb-{!65FY3X*v!r}sDLgP@I8?hs~`yP!fh)h=W>jQ-(azTLnvHbzo zpRPi#>S`po_JtuKA!erX+pd^=Zre}w?PjGtId9JbU#)ep*K}9Q>ASk-FU@tKKs6e?!;*!Cg|%sJE9a>uJ!de1L###tQda@c zb|%>H8cYuH>+f`5|NeWG#}{h=x#ubJV_&)i;&L4#_-Dk`fJ2`3;M1S%_--%LrJ*6F zj42qpmJ^Ub%ybU&C19%Os9CkxqkiQ*WV}k2bQa!m z;oYqSob~HX>S7+Ki)Z_#VlK_Nk5-8aDVX^ARW_dBr~o4R7O(tFqBKCUA0Hp+T|FY^ zGx6z`R`9$yvPHfh2N#M)_!V;TDQ92YwTxm9#PPL*sG>J%nRW7IO;o=6#goBiNvEkf zf86gyI6a3UPr|=jZ;>)EO8M3Ia9#Lqq+&4R0Vv=iRaTqsXeDh_b#--@227u>Wo}bHdqtXlWTo5F;bh zi%8VYy&~2VPeVmJGT%7mP&Sp{tY?PznI+iub=zvkXqiT+MVXj5ndXR`K7W7bvsWTf zze!6Sd`w%Q9$cIKCI0aF$L&Nfi<#Qxz)=_G(xQ9nydt5b@AT0y+Iwp7bHU2RazsYH zZtPap8vbA%byPD^RRzJE!>Q$CHUX5x7$U14)W=!hiR6P--B z4u8ilOo4Z-7bmZzdl1yfiDvE{AoQOKKid!W`I?F_xtmIgl$KuMJIU%c7>64F$*=Z5Y_wVl!LIQU$xm!m`1vGf`&si2-ZCuCla|?;;h2k?HunQF% zO?-YMFQoCx1|0i)-a2=*)c8{uN>xfm?MLP5sU^kEOEc{!&|mLf9JGnx=XEtOD)*({ zpQyH{?MFL1QmCXrh1I4s`MhVgZa*`Ytk*`}nUexu^8&1mlzw5&mv1|yM}p~#^FLL_ zp~+P*BNqPC^2`oAW!AxP+ZEeoauhAJ~$*Fx-?;LCKo=l4T)aJZL_APhF)X-8r zuqwI`k=^W^)-53ht9<( z?eh8QVfpdq{{EY0K~@rZ=CNYf8pY<`d2<*eM6V&Itp4-;dHt4&sBLb38Mb42N6^SG z0!ZA+h!z7K+NtzZB+=lakQ%H%a^lR8%u@SNiAA5=IW z&MVgvj@$A{ka15^>ytag4bw?eef6{G>&|`wc#5X$$mRI*OfK>(*&iATo?2ZAoTyi=D$w)%y5Ldvmh0 zujU!Kb(IBD&zP0VNaEIl7JJus)H1rrCPtMq7S(>>_Pgl=tH>*YP?3TjwZQbFjO!%X ze(ykVFOB04j>*+T>UCLdlg9U)M=;{EKteNNp@OwT;LcO^JqM9_Mg_P3#03v};Z^b+ zzHtS7y}Jgn^kekK+fwJ=(-Hf5A|bD1#ribox9s&Us3DNG>RusXA?5iMO`EvPopIxG zgK)nI?jn<7X;$lu%FWG{x;gdN3D2*06~-SNl5!n*d&a8h5Bz?Nu(|-+n@e@$>(gTj z#h=HF1lTQjx}D-IoEas?cuTW%k5{FK^FrgjDG%ECHthAySqtxOqRx5k>F7vz5zf3` zmP6*0ABBlCx`xQW1k)1<_TN6LPn=kPk z|4`~Ys>mHQ<7HCpjSQv^CnCNbW6x0Ds8d98Dl6!7gg`0)!KaXx!L(`36d6%&^jvHF z+UC4Jdg7B{^MjSi?mD%{q>R`opopzIfp+WXaK7sIMbIg3Yb^^gO~tX${J|UJLCc{6 zQ^i<}O%&+_!ac|cYnhA=pcbCN<2sQcQ1aEp@EKWVwA)^B6)E>YrM{BQ?TFa`5V#8?@ysIoNlS!@4HE#pcX@IeVnq zND$Q=Iww|qb942~o!evf?LoulW)l&QNV(J!P1&ADAIxq5PhfxE=%w;|m&1-4E#C;* z&}*#q@imxX_8D^j=g5NA=Ct;9`RRN3AURNz?)dO9t$5hII_vtG8F_h@#|%EP;__!6 z5S!Lkprp~}tz4FhHl_@8X4<3?FEA`}YA=!hgp7zmmS%GVW;Kd2JLF;jg#fJ*IAwsD zTfe^NP0=$FIjk>oxH!L9%8J%vyh>GCYWn(^llYobV8k&T=vnPJ76ltbiPVtt8ynFK zK2^5mm~cn^%1M)2)XA6Orl z!lUGHEJj6b4LWaAYDI z8h`Q$PuYrO{Y#_Wxu|)@EJ?sUt%alf!Oc(<5`{u>DW`j+fPRqp5NIOv#Thr& z6kn$QG+?e0xLDSJBan_0DlQb;1QEa#E&RoWAiYzjx4mVTs_BY{5-Wai0b*-X-Rk8s zTP2i?1*~p23-P*PMt%BG0{t|hS1bc!hsEu-qQF(mPK8t+xdpv3wQsS4}Kf0BJdmwu%7bpK#~`1awEyin1@d&?~L=!Wfm zcqtUsuzidWzUOR4X!+ZgmJAS|)pLv6(enmitc>E@PVj#Yj(V=E>?4^CB$n8x`;(7u z9{>ssUdTJNXD_mmxq%(OgzVt3-j}Focbm;U%moUj#P00fAW^Oj)qR=T@#e!B@V&RY ze&yJu>+(!_HG2&)9X%v*a#x3nWZ?3sQu9!P_7cur>oI?TBe)e0v`EXV9%v;HK^}Y~ zyK^}8T$+_;)MqVV3p9f2_drU$?gZSzae4P7C>PH}P73o#Z^G>WQ8t9n(BF-l7d@{Ym#WUI@Ju)1sCD|T-9SGtzwDls>v2gKFuAA zis`o|g+-%(x%=7>o>vYy3viof$w0jeF%{z2W1R=m6k5*^<0t(+zr1>SoTBeLT4qq* zi1d`UcOByoZa7?_FI2y7Pl|5-@EhDRM>!AVRLxMZQ;GG~z5N1Pnvu36?}}s#<()*H_EKv}2_xqDPKm6iUZa~&dI{W*5PDIkfeMb!|mIG&RfDVXi zji8*5bhUSBw0%n7&IyT;RPfXctd zGSEgs1GK)%G5?Cu)wb@Bt9iWcO^n@SFBsgyNVc!RQY9v}C8?_P6TXGjd>ipnn9tWq zhfX^2GEVx6K1)f0gKib0#n$gXc#oo~g2?$kH+_-l-k&j1=>R{&I^kMKZ;tS4B(PG{ zxK!CnU;$S6o?^Saq+2s4JeyMnGzzv5|6%vO+t7n4tEMK{C(&L>wpC#3TeY(C#_JP4 zJ)P{kmvQep-^lM}&VTIuSp;n*Vilz7?N%llmh-+B%-oUOY0Xid3qkx$1+G#EBxJz9 zXVH>;hvsD0yU>#qs6}5GIAc8^m{=1rfWvNu!m53Io)hzC} z6lj5ls$G=^S%OIFQsC6!i<7WiHbw)o-|Aj%*U}^3my>q=jD1%+63Kpx_G1ix2z#dK z0hwd9fkeuaL(-6wF_b81d;a@<10$m*_iCRUcj>j9tYXji_iENA!g!S2UDt=^)lAB6 zql~a7$xEZutCufd2Dzp9o1OZd?9s^Ks=&UXsv}}XwqJG~oFXFVCXrM5C8~Ppg4=_| zq)EsgX63ssMr6WXcAak;WOsH!Bg3p4X^-MpKl8G~u+yhccY%vpvp$p;uju2qJlnv)YScpwCfAuwiQA^W6kRWqxhu=H-y05YOf)ORyi!wu7DxD7_mD&SZd@WfE5 zVl%Z4^%54vf=aT4oX?dIggLCdX@Ezkm8p~Z0!cOH#Xgl}-%(kt zj=4OCD$_=w6^5_Zd;zXC22t}zi32`>fG@Ib?pj#u)10nY>~lKr5;ir~Cj+vPL*!To|MvRwV0s=A@GJyL)#Z(lL$rvuRxdm+cy(d|HQmbccU zC0bNe!z6%0iV<`gdvlrn*dS#qgcR0Eh~CAeECLP2bRwVf%1ew^`)Sl}PR9dwrN}IE z98;cb3~#?j+gChaSe@yUm`tEbhx5wISycOIKu@JeL2&Tn;dR;%!NW{tQZ*tC4Gq%) zAdEkIBM2VzOF1PhpWooBok@-3EojcWQa8Un5JFgnmczR_bcZ189xdH*UK(KdmdFeZ z5HH_gObYn9uhoHX%UHx#5J6hOiA!-RQnOq1C_!L- z>eLpmYR1wj7S|l4|jtG{?xg zR1&*57PzRz;e7F2LvGnP@$Tcb_NWNen#+L*M)u??>7{YGW2#cyN)_|Z;cPQ85ZKaI?Gn8`n41T>ioD`v9CcrSj*SDr@B;5rsDKK*088k)61?x> zrIr>_2Bl-ME8jm%6Jv85)|bkL#+=qOJN_w-AQY}H4Fbrpj?#Ej3G2Fwy`MCsC&%lt$U=*AUREMhZ?dK6<`qm{&f_>@5 zcUFveZJH+<;D^Do?$7tX7`gds-`{h@q$Hr-D9^#nv?RdixC{Usa!UQaqQlW_zni@| zUmRZNGpNkJofiRrOSujEjW|}@x0^v2-}N(wP!4OcjLX=Y^45XOAMU;GKCs(KF=7>4 zTcyy^PZJsu%MTU4c{A=?F-87-I1B|({`#<;tHBRDlfC<4@dvqI%)=UP?zMXu-fu7< zM{(6?m>41sy@>Aj-SPcOE@%O^-SPVIx)#{(NI{4=o52k;N^1|BT9rpPEs|0d}rgz0W2L%8)Jm-#cl zv_C`s2#s6~33ZP%zzp%a8J{@{A#HYBf-IRC;WK-~smpt`2>N=)7FtxhbrxM?*TZdZ z1KCfoI_zjfZtU&#wtElvsSaF^>f_6E>35GuV1nB-iYn@_XI%N`6g~Oz!|t(*f#GFx z94T>64xMr>Q7l)tD+9T`?M=6v{czLNBedKJ@b~^>XRXDIKCS;VJ$y95`RxMN7Jd(Z zm%5MI|F_@w_&3}Rxvg}$BGUnve540wkF5R z7FYc5)5g|Fksiv3U{hnoO#$_|8Mg3?IiNRSgV-Nj^k=evuT|%_$s1MT+}px7WRwyY05>3Ni)O4ISCn=G82K{U)=zgxK-U5eE6|g2uP7&7`ihl9iq|b zw>@RoQEkO`G4U#1o5Jl3bOo3q`_%`xoO^QBa^yh8RS~8oIOyefQejMp8J)p1 z{5_UDr7;BIf2@@p80dB`mx@e(FW(UkpFjt2Znn4oPkACCin-TOF#v;gZ(p0PF&jv^ zjzWP+dv3eqRiS7;Gwb$Tm&|aEznk&jAwtHWcDq5Pw-aS~b^ebXXD=kLuIq%|oM}f( zFDfmV=KU>=x9mu`(chLxw~w4Kb0AsnJ;Sjl`~F^0+s{vPoJQW=@}29+C~w^!S|NO6 z63JD72Yik{XXCur=j;aBY#Uq9dMGFwodNtLz?L+t7AuVth~F7GoyHB!Hq zX@%(P85zYw)CoC(>P$VrC=ly0plOSEc*!I=JY;u0hmY3l=jksm?KLS2@Uirtt1Mmq za<1BM`Gs&eZ^oHEVk?4p#o#kJ}o^uO8ST6OsI68hu@`u<9DeeqD!JCikBWQ=@^u{5{{T zhea5TH!M;%=||x*ER02J=7}0b5-h#%wItE67U#Li;HoXQtW(tfzM1kCXYj(lZ{_>{ z7hI=pKP9AHFMo08_gmpK$X?tj7sheyRiW5Xm(O)l*4v*C$n9+&#=VpjM7~~n{2s1S zG4JKgbI5Z}!*!tB;~d3Cz;x{?!Qa0;6xxuR{t_}0XUN3%=7o6asKbPoka_q2VvnAv zzDGEyjlbIO9%w(;B?M9NZD`~i1s#~2tIu!mFXHdA>+4NYOk5T!uWzoXe=Pp|4Ob8$ zi@)^xW!3SWiC6aMavljo7G2k5A7d#=o;GY^Xca&k}GujiZi zdvzqv4Y}bDhj|oyHBkpcNJ_Z5Qec;=WDBP^skx)qLNa;#-8wpJn1J=Gx}$emEH2ZH z4)cC@VVdY$cpA@4suw*_z{HJ$wR$gR`|Gc`MDmPNpx+gE#&V*Wr+uWij<81x7!!7Q zNJ6CCP^XMQ(QAc^@ z`W8`%pdH!}zCMU!HPa>qJlesZY2SxO#3?Op1a|P9{SDbwYSR6bmul+H{J88g;`M3# zvBSu_{@a7{b(ZL*hs4xYd7QtA1pRYAc?!~h({bw4TlD4jI{HGCO@xvhG{yF~5)mv4 znW`kL)n?go=IG{Q0ufNtly23{&i%)|F_yvpfcJS{?NFsJc+dBVSFon>{#%s}s2qqG%o9b$bCkLo!M*wDgTx8wkl zC~Zy$+Zx%RAvM*hF6FT_vNq@CY2OZ7A+#|nE-oT=;&&)g3)%$iR~x}BJ+M9q z?0d%4DMT~ip{qa$P|PzbZ21?HO9yCA1mkcPF)FovG-p-wsK z6z)Z6=pO&s*N<-xJU-Ye3PgofyVlkAR$PoAcq$5vzdn+&m@TnMVa=xIQ>oD~`mqrhjp1BU1t@NRWIRa~nLGi%kxd)D{Gy;CI zn@48$J(Z%mJ-FW%s&Od@K3-QjqjQiq0qDV*n+$EG#b`)|iy?0lll|Dx6>n|UVVK~) zXH#iy583O4>PoYY{m8o&*+Qs$l*mf;HU0e5U)CW7uBp&81{zo~=5#7DbW-p44HK6v z{B->NYXLMaVbd^#km0|1d&vUi312s|44TUT(NaG`Of7Xw7u>AqFp16EkyDcnLCWj_ z&80Y7e|(4M{XGXDDT8PammiB3q@^wgDG|AjJ^vhjd?tM>%hqJN7}~(f>8MctTpz{| zNOX9z(Oaid6MzIIh+L{Bj_nV$cjQu-dhSDV87_2FU0;!w4rUBdv6sn5X{&LPEx!mT zyxy#gIM4W2I$G)Gj$^JHqENNV-?a-v5>y%EC%fJuCxbRVs`vctMIG?Mdym+~ktQ9~ zpN-6_WHkNyg+u8%P&(3~xDm!UvHurL)Kti>bsa!Hq4pvvCeH|zVsmqWCJ^ENmJnwZ ztiT4M1|3q9fueBkRI>NY<2_hb-F%X&o2k!6qM&5$+Rex*$hZ}%QrF&QMG3l@R+tdC z{P6G4aQG-e9c7APEJ{KnvMohL3F4J&kWh9Rq58tEV#Wn6Z9LN5#dfVen>b3}aPCI} zVCfSpr@AUU2qe2#bSgptxf+2PDTC?D9t8Em0YD%t25HczJmhQPWP`CgCqezb0}4pU zpQq&I=2G-+_nfWgVry<7BO$PL*cubH+%Yh3H;xM@Ie<8{y^2HTuH zQ<#|rAW;==)!*{Ycmrh&(7;-%GR}JZ&2g-8p?SPuyb7_M@O$l8I&te3P`eIL`mc|4AX zzNEqUdGbub0i;=}tKK9-+oRoi^f`dI>|9;DM=*If3BAs023E}=V>J$U6&I|IlDx}U zs+aXY^?XZ5z`J=WS2ocPGv8N0CL*vFRh#9*ThTny4)#FpsbnO^WjJ+bCiXOb1o9q_ zwEfhlLpp73@h%Qrgn)Sg`D)+oskreQ-~s?W0ri!gUt}H}KmZJDM%>K9*2M}ctPj|@ zoEZmdJGyXuZISGvBHC$Po*#M zzMRM6e)k)@<&c6|qEXE)1JG>Nz_+@)bKS&OCom(o*pT(o77C=7!`9Fz zls|z*ot7kSaqh8OUoc@CF|V~w^=W9R*aFr#QV_piASGD+#EW?>%kGi#>eY1u26qD8 zrf0^wEB_waq}%tBdKEUaPfFkXgq#DKu(smdC!iYGmu%983Cm4ck{jNTq)Ox2YWt>{ zD(5DK`i^lfx9Io)tA_y={z20*X#-Onw|oSLGI4*BDVRvM2u+Qw!)PeUp_jG zR-rl+h`5qQ{=g}+)y9WC^dto@BYlynB(E(o1P$^#1FyI5PRWWfakXGp~ngZyiKhN$URYMV2?=deA#7hN+E zESk<>7IjQv_N!XSO&Y*p+W>Tkcs}6BQmgSd*h#WWCXt8BH;!5O4pwe%L~2M3I?@5I zFWem1Z_P1qsmgCTcGp4W-&l22%W=u=5m)vBLiKMu4#0d>^jnIyd8QyNW}M&FEE{-=eOwOY!Vmo)U8x_~ z3djGJANBo|-}B4c|NqYuZR4$dY_#3E`n#NlwbCEpE`PMV<9p}F<<_(YbMnLexIPzp z_1$CL_PD+u_v=eW`LQvq4Dux^kldKrN^LO0cX21apwBl-%1EU(h(|~ zj$51kIQz{~Co}JbipEjsCVl(c@0<>+>0pgPz5q57+ZJW_`4Y*+U-S>mIHeN$E^29SBdHO2{2fggP|zevcBI8!T~fXU!9CkA>{uk+QuiZd}C- z7=OsUy71S(z#E(UQe;!*vmDFLZ9>7yaU;X2N03?u!ny-3^oW^ljSg@Q1D3m$CM|iZ z8#cGcoi!a=c?`TCr9@Oq&EDm7PEuM)kGj1W5gZMuun@AahLn54tL>bLk%B~n(E)3-u)G-l+V0`)FPSmLJG(U!hCdih%qzYL2px6gw4^R$i#{qnOGOh?P%)W2|{zk9>D>ZMZ{{VnARtgJvmi@{y@JPfy9=JR1_wA!~F=o>TOcQ+xHYP{~t$~o#HaP*G? zUDUUA$Vi}b0wcuFJ=`t=e||on@s*Q+*H)6ns`kItOJirzS<7Np&5sWvlvzIOgUbrl zl^SuQ-DNHn3}y!|3;21xM7CS_zDn*geVwn^D=|6GO6QpT(k?3PBA;jdj!Nj2)cyLS z(tGAu;BqoM#A@X8Gw*Yq@sfA%608{8Y&=U(FF3xC#^YW;kj>kNRceNS8; zvr-`1QVTn#ZZU{#%aezdbtyLh)wR@U8+?3Z#90rexz}J| z`*1+s3CoymL?AC;2GK?p>PXb@YJ0MrqB3c->WBn3K^E#xwlCxqdg>%(^WA-Lr&7H2 zC0KnGxmG%td`^ zad}+5Y?qd{@2D*)I%k=L4h?z_C0cEcU0med1<_xQAgx*Vd79mGe8u? zGic-zzvZ#Pz~x%`YMc(tL!S+LSD550<_x>OI*e~{ev*#7Z z0nE&GUFW&Z6`%E8LJ%%$P;lu-H%WMCJNiW)ZwW(eYrT6s1V%K`ef+TUFqbqbv!=4&fdwV$77Q*_bF6oINSZSr>b%Rw3RJY$UbWifql@n;*<43erH zIx>fhRNtL!bml|6hrM4$2crw0M$nKg=|Z9^*~H!V9zdk9HOJ#EJaM}2oL}rc|23sh zg8pc**JD|I0h`EH{*?FdMkWj)N7hUQEKj}!2wV6V(j1Nc_!i$z8^|RKiZ%lQ*e)ME zQ5kkQPQkouOLh@c{&?p{WKv@Wa(HTZQ|dMv z9lLuH1bHA8!~>4C_etH+*t>3_?e9CwKRMup-a>Xuh%zgL7u0!y+zvS4=Pfl;Y9+pD z?QR-CieO^gt}FJY6WN1AAw~wtQ`4W`F1rW7ibSP1 zK!DvsGhrvIIg*8SP6s4`T6k^P@E+_$)i@vaX!cq275Bl>E`|=cL6bfI_#F#t3;JfuW`CgLCR>#jaAd11#cz!Z6vTfNH6b7sn0K&N~YXigD8r1B) z_qQgI7*POTiu;NTMx2CKn*v$yL9@X75vx<#w<|!Sej4v7g~sYu!*8fNuKk&bo1X_J zoDZrQ2{_oXYDYkfJPF;etqk`Z;_QWkp!hCi4kQozBA`n238M170_Qy;^bx>)Asofu zC60?En@8WR*UWTNf9+NXql&Uc={|P{cJb$w^_M$4UiKzB|UE9ELx}hce?nL3+jT7ayM}I7Swn6Wfp|vv2Q6hS652?wJ zGK%zmYk~6e+&6G8EdR^-Gd?ys&x=_3S-P_RvkkHWouEwlp2fgv<&mcsMAug(y-q~- zB`~A*Kii-WJJ(R6N^igD=K>{=h6RbmR2_6SX{})fT3+a0Ggo{1)@y!SNcnzJItNc`Cf%y{v8y zX*3XPeYf2sDY=m1?Un2?-Ene+ZG$qookc`jkA#AmUhwB)(~i2QH}zWIIku_i+Z$m<<(kRd-0VJunzyAs<<5AV5m7dA&U{e#W?dQ+AYG1Dd z`5G2LKof%31Y4fq+HoLlcX#)r2)4gx5T;`&x)Ilz($D!8S=kft1q;s_Jp`qgG(yhGX$9;WK9HaeNU5aDq zTSJ`v>2B1Rvy`KwHVr=r*uojw0(MvW@?WUJnqfd}sP8TxY_d{hZr?I;-1ui{AQqpZl@kCg7Zd)o<^Kk1?#Lu5U z$-U7VQyDI?R`-_|8b05T;g}Ou3Chh4bUB_UK%}ZKs2og;RvzwkB(=hlPJO<+?2jJ# z>K->(0a`zy(RX#A`_6W=@!aM_p>A31%EA2@b`AmYdpyaZl3E2CYReLH0-YUn>)V+N z!w35lz1GDBDfyy(w8y%Qy0Zi-@}U4tq|8pgRf9%7XB&yY(K;ujTsCN z7h!0~3??(_&BA}FSsea>|M`VW3UJv@cBH87t{ffYX_zD?ej4NxG?7gne-1hfh zkkky4jN>xCIW6L7p)_WwQ5n}~HJW*^4%HRQ5xD#l-;1Y+6CU}Sm@HNi=Vd?EP>Rs< zeFk2slS-lP>IzUVBAXKsKp^%}6K!xr+rrw4koZwi%(?Jj?~R!2*4gCbWP}fJB{Q&Z zTj6v$gK*j3;|kwCMgB8FoAo@NPjHapo9t|gGCe)h4~GjlzuvQ{O!l{gP9w=klc$eL zR}f#~oDD&;Q$s@nneq*it?^>dLJm-0G0EiK;laUo^(_kyP;*^c>6XGBAF{w4dod0z#Y{fIG4`rQ<=)Cr z(yxzfkZtx}@IqOw?+-06YIMX=LjM~n&shGA^|-N?l(%o+_JOEWFiQDBtF$r~rrH8O zBZJFxW2?D?Owk=fk9c_ho{m0ui|u%evVnoY$>E1+XQ_R3R(a#Jov@w8iR;LE^2giqki<{>bad!hv1x0o z4O5NkW+0>IOO~S|tNfU}_IRh^5TDZK`y5qg+)!i5RFhhv?rntB0|$YEa%(aMHqu~Y z6LTDnd@h7fw}D90zyR5hN72;Je2u{(JxaRW-6^+L@=yv+PJuP0SZ?`*vR7*5hTiGD0Q0QBKSR(<14A-6GyS~Sz zNoU-x@LZvIgBoo(-beLi-Xq&t8zOp{a$W*8Uh4~UfDkSA<1K6G+T-aSI#I{g%no&% z)}Q1A)W6!a-`g%e>6JIJt*lIH8^}?`-Y*!~e81*2zKhSlMf#t`*%E;@K$1c;`Y4SiGpad|yG31t+ojreyUQeoy7j?hG$;p*wSjGt^o%1s%f8=S=_@Vv6R8wFC=|}{f zqxAdMw|wSXSKw-=a+ZJcdCXp@96+PTM8C)L(Xpvk%fJ%^7P#p$KmY3JXIz90dQ`Xo zv%>Q|Ns{5^r%z_%>B4X$l6ev#Ug@MO7sHxRNPFBn<%X}bqFD@rQqHVp)+1Hqd*Q#k ztYAwUibWDUMXDbTt5II2;nPoB5-=*e;=Z>g|MSz&RQRIUv1RD>tt_SX&Q4OKq-Jg` zmtH#fNRN<8uz)$VIOB#jX%`F7d{bPzD$ss{V4?MFi++_#Z04AQ$)@+6&a}I zJ?a~eOG;2=XIOiX7Y#qn=n7onEW z@|;|q^_vK|+%WAQnn{SB7qXwf40=L7xMgJ*2nghL-9$*q$ZUR&&?MZkYUa6nm$Zv_ zq0H%r?q%9uhCm_)8EtJUn8%Djs7}w=9`C(MlyoS?bTbz$q7Nvw||C0_=ON9HN)Eb@*0D(i4u;NKn>sxNZHqU z!c`lm=iRhA7?+y)m+~oY%9hHdg zFUU=JJQ{qBDVf9>F+ntIAe>5hjV%nFEuUi;@1z)r;&vPoR@wl{Bw4r^EE<5aj<{C?d|3>XR7q9Ks)8JQpeSO;B zY)tsF{(0A+x;umjGO(914{vsKw15BJ+!l8EEZ+GkuoESs#-ZPKcBWOR|FNf@thC@n zF)^{Hg)@i-%(R7*JKy@}y%uR6v9F(nNBFN-)DxS(uIrya3d;4HMDYIWFGxt#E;is` zZ_R&S>7;M4X5!y3*0cY)n*YAh{R=VEDhLu1Ptjd!0rqv4e|@m0x*8Evi0H+ME7*VX zx+eKYIsCu>xTw&?nu@(M|9#!R@SnTz-|rPe{GTiR@0Vg{{L7K}*O$_e`Ty|Z{(t?2 z`iP#R-<%728_~1R!zor%@&0;yZSOJoS!m_ye(?7<#;O;gCCX)FEmf*Or+P26$!e&G z?#axI$O|1-UxF4S{RRh8)T=H}uAa>FCe$>@}a^xe=zm4T;HY^Dfz7DCP8!Vn47z5Snr**X3}`YQd-iAx)_IFMqgufWG5KmeAsA}^<4>ugl4{_goqc9@<}^fX z6P)Ym_a7+A!OTTlwv6^+3XGmt^wtqi8AsawyrA{OWJ?z$T@%v>SR;ahY%Kp&T)cn( z{z0CbBM`aN7MLS^W`&5>#VF>eckj|mDngXj(RJZvgur`8z;t)hg5ZnU4?gb;rFcvNp}c<%9pdV`!ZPq^Dn1nXSHX)d2mfwRADWW;%3EJ z0TPlqbr@bPBqBVeqIf}OeIl^d7Z>E6uYZnsTgY|i6&ergq;?GCkZd3DL2jS%c8yAs z=#)CtjI@S8ceO#iwQOMLgFsk5`un@`a!*-6SG^Yi+ow;T&W~76rOQ{i9NuDL zkZ2ADcf)1Fc+;7Z+du~Il#ut%I*(z0A>hUcpT3N?$>xst7wX(8r$|U_>?GcRqZ}GR zUX}UGWE1zdZ|};l;x^c&XV2p`H8zqiFE8KfF`|~0l}&W6_G!<(0UzzZ+ntmPl1$AK z;C>BTn!MI4KP5TbU{pz#czea(|`ub?sWWQkT4$%I*0n6Nj+o{%Qd7IN(&nAUb^a`# z$zq8~yvg(PsysaK7P93b+;DqLHzpfOoNc7qPmkd$>%$GIS(f7qMED&4I8?a1rJ6Ja z2-$30gLQRfC@($veZjG}}S;PO+Jhi@!3cGVhbh~}E?h&hH8yk#P2ZttyA#;a~ z)cns2Dt>_BpSR!_e)=>2O{eyvxY&YzcTx{M3&*l;<#uMttw{2^^rAw9&B-vIevJ)| zZ@^{gxz3oD5&xY~oZoBAg5a}nUSp@WU-&_0Z84Bduu90#I_AD_Gd4sCihEqs zEcUi!qV@cKlgyO!_Vw(Zct#Rb0*ssAS-px#1%L;8k3FnMrJibB8Ra%?4vFEI?c_ly zDk=HG^sUZV4m_!Y(|he4Y<@e|gI%#L**`G#WBk=U(~duBV0q>mX56jY;N&W_VPiS_ ztTQ@x7(LRP1`9|br#mS#JHN!sQSCSaYhg7`gmA$zWic~*IX$?f?gC<*Wv|8p_EF;| zld011;97?HGHeG>+t==I&vR|8L>f)G{UOJ*w6qaqk$V|0`M4P8tsQlVs0(oi&wG+Eb0>=F_XvMSCDmLCuu^2w-k<_m95GEx{F+q)sBYfCYzs8AM{L&|a;p8^r zp45;}*=UnfxBOj3K6bhKQa6vzCT=}+Gw|6B-k>cL4T_9(Hyf&>E7Q@#4`)^Y>nHTo znrOc(&URxG$IQaQw7H$c#=)Vv9tXGiE2-1kSSTDzlasG;U1(`SxsyapfeL!~_;C}= zd(RB0{qcty3gfLoCQZbYG#rj85eyRP^7*ks-s`mVcjeQpQzYWy;nRz|oqfT^mMG#% z`q(i)U3sKT`(UXyFfA?ZuraDMWzz>86zt`1pD{m%l!p1JOyJh@QtM*6aFIPAdUQ zlssn;Flzj@R`DYfKtx5t&hKz4P2dEn|ljsu25tT1RXH5 z9p*05fPw7$S;Fz)LF?3R{*X3iso@!vKuye_`L7HWK_=_gH?#3GUl+c=h8X!5m)=&( zRX2rLL$*ys(D~^PLl1ZwN3;9gmlRUZKw%Zwo?_M>1igjjX7jLMVo584-{&f z8pi2~GSVXtIMp7GQHiavaDm1{yW~N3f_{M9x4L@p_CH3~@z43K&3JMNx zNcYDlp`<*PUtrnss<0E0~WWgOczBp(VjYFOGSJl#_2%T?6I--r2x z9NJYpRJB+bPZQ4J&o>QMNLucp6y#ilVB#P4n#4S}Sd3iQRFmsBqL_QDof&L@pQ#5k zv~lD5`MM!bsHWt5w2}WRM!!QdV*S%X^60D?6}FSEHxyE{82#%Pb=z`TB1XIn_864~L@PhcoU#l=m`%pOi6N`8O;uu-{;`*mrcW#r8n zT-*9?m6LLCP#!2N+fEPLe#Z#-HqN!HVg0KQzaQvtR|Y^1AYRm({+*#%PkCm7YqC)x z(i=mcKU5$+=C(^rNB($G_~Kh5Jv|!J*65U&)nJrNp5x?`VzM;LQC6a^bm z0Swob6K{-zQYXHL)xi@G$i_Olkhbd0*WwAI7M3|NKB)-BAFp!{g*548&{y?3oc`Mu zL+J|p>r{MCgqWC_@xvtf_;BuuOY|^_txuo9_>A!K3iy3}Pj+H;VA4C;V21wenx+)i z^@oR_nXD<$GdYTak}`1Z*DtfNs@vQ~b(fi$nW6bdI(^+>zm|~4+B#k(@rGACyD~V< z&0i9=#Kf`AT+`jlT8r;$LpYroU!{2*Zh>*V?xd~P^kn<_=3ce)y75r5_~Jl04|hc+ zHaqLEJV+A9ZRqFr`^)0sXJ%SDT4ND&QU`D`;JX@Jz8o2wOlWOwZ7X#1=1nRI%UqX8 zM(Yo=tODXbt4_n^Mv~vmexGRyK%Ym_cp-e4ySs(vmP0fPw4lXe>1=>s+-jo=K!dtY-Dz8u67Y ze$a(WPL)z{Yv|Xn)*uq*&-QH#JG7$9Z=6vR9s0J21Jt*cR!K}4h?vZvPmiZx$t<_d z=BZDiP!SW#54QYoswU-Xe`XTnHj32_-&5+ow|J^;U_j@4o-mrKhJ7{BZggjxEa&Gr zA4ojkjiAS6WzF)yJvKKtukbj$6^6IdW@m4|x#!i|x`CXFs2J9k-q$gII#z6#o0mVc zZ#&bxUk5ee21y{z0?vaa(pW4NbM_F%y~h-F0aO7S(qx^q%UF(eU6z|Kj#ko~IVC(I z#jUrVsB0=oOBu;bNT3lb)+Z^y>RNoezf-$iT#P4e-%H2!(Zj1(u3-H#S8s@baDLCM#Uq3e= zI_p~NdP?1sEa_Wh^%&83^cB<~6(DWsq>Yk#^5hKSN<6avC?@2F0)&>@*5v_f=u^a( z7nX5qT)yT5IiVwtQc^xbVURnX0@?fF{Uy9qnHwA&kdZKaGJ>P0SD;sY*M>^w;@ZSu zo;szit?h8tv(Spu6wv;C84odJ%X>D}Uhhe%O+*RoX$b5LjU_HDX;GG96tP}cOQ@0* z8Of=S9lg~oy8Ok2AwnugB?*r9BoBTH56|K>NOO>mKvMT9A^JX~?_NhRNTxq3n%mui z>`Bv)1TWGNF)^{om>5=cHEhZodPTI|iMBDrsO%I5>f30Ybq8^e$kX29`{Z=e$N0fW zXq`GMz=x!;PNQqx%{G^$qtZ;YYCN~EPsrNBHi3in zK4;-EhFsAHVo1p+ zJqGn!LO_k6?pY{=zVqs9oZi~Ar+&8}s4TS*gwkVQj_Tf{$hAM^O-ZT7HSWinwBac! zDb170am@P<$_PY1qhQ>Ewe9m6`0;P8~#EPS+tndeZx*Y;0`7UOol(ol)gUN5>NZpz7oG$kPhb)G#WmN(Gb?yM}&_C7#JX(P4Gj$yg$-n}dL z*X>I{iArCzgxVC^T~a3m9@=9i{$5H{5=IQ-wg zr)EZD)E=oF#cai9?B_5z&uvss1wz?UR!>jVXxLDzXmWp|ZKlLm+zYKUyeq>(YSP~v z94}zYgx*IAmy979Jdbyq`b^u>va_$>xSZy;Q`2)@V9|@e+w@c<-{oOu zwsrHS+u{H%8G^VB?nnES{l(lbF;Un&N(ki(IE7DrK9y+P2s;aOT z#NZ~tK;&pJ)6^e7P6rzIkfz{=^z`-;!!k|>#v)g@kXl3I67qELlK~A-2Zzr;g~&10 zxb3>O>iSPA?jfD~>Be{*c~+nm6mL!)yrh<r|9NC;LG{2+(sG&kJ>@pBVzBsJm#pb>EX!m;;6~9~+bl6VY%) z^AJ7$c`GRTmeO&@`i-6`HT^YU`HZep&;Lq;Q$YnIbI{^XXK_+LWo51pN&=ni+iw7sP(>L=V?FH@*6a6AxH7G3pl${p`#Ej} z9_u8Rlf|g!2_rid8B|-aDr<2TJVXCv_3%NB4%Al(0&S>6D~(NzjnVbIPd06zWZc-> zYTTG=ib{?Rm`OR$6ylab>4tpgg#6yw`A(;2t~NGw#+~)5kxKP_l@24y$OYYlt*oci zKC5*fRASf>v&CAjT@@AW?7nYqEdTka1!CMd2qI<%1o1DPU%!4)3`*P$r#=U#g2pF} zi!mIJ?@oX18H(G|)t%nwU&zX0J*1zifzu_1ON+4Tu>%Pi9j=tNC_WL90*u_?d$z2> zsar#pO{P5Mb|i4ex{R3Upkt)?>?}_ko9psgSKRN@5jayAZ{EB&U46Jc-vGYY^X`wq z_;K7*0VsmiKb2&LhFX^%s6rog^W`~Hn}%o3&X6QD87`SBDep;9kZ1koJu^ESbilUB z%bySoaS@cw{4;9B3Fnvc=WHBo8VAh>_{`dkX&oibfLlO9MWsFYNlE7Dr~H~EDfq5x zo6}I9=_9&UqxxU&x`EVCrgzOYQNn z35>u!I-B7J!;Q*|!I+jD&n-f#IdMT9JNlP%NhD_xB7Z~_PXrVpgZu{iiRSgJ_+Q*? zs?cB3(9~=NprP-}W2NuB{Js#h9vH?+aFl=(DCBlRMM_3$Ti;6I9X?^{C98@noRCR+!&=i%`?d{N=o85pM{)bJ{CL zVPSV;DJ?BZxG~df%g?%Gl$7v;FaF+b8tKCC`u-!Wc!Gp(XSW44ytB902+csL6KV`| zT?;9}|FdZr*LYz>>`QbUPjL)vnJ*FqHHz=~g*=*nUjadWr! zwSOpOnpLe^u3j-cH+NOgw;c!lYA-r*w;jK#Dz`m$oS{sfqy$;b50n~hSq2%IhsC8W zv@*+gTXR;trENjX_QtX>`J5N0%63kJf{yK#xzisnRH~m$PD&#nTEVLyYmyxuy3y%r zn0)*=_J-2^3n@3N(6fe>(uQ-TTSnV3PmY8n*;D+{U@44!etiDiq3(j{?2r)-qM<<= z;kO$aBT1my^@$QvsK!uLZQT8SRom)w<6k^OR%RyClKeF%MOUQ5pzIQ^v;1wVQPl4# zFX|1lKiyEQ;PoCC1B4SdJ3BjG$o!Us*Y|oLrf}MaioY{j3okD&PO24M%@+0@x)^-e zCCbc1WdGXYp_mAZm{*WxLPjdL#%%V%YLB z3*1S3rU6%fp?4|MGfXoHpDW0VHz!SsE}kk`AGjFFx2e#51{*3fz?g4jF!*<*#*L#a zCr5g6YMu6~+Ubz~i6d&?rodBAA! zL-#i8Ue`hVYe3f8#Te3#@7Xu)pNDuJ>|!#{g!LG^hfepXk}>Y@ugx$dGPoV@H@|1H z=f~RN0v-F{)2CcqNpb0&24;E{UZ)~330v?L0 zxQN`?C}eW2`!R*{8&s;{>Cxfx@Hs)jn#fIA-_gJJMCJ4n&h5vK!!W1LED}mrBt4MR zOIQaeO};C(V$M5X5!bPD41hZbDbAmN=O}`@{plK}v@Bj$aST4cWkw;Qx6+<6&*Y!2 zktq91*z+P~Tt;u-FKHMG+LzncBjmXpeIS~)e>mzlUhMbK;q;IcCXh!RR6`~k$`*GZ zDTQ-mURQn4eLHS+Ae0&#d{n-rvv@P^c{DlUjgjBV(TSHiYLOXH@IgwZXQY=nY(4>T zJ%aZ+56Hb2P~P~kMD9K`eppwP>>j+z^Rvp);`@<31^B+U(1?iGD*w8edxKL@P(exx z0q*9tO#_qD`|{DIabm8czfFib&mYv?&)dwelnTBrfL@#Z|n#x2Lp^a1-%Iy;E+s_OkopiQ2mPl>JSS! zw!Wod8-lYz5NuRvA)4^laZO94C=70krQ4mRizeL-ILwz?e&~rKL~lwU;b5W z6e0QIM`yXJwi#9bfMk7rJz_nMC*#3VzxHQ0a)C~B#nt_d23hQ}4+Ds0o<3z^VrI(X z7G`shG#?%9Zd&OiU;SG88k^&=9+v|{0Yg~>QtPE$Jls^11b`Q8ZS}s>&?N4j0yE&= zLJ(4qRyXh_z8VbJ_%t-J$(k*SAL#GCj*21yxlhg;uY~<#-GR-YvYorMw>s|cXfbq; zKuJ+gQAH&nv`Z}v25ml_#T8R^BLo|P^(rvv@(*_YD}#W4yMdLbts(VFdfH{+n-d;Ea^CKmcz}vbx`z?)Horf#cJ}Tla0lqg83Wy*QN}5nc zBsv6!CcsYR`DQg+YCdy!Rnj<=|3wcFmIFa=Juf>tIs&>)3$hn#E`~SQ6WiMro7-BZ z%1xRky_U9p-OIPFw5zS?z-%|^c3$Rp6%Pr1-PM;7FX1UNJu?%PoE*+=P<5fM%+van z8a*T@AeJwI;nlPyUwuaow{tb}NU>GmXG(D%wtt;$yIzSICH6<8jLx#e)YKu6dYX3a zw+|-2?>D}{+M_;vdOxUG%{$~%ND()B{2NI!eKMfKO}Tv;*gPE`5iKG$-a=Uo^~1iD!r!`Cxc2#RvVt-%HmpnE@|9mIgF#@dHm@LkYV^R z4oTy9i4COL7(mRb0g@to7V{5mQywac(!g5REw%C&bzYx__Uc^F7}5P^7F?RIH5Xc* z>NOV^YYJfiI`NbZa&7qFbAl8WqSov1Tvse-j2qhfm3Bnt!o3P_j6`%yw26o<6HFj% zcGc5P#*xATfZ574VbtQ(YHCEbM-F=t>Q@}SN&g`E5`@Wgb#*TgUb>rogG~Ib8xw4c zp-UfLxo;g@#IBf(t$8+ys543a6vyUZ%utg-&{mc%TfL|F;6WsZ5jMP1wEYW* zAq6wW_k71JzF7M;c$+Smb-G4oCSJE$#nFp(%+4;Lb}5ZS!!+3m2?RE=63b-}aPfop z6}M5Sn4}dIg+q=k0|3+tPf-Na8(N#iP~78Bp80EmiWL8y^@!%?vn(tu)4AIun-U{s zR`NhGlssF}1dPmd!w08iyo?S&d!M1l)(9yZ8(u6>@D(K59KB?wjfAWvGz)ZtS(W0_ zzuh-?admBY`dyn=lyPFKqb-6?_GZ>2c+3!5B?SP?e2w=S;ys#|T(X%gSdI#FjM9E? z6`oY>%nTKRKES5FxbTGwiBeKw4?UC5uKckd5@x^w1=238c^?~i479#Y89%;5f|{s> z!eDiYGYZ-rHe8*Sp(UvIvpw5)qY?zT#oA?0^9BR!W%l3SWg%_YiMXg;?j_zDP8SNI zN{kZRgpiSnJCv9P-`}J;S8cf5Te5$kpV$sX-})gwDDKPy05pE@EX5;@87(a>)3S>o zVada(TS5#tB6)NjtxRVv6bBJLFqMmfD!jL>q|rmzkfqiKppM))u28=m)y?9Ss|5zk z!rDsffwMG}S7LU|5RKmk^4RQ$g)+Qoe^rTF!e(?T$nDPTErevt? ztldw!`|~`NIo|iG>SjG?ZXaDeeq{*+dL#rMprc`OYUFJ))SX0nd~uDkYeXWJQ|Arg z(a~^S6Sb$;(p!j}sYD!ZKs;AJ>bebS zhxW7pc7Ob7v2j#7Tm9l$>DPU8bF&F)23$M>nZrxuBqS8!KRL9fAqLO0Ra(*VG8cy2 z1B_9?!KU&d`!MO<#Q;EzZJv)rG(bT+yP{75K3s))@wVXLQE+QtGt4&9Ob(V~ViEHU z`HDe(_)XV5*WELgu1msuvk>kp2g4$m$iH5#z=^DaY(g+Uiv}jU9^dlGA()^VmNod$ookvqpP)zrD z;9)63H9qscFRUs_Vyx~8>6i;pH9*w+2H~|Nk^&!P9({k4gaZDGBb=0qU|qcXx`f` z$)4_^4y!Yyd(3{;g{d`KPwT#f-H-hrPOc>>4E4sl{?*CJb`Sh)y{uF;lu0$AUaRyM zU>7jj1`#H;X=|8&PBk9nD=%+9Q-XZ)73+odv3McZFU@!MP9@&QN=`7xUd68HQ?1-9 z&SwDr8|muoY&<8}VYJ6%Pw;T<9aEU zS{Ls9&y0+WRJp0YuSurA*)ohe?7YQ}6(%+uy?4Nk?MSGqdK@Zxbz(41zxWVRr53W!&c<;!;569S z*=npg+{+~qfi9(!(?9OPNQ zqEBE}OOniNQ{YC`@>@td1tS6cY|%%4*sR2q~wZ+igLo87MQa<uQt23qQJmKW$q1BnYzrUjrW& z6c+Mk;zzJxdT9^{Y^j9}>Hz>v zK}z4}{Wb~YX>~mcB3W@>bbs^*@0>}D=?ogD+{DxZA1J)M<4>T^Tac=^`2W-=TP+9(CvDJjXqL z9O@x6B(PXoJ020S!vbNM<=7<FYKov}bn zFbbMth!&U?NIdqrWttj+rM(A&?3ypHDyk2c>T4Qud@)#{y6q-lNkor80F@>M#s++t z!btm=Y*@244t9-6k;HzZNFsMUJjf3yKVg7dW%=iX2>4}UlB>b$MMW@x`YJpnmOrs# zLqRo94fr$1P@<0Oez;Cf6-%fHK-z>*PB=&cI(Cwxg5%S;*w4f={Wo|1JdTziyH;(a zu3d}k6LFQn-5Vu6Z@t0Wcows zH+g?uwc#-XgWO&Dsdv?)0P=9za?7A4)wI#GkUj8wHa`#bBoZJJv7n)og@rBMjj(r* zZeo$K6I?6igLMPT-(SuFzeeHBv*U&?MWfU;p5@<>&geVtb+YBv?cS3alcq_{Oy6@2 ze3bRDwhfaixO@3e@dbsIbAoEt?ay;_wTMyaBPO&Tdu)7P`cs<;y`=oZLnNDovw}br zQjWiIT1m?lH51q6sR-WdYU*tsJg4g@O8&9v7rwQ0&MxmD<|pYZigW3;hn@m_j<3Ib zy6&*smNB==VTNQn<2KNf_GR41qfN_Pkb>)ld&oY>`!hv9IuZ+%FSHY1EOzzlpZlg( zjk@&B&;_6CxfgfN@)%lf+x*4Xf#Wt3jg!M;x1-);ju8}gvC3(`KLw#ICfqpIa>}i2mWzyI>g5pPlQb4sc4~(Jk^~EEcw6)}{ za##*~oUbMO_%VUI6N(g-lc9bNZ*7&BjLcUapOT52TXRGB)&B;y5vIR^E4B8=(YVx+ zBb>p$Az8foe?h#4jD8;v=J{qQ7!YGv==b&rvHgs0`)hA=RLN$c-y8S>tWm9~s)Fh; zuH(vJ!}#fOizI0~W2RW0=5PY$5U(qaN8!mdKd?r4h>cnEy)Pa=KCUn_@{ttfsAePv zoJYr-g}}w8CzxnSFJP2VFR z6oBM1LsnxOI~`=wn{&jEl3Q_)n6Dp^lv$6RhZ`G4ef!c#L3u|0Ym?VY#|Kl)y5C-6 z!;dKwjVnicOTHhzKA(cy30T^@V-Nh!V>ic@`(v2;245RTNB?b-7gSfTV)5F8N?X(1 z++1ToJbDLv>rrH_rUUao78&Ol?XCV#gk3U*@wlnCukUqf>Fsw$*G=o|Uk`tgM?YH! z1-oGA37#q$llJo)g(!WkP4%WZ)+Hq)+5uf@WIpJ=%GtxF_HP0;5JaZaD6OLDvp$$1RA3 z9n;JJL?>i7djaD6NZseI9n1K$X8jd>FujTJ#fulT_QC;hiZ#sF=X1JgZ#&+G>XrLNgYHSVqcM;iyzZn?QJ zZK`A6AL@R+#6)}jIVw8l+z;Kg&7&xWYML7L+7%K!#7`MwvN_k z*ylei(p6t{O7yu-Myr3uhkO zM6$Jx{dU$n)L9PjaMm}~_Y!xjsMw`go4E4) zkT1vAuCXCOOhQ_4wAPvEE%oW;)4jFG=;*W8+?|G2h3y|gMjLvHDX28>-@g<_ zP_tldIm!S7)S}F0p#2oGrgbiX~8ohDD$>DV1=8$J`Q`ZO$w1qVGH_Fh) zRtMst6K`{fN+3QhzoZg(yA0tMgz2^vMPm^vXX zHo0-TNa6pE5|mmKDaAGqt3yLSOwWAN#m!?rWXJd)bLrwD1lTSDfq4PLsxxxO&xd<| zU8@-KAvrJ3RpUMcGqC`;bn-`9$Em5%BVugY4DFyM(wLp@?jij~eTh zhM6$oeP?AP&SI$OswH^8kLSegvCOSiC zV0k%B*@d32M=QV7h9jK9Mc%o|#+oerjF0X$&6?*C6gOlDQmLt-Q;Pdoaj?tsbWHJ` zuVYWXj;)~iPAkvw@!3chM>ckQnorIUAis8%W6(V-)n7W6sbG+b=E%3r9f%s#3)~%f z_(9L!vBTbM>#HO3N5O_A=KSUhLDDkQk5(gEPQ4C~t{JMTFu4?NGO%4>^dpuz8M#N< zD|2C!A+yiS#dB8CkU{tzRJKB6{0%a!#*d(it+Y+Ng@6A8P$^6L@O%aeJp?5R9Y%g%_fH@%>i)+tI8r?ApyD!s3; zBy0|a>d8~@^n@A?@`=8XX?UAa?Rx)cRi67rqtcQXRae5*38MOSNsKWlOXO-ilf1Kb zXb&dl_H<9J`%>T67yjE?&G9p?pR|X96bM%g{T@45wr!M%R#a3V$R{R3MC|Vh7fr-; zX(Wj>NjnIss{=Gr zSR<<>z~h0nWeq`!{nE_NO_717V5(SWK^@g$d|rAVH*H|Zw|)#SG(0@K8pjPHD=UP% zk=T0e_;?(~qifMKLnShU-lOLiI1~)*rs#rdn85)nb05})6Fg`Q&iw|A?a2xAh| zl2gKK=uNoD({TzhRH!|ifF+IaE0l5wDec+Yk=S1%}q@!1Td;+ zfKdy&;M&{Ut5=(9<(;{XH+nKQ=WR!3iX{z!EBa_fb?;YifoWcsA-%tzk-4hTKmc*< zce0Px)QVW?Np2VSAj`*w&h^Po5@*P$Cs}n)AFXEU8Z#wQBnm)zd__6V+N-;eQnEHn zuzHvmo2bf2IYq-ZXLQxK5rSPg^|P#kX0L4jZT@h{!nl&6_FK{0sS++=iuUR&?v02# zTzSv&O1Y{86v#%c$A6`X_^Q=8$!1v=NJZyoW@eMg$VHEXRnY3WkFro7kFAcN1FAY50gWp;+_eO51JC5FLx+J)FNXcV78EK7OQKY^@HcxYfHIFNO|exb9q?kh0s|YqXl} z9nhzkwL$+rhzg9aCMyfI>k*<;+{-RR!Z(i|IT+B#5`N!1 zI$8>4&oCWf^d>{0&Wp5gh#NGESFe?$)Zs{Ig^8rB!gfHmjj+t8Y{Q}gwA_!_Li3@P$!DrGll=P=q-^*!rl>0Mk|;Y`!<@~=N8 z107iDUX9nl^*o?A(!|bVmhv@sBmMFB)Ya(YnC#mGfK%PEmSfCY2cPf1;&mt~HU{4bMAnd7nfM4b75f?tYXL zJM3s*gjg)vIVhKcp?+mfj~``@q;+(ODtegQg)29*VCJmTvIHwj!<-%&DR-{? zNK#s=|J^OFqQXtl(D76_0ol!Qzy!952YWRUkP`44!a`VO1UNQy-VL-y3QvE2^R3?x z-`;;gPaj(ol3{ShN+bOvD$_mw4CpVChp1?NPsMp-mav@_TrS-KM;kWd1{z=IRv&|v z3bNfFQmOU6{?oRt2!lb%c?wr@Qmb}>SJNQ`uXb(_I>oh zB2NK8V(BaF6J9yqV!Lw}Cw+uWTONXa+-YFky*G?_`~WMfh_y1w}3%e)#y4Vg^w z$E-(yaGnXy<(5*1AY(aU*!VU-S!vxKZa!A#+(!jE;(zZMv&)w8y0A_yTQts-th4w3bNAr!^3scSb(;%l zBW~R~^Edw2sfPl_9U-;;W08@&CcJ~Ek3Y?GYP#-oIIP6fY7yG~pgA1+WJDCF#`hzG z=Th~3EpnTpzW&OyY@`#>`A*}QGX&h!&srA5@B9M4Atpo0yiUq41hX$hM27tKrBz+z`eLq+({1miSsrtZrUbM zF*tTmtU((^_&lTg%$e(%s;VkucBS>~7ffOnHuL3IR~3^Djv<{ol2!r!9G;V0m*l){ zV`J6Rhajd{817J3r!v4%nchA`0#dM_ zK^lap#B&z+>q|x9dVlA~Ao4c-jvBvW&fC0s=;P~CT3)>p=p=7**>xy@L3>z`17R(;dc_BAcgKy<)~WV@N^{3AKKSj9M~ZSz*cdo5g8mt$ zA@Bkq@hA&$=Ng-9ADG1$#;1q_UQQZcz%@~*lcT(5pir!MWmcJxHOu=Q3K7TMv4 zv#&w1N53`4T9=lW&~!~d?H?(sUAQfGn`*-h8NibBVs`9&6BwTiV~iJT)%lBO01?Zu zpabMVq{oVp8zrS-|QLC2z; zQPE2x+b3SMAo!7JjWZ6LrqCj;1SC!nEGleEh^5>|=Bu>l7|D88&oM>^d3!^Oh{I?+ zf%U}x!GSBVpe29@TnLU~0APYZx54t7mO7_1Z_OElS;=*fe_u}t4qL>Lk?`PurP1lv+6;QbUooXi(khZdVTJ+ zyd6Q?l7g(>Q7+=9c1a9ZS3f1KxbOO!*CpZboBNEB0PiXq9o^@5S`5Z#LnfH}C7>^T zd(+oZjP*YY{rB62{t_cTE^f8Gw!F0Tr&ur%^7ZEjKYvz_H(U`ztWY&gfciwrrV9Z9 z!qT6=G{B!Hlzy_ZpueihYNC5;M`k&gb!B%Xi4UY+`xeF8!vz{AU-pZX3)I-Wpo%Eh zfP_l3GOt+}*bbBxL@>Jzwv@5au-Z1KX2O}TG8pIdj|ai5=Omz4#So|5#Vrm1WL86 z&l}?}9s_|}qbXM5a@z&*GS2c`l}0MS-(uMFI|byFRa6EB{;Wsj(Z(pqRC+7chB|tC zl`}OOVM04zyJ&zC`fSw=_d4_n*hQ-R>FpTEVPAHM%H$XW@I3=iEi(j7^_e+2_JC*O z^VVl=s+tv$N1uTh9r$^FUhV%$$OwdeFMx+9C%|T9067eWg5IuvYN@b~`saK2baQia zzkt=>%YFjTxvH{{`RKL!YbdJJK%(D-DFEBNev|O^s zIEUc{SV|*sjo2#;oc*gGJEs<-#^@Ul>pT-dzQ*o$X!1Pcc}Gu2mez&S;r5J1MA7px zkx$Of4VPxv7{}DVW@l501CX~leSr%6j_%3!>_b79?}kh&CAPoOdz1LZz|wS^cQv2o zY+=y+mkV&P$x0D0Lee(+DVUf!8>oG5j#v7E@!r_T#ikTsyGe8fA@OXoxme$9@p8A` zS$_0ol|B8G*M!`=7Ro|6+;lLLol`hj5;S&@0#Oa#x1u%V_`b=>l%D5;IS)2d1w9j1 zS53}Ow#*%%O*OYs9-?M0O&5Y95%LG?Nc7iJ7r<)hnjYKl9s5zJS^MMJTmRjyX1~Ih zn~0MR7WsJvI2rJ+zU#1HY&C(5FEwb#BP0})lfwo|JG`&^x(aCg!%3Qe9Er=;h>A32 zWc71L@C9td#mDbCoNQkuiqSZ4UdEseDv9N#K73V?r4;tcG^l}X;;C^hO zRxs+`4`RsHIQuIF_T_jQUf%FO-yWPF?Z}&$Fx2w#(T=W!+X;ub5;6RUk7)9_xss+jzIZu?VX)LCn&E}ka62Eu;H>@?>sckBeLkJxaScg!(b#Wm94KXtD=hz^}SR8DX{^dKi(n{)1>BZ&A zU;LQ5cgni4b+kuAN-H%!S_s=QiaLexb6&@{2ARVF+8VBPznk%ymK04{qi#n?(z?_0 z{a-f>f8?GqEt4Ii&_Dz_uxR*q@|2Ha18RDBeL2EayVjDd7A`UM_jBv)Q(k5&MSCV> zYP{ThRw~%evy+xMzl|RdqJ)H-JK;nfBJLesGHruRl+p2or8!I}4xQVyYkpShc54Oz z#mBtcoFr{Slxulg%V~SOi8!@yv|6U8w?pr8ycEfKqy;Q+1EVXs1F-M!qlTT&U=trR zs-u%5L3`@kcwm=K^h))Og~cljY>K%R%PBy}Yr4AxR#DeXBB!;}&GPzhp`p*{=wf=v zcS4W2R=QX_{`^Uk=t}|G;=$mnUTfzWHQ~Z?Kl%hs;0bM&j`{H6!!K}yoXuUGOjOt! zWNk%)4p_iLH@pQQMmmyS!+e}`dt1c90!yPh=90s7 zDQKytwwA+W_i1*+1V%#qqrPsv1W7uTr<&o z)N=J;+8#6?;o}i>=TLhUnh4nmV`5_RJFa(k6(fD}q*{TG9_iksxn^y;xtiVCpdsL& zb^nPdF2Cp#e#yX~`}_O|7zl>sq`=f0Q-Y}E4O-}14!}b1x8M}^f{8P7r{N+jOF9;> zA8~Ydi#1B~_~zc(*%?xBk#@N!^;VC)qo)f*9g!X2c+yvmyNJ zc9kT#CQI!}>FHu^>;?cwU{J^l7AQ(Lq-~!>L#4H48u;ye@whtTl}X|70|NyR*Kgs$ z^gu{Fgqb9N^^6?EJ`ukII5*1GJT5!hL74o6h^W^u7(t|-MAY>6Yjh_H#PXtCe8G~xe^6x!&N^neUBJF# zZmU;qeK7d_nM@+PFR-cl9vv@f^ZvnGZ5I*hLx{^w{Yk$GH}a(EVti`y2?0fSonlTI zVCZ`$?5{tVMKt7&n1#hY8HT@4-UCx~Po`M=6`&4zkB^f;&v$9GnMjJ>tDvr~^}=mk zFAOc9p?m*ED-I}9a)>_$a1Z(vA~Z&Q-yoIGs5c1&LLSYa&RC02)q9Nx&G(49(CaRL z6Txjhs%w_7|98GMEE-A|JtRhm00;=AO%?n$M(GQcCMR~7@m~h{Jp*<=02N5w$jhI~ZJTI7;5DG?y zyfZsa2<&W*nT|5SW3iyI+ffl`_Bt8=_{eTBcW>CehG$#a$_%UaeH#9Z-hCUC7G zvza_|gJ=>2!BEuOVCB2_$sWTe1)KcJaYiTQ!HV?LIa|&U#OlMvDmdQq#=V)2i9CWs z8SOe{SN6oLB@1J-aqOPwI%V}YKE^4YF@SzP#Pc*wlsWn)^m_fE@@+e|#duBN3I@){ ziW-)lUady^?MuFFufqYI?{$s63kaQm&?fMMO0GNx&S$^>8iYJ_jF3pFS^sbq!mFDc z5_XSK-dT@~o5uV>dz#yz?OBU(f20-y*Yl+4+Bq^?_H1cI=ems|Fkqz25EGkf{AL`( z>u<5JPMzZh`mY|`-?q)c$7Iu|aLMQ)lW(*MB0x2cdk!kg%yYY0#F#K5*W z{PRTAdgi$ReE?|P7_2Z=Y#q-dq~uh-JRlJu+FqDbNGYCW)sRv)A{r~kctTAr0onse z4li}D0l7HCXJcYwVi>M<5dkpV{hICA+lvKP{H;Cpp10pWS8lKZFwz6n0Q zY3#>K892~a_A&;~t_ysMF!tKWt=Vk)pMvl?Ff2&Fb>GnAXyoQL4&+jM z_T1Tcp)|^Ag=;JZ=e(}`_6!g9V*d$b!RcH!kx;wE+2s3onG<*}>dK$K3%m^~k+t?W z+ZN#eZ-_Gm`F^kL^!opEb*HhB?Eks8e}?woAM@N&_J0m{{S%uSDg#KVfd@c&gU{o%L_gu*@%Wz?fxVB~VkjF_ z%^js?XY4^$3!)&&dH;X8XexZX|9>4e?rMhg_C^{byIP}|E^s$6GYX22C#7Q~tmUgU;U>$7k^fM@ zhwJjqd}>U1KIzf_@r9(PzkuAr#q3nv9wjAZEvSws-qT5%`v?0BY%JHofPqo*VKic!&CFoi@ULt)C~tV!%nwpPXf`PAOJ;FD;-Et(l8NdOz}uJ03F zXL!--o;#|;Y$C)nU~L6mQ(n?AseS7|3mKy}Eku*uSoGlEO4-Y_*|wI0)Hef-PuQ;b z9|u@Bx9&{f4XLT@Wf`UR?NQe$I&U}He-!R3-Hxn?Ck?xaqYN_K%;(KGYUh0z2cz7` zKZ2MXwmE5~7~M1YJEvEYOth0suj}@qb_2cS$V@ab{x4UeN5y3`L)%LH+U*gX!*Q69 z7Or=)F$Ro`UNd6WN$EI03E-!cW{$=9$x)u!@l}W`*#TFP2?LTZ%pJ|c`Fn4T58iDU z%G~vrMjV#{&qw8G9sQut(&Dw&X7Dgo6XZF3JO`*paj>OB+wND7v39`W{s_d835&Zdq8SUaa+#|&>p+G%{_n)zm&#o0??IzRUpyS z*B1`hr1(z=#llmBx@0cEE{ywxuoYykMNyX3rc?gGv8?50M`l+QI?kIwz&Hqa3Kex5 z-i((Ae9L73mI0q$x}N2flvj_X-hfIPh#GB~SNZYjHb|dqUF@VO4Q=@Jy1%_ zNu0>Fwif;1j)!B@EdH}6UXE1+>-ESx_TCo#*T0!EQT&&@OWUb= zPw7JoYj9<{3rm~|Ld8O3xF~A<1iZTGmdDGaW>Y%5f1v*!)!V`MRxWtP%jbji(XHE>o8aCB?-OW za`hPQwwx(BG4)f94DXyqU-1`uk4(oR;vd=Z-$^?&sU#~~u-4z1!3$8gd^pADJ02;# zI#gTj`8YL&lT9H$y`ztgbD94>AL)|CtRI4v`FG235_w{KBq4EnTro#^bKQ!Al@*na zs{k`qP?r5;Hun26jGi-8R3oQzzBsD2cC-o+5$FdwvZ?;{zP|dCfIr|0s-M0U%<2`l zMZ_c_PehC5NRd{+5jl^|!6-BI`qTgv4dP#$;z3bR(3C;y4X9U~mXlntt0iKmtA^lp z?GaboKe#b)%_c=HM7nZ8D5|=eUOroT%nBq8gg(B$M?-H#ea_5!RpRnxOc!UrRavI$ z_TG5<0iDTN*0bK#qu9mXWC5JY%Y{tEm0k}(haaDqx?!HGt}1J4ZM`3EHgX-abwduC zt4cg3>`*C!ejxF~b-Fm7O{V>K?aAf3U8BF}zUGHM6X$xwzfE!W;>80M1%dwSM1j1VLm#giWQY z0uNV?yop`Rcy;412sHx;mA-!Z^Sw@M5OM{ZFEpT|&85dA^YVt$u1#85)FBnC(}PUA4z#rJYdFT$PL28B9AdlrEG%FIZB=UJ12u(ta@ffz59m#)Ey4%)A3*K zqL+c@NOU1#Rbe51{#)?C_Ym}LE&%ft%^I7ZQI?OJhKHlZ0>Yp2un<8)#eWMZ=5V+l zue_k6+dq&Ow!K_cXgvqKijFqfMBs)-3q%e%%^KaH2Uw9Px8Rdm_i&nkaE9fq?Cibv z?HS1CWF@|LYPGfTT-p~LLP&$%(j!l*a-s~J_10%^Pv=3V+0jck-%Dxc;!a*~L94}2 zA?V^A>yH(Y=E0|2q}nuYyFkM6`2kocl19h=-P8BvpK86io$4Qq)Dh`ZE1jRWlrg{< z4St@7-Z7d^1MB3l$1y&bWQV2{W|bq3Tl_R%mv>UyPR_*6SyU{i$>(^@$Qe1MH_a-B zoy{$F@ZTI+DdvvCxFC-M;8|dIAwyP)G-_kIJCh;LX_#jl-xuJp4uQ=9yg_NP7`(tk zM>WJT8ZqqUN{hy6^#OiGqBWMA3JS6dnetN(gCbxT$=MD*(f&s4c%fdQC7rWAym`PE z+P+1n>xRpW67t=_40dE5eKg{-t_5D(-dV|$LfJmB*mG5JzFRi1bo8_$;m&iMZu-4W zY#EuJz0J#PeVAH%T1QL$?M@M#$}zq@WKLvhbAb!KX`mAO%Nx{w&O6oj~E&1Mk_Q@`E1ueVKQn78r(_zZUFKsVN6NSgY^0K#Bz z;FCJiJq*1VKD1K)zS7CM9}M&Dw>_Z|um=((M$RrEUC&i?U61xn=!_--`HEu0QNcmd z;_sqgw7KOz$B*}jY|M{_@3fvXE%V2Ojg)}38xqd*n_VH^&iG^{a~kWF@M-Y2xHe8-EQhWXD+eQxbl-Pv>wF|J~)m_1|4wuJ*#<8~gZOW51nmDH$j2 zV)u9bmN^$mmA^8zcJ`S7{T^jS+OYjl_hcZQ^>z-pXYw6MGE%j*wbW5ZXoz2M#rmS4 zjp^nNY2n$C#%d8t><b!U7mZAbY zt8JGr`K+c9t$kC~LvRh=*0SwSGx0zewC?6IlKkh-&2vv)Ab`YVD=>p_qyc+3kL46Y zb~puh5ZJoKf~NqKy*5^RRXw{p)+l^ND|iFiuzjdU3;*tq6o7t+B-evwrqP9!m39z> z@PTqAujt!}tCiI5Qy1|(LxlomX3!E3+5nk=LaHILCOgQKZm|sC0PI_=04OMyAa=$* z4tg}7r>DEz_~HB3`)YCcMPdK(Ar)&J1C0$$o6F8@T93icMsN6sojp^{cobDzd zEi@C4bDr|zLjn@urfe2MJSo>Nbf(0t?Cyd4eUH4BFgpHzw({|3`sq6kBBiTugTFy5 zz9mJO^b>qfjlcJ1erXGErryoBrlPS`+O>FX%29XP7XLwT}hY)GDIhvWr8OrCV<=%|=T6F5+5-l!^dh-6L4h;AJ~MghtS7#tL4FAug* z=Y;F^6%?H0#*j_VJ0`F7@#z?~+Fx*|B=|@qswk*U;(8egsYzDb?zy8!=P#D(qa~5A z`OvRDS?RP~)4W-VuBoPyf;lnnt?(FFZAcvzMdl586^W=5-#~{c%LqW6*3|NKdU*Ap zb!&CR^bU9G6@j9(z!w#!vOnRXVEHjXb-)RNkgiyQ5Ud!n}6QpuaHS4HoHN-J|yzki7x$_Ljwg z%QHO-HB=u!=z)lu{q?1*uPY}sMMPBp`7E=x#qhC2IH|8Xd^rd1f20>d#+`3}5VP+a zn)~NHD2i#1)Jv&7Qm-5hU7~Wha;rVtJvlM%nRMKosSdxnSTC>}x;Q&yck5OK=InqR z?tcRB+#RS0-_28t6q{xF4Ct*&w)P8Nn2oK#9}E&L)rw<2)?e)9b-Xjxj=muMipC8}GXfe&xrw}DZ3X09M+3MMkjoS75#OT07_fO}ig4N+# z&X^dYmX=kh^6B3IYSPy(Y!hk|o0nU>f(4%pMvYV1=NpQKjJ@_~sXb1g0r@~07HC+_ zxC;QX|I9SwJVnLxN8>;*aW&ipU{`0vgF_s*h!l?n>T7wq{eOnD<79SWV?$aS20hx~ z)!i6P?T`6zk}B++G6`zet?*7?qO5<=AW#Zjcz;Wge8w1EsbSe~R$FR|9*)9$`eAzd5)(JN4Iz9 zOS(g62P+|0BeYws{N7dk{FVUc|O|Y zy0&Ms|G52!cmXXhGm$wH#;eU}xT6x0ljd0iPW*rhTeV}8SGU}&- zD$dB0KAOG#V#95nCk4${U6L$y1=1_$@_#?%(17>U?0ZsIs6Pgb*Mxe{2h~P*-`AA* z0j})%AvX6EuCvo!=kQn01lX~>&);?l2T;?6FFO^vIHh>g3CA)8yZ?y&d9nHv5Myof z#|ZnvG6JVhLKT)iCWX4+^%)C&dNlfk>x|FEY1pwZ*};;&+Uu%h7-_#G&ZR?8eFQ1b|7vC`FKxe@%9VNRBx@an~n=fEV3O!=4CNR(vx! zGHVM?H`eAEC|vyz!fCD*yyqimXd`x`@YNS1E>3q%)7NKHY5nmPT4Mgu(b_%-UJY{B z)Uy^XZVE9kU;~A8jKNsTne^}#?VE2-LjXZop?*pUkHwPJp2A$CuMI>-+Uu8ts{G2o zDeV=4lKs+Y_i8aKhOSUG~*Z}w1|m%{N68l>AIsh?o+Wfh!* zGh-W5x&*!~-x=yVeQlTz(-vDF{85Cnk!~3`#ZYu0ZhaXPZ7vJor4g^sAJsybyXMua z%mM-E1@tzQWOhIVa)p7FdBWNDm~U=%^*t~=2j1u0XJ=W?3s%;~@@@%#YomaOUx0fD z_2KxG02?SM1432IWTnFsa&mIzo71~2EG)NFnPCUe>hS#edIQx1yMwsnzX8CDq_q1; zn4wa=XHMkq5i<~qq&ai?E1JM--HQS&UQngKEi6~6=!`hq);k`=1faH82Qpsvn~(^a z;Ht$V&J93uDyc3F>olBH038DZ;~aXD1j8C{`TEYt>+C*!s$v3CRTfKOYw)fGBGT3UH4H*m?^HolCiB>>yL2%zwVOHTgVzWxH2 zl2R6QpPkxnsb|C#|J~{VT~03c^Y4J3ff%5%fg<`6Ai>{vf(#H8FkyHwhbilh-s$ex>_e%sO{1<@{HhB}Wz^VE5dX(W{P9XYOcnMuQy>pmMk&VGYS0eIM~XRZq&se#%LJ zX+G|98WBrXY|`ZFHCF{NB&5TPwY|HcbMi~xyMaQLw7A8h|K$RRQ(yX(bz#cXxLfe8 zELSXcZ??Uoj0?z%rU6^F-qNzP!(944rg&D}2=V8f4H%g%7MLaP>pky8`!RzPquCto z+T`kvWVy+&-6Nv(bu{0$`%N4BBJP|Ivw5~Qp7 zRc@rS#6$}pax&8Wvg@7Am6k_8zteff#B$FYO#Q*_r2D@R`K$m#u7v3=F=K>a?n42*;ldFvPFna%&d@9j0A~2n zq(-B10MKdkJEN>U+)_OMl`2ukOVFkI+AV!#72ye=T(0EUAd8OY>lnLvadd18mVw-X ztz?datd5?iLi2_5I^x}x94BbMio}^@l(I7=wEIWyuCD*cH1C}AEnV?7NbZfx?SB7Q z7>;v&C+Ix;eQ1vGub-p%pT%{aeO@3DJDDKNlMdFQKIxKd$f)UEz!VN)K=d+2mKv%h zx$E_FFK^GdZ^k&*M#Fl1(NE8PrM+nvIxTZwjG2z6Uz z0D8{9hliDB@sJ#qCIff8fyLgMZy}%*Jbm$^W!2H+`s`I0rBL?G=}%8R;$hI+A}ghL zvIg)W8UcC?V9KJzNC@=sZ_y=C&|e39^U_iTQOm6kF^Cj{iCGvB?`G?Od?mN_PYP5< zrU%GT3B0MjRAsG-imtUADi*g(GSVHU7*O>U{|!$PB{6RcQ@KiXtKtYGFDffu%I{WN zFyp0wu45y)1}byZzTDPomkSUr(#20sBoX`Myw!5eonpK!MS+2xy3-#anC=U0ysrZN ze2Wq%(BFEo#Ti?zn6WW4UHi{FJLxmj=COmIJ~$rEiCsDQg9zr|y((>DH)vZp6SkSj z25z2n-Dj(Jo~I4FY=yPcc0A#tiW`Nc11OT=xS zLF&my^nPnFa#t)oR?FX|a_*XBN1~Kv)W(e|X(M>fMnaHUQ(&++fk@}z<~7j+z6=dZ z)>a8+gi0b-h1o!VH*JXP8!e@+KB~+0 zvsdV!X)fhAd`afZRje9Y<4W2Ai6bUgqYA?0f zAKsEbGNP@r>(=?1eJ`GYq%-j zbniuj7NR?l>}8&yC0S;N#)I(m12ix_n|$lpeO1LuFf!9 zR!Zh$2$M$3Xm3%At-o;M6(210lBB;_2NZm2+>RLW+d?w{xD8aN!f0HG|(g>470Vx#zr{o$iz2y;M2#m+1~Nm zocJBMK!0krrw=&Y%5Dvu9MNN8{A$iyvBXJS-0;)N1?5j;OSXHqIR*v>dB&f4`^SnH zgq@8hfIc@KsQVvbe}T5^x2}}i#Y)k=yH`6`o@^q<7zc!S3*b5D>e`l-gRJR zM%y296p7mp6PaJ49NrYd%t2^@I$n^UFUHHC?T7GW&K+%$$XLJ5X|n2N-=i;opZ7ja z8Fj4BANk>Sk*S}eR{FFczS8PdBeEItM4dUp{U$uub9Qcc?j^F$>nmJE5fm>aTT)p0 z_3kdV4n(g{MIs%2Q6l`;~!52ydGWi);cn}#acG=xU-gdLA44X zE((=PxE^jjnNNl5K2j$ILq*vt9h78vt3RbAf=PMiH=~|7K?2)N*VXsoH(^~q-X0$@ z2~??m8H6Lts1!?72i;b=#d8Wu~otEv;*zMiiTn&%_D#j&w|4 zubC9sBVcX^g#EAvCEkhc4aG1ck481lO+ow)I^QFmv!iB3Rg!=BSX1h>auu(S0K6wv9_(Kt0Vgw+tvee^Q3B`IxkXp*P<2`ORbt4VP!9UWhY4FD4S zA5-SvBrA*Zjv5Y_NeRnCfU3U@!?QG%_47yVHq2DJuNtsHlMTJZ5+531x~7p}D8uER zJ=+rumLj>!cNnvs5f9)89t+hzd11@H@y01j@Qz5e{VTl5D$@+>C3Q6lN-#+k|GHE^ z+(TRxXYV4IP|Z`VS(^v-FC+s4{ic*+BFiJ~B|D^}bkdN;nM5(|fWSC@)o$f`7#J9M z&SCFh#}S9~Jt;~vj&#yFDxvqre?Z85fqDoEf%-xXa?KUgWGrku9@g|BzusOe~&b^C*w2&A7*#7#jxqh7$fP4K#dZn#-`82coItMVxc1cg)Gi zKeFL8OxNg)oJ`%8T9WmSs(Y+V4JBfRdK*whrr*TXlg%;Ntr}+81XZ1tfa;_U>PT)w zG`(ceCsBzAA~`ER14*JQIZT1DECX&(XL*$b!-ar82LPQt=j8eJP00Q|@{m)FeT<3V z7efJ(%D=N4Z-Z(# zDg;fJIuHR-9UHsC-}$k~$-cEVD>>{5V>VQNmZIYoN!M}}i;^i1ZvXx1ZB~4^^4t9* zVNe8E@#}JpK{T5F&H1Asp`Xr?as2BnZh}!qJ8^2wCtYkM(EcjtP>Zsl+tMaCV`H74 zMv9N;v#ogQZ=8ts;bt7HP4g<{iW@p86s1d-+ai9FDs3Th4@S`bYw%C!j9vs!sX zhtM@`n_;?FJm#3JIsAxgN&Kn0EZV45v&5FiZEfu?Q00)s^SUt zR2)z!1xVn_7=RJJ#}vNr?JIBI%9qAbEpDi+RK4M&&Fkfsn3&j}(b4m(eBm{Pnb1#p z9a6`at3~!J{xfJZuUo{%ToqL2YfTX`D4vl^c7-+34Mt3XO-$j6yaxtdWi@}^Ywg_S z&Yu;ivaUbNZg5{r_Y$_85H%7@oo!K3QP8%JJcY1a zR}3=&Gam+4e9Vujx@ETzS+@aolo5l*gNdg$&ZqH@y>u9{sbvl4X z1gUW?SY$T>j$Xw#&gX|~7@dlyp#B^HN-LC)>abblHT8Cj6=)2lZ$RS-BWxDo2u#cp z8Si9SSy^%IpQvOCfIe_26sq~!>x;0{j`C%l?b`{B_aH9?mK`hQ@tmL|BuD)5QUGK0 zPsJSIr&n7TYv zpA3<|i^L`-TGjs7t#Qf)OT(cLf}EF=Ytj%?L%(LZeii!b~l35(s)EVWu=>F|$1&)o|~n`Z#Y z|09RhilX-h8h5`I0{<===L3|+XVv%ScdZriZSn{RZ6Du;>;GL1tgRQY%^Ab>f|;vL zpg&!T+4jq@mzks9$8 zl-39qGh;}QuI<)9jGW6D)a zdhTriKd3zb6{!vghNID&=H2}@A_>6Q(B20~Kwv*ATUg`4P+&$QFcg9G@{wcHbd zKU-2qLi03?cdKtvLAL|k1Rp`oONTE-7j3AMOZ*ORGfdz82%^DVl3y}!;t$t0@qlpNFqn%)^o zb?0%L>^s0IXil!ESztoDfOkbd+Zz{`Te5d35t%A`IItt#BYjyb3u1rox9}=Z55LdL z6UM@AsV$DD@C7q(@6a8wkr%Zc7W#MKm>abE%M|ek9oSDrJh7Lpo<+d{&X=g^_`-;X zMRok8h7Gwp6bvTa`L45z6GcH?!@MZNAIaZ{Al6tS;kV$O?lYH(-$5C~;R53n`|blV`JGgmqoP-kUqlL5$r=j_Cpb}>i8N`q#v!3)aMZ>3gC?e11(u~)qcuCw`j z2e2|LOotM*>KqIrs9)RsdU{LRKD&R0K10WAjY6i{r%(8Mh5)px_-Km6Z-i_9w@jG|7N8 z5mf`9FSECcdkwqOxINyn$=djNI*$F^707>_9|0qnL7>YECVmfCs0bJ4u0O%9P09Ok zU|+<78x4-~fgAj{G+4dcG}1A$hn$x*v(a?{9Z2{X0`E7?Mp!gBK|iS8rkIS$MOcBC zA5yb<-^Xk;oRXi~-OKAZU=<9nQk5P6;^h; z#9B_R&RRn#`JHt88aye+tEtU3$dO3ogNNA5$uAXaoVLiAR0?%Lx0A=o)rm~4Yvkb} za7@eV0YIhA!OcY7PA)?cG-Lq>^A6)v#-O13lSgD4`mF3#B+^`?EqLyd>>cD8bnG*D z?yR6p;m+z^|NMbSi4TFo`u_D7tFk7aAUxjLKE9;LWKaR;Ef@H{P=12yimh}TVXQQn z__)DSIF^!J_o>ju7I>#9!(a_$yjIhS8*w-7)cIOA!~k!)skU|>?2Pfr%;8O9W=EmzRqXzv=g6gcqr|S)D>qA)eGOzGA6pa zXZ2~n3w&sc@Deb*Nu%^)l~EY${nN>x{QNSUVXw)dHp0uTBMz6IX~Q81Y1cn|ea%d* zHN&SK82GSLd(E>}c{^7_8pL=6U>u-1cmCfv$ZyS#qkrKvqJTgQ{*^5Ho zGk2LV2;5Jy3k!$Vdi^Z3=eB;<)Kxo}iQWa5{Il+hwv>!aRHY^QOud_^it}Z@BK~^f z=;~En?i?oqW`o$|T=^v0EuM%AAe_}Jm9|J6*RquR0BeizgnMryFlxY=Z4Wgwc=+hYRPXpd#`JbvD( zotKOk`VfJzueen9`jLK-GwPm;w^|o5RXfQsnr@Q%* z$sDTkXN_A*$)~KYT1C{OLQdXP9}z2W&wE-C6_>lYO&L}0hBLu$sM4RZ_h|ZfEb5nt zds~_y)M}B6W`uKyu%}RRzD4o_XNu61;^J8{iW@d@7 z?!o(3R4wDhtNR3=Q}Squ66<|%;kdBCP~^{p@=3<^Ni{YX%FPUD0tkBga;KBZYOWe=x{8*_oU zxY=Yyo~ipldaH~ljCb9gkXcVc?*t zrXl1eJGB}|a&&fTAolK^$#S>aqX$?@u~u3~XE~|yl?TVi%Co5YPbIp~WaGFbfgQvc z(1+%>PB}Xt1%07l5rjKTw5#|AU=8fx)0zCCPtDc+A{rDbeXg}WKc7ywgUon{PjA*z zP2;eoUNYG@tG&0^ca!zKJ^rUA!OLZR%sqNQ2X%GakS4jy4jtGmsW_d$og z?kz;4P})0{rp#j$6O~po{M^=)GAHTjRL*XZKo-wrd`zcnY)mDqD@qxVkq$>xbUL~? z&t%XY*GEV87TO6ZDX*r-9^bBWH$2K=@mlz)fN`~J_JVPMftDoHuK438dSnWeY){RT zb7SDBbmB*rsTMXkwfETjGpXn$*TwIJkI`x2 zT(KS<@x;-6TOH+zLJw)Y(dLBd=#D#R?$&$wQjuRveY_OTmb;fSK{Gmg*Sn$wX#G$M z*(u${z>(Yd2+8~c`asrzw1#@=tEHi-yWsG@HoUoS4`Q_O^LJ+gcL7%4lV;LTaX$&M zmw#VAN~5KwdLx7a2X+=})j-vr}!w zHYq{PPEIKj4A+-+w_Mm?99Y|9M8_11|A@Jzm{IWBXrEK-a__-T!%3f_v}R|2)%u z_P`tMe?9#r`u~{|?ouCg`p9(vu>fqD>#h#K!RccLQn6JW$z4%6m-CEBOOh zZNPyWM90vSG!zD#EU}G5nGc>k_SGI>lu+4Bo-ciqz=j_*J9KGb`imzR}Zh6fx-}7^Z zYG9T+MV#pzmVl%_ERADm=37(qABIJaDEvWbbVW zLHm=7W${SBT1$JohCW6!^i8N*)M@9fK(Hoj3Zdr5xMhQ#_r1k6#7c4&vc5z-2lda7 z{A14guoK(Eq_hTQBkMac3yTW^2q?Z}{U2J1R@lu-rV{R}UeHM=LFuwVg_CZ%ErmkC z1DnD{topevV0^513rrj;!DsX|3eSv!Qb)&Hh3 z97$e|VEt)HDKL3+wL3pEMcO$NYGCIkW{C=@ua6*em$S8~c(wAWp)1J=3J6;(g93wQ zclp(lEUAkFI`gt_pFe3NPRuUmK*lZ~+Ct{ilcXF;Gd;Ey%y;`(&*?MOsXr^3qeZ<> zZ;JE=_@2v%4E(Zq(PvTH^{FTRrKfA@X9UBy+it7~4>=NTf zb7BNqm0eZidEVY4ZD|XCUN~gP#g(xZ#(uSoAM=Bup}pNh$=Z0C;WSD^L&1xqG4Gog zs_6#|vMF}){M9;qjba79-N-fzYV64YS9|+At5H6NHY=(*J}u`F1uJBU8Uv@CA3D|Y zKVWW6u5KQPpQ&7Usc+@zzntN{->=nnP%xhHAZx9UX>Rm1d-)T`@)cu26;m4$ekI5@ zHn*qCid-RT+oe|^Dfvg&&H3zVkAEid0r@o8rrR$APm&*Q21c_p7h?lP20?ujOZY$(b9-Vt#9k}%Md>57AdYZwjU^7A@UnX zDD_DFf#L%xTXi*)dXRdAMfW@9r-m$0Bybs&YxE^gqoJ!>e38nnTNfgERV;U@i$ooj zy}X#uKXY8Vd#7}@oBxHn9{u34jh)YLl9gN&96Dd;L)CshFe48CwBtHA%bLR*&wfpY z?rqS4qS%8UNdyH)1A>PV&|l5QQA~^Lgzak}xS1^Nm6@5jgukPOqQcF9=U}pSnfNak zpdT%$kyaOdkJsTM49($Dm7Aqs-?y1GU?Px~8T#Y%!VJs2J)>i~rR)y#v^QL}KF(FY zjxQ1JbRIC<`#sG4poQXPL+bVYabnj`ksZ5Y*T&5lxku$SX5lD^s0(VZXm~e#j)Tir z9lYrCu5~ZG!Ajg5(&~TK(;xry2_b|xw%25bI|R#fYwF^fj*k6((X#_`wQb-X?_6Ts z{Tg~GzHaG4ndIQbp3~wcCPMlzhaw34V$+#Oj<4y`hlfi4kP+lQanft@^AXCCe`fQx z7Fhg;C&{Hg7#el{0>bost5`$BD2mW<^kCQvwXDFch5R40bg^SWI@D!KtbnL#^_|fR zd@6#gpPq9fS3ev;^nwC08oTecsF`1bNqO!MdvrlWimZDarm6~hzOA;fU9n=+msOP$ z!NbA9J~F7D6MzmQdcK-3&TGHQ^#gL)v9lzG=;}K@xaX)W>lr2l1~Y_@rhr=i?28p5~w-M+mC+%t+hCk>!k|vyvD6Q_t4cy1sZs)s7Q* zk~Umar+UF)Axz#|*g*dm{H~qTcFya=2CTqMesVn~GUU94;uWo=2vR~N<2Qu2@XFJr zgMsbak8G~|GXvS@a~~dj$o$RK)%C!^14(qvROQ8I&1aOIl-r=4ga*hTfDdvWA#D8S zk#?$r{b7`#kfh`_PQD-OyT@IuGlcfyv%zm*xpZP;WIi(E!_E@BCjOlWtRE3CV&-!6jw1$hfcUqZk9Pt-*e>Ze_5C`>t6cH08-_n4i}{@TP?qJq^A11d_ib-Ca*)A zmhzbdfO`3 z7A~h#^zLPzSvS(_+w_TeemLHiAt!+7`ZO;xH^lYCVkqY*`FSfz$hp%66U-N}GR*84 zU^$4aOZ>!lPq4*Fx&RtbI}Cf66Br+Dp~$_%@&b1x8vVfz(KhuhGZv#QsJI09i?^?D zaIwO<-_OOmYiMd}o_eQz67=c(%_&Q)W`Rozgp#pPeYQ&0SMuyTj2iN_D(&oD+ zTyf0BU(}_~x8(m}c#|=kRHU^>zlGAakKaftMRuk3EsgT7?+3h(pI3}$H@n-#?wyVN zq>icLV2zUnEX3@VhqjVU6#ax>kDc7XBo-}72`#LRPS=9l{r!4i^6&A!n#Af#a zKCJLw+?#O=I)**hzG@S(7zc)_;PK3VEKGY9$@s^pgE=~zD@s!Av+lhqDA`|6L>`vM znZ9u(wbtBrpUh|0L?B-YE?X6}oc7m=zGwe7VYyA!yT!uN19$M^!kQ%n7<3d1f zVEqWd0hs$r@z31FgFI&BK}S8!yL#6AUxWS#;6aI+Va}*8>)i$v#xPUDk_Fb(JP0u% zXB%JB%wDn#S>0v-XRz|031#eK%l|MyEo(~7Tu62cNbKTyYut~7eAR1k)Docj4>w`7qK6n z9MM2SHssQwTem*lXm)C!>U2$p>;!JON>FAE5My!gRC zovhP`lR{_Tu#e&c7%MnW8q}ovdhThRnwY# zIct;cC9Db(iL+myHCxo**G2bbIf`S=>Wr#9Dd@z+0iXUmMRz5T3nM0p;ugKQ`GHAD zt>$W_%zjoOi8JazMZHk9@#)+EfdJj5GV<#DHwh&KBVXT&e697VPDA{)fK&yfDRqgW zKU%+IQ0XQgqIj=bzop%^#typPqu(UbUa{-Rs){-OP){ z#qt%%UB6WM5H+n)kJp*Iu@a74t=0^TittR_2l3^e)aS#7MMVBuFW!544R72exXlp@^}vKU=Vhf~lZJ+#PJys9x0Tnvk=J0<67m*K?*MK7Z7@YDjIe5WWGAH z0Wq3K-$mriXH}^DH2BIKiN612ObN?q#j z#b?mnnS5;n+dPwby@v9m+n0q}BI|Pigw%8|DPo!)NRHH7?Lu8tn26AhDvU28+_0?& zRk7l$$CQnqb*NQ_`pvCH>=PdKD$ci12zT?obVsXZ{;1wa!{wXXO9S!!*W+Go zPvkf8M;x!oe22}7D`~AdtW<}vN#x@%V-nL-2!`ecf33-J35B_gd&)WR+ibHTn!1_x?ALFaeVsn*3UgebADv$$t&=|NCg21iSCu zBUZ?;WwMSza-CXidRenJcKH&v{hu|!hG50;cPnDYN7d(l{rAO^pY{LvH_iii<^Kn8 z!CS=q4*=w71CB(j{SojJajy^aPNs=}kBH?DfO~95f8`Ididu^o_VE<`d0B+-TX_tv z8sq$2{K@xevaWgc1NXlp$j0{H&jsA!|GNJ7ASVx{C7f2Qzq^gTvqOexX}^WL<%D!3=M_Hwp1Dz#FrTV2C%bLXdn1 zVt241X$5o!CBc38B-|LjtG>eLs4XY`AT<60Ym{wmg1Hbw;{xna`hQ+LZ)8vx+Yg_p&#{H-muEU!kt`p z6FJux&_9Xa0wt;Z2nW)pJ&sr)Fg2=`U4*Ob|jkVQ!tQM3@QnEl-*?!ozZ&mnhk4xD+>UR9EH-v3bADz^4rIs zSf|FpFjWY51_m1OB&dAsElO)vvaRu+G5{G;ILBfR8h&3c51HJsB>qTdHSAP7x~ql; z=0k>#%Fc~MaVuHZ^3X*E8TZ~BG6lxQ9MNs8V@r}H5S)%m$jZ_;jiZ|aD*O_M!{K>k zgs~d$HNJiQDpoa8!@Zs?{*% zxg;I>W+ZGUzs%&(cee`7p+C4)RdHLqP?o=0W#6{fxMf=as5n}w6PUn@kk%bnJ7!pG zhuEASHugVvjy1ZQOT-IWa{(h#VEI*$v@iS1#g)JRG`9LntT=H1ble8|!Bd8&dRp>6 zQwEr>)ZW_cfu`jCLgf6GXiZ(+JjDo`Hn@eYa&d+ZS_%(lV#IbWpx0ISCPX7i2bMtb z#TeGioSaie!20z32D!ZU!7Q2u0E0$>Gt3h-g?Ssiw^sqE4=$K$QSOu79is|bobi~d zMAqxJ17gIDu?Naz3kyL6K2bace;Z0QFmQ;?K{#Y?ZY~|PWNw&uM-{C}oH|bbb_AjK z^7ot4fg7h62XXq!c)UXBTDC)1a!%&W=jv!-gS-qwSEKgPE^TOaBFD=lnc*gqDTaTl zs;)JB^E4vvE?2Rh7aq`I2o6vwQPcVx0R7FQqoZX#hKh7lb~O)%*)zqRA#i{O%wm)p zpIekVRCbWAZf*!jmd0Z+m{}w^$`x)qhXVj&DjVZ)A1=L43EI#kIYP%nX=rrFdVTN7 z5z~4*=d#%+si{(~I}!1$ak&~D|q|{Jg*|+TBjGiqB6YQ#Pji3)faw9?VItRXDn}_2nnaq44C2ky$Cb z&z^)x(o5UsiScO+pq;bpZ?1Qyg@+S7UHc2LU^Hev=IgV-wNql2m;C*yiT64G-WP&z z7f|l|+3v>eQkR^mOBQJK4an6bj`&RIMsT zF3Sg-v_YOu7j#wh`gWr~Pxty)8=JNmiL3&{l3W1uTp&l&V(l(E`!yLu5c0$a=Z;Tx zrDBY*W+JrE3fUqvjJdqe-0(79zlvQ&b5ex4yt1}o08ZsqVPn!`M65yeeVcMR7w?%3 z$3GsUFpc^3-pR5SrZcBmwI6q2wqUTb#!g;rLwva`PA0b^K``kHPp zOsuTfcTFdS;|4&#ZxWU#b~H%m9D&DqD7{lK1sbDd&B+wBH#gscMoWoxb#+r+DVSb+ zbq%zeJJI?5hXXL5F-H%|%gfJESH}$(R9AL9%k5TwN1)5g%SXJYLyMKUWnJ>dnxl=$ z-p~TtJ}2XlzJkgu7ot^aOUGxrOFzZW9ZB`bCKZ|HKv&*8PeN$&WoNyzO8s1I-LR*} zkkehf_P+P;bSBHNegJuxtTZ44r!QRX4&QO@l}n(wD~JHcP-|SBK?fN(7f;U;Fd=!i z(_O-*6?q4bb7%9Tw!gk?zGPKu&LijX8GfoD0{Z9mXA9{YWI1%&xsRXtw&^$CR2#fg zc95MbQ#I^Rp>I-eva)*tAkaxC#jqAx^*d{=6p97v;E#FmT?MAt0?;w(pIEHo;NvR> z(Hxhq`T*KYRHzx~ z%JVQ8I&`NkY|r%<2+g?YWu(Zs8u4y!=mGr8nvI~Xg@mx{YBDY(cOk1Mr#LE8=NjR1 zNw4THXrS!7vu2NEu3Z1r+>B|%5srJS{O9-mKkQE*^6R6HiAW3F=#fC!ELDx6N=Ptyt41N7>b?x|%{{1{HD69HQn@mz!N9p!n1kD)Q%YGtIlw@I z2t~hzJAgBK*VLHo8N3rGg4fYsF}m2i=66KfH?$!- zuDw$Wgoh2?++O3EOJ@3YsHX(I-i>AdrID8cP&=rur+44kHANe}(ZqwWSt%oE5gXix zWf78*L(t(XQ^0xrY2@MK+O!|KKy=%8wl@c3sbMN{SCGyJtxZwIgtnvs|ftI@aSf$pMpC4_mu;e5sXG)P4K;kl}_doEiuNnMd zodKkx%|V98Xst7CIcE4QI{WeCA1SpV?G;MOXW@)}ztp&StIluU!StLIBtPIrWtEji z;QMhBJ~Q7!N;}VnPMaI5Dc4R{z(QBDLgu?ab(D%(VEkpTOz4y?J|@Co9Y;}T#82&+ zs=PY5_XhCHLSri5+S;1lvtI=_8g*3K+J`mhG%D)Ndj0Wh*U{GoM{3d;J4?N|LK0Yc zP^n_2;?AkPxIDa^Ad)D+LP$$Xy>u4lhj4^zU(Ko*fnxjzv&sA~M$;5Rl4niK@%M5B zgyzGS;$P<__?atQ{*P)4dTa0p8=-v+I z*4Ad8Q1FRg)9MpkKZfZN+7rb<-76(_k^QHi5VJX7eu7Nn1r;7p{lx2Cg@0l`nef1c?9bl`^7!VR@z3@OwM3(iXBeZ~HHkdd}Sz#TJ)(7 z=5_Q2V|TH0t6_&^CVBekP_WC&6${-%@<9POQDaT zepdKO?h9VmG7K0m5wdl6C+2%J)S|OvV(uXH+{|AK4F+EM)eJ3cycGinXmGIDY>Fqo z2cSJ)2ng)H-t7pp9%yataodF>jDz>YIe_*Xba6vgGIU$Y4D5lF+~6&O_s#1nyXKr? z<}nSaso=I+&d`{P&>;EE_V$BG@jKARl-b~X*g?qIw|+QG=8szth~vdefpeJB$s!%Dn;oz2yVv<0Oo^0 zyA0%?OF2Yrore1hYQzWU`ZOE{MR5Ie=}!pD+Hu_R z(xbh&y2KR}2You#B?%=-lJy_rGh{Z??>?| zsR0378Y?7%SeAAbrnbe$D+T%0)Yi6HU%m*&mcUrkFpYQrMUArwYIZ3RR`WIRT0OGxB%{!n6BqbF)sU&t-v>NFh1JA|{v^g4F@CMPFnQTsqQ z{U{gOWtt`)Fprh%FCS>G^;&;ge85Wo>$N8n5DtOK621HO553;Jrgth#16VFN8{$C- zVl2{{qmoHaMfI}tnxc3M-@d)vA3U!`Y2b0LR4eHYb1b)he{Bfo7@fI%%e5`8)wDbi z2i1}V$uh2U)St$2w!?n$!uqBGe2Sl-T3c*W&qfp8C@X7SpMLu6@Gd2tF4mEv{?Xh-9fCzleEm>h2Z85|iuL;Y!^n z>Kw?47MsbIm-^&#`vLT)=hFU5=Ay4utYDuR=IiA^5Ty+iDPhmi`UpJA3Ia#XMYGKg z2p@h^R8-_>Nw?aXjs36&$5|VZNbH+>_a}W$#rh%3ZH7pL!*{cQTiPk3?&V9N!++BG zUqpotNv%~ox_w;vrIlumbIA#$&SD`lcW$r5DtryO*6(Uy3}<%*&@dNA>D#l^zjtri zQ`YX4)+MO!Z&>eA%pG7odnD?`XtN6x7e+%79jvG(gLs|D@2i{P?w zEBd*WEkZ#mX^B!d0UnI@)kt$$U%Z?u0a+H%qr|uXL3?Vz?UJMwMC8h%}pNZJ5|xjXvlk-*7;!rsdJky z9(vq7Qh^o9PGhPma_)LgxBrSbC0gJnMrIM}AeDtchn|s92kNTuowpgv3!@f0M^4>& zKGn(b%P$_J0a!qrUL}@8RsJOlZi{h>{vMS20DU}a_XjQYAGdc4?Vq1AEe)>u^~8z( zxdxK3d0x!D@q3g0lM&h3&SY4VneZ#YWvCZ6DT^~IG0!(CcgX3A3{ZtQ#2BLJlYCrn zpxafRe~Be+Ay%hz=*-pocdU-nuYno614&(j5=$i@cp=uOyLEcx!ED@)4sGHSR8TNo z9KsO+B!IZwreW7o1jtrer&TQJw5^aWp1)Vairja$8L{?1Z11i=QfoHV{Lu^O`<|jF{tHg`ce>`?y2yWpQnQVOW2owLgG{{HGSPsS@6DI&m9a2+S(hWi7-4bYp%`;AAA1S61q(g}-pNICu#%*1l!9 zQvI)POKq_=k0l*j&YHRIV)zo}Lynu6<-`J$C+JXEFS%jq+~VIO%vP_?!ox6S<>rvCR&u@wEmn(duFc_Xp9aX>FG`~erNgtAp_7O5{A5%w9SSQ|;Jvf|bF-kDSMDfbld{}n2P%F52 zC*$E%N17-`Q=66c0fMe>LlCNnA9dQ^g$SYsHFd_lyNik~)|a7A88c}MjOFc4GihSA z#CUJ!9xBJY#@qR8<^e!p=gv{Q3j|YZG zEx3Z*chc7KNR1YDG-P0-){;^?MDQ#rFvxcsB0T8%ffx!|UqFQJf@>Wk0LI*CEL8Es z!s{TvMFU7orq7$Mx&b?G^CSJ)vxJ69_eTQx4> zayduJeI|$D&a&<0(|y%mSunc&74Ae+*d23}!srBXvtmT-dDjn*p8?J7Nts>i?}!5P zPuCUwmimBsh68c^fCmp=MJ7zu?~d-fFWsIadSoOCupx5=*L+3Q8N$qP}RA z7rkE_4*_tdeC*5>@aS6V;INtf@WiJzH{SwW57ypmET%{!hfV?<3!uQbkFK&ZXKV=K zH~sUDqUP0qT+lf)@MqXAYmbNka^v8>Hq}`OfY0nqPou)pV$RMw^K%_`RK~R&zBhoM zYS4-ntni)vHTB98HY|K=eqbyVVKiQNl9xYcWhSL9rVBAklvgf`Ox04uT~xX;%U8s{ z5P~eP50k~@F~NK&7{zURFn_s)@}5t^U#fb`%o@R^#SPYWAk!(f&B$8J()?N zbYul|;pxw`_X%O_a4H0>#R<$dva5hZnfFA?8)&*~qMzIOnS>qg*n$wvig&1LU=-Yv zZ}E7TgegNn#wP+g^hYm56#{tSGEj;Ac5htTJ9}rQk{@*@=LOdpNHCRbaP=0l`ZJZ4 zK|5G_dLdrQg z_E&l8rNRuE*7}x~Idm2@hDXnU;!A;XRWaDCqNB4!SH>Pe4W+1X#pUax17!$D+qsf} zat9#bNo^*cDzf|B+@E_J-LyDg@WA#3nc=U#5o?xv4>WYXCgNs4#GTxPs#{C?YV%c# zI=-Tt&l6`X`;-8XHXbOpN{o{}u++gNRS`5G)(LqulX5#ls2r1V?79T8qN$CIO)g$} zcSE0*%}BID8Loc&)L*H^)0re?ChuMC!q_`-_}DQ+XhgrcSalA$Fe7o`;6Vm*;j_-9 zPtUFOY4NzQtgunf0V5xWZiB|Bk@nKu{3g3GXT$olZuw))OZA{>weEORBy3y)D|`$; z&P#d)EH@Y@B;W#_uay*MN6iW65y_GP=m-vxTzMGurcIu?k({ht?bJ;FWNH1o4|-*^ zQj`sgS)o&0{^6gF={6`JCv-oJzr@uauJNf8+-=|hNBix{_F!3K?f)Oqs=rxReD&Yg2OhjVOUkHR$Ro3li-{fCZUo);Ib zw(B@|sICK@&3$0VscM-&H>7{9%IK#7P=K1Lt}_jsQ{*}^uHWa0(dK7y*m)Iv^_-ma ztXXVgA_F9}X9i1cArr<=tTYrccAhR?oFh)#mQw`;q;K&1?asGz!Y(yR9m2^teYOVc zsfwPMpT-3EOt(-PDVNyroJgHT#bE2hK~?UQT&Nx@n5+}U-P?(ZT$kT6*0(2k} zzr7}c=xyT=!MRQ!-(2VcViPY3uz8sbl**IGPn~uv7jtc02MTi3m*O`jjjf!?y_4wd z##>QN^Q6wXzM8C1W=K72fZ*ULgg%4{ejWPg3Q$iMwWz&e-=2WzY0z>%&czFDyeM|V zS-&s0ZNC4DI(|WWj*=i6#{vw%qWU;?&qm4>z~EBu+EH^&Gi0Ho(TM9#I%#OR_Y;#v_$<#{(c#<^n17g+N{`I*sQW>frybCP6QV(Zmk^-RTli|m3DL_BLd9p#szPqZ5MIMM6VE?+{5UUo$2(UjVZN_e5Mf#s8WPc6 z>Ol!4-fo7}NK)~ztc3zX@=2AMoTm)$I2;5}9hR8d&>(D7v@*zmY+FhZq2j)X@E604!5fZ?kYbBJYkSarq`K9u4sy#8Cq>p;Y&Dzlo4HWKjPvVMC|^JDSA-u>Jfdm(L*+J4eA*f%fz)kPDG22FV?Vp7D;*7lrENmhZyyr@>XygqET zKB$v`K;FWz=&q{!aDzB#lG2w^Rt;@yR<2_x6?N$E)$?Lj%2!TU9R>KRCxLPoUE%28 zn;1_jw!rc!1-=rs`MzSw8R&xCDa{V3(rT6CB#YB ziZ(Kb>y|Hy6b7N*@jLu{C;6hNbX}(EzE25%>DnTo(DfJF_oq+DkOYLAHtQrvJQ6Q2 z4vshcq7i3(Yr^#BOD}Vs-2PIF2l>HGkiag_EY;UGLFf@n940x^s6;ba(>pR7z4p_};m5W2gm%Eyu9&0bITh!fL z?fDRm&r~e)+oCw8kN6E+tuK_Hld}21!=>QcB0LQBfNzJ!C)>YOy7k<{4q>}Kba&jn zlxmh7u$~Y?8LHSZO1PBY5_5}R3j}VCD=mC>tDViA?Zs(892k#D3wALSG+udsp`GJG zYY{npX~1MU_P1R7Li_S_j;Jejri4jb6THrx`h4f>nrjV6d)l6Td9hr3c3rBkao(EVqVEqngln$H>S5kAQE_F02o^mQ1JB zEw{ofY$2^apo%yxPsfMzhYFzT3p}Hg3mjQIo&tKu1?|_b_>`hLRxnSeK0=AoWH3oG?ua@mj4Y_5xW=b|;ydz0b=28y9y{ya1>sv4#z>2A73@lOYCiVO;$ zYlY6HKD%Hb%C8vE8+XaF{)!eiiu;9ufsw(Hc_?F#-Caig29S4~V4`_~3WFNYr4*>i z2k!ci{1}Q!ze?XA%1>QTVW<~x7Ov6hg!K7C8{K;c*4L|;4N4#)*!q;mOZ!?4omW6(W<-xgr=)Pg=Flpy9H&ic|RKkHm>ZsJk3ujQlc*0nykhq8jtQNi? zw?ScQh((g1h7n<8b+2ZfSb-qAl{s&cKespQt%Yt15(s&twQIBu$1>!)gvZ)^9g@QTyFYEc()nfD-;7Sh*;N3aim>nr-)TaYq$n8 zCb{KIBr4o{v%N;su6X4v!Ys<#?v%9`-)^m#)A(pCmVbA#MqNXr+?w&|wg3AAA|$%q zd0%H|XYLY#+a6^RL7T{nsfu!D?ltx}Tj)>>T=Pyz@YCbB1AZ!Cq}1EBdeK7?6u z{MKQZ+m*>z`;NXd6%rHc!tXA{4tw(7b@yA$Nwi2y^&64^7ojCXHH=th*uK!KmA8uE(IaGdvjd*9YQFz%Q)kRhzLoyLFQ!bJZ<`YIA2?);B#X#Lvf5E zW#-vnh_>rqUPuzNNLa+|4b7Ke4eR>lGn;>L-auSI%{gOotE+)Z){#G?;`8D0PXVi+ z63g1f6sOmdv@u>WbWNo>W4L2YnjqRvnr3&=u@F_&rTTe zNrn>Z8SkY`%yrX1-eBUPyLQ)9$|bW$0Zoru1LhV9_Orb*?460b10qay(26wX_6x08 z$*QN#xsnTaE&HRO&yTF)!rPG12lPl>T}=E8Es1LvQ3ldn&?b2+>1_nt_7hZ88(NS9 z`icakJ=lBZ!mo3T-?HCh6)i~4uxxM%xfRVFs}{*)uWPb%IHWl;NCL6e_0hjK&ns)8 z*0std#Gm3$Sv;Twhs7ubMmM&L>^&uGrN^5Iud$UQF1#;T1+W=g-)Dd4Jv%Cfw#hTG z6tsl(3kK`D-fs08-FRG4VE@69n9&BEu31ca@^f)UN&ViMi2Qg*`PwpyVAjvjF=h&d z(9C0+!<7;~X`FKG81$-Hs$~>jT}ILmW&0#{)qGoP_wMc1F5Vh>{FCQoPNHQSU(kYO z9p^4bvaGww&#R66jNc~h+kg15pZ)4@&q3zWc^4wpmh0>)aYC^d9G{C;-qzLC#JN-{@K#Y|yQ9t|k@>r{!Fxmt&`WskU^~x6Uqz?-FC{Ax${j~TcT?;4+C&Vy z4ULSZ8mOZh@J4SYJ#3wEh=1afm-tkKNU{02#0YI**n8#dA&hZ}CHdZSQK=BxMlgy% zM}N1Fz!Iu!!jW>;BBIy2io9kA=M?kJ>M!-Zwvtsu@ghA=83ykiXfR$Hnx|oQM=9&n zprtn0E&5xl&E&vg?E0W>qoiNQ;NGGqN@EOfoTHO|1mxJO1zN>XtIy-=jnOF{_!?3% z7LpD%8)X`j_dd1+2Zi(YmQVLU3Q_&w@p|Y%sxh750AeTdy9?!IWQUZ=*WcyZl?&LHapxay3i> z0{jL;ck9>IL;?ao{x#yyq|2`_NO`vkCQfvt@aMJUJm64KxIFBJLt-s_w2sw}cb%cE z&zPgeAMVkYsoC(P@?fOb`c}RM>@Ln}pa!sJuZcQ}GF2nquKj!?-AdH%jX9(y+TN3! zt1d#WbtPXpM65-2*dz9<&av)8#Kc7`JuK{ao9wV#>KJ1S+>1Bid}uH=LM06}(^H{) zx(XGZ>Dr2ZZSgYOyew3DMN5F~pf&brfZgq3>B3+}M|oHA7BqwHHhD8e-J*ELPw&3L!yHw!)U{#Le7F?YBmJqBWDHg~ARV>jL6` zEosqcI+;e5=d=nIsv4yYTxD#J_YaLQ-|esYvbU~BPz^f@w(|+MvALH$ zZKLKl$bScbwtrHhWpJsQ?W@Z+*R-#c`giSidX_cw)nV%Euf|@`={m+l+VYuzki8t) zOzIZKws2VhA)IJ5!C$gRH4P^qB%v8Cm~g!=<=vv!-fgX5QS2Q$Ug^pl+5PhnpJr-i z=4+oV6ZxHq^hrQ-I<{BErz?os-H&#T;4^5+b&j7id}ltN9blhHhk8$kna|B@iLS-i z!{WxdDCWK-6_je%mGO-;zMoZ-By81`UnwhvF}^tu|i4PsTVI! z%S*DWY~L@Q+ifu##8v{_;9-O9ff{v4GK#3;v=FYjlAHY3`PGB+^IIeB>( zVMc@cOI5=_B1qA{lh~NvQ3St@?3libJkC_<9oM<~l7uyz&W;Vl06SVpNT^lOlMqYP zW*V}Ltw3wDy*)W+SWJE*CX5KGNA)sZ_rwSb5hI(e!W^)^*qHfq(jKHa6~|P~hmYX! z7Kx2$Yx5lr>3+d4S8G)0PqweB-)TzjX0z}Zci_2@uU}}pGi93GD=%YZi5uTiEb4Et z(C4)9wE-2QRK?|b5l*YUaB+$Uk|FWnj>2+vakF5b6rmb2Uz9|T5RrxOOno#at8o4;ex|J5gG+HP zT}Vt=JvDQp@Buxzyi$}@Tf+5@E%*(bz9%_L_)(I=!osA@pN$H;xEcJVwSFKSq*J+E zO;Jyu9}P+G?%Vzr%ZnQEavsdHd8$J9egA}DMVh8&rkTl}8&D=H^2j-*q61El@|dX4 zek9hIzLWb%TXj)pvuXnparxJ$^UdimAX#2Ae>QkBba!}Nuf(z;UfEc|ABwxI{3%cy zU2IV~hPOQ>YWX^JV56|}?5W2!KBw%vUPtR2Y&gd#%h@L-%)0iWR=>4f!kQ;XeA2UK zeUEh~mcL|b|4L3=VtkHe!?DAmr|#eUgzjH5Grb6W>9rhJ--T+6p$g|%W2+!hIdjx? zOB6lQ^t6dhfX~Md9my3}kP!oTHFAC1B_U4WXTarJ{MM@VPzLFe`P<7t{=GE#b9LrD z1QcCW7l)UsCt8KtixKwPOkJxKZt?f89~nM#|DCGU+?n|>!O7M`o}#7$joa#rHPikB zn?JK|_pn>2!+3RH9x^^YK1szh6nNb$t!`hw1#|EWqZ$%&Gc$jOL(fv&f$^OVdS@Hb zW_`Be+;F5j<m$^AG9dB&a3GTDmcHbS~~cdCwf}xxTs6vDuwf_;`q9 zb9A%RaF%tsutV2qDV`JGo{L)!T>pOa47Q&h?jxP6Vn6eWvHzf5yJ%HWd|>p|jr+$H zCt_G9TJ7S93{XF`hy51IbdEU=g;w0R3GEyI>rBW*w4@KI!<1{-F*RT+N&ai~_}WS% z`#|7g@(}ak{@!Y@SftY5x+sU^*iKt3`P_)+<67;I+UX3K22;GnI2xCb;9%NXA%K7b z5iOvES{v6C6>9ymyHrB*+xfMZptM|HV0)dPCm}riGEwd|8{2<25I0l}jv}&}B8>y0 z4j$vSdv{E3B2sRwqs1|;wT8ezwzaq6@4Ly3`s#ZS#-q-7ef$N14hvlgW8Ikb_fY?y zn_M!1zULXcV_XVuccbP&sb&nuKj1-241~V=OV#ULEUTWzCog%3Klrv`3OGmhE(pYE zfWICN4<}O!Yqq9GRS{^Y*Y7Y4**%ZYR-TM=2>};08Eu5X+rT4!IP9!*z`8h2eGe#Yb|xy+Tvf(E{Vm(cM;_HG)i^VJrf@zH1(1 z8@So_6h?IAi)xZ&fco((EARSBtUR$L-#L~KH=|i|NWzEgniET`mT0?_S(5tzH`Pk{ zIkNAS#17>q=Op+vZNG%`rU8$opLQ3#xWY5REAJ6#@Ndkw=%ds!e`-V7%6K%X)}c`? z())eDG-lT_$UfS_zq103?gQyP3U##3v<@(RmX|tPxh^(a=`(Ou%G)mD`SbPl^mM(f z^#gsszz zB9A&GE7oUhwh_RgCL+*iP`F#UkZwAk46$OXg+C=pAPH!8mJhx@5kSn7u*Oci&_VP( z0z!}1gs~8RG=wc;t|3d^JW7EE=!rFv;ttk^2huUg)cyN&k@$bGW~P+SOJds_Br4_=cv+Y>08d^-(3sJ$|O7YN2zFx^-TF(Qn{G!9nOiobEvrKw2&o@0auXZ!S7NU&;qd-O;f)pcIDe>-;vPW z0_zG`hU9?hR9ADwX`%cLhZ|YSC;bU+{QVpGDQu=HlYxL-G~E`9kmd0gNVqXg?*|o9jN@0a#2Gg*G72 z3Qjf17m&%mM6LDhSRIFs8Hrg&1IP6q83~V(>iUgdjaeX>@rC-{&W+b>KM$rHpmnP` zZqf}{tASg42-Y?|$;p9%mxV+`+F@=BU8Ec~X5gun2DS)dI?9Y2_y$`{1yi7pUW6j7 zd!Q#~_^BO!yowH4PkAO=_>{aCaRWKz^POvZ>)n`ptKWhFJc|_o*ZQ+@Xvp`$KY3h= zbx1&VrSnh7P_&EJEoeXh(OY0R&%zK`1bTn|n_}XcIlR!n0wy++tQcwzai+19E|>57xyggdL>B?jB_C zV!4R=)2Y308XZi^vVYG~Ylzue9Gd)iLPxX2HoCJuV11n_R(mn}>RQWy_dh@HsX}B5 z&a}4p(7t!x8s!y|`FTZ}RTOH+cPHi{DM(+1%o$MUUe$T)2xswl&-XETTZ{x>{sr%5 zHw=G{Hk9D!Z~C~ADnFT0WuT;(^oQsaMt zE#G&4jjc!m2Kw)}{-5b7{Xd7{f9MeYJvMk{3qP{E1^eG4f;Sp(js&-5+cXIjk-lGd zegmWM_jTYJ Date: Thu, 5 Feb 2026 15:33:41 +0300 Subject: [PATCH 22/27] implement bonus task --- .github/workflows/go-ci.yml | 91 +++++++++++++++++++++++++++++++++ .github/workflows/python-ci.yml | 21 ++++++-- app_go/README.md | 3 ++ app_go/docs/LAB03.md | 28 ++++++++++ app_python/README.md | 12 ++++- 5 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/go-ci.yml create mode 100644 app_go/docs/LAB03.md diff --git a/.github/workflows/go-ci.yml b/.github/workflows/go-ci.yml new file mode 100644 index 0000000000..7e6106ac02 --- /dev/null +++ b/.github/workflows/go-ci.yml @@ -0,0 +1,91 @@ +name: Go CI/CD Pipeline + +on: + push: + branches: [ "master", "lab3" ] + paths: + - 'app_go/**' + - '.github/workflows/go-ci.yml' + pull_request: + branches: [ "master", "lab3" ] + paths: + - 'app_go/**' + - '.github/workflows/go-ci.yml' + +env: + REGISTRY: docker.io + GO_IMAGE_NAME: ${{ github.repository }}-go + +jobs: + ci: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.22.x' + + - name: Cache Go modules + uses: actions/cache@v4 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('app_go/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Download dependencies + working-directory: app_go + run: go mod download + + - name: Lint with golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: v1.59.1 + working-directory: app_go + args: --timeout=2m + + - name: Run unit tests + working-directory: app_go + run: go test ./... -v + + cd: + needs: ci + runs-on: ubuntu-latest + if: > + github.event_name == 'push' && + (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/lab3') + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata for Docker tags + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.GO_IMAGE_NAME }} + tags: | + type=raw,value=latest + type=raw,value={{date 'YYYY.MM'}} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: app_go + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index c81a701d51..05ee2c4125 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -3,9 +3,14 @@ name: Python CI/CD Pipeline on: push: branches: [ "master", "lab3" ] + paths: + - 'app_python/**' + - '.github/workflows/python-ci.yml' pull_request: branches: [ "master", "lab3" ] - + paths: + - 'app_python/**' + - '.github/workflows/python-ci.yml' env: REGISTRY: docker.io IMAGE_NAME: zsalavat/devops-info-service-python @@ -46,10 +51,18 @@ jobs: run: | pylint --disable=R,C,W1203,W1514,W0621,W0611,E0401 app_python/app.py app_python/tests/ - - name: Run unit tests - working-directory: app_python + - name: Run unit tests with coverage run: | - pytest -v --tb=short + pytest -v --tb=short --cov=app_python --cov-report=xml --cov-report=term + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + files: ./coverage.xml + flags: python + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} + - name: Run security scan with pip-audit working-directory: app_python diff --git a/app_go/README.md b/app_go/README.md index 47d73b1896..f46a767603 100644 --- a/app_go/README.md +++ b/app_go/README.md @@ -1,6 +1,9 @@ # DevOps Course Info Service (Go) +![Go CI/CD Pipeline](https://github.com/setterwars/DevOps-Core-Course/actions/workflows/go-ci.yml/badge.svg) + + ## Overview Go implementation of the `app_python` DevOps Info Service. diff --git a/app_go/docs/LAB03.md b/app_go/docs/LAB03.md new file mode 100644 index 0000000000..796f363881 --- /dev/null +++ b/app_go/docs/LAB03.md @@ -0,0 +1,28 @@ +# Lab 3 — Go App CI (Bonus) + +## Workflow Summary +- Location: [.github/workflows/go-ci.yml](../../.github/workflows/go-ci.yml) +- Stages: + - Setup Go 1.22 + - Lint with golangci-lint + - go test ./... -v. + - Build & push Docker image + - latest and CalVer tags + +## Path Filters (Selective CI) +- Path filter created Now then edit files in `app_python` triggered `python-ci.yml` then in `app_go` triggered `go-ci.yml` + +## Versioning Strategy +- Strategy: CalVer (monthly), tags like 2026.02 + latest. +- Rationale: services deploy continuously; date-based tags communicate freshness and cadence. + +## Evidence (Replace with your links) +- Successful workflow run: +- Docker Hub image/tags: + +## Best Practices Applied +- Path filters to scope runs per app. +- Dependency caching for faster builds. +- Job dependency: CD waits for CI to pass. +- Conditional push: only on master/lab3 branches. +- Buildx for reproducible multi-arch-friendly builds. diff --git a/app_python/README.md b/app_python/README.md index cbc06807d7..d40cea9573 100644 --- a/app_python/README.md +++ b/app_python/README.md @@ -1,8 +1,9 @@ # DevOps Course Info Service -## CI/CD pipeline badget +## CI/CD status & coverage ![Python CI/CD Pipeline](https://github.com/setterwars/DevOps-Core-Course/actions/workflows/python-ci.yml/badge.svg) +[![codecov](https://codecov.io/gh/setterwars/DevOps-Core-Course/branch/master/graph/badge.svg)](https://codecov.io/gh/setterwars/DevOps-Core-Course) ## Overview @@ -69,3 +70,12 @@ docker pull zsalavat/devops-info-service-python docker run --rm -p 5000:5000 zsalavat/devops-info-service-python:latest ``` +### Test Running + +For running tests used `pytest` + +for run test use: + +```bash +pytest +``` \ No newline at end of file From bd545f2503db775c032a3b95b2a904f387c0177f Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 15:36:55 +0300 Subject: [PATCH 23/27] implement bonus task --- .github/workflows/go-ci.yml | 2 +- app_go/docs/LAB03.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go-ci.yml b/.github/workflows/go-ci.yml index 7e6106ac02..efdc2ddbc6 100644 --- a/.github/workflows/go-ci.yml +++ b/.github/workflows/go-ci.yml @@ -14,7 +14,7 @@ on: env: REGISTRY: docker.io - GO_IMAGE_NAME: ${{ github.repository }}-go + IMAGE_NAME: zsalavat/devops-info-service-go jobs: ci: diff --git a/app_go/docs/LAB03.md b/app_go/docs/LAB03.md index 796f363881..c84e71a1c5 100644 --- a/app_go/docs/LAB03.md +++ b/app_go/docs/LAB03.md @@ -6,7 +6,7 @@ - Setup Go 1.22 - Lint with golangci-lint - go test ./... -v. - - Build & push Docker image + - Build & push Docker image using BUILDX - latest and CalVer tags ## Path Filters (Selective CI) @@ -17,8 +17,8 @@ - Rationale: services deploy continuously; date-based tags communicate freshness and cadence. ## Evidence (Replace with your links) -- Successful workflow run: -- Docker Hub image/tags: +- Successful workflow run +- Docker Hub image/tags ## Best Practices Applied - Path filters to scope runs per app. From 924736f16dab129e82b8a86a9876fbbeacae4c32 Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 15:41:10 +0300 Subject: [PATCH 24/27] some fix --- .github/workflows/go-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go-ci.yml b/.github/workflows/go-ci.yml index efdc2ddbc6..73cb223eac 100644 --- a/.github/workflows/go-ci.yml +++ b/.github/workflows/go-ci.yml @@ -77,7 +77,7 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ${{ env.GO_IMAGE_NAME }} + images: ${{ env.IMAGE_NAME }} tags: | type=raw,value=latest type=raw,value={{date 'YYYY.MM'}} From 672ee755e4289be1507d1b8074bd06f989452e63 Mon Sep 17 00:00:00 2001 From: Salavat Zaynulin Date: Thu, 5 Feb 2026 15:45:11 +0300 Subject: [PATCH 25/27] completed lab --- app_go/docs/LAB03.md | 10 ++++++++-- app_go/docs/screenshots/worked-ci-cd.png | Bin 0 -> 134262 bytes 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 app_go/docs/screenshots/worked-ci-cd.png diff --git a/app_go/docs/LAB03.md b/app_go/docs/LAB03.md index c84e71a1c5..e46338c000 100644 --- a/app_go/docs/LAB03.md +++ b/app_go/docs/LAB03.md @@ -17,8 +17,10 @@ - Rationale: services deploy continuously; date-based tags communicate freshness and cadence. ## Evidence (Replace with your links) -- Successful workflow run -- Docker Hub image/tags +- Successful workflow run:https://github.com/setterwars/DevOps-Core-Course/actions/runs/21711862898 +- Docker Hub image/tags: https://hub.docker.com/repository/docker/zsalavat/devops-info-service-go/general +![ci-cd-done](screenshots/worked-ci-cd.png) + ## Best Practices Applied - Path filters to scope runs per app. @@ -26,3 +28,7 @@ - Job dependency: CD waits for CI to pass. - Conditional push: only on master/lab3 branches. - Buildx for reproducible multi-arch-friendly builds. + +## IMPORTANT NOTE + +Cant use synk in this part API TOKEN only in Enterprice Subscribtion \ No newline at end of file diff --git a/app_go/docs/screenshots/worked-ci-cd.png b/app_go/docs/screenshots/worked-ci-cd.png new file mode 100644 index 0000000000000000000000000000000000000000..97bf61eb33566a21bed22ad89aff3b5ed01513c2 GIT binary patch literal 134262 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%0A@Ku8#wdH7LwZKh?uz3YO2Jv@tSq@ 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