Готовитесь к должности инженера по машинному обучению? Эта статья написана для вас! Почему?
Потому что мы будем опираться на прототип Flask и создадим полнофункциональный и масштабируемый сервис. В частности, мы будем настраивать приложение глубокого обучения, обслуживаемое uWSGI и Nginx. Мы рассмотрим все шаг за шагом: от того, как начать с простого приложения Flask, uWSGI действовать как полноценный веб-сервер и скрывать его за Nginx (в качестве обратного прокси-сервера), чтобы обеспечить более надежную обработку соединения. Все это будет сделано поверх проекта глубокого обучения, который мы создали до сих пор, который выполняет семантическую сегментацию изображений с использованием пользовательской модели Unet и Tensorflow.
До этого момента в этой серии мы брали блокнот colab, конвертировали в высокооптимизированный проект с модульными тестами и улучшениями производительности, обучали модель в облаке Google и разработали прототип Flask, чтобы его можно было предоставить пользователям.
Мы усовершенствуем этот прототип Flask и создадим полнофункциональный и масштабируемый сервис.
Что такое uWSGI?
Согласно официальному сайту:
uWSGI — это сервер приложений, целью которого является предоставление полного стека для разработки и развертывания веб-приложений и сервисов.
Как и большинство серверов приложений, он не зависит от языка, но наиболее популярно его использование для обслуживания приложений Python. uWSGI построен на основе спецификации WSGI и взаимодействует с другими серверами по низкоуровневому протоколу, называемому uwsgi.
Хорошо, это очень запутанно, и нам нужно прояснить это, поэтому давайте начнем с некоторых определений.
-
WSGI (Интерфейс шлюза веб-сервера): интерфейс Спецификация, которая определяет связь между веб-сервером и веб-приложением. Проще говоря, он говорит нам, какие методы должны быть реализованы для передачи запросов и ответов туда и обратно между сервером и приложением. По сути, это интерфейс API, который используется для стандартизации связи и является частью стандартной библиотеки Python.
-
uWSGI: Ан приложение сервер, который взаимодействует с приложениями на основе спецификации WSGI и с другими веб-серверами по множеству других протоколов (например, HTTP). В большинстве случаев он действует как промежуточное ПО, поскольку переводит запросы от обычного веб-сервера в формат, понятный приложению (WSGI).
-
увсги: низкоуровневый/двоичный протокол который обеспечивает связь между веб-серверами. uwsgi — это проводной протокол, реализованный сервером uWSGI в качестве предпочтительного способа взаимодействия с другими веб-серверами и экземплярами uWSGI. По сути, он определяет формат данных, отправляемых между серверами и экземплярами.
Теперь давайте посмотрим на картину в целом: наше программное обеспечение открыто для веб-приложения с использованием Flask. Это приложение будет обслуживаться с использованием сервера uWSGI и будет взаимодействовать с ним с использованием спецификации WSGI. Более того, сервер uWSGI теперь будет скрыт за другим веб-сервером (в нашем случае Nginx), с которым он будет общаться по протоколу uwsgi. Имеет ли это смысл? Серьезно, я не знаю, почему они назвали все это одними и теми же инициалами!
На вашем месте у меня было бы еще два вопроса.
Зачем нам в первую очередь нужен сервер uWSGI (разве недостаточно Flask?) и зачем нам нужен еще один веб-сервер, такой как Nginx, перед uWSGI?
И они оба действительны.
Зачем нам нужен uWSGI? Разве Flask не подходит?
Хотя Flask может выступать в качестве веб-сервера HTTP, он не был разработан и оптимизирован для безопасность, масштабируемостьи эффективность. Это скорее фреймворк для создания веб-приложений, как описано в предыдущей статье. uWSGI, с другой стороны, был создан как полнофункциональный веб-сервер, и он решает многие проблемы из коробки, которые Flask даже не затрагивает.
Примеры:
-
Управление процессом: он обрабатывает создание и обслуживание нескольких процессов, поэтому у нас может быть параллельное приложение в одной среде и возможность масштабирования для нескольких пользователей.
-
Кластеризация: его можно использовать в кластере экземпляров
-
Балансировка нагрузки: балансирует нагрузку запросов к разным процессам
-
Мониторинг: он предоставляет готовые функции для мониторинга производительности и использования ресурсов.
-
Ограничение ресурсов: его можно настроить для ограничения использования ЦП и памяти до определенной точки
-
Конфигурация: он имеет огромное количество настраиваемых параметров, что дает нам полный контроль над его выполнением.
Сказав это, давайте рассмотрим наш следующий инструмент: Nginx.
Что такое Nginx и зачем он нам нужен?
Nginx — это высокопроизводительный, масштабируемый и высокодоступный веб-сервер (много здесь). Он действует как балансировщик нагрузки, обратный прокси-сервер и механизм кэширования. Его также можно использовать для обслуживания статических файлов, для обеспечения безопасности и шифрования запросов, для ограничения их скорости, и предположительно он может обрабатывать более 10000 одновременных подключений (на самом деле, по моему опыту, это не столько предположение). По сути, это uWSGI на стероидах.
Nginx чрезвычайно популярен и является частью технического стека многих крупных компаний. Так почему мы должны использовать его перед uWSGI? Ну, главная причина в том, что мы просто хотим взять лучшее из обоих миров. Нам нужны те функции uWSGI, которые специфичны для Python, но нам также нравятся все дополнительные функции, предоставляемые Nginx. Хорошо, если мы не ожидаем, что наше приложение будет масштабироваться до миллионов пользователей, в этом может не быть необходимости, но в этой серии статей это наша конечная цель. Кроме того, это невероятно полезные знания для инженера по машинному обучению. Никто не ожидает, что мы будем экспертами, но знание основ не может навредить нам.
В этом примере мы собираемся использовать Nginx в качестве обратного прокси перед uWSGI. Обратный прокси — это просто система, которая перенаправляет все запросы из Интернета на наш веб-сервер и обратно. Это единая точка связи с внешним миром, обладающая невероятно полезными функциями. Во-первых, он может балансировать нагрузку в миллионы запросов и равномерно распределять трафик по множеству инстансов uWSGI. Во-вторых, он обеспечивает уровень безопасности, который может предотвратить атаки, и использует шифрование при обмене данными. И последнее, но не менее важное: он также может кэшировать контент и ответы, что повышает производительность.
Я надеюсь, что вы уже убедились. Но хватит теории. Думаю, пора замарать руки и посмотреть, как все это можно настроить на практике.
Настройте сервер uWSGI с помощью Flask
В предыдущей статье мы разработали приложение Flask. Он получает изображение в качестве запроса, предсказывает его маску сегментации, используя построенную нами модель Tensorflow Unet, и возвращает ее клиенту.
import os
import traceback
from flask import Flask, jsonify, request
from executor.unet_inferrer import UnetInferrer
app = Flask(__name__)
APP_ROOT = os.getenv('APP_ROOT', '/infer')
HOST = "0.0.0.0"
PORT_NUMBER = int(os.getenv('PORT_NUMBER', 8080))
u_net = UnetInferrer()
@app.route(APP_ROOT, methods=["POST"])
def infer():
data = request.json
image = data['image']
return u_net.infer(image)
@app.errorhandler(Exception)
def handle_exception(e):
return jsonify(stackTrace=traceback.format_exc())
if __name__ == '__main__':
app.run(host=HOST, port=PORT_NUMBER)
Приведенный выше код останется нетронутым, потому что для использования uWSGI нам просто нужно выполнить несколько небольших шагов поверх приложения Flask.
После установки uWSGI с помощью pip
pip install uwsgi
мы можем просто запустить экземпляр с помощью следующей команды:
uwsgi --http 0.0.0.0:8080 --wsgi-file service.py --callable app
Это говорит uWSGI запустить сервер с адресом 0.0.0.0 и портом 8080, используя приложение, расположенное в файле service.py, где находится наш код Flask. Нам также необходимо предоставить вызываемый параметр (должна быть функция), который можно вызывать с помощью спецификации WSGI. В нашем случае это экземпляр Flask, который мы создали и к которому привязали все маршруты.
app = Flask(__name__)
Когда мы нажмем Enter, будет создан полный сервер uWSGI, и мы сможем получить к нему доступ на нашем локальном хосте.
Это действительно так просто! И, конечно же, если мы запустим клиентский скрипт, который мы создали в предыдущей статье, он вместо этого обратится к серверу uWSGI и вернет маску сегментации нашего маленького йоркширского терьера.
Совет: обратите внимание, что вместо того, чтобы передавать все параметры с помощью командной строки, мы можем создать простой файл конфигурации и заставить сервер читать прямо из него.
И на самом деле, обычно это предпочтительный способ, особенно потому, что позже мы развернем сервер в облаке, и гораздо проще изменить параметр конфигурации, чем изменить команду терминала.
Пример файла конфигурации (app.ini) может выглядеть следующим образом:
[uwsgi]
http = 0.0.0.0:8080
module = app.service
callable = app
die-on-term = true
chdir = /home/aisummer/src/soft_eng_for_dl/
virtualenv = /home/aisummer/miniconda3/envs/Deep-Learning-Production-Course/
processes = 1
master = false
vacuum = true
Здесь мы определяем наш URL-адрес «http» и «вызываемый», как и раньше, и используем параметр «модуль», чтобы указать, где находится модуль python с нашим приложением. Кроме того, нам нужно указать некоторые другие вещи, чтобы избежать неправильной настройки сервера, такие как полный путь к каталогу приложения («chdir»), а также путь к виртуальной среде (если мы его используем).
Также обратите внимание, что здесь мы не используем многопроцессорность (process=1) и у нас есть только основной процесс (подробнее о процессах в документации). «Die-on-term» — удобный вариант, который позволяет нам убить сервер из терминала, а «vacuum» предписывает uWSGI периодически очищать неиспользуемые сгенерированные файлы.
Настройка сервера uWSGI не является простой задачей и требует тщательного изучения, поскольку существует так много вариантов и параметров, которые необходимо учитывать. Здесь я не буду подробно анализировать все различные варианты и детали, но предлагаю поискать их в официальной документации. Как всегда, мы предоставим дополнительные ссылки в конце.
Чтобы запустить сервер, мы можем сделать:
uwsgi app.ini
Подключите Nginx в качестве обратного прокси
Следующей задачей в списке задач является подключение сервера Nginx, что опять же так же просто, как создание файла конфигурации.
Сначала мы устанавливаем его с помощью «apt-get install».
sudo apt-get install nginx
Затем мы хотим создать файл конфигурации, который должен находиться в каталоге «/etc/nginx/sites-available» (если вы работаете в Linux), и он должен быть назван в честь нашего приложения.
sudo nano /etc/nginx/sites-available/service.conf
Затем мы создаем очень простой файл конфигурации, который содержит только абсолютный минимум для запуска прокси. Опять же, чтобы узнать обо всех доступных параметрах конфигурации, обязательно ознакомьтесь с официальной документацией.
server {
listen 80;
server_name 0.0.0.0;
location {
include uwsgi_params;
uwsgi_pass unix: /home/aisummer/src/soft_eng_for_dl/app/service.sock;
}
}
Здесь мы указываем Nginx прослушивать порт 80 по умолчанию для запросов, поступающих с сервера, расположенного в 0.0.0.0. Блок местоположения идентифицирует все запросы, поступающие из Интернета на сервер uWSGI, включая «uwsgi_params», который указывает все общие параметры uWSGI, и «uwsgi_pass», чтобы перенаправить их в определенный сокет.
Какой сокет вы можете задаться вопросом? Мы не создали сокет. Это верно. Вот почему нам нужно объявить сокет в нашем конфигурационном файле uWSGI, добавив следующие 2 строки:
socket = service.sock
chmod-socket = 660
Это указывает серверу прослушивать сокет 660. И помните, что Nginx общается с вашим uWSGI через сокет, используя протокол uwsgi.
Разъем: веб-сокет — это двунаправленное безопасное соединение, которое обеспечивает двусторонний интерактивный сеанс связи между пользователем и клиентом. Таким образом, мы можем отправлять сообщения на сервер и получать ответы, управляемые событиями, без необходимости опрашивать сервер для ответа.
Наконец, мы включаем вышеуказанную конфигурацию, выполнив приведенную ниже команду, которая связывает нашу конфигурацию в каталоге «sites-available» с каталогом «sites-enabled».
sudo ln -s /etc/nginx/sites-available/service /etc/nginx/sites-enabled
И запускаем Nginx
sudo nginx -t
Если все прошло хорошо, мы должны увидеть наше приложение на локальном хосте, и мы можем снова использовать наш клиент, чтобы убедиться, что все работает должным образом.
Заключение
Мы использовали uWSGI для создания сервера из нашего приложения Flask и спрятали сервер за обратным прокси-сервером Nginx для обеспечения безопасности и балансировки нагрузки. В результате у нас есть официальное приложение Deep Learning, которое можно без проблем масштабировать до миллионов пользователей. И самое приятное то, что его можно развернуть в облаке именно так, как он есть, и использовать его прямо сейчас. Или вы можете настроить свой собственный сервер в своем подвале, но я бы не советовал этого делать.
Благодаря всем шагам и оптимизации, которые мы сделали, мы можем быть уверены в производительности нашего приложения. Таким образом, нам не нужно слишком беспокоиться о таких вещах, как задержка, эффективность, безопасность. И чтобы доказать, что это действительно так, в следующих статьях мы собираемся развернуть наше приложение Deep Learning в облаке Google, используя Docker Containers и Kubernetes. Не могу дождаться, чтобы увидеть вас там.
В качестве дополнительного материала я настоятельно рекомендую курс TensorFlow: Advanced Techniques Specialization от deeplearning.ai, размещенный на Coursera, который даст вам базовое понимание Tensorflow.
До свидания…
Ресурсы
* Раскрытие информации: Обратите внимание, что некоторые из приведенных выше ссылок могут быть партнерскими ссылками, и мы без дополнительных затрат для вас получим комиссию, если вы решите совершить покупку после перехода по ссылке.