Ось твій урок у стилі CS50. Вмикаймо уяву, відкриваймо термінал і поїхали!
🎓 Урок: Celery у Docker-середовищі (асинхронність у контейнерах)
1. 🔥 Вступ: Чому ваш сайт "завис"?
Уявіть собі ситуацію. Ви заходите на сайт, реєструєтесь, натискаєте кнопку "Створити акаунт" і… чекаєте. Крутиться спінер завантаження. Минає секунда, дві, п’ять. Ви починаєте нервувати. "Чи натиснув я кнопку? Може, Інтернет зник?".
А насправді, що відбувається "під капотом"? Сервер отримав ваші дані, зберіг їх у базу, а потім почав генерувати PDF-звіт, стискати ваше фото профілю або відправляти вітальний email через повільний SMTP-сервер. І поки він це робить, він не може відповісти вам: "Усе ок, друже, вітаємо!". Він зайнятий.
Риторичне запитання: Чи хотіли б ви, щоб офіціант у ресторані, прийнявши ваше замовлення, йшов на кухню, сам чистив картоплю, смажив м’ясо, і тільки потім повертався до вас, ігноруючи всіх інших клієнтів у залі?
Звісно, ні! Офіціант (ваш веб-сервер) має лише прийняти замовлення і передати його на кухню (воркеру).
Саме для цього нам потрібен Celery — це наша "кухня". А Docker дозволяє нам побудувати цей ресторан так, щоб і офіціанти, і кухарі працювали в ідеально ізольованих, але пов'язаних кімнатах.
Без цієї теми ви будете створювати програми, які "гальмують" на кожній складній операції.
2. 🧠 Теоретична база: Як це працює (Кухня та Тікети)
Давайте розберемо архітектуру. У нас є три головні дійові особи.
- Producer (Веб-додаток/Django/Flask): Це офіціант. Він каже: "Треба надіслати лист". Але він не робить це сам. Він створює "тікет" (задачу).
- Broker (Redis або RabbitMQ): Це дошка із замовленнями на кухні. Офіціант чіпляє туди тікет. Брокер — це посередник. Він зберігає чергу задач.
- Consumer (Celery Worker): Це кухар. Він бачить новий тікет на дошці, бере його і починає готувати (відправляти лист), поки офіціант вже обслуговує іншого клієнта.
А до чого тут Docker?
Раніше ми запускали це все в різних терміналах на одній машині. Але в професійному світі ("production") ми хочемо ізоляції.
У Docker-світі кожен з цих гравців — це окремий контейнер:
* Контейнер web (ваш код сайту).
* Контейнер redis (ваша дошка оголошень).
* Контейнер worker (ваш код сайту + Celery, який виконує задачі).
❗️ Що треба запам’ятати залізно: І
web, іworkerзазвичай використовують один і той самий Docker-образ (image). Чому? Бо воркеру теж потрібен доступ до ваших моделей бази даних, функцій та налаштувань, щоб виконати задачу. Різниця лише в команді запуску!
3. 🧪 Приклади: Від коду до Docker Compose
Крок 1: Мінімальна задача (Python)
Припустимо, у нас є файл tasks.py.
# tasks.py
import time
from celery import Celery
# Налаштовуємо Celery на роботу з Redis
app = Celery('tasks', broker='redis://redis:6379/0')
@app.task
def long_task(name):
print(f"Починаю обробку для {name}...")
time.sleep(10) # Імітація важкої роботи
print(f"Готово! {name} оброблено.")
return f"Hello, {name}!"
Крок 2: Магія Docker Compose
Ось тут стається найцікавіше. Як нам запустити це як оркестр? Дивимось на docker-compose.yml.
Питання до вас: Що ви очікуєте побачити у файлі конфігурації? Скільки сервісів нам потрібно мінімум? (Правильно, три: програма, брокер, воркер).
version: '3.8'
services:
# 1. Наш Брокер (Пошта/Дошка)
redis:
image: redis:alpine
# 2. Наш Веб-сервіс (Офіціант) - імітуємо просто запуск скрипта
web:
build: .
command: python app.py # Запускає веб-сервер
depends_on:
- redis
volumes:
- .:/app
# 3. Наш Воркер (Кухар)
worker:
build: . # ТА Ж САМА збірка, що і для web!
# АЛЕ інша команда запуску:
command: celery -A tasks worker --loglevel=info
depends_on:
- redis
volumes:
- .:/app
environment:
- CELERY_BROKER_URL=redis://redis:6379/0
Чому це працює?
Ми використовуємо один код (build: .), але наказуємо контейнерам робити різні речі. Контейнер web слухає користувачів. Контейнер worker мовчки сидить і дивиться на redis, чекаючи на роботу.
4. 🛠 Практична частина
Завдання для закріплення. Не бійтеся помилятися — це найкращий спосіб навчання!
Завдання 1: "Hello World" у логах
Створіть структуру файлів з прикладу вище. Запустіть docker-compose up. Викличте задачу з web контейнера (можна через docker-compose exec web python -> from tasks import long_task; long_task.delay('Student')).
Знайдіть у логах контейнера worker момент, коли задача почалася і закінчилася.
Завдання 2: "Саботаж"
Зупиніть контейнер worker (docker-compose stop worker), але залиште web і redis. Відправте 5 задач.
Питання: Де вони ділися?
Дія: Запустіть worker знову. Що сталося? (Спойлер: вони мають виконатися, бо Redis їх дбайливо зберіг).
Завдання 3: Scaling (Масштабування)
Уявіть, що прийшов "Чорний п'ятниця". Один кухар не справляється.
Спробуйте команду: docker-compose up -d --scale worker=3.
Тепер відправте 10 важких задач одночасно. Подивіться в логи: як задачі розподілилися між трьома воркерами?
Завдання 4: Реальний кейс
Напишіть функцію, яка не просто робить sleep, а, наприклад, записує поточний час у текстовий файл log.txt.
Нюанс: Перевірте, в якому контейнері з'явився файл? (Пам'ятайте про Volume).
5. 💡 Мислення як у розробника
Як відрізнити новачка від профі в роботі з Celery та Docker?
1. Помилка: "Передача об'єктів"
Новачок: Передає в задачу цілий об'єкт користувача (User object).
Профі: Передає тільки user_id.
Чому? Поки задача дійде до виконання в черзі, дані користувача в базі могли змінитися. Воркер має сам дістати свіжі дані з бази за ID. До того ж, серіалізація складних об'єктів часто ламається.
2. Помилка: "Воркер без коду"
Новачок: Думає, що воркер — це якась магічна хмара.
Профі: Розуміє, що воркер — це копія вашого проекту. Якщо ви змінили код у tasks.py, вам треба перезапустити (або перебудувати) і контейнер воркера теж!
3. Порада: Моніторинг
Коли задачі виконуються у фоні, ви не бачите помилок на екрані браузера.
Профі: Завжди додає інструмент типу Flower (веб-інтерфейс для Celery) у docker-compose, щоб візуально бачити: "Ага, ця задача впала, бо SMTP-сервер недоступний".
6. 🧩 Підсумок
Отже, що ми сьогодні зробили? Ми взяли синхронний, повільний процес і розбили його на швидкі, асинхронні шматки. Ми використали Docker, щоб розкласти компоненти (Брокер, Воркер, Додаток) по поличках, як інгредієнти на професійній кухні.
Тепер ви вмієте:
1. Описувати сервіс worker у docker-compose.yml.
2. Розуміти потік даних: App -> Redis -> Worker.
3. Масштабувати обробку задач однією командою.
Що далі? Ви запитаєте: "А як зробити так, щоб задача виконувалася щоранку о 9:00, наприклад, розсилка новин?". Для цього нам знадобиться Celery Beat — диригент нашого оркестру. Але це… вже тема наступного уроку!
Кодимо далі! 🚀