omeryanbas.com

Ömer Yanbaş

Genel Müdür, Ticofab Yazılım

Operasyon

Yeniden başlatmanın siteyi düşürmesi: süreç yöneticisi ve açılışta kalıcılık

Süreç yöneticisi uygulamaları ancak service unit'i enable ve süreç listesi kayıtlıysa geri getirir. İkisi nasıl kontrol edilir, nasıl kanıtlanır.

Sunucu kimseye sormadan yeniden başlıyor: sağlayıcının bakım penceresi, bir kernel güncellemesi, bir elektrik olayı. Bir dakikadan kısa sürede geri geliyor ve site her isteğe 502 dönüyor. Web sunucusu çalışıyor ve cevap veriyor, sertifika geçerli, config değişmemiş, proxy'nin arkasındaki portta ise dinleyen hiç kimse yok. Aylardır uygulamayı güvenilir biçimde ayakta tutan süreç yöneticisi hiç başlamamış, biraz daha bakınca da başlasa neyi başlatacağını bilemeyeceği anlaşılıyor.

Gerçekte ne oluyor

Süreç yöneticisi de sonuçta uzun ömürlü bir süreçtir. Uygulamalarınızı izler, çöktüklerinde yeniden başlatır, log'larını tutar ve bunların hiçbirinin makine kapalıyken olan bitenle ilgisi yoktur. Uygulamaların açılışta geri gelmesi için üç ayrı koşulun sağlanması gerekir:

  1. Süreç yöneticisini birinin başlatması gerekir. systemd'li bir makinede bu, kurulu ve enable edilmiş bir service unit demektir; yani açılışta ulaşılan hedefin içinde symlink'in var olması.
  2. Yöneticinin neyi başlatacağını bilmesi gerekir. Bu da belirli bir kullanıcının home dizinine yazılmış ve unit başlarken geri okunan bir süreç listesidir.
  3. Kayıtlı listenin gerçekle uyuşması gerekir. O liste, birinin kaydet komutunu çalıştırdığı andaki fotoğraftır, çalışanların canlı aynası değil.

Baktığım bozuk kurulumların neredeyse hepsi bunlardan birini sağlıyor, diğerlerini sağlamıyordu. En yaygın hali, ilk deploy sırasında elle başlatılmış, o günden beri çalışan, hiç liste yazmamış ve hiç unit kurmamış bir yönetici. O makineyle ilgili her şey çalışır, ta ki biri kapatana kadar.

İkinci yaygın hal daha sinsi. Unit var ve enable edilmiş, ama bir kullanıcıyla çalışıyor, süreç listesi ise başka bir kullanıcıyla kaydedilmiş. Unit başlar, o kullanıcının home dizininde liste arar, bulamaz, hiçbir şey geri yüklemez ve başarıyla biter. Bütün durum kontrolleri yeşil rapor eder.

Üçüncü hal kayma. Unit de liste de yerinde, ama liste on bir ay önce, iki uygulama daha eklenmeden önce kaydedilmiş. O iki uygulama geri gelmez ve ana uygulama geldiği için makine toparlanmış gibi görünür.

Üç halin ortak yanı, hiçbirinin günlük işleyişte belirti vermemesi. Makine aylarca ayakta kalır, uygulama çöktüğünde yönetici onu zaten geri getirir, deploy'lar sorunsuz geçer. Kurulumun eksik yarısı yalnızca makine kapanıp açıldığında çalışacak olan yarısıdır ve bu da çoğu sunucuda yılda bir iki kez, genelde de sizin seçmediğiniz bir anda olur. Uptime ne kadar uzunsa o yarının en son çalıştığı an da o kadar geridedir. Yani uzun uptime bir güven işareti değil, test edilmemiş bir açılış yolunun ölçüsüdür.

Nasıl görülür

Belirtiden başlayıp aşağı inin. 502, proxy'nin bağlanamadığı anlamına gelir, o yüzden ilk soru portu tutan bir şey olup olmadığı:

curl -o /dev/null -s -w '%{http_code}\n' https://ornek.com/
# 502

ss -ltnp | grep 3000
# (çıktı yok)

Sonra iki soruyu ayrı ayrı sorun. Unit kurulu ve enable mı, ve sandığınız kullanıcıyla mı çalışıyor:

systemctl is-enabled pm2-deploy.service
# disabled

systemctl cat pm2-deploy.service | grep -i '^User\|^Environment'
# User=deploy
# Environment=PM2_HOME=/home/deploy/.pm2

Ve o kullanıcı için kayıtlı bir liste var mı, içinde beklediğiniz uygulamalar duruyor mu:

ls -l /home/deploy/.pm2/dump.pm2
# ls: cannot access '/home/deploy/.pm2/dump.pm2': No such file or directory

grep -o '"name":"[^"]*"' /home/deploy/.pm2/dump.pm2 | sort -u

İki cevap, iki bağımsız arıza. Unit yoksa süreç yöneticisini başlatan hiçbir şey yoktur. Dump yoksa ya da eskiyse yönetici başlar ve hiçbir şey geri yüklemez. Bunun bu kadar sık ısırmasının sebebi, günlük kullandığınız komutların, süreçleri listelemenin ve birini yeniden başlatmanın, bu yolların hiçbirine uğramaması.

Çözüm

Uygulamaların sahibi olan kullanıcıyla çalıştırılacak üç komut ve deploy script'ine eklenecek bir satır.

pm2 list
# reboot sonrası çalışması gereken şey tam olarak bu mu, doğrulayın

pm2 save
# bu kullanıcının home dizinine mevcut listeyi yazar

pm2 startup
# bu kullanıcı için service unit'ini kuran komutu ekrana yazar
# yazdığı komutu root olarak, birebir yazdığı gibi çalıştırın

İnsanların atladığı adım sonuncusu, çünkü pm2 startup işi kendisi yapmaz, yalnızca çalıştırmanız gereken komutu yazar. Yazdığını çalıştırın, sonra doğrulayın:

systemctl is-enabled pm2-deploy.service
# enabled

Sonra kaymayı kapatın. Kayıtlı liste bir fotoğraf olduğu için kaydetme işi deploy'un sonuna, uygulamalar çalışıp sağlık kontrolünden geçtikten sonraya aittir:

# deploy script'inde, restart ve health check'ten sonra
pm2 save

Deploy script'iniz bir şeyi yeniden başlatmadan önce zaten build çıkış kodunu kontrol ediyorsa, bu onun bir adım sonraki hali. Bozuk bir build'i yeniden başlatıp sonra onu açılış listesine kaydeden bir deploy, tek bir kötü sürümü kalıcı hale getirir. Yeniden başlatmadan önce çıkış kodunu kontrol etmenin gerekçesi de budur.

Üç komutun çözmediği kısım açılış sıralaması. Açılışta uygulamanız veritabanı bağlantı kabul etmeden ya da bir mount hazır olmadan başlayabilir. Hemen çıkan bir süreç yeniden başlatılır, birkaç saniye içinde art arda hata gören bir yönetici ise denemeyi bırakır ve uygulamayı durdurulmuş halde bırakır. Çözüm uygulama tarafında: ilk bağlantıda çıkmak yerine kısa bir backoff ile yeniden deneyin, hazır olup olmadığına sağlık kontrolü karar versin.

Çalıştığını nasıl doğrularsınız

Konfigürasyona güvenmeyin, çalıştırın. Yöneticiyi tamamen durdurup unit'i yeniden başlatmak, açılışın izleyeceği yolun aynısını koşturur:

pm2 kill
ss -ltnp | grep 3000
# (çıktı yok, beklenen bu)

systemctl start pm2-deploy.service
pm2 list
# uygulamalar geri geldi

curl -o /dev/null -s -w '%{http_code}\n' https://ornek.com/
# 200

Bu test on beş saniye sürer ve unit'i, kayıtlı listeyi ve kullanıcıyı birlikte kapsar. Kapsamadığı şey makinedeki diğer servislerle sıralama, o yüzden sakin bir zaman diliminde bir kez gerçek bir yeniden başlatma planlayın ve aynı üç şeye tekrar bakın:

uptime
# up 2 minutes

systemctl is-active pm2-deploy.service
# active

curl -o /dev/null -s -w '%{http_code}\n' https://ornek.com/
# 200

Bunu bir kez bilerek yapmak, başkasının bakım penceresinde öğrenmekten çok daha ucuzdur. Yedek işinin çıkış koduna inanmak yerine yedeği bilerek geri yüklemenin gerekçesiyle aynı gerekçe.

Nelere dikkat etmeli

  • Bir uygulamayı yeniden başlatmak açılış hakkında hiçbir şey kanıtlamaz. pm2 restart da systemctl restart da zaten ısınmış yolları kullanır. Anlamlı olan tek test sıfırdan başlayandır.
  • Her şeyin hangi kullanıcıya ait olduğuna bakın. Root ile kaydedilmiş liste ve deploy kullanıcısıyla çalışan unit en sık görülen uyumsuzluktur ve parçaların her biri tek tek doğru görünür.
  • Enable olmak çalışıyor olmak değildir, çalışıyor olmak da enable olmak değildir. Bugün elle başlatılan bir servis yarın orada olmaz, bugün enable edilen bir servis de biri başlatana kadar çalışmıyordur.
  • Dolan disk, servislerin açılışta başlamasını konfigürasyon hatası gibi görünen biçimlerde engeller, çünkü log yazılamaz ve socket açılamaz. Saklama süresi ve rotasyon, makineyi yalnızca derli toplu tutmanın değil, açılabilir tutmanın da parçasıdır.
  • Konteynerlerde aynı tuzak başka kelimelerle var. Restart politikası olmayan bir konteyner host yeniden başlayınca yok olur ve restart politikası yalnızca host düşerken var olan konteynerleri kapsar.
  • Uygulamaları sistem servisi yerine kullanıcı kapsamlı bir serviste çalıştırıyorsanız, o kullanıcı için lingering açık değilse oturum kapandığında uygulamalar da kapanır.
  • Açılıştan sonraki ilk kontrolü uygulamanın portuna değil, dışarıdan görünen adrese yapın. Uygulama ayağa kalkmış ama reverse proxy'nin upstream tanımı başka bir portu gösteriyorsa 502 aynı şekilde sürer ve siz süreç yöneticisini suçlamaya devam edersiniz.

Açılış da sonuçta bir kod yoludur ve normal işleyişte hiç çalışmayan tek yoldur; bozuk olanın o olmasının sebebi de budur. Sorulacak soru "süreç yöneticisi kurulu mu" değil, "onu ne başlatıyor, ne okuyor ve o dosya en son ne zaman yazıldı" olmalı. Her deploy üçüncü sorunun cevabını güncel bırakmalı, yılda bir kez de makine ilk ikisini kanıtlamak için bilerek yeniden başlatılmalı. Sizin seçtiğiniz bir reboot deneydir, seçmediğiniz bir reboot ise olaydır.

Sorular ve cevaplar

Sunucu yeniden başladıktan sonra uygulamam neden ayağa kalkmadı?
Çünkü süreç yöneticisi kendi başına yeniden başlatmadan sağ çıkmaz. Onu birinin başlatması gerekir, yani doğru kullanıcı için kurulu ve enable edilmiş bir service unit. Bir de neyi başlatacağını bilmesi gerekir, yani aynı kullanıcı için kaydedilmiş bir süreç listesi. İkisinden biri eksikse makine web sunucusu çalışır, arkası boş halde geri gelir.
Yeniden başlatmadan sonra gelen 502'nin sebebi ne?
Reverse proxy açıldı, uygulama açılmadı. Proxy dinleyen kimsenin olmadığı bir porta bağlanmaya çalışır ve anında 502 döner. Portu hangi sürecin tuttuğuna bakmak bunu tek komutta doğrular. Açılışta başlayıp hiç geçmeyen bir 502 neredeyse her zaman yavaş bir servisi değil, hiç başlamamış bir servisi gösterir.
pm2 save her deploy'dan sonra çalışmalı mı?
Deploy hangi uygulamaların var olduğunu, adlarını ya da başlatma komutlarını değiştiriyorsa evet. Kayıtlı liste canlı bir ayna değil, anlık bir fotoğraftır. Son kayıttan sonra eklenen bir uygulama listede yoktur ve açılışta geri gelmez. Kaydetmeyi deploy script'inin sonuna koymak bu kararı kimsenin hafızasına bırakmaz.
Sunucuyu yeniden başlatmadan açılış kalıcılığını nasıl test ederim?
Süreç yöneticisini tamamen durdurun, sonra service unit'ini yeniden başlatın ve uygulamaların geri gelip istek cevapladığını kontrol edin. Bu, açılışın kullanacağı unit'i ve kayıtlı listeyi birebir çalıştırır. Diğer servislerle sıralamayı test etmez, o yüzden bakım penceresinde bir kez gerçek bir yeniden başlatma yapmaya yine değer.
Süreç listesi neden bir kullanıcıda boş, diğerinde dolu geliyor?
Kayıtlı liste, kaydeden kullanıcının home dizininde durur, service unit ise tek bir kullanıcı olarak çalışır. Liste root olarak kaydedildiyse ve unit deploy kullanıcısıyla çalışıyorsa unit başlar, o kullanıcının home dizininde liste bulamaz ve hiçbir şey geri yüklemez. Her şeyden önce unit'in hangi kullanıcıyla çalıştığına bakın.