הגדרה של רשתות SR-IOV

באמצעות וירטואליזציה של קלט/פלט עם שורש יחיד (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

תהליך ההגדרה כולל את השלבים הכלליים הבאים:

  1. מגדירים את האשכול כדי להפעיל רשת SR-IOV.
  2. מגדירים את מפעיל ה-SR-IOV, משאב בהתאמה אישית של SriovOperatorConfig.
  3. מגדירים את כללי המדיניות של SR-IOV ואת ה-VF.
  4. יוצרים משאב בהתאמה אישית של 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:

  1. מדיניות בעדיפות גבוהה יותר מחליפה מדיניות בעדיפות נמוכה יותר רק כשיש חפיפה בחלוקה למחיצות של PF.

  2. כללי מדיניות עם אותה עדיפות ממוזגים:

    1. כללי המדיניות ממוינים לפי שם ומעובדים בסדר הזה
    2. כללי מדיניות עם חלוקה חופפת של PF מוחלפים
    3. כללי מדיניות עם חלוקה למחיצות 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