From bc26e6c96d31e2327d953e12dd867c8960e08434 Mon Sep 17 00:00:00 2001 From: Maksim Malov Date: Sun, 25 Jan 2026 19:21:18 +0300 Subject: [PATCH 1/5] feat: implement lab01 devops info service --- app_python/.gitignore | 12 + app_python/README.md | 41 +++ app_python/app.py | 162 +++++++++ app_python/docs/LAB01.md | 341 ++++++++++++++++++ .../docs/screenshots/01-main-endpoint.png | Bin 0 -> 45712 bytes .../docs/screenshots/02-health-check.png | Bin 0 -> 11296 bytes .../docs/screenshots/03-formatted-output.png | Bin 0 -> 55725 bytes app_python/requirements.txt | 2 + app_python/tests/__init__.py | 0 9 files changed, 558 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/01-main-endpoint.png create mode 100644 app_python/docs/screenshots/02-health-check.png create mode 100644 app_python/docs/screenshots/03-formatted-output.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..4de420a8f7 --- /dev/null +++ b/app_python/.gitignore @@ -0,0 +1,12 @@ +# Python +__pycache__/ +*.py[cod] +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..32d32ac023 --- /dev/null +++ b/app_python/README.md @@ -0,0 +1,41 @@ +# devops-info-service + +## Overview +`devops-info-service` is a lightweight HTTP service built with **FastAPI** that returns comprehensive runtime and system information. It exposes: +- service metadata (name, version, description, framework), +- system details (hostname, OS/platform, architecture, CPU count, Python version), +- runtime data (uptime, current UTC time), +- request details (client IP, user-agent, method, path), +- a list of available endpoints. + +## Prerequisites +- **Python:** 3.10+ (recommended 3.11+) +- **Dependencies:** listed in `requirements.txt` + +## Installation + +``` +python -m venv venv +source venv/bin/activate +pip install -r requirements.txt +``` + +## Running the Application +``` +python app.py +# Or with custom config +PORT=8080 python app.py +``` + +## API Endpoints +- `GET /` - Service and system information +- `GET /health` - Health check + +## Configuration + +The application is configured using environment variables. + +| Variable | Default | Description | Example | +|---------|---------|-------------|---------| +| `HOST` | `127.0.0.1` | Host interface to bind the server to | `0.0.0.0` | +| `PORT` | `8000` | Port the server listens on | `8080` | \ No newline at end of file diff --git a/app_python/app.py b/app_python/app.py new file mode 100644 index 0000000000..a1e16fe273 --- /dev/null +++ b/app_python/app.py @@ -0,0 +1,162 @@ +""" +DevOps Info Service +Main application module +""" +import logging +import os +import platform +import socket +from datetime import datetime, timezone + +import uvicorn +from fastapi import FastAPI, Request, status +from fastapi.responses import JSONResponse +from starlette.exceptions import HTTPException + +app = FastAPI() + +# Configuration +HOST = os.getenv("HOST", "0.0.0.0") +PORT = int(os.getenv("PORT", 5000)) +DEBUG = os.getenv("DEBUG", "False").lower() == "true" + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +# Application start time +start_time = datetime.now() + + +def get_service_info(): + """Get information about service.""" + logger.debug('Getting info about the service.') + return { + "name": "devops-info-service", + "version": "1.0.0", + "description": "DevOps course info service", + "framework": "FastAPI", + } + + +def get_system_info(): + """Get information about system.""" + logger.debug('Getting info about the system.') + return { + "hostname": socket.gethostname(), + "platform": platform.system(), + "platformVersion": platform.version(), + "architecture": platform.machine(), + "cpu_count": os.cpu_count(), + "python_version": platform.python_version(), + } + + +def get_uptime(): + """Get uptime.""" + logger.debug('Getting uptime.') + delta = datetime.now() - start_time + seconds = int(delta.total_seconds()) + hours = seconds // 3600 + minutes = (seconds % 3600) // 60 + return {"seconds": seconds, "human": f"{hours} hours, {minutes} minutes"} + + +def get_runtime_info(): + """Get information about runtime.""" + logger.debug('Getting runtime info.') + uptime = get_uptime() + uptime_seconds, uptime_human = uptime["seconds"], uptime["human"] + current_time = datetime.now(timezone.utc) + + return { + "uptime_seconds": uptime_seconds, + "uptime_human": uptime_human, + "current_time": current_time, + "timezone": "UTC", + } + + +def get_request_info(request: Request): + """Get information about request.""" + logger.debug('Getting info about request.') + return { + "client_ip": request.client.host, + "user_agent": request.headers.get("user-agent"), + "method": request.method, + "path": request.url.path, + } + + +def get_endpoints(): + """Get all existing ednpoints.""" + logger.debug('Getting list of all endpoints.') + return [ + {"path": "/", "method": "GET", "description": "Service information"}, + {"path": "/health", "method": "GET", "description": "Health check"}, + ] + + +@app.get("/", status_code=status.HTTP_200_OK) +async def root(request: Request): + """Main endpoint - service and system information.""" + logger.debug(f'Request: {request.method} {request.url.path}') + return { + "service": get_service_info(), + "system": get_system_info(), + "runtime": get_runtime_info(), + "request": get_request_info(request), + "endpoints": get_endpoints(), + } + + +@app.get("/health", status_code=status.HTTP_200_OK) +async def health(request: Request): + """Endpoint to check health.""" + logger.debug(f'Request: {request.method} {request.url.path}') + return { + "status": "healthy", + "timestamp": datetime.now(timezone.utc), + "uptime_seconds": get_uptime()["seconds"], + } + + +@app.exception_handler(HTTPException) +async def http_exception_handler(request: Request, exc: HTTPException): + """Exception 404 (Not found) that endpoint does not exists.""" + if exc.status_code == 404: + return JSONResponse( + status_code=404, + content={ + "error": "Not Found", + "message": "Endpoint does not exist", + }, + ) + return JSONResponse( + status_code=exc.status_code, + content={ + "error": "HTTP Error", + "message": exc.detail if exc.detail else "Request failed", + }, + ) + + +@app.exception_handler(Exception) +async def unhandled_exception_handler(request: Request, exc: Exception): + """Exception 500 (Internal Server Error) - For any unhandled errors.""" + return JSONResponse( + status_code=500, + content={ + "error": "Internal Server Error", + "message": "An unexpected error occurred", + }, + ) + + +if __name__ == "__main__": + """The entry point.""" + logger.info('Application starting...') + + uvicorn.run("app:app", host=HOST, port=PORT, reload=True) diff --git a/app_python/docs/LAB01.md b/app_python/docs/LAB01.md new file mode 100644 index 0000000000..3c073f4777 --- /dev/null +++ b/app_python/docs/LAB01.md @@ -0,0 +1,341 @@ +# 1. Framework selection. +## Choice +I have chose FastAPI because I have had an experience with it and have no experience with other frameworks. + +## Comparison with Alternatives + +| Criteria | FastAPI (chosen) | Flask | Django (DRF) | +|---------|-----------------|-------|--------------| +| Primary use | APIs / microservices | Lightweight web apps & APIs | Full-stack apps & large APIs | +| Performance model | ASGI (async-ready) | WSGI (sync by default) | WSGI/ASGI (heavier stack) | +| Built-in API docs | Yes (Swagger/OpenAPI) | No (manual/add-ons) | Yes (via DRF) | +| Validation / typing | Strong (type hints + Pydantic) | Manual or extensions | Strong (serializers) | +| Boilerplate | Low | Very low | Higher | +| Learning curve | Low–medium | Low | Medium–high | +| Best fit for this lab | Excellent | Good | Overkill | + +--- + +# 2. Best Practices Applied +## Clean Code Organization + +### 1) Clear Function Names +The code uses descriptive, intention-revealing function names that clearly communicate what each block returns: + +```python +def get_service_info(): + """Get information about service.""" + ... + +def get_system_info(): + """Get information about system.""" + ... + +def get_runtime_info(): + """Get information about runtime.""" + ... + +def get_request_info(request: Request): + """Get information about request.""" + ... +``` +**Why it matters**: Clear naming improves readability, reduces the need for extra comments, and makes the code easier to maintain and extend. + +### 2) Proper imports grouping +Imports are organized by category (standard library first, then third-party libraries), which is the common Python convention: +```python +import logging +import os +import platform +import socket +from datetime import datetime, timezone + +import uvicorn +from fastapi import FastAPI, Request, status +from fastapi.responses import JSONResponse +from starlette.exceptions import HTTPException +``` +**Why it matters**: Grouped imports make dependencies easier to understand at a glance, help keep the file structured, and align with typical linting rules. + +### 3) Comments only where needed +Instead of excessive inline comments, the code relies on clear names and short docstrings: +```python +""" +DevOps Info Service +Main application module +""" + +def get_uptime(): + """Get uptime.""" + ... +``` +**Why it matters**: Too many comments can become outdated. Minimal documentation plus clean naming keeps the codebase readable and accurate. + +### 4) Follow PEP 8 +The implementation follows common PEP 8 practices: +- consistent indentation and spacing, +- snake_case for variables and function names, +- configuration/constants placed near the top of the module (HOST, PORT, DEBUG), +- readable multi-line formatting for long calls: +```python +""" +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +``` +**Why it matters**: PEP 8 improves consistency, supports teamwork, and makes the code compatible with linters/formatters such as `flake8`, `ruff`, and `black`. + +## Error Handling +The service implements centralized error handling using FastAPI/Starlette exception handlers. This ensures that errors are returned in a consistent JSON format and that clients receive meaningful messages instead of raw stack traces. + +### HTTP errors (e.g., 404 Not Found) +A dedicated handler processes HTTP-related exceptions and customizes the response for missing endpoints. + +```python +from starlette.exceptions import HTTPException + +@app.exception_handler(HTTPException) +async def http_exception_handler(request: Request, exc: HTTPException): + if exc.status_code == 404: + return JSONResponse( + status_code=404, + content={ + "error": "Not Found", + "message": "Endpoint does not exist", + }, + ) + + return JSONResponse( + status_code=exc.status_code, + content={ + "error": "HTTP Error", + "message": exc.detail if exc.detail else "Request failed", + }, + ) +``` +**Why it matters**: +- Provides a clear and user-friendly message for invalid routes. +- Keeps error responses consistent across the API. +- Avoids exposing internal implementation details to the client. + +### Unhandled exceptions (500 Internal Server Error) +A global handler catches any unexpected exceptions and returns a safe, standardized response. + +```python +@app.exception_handler(Exception) +async def unhandled_exception_handler(request: Request, exc: Exception): + return JSONResponse( + status_code=500, + content={ + "error": "Internal Server Error", + "message": "An unexpected error occurred", + }, + ) +``` +**Why it matters**: +- Prevents server crashes from unhandled errors. +- Ensures clients always receive valid JSON (important for automation/scripts). +- Helps keep production behavior predictable while preserving the option to log the exception internally. + +## 3. Logging +The service includes basic logging configuration to improve observability and simplify debugging. Logs are useful both during development (troubleshooting requests and behavior) and in production (monitoring, incident investigation). + +### Logging setup +A global logging configuration is defined at startup with a consistent log format: +```python +import logging + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger(__name__) +``` +**Why it matters**: +- Provides timestamps and log levels for easier troubleshooting. +- A consistent format makes logs easier to parse in log aggregators (e.g., ELK, Loki). +- Centralized config avoids inconsistent logging across modules. + +### Startup logging +The application logs an informational message when it starts: +```python +if __name__ == "__main__": + logger.info("Application starting...") + uvicorn.run("app:app", host=HOST, port=PORT, reload=True) +``` +**Why it matters**: +- Confirms that the service started successfully. +- Helps identify restarts and uptime issues. + +### Request logging (debug level) +Each endpoint logs basic request information (method and path): +```python +@app.get("/", status_code=status.HTTP_200_OK) +async def root(request: Request): + logger.debug(f"Request: {request.method} {request.url.path}") + ... +``` +**Why it matters**: +- Helps trace API usage during development. +- Useful for debugging routing problems and unexpected client behavior. + +## 4. Dependencies (requirements.txt) +The project keeps dependencies minimal and focused on what is required to run a FastAPI service in production. +### requirements.txt +``` +fastapi>=0.110 +uvicorn[standard]>=0.27 +``` +**Why it matters**: +- Faster builds & simpler setup: fewer packages mean faster installation and fewer moving parts. +- Lower risk of conflicts: minimal dependencies reduce version incompatibilities and “dependency hell”. +- Better security posture: fewer third-party libraries reduce the overall attack surface. +- More predictable deployments: only installing what the service truly needs improves reproducibility across environments (local, CI, Docker, VM). + +## 5. Git Ignore (.gitignore) + +A `.gitignore` file is used to prevent committing temporary, machine-specific, or sensitive files into the repository. + +### Recommended `.gitignore` +```gitignore +# Python +__pycache__/ +*.py[cod] +venv/ +*.log + +# IDE +.vscode/ +.idea/ + +# OS +.DS_Store +``` +**Why it matters**: +- Keeps the repository clean: avoids committing generated files (`__pycache__`, build outputs, logs). +- Improves portability: prevents OS- and IDE-specific files from polluting the project and causing noisy diffs. +- Protects secrets: ensures configuration files like `.env` (which may contain API keys or credentials) are not accidentally pushed. +- Reduces merge conflicts: fewer irrelevant files tracked by Git means fewer conflicts between contributors. + +# 3. API Documentation +The service exposes two endpoints: the main information endpoint and a health check endpoint. +## Request/response examples +### GET `/` — Service and System Information +**Description:** +Returns comprehensive metadata about the service, system, runtime, request details, and available endpoints. +**Request example:** +```bash +curl -i http://127.0.0.1:5000/ +``` +**Response example (200 OK):** +```json +{ + "service": { + "name": "devops-info-service", + "version": "1.0.0", + "description": "DevOps course info service", + "framework": "Flask" + }, + "system": { + "hostname": "my-laptop", + "platform": "Linux", + "platform_version": "Ubuntu 24.04", + "architecture": "x86_64", + "cpu_count": 8, + "python_version": "3.13.1" + }, + "runtime": { + "uptime_seconds": 3600, + "uptime_human": "1 hour, 0 minutes", + "current_time": "2026-01-07T14:30:00.000Z", + "timezone": "UTC" + }, + "request": { + "client_ip": "127.0.0.1", + "user_agent": "curl/7.81.0", + "method": "GET", + "path": "/" + }, + "endpoints": [ + {"path": "/", "method": "GET", "description": "Service information"}, + {"path": "/health", "method": "GET", "description": "Health check"} + ] +} +``` +### GET /health — Health Check +**Description:** +Returns a simple status response to confirm the service is running.**Request example:** +```bash +curl -i http://127.0.0.1:5000/health +``` +**Response example (200 OK):** +```json +{ + "status": "healthy", + "timestamp": "2024-01-15T14:30:00.000Z", + "uptime_seconds": 3600 +} +``` +## Testing commands +### Basic tests +```bash +curl http://127.0.0.1:5000/ +curl http://127.0.0.1:5000/health +``` +### Test 404 handling (unknown endpoint) +```bash +curl -i http://127.0.0.1:5000/does-not-exist +``` +Expected response (404): +```json +{ + "error": "Not Found", + "message": "Endpoint does not exist" +} +``` + +# 4. Testing Evidence +Check screenshots. + +# 5. Challenges & Solutions +I have no problems in this lab. + +# GitHub Community +**Why Stars Matter:** + +**Discovery & Bookmarking:** +- Stars help you bookmark interesting projects for later reference +- Star count indicates project popularity and community trust +- Starred repos appear in your GitHub profile, showing your interests + +**Open Source Signal:** +- Stars encourage maintainers (shows appreciation) +- High star count attracts more contributors +- Helps projects gain visibility in GitHub search and recommendations + +**Professional Context:** +- Shows you follow best practices and quality projects +- Indicates awareness of industry tools and trends + +**Why Following Matters:** + +**Networking:** +- See what other developers are working on +- Discover new projects through their activity +- Build professional connections beyond the classroom + +**Learning:** +- Learn from others' code and commits +- See how experienced developers solve problems +- Get inspiration for your own projects + +**Collaboration:** +- Stay updated on classmates' work +- Easier to find team members for future projects +- Build a supportive learning community + +**Career Growth:** +- Follow thought leaders in your technology stack +- See trending projects in real-time +- Build visibility in the developer community \ No newline at end of file diff --git a/app_python/docs/screenshots/01-main-endpoint.png b/app_python/docs/screenshots/01-main-endpoint.png new file mode 100644 index 0000000000000000000000000000000000000000..2b5ad4b17fe1c5d8a4dfb28c9437474f4aff5f03 GIT binary patch literal 45712 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 WC9Q1i55R&_C_GesP{U literal 0 HcmV?d00001 diff --git a/app_python/docs/screenshots/02-health-check.png b/app_python/docs/screenshots/02-health-check.png new file mode 100644 index 0000000000000000000000000000000000000000..321da884509116354b02fdef84398862741ade84 GIT binary patch literal 11296 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 literal 0 HcmV?d00001 diff --git a/app_python/docs/screenshots/03-formatted-output.png b/app_python/docs/screenshots/03-formatted-output.png new file mode 100644 index 0000000000000000000000000000000000000000..bd76d10670db1ebaaa117ef4380dd37987cd0289 GIT binary patch literal 55725 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.110 +uvicorn[standard]>=0.27 \ 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 2601ee15ecd8b90c049f9e844b67718717e5b1c4 Mon Sep 17 00:00:00 2001 From: Maksim Malov Date: Sun, 25 Jan 2026 20:03:17 +0300 Subject: [PATCH 2/5] fix: minor fixes --- app_python/README.md | 8 ++++---- app_python/app.py | 6 +++--- app_python/docs/LAB01.md | 18 +++++++++--------- app_python/requirements.txt | 4 ++-- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app_python/README.md b/app_python/README.md index 32d32ac023..74ffca1c2b 100644 --- a/app_python/README.md +++ b/app_python/README.md @@ -35,7 +35,7 @@ PORT=8080 python app.py The application is configured using environment variables. -| Variable | Default | Description | Example | -|---------|---------|-------------|---------| -| `HOST` | `127.0.0.1` | Host interface to bind the server to | `0.0.0.0` | -| `PORT` | `8000` | Port the server listens on | `8080` | \ No newline at end of file +| Variable | Default | Description | Example | +|---------|---------|-------------|-------------| +| `HOST` | `0.0.0.0` | Host interface to bind the server to | `127.0.0.1` | +| `PORT` | `8000` | Port the server listens on | `8080` | \ No newline at end of file diff --git a/app_python/app.py b/app_python/app.py index a1e16fe273..8cd20e0680 100644 --- a/app_python/app.py +++ b/app_python/app.py @@ -17,7 +17,7 @@ # Configuration HOST = os.getenv("HOST", "0.0.0.0") -PORT = int(os.getenv("PORT", 5000)) +PORT = int(os.getenv("PORT", 8000)) DEBUG = os.getenv("DEBUG", "False").lower() == "true" logging.basicConfig( @@ -47,7 +47,7 @@ def get_system_info(): return { "hostname": socket.gethostname(), "platform": platform.system(), - "platformVersion": platform.version(), + "platform_version": platform.version(), "architecture": platform.machine(), "cpu_count": os.cpu_count(), "python_version": platform.python_version(), @@ -156,7 +156,7 @@ async def unhandled_exception_handler(request: Request, exc: Exception): if __name__ == "__main__": - """The entry point.""" + # The entry point logger.info('Application starting...') uvicorn.run("app:app", host=HOST, port=PORT, reload=True) diff --git a/app_python/docs/LAB01.md b/app_python/docs/LAB01.md index 3c073f4777..1d43f6863d 100644 --- a/app_python/docs/LAB01.md +++ b/app_python/docs/LAB01.md @@ -183,9 +183,9 @@ async def root(request: Request): ## 4. Dependencies (requirements.txt) The project keeps dependencies minimal and focused on what is required to run a FastAPI service in production. ### requirements.txt -``` -fastapi>=0.110 -uvicorn[standard]>=0.27 +```txt +fastapi==0.122.0 +uvicorn[standard]==0.38.0 ``` **Why it matters**: - Faster builds & simpler setup: fewer packages mean faster installation and fewer moving parts. @@ -226,7 +226,7 @@ The service exposes two endpoints: the main information endpoint and a health ch Returns comprehensive metadata about the service, system, runtime, request details, and available endpoints. **Request example:** ```bash -curl -i http://127.0.0.1:5000/ +curl -i http://127.0.0.1:8000/ ``` **Response example (200 OK):** ```json @@ -235,7 +235,7 @@ curl -i http://127.0.0.1:5000/ "name": "devops-info-service", "version": "1.0.0", "description": "DevOps course info service", - "framework": "Flask" + "framework": "FastAPI" }, "system": { "hostname": "my-laptop", @@ -267,7 +267,7 @@ curl -i http://127.0.0.1:5000/ **Description:** Returns a simple status response to confirm the service is running.**Request example:** ```bash -curl -i http://127.0.0.1:5000/health +curl -i http://127.0.0.1:8000/health ``` **Response example (200 OK):** ```json @@ -280,12 +280,12 @@ curl -i http://127.0.0.1:5000/health ## Testing commands ### Basic tests ```bash -curl http://127.0.0.1:5000/ -curl http://127.0.0.1:5000/health +curl http://127.0.0.1:8000/ +curl http://127.0.0.1:8000/health ``` ### Test 404 handling (unknown endpoint) ```bash -curl -i http://127.0.0.1:5000/does-not-exist +curl -i http://127.0.0.1:8000/does-not-exist ``` Expected response (404): ```json diff --git a/app_python/requirements.txt b/app_python/requirements.txt index c0d860c195..94a41a1968 100644 --- a/app_python/requirements.txt +++ b/app_python/requirements.txt @@ -1,2 +1,2 @@ -fastapi>=0.110 -uvicorn[standard]>=0.27 \ No newline at end of file +fastapi==0.122.0 +uvicorn[standard]==0.38.0 From 1bde0adbfa60149fe525a77b1c7e2f0c66b5d5d4 Mon Sep 17 00:00:00 2001 From: Maksim Malov Date: Sun, 25 Jan 2026 20:34:54 +0300 Subject: [PATCH 3/5] feat: lab01 bonus task completed --- app_go/.gitignore | 18 ++ app_go/README.md | 43 +++ app_go/docs/GO.md | 5 + app_go/docs/LAB01.md | 217 ++++++++++++++ app_go/docs/screenshots/01-main-endpoint.png | Bin 0 -> 44356 bytes app_go/docs/screenshots/02-health-check.png | Bin 0 -> 8540 bytes .../docs/screenshots/03-formatted-output.png | Bin 0 -> 14355 bytes app_go/go.mod | 3 + app_go/main.go | 277 ++++++++++++++++++ 9 files changed, 563 insertions(+) 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/01-main-endpoint.png create mode 100644 app_go/docs/screenshots/02-health-check.png create mode 100644 app_go/docs/screenshots/03-formatted-output.png create mode 100644 app_go/go.mod create mode 100644 app_go/main.go diff --git a/app_go/.gitignore b/app_go/.gitignore new file mode 100644 index 0000000000..d6c75d54e3 --- /dev/null +++ b/app_go/.gitignore @@ -0,0 +1,18 @@ +# Go binaries / build output +devops-info-service +*.exe +*.out +*.test +bin/ +dist/ + +# IDE/editor +.vscode/ +.idea/ + +# OS files +.DS_Store +Thumbs.db + +# Logs +*.log \ No newline at end of file diff --git a/app_go/README.md b/app_go/README.md new file mode 100644 index 0000000000..e53cc9a7e6 --- /dev/null +++ b/app_go/README.md @@ -0,0 +1,43 @@ +# devops-info-service (Go) + +## Overview +`devops-info-service` is a lightweight HTTP service written in Go. It returns: +- service metadata (name, version, description, framework), +- system information (hostname, OS/platform, architecture, CPU count, Go version), +- runtime information (uptime, current UTC time), +- request information (client IP, user-agent, method, path), +- a list of available endpoints. + +This is useful for DevOps labs and basic observability: quick environment inspection and health checks. + +--- + +## Prerequisites +- **Go:** 1.22+ (recommended) +- No external dependencies (standard library only) + +--- + +## Installation +```bash +cd app_go +go mod tidy +``` + +## Running the Application +```bash +go run . +``` + +## API Endpoints +- `GET /` - Service and system information +- `GET /health` - Health check + +## Configuration + +The application is configured using environment variables. + +| Variable | Default | Description | Example | +|---------|---------|-------------|---------| +| `HOST` | `0.0.0.0` | Host interface to bind the server to | `0.0.0.0` | +| `PORT` | `8080` | Port the server listens on | `8080` | \ No newline at end of file diff --git a/app_go/docs/GO.md b/app_go/docs/GO.md new file mode 100644 index 0000000000..39785012a7 --- /dev/null +++ b/app_go/docs/GO.md @@ -0,0 +1,5 @@ +### Why Go? +- **Compiled binary**: produces a single executable (useful for multi-stage Docker builds). +- **Fast startup and low overhead**: good for microservices. +- **Standard library is enough**: `net/http` covers routing and HTTP server without external frameworks. +- **Great DevOps fit**: simple deployment, small runtime requirements. diff --git a/app_go/docs/LAB01.md b/app_go/docs/LAB01.md new file mode 100644 index 0000000000..52920f06bc --- /dev/null +++ b/app_go/docs/LAB01.md @@ -0,0 +1,217 @@ +# Lab 1 (Bonus) — DevOps Info Service in Go + +## 1. Language / Framework Selection + +### Choice +I implemented the bonus service in **Go** using the standard library **net/http** package. + +### Why Go? +- **Compiled binary**: produces a single executable (useful for multi-stage Docker builds). +- **Fast startup and low overhead**: good for microservices. +- **Standard library is enough**: `net/http` covers routing and HTTP server without external frameworks. +- **Great DevOps fit**: simple deployment, small runtime requirements. + +### Comparison with Alternatives + +| Criteria | Go (net/http) (chosen) | Rust | Java (Spring Boot) | C# (ASP.NET Core) | +|---------|--------------------------|------|---------------------|-------------------| +| Build artifact | Single binary | Single binary | JVM app + deps | .NET app + deps | +| Startup time | Fast | Fast | Usually slower | Medium | +| Runtime deps | None | None | JVM required | .NET runtime | +| HTTP stack | stdlib | frameworks (Axum/Actix) | Spring ecosystem | ASP.NET stack | +| Complexity | Low | Medium–high | Medium | Medium | +| Best fit for this lab | Excellent | Good | Overkill | Good | + +--- + +## 2. Best Practices Applied + +### 2.1 Clean Code Organization +- Clear data models (`ServiceInfo`, `Service`, `System`, `RuntimeInfo`, `RequestInfo`, `Endpoint`). +- Helper functions for concerns separation: + - `runtimeInfo()`, `requestInfo()`, `uptime()`, `isoUTCNow()`, `clientIP()`, `writeJSON()`. + +### 2.2 Configuration via Environment Variables +The service is configurable via environment variables: +- `HOST` (default `0.0.0.0`) +- `PORT` (default `8080`) +- `DEBUG` (default `false`) + +Implementation uses a simple helper: +```go +func getenv(key, def string) string { + v := os.Getenv(key) + if v == "" { + return def + } + return v +} +``` + +### 2.3 Logging Middleware +Request logging is implemented as middleware: +```go +func withLogging(logger *log.Logger) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + next.ServeHTTP(w, r) + logger.Printf("%s %s (%s) from %s in %s", + r.Method, r.URL.Path, r.Proto, r.RemoteAddr, time.Since(start)) + }) + } +} +``` + +### 2.4 Error Handling +#### 404 Not Found +Unknown endpoints return a consistent JSON error: +```json +{ + "error": "Not Found", + "message": "Endpoint does not exist" +} +``` +This is implemented via a wrapper that enforces valid paths: +```go +func withNotFound(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" && r.URL.Path != "/health" { + writeJSON(w, http.StatusNotFound, ErrorResponse{ + Error: "Not Found", + Message: "Endpoint does not exist", + }) + return + } + next.ServeHTTP(w, r) + }) +} +``` +#### 500 Internal Server Error (panic recovery) +A recover middleware prevents crashes and returns a safe JSON response: +```go +func withRecover(logger *log.Logger) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer func() { + if rec := recover(); rec != nil { + logger.Printf("panic recovered: %v", rec) + writeJSON(w, http.StatusInternalServerError, ErrorResponse{ + Error: "Internal Server Error", + Message: "An unexpected error occurred", + }) + } + }() + next.ServeHTTP(w, r) + }) + } +} +``` +### 2.5 Production-Friendly HTTP Server Settings +The service uses `http.Server` with timeouts: +```go +srv := &http.Server{ + Addr: addr, + Handler: handler, + ReadHeaderTimeout: 5 * time.Second, +} +``` +## 3. API Documentation +### 3.1 GET / — Service and System Information +**Description**: Returns service metadata, system info, runtime info, request info, and available endpoints. + +**Request**: +```bash +curl -i http://127.0.0.1:8080/ +``` +**Response (200 OK) example**: +```json +{ + "service": { + "name": "devops-info-service", + "version": "1.0.0", + "description": "DevOps course info service", + "framework": "Go net/http" + }, + "system": { + "hostname": "DESKTOP-KUN1CI4", + "platform": "windows", + "platform_version": "unknown", + "architecture": "amd64", + "cpu_count": 8, + "go_version": "go1.25.6" + }, + "runtime": { + "uptime_seconds": 6, + "uptime_human": "0 hours, 0 minutes", + "current_time": "2026-01-25T17:17:32.248Z", + "timezone": "UTC" + }, + "request": { + "client_ip": "::1", + "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0", + "method": "GET", + "path": "/" + }, + "endpoints": [ + { + "path": "/", + "method": "GET", + "description": "Service information" + }, + { + "path": "/health", + "method": "GET", + "description": "Health check" + } + ] +} +``` +### 3.2 GET /health — Health Check +**Description**: Description: Simple health endpoint used for monitoring and probes. + +**Request**: +```bash +curl -i http://127.0.0.1:8080/health +``` +**Response (200 OK) example**: +```json +{ + "status": "healthy", + "timestamp": "2026-01-25T17:19:02.582Z", + "uptime_seconds": 96 +} +``` +### 3.3 404 Behavior +**Request**: +```bash +curl -i http://127.0.0.1:8080/does-not-exist +``` +**Response (404 Not Found)**: +```json +{ + "error": "Not Found", + "message": "Endpoint does not exist" +} +``` + +## 4. Build & Run Instructions +### 4.1 Run locally (no build) +```bash +go run main.go +``` +### 4.2 Build binary +```bash +go build -o devops-info-service main.go +``` +Run: +```bash +./devops-info-service +``` +### 4.3 Environment variables examples +```bash +HOST=127.0.0.1 PORT=3000 ./devops-info-service +DEBUG=true PORT=8081 ./devops-info-service +``` +## 5. Challenges & Solutions +I don't know how `go` works. diff --git a/app_go/docs/screenshots/01-main-endpoint.png b/app_go/docs/screenshots/01-main-endpoint.png new file mode 100644 index 0000000000000000000000000000000000000000..c0f1b2ed5c567e9a28506ba411e332f351df7a73 GIT binary patch literal 44356 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 literal 0 HcmV?d00001 diff --git a/app_go/docs/screenshots/02-health-check.png b/app_go/docs/screenshots/02-health-check.png new file mode 100644 index 0000000000000000000000000000000000000000..aed7a379187856f47ca59651519055fd113e1221 GIT binary patch literal 8540 zcmdsdXH*m2_iku{R0WZjj!3UcCv-uolu)Is^xj*jQUp{8J<@v#RXPMi5s)UKgS4RZ zA~l2#`RDiE`@d`5FL&K_Ki&^BbM~5j_MClY&2ye-pNZGgQKKMbAO!#b6dLL(`TzhP z2-lt^xraMDHq~d~E_gorYA*n_NTwZ}LFk~QtpotnL&$F465;Iop6aGP003q0e-69> zj|y7=fbCdAMaeMGYJZu`(-4CoKC;%^t1=ETDtYB|Ux}PI)-`)6Cf8;?+55NFQ6LJJ(<`ud%mS zEPAQYVeBAW5;A%&>cbU`ocB4)Y3%5_RX}2)IbAm!!pwvK0PjvQ79XHU>Meh{PLrmB z2hfjB;Q>V85#n+rASnU>?u~OTdnf$aU|$ zw3j|!d$sB;d;Z;rn;PJomnM_Zd9g|cKSu>`xQX7hR>_|IvT5>KQ|063E#$9li|oYI zcnnSc%E#U~fRFp>oc3mG#KLX@;nxQpb`i@R0d**m#;mNY?b?omBG$X3SnsrfVHX&1u+)Z^mK(I0X=rXz>sWet0sv7Qua1rvn~`u%cl}!9Y2R8NKw}u5~Yw9c_1*4)1lBQ!PX`As!&V2mCn%98}!%|bHGv| zl#o%zzgoa4H~j9h;n8p}lW2_A51-ASg)7**^Ctg&GX^ooB&)UGcHa{7d&f51Cf~c< zlT8!-i5s-K7E~x5Z+f|;8}AD@nKi%Aybs%kbGS!K=v5wufsg`elB zjh`L=s25Rc?kA5TyUS1OaJR+Bpu3ao95#oBnh~VYNL($0cJ4fBr;nGq-eD;_G00t^ zs-mJG2)zmN@~XF55TC6!RkpLUYoK^W-WuyIK9l9Yzfd|?!U|n&vvI)e&J-fn{>K;< zvnZEfef!;S85kMuFo>^T^@KrfH5uHkpkmdb6j2X?p2VAGBf_uj4Z94tLX2wdZZ*Do z$o-NZ4n9ucnC7z=@KO;d1GZu~n`;qx5%f|FLCzd+DX9M-^l}60w9w?#AehhX+(s1n0Uk=#5gRchO#lwy6sU zm!bRETY0PxHS-ec93Q>|LGZyuL1V)yQ< zKk(sCmcn1f(`UkE{Y@5f0eh|tr;D29mJ$=s<1p!?jWAG`@!IkdxoAY0>w?-y9VdgY zZt?5R%(C^$^2IBtzV^P<-xPpo@E?(jM)%;8Z@8_%Hw?HJnyEnPHwUA8N~J-4G~cTXs0- z8@vlE-;U?bOQMlf^5vv#TKaT&e%^(()Cmho&|Y+H^_b^~9QQT>Q1|S(KYS40Xyi}m z7XI#evcoN{*v2g0?nFxIJDuwTLIz#IQ~PhN?nka>wXMZVykh2rT^R&?>RiBkPFNS) zWo_Bt9VT%OgdLtOB-F-V*C^Ak<5{MaToyMH*x9aR;#Qx{XCN@)O)+Itg3#@tn(g<& z`zbpywCrC@wFz%-Bm!ov3Hx|o>d^FXo=+mqI)zHuAM%)Evs5~^x_3?snmvvQp3BYe z2e+v!rFq1f*1sb%GBO%kd;@1#A=^D5kKY|HR6a2-M}D;%d0bgnH|L|`=jTW7Vzb^s z)0W2kL8iGn_(kz=efkxdEV%Niy(ma2Pnd(^N|%8n$~Xt}SXaC&MPjFaNW+)>djRxf ziV&zYgxSbV_Z5_|JPnu&WPczC*EFo})qF#M-dR*#WdtyG2~(idRKo3Cfw@>F2y zW{Z}bMh?s-^H1P|%vxKy0eJ%PCp9m#B|T=fI_|a3$(=9xfQ5ka2S_+JSxqy6QBnt0 zJdDFyWc#b%_R+XS;?)?@8XLoDr!UWf3CspQAg<}Rufu}+@GGc(qMOV!;3aI?ctkQE~hEe7S9tG zMffe1b{^Dxvo!qEC24G&X*1_hp1x#!S;oT&k6L1tD0xk-9YP)(CO%~fm5C!g?Pb#$ z&{tpxH4+l9_4VtH2 zOc&@fspRZ7!uhaR`~WjY^(3Vvl4q?2)EN{yRi@qKIxm7~^6~P!TX3mMFAk?p?-mG2 zy~GV&~||4iKC7-(t0qNuyrVdE(avTPV*m+@e}} z@v`YvfOi7@)5^;yoFI3X(g@Tg>Sy3zS;$Q1R$lPwB(gV`B>)vv&LoOtmfXtmNwiw) z$C^0)`5tp}fKN$Tp5y(|D%EP`5*e@^&~=^599TUE$IO~<$U;$m9|qWLMuw#A$XGRrZZMma20y_Vev z_LblB=(qlHUHGjFA0Jxwf2ye>S6shc%@$Rzz%me2y-zEz;z~)6mlTl)Evkspx%+nd4@^{P`xU}nV^t22|Pw5Y0!msyR?ILLagR+DZ z0=?&-PXyoOv^?O{kNByajibVeTnF`A!Rqb4ACX)UKNTO0dN0lQ83nrwZ;+pnlmT{P z*cQkRwL;8lJK9sScqYAY$8F9>=aJDGMwTwn{ny z@zY&b_eZfSkZlBn2CKCpsw@BDu|*JRI+X%Q7;jKWnlahBv}Y zDyapPDYYiAs5>27brO2@+aDAs1Omt%d|?8+FSPFLKH(m@w8a-v)GdzM9uccKe0h3- zhdAwC^98MRin)EtBUb-@vUfF+9>hI?O*<=3P3)x-vu%|RQP{2@xYHVI@CS!XS}@da z^3?oOPKT#*YE74~Dw!Y77kWuM5xt;%S~u2D5FxAtiyGU3$R;n8FzV?Z!Lg;3^M zbD0(kU13IiL~wj-AKXvX8omwM%J*$m04FzEt~fOr2}47!n_JdGr52@9!MQa>(|^q! zSgFvp0j!^}zn0K%_inCF^VNPHBad?InDDm#)Kc`xK?vt$4}xh=!LmC($%~h$e3GlY z-%DyC^Wiz_5N#=Q`8%)P#naUu-F2G?S1u2)2DdC#yQT~)IKPST_{l*5G?67eh&Ra0 zTnhSF6}IbE z+%Jo1`Y18{yBE)%g%&<*Bx|%u`U=LN#ee$zs42LICChAyLUv>mDl zo#I>I?i_@P)5qB`+cE1t+3&=1!k_92$bU^XZVsFqrA4oDx?OM5?fUJ?F0IxXlq$Gb z{<#Tlstd}u;%@r;SS?MpaO+{%=vtQvsJd8WuH_)5eWjk=4CS}89hrj%Z@bf4Im|9rYK24I8}K!81r)F}ylF)&g|Y~s;Ad(h>SRNryY3VQ5tigI5bL4bN)+7u6;Lz>HgSLl(_DxRq0#@ zYq*Aqkb-*6VYn)LnZD_*`E=N+58`n_07)BGB}6GeutbigW-^zg`Wy;0S_XB%*xwZl zBKA0si690q*IPQopIR8r9_nhD1o7XCBxJ`Hk%S8E!Y#an1;jK7ld{K)btDe;Y zjpmC%9ed1Xa*`xqd1||d>8oLN!;ZSq=v74^cwVW}B0RdKIjkCrf65{qOT~I1b!qEo zAg1`}pL)XI-MQmQVk~51@1K*p{FPN~d7<$L&<)W1Pi^7>u3$kvxEtc*)0jsDh_D@G zTj&Z8m&4)HkfI+8&MWeNuVwt}&lZOd_($A`6SAw6t`Z zXi%dSJ^*kW80EO9&zC8l!TecmTE>%A#y1>Hw~q?C2I8YI1C|nZ5E`$}}{KwF<(SfmLXt;)()T%)+Sv01&M_ zK>gp9n0h5&4~iO(83-;&nq=JOSjh)Y6{D!ET;^CpM;}HW zq*-QZo)_-C_1B_K!VJi?zcb$Pg?Qoyp8>|IIb{2ORrnkEvmt$JW6D4$8*i2;q#uIM zMNYnvJ8-p?B?5uK?d}vQwfqxth{x}sJbpSKBw$`Zd*%|KfwNQcPS{{9wGf@8VcFLi zyL0Kv5$@Y(dUgr{C^obO$fq*!RVaV^oJR_*JRd1#-Z&GPGn`;+eT&+!C=Q7-6ViO0 zx8dx4I0ycDgC+)6#vM?ZSI6P{QMgou{5?hj-tryn~OVyF-?^Z~T!(yR(>a4* z8K!!u+cG2pBEwa9Foun-FDbxDb8L{xz8j%y{O#!&FrVqm=~FP!OnL;?qZue!dwSi7jC9BHlVH&Hj?_1qcZtsi<*%yL!n z8>RP0XqmIfV@1Pf%%9CNQn=nD*4w2xgUf5Dn#J=t%79a|D7n95Gv1;xsS@^KQ>V75 zjFYUN_Elu({%|{jj4jw!A`X6N`pCd)-_W^j!07(;Ods{0^z3cSO$_sJ8J z+f-0G9I?^EjAvRo!W|ciBiFJR&k2H57oM>@kvQRT!lHYK+V>uV0il-{Wb3GwUIrW1 zk6mv#RTxhtWN!i@(L^cNAv2pd-cmF8Lc(njt;y-Si@VfyNk9np7rL3-|mg z2G1Tm4v4=X@Hc(I+ti-%P@z#V14sp#9hLZ|#^3Bp$68tA+)q7L6`(dZ@R38x{&!wRiQ`ZWKw;s`WXwumOc1xd858>&PXE6N0-7Xu} zQmUBqP~*U4tUO(2Fi5Fsb3#w* zxdN$fOkuLqe)oZ@SP|jdc9P*7J2IJ;-+m_xeF9XAkTC1z`8IY$z&G7P^mpWZ@aXc3 z#Duzzp6ew`JK224pL9dQZxEzes}wWu6IKMawAZNOueDE*X!LjdlpDPdVdJz83O9v% zteRT{`yqPMB3t6c2P5zz zhMC%6eXnwC;*RNu0&BC-g%8c4?DSn!>&_0`?J(qh?s`#9<+5ZTw>r&$8=v>zkad5y zPpxu;*;YD+xtn{t`Y-)l#cRLQM=2b^VTR+=)PQ!}}zkUw-=qHP~l-H*i6n>?GF zKB~r7zWFw{?mwJT=IoIdg)nNWny$tz8ZT%B^So~iu(y8ZFgJF{1Tr1BPdKW&MNKP-gnZ;|t!*8IW??OIpU&jgOYF9N+r z>8{S~9J)`MgH4OGY=ieJsVf=6x+#TnqB+dA-gniEuDBzuH~V@uHXvDHuETQcMtSBU z3K=OnENfbQMe@OT3dn~+NYG=EoNr(Io0op}fV_}HJehZmAc8jj={l8-ob%hqcNa?bl( zIL<8KuzfFaT~GeD z>-Q)uS)hk^{rl|w28rYsVCjehj?||a>paAB>ICPq3W?AcLY~tJ;l9yU2P~HbXQpnD zA4&94eKzND6l0xF9omjr4So$~6Fz;rRJX&(xz5B5;qjV044UJ;b6KxGhkQF`)s^xS z()g_20Gmr-X6}uggPrqlUjpRRp#B2QKlvV(J~ARr&UAcSF>9DE+p3h<@z~nlQqzlC zuL9Fknw?=PF6OKN$*e1)&OMsbyy$jo5uyLElpi=cBpyHTTa<-vX*lt>ur>aV`XSq4*gI%A#@iutQjeg%0EWC0gPEx%q48^ zwB=>o0$bNDa(#0z#xx6xrOb}BxEbXhmYn%Ui0v+*f>IxOAOAA7y%~Bi4Q+Wz$cKp; zE&OKs4h02qC+UDw=*H~_;(4SDlAFhJ?!Q9;sjO9*AsLp_t3!bXuToW>otCTws}ftx zAGmU+|9a*3n8vBW`4=Y7*}i1GnwlL zTXLV8N>w1!cW9W()Gh+j|LO>ENC?8crP}mMHfQK?hJHNHWj{^!3na90@*gY}^l>Fz z$SiE)4c`E#GEhBV$;u(^0Rc#16`-_j^Iz7 z!Ma!Ul9$8{TVZ~xzY|idC<}-OUHN1KhHFm}@9BKn*nO=(f=PRpkTIjQdME{=33xUX zI(&PySBZS+K>ul3LSIuT94KrZYj~r>vZ-ahiJEP--g=$0W)gxvpyc&(4@sPLQH9q& znLZox73dFF_&)c1<%$DBTOhODP>;4)RU(xJZ0~?gvv~lD;3sjUAtOo49S7OZsviNq zv(d8PWX^x~p2`1}W?!Q!;0n*Ud%8k!yXTTJag~3ZysSj|9DgJ)ZlEi{yjwS`R5~q}szp(K&;txf4U6@b+0P%fRhmf}zf6bVw?-Dz%#C@45Re;$tp>`P5h zP`FEFB}COcK>Jz37SxK#TgEZJHY1HTpYV4GH*ZF!qjJ_ibE7vW)kbK4d~5uYgL{mp z?g`(a3ATKF@@yNLwo`vvQJ%K=+g**iVIt`z(R%TS4O-&~;sT8;3}UsZYK@9WhLf|o4pck3DUotK(_b`Avv#Btw$NAY*H-Qefz$A8y& z{v6`Nj{@c2#oPbe@!1vnrMn|pJ)f&w$Uc1cyUMQXk+PnyF2~#e>b0b&MpYM%kc#h| zk{^;+iNf{XUxh-WnRV8+J@5>*4nrfZvOZ=(SWS(j@mh z-fjmmz2B!~tUnMc)-OgV>*choViMuOC}v95(po8Krs*%s5Wk$;1|3ayIcxoyOS-Oz zz?v!J&Wz5&ig`VcwrTx~V(}Lbsu!ViVXYeMu0F?cy|7$!VQ=uDgWAP}Yx#`fIRX!t z&^mVa#tmA=r{*b}RmRdJC*Sm@sr8Jb{m6@E-J!}ezBQ+eDhnP!`rT-7Jv8f^T*+Rx zZiS{drp}|S=g8yY*0Qs_eK0MMlA%a5WyqPaKFAgN>nAU})3rzSN)D((I^CV5WktiD z+fvBKdxIqU()*0rnZe~K&iteaA&{y(4(}StR?pgrYpS1ufy6~R7{qveWZTzU2ZoX< zW)-(?GCIC1pz*SMt0I3XlKINqo*n2eE0`gUo}<~xcC<2P8>bajR^TXpuh?4O7Pc?( z6C8pI+1VDQ(KyuqC?7q4_ak_~Hm2DUL!}H-Vc!Pf`~lh8lxGx7NU^x)S$adO15+rg zdY6S}excp52`RV38etx)0E5(pw&eDATx7}){FH&GhSr!hNd$0jiIe%*-gC#)XyC=^0=tOeJ=4=dz84Pzt?U<~{H!;SBfL zmipwJ{uA&O#hh^U*BQNL!A0oUu@8gM{x*bN6JV@656PF9cAD2@2@|kR`=ys~j<90V zJY8#!l!WX^x*k$7-z($Q8Fms3rYdo@(ra$c&$HIK!v|9vK=GX~pdI>imO7r7#$xY; zjfh@N@Pd}PM&Is~k|$ag?N#}Du6`6ShV=3d%z|g9zO-|QNxh=j^&(kl1aiSs ziMDO4PzqF*rO1!?wJxD2tT1B(zY927yjorFflnW@H8p7+v9#BGF#tOIm6euq(Bn)A z&a)|un>RV3UX3KB)sL&84+C`hrahO-E4GDArll6qy&w90gn?G<0^E}_uvY?T6NGONi-`|m8s{+em=>V3)wN3 zZp{6}s}P#mBPmo>KIy5lc5X3vc@1Eg{>lX5CgE7~R>RfbhcgWx?FRuR_i|zSMAnY=)Wso|1a#;EjI_&C`wKbfMo^wITevj4?XQngz_$ka} z1!Bf&cm!KDh%l3j;YP60X@dNNoh){L+K!~&DL3g`(HdX(Q2sdg2-)Nz*BvV`;5z)_ zM75tV8Paj9Kz&^yX@aHGdJ=z{@+56E0>&%Z?KnB0s|FVE#JMaNR3h*{liu1$7A`ng z8%&6@BN~|wU8AEJ$6h;i1}%G=s#gN>kOgr|)8iRYvTxzyi;7irptsFmn--`@*Sq}fj%}v)W$hX9=^zI!T@Co7Y zl0E|&w9X&whCk7s-OIRp2ubd($aQIJ{RTSzmL|e0g?5I3(q?O?wKm-@afc5?WS`CK zi>IszrIuoYo5X{S7AxK~n1Gml&yXjcdlN?Onfu8t-T*MFV1~aMH`Dl7W$_H^<{oBQ zQP^T-{gm5xqf{?x+!dAEfCoX;NZ${O<+e3(StkL^eGc*NPLlu(9jX)`k-DO zCM(QTKW+S-#POhyS|)qiZXxYuG`T2lCFwNC5%_RHW4-sqG(}^U)B6of*sm+$y?Uwn9{}m4zD+?RD;NJLRA(r_=gSk z%;L&P+wbo(%f=JWO-`!P-p=nYv5?tL1Hpx)#FD^`##=X4`S0kt7`01W)NBJk^XaKh z@ZRt;M^d_z9lLZ~ZFu^XG^Scr)Y_W5B_GC*8Q0q~lzBTVI`cZpVNO-}7Q!6PhBKw! zhw`rFf7BR2B+o$jsai_D54x5mHh!OSckO#Nt$){Y zbq;jC{rzwO?7H#%`Q$O{nlT1J0Oz)`5sLBlLO6fDy#;+%H4(0(n+X~%AZYRJd}^Vr zH!)AR2k4RD8DfCAq;@P`V=3I|6PHH zro8-cwM4|5QvF~X$y8(ozDR<;Y%P-Zv!cH6P=l?0so(W-Z1L@8P1w^ygjJJ(-mT3E zU*}yNaNny9U?3{Inu8urBa~v$@@8l}0KZ%RrtE46H}R@RxW^NVe(!bs0DSR1SAazP zOt>dPoWwPtBC+55&!jfsKX}hmFYVT!;l4sdFny$n1#GNjljy zqgvy2UHHbaY2@%NEA3348VPekRaeY%_YPuv`6*o*@UZfQ1*?)xQ zFjtRSU%^_6i-gmE{w^imjE<-QbNHe7MU{diT=k{3RrYNl&-N*jmBz6vya_Bj(i5@s zBEw)GWWTV5S{_|oHNGWUOMG!5>9!1Ot-h*MpvebPBdJ=XB}O}~Z&(xeFf+!Qrz5`p zd4o`NQKOIFEgW@0;rG84)P?kS8#xbltqQ-I2r(J6WxOjnfQhb^%O|TgGxzeDl8JP@ z+y1gg=lg-Ms+r3^JIkl)s#$B!5fe%D4l^E}7{}^fhbT58n@crrH@LfQ@~`$g&G5JC zdx6{{1*Y!*{-Pd5ugfT3nRaU+FVgYYgG)}e6z0qmbQzm}0ioKfcwb(EmGg3!kNMkG z?NnX4@e8W?aejDm4c%!%-^;x%_;JF}+YEvhm1nag?z{QkFhc6*kS;V^?l06@^iPZl zyHp5ml-+Y(%ffBKUMUuv_aw+M!tZW{%expzS$?a0R(xd8%SpoSt7((Nkghh>K{ zq3nvC11wLkqci7TOTzWukO#l=oNl2blZw>{^n}M_z|*MZ3%pn|wwqHuKto>Yo=>p+ z&~?j4Mg0PiVTmm#9ctRQP*K^-mr7l8H506&xAVra2xBxVlc8VFZ1`zo(seXEray{GX^l0&zs)4*8pK!|p>Rk&U9 zb*U#T)Cwjp8Ik0mgFDSH9vpqqp7c_gFDToW%)UG$fq|*@iVtb)Hl6*fY&b8^a_b-1O%{K(2ZC0+!&Qn4yf`RA3g=-HATEe^th@IRjAB-T7ltxS{J&2?Q&@ADbh-MncjLN{0z#OT?AT zRfUUjb-{V&k+o&x8n>0PnD6UP6)pqxU%IQN!`$6iW}D%-s~L7bjqL)r(bdzhDx6Vs4@)#Cx$YDhJ7H@I?sp0OHM94Ab5$ z=wG;79TZJBL=|Ee?PscQBC^%mWKVAl+Nge|l_XegiRjyRnMJsQ4_s8u*3}uva*1|c zr=!iG=G@Oj4;EPYJS|XiCm%bL8#7spx)q?73H2Mez51Q&6THU&`yA-2LcG`v!%gkp z*r83=OsoOnx3@3P0KEC#vGgfQCHre{e<}TV--IYXM>)9$$gy6=89|~c?3vFFAMU8f zOeQN<4v=5t_Ow=Rq%-nPis4=vcM@~l(Uu7vWh4O(63hICz3xN^SMkJ1@+0%7kg7m*P-|9GB0+D?Wf#c0PwFEBxw~7LvGD*7JvmpZq|( zT)`(jajWF2<^fe7#L-o`RL13ux+U`2CW-(wayLb4*ZjTldv~!I6@|$>B`xM6N~3~0 zWELDV_0MgD`!buvWfc#a!!yyIuoS_%1j?OUi3rWn#sbG+$z%KA>h|!Vm{^Bo;jV&9 zQ=9W`bY^Qw3ErzVXuNm7F{xAeZW!#&`Rr~mZs1|wJ!TE8$B-kA^(v=+FY)X6YDSx~ zxGqx)aUf{GS|gw%r2AAjHuBt%wlMkvitNUGwNR~`E^*!d>(M4Jh57Ja{6=LoOG{U( z%Ffpp(cOvF(=w+w0sCXliY4GIPb`guClG*JzDf+IC8%iX5ochQShiz&=nm;q)he-$ zytLna(O$V@jvL*}c9ma?jh_qe60gH3+_tA>J{)~U7H*FMa={>St}#XVCK;2D-+`l0 z29ZW5_tF{sre7!m=I6ST&un<>rtT1hH8+J5z=1O^RAVgL=HStfxu7xDEfFWhM~h>k zr2gIuJjlM<`lH$N^sLy|?%6JTbl|&GzNj?mp+#C3k@1!8a_3fbF2l0LRf$I67>szm45-jA3!OuvfOzac#i&;Rl z%bB?V$Jcnp=?x{`6rx+`NBYZ`x!3XjD?DhMt6EhOC{6$cnt=7}N2&M)o%`Ic;Pigm z=2#lPAdAD0{dZ)m^;FaPmG?kETL)vUZ@6+4kOZcs*Wz>^bbCgPMHYxB2=`W`|MS5i z46SUG)2u>RQvYXHd4sto+F&oHKiv%G@^eiP{wB@OY7{(5m2Tfhh#GM?t*e<(#t0 zG;~*Yx?Z%i55~IO5uGbrYj`?sx0w~;er>o6Lr&Y}kP=xhsyM|^+5{eXQ&qUeY4ulg z&D6fpzj%ZnKxV_0eXeNLeMN5RsdMr4me|_*B;(r6)9Y(;$;S{c1dSC1Qy^&rdE7~C zoGqPIjs`jYT@OB9GE`rC=kz?KXp4M^uyO|~jF+L(f1oyFdRX^#oFnV1Gn#erpk5{i z^Uv-3g9(LmlN^?^nz*(Z$iQ_1qol{PGIj~|n?vL)WVz{Jr88`UuOzHvYew5qP`-8dZ$fHv)}9ZmMEZPt@|p9eHu&}98jBj0XBq(p*A-&K%(Vvw zSdW#^{t0}4^4e3N{=wX5m2dP<{;JLalo}Q6Pq z&oWQ_Az)8)YWqTz7d#S9PVkCc`nBg;=U?K94;+h6ag@7%aV0xkdpQo!21j2+VGFs@ zs&BsS61|idUb^&asJuhufkC_*FJGLeX>Q7Z(#v)A;F?%fUB%+r^&c#Mml(Pf?JjvY z@hexlnCiSWkmjfTA0z?kTCP{GZq zVA~eLt#ykmoJlTpqP}NM-ql&9NK!NBh|H#p9$Ucq1w>Mb{ECuZB;C+fuyj}YS~*+> zH04FKp_DYBdnrY$XSVF81BUr1R43&lO~&iSqucA8I*Pps)~*5Jxhz2&vc^c{Rb+KS zXx3XMp_1957_i)qmnr2!evDS_Tuzj7GOOoU0Cp)a8WXUS`fRw2pQQY1<&|rd8KT+# zj9%yB(HckihpdMim_m%<{kL06o33TGS5|@JRK13R^w1KOD_5{Y=+=%paUg%^nx^Wf z813@YF_(e)BDMXmGd?6_E=d|W~gFiudLWQ9EF>?geH`k_N z@KY$({^h>J51awpp^k;pIdu{FG9e~uorj}RkA2L7FHigHm(3Y|@!0)hzDL9^bTJBjco&XZi|F&1WIg)G4$A%p2VLU)8jb}R zGilsJ&R%9s#n0nOywkaGtecF@8Cx)SGN5H|?4ko!4WNS4>k=f^0tTdPeAL#8TbJf* z8KSPp6~?Q-GqGhiI5YCQtf-bNl2LCEi52zKpO!nmbsjJ{*PeCFfdN$9SMcU?ye~F~ zB}061M)$L7OGkVNi4&Oddks93#?2Q=~}Wvodr?TZQ5&dLZ9qR9D=3?y?vBA z}kh}t<(@NBRt=esDd4q1PHDUmKyEVnkuguP3LV|2wh_t6vd|cf?At4 zRg1TS$z3dZ+XdV?V58mX^qtTMc$C;b7RWaVyadmTm5`k|h+POi1lNY-`_5FF7j8R8 zpTB~t+}=Wu~s&rNaf{>mdDJECGCBm9v=qBPpHpaqGK8-s+I<4 z2d%eaBu<7v0?f+>tk%MfxvrS(0c89yXzR&}6iD*;x;7q>IdkA7YL{YY_@X73 zYuJ?xa-`-WFZw+?OI)`T&A+{m?7AH(V9=Z!bj4b7Z1#6f)rpC;4(;PS3z?jD^z z#8Jr}_~Cw~v)!P{Y@B7Wa2O-K{+kA@K)?td`P{$C%I{Z8*!I|!WlZ;2Y~mrZTl&m4 zrZv|V3eq)&$<)29T7$JG%~GepNN=ccfxWe0B zNybS*sP8EB$_OS06ELuArs&2Ep;_8fs(81qzOtJbHz-j;Uyq;g4lhq1Q|x#r6!8#_HV7d$gNY3T2L-r=S*PJEL!t&B;YCZWt68rTIrvrifo z_(h8Ol|}htOV&c-fq`dXZg~3`@_@wL#(K+;|C|?+8PY<2yY+fa9 zQI(8j;ZvbzBj4MxwPOX4RyOk zVZE18{k?k}a-oYN$BYM{D46Dkevh()1WG2*V%>1xq*Lnd zlzpi;@yQiJzaASkdH2t;#YJvRt#!miw&%BU14En6tTvu`5_n6Ndr2EfFa3Ng2>BGUsLEdUUGDni zr}z2&<4U|$|2oQzCU-supLlX{QmQ-)17v({M=<=n6ZmoT$Yl`~idK|!ft6?C>A|>I z6+rxo#p%<1`AEX(M-8wB$P35>r%X)bZ4VWO?^~8LFVd={67nlP|9S<8Dg@;tWqz{d z)T-cKMZn37$VP1j~7E>Le5gfRhi%ihC|kf_Ky(Re8ym*ExF~aM?dXTTnH^ zHaRPb^&MSiFbq1(Hq*!SF&e2GZT~h>*d7@dcm1cjTmg>Nbn`hLPVmG3WVqIfOdY~= zl~EqB5s)fICv!$ekzqq5aVU-FVZElkECdyN<^v^9Fr+`go*eXcjv29c{tUNg3I{DyTUi#8xwpki`(nRnfr zjo%Gt;5{jgAr7jrW>a?pD$U3Xz1m=V%f*bJ}!vzvL|E$JV+IJ%i%8P2zZ)%YX7bo-;mG>4S?gUygWa zkEU}jJ4b>G9V)>-q5^FhM~MfB|)y6-NVhfw~i3>;~2on6=R z%$AscNF-V!LY4lOPt_;`x%V`xn+$$jA-fpy=|8x;e$EoeSmJPZmj!1v&uRRo*i;@u zCHf1sPga}TD}fZv4X4=WL)RsB7LI60&K2Fm6r8H?PFeebcBtZ1UlMQs589>jvh%Q^ z{X;to*%#1}hPc)a2EXwBZVmqBdKR132SO1^9sH!?eG|7pd$C5SUybERggV1UfdjP> zlRDbrr~gI{kg?PEvFN>0V@swnDHabozj4bq3tfY(uh?*uM1Bre@zsfLupO=^I3qLx zrW+Z$)x4h3f84cEr1ZY{2x?uSwL&-hvZ;G2glp9*p9}t8r%8dmU~s;7(p^_!)_)OqW zxuFP%DS&I)??+>Yrn`~nWp3m+f>p66d@T&HDGOLKj9 zq!`WvRHzf531(*N1p`e4ER*zeq7m&SYB_u|!|XwFF=h*xYa*M`-5~Fm1qaUFl87Xq~1if+r`5%4?PqG!y0aE_%rGBJgt_m(fBk=dleM2P8`LP{wI$xHxu!h&D z9C>$M7OCAKFO8ZsF8M=6#h%Ne8Fyh3gww&+4Sw0vy%|xkTr1+hOFTx&dE$yWWO#s=6gKv9WFs6Z< zSf{B6G4-dKjF+p9%~&!6N9C3r{R-9G$pinxorKV~(50raRP0brl1VQcFF+YIJONN~ zvi-(q+U^x+3kya3)Vm^Fq9MT|!U}d&9f+(IOCdia@3|49v1`PPqSkS6kP4O?hU-s; z=b_xW7216yE3K;wnc%lZBLkBzjLJQ4=rGN$dvJV-Szn_LC*-q(KX-b0&NpLoF28eR zd~R>3R(~+eL(br8s^MC9{Cd3QoqeV1PAYz!Soc=#`6RI5+)|4nX7bDOMwlT)k`!^3 zhK}hYzHrDVcY9V zpgMcyVq;P-Qd15k8hOxYupCM3(T=bnW}y@erli?+fcnm@j4zixi#h+8PmonAZ9|qS z!hEu_CpY*cLfDB<8PdXDC zh!exo)-+`~ZdY`Z>e#_0M_J1WD}w3J+>KFT&Ta;6pLVbZsfiWlO99nIt|B-W&7P!w zrUwh)ZX#q}(PD8Rm1K>Sne)c_`9lba{@yPlxV%FLsV-Qf*K)fG5WXc2F_4)MAm^yk0YvA7%DDxUHD4_)c3GxWd8B1jvFSv z=alBJ@E>2UO8wUeb4~bWw)gP8cINT(4S*{Alv(}q|83Wgmk}|XY_)g~c73bc_gN-+ z(MAwBR-{Msi7$)Dvk^9+Kp5a&xxrr$Wm7o?ky7c8=Qu-Tcc*LBT@8ungG31f^t$!K z13Svcd~nyU8@^o2#$-|A?gTy17N2CqQLxK}nvX%+JlZsIjJnQN6$Vm^j&-lql<=Bs zht*Ko)r^2DvBYnm@V9{KlgFAw3aQ!=B(`tGRigq7h z(Z3AJNb64yloKVUgeo&-jLQVGYn_5QD^uA5)3mw9v@U~hB`rOlFDy7_5p-{a_eMvF zza7W3O517{E16J88;>4?zJa#H6WuOfhVZ$i=RpY)%Cnm9mu-{TU0skmzWEf~`#ibA z5)N-K^U{BK01%a53mUbid>M#F@O=-Tj>?8kt|eXFHu%tn{i?l58rhEqN6Ma+$HNGW zWp+mmA(oTp?<6ZgSyv?}fj3VcG|WkTo!X}U-DT=FATnnBp?b^mYB2RRo0IRrLDdTY ztfU=Tn6t=YqpL~I{=5)}9+4U~FnF44e1JkX_&1Uq!CCY&0W}VW z+`%d>a%N$kVcb*?eW;}4UPUiZjsgs~q`C^Pi>lnB0-G+}$U%tkL~VK-H*Ny_&9_ub z@#{<*8NN@vFNJA z9M_h{e|Me4@#`hm~^G&7aJYA?9=U zVaY?w#h|^CP#l|djFr_g_fIl|`1W@fAH-oF_ShAL_>kqWw%zM+<$)&Fnz01KWESQPKuuR z0dTXTbY}Kc-l5nspzcsq=b=}lle>mK4jIWOrL+`6HH5TI{x~Z~InHqoQa0udHB@{^ zE-9yi*d2c4Eo>pUcsxZCaq`T$t~MIfbzIIkm%5YF<0pI^?O!H_`pE>Qn4tr45m%Fb ziSOc58bDGt706V8zG3l5B}{RbI%9mOQAI6#kLX+x*r^Cx=k0Sh;%XG4L-Rzyy@q=t_*f)RaXz;i@{xa|8#M? zj@?y)tf<)MbH`umDxft2`xi2$9B!zJs5e;Uatae(AN?QH{l-(zpfd3fbz^0|&g_ch zT={RDK^F+P9KkcYto;UtwuA4a`i0o2*8ND=Mv^F*jSX6C{CU*spA&VL5PU`Po}p;* zJ-LV)3_L%VYQP;D?km61|iTTCyPn>lT2i#fU;+^lvO!hDB~^j^eW8 z|K^#n?mMaD=8lIvv!N&Vz14|re!nZ9BW*#-ev_3mveb@wnL=h*eE3Xn)5)0wOrGhL zB&xKzKery;nD!^lq>3g6pB~$=8$*!4kDzxWcr(t|oGCv+%fUa?-hpc;Y@%p$njRVz zn%P_i3GGL=-6|H9^y9R+54~GM`Uj6HA%Ltae*Z=}@Z*WCvr1vckD6Vf7nADCIz^s_ zO}ZYOxh*VOOuMqQG5EqUUVhRA5utj8<^@Q0kvYjB#jQPd-kOB8MT!~R$1XYbq%{fa zr@r&YzH9k%1)ZVO^%V{gk+fCF-B(j%-VbLruuwp}bWc3lmJP?`fN&H-@ShyC_F$i; zM;haA1RBOVMbCk+cH!P%gBbHullM=3)OzAj-ElG0Pjc-k6N_^jeH%7_zJYtBbjeS; zmr(1`uAGfSs+;YX?OUyIZ4Yl#)OSCEPD0^HwEf56F@fFQP$-ur)j!(ia_-*KA|c?EN@lvP)`ks@oNb9 z7-!Wy{*$lf=t?t%T^6%sNGnZgKH@aL>;?L_dd@%LB=;e-it9oSt0M>L-|iv z{PjF++o$6-(a?VU0g_YTiUyy?*Y|(o(+sqoXk~sSGecIbGa{{QKR#smL^Z+7evx^t z@YQ;@il7qy>Tioh*4HW0>eb9;&WY^&-YZkWJvag6)Eb6b8zfOZ#2K1jxHu|OaTKzB zE@dab3EOo}Ywp}3Wd>85HUu?OmL?Ohi8()U)(g36ENq2U^%GJ9ysjtZL(@V0_Zj8c z6N{;=8p#y6l|I;EU4V_k6qj>XompR;gH9HFfdBSPRt{1735`EkV3!SdhbOQSc?v&*;EmVZh>`%OqKb{fhzq|O!tQFs zbEFb4{zv6KS?rViI1Cq^&xBPyJl%Q25{^n zTMGySDMCQLrb}^MG~p?Od0C`nE&HT~g}8N5|ELm)c?gR;pfEi(?z{WpB&_Mx22-6X zEKBKH@uKuTv>(2smD_(wzUQ1G7iu9-`tHHS%{Q}4i}zwj@IFM0lQ zn3Y4&QTQ*1SsyrTwvI}q{Ha}*-Jlfv^|;78M?Y-|^4VI{W~by+H~o%)LSbSVg%@^G zB#3Sz9&SK)-Riv9LJjie;8T0l>x)!?+RP7e6!DE_`k0p_1z0EI0J$P@`rucgCGWLp zB6gGUV*)?fQU;jIc`seSx`S*VDi+VpYDV50HIgq{P@c;U@6_rdv%aIg0`!wDgP1PM z$>*^0YUF$O0@1r(k*# znNh#d`8YBuCs?^JOn7n_SnK#m>>)6P;aow=ypFzO!hX?66UV&IX2quvbo*tjynanx zbefm=2gbr~?i`*?nu}Gg8lgBd(zhhETvTZ|oKL)iL40nU3}5pmA3?Je=`lyB3WZt3 zl)x{Hm0EZrqVHLcF&}i?0PHiBNj|d?B-xtcJML~9bn)(k)JknY&9q3k>T2bH*s??) z@pdDA&+n92=BzBxpT&6A^xEKNk!4i?S2jtPi~GYYsu&lRAGff>W+d{IbhU?&*}#g2Um}^$!w0id-AUYfu`%}grrBm_ ziSh>6F0*gDQ20Op&asiX{E>h|3M{zJxwyc8j?d!02t0kGrU(1A(-1t0rj~Qz;$J7g zDD9Tk*5AuVI^BgpQlG>_E0R)H@P^%=(L7Y^e~r6)rj(R4OyS<%G46p_!Z2}M>YQz@SRFxbEM?D z6`%bLkw0%qC#%kHofdVX|5bCv2)p0Je>wmXAOBXnX(JOD+8xU#!1l|L*5M%?T!jAY{?Q{f&E2 Date: Sun, 1 Feb 2026 13:39:27 +0300 Subject: [PATCH 4/5] feat: LAB02 done --- app_python/.dockerignore | 22 ++++ app_python/Dockerfile | 14 +++ app_python/README.md | 27 ++++- app_python/docs/LAB02.md | 243 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 app_python/.dockerignore create mode 100644 app_python/Dockerfile create mode 100644 app_python/docs/LAB02.md diff --git a/app_python/.dockerignore b/app_python/.dockerignore new file mode 100644 index 0000000000..44fa25304b --- /dev/null +++ b/app_python/.dockerignore @@ -0,0 +1,22 @@ +# 🐙 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/ \ No newline at end of file diff --git a/app_python/Dockerfile b/app_python/Dockerfile new file mode 100644 index 0000000000..0364cfd9a7 --- /dev/null +++ b/app_python/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.12-slim + +RUN useradd --create-home --shell /bin/bash appuser + +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +COPY app.py . + +EXPOSE 8000 + +USER appuser + +CMD ["python", "app.py"] \ No newline at end of file diff --git a/app_python/README.md b/app_python/README.md index 74ffca1c2b..2475567ab9 100644 --- a/app_python/README.md +++ b/app_python/README.md @@ -38,4 +38,29 @@ The application is configured using environment variables. | Variable | Default | Description | Example | |---------|---------|-------------|-------------| | `HOST` | `0.0.0.0` | Host interface to bind the server to | `127.0.0.1` | -| `PORT` | `8000` | Port the server listens on | `8080` | \ No newline at end of file +| `PORT` | `8000` | Port the server listens on | `8080` | + +# Docker + +## Building the image locally +Command pattern: +```bash +docker build -t : +``` + +## Running a container +Command pattern: +```bash +docker run --rm -p : : +``` + +## Pulling from Docker Hub +Command pattern: +```bash +docker pull /: +``` +Then run: +```bash +docker run --rm -p : /: +``` + diff --git a/app_python/docs/LAB02.md b/app_python/docs/LAB02.md new file mode 100644 index 0000000000..977e742f10 --- /dev/null +++ b/app_python/docs/LAB02.md @@ -0,0 +1,243 @@ +# Docker Best Practices Applied + +## 1. Non-root user +**What I did:** +Created a dedicated user (`appuser`) and ran the container as that user. + +**Why it matters**: +Running as root in a container increases the impact of a container escape or a vulnerable dependency. A non-root user reduces privileges and limits potential damage. + +**Dockerfile snippet:** +```dockerfile +RUN useradd --create-home --shell /bin/bash appuser +USER appuser +``` + +## 2. Layer caching +**What I did:** +Copied `requirements.txt` first and installed dependencies before copying the rest of the source code. + +**Why it matters**: +Docker caches layers. Dependencies change less frequently than application code, so separating them allows rebuilds to reuse the cached dependency layer and rebuild faster. + +**Dockerfile snippet:** +```dockerfile +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +COPY app.py . +``` + +## 3. .dockerignore +**What I did:** +Added .dockerignore to exclude local artifacts such as venvs, caches, and IDE folders. + +**Why it matters**: +Docker sends the build context to the daemon. Excluding unnecessary files makes builds faster, reduces image bloat, and prevents leaking local secrets/files into the build. + +**.dockerignore snippet:** +```.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/ +``` + +## 4. Minimal base image +**What I did:** +Used `python:3.12-slim`. + +**Why it matters**: +Smaller images generally mean fewer packages, fewer vulnerabilities, less bandwidth and faster pulls/deployments. + +**dockerfile snippet:** +```dockerfile +FROM python:3.12-slim +``` + +# Image Information & Decisions +## Base image chosen and justification (why this specific version?) +**Base image**: +`python:3.12-slim` + +**Justification**: +- Python 3.12 matches the project runtime requirements. +- slim variant keeps image smaller and reduces OS packages. +- Official Python images are widely used and well maintained. + +## Final image size and my assessment +**Image size**: +`195MB` + +**My assessment**: +Further optimization is possible (multi-stage build, wheels caching, removing build deps), but for this lab the size is acceptable. + +## Layer structure explanation +1. Base image layer (`python:3.12-slim`) +2. User creation layer +3. `WORKDIR` and `requirements.txt` copy layer +4. pip install dependencies layer (largest and most valuable for caching) +5. Application source copy layer +6. EXPOSE, USER, and CMD metadata layers + +## Optimization choices +- Used dependency-first copying to maximize caching. +- Used `--no-cache-dir` with pip to reduce layer size. +- Used slim base image to reduce OS footprint. +- Added `.dockerignore` to reduce build context. + +# Build & Run Process +## Complete terminal output from build process +```bash + docker build -t python_app:1.0 . +[+] Building 5.4s (12/12) FINISHED docker:desktop-linux + => [internal] load build definition from Dockerfile 0.0s + => => transferring dockerfile: 277B 0.0s + => [internal] load metadata for docker.io/library/python:3.12-slim 3.7s + => [auth] library/python:pull token for registry-1.docker.io 0.0s + => [internal] load .dockerignore 0.0s + => => transferring context: 289B 0.0s + => [1/6] FROM docker.io/library/python:3.12-slim@sha256:5e2dbd4bbdd9c0e67412aea9463906f74a22c60f89e 0.0s + => [internal] load build context 0.1s + => => transferring context: 62.28kB 0.1s + => CACHED [2/6] RUN useradd --create-home --shell /bin/bash appuser 0.0s + => CACHED [3/6] WORKDIR /app 0.0s + => CACHED [4/6] COPY requirements.txt . 0.0s + => CACHED [5/6] RUN pip install --no-cache-dir -r requirements.txt 0.0s + => [6/6] COPY app.py . 0.8s + => exporting to image 0.4s + => => exporting layers 0.3s + => => writing image sha256:a3d1dd41a468a1bb53d02edd846964c240eb160f49fd28e9f6ad90fc15677c52 0.0s + => => naming to docker.io/library/python_app:1.0 0.0s + +View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/djiu836gkk9g7syuakivz5ynt +``` + +## Terminal output showing container running +```bash + docker run --rm -p 8000:8000 python_app:1.0 +2026-01-31 18:29:56,977 - __main__ - INFO - Application starting... +INFO: Will watch for changes in these directories: ['/app'] +INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) +INFO: Started reloader process [1] using WatchFiles +INFO: Started server process [8] +INFO: Waiting for application startup. +INFO: Application startup complete. +``` + +## Terminal output from testing endpoints +```bash +curl http://localhost:8000/ + + StatusCode : 200 +StatusDescription : OK +Content : {"service":{"name":"devops-info-service","version":"1.0.0","description":"DevOps cours + e info service","framework":"FastAPI"},"system":{"hostname":"3a77a23940f7","platform": + "Linux","platform_version":"... +RawContent : HTTP/1.1 200 OK + Content-Length: 755 + Content-Type: application/json + Date: Sat, 31 Jan 2026 18:31:18 GMT + Server: uvicorn + +Forms : {} +Headers : {[Content-Length, 755], [Content-Type, application/json], [Date, Sat, 31 Jan 2026 18:3 + 1:18 GMT], [Server, uvicorn]} Images : {} InputFields : {} Links : {} +ParsedHtml : mshtml.HTMLDocumentClass +RawContentLength : 755 +``` + +```bash +curl http://localhost:8000/health + + +StatusCode : 200 +StatusDescription : OK +Content : {"status":"healthy","timestamp":"2026-01-31T18:31:26.924513+00:00","uptime_seconds":89 + } +RawContent : HTTP/1.1 200 OK + Content-Length: 87 + Content-Type: application/json + Date: Sat, 31 Jan 2026 18:31:26 GMT + Server: uvicorn + + {"status":"healthy","timestamp":"2026-01-31T18:31:26.924513+00:00","uptime_... +Forms : {} +Headers : {[Content-Length, 87], [Content-Type, application/json], [Date, Sat, 31 Jan 2026 18:31 + :26 GMT], [Server, uvicorn]} +Images : {} +InputFields : {} +Links : {} +ParsedHtml : mshtml.HTMLDocumentClass +RawContentLength : 87 +``` + +## Terminal output showing successful push: +```bash +docker push newspec/python_app:1.0 + +The push refers to repository [docker.io/newspec/python_app] +8422fdf98022: Pushed +56a7b3684a2c: Pushed +410b7369101c: Pushed +4e7298e95b69: Pushed +b68196304589: Pushed +343fbb74dfa7: Pushed +cfdc6d123592: Pushed +ff565e4de379: Pushed +e50a58335e13: Pushed +1.0: digest: sha256:9084f1513bc5af085a268ee9e8b165af82f7224e442da0790cf81f07b67ab10e size: 2203 + +``` + +## Docker Hub repository URL +`https://hub.docker.com/repository/docker/newspec/python_app` + +## My tagging strategy +`:1.0`(major/minor) + +# Technical analysis +## Why does your Dockerfile work the way it does? +- The image is based on a minimal Python runtime. +- Dependencies are installed before application code to leverage Docker layer caching. +- The app runs as a non-root user for better security. + +## What would happen if you changed the layer order? +If the Dockerfile copied the entire project (`COPY . .`) before installing requirements, then any code change would invalidate the cache for the dependency install layer. That would force `pip install` to run again on every rebuild, making builds much slower. + +## What security considerations did you implement? +- Non-root execution reduces privilege. +- Smaller base image reduces attack surface. +- `.dockerignore` prevents accidentally shipping local files (including potential secrets) into the image. + +## How does .dockerignore improve your build? +- Reduces build context size (faster build, less IO). +- Avoids copying local venvs and cache files into the image. +- Prevents leaking IDE configs, git history, logs, and other irrelevant files. + +## Challenges & Solutions +# 1. “I cannot open my application” after `docker run` +**Fix**: Published the port with `-p 8000:8000` + +# What I Learned +- Containers need explicit port publishing to be accessible from the host. +- Layer ordering dramatically affects build speed due to caching. +- Running as non-root is a simple but important security improvement. +- .dockerignore is crucial to keep images clean and builds fast. \ No newline at end of file From 26a6262c41cc98767451a8016ba79a51ea54d67d Mon Sep 17 00:00:00 2001 From: Maksim Malov Date: Sun, 1 Feb 2026 15:14:18 +0300 Subject: [PATCH 5/5] feat: lab02 bonus task is done --- app_go/.dockerignore | 9 ++ app_go/Dockerfile | 14 +++ app_go/docs/LAB02.md | 215 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 app_go/.dockerignore create mode 100644 app_go/Dockerfile create mode 100644 app_go/docs/LAB02.md diff --git a/app_go/.dockerignore b/app_go/.dockerignore new file mode 100644 index 0000000000..52c65fe81d --- /dev/null +++ b/app_go/.dockerignore @@ -0,0 +1,9 @@ +.git +*.md +docs/ +bin/ +dist/ +tmp/ +.idea/ +.vscode/ +.DS_Store \ No newline at end of file diff --git a/app_go/Dockerfile b/app_go/Dockerfile new file mode 100644 index 0000000000..6dd906a964 --- /dev/null +++ b/app_go/Dockerfile @@ -0,0 +1,14 @@ +# 🔨 Stage 1: Builder +FROM golang:1.22-alpine AS builder +WORKDIR /app +COPY go.mod ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 go build -o myapp + +# 🚀 Stage 2: Runtime +FROM gcr.io/distroless/static-debian12:nonroot +WORKDIR /app +COPY --from=builder /app/myapp . +EXPOSE 8080 +CMD ["./myapp"] \ 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..ee98527851 --- /dev/null +++ b/app_go/docs/LAB02.md @@ -0,0 +1,215 @@ +# Multi-stage build strategy +## Stage 1 - Builder +**Purpose:** compile the Go application using a full Go toolchain image. + +**Key points:** +- Uses `golang:1.22-alpine` to keep the builder stage smaller than Debian-based images. +- Copies `go.mod` first and runs `go mod download` to maximize Docker layer caching. +- Builds a Linux binary with `CGO_ENABLED=0` (static binary), which allows a minimal runtime image. + +**Dockerfile snippet:** +```dockerfile +FROM golang:1.22-alpine AS builder +WORKDIR /app +COPY go.mod ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 go build -o myapp +``` + +## Stage 2 - Runtime +**Purpose:** run only the compiled binary in a minimal image with a non-root user. + +**Key points:** +- Uses `gcr.io/distroless/static-debian12:nonroot`. +- Distroless images contain only what is required to run the app (no package manager, no shell), reducing image size and attack surface. +- Runs as a **non-root** user (provided by the `:nonroot` tag). + +**Dockerfile snippet:** +```dockerfile +FROM gcr.io/distroless/static-debian12:nonroot +WORKDIR /app +COPY --from=builder /app/myapp . +EXPOSE 8080 +CMD ["./myapp"] +``` + +# Size comparison with analysis (builder vs final image) +## Builder +```bash +docker images newspec/app_go:builder +REPOSITORY TAG IMAGE ID CREATED SIZE +newspec/app_go builder 21b01f8c6103 37 minutes ago 305MB +``` +## Final +```bash +docker images newspec/app_go:1.0 +REPOSITORY TAG IMAGE ID CREATED SIZE +newspec/app_go 1.0 a944205e6030 59 minutes ago 9.32MB +``` +## Analysis +The builder image contains the Go toolchain and build dependencies, so it is significantly larger. +The final image contains only the static binary, which is much smaller and safer. + +# Why multi-stage builds matter for compiled languages + +Compiled languages (like Go, Rust, Java with native images, etc.) typically require a **heavy build environment**: compilers, linkers, SDKs, package managers, and temporary build artifacts. If you ship that same environment as your runtime container, the final image becomes unnecessarily large and less secure. + +Multi-stage builds solve this by separating concerns: + +### 1) Smaller final images (faster pull & deploy) +- The **builder stage** includes the full toolchain (large). +- The **runtime stage** contains only the compiled output (usually just a single binary). +This dramatically reduces image size, which improves: +- CI/CD speed (less time to push/pull) +- Startup speed (faster distribution in clusters) +- Bandwidth/storage usage + +### 2) Better security (smaller attack surface) +The runtime image no longer contains: +- compilers (go, gcc, build tools) +- package managers +- shells and utilities (especially with distroless/scratch) + +Fewer components, so fewer potential vulnerabilities (CVEs) and fewer tools available to an attacker if the container is compromised. + +### 3) Cleaner separation of build vs runtime +Multi-stage builds enforce a clear boundary: +- build dependencies exist only where needed (builder stage) +- runtime image stays minimal and focused on execution + +This makes the container easier to reason about and maintain. + +### 4) Reproducible and cache-friendly builds +When the Dockerfile copies dependency descriptors first (e.g., `go.mod`/`go.sum`) and downloads dependencies before copying source code: +- Docker can reuse cached layers if dependencies are unchanged +- rebuilds after code changes are significantly faster + +### 5) Enables ultra-minimal runtime images +Compiled apps can often run in very small base images: +- `distroless` (secure and minimal) +- `scratch` (almost empty) + +This is usually impossible for interpreted languages without bundling an interpreter runtime. + +**Summary:** For compiled languages, multi-stage builds provide the best of both worlds — a full build environment when you need it, and a minimal secure runtime image when you deploy. + +# Terminal output showing build process +## Builder +```bash +docker build --target builder -t newspec/app_go:builder . +[+] Building 2.4s (12/12) FINISHED docker:desktop-linux + => [internal] load build definition from Dockerfile 0.0s + => => transferring dockerfile: 349B 0.0s + => [internal] load metadata for docker.io/library/golang:1.22-alpine 2.1s + => [auth] library/golang:pull token for registry-1.docker.io 0.0s + => [internal] load .dockerignore 0.1s + => => transferring context: 105B 0.0s + => [builder 1/6] FROM docker.io/library/golang:1.22-alpine@sha256:1699c10032ca2582ec89a24a1312d986a3f094aed3d5c1147b19880afe40e052 0.0s + => [internal] load build context 0.0s + => => transferring context: 146B 0.0s + => CACHED [builder 2/6] WORKDIR /app 0.0s + => CACHED [builder 3/6] COPY go.mod ./ 0.0s + => CACHED [builder 4/6] RUN go mod download 0.0s + => CACHED [builder 5/6] COPY . . 0.0s + => CACHED [builder 6/6] RUN CGO_ENABLED=0 go build -o myapp 0.0s + => exporting to image 0.0s + => => exporting layers 0.0s + => => writing image sha256:21b01f8c61038149b9130afe7881765d625b2eb6622b6b46f42682d26b10ae2b 0.0s + => => naming to docker.io/newspec/app_go:builder 0.0s + +View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/wvw3g1yzoqu1uput2mz8me7zx +``` + +## Final +```bash +docker build -t newspec/app_go:1.0 . +[+] Building 1.5s (15/15) FINISHED docker:desktop-linux + => [internal] load build definition from Dockerfile 0.0s + => => transferring dockerfile: 349B 0.0s + => [internal] load metadata for gcr.io/distroless/static-debian12:nonroot 1.0s + => [internal] load metadata for docker.io/library/golang:1.22-alpine 0.7s + => [internal] load .dockerignore 0.0s + => => transferring context: 105B 0.0s + => [builder 1/6] FROM docker.io/library/golang:1.22-alpine@sha256:1699c10032ca2582ec89a24a1312d986a3f094aed3d5c1147b19880afe40e052 0.0s + => [stage-1 1/3] FROM gcr.io/distroless/static-debian12:nonroot@sha256:cba10d7abd3e203428e86f5b2d7fd5eb7d8987c387864ae4996cf97191b33764 0.0s + => [internal] load build context 0.0s + => => transferring context: 146B 0.0s + => CACHED [builder 2/6] WORKDIR /app 0.0s + => CACHED [builder 3/6] COPY go.mod ./ 0.0s + => CACHED [builder 4/6] RUN go mod download 0.0s + => CACHED [builder 5/6] COPY . . 0.0s + => CACHED [builder 6/6] RUN CGO_ENABLED=0 go build -o myapp 0.0s + => CACHED [stage-1 2/3] WORKDIR /app 0.0s + => CACHED [stage-1 3/3] COPY --from=builder /app/myapp . 0.0s + => exporting to image 0.1s + => => exporting layers 0.0s + => => writing image sha256:c9ff1572d8a13240f00ef7d66683264e0fbf4fa77c12790dc3f3428972819321 0.0s + => => naming to docker.io/newspec/app_go:1.0 0.0s + +View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/nvdyhylzo1hzpemy23lt42ll1 +``` +# Technical explanation of each stage's purpose +### Stage 1 — Builder (Compile Environment) +**Goal:** Produce a Linux executable from Go source code in a controlled build environment. + +**Why this stage exists:** +- Go compilation requires the Go toolchain (compiler, linker) which is large and should not be shipped in the final runtime image. +- The builder image provides everything needed to compile the application. + +**What happens technically:** +1. **Set working directory** + - `WORKDIR /app` defines where source code and build steps run inside the container. + +2. **Copy dependency definition first** + - `COPY go.mod ./` is done before copying the whole source tree. + - This allows Docker to cache the dependency download layer. + - Even if code changes, dependencies may not, so rebuilds are faster. + +3. **Download modules** + - `RUN go mod download` fetches required modules. + - In this project there are no external module dependencies, so Go prints: + `go: no module dependencies to download` + - The step is still good practice and keeps the Dockerfile consistent for future changes. + +4. **Copy application source code** + - `COPY . .` brings in the Go source files. + - This layer changes most often, so it comes after dependency caching steps. + +5. **Compile a static binary** + - `RUN CGO_ENABLED=0 go build -o myapp` + - `CGO_ENABLED=0` disables C bindings so the binary is statically linked. + - A static binary does not require libc or other runtime shared libraries, enabling minimal runtime images. + +**Output of the stage:** a compiled executable (`/app/myapp`). + +--- + +### Stage 2 — Runtime (Execution Environment) +**Goal:** Run only the compiled binary in a minimal and secure container image. + +**Why this stage exists:** +- The runtime stage should not contain compilers, source code, or build tools. +- A smaller runtime image reduces attack surface and improves deployment speed. + +**What happens technically:** +1. **Choose a minimal base image** + - `FROM gcr.io/distroless/static-debian12:nonroot` + - Distroless images contain only the minimum required runtime files. + - The `:nonroot` variant runs as a non-root user by default. + +2. **Set working directory** + - `WORKDIR /app` provides a predictable location for the binary. + +3. **Copy only the build artifact** + - `COPY --from=builder /app/myapp .` + - This copies only the compiled binary from the builder stage. + - No source code, no Go toolchain, no dependency caches are included. + +4. **Run the application** + - `CMD ["./myapp"]` starts the service. + - The application reads `HOST` and `PORT` environment variables: + - defaults: `HOST=0.0.0.0`, `PORT=8080` + - When running the container, port mapping must match the internal listening port (e.g., `-p 8000:8080`). + +**Output of the stage:** a minimal runtime container that executes the Go binary as a non-root user. \ No newline at end of file