السحابة المتعددة: Azure وGCP

Azure DevOps وAKS

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

Azure DevOps وAKS

تُقدّم Azure قصة DevOps متكاملة تمتد من التحكم في الكود المصدري عبر بناء الحاويات وصولًا إلى Kubernetes المُدار. ثلاث خدمات تُشكّل النواة: Azure Pipelines لـ CI/CD، وAzure Container Registry (ACR) لتخزين الصور الخاصة، وAzure Kubernetes Service (AKS) لـ Kubernetes المُدار. على نطاق شركات التقنية الكبرى، تتشابك هذه الخدمات مع Azure Active Directory (Entra ID)، والتحكم في الوصول المستند إلى الأدوار (RBAC)، والهويات المُدارة — بحيث لا تكون هناك كلمات مرور أو رموز طويلة الأمد في أي مكان ضمن السلسلة.

Azure Pipelines: CI/CD بدون تشتت الأسرار

Azure Pipelines هو منصة CI/CD المستضافة من Microsoft. تُعرَّف خطوط الأنابيب في YAML في جذر المستودع (azure-pipelines.yml) وتعمل على وكلاء مستضافة من Microsoft (Ubuntu وWindows وmacOS) أو مجموعات وكلاء مستضافة ذاتيًا. الفارق المعماري الرئيسي عن GitHub Actions هو أن Azure Pipelines تمتلك مفاهيم من الدرجة الأولى وهي البيئات (Environments) ووظائف النشر (Deployment Jobs) — مما يمنحك بوابات موافقة وأقفال حصرية وسجل نشر مدمج على مستوى المنصة، لا مضافًا في مرحلة لاحقة.

نمط المصادقة الموصى به بين Pipelines وخدمات Azure هو اتصال خدمة Workload Identity Federation (WIF). يحصل خط الأنابيب على رمز OIDC قصير الأجل من نقطة نهاية رمز Azure DevOps ويتبادله مع رمز وصول Azure AD عبر az login --federated-token — لا يُخزَّن أي سر لمبدأ الخدمة في أي مكان. هذا هو المعيار الحالي منذ 2024؛ النهج القديم بتخزين سر العميل في اتصال الخدمة يُعدّ الآن خطرًا على الامتثال.

# azure-pipelines.yml — خط أنابيب كامل: CI + دفع إلى ACR + نشر على AKS trigger: branches: include: [main] paths: exclude: ['**.md'] variables: imageRepository: 'api-service' containerRegistry: 'acmecr.azurecr.io' dockerfilePath: 'Dockerfile' tag: '$(Build.BuildId)' stages: - stage: Build displayName: Build & Push Image jobs: - job: BuildJob pool: vmImage: 'ubuntu-latest' steps: - task: Docker@2 displayName: Build and push to ACR inputs: command: buildAndPush repository: $(imageRepository) dockerfile: $(dockerfilePath) containerRegistry: 'acme-acr-service-connection' # اتصال WIF tags: | $(tag) latest - stage: DeployStaging displayName: Deploy to Staging dependsOn: Build jobs: - deployment: DeployToStaging environment: 'staging' # البيئة تحتوي على بوابات موافقة وسجل pool: vmImage: 'ubuntu-latest' strategy: runOnce: deploy: steps: - task: AzureCLI@2 displayName: Set AKS context inputs: azureSubscription: 'acme-aks-service-connection' scriptType: bash scriptLocation: inlineScript inlineScript: | az aks get-credentials \ --resource-group acme-rg \ --name acme-aks-staging \ --overwrite-existing kubectl set image deployment/api-service \ api-service=$(containerRegistry)/$(imageRepository):$(tag) \ --namespace staging kubectl rollout status deployment/api-service --namespace staging - stage: DeployProd displayName: Deploy to Production dependsOn: DeployStaging condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) jobs: - deployment: DeployToProd environment: 'production' # يتطلب موافقة يدوية pool: vmImage: 'ubuntu-latest' strategy: runOnce: deploy: steps: - task: AzureCLI@2 displayName: Rolling deploy to prod AKS inputs: azureSubscription: 'acme-aks-service-connection' scriptType: bash scriptLocation: inlineScript inlineScript: | az aks get-credentials \ --resource-group acme-rg \ --name acme-aks-prod kubectl set image deployment/api-service \ api-service=$(containerRegistry)/$(imageRepository):$(tag) \ --namespace production kubectl rollout status deployment/api-service \ --namespace production --timeout=300s
وسِّم الصور دائمًا بمعرّف البناء وحدِّث latest معه في آنٍ واحد. وسم معرّف البناء ثابت وقابل للتدقيق (يمكنك تتبع أي حاوية تعمل حتى تصل إلى تشغيل خط الأنابيب). وسم latest مؤشر مريح للتطوير المحلي. في مانيفيستات Kubernetes على المجموعة، استخدم الوسم الثابت — لا latest أبدًا — حتى يكون التراجع أمر واحد kubectl rollout undo لا إعادة بناء.

Azure Container Registry: ما هو أبعد من سجل بسيط

ACR أكثر بكثير من Docker Hub خاص. في المستوى Premium (المطلوب للإنتاج) يُضيف التكرار الجغرافي (geo-replication) والنقاط الطرفية الخاصة وثقة المحتوى وفحص الثغرات المدمج عبر Microsoft Defender for Containers. يعتمد نموذج المصادقة على الهويات المُدارة: تحصل هوية مجموعة عقد AKS (أو هوية kubelet المُدارة) على دور AcrPull على السجل، بحيث تستطيع Kubernetes سحب الصور بدون imagePullSecret. هذا هو نمط السحب بدون أسرار الذي يجب أن تستخدمه كل مجموعة AKS في الإنتاج.

# إنشاء ACR وربطه بـ AKS مع سحب صور بدون أسرار # (هوية kubelet المُدارة تحصل على AcrPull تلقائيًا) az acr create \ --resource-group acme-rg \ --name acmecr \ --sku Premium \ --location eastus az aks create \ --resource-group acme-rg \ --name acme-aks-prod \ --node-count 3 \ --node-vm-size Standard_D4s_v5 \ --enable-managed-identity \ --attach-acr acmecr \ # يمنح AcrPull لهوية kubelet تلقائيًا --enable-oidc-issuer \ # مطلوب لـ Workload Identity --enable-workload-identity \ --network-plugin azure \ --network-policy calico \ --generate-ssh-keys # التحقق من إنشاء تعيين الدور az role assignment list \ --scope $(az acr show --name acmecr --query id -o tsv) \ --query "[?roleDefinitionName=='AcrPull']"

أساسيات AKS: ما يُخفيه مستوى التحكم المُدار (وما لا يُخفيه)

تُدير AKS مستوى التحكم في Kubernetes (etcd وخادم API والجدولة ومتحكم الإدارة) مجانًا — تُصحّح Microsoft الأخطاء وتُحدّثه. أنت تدفع فقط للأجهزة الافتراضية لعُقَد العمل. لكن "مُدار" لا يعني "بدون عمليات": أنت لا تزال تمتلك تحجيم مجموعات العُقَد وقنوات الترقية وخيارات الشبكة وRBAC وأمان الحاويات والتوسع التلقائي وتهيئة أعباء العمل. القرارات التي تتخذها عند إنشاء المجموعة مكلفة التغيير لاحقًا.

AKS Architecture: Control Plane, Node Pools, and Integrated Services Microsoft-Managed API Server (kube-apiserver) etcd (HA, encrypted) Scheduler / Controllers Azure AD / Entra RBAC Customer-Managed (Worker Nodes) System Node Pool (kube-system workloads) coredns metrics-server azure-npm otelcollector User Node Pool(s) (application workloads) api-service 3 replicas worker-service 2 replicas Cluster Autoscaler (min=2, max=20) Azure Load Balancer / AGIC Azure Monitor / Container Insights ACR (AcrPull via MI) Azure Key Vault (CSI driver) Azure Virtual Network (CNI overlay) — each pod gets a VNet IP kubectl
تقسيم المسؤوليات في AKS: Microsoft تمتلك مستوى التحكم؛ أنت تمتلك مجموعات العُقَد والشبكات والتوسع التلقائي وتهيئة أعباء العمل.

قرارات AKS الحيوية في الإنتاج

مكوّن الشبكة: استخدم --network-plugin azure مع --network-plugin-mode overlay (CNI Overlay، وهو إصدار GA منذ 2024). يمنح هذا كل حاوية عنوان IP حقيقيًا في VNET دون استهلاك مساحة IP للشبكة الفرعية كما تفعل Azure CNI الكلاسيكية — وهي نقطة ألم معروفة تسببت في نفاد عناوين IP على نطاق واسع. اقرنه بـ --network-policy calico أو --network-policy azure لتطبيق سياسة الشبكة على مستوى الحاوية.

قناة الترقية: اضبط --auto-upgrade-channel patch للإنتاج. هذا يُطبّق تلقائيًا ترقيات الإصدار التصحيحي (مثل 1.29.3 → 1.29.5) ضمن إصدارك الثانوي الحالي، محافظًا على التحديث مقابل CVEs دون تغييرات API كبيرة. ترقيات الإصدار الثانوي (1.29 → 1.30) تبقى يدوية حتى تتمكن من اختبار توافق أعباء العمل أولًا.

Workload Identity (لا Pod Identity): إضافة AAD Pod Identity القديمة مُهملة. استخدم Azure Workload Identity — الخلف المستند إلى OIDC — حتى تتمكن الحاويات من افتراض هويات Azure AD بدون daemon sets على مستوى العقدة أو تخزين بيانات اعتماد كـ Kubernetes secrets.

# تهيئة Workload Identity: منح حاوية وصولًا إلى Key Vault بدون أسرار # الخطوة 1 — إنشاء هوية مُدارة لحمل العمل az identity create \ --name api-service-identity \ --resource-group acme-rg MI_CLIENT_ID=$(az identity show \ --name api-service-identity \ --resource-group acme-rg \ --query clientId -o tsv) # الخطوة 2 — منح وصول Key Vault للهوية az keyvault set-policy \ --name acme-keyvault \ --secret-permissions get list \ --spn $MI_CLIENT_ID # الخطوة 3 — ربط الهوية بمُصدر OIDC الخاص بـ AKS AKS_OIDC=$(az aks show \ --name acme-aks-prod \ --resource-group acme-rg \ --query oidcIssuerProfile.issuerUrl -o tsv) az identity federated-credential create \ --name api-service-fed \ --identity-name api-service-identity \ --resource-group acme-rg \ --issuer $AKS_OIDC \ --subject "system:serviceaccount:production:api-service-sa" \ --audiences api://AzureADTokenExchange # الخطوة 4 — إضافة تعليق توضيحي لـ ServiceAccount في Kubernetes # kubectl annotate serviceaccount api-service-sa \ # azure.workload.identity/client-id=$MI_CLIENT_ID \ # --namespace production # الخطوة 5 — وسم الحاوية/النشر لاستخدام workload identity # spec.template.metadata.labels: # azure.workload.identity/use: "true" # يُدرج webhook Azure Workload Identity تلقائيًا AZURE_CLIENT_ID و # AZURE_TENANT_ID وحجم الرمز المُتوقَّع — يستخدم تطبيقك DefaultAzureCredential
لا تخزّن بيانات اعتماد Azure كـ Kubernetes Secrets أبدًا. أمر kubectl get secret -o yaml من أي مسؤول namespace يكشف بيانات الاعتماد المُشفَّرة بـ base64 فورًا. Workload Identity يُلغي هذا تمامًا. للأحمال التي تحتاج قراءة الأسرار وقت التشغيل، ارفعها عبر Azure Key Vault CSI driver (secrets-store-csi-driver) — تُعرض الأسرار كملفات أو متغيرات بيئة من Key Vault، ولا تُخزَّن أبدًا في etcd.

تحسين تكاليف AKS على نطاق واسع

مجموعات AKS في الإنتاج تُهدر عادةً 40-60% من الموارد المُخصَّصة خلال ساعات انخفاض النشاط. الحلول التصحيحية متعددة الطبقات: Vertical Pod Autoscaler (VPA) يُحسّن طلبات CPU/الذاكرة (حاوية تطلب 2 CPU لكن تستخدم 200m تُهدر سعة العقدة)؛ Cluster Autoscaler يُزيل العقد غير المُستغَلة بعد فترة تبريد قابلة للتهيئة؛ ومجموعات عقد Spot تتعامل مع أعباء الدُفعات أو المتحملة للأخطاء بخصم 60-90%. هيِّئ مجموعات Spot منفصلة بدلًا من خلط عقد Spot والعقد العادية في المجموعة ذاتها — سلوك الإخلاء يختلف ومجموعات المزج تُسبّب أخطاء جدولة يصعب تشخيصها.

ثالوث Azure DevOps + ACR + AKS يتوافق مباشرةً مع نموذج GitOps: تبني Azure Pipelines صورةً ثابتةً وتدفعها إلى ACR؛ مُشغّل GitOps (Flux أو Argo CD يعمل في AKS) يكتشف تحديث الصورة ويُطبّق المانيفيست الجديد. تمتلك Pipelines أثر البناء؛ يمتلك مُشغّل GitOps حالة المجموعة. هذا الفصل بين المسؤوليات هو النمط الجاهز للإنتاج في المتاجر الأصلية على Azure — خط الأنابيب لا يشغّل kubectl apply مباشرةً في هذا النموذج.