Модуль 38

Docker для FastAPI-застосунків

Ось урок у стилі CS50, створений спеціально для тебе. Вмикай уяву, ми починаємо!


🎓 CS50 Style: Docker для FastAPI-застосунків

Привіт, друзі! 👋 Я радий вас бачити.

1. 🔥 Вступ: "У мене все працює, це твій сервер кривий!"

Уявіть ситуацію. Ви написали геніальний API на FastAPI. На вашому ноутбуці все літає: Python 3.11, останні бібліотеки, все налаштовано ідеально. Ви відправляєте код колезі або, що ще відповідальніше, завантажуєте його на сервер (AWS, DigitalOcean — неважливо).

І тут... Помилка 500. Або ModuleNotFoundError. Або взагалі нічого не запускається, бо на сервері стоїть старий Python 3.8.

Ви кажете класичну фразу розробника: "Ну, на моїй машині все працює!" (It works on my machine).

Але користувачам байдуже, що працює у вас на кухні. Їм треба, щоб працювало у них в браузері.

Питання до вас: Як гарантувати, що ваше середовище розробки (версія OS, бібліотеки, налаштування) буде ідентичним середовищу, де код запускається в реальності?

Ви можете написати інструкцію на 10 сторінок: "Спочатку встановіть це, потім видаліть це...". Але люди помиляються.

Аналогія: Уявіть, що ви переїжджаєте в новий будинок. * Традиційний підхід: Ви перевозите меблі (код) і сподіваєтесь, що в новому будинку стіни стоять так само і розетки там, де треба. * Docker: Ви берете всю свою кімнату — зі стінами, шпалерами, меблями та улюбленим вазоном — пакуєте її в контейнер і перевозите. У новому місці ви просто ставите контейнер. Всередині — все ідеально, як ви й залишили.

Ось навіщо нам Docker. Це спосіб "законсервувати" ваше середовище разом із кодом.


2. 🧠 Теоретична база: Що там "під капотом"?

Давайте розберемося з магією, але без зайвої академічності.

Три кити Docker 🐳

  1. Dockerfile (Рецепт) 📜 Це текстовий файл, де ви пишете інструкцію. Наприклад: "Візьми Linux, постав туди Python, скопіюй мій код, запусти його".

  2. Image (Образ/Зліпок) 💿 Це готовий "пиріг", який ви спекли за рецептом. Він незмінний. Це файл, який лежить на диску. Він містить вашу операційну систему (урізану), бібліотеки та код.

  3. Container (Контейнер) 📦 Це запущений процес. Це коли ви взяли Образ і сказали: "Оживи!". Контейнер — це ізольована коробка, в якій працює ваша програма.

Як це працює? (Інтуїтивно)

Ви можете подумати: "Це ж віртуальна машина (VM)!". Ні, і це важливо.

  • Віртуальна машина — це як окремий будинок з власним фундаментом. Це важко і повільно.
  • Docker — це як квартира в будинку. Фундамент (ядро ОС Linux) спільний у всіх квартир, але стіни ізольовані. Якщо ви свердлите стіну в Контейнері А, в Контейнері Б нічого не чути.

👉 Запам’ятайте: Docker дозволяє запускати програму в ізоляції, але використовує ресурси основної системи дуже економно.


3. 🧪 Приклади: Від коду до контейнера

Давайте створимо мінімальний FastAPI застосунок і "запакуємо" його.

Крок 0: Наш код

У нас є файл main.py:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Привіт з Docker-контейнера! 🐳"}

І файл requirements.txt:

fastapi
uvicorn

Якби ми запускали це звичайно, ми б писали в терміналі uvicorn main:app --reload. Але ми хочемо Docker.

Крок 1: Пишемо Dockerfile

Створіть файл з назвою Dockerfile (без розширення) поруч із main.py.

# 1. Базовий образ. Беремо готовий Linux з Python 3.9 (версія slim - легка)
FROM python:3.9-slim

# 2. Робоча директорія. Створюємо папку /app всередині контейнера і заходимо в неї
WORKDIR /app

# 3. Копіюємо requirements.txt З вашого компа В контейнер
COPY requirements.txt .

# 4. Встановлюємо бібліотеки ВСЕРЕДИНІ контейнера
RUN pip install --no-cache-dir -r requirements.txt

# 5. Копіюємо весь інший код (main.py) в контейнер
COPY . .

# 6. Команда, яка виконається при запуску контейнера
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

Питання до вас: Чому ми копіюємо requirements.txt окремо (крок 3), а не разом з усім кодом (крок 5)? Відповідь: Docker кешує шари. Якщо ви зміните код у main.py, але не зміните бібліотеки, Docker пропустить довгу інсталяцію pip install і використає кеш. Це економить час! ⚡️

Крок 2: Будуємо образ (Build)

Ми перетворюємо текст (Dockerfile) у бінарний образ.

docker build -t my-fastapi-app .

(Крапка в кінці важлива! Вона каже: "шукай Dockerfile тут, у цій папці")

Крок 3: Запускаємо (Run)

docker run -p 8000:80 my-fastapi-app

🛑 Стоп! Найважливіший момент. Зверніть увагу на -p 8000:80. * У Dockerfile ми запустили uvicorn на порту 80 (всередині контейнера). * Наш ноутбук нічого не знає про порти всередині контейнера. * Прапор -p 8000:80 означає: "Візьми порт 8000 на моєму ноутбуці і з'єднай його (прокинь тунель) до порту 80 у контейнері".

Тепер відкриваємо браузер: http://localhost:8000. Ви маєте побачити: {"message": "Привіт з Docker-контейнера! 🐳"}.


4. 🛠 Практична частина

Час бруднити руки кодом! Виконайте ці завдання:

🔹 Завдання 1: Hello World

Створіть файли main.py, requirements.txt, Dockerfile як у прикладі вище. Збілдіть образ і запустіть його. Переконайтесь, що працює в браузері.

🔹 Завдання 2: Зламаємо систему

Змініть текст у main.py на "Я змінив код!". Оновіть сторінку в браузері. Питання: Текст змінився? Відповідь: Ні! Чому? Бо контейнер працює на старій копії коду, яка була "запечена" в образі. Дія: Зупиніть контейнер (Ctrl+C), перебудуйте образ (docker build ...) і запустіть знову.

🔹 Завдання 3: Гра з портами

Зупиніть контейнер. Запустіть його з командою: docker run -p 5000:80 my-fastapi-app За якою адресою тепер доступний сайт? Перевірте.

🔹 Завдання 4: Міні-кейс "Змінні оточення"

У реальному житті ми не пишемо паролі в коді. 1. Додайте в main.py: python import os @app.get("/env") def read_env(): return {"secret": os.getenv("MY_SECRET", "Not Found")} 2. Перебудуйте образ. 3. Запустіть з прапором -e: docker run -p 8000:80 -e MY_SECRET="SuperPassword123" my-fastapi-app 4. Перевірте http://localhost:8000/env.

🔹 Завдання 5: А що, якщо...

А що, якщо вам треба видалити контейнер, який ви запустили, але він "висить"? Знайдіть команду, як подивитися список запущених контейнерів (docker ps) і як "вбити" контейнер силоміць (docker rm -f <ID>).


5. 💡 Мислення як у розробника

Як думає Senior Developer, коли працює з Docker?

  1. "Я не хочу забруднювати свою систему". Замість того, щоб ставити PostgreSQL, Redis та Python 3.8 прямо на свій MacBook, я запущу їх у контейнерах. Якщо я щось зламаю — я просто видалю контейнер і створю новий за секунду. Моя основна система залишається чистою.

  2. Шари мають значення. Новачки часто пишуть у Dockerfile спочатку COPY . ., а потім RUN pip install .... Це помилка. Будь-яка зміна в коді (навіть пробіл) змусить Docker перевстановлювати всі бібліотеки наново при кожному білді. Правило: Спочатку те, що змінюється рідко (requirements), потім те, що часто (код).

  3. Безпека. Ніколи не зберігайте .env файли або секретні ключі прямо всередині образу (не робіть COPY .env .), якщо ви плануєте публікувати цей образ. Використовуйте змінні оточення при запуску (як у Завданні 4).


6. 🧩 Підсумок

Отже, що ми сьогодні зробили? 1. Зрозуміли, що Docker — це не віртуальна машина, а "судочок" для вашого коду 🍱. 2. Навчилися писати Dockerfile для FastAPI. 3. Запустили сервер, прокинули порти і передали змінні оточення.

Тепер ви вмієте: Передавати свій код будь-кому, і якщо у них є Docker — він гарантовано запуститься і працюватиме так само, як у вас.

🔮 Тизер наступного уроку

Один контейнер — це круто. Але що робити, якщо нашому FastAPI потрібна база даних PostgreSQL? Запускати два контейнери вручну і намагатися їх з'єднати? Це головний біль. На наступному уроці ми познайомимося з Docker Compose — диригентом, який змусить цілий оркестр контейнерів грати злагоджено однією командою.

А поки — це був CS50! 👨‍💻