ویرگول
ورودثبت نام
فرهاد صادقی
فرهاد صادقیمهندس نرم افزار، طراحی و راه اندازی سیستم های نرم افزاری بر پایه معماری میکروسرویس
فرهاد صادقی
فرهاد صادقی
خواندن ۱۶ دقیقه·۶ روز پیش

آموزش Kubernetes - قسمت هشتم (Deployment and Node Assignment)

Labels

Label ها Key/Value pair هستند. در کوبرنتیز به هر آبجکتی می توان لیبل چسباند.

با لیبل می توانیم آبجکت ها را دسته بندی کنیم. مثلا اگر ما یک برنامه داریم که از دو قسمت Backend و Frontend تشکیل شده است. می توانیم برای همه پادهای Backend آن برنامه لیبل Backend و برای همه پادهای Fronend آن لیبل Frontend بزنیم.

برای مشاهده لیبل ها، کافی است به انتهای دستور عبارت show-labels را اضافه کنیم.

[root@MASTER-01 ~]# kubectl get po --show-labels NAME READY STATUS RESTARTS AGE LABELS mywebapp-9qsx8 1/1 Running 0 4m3s app=nginx mywebapp-hhpkh 1/1 Running 0 2d10h app=nginx nginx-1 1/1 Running 0 145m app=web nginx-2 1/1 Running 0 142m app=web webapp-56nnr 1/1 Running 1 (2d10h ago) 44d app=nginx,controller-revision-hash=5597cb68cb,pod-template-generation=1 webapp-5bcxx 1/1 Running 0 2d8h app=nginx,controller-revision-hash=5597cb68cb,pod-template-generation=1 webapp-fbd9ddfc7-5xdqn 1/1 Running 0 2d10h app=nginx,pod-template-hash=fbd9ddfc7 webapp-fbd9ddfc7-f88gt 1/1 Running 0 2d10h app=nginx,pod-template-hash=fbd9ddfc7 webapp-fbd9ddfc7-wbzlf 1/1 Running 0 2d10h app=nginx,pod-template-hash=fbd9ddfc7

انتخاب پادهایی که دارای لیبل app=web هستند:

[root@MASTER-01 ~]# kubectl get po --selector=app=web NAME READY STATUS RESTARTS AGE nginx-1 1/1 Running 0 146m nginx-2 1/1 Running 0 143m

انتخاب پادهایی که دارای لیبل app=web یا app=web2 هستند:

[root@MASTER-01 ~]# kubectl get po --selector="app in (web, web2)" NAME READY STATUS RESTARTS AGE nginx-1 1/1 Running 0 146m nginx-2 1/1 Running 0 143m nginx-3 1/1 Running 0 143m

ثبت لیبل با دستور kubectl label [resourcetype] [resourcename] [key=value] انجام می شود. به عنوان مثال دستور زیر لیبل hdd=ssd را برای نود worker-02 ثبت می کند.

[root@MASTER-01 ~]# kubectl label node worker-02 hdd=ssd node/worker-02 labeled

ReplicaSet

وقتی ما یک پاد بالا می آوریم هیچ کنترل کننده ای برای آن پاد وجود ندارد که سلامت پاد را بررسی کند یا وقتی آن پاد حذف شد مجددا یک نسخه از آن بالا بیاورد.

ReplicaSet همان مفهوم بالادستی برای پاد است.

یعنی بجای اینکه ما یک منیفست پاد تهیه کنیم و آن را در کوبرنتیز بالا بیاوریم، یک کنترل کننده پاد تعریف می کنیم و آن کنترل کننده به تعداد نسخه هایی که برایش مشخص کرده ایم پاد بالا می آورد.

Replicaset یکی از منابع تعریف شده در کوبرنتیز می باشد.

[root@MASTER-01 ~]# kubectl api-resources | grep replicaset replicasets rs apps/v1 true ReplicaSet

بیایید یک mainifest برای اولین replicaset خود بنویسیم:

replicaset.yaml

apiVersion: apps/v1 kind: ReplicaSet metadata: name: mywebapp labels: app: v10.1.1 spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: docker.arvancloud.ir/nginx:latest imagePullPolicy: IfNotPresent

replicas : تعداد نسخه هایی از پاد می باشد که میخواهیم همزمان داشته باشیم

selector : نحوه انتخاب پادهای زیر replicaset را مشخص می کند. یعنی چه پادهایی در کنترل replicase هستند.

template : یک قالب برای پادهای زیرمجموعه replicaset مشخص می کند. نحوه تعریف آن دقیقا شبیه تعریف پاد می باشد با این تفاوت که نباید برای آن نام تعریف شود. همچنین هر آنچه در selector مشخص شده است باید در این قسمت برای پاد در نظرگرفته شود تا هر یک از پادهایی که ایجاد می شوند در زمره زیرمجموعه های replicaset قرار بگیرند.

[root@MASTER-01 ~]# kubectl apply -f replicaset.yaml replicaset.apps/mywebapp created [root@MASTER-01 ~]# kubectl get rs NAME DESIRED CURRENT READY AGE mywebapp 2 2 2 36s

همانطور که مشاهده می کنید مانند سایر منابع، ما می توانیم لیست replicaset های موجود را مشاهده نماییم.

DESIRED و CURRENT و READY به ترتیب تعداد پادهای تعیین شده در replicaset، تعداد پادهای فعلی اجرا شده و تعداد پادهای اجرا شده بدون مشکل را نمایش می دهد.

[root@MASTER-01 ~]# kubectl get po NAME READY STATUS RESTARTS AGE mywebapp-5nb6h 1/1 Running 0 5m27s mywebapp-ksb8l 1/1 Running 0 5m26s

بعد از apply کردن replicaset، دو پاد مطابق با template اجرا می شود. همانطور که مشاهده می کند نام پادها برابر با نام replicaset و یک رشته رندوم می باشد.

Pod_Name = Replicaset_name + Random_String

حالا اگر ما یکی از پادها را حذف کنیم، replicaset بلافاصله یک پاد دیگر را بعنوان جایگزین آن بالا می آورد تا همواره 2 پاد (به تعداد replicas) بالا باشد.

[root@MASTER-01 ~]# kubectl delete po mywebapp-5nb6h pod "mywebapp-5nb6h" deleted from default namespace [root@MASTER-01 ~]# kubectl get po NAME READY STATUS RESTARTS AGE mywebapp-jms8t 1/1 Running 0 15s mywebapp-ksb8l 1/1 Running 0 14m

اگر برنامه ما از چند قسمت تشکیل شده باشد؛ مثلا از front و back تشکیل شده باشد، باید به ازای هر کدام از آن بخش ها یک ReplicaSet بالا بیاوریم.

هر چند که برای کاهش یا افزایش تعداد پادها بهتر است فایل manifest را تغییر داده و مجددا apply کنیم، ولی می توانیم با دستور scale هم این کار را انجام دهیم.

[root@MASTER-01 ~]# kubectl scale replicaset mywebapp --replicas=3 replicaset.apps/mywebapp scaled [root@MASTER-01 ~]# kubectl get po NAME READY STATUS RESTARTS AGE mywebapp-jms8t 1/1 Running 0 8h mywebapp-ksb8l 1/1 Running 0 8h mywebapp-q78fm 1/1 Running 0 6s

برخی مواقع نیاز است تا تعداد پادها صفر شود تا هیچ پادی از برنامه بالا نباشد. به عنوان مثال ما اگر بخواهیم یک نرم افزار را بروزرسانی کنیم و در طول مدت بروزرسانی هیچ درخواستی سمت دیتابیس ثبت نشود، باید ابتدا تعداد پادهای دیتابیس را به صفر scale کنیم و بعد از انجام کار، مجددا تعداد پادها را به تعداد قبلی برگردانیم.

توجه داشته باشید که حتی زمانی که عدد replicas صفر می شود، درست است که هیچ پادی بالا نیست اما همچنان ReplicaSet فعال است.

[root@MASTER-01 ~]# kubectl scale replicaset mywebapp --replicas=0 replicaset.apps/mywebapp scaled [root@MASTER-01 ~]# kubectl get po NAME READY STATUS RESTARTS AGE mywebapp-jms8t 1/1 Terminating 0 8h mywebapp-ksb8l 1/1 Terminating 0 8h mywebapp-q78fm 1/1 Terminating 0 74s [root@MASTER-01 ~]# kubectl get po NAME READY STATUS RESTARTS AGE

وقتی ما با دستور scale تعداد replicas را تغییر می دهیم، فایل منیفست تغییر نمی کند. برای دریافت فایل منیفست از ReplicaSet موجود، از دستور زیر می توان استفاده کرد:

[root@MASTER-01 ~]# kubectl get rs mywebapp -o yaml > replicaset.yaml

حذف ReplicaSet:

[root@MASTER-01 ~]# kubectl get po NAME READY STATUS RESTARTS AGE mywebapp-2689w 1/1 Running 0 33m mywebapp-b4gbj 1/1 Running 0 33m mywebapp-lzk4d 1/1 Running 0 33m [root@MASTER-01 ~]# kubectl delete rs mywebapp replicaset.apps "mywebapp" deleted from default namespace [root@MASTER-01 ~]# kubectl get po NAME READY STATUS RESTARTS AGE

همانطور که ملاحظه می کنید، با حذف یک ReplicaSet همه پادهای زیر مجموعه آن هم حذف می شوند.


Deployment

[root@MASTER-01 ~]# kubectl api-resources | grep deployment deployments deploy apps/v1 true Deployment

ما معمولا از Replicaset بصورت مستقیم استفاده نمی کنیم و بجای آن از Deployment استفاده می کنیم.

اگر برنامه ما همواره بروزرسانی می شود و نسخه جدید آن باید منتشر شود، می بایست از Deployment استفاده شود.

فرض کنید ما یک ReplicaSet از نسخه 1.0.0 برنامه backend داشته باشیم و بخواهیم نسخه برنامه را در Replicaset به 2.0.0 تغییر دهیم.

برای این کار ما نیاز به یک کنترل کننده بالادستی ReplicaSet داریم تا عمل آپدیت را انجام دهد.

Deployment این کار را انجام می دهد. Deployment در دل خود یک ReplicaSet از برنامه بالا می آورد. وقتی ما نسخه برنامه را از 1.0.0 به 2.0.0 تغییر می دهیم، Deployment به آرامی تعداد replicas در ReplicaSet فعلی را کم می کند و تعداد replicas در ReplicaSet جدید را افزایش می دهد. این فرآیند تا زمانی که تعداد replicas در ReplicaSet قدیمی صفر شود و همه آنها به ReplicaSet جدید منتقل شوند ادامه پیدا می کند.

حالا سوال اینجاست که چرا ما آپدیت نسخه برنامه را در ReplicaSet انجام نمی دهیم؟ جواب این است که در این صورت خیلی از قابلیت هایی که Deployment ارائه می دهد را از دست می دهیم. به عنوان مثال:

  • فرآیند بروز رسانی به مرور و بدون DownTime انجام نخواهد شد.

  • قابلیت برگشت به نسخه قبلی ReplicaSet را نخواهیم داشت. (Deployment تا 10 نسخه قبلی ReplicaSet خود را نگه می دارد و هر زمان نیاز باشد می توان به هر یک از آنها برگردیم.)

بیایید یک manifest برای اولین deployment خود بنویسیم:

deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata: name: webapp labels: app: v10.1.1 spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: docker.arvancloud.ir/nginx:1.14.1 imagePullPolicy: IfNotPresent

[root@MASTER-01 ~]# kubectl get po NAME READY STATUS RESTARTS AGE [root@MASTER-01 ~]# kubectl apply -f deployment.yaml deployment.apps/webapp created [root@MASTER-01 ~]# kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE webapp 3/3 3 3 37m [root@MASTER-01 ~]# kubectl get rs NAME DESIRED CURRENT READY AGE webapp-fbd9ddfc7 3 3 1 19s [root@MASTER-01 ~]# kubectl get po NAME READY STATUS RESTARTS AGE webapp-fbd9ddfc7-fkbf4 1/1 Running 0 38s webapp-fbd9ddfc7-hdjwj 1/1 Running 0 38s webapp-fbd9ddfc7-n98gv 1/1 Running 0 38s

وقتی ما فایل مانیفست deployment را apply می کنیم، کوبرنتیز یک Deployment می سازد و در دل آن یک ReplicaSet ساخته و پادها را داخل آن ReplicaSet بالا می آورد.

اگر به نحوه نام گذاری این منابع دقت کنید متوجه می شوید نام گذاری آنها با قوانین زیر انجام می شود:

Replicaset_Name = Deployment_Name + Random_String

Pod_Name = Replicaset_Name + Random_String

نحوه scale کردن در Deployment همانند ReplicaSet می باشد و همان قوانین در اینجا هم حاکم است:

[root@MASTER-01 ~]# kubectl scale deployment webapp --replicas=2 deployment.apps/webapp scaled [root@MASTER-01 ~]# kubectl get po NAME READY STATUS RESTARTS AGE webapp-fbd9ddfc7-fkbf4 1/1 Running 0 47m webapp-fbd9ddfc7-hdjwj 1/1 Running 0 47m

Workload/ Workload Management: لغتی است که به هر یک از ReplicaSetها، Deploymentها، StatefullSetها و ... اطلاق می شود.

[root@MASTER-01 ~]# kubectl get po --show-labels NAME READY STATUS RESTARTS AGE LABELS webapp-fbd9ddfc7-fkbf4 1/1 Running 0 4h40m app=nginx,pod-template-hash=fbd9ddfc7 webapp-fbd9ddfc7-fs257 0/1 Pending 0 1s app=nginx,pod-template-hash=fbd9ddfc7 webapp-fbd9ddfc7-hdjwj 1/1 Running 0 4h40m app=nginx,pod-template-hash=fbd9ddfc7 webapp-fbd9ddfc7-x4qpz 0/1 Pending 0 1s app=nginx,pod-template-hash=fbd9ddfc7

همانطور که مشاهده می کنید لیبل های زیر به همه پادهای Deployment اختصاص داده شده است:

app=nginx

pod-template-hash=fbd9ddfc7

همانطور که گفته شد هر بار که ما فایل مانیفست deployment را تغییر داده و apply میکنیم، کوبرنتیز یک ReplicaSet جدید می سازد و فرآیند حذف یکی یکی پادها از ReplicaSet قدیمی و بالا آوردن پاد جدید در ReplicaSet جدید را انجام میدهد. کوبر همیشه تعداد ده ReplicaSet آخر را نگه می دارد.

[root@MASTER-01 ~]# kubectl apply -f deployment.yaml deployment.apps/webapp configured [root@MASTER-01 ~]# kubectl get rs NAME DESIRED CURRENT READY AGE webapp-dffd67d4c 1 1 0 5s webapp-fbd9ddfc7 3 3 3 4h58m

ما علاوه بر تغییر فایل مانیفست و اعمال آن در کوبرنتیز، می توانیم تغییرات خود در deployment را به صورت اجرای دستور کوبرنتیز نیز اعمال نماییم:

[root@MASTER-01]# kubectl set image deployments webapp nginx=docker.arvancloud.ir/nginx:1.18.1 deployment.apps/webapp image updated

همچنین با دستور زیر می توانیم manifest ذخیره شده deployment در پایگاه داده را باز کرده و ویرایش کنیم. بعد از ذخیره و بستن آن، deployment بروزرسانی خواهد شد.

[root@MASTER-01]# kubectl edit deployment webapp deployment.apps/webapp edited

بنابراین سه روش زیر برای تغییر و بروزرسانی deployment وجود دارد:

  • تغییر فایل مانیفست خودمان و apply کردن مجدد آن

  • انجام تغییر در deployment با دستور مستقیم

  • باز کردن مانیفست ذخیره شده deployment در پایگاه داده، تغییر و ذخیره آن


DaemonSet

در Deployment ما تعداد Replica را خودمان مشخص می کنیم ولی در DaemonSet تعداد Replica برابر با تعداد نودهای Worker می باشد. یعنی به ازای هر نود Worker یک پاد بالا می آید.

[root@MASTER-01 ~]# kubectl api-resources | grep daemonset daemonsets ds apps/v1 true DaemonSet

بیایید یک manifest برای اولین DaemonSet خود بنویسیم و آن را در کلاستر کوبرنتیز اعمال کنیم:

daemonset.yaml

apiVersion: apps/v1 kind: DaemonSet metadata: name: webapp labels: nginx: 1.17.1 spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: docker.arvancloud.ir/nginx:1.17.1 imagePullPolicy: IfNotPresent
[root@MASTER-01 ~]# kubectl apply -f daemonset.yaml daemonset.apps/webapp created [root@MASTER-01 ~]# kubectl get ds NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE webapp 2 2 2 2 2 <none> 42s

[root@MASTER-01]# kubectl get no NAME STATUS ROLES AGE VERSION master-01 Ready control-plane 61d v1.35.1 worker-01 Ready worker 60d v1.35.1 worker-02 Ready worker 59d v1.35.1 [root@MASTER-01]# kubectl get po NAME READY STATUS RESTARTS AGE webapp-45kgs 1/1 Running 0 2m26s webapp-7jhpb 1/1 Running 0 2m26s [root@MASTER-01]# kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES webapp-45kgs 1/1 Running 0 2m37s 10.100.171.38 worker-01 <none> <none> webapp-7jhpb 1/1 Running 0 2m37s 10.103.204.83 worker-02 <none> <none>

همانطور که ملاحظه می کنید، روی هر نود worker یک پاد بالا آمده است.

کاربرد daemonset در مواقعی است که ما میخواهیم یک سرویس را روی همه نودهای سرور اجرا کنیم. به عنوان مثال فرض کنید ما می خواهیم روی هر نود worker یک سرویس مانند Filebeat بالا بیاوریم که فایلهای لاگ را خوانده و آنها را به Elk ارسال کند، اینجاست که daemonset به کارمان می آید.

برخی از ابزارهای مدیریت کلاستر کوبرنتیز هم از DaemonSet استفاده می کنند. مانند calico-node و kube-proxy

[root@MASTER-01]# kubectl get po -n kube-system NAME READY STATUS RESTARTS AGE calico-kube-controllers-9dff488b-6kbvv 1/1 Running 7 (4d12h ago) 60d calico-node-dff24 1/1 Running 3 (4d12h ago) 60d calico-node-mbtlb 1/1 Running 1 (47d ago) 59d calico-node-pbjmj 1/1 Running 1 (4d14h ago) 60d coredns-7d764666f9-pwjgn 1/1 Running 2 (4d12h ago) 61d coredns-7d764666f9-qrqvv 1/1 Running 2 (4d12h ago) 61d etcd-master-01 1/1 Running 2 (4d12h ago) 61d kube-apiserver-master-01 1/1 Running 4 (4d12h ago) 61d kube-controller-manager-master-01 1/1 Running 18 (2d7h ago) 61d kube-proxy-46f4b 1/1 Running 1 (47d ago) 59d kube-proxy-v6zgz 1/1 Running 1 (4d14h ago) 60d kube-proxy-xp7vx 1/1 Running 2 (4d12h ago) 61d kube-scheduler-master-01 1/1 Running 17 (2d7h ago) 61d metrics-server-d6484c959-rk64z 1/1 Running 0 48d metrics-server-d6484c959-x84k6 1/1 Running 0 4d14h [root@MASTER-01]# kubectl get ds -n kube-system NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE calico-node 3 3 3 3 3 kubernetes.io/os=linux 60d kube-proxy 3 3 3 3 3 kubernetes.io/os=linux 61d

توجه داشته باشید که می توان تنظیمات پیش فرض daemonset را طوری تغییر داد که پادها علاوه بر نودهای worker، روی نودهای master نیز بالا بیایند.

How to schedule daemonset on all nodes (master and worker nodes):

apiVersion: apps/v1 kind: DaemonSet metadata: name: webapp labels: nginx: 1.17.1 spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: tolerations: - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule containers: - image: docker.arvancloud.ir/nginx:1.17.1 imagePullPolicy: IfNotPresent name: nginx

StatefullSet

اگر ما بخواهیم یک پاد روی یک یا چند نود با پارامترهای مشخص بالا بیاید از StatefullSet استفاده می کنیم. به عنوان مثال فرض کنید ما میخواهیم دیتابیس Postgre خود را در کلاستر کوبرنتیز بالا بیاوریم. در این صورت از StatefullSet استفاده می کنیم؛ چرا که میخواهیم یک یا چند پاد از دیتابیس روی نودهای مشخص بالا بیایند و همیشه همانجا اجرا شوند بدون اینکه Scale شوند، جابجا شوند یا مشخصات آنها مانند نام پاد، شبکه و ... تغییر کند.


NodeSelector

گاهی اوقات نیاز است تا پاد ما روی نود یا نودهای خاصی بالا بیاید. مثلا فرض کنید برنامه ما دائما نیاز به خواندن و نوشتن در هارددیسک داشته باشد بنابراین باید عملیات I/O هارددیسک به سرعت انجام شود و فقط یکی از نودهای ما دارای هارد پرسرعت SSDمی باشد. بنابراین ما دوست داریم که پاد آن برنامه حتما روی آن نود مشخص که دارای هارد SSD است اجرا شود.

یا فرض کنید ما از یک نسخه از AI استفاده می کنیم و نیاز به GPU بالا داریم. بنابراین ما دوست داریم پاد آن برنامه روی نودهایی با GPU بالا اجرا شود.

در همه این مثالها ما می توانیم از مفهوم node selector استفاده کنیم. در node selector ما روی هر یک از نودهای کلاستر یک سری label می زنیم و با استفاده از آن لیبل ها می توانیم نود/نودهای موردنظر خود را فیلتر نماییم.

[root@MASTER-01]# kubectl uncordon Worker-02 Worker-02 uncordoned
apiVersion: apps/v1 kind: Deployment metadata: name: webapp labels: app: v11.1.1 spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: docker.arvancloud.ir/nginx:1.16.1 imagePullPolicy: IfNotPresent nodeSelector: hdd: ssd
[root@MASTER-01]# kubectl apply -f deployment.yaml deployment.apps/webapp created
[root@MASTER-01]# kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE webapp 3/3 3 3 4m30s [root@MASTER-01]# kubectl get rs NAME DESIRED CURRENT READY AGE webapp-566fd96dbb 3 3 3 5m12s [root@MASTER-01]# kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES webapp-566fd96dbb-2s6gj 1/1 Running 0 5m25s 10.103.204.85 worker-02 <none> <none> webapp-566fd96dbb-xdwwr 1/1 Running 0 5m24s 10.103.204.86 worker-02 <none> <none> webapp-566fd96dbb-z2h8x 1/1 Running 0 5m24s 10.103.204.84 worker-02 <none> <none>

همانطور که مشاهده می شود، هر 3 پاد روی نود worker-02 بالا آمده اند چون فقط این نود دارای لیبل hdd = ssd می باشد.

توجه داشته باشید که اگر ما در بخش nodeSelector، لیبلی را مشخص کنیم که روی هیچ نودی وجود نداشته باشد، اجرای پاد در وضعیت Pending می ماند.


Node affinity & node anti affinity

در Node Affinity ما مشخص می کنیم که پاد روی چه نودهایی می تواند بالا بیاید.

NodeSelector در واقع یک Node Affinity ساده است که نودها را تنها براساس لیبل مشخص می کند.

Node Affinity را می توان در دو نوع مشخص کرد:

requiredDuringSchedulingIgnoredDuringExecution:

در این حالت حتما باید نود دارای شرایط مشخص شده باشد تا پاد روی آن بالا بیاید. اگر نودی با شرایط خواسته شده پیدا نشود، وضعیت پاد Pending می ماند.

preferredDuringSchedulingIgnoredDuringExecution:

در این حالت اگر نودی با شرایط مشخص شده پیدا شود، پاد روی آن بالا می آید. در غیر این صورت، پاد روی نزدیکترین نود به شرایط تعیین شده (نودی با بالاترین امتیاز)، بالا می آید.

فرض کنید ما یک کلاستر کوبرنتیز داریم که نودهای آن در سه دیتاسنتر تهران، تبریز و مازندران قرار دارند و مکان دیتاسنتر با یک لیبل در نود مشخص شده است.

[root@MASTER-01 ~]# kubectl label node worker-02 topology.kubernetes.io/zone=Tehran node/worker-02 labeled

حالا با node affinity می توانیم تعیین کنیم که پادهای برنامه فقط روی نودهای واقع در تهران یا تبریز بالا بیایند.

apiVersion: apps/v1 kind: Deployment metadata: name: webapp labels: app: v11.1.1 spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: docker.arvancloud.ir/nginx:1.16.1 imagePullPolicy: IfNotPresent affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: topology.kubernetes.io/zone operator: In values: - Tehran - Tabriz preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: another-node-label-key operator: In values: - another-node-label-vlaues
[root@MASTER-01]# kubectl apply -f deployment.yaml deployment.apps/webapp created [root@MASTER-01]# kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES webapp-6b6984c47d-2fhm7 1/1 Running 0 16s 10.103.204.88 worker-02 <none> <none> webapp-6b6984c47d-dsjnj 1/1 Running 0 16s 10.103.204.87 worker-02 <none> <none> webapp-6b6984c47d-qxp82 1/1 Running 0 16s 10.103.204.89 worker-02 <none> <none>

سایر operatorهای affinity عبارتد از :

  • In

  • NotIn

  • Exists

  • DoesNotExist

  • Gt (Greater than)

  • Lt (Less than)

Weight:

در preferredDuringSchedulingIgnoredDuringExecution می توانیم چند preference تعریف کنیم و برای هر یک از آنها عددی بین 1 تا 100 را به عنوان وزن تعیین کنیم.

ممکن است از بین نودهای کلاستر چند نود حائز شرایط معین شده باشند. در این صورت آن نودی انتخاب می شود که مجموع weight برای آن بیشتر از بقیه نودها باشد.


Pod affinity & pod anti affinity

این مورد خیلی شبیه به node affinity/node anti affinity است. با این تفاوت که انتخاب نود بر اساس پادهای داخل آن صورت می گیرد.

فرض کنید که سیستم ما دارای دو برنامه جدا از هم می باشد یکی backend و دیگری frontend. این دو برنامه داده های زیادی را بین هم رد و بدل می کنند و ما برای افزایش سرعت برنامه و کاهش زمان پاسخ میخواهیم پادهای این دو برنامه کنار هم و روی یک نود اجرا شوند. برای این منظور از Pod Affinity استفاده می کنیم؛ به عنوان مثال در فایل مانیفست frontend و در بخش Pod Affinity تعیین می کنیم که این پاد روی نودی بالا بیاید که پاد backend اجرا می شود.

حالا فرض کنید ما دو برنامه داریم که هر دو نیاز به CPU بالا هستند و پردازشهای سنگینی را انجام می دهند. قاعدتا ما می خواهیم که پادهای این دو برنامه روی یک نود اجرا نشوند و روی نودهای جداگانه بالا بیایند. برای این منظور از Pod Anti Affinity استفاده می کنیم.

معمولا ما ترجیح می دهیم که هر یک از replicaهای پاد روی نودهای جداگانه قرار بگیرند و دو پاد از یک برنامه روی یک نود نباشند. مثلا اگر ما سه نسخه از Redis بالا می آوریم بهتر است هر یک روی نودهای جداگانه باشد تا در مواقعی که یک نود دچار مشکل شد نودهای دیگر جوابگوی درخواستها باشند. این مورد هم یکی از کاربردهای عمده Pod Anti Affinity می باشد.

منابع:

https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/

https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/

https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/

https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/

kubernetesdeployment
۲
۰
فرهاد صادقی
فرهاد صادقی
مهندس نرم افزار، طراحی و راه اندازی سیستم های نرم افزاری بر پایه معماری میکروسرویس
شاید از این پست‌ها خوشتان بیاید