nix-community / nixos-generators

Collection of image builders [maintainer=@Lassulus]
MIT License
1.82k stars 144 forks source link

amazon/EC2 AMI - ClientError: Unable to find an etc directory with fstab. #343

Closed TimekillerTK closed 4 months ago

TimekillerTK commented 4 months ago

I'm having a lot of issues trying to build functional EC2 AMIs. The image builds, but trying to import the built image (via the VM Import/Export tool: https://docs.aws.amazon.com/vm-import/latest/userguide/vmimport-image-import.html) as an AMI causes the following error:

ClientError: Unable to find an etc directory with fstab.

I'm using the following command & configuration:

flake.nix

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
    nixos-generators = {
      url = "github:nix-community/nixos-generators";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };
  outputs = { self, nixpkgs, nixos-generators, ... }: {
    packages.x86_64-linux = {
      amazon = nixos-generators.nixosGenerate {
        system = "x86_64-linux";
        modules = [
          ./configuration.nix

          # NOTE: https://github.com/nix-community/nixos-generators/issues/150
          ({...}: { amazonImage.sizeMB = 6 * 1024; })
        ];

        format = "amazon";
      };
    };
  };
}

configuration.nix

{  modulesPath, config, pkgs, lib, ... }:
{
  imports = [ 
    "${modulesPath}/virtualisation/amazon-image.nix"
  ];

  ec2.hvm = true;
  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";

  # List packages installed in system profile.
  environment.systemPackages = with pkgs; [ 
    vim 
    nmap
    jq
    awscli2
  ];
  networking.hostName = "nixos"; # Define your hostname.

  # Enabling Flakes
  nix.settings.experimental-features = [ "nix-command" "flakes" ];

  # Set your time zone.
  time.timeZone = "Europe/Amsterdam";

  # Select internationalisation properties.
  i18n.defaultLocale = "en_US.UTF-8"; 
  i18n.extraLocaleSettings = { 
    LC_ADDRESS = "nl_NL.UTF-8"; 
    LC_IDENTIFICATION = "nl_NL.UTF-8"; 
    LC_MEASUREMENT ="nl_NL.UTF-8"; 
    LC_MONETARY = "nl_NL.UTF-8"; 
    LC_NAME = "nl_NL.UTF-8"; 
    LC_NUMERIC = "nl_NL.UTF-8"; 
    LC_PAPER = "nl_NL.UTF-8"; 
    LC_TELEPHONE = "nl_NL.UTF-8";
    LC_TIME = "nl_NL.UTF-8";
  };
  system.stateVersion = "24.05"; # Did you read the comment?
}
TimekillerTK commented 4 months ago

I sat myself down and did some reading and managed to find some answers. I found them by investigating how the official NixOS AMI Images are/were created:

To create functional EC2 AMIs, I needed to change my approach. First use the AWS SDK/CLI to import-snapshot

aws ec2 import-snapshot \
    --disk-container file://import_snapshot.json

JSON file with disk container details

{
    "Description": "Your NixOS AMI",
    "Format": "VHD",
    "UserBucket": {
        "S3Bucket": "YourBucketName",
        "S3Key": "path/to/nixos-amazon-image-24.05.20240618.938aa15-x86_64-linux.vhd"
    }
}

Once the snapshot import is successful, use register-image to register the snapshot as an AMI:

aws ec2 register-image \
    --name "my-ami-image" \
    --architecture "x86_64" \
    --boot-mode "legacy-bios" \
    --root-device-name '/dev/xvda' \
    --virtualization-type hvm \
    --ena-support \
    --imds-support='v2.0' \
    --sriov-net-support simple \
    --block-device-mappings "file://register_image.json"

JSON file with block device mappings

[
    {
        "DeviceName": "/dev/xvda",
        "Ebs": {
            "SnapshotId": "snap-xxxxxxxxxxxxxx",
            "VolumeType": "gp3"
        }
    }
]

It's key to set the boot-mode to whatever you set in your configuration.nix, which in my case was legacy-bios, but you can create a UEFI image just as easily.

It's also important to set the root device name accordingly to the virtualization type used on the EC2 instance. /dev/xvda in my case, since it's hvm - see more about device naming on EC2 instances.

I'm still not entirely sure how to create functional EC2 AMIs with the VM Import/Export tool, but I suspect it's because there's missing boot information in the configuration.nix which is otherwise provided via register-image.