Rënia në përrallën e lepujve: Një histori mbi një gabim rindezjeje varnish — pjesa 1

ghostinushanka, duke duke në butona për 20 minuta të mëparshme, sikur jeta e tij të varej nga kjo, kthehet për mua me një shprehje gjysmë të egër në sy dhe një buzëqeshje të mençur — "Dude, duket se kam kuptuar."

"Shiko këtu," — thotë, duke treguar për një nga simbolet në ekran — "Vë bast me kapelën time të kuqe se nëse shtojmë këtu atë që të kam dërguar pak më parë" — duke treguar në një pjesë tjetër të kodit — "do të shmanget gabimi."

Pak i habitur dhe i lodhur, unë modifikoj shprehjen sed me të cilën po punonim prej disa kohësh, e ruaj skedarin dhe e ekzekutoj systemctl varnish reload. Mesazhi i gabimit zhduket...

"E-mailet që kisha shkëmbyer me kandidatin," vazhdoi kolegu im, ndërsa buzëqeshja e tij shndërrohet në një buzëqeshje të sinqertë plot gëzim, "Më erdhi në mendje se ishte pikërisht e njëjta problem!"

Si filloi gjithçka

Artikulli nënkupton një kuptim të parimeve të funksionimit të bash, awk, sed dhe systemd. Njohuria për varnish është e mirëpritur, por nuk është e detyrueshme.
Markat e përkohshme në snippet janë ndryshuar.
Shkruar së bashku me ghostinushanka.
Ky tekst është një përkthim i origjinalit, i botuar në anglisht dy javë më parë; përkthimi boikoden.

Dielli shkëlqen përmes dritareve panoramike një mëngjes tjetër të ngrohtë vjeshte, një filxhan pije të freskëta me kofein qëndron përveç tastierës, në kufje dëgjohet simfonia ime e preferuar e tingujve, e cila mbulon zhurmën e tastierave mekanike, dhe regjistrimi i parë në listën e biletave në kanban bëhet me një titull fatlum “Investigate varnishreload sh: echo: I/O error in staging” (Hetoni “varnishreload sh: echo: I/O error” në skenë). Kur bëhet fjalë për varnish, nuk ka vend për gabime, madje edhe nëse ato nuk rezultojnë në ndonjë problem si në këtë rast.

Për ata që nuk janë të njohur me varnishreload, kjo është një skript i thjeshtë shell, i përdorur për të rifreskuar konfigurimin të varnish — gjithashtu i njohur si VCL.

Si titulli i biletës sugjeron, ka ndodhur një gabim në një nga serverët në skenë, dhe pasi isha i sigurt se rutizimi i varnish në skenë funksionon pa probleme, supozova se do të ishte një gabim i vogël. Ajo ishte thjesht një mesazh i kapur në një rrjedhë të mbyllur. E marr biletën për vete, duke qenë plotësisht i sigurt se do ta shenoj si të gatshme për një kohë më pak se 30 minuta, duke e goditur veten në shpatull për të pastruar bordin nga një tjetër mbetje dhe do të kthehem në gjëra më të rëndësishme.

Duke goditur në mur me shpejtësi 200 km/h

Duke hapur skedarin varnishreload, në një nga serverët nën menaxhimin e Debian Stretch, pashë një skenar shell me më pak se 200 rreshta.

Duke kaluar shpejt skenarin, nuk pashë asgjë që mund të shkaktonte probleme kur do të përdorej disa herë drejtpërdrejt nga terminali.

Në fund të fundit, kjo është skena, edhe nëse dështon, askush nuk do të ankohej, mirë... jo shumë. E nisa skenarin dhe shoh se çfarë do të shkruhet në terminal, por tashmë nuk duket asnjë gabim.

Disa ekzekutime të tjera për të siguruar se nuk mund ta riprodhoj gabimin pa ndihma të tjera, dhe filloj të mendoj se si ta ndryshoj këtë skenar dhe ta bëj të shfaqë sërish gabimin.

Mbase t'ia ndaloj STDOUT (me > &-)? Ose STDERR? Asnjëra nuk funksionoi në fund.

Kështu që, duket se systemd në njëfarë mënyre ndryshon ambientin e ekzekutimit, por si dhe pse?
Hap vim dhe editoj varnishreload, duke shtuar set -x në pjesën e sipërme, duke shpresuar që dalja e debug e skenarit do të ndriçojë pak.

Skedari është modifikuar, kështu që e riçel varnish dhe shoh që ndryshimi e prish plotësisht... Dalja është një chaos i plotë, ku ka tonelata kode si C. As skrollimi në terminal nuk është e mjaftueshme për të gjetur se ku fillon. Jam në konfuzion të plotë. A mund të ndikojë moda e ndihmës në funksionimin e programeve që ekzekutohen në skenar? Jo, marrëzi. A është një bugin në shell? Disa skenarë të mundshëm më kalojnë në mendje si barinjtë në direksione të ndryshme. Një gotë me një pije të plotë me kafe shpejt e zbraz, një udhëtim i shpejtë në kuzhinë për t'u furnizuar dhe... le të shkojmë. Hap skenarin dhe besohem në shë-bang: #!/bin/sh.

/bin/sh — këtë është thjesht një sym-link në bash, kështu që skenari interpretohet në modalitetin kompatibil POSIX, apo jo? Jo kështu! Shell-i parazgjedhur në Debian është dash, dhe kjo është pikërisht ajo që citohet /bin/sh.

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

Për të provuar, e kam ndryshuar shë-bangun në #!/bin/bash, e fshita set -x dhe provova përsëri. Në fund, gjatë rindizjes së varnish-it, në daljen e tij u shfaq një gabim i përshtatshëm:

Jan 01 12:00:00 hostname varnishreload[32604]: /usr/sbin/varnishreload: linja 124: echo: gabim shkrimi: Tuba i prishur
Jan 01 12:00:00 hostname varnishreload[32604]: VCL 'reload_20190101_120000_32604' i kompiluar

Linja 124, ja ajo!

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                         # gjithë kjo ceremonie për të trajtuar boshllëqet në 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 "dështoi për të marrë emrin e skedës VCL"
129         fi
130
131         echo "$VCL_FILE"
132 }

Por siç rezultoi, linja 124 është mjaft e zbrazët dhe nuk paraqet interes. Mund të supozova vetëm se gabimi ndodhi si një pjesë e një shumëlinjëshi, që fillon në linjën 116.
Çfarë regjistrohet në fund në variablën VCL_FILE si rezultat i ekzekutimit të sub-shell-it të përmendur më sipër?

Në fillim, ai dërgon përmbajtjen e variablës VLC_SHOW, krijuar në linjën 115, komandës tjetër përmes tubave. E atje, çfarë ndodh atëherë?

Së pari, aty përdoret varnishadm, që është pjesë e paketës instalimit të varnish, për të konfiguruar varnish pa e rinisur.

Nënkomanda vcl.show -v përdoret për të shfaqur të gjithë konfigurimin VCL të specifikuar në ${VCL_NAME}, në STDOUT.

Për të treguar konfigurimin aktiv aktual të VCL-së, si dhe disa versione të mëparshme të konfigurimeve të rrugëzimit të varnish-it që janë ende në memorie, mund të përdoret komanda varnishadm vcl.list, dalja e së cilës do të jetë e ngjashme me atë më poshtë:

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

Vlera e variables ${VCL_NAME} vendoset në një pjesë tjetër të skriptit varnishreload në emrin e VCL aktive në atë moment, nëse ka një të tillë. Në këtë rast do të jetë “reload_20190101_120000_12397”.

Shkëlqyer, variabla ${VCL_SHOW} përmban konfigurimin e plotë për varnish, tani është e qartë. Tani, përfundimisht, kuptova pse dalja e dash-it set -x ishte aq e shkatërruar — ajo përfshinte përmbajtjen e konfigurim të marrë.

Është e rëndësishme të kuptoni se konfigurimi i plotë VCL shpesh mund të mbështetet në disa skedarë. Komentet në stilin C përdoren për të përcaktuar se ku janë përfshirë skedarët e konfigurimit në të tjerë, dhe kjo është pikërisht ajo për çfarë flitet në këtë fragment kodigo.
Sintaksa e komenteve që përshkruajnë skedarët e përfshirë ka këtë format:

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

Numrat në këtë kontekst nuk janë të rëndësishëm, na intereson emri i skedarit.

Çfarë ndodh në fund në moçalin e komandave që fillon në rreshtin 116?
Le të shqyrtojmë.
Komanda përbëhet nga katër pjesë:

  1. Një thjeshtë echo, e cila shfaq vlerën e variablës ${VCL_SHOW}
    echo "$VCL_SHOW"
  2. awk, e cila kërkon rreshtin (regjistrimin) ku fusha e parë, pas ndarjes së tekstit, do të jetë "//", dhe e dyta - "VCL.SHOW".
    Awk do të shkruajë rreshtin e parë që përputhet me këto shabllone dhe menjëherë do të ndalë përpunimin.
    awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
  3. Blloku i kodit që ruan vlerat e fushave të ndara me hapësira në pesë variabla. Variabla e pestë FILE merr mbetjen e rreshtit. Në fund, echo fundit shfaq përmbajtjen e variablës ${FILE}.
    { read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
  4. Pasi të gjithë hapat nga 1 deri në 3 janë të mbyllura në një nën-shelë, shfaqja e vlerës $FILE do të regjistrohet në variablën VCL_FILE.

Siç del nga komenti në rreshtin 119, kjo ka vetëm një qëllim: të trajtojë në mënyrë të sigurt rastet kur VCL do të referohet në skedarë me karaktere hapësire në emrin e tyre.

Unë e kam komentuar logjikën origjinale të përpunimit për ${VCL_FILE} dhe përpiqem të ndryshoj rendin e komandave, por kjo nuk kishte asnjë rezultat. Gjithçka funksiononte mirë për mua, por kur aktivizova shërbimin, ndodhi një gabim.

Duket se gabimi thjesht nuk është riprodhues kur ekzekutoj skriptin manualisht, dhe parashikimet 30 minuta tashmë kanë përfunduar rreth gjashtë herë, me një detyrë më prioritare që ka mënyruar punët e tjera në anash. Pjesa tjetër e javës ishte e mbushur me detyra të ndryshme dhe ishte pak e arritur nga një raport mbi sed dhe një intervistë me një kandidat. Problemi me gabimin në varnishreload u humb përgjithmonë në rërën e kohës.

Sed-fu i njohur… në të vërtetë… është një katastrofë

JavaScript dheu i lirë javën tjetër, kështu që vendosa të merrem përsëri me këtë bilet. Shpresoja se në trurin tim, ndonjë proces në sfond gjatë gjithë këtij kohë do të kishte gjetur një zgjidhje për këtë problem dhe kësaj here do ta kuptoja saktësisht se çfarë e shkaktonte.

Pasi herën e kaluar, ndryshimi i thjeshtë i kodit nuk ndihmoi, vendosa ta rishkruaj duke filluar nga rreshti 116. Në çdo rast, kodi ekzistues ishte i çuditshëm. Dhe nuk ka ndonjë nevojë për të përdorur read.

Duke e parë gabimin përsëri:
sh: echo: broken pipe — në këtë komandë echo është në dy vende, por dyshoj se e para është fajtori më i mundshëm (ose të paktën një bashkëfajtor). Awk gjithashtu nuk shkakton besim. Dhe në rast se vërtet kjo awk | {read; echo} konstruim shkakton të gjitha këto probleme, pse të mos e zëvendësojmë atë? Kjo komandë një-rresht është e thjeshtë dhe nuk shfrytëzon të gjitha mundësitë e awk, po ashtu edhe ky i tepërt read si shtesë.

Pasi javën e kaluar kishte një prezantim mbi sed, doja të provoja aftësitë e mia të reja dhe ta thjeshtësoja echo | awk | { read; echo} në një formë më të qartë echo | sed. Megjithëse kjo nuk është sigurisht metoda më e mirë për identifikimin e gabimeve, mendova se të paktën do të provonin shutter-fu tim dhe ndoshta do të mësoja diçka të re në lidhje me problemin. Gjatë procesit, kërkova ndihmën e kolegut tim, autorit të prezantimit mbi sed, për të krijuar një skript më efikas sed.

E dërgova përmbajtjen e varnishadm vcl.show -v "$VCL_NAME" në një skedar, kështu që mund të përqendrohem në shkruarjen e skriptit sed pa ndonjë shqetësim për riparimin e shërbimit.

Një përmbledhje e asaj se si sed përpunon të dhënat hyrëse mund të gjendet në udhëzimin e tij GNU. Në burimet e sed, simboli n është qartë i caktuar si ndarës rreshtash.

Pas disa kalimeve dhe me rekomandimet e kolegut tim, shkruam një skript sed që jepte të njëjtin rezultat si e gjithë rreshti origjinal 116.

Më poshtë është një shembull i skedarit me të dhëna hyrëse:

> cat vcl-example.vcl
Text
// VCL.SHOW 0 1578 file me 3 hapësira.vcl
Më shumë tekst
// VCL.SHOW 0 1578 file.vcl
Edhe më shumë tekst
// VCL.SHOW 0 1578 file me dy hapësira.vcl
Teksti përfundimtar

Kjo nuk mund të jetë e dukshme nga përshkrimi i mësipërm, por na intereson vetëm komenti i parë // VCL.SHOW, dhe në të dhënat hyrëse mund të vendosen disa prej tyre. Kjo është arsyeja pse awk origjinal përfundon pas takimit të parë.

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

Prandaj, përmbajtja e skriptit varnishreload do të dukej diçka si kjo:

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

Logjika e mësipërme mund të shprehet shkurtazi si:
Nëse stringu përputhet me shprehjen e rregullt // VCL.SHOW, atëherë hani textin me ethe që përfshin të dy numrat në këtë string dhe ruani gjithçka që mbetet pas kësaj operacioni. Jepni vlerën e ruajtur dhe përfundoni programin.

E thjeshtë, apo jo?

Ishim të kënaqur me skriptin sed dhe me faktin se ai zëvendëson atë të gjithë kodin origjinal. Të gjitha testet e mia dhanë rezultatet e dëshiruara, kështu që e ndryshova “varnishreload” në server dhe e rilansova systemctl reload varnish. Një gabim i keq echo: shkruaj gabim: Tub i prishur na qeshte sërish në fytyrë. Kursori që qeshte priste futjen e një komande të re në errësirën e terminalit…

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster