برنامه نویسی

نحوه ساخت میکروسرویس با Docker – The Orchestration

سلام به همه!
تاکنون در این مجموعه به معماری سطح بالای برنامه‌ای که می‌سازیم و کد 4 سرویسی که بخشی از برنامه فروشگاهی ساده ما هستند، نگاه کرده‌ایم. و امروز، ما به آن نگاه خواهیم کرد docker-compose برای اجرای کامل برنامه فایل کنید و چندتا درخواست بفرستید تا ببینید رونق میگیره یا نه 💥.

اگر می‌خواهید مستقیماً به کد بروید، دوباره کد کامل در مخزن Github موجود است.

بیا شروع کنیم


قبل از اینکه شروع کنیم

خوب، پس قبل از شروع، من فقط به سرعت توضیح خواهم داد که چگونه خود را سازماندهی کرده ام docker-compose فایل ها و اینکه چرا کاری را انجام دادم.

من 2 روش برای نوشتن فایل های نوشتن دیدم:

  1. قرار دادن تمامی سرویس ها در یک فایل واحد.
  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
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همچنین، ما گزارش‌هایی را در کنسول دریافت می‌کنیم تا بررسی کنیم که همه چیز طبق انتظار اجرا می‌شود:

ثبت سفارش جدید ثبت شده است


این همه برای امروز بود. طولانی بود، بنابراین بسیار سپاسگزارم و به همه کسانی که تا پایان کار ماندند تبریک می گویم.

اما صبر کن! من یک جایزه کوچک برای همه شما زبان آموزان خوب دارم 🤩. اما برای این باید منتظر قسمت بعدی باشید 😛.

امیدوارم از این سریال لذت برده باشید و چیزهای جدیدی یاد گرفته باشید.

با خیال راحت هر سوالی دارید در نظرات زیر مطرح کنید.

به سلامتی!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا