SSD Nodes Learn 8GB RAM — $66/שנה
מדריכים Matt Connorמאת Matt Connor · עודכן 2026-08-01

רשתות Docker Compose: DNS, host ופתיחת פורטים

למדו כיצד Compose יוצר רשת bridge, איך DNS לפי שם שירות עובד, מתי host mode משתלם, איך לשתף רשת בין פרויקטים ולמה פורט מפורסם עוקף UFW.

מה Compose בונה לפני שהיישום שלך מופעל

רשת ה-Docker Compose מתחילה בכלל אחד: docker compose up יוצר רשת פרטית עבור הפרויקט, מחבר אליה כל שירות ומאפשר לשירותים האלה להגיע זה לזה באמצעות שם השירות. אין צורך לכתוב אפילו שורת networks: אחת כדי לקבל זאת. רוב הבלבול בנוגע לרשתות Compose נובע מכך שלא יודעים שהגדרת ברירת המחדל כבר קיימת.

הנה קובץ קטן. שמור אותו בשם compose.yaml בספרייה בשם shop.

services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example

הפעל אותו ובדוק מה Docker יצר:

docker compose up -d
docker network ls

כעת הרשימה כוללת רשת בשם shop_default. Compose נותן לה את השם <project>_default, ושם הפרויקט מוגדר כברירת מחדל כשם הספרייה באותיות קטנות. ניתן לשנות זאת באמצעות docker compose -p myproject up -d או באמצעות name: myproject ברמה העליונה בקובץ. מנהל ההתקנים שלה הוא bridge, שהוא מתג וירטואלי בתוך המארח. כל מכל מקבל כתובת ברשת משנה פרטית, ותעבורה יוצאת מתורגמת לכתובת המארח בעת היציאה.

docker compose down מוחק את הרשת הזאת. לכן מכל ישן מפרויקט קודם עלול להשאיר רשת פתוחה: Docker מסרב ומציג error while removing network: network shop_default has active endpoints, והפתרון הוא לעצור או להסיר את המכל שעדיין מחובר אליה.

אם Compose חדש לך, כדאי לקרוא תחילה את מבנה קובץ Compose ופקודות מחזור החיים, משום שכל ההמשך מניח שאתה יודע להפעיל ולעצור פרויקט.

DNS לפי שם שירות הוא החלק שמתחילים מפספסים

בכל רשת שהמשתמש הגדיר, Docker מפעיל שרת DNS מוטמע שכל מכל מכיר ב-127.0.0.11. השרת פותר שמות שירות לכתובות המכלים הנוכחיות. לכן web מגיע למסד הנתונים בשם המארח db, ביציאה 5432, ללא כל תצורה.

docker compose exec web getent hosts db

הפקודה מציגה שורה כגון 172.18.0.2 db. אם לא מוצג דבר, שני השירותים אינם נמצאים באותה רשת.

הטעות שכמעט כולם עושים פעם אחת היא שימוש ב-localhost בתצורת היישום. בתוך מכל, localhost מציין את המכל עצמו, ולא את המארח ולא את השירות האחר. לקוחות Postgres מדווחים על כך בבירור:

could not connect to server: Connection refused
	Is the server running on host "localhost" (127.0.0.1) and accepting
	TCP connections on port 5432?

מחרוזת החיבור צריכה להיות postgresql://postgres:example@db:5432/postgres. החלק המציין את המארח הוא שם השירות.

יש שני פרטים שיחסכו זמן בהמשך. שמות נפתרים לפי מה שפועל כעת, לכן docker compose up -d --scale web=3 מחזיר שם אחד עם שלוש כתובות, ולקוח ששומר רשומות DNS במטמון ללא הגבלת זמן יישאר מקושר למכל שכבר אינו פעיל. בנוסף, לרשת הישנה bridge, שבה משתמשת פקודת docker run ללא --network, אין פתרון שמות כלל. לכן ההנחיות משנת 2016 בנושא קישורי מכלים אינן תואמות למה שאתם רואים.

אין צורך ב-ports: כדי לחבר בין שני שירותים

ports: מפרסם יציאה של קונטיינר במארח. הוא מיועד לתעבורת רשת שמגיעה מחוץ ל-Docker. אין לו קשר לתעבורה בין שירותים, שכבר פועלת בכל טווח היציאות ברשת של הפרויקט.

לכן ה-ports: - "5432:5432" שאנשים רבים מוסיפים לשירות מסד הנתונים שלהם אינו מועיל ואף גורם נזק ממשי: הוא חושף את Postgres בממשק הציבורי של השרת. מחקו אותו. אם אתם רוצים לאפשר גישה אליו מהמחשב הנייד לצורך הגירה, קשרו אותו לממשק הלולאה החוזרת באמצעות "127.0.0.1:5432:5432", וגשת אליו דרך מנהרת SSH. ההבדל בין שקע שמאזין, יציאה שפורסמה וכלל חומת אש מוסבר באופן הפעולה של יציאות ושירותים מאזינים ב-Linux.

expose: הוא תיעוד בלבד במסגרת Compose. הוא אינו פותח דבר, משום ששום דבר לא נחסם בין קונטיינרים באותה רשת.

מתי network_mode host מתאים, ומה המחיר שלו

מצב host מבטל את מרחב שמות הרשת של הקונטיינר ומאפשר לתהליך להשתמש ישירות בממשקי הרשת של המארח.

services:
  probe:
    image: alpine:3.20
    network_mode: host
    command: sleep infinity

יש לכך סיבות מוצדקות. תהליך שצריך לראות תעבורת broadcast או multicast ברשת המקומית, כגון גילוי התקנים עבור שרת מדיה או רכזת אוטומציה ביתית, אינו יכול לראות אותה מאחורי bridge, מכיוון שה־bridge אינו מעביר תעבורה זו לקונטיינר. סוכן ניטור שקורא את מוני ממשקי הרשת של המארח זקוק לממשקי הרשת של המארח. בנוסף, מדלגים על שלב תרגום הכתובות, דבר שחשוב בקצבי מנות גבוהים.

המחירים ברורים.

ports: מפסיק לפעול. Docker מזהיר שיציאות שפורסמו מתבטלות בעת שימוש במצב רשת host, והקונטיינר נקשר לכל כתובת ולכל יציאה שהתהליך שלו מבקש. שני קונטיינרים במצב host שמנסים להשתמש ביציאה 8080 מתנגשים, והשני מפסיק לפעול עם bind: address already in use.

פתרון שמות לפי שם שירות אינו זמין בשני הכיוונים. הקונטיינר אינו נמצא ברשת הפרויקט, ולכן אינו יכול לפתור את db, ושאר השירותים אינם יכולים לפתור אותו. הוא מגיע אליהם רק דרך יציאות שפורסמו במארח, בדרך כלל ב־127.0.0.1.

הבידוד אינו קיים. תהליך שנקשר אל 0.0.0.0 בתוך קונטיינר במצב host מאזין בכל ממשקי הרשת של השרת, כולל הממשק הציבורי, בדיוק כמו חבילה שהותקנה באמצעות apt. יש לכך יתרון אחד: תעבורה זו עוברת בנתיב הקלט הרגיל, ולכן כללי UFW חלים עליה. הדבר אינו נכון לגבי יציאות שפורסמו.

מצב host הוא תכונה של Docker Engine ב־Linux. Docker Desktop תומך בו רק החל מגרסה 4.34, ורק לאחר שמפעילים אותו. בנוסף, לקונטיינרים יש מגבלות: הם אינם יכולים להיקשר לכתובות IP של המארח, ונתמכים רק TCP ו־UDP. אם מחצית מהצוות משתמשת בשרתי Linux ומחצית ב־Docker Desktop, יש לצפות שאותו קובץ יתנהג באופן שונה.

השתמשו במצב host כאשר יש צורך בממשקי הרשת של המארח. אל תשתמשו בו כדי לתקן בעיית חיבור, מכיוון שבדרך כלל הוא מחליף בעיה אחת בבעיה מורכבת יותר.

חיבור בין שני פרויקטים של Compose באמצעות רשת חיצונית

רשת שנוצרה על ידי פרויקט אחד אינה גלויה לפרויקט אחר. לכן reverse proxy ב-proxy/compose.yaml אינו יכול לראות אפליקציה ב-app/compose.yaml, גם כאשר שניהם נמצאים באותו שרת. הפתרון הוא רשת שאף אחד מהפרויקטים אינו מנהל.

יש ליצור אותה פעם אחת, באופן ידני:

docker network create edge

לאחר מכן יש להצהיר עליה כרשת חיצונית בכל אחד מהפרויקטים. בצד ה-proxy:

services:
  proxy:
    image: traefik:v3.1
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - edge

networks:
  edge:
    name: edge
    external: true

בצד האפליקציה:

services:
  app:
    image: nginx:1.27
    networks:
      - edge
      - internal
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    networks:
      - internal

networks:
  edge:
    name: edge
    external: true
  internal:

external: true מורה ל-Compose להתחבר לרשת קיימת במקום ליצור רשת חדשה, ולהשאיר אותה במקומה לאחר docker compose down. למפתח name: הנפרד יש חשיבות רבה יותר מכפי שנדמה: בלעדיו Compose מחפש רשת ששמה המדויק הוא edge, ובעזרתו אפשר לתת לרשת שם אחד בקובץ ושם אחר במארח.

אם הרשת אינה קיימת, Compose מסרב להפעיל את השירותים ומדווח שהרשת הוגדרה כרשת חיצונית אך לא נמצאה. יש ליצור אותה תחילה.

שימו לב לאופן שבו קובץ האפליקציה משתמש ב-internal. מסד הנתונים נמצא רק ברשת המקומית של הפרויקט, ולכן ה-proxy אינו יכול להגיע אליו ורק app יכול לעשות זאת. הוספת internal: true תחת רשת מרחיקה לכת יותר ומסירה ממסד הנתונים גם את נתיב הגישה לעולם החיצון. זוהי ברירת מחדל טובה למסד נתונים, אך יש להכיר במחיר שלה לפני שמפעילים אותה: קונטיינר ברשת פנימית אינו יכול להוריד דבר, ולכן נקודת כניסה שמפעילה apt-get update או pip install בעת ההפעלה תיתקע ולבסוף תיכשל עקב פסק זמן.

להגדרה מלאה עם כללי ניתוב ותעודות, ראו הפעלת כמה אפליקציות מאחורי מופע Traefik אחד.

יציאות שפורסמו עוקפות את UFW

זהו החלק ברשת Compose שהופך לאירוע אבטחה. אתם מפרסמים יציאה, מוודאים ש-UFW פעיל וחוסם הכול מלבד SSH, ועדיין ניתן לגשת לשירות מהאינטרנט.

sudo ufw status
curl http://203.0.113.10:8080

UFW מציין שהיציאה חסומה. ה-curl עדיין מחזיר את הדף. אין כאן תקלה. Docker כותב ישירות אל iptables כללי תרגום כתובות והעברה משלו. תעבורה אל יציאת container שפורסמה מועברת אל ה-container במקום להימסר ל-host. לכן היא אינה עוברת בשרשרת ש-UFW מנהל עבור תעבורה המיועדת מקומית. הכללים של Docker נבדקים גם לפני הכללים של UFW.

התיקון הקצר הוא לפרסם רק במקומות שבהם הדבר נדרש:

    ports:
      - "127.0.0.1:8080:80"

כך הצד של ה-host נקשר ל-loopback. לכן ניתן לגשת אל היציאה מהשרת עצמו ומעל מנהרת SSH, אך לא משום מקום אחר. מקמו את נקודת הכניסה הציבורית מאחורי reverse proxy שמפרסם במכוון את 80 ואת 443. ההסבר המלא, כולל שרשרת DOCKER-USER למקרים שבהם חייבים לסנן יציאה שפורסמה, נמצא ב-מדוע Docker מפרסם ישירות מעבר ל-UFW וכיצד לתקן זאת.

כיצד לאתר את הבעיה באמצעות 4 פקודות

תחילה בדקו באיזו רשת כל מכולה נמצאת בפועל:

docker network inspect shop_default

הפלט של Containers מציג כל מכולה מחוברת ואת הכתובת שלה. שירות שאינו מופיע ברשימה מחובר לרשת אחרת, פועל במצב מארח או אינו פועל.

בדקו פתרון שמות ממכולה זמנית המחוברת לאותה רשת. כך אינכם זקוקים לכלי אבחון בתוך התמונות שלכם:

docker run --rm --network shop_default busybox nslookup db
docker run --rm --network shop_default busybox nc -zv db 5432

כישלון של nslookup מצביע על בעיה בפתרון השמות או בחברות ברשת. הצלחה של nslookup לצד כישלון של nc פירושה שהשירות פועל, אך אינו מאזין ביציאה זו, או שהוא מאזין ב-127.0.0.1 בתוך המכולה שלו במקום ב-0.0.0.0. מצב זה נפוץ בשרתי פיתוח. הפתרון נמצא בכתובת הקישור של היישום, ולא ב-Docker.

יש כשל נוסף שנראה כמו באג ב-Docker. אם המכולות יכולות לתקשר זו עם זו, אך אינן יכולות להגיע למחשב ברשת המשרד או ה-VPN, כנראה שטווח רשת המשנה של Docker חופף לרשת זו. כברירת מחדל, Docker מקצה כתובות החל מ-172.17.0.0/16. העבירו את מאגר הכתובות ב-/etc/docker/daemon.json:

{
  "default-address-pools": [
    { "base": "10.200.0.0/16", "size": 24 }
  ]
}

לאחר מכן הריצו את sudo systemctl restart docker וצרו מחדש את הרשתות המושפעות, מכיוון שרשת קיימת שומרת את רשת המשנה שהוקצתה לה בעת יצירתה.

FAQ

מדוע הקונטיינרים שלי אינם יכולים להגיע זה לזה באמצעות שם השירות?

הם אינם מחוברים לאותה רשת. Compose מחבר כל שירות ל-<project>_default באופן אוטומטי, אך ברגע שמוסיפים לשירות רשימת networks:, הרשימה הופכת לקבוצת הרשתות המלאה שלו, ורשת ברירת המחדל אינה נכללת עוד באופן משתמע. הריצו את docker network inspect <network> ובדקו ששני הקונטיינרים מופיעים בבלוק Containers. בדקו גם שאף אחד מהשירותים אינו משתמש ב-network_mode: host, מכיוון שקונטיינר במצב מארח אינו מחובר לרשת Docker כלשהי ואינו יכול לפתור שמות שירותים.

האם עליי לפרסם יציאות כדי ששירות אחד יוכל להגיע לשירות אחר?

לא. ברשת Compose, כל יציאה של כל קונטיינר נגישה לקונטיינרים האחרים באותה רשת. ports: מיועד רק לחשיפת קונטיינר לתעבורת רשת שמגיעה מחוץ ל-Docker, ואילו expose: משמש לתיעוד. פרסום יציאה של מסד נתונים הוא נוהג נפוץ ויקר, מכיוון שהוא מציב את מסד הנתונים בממשק הציבורי של השרת.

מה ההבדל בין תקשורת רשת במצב bridge לבין תקשורת רשת במצב host?

במצב bridge הקונטיינר מקבל מרחב שמות רשת וכתובת משלו במתג וירטואלי, כולל פתרון שמות אוטומטי בין קונטיינרים ותרגום של תעבורה יוצאת. במצב host הקונטיינר משתמש ישירות במחסנית הרשת של המארח: אין כתובת נפרדת, אין פתרון שמות לפי שם שירות, אין פרסום יציאות ואין בידוד ממאזינים אחרים של המארח. bridge הוא מצב ברירת המחדל והוא הבחירה הנכונה, אלא אם התהליך זקוק לממשקי הרשת של המארח.

כיצד מחברים קונטיינרים משני קובצי Compose שונים?

צרו רשת משותפת באמצעות docker network create edge, ולאחר מכן הצהירו עליה בשני הקבצים באמצעות external: true וחברו אליה את השירותים שצריכים לתקשר. Compose לא ייצור אותה ולא ימחק אותה. אם תדלגו על שלב היצירה, Compose יסרב להתחיל וידווח שהרשת הוצהרה כרשת חיצונית אך לא נמצאה.

מדוע ניתן להגיע לקונטיינר שלי מהאינטרנט כאשר UFW חוסם את היציאה?

מכיוון שיציאה שפורסמה מטופלת באמצעות כללי העברת התעבורה ש-Docker מוסיף ל-iptables. כללים אלה נבדקים לפני הכללים של UFW, ותעבורה מועברת ממילא אינה עוברת בשרשרת ש-UFW מסנן. קשרו את צד המארח ל-loopback באמצעות "127.0.0.1:8080:80", והציבו כל שירות ציבורי מאחורי reverse proxy ביציאות 80 ו-443.