Привет, Хабр! Недавно я писал статью о запуске Telegram-бота на aiogram 3.x с использованием веб-хуков и FastAPI. Однако, в той статье я упустил такие важные моменты, как создание FastAPI приложения, настройка NGINX, настройка VPS сервера и другие детали, которые могут затруднить работу с веб-хуками для новичков.

В этом руководстве я постараюсь восполнить пробелы и подробно рассказать обо всех аспектах настройки. Это будет полезно как тем, кто планирует создавать Telegram-ботов на веб-хуках с использованием FastAPI, так и тем, кто просто хочет научиться настраивать VPS сервер, NGINX и базовое FastAPI приложение.

В этой и следующих публикациях я постараюсь максимально доступно объяснить все шаги. Сегодня мы рассмотрим следующие темы:

  • Покупка VPS сервера

  • Покупка доменного имени с привязкой к серверу

  • Создание простого FastApi приложения, которое будет запускать index.html файл с простыми стилями на главной странице сайта.

Покупка VPS сервера

Для наших целей подойдет любой VPS сервер с установленным Linux (в данном примере мы будем использовать Ubuntu). Предположим, что вы приобрели VPS сервер с доступом к нему через SSH. Данные подключения могут выглядеть примерно так (естественно, данные вымышленные):

IPv4-адрес сервера: 000.0.000.000
Пользователь: root
Пароль: ZZujF38P$LcpWWZh

Далее:

  1. Запускаем командную строку

  2. Вводим команду: ssh root@000.0.000.000

  3. Подтверждаем сохранение SSH ключа командой yes

  4. Вводим пароль (при вставке пароль не отображается, что является нормальной защитной мерой, затем нажимаем ENTER)

После успешного входа, вас поприветствует Ubuntu, что будет свидетельствовать о том, что сервер работает и доступ к нему есть.

Покупка доменного имени с привязкой к айпи

Иногда вместе с сервером предоставляется технический домен (например, vm24322447.235ssd.had.wf). Если этого домена достаточно для ваших задач, шаг с покупкой и привязкой доменного имени можно пропустить. В противном случае:

  1. Проверяем, свободен ли домен.

  2. Покупаем домен (только домен, без хостинга).

  3. Настраиваем A-запись, которая связывает домен с IP адресом сервера (пример настройки A-записи приведен ниже на скрине).

4. В течение суток доменное имя привяжется к вашему сайту.

Для проверки, к какому IP-адресу привязан домен, можно использовать команду nslookup. Она доступна на большинстве операционных систем, включая Windows, macOS и Linux. Вот как это сделать:

  1. Откройте командную строку (терминал).

  2. Введите команду nslookup и имя домена, например:

    nslookup example.com

    Замените example.com на ваше доменное имя.

  3. Нажмите ENTER.

Команда nslookup выполнит запрос к DNS-серверу и выведет информацию о IP-адресе, к которому привязан домен.

Установка FastAPI приложения

Теперь, когда у нас есть VPS сервер и привязанный к нему домен, можем приступить к созданию FastAPI приложения. Для удобства на сервере воспользуемся утилитой screen.

Использование утилиты screen

screen позволяет создавать виртуальные окна, которые можно свернуть. Создаем такое окно, запускаем виртуальное окружение, запускаем скрипт, свертываем окно и продолжаем работать.

Подробные инструкции по screen вы найдете во второй части, пока просто запомним что есть такая утилита.

Создание структуры проекта FastApi

Наконец-то мы дошли и до этого этапа. Начнем писать код. Для начала давайте подготовим простую структуру для проекта (все буду намеренно упрощать, так что тапками в меня бросать не нужно).

── app.py
├── templates
│   └── index.html
└── static
    └── style.css

• Папка templates с файлом index.html (наш простой HTML файл)

• Папка static с файлом style.css (наши простые стили)

• Файл app.py (файл с кодом FastAPI приложения)

Пример index.html файла:

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Главная страница fast api</title>
    <link rel="stylesheet" href="./static/style.css">
</head>
<body>
    <header>
        <nav>
            <ul>
                <li><a href="#">Главная</a></li>
                <li><a href="#">О нас</a></li>
                <li><a href="#">Контакты</a></li>
            </ul>
        </nav>
    </header>
    <main>
        <div class="container">
            <h1>Добро пожаловать на главную страницу мега-сайта!</h1>
            <p>Какой-то абзац</p>
            <section class="features">
                <h2>Заголовое 2:</h2>
                <ul>
                    <li>Пункт 1</li>
                    <li>Пункт 2</li>
                    <li>Пункт 3</li>
                    <li>Пункт 4</li>
                </ul>
            </section>
            <section class="cta">
                <a href="#" class="button">Зарегистрироваться</a>
            </section>
        </div>
    </main>
    <footer>
        <p>&copy; 2024 Алексей Яковенко. Все права защищены.</p>
    </footer>
</body>
</html>

Как вы видите код максимально простой и нас разве что может заинтересовать строка с импортом файла стилей:

<link rel="stylesheet" href="./static/style.css">

Стили могут выглядеть так:

body {
    font-family: Arial, sans-serif;
    background-color: #f0f0f0;
    margin: 0;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

header {
    background-color: #4CAF50;
    padding: 1em 0;
    color: white;
    text-align: center;
}

nav ul {
    list-style: none;
    padding: 0;
}

nav ul li {
    display: inline;
    margin: 0 15px;
}

nav ul li a {
    color: white;
    text-decoration: none;
    font-weight: bold;
}

.container {
    flex: 1;
    text-align: center;
    background-color: #fff;
    padding: 20px;
    margin: 20px auto;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    max-width: 800px;
}

h1 {
    color: #333;
}

.features, .cta {
    text-align: left;
    margin-top: 20px;
}

.features h2, .cta h2 {
    color: #4CAF50;
}

.features ul {
    list-style: none;
    padding: 0;
}

.features ul li {
    background: url('data:image/svg+xml;utf8,<svg fill="%234CAF50" height="20" width="20" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>') no-repeat left center;
    padding-left: 25px;
    margin-bottom: 10px;
}

.button {
    display: inline-block;
    padding: 10px 20px;
    color: white;
    background-color: #4CAF50;
    border-radius: 5px;
    text-decoration: none;
    font-weight: bold;
    transition: background-color 0.3s ease;
}

.button:hover {
    background-color: #45a049;
}

footer {
    background-color: #4CAF50;
    color: white;
    text-align: center;
    padding: 1em 0;
    margin-top: auto;
}

Теперь приступим к коду FastAPI приложения. Для начала установим необходимые модули:

pip install jinja2 fastapi uvicorn
  • jinja2 — позволяет использовать язык шаблонов Jinja2 для динамической генерации HTML-страниц.

  • fastapi — основной фреймворк для создания нашего веб-приложения.

  • uvicorn — ASGI сервер для запуска FastAPI приложения.

Импортируем необходимые модули:

from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.requests import Request
import uvicorn

Настраиваем приложение FastAPI:

С помощью этих строк кода вы:

  1. Создаёте приложение FastAPI.

  2. Настраиваете обработку статических файлов.

  3. Настраиваете использование HTML-шаблонов для динамического создания веб-страниц.

Подробнее этот пример рассматривал в статье про телеграм бота с вебкухами.

Настраиваем наш первичный эндпоинт, который будет отвечать за бработку GET-запроса к главной странице сайта:

@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

Обратите внимание прописал как асинхронную функцию. Возможность асинхронности одна из моих любимых фишек в FastApi. Может когда-то подробнее поговорим и об этом аспекте.

Ну и останется только запустить

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=5000)

Хост "0.0.0.0" указывается чтоб он слушал все что будет подключаться к fast api. Отлично, стартовые настойки выполнены!

Полный код:

from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.requests import Request
import uvicorn


app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")


@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=5000)

Установка NGINX, запуск FastAPI и привязка HTTPS

Эти задачи будут рассмотрены во второй части статьи, где я подробно расскажу про:

  • Подъем NGINX

  • Запуск FastAPI приложения на сервере (вместе с утилиткой screen)

  • Привязку HTTPS протокола (бесплатно и просто)

  • Работа с утилитой SCREEN

Следите за обновлениями и до встречи во второй части!

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


  1. Groosha
    06.06.2024 10:39
    +2

    Не надо screen, пожалуйста. Возьмите хотя бы systemd, он поддерживает автозапуск (например, когда сервер ВНЕЗАПНО перезагрузился глубокой ночью) и автоматический перезапуск в случае аварийной остановки. Screen хорошо подходит, когда надо небольшой скриптик в фоне запустить, а потом через какое-то время посмотреть, как оно там выполняется. Но в данном случае лучше выбрать что-то получше.


    1. yakvenalex Автор
      06.06.2024 10:39

      Так упор на простоту) В своих проектах я использую systemctl, но, скажу честно, попервой сам юзал SCREEN, чем не горжусь) Как по мне для тех кто знакомится только с темой VPS - SCREEN самое то, но замечание принимаю)


      1. Groosha
        06.06.2024 10:39

        Я тоже использовал screen в начале своего пути. Отхлебнул кучу проблем, а systemd не сильно сложнее.


        1. yakvenalex Автор
          06.06.2024 10:39

          Ну SCREEN в пользовании не отличается особо от того же запусков скриптов на своем компе. Допустим тот же print, Все же его на старте используют, а в SYSTEMCTL уже не поиспользуешь это - только логирование, а это уже дополнительные знания какие-никакие и дополнительный опыт нужен. Так что вторую часть точно вокруг SCREEN закрою, но туториал по SYSTEMCTL, если кому нужен будет, тоже набросаю)


    1. Ryav
      06.06.2024 10:39

      Поддерживаю, через службу самое оно. Ещё варианты: запускать сессию tmux при запуске системы с запуском сервиса (практически аналог screen), собрать контейнер и так же стартовать его при запуске.


  1. andrezh
    06.06.2024 10:39
    +4

    Хост "0.0.0.0" указывается чтоб он слушал все что будет подключаться к fast api.

    Зачем FastAPI высовывать наружу, если его туда будет nginx проксировать?


    1. yakvenalex Автор
      06.06.2024 10:39

      Упор на простоту и не то чтоб информация о боевом проекте) Смысл в том, чтоб новички, которые впервые взаимодействуют с VPS и FastAPI получили первый положительный опыт. Потратили пол часа и подняли свое первое приложение на своем VPS, а все остальное это с опытом приходит)


      1. andrezh
        06.06.2024 10:39
        +2

        Указать 127.0.0.1 вместо 0.0.0.0. Что сложного?


    1. yakvenalex Автор
      06.06.2024 10:39

      Ну и в целом же есть преимущества. Например это локальное тестирование. Не нужно замарачиваться на старте. Оно одинаково будет запускаться что на локальной машине что на сервере. Далее.Настройки становятся более гибкими. Иногда возникают ситуации, когда нужно предоставить доступ к FastAPI напрямую, минуя NGINX. Например, для доступа к административным функциям или для удобства тестирования определенных эндпоинтов. Ну и на более глубоких материях - масштабирование по горизонтали)


      1. Veritaris
        06.06.2024 10:39
        +2

        Напрямую...куда? На 127.0.0.1/<эндпоинт>? Чем это будет отличаться от

        location / {
            proxy_pass http://127.0.0.1;
        }
        

        ?

        Ну и на более глубоких материях - масштабирование по горизонтали)

        А разве горизонтальное масштабирование как раз не достигается с помощью того же round-robing балансировки? Прямой же доступ как раз усложнит такое. С одной стороны через nginx прописать RR и он сам будет крутить адреса в локальной сети, с другой - на клиенте менять порт приложения, на который нужно сходить. Что-то у вас не сходится


        1. stvoid
          06.06.2024 10:39
          +3

          Что вы тут доказываете, у вас в подписи разве есть это?

          Опытный python разработчик с многолетним стажем
          


          1. yakvenalex Автор
            06.06.2024 10:39

            Что такое "опытный" по-вашему? Сколько нужно иметь проектов, лет коммерческой практики чтоб быть "опытным"?)


            1. stvoid
              06.06.2024 10:39

              Я вас защищаю, как видите, вы скажите мне.

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


  1. stvoid
    06.06.2024 10:39
    +8

    Очередной гайд копипаста от прошедшего курсы курсы месяц назад. Я не знаю как это охарактеризовать. Всё что выше и ниже напихают автору - всё по дело.
    Обычно я пропускаю такие статьи и просто плююсь что потратил время, но как же уже накипело.
    Что вы привнесли этим? Тут есть что-то новое? Это даже хуже - это что-то 10 летней давности.

    Но даже такую копипасту можно было бы разбавить, добавив пример systemd юнита, а может кто-то альпайн хочет на VPS - опишите как там демона написать, заодно сами разберетесь.

    Еще больше я в шоке что не вижу ни рекламы, ни ссылок на очередной телеграм канал - нафига это вообще существует?


    1. yakvenalex Автор
      06.06.2024 10:39

      Вы я так понимаю супер опытный python разработчик. Представим. А теперь представим себя на этапе когда у вас вообще нет опыта, вы впервые заходите на VPS сервер, впервые запускаете FastApi, а вам начинают с проходняка рассказывать про systemctl и прочее. Где ваши личные публикации, ваши работы? Вы не зная ничего читаете пост человека который поделился своим опытом и начинаете его обмазывать в непонятную субстанцию) Зачем существуют такие комментарии?


      1. stvoid
        06.06.2024 10:39
        +4

        Есть гугл с намного более подробными и верными руководствами, чем ваше. Помимо этого на хабре. О! Я вам помогу, а заодно тем кто наткенется на эту копипасту (вы тут на какие-то мои отсутствующие статьи наезжаете, а где у вас тут оригинальность? Как докажете?).

        Просто выборка с хабра:
        Краткая история о том, как развернуть веб-сервер Flask в docker контейнере
        Подготовка сервера для публикации web-app на Python
        Фантастически быстрый деплой веб-приложения
        Несколько советов по организации Python-приложения на сервере
        Установка Django-проекта на VPS (centOS 7) [Для новичков]

        site:habr.com python сервер как развернуть

        Вы даже про nginx не описали, что тут вообще читать? Зачем разбивка на части? Может быть чтобы просто нафармить кармы на этой копипасте растянув казалось бы конечную мысль на несколько маленьких статей? Да нет, не думаю что вы на столько меркантильны


        1. yakvenalex Автор
          06.06.2024 10:39

          Ну это вообще интересно) Ну давайте вы мне напишите как через SSH заходить на сервак) Все способы, а я вам в ответ статьи поскидываю) Далее. Давайте вы расскажете как с кода запустить фаст апи через приложение и туда же вам статьи скину) Естественно в интернете много публикаций об этом, так что это знчит. Повторюсь. Прошлая статья в контексте которой идет эта) Если я копипастом занимаюсь - покажите примеры в рунете. Именно 3.7) На дворе 2024-й год каждый уже о чем то сказал и это нормально. Есть вопрос личного опыта не более того. У меня за спиной более 100 проектов разных с разной нагруженностью, а вы тут рассказываете о курсах которые месяц назад я прошел. В этом прикол, в токсичности. Благодаря таким как вы люди просто не хотят делиться опытом или прочтут коммент ваш и подумают "Ну это лажа", а после не разобравшись с вопросом забъют или на тему или на программирование. Я очень много времени потратил на старте на разные вопросы, потому что на старте трудно все это. Сервера, VPS, фаст апи, постгресы и прочее прочее прочее. В общем считаю так, если вам что=то не понравилось - держите свое мнение при себе или делитесь конструктивной критикой. Благодарю за внимание)


          1. stvoid
            06.06.2024 10:39
            +4

            Считаю что поделился более чем конструктивной критикой пусть и долей токсичности. Выше комментарии вам так же по делу напихали.
            Это смешно мерятся кол-вом проектов, а тем более советовать что-то писать. Вы из тех кто "сначала сам сделай, а потом критикуй"? Тогда понятно почему у вас так пригорает. Я, да и думаю многие, не пишут на хабр просто потому, что это не принесет чего-то нового. Конечно, есть статьи где люди описывают свой опыт, как применяли что-то и там как бы объективно ничего нового может не быть, но хотя бы интересно читать как люди приходят к тем или иным решениям, оценивают разные инструменты и т.п.

            А у вас, повторю, просто вредные советы... в 2024 году


        1. yakvenalex Автор
          06.06.2024 10:39

          А, ну и по второй части, да текста и так дофига. На минуточку, это вообще всего вторая публикация моя тут) Я не особо ещё разобрался)


    1. yakvenalex Автор
      06.06.2024 10:39

      Ну и давайте начнем с того что этот пост идет в контексте предыдущего. Там я сказал "представим что мы это умеем". Тут я написал самое простое решение из возможных. Хук ничего сложнее чем изложено в этой публикации и в следующей не требует (кстати за копипасту, покажите мне пример копипасты прошлой статьи или курс на котором рассказывают как на aiogram3.x настроить вебхук в связке с fastapi


      1. stvoid
        06.06.2024 10:39

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


        1. yakvenalex Автор
          06.06.2024 10:39

          Вопрос в том как я пишу. Без вопросов, практики могут быть устаревшими, но как быть если такой у меня стиль. Выработанный годами. Там выше писали за 0.0.0.0 я прокомментировал как и тему со SCREEN. В остальном то что не так?) Любой новичек прочитав мою статью сможет повторить код и запустить его. Для меня это важно. Чтоб это было просто и легко для каждого, так как я десятки часов в свое время потратил на поиск этой инфы. Вот и все. И дальше я планирую делиться своим опытом. Вот таким какой он есть, даже если он капец как устарел)


  1. yakvenalex Автор
    06.06.2024 10:39

    Напишу отдельный комментарий общий. По возможности дайте реакцию. Суть такая. У меня есть некий стиль написания кода (возможно устаревший, некорректный, плохо наученный и прочее, а возможно неповторимый, обалденный и вообще все мечтали о нем услышать, я просто не в курсе дела). Я планирую ним делиться тут. Вот так как есть, так как я пишу и так как можно писать. Неплохо ориентируюсь в темах парсеров, ботов в телеграмм, автоматизации, серверов, фаст апи, базы данных ну и прочее. Вопрос такой. Есть ли смысл делать публикации с учетом того что мой код может оказаться плохим? Смысл делиться опытом который может оказаться плохим? Вопрос действительно важный. Спасибо за ответы)


    1. stvoid
      06.06.2024 10:39
      +1

      Проблема в вашей реакции на комментарии. Вы публикуете в открытом источнике, с открытыми комментариями, зачем то спорите не по делу.
      Без всякого негатива - хорошо что вы осознаете что у ваши практики устарели, но может немного для себя стоит тогда пересмотреть что сейчас актуально? Я много лет писал только бэки, а когда пришлось заниматься фулстаком, то с удивлением обнаружил для себя, что сейчас все уже давно пишут на ванильном JS без JQuery, да и вообще существуют более удобные вещи типа vue для ускорения написания фронта со всеми современными фичами.

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

      Если ваша цель не просто фарм кармы для чего-то и добавление в резюме что "вот, а я на хабре пишук", а вы действительно горите желанием поделится опытом, то просто немного освежите знания, даже не навыки.

      Как вы думаете, почему я вообще эту статью открыл, хотя она очевидно для новичков? Я ожидал увидеть для себя что-то новое, может какая-то хитрая настройка nginx появилась, может какой-то ASGI новый появился или с хитрыми настройками. Да даже если бы не увидел нового, но это просто тут было бы - это уже ок.

      Это просто ИМХО, в открытой статье с открытыми комментариями. Единственное зачем я пишу что-то критическое в комментариях - это получить от авторов фидбэк в виде следующих хороших статей, надеюсь у вас они так же будут. Прошлая статья ведь действительно полезная у вас.


    1. andrezh
      06.06.2024 10:39
      +3

      Была на Хабре некая программистка на PHP и React, у которой тоже был свой стиль и видение кода. Победительница всесоюзных олимпиад и т.п. Писала гайды для новичков и статьи про чистый код. Когда ей объяснили, что парсить csv через explode не совсем правильно, axios не нужен, не использовать хуки и ts в 2022 как-то странно, да и в принципе весь ее код, мягко говоря, не тянет даже на позицию Junior (она позиционировала себя как Senior), у нее не выдержала психика и она начала кричать "спервадобейся!" и "покажимнесвоистатьи!". Закончилось все read-only.

      Тут вопрос больше к вам. Готовы ли вы к критике? Ведь может оказаться, что Опытный python разработчик с многолетним стажем - это лишь ваше восприятие себя, а вам тут будут объяснять, что на самом деле это не так.


      1. yakvenalex Автор
        06.06.2024 10:39

        Понимаете в чем дело. Я же не отрицаю конструктивной критики, но тут ситуация когда ты потратил время, описал свой опыт (поделился), а в ответ тебе предъява что это копипаст и что ты курсы месяц назад прошел.


    1. pvzh
      06.06.2024 10:39
      +1

      Пожалуйста, не надо делиться таким. Вам явно не хватает чувства меры. Если цель это Телеграм-бот и вы взяли aiogram, который уже использует aiohttp, то зачем приплетать довольно жирный FastAPI и к нему вдобавок Jinja? Преимущество FastAPI перед aiohttp в валидации и автодокументации, у вас это не применяется, так зачем? Зачем раздаёте статические файлы через приложение, когда изначально заявлен Nginx? Про print для логирования и screen для деплоя уже намекнули, не надо это новичкам.


      1. yakvenalex Автор
        06.06.2024 10:39

        Есть такая штука как WEB-приложения в telegram. Когда нужно прикрутить его к боту намного проще когда все крутится на одном FastApi приложении. Так же, достаточно часто бывает когда есть 1 сервер и к нему прикручено 1 доменное имя, а клиент хочет и сайт себе и бота. Тогда так же удобнее FastApi использовать. Ну и как по мне проще FastApi настраивать в контексте вебхуков, даже если оно тяжелее идет. Далее print и Screen для деплоя. Я думаю что мне виднее как писать, правда?) В следующий раз распишу про systemctl если нужно будет это кому-то)