רשתות עם מחסנית כפולה ב-Google Distributed Cloud מאפשרות לאשכולות לקבל תנועה ממכשירים חיצוניים שמשתמשים ב-IPv4 או ב-IPv6. Pod וצמתים מקבלים גם כתובות IPv4 וגם כתובות IPv6, ולשירות Kubernetes יכולה להיות כתובת IPv4, כתובת IPv6 או שתיהן.
באשכולות עם מחסנית כפולה תמיד משתמשים במצב שטוח ל-IPv6. ב-IPv4, אשכולות עם שני סוגי כתובות IP משתמשים במצב אי כברירת מחדל, אבל אפשר להגדיר מצב שטוח במקום זאת. כדי ליצור אשכול עם שני סוגי כתובות IP, צריך להפעיל את שני סוגי כתובות ה-IP ברשת הבסיסית. אי אפשר להפעיל אשכול עם מחסנית כפולה ברשת עם מחסנית אחת של IPv4 או IPv6.
לפני שמתחילים
אם צומתי האשכול מריצים את RedHat Enterprise Linux ומופעל בהם SELinux, בכל צומת:
ב
/etc/firewalld/firewalld.conf, מגדירים אתIPv6_rpfilter=no.מריצים את
systemctl restart firewalld.
אם צומתי האשכול משתמשים בהגדרה אוטומטית של כתובת ללא שמירת מצב (SLAAC) להגדרת רשת IPv6, עיינו במאמר הגדרת IPv6 של צומת לרשתות עם מחסנית כפולה כדי להגדיר את הצמתים לקבלת הודעות על נתבים (RAs).
סקירה כללית על יצירת אשכול עם מחסנית כפולה
אפשר להפעיל רשתות עם שני סוגי כתובות IP כשיוצרים אשכול חדש, אבל אי אפשר להפעיל אותן באשכול קיים.
פועלים לפי ההוראות באחד ממסמכי יצירת האשכולות.
בקובץ התצורה, צריך לכלול מניפסטים של:
- משאב של מרחב שמות
- משאב של אשכול
- משאב אחד או יותר של NodePool
- משאב אחד או יותר מסוג
ClusterCIDRConfig
ממלאים את המניפסט של מרחב השמות ואת המניפסטים של NodePool כמו באשכול עם מחסנית אחת.
במניפסט של האשכול, בקטע clusterNetwork.services.cidrBlocks, מציינים גם טווח CIDR של IPv4 וגם טווח CIDR של IPv6. זהו הקריטריון להפעלת אשכול עם מחסנית כפולה. כלומר, אם תספקו טווחי CIDR של שירות גם ל-IPv4 וגם ל-IPv6, לאשכול תהיה רשת עם מחסנית כפולה.
במניפסט של האשכול, בקטע clusterNetwork.pods.cidrBlocks, מציינים טווח CIDR של IPv4, אבל לא מציינים טווח CIDR של IPv6. טווחי ה-CIDR של IPv6 לפודים מצוינים במניפסטים של ClusterCIDRConfig.
אם אתם משתמשים באיזון עומסים מקובץ, צריך לספק גם כתובות IPv4 וגם כתובות IPv6 בקטע loadBalancer.addressPools במניפסט של האשכול.
המשאבים של ClusterCIDRConfig מיועדים לציון טווחי CIDR של IPv4 ו-IPv6 לפודים. אפשר להשתמש במשאב ClusterCIDRConfig אחד כדי לציין טווחי CIDR שחלים על כל האשכול. כלומר, כתובות ה-IPv4 של Pod לכל הצמתים נלקחות מטווח CIDR יחיד, וכתובות ה-IPv6 של Pod לכל הצמתים נלקחות מטווח CIDR יחיד. אפשר גם להשתמש בכמה משאבי ClusterCIDRConfig כדי לציין טווחי CIDR שחלים על מאגר צמתים מסוים או על צומת מסוים.
נגישות לכתובות IP של Pod
אשכול עם מחסנית כפולה משתמש במצב שטוח לרשת IPv6. הדוגמה שמופיעה במסמך הזה היא לאשכול שמשתמש ברשת סטטית במצב שטוח ל-IPv6. כלומר, האשכול לא מוגדר לשימוש ב-Border Gateway Protocol (BGP).
באשכול שמשתמש ברשת סטטית במצב שטוח, צריך לציין כתובות IP של צומת ושל Pod שכולן חלק מאותו רשת משנה. ההגדרה הזו מאפשרת ללקוחות מחוץ לאשכול, אבל באותו דומיין של שכבה 2 כמו צומתי האשכול, לשלוח חבילות ישירות לכתובות ה-IP של ה-Pod.
לדוגמה, נניח שצומתי האשכול ומכונות אחרות נמצאים באותו דומיין של שכבה 2. זו דרך אחת שבה אפשר לציין טווחי כתובות:
| מטרה | טווח | מספר הכתובות |
|---|---|---|
| כל הדומיין של שכבה 2 | fd12::/108 | 220 |
| קבוצת Pod | fd12::1:0/112 | 216 |
| צמתים | fd12::2:0/112 | 216 |
| מכונות אחרות | fd12::3:0/112 | 216 |
| אח"מים | fd12::4:0/112 | 216 |
בדוגמה שלמעלה, אלה הנקודות העיקריות שצריך להבין:
כל הכתובות של הצמתים, ה-Pods והמכונות נמצאות בטווח הגדול:
fd12::/108.כתובות ה-IP של הפודים נמצאות בקבוצת משנה של הטווח הגדול.
כתובות ה-IP של הצמתים נמצאות בקבוצת משנה אחרת של הטווח הגדול.
כתובות ה-IP של מכונות אחרות נמצאות בקבוצת משנה אחרת של הטווח הגדול.
כל טווחי קבוצות המשנה נבדלים זה מזה.
בדוגמה הקודמת, לכל מכונה בדומיין של שכבה 2, כולל צומתי האשכול, צריך להיות כלל העברה לטווח הגדול. לדוגמה:
inet fd12::/108 scope global eth0
הגדרת IPv6 של צומת לרשתות עם סטאק כפול
כשמפעילים רשתות בסטאק כפול, Google Distributed Cloud מגדיר באופן אוטומטי כל צומת כדי לאפשר העברת חבילות IPv6 על ידי הגדרת הפרמטר net.ipv6.conf.all.forwarding sysctl ל-1. הגדרת הליבה הזו נדרשת לשכבת הרשת של האשכול כדי לנתב תנועת IPv6 של Pods ו-Services בין צמתים.
שיקולים לגבי פרסום נתבים
להפעלת net.ipv6.conf.all.forwarding יש תופעת לוואי רגילה של ליבת Linux:
ממשקי הרשת מפסיקים לקבל הודעות נתב (RA) של IPv6 כברירת מחדל.
ליבת Linux מניחה שמערכת שמופעלת בה העברת חבילות פועלת כנתב, ונתבים לא מעבדים הודעות RA מנתבים אחרים כברירת מחדל.
השינוי הזה יכול לשבש צמתים שמסתמכים על הגדרה אוטומטית של כתובות בלי שמירת מצב (SLAAC) כדי לקבל כתובות IPv6, קידומות או נתיבי ברירת מחדל מהרשת, מה שעלול להוביל לאובדן הקישוריות של IPv6 ברמת הצומת.
ההגדרה הנדרשת לצמתים שתלויים ב-SLAAC
אם הצמתים משתמשים ב-SLAAC להגדרת רשת IPv6, צריך להגדיר ידנית את הליבה (kernel) כך שתקבל RAs כשהעברת חבילות IPv6 מופעלת.
כדי לאשר RAs בממשק כשהעברת IPv6 מופעלת, מגדירים באופן קבוע את הפרמטר net.ipv6.conf.INTERFACE_NAME.accept_ra sysctl ל-2 בכל צומת שמושפע:
מוסיפים את ההגדרה sysctl לקובץ תצורה ב-
/etc/sysctl.d/, למשל/etc/sysctl.d/60-ipv6-ra.conf:net.ipv6.conf.INTERFACE_NAME.accept_ra=2
מחליפים את
INTERFACE_NAMEבשם של ממשק הרשת, למשלeth0.מחילים את הגדרת sysctl:
sudo sysctl --system
הגדרת net.ipv6.conf.INTERFACE_NAME.accept_ra לערך 2 מבטיחה שהצומת יוכל לנתב חבילות IPv6 לאשכול, תוך המשך קבלת הגדרות הרשת שלו באמצעות RAs.
דוגמה: יצירת אשכול עם מחסנית כפולה
כשיוצרים אשכול עם מחסנית כפולה, יש כמה אפשרויות. לדוגמה, יכולים להיות טווחי CIDR ברמת האשכול או טווחי CIDR שחלים על מאגרי צמתים מסוימים. אפשר לשלב רשת שטוחה של IPv6 עם רשת במצב אי של IPv4. או שגם רשתות IPv4 וגם רשתות IPv6 יכולות להיות שטוחות. אתם יכולים להשתמש באיזון עומסים בחבילה או באיזון עומסים ידני.
בקטע הזה מופיעה דוגמה אחת ליצירת אשכול עם מחסנית כפולה. לאשכול בדוגמה הזו יש את המאפיינים הבאים:
- רשת IPv4 במצב אי (island)
- רשת IPv6 במצב שטוח
- טווח CIDR של IPv4 ל-Pods בכל האשכול
- טווח CIDR של IPv6 ל-Pods בכל האשכול
- טווח CIDR של IPv4 לכל האשכול בשביל שירותים
- טווח CIDR של IPv6 ברמת האשכול לשירותים
- מאגר כתובות IPv4 לשימוש בשירותים מסוג
LoadBalancer - מאגר כתובות IPv6 לשימוש בשירותים מסוג
LoadBalancer - איזון עומסים בחבילה
במאמר וריאציות של שימוש ב-ClusterCIDRConfig תוכלו לקרוא דוגמאות נוספות להגדרות אישיות.
מילוי קובץ תצורה
פועלים לפי ההוראות באחד ממסמכי יצירת האשכולות.
בקובץ התצורה, במניפסט Cluster:
בשביל
clusterNetwork.pods.cidrBlocks, צריך לציין טווח CIDR יחיד של IPv4.בשביל
clusterNetwork.services.cidrBlocks, צריך לציין שני טווחי CIDR: אחד ל-IPv4 ואחד ל-IPv6.בשביל
loadBalancer.addressPools, צריך לספק שני טווחי כתובות: אחד ל-IPv4 ואחד ל-IPv6. כשיוצרים שירות מסוגLoadBalancer, כתובות ה-IP החיצוניות של השירות נבחרות מהטווחים האלה.
דוגמה שמציגה את החלקים הרלוונטיים במניפסט של אשכול:
apiVersion: baremetal.cluster.gke.io/v1
kind: Cluster
metadata:
name: "dual-stack"
namespace: "cluster-dual-stack"
spec:
clusterNetwork:
pods:
cidrBlocks:
- "192.168.0.0/16"
services
cidrBlocks:
- "172.16.0.0/20"
- "fd12::5:0/116"
...
loadBalancer:
mode: "bundled"
...
addressPools:
- name: "pool-1"
addresses:
- "10.2.0.212-10.2.0.221"
- "fd12::4:101-fd12::4:110"
באותו קובץ תצורה, כוללים מניפסט של ClusterCIDRConfig.
מגדירים את
ipv4.cidrלאותו טווח CIDR שציינתם במניפסט שלCluster. זו דרישה אם IPv4 נמצא במצב אי.מגדירים את
namespaceלאותו ערך שציינתם במניפסט שלCluster.מגדירים את
ipv6.cidrלטווח CIDR של IPv6 ל-Pods.לכל טווח CIDR, צריך לציין ערך ל-
perNodeMaskSizeכדי לקבוע כמה כתובות Pod יוקצו לכל צומת. מספר כתובות ה-IPv4 שמוקצות לכל צומת צריך להיות זהה למספר כתובות ה-IPv6 שמוקצות לכל צומת. צריך להגדיר את הערכים שלperNodeMaskSizeבהתאם. לדוגמה, אם רוצים 28 כתובות לכל צומת, מגדירים את הערכים שלperNodeMaskSizeבאופן הבא:ipv4.perNodeMaskSize: 24# (32 - 8 = 24)ipv6.perNodeMaskSize: 120# (128 - 8 = 120)
דוגמה למניפסט של ClusterCIDRConfig:
apiVersion: baremetal.cluster.gke.io/v1alpha1
kind: ClusterCIDRConfig
metadata:
name: "cluster-wide-ranges"
namespace: "cluster-dual-stack" # Must be the same as the Cluster namespace.
spec:
ipv4:
cidr: "192.168.0.0/16" # For island mode, must be the same as the Cluster CIDR.
perNodeMaskSize: 24
ipv6:
cidr: "fd12::1:0/112"
perNodeMaskSize: 120
בדוגמה הקודמת:
טווח ה-CIDR של Pod ב-IPv4 כולל 2(32-16) = 216 כתובות. גודל המסכה לכל צומת הוא 24, כך שמספר הכתובות שמוקצות לכל צומת הוא 2(32-24) = 28.
טווח ה-CIDR של Pod ב-IPv6 כולל 2(128-112) = 216 כתובות. גודל המסכה לכל צומת הוא 120, כך שמספר הכתובות שמוקצות לכל צומת הוא 2(128-120) = 28.
קובץ תצורה לדוגמה
סיום יצירת האשכול
מסיימים ליצור את האשכול כמו שמתואר במסמך ליצירת אשכול.
הצגת צמתים ו-Pods של אשכולות
מציגים את צומתי האשכול:
kubectl --kubeconfig CLUSTER_KUBECONFIG get nodes --output yaml
מחליפים את CLUSTER_KUBECONFIG בנתיב של קובץ ה-kubeconfig של האשכול.
בפלט, אפשר לראות את כתובות ה-IPv4 וה-IPv6 של כל צומת. אפשר גם לראות את טווחי הכתובות של IPv4 ו-IPv6 לפודים בצומת. לדוגמה:
- apiVersion: v1
kind: Node
...
spec:
podCIDR: 192.168.1.0/24
podCIDRs:
- 192.168.1.0/24
- fd12::1:100/120
providerID: baremetal://10.2.0.5
status:
addresses:
- address: 10.2.0.5
type: InternalIP
- address: fd12::2:5
type: InternalIP
מציגים את ה-Pods באשכול:
kubectl --kubeconfig CLUSTER_KUBECONFIG get pods --all-namespaces
בוחרים Pod אחד ומפרטים את הפרטים שלו. לדוגמה:
kubectl --kubeconfig CLUSTER_KUBECONFIG get pod gke-metrics-agent-b9qrv \ --namespace kube-system \ -- output yaml
בפלט אפשר לראות את כתובות ה-IPv4 וה-IPv6 של ה-Pod. לדוגמה:
apiVersion: v1 kind: Pod metadata: ... name: gke-metrics-agent-b9qrv namespace: kube-system ... status: ... podIPs: - ip: 192.168.1.146 - ip: fd12::1:11a
וריאציות של שימוש ב-ClusterCIDRConfig
בדוגמה הקודמת השתמשנו באובייקט ClusterCIDRConfig כדי לציין טווחי CIDR של Pod ברמת האשכול. כלומר, טווח CIDR אחד של IPv4 משמש לכל ה-Pods באשכול. טווח CIDR אחד של IPv6 משמש לכל ה-Pods באשכול.
במצבים מסוימים, יכול להיות שלא תרצו להשתמש בטווח CIDR אחד לכל ה-Pods באשכול. לדוגמה, יכול להיות שתרצו לציין טווח CIDR נפרד לכל מאגר צמתים, או טווח CIDR נפרד לכל צומת. למידע נוסף על ClusterCIDRConfig ודוגמאות לשימוש בו, קראו את המאמר הסבר על המשאב המותאם אישית ClusterCIDRConfig.
לדוגמה, בקובץ ה-ClusterCIDRConfig הבא מצוין טווח CIDR למאגר צמתים בשם "workers".
apiVersion: baremetal.cluster.gke.io/v1alpha1
kind: ClusterCIDRConfig
metadata:
name: "worker-pool-ccc"
namespace: "cluster-dual-stack"
spec:
ipv4:
cidr: "192.168.0.0/16"
perNodeMaskSize: 24
ipv6:
cidr: "fd12::1:0/112"
perNodeMaskSize: 120
nodeSelector:
matchLabels:
baremetal.cluster.gke.io/node-pool: "workers"
הפקודה הבאה, ClusterCIDRConfig, מציינת טווח CIDR לצומת יחיד עם כתובת ה-IP 10.2.0.5:
apiVersion: baremetal.cluster.gke.io/v1alpha1
kind: ClusterCIDRConfig
metadata:
name: "range-node1"
namespace: "cluster-dual-stack"
spec:
ipv4:
cidr: "192.168.1.0/24"
perNodeMaskSize: 24
ipv6:
cidr: "fd12::1:0/120"
perNodeMaskSize: 120
nodeSelector:
matchLabels:
baremetal.cluster.gke.io/k8s-ip: "10.2.0.5"
יצירת שירות מסוג ClusterIP עם מחסנית כפולה
הנה מניפסט לפריסה:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "my-deployment"
spec:
selector:
matchLabels:
app: "try-dual-stack"
replicas: 3
template:
metadata:
labels:
app: "try-dual-stack"
spec:
containers:
- name: "hello"
image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:2.0"
שומרים את המניפסט בקובץ בשם my-deployment.yaml ויוצרים את הפריסה:
kubectl --kubeconfig CLUSTER_KUBECONFIG apply -f my-deployment.yaml
מחליפים את CLUSTER_KUBECONFIG בנתיב של קובץ ה-kubeconfig של האשכול.
זהו מניפסט של שירות מסוג ClusterIP:
apiVersion: v1
kind: Service
metadata:
name: "my-service"
spec:
selector:
app: "try-dual-stack"
type: "ClusterIP"
ipFamilyPolicy: "RequireDualStack"
ipFamilies:
- "IPv6"
- "IPv4"
ports:
- port: 80
targetPort: 8080
בהקשר של התרגיל הזה, אלה הנקודות העיקריות שצריך להבין לגבי המניפסט של השירות שלמעלה:
השדה
ipFamilyPolicyמוגדר לערךRequireDualStack. המשמעות היא שכתובות IPv6 ו-IPv4 שלClusterIPמוקצות לשירות.בשדה
ipFamilies, משפחת IPv6 מופיעה ראשונה ומשפחת IPv4 מופיעה שנייה. כלומר,spec.ClusterIPשל השירות תהיה כתובת IPv6 שנבחרה מתוךclusterNetwork.services.cidrBlocksבמניפסט של האשכול.
שומרים את המניפסט בקובץ בשם my-cip-service.yaml ויוצרים את השירות:
kubectl --kubeconfig CLUSTER_KUBECONFIG apply -f my-cip-service.yaml
פרטים על השירות:
kubectl --kubeconfig CLUSTER_KUBECONFIG get service my-service --output yaml
בפלט אפשר לראות את כתובות ה-IP של האשכול של השירות. לדוגמה:
apiVersion: v1 kind: Service metadata: name: my-service … spec: clusterIP: fd12::5:9af clusterIPs: - fd12::5:9af - 172.16.12.197
בצומת של אשכול, קוראים לשירות:
curl IPV4_CLUSTER_IP curl IPV6_CLUSTER_IP
בפלט מוצגת ההודעה "Hello world":
Hello, world! Version: 2.0.0 Hostname: my-deployment-xxx
יצירת שירות מסוג LoadBalancer עם מחסנית כפולה
זהו מניפסט של שירות מסוג LoadBalancer:
apiVersion: v1
kind: Service
metadata:
name: "my-lb-service"
spec:
selector:
app: "try-dual-stack"
type: "LoadBalancer"
ipFamilyPolicy: "RequireDualStack"
ipFamilies:
- "IPv6"
- "IPv4"
ports:
- port: 80
targetPort: 8080
שומרים את המניפסט בקובץ בשם my-lb-service.yaml ויוצרים את השירות:
kubectl --kubeconfig CLUSTER_KUBECONFIG apply -f my-lb-service.yaml
זוכרים שבמניפסט של האשכול ציינתם טווח של כתובות IPv6 וטווח של כתובות IPv4 לשימוש בשירותים מסוג LoadBalancer?
loadBalancer:
mode: "bundled"
...
addressPools:
- name: "pool-1"
addresses:
- "10.2.0.112-10.2.0.221"
- "fd12::4:101-fd12::4:110"
לשירות תוקצה כתובת IPv4 חיצונית שנבחרה מטווח ה-IPv4 וכתובת IPv6 חיצונית שנבחרה מטווח ה-IPv6.
פרטי הרשימה של השירות:
kubectl --kubeconfig CLUSTER_KUBECONFIG get service my-lb-service --output yaml
בפלט אפשר לראות את הכתובות החיצוניות של השירות. לדוגמה:
apiVersion: v1
kind: Service
metadata:
name: my-lb-service
...
status:
loadBalancer:
ingress:
- ip: 10.2.0.213
- ip: fd12::4:101
ערכים אפשריים של ipFamilyPolicy
כשיוצרים שירות עם מחסנית כפולה, אפשר להגדיר את ipFamilyPolicy לאחד מהערכים הבאים:
SingleStack: הבקר מקצה כתובת IP של אשכול לשירות, שנבחרה מהטווח הראשון שצוין במניפסט של האשכול בקטעclusterNetwork.services.cidrBlocks.
PreferDualStack: הבקר מקצה כתובות IP של אשכול IPv4 ו-IPv6 לשירות, שנבחרו מהטווחים שצוינו במניפסט של האשכול בקטעclusterNetwork.services.cidrBlocks. אם האשכול הוא לא אשכול עם מחסנית כפולה, ההתנהגות זהה לזו שלSingleStack.
RequireDualStack: הבקר מקצה כתובות IP של אשכול IPv4 ו-IPv6 לשירות, שנבחרו מהטווחים שצוינו במניפסט של האשכול בקטעclusterNetwork.services.cidrBlocks. הוא מגדיר את הערך שלspec.clusterIPעל סמך משפחת הכתובות הראשונה שצוינה במניפסט של השירות בקטעipFamilies.
מידע נוסף
מידע נוסף על יצירת שירותים עם שני סוגי כתובות IP זמין במאמר אפשרויות לשני סוגי כתובות IP בשירותים חדשים.