در این پست در مورد لزوم استفاده از health check برای بررسی وضعیت کانتینرها با هم صحبت می‌کنیم و می‌گیم که اصلا health check چیه و چرا باید به کانتینر اضافه بشه و بعد از اون نحوه‌ی تعریف health check در داکر رو توضیح می‌دیم.

بیاین با مثال زیر شروع کنیم: 

فرض کنین dockerfile شما ایجاد شده است.

image هم ساخته شده و آماده‌ هستین تا از کانتینرها استفاده کنین.

بعد از اجرای سریع داکر، کانتینر بالا اومده و شروع به کار کرده، حالا مشتاقانه منتظر این هستین که برنامه شروع به کار کنه.

چند ثانیه صبر می‌کنین ...

docker ps گزارش می‌کنه که کانتینر در حال اجراست، اما چرا هیچ درخواستی دریافت یا ارسال نمی‌شه؟

چه اتفاقی رخ داده؟

با ما در ادامه‌ی پست همراه باشین تا با هم به این سوال‌ها پاسخ بدیم. 

. . .

ایجاد یک کانتینر Nginx در داکر

اول با استفاده از dockerfile زیر، ساده‌ترین کانتینر داکر رو ایجاد کنین:

FROM nginx:1.17.7

image رو بسازین و یک کانتینر رو راه‌اندازی کنین:

docker build -t docker-health .
docker run --rm --name docker-health -p 8080:80 docker-health

حالا کانتینر nginx، در پورت محلی 8080 در حال اجراست.
برای این‌که از اجرای nginx در داکر مطمئن بشین، می‌تونین دستور curl localhost:8080
رو تایپ کنین.
یا این‌که آدرس http: // localhost: 8080 رو در مرورگرتون باز کنین:

Nginx در حال اجرا در داکر

در تصویر زیر می‌بینین که docker ps گزارش داده که کانتینر، بالا اومده و چند ثانیه‌ی دیگه اجرا می‌شه:

پروسه‌ی اجرای داکر

اما داکر چه چیزی رو بررسی می‌کنه تا گزارش کنه که کانتینری در حال اجراست؟

در واقع داکر فرآیندهای داخل کانتینر رو بررسی می‌کنه. بیاین در ادامه با این فرآیندها بیش‌تر آشنا بشیم.

بررسی فرآیندهای داخل کانتینر Nginx

فرآیندهای در حال اجرا در داخل کانتینر

در dockerfile برای پیکربندی نحوه‌ی اجرای کانتینرها، دو دستورالعمل CMD یا ENTRYPOINT مورد استفاده قرار می‌گیره و میشه با استفاده از این دو دستور پارامترهایی رو برای یک کانتینر اجرایی ارائه کرد.

با استفاده از این دستورالعمل‌ها، فرآیندی اجرا می‌شه به نام 1 PID.

حالا PID چیه ؟

PID شماره‌ی شناسایی فرآیند است که در هنگام ایجاد هر فرآیند، به طور خودکار به هر کدوم از فرآیندها اختصاص داده می‌شه.

فرآیندی که به عنوان PID 1 در داخل یک کانتینر اجرا می‌شه، رفتار ویژه‌ای از خودش نشون میده.
چون هر سیگنالی مثل SIGINT یا SIGTERM رو نادیده می‌گیره و اجراش به پایان نمی‌رسه مگر این‌که برای پایان اجراش کد مناسبی نوشته شده باشه.
 docker engine هم واقعاً نمی‌دونه که یک برنامه‌ی مبتنی بر کانتینر چه کاری داره انجام میده.
حالا تا زمانی که PID 1 بالا اومده و در حال اجرا باشه، docker engine گزارش می‌کنه که کانتینر هم بالا اومده و در حال اجراست.
حتی اگه با استفاده از دستور docker pause، کانتینر رو pause کرده باشین، باز هم در خروجی، کانتینر رو در حال اجرا می‌بینین ( اما با یک فلگ paused): 

یک کانتینر Pause شده

در ادامه، چند فرآیند رو توضیح دادیم که داکر با بررسی اونا، پیغام خطایی رو در خروجی گزارش کرده:

لزوم استفاده از Health Check

تصور کنین که کانتینر مبتنی بر nginx، به این دلیل ایجاد شده تا یک‌ فایل استاتیک به اسم  system-status.txt رو ارائه بده که اطلاعات مربوط به سلامت سیستم رو در خودش نگه‌داری می‌کنه.

وقتی کانتینر بالا میاد، ممکنه این فایل فورا در دسترس نباشه.

حالا سعی کنین کمی بعد از ایجاد کانتینر، این فایل رو دریافت کنین. با توجه به تصویر زیر می‌بینین که این فایل وجود نداره، و با خطای 404 رو به رو شدین!

فایل health check با نام system-status هنوز در دسترس نیست.

یک فرآیند دیگه رو هم تصور کنین:

ممکنه بخواین در دسترس بودن API برنامه‌تون رو چک کنین. از اون‌ جایی که ممکنه چند ثانیه طول بکشه تا برنامه‌ی مبتنی بر کانتینر شما بوت بشه و API رو در دسترس قرار بده، در خروجی گزارش می‌شه که کانتینر بالا اومده ولی هنوز API ای ارائه نشده است.

در واقع شما با کانتینری مواجه هستین که مشکلاتی داره، اما docker ps میگه که کانتینر به خوبی کار می‌کنه (یا به تعبیر دیگه کانتینر خوبی دارین).

حالا این خطاها چه مفهومی دارن؟ در نهایت آیا کانتینر در وضعیت خوبی قرار داره یا نه؟ 

اگه داکر به جای بررسی‌های معمولی که روی process ها انجام می‌ده، روش دیگه‌ای رو برای انجام بررسی‌ها ارائه‌ می‌کرد،‌ بهتر نبود؟
نوعی بررسی که شما به عنوان توسعه‌دهندگان برنامه، بتونین به صورت سفارشی و متناسب با برنامه‌های داخل کانتینر اون رو تعریف کنین.

حالا ببینیم چطوری می‌تونین این کار رو انجام بدین و در واقع یک custom health check رو تعریف کنین! 

اول بیاین با تعریف health check بیش‌تر آشنا بشیم. 

Health Check چیست؟

معنی health check رو از اسمش می‌تونین متوجه بشین : روشیه برای بررسی سلامت بعضی resource ها.

health check، دستوریه که برای تعیین سلامت یک کانتینر در حال اجرا، استفاده می‌شه.

زمانی که دستور health check شناسایی میشه، این دستور به داکر اعلام می‌کنه که چطوری کانتینر رو آزمایش کنه تا ببینه کانتینر به درستی کار می‌کنه یا نه.
بدون مشخص شدن health check، داکر نمی‌دونه که سرویس‌های داخل کانتینر شما واقعاً بالا اومدن و در حال اجرا هستن یا نه؟!

health check می‌تونه در dockerfile و یا در یک compose-file مشخص بشه.

آشنایی با دستورات Container Health Check

در ورژن docker engine 1.12، امکان تعریف دستورات سفارشی برای health check وجود داره. 

اگر چه این ویژگی از اواسط سال ۲۰۱۶ ارائه شده، اما خیلی عجیبه که هنوز بسیاری از docker image ها ازش استفاده نمی‌کنن.

health check سفارشی در dockerfile، با نوشتن دستور HEALTHCHECK مشخص می‌شه.

دستور check در داخل کانتینر اجرا می‌شه، بنابراین مطمئن بشین که این دستور داره به درستی کار می‌کنه.
این دستور می‌تونه هر چیزی رو چک کنه و بعد متناسب با نوع بررسی انجام شده، exit-code ای رو در خروجی برمی‌گردونه که وضعیت کانتینر رو نشون میده.

طبق اسناد رسمی داکر، exit code می‌تونه یکی از کدهای زیر باشه:

0 : وضعیت success- کانتینر سالم و آماده‌ی استفاده است.

1: وضعیت unhealthy-کانتینر به درستی کار نمی‌کنه.

2: وضعیت reserved- از این نوع exit code استفاده نشود.


همون طور که در بخش‌های قبل دیدیم ممکنه در خروجی کانتینری رو داشته باشیم که ظاهرا داره به خوبی کار می‌کنه، اما با توجه به دستورات بالا می‌تونیم بگیم که اگه کد وضعیت خروجی از 0 بزرگ‌تر باشه، یعنی مشکلی در کانتینر وجود داره.

بازنویسی Dockerfile با دستور Health Check

حالا سعی کنین dockerfile مورد استفاده در مثال بالا رو این بار با انجام  health check، بازنویسی کنین:

FROM nginx:1.17.7
RUN apt-get update && apt-get install -y wget
HEALTHCHECK CMD wget -q --method=HEAD localhost/system-status.txt

ببینیم دستورات بالا چه مفهومی دارن:

در dockerfile از دستور RUN استفاده شده تا wget client نصب بشه تا در نهایت در تعریف health check مورد استفاده قرار بگیره.

در خط بعدی  HEALTHCHECK  تعریف شده.
با استفاده از url ،wget مربوط به فایل سفارشی system-status.txt فراخوانی می‌شه.
در صورت پیدا شدن فایل، نتیجه‌ی exit code برابر با 0 خواهد بود، در غیر این صورت، 8 در خروجی برگردونده می‌شه.

با توجه به صفحه‌ی رسمی exit code های مرتبط با wget ، کد 8 به این معناست که ‌: “سرور پاسخ خطایی رو ارسال کرده ”.

برای این‌که با دستورات dockerfile بیش‌تر آشنا بشین، پیشنهاد می‌کنم این پست سکو رو هم مطالعه کنین.

تست کردن Health Check

با استفاده از dockerfile ایجاد شده در بالا، می‌خوایم image جدیدی بسازیم و کانتینر رو مجدداً اجرا کنیم:

ساخت و اجرای کانتینر با Health check فعال شده 

-حالا با تایپ دستور docker ps، در خروجی می‌بینین که docker engine علاوه بر این‌که نشون داده کانتینر بالا اومده، وضعیت health کانتینر رو هم نشون داده:

یک کانتینر بالا اومده و هنوز هیچ health check ای انجام نشده است.

با توجه به پیکربندی پیش فرض انجام شده برای health check، دستور health check بلافاصله اجرا نمی‌شه.
بنابراین چیزی که به عنوان وضعیت health مشاهده می‌کنین وضعیت starting هست 
(health: starting).

خب این نشون می‌ده که کانتینر بالا اومده، اما هنوز هیچ health check ای انجام نشده.

-بعد از 30 ثانیه، دستور health check اجرا می‌شه، و از اون‌جایی که system-status.txt ای وجود نداره، حالا کانتینر به عنوان یک کانتینر unhealthy در خروجی گزارش داده می‌شه:

یک کانتینر unhealthy 

- حالا بیایین با ایجاد یک نمونه فایل system-status.txt، سعی کنیم کانتینرمون رو بهبود بدیم:

docker exec docker-health sh -c \
'echo OK > /usr/share/nginx/html/system-status.txt'

-در ۳۰ ثانیه بعدی، وضعیت کانتینر باید healthy گزارش بشه:

یک کانتینر healthy 

معرفی پارامترهای دیگر Health Check 

دستور HEALTHCHECK با چهار پارامتر مختلف دیگه هم استفاده می‌شه:

-- interval =   مدت زمان پیش‌فرض برابر است با 30 ثانیه

-- timeout =  مدت زمان پیش‌فرض برابر است با 30 ثانیه

-- start-period = مدت زمان پیش‌فرض برابر است با 0 ثانیه

-- N = retries (مقدار پیش فرض برابر است با 3)

interval:

این گزینه فاصله‌ی زمانی بین دو health check رو مشخص می‌کنه (مقدار پیش‌فرض برابر است با 30 ثانیه).

timeout:

مهلت اجرای دستور health check.
در واقع تعداد ثانیه‌هایی رو مشخص می‌کنه که داکر در انتظار دستور health check شما می‌مونه تا بتونه exit code رو در خروجی برگردونه.
اگه از 30 ثانیه بیش‌تر منتظر بمونه، health check ای انجام نمی‌شه.

start-period:

گزینه start-period تعداد ثانیه‌هایی که کانتینر برای بوت شدن نیاز داره رو مشخص می‌کنه.
درطول این مدت اگه exit code بیش‌تر از صفر گزارش بشه، کانتینر، unhealthy محسوب نمی‌شه.

retries:

این گزینه یعنی اگه تعداد دفعات health check ناموفق (خرابی‌های متوالی لازم) به میزان N برسه، اون وقت می‌تونین بگین که وضعیت کانتینر، unhealthy در نظر گرفته می‌شه. (مقدار پیش‌فرض N، برابر است با 3).

  اگه اطلاعات بیش‌تری در مورد پارامترهای health check می‌خواین، می‌تونین این‌جا بخونین.

رفتار Docker Swarm

بهتره برای انجام تست health check ، کانتینرها رو در docker swarm اجرا کنین.
چون تا زمانی که یک کانتینر در وضعیت unhealthy  یا (health:starting) قرار داشته باشه، routing غیر فعال می‌شه و هیچ درخواستی به کانتینر نمی‌رسه.

جمع‌بندی

توی این پست توضیح دادیم که شروع به کار کانتینر لزوماً به این معنا نیست که برنامه‌تون به درستی بالا اومده و یا طبق اون چیزی که طراحی کردین رفتار می‌کنه.
برای حل این مشکلات می‌تونین از ویژگی health check داکر استفاده کنین. 

انجام تست health check داکر خیلی راحت انجام می‌گیره و قبل از این‌که به یک مشکل جدی برخورد کنین،‌ بهتون کمک می‌کنه تا عملکردهای نامنظم رو به سرعت شناسایی کنین. 

پس دفعه‌ی بعدی که dockerfile ای رو ایجاد کردین،‌ باید به health check هم توجه داشته باشین و اونو به dockerfile اضافه کنین. 

منابع:

How to Implement Docker Health Checks
Health check of Docker containers
How to Add a Health Check to Your Docker Container