Kernel vastzetten op VPS: zo wijzigt u de bootvolgorde
GRUB_DEFAULT werkt niet op Ubuntu cloud images. Ontdek hoe u de juiste menu-items uitleest en de kernel veilig vastzet zonder dat u een rescue-console hoeft te gebruiken.
Wat bepaalt welke kernel uw VPS opstart
Welke kernel uw VPS bij de volgende opstart gebruikt, wordt bepaald door één gegenereerd bestand, /boot/grub/grub.cfg. U bewerkt dit bestand nooit zelf. U bewerkt de invoerbestanden en genereert het bestand opnieuw. Bij een Ubuntu-cloudimage is een van deze invoerbestanden afkomstig van de leverancier van de image. Dit kan ervoor zorgen dat menuselecties irrelevant worden. Daarom veranderen GRUB_DEFAULT=1 gevolgd door update-grub niets op een gehuurde server, terwijl dezelfde twee stappen wel werken op een installatie op een laptop.
Werk in deze volgorde. Bevestig eerst of u zelf de kernel mag kiezen. Lees elk invoerbestand, inclusief de bestanden die door de leverancier zijn toegevoegd. Lees de gegenereerde uitvoer en tel het werkelijke aantal vermeldingen. Kies pas daarna een methode voor het vastzetten (pinning). Als u dit fout doet op een machine die u alleen via SSH kunt bereiken, kost dit u een rescue-console. De veiligste antwoorden staan daarom aan het einde van deze pagina en zijn vaak de juiste keuze.
Controleer eerst of de kernel van u is om deze vast te pinnen
uname -r
systemd-detect-virt
ls -1 /boot/vmlinuz-*systemd-detect-virt Als kvm, qemu of xen wordt afgedrukt, draait u uw eigen kernel en is alles hieronder van toepassing. Als lxc of openvz wordt afgedrukt, deelt uw server de kernel van de host; u beschikt dan niet over een eigen bootloader en er valt niets vast te pinnen. In dat geval rapporteert uname -r een versie die in het geheel niet in /boot/vmlinuz-* voorkomt, omdat de actieve kernel toebehoort aan de host en geen enkele instelling op uw schijf dit kan wijzigen.
ls -1 /boot/vmlinuz-* is de daadwerkelijke lijst met kernels waaruit u kunt kiezen. Als deze slechts één regel bevat, is de vorige kernel al verwijderd en kan geen enkele bootloader-instelling deze terugzetten. Dit gebeurt meestal tijdens een autoremove, wat het waard is om te begrijpen voordat u oude kernels op Ubuntu opruimt op een server die voor u van belang is.
Het bestand dat u bewerkt is niet het bestand dat GRUB leest
/etc/default/grub bevat eenvoudige shell-variabeletoewijzingen. Dit is een invoerbestand. /boot/grub/grub.cfg is de uitvoer en begint met # DO NOT EDIT THIS FILE en de reden hiervoor. Alles wat u in de uitvoer schrijft, is verdwenen zodra een kernelpakket wordt geïnstalleerd of verwijderd, omdat de scripts van die pakketten het bestand opnieuw genereren.
cat /usr/sbin/update-grubupdate-grub is een wrapper. Dit script voert grub-mkconfig -o /boot/grub/grub.cfg uit, dat de variabelen leest, elk script in /etc/grub.d/ uitvoert en het resultaat wegschrijft. Twee commando's, één richting: invoer gaat erin, grub.cfg komt eruit.
Wat uw instelling overschrijft: /etc/default/grub.d
grep -rn '^[^#]' /etc/default/grub /etc/default/grub.d/Het tweede pad is het onderdeel dat vaak over het hoofd wordt gezien. grub-mkconfig laadt eerst /etc/default/grub, en vervolgens elk *.cfg-bestand in /etc/default/grub.d/ in glob-volgorde. Lees de code die dit uitvoert:
grep -n 'default/grub' /usr/sbin/grub-mkconfigHet laden (sourcing) gebeurt via een standaard shell, dus de laatste toewijzing wint. Ubuntu cloud-images leveren bestanden in die map, en zij stellen zaken in zoals de timeout en de kernel command line nadat uw bestand al is ingelezen. Uw GRUB_TIMEOUT=10 in /etc/default/grub wordt een moment later overschreven door een vendor-bestand dat deze op 0 zet. De bovenstaande grep toont de exacte toewijzingen op uw image; vertrouw dus op die uitvoer in plaats van op deze zin.
De praktische regel die hieruit volgt: plaats uw eigen instellingen in een bestand dat als laatste wordt gesorteerd, zoals /etc/default/grub.d/99-local.cfg, in plaats van /etc/default/grub te bewerken. Op die manier kan niets dat door de image wordt meegeleverd, na uw configuratie worden geladen.
Waarom GRUB_FORCE_PARTUUID de menuselectie irrelevant maakt
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.cfgGRUB_FORCE_PARTUUID instrueert de generator om het root-bestandssysteem te vinden op basis van de partitie-UUID, direct weggeschreven naar de kernel-commandoregel als root=PARTUUID=..., in plaats van te zoeken naar een bestandssysteem-UUID tijdens het opstarten. De leverancier van de image stelt dit in omdat het een disk-image betrouwbaar laat opstarten op hardware waarvoor deze niet is gebouwd. De tweede grep toont u de code die op de variabele reageert, in /etc/grub.d/10_linux. Dat script staat op uw eigen schijf en is de autoriteit voor wat uw image doet.
De consequentie is hier van belang: op dat pad schrijft de generator een direct opstartitem in plaats van een volledige lijst met geïnstalleerde kernels. Tel wat u uiteindelijk heeft overgehouden.
sudo grep -cE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfg
sudo grep -nE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfgAls het aantal 1 is, is er geen tweede item om te selecteren, dus benoemt GRUB_DEFAULT=1 een item dat niet bestaat. GRUB kan dit niet oplossen, dus start het het eerste item op, wat de nieuwe kernel is die u probeerde te vermijden. grub-set-default helpt ook niet, omdat de standaardinstelling niet het defecte onderdeel is. Het menu waaruit u probeert te kiezen, is nooit gegenereerd.
Om een volledig menu terug te krijgen, verplaatst u het bestand van de leverancier en bekijkt u het resultaat voordat u het definitief maakt. grub-mkconfig zonder -o schrijft naar de standaarduitvoer en wijzigt niets op de schijf.
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) 'Een aantal dat verspringt van 1 naar meerdere betekent dat de items verschijnen zodra de forcering is verwijderd. Er is nog niets geschreven. Plaats het bestand terug als het tweede aantal niet correct lijkt, omdat de geforceerde PARTUUID de manier is waarop de image van uw provider het root-bestandssysteem lokaliseert, en het verwijderen ervan verplaatst de machine naar het zoekpad. Maak een snapshot voordat u update-grub daadwerkelijk uitvoert.
Als uw enige doel is om één slechte kernel te overleven, stop dan hier en gebruik de veiligere opties verderop. Het opnieuw opbouwen van het opstartmenu op een externe server om aan een enkele upgrade te ontsnappen, brengt meer risico met zich mee dan het probleem waard is.
Waarom itemnummers ongeschikt zijn voor pinning
GRUB_DEFAULT accepteert een nummer, een titel of een identifier. Nummers tellen items op het hoogste niveau vanaf 0. Een genest item gebruikt > als scheidingsteken, dus GRUB_DEFAULT="1>2" betekent het item op index 2 binnen het submenu op index 1.
Indices verschuiven. 10_linux toont kernels met de nieuwste bovenaan; het installeren van een kernel schuift elk ouder item één positie naar beneden, en het verwijderen van een kernel schuift ze omhoog. Uw zorgvuldige 1>2 wordt daarna nog steeds opgelost. Het verwijst nu echter naar een andere kernel. Er treden geen fouten op, er verschijnen geen waarschuwingen, en u merkt het pas na een herstart.
Identifiers verschuiven niet, omdat ze elk de kernelversie bevatten. Lees de uwe uit:
sudo awk -F"'" '/menuentry_id_option/ {print $2, "==>", $4}' /boot/grub/grub.cfgNegeer de eerste paar regels van de uitvoer; dit is de variabele die in de header wordt gedefinieerd. Daarna staat aan de linkerkant de titel die een gebruiker ziet, en aan de rechterkant de identifier die u aan de tools meegeeft. Voor een item binnen een submenu combineert u de identifier van het submenu en de identifier van het item met >, in die volgorde, exact zoals bij de numerieke notatie.
Boot de vorige kernel eenmalig met grub-reboot
Een eenmalige selectie is de juiste keuze op een externe server, omdat deze actie zichzelf ongedaan maakt. grub-reboot schrijft next_entry naar /boot/grub/grubenv. GRUB leest deze variabele, wist deze en slaat de gewiste waarde op voordat er iets wordt opgestart. Hierdoor wordt een kernel die een kernel panic veroorzaakt niet opnieuw geprobeerd bij de volgende boot. U krijgt één poging, waarna de machine automatisch terugkeert naar de standaardinstelling.
Controleer eerst of uw gegenereerde configuratie deze variabele überhaupt leest:
sudo grep -n -B2 -A5 'next_entry' /boot/grub/grub.cfgU zoekt naar een load_env-regel en een blok dat default instelt op basis van next_entry. Als de grep niets retourneert, leest uw image grubenv niet tijdens het opstarten. In dat geval wordt grub-reboot wel geaccepteerd in de shell, maar genegeerd door de bootloader. Dit is hetzelfde geforceerde directe opstartpad uit de vorige sectie dat op een tweede plek opduikt.
sudo grub-reboot '<the identifier you copied>'
sudo grub-editenv listgrub-editenv list zou nu een next_entry=-regel moeten tonen die precies bevat wat u heeft doorgegeven. Open de console van uw provider in een browsertabblad, start de machine opnieuw op en controleer het resultaat.
sudo rebootuname -rAls uname -r de oudere versie rapporteert, betekent dit dat de pin is geslaagd. Als de nieuwe versie wordt gerapporteerd, betekent dit dat de identifier niet kon worden opgelost of dat grubenv niet wordt gelezen. De machine is in beide gevallen opgestart, wat precies het doel is van de eenmalige methode.
Maak de keuze permanent met GRUB_DEFAULT=saved
GRUB_DEFAULT=saved zorgt ervoor dat de standaardwaarde uit saved_entry in grubenv wordt gehaald, en u stelt die waarde in met grub-set-default. Deze instelling blijft behouden na kernel-installaties, omdat update-grub het bestand grub.cfg herschrijft en nooit grubenv aanpast.
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.cfgHet laatste commando moet set default="${saved_entry}" weergeven. Als het set default="0" weergeeft, heeft een proces dat na uw bestand wordt geladen GRUB_DEFAULT teruggezet naar een letterlijke waarde. Vermeld daarom /etc/default/grub.d/ opnieuw en controleer of 99-local.cfg daadwerkelijk als laatste wordt gesorteerd.
GRUB_SAVEDEFAULT=true is een andere instelling die gemakkelijk hiermee verward kan worden. Deze slaat de laatst opgestarte kernel op als de nieuwe standaard, waardoor de standaardinstelling de laatst geslaagde boot volgt. Op een server betekent dit dat een onbeheerde herstart uw vastgezette versie stilletjes kan wijzigen. Laat deze optie uitgeschakeld, tenzij dit het gewenste gedrag is.
Een vastgezette versie op basis van een identifier kan nog steeds op één manier falen. Als u de kernel verwijdert waarnaar wordt verwezen, kan de identifier niet meer worden opgelost. Hierdoor valt het systeem terug op het eerste item in de lijst. Houd daarom ook het pakket vast (hold) of zorg ervoor dat de kernel wordt uitgesloten van autoremove.
Het menu op een provider-console krijgen
Interactief kiezen vereist dat het menu op het scherm verschijnt, maar cloud-images verbergen dit. Plaats deze instellingen in het bestand dat als laatste wordt ingelezen en voer daarna sudo update-grub uit.
GRUB_TIMEOUT=10
GRUB_TIMEOUT_STYLE=menu
GRUB_RECORDFAIL_TIMEOUT=10GRUB_TIMEOUT_STYLE=hidden in combinatie met GRUB_TIMEOUT=0 toont helemaal niets, waardoor iemand die de console bekijkt ziet dat de kernel-berichten direct starten en concludeert dat de bootloader is overgeslagen. GRUB_RECORDFAIL_TIMEOUT is de afzonderlijke time-out die wordt gebruikt na een boot die niet is voltooid; cloud-images zetten deze ook op 0. Dit is de reden waarom een server die niet is opgestart alsnog niet stopt om op u te wachten.
Als uw provider een seriële console aanbiedt in plaats van een grafische en u ziet nog steeds niets, dan schrijft GRUB naar een terminal die u niet kunt zien. Voeg beide regels toe, omdat de eerste de uitvoer selecteert en de tweede de poort configureert:
GRUB_TERMINAL="console serial"
GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"Vanaf nu wordt er bij elke boot tien seconden gewacht. Zet de time-out terug naar 0 zodra u klaar bent.
Veiligere opties dan het bewerken van de bootloader
Het wijzigen van de bootloader-input op een machine die u alleen via SSH bereikt, is de optie met het hoogste risico op deze pagina. Er bestaan betere oplossingen en deze lossen doorgaans het werkelijke probleem op.
Houd de kernel-pakketten tegen. Als het doel is "geef mij geen nieuwere kernel", geef dit dan aan bij de pakketbeheerder in plaats van bij de bootloader.
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 showholdGebruik de namen die het eerste commando heeft weergegeven, aangezien cloud-images vaak de virtual- of kvm-variant installeren in plaats van generic. Een vastgehouden pakket wordt overgeslagen door apt upgrade, wat dit aankondigt met The following packages have been kept back:, en het wordt ook overgeslagen door onbeheerde upgrades op Ubuntu. De prijs is reëel: een vastgehouden kernel ontvangt geen beveiligingsupdates meer. Behandel dit dus als een pauze met een einddatum en geef het pakket weer vrij met sudo apt-mark unhold. Als u kernel-updates vermijdt omdat reboots downtime veroorzaken in plaats van omdat een specifieke kernel slecht is, biedt live kernel patching op een VPS daarvoor de oplossing.
Maak een snapshot vóór de upgrade. Een snapshot herstelt het systeem binnen enkele minuten, zonder dat u via de console hoeft te typen en zonder het risico op een half doorgevoerde bootloader-wijziging. Maak een snapshot, voer de upgrade uit, start opnieuw op en verifieer. Als de nieuwe kernel niet naar behoren werkt, zet u de snapshot terug en is het opstartpad exact zoals het was.
Gebruik de console of een rescue-image voor een server die al offline is. Zodra de server niet meer opstart, is de bootloader-configuratie niet de plek waar u dit herstelt. Dat herstelpad volgt een eigen procedure: wat te doen als een VPS niet opstart na een kernel-update.
Wat er misgaat en de melding die u ziet
Uw wijziging in /boot/grub/grub.cfg is verdwenen. Er is een kernelpakket geïnstalleerd of verwijderd, het bijbehorende maintainer-script heeft update-grub uitgevoerd en het bestand is opnieuw gegenereerd op basis van de invoer. De # DO NOT EDIT THIS FILE-header vermeldt de twee invoerlocaties. Bewerk die bestanden.
grub-editenv: error: environment block too small. /boot/grub/grubenv ontbreekt of is afgebroken. Maak het opnieuw aan met sudo grub-editenv /boot/grub/grubenv create, stel daarna uw waarde opnieuw in en bevestig met sudo grub-editenv list.
Een vastgezette (pinned) kernel veroorzaakt een kernel panic met VFS: Unable to mount root fs on unknown-block(0,0). Het item dat u heeft vastgezet, verwijst naar een kernel of initrd die niet langer op de schijf staat. Dit gebeurt meestal omdat het pakket is verwijderd terwijl de identifier in grubenv is blijven staan. Herstel vindt plaats door via de console op te starten met een werkend item en vervolgens de verouderde waarde te wissen.
uname -r is ongewijzigd na een herstart waarbij u verandering verwachtte. Controleer de volgende drie zaken in deze volgorde: toont grub-editenv list nog steeds uw waarde of is deze verbruikt; verschijnt de identifier die u heeft ingesteld in de huidige grub.cfg; bevat grub.cfg een set default-regel die de variabele leest die u heeft ingesteld. Eén van deze drie punten verklaart het probleem altijd.
Het menu verscheen uit zichzelf na een crash. GRUB registreert een mislukte boot in grubenv als recordfail=1, wat het menu bij de volgende opstart afdwingt zodat een beheerder kan ingrijpen. Wis dit met sudo grub-editenv /boot/grub/grubenv unset recordfail zodra de machine weer gezond is.
De enige zin die u moet onthouden: het bestand dat u bewerkt is niet het bestand dat GRUB leest, en bij een cloud-image ontstaat de verwarring precies in dat verschil. Lees eerst de gegenereerde configuratie. Elke beslissing op deze pagina vloeit voort uit wat daar daadwerkelijk staat.
FAQ
Waarom wijzigt GRUB_DEFAULT=1 de kernel waarmee mijn VPS opstart niet?
Omdat een Ubuntu cloud-image in de gegenereerde /boot/grub/grub.cfg vaak slechts één boot-entry bevat. Index 1 verwijst daardoor naar niets en GRUB valt terug op de eerste entry. Controleer dit met sudo grep -cE '^\s*(menuentry|submenu) ' /boot/grub/grub.cfg. Een telling van 1 is het resultaat. De oorzaak is GRUB_FORCE_PARTUUID, ingesteld door de image-leverancier in een bestand onder /etc/default/grub.d/. Dit dwingt de generator naar een direct boot-pad in plaats van een volledige lijst met geïnstalleerde kernels op te bouwen. Zoek het bestand met grep -rn GRUB_FORCE_PARTUUID /etc/default/grub /etc/default/grub.d/.
Hoe start ik eenmalig op met de vorige kernel?
Voer sudo grub-reboot '<identifier>' uit met een identifier die u kopieert uit uw eigen grub.cfg en herstart vervolgens terwijl de console van de provider al geopend is. GRUB wist next_entry voordat het opstart, waardoor de keuze voor precies één poging geldt en een kernel die een kernel panic veroorzaakt niet opnieuw wordt geprobeerd. Controleer of de waarde is opgeslagen met sudo grub-editenv list. Voordat u hierop vertrouwt, voert u sudo grep -n next_entry /boot/grub/grub.cfg uit, omdat een image waarvan de configuratie grubenv nooit laadt, het commando zonder foutmelding negeert.
Moet ik vastleggen op basis van het entry-nummer of de identifier?
Op basis van de identifier. Entry-nummers zijn posities in een lijst die 10_linux herbouwt met de nieuwste bovenaan. Het installeren of verwijderen van een kernel verschuift deze nummers, waardoor een verouderde 1>2 nog steeds naar een bestaande maar onjuiste entry verwijst zonder dat u een waarschuwing krijgt. Identifiers bevatten de kernelversie; ze komen dus overeen met de beoogde kernel of de resolutie mislukt. Toon ze met sudo grep -n menuentry_id_option /boot/grub/grub.cfg en kopieer de tekst tussen aanhalingstekens die op elke entry-regel volgt.
Is het vasthouden van het kernel-pakket veiliger dan het aanpassen van de bootloader?
Voor het gebruikelijke doel wel. sudo apt-mark hold linux-image-virtual linux-headers-virtual voorkomt dat er überhaupt een nieuwere kernel wordt geïnstalleerd, waardoor het boot-pad ongewijzigd blijft en er niets mis kan gaan vanaf een console waarover u mogelijk niet beschikt. Controleer eerst de namen van de geïnstalleerde flavours op uw eigen systeem met apt list --installed en verifieer de hold-status met apt-mark showhold. Het nadeel is dat een vastgehouden kernel geen beveiligingsupdates ontvangt; bepaal dus wanneer u sudo apt-mark unhold uitvoert voordat u de hold instelt.