مسیری که طی میشود تا به یک وبسایت برسیم
ابتدا قرار بود این مقاله متنی باشه برای توضیح تقسیم بار در سیستم توزیع شده کوبرنتیز1 و چالش هایی که پیش روی ما میزاره. اما لازم دیدم قبل از اینکه به بحث اصلی بپردازم از جزئیات سیر و سفری که یک مشتری 2 و تعاملش با کارساز3 صحبت کنم این مقاله میتونه برای توسعه دهنده ها و مدیران سیستم و تمامی شاخه های زیرساخت و توسعه نرم افزار جذاب باشه.
نکته: برای اینکه مقاله خاک نخوره تا جایی که نوشتم رو منتشر میکنم و سعی میکنم که تو هفته های آینده بخش های دیگه رو اضافه کنم بخش های باقی مونده شامل
- مقایسه ای بین الگوریتم های مختلف توزیع بار
- مقایسه ای بین لودبالانسر های معروف مثل nginx و haproxy و envoy راهکارهای فعلی تقسیم بار تو کوبرنتیز که شامل ترافیک خروجی و ترافیک ورودی میشه
- مقایسه رویکرد های مختلف برای توزیع بار بین multi-cluster
چند تا چالش اساسی درمورد ساختار اینترنت و وب اپلیکیشن ها هستن که به عنوان یه مهندس سیستم تو حوزه زیرساخت همیشه چالش برانگیز و پر از جزئیات مهم هستن این چالش ها شامل مقیاس پذیری یعنی اینکه سیستم ما چقدر گنجایش یا ظرفیت بزرگ شدن و به چه سرعتی رو داره چالش تاب آوری یعنی اینکه چقدر میتونه در مقابل خطاهای نرم افزاری یا از دسترس خارج شدن سخت افزار تحمل داشته باشه و چالش های دیگه ای که تو مقالات دیگه به تفصیل درموردشون صحبت شده ولی تمرکز من اینجا بیشتر روی توزیعبارLoadBalacning تو سیستم های توزیع شده به خصوص Kubernetes هستش
اولش بزارین یه مقدار درمورد اینکه چه فرایندی اتفاق میفته تا درخواست یه کاربر به دست کارساز ما برسه درنهایت پاسخ به سمت کاربر ارسال میشه صحبت کنیم
ایجاد یک درخواست HTTP
مرحله اول درخواست DNS4 که کاربر به DNS سروری که روی سیستم عاملش resolve.conf یا به صورت دیفالت روی شبکه محلیش 5 تعبیه شده درخواستش رو ارسال میکنه این DNS server مثلا میتونه 8.8.8.8 یا 8.8.4.4 باشه برای مثال این نتیجه درخواست dns به وسیله ابزار dig به سمت behroozam.com
dig @8.8.8.8 behroozam.com +short
188.114.97.1
188.114.96.1
2- مرحله دوم ساختن یه http request6 هستش ما قراردادهای ارتباطی دیگه ای مثل quic هم داریم ولی به خاطر اینکه HTTP روی بستر TCP و مدت خیلی زیادی که استاندارده از HTTP برای این مسیر رفت و برگشت درخواست و پاسخ استفاده میکنیم هرچند واقعا توصیه میکنم درمورد quic بخونید چون آینده حداقل ارتباط بین سرویس ها و مروگرهای مدرن به دلیل اینکه UDP خیلی قابل اتکا شده و ارتباطات سریعتر و بهتره شده به نظر من روی quic خواهد بود.
برای ساختن درخواست http من تصور میکنم که کاربر برای مثال از curl استفاده میکنه به این صورت
domain=behroozam.com
host_ip=$(dig @8.8.8.8 +short -p 53 $domain | awk 'NR==1{print $0}')
curl -H "Host: $domain" $host_ip
تو این مرحله ما به اون DNS A record که ایپی github pages هست یه درخواست HTTP رو ارسال میکنیم تفاوت این قضیه با زمانی که به صورت عادی curl behroozam.com بکنیم تو این هستش که به صورت استاندارد ابزار curl میاد و fqdn رو توی هدر Host قرار میده ولی اینجا به دلیل آموزشی بودن مقاله من سعی میکنم همه چیز رو با جزئیاتش جلو برم و چیزی رو به استاندارد ها واگذار نکنم
خب اگه اینجا رو بخوایم با مدل TCP/IP شرح بدیم تو لایه 4 یا application ما یه درخواست DNS ایجاد کردیم اگه علاقهمند هستین میتونید جزیات پیام DNS رو اینجا مطالعه کنید. انواع و اقسام مختفلی از dns record ها وجود داره و این پروتکول درواقع به یکی از مهم ترین پروتکول های اینترنت تبدیل شده پس دانش نسبت به مهم ترین record ها مثل CNMAE و A record الزامیه.
تو لایه 3 که transport هستش ما به port 53 آیپی 8.8.8.8 به وسیله ابزارdig پیام درخواست DNS رو ارسال میکنیم تفاوت بین دو پرتکول udp و tcp رو میتونید اینجا تو این ویدئو بسیار خوب به زبان فارسی ببینید.
لایه ۲ و ۱ که لایه شبکه و لینک فیزیکی هستش یه مقداری جزئیات زیادی داره اگه علاقهمند هستین که بدونید اون وسط چه اتفاقی میفته که به سمت host_ip مسیریابی7 اتفاق میفته و پکت ارسال میشه و دریافت میشه توصیه میکنم دوره network+ رو یه مروری بکنید ولی به صورت خلاصه چون بازه ایپی host_ip تو بازه ipv4 های رجسیتر شده اینترنتی هستش ( به صورت عامه میگن ایپی ولید ولی این تعریف درستی نیست درواقع public ip تو استاندارد ICANN ) سیستم عامل برای مسیر یابی درخواست ما رو به سمت default route ارسال میکنه این پکت های ip یه source ip دارن که مشخص میکنه که ارسال کننده این پیام کامپیوتر ما هستش و یه destination ip که درواقع host_ip هستش این وسط تو اینترنت کلی این پکت ما میچرخه با پرتکول های مختلف مسیریابی و source nat و destination nat تا درنهایت به دست کارساز میرسه
3- تو کارساز همه اون مراحلی که بالا برای ساختن یه http request انجام شد حالا به صورت معکوس اتفاق میفته یعنی از لایه 1 این بار به لایه 4 برای اینکه سناریو خیلی ساده نباشه من اینجا معماری یه سیستم high available رو تصور میکنم که ما بین دوتا لودبالانسر keepalived داریم و این دو تا به عنوان A رکورد پشت dns server ما هستن و هرکدوم از این لودبالانسر ها به backend های یکسانی متصل میشه
مابقی موارد درمورد سیستم دیزاین مثل اینکه کجا از cache یا key value store یا message broker یا sharded database استفاده میکنیم رو بعدا میشه تو مقالات جداگانه ای بهشون اشاره کرد
به هر صورت اولین ریکوئستی که به سمت سرور ما میاد غالبا HTTPS و با اکستنشن tls v1.3 هستش یعنی چه ؟ یعنی اگه سه مرحله اولی که packet لایه فیزیکال به پکت ip ترجمه شد و بعد از اون نشست اولیه TCP یعنی SYN و SYN ACK و ACK بین مشتری client و کارساز server برقرار شد حالا یه مرحله جانبی اتفاق میفته که تو مدل TCP/IP معمولا بهش میگن لایه 3.5 یا تو مدل OSI بهش میگن presentation layer تو این مرحله اگه کارساز از ciphersuite که مشتری فرستاده پشتیبانی کنه و همه چیز بر وفق مراد باشه یعنی از نظر امنیتی کارساز ورژن tls و ciphersuit رو تایید کنه بین مشتری و کارساز یه key exchange اتفاق میفته که درنهایت به ساختن یه symmetric key منتهی میشه که برای ارتباط امن و رمزنگاری شده بین مشتری و کارساز استفاده میشه پیام اولیه ای که مشتری برای کارساز میفرسته clienthello هستش که شامل اطلاعاتی مثل SNI میشه این SNI درواقع FQDN دامنه ای هستش که مشتری درخواست اتصال رو بهش داده علتی که از SNI استفاده میشه اینه که بشه روی یه کارساز از چند domain مختلف پشتیبانی کرد یعنی پروتکول tls از SNI استفاده میکنه تا در جواب clienthello کلید domain که تو clienthello بوده رو برگردونه برای خوندن جزئیات اینکه اینجا چه اتفاقی افتاد خوندن این مقاله رو حتما پیشنهاد میکنم چون این دانش درمورد خطایابی HTTPS خیلی میتونه به ما تو شرایط مختلف کمک کنه
حالا سوال اینه که این مرحله 3.5 توسط چه نرم افزاری یا کجای سیستم عامل اتفاق میفته بزارین یکم شیرجه بزنیم تو مفاهیم شبکه و سیستم عامل
وقتی یه پکت لایه دو یا فیزیکال به سمت کارساز ما میاد یعنی به فرض اگر ما با یه ethernet connection به یه switch متصل هستیم این پکت دنبال کارساز ما با MAC ادرسی میگرده که توی destination عه frame اون خورده این پکت میاد تو کارت شبکه کارساز ما که destionation MAC رو داره چطوری ؟ Switch ما میاد و یه frame رو به همه دستگاه های روشن تو شبکه میفرسته به اصطلاح broadcast میکنه به اسم arp packet که وقتی دستگاه ما این پیام رو دریافت میکنه تو پاسخش ip address و MAC ادرس خودش رو برای switch میفرسته اینشکلی سویچ ما میفهمه که باید کجا پیامش رو ارسال کنه و درواقع ip رو به MAC مپ map میکنه.
من تصور میکنم کارساز ما اینجا یه کارساز گنو/لینوکسی هستش
کارت شبکه کارساز frame تحویل گرفته شده رو تحویل kernel میده کرنل هم میاد اینو تحویل iptables تو لایه user-space میده
تفاوت user-space و kernel space تو اینه که kernel space یه فضای virtual memory رزرو شده برای سیستم عامل هستش که اپلیکیشن های کاربردی تو لایه user-space حق دسترسی رو به اون ندارن و این kernel که اگه اپلیکیشی تو لایه user-space درخواستی برای دسترسی به سخت افزار داشته باشه رو میده یا نمیده ولی به صورت کلی syscal ها یا پیام های سیستمی جهت مدیریت سخت افزار تو kernel space اتفاق میفته و مابقی چیزا مثل اپلیکیشن های کاربردی تو user-space
میرسیم به بحث بزرگ و البته خیلی مهم iptables درواقع ما اگه یه فهم درستی از iptables داشته باشیم مابقی firewall ها و سیستم هایی که packet manipulation رو انجام میدن برای ما ساده میشه هرچند که تو یه ستون پنج خطی واقعا نمیشه به همه کارهایی که iptable انجام میده اشاره کرد ولی من به صورت کوتاه و مختصر یکی از کارایی که میشه انجام داد و نحوه پاسخ دادن iptables به اون رو شرح میدم
تو این مرحله پکت دریافتی باید از هفت خوان رستم عه iptables عبور کنه اینجوری که iptables در مرحله اول پکت خامی رو که kernel تحویل گرفته رو تو prerouting برسی میکنه اگه با rule nat مطابقت داشت میفرسته برای forward chain در غیر این صورت برای input ارسال میکنه و اگر تو input chain هم rule ای جهت drop یا reject کردن packet نباشه برای user-space و اپلیکیشنی که روی اون port listern کرده ارسال میکنه تو دیاگرام پایین به صورت کامل میتونید فرایند هایی که اتفاق میفته رو مشاهده کنید
مثال عملیش اینجوری میشه که اگه شما روی کارساز خودتون برای مثال این کامند رو اجرا کنید
iptables-legacy -A INPUT -p tcp --dport 8000 -j REJECT
اگه هر کلاینتی با هر source ip برای پورت 8000 یه درخواست http رو بفرسته پیامی رو دریافت نمیکنه با اینکه یه اپلیکیشن داره به پورت 8000 گوش میده. تفاوت این کامند با زمانی که DROP میکنیم پکت رو اینه که تو REJECT یه EMPTY RESPONSE به سمت کلاینت فرستاده میشه که tcp connection رو قطع میکنه ولی تو DROP کلاینت بخت برگشته هیچ جوابی رو دریافت نمیکنه و تا زمانی که tcp timeout نشده منتظر جواب باقی میمونه.
حالا برگردیم دوباره به اون سوال بالا که چه کسی مسئول مرحله 3.5 یعنی لایه نشست هستش و این کار رو انجام میده تا اینجای کار دیدیم که frame لایه دو شبکه به packet تبدیل شد پکت راهش رو از میان iptables به سمت اپلیکیشن پیدا کرد و حالا اینجا اپلیکیشن که loadbalancer ما باشه محتوای پکت دریافتی رو تفسیر میکنه اگر که http باشه مستقیما پردازشش میکنه و اگر HTTPS باشه اول SNI رو چک میکنه و بعد ادامه ماجراهایی که تو ssl handshake و تبادل کلید نامتقارن و متقارن بالا دیدیم.