333
1

קוברנטיס: איך מייצרים אפליקציה עם יתירות גבוהה

333
זמן קריאה: 6 דקות

כתבתם קוד, הרצתם טסטים ובניתם אפליקציה ואתם בטוחים שהקוד מוכן לפרודקשן. 

אם בעבר היינו לוקחים שרת, מתקינים את התלויות ומריצים את האפליקציה, היום כדי להנגיש את האפליקציה ולהרוויח את היתרונות של מיקרו סרוויסים וקוברנטיס יש צורך בקבצי קונפיגורציה המתארים את הפיצ’רים בקוברנטיס שבהם אנחנו רוצים להשתמש. איך אנחנו רוצים שהאפליקציה תהיה מונגשת ואיך אנחנו רוצים שהאפליקציה תתנהל במצב רגיל ובשדרוג. 

הבהרה: בכתבה לא נדבר על אפליקציה ששומרת עליה תוכן למשל דאטא בייס, נתמקד באפליקציה שהיא stateless כלומר ללא מצב, למשל שרת ווב. 

בנוסף הכתבה תתמקד רק על הצד הקוברנטיסי ולא על הקוד של האפליקציה עצמה. 

אז מה זה בעצם יתירות ולמה היא חשובה כל כך?
יתירות על פי הגדרה היא:

כפילות המשמשת למניעת כשל או קריסת המערכת.

היתירות היא אחת התכונות החזקות בקוברניטיס המקנות יציבות וזמינות לאפליקציות שרצות על גביה. בכתבה נלמד את היכולות הקיימות ואיך לנצל אותן בצורה מיטבית.  

לפני שנתחיל לכתוב את קבצי הקונפיגורציה של האפליקציה שלנו נשאל שאלה פשוטה. איך קוברנטיס יודע מה מצבם של האפליקציות שהוא מריץ?

 למשל, האם האפליקציה מוכנה לקבל בקשות?

 האם האפליקציה איבדה יכולת לתקשר עם הדאטא בייס שלה או שהוא נכנס ל Deadlock?

בשביל זה יש רכיב שנקרא kubelet (נדבר עליו לעומק בפוסט אחר) שיודע לעשות דבר שנקרא probe. הprobe יכול לדגום על ידי בקשת HTTP או TCP למקום מוגדר, ויחזיר תשובה תקינה אם האפליקציה מוכנה.

בקטגוריה הזו יש 3 סוגים שונים. 

Readiness probe – מגדיר אם הpod מוכן לקבל בקשות. אם הבדיקה נכשלת בינתיים לא נעביר בקשות מהמשתמש לpod הזה. כשהדגימה תקבל תשובה של הצלחה הpod נכנס למצב מוכן והוא יכנס תחת מאזן העומסים של ה service ויקבל בקשות.

Liveness probe – מגדיר האם הpod במצב תקין או שגוי. במידה והוא מחזיר מצב שגוי יתבצע איתחול לpod .

Startup probe – מתאים לאפליקציות שלוקח להם זמן רב לעלות. הדגימה בודקת מתי אפשר להתחיל להריץ את הprobes האחרים.

עכשיו שאנחנו יודעים איך להעביר לקוברנטיס את מצב האפליקציה שלנו, נגדיר את הרכיבים שאנחנו צריכים בקבצי קונפיגורציה של האפליקציה.

  • Deployment 
    • Probes
    • Requests\Limits
    • Upgrade strategy
  • Service
    • Type
  • Pdb

Deployment:

קובץ הקונפיגורציה של ה Deployment מתאר איך האפליקציה צריכה להתנהג בתוך קוברנטיס.

				
					apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
        version: v1
    spec:
      containers:
      - name: my-app
        image: my-app-image
        resources:
          limits:
            memory: 600Mi
            cpu: 1
          requests:
            memory: 300Mi
            cpu: 500m 
        ports:
        - name: http
          containerPort: 8000
        - name: probe
          containerPort: 8001
        livenessProbe:
          httpGet:
            path: /live
            port: probe
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: probe
          initialDelaySeconds: 15
          periodSeconds: 5


				
			

נסתכל על שני חלקים בצורה מעמיקה 

Upgrade strategy:

				
					strategy:
type: RollingUpdate
rollingUpdate:
  maxSurge: 1
  maxUnavailable: 0
				
			

ישנם שני אסטרטגיות לשדרוג של האפליקציה:

אסטרטגיה  ראשונה היא recreate כלומר תמחק ותיצור חדש. השיטה מהירה מאוד אבל השירות נפגע.

כך נראה השדרוג באסטרטגיה הזו:

אסטרטגיה נוספת שקיימת היא RollingUpdate כלומר שדרוג בחלקים. השדרוג איטי יותר אבל דואג שלא יהיה מצב שהשירות שלנו לא תהיה זמינה.

וכך נראה השדרוג באסטרטגיה הזו:

maxSurge – מגדיר כמה בכמה podים אפשר לחרוג מעבר לכמות המוגדרת ב Deployment 

במקרה שלנו 5+1 

ולכן יכול להיות עד 6 podים ביחד. 

maxUnavailable – מגדיר כמה podים אפשר לחרוג מתחת לכמות המוגדרת ב Deployment 

במקרה שלנו 5-0 

ולכן לא יהיה אף פעם פחות מ 5 podים ביחד.

Resource limits:

				
					resources:
  limits:
    memory: 600Mi
    cpu: 1
  requests:
    memory: 300Mi
    cpu: 500m 


				
			

ישנם שני סוגי הגדרות ביחס ל cpu ו memory 

Requests – מגדיר את הכמות המינימלית של המשאבים שצריך שיהיה פנוי בשרת כדי שנוכל להריץ את הpod  עליו.

Limits – הכמות המקסימלית של המשאבים שיכול ה pod לצרוך. 

במקרה ונעבור את מגבלת הזיכרון נקבל oom וריסטרט לpod. 

במקרה שנעבור את את מגבלת ה cpu יתחיל תהליך של cpu throttling. 

Service:

קובץ הקונפיגורציה של ה service מגדיר איך ניתן לגשת לאפליקציה.

				
					apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 8000
    - name: probe
      protocol: TCP
      port: 8001
      targetPort: 8001
  type: LoadBalancer
				
			

נתייחס כאן לכמה חלקים:

				
					selector:
      app: my-app
				
			

כדי להכווין את הבקשות שמגיעות לservice אל הpodים המתאימים צריך להשתמש בתיוגים(labels).

אם נסתכל על ה deployment שכתבנו למעלה יש תיוג

app: my-app

ולכן אצלנו בservice נגדיר שינתב את הבקשות לpodים עם התיוגים המתאימים.

				
					apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
				
			
				
					apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
				
			

דבר נוסף שחשוב שנשים לב אליו הוא

type: LoadBalancer

נזכיר שתפקיד הservice הוא להיות מאזן עומסים מעל הpodים והוא נקודת הגישה לאפליקציה.

ישנם שני כיוונים שמהם צורכים את המיקרו-סרביסים:

  • מתוך הcluster – למשל רכיב צד שרת. הפניות אל רכיב כזה לא נעשות ישירות מהמשתמש אלא מרכיב מתווך שנמצא גם כן בתוך הcluster.
  • מחוץ לcluster – למשל רכיב  צד לקוח. הפניות אל רכיב כזה נעשות ישירות מהמשתמש.

ברירת המחדל של service בקוברנטיס הוא clusterIP, כלומר לחשוף את האפליקציה רק בצורה פנימית דבר שמתאים לסרביסים מהסוג הראשון בלבד.

במצב של אפליקציה מהסוג השני שרוצים לחשוף אותו החוצה ישנם 2 אופציות:

  1. NodePort
  2. LoadBalancer

הראשון NodePort נותן פורט שיהיה נגיש מעל כל שרת בcluster למשל אם נפנה לnode3 בפורט 31600 הוא יפנה אותנו ל service my-service 

השני LoadBalancer נותן IP חיצוני שנגיש מכל מקום. בכל ספק שירות קוברנטיס הדבר ממומש בצורה קצת שונה אך התוצאה הסופית זהה.

בנוסף, יש אפשרות לחשוף את האפליקציה בצורה חיצונית באמצעות Ingress. Ingress הוא לא type אמיתי אלא דרך להנגיש מחוץ לcluster באמצעות רכיב נוסף(לא נרחיב עליו כעת).

Pdb:

אחד היתרונות הבולטים בקוברנטיס זה שהתשתית היא לא חייבת להיות סטטית, כלומר ניתן להחליף ללא שום מאמץ את השרת שעליו רצים הקונטיינרים.

מצב זה יכול ליצור מצבים בעייתים, למשל אם יש לנו deployment עם 3 pods ולצורך תחזוקה מורידים את השרת node1:

בשלב השני יש מצב שבו יהיה לנו כמות podים קטנה משמעותית מהכמות הרצויה עד שהם יעלו בשרת אחר, מצב שיכול לפגוע באיכות השירות שאנחנו מספקים.

לכן בכדי לשלוט על המצב הזה, ניצור pdb.

				
					apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: my-app-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: my-app
				
			

נראה שהשתמשנו שוב פעם בlabel כדי לשייך את הpdb לdeployment מסוים.

בנוסף הגדרנו שלא יכול להיות מצב שבו יהיה פחות מ2 podים בו״ז ולכן הורדת השרת תקבל השהייה כד למצב שבו המינימום הנדרש יתקיים:

במצב זה שלטנו על המצב ולא נתנו לשירות שלנו להיפגע באופן משמעותי.

הערה: חשוב לדעת שאם נגדיר שצריך 100% מהpods למעלה כל הזמן זה ימנע להוריד שרתים בכלל, ולכן לא מומלץ להגדיר pdb עם הגדרות כאלה.

לסיכום: כאשר אנחנו רוצים לעלות לפרודקשן חשוב לדאוג להנגיש בצורה נכונה ומתאימה את השירות. בנוסף יש לנצל את הדרכים שקוברנטיס יודע לספק לנו כדי שהשירות שלנו יהיה עם יתירות וזמינות כמה שיותר גבוהות.

יונה דיסין
WRITEN BY

יונה דיסין

Devops Engineer @VMware
נהנה להתעדכן ולהשתמש בטכנולוגיות החמות והחדשות בשוק.

One thought on “קוברנטיס: איך מייצרים אפליקציה עם יתירות גבוהה

  1. […] יצרנו קונטיינר שירוץ בקוברנטיס, לאחר זמן קצר הוא יתחיל לרוץ באחד השרתים בקלאסטר. במאמר הקרוב נדבר על איך קורה הבחירה של השרת הדרכים השונות לבחור מקום ועוד.  […]

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *