نحوه ساخت میکروسرویس با 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
}
همچنین، ما گزارشهایی را در کنسول دریافت میکنیم تا بررسی کنیم که همه چیز طبق انتظار اجرا میشود:
این همه برای امروز بود. طولانی بود، بنابراین بسیار سپاسگزارم و به همه کسانی که تا پایان کار ماندند تبریک می گویم.
اما صبر کن! من یک جایزه کوچک برای همه شما زبان آموزان خوب دارم 🤩. اما برای این باید منتظر قسمت بعدی باشید 😛.
امیدوارم از این سریال لذت برده باشید و چیزهای جدیدی یاد گرفته باشید.
با خیال راحت هر سوالی دارید در نظرات زیر مطرح کنید.
به سلامتی!