EduFlow

الوحدة 2 · Images جاهزة للإنتاج

الـ Tags والـ Digests والـ Registries

18 دقراءة 4 درتّب الخطوات

الدرس في لمحة

سمِّ images ‏notes-api وأعطها أرقام إصدارات وارفعها إلى registry بحيث يمكن تتبّع كل deploy، وثبّت الـ images بالـ digest، وابنِ لمعماريّتَي amd64 وarm64 حتى يستطيع الـ cluster تشغيل ما بناه جهازك.

  1. 1تشريح مرجع الـ image
  2. 2مخطّط tags يصمد
  3. 3رفع الـ image إلى registry
  4. 4الـ images متعدّدة المنصّات
  5. 5التثبيت بالـ digest

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

في الحادية عشرة ليلًا من ليلة إطلاق، أعطى اثنان من الـ Pods التابعة للخدمة نفسها إجابتين مختلفتين عن الطلب نفسه. كلاهما كان يشغّل api:latest. أحدهم أعاد دفع latest بعد الظهر، فسحب أحد الـ nodes الـ image الجديدة بينما بقي الآخر يستخدم نسخته المخزّنة. قضينا ساعة نثبت أن الكود سليم قبل أن ينظر أحد إلى الـ image. الأسماء جزء من عملية النشر، فاجعلها دقيقة.

تشريح مرجع الـ image

Text
ghcr.io/skylane/notes-api:1.4.0@sha256:3f1c9e…
└──┬──┘ └──┬──┘ └───┬───┘ └─┬─┘ └─────┬─────┘
registry  namespace  repo    tag      digest
  • الـ registry (مستودع الـ images) هو الخادم الذي يخزّن الـ images: ‏Docker Hub (الافتراضي حين لا تذكره)، وGitHub Container Registry ‏(ghcr.io)، وAmazon ECR، وGoogle Artifact Registry، وغيرها.
  • الـ tag تسمية سهلة القراءة على البشر. وهو مؤشّر، والمؤشّرات يمكن أن تتحرّك.
  • الـ digest (البصمة) هو hash من نوع SHA-256 للـ manifest الخاص بالـ image (أو لفهرس الـ image في حالة image متعدّدة المنصّات)، والـ manifest بدوره يسرد hash كل layer. غيّر بايتًا واحدًا في أي مكان فيتغيّر الـ digest. لا يمكن نقله.

الـ image ‏notes-api:latest ليست مميّزة. ‏latest مجرد الـ tag الذي يستخدمه Docker حين لا تحدّد واحدًا. لا يعني «الأحدث»، ولا شيء يحدّثه ما لم يدفعه أحد.

مخطّط tags يصمد

لكل بناء قد يُنشر، ادفع اثنين من الـ tags للـ image نفسها:

  • إصدار يتحدّث عنه الناس: 1.4.0.
  • الـ commit الذي جاء منه: sha-9f2c1ab.

حين يُبلغ أحدهم عن خلل في «1.4.0»، يأخذك الـ tag الخاص بالـ SHA مباشرة إلى الكود. والقاعدة التي تمنع حادث ليلة الإطلاق: لا تدفع أبدًا محتوى مختلفًا تحت tag منشور فعلًا. إن أصلحت شيئًا، فهذا 1.4.1. كثير من الـ registries تستطيع فرض ذلك؛ فلـ Amazon ECR مثلًا إعداد لثبات الـ tags يرفض أي دفع إلى tag موجود.

ابنِ مرة واحدة، ثم رقِّ (promote) الـ image نفسها من بيئة إلى أخرى مرات عديدة. الـ image التي تجتاز staging يجب أن تكون بالضبط الـ image التي تصل إلى الإنتاج، ويعرّفها الـ digest نفسه. لا تُعد البناء من الـ commit نفسه للإنتاج «احتياطًا»: إعادة البناء قد تسحب base image أحدث أو اعتمادية غير مباشرة مختلفة، وعندها يشغّل الإنتاج شيئًا لم يختبره أحد. الترقية تغيير في الـ digest الذي يشير إليه الـ manifest الخاص بكل بيئة، لا بناء جديد.

حافظ على ترتيب الـ registry أيضًا. قواعد الاحتفاظ التي تحذف الـ images بلا tags وtags الـ SHA القديمة بعد بضعة أشهر تخفّض تكاليف التخزين، لكن استثنِ منها كل ما زال manifest يشير إليه، وإلا فسيفشل التراجع التالي بسبب image مفقودة.

رفع الـ image إلى registry

Terminal
echo "$GHCR_TOKEN" | docker login ghcr.io -u amara-skylane --password-stdin

GIT_SHA=$(git rev-parse --short HEAD)
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t ghcr.io/skylane/notes-api:1.4.0 \
  -t ghcr.io/skylane/notes-api:sha-$GIT_SHA \
  --push .

docker buildx imagetools inspect ghcr.io/skylane/notes-api:1.4.0

الخيار --password-stdin يُبقي الـ token خارج سجلّ الأوامر في الـ shell. وفي الـ CI، استخدم الـ token قصير العمر الذي توفّره المنصّة بدلًا من token شخصي. والأمر الأخير يطبع الـ digest الخاص بالـ image والمنصّات التي تحتويها.

الـ images متعدّدة المنصّات

قد يكون حاسوبك arm64 ‏(Apple Silicon) بينما الـ nodes في الـ cluster من نوع amd64، أو العكس. الـ image المبنية لمعمارية معالج معيّنة تفشل على الأخرى برسالة مقتضبة: exec format error. البناء متعدّد المنصّات يحلّ ذلك: ‏--platform linux/amd64,linux/arm64 يبني النسختين ويدفع image index (فهرس الـ image)، وهو قائمة صغيرة تربط كل منصّة بالـ manifest الخاص بها. حين يسحب node الـ image ‏notes-api:1.4.0، يختار الـ runtime الـ manifest المناسب لمعماريّته.

الـ tag ‏1.4.0 يشير إلى digest لفهرس image. الفهرس يسرد manifest لـ amd64 وmanifest لـ arm64، وكل منهما يسرد الـ layers الخاصة به. tag 1.4.0 image index sha256:3f1c… manifest لـ linux/amd64 layers لـ nodes ‏x86 manifest لـ linux/arm64 layers لـ nodes ‏ARM قابل للنقل ثابت
tag واحد يشير إلى فهرس، وكل node يسحب الـ manifest الخاص بمعالجه

البناء لمعمارية غير معمارية جهازك يعمل تحت المحاكاة، وقد يكون بطيئًا مع خطوات npm ci الثقيلة. الـ runners في الـ CI التي تملك أجهزة arm64 وamd64 أصلية أسرع حين يكبر البناء. ولكي تحتفظ بالـ images متعدّدة المنصّات محليًا، يحتاج Docker إلى مخزن الـ images الخاص بـ containerd ‏(containerd image store)، وهو الإعداد الافتراضي في تثبيتات Docker Desktop الجديدة؛ أما في الإعدادات الأقدم فتأكّد من أنه مفعّل.

التثبيت بالـ digest

الـ digest يضمن أن كل node يشغّل بايتات متطابقة، مهما حدث للـ tag لاحقًا. تستطيع النشر بالاثنين معًا، والـ digest هو الذي يفوز:

YAML
image: ghcr.io/skylane/notes-api:1.4.0@sha256:3f1c9e…

يبقى الـ tag في المرجع من أجل البشر؛ والـ runtime يسحب بالـ digest. افعل الشيء نفسه مع الـ base images في الـ Dockerfile، حتى لا تلتقط إعادة البناء في الشهر القادم نسخة مختلفة من node:24-slim بصمت:

Dockerfile
FROM node:24-slim@sha256:<digest-from-imagetools-inspect> AS build

التثبيت يقايض الترقيعات التلقائية بإمكانية التنبّؤ، لذا اقرنه بأداة آلية مثل Dependabot أو Renovate تفتح pull request حين يُنشر digest جديد للـ base. فتحصل على التحديثات مراجَعة ومختبَرة، بدلًا من المفاجآت.

الـ image لديك الآن مسمّاة، وذات رقم إصدار، ومرفوعة إلى الـ registry. وقبل أن ينشرها أحد، يجب أن تعرف ما بداخلها، وهذا موضوع الدرس التالي.

أهم النقاط

  • مرجع الـ image هو registry/namespace/repository:tag@digest؛ الـ tag لافتة قابلة للنقل، والـ digest بصمة المحتوى.
  • الـ tag ‏latest مجرد اسم الـ tag الافتراضي، لا أحدث بناء؛ انشر إصدارات صريحة مثل 1.4.0 أو SHA من Git.
  • لا تدفع أبدًا محتوى مختلفًا تحت tag منشور فعلًا؛ وفعّل ثبات الـ tags (tag immutability) حيث يدعمه الـ registry لديك.
  • التثبيت بالـ digest ‏(@sha256:…) يضمن أن كل node يشغّل البايتات نفسها، لتطبيقك وللـ base images.
  • ابنِ images متعدّدة المنصّات بـ docker buildx build --platform linux/amd64,linux/arm64 حين يستخدم المطوّرون والخوادم معالجات مختلفة.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

اثنان من الـ Pods في الـ Deployment نفسه، وكلاهما يستخدم notes-api:latest، يتصرّفان بشكل مختلف. ما التفسير الأرجح؟

السؤال 2 من 4

ماذا يحدّد digest مثل sha256:3f1c…؟

السؤال 3 من 4

يبني مطوّرٌ image ‏notes-api على حاسوب Apple Silicon ويدفعها. على الـ cluster ذي معمارية amd64 يكتب الـ Pod في سجلّه exec format error. ما الحل؟

السؤال 4 من 4

أي مخطّط للـ tags يعطي أفضل قابلية للتتبّع لإصدارات notes-api؟

تدريب

رتّب الخطوات

اشحن الإصدار 1.4.0

رتّب خطوات إطلاق notes-api 1.4.0، من الكود المصدري إلى نشر متحقَّق منه يشغّل فيه كل node البايتات نفسها تمامًا.

استخدم أزرار الأسهم لإعادة الترتيب.

  1. 1kubectl rollout status deployment/notes-api
  2. 2الأمر docker buildx imagetools inspect ghcr.io/skylane/notes-api:1.4.0 لقراءة الـ digest
  3. 3ادفع كلا الـ tags إلى ghcr.io
  4. 4docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/skylane/notes-api:1.4.0 -t ghcr.io/skylane/notes-api:sha-9f2c1ab .
  5. 5اضبط image: ghcr.io/skylane/notes-api:1.4.0@sha256:… في الـ manifest الخاص بالـ Deployment
  6. 6kubectl apply -f k8s/
  7. 7شغّل مجموعة الاختبارات وفحصًا أمنيًا على الـ image المبنية