Jatuh di Lubang Arnab: Kisah Kegagalan Muat Semula Satu Varnish - Bahagian 1

ghostinushanka, setelah memalu butang selama 20 minit sebelum ini seolah-olah hidupnya bergantung padanya, menoleh kepada saya dengan pandangan separa liar di matanya dan senyuman licik - "Kawan, saya rasa saya dapat."

"Lihat di sini," katanya sambil menunjuk ke salah satu simbol pada skrin, "Saya yakin topi merah saya bahawa jika kami menambah di sini apa yang baru saya hantar kepada anda," sambil menunjuk ke bahagian kod yang lain, "ralat tidak akan lagi menjadi. akan dipaparkan."

Sedikit hairan dan letih, saya mengubah suai ungkapan sed yang telah kami usahakan untuk seketika, simpan fail dan jalankan systemctl varnish reload. Mesej ralat telah hilang...

"E-mel yang saya tukar dengan calon," rakan sekerja saya menyambung, sambil senyumannya berkembang menjadi senyuman kegembiraan yang tulen, "Tiba-tiba saya sedar bahawa ini adalah masalah yang sama!"

Bagaimana semuanya bermula

Artikel tersebut menganggap pemahaman tentang cara bash, awk, sed dan systemd berfungsi. Pengetahuan tentang varnis lebih disukai, tetapi tidak diperlukan.
Cap masa dalam coretan telah ditukar.
Ditulis dengan ghostinushanka.
Teks ini adalah terjemahan daripada asal yang diterbitkan dalam bahasa Inggeris dua minggu lalu; terjemahan boikoden.

Matahari bersinar melalui tingkap panorama pada satu lagi pagi musim luruh yang hangat, secawan minuman kaya kafein yang baru disediakan terletak jauh dari papan kekunci, simfoni bunyi kegemaran anda berbunyi dalam fon kepala anda, menenggelamkan gemerisik papan kekunci mekanikal dan entri pertama dalam senarai tiket tunggakan di papan Kanban secara main-main bersinar dengan tajuk "Siasat varnishreload" sh: echo: I/O error in staging" (Siasat "varnishreload sh: echo: I/O error" dalam pementasan). Apabila ia datang kepada varnis, terdapat dan tidak boleh ada ruang untuk kesilapan, walaupun ia tidak mengakibatkan sebarang masalah seperti dalam kes ini.

Bagi yang belum kenal varnishreload, ini ialah skrip shell mudah yang digunakan untuk memuatkan semula konfigurasi varnis - juga dipanggil VCL.

Seperti yang dicadangkan oleh tajuk tiket, ralat berlaku pada salah satu pelayan di atas pentas, dan kerana saya yakin bahawa penghalaan varnis di atas pentas berfungsi dengan baik, saya menganggap bahawa ini adalah ralat kecil. Jadi, hanya mesej yang berakhir dalam aliran keluaran yang sudah tertutup. Saya mengambil tiket itu untuk diri saya sendiri, dengan penuh keyakinan bahawa saya akan menandakannya siap dalam masa kurang daripada 30 minit, menepuk belakang diri saya untuk membersihkan papan dari satu lagi sampah dan kembali kepada perkara yang lebih penting.

Rempuh dinding pada kelajuan 200 km/j

Membuka fail varnishreload, pada salah satu pelayan yang menjalankan Debian Stretch, saya melihat skrip shell kurang daripada 200 baris panjang.

Setelah melalui skrip, saya tidak perasan apa-apa yang boleh mengakibatkan masalah apabila menjalankannya beberapa kali terus dari terminal.

Lagipun ni tahap, kalau pecah pun takde orang merungut, yelah..tak keterlaluan. Saya menjalankan skrip dan melihat apa yang akan ditulis ke terminal, tetapi ralat tidak lagi kelihatan.

Beberapa lagi larian untuk memastikan bahawa saya tidak dapat menghasilkan semula ralat tanpa sebarang usaha tambahan, dan saya mula memikirkan cara untuk menukar skrip ini dan menjadikannya masih membuang ralat.

Bolehkah skrip mengatasi STDOUT (menggunakan > &-)? Atau STDERR? Kedua-duanya tidak berjaya pada akhirnya.

Nampaknya systemd entah bagaimana mengubah suai persekitaran permulaan, tetapi bagaimana, dan mengapa?
Saya membuka vim dan mengedit varnishreload, menambah set -x betul-betul di bawah shebang, dengan harapan bahawa output nyahpepijat skrip akan memberi sedikit pencerahan.

Fail itu diperbetulkan, jadi saya memuatkan semula varnis dan melihat bahawa perubahan itu benar-benar memecahkan segala-galanya... Ekzos adalah kucar-kacir lengkap, di mana terdapat banyak kod seperti C. Malah menatal dalam terminal tidak mencukupi untuk mencari di mana ia bermula. Saya benar-benar keliru. Bolehkah mod nyahpepijat menjejaskan operasi program yang dilancarkan dalam skrip? Tidak, ia mengarut. Pepijat dalam cangkerang? Beberapa kemungkinan senario sedang berlumba-lumba di kepala saya seperti lipas dalam arah yang berbeza. Cawan minuman berkafein dikosongkan serta-merta, pergi ke dapur untuk mengisi semula stok dan... kita pergi. Saya membuka skrip dan melihat lebih dekat pada shebang: #!/bin/sh.

/bin/sh - ini hanyalah symlink ke bash, jadi skrip ditafsirkan dalam mod serasi POSIX, bukan? Tidak begitu! Cangkang lalai pada Debian ialah dash, dan itulah rupanya. merujuk /bin/sh.

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

Sebagai ujian, saya menukar shebang kepada #!/bin/bash, dipadamkan set -x dan mencuba lagi. Akhirnya, selepas but semula varnis berikutnya, ralat yang boleh diterima muncul dalam output:

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

Baris 124, ini dia!

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 }

Tetapi ternyata, baris 124 agak kosong dan tidak menarik. Saya hanya boleh menganggap bahawa ralat berlaku sebagai sebahagian daripada rentetan berbilang baris bermula pada baris 116.
Apakah yang akhirnya ditulis kepada pembolehubah? VCL_FILE sebagai hasil daripada melaksanakan sub-kulit di atas?

Pada mulanya, ia menghantar kandungan pembolehubah VLC_SHOW, dibuat pada baris 115, mengikut arahan melalui paip. Dan kemudian apa yang berlaku di sana?

Pertama, ia digunakan di sana varnishadm, yang merupakan sebahagian daripada pakej pemasangan varnis, untuk menyediakan varnis tanpa dimulakan semula.

Sub-pasukan vcl.show -v digunakan untuk mengeluarkan keseluruhan konfigurasi VCL yang dinyatakan dalam ${VCL_NAME}, kepada STDOUT.

Untuk memaparkan konfigurasi VCL aktif semasa, serta beberapa versi sebelumnya konfigurasi penghalaan varnis yang masih dalam ingatan, anda boleh menggunakan arahan varnishadm vcl.list, outputnya akan serupa dengan yang di bawah:

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

Nilai boleh ubah ${VCL_NAME} dipasang di bahagian lain skrip varnishreload kepada nama VCL yang sedang aktif, jika ada. Dalam kes ini ia akan menjadi "muat semula_20190101_120000_12397".

Hebat, berubah-ubah ${VCL_SHOW} mengandungi konfigurasi lengkap untuk varnis, jelas buat masa ini. Sekarang saya akhirnya faham mengapa output dash adalah set -x ternyata sangat rosak - ia termasuk kandungan konfigurasi yang dihasilkan.

Adalah penting untuk memahami bahawa konfigurasi VCL yang lengkap selalunya boleh disatukan daripada beberapa fail. Komen gaya C digunakan untuk mengenal pasti di mana fail konfigurasi tertentu telah disertakan dalam yang lain, dan itulah maksud baris coretan kod berikut.
Sintaks untuk ulasan yang menerangkan fail yang disertakan adalah dalam format berikut:

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

Nombor tidak penting dalam konteks ini, kami berminat dengan nama fail.

Apa yang akhirnya berlaku dalam paya arahan bermula pada baris 116?
Katakanlah.
Pasukan ini terdiri daripada empat bahagian:

  1. Mudah echo, yang mencetak nilai pembolehubah ${VCL_SHOW}
    echo "$VCL_SHOW"
  2. awk, yang mencari baris (rekod) di mana medan pertama, selepas memecahkan teks, ialah “//”, dan yang kedua ialah “VCL.SHOW”.
    Awk akan menulis baris pertama yang sepadan dengan corak ini dan kemudian menghentikan pemprosesan serta-merta.

    awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}'
  3. Blok kod yang menyimpan nilai medan kepada lima pembolehubah, dipisahkan oleh ruang. Pembolehubah FILE kelima menerima baris yang lain. Akhirnya, gema terakhir menulis kandungan pembolehubah ${FILE}.
    { read -r DELIM VCL_SHOW INDEX SIZE FILE; echo "$FILE" }
  4. Oleh kerana semua langkah 1 hingga 3 disertakan dalam subkulit, mengeluarkan nilai $FILE akan ditulis kepada pembolehubah VCL_FILE.

Seperti yang dicadangkan oleh ulasan pada baris 119, ini memenuhi tujuan tunggal untuk mengendalikan kes yang boleh dipercayai di mana VCL akan merujuk fail dengan ruang dalam nama mereka.

Saya telah mengulas logik pemprosesan asal untuk ${VCL_FILE} dan cuba menukar urutan arahan, tetapi ia tidak membawa kepada apa-apa. Semuanya berfungsi dengan baik untuk saya, tetapi apabila saya memulakan perkhidmatan ia memberikan ralat.

Nampaknya ralat itu tidak boleh dibuat semula apabila menjalankan skrip secara manual, manakala 30 minit yang sepatutnya telah tamat enam kali dan, sebagai tambahan, tugas keutamaan yang lebih tinggi telah muncul, menolak perkara lain. Selebihnya minggu ini diisi dengan pelbagai tugas dan hanya sedikit dicairkan oleh laporan sed dan temuduga dengan calon. Masalah dengan ralat dalam varnishreload hilang tidak dapat dipulihkan dalam pasir masa.

Apa yang dipanggil sed-fu anda... sebenarnya... sampah

Minggu berikutnya saya mempunyai satu hari yang agak lapang, jadi saya memutuskan untuk menangani tiket ini sekali lagi. Saya berharap bahawa dalam otak saya, beberapa proses latar belakang telah mencari penyelesaian untuk masalah ini selama ini, dan kali ini saya pasti akan memahami apa yang sedang berlaku.

Memandangkan hanya menukar kod itu tidak membantu kali terakhir, saya hanya memutuskan untuk menulis semula bermula dari baris 116. Walau apa pun, kod sedia ada adalah bodoh. Dan sama sekali tidak perlu menggunakannya read.

Melihat kesilapan sekali lagi:
sh: echo: broken pipe — gema muncul di dua tempat dalam arahan ini, tetapi saya mengesyaki bahawa yang pertama adalah penyebab yang lebih berkemungkinan (atau sekurang-kurangnya rakan sejenayah). Awk juga tidak menimbulkan keyakinan. Dan sekiranya ia benar-benar berlaku awk | {read; echo} reka bentuk membawa kepada semua masalah ini, mengapa tidak menggantikannya? Perintah satu baris ini tidak menggunakan semua ciri awk, malah yang tambahan ini read sebagai tambahan.

Sejak minggu lepas ada laporan mengenai sed, saya ingin mencuba kemahiran yang baru saya perolehi dan memudahkan echo | awk | { read; echo} menjadi lebih mudah difahami echo | sed. Walaupun ini bukan pendekatan terbaik untuk mengenal pasti pepijat, saya fikir saya sekurang-kurangnya akan mencuba sed-fu saya dan mungkin mempelajari sesuatu yang baharu tentang masalah itu. Sepanjang perjalanan, saya meminta rakan sekerja saya, pengarang ceramah sed, untuk membantu saya menghasilkan skrip sed yang lebih cekap.

Saya menjatuhkan kandungannya varnishadm vcl.show -v "$VCL_NAME" ke fail, jadi saya boleh menumpukan pada menulis skrip sed tanpa perlu reboot perkhidmatan.

Penerangan ringkas tentang cara sed memproses input boleh didapati dalam manual GNU beliau. Dalam sumber sed simbol n dinyatakan secara eksplisit sebagai pemisah garis.

Dalam beberapa pas dan dengan cadangan rakan sekerja saya, kami menulis skrip sed yang memberikan hasil yang sama seperti keseluruhan baris asal 116.

Di bawah ialah contoh fail dengan data input:

> 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

Ini mungkin tidak jelas daripada penerangan di atas, tetapi kami hanya berminat dengan ulasan pertama // VCL.SHOW, dan mungkin terdapat beberapa daripadanya dalam data input. Inilah sebabnya mengapa awk asal berakhir selepas perlawanan pertama.

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

Jadi, kandungan skrip varnishreload akan kelihatan seperti ini:

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

Logik di atas boleh dinyatakan secara ringkas seperti berikut:
Jika rentetan itu sepadan dengan ungkapan biasa // VCL.SHOW, kemudian dengan rakus memakan teks yang merangkumi kedua-dua nombor dalam baris ini dan simpan semua yang tinggal selepas operasi ini. Pancarkan nilai yang disimpan dan tamatkan program.

Mudah, bukan?

Kami gembira dengan skrip sed dan fakta bahawa ia menggantikan semua kod asal. Semua ujian saya memberikan hasil yang diingini, jadi saya menukar "varnishreload" pada pelayan dan menjalankannya semula systemctl reload varnish. Kesilapan teruk echo: write error: Broken pipe ketawa di muka kami lagi. Kursor mengenyit sedang menunggu arahan baharu untuk dimasukkan dalam kekosongan gelap terminal...

Sumber: www.habr.com

Tambah komen