So, Let's get started...
What are Taints and Tolerations?
အဓိက ကတော့ Pod တွေကို ဘယ် node တွေ ပေါ်မှာ run စေချင်လဲ မ run စေချင်ဘူးလဲ ဆိုတာကို manage လုပ်ရာတွင် အသုံးပြုပါတယ်။ အဲ့တော့ ဥပမာ အနေနဲ့ ပြောရရင် ရပ်ကွက်ထဲက play-ground တစ်ခုမှာ ကြက်တောင်ရိုက်ဖို့ နေရာလေးတစ်ခု လုပ်ပေးထားတယ် အဲ့တော့ ကြက်တောင်ရိုက်ဖို့ လာတဲ့ သူတွေပဲ ဒီနေရာမှာ ကြက်တောင်လာရိုက်ကြတယ်ပေါ့။ ဒီနေရာမှာ ကြက်တောင်ရိုက်ဖို့ နေရာက Node ဖြစ်ပြီး သူကို့ လာအသုံးပြုမယ့် သူတွေက Pod တွေအနေနဲ့ သတ်မှတ်မယ်။ အဲ့အတွက်ကြောင့် Node က Taint အနေနဲ့ ကြက်တောင်ရိုက်ရန်ဆိုပြီး သတ်မှတ်ထားတော့ကြောင့် ဒီ Taint နဲ့ သတ်ဆိုင်တဲ့ ကြက်တောင်ရိုက်ဖို့လာတဲ့ Toleration ရှိတဲ့ Pod တွေသာ လာကစားခွင့်ပြုတယ့်သဘောဖြစ်ပါတယ်။
Example: အနေနဲ့က ဒီ Specific Node ပေါ်မှာ dedicated team တစ်ခုက အသုံးပြုပဲ Pod တွေကိုပဲ ပေး run စေချင်တယ်။ အဲ့လိုအခြေနေမျိုးတွေမှာ တစ်ခြား team တွေနဲ့ မလျောစေချင်တဲ့အခါ အသုံးပြုပါတယ်။
Which commands is being used to taint a node?
$kubectl taint node <node-name> key=value:taint-effect ဆိုပြီး Node တစ်ခုပေါ်မှာ Taint ကို သတ်မှတ်ပါတယ်။ Taint တစ်ခု သတ်မှတ်တဲ့ နေရာမှာ key=value:taint-effect က အဓိကကျတယ်။ ဒီ key=value ကို Pod မှာ define လုပ်ပြီး Pod သည် ဒီ Node ပေါ်မှာ run ခွင့် ရှိမရှိဆုံးဖြတ်ထားဖြစ်ပါတယ်။
How many types of Taint effect can define on a node?
Node ပေါ်မှာ Taint အမျိုးအစား ၃ မျိုး သတ်မှတ်နိုင်ပါတယ်။
1. NoSchedule :
key=value:NoSchedule ဆိုပြီး သတ်မှတ်ထားရင် ဒီ key=value နဲ့ ကိုက်ညီတဲ့ Pod တွေမှလွဲပြီး ဒီ Node ပေါ်မှာ တစ်ခြား Pod တွေ run ဖို့ schedule လုပ်ပေးမှာမဟုတ်ဘူးလို့ သတ်မှတ်ပါတယ်။ အဲ့တော့ ကြက်တောင်ရိုက်ဖို့ လာတဲ့ သူတွေမှလွဲပြီး ကျန်တဲ့သူတွေကို ပေးမသုံးဘူးဆိုတဲ့သဘောဖြစ်ပါတယ်။
2. PreferNoSchedule:
key=value:PreferNoSchedule ဆိုပြီး သတ်မှတ်ရင် ဒီ key=value နဲ့ မကိုက်ညီတဲ့ Pod တွေ ကို ဒီ Node ပေါ်မှာ မ run နိုင်အောင် ကြိုးစားပေးမယ် ဒါပေမယ့် လုံး၀ မသုံးမပြုနိုင်တာတော့မဟုတ်ဘူး။ ဥပမာ ကြက်တောင်ရိုက်ဖို့အတွက် လုပ်ပေးထားသော်လည်း ပိုက်ကျော်ခြင်း ခတ်ဖို့အတွက် နေရာမရှိတော့တဲ့အခါမျိုးမှာ No Choice အနေနဲ့ အသုံးပြုခွင့်ပေးလိုက်တဲ့သဘောမျိုးဖြစ်ပါတယ်။
3. NoExecute:
key=value:NoExecute ဆိုပြီးတော့ သတ်မှတ်လိုက်ရင် လက်ရှိ Node မှာ run နေတဲ့ Pod တွေက သတ်မှတ်လိုက်တဲ့ NoExecute Taint effect ကို Toleration မရှိခဲ့ရင် run ခွင့် ရှိတော့မှာမဟုတ်ဘူး ထိုနည်းတူ new pod တွေလဲ Toleration မရှိခဲ့ရင် node ပေါ်မှာ run ဖို့အတွက် schedule သတ်မှတ်မှာ မဟုတ်တော့ပါဘူး။
Here comes, defining a toleration on Pod...
အခုဆို Node က Taint တွေသတ်မှတ်လိုက်တယ် သတ်မှတ်လိုက်တဲ့ Taint တွေနဲ့ သက်ဆိုင်တဲ့ toleration တွေကို Pod မှာ define လုပ်ပေးရပါတယ်။
Sample Pod Definition File with Toleration
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx-container
image: nginx
tolerations:
- key:" app"
operator: "Equal"
value: nginx
effect: NoSchedule
အပေါ်မှာပြထားတဲ့ အတိုင်း spec: အောက်မှာ tolerations: တစ်ခု သတ်မှတ်ပြီး app=blue:NoSchedule ဆိုတဲ့ key=value:taint-effect အရ ဒီ Pod သည် app=nginxTaint effect သတ်မှတ်ထားတဲ့ node တွေပေါ်မှာ run လို့ရပြီး ကျန်တဲ့ value သတ်မှတ်ထား သော Pod တွေကို Node ပေါ်မှာ run ဖို့ schedule ပေးလုပ်မှာမဟုတ်ပါဘူး။
အောက်ပုံမှာဆိုရင် အရင်ဆုံး မိမိ ရဲ့ node name ကို သိဖို့အတွက် $kubetcl get nodes ဆိုပြီး ရှာမှာဖြစ်ပြီး ကျွန်တော်က လက်ရှိမှာ minikube ကို အသုံးပြုထားပါတယ် nodename ကတော့ "minikube" ဖြစ်ပါတယ်။ ပြီးရင် default အားဖြင့် node ပေါ်မှာ Taint သတ်မှတ်ထားခြင်းမရှိပါဘူး။ $kubectl describe node minikube နဲ့ကြည့်တဲ့အခါ Taints: မှာ ဘာမှမသတ်မှတ်ထားတာ တွေ့ရမှာဖြစ်ပါတယ်။
Defining a Taint on a Node
အိုကေ ဒါဆို ကျွန်တော်တို့ Node ပေါ်မှာ Taint key=value တစ်ခု သတ်မှတ်မယ်။ သတ်မှတ်လိုက်တဲ့ Key=Value က လာပြီး run မယ့် Pod app သည် nginx ဖြစ်ရမယ် ဖြစ်ပြီး app=nginx မဟုတ်ခဲ့လျှင် NoSchedule လို့ သတ်မှတ်ထားတဲ့အတွက် တစ်ခြား app=nginx မဟုတ်တဲ့ Pod တွေ လာ run လို့ ရမှာမဟုတ်ပါဘူး။
Defining a Tolerations on Nginx-Pod and Redis-Pod definition file
အခု node ပေါ်မှာ Taint သတ်မှတ်ပြီးတဲ့နောက် Pod definition file ထဲမှာ Tolerations သတ်မှတ်ပေးရမှာဖြစ်ပါတယ်။ ဒါမှ ဒီ node ပေါ်မှာ run နိုင်မယ့် Pod ဖြစ်မဖြစ် ဆုံးဖြတ်နိုင်မှာဖြစ်ပါတယ်။ အောက်ပုံ မှာဆိုရင် Nginx-Pod နဲ့ Redis-Pod ကို compare လုပ်ပြပေးထားပါတယ်။ သူတို့ရဲ့ Tolerations Key=Value တွေ သတ်မှတ်ထားပါတယ်။
Creating a Pod
အရင်ဆုံး nginx-pod ကို စတင် create လုပ်ပါမယ်။ Nginx-Pod က Taint အနေနဲ့ သတ်မှတ်ထားတဲ့ Key=Value match ဖြစ်တဲ့အတွက် $kubectl get pods နဲ့ ကြည့်တဲ့ အခါ "running" ဖြစ်နေမှာဖြစ်ပါတယ်။
အောက်ပုံမှာ ထပ်မံပြီး redis-pod ကို create လုပ်လိုက်တဲ့အခါမှာ Taint အနေနဲ့ သတ်မှတ်ထားတဲ့ Key=Value နဲ့ မကိုက်ညီတဲ့အတွက် create လုပ်ပြီးပေမယ့်လဲ effect: NoSchedule ဖြစ်လို့ running မဖြစ်ပဲ "pending" ဆိုပြီးပြနေတာကို တွေ့ရမှာဖြစ်ပါတယ်။ တစ်ကယ်က ကျွန်တော်က minikube အသုံးပြုထားတဲ့အတွက် node တစ်ခုထဲဖြစ်နေတာဖြစ်ပီး ပုံမှန် node က တစ်ခု ထက်ပိုရှိနေမယ်ဆိုရင် တစ်ခြား node ပေါ်မှာ သွား run မှာဖြစ်ပါတယ်။
How about Taint with PreferNoSchedule Effect?
ကျွန်တော်တို့ NoSchedule နဲ့ အသုံးပြုပြီးနောက်မှာ PreferNoSchedule ကို အသုံးပြုကြည့်ရအောင်။
အောက်ပုံမှာဆိုရင် Node ပေါ်မှာ Taint effect ကို PreferNoSchedule အဖြစ် အသုံးပြုထားတယ် အဲ့အတွက် Kube-Scheduler က တစ်ခြား ရွေးစရာ Node မရှိခဲ့ဘူးဆိုရင် app=nginx မဟုတ်ခဲ့သည့်တိုင်အောင် Pod ကို လက်ရှိ Taint သတ်မှတ်ထားတဲ့ node ပေါ်မှာ run ခွင့်ပေးမှာဖြစ်ပါတယ်။ အခုပုံမှာဆိုရင် app=redis ဖြစ်နေပေမယ့်လည်း running ဖြစ်နေတာကို တွေ့ရမှာဖြစ်ပါတယ်။
What if we use Taint with NoExecute Effect?
NoExecute ဆိုပြီး Node ပေါ်မှာ သတ်မှတ်လိုက်လျှင် အခုလက်ရှိ run နေတဲ့ Pod တွေမှာ Key=Value:Effect က သတ်မှတ်ထားတဲ့ Taint နဲ့ match မဖြစ်ဘူးဆိုရင် Pods တွေက ချက်ချင်း terminate ဖြစ်သွားမှာဖြစ်ပြီး အသစ်လာ run မယ့် Pod တွေမှာလဲ သတ်မှတ်ထားတဲ့ Teffect ကို Toleration မရှိဘူးဆိုရင် Node ပေါ်မှာ လာရောက် run ခွင့် ရှိမှာမဟုတ်ပါဘူး။
အခုအောက်ပုံမှာဆိုရင်
1. လက်ရှိ Taint effect သည် PreferNoSchedule ဖြစ်ပြီး တစ်ဖက်မှာ Pods တွေသည်လဲ running state ဖြစ်နေဆဲဖြစ်ပါတယ်။
2. အဲ့ဒီမှာ ကျွန်တော်တို့ လက်ရှိ Taint effect ကို ပြန်ဖျက်ချင်တယ်ဆိုရင် Taint သတ်မှတ်တဲ့ command ကို ပဲပြန်အသုံးပြုပြီး နောက်ဆုံးမှာ "-" minus symbol လေး ထည့်ပြီး ပြန်ဖျက်ရတာဖြစ်ပါတယ်။ အခု ချိန်ထိ က တစ်ဖက်မှာ Pods တွေက run နေတုန်းပဲဖြစ်ပြီး ဘာလို့လဲဆိုတော့ လက်ရှိ Node ပေါ်မှာ ဘာ Taint effect မှမရှိသေးလို့ဖြစ်ပါတယ်။
3. နောက်ဆုံး အဆင့်မှာ ကျွန်တော်တို့ Taint effect "NoExecute" ဆိုပြီးသတ်မှတ်လိုက်ပြီ အဲ့ဒီအခါမှာ လက်ရှိ Pods တွေရဲ့ Tolerations မှာ NoExecute ကို မသတ်မှတ်ထားတဲ့အတွက် ချက်ချင်းဆိုသလို Node ပေါ်ကနေ terminate လုပ်ခံရတာဖြစ်ပါတယ်။
4. အိုကေ ဒါဆို နောက်ထပ်တစ်ဆင့်အနေနဲ့ Nginx-Pod မှာ Tolerations ကိုပြန်ပြင်မယ် ဒီတစ်ခေါက်မှာတော့ app=nginx:NoExecute ဆိုပြီး သတ်မှတ်လိုက်ပြီးတော့ Pod ကို ပြန် create လုပ်ကြည့်တဲ့အခါမှာ သတ်မှတ် ထားတဲ့ Taint ကို Tolerations ရှိတဲ့အတွက် running ဖြစ်နေတာကိုတွေ့ရမှာဖြစ်ပါတယ်။
What is Node-Selector?
Taints and Tolerations အပိုင်းပြီးသွားတဲ့အခါ အခု နောက်တစ်ခုအနေနဲ့က Node-Seletctor အကြောင်းဖြစ်ပါတယ်။
Different between Taints and Tolerations and Node-Selector
Taint and Tolerations combo က Node-Selector နဲ့ ဘာကွာလဲဆိုတော့ Taint and Tolerations သည် သူတို့ သတ်မှတ်ထားတဲ့ Taint effect တွေကို Tolerations ရှိတဲ့ Pod တွေလာရောက် run နိုင်တယ်လို့ သတ်မှတ်ထားတာဖြစ်ပြီး တစ်ချို့ အခြေအနေတွေမှာ ချွင်းချက်အနေနဲ့ Taint effect နဲ့ မကိုက်သော်လည်း run ခွင့်ပေးပြီး Node-Selector ကျတော့ လုံး၀ကို ဒီ Pod သည် ဒီ Node မှာပဲ run ကို run ရမယ် scheduler သည် ဒီ node ပေါ်မှာပဲ ဒီ Pod ကို assign ချရမယ်ဆိုတဲ့ သဘောမျိုးဖြစ်ပါတယ်။
Example: အနေနဲ့က Kubernetes Cluster Environment မှာ လက်ရှိ သုံးနေတဲ့ Node တွေရဲ့ Size ချင်း မညီမျှတဲ့အခါ ပြောချင်တာက Hardware Resources တွေ မတူတဲ့ အခါမှာ small resources ပဲ ရှိတဲ့ Node တွေပေါ်မှာ Pod တွေ အများကြီး overload ဖြစ်အောင်မသုံး စေချင်တဲ့ အခါ ဝန်မပိစေချင်တဲ့အခါမျိုး တွေမှာ အသုံးပြုပါတယ်။
How to determine which node is large or small?
အဲ့တော့ ဘယ် node ကတော့ Large ဖြစ်ပြီး ဘယ် node ကတော့ small node ဖြစ်တယ်ဆိုတာ ကို ကျွန်တော်တို့ "Labels" နဲ့ Key=Value ဆိုပြီး Node ပေါ်မှာ သတ်မှတ်ပြီး ဆုံးဖြတ်ပါတယ်။
Which command to define a label on a node?
$kubectl label node <node-name> <label-key>=<label-value> ဆိုပြီးတော့ အသုံးပြုပြီး သတ်မှတ်ပါတယ်။ အောက်ပုံမှာ ပြထားပါတယ်။
Label ကို ပြန်ပြီး remove လုပ်ချင်တယ်ဆိုရင်တော့ $kubectl label node minikube size- ဆိုပြီး အသုံးပြုပါတယ်။
Let's try to create Pod with Node-Selector
1. အခု အရင်ဆုံး Node မှာ Size=Large ဆိုပြီး Label define လုပ်ထားတယ်။ ပြီးတော့ တစ်ဖက်မှာက ကျွန်တော် Nginx-Pod definition file မှာ "nodeSelector: Large" ဆိုပြီး သတ်မှတ်ထားတဲ့အတွက် ဒီ Nginx-Pod သည် Node label size=Large ဆိုပြီး define လုပ်ထားတဲ့ Node တွေပေါ်မှာ run လို့ရတာကိုတွေ့ရမှာဖြစ်ပါတယ်။
2. အောက်ပုံမှာ ကျတော့ Redis-Pod တစ်ခုရဲ့ "nodeSelector: small" ဆိုပြီးတော့ သတ်မှတ်ထားသည့်အတွက် Node Label Size=Large သတ်မှတ်ထားတဲ့ Node ပေါ်မှာ run လို့ မရပဲ Pending ဖြစ်နေတာကို တွေ့မှာဖြစ်ပါတယ်။
What is Node-Affinity?
Node-Affinity နဲ့ Node-Selector အဓိက အလုပ်လုပ်ပုံခြင်းက အတူတူပဲဖြစ်ပြီး ဘာကွာသွားလဲ ဆိုရင် node-affinity က pod placement on node ပိုင်းမှာ node-selector ထက်ပိုပြီး ရွေးချယ်စရာ options ပိုင်း မှာ ပိုပြီးများပါတယ်။ Node-Selector က Large or Small အဲ့လိုမျိုး တစ်မျိုးထဲပဲ သတ်မှတ်လို့ ရနိုင်ပြီး Node-Affinity မှာဆိုရင်တော့ Value နှစ်ခု ကို တစ်ပြိုက်နက် သတ်မှတ်နိုင်တာတွေ ဥပမာ "Large or Medium" ဒါမဟုတ် "Not Small" စတဲ့ advance features တွေ ကို သတ်မှတ်နိုင်ပါတယ်။
How does node-affinity work?
Node-Affinity ဘယ်လိုမျိုး အလုပ်လုပ်သလဲဆိုတော့ သူလဲပဲ Node မှာ သတ်မှတ်ရမယ့် Label ရှိမယ် နောက် အဲ့ Label နဲ့ ကိုက်ညီမယ့် Pod မှာ သတ်မှတ်ရမယ့် Affinity-Rule ရှိမယ်။ ဒီ Lable နဲ့ Affinity-Rule တွေ ကိုက်ညီမှ Kube-Scheduler က Pod ကို Node ပေါ်မှာ run ခွင့် ပေးမှာ ဖြစ်ပါတယ်။ Node-Affinity မှာ....
Type-1 (requiredDuringSchedulingIgnoredDuringExecution)
Type-2 (perferredDuringSchedulingIgnoredDuringExecution)
Type-3 (requiredDuringSchdeulingRequiredDuringExecution)
ဆိုပြီး Type ၃မျိုး ရှိတယ်။
Type-1 (requiredDuringSchedulingIgnoredDuringExecution)
Affinity rule သတ်မှတ် ထားတဲ့ Pod တစ်ခု ကို စ create လုပ်ပြီးတဲ့နောက် အဲဒီ Pod run မယ့် Node သည် Pod ရဲ့ affinity rule နဲ့ ကိုက်ညီမယ့် Label define မလုပ်ထားဘူးဆိုရင် သူနဲ့ ကိုက်ညီမယ့် Node တစ်ခု ရှိလာတဲ့အထိ ကို အချိန် တစ်ခုထိ စောင့်နေပြီး လုံးဝကို မရှိတော့ဘူးဆိုရင်တော့ Pod ကို Kube-Scheduler က run ခွင့်ပေးမှာမဟုတ်တော့ပါဘူး။ ဒါကြောင့် သူ့ကို Required Typeလိုခေါ်ပြီး Pod တစ်ခု run ဖို့က Affinity-rules နဲ့ label define လုပ်ထားတဲ့ node လိုကိုလိုအပ်တဲ့ Type အမျိုးအစားလို့သတ်မှတ်ပါတယ်။
Type-2 (perferredDuringSchedulingIgnoredDuringExecution)
Type-1 Required Type နဲ့ ဆန့်ကျင်စွာ ကိုက်ညီမယ့် Node တစ်ခု ရှာမတွေ့ ခဲ့ ဘူးဆိုရင်တောင် Pod မှာ သတ်မှတ်ထား တဲ့ Affinity-Rule ကို Ignore လုပ်ပြီး အဆင်ပြေမယ့် Node တစ်ခုခုမှာ Kube-Scheduler က run ခွင့် ပေးလိုက်မှာဖြစ်ပါတယ်။
Both Type-1 and Type-2 က လက်ရှိ scheduled ဖြစ်ပြီးသား running ဖြစ်နေပြီးသား Pod တွေ ပေါ်မှာ Node-Affinity rules တွေ changes ဖြစ်ခဲ့ရင်တောင် "IgnoreDuringExecution" ဖြစ်တဲ့အတွက် Ignore လုပ်ပြီး ဆက်လက် running ဖြစ်နေမှာဖြစ်ပါတယ်။
Type-3 (requiredDuringSchdeulingRequiredDuringExecution)
RequiredDuringScheduling ဖြစ်တဲ့အတွက် Type-1 နဲ့ တူမယ် နောက်ထပ် တစ်ခုက "RequiredDuringExecution" ဖြစ်တဲ့အတွက် running လုပ်နေစဥ်မှာ Affinity-Rule changes လုပ်လိုက်လျှင် လက်ရှိ node ပေါ်မှာ run နေတဲ့ Pod တွေရဲ့ affinity-rules နဲ့ မကိုက်ညီတော့တဲ့အခါ Pods တွေကို ဖယ်ထုတ်ပစ်မှာ ဖြစ်ပါတယ်။
Node-Affinity rule က ကျွန်တော်လဲ ဖတ်လေ ရှုတ်လေ ပဲ :P anyway.. Let's continue....
Deploying a Pod with node-affinity rules
အခု ကျွန်တော်တို့ Node-Affinity rule သတ်မှတ်ပြီးတော့ Pod တည်ဆောက်ကြည့်ရအောင်။ Node Label ကတော့ size=Large ဆိုပြီး Node-Selector မှာသတ်မှတ်ခဲ့တဲ့အတိုင်းပဲ သတ်မှတ်ထားမယ်။ နောက် Pod ရဲ့ definition file ထဲမှာ Node-Affinity rules ကိုသတ်မှတ်ပြီး create လုပ်ကြည့်မှာဖြစ်ပါတယ်။
အောက်ပုံမှာဆိုရင် Node Label က size=Large ဖြစ်တဲ့အတွက် သူ့အပေါ်မှာ လာ run မယ့် Pod ရဲ့ Affinity-Rule သည် Large or Medium ဖြစ်ရမယ်ဆိုပြီး Nginx-Pod definition file ထဲမှာ သတ်မှတ်ထားတာဖြစ်ပါတယ်။ ပြီးရင် create လုပ်ပြီး တဲ့ အခါ Node ရဲ့ Label နဲ့ Affinity-Rule နဲ့ ကိုက်ညီတဲ့အတွက် running ဖြစ်နေတာကို တွေ့မှာဖြစ်ပါတယ်။ ဒီနေရာမှာ Operator : In ဆိုတာကို သုံးသွားပါတယ်။ တစ်နည်းအားဖြင့် အကျုံးဝင်တဲ့ values တွေ ဆိုပြီးသတ်မှတ်ပါတယ်။
အောက်ပုံမှာဆိုရင်လဲ Pod ရဲ့ Node-Affinity rule ကို Operator change ပြီး အသုံးပြုသွားတာဖြစ်ပါတယ်။ ဒီမှာတော့ operator: NotIn , Values: Small ဆိုပြီးသတ်မှတ်ထားတဲ့အတွက် Node Label သည် as long as "Small" မသတ်မှတ်ထားဘူးဆိုရင် ဒီ Pod သည် ဒီ Node ပေါ်မှာ အသုံးပြုနိုင်ပါတယ်။
အခုအောက်ပုံမှာကျတော့ Node-Affinity Rule မှာ Operator ကို "Exists" ဆိုပြီး အသုံးပြုသွားတာတွေ့ရပါမယ်။ Exists ဆိုတဲ့ operator ရဲ့ အလုပ်လုပ်ပုံက သူ့မှာ value သတ်မှတ်စရာ မလိုပဲ as long as Node Label ဖြစ်တဲ့ key: size တာ တူညီခဲ့တယ်ဆိုရင် ဘယ်လို size မျိုးပဲဖြစ်ပေစ Large, Medium or Small စဥ်းစားမနေပဲနဲ့ Pod ကို ဒီ Node ပေါ်မှာ run နိုင်မှာဖြစ်ပါတယ်။
PS: That's a lot I need to know regarding scheduling chapters. For now, let me stop at this state and will update once I get more understanding.
That's it 😊
Pls Like and Subscribe Our Root Of Info FB Page and Youtube Channel
https://www.facebook.com/rootofinfo
https://www.youtube.com/channel/UCkOi7WxhUBKONv3uD0CvuWw?view_as=subscriber
Thank you!!!
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.