Рубрики
Без рубрики

Докеризация приложений Python

Автор оригинала: 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 обнаруживает изменения и перезапускает приложение:

Скриншот веб-приложения

Чтобы остановить контейнер, нажмите CtrlC и удалите контейнер, запустив 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 и изучили методы установки внешних зависимостей.

Контейнеризация-это мощная технология, которая позволяет быстро разрабатывать и развертывать приложения в облаке, и мы надеемся, что вы сможете применить то, что узнали здесь, в своих собственных приложениях.