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 نفسها.

مسار الهجرة

  1. ثبّت وسوم الصور في CI. العنقود يجب أن يرجع إلى example/api:1.4.0 وليس latest عائم وليس كتلة build:.
  2. انقسم التكوين. Non-secrets تذهب إلى ConfigMap. كلمة المرور تذهب إلى Secret أنشئت خارج git على أي عنقود مشترك.
  3. حوّل كل healthcheck Compose إلى readiness probe. احفظ liveness probe فقط لفحص يعني “أعد تشغيل هذه الحاوية.”
  4. أنشئ PVC و انسخ البيانات مع backup خاص بك. docker compose down -v بالإضافة إلى دعوة جديدة تسقط قاعدة البيانات.
  5. طبّق shop.yaml على kind. أصلح Pending pods و pulls صورة هناك.
  6. أضف requests و limits و اعرض API تحت الحمل قبل نسخ الأرقام.
  7. ضع Ingress أمام web. اترك kubectl port-forward للتصحيح.
  8. قرّر حيث Postgres يعيش قبل المستخدمين الانتقال: قاعدة بيانات مدارة أو StatefulSet مع backups.
  9. طبّق على العنقود المشترك و شغّل 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.

كيف تم التحقق من هذا الدليل

كُتب يدويًا من الوثائق الرسمية، وأُعيد فحص كل أمر، ثم رُوجع مرة ثانية قبل النشر. آخر مراجعة في .

المصادر الرسمية التي قُرئت لهذا المقال

شاركها.

التعليقات مغلقة.