Перейти до змісту

Частина X. Linux, DevOps і Deployment

Застосунок, який працює тільки на ноутбуці розробника — не production. Ця частина охоплює Linux-основи, Docker, Docker Compose, Nginx і CI.

Передумови: загальне розуміння проєкту. Рівень: Intermediate → Production.


Розділи

Linux

Документ Що вивчимо
Ментальна модель Linux kernel, userspace, файли як абстракція, PID, fd
Термінал та shell bash, zsh, stdin/stdout/stderr, pipes, > >>, $?
Файлова система FHS, ls, find, tree, pwd, cd, symlinks
Файли та права chmod, chown, umask, sudo, users/groups
Процеси та порти ps, top, kill, lsof, netstat, ss, systemctl
Пакетні менеджери apt, pip, pyenv, nvm
SSH та сервери ssh, scp, rsync, authorized_keys, jump host
Змінні середовища export, .env, os.environ, secrets management
Bash скрипти shebang, змінні, умови, цикли, функції, exit codes
Makefile targets, phony, make test, make build

Docker

Документ Що вивчимо
Docker та Runtime image, container, layer cache, volumes, networks
Docker: повний посібник Dockerfile, build, run, exec, logs, compose integration

Deployment

Документ Що вивчимо
Django на Linux virtualenv, gunicorn, systemd service, collectstatic
Nginx + Gunicorn/Uvicorn proxy_pass, WebSocket upgrade, SSL, static files
Логи та моніторинг journalctl, docker logs, LOGGING в Django
Docker основи image layers, registry, ENTRYPOINT, multi-stage
Docker Compose сервіси, volumes, networks, health checks, depends_on
DevOps workflow CI/CD pipeline, blue-green deploy, rollback
Kubernetes огляд Pod, Deployment, Service, Ingress — концептуально
Roadmap що вчити далі після курсу

Ключові концепти

Docker Compose стек Notes Chat App:

# docker-compose.yml (спрощено)
services:
  db:
    image: postgres:16
    volumes: [pgdata:/var/lib/postgresql/data]

  redis:
    image: redis:7-alpine

  web:
    build: .
    command: ["/app/entrypoint.sh"]
    environment:
      DATABASE_URL: postgres://user:pass@db:5432/notes
      REDIS_URL: redis://redis:6379/0
    depends_on: [db, redis]
    ports: ["8001:8001"]

  nginx:
    image: nginx:1.27
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - staticfiles:/app/staticfiles
    ports: ["80:80"]
    depends_on: [web]

  selenium:
    image: selenium/standalone-chrome
    ports: ["4444:4444"]

volumes:
  pgdata:
  staticfiles:

entrypoint.sh — startup sequence:

#!/bin/bash
set -e

# 1. Чекаємо PostgreSQL
until pg_isready -h db -p 5432; do sleep 1; done

# 2. Застосовуємо міграції
python manage.py migrate --noinput

# 3. Збираємо статику
python manage.py collectstatic --noinput

# 4. Seed demo data (якщо SEED_DEMO_DATA=1)
if [ "$SEED_DEMO_DATA" = "1" ]; then
    python manage.py seed_demo_data
fi

# 5. Запускаємо Daphne ASGI server
exec daphne -b 0.0.0.0 -p 8001 notes_project.asgi:application

Nginx конфігурація для WebSocket:

# nginx/nginx.conf
upstream django {
    server web:8001;
}

server {
    listen 80;

    location / {
        proxy_pass http://django;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # WebSocket upgrade
    location /ws/ {
        proxy_pass http://django;
        proxy_http_version 1.1;
        proxy_set_header Upgrade    $http_upgrade;   # ← обов'язково для WS
        proxy_set_header Connection "upgrade";
    }

    # Static files — Nginx роздає напряму (не Django)
    location /static/ {
        alias /app/staticfiles/;
    }
}

Environment variables — secrets:

# .env (ніколи не комітити у git)
SECRET_KEY=django-prod-secret-key-here
DATABASE_URL=postgres://user:pass@db:5432/notes
REDIS_URL=redis://redis:6379/0
DEBUG=False
ALLOWED_HOSTS=notes.example.com,www.notes.example.com

# В Python:
import os
SECRET_KEY = os.environ['SECRET_KEY']   # ← Exception якщо відсутній (не default!)

Production checklist:

✓ DEBUG=False
✓ SECRET_KEY — з env, не у коді
✓ DATABASE_URL — відсутність → Exception (не SQLite fallback!)
✓ ALLOWED_HOSTS — конкретні домени
✓ HTTPS — CSRF_TRUSTED_ORIGINS, SESSION_COOKIE_SECURE
✓ Static files — Nginx, не Django
✓ DebugExceptionMiddleware — тільки if DEBUG  (вже виправлено у Batch N)
✓ Logrotate / log aggregation
✓ Health check endpoint
✓ CI: тести зелені перед деплоєм

Де це у Notes Chat App

Файл Що показує
docker-compose.yml повний стек: db, redis, web, nginx, ngrok, selenium
Dockerfile multi-stage build: FROM python:3.12-slim
entrypoint.sh startup: wait-for-db → migrate → collectstatic → daphne
nginx/nginx.conf proxy_pass, WebSocket upgrade, static alias
.github/workflows/django-tests.yml GitHub Actions CI
notes_project/settings.py SECRET_KEY = os.environ['SECRET_KEY'], DATABASE_URL parsing

Педагогічний зв'язок

Linux basics → Docker → Docker Compose → Nginx → GitHub Actions CI
Частина X охоплює весь deployment стек Notes Chat App (Крок 8 Zero to Hero)

Zero to Hero: Крок 8 — Docker Compose, Nginx, GitHub Actions.


Практика

  1. Виконай docker compose ps — переглянь стан всіх сервісів.
  2. Виконай docker compose logs web --tail=50 — знайди startup sequence.
  3. Виконай docker compose exec web python manage.py shell → from django.conf import settings; print(settings.DEBUG).
  4. Змінений SECRET_KEY у .env → docker compose up -d → переконайся що застосунок перезапустився.
  5. Виконай docker compose exec web python manage.py check --deploy — перевір production checklist.

Контрольні питання

  • Що таке Docker image і Docker container? Яка між ними різниця?
  • Навіщо volumes: у Docker Compose? Що трапиться з даними БД без volume?
  • Чому Nginx роздає /static/ напряму, а не через Django?
  • Що таке ENTRYPOINT у Dockerfile?
  • Чому DATABASE_URL відсутність повинна давати Exception, а не SQLite fallback?
  • Що таке proxy_set_header Upgrade $http_upgrade? Навіщо для WebSocket?
  • Що перевіряє python manage.py check --deploy?

Далі → Notes Chat App Deep Dive — фінальний архітектурний розбір.