Flatcar Container Linux startup process

    The Flatcar Container Linux startup process is built on the standard Linux startup process . Since this process is already well documented and generally well understood, this document will focus on aspects specific to booting Flatcar Container Linux.

    Bootloader

    GRUB is the first program executed when a Flatcar Container Linux system boots. The Flatcar Container Linux GRUB config has several roles.

    First, the GRUB config specifies which usr partition to use from the two usr partitions Flatcar Container Linux uses to provide atomic upgrades and rollbacks.

    Second, GRUB checks for a file called flatcar/first_boot in the EFI System Partition to determine if this is the first time a machine has booted (or it checks for coreos/first_boot if the machine was updated from CoreOS CL). If that file is found, GRUB sets the flatcar.first_boot=detected Linux kernel command line parameter. This parameter is used in later stages of the boot process.

    Finally, GRUB searches for the initial disk GUID (00000000-0000-0000-0000-000000000001) built into Flatcar Container Linux images. This GUID is randomized later in the boot process so that individual disks may be uniquely identified. If GRUB finds this GUID it sets another Linux kernel command line parameter, flatcar.randomize_guid=00000000-0000-0000-0000-000000000001.

    Early user space

    After GRUB, the Flatcar Container Linux startup process moves into the initial RAM file system. The initramfs mounts the root filesystem, randomizes the disk GUID, and runs Ignition.

    If the flatcar.randomize_guid kernel parameter is provided, the disk with the specified GUID is given a new, random GUID.

    If the flatcar.first_boot kernel parameter is provided and non-zero, Ignition and networkd are started. networkd will use DHCP to set up temporary IP addresses and routes so that Ignition can fetch its configuration from the network.

    Ignition

    When Ignition runs on Flatcar Container Linux, it reads the Linux command line, looking for flatcar.oem.id. Ignition uses this identifier to determine where to read the user-provided configuration and which provider-specific configuration to combine with the user’s. This provider-specific configuration performs basic machine setup, and may include enabling [email protected] (covered in more detail below).

    After Ignition runs successfully, if flatcar.first_boot was set to the special value detected, Ignition mounts the EFI System Partition and deletes the flatcar/first_boot file (or coreos/first_boot if the machine was updated from CoreOS CL).

    User space

    After all of the tasks in the initramfs complete, the machine pivots into user space. It is at this point that systemd begins starting units, including, if it was enabled, [email protected].

    SSH keys

    [email protected] is responsible for fetching SSH keys from the machine’s environment. The keys are written to ~core/.ssh/authorized_keys.d/coreos-metadata and update-ssh-keys is run to update ~core/.ssh/authorized_keys. On cloud platforms, the keys are read from the provider’s metadata service. This service is not supported on all platforms and is enabled by Ignition only on those which are supported.

    Reprovisioning

    To trigger a new Ignition run, you should use the flatcar-reset tool (available from Alpha 3535.0.0) for a (selective) cleanup of the root filesystem during the next boot. It takes care of cleaning up old state (e.g., files from the old configuration or any side effects such as state files) while keeping only the data you want to keep through the --keep-paths argument. The paths to keep can be specified as regular expressions. The machine ID can be kept through the --keep-machine-id argument (turning it into a kernel cmdline parameter because /etc/machine-id can’t be preserved directly for systemd first boot semantics). It is also possible to specify that a local or a particular remote Ignition configuration should be used.

    When paths to keep are specified, only needed paths should be used and not those set up by the old Ignition config or side effects of it, to really discard the old configuration state. When a path specified is a directory, the contents are preserved as well because MYPATH/.* is automatically appended as an additional regular expression for paths to keep. To delete the contents of a directory but keep the directory itself, specify it as an equivalent regular expression in the form of '^/etc/mypath', '/etc/mypath$', '/etc/mypat[h]', '/etc/(mypath)', or '(/etc/mypath)'.

    The used regular expression language is that of egrep. Assuming you specified /etc/mypath, you can test which paths will be deleted with the following command (note the -not):

    find / /etc -xdev -regextype egrep -not -regex '(/etc/mypath|/etc/mypath/.*)'
    

    You can test which path will be kept with the following command (note the absence of -not):

    find / /etc -xdev -regextype egrep -regex '(/etc/mypath|/etc/mypath/.*)'
    

    Both / and /etc need to be specified because /etc is an overlay mount.

    Meaningful examples are:

    • '/etc/ssh/ssh_host_.*' to preserve SSH host keys
    • /var/log to preserve system logs
    • /var/lib/docker and /var/lib/containerd to preserve container state and images

    An example for selectively resetting the OS with retriggering Ignition while keeping SSH host keys, logs, and machine ID:

    sudo flatcar-reset --keep-machine-id --keep-paths '/etc/ssh/ssh_host_.*' /var/log
    sudo systemctl reboot
    

    Technical Details for Manual Ignition Re-runs

    Not recommended but possible is to either manually set flatcar.first_boot=1 as temporary kernel command line parameter in GRUB or to create the flag file with touch /boot/flatcar/first_boot (or /boot/coreos/first_boot if the machine was updated from CoreOS CL). Be aware that if you changed the Ignition config in the mean time, old files not known to the new Ignition config will be kept, and any other runtime data, too. Systemd service presets are also not reevaluated automatically. This means that newly declared service units won’t be enabled unless you also invalidate the machine ID or create the symlinks for the service targets.

    To ensure that the systemd service presets are reevaluated you should invalidate the machine ID executing sudo rm /etc/machine-id before the reboot. This will give the node a new machine ID unless you have added the current machine ID as kernel argument in /usr/share/oem/grub.cfg (append the line set linux_append="$linux_append systemd.machine_id=..." to the end of the file, with the current machine ID instead of ...).

    If you can’t do this, you have to create the symlinks for the service target through Ignition links entries. Here is an example config with an additional links entry that ensures that the new service unit is enabled if this config is used for reprovisioning:

    {
      "ignition": {
        "version": "2.2.0"
      },
      "systemd": {
         "units": [
           {
             "name": "my.service",
             "enabled": true,
             "contents": "[Service]\nType=oneshot\nExecStart=/usr/bin/echo Hello World\n\n[Install]\nWantedBy=multi-user.target"
           }
         ]
       },
       "storage": {
         "links": [
           {
             "filesystem": "root",
             "path": "/etc/systemd/system/multi-user.target.wants/my.service",
             "target": "/etc/systemd/system/my.service"
           }
         ]
       }
    }
    

    Debugging Ignition

    In the event of Ignition failure, there are most of the time three scenarios:

    • Instance console access with emergency shell

    In this case, you should be able to quickly identify the error (malformed config, provider not implemented, unable to fetch configuration from the metadata service, unable to fetch a remote resource). From the emergency shell, it’s possible to query the Ignition status with journalctl -u ignition*.

    • Instance console access looks stuck

    It usually means that you landed into an emergency shell but the console is not correctly setup. You should reboot the instance and access the GRUB menu, then type e (for edit) and add: systemd.journald.max_level_console=debug console=ttyS0 as kernel command line parameters, it should look like this: linux$suf $gptprio_kernel $gptprio_cmdline $linux_cmdline systemd.journald.max_level_console=debug console=ttyS0. Hit Ctrl+x to finally boot, you should get back to the previous point.

    • No instance console access

    You can first try to validate your Ignition configuration offline:

    # Quick check: is it valid JSON?
    $ jq < config.json
    $ curl -fsSL https://github.com/coreos/ignition/releases/latest/download/ignition-validate-x86_64-linux -o ignition-validate
    $ chmod u+x ./ignition-validate
    $ ./ignition-validate config.json
    warning at $.storage.files.0.contents.souce, line 7 col 21: Unused key souce