למכונות מסוימות שעברו אופטימיזציה לשימוש במאיצים, כולל A4X Max, A4X, A4 ו-A3 Ultra, יש שני ממשקי רשת שאפשר לצרף לרשתות רגילות של ענן וירטואלי פרטי (VPC). למכונות האלה יש גם MRDMA ממשקי רשת שצריך לצרף אותם לרשת RoCE VPC.
במארח, אלה Titanium IPU שמחוברים לשקעי CPU נפרדים ולצמתים של גישה לזיכרון לא אחיד (NUMA). יחידות ה-IPU האלה זמינות בתוך המכונה הווירטואלית ככרטיסי רשת וירטואליים של Google (gVNIC), ומספקות רוחב פס ברשת לפעולות אחסון כמו יצירת נקודות ביקורת, טעינת נתוני אימון, טעינת מודלים וצרכים כלליים אחרים ברשת. טופולוגיית ה-NUMA של המכונה, כולל של gVNIC, גלויה למערכת ההפעלה (OS) של האורח.
במסמך הזה מתוארות שיטות מומלצות לשימוש בשני gVNIC במכונות האלה.
סקירה כללית
באופן כללי, מומלץ להשתמש בהגדרות הבאות, בלי קשר לאופן שבו אתם מתכננים להשתמש בכמה כרטיסי רשת של המארח:
- הגדרות רשת: כל ממשק רשת gVNIC חייב להשתמש ברשת VPC ייחודית. אם אתם מגדירים VPC, כדאי לשים לב לנקודות הבאות:
- משתמשים ביחידת שידור מקסימלית (MTU) גדולה לכל רשת VPC. מומלץ להשתמש בערך 8896, שהוא ה-MTU המקסימלי הנתמך. יכול להיות שביצועי הכניסה של חלק מעומסי העבודה יואטו בגלל שהמערכת משמיטה מנות נתונים נכנסות בצד המקלט. אפשר להשתמש בכלי ethtool כדי לבדוק אם הבעיה קיימת. במקרה כזה, יכול להיות שיהיה שימושי לשנות את ה-MSS של TCP, את ה-MTU של הממשק או את ה-MTU של ה-VPC כדי לאפשר הקצאת נתונים יעילה ממטמון הדפים, וכך לאפשר למסגרת הנכנסת בשכבה 2 להתאים לשני מאגרי נתונים זמניים בגודל 4KB.
- הגדרות האפליקציה
- התאמה של האפליקציה ל-NUMA. שימוש בליבות מעבד, בהקצאות זיכרון ובממשק רשת מאותו צומת NUMA. אם אתם מריצים מופע ייעודי של האפליקציה כדי להשתמש בצומת NUMA ספציפי או בממשק רשת, אתם יכולים להשתמש בכלים כמו
numactlכדי לצרף את משאבי המעבד והזיכרון של האפליקציה לצומת NUMA ספציפי.
- התאמה של האפליקציה ל-NUMA. שימוש בליבות מעבד, בהקצאות זיכרון ובממשק רשת מאותו צומת NUMA. אם אתם מריצים מופע ייעודי של האפליקציה כדי להשתמש בצומת NUMA ספציפי או בממשק רשת, אתם יכולים להשתמש בכלים כמו
- הגדרות מערכת ההפעלה
- מפעילים את האפשרויות TCP segmentation offload (TSO) ו-large receive offload (LRO).
- לכל ממשק gVNIC, מוודאים שהזיקה ל-SMP מוגדרת כך שבקשות ההפרעה (IRQs) שלו מטופלות באותו צומת NUMA כמו הממשק, וההפרעות מפוזרות בין הליבות. אם אתם מפעילים תמונת מערכת הפעלה לאורח שסופקה על ידי Google, התהליך הזה מתבצע באופן אוטומטי באמצעות הסקריפט
google_set_multiqueue. - כדאי לבדוק הגדרות כמו RFS, RPS ו-XPS כדי לראות אם הן יכולות לעזור לכם בעומס העבודה.
- ב-A4X וב-A4X Max, Nvidia ממליצה להשבית את התזמון האוטומטי של NUMA.
- אין תמיכה ב-Linux kernel bonding עבור gVNIC במכונות האלה.
דפוסים לשימוש בכמה כרטיסי רשת של מארח
בקטע הזה מפורטות תבניות כלליות לשימוש בכמה כרטיסי רשת של מארחים ב-Google Cloud.
| דוגמת קוד | פריסת תהליך נתמכת | נתיב הפריסה: Compute Engine | נתיב הפריסה: GKE | נתיב הפריסה: Slurm | הערות |
|---|---|---|---|---|---|
| שינוי האפליקציה לשימוש בממשק ספציפי | תהליך שרד לכל ממשק | כן | כן | כן | נדרשים שינויים בקוד של האפליקציה |
| שינוי האפליקציה כך שתשתמש בשני הממשקים | תהליך עם שני ממשקים | כן | כן | כן | נדרשים שינויים בקוד של האפליקציה |
| שימוש במרחב שמות ייעודי ברשת עבור אפליקציות ספציפיות | תהליך שרד לכל ממשק | כן | כן (רק למאגרי מידע עם הרשאות מיוחדות) | לא | |
| מיפוי של כל התעבורה של מאגר תגים לממשק יחיד | כל התנועה במאגר ממופה לממשק אחד | כן | כן | לא | |
| קישור בין רשתות שכנות (peering) של VPC ומאפשרים למערכת לאזן את עומס הסשנים בין הממשקים | תהליך עם שני ממשקים | כן* | כן* | כן* | קשה או בלתי אפשרי לבצע התאמה ל-NUMA. נדרשת גרסת Linux Kernel 6.16 ואילך* |
| פיצול התנועה בין רשתות | תהליך עם שני ממשקים, שבר תהליך לכל ממשק | כן* | כן* | כן* | יכול להיות שיידרשו שינויים בקוד כדי לבצע התאמה ל-NUMA אם מריצים תהליך עם שני ממשקים. |
| שימוש ב-SNAT לבחירת ממשק המקור | תהליך עם שני ממשקים, שבר תהליך לכל ממשק | כן | כן (נדרשות הרשאות אדמין כדי להגדיר) | כן (נדרשות הרשאות אדמין כדי להגדיר) | יכול להיות שיהיה קשה יותר להגדיר אותו בצורה נכונה |
* האפשרות הזו בדרך כלל לא מומלצת, אבל היא יכולה להיות שימושית לעומסי עבודה מוגבלים בפלטפורמות x86 (A3 Ultra ו-A4).
שינוי האפליקציה לשימוש בממשק ספציפי
דרישות:
- השיטה הזו מחייבת שינויים בקוד של האפליקציה.
- נדרשות הרשאות לאחת או יותר מהשיטות הבאות:
bind()דורש הרשאות מיוחדות רק אם משתמשים ביציאת מקור עם הרשאות.-
SO_BINDTODEVICE: נדרשת הרשאהCAP_NET_RAW.
- יכול להיות שתצטרכו לשנות את טבלת הניתוב של ליבת המערכת כדי להגדיר מסלולים ולמנוע ניתוב לא סימטרי.
סקירה כללית
בשיטה הזו, אתם משלימים את הפעולות הבאות:
- מוסיפים קשירת ממשק רשת לקוד המקור של האפליקציה באמצעות אחת מהאפשרויות הבאות:
- משתמשים ב-
bind()כדי לקשור שקע לכתובת IP ספציפית של מקור - משתמשים באפשרות השקע
SO_BINDTODEVICEכדי לקשור שקע לממשק רשת מסוים
- משתמשים ב-
- משנים את טבלת הניתוב של ליבת המערכת לפי הצורך כדי לוודא שקיים מסלול מממשק הרשת של המקור לכתובת היעד. בנוסף, יכול להיות שיהיה צורך במסלולים כדי למנוע ניתוב לא סימטרי. מומלץ להגדיר ניתוב מדיניות כמו שמתואר במאמר הגדרת ניתוב לממשק רשת נוסף.
- אפשר גם להשתמש בפקודה
numactlכדי להפעיל את האפליקציה. בגישה הזו, משתמשים בזיכרון ובמעבדים שנמצאים באותו צומת NUMA כמו ממשק הרשת שנבחר.
אחרי שמבצעים את השלבים הקודמים, מופעים של האפליקציה פועלים באמצעות ממשק רשת ספציפי.
שינוי האפליקציה כדי להשתמש בשני הממשקים
דרישות:
- השיטה הזו מחייבת שינויים בקוד של האפליקציה.
- כדי להשתמש באחת או יותר מהשיטות הבאות, נדרשות הרשאות:
bind()דורש הרשאות מיוחדות רק אם משתמשים ביציאת מקור עם הרשאות.-
SO_BINDTODEVICE: נדרשת הרשאהCAP_NET_RAW.
- יכול להיות שתצטרכו לשנות את טבלת הניתוב של ליבת המערכת כדי להגדיר מסלולים ולמנוע ניתוב לא סימטרי.
סקירה כללית
כדי להטמיע את התבנית הזו, מבצעים את הפעולות הבאות:
- מוסיפים קשירת ממשק רשת לקוד המקור של האפליקציה באמצעות אחת מהאפשרויות הבאות:
- משתמשים בקריאת המערכת
bind()כדי לקשור שקע לכתובת IP מסוימת של מקור - משתמשים באפשרות השקע
SO_BINDTODEVICEכדי לקשור שקע לממשק רשת מסוים
- משתמשים בקריאת המערכת
- אם האפליקציה פועלת כלקוח, תצטרכו ליצור שקע לקוח נפרד לכל ממשק רשת מקור.
- משנים את טבלת הניתוב של ליבת המערכת לפי הצורך כדי לוודא שקיים מסלול מממשק הרשת של המקור לכתובת היעד. בנוסף, יכול להיות שתצטרכו גם מסלולים כדי למנוע ניתוב אסימטרי. מומלץ להגדיר ניתוב מדיניות כמו שמתואר במאמר הגדרת ניתוב לממשק רשת נוסף.
- מומלץ לחלק את פעילות הרשת לשרשורים שפועלים באותו צומת NUMA כמו ממשק gVNIC. דרך נפוצה לבקש צומת NUMA ספציפי לשרשור היא לקרוא ל-
pthread_setaffinity_np.- מכיוון שהאפליקציה משתמשת במשאבים בכמה צמתי NUMA, צריך להימנע משימוש ב-
numactlאו לוודא שהפקודהnumactlכוללת את צמתי ה-NUMA של כל ממשקי הרשת שבהם האפליקציה משתמשת.
- מכיוון שהאפליקציה משתמשת במשאבים בכמה צמתי NUMA, צריך להימנע משימוש ב-
שימוש במרחב שמות ייעודי ברשת לאפליקציות ספציפיות
דרישות:
- נדרשת יכולת
CAP_SYS_ADMIN. - לא תואם ל-GKE autopilot.
- אם משתמשים ב-GKE, צריך להשתמש בקונטיינר עם הרשאות.
בקטע הזה מתוארים דפוסים שאפשר להשתמש בהם כדי ליצור מרחב שמות ברשת שמשתמש בממשק רשת משני. הדפוס שמתאים לעומס העבודה שלכם תלוי בתרחיש הספציפי. הגישות שמשתמשות במתג וירטואלי או ב-IPvlan מתאימות יותר למקרים שבהם כמה אפליקציות צריכות להשתמש בממשק המשני ממרחבי שמות שונים ברשת.
סקירה כללית: העברת הממשק המשני למרחב שמות ייעודי ברשת
הדפוס הזה כולל יצירה של מרחב שמות ברשת, העברה של הממשק המשני של gVNIC למרחב השמות החדש, ולאחר מכן הפעלה של האפליקציה ממרחב השמות הזה. יכול להיות שיהיה פחות מסובך להגדיר את התבנית הזו ולבצע בה שינויים בהשוואה לשימוש במתג וירטואלי. עם זאת, לאפליקציות מחוץ למרחב השמות החדש של הרשת לא תהיה גישה ל-gVNIC המשני.
בדוגמה הבאה מוצגת סדרה של פקודות שאפשר להשתמש בהן כדי להעביר את eth1 למרחב השמות החדש של הרשת שנקרא second.
ip netns add second
ip link set eth1 netns second
ip netns exec second ip addr add ${ETH1_IP}/${PREFIX} dev eth1
ip netns exec second ip link set dev eth1 up
ip netns exec second ip route add default via ${GATEWAY_IP} dev eth1
ip netns exec second <command>
כשמריצים את הפקודה הזו, הביטוי <command> מופעל בתוך מרחב השמות של הרשת, ומשתמש בממשק eth1.
אפליקציות שפועלות בתוך מרחב השמות החדש של הרשת משתמשות עכשיו ב-gVNIC המשני. אפשר גם להשתמש בפקודה numactl כדי להריץ את האפליקציה באמצעות הזיכרון והמעבדים שנמצאים באותו צומת NUMA כמו ממשק הרשת שבחרתם.
סקירה כללית: שימוש במתג וירטואלי ובמרחב שמות ברשת עבור ממשק משני בתבנית הזו יוצרים הגדרה של מתג וירטואלי כדי להשתמש ב-gVNIC המשני ממרחב שמות ברשת.
השלבים הכלליים הם:
- יוצרים צמד של מכשירי אתרנט וירטואליים (veth). משנים את יחידת השידור המקסימלית (MTU) בכל אחד מהמכשירים כך שתתאים ל-MTU של gVNIC המשני.
- מריצים את הפקודה הבאה כדי לוודא שהעברת כתובות IP מופעלת עבור IPv4: sysctl -w net.ipv4.ip_forward=1
- מעבירים קצה אחד של זוג veth למרחב שמות חדש ברשת, ומשאירים את הקצה השני במרחב השמות של הבסיס.
- מיפוי התנועה ממכשיר veth לממשק gVNIC המשני. יש כמה דרכים לעשות את זה, אבל אנחנו ממליצים ליצור טווח של כינויי IP לממשק המשני של המכונה הווירטואלית ולהקצות כתובת IP מהטווח הזה לממשק הצאצא במרחב השמות.
- מריצים את האפליקציה ממרחב השמות החדש של הרשת. אתם יכולים להשתמש בפקודה
numactlכדי להריץ את האפליקציה באמצעות זיכרון ומעבדים שנמצאים באותו צומת NUMA כמו ממשק הרשת שנבחר.
בהתאם להגדרות של האורח ועומס העבודה, אפשר גם להשתמש במנהל ההתקנים (דרייבר) IPvlan עם ממשק IPvlan שמקושר ל-gVNIC המשני, במקום ליצור את מכשירי veth.
מיפוי של נפח התנועה של מאגר תגים שלם לממשק יחיד
דרישות:
- האפליקציה צריכה לפעול בתוך קונטיינר שמשתמש במרחב שמות של רשת לצורך יצירת רשת של קונטיינרים, כמו GKE, Docker או Podman. אי אפשר להשתמש ברשת המארחת.
הרבה טכנולוגיות של קונטיינרים, כמו GKE, Docker ו-Podman, משתמשות במרחב שמות ייעודי ברשת כדי לבודד את התנועה של הקונטיינר. אחר כך אפשר לשנות את מרחב השמות של הרשת, ישירות או באמצעות הכלים של טכנולוגיית הקונטיינרים, כדי למפות את התנועה לממשק רשת אחר.
ב-GKE, הממשק הראשי נדרש לתקשורת פנימית של Kubernetes. עם זאת, אפשר לשנות את נתיב ברירת המחדל בתרמיל כדי להשתמש בממשק המשני, כמו שמוצג במניפסט של תרמיל GKE הבא.
metadata:
…
annotations:
networking.gke.io/default-interface: 'eth1'
networking.gke.io/interfaces: |
[
{"interfaceName":"eth0","network":"default"},
{"interfaceName":"eth1","network":"secondary-network"},
]
הגישה הזו לא מבטיחה התאמה של NUMA בין ממשק הרשת שמוגדר כברירת מחדל לבין המעבדים או הזיכרון.
שימוש בקישור בין רשתות שכנות (peering) של VPC ומאפשרים למערכת לבצע איזון עומסים של סשנים בין ממשקים
דרישות:
- צריך להגדיר קישור בין רשתות VPC שכנות (peering) בין רשתות ה-VPC של כרטיסי ה-gVNIC הראשי והמשני.
- כדי לאזן עומסים של סשנים של TCP בין ממשקי מקור אם שולחים לכתובת IP ויציאה יחידות של יעד, צריך את גרסת ליבת Linux 6.16.
- עומס העבודה עדיין יכול לעמוד בדרישות הביצועים שלכם כשמערך הרשת יוצר העברות זיכרון בין שקעים.
סקירה כללית
במקרים מסוימים, קשה לפצל את חיבורי הרשת בתוך אפליקציה או בין מופעים של אפליקציה. בתרחיש הזה, עבור חלק מהאפליקציות שפועלות במכונות וירטואליות מסוג A3U או A4 שלא רגישות להעברה בין NUMA או בין שקעים, יכול להיות נוח להתייחס לשני הממשקים כניתנים להחלפה.
אחת הדרכים לעשות את זה היא להשתמש ב-sysctl fib_multipath_hash_policy ובנתיב מרובה נתיבים:
PRIMARY_GW=192.168.1.1 # gateway of nic0
SECONDARY_GW=192.168.2.1 # gateway of nic1
PRIMARY_IP=192.168.1.15 # internal IP for nic0
SECONDARY_IP=192.168.2.27 # internal IP nic1
sysctl -w net.ipv4.fib_multipath_hash_policy=1 # Enable L4 5-tuple ECMP hashing
ip route add <destination-network/subnet-mask> nexthop via ${PRIMARY_GW} nexthop
via ${SECONDARY_GW}
פיצול התנועה בין רשתות
דרישות:
-
nic0ו-nic1במכונה הווירטואלית נמצאים ברשתות VPC ובתת-רשתות נפרדות. בדפוס הזה, כתובות היעד צריכות להיות מחולקות בין רשתות ה-VPC שלnic0ושלnic1.
סקירה כללית
כברירת מחדל, ליבת Linux יוצרת מסלולים לרשת המשנה של nic0 ולרשת המשנה של nic1, שינתבו את התנועה לפי היעד דרך ממשק הרשת המתאים.
לדוגמה, נניח שפרויקט nic0 משתמש ברשת VPC net1 עם תת-רשת subnet-a, ופרויקט nic1 משתמש ברשת VPC net2 עם תת-רשת subnet-b. כברירת מחדל, תקשורת עם כתובות IP של עמיתים ב-subnet-a תשתמש ב-nic0, ותקשורת עם כתובות IP של עמיתים ב-subnet-b תשתמש ב-nic1. לדוגמה, התרחיש הזה יכול לקרות עם קבוצה של מכונות וירטואליות עמיתות עם כרטיס רשת יחיד שמחוברות ל-net1 וקבוצה שמחוברת ל-net2.
שימוש ב-SNAT לבחירת ממשק המקור
דרישות:
-
CAP_NET_ADMINנדרש להגדרת כללי iptables הראשוניים, אבל לא להפעלת האפליקציה. - כשמשתמשים בכללים בשילוב עם כללי iptables אחרים או עם הגדרות ניתוב אחרות, צריך לבדוק אותם בקפידה.
שימו לב:
- הקישור של כרטיס ה-NIC נכון רק בזמן יצירת החיבור. אם השרשור עובר למעבד שמשויך לצומת NUMA אחר, החיבור יסבול מעונשים חוצי-NUMA. לכן הפתרון הזה הכי שימושי כשקיים מנגנון כלשהו לקשירת שרשורים לקבוצות ספציפיות של ליבות CPU.
- רק חיבורים שמקורם במחשב הזה יקושרו ל-NIC ספציפי. חיבורים נכנסים ישויכו ל-NIC שתואם לכתובת שאליה הם מיועדים.
סקירה כללית
בתרחישים שבהם קשה להשתמש במרחבי שמות של רשתות או לבצע שינויים באפליקציה, אפשר להשתמש ב-NAT כדי לבחור ממשק מקור. אפשר להשתמש בכלים כמו iptables כדי לשכתב את כתובת ה-IP של המקור עבור זרימה מסוימת, כך שתתאים לכתובת ה-IP של ממשק מסוים על סמך מאפיין של האפליקציה השולחת, כמו cgroup, משתמש או CPU.
בדוגמה הבאה נעשה שימוש בכללים שמבוססים על מעבד. התוצאה הסופית היא שזרימה שמקורה בשרשור שפועל בכל מעבד נתון מועברת על ידי gVNIC שמצורף לצומת NUMA התואם של אותו מעבד.
# --- Begin Configuration ---
OUTPUT_INTERFACE_0="enp0s19" # CHANGEME: NIC0
OUTPUT_INTERFACE_1="enp192s20" # CHANGEME: NIC1
CPUS_0=($(seq 0 55; seq 112 167)) # CHANGEME: CPU IDs for NIC0
GATEWAY_0="10.0.0.1" # CHANGEME: Gateway for NIC0
SNAT_IP_0="10.0.0.2" # CHANGEME: SNAT IP for NIC0
CONNMARK_0="0x1"
RT_TABLE_0="100"
CPUS_1=($(seq 56 111; seq 168 223)) # CHANGEME: CPU IDs for NIC1
GATEWAY_1="10.0.1.1" # CHANGEME: Gateway for NIC1
SNAT_IP_1="10.0.1.2" # CHANGEME: SNAT IP for NIC1
CONNMARK_1="0x2"
RT_TABLE_1="101"
# --- End Configuration ---
# This informs which interface to use for packets in each table.
ip route add default via "$GATEWAY_0" dev "$OUTPUT_INTERFACE_0" table "$RT_TABLE_0"
ip route add default via "$GATEWAY_1" dev "$OUTPUT_INTERFACE_1" table "$RT_TABLE_1"
# This is not required for connections we originate, but replies to
# connections from peers need to know which interface to egress from.
# Add it before the fwmark rules to implicitly make sure fwmark takes precedence.
ip rule add from "$SNAT_IP_0" table "$RT_TABLE_0"
ip rule add from "$SNAT_IP_1" table "$RT_TABLE_1"
# This informs which table to use based on the packet mark set in OUTPUT.
ip rule add fwmark "$CONNMARK_0" table "$RT_TABLE_0"
ip rule add fwmark "$CONNMARK_1" table "$RT_TABLE_1"
# Relax reverse path filtering.
# Otherwise, we will drop legitimate replies to the SNAT IPs.
sysctl -w net.ipv4.conf."$OUTPUT_INTERFACE_0".rp_filter=2
sysctl -w net.ipv4.conf."$OUTPUT_INTERFACE_1".rp_filter=2
# Mark packets/connections with a per-nic mark based on the source CPU.
# The `fwmark` rules will then use the corresponding routing table for this traffic.
for cpu_id in "${CPUS_0[@]}"; do
iptables -t mangle -A OUTPUT -m state --state NEW -m cpu --cpu "$cpu_id" -j CONNMARK --set-mark "$CONNMARK_0"
iptables -t mangle -A OUTPUT -m state --state NEW -m cpu --cpu "$cpu_id" -j MARK --set-mark "$CONNMARK_0"
done
for cpu_id in "${CPUS_1[@]}"; do
iptables -t mangle -A OUTPUT -m state --state NEW -m cpu --cpu "$cpu_id" -j CONNMARK --set-mark "$CONNMARK_1"
iptables -t mangle -A OUTPUT -m state --state NEW -m cpu --cpu "$cpu_id" -j MARK --set-mark "$CONNMARK_1"
done
# For established connections, restore the connection mark.
# Otherwise, we will send the packet to the wrong NIC, depending on existing
# routing rules.
iptables -t mangle -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark
# These rules NAT the source address after the packet is already destined to
# egress the correct interface. This lets replies to this flow target the correct NIC,
# and may be required to be accepted into the VPC network.
iptables -t nat -A POSTROUTING -m mark --mark "$CONNMARK_0" -j SNAT --to-source "$SNAT_IP_0"
iptables -t nat -A POSTROUTING -m mark --mark "$CONNMARK_1" -j SNAT --to-source "$SNAT_IP_1"