באמצעות וירטואליזציה של קלט/פלט עם שורש יחיד (SR-IOV) אתם יכולים לנהל ולהקצות חיבורי רשת עם ביצועים גבוהים לעומסי עבודה של Pod ב-Google Distributed Cloud. SR-IOV הופך כרטיס רשת (NIC) לזמין כמכשירי רשת בליבת Linux, כך שחבילות נתונים עוברות ישירות בין כרטיס הרשת לבין ה-Pod, וכך משפרות את ביצועי הרשת. כדאי להשתמש ב-SR-IOV אם עומסי העבודה של ה-Pod דורשים רשת מהירה.
באמצעות SR-IOV, אתם יכולים להגדיר פונקציות וירטואליות (VF) במכשירים נתמכים של צומתי אשכולות, ולציין את מודול הליבה (kernel) שצריך לקשר לפונקציות הווירטואליות האלה. התכונה הזו זמינה לאשכולות שמריצים עומסי עבודה, כולל אשכולות היברידיים, עצמאיים ומשתמשים.
דרישות
כדי להשתמש בתכונת הרשת SR-IOV, באשכול צריכים להיות לפחות שני צמתים.
כדי להשתמש בתכונת הרשת SR-IOV, צריך להתקין את מנהלי ההתקנים הרשמיים של מתאמי הרשת בצומתי האשכול. צריך להתקין את מנהלי ההתקנים לפני שמשתמשים באופרטור SR-IOV. כדי להשתמש במודול vfio-pci לפונקציות הווירטואליות, צריך לוודא שהמודול זמין בצמתים שבהם רוצים להשתמש בו.
סקירה כללית על הגדרת רשת SR-IOV
תהליך ההגדרה כולל את השלבים הכלליים הבאים:
- מגדירים את האשכול כדי להפעיל רשת SR-IOV.
- מגדירים את מפעיל ה-SR-IOV, משאב בהתאמה אישית של
SriovOperatorConfig. - מגדירים את כללי המדיניות של SR-IOV ואת ה-VF.
- יוצרים משאב בהתאמה אישית של
NetworkAttachmentDefinitionשמפנה ל-VF.
הפעלת רשת SR-IOV באשכול
כדי להפעיל רשת SR-IOV ב-Google Distributed Cloud, מוסיפים את השדה multipleNetworkInterfaces ואת השדה sriovOperator לקטע clusterNetwork של אובייקט האשכול, ומגדירים את שני השדות כ-true.
apiVersion: baremetal.cluster.gke.io/v1
kind: Cluster
metadata:
name: cluster1
spec:
clusterNetwork:
multipleNetworkInterfaces: true
sriovOperator: true
...
השדה sriovOperator ניתן לשינוי אחרי שיוצרים את האשכול.
הגדרת האופרטור SR-IOV
המשאב המותאם אישית SriovOperatorConfig מספק הגדרה גלובלית לתכונת הרשת SR-IOV. השם של המשאב המותאם אישית הזה הוא default והוא נמצא במרחב השמות gke-operators. המשאב המותאם אישית SriovOperatorConfig מכובד רק עבור השם ומרחב השמות האלה.
אפשר לערוך את האובייקט הזה באמצעות הפקודה הבאה:
kubectl -n gke-operators edit sriovoperatorconfigs.sriovnetwork.k8s.cni.cncf.io defaultדוגמה להגדרת משאב בהתאמה אישית של SriovOperatorConfig:
apiVersion: sriovnetwork.k8s.cni.cncf.io/v1
kind: SriovOperatorConfig
metadata:
name: default
namespace: gke-operators
spec:
configDaemonNodeSelector:
nodePool: "withSriov"
disableDrain: false
logLevel: 0
בקטע configDaemonNodeSelector אפשר להגביל את הצמתים שהאופרטור SR-IOV יכול לטפל בהם. בדוגמה שלמעלה, האופרטור מוגבל רק לצמתים עם התווית nodePool: withSriov. אם לא מציינים את השדה configDaemonNodeSelector, המערכת מוסיפה את תוויות ברירת המחדל הבאות:
beta.kubernetes.io/os: linux
node-role.kubernetes.io/worker: ""
השדה disableDrain מציין אם לבצע פעולת ניקוז של צומת Kubernetes לפני שצריך להפעיל מחדש את הצומת או לפני שמשנים הגדרת VF ספציפית.
יצירת כללי מדיניות של SR-IOV
כדי להגדיר פונקציות וירטואליות ספציפיות באשכול, צריך ליצור משאב בהתאמה אישית של SriovNetworkNodePolicy במרחב השמות gke-operators.
דוגמה למניפסט של משאב מותאם אישית מסוג SriovNetworkNodePolicy:
apiVersion: sriovnetwork.k8s.cni.cncf.io/v1
kind: SriovNetworkNodePolicy
metadata:
name: policy-1
namespace: gke-operators
spec:
deviceType: "netdevice"
mtu: 1600
nodeSelector:
baremetal.cluster.gke.io/node-pool: node-pool-1
nicSelector:
pfNames:
- enp65s0f0
deviceID: "1015"
rootDevices:
- 0000:01:00.0
vendor: "15b3"
numVfs: 4
priority: 80
resourceName: "mlnx"
בקטע nodeSelector אפשר להגביל עוד יותר את הצמתים שבהם צריך ליצור את ה-VFs. המגבלה הזו היא בנוסף לבוררים מ-SriovOperatorConfig שמתוארים בקטע הקודם.
בשדה deviceType מציינים את מודול הליבה (kernel) שבו רוצים להשתמש בשביל ה-VF. האפשרויות הזמינות ל-deviceType הן:
-
netdeviceלמודול ליבה (kernel) סטנדרטי ספציפי ל-VF -
vfio-pciלמנהל ההתקן של VFIO-PCI
השדה resourceName מגדיר את השם שבו הפונקציות הווירטואליות מיוצגות בצומת Kubernetes.
אחרי שתהליך ההגדרה מסתיים, צומתי האשכול שבחרתם מכילים את המשאב שהוגדר, כמו בדוגמה הבאה (שימו לב ל-gke.io/mlnx):
apiVersion: v1
kind: Node
metadata:
name: worker-01
spec:
…
status:
allocatable:
cpu: 47410m
ephemeral-storage: "210725550141"
gke.io/mlnx: "4"
hugepages-1Gi: "0"
hugepages-2Mi: "0"
memory: 59884492Ki
pods: "250"
capacity:
cpu: "48"
ephemeral-storage: 228651856Ki
gke.io/mlnx: "4"
hugepages-1Gi: "0"
hugepages-2Mi: "0"
memory: 65516492Ki
pods: "250"
האופרטור תמיד יוסיף את הקידומת gke.io/ לכל משאב שתגדירו באמצעות SriovNetworkNodePolicy.
ציון בורר NIC
כדי ש-SriovNetworkNodePolicy יפעל כמו שצריך, צריך לציין לפחות בורר אחד בקטע nicSelector. השדה הזה מכיל כמה אפשרויות לזיהוי פונקציות פיזיות (PF) ספציפיות בצמתים של האשכול. רוב המידע שנדרש בשדה הזה מזוהה בשבילכם ונשמר במשאב המותאם אישית SriovNetworkNodeState. יהיה אובייקט לכל צומת שהאופרטור הזה יכול לטפל בו.
כדי לראות את כל הצמתים הזמינים, משתמשים בפקודה הבאה:
kubectl -n gke-operators get sriovnetworknodestates.sriovnetwork.k8s.cni.cncf.io -o yamlדוגמה לצומת:
apiVersion: sriovnetwork.k8s.cni.cncf.io/v1
kind: SriovNetworkNodeState
metadata:
name: worker-01
namespace: gke-operators
spec:
dpConfigVersion: "6368949"
status:
interfaces:
- deviceID: "1015"
driver: mlx5_core
eSwitchMode: legacy
linkSpeed: 10000 Mb/s
linkType: ETH
mac: 1c:34:da:5c:2b:9c
mtu: 1500
name: enp1s0f0
pciAddress: "0000:01:00.0"
totalvfs: 4
vendor: 15b3
- deviceID: "1015"
driver: mlx5_core
linkSpeed: 10000 Mb/s
linkType: ETH
mac: 1c:34:da:5c:2b:9d
mtu: 1500
name: enp1s0f1
pciAddress: "0000:01:00.1"
totalvfs: 2
vendor: 15b3
syncStatus: Succeeded
הגדרת חלוקה למחיצות של פונקציות פיזיות
חשוב לשים לב במיוחד לשדה pfNames בקטע nicSelector. בנוסף להגדרת ה-PF המדויק שבו צריך להשתמש, היא מאפשרת לציין את ה-VFs המדויקים שבהם צריך להשתמש בשביל ה-PF שצוין והמשאב שמוגדר במדיניות.
הנה דוגמה:
apiVersion: sriovnetwork.k8s.cni.cncf.io/v1
kind: SriovNetworkNodePolicy
metadata:
name: policy-1
namespace: gke-operators
spec:
deviceType: "netdevice"
mtu: 1600
nodeSelector:
baremetal.cluster.gke.io/node-pool: node-pool-1
nicSelector:
pfNames:
- enp65s0f0#3-6
deviceID: "1015"
rootDevices:
- 0000:01:00.0
vendor: "15b3"
numVfs: 7
priority: 80
resourceName: "mlnx"
בדוגמה שלמעלה, המשאב gke.io/mlnx משתמש רק בפונקציות וירטואליות שממוספרות 3-6, ומציג רק ארבע פונקציות וירטואליות זמינות. מכיוון שהפונקציות הווירטואליות תמיד נוצרות מהאינדקס אפס, מספר הפונקציות הווירטואליות שביקשתם, numVfs, צריך להיות לפחות בגובה של ערך סגירת הטווח (בספירה מאפס). לפי הלוגיקה הזו של המספור, בדוגמה שלמעלה הערך של numVfs הוא 7. אם מגדירים טווח של 3 עד 4 (enp65s0f0#3-4), הערך של numVfs צריך להיות לפחות 5.
אם לא מציינים את החלוקה למחיצות, הערך של numVfs מגדיר את טווח ה-VF שבו משתמשים, שתמיד מתחיל מאפס. לדוגמה, אם מגדירים את numVfs=3 בלי לציין חלוקה למחיצות, המערכת משתמשת ב-0-2 VFs.
הסבר על עדיפות המדיניות
אפשר לציין כמה אובייקטים של SriovNetworkNodePolicy כדי לטפל בספקים שונים או בהגדרות שונות של VF. ניהול של כמה אובייקטים וספקים עלול להיות בעייתי כשכמה כללי מדיניות מפנים לאותו PF. כדי לטפל במצבים כאלה, השדה priority פותר את ההתנגשויות על בסיס כל צומת בנפרד.
זהו סדר העדיפויות של כללי מדיניות חופפים של PF:
מדיניות בעדיפות גבוהה יותר מחליפה מדיניות בעדיפות נמוכה יותר רק כשיש חפיפה בחלוקה למחיצות של PF.
כללי מדיניות עם אותה עדיפות ממוזגים:
- כללי המדיניות ממוינים לפי שם ומעובדים בסדר הזה
- כללי מדיניות עם חלוקה חופפת של PF מוחלפים
- כללי מדיניות עם חלוקה למחיצות PF שלא חופפות מוזגו וכולם קיימים
מדיניות בעדיפות גבוהה היא מדיניות עם ערך מספרי נמוך יותר בשדה priority. לדוגמה, העדיפות של מדיניות עם priority: 10 גבוהה יותר מהעדיפות של מדיניות עם priority: 20.
בקטעים הבאים מפורטות דוגמאות למדיניות של הגדרות חלוקה למחיצות שונות.
PF עם חלוקה למחיצות
פריסת שני המניפסטים הבאים של SriovNetworkNodePolicy יוצרת שני משאבים זמינים: gke.io/dev-kernel ו-gke.io/dev-vfio. לכל משאב יש שתי פונקציות וירטואליות שלא חופפות.
kind: SriovNetworkNodePolicy
metadata:
name: policy-1
spec:
deviceType: "netdevice"
nodeSelector:
baremetal.cluster.gke.io/node-pool: node-pool-1
nicSelector:
pfNames:
- enp65s0f0#0-1
numVfs: 2
priority: 70
resourceName: "dev-kernel"
kind: SriovNetworkNodePolicy
metadata:
name: policy-2
spec:
deviceType: "vfio-pci"
nodeSelector:
baremetal.cluster.gke.io/node-pool: node-pool-1
nicSelector:
pfNames:
- enp65s0f0#2-3
numVfs: 4
priority: 70
resourceName: "dev-vfio"
חלוקה חופפת למחיצות של PF
אם פורסים את שני המניפסטים הבאים של SriovNetworkNodePolicy, רק המשאב gke.io/dev-vfio יהיה זמין. טווח ה-VF של policy-1 הוא 0-2, והוא חופף ל-policy-2. בגלל השם, policy-2 מעובד אחרי policy-1. לכן, רק המשאב שצוין ב-policy-2, gke.io/dev-vfio, זמין.
kind: SriovNetworkNodePolicy
metadata:
name: policy-1
spec:
deviceType: "netdevice"
nodeSelector:
baremetal.cluster.gke.io/node-pool: node-pool-1
nicSelector:
pfNames:
- enp65s0f0
numVfs: 3
priority: 70
resourceName: "dev-kernel"
kind: SriovNetworkNodePolicy
metadata:
name: policy-2
spec:
deviceType: "vfio-pci"
nodeSelector:
baremetal.cluster.gke.io/node-pool: node-pool-1
nicSelector:
pfNames:
- enp65s0f0#2-3
numVfs: 4
priority: 70
resourceName: "dev-vfio"
חלוקה למחיצות של פונקציות פיזיות (PF) ללא חפיפה עם עדיפויות שונות
פריסת שני המניפסטים הבאים של SriovNetworkNodePolicy יוצרת שני משאבים זמינים: gke.io/dev-kernel ו-gke.io/dev-vfio. לכל משאב יש שתי פונקציות וירטואליות שלא חופפות. למרות שלמדיניות policy-1 יש עדיפות גבוהה יותר מאשר למדיניות policy-2, מכיוון שהחלוקה למחיצות של PF לא חופפת, אנחנו ממזגים את שתי כללי המדיניות.
kind: SriovNetworkNodePolicy
metadata:
name: policy-1
spec:
deviceType: "netdevice"
nodeSelector:
baremetal.cluster.gke.io/node-pool: node-pool-1
nicSelector:
pfNames:
- enp65s0f0
numVfs: 2
priority: 10
resourceName: "dev-kernel"
kind: SriovNetworkNodePolicy
metadata:
name: policy-2
spec:
deviceType: "vfio-pci"
nodeSelector:
baremetal.cluster.gke.io/node-pool: node-pool-1
nicSelector:
pfNames:
- enp65s0f0#2-3
numVfs: 4
priority: 70
resourceName: "dev-vfio"
בדיקת סטטוס ההגדרה של מדיניות SR-IOV
כשמחילים את כללי המדיניות של SR-IOV, אפשר לעקוב אחרי ההגדרות הסופיות של הצמתים ולראות אותן במשאב המותאם אישית SriovNetworkNodeState של הצומת הספציפי. בקטע status, השדה syncStatus מייצג את השלב הנוכחי של הדימון (daemon) של ההגדרות האישיות. הסטטוס Succeeded מציין שההגדרה הסתיימה. הקטע spec במשאב המותאם אישית SriovNetworkNodeState מגדיר את המצב הסופי של הגדרת ה-VF של אותו צומת, על סמך מספר כללי המדיניות וסדרי העדיפויות שלהם. כל ה-VF שנוצרו יופיעו בקטע status של ה-PF שצוינו.
דוגמה למשאב בהתאמה אישית של SriovNetworkNodeState:
apiVersion: sriovnetwork.k8s.cni.cncf.io/v1
kind: SriovNetworkNodeState
metadata:
name: worker-02
namespace: gke-operators
spec:
dpConfigVersion: "9022068"
interfaces:
- linkType: eth
name: enp1s0f0
numVfs: 2
pciAddress: "0000:01:00.0"
vfGroups:
- deviceType: netdevice
policyName: policy-1
resourceName: mlnx
vfRange: 0-1
status:
interfaces:
- Vfs:
- deviceID: "1016"
driver: mlx5_core
mac: 96:8b:39:d8:89:d2
mtu: 1500
name: enp1s0f0np0v0
pciAddress: "0000:01:00.2"
vendor: 15b3
vfID: 0
- deviceID: "1016"
driver: mlx5_core
mac: 82:8e:65:fe:9b:cb
mtu: 1500
name: enp1s0f0np0v1
pciAddress: "0000:01:00.3"
vendor: 15b3
vfID: 1
deviceID: "1015"
driver: mlx5_core
eSwitchMode: legacy
linkSpeed: 10000 Mb/s
linkType: ETH
mac: 1c:34:da:5c:2b:9c
mtu: 1500
name: enp1s0f0
numVfs: 2
pciAddress: "0000:01:00.0"
totalvfs: 2
vendor: 15b3
- deviceID: "1015"
driver: mlx5_core
linkSpeed: 10000 Mb/s
linkType: ETH
mac: 1c:34:da:5c:2b:9d
mtu: 1500
name: enp1s0f1
pciAddress: "0000:01:00.1"
totalvfs: 2
vendor: 15b3
syncStatus: Succeeded
יצירת משאב בהתאמה אישית של NetworkAttachmentDefinition
אחרי שמגדירים את ה-VF באשכול והם מופיעים כמשאב בצומת Kubernetes, צריך ליצור NetworkAttachmentDefinition שמפנה למשאב. הפניה באמצעות הערה מסוג k8s.v1.cni.cncf.io/resourceName.
דוגמה למניפסט של NetworkAttachmentDefinition שמפנה למשאב gke.io/mlnx:
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: gke-sriov-1
annotations:
k8s.v1.cni.cncf.io/resourceName: gke.io/mlnx
spec:
config: '{
"cniVersion": "0.3.0",
"name": "mynetwork",
"type": "sriov",
"ipam": {
"type": "whereabouts",
"range": "21.0.108.0/21",
"range_start": "21.0.111.16",
"range_end": "21.0.111.18"
}
}'
הערך של NetworkAttachmentDefinition חייב להיות sriov בתור סוג ה-CNI.
מפנים לכל משאב מותאם אישית של NetworkAttachmentDefinition שנפרס ב-Pods באמצעות אנוטציה של k8s.v1.cni.cncf.io/networks.
דוגמה להפניה למשאב בהתאמה אישית של NetworkAttachmentDefinition ב-pod:
apiVersion: v1
kind: Pod
metadata:
name: samplepod
annotations:
k8s.v1.cni.cncf.io/networks: gke-sriov-1
spec:
containers:
...
כשמפנים למשאב מותאם אישית של NetworkAttachmentDefinition בעומסי עבודה, לא צריך לדאוג להגדרות המשאבים של ה-Pods או למיקום שלהם בצמתים ספציפיים, כי זה נעשה באופן אוטומטי.
בדוגמה הבאה מוצג משאב מותאם אישית של NetworkAttachmentDefinition עם הגדרת VLAN. בדוגמה הזו, כל VF שייך ל-VLAN 100:
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: gke-sriov-vlan-100
annotations:
k8s.v1.cni.cncf.io/resourceName: gke.io/mlnx
spec:
config: '{
"cniVersion": "0.3.0",
"name": "mynetwork",
"type": "sriov",
"vlan": 100,
"ipam": {
"type": "whereabouts",
"range": "21.0.100.0/21"
}
}'
מידע נוסף
בקטעים הבאים מוסבר איך להגדיר רשת SR-IOV.
הפעלות מחדש של הצומת
כשהמפעיל של SR-IOV מגדיר את הצמתים, יכול להיות שצריך להפעיל מחדש את הצמתים. יכול להיות שתצטרכו להפעיל מחדש את הצמתים במהלך הגדרת ה-VF או הליבה. הגדרת הליבה כוללת הפעלה של תמיכה בפונקציונליות של SR-IOV במערכת ההפעלה.
מתאמי רשת נתמכים
בטבלה הבאה מפורטים מתאמי הרשת שנתמכים באשכולות בגרסה 1.36.x:
| שם | מזהה הספק | מזהה המכשיר | מזהה המכשיר של VF |
|---|---|---|---|
| Intel i40e XXV710 | 8086 | 158a | 154c |
| Intel i40e 25G SFP28 | 8086 | 158b | 154c |
| Intel i40e 10G X710 SFP | 8086 | 1572 | 154c |
| Intel i40e XXV710 N3000 | 8086 | 0d58 | 154c |
| Intel i40e 40G XL710 QSFP | 8086 | 1583 | 154c |
| Intel ice Columbiaville E810-CQDA2 2CQDA2 | 8086 | 1592 | 1889 |
| Intel ice Columbiaville E810-XXVDA4 | 8086 | 1593 | 1889 |
| Intel ice Columbiaville E810-XXVDA2 | 8086 | 159b | 1889 |
| Nvidia mlx5 ConnectX-4 | 15b3 | 1013 | 1014 |
| Nvidia mlx5 ConnectX-4LX | 15b3 | 1015 | 1016 |
| Nvidia mlx5 ConnectX-5 | 15b3 | 1017 | 1018 |
| Nvidia mlx5 ConnectX-5 Ex | 15b3 | 1019 | 101a |
| Nvidia mlx5 ConnectX-6 | 15b3 | 101b | 101c |
| Nvidia mlx5 ConnectX-6_Dx | 15b3 | 101d | 101e |
| Nvidia mlx5 MT42822 BlueField-2 integrated ConnectX-6 Dx | 15b3 | a2d6 | 101e |
| Broadcom bnxt BCM57414 2x25G | 14e4 | 16d7 | 16dc |
| Broadcom bnxt BCM75508 2x100G | 14e4 | 1750 | 1806 |