السقوط في حفرة الأرنب: قصة خطأ إعادة تشغيل الورنيش - الجزء الأول

Ghostinushanka، بعد الضرب على الأزرار في العشرين دقيقة الماضية كما لو أن حياته تعتمد عليها ، استدار إلي بتعبير شبه جامح في عينيه وابتسامة خبيثة - "يا صاح ، أعتقد أنني أفهم."

"انظر هنا" ، كما قال ، مشيرًا إلى أحد الرموز على الشاشة ، "أراهن أن قبعتي الحمراء ، إذا أضفنا ما أرسلته إليك هنا للتو" ، مشيرًا إلى قسم آخر من الكود ، "لن يتم عرض الخطأ مرة أخرى".

مرتبكًا ومتعبًا بعض الشيء ، قمت بتغيير عبارة sed التي كنا نعمل عليها لفترة من الوقت ، وحفظ الملف ، وتشغيل systemctl varnish reload. اختفت رسالة الخطأ ...

تابع زميلي قائلاً: "رسائل البريد الإلكتروني التي تبادلتها مع المرشح" ، بينما تتحول ابتسامته المتكلفة إلى ابتسامة حقيقية مليئة بالبهجة ، "اتضح لي فجأة أن هذه هي المشكلة نفسها تمامًا!"

كيف جميعا لم تبدأ

تفترض المقالة فهمًا لكيفية عمل bash و awk و sed و systemd. يفضل معرفة الورنيش ولكنها غير مطلوبة.
تم تغيير الطوابع الزمنية في القصاصات.
مكتوب مع Ghostinushanka.
هذا النص هو ترجمة للنص الأصلي الذي نُشر باللغة الإنجليزية قبل أسبوعين ؛ ترجمة بويكودن.

تشرق الشمس من خلال النوافذ البانورامية في صباح خريفي دافئ آخر ، ويبقى فنجان من مشروب يحتوي على الكافيين طازجًا بعيدًا عن لوحة المفاتيح ، ويتم تشغيل سمفونية صوتية مفضلة على حفيف لوحات المفاتيح الميكانيكية في سماعات الرأس ، ويضيء الإدخال الأول في قائمة التذاكر المتراكمة على لوحة كانبان بشكل مرح مع العنوان المشؤوم "Investigate varnishreload sh:" المرحلة). عندما يتعلق الأمر بالورنيش ، فلا توجد أخطاء ولا يمكن أن يكون هناك أي أخطاء ، حتى لو لم ينتج عنها أي مشاكل ، كما في هذه الحالة.

بالنسبة لأولئك الذين ليسوا على دراية الورنيش، هذا هو برنامج نصي بسيط يستخدم لإعادة تحميل التكوين الورنيش - تسمى أيضًا VCL.

كما يوحي عنوان التذكرة ، حدث الخطأ على أحد الخوادم في المرحلة ، وبما أنني كنت واثقًا من أن توجيه الورنيش في المرحلة يعمل بشكل صحيح ، فقد افترضت أن هذا سيكون خطأ بسيطًا. لذلك ، مجرد رسالة وصلت إلى تدفق إخراج مغلق بالفعل. أحصل على تذكرة لنفسي ، وأنا على ثقة تامة من أنني سأضعها على استعداد في أقل من 30 دقيقة ، وأربت على كتفي لتخليص السبورة من القمامة التالية وأعود إلى أشياء أكثر أهمية.

الاصطدام بجدار بسرعة 200 كم / ساعة

فتح ملف varnishreload، على أحد الخوادم التي تقوم بتشغيل Debian Stretch ، رأيت نصًا شيلًا يقل طوله عن 200 سطر.

من خلال تشغيل البرنامج النصي ، لم أر أي شيء يمكن أن يسبب مشاكل عند تشغيله عدة مرات مباشرة من الجهاز.

بعد كل شيء ، هذه مرحلة ، حتى لو تعطلت ، فلن يشتكي أحد ، حسنًا ... ليس كثيرًا. أقوم بتشغيل البرنامج النصي وأرى ما سيتم كتابته على الجهاز ، لكن الأخطاء لم تعد مرئية.

يتم تشغيل زوجين آخرين للتأكد من أنني لا أستطيع إعادة إنتاج الخطأ دون بذل بعض الجهد الإضافي ، وبدأت في اكتشاف كيفية تغيير هذا البرنامج النصي وجعله لا يزال يلقي بخطأ.

يمكن للبرنامج النصي منع STDOUT (باستخدام > &-)؟ أو ستدير؟ لا يعمل في النهاية.

من الواضح أن systemd يغير بيئة التشغيل بطريقة ما ، ولكن كيف ولماذا؟
أقوم بتشغيل vim وتحريره varnishreloadمضيفا set -x مباشرة تحت shebang ، على أمل أن يؤدي تصحيح أخطاء إخراج النص إلى إلقاء بعض الضوء.

تم إصلاح الملف ، لذلك أعيد تحميل الورنيش وأرى أن التغيير كسر كل شيء تمامًا ... العادم عبارة عن فوضى كاملة ، مع وجود الكثير من الشفرات الشبيهة بـ C بداخله. حتى التمرير في الجهاز لا يكفي للعثور على المكان الذي يبدأ منه. أنا في حيرة من أمري. هل يمكن أن يؤثر وضع التصحيح على عمل البرامج التي يتم تشغيلها في البرنامج النصي؟ لا هراء. علة في القشرة؟ عدة سيناريوهات محتملة تطير في رأسي مثل الصراصير في اتجاهات مختلفة. كوب من المشروب المليء بالكافيين يفرغ على الفور ، ورحلة سريعة إلى المطبخ لإعادة الإمداد و ... دعنا نذهب. أفتح النص وألقي نظرة فاحصة على shebang: #!/bin/sh.

/bin/sh - هذا مجرد ارتباط رمزي bash ، لذلك يتم تفسير النص في الوضع المتوافق مع POSIX ، أليس كذلك؟ لم يكن هناك! الصدفة الافتراضية في دبيان هي dash ، وهي بالضبط ما تذكر /bin/sh.

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

من أجل المحاكمة ، قمت بتغيير shebang إلى #!/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 النشط حاليًا ، إن وجد. في هذه الحالة سيكون "reload_20190101_120000_12397".

حسنًا ، متغير. ${VCL_SHOW} يحتوي على التكوين الكامل للورنيش ، واضح حتى الآن. الآن فهمت أخيرًا لماذا شرطة الإخراج مع set -x تبين أنها معطلة للغاية - تضمنت محتويات التكوين الناتج.

من المهم أن نفهم أن تكوين VCL الكامل يمكن غالبًا تجميعه معًا من ملفات متعددة. تُستخدم تعليقات C-style لتحديد مكان تضمين ملف تكوين في ملف آخر ، وهذا هو بالضبط ما يدور حوله السطر التالي من مقتطف الشفرة.
صيغة التعليقات التي تصف الملفات المضمنة لها التنسيق التالي:

// 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} وحاولوا تغيير تسلسل الأوامر ، لكن ذلك لم يؤد إلى شيء. كل شيء يعمل بشكل جيد بالنسبة لي ، وفي حالة بدء الخدمة ، حدث خطأ.

يبدو أن الخطأ ببساطة لا يمكن تكرار حدوثه عند تشغيل البرنامج النصي يدويًا ، في حين أن الدقائق الثلاثين المقدرة قد انتهت بالفعل ست مرات ، بالإضافة إلى ظهور مهمة ذات أولوية أعلى ، مما دفع بقية الحالات جانبًا. كان بقية الأسبوع مليئًا بمجموعة متنوعة من المهام وتم تخفيفه قليلاً فقط بالحديث على Sed ومقابلة مع المرشح. مشكلة خطأ في varnishreload ضاع بشكل لا رجعة فيه في رمال الزمن.

ما يسمى سيد فو ... في الواقع ... قمامة

كان الأسبوع التالي يومًا مجانيًا إلى حد ما ، لذلك قررت شراء هذه التذكرة مرة أخرى. كنت آمل أنه في ذهني ، كانت بعض العمليات الخلفية طوال هذا الوقت تبحث عن حل لهذه المشكلة ، وهذه المرة سأفهم بالتأكيد ما هو الخطأ.

منذ آخر مرة لم يساعد تغيير الرمز ، قررت فقط إعادة كتابته بدءًا من السطر 116. على أي حال ، كان الرمز الحالي سخيفًا. وليس هناك حاجة على الإطلاق للاستخدام read.

النظر في الخطأ مرة أخرى:
sh: echo: broken pipe - في هذا الأمر ، يوجد الصدى في مكانين ، لكنني أظن أن الأول هو الجاني الأكثر احتمالًا (حسنًا ، أو على الأقل شريك). Awk لا يوحي بالثقة أيضًا. وفي حال كان الأمر كذلك حقًا awk | {read; echo} التصميم يؤدي إلى كل هذه المشاكل ، فلماذا لا يتم استبداله؟ لا يستخدم هذا الأمر المكون من سطر واحد جميع ميزات awk ، وحتى هذه الميزات الإضافية read في الملحق.

منذ الأسبوع الماضي كان هناك تقرير عن sedكنت أرغب في تجربة مهاراتي المكتسبة حديثًا وتبسيطها echo | awk | { read; echo} إلى مفهوم أكثر echo | sed. في حين أن هذا ليس بالتأكيد أفضل طريقة للقبض على الخطأ ، فقد اعتقدت أنني سأحاول على الأقل تجربة sed-fu وربما أتعلم شيئًا جديدًا عن المشكلة. على طول الطريق ، طلبت من زميلي ، كاتب sed talk ، مساعدتي في التوصل إلى نص برمجي أكثر كفاءة.

لقد أسقطت المحتوى varnishadm vcl.show -v "$VCL_NAME" إلى ملف حتى أتمكن من التركيز على كتابة البرنامج النصي sed دون أي متاعب من إعادة تشغيل الخدمة.

يمكن العثور على وصف موجز لكيفية تعامل sed بالضبط مع المدخلات دليل GNU الخاص به. في مصادر sed ، الرمز n محددًا صراحةً كفاصل أسطر.

في العديد من الممرات ، وبنصيحة من زميلي ، كتبنا نص sed الذي أعطى نفس نتيجة السطر الأصلي بأكمله 116.

يوجد أدناه نموذج لملف يحتوي على بيانات الإدخال:

> 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

لذا فإن محتويات البرنامج النصي الخاص بإعادة تحميل الورنيش ستبدو كالتالي:

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

يمكن تلخيص المنطق أعلاه على النحو التالي:
إذا كانت السلسلة تتطابق مع التعبير العادي // VCL.SHOW، ثم تلتهم بشراهة النص الذي يتضمن كلا الرقمين في هذا السطر ، وحفظ ما تبقى بعد هذه العملية. إصدار القيمة المخزنة وإنهاء البرنامج.

بسيط ، أليس كذلك؟

كنا سعداء بالبرنامج النصي sed وحقيقة أنه يحل محل جميع الكود الأصلي. أعطت جميع اختباراتي النتائج المرجوة ، لذلك قمت بتغيير "varnishreload" على الخادم وتشغيلها مرة أخرى systemctl reload varnish. خطأ قذر echo: write error: Broken pipe ضحك في وجهنا مرة أخرى. كان هناك مؤشر يغمز في انتظار إدخال أمر جديد في الفراغ المظلم للمحطة ...

المصدر: www.habr.com

إضافة تعليق