Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve correct Amazon attached EBS device from instance metadata endpoint

EDIT and TL;DR:

ubuntu@ip-172-31-19-77:~/.aws$ aws ec2 describe-instances | jq . | grep -i device
          "BlockDeviceMappings": [],
          "RootDeviceType": "ebs",
          "RootDeviceName": "/dev/sda1",
                "DeviceIndex": 0,
          "BlockDeviceMappings": [
              "DeviceName": "/dev/sda1",
              "DeviceName": "/dev/xvdb",
          "RootDeviceType": "ebs",
          "RootDeviceName": "/dev/sda1",
                "DeviceIndex": 0,
          "BlockDeviceMappings": [
              "DeviceName": "/dev/sda1",
          "RootDeviceType": "ebs",
          "RootDeviceName": "/dev/sda1",
                "DeviceIndex": 0,
          "BlockDeviceMappings": [
              "DeviceName": "/dev/xvda",
          "RootDeviceType": "ebs",
          "RootDeviceName": "/dev/xvda",
                "DeviceIndex": 0,
          "BlockDeviceMappings": [
              "DeviceName": "/dev/sda1",
              "DeviceName": "/dev/sdf",
          "RootDeviceType": "ebs",
          "RootDeviceName": "/dev/sda1",
ubuntu@ip-172-31-19-77:~/.aws$ aws ec2 describe-volumes | jq . | grep -i device
          "Device": "/dev/sda1"
          "Device": "/dev/sdf"
          "Device": "/dev/xvda"
          "Device": "/dev/sda1"
          "Device": "/dev/xvdb"
          "Device": "/dev/sda1"
ubuntu@ip-172-31-19-77:~/.aws$ ls /dev/sd*
ls: cannot access '/dev/sd*': No such file or directory
ubuntu@ip-172-31-19-77:~/.aws$ ls /dev/xv*
ls: cannot access '/dev/xv*': No such file or directory
ubuntu@ip-172-31-19-77:~/.aws$ lsblk
NAME        MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme0n1     259:0    0   1G  0 disk
nvme1n1     259:1    0   8G  0 disk
└─nvme1n1p1 259:2    0   8G  0 part /

Explanation:

I would like to know in advance which block device a particular AWS instance is supposed to have post-attachment.

Unfortunately, the instance metadata endpoint for block devices introduced back in 2007 does not seem to work reliably anymore?

Here's an example t2.medium instance I'm running with latest Ubuntu (17.10) AMI at the time of writing this (not Amazon Linux):

enter image description here

Not only the EBS modules show up in the console, but seem to be properly attached:

enter image description here

According to the official AWS docs on block-device-mappings:

block-device-mapping/ebsN: The virtual devices associated with Amazon EBS volumes, if any are present. Amazon EBS volumes are only available in metadata if they were present at launch time or when the instance was last started. The N indicates the index of the Amazon EBS volume (such as ebs1 or ebs2). 2007-12-15

But unfortunately there are no such endpoints present after the instance is fully up and running and all EBS volumes are attached:

$ curl http://169.254.169.254/latest/meta-data/block-device-mapping/
ami
ephemeral0
ephemeral1
$ curl http://169.254.169.254/latest/meta-data/block-device-mapping/ami
/dev/sda1
$ curl http://169.254.169.254/latest/meta-data/block-device-mapping/ebs1
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <title>404 - Not Found</title>
 </head>
 <body>
  <h1>404 - Not Found</h1>
 </body>
</html>
$ curl http://169.254.169.254/latest/meta-data/block-device-mapping/ebs2
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <title>404 - Not Found</title>
 </head>
 <body>
  <h1>404 - Not Found</h1>
 </body>
</html>

On the other hand, the operating system (under XEN device scheme), exposes the volumes via xvdXX devices:

$ ls /dev/xvd*
/dev/xvda  /dev/xvda1  /dev/xvdf  /dev/xvdg

So this leaves me with a poor solution of just assuming which block device is going to be depending on which instance I'm running, which predictably breaks once, for instance, Amazon introduces new iron with completely different underlying block device naming schemes such as /dev/nvme0n1p1:

https://twitter.com/braincode/status/968005482102190080

Here's what happens on freshly instantiated M5 instance with an EBS volume attached:

$ curl http://169.254.169.254/latest/meta-data/block-device-mapping/
ami
ebs2
root

$ curl http://169.254.169.254/latest/meta-data/block-device-mapping/ebs2
sdf
  1. Why is only ebs2 listed and not ebs1?
  2. sdf?

There's not a single sd* on m5 instance's operating system:

$ ls /dev/sd*
ls: cannot access '/dev/sd*': No such file or directory

What am I doing wrong? Is this a known bug? How do people/boto/cloudinit modules handle that?

like image 505
brainstorm Avatar asked Apr 18 '18 04:04

brainstorm


People also ask

What is the proper URL for accessing the EC2 instance metadata?

To view instance metadata, you can only use the link-local address of 169.254. 169.254 to access. Requests to the metadata via the URI are free, so there are no additional charges from AWS. Using the curl tool on Linux or the PowerShell cmdlet Invoke-WebRequest on Windows, you will first create your token.

How do I know if my EBS is attached to EC2?

Open the Amazon EC2 console at https://console.aws.amazon.com/ec2/ . In the navigation pane, choose Instances. Select the instance. On the Storage tab, the Block devices section lists the volumes that are attached to the instance.

What is AWS instance metadata?

Instance metadata is data about your instance that you can use to configure or manage the running instance. Instance metadata is divided into categories, for example, host name, events, and security groups. You can also use instance metadata to access user data that you specified when launching your instance.


1 Answers

Getting and tagging the EBS RootVolume from running EC2instance: this is how you can achieve it:

Instance_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
Instance_Name=$(aws ec2 describe-instances --instance-ids ${Instance_ID} --query 'Reservations[0].Instances[0].Tags[?Key==`Name`].Value' --output text)
RootDeviceName=$(aws ec2 describe-instances --instance-ids ${Instance_ID} --query 'Reservations[0].Instances[0].RootDeviceName' --output text)
RootVolumeId=$(aws ec2 describe-instances --instance-ids ${Instance_ID} --query "Reservations[0].Instances[0].BlockDeviceMappings[?DeviceName==\`${RootDeviceName}\`].Ebs.VolumeId" --output text)

if you want to set a RootVolume Tag (which isn't supported by CloudFormation yet (2019-03-14)):

aws ec2 create-tags --resources ${RootVolumeId} --tags Key=Name,Value=RootVolume-${Instance_Name}

You need the following IAM Permissions for that:

"ec2:DescribeInstances"
"ec2:CreateTags"

This is how it works:

just look at the (shortened) ec2 json response:

aws ec2 describe-instances --instance-ids ${Instance_ID}
{
    "Reservations": [
        {
            "Instances": [
                {
                    "InstanceId": "i-XXXXX", 
                    "BlockDeviceMappings": [
                        {
                            "DeviceName": "/dev/xvda", 
                            "Ebs": {
                                "Status": "attached", 
                                "DeleteOnTermination": true, 
                                "VolumeId": "vol-XXXXX", 
                                "AttachTime": "2019-02-27T07:56:07.000Z"
                            }
                        }, 
                        {
                            "DeviceName": "/dev/sdm", 
                            "Ebs": {
                                "Status": "attached", 
                                "DeleteOnTermination": false, 
                                "VolumeId": "vol-XXXXX", 
                                "AttachTime": "2019-02-27T07:58:02.000Z"
                            }
                        }
                    ], 
                    "RootDeviceType": "ebs", 
                    "RootDeviceName": "/dev/xvda", 
                }
            ], 
            "ReservationId": "r-XXXXX", 
            "RequesterId": "XXXXX", 
            "Groups": [], 
            "OwnerId": "XXXXX"
        }
    ]
}

As you can see: every instance has BlockDeviceMappings and a RootDeviceName property.
1) You can select the current as DeviceName as Value from RootDeviceName property.
2) With that information you can parse your BlockDeviceMappings by matching your selected DeviceName and finally get the VolumeID.

Thats it.

like image 90
mengmann Avatar answered Oct 14 '22 14:10

mengmann