Вы здесь:Время выполнения dist/
Что работает в продакшене
ЗачемЗная, что делает двоичный файл и что он оставляет другим, вы определяете всё остальное в настройке.
cybertrain build компилирует фреймворк, ваши контроллеры и модели и интерпретатор шаблонов в dist/blog, встраивая все представления, и копирует рядом public/. Там, где он работает, больше ничего не устанавливается: ни Ruby, ни gem.
browser ──HTTPS──▶ reverse proxy (kamal-proxy or Caddy)
└─ HTTP/1.1 ──▶ dist/blog (port 3000)
├─ public/ static files, 404.html, 500.html
└─ storage/production.sqlite3
- Он связан с системными SQLite и libc, поэтому собирайте его на той же ОС и том же процессоре, что и машина, на которой он работает. Сборка внутри образа Docker, как в конфигурации Kamal, решает это сама.
- Он говорит только на обычном HTTP/1.1. TLS и HTTP/2 — задача обратного прокси. В продакшене cookie сессии имеет атрибут
Secure, поэтому сайт должен отдаваться по HTTPS, иначе формы падают сInvalid authenticity token. - База данных — один файл SQLite. Запускайте один процесс на одном сервере и храните файл там, где он переживает развёртывание.
- По умолчанию он в режиме продакшена и не запускается без
CYBERTRAIN_SECRET_KEY_BASE../blog migrateприменяет встроенные в него миграции.
Два способа развёртывания
ЗачемОба ставят HTTPS перед одним и тем же двоичным файлом; различаются они тем, где он собирается и как развёртывание его заменяет.
| Kamal | systemd и Caddy | |
|---|---|---|
| Где собирается | В образе Docker, на вашей машине или удалённом сборщике | На сервере, из рабочей копии git |
| Перед приложением | kamal-proxy с сертификатом Let’s Encrypt | Caddy с сертификатом Let’s Encrypt |
| Статические файлы | Приложение отдаёт public/ | Caddy сам отдаёт dist/public/ |
| Развёртывание | Новый контейнер получает трафик после проверки работоспособности: ни одно соединение не отклоняется | Перезапуск: несколько секунд Caddy отдаёт 500.html |
| Откат | kamal rollback VERSION | Направить current на предыдущий выпуск и перезапустить |
| Серверу нужно | SSH и Docker (его ставит kamal setup), а также реестр образов | Ruby, набор инструментов C, заголовки SQLite, CLI cybertrain и Spinel |
В любом случае приложение — это один процесс и его файл SQLite на одном сервере.
Развёртывание через Kamal
Зачемcybertrain new уже написал файлы; вам остаётся указать сервер, домен и реестр.
Dockerfileэтап сборки на Ubuntu запускаетcybertrain setupиcybertrain build; облегчённый этап того же выпуска получает толькоdist/и библиотеки, с которыми он связанconfig/deploy.ymlсервер, домен, kamal-proxy с TLS на порт 3000, проверка работоспособности и том базы данных.kamal/secretsчитаетKAMAL_REGISTRY_PASSWORDиCYBERTRAIN_SECRET_KEY_BASEиз вашего окружения, но никогда не хранит сами значенияpublic/up.htmlстраница, которую kamal-proxy опрашивает, прежде чем направить трафик в новый контейнер
Заполните значения, помеченные CHANGE ME, в config/deploy.yml, сделайте коммит и выполните:
export KAMAL_REGISTRY_PASSWORD=...
export CYBERTRAIN_SECRET_KEY_BASE="$(openssl rand -hex 32)" # один раз; сохраните его
kamal setup # первый раз: Docker и kamal-proxy на сервере, сборка, публикация, запуск
kamal deploy # каждое следующее развёртывание
- Каждое развёртывание собирает образ (после первого раза Spinel остаётся в кеше слоёв Docker), публикует его и запускает контейнер, который выполняет
./blog migrate, а затемexec ./blogна0.0.0.0:3000. - kamal-proxy ждёт, пока
/up.htmlне ответит 200, переключает трафик на новый контейнер и останавливает старый, который поSIGTERMзавершает обрабатываемые запросы. - База данных лежит на томе
blog_storage, смонтированном в/app/storage, поэтому переживает каждое развёртывание. - Укажите в
builder.archпроцессор сервера. Spinel компилируется внутри образа, поэтому сборка для другой архитектуры под эмуляцией медленная; вместо неё собирайте на сервере черезbuilder.remote.
Пошаговое руководство, включая резервное копирование тома, — docs/deploy-kamal.md. В examples/blog та же конфигурация, и CI собирает и проверяет его образ.
За Caddy, под systemd
ЗачемБез Docker: сервер собирает приложение из git, systemd поддерживает его работу, Caddy смотрит в интернет.
- Приложение слушает только
127.0.0.1:3000, поэтому до него не добраться иначе как через Caddy.public/иstorage/разрешаются от рабочего каталога, поэтому systemd запускает его с рабочим каталогомdist/и читает секрет из файла окружения вне репозитория. - Скрипт развёртывания клонирует каждый выпуск в отдельный каталог, проверяет, что
gen/актуален, выполняетcybertrain buildиdist/blog migrate, затем направляет на него ссылкуcurrent, перезапускает службу и ждёт ответаup.html; если ответа нет, скрипт возвращает предыдущий выпуск. Ошибка до переключения работающий выпуск не затрагивает. Возврат не отменяет миграции, поэтому они должны оставаться совместимыми с предыдущим выпуском; при неудачном первом развёртывании возвращаться некуда, и скрипт просто завершается с ошибкой. - Caddy получает сертификат, перенаправляет HTTP на HTTPS, сам отдаёт файлы из
dist/public/и проксирует всё остальное в приложение. Пока приложение перезапускается, он отвечает500.html.
blog.example.com {
encode zstd gzip
root * /srv/blog/current/dist/public
@static file
handle @static {
file_server
}
handle {
reverse_proxy 127.0.0.1:3000
}
handle_errors {
rewrite * /500.html
file_server
}
}
Приложение добавляет X-Content-Type-Options и X-Frame-Options к тому, что отдаёт; файлы, которые Caddy отдаёт из dist/public/, через приложение не проходят, так что, если они нужны и там, добавьте их в блок сайта. С nginx всё так же: завершить TLS, отдавать dist/public/, проксировать остальное.
Полное руководство, от нового сервера Ubuntu до резервных копий и откатов, — docs/deploy.en.md (японский оригинал — docs/deploy.md).
Настройка
ЗачемНастройки продакшена берутся из окружения, поэтому один и тот же двоичный файл работает где угодно.
| Переменная | В продакшене |
|---|---|
CYBERTRAIN_SECRET_KEY_BASE | Обязательна. Подписывает cookie сессии; её смена разлогинивает всех. Создайте один раз командой openssl rand -hex 32 |
CYBERTRAIN_DATABASE | Файл SQLite; по умолчанию storage/production.sqlite3 в рабочем каталоге. Держите его вне выпуска |
CYBERTRAIN_HOST | По умолчанию 127.0.0.1, что верно за прокси на той же машине; в контейнере 0.0.0.0 |
PORT | По умолчанию 3000; туда пересылает прокси |
CYBERTRAIN_ENV | В собранном двоичном файле уже production; development запустил бы слежение за файлами и пересборки |
Шаг учебника Сборка для развёртывания перечисляет все переменные и соответствующие им атрибуты config/app.rb.
Повседневная работа
- Любое изменение — это развёртывание. Представления встраиваются при сборке, поэтому даже изменение только представления требует пересборки и перезапуска. Коммитьте
gen/вместе с изменением: скрипт развёртывания останавливается на устаревшемgen/. - Миграции выполняются до того, как новый код начнёт отвечать: в скрипте развёртывания или при запуске контейнера. Удаляйте столбец в одном из следующих развёртываний, когда работающий код его уже не читает.
- Делайте резервные копии через
.backupSQLite, а не копированием файла: так получается согласованная копия, даже пока приложение пишет. - Откат через миграцию означает откат и базы данных: перед переключением выполните
db rollbackиз более нового выпуска. - Остановка аккуратная: по
SIGTERMсервер перестаёт принимать соединения, завершает обрабатываемые запросы и выходит, не позже чем через 10 секунд.