2014年7月にEC2のインスタンスファミリーにt2系のインスタンスが追加されました。一つ前の世代のインスタンスであるt1系のインスタンスの価格よりも安く、スペックは上がっているお得なインスタンスです。

こうなるとt1系のインスタンスを使い続ける理由は無いので、t1.micro → t2.micro へインスタンスをアップデートしたくなります。ただこのt2系のインスタンスを使用するには以下の制約があります。

  • EC2-VPCのみサポート
    EC2-Classicのプラットフォームはサポートしてないので、EC2-Classicの環境ではVPCを作成してそのVPCでインスタンスを起動する必要があります。
  • サポートしている仮想化方式はHVMのみ
    t2系のインスタンスはHVM AMIからしか起動できません。仮想化方式がPVのAMIからインスタンスを起動することはできません。

EC2-VPCの制約はVPCを作成すればいいので特に問題にはなりませんが、仮想化方式の方がネックになります。t1.microで起動しているインスタンスがPVのAMIであれば、そのままではt2系のインスタンスへ変更することはできません。AWSからも現時点では公式にPV→HVMの変換機能やツールは提供されていません。

ということで、今回は pv2hvm というツールを使って移行してみました。pv2hvmは作業用のインスタンスに元のAMIのスナップショットと新規に作成した移行先のディスクボリュームをマウントし、データのコピー、grubのインストール等を行い、HVMなAMIの作成を一括で行ってくれるツールです。

要件と制約

pv2hvmを使用するにあたり以下の要件を満たす必要があります。

  • 必ずEC2インスタンスで動かすこと
  • AWS SDK for Rubyが必要
  • EC2の管理権限を持つインスタンスプロファイルを持ってること
  • 元のAMIは自分のものかスナップショットの作成が許可されたものであること
  • 元のAMIにgrubがインストールされていること
  • テストは最近のAmazon Linuxでしか行われていない
  • マーケットプレイスのAMIは変換できない

では早速マイグレーション!

1. grubのインストール

移行対象のインスタンスにgrubをインストールします。

$ sudo yum install grub
2. AMIの作成

移行対象のインスタンスを停止し、AMIを作成します。作成したAMIのIDをメモしときます。

3. 変換処理

変換処理を行うインスタンスを新しく起動します。起動の際にEC2のPower User権限を持ったIAM Roleを適用しておきます。
インスタンスが起動したらAWS SDK for Rubyをインストールします。

$ sudo yum groupinstall "Development Tools"
$ sudo yum install ruby-devel
$ sudo gem install nokogiri
$ sudo gem install aws-sdk

pv2hvmをダウンロードして変換処理を実行。

$ wget  https://raw.githubusercontent.com/j3tm0t0/pv2hvm/master/pv2hvm.rb

※変換処理を行う際にpv2hvm内部でddコマンドが発行されますが、bsオプションが付加されてない状態で実行されるのでddの実行に結構時間がかかります。そのため↓のようにddコマンドを発行する際にbsオプションを渡すよう変更しました。

"dd if=/dev/xvdm of=/dev/xvdo1",
↓
"dd if=/dev/xvdm of=/dev/xvdo1 bs=256M",

変換処理実行。

$ sudo ruby pv2hvm.rb 2で作ったAMIのID
source ami=ami-XXXXXXXX
-- prepare volume
creating source volume from snapshot : snap-b3f5a535
 vol-a5a8d8af created and attached to /dev/sdm
creating target volume with size : 31
 vol-48a9d942 created and attached to /dev/sdo

-- copy disk
# parted /dev/xvdo --script 'mklabel msdos mkpart primary 1M -1s print quit'
モデル: Xen Virtual Block Device (xvd)
ディスク /dev/xvdo: 33.3GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: msdos

番号  開始    終了    サイズ  タイプ   ファイルシステム  フラグ
 1    1049kB  33.3GB  33.3GB  primary


# partprobe /dev/xvdo

# udevadm settle

# dd if=/dev/xvdm of=/dev/xvdo1 bs=256M

-- install grub
# mount /dev/xvdo1 /mnt

# cp -a /dev/xvdo /dev/xvdo1 /mnt/dev/

# rm -f /mnt/boot/grub/*stage*

# cp /mnt/usr/*/grub/*/*stage* /mnt/boot/grub/

# rm -f /mnt/boot/grub/device.map

# printf "device (hd0) /dev/xvdo\nroot (hd0,0)\nsetup (hd0)\n" | chroot /mnt grub --batch
Probing devices to guess BIOS drives. This may take a long time.


    GNU GRUB  version 0.97  (640K lower / 3072K upper memory)

 [ Minimal BASH-like line editing is supported.  For the first word, TAB
   lists possible command completions.  Anywhere else TAB lists the possible
   completions of a device/filename.]
grub> device (hd0) /dev/xvdo
grub> root (hd0,0)
 Filesystem type is ext2fs, partition type 0x83
grub> setup (hd0)
 Checking if "/boot/grub/stage1" exists... yes
 Checking if "/boot/grub/stage2" exists... yes
 Checking if "/boot/grub/e2fs_stage1_5" exists... yes
 Running "embed /boot/grub/e2fs_stage1_5 (hd0)"...  29 sectors are embedded.
succeeded
 Running "install /boot/grub/stage1 (hd0) (hd0)1+29 p (hd0,0)/boot/grub/stage2 /boot/grub/grub.conf"... succeeded
Done.
grub> 
# cat /mnt/boot/grub/menu.lst | tee /dev/stderr > /mnt/boot/grub/menu.lst.bak
# created by imagebuilder
default=0
timeout=1
hiddenmenu

title Amazon Linux 2014.09 (3.14.23-22.44.amzn1.x86_64)
root (hd0)
kernel /boot/vmlinuz-3.14.23-22.44.amzn1.x86_64 root=LABEL=/ console=hvc0 LANG=en_US.UTF-8 KEYTABLE=us
initrd /boot/initramfs-3.14.23-22.44.amzn1.x86_64.img

title Amazon Linux 2014.09 (3.14.20-20.44.amzn1.x86_64)
root (hd0)
kernel /boot/vmlinuz-3.14.20-20.44.amzn1.x86_64 root=LABEL=/ console=hvc0 LANG=en_US.UTF-8 KEYTABLE=us
initrd /boot/initramfs-3.14.20-20.44.amzn1.x86_64.img

title Amazon Linux 2014.09 (3.14.19-17.43.amzn1.x86_64)
root (hd0)
kernel /boot/vmlinuz-3.14.19-17.43.amzn1.x86_64 root=LABEL=/ console=hvc0 LANG=en_US.UTF-8 KEYTABLE=us
initrd /boot/initramfs-3.14.19-17.43.amzn1.x86_64.img


# cat /mnt/boot/grub/menu.lst.bak | perl -pe "s/\(hd0\)/\(hd0,0\)/;s/console=\S+/console=ttyS0/" | tee /dev/stderr > /mnt/boot/grub/menu.lst
# created by imagebuilder
default=0
timeout=1
hiddenmenu

title Amazon Linux 2014.09 (3.14.23-22.44.amzn1.x86_64)
root (hd0,0)
kernel /boot/vmlinuz-3.14.23-22.44.amzn1.x86_64 root=LABEL=/ console=ttyS0 LANG=en_US.UTF-8 KEYTABLE=us
initrd /boot/initramfs-3.14.23-22.44.amzn1.x86_64.img

title Amazon Linux 2014.09 (3.14.20-20.44.amzn1.x86_64)
root (hd0,0)
kernel /boot/vmlinuz-3.14.20-20.44.amzn1.x86_64 root=LABEL=/ console=ttyS0 LANG=en_US.UTF-8 KEYTABLE=us
initrd /boot/initramfs-3.14.20-20.44.amzn1.x86_64.img

title Amazon Linux 2014.09 (3.14.19-17.43.amzn1.x86_64)
root (hd0,0)
kernel /boot/vmlinuz-3.14.19-17.43.amzn1.x86_64 root=LABEL=/ console=ttyS0 LANG=en_US.UTF-8 KEYTABLE=us
initrd /boot/initramfs-3.14.19-17.43.amzn1.x86_64.img


# rm -f /mnt/dev/xvdo /mnt/dev/xvdo1

# umount /mnt

-- create snapshot of target volume
snapshot ID = snap-531142d5

image Id = 新しく作られたAMIのID
-- cleanup
deleting volumes
 vol-a5a8d8af deleted
 vol-48a9d942 deleted

以上で変換処理は終了です。image Id に記載されている新しく作られたAMIのIDをAMIのリストから選択して、t2系のインスタンスを選択して起動すれば完了です。移行処理はほぼpv2hvmが行ってくれるので簡単です。

※ PVとHVMの違い
  • PV(準仮想化)
    EC2のサービス開始当初から提供されている仮想化方式で、PV-GRUBと呼ばれる専用のブートローダで起動されます。特別なハードウェアの拡張には対応していません。
  • HVM(ハードウェア仮想マシン)
    完全に仮想化されたハードウェアを備えており、起動は物理マシンのOSと変りなく、ルートデバイスにあるブートローダからデバイス内のカーネルを起動します。PVと違いホスト上のハードウェアへの高速なアクセスをするためのハードウェア拡張が利用可能で、c3系のインスタンスで利用可能なSR-IOVによるパフォーマンスの向上などのメリットが享受できます。

もともとはPVの方が、IO系のハードウェアエミュレートのオーバーヘッドを回避するドライバを活用することでHVMより性能が優れていたようですが、HVMでもそのPVドライバが利用できるようなり、またHVM自体の機能強化により最近では、PVと同等かそれ以上の性能が出せるようになったようです。