Deploye Capistrano bez łez
Capistrano automatyzuje wdrożenie po SSH z atomowymi releasami i rollbackiem na jedną komendę. Praktyczny przewodnik po modelu releases/current, plikach współdzielonych, łańcuchu zadań Rails, konfiguracji multistage i pułapkach wartych poznania.
Wdrażanie aplikacji Rails ręcznie — git pull, bundle, migrate,
assets:precompile, restart i modlitwa — działa do dnia, w którym nie działa, a
dzień, w którym nie działa, to zwykle dzień, w którym najbardziej tego
potrzebowałeś. Capistrano zastępuje ten rytuał jedną powtarzalną komendą, która robi
te same kroki w tej samej kolejności za każdym razem, czyni przełączenie na nowy kod
atomowym i pozwala natychmiast się wycofać, gdy coś jest nie tak. Dla zespołów Ruby
to domyślne narzędzie deploy, a gdy raz jest skonfigurowane, wdrażanie przestaje być
rzeczą, której się boisz. Oto jak działa i jak je skonfigurować bez łez, których
tytuł obiecuje oszczędzić.
Model myślowy: releases i symlink current
Wszystko w Capistrano nabiera sensu, gdy zrozumiesz jego układ katalogów na
serwerze. Nie wdraża do jednego katalogu i go nadpisuje. Zamiast tego trzyma folder
releases/ z datowanymi katalogami, po jednym na deploy, oraz symlink current
wskazujący na aktywny:
/var/www/myapp/
├── releases/
│ ├── 20150518093000/
│ ├── 20150519140500/
│ └── 20150520101500/ <- najnowszy
├── current -> releases/20150520101500/
├── repo/ <- lustro git
└── shared/ <- przetrwa między releasami
Deploy klonuje nowy kod do świeżego datowanego katalogu, uruchamia tam wszystkie
kroki budowania, podczas gdy stary release wciąż obsługuje ruch, i dopiero na samym
końcu przełącza symlink current na nowy katalog w jednej atomowej operacji. To
funkcja, która liczy się najbardziej: przełączenie to pojedyncza zmiana symlinka,
więc użytkownicy nigdy nie widzą na wpół zbudowanego release’u. A rollback to po
prostu przełączenie current z powrotem na poprzedni znacznik czasu — cap production deploy:rollback — co jest natychmiastowe, bo stary release wciąż siedzi
tuż obok na dysku.
Pliki i katalogi współdzielone
Model datowanych release’ów rodzi oczywiste pytanie: a co z rzeczami, które muszą
przetrwać deploy — wgrane pliki, logi, master.key, skompilowane assety, które
chcesz reużyć? Te żyją w shared/, a Capistrano symlinkuje je do każdego nowego
release’u:
# config/deploy.rb
set :linked_files, %w[config/database.yml config/secrets.yml]
set :linked_dirs, %w[log tmp/pids tmp/cache public/system public/uploads]
linked_files i linked_dirs to mechanizm trzymający stan poza jednorazowymi
katalogami release’ów. Twój produkcyjny database.yml i secrets.yml żyją raz w
shared/config/ (umieszczone tam poza obiegiem, nigdy commitowane), a każdy release
symlinkuje do nich. Wgrania użytkowników w public/uploads przetrwają, bo też żyją w
shared/. Dyscyplina odzwierciedla to, czego nauczyliśmy się, konteneryzując usługi
ze stanem: release jest jednorazowy, dane współdzielone są cenne, a mają różne cykle
życia.
Łańcuch zadań Rails
Capistrano to runner zadań, a jego wtyczki wpinają znane kroki deploy we właściwe miejsce cyklu życia release’u:
# Gemfile
gem "capistrano-rails" # assety + migracje
gem "capistrano-bundler" # bundle install
gem "capistrano-rbenv" # właściwe Ruby
gem "capistrano3-puma" # restart serwera aplikacji
Z nimi wymaganymi deploy uruchamia, po kolei: pobranie kodu do nowego release’u,
bundle install, uruchomienie oczekujących migracji, precompilację assetów, potem
symlink current i restart serwera aplikacji. Dwie wygody warte wiedzy:
capistrano-rails jest na tyle mądry, by pominąć precompilację assetów, gdy nic
pod app/assets się nie zmieniło (duża oszczędność czasu, bo precompilacja to
najwolniejszy krok), a migracje uruchamiają się wobec nowego kodu, zanim symlink się
przełączy. Cały łańcuch to jedna komenda:
cap production deploy
Multistage: staging i produkcja z jednej konfiguracji
Niemal zawsze wdrażasz na więcej niż jedno środowisko, a wsparcie multistage w
Capistrano trzyma je uczciwymi. Współdzielone ustawienia żyją w config/deploy.rb;
specyfika per środowisko żyje w config/deploy/staging.rb i
config/deploy/production.rb:
# config/deploy/production.rb
server "web1.example.com", user: "deploy", roles: %w[app web db]
set :branch, "master"
# config/deploy/staging.rb
server "staging.example.com", user: "deploy", roles: %w[app web db]
set :branch, ENV["BRANCH"] || "develop"
Teraz cap staging deploy i cap production deploy używają tego samego łańcucha
zadań wobec różnych hostów i gałęzi — co jest dokładnie sposobem, w jaki zapewniasz,
że staging naprawdę próbuje produkcję, zamiast dryfować w inny proces.
Pułapki warte poznania
Kilka rzeczy, które kosztują ludzi popołudnie za pierwszym razem:
- Pierwszy deploy potrzebuje plików współdzielonych na miejscu. Capistrano
symlinkuje
linked_files, ale ich nie tworzy —shared/config/database.ymlmusi istnieć na serwerze przed pierwszym deployem, inaczej zawiedzie. Umieść je tam ręcznie (albo jednorazowym zadaniem) z góry. deploy:checkprzed deployem. Weryfikuje strukturę katalogów, pliki linkowane i dostęp SSH bez wdrażania. Uruchom go najpierw na nowym serwerze i napraw, na co narzeka.- Migracje nie są automatycznie zero-downtime. Capistrano czyni przełączenie kodu atomowym; nie czyni destrukcyjnej migracji bezpieczną. Migracja usuwająca kolumnę, której stary release wciąż używa, popsuje rzeczy w krótkim nakładaniu. Migracje wstecznie kompatybilne (dodaj kolumny, zanim ich użyjesz, usuń je deploy później) wciąż są twoim zadaniem.
- Symlink
currentprzełącza się ostatni, ale serwer aplikacji musi go przeładować. Upewnij się, że twój restart Pumy/Unicorna faktycznie podejmuje nowycurrent— phased restart trzymający stare workery wskazujące na stary release to klasyczne źródło „wdrożyłem, ale nic się nie zmieniło”. - Trzymaj rozsądny
keep_releases. Stare release’y piętrzą się na dysku;set :keep_releases, 5przycina je, trzymając wystarczająco historii na rollback.
Werdykt
Capistrano zamienia wdrożenie z lękliwego ręcznego rytuału w nudną, powtarzalną
komendę — co jest dokładnie tym, czym chcesz, by wdrożenie było. Model
releases-i-symlink daje ci atomowe przełączenia i natychmiastowe rollbacki za darmo,
shared/ trzyma twój stan bezpiecznym między deployami, łańcuch wtyczek obsługuje
specyfikę Rails, a multistage trzyma staging i produkcję zestrojone. Skonfiguruj
pliki współdzielone raz, pisz migracje wstecznie kompatybilne, a cap production deploy staje się komendą, którą uruchamiasz bez zastanowienia. W późniejszej epoce
będziemy wdrażać kontenerami i pipeline’ami, ale idee, które Capistrano wbija —
atomowe release’y, oddzielony stan, powtarzalne kroki, łatwy rollback — to te, na
których wciąż zbudowany jest każdy system deploy po nim.