EBS スナップショットと仲良くなりたい、と思ったら coldsnap

結論

EBS スナップショットをダウンロードしたり EBS スナップショットとしてアップロードしたかったら coldsnap を使おう。Rust で書かれてて速そう。

coldsnap どうやって使うの?

Rust で書かれてるので cargo install coldsnap とかするとインストールできる。

$ coldsnap download snapshot-00000000000000000 snapshot-00000000000000000.img

と実行すると snapshot-00000000000000000.imgsnapshot-00000000000000000 の中身が落ちてくる。凄い!

$ coldsnap upload test.img

と実行すると test.img の内容でスナップショットができる。凄い!

以下蛇足

EBS スナップショットをダウンロード、何や?

EBS direct API というのがあって EBS スナップショットがユーザーにも扱えるようになってます。 Action が 6 つしかなくてとてもシンプル。

  • CompleteSnapshot
  • GetSnapshotBlock
  • ListChangedBlocks
  • ListSnapshotBlocks
  • PutSnapshotBlock
  • StartSnapshot

AWS CLI で試してみる

下準備

インスタンスの上で試してみます。 EBS ボリュームを作ります。1 GiB で十分なのでそれで。

$ volume_id=$(aws --output text ec2 create-volume --availability-zone $AZ --size 1 --query 'VolumeId')

インスタンスにアッタッチして使えるようにしましょう。

$ token=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 10")
$ instance_id=$(curl -s -H "X-aws-ec2-metadata-token: $token" http://169.254.169.254/latest/meta-data/instance-id)
$ aws ec2 attach-volume --volume-id $volume_id --instance-id $instance_id --device /dev/sdx --query 'State'
"attaching"

インスタンスから見えるようになりました。

$ sudo fdisk -l /dev/nvme7n1 
Disk /dev/nvme7n1: 1 GiB, 1073741824 bytes, 2097152 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes

空っぽの EBS ボリュームですがとりあえずスナップショット作ってみます。

$ snapshot_id_1=$(aws --output text ec2 create-snapshot --volume-id $volume_id --query 'SnapshotId')

スナップショットの構成を読んでみる

ListSnapshotBlocks を使うとスナップショットがどのように構成されているかを表示することができます。

$ aws ebs list-snapshot-blocks --snapshot-id $snapshot_id_1
{
    "Blocks": [],
    "ExpiryTime": 1663734712.053,
    "VolumeSize": 1,
    "BlockSize": 524288
}

524288 B = 512 KiB のブロックが 0 コで構成されているスナップショットだ、という表示が出ました。 何も書いてないので当然ですね。 BlockSize は現在のところ 512 KiB だけだそうです。

内容がないと面白くないので少しだけ書いてみます。

$ echo -n wozozo | sudo dd of=/dev/nvme7n1 
0+1 records in
0+1 records out
6 bytes copied, 0.0215656 s, 0.3 kB/s

スナップショットを再度取って構成を表示してみると?

$ snapshot_id_2=$(aws --output text ec2 create-snapshot --volume-id $volume_id --query 'SnapshotId')
$ aws ebs list-snapshot-blocks --snapshot-id $snapshot_id_2
{
    "Blocks": [
        {
            "BlockIndex": 0,
            "BlockToken": "ACIBAfD21rcx+g2ieMHO/YtJBM48t3hp4jkczhv3SIk6UU1GHGMbO++vGwlZ"
        }
    ],
    "ExpiryTime": 1664332577.454,
    "VolumeSize": 1,
    "BlockSize": 524288
}

ブロック 1 つで構成されているスナップショットであることが確認できました!!

スナップショットのブロックを取得してみる

このブロックを取得してみましょう。 GetSnapshotBlock が使えます。 ブロックの指定には BlockToken 等を指定します。

$ aws ebs get-snapshot-block --snapshot-id $snapshot_id_2 --block-index 0 --block-token "ACIBAfD21rcx+g2ieMHO/YtJBM48t3hp4jkczhv3SIk6UU1GHGMbO++vGwlZ" wozozo.dat
{
    "DataLength": "524288",
    "Checksum": "Z8xlnwXWxcayKPxJmnIdw3A+aC7haNio+dC+KgEAJ8U=",
    "ChecksumAlgorithm": "SHA256"
}

wozozo.dat にブロックが書き込まれたはずなので確認してみましょう。

$ ls -l wozozo.dat 
-rw-rw-r-- 1 ubuntu ubuntu 524288 Sep 21 04:35 wozozo.dat
$ openssl sha256 -binary wozozo.dat | base64
Z8xlnwXWxcayKPxJmnIdw3A+aC7haNio+dC+KgEAJ8U=
$ od -c wozozo.dat 
0000000   w   o   z   o   z   o  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
2000000

未使用部分はヌル文字で埋められた 512 KiB のブロックがダウンロードできていました。 チェックサムも合っていますね!

差分を確認してみる

AWS CLIListChangedBlocks もサポートしてるので試してみます。 ディスクの冒頭を上書きして、更にちょっと進んだところにも書いてスナップショットを取ります。

$ echo -n dancho | sudo dd of=/dev/nvme7n1 
0+1 records in
0+1 records out
6 bytes copied, 0.0192607 s, 0.3 kB/s
ubuntu@ip-172-31-0-38:~$ echo -n dancho | sudo dd of=/dev/nvme7n1 bs=512K seek=16
0+1 records in
0+1 records out
6 bytes copied, 0.0176629 s, 0.3 kB/s
ubuntu@ip-172-31-0-38:~$ snapshot_id_3=$(aws --output text ec2 create-snapshot --volume-id $volume_id --query 'SnapshotId')

直前のスナップショットと比較してみると

ubuntu@ip-172-31-0-38:~$ aws ebs list-changed-blocks --first-snapshot-id $snapshot_id_2 --second-snapshot-id $snapshot_id_3
{
    "ChangedBlocks": [
        {
            "BlockIndex": 0,
            "FirstBlockToken": "ACIBAV721rcx+g2ieMHO/YtJBM48t3hp4jq01Byg+OVf01PvSs1NvFrpHieR",
            "SecondBlockToken": "ACIBAWW0tZi8nuBpxLyQyWckOBc0xGOU1ovXWL1fhSTVPL2ZjU6iUhTmtZnk"
        },
        {
            "BlockIndex": 16,
            "SecondBlockToken": "ACIBAY3+0jkvSBeEojA1fi37tpi2Z/UkctZcE3GqAy2JKI0HmD1gDMAv5IkF"
        }
    ],
    "ExpiryTime": 1664339386.537,
    "VolumeSize": 1,
    "BlockSize": 524288
}
$ snapshot_id_3=$(aws --output text ec2 create-snapshot --volume-id $volume_id --query 'SnapshotId')

更新と追記で 2 つのエントリーが確認できました。

結論

こんな感じのことをまるっとやってくれる coldsnap すてき!