در پروژه های مربوط به توسعه معماری میکروسرویس، CI/CD از مقوله یک فرصت خوشایند به مقوله یک ضرورت فوری حرکت می کند. تست خودکار بخشی جدایی ناپذیر از یکپارچگی مداوم است، یک رویکرد شایسته که می تواند شب های دلپذیر بسیاری را با خانواده و دوستان به تیم بدهد. در غیر این صورت، پروژه هرگز به پایان نخواهد رسید.
میتوان کل کد میکروسرویس را با تستهای واحد با اشیاء ساختگی پوشاند، اما این فقط تا حدی مشکل را حل میکند و سوالات و مشکلات زیادی را بهویژه هنگام آزمایش کار با دادهها بر جای میگذارد. مثل همیشه، مهمترین آنها آزمایش سازگاری دادهها در یک پایگاه داده رابطهای، آزمایش کار با سرویسهای ابری و ایجاد فرضیات نادرست هنگام نوشتن اشیاء ساختگی است.
همه اینها و کمی بیشتر را می توان با آزمایش کل میکروسرویس در ظرف Docker حل کرد. یک مزیت بدون شک برای اطمینان از اعتبار تست ها این است که همان تصاویر Docker که وارد مرحله تولید می شوند آزمایش می شوند.
اتوماسیون این رویکرد تعدادی از مشکلات را ارائه می دهد که راه حل آنها در زیر توضیح داده می شود:
- درگیری وظایف موازی در همان میزبان داکر؛
- تضادهای شناسه در پایگاه داده در طول تکرارهای آزمایشی.
- در انتظار آماده شدن میکروسرویس ها؛
- ادغام و خروجی سیاهههای مربوط به سیستم های خارجی.
- آزمایش درخواست های HTTP خروجی؛
- تست سوکت وب (با استفاده از SignalR)؛
- آزمایش احراز هویت و مجوز OAuth.
این مقاله بر اساس
در این مقاله به شما میگویم که چگونه از یک اسکریپت برای اجرای سرویس تحت آزمایش، پایگاه داده و سرویسهای Amazon AWS در Docker استفاده کنید، سپس روی Postman آزمایش کنید و پس از تکمیل آنها، کانتینرهای ایجاد شده را متوقف و حذف کنید. هر بار که کد تغییر می کند، تست ها اجرا می شوند. به این ترتیب، ما مطمئن می شویم که هر نسخه به درستی با پایگاه داده و سرویس های AWS کار می کند.
همین اسکریپت هم توسط خود توسعه دهندگان روی دسکتاپ ویندوزشان و هم توسط سرور Gitlab CI تحت لینوکس اجرا می شود.
برای توجیه، معرفی آزمایشهای جدید نباید نیاز به نصب ابزارهای اضافی چه در رایانه توسعهدهنده یا روی سروری که در آن آزمایشها بر روی یک commit اجرا میشوند، داشته باشد. Docker این مشکل را حل میکند.
آزمایش باید به دلایل زیر روی سرور محلی اجرا شود:
- شبکه هرگز کاملا قابل اعتماد نیست. از هزار درخواست، یکی ممکن است شکست بخورد.
در این صورت، تست خودکار کار نخواهد کرد، کار متوقف می شود و باید دلیل آن را در سیاهه ها جستجو کنید. - درخواست های خیلی مکرر توسط برخی از سرویس های شخص ثالث مجاز نیست.
علاوه بر این، استفاده از پایه نامطلوب است زیرا:
- یک پایه نه تنها با کد بدی که روی آن اجرا می شود، بلکه با داده هایی که کد صحیح نمی تواند آنها را پردازش کند، می تواند شکسته شود.
- مهم نیست که چقدر سعی می کنیم تمام تغییرات ایجاد شده توسط آزمون را در طول خود آزمون به عقب برگردانیم، ممکن است مشکلی پیش بیاید (در غیر این صورت، چرا تست کنید؟).
درباره سازمان پروژه و فرآیند
شرکت ما یک برنامه وب میکروسرویس را توسعه داده است که در Docker در ابر AWS آمازون اجرا می شود. آزمایشهای واحد قبلاً در پروژه استفاده میشد، اما اغلب خطاهایی رخ میداد که آزمایشهای واحد آن را شناسایی نکردند. لازم بود یک میکروسرویس کامل به همراه پایگاه داده و خدمات آمازون آزمایش شود.
این پروژه از یک فرآیند یکپارچه سازی مداوم استاندارد استفاده می کند که شامل آزمایش میکروسرویس با هر تعهد می شود. پس از تعیین یک کار، توسعه دهنده تغییراتی را در میکروسرویس ایجاد می کند، آن را به صورت دستی آزمایش می کند و تمام تست های خودکار موجود را اجرا می کند. در صورت لزوم، توسعه دهنده آزمایش ها را تغییر می دهد. در صورت عدم مشاهده مشکل به شعبه این موضوع تعهد داده می شود. پس از هر commit، تست ها به صورت خودکار بر روی سرور اجرا می شوند. ادغام در یک شاخه مشترک و راه اندازی تست های خودکار روی آن پس از بررسی موفقیت آمیز اتفاق می افتد. اگر آزمایشات روی شعبه مشترک قبول شود، سرویس به طور خودکار در محیط آزمایشی در سرویس کانتینر الاستیک آمازون (نیمکت) به روز می شود. پایه برای همه توسعه دهندگان و آزمایش کنندگان ضروری است و شکستن آن توصیه نمی شود. آزمایشکنندگان در این محیط با انجام تستهای دستی، یک اصلاح یا یک ویژگی جدید را بررسی میکنند.
معماری پروژه
این برنامه از بیش از ده سرویس تشکیل شده است. برخی از آنها در NET Core و برخی در NodeJs نوشته شده اند. هر سرویس در یک ظرف Docker در سرویس کانتینر الاستیک آمازون اجرا می شود. هر کدام پایگاه داده Postgres خود را دارند و برخی نیز Redis دارند. هیچ پایگاه داده مشترکی وجود ندارد. اگر چندین سرویس به داده های یکسانی نیاز داشته باشند، این داده ها وقتی تغییر می کند، از طریق SNS (سرویس اطلاع رسانی ساده) و SQS (سرویس صف ساده آمازون) به هر یک از این سرویس ها منتقل می شود و سرویس ها آن را در پایگاه داده جداگانه خود ذخیره می کنند.
SQS و SNS
SQS به شما امکان می دهد پیام ها را در یک صف قرار دهید و پیام ها را از صف با استفاده از پروتکل HTTPS بخوانید.
اگر چندین سرویس یک صف را بخوانند، هر پیام فقط به یکی از آنها می رسد. این هنگام اجرای چندین نمونه از یک سرویس برای توزیع بار بین آنها مفید است.
اگر می خواهید هر پیام به چندین سرویس تحویل داده شود، هر گیرنده باید صف خاص خود را داشته باشد و SNS برای کپی کردن پیام ها در صف های متعدد مورد نیاز است.
در SNS شما یک موضوع ایجاد می کنید و در آن مشترک می شوید، به عنوان مثال، یک صف SQS. می توانید به موضوع پیام بفرستید. در این صورت، پیام به هر صفی که در این موضوع ثبت شده است ارسال می شود. SNS روشی برای خواندن پیام ها ندارد. اگر در حین اشکال زدایی یا آزمایش نیاز دارید که بفهمید چه چیزی به SNS ارسال می شود، می توانید یک صف SQS ایجاد کنید، آن را در موضوع مورد نظر مشترک کنید و صف را بخوانید.
دروازه API
اکثر خدمات مستقیماً از طریق اینترنت قابل دسترسی نیستند. دسترسی از طریق API Gateway است که حقوق دسترسی را بررسی می کند. این نیز خدمات ما است و آزمایشاتی نیز برای آن وجود دارد.
اعلان های زمان واقعی
برنامه استفاده می کند
روش تست معروف
تست های واحد مواردی مانند پایگاه داده را با اشیاء ساختگی جایگزین می کنند. اگر یک میکروسرویس، برای مثال، سعی کند رکوردی را در یک جدول با یک کلید خارجی ایجاد کند، و رکورد ارجاع شده توسط آن کلید وجود نداشته باشد، در این صورت درخواست نمی تواند اجرا شود. تست های واحد نمی توانند این را تشخیص دهند.
В
پایگاه داده درون حافظه یکی از DBMS هایی است که توسط Entity Framework پشتیبانی می شود. به طور خاص برای آزمایش ایجاد شده است. داده ها در چنین پایگاه داده ای فقط تا زمانی که فرآیند استفاده از آن خاتمه یابد ذخیره می شود. نیازی به ایجاد جداول ندارد و یکپارچگی داده ها را بررسی نمی کند.
اشیاء ساختگی کلاسی را که جایگزین میکنند مدلسازی میکنند تا حدی که توسعهدهنده آزمایشی نحوه عملکرد آن را بفهمد.
نحوه وادار کردن Postgres به شروع خودکار و انجام مهاجرت هنگام اجرای آزمایش در مقاله مایکروسافت مشخص نشده است. راه حل من این کار را انجام می دهد و علاوه بر این، هیچ کدی را به طور خاص برای آزمایش به خود میکروسرویس اضافه نمی کند.
بیایید به سراغ راه حل برویم
در طول فرآیند توسعه، مشخص شد که تست های واحد برای یافتن به موقع همه مشکلات کافی نیست، بنابراین تصمیم گرفته شد که از زاویه دیگری به این موضوع پرداخته شود.
راه اندازی محیط تست
اولین کار ایجاد یک محیط آزمایشی است. مراحل لازم برای اجرای میکروسرویس:
- سرویس تحت آزمایش را برای محیط محلی پیکربندی کنید، جزئیات اتصال به پایگاه داده و AWS را در متغیرهای محیطی مشخص کنید.
- Postgres را راه اندازی کنید و با اجرای Liquibase مهاجرت را انجام دهید.
در DBMS های رابطه ای، قبل از نوشتن داده ها در پایگاه داده، باید یک طرحواره داده، به عبارت دیگر، جداول ایجاد کنید. هنگام به روز رسانی یک برنامه، جداول باید به فرم استفاده شده توسط نسخه جدید و ترجیحاً بدون از دست دادن داده ها آورده شوند. به این میگن مهاجرت. ایجاد جداول در یک پایگاه داده در ابتدا خالی یک مورد خاص از مهاجرت است. مهاجرت را می توان در خود برنامه ایجاد کرد. هر دو دات نت و نود جی اس فریمورک های مهاجرت دارند. در مورد ما، به دلایل امنیتی، میکروسرویس ها از حق تغییر طرح داده محروم هستند و مهاجرت با استفاده از Liquibase انجام می شود. - آمازون LocalStack را راه اندازی کنید. این پیاده سازی خدمات AWS برای اجرا در خانه است. یک تصویر آماده برای LocalStack در Docker Hub وجود دارد.
- اسکریپت را اجرا کنید تا موجودیت های لازم در LocalStack ایجاد شود. اسکریپت های شل از AWS CLI استفاده می کنند.
برای آزمایش روی پروژه استفاده می شود
تست خودکار چگونه کار می کند؟
در طول آزمایش، همه چیز در Docker کار می کند: سرویس تحت آزمایش، Postgres، ابزار مهاجرت، و Postman، یا بهتر است بگوییم نسخه کنسول آن - Newman.
Docker تعدادی از مشکلات را حل می کند:
- استقلال از پیکربندی میزبان؛
- نصب وابستگی ها: داکر تصاویر را از داکر هاب دانلود می کند.
- بازگرداندن سیستم به حالت اولیه: به سادگی ظروف را بردارید.
Docker-compose کانتینرها را در یک شبکه مجازی جدا شده از اینترنت متحد می کند که در آن کانتینرها یکدیگر را با نام دامنه پیدا می کنند.
تست توسط یک پوسته اسکریپت کنترل می شود. برای اجرای تست در ویندوز از git-bash استفاده می کنیم. بنابراین، یک اسکریپت هم برای ویندوز و هم برای لینوکس کافی است. Git و Docker توسط همه توسعه دهندگان در پروژه نصب شده است. هنگام نصب Git در ویندوز، git-bash نصب می شود، بنابراین همه آن را نیز دارند.
اسکریپت مراحل زیر را انجام می دهد:
- ساخت تصاویر داکر
docker-compose build
- راه اندازی پایگاه داده و LocalStack
docker-compose up -d <контейнер>
- انتقال پایگاه داده و آماده سازی LocalStack
docker-compose run <контейнер>
- راه اندازی سرویس تحت آزمایش
docker-compose up -d <сервис>
- اجرای آزمون (نیومن)
- توقف تمام ظروف
docker-compose down
- ارسال نتایج در Slack
ما یک چت داریم که در آن پیامهایی با علامت سبز یا صلیب قرمز و پیوندی به گزارش ارسال میشوند.
تصاویر Docker زیر در این مراحل نقش دارند:
- سرویس مورد آزمایش همان تصویری است که برای تولید وجود دارد. پیکربندی برای تست از طریق متغیرهای محیطی است.
- برای Postgres، Redis و LocalStack، تصاویر آماده از Docker Hub استفاده می شود. همچنین تصاویر آماده برای Liquibase و Newman وجود دارد. ما اسکلت خود را بر روی اسکلت آنها می سازیم و فایل های خود را به آنجا اضافه می کنیم.
- برای تهیه LocalStack از یک تصویر آماده AWS CLI استفاده می کنید و بر اساس آن یک تصویر حاوی اسکریپت ایجاد می کنید.
با استفاده از
مشکلاتی که ممکن است با آن مواجه شوید
در انتظار آمادگی
هنگامی که یک کانتینر با یک سرویس در حال اجرا است، این بدان معنا نیست که آماده پذیرش اتصالات است. باید منتظر بمانید تا اتصال ادامه یابد.
این مشکل گاهی اوقات با استفاده از یک اسکریپت حل می شود
تصمیم: اسکریپت های تامین کننده LocalStack که منتظر یک پاسخ 200 از SQS و SNS هستند.
تضادهای کار موازی
چندین آزمایش می توانند به طور همزمان روی یک میزبان Docker اجرا شوند، بنابراین نام کانتینر و شبکه باید منحصر به فرد باشد. علاوه بر این، آزمایشهای شاخههای مختلف یک سرویس نیز میتوانند به طور همزمان اجرا شوند، بنابراین نوشتن نام آنها در هر فایل نوشتن کافی نیست.
تصمیم: اسکریپت متغیر COMPOSE_PROJECT_NAME را روی یک مقدار منحصر به فرد تنظیم می کند.
ویژگی های ویندوز
مواردی وجود دارد که میخواهم هنگام استفاده از Docker در ویندوز به آنها اشاره کنم، زیرا این تجربیات برای درک اینکه چرا خطاها رخ میدهند مهم هستند.
- اسکریپت های شل در یک کانتینر باید دارای انتهای خط لینوکس باشند.
نماد پوسته CR یک خطای نحوی است. به سختی می توان از پیام خطا تشخیص داد که این مورد است. هنگام ویرایش چنین اسکریپت هایی در ویندوز، به یک ویرایشگر متن مناسب نیاز دارید. علاوه بر این، سیستم کنترل نسخه باید به درستی پیکربندی شود.
git به این صورت پیکربندی می شود:
git config core.autocrlf input
- Git-bash پوشههای استاندارد لینوکس را شبیهسازی میکند و هنگام فراخوانی یک فایل exe (از جمله docker.exe)، مسیرهای لینوکس مطلق را با مسیرهای ویندوز جایگزین میکند. با این حال، این برای مسیرهایی که در ماشین محلی (یا مسیرهای موجود در یک کانتینر) نیستند، معنی ندارد. این رفتار را نمی توان غیرفعال کرد.
تصمیم: یک اسلش اضافی به ابتدای مسیر اضافه کنید: //bin به جای /bin. لینوکس چنین مسیرهایی را درک می کند؛ برای آن، چندین اسلش با یک اسلش یکسان است. اما git-bash چنین مسیرهایی را نمی شناسد و سعی در تبدیل آنها ندارد.
خروجی ورود به سیستم
هنگام اجرای آزمایشها، میخواهم گزارشهایی را هم از نیومن و هم از سرویس در حال آزمایش ببینم. از آنجایی که رویدادهای این گزارشها به هم مرتبط هستند، ترکیب آنها در یک کنسول بسیار راحتتر از دو فایل جداگانه است. نیومن از طریق راه اندازی می شود اجرای docker-compose، و بنابراین خروجی آن به کنسول ختم می شود. تنها چیزی که باقی می ماند این است که مطمئن شوید که خروجی سرویس نیز به آنجا می رود.
راه حل اصلی انجام بود docker-compose بدون پرچم -d، اما با استفاده از قابلیت های پوسته، این فرآیند را به پس زمینه ارسال کنید:
docker-compose up <service> &
این کار تا زمانی که لازم شد گزارشها از Docker به یک سرویس شخص ثالث ارسال شوند کار میکرد. docker-compose خروجی گزارشها به کنسول متوقف شد. با این حال، تیم کار کرد داکتر پیوست.
تصمیم:
docker attach --no-stdin ${COMPOSE_PROJECT_NAME}_<сервис>_1 &
تضاد شناسه در طول تکرارهای آزمایشی
تست ها در چندین تکرار اجرا می شوند. پایگاه داده پاک نمی شود. رکوردهای موجود در پایگاه داده دارای شناسه های منحصر به فرد هستند. اگر شناسه های خاصی را در درخواست ها بنویسیم، در تکرار دوم دچار تداخل خواهیم شد.
برای جلوگیری از آن، یا شناسه ها باید منحصر به فرد باشند یا تمام اشیاء ایجاد شده توسط آزمایش باید حذف شوند. برخی از اشیاء را نمی توان به دلیل الزامات حذف کرد.
تصمیم: GUID ها را با استفاده از اسکریپت های Postman ایجاد کنید.
var uuid = require('uuid');
var myid = uuid.v4();
pm.environment.set('myUUID', myid);
سپس از نماد در پرس و جو استفاده کنید {{myUUID}}، که با مقدار متغیر جایگزین می شود.
همکاری از طریق LocalStack
اگر سرویس مورد آزمایش در یک صف SQS بخواند یا بنویسد، برای تأیید این موضوع، خود تست نیز باید با این صف کار کند.
تصمیم: درخواست های پستچی به LocalStack.
API خدمات AWS مستند شده است، و اجازه می دهد پرس و جوها بدون SDK انجام شوند.
اگر سرویسی در یک صف بنویسد، آن را می خوانیم و محتوای پیام را بررسی می کنیم.
اگر سرویس به SNS پیام می فرستد، در مرحله آماده سازی LocalStack نیز یک صف ایجاد می کند و در این موضوع SNS مشترک می شود. سپس همه چیز به آنچه در بالا توضیح داده شد ختم می شود.
اگر سرویس نیاز به خواندن پیامی از صف دارد، در مرحله تست قبلی این پیام را در صف می نویسیم.
آزمایش درخواستهای HTTP که از میکروسرویس مورد آزمایش نشات میگیرند
برخی از سرویس ها از طریق HTTP با چیزی غیر از AWS کار می کنند و برخی از ویژگی های AWS در LocalStack پیاده سازی نمی شوند.
تصمیم: در این موارد می تواند کمک کند
آزمایش احراز هویت و مجوز OAuth
ما از OAuth و استفاده می کنیم
تمام تعامل بین سرویس و ارائه دهنده OAuth به دو درخواست کاهش می یابد: اول، پیکربندی درخواست می شود /.well-known/openid-configuration، و سپس کلید عمومی (JWKS) در آدرس پیکربندی درخواست می شود. همه اینها محتوای ثابت است.
تصمیم: ارائه دهنده آزمایشی OAuth ما یک سرور محتوای ثابت و دو فایل روی آن است. توکن یک بار تولید می شود و به Git متعهد می شود.
ویژگی های تست SignalR
پستچی با سوکت های وب کار نمی کند. یک ابزار ویژه برای آزمایش SignalR ایجاد شد.
یک کلاینت SignalR می تواند فراتر از یک مرورگر باشد. یک کتابخانه کلاینت برای آن تحت .NET Core وجود دارد. کلاینت، که در NET Core نوشته شده است، یک اتصال برقرار می کند، احراز هویت می شود و منتظر دنباله خاصی از پیام ها است. اگر پیام غیرمنتظره ای دریافت شود یا اتصال قطع شود، کلاینت با کد 1 خارج می شود. اگر آخرین پیام مورد انتظار دریافت شود، کلاینت با کد 0 خارج می شود.
نیومن به طور همزمان با مشتری کار می کند. چندین مشتری راه اندازی می شوند تا بررسی کنند که پیام ها به همه کسانی که به آنها نیاز دارند تحویل داده می شود.
برای اجرای چندین مشتری از این گزینه استفاده کنید -- مقیاس در خط فرمان docker-compose.
قبل از اجرا، اسکریپت Postman منتظر میماند تا همه مشتریان ارتباط برقرار کنند.
ما قبلاً با مشکل انتظار برای اتصال مواجه شده ایم. اما سرورهایی وجود داشتند و مشتری اینجاست. یک رویکرد متفاوت مورد نیاز است.
تصمیم: مشتری در کانتینر از مکانیزم استفاده می کند
HEALTHCHECK --interval=3s CMD if [ ! -e /healthcheck ]; then false; fi
تیم داکر بازرسی وضعیت عادی، وضعیت سلامت و کد خروج کانتینر را نشان می دهد.
پس از تکمیل نیومن، اسکریپت بررسی می کند که تمام کانتینرهای دارای کلاینت با کد 0 خاتمه یافته باشند.
هاپینس وجود دارد
پس از غلبه بر مشکلاتی که در بالا توضیح داده شد، مجموعهای از تستهای دویدن پایدار داشتیم. در آزمایشها، هر سرویس به عنوان یک واحد کار میکند و با پایگاه داده و آمازون LocalStack تعامل دارد.
این آزمایشها از تیمی متشکل از 30+ توسعهدهندگان در برابر خطاهای یک برنامه کاربردی با تعامل پیچیده 10+ میکروسرویس با استقرار مکرر محافظت میکنند.
منبع: www.habr.com