Quyon teshigidan pastga tushish: bitta lakni qayta ishga tushirishda xatolik hikoyasi - 1-qism

ghostinushanka20 daqiqa davomida tugmalarni bolg'alab, go'yo hayoti bunga bog'liq bo'lib, ko'zlarida yarim vahshiy nigoh va ayyor jilmayish bilan menga o'girildi - "Do'stim, men tushundim, deb o'ylayman."

"Mana bu yerga qarang," deydi u ekrandagi belgilardan birini ko'rsatib, "Men qizil shlyapamga tikib qo'yamanki, agar men sizga yuborgan narsamni qo'shsak," kodning boshqa bo'limiga ishora qilib, "xato endi bo'lmaydi. ko'rsatiladi."

Bir oz hayron va charchadim, men bir muncha vaqt ishlagan sed ifodasini o'zgartiraman, faylni saqlang va ishga tushiraman systemctl varnish reload. Xato xabari yo'qoldi...

"Nomzod bilan almashgan elektron pochta xabarlari," deb davom etdi hamkasbim, uning tabassumi samimiy quvonch tabassumiga aylanganda, "Birdan menga bu xuddi shu muammo ekanligini anglab etdi!"

Hammasi qanday boshlandi

Maqolada bash, awk, sed va systemd qanday ishlashi haqida tushuncha berilgan. Lakni bilish afzalroq, lekin shart emas.
Snippetlardagi vaqt belgilari o'zgartirildi.
bilan yozilgan ghostinushanka.
Ushbu matn ikki hafta oldin ingliz tilida nashr etilgan asl nusxaning tarjimasi; tarjima boykoden.

Kuzning yana bir iliq tongida quyosh panoramali derazalar orqali porlaydi, bir chashka yangi tayyorlangan kofeinga boy ichimlik klaviaturadan uzoqlashadi, sizning sevimli tovush simfoniyangiz naushniklarda yangraydi, mexanik klaviaturalarning shitirlashini bosadi va birinchi yozuv. Kanban taxtasidagi qoldirilgan chiptalar roʻyxatida “Varnishreloadni tekshirish” sh: echo: sahnalashtirishda kiritish-chiqarish xatosi” (Investigate “varnishreload sh: echo: I/O error” in staging) taqdirli sarlavhasi bilan oʻynoqi yonib turadi. Lak haqida gap ketganda, xatolar uchun hech qanday joy yo'q va bo'lishi mumkin emas, hatto ular bu holatda bo'lgani kabi hech qanday muammoga olib kelmasa ham.

Tanish bo'lmaganlar uchun lakni qayta yuklash, bu konfiguratsiyani qayta yuklash uchun ishlatiladigan oddiy qobiq skriptidir lak - VCL deb ham ataladi.

Chipta nomidan ko'rinib turibdiki, xato sahnadagi serverlardan birida yuz berdi va men sahnadagi lak marshruti to'g'ri ishlayotganiga amin bo'lganim uchun, bu kichik xato bo'ladi deb o'yladim. Shunday qilib, allaqachon yopiq chiqish oqimida tugagan xabar. Men chiptani 30 daqiqadan kamroq vaqt ichida tayyor bo'lishini belgilab qo'yishimga to'liq ishonch bilan o'zim uchun olaman, taxtani yana bir axlatdan tozalab, yana muhimroq ishlarga qaytaman.

200 km/soat tezlikda devorga urildi

Faylni ochish varnishreload, Debian Stretch bilan ishlaydigan serverlardan birida men 200 satrdan kam uzunlikdagi qobiq skriptini ko'rdim.

Skriptni ko'rib chiqqach, men uni bir necha marta to'g'ridan-to'g'ri terminaldan ishga tushirishda muammolarga olib keladigan hech narsani sezmadim.

Axir bu sahna, sinsa ham, hech kim nolimaydi, mayli... ortiqcha emas. Men skriptni ishga tushiraman va terminalga nima yozilishini ko'raman, lekin xatolar endi ko'rinmaydi.

Hech qanday qo'shimcha harakatlarsiz xatoni takrorlay olmasligimga ishonch hosil qilish uchun yana bir nechta yugurib chiqdim va men ushbu skriptni qanday o'zgartirishni va uni xatoga yo'l qo'ymaslikni aniqlay boshladim.

Skript STDOUTni bekor qila oladimi ( > &-)? Yoki STDERRmi? Bularning hech biri oxir-oqibat ishlamadi.

Ko'rinishidan, systemd qandaydir tarzda ishga tushirish muhitini o'zgartiradi, lekin qanday qilib va ​​nima uchun?
Men vim-ni ochaman va tahrir qilaman varnishreload, qo'shish set -x o'ng shebang ostida, skript disk raskadrovka chiqishi biroz yorug'lik beradi, deb umid.

Fayl tuzatildi, shuning uchun men lakni qayta yuklayman va o'zgarish hamma narsani butunlay sindirib tashlaganini ko'raman ... Egzoz to'liq tartibsizlik bo'lib, unda tonna C-ga o'xshash kod mavjud. Hatto terminalda aylanib o'tish ham uning qaerdan boshlanganini topish uchun etarli emas. Men butunlay sarosimaga tushdim. Nosozliklarni tuzatish rejimi skriptda ishga tushirilgan dasturlarning ishlashiga ta'sir qilishi mumkinmi? Yo'q, bu bema'nilik. Qobiqdagi xato? Bir nechta mumkin bo'lgan stsenariylar boshimdan turli yo'nalishlarda hamamböceği kabi yugurmoqda. Kofeinli ichimlikning kubogi bir zumda bo'shatiladi, zaxirani to'ldirish uchun oshxonaga tez sayohat va ... biz ketamiz. Men skriptni ochaman va shebangga diqqat bilan qarayman: #!/bin/sh.

/bin/sh - bu shunchaki bash uchun simli havola, shuning uchun skript POSIX-mos keladigan rejimda talqin qilinadi, to'g'rimi? Unday emas! Debian-dagi standart qobiq chiziqchadir va u xuddi shunday ko'rinadi. ishora qiladi /bin/sh.

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

Sinov sifatida men shebangni o'zgartirdim #!/bin/bash, oʻchirildi set -x va yana urinib ko'ring. Nihoyat, lakni keyinchalik qayta ishga tushirgandan so'ng, chiqishda ruxsat etilgan xatolik paydo bo'ldi:

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-qator, mana!

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 }

Ammo ma'lum bo'lishicha, 124-qator juda bo'sh va qiziq emas. Men xato 116-qatordan boshlangan ko'p qatorli satrning bir qismi sifatida sodir bo'lgan deb taxmin qilishim mumkin edi.
O'zgaruvchiga oxir-oqibat nima yoziladi? VCL_FILE yuqoridagi pastki qobiqni bajarish natijasida?

Boshida u o'zgaruvchining mazmunini yuboradi VLC_SHOW, quvur orqali buyruqni bajarib, 115-qatorda yaratilgan. Va keyin u erda nima bo'ladi?

Birinchidan, u erda ishlatiladi varnishadmlakni o'rnatish paketining bir qismi bo'lgan lakni qayta ishga tushirmasdan o'rnatish uchun.

Quyi jamoa vcl.show -v da ko'rsatilgan butun VCL konfiguratsiyasini chiqarish uchun ishlatiladi ${VCL_NAME}, STDOUT uchun.

Joriy faol VCL konfiguratsiyasini, shuningdek, hali ham xotirada bo'lgan lak marshrutlash konfiguratsiyasining bir nechta oldingi versiyalarini ko'rsatish uchun siz buyruqdan foydalanishingiz mumkin. varnishadm vcl.list, chiqishi quyidagiga o'xshash bo'ladi:

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

O'zgaruvchan qiymat ${VCL_NAME} skriptning boshqa qismida o'rnatilgan varnishreload hozirda faol VCL nomiga, agar mavjud bo'lsa. Bu holda "reload_20190101_120000_12397" bo'ladi.

Ajoyib, o'zgaruvchan ${VCL_SHOW} lak uchun to'liq konfiguratsiyani o'z ichiga oladi, hozircha aniq. Endi men nihoyat tire chiqishi nima uchun ekanligini tushundim set -x shunchalik buzilgan bo'lib chiqdi - u hosil bo'lgan konfiguratsiyaning mazmunini o'z ichiga oladi.

To'liq VCL konfiguratsiyasi ko'pincha bir nechta fayllardan birlashtirilishi mumkinligini tushunish muhimdir. C uslubidagi sharhlar ma'lum konfiguratsiya fayllari boshqalarga qaerga kiritilganligini aniqlash uchun ishlatiladi va quyidagi kod parchasi aynan shu narsa haqida.
Kiritilgan fayllarni tavsiflovchi sharhlar sintaksisi quyidagi formatda:

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

Raqamlar bu kontekstda muhim emas, bizni fayl nomi qiziqtiradi.

116-qatordan boshlanadigan buyruqlar botqog'ida oxir-oqibat nima sodir bo'ladi?
Keling, ko'rib chiqaylik.
Jamoa to'rt qismdan iborat:

  1. Oddiy echo, bu o'zgaruvchining qiymatini chop etadi ${VCL_SHOW}
    echo "$VCL_SHOW"
  2. awk, matnni buzgandan so'ng birinchi maydon "//", ikkinchisi esa "VCL.SHOW" bo'lgan satrni (yozuvni) qidiradi.
    Awk ushbu naqshlarga mos keladigan birinchi qatorni yozadi va keyin darhol ishlov berishni to'xtatadi.

    awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
  3. Maydon qiymatlarini bo'shliqlar bilan ajratilgan beshta o'zgaruvchiga saqlaydigan kod bloki. Beshinchi FILE o'zgaruvchisi qatorning qolgan qismini oladi. Nihoyat, oxirgi echo o'zgaruvchining mazmunini yozadi ${FILE}.
    { read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
  4. 1 dan 3 gacha bo'lgan barcha bosqichlar pastki qavatga kiritilganligi sababli, qiymat chiqariladi $FILE o'zgaruvchiga yoziladi VCL_FILE.

119-qatordagi sharhdan ko'rinib turibdiki, bu VCL o'z nomlarida bo'sh joylar bo'lgan fayllarga havola qiladigan holatlarni ishonchli hal qilishning yagona maqsadiga xizmat qiladi.

Men asl ishlov berish mantiqini izohladim ${VCL_FILE} va buyruqlar ketma-ketligini o'zgartirishga harakat qildi, lekin bu hech narsaga olib kelmadi. Men uchun hamma narsa yaxshi ishladi, lekin xizmatni boshlaganimda xatolik yuz berdi.

Ko'rinishidan, skriptni qo'lda ishga tushirishda xatolik shunchaki takrorlanmaydi, taxmin qilingan 30 daqiqa allaqachon olti marta tugagan va bundan tashqari, boshqa masalalarni chetga surib qo'ygan yuqoriroq vazifa paydo bo'lgan. Haftaning qolgan qismi turli xil vazifalar bilan to'ldirildi va faqat sed bo'yicha hisobot va nomzod bilan suhbat orqali biroz suyultirildi. Xato bilan bog'liq muammo varnishreload vaqt qumida qaytarib bo'lmaydigan darajada yo'qolgan edi.

Sening sed-fu deganingiz... aslida... axlat

Keyingi hafta menda juda bo'sh kun bor edi, shuning uchun men bu chiptani yana bir bor hal qilishga qaror qildim. Men miyamda qandaydir fon jarayonlari shu vaqt davomida bu muammoga yechim izlagan deb umid qilgandim va bu safar nima bo'layotganini aniq tushunaman.

Kodni oddiygina o'zgartirish oxirgi marta yordam bermagani uchun men uni 116-qatordan boshlab qayta yozishga qaror qildim. Har holda, mavjud kod ahmoq edi. Va uni ishlatishning mutlaqo hojati yo'q read.

Xatoga yana bir bor qarab:
sh: echo: broken pipe — aks-sado ushbu buyruqning ikkita joyida paydo bo'ladi, lekin men birinchisi aybdor (yoki hech bo'lmaganda sherik) deb o'ylayman. Awk ham ishonchni ilhomlantirmaydi. Va agar haqiqatan ham shunday bo'lsa awk | {read; echo} dizayn bu muammolarning barchasiga olib keladi, nega uni almashtirmaslik kerak? Ushbu bir qatorli buyruq awk ning barcha xususiyatlaridan foydalanmaydi va hatto bu qo'shimcha read bunga qo'chimcha.

O'tgan haftadan beri hisobot bor edi sed, Men yangi olingan ko'nikmalarimni sinab ko'rmoqchi edim va soddalashtirdim echo | awk | { read; echo} yanada tushunarli holga keltiring echo | sed. Bu, albatta, xatoni aniqlashning eng yaxshi usuli bo'lmasa-da, men hech bo'lmaganda sed-funi sinab ko'raman va muammo haqida yangi narsalarni bilib olaman deb o'yladim. Yo‘l-yo‘lakay sed nutqi muallifi hamkasbimdan samaraliroq sed skriptini ishlab chiqishda yordam berishini so‘radim.

Men tarkibni tashladim varnishadm vcl.show -v "$VCL_NAME" faylga, shuning uchun men xizmatni qayta ishga tushirishda hech qanday qiyinchiliksiz sed skriptini yozishga e'tibor qaratishim mumkin edi.

Sed ning kiritish jarayoni qanday amalga oshirilishi haqida qisqacha tavsif uning GNU qo'llanmasi. Sed manbalarida belgi n chiziq ajratuvchi sifatida aniq belgilangan.

Bir nechta o'tishlarda va mening hamkasbimning tavsiyalari bilan biz sed skriptini yozdik, u butun asl 116-qator bilan bir xil natija berdi.

Quyida kiritilgan ma'lumotlarga ega namuna fayli mavjud:

> 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 yuqoridagi tavsifdan aniq bo'lmasligi mumkin, ammo bizni faqat birinchi sharh qiziqtiradi // VCL.SHOW, va kirish ma'lumotlarida ulardan bir nechtasi bo'lishi mumkin. Shuning uchun asl awk birinchi o'yindan keyin tugaydi.

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

Shunday qilib, lakni qayta yuklash skriptining mazmuni quyidagicha ko'rinadi:

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

Yuqoridagi mantiqni qisqacha quyidagicha ifodalash mumkin:
Agar satr muntazam ifodaga mos kelsa // VCL.SHOW, keyin bu satrdagi ikkala raqamni o'z ichiga olgan matnni ochko'zlik bilan yutib yuboring va ushbu operatsiyadan keyin qolgan hamma narsani saqlang. Saqlangan qiymatni chiqaring va dasturni tugating.

Oddiy, shunday emasmi?

Biz sed skriptidan va u barcha asl kodni almashtirganidan mamnun edik. Mening barcha testlarim kerakli natijalarni berdi, shuning uchun men serverdagi "lakni qayta yuklash" ni o'zgartirdim va uni qayta ishga tushirdim. systemctl reload varnish. Yomon xato echo: write error: Broken pipe yana yuzimizga kuldi. Ko‘z qisib turgan kursor terminalning qorong‘u bo‘shlig‘ida yangi buyruq kiritilishini kutayotgan edi...

Manba: www.habr.com

a Izoh qo'shish