בפוסט הקודם על קוברנטיס, הגדרנו Pod – יחידת ההרצה הקטנה ביותר בקוברנטיס.
סיימנו בכך ש Pod שנוצר בגפו, מה שקרוי ״Naked Pod״ – ואיננו בעל שרידות גבוהה: אם הוא קרס, או ה node שבו הוא רץ קרס / נסגר – קוברנטיס לא יחדש אותו.
בפוסט הזה נציג סמנטיקה מרכזית נוספת בעבודה עם קוברנטיס: ה Deployment – המכמיסה בתוכה סמנטיקה בשם ReplicaSet אותה נסביר גם כן. הסמנטיקות הללו הן מה שמאפשרות להגדיר/לעדכן pods כך שיהיו resilient. זוהי תכונה קריטית שאנו מקבלים מקוברנטיס, ולא היינו מקבלים מ Dokcer לבדו.
בואו נתחיל.
Resilient Pods
בכדי ליהנות מיכולת מתקדמות של קוברנטיס כגון Self-Healing ו Auto-Scaling – עלינו להימנע מהגדרה של naked pods, ולהשתמש בסמנטיקה בשם ReplicaSet.
ReplicaSet הוא המקבילה של AWS Auto-Scaling-Group, מנגנון המוודא שמספר ה Pods שרצים:
- לא יורד ממספר מסוים (Auto-Healing)
- עולה בצורה דינאמית כאשר יש עומס על ה Pods (למשל: CPU utilization מעל 60%) – עד גבול מסוים שהגדרנו, ויורד בחזרה – כאשר העומס חולף.
![]() תרשים קונספטואלי ולא מדויק. נדייק אותו בהמשך. |
מה Deployment מוסיף על ReplicaSet? במחשבה ראשונה נשמע שאנו זקוקים רק ל ReplicaSet.
- אנו מגדירים מצב רצוי
- קוברנטיס דוגם כל הזמן את המצב המערכת, ואם יש פעם בין המצב הנוכחי למצב הרצוי – היא פועלת לסגירת הפער.
- קוברנטיס רואה שלא רוצים יותר את גרסה 11 – הוא מכבה את כל ה Pod replicas בגרסה הזו.
- לא טוב! מרגע זה אנחנו ב downtime.
- קוברנטיס רואה שרוצים את גרסה 12 – הוא מתחיל להריץ Pod replicas של הגרסה הזו.
- אבוי! בגרסה 12 יש באג בקוד, והמיקרו-שירות לא מצליח לרוץ.
- קוברנטיס יוצר Log מפורט וברור של הבעיה – אבל עד שלא נטפל בה בעצמנו – אנחנו ב Full Downtime.
Deployment, לשמחתנו, מוסיף את היכולות / האחריות הבאה:
- ביצוע סדר הפעולות בצורה נכונה – כך שתמיד יהיו מספיק Pods שרצים.
- בחינת (probing) ה Pod replicas החדשים – ו rollback במקרה של בעיה.
- למשל: בדוק שה pod replica החדש שעלה הוא תקין (ע״פ תנאים מסוימים, מה שנקרא Readiness check) – לפני שאתה מעלה עוד pod replicas מהגרסה החדשה או מוריד עוד ישנים.
- ישנן מגוון הגדרות בכדי לשלוט בהתנהגות המדויקת.
בתרשים למעלה ערבבתי, לצורך הפשטות, בין entities שהם קונפיגורציה (באפור: deployment, replica set) לבין entities של זמן-ריצה (בכתום: Pod replica). אני רוצה כעת לדייק את הדברים בעזרת התרשים הבא:
קונספט ארכיטקטוני מרכזי בקוברנטיס הוא ה Controllers, שהם בעצם מעין Modules או Plug-Ins של סביבת הניהול של קוברנטיס (ה Control Plane). ה Controllers מאזינים ל API Server הפנימי של קוברנטיס אחר שינויים על ה Resource הרלוונטי להם (Deployment, Service, וכו’) או שינויים בדרישות – ואז מחילים את שינויים נדרשים.
בעיקרון ה Controllers רצים ב Reconciliation loop (כמו event loop) שנקרא כך בגלל שכל פעם שיש אירוע (שינוי שנדרש) הם מבצעים פעולה על מנת “ליישר קו” (reconcile) וכך, לעתים בצעדים, ה loop דואג שכל הזמן המצב בשטח יגיע במהרה למצב שהוגדר. מדי כמה זמן הם בודקים את סה”כ ההגדרות ומגיבים לפער – אם קיים. כלומר: גם אם event מסוים פוספס / לא טופל – תוך זמן קצר ה controller יגלה את הפער וישלים אותו.
בניגוד להפשטה המוצגת בתרשים למעלה, Controllers לעולם לא יתקשרו ישירות אחד עם השני (אחרת: הם לא יהיו Pluggable). כל קריאה היא ל API Server, למשל “צור משאב מסוג Replication”, שבתורו יפעיל אירוע מתאים שייקלט ע”י ה Replication Controller ויוביל לפעולה.
אנחנו נראה בהמשך את ההגדרות של ה Deployment ואת ה template המדובר.
הנה תיאור דינאמי של תהליך ה deployment:
ה Deployment ישמור את ה replica set הקודם (ועליו כל ההגדרות), על מנת לאפשר תהליך של Rollback.
כמובן שההתנהגות המדויקת של שלב ה Mid (ליתר דיוק: סדרת שלבי ה mid) תלויה מאוד באסטרטגיית ה Deployment שנבחרת.
אני מניח שאתם מכירים את 2 האסטרטגיות המרכזיות, המקובלות בכלל בתעשייה: Rolling deployment ו Blue/Green deployment.
קוברנטיס לא תומך היום (בעת כתיבת הפוסט) ב Blue/Green deployments כיכולת-ליבה (אם כי יש מדריכים שמראים כיצד ניתן להשיג זאת, על בסיס יכולות קיימות של קוברנטיס – הנה דוגמה לאחד).
בכלל, כל נושא ה deployments הוא GA רק מגרסה 1.9 (תחילת 2018). כיום קוברנטיס תומך רק באסטרטגיות: “Recreate” (אסטרטגיה סופר פשוטה, בה יש downtime בהגדרה) ו “RollingUpdate” (ברירת המחדל).
הנה שני מקורות, המציגים כ”א כמה תסריטי deployment אפשריים, בהתבסס על יכולות הליבה של קוברנטיס:
- מצגת של Cloud Native Computing Foundation (בקיצור: CNCF) – הארגון שהוקם על מנת לקחת אחריות על פרויקט קוברנטיס, ולנהל אותו כ Open Source בלתי-תלוי. יש גם סיכום one-pager של האסטרטגיות שהוא מציג.
- פוסט של ארגון בשם Container Solutions – שגם הוא מציג בצורה ויזואלית יפה, את משמעות האסטרטגיות השונות (סט דומה, אך לא זהה לקודם).
הגדרת Deployment בפועל
בלי לקשקש הרבה, נגדיר manifest של deployment:
apiVersion: apps/v1 kind: Deployment metadata: name: hello-deploy labels: app: hello-world-deployment spec: replicas: 4 selector: matchLabels: app: hello-world strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 maxSurge: 1 minReadySeconds: 5 template: metadata: labels: app: hello-world env: dev version: v1 spec: containers: - name: hello-pod image: hello-world:latest ports: - containerPort: 8080 protocol: TCP
- את הגדרת ה pod ניתן למצוא תחת ה template והן זהות ל Pod שהגדרנו בפוסט הקודם (מלבד label אחד שהוספנו). שימו לב שה metadata של ה Pod מופיע תחת template, ולא במפתח ה metadata של ה manifest.
- replicas הוא מספר העותקים של ה pod שאנו רוצים להריץ.
- ה selector משמש לזיהוי חד משמעי של סוג ה pod שאנו מתארים. חשוב שניתן label “יציב” שבעזרתו קוברנטיס יידע לקשר ולהשוות אם היו שינויים בין pod templates. אם היה שינוי – עליו לבצע deploy.
- למשל: אם שינינו גם את label הגרסה וגם את ה image – איך אפשר לקשר בוודאות שמדובר באותו האפליקציה, ולא בחדשה?
- מקובל להשתמש ב label בשם app או app.kubernetes.io/name.
- ל labels ניתן להוסיף namespace וקוברנטיס מציע כמה שמות “סטנדרטים” ל labels. אני לא יודע כמה השימוש בהם באמת נפוץ.
- על אסטרטגיית ה deployment כבר דיברנו. הנה 2 פרמטרים חשובים של אסטרטגית rolling deployment:
- maxUnavailable – הכמות המרבית המותרת של pods שאינם זמינים תוך כדי פעולת deploy. הדבר משפיע כמה pods קוברנטיס יכול “להוריד במכה” כאשר הוא מתקין גרסה חדשה. המספר מתייחס למספר ה pods התקינים בגרסה הישנה + החדשה ביחד, וברירת המחדל היא 25%. ניתן גם לקבוע 0.
- maxSurge – הוא פחות או יותר הפרמטר ההופכי: כמה pods חדשים ניתן להרים “במכה”. ככל שהמספר גדול יותר – כך ה rolling deployment עשוי להיות מהיר יותר. גם כאן ברירת המחדל היא 25%.
בקיצור גמור: אם יש לנו 4 pod replicas, הערכים שקבענו ב manifest יבטיחו שה cluster תמיד יכיל בין 3 ל 5 pod replicas בזמן deployment.
- minReadySeconds – שדה רשות (ערך ברירת מחדל = 0) שמציין כמה שניות לחכות לאחר שה pod מוכן (“ready”) לפני שמעבירים לו תעבורה. ההמתנה הזו היא פרקטיקה מקובלת, מכיוון שהנזק מ pod בעייתי שמחובר ל production traffic – עשוי להיות משמעותי. אפשר להיתקל גם בערכים של 20 ו 30 שניות. חשוב להזכיר שערך גבוה יאט את תהליך ה rolling deployment מכיוון שאנו ממתינים ל minReadySeconds – לפני שאנו ממשיכים להחליף עוד pods.
- כאן שווה להזכיר את ה Readiness & Liveliness Probes של קוברנטיס. קוברנטיס מריץ ב nodes רכיב טכני הרץ כ container ומבצע בדיקות Health-check על ה pods השונים ב node ומדווח את התוצאות הלאה. כל pod צריך לענות ל2 קריאות: well-known/live./ ו well-known/ready./
- מצב ה live הוא אות חיים בסיסי. המימוש המומלץ הוא פשוט להחזיר HTTP 200 OK מבלי לבצע פעולות נוספות. אם התשובה המתקבלת היא לא 2xx – קוברנטיס יאתחל את ה pod הזה מיד.
- מצב ה ready אמור להיות עשיר יותר, בד”כ בודקים גישה לבסיס הנתונים (“SELECT 1”) או גישה למשאבים קריטיים אחרים ל Pod (למשל: גישה לרדיס, או שירותים אחרים הקריטיים לפעילות ה pod). אם האפליקציה עוברת עדכון (למשל: הטמעת קונפיגורציה חדשה / עדכון caches ארוך) – הדרך הנכונה היא להגדיר אותה כ “לא ready” בזמן הזה.
אם התשובה ל ready היא שלילית, קוברנטיס עשויה לנתק אותו מתעבורה נכנסת עד שיסתדר. אם המצב מתמשך (ברירת המחדל = 3 כישלונות רצופים) – ה pod יעבור restart.- שגיאה נפוצה היא להגדיר את live ו ready אותו הדבר – אבל אז מערבבים פעולות live יקרות מדי, ו restarts מיותרים של pods (כי עשינו restart בעקבות live אחד שכשל, נניח – מתקלת רשת נקודתית ואקראית).
- כאן שווה להזכיר את ה Readiness & Liveliness Probes של קוברנטיס. קוברנטיס מריץ ב nodes רכיב טכני הרץ כ container ומבצע בדיקות Health-check על ה pods השונים ב node ומדווח את התוצאות הלאה. כל pod צריך לענות ל2 קריאות: well-known/live./ ו well-known/ready./
$ kubectl apply -f my-deployment.yaml
$ kubectl get deployment hello-deploy
אם אנו מגלים שהגרסה לא טובה (נניח: עלה באג חדש ומשמעותי לפרודקשיין), אנו יכולים לבצע rollback. הפקודה:
$ kubectl rollout history deployment hello-deploy
$ kubectl rollout undo deployment hello-deploy --to-revision=1
סיכום
סקרנו את משאבי ה ReplicaSet וה Deployment בקוברנטיס – הדרך העיקרית לעדכן את המערכת ב pods חדשים / מעודכנים, בצורה Resilient.
על הדרך, הרחבנו מעט את ההבנה כיצד קוברנטיס עובד.
עדיין, לאחר שביצענו deployment לא נוכל לגשת ל pods שלנו (בקלות הרצויה). לשם כך עלינו להגדיר עוד משאב-ליבה בקוברנטיס בשם Service, המתייחס ל”קבוצה של Pod replicas בעלי אותו הממשק”.
סמנטיקת ה Service מתוכננת להיות הנושא לפוסט הבא בסדרה.
שיהיה בהצלחה!
פוסט יפה ואינפורמטיב, מתמצת בצורה יפה את הנושא. אשמח להמשך.
תודה רבה!הוא ב TODO שלי – בלי נדר!
פוסט מעולה, מת על ההסברים שלך.. מחכה להמשך
היי, תודה רבה וסליחה על השאלה המאוחרת…
כתבת על מספר הפודים:”עולה בצורה דינאמית כאשר יש עומס על ה Pods (למשל: CPU utilization מעל 60%)”
מאיפה נמדד ה100%? מה host machine כלומר 60% זה מכל הcpu במכונה, מהתהליך של docker? ממשהו אחר?
היי דן,
ה 100% של ה CPU הוא מה שהוקצה ל container, ובהחלט לא זה של ה host machine. לכל container שרץ אפשר (ומומלץ) להגביל את כמות הזיכרון וה CPU שהוא יכול לצרוך. את ההגדרות הללו עושים כחלק מההגדרות של pod/resources. המשמעות של CPU תלויה בסביבת הריצה ולרוב היא תהיה ביחידות של virtual CPU ע”פ הגדרות של סביבת הענן (אמזון וכו’) או הוירטואליזציה. למשל: אנו משתמשים במכונות עם 16 vCPU להריץ Kubernetes worker node – אבל מגבילים את ה pods לא לצרוך יותר מ 1 או 2 vCPU. כחלק מה autoscaling, אנו יכולים להגדיר שאם ה container/pod צורך יותר מ 60% מהמשאבים שהוקצו לו – נרים עוד מופע של אותו ה pod כדי לתת לו מרחב נשימה.
אפשר לקרוא על זה עוד בתיעוד של קוברנטיס, למשל: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/