Dovşan çuxurundan aşağı düşmək: bir lakın yenidən yüklənməməsinin hekayəsi - 1-ci hissə

ghostinushanka20 dəqiqədir ki, sanki həyatı bundan asılımış kimi düymələri döyəcləyərək, gözlərində yarı vəhşi baxışlar və hiyləgər təbəssümlə mənə tərəf döndü - "Dostum, məncə başa düşdüm."

"Bura baxın," o, ekrandakı simvollardan birini göstərərək deyir, "qırmızı papağıma mərc edirəm ki, sizə indicə göndərdiyimi bura əlavə etsək," kodun başqa bir hissəsinə işarə edərək, "xəta artıq olmayacaq. göstəriləcək”.

Bir az çaşqın və yorğun, bir müddətdir üzərində işlədiyimiz sed ifadəsini dəyişdirirəm, faylı yadda saxlayın və işə salın systemctl varnish reload. Səhv mesajı itdi...

"Namizədlə mübadilə etdiyim e-poçtlar" dedi həmkarım gülümsəməsi həqiqi sevinc təbəssümünə çevrilərkən, "Birdən mənə elə gəldi ki, bu, eyni problemdir!"

Hər şey necə başladı

Məqalə bash, awk, sed və systemd-in necə işlədiyini başa düşməyi nəzərdə tutur. Lak haqqında biliklərə üstünlük verilir, lakin tələb olunmur.
Parçalardakı vaxt möhürləri dəyişdirildi.
ilə yazılmışdır ghostinushanka.
Bu mətn iki həftə əvvəl ingilis dilində nəşr olunmuş orijinalın tərcüməsidir; tərcümə boikoden.

Günəş başqa bir isti payız səhərində panoramik pəncərələrdən parlayır, bir fincan təzə hazırlanmış kofeinlə zəngin içki klaviaturadan uzaqda dincəlir, qulaqlıqlarınızda sevimli səs simfoniyası səslənir, mexaniki klaviaturaların xışıltısını boğur və ilk giriş. Kanban lövhəsindəki geridə qalan biletlərin siyahısında “Lak yüklənməsini araşdırın” sh: echo: səhnələşdirmədə I/O xətası” (investigate “varnishreload sh: echo: I/O error” in quruluşda) taleyüklü başlığı ilə oynaq şəkildə parlayır. Lak məsələsinə gəlincə, bu vəziyyətdə olduğu kimi heç bir problemlə nəticələnməsə belə, səhvlərə yer yoxdur və ola da bilməz.

Tanış olmayanlar üçün lak yenidən yükləyin, bu konfiqurasiyanı yenidən yükləmək üçün istifadə edilən sadə qabıq skriptidir lak - həmçinin VCL adlanır.

Biletin adından göründüyü kimi, səhv səhnədəki serverlərdən birində baş verdi və səhnədəki lak marşrutunun düzgün işlədiyinə əmin olduğum üçün bunun kiçik bir səhv olacağını güman etdim. Beləliklə, artıq bağlanmış bir çıxış axınında sona çatan bir mesaj. Bileti özüm üçün götürürəm, 30 dəqiqədən az müddətdə hazır olduğunu qeyd edəcəyəm, taxtanı daha bir zibildən təmizləmək üçün özümü kürəyinə vuracağam və daha vacib məsələlərə qayıdacağam.

200 km/saat sürətlə divara çırpıldı

Faylın açılması varnishreload, Debian Stretch ilə işləyən serverlərdən birində 200 sətirdən az bir qabıq skripti gördüm.

Skriptdən keçdikdən sonra onu birbaşa terminaldan dəfələrlə işə salarkən problemlə nəticələnə biləcək heç nə görmədim.

Axı bu bir mərhələdir, qırılsa da, heç kim şikayət etməz, yaxşı... çox da deyil. Skripti işlədirəm və terminala nə yazılacağını görürəm, amma xətalar artıq görünmür.

Heç bir əlavə səy göstərmədən səhvi təkrarlaya bilməyəcəyimə əmin olmaq üçün daha bir neçə çalışır və mən bu skripti necə dəyişdirəcəyimi anlamağa başlayıram və hələ də səhv buraxıram.

Skript STDOUT-u ləğv edə bilərmi ( > &-)? Yoxsa STDERR? Bunların heç biri sonda işə yaramadı.

Görünür systemd başlanğıc mühitini birtəhər dəyişdirir, amma necə və niyə?
vim açıb redaktə edirəm varnishreload, əlavə edir set -x skriptin debug çıxışının bir az işıq saçacağına ümid edərək, shebang altında.

Fayl düzəldildi, ona görə də lakı yenidən yükləyirəm və görürəm ki, dəyişiklik hər şeyi tamamilə pozdu ... Egzoz tam bir qarışıqlıqdır, içərisində tonlarla C-yə bənzər kod var. Terminalda sürüşmək belə onun haradan başladığını tapmaq üçün kifayət deyil. Mən tamamilə qarışıqam. Sazlama rejimi skriptdə işə salınmış proqramların işinə təsir edə bilərmi? Xeyr, cəfəngiyatdır. Qabıqdakı səhv? Bir neçə mümkün ssenari başımda tarakanlar kimi müxtəlif istiqamətlərdə dolaşır. Kofeinli içkinin fincanı dərhal boşaldılır, ehtiyatı doldurmaq üçün mətbəxə tez səfər və... yola düşürük. Ssenariyi açıb şebanqa daha yaxından baxıram: #!/bin/sh.

/bin/sh - bu, sadəcə olaraq bash üçün simvolik keçiddir, buna görə də skript POSIX uyğun rejimdə şərh olunur, elə deyilmi? Elə deyil! Debian-da defolt qabıq tiredir və o, məhz belə görünür. istinad edir /bin/sh.

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

Test olaraq, mən shebang dəyişdirdim #!/bin/bash, silindi set -x və yenidən cəhd etdi. Nəhayət, lakın sonrakı yenidən yüklənməsindən sonra çıxışda dözülən bir səhv meydana çı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                         # 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 }

Ancaq göründüyü kimi, 124-cü sətir kifayət qədər boşdur və heç bir maraq doğurmur. Mən yalnız güman edə bilərdim ki, xəta 116-cı sətirdən başlayan çoxsətirli sətirin bir hissəsi kimi baş verib.
Sonda dəyişənə nə yazılır? VCL_FILE yuxarıdakı alt qabığın icrası nəticəsində?

Başlanğıcda dəyişənin məzmununu göndərir VLC_SHOW, boru vasitəsilə əmrə əməl edərək 115-ci sətirdə yaradılmışdır. Və orada nə baş verir?

Birincisi, orada istifadə olunur varnishadmlak quraşdırma paketinin bir hissəsi olan lakın yenidən başlamadan qurulması üçün.

Alt komanda vcl.show -v -də göstərilən bütün VCL konfiqurasiyasını çıxarmaq üçün istifadə olunur ${VCL_NAME}, STDOUT üçün.

Cari aktiv VCL konfiqurasiyasını, eləcə də hələ də yaddaşda olan lak marşrutlaşdırma konfiqurasiyalarının bir neçə əvvəlki versiyasını göstərmək üçün əmrdən istifadə edə bilərsiniz. varnishadm vcl.list, çı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şən dəyər ${VCL_NAME} skriptin başqa bir hissəsində quraşdırılmışdır varnishreload əgər varsa, hazırda aktiv olan VCL-nin adına. Bu halda "yenidən yüklə_20190101_120000_12397" olacaq.

Əla, dəyişkən ${VCL_SHOW} lak üçün tam konfiqurasiyanı ehtiva edir, hələlik aydındır. İndi nəhayət tire çıxışının niyə olduğunu başa düşdüm set -x o qədər qırıldı - nəticədə konfiqurasiyanın məzmununu ehtiva etdi.

Tam VCL konfiqurasiyasının tez-tez bir neçə fayldan birləşdirilə biləcəyini başa düşmək vacibdir. C-stil şərhləri, müəyyən konfiqurasiya fayllarının başqalarına daxil edildiyi yerləri müəyyən etmək üçün istifadə olunur və aşağıdakı kod parçacığı sətri bundan ibarətdir.
Daxil edilmiş faylları təsvir edən şərhlərin sintaksisi aşağıdakı formatdadır:

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

Bu kontekstdə rəqəmlər vacib deyil, biz faylın adı ilə maraqlanırıq.

116-cı sətirdən başlayan əmrlər bataqlığında nəticədə nə baş verir?
Baxaq.
Komanda dörd hissədən ibarətdir:

  1. Sadə echo, dəyişənin dəyərini çap edən ${VCL_SHOW}
    echo "$VCL_SHOW"
  2. awk, mətni pozduqdan sonra birinci sahənin “//”, ikincinin isə “VCL.SHOW” olduğu sətir (qeyd) axtarır.
    Awk bu nümunələrə uyğun gələn ilk sətri yazacaq və dərhal emalını dayandıracaq.

    awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
  3. Sahə dəyərlərini boşluqlarla ayrılmış beş dəyişəndə ​​saxlayan kod bloku. Beşinci FILE dəyişəni sətrin qalan hissəsini alır. Nəhayət, sonuncu əks-səda dəyişənin məzmununu yazır ${FILE}.
    { read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
  4. 1-dən 3-ə qədər olan bütün addımlar alt qabığa daxil edildiyi üçün dəyəri çıxarır $FILE dəyişənə yazılacaq VCL_FILE.

119-cu sətirdəki şərhdən göründüyü kimi, bu, VCL-nin adlarında boşluq olan fayllara istinad edəcəyi halların etibarlı şəkildə idarə edilməsinin yeganə məqsədinə xidmət edir.

Mən orijinal emal məntiqini şərh etdim ${VCL_FILE} və əmr ardıcıllığını dəyişdirməyə çalışdı, lakin heç bir nəticə vermədi. Hər şey mənim üçün yaxşı işlədi, amma xidməti işə salanda xəta verdi.

Görünür, skripti əl ilə işləyərkən səhv sadəcə təkrarlanmır, ehtimal olunan 30 dəqiqə artıq altı dəfə keçib və əlavə olaraq, daha yüksək prioritet vəzifə ortaya çıxıb, digər məsələləri kənara qoyub. Həftənin qalan hissəsi müxtəlif tapşırıqlarla dolu idi və yalnız sed haqqında hesabat və namizədlə müsahibə ilə bir qədər azaldıldı. Səhv ilə bağlı problem varnishreload zamanın qumlarında geri dönməz şəkildə itdi.

Sizin sed-fu dediyiniz... əslində... zibildir

Növbəti həftə kifayət qədər pulsuz bir günüm oldu, ona görə də bu biletlə yenidən məşğul olmaq qərarına gəldim. Ümid edirdim ki, beynimdə hansısa fon prosesi bütün bu müddət ərzində bu problemin həllini axtarırdı və bu dəfə nə baş verdiyini mütləq başa düşəcəm.

Sadəcə olaraq kodu dəyişmək son dəfə kömək etmədiyi üçün mən sadəcə 116-cı sətirdən başlayaraq onu yenidən yazmağa qərar verdim. Hər halda, mövcud kod axmaq idi. Və ondan istifadə etməyə qətiyyən ehtiyac yoxdur read.

Xətaya yenidən baxırıq:
sh: echo: broken pipe — əks-səda bu əmrdə iki yerdə görünür, lakin mən şübhələnirəm ki, birincisi daha çox günahkardır (və ya heç olmasa ortaqdır). Awk da güvən yaratmır. Və həqiqətən belə olduğu halda awk | {read; echo} dizayn bütün bu problemlərə gətirib çıxarır, niyə onu dəyişdirməyək? Bu bir sətirli əmr awk-ın bütün xüsusiyyətlərindən, hətta bu əlavədən də istifadə etmir read əlavə olaraq.

Keçən həftədən bir hesabat var idi sed, yeni əldə etdiyim bacarıqlarımı sınamaq və sadələşdirmək istədim echo | awk | { read; echo} daha başa düşüləndir echo | sed. Bu, şübhəsiz ki, səhvi müəyyən etmək üçün ən yaxşı yanaşma olmasa da, ən azı sed-fu-mu sınamaq və bəlkə də problem haqqında yeni bir şey öyrənəcəyimi düşündüm. Yol boyu mən sed müzakirəsinin müəllifi olan həmkarımdan xahiş etdim ki, mənə daha səmərəli sed skripti hazırlamağa kömək etsin.

İçindəkiləri atdım varnishadm vcl.show -v "$VCL_NAME" bir fayla, buna görə də xidmətin yenidən başlaması ilə bağlı heç bir çətinlik çəkmədən sed skriptini yazmağa diqqət yetirə bildim.

Sed prosesləri daxiletməsinin tam olaraq necə tapıla biləcəyinin qısa təsviri onun GNU təlimatı. Sed mənbələrində simvol n açıq şəkildə xətt ayırıcı kimi göstərilmişdir.

Bir neçə keçiddə və həmkarımın tövsiyələri ilə bütün orijinal sətir 116 ilə eyni nəticəni verən sed skript yazdıq.

Aşağıda giriş məlumatları ilə bir nümunə fayldı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, yuxarıdakı təsvirdən aydın görünməyə bilər, lakin bizi yalnız ilk şərh maraqlandırır // VCL.SHOW, və giriş məlumatlarında onlardan bir neçəsi ola bilər. Buna görə orijinal awk ilk matçdan sonra bitir.

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

Beləliklə, varnishreload skriptinin məzmunu belə görünəcək:

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

Yuxarıdakı məntiqi qısaca belə ifadə etmək olar:
Əgər sətir normal ifadəyə uyğun gəlirsə // VCL.SHOW, sonra bu sətirdə hər iki rəqəmi ehtiva edən mətni acgözlüklə yeyin və bu əməliyyatdan sonra qalan hər şeyi yadda saxlayın. Saxlanılan dəyəri buraxın və proqramı bitirin.

Sadə, elə deyilmi?

Biz sed skriptindən və onun bütün orijinal kodu əvəz etməsindən məmnun idik. Bütün testlərim istənilən nəticəni verdi, ona görə də serverdəki “lak yenidən yükləmə”ni dəyişdim və yenidən işə saldım. systemctl reload varnish. Pis səhv echo: write error: Broken pipe yenə üzümüzə güldü. Göz vuran kursor terminalın qaranlıq boşluğunda yeni əmrin daxil edilməsini gözləyirdi...

Mənbə: www.habr.com

Добавить комментарий