העברה ממכונות וירטואליות לקונטיינרים (VM → Docker/K8s)
העברת אפליקציה מ-VM לקונטיינרים היא משימה שכל צוות פוגש במוקדם או במאוחר. בניסיון שלנו, ראינו כיצד אפליקציות legacy שרצו שנים על שרתים ייעודיים נשברו בקונטיינרים בגלל תלויות לא ברורות. המהנדסים שלנו עזרו לעשרות לקוחות לקצר את זמן הפריסה פי 3 ולחסל בעיות סביבה. להלן מתודולוגיה שלב-אחר-שלב שהוכחה על פרויקטים עם 50+ מיקרוסרוויסים.
למה קונטיינריזציה עדיפה על פני VM
VM דורשת התקנת מערכת הפעלה, הגדרת מאגרי חבילות, עדכוני קרנל—באופן ידני או דרך Ansible. קונטיינר נבנה פעם אחת ורץ בכל מקום. לפי הנתונים שלנו, פיתוח על קונטיינרים מאיץ מחזורי CI/CD ב-40%. ב-Kubernetes מקבלים autoscaling (HPA/VPA), ריפוי עצמי, ועדכונים מתגלגלים מובנים. בנוסף, חיסכון ברישיונות מערכת הפעלה מסתכם בהפחתה של 30–50% בעלויות התשתית.
התחלה עם מלאי VM
לפני כתיבת ה-Dockerfile הראשון, צריך להבין בדיוק מה רץ על ה-VM. לעיתים קרובות, מכונת ייצור אחת מאחסנת: nginx כ-reverse proxy, האפליקציה עצמה (Node.js), PostgreSQL, Redis, עבודת cron לגיבויים, וסוכן דואר מקומי Postfix. אלה שבעה קונטיינרים נפרדים אם עושים זאת נכון.
# Что запущено на VM
systemctl list-units --type=service --state=running
ps auxf | grep -v grep
# Какие порты слушают
ss -tlnp
# Какие крон-задачи
crontab -l
ls /etc/cron.d/ /etc/cron.daily/
# Зависимости приложения (пример для Node.js)
cat package.json | jq '.dependencies | keys[]'
node --version; npm --versionהתוצאה של המלאי היא רשימה של כל מה שצריך להעביר, עם הבנה של התלויות בין הרכיבים.
מלכודות טיפוסיות ב-Dockerfile
לוגים. האפליקציה כותבת ל-# Что запущено на VM systemctl list-units --type=service --state=running ps auxf | grep -v grep # Какие порты слушают ss -tlnp # Какие крон-задачи crontab -l ls /etc/cron.d/ /etc/cron.daily/ # Зависимости приложения (пример для Node.js) cat package.json | jq '.dependencies | keys[]' node --version; npm --version . בקונטיינר, לוגים חייבים ללכת ל-stdout/stderr—אחרת Docker daemon, Fluentd, או Loki לא יאספו אותם. אפשרות אחת: להוסיף קישור סימלי /var/log/app/access.log ב-Dockerfile. אפשרות טובה יותר: לתקן את הגדרות הלוגינג של האפליקציה עצמה.
הגדרה דרך משתני סביבה. נתיב קשיח ln -sf /dev/stdout /var/log/app/access.log חייב להיות מוחלף במשתני ENV או בהרכבת volume. Twelve-Factor App הוא לא רק קונספט אופנתי—זו דרישה לפעולה תקינה ב-K8s.
משתמשים והרשאות. האפליקציה רצה כ-root על ה-VM. בקונטיינר, זו סיכון אבטחתי. הוסף משתמש לא-מורשה:
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine
RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --chown=appuser:appgroup . .
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]
מערכת init. האפליקציה משתמשת ב-supervisord או יוצרת תהליכי ילד ישירות. בקונטיינר, PID 1 חייב לטפל נכון ב-SIGTERM ולהעביר אותות לתהליכי הילד. אם האפליקציה לא יכולה, השתמש ב-tini כ-init:
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "server.js"]נתונים עם מצב (Stateful). האפליקציה כותבת קבצים שהועלו ל-/etc/app/config.yaml. בקונטיינר, זה חייב להיות מועבר ל-PersistentVolume (K8s) או להיות מורכב דרך FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production FROM node:20-alpine RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser WORKDIR /app COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules COPY --chown=appuser:appgroup . . USER appuser EXPOSE 3000 CMD ["node", "server.js"] (Docker). נתונים לא חייבים לחיות בתוך הקונטיינר.
מניפסט Kubernetes טיפוסי
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-backend
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
containers:
- name: backend
image: registry.internal/app-backend:1.2.3
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
- name: REDIS_URL
value: "redis://redis-service:6379"
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
volumeMounts:
- name: uploads
mountPath: /app/uploads
volumes:
- name: uploads
persistentVolumeClaim:
claimName: app-uploads-pvc
משאבים חסרים הם אחת הטעויות הנפוצות ביותר בפריסה הראשונה ל-K8s. ללא limits, pod יכול לצרוך את כל הזיכרון על הצומת; ללא requests, ה-scheduler לא יכול לחלק pods בצורה נכונה.
Cron Jobs → CronJob
cron מערכתי מועבר ל-K8s CronJob:
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: db-backup
spec:
schedule: "0 2 * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: registry.internal/db-backup:latest
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
RUN apk add --no-cache tini ENTRYPOINT ["/sbin/tini", "--"] CMD ["node", "server.js"] מונע התחלת עבודה חדשה אם הקודמת עדיין רצה.
הבטחת אמינות במהלך ההעברה
אנו מבטיחים שאחרי המעבר כל מדדי SLO המרכזיים יהיו תחת שליטה. הגדרת ניטור Prometheus+Grafana היא חלק מהחבילה הסטנדרטית. כל התצורות מנוהלות בגרסאות ב-Git—זה מבטל את הסיכון לאובדן שינויים. הצוות שלנו מורכב ממנהלי Kubernetes מוסמכים ומהנדסי ענן עם ניסיון של 10+ שנים. השלמנו מעל 30 העברות מוצלחות עבור מוצרי fintech, מסחר אלקטרוני ו-SaaS.
| קריטריון | VM | קונטיינרים (Docker/K8s) |
|---|---|---|
| זמן אתחול | 30–60 שניות | 1–2 שניות |
| ניצול משאבים | מערכת הפעלה כבדה, בידוד hypervisor | תהליך קל, קרנל משותף |
| עלות (TCO) | גבוהה יותר בגלל רישיונות ו-over-provisioning | נמוכה ב-30–50% דרך מיקום קומפקטי |
מה כולל שירות ההעברה שלנו
- ביקורת תשתית VM ומפת תלויות
- Dockerfile לכל מיקרוסרוויס עם build רב-שלבי
- Helm charts לפריסת K8s
- הגדרת CI/CD (GitLab CI / GitHub Actions)
- ניטור והתראות (Prometheus, Loki, Grafana)
- תיעוד תפעולי ו-runbook
- העברת ידע לצוות הלקוח (הדרכה של 1–2 ימים)
באיזו אסטרטגיית העברה לבחור?
הרצף הנכון לאפליקציית ייצור:
- קונטיינריזציה — כתיבת Dockerfile, בדיקה מקומית
- CI לתמונה — בנייה ודחיפה ל-registry בכל merge ל-main
- פריסה ל-staging על K8s, הרצת בדיקות
- ריצה מקבילה — ה-VM הישן וה-pods החדשים רצים במקביל, התעבורה נשארת על ה-VM
- העברת תעבורה — Ingress או DNS מצביעים על K8s Service
- תצפית — שבוע בייצור, השוואת מדדים לבסיס
- הורדת VM משימוש — לאחר אישור יציבות
לעולם אל תעביר תעבורה ותכבה את ה-VM באותו זמן. זה נראה מובן מאליו, אבל בלחץ של דדליין, קל לשכוח.
מוכנים להתחיל את ההעברה? קבלו ייעוץ עם המהנדס שלנו.
לוחות זמנים
| מורכבות האפליקציה | זמן קונטיינריזציה | זמן העברת K8s |
|---|---|---|
| פשוטה (תהליך 1, ללא מצב) | 1–2 ימים | 2–3 ימים |
| בינונית (2–3 רכיבים, עם מצב) | 3–5 ימים | שבוע |
| מורכבת (5+ רכיבים, תלויות legacy) | 1–2 שבועות | 2–4 שבועות |
ביקורת תשתית VM ראשונית והערכה אורכים 1–2 ימים. לוחות זמנים סופיים נקבעים לאחר מכן. הזמינו ביקורת על תשתית ה-VM שלכם—נכין תוכנית העברה תוך 1–2 ימים.







