- •ВВЕДЕНИЕ
- •1. ПОДГОТОВКА К РАЗРАБОТКЕ
- •1.1. НАЧАЛО РАБОТЫ
- •1.2. ИСПОЛЬЗОВАНИЕ КОНТЕЙНЕРОВ DOCKER
- •1.3. НАСТРОЙКА СУБД POSTGRESQL
- •1.4. НАСТРОЙКА ТЕСТОВ
- •1.6. СОЗДАНИЕ RESTFUL-МАРШРУТОВ
- •1.6.1. МАРШРУТ POST /USERS
- •1.6.3. МАРШРУТ GET /USERS
- •1.6.2. МАРШРУТ GET /USERS/:ID
- •1.7. ИСПОЛЬЗОВАНИЕ ШАБЛОНОВ JINJA
- •1.8. СТРУКТУРА ПРОЕКТА
- •2. ОТЛАДКА ПРИЛОЖЕНИЯ
- •2.1. ПОКРЫТИЕ ТЕСТАМИ И КАЧЕСТВО КОДА
- •2.1.1. ПОКРЫТИЕ КОДА ТЕСТАМИ
- •2.1.2. КАЧЕСТВО КОДА
- •2.2. НЕПРЕРЫВНАЯ ИНТЕГРАЦИЯ
- •2.3. ПАНЕЛЬ ИНСТРУМЕНТОВ ОТЛАДКИ FLASK
- •2.4. СТРУКТУРА ПРИЛОЖЕНИЯ
- •3. РАЗВИТИЕ ПРИЛОЖЕНИЯ
- •3.1. ПАКЕТ FLASK MIGRATE
- •3.1.1. ИЗМЕНЕНИЕ МОДЕЛИ
- •3.1.2. НАСТРОЙКА FLASK MIGRATE
- •3.2. РЕФАКТОРИНГ КОДА
- •3.3. ПАКЕТ FLASK BCRYPT
- •3.4. НАСТРОЙКА LOG ROUNDS
- •3.5. ИСПОЛЬЗОВАНИЕ ТОКЕНОВ JWT
- •3.5.1. ПАКЕТ PYJWT
- •3.5.2. РАСШИФРОВКА ТОКЕНА
- •3.6. МАРШРУТЫ ДЛЯ АУТЕНТИФИКАЦИИ
- •3.6.1. МАРШРУТ POST /AUTH/REGISTER
- •3.6.2. МАРШРУТ POST /AUTH/LOGIN
- •3.6.3. МАРШРУТ GET /AUTH/LOGOUT
- •3.6.4. МАРШРУТ GET /AUTH/STATUS
- •ЗАКЛЮЧЕНИЕ
- •СПИСОК ЛИТЕРАТУРЫ
'message': 'pong!'
})
1.2. ИСПОЛЬЗОВАНИЕ КОНТЕЙНЕРОВ DOCKER
Для начала необходимо убедиться, что в окружении установлены пакеты
Docker, Docker Compose и Docker Machine:
$ docker -v
Docker version 17.12.0-ce, build c97c6d6 $ docker-compose -v
docker-compose version 1.18.0, build 8dd22a9 $ docker-machine -v
docker-machine version 0.13.0, build 9ba6da9
Перейдём к задаче создания нового хоста Docker с помощью Docker
Machine и связыванию его с клиентом Docker:
$ docker-machine create -d virtualbox testdriven-dev $ docker-machine env testdriven-dev
$ eval "$(docker-machine env testdriven-dev)"
Добавим файл Dockerfile-dev следующего содержания в каталог users:
FROM python:3.6.4
# определение рабочего каталога
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# добавление файла с требованиями
COPY ./requirements.txt /usr/src/app/requirements.txt
# установка зависимостей
RUN pip install -r requirements.txt
# добавление приложения
COPY . /usr/src/app
# запуск сервера
CMD python manage.py run -h 0.0.0.0
Также добавим файл .dockerignore в каталог users:
env
.dockerignore Dockerfile-dev Dockerfile-prod
7
Как и файл. gitignore, файл .dockerignore позволяет исключить определён-
ные файлы и каталоги из списка копируемых объектов в образ. Затем добавим в корень файл docker-compose-dev.yml со следующим содержимым:
version: '3.4'
services:
users:
container_name: users build:
context: ./services/users dockerfile: Dockerfile-dev
volumes:
-'./services/users:/usr/src/app'
ports:
-5001:5000
environment:
-FLASK_APP=project/__init__.py
-FLASK_DEBUG=1
Эта конфигурация служит для создания контейнера с именем users из файла Dockerfile. Том volume используется для монтирования кода в контейнер.
Это необходимо для среды разработки, чтобы контейнер обновлялся при каж-
дом изменении исходного кода. Без этого пришлось бы пересобирать образ по-
сле каждого изменения, внесённого в исходный код.
Обратите внимание на используемую версию файла сборки Docker – 3.4.
Имейте в виду, что это не связано напрямую с установленной версией Docker Compose – версия определяет формат файла, который мы используем.
Создадим образ:
$ docker-compose -f docker-compose-dev.yml build
При первом запуске процесс займёт несколько минут. Последующие сборки будут выполнять намного быстрее, поскольку Docker кеширует резуль-
таты первой сборки. Как только сборка будет завершена, запустим контейнер:
$ docker-compose -f docker-compose-dev.yml up -d
Флаг -d используется для запуска контейнеров в фоновом режиме.
Для получения IP-адреса, связанного с контейнером, выполним следую-
щую команду:
docker-machine ip testdriven-dev
Перейдём по адресу http://DOCKER_MACHINE_IP:5001/users/ping. Мы убедимся, что видим тот же JSON-ответ, что и раньше. Затем добавим пере-
8
менную среды в файл docker-compose-dev.yml, чтобы загрузить конфигурацию
приложения для среды разработки:
version: '3.4'
services:
users: container_name: users build:
context: ./services/users dockerfile: Dockerfile-dev
volumes:
-'./services/users:/usr/src/app'
ports:
-5001:5000
environment:
-FLASK_APP=project/__init__.py
-FLASK_DEBUG=1
-APP_SETTINGS=project.config.DevelopmentConfig
Затем обновим файл project/__init__.py, чтобы получить переменные среды:
#services/users/project/__init__.py
#инстанционирование приложения
app = Flask(__name__)
# подключение конфигурации
app_settings = os.getenv('APP_SETTINGS') app.config.from_object(app_settings)
@app.route('/users/ping', methods=['GET']) def ping_pong():
return jsonify({ 'status': 'success', 'message': 'pong!' })
Обновим контейнер:
$ docker-compose -f docker-compose-dev.yml up -d
Чтобы убедиться в том, что загружена правильная конфигурация, необ-
ходимо добавить оператор print в файл __init__.py перед обработчиком мар-
шрута:
import sys
9
print(app.config, file=sys.stderr)
После этого просмотрим записи в журнале:
$ docker-compose -f docker-compose-dev.yml logs
Мы должны увидеть результат похожий на следующий вывод:
<Config {
'DEBUG': True, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'USE_X_SENDFILE': False, 'LOGGER_NAME': 'project', 'LOGGER_HANDLER_POLICY': 'always', 'SERVER_NAME': None, 'APPLICATION_ROOT': None, 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(0, 43200), 'TRAP_BAD_REQUEST_ERRORS': False, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': True, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None}
>
Обязательно удалите оператор print перед тем, как продолжить разработ-
ку приложения дальше.
1.3. НАСТРОЙКА СУБД POSTGRESQL
Перейдём к настройке сервиса PostgreSQL. Запустим его, развернём в контейнере и свяжем с контейнером users. Добавим пакеты Flask-SQLAlchemy
и psycopg2 в файл requirements.txt:
Flask-SQLAlchemy==2.3.2
psycopg2==2.7.3.2
Обновим файл config.py:
# services/users/project/config.py import os
class BaseConfig:
"""Базовая конфигурация"""
TESTING = False SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(BaseConfig):
10
"""Конфигурация для разработки"""
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
class TestingConfig(BaseConfig):
"""Конфигурация для тестирования"""
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_TEST_URL')
class ProductionConfig(BaseConfig):
"""Конфигурация для производственной среды"""
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
Обновим файл __init__.py, чтобы создать новый экземпляр SQLAlchemy и
определим модель базы данных:
# services/users/project/__init__.py import os
import datetime
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
# инстанционирование приложения app = Flask(__name__)
# подключение конфигурации
app_settings = os.getenv('APP_SETTINGS') app.config.from_object(app_settings)
# инстанционирование базы данных db = SQLAlchemy(app)
# модель
class User(db.Model): __tablename__ = "users"
id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(128), nullable=False)
email = db.Column(db.String(128), nullable=False)
active = db.Column(db.Boolean(), default=True, nullable=False)
def __init__(self, username, email): self.username = username self.email = email
# маршруты
@app.route('/users/ping', methods=['GET']) def ping_pong():
11
return jsonify({
'status': 'success',
'message': 'pong!'
})
Добавим каталог db в структуру project и добавим файл create.sql в этот новый каталог:
CREATE DATABASE users_prod;
CREATE DATABASE users_dev;
CREATE DATABASE users_test;
Затем добавим файл Dockerfile в тот же каталог:
FROM postgres
# запуск скрипта create.sql при инициализации
ADD create.sql /docker-entrypoint-initdb.d
Мы расширяем официальный образ PostgreSQL, добавляя файл формата
SQL в каталог docker-entrypoint-initdb.d контейнера, который будет выполнять-
ся при инициализации.
Обновим содержимое файла docker-compose.yml-dev:
version: '3.4' services:
users: container_name: users build:
сontext: ./services/users dockerfile: Dockerfile-dev
volumes:
- './services/users:/usr/src/app' ports:
-5001:5000
environment:
-FLASK_APP=project/__init__.py
-FLASK_DEBUG=1
-APP_SETTINGS=project.config.DevelopmentConfig
-DATABASE_URL=postgres://postgres:postgres@usersdb:5432/users_dev
-DATABASE_TEST_URL=postgres://postgres:postgres@usersdb:5432/users_test
depends_on:
-users-db
links:
- users-db
12
users-db:
container_name: users-db build:
context: ./services/users/project/db dockerfile: Dockerfile
ports:
-5435:5432
environment:
-POSTGRES_USER=postgres
-POSTGRES_PASSWORD=postgres
После запуска СУБД PostgreSQL будет доступна на порту 5435 на хост-
машине и на порту 5432 для служб, работающих в других контейнерах. По-
скольку служба users зависит не только от того, что контейнер запущен и вы-
полняется, но и от того, что фактический экземпляр PostgreSQL также запущен и выполняется, добавим файл-скрипт entrypoint.sh в контейнер users:
#!/bin/sh
echo "Waiting for postgres..." while ! nc -z users-db 5432;
do sleep 0.1 done
echo "PostgreSQL started"
python manage.py run -h 0.0.0.0
Обновим файл Dockerfile-dev:
FROM python:3.6.4
# установка зависимостей среды
RUN apt-get update -yqq \
&&apt-get install -yqq --no-install-recommends \ netcat \
&&apt-get -q clean
# определение рабочего каталога
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# добавление файла с требованиями
COPY ./requirements.txt /usr/src/app/requirements.txt
13
