Merge pull request #177010 from flokli/make-kexec
nixos/modules/installer/kexec/kexec-boot.nix: move into nixos/lib, expose `kexec` nixos/release.nix
This commit is contained in:
commit
2b9be3117e
8 changed files with 201 additions and 55 deletions
|
@ -0,0 +1,94 @@
|
||||||
|
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-booting-via-kexec">
|
||||||
|
<title><quote>Booting</quote> into NixOS via kexec</title>
|
||||||
|
<para>
|
||||||
|
In some cases, your system might already be booted into/preinstalled
|
||||||
|
with another Linux distribution, and booting NixOS by attaching an
|
||||||
|
installation image is quite a manual process.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
This is particularly useful for (cloud) providers where you can’t
|
||||||
|
boot a custom image, but get some Debian or Ubuntu installation.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
In these cases, it might be easier to use <literal>kexec</literal>
|
||||||
|
to <quote>jump into NixOS</quote> from the running system, which
|
||||||
|
only assumes <literal>bash</literal> and <literal>kexec</literal> to
|
||||||
|
be installed on the machine.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Note that kexec may not work correctly on some hardware, as devices
|
||||||
|
are not fully re-initialized in the process. In practice, this
|
||||||
|
however is rarely the case.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
To build the necessary files from your current version of nixpkgs,
|
||||||
|
you can run:
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
nix-build -A kexec.x86_64-linux '<nixpkgs/nixos/release.nix>'
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
This will create a <literal>result</literal> directory containing
|
||||||
|
the following:
|
||||||
|
</para>
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>bzImage</literal> (the Linux kernel)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>initrd</literal> (the initrd file)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>kexec-boot</literal> (a shellscript invoking
|
||||||
|
<literal>kexec</literal>)
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
<para>
|
||||||
|
These three files are meant to be copied over to the other already
|
||||||
|
running Linux Distribution.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Note it’s symlinks pointing elsewhere, so <literal>cd</literal> in,
|
||||||
|
and use <literal>scp * root@$destination</literal> to copy it over,
|
||||||
|
rather than rsync.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Once you finished copying, execute <literal>kexec-boot</literal>
|
||||||
|
<emphasis>on the destination</emphasis>, and after some seconds, the
|
||||||
|
machine should be booting into an (ephemeral) NixOS installation
|
||||||
|
medium.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
In case you want to describe your own system closure to kexec into,
|
||||||
|
instead of the default installer image, you can build your own
|
||||||
|
<literal>configuration.nix</literal>:
|
||||||
|
</para>
|
||||||
|
<programlisting language="bash">
|
||||||
|
{ modulesPath, ... }: {
|
||||||
|
imports = [
|
||||||
|
(modulesPath + "/installer/netboot/netboot-minimal.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
services.openssh.enable = true;
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [
|
||||||
|
"my-ssh-pubkey"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
</programlisting>
|
||||||
|
<programlisting>
|
||||||
|
nix-build '<nixpkgs/nixos>' \
|
||||||
|
--arg configuration ./configuration.nix
|
||||||
|
--attr config.system.build.kexecTree
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
Make sure your <literal>configuration.nix</literal> does still
|
||||||
|
import <literal>netboot-minimal.nix</literal> (or
|
||||||
|
<literal>netboot-base.nix</literal>).
|
||||||
|
</para>
|
||||||
|
</section>
|
|
@ -638,6 +638,7 @@ $ passwd eelco
|
||||||
<title>Additional installation notes</title>
|
<title>Additional installation notes</title>
|
||||||
<xi:include href="installing-usb.section.xml" />
|
<xi:include href="installing-usb.section.xml" />
|
||||||
<xi:include href="installing-pxe.section.xml" />
|
<xi:include href="installing-pxe.section.xml" />
|
||||||
|
<xi:include href="installing-kexec.section.xml" />
|
||||||
<xi:include href="installing-virtualbox-guest.section.xml" />
|
<xi:include href="installing-virtualbox-guest.section.xml" />
|
||||||
<xi:include href="installing-from-other-distro.section.xml" />
|
<xi:include href="installing-from-other-distro.section.xml" />
|
||||||
<xi:include href="installing-behind-a-proxy.section.xml" />
|
<xi:include href="installing-behind-a-proxy.section.xml" />
|
||||||
|
|
64
nixos/doc/manual/installation/installing-kexec.section.md
Normal file
64
nixos/doc/manual/installation/installing-kexec.section.md
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
# "Booting" into NixOS via kexec {#sec-booting-via-kexec}
|
||||||
|
|
||||||
|
In some cases, your system might already be booted into/preinstalled with
|
||||||
|
another Linux distribution, and booting NixOS by attaching an installation
|
||||||
|
image is quite a manual process.
|
||||||
|
|
||||||
|
This is particularly useful for (cloud) providers where you can't boot a custom
|
||||||
|
image, but get some Debian or Ubuntu installation.
|
||||||
|
|
||||||
|
In these cases, it might be easier to use `kexec` to "jump into NixOS" from the
|
||||||
|
running system, which only assumes `bash` and `kexec` to be installed on the
|
||||||
|
machine.
|
||||||
|
|
||||||
|
Note that kexec may not work correctly on some hardware, as devices are not
|
||||||
|
fully re-initialized in the process. In practice, this however is rarely the
|
||||||
|
case.
|
||||||
|
|
||||||
|
To build the necessary files from your current version of nixpkgs,
|
||||||
|
you can run:
|
||||||
|
|
||||||
|
```ShellSession
|
||||||
|
nix-build -A kexec.x86_64-linux '<nixpkgs/nixos/release.nix>'
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create a `result` directory containing the following:
|
||||||
|
- `bzImage` (the Linux kernel)
|
||||||
|
- `initrd` (the initrd file)
|
||||||
|
- `kexec-boot` (a shellscript invoking `kexec`)
|
||||||
|
|
||||||
|
These three files are meant to be copied over to the other already running
|
||||||
|
Linux Distribution.
|
||||||
|
|
||||||
|
Note it's symlinks pointing elsewhere, so `cd` in, and use
|
||||||
|
`scp * root@$destination` to copy it over, rather than rsync.
|
||||||
|
|
||||||
|
Once you finished copying, execute `kexec-boot` *on the destination*, and after
|
||||||
|
some seconds, the machine should be booting into an (ephemeral) NixOS
|
||||||
|
installation medium.
|
||||||
|
|
||||||
|
In case you want to describe your own system closure to kexec into, instead of
|
||||||
|
the default installer image, you can build your own `configuration.nix`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ modulesPath, ... }: {
|
||||||
|
imports = [
|
||||||
|
(modulesPath + "/installer/netboot/netboot-minimal.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
services.openssh.enable = true;
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [
|
||||||
|
"my-ssh-pubkey"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```ShellSession
|
||||||
|
nix-build '<nixpkgs/nixos>' \
|
||||||
|
--arg configuration ./configuration.nix
|
||||||
|
--attr config.system.build.kexecTree
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure your `configuration.nix` does still import `netboot-minimal.nix` (or
|
||||||
|
`netboot-base.nix`).
|
|
@ -476,6 +476,7 @@ With a partitioned disk.
|
||||||
```{=docbook}
|
```{=docbook}
|
||||||
<xi:include href="installing-usb.section.xml" />
|
<xi:include href="installing-usb.section.xml" />
|
||||||
<xi:include href="installing-pxe.section.xml" />
|
<xi:include href="installing-pxe.section.xml" />
|
||||||
|
<xi:include href="installing-kexec.section.xml" />
|
||||||
<xi:include href="installing-virtualbox-guest.section.xml" />
|
<xi:include href="installing-virtualbox-guest.section.xml" />
|
||||||
<xi:include href="installing-from-other-distro.section.xml" />
|
<xi:include href="installing-from-other-distro.section.xml" />
|
||||||
<xi:include href="installing-behind-a-proxy.section.xml" />
|
<xi:include href="installing-behind-a-proxy.section.xml" />
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
# This module exposes a config.system.build.kexecBoot attribute,
|
|
||||||
# which returns a directory with kernel, initrd and a shell script
|
|
||||||
# running the necessary kexec commands.
|
|
||||||
|
|
||||||
# It's meant to be scp'ed to a machine with working ssh and kexec binary
|
|
||||||
# installed.
|
|
||||||
|
|
||||||
# This is useful for (cloud) providers where you can't boot a custom image, but
|
|
||||||
# get some Debian or Ubuntu installation.
|
|
||||||
|
|
||||||
{ pkgs
|
|
||||||
, modulesPath
|
|
||||||
, config
|
|
||||||
, ...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
(modulesPath + "/installer/netboot/netboot-minimal.nix")
|
|
||||||
];
|
|
||||||
|
|
||||||
config = {
|
|
||||||
system.build.kexecBoot =
|
|
||||||
let
|
|
||||||
kexecScript = pkgs.writeScript "kexec-boot" ''
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
if ! kexec -v >/dev/null 2>&1; then
|
|
||||||
echo "kexec not found: please install kexec-tools" 2>&1
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
SCRIPT_DIR=$( cd -- "$( dirname -- "''${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
|
||||||
kexec --load ''${SCRIPT_DIR}/bzImage \
|
|
||||||
--initrd=''${SCRIPT_DIR}/initrd.gz \
|
|
||||||
--command-line "init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}"
|
|
||||||
kexec -e
|
|
||||||
''; in
|
|
||||||
pkgs.linkFarm "kexec-tree" [
|
|
||||||
{
|
|
||||||
name = "initrd.gz";
|
|
||||||
path = "${config.system.build.netbootRamdisk}/initrd";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "bzImage";
|
|
||||||
path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "kexec-boot";
|
|
||||||
path = kexecScript;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -101,6 +101,37 @@ with lib;
|
||||||
boot
|
boot
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# A script invoking kexec on ./bzImage and ./initrd.gz.
|
||||||
|
# Usually used through system.build.kexecTree, but exposed here for composability.
|
||||||
|
system.build.kexecScript = pkgs.writeScript "kexec-boot" ''
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
if ! kexec -v >/dev/null 2>&1; then
|
||||||
|
echo "kexec not found: please install kexec-tools" 2>&1
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "''${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
kexec --load ''${SCRIPT_DIR}/bzImage \
|
||||||
|
--initrd=''${SCRIPT_DIR}/initrd.gz \
|
||||||
|
--command-line "init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}"
|
||||||
|
kexec -e
|
||||||
|
'';
|
||||||
|
|
||||||
|
# A tree containing initrd.gz, bzImage and a kexec-boot script.
|
||||||
|
system.build.kexecTree = pkgs.linkFarm "kexec-tree" [
|
||||||
|
{
|
||||||
|
name = "initrd.gz";
|
||||||
|
path = "${config.system.build.netbootRamdisk}/initrd";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "bzImage";
|
||||||
|
path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "kexec-boot";
|
||||||
|
path = config.system.build.kexecScript;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
boot.loader.timeout = 10;
|
boot.loader.timeout = 10;
|
||||||
|
|
||||||
boot.postBootCommands =
|
boot.postBootCommands =
|
||||||
|
|
|
@ -151,6 +151,13 @@ in rec {
|
||||||
# Build the initial ramdisk so Hydra can keep track of its size over time.
|
# Build the initial ramdisk so Hydra can keep track of its size over time.
|
||||||
initialRamdisk = buildFromConfig ({ ... }: { }) (config: config.system.build.initialRamdisk);
|
initialRamdisk = buildFromConfig ({ ... }: { }) (config: config.system.build.initialRamdisk);
|
||||||
|
|
||||||
|
kexec = forMatchingSystems supportedSystems (system: (import lib/eval-config.nix {
|
||||||
|
inherit system;
|
||||||
|
modules = [
|
||||||
|
./modules/installer/netboot/netboot-minimal.nix
|
||||||
|
];
|
||||||
|
}).config.system.build.kexecTree);
|
||||||
|
|
||||||
netboot = forMatchingSystems supportedSystems (system: makeNetboot {
|
netboot = forMatchingSystems supportedSystems (system: makeNetboot {
|
||||||
module = ./modules/installer/netboot/netboot-minimal.nix;
|
module = ./modules/installer/netboot/netboot-minimal.nix;
|
||||||
inherit system;
|
inherit system;
|
||||||
|
|
|
@ -18,8 +18,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
|
||||||
virtualisation.vlans = [ ];
|
virtualisation.vlans = [ ];
|
||||||
environment.systemPackages = [ pkgs.hello ];
|
environment.systemPackages = [ pkgs.hello ];
|
||||||
imports = [
|
imports = [
|
||||||
"${modulesPath}/installer/kexec/kexec-boot.nix"
|
"${modulesPath}/installer/netboot/netboot-minimal.nix"
|
||||||
"${modulesPath}/profiles/minimal.nix"
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -33,14 +32,14 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
|
||||||
node1.connect()
|
node1.connect()
|
||||||
node1.wait_for_unit("multi-user.target")
|
node1.wait_for_unit("multi-user.target")
|
||||||
|
|
||||||
# Check if the machine with kexec-boot.nix profile boots up
|
# Check if the machine with netboot-minimal.nix profile boots up
|
||||||
node2.wait_for_unit("multi-user.target")
|
node2.wait_for_unit("multi-user.target")
|
||||||
node2.shutdown()
|
node2.shutdown()
|
||||||
|
|
||||||
# Kexec node1 to the toplevel of node2 via the kexec-boot script
|
# Kexec node1 to the toplevel of node2 via the kexec-boot script
|
||||||
node1.succeed('touch /run/foo')
|
node1.succeed('touch /run/foo')
|
||||||
node1.fail('hello')
|
node1.fail('hello')
|
||||||
node1.execute('${nodes.node2.config.system.build.kexecBoot}/kexec-boot', check_return=False)
|
node1.execute('${nodes.node2.config.system.build.kexecTree}/kexec-boot', check_return=False)
|
||||||
node1.succeed('! test -e /run/foo')
|
node1.succeed('! test -e /run/foo')
|
||||||
node1.succeed('hello')
|
node1.succeed('hello')
|
||||||
node1.succeed('[ "$(hostname)" = "node2" ]')
|
node1.succeed('[ "$(hostname)" = "node2" ]')
|
||||||
|
|
Loading…
Reference in a new issue