Программисты любят программировать. Но если вы – программист, и результат вашего творения делается не “в стол”, рано или поздно наступит момент, когда нужно показать его миру: заказчику, пользователям, инвесторам, etc. Хорошо, когда вы работаете в компании, где есть целый отдел (или хотя бы отдельный специально обученный человек), который может развернуть ваше приложение, где вы скажете и как вы скажете. Однако не все компании могут себе такое позволить. А уж если вы фрилансер или это ваш пет-проект, развертывание приложения точно ляжет на вас.

О чем вам нужно позаботиться перед развертыванием? Арендовать сервер, настроить его, зарегистрировать доменное имя, получить SSL-сертификат, подумать о доставке обновлений.

Чтобы предметно рассмотреть процесс развертывания, напишем небольшой API-сервис TODO-заметок на языке программирования Python с использованием микрофреймворка Flask и развернем его разными способами.

Планирование

Каждая заметка будет определяться следующим образом:

{
  "text": "Купить молоко",
  "done": true
}

Все заметки будут храниться массивом в файле формата JSON.

Определим API следующим образом.

GET /todo получает список всех TODO.
GET /todo/<id> получает TODO с заданным id (индексом в массиве).
POST /todo добавляет новую TODO в конец списка.
PUT /todo/<id> заменяет TODO с заданным id.

Реализация

Для начала напишем само приложение.

Так как наше приложение использует Flask, создадим файл requirements.txt:

Flask==2.2.2
Flask-CORS==3.0.10

Напишем код приложения, работающий локально, в файле app.py:

import json
from flask import Flask, request, abort
from flask_cors import CORS

FILENAME = "todo.json"

def get_data():
   try:
       with open(FILENAME, "r", encoding="utf-8") as f:
           return json.load(f)
   except FileNotFoundError:
       return []

def save_data(data):
   with open(FILENAME, "w", encoding="utf-8") as f:
       json.dump(data, f)

app = Flask(__name__)
cors = CORS(app)

@app.route("/")
def index():
   return "TODO App"

@app.route("/todo")
def get_all_todo():
   return get_data()

@app.route("/todo/<int:id>")
def get_single_todo(id):
   data = get_data()
   if id < 0 or id >= len(data):
       abort(404)
   return data[id]

@app.route("/todo", methods=["POST"])
def add_new_todo():
   new_todo = request.json
   if new_todo is None:
       abort(400)
   data = get_data()
   data.append(new_todo)
   save_data(data)
   return "OK", 201
   
@app.route("/todo/<int:id>", methods=["PUT"])
def update_todo(id):
   data = get_data()
   if id < 0 or id >= len(data):
       abort(404)
   updated_todo = request.json
   if updated_todo is None:
       abort(400)
   data[id] = updated_todo
   save_data(data)
   return "OK"

if __name__ == "__main__":
   app.run(port=8080)

Установим зависимости:

pip install -r requirements.txt

Запустим приложение:

python app.py

Убедимся в его работоспособности при помощи Postman

Визуализация Postman

Развертывание вручную

Если развертывание приложений не является для вас чем-то регулярным, вы вряд ли на память помните все шаги, да и шаблонов нужных конфигурационных файлов у вас (еще) нет. В этом случае вы скорее всего загуглите что-то вроде “nginx gunicorn flask” (или даже “flask production”, если до этого вы вообще ни разу ничего не развертывали) и скопируете конфигурационные файлы из первой статьи, которую найдете. Так как об этом написано множество статей (например, эта), пробежимся по этому процессу в общих чертах:

  1. Арендовать виртуальный сервер. Столкнуться с тем, что там Linux. Почитать статьи, посмотреть видео на ютубе, запомнить основные команды.

  2. Установить nginx.

  3. Установить pip и сопутствующие пакеты.

  4. Установить git.

  5. Склонировать репозиторий с кодом приложения на сервере.

  6. Создать виртуальное окружение python (опционально).

  7. Установить зависимости из requirements.txt

  8. Добавить виртуальный хост в nginx:

    1. Зарегистрировать доменное имя.

    2. Написать конфиг в /etc/nginx/sites-available (возможно, узнать как вообще пишутся конфиги виртуальных хостов для nginx и что такое upstream?).

    3. Сделать симлинк на написанный конфиг в /etc/nginx/sites-enabled.

    4. Перезапустить nginx.

  9. Столкнуться с тем, что браузер считает наш сервис небезопасным, установить certbot и сгенерировать SSL-сертификат.

  10. Почувствовать радость от победы, но в будущем обнаружить, что при перезагрузке сервера приложение не перезапускается.

  11. Научиться писать unit-файлы для systemd (+ узнать о существовании systemd).

У автора на этот процесс уходит примерно час-полтора, если не учитывать борьбу с 502 Bad Gateway в nginx из-за опечатки в конфиге.

Что можно улучшить?

Если развертывать нужно часто и однотипные приложения, то в какой-то момент вы начнете задумываться об автоматизации этого процесса: разберетесь с Ansible, начнете использовать CI/CD решения. Это безусловно будет для вас полезно, но, может быть, можно проще?

В таких сервисах, как Amvera или Heroku, вам уже предоставляется репозиторий с кодом, куда достаточно сделать git push для развертывания. Рассмотрим развертывание приложения в Amvera.

Так как в Amvera приложения исполняются в контейнерах, напишем Dockerfile для нашего приложения.

FROM python:3.10-slim-buster

WORKDIR /app

ENV PORT=80

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

ENTRYPOINT gunicorn --bind 0.0.0.0:$PORT app:app

Так как для развертывания нашего приложения мы используем gunicorn, добавим его в requirements.txt:

Flask==2.2.2
Flask-CORS==3.0.10
gunicorn==20.1.0

В отличие от виртуальных машин для сохранности данных их нужно помещать в папку /data.

Добавим import os в начало файла app.py, а также изменим объявление переменной FILENAME:

FILENAME = "/data/todo.json" if "AMVERA" in os.environ else "todo.json"

Теперь начало файла app.py выглядит так:

import os
import json
from flask import Flask, request, abort
from flask_cors import CORS

FILENAME = "/data/todo.json" if "AMVERA" in os.environ else "todo.json"

def get_data():
# ... остаток файла

Инициализируем репозиторий git:

git init

Добавим созданные нами файлы в индекс:

git add app.py requirements.txt amvera.yml

Зафиксируем изменения:

git commit -m "TODO App"

Создание проекта в Amvera

Теперь нужно создать проект в Amvera. Откроем страницу https://cloud.amvera.ru/projects и нажмите кнопку "Создать". Укажите параметры как на изображении:

Перейдем на страницу проекта: https://cloud.amvera.ru/projects/todo-app. Там нас интересуют команды для привязки проекта к существующему репозиторию:

git remote add amvera https://git.amvera.ru/<имя-пользователя>/todo-app
git push amvera master

После этого начнется сборка и развертывание проекта. Дождитесь появления статуса "Успешно развернуто":

Проверка работоспособности

Снова воспользуемся Postman для отправки запросов.

Для проверки сохранности данных после перезагрузки, перезапустим сервис:

После завершения перезапуска отправим запрос Постманом для проверки наличия TODO заметок. Если они есть, значит мы все сделали правильно.

Также нам не пришлось задумываться об SSL-сертификатах. Они были автоматически выписаны для нашего приложения при первом развертывании.

Поздравляем, вы успешно создали свое первое приложение на Amvera!

В чем преимущества такого подхода

  1. Экономия времени на первом развертывании. При ручном развертывании вам нужно было разобраться в нескольких технологиях (Linux, nginx, systemd). В случае с Amvera, это всего один Dockerfile.

  2. При последующем обновлении все еще проще. Потребуется только запушить обновления в Git через командную строку или любимое GUI приложение, и все!

  3. Вы автоматизируете процесс доставки и развертывания кода ваших сервисов. Это совсем другой пользовательский опыт, где вам не нужно отвлекаться на посторонние от разработки вещи.

  4. Легче масштабироваться. У вас увеличилась нагрузка? Просто создайте копию контейнера, а балансировщик сам распределит нагрузку.

P/S/ Будем рады всем желающим поучаствовать в бета-тесте нашей облачной платформы Amvera, который мы проводим с октября по декабрь 2022 г. В сервисе вы сможете опробовать описанный в статье подход. Это бесплатно для участников. Для участия нужно просто зарегистрироваться, после чего на счет поступит 1 т.р. Если их не хватит – напишите нам, и мы руками добавим столько вычислительного ресурса, сколько требуется для вашего проекта. Будем рады, если потом дадите обратную связь и поможете нам сделать действительно полезный и удобный сервис!

Комментарии (3)


  1. conopus
    21.11.2022 21:22
    +4

    Я понимаю, что это вы так свои облачные сервисы рекламируете, но развернуть Flask pet project на бесплатном тарифе pythonanyware.com мне показалось проще, чем дочитать вашу инструкцию. Включая настройку GitHub Actions.


    1. kirillkosolapov
      21.11.2022 22:43
      +2

      Ну никто же не говорит об универсальности предлагаемого подхода. Но развертывать обновления просто делая push в мастер - это реально не сложно. А так можно и Heroku использовать, правда они бесплатные тарифы убирают.


    1. Data4
      21.11.2022 23:08

      Но если будет проект чуть сложнее, чем что-то очень простое, то проще один раз с докером разобраться и потом пушить в мастер.