Минимализм: Когда фреймворки — это перебор.
Сегодня под любую задачу найдётся фреймворк: для сайтов — Django, для API — FastAPI, для ботов — aiogram. Но что, если задача элементарна? Например, нужно просто, чтобы при обращении к определённому адресу в браузере на сервере запускался Python-скрипт. Без базы данных, без роутинга, без десятков зависимостей. В этом случае фреймворки — как молоток для открывания бутылки: можно, но громоздко. Нам подойдёт чистый, прямолинейный подход — минимальный Python и чуть-чуть системной магии.
aiohttp — лёгкий путь в мир API без лишнего веса
Для эксперимента я решил собрать простой тестовый пример — без лишнего шума, просто чтобы понять, как это всё работает. И тут снова выручила моя старая знакомая — библиотека aiohttp. Она, кроме того что асинхронная, так еще и универсальная: может быть и клиентом, и сервером. При этом весит очень мало и почти не грузит систему. В отличие от тяжеловесных фреймворков, aiohttp — это швейцарский нож для Python-разработчика (у меня есть статья с таким названием 🙂 ), которому нужно быстро и аккуратно решить задачу.
Аскетичная лаборатория: VPS + Apache + Python = API
Итак, поле для экспериментов готово: недорогой VPS с Ubuntu, установленный Apache и доменное имя, направленное на этот сервер. Минимум технологий — максимум пользы. Это идеальная песочница, чтобы не просто читать про API, а действительно понять, как он работает. Цель проста: создать эндпоинт http://site.com/hi, который по запросу будет радостно отвечать браузеру строкой:
«Привет! Это твой первый API!»
Вот и весь план — без фреймворков, без баз данных, без Docker и прочей артиллерии. Только Python, aiohttp и Apache, объединённые в простую, но работающую систему.
Простой aiohttp-сервер шаг за шагом
Первым делом я написал вот такое, по сути минимальный пример из официальной документации к aiohttp:
from aiohttp import web
async def hi_handler(request):
return web.Response(text='Привет! Это твой первый API!', content_type='text/plain')
app = web.Application()
app.router.add_get('/hi', hi_handler)
if __name__ == '__main__':
web.run_app(app, host='127.0.0.1', port=8080)
Несмотря на то, что код получился небольшой, это вполне рабочий web сервер. Спасибо aiohttp!
Разбор кода сервера
Давайте разберёмся, что именно делает этот код. Он создает простой HTTP-сервер с помощью библиотеки aiohttp, который отвечает на запрос по адресу /hi. Вот пошаговое объяснение:
from aiohttp import web
Импортируем модуль web из библиотеки aiohttp. Это основа для создания серверной части: здесь есть всё — от приложения до маршрутизаторов и ответа.
async def hi_handler(request):
return web.Response(text='Привет! Это твой первый API!', content_type='text/plain')
Мы создаём асинхронную функцию-обработчик, которая будет вызываться при обращении к определённому URL (в данном случае — /hi).
- async def — функция объявлена как асинхронная. Это позволяет серверу обрабатывать сразу несколько запросов, не блокируя выполнение.
- web.Response(…) — отправляем клиенту ответ:
- text=’Привет!...’ — текст, который увидит пользователь.
- content_type=‘text/plain’ — тип содержимого, обычный текст.
app = web.Application()
Создаём экземпляр веб-приложения. Это как пустая коробка, в которую мы будем складывать маршруты, настройки и другие элементы сервера.
app.router.add_get('/hi', hi_handler)
Регистрируем маршрут:
- При GET-запросе к адресу /hi вызывается функция hi_handler.
- Это и есть та самая “дверь”, через которую пользователь попадает в нашу функцию.
if __name__ == '__main__':
web.run_app(app, host='127.0.0.1', port=8080)
Запускаем сервер:
- web.run_app(app, …) — стартует веб-сервер и начинает слушать указанный адрес и порт.
- host=‘127.0.0.1’ — сервер доступен только с этого компьютера (локально).
- port=8080 — порт, на котором всё будет работать. Можно открыть браузер и набрать http://127.0.0.1:8080/hi, чтобы увидеть результат.
if __name__ == ‘__main__’: — стандартная проверка, чтобы код запускался только если файл был запущен напрямую, а не импортирован как модуль.
И вот, за несколько строк мы получаем рабочий HTTP-сервер, который обрабатывает запросы асинхронно и отвечает браузеру строкой. Всё просто.
Подключаем Apache как обратный прокси
Теперь, когда у нас есть работающий скрипт на aiohttp, надо сделать так, чтобы его можно было вызывать не только по localhost, а снаружи — через браузер по обычному HTTP-запросу. В этом нам поможет Apache, выступая в роли обратного прокси (reverse proxy).
Что такое обратный прокси?
Это когда Apache принимает внешний запрос (например, на http://yourdomain.ru/hi), а сам прокидывает его внутрь — на наш Python-сервер, работающий на 127.0.0.1:8080. Пользователь этого даже не заметит: для него всё выглядит, как обычный сайт.
Что нужно сделать
Откроем конфигурационный файл сайта в Apache. Это может быть, например:
/etc/apache2/sites-available/000-default.conf
или, если у нас виртуальный хост (у меня есть сайты на сервере, у меня так):
/etc/apache2/sites-available/site.com.conf
Добавляем внутрь
ProxyPreserveHost On
ProxyPass '/hi' 'http://127.0.0.1:8080/hi'
ProxyPassReverse '/hi' 'http://127.0.0.1:8080/hi'
Что делают эти строки:
- ProxyPreserveHost On — сохраняет оригинальный заголовок Host, передаваемый клиентом.
- ProxyPass — говорит Apache, что все обращения к /hi надо прокидывать на Python-сервер.
- ProxyPassReverse — корректно возвращает ответы от Python обратно пользователю.
Не забудем включить нужные модули Apache через терминал Ubuntu, или что там у нас на VPS:
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo systemctl restart apache2
Результат
Теперь, когда ты откроешь в браузере:
http://site.com/hi
Apache примет этот запрос и передаст его Python-серверу, а ответ — вернёт обратно. Всё работает как одно целое, и никто не догадывается, что под капотом — aiohttp.
Гибкая и лёгкая основа для собственного API
Теперь у тебя есть минималистичная, но мощная связка:
aiohttp — лёгкий Python-сервер, быстро реагирующий на запросы. Apache — фронтенд, работающий как прокси, принимающий HTTP/HTTPS-запросы.
Эта пара уже даёт отличную базу для чего угодно:
легко прикручивается HTTPS через Let’s Encrypt или другой сертификат; можно оформить запуск Python-сервера как systemd-сервис, чтобы он стартовал вместе с системой; удобно добавлять новые эндпоинты под конкретные задачи: от запуска скриптов до интеграций с другими сервисами; и всё это без тяжеловесных фреймворков и лишнего кода.
Такой подход особенно хорош, когда нужен небольшой серверный функционал, а не огромная платформа — быстро, просто и под полным контролем.
Зачем всё это: мониторинг без лишнего шума
Этот мини-сервер я сделал не просто так. Мне нужно было удобно следить за состоянием моего VPS, где совсем немного оперативки. Идея простая: открываю страницу в браузере → получаю свободную память и список топ ресурсоёмких процессов.
Никаких входов по SSH, никаких тяжёлых дашбордов. Просто быстрый запрос и понятный ответ — всё, что нужно для рутинной проверки в моменте.
Есть конечно еще мобильный терминал для смартфона, но с его экрана не очень удобно писать команды по SSH.
Добавить эту логику в скрипт с aiohttp
— дело пары строк. Вместо “Привет” он может возвращать, например:
- количество свободной оперативной памяти,
- список самых «тяжёлых» процессов (CPU / RAM),
- аптайм сервера,
- место на диске и тп.
Насколько «лёгкий» aiohttp на деле?
Я специально посмотрел: сам импорт aiohttp
в интерпретатор Python на моём VPS увеличивает потребление памяти примерно на 10 МБ ОЗУ. Это, конечно, не ноль, но вполне разумная цена за:
- сервер на Python, который держит соединение;
- асинхронную обработку запросов;
- лёгкость в доработке под любые задачи.
В общем, если нужен свой маленький API, без тяжёлых фреймворков — aiohttp
+ Apache как прокси дают отличную основу. Всё просто, прозрачно и гибко.