Реактивные рельсы 🛫

Этот пост с гордостью спонсирован компанией Code & Co.

Отказ от ответственности: я никак не связан с fly.io, это просто личный рассказ о моем опыте работы с ним.

Fly.io 101

Несмотря на то, что fly.io в наши дни является практически «Phoenix First» (не зря же они наняли Криса МакКорда!), это все еще очень хороший способ развернуть Reactive Rails приложение, как показал здесь Мэтт Йоркли. Давайте пройдемся по краткому списку предварительной информации, прежде чем мы начнем.

Docker или Buildpacks?

Многие Rails-разработчики, рассматривающие Fly в качестве платформы как сервиса, привыкли к способу развертывания приложений на Heroku с помощью git push и Cloud Native Buildpacks, которые управляют процессом развертывания автоматически. Хотя Fly по умолчанию использует конструктор Dockerfile — и действительно, мастер установки Rails сгенерирует его для вас, как мы увидим, — вы также можете использовать конструктор buildpack, указав builder и buildpacks в конфигурации fly.toml:

[build]
  builder = "heroku/buildpacks:18"
  buildpacks = ["...", "..."]

Вход в полноэкранный режим Выход из полноэкранного режима

Также обратитесь к этой статье блога, чтобы выбрать правильный билдер (на момент написания статьи это heroku/buildpacks:18).

Бесплатные возможности

Хотя Fly имеет довольно щедрый набор бесплатных льгот, которые позволят вам начать и поддерживать достаточно большой побочный проект бесплатно, я хотел бы особо отметить их бесплатное предложение postgres, которое в значительной степени основывается на разрешенных 3 ГБ объемного хранилища. Вот несколько примеров того, как его настроить. Имейте в виду, что бесплатная вычислительная мощность суммируется для всех приложений — если вы превысите ее, то, возможно, не сможете сделать бесплатным весь свой Rails-проект.

Начало работы

  1. Первое, что вам нужно сделать, это установить flyctl CLI (см. здесь).
  2. Далее, что вполне очевидно, вам нужно создать учетную запись Fly. Запустите команду flyctl auth signup, которая в свою очередь откроет для вас браузер. Обратите внимание, что вам придется заранее предоставить информацию о кредитной карте, потому что «вот что произойдет, если вы предоставите людям полный доступ к хостинговой платформе по принципу freemium: много-много бесплатных виртуальных машин для добычи криптовалют». Справедливо с моей точки зрения.
  3. Запустите flyctl auth login для аутентификации во Fly.

Теперь вы готовы к развертыванию своего первого приложения.

Мастер настройки

В оставшейся части этого руководства предположим, что у вас есть простое приложение Rails под названием my-app, а рабочий каталог вашего терминала указывает на корневую папку этого приложения.

Я начну с краткого изложения официального руководства по началу работы с Rails, а затем перейду к более сложным темам.

Мастер настройки для приложений Rails на удивление полезен. Если вы запустите fly launch, он:

  1. Определит приложение Rails
  2. Попросит вас указать вашу организацию и основной регион
  3. Создаст файл fly.toml, который мы рассмотрим позже
  4. Спросит, хотите ли вы установить базу данных Postgresql прямо сейчас. Если вы это сделаете, это избавит вас от необходимости создавать и подключаться к ней вручную. Это очень полезно, потому что сразу же устанавливается DATABASE_URL через секрет приложения.
  5. Затем выполняется процесс подготовки приложения к развертыванию. Обратите внимание, что на этом этапе оно еще не развернуто!

Ниже приведен пример вывода этой команды:

$ fly launch
Creating app in /.....
Scanning source code
Detected a Rails app
? App Name (leave blank to use an auto-generated name): my-app

? Select organization: Julian Rubisch (personal)
? Select region: fra

Created app my-app in organization personal
Wrote config file fly.toml
? Would you like to setup a Postgresql database now? y

? Select configuration: [Use arrows to move, type to filter]
> Development - Single node, 1x shared CPU, 256MB RAM, 1GB disk
  Production - Highly available, 1x shared CPU, 256MB RAM, 10GB disk
  Production - Highly available, 1x Dedicated CPU, 2GB RAM, 50GB disk
  Production - Highly available, 2x Dedicated CPUs, 4GB RAM, 100GB disk

Creating postgres cluster my-app-db in organization personal
[...]

Postgres cluster my-app-db is now attached to my-app
The following secret was added to my-app:
  DATABASE_URL=postgres://xxxxxxx:xxxxxxx@top2.nearest.of.my-app-db.internal:5432/my-app
Postgres cluster my-app-db is now attached to my-app

Your Rails app is prepared for deployment. Production will be setup with these versions of core runtime packages:

Ruby 3.0.0
Bundler 2.2.5
NodeJS 14
Postgres cluster my-app-db is now attached to my-app
The following secret was added to my-app:
  DATABASE_URL=postgres://xxxxxxx:xxxxxxx@top2.nearest.of.my-app-db.internal:5432/my-app
Postgres cluster my-app-db is now attached to my-app

Your Rails app is prepared for deployment. Production will be setup with these versions of core runtime packages:

Ruby 3.0.0
Bundler 2.2.5
NodeJS 14

[...]

run 'fly deploy --remote-only' 

Войти в полноэкранный режим Выйти из полноэкранного режима

Конфигурационный файл fly.toml, созданный для вас CLI, выглядит следующим образом:

# fly.toml file generated for my-app on 2022-06-06T13:44:47+02:00

app = "my-app"

kill_signal = "SIGINT"
kill_timeout = 5
processes = []

[build]

  [build.args]
    BUNDLER_VERSION = "2.2.5"
    NODE_VERSION = "14"
    RUBY_VERSION = "3.0.0"

[deploy]
  release_command = "bundle exec rails db:migrate"

[env]
  PORT = "8080"
  SERVER_COMMAND = "bundle exec puma -C config/puma.rb"

[experimental]
  allowed_public_ports = []
  auto_rollback = true

[[services]]
  http_checks = []
  internal_port = 8080
  processes = ["app"]
  protocol = "tcp"
  script_checks = []

  [services.concurrency]
    hard_limit = 25
    soft_limit = 20
    type = "connections"

  [[services.ports]]
    force_https = true
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

  [[services.tcp_checks]]
    grace_period = "1s"
    interval = "15s"
    restart_limit = 0
    timeout = "2s"

[[statics]]
  guest_path = "/app/public"
  url_prefix = "/"

Вход в полноэкранный режим Выйти из полноэкранного режима

Обратите внимание, что он

  • получил BUNDLER_VERSION, NODE_VERSION и RUBY_VERSION из вашего приложения
  • установили SERVER_COMMAND в команду запуска puma
  • добавили release_command, которая будет запускать ваши миграции, как вы, вероятно, привыкли, если являетесь пользователем Heroku.

Кроме того, должен был быть создан Dockerfile для вашего ознакомления, или, более конкретно, для Fly, чтобы использовать его для развертывания. Я не буду воспроизводить здесь листинг этого файла (вы можете легко взглянуть на него сами, если выполните описанные выше шаги), но отмечу некоторые интересные детали:

  1. В нем используется образ Fullstaq Ruby с включенным jemalloc. Вы можете прочитать в его FAQ, почему это хорошо иметь включенным по умолчанию.
  2. Он использует volta.sh для установки node и yarn
  3. Затем он использует многоступенчатую сборку Docker для установки пакетов ОС, gems и пакетов node по отдельности.
  4. Наконец, он скопирует каталог вашего приложения и запустит rails assets:precompile для вас.

Небольшое отступление: На этом этапе я обычно люблю создавать каталог fly, в котором будут храниться все конфигурационные файлы для Fly (потому что их будет больше!):

$ mkdir fly
$ mv fly.toml fly

Вход в полноэкранный режим Выйти из полноэкранного режима

Развертывание

Теперь мы готовы развернуть наше приложение! Давайте сделаем небольшую паузу и импортируем RAILS_MASTER_KEY в секреты приложения Fly (предполагается, что вы используете Rails >= 6.1):

$ fly secrets set RAILS_MASTER_KEY=$(cat config/master.key) -c fly/fly.toml

Вход в полноэкранный режим Выход из полноэкранного режима

После этого развертывание сводится к выполнению следующих действий

$ fly deploy --remote-only -c fly/fly.toml

Войти в полноэкранный режим Выйти из полноэкранного режима

Примечание: опция --remote-only указывает Fly использовать удаленный сборщик (которые бесплатны), а не вашу локальную установку Docker — это особенно полезно, если вы, как и я, работаете на компьютере на базе Apple Silicon/ARM64, что может привести к проблемам.

Если эта команда выполнена без ошибок, вы должны иметь возможность просмотреть свое приложение на приборной панели Fly:

Вместе с сопутствующим, отдельным приложением базы данных:

Redis

Скорее всего, поскольку мы говорим о реактивных Rails-приложениях, вам понадобится хранилище ключей/значений типа Redis для ActionCable, Sidekiq, кэширования фрагментов и других привычных вещей.

Несмотря на то, что в блоге есть статья о том, как добиться глобального распределения Redis, написанная основателем fly Куртом Маки, я бы хотел, чтобы вы на секунду задержали дыхание. Есть еще один вариант, который подходит нам даже больше: KeyDB, который недавно был приобретен компанией Snap.

Из README проекта:

KeyDB — это высокопроизводительный форк Redis с упором на многопоточность, эффективность использования памяти и высокую пропускную способность.

Довольно прозорливая команда Fly собрала образец приложения KeyDB для нашего использования на https://github.com/fly-apps/keydb/. Один из теглайнов гласит:

KeyDB также поддерживает режим мультимастера, который уникально подходит для развертывания на Fly.

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

Руководство по работе с KeyDB

Процесс начинается с клонирования репозитория Github в ваше приложение. Я всегда делаю это в папке fly/keydb, как подмодуль git, примерно так:

$ mkdir fly/keydb-cache
$ git clone https://github.com/fly-apps/keydb.git fly/keydb-cache
$ cd fly/keydb-cache

Войти в полноэкранный режим Выйти из полноэкранного режима

Прежде чем продолжить, откройте fly/keydb-cache/fly.toml в вашем редакторе и переименуйте его, чтобы отразить название вашего основного приложения. Обратите внимание, что я также назвал его «cache», чтобы отличить его от будущих дополнительных экземпляров.

app = "my-app-keydb-cache"

[mount]
  destination = "/data"
  source = "keydb_server"
Войдите в полноэкранный режим Выход из полноэкранного режима
$ fly launch

An existing fly.toml file was found for app keydb-multimaster-example
? Would you like to copy its configuration to the new app? Yes
Creating app in /Users/jrubisch/.../my-app/fly/keydb-cache
Scanning source code
Detected a Dockerfile app
? App Name (leave blank to use an auto-generated name): my-app-keydb
? Select organization: Julian Rubisch (personal)
? Select region: fra (Frankfurt, Germany)
Created app my-app-keydb in organization personal
Wrote config file fly.toml
? Would you like to setup a Postgresql database now? No
? Would you like to deploy now? No
Your app is ready. Deploy with `flyctl deploy`

Войдите в полноэкранный режим Выход из полноэкранного режима

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

Стандартная конфигурация требует, чтобы мы установили KEYDB_PASSWORD; после этого мы создаем том под названием keydb_server (как было указано в вышеприведенном конфигурационном файле) в каждом регионе, где мы хотим развернуть и синхронизировать приложение KeyDB. Обратите внимание, что в этом примере я ограничил размер до 1 ГБ, так как этого достаточно для побочного проекта или конфигурации staging — возможно, вы захотите увеличить этот размер в соответствии с требованиями вашего приложения. Наконец, мы deploy.

$ fly secrets set KEYDB_PASSWORD=password

$ fly volumes create keydb_server --size 1 --region fra
$ fly volumes create keydb_server --size 1 --region ... # repeat for every region

$ fly deploy

Вход в полноэкранный режим Выход из полноэкранного режима

Вот важный момент: по умолчанию fly использует тома для управления размещением регионов, поэтому приложение будет запускаться для каждого региона, где вы развернули том. Позже вы захотите развернуть экземпляр вашего основного приложения в каждом регионе с экземпляром KeyDB, но об этом мы поговорим позже.

Расширенная настройка KeyDB

Если ваше приложение работает некоторое время, есть шанс, что ваш экземпляр KeyDB заполнится данными. К счастью для нас, KeyDB поддерживает стандартные параметры конфигурации Redis (полный список см. здесь). Из них наиболее важными являются maxmemory и maxmemory-policy, которые я добавил в стандартный конфиг (находится по адресу fly/keydb-cache/keydb.conf).

dir /data/ 
protected-mode no 
appendonly no

# snapshotting
save 900 1
save 300 10
save 60 10000

# replication
active-replica yes
multi-master yes
replica-serve-stale-data no

# add these:
maxmemory 100mb # scale at your own leisure
maxmemory-policy allkeys-lru # or try allkeys-lfu

Войти в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что LRU означает «Least Recently Used», а LFU означает «Least Frequently Used». Когда вы добавили свои параметры конфигурации, просто разверните систему снова. Если вы хотите взглянуть на пример приложения KeyDB-with-Rails, вот один из примеров с использованием AnyCable: https://github.com/superfly/anycable-rails.

Несколько экземпляров

В зависимости от архитектуры и размера вашего приложения, вы можете захотеть развернуть более одного экземпляра KeyDB (например, один для ActionCable, один для кэша Rails, один для kredis, возможно даже один для хранения сессий и т.д.) Для этого начните сначала и клонируйте новую версию исходного репозитория KeyDB, например, в fly/keydb-cable и т.д. Ниже мы поговорим о том, как соответствующим образом адаптировать ваши конфигурации.

Региональные конфигурации приложений

Наконец, УТП Fly.io — это глобально распределенные операции, и мы, конечно же, хотим извлечь из этого выгоду. К счастью, конфигурация виртуальных машин Fly позволяет нам легко сделать именно это.

Внутренняя частная сеть Fly использует региональные классификаторы, которые открыты для вашего приложения как переменная окружения FLY_REGION. Таким образом, мы можем обратиться к экземпляру(ам) KeyDB, относящемуся к региону, в котором развернуто наше Rails-приложение, следующим образом:

# ...
production:
  adapter: redis
  url: <%= "redis://#{ENV['FLY_REGION']}.#{ENV['REDIS_CABLE_HOST']}/1" %>
  password: <%= ENV.fetch("REDIS_CABLE_PASSWORD", "password") %>
  channel_prefix: my_app_production

Войти в полноэкранный режим Выйти из полноэкранного режима
  • Конфигурация кэша, в конфигурационном файле вашей среды (например, config/production.rb)
# ...

config.cache_store = :redis_cache_store, {
  url: "redis://#{ENV["FLY_REGION"]}.#{ENV["REDIS_CACHE_HOST"]}/1",
  password: ENV["REDIS_CACHE_PASSWORD"]
}

# ...

Вход в полноэкранный режим Выход из полноэкранного режима
  • Конфигурация Kredis, config/redis/shared.yml.
# ...

production: &production
  url: <%= "redis://#{ENV['FLY_REGION']}.#{ENV['REDIS_KREDIS_HOST']}/0" %>
  password: <%= ENV["REDIS_KREDIS_PASSWORD"] %>
  timeout: 1

Войти в полноэкранный режим Выход из полноэкранного режима

и т.д.

Обратите внимание, что я использовал разные переменные окружения HOST и PASSWORD для каждого экземпляра KeyDB. Прежде чем мы сможем снова развернуть наше основное приложение Rails, мы должны установить их как секреты Fly:

В корневом каталоге вашего приложения сделайте:

$ fly secrets set REDIS_CABLE_PASSWORD=password -c fly/fly.toml
$ fly secrets set REDIS_CABLE_HOST=my-app-keydb-cable.internal:6379 -c fly/fly.toml    

$ fly secrets set REDIS_CACHE_PASSWORD=password -c fly/fly.toml
$ fly secrets set REDIS_CACHE_HOST=my-app-keydb-cache.internal:6379 -c fly/fly.toml    

$ fly secrets set REDIS_KREDIS_PASSWORD=password -c fly/fly.toml
$ fly secrets set REDIS_KREDIS_HOST=my-app-keydb-kredis.internal:6379 -c fly/fly.toml    

Войдите в полноэкранный режим Выйти из полноэкранного режима

После этого мы снова готовы fly deploy -c fly/fly.toml.

Фоновые рабочие

Прежде чем мы начнем изучение того, как установить рабочие узлы для таких обработчиков заданий, как ActiveJob, Sidekiq и т.д., нам нужно рассмотреть предварительный вопрос:

Хотим ли мы запускать сервер приложений (т.е. puma) и рабочие процессы в одном приложении/инстансе или в разных?

Другими словами, мы предпочитаем подход Heroku (один «dyno» на рабочую нагрузку), или мы хотим использовать многопроцессную поддержку Fly? У каждого подхода есть свои плюсы и минусы, давайте быстро рассмотрим их:

Многопроцессное приложение Отдельные приложения
Установить Запуск Procfile с помощью, например, Overmind Развертывание отдельных приложений с отдельными fly-конфигами
Развертывание простой (довольно) сложный
Масштабирование необходимо масштабировать обе рабочие нагрузки вместе можно масштабировать обе рабочие нагрузки независимо друг от друга
Стоимость низкая для небольших проектов, выполняющих только одну (или несколько) ВМ, может стать дорогостоящей, если вам нужно масштабировать рабочие узлы высокая первоначальная стоимость (минимум 2 ВМ), может окупиться, если вам нужно много рабочих узлов.

Вам придется принимать решение самостоятельно, но более гибким, на мой взгляд, является запуск их как отдельных приложений Fly. Кроме того, многопроцессорность все еще является функцией предварительного просмотра, о чем подробно говорится в этой статье блога. На данный момент мы будем работать с мульти-приложением, поэтому давайте просто клонируем наш fly.toml в отдельный fly.worker.toml:

$ cp fly/fly.toml fly/fly.worker.toml
$ fly launch --name my-app-worker --no-deploy
Creating app in /Users/jrubisch/Documents/_CODE/saas/my-app
Scanning source code
Detected a Dockerfile app
Selected App Name: my-app-worker
? Select organization: Julian Rubisch (personal)
? Select region: fra (Frankfurt, Germany)
Created app my-app-worker in organization personal
Wrote config file fly.toml
? Would you like to setup a Postgresql database now? No
? Would you like to deploy now? No
Your app is ready. Deploy with `flyctl deploy`

Вход в полноэкранный режим Выход из полноэкранного режима

Будьте осторожны, чтобы удалить команду миграции release, чтобы избежать условий гонки. Также измените SERVER_COMMAND для вашего процессора заданий (Sidekiq в примере blow) соответствующим образом, и удалите все сервисы (так как рабочий узел не должен открывать ни одного порта для публичного доступа).

app = "my-app-worker"

kill_signal = "SIGINT"
kill_timeout = 5
processes = []

[build]

  [build.args]
    BUNDLER_VERSION = "2.2.5"
    NODE_VERSION = "14"
    RUBY_VERSION = "3.0.0"

# remove this 👇🏻
# [deploy]
# release_command = "bundle exec rails db:migrate"

[env]
  # change this 👇🏻
  SERVER_COMMAND = "bundle exec sidekiq -C config/sidekiq.yml"

[experimental]
  allowed_public_ports = []
  auto_rollback = true

# a worker node doesn't expose any services
# [[services]]
# deleted 🚮

Войдите в полноэкранный режим Выход из полноэкранного режима

Рабочий Redis

Выше я упоминал, что есть некоторые нюансы в том, как работать с Redis в контексте рабочих узлов. В то время как для побочного проекта вы можете вполне обойтись развертыванием другого (или того же самого) экземпляра KeyDB, для производственных сценариев вы захотите перестраховаться и использовать ванильный экземпляр Redis со всеми гарантиями согласованности, которые многомастерная KeyDB не предоставляет. Кроме того, есть вероятность, что вы не захотите отправлять данные для ваших работников по всему миру, поэтому один экземпляр Redis на регион, хранящий данные сериализации ActiveJob/Sidekiq, будет не только достаточно хорош, но и, вероятно, именно то, что мы ищем.

Для этого я обычно просто возвращаюсь к ссылке Redis on Fly, т.е. создайте приложение fly с помощью fly launch и настраивайте по ходу дела, или просто создайте отдельный файл конфигурации в fly/fly.redis.toml:

app = "my-app-worker-redis"

kill_signal = "SIGINT"
kill_timeout = 5
processes = []

[build]
  image = "flyio/redis:6.2.6"

[env]
  MAXMEMORY_POLICY = "noeviction"

[experimental]
  allowed_public_ports = []
  auto_rollback = true

[metrics]
  port = 9091
  path = "/metrics"

[[mounts]]
  destination = "/data"
  source = "redis_server"

Вход в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что MAXMEMORY_POLICY может быть установлена как переменная окружения, как указано в документации. В случае вашего бэкенда обработки заданий, вы хотите, чтобы это было noeviction, потому что вы определенно хотите, чтобы он громко отказал, прежде чем какие-либо задания будут тихо отброшены.

Затем мы снова проходим цикл fly launch, fly deploy:

$ fly launch --name my-app-worker-redis --no-deploy
$ fly secrets set REDIS_WORKER_PASSWORD=super-secret-password -c fly/fly.redis.toml
$ fly volumes create redis_server --size 1 --region fra

Вход в полноэкранный режим Выход из полноэкранного режима

Используя пример Sidekiq, вам нужно указать URL Redis и пароль таким же образом, как мы делали выше, как для клиента, так и для сервера:

if Rails.env.production?
  Sidekiq.configure_server do |config|
    config.redis = {
      url: "redis://#{ENV['FLY_REGION']}.#{ENV['REDIS_WORKER_HOST']}/1",
      password: ENV["REDIS_WORKER_PASSWORD"]
    }
  end

  Sidekiq.configure_client do |config|
    config.redis = {
      url: "redis://#{ENV['FLY_REGION']}.#{ENV['REDIS_WORKER_HOST']}/1",
      password: ENV["REDIS_WORKER_PASSWORD"]
    }
  end
end

Войти в полноэкранный режим Выйти из полноэкранного режима

Мы должны установить эти переменные окружения как в нашем приложении, так и в рабочих изображениях, чтобы обеспечить взаимодействие между ними:

$ fly secrets set REDIS_WORKER_PASSWORD=password -c fly/fly.toml
$ fly secrets set REDIS_WORKER_HOST=my-app-worker-redis.internal:6379 -c fly/fly.toml    

$ fly secrets set REDIS_WORKER_PASSWORD=password -c fly/fly.worker.toml
$ fly secrets set REDIS_WORKER_HOST=my-app-worker-redis.internal:6379 -c fly/fly.worker.toml    

Войти в полноэкранный режим Выйти из полноэкранного режима

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

$ fly secrets set RAILS_MASTER_KEY=$(cat config/master.key) -c fly/fly.worker.toml
$ fly deploy -c fly/fly.worker.toml --remote-only

Войти в полноэкранный режим Выход из полноэкранного режима

Не забудьте развернуть и Redis, и рабочие приложения во всех регионах, которые для вас актуальны!

Среды

Скорее всего, когда вы работаете над любым нетривиальным приложением, вам нужны (по крайней мере) отдельные среды staging и production. Здесь я должен признать, что аналогов CI-трубопроводам Heroku во Fly нет (и, насколько я понимаю их продукт, это также кажется совершенно нецелесообразным).

Обычно я делаю так: просто дублирую соответствующие конфигурационные файлы в директории fly. Основываясь на приведенном выше примере приложения, в итоге я получу следующее

$ ls -l fly
-rwxr-xr-x 1 entrypoint.sh
-rw-r--r-- 1 fly.redis.production.toml
-rw-r--r-- 1 fly.redis.staging.toml
-rw-r--r-- 1 fly.web.production.toml
-rw-r--r-- 1 fly.web.staging.toml
-rw-r--r-- 1 fly.worker.production.toml
-rw-r--r-- 1 fly.worker.staging.toml
drwxr-xr-x 12 keydb-staging-cable
drwxr-xr-x 12 keydb-production-cable
drwxr-xr-x 12 keydb-staging-cache
drwxr-xr-x 12 keydb-production-cache
drwxr-xr-x 12 keydb-staging-kredis
drwxr-xr-x 12 keydb-production-kredis

Вход в полноэкранный режим Выйти из полноэкранного режима

Возможно, есть какие-то интересные способы компоновки файлов TOML (как, например, YAML) для уменьшения дублирования, но, честно говоря, я еще не изучал их. Я, возможно, продолжу эту статью более подробной информацией, когда у меня будет свободное время для этого и/или будет достаточно больно 😜.

Развертывание конвейера с помощью действий Github

Последний кусочек головоломки — как запустить этот рабочий процесс в реальном CI/CD. На самом деле здесь есть довольно хорошая статья, но я все же быстро проведу вас через соответствующий YAML файл конфигурации (по адресу .github/workflows/release.yml).

Для простоты я объединил оба действия для выпуска staging и production сборок в одном файле. Я использую простой переключатель с помощью if: ${{ github.ref_name == 'staging' }}, чтобы отфильтровать, какие шаги использовать, но обратите внимание, что вы также можете просто поместить их в два отдельных YAML-файла рабочего процесса.

Единственный подготовительный шаг, который вам нужно сделать, это добавить FLY_API_TOKEN (полученный с помощью fly auth token) в секреты вашего репозитория Github. Затем мы используем официальное действие flyctl Github для запуска скриптов развертывания, как мы это делали из командной строки — и все!

name: Release

on:
  push:
    branches:
      - staging
      - master

env:
  FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

jobs:
  deploy:
    name: Deploy app
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: superfly/flyctl-actions@1.3
        if: ${{ github.ref_name == 'staging' }}
        with:
          args: "deploy -c fly/fly.worker.staging.toml"
      - uses: superfly/flyctl-actions@1.3
        if: ${{ github.ref_name == 'staging' }}
        with:
          args: "deploy -c fly/fly.web.staging.toml"
      # cheap staging/production switch
      - uses: superfly/flyctl-actions@1.3
        if: ${{ github.ref_name == 'master' }}
        with:
          args: "deploy -c fly/fly.worker.production.toml"
      - uses: superfly/flyctl-actions@1.3
        if: ${{ github.ref_name == 'master' }}
        with:
          args: "deploy -c fly/fly.web.production.toml"

Вход в полноэкранный режим Выход из полноэкранного режима

Ограничения

Хотя реализация postgres от Fly предлагает множество удобств для операционной деятельности из коробки (например, High Availability, легкое обновление), это не полностью управляемое решение, как предложение Heroku. Самое неприятное, что в нем отсутствует решение для автоматического резервного копирования и восстановления.

Что показалось немного необычным, генеральный директор Fly рекомендовал CrunchyData в качестве облачного решения, которое, по крайней мере, в моем понимании, сводит на нет преимущество наличия базы данных рядом с вашим сервером приложений, добавляя задержку между облачными провайдерами 🤷 (добавление: даже если кажется, что это не так уж и много). Тем более, что Fly всегда очень гордилась и говорила о том, что все работает на их собственном оборудовании, но Курт Маки также дал понять, что Fly не будет поставлять инфраструктуру, специфичную для Postgres, и они предпочтут передать эту ответственность третьей стороне, такой как Supabase (кстати, будем надеяться, что PlanetScale скоро получит версию Postgres).

Завершая этот раздел на позитивной ноте, в ответ на рекомендацию CrunchyData основатель SavvyCal Деррик Реймер счел ее достаточной для своих целей, а это, безусловно, очень хороший отзыв.

Заключение и последующие действия

Особенно по сравнению с Heroku, нам все еще приходилось много возиться вручную, что может показаться разочаровывающим со стороны. Я считаю сочетание мощного CLI с выразительными конфигурационными файлами плюсом, потому что это позволяет вывести на поверхность критически важную информацию об инфраструктуре вместо того, чтобы прятать ее за приборной панелью PaaS или нелегко запоминающимися вызовами CLI. Кроме того, пройдя долгий путь с точки зрения (Dev)Ops, такая установка для меня кажется оптимальным вариантом между слишком запутанными инфраструктурными абстракциями, которые утекают, как только происходит что-то необычное, и полностью индивидуальными развертываниями Kubernetes. Но, пожалуйста, воспринимайте последнее предложение с долей соли — ваш пробег может сильно отличаться!

Более того, CLI fly, а также веб-приложение значительно улучшили свою функциональность и UX за последние пару месяцев, поэтому я с нетерпением жду, куда нас заведет это путешествие. Кто знает, может быть, «установщик в один клик», придуманный в этой статье, находится всего в нескольких PR! Может быть, это также просто вопрос того, что кто-то предоставит соответствующий RailsByte.

Завершая эту статью, я хотел бы сделать два последних замечания:

  • В зависимости от требований вашего приложения, возможно, стоит отметить, что многорегиональный postgres является гражданином первого класса на Fly — это означает, что вы можете иметь актуальные реплики для чтения рядом с серверами вашего приложения. Однако имейте в виду, что запись через долгоживущие соединения (т.е. websockets) не рекомендуется для работы с этой парадигмой — другими словами, упаковывайте свои разрушительные действия в POST-запросы/регулярные отправки форм.
  • В мае 2022 года было объявлено о присоединении litestream к Fly. Если вы еще не слышали о нем, вот краткое описание: экземпляры SQLite, реплицируемые через S3 (или совместимое объектное хранилище) и синхронизируемые непосредственно с вашим сервером приложений (скоро). Из статьи по ссылке:

SQLite не просто находится на одной машине с вашим приложением, а фактически встроен в процесс работы приложения. Когда вы размещаете данные рядом с вашим приложением, вы можете увидеть, как задержка на запрос снижается до 10-20 микросекунд.

Вот это да! Будущее выглядит потрясающе.

Оцените статью
devanswers.ru
Добавить комментарий