1. היי!
תודה שהצטרפת אלינו ל-Codelab של Google בנושא Istio Multi Cloud Burst.כדי להשתתף ב-Codelab הזה, צריך ניסיון מעשי ברמת מתחילים ב-Kubernetes, ב-Node וב-Go. מה צריך
|
|
מה תלמדו
- איך יוצרים אשכול Kubernetes ב-GKE
- איך להתקין את Istio באשכול Kubernetes באמצעות Helm
- איך להתקין את Istio Multicluster באמצעות Helm
- פריסת אפליקציית אינטרנט מקוד המקור ל-Kubernetes
- כתיבת כללי ניתוב תנועה והחלה שלהם על Istio
- מדדי Prometheus
- פיתוח ושליפה של קובצי אימג' של קונטיינרים בתוך אשכול Kubernetes
2. תהליך ההגדרה
אפשר לעקוב אחרי הקוד של Codelab הזה באחד מהדרכים הבאות:
- Google Cloud Shell (מומלץ): מעטפת בדפדפן, עם כלים מותקנים
- במחשב הנייד (פועלים לפי ההוראות הבאות)
תחילת העבודה עם Google Cloud Platform
- אם אין לכם חשבון GCP, תוכלו לקבל כרטיס לחשבון משתמש בחינם מהמרצה.
- נכנסים למסוף Google Cloud ולוחצים על 'בחירת פרויקט':

- מקדישים הערה למזהה הפרויקט במקום כלשהו, ואז לוחצים על הפרויקט כדי לבחור אותו:

אפשרות 1: שימוש ב-Google Cloud Shell (מומלץ)
Cloud Shell מספק מעטפת של שורת פקודה בתוך הדפדפן, עם הכלים הנדרשים שמותקנים ומאומתים באופן אוטומטי בחשבון שלכם ב-Google Cloud Platform. (אם אתם לא רוצים להריץ את התרגיל הזה ב-Cloud Shell, תוכלו לדלג לקטע הבא).
נכנסים ל-Cloud Console ולוחצים על 'הפעלת Cloud Shell' בסרגל הכלים שבפינה הימנית העליונה:

הוספת כלים ל-Cloud Shell
- התקנה
kubectx****: מורידים את סקריפט ה-bash מכאן למיקום כלשהו ב-PATH. - מתקינים את
helm****: פועלים לפי ההוראות האלה.
לחלופין, אפשר להריץ את הפקודות הבאות כדי להתקין את שניהם ב-~/.bin ולהוסיף אותו למשתנה $PATH:
mkdir -p ~/.bin && \
cd ~/.bin && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubectx && \
chmod +x kubectx && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubens && \
chmod +x kubens && \
curl -LO https://storage.googleapis.com/kubernetes-helm/helm-v2.12.0-linux-amd64.tar.gz && \
tar xzf helm-v2.12.0-linux-amd64.tar.gz && \
rm helm-v2.12.0-linux-amd64.tar.gz && \
mv linux-amd64/helm ./helm && \
rm -r linux-amd64 && \
export PATH=${HOME}/.bin:${PATH}
ריכזנו כאן כמה טיפים מהירים שיעזרו לכם להשתמש ב-Cloud Shell בקלות רבה יותר:
1. מנתקים את המעטפת לחלון חדש: |
|
2. באמצעות עורך קבצים: לוחצים על סמל העיפרון בפינה השמאלית העליונה כדי להפעיל עורך קבצים בדפדפן. זה שימושי כי אנחנו נעתיק קטעי קוד לקבצים. |
|
3. להתחיל כרטיסיות חדשות: אם אתם צריכים יותר מהנחיה אחת במסוף. |
|
4. הגדלת הטקסט: גודל הגופן שמוגדר כברירת מחדל ב-Cloud Shell יכול להיות קטן מדי לקריאה. | Ctrl-+ ב-Linux או ב-Windows, ⌘-+ ב-macOS |
אפשרות 2: הגדרת המחשב הנייד (לא מומלץ)
אם אתם מעדיפים להשתמש בסביבת תחנת העבודה שלכם במקום ב-Cloud Shell, תוכלו להגדיר את הכלים הבאים:
- מתקינים את
gcloud:(הוא מותקן מראש ב-Cloud Shell). פועלים לפי ההוראות כדי להתקין אתgcloudבפלטפורמה. נשתמש בו כדי ליצור אשכול Kubernetes. - מתקינים את
kubectl:(הוא מותקן מראש ב-Cloud Shell). מריצים את הפקודה הבאה כדי להתקין:
gcloud components install kubectl
מריצים את הפקודה הבאה כדי לאמת את gcloud. תתבקשו להתחבר באמצעות חשבון Google. לאחר מכן, בוחרים את הפרויקט שנוצר מראש (שמוצג למעלה) כפרויקט ברירת המחדל. (אפשר לדלג על הגדרת תחום מחשוב):
gcloud init
- התקנה
curl:מותקנת מראש ברוב המערכות של Linux/macOS. סביר להניח שכבר יש לכם אותו. אחרת, אפשר לחפש באינטרנט איך להתקין אותו. - התקנה
kubectx****: מורידים את סקריפט ה-bash מכאן למיקום ב- $PATH - מתקינים את
helm****: פועלים לפי ההוראות האלה.
3. הגדרת פרויקט GCP
מפעילים בפרויקט את ממשקי ה-API של GKE (Google Kubernetes Engine), GCR (Google Container Registry) ו-GCB (Google Cloud Build):
gcloud services enable \ cloudapis.googleapis.com \ container.googleapis.com \ containerregistry.googleapis.com \ cloudbuild.googleapis.com
הגדרה של משתני סביבה
במהלך ההגדרה נעבוד הרבה עם הפרויקט ב-Google Cloud, לכן כדאי להגדיר משתנה סביבה לשימוש מהיר
export GCLOUD_PROJECT=$(gcloud config get-value project)
במהלך הסדנה נוצר קוד וקבצי תצורה, לכן נוצר ספריית פרויקט ונעבור אליה.
mkdir -p src/istio-burst && \ cd src/istio-burst && \ export proj=$(pwd)
4. יצירת אשכול Kubernetes 'ראשי'
אתם יכולים ליצור בקלות אשכולות Kubernetes מנוהלים באמצעות Google Kubernetes Engine (GKE).
הפקודה הבאה תיצור אשכול Kubernetes:
- בשם 'primary',
- באזור us-west1-a,
- הגרסה האחרונה של Kubernetes שזמינה,
- עם 4 צמתים ראשוניים
export cluster=primary
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "4" --network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias
(הפעולה עשויה להימשך כ-5 דקות. אפשר לצפות באשכול שנוצר במסוף Cloud.)
אחרי יצירת האשכולות של Kubernetes, gcloud מגדיר את kubectl עם פרטי הכניסה שמפנים לאשכולות.
gcloud container clusters get-credentials $cluster --zone=$zone
עכשיו אמורה להיות לך אפשרות להשתמש ב-kubectl עם האשכולות החדשים.
מריצים את הפקודה הבאה כדי להציג את צמתים של Kubernetes באשכולות (הסטטוס שלהם אמור להיות 'מוכן'):
kubectl get nodes
שינוי שמות ב-Kubeconfig כדי להקל על השימוש
אנחנו עוברים בין ההקשרים בתדירות גבוהה, ולכן כדאי להשתמש בכינוי קצר לאשכולות שלנו.
הפקודה הזו תשנה את השם של הרשומה ב-kubeconfig שיצרתם ל-primary.
kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}
הגדרת הרשאות:
כדי לפרוס את Istio, צריך להיות לכם הרשאת אדמין באשכול. הפקודה הזו תגדיר את כתובת האימייל שמשויכת לחשבון Google Cloud שלכם כאדמין של האשכולות
kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole=cluster-admin \
--user=$(gcloud config get-value core/account)
5. יצירת אשכול ל'פרץ פעילות'
הפקודה הבאה תיצור אשכול Kubernetes:
- בשם 'burst',
- באזור us-west1-a,
- הגרסה האחרונה של Kubernetes שזמינה,
- עם צומת ראשוני אחד
- התכונה 'שינוי אוטומטי של קצב הקצאת המשאבים' מופעלת עד 5 צמתים
export cluster=burst
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "1" --enable-autoscaling --min-nodes=1 --max-nodes=5 \
--network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias
(הפעולה עשויה להימשך כ-5 דקות. אפשר לצפות באשכול שנוצר במסוף Cloud.)
אחרי יצירת האשכולות של Kubernetes, gcloud מגדיר את kubectl עם פרטי הכניסה שמפנים לאשכולות.
gcloud container clusters get-credentials $cluster --zone=$zone
עכשיו אמורה להיות לך אפשרות להשתמש ב-kubectl עם האשכולות החדשים.
מריצים את הפקודה הבאה כדי להציג את צמתים של Kubernetes באשכולות (הסטטוס שלהם אמור להיות 'מוכן'):
kubectl get nodes
שינוי שמות ב-Kubeconfig לשיפור הנוחות
הפקודה הזו תשנה את הערך של הרשומה ב-kubeconfig שיצרתם עכשיו ל-burst.
kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}
הגדרת הרשאות:
כדי לפרוס את Istio Remote, צריך להיות לכם הרשאת אדמין באשכול. הפקודה הזו תגדיר את כתובת האימייל שמשויכת לחשבון Google Cloud שלכם כאדמין של האשכולות
kubectl create clusterrolebinding cluster-admin-binding \
--clusterrole=cluster-admin \
--user=$(gcloud config get-value core/account)
6. החלת כללי חומת אש
כדי ששני האשכולות שלנו יוכלו לתקשר ביניהם, נצטרך ליצור כלל של חומת אש.
מריצים את הפקודות הבאות כדי ליצור כלל חומת אש ב-Google Cloud Platform שיאפשר לאשכולות שלנו לתקשר.
function join_by { local IFS="$1"; shift; echo "$*"; }
ALL_CLUSTER_CIDRS=$(gcloud container clusters list \
--filter="(name=burst OR name=primary) AND zone=$zone" \
--format='value(clusterIpv4Cidr)' | sort | uniq)
ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
ALL_CLUSTER_NETTAGS=$(gcloud compute instances list \
--filter="(metadata.cluster-name=burst OR metadata.cluster-name=primary) AND metadata.cluster-location=us-west1-a" \
--format='value(tags.items.[0])' | sort | uniq)
ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
gcloud compute firewall-rules create istio-multicluster-test-pods \
--allow=tcp,udp,icmp,esp,ah,sctp \
--direction=INGRESS \
--priority=900 \
--source-ranges="${ALL_CLUSTER_CIDRS}" \
--target-tags="${ALL_CLUSTER_NETTAGS}" --quiet
הגדרנו את שני האשכולות שלנו והם מוכנים לפריסה של האפליקציה ושל Istio.
7. מבוא ל-Istio
מהו Istio?
Istio הוא מישור בקרה של service mesh שמטרתו "לקשר, לאבטח, לשלוט ולנטר שירותים". הוא עושה זאת במגוון דרכים, אבל בעיקר על ידי הוספת קונטיינר proxy ( Envoy) לכל אחד מה-Pods של Kubernetes שנפרסו. מאגר ה-proxy שולט בכל תקשורת הרשת בין מיקרו-שירותים בשילוב עם מדיניות למטרות כלליות ועם מרכז טלמטריה ( Mixer).

אפשר להחיל את כללי המדיניות האלה בנפרד מהפריסות והשירותים של Kubernetes. המשמעות היא שמפעיל הרשת יכול לעקוב אחרי פעילות הרשת, להגביל, להפנות או לשכתב את כללי המדיניות ברשת בלי לפרוס מחדש את האפליקציות המשויכות.
חלק מהתכונות של ניהול התנועה שנתמכות ב-Istio הן:
- מפסקים חשמליים
- חלוקת תנועה לפי אחוזים
- כתיבה מחדש של כתובות URL
- סיום TLS
- בדיקות תקינות
- איזון עומסים
לצורך הסדנה הזו, נתמקד בחלוקת תנועה לפי אחוזים.
מונחי Istio שנעשה בהם שימוש
VirtualService
VirtualService מגדיר קבוצת כללים לניתוב תנועה שחלים כשמפנימים לארח.
Gateway
שער הוא מאזן עומסים שפועל בקצה של הרשת המשולבת ומקבל חיבורי HTTP/TCP נכנסים או יוצאים. בשערים אפשר לציין יציאות, הגדרות SNI וכו'.
DestinationRule
DestinationRule מגדיר כללי מדיניות שחלים על תנועה המיועדת לשירות אחרי הניתוב. הן מציינות את ההגדרות של איזון העומסים, גודל מאגר החיבורים מהתוסף ושל הגדרות זיהוי חריגים.
Istio Multicluster
יכול להיות ששמתם לב כשיצאנו את שני האשכולות שלנו, שהאשכול primary כלל 4 צמתים ללא התאמה אוטומטית לעומס, והאשכול burst כלל צומת אחד עם התאמה אוטומטית לעומס עד 5 צמתים.
יש שתי סיבות להגדרה הזו.
קודם כול, אנחנו רוצים לדמות תרחיש של העברה מ-on-prem ל-Cloud. בסביבה מקומית אין לכם גישה לאשכולות עם התאמה אוטומטית לעומס, כי יש לכם תשתית קבועה.
שנית, הגדרה של 4 צמתים (כפי שהוגדר למעלה) היא הדרישות המינימליות להרצת Istio. השאלה המתבקשת היא: אם נדרשים ל-Istio לפחות 4 צמתים, איך אפשר להריץ את Istio באשכול burst עם צומת אחד? התשובה היא שב-Istio Multicluster מותקנת קבוצה קטנה בהרבה של שירותי Istio, והוא מתקשר עם התקנת Istio באשכול הראשי כדי לאחזר את כללי המדיניות ולפרסם את נתוני הטלמטריה.
8. סקירה כללית על ארכיטקטורת אפליקציות
סקירה כללית על הרכיבים
אנחנו פורסים אפליקציה תלת-שכבתית באמצעות NodeJS ו-Redis.
Worker
אפליקציית העבודה נכתבת ב-NodeJS, והיא תאזין לבקשות HTTP POST נכנסות, תבצע עליהן פעולת גיבוב, ואם מוגדר משתנה סביבה בשם PREFIX, היא תוסיף את הערך הזה לתחילת הגיבוב. אחרי חישוב הגיבוב, האפליקציה שולחת את התוצאה בערוץ calculation בשרת Redis שצוין.
בהמשך נשתמש במשתנה הסביבה PREFIX כדי להדגים את הפונקציונליות של מספר אשכולות.
לידיעתך: אלו החבילות שבהן האפליקציה משתמשת.
body-parser:מאפשרת לנו לנתח את בקשות ה-http שלנוcors:מאפשרת להשתמש בשיתוף משאבים בין מקורות (CORS)dotenv:ניתוח קל של משתני סביבהexpress:אירוח פשוט של אתריםioredis:ספריית לקוח לתקשורת עם מסדי נתונים של Redismorgan:יומן מובנה נחמד
חזית
חזית האתר שלנו היא גם אפליקציית NodeJS שמארחת דף אינטרנט באמצעות express. הוא מקבל תדירות שהמשתמש מזין ושולח בקשות לאפליקציה worker שלנו בקצב הזה. האפליקציה הזו גם נרשמת להודעות בערוץ Redis בשם calculation ומציגה את התוצאות בדף אינטרנט.
האפליקציה משתמשת ביחסי התלות הבאים.
body-parser:מאפשרת לנו לנתח את בקשות ה-http שלנוdotenv:ניתוח קל של משתני סביבהexpress:אירוח פשוט של אתריםioredis:ספריית לקוח לתקשורת עם מסדי נתונים של Redismorgan:יומנים מובְנים נוחיםrequest:הרשאה לשלוח בקשות HTTPsocket.io:מאפשר תקשורת דו-כיוונית מדף האינטרנט לשרת
בדף האינטרנט הזה נעשה שימוש ב-Bootstrap לצורך עיצוב, והוא נראה כך כשמריצים אותו:

תרשים הארכיטקטורה

תרשים פריסה
אנחנו פורסים את האפליקציה הסופית בשני האשכולות שיצרנו. כל הרכיבים (frontend, worker ו-Redis) יפורסו באשכול primary, אבל רק האפליקציה worker יפורסה באשכול burst.
לפניכם תרשים שמתאר את שני האשכולות. התיבות שמסומנות בקו אדום הן שירותי Kubernetes, והתיבות שמסומנות בקו כחול הן פריסות של Kubernetes. התיבות הצהובות מציינות את ההתקנה שלנו של Istio.
שימו לב שעדיין יש שירות ל-Redis באשכול burst, למרות שאין פריסה של Redis באשכול. אנחנו צריכים את השירות הזה באשכולות כדי ש-Kubernetes DNS יוכל לפתור את הבקשה, אבל כשהבקשה תישלח בפועל, שרת ה-proxy של Istio יפנה מחדש את הבקשה לפריסה של Redis באשכולות primary.
לאפליקציה הסופית תהיה פריסה נוספת שתופעל באשכול primary בשם istiowatcher.. כך נוכל לנתב מחדש באופן דינמי את התנועה אל burst באופן אוטומטי כשהתנועה שלנו חורגת מסף מסוים.

9. יצירת קבצים לפריסה של אפליקציות
אנחנו צריכים ליצור קבוצה של מניפסטים של Kubernetes כדי לפרוס את האפליקציה שלנו
עוברים לספריית הבסיס של הפרויקט ויוצרים תיקייה חדשה בשם kubernetes
mkdir ${proj}/kubernetes && cd ${proj}/kubernetes
כתיבת frontend.yaml
הפעולה הזו תיצור גם פריסה וגם שירות ב-Kubernetes כדי לגשת לקובץ האימג' של הקצה הקדמי.
מוסיפים את הטקסט הבא ל-frontend.yaml.
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: frontend-deployment
labels:
app: frontend
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: gcr.io/istio-burst-workshop/frontend
ports:
- containerPort: 8080
readinessProbe:
initialDelaySeconds: 10
httpGet:
path: "/_healthz"
port: 8080
httpHeaders:
- name: "Cookie"
value: "istio_session-id=x-readiness-probe"
livenessProbe:
initialDelaySeconds: 10
httpGet:
path: "/"
port: 8080
httpHeaders:
- name: "Cookie"
value: "istio_session-id=x-liveness-probe"
env:
- name: PORT
value: "8080"
- name: PROCESSOR_URL
value: "http://worker-service"
- name: REDIS_URL
value: "redis-cache-service:6379"
---
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
type: ClusterIP
selector:
app: frontend
ports:
- name: http
port: 80
targetPort: 8080
דברים חשובים שצריך לשים לב אליהם ב-Deployment
- צינינו שהיציאה שבה האפליקציה תפעל היא
8080 - הגדרנו את הכתובת של העובד כ-"
http://worker-service", ונשתמש בתכונה המובנית של DNS ב-Kubernetes כדי לפתור את השירות שנוצר - הגדרנו את הכתובת של
REDIS_URLכ-"redis-cache-service:6379", ונשתמש בתכונה המובנית של DNS ב-Kubernetes כדי לפתור את כתובות ה-IP שהתקבלו. - הגדרנו גם בדיקות
livenessו-readinessבקונטיינר כדי לעדכן את Kubernetes כשהקונטיינר פועל.
כתיבה של worker-service.yaml
אנחנו כותבים את הגדרת השירות ב-Kubernetes בקובץ נפרד מהגדרת הפריסה, כי נשתמש שוב בשירות הזה בכמה אשכולות, אבל נכתוב פריסה שונה לכל אשכול.
מוסיפים את הטקסט הבא בשדה worker-service.yaml
apiVersion: v1
kind: Service
metadata:
name: worker-service
spec:
type: ClusterIP
selector:
app: worker
ports:
- name: http
port: 80
targetPort: 8081
כתיבה של worker-primary.yaml
זו תהיה הפריסה של worker שנדחוף לאשכולות הראשי.
מוסיפים את הטקסט הבא ל-worker-primary.yaml.
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: worker-deployment
labels:
app: worker
spec:
replicas: 1
selector:
matchLabels:
app: worker
template:
metadata:
labels:
app: worker
cluster-type: primary-cluster
spec:
containers:
- name: worker
image: gcr.io/istio-burst-workshop/worker
imagePullPolicy: Always
ports:
- containerPort: 8081
readinessProbe:
initialDelaySeconds: 10
httpGet:
path: "/_healthz"
port: 8081
httpHeaders:
- name: "Cookie"
value: "istio_session-id=x-readiness-probe"
livenessProbe:
initialDelaySeconds: 10
httpGet:
path: "/"
port: 8081
httpHeaders:
- name: "Cookie"
value: "istio_session-id=x-liveness-probe"
env:
- name: PORT
value: "8081"
- name: REDIS_URL
value: "redis-cache-service:6379"
שימו לב שאנחנו פועלים לפי אותו דפוס של מתן בדיקות liveness ו-readiness, וגם מציינים את משתני הסביבה PORT ו-REDIS_URL לשימוש באפליקציה.
דבר נוסף שחשוב לציין בפריסה הזו הוא היעדר משתנה הסביבה PREFIX. המשמעות היא שתוצאות החישוב שלנו יהיו גרסאות גולמיות של גיבוב (hash) (ללא תוספת קידומת).
הנקודה החשובה האחרונה בפריסה הזו היא התווית cluster-type: primary-cluster. נשתמש בו בהמשך כשנדון בניתוב תנועה ב-Istio Multicluster
כתיבה של redis.yaml
התקשורת מה-worker חזרה לחזית מתבצעת דרך ערוץ Redis, ולכן אנחנו צריכים לפרוס אפליקציית Redis באשכול שלנו.
מוסיפים את הטקסט הבא לקובץ redis.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: redis-cache
spec:
template:
metadata:
labels:
app: redis-cache
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
readinessProbe:
periodSeconds: 5
tcpSocket:
port: 6379
livenessProbe:
periodSeconds: 5
tcpSocket:
port: 6379
volumeMounts:
- mountPath: /data
name: redis-data
resources:
limits:
memory: 256Mi
cpu: 125m
requests:
cpu: 70m
memory: 200Mi
volumes:
- name: redis-data
emptyDir: {}
זוהי פריסה סטנדרטית למחצה של אפליקציית Redis. הוא יוצר קונטיינר על סמך התמונה redis:alpine, חושף את היציאות המתאימות ומגדיר מגבלות הגיוניות על המשאבים.
כתיבת redis-service.yaml
אנחנו צריכים שירות Kubernetes כדי לתקשר עם אפליקציית Redis שלנו
מוסיפים את הטקסט הבא לקובץ redis-service.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-cache-service
spec:
type: ClusterIP
selector:
app: redis-cache
ports:
- port: 6379
targetPort: 6379
כך השירות בשם redis-cache-service יקבל גישה לפריסה של Redis.
10. פריסת האפליקציה
אחרי ששלחנו את התמונות שלנו ל-GCR וכתבנו את המניפסטים של Kubernetes, זה הזמן להפיץ את האפליקציה ולראות איך היא פועלת.
מריצים את הפקודות הבאות כדי לפרוס את האפליקציה
- מוודאים שנמצאים באשכול הנכון
kubectx primary
- פריסה של Redis Cache
kubectl apply -f redis.yaml
- פריסה של שירות Redis
kubectl apply -f redis-service.yaml
- פריסה של הקצה הקדמי
kubectl apply -f frontend.yaml
- פריסת עובד
kubectl apply -f worker-primary.yaml
- פריסת שירות העבודה
kubectl apply -f worker-service.yaml
פרסנו את האפליקציה שלנו ב-GKE. מזל טוב!
בדיקה
ממתינים עד שהפקודות יהיו פעילות
kubectl get pods -w
אחרי שכל ה-pods יהיו בסטטוס 'Running', מקישים על Ctrl + C.
NAME READY STATUS RESTARTS AGE frontend-deployment-695d95fbf7-76sd8 1/1 Running 0 2m redis-cache-7475999bf5-nxj8x 1/1 Running 0 2m worker-deployment-5b9cf9956d-g975p 1/1 Running 0 2m
שימו לב שלא חשפנו את הקצה הקדמי דרך LoadBalancer. הסיבה לכך היא שבשלב מאוחר יותר ניגשים לאפליקציה דרך Istio. כדי לבדוק שכל הדברים פועלים, נשתמש ב-kubectl port-forward.. מריצים את הפקודה הבאה כדי להעביר את היציאה 8080 במחשב המקומי (או ב-Cloud Shell) ליציאה 8080 שבה פועלת הפריסה של frontend.
kubectl port-forward \
$(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}') \
8080:8080
אם מריצים את הקוד באופן מקומי: פותחים דפדפן אינטרנט ועוברים אל http://localhost:8080
אם אתם מריצים את הקוד ב-Cloud Shell: לוחצים על הלחצן 'תצוגה מקדימה באינטרנט' ובוחרים באפשרות 'תצוגה מקדימה ביציאה 8080'.

חזית האתר אמורה להופיע. אם מזינים מספר בתיבה 'תדירות', אמורים להופיע גיבוב (hash)

כל הכבוד, הכול מוכן!
מקישים על Ctrl+C כדי להפסיק את העברת היציאה.
11. ניקוי אפליקציה שנפרסה
אנחנו נחיל את Istio על האשכולות שלנו ולאחר מכן נעביר מחדש את האפליקציה, לכן קודם ננקה את האפליקציה הנוכחית.
מריצים את הפקודות הבאות כדי למחוק את כל הפריסות והשירותים שיצרתם
- מחיקת
redis-cache-service
kubectl delete -f redis-service.yaml
- מחיקת
redis
kubectl delete -f redis.yaml
- מחיקת
frontend
kubectl delete -f frontend.yaml
- מחיקת
worker
kubectl delete -f worker-primary.yaml
- מחיקת
worker-service
kubectl delete -f worker-service.yaml
12. התקנת Istio באשכול הראשי
הורדת Istio
הגרסאות של Istio מתארחות ב-GitHub. הפקודות הבאות יורידו את הגרסה 1.0.0 של istio ויפרקו אותה.
- עוברים לשורש הפרויקט
cd ${proj}
- הורדת הארכיון
curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
- חילוץ והסרה של הארכיון
tar xzf istio-1.0.0-linux.tar.gz && rm istio-1.0.0-linux.tar.gz
יצירת תבנית Istio
הפעלת הפקודה הבאה ב-Helm תיצור את התבנית להתקנת Istio באשכול.
helm template istio-1.0.0/install/kubernetes/helm/istio \ --name istio --namespace istio-system \ --set prometheus.enabled=true \ --set servicegraph.enabled=true > istio-primary.yaml
הפקודה הזו יוצרת קובץ בשם istio-primary.yaml בספרייה הנוכחית, שמכיל את כל ההגדרות והמפרטים הנדרשים לפריסה ולהפעלה של Istio.
שימו לב לשני הפרמטרים --set. התוספים האלה מוסיפים למערכת Istio תמיכה ב-Prometheus וב-ServiceGraph. נשתמש בשירות Prometheus בהמשך הסדנה.
פריסה של Istio
כדי לפרוס את Istio, קודם צריך ליצור מרחב שמות שנקרא istio-system שבו יוכלו לפעול הפריסות והשירותים של Istio.
kubectl create namespace istio-system
ולבסוף מחילים את הקובץ istio-primary.yaml שיצרנו באמצעות Helm
kubectl apply -f istio-primary.yaml
מרחב השמות שמוגדר כברירת מחדל לתוויות
Istio פועל על ידי הזרקת שירות proxy של צדדים נלווים לכל אחד מהפריסות שלכם. האפשרות הזו זמינה רק אם בוחרים בה, לכן צריך לתייג את מרחב השמות default בתווית istio-injection=enabled כדי ש-Istio תוכל להחדיר את ה-sidecar בשבילנו באופן אוטומטי.
kubectl label namespace default istio-injection=enabled
מזל טוב! יש לנו אשכול פעיל עם Istio, ועכשיו אנחנו מוכנים לפרוס את האפליקציה.
13. פריסת האפליקציה שלנו באמצעות ניהול התנועה ב-Istio
יצירת קובצי תצורה של ניהול תעבורת נתונים ב-Istio
Istio פועל באופן דומה ל-Kubernetes, כי הוא משתמש בקבצי yaml לתצורה. לכן, אנחנו צריכים ליצור קבוצת קבצים שתוכלו להנחות את Istio איך לחשוף את התנועה שלנו ולנתב אותה.
יוצרים ספרייה בשם istio-manifests ועוברים אליה
mkdir ${proj}/istio-manifests && cd ${proj}/istio-manifests
כתיבת הקובץ frontend-gateway.yaml
הקובץ הזה יחשוף את אשכול Kubernetes שלנו באופן דומה ל-GKE LoadBalancer, וינתב את כל התנועה הנכנסת לשירות הקצה שלנו.
יוצרים קובץ בשם frontend-gateway.yaml ומוסיפים את הטקסט הבא.
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: frontend-gateway
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: frontend-ingress-virtual-service
spec:
hosts:
- "*"
gateways:
- frontend-gateway
http:
- route:
- destination:
host: frontend-service
port:
number: 80
כתיבה של redis-virtualservice.yaml
יוצרים קובץ בשם redis-virtualservice.yaml ומזינים את הקטע הבא
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: redis-virtual-service
spec:
hosts:
- redis-cache-service
gateways:
- mesh
tcp:
- route:
- destination:
host: redis-cache-service.default.svc.cluster.local
כתיבה של worker-virtualservice.yaml
יוצרים קובץ בשם worker-virtualservice.yaml ומזינים את הקטע הבא
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: worker-virtual-service
spec:
hosts:
- worker-service
gateways:
- mesh
http:
- route:
- destination:
host: worker-service.default.svc.cluster.local
port:
number: 80
פריסה של מדיניות ניהול תעבורת הנתונים ב-Istio
פריסת כללי המדיניות של Istio מתבצעת באותו אופן שבו פורסים משאבים אחרים ב-Kubernetes, באמצעות kubectl apply
- החלת השער שלנו
kubectl apply -f frontend-gateway.yaml
- החלת VirtualService של Redis
kubectl apply -f redis-virtualservice.yaml
- החלת VirtualService של Worker
kubectl apply -f worker-virtualservice.yaml
פריסה של אפליקציה
- חוזרים לספרייה
kubernetes
cd ${proj}/kubernetes
- פריסה של Redis Cache
kubectl apply -f redis.yaml
- פריסה של שירות Redis
kubectl apply -f redis-service.yaml
- פריסה של הקצה הקדמי
kubectl apply -f frontend.yaml
- פריסת עובד
kubectl apply -f worker-primary.yaml
- פריסת שירות העבודה
kubectl apply -f worker-service.yaml
אימות
בשלב הזה פרסנו מחדש את האפליקציה שלנו באשכול עם Istio ומדיניות ניהול תעבורת נתונים.
נמתין עד שכל עומסי העבודה יהיו מחוברים
אחרי שהכול יפעל, צריך לקבל את IngressGateway שהגדרתם ב-frontend-ingressgateway.yaml
$ kubectl -n istio-system get svc istio-ingressgateway NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istio-ingressgateway LoadBalancer 10.36.3.112 35.199.158.10 80:31380/TCP,
אפשר לעבור לכתובת <EXTERNAL-IP> או להריץ אותה ב-curl, ואמור להופיע החזית.
$ curl 35.199.158.10
<!doctype html>
<html>
<head>
<title>String Hashr</title>
<!-- Bootstrap -->
...
14. התקנת Istio באשכול 'burst'
הקדשנו הרבה זמן להגדרה ולפריסה באשכול primary שלנו, אבל יש לנו עוד אשכול שלם לפריסה!
בקטע הזה נצטרך לאחזר משתני תצורה בשני האשכולות, לכן חשוב לשים לב לאשכול שאליו אנחנו מפנים בכל פקודה.
יצירת המניפסט מרחוק של Istio
בדומה לפריסה של Istio באשכול primary, נשתמש ב-Helm כדי ליצור תבנית לפריסה של Istio מרחוק באשכול burst. עם זאת, לפני שנוכל לעשות זאת, נצטרך לקבל מידע על האשכולות של primary
איסוף מידע על האשכולות הראשיים
שינוי לאשכולות primary
kubectx primary
הפקודות הבאות מאחזרות את כתובות ה-IP של פקעות שונות באשכול הראשי. Istio Remote משתמש בהם כדי לתקשר חזרה עם האשכולות הראשיים.
export PILOT_POD_IP=$(kubectl -n istio-system get pod -l istio=pilot -o jsonpath='{.items[0].status.podIP}')
export POLICY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=policy -o jsonpath='{.items[0].status.podIP}')
export STATSD_POD_IP=$(kubectl -n istio-system get pod -l istio=statsd-prom-bridge -o jsonpath='{.items[0].status.podIP}')
export TELEMETRY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=telemetry -o jsonpath='{.items[0].status.podIP}')
export ZIPKIN_POD_IP=$(kubectl -n istio-system get pod -l app=jaeger -o jsonpath='{range .items[*]}{.status.podIP}{end}')
יצירת תבנית מרחוק
עכשיו נשתמש ב-helm כדי ליצור קובץ בשם istio-remote-burst.yaml, שאותו נוכל לפרוס באשכול burst.
מעבר לשורש הפרויקט
cd $proj
helm template istio-1.0.0/install/kubernetes/helm/istio-remote --namespace istio-system \
--name istio-remote \
--set global.remotePilotAddress=${PILOT_POD_IP} \
--set global.remotePolicyAddress=${POLICY_POD_IP} \
--set global.remoteTelemetryAddress=${TELEMETRY_POD_IP} \
--set global.proxy.envoyStatsd.enabled=true \
--set global.proxy.envoyStatsd.host=${STATSD_POD_IP} \
--set global.remoteZipkinAddress=${ZIPKIN_POD_IP} > istio-remote-burst.yaml
התקנת Istio Remote באשכול ה-burst
כדי להתקין את Istio באשכולות burst, צריך לפעול לפי אותם שלבים כמו בהתקנה באשכולות primary, אבל להשתמש בקובץ istio-remote-burst.yaml במקום זאת.
שינוי kubecontext ל-burst
kubectx burst
יצירת מרחב השמות istio-system
kubectl create ns istio-system
החלת istio-burst.yaml
kubectl apply -f istio-remote-burst.yaml
מרחב השמות שמוגדר כברירת מחדל לתווית
שוב, אנחנו צריכים לתייג את מרחב השמות default כדי שאפשר יהיה להחדיר את שרת ה-proxy באופן אוטומטי.
kubectl label namespace default istio-injection=enabled
מזל טוב! בשלב הזה הגדרנו את Istio Remote באשכול burst. עם זאת, בשלב הזה האשכולות עדיין לא יכולים לתקשר. אנחנו צריכים ליצור קובץ kubeconfig לאשכולות burst, שאפשר לפרוס באשכולות primary כדי לקשר ביניהם.
יצירת קובץ kubeconfig לאשכולות 'burst'
שינוי לאשכול לצורך התפרצות תנועה
kubectx burst
הגדרת הסביבה
אנחנו צריכים לאסוף מידע על האשכולות כדי ליצור עבורם קובץ kubeconfig.
- אחזור השם של האשכול
CLUSTER_NAME=$(kubectl config view --minify=true -o "jsonpath={.clusters[].name}")
- אחזור השם של שרת האשכולות
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
- אחזור השם של הסוד של רשות האישורים של חשבון השירות
istio-multi
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
- אחזור הנתונים של רשות האישורים שמאוחסנים בסוד הקודם
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
- אחזור האסימון שנשמר בסוד הקודם
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)
יצירת קובץ kubeconfig
אחרי שמגדירים את כל משתני הסביבה האלה, צריך ליצור את קובץ ה-kubeconfig.
cat <<EOF > burst-kubeconfig
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ${CA_DATA}
server: ${SERVER}
name: ${CLUSTER_NAME}
contexts:
- context:
cluster: ${CLUSTER_NAME}
user: ${CLUSTER_NAME}
name: ${CLUSTER_NAME}
current-context: ${CLUSTER_NAME}
kind: Config
preferences: {}
users:
- name: ${CLUSTER_NAME}
user:
token: ${TOKEN}
EOF
הפקודה הזו תיצור קובץ חדש בשם burst-kubeconfig בספרייה הנוכחית, שבו האשכולות primary ו-burst יוכלו להשתמש כדי לאמת ולנהל את האשכולות.
חזרה לאשכול הראשי
kubectx primary
החלת kubeconfig ל'פרץ', על ידי יצירת סוד וסימון שלו
kubectl create secret generic burst-kubeconfig --from-file burst-kubeconfig -n istio-system
תיוג הסוד כדי ש-Istio ידע להשתמש בו לאימות בכמה אשכולות
kubectl label secret burst-kubeconfig istio/multiCluster=true -n istio-system
מזל טוב! שני האשכולות מאומתים ומנהלים תקשורת ביניהם באמצעות Istio Multicluster. פריסה של האפליקציה שלנו בכמה אשכולות
15. פריסת אפליקציה בכמה אשכולות
יצירת פריסות
עוברים לספרייה kubernetes
cd ${proj}/kubernetes
יצירת פריסה של כוח עבודה לאשכולות 'burst': worker-burst.yaml
יוצרים קובץ בשם worker-burst.yaml ומזינים בו את הטקסט הבא:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: worker-deployment
labels:
app: worker
spec:
replicas: 1
selector:
matchLabels:
app: worker
template:
metadata:
labels:
app: worker
cluster-type: burst-cluster
spec:
containers:
- name: worker
image: gcr.io/istio-burst-workshop/worker
imagePullPolicy: Always
ports:
- containerPort: 8081
readinessProbe:
initialDelaySeconds: 10
httpGet:
path: "/_healthz"
port: 8081
httpHeaders:
- name: "Cookie"
value: "istio_session-id=x-readiness-probe"
livenessProbe:
initialDelaySeconds: 10
httpGet:
path: "/"
port: 8081
httpHeaders:
- name: "Cookie"
value: "istio_session-id=x-liveness-probe"
env:
- name: PORT
value: "8081"
- name: REDIS_URL
value: "redis-cache-service:6379"
- name: PREFIX
value: "bursty-"
שימו לב שהקובץ הזה כמעט זהה לקובץ worker-primary.yaml שיצרנו מקודם. יש שני הבדלים עיקריים.
ההבדל העיקרי הראשון הוא שהוספנו את משתנה הסביבה PREFIX עם הערך bursty-.
env:
- name: PORT
value: "8081"
- name: REDIS_URL
value: "redis-cache-service:6379"
- name: PREFIX
value: "bursty-"
המשמעות היא שהעובד שלנו באשכול burst יוסיף את הקידומת bursty- לכל הגיבובים שהוא שולח. כך נוכל לדעת שהאפליקציה שלנו פועלת באשכולות שונים.
ההבדל העיקרי השני הוא ששינינו את התווית cluster-type בפריסה הזו מ-primary-cluster ל-burst-cluster.
labels:
app: worker
cluster-type: burst-cluster
נשתמש בתווית הזו בהמשך, כשנודאג לעדכון של VirtualService.
שינוי שירותי Istio
נכון לעכשיו, שירותי Istio שלנו לא מנצלים את שני הפריסות שלנו. 100% מהתנועה שלנו מנותבת לאשכול 'ראשי'. נשנה את זה.
עוברים לספרייה istio-manifests
cd ${proj}/istio-manifests
עריכת הקובץ worker-virtualservice.yaml כך שיכלול את DestinationRules
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: worker-virtual-service
spec:
hosts:
- worker-service
gateways:
- mesh
http:
- route:
- destination:
host: worker-service.default.svc.cluster.local
subset: primary
port:
number: 80
weight: 50
- destination:
host: worker-service.default.svc.cluster.local
subset: burst
port:
number: 80
weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: worker-destination-rule
spec:
host: worker-service
trafficPolicy:
loadBalancer:
simple: RANDOM
subsets:
- name: primary
labels:
cluster-type: primary-cluster
- name: burst
labels:
cluster-type: burst-cluster
אפשר לראות שהוספנו יעד שני ל-VirtualService. הוא עדיין מפנה לאותו מארח (worker-service.default.svc.cluster.local)), אבל 50% מהתנועה מנותבת לקבוצת המשנה primary ו-50% הנותרים מנותבים לקבוצת המשנה burst.
הגדרנו את קבוצת המשנה primary כפריסות עם התווית cluster-type: primary-cluster ואת קבוצת המשנה burst כפריסות עם התווית cluster-type: burst-cluster.
כך התנועה מתחלקת באופן שווה בין שני האשכולות.
פריסה באשכולות
פריסה של redis-service.yaml לאשכול לצורך התפרצות ביקוש (burst)
שינוי קובץ ה-kubeconfig ל-burst
kubectx burst
מעבר לשורש הפרויקט
cd ${proj}
לאחר מכן פורסים
פריסת redis-service.yaml למקבץ ה-burst
kubectl apply -f kubernetes/redis-service.yaml
פריסה של worker-burst.yaml לאשכול ה-burst
kubectl apply -f kubernetes/worker-burst.yaml
פריסה של worker-service.yaml לאשכול ה-burst
kubectl apply -f kubernetes/worker-service.yaml
החלת VirtualServices של Istio
שינוי kubeconfig ל-primary
kubectx primary
לאחר מכן, פריסה
kubectl apply -f istio-manifests/worker-virtualservice.yaml
איך מוודאים שהכל עובד
כדי לוודא שהיא פועלת, עוברים לנקודת ה-Ingress של Istio ומבחינים בכך של-50% מהגיבוב מצורף הקידומת 'burst-'.

פירוש הדבר הוא שאנחנו מצליחים לתקשר בין אשכולות. נסו לשנות את המשקלים של השירותים השונים ולהחיל את הקובץ worker-virtualservice.yaml. זו דרך מצוינת לאזן את התנועה בין אשכולות, אבל מה אם נוכל לעשות זאת באופן אוטומטי?
16. שימוש במדדים של Prometheus
מבוא ל-Prometheus
Prometheus הוא ערכת כלים בקוד פתוח למעקב אחרי מערכות ולשליחת התראות, שנוצרה במקור ב-SoundCloud. הוא שומר על מודל נתונים רב-ממדי עם נתוני סדרות זמנים שזוהו לפי שם המדד וזוגות מפתח/ערך.
לידיעתכם, זהו תרשים הארכיטקטורה של Prometheus:

כשפורסים את Istio עם Prometheus, הוא מדווח באופן אוטומטי על מדדים שונים לשרת Prometheus. אנחנו יכולים להשתמש במדדים האלה כדי לנהל את האשכולות שלנו בזמן אמת.
הצגת מדדי Prometheus
כדי להתחיל, אנחנו צריכים לחשוף את הפריסה של Prometheus.
עוברים לכרטיסייה Workloads ב-GKE, ומבצעים פירוט לעומס העבודה prometheus.

אחרי שמציגים את פרטי הפריסה, עוברים אל Actions (פעולות) -> Expose (חשיפת השירות).

בוחרים להעביר לשקע 9090 ומקלידים 'מאזן עומסים'.

ובוחרים באפשרות 'חשיפת הנתונים'.
הפעולה הזו תיצור שירות בכתובת IP שגלויה לכולם, שבאמצעותה נוכל לבחון את מדדי Prometheus.
ממתינים עד לנקודת הקצה שתהיה פעילה, ואז לוחצים על כתובת ה-IP לצד 'נקודות קצה חיצוניות' 
עכשיו אמור להופיע ממשק המשתמש של Prometheus.

ל-Prometheus יש מספיק מדדים כדי להפוך לסדנה משלו. בינתיים, נתחיל בסקירה של המדד istio_requests_total.
הרצת השאילתה הזו מחזירה קבוצה של נתונים. מדובר במדדים של כל הבקשות שעוברות דרך service mesh של Istio, וזה הרבה! נשנה את הביטוי כדי לסנן את התוצאות ולהגיע למה שבאמת מעניין אותנו:
בקשות שבהן שירות היעד הוא worker-service.default.svc.cluster.local והמקור הוא frontend-deployment מוגבלות ל-15 השניות האחרונות
השאילתה הזו נראית כך:
istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s]
וגם מאפשרת לנו לעבוד עם קבוצת נתונים נוחה יותר לניהול.

אבל עדיין קצת צפוף. אנחנו רוצים לדעת מה מספר הבקשות לשנייה, ולא את כל הבקשות.
כדי לעשות זאת, אפשר להשתמש בפונקציה המובנית rate
rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])

אנחנו מתקרבים, אבל אנחנו צריכים לצמצם את המדדים האלה עוד קצת לקבוצה לוגית.
כדי לעשות זאת, אפשר להשתמש במילות המפתח sum ו-by כדי לקבץ את התוצאות ולסכם אותן.
sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

יופי! אנחנו יכולים לקבל מ-Prometheus את המדדים המדויקים שאנחנו צריכים.
השאילתה הסופית שלנו ל-Prometheus
אחרי כל מה שלמדנו, השאילתה האחרונה שצריך לשלוח ל-Prometheus היא
sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)
עכשיו אפשר להשתמש ב-HTTP API שלהם כדי לקבל את המדד.
אנחנו יכולים לשלוח שאילתות ל-API שלהם באמצעות השאילתה שלנו על ידי שליחת בקשת HTTP GET באופן הבא. צריך להחליף את <prometheus-ip-here>
curl http://<prometheus-ip-here>/api/v1/query?query=sum\(rate\(istio_requests_total%7Breporter%3D%22destination%22%2C%0Adestination_service%3D%22worker-service.default.svc.cluster.local%22%2C%0Asource_workload%3D%22frontend-deployment%22%7D%5B15s%5D\)\)%20by%20\(source_workload%2C%0Asource_app%2C%20destination_service\)
דוגמה לתגובה:
{
"status": "success",
"data": {
"resultType": "vector",
"result": [
{
"metric": {
"destination_service": "worker-service.default.svc.cluster.local",
"source_app": "frontend",
"source_workload": "frontend-deployment"
},
"value": [
1544404907.503,
"18.892886390062788"
]
}
]
}
}
עכשיו אפשר לחלץ את ערך המדד מה-JSON
ניקוי
אנחנו צריכים למחוק את השירות שבו השתמשנו כדי לחשוף את Prometheus. במסוף Google Cloud, עוברים לחלק העליון של השירות שיצרנו ולוחצים על 'Delete' (מחיקה).

השלבים הבאים:
אחרי שמצאנו דרך לגלות איך התנועה עוברת באשכול ובאיזו מהירות, השלב הבא הוא לכתוב קובץ בינארי קטן שמבצע שאילתות ל-Prometheus מדי פעם. אם מספר הבקשות לשנייה ל-worker חורג מסף מסוים, המערכת מחילה משקלים שונים של יעדים על השירות הווירטואלי של העובד כדי לשלוח את כל התנועה לאשכול burst. כשמספר הבקשות לשנייה יורד מתחת לסף נמוך יותר, שולחים את כל התנועה חזרה אל primary.
17. יצירת התקפה מסוג Burst בין אשכולות
הגדרה
הגדרת כל התנועה של שירותי העבודה לאשכולות הראשיים
כל התנועה שמגיעה אל worker-service ומתבצעת ניתוב שלה לאשכולות primary תחשב כמצב 'ברירת המחדל' של האפליקציה שלנו
עורכים את $proj/istio-manifests/worker-virtualservice.yaml כך שייראה כך
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: worker-virtual-service
spec:
hosts:
- worker-service
gateways:
- mesh
http:
- route:
- destination:
host: worker-service.default.svc.cluster.local
subset: primary
port:
number: 80
weight: 100
- destination:
host: worker-service.default.svc.cluster.local
subset: burst
port:
number: 80
weight: 0
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: worker-destination-rule
spec:
host: worker-service
trafficPolicy:
loadBalancer:
simple: RANDOM
subsets:
- name: primary
labels:
cluster-type: primary-cluster
- name: burst
labels:
cluster-type: burst-cluster
מוודאים שאתם מחוברים לאשכולות primary
kubectx primary
החלת istio-manifests/worker-virtualservice.yaml
kubectl apply -f istio-manifests/worker-virtualservice.yaml
כתיבה של הדימון istiowatcher
נשתמש ב-Go כדי לכתוב את השירות הזה בגלל המהירות והניידות שלו. התהליך הכולל של האפליקציה יהיה הפעלה וביצוע שאילתה ב-Prometheus בכל שנייה.
יוצרים ספרייה חדשה בשם istiowatcher ב-src.
mkdir -p ${proj}/src/istiowatcher && cd ${proj}/src/istiowatcher
נתקשר ל-istioctl מתוך הקונטיינר כדי לבצע שינויים במישור הבקרה של Istio מתוך האשכול.
כתיבת istiowatcher.go
יוצרים קובץ בשם istiowatcher.go בתיקייה הזו ומוסיפים את הטקסט הבא
package main
import (
"github.com/tidwall/gjson"
"io/ioutil"
"log"
"net/http"
"os/exec"
"time"
)
func main() {
//These are in requests per second
var targetLow float64 = 10
var targetHigh float64 = 15
// This is for the ticker in milliseconds
ticker := time.NewTicker(1000 * time.Millisecond)
isBurst := false
// Our prometheus query
reqQuery := `/api/v1/query?query=sum(rate(istio_requests_total{reporter="destination",destination_service="worker-service.default.svc.cluster.local",source_workload="frontend-deployment"}[15s]))by(source_workload,source_app,destination_service)`
for t := range ticker.C {
log.Printf("Checking Prometheus at %v", t)
// Check prometheus
// Note that b/c we are querying over the past 5 minutes, we are getting a very SLOW ramp of our reqs/second
// If we wanted this to be a little "snappier" we can scale it down to say 30s
resp, err := http.Get("http://prometheus.istio-system.svc.cluster.local:9090" + reqQuery)
if err != nil {
log.Printf("Error: %v", err)
continue
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
val := gjson.Get(string(body), "data.result.0.value.1")
log.Printf("Value: %v", val)
currentReqPerSecond := val.Float()
log.Printf("Reqs per second %f", currentReqPerSecond)
if currentReqPerSecond > targetHigh && !isBurst {
applyIstio("burst.yaml")
log.Println("Entering burst mode")
isBurst = true
} else if currentReqPerSecond < targetLow && isBurst {
applyIstio("natural.yaml")
log.Println("Returning to natural state.")
isBurst = false
}
}
}
func applyIstio(filename string) {
cmd := exec.Command("istioctl", "replace", "-f", filename)
if err := cmd.Run(); err != nil {
log.Printf("Error hit applying istio manifests: %v", err)
}
}
כתיבה של קובץ Dockerfile
יוצרים קובץ חדש בשם Dockerfile ומזינים בו את הטקסט הבא.
FROM golang:1.11.2-stretch as base
FROM base as builder
WORKDIR /workdir
RUN curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
RUN tar xzf istio-1.0.0-linux.tar.gz
RUN cp istio-1.0.0/bin/istioctl ./istioctl
FROM base
WORKDIR /go/src/istiowatcher
COPY . .
COPY --from=builder /workdir/istioctl /usr/local/bin/istioctl
RUN go get -d -v ./...
RUN go install -v ./...
CMD ["istiowatcher"]
קובץ Dockerfile רב-שלבי שמוריד ומחלץ את הגרסה 1.0.0 של Istio בשלב הראשון. בשלב השני, המערכת מעתיקה את כל התכנים מהספרייה שלנו לקובץ האימג', ולאחר מכן מעתיקה את istioctl משלב ה-build אל /usr/local/bin (כדי שהאפליקציה שלנו תוכל להפעיל אותו), מקבלת את יחסי התלות, אוספת את הקוד ומגדירה את CMD כ-istiowatcher.
כתיבת burst.yaml
זהו הקובץ istiowatcher שיוחל כשמספר הבקשות לשנייה ל-worker מ-frontend חורג מ-15.
יוצרים קובץ חדש בשם burst.yaml ומזינים בו את הטקסט הבא.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: worker-virtual-service
spec:
hosts:
- worker-service
gateways:
- mesh
http:
- route:
- destination:
host: worker-service.default.svc.cluster.local
subset: primary
port:
number: 80
weight: 0
- destination:
host: worker-service.default.svc.cluster.local
subset: burst
port:
number: 80
weight: 100
כתיבת קובץ natural.yaml
נתייחס למצב הזה כמצב 'טבעי' שאליו נשוב כשמספר הבקשות לשנייה מ-frontend ל-worker יירד מתחת ל-10. במצב הזה, 100% מהתנועה מנותבת לאשכולות primary.
יוצרים קובץ חדש בשם natural.yaml ומוסיפים אליו את הטקסט הבא
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: worker-virtual-service
spec:
hosts:
- worker-service
gateways:
- mesh
http:
- route:
- destination:
host: worker-service.default.svc.cluster.local
subset: primary
port:
number: 80
weight: 100
- destination:
host: worker-service.default.svc.cluster.local
subset: burst
port:
number: 80
weight: 0
יצירה של istiowatcher והעברה שלו ל-Push
מריצים את הפקודה הבאה כדי לשלוח את הספרייה הנוכחית ל-Google Cloud Build (GCB), שיוצר את קובץ האימג' ומתייג אותו ב-GCR.
gcloud builds submit -t gcr.io/${GCLOUD_PROJECT}/istiowatcher
פריסה של istiowatcher
עוברים לספרייה kubernetes
cd ${proj}/kubernetes/
כתיבה של קובץ פריסה: istiowatcher.yaml
יוצרים קובץ בשם istiowatcher.yaml ומוסיפים את הקטע הבא (מחליפים את <your-project-id>).
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: istiowatcher-deployment
labels:
app: istiowatcher
spec:
replicas: 1
selector:
matchLabels:
app: istiowatcher
template:
metadata:
labels:
app: istiowatcher
spec:
serviceAccountName: istio-pilot-service-account
automountServiceAccountToken: true
containers:
- name: istiowatcher
image: gcr.io/<your-project-id>/istiowatcher
imagePullPolicy: Always
פריסה
מוודאים שאנחנו פועלים באשכול הראשי
kubectx primary
פריסה של istiowatcher.yaml במרחב השמות istio-system
kubectl apply -n istio-system -f istiowatcher.yaml
חשוב לשים לב להוראות serviceAccountName ו-automountServiceAccountToken ב-YAML. כך נקבל את פרטי הכניסה הנדרשים להרצת istioctl מתוך האשכול.
אנחנו צריכים לפרוס את זה גם במרחב השמות istio-system כדי לוודא שיש לנו את פרטי הכניסה של istio-pilot-service-account. (הוא לא קיים במרחב השמות default).
צפייה במעבר האוטומטי של התנועה
עכשיו מגיע הרגע הקסום! נעבור לקצה הקדמי ונגדיל את מספר הבקשות לשנייה ל-20
שימו לב שהפעולה נמשכת כמה שניות, אבל היא מאיצה וכל הגיבובים שלנו מתחילים בתחילית 'bursty-'.
הסיבה לכך היא שאנחנו לוקחים דגימות מ-Prometheus בטווח 15s, וכתוצאה מכך יש עיכוב קל בזמן התגובה. אם נרצה להגדיר טווח צר יותר, נוכל לשנות את השאילתה ל-Prometheus כך שתהיה 5s.
18. מה השלב הבא?
ניקוי
אם אתם משתמשים בחשבון זמני שסופק לצורך הסדנה הזו, אין צורך לנקות.
אפשר למחוק את אשכולות Kubernetes, את כלל חומת האש ואת התמונות ב-GCR
gcloud container clusters delete primary --zone=us-west1-a
gcloud container clusters delete burst --zone=us-west1-a
gcloud compute firewall-rules delete istio-multicluster-test-pods
gcloud container images delete gcr.io/$GCLOUD_PROJECT/istiowatcher
המשך הדרך
- השתתפות בכנסים של Istio Talks
- קבלת ההסמכה: פיתוח האפליקציה הבאה באמצעות Kubernetes ו-Istio
- הרצאת מפתח: Kubernetes, Istio, Knative: Open Cloud Stack החדש – Aparna Sinha, מנהלת קבוצת מוצרים ב-Kubernetes, Google
- מדריך: שימוש ב-Istio – Lee Calcote ו-Girish Ranganathan, SolarWinds
- Istio – The Packet's-Eye View – Matt Turner, Tetrate
- האם Istio היא חומת האש מהדור הבא ביותר שנוצרה אי פעם? - ג'ון מורלו (John Morello), Twistlock
- קריאת מסמכי התיעוד של Istio
- הצטרפות לקבוצות העבודה של Istio
- עקבו אחרי @IstioMesh ב-Twitter




