הקונטיינר לא מגיע ל-API הזמין רק דרך VPN? עשו פרוקסי במארח
המנהרה חיה במארח, ולכן לקונטיינר אין נתיב לכתובת הפרטית. פרסמו פרוקסי הפוך צר וקראו לו בשם host.docker.internal.
שירות ה-Compose שלכם נכשל בזמן המתנה מול API שהדפדפן על אותה מכונה פותח בלי שום תלונה. ההבדל הזה הוא הרמז: הבקשה שעובדת יוצאת ממארח עם מנהרת VPN, והבקשה שנתקעת יוצאת מקונטיינר עם מחסנית הרשת שלו. דבר בתוך הקונטיינר לא מכיר את הנתיבים של המנהרה, ולכן היעד הפרטי נשאר בלתי נגיש איך שלא ייכתב השם. התיקון הוא קפיצה מכוונת דרך המארח, לא טבלת ניתוב חכמה יותר.
התחילו בלמה שני המסלולים שונים, ואז החליטו היכן המנהרה צריכה לחיות.
למה המארח עובד והקונטיינר לא
העלאת המנהרה מעניקה למארח ממשק, סט נתיבים ובדרך כלל רשומת פותר שמספק ה-VPN. קונטיינר לא יורש מזה דבר: הוא מתחיל עם ממשק אחד על הגשר של המנוע ונתיב ברירת מחדל שמצביע על אותו גשר. נתיבים המותקנים במארח בלתי נראים לכל מה שרץ בתוך מרחב השמות הזה.
לכן התסמינים נראים לא עקביים ולא סתם שבורים. המארח מפענח את השם הפרטי ומתחבר, כי גם השם וגם הנתיב קיימים שם. הקונטיינר שולח את החבילה לכתובת פרטית ששום דבר בנתיב הגשר לא יכול להגיע אליה, והבקשה נכשלת בזמן המתנה. הוספת השם לקובץ hosts של הקונטיינר לא משנה דבר, כי הכתובת עצמה אינה נגישה מהמקום שבו יושב הקונטיינר.
הגיעו למארח מתוך הקונטיינר
תהליך בקונטיינר שמחייג אל localhost מחייג אל הקונטיינר. Docker Desktop מפרסם את host.docker.internal כשם שמצביע חזרה אל המכונה שמריצה את המנוע; מנועי Linux משאירים אותו לא מוגדר אלא אם השירות מבקש אותו, וזה בדיוק מה שהמיפוי שלמטה עושה. עם זה, היישום מכוון לשם ולפורט במקום לכתובת הפרטית.
services:
app:
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
INTERNAL_API_HOST: "host.docker.internal:8666"המנהרה נשארת בצד המארח של הקפיצה, והפורט במשתנה הזה הוא זה שהפרוקסי יאזין עליו. קונטיינר שמאבד את המנהרה נכשל עכשיו בקפיצה ידועה אחת במקום לנסות נתיב שמעולם לא היה לו.
בחרו היכן המנהרה חיה
שלוש צורות אפשריות לשירות Compose שזקוק ל-API פרטי, והן נבדלות בכך איזה תהליך מחזיק במנהרה. לקוח VPN יכול לרוץ בתוך תמונת הקונטיינר, מה שמחייב חשיפת התקן מנהרה והענקת יכולת ניהול הרשת שנלווית לכך. היישום יכול לחלוק את מרחב השמות של הרשת עם קונטיינר מנהרה נפרד, כך ששני התהליכים רואים מחסנית אחת וטבלת ניתוב אחת. או שהמנהרה יכולה להישאר במארח, עם מעביר צר מהמארח אל הקונטיינר.
השתיים הראשונות גוררות קובצי פרופיל, התנהגות התחברות מחדש ומדיניות פותר אל תוך הפרויקט, והופכות כל שירות בקובץ לתלוי בבריאות המנהרה. זה יותר מדי הצמדה לקריאת API אחת. עבור מחסנית פיתוח שקוראת נקודת קצה פנימית אחת, המעביר בצד המארח הוא השינוי הקטן ביותר שעדיין שומר על האישורים במקום שבו צוות הפלטפורמה מנהל אותם.
פרסמו נתיב צר אחד במארח
התחילו ממה שהמעביר יורש: הריצו אותו בחשבון שמחזיק במנהרה, והשקעים היוצאים שלו יקבלו את הנתיבים של המארח ואת הפותר של המארח. בלוק location אחד ב-nginx מכסה נתיב אחד.
server {
listen 8666;
location /search/ {
proxy_pass http://10.20.30.40:8090/;
proxy_set_header Host internal-api.example;
proxy_read_timeout 60s;
}
}התייחסו לכתובת ולפורט כאל ממלאי מקום והציבו את הזוג שה-API באמת מאזין עליו. כל upstream נוסף זוכה לבלוק location משלו, וכל בלוק צריך להתיר רק את הנתיבים שהשירות הזה קורא, כדי שהפרוקסי לא יהפוך לדלת כללית אל הרשת הפרטית. קשרו את המאזין לכתובת הגשר עבור לקוחות שמגיעים דרך host-gateway, ל-loopback רק כשהלקוח חולק את מרחב השמות של הרשת של המארח, ולעולם לא ל-0.0.0.0. בחרו גם פורט שעדיין פנוי: מספר שכבר תפוס משאיר את הפרוקסי לא מאזין בעוד הקונטיינר ממשיך להיכשל בזמן המתנה, וזה לא ניתן להבחנה מהתקלת הרשת שבה התחלתם.
פורטים, שמות וההחלטה על TLS
פענוח שמות מתפצל כמו הניתוב. החיפושים הציבוריים של הקונטיינר עוברים דרך הפותר של המנוע, ולכן שם פרטי שמתפענח במארח לא יתפענח בתוך הקונטיינר. הפרוקסי הוא הרכיב שמפענח את השם הפרטי, כי הוא יורש את הפותר של המארח.
TLS הוא ההחלטה שנדחית ואז מסבירה את הכשל הבא. אם הפרוקסי מעביר בייטים ללא שינוי, הקונטיינר רואה את התעודה של ה-API עצמו וזקוק ל-CA הפנימי במאגר האמון שלו. אם הפרוקסי מסיים TLS, האמון הזה עובר למארח והיישום מתחיל לבנות קישורים מכותרת ה-host שהוא מקבל. בחרו סידור אחד והחילו אותו על כל משאב, כי הפניה שמנפיק האחר מצביעה על שם מארח שאין לקונטיינר דרך לפענח.
אמתו מתוך השירות
שלחו את הבקשה מהקונטיינר, לא מהמארח: curl פשוט אל נתיב הפרוקסי, ואז בדיקת הבריאות של היישום עצמו. כשהפרוקסי עונה אבל היישום עדיין נכשל, השוו שלושה דברים: כתובת הבסיס שהוא בונה, כל משתנה פרוקסי בסביבה שלו, וה-CA שהוא סומך עליו. כולם נמצאים בסביבה ובלוגים של הקונטיינר, ותיקון כל אחד מהם משאיר את ה-VPN בשקט.
כתבו את הסידור שנבחר ל-README של הפרויקט כל עוד הוא טרי. אחרת האדם הבא שיתקל בפסק זמן הזה יבזבז את אותה אחר הצהריים בגילוי מחדש שהמנהרה חיה במארח.