Nokrišana truša bedrē: stāsts par vienas lakas pārkraušanas neveiksmi — 1. daļa

ghostinushanka, iepriekšējās 20 minūtes sita pa pogām, it kā no tā būtu atkarīga viņa dzīvība, pagriežas pret mani ar pusmežonu acu skatienu un viltīgu smīnu - "Draugs, es domāju, ka sapratu."

"Paskatieties šeit," viņš saka, norādot uz vienu no ekrānā redzamajiem simboliem, "es varu derēt, ka, ja mēs šeit pievienosim to, ko es jums tikko nosūtīju," norādot uz citu koda sadaļu, kļūda vairs nebūs. tiks parādīts."

Nedaudz neizpratnē un noguris es pārveidoju sed izteiksmi, pie kuras esam strādājuši kādu laiku, saglabāju failu un palaist systemctl varnish reload. Kļūdas ziņojums ir pazudis...

"E-pasta ziņojumi, ar kuriem es apmainījos ar kandidātu," mans kolēģis turpināja, viņa smaidam pāraugot patiesā prieka smaidā, "man pēkšņi sapratu, ka šī ir tieši tā pati problēma!"

Kā tas viss sākās

Rakstā tiek pieņemta izpratne par to, kā darbojas bash, awk, sed un systemd. Zināšanas par laku ir vēlamas, bet nav obligātas.
Fragmentos ir mainīti laikspiedoli.
Rakstīts ar ghostinushanka.
Šis teksts ir pirms divām nedēļām angļu valodā publicētā oriģināla tulkojums; tulkojums boikoden.

Saule spīd pa panorāmas logiem kārtējā siltā rudens rītā, tase svaigi pagatavota kofeīna bagāta dzēriena atrodas prom no klaviatūras, austiņās skan jūsu iecienītākā skaņu simfonija, kas apslāpē mehānisko klaviatūru šalkoņu, un pirmais ieraksts atpalikušo biļešu sarakstā uz Kanban tablo rotaļīgi spīd ar liktenīgo nosaukumu “Investigate lakas pārlādēšana” sh: echo: I/O error in stage” (Investigate “varnishreload sh: echo: I/O error” in stage). Runājot par laku, kļūdām ir un nevar būt vietas, pat ja tās nerada nekādas problēmas kā šajā gadījumā.

Tiem, kas nav pazīstami ar lakas pārkraušana, šis ir vienkāršs čaulas skripts, ko izmanto, lai atkārtoti ielādētu konfigurāciju laka - sauc arī par VCL.

Kā liecina biļetes nosaukums, kļūda radās vienā no skatuves serveriem, un, tā kā biju pārliecināts, ka lakas likšana uz skatuves darbojas pareizi, pieņēmu, ka tā būs neliela kļūda. Tātad, tikai ziņojums, kas nonāca jau slēgtā izvades straumē. Paņemu biļeti sev, pilnīgā pārliecībā, ka atzīmēšu to gatavu nepilnu 30 minūšu laikā, pabužinoju sev pa muguru par kārtējo atkritumu iztīrīšanu un atgriežos pie svarīgākām lietām.

Ietriecoties sienā ar ātrumu 200 km/h

Faila atvēršana varnishreload, vienā no serveriem, kurā darbojas Debian Stretch, es redzēju čaulas skriptu, kas ir mazāks par 200 rindiņām.

Izejot cauri skriptam, es nepamanīju neko tādu, kas varētu radīt problēmas, palaižot to vairākas reizes tieši no termināļa.

Galu galā šis ir posms, pat ja tas salūzīs, neviens nesūdzēsies, nu... ne pārāk. Palaižu skriptu un skatos, kas tiks rakstīts terminālī, bet kļūdas vairs nav redzamas.

Vēl pāris reizes, lai pārliecinātos, ka nevaru atveidot kļūdu bez papildu piepūles, un es sāku izdomāt, kā mainīt šo skriptu un panākt, lai tas joprojām radītu kļūdu.

Vai skripts var ignorēt STDOUT (izmantojot > &-)? Vai STDERR? Neviens no tiem beigās nedarbojās.

Acīmredzot systemd kaut kā maina startēšanas vidi, bet kā un kāpēc?
Atveru vim un rediģēju varnishreload, pievienojot set -x tieši zem shebang, cerot, ka skripta atkļūdošanas izvade radīs zināmu gaismu.

Fails ir izlabots, tāpēc pārlādēju laku un redzu, ka maiņa pilnībā visu izjauca... Izpūtējs ir pilnīgs bardaks, kurā ir tonnām C līdzīga koda. Pat ar ritināšanu terminālī nepietiek, lai atrastu, kur tas sākas. Esmu galīgi apjukusi. Vai atkļūdošanas režīms var ietekmēt skriptā palaistu programmu darbību? Nē, tas ir muļķības. Kļūda čaulā? Vairāki iespējamie scenāriji manā galvā kā tarakāni šņāc dažādos virzienos. Kofeīnu saturošā dzēriena krūze acumirklī tiek iztukšota, ātrs brauciens uz virtuvi, lai papildinātu krājumus un... dodamies ceļā. Es atveru skriptu un tuvāk apskatu shebangu: #!/bin/sh.

/bin/sh - šī ir tikai saite uz bash, tāpēc skripts tiek interpretēts ar POSIX saderīgā režīmā, vai ne? Ne tā! Debian noklusējuma apvalks ir dash, un tieši tā tas izskatās. atsaucas /bin/sh.

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

Pārbaudei es nomainīju shebangu uz #!/bin/bash, dzēsts set -x un mēģināja vēlreiz. Visbeidzot, pēc sekojošas lakas pārstartēšanas izvadā parādījās pieļaujama kļūda:

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. rinda, lūk!

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 }

Bet, kā izrādās, 124. rinda ir diezgan tukša un neinteresē. Varēju tikai pieņemt, ka kļūda radās kā daļa no vairākrindu virknes, kas sākas 116. rindā.
Kas galu galā tiek ierakstīts mainīgajā? VCL_FILE iepriekš minētā apakščaulas izpildes rezultātā?

Sākumā tas nosūta mainīgā saturu VLC_SHOW, kas izveidots 115. rindā, izpildot komandu caur cauruli. Un kas tad tur notiek?

Pirmkārt, to tur izmanto varnishadm, kas ir daļa no lakas uzstādīšanas komplekta, lakas uzstādīšanai bez restartēšanas.

Apakškomanda vcl.show -v izmanto, lai izvadītu visu VCL konfigurāciju, kas norādīta ${VCL_NAME}, uz STDOUT.

Lai parādītu pašreizējo aktīvo VCL konfigurāciju, kā arī vairākas iepriekšējās lakas maršrutēšanas konfigurāciju versijas, kas joprojām ir atmiņā, varat izmantot komandu varnishadm vcl.list, kura izvade būs līdzīga tālāk norādītajai:

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

Mainīga vērtība ${VCL_NAME} ir instalēta citā skripta daļā varnishreload uz pašlaik aktīvā VCL nosaukumu, ja tāds ir. Šajā gadījumā tas būs “pārlādēt_20190101_120000_12397”.

Lieliski, mainīgi ${VCL_SHOW} satur pilnīgu lakas konfigurāciju, pagaidām skaidrs. Tagad es beidzot saprotu, kāpēc domuzīmes izvade ir set -x izrādījās tik salauzta - tajā tika iekļauts iegūtās konfigurācijas saturs.

Ir svarīgi saprast, ka pilnīgu VCL konfigurāciju bieži var apvienot no vairākiem failiem. C stila komentāri tiek izmantoti, lai noteiktu, kur noteikti konfigurācijas faili ir iekļauti citos, un tieši par to ir norādīta šī koda fragmenta rinda.
Sintakse komentāriem, kas apraksta iekļautos failus, ir šādā formātā:

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

Cipari šajā kontekstā nav svarīgi, mūs interesē faila nosaukums.

Kas galu galā notiek komandu purvā, kas sākas 116. rindā?
Atzīsim.
Komanda sastāv no četrām daļām:

  1. Vienkāršs echo, kas izdrukā mainīgā lieluma vērtību ${VCL_SHOW}
    echo "$VCL_SHOW"
  2. awk, kas meklē rindiņu (ierakstu), kur pirmais lauks pēc teksta laušanas ir “//”, bet otrais ir “VCL.SHOW”.
    Awk izrakstīs pirmo rindiņu, kas atbilst šiem modeļiem, un pēc tam nekavējoties pārtrauks apstrādi.

    awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
  3. Koda bloks, kas saglabā lauku vērtības piecos mainīgajos, atdalītos ar atstarpēm. Piektais FILE mainīgais saņem pārējo rindas daļu. Visbeidzot, pēdējā atbalss izraksta mainīgā saturu ${FILE}.
    { read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
  4. Tā kā visas darbības no 1. līdz 3. ir ietvertas apakščaulā, tiek izvadīta vērtība $FILE tiks ierakstīts mainīgajā VCL_FILE.

Kā liecina komentārs par 119. rindu, tas ir vienīgais mērķis, lai droši apstrādātu gadījumus, kad VCL atsaucēs uz failiem ar atstarpēm to nosaukumos.

Esmu komentējis sākotnējo apstrādes loģiku ${VCL_FILE} un mēģināja mainīt komandu secību, bet tas ne pie kā nenoveda. Man viss darbojās labi, bet, uzsākot pakalpojumu, tika parādīta kļūda.

Šķiet, ka kļūda vienkārši nav atkārtojama, palaižot skriptu manuāli, savukārt it kā 30 minūtes jau sešas reizes ir beigušās un turklāt ir parādījies augstākas prioritātes uzdevums, nobīdot malā citas lietas. Pārējā nedēļa bija piepildīta ar dažādiem uzdevumiem, un to tikai nedaudz atšķaidīja ziņojums par sed un intervija ar kandidātu. Problēma ar kļūdu varnishreload bija neatgriezeniski pazudis laika smiltīs.

Jūsu tā sauktais sed-fu... patiesībā ir... atkritumi

Nākamajā nedēļā man bija viena diezgan brīva diena, tāpēc nolēmu vēlreiz ķerties pie šīs biļetes. Cerēju, ka manās smadzenēs kāds fona process visu šo laiku ir meklējis risinājumu šai problēmai, un šoreiz es noteikti sapratīšu, kas notiek.

Tā kā iepriekšējā reizē vienkārša koda maiņa nepalīdzēja, es vienkārši nolēmu to pārrakstīt, sākot no 116. rindiņas. Katrā ziņā esošais kods bija stulbs. Un nav absolūti nekādas vajadzības to izmantot read.

Vēlreiz apskatot kļūdu:
sh: echo: broken pipe — šajā komandā atbalss parādās divās vietās, bet man ir aizdomas, ka pirmais ir visticamākais vaininieks (vai vismaz līdzdalībnieks). Awk arī nerada pārliecību. Un gadījumā, ja tā tiešām ir awk | {read; echo} dizains noved pie visām šīm problēmām, kāpēc gan to neaizstāt? Šī vienas rindas komanda neizmanto visas awk funkcijas un pat šo papildu read papildus.

Kopš pagājušās nedēļas bija ziņojums par sed, gribēju izmēģināt savas jauniegūtās prasmes un vienkāršot echo | awk | { read; echo} saprotamākā echo | sed. Lai gan šī noteikti nav labākā pieeja kļūdas identificēšanai, es domāju, ka vismaz izmēģināšu savu sed-fu un varbūt uzzināšu kaut ko jaunu par problēmu. Pa ceļam es palūdzu savam kolēģim, sed sarunas autoram, palīdzēt man izdomāt efektīvāku sed skriptu.

Es nometu saturu varnishadm vcl.show -v "$VCL_NAME" uz failu, lai es varētu koncentrēties uz sed skripta rakstīšanu bez problēmām ar pakalpojuma atsāknēšanu.

Īss apraksts par to, kā tieši sed procesu ievade ir atrodama viņa GNU rokasgrāmata. Sed avotos simbols n skaidri norādīts kā rindu atdalītājs.

Vairākos piegājienos un ar mana kolēģa ieteikumiem mēs uzrakstījām sed skriptu, kas deva tādu pašu rezultātu kā visa sākotnējā 116. rinda.

Zemāk ir faila paraugs ar ievades datiem:

> 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

Tas var nebūt acīmredzams no iepriekš minētā apraksta, bet mūs interesē tikai pirmais komentārs // VCL.SHOW, un ievades datos tie var būt vairāki. Tāpēc sākotnējais awk beidzas pēc pirmās spēles.

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

Tātad lakas pārlādēšanas skripta saturs izskatīsies apmēram šādi:

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

Iepriekš minēto loģiku var īsi izteikt šādi:
Ja virkne atbilst regulārai izteiksmei // VCL.SHOW, tad alkatīgi aprīt tekstu, kurā ir iekļauti abi skaitļi šajā rindā, un saglabāt visu, kas paliek pēc šīs darbības. Izsniedziet saglabāto vērtību un pabeidziet programmu.

Vienkārši, vai ne?

Mēs bijām apmierināti ar sed skriptu un to, ka tas aizstāja visu sākotnējo kodu. Visi testi deva vēlamos rezultātus, tāpēc es nomainīju “lakas pārlādēšanu” serverī un palaist to vēlreiz systemctl reload varnish. Slikta kļūda echo: write error: Broken pipe atkal smējās mums sejā. Mirgojošais kursors gaidīja, kad termināļa tumšajā tukšumā tiks ievadīta jauna komanda...

Avots: www.habr.com

Pievieno komentāru