Kürkün bir quyusuna düşmək: Varnish-in yüklənməsi ilə bağlı bir səhv hekayəsi — 1-ci hissə

ghostinushanka, düymələri əvvəlki 20 dəqiqə ərzində sanki həyatı buna bağlıymış kimi basaraq, gözlərində yarı vəhşi bir ifadə və hiyləgər bir təbəssümlə mənə çevrilir - "Dostum, sanırım başa düşdüm."

"Bura bax," - deyir, ekranın bir simvoluna işarə edərək - "Mənim qırmızı şlyapama bahisin var, əgər burada sənə göndərdiyim şeyi əlavə etsək" - kodun başqa bir hissəsinə işarə edərək - "səhv artıq yaranmayacaq."

Bir qədər çaşmış və yorğun, artıq bir müddətdir üzərində işlədiyimiz sed ifadəsini dəyişdirirəm, faylı saxlayıram və işə salıram systemctl varnish reload. Səhv mesajı yox oldu…

"Namizəd ilə mübadilə etdiyim e-maillər," - davam etdi həmkarım, gülüşü həqiqi bir sevinc dolu gülümsəməyə çevrilərək, "Mənə subyektiv olaraq bu eyni problem olduğu qənaətinə gəldim!"

Hər şey necə başladı

Məqalə bash, awk, sed və systemd-nin iş prinsiplərini başa düşməyi tələb edir. Varnish bilikləri arzuolunandır, amma mütləq deyil.
Snippet-lərdə vaxt damğaları dəyişdirildi.
Birlikdə yazılmışdır ghostinushanka.
Bu mətn, iki həftə əvvəl ingiliscə dərc edilmiş orijinalın tərcüməsidir; tərcümə boikoden.

Günəş panoramik pəncərələrdən keçərək, bir daha isti payız səhərində parlayır, yeni hazırlanmış kofein baxımından zəngin bir fincan klaviaturanın yanına qoyulub, qulaqlıqlarda sevimli simfoniyam səslənir və mexaniki klaviaturaların hışırtısını üstələyir, kanban lövhəsində arxa planda ilk biletin başlığı “varnishreload sh: echo: I/O error in staging” (Rejisdə “varnishreload sh: echo: I/O error” araşdırın) parlayır. Varnish haqqında danışdıqda, səhvlərin yeri yoxdur və ola bilməz, hətta bunlar bu halda olduğu kimi heç bir problemə çevrilməsələr də.

Tanışı olmayanlar üçün varnishreload, konfiqurasiyanı yükləmək üçün istifadə olunan sadə bir shell skriptdir varnishin — eyni zamanda VCL adlanır.

Biletin adından da anlaşılacağı üzere, hata sahne sunucularından birinde meydana geldi ve sahnede varnish yönlendirmesinin düzgün çalıştığından emin olduğum için bunun ufak bir hata olacağını düşündüm. Yani, zaten kapatılmış olan bir çıkış akışına düşmüş bir mesaj. Bileti alıyorum, onu 30 dakika içinde çözeceğime dair tam bir güvenle, panoda bir başka gereksiz yükten kurtulduğum için kendime bir kez daha teşekkür ediyorum ve daha önemli işlere dönüyorum.

200 km/s hızla bir duvara çarparak

Bir dosyayı açtığımda, varnishreloadDebian Stretch ile yönetilen bir sunucuda, 200 satırdan daha kısa bir shell script gördüm.

Script üzerinden geçerken, bir terminalden tekrar tekrar çalıştırmanın sorun çıkarabileceğine dair bir şey fark etmedim.

Sonuçta, bu bir sahne, bozulsa bile kimse şikayet etmeyecek, yani... çok fazla değil. Scripti çalıştırıyorum ve terminalde ne olacağını izliyorum, ama hata mesajları yok artık.

Hata ile karşılaşmadığımı doğrulamak için birkaç kez daha çalıştırıyorum ve bu scripti nasıl değiştirebileceğimi düşünüyorum, böylece hata vermesini sağlayacağım.

Belki scriptin STDOUT'unu engelleyebilirim (şu şekilde: > &-)? Ya da STDERR? Her ikisi de sonuçta işe yaramadı.

Görünüşe göre systemd, çalışma ortamını bir şekilde değiştiriyor, ama nasıl ve neden?
Vim'i açıyorum ve editliyorum, varnishreloadset -x şebang'ın hemen altına ekliyorum, scriptin debug çıktısının biraz ışık tutmasını umarak. Dosya düzenlendi, bu yüzden varnish'i yeniden başlatıyorum ve bu değişikliğin her şeyi bozduğunu görüyorum... Çıktı — tam bir karmaşa, içerisinde tonlarca C benzeri kod. Terminalde kaydırmak bile başlangıcını bulmak için yeterli değil. Tam bir şaşkınlık içindeyim. Debug modu, scriptte çalıştırılan programların işleyişini etkileyebilir mi? Hayır, saçmalık. Shell'de bir hata mı var? Birkaç olası senaryo kafamda, etrafa dağılmış hamam böcekleri gibi koşuyor. Kafein dolu bir fincanım aniden boşalıyor, depoyu doldurmak için mutfağa hızlı bir yolculuk ve... hadi gidelim. Scripti açıp şebang'a dikkatlice bakıyorum:

— bu sadece bash'e bir sembolik bağlantı, bu yüzden scriptin POSIX uyumlu modda yorumlanması gerek, değil mi? Hayır, burada bir sorun var! Debian'daki varsayılan shell, dash ve işte bu, #!/bin/sh.

/bin/sh değinildiği şey. direnir. /bin/sh.

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

Sınaq üçün, şebangı dəyişdirdim #!/bin/bash, sildim şebang'ın hemen altına ekliyorum, scriptin debug çıktısının biraz ışık tutmasını umarak. və bir daha cəhd etdim. Nəhayət, varnish-in sonrakı yenidən yüklənməsi zamanı nəticədə mənalı bir xəta çıxdı:

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

124-cü sətir, budur!

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                         # bütün bu mərasim FAYL’dakı boşluqları idarə etmək üçündür
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 }

Amma göründüyü kimi, 124-cü sətir olduqca boşdur və maraq doğurmur. Yalnızca, xətanın 116-cı sətirdə başlayan çoxsətirli bir tərkib hissəsi kimi meydana gəldiyini düşünə bildim.
Nəticədə dəyişkənə yazılan VCL_FILE yuxarıda qeyd olunan sub-shell’in icrası nəticəsində nədir?

Əvvəlcə, 115-ci sətirdə yaradılmış VLC_SHOW, ardından yayı alsın, onun içərisinə yönləndirilir. Bəs orada nə baş verir?

Birincisi, burada varnishadm, varnish-in yenidən başladılmadan konfiqurasiya edilməsi üçün varnish paketinin bir hissəsidir.

Alt komanda vcl.show -v VCL konfiqurasiyasının hamısını ${VCL_NAME}, STDOUT-da göstərmək üçün istifadə olunur.

Hazırda aktiv olan VCL konfiqurasiyasını, eləcə də hələ də yaddaşda olan bir neçə əvvəlki varnish konfiqurasiyalarını göstərmək üçün varnishadm vcl.listkomandasını istifadə edə bilərsiniz, onun çıxışı aşağıdakı kimi olacaq:

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

Dəyişkənin dəyəri ${VCL_NAME} scriptin başqa bir hissəsində varnishreload aktiv olan VCL adını, əgər varsa, təyin olunur. Bu halda bu, "reload_20190101_120000_12397" olacaq.

Əla, dəyişkən ${VCL_SHOW} varnish üçün tam konfiqurasiyanı ehtiva edir, indi aydındır. İndi nəhayət, dash ilə şebang'ın hemen altına ekliyorum, scriptin debug çıktısının biraz ışık tutmasını umarak. çıxışın nə üçün belə pozulduğunu anladım - bu, əldə olunan konfiqurasiyanın məzmununu əhatə edirdi.

Vacibdir başa düşmək ki, VCL-in tam konfiqurasiyası tez-tez bir neçə fayldan hazırlanır. C stilindəki şərhlər, konfiqurasiya fayllarının harada daxil edildiyini müəyyən etmək üçün istifadə olunur və aşağıdakı kod fraqmentinin üstündə tam olaraq bu barədə danışılır.
Daxil olunmuş faylları təsvir edən şərhlərin sintaksisi aşağıdakı formatda olur:

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

Bu kontekstdə rəqəmlərin əhəmiyyəti yoxdur, bizi yalnız fayl adı maraqlandırır.

116-cı sətirdən başlayan komandalar bataqlığında nə baş verir?
Gəlin başa düşək.
Komanda dörd hissədən ibarətdir:

  1. Sadə echo, dəyişənin dəyərini çıxaran ${VCL_SHOW}
    echo "$VCL_SHOW"
  2. awk, mətni parçalayandan sonra birinci sahə "//", ikinci sahə isə "VCL.SHOW" olan sətri axtarır.
    Awk, bu şablonlara uyğun ilk sətiri çıxaracaq və dərhal işləməyi dayandıracaq.
    awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
  3. Boşluqlarla ayrılmış sahələrin qiymətlərini beş dəyişəndə saxlayan kod bloku. Beşinci dəyişən FILE, sətirin qalan hissəsini alır. Nəticədə, son echo dəyişənin məzmununu çıxarır ${FILE}.
    { read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
  4. Bütün 1-dən 3-ə qədər olan addımlar alt-konsolda yerləşdiyindən, $FILE dəyəri bir dəyişənə yazılacaq. VCL_FILE.

119-cu sətirdəki şərhdən; bu, yalnız bir məqsəd üçündür: VCL-in adında boşluq simvolları olan fayllara istinad etdiyi halları etibarlı işləmək.

Mən orijinal emal məntiqini şərh etdim ${VCL_FILE} və əmrlərin ardıcıllığını dəyişməyə çalışdım, lakin buna heç bir nəticə vermədi. Məndə hər şey təmiz işləyirdi, lakin xidmət başladıqda bir xəta verirdi.

Görünür ki, xətanın tək başına skripti işə salanda təkrarlanması mümkün deyildi; nəzərdə tutulan 30 dəqiqə artıq altı dəfə başa çatdı və üstəlik, daha prioritet bir iş də ortaya çıxdı. Həftənin qalan hissəsi müxtəlif məsələlərlə doldu və yalnız azacıq sed haqqında təqdimat və namizəd müsahibəsi ilə aralanmışdı. Xətanın problemi varnishreload zamanda itirildi.

Sizin bu məşhur sed-fu... əslində... bərbad vəziyyətdədir.

Gələn həftə bir az boş vaxtım oldu, buna görə də bu biletə yenidən baxmaq qərarına gəldim. Ümid edirdim ki, beynimdə bir arxa proses bu müddət ərzində problemin həllini axtarırdı və bu dəfə nə baş verdiyini mütləq anlayacağam.

Keçən dəfə kodu sadəcə dəyişdirmək kömək etmədiyinə görə, 116-cı sətirdən başlayaraq onu yenidən yazmağa qərar verdim. Hər halda, mövcud kod çox tərs idi. Və orada tamamilə heç bir ehtiyac yoxdur read.

Xətaya bir daha baxanda:
sh: echo: broken pipe — bu komandada echo iki yerdə var, amma birincinin daha çox günahkar olacağını düşünürəm (ya da ən azından həmkârıdır). Awk da inam vermir. Və əgər bu həqiqətən awk | {read; echo} konstruksiyası bu bütün problemlərə səbəb olursa, niyə onu əvəz etməyək? Bu tək sətirli komanda awk-ın bütün imkanlarından istifadə etmir, üstəlik bu əlavə read imkanları da var.

Keçən həftə sedhaqqında bir təqdimat olduğu üçün, mən yeni əldə etdiyim bacarıqları sınamaq və echo | awk | { read; echo} daha aydın bir şəkildə formalaşdırmaq istədim. echo | sed. Bu, mütləq xətanın aşkar edilməsi üçün ən yaxşı yanaşma olmasa da, düşündüm ki, ən azından sed-fu-mi sınayım və bəlkə də problemlə bağlı yeni məlumatlar əldə edim. Prosesi davam etdirərkən, sed haqqında təqdimat müəllifi olan həmkarımdan daha səmərəli bir sed skripti yaratmağa kömək etməsini istədim.

Mən varnishadm vcl.show -v "$VCL_NAME" məzmununu bir fayla göndərdim ki, sed skriptini yazmağa diqqətimi cəmləşdirim, xidmətin yenidən başlaması ilə bağlı heç bir narahatlığım olmasın.

Sed-in giriş məlumatlarını necə idarə etdiyi haqqında qısa təsvir onun GNU rəhbərlik kitabındatapıla bilər. n Sed-in mənbə kodunda

simvolu açıq-aşkar xətt ayrıcı kimi göstərilmişdir.

Bir neçə keçid və həmkarımın tövsiyələri ilə, 116-cı sətirin tam effektivliyi ilə eyni nəticəni verən bir sed skripti yazdıq.

Aşağıda giriş məlumatları üçün faylın bir nümunəsi var:

> 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 // VCL.SHOWYuxarıda göstərilən təsvirə əsasən, bu açıq-aydın görünməyə bilər, amma bizi yalnız ilk şərh maraqlandırır

# шаг первый, вывести только строки с комментариями
# используя возможности 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

, burada giriş məlumatlarında bir neçə ola bilər. Məhz buna görə də orijinal awk ilk uyğunlaşmadan sonra işini dayandırır.

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

Yuxarıda göstərilən məntiqi qısa şəkildə aşağıdakı kimi ifadə etmək olar:
Əgər sətir müntəzəm ifadəyə uyğundursa // VCL.SHOW, onda bu sətirdəki hər iki sayını əhatə edən mətni söğüşlə yeyin və bu əməliyyatdan sonra qalanı saxlayın. Saxlanılan dəyəri verin və proqramı bitirin.

Sadədir, elə deyilmi?

Biz sed skripti ilə razı idik və bunun orijinal kodu tamamilə əvəz etməsi faktı ilə. Bütün testlərim istədiyim nəticələri verdi, buna görə "varnishreload"-u serverdə dəyişdirdim və yenidən başladım systemctl reload varnish. Bərbad bir xəta echo: write error: Broken pipe yenidən bizə gülürdü. Göz qırpan imleç qaranlıq terminalda yeni əmrin daxil edilməsini gözləyirdi...

Mənbə: habr.com

DDoS qoruması olan saytlara etibarlı hosting satın alın, VPS VDS serverlər 🔥 DDoS qoruması olan saytlara etibarlı hosting satın alın, VPS VDS serverlər | ProHoster