Gnu Tips

وبلاگ شخصی وحید معانی

imagePullPolicy

imagePullPolicyهنگام ساختن پاد در کوبرنتیز می‌شه با استفاده از فیلد imagePullPolicy تعیین کرد که چه رفتاری با imageی که برای ساختن کانتینر مشخص شده داشته باشه. در حالت کلی این فیلد سه مقدار Always، IfNotPresent و Never رو می‌تونه داشته باشه. یه مرور کوتاه می‌کنیم به خاصیتی که هر کدوم از این مقادیر دارن.

 

  • Always: باعث می‌شه image فارغ از اینکه روی سرور وجود داره یا نه دوباره از ریجستری pull بشه.
  • IfNotPresent: در این حالت ابتدا سرور چک می‌شه و اگر image به صورت لوکال وجود نداشته باشه اقدام به pull کردن از ریجستری می‌کنه.
  • Never: در این حالت فقط لوکال رو به دنبال image می‌گرده و اگر وجود نداشته باشه هیچ تلاشی برای pull کردن اون از هیچ ریجستری انجام نمی‌شه.

نکته‌ی مهمی که باید بهش توجه داشته باشیم این هست که برای همه‌ی image‌هایی که با تگ latest مشخص می‌شن مقدار پیش‌فرض این فیلد Always در نظر گرفته می‌شه و برای همه‌ی حالات دیگه IfNotPresent!

طبق داکیومنت‌های خود Kubernetes می‌تونیم یکی از موارد زیر رو انجام بدیم:

  • مقدار imagePullPolicy رو برابر Always قرار بدیم.
  • از نوشتن فیلد imagePullPolicy صرف نظر کنید و image را با تگ latest استفاده کنید.
  • از نوشتن فیلد imagePullPolicy صرف نظر کنید و image را بدون تگ استفاده کنید.
  • ادمیشن کنترلر AlwaysPullImage را فعال کنید.

 

امّا باید در نظر داشته باشیم که طبق Best Practices شرایط زیر اتفاق می‌افته:

  • اگر فیلد imagePullPolicy با ifNotPresent مقدار دهی شده باشد ایمیج فقط زمانی pull می‌شود که به صورت localy وجود نداشته باشد.
  • اگر فیلد imagePullPolicy با Always مقداردهی شده باشد ایمیج هر زمانی که پاد شروع به کار می‌کند pull می‌شود.
  • اگر از نوشتن فیلد imagePullPolicy صرف‌نظر شده باشد و تگ image برابر latest بوده و یا کلاً تگ استفاده نشده باشد، مقدار آن Always در نظر گرفته می‌شود.
  • اگر از نوشتن فیلد imagePullPolicy صرف‌نظر شده باشد و تگ image با مقداری غیر از latest وجود داشته باشد، مقدار آن ifNotPresent در نظر گرفته می‌شود.
  • اگر برای فیلد imagePullPolicy از مقدار Never استفاده شده باشد از imageهای موجود به صورت localy استفاده می‌شود و هیچ تلاشی برای pull کردن آنها انجام نمی‌گیرد.

 

امّا چیزی که باعث شد من بخوایم این بحث رو به صورت عمیق بررسی کنم یک داستان مجزاست. رویه‌ی ما در تیم DevOps به این شکل هست که برای اجتناب از ایجاد کانفلیکت در منیفست‌ها اونها رو داخل یک دایرکتوری به اسم همون پروژه بر روی یک مخزن گیت نگهداری می‌کنیم و کلیه‌ی اعضا برای ایجاد تغییرات باید پس از انجام تغییرات بر روی فایل منیفست اون رو apply بکنه. مشکل از اونجایی شروع شد که همیشه قبل از apply کردن منیفیست‌های deployment و امثالهم باید تگ آخرین image مورد استفاده‌ی پاد رو پیدا کرده و داخل منیفست آپدیت می‌کردیم. برای حل این مشکل اومدیم هر بار که image جدید بیلد می‌شه به همراه تگ ورژن معمول یک تگ latest هم ایجاد کردیم و push کردیم داخل ریجستری. ظاهراً مشکل حل شد. امّا در واقع مشکل دیگه‌ای درست کرده بودیم اون هم زمانی که که ما مقدار فیلد imagePullPolicy رو به درستی انتخاب نکرده باشیم. یعنی اینکه اگر مقدار این فیلد برابر ifNotPresent باشه موقع apply کردن منیفست با تگ latest ممکنه پاد بر روی ورکری schedule بشه که این image با تگ latest موجود بر روی اون به نسخه‌ی قدیمی‌تر image اشاره داشته باشه چون در اینصورت همون image به اشتباه دیپلوی می‌شه و کار خیلی بد پیش می‌ره و ممکنه ساعت‌ها درگیرمون بکنه. البته می‌تونیم با استفاده از مقدار Always برای فیلد مورد بحث مشکل رو رفع کنیم ولی چرا باید برای pull کردن خیلی از imageهایی که لازم داریم و بر روی ورکر داریمشون منابع صرف کنیم؟ پس می‌ریم سراغ راه‌حل بهتر.

امّا در گزینه‌های بالا جواب مشکلی که داریم داده شده. به دو مورد آخر در قسمت best practiceها توجه کنید! بهترین روش صرف‌نظر کردن از فیلد imagePullPolicy هست. در این حالت اگر image تگ latest داشته باشد یا کلاً بدون تگ باشد (که همان معنی latest رو داره) image همیشه pull گرفته می‌شه (Always) و اگر image تگ ورژنی (هر چیزی غیر از latest)‌ داشته باشه مقدار فیلد imagePullPolicy برابر ifNotPresent در نظر گرفته می‌شه و در صورت وجود image دیگه نیازی به pull کردن اون نداریم.

 

Act like a big brother

سلام

Act like a big brother

موقعیت‌های کمی پیش میاد ولی تا الان دوبار مجبور شدم دسترسی سروری که مسئولیتش با من هست رو به کسی بدم که هیچ تعهدی نسبت بهش نداره (یا اگر داره خیلی کم) برای همین به نظرم عاقلانه‌س که در لحظه بتونم ببینم چه کاری داره روش انجام می‌ده. ولی خب چطوری؟

بخوام جوابی که پیدا کردم رو به شکل کوتاه بگم می‌شه: استفاده از screen

Screen ابزاری هست که امکان ایجاد پنجره‌های متعدد از ترمینال رو می‌ده (امیدوارم تونسته باشم خوب تعریفش کنم :-/) می‌شه باهاش کارهای مختلفی انجام داد ولی چیزی که مد نظر من هست اتصال به یک session از مکان‌های مختلفه. (اگر باهاش آشنایی ندارید توصیه می‌کنم این پست از Tecmint رو بخونید.)

امّا سناریو: همه‌ی کانکشن‌های ssh رو می‌برین روی یه session تا بتونیم کاری که مهمونمون روی سرور در حال انجامش هست رو ببینیم.

قطعاً من نمی‌تونم به طرف مقابلم بگم «لطفاً هر موقع خواستی کانکت بشی اوّلاً به من خبر بده، دوّما حتماً داخل فلان session کارهات رو انجام بده تا من هم ببینم.» می‌خوام غیر از اینکار چاره‌ی دیگه‌ای نداشته باشه (‪My server, My rules ¯\_(ツ)_/¯‬). پس می‌یام توی home کاربری که دسترسیش رو بهش دادم یه فایل ‪.bash_profile‬ می‌سازم با محتویات زیر:

این فایل موقع ایجاد session با این کاربر خونده و محتویاتش اجرا می‌شه. با این فایل من تعداد sessionهای موجود رو می‌خونم (خط اوّل) و اگر sessionی موجود بود مستقیم به همون وصل می‌شم (خط هشتم) و اگر هیچ sessionی موجود نبود یکی می‌سازم و بهش وصل می‌شم (خط پنجم). خب طبیعی هست که اوّلین کاربر session رو می‌سازه و کاربرای بعدی همگی به همون وصل می‌شن.

امّا توی این فایل یکی دوتا نکته هست که به نظرم مهم هستن.

اوّل اینکه برای اجرای دستورات از exec استفاده کردیم، می‌شد بدون اون هم دستورات رو نوشت و نتیجه هم گرفت امّا نکته‌ی مهم اینه که با این کار اگر کاربر جدید sessionش رو ببنده (حالا به هر طریقی) دوباره بر می‌گرده به خود سرور و از دید من خارج می‌شه. یعنی کاربر خیلی راحت با زدن Ctrl+d می‌تونه رشته‌های من رو پنبه کنه. ولی وقتی من از exec استفاده می‌کنم وقتی کاربر session رو می‌بنده به کل ارتباطش با سرور قطع می‌شه و مجبور هست دوباره کانکت بشه. یعنی بر می‌گرده به نقطه‌ی اوّلش! حالا هر چند بار که می‌خواد :-))

نکته‌ی دوّم این هست که من برای شروع session از دستور screen -s bash استفاده کردم تا موقع ایجاد session جدید دیگه اون متن ابتدایی که نیاز به space یا Enter برای شروع داره رو نبینم و مستقیم برم داخل session. دفعه‌ی قبل از همین طریق لو رفتم. هر چند کاربر بازم با دستور screen -list هم می‌تونه متوجه بشه که داخل screen هست ولی همون دفعه‌ی اوّل خیلی سخته که اصلاً به موضوع فکر کنه.

یه نکته‌ی مهم دیگه این هست که وقتی من داخل یه session دارم رفتار مهمون رو نگاه می‌کنم وقتی مهمون عزیزتر از جانم session خودش رو می‌بنده من هم از این طرف پرت می‌شم بیرون. چون کل session screen کیل شده. خب مهمون هنوز دسترسی داره و ممکنه کلاً من رو گول بزنه و دوباره کانکت بشه :-؟

کاری که کردم این بود که یه حلقه‌ی بی‌نهایت نوشتم و توش به سرور وصل شدم. یعنی هر چندبار که ارتباط من با سرور قطع می‌شد سریع یکی دیگه ایجاد می‌شد:

و خب چون من با ssh key به سرور کانکت می‌شم تا مهمونم بخواد پسورد رو بزنه دوباره می‌بینه من نشستم تو session :-)))

در آخر هم بعد از اینکه پسورد رو عوض کردین کافیه فایل ‪.bash_profile‬ رو به یه چیز دیگه تغییر اسم بدین یا کلاً پاکش کنید تا دسترسی‌های بعدی خودتون هم دچار مشکل نشه.

تمرین در خانه: حالا فرض کنید کار مهمون تموم شد و شما پسورد رو عوض کردین، چطوری این حلقه‌ی بی‌نهایت رو می‌خواین بشکنین؟ :-)))

 

 

تسکی که به خیر گذشت.

توی شرکت به این نتیجه رسیده بودیم که یه تعداد از فایل‌هایی که روی سرور داریم اضافی هست و باید کلاً پاک بشن. من که می‌گم یه تعداد شما خودتون یه تعداد خیلی بالا رو در نظر بگیرید. برای این کار از طرف یه تیم دیگه لیست فایل‌هایی که برامون مهم هستن و باید بمونن رو بهم ارسال کرده بودن (لیست A) و من باید همه‌ی فایل‌های غیر از اونها رو از روی استوریج پاک می‌کردم.

سناریوی خیلی پیچیده‌ای نیست کافیه من لیست فا‌یل‌های موجود رو تهیه کنم (لیست B)، فایل‌هایی از لیست ‌B که داخل لیست A هستن رو حذف کنم و لیست C رو بسازم و دونه دونه‌ی فایل‌های موجود توی لیست C رو حذف کنم. :-) خب من هم همین کار رو کردم. طبیعتاً این کار رو دستی انجام نمی‌دم و انتخاب اوّلم برای انجامش اسکریپت Bash بود. یه حلقه‌ی while که خطوط لیست B رو دونه دونه می‌خوند و توی لیست A دنبال اون می‌گشت اگر پیداش نمی‌کرد اون رو به یه فایل دیگه تحت عنوان فایل‌های لیست C اضافه می‌کرد.

از اونجایی که آدم خیلی محتاطی هستم و عملاً امکان تهیه‌ی نسخه‌ی پشتیبان نداشتم (حجم و تعداد فایل‌ها این اجازه رو به ما نمی‌داد!) حداقل‌ها رو برای کاهش خطا انجام دادم. در مرحله‌ی اوّل تعداد فایل لیست‌ها رو با یه جمع و تفریق ساده بررسی کردم چون اصولاً باید

(تعداد فایل‌های لیست B) – (تعداد فایل‌های لیست A) = (تعداد فایل‌ها لیست C)

حداقل دوّم این بود که چند تا از فایل‌های توی لیست C رو به صورت رندم توی لیست A چک کنم تا مبادا (احتمال خیلی خیلی پایین) اسکریپت اشتباه کرده باشه.

من در مرحله‌ی جمع و تفریق به مشکل خوردم. این معادله برقرار نبود!!!! تعداد اختلاف خیلی خیلی بالا بود. اسکریپت به خوبی و بدون خطا داشت اجرا می‌شد، بدون خطا تموم می‌شد ولی خروجی اون چیزی نبود که باید باشه. چیزای مختلفی رو چک کردم. کنار لیست C یه لیست دیگه به اسم D ساختم تا شروط به شکل if … else بررسی بشن و دقیقاً عملکرد اسکریپت رو ببینم ولی نتیجه تفاوتی نداشت. انواع لاگ‌ها رو فعال کردم ولی چیزی عایدم نشد. اسکریپت بدون خطا تموم می‌شد ولی جمع و تفریق اختلاف خیلی خیلی زیادی با مقادیر مورد انتظار داشت. در نهایت به صورت تصادفی متوجه شدم اسکپریت داخل لیست B از یه خطی جلوتر نمی‌ره و فقط ۱۵۰هزار تا (البته حدوداً چون مقدار واقعی یه خورده کمتر بود!) فایل رو چک می‌کنه!! هنوز نمی‌دونم مشکل چی بوده ولی خیلی مسخره بود. پس این اسکریپت رو بی‌خیال شدم و رفتم سراغ پایتون. خیلی بهتر از Bash عمل کرد و کار رو یک سره کرد برام.

بعد از اینکه دوتا حداقل احتیاطی رو رد کردم لیست واقعی فایل‌هایی که باید حذف می‌شدن جلوی روم بود. اقدام احتیاطی سوّم این بود که به جای حذف فایل‌ها اونها رو به یه دایرکتوری دیگه منتقل کنم و مثلاً چند روز منتظر بمونم اگر داد کسی در نیومد در نهایت یه جا پاکشون کنم ولی حجم فایل‌ها، تعداد فایل‌ها، ددلاین و فشار‌های کاری دیگه این اجازه رو بهم ندادن و مجبورم کردن مستقیم وارد پرسه‌ی حذف بشم و تا تموم شدن اون و حتی تا چند روز بعد از اون استرسش تحمل کنم. الان که دیگه همه چیز به خوبی و خوشی تموم شده به این نتیجه رسیدم اگر بار دیگه همچین تسکی داشته باشم و حتی اگر حجم و تعداد فایل‌ها اجازه‌ی تهیه‌ی پشیبان رو ندن به این صورت عمل کنم که همه‌ی فایل‌ها رو به شکل کاملاً ترتیبی تغییر نام بدم تا اگر فایلی به اشتباه داخل این لیست بود بتونم به راحتی ریکاورش کنم (البته به شرطی که اون «فشار‌های کاری دیگه» که بالا گفتم وجود نداشته باشن.)

چون آدم فراموشکاری هستم اینها رو نوشتم تا اون «دفعه‌ی بعد» بدونم چه تصمیماتی داشتم. شما چه ایده‌/تجربه‌ای دارید؟

نوشتن کد، ایمن‌تر، بهینه‌تر

Post's QR-Code

اسم سرخ‌پوستی این پست رو می‌شه «چرا باید از set -exuo pipefaile استفاده بکنیم؟» انتخاب کرد. ولی خب واقعاً چرا؟

بعضیا معتقدن که استفاده از اسکریپت‌های شل [۱] به شکل زبان‌های سطح بالا اشتباه هست. چون برای مثال توی اسکریپت‌های ruby اگر از متغیری استفاده کنیم که هیچ مقداری براش پیش‌بینی نشده با خطا مواجه می‌شیم ولی به قول معروف Bash مثل یه حیوان نجیب ندید می‌گیره و می‌ره به ادامه‌ی کاراش می‌رسه. البته خب بد هم نمی‌گن این مدل مسائل توی اسکرپیت‌هایی که روی سیستم‌های production اجرا می‌شه ممکنه خسارت‌های جبران‌ناپذیر یا پر هزینه به سیستم بزنه. خب این می‌شه جواب سؤال. ولی چطوری؟ خب می‌ریم برای ادامه.

Bash یک دستور داخلی به اسم set داره که با استفاده از اون می‌شه رفتاری‌های bash رو برای شرایط مختلف تغییر داد. توی این پست با ۴تا از اونها که فعلاً برامون مهم هستن آشنا می‌شیم.

اوّلینشون e هست. رفتار Bash در مواجه به دستوراتی که به درستی اجرا نمی‌شن یا به اصطلاح fail می‌شن این جوری هست که کلاً ندید می‌گیردشون و می‌ره سراغ بقیه‌ی دستورات. البته بعضی اوقات شاید یه خطایی هم چاپ کنه ولی خب دیگه کار از کار گذشته و ممکنه دستوری که نباید، اجرا شده باشه. در این شرایط قبلاً یکی یکی return code دستورات رو بررسی می‌کردیم و اگر غیر صفر بود خودمون دستی هندلش می‌کردیم. ولی اگر با استفاده از دستور set آپشن e رو اعمال کنیم دیگه Bash نسبت به دستوراتی که fail می‌شن بی‌تفاوت نخواهد بود و در صورت وقوع اوّلین دستور fail شده فوراً اجرای اسکریپت رو متوقف می‌کنه و میاد بیرون!!
البته Bash توی این تغییر رفتار به اندازه‌ای هوشمند هست که دستورات با return code غیر صفر داخل شرط‌ها رو ندید بگیره.

خب یه سؤال، اگر می‌دونیم یه دستور ممکنه fail بشه و می‌خوایم که در این صورت Bash به اجرای اسکرپیت ادامه بده چی کار می‌شه کرد؟ جواب می‌شه استفاده از “‪|| true‬”. برای مثال:

دلیلش این هست که توی دستوراتی که pipeline استفاده شده، Bash فقط return code آخرین دستور رو برای شکست یا موفقیت‌آمیز بودن دستور در نظر می‌گیره و خب return code دستور true هم که همیشه صفر هست.
خب یه سؤال دیگه، اگر یکی از دستورات مهم ما داخل pipeline باشه و برای مثال آخرین دستورش دستوری هست که return codeش حتماً صفر هست اون موقع چی؟! دوباره بر می‌گردیم به نقطه‌ی اوّل؟!!
خب نه لزوماً. آپشن بعدی راه‌حل ماست.

دوّمین آپشن ‪-o pipefail‬ هست. این آپشن باعث می‌شه Bash در مواجه با دستورات pipeline به جای در نظر گرفتن return code دستور آخر، return code دستوری که fail شده رو در نظر بگیره (البته در صورت وجود وگرنه نه return code همون صفر خواهد بود). یعنی دقیقاً جواب سؤال قبلی!

توی مقدمه ایرادی برای Bash گرفتیم در ارتباط با متغیر‌هایی که هنوز set نشدن. خب این رفتار هم از جمله رفتارهای مهم و مؤثر در نتیجه‌ی اسکریپت هست پس با آپشن سوّم این پست که u هست اون رو تغییر می‌دیم. از این به بعد Bash با دیدن متغیری که هیچ مقداری برایش set نشده (یا قبلاً set شده بوده ولی الان unset شده) فوراً اجرای اسکرپیت رو متوقف می‌کنه.

آخرین آپشن، x هست. قبلاً برای دیباگ اسکریپت‌ها اونها رو با bash -x اجرا می‌کردم مثلاً اگر اسکریپتی توی cron داشتم و می‌خواستم برای کنترل و پیگیری نتایج ناخواسته لاگ داشته باشم بی‌خیال shibang می‌شدم و اون رو با bash -x اجرا می‌کردم. ولی از این به بعد کافی آپشن x رو اوّل اسکریپت تنظیم کنم تا قبل از اجرای هر دستور اون رو توی خروجی استاندارد چاپ کنه.

پس اینجوری می‌شه اوّل اسکریپت یک خط

اضافه می‌کنیم.

این پست ترجمه‌ای آزاد بود از لینک‌های زیر:

1. Safer bash scripts with ‘set -euxo pipefail’

2. pipefail – testing pipeline exit codes

3. Google.com


[۱] Shell Script