{"id":"l-dk-3-2","courseId":"docker-kubernetes","lang":"ar","kind":"lesson","summary":"احفظ بيانات Postgres عبر إعادات التشغيل باستخدام named volume، وجهّز قاعدة البيانات ببيانات أولية عبر سكربت تهيئة مربوط بـ bind mount، واعرف أي أوامر Compose تحفظ بياناتك وأيها يدمّرها.","html":"<p>في الأسبوع الذي أضفنا فيه Compose إلى أحد مشاريع Skylane، شغّل مطوّر <code dir=\"ltr\">docker compose down -v</code> «لينظّف» قبل عرض تقديمي. فنظّف ثلاثة أيام من بيانات الاختبار المُدخلة بعناية. الـ containers مصمَّمة ليُتخلَّص منها، لذا يجب أن يعيش كل ما تريد الاحتفاظ به خارجها، ويجب أن تعرف بالضبط أي أمر يمحوه.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">الـ Named volumes لملفات قاعدة البيانات</h2>\n<p>أضف volume إلى خدمة <code dir=\"ltr\">db</code> وصرّح به في المستوى الأعلى:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"yaml\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">compose.yaml</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">services</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">  # api: …as before</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  db</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    image</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">postgres:17</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    environment</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">      POSTGRES_USER</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">notes</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">      POSTGRES_PASSWORD</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">notes</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">      POSTGRES_DB</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">notes</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    volumes</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">pgdata:/var/lib/postgresql/data</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">volumes</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  pgdata</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span></code></pre></div>\n<p>الـ <strong>named volume</strong> (وحدة تخزين مسمّاة) تخزين ينشئه Docker ويديره. يضيف Compose اسم المشروع كبادئة له، فيصبح اسم هذا <code dir=\"ltr\">notes_pgdata</code>. إنه ليس جزءًا من أي container، لذا فإن حذف <code dir=\"ltr\">db</code> وإعادة إنشائه يتركان البيانات في مكانها:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> compose</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> down</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">        # containers and network removed, pgdata kept</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> compose</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> up</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -d</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">       # notes are still there</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> volume</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> ls</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">           # notes_pgdata</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> volume</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> inspect</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes_pgdata</span></span></code></pre></div>\n<p>الـ named volumes هي الخيار الافتراضي الصحيح لملفات قاعدة البيانات. يضبط Docker الملكية بشكل صحيح للـ container، ولا تواجه غرائب نظام ملفات المضيف، وعلى Docker Desktop هي أسرع بكثير من الـ bind mounts لأنها تعيش داخل آلة Linux الافتراضية.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>الأمر down -v كعادة تنظيف</span></p>\n<p>الأمر <code dir=\"ltr\">docker compose down -v</code> يحذف البيئة <em>وكل</em> named volume تصرّح به. يتعلّمه الناس على أنه «إعادة الضبط الكاملة» ويكتبونه بحكم العادة. استخدم <code dir=\"ltr\">down</code> العادي افتراضيًا، واحتفظ بـ <code dir=\"ltr\">-v</code> للحظات التي تريد فيها حقًا قاعدة بيانات فارغة. وإذا كانت بيانات التطوير ثمينة، فخذ نسخة احتياطية أولًا:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">docker</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> compose</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> exec</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> db</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> pg_dump</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -U</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> notes</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ></span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> backup.sql</span></span></code></pre></div>\n</aside>\n<p>الاستعادة هي الأنبوب نفسه في الاتجاه المعاكس: <code dir=\"ltr\">docker compose exec -T db psql -U notes notes &lt; backup.sql</code>. الخيار <code dir=\"ltr\">-T</code> يعطّل الطرفية الوهمية (pseudo-TTY) حتى يمكن تمرير الملف إلى الداخل. جرّب ذلك مرة في وقت هادئ، حتى لا تكون أول استعادة لك في اليوم الذي تحتاج إليها فيه.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">الـ Bind mounts للملفات التي تعدّلها</h2>\n<p>الـ <strong>bind mount</strong> يربط مسارًا على جهازك بداخل الـ container. التعديلات على أي جهة تظهر على الأخرى فورًا. هذا ما تريده للملفات التي تعيش في مستودعك، مثل سكربت المخطّط:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"yaml\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">YAML</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  db</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    image</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">postgres:17</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    volumes</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">pgdata:/var/lib/postgresql/data</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">./db/init:/docker-entrypoint-initdb.d:ro</span></span></code></pre></div>\n<p>تشغّل image ‏Postgres كل ملف <code dir=\"ltr\">.sql</code> و<code dir=\"ltr\">.sh</code> في <code dir=\"ltr\">/docker-entrypoint-initdb.d</code>، بترتيب الأسماء، <strong>فقط حين يكون مجلد البيانات فارغًا</strong>. في أول <code dir=\"ltr\">up</code> على الإطلاق، ينشئ <code dir=\"ltr\">db/init/001-schema.sql</code> الجدول <code dir=\"ltr\">notes</code>. وفي كل تشغيل لاحق يُتجاهل المجلد، لأن الـ volume يحتوي أصلًا على قاعدة بيانات مهيّأة. حين تغيّر المخطّط بعد ذلك، اكتب migration يشغّلها التطبيق، أو امسح volume التطوير عن قصد.</p>\n<p>اللاحقة <code dir=\"ltr\">:ro</code> تجعل الـ mount للقراءة فقط داخل الـ container. أعطِ الـ containers أقل صلاحية تكفي.</p>\n<p>للـ bind mounts عيبان مزعجان. على Linux تكون الملفات مملوكة لـ UID مستخدم المضيف، الذي قد لا يطابق المستخدم داخل الـ container، فتفشل الكتابة بـ <code dir=\"ltr\">EACCES</code>. وعلى macOS وWindows يعبر الوصول إلى الملفات حدود الآلة الافتراضية فيكون أبطأ، ولهذا يجب ألا تُربط المجلدات الثقيلة مثل <code dir=\"ltr\">node_modules</code> من المضيف أبدًا. ستستخدم أداة أفضل لتعديل الكود الحيّ، Compose watch، في الدرس 3.4.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">الـ tmpfs للبيانات المؤقتة</h2>\n<p>الـ mount من نوع <code dir=\"ltr\">tmpfs</code> يعيش في الذاكرة ويختفي حين يتوقّف الـ container. إنه مفيد للملفات المؤقتة التي لا تريدها على القرص أبدًا، أو لقاعدة بيانات اختبار تُحذف بعد الاستخدام حيث تهمّ السرعة أكثر من البقاء:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"yaml\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">YAML</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  db-test</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    image</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">postgres:17</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    tmpfs</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">:</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      - </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">/var/lib/postgresql/data</span></span></code></pre></div>\n<h2 id=\"sec-4\" tabindex=\"-1\">الـ Volumes وإصدارات قاعدة البيانات</h2>\n<p>الـ volume يخزّن صيغة قاعدة البيانات على القرص، وهي تخصّ إصدارًا رئيسيًا واحدًا. إذا رفعت <code dir=\"ltr\">postgres:17</code> إلى <code dir=\"ltr\">postgres:18</code> وأبقيت الـ volume نفسه، فسيرفض الخادم الجديد البدء على الملفات القديمة. كما أن image ‏Postgres 18 نقلت موقع البيانات الافتراضي: فهي تتوقّع mount واحدًا في <code dir=\"ltr\">/var/lib/postgresql</code> (وتحفظ البيانات في مجلد فرعي يحمل رقم الإصدار) بدلًا من <code dir=\"ltr\">.../data</code>. وإذا وجدت نقطة دخولها بيانات أو mount في المسار القديم، فإنها تتوقّف برسالة خطأ تشرح هذا التغيير. اقرأ ملاحظات الإصدار الخاصة بالـ image قبل رفع إصدار رئيسي.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>عامل الترقيات الرئيسية كعمليات ترحيل</span></p>\n<p>في التطوير: خذ dump، وارفع الـ image، واحذف الـ volume القديم، وابدأ من جديد، واستعِد الـ dump. في الإنتاج ستخطّط للشيء نفسه باستخدام <code dir=\"ltr\">pg_upgrade</code> أو أدوات الترقية في قاعدة البيانات المُدارة، مع مسار للتراجع. وفي الحالتين، ليست مجرد تغيير في الـ tag.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">أي mount لأي مهمة</h2>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th>الحاجة</th>\n<th>الاستخدام</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>ملفات قاعدة بيانات تصمد بعد إعادة التشغيل</td>\n<td>named volume</td>\n</tr>\n<tr>\n<td>سكربتات بيانات أولية أو إعدادات من المستودع</td>\n<td>‏bind mount مع <code dir=\"ltr\">:ro</code></td>\n</tr>\n<tr>\n<td>مساحة مؤقتة سريعة تُحذف بعد الاستخدام</td>\n<td><code dir=\"ltr\">tmpfs</code></td>\n</tr>\n<tr>\n<td>الكود المصدري أثناء التطوير</td>\n<td>‏Compose watch (الدرس 3.4)</td>\n</tr>\n</tbody>\n</table></div><p>بياناتك الآن تعيش أطول من الـ containers. لكن شغّل البيئة من البداية الباردة، وقد ترى الـ API يفشل في أول بضعة طلبات: إنه يبدأ قبل أن يصبح Postgres جاهزًا لقبول الاتصالات. ترتيب الإقلاع هو التالي.</p>\n","toc":[{"id":"sec-1","text":"الـ Named volumes لملفات قاعدة البيانات","level":2},{"id":"sec-2","text":"الـ Bind mounts للملفات التي تعدّلها","level":2},{"id":"sec-3","text":"الـ tmpfs للبيانات المؤقتة","level":2},{"id":"sec-4","text":"الـ Volumes وإصدارات قاعدة البيانات","level":2},{"id":"sec-5","text":"أي mount لأي مهمة","level":2}],"takeaways":["الـ named volumes يديرها Docker وتعيش أطول من الـ containers؛ استخدمها لملفات قاعدة البيانات.","الـ bind mounts تربط مسارًا على المضيف بداخل الـ container؛ استخدمها للملفات التي تعدّلها على المضيف، مثل سكربتات البيانات الأولية، وركّبها للقراءة فقط متى استطعت.","الأمر <code dir=\"ltr\">docker compose down</code> يحتفظ بالـ named volumes؛ أما <code dir=\"ltr\">docker compose down -v</code> فيحذفها مع كل بياناتها.","السكربتات في <code dir=\"ltr\">/docker-entrypoint-initdb.d</code> لا تعمل إلا حين يكون مجلد بيانات Postgres فارغًا، أي عند أول تشغيل على الإطلاق.","صيغة تخزين قاعدة البيانات على القرص مرتبطة بإصدارها الرئيسي؛ والترقية بين إصدارات Postgres الرئيسية تحتاج إلى dump واستعادة أو <code dir=\"ltr\">pg_upgrade</code>، لا إلى tag جديد."],"further":[{"title":"Volumes","url":"https://docs.docker.com/engine/storage/volumes/"},{"title":"Bind mounts","url":"https://docs.docker.com/engine/storage/bind-mounts/"},{"title":"Compose volumes reference","url":"https://docs.docker.com/reference/compose-file/volumes/"}],"quiz":[{"q":"<p>تشغّل <code dir=\"ltr\">docker compose down</code> ثم <code dir=\"ltr\">docker compose up -d</code>. خدمة <code dir=\"ltr\">db</code> تركّب الـ named volume ‏<code dir=\"ltr\">pgdata</code>. ماذا يحدث لملاحظاتك؟</p>","options":[{"text":"تختفي، لأن <code dir=\"ltr\">down</code> يحذف الـ containers.","why":"الـ containers تُحذف، لكن الـ named volume لا يُحذف. يجد Postgres بياناته مجددًا في التشغيل التالي."},{"text":"تختفي ما لم تشغّل <code dir=\"ltr\">docker compose stop</code> أولًا.","why":"الفرق بين <code dir=\"ltr\">stop</code> و<code dir=\"ltr\">down</code> يخصّ الـ containers، لا الـ named volumes. كلاهما يترك <code dir=\"ltr\">pgdata</code> كما هو."},{"text":"تُستعاد من اللقطة الأولية في الـ image.","why":"الـ images لا تلتقط البيانات. الـ volume هو ما يحفظها."},{"text":"ما زالت موجودة؛ ‏<code dir=\"ltr\">down</code> لا يحذف الـ named volumes ما لم تضف <code dir=\"ltr\">-v</code>.","why":"صحيح. وحده <code dir=\"ltr\">docker compose down -v</code> (أو <code dir=\"ltr\">docker volume rm</code>) يحذف الـ volume."}],"answer":3},{"q":"<p>تعدّل <code dir=\"ltr\">db/init/001-schema.sql</code> لإضافة عمود، وتشغّل <code dir=\"ltr\">docker compose up -d</code>، فلا يظهر العمود. لماذا؟</p>","options":[{"text":"الـ bind mounts لا تُقرأ إلا عند بناء الـ image.","why":"الـ bind mounts حيّة؛ يرى الـ container تعديلك فورًا. المشكلة في توقيت قراءة Postgres للمجلد."},{"text":"سكربتات التهيئة لا تعمل إلا حين يكون مجلد البيانات فارغًا، والـ volume لديك فيه بيانات أصلًا.","why":"صحيح. نقطة الدخول تتخطّى <code dir=\"ltr\">/docker-entrypoint-initdb.d</code> إذا كانت قاعدة البيانات مهيّأة. استخدم migration، أو امسح volume التطوير عن قصد."},{"text":"يجب أن يكون اسم الملف <code dir=\"ltr\">init.sql</code> بالضبط.","why":"أي ملف <code dir=\"ltr\">.sql</code> أو <code dir=\"ltr\">.sql.gz</code> أو <code dir=\"ltr\">.sh</code> في ذلك المجلد يُنفَّذ، بترتيب الأسماء."},{"text":"الخيار <code dir=\"ltr\">:ro</code> يمنع Postgres من قراءة الملف.","why":"الخيار <code dir=\"ltr\">:ro</code> يمنع الـ container من <em>الكتابة</em>. القراءة مسموحة."}],"answer":1},{"q":"<p>أي mount يجب أن يحفظ بيانات Postgres في بيئة تطوير تريدها أن تصمد بعد إعادة التشغيل؟</p>","options":[{"text":"named volume مثل <code dir=\"ltr\">pgdata:/var/lib/postgresql/data</code>.","why":"صحيح. يديره Docker، ويعيش أطول من الـ containers، ويجنّبك مفاجآت صلاحيات الملفات والأداء على المضيف."},{"text":"‏mount من نوع <code dir=\"ltr\">tmpfs</code> في <code dir=\"ltr\">/var/lib/postgresql/data</code>.","why":"الـ tmpfs يعيش في الذاكرة ويختفي حين يتوقّف الـ container. يصلح للاختبارات المؤقتة، لا هنا."},{"text":"الـ layer القابلة للكتابة في الـ container، وهي الافتراضية.","why":"تلك الـ layer تُحذف مع الـ container، فيضيّع <code dir=\"ltr\">docker compose down</code> كل شيء."},{"text":"‏bind mount إلى مجلد <code dir=\"ltr\">src/</code> لديك.","why":"خلط ملفات قاعدة البيانات بشجرة الكود المصدري يستدعي commits بالخطأ وأخطاء صلاحيات."}],"answer":0},{"q":"<p>تغيّر image الـ <code dir=\"ltr\">db</code> من <code dir=\"ltr\">postgres:17</code> إلى <code dir=\"ltr\">postgres:18</code> وتُبقي الـ volume ‏<code dir=\"ltr\">pgdata</code> نفسه. ما الذي يجب أن تتوقّعه؟</p>","options":[{"text":"يرقّي Postgres الملفات في مكانها عند البدء.","why":"لا يحوّل Postgres مجلدات البيانات بين الإصدارات الرئيسية من تلقاء نفسه."},{"text":"لا شيء يتغيّر، لأن الـ volumes مستقلة عن الإصدار.","why":"الـ volume يخزّن صيغة Postgres على القرص، وهي خاصة بالإصدار الرئيسي."},{"text":"يرفض الإصدار الجديد مجلد البيانات القديم (وimage الإصدار 18 تتوقّع أيضًا مسار mount مختلفًا)؛ تحتاج إلى dump واستعادة أو <code dir=\"ltr\">pg_upgrade</code>.","why":"صحيح. الترقيات الرئيسية عملية ترحيل. في التطوير خذ dump، وامسح الـ volume، واستعِد؛ وفي الإنتاج خطّط لها مسبقًا."},{"text":"يُعاد تسمية الـ volume تلقائيًا إلى <code dir=\"ltr\">pgdata-18</code>.","why":"لا يعيد Compose تسمية الـ volumes أبدًا. يركّب بالضبط ما يسمّيه الملف."}],"answer":2}],"exercise":{"kind":"choice","multiple":true,"options":["pg-named","init-bind","src-volume","pg-tmpfs","tmpfs-scratch"],"correct":["pg-named","init-bind","tmpfs-scratch"],"title":"اختر الـ mount المناسب","prompt":"<p>تراجع خيارات التخزين في بيئة Compose الخاصة بـ notes-api. اختر <strong>كل</strong> ربط صحيح بين الحاجة ونوع الـ mount.</p>\n","hints":["اسأل سؤالين عن كل واحد: هل يجب أن تبقى البيانات بعد الـ container، وهل تأتي من مستودعك؟","الـ named volume لا يعكس أبدًا التعديلات التي تجريها في محرّرك؛ والـ tmpfs لا يصمد أبدًا بعد الإيقاف.","ثلاثة من الروابط صحيحة."],"explanation":"<p>بيانات Postgres مكانها named volume: فهو يصمد بعد <code dir=\"ltr\">down</code> ويتجنّب مشاكل الصلاحيات والأداء على المضيف. سكربتات البيانات الأولية تعيش في المستودع، لذا فإن bind mount للقراءة فقط إلى <code dir=\"ltr\">/docker-entrypoint-initdb.d</code> هو الصحيح. والمساحة المؤقتة التي يُتخلَّص منها هي بالضبط ما وُجد الـ tmpfs من أجله. أما named volume لـ <code dir=\"ltr\">src/</code> فسيجمّد نسخة من كودك لا ترى تعديلاتك أبدًا، والـ tmpfs لبيانات تريد الاحتفاظ بها يضيّعها مع كل إيقاف.</p>\n","checkLabels":{},"optionText":{"pg-named":{"text":"بيانات Postgres التي يجب أن تصمد بعد إعادة التشغيل ← named volume ‏<code dir=\"ltr\">pgdata:/var/lib/postgresql/data</code>","why":"صحيح. الـ named volumes تعيش أطول من الـ containers ويديرها Docker."},"init-bind":{"text":"سكربتات مخطّط SQL من المستودع ← bind mount ‏<code dir=\"ltr\">./db/init:/docker-entrypoint-initdb.d:ro</code>","why":"صحيح. الملفات تعيش في مستودعك، والـ container يحتاج إلى قراءتها فقط."},"src-volume":{"text":"تعديل <code dir=\"ltr\">src/</code> مباشرة أثناء التطوير ← named volume ‏<code dir=\"ltr\">src:/app/src</code>","why":"الـ named volume يُملأ مرة واحدة ثم يصبح مستقلًا عن مجلدك على المضيف، فلا تصل التعديلات إلى الـ container أبدًا. استخدم Compose watch أو bind mount."},"pg-tmpfs":{"text":"بيانات Postgres التي تريد الاحتفاظ بها بين أيام العمل ← <code dir=\"ltr\">tmpfs: /var/lib/postgresql/data</code>","why":"الـ tmpfs يعيش في الذاكرة؛ وإيقاف الـ container يمحوه."},"tmpfs-scratch":{"text":"ملفات مؤقتة لتشغيل اختبار يجب أن تختفي بعده ← <code dir=\"ltr\">tmpfs: /tmp/scratch</code>","why":"صحيح. سريع، ولا يُكتب على القرص أبدًا، ويختفي حين يتوقّف الـ container."}}},"words":670,"readMinutes":4}