- להתקין Docker Desktop על Windows (דרך WSL2) או על Mac, ולוודא שהוא עובד עם
docker run hello-world— בלי להיתקע בשלב ההתקנה. - להריץ image אמיתי מ-Docker Hub (
nginx) ולפתוח אותו ב-browser דרךdocker run -p 8080:80 nginx— לראות דף אמיתי בלי לכתוב שורת קוד. - להפעיל את 6 הפקודות היומיומיות שמכסות 90% מהעבודה:
docker run,docker ps/ps -a,docker logs,docker stop,docker rm,docker exec -it … sh— ולהבין מה כל אחת מציגה בפלט. - להסביר את כיוון ה-port mapping (host:container) ולמה
-p 8080:80אומר "האזן על 8080 אצלי, הפנה ל-80 בתוך ה-container" — ולמה ה-container חייב להאזין על0.0.0.0ולא על127.0.0.1. - לקבוע אם Docker Desktop חינמי במקרה שלכם לפי הסף הרשמי (פחות מ-250 עובדים וגם פחות מ-$10M הכנסה שנתית) — ולזהות חלופה חינמית לכל שימוש מסחרי במידת הצורך.
- פרקים קודמים: פרק 1 — למה Docker, מ-'רץ אצלי במחשב' ל-'רץ בכל מקום'. חובה. בלי ההבחנה image/Container מהפרק ההוא, הפקודות כאן ירגישו אקראיות.
- מחשב: Windows 10/11 (עם WSL2 מופעל) או macOS (גרסה אחרונה — Monterey, Ventura, Sonoma ומעלה). שתי הפלטפורמות מכוסות בפרק. Linux מוסבר בקצרה בסוף.
- נוחות בסיסית ב-Terminal: להריץ פקודה, לנווט בין תיקיות עם
cd, לערוך קובץ טקסט. לא צריך לכתוב סקריפטים, רק להריץ. - חיבור אינטרנט יציב: Docker Desktop שוקל 1-2 GB להורדה. images שנוריד במהלך הפרק (hello-world, nginx) הם עשרות MB בודדים, אבל עדיף WiFi חזק.
- חשבון GitHub (חינמי): לא חובד לפרק הזה, אבל נדרש לפרק 6 (ghcr.io). אם עדיין אין לכם — רגע אחד על github.com ואתם מסודרים.
- כונן פנוי: 10 GB לפחות. Docker Desktop יוצר VMs ו-images, והוא לא אוהב דיסקים מלאים.
הפרק הזה הוא הסף המעשי של הקורס. אחרי שתסיימו אותו, "רץ אצלי במחשב" יהפוך ל"רץ גם אצלי במחשב, בלי שאני אכתוב שורת קוד". בפרק 3 כבר נארוז את האפליקציה שלכם לתוך image, ובפרק 6 נעלה אותו לשרת אמיתי. אבל הבסיס מתחיל כאן, בפקודה אחת של docker run.
התקנה על Windows — Docker Desktop דרך WSL2
על Windows, Docker Desktop לא רץ ישירות על הקרנל של Windows. הוא רץ בתוך מכונה וירטואלית קטנה שמבוססת על WSL2 (Windows Subsystem for Linux, גרסה 2) — שזה בעצם kernel של לינוקס אמיתי שמיקרוסופט בנו, ומשתלב בחלונות. למה זה חשוב? כי containers מעצם הגדרתם צריכים kernel של לינוקס (או Windows containers, אבל זה סיפור אחר לגמרי). בלי WSL2, Docker Desktop היה צריך להריץ VM כבד — איטי, זולל זיכרון, וחוסם אתכם מלהריץ קוד לינוקסאי מקומי. עם WSL2, ה-container שלכם רץ באותה סביבה שבה הוא היה רץ על שרת אמיתי, ואתם מקבלים ביצועים טובים בהרבה.
שלב 1: לוודא ש-WSL2 מותקן ומעודכן
ב-Windows 10/11 המודרני, WSL2 כבר מותקן, אבל לא תמיד בגרסה האחרונה. Docker Desktop דורש WSL בגרסה 2.1.5 ומעלה (עדיף עדכני). פתחו PowerShell או Terminal של Windows כמנהל (Run as Administrator) והריצו:
wsl --version
wsl --update
הפלט הראשון יראה לכם את הגרסה הנוכחית (למשל, "WSL version: 2.3.26.0"). השני יוריד ויתקין את הגרסה האחרונה. אחרי עדכון, הפעילו מחדש את Windows — לא מוותרים על זה. WSL2 הוא רכיב kernel; שינויים בו דורשים אתחול.
ודאו שהפצת לינוקס ברירת-מחדל מוגדרת ל-WSL2:
wsl --set-default-version 2
wsl --list --verbose
הפלט צריך להראות את ההפצה שלכם (Ubuntu היא ברירת המחדל) עם עמודת "VERSION" שמכילה "2". אם מופיע "1" — המירו עם wsl --set-version <DistroName> 2 (למשל, wsl --set-version Ubuntu 2).
שלב 2: הורדה והתקנה של Docker Desktop
היכנסו ל-docker.com/products/docker-desktop ולחצו "Download for Windows". הקובץ הוא Docker Desktop Installer.exe, שוקל כ-1.2 GB. הריצו אותו. ההתקנה לוקחת 2-5 דקות ותבקש מכם לסגור את כל החלונות הפתוחים בסוף (זה תוקף רכיבי מערכת, לא רק קבצים רגילים).
בסיום ההתקנה תתבקשו להפעיל מחדש את Windows (שוב). זה הצעד האחרון שדורש אתחול; אחרי זה לא תצטרכו להפעיל שוב בגלל Docker.
שלב 3: ההגדרות הראשונות ב-Docker Desktop
אחרי ההפעלה מחדש, Docker Desktop יפתח אוטומטית. בהפעלה הראשונה הוא יבקש:
- Service agreement — אשרו (זה תנאי השימוש, לא מסמך משפטי שצריך לקרוא).
- האם להשתמש ב-WSL2 backend (מומלץ) או Hyper-V backend (ישן) — בחרו WSL2. זה ה-default המודרני, וזה מה שעובד הכי טוב.
- אילו הפצות WSL לשלב — סמנו את Ubuntu (או כל הפצה שאתם משתמשים בה).
תחת Settings → Resources → WSL Integration, ודאו שההפצה שלכם מסומנת. זה מה שמאפשר לכם להריץ docker מתוך ה-terminal של ההפצה (Ubuntu) בלי קונפיגורציה נוספת.
שלב 4: אימות ראשון
פתחו את ה-terminal של אובונטו (או PowerShell, אם הוספתם את Docker CLI ל-PATH), והריצו:
docker --version
docker run hello-world
הפלט הראשון יראה משהו כמו Docker version 27.x.x, build xxxxx — זה אומר שה-CLI מותקן. הפקודה השנייה תוריד image קטן בשם hello-world מ-Docker Hub, תריץ אותו, ותדפיס הודעת הצלחה. אם אתם רואים "Hello from Docker!" — סיימתם. המערכת שלכם מוכנה.
אם עוד לא התקנתם: פתחו docker.com/products/docker-desktop, הורידו את הגרסה ל-OS שלכם, והתקינו. אם כבר התקנתם: פתחו terminal והריצו docker run hello-world. תוצאה צפויה: הודעה שמתחילה ב-"Hello from Docker!" ומסבירה בכמה משפטים מה קרה (Docker יצר קשר עם ה-daemon, משך את ה-image, הריץ אותו, והדפיס). אם אתם רואים את ההודעה — Docker מותקן ופועל. אם לא — חיזרו לסעיף הרלוונטי.
מלכודת מיקום הקבצים ב-WSL2 — לאן לשמור את הפרויקט
זו המלכודת הכי שכיחה ב-Windows, והיא גורמת לתלונה הנפוצה ביותר: "Docker איטי לי, ה-build לוקח 10 דקות במקום דקה". הפתרון פשוט, אבל צריך להכיר אותו לפני שמתחילים לעבוד.
שתי מערכות קבצים, שתי ביצועים
ב-WSL2 יש שתי מערכות קבצים:
- מערכת הקבצים של Windows (C:) — נגישה מ-WSL דרך
/mnt/c/.... הקבצים שלכם נמצאים שם, והם נראים גם מ-WSL. - מערכת הקבצים של לינוקס בתוך WSL2 — נגישה דרך
\\wsl$\Ubuntu\home\<user>\...מ-Windows. הקבצים נשמרים ב-VHD של WSL2.
כשאתם עורכים קבצים בפרויקט שמקושר מ-WSL ל-Windows (למשל, פתחתם VSCode על C:\Users\me\myapp ומריצים docker build מתוך WSL שמסתכל על /mnt/c/Users/me/myapp), כל קריאה/כתיבה צריכה לעבור דרך גשר בין שתי מערכות הקבצים. זה מאוד איטי — במיוחד עבור file watching (המנגנון שבודק "האם קובץ השתנה?"). זה גם יוצר race conditions משונים שקשה לאבחן.
הכלל הפשוט
שמרו את קבצי הפרויקט בתוך מערכת הקבצים של לינוקס ב-WSL2, לא על C:. כלומר, עבדו עם ~/projects/myapp (או כל תיקייה בתוך ה-home של המשתמש באובונטו), ולא עם C:\Users\me\projects\myapp.
הדרך המעשית:
- פתחו את terminal של אובונטו.
- צרו תיקייה:
mkdir -p ~/projects/myapp && cd ~/projects/myapp. - את קוד הפרויקט שלכם — העתיקו לכאן (או
git clone <url>). - פתחו את VSCode דרך WSL:
code .(הפקודה code מותקנת אוטומטית עם הרחבת "Remote - WSL" של VSCode). - הריצו
docker buildו-docker runמה-terminal של אובונטו. הכל ירוץ במהירות מלאה.
אבל אני רגיל לעבוד ב-C:
אם אתם חייבים לעבוד ב-C: (למשל, הפרויקט מסונכרן עם OneDrive, או יש לכם סקריפטים שמתעדכנים מ-Windows), עדיין אפשר — פשוט צפו לביצועים נמוכים יותר. ה-build יעבוד, ה-image ייווצר, ה-container ירוץ, אבל הזמן יהיה ארוך פי 5-10. ברגע שתרגישו "Docker זה איטי", תדעו לבדוק קודם את מיקום הקבצים — וברוב המקרים זה יפתור את התלונה.
אם אתם על Windows, פתחו את terminal של אובונטו והריצו pwd. אם התשובה היא משהו כמו /home/yourname או /home/yourname/projects/myapp — אתם במקום הנכון. אם התשובה היא /mnt/c/Users/... — אתם עובדים מכונן C, וכדאי להזיז את הפרויקט לתוך ~/ לפני שממשיכים. תוצאה צפויה: נתיב שמתחיל ב-/home/ או /root/ בתוך WSL.
התקנה על Mac — Docker Desktop והחלופות הקלות
על Mac ההתקנה פשוטה יותר, כי macOS מבוסס על Unix ו-Docker Desktop רץ בתוך VM קל (המקבילה ל-WSL2 ב-Windows, אבל אפשר להתקין אותו בלי להפעיל שום רכיב מערכת).
אפשרות 1: Docker Desktop (הברירה הסטנדרטית)
הורידו מ-docker.com/products/docker-desktop את הגרסה ל-Apple Silicon (M1/M2/M3/M4) או Intel, תלוי בדגם שלכם. תבדקו ב-"About This Mac" → "Chip" אם אתם לא בטוחים. רוב המקינטושים מ-2021 ואילך הם Apple Silicon.
ההתקנה: גררו את Docker.app ל-Applications. בהרצה הראשונה macOS יבקש אישור לגישה לרשת (אשרו), ו-Docker Desktop יציג את ה-Service Agreement. קחו כוס קפה — ההורדה הראשונית של ה-runtime כברידה אולי 1-2 דקות.
אחרי ש-Docker Desktop רץ (תראו אייקון של לוויתן בתפריט העליון), פתחו Terminal והריצו docker --version ו-docker run hello-world. אם ההודעה המוכרת מופיעה — סיימתם.
אפשרות 2: OrbStack — חלופה קלה ומהירה יותר
OrbStack הוא כלי שעושה בדיוק את אותו דבר כמו Docker Desktop (GUI + CLI + container engine), אבל עם שתי יתרונות משמעותיים למקינטוש:
- זמן הרצה של container: ~1-2 שניות במקום 5-8 שניות. container קטן עולה ברגע, לא בכמה שניות.
- Footprint קטן בהרבה: OrbStack צורך כ-300-500 MB של זיכרון במצב idle, לעומת 1.5-2 GB של Docker Desktop. על Mac עם 8 GB זיכרון — זה ההבדל בין "עובד חלק" לבין "מתחיל להחליף לדיסק".
התקנה: הורידו מ-orbstack.dev, גררו ל-Applications, הריצו. ה-CLI של OrbStack מתחזה בתור docker — כלומר, אחרי ההתקנה, docker run nginx עובד בדיוק אותו דבר. אין צורך לשנות שום סקריפט.
OrbStack הוא חינמי לשימוש אישי. לעסקים יש רישיון בתשלום, אבל ל-Vibe Coder סולו הוא בחינם.
אפשרות 3: Rancher Desktop (חינמי לכל שימוש מסחרי)
אם אתם עובדים בחברה שעוברת את הסף של Docker Desktop (250 עובדים או $10M הכנסה — נדבר על זה בסעיף הבא), Rancher Desktop של SUSE הוא תחליף מלא שתמיד חינמי, גם לשימוש מסחרי (רישיון Apache-2.0). הוא גם מגיע עם k3s (קוברנטיס קל) בנוסף ל-Docker Engine, אבל לא חייבים להשתמש בזה.
הורידו מ-rancherdesktop.io. ה-CLI שלו גם מתחזה בתור docker, כך שהפקודות בפרק הזה עובדות בדיוק אותו דבר.
אז מה לבחור?
המלצה מעשית: Docker Desktop הוא הבחירה הבטוחה ל-Vibe Coder סולו. הוא חינמי, הוא רשמי, התיעוד של Docker מתייחס אליו ישירות. התקינו אותו, המשיכו הלאה. OrbStack — אם המק שלכם חזק אבל הזיכרון צמוד, או אם אתם מתעצבנים מ-5 שניות boot. Rancher Desktop — אם המעסיק שלכם ביקש רישיון מסחרי.
החליטו: Docker Desktop (ברירת מחדל), OrbStack (Mac, רוצים מהירות), או Rancher Desktop (צרכים מסחריים). התקינו, הריצו docker run hello-world. תוצאה צפויה: ההודעה המוכרת של Hello from Docker! — בלי קשר לאיזה כלי בחרתם.
מיתוס העלות — מתי Docker Desktop חינמי ומתי לא
אחת השאלות שאני שומע הכי הרבה: "אבל Docker Desktop עולה כסף, נכון?" התשובה הקצרה: לא, לא בשבילכם. התשובה הארוכה דורשת להבין את הסף הרשמי.
הסף הרשמי (נכון ל-2026)
לפי docs.docker.com/subscription/desktop-license, Docker Desktop חינמי עבור:
- שימוש אישי (personal use).
- חינוך (education).
- פרויקטי open source לא-מסחריים.
- עסקים קטנים שעומדים בשני התנאים הבאים ביחד: פחות מ-250 עובדים וגם פחות מ-$10M הכנסה שנתית.
הקריטי כאן הוא ה-וגם. אם לחברה שלכם יש 200 עובדים אבל $20M הכנסה — אתם מעל הסף, וצריך רישיון. אם יש 300 עובדים אבל $5M — גם מעל הסף. רק עסקים שמתחת לשתי הספים ביחד זכאים לחינם.
מה עולה וכמה
התוכניות המסחריות (מחירים ל-2026, לחודש, חיוב חודשי — שנתי זול יותר):
| תוכנית | מחיר | למי מתאים |
|---|---|---|
| Personal | חינם | שימוש אישי, חינוך, open source |
| Pro | $11 למשתמש לחודש | פרילנסרים, סטארטאפים מעל הסף |
| Team | $16 למשתמש לחודש | צוותים קטנים, מעל הסף |
| Business | $24 למשתמש לחודש | ארגונים, governance ו-SSO |
מה עם Vibe Coder סולו?
המצב הרגיל שלכם: אתם אדם אחד, אולי עם עוד חבר או שניים שעובדים אתכם. אתם רחוקים מאוד מ-250 עובדים, והכנסה של $10M שנתית זו חלום רחוק לרוב הפרויקטים האישיים. ברירת המחדל שלכם: Docker Desktop חינמי.
המקרה היחיד שבו תצטרכו לשלם הוא אם תעבדו בחברה גדולה שתדרוש רישיון מסחרי — ואז היא תשלם, לא אתם.
Docker Engine (בלי Desktop) הוא OSS תמיד
חשוב להפריד: Docker Engine (ה-daemon שמריץ containers, dockerd) הוא קוד פתוח תמיד, בנפרד מ-Docker Desktop. על Linux, אתם מתקינים רק את Docker Engine וזהו — חינמי לכל שימוש, תמיד. הרישוי של Docker Desktop קיים רק על המוצר הספציפי הזה (האפליקציה הגרפית ל-Windows/Mac).
בקיצור: ה-Vibe Coder הישראלי הממוצע לא ישלם על Docker לעולם. המיתוס ש-"Docker זה כלי יקר" הוא בעיקר חוסר היכרות עם הסף.
לפני שאתם מתחילים לחפש חלופות, ענו על שלוש שאלות בסדר הזה:
- האם אני משתמש ב-Docker לפרויקט אישי, חינוך, או open source לא-מסחרי?
- כן → חינמי. התקינו Docker Desktop והמשיכו.
- לא → עברו לשאלה 2.
- האם החברה שלי (או הסטארטאפ שלי) מתחת ל-250 עובדים וגם מתחת ל-$10M הכנסה שנתית?
- כן לשניהם → חינמי. עומדים בסף המסחרי.
- לא לפחות לאחד → עברו לשאלה 3.
- האם המעסיק שלי מוכן לשלם רישיון Pro/Team/Business?
- כן → Docker Desktop בתשלום. תשלום של המעסיק, לא שלכם.
- לא → התקינו Rancher Desktop (חינמי לכל שימוש מסחרי, Apache-2.0). הפקודות זהות, ה-CLI זהה.
איך להשתמש ב-Framework: ענו על השאלות בראש לפני ההתקנה. ב-95% מהמקרים של Vibe Coder סולו או סטארטאפ קטן, התשובה תהיה "חינמי" — והחלק הזה של הדאגה ייעלם.
הניצחון המיידי — docker run hello-world
הפקודה הזו תעבוד או לא תעבוד. אין באמצע. אם היא עובדת — יש לכם Docker פעיל, חיבור לרשת, ו-image registry פתוח. אם לא — יש לכם בעיה שצריך לפתור לפני שממשיכים. בואו נראה מה היא עושה.
מה קורה שלב אחר שלב
כשאתם מריצים docker run hello-world בפעם הראשונה, מאחורי הקלעים קורים ארבעה דברים:
- Docker בודק אם ה-image
hello-worldקיים מקומית. הוא לא (אין לכם image כזה במחשב), אז Docker ממשיך לשלב הבא. - Docker פונה ל-Docker Hub (registry ציבורי ברירת מחדל) ומוריד את ה-image. הוא שוקל 13 KB — זה image של "Hello World" שעושה כלום חוץ מלהדפיס הודעה. ההורדה לוקחת פחות משנייה.
- Docker יוצר container מה-image ומריץ את הפקודה הראשית שלו (במקרה הזה, סקריפט שמדפיס את ההודעה).
- ה-container מסיים את עבודתו ויוצא. ה-image נשאר במחשב שלכם, מוכן לריצה חוזרת בלי הורדה.
הפלט אמור להיראות בערך כך:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
מה זה אומר עליכם
אם אתם רואים את ההודעה הזו, זה אומר ש-4 דברים עובדים אצלכם במקביל:
- ה-CLI של Docker תקין — הפקודה זוהתה.
- ה-Docker daemon (השרת הפנימי) רץ — יש לכם "מנוע" שמריץ containers.
- הרשת שלכם פתוחה ל-Docker Hub — הורדה עברה.
- ה-registry פתוח — Docker Hub הגיב.
אם משהו נשבר, ההודעת השגיאה תספר לכם איפה. "Cannot connect to Docker daemon" → ה-daemon לא רץ, צריך להפעיל את Docker Desktop. "Timeout pulling image" → בעיית רשת, צריך לבדוק proxy או חומת אש. "Permission denied" → בלינוקס, צריך להוסיף את המשתמש לקבוצת docker.
הריצו docker run hello-world ב-terminal. תוצאה צפויה: הודעה שמתחילה ב-"Hello from Docker!" ומסבירה את 4 השלבים ש-Docker עשה. אם קיבלתם את זה — אתם ב-1% הראשון של הקורס. אם קיבלתם שגיאה, חזרו לסעיף 1 או 3 לפי ה-OS שלכם.
ה-container הראשון שלכם — nginx עם port mapping
עכשיו, אחרי שה"Hello World" עבר, הגיע הזמן להריץ משהו אמיתי. לא סקריפט שמדפיס הודעה, אלא שרת web שמגיש דף אמיתי ב-browser. ה-image הזה נקרא nginx (מבוטא "engine-x") — שרת HTTP פופולרי שמשרת כ-30% מהאתרים בעולם.
הפקודה
docker run -p 8080:80 nginx
זה הכל. פקודה אחת. הפלט יראה הורדה של ה-image (כ-50 MB, לוקח 5-20 שניות תלוי ברשת), ואז הודעות של nginx שמתחיל לרוץ. ה-container לא יוצא — הוא ממשיך לרוץ ב-foreground, מדפיס לכם logs של בקשות HTTP. זה הסימן שהוא חי.
עכשיו: פתחו browser (Chrome, Firefox, Safari — מה שבא לכם), ובשורת הכתובת הקלידו:
http://localhost:8080
אם אתם רואים את הדף המפורסם של nginx ("Welcome to nginx!"), זה הרגע שבו הבנתם את Docker. הרצתם שרת web אמיתי, בלי להתקין שום דבר על המחשב שלכם, בלי להגדיר אף קובץ קונפיגורציה, בלי לפתוח פורט ב-firewall של המערכת. הכל קרה בתוך container סגור, מבודד מהמערכת שלכם.
למה -p 8080:80?
הפלאג -p הוא port mapping — זה החלק הכי חשוב בפרק הזה, ונחזור אליו בסעיף נפרד. בקצרה: ה-container של nginx מאזין על פורט 80 בתוך עצמו (כי כל שרת HTTP ברירת-מחדל). המכונה שלכם לא יכולה לגעת בפורט הזה ישירות — ה-container הוא מכונה וירטואלית מבודדת. -p 8080:80 אומר ל-Docker: "כל בקשה שמגיעה לפורט 8080 אצלי, תעביר לפורט 80 בתוך ה-container."
ה-8080 הוא הפורט הציבורי שלכם (אתם בחרתם 8080 כי הוא פנוי, וכי הוא לא דורש הרשאות admin ברוב המערכות). ה-80 הוא הפורט הפנימי של ה-container. שמאל=המארח שלכם, ימין=ה-container. נחזור לזה בהרחבה.
מה עכשיו?
ה-container רץ ב-foreground ב-terminal. כדי לעצור אותו: Ctrl+C בחלון ה-terminal. ה-container ייסגר, אבל ה-image יישאר במחשב. אם תריצו שוב את אותה פקודה, הורדה לא תקרה — ה-image כבר שם.
אם תרצו להריץ את ה-container ברקע (לא תופס את ה-terminal), הוסיפו -d (detached):
docker run -d -p 8080:80 nginx
הפלט יהיה מחרוזת ארוכה — זה ה-container ID. השתמשו בו כדי לעצור את ה-container: docker stop <container-id>. נלמד את זה בסעיף הבא.
הריצו docker run -d -p 8080:80 nginx (ה--d שולח את ה-container לרקע ומחזיר לכם את ה-ID). פתחו http://localhost:8080 ב-browser. תוצאה צפויה: דף ה-Welcome של nginx. אם אתם רואים את זה — הכל עובד. אם לא — חזרו לסעיף על port mapping.
6 הפעלים היומיומיים — run, ps, logs, stop, rm, exec
יש ~70 פקודות Docker שונות, אבל בפועל 6 פקודות מכסות 90% מהצרכים שלכם. הכירו אותן, השתמשו בהן, ותוכלו להריץ כל container שתרצו. נעבור עליהן אחת-אחת, בדיוק בסדר שבו תשתמשו בהן ביום עבודה רגיל.
1. docker run — להריץ container מ-image
כבר הכרתם: docker run -p 8080:80 nginx. הפקודה הזו עושה 4 דברים ברגע אחד:
- בודקת אם ה-image קיים מקומית; אם לא — מושכת מ-registry.
- יוצרת container חדש מה-image.
- מריצה את הפקודה הראשית של ה-image (CMD ב-Dockerfile).
- ברירת מחדל: מציגה את הפלט של ה-container ב-terminal.
הפלאגים הכי שימושיים:
-d— detached, רץ ברקע ולא תופס את ה-terminal.-p host:container— port mapping (נפרט בסעיף הבא).--name mycontainer— נותן ל-container שם במקום מחרוזת רנדומלית.-e KEY=VALUE— מעביר env var (נשתמש בזה בפרק 4).--rm— מוחק את ה-container אוטומטית כשהוא נעצר. שימושי לטסטים.
2. docker ps ו-docker ps -a — מי רץ, מי עצר
docker ps מציג את ה-containers שרצים עכשיו. הפלט הוא טבלה עם 7 עמודות:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp wonderful_elion
הסבר על העמודות:
- CONTAINER ID — מזהה ייחודי. 12 תווים מתוך hash מלא. מספיק בשביל כל פקודה אחרת.
- IMAGE — ה-image שממנו ה-container רץ.
- COMMAND — הפקודה הראשית של ה-container.
- CREATED — מתי נוצר.
- STATUS — "Up X minutes" אם רץ, "Exited (0) X minutes ago" אם עצר.
- PORTS — port mapping (כאן: 8080 אצלי → 80 ב-container).
- NAMES — שם ידידותי. אם לא קבעתם, Docker ייתן שם אקראי כמו "wonderful_elion".
docker ps -a (הדגל -a = all) מציג גם containers שעצרו. זה הכלי הראשון שתריצו כשמשהו "נעלם" — הוא יגיד לכם "Exited (0)" (יצא תקין) או "Exited (1)" (קרס עם שגיאה).
3. docker logs <id-or-name> — מה ה-container אומר
כש-container רץ ברקע (-d), אתם לא רואים את הפלט שלו. docker logs מציג אותו: כל שורה שה-container הדפיס ל-stdout/stderr.
docker logs a1b2c3d4e5f6
פלאגים שימושיים:
-f— follow, ממשיך להציג logs חדשים כשהם מגיעים (כמוtail -f).--tail 50— רק 50 השורות האחרונות (לא כל ההיסטוריה).-t— מוסיף timestamps.
טיפ: אם container קרס ואתם רואים "Exited (1)", docker logs הוא הצעד הראשון שלכם. ברוב המקרים הודעת השגיאה האמיתית נמצאת שם.
4. docker stop <id-or-name> — לעצור בנימוס
docker stop שולח SIGTERM ל-container, נותן לו 10 שניות לסיים בנימוס, ואז שולח SIGKILL אם הוא לא מסיים. זו הדרך הנכונה לעצור container.
docker stop a1b2c3d4e5f6
אפשר לעצור כמה containers בבת אחת:
docker stop a1b2c3d4e5f6 f6e5d4c3b2a1 wonderful_elion
5. docker rm <id-or-name> — למחוק container שעצר
container שעצר לא נמחק אוטומטית. הוא נשאר ברשימה של docker ps -a, תופס מקום בדיסק, ומבלבל אתכם. docker rm מוחק אותו סופית.
docker rm a1b2c3d4e5f6
כדי למחוק את כל ה-containers שעצרו בבת אחת:
docker container prune
Docker ישאל אישור, וימחק את כל מה שלא רץ. זה שווה ערך לפקודה "נקה הכל" במטבח — זהירים.
חשוב: docker rm מוחק container. docker rmi (עם i של image) מוחק image. אל תבלבלו. זו אחת הטעויות הכי שכיחות של מתחילים.
6. docker exec -it <id-or-name> sh — להיכנס פנימה
זו הפקודה הכי כיפית והכי מפחידה בו-זמנית. היא פותחת shell בתוך container רץ. אתם בתוך ה-container כאילו אתם SSH-ים לתוך מכונה קטנה.
docker exec -it a1b2c3d4e5f6 sh
הפירוט: -i = interactive (קלט מהמקלדת), -t = tty (terminal אמיתי), sh = ה-shell שתריצו. בתוך רוב ה-images (כולל nginx) זה shell קל (sh, לא bash). לצאת: exit או Ctrl+D.
למה זה שימושי? כי container הוא "קופסה שחורה" מבחוץ. docker exec מאפשר לכם:
- לראות את מערכת הקבצים של ה-container (
ls /,cat /etc/nginx/nginx.conf). - לבדוק אם process רץ (
ps aux). - להריץ פקודה ידנית שעוזרת לאבחן בעיה.
- לבדוק env vars (
env), networking (cat /etc/hosts), ועוד.
אזהרה: שינויים שתעשו בתוך ה-container (עריכת קובץ, התקנת חבילה) יימחקו כשה-container ייעצר. ה-container הוא disposable — לא לערוך בפנים, אלא לתקן ב-image. נחזור לזה בפרק 4.
הריצו את 6 הפעלים ברצף, בדיוק בסדר הזה:
docker run -d --name mynginx -p 8080:80 nginx— הריצו nginx ברקע, עם שם שתזכרו.docker ps— ראו שהוא רץ, שימו לב לעמודה PORTS.docker logs mynginx— ראו את הלוג של nginx.docker exec -it mynginx sh— נכנסתם פנימה? הריצוls /usr/share/nginx/htmlותראו אתindex.htmlשל nginx.exitלצאת.docker stop mynginx— עצרתם אותו.docker rm mynginx— מחקתם אותו.
תוצאה צפויה: 6 פלטים נקיים, אחד אחרי השני, בלי שגיאות. אם משהו נשבר — חזרו לסעיף הרלוונטי ובדקו.
port mapping מפוענח — host:container
זה החלק שמבלבל 80% מהמתחילים. ברגע שתבינו את הכיוון, הכל יסתדר. בואו נפרק את -p 8080:80 לגורמים.
הרעיון: שתי מכונות, שני עולמות
container הוא כמו מכונה קטנה שרצה בתוך המחשב שלכם. היא:
- יש לה מערכת קבצים משלה (
/,/etc,/usr...). - יש לה רשת משלה. היא לא רואה את המארח ישירות, והמארח לא רואה אותה ישירות.
- יש לה פורטים משלה, מספרים 1-65535. אפסילון רץ על 80 בתוך ה-container, אבל אם תפתחו
localhost:80בדפדפן — לא יקרה כלום.
-p הוא ה"גשר". הוא אומר ל-Docker: "תאזין בפורט 8080 אצלי במכונה האמיתית. כל בקשה שתגיע לשם — תעביר לפורט 80 בתוך ה-container."
התחביר: host:container
תמיד שמאל=מארח, ימין=container. זה החוק היחיד שצריך לזכור.
docker run -p 8080:80 nginx— "8080 על המארח → 80 ב-container." אתם ניגשים ב-browser ל-localhost:8080.docker run -p 3000:3000 myapp— "3000 על המארח → 3000 ב-container." אתם ניגשים ב-localhost:3000.docker run -p 80:3000 myapp— "80 על המארח → 3000 ב-container." (האפליקציה רצה על 3000 בפנים, אבל חשופה ב-80 בחוץ — שימושי כשרוצים URL נקי בלי :פורט.)
למה הכיוון מבלבל?
כי רוב האנשים חושבים "אני רוצה לחשוף את 80 של ה-container" — אז הם כותבים -p 80:something בלי לחשוב. אבל האסימטריה ברורה אם חושבים על זה ככה: ה-container כבר מאזין על 80 בפנים. השאלה היא איזה פורט במכונה שלי יחשוף אותו.
עוד דרך לחשוב על זה: -p הוא כמו מתאם לחשמל בנסיעה לחו"ל. ה-container זה המכשיר שלכם (תקע אירופאי, רץ על 220V). המארח זה הקיר המקומי (תקע אמריקאי, 110V). ה--p הוא המתאם: "תקע אמריקאי → תקע אירופאי". הוא לא משנה את המכשיר; הוא מאפשר לו לדבר עם הקיר.
מה קורה אם לא כותבים -p?
אם תריצו docker run nginx בלי -p, ה-container ירוץ, יאזין על 80 בתוך עצמו, אבל אף אחד מבחוץ לא יוכל להגיע אליו. זה שימושי לפעמים (למשל, כשה-container מתקשר עם containers אחרים ברשת פנימית), אבל לרוב תרצו גישה מהדפדפן שלכם.
אימות שהמיפוי עובד
הריצו:
docker run -d -p 8080:80 --name web1 nginx
docker ps
בעמודה PORTS תראו: 0.0.0.0:8080->80/tcp. המשמעות: Docker מאזין על 0.0.0.0:8080 (כל הממשקים, פורט 8080) במארח, ומעביר ל-80/tcp ב-container. פתחו http://localhost:8080 — תראו את ה-Welcome של nginx.
עכשיו הריצו:
docker run -d -p 9090:80 --name web2 nginx
docker ps
הוספתם container שני, על פורט שונה במארח (9090 במקום 8080) — אבל אותו פורט ב-container (80). עכשיו http://localhost:9090 יפתח את ה-container השני. שני containers, אותו image, שתי כתובות שונות. זו ההמחשה הכי טובה לכך ש-image ≠ container.
הריצו:
docker run -d -p 8080:80 --name web1 nginx— בדקוhttp://localhost:8080, אתם אמורים לראות nginx.docker run -d -p 9090:80 --name web2 nginx— בדקוhttp://localhost:9090, אתם אמורים לראות nginx שוב.docker ps— ראו את שניהם ברשימה, עם PORTS שונים.docker stop web1 web2 && docker rm web1 web2— נקו.
תוצאה צפויה: שני containers רצים במקביל, שני דפי nginx זמינים בשני ports שונים. אם אתם רואים את זה — אתם מבינים port mapping.
מלכודת ה-0.0.0.0 — למה האפליקציה חייבת להאזין נכון
עד עכשיו השתמשנו ב-nginx, שמאזין על 0.0.0.0 כברירת מחדל. אבל כשתריצו את האפליקציה שלכם (Next.js, Flask, FastAPI, Express, Streamlit), אתם עלולים להיתקל בתופעה הזו:
- הרצתם
docker run -p 3000:3000 myapp. - ה-container רץ (
docker psמראה "Up"). - אבל
http://localhost:3000מציג דף ריק, או "Connection refused", או נתקע. docker logsמראה: "Listening on http://127.0.0.1:3000".
זה הרגע שבו 50% מהמתחילים מוותרים על Docker ומחליטים שזה "לא עובד". הפתרון הוא הבנה של שני כתובות IP.
127.0.0.1 מול 0.0.0.0
שתי הכתובות האלה נראות דומות אבל מתנהגות הפוך:
- 127.0.0.1 (localhost) — "רק אני." האפליקציה שמאזינה על 127.0.0.1 מקבלת חיבורים מאותו process בלבד. בתוך container, זה אומר "רק מ-process-ים בתוך אותו container." היא לא מקבלת חיבורים מבחוץ, גם אם
-pמוגדר נכון. - 0.0.0.0 (any address) — "כולם." האפליקציה שמאזינה על 0.0.0.0 מקבלת חיבורים מכל ממשק רשת — מהקיר המקומי של ה-container, מהמארח דרך
-p, ומ-containers אחרים באותה רשת.
המלכודת של אפליקציות AI
הרבה אפליקציות ש-AI כותב עובדות בריצה מקומית (npm run dev, flask run, uvicorn app:app). הן לא בנויות להיות בתוך container. הן מאזינות על 127.0.0.1 — כי ל-AI לא אכפת שהאפליקציה תהיה נגישה מבחוץ; הוא רק רוצה שהיא תעבוד על המכונה שלכם.
כשאתם עוטפים את האפליקציה ב-container, ה-127.0.0.1 הופך לבעיה: הוא אומר ל-container "אל תקבל חיבורים מהעולם החיצון" — וזה בדיוק מה שאתם כן רוצים כשאתם משתמשים ב--p.
הפתרון: האזינו על 0.0.0.0
הדרך הנכונה להריץ את האפליקציה שלכם ב-container (או בכל סביבה שרוצה גישה מבחוץ):
- Next.js / Node.js:
next dev -H 0.0.0.0אוnpm run dev -- -H 0.0.0.0. - Flask:
flask run --host=0.0.0.0. - FastAPI / Uvicorn:
uvicorn app:app --host 0.0.0.0. - Express:
app.listen(3000, '0.0.0.0')בקוד. - Streamlit:
streamlit run app.py --server.address 0.0.0.0.
אם אתם בונים את האפליקציה שלכם ב-Dockerfile (פרק 3), הפקודה שתרוץ ב-CMD צריכה לכלול את 0.0.0.0. למשל:
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "3000"]
איך לאבחן את הבעיה
אם http://localhost:8080 לא עובד:
docker ps— ה-container רץ?docker logs <id>— ראו על איזה address הוא מאזין. אם אתם רואים "127.0.0.1" במקום "0.0.0.0", זו הבעיה.- אם זו הבעיה, תקנו את הפקודה שמריצה את האפליקציה (או את ה-Dockerfile בפרק 3).
docker exec -it <id> shואזnetstat -tlnp(אוss -tlnp) — ראו את כל ה-ports הפתוחים ב-container.
הכלל: ה-container חייב להאזין על 0.0.0.0 כדי ש--p יעבוד. ה--p פותח פורט במארח, אבל אם ה-container לא מאזין על הפורט הנכון בפנים — אין לו לאן להעביר.
מה לגבי EXPOSE?
תראו ב-Dockerfile של images רבים שורה כמו EXPOSE 80 או EXPOSE 3000. זו לא פקודה שמפרסמת את הפורט. זו רק תיעוד. היא אומרת ל-Docker "ה-container הזה מתכוון להאזין על פורט 80", אבל לא עושה כלום ברשת.
כדי שהפורט באמת יהיה נגיש מבחוץ, אתם תמיד צריכים -p host:container ב-docker run, או ports: ב-compose (פרק 5). EXPOSE זה כמו הערה בקוד — מתעדת כוונה, לא מבצעת.
אז אל תסתמכו על EXPOSE. תמיד כתבו -p בפקודה שלכם.
כש-container לא מגיב ב-browser, עלו בסולם הזה לפי הסדר. כל שלב לוקח 10 שניות ופותר 80% מהבעיות:
- האם ה-container רץ?
docker ps— אם לא,docker ps -aכדי לראות למה הוא עצר (Exited (1) = קרס). - האם יש לו port mapping? עמודה PORTS צריכה להיות לא ריקה. אם ריקה, הוספתם
-p? - מה ה-logs אומרים?
docker logs <id>. האם יש שגיאה? האם הוא מאזין על 127.0.0.1 או 0.0.0.0? אם 127 — זו הבעיה. - האם הדפדפן מנסה את הכתובת הנכונה?
http://localhost:PORT— שימו לב שה-port הוא ה-host port (שמאל ב--p), לא ה-container port. - האם הכלי (browser, curl, postman) מצליח להגיע?
curl http://localhost:8080מה-terminal עצמו. אם curl עובד אבל הדפדפן לא — בעיה בדפדפן (cache, extension). אם curl נופל — בעיה ב-container.
איך להשתמש ב-Framework: אל תקפצו לשלב 5. עברו שלב-שלב. רוב הבעיות נפתרות בשלב 2-3. אם הגעתם לשלב 5 ועדיין לא עובד, יש בעיה רשת עמוקה יותר (DNS, IPv6, firewall) — וזה כבר סיפור לפרק 6 או לקורס נפרד.
ניקיון וסדר — stop, rm, ps -a
אחרי שעבדתם קצת עם containers, תגלו שנוצר לכם "מחסן" של containers שעצרו, images שלא בשימוש, ו-volumes זמניים. Docker נותן לכם כלים לסדר את זה — אבל חשוב להבין מה כל אחד מוחק, כדי לא למחוק משהו חשוב.
שלוש פקודות הניקיון המרכזיות
docker container prune — מוחק את כל ה-containers העצורים. לא נוגע ברצים, לא נוגע ב-images, לא נוגע ב-volumes. זו הפקודה הבטוחה ביותר להריץ כשאתם רוצים לסדר.
docker image prune — מוחק images שאין להם שום container שמשתמש בהם ("dangling images"). שימושי כש-pull-יתם image חדש עם אותו שם, או כשבניתם image מקומי שכבר לא רלוונטי.
docker system prune — מוחק הכל: containers עצורים, images שלא בשימוש, networks לא בשימוש, ו-build cache. זו פקודה "גרעין" — שימושית אחרי ניסויים רבים, אבל מסוכנת אם לא זוכרים מה היה שם. תמיד תעברו על הפלט לפני שתקישו Y.
פקודת "הכל בבת אחת"
אם אתם רוצים לסגור הכל ולהתחיל מאפס:
docker stop $(docker ps -q)
docker rm $(docker ps -aq)
הסבר: docker ps -q מחזיר רק IDs של containers רצים. docker ps -aq מחזיר IDs של כל ה-containers (כולל עצורים). $(...) מחליף את הרשימה לפקודה. שימו לב: הפקודה השנייה תנסה למחוק גם containers רצים — לכן ה- stop לפני.
מה לא למחוק
volumes (פרק 4). docker volume prune מוחק volumes שאינם בשימוש — כלומר, volumes שאף container לא מצביע עליהם. אם הפעלתם מסד נתונים ב-container, הווליום שלו יישאר גם אחרי docker rm — וזה בכוונה, כי הוא מכיל את הנתונים שלכם. אל תריצו volume prune בלי לבדוק קודם.
images עם tag. docker image prune בלי דגלים ימחק רק dangling images. אם תריצו docker image prune -a — הוא ימחק גם images שיש להם tag אבל אף container לא משתמש בהם כרגע. זה הגיוני לפעמים (אם אתם יודעים שלא תצטרכו אותם), אבל זה גם ימחק לכם את ה-image של nginx שלקח לכם 20 שניות להוריד. היזהרו.
רשימת containers שעצרו
תריצו את זה מדי פעם כדי לראות מה תקוע:
docker ps -a --filter "status=exited"
הפלט יראה לכם רק containers שעצרו. אם יש שם משהו שאתם לא מזהים — זה הזמן לחקור. אם זה ניסוי ישן — docker rm <id> ולהמשיך.
הריצו docker ps -a --filter "status=exited" כדי לראות מה תקוע אצלכם. אם יש משהו — מחקו אותו עם docker rm <id>. אם ריק — מצוין, התחלתם נקי. תוצאה צפויה: רשימה ריקה או רשימה שמתרוקנת אחרי הפקודה. אל תריצו system prune עדיין — תכירו קודם את הסביבה.
הגדרת המסך — מה לוודא לפני שממשיכים
לפני שאתם סוגרים את הפרק הזה, יש 4 דברים שכדאי לוודא שהם עובדים. אם כולם עובדים — אתם מוכנים לפרק 3. אם משהו נשבר — חזרו לסעיף הרלוונטי.
4 הבדיקות לפני המעבר
- Docker Desktop רץ. האייקון של הלוויתן בתפריט (Mac) או ב-system tray (Windows) לא מציג שגיאה. אם הוא אדום/כתום/אפור — לחצו ובדקו מה חסר.
docker --versionעובד. פלט כמוDocker version 27.x.x, build xxxxx. אם לא — ה-CLI לא ב-PATH, צריך להפעיל מחדש את ה-terminal.docker run hello-worldעובד. הודעת "Hello from Docker!" מופיעה. זה מאמת שיש לכם engine, רשת, ו-registry.docker run -p 8080:80 nginx+ browser עובדים. ה-Welcome של nginx מופיע ב-localhost:8080. זה מאמת את כל המיומנויות של הפרק: image pull, container run, port mapping, ו-0.0.0.0.
אם כל 4 עובדים — אתם מוכנים לפרק 3. בו נלמד לארוז את האפליקציה שלכם לתוך image, לא רק להריץ images של אחרים.
מה לא צריך לבדוק (עדיין)
אל תבדקו את הדברים האלה בשלב הזה — הם לפרק 3+:
- בניית image מ-Dockerfile (פרק 3).
- Volumes ו-persistence של נתונים (פרק 4).
- Secrets ב-runtime (פרק 4).
- Compose עם מסד נתונים (פרק 5).
- Push ל-ghcr.io או deploy ל-VPS (פרק 6).
הפרק הזה הוא הבסיס. אם הוא יציב — השאר ייבנה עליו.
עברו על 4 הבדיקות למעלה, אחת-אחת, בלי לדלג. אם הכל עובד — סימנו V בראש. אם משהו נשבר, חזרו לסעיף שלו. תוצאה צפויה: רשימה של 4 V-ים. אם יש לכם 4 — סיימתם את הפרק. מחר או היום בערב, כשתפתחו את פרק 3, תוכלו להתחיל מיד בלי לבזבז זמן על debug של ההתקנה.
הצורך הכי גדול שלכם בקורס הזה הוא לדעת איפה אתם ברגע נתון. הנה המסגרת הפשוטה:
- state: building — אתם בונים image. אתם עובדים עם
docker build,dockerfile, וקבצים. הריצה היא רק בדיקה ("עובד? יאללה commit"). - state: running — ה-image קיים, ואתם מריצים container. אתם עובדים עם
docker run,docker ps,docker logs,docker exec. הריצה היא ה-production, לא בדיקה.
הכלל הפשוט: כל פקודה שאתם מריצים — שאלו "אני ב-building או ב-running?" אם אתם ב-building, הקובץ החשוב הוא Dockerfile. אם אתם ב-running, המקור אמת הוא ה-container.
דוגמה:
- "האפליקציה שלי לא רצה בתוך ה-container" → state: running →
docker logsקודם, לא לפתוח Dockerfile. - "ה-image גדול מדי" → state: building → תקנו את ה-Dockerfile (פרק 3).
- "ה-data נעלם אחרי restart" → state: running → חסר volume (פרק 4).
איך להשתמש ב-Framework: לפני כל פעולה, קבעו את ה-state. זה חוסך לכם חיפושים ארוכים במקום הלא נכון. רוב הבעיות של מתחילים נובעות מלבלבל בין שני ה-states — מנסים לתקן container רץ על-ידי עריכת Dockerfile, או מנסים לאבחן בעיית build בלוגים של container.
המטרה: לעבור את כל 6 הפעלים ברצף, בלי להציץ בסעיף 7. אם אתם צריכים לחזור לסעיף — זה בסדר, המטרה היא לבנות שריר.
מה תעשו:
- פתחו terminal נקי (סגרו קודם, פתחו מחדש).
- הריצו
docker run -d -p 8080:80 --name mysite nginx. אם ה-port תפוס, החליפו ל-8081, 8082 — מה שפנוי. - פתחו
http://localhost:8080ב-browser. ודאו שאתם רואים את nginx. - חיזרו ל-terminal:
docker ps. ראו אתmysiteבטבלה. docker logs mysite— ראו את ה-logs.docker exec -it mysite sh— נכנסתם פנימה? הריצוls /usr/share/nginx/htmlואזcat /usr/share/nginx/html/index.html | head -5. תראו את ה-HTML של nginx.exitלצאת.docker stop mysite.docker rm mysite.docker ps -a— רשימה ריקה (או כל מה שלא היה קשור לתרגיל).
תוצאה צפויה: 9 פלטים נקיים אחד אחרי השני, בלי שגיאות. ה-browser מציג את nginx באמצע התרגיל. ה- docker exec מציג את הקבצים של ה-image. בסוף — רק containers זמניים (אם היו) נשארים. אם משהו נשבר — חזרו לסעיף 7 ובדקו איפה.
המטרה: להוכיח בעיניים ש-image ≠ container. אותו שימוש (הגשת דף), שתי מימושים שונים, שתי כתובות שונות.
מה תעשו:
- הריצו
docker run -d -p 8080:80 --name nginx1 nginx— nginx על 8080. - הריצו
docker run -d -p 9090:80 --name apache1 httpd— Apache (httpd) על 9090. שימו לב: זה image אחר (httpd), אבל גם הוא מאזין על 80 בפנים. ה-port mapping הוא זה שמשתנה. - הריצו
docker ps— תראו שני containers ברשימה. שימו לב לעמודה IMAGE:nginxו-httpdשונים, אבל PORTS: 0.0.0.0:8080->80 ו-0.0.0.0:9090->80 — אותו container port (80), שני host ports שונים. - פתחו
http://localhost:8080— "Welcome to nginx!". - פתחו
http://localhost:9090— "It works!" (של Apache). - בדקו שה-logs נפרדים:
docker logs nginx1מראה רק בקשות ל-nginx,docker logs apache1מראה רק בקשות ל-Apache. - סיימתם?
docker stop nginx1 apache1 && docker rm nginx1 apache1.
תוצאה צפויה: שני דפי HTML שונים בשני tabs של הדפדפן, שני containers רצים במקביל, שני stream-ים של logs. זו הוכחה ויזואלית ש-image הוא ה"מתכון" (כל image מגיש דף אחר), ו-container הוא ה"מנה הרצה" (כל אחד רץ בנפרד). אם אתם רואים את זה — אתם מבינים את ההבחנה.
המטרה: לחוות את המלכודת הנפוצה ביותר של אפליקציות AI — ולתקן אותה. התרגיל הזה לא דורש אפליקציה שלכם; הוא משתמש ב-Python image סטנדרטי שמדמה את הבעיה.
מה תעשו:
- צרו תיקייה זמנית:
mkdir ~/docker-0-0-0-0-test && cd ~/docker-0-0-0-0-test. - צרו קובץ
app.pyעם התוכן הבא (Flask server שמאזין על 127.0.0.1):from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello from inside the container!' if __name__ == '__main__': app.run(host='127.0.0.1', port=3000) - צרו
requirements.txtעם שורה אחת:flask==3.0.0. - הריצו:
docker run -d -p 3000:3000 -v $(pwd):/app -w /app python:3.12-slim sh -c "pip install -q -r requirements.txt && python app.py" - המתינו 5 שניות ואז
curl http://localhost:3000או פתחו ב-browser. מה אתם רואים? "Connection refused" או שגיאה דומה. זו המלכודת. - עצרו ומחקו:
docker stop $(docker ps -q) && docker rm $(docker ps -aq). - עכשיו תקנו: שנו את
app.pyכך שיאזין על0.0.0.0במקום127.0.0.1:if __name__ == '__main__': app.run(host='0.0.0.0', port=3000) - הריצו שוב את אותה פקודה משלב 4.
curl http://localhost:3000— עכשיו אתם אמורים לראות "Hello from inside the container!".- נקו:
docker stop $(docker ps -q) && docker rm $(docker ps -aq).
תוצאה צפויה: בריצה הראשונה — שגיאה. בריצה השנייה (אחרי שינוי ל-0.0.0.0) — הודעת "Hello from inside the container!" ב-browser. זו ההוכחה המעשית: הקוד שלכם חייב להאזין על 0.0.0.0 כדי ש-port mapping יעבוד. אתם תתקלו בזה שוב ושוב, בכל אפליקציה ש-AI יבנה לכם; עכשיו אתם יודעים איך לזהות ולתקן ב-30 שניות.
זו הטעות הראשונה שתעשו (אם עוד לא עשיתם). אתם כותבים -p 80:8080 כשאתם מתכוונים -p 8080:80. ה-container רץ. docker ps מראה PORTS. אבל localhost:8080 לא עובד. אתם פותחים גוגל, מחפשים "docker not working", ומבלים 20 דקות בפורומים.
הסימפטום המדויק: הרצתם container של אפליקציה שמאזינה על 3000. במקום לכתוב -p 3000:3000 (האזנה ב-3000 במארח, הפניה ל-3000 ב-container), כתבתם -p 3000:80 (האזנה ב-3000 במארח, הפניה ל-80 ב-container). המארח מאזין, אבל ב-container אף אחד לא מאזין על 80. הבקשה נופלת.
התיקון: זכרו את הכלל: שמאל=מארח, ימין=container. אם האפליקציה ב-container מאזינת על 3000, אז הפורט הימני חייב להיות 3000. את הפורט השמאלי אתם בוחרים (8080, 9090, 3000 — מה שפנוי). אם אתם לא זוכרים, תחזרו לסעיף 8 ותקראו שוב את הדוגמאות.
המלכודת הזו עוד יותר מתסכלת מהקודמת, כי היא נראית כאילו הכל בסדר: ה-container רץ, ה-port mapping מוגדר, אבל הדפדפן מציג "Connection refused" או נתקע. רוב האנשים מניחים ש-Docker בעייתי, ומוותרים.
הסיבה האמיתית: האפליקציה שלכם (Next.js, Flask, FastAPI, וכו') מאזינה בריצה מקומית על 127.0.0.1. זה עובד מצוין כשהיא רצה ישירות על המחשב שלכם — ה-browser פונה ל-localhost, האפליקציה עונה מ-localhost, הכל טוב. אבל בתוך container, 127.0.0.1 הוא ה-container עצמו, לא המארח. גם אם -p מעביר את הבקשה נכון, ברגע שהיא מגיעה לפנים — אין process שמקבל אותה.
התיקון: האזינו על 0.0.0.0. לכל framework זה קצת אחרת: flask run --host=0.0.0.0, uvicorn --host 0.0.0.0, next dev -H 0.0.0.0, וכו'. הריצו docker logs <id> כדי לוודא שאתם רואים "0.0.0.0" ולא "127.0.0.1" או "localhost" בפלט. אם רואים 127 — תקנו את הפקודה לפני שממשיכים.
הפחד הזה עוצר אנשים לפני שהם בכלל מתקינים. הם שומעים "Docker Desktop Pro $11 לחודש" ומניחים שזה מה שהם צריכים. אז הם נשארים בלי Docker, מתעצבנים מ-"רץ אצלי במחשב", ומוותרים על deploy.
המציאות: Docker Desktop חינמי לכל Vibe Coder סולו, לכל סטארטאפ קטן, לכל חברה עם פחות מ-250 עובדים וגם פחות מ-$10M הכנסה שנתית. הסף הזה גבוה בהרבה מהמצב הרגיל שלכם. גם אם תגיעו לחברה שמעל הסף, המעסיק ישלם (או שתתקינו Rancher Desktop, שתמיד חינמי). אין סיבה להימנע מ-Docker בגלל עלות.
התיקון: התקינו Docker Desktop עכשיו. הוא חינמי ב-99% מהמקרים. אם תגלו שאתם במקרה הנדיר שבו צריך רישיון, תתמודדו עם זה אז. אל תקראו מאמרים על רישוי לפני שהתקנתם — זה כמו לקרוא חוזה שכירות לפני שבכלל החלטתם איפה לגור.
יומי (10 דקות, 14 ימים ראשונים):
- 5 דקות — תרגול ה-6 פעלים. פתחו terminal, הריצו
docker run -d -p 8080:80 --name mynginx nginx, ואז תרגלו את 6 הפקודות:ps,logs,exec,stop,rm. אחר כךdocker run -d -p 9090:80 nginxושוב. המטרה: שהפקודות יהפכו לטבע שני. - 5 דקות — ניסוי port mapping. נסו להחליף את הפורטים:
-p 7000:80,-p 8888:80,-p 1234:80. ראו בעצמכם שה-host port יכול להיות כל דבר, אבל ה-container port חייב להתאים לאפליקציה.
שבועי (20 דקות, יום ראשון): חזרה על הבדל בין image ל-container. רוצו 3 images שונים מ-Docker Hub (nginx, httpd (Apache), caddy) — כולם שרתי web — וראו את 3 הדפים השונים. זה מחזק את ההבנה ש-image ≠ container: אותו שימוש (הגשת דף), 3 מימושים שונים, אותה צורת עבודה.
לפני פרק 3 (סוף שבוע שני): ודאו ש-4 הבדיקות מסעיף 11 עוברות: Docker Desktop רץ, --version עובד, hello-world עובד, nginx ב-browser עובד. אם משהו נשבר, תקנו עכשיו. בפרק 3 נתחיל לבנות image של האפליקציה שלכם, ואתם צריכים סביבה יציבה.
ברגע שתסיימו את הפרק הזה — אתם מוכנים לפרק 3. המעבר הוא מ-"להריץ images של אחרים" ל-"לבנות image משלי". זה הצעד הגדול הראשון בקורס. אל תקפצו — ודאו ש-6 הפעלים ו-port mapping יציבים בראש לפני שממשיכים.
- ההתקנה של Docker שונה בין Windows ל-Mac, אבל התוצאה זהה. ב-Windows: Docker Desktop דרך WSL2 (kernel לינוקס אמיתי, גרסה 2.1.5+). ב-Mac: Docker Desktop, OrbStack (קל יותר), או Rancher Desktop (חינמי מסחרי). בשני המקרים,
docker run hello-worldמאמת שהכל עובד. - ב-WSL2, שמרו קבצים בתוך
~/(Linux filesystem), לא ב-C:. קבצים על C: שעוברים mount לתוך WSL גורמים לבנייה ול-file watching איטיים פי 5-10. הכלל:pwdצריך להתחיל ב-/home/או/root/. - Docker Desktop חינמי ל-Vibe Coder סולו. הסף הרשמי: פחות מ-250 עובדים וגם פחות מ-$10M הכנסה שנתית. Vibe Coder סולו, פרילנסר, וסטארטאפ קטן נופלים הרבה מתחת לסף. המיתוס "Docker זה כלי יקר" פשוט לא נכון לרוב המוחלט של הקורסים.
- 6 הפעלים מכסים 90% מהצרכים.
run(להריץ),ps/ps -a(לראות מי רץ ומי עצר),logs(לראות פלט),stop(לעצור),rm(למחוק container שעצר),exec -it … sh(להיכנס לפנים). אלה הפקודות שתריצו שוב ושוב — לא 70 הפקודות האחרות. - port mapping: שמאל=מארח, ימין=container.
-p 8080:80אומר "8080 במכונה שלי → 80 בתוך ה-container." הכיוון קבוע. אם התוצאה לא עובדת, בדקו שאתם לא הפכתם. - ה-container חייב להאזין על 0.0.0.0, לא על 127.0.0.1. זו המלכודת הכי שכיחה של אפליקציות AI. הן רצות מקומית על 127.0.0.1, וברגע שעוטפים ב-container הן לא נגישות. הפתרון:
--host 0.0.0.0בכל framework. EXPOSEזה תיעוד, לא פקודה.EXPOSE 80ב-Dockerfile לא פותח שום פורט. צריך-p host:containerבכלdocker runכדי שהפורט באמת יהיה נגיש מבחוץ.EXPOSEרק מתעד כוונה.
אם תצאו מהפרק הזה עם דבר אחד בראש, שיהיה זה: docker run -p 8080:80 nginx ופתיחת http://localhost:8080 עובדים. זה הוכחה שהמערכת שלכם מוכנה: CLI תקין, engine רץ, רשת פתוחה, registry נגיש, port mapping תקין, ה-container מאזין נכון. כל אחד מ-6 הפעלים הוא כלי; הצירוף שלהם הוא היכולת להריץ כל container בעולם. אם הצלחתם להריץ את nginx ב-browser — אתם יודעים 80% ממה ש-Vibe Coder צריך לדעת על הרצת containers. ה-20% הנותרים זה אריזה של האפליקציה שלכם (פרק 3) ושמירת נתונים (פרק 4). אבל הבסיס — הרצה, מעקב, אבחון, ניקיון — זה כבר שלכם.
- שאלה: הרצתם
docker run -p 3000:80 myappוה-container רץ, אבלhttp://localhost:3000מציג "Connection refused." מה הסיבה הסבירה ביותר, ואיך תאבחנו אותה?
תשובה: הסיבה הסבירה ביותר: האפליקציה מאזינה בתוך ה-container על port אחר (לא 80), או שהיא מאזינה על 127.0.0.1 במקום 0.0.0.0. האבחון:docker logs <id>— חפשו את השורה שאומרת על איזה host:port היא מאזינה. אם אתם רואים "127.0.0.1" או פורט אחר מ-80, תקנו את הפקודה (להוסיף--host 0.0.0.0או לתקן את המיפוי ל-port הנכון). - שאלה: אתם על Windows, וה-build של ה-image לוקח 12 דקות במקום דקה. ה-Workspace שלכם נמצא ב-
C:\Users\me\projects\myapp. מה הסיבה, ומה הפתרון?
תשובה: הסיבה: אתם עובדים על C: שמועבר mount לתוך WSL דרך/mnt/c/. כל קריאת קובץ עוברת גשר בין שתי מערכות קבצים, מה שהופך את הבנייה לאיטית פי 5-10. הפתרון: העתיקו את הפרויקט לתוך~/projects/myappב-WSL, פתחו אותו מ-VSCode דרך WSL (code .), והריצו build מה-terminal של אובונטו. הזמן ירד לדקה או פחות. - שאלה: הרצתם
docker stop mycontainer, אבל אחר כךdocker run mycontainerנותן שגיאה "No such image." למה, ואיך תתקנו?
תשובה:docker stopעוצר container, לא מוחק image. הבעיה היא ש-mycontainerהוא שם של container, לא של image.docker run mycontainerמחפש image בשם הזה, לא container. התיקון:docker run -p 8080:80 nginx(או שם ה-image האמיתי שלכם). אם אתם רוצים להריץ מחדש את אותו container, תחילהdocker start mycontainer(מתחיל אותו מחדש), לאdocker run. - שאלה: עמית שלכם עובד בסטארטאפ של 30 אנשים עם $4M הכנסה. הוא שואל אם הוא צריך לשלם על Docker Desktop. מה תענו לו?
תשובה: לא, הוא לא צריך לשלם. הסף הוא פחות מ-250 עובדים וגם פחות מ-$10M הכנסה — הוא עומד בשניהם. Docker Desktop חינמי. אם יום אחד החברה תגיע ל-260 עובדים, הם יצטרכו להחליט בין Pro ($11/user/mo) לבין Rancher Desktop (חינמי, Apache-2.0). - שאלה: למה
EXPOSE 80ב-Dockerfile לא מספיק כדי שהפורט יהיה נגיש מהדפדפן, ומה עוד צריך?
תשובה:EXPOSEהוא instruction תיעודי בלבד — הוא אומר ל-Docker "ה-container הזה מתכוון להאזין על 80", אבל לא עושה שום דבר ברשת. כדי שהפורט באמת יהיה נגיש מהמארח, אתם חייבים להעביר-p host:containerבפקודתdocker run(למשל,docker run -p 8080:80 myapp), אוports:ב-compose.EXPOSEלבדו לא פותח כלום.
- Docker Desktop מותקן ועובד — אישור עם
docker run hello-worldשמחזיר את ההודעה המוכרת. אם אתם על Mac, ייתכן שהתקנתם OrbStack או Rancher Desktop במקום — זה תקין, ה-CLI זהה. - container ראשון רץ ונגיש ב-browser —
docker run -p 8080:80 nginxופתיחה ב-http://localhost:8080מציגה את דף ה-Welcome של nginx. זו הוכחה ש-port mapping, 0.0.0.0, והרשת כולה עובדים אצלכם. - דף-עזר אישי של 6 הפעלים (
run,ps,logs,stop,rm,exec) — פיזית (sticky note) או דיגיטלית (Notepad / Notes / Google Doc). כל פעל עם דוגמה אחת מהתרגול שלכם. ממוספר לפי סדר השימוש: קודםrun(להריץ), אחר כךps(לראות), אחר כךlogs(לאבחן), אחר כךstop/rm(לסיים), ולבסוףexec(להיכנס פנימה). - הוכחת עבודה עם 2 containers במקביל — הרצת
web1על 8080 ו-web2על 9090, שניהם מ-image של nginx, שניהם נגישים ב-browser. ההוכחה הויזואלית ש-image ≠ container (אותו image, שתי ריצות, שתי כתובות). - 4 בדיקות סיום שעברו (מסעיף 11) — Docker Desktop רץ,
docker --versionעובד,hello-worldעובד,nginxב-browser עובד. רשימה של 4 V-ים, מוכנה לפרק 3.
בפרק 3 (לארוז את האפליקציה — Dockerfile, docker init, ו-images קטנים) נעבור מ-"להריץ images של אחרים" ל-"לבנות image משלי." תכירו את docker init — פקודה שמזהה את השפה של הפרויקט שלכם (Python, Node.js, Go, וכו') ויוצרת Dockerfile אוטומטית, מוכן ל-production. אחר כך תלמדו לקרוא ולתקן את ה-Dockerfile שורה-שורה: FROM (איזה base), WORKDIR (איפה הקוד נמצא), COPY (להעתיק פנימה), RUN (להתקין dependencies), EXPOSE (תיעוד), CMD (מה רץ). תלמדו להקטין את ה-image (base קטן במקום הדביאן המלא), לסדר את השכבות נכון (cache), ולוודא שהאפליקציה שלכם מאזינה על 0.0.0.0. הפרק הזה היה "להריץ"; פרק 3 הוא "לארוז".
- ☐ Docker Desktop מותקן ורץ (אייקון לוויתן בתפריט / system tray, ללא שגיאות).
- ☐
docker --versionמחזיר מספר גרסה (למשל, 27.x.x) בלי שגיאה. - ☐
docker run hello-worldמחזיר את ההודעה המוכרת של "Hello from Docker!". - ☐
docker run -p 8080:80 nginxופתיחה ב-http://localhost:8080מציגה את דף ה-Welcome של nginx. - ☐ תרגלתי את 6 הפעלים היומיומיים (
run,ps,logs,stop,rm,exec) בסדר הנכון לפחות פעם אחת. - ☐ הצלחתי להריץ 2 containers של nginx במקביל (
web1על 8080,web2על 9090) ולראות את שניהם ב-browser. - ☐ אני יכול להסביר בלי להציץ: למה
-p 8080:80אומר "8080 במארח, 80 ב-container" ולא ההפך. - ☐ אני יכול להסביר בלי להציץ: למה האפליקציה חייבת להאזין על
0.0.0.0ולא על127.0.0.1כדי ש--pיעבוד. - ☐ אני יכול לומר: האם Docker Desktop חינמי במצב שלי, ולמה (להזכיר את הסף 250/$10M).
- ☐ אם אני על Windows — אימתי שהפרויקט שלי נמצא בתוך
~/ב-WSL ולא ב-/mnt/c/. - ☐ אם אני על Mac — בחרתי בין Docker Desktop, OrbStack, ו-Rancher Desktop ואני יודע למה.
- ☐ הכנתי דף-עזר אישי (פיזי או דיגיטלי) של 6 הפעלים עם דוגמה אחת לכל אחד, ושמרתי אותו ליד המקלדת.
- ☐ אני מרגיש/ה שאני מוכן/ה לפרק 3 (לארוז את האפליקציה). אם לא — חוזר/ת לסעיף הרלוונטי.