SSD Nodes Learn 🎉 VPS من $5.50/شهر
الأدلة Matt Connorبقلم Matt Connor

تثبيت نواة الإقلاع التالية على VPS مع Ubuntu

لماذا لا يغيّر GRUB_DEFAULT شيئاً في صورة Ubuntu السحابية؟ اقرأ إدخالات GRUB الفعلية وثبّت إقلاع النواة التالية بأمان دون فقدان وصول SSH.

ما الذي يحدد نواة التشغيل التالية لخادم VPS لديك

يحدد ملف واحد مُولَّد، وهو /boot/grub/grub.cfg، نواة التشغيل التي سيقلع بها خادم VPS لديك في المرة التالية. لا تعدّل هذا الملف مباشرةً. عدّل مدخلاته ثم أعد توليده. في صورة Ubuntu السحابية، يأتي أحد هذه المدخلات من مورّد الصورة، وقد يجعل اختيار القائمة غير ذي صلة. لذلك لا يغيّر تنفيذ GRUB_DEFAULT=1 ثم update-grub شيئاً على خادم مستأجر، بينما تنجح الخطوتان نفسيهما عند تثبيت النظام على حاسوب محمول.

نفّذ العمل بهذا الترتيب. تأكد أولاً من أنّ اختيار النواة متاح لك. اقرأ كل ملف إدخال، بما في ذلك الملفات التي أضافها المورّد. اقرأ الناتج المُولَّد، واحسب عدد الإدخالات الموجودة فيه فعلياً. بعد ذلك فقط اختر طريقة تثبيت النواة. إذا أخطأت في ذلك على جهاز لا تصل إليه إلا عبر SSH، فقد تحتاج إلى وحدة تحكم للإنقاذ. لذلك توجد الإجابات الأكثر أماناً في نهاية هذه الصفحة، وغالباً ما تكون هي الاختيار الصحيح.

تحقّق أولاً من أن النواة ملكك ويمكن تثبيتها

uname -r
systemd-detect-virt
ls -1 /boot/vmlinuz-*

إن كان الناتج يتضمن systemd-detect-virt أو kvm أو qemu أو xen، فهذا يعني أنك تستخدم نواتك الخاصة، وتنطبق جميع الخطوات التالية. أما إذا تضمن الناتج lxc أو openvz، فهذا يعني أن خادمك يشارك نواة المضيف، ولذلك لا يوجد محمّل إقلاع خاص بك ولا توجد نواة يمكنك تثبيتها. في هذه الحالة يعرض uname -r إصداراً لا يظهر في /boot/vmlinuz-* إطلاقاً، لأن النواة العاملة تخص المضيف، ولا يمكن لأي إعداد على قرصك تغييرها.

يمثل ls -1 /boot/vmlinuz-* القائمة الفعلية للنوى التي يمكنك الاختيار بينها. إذا احتوت القائمة على سطر واحد، فهذا يعني أن النواة السابقة حُذفت بالفعل، ولا يمكن لأي إعداد في محمّل الإقلاع استعادتها. يحدث ذلك عادةً أثناء عملية إزالة تلقائية، ومن المفيد فهمها قبل تنظيف النوى القديمة على Ubuntu في خادم مهم لديك.

الملف الذي تعدّله ليس الملف الذي يقرأه GRUB

يحتوي /etc/default/grub على تعيينات متغيرات shell عادية. وهو ملف الإدخال. أما /boot/grub/grub.cfg فهو ملف الإخراج، ويبدأ بـ# DO NOT EDIT THIS FILE والسبب. يختفي أي شيء تكتبه في ملف الإخراج عند تثبيت حزمة kernel أو إزالتها في المرة التالية، لأن نصوص الحزمة تعيد إنشاءه.

cat /usr/sbin/update-grub

update-grub هو غلاف. ويشغّل grub-mkconfig -o /boot/grub/grub.cfg، الذي يقرأ المتغيرات، ويشغّل كل نص في /etc/grub.d/، ثم يكتب النتيجة. أمران واتجاه واحد: تدخل المدخلات، ويخرج grub.cfg.

What overrides your setting: /etc/default/grub.d

grep -rn '^[^#]' /etc/default/grub /etc/default/grub.d/

The second path is the part people miss. grub-mkconfig sources /etc/default/grub first, then every *.cfg file in /etc/default/grub.d/ in glob order. Read the code that does it:

grep -n 'default/grub' /usr/sbin/grub-mkconfig

Sourcing is plain shell, so the last assignment wins. Ubuntu cloud images ship files in that directory, and they set things like the timeout and the kernel command line after your file has already been read. Your GRUB_TIMEOUT=10 in /etc/default/grub is overwritten a moment later by a vendor file that sets it to 0. The grep above prints the exact assignments on your image, so read those rather than trusting this sentence.

The practical rule that follows: put your own settings in a file that sorts last, such as /etc/default/grub.d/99-local.cfg, instead of editing /etc/default/grub. Then nothing shipped by the image can land after you.

لماذا يجعل GRUB_FORCE_PARTUUID تحديد عنصر القائمة بلا جدوى

grep -rn GRUB_FORCE_PARTUUID /etc/default/grub /etc/default/grub.d/
grep -n GRUB_FORCE_PARTUUID /etc/grub.d/10_linux
sudo grep -n 'root=PARTUUID' /boot/grub/grub.cfg

تُخبر GRUB_FORCE_PARTUUID المُولِّد بالعثور على نظام الملفات الجذري باستخدام UUID للقسم، ويُكتب ذلك مباشرةً في سطر أوامر النواة بصيغة root=PARTUUID=...، بدلاً من البحث عن UUID لنظام الملفات أثناء الإقلاع. يضبط مورّد الصورة هذا الخيار لأنه يتيح لصورة قرص واحدة الإقلاع بشكل موثوق على أجهزة لم تُبنَ الصورة عليها. يوضح أمر grep الثاني الشيفرة التي تتعامل مع المتغير في /etc/grub.d/10_linux. يوجد هذا البرنامج النصي على قرصك نفسه، وهو المرجع المعتمد لتحديد سلوك صورتك.

النتيجة هي المهمة هنا: في هذا المسار، ينشئ المُولِّد إدخال إقلاع مباشراً بدلاً من قائمة كاملة بالنوى المثبّتة. احسب عدد الإدخالات الناتجة.

sudo grep -cE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfg
sudo grep -nE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfg

إذا كان العدد 1، فلا يوجد إدخال ثانٍ لتحديده، ولذلك يشير GRUB_DEFAULT=1 إلى إدخال غير موجود. لا يستطيع GRUB حله، فيقلع من الإدخال الأول، وهو النواة الجديدة التي كنت تحاول تجنّبها. ولا يساعد grub-set-default أيضاً، لأن الخلل ليس في الإعداد الافتراضي. فالقائمة التي تحاول الاختيار منها لم تُنشأ أصلاً.

لاستعادة القائمة الكاملة، انقل ملف المورّد إلى مكان آخر، ثم اعرض النتيجة مسبقاً قبل تطبيقها. يكتب grub-mkconfig، من دون -o، إلى الإخراج القياسي ولا يغيّر أي شيء على القرص.

sudo grub-mkconfig 2>/dev/null | grep -cE '^\s*(menuentry|submenu) '
sudo mkdir -p /root/grub-backup
sudo mv /etc/default/grub.d/<the file your grep named> /root/grub-backup/
sudo grub-mkconfig 2>/dev/null | grep -cE '^\s*(menuentry|submenu) '

يعني انتقال العدد من 1 إلى عدة إدخالات أن الإدخالات تظهر بعد إزالة فرض الخيار. لم يُكتب أي شيء بعد. أعد الملف إلى مكانه إذا لم يكن العدد الثاني مناسباً، لأن PARTUUID المفروض هو الطريقة التي تستخدمها صورة مزوّد الخدمة لتحديد نظام الملفات الجذري، وإزالته تنقل الجهاز إلى مسار البحث بدلاً من ذلك. أنشئ لقطة قبل تشغيل update-grub فعلياً.

إذا كان هدفك الوحيد هو تجاوز نواة معطّلة واحدة، فتوقف هنا واستخدم الخيارات الأكثر أماناً أدناه. إن إعادة بناء قائمة الإقلاع على خادم بعيد لتجاوز ترقية واحدة تنطوي على مخاطر أكبر من المشكلة نفسها.

لماذا تُعد أرقام الإدخالات الخيار الخاطئ للتثبيت

GRUB_DEFAULT يقبل رقماً أو عنواناً أو معرّفاً. تبدأ أرقام الإدخالات الرئيسية من 0. ويُستخدم > فاصلاً في الإدخال المتداخل، لذلك يعني GRUB_DEFAULT="1>2" الإدخال عند الفهرس 2 داخل القائمة الفرعية عند الفهرس 1.

تتغير الفهارس. يعرض 10_linux نُوى النظام بترتيب الأحدث أولاً، لذلك يؤدي تثبيت نواة إلى دفع كل إدخال أقدم إلى الأسفل بمقدار واحد، بينما يؤدي حذف نواة إلى رفعها. سيظل 1>2 الذي أعددته بعناية صالحاً بعد ذلك. لكنه سيشير الآن إلى نواة مختلفة. لن يظهر أي خطأ أو تحذير، وستكتشف المشكلة بعد إعادة التشغيل.

أما المعرّفات فلا تتغير، لأن كل معرّف يتضمن إصدار النواة. اقرأ المعرّف لديك:

sudo awk -F"'" '/menuentry_id_option/ {print $2, "==>", $4}' /boot/grub/grub.cfg

تجاهل الأسطر الأولى القليلة في المخرجات، فهي تمثل المتغير الذي يُعرَّف في الترويسة. بعد ذلك، يمثّل الجانب الأيسر العنوان الذي يراه القارئ، بينما يمثّل الجانب الأيمن المعرّف الذي تمرره إلى الأدوات. بالنسبة إلى إدخال داخل قائمة فرعية، صِل معرّف القائمة الفرعية بمعرّف الإدخال باستخدام >، بهذا الترتيب تماماً كما في الصيغة الرقمية.

شغّل النواة السابقة مرة واحدة باستخدام grub-reboot

يُعدّ الاختيار لمرة واحدة الإجراء المناسب على خادم بعيد، لأنه يلغي نفسه تلقائياً. يكتب grub-reboot القيمة next_entry في /boot/grub/grubenv. يقرأ GRUB هذا المتغير، ويمسحه، ويحفظ القيمة الممسوحة قبل إقلاع أي شيء، لذلك لا تُعاد محاولة تشغيل نواة تتسبب في kernel panic أثناء الإقلاع التالي. تحصل على محاولة واحدة، ثم يعود الجهاز تلقائياً إلى الإعداد الافتراضي المعتاد.

تحقق أولاً من أن الإعداد المُنشأ يقرأ هذا المتغير أصلاً:

sudo grep -n -B2 -A5 'next_entry' /boot/grub/grub.cfg

يجب أن ترى سطراً يحتوي على load_env وكتلةً تضبط default من next_entry. إذا لم يُخرج grep أي نتيجة، فهذا يعني أن صورتك لا تقرأ grubenv أثناء الإقلاع، ولذلك سيقبل shell الأمر grub-reboot ثم يتجاهله محمّل الإقلاع. هذا هو مسار الإقلاع المباشر المفروض نفسه من القسم السابق، لكنه يظهر هنا في موضع ثانٍ.

sudo grub-reboot '<the identifier you copied>'
sudo grub-editenv list

يجب أن يطبع grub-editenv list الآن سطراً يحتوي على next_entry= بالقيمة نفسها التي مررتها تماماً. افتح وحدة تحكم مزود الخدمة في علامة تبويب بالمتصفح، ثم أعد التشغيل وتحقق من النتيجة.

sudo reboot
uname -r

يعني إبلاغ uname -r عن الإصدار الأقدم أن التثبيت المؤقت نجح. أما إبلاغه عن الإصدار الجديد فيعني إما أن المعرّف لم يُحلّ، أو أن grubenv لا تتم قراءته. وفي كلتا الحالتين يكون الجهاز قد عاد للعمل، وهذا هو الهدف من استخدام الصيغة لمرة واحدة.

اجعل الاختيار ثابتاً باستخدام GRUB_DEFAULT=saved

يجعل GRUB_DEFAULT=saved القيمة الافتراضية مأخوذة من saved_entry في grubenv، وتضبط هذه القيمة باستخدام grub-set-default. وتبقى هذه القيمة بعد تثبيت نوى جديدة، لأن update-grub يعيد كتابة grub.cfg ولا يلمس grubenv.

echo 'GRUB_DEFAULT=saved' | sudo tee /etc/default/grub.d/99-local.cfg
sudo update-grub
sudo grub-set-default '<the identifier you copied>'
sudo grub-editenv list
sudo grep -n 'set default' /boot/grub/grub.cfg

يجب أن يطبع الأمر الأخير set default="${saved_entry}". إذا طبع set default="0"، فهذا يعني أن شيئاً جرى تحميله بعد ملفك أعاد ضبط GRUB_DEFAULT إلى قيمة حرفية. لذلك اعرض /etc/default/grub.d/ مرة أخرى، وتحقق من أن 99-local.cfg يأتي فعلاً في الترتيب الأخير.

يختلف GRUB_SAVEDEFAULT=true عن هذا الإعداد، ومن السهل الخلط بينهما. فهو يحفظ ما أقلعت به للتو باعتباره القيمة الافتراضية الجديدة، ولذلك تتبع القيمة الافتراضية آخر إقلاع ناجح. على الخادم، يعني ذلك أن إعادة التشغيل غير التفاعلية قد تغيّر اختيارك بصمت. اتركه معطلاً ما لم يكن هذا هو السلوك المطلوب.

يفشل التثبيت حسب المعرّف في حالة واحدة أيضاً. إذا أزلت النواة التي يشير إليها المعرّف، فلن يعود المعرّف قابلاً للحل، وتعود إلى الإدخال الأول. لذلك ثبّت الحزمة أيضاً، أو امنع إزالة تلك النواة تلقائياً.

إظهار القائمة في وحدة تحكم مزوّد الخدمة

يتطلب الاختيار التفاعلي ظهور القائمة على الشاشة، لكن صور السحابة تخفيها. أضف هذه الإعدادات إلى الملف الذي يأتي أخيراً في الترتيب، ثم شغّل sudo update-grub.

GRUB_TIMEOUT=10
GRUB_TIMEOUT_STYLE=menu
GRUB_RECORDFAIL_TIMEOUT=10

لا يعرض GRUB_TIMEOUT_STYLE=hidden مع GRUB_TIMEOUT=0 أي شيء على الإطلاق، لذلك يرى من يراقب وحدة التحكم رسائل النواة تبدأ فوراً، ويستنتج أن محمّل الإقلاع قد تم تخطيه. يمثّل GRUB_RECORDFAIL_TIMEOUT مهلة الانتظار المنفصلة المستخدمة بعد إقلاع لم يكتمل. وتضبطها صور السحابة على 0 أيضاً، ولذلك لا يتوقف الخادم الذي فشل إقلاعه للتو ولا ينتظر تدخلك.

إذا كان مزوّد الخدمة يوفّر وحدة تحكم تسلسلية بدلاً من وحدة تحكم رسومية، وما زلت لا ترى شيئاً، فهذا يعني أن GRUB يكتب إلى طرفية لا يمكنك رؤيتها. أضف السطرين معاً، لأن الأول يحدد مخرجات العرض، والثاني يضبط المنفذ:

GRUB_TERMINAL="console serial"
GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"

ستُضاف عشر ثوانٍ إلى كل عملية إقلاع من الآن فصاعداً. أعد ضبط المهلة إلى 0 بعد الانتهاء.

خيارات أكثر أماناً من تعديل محمّل الإقلاع

يُعد تغيير إدخال محمّل الإقلاع على جهاز لا تصل إليه إلا عبر SSH الخيار الأعلى خطراً في هذه الصفحة. توجد حلول أبسط، وهي تعالج المشكلة الفعلية عادةً.

ثبّت حزم النواة. إذا كان الهدف هو «لا تثبّت لي نواة أحدث»، فاطلب ذلك من مدير الحزم بدلاً من محمّل الإقلاع.

apt list --installed 2>/dev/null | grep -E '^linux-(image|headers|generic|virtual|kvm|aws|azure|gcp|oracle)'
sudo apt-mark hold linux-image-virtual linux-headers-virtual
apt-mark showhold

استخدم الأسماء التي طبعها الأمر الأول، لأن صور السحابة تثبّت عادةً النكهة virtual أو kvm بدلاً من generic. يتجاوز apt upgrade الحزمة المثبّتة، ويعلن ذلك عبر The following packages have been kept back:، كما تتجاوزها التحديثات غير التفاعلية على Ubuntu أيضاً. لكن لهذا الإجراء تكلفة فعلية: تتوقف النواة المثبّتة عن تلقي إصلاحات الأمان، لذا اعتبره إيقافاً مؤقتاً وحدد له تاريخاً، ثم ألغِ التثبيت عبر sudo apt-mark unhold. إذا كنت تتجنب تحديثات النواة لأن إعادة التشغيل تسبب توقفاً، وليس لأن نواة معينة معيبة، فإن تصحيح النواة مباشرةً على VPS يعالج هذه المشكلة بدلاً من ذلك.

أنشئ لقطة قبل الترقية. تستعيد اللقطة النظام خلال دقائق، من دون إدخال أوامر عبر وحدة التحكم ومن دون احتمال تطبيق تغيير غير مكتمل على محمّل الإقلاع. أنشئ اللقطة، ثم رقِّ النظام وأعد التشغيل وتحقق منه. إذا واجهت النواة الجديدة مشكلة، فاستعد اللقطة، وسيعود مسار الإقلاع تماماً إلى حالته السابقة.

استخدم وحدة التحكم أو صورة إنقاذ لجهاز متوقف بالفعل. عندما يتعذر على الخادم الإقلاع، لا يكون إعداد محمّل الإقلاع هو المكان المناسب لإصلاح المشكلة، كما أن مسار الاسترداد هذا إجراء مستقل: ما الذي يجب فعله عندما يتعذر على VPS الإقلاع بعد تحديث النواة.

ما الذي يتعطل، والرسالة التي ستراها

اختفى التعديل الذي أجريته على /boot/grub/grub.cfg. تم تثبيت حزمة kernel أو إزالتها، وشُغِّل البرنامج النصي للصيانة الخاص بها update-grub، ثم أُعيد إنشاء الملف من المدخلات. يحدّد رأس # DO NOT EDIT THIS FILE موقعي الإدخال. عدّل هذين الموقعين.

grub-editenv: error: environment block too small. الملف /boot/grub/grubenv مفقود أو مبتور. أعد إنشاءه باستخدام sudo grub-editenv /boot/grub/grubenv create، ثم عيّن قيمتك مرة أخرى وتحقق منها باستخدام sudo grub-editenv list.

يتسبب kernel مثبّت في تعطل النظام مع VFS: Unable to mount root fs on unknown-block(0,0). يشير الإدخال الذي ثبّتَّه إلى kernel أو initrd لم يعد موجوداً على القرص، ويحدث ذلك عادةً لأن الحزمة أُزيلت بينما ظل المعرّف في grubenv. تتمثل الاستعادة في الإقلاع من وحدة التحكم باستخدام إدخال عامل، ثم مسح القيمة القديمة.

لم يتغير uname -r بعد إعادة تشغيل توقعت أن تغيّره. تحقق من ثلاثة أمور بالترتيب: هل ما زال grub-editenv list يعرض قيمتك أم استُهلكت؛ هل يظهر المعرّف الذي عيّنته في grub.cfg الحالي؛ هل يحتوي grub.cfg على سطر set default يقرأ المتغير الذي عيّنته. يفسّر أحد هذه الأمور الثلاثة السبب في كل مرة.

ظهرت القائمة تلقائياً بعد تعطل. يسجّل GRUB فشل الإقلاع في grubenv على أنه recordfail=1، ويفرض ذلك ظهور القائمة في الإقلاع التالي حتى يتمكن شخص من التدخل. امسح هذه القيمة باستخدام sudo grub-editenv /boot/grub/grubenv unset recordfail بعد أن تصبح الآلة سليمة.


الجملة الوحيدة التي تستحق الاحتفاظ بها هي: الملف الذي تعدّله ليس الملف الذي يقرأه GRUB، وعلى صورة سحابية تكون الفجوة بينهما هي مصدر الالتباس. اقرأ الإعداد المُنشأ أولاً. تستند كل قرارات هذه الصفحة إلى ما يقوله فعلياً.

FAQ

لماذا لا يغيّر GRUB_DEFAULT=1 النواة التي يقلع منها VPS؟

لأن صورة Ubuntu السحابية تحتوي غالباً على ملف /boot/grub/grub.cfg مولّد يضم إدخال إقلاع واحداً فقط، ولذلك لا يشير الفهرس 1 إلى أي إدخال، ويعود GRUB إلى الإدخال الأول. تحقّق من ذلك باستخدام sudo grep -cE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfg. إذا كان العدد 1، فهذا هو السبب. السبب هو GRUB_FORCE_PARTUUID، الذي يحدده مورّد الصورة في ملف ضمن /etc/default/grub.d/، ما يضع المولّد في مسار إقلاع مباشر بدلاً من إنشاء قائمة كاملة بالنوى المثبتة. اعثر على الملف باستخدام grep -rn GRUB_FORCE_PARTUUID /etc/default/grub /etc/default/grub.d/.

كيف أقلع من النواة السابقة مرة واحدة فقط؟

شغّل sudo grub-reboot '<identifier>' باستخدام معرّف تنسخه من ملف grub.cfg الخاص بك، ثم أعد التشغيل مع إبقاء وحدة تحكم المزوّد مفتوحة. يمسح GRUB قيمة next_entry قبل الإقلاع، لذلك ينطبق الاختيار على محاولة واحدة فقط، ولا تجري إعادة محاولة الإقلاع بنواة تتسبب في panic. تحقّق من تسجيل القيمة باستخدام sudo grub-editenv list. قبل الاعتماد على ذلك، شغّل sudo grep -n next_entry /boot/grub/grub.cfg، لأن الصورة التي لا يحمّل إعدادها grubenv ستتجاهل الأمر دون إظهار خطأ.

هل يجب تثبيت الاختيار حسب رقم الإدخال أم حسب المعرّف؟

حسب المعرّف. أرقام الإدخالات هي مواضع في قائمة يعيد 10_linux إنشاءها بترتيب الأحدث أولاً، لذلك يؤدي تثبيت أي نواة أو إزالتها إلى تغيير هذه الأرقام. كما أن 1>2 قديماً قد يشير مع ذلك إلى إدخال صحيح لكنه غير مقصود، دون عرض أي تحذير. تتضمن المعرّفات إصدار النواة، ولذلك إما أن تطابق النواة المقصودة أو يفشل حلّها. اعرضها باستخدام sudo grep -n menuentry_id_option /boot/grub/grub.cfg، وانسخ السلسلة المقتبسة التي تلي كل سطر إدخال.

هل تثبيت حزمة النواة أكثر أماناً من تغيير محمّل الإقلاع؟

بالنسبة إلى الهدف المعتاد، نعم. يوقف sudo apt-mark hold linux-image-virtual linux-headers-virtual وصول نواة أحدث أصلاً، لذلك لا يتغير مسار الإقلاع، ولا يوجد ما قد يخطئ بسببه استخدام وحدة تحكم قد لا تتوفر لديك. تحقّق أولاً من أسماء النكهات المثبتة على خادمك باستخدام apt list --installed، وتحقّق من التثبيت باستخدام apt-mark showhold. المقابل هو أن النواة المثبتة لا تتلقى إصلاحات أمنية، لذلك حدّد موعد تشغيل sudo apt-mark unhold قبل تنفيذ التثبيت.