لنفرض أنك في شركة ناشئة تبيع منتجاتها عبر الإنترنت، وكل طلب يولد فاتورة PDF يحفظها التطبيق على السيرفر، إضافة إلى قاعدة البيانات وصور المنتجات. وبعد سنة وصلت رسالة من نظام المراقبة تقول إن القرص الرئيسي ممتلئ بنسبة 91%، فطلب الفريق من مزود الاستضافة قرصاً إضافياً بحجم 100 GB، وظهر في لوحة التحكم مكتوباً عليه «مرفق Attached». ثم دخل أحد المطورين إلى السيرفر فلم يجد أي مجلد جديد، ولا أي مساحة إضافية في df -h، وكأن شيئاً لم يحدث. وبعد بحث سريع في الإنترنت كتب ثلاثة أوامر نسخها من إجابة قديمة، فظهر المجلد وظهرت المساحة، ثم أعاد تشغيل السيرفر في نهاية اليوم ليتأكد أن كل شيء بخير، فلم يرجع السيرفر أصلاً.
وكل خطوة في هذه القصة لها سبب واضح عندما تعرف كيف يرى Linux الأقراص: القرص الجديد جهاز خام لا يحمل أي شيء، ويحتاج جدول أقسام Partition Table، ثم نظام ملفات File System، ثم تركيباً Mount في مجلد، ثم سطراً في الملف /etc/fstab حتى يعود بعد الإقلاع، وسطر واحد خاطئ في هذا الملف يكفي لإيقاف الإقلاع كله. وفي هذا الدليل سوف نمر على كل هذه الخطوات على قرص حقيقي بالنسبة للنظام، ونرى أخطاءها الشائعة بأعيننا، ثم نضيف مساحة تبديل Swap، ونوسع القرص بعد ذلك دون إيقاف. وإذا لم تقرأ بعد عن الـ Inode وفكرة نظام الملفات فابدأ بقسم نظام الملفات في مقال نظام التشغيل Operating System.
وسوف نناقش في هذا المقال ما يلي:
- الطبقات الأربع بين القرص والمجلد، وكيف تراها بالأوامر
lsblkوblkidوfindmnt. - تقسيم القرص الجديد بجدول GPT وتهيئته بنظام ext4، ومشكلة واجهتنا مع ملف الجهاز المفقود.
- التركيب Mount، ولماذا اختفت ملفات كتبها التطبيق قبل التركيب.
- الملف fstab حقلاً حقلاً، ولماذا نستخدم الـ UUID بدلاً من اسم الجهاز، وكيف تكتشف السطر الخاطئ قبل أن يكتشفه الإقلاع.
- القرص الممتلئ والكتل المحجوزة Reserved Blocks، ولماذا يكتب root عندما يعجز التطبيق.
- مساحة التبديل Swap: ملف أم قسم، وكم تحتاج، ولماذا رفضها النظام على نوع معين من الأنظمة.
- توسيع القرص دون إيقاف، وخيارات التركيب مثل
roوnoexec، ثم الأخطاء الشائعة وقائمة تحقق.
تجهيز المختبر: قرص من ملف Loop Device
لكي نجرب كل شيء دون قرص حقيقي ودون أن نخاطر بأقراص الجهاز، نستخدم ميزة في النواة اسمها جهاز الحلقة Loop Device: ملف عادي على القرص تعرضه النواة كأنه قرص كامل، فتقسمه وتهيئه وتركبه بنفس الأوامر التي تستخدمها مع قرص حقيقي في السيرفر، كما تشرح صفحة losetup(8). والأوامر تعمل داخل Container من Debian 13 فيه أدوات الأقراص فقط:
FROM debian:trixie-slim
RUN apt-get update && apt-get install -y --no-install-recommends util-linux fdisk parted e2fsprogs procps && rm -rf /var/lib/apt/lists/*
WORKDIR /labdocker build -t sbl-disks:1 .
docker run -d --name sbl-dk --privileged sbl-disks:1 sleep infinityوالخيار --privileged ضروري هنا، لأن ربط ملف بجهاز حلقة وتركيب أنظمة الملفات عمليات تحتاج صلاحيات كاملة على النواة، وDocker يمنعها عن الـ Containers العادية لسبب وجيه، فلا تستخدم هذا الخيار أبداً مع Container يشغل تطبيقاً. ولاحظ أيضاً أن أجهزة الحلقة تخص نواة الجهاز كله، فالمختبر يجب أن ينتهي بفصلها كما سنفعل في آخره. وكل خطوة في سكربت مستقل في مجلد المختبر، يعمل بالأمر docker exec sbl-dk bash /lab/01-new-disk.sh وما يليه.
من القرص Disk إلى المجلد: أربع طبقات
قبل أي أمر لنرسم الصورة التي غابت عن المطور في القصة. فالقرص الذي أضافه المزود يظهر للنظام كجهاز كتل Block Device له اسم مثل /dev/sdb أو /dev/vdb أو /dev/nvme1n1، وهو في هذه اللحظة سلسلة من البايتات الفارغة لا يعرف عنها النظام شيئاً. وحتى تكتب فيه ملفاً يجب أن تمر بأربع طبقات بالترتيب:

- جدول الأقسام Partition Table: يقسم القرص إلى أجزاء مستقلة اسمها الأقسام Partitions، ونوعه اليوم GPT في الغالب، والنوع القديم MBR لا يدعم أقراصاً أكبر من 2 TiB. وقد يتساءل البعض: لماذا القسم إذا كنت سأستخدم القرص كله؟ والإجابة أن النظام يستطيع فعلاً أن يضع نظام الملفات على القرص كله دون أقسام، ولكن القسم يجعل القرص مفهوماً لأي أداة تراه، فلا تظن أداة أخرى أنه قرص فارغ فتكتب عليه، ولذلك هو العرف السائد.
- نظام الملفات File System: يحول القسم إلى Inodes ومجلدات وسجل Journal، وأشهره على السيرفرات ext4 وXFS، وقد شرحنا ما بداخل ext4 في مقال نظام الملفات من الداخل.
- التركيب Mount: يربط نظام الملفات بمجلد في الشجرة الواحدة التي تبدأ من
/، فلا توجد في Linux حروف أقراص مثل D: وE:، وإنما مجلدات تقود إلى أقراص مختلفة. - الملف fstab: يحفظ التركيب حتى يتكرر عند كل إقلاع، فالأمر
mountوحده يختفي أثره عند إعادة التشغيل.
والمطور في القصة نفذ الطبقات الثلاث الأولى بالأوامر المنسوخة، وأخطأ في الرابعة، وهذا ما سوف نصل إليه.
ربط القرص الجديد وتقسيمه بجدول GPT
السكربت الأول ينشئ ملفاً بحجم 1 GiB ليلعب دور القرص الجديد، ويربطه بجهاز حلقة، ثم يقسمه بالأداة parted بجدول GPT وقسم واحد يأخذ القرص كله:
mkdir -p /lab/disks
truncate -s 1G /lab/disks/data.img
ls -lhs /lab/disks/data.img
DEV=$(losetup -f --show -P /lab/disks/data.img)
echo "$DEV" > /lab/disks/dev
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS "$DEV"
parted -s "$DEV" mklabel gpt mkpart data ext4 1MiB 100%
parted -s "$DEV" unit MiB print
lsblk -o NAME,SIZE,TYPE,PARTLABEL "$DEV"
ls -l "${DEV}p1"
cat /sys/block/$(basename $DEV)/$(basename $DEV)p1/devوالمخرج سوف يكون كما يلي (حذفنا سطور التتبع التي يطبعها set -x):
0 -rw-r--r-- 1 root root 1.0G Oct 30 18:36 /lab/disks/data.img
NAME SIZE TYPE FSTYPE MOUNTPOINTS
loop2 1G loop
sh: 1: udevadm: not found
sh: 1: udevadm: not found
sh: 1: udevadm: not found
sh: 1: udevadm: not found
Model: Loopback device (loopback)
Disk /dev/loop2: 1024MiB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 1.00MiB 1023MiB 1022MiB data
NAME SIZE TYPE PARTLABEL
loop2 1G loop
`-loop2p1 1022M part
ls: cannot access '/dev/loop2p1': No such file or directory
259:0في المخرج أعلاه لاحظ التالي:
- الرقم 0 في أول السطر هو المساحة الحقيقية التي يشغلها الملف، بينما حجمه الظاهر 1.0G. فالأمر
truncateينشئ ملفاً متفرقاً Sparse File لا يأخذ مساحة إلا عندما نكتب فيه، وهذا يجعل المختبر خفيفاً. - الأمر
lsblkيعرض القرصloop2بحجم 1G دون نظام ملفات ودون نقطة تركيب، وهذا بالضبط ما رآه المطور في القصة عندما كتبlsblkفوجدsdbبلا شيء تحته. أماdf -hفلا يعرض إلا أنظمة الملفات المركبة، ولهذا لم تظهر المساحة فيه. - بدأنا القسم من 1MiB وليس من أول القرص، والسبب أن الجدول GPT نفسه يحتاج المساحة الأولى، وأن البدء من حد 1MiB يجعل القسم متوافقاً مع حجم الكتل الداخلية لأي قرص SSD، ولهذا تجد نفس الرقم في أغلب أدوات التقسيم.
- الجدول يقول إن القسم من 1.00MiB إلى 1023MiB، وآخر 1MiB محجوز للنسخة الاحتياطية من الجدول، فأداة GPT تحفظ نسختين في بداية القرص ونهايته.
مشكلة واجهتنا: القسم موجود وملف الجهاز Device Node غير موجود
آخر سطرين في المخرج هما أول مشكلة واجهتنا: lsblk يرى القسم loop2p1 بحجم 1022M، أي أن النواة تعرفه، ولكن الملف /dev/loop2p1 الذي نحتاجه للتهيئة غير موجود. والسطور udevadm: not found تشرح السبب: في أي نظام عادي توجد خدمة اسمها udev تراقب الأجهزة الجديدة وتنشئ لها ملفات في /dev، وأداة parted طلبتها بعد تعديل الجدول فلم تجدها، لأن الـ Container لا يشغل udev. والنواة نفسها تنشر رقمي الجهاز في الملف /sys/block/loop2/loop2p1/dev، وهما 259:0، أي الرقم الرئيسي Major والرقم الفرعي Minor اللذان يعرف بهما النظام أي جهاز، فأنشأنا الملف بأنفسنا بالأمر mknod. وعلى أي سيرفر حقيقي لن تحتاج هذه الخطوة، لأن udev يعمل دائماً، ولكنها تريك أن ملفات /dev ليست الأجهزة نفسها، وإنما أبواب إليها بأرقام.
تهيئة القسم Format بنظام ext4
السكربت الثاني ينشئ ملف الجهاز، ثم يهيئ القسم بالأمر mkfs.ext4 مع اسم data، ثم يركبه ويقرأ نتيجته:
DEV=$(cat /lab/disks/dev); P=${DEV}p1
mknod "$P" b $(cat /sys/block/$(basename $DEV)/$(basename $P)/dev | tr ':' ' ')
ls -l "$P"
mkfs.ext4 -L data "$P"
blkid "$P"
mkdir -p /srv/data
mount "$P" /srv/data
findmnt /srv/data
df -hT /srv/data
df -i /srv/data
ls -la /srv/databrw-r--r-- 1 root root 259, 0 Oct 30 18:37 /dev/loop2p1
mke2fs 1.47.2 (1-Jan-2025)
Discarding device blocks: done
Creating filesystem with 261632 4k blocks and 65408 inodes
Filesystem UUID: 4875ede9-71d9-4370-8967-4e2bcdb5cd33
Superblock backups stored on blocks:
32768, 98304, 163840, 229376
Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done
/dev/loop2p1: LABEL="data" UUID="4875ede9-71d9-4370-8967-4e2bcdb5cd33" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="data" PARTUUID="3d5f4d4b-74df-4c4f-af6a-568301e00391"
TARGET SOURCE FSTYPE OPTIONS
/srv/data /dev/loop2p1 ext4 rw,relatime
Filesystem Type Size Used Avail Use% Mounted on
/dev/loop2p1 ext4 988M 276K 921M 1% /srv/data
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/loop2p1 65408 12 65396 1% /srv/data
total 24
drwxr-xr-x 3 root root 4096 Oct 30 18:37 .
drwxr-xr-x 1 root root 4096 Oct 30 18:37 ..
drwx------ 2 root root 16384 Oct 30 18:37 lost+foundوهذا المخرج فيه أغلب ما تحتاج أن تعرفه عن نظام الملفات الجديد، فلنقرأه:
- الحرف
bفي أول سطرls -lيعني جهاز كتل Block Device، والرقمان 259 و0 هما رقما الجهاز اللذان قرأناهما من/sys. 261632 4k blocks and 65408 inodes: القسم صار 261632 كتلة من 4 KB، ومعها 65408 Inode، أي Inode واحد لكل 16 KB من المساحة، وهذا هو الحد الأقصى لعدد الملفات على هذا القرص مهما كانت صغيرة. وإذا كنت ستحفظ ملايين الملفات الصغيرة فاطلب Inodes أكثر عند التهيئة بالخيار-iأو-Nكما في صفحة mke2fs(8)، لأن عددها لا يتغير بعد ذلك إلا بالتوسيع.Filesystem UUID: رقم فريد يولدهmkfsلنظام الملفات، ويبقى معه مهما تغير اسم الجهاز، وهو الرقم الذي سنكتبه في fstab.Superblock backups: نسخ احتياطية من الكتلة التي تصف نظام الملفات، موزعة على القرص، حتى يستطيعfsckالإصلاح إذا تلفت النسخة الأولى.Creating journal: السجل Journal الذي يحمي نظام الملفات من التلف عند انقطاع الكهرباء.- الأمر
blkidيعرض كل الأسماء التي يمكن أن تشير بها إلى هذا القسم:LABELوUUIDلنظام الملفات، وPARTLABELوPARTUUIDللقسم نفسه في جدول GPT. - الأمر
df -hTيقول إن الحجم 988M والمستخدم 276K والمتاح 921M فقط، والفرق ليس خطأ، وسوف نعرف سببه في قسم القرص الممتلئ. - المجلد
lost+foundينشئهmkfsليضع فيهfsckالملفات التي يجدها بلا اسم عند الإصلاح، فلا تحذفه.
ext4 أم XFS: أي نظام ملفات File System تختار؟
اخترنا ext4 لأنه الافتراضي في Debian وUbuntu، وهو ما ستجده على أغلب السيرفرات التي تديرها، ولكنه ليس الخيار الوحيد. فالتوزيعات من عائلة Red Hat مثل RHEL وRocky وAlma تستخدم XFS افتراضياً، وستقابله حتماً إذا عملت عليها. والفرق بينهما لا يظهر في الاستخدام اليومي، فكلاهما نظام ملفات بسجل Journal، وكلاهما يتحمل أحجاماً أكبر بكثير مما ستحتاجه، وكلاهما يتوسع وهو مركب. وتظهر الفروق في الحالات الخاصة: ext4 يصغر إذا فككت تركيبه، وXFS لا يصغر عملياً، فإذا كنت تتوقع أن تعيد تقسيم القرص يوماً فهذه نقطة لصالح ext4. وفي المقابل يقسم XFS القرص إلى مجموعات تخصيص Allocation Groups يعمل كل منها باستقلال، فيتعامل جيداً مع الكتابة المتوازية من خيوط كثيرة على ملفات كبيرة، ولهذا يفضل في بعض قواعد البيانات وخوادم الملفات الكبيرة، كما يشرح توثيق XFS في النواة. وXFS ينشئ الـ Inodes عند الحاجة، فلا ينفد عددها بنفس الطريقة التي رأيناها في ext4. أما أنظمة مثل Btrfs وZFS فتقدم أشياء أخرى تماماً، مثل اللقطات Snapshots وفحص سلامة البيانات، وقد شرحنا الثاني في مقال ZFS للمبتدئين. والقاعدة العملية: استخدم الافتراضي في توزيعتك ما لم يكن عندك سبب محدد، لأن فريقك وأدواته ووثائقه مبنية عليه، والتنقل بين أنظمة الملفات على نفس السيرفرات يضاعف ما يجب أن يعرفه من يستلم المناوبة ليلاً.
التركيب Mount: ولماذا اختفت الفواتير؟
في القصة كان التطبيق مضبوطاً من البداية على حفظ الفواتير في المجلد /srv/data، وكان المجلد موجوداً على القرص الرئيسي قبل وصول القرص الجديد، فكتب فيه التطبيق فواتير لعدة ساعات. ثم ركب المطور القرص الجديد على نفس المجلد، فاختفت الفواتير، وظن الفريق أن البيانات ضاعت. والسكربت الثالث يعيد نفس المشهد:
umount /srv/data
echo "uploaded before the disk was mounted" > /srv/data/invoice-0001.pdf
du -sh /srv/data
mount /dev/loop2p1 /srv/data
ls /srv/data
umount /srv/data
ls /srv/data8.0K /srv/data
lost+found
invoice-0001.pdfبعد التركيب لم يظهر في المجلد إلا lost+found، وبعد فك التركيب رجعت الفاتورة كما هي. والتفسير أن التركيب لا ينسخ شيئاً ولا يحذف شيئاً، وإنما يجعل المجلد باباً إلى نظام ملفات آخر، فيصبح ما كان في المجلد على القرص الرئيسي مخفياً تحت الباب، ويبقى يشغل مساحة القرص الرئيسي دون أن يراه أحد:

وهذا يسبب مشكلتين على السيرفر: بيانات «ضائعة» كتبت في الوقت الخطأ، ومساحة على القرص الرئيسي لا يفسرها du لأنه لا يرى ما تحت نقطة التركيب. والحل في هذه الحالة أن تفك التركيب، وتنقل الملفات إلى مكان مؤقت، ثم تركب القرص وتعيدها إليه. والوقاية أبسط: ركب القرص قبل أن تشغل التطبيق الذي يكتب فيه، وإذا كان التطبيق خدمة systemd فاجعلها تنتظر التركيب بالخيار RequiresMountsFor=/srv/data كما في صفحة systemd.unit(5)، فلا تبدأ الخدمة إذا لم يكن القرص مركباً، بدلاً من أن تكتب بصمت في القرص الرئيسي.
لماذا لا يتفق df مع du؟
هذا السؤال يأتي دائماً بعد مشكلة مثل الفواتير المخفية: df يقول إن القرص الرئيسي مستخدم بنسبة 91%، ومجموع ما يعرضه du لكل المجلدات أقل من ذلك بكثير. والسبب أن الأمرين يجيبان عن سؤالين مختلفين، كما في صفحتي df(1) وdu(1): الأمر df يسأل نظام الملفات نفسه عن عدد الكتل المستخدمة والحرة، أما du فيمشي في شجرة المجلدات ويجمع أحجام الملفات التي يراها. فكل ما لا يراه du في الشجرة يظهر في df فقط، وأشهره أربعة أشياء: ملفات مخفية تحت نقطة تركيب كما رأينا، وملفات محذوفة ما زال برنامج يفتحها، والكتل المحجوزة لـ root التي تحسب عند df ضمن غير المتاح، والملفات المتفرقة Sparse Files التي يختلف حجمها الظاهر عن المساحة التي تشغلها فعلاً. وقد رأينا الأخيرة في أول سطر من المختبر، فالملف data.img حجمه في ls 1.0G بينما المساحة التي يشغلها صفر، والأمر du بدون خيارات يعرض المساحة الفعلية، والأمر du --apparent-size يعرض الحجم الظاهر. ولكي تجد المساحة المخفية تحت نقطة تركيب على سيرفر يعمل، دون أن تفك تركيب أي شيء، ركب القسم الرئيسي نفسه مرة ثانية في مجلد مؤقت بالأمر mount --bind / /mnt/rootview، فترى فيه المجلدات كما هي على القرص دون الأقراص المركبة فوقها، ثم استخدم du هناك.
الملف fstab: التركيب الذي يبقى بعد الإقلاع
الأمر mount يركب الآن فقط، ومع أول إعادة تشغيل يرجع المجلد فارغاً ويعود التطبيق للكتابة في القرص الرئيسي، وهي نفس المشكلة السابقة مرة أخرى. ولكي يتكرر التركيب عند كل إقلاع نكتب سطراً في الملف /etc/fstab، الذي يقرؤه النظام عند الإقلاع ويركب كل ما فيه. والسطر الذي أضفناه:
UUID=$(blkid -s UUID -o value /dev/loop2p1)
echo "UUID=$UUID /srv/data ext4 defaults,noatime,nofail 0 2" >> /etc/fstab
cat /etc/fstab# UNCONFIGURED FSTAB FOR BASE SYSTEM
UUID=4875ede9-71d9-4370-8967-4e2bcdb5cd33 /srv/data ext4 defaults,noatime,nofail 0 2والسطر ستة حقول تفصلها مسافات، كما في صفحة fstab(5):

- المصدر: كتبناه بالصيغة
UUID=وليس/dev/loop2p1، والسبب في القسم التالي. - الخيار
noatime: يمنع نظام الملفات من كتابة وقت آخر قراءة في كل مرة يقرأ فيها أحد ملفاً، فيوفر كتابات لا يحتاجها أغلب الناس. والوضع الافتراضيrelatimeالذي رأيناه فيfindmntحل وسط لا يحدث هذا الوقت إلا إذا كان أقدم من وقت آخر تعديل أو مضى عليه يوم. - الخيار
nofail: وهو أهم ما في السطر، فبدونه يكون هذا التركيب شرطاً لإكمال الإقلاع، فإذا غاب القرص لأي سبب، مثل فصله من لوحة المزود أو خطأ في الرقم، توقف النظام عن الإقلاع ودخل وضع الطوارئ Emergency Mode وانتظر من يدخل إليه من شاشة المزود. ومعnofailيقول توثيق systemd.mount(5) إن الإقلاع يكمل دون انتظار هذا التركيب ومهما كانت نتيجته. واستخدمه لكل قرص بيانات، ولا تستخدمه للقسم الرئيسي طبعاً. - الرقم الأخير 2: يعني أن
fsckيفحص نظام الملفات عند الإقلاع إذا احتاج ذلك، بعد القسم الرئيسي الذي يأخذ الرقم 1.
وقبل أي إعادة تشغيل يوجد أمران يغنيانك عن الاكتشاف المؤلم، وهما ما نسيه المطور في القصة: findmnt --verify الذي يفحص الملف كله سطراً سطراً، ثم mount -a الذي يركب كل ما في الملف ولم يركب بعد، فإذا نجح الآن نجح عند الإقلاع غالباً:
findmnt --verify --verbose 2>&1 | tail -8
mount -a
findmnt /srv/data/srv/data
[ ] target exists
[ ] VFS options: noatime
[ ] userspace options: nofail
[ ] UUID=4875ede9-71d9-4370-8967-4e2bcdb5cd33 translated to /dev/loop2p1
[ ] source /dev/loop2p1 exists
[ ] FS type is ext4
Success, no errors or warnings detected
TARGET SOURCE FSTYPE OPTIONS
/srv/data /dev/loop2p1 ext4 rw,noatimeولاحظ أن الأداة تحققت من كل شيء: المجلد موجود، والخيارات مفهومة، والـ UUID يقود إلى جهاز موجود، والنوع صحيح. وبعد mount -a صار التركيب بالخيار noatime كما كتبنا.
ماذا لو كان في سطر fstab خطأ واحد؟ وضع الطوارئ Emergency Mode
لنكرر الآن ما حدث في القصة: سطر لقرص نسخ احتياطي بحرف واحد خاطئ في آخر الـ UUID، وبدون nofail، ولمجلد غير موجود. والسكربت الرابع يضيفه ثم يفحص:
cp /etc/fstab /etc/fstab.bak
echo "UUID=4875ede9-71d9-4370-8967-4e2bcdb5cd34 /srv/backup ext4 defaults 0 2" >> /etc/fstab
findmnt --verify
mount -a; echo "mount -a exit: $?"
cp /etc/fstab.bak /etc/fstab0 parse errors, 2 errors, 0 warnings
/srv/backup
[E] unreachable on boot required target: No such file or directory
[E] unreachable on boot required source: UUID=4875ede9-71d9-4370-8967-4e2bcdb5cd34
mount: /srv/backup: can't find UUID=4875ede9-71d9-4370-8967-4e2bcdb5cd34.
mount -a exit: 32والعبارة المهمة هنا unreachable on boot required، أي أن هذا التركيب مطلوب للإقلاع ولن يتحقق، وهذا بالضبط ما أوقف سيرفر القصة. ولو كتب المطور هذا الأمر قبل إعادة التشغيل لرأى الخطأين في ثانية. ولاحظ أننا نسخنا الملف قبل التعديل وأعدناه بعده، وهذه عادة تستحق أن تلتزم بها مع fstab تحديداً. وإذا وجدت نفسك في وضع الطوارئ بعد إعادة التشغيل فالحل من شاشة المزود: ادخل بكلمة مرور root، وأعد تركيب القسم الرئيسي للكتابة بالأمر mount -o remount,rw /، وأصلح السطر أو علق عليه بعلامة #، ثم أعد الإقلاع.
running، لأن تركيب الأقراص عند الإقلاع مهمة السيرفر الحقيقي. ولهذا فإن findmnt --verify وmount -a هما الاختبار الذي تملكه قبل الإقلاع، فلا تتجاوزهما.لماذا الـ UUID وليس اسم الجهاز؟
أسماء مثل /dev/sdb تعطيها النواة للأقراص بترتيب اكتشافها عند الإقلاع، والترتيب ليس ثابتاً: إضافة قرص جديد، أو تغيير منفذ، أو قرص يتأخر في الاستجابة، كل ذلك قد يجعل sdb اسماً لقرص آخر في الإقلاع التالي، فيركب النظام القرص الخطأ في المجلد الخطأ، أو يفشل في التركيب. وفي المختبر جربنا ذلك: فصلنا قرص البيانات، وربطنا قرصاً آخر صغيراً فأخذ الاسم loop2، ثم أعدنا ربط قرص البيانات:
umount /srv/data
losetup -d /dev/loop2
truncate -s 64M /lab/disks/other.img
losetup -f --show /lab/disks/other.img
DEV=$(losetup -f --show -P /lab/disks/data.img); echo "data disk is now $DEV"
blkid /dev/loop3p1
mount -a
findmnt /srv/data/dev/loop2
data disk is now /dev/loop3
/dev/loop3p1: LABEL="data" UUID="4875ede9-71d9-4370-8967-4e2bcdb5cd33" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="data" PARTUUID="3d5f4d4b-74df-4c4f-af6a-568301e00391"
TARGET SOURCE FSTYPE OPTIONS
/srv/data /dev/loop3p1 ext4 rw,noatimeصار قرص البيانات loop3، والقسم loop3p1، ومع ذلك ركبه mount -a في /srv/data دون أي تعديل في fstab، لأن الـ UUID بقي كما هو. ولو كتبنا في fstab /dev/loop2p1 لحاول النظام تركيب القرص الصغير الآخر في مكان الفواتير:

وفي الحقيقة ظهر أول مرة في هذه الخطوة المصدر /dev/loop2p1 بدلاً من loop3p1، والسبب ملف الجهاز القديم الذي أنشأناه يدوياً وبقي في /dev برقمي 259:0، وهما نفس رقمي القسم الجديد، فوجد النظام القسم عبر الاسم القديم. فالجهاز كان صحيحاً والاسم مضللاً، وبعد حذف الملف القديم ظهر الاسم الصحيح. وهذا تفصيل خاص بغياب udev في المختبر، ولكنه يؤكد نفس الدرس: الاسم في /dev مجرد باب، والذي يحدد القرص هو رقماه وما عليه. وفي السحابة المشكلة أوضح، فتوثيق Amazon عن أسماء أقراص EBS من نوع NVMe يقول إن تعريفات النظام تكتشف الأقراص المرفقة عند الإقلاع وتعطيها أسماء بحسب ترتيب استجابتها، لا بحسب الاسم الذي اخترته في لوحة التحكم، فالقرص الذي سميته عند الإرفاق /dev/sdf قد يظهر داخل السيرفر باسم /dev/nvme1n1 أو /dev/nvme2n1. ولهذا السبب نفسه تنشئ مزودات مثل Google Cloud روابط ثابتة للأقراص في /dev/disk/by-id/ كما في توثيقها عن روابط الأقراص. والبدائل المقبولة للـ UUID في fstab هي LABEL=data إذا كنت متأكداً أن الاسم فريد على السيرفر، وPARTUUID عندما تريد أن تشير إلى القسم لا إلى نظام الملفات، والمسارات الثابتة في /dev/disk/by-id/ التي تبنى من الرقم التسلسلي للقرص.
القرص الممتلئ والكتل المحجوزة Reserved Blocks
رأينا أن df عرض الحجم 988M والمتاح 921M على قرص فارغ تقريباً. ولكي نفهم هذا الفرق ملأنا القرص كما يملؤه التطبيق، أي بمستخدم عادي اسمه app وليس root، حتى لم يبق شيء:
tune2fs -l /dev/loop3p1 | grep -E "Block count|Reserved block count|Block size"
su -s /bin/bash app -c 'dd if=/dev/zero of=/srv/data/uploads/big.bin bs=1M status=none; echo "dd exit: $?"; ls -lh /srv/data/uploads/big.bin; echo "one more line" >> /srv/data/uploads/log.txt; echo "append exit: $?"'
df -h /srv/data
echo "root can still write" > /srv/data/root-note.txt && echo "root write ok"Block count: 261632
Reserved block count: 13081
Block size: 4096
dd: error writing '/srv/data/uploads/big.bin': No space left on device
dd exit: 1
-rw-rw-r-- 1 app app 921M Oct 30 18:39 /srv/data/uploads/big.bin
append exit: 1
bash: line 1: echo: write error: No space left on device
Filesystem Size Used Avail Use% Mounted on
/dev/loop3p1 988M 921M 0 100% /srv/data
root write okفي المخرج أعلاه لاحظ التالي:
- نظام الملفات يحجز 13081 كتلة من 261632، أي 5% بالضبط، وهي 51 MiB تقريباً، وهذه هي القيمة الافتراضية لـ ext4.
- المستخدم
appكتب 921M ثم توقف بالخطأNo space left on device، واسمه في البرمجةENOSPC، وأي محاولة كتابة بعدها فشلت، حتى إضافة سطر واحد إلى ملف سجل. والسطرappend exit: 1ظهر قبل رسالة الخطأ نفسها لأن المخرج العادي ومخرج الأخطاء يصلان إلى الشاشة في طريقين مختلفين، وهذا شكل ستراه كثيراً. dfيقول 100% والمتاح صفر، بينما root كتب ملفاً بعد ذلك دون مشكلة.
والسبب كما تقول صفحة tune2fs(8) أن ext4 يحجز نسبة من الكتل لا يستخدمها إلا root، حتى تستمر خدمات النظام التي تعمل بصلاحياته في العمل بعد أن يملأ المستخدمون القرص، وحتى يقلل التجزئة. وهذا في القسم الرئيسي حماية حقيقية: عندما يملأ تطبيق القرص يستطيع مدير النظام أن يدخل ويحذف ويصلح. أما في قرص بيانات كبير لا يكتب فيه إلا التطبيق، فإن 5% من 2 TB هي 100 GB معطلة، ولذلك يمكنك تقليلها:
tune2fs -m 1 /dev/loop3p1
df -h /srv/data
su -s /bin/bash app -c 'echo "one more line" >> /srv/data/uploads/log.txt; echo "append exit: $?"'tune2fs 1.47.2 (1-Jan-2025)
Setting reserved blocks percentage to 1% (2616 blocks)
Filesystem Size Used Avail Use% Mounted on
/dev/loop3p1 988M 921M 41M 96% /srv/data
append exit: 0صار المحجوز 1%، وظهر 41M متاحة للتطبيق دون أن نحذف شيئاً، ونجحت الكتابة. وهذا تغيير يعمل فوراً على نظام ملفات مركب. ولاحظ أن الحجم نفسه 988M وليس 1022M لأن جداول الـ Inodes والسجل أخذت مكانها من القسم عند التهيئة، أما الفرق بين الحجم والمتاح فمعظمه الكتل المحجوزة لـ root، وجزء صغير منه احتياطي داخلي يحتفظ به ext4 لعملياته، ولهذا ظهر 41M وليس 51M بعد التغيير:

ولا تقلل هذه النسبة في القسم الرئيسي، فهي التي تنقذك عندما تملأ السجلات القرص في منتصف الليل. ويوجد نوعان آخران من «القرص الممتلئ» يربكان الناس: نفاد الـ Inodes بينما المساحة فارغة، وملف محذوف ما زال برنامج يكتب فيه فلا تعود مساحته، وقد شرحنا الاثنين مع طريقة كشفهما في قسم القرص من مقال تحليل أداء سيرفر Linux في 60 ثانية، فلن نكررهما هنا.
مساحة التبديل Swap: ملف أم قسم؟
مساحة التبديل Swap مكان على القرص تنقل إليه النواة صفحات الذاكرة التي لم تستخدم منذ مدة، لتفرغ مكانها في RAM لمن يحتاجه الآن. وهي ليست ذاكرة إضافية بالمعنى الحقيقي، لأن القرص أبطأ من الذاكرة بآلاف المرات، وإنما مساحة أمان: تبقي السيرفر يعمل ببطء بدلاً من أن يبدأ قاتل العمليات OOM Killer في إنهاء الخدمات عند أول ضغط على الذاكرة. وقديماً كان الناس يخصصون لها قسماً كاملاً عند تثبيت النظام، أما اليوم فالشائع ملف عادي، لأنك تنشئه وتكبره وتحذفه دون إعادة تقسيم القرص. والسكربت السادس ينشئ ملف Swap بحجم 256 MiB على قرص البيانات:
swapon --show
free -m
cat /proc/sys/vm/swappiness
fallocate -l 256M /srv/data/swapfile
chmod 600 /srv/data/swapfile
mkswap /srv/data/swapfile
swapon /srv/data/swapfile
swapon --show
free -mNAME TYPE SIZE USED PRIO
/dev/sdb partition 8G 0B -2
total used free shared buff/cache available
Mem: 32036 2034 27719 41 2774 30001
Swap: 8192 0 8192
60
Setting up swapspace version 1, size = 256 MiB (268431360 bytes)
no label, UUID=a09113c0-ee4c-491e-8466-6af8dbfb0784
NAME TYPE SIZE USED PRIO
/dev/sdb partition 8G 0B -2
/srv/data/swapfile file 256M 0B -3
total used free shared buff/cache available
Mem: 32036 2033 27719 41 2774 30002
Swap: 8447 0 8447في المخرج أعلاه لاحظ التالي:
- الجهاز الذي يشغل Docker عنده قسم Swap بحجم 8G هو
/dev/sdb، وبعد التفعيل ظهر ملفنا تحته بنوعfileوأولوية أقل، فالنواة تستخدم الأعلى أولوية أولاً. - الصلاحية 600 ضرورية، لأن ملف Swap يحمل نسخاً من ذاكرة البرامج، وفيها أحياناً كلمات مرور ومفاتيح، فلا يجب أن يقرأه غير root، والأمر
swaponيحذرك إذا كانت الصلاحيات أوسع. - السطر Swap في
freeزاد من 8192 إلى 8447 وليس 8448، والفرق صفحة واحدة يحجزهاmkswapلرأس الملف. - القيمة
swappinessهي 60، وهي تقول للنواة كم تميل إلى نقل صفحات البرامج إلى Swap بدلاً من التخلص من نسخ الملفات المحفوظة في الذاكرة، والمدى من 0 إلى 200 كما في توثيق إعدادات الذاكرة في النواة.
ولكي يبقى الملف بعد الإقلاع يضاف إلى fstab بسطر نوعه swap ونقطة تركيبه none:
/srv/data/swapfile none swap defaults,nofail 0 0وبعد التجربة ألغينا التفعيل بالأمر swapoff وحذفنا الملف، لأن ملف Swap المفعل من داخل الـ Container يخص نواة الجهاز كله وليس الـ Container. وكم تحتاج من Swap؟ القاعدة القديمة «ضعف الذاكرة» وضعت لأجهزة فيها 256 MB من RAM، ولا معنى لها على سيرفر فيه 64 GB. والمعقول لأغلب السيرفرات مساحة صغيرة من 1 إلى 4 GB تمتص الضغط المفاجئ وتعطيك وقتاً لترى المشكلة في المراقبة، أما إذا وجدت السيرفر يستخدم Swap باستمرار فالحل ذاكرة أكثر وليس Swap أكبر. وتشرح أقسام الذاكرة في مقال تحليل الأداء متى يصبح Swap مشكلة وكيف تقيسه.
ماذا لو وضعنا ملف Swap على نظام ملفات لا يناسبه؟
قبل أن نضع الملف على قرص البيانات جربنا أن نضعه على نظام ملفات الـ Container نفسه، وهو من نوع overlay:
overlay overlay 1055762868 156584856 845474540 16% /
swapon: /swap-on-overlay: swapon failed: Invalid argument
swapon exit: 255رفضت النواة الملف برسالة Invalid argument رغم أن mkswap نجح. والسبب أن النواة تكتب في ملف Swap مباشرة إلى كتل القرص دون المرور بنظام الملفات، فتحتاج أن تعرف مكان كل كتلة مسبقاً وأن يبقى ثابتاً، وأنظمة مثل overlay لا تعطيها ذلك. وتذكر صفحة swapon(8) حالات مشابهة: الملفات المتفرقة التي ينشئها truncate أو cp مرفوضة، وعلى Btrfs يحتاج الملف خاصية عدم النسخ عند الكتابة، وقد يعامل fallocate على بعض أنظمة الملفات كملف متفرق، ولذلك تنصح الصفحة بالأمر dd من /dev/zero كأكثر طريقة تعمل في كل مكان. أما على ext4 فقد عمل fallocate كما رأينا.
توسيع القرص Resize دون إيقاف
بعد أشهر امتلأ قرص البيانات أيضاً، فزاد الفريق حجمه من لوحة المزود. وهنا يتكرر ما حدث في البداية: الحجم الجديد لا يظهر في df، لأن الزيادة وصلت إلى طبقة القرص فقط، والقسم ونظام الملفات ما زالا بالحجم القديم. ومثلنا ذلك في المختبر بزيادة حجم الملف إلى 1.5 GiB، ثم أخبرنا جهاز الحلقة بالحجم الجديد بالأمر losetup -c، وهو ما يحدث تلقائياً في أغلب السحابات أو بإعادة فحص القرص:
NAME SIZE
loop3 1.5G
`-loop3p1 1022M
Filesystem Size Used Avail Use% Mounted on
/dev/loop3p1 988M 284K 961M 1% /srv/dataالقرص 1.5G، والقسم 1022M، ونظام الملفات 988M. وأول محاولة كانت توسيع القسم بالأداة parted وهو مركب، فتوقفت عند سؤالين لا يقبل الوضع غير التفاعلي الإجابة عنهما:
Warning: Not all of the space available to /dev/loop3 appears to be used, you can fix the GPT to use all of the space (an extra 1048576 blocks) or continue with the current setting?
Warning: Partition /dev/loop3p1 is being used. Are you sure you want to continue?والتحذير الأول مهم: نسخة الجدول الاحتياطية في GPT تحفظ في آخر القرص، وعندما كبر القرص بقيت النسخة في مكانها القديم في المنتصف، فيجب نقلها إلى النهاية الجديدة أولاً. فانتقلنا إلى الأداة sfdisk التي تعمل جيداً في السكربتات، وهذا السكربت السابع:
sfdisk --relocate gpt-bak-std /dev/loop3
echo ", +" | sfdisk --no-reread -N 1 /dev/loop3 2>&1 | tail -6
lsblk -o NAME,SIZE /dev/loop3
partx -u /dev/loop3
lsblk -o NAME,SIZE /dev/loop3
resize2fs /dev/loop3p1
df -h /srv/data
cat /srv/data/orders.csvThe partition table has been altered.
Calling ioctl() to re-read partition table.
Re-reading the partition table failed.: Device or resource busy
The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe(8) or partx(8).
Syncing disks.
NAME SIZE
loop3 1.5G
`-loop3p1 1022M
NAME SIZE
loop3 1.5G
`-loop3p1 1.5G
resize2fs 1.47.2 (1-Jan-2025)
Filesystem at /dev/loop3p1 is mounted on /srv/data; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 1
The filesystem on /dev/loop3p1 is now 392955 (4k) blocks long.
Filesystem Size Used Avail Use% Mounted on
/dev/loop3p1 1.5G 288K 1.5G 1% /srv/data
order 1001في المخرج أعلاه لاحظ التالي:
- الأمر الأول نقل نسخة الجدول الاحتياطية إلى آخر القرص، والثاني جعل القسم الأول يأخذ كل المساحة، والرمز
, +يعني «نفس البداية، وأكبر نهاية ممكنة». Re-reading the partition table failed.: Device or resource busy: الجدول على القرص تغير، ولكن النواة رفضت قراءته من جديد لأن القسم مركب ومستخدم، فبقيlsblkيعرض 1022M. وهذه هي المشكلة الثانية التي واجهتنا، وحلها في الرسالة نفسها: الأمر partx -u يخبر النواة بالحجم الجديد للقسم دون أن يطلب إعادة قراءة الجدول كله، فصار القسم 1.5G.- الأمر resize2fs وسع ext4 وهو مركب، وهذا ما تعنيه عبارة
on-line resizing، فصار 392955 كتلة بدلاً من 261632. - الملف
orders.csvالذي كتبناه قبل التوسيع بقي كما هو، والتطبيق لم يتوقف لحظة.

وعلى السيرفر الحقيقي تختصر الأداة growpart الخطوتين الأوليين في أمر واحد، وإذا كان القرص مبنياً على LVM كما في التثبيت الافتراضي لـ Ubuntu فهناك طبقتان إضافيتان، وقد شرحنا الطريق كاملاً لقرص النظام في دليل توسيع قرص النظام في Ubuntu عند استخدام LVM. ولاحظ أن التوسيع ممكن وهو مركب، أما التصغير فقصة أخرى: ext4 لا يصغر إلا وهو غير مركب، وXFS لا يدعم التصغير إلا في حالة محدودة جداً كما تقول صفحة xfs_growfs(8)، ولذلك ابدأ بحجم معقول ووسع عند الحاجة، ولا تفعل العكس. وقبل أي تعديل على جدول الأقسام خذ Snapshot أو نسخة احتياطية، فخطأ في رقم بداية القسم يجعل نظام الملفات غير قابل للقراءة، وقد شرحنا ذلك في دليل النسخ الاحتياطي بقاعدة 3-2-1.
خيارات التركيب Mount Options: للقراءة فقط ومنع التنفيذ
الخيارات في الحقل الرابع من fstab تغير سلوك نظام الملفات كله، وأغلبها يمكن تغييره دون فك التركيب بالخيار remount. وجربنا اثنين منها على قرص البيانات:
mount -o remount,ro /srv/data
touch /srv/data/new.txt
mount -o remount,rw,noexec /srv/data
printf '#!/bin/sh\necho hello from the data disk\n' > /srv/data/run.sh && chmod +x /srv/data/run.sh
/srv/data/run.sh; echo "exec exit: $?"
sh /srv/data/run.sh
mount -o remount,exec /srv/datatouch: cannot touch '/srv/data/new.txt': Read-only file system
/lab/07-grow-and-options.sh: line 22: /srv/data/run.sh: Permission denied
exec exit: 126
hello from the data diskro: للقراءة فقط، ومفيد عندما تريد نسخ قرص دون أن يتغير، أو عندما تحمي قرص أرشيف من خطأ. ولاحظ أن النواة نفسها قد تعيد تركيب نظام الملفات بهذا الوضع إذا اكتشفت فيه خطأ، عندما يكون الخيارerrors=remount-roمضبوطاً، وهو الشائع للقسم الرئيسي في Debian وUbuntu، فإذا رأيت فجأةRead-only file systemعلى قرص يعمل فاقرأ سجل النواة بالأمرdmesgقبل أي شيء.noexec: يمنع تشغيل أي برنامج من هذا القرص، فرفضت النواة تشغيل السكربت برمز 126 رغم صلاحية التنفيذ. وهو مناسب لمجلدات الرفع مثل الفواتير والصور، فإذا رفع مخترق سكربتاً عبر ثغرة لا يستطيع تشغيله مباشرة. ولكن انظر إلى السطر الأخير:sh /srv/data/run.shعمل، لأن البرنامج الذي ينفذ هنا هوshمن القرص الرئيسي، والسكربت مجرد ملف يقرؤه. فالخيارnoexecطبقة حماية مفيدة وليس جداراً كاملاً.- ومن الخيارات الشائعة أيضاً
nosuidالذي يتجاهل صلاحية SUID على هذا القرص، وnodevالذي يمنع ملفات الأجهزة عليه، وكلاهما معقول لأي قرص بيانات، وتجد القائمة الكاملة في صفحة mount(8).
الأوامر Commands التي تحتاجها مع الأقراص Disks
| السؤال | الأمر | ما تبحث عنه |
|---|---|---|
| ما الأقراص والأقسام الموجودة؟ | lsblk -f | قرص بلا أقسام أو قسم بلا نظام ملفات |
| ما أسماء هذا القسم الثابتة؟ | blkid /dev/sdb1 | UUID وLABEL وPARTUUID |
| ما المركب الآن وبأي خيارات؟ | findmnt أو findmnt /srv/data | المصدر والخيارات الفعلية |
| هل الملف fstab سليم؟ | findmnt --verify ثم mount -a | أي سطر بالحرف [E] |
| كم بقي من المساحة والـ Inodes؟ | df -hT وdf -i | الاستخدام ونوع نظام الملفات |
| من يأخذ المساحة؟ | du -xh --max-depth=1 /srv | الخيار -x يبقى في نفس نظام الملفات |
| كم يحجز ext4 لـ root؟ | tune2fs -l /dev/sdb1 | Reserved block count |
| ما مساحة التبديل المفعلة؟ | swapon --show وfree -m | الحجم والاستخدام والأولوية |
| هل وصل الحجم الجديد؟ | lsblk ثم df -h | أي طبقة ما زالت بالحجم القديم |
أخطاء شائعة
- البحث عن القرص الجديد في
df -h: لا يظهر فيه حتى يقسم ويهيأ ويركب، والأمر الصحيحlsblk. - تصديق
duوحده عند البحث عن المساحة: لا يرى ما تحت نقاط التركيب ولا الملفات المحذوفة المفتوحة، فقارنه دائماً بما يقولهdf. - تهيئة القرص الخطأ:
mkfsلا يسأل، فتأكد من الاسم بالحجم فيlsblkوبأنه بلا نظام ملفات قبل أي تهيئة. - تشغيل التطبيق قبل التركيب: يكتب في المجلد على القرص الرئيسي، ثم يخفي التركيب ما كتبه.
- الاكتفاء بالأمر
mount: يختفي أثره عند إعادة التشغيل، ويعود التطبيق للكتابة في القرص الرئيسي دون أن يلاحظ أحد. - كتابة
/dev/sdb1في fstab: الأسماء تتغير بترتيب الاكتشاف، والصحيحUUID=. - نسيان
nofailلقرص بيانات: غيابه يوقف الإقلاع كله في وضع الطوارئ. - إعادة التشغيل دون
findmnt --verifyوmount -a: هذه هي الطريقة التي توقف بها سيرفر القصة. - تقليل الكتل المحجوزة في القسم الرئيسي: تفقد المساحة التي تنقذك عندما تملأ السجلات القرص.
- ملف Swap بصلاحيات مفتوحة، أو على نظام ملفات لا يناسبه، أو مصنوع بالأمر
truncate. - توسيع القرص من لوحة المزود والاكتفاء بذلك: يجب توسيع القسم ثم نظام الملفات، وقد يحتاج القسم المستخدم
partx -uحتى تراه النواة.
قائمة تحقق Checklist لإضافة قرص بيانات
- Snapshot أو نسخة احتياطية قبل أي تعديل على الأقسام.
lsblk -f: القرص الجديد بالحجم المتوقع ودون نظام ملفات.- جدول GPT وقسم يبدأ من 1MiB.
mkfs.ext4 -Lباسم واضح، أو XFS إذا كان هذا معيار فريقك، مع عدد Inodes مناسب إذا كانت الملفات صغيرة جداً.- المجلد فارغ قبل التركيب، والتطبيق متوقف أو لا يكتب فيه بعد.
- سطر fstab بالـ UUID، مع
nofailوnoatime، وnosuid,nodevوربماnoexecلمجلدات الرفع. findmnt --verifyبلا أخطاء، ثمmount -aبلا أخطاء، ثم إعادة تشغيل واحدة مخططة للتأكد.- الصلاحيات والمالك على المجلد للمستخدم الذي يعمل به التطبيق، و
RequiresMountsForفي خدمته. - مراقبة المساحة والـ Inodes على القرص الجديد بتنبيه قبل 85% وليس عند 100%.
- ملف Swap صغير بصلاحية 600 وسطر في fstab إذا كان السيرفر بلا Swap.
حذف المختبر Cleanup
أجهزة الحلقة تخص نواة الجهاز، فيجب فك التركيب وفصلها قبل حذف الـ Container، وإلا بقيت مربوطة بملفات لم تعد موجودة:
docker exec sbl-dk sh -c 'umount /srv/data; losetup -d /dev/loop3 /dev/loop2; losetup -l | grep lab || echo "no lab loops"'
docker rm -f sbl-dk
docker rmi sbl-disks:1الخلاصة
وصلنا لنهاية الموضوع، وأهم ما فيه:
- بين القرص والمجلد أربع طبقات: جدول الأقسام، ونظام الملفات، والتركيب، والسطر في fstab، والمساحة لا تظهر في
dfحتى تكتمل الثلاث الأولى. - التركيب يخفي محتوى المجلد ولا يحذفه، فالفاتورة التي كتبت قبل التركيب رجعت بعد
umount. - الأمر
dfيسأل نظام الملفات والأمرduيجمع ما يراه في الشجرة، ولهذا لا يرىduالملفات المخفية تحت نقاط التركيب ولا الملفات المحذوفة المفتوحة ولا الكتل المحجوزة، والملف المتفرقdata.imgكان حجمه الظاهر 1.0G ومساحته الفعلية صفراً. - الـ UUID يبقى مع نظام الملفات، بينما تغير اسم القرص في المختبر من
loop2إلىloop3بمجرد أن سبقه قرص آخر. nofailيمنع قرص البيانات الغائب من إيقاف الإقلاع، وfindmnt --verifyأظهر السطر الخاطئ بخطأين قبل أي إعادة تشغيل.- ext4 يحجز 5% لـ root، فتوقف التطبيق عند 100% بينما كتب root، و
tune2fs -m 1أعاد 41M على قرص بيانات. - ملف Swap يحتاج صلاحية 600 ونظام ملفات يعطي النواة كتلاً ثابتة، فرفضته النواة على overlay وقبلته على ext4.
- التوسيع من الأسفل إلى الأعلى: القرص ثم القسم ثم نظام الملفات، والقسم المستخدم احتاج
partx -uحتى تراه النواة، وresize2fs عمل والقرص مركب.
وفي المقال القادم بإذن الله سوف نستخدم كل هذا في تشخيص سيرفر معطل، ومنه القرص الممتلئ الذي يوقف الخدمات. وهذه مقالات مرتبطة:
دليلنظام التشغيل Operating System: ماذا يفعل بين برامجك والجهاز؟خريطة للمبتدئين تريك بأوامر حقيقية كيف يدير نظام التشغيل العمليات والذاكرة والملفات، ولماذا لا يسقط جهازك كله عندما يسقط برنامج واحد.
دليلنظام الملفات File System: Inodes والسجل Journal وfsyncنتابع الكتابة من برنامجك حتى القرص داخل ext4، ونقطع الكهرباء عن نظام ملفات يعمل لنرى ماذا يبقى فعلاً، ومتى تحتاج fsync وكم يكلف.
دليلتوسيع قرص النظام في Ubuntu عند استخدام LVMعندما تزيد حجم القرص من Proxmox أو من لوحة المزود ويبقى الحجم القديم، هكذا توسع قرص النظام في Ubuntu دون إيقاف السيرفر.
دليلأنواع التخزين وواجهة S3: أين تضع بيانات تطبيقك؟أين تضع قاعدة البيانات وملفات المستخدمين والنسخ الاحتياطية، ولماذا يناسب كلاً منها نوع مختلف من التخزين.
دليلتحليل أداء سيرفر Linux في 60 ثانية: ماذا تكتب أولاً؟ماذا تكتب أول ما تدخل إلى سيرفر بطيء، وكيف تعرف خلال دقيقة هل المشكلة في المعالج أم الذاكرة أم القرص أم الشبكة.سجل التحديثات
- أكتوبر 2026: كتابة الدليل، وتنفيذ التجارب على أجهزة حلقة Loop Devices داخل Container من Debian 13 (trixie) مع util-linux 2.41.5 وe2fsprogs 1.47.2 وparted 3.6، على نواة Linux 5.15 (WSL2) وDocker Engine 29.8.0.