SSD Nodes Learn Hosting plans →
מדריכים Matt Connorמאת Matt Connor · עודכן 2026-08-07

איך עובדת רשת ב-Docker Compose: מדריך מעשי

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

מה Docker 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 ברמה העליונה בקובץ. ה-driver שלה הוא bridge, שהוא מתג וירטואלי בתוך המארח. כל מכולה מקבלת כתובת ברשת משנה פרטית, ותעבורה יוצאת מתורגמת לכתובת של המארח בדרכה החוצה.

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

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

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

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

docker compose exec web getent hosts db

פקודה זו מדפיסה שורה הדומה ל-172.18.0.2 db. אם היא לא מדפיסה דבר, שני השירותים אינם נמצאים על אותה רשת.

הטעות שכולם עושים לפחות פעם אחת היא שימוש ב-localhost בתצורת היישום. בתוך מכולה, localhost הוא המכולה עצמה, לא המארח (host) ולא השירות האחר. לקוחות 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. חלק ה-host הוא שם השירות.

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

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

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

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

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

מתי כדאי להשתמש ב-network_mode host ומה המחיר הכרוך בכך

מצב host מבטל את מרחב השמות (namespace) של הרשת של המכולה ומאפשר לתהליך להשתמש בממשקי הרשת של המארח ישירות.

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

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

המחירים לכך ברורים.

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

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

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

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

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

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

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

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

docker network create edge

לאחר מכן, הגדירו אותה כחיצונית (external) בכל פרויקט. בצד ה־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 תחת הגדרת רשת מרחיקה לכת ומסירה את הניתוב שלה לעולם החיצון לחלוטין. זוהי ברירת מחדל טובה עבור מסד נתונים, עם עלות אחת שכדאי להכיר לפני ההגדרה: מכולה ברשת פנימית אינה יכולה להוריד דבר, לכן תהליך entrypoint שמריץ apt-get update או pip install בעת העלייה ייתקע ולבסוף ייכשל עקב timeout.

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

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

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

sudo ufw status
curl http://203.0.113.10:8080

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

הפתרון המהיר הוא לפרסם רק היכן שצריך:

    ports:
      - "127.0.0.1:8080:80"

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

כיצד לבצע ניפוי שגיאות בארבע פקודות

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

docker network inspect shop_default

הבלוק Containers מציג את כל המכולות המחוברות עם הכתובת שלהן. שירות שאינו מופיע ברשימה זו נמצא ברשת אחרת, במצב host, או שאינו רץ.

בדקו את פתרון השמות (name resolution) ממכולה זמנית המחוברת לאותה רשת, כך שלא תזדקקו לכלי עבודה בתוך האימג'ים שלכם:

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. המקרה האחרון נפוץ בשרתי פיתוח, והתיקון נמצא בכתובת ה-bind של היישום, לא ב-Docker.

כשל נוסף שנראה כמו באג ב-Docker: אם מכולות יכולות לתקשר זו עם זו אך אינן מצליחות להגיע למכונה ברשת המשרדית או ב-VPN, ייתכן שסאב-נט של Docker חופף לרשת ההיא. כברירת מחדל, Docker מקצה כתובות החל מ-172.17.0.0/16 ומעלה. העבירו את ה-pool ב-/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, כיוון שמכולה במצב host אינה נמצאת על אף רשת Docker ואינה יכולה לבצע רזולוציה לשמות שירותים.

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

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

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

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

כיצד אחבר מכולות משני קובצי Compose שונים?

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

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

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