لماذا تُعدّ `latest` المشكلة الحقيقية
عندما تُحدَّث صورة Docker المُوسَمة بـ latest في سجل الصور، يقوم docker compose pull بتنزيلها بصمت. لا تحذير، لا فرق. تُعيد تشغيل الـ stack فتكتشف أن الحاوية الجديدة لا تستطيع قراءة البيانات التي تركتها القديمة.
هذا بالضبط هو الخطر مع Meilisearch. تحمل قاعدة البيانات رقم إصدار المحرك الذي كتبها: عند الترقية، يرفض المحرك الجديد فتحها ما لم يُطلب منه صراحةً ترحيلها بالخيار --upgrade-db (أو المتغير MEILI_UPGRADE_DB). ومع سياسة إعادة التشغيل، تدخل الحاوية حينها في حلقة تعطل مستمرة. وبمجرد ترحيل القاعدة، لا تستطيع الصورة القديمة فتحها من جديد: لا يدعم Meilisearch الرجوع إلى إصدار أقدم. شبكة الأمان النظيفة الوحيدة هي dump أو snapshot للقرص يُنشأ قبل التحديث — وهو أمر مستحيل بمجرد ترحيل القاعدة أو توقف الحاوية.
وسم latest لا يحل إلى نفس الصورة بحسب وقت السحب. مطوران يُشغّلان docker compose pull بفارق اثنتي عشرة ساعة قد يسحبان إصدارين مختلفين. على stack إنتاجية، هذا الغموض غير مقبول. الحل ليس تجنب التحديثات: بل التحكم الصريح في الإصدار الذي يعمل والقرار الواعي بموعد الانتقال إلى الإصدار التالي.
ما يغطيه هذا الدليل — وما لا يغطيه
- ما يغطيه هذا الدليل: بروتوكول خطوة بخطوة لتحديث stack Docker Compose على VPS — تثبيت الإصدار، نسخ احتياطي للأقراص، مراجعة ملاحظات الإصدار، التراجع السريع، healthchecks كشبكة أمان.
- مُغطى في أدلة أخرى: قائمة التدقيق الأولية (
docker-compose-production-checklist)، وإعدادdepends_onوservice_healthy(docker-compose-depends-on-healthcheck)، ومقارنة أدوات التحديث التلقائي كـ Watchtower أو Diun. - ما لا يوصي به هذا الدليل: Watchtower أو أي أداة auto-pull في الإنتاج — هذا هو بالضبط النمط المضاد الذي توضحه حالات التغييرات الجذرية.
- الجمهور المستهدف: المطورون والوكالات التي تدير stack واحدة أو أكثر من Docker Compose في الإنتاج على VPS، مع وصول root وأقراص بيانات دائمة.
الخطوة 1 — ثبّت جميع صورك
الإجراء الأول قبل أي تحديث هو استبدال كل image: getmeili/meilisearch:latest أو image: supabase/postgres بإصدار محدد.
شكلان مقبولان:
- وسم الإصدار: image: getmeili/meilisearch:v1.53.0 — مقروء، قابل للتتبع في git، سهل التصحيح.
- ملخص SHA256: image: getmeili/meilisearch@sha256:abc123… — ثابت، يضمن سحب نفس القطعة بالضبط في كل نشر، حتى لو أُعيدت كتابة الوسم.
للحصول على ملخص صورة تعمل بالفعل:
docker inspect --format='{{index .RepoDigests 0}}' getmeili/meilisearch:v1.53.0بعد تثبيت صورك، احفظ ملف docker-compose.yml في git. كل رفع إصدار يصبح commit واحداً — سجل واضح وتراجع سهل (git revert + docker compose up -d).
بروتوكول التحديث — الخطوات الخمس
اقرأ ملاحظات الإصدار قبل السحب
أولاً، راجع ملاحظات الإصدار الجديد. ابحث عن كلمات
breaking،migration،incompatible،pg_upgrade،dump. هذا ليس اختيارياً: وثّق Supabase صراحةً أن الترقية من PostgreSQL 15 إلى 17 تستلزمpg_upgradeيدوياً — حاوية PG 17 ترفض الإقلاع على قرص PG 15، وعملية التهيئة لا تُهجّر البيانات تلقائياً.بالنسبة لـ Langfuse v4 (نُشر الإصدار v4.0.0 في 29 يوليو 2026)، تُرفض SDK Python v2 والإصدارات الأقدم عند الاستيعاب من طرف الـ stack الجديدة — تغيير جذري يؤثر على جميع الخدمات العميلة التي تتتبع عبر الـ API القديمة.
ثلاث دقائق من القراءة توفر عليك ساعات من استعادة البيانات.
احتفظ بنسخة احتياطية للأقراص قبل السحب
لا تسحب أبداً قبل أن يكون لديك نسخة احتياطية صالحة للاستخدام. بالنسبة للأقراص المسماة، هناك مقاربتان:
تصدير تطبيقي (موصى به لقواعد البيانات) — يجب أن تكون الخدمة في حالة
healthyقبل التصدير:docker compose exec -T db pg_dump -U postgres -Fc mydb > backup_$(date +%Y%m%d_%H%M%S).dumpيعطّل الخيار
-Tالطرفية الوهمية: بدونه قد يتلف تدفق الـ dump الثنائي.لقطة قرص خام — مفيدة للتخزينات الثنائية (Meilisearch، Redis، MinIO):
docker run --rm \ --volumes-from $(docker compose ps -q meilisearch) \ -v $(pwd)/backups:/backup \ alpine tar czf /backup/meili_$(date +%Y%m%d_%H%M%S).tar.gz /meili_dataتحقق من أن النسخة الاحتياطية قابلة للقراءة قبل المتابعة. ملف dump تالف يُكتشف أثناء الاستعادة هو أكثر السيناريوهات تكلفةً.
اسحب الصورة الجديدة واختبرها خارج الإنتاج
حدّث الوسم في
docker-compose.yml، ثم اسحب الصورة دون إعادة تشغيل الخدمة:docker compose pull meilisearchإذا أتاحت بيئتك ذلك، اختبر الصورة الجديدة على نسخة مستنسخة من القرص في بيئة ddev أو VM تجريبي قبل المساس بالإنتاج. تحقق من سجلات الإقلاع بحثاً عن أي أخطاء تهجير:
docker compose up -d meilisearch docker compose logs -f meilisearchانتظر حتى يصل healthcheck إلى حالة
healthyقبل التحقق من النجاح. خدمة تبدأ لكنها ليستhealthyبعد ليست خدمة جاهزة.تحقق من healthchecks
healthcheck مُهيَّأ جيداً هو خط الكشف الأول. يجب أن يكون موجوداً على كل خدمة حيوية في الـ stack، بصيغة Compose v2:
healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:7700/health || exit 1"] interval: 10s timeout: 5s retries: 5 start_period: 30sحقل
start_periodحاسم للخدمات التي تستغرق وقتاً في الإقلاع (قواعد البيانات، محركات البحث): يمنع Docker من إعلان الحاويةunhealthyأثناء مرحلة التهيئة وتشغيل إعادة تشغيل مبكرة.راجع
docker-compose-depends-on-healthcheckللإعداد الكامل لـservice_healthyعلى PostgreSQL — نفس المبدأ ينطبق على أي خدمة تحتاج وقتاً للتهيئة.التراجع في حال حدوث مشكلة
إذا فشل الإصدار الجديد في الإقلاع أو أنتج أخطاء، يجب أن يستغرق التراجع أقل من دقيقتين. الإجراء:
1. عُد إلى الوسم السابق في
docker-compose.yml(أوgit revertإذا حفظت الرفع).
2. أعِد تشغيل الخدمة المعنية فقط، دون إعادة إنشاء الأقراص:docker compose up -d --no-deps --force-recreate meilisearch3. تحقق من السجلات فوراً:
docker compose logs -f meilisearchعلامة
--no-depsضرورية: تُعيد تشغيل الخدمة المستهدفة دون المساس بالحاويات الأخرى (قاعدة البيانات، الذاكرة المؤقتة، الوكيل). بدونها، قد يُعيدdocker compose up -dإنشاء الـ stack بأكملها.⚠️ إذا هاجر الإصدار الجديد تنسيق البيانات على القرص (Meilisearch بعد
--upgrade-db، Supabase PG17)، فإن التراجع عن الصورة وحده لا يكفي — وهذا بالضبط لماذا النسخة الاحتياطية للقرص شرط مسبق لا خيار.
احتفظ بالإصدار السابق متاحاً محلياً
قبل سحب الصورة الجديدة، قم بوسم الصورة الحالية في الإنتاج باسم احتياطي:
docker tag getmeili/meilisearch:v1.53.0 getmeili/meilisearch:rollbackيتيح لك ذلك العودة إلى الحالة الدقيقة للإنتاج في حالات الطوارئ، حتى إذا لم يعد لديك وصول إلى السجل أو كان الاتصال بطيئاً. على VPS بعرض نطاق ترددي محدود، يوفر لك هذا الوسم المحلي دقائق عدة من التنزيل في أسوأ وقت ممكن.
التغييرات الجذرية الأخيرة التي أوقفت تشغيل الخوادم
هذه الأمثلة الأربعة توضح لماذا البروتوكول أعلاه ليس نظرياً.
Meilisearch (عند الترقية): يرفض المحرك الأحدث فتح قاعدة كتبها إصدار أقدم، مع رسالة Your database version (…) is incompatible with your current engine version، ما لم يُمرَّر الخيار --upgrade-db. وقد يغيّر هذا الترحيل تنسيق البيانات على القرص: في الإصدار v1.37.0 (مارس 2026)، حُوِّلت فهارس المتجهات التي بقيت بتنسيق arroy إلى مخزن HNSW الجديد (hannoy). والرجوع إلى إصدار أقدم غير مدعوم (Downgrade is not supported). المخرج النظيف الوحيد بعد الفشل هو تصدير dump قبل التحديث ثم استيراده (meilisearch --db-path /meili_data --import-dump /backup.dump) — وهو ممكن فقط إذا صدّرت قبل التحديث.
Supabase Docker PostgreSQL 15 → 17 (PG 17 افتراضي منذ 17 يونيو 2026): حاوية supabase/postgres:17 لا تستطيع قراءة قرص أُهيِّئ بواسطة PG 15. وثّق Supabase صراحةً أن هذه القفزة تستلزم pg_upgrade عبر سكريبت مخصص — عملية تهيئة الحاوية لا تفعل ذلك تلقائياً. دون تهجير مسبق، لا تبدأ قاعدة البيانات.
Langfuse v3 → v4 (نُشر الإصدار v4.0.0 في 29 يوليو 2026): تتخلى v4 عن نقاط النهاية القديمة لاستيعاب الدفعات لصالح OpenTelemetry. تُرفض SDK Python v2 والأقدم وSDK JS/TS v3 والأقدم عند الاستيعاب بمجرد أن تعمل الـ stack v4 بوضع الكتابة الافتراضي (events_only)؛ ويتيح الوضعان legacy وdual تنفيذ الترحيل على مراحل. إذا لم تُهجَّر خدماتك العميلة قبل تحديث الخادم، تفقد جميع تتبعاتها بصمت.
NocoDB (سلسلة 2026.09): لا قاعدة بيانات معطلة، بل تغيير غير متوافق أُعلن عنه مسبقًا. تذكّر ملاحظات الإصدار 2026.09.0 بأن التواريخ التي تعيدها الـ API REST والـ webhooks والتصديرات ستنتقل من دقة الثانية إلى دقة الميلي ثانية على PostgreSQL وSQLite في إصدار قادم. العميل الذي يقرأ التواريخ بتنسيق صارم سيتعطل دون أن يُنبّه الخادم إلى شيء — وهذا بالضبط ما تتيح قراءة ملاحظات الإصدار توقّعه. فخ آخر مستقل عن الإصدار: تثبيت يستخدم bind-mounts (./postgres، ./nocodb) يتغير مسار تحميله بين نشرين يبدأ على قاعدة بيانات فارغة. الأقراص المسماة تمنع هذا الانحراف.
استراتيجيات التحديث: مقارنة
مرّر الجدول أفقيًا
| الاستراتيجية | أمان البيانات | وقت التحضير | التراجع |
|---|---|---|---|
| `docker compose pull` + `up -d` مباشرة | لا ضمان — التغييرات الجذرية غير مكتشفة | أقل من دقيقة | صعب في حال هجرة البيانات |
| رفع وسم مُصدَّر + نسخة احتياطية للأقراص | عالٍ — البيانات محفوظة قبل أي تغيير | 10 إلى 20 دقيقة | سهل: تراجع الوسم + `up -d --no-deps` |
| اختبار على بيئة تجريبية قبل الإنتاج | أقصى مستوى — التغييرات الجذرية مكتشفة خارج الإنتاج | متغير حسب البيئة | غير ضروري إذا نجح الاختبار |
| صورة مثبتة بملخص SHA256 | عالٍ — محصّن ضد إعادة كتابة الوسم | مثل وسم الإصدار | مثل وسم الإصدار |
دمج هذا البروتوكول في سير عملك
بروتوكول يبقى في دليل لا فائدة منه. لتطبيقه بشكل منهجي، أخرج الإصدار إلى ملف .env مُصدَّر في git:
# .env
MEILISEARCH_VERSION=v1.53.0
POSTGRES_VERSION=15.6# docker-compose.yml
services:
meilisearch:
image: getmeili/meilisearch:${MEILISEARCH_VERSION}تحديث إصدار يصبح حينئذٍ commit واحد على .env — مقروء في git log، قابل للعكس بـ git revert، وقابل للنشر عبر CI/CD دون تعديل ملف Compose الرئيسي.
بالنسبة للوكالات التي تدير stacks متعددة لعملاء مختلفين، أنشئ ملف CHANGELOG_INFRA.md لكل عميل: كل تحديث موثق بالإصدار السابق، والتاريخ، والنسخة الاحتياطية المنجزة، والنتيجة. هذا أيضاً ما يحميك تعاقدياً في حال حدوث حادث لاحق.
الأتمتة دون فقدان التحكم
إذا أردت الإخطار بالإصدارات الجديدة دون السحب التلقائي، تراقب Diun (Docker Image Update Notifier) سجلك وترسل لك إشعاراً (Slack، بريد إلكتروني، webhook) عند توفر صورة جديدة. تظل صاحب قرار موعد التحديث.
هذا هو الفرق الجوهري عن Watchtower: Diun تُخطر، Watchtower تتصرف. على stack إنتاجية مع أقراص دائمة، الإخطار هو المستوى الصحيح من الأتمتة — الفعل يبقى يدوياً ومسبوقاً بالبروتوكول أعلاه.