نحوه ساخت میکروسرویس با Docker – The Orchestration
سلام به همه!
تاکنون در این مجموعه به معماری سطح بالای برنامهای که میسازیم و کد 4 سرویسی که بخشی از برنامه فروشگاهی ساده ما هستند، نگاه کردهایم. و امروز، ما به آن نگاه خواهیم کرد docker-compose برای اجرای کامل برنامه فایل کنید و چندتا درخواست بفرستید تا ببینید رونق میگیره یا نه 💥.
اگر میخواهید مستقیماً به کد بروید، دوباره کد کامل در مخزن Github موجود است.
بیا شروع کنیم
قبل از اینکه شروع کنیم
خوب، پس قبل از شروع، من فقط به سرعت توضیح خواهم داد که چگونه خود را سازماندهی کرده ام docker-compose فایل ها و اینکه چرا کاری را انجام دادم.
من 2 روش برای نوشتن فایل های نوشتن دیدم:
- قرار دادن تمامی سرویس ها در یک فایل واحد.
- گروه بندی منطقی سرویس ها در فایل های جداگانه بر اساس ماهیت آنها.
در رویکرد اول، همانطور که توضیحات نشان می دهد، ما فقط یک مورد را خواهیم داشت docker-compose.yml فایل و اجرای برنامه فوق العاده آسان خواهد بود. فقط انجام بده docker-compose up و بس
اما، من نوشتن سرویسها به این روش را کمی دست و پا گیر یافتم، زیرا باید به یاد داشته باشم که آن را اضافه کنم depends_on گزینه ای در بسیاری از تعاریف سرویس ها به منظور جلوگیری از هرگونه خرابی به دلیل شروع سرویس ها قبل از اجرای وابستگی ها یا Nginx Gateway به دنبال خدمات و یافتن چیزی نیست.
برای اینکه تمام وابستگی ها را به صورت دستی در فایل های کامپوزی اضافه نکنم، تصمیم گرفتم روش دوم را دنبال کنم و هر فایل را تک به تک با ترتیب خاصی اجرا کنم. یک دقیقه دیگر متوجه منظور من خواهید شد.
به این ترتیب، قبل از نوشتن تعریف سرویس، فقط باید یک سوال از خود بپرسیم
آیا این سرویس یک وابستگی به برنامه است، یک چیز راهاندازی/پیشنیاز یا بخشی از کد برنامه (مانند خدمات محصولات یا خدمات سفارشها)
و سپس، تعریف سرویس را در قسمت مربوطه قرار دهید docker-compose فایل.
من شخصاً این رویکرد را در درازمدت از یک POV قابل نگهداری بسیار بهتر می دانم.
و با وجود این موضوع، بیایید این بار به طور واقعی شروع کنیم.
راه اندازی و پیش نیاز
این فایل به مواردی اختصاص داده شده است که حتی قبل از شروع اجرای برنامه باید از آنها مراقبت کنیم. مانند راهاندازی شبکهها، حجمها، مهاجرت و کاشت پایگاههای اطلاعاتی، و غیره.
version: '2'
networks:
shop-intranet:
driver: default
volumes:
shop-data:
و سپس، برای اجرای این فایل
docker-compose --file docker-compose.setup.yml up
وابستگی های کاربردی
هر ابزار/برنامهای که باید قبل از شروع کد برنامه اصلی خود اجرا کنیم، باید در این فایل قرار گیرد، به عنوان مثال: پایگاههای داده، حافظه پنهان، کارگزاران پیام و غیره.
من از این فایل برای شروع استفاده می کنم nats-broker برای انتقال پیام بین سفارشات و خدمات اعلان استفاده می شود.
version: '2'
services:
nats-broker:
image: nats:2.9-alpine
و سپس، برای اجرای این فایل
docker-compose --file docker-compose.deps.yml up
خدمات کاربردی
این فایل برای سرویس های پشتیبان ما است که نوشته ایم. در اینجا، ما تمام خدمات خود را که برنامه را تشکیل می دهند قرار می دهیم. تعریف یک سرویس منفرد به شکل زیر خواهد بود:
auth:
extends:
file: auth/docker-compose.yml
service: app
env_file: .env
و همچنین میتوانیم خدمات را به شبکه و حجمی که قبلاً ایجاد کردهایم مرتبط کنیم، مانند این:
auth:
extends:
file: auth/docker-compose.yml
service: app
env_file: .env
networks:
- shop-intranet
volumes:
- shop-data:/app
و سپس، برای اجرای این فایل
docker-compose --file docker-compose.services.yml up
دروازه Nginx
این فایل حاوی سرور Nginx ما است و یک نقطه ورودی به پشتیبان برنامه ما از اینترنت خارجی باز می کند.
اول، بیایید نگاه کنیم nginx.conf فایلی که پیکربندی سرور خود را در آن قرار داده ایم. بیشتر فایل یک پیکربندی بسیار ابتدایی Nginx است. بنابراین، من بخش های خاص برنامه خود را توضیح می دهم.
به این صورت است که ما یک گروه سرور را تعریف می کنیم.
upstream products {
server microservices-shop_products_1:4000;
}
گروه سرور راهی برای نامگذاری یک آدرس (IP/نام میزبان و شماره پورت) است تا بتوانیم بعداً از آن در پیکربندی باقیمانده خود استفاده کنیم. ما در یک لحظه نگاه خواهیم کرد که چگونه می توانیم از آن استفاده کنیم.
در اینجا نام میزبان نام کانتینر در حال اجرا و 4000 شماره پورت داخل آن کانتینر است.
ما میتوانیم گروههای سرور دیگر را برای خدمات سفارشات و محصولات دقیقاً به همین روش تعریف کنیم.
سپس می توانیم مسیریابی خدمات خود را تعریف کنیم. برای آن ما یک بلوک مکان تعریف می کنیم. بیایید با ساده ترین مورد شروع کنیم.
location ^~ /products/ {
proxy_pass http://products/;
}
اینجا، /products/ پیشوند مسیر درخواست های ورودی و در خط دوم است products نام گروه سروری است که قبلا تعریف کردیم. بنابراین، این قطعه پیکربندی به این معنی است که هر درخواستی وارد می شود /products/<anything>، آنها را به گروه سرور نامگذاری شده ارسال کنید products. این proxy_pass دستورالعملی که برای فوروارد درخواست ها استفاده می شود از این طریق است.
یک چیز دیگر، قبل از ارسال درخواست، Nginx پیشوند مسیر را از درخواست حذف می کند، بنابراین یک ورودی /products/search درخواست فقط به خدمات محصولات می رسد /search
سپس گروه سرور سرویس سفارشات را داریم.
location ^~ /orders/ {
auth_request /auth/verify;
proxy_pass http://orders/;
}
چیز جدید اینجاست auth_request /auth/verify;. این خط به این معنی است که قبل از ارسال درخواست به orders گروه سرور، احراز هویت کاربر را با استفاده از /auth/verify نقطه پایانی بنابراین، Nginx به صورت داخلی درخواستی را به سرویس auth ارسال می کند، و اگر پاسخ داده شود 200 OK، سپس درخواست به سرویس سفارشات ارسال می شود و اگر پاسخ داده شود 401 Unauthorized، سپس Nginx نیز a را به عقب می فرستد 401 به درخواست کننده
در نهایت، سخت ترین مورد برای من، گروه سرور auth بود
location ^~ /auth/ {
internal;
proxy_pass http://auth/;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original_URI $request_uri;
proxy_set_header Authorization $http_authorization;
}
این internal در شروع به این معنی است که این مکان فقط توسط Nginx در داخل قابل دسترسی است. اگر درخواستی داشته باشیم /auth/anything، ما برمی گردیم a 404 Not Found واکنش.
سپس، یک دسته از چیزهای بیهوده به نظر می رسد. بیایید یک به یک به معنی هر خط نگاه کنیم:
-
proxy_pass_request_body off;– Nginx قبل از ارسال درخواست، بدنه درخواست را حذف می کند. -
proxy_set_header Content-Length "";– تنظیمContent-Lengthسربرگ تا خالی از آنجایی که بدنه درخواست را حذف کردهایم، داشتن طول محتوای غیر صفر فایدهای ندارد. -
proxy_set_header X-Original_URI $request_uri;– تنظیم کنیدX-Original_URIسربرگ به$request_uri، که متغیری است که توسط Nginx ارائه شده است و مقدار URL درخواست فعلی است. این برای این است که Nginx بداند پس از تأیید اعتبار، کجا درخواست را ارسال کند. -
Authorization $http_authorization– تنظیمAuthorizationسربرگ، زیرا این چیزی است که ما باید در سرویس احراز هویت تأیید کنیم.
این برای nginx.conf فایل. حالا به سراغ آخرین قطعه پازل می رویم.
در اینجا تعریف سرویس برای سرور Nginx آمده است.
version: '2'
services:
nginx:
image: nginx:1.25-alpine
ports:
- 80:80
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ما در حال نقشه برداری از بندر محلی هستیم 80 به بندر 80 در ظرف Nginx و volumes بخش برای جایگزینی استفاده می شود /etc/nginx/nginx.conf داخل ظرف با محلی ما nginx.conf فایل، زیرا Nginx به صورت پیشفرض به دنبال پیکربندی میگردد.
این :ro در پایان ساخت آن است readonly برای جلوگیری از هرگونه تغییر در فایل محلی از داخل کانتینر. به عبارت ساده، ظرف می تواند فایل را بخواند، اما نمی تواند هیچ تغییری در آن ایجاد کند.
و سپس، برای اجرای این فایل
docker-compose --file docker-compose.gateway.yml up
و وویلا! 🤌 بالاخره موفق شدیم اپلیکیشن مبتنی بر میکروسرویس خود را اجرا کنیم.
بیایید چند درخواست داشته باشیم
GET /products/?ids=
توجه: ما می توانیم از آن بگذریم
idsپارامتر پرس و جو برای دریافت همه محصولات.
curl http://localhost/products/?ids=2,5
واکنش:
[
{
"id": "2",
"name": "Half Sleeve Shirt",
"price": 15,
"keywords": [
"shirt",
"topwear"
]
},
{
"id": "5",
"name": "Sunglasses",
"price": 25,
"keywords": [
"accessories",
"sunglasses"
]
}
]
POST /orders/
curl http://localhost/orders/ \
--header 'Authorization: secret-auth-token' \
--header 'Content-Type: application/json' \
--data '{
"userId": "saini-g",
"productIds": ["2", "4"]
}'
واکنش:
{
"productIds": [
"2",
"4"
],
"totalAmount": 33,
"userId": "saini-g",
"id": 2487
}
همچنین، ما گزارشهایی را در کنسول دریافت میکنیم تا بررسی کنیم که همه چیز طبق انتظار اجرا میشود:

این همه برای امروز بود. طولانی بود، بنابراین بسیار سپاسگزارم و به همه کسانی که تا پایان کار ماندند تبریک می گویم.
اما صبر کن! من یک جایزه کوچک برای همه شما زبان آموزان خوب دارم 🤩. اما برای این باید منتظر قسمت بعدی باشید 😛.
امیدوارم از این سریال لذت برده باشید و چیزهای جدیدی یاد گرفته باشید.
با خیال راحت هر سوالی دارید در نظرات زیر مطرح کنید.
به سلامتی!



