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

במאמר הזה מוסבר איך להגדיר רשתות עם וירטואליזציה של קלט/פלט (SR-IOV) עם שורש יחיד ב-Google Distributed Cloud. ‫SR-IOV מספק וירטואליזציה של קלט/פלט כדי להפוך כרטיס ממשק רשת (NIC) לזמין כמכשירי רשת בקרנל של Linux. כך תוכלו לנהל את חיבורי הרשת ולהקצות אותם לפודים. הביצועים משתפרים כי החבילות עוברות ישירות בין כרטיס הרשת לבין ה-pod.

כדאי להשתמש בתכונה הזו אם אתם צריכים רשת מהירה לעומסי העבודה של ה-Pod. ‫SR-IOV ב-Google Distributed Cloud מאפשר להגדיר את הפונקציות הווירטואליות (VF) במכשירים הנתמכים של צמתי האשכול. אפשר גם לציין את מודול הליבה הספציפי שרוצים לקשר ל-VF.

התכונה הזו זמינה לאשכולות שמריצים עומסי עבודה, כמו אשכולות היברידיים, עצמאיים ומשתמשים. כדי להשתמש בתכונת הרשת SR-IOV, באשכול צריכים להיות לפחות שני צמתים.

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

  1. מגדירים את האשכול כדי להפעיל רשת SR-IOV.
  2. מגדירים את אופרטור SR-IOV, שהוא משאב מותאם אישית של SriovOperatorConfig.
  3. הגדרת כללי מדיניות של SR-IOV והגדרת VFs.
  4. יוצרים NetworkAttachmentDefinition משאב בהתאמה אישית שמפנה אל ה-VF.

דרישות

כדי להשתמש בתכונת הרשת SR-IOV, צריך לוודא שהדרייברים הרשמיים של מתאמי הרשת קיימים בצמתי האשכול. צריך להתקין את מנהלי ההתקנים לפני שמשתמשים באופרטור SR-IOV. בנוסף, כדי להשתמש במודול vfio-pci עבור הפונקציות הווירטואליות, צריך לוודא שהמודול זמין בצמתים שבהם רוצים להשתמש בו.

הפעלת רשת SR-IOV באשכול

כדי להפעיל רשת SR-IOV ב-Google Distributed Cloud, מוסיפים את השדה multipleNetworkInterfaces ואת השדה sriovOperator לקטע clusterNetwork של אובייקט Cluster ומגדירים את שני השדות לערך 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

כדי להגדיר VFs ספציפיים באשכול, צריך ליצור 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 אפשר להגביל עוד יותר את הצמתים שבהם צריך ליצור את הפונקציות הווירטואליות. המגבלה הזו היא בנוסף לסלקטורים מתוך SriovOperatorConfig שמתוארים בקטע הקודם.

השדה deviceType מציין את מודול הליבה שבו יש להשתמש עבור ה-VF. האפשרויות הזמינות ל-deviceType הן:

  • ‫netdevice עבור מודול ליבת תקני ספציפי ל-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 המדויק לשימוש, המדיניות מאפשרת לציין את ה-VF המדויק לשימוש עבור ה-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 משתמש רק ב-VF שמספרו 3 עד 6, ומוצגים רק ארבעה VF זמינים. מכיוון ש-VF נוצרים תמיד מאינדקס אפס, מספר ה-VF המבוקש, numVfs, צריך להיות לפחות גבוה כמו ערך הסגירה של הטווח (ספירה מאפס). הלוגיקה הזו של המספור מסבירה למה הערך של numVfs מוגדר כ-7 בדוגמה הקודמת. אם מגדירים טווח של 3 עד 4 (enp65s0f0#3-4), הערך של numVfs חייב להיות לפחות 5.

אם לא מציינים את החלוקה למחיצות, השדה numVfs מגדיר את טווח הפונקציות הווירטואליות שנעשה בהן שימוש, שתמיד מתחיל מאפס. לדוגמה, אם מגדירים numVfs=3 בלי לציין חלוקה למחיצות, נעשה שימוש ב-VFs 0-2.

הסבר על סדר העדיפויות של המדיניות

אפשר לציין כמה אובייקטים של SriovNetworkNodePolicy כדי לטפל בספקים שונים או בהגדרות שונות של VF. ניהול של כמה אובייקטים וספקים עלול להיות בעייתי אם כמה כללי מדיניות מפנים לאותו PF. כדי לטפל במצבים כאלה, השדה priority פותר את הקונפליקטים על בסיס כל צומת.

הנה לוגיקת קביעת העדיפות למדיניות PF חופפת:

  1. מדיניות בעדיפות גבוהה יותר דורסת מדיניות בעדיפות נמוכה יותר רק אם חל חפיפה בין חלוקת המדיניות.

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

    1. המדיניות ממוינת לפי שם ומעובדת בסדר הזה
    2. כללי מדיניות עם חלוקת PF חופפת נכתבים מחדש
    3. מדיניות עם חלוקת PF לא חופפת מאוחדת וכל הנוכחים

מדיניות בעדיפות גבוהה היא מדיניות עם ערך מספרי נמוך יותר בשדה priority. לדוגמה, העדיפות של מדיניות עם priority: 10 גבוהה יותר מהעדיפות של מדיניות עם priority: 20.

בקטעים הבאים מופיעות דוגמאות למדיניות עבור תצורות שונות של חלוקה למחיצות.

PF מחולק למחיצות

פריסת שני המניפסטים הבאים של SriovNetworkNodePolicy יוצרת שני משאבים זמינים: gke.io/dev-kernel ו-gke.io/dev-vfio. לכל משאב יש שני VFs שלא חופפים.

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. לכל משאב יש שני VFs שלא חופפים. למרות של-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 מייצג את השלב הנוכחי של דמון ההגדרה. המצב 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 שנפרסו בתרמילים באמצעות הערה מסוג 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:

שם מזהה הספק מזהה המכשיר מזהה מכשיר וירטואלי
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