Docker מאפס — מדריך למתחילים
"אצלי על המחשב זה עובד." המשפט הזה גרם ליותר incidents ב-production מכל דבר אחר שאני יכול לחשוב עליו. הסיבה תמיד אותה הסיבה: הסביבה של ה-developer שונה מהסביבה שבה האפליקציה רצה. Node.js 18 על המחשב, Node.js 16 בproduction. ספרייה שמותקנת globally על המחשב, לא קיימת בשרת. SSL certificate שעובד locally, לא מוגדר בענן.
Docker פותר את זה באלגנטיות: ה-Application + כל ה-dependencies שלה נארזים יחד ל-Container שרץ אותו דבר בכל מקום — Windows, Mac, Linux, AWS, GCP.
Containers vs Virtual Machines
לפני שמבינים Docker, צריך להבין למה הוא שונה מVirtual Machine.
VM מפעילה OS שלם לכל process — גיגות של memory, זמן boot של דקות. Container משתף את ה-kernel של ה-Host OS — רק ה-app ו-dependencies שלה, עולה בשניות, שוקל עשרות MB.
| Virtual Machine | Docker Container | |
|---|---|---|
| גודל | GB+ (OS מלא) | עשרות-מאות MB |
| Boot time | דקות | שניות (לפעמים milliseconds) |
| Isolation | מלאה — OS נפרד | Process isolation, kernel משותף |
| Overhead | גבוה | נמוך |
| שימוש | Security isolation מלאה | Application deployment |
מושגי יסוד
Image — תבנית read-only. כמו blueprint. Image של Node.js 20 מכיל את Node.js, npm, ו-OS בסיסי (Alpine/Debian). Image לא רץ — הוא בסיס ליצירת Container.
Container — instance שרץ של Image. כמו process שנוצר מה-Image. אפשר להריץ כמה containers מאותו Image.
Dockerfile — script שמגדיר איך לבנות Image. כל שורה = שכבה (layer). Docker מcaches שכבות לא-שינויות.
Registry — אחסון ל-Images. Docker Hub הוא הpublic registry. AWS ECR, Google Container Registry — private registries.
פקודות שתשתמשו בהן מדי יום
# הורדת Image מDocker Hub
docker pull node:20-alpine
# הרצת Container (interactive + remove on exit)
docker run -it --rm node:20-alpine node --version
# הרצת app ב-background, עם port mapping ושם
docker run -d -p 3000:3000 --name my-api my-app:latest
# רשימת containers פעילים
docker ps
# רשימת כל containers (כולל stopped)
docker ps -a
# צפייה ב-logs (עם follow)
docker logs -f my-api
# כניסה לcontainer שרץ (debugging)
docker exec -it my-api sh
# עצירה והסרת container
docker stop my-api && docker rm my-api
# רשימת images מקומיים
docker images
# הסרת images לא-בשימוש (פינוי disk)
docker image prune -aשימו לב ל-p 3000:3000 — port mapping: HOST_PORT:CONTAINER_PORT. הcontainer חשוף מבפנים על port 3000, ואתם חושפים אותו מ-port 3000 של ה-host.
Dockerfile — מתכון לImage
# בסיס: Node.js 20 על Alpine Linux (קטן — ~50MB)
FROM node:20-alpine
# מניעת חלמון בריצה כ-root (security best practice)
RUN addgroup -S app && adduser -S app -G app
# הגדרת תיקיית עבודה בתוך Container
WORKDIR /app
# קודם מעתיקים RONLY את package files — לcaching
# כל עוד package.json לא השתנה, Docker מcaches npm ci
COPY package*.json ./
# התקנת dependencies בלבד (ללא devDependencies)
RUN npm ci --only=production
# עכשיו מעתיקים את שאר הקוד
COPY --chown=app:app . .
# החלפה למשתמש non-root
USER app
# הצהרה שה-container מאזין על port 3000
EXPOSE 3000
# הפקודה שרצה כש-Container מתחיל
CMD ["node", "dist/server.js"]סדר השכבות חשוב לביצועים: Docker מcaches כל layer. אם package.json לא השתנה, ה-npm ci לא ירוץ שוב — Docker ישתמש בcache. לכן: קודם dependencies (שמשתנות לאט), אחר כך קוד (שמשתנה מהר).
Docker Compose — כמה Services ביחד
ברוב הפרויקטים יש יותר מContainer אחד: app + PostgreSQL + Redis. Docker Compose מנהל את כולם בקובץ אחד.
# docker-compose.yml
services:
app:
build: .
ports:
- "3000:3000"
environment:
NODE_ENV: development
DATABASE_URL: postgres://postgres:secret@db:5432/myapp
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy # מחכה שDB יהיה ready
cache:
condition: service_started
volumes:
- .:/app # hot reload בdevelopment
- /app/node_modules # anonymous volume — לא overwrite node_modules
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data # persistent storage
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # init script
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
command: redis-server --appendonly yes # persistence
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:# הרמת כל הsystem (build + start)
docker compose up --build -d
# הורדה + הסרה של containers (volumes נשמרים)
docker compose down
# הורדה + הסרת volumes (cleanup מלא)
docker compose down -v
# צפייה ב-logs של service ספציפי
docker compose logs -f app
# הרצת command בcontainer
docker compose exec app npm run db:migrate
# restart service ספציפי
docker compose restart appdepends_on עם condition: service_healthy מחכה שה-healthcheck של DB יצלח לפני שמתחיל את ה-app. בלי זה, אפליקציה שמנסה להתחבר לDB לפני שהוא ready — תכשל ב-startup. זה detail קטן שחוסך הרבה debugging.
Multi-Stage Builds — Image קטן יותר ל-Production
Build tools, TypeScript compiler, test runners — כל אלה לא צריכים להיות בImage הproduction. Multi-stage build מאפשר לבנות בstage אחד ולהעתיק רק artifacts לstage ה-production.
# ---- Stage 1: Build ----
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci # כולל devDependencies
COPY . .
RUN npm run build # TypeScript compile
RUN npm run test # tests רצים בbuild time
# ---- Stage 2: Production ----
FROM node:20-alpine AS production
# רק production dependencies
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# מעתיקים רק את ה-build output מstage הראשון
COPY --from=builder /app/dist ./dist
RUN addgroup -S app && adduser -S app -G app
USER app
EXPOSE 3000
CMD ["node", "dist/server.js"]התוצאה: Image שמכיל רק Node.js, production dependencies, ו-compiled JS. ללא TypeScript, ללא test runners, ללא source maps. קטן יותר = deploy מהיר יותר = attack surface קטן יותר.
לעולם אל תשמרו secrets ישירות ב-Dockerfile או ב-docker-compose.yml שמועלה ל-git. DATABASE_URL, API keys, JWT secrets — תעברו אותם דרך environment variables, Docker secrets, או כלי כמו AWS Secrets Manager. Image שמכיל secrets = כל מי שיש לו גישה לimage יש לו את ה-secrets.
.dockerignore — לא כל קובץ שייך ל-Image
כמו .gitignore — קובץ שמגדיר מה לא להעתיק ל-Image:
# .dockerignore
node_modules
dist
.git
.env
.env.*
coverage
*.test.ts
*.spec.ts
README.md
docker-compose*.ymlבלי .dockerignore, COPY . . מעתיקה את node_modules (עשרות MB) ו-.env (עם secrets) לImage. הprimer לbuild איטי ו-security hole.
חידון
מה הבעיה בלשמור DATABASE_URL ישירות ב-Dockerfile?