Ось урок у стилі 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 🐳
-
Dockerfile (Рецепт) 📜 Це текстовий файл, де ви пишете інструкцію. Наприклад: "Візьми Linux, постав туди Python, скопіюй мій код, запусти його".
-
Image (Образ/Зліпок) 💿 Це готовий "пиріг", який ви спекли за рецептом. Він незмінний. Це файл, який лежить на диску. Він містить вашу операційну систему (урізану), бібліотеки та код.
-
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?
-
"Я не хочу забруднювати свою систему". Замість того, щоб ставити PostgreSQL, Redis та Python 3.8 прямо на свій MacBook, я запущу їх у контейнерах. Якщо я щось зламаю — я просто видалю контейнер і створю новий за секунду. Моя основна система залишається чистою.
-
Шари мають значення. Новачки часто пишуть у Dockerfile спочатку
COPY . ., а потімRUN pip install .... Це помилка. Будь-яка зміна в коді (навіть пробіл) змусить Docker перевстановлювати всі бібліотеки наново при кожному білді. Правило: Спочатку те, що змінюється рідко (requirements), потім те, що часто (код). -
Безпека. Ніколи не зберігайте
.envфайли або секретні ключі прямо всередині образу (не робітьCOPY .env .), якщо ви плануєте публікувати цей образ. Використовуйте змінні оточення при запуску (як у Завданні 4).
6. 🧩 Підсумок
Отже, що ми сьогодні зробили?
1. Зрозуміли, що Docker — це не віртуальна машина, а "судочок" для вашого коду 🍱.
2. Навчилися писати Dockerfile для FastAPI.
3. Запустили сервер, прокинули порти і передали змінні оточення.
Тепер ви вмієте: Передавати свій код будь-кому, і якщо у них є Docker — він гарантовано запуститься і працюватиме так само, як у вас.
🔮 Тизер наступного уроку
Один контейнер — це круто. Але що робити, якщо нашому FastAPI потрібна база даних PostgreSQL? Запускати два контейнери вручну і намагатися їх з'єднати? Це головний біль. На наступному уроці ми познайомимося з Docker Compose — диригентом, який змусить цілий оркестр контейнерів грати злагоджено однією командою.
А поки — це був CS50! 👨💻