Tavşan Deliğinden Aşağı Düşmek: Bir Vernik Yeniden Yükleme Arızasının Hikayesi - Bölüm 1

HayaletinushankaSon 20 dakikadır sanki hayatı buna bağlıymış gibi düğmelere basıp duran , gözlerinde yarı çılgın bir bakış ve sinsi bir sırıtışla bana dönüyor: "Dostum, sanırım anladım."

"Buraya bakın" diyor, ekrandaki sembollerden birini işaret ederek, "Kırmızı şapkamla bahse girerim ki, size az önce gönderdiğim şeyi buraya eklersek," kodun başka bir bölümünü işaret ederek, "hata artık silinmeyecek. sergilenecek."

Biraz şaşkın ve yorgunum, bir süredir üzerinde çalıştığımız sed ifadesini değiştirip dosyayı kaydedip çalıştırıyorum systemctl varnish reload. Hata mesajı kayboldu...

"Adayla paylaştığım e-postalar," diye devam etti meslektaşım, sırıtışı gerçek bir sevinç gülümsemesine dönüşürken, "Birdenbire bunun tamamen aynı sorun olduğunu fark ettim!"

Hepsi nasıl başladı

Makale bash, awk, sed ve systemd'nin nasıl çalıştığının anlaşıldığını varsayar. Vernik bilgisi tercih edilir ancak zorunlu değildir.
Parçacıklardaki zaman damgaları değiştirildi.
İle yazıldı Hayaletinushanka.
Bu metin, iki hafta önce İngilizce olarak yayınlanan orijinalin çevirisidir; tercüme boikoden.

Yine sıcak bir sonbahar sabahında güneş panoramik pencerelerden parlıyor, taze hazırlanmış, kafein açısından zengin bir fincan içecek klavyeden uzakta duruyor, en sevdiğiniz ses senfonisi kulaklığınızda duyuluyor, mekanik klavyelerin hışırtısını bastırıyor ve ilk giriş Kanban panosundaki birikmiş talepler listesinde "Vernishreload'u araştırın" sh: echo: Evrelemede G/Ç hatası" (Aşamada "varnishreload sh: echo: G/Ç hatası"nı araştırın) kaçınılmaz başlığıyla şakacı bir şekilde parlıyor. Konu vernik olduğunda, bu durumda olduğu gibi sorun yaratmasa bile hataya yer yoktur, olamaz.

aşina olmayanlar için cilayı yeniden yükle, bu yapılandırmayı yeniden yüklemek için kullanılan basit bir kabuk betiğidir vernik - aynı zamanda VCL olarak da adlandırılır.

Biletin adından da anlaşılacağı gibi hata sahnedeki sunuculardan birinde meydana geldi ve sahnedeki vernik yönlendirmesinin düzgün çalıştığından emin olduğum için bunun küçük bir hata olacağını varsaydım. Yani zaten kapalı olan bir çıktı akışıyla sonuçlanan bir mesaj. Bileti kendim için alıyorum, 30 dakikadan daha kısa bir süre içinde hazır olarak işaretleyeceğime tamamen güveniyorum, tahtayı bir başka çöpten temizlemek için sırtımı sıvazlayıp daha önemli konulara geri dönüyorum.

200 km/saat hızla duvara çarpmak

Dosyayı açma varnishreloadDebian Stretch çalıştıran sunuculardan birinde, 200 satırdan daha kısa bir kabuk betiği gördüm.

Komut dosyasını inceledikten sonra, onu doğrudan terminalden birden çok kez çalıştırırken sorunlara yol açabilecek hiçbir şey fark etmedim.

Sonuçta bu bir aşama, bozulsa bile kimse şikayet etmeyecek, yani... çok fazla değil. Komut dosyasını çalıştırıyorum ve terminale ne yazılacağını görüyorum, ancak hatalar artık görünmüyor.

Hatayı herhangi bir ek çaba harcamadan yeniden oluşturamayacağımdan emin olmak için birkaç çalıştırma daha yapıyorum ve bu betiği nasıl değiştireceğimi ve hala hata vermesini sağlayacağımı bulmaya başlıyorum.

Komut dosyası STDOUT'u geçersiz kılabilir mi (kullanarak > &-)? Yoksa STDERR mi? Bunların hiçbiri sonunda işe yaramadı.

Görünüşe göre systemd bir şekilde başlangıç ​​​​ortamını değiştiriyor, ama nasıl ve neden?
Vim'i açıp düzenliyorum varnishreload, ekleme set -x betiğin hata ayıklama çıktısının biraz ışık tutacağını umuyorum.

Dosya düzeltildi, bu yüzden verniği yeniden yükledim ve değişikliğin her şeyi tamamen bozduğunu görüyorum... Egzoz, içinde tonlarca C benzeri kodun bulunduğu tam bir karmaşa. Terminalde kaydırma yapmak bile nerede başladığını bulmak için yeterli değildir. Tamamen kafam karıştı. Hata ayıklama modu bir komut dosyasında başlatılan programların çalışmasını etkileyebilir mi? Hayır, bu saçmalık. Kabukta hata mı var? Kafamda farklı yönlere doğru giden hamamböcekleri gibi birçok olası senaryo uçuşuyor. Kafeinli içecek bardağı anında boşaltılıyor, stokları yenilemek için mutfağa hızlı bir yolculuk yapılıyor ve... yola çıkıyoruz. Senaryoyu açıyorum ve olaya daha yakından bakıyorum: #!/bin/sh.

/bin/sh - bu sadece bash'a bir sembolik bağlantıdır, dolayısıyla komut dosyası POSIX uyumlu modda yorumlanır, değil mi? Öyle değil! Debian'daki varsayılan kabuk dash'tır ve tam olarak buna benzemektedir. ifade eder /bin/sh.

# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 24  2017 /bin/sh -> dash

Bir test olarak shebang'ı şu şekilde değiştirdim: #!/bin/bash, silindi set -x ve tekrar denedim. Son olarak, verniğin daha sonra yeniden başlatılmasının ardından çıktıda kabul edilebilir bir hata ortaya çıktı:

Jan 01 12:00:00 hostname varnishreload[32604]: /usr/sbin/varnishreload: line 124: echo: write error: Broken pipe
Jan 01 12:00:00 hostname varnishreload[32604]: VCL 'reload_20190101_120000_32604' compiled

Hat 124, işte burada!

114 find_vcl_file() {
115         VCL_SHOW=$(varnishadm vcl.show -v "$VCL_NAME" 2>&1) || :
116         VCL_FILE=$(
117                 echo "$VCL_SHOW" |
118                 awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}' | {
119                         # all this ceremony to handle blanks in FILE
120                         read -r DELIM VCL_SHOW INDEX SIZE FILE
121                         echo "$FILE"
122                 }
123         ) || :
124
125         if [ -z "$VCL_FILE" ]
126         then
127                 echo "$VCL_SHOW" >&2
128                 fail "failed to get the VCL file name"
129         fi
130
131         echo "$VCL_FILE"
132 }

Ancak 124. satırın oldukça boş olduğu ve ilgi çekici olmadığı ortaya çıktı. Hatanın yalnızca 116. satırdan başlayan çok satırlı bir dizenin parçası olarak oluştuğunu varsayabilirim.
Sonuçta değişkene ne yazılır? VCL_FILE yukarıdaki alt kabuğun çalıştırılmasının bir sonucu olarak?

Başlangıçta değişkenin içeriğini gönderir VLC_SHOW, boru aracılığıyla verilen komutun ardından 115. satırda oluşturuldu. Peki orada ne olur?

Öncelikle orada kullanılıyor varnishadmVernik kurulum paketinin bir parçası olan, yeniden başlatmaya gerek kalmadan vernik kurulumu için.

Alt takım vcl.show -v belirtilen VCL konfigürasyonunun tamamının çıktısını almak için kullanılır. ${VCL_NAME}, STDOUT'a.

Geçerli etkin VCL yapılandırmasının yanı sıra, hâlâ bellekte olan vernik yönlendirme yapılandırmalarının önceki birkaç sürümünü görüntülemek için şu komutu kullanabilirsiniz: varnishadm vcl.listçıktısı aşağıdakine benzer olacaktır:

discarded   cold/busy       1 reload_20190101_120000_11903
discarded   cold/busy       2 reload_20190101_120000_12068
discarded   cold/busy       16 reload_20190101_120000_12259
discarded   cold/busy       16 reload_20190101_120000_12299
discarded   cold/busy       28 reload_20190101_120000_12357
active      auto/warm       32 reload_20190101_120000_12397
available   auto/warm       0 reload_20190101_120000_12587

Değişken değer ${VCL_NAME} betiğin başka bir bölümüne yüklenir varnishreload Varsa, şu anda aktif olan VCL'nin adına. Bu durumda “reload_20190101_120000_12397” olacaktır.

Harika, değişken ${VCL_SHOW} vernik için tam konfigürasyonu içerir, şimdilik açıktır. Şimdi nihayet kısa çizgi çıktısının neden olduğunu anlıyorum set -x o kadar bozuk olduğu ortaya çıktı ki, ortaya çıkan konfigürasyonun içeriğini içeriyordu.

Tam bir VCL konfigürasyonunun genellikle birkaç dosyadan bir araya getirilebileceğini anlamak önemlidir. C tarzı yorumlar, belirli yapılandırma dosyalarının diğerlerinin neresine dahil edildiğini belirlemek için kullanılır ve aşağıdaki kod pasajının konusu da budur.
Dahil edilen dosyaları açıklayan yorumların sözdizimi aşağıdaki formattadır:

// VCL.SHOW <NUM> <NUM> <FILENAME>

Bu bağlamda sayılar önemli değil, dosya adıyla ilgileniyoruz.

116. satırdan başlayan komutlar bataklığında sonuçta ne olur?
Şunu kabul edelim ki.
Ekip dört bölümden oluşuyor:

  1. basit echodeğişkenin değerini yazdıran ${VCL_SHOW}
    echo "$VCL_SHOW"
  2. awkmetni böldükten sonra ilk alanın “//” ve ikincisinin “VCL.SHOW” olduğu bir satırı (kaydı) arar.
    Awk, bu kalıplarla eşleşen ilk satırı yazacak ve ardından işlemeyi hemen durduracaktır.

    awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
  3. Alan değerlerini boşluklarla ayrılmış beş değişkende saklayan bir kod bloğu. Beşinci FILE değişkeni satırın geri kalanını alır. Son olarak, son yankı değişkenin içeriğini yazar ${FILE}.
    { read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
  4. 1'den 3'e kadar olan tüm adımlar bir alt kabuk içine alındığından, değerin çıktısı alınır. $FILE bir değişkene yazılacak VCL_FILE.

119. satırdaki yorumun da belirttiği gibi, bu, yalnızca VCL'nin adlarında boşluk bulunan dosyalara referans vereceği durumların güvenilir bir şekilde ele alınması amacına hizmet eder.

Orijinal işleme mantığını yorumladım ${VCL_FILE} ve komut sırasını değiştirmeye çalıştım ama hiçbir sonuca varamadım. Benim için her şey yolunda gitti ancak hizmeti başlattığımda hata verdi.

Görünüşe göre, komut dosyası manuel olarak çalıştırıldığında hata tekrarlanamaz, sözde 30 dakika zaten altı kez dolmuş ve buna ek olarak, diğer konuları bir kenara iterek daha yüksek öncelikli bir görev ortaya çıkmıştır. Haftanın geri kalanı çeşitli görevlerle doluydu ve sadece sed hakkında bir rapor ve bir adayla yapılan röportajla biraz sulandı. Hata ile ilgili sorun varnishreload zamanın kumları arasında geri dönülemez bir şekilde kaybolmuştu.

Senin sözde sed-fu'n... aslında... saçmalık

Sonraki hafta oldukça boş bir günüm vardı, bu yüzden bu cezayı yeniden ele almaya karar verdim. Bunca zamandır beynimde bir arka plan sürecinin bu soruna çözüm aradığını ve bu sefer neler olup bittiğini kesinlikle anlayacağımı umuyordum.

Geçen sefer sadece kodu değiştirmek işe yaramadığından, 116. satırdan başlayarak yeniden yazmaya karar verdim. Her durumda, mevcut kod aptalcaydı. Ve kesinlikle kullanmaya gerek yok read.

Hataya tekrar bakıyorum:
sh: echo: broken pipe - echo bu komutta iki yerde görünüyor, ancak ilkinin daha muhtemel suçlu (veya en azından suç ortağı) olduğundan şüpheleniyorum. Awk da güven telkin etmiyor. Ve gerçekten öyle olması durumunda awk | {read; echo} tasarım tüm bu sorunlara yol açıyor, neden onu değiştirmiyorsunuz? Bu tek satırlık komut, awk'nin tüm özelliklerini ve hatta bu ekstra özelliği bile kullanmaz read Ek olarak.

Geçen haftadan bu yana bir rapor vardı. sed, yeni edindiğim becerilerimi denemek ve basitleştirmek istedim echo | awk | { read; echo} daha anlaşılır bir hale echo | sed. Bu kesinlikle hatayı tanımlamak için en iyi yaklaşım olmasa da, en azından sed-fu'yu deneyebileceğimi ve belki sorun hakkında yeni bir şeyler öğrenebileceğimi düşündüm. Yol boyunca, sed konuşmasının yazarı olan meslektaşımdan daha verimli bir sed senaryosu geliştirmeme yardım etmesini istedim.

içindekileri bıraktım varnishadm vcl.show -v "$VCL_NAME" Böylece hizmetin yeniden başlatılmasıyla uğraşmadan sed betiğini yazmaya odaklanabildim.

Sed'in girdiyi tam olarak nasıl işlediğine dair kısa bir açıklama şu adreste bulunabilir: onun GNU kılavuzu. Sed kaynaklarında sembol n açıkça satır ayırıcı olarak belirtilmiştir.

Birkaç geçişte ve meslektaşımın tavsiyeleriyle, orijinal 116 satırının tamamıyla aynı sonucu veren bir sed betiği yazdık.

Aşağıda giriş verilerini içeren örnek bir dosya bulunmaktadır:

> cat vcl-example.vcl
Text
// VCL.SHOW 0 1578 file with 3 spaces.vcl
More text
// VCL.SHOW 0 1578 file.vcl
Even more text
// VCL.SHOW 0 1578 file with TWOspaces.vcl
Final text

Bu, yukarıdaki açıklamadan açıkça anlaşılmayabilir, ancak biz yalnızca ilk yorumla ilgileniyoruz // VCL.SHOWve giriş verilerinde bunlardan birkaçı olabilir. Bu nedenle orijinal awk ilk maçtan sonra sona eriyor.

# шаг первый, вывести только строки с комментариями
# используя возможности sed, определяется символ-разделитель с помощью конструкции '#' вместо обычно используемого '/', за счёт этого не придётся экранировать косые в искомом комментарии
# определяется регулярное выражение “// VCL.SHOW”, для поиска строк с определенным шаблоном
# флаг -n позаботится о том, чтобы sed не выводил все входные данные, как он это делает по умолчанию (см. ссылку выше)
# -E позволяет использовать расширенные регулярные выражения
> cat vcl-processor-1.sed
#// VCL.SHOW#p
> sed -En -f vcl-processor-1.sed vcl-example.vcl
// VCL.SHOW 0 1578 file with 3 spaces.vcl
// VCL.SHOW 0 1578 file.vcl
// VCL.SHOW 0 1578 file with TWOspaces.vcl

# шаг второй, вывести только имя файла
# используя команду “substitute”, с группами внутри регулярных выражений, отображается только нужная группa
# и это делается только для совпадений, ранее описанного поиска
> cat vcl-processor-2.sed
#// VCL.SHOW# {
    s#.* [0-9]+ [0-9]+ (.*)$#1#
    p
}
> sed -En -f vcl-processor-2.sed vcl-example.vcl
file with 3 spaces.vcl
file.vcl
file with TWOspaces.vcl

# шаг третий, получить только первый из результатов
# как и в случае с awk, добавляется немедленное завершения после печати первого найденного совпадения
> cat vcl-processor-3.sed
#// VCL.SHOW# {
    s#.* [0-9]+ [0-9]+ (.*)$#1#
    p
    q
}
> sed -En -f vcl-processor-3.sed vcl-example.vcl
file with 3 spaces.vcl

# шаг четвертый, схлопнуть всё в однострочник, используя двоеточия для разделения команд
> sed -En -e '#// VCL.SHOW#{s#.* [0-9]+ [0-9]+ (.*)$#1#p;q;}' vcl-example.vcl
file with 3 spaces.vcl

Yani, vernishreload betiğinin içeriği şuna benzer:

VCL_FILE="$(echo "$VCL_SHOW" | sed -En '#// VCL.SHOW#{s#.*[0-9]+ [0-9]+ (.*)$#1#p;q;};')"

Yukarıdaki mantık kısaca şu şekilde ifade edilebilir:
Dize normal bir ifadeyle eşleşiyorsa // VCL.SHOW, daha sonra bu satırdaki her iki sayıyı da içeren metni açgözlülükle yutun ve bu işlemden sonra kalan her şeyi kaydedin. Saklanan değeri yayınlayın ve programı sonlandırın.

Basit, değil mi?

Sed betiğinden ve tüm orijinal kodun yerini alması gerçeğinden memnunduk. Tüm testlerim istenen sonuçları verdi, bu yüzden sunucudaki “vernishreload”ı değiştirip tekrar çalıştırdım. systemctl reload varnish. Büyük yanlış echo: write error: Broken pipe yine yüzümüze güldü. Yanıp sönen imleç, terminalin karanlık boşluğunda yeni bir komutun girilmesini bekliyordu...

Kaynak: habr.com

Yorum ekle