Для чего вообще нужен docker контейнер? Обычно, во время разработки, для каждого проекта вы настраиваете своё окружение. Но вот произошла такая ситуация: что-то случилось с вашим компьютером и приходится переустанавливать операционную систему(ОС). Соответственно, чтобы запустить ваш проект, необходимо настраивать окружение заново. Бывает ещё гигантское количество ситуаций, которые сводятся к одной проблеме - настройка окружения для разработки. Так вот Docker - коробка, которую достаточно единожды настроить под проект, чтобы в дальнейшем не было проблем с эксплуатацией/расширением сервиса

Для начала необходимо установить Docker Engine по одной из этих инструкций
Следующим этапом устанавливаем Docker Compose - к счастью, эта инструкция меньше =)
Поздравляю! Вы проделали треть работы
Давайте создадим структуру нашего проекта.
- backend/ - main.py - requirements.txt - Dockerfile - docker-compose.yml
В файле requirements.txt у нас будут прописаны, скачиваемые для проекта библиотеки.
Flask==1.1.2
В main.py содержится простенький сервер для проверки работоспособности контейнера.
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return "<h1>Hello, World!<h1>" if __name__ == '__main__': app.run(host='0.0.0.0')
И, переходя к самому сладкому, расскажу про ещё одну важную вещь - DockerHub. Это место, где разработчики размещают свои созданные образы контейнеров. Вы тоже можете разместить свой образ на этой площадке.
Перейдём к следующему шагу - созданию Dockerfile.
# Выкачиваем из dockerhub образ с python версии 3.9 FROM python:3.9 # Устанавливаем рабочую директорию для проекта в контейнере WORKDIR /backend # Скачиваем/обновляем необходимые библиотеки для проекта COPY requirements.txt /backend RUN pip3 install --upgrade pip -r requirements.txt # |ВАЖНЫЙ МОМЕНТ| копируем содержимое папки, где находится Dockerfile, # в рабочую директорию контейнера COPY . /backend # Устанавливаем порт, который будет использоваться для сервера EXPOSE 5000
Но Dockerfile - это лишь организация рабочего пространства внутри нашего контейнера. Так как запросы поступают извне, необходимо настроить инфраструктуру. Для этого нам нужен docker-compose.yml.
version: '3' services: flask: # Путь до Dockerfile build: ./backend # Имя для создаваемого контейнера container_name: backend-flask # Создание переменных окружения в контейнере environment: # для отладки (при запуске в релиз убрать!) - FLASK_ENV=development ## Позволяет отслеживать процесс работы приложения в командной строке - PYTHONUNBUFFERED=True ## # Перезапускаем сервис в случае падения restart: on-failure # Прокладывам путь для файлов. Все файлы, которые хранятся у вас в # директории ./backend, появятся в директории контейнера /backend volumes: - ./backend:/backend # Открываем порт в контейнер # Порт, который будет смотреть наружу : порт который используется внутри контейнера ports: - "5000:5000" command: python main.py
Всё готово, осталось только запустить командой docker-compose upиз директории с файлом docker-compose.yml.
Сервер на ПРОКАЧКУ
Круто! Теперь у нас есть веб-сервер, который развёрнут при помощи контейнеров, но этого всё ещё мало. Нам нужна более серьёзная архитектура, как у крутых проггеров. Давайте её прокачаем!
Добавим к нашему проекту gunicorn для будущего распределения нагрузки.
requirements.txt
Flask==1.1.2 gunicorn==20.0.4
И расширим нашу структуру приложения. Теперь приложение выглядит так.
- backend/ - app/ - __init__.py - routes.py - config.py - modules/ - hello_world.py - main.py - requirements.txt - Dockerfile - settings.ini - docker-compose.yml
Как вы наверняка заметили, в структуру добавились файл settings.ini, папка для инициализации нашего приложения и страничка modules/hello_world.py.
Обо всём по порядку.
Инициализация приложения
В файл app/__init__.py импортируем библиотеки. Заранее импортируем наши компоненты, которые опишем чуть ниже.
import os from flask import Flask from app.routes import route from app.config import config, init_config
Создадим функцию, которая будет создавать наше приложение.
def create_flask_app(): app = Flask(__name__) return app
В функции инициализируем пути к модулям(routes), подключим файл с конфигом и обновим конфигурацию приложения.
def create_flask_app(): app = Flask(__name__) # Подключаем все роуты приложения route(app) # Считываем переменную окружения "CONFIG_PATH", если она есть, # то берём путь из неё, иначе указанный по умолчанию "./settings.ini" path = os.environ.get('CONFIG_PATH') if os.environ.get( 'CONFIG_PATH') else "./settings.ini" # Инициализируем конфиг по вышеуказанному пути init_config(path) # Обновляем конфигурацию приложения Flask # Если файл не найден или данные которые используются при обновлении отсутствуют, # то вылетит Exception - KeyError try: app.config.update(dict( SECRET_KEY=str(config['FLASK_APP']['FLASK_APP_SECRET_KEY']) )) print(f"\n\033[32m Сервер запустился с конфигом:\n\033[32m {path}\n") except KeyError: print(f"\033[31m Файл {path} не найден или неверный") return app
В конечном итоге, файл app/__init__.py выглядит так.
import os from flask import Flask from app.routes import route from app.config import config, init_config def create_flask_app(): app = Flask(__name__) route(app) path = os.environ.get('CONFIG_PATH') if os.environ.get( 'CONFIG_PATH') else "./settings.ini" init_config(path) try: app.config.update(dict( SECRET_KEY=str(config['FLASK_APP']['FLASK_APP_SECRET_KEY']) )) print(f"\n\033[32m Сервер запустился с конфигом:\n\033[32m {path}\n") except KeyError: print(f"\033[31m Файл {path} не найден или неверный") return app
Теперь можно и переписать app/main.py.
from app import create_flask_app if __name__ == "__main__": create_flask_app().run(host='0.0.0.0')
После всех изменений с инициализацией приложения нужно поменять в docker-compose.yml команду запуска приложения.
# Заменить command: python main.py # на command: gunicorn main:"create_flask_app()" -b 0.0.0.0:5000 --reload # gunicorn запускает в файле main.py, функцию create_flask_app по адресу 0.0.0.0:5000
Конфигурационный файл
Во время разработки в приложении частенько, могут встречаться, данные вроде паролей, адресов, портов, токенов и прочих секретных вещей, которые нельзя оставлять в коде. Для этого всё выносится в отдельный файл (который, конечно же, никуда не выкладывается :))
За инициализацию конфига будет отвечать config.py.
import os import configparser config = configparser.ConfigParser() def init_config(path): config.optionxform = str config.read(path)
Прорубаем путь до вашей странички
Дело в том, что в первой версии нашего приложения адрес для страницы указывали в декораторе app
@app.route('/'), но так как app теперь используется только для ин��циализации, то нам нужно его чем-то заменить. Здесь нас выручит класс Blueprint. Blueprint используется для создания модульных приложений Flask`а.
Для начала, создадим страничку в файле modules/hello_world.py.
from flask import Blueprint hello_world_bp = Blueprint('hello_world', __name__) @hello_world_bp.route('/') def hello_world(): return "<h1>Hello, World!<h1>"
После того, как создали новый модуль приложения, необходимо сказать Flask`у, где он располагается (зарегистрировать модуль). Здесь и нужен файл routes.py.
from modules.hello_world import hello_world_bp def route(app): app.register_blueprint(hello_world_bp)
Теперь при попытке запустить приложение docker-compose up --build (флаг build нужен для пересборки контейнера, т.к. добавили новые пакеты для скачивания) должна вылететь ошибка о том, что что-то не так с нашим конфигом.
Starting backend-flask ... done Attaching to backend-flask backend-flask | [2020-12-26 09:05:41 +0000] [1] [INFO] Starting gunicorn 20.0.4 backend-flask | [2020-12-26 09:05:41 +0000] [1] [INFO] Listening at: <http://0.0.0.0:5000> (1) backend-flask | [2020-12-26 09:05:41 +0000] [1] [INFO] Using worker: sync backend-flask | [2020-12-26 09:05:41 +0000] [7] [INFO] Booting worker with pid: 7 backend-flask | Файл ./settings.ini не найден или неверный
Заходим в settings.ini и добавляем значение, которое отсутствовало.
[FLASK_APP] FLASK_APP_SECRET_KEY=Super_slojniy_secret_key
Запускаем приложение и радуемся
Starting backend-flask ... done Attaching to backend-flask backend-flask | [2020-12-26 09:12:34 +0000] [1] [INFO] Starting gunicorn 20.0.4 backend-flask | [2020-12-26 09:12:34 +0000] [1] [INFO] Listening at: <http://0.0.0.0:5000> (1) backend-flask | [2020-12-26 09:12:34 +0000] [1] [INFO] Using worker: sync backend-flask | [2020-12-26 09:12:34 +0000] [7] [INFO] Booting worker with pid: 7 backend-flask | backend-flask | Сервер запустился с конфигом: backend-flask | ./settings.ini backend-flask |
Поздравляю, вы прекрасны! =)

