Сборка и развертывание приложения Flask с помощью Docker в Ubuntu 18.04

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

Flask – это микрофреймворк на Python. Он называется микрофреймворком, потому что для его работы не требуются специальные инструменты или плагины. Flask легкий и гибкий, но в то же время хорошо структурированный, что дает ему преимущества по сравнению с другими фреймворками.

Развертывание приложения Flask с помощью Docker позволит вам реплицировать его на разные серверы с минимальной перенастройкой.

Данный мануал поможет вам создать приложение Flask и развернуть его с помощью Docker, а также обновить приложение после развертывания.

Требования

1: Создание приложения Flask

Для начала нужно создать структуру каталогов, которая будет содержать приложение Flask. В мануале мы создадим каталог с именем TestApp в /var/www, но вы можете назвать его как угодно.

sudo mkdir /var/www/TestApp

Перейдите в новый каталог:

cd /var/www/TestApp

Затем создайте базовую структуру каталогов для приложения Flask:

sudo mkdir -p app/static app/templates

Флаг -p позволяет mkdir создать указанный каталог и все родительские каталоги, которых не существует. В этом случае mkdir создаст родительский каталог app в процессе создания каталогов static и templates.

Каталог app будет содержать все файлы, связанные с приложением Flask, такие как его представления и макеты. Представления – это код, который вы пишете для ответа на запросы к вашему приложению. Макеты создают компоненты приложения и поддерживают общие шаблоны в одном или в нескольких приложениях.

Каталог static – это место, где находятся такие ресурсы, как изображения, файлы CSS и JavaScript. Каталог templates – это место, где хранятся HTML-шаблоны для вашего проекта.

Теперь, когда базовая структура каталогов готова, создайте файлы, необходимые для запуска приложения Flask. Сначала создайте файл __init__.py внутри каталога приложения. Этот файл сообщает интерпретатору Python, что каталог app является пакетом и должен рассматриваться как таковой.

Чтобы создать файл, введите:

sudo nano app/__init__.py

Пакеты в Python позволяют группировать модули в логические пространства имен или иерархии. Такой подход позволяет разбить код на отдельные и управляемые блоки, выполняющие определенные функции.

Затем нужно добавить в __init__.py код, который создаст экземпляр Flask и импортирует логику из файла views.py (его мы создадим после). Добавьте следующий код в ваш новый файл:

from flask import Flask
app = Flask(__name__)
from app import views

Сохраните и закройте файл.

Теперь можно создать файл views.py в каталоге app. В этом файле будет храниться основная логика приложения.

sudo nano app/views.py

Добавьте в файл views.py следующий код. Он будет возвращать строку hello world! пользователям, посетившим вашу страницу.

from app import app
@app.route('/')
def home():
return "hello world!"

Строка @app.route перед функцией называется декоратором. Декораторы модифицируют функцию, которая следует за ним. В этом случае декоратор сообщает Flask, какой URL вызовет функция home(). Текст «hello world», возвращенный функцией home, будет отображаться пользователю в браузере.

После файла views.py вы готовы создать файл uwsgi.ini. Этот файл будет содержать настройки uWSGI для вашего приложения. uWSGI – это средство развертывания для Nginx, которое является одновременно протоколом и сервером приложений; сервер приложений может обслуживать протоколы uWSGI, FastCGI и HTTP.

Чтобы создать этот файл, выполните следующую команду:

sudo nano uwsgi.ini

Затем добавьте следующее содержимое в ваш файл для настройки сервера uWSGI:

[uwsgi]
module = main
callable = app
master = true

Этот код определяет модуль, с помощью которого будет обслуживаться приложение Flask. В данном случае это файл main.py, упоминаемый здесь как main. Опция callable позволяет uWSGI использовать экземпляр app, экспортированный основным приложением. Опция master позволяет вашему приложению продолжать работу, поэтому при перезагрузке всего приложения время простоя будет небольшим.

Затем создайте файл main.py, который является точкой входа в приложение. Точка входа объясняет uWSGI, как взаимодействовать с приложением.

sudo nano main.py

Затем скопируйте и вставьте следующее в файл. Это импортирует экземпляр приложения Flask по имени app из ранее созданного пакета приложения.

from app import app

Наконец, создайте файл requirements.txt, чтобы указать зависимости, которые менеджер пакетов pip установит при развертывании Docker:

sudo nano requirements.txt

Вставьте следующую строку, чтобы добавить Flask в качестве зависимости:

Flask==1.0.2

Эта строка указывает версию Flask, которая будет установлена. На момент написания этого мануала 1.0.2 была последней версией Flask. Вы можете проверить наличие обновлений на официальном сайте Flask.

Сохраните и закройте файл. Вы успешно настроили свое приложение.

2: Настройка Docker

На этом этапе пора создать два файла, Dockerfile и start.sh, чтобы подготовить развертывание Docker. Dockerfile – это текстовый документ, который содержит команды, используемые для сборки образа. Start.sh – это сценарий оболочки, который создаст образ и контейнер из Dockerfile.

Сначала создайте Dockerfile.

sudo nano Dockerfile

Затем добавьте желаемую конфигурацию в Dockerfile. Эти команды определяют, как будет построен образ, и какие дополнительные требования будут включены.

FROM tiangolo/uwsgi-nginx-flask:python3.6-alpine3.7
RUN apk --update add bash nano
ENV STATIC_URL /static
ENV STATIC_PATH /var/www/app/static
COPY ./requirements.txt /var/www/requirements.txt
RUN pip install -r /var/www/requirements.txt

В этом примере образ Docker будет собран на основе существующего образа tiangolo/uwsgi-nginx-flask, который можно найти на DockerHub. Этот образ Docker поддерживает широкий спектр версий Python и образов ОС.

Первые две строки указывают родительский образ, который вы будете использовать для запуска приложения, установки процессора команд bash и текстового редактора nano. Они также устанавливают клиент git для извлечения и загрузки кода на хостинг-сервисы контроля версий (такие как GitHub, GitLab и Bitbucket). ENV STATIC_URL/static — переменная окружения, специфичная для этого образа Docker. Он определяет папку, из которой обслуживаются все ресурсы, такие как изображения, файлы CSS и JavaScript.

Последние две строки скопируют файл requirements.txt в контейнер, чтобы его можно было выполнить, а затем проанализируют файл requirements.txt для установки указанных зависимостей.

Сохраните и закройте файл после добавления конфигурации.

Dockerfile готов. Скоро вы сможете написать свой скрипт start.sh, который будет создавать контейнер Docker. Но сначала нужно убедиться, что у вас есть открытый порт, который можно указать в конфигурации. Чтобы проверить, свободен ли тот или иной порт, выполните следующую команду:

sudo nc localhost 56733 < /dev/null; echo $?

Если вывод команды выше равен 1, то порт свободен и доступен для использования. В противном случае вам нужно будет выбрать другой порт для конфигурации start.sh.

Найдя открытый порт для использования, создайте скрипт start.sh:

sudo nano start.sh

Сценарий start.sh – это сценарий оболочки, который будет создавать образ из Dockerfile и собирать контейнер из получившегося образа Docker. Добавьте такую конфигурацию в новый файл:

#!/bin/bash
app="docker.test"
docker build -t ${app} .
docker run -d -p 56733:80 \
--name=${app} \
-v $PWD:/app ${app}

Первая строка называется шебанг. Она указывает, что это bash-файл, и что он будет выполняться как команды. Следующая строка указывает имя, которое нужно присвоить образу и контейнеру, и сохраняет его как переменную app. Следующая строка помогает Docker собрать образ из Dockerfile, расположенного в текущем каталоге. В итоге получится образ docker.test (в этом примере).

Последние три строки создают новый контейнер docker.test, доступ к которому осуществляется через порт 56733, а затем связывают текущий каталог с каталогом /var/www  контейнера.

Флаг -d используется для запуска контейнера в режиме демона или в качестве фонового процесса. Флаг -p привязывает порты на сервере к определенному порту в контейнере Docker. В этом случае порт 56733 привязывается к порту 80 на контейнере Docker. Флаг -v указывает том Docker для монтирования в контейнере (в этом случае весь каталог проекта смонтирован в папку /var/www контейнера Docker).

Выполните скрипт start.sh, чтобы создать образ Docker и собрать контейнер из полученного образа:

sudo bash start.sh

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

sudo docker ps

Вы получите такой вывод:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                            NAMES
58b05508f4dd        docker.test         "/entrypoint.sh /sta…"   12 seconds ago      Up 3 seconds       443/tcp, 0.0.0.0:56733->80/tcp   docker.test

Вы увидите, что контейнер docker.test запущен. Теперь, когда он работает, перейдите по IP-адресу, указав ваш порт: http: // ip-address: 56733

Вы увидите страницу с сообщением:

Hello world!

Вы успешно развернули приложение Flask в Docker. Далее нужно научиться использовать шаблоны для отображения контента пользователям.

3: Обслуживание шаблонов

Шаблоны — это файлы, которые отображают статический и динамический контент для пользователей, посещающих ваше приложение. Давайте создадим HTML-шаблон для домашней страницы приложения.

Для начала создайте файл home.html в каталоге app/templates:

sudo nano app/templates/home.html

Добавьте код для вашего шаблона. Этот код создаст страницу HTML5, которая содержит заголовок и текст.

<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Welcome home</title>
</head>
<body>
<h1>Home Page</h1>
<p>This is the home page of our application.</p>
</body>
</html>

Сохраните и закройте файл.

Затем измените файл app/views.py для обслуживания созданного файла:

sudo nano app/views.py

Добавьте следующую строку в начало вашего файла, чтобы импортировать метод render_template из Flask. Этот метод анализирует файл HTML и отображает веб-страницу пользователю.

from flask import render_template
...

В конце поместите новый маршрут для рендеринга файла шаблона. Этот код указывает, что содержимое файла home.html предоставляется пользователям каждый раз, когда они посещают маршрут /template в вашем приложении.

...
@app.route('/template')
def template():
return render_template('home.html')

В итоге файл app/views.py должен выглядеть так:

from flask import render_template
from app import app
@app.route('/')
def home():
return "Hello world!"
@app.route('/template')
def template():
return render_template('home.html')

Сохраните и закройте файл.

Чтобы эти изменения вступили в силу, нужно остановить и перезапустить контейнеры Docker. Выполните следующую команду, чтобы пересобрать контейнер:

sudo docker stop docker.test && sudo docker start docker.test

Посетите ваше приложение по адресу http://your-ip-address:56733/template, чтобы увидеть новый обслуживаемый шаблон.

This is the home page of our application.

Вы создали шаблон Docker для обслуживания посетителей вашего приложения. На следующем этапе мы покажем, как можно обновить приложение без перезапуска контейнера Docker.

4: Обновление приложения

Иногда появляется необходимость внести в приложение какие-то изменения, будь то установка новых требований, обновление контейнера Docker или отладка HTML и логики. В этом разделе мы настроим touch-reload для внесения этих изменений без необходимости перезапуска контейнера Docker.

Автозагрузка Python отслеживает изменения всей файловой системы и обновляет приложение при обнаружении изменений. Автоматическую перезагрузку не рекомендуется выполнять в производстве, поскольку она может очень быстро потреблять ресурсы. Теперь можно использовать touch-reload для отслеживания изменений в конкретном файле и его перезагрузки в случае обновления или замены.

Откройте файл uwsgi.ini:

sudo nano uwsgi.ini

Добавьте в него выделенную красным строку:

module = main
callable = app
master = true
touch-reload = /app/uwsgi.ini

Эта строка указывает файл, изменение которого будет вызвать полную перезагрузку приложения. После внесения изменений сохраните и закройте файл.

Чтобы продемонстрировать работу автозагрузки, внесите небольшое изменение в свое приложение. Откройте app/views.py:

sudo nano app/views.py

Замените строку, возвращаемую функцией home:

from flask import render_template
from app import app
@app.route('/')
def home():
return "<b>There has been a change</b>"
@app.route('/template')
def template():
return render_template('home.html')

Сохраните и закройте файл после внесения изменений.

Если вы теперь откроете домашнюю страницу своего приложения по адресу http://ip-address:56733, вы заметите, что изменения не появились. Это связано с тем, что условием для перезагрузки является только изменение файла uwsgi.ini. Чтобы активировать условие и перезагрузить приложение, используйте touch:

sudo touch uwsgi.ini

Перезагрузите домашнюю страницу приложения в вашем браузере снова. Вы обнаружите, что приложение подгрузило изменения:

There has been a change

Заключение

Теперь вы умеете создавать и развертывать приложения Flask в контейнере Docker. Также вы знаете, как настроить touch-reload и обновить свое приложение, не останавливая контейнер.

Полученное здесь тестовое приложение можно использовать как шаблон для более сложных вещей. Много полезного можно найти в документации Docker.

Tags: , , ,