Қоян тесігінен құлау: бір лакпен қайта жүктеу қателігі туралы оқиға - 1 бөлім

Гостинушанка, өмірі осыған байланысты болғандай алдыңғы 20 минут бойы түймелерді қағып алғаннан кейін, көздерінде жартылай жабайы көрініспен және қу күлімсіреумен маған бұрылады - «Жігіт, мен түсіндім деп ойлаймын».

«Мына жерге қараңдар», - дейді ол экрандағы кейіпкерлердің біріне нұсқап, «Егер біз сізге жаңа ғана жібергенімді қоссақ, қызыл қалпақшама бәс тігемін» - кодтың басқа бөліміне нұсқау - «қате енді болмайды. шығарылады».

Біраз таң қалдым және шаршадым, мен біраз уақыт жұмыс істеп жатқан sed мәлімдемесін өзгертіп, файлды сақтап, іске қостым systemctl varnish reload. Қате туралы хабар жоғалып кетті...

«Үміткермен алмасатын электрондық хаттар», - деп жалғастырды әріптесім, оның күлкісі қуанышқа толы шынайы күлкіге айналды, - бұл кенеттен маған дәл осындай проблема болды!

Бәрі қалай басталды

Мақала bash, awk, sed және systemd қалай жұмыс істейтінін түсінуді болжайды. Лак туралы білім артықшылық береді, бірақ қажет емес.
Үзінділердегі уақыт белгілері өзгертілді.
-мен жазылған Гостинушанка.
Бұл мәтін екі апта бұрын ағылшын тілінде жарияланған түпнұсқаның аудармасы; аударма бойкоден.

Күздің тағы бір жылы таңында панорамалық терезелерден күн сәулесі түседі, бір кесе жаңа қайнатылған кофеинді сусын пернетақтаның бүйіріне қойылады, құлаққаптарда механикалық пернетақталардың сыбдырында дыбыстардың сүйікті симфониясы ойналады және экрандағы алғашқы жазба. Канбан тақтасындағы кешігу билеттерінің тізімі «Investigate varnishreload sh: echo: In/O error in staging» (Staging «varnishreload sh: echo: I/O қатесін» зерттеңіз) тағдырлы тақырыбымен ойнақы жарқырайды. Лакқа келетін болсақ, қателер жоқ және болуы мүмкін емес, тіпті егер олар осы жағдайдағыдай ешқандай қиындық тудырмаса да.

Танымайтындар үшін лакпен қайта жүктеу, бұл конфигурацияны қайта жүктеу үшін пайдаланылатын қарапайым қабық сценарийі лак - VCL деп те аталады.

Билеттің атауы айтып тұрғандай, қате сахнадағы серверлердің бірінде орын алды және мен сахнадағы лактың маршруттауы дұрыс жұмыс істеп тұрғанына сенімді болғандықтан, бұл кішігірім қателік болады деп ойладым. Сонымен, қазірдің өзінде жабық шығыс ағынына түскен хабарлама ғана. Мен билетті 30 минуттан аз уақыт ішінде дайын деп белгілеп, келесі қоқыстардан тазарту үшін өзімді иығымнан қағып, маңызды нәрселерге ораламын.

200 км/сағ жылдамдықпен қабырғаға соғылды

Файлды ашу varnishreload, Debian Stretch жұмыс істейтін серверлердің бірінде мен ұзындығы 200 жолдан аз қабық сценарийін көрдім.

Сценарий арқылы жұмыс істей отырып, мен оны тікелей терминалдан бірнеше рет іске қосқан кезде қиындық тудыратын ештеңе көрмедім.

Өйткені, бұл бір кезең, сынса да, ешкім шағымданбайды, жақсы... артық емес. Мен сценарийді іске қосып, терминалға не жазылатынын көремін, бірақ қателер енді көрінбейді.

Қосымша күш-жігерсіз қатені қайта жасай алмайтыныма көз жеткізу үшін тағы бірнеше жүгіреді, мен бұл сценарийді қалай өзгерту керектігін анықтай бастадым және оны әлі де қате жібереді.

Сценарий STDOUT ( > &-)? Немесе STDERR? Ешқайсысы соңында жұмыс істемеді.

Әлбетте, systemd іске қосу ортасын қандай да бір жолмен өзгертеді, бірақ қалай және неге?
Мен vim-ді қосып, өңдеймін varnishreload, қосу set -x тікелей shebang астында, сценарийдің шығуын жөндеу біраз жарық түсіреді деп үміттенеді.

Файл түзетілді, сондықтан мен лакпен қайта жүктеп жатырмын және бұл өзгеріс бәрін толығымен бұзғанын көрдім ... Шығарылған газ - бұл толық тәртіпсіздік, онда тонна C-тәрізді код бар. Терминалдың қай жерде басталатынын табу үшін тіпті оны айналдыру жеткіліксіз. Мен мүлдем абдырап қалдым. Түзету режимі сценарийде орындалатын бағдарламалардың жұмысына әсер ете ала ма? Жоқ, ақымақ. Қабықтағы қате? Менің басымда әртүрлі бағытта тарақандар сияқты бірнеше ықтимал сценарий ұшып жатыр. Кофеині бар бір кесе сусын бірден таусылады, ас үйге тез бару және... кеттік. Мен сценарийді ашып, шебанға жақынырақ қараймын: #!/bin/sh.

/bin/sh - бұл жай ғана bash символдық сілтеме, сондықтан сценарий POSIX-үйлесімді режимде түсіндіріледі, солай емес пе? Ол жерде болмады! Debian-дағы әдепкі қабық сызықша болып табылады, бұл дәл солай сілтеме жасайды /bin/sh.

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

Сынақ үшін мен шебанг деп өзгерттім #!/bin/bash, жойылды set -x және қайтадан тырысты. Соңында, лакпен кейінгі қайта жүктеу кезінде шығыста рұқсат етілген қате пайда болды:

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-жол, міне!

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 }

Бірақ белгілі болғандай, 124-жол өте бос және қызығушылық тудырмайды. Мен қате 116-жолдан басталатын көп жолдың бөлігі ретінде орын алды деп болжауға болады.
Ең соңында айнымалыға не жазылады VCL_FILE жоғарыда аталған ішкі қабықты орындау нәтижесінде?

Бастапқыда ол айнымалының мазмұнын жібереді VLC_SHOW, 115-жолда жасалған, құбыр арқылы келесі пәрменге. Ал сонда не болады?

Біріншіден, ол пайдаланады varnishadm, лак орнату пакетінің бөлігі болып табылады, қайта іске қоспай лак конфигурациялау үшін.

ішкі команда vcl.show -v көрсетілген бүкіл VCL конфигурациясын шығару үшін пайдаланылады ${VCL_NAME}, STDOUT үшін.

Ағымдағы белсенді VCL конфигурациясын, сондай-ақ жадта тұрған лактың маршруттау конфигурацияларының бірнеше алдыңғы нұсқаларын көрсету үшін пәрменді пайдалануға болады. varnishadm vcl.list, оның шығысы келесіге ұқсас болады:

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

Айнымалы мән ${VCL_NAME} сценарийдің басқа бөлігінде орнатыңыз varnishreload ағымдағы белсенді VCL атауына, егер бар болса. Бұл жағдайда ол “қайта жүктеу_20190101_120000_12397” болады.

Жарайды, айнымалы. ${VCL_SHOW} лак үшін толық конфигурацияны қамтиды, әзірге түсінікті. Енді мен неге сызықша шығаруды түсіндім set -x соншалықты сынған болып шықты - ол алынған конфигурацияның мазмұнын қамтиды.

Толық VCL конфигурациясын жиі бірнеше файлдардан біріктіруге болатындығын түсіну маңызды. C стиліндегі түсініктемелер бір конфигурация файлының басқа файлға қай жерде қосылғанын анықтау үшін пайдаланылады және код үзіндісінің келесі жолы дәл осыған қатысты.
Қосылған файлдарды сипаттайтын түсініктемелердің синтаксисі келесі пішімге ие:

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

Бұл контексттегі сандар маңызды емес, бізді файл атауы қызықтырады.

Сонымен, 116-жолдан басталатын командалар батпағында не болады?
Оны анықтайық.
Команда төрт бөліктен тұрады:

  1. Қарапайым echo, ол айнымалының мәнін көрсетеді ${VCL_SHOW}
    echo "$VCL_SHOW"
  2. awk, ол жолды (жазбаны) іздейді, мұнда мәтінді бөлгеннен кейін бірінші өріс «//», ал екіншісі «VCL.SHOW» болады.
    Awk осы үлгілерге сәйкес келетін бірінші жолды жазады, содан кейін өңдеуді дереу тоқтатады.

    awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
  3. Өріс мәндерін бос орындармен бөлінген бес айнымалыда сақтайтын код блогы. FILE бесінші айнымалысы жолдың қалған бөлігін алады. Соңында, соңғы жаңғырық айнымалының мазмұнын жазады ${FILE}.
    { read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
  4. 1-ден 3-ке дейінгі барлық қадамдар ішкі қабықшаға салынғандықтан, мәннің шығысы $FILE айнымалыға жазылады VCL_FILE.

119-жолдағы түсініктеме көрсеткендей, бұл VCL атауларында бос орын таңбалары бар файлдарға сілтеме жасайтын жағдайларды сенімді өңдеудің жалғыз мақсатына қызмет етеді.

Мен бастапқы өңдеу логикасын түсіндірдім ${VCL_FILE} және командалар ретін өзгертуге тырысты, бірақ бұл ештеңеге әкелмеді. Мен үшін бәрі таза жұмыс істеді және қызметті іске қосқан жағдайда қателік берді.

Сценарийді қолмен іске қосқан кезде қате қайталанбайтын сияқты, ал есептелген 30 минут алты рет аяқталды және сонымен қатар қалған істерді шетке ысыратын жоғарырақ тапсырма пайда болды. Аптаның қалған бөлігі әртүрлі тапсырмаларға толы болды және тек sed туралы әңгіме мен кандидатпен сұхбаттасумен аз ғана сұйылтылған. Қате мәселесі varnishreload уақыт құмында қайтарымсыз жоғалып кетті.

Сенің сед-фу деп аталатын... шын мәнінде... қоқыс

Келесі аптада өте бос күн болды, сондықтан мен бұл билетті қайта алуды шештім. Мен осы уақыт бойы миымда қандай да бір фондық процесс осы мәселенің шешімін іздеді деп үміттендім және бұл жолы мен не дұрыс емес екенін түсінемін.

Соңғы рет кодты өзгерту көмектеспегендіктен, мен оны 116-шы жолдан бастап қайта жазуды шештім. Қалай болғанда да, қолданыстағы код ақымақ болды. Және мүлдем пайдаланудың қажеті жоқ read.

Қатені қайта қарау:
sh: echo: broken pipe - бұл пәрменде жаңғырық екі жерде бар, бірақ мен біріншісі ықтимал кінәлі (жақсы немесе кем дегенде сыбайлас) деп күдіктенемін. Awk да сенімді шабыттандырмайды. Және бұл шынымен болған жағдайда awk | {read; echo} дизайн барлық осы мәселелерге әкеледі, неге оны ауыстырмасқа? Бұл бір жолды пәрмен awk барлық мүмкіндіктерін пайдаланбайды, тіпті бұл қосымша read қосымшада.

Өткен аптадан бері есеп берілді sedМен жаңадан алған дағдыларымды байқап, жеңілдеткім келді echo | awk | { read; echo} неғұрлым түсінікті echo | sed. Бұл қатені анықтаудың ең жақсы тәсілі болмаса да, мен кем дегенде сед-фуды қолданып көремін деп ойладым және мәселе туралы жаңа нәрсе білемін деп ойладым. Жолда мен әріптесім, sed talk-жазушыдан тиімдірек сценарий ойлап табуға көмектесуін өтіндім.

Мен мазмұнды тастадым varnishadm vcl.show -v "$VCL_NAME" файлға, сондықтан мен қызметті қайта іске қосу қиындықтарынсыз sed сценарийін жазуға назар аудара аламын.

sed енгізуді дәл қалай өңдейтінінің қысқаша сипаттамасын мына жерден табуға болады оның GNU нұсқаулығы. sed дереккөздерінде символ n сызық бөлгіш ретінде анық көрсетілген.

Бірнеше өтуде және әріптесімнің кеңесімен біз бүкіл түпнұсқа 116 жолымен бірдей нәтиже беретін sed сценарийін жаздық.

Төменде кіріс деректері бар үлгі файлы берілген:

> 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.SHOW, және кіріс деректерінде олардың бірнешеуі болуы мүмкін. Сондықтан бастапқы awk бірінші сәйкестіктен кейін аяқталады.

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

Осылайша, lakreload сценарийінің мазмұны келесідей болады:

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

Жоғарыда келтірілген логиканы келесідей қорытындылауға болады:
Жол тұрақты өрнекке сәйкес келсе // VCL.SHOW, содан кейін сол жолдағы екі санды қамтитын мәтінді ашкөздікпен жалмап, осы әрекеттен кейін қалғанын сақтаңыз. Сақталған мәнді шығарып, бағдарламаны аяқтаңыз.

Қарапайым, солай емес пе?

Біз sed сценарийіне және оның барлық бастапқы кодты алмастыратынына риза болдық. Менің барлық сынақтарым қажетті нәтижелерді берді, сондықтан мен сервердегі «лакты қайта жүктеуді» өзгертіп, қайтадан іске қостым. systemctl reload varnish. Жаман қате echo: write error: Broken pipe тағы да жүзімізге күлді. Терминалдың қараңғы бос жерінде жаңа пәрменнің енгізілуін көзді қысып тұрған курсор күтіп тұрды...

Ақпарат көзі: www.habr.com

пікір қалдыру