במדריך הזה תלמדו איך לפרוס אשכול של שרתי Memcached מבוזרים ב-Google Kubernetes Engine (GKE) באמצעות Kubernetes, Helm ו-Mcrouter. Memcached היא מערכת פופולרית של קוד פתוח למטרות שונות של שמירת נתונים במטמון. הוא משמש בדרך כלל כמאגר זמני לנתונים שמשתמשים בהם בתדירות גבוהה, כדי להאיץ את פעולת אפליקציות האינטרנט ולהפחית את העומס על מסדי הנתונים.
המאפיינים של Memcached
ל-Memcached יש שתי מטרות עיצוב עיקריות:
- פשטות: פונקציות Memcached פועלות כמו טבלת גיבוב גדולה, ומציעות API פשוט לאחסון ולאחזור של אובייקטים בכל צורה לפי מפתח.
- מהירות: נתוני המטמון ב-Memcached נשמרים רק בזיכרון גישה אקראית (RAM), ולכן הגישה לנתונים מהירה מאוד.
Memcached היא מערכת מבוזרת שמאפשרת להגדיל את הקיבולת של טבלת הגיבוב שלה באופן אופקי במאגר של שרתים. כל שרת Memcached פועל בבידוד מוחלט מהשרתים האחרים במאגר. לכן, הניתוב ואיזון העומסים בין השרתים צריכים להתבצע ברמת הלקוח. לקוחות Memcached משתמשים בסכימת גיבוב עקבית כדי לבחור את שרתי היעד המתאימים. התוכנית הזו עוזרת להבטיח את התנאים הבאים:
- אותו שרת תמיד נבחר לאותו מפתח.
- השימוש בזיכרון מאוזן באופן שווה בין השרתים.
- מספר המפתחות שמועברים הוא מינימלי כשמאגר השרתים מצטמצם או מתרחב.
התרשים הבא מציג ברמה גבוהה את האינטראקציה בין לקוח Memcached לבין מאגר מבוזר של שרתי Memcached.
מטרות
- מידע על כמה מאפיינים של הארכיטקטורה המבוזרת של Memcached.
- פריסת שירות Memcached ב-GKE באמצעות Kubernetes ו-Helm.
- כדי לשפר את הביצועים של המערכת, אפשר לפרוס את Mcrouter, שרת proxy של Memcached בקוד פתוח.
עלויות
במסמך הזה משתמשים ברכיבים הבאים של Google Cloud, והשימוש בהם כרוך בתשלום:
- Compute Engine
כדי להעריך את ההוצאות בהתאם לתחזית השימוש שלכם, אתם יכולים להיעזר במחשבון העלויות.
לפני שמתחילים
- נכנסים לחשבון Google Cloud . אם אתם משתמשים חדשים ב- Google Cloud, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
Roles required to select or create a project
- Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
-
Create a project: To create a project, you need the Project Creator role
(
roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.createpermission. Learn how to grant roles.
-
Verify that billing is enabled for your Google Cloud project.
Enable the Compute Engine and GKE APIs.
Roles required to enable APIs
To enable APIs, you need the Service Usage Admin IAM role (
roles/serviceusage.serviceUsageAdmin), which contains theserviceusage.services.enablepermission. Learn how to grant roles.-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
Roles required to select or create a project
- Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
-
Create a project: To create a project, you need the Project Creator role
(
roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.createpermission. Learn how to grant roles.
-
Verify that billing is enabled for your Google Cloud project.
Enable the Compute Engine and GKE APIs.
Roles required to enable APIs
To enable APIs, you need the Service Usage Admin IAM role (
roles/serviceusage.serviceUsageAdmin), which contains theserviceusage.services.enablepermission. Learn how to grant roles.- מפעילים מופע של Cloud Shell.
פתיחת Cloud Shell
פריסת שירות Memcached
דרך פשוטה לפרוס שירות Memcached ב-GKE היא באמצעות תרשים Helm. כדי להמשיך בהטמעה, פועלים לפי השלבים הבאים ב-Cloud Shell:
יוצרים אשכול GKE חדש עם שלושה צמתים:
gcloud container clusters create demo-cluster --num-nodes 3 --location us-central1-fמורידים את הארכיון הבינארי
helm:HELM_VERSION=3.7.1 cd ~ wget https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gzמחלצים את קובץ הארכיון למערכת המקומית:
mkdir helm-v${HELM_VERSION} tar zxfv helm-v${HELM_VERSION}-linux-amd64.tar.gz -C helm-v${HELM_VERSION}מוסיפים את ספריית הקובץ הבינארי
helmלמשתנה הסביבהPATH:export PATH="$(echo ~)/helm-v${HELM_VERSION}/linux-amd64:$PATH"הפקודה הזו מאפשרת לגלות את הקובץ הבינארי
helmמכל ספרייה במהלך סשן Cloud Shell הנוכחי. כדי שההגדרה הזו תישמר בכמה סשנים, מוסיפים את הפקודה לקובץ~/.bashrcשל המשתמש ב-Cloud Shell.מטמיעים גרסה חדשה של תרשים Helm של Memcached עם ארכיטקטורה של זמינות גבוהה:
helm repo add bitnami https://charts.bitnami.com/bitnami helm install mycache bitnami/memcached --set architecture="high-availability" --set autoscaling.enabled="true"תרשים ה-Helm של Memcached משתמש בבקר StatefulSet. אחד היתרונות בשימוש בבקר StatefulSet הוא ששמות הפודים מסודרים וניתנים לחיזוי. בדוגמה הזו, השמות הם
mycache-memcached-{0..2}. הסדר הזה מקל על לקוחות Memcached להפנות לשרתים.כדי לראות את הפודים הפועלים, מריצים את הפקודה הבאה:
kubectl get podsGoogle Cloud הפלט במסוף נראה כך:
NAME READY STATUS RESTARTS AGE mycache-memcached-0 1/1 Running 0 45s mycache-memcached-1 1/1 Running 0 35s mycache-memcached-2 1/1 Running 0 25s
גילוי נקודות קצה של שירות Memcached
תרשים ה-Helm של Memcached משתמש בשירות ללא כתובת IP. שירות ללא ראש חושף את כתובות ה-IP של כל הפודים שלו, כדי שאפשר יהיה לגלות אותם בנפרד.
מוודאים שהשירות שנפרס הוא headless:
kubectl get service mycache-memcached -o jsonpath="{.spec.clusterIP}"הפלט
Noneמאשר שלשירות איןclusterIPולכן הוא headless.השירות יוצר רשומת DNS לשם מארח בפורמט:
[SERVICE_NAME].[NAMESPACE].svc.cluster.localבמדריך הזה, שם השירות הוא
mycache-memcached. מכיוון שלא הוגדר מרחב שמות באופן מפורש, נעשה שימוש במרחב השמות שמוגדר כברירת מחדל, ולכן שם המארח כולו הואmycache-memcached.default.svc.cluster.local. שם המארח הזה מומר לקבוצה של כתובות IP ודומיינים עבור כל שלושת הפודים שנחשפים על ידי השירות. אם בעתיד יתווספו מאגרי פודים למאגר או יוסרו ממנו פודים ישנים,kube-dnsיעדכן אוטומטית את רשומת ה-DNS.באחריות הלקוח לגלות את נקודות הקצה של שירות Memcached, כפי שמתואר בשלבים הבאים.
מאחזרים את כתובות ה-IP של נקודות הקצה:
kubectl get endpoints mycache-memcachedהפלט אמור להיראות כך:
NAME ENDPOINTS AGE mycache-memcached 10.36.0.32:11211,10.36.0.33:11211,10.36.1.25:11211 3m
שימו לב שלכל פוד של Memcached יש כתובת IP נפרדת, בהתאמה:
10.36.0.32,10.36.0.33ו-10.36.1.25. יכול להיות שכתובות ה-IP האלה יהיו שונות במופעים של השרת שלכם. כל פוד מאזין ליציאה11211, שהיא יציאת ברירת המחדל של Memcached.כחלופה לשלב 2, אפשר לבצע בדיקת DNS באמצעות שפת תכנות כמו Python:
מפעילים מסוף אינטראקטיבי של Python בתוך האשכול:
kubectl run -it --rm python --image=python:3.10-alpine --restart=Never pythonבמסוף Python, מריצים את הפקודות הבאות:
import socket print(socket.gethostbyname_ex('mycache-memcached.default.svc.cluster.local')) exit()הפלט אמור להיראות כך:
('mycache-memcached.default.svc.cluster.local', ['mycache-memcached.default.svc.cluster.local'], ['10.36.0.32', '10.36.0.33', '10.36.1.25'])
כדי לבדוק את הפריסה, פותחים סשן
telnetעם אחד משרתי Memcached שפועלים ביציאה11211:kubectl run -it --rm busybox --image=busybox:1.33 --restart=Never telnet mycache-memcached-0.mycache-memcached.default.svc.cluster.local 11211בשורת הפקודה
telnet, מריצים את הפקודות הבאות באמצעות פרוטוקול ASCII של Memcached:set mykey 0 0 5 hello get mykey quit
הפלט שמתקבל מוצג כאן בטקסט מודגש:
set mykey 0 0 5 hello STORED get mykey VALUE mykey 0 5 hello END quit
הטמעה של לוגיקה לגילוי שירותים
עכשיו אפשר להטמיע את הלוגיקה הבסיסית של גילוי שירותים שמוצגת בתרשים הבא.
באופן כללי, הלוגיקה של גילוי שירותים מורכבת מהשלבים הבאים:
- האפליקציה שולחת שאילתות אל
kube-dnsלגבי רשומת ה-DNS שלmycache-memcached.default.svc.cluster.local. - האפליקציה מאחזרת את כתובות ה-IP שמשויכות לרשומה הזו.
- האפליקציה יוצרת מופע של לקוח Memcached חדש ומספקת לו את כתובות ה-IP שאוחזרו.
- מאזן העומסים המשולב של לקוח Memcached מתחבר לשרתי Memcached בכתובות ה-IP שצוינו.
עכשיו מטמיעים את הלוגיקה של גילוי השירות באמצעות Python:
פורסים פוד חדש עם Python באשכול ומתחילים סשן של מעטפת בתוך הפוד:
kubectl run -it --rm python --image=python:3.10-alpine --restart=Never shמתקינים את הספרייה
pymemcache:pip install pymemcacheמריצים את הפקודה
pythonכדי להפעיל קונסולת Python אינטראקטיבית.במסוף Python, מריצים את הפקודות הבאות:
import socket from pymemcache.client.hash import HashClient _, _, ips = socket.gethostbyname_ex('mycache-memcached.default.svc.cluster.local') servers = [(ip, 11211) for ip in ips] client = HashClient(servers, use_pooling=True) client.set('mykey', 'hello') client.get('mykey')הפלט אמור להיראות כך:
b'hello'
הקידומת
bמציינת מחרוזת ליטרלית של בייטים, שהוא הפורמט שבו Memcached מאחסן נתונים.יוצאים ממסוף Python:
exit()כדי לצאת מסשן המעטפת של ה-Pod, מקישים על
Control+D.
הפעלת איגום חיבורים
ככל שהצורך שלכם בזיכרון מטמון גדל, והמאגר מתרחב לעשרות, מאות או אלפי שרתים של Memcached, יכול להיות שתיתקלו במגבלות מסוימות. בפרט, המספר הגדול של חיבורים פתוחים מלקוחות Memcached עלול להעמיס עומס כבד על השרתים, כפי שמוצג בתרשים הבא.
כדי לצמצם את מספר החיבורים הפתוחים, צריך להוסיף שרת proxy כדי להפעיל את התכונה connection pooling, כמו בתרשים הבא.
Mcrouter (מבוטא "מיק ראוטר"), פרוקסי Memcached חזק עם קוד פתוח, מאפשר איגום חיבורים. השילוב של Mcrouter הוא חלק, כי הוא משתמש בפרוטוקול Memcached ASCII הרגיל. ללקוח Memcached, Mcrouter מתנהג כמו שרת Memcached רגיל. לשרת Memcached, Mcrouter מתנהג כמו לקוח Memcached רגיל.
כדי לפרוס את Mcrouter, מריצים את הפקודות הבאות ב-Cloud Shell.
מוחקים את הגרסה של תרשים Helm שהותקנה קודם:
mycachehelm delete mycacheפורסים פודים חדשים של Memcached ופודים של Mcrouter על ידי התקנה של תרשים Helm חדש של Mcrouter:
helm repo add stable https://charts.helm.sh/stable helm install mycache stable/mcrouter --set memcached.replicaCount=3תאי ה-proxy מוכנים עכשיו לקבל בקשות מאפליקציות לקוח.
כדי לבדוק את ההגדרה הזו, מתחברים לאחד ממאגרי ה-proxy. משתמשים בפקודה
telnetביציאה5000, שהיא יציאת ברירת המחדל של Mcrouter.MCROUTER_POD_IP=$(kubectl get pods -l app=mycache-mcrouter -o jsonpath="{.items[0].status.podIP}") kubectl run -it --rm busybox --image=busybox:1.33 --restart=Never telnet $MCROUTER_POD_IP 5000בשורת הפקודה של
telnet, מריצים את הפקודות הבאות:set anotherkey 0 0 15 Mcrouter is fun get anotherkey quit
הפקודות מגדירות את הערך של המפתח ומציגות אותו.
הפריסה של ה-proxy שמאפשרת איגום חיבורים הושלמה.
הפחתת זמן האחזור
כדי להגביר את החוסן, נהוג להשתמש באשכול עם כמה צמתים. במדריך הזה נעשה שימוש באשכול עם שלושה צמתים. עם זאת, שימוש בכמה צמתים גם מגדיל את הסיכון לזמן אחזור ארוך יותר, שנגרם מתנועת רשת כבדה יותר בין הצמתים.
מיקום משותף של פודים של שרת proxy
כדי להפחית את הסיכון הזה, אפשר לחבר את תרמילי האפליקציות של הלקוח רק לתרמיל proxy של Memcached שנמצא באותו צומת. התרשים הבא ממחיש את ההגדרה הזו.
כדי לבצע את ההגדרה הזו:
- מוודאים שכל צומת מכיל פוד פרוקסי אחד שפועל. גישה נפוצה היא פריסת תרמילי ה-proxy באמצעות בקר DaemonSet. כשמוסיפים צמתים לאשכול, תרמילי proxy חדשים מתווספים אליהם באופן אוטומטי. כשצמתים מוסרים מהאשכול, הפודים האלה עוברים איסוף פסולת. במדריך הזה, תרשים ה-Helm של Mcrouter שפרסתם קודם משתמש בבקר DaemonSet כברירת מחדל. לכן, השלב הזה כבר הושלם.
- מגדירים ערך
hostPortבפרמטרים של Kubernetes במאגר של ה-proxy כדי שהצומת יאזין ליציאה הזו ויפנה את התנועה אל ה-proxy. במדריך הזה, תרשים ה-Helm של Mcrouter משתמש בפרמטר הזה כברירת מחדל עבור יציאה5000. לכן השלב הזה כבר הושלם. כדי לחשוף את שם הצומת כמשתנה סביבה בתוך תרמילי האפליקציה, משתמשים בערך
spec.envובוחרים באפשרותspec.nodeNamefieldRef. מידע נוסף על השיטה הזו מופיע במאמרי העזרה בנושא Kubernetes.פריסת קבוצות Pod של אפליקציות לדוגמה. הפקודה הבאה מפעילה Kubernetes Deployment. פריסה היא אובייקט Kubernetes API שמאפשר להפעיל כמה רפליקות של Pods שמפוזרות בין הצמתים באשכול:
cat <<EOF | kubectl create -f - apiVersion: apps/v1 kind: Deployment metadata: name: sample-application spec: selector: matchLabels: app: sample-application replicas: 9 template: metadata: labels: app: sample-application spec: containers: - name: busybox image: busybox:1.33 command: [ "sh", "-c"] args: - while true; do sleep 10; done; env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName EOF
כדי לוודא ששם הצומת נחשף, בודקים באחד מפודי האפליקציה לדוגמה:
POD=$(kubectl get pods -l app=sample-application -o jsonpath="{.items[0].metadata.name}") kubectl exec -it $POD -- sh -c 'echo $NODE_NAME'הפלט של הפקודה הזו הוא שם הצומת בפורמט הבא:
gke-demo-cluster-default-pool-XXXXXXXX-XXXX
חיבור ה-Pods
הפודים של האפליקציה לדוגמה מוכנים עכשיו להתחבר לפוד Mcrouter שפועל בצמתים ההדדיים המתאימים ביציאה 5000, שהיא יציאת ברירת המחדל של Mcrouter.
מתחילים חיבור לאחד מה-pods על ידי פתיחת סשן
telnet:POD=$(kubectl get pods -l app=sample-application -o jsonpath="{.items[0].metadata.name}") kubectl exec -it $POD -- sh -c 'telnet $NODE_NAME 5000'בשורת הפקודה של
telnet, מריצים את הפקודות הבאות:get anotherkey quitהפלט שמתקבל:
Mcrouter is fun
לסיום, כדוגמה, קוד ה-Python הבא הוא תוכנית לדוגמה שמבצעת את החיבור הזה על ידי אחזור המשתנה NODE_NAME מהסביבה ושימוש בספריית pymemcache:
import os
from pymemcache.client.base import Client
NODE_NAME = os.environ['NODE_NAME']
client = Client((NODE_NAME, 5000))
client.set('some_key', 'some_value')
result = client.get('some_key')
הסרת המשאבים
כדי להימנע מחיובים בחשבון Google Cloud בגלל השימוש במשאבים שנעשה במסגרת המדריך הזה, אפשר למחוק את הפרויקט שמכיל את המשאבים, או להשאיר את הפרויקט ולמחוק את המשאבים בנפרד.
מריצים את הפקודה הבאה כדי למחוק את אשכול GKE:
gcloud container clusters delete demo-cluster --location us-central1-fאפשר גם למחוק את קובץ ה-Helm הבינארי:
cd ~ rm -rf helm-v3.7.1 rm helm-v3.7.1-linux-amd64.tar.gz
המאמרים הבאים
- כדאי לעיין בתכונות הרבות האחרות ש-Mcrouter מציע מעבר לאיגום חיבורים פשוט, כמו רפליקות של מעבר לגיבוי בעת כשל, זרמי מחיקה מהימנים, חימום מטמון קר ושידור מרובה אשכולות.
- כדי לקבל פרטים נוספים על הגדרות Kubernetes, אפשר לעיין בקובצי המקור של תרשים Memcached ושל תרשים Mcrouter.
- כדאי לקרוא על טכניקות יעילות לשימוש ב-Memcached ב-App Engine. חלק מההגדרות האלה חלות על פלטפורמות אחרות, כמו GKE.