Skip to content

Установка на Oracle Cloud

Так echo-words и задуман работать: на виртуалке Oracle Cloud из тарифа Always Free. Она стоит $0 в месяц и работает всегда, а телефон и компьютеры достают до неё через вашу приватную сеть Tailscale, и другого входа нет. Руками вы делаете две вещи: создаёте виртуалку и подключаете её к сети. Дальше inv setup-app готовит машину, а inv deploy ставит приложение. Каждое следующее обновление — снова inv deploy.

Что понадобится

  • учётная запись Oracle Cloud Free Tier и резервный код входа к ней, сгенерированный и сохранённый не в телефоне: меню профиля → User settings → Security → Bypass codes → Generate. Если телефон, с которым вы входите, потерян, код вернёт доступ без обращения в поддержку Oracle.
  • учётная запись Tailscale (тариф Personal бесплатный) и приложение Tailscale на телефоне
  • бесплатный ключ Gemini и логин AnkiWeb, см. Ключи
  • на вашем компьютере: git, ssh, uv и копия этого репозитория, потому что команды развёртывания запускаются из неё:
git clone https://github.com/andgineer/echo-words.git
cd echo-words
uv sync

Создайте виртуалку

В консоли Oracle Cloud создайте вычислительный инстанс формы VM.Standard.E2.1.Micro из образа Canonical Ubuntu 22.04 Minimal со своим публичным ssh-ключом. Запишите его публичный IP-адрес: развёртывание заходит как ubuntu@<этот адрес>.

Форма — x86_64 с 1 ГБ оперативной памяти, и файл подкачки на 2 ГБ — жёсткое требование, его создаёт установка. Юнит systemd дополнительно задаёт MemoryHigh=600M и MemoryMax=700M, так что разросшийся бэкенд не уронит виртуалку. Форма Arm A1.Flex, когда в регионе есть свободные мощности, снимает эти ограничения, но на неё никто не рассчитывает.

Развёртывание — это задачи invoke, запускаемые по ssh с вашей машины.

Сначала подключите узел к сети Tailscale

Tailscale — единственная входная дверь приложения. Подключите виртуалку к сети до запуска установки, выполнив на ней по ssh:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Установка затем настраивает tailscale serve --bg 8080, публикуя приложение в корне HTTPS-адреса узла, тогда как uvicorn остаётся привязанным к 127.0.0.1:8080. Ни страницы входа, ни выхода в интернет нет. Имя узла в сети остаётся прежним.

Секреты

mkdir -p .deploy
cp .deploy.example/.env .deploy/.env
chmod 600 .deploy/.env

.deploy/ в gitignore. Задайте ECHOWORDS_DEPLOY_HOST — ssh-адрес виртуалки, её публичный адрес вида ubuntu@203.0.113.10: администрирование идёт по публичному ssh, и только само приложение доступно исключительно внутри сети Tailscale. Переменная окружения с тем же именем перекрывает значение для разовой цели. Развёртывание определяет хост до того, как что-либо собирать, поэтому пропущенное или неотредактированное значение падает сразу, а не после сборки фронтенда.

Ключи и логин AnkiWeb заполняются так, как описано на странице Ключи. Они никогда не попадают в репозиторий, в фикстуру теста или в строку лога.

Установка и развёртывание

uv run inv setup-app --with-host-prep   # один раз, идемпотентно
uv run inv deploy --ref=main
uv run inv status
uv run inv logs

После этого приложение доступно по адресу https://<узел>.<сеть>.ts.net/, а дальше ведёт Установка на телефон. Чтобы обновиться потом, выполните git pull и снова uv run inv deploy --ref=main.

setup-app устанавливает Node 22, uv и Tailscale, клонирует репозиторий, устанавливает и включает сервис, создаёт и активирует /swapfile на 2 ГБ и проверяет его формат, активный объём и единственную каноническую запись в /etc/fstab. Он создаёт отсутствующий файл подкачки и умеет увеличивать слишком маленький файл, у которого уже есть сигнатура swap, но останавливается, не перезаписывая существующий файл другого типа, символьную ссылку, специальный путь или неоднозначную конфигурацию fstab. Ошибки создания, активации и проверки подкачки прерывают установку.

Тот же проход усиливает sshd и включает явную тюрьму fail2ban для sshd (3 неудачи за 10 минут, нарастающие баны по суткам с потолком в 30 дней). Тюрьма работает через бэкенд systemd и исключает диапазон Tailscale 100.64.0.0/10, поэтому администрирование через сеть Tailscale не забанит само себя; публичный ssh — путь развёртывания и всё прочее, что приходит на порт 22 из интернета, — тюрьме подчиняется. Установка также отключает rpcbind и ограничивает системный журнал — по размеру (200 МБ) и по возрасту (три месяца). В образе нет logrotate, поэтому установка ставит его и включает его таймер — на машине без cron запускать конфигурации ротации больше нечему, — и файлы, которые эти конфигурации называют, в первую очередь запись неудачных логинов, перестают расти всё время жизни хоста. Ротация месячная, поэтому файл, который не ротировался ни разу, будет впервые подрезан через календарный месяц после прохода. Установка также ужесточает периодическую автоочистку apt и очищает кэш пакетов. Межсетевой экран хоста остаётся как был: правила для loopback и финальный REJECT переустанавливаются только при их отсутствии, а отклонённое изменение пропускается, не роняя проход. Существующий checkout и работающий сервис установка намеренно не трогает, а на чистом хосте сервис не запускает.

deploy — единственный путь активации кода и PWA. Он привязывает ref к конкретному коммиту, так что ни локальная ветка, ни незакоммиченные правки в поставку не попадают: ref достаточно иметь локально, и он должен быть отправлен в origin, из которого клонируется виртуалка. Задача требует чистого локального checkout запрошенного ref, переключает виртуалку на тот же коммит, выполняет uv sync --no-dev, собирает _static/ на виртуалке командой uv run --no-dev inv build-static, синхронизирует секреты, запускает или перезапускает юнит и завершается ошибкой, если /api/health не ответит за 30 секунд.

Удалённый checkout проверяется на изменённые отслеживаемые и неожиданные неотслеживаемые файлы — и до переключения, и до синхронизации; развёртывание останавливается и печатает их, ничего не удаляя. Пути времени выполнения из gitignore — .deploy/, data/, .venv/, _static/ — остаются на месте.

Warning

Никогда не редактируйте файлы на сервере. Удалённый checkout — это цель развёртывания, а не рабочая копия: исправляйте в репозитории и разворачивайте снова. Изменения хоста место в задачах setup-app / host-prep, чтобы виртуалка оставалась воспроизводимой из репозитория.

Проверка результата

Развёртывание считается завершённым, только когда пройдёт опрос работоспособности. После этого проверьте inv status и inv logs.

inv status показывает текущий VmRSS основного процесса вместе с пиковым за время жизни VmHWM, а также MemoryCurrent, MemoryHigh и MemoryMax cgroup сервиса. Он также печатает корневую файловую систему, размер каталога данных и объём журнала, так что рост диска читается из той же команды. Отдельно он показывает memory.peak cgroup только на ядрах, которые экспортируют этот файл; ядро 5.15 из Ubuntu 22.04 может сообщить, что он не поддерживается. Команда падает, если сервиса или юнита нет, вместо того чтобы случайно прочитать корневую cgroup. Цикл ожидания готовности Tailscale в сервисе делает запуск после перезагрузки предсказуемым.

Чтобы собрать PWA локально без развёртывания, используйте inv build-static.

Релизы

inv ver-bug, inv ver-feature и inv ver-release поднимают версию в src/echo_words/__about__.py, коммитят её и отправляют коммит вместе с тегом vX.Y.Z. CI запускается на этот push и при успехе публикует пакет на PyPI и создаёт релиз на GitHub.