Docker المتقدم وأمن الحاويات

سجلات الصور في بيئة الإنتاج

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

سجلات الصور في بيئة الإنتاج

كل صورة حاوية تعمل في بيئة الإنتاج يتم سحبها من سجل — وهو مخزن للقطع الأثرية يُنظَّم حسب الاسم والوسم، ويعتمد على عنونة المحتوى. قد يكون Docker Hub مناسبًا للتجارب المفتوحة، لكن الفرق في بيئات الإنتاج الحقيقية تشغّل سجلات خاصة لأسباب تتعلق بالأمان وزمن الاستجابة والتكلفة والامتثال. يتناول هذا الدرس الخيارات المُدارة الرئيسية (ECR وGAR وHarbor)، والممارسات التشغيلية التي تُبقي هذه السجلات بصحة جيدة — من سياسات دورة حياة الصور وفحص الثغرات وذاكرة التخزين المؤقت للسحب، وصولًا إلى أنماط الفشل الشائعة التي تقع فيها الفرق التي تتجاهل هذه البنية التحتية.

لماذا تهم السجلات الخاصة

تفرض السجلات العامة قيودًا على معدل الطلبات (Docker Hub: 100 سحب مجهول / 200 سحب موثّق لكل ست ساعات لكل عنوان IP). في كتلة Kubernetes مكوّنة من 50 عقدة تسحب الصورة ذاتها عند بدء التشغيل، ستصل إلى هذا الحد في ثوانٍ. علاوةً على قيود المعدل، توفر السجلات الخاصة:

  • التحكم في الوصول: تحكم سياسات IAM أو RBAC بمن يستطيع دفع الصور أو سحبها من أي مستودع.
  • القرب الشبكي: الصور المسحوبة من سجل في نفس المنطقة السحابية تتفادى رسوم نقل البيانات الخارجية وتُقلل زمن الاستجابة من دقائق إلى ثوانٍ للصور الكبيرة.
  • سجل المراجعة: كل عملية دفع أو سحب مُسجَّلة، وهو أمر إلزامي لامتثال معايير SOC 2 وISO 27001.
  • تطبيق الثبات: يمكن تعطيل الكتابة فوق الوسوم، لضمان أن v1.4.2 يُشير دائمًا إلى نفس الملخص الرقمي.

Amazon ECR (سجل حاويات مرن)

ECR هو الخيار الطبيعي للأحمال العاملة على AWS (ECS وEKS وLambda). تعتمد المصادقة على رموز قصيرة العمر تُصدرها واجهة AWS السطرية — دون بيانات اعتماد طويلة الأمد مخزّنة في ~/.docker/config.json.

# مصادقة عميل Docker مع ECR (ينتهي الرمز بعد 12 ساعة) aws ecr get-login-password --region us-east-1 \ | docker login --username AWS --password-stdin \ 123456789012.dkr.ecr.us-east-1.amazonaws.com # إنشاء مستودع خاص (يُنفَّذ مرة واحدة، عادةً عبر Terraform) aws ecr create-repository \ --repository-name myapp/api \ --image-scanning-configuration scanOnPush=true \ --image-tag-mutability IMMUTABLE \ --region us-east-1 # الوسم والدفع docker tag myapp:latest \ 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp/api:v1.4.2 docker push \ 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp/api:v1.4.2 # قائمة الصور مع ملخصها الرقمي وتاريخ الدفع aws ecr describe-images \ --repository-name myapp/api \ --query 'sort_by(imageDetails, &imagePushedAt)[*].[imageTags[0],imageDigest,imagePushedAt]' \ --output table
فعّل الوسوم الثابتة في ECR منذ البداية. الوسوم القابلة للتعديل تعني أن أنبوب CI يمكنه الكتابة فوق وسم production بصورة معطوبة بصمت — والتراجع الذي ظننت أنه يشير إلى الملخص القديم بات يشير إلى الصورة المعطوبة الجديدة. الوسوم الثابتة تُلزم باستخدام وسم إصدار فريد لكل إصدار وتجعل عمليات التراجع حتمية.

تُؤتمت سياسات دورة حياة ECR عملية التنظيف. بدونها، يتراكم في المستودع الذي يتلقى 20 عملية دفع يوميًا آلاف الصور وتتصاعد فاتورة التخزين. تحتفظ السياسة التالية بآخر 30 إصدارًا موسومًا وتُنهي الطبقات غير الموسومة (المخلّفات من عمليات البناء الفاشلة) تلقائيًا:

# ecr-lifecycle-policy.json { "rules": [ { "rulePriority": 1, "description": "انتهاء صلاحية الصور غير الموسومة بعد يوم واحد", "selection": { "tagStatus": "untagged", "countType": "sinceImagePushed", "countUnit": "days", "countNumber": 1 }, "action": { "type": "expire" } }, { "rulePriority": 2, "description": "الاحتفاظ بآخر 30 إصدارًا موسومًا", "selection": { "tagStatus": "tagged", "tagPrefixList": ["v"], "countType": "imageCountMoreThan", "countNumber": 30 }, "action": { "type": "expire" } } ] } # تطبيق السياسة aws ecr put-lifecycle-policy \ --repository-name myapp/api \ --lifecycle-policy-text file://ecr-lifecycle-policy.json

Google Artifact Registry (GAR)

GAR هو خلف Google Container Registry (GCR) والخيار الأمثل لأحمال العمل على GKE. يدعم صيغًا متعددة (Docker وHelm وnpm وMaven) في خدمة واحدة. تعتمد المصادقة على Workload Identity Federation في GKE — دون ملفات مفاتيح حسابات الخدمة.

# المصادقة (على محطة عمل؛ عقد GKE تستخدم Workload Identity تلقائيًا) gcloud auth configure-docker us-central1-docker.pkg.dev # إنشاء مستودع (يُفضَّل استخدام Terraform في الإنتاج) gcloud artifacts repositories create myapp \ --repository-format=docker \ --location=us-central1 \ --description="صور الإنتاج لـ myapp" # دفع صورة docker tag myapp:latest \ us-central1-docker.pkg.dev/my-gcp-project/myapp/api:v1.4.2 docker push \ us-central1-docker.pkg.dev/my-gcp-project/myapp/api:v1.4.2 # تعيين سياسة تنظيف: حذف الصور غير الموسومة الأقدم من 7 أيام gcloud artifacts repositories set-cleanup-policies myapp \ --location=us-central1 \ --policy='{ "name": "delete-untagged", "action": {"type": "Delete"}, "condition": { "tagState": "UNTAGGED", "olderThan": "604800s" } }'

Harbor — سجل مستضاف ذاتيًا، مستقل عن السحابة

Harbor هو السجل مفتوح المصدر الحاصل على شهادة CNCF، والذي تختاره الفرق عندما تحتاج إلى تخزين محلي (بيئات معزولة عن الشبكة، اشتراطات صارمة لمحلية البيانات، بيئات متعددة السحب دون الارتباط بمورد واحد). يضيف Harbor واجهة مستخدم وRBAC وحصصًا وقواعد نسخ متماثل وفحص Trivy المدمج فوق مواصفة OCI Distribution.

Registry architecture: CI/CD pushes to private registry, Kubernetes nodes pull from it via pull-through cache CI/CD Pipeline (GitHub Actions) push Private Registry ECR / GAR / Harbor Immutable tags Lifecycle policies Vuln scanning cached pull Pull-Through Cache Regional mirror pull Node A kubelet Node B kubelet Vuln Scanner Trivy / Inspector
تدفق الصور: يدفع أنبوب CI/CD إلى سجل خاص؛ تسحب عقد Kubernetes عبر ذاكرة تخزين مؤقت إقليمية؛ يفحص الماسح الضوئي للثغرات كل صورة مدفوعة.

ذاكرة التخزين المؤقت للسحب العابر (Pull-Through Cache)

ذاكرة التخزين المؤقت للسحب العابر هي وكيل سجل يقف بين عقدك وسجل المصدر (Docker Hub أو Quay أو حتى سجلك الخاص في منطقة بعيدة). عند السحب الأول، يجلب الوكيل طبقة الصورة ويخزنها. تُخدَّم عمليات السحب اللاحقة محليًا — وتنجو من انقطاع المصدر وقيود المعدل.

تدعم ECR قواعد ذاكرة التخزين المؤقت للسحب العابر بشكل أصلي منذ عام 2022. الإعداد عملية Terraform أو CLI تتم مرة واحدة:

# إنشاء قاعدة ذاكرة تخزين مؤقت للسحب تعكس Docker Hub في ECR aws ecr create-pull-through-cache-rule \ --ecr-repository-prefix "dockerhub" \ --upstream-registry-url "registry-1.docker.io" \ --region us-east-1 # بعد هذا، أي عقدة تسحب: # 123456789012.dkr.ecr.us-east-1.amazonaws.com/dockerhub/library/nginx:1.27 # ستسحب من Docker Hub عند غياب الصورة في الذاكرة، ثم تُخدَّم من ECR. # لإعداد متعدد المناطق، انسخ الصور بقواعد نسخ ECR aws ecr create-replication-configuration --replication-configuration '{ "rules": [{ "destinations": [ {"region": "eu-west-1", "registryId": "123456789012"}, {"region": "ap-southeast-1", "registryId": "123456789012"} ], "repositoryFilters": [{ "filter": "myapp/", "filterType": "PREFIX_MATCH" }] }] }'
ذاكرة التخزين المؤقت في Harbor (Proxy Cache): يُسمي Harbor هذه الميزة "Proxy Cache". تُهيئ نقطة نهاية تشير إلى Docker Hub (أو أي سجل OCI) وتنشئ مشروعًا مخصصًا مدعومًا بها. تسحب الفرق بعدها من harbor.internal/dockerhub-cache/library/nginx:1.27 بدلًا من Docker Hub مباشرةً. يحترم Harbor ترويسات التحكم في التخزين المؤقت للمصدر ويعيد الجلب عند تحديث الصورة.

الاحتفاظ بالصور وأفضل ممارسات دورة الحياة

على نطاق الشركات الكبرى، تُولّد خدمة واحدة تتلقى 10 عمليات دفع يوميًا 3,650 صورة في السنة. اضرب ذلك في 200 خدمة وستحصل على 730,000 صورة — معظمها لم يُسحب أبدًا بعد النشر الأولي. تتبع إدارة دورة الحياة الجيدة هذه القواعد:

  1. وسم إصدارات semver بوضوح (v1.4.2)، ولا تنشر أبدًا من latest. يجب أن تُنهي سياسات دورة الحياة فقط الصور الموسومة ببادئة تعكس بنيات مؤقتة (مثل sha- أو branch-).
  2. الاحتفاظ بنافذة متجددة من الإصدارات الحديثة (30–90 يومًا أو آخر N إصدار) لضمان إمكانية التراجع دائمًا دون الحاجة لإعادة البناء.
  3. إنهاء الكتل غير الموسومة بسرعة (24–48 ساعة). الصور غير الموسومة هي قطع أثرية من عمليات دفع CI الفاشلة أو الملغاة — تستهلك مساحة تخزين ولا يُنشر منها شيء.
  4. الحجب عند اكتشاف ثغرات حرجة وقت الدفع. يمكن ربط ECR Enhanced Scanning (المدعوم بـ AWS Inspector) وفحص GAR المدمج بـ CI لفشل الأنبوب عند اكتشاف ثغرة CRITICAL، قبل أن تصل الصورة إلى الإنتاج.
لا تحذف أبدًا صورة منشورة حاليًا. تعمل سياسات دورة الحياة بشكل غير متزامن وفق جدول زمني. إن أنهيت بعدوانية الصور "القديمة" واحتاجت عقدة Kubernetes إلى إعادة جدولة حاوية (مثلًا بعد إعادة تشغيل العقدة)، سيفشل kubelet في سحب الصورة المحذوفة وستدخل الحاوية في حالة ImagePullBackOff. احتفظ على الأقل بالملخص الرقمي للإصدار المنشور حاليًا مثبتًا، أو استخدم وسوم الملخص (sha256:abc123) التي تديرها بشكل صريح بمعزل عن نافذة الإنهاء.

أنماط المصادقة على نطاق واسع

بيانات الاعتماد طويلة الأمد في imagePullSecrets تُشكل خطرًا أمنيًا — تحديثها يستلزم تعديل كل namespace. النمط الإنتاجي هو IAM على مستوى العقدة: في EKS، أسند أذونات قراءة ECR لدور IAM الخاص بالعقدة؛ في GKE، استخدم Workload Identity. يستوثق kubelet آنئذٍ بشفافية باستخدام موفر بيانات اعتماد العقدة، ولا حاجة لإدارة أي أسرار على مستوى التطبيق.

للسحب عبر الحسابات أو المشاريع (مثلًا سجل "صور ذهبية" مشترك تصل إليه فرق عديدة)، استخدم سياسات ECR عبر الحسابات أو ربط IAM في GAR الذي يمنح حساب خدمة المشروع المستهلك دور roles/artifactregistry.reader على المشروع المصدر — مصادقة عبر تفويض IAM دون أي بيانات اعتماد.