Модуль 21

FastCGI та робота з Python (uWSGI, Gunicorn)

Ось твій урок у стилі CS50. Вмикаємо енергію, харизму і поринаємо у світ веб-серверів! 🚀


🎓 Тема: FastCGI, WSGI та магія Gunicorn/uWSGI

Привіт, світе! 👋 Я радий бачити вас на цьому занятті.

Сьогодні ми зазирнемо під капот веб-розробки. Ми написали код на Python, він працює у вас на ноутбуці, ви натискаєте "Run" у терміналі, і все чудово. Але як цей код стає доступним для мільйонів користувачів в Інтернеті? Як змусити професійний сервер "розуміти" Python?

Це питання, яке відділяє любителя від професійного бекенд-інженера. Поїхали!


1. 🔥 Вступ: проблема та мотивація

Уявіть ситуацію. Ви створили геніальний стартап — скажімо, сервіс, що генерує меми за допомогою ШІ. Ви орендували потужний сервер, встановили туди наднадійний Nginx (або Apache), завантажили свій Python-скрипт і... тиша.

Сервер Nginx дивиться на ваш файл main.py і каже: "Я не знаю, що з цим робити. Я вмію віддавати картинки та HTML-файли. А оце ваше def main(): — це для мене китайська грамота".

Риторичне запитання: Чи можемо ми навчити Nginx розуміти Python напряму? Теоретично — так, але це погана ідея. Чому? Бо Nginx має бути легким швейцаром, який швидко відчиняє двері, а не кухарем, який смажить котлети.

Аналогія з рестораном: 🍝 * Клієнт (Browser) приходить у ресторан і хоче стейк. * Nginx — це хостес (адміністратор) на вході. Він зустрічає гостей, садить за стіл, дає меню. Він супершвидкий. Але він не вміє готувати. * Python — це шеф-кухар на кухні. Він повільніший, творчий, він знає рецепт стейка.

Проблема в тому, що Хостес (Nginx) і Шеф (Python) знаходяться в різних кімнатах і не чують одне одного. Нам потрібен офіціант, який візьме замовлення від хостес, побіжить на кухню, передасть його шефу, зачекає страву і принесе її назад.

Ось цим "офіціантом" і є Gunicorn або uWSGI. Без них ваш ресторан не запрацює.


2. 🧠 Теоретична база (без сухої академічності)

Давайте розберемося, як еволюціонувала ця "служба доставки" замовлень.

📜 Етап 1: CGI (Common Gateway Interface) — Стара школа

Колись давно, на кожне замовлення сервер запускав скрипт з нуля. * Прийшов клієнт -> Запустили Python -> Python завантажив бібліотеки -> Виконав код -> Помер. * Проблема: Це як наймати нового кухаря для кожної тарілки супу, а потім звільняти його. Страшенно повільно!

🚀 Етап 2: FastCGI — Розумний підхід

Люди зрозуміли, що запускати процес щоразу — дорого. FastCGI запропонував тримати процес запущеним. Кухар (Python) приходить на роботу зранку і чекає замовлень весь день. Це вже швидше.

🐍 Етап 3: WSGI (Web Server Gateway Interface)

Це те, що потрібно запам’ятати обов’язково. У світі Python існує домовленість (стандарт PEP 3333). Це як спільна мова. * WSGI — це не програма, це правило. Правило каже: "Якщо ти хочеш бути веб-програмою на Python, ти маєш мати функцію, яка приймає два аргументи і повертає відповідь".

⚙️ Хто такі Gunicorn та uWSGI?

Це WSGI-сервери (Application Servers). Це ті самі "супер-офіціанти". 1. Вони запускають ваш Python-код і тримають його в пам'яті. 2. Вони слухають команди від Nginx. 3. Вони керують потоками (workers). Якщо прийшло 100 людей одночасно, Gunicorn каже: "Окей, мені потрібно 4 клони Python-скрипта, щоб усіх обслужити".

Інтуїтивно: * Nginx: "Я отримав запит на /api/user. Гей, Gunicorn, розберись!" * Gunicorn: "Зрозумів. Передаю це в Python-функцію app(). Чекаю... О, Python повернув JSON. Тримай, Nginx!" * Nginx: "Дякує, відправляю клієнту".


3. 🧪 Приклади (від простого до реального)

Приклад 1: Найпростіший WSGI-додаток (Без фреймворків)

Ви, мабуть, очікуєте побачити тут Flask або Django? А давайте подивимось, як це працює "голому" Python, щоб зрозуміти суть.

Створіть файл simple_app.py:

# Це і є WSGI-додаток. Просто функція.
def application(environ, start_response):
    # 1. Формуємо заголовки (Headers)
    status = '200 OK'
    headers = [('Content-type', 'text/plain; charset=utf-8')]

    # 2. Кажемо серверу: "Ми готові відповідати"
    start_response(status, headers)

    # 3. Повертаємо тіло відповіді (Body)
    return [b"Hello, CS50! This is raw WSGI."]

Питання до студента: Як ви думаєте, якщо я запущу python simple_app.py, що станеться? (Пауза для роздумів) Відповідь: Нічого! Це просто функція. Вона не слухає порт, вона не вміє приймати HTTP-запити. Їй потрібен Gunicorn.

Приклад 2: Запускаємо з Gunicorn

Встановіть Gunicorn: pip install gunicorn Запустіть у терміналі (в тій самій папці):

gunicorn -w 4 simple_app:application
  • -w 4: Запусти 4 "робітники" (workers). Тобто 4 кухарі готові працювати паралельно.
  • simple_app:application: В файлі simple_app візьми функцію application.

Тепер відкрийте браузер на http://127.0.0.1:8000. Вуаля! Ви бачите текст. Gunicorn став мостом між браузером і вашою функцією.

Приклад 3: Реальний світ (Flask + Gunicorn)

У реальному проєкті ми використовуємо фреймворки, які вже мають цю application функцію всередині.

Файл my_flask.py:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "Я працюю через Gunicorn!"

# Зверніть увагу: ми НЕ пишемо app.run() тут для продакшену!

Запуск:

gunicorn -w 4 -b 0.0.0.0:5000 my_flask:app

Тепер Gunicorn взяв об'єкт app з Flask (який є WSGI-сумісним) і обслуговує його на порту 5000.


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

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

  1. 🔹 Hello World: Створіть файл wsgi_test.py з "голою" функцією WSGI (як у прикладі 1), яка повертає ваше ім'я. Запустіть через Gunicorn.
  2. 🔹 Паралельність: Запустіть Gunicorn з 1 воркером (-w 1). Додайте у код time.sleep(5). Відкрийте дві вкладки в браузері одночасно.
    • Що відбувається? (Друга вкладка висітиме 5 секунд, поки перша не закінчить).
    • Виправлення: Перезапустіть з -w 4. Тепер обидві вкладки завантажуються майже одночасно? Чому?
  3. 🔹 Флаги: Спробуйте запустити Gunicorn, прив'язавши його до іншого порту (наприклад, 8080) за допомогою прапорця -b.
  4. 🔹 Міні-кейс (uWSGI): Якщо маєте сміливість, встановіть uwsgi (pip install uwsgi) і спробуйте запустити той самий файл через нього.
    • Команда: uwsgi --http :8000 --wsgi-file simple_app.py
    • Порівняйте вивід у консолі. Він виглядає страшніше, правда? (uWSGI потужніший, але складніший).

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

Ось де новачки часто помиляються, а сеньйори лише хитро посміхаються.

❌ Типова помилка новачка

Запускати python app.py (вбудований сервер Flask/Django) на реальному сервері в Інтернеті. Чому це погано? Вбудований сервер — це іграшка. Він однопоточний (здебільшого) і не захищений від навантажень. Він впаде від 10 запитів на секунду. Gunicorn витримає тисячі.

🤔 Як думає профі?

Профі думає про розподіл відповідальності. * "Статичні файли (картинки, CSS)? Нехай віддає Nginx. Він робить це миттєво." * "Логіка і база даних? Це до Gunicorn + Python." * "Безпека (SSL/HTTPS)? Це налаштовуємо на Nginx."

💡 Порада з практики

Кількість воркерів (-w) для Gunicorn зазвичай розраховують за формулою: (2 x кількість_ядер_CPU) + 1. Якщо у вас 2 ядра, ставте 5 воркерів. Більше — не значить краще, процесор просто захлинеться перемиканням контексту.


6. 🧩 Підсумок

Отже, що ми сьогодні розклали по поличках:

  1. Nginx — це вхідні двері та метрдотель.
  2. Python — це шеф-кухар, який готує код.
  3. WSGI — це мова спілкування між ними.
  4. Gunicorn/uWSGI — це офіціанти, які бігають між залом і кухнею, забезпечуючи магію.

Тепер ви не просто "запускаєте скрипт", ви деплоїте аплікацію. Ви вмієте змусити ваш код працювати стабільно під навантаженням.

Що далі? Уявіть, що у вас 5 різних проєктів на Python з різними версіями бібліотек. Як не перетворити сервер на смітник? На наступному уроці ми запакуємо все це в контейнери. Готуйтесь, тема наступного уроку — Docker! 🐳

А поки що — це був CS50. (Тобто, наш урок). 😉