معرفی کرفس و لونا

کرفس مجموعه‌ای فعال در حوزه سلامت دیجیتال است. محصول کرفس بر تغذیه، برنامه غذایی و سبک زندگی سالم تمرکز دارد و لونا محصول این مجموعه در حوزه سلامت زنان است. هر دو محصول برای ارائه سرویس پایدار به کاربران خود به زیرساختی مشترک اما از نظر منطقی تفکیک‌شده متکی‌اند.

کرفس و لونا روی یک Kubernetes Cluster مشترک در ستون اجرا می‌شوند، اما Namespaceها و سرویس‌های داده هر محصول مستقل است. این تفکیک، مدیریت Deployment، منابع و تغییرات هر محصول را بدون درگیرکردن محصول دیگر ممکن می‌کند.

رابطه کرفس با Dropp ابتدا از همکاری در توسعه نرم‌افزار موبایل آغاز شد. پس از تجربه مثبت این همکاری، مسئولیت DevOps و زیرساخت نیز به تیم Dropp سپرده شد؛ بنابراین شروع پروژه مهاجرت بر پایه شناخت قبلی و اعتماد شکل‌گرفته میان دو تیم بود.

مسئله: زیرساخت بدون مالک عملیاتی مشخص

در زمان شروع همکاری DevOps، زیرساخت کرفس با بحران فعال یا قطعی گسترده مواجه نبود؛ مسئله مهم‌تر، نبود مالک مشخص برای عملیات روزانه بود. Monitoring باید به‌طور مستمر بررسی می‌شد، Alertها نیازمند پاسخ بودند و در زمان بروز Incident باید تیمی مسئول تشخیص و رفع مشکل می‌بود.

این خلأ عملیاتی درست زمانی شکل گرفت که ستون مهاجرت از نسل اول به نسل دوم سرویس Kubernetes خود را الزامی کرده و برای آن Deadline تعیین کرده بود. در نتیجه، کرفس هم‌زمان به دو خروجی نیاز داشت: تیمی که فوراً مسئولیت نگهداری زیرساخت موجود را بپذیرد و تیمی که بتواند مهاجرتی حساس را بدون آسیب به سرویس و داده‌ها طراحی و اجرا کند.

ریسک تجاری صرفاً از دسترس خارج‌شدن سرویس نبود. عقب‌افتادن از Deadline، نبود دید کافی نسبت به وضعیت کلاستر و وابستگی عملیات به مداخله‌های پراکنده می‌توانست تمرکز تیم محصول را از توسعه کرفس و لونا به خاموش‌کردن آتش‌های زیرساختی منتقل کند.

وضعیت اولیه زیرساخت

هر دو محصول روی نسل اول Kubernetes ستون مستقر بودند. معماری نرم‌افزار شامل سرویس‌های Backend مبتنی بر Node.js، Frontend مبتنی بر Next.js، پایگاه‌های داده MongoDB و PostgreSQL، Redis، RabbitMQ، Nginx و Object Storage بود. کرفس و لونا از یک کلاستر مشترک استفاده می‌کردند، اما در Namespaceهای جداگانه و با سرویس‌های داده مستقل اجرا می‌شدند.

فرایند CI/CD بر GitLab CI و Helm متکی بود. GitLab CI پس از Build، فرمان `helm upgrade` را مستقیماً روی کلاستر اجرا می‌کرد. این روش کار می‌کرد، اما میان فرایند CI و اعمال تغییر روی محیط Production جداسازی روشنی ایجاد نمی‌کرد و وضعیت مطلوب Deploymentها نیز به‌شکل یکپارچه از طریق Git کنترل نمی‌شد.

Backupها از قبل گرفته می‌شدند و مدیریت دسترسی‌ها برقرار بود. در مقابل، پوشش Monitoring برای مشاهده وضعیت کامل Cluster، Nodeها، Podها و سرویس‌ها کافی نبود و مستندات عملیاتی نیز به تکمیل نیاز داشت. بنابراین پروژه از یک زیرساخت ازکارافتاده آغاز نشد؛ هدف، کاهش ریسک‌های پنهان و رساندن عملیات به سطحی قابل‌مشاهده، قابل‌تکرار و قابل‌پشتیبانی بود.

محدودیت‌ها و Deadline پروژه

مهم‌ترین محدودیت پروژه، Deadline تعیین‌شده برای خروج از نسل اول کلاستر Kubernetes ستون بود. کرفس قصد داشت روی زیرساخت ستون باقی بماند؛ بنابراین راهکار باید به‌جای Replatforming یا تغییر ارائه‌دهنده، مهاجرتی امن میان دو نسل از همان سرویس مدیریت‌شده را ممکن می‌کرد.

اجرای پروژه هم‌زمان با جنگ و محدودیت‌های گسترده دسترسی به اینترنت در ایران بود. دسترسی ناپایدار به Registryها و Repositoryهای خارجی می‌توانست Build و Deployment را مختل کند. این شرایط صرفاً یک مسئله جانبی نبود و مستقیماً بر طراحی راهکار مدیریت Dependencyها و Docker Imageها اثر گذاشت.

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

Audit اولیه: چه چیزی باید تغییر می‌کرد؟

تیم Dropp Tempo پیش از اجرای مهاجرت، وضعیت کلاستر، روش Deployment، وابستگی‌های خارجی، Monitoring، Backup، دسترسی‌ها و مصرف منابع را بررسی کرد. هدف Audit این نبود که همه‌چیز از ابتدا بازطراحی شود؛ باید مشخص می‌شد کدام بخش‌ها سالم‌اند، کدام ریسک‌ها باید پیش از Cutover برطرف شوند و چه بهبودهایی می‌توانند هم‌زمان با مهاجرت انجام شوند.

بررسی‌ها نشان داد Backupها برقرارند و می‌توان آن‌ها را حفظ و صحت‌سنجی کرد. دسترسی‌ها نیز مدیریت شده بودند. در مقابل، Monitoring دید کافی از تمام لایه‌های زیرساخت ارائه نمی‌داد، مستندات عملیاتی کامل نبود، CD مستقیماً از GitLab CI روی کلاستر اعمال می‌شد و دسترسی به Dependencyها و Imageهای خارجی می‌توانست تحت تأثیر اختلال اینترنت قرار بگیرد.

همچنین با بررسی مصرف واقعی منابع مشخص شد می‌توان Resource Request و Limitهای Deploymentها را دقیق‌تر تنظیم و ظرفیت Workerها را پس از مهاجرت بهینه کرد. خروجی Audit به یک نقشه راه اجرایی تبدیل شد: ابتدا آماده‌سازی محیط مقصد و کنترل ریسک مهاجرت، سپس بهبود Deployment، Observability، امنیت و بهره‌وری منابع.

تصمیم‌های مهندسی: چرا مهاجرت مرحله‌ای؟

اولین تصمیم مهم، پرهیز از Cutover هم‌زمان کرفس و لونا بود. لونا زودتر به کلاستر جدید منتقل شد تا زیرساخت مقصد، الگوی Deployment و فرایندهای عملیاتی در مقیاسی محدودتر آزموده شوند. پس از پایدارشدن لونا و تأیید عملکرد آن، مهاجرت کرفس آغاز شد. این ترتیب، دامنه هر تغییر را کوچک‌تر و عیب‌یابی را ساده‌تر می‌کرد.

برای دیتابیس اصلی کرفس نیز اتکا به انتقال نهایی یک‌باره انتخاب مناسبی نبود. حجم داده و محدودیت Throughput میان مبدأ و مقصد می‌توانست پنجره توقف را طولانی کند. به همین دلیل همگام‌سازی با Debezium و Kafka Connect از ابتدای فرایند آغاز شد تا داده‌های موجود به‌تدریج منتقل شوند و تغییرات ایجادشده هنگام انتقال نیز به مقصد برسند.

دو تصمیم دیگر نیز بخشی از معماری مقصد بودند: انتقال CD به مدل GitOps مبتنی بر ArgoCD و راه‌اندازی Nexus داخلی برای کاهش وابستگی Build و Deployment به Registryها و Repositoryهای خارجی. به این ترتیب، مهاجرت فقط مسئله انتقال سرویس‌ها را حل نکرد؛ نقاط اصطکاک عملیاتی بعد از مهاجرت نیز هدف قرار گرفتند.

اجرای مهاجرت: از Staging تا همگام‌سازی داده

ابتدا اجزای موردنیاز در نسل دوم Kubernetes ستون آماده و ساختار Namespaceها، Ingress، Storage و Deploymentهای هر محصول در محیط مقصد بازسازی شد. برای هر محصول، نسخه Staging در Namespace مستقل Deploy شد تا تیم کرفس بتواند پیش از انتقال ترافیک Production، عملکرد سرویس‌ها و ارتباط آن‌ها با اجزای زیرساخت را بررسی کند.

مهاجرت لونا به‌عنوان مرحله اول انجام شد. این مرحله علاوه بر انتقال یک محصول واقعی، فرصتی برای سنجش روند مهاجرت، تنظیمات کلاستر جدید و نحوه همکاری دو تیم فراهم کرد. پس از تأیید این مرحله، مسیر مهاجرت کرفس با توجه ویژه به دیتابیس اصلی آن ادامه یافت.

Debezium و Kafka Connect از ابتدای مجموعه داده شروع به همگام‌سازی کردند و تغییراتی را که در طول انتقال روی دیتابیس مبدأ ایجاد می‌شد نیز به مقصد رساندند. پس از نزدیک‌شدن دو سمت به وضعیت همگام، زمان Cutover نهایی تعیین شد. Functional Test توسط تیم کرفس انجام گرفت؛ تست بار با k6 در این پروژه اجرا نشد و در نتایج نیز ادعایی درباره آن مطرح نمی‌شود.

Before / After Architecture
Before
  • Kubernetes Cluster نسل اول ستون
  • GitLab CI با اجرای مستقیم helm upgrade
  • Dependency و Imageهای وابسته به منابع خارجی
  • Monitoring محدود
  • نبود مالکیت عملیاتی یکپارچه
After
  • Kubernetes Cluster نسل دوم ستون
  • Namespace مستقل برای کرفس و لونا
  • GitLab CI برای Build
  • Nexus برای Image و Dependency
  • ArgoCD برای GitOps CD
  • Vault برای Secrets
  • Prometheus و Grafana برای Monitoring
  • Uptime Kuma برای External Endpoint Checks
  • Daily Backup به Object Storage
  • Managed DevOps توسط Dropp Tempo

Cutover چهار‌ساعته و طراحی مسیر بازگشت

Cutover کرفس در یک بازه ازپیش‌برنامه‌ریزی‌شده انجام شد. کاربران پیشاپیش از Maintenance مطلع شدند و سرویس کرفس در طول پنجره انتقال به حالت Read-only رفت؛ لونا پیش از این مرحله منتقل شده بود و در این Cutover درگیر نبود.

در این بازه، همگام‌سازی نهایی داده تکمیل، سرویس‌ها روی مقصد کنترل و مسیر ترافیک به کلاستر جدید منتقل شد. تیم کرفس نیز عملکرد اپلیکیشن و صحت داده را بررسی کرد. کل فرایند Cutover حدود چهار ساعت طول کشید و بدون Data Loss به پایان رسید.

برای اینکه موفقیت اولیه به تصمیمی برگشت‌ناپذیر تبدیل نشود، کلاستر قبلی بلافاصله حذف نشد. محیط قدیمی یک هفته دست‌نخورده باقی ماند تا در صورت مشاهده مشکل پس از انتقال، مسیر Rollback در دسترس باشد. این بازه فرصت می‌داد رفتار واقعی سیستم در Production بررسی شود، بدون آنکه راه بازگشت زودتر از موعد از بین برود.

Migration Flow
01

انتقال لونا

شروع مهاجرت مرحله‌ای با اولین محصول.

02

راه‌اندازی Staging

Deploy نسخه‌ی Staging در یک Namespace مستقل.

03

Functional Test

تست عملکردی نسخه‌ی Staging پیش از هر Cutover.

04

شروع Sync با Debezium و Kafka Connect

همگام‌سازی داده‌ی اولیه و تغییرات حین انتقال بین دو کلاستر.

05

Read-only Window

قرار دادن سرویس در حالت Read-only پیش از Cutover نهایی.

06

Final Cutover

سوییچ نهایی به کلاستر جدید در یک پنجره‌ی چهارساعته.

07

Validation

بررسی صحت داده و سلامت سرویس بعد از Cutover.

08

حفظ کلاستر قبلی برای یک هفته

نگه‌داشتن کلاستر قدیم دست‌نخورده برای امکان Rollback.

09

پایان Rollback Window

جمع‌بندی مهاجرت بعد از یک هفته بدون نیاز به بازگشت.

از Helm مستقیم به GitOps با ArgoCD

در معماری قبلی، GitLab CI علاوه بر Build، اجرای مستقیم `helm upgrade` روی کلاستر را نیز بر عهده داشت. Dropp Tempo این مسئولیت‌ها را تفکیک کرد: GitLab CI همچنان Build را انجام می‌دهد و Artifactها و Imageها را آماده می‌کند، اما اعمال وضعیت مطلوب روی Kubernetes به ArgoCD سپرده شده است.

با پیاده‌سازی GitOps، تعریف وضعیت موردانتظار سرویس‌ها در Git نگهداری می‌شود و ArgoCD وضعیت کلاستر را با آن تطبیق می‌دهد. این تغییر، Deployment را قابل‌مشاهده‌تر و قابل‌ردیابی‌تر کرد و وابستگی اعمال تغییرات Production به اجرای مستقیم فرمان از داخل Pipeline را کاهش داد.

نتیجه این تصمیم صرفاً اضافه‌شدن یک ابزار جدید نبود. تیم عملیات اکنون دید روشن‌تری از وضعیت Sync هر سرویس، تفاوت میان Desired State و وضعیت واقعی کلاستر و تاریخچه تغییرات دارد؛ همان چیزی که برای اداره مستمر چند محصول روی یک کلاستر مشترک ضروری است.

Nexus و کاهش وابستگی به منابع خارجی

اختلال دسترسی به Registryها و Package Repositoryهای خارجی می‌تواند حتی در صورت سلامت کامل کلاستر، Build یا Deployment را متوقف کند. این ریسک در دوره اجرای پروژه و هم‌زمان با محدودیت‌های اینترنتی ملموس‌تر بود.

Dropp Tempo یک Nexus داخلی برای میزبانی و Cache کردن Dependencyها و Docker Imageهای موردنیاز کرفس راه‌اندازی کرد. در معماری جدید، فرایندهای Build و Deployment تا حد ممکن منابع موردنیاز خود را از Nexus متعلق به کرفس دریافت می‌کنند.

این راهکار وابستگی لحظه‌ای به منابع خارجی را کاهش داد و کنترل بیشتری بر دسترس‌پذیری Artifactهای موردنیاز تیم ایجاد کرد. هدف، ادعای استقلال کامل از اینترنت نبود؛ هدف این بود که یک اختلال بیرونی کمترین اثر ممکن را بر ریتم توسعه و انتشار محصول بگذارد.

Observability و Alerting چندلایه

یکی از مهم‌ترین خروجی‌های Audit، نیاز به دید عملیاتی کامل‌تر روی کلاستر و سرویس‌ها بود. Prometheus و Grafana برای جمع‌آوری Metricها و نمایش وضعیت زیرساخت به کار گرفته شدند و پوشش Monitoring از سطح Node و Pod تا سرویس، دیتابیس و Endpoint گسترش یافت.

در کنار Monitoring داخل کلاستر، Endpointهای عمومی از طریق سیستم Monitoring مستقل Dropp Tempo و Uptime Kuma نیز بررسی می‌شوند. این لایه مستقل کمک می‌کند دسترس‌پذیری سرویس از بیرون همان زیرساخت نیز سنجیده شود و تشخیص مشکل فقط به ابزارهای داخل کلاستر وابسته نباشد.

Alertها به‌صورت هم‌زمان از طریق ایمیل و کانال‌های ارتباطی تیمی به Dropp Tempo و تیم کرفس تحویل داده می‌شوند. به این ترتیب، Monitoring از مجموعه‌ای از Dashboardها به یک فرایند عملیاتی تبدیل شده است: مشاهده، Alert، دریافت مسئولانه و اقدام توسط تیمی که مالک سلامت زیرساخت است.

Uptime Kuma

مدیریت Secrets و Security Hardening

در زیرساخت جدید، HashiCorp Vault برای مدیریت متمرکز Secrets اضافه شد تا اطلاعات حساس خارج از تنظیمات پراکنده سرویس‌ها و فرایندهای دستی نگهداری شوند. جزئیات سیاست‌ها و ساختار دسترسی به دلایل امنیتی منتشر نمی‌شود، اما هدف اصلی ایجاد یک نقطه کنترل مشخص برای چرخه عمر Secrets بود.

تمام سرورهای جدید نیز پیش از قرارگرفتن در مسیر عملیاتی Hardening شدند. این کار بخشی از آماده‌سازی محیط مقصد بود، نه اقدامی جداگانه پس از مهاجرت. در نتیجه امنیت از ابتدا در طراحی و تحویل زیرساخت جدید لحاظ شد، بدون آنکه اطلاعات قابل‌بهره‌برداری درباره توپولوژی یا کنترل‌های داخلی کرفس افشا شود.

Backup روزانه و Restore Test واقعی

Backupهای کرفس پیش از شروع پروژه برقرار بودند و Audit اولیه وجود و سلامت فرایند آن‌ها را تأیید کرد. در معماری نهایی، Backupها به‌صورت روزانه تهیه و به Object Storage منتقل می‌شوند.

تیم Dropp Tempo صرفاً به موفق‌بودن Job یا وجود فایل Backup اکتفا نکرد. یک Restore Test واقعی انجام شد تا مشخص شود نسخه‌های موجود در زمان نیاز قابلیت بازیابی دارند. این آزمون، تفاوت میان «داشتن فایل پشتیبان» و «داشتن مسیر بازیابی قابل‌اعتماد» را روشن می‌کند.

فرایند Backup و کنترل وضعیت آن امروز بخشی از خدمات مستمر زیرساخت است و در کنار Monitoring و Incident Response نگهداری می‌شود.

۲۰٪ کاهش Worker Nodeها با بهینه‌سازی منابع

پس از ایجاد دید دقیق‌تر نسبت به مصرف منابع، Deploymentها و مقادیر Resource Request و Limit بر اساس نیاز واقعی بازبینی شدند. هدف این نبود که صرفاً تعداد سرورها کاهش پیدا کند؛ ابتدا باید مشخص می‌شد هر سرویس واقعاً چه ظرفیتی نیاز دارد و کاهش منابع در کدام بخش بدون ایجاد ریسک عملیاتی امکان‌پذیر است.

در نتیجه این بهینه‌سازی، تعداد Worker Nodeهای کلاستر از ۱۵ به ۱۲ کاهش یافت؛ معادل ۲۰٪ کاهش در تعداد Workerها. این کاهش ظرفیت، هزینه جاری زیرساخت را نیز کمتر کرد.

اهمیت این نتیجه در این است که صرفه‌جویی پس از مشاهده و اندازه‌گیری انجام شد، نه با حدس یا کاهش یک‌باره ظرفیت. Monitoring جدید مبنایی برای ادامه بهینه‌سازی منابع در دوره پشتیبانی فراهم کرده است.

نتایج فنی و اثر تجاری

مهاجرت نسل زیرساخت طی حدود سه ماه تکمیل شد. Cutover نهایی کرفس در یک پنجره چهار‌ساعته Read-only و بدون Data Loss انجام گرفت و کلاستر قدیمی نیز برای یک هفته به‌عنوان مسیر Rollback حفظ شد. انتقال مرحله‌ای لونا و کرفس باعث شد دامنه ریسک هر مرحله محدود و قابل‌کنترل باقی بماند.

در سطح فنی، CD مستقیم از GitLab CI جای خود را به GitOps مبتنی بر ArgoCD داد؛ Nexus وابستگی Build و Deployment به منابع خارجی را کاهش داد؛ Vault مدیریت متمرکز Secrets را فراهم کرد؛ Monitoring از Node و Pod تا دیتابیس و Endpoint توسعه یافت؛ و Backup روزانه با Restore Test واقعی اعتبارسنجی شد.

در سطح منابع، بهینه‌سازی Deploymentها امکان کاهش Worker Nodeها از ۱۵ به ۱۲ را فراهم کرد. در سطح کسب‌وکار نیز کرفس از یک خلأ در مالکیت عملیاتی به مدلی رسید که یک تیم مشخص مسئول Monitoring، Incident Response، Deployment، Backup و بهبود مستمر زیرساخت است. مهم‌ترین نتیجه فقط پایان موفق مهاجرت نبود؛ ایجاد شرایطی بود که تیم محصول بتواند بدون بازگشت به عملیات واکنشی، روی توسعه کرفس و لونا تمرکز کند.

از پروژه مهاجرت تا Managed DevOps مستمر

همکاری Dropp Tempo و کرفس با پایان Cutover متوقف نشد. پس از تکمیل مهاجرت، مدیریت Kubernetes، Monitoring و پاسخ به Alertها، Incident Response، Deployment، Backup، Troubleshooting زیرساخت و بهینه‌سازی منابع در قالب Managed DevOps ادامه پیدا کرد.

این مرحله همان بخش Retain در مدل همکاری Dropp Tempo است: زیرساخت تحویل داده نمی‌شود تا دوباره بدون Owner باقی بماند. تیمی که زمینه تصمیم‌های معماری و جزئیات مهاجرت را می‌شناسد، مسئولیت عملیات روزانه و بهبود تدریجی آن را نیز حفظ می‌کند.

برای کرفس، نتیجه یک پروژه مقطعی نبود؛ یک مسیر عملیاتی پایدار شکل گرفت که در آن وضعیت زیرساخت به‌طور مستمر دیده می‌شود، Alertها Owner دارند و تغییرات بعدی می‌توانند با همان نظم و کنترل ریسکی انجام شوند که در مهاجرت به کار گرفته شد.