Автор оригинала: Guest Contributor.
Докеризация приложений Python
Вступление
Docker – это широко распространенный и используемый ведущими ИТ-компаниями инструмент для создания, управления и защиты своих приложений.
Контейнеры, такие как Docker, позволяют разработчикам изолировать и запускать несколько приложений в одной операционной системе, а не выделять виртуальную машину для каждого приложения на сервере. Использование этих более легких контейнеров приводит к снижению затрат, лучшему использованию ресурсов и более высокой производительности.
Если вам интересно читать больше, вам следует взглянуть на Docker: A High Level Introduction .
В этой статье мы напишем простое веб-приложение Python с использованием Flask и подготовим его к “докеризации”, а затем создадим образ Docker и развернем его как в тестовой, так и в производственной среде.
Примечание : В этом учебнике предполагается, что на вашем компьютере установлен Docker. Если нет, то вы можете следовать официальному руководству по установке Docker .
Что такое Докер?
Docker-это инструмент, который позволяет разработчикам отправлять свои приложения (вместе с библиотеками и другими зависимостями), гарантируя, что они могут работать с точно такой же конфигурацией, независимо от среды, в которой они развернуты.
Это делается путем изоляции приложений в отдельных контейнерах, которые, хотя и разделены контейнерами, совместно используют операционную систему и соответствующие библиотеки.
Docker можно разбить на:
- Docker Engine – Инструмент упаковки программного обеспечения, используемый для контейнеризации приложений.
- Docker Hub – Инструмент для управления контейнерными приложениями в облаке.
Почему Контейнеры?
Важно понимать важность и полезность контейнеров. Хотя они могут не иметь большого значения для одного приложения, развернутого на сервере, или в домашних проектах, контейнеры могут быть спасением, когда дело доходит до надежных и ресурсоемких приложений, особенно если они используют один и тот же сервер или если они развернуты во многих различных средах.
Сначала это было решено с помощью виртуальных машин , таких как VMware и гипервизоры , хотя они оказались не оптимальными с точки зрения эффективности, скорости и переносимости.
Контейнеры Docker являются легкой альтернативой виртуальным машинам – в отличие от виртуальных машин, нам не нужно предварительно выделять для них оперативную память, процессор или другие ресурсы, и нам не нужно загружать новую виртуальную машину для каждого приложения, поскольку мы работаем только с одной операционной системой.
Разработчикам не нужно обременять себя доставкой специальных версий программного обеспечения для различных сред, и они могут сосредоточиться на создании основной бизнес – логики приложения.
Настройка проекта
Flask -это микро-фреймворк Python, используемый для создания как простых, так и продвинутых веб-приложений. Благодаря простоте использования и настройки мы будем использовать его для нашего демонстрационного приложения.
Если у вас еще не установлена вспышка, это легко сделать с помощью одной команды:
$ pip install flask
После установки Flask создайте папку проекта с именем Flask App
для примера. В этой папке создайте базовый файл с именем что-то вроде app.py
.
Внутри app.py
импортируйте модуль Flask
и создайте веб-приложение, используя следующее:
from flask import Flask app = Flask(__name__)`
Далее определим базовый маршрут /
и соответствующий обработчик запроса:
@app.route("/") def index(): return """Python Flask in Docker!
A sample web-app for running Flask inside Docker.
"""
Наконец, давайте запустим приложение, если скрипт вызывается как основная программа:
if __name__ == "__main__": app.run(debug=True, host='0.0.0.0')
$ python3 app.py
Перейдите в свой браузер по адресу http://localhost:5000/
. Вам должно быть предложено сообщение “Dockerizing Python app using Flask”!
Докеризация приложения
Чтобы запустить приложение с помощью Docker, мы должны построить контейнер со всеми используемыми в нем зависимостями, который в нашем случае является только Flask. Для этого мы включим requirements.txt
файл, содержащий необходимые зависимости, и создайте Dockerfile , который использует этот файл для создания образа.
Кроме того, когда мы запускаем контейнер, мы должны иметь доступ к HTTP-портам, на которых работает приложение.
Подготовка заявки
Включая зависимости в requirements.txt
файл очень прост. Нам просто нужно включить имя и версию зависимости:
Flask==1.0.2
Далее нам нужно убедиться, что все файлы Python, необходимые для запуска нашего приложения, находятся в папке верхнего уровня, например, под названием app
.
Также рекомендуется, чтобы главная точка входа была названа app.py
поскольку это хорошая практика, чтобы назвать Flash-объект, созданный в скрипте, как app
, чтобы облегчить развертывание.
docker-flask-tutorial ├── requirements.txt ├── Dockerfile └── app └── app.py └──
Создание файла Dockerfile
A Dockerfile – это, по сути, текстовый файл с четко определенными инструкциями о том, как создать образ Docker для нашего проекта.
Далее мы создадим образ Docker на основе Ubuntu 16.04 и Python 3.X:
FROM ubuntu:16.04 MAINTAINER Madhuri Koushik "[email protected]" RUN apt-get update -y && \ apt-get install -y python3-pip python3-dev COPY ./requirements.txt /requirements.txt WORKDIR / RUN pip3 install -r requirements.txt COPY . / ENTRYPOINT [ "python3" ] CMD [ "app/app.py" ]
Здесь есть несколько команд, которые заслуживают надлежащего объяснения:
- FROM – Каждый файл Dockerfile начинается с ключевого слова
FROM
. Он используется для указания базового изображения, из которого строится изображение. Следующая строка содержит метаданные о сопровождающем изображении. - RUN – Мы можем добавить дополнительное содержимое в образ, запустив задачи установки и сохранив результаты этих команд. Здесь мы просто обновляем информацию о пакете, устанавливаем
python3
иpip
. Мы используемpipe
во второй командеRUN
для установки всех пакетов вrequirements.txt
файл. - COPY – Команда
COPY
используется для копирования файлов/каталогов с хост-компьютера в контейнер во время процесса сборки. В этом случае мы копируем файлы приложения, включаяrequirements.txt
. - WORKDIR – задает рабочий каталог в контейнере, который используется RUN, COPY и т. Д…
- ТОЧКА ВХОДА – Определяет точку входа приложения
- CMD – Запускает
app.py
файл в каталогеapp
.
Как создаются образы Docker
Образы Docker создаются с помощью команды docker build
. При построении образа Docker создает так называемые “слои”. Каждый слой записывает изменения, вызванные командой в файле Dockerfile, и состояние изображения после выполнения команды.
Docker внутренне кэширует эти слои, так что при перестройке изображений ему нужно воссоздавать только те слои, которые изменились. Например, как только он загрузит базовый образ для ubuntu:16.04
, все последующие сборки того же контейнера могут повторно использовать его, так как это не изменится. Однако во время каждой перестройки содержимое каталога приложений, скорее всего, будет отличаться, и поэтому этот слой будет перестраиваться каждый раз.
Всякий раз, когда какой-либо слой перестраивается, все слои, следующие за ним в файле Dockerfile, также должны быть перестроены. Важно иметь это в виду при создании Dockerfiles. Например, мы КОПИРУЕМ
requirements.txt сначала файл и установите зависимости перед
КОПИЕЙ в остальной части приложения. Это приводит к созданию слоя Docker, содержащего все зависимости. Этот слой не нужно перестраивать, даже если другие файлы в приложении изменяются, пока нет новых зависимостей.
Таким образом, мы оптимизируем процесс сборки нашего контейнера, отделяя установку pip
от развертывания остальной части нашего приложения.
Построение образа Docker
Теперь, когда наш Dockerfile готов и мы понимаем, как работает процесс сборки, давайте продолжим и создадим образ Docker для нашего приложения:
$ docker build -t docker-flask:latest .
Запуск приложения в режиме отладки с автоматическим перезапуском
Из-за преимуществ контейнеризации, описанных ранее, имеет смысл разрабатывать приложения, которые будут развернуты в контейнерах внутри самого контейнера. Это гарантирует, что с самого начала среда, в которой создается приложение, будет чистой и, таким образом, исключит сюрпризы во время доставки.
Однако при разработке приложения важно иметь быстрые циклы перестройки и тестирования, чтобы проверить каждый промежуточный шаг во время разработки. Для этой цели разработчики веб-приложений полагаются на средства автоматического перезапуска, предоставляемые такими фреймворками, как Flask. Это можно использовать и изнутри контейнера.
Чтобы включить автоматический перезапуск, мы запускаем контейнер Docker, сопоставляя наш каталог разработки с каталогом приложений внутри контейнера. Это означает, что Flask будет следить за файлами на хосте (через это сопоставление) для любых изменений и автоматически перезапускать приложение, когда оно обнаруживает какие-либо изменения.
Кроме того, нам также необходимо перенаправить порты приложений из контейнера на хост. Это делается для того, чтобы браузер, работающий на хосте, мог получить доступ к приложению.
Чтобы достичь этого, мы запускаем контейнер Docker с параметрами volume-mapping и port-forwarding :
$ docker run --name flaskapp -v$PWD/app:/app -p5000:5000 docker-flask:latest
Это делает следующее:
- Запускает контейнер на основе образа
docker-flask
, который мы создали ранее. - Имя этого контейнера имеет значение
flaskapp
. Без опции--name
Docker выбирает произвольное (и очень интересное) имя для контейнера. Явное указание имени поможет нам найти контейнер (для остановки и т. Д.) - Параметр
-v
монтирует папку приложения на хосте в контейнер. - Параметр
-p
сопоставляет порт контейнера с хостом.
Теперь доступ к приложению можно получить по адресу http://localhost:5000
или http://0.0.0.0:5000/
:
Если мы вносим изменения в приложение во время работы контейнера и сохраняем файл, Flask обнаруживает изменения и перезапускает приложение:
Чтобы остановить контейнер, нажмите Ctrl – C и удалите контейнер, запустив docker rm flask app
.
Запуск приложения в производственном режиме
Хотя запуск приложения с помощью Flask напрямую достаточно хорош для разработки, нам нужно использовать более надежный метод развертывания для производства.
Обычно производственному веб-приложению Flask может потребоваться обрабатывать несколько параллельных подключений, и поэтому оно обычно развертывается на веб-сервере, совместимом с WSGI.
Популярной альтернативой является nginx + uwsgi, и в этом разделе мы рассмотрим, как настроить наше веб-приложение для производства. Nginx -это веб-сервер с открытым исходным кодом, а uWSGI – “быстрый, самовосстанавливающийся контейнерный сервер приложений”.
Во-первых, мы создаем фасад, который запускает наше приложение либо в режиме разработки, либо в режиме производства, и в зависимости от режима он выберет прямой запуск nginx или Python.
Мы будем называть этот файл launch.sh
и это будет простой шелл-скрипт. Этот файл основан на entry-point.sh :
#!/bin/bash if [ ! -f /debug0 ]; then touch /debug0 while getopts 'hd:' flag; do case "${flag}" in h) echo "options:" echo "-h show brief help" echo "-d debug mode, no nginx or uwsgi, direct start with 'python3 app/app.py'" exit 0 ;; d) touch /debug1 ;; *) break ;; esac done fi if [ -e /debug1 ]; then echo "Running app in debug mode!" python3 app/app.py else echo "Running app in production mode!" nginx && uwsgi --ini /app.ini fi
Затем мы создаем файл конфигурации uWSGI для нашего приложения и конфигурацию nginx .
По сути, этот файл описывает нашу точку входа приложения в uWSGI/nginx:
[uwsgi] plugins = /usr/lib/uwsgi/plugins/python3 chdir = /app module = app:app uid = nginx gid = nginx socket = /run/uwsgiApp.sock pidfile = /run/.pid processes = 4 threads = 2
Наконец, мы модифицируем наш Dockerfile, чтобы включить nginx и uWSGI. Помимо установки nginx, uWSGI и плагина uWSGI Python3, теперь он также копирует файл nginx.conf
в соответствующее место и устанавливает права пользователя, необходимые для запуска nginx.
Кроме того, Dockerfile ENTRYPOINT
устанавливается в сценарий оболочки, который помогает нам запускать контейнер в режиме отладки или производства:
FROM ubuntu:16.04 MAINTAINER Madhuri Koushik "[email protected]" RUN apt-get update -y && \ apt-get install -y python3-pip python3-dev && \ apt-get install -y nginx uwsgi uwsgi-plugin-python3 COPY ./requirements.txt /requirements.txt COPY ./nginx.conf /etc/nginx/nginx.conf WORKDIR / RUN pip3 install -r requirements.txt COPY . / RUN adduser --disabled-password --gecos '' nginx\ && chown -R nginx:nginx /app \ && chmod 777 /run/ -R \ && chmod 777 /root/ -R ENTRYPOINT [ "/bin/bash", "/launcher.sh"]
Теперь мы можем восстановить изображение:
$ docker build -t docker-flask:latest .
И запустите приложение с помощью nginx:
$ docker run -d --name flaskapp --restart=always -p 80:80 docker-flask:latest
Этот образ является автономным и требует указания только сопоставления портов во время развертывания. Это позволит запустить и запустить команду в фоновом режиме. Чтобы остановить и удалить этот контейнер, выполните следующую команду:
$ docker stop flaskapp && docker rm flaskapp
Кроме того, если нам нужно отладить или добавить функции, мы можем легко запустить контейнер в режиме отладки, монтируя нашу собственную версию исходного дерева:
$ docker run -it --name flaskapp -p 5000:5000 -v$PWD/app:/app docker-flask:latest -d
Управление Внешними Зависимостями
При отправке приложений в контейнерах необходимо помнить, что ответственность разработчика за управление зависимостями увеличивается. Помимо определения и указания правильных зависимостей и версий, они также отвечают за установку и настройку этих зависимостей в среде контейнера.
К счастью, requirements.txt
– это простой механизм для определения зависимостей. К нему можно добавить любой пакет, доступный через pip
.
Но опять же, в любое время requirements.txt
файл изменен, образ Docker должен быть перестроен.
Установка зависимостей при запуске
Иногда может потребоваться установить дополнительные зависимости во время запуска. Скажем, вы пробуете новый пакет во время разработки и не хотите каждый раз заново создавать образ Docker или хотите использовать последнюю доступную версию на момент запуска. Этого можно добиться, изменив лаунчер для запуска pip
в начале запуска приложения.
Мы также можем установить дополнительные зависимости пакетов на уровне ОС. Давайте изменим launcher.sh
:
#!/bin/bash if [ ! -f /debug0 ]; then touch /debug0 if [ -e requirements_os.txt ]; then apt-get install -y $(cat requirements_os.txt) fi if [ -e requirements.txt ]; then pip3 install -r requirements.txt fi while getopts 'hd' flag; do case "${flag}" in h) echo "options:" echo "-h show brief help" echo "-d debug mode, no nginx or uwsgi, direct start with 'python3 app/app.py'" exit 0 ;; d) echo "Debug!" touch /debug1 ;; esac done fi if [ -e /debug1 ]; then echo "Running app in debug mode!" python3 app/app.py else echo "Running app in production mode!" nginx && uwsgi --ini /app.ini fi
Теперь, в requirements_os.txt
, мы можем указать список разделенных пробелами имен пакетов в одной строке, и они вместе с пакетами в requirements.txt
будет установлен до запуска приложения.
Хотя это предусмотрено для удобства во время разработки, устанавливать зависимости во время запуска не рекомендуется по нескольким причинам:
- Это разрушает одну из целей контейнеризации, которая заключается в исправлении и тестировании зависимостей, которые не изменяются из-за изменения среды развертывания.
- Это добавляет дополнительные накладные расходы при запуске приложения, что увеличит время запуска контейнера.
- Вытягивание зависимостей при каждом запуске приложения – это плохое использование сетевых ресурсов.
Вывод
В этой статье мы погрузились в Docker, широко используемый инструмент контейнеризации. Мы создали простое веб-приложение с Flask, пользовательским образом Docker на базе Ubuntu для запуска нашего веб-приложения в режиме разработки и производства.
Наконец, мы настроили развертывание нашего веб-приложения с помощью nginx и uWSGI в контейнере Docker и изучили методы установки внешних зависимостей.
Контейнеризация-это мощная технология, которая позволяет быстро разрабатывать и развертывать приложения в облаке, и мы надеемся, что вы сможете применить то, что узнали здесь, в своих собственных приложениях.