Compose و Kubernetes يستطيعان تشغيل نفس ثلاث عمليات: واجهة ويب و API و Postgres. Compose هو مشروع على محرك واحد. Kubernetes هو مجموعة كائنات API يستطيع لوحة التحكم إعادة جدولتها. أدناه compose.yaml واحد، الـ manifests التي تطابقها، والنقطة التي يجب على فريق أن يغير فيها الأدوات.
الجواب المختصر
اختر Compose عندما فقدان الآلة يعني التطبيق معطل، وهذا مقبول. اختر Kubernetes عندما تستطيع عقدة أن تختفي و Pod يجب أن يبدأ في مكان آخر، أو عندما تشغل عنقود بالفعل. كلا الأداتين تبدآ حاويات الويب و API و قاعدة البيانات. فقط Kubernetes لديها scheduler و Service يتتبع Pods الجاهز و تاريخ rollout.
أسماء خدمات Compose هي أسماء DNS على شبكة المشروع. أسماء Service Kubernetes هي أسماء DNS داخل namespace (api و api.demo.svc.cluster.local). http://api:3000 يعمل في كلا الملفات إذا كان Service مسمى api ويستمع على port 3000.
جنبًا إلى جنب
| القضية | compose.yaml | Kubernetes |
|---|---|---|
| عملية الويب و API و قاعدة البيانات | services.web و services.api و services.postgres |
Deployment + Pod template |
| منفذ الواجهة المنشورة | ports: ["8080:80"] على web |
ClusterIP Service ثم kubectl port-forward أو Ingress |
| عنوان API | http://api:3000 |
Service api port 3000 |
| إعدادات غير سرية | environment |
ConfigMap app-config |
| كلمة المرور | متغير .env |
Secret app-secret |
| قرص قاعدة البيانات | volumes مسماة pgdata |
PersistentVolumeClaim pgdata |
| ترتيب البدء | depends_on مع service_healthy |
readinessProbe؛ العملاء يحاولون مجددًا |
| فحص الصحة | healthcheck داخل الحاوية |
readinessProbe و livenessProbe، تشغيل من قبل kubelet |
| إعادة التشغيل | restart: unless-stopped |
Deployment يعيد إنشاء Pod (restartPolicy: Always) |
| نسخ إضافية من API | docker compose up --scale api=3 |
spec.replicas |
| البناء | docker build ثم وسم صورة |
نفس مرجع الصورة؛ العنقود لا يبني |
ملف Compose
change-me تقف بدلاً من كلمة مرور. ضعها في ملف .env بجانب ملف Compose واترك هذا الملف خارج git. Compose يقرأ .env تلقائيًا. تفاصيل الأوامر تعيش في Docker Compose cheat sheet.
services:
web:
image: example/web:1.4.0
ports:
- "8080:80"
environment:
API_URL: http://api:3000
depends_on:
api:
condition: service_healthy
restart: unless-stopped
api:
image: example/api:1.4.0
environment:
DATABASE_URL: postgres://app:${POSTGRES_PASSWORD}@postgres:5432/app
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/health"]
interval: 10s
timeout: 3s
retries: 5
start_period: 10s
restart: unless-stopped
postgres:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: app
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 10
restart: unless-stopped
volumes:
pgdata:
فحص wget هذا يعمل داخل الصورة، إذًا الملف الثنائي يجب أن يكون هناك. بناء وسوم مع Dockerfile cheat sheet.
# --wait يرجع non-zero إذا healthcheck أبدًا لم يمرّ
docker compose up -d --wait
docker compose ps
docker compose down -v
down -v يحذف volume pgdata. استخدمها في CI. لا تستخدمها ضد قاعدة بيانات كنت تقصد الاحتفاظ بها.
manifests Kubernetes
احفظ هذا كـ shop.yaml وطبّقه مع kubectl. قيمة Secret يجب أن تطابق POSTGRES_PASSWORD أو API لا تستطيع تسجيل الدخول. على عنقود مشترك، أنشئ Secret خارج repo git وحذف كتلة stringData.
apiVersion: v1
kind: Namespace
metadata:
name: demo
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: demo
data:
API_URL: "http://api:3000"
POSTGRES_USER: app
POSTGRES_DB: app
---
apiVersion: v1
kind: Secret
metadata:
name: app-secret
namespace: demo
type: Opaque
stringData:
POSTGRES_PASSWORD: change-me
DATABASE_URL: "postgres://app:change-me@postgres:5432/app"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pgdata
namespace: demo
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
securityContext:
fsGroup: 999
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
env:
- name: POSTGRES_USER
valueFrom:
configMapKeyRef: {name: app-config, key: POSTGRES_USER}
- name: POSTGRES_DB
valueFrom:
configMapKeyRef: {name: app-config, key: POSTGRES_DB}
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef: {name: app-secret, key: POSTGRES_PASSWORD}
readinessProbe:
exec:
command: ["pg_isready", "-U", "app", "-d", "app"]
periodSeconds: 5
livenessProbe:
exec:
command: ["pg_isready", "-U", "app", "-d", "app"]
initialDelaySeconds: 15
periodSeconds: 10
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: pgdata
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: demo
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: example/api:1.4.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef: {name: app-secret, key: DATABASE_URL}
readinessProbe:
httpGet: {path: /health, port: 3000}
periodSeconds: 10
resources:
requests: {cpu: "100m", memory: 128Mi}
limits: {memory: 256Mi}
---
apiVersion: v1
kind: Service
metadata:
name: api
namespace: demo
spec:
selector:
app: api
ports:
- port: 3000
targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: example/web:1.4.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
env:
- name: API_URL
valueFrom:
configMapKeyRef: {name: app-config, key: API_URL}
readinessProbe:
httpGet: {path: /, port: 80}
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: web
namespace: demo
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
probes httpGet تعمل على kubelet، إذًا تلك الصور لا تحتاج wget. fsGroup: 999 يسمح لصورة Postgres الرسمية كتابة volume. imagePullPolicy: IfNotPresent يناسب وسم محمّل محليًا؛ :latest لا يزال افتراضي Always ويتصل بـ registry.
kubectl apply -f shop.yaml
kubectl rollout status deployment/api -n demo
kubectl port-forward -n demo svc/web 8080:80
port-forward يحل محل 8080:80 من Compose على محمولك. آلات أخرى يجب أن تستخدم Ingress.
PVC بدون StorageClass تبقى Pending (Pod Pending / FailedScheduling). حد صغير جدًا هو OOMKilled. سيء DATABASE_URL هو CrashLoopBackOff. صورة مفقودة هي ImagePullBackOff.
اترك Postgres على نسخة واحدة. ReadWriteOnce لن تربط نفس القرص على عقدتين. استخدم StatefulSet و volumeClaimTemplates لاحقًا إذا احتجت اسم مستقر وقرص واحد لكل نسخة.
متى تختار Compose
اختر Compose عندما جميع هذه صحيح:
- Stack يناسب آلة واحدة، بما في ذلك قرص Postgres.
- سياسة إعادة التشغيل كافية عندما عملية تخرج.
- المطورون و CI يشغّلون
docker compose upبالفعل. - Pod لا يحتاج الانتقال إلى عقدة أخرى.
احفظ Compose للحلقة الداخلية حتى بعد الإنتاج هو عنقود. تغيير ملف و docker compose up أقصر من push و rollout.
متى تختار Kubernetes
اختر Kubernetes عندما واحد من هذه صحيح:
- تحتاج أكثر من عقدة واحدة أو عقدة يمكن تصريفها بينما API يبقى يخدم.
- تريد rollout مسجّل و
kubectl rollout undoلكل Deployment. - عدة بيئات يجب أن تشارك نفس manifests، مع اختلافات صغيرة يتعامل معها Helm أو Kustomize.
- فريق المنصة يشغّل العنقود بالفعل ويتوقع requests و limits و probes.
نسخة ثانية ليست سبب كافٍ. --scale بالفعل يبدأ حاويات API إضافية على محرك واحد. انتقل عندما التموضع أو الترقيات أو السياسة مهمة.
الطريق الوسيط
Compose في CI. ابدأ stack، فشّل الوظيفة إذا healthchecks لم تمرّ، ثم احذف volumes:
docker compose up -d --wait
docker compose ps
docker compose down -v
kind لـ لوحة تحكم محلية. حمّل صور الويب و API ثم طبّق shop.yaml. العقدة قد تسحب postgres:16. أي عنقود محلي الاستخدام مغطى في kind vs minikube vs k3d.
kind create cluster --name demo
kind load docker-image example/web:1.4.0 --name demo
kind load docker-image example/api:1.4.0 --name demo
kubectl apply -f shop.yaml
Kompose كمسودة وليس pipeline. من الدليل الذي يحتوي compose.yaml:
kompose convert -f compose.yaml
Kompose يكتب ملف YAML واحد لكل كائن في الدليل الحالي. راجع probes و كلمة المرور و imagePullPolicy و Service type قبل أن يطبق أي شخص آخر. Kubernetes لا تشغّل compose.yaml نفسها.
مسار الهجرة
- ثبّت وسوم الصور في CI. العنقود يجب أن يرجع إلى
example/api:1.4.0وليسlatestعائم وليس كتلةbuild:. - انقسم التكوين. Non-secrets تذهب إلى ConfigMap. كلمة المرور تذهب إلى Secret أنشئت خارج git على أي عنقود مشترك.
- حوّل كل healthcheck Compose إلى readiness probe. احفظ liveness probe فقط لفحص يعني “أعد تشغيل هذه الحاوية.”
- أنشئ PVC و انسخ البيانات مع backup خاص بك.
docker compose down -vبالإضافة إلى دعوة جديدة تسقط قاعدة البيانات. - طبّق
shop.yamlعلى kind. أصلح Pending pods و pulls صورة هناك. - أضف requests و limits و اعرض API تحت الحمل قبل نسخ الأرقام.
- ضع Ingress أمام web. اترك
kubectl port-forwardللتصحيح. - قرّر حيث Postgres يعيش قبل المستخدمين الانتقال: قاعدة بيانات مدارة أو StatefulSet مع backups.
- طبّق على العنقود المشترك و شغّل
kubectl rollout statusثم بدّل DNS.
الأسئلة الشائعة
هل Docker Compose نفسه Kubernetes؟
لا. Compose يبدأ الحاويات على محرك Docker واحد ويربطها بشبكة مشروع. Kubernetes يخزن Deployments و Services و volumes في API ويستطيع وضع Pods على عقد أخرى. الصورة قد تكون متطابقة. نموذج الكائن ليس كذلك.
متى يجب أن أنتقل من Docker Compose إلى Kubernetes؟
انتقل عندما تحتاج العملية للبقاء على قيد الحياة بعد فشل العقدة أو إطلاق بدون توقف أو مشاركة عنقود تشغله فرق أخرى بالفعل. ابقَ على Compose عندما تكون آلة واحدة وسياسة إعادة تشغيل ووظيفة CI كافية. شريحة تقول الإنتاج = Kubernetes ليست ضرورة.
هل Kompose ينتج manifests جاهزة للإنتاج؟
ينتج مسودة أولى. kompose convert -f compose.yaml يكتب Deployment و Service لكل خدمة Compose و PersistentVolumeClaim للـ volumes المسماة. تحرير probes و Secrets و حدود الموارد و Service type قبل أن يطبق أي شخص آخر الملفات.
ماذا يحل محل depends_on في Kubernetes؟
لا شيء ينتظر مرة واحدة عند البدء ثم يتنحى. readiness probe تزيل Pod من Service endpoints حتى تستطيع الخدمة وتفعل ذلك طوال حياة Pod. العملاء يجب أن يحاولوا مجددًا. init container يستطيع حجب app container لكنه لا يحل محل probe.
كيف تم التحقق من هذا الدليل
كُتب يدويًا من الوثائق الرسمية، وأُعيد فحص كل أمر، ثم رُوجع مرة ثانية قبل النشر. آخر مراجعة في .
المصادر الرسمية التي قُرئت لهذا المقال

