GitOps مع ArgoCD وFlux

تطبيقات ArgoCD والمزامنة

18 دقيقة الدرس 4 من 30

تطبيقات ArgoCD والمزامنة

يُعدّ التطبيق (Application) الوحدة الأساسية في ArgoCD. يربط التطبيقُ مصدرًا في Git (مستودع + مسار + مراجعة مستهدفة) بوجهة في Kubernetes (كلستر + فضاء أسماء)، ويقارن باستمرار ما هو موجود في Git بما يعمل فعليًا في الكلستر. فهم آلية عمل حلقة المقارنة هذه وكيفية ضبطها هو الفارق بين سير عمل GitOps تثق به الفرق وسير عمل يولّد ضجيجًا وجهدًا يدويًا.

مانيفست التطبيق

تعرّف التطبيق كمورد Kubernetes مخصص (النوع Application، مجموعة API هي argoproj.io/v1alpha1) وتحفظه في Git، أو تنشئه عبر CLI أو واجهة المستخدم. الحقول الجوهرية هي spec.source (موقع الحالة المرغوبة)، وspec.destination (موضع التطبيق)، وspec.syncPolicy (كيفية وموعد المصالحة).

# apps/my-api.yaml — محفوظ في مستودع GitOps apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: my-api namespace: argocd # فضاء أسماء مستوى التحكم في ArgoCD finalizers: - resources-finalizer.argocd.argoproj.io # حذف متتالي عند الإزالة spec: project: default source: repoURL: https://github.com/acme/k8s-config.git targetRevision: HEAD # تتبع main؛ أو التثبيت على وسم/SHA path: apps/my-api/overlays/production destination: server: https://kubernetes.default.svc # داخل الكلستر namespace: my-api-prod syncPolicy: automated: prune: true # حذف الموارد المحذوفة من Git selfHeal: true # التراجع عن التغييرات خارج النطاق syncOptions: - CreateNamespace=true # إنشاء فضاء الأسماء إن لم يكن موجودًا - PrunePropagationPolicy=foreground - ApplyOutOfSyncOnly=true retry: limit: 5 backoff: duration: 5s factor: 2 maxDuration: 3m
المفهوم الأساسي: ArgoCD هو متحكّم يعمل على مستوى الحالة (level-triggered) لا على مستوى الأحداث (event-driven). يشغّل حلقة مصالحة (استطلاع افتراضي كل 3 دقائق، أو فورًا عند استلام webhook) تسعى دائمًا إلى تقارب حالة الكلستر مع حالة Git. تتحكّم سياسة المزامنة في كيفية حدوث هذا التقارب — تلقائيًا أو بموافقة بشرية.

سياسات المزامنة: يدوية مقابل تلقائية

بدون syncPolicy.automated، يبقى التطبيق في حالة OutOfSync عند اكتشاف الانجراف لكنه لا يتصرف تلقائيًا. يجب على المهندس النقر على "Sync" في واجهة المستخدم أو تشغيل argocd app sync my-api. هذا هو الإعداد الافتراضي الصحيح لبيئات الإنتاج التي تستلزم بوابة بشرية — المزامنة التلقائية في الإنتاج قرار يتعلق بنضج الفريق وثقته، لا بالإمكانية التقنية.

عند تفعيل automated، تُزامن ArgoCD فورًا عند انجراف الحالة الحية عن Git. الحقلان الفرعيان الأهم:

  • prune: true — تُحذف الموارد الموجودة في الكلستر لكنها لم تعد موجودة في Git. بدون هذا الخيار، تترك المانيفستات المحذوفة نشرات وخدمات وConfigMaps يتيمة تعمل خفية إلى الأبد — مصدر شائع لمفاجآت أمنية وتكلفية. فعّله وادمجه مع PrunePropagationPolicy=foreground لحذف التوابع قبل أصولها.
  • selfHeal: true — إذا شغّل أحدهم kubectl apply مباشرةً على الكلستر ناقضًا بذلك عقد GitOps، تكتشف ArgoCD الانجراف خلال دورة الاستطلاع وتعيد الحالة الحية إلى Git في غضون ثوانٍ. هذا ما يُرسّخ ضمان مصدر الحقيقة الوحيد.
خطأ إنتاجي شائع: لا تفعّل automated + prune في الإنتاج منذ اليوم الأول دون مراجعة الموارد الموجودة في الكلستر مقابل ما هو متتبَّع في Git. ستحذف ArgoCD بإخلاص كل شيء لا تجده في المسار المستهدف — بما في ذلك موارد تابعة لأنظمة أخرى أو أُنشئت يدويًا. حدّد هذه الموارد اليتيمة وأدرجها في Git أولًا.

موجات المزامنة: ترتيب النشر

يطبّق Kubernetes جميع الموارد في عملية مزامنة بشكل متزامن افتراضيًا. يخلق ذلك مشكلات عندما يكون الترتيب مهمًا: نشر يعتمد على وجود ConfigMap، أو Job يجب أن يكتمل قبل بدء التطبيق، أو CRD يجب أن يكون موجودًا قبل نسخه. تحلّ ArgoCD هذه المشكلة عبر موجات المزامنة (sync waves).

عيّن رقم موجة لأي مورد عبر التعليق التوضيحي argocd.argoproj.io/sync-wave. تُطبَّق الموارد في الموجة -1 أولًا، ثم الموجة 0 (الافتراضية)، ثم الموجة 1، وهكذا. تنتظر ArgoCD حتى تصبح جميع موارد الموجة N سليمة قبل المضي في الموجة N+1.

# 1. فضاء الأسماء و RBAC أولًا (الموجة -2) apiVersion: v1 kind: Namespace metadata: name: my-api-prod annotations: argocd.argoproj.io/sync-wave: "-2" --- # 2. Secrets / ConfigMaps قبل Pods (الموجة -1) apiVersion: v1 kind: ConfigMap metadata: name: my-api-config namespace: my-api-prod annotations: argocd.argoproj.io/sync-wave: "-1" data: LOG_LEVEL: "info" DB_HOST: "postgres.my-api-prod.svc.cluster.local" --- # 3. Job ترحيل قاعدة البيانات قبل النشر (الموجة 0) apiVersion: batch/v1 kind: Job metadata: name: my-api-migrate namespace: my-api-prod annotations: argocd.argoproj.io/sync-wave: "0" argocd.argoproj.io/hook: Sync argocd.argoproj.io/hook-delete-policy: BeforeHookCreation spec: template: spec: restartPolicy: Never containers: - name: migrate image: acme/my-api:1.4.2 command: ["./migrate", "--run"] --- # 4. النشر الرئيسي في النهاية (الموجة 1) apiVersion: apps/v1 kind: Deployment metadata: name: my-api namespace: my-api-prod annotations: argocd.argoproj.io/sync-wave: "1" spec: replicas: 3 selector: matchLabels: app: my-api template: metadata: labels: app: my-api spec: containers: - name: my-api image: acme/my-api:1.4.2 ports: - containerPort: 8080
ArgoCD Sync Wave Execution Order Wave -2 Namespace RBAC سليم؟ ✓ متابعة Wave -1 ConfigMap Secret سليم؟ ✓ متابعة Wave 0 DB Migrate Job اكتمل؟ ✓ متابعة Wave 1 Deployment Service اكتمل الطرح؟ ✓ مُزامَن ترتيب تنفيذ موجات المزامنة — مرتّب ومحكوم بفحوصات الصحة
تطبّق ArgoCD الموجات بشكل متسلسل، مع التحقق من الصحة قبل الانتقال إلى الموجة التالية.

التحكم في المزامنة من سطر الأوامر

يُعدّ سطر أوامر argocd أساسيًا لبرمجة عمليات المزامنة في خطوط أنابيب CI والاستجابة للحوادث. الأوامر الرئيسية:

# التحقق من الحالة الحالية للتطبيق argocd app get my-api # تشغيل مزامنة يدوية (تحترم syncPolicy الحالية) argocd app sync my-api # المزامنة والانتظار حتى يصبح التطبيق سليمًا argocd app sync my-api --timeout 180 # إجبار المزامنة حتى لو كانت الحالة Synced (إعادة تطبيق الكل) argocd app sync my-api --force # مزامنة موارد محددة فقط (مفيد للطرح المستهدف) argocd app sync my-api --resource apps:Deployment:my-api # المزامنة مع مراجعة محددة دون تعديل مانيفست التطبيق argocd app sync my-api --revision v1.4.2 # تحديث صعب يعيد استنساخ Git ويتجاوز ذاكرة التخزين المؤقت argocd app get my-api --hard-refresh # التراجع إلى مراجعة سابقة (من سجل ArgoCD لا من Git) argocd app history my-api argocd app rollback my-api 3 # 3 هو معرف السجل من الأمر السابق
ممارسة إنتاجية: في خطوط أنابيب CI/CD، استخدم argocd app sync --timeout مقترنًا بـargocd app wait --health كبوابة ما بعد النشر. يوقف هذا خط الأنابيب حتى يصل الطرح إلى حالة Healthy، مما يمنحك إشارة حقيقية بأن النسخة الجديدة تخدم الحركة قبل أن يعلن خط الأنابيب نجاح النشر. ادمجه مع تنبيهات على انتقالات Degraded في منظومة المراقبة.

خيارات المزامنة الجديرة بالمعرفة

تتيح لك قائمة spec.syncOptions ضبط السلوك لكل تطبيق على حدة:

  • ApplyOutOfSyncOnly=true — يتخطى تطبيق الموارد المتزامنة بالفعل. يُسرّع المزامنة بشكل كبير للتطبيقات الكبيرة التي تضم مئات الموارد. فعّله لجميع التطبيقات التي تتجاوز ~20 موردًا.
  • ServerSideApply=true — يستخدم kubectl apply --server-side بدلًا من apply من جهة العميل. يُلغي خطأ "التعليق التوضيحي طويل جدًا" على CRDs وConfigMaps الكبيرة، ويتعامل بشكل صحيح مع ملكية الحقول عندما تديرها عدة متحكمات.
  • RespectIgnoreDifferences=true — يأمر محرك المزامنة باحترام قواعد spec.ignoreDifferences عند تقرير تطبيق مورد أم لا، لا فقط عند حساب حالة الفروق. يمنع إعادة التطبيق غير الضرورية على الموارد التي تديرها webhooks الاستيعاب.
  • Replace=true (تعليق توضيحي على مستوى المورد) — يُجبر على تشغيل kubectl replace بدلًا من apply للحقول غير القابلة للتغيير مثل spec.selector في Job. استخدمه بحذر — يحذف المورد ويعيد إنشاءه.
ignoreDifferences: كثيرًا ما تُعدّل Webhooks والمتحكمات الموارد بعد التطبيق (إضافة تعليقات توضيحية، تعيين قيم افتراضية). تكتشف ArgoCD هذه التغييرات كانجراف وتُشغّل مصالحة مستمرة. استخدم spec.ignoreDifferences لاستثناء مسارات JSON pointer محددة — مثل حقل caBundle الذي تحقنه cert-manager في كائنات MutatingWebhookConfiguration — لكي تتوقف ArgoCD عن الإبلاغ عن انجراف وهمي على موارد لا تملكها بالكامل.

حلقة التغذية الراجعة للإصلاح الذاتي

مع تفعيل selfHeal: true، تعمل حلقة المصالحة باستمرار. عندما يشغّل مشغّل كلستر الأمر kubectl scale deployment my-api --replicas=1 خلال حادث طارئ، تكتشف ArgoCD انجراف عدد النسخ خلال دورة الاستطلاع التالية (ما يصل إلى 3 دقائق افتراضيًا، أو أسرع إن كان تسليم webhook سريعًا) وتُعيد النشر إلى عدد النسخ المُعلَن في Git. هذا مقصود — مصدر الحقيقة هو Git لا الكلستر الحي. إن احتجت إلى تجاوز مؤقت، فالإجراء الصحيح هو إيداع التجاوز في Git، والسماح لـArgoCD بتطبيقه، ثم التراجع عن الإيداع حين تنتهي الحادثة. يترك هذا مسارًا للتدقيق؛ أما أمر kubectl المجرد فلا يترك شيئًا.