Falling Down the Rabbit Hole: Die storie van een vernis-herlaaifout - Deel 1

Ghostinushanka, nadat hy die vorige 20 minute op die knoppies gestamp het asof sy lewe daarvan afhang, draai na my met 'n semi-wilde uitdrukking in sy oë en 'n slinkse glimlag - "Dude, ek dink ek verstaan."

"Kyk hier," sê hy en wys na een van die karakters op die skerm, "ek wed my rooi hoed dat as ons hier byvoeg wat ek sopas vir jou gestuur het" - en na 'n ander gedeelte van die kode wys - "die fout is nie meer sal nie vertoon word."

'n Bietjie verbaas en moeg verander ek die sed-stelling waaraan ons al 'n rukkie gewerk het, stoor die lêer en hardloop systemctl varnish reload. Die foutboodskap het verdwyn...

"Die e-posse wat ek met die kandidaat uitgeruil het," het my kollega voortgegaan, terwyl sy glimlag verander in 'n opregte glimlag vol vreugde, "Dit het skielik tot my deurgedring dat dit presies dieselfde probleem is!"

Hoe het dit alles begin

Die artikel veronderstel 'n begrip van hoe bash, awk, sed en systemd werk. Kennis van vernis word verkies maar nie vereis nie.
Tydstempels in brokkies is verander.
Geskryf met Ghostinushanka.
Hierdie teks is 'n vertaling van die oorspronklike wat twee weke gelede in Engels gepubliseer is; vertaling boyikoden.

Die son skyn deur die panoramiese vensters op nog 'n warm herfsoggend, 'n koppie vars gebroude kafeïenhoudende drankie rus aan die kant van die klawerbord, 'n gunsteling simfonie van klanke speel in die oorfone oor die geritsel van meganiese klawerborde, en die eerste inskrywing in die lys van agterstandkaartjies op die kanban-bord gloei speels met die noodlottige titel “Ondersoek varnishreload sh: eggo: I/O error in staging” (Ondersoek “varnishreload sh: eggo: I/O error” in staging). Wanneer dit by vernis kom, is daar geen en kan geen foute wees nie, al lei dit nie tot enige probleme nie, soos in hierdie geval.

Vir die wat nie vertroud is met vernislaai, dit is 'n eenvoudige dopskrif wat gebruik word om die konfigurasie te herlaai vernis - ook genoem VCL.

Soos die titel van die kaartjie aandui, het die fout op een van die bedieners in die verhoog voorgekom, en aangesien ek vol vertroue was dat vernis se roetering in die verhoog behoorlik werk, het ek aangeneem dat dit 'n geringe fout sou wees. So, net 'n boodskap wat in 'n reeds geslote uitsetstroom beland het. Ek vat 'n kaartjie vir myself, in volle vertroue dat ek dit binne minder as 30 minute gereed sal merk, klop myself op die skouer om die bord skoon te maak van die volgende rommel en kom terug na belangriker dinge.

Vas teen 'n muur teen 200 km/h

Maak 'n lêer oop varnishreload, op een van die bedieners wat Debian Stretch gebruik, het ek 'n dopskrif van minder as 200 reëls lank gesien.

Deur die skrip te hardloop, het ek niks gesien wat probleme kan veroorsaak toe ek dit verskeie kere direk vanaf die terminale hardloop nie.

Dit is immers 'n stadium, al breek dit, sal niemand kla nie, wel ... nie te veel nie. Ek hardloop die script en kyk wat sal uitgeskryf word na die terminale, maar die foute is nie meer sigbaar nie.

Nog 'n paar lopies om seker te maak dat ek nie die fout kan reproduseer sonder 'n bietjie ekstra moeite nie, en ek begin uitvind hoe om hierdie skrif te verander en dit steeds 'n fout te maak.

Kan die skrif STDOUT blokkeer (met > &-)? Of STDERR? Nie een het op die ou end gewerk nie.

Dit is duidelik dat die sisteem die loopomgewing op een of ander manier verander, maar hoe en hoekom?
Ek skakel vim aan en redigeer varnishreload, byvoeging set -x reg onder die shebang, met die hoop dat ontfouting van die uitvoer van die skrif 'n bietjie lig sal werp.

Die lêer is reggemaak, so ek herlaai vernis en sien dat die verandering alles heeltemal gebreek het ... Die uitlaat is 'n volledige gemors, met tonne C-agtige kode in. Selfs om in die terminale te blaai is nie genoeg om te vind waar dit begin nie. Ek is heeltemal deurmekaar. Kan ontfoutmodus die werk van programme wat in 'n skrip loop, beïnvloed? Nee, snert. Gogga in die dop? Verskeie moontlike scenario's vlieg soos kakkerlakke in my kop in verskillende rigtings. 'n Koppie kafeïen-vol drankie raak onmiddellik leeg, 'n vinnige reis na die kombuis vir 'n hervoorraad en ... kom ons gaan. Ek maak die skrif oop en bekyk die shebang van naderby: #!/bin/sh.

/bin/sh - dit is net 'n bash simlink, so die skrif word geïnterpreteer in POSIX-versoenbare modus, reg? Dit was nie daar nie! Die verstek dop op Debian is dash, wat presies is verwys /bin/sh.

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

Ter wille van beproewing het ek die shebang verander na #!/bin/bash, geskrap set -x en weer probeer. Ten slotte, met die daaropvolgende herlaai van vernis, het 'n aanvaarbare fout in die uitvoer verskyn:

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

Lyn 124, hier is dit!

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 }

Maar soos dit geblyk het, is lyn 124 taamlik leeg en van geen belang nie. Ek kon net aanneem dat die fout plaasgevind het as deel van 'n multilyn wat op lyn 116 begin het.
Wat word uiteindelik geskryf aan die veranderlike VCL_FILE as gevolg van die uitvoering van die bogenoemde sub-dop?

Aan die begin stuur dit die inhoud van die veranderlike VLC_SHOW, geskep op reël 115, na die volgende opdrag deur die pyp. En wat gebeur dan daar?

Eerstens, dit gebruik varnishadm, wat deel is van die vernisinstallasiepakket, om vernis op te stel sonder om te herbegin.

subopdrag vcl.show -v word gebruik om die hele VCL-konfigurasie wat in gespesifiseer word uit te voer ${VCL_NAME}, na STDOUT.

Om die tans aktiewe VCL-konfigurasie sowel as verskeie vorige weergawes van vernis se roeteringkonfigurasies wat nog in die geheue is te vertoon, kan jy die opdrag gebruik varnishadm vcl.list, waarvan die uitset soortgelyk aan die volgende sal wees:

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

Veranderlike waarde ${VCL_NAME} opgestel in 'n ander deel van die draaiboek varnishreload na die naam van die tans aktiewe VCL, indien enige. In hierdie geval sal dit "reload_20190101_120000_12397" wees.

Goed, veranderlik. ${VCL_SHOW} bevat die volledige konfigurasie vir vernis, tot dusver duidelik. Nou verstaan ​​ek uiteindelik hoekom streep uitset met set -x blyk so stukkend te wees - dit het die inhoud van die gevolglike konfigurasie ingesluit.

Dit is belangrik om te verstaan ​​dat 'n volledige VCL-konfigurasie dikwels uit verskeie lêers saamgevoeg kan word. C-styl opmerkings word gebruik om te definieer waar een konfigurasielêer in 'n ander ingesluit is, en dit is presies waaroor die volgende reël kodebrokkie gaan.
Die sintaksis vir opmerkings wat ingeslote lêers beskryf, het die volgende formaat:

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

Die nommers in hierdie konteks is nie belangrik nie, ons stel belang in die lêernaam.

So, wat gebeur in die moeras van opdragte wat op reël 116 begin?
Kom ons vind dit uit.
Die opdrag bestaan ​​uit vier dele:

  1. eenvoudige echo, wat die waarde van die veranderlike vertoon ${VCL_SHOW}
    echo "$VCL_SHOW"
  2. awk, wat 'n reël (rekord) soek, waar die eerste veld, nadat die teks verdeel is, "//" sal wees, en die tweede een sal wees "VCL.SHOW".
    Awk sal die eerste reël uitskryf wat by hierdie patrone pas en dan dadelik ophou verwerk.

    awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
  3. 'n Blok kode wat die veldwaardes in vyf veranderlikes stoor, geskei deur spasies. Die vyfde veranderlike FILE ontvang die res van die lyn. Laastens skryf die laaste eggo die inhoud van die veranderlike uit ${FILE}.
    { read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
  4. Aangesien alle stappe 1 tot 3 in 'n sub-dop ingesluit is, is die uitvoer van die waarde $FILE sal na 'n veranderlike geskryf word VCL_FILE.

Soos die kommentaar op reël 119 aandui, dien dit die uitsluitlike doel om sake betroubaar te hanteer waar die VCL sal verwys na lêers met spasiekarakters in hul name.

Ek het die oorspronklike verwerkingslogika vir kommentaar gelewer ${VCL_FILE} en het probeer om die volgorde van opdragte te verander, maar dit het tot niks gelei nie. Alles het vir my skoon gewerk, en in die geval van die begin van die diens, het dit 'n fout gegee.

Dit blyk dat die fout eenvoudig nie reproduseerbaar is wanneer die skrif met die hand uitgevoer word nie, terwyl die geskatte 30 minute reeds ses keer geëindig het en daarbenewens 'n hoër prioriteit taak verskyn het, wat die res van die sake opsy geskuif het. Die res van die week was gevul met 'n verskeidenheid take en is net effens verwater met 'n praatjie oor sed en 'n onderhoud met die kandidaat. Fout probleem in varnishreload onherstelbaar verlore in die sand van tyd.

Jou sogenaamde sed-fu... eintlik... gemors

Die volgende week het een redelik vry dag gehad, so ek het besluit om hierdie kaartjie weer op te neem. Ek het gehoop dat een of ander agtergrondproses al die tyd in my brein na 'n oplossing vir hierdie probleem gesoek het, en hierdie keer sal ek beslis verstaan ​​wat fout is.

Aangesien die laaste keer net die kode verander het nie gehelp het nie, het ek net besluit om dit te herskryf vanaf die 116de reël. In elk geval, die bestaande kode was dom. En dit is absoluut nie nodig om te gebruik nie read.

Kyk weer na die fout:
sh: echo: broken pipe - in hierdie opdrag is eggo op twee plekke, maar ek vermoed dat die eerste die meer waarskynlike skuldige is (wel, of ten minste 'n medepligtige). Awk wek ook nie selfvertroue nie. En as dit regtig is awk | {read; echo} die ontwerp lei tot al hierdie probleme, hoekom vervang dit nie? Hierdie eenreëlopdrag gebruik nie al die kenmerke van awk nie, en selfs hierdie ekstra read in aanhangsel.

Sedert verlede week was daar 'n berig oor sedEk wou my nuutverworwe vaardighede probeer en vereenvoudig echo | awk | { read; echo} in 'n meer verstaanbare echo | sed. Alhoewel dit beslis nie die beste benadering is om die fout te vang nie, het ek gedink ek sal ten minste my sed-fu probeer en dalk iets nuuts oor die probleem leer. Langs die pad het ek my kollega, die sed talk-skrywer, gevra om my te help om met 'n meer doeltreffende sed-skrif vorendag te kom.

Ek het die inhoud laat vaar varnishadm vcl.show -v "$VCL_NAME" na 'n lêer sodat ek kan fokus op die skryf van die sed script sonder enige van die moeite van diens herbegin.

'n Kort beskrywing van presies hoe sed insette hanteer, kan gevind word in sy GNU-handleiding. In die sed-bronne is die simbool n eksplisiet gespesifiseer as 'n lynskeier.

In verskeie passe, en met die advies van my kollega, het ons 'n sed-skrif geskryf wat dieselfde resultaat as die hele oorspronklike reël 116 gegee het.

Hieronder is 'n voorbeeldlêer met invoerdata:

> 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

Dit is dalk nie duidelik uit die beskrywing hierbo nie, maar ons stel net belang in die eerste opmerking // VCL.SHOW, en daar kan verskeie van hulle in die invoerdata wees. Dit is hoekom die oorspronklike awk eindig na die eerste wedstryd.

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

Die inhoud van die varnishreload script sal dus iets soos volg lyk:

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

Bogenoemde logika kan soos volg opgesom word:
As string ooreenstem met gewone uitdrukking // VCL.SHOW, verslind dan gulsig die teks wat beide nommers in daardie reël insluit, en stoor wat ook al oorbly na hierdie bewerking. Reik die gestoorde waarde uit en beëindig die program.

Eenvoudig, is dit nie?

Ons was tevrede met die sed-skrif en die feit dat dit al die oorspronklike kode vervang. Al my toetse het die gewenste resultate gegee, so ek het die "varnishreload" op die bediener verander en weer gehardloop systemctl reload varnish. Vieslike fout echo: write error: Broken pipe weer in ons gesig gelag. 'n Knipperende wyser het gewag vir 'n nuwe opdrag om in die donker leemte van die terminaal in te voer...

Bron: will.com

Voeg 'n opmerking