vague memory

うろ覚えを無くしていこうともがき苦しむ人の備忘録

EC2 EBS縮小 (というより CentOS ディスク縮小) の流れ

AWS EC2(Linux) のディスクサイズ縮小を行う機会があったので、備忘として整理します。 復旧や作り直しと同意になるので極力行いたくない作業です。

流れは以下のようになります。
各 Volume、Snapshot には判別しやすい名称を付けておくと混乱しないと思います。

01. 縮小対象 EC2 インスタンスの Volume(vol-A) から Snapshot(snap-A) を作成
02. Snapshot から Volume(vol-B) を作成
03. 縮小後のサイズを指定して、空の Volume(vol-C) を作成
04. 作業用 EC2 インスタンスを起動
05. 作業用 EC2 インスタンスに Volume(vol-B、vol-C) をアタッチ
06. データコピー
07. 作業用 EC2 インスタンスから Volume(vol-C) をデタッチ
08. Volume(vol-C) から Snapshot(snap-C) を作成
09. Snapshot(snap-C) から AMI を作成
    - マネジメントコンソールでの操作はデフォルトで Virtualization type  が PV 指定になるようなので、 HVM の場合は注意
10. AMI から縮小後サイズの EC2 インスタンスを起動





肝は 05 のデータコピー部分になるかと思いますので、以下、05部分の手順(一例)です。
ファイルシステムによりコマンドが多少違いますし、環境によっても異なるのであくまで例です。
起動に失敗する場合は大抵 fstab と grub の設定部分誤りなので、その辺りに目星を付けて修正すると起動できると思います。

また、SELINUX は無効にしておいた方がトラブルは少ないと思います。(ssh接続不可等が発生する可能性があります)

$ grep '^SELINUX' /etc/selinux/config
SELINUX=disabled
SELINUXTYPE=targeted

環境

ext4 と xfs で 30GB -> 10GB への縮小を行います。
作業用インスタンスのOSは、コマンド有無があるので対象OSと合わせると良いかと思います。 また、Marketplace のAMIだと stop しないと attach/detach が行えないようなので、 Amazon Linux 等の公式AMIを使用するのが良いのかもしれません。

vol-xxxxxxxx : Unable to detach volume 'vol-xxxxxxxx' with Marketplace codes from instance 'i-xxxxxxxx' as the instance is not in the 'stopped' state.

今回は AWS Marketplace にある CentOS 標準の AMI を対象に実施しました。

縮小元インスタンス情報

Apacheは起動後の確認に使用しただけですが、一応載せてます。

ext4

[root@ip-172-30-0-241 /]# cat /etc/redhat-release
CentOS release 6.8 (Final)
[root@ip-172-30-0-241 /]# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       30G  1.1G   27G   4% /
tmpfs           498M     0  498M   0% /dev/shm
[root@ip-172-30-0-241 /]# httpd -v
Server version: Apache/2.2.15 (Unix)
Server built:   Jul 18 2016 15:24:00

xfs

[root@ip-172-30-0-149 /]# cat /etc/redhat-release
CentOS Linux release 7.2.1511 (Core)
[root@ip-172-30-0-149 /]# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       30G  1.4G   29G   5% /
devtmpfs        478M     0  478M   0% /dev
tmpfs           496M     0  496M   0% /dev/shm
tmpfs           496M   13M  484M   3% /run
tmpfs           496M     0  496M   0% /sys/fs/cgroup
tmpfs           100M     0  100M   0% /run/user/1000
[root@ip-172-30-0-149 /]# httpd -v
Server version: Apache/2.4.6 (CentOS)
Server built:   Jul 18 2016 15:30:14

データコピー手順

ほぼ同様の流れなので特記しない限りは ext4 の実行結果を載せます。

1) アタッチ状態確認

Volume(vol-B、vol-C) がアタッチされている事を確認します。 vol-Bを xvdf、vol-C(空ボリューム)を xvdg としています。

[root@ip-172-30-0-241 /]# lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  30G  0 disk
└─xvda1 202:1    0  30G  0 part
xvdf    202:80   0  30G  0 disk
└─xvdf1 202:81   0  30G  0 part /
xvdg    202:96   0  10G  0 disk

2) パーティション設定

縮小前(xvdf)の設定に合わせて縮小後(xvdg)のパーティション設定を行います。

[root@ip-172-30-0-241 /]# fdisk /dev/xvdg
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0x714a83d2.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
         switch off the mode (command 'c') and change display units to
         sectors (command 'u').

Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-1305, default 1):
Using default value 1
Last cylinder, +cylinders or +size{K,M,G} (1-1305, default 1305):
Using default value 1305

Command (m for help): a
Partition number (1-4): 1

Command (m for help): p

Disk /dev/xvdg: 10.7 GB, 10737418240 bytes
255 heads, 63 sectors/track, 1305 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x714a83d2

    Device Boot      Start         End      Blocks   Id  System
/dev/xvdg1   *           1        1305    10482381   83  Linux

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

縮小前後(xvdf/xvdg)で同様の設定になっている事を確認します。

[root@ip-172-30-0-241 /]# fdisk -l

Disk /dev/xvda: 32.2 GB, 32212254720 bytes
255 heads, 63 sectors/track, 3916 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00057cbb

    Device Boot      Start         End      Blocks   Id  System
/dev/xvda1   *           1        3916    31454246   83  Linux

Disk /dev/xvdf: 32.2 GB, 32212254720 bytes
255 heads, 63 sectors/track, 3916 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00057cbb

    Device Boot      Start         End      Blocks   Id  System
/dev/xvdf1   *           1        3916    31454246   83  Linux

Disk /dev/xvdg: 10.7 GB, 10737418240 bytes
255 heads, 63 sectors/track, 1305 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x714a83d2

    Device Boot      Start         End      Blocks   Id  System
/dev/xvdg1   *           1        1305    10482381   83  Linux

3) マウント

ファイルシステムを作成後、マウントします。

[root@ip-172-30-0-241 /]# mkfs.ext4 /dev/xvdg1
mke2fs 1.41.12 (17-May-2010)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
655360 inodes, 2620595 blocks
131029 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2684354560
80 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
    32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632

Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 21 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.
[root@ip-172-30-0-241 /]# mkdir -p /mnt/old /mnt/new/boot
[root@ip-172-30-0-241 /]# mount -t ext4 /dev/xvdf1 /mnt/old
[root@ip-172-30-0-241 /]# mount -t ext4 /dev/xvdg1 /mnt/new
[root@ip-172-30-0-241 /]# mount -l
/dev/xvdf1 on / type ext4 (rw)
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
devpts on /dev/pts type devpts (rw,gid=5,mode=620)
tmpfs on /dev/shm type tmpfs (rw,rootcontext="system_u:object_r:tmpfs_t:s0")
none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)
/dev/xvdf1 on /mnt/old type ext4 (rw)
/dev/xvdg1 on /mnt/new type ext4 (rw)
  • xfs

xfs を扱うためのパッケージをインストールしておきます。

yum install xfsprogs xfsdump
[root@ip-172-30-0-149 /]# mkfs.xfs /dev/xvdg1
meta-data=/dev/xvdg1             isize=256    agcount=4, agsize=655296 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=0        finobt=0
data     =                       bsize=4096   blocks=2621184, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=0
log      =internal log           bsize=4096   blocks=2560, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
[root@ip-172-30-0-149 /]# mkdir -p /mnt/old /mnt/new/boot
[root@ip-172-30-0-149 /]# mount -t xfs /dev/xvdf1 /mnt/old
[root@ip-172-30-0-149 /]# mount -t xfs /dev/xvdg1 /mnt/new
[root@ip-172-30-0-149 /]# mount -l
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime,seclabel)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
devtmpfs on /dev type devtmpfs (rw,nosuid,seclabel,size=489108k,nr_inodes=122277,mode=755)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,seclabel)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,seclabel,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,nodev,seclabel,mode=755)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,seclabel,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
pstore on /sys/fs/pstore type pstore (rw,nosuid,nodev,noexec,relatime)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
configfs on /sys/kernel/config type configfs (rw,relatime)
/dev/xvdf1 on / type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
rpc_pipefs on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw,relatime)
selinuxfs on /sys/fs/selinux type selinuxfs (rw,relatime)
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=27,pgrp=1,timeout=300,minproto=5,maxproto=5,direct)
debugfs on /sys/kernel/debug type debugfs (rw,relatime)
hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime,seclabel)
mqueue on /dev/mqueue type mqueue (rw,relatime,seclabel)
nfsd on /proc/fs/nfsd type nfsd (rw,relatime)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,seclabel,size=101536k,mode=700,uid=1000,gid=1000)
/dev/xvdf1 on /mnt/old type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
/dev/xvdg1 on /mnt/new type xfs (rw,relatime,seclabel,attr2,inode64,noquota)

その他にラベル付与やスワップ領域作成等が必要な場合は実施しておきます。

### ext4
# ラベル
e2label /dev/xvdg1 /
blkid
# スワップ
mkswap /dev/xvdg2

### xfs
# ラベル
xfs_admin -L "/boot" /dev/xvdg1
xfs_admin -L "/" /dev/xvdg3
blkid
# スワップ
mkswap /dev/xvdg2

4) ファイルコピー

容量により時間が掛かるのでバックグラウンド実行しておきます。複数パーティションが対象となる場合は、それぞれ実行します。

cd /mnt/old/
( LANG=C date; nohup rsync -avxH \
  --exclude ""proc/*"" \
  --exclude ""sys/*"" \
  --exclude ""dev/*"" \
  /mnt/old/ /mnt/new/; date \
) > /tmp/disk-sync.log 2>&1 < /dev/null &
  • xfs
cd /mnt/old/
( LANG=C date; nohup xfsdump -J - /mnt/old \
  | xfsrestore -J -p 60 - /mnt/new ; date \
) > /tmp/disk-sync.log 2>&1 < /dev/null &

5) grubインストール

chroot

OS起動時に作成されるディレクトリ群をマウントした後、 /mnt/new/ に対して chroot します。 (chroot した事を確認するためにログファイルコピーを行っていますが、必須ではありません。)

cp -ip /tmp/disk-sync.log /mnt/new/var/log/
gzip /mnt/new/var/log/disk-sync.log
ls -l /mnt/new/var/log/disk-sync.log*

mount -t proc /proc /mnt/new/proc
mount -t sysfs /sys /mnt/new/sys
mount --bind /dev /mnt/new/dev
chroot /mnt/new/
ls -l /var/log/disk-sync.log.gz

grub

[root@ip-172-30-0-241 /]# grub
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/xvdg
device (hd0) /dev/xvdg
grub> root (hd0,0)
root (hd0,0)
 Filesystem type is ext2fs, partition type 0x83
grub> setup (hd0)
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)"...  27 sectors are embedded.
succeeded
 Running "install /boot/grub/stage1 (hd0) (hd0)1+27 p (hd0,0)/boot/grub/stage2 /boot/grub/grub.conf"... succeeded
Done.
grub> quit
quit
  • xfs
[root@ip-172-30-0-149 /]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.10.0-327.36.3.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-327.36.3.el7.x86_64.img
Found linux image: /boot/vmlinuz-3.10.0-327.10.1.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-327.10.1.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-f32e0af35637b5dfcbedcb0a1de8dca1
Found initrd image: /boot/initramfs-0-rescue-f32e0af35637b5dfcbedcb0a1de8dca1.img
done
[root@ip-172-30-0-149 /]# grub2-install /dev/xvdg
Installing for i386-pc platform.
Installation finished. No error reported.

6) fstab/grub.conf 書き換え

fstab 、grub.conf に UUID 指定がある場合は、書き換えを行います。 (CentOS6 には grub-mkconfig が無いようなので grub.conf は直接書き換えています。)

[root@ip-172-30-0-241 /]# blkid
/dev/xvda1: UUID="cdbab22a-45d6-4cce-95a3-681f42187a46" TYPE="ext4"
/dev/xvdg1: UUID="67a96f57-8e2a-4d6f-b47d-2128b558cc58" TYPE="ext4"
[root@ip-172-30-0-241 /]# cp -ip /etc/fstab /etc/fstab.org
[root@ip-172-30-0-241 /]# vi /etc/fstab
[root@ip-172-30-0-241 /]# diff /etc/fstab.org /etc/fstab
9c9
< UUID=cdbab22a-45d6-4cce-95a3-681f42187a46 /                       xfs     defaults        0 0
---
> UUID=67a96f57-8e2a-4d6f-b47d-2128b558cc58 /                       xfs     defaults        0 0
[root@ip-172-30-0-241 /]# cp -ip /boot/grub/grub.conf /boot/grub/grub.conf.org
[root@ip-172-30-0-241 /]# vi /boot/grub/grub.conf
[root@ip-172-30-0-241 /]# diff /boot/grub/grub.conf.org /boot/grub/grub.conf
17c17
<    kernel /boot/vmlinuz-2.6.32-573.18.1.el6.x86_64 ro root=UUID=cdbab22a-45d6-4cce-95a3-681f42187a46 rd_NO_LUKS rd_NO_LVM LANG=en_US.UTF-8 rd_NO_MD console=ttyS0,115200 crashkernel=auto SYSFONT=latarcyrheb-sun16  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM
---
>    kernel /boot/vmlinuz-2.6.32-573.18.1.el6.x86_64 ro root=UUID=67a96f57-8e2a-4d6f-b47d-2128b558cc58 rd_NO_LUKS rd_NO_LVM LANG=en_US.UTF-8 rd_NO_MD console=ttyS0,115200 crashkernel=auto SYSFONT=latarcyrheb-sun16  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM
  • xfs

UUID指定部分を書き換えます。 CentOS7 では grub2 が導入済みのようなので /etc/grub/ ではなく、 /etc/grub2/ が使用されます。

[root@ip-172-30-0-149 /]# blkid | grep xvdg
/dev/xvdg1: UUID="5a413a36-b3cd-4902-b18f-3887282d32b7" TYPE="xfs"
[root@ip-172-30-0-149 /]# cp -ip /etc/fstab /etc/fstab.org
[root@ip-172-30-0-149 /]# vi /etc/fstab
[root@ip-172-30-0-149 /]# diff /etc/fstab.org /etc/fstab
9c9
< UUID=ef6ba050-6cdc-416a-9380-c14304d0d206 /                       xfs     defaults        0 0
---
> UUID=5a413a36-b3cd-4902-b18f-3887282d32b7 /                       xfs     defaults        0 0

7) アンマウント

# chrootを終了
[root@ip-172-30-0-241 /]# exit
exit
# アンマウント
[root@ip-172-30-0-241 old]# cd /
[root@ip-172-30-0-241 /]# umount /mnt/old/
[root@ip-172-30-0-241 /]# umount /mnt/new/proc /mnt/new/sys /mnt/new/dev
[root@ip-172-30-0-241 /]# umount /mnt/new/
[root@ip-172-30-0-241 /]# mount -l | grep "old\|new"

ディスク内容の移行が完了したら、 作業用インスタンスから Volume をデタッチして後続作業を行います。

Nagios 通知有効/無効を curl で切替

f:id:htnosm:20161107012121p:plain

Nagios で通知(Notifications)の有効/無効切替を curl で実行します。

_NAGIOS="nagios.example.com"
_NAGIOS_USER="nagiosadmin"
_NAGIOS_PASS="nagiosadmin"
_HOST="localhost"

## 無効化
# Disable notifications for this host
curl -s -S http://${_NAGIOS}/nagios/cgi-bin/cmd.cgi -u ${_NAGIOS_USER}:${_NAGIOS_PASS} -d "cmd_mod=2&cmd_typ=25&host=${_HOST}" \
| grep 'Message'

# Disable notifications for all services on this host
curl -s -S http://${_NAGIOS}/nagios/cgi-bin/cmd.cgi -u ${_NAGIOS_USER}:${_NAGIOS_PASS} -d "cmd_mod=2&cmd_typ=29&host=${_HOST}" \
| grep 'Message'

## 有効化
# Enable notifications for this host
#curl -s -S http://${_NAGIOS}/nagios/cgi-bin/cmd.cgi -u ${_NAGIOS_USER}:${_NAGIOS_PASS} -d "cmd_mod=2&cmd_typ=24&host=${_HOST}" | grep 'Message'

# Enable notifications for all services on this host
#curl -s -S http://${_NAGIOS}/nagios/cgi-bin/cmd.cgi -u ${_NAGIOS_USER}:${_NAGIOS_PASS} -d "cmd_mod=2&cmd_typ=28&host=${_HOST}" | grep 'Message'

経緯

WebUI での設定は対象台数が増加すると少々厳しくなります。 また、コマンドファイルへコマンドを渡すことでも実行可能ですが、nagiosサーバへのログインを行わずに外部から切替を行いたいと思いました。
NagiosAPI は無いようですが、WebUI から実行できるのだから、curl でも実行できるだろうという所です。
実行内容は WebUI で Enable|Disable notifications 〜 のリンクをクリックした場合と同等です。

f:id:htnosm:20161107012122p:plain

環境

WebUI で同様の操作が実行できることが前提条件です。
外部コマンド で実現しており、その有効か設定が必要です。Version 4.0.8 ではインストール時点で有効化されていました。

設定値としては check_external_commands を有効にします。 command_check_interval は明記しない場合、デフォルト 60 秒です。

check_external_commands=1
command_file=/var/spool/nagios/cmd/nagios.cmd

パラメータ

cmd_mod

"2" を指定で WebUI 上の Commit をクリックした状態になります。 言い換えると指定しないと実行されません。

cmd_typ

コマンドです。 外部コマンドIDにそれぞれ番号が割り振りされており、その番号をしていします。 WebUI 上のそれぞれのコマンドのリンクから確認できます。
通知有効無効に関わる cmd_typ は以下になります。

cmd_typ Command ID Link Name
24 ENABLE_HOST_NOTIFICATIONS Enable notifications for this host
25 DISABLE_HOST_NOTIFICATIONS Disable notifications for this host
28 ENABLE_HOST_SVC_NOTIFICATIONS Enable notifications for all services on this host
29 DISABLE_HOST_SVC_NOTIFICATIONS Disable notifications for all services on this host

host

Nagios 上で定義済みの一意な host_name です。

返却値

WebUI での実行同様の HTML が返却されます。

f:id:htnosm:20161107012123p:plain

成功すると infoMessage として以下が返されます。

Your command request was successfully submitted to Nagios for processing.

失敗した場合 errorMessage として返されます。

例) 認証失敗
Sorry, but you are not authorized to commit the specified command.
例) nagios プロセス未起動
Error: Could not stat() command file


通常はダウンタイム設定で行いたい所ですが、メンテナンス作業等で再開時間が定まらない場合などで有用と思います。

Mackerel事始め AWSインテグレーション タグ絞り込み小ネタ

AWSインテグレーションのタグ絞り込み設定の小ネタです。
適切なタグを付与して使用していれば、いずれも引っ掛かる事は無いと思います。

: を含むタグ

CloudFormation でインスタンスを起動した場合や、 Auto Scaling Group 配下のインスタンスに自動的に付与されるタグに : が含まれています。

f:id:htnosm:20161105045853p:plain

そのタグを絞り込みに使用したい場合はダブルクォーテーションで囲みます。

f:id:htnosm:20161105045854p:plain

250文字数制限

タグ、除外タグともに250文字が上限です。 超過すると too long と怒られます。 上記の自動付与される長いタグ名などを使用しているとあっさり超えます。

f:id:htnosm:20161105045855p:plain

Mackerel事始め mkr(4) dashboards

f:id:htnosm:20161012181150p:plain

mkr v0.11.1 で dashboardsサブコマンドが追加されています。

公式に詳細説明があります。

が、どういう風に見えるのかは載っていないので、説明にある内容でのダッシュボード表示を試してみます。

dashboards

mkr v0.11.1 以上で dashboards コマンドを使用できます。

$ mkr help | grep -A 1 -e VERSION -e dashboards
VERSION:
   0.11.3
--
     dashboards
     help, h     Shows a list of commands or help for one command

dashboards help

機能は今の所 generate のみです。
-p 付与で標準出力へ、オプション無しだと Mackerel への反映となります。 同一名称(url_path)のダッシュボードが既に存在する場合でも、上書きされます。

$ mkr dashboards generate -h
NAME:
    generate - Generate custom dashboard

USAGE:
    mkr generate

DESCRIPTION:
    A custom dashboard is registered from a yaml file.
    Requests "POST /api/v0/dashboards". See https://mackerel.io/ja/api-docs/entry/dashboards#create.


OPTIONS:
    --print, -p  markdown is output in standard output.

ホストグラフ指定

ホストとメトリックを指定するホストグラフの一覧

ホストグラフの例を表示します。 複数ホスト・メトリックを縦横に並べたダッシュボードの生成になります。 f:id:htnosm:20161012175529p:plain

個別グラフ指定

サービスメトリックや式によるグラフなどを個別に指定するグラフの一覧
- service_name のみの指定でサービスグラフ
- service_name, role_name の指定でロールグラフ
- host_id の指定でホストグラフ
- query の指定で式によるグラフ

ここで言うサービスグラフは サービスメトリック であり、サービス名指定でロールを跨いでの表示ができるわけでは無いようです。
個別グラフの例を表示します。手頃なメトリックがなかったので指定しているメトリックは変えています。 f:id:htnosm:20161012175530p:plain


yamlを定義しておくことで履歴管理、ある程度の使い回しができるようになります。 WebのUIでURLコピー&ペーストで作成するよりは作成しやすいかなと思いました。

Mackerel事始め AWSインテグレーション(EC2)

Mackerel の AWS インテグレーションに EC2 が追加されました。

とある環境で mackerel-agent のバージョンアップの機会があったので導入してみました。
AWSインテグレーションの導入は公式の手順で特に躓くこと無く導入できました。

導入時の注意点

適用する際の注意点としてはこの辺りかと思います。

  • mackerel-agent 未導入でも EC2 の監視が可能
  • mackerel-agent 0.34.0 以上 導入済みの場合、ホスト情報は統合される(重複カウントされない)
    • mackerel-agent 0.34.0 未満 は統合されない(重複カウントされる)
  • Free プランでは利用不可

取得内容の確認

当方環境では下記項目のグラフが表示されました。 ()内はメトリック名です。

  • CPU (custom.ec2.cpu)
  • CPU Credit Balance/Usage (custom.ec2.cpu_credit.*)
  • Disk IO Read/Write (custom.ec2.disk_io.*)
  • Disk OPS Read/Write (custom.ec2.disk_ops.*)
  • Network Packets (custom.ec2.network_packets.*)
  • Network Traffic (custom.ec2.network.*)
  • Status Check Failed (custom.ec2.status_check_failed.*)

取得間隔は 5分と記載がありますが、 EC2側で Detailed Monitoring が有効となっている場合は 1分のグラフ表示となっていました。

mackerel-agent 無し

  • mackerel-agent 未導入、もしくはバージョンが 0.34.0 未満の場合
    • mackerel-agent 導入済みホストとは別のホストとして登録される

f:id:htnosm:20160831064933p:plain

mackerel-agent 有り

  • mackerel-agent バージョン 0.34.0 以上が導入済みの場合
    • 同一ホストとして登録される

f:id:htnosm:20160831064956p:plain

重複登録後に統合を行いたい場合

重複登録された後に mackerel-agent のアップデートを行いましたが、 EC2上で mackerel-agent をアップデート だけでは反映されませんでした。
アップデート時に mackerel-agent の再起動が掛かっており、 コマンドで確認しても正常に更新されている状態です。

  • EC2 上の mackerel-agent Version
    • 最新版(0.34.0) へ更新済み
$ /usr/bin/mackerel-agent version
mackerel-agent version 0.34.0 (rev 694915a) [linux 386 go1.6.2]
  • Mackerel 上のホスト情報
    • 未更新(0.32.2) のまま

f:id:htnosm:20160902210154p:plain

解消方法

ホストIDファイルを削除し新規登録することで、 ホスト情報のMackerelエージェントのバージョンが最新版になり、 AWSインテグレーションでの取得情報との統合が成されました。

# ホストID削除
$ sudo rm /var/lib/mackerel-agent/id
# mackerel-agent 再起動
$ sudo /etc/init.d/mackerel-agent restart

尚、ホストの新規登録のため、既存情報への影響があります。

  • 既存のメトリックスは引き継がれない
  • 既存のホストは Poweroff となる
    • 退役忘れに注意

これまで別の方法で監視していた Status Check Failed が取得できるようになったのは嬉しいです。
mackerel-agent の更新で新規ホスト登録となる点は改善して欲しいと思います。

AWS EC2 インスタンスの復旧(Auto Recovery)設定の落とし穴

AWS EC2 インスタンスには、 Status Checked Failed 発生時に自動的に復旧を試みる機能があります。
設定自体は正しく行えているように見えるのに、いざ異常発生時に復旧に失敗するケースに当たりました。

Action failed. Encountered error calling EC2 recover. (401: AWS was not able to validate the provided access credentials)

復旧に失敗した RecoveryAlarm は IAM Role を使用して CloudFormation で作成していましたが、作成(設定)自体は正常に行えていました。
結論としてはドキュメントに記載があるのですが、IAM Role で復旧アラームを作成している場合、復旧アクションは動作しません。

IAM ロール (たとえば、Amazon EC2 インスタンスプロファイル) を使用している場合は、アラームアクションを使用してインスタンスを停止、終了、または再起動することはできません。

尚、復旧(recover)アクションと同様に、 停止(stop)、終了(terminate)、再起動(reboot) を実行するアラームを作成できますが、 復旧(recover)とその他のアクションでは使用される実行権限が異なります。 (アラーム作成時の権限が使用されるのは 復旧(revocer) の場合のみ)

IAM ロールに基づいて、アラームアクションを使用してインスタンスを停止、終了、または再起動する場合は、EC2ActionsAccess ロールしか使用できません。他の IAM ロールはサポートされていません。別の IAM ロールを使用している場合は、インスタンスを停止、終了、または再起動できません。

AWS Security Token Service (AWS STS) を用いて許可された一時的な認証情報を使用している場合は、アラームアクションを用いて Amazon EC2 インスタンスを復旧することはできません。

ドキュメントの内容だけではわかり難かったので、実際に復旧アクションの挙動を確認してみました。

IAM 準備

IAM Role と IAM User での比較を行います。

IAM Role / IAM User

IAM Role と IAM User を準備します。今回はいずれも PowerUserAccess を付与しています。

  • IAM Role

f:id:htnosm:20160816230225p:plain

  • IAM User

f:id:htnosm:20160816230226p:plain

aws configure

デフォルトで IAM Role を使用し、profile=cfn で IAM User を使用できるよう設定します。

$ aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                <not set>             None    None
access_key     ****************EFEA         iam-role
secret_key     ****************dE0o         iam-role
    region           ap-northeast-1      config-file    ~/.aws/config
$ cat ~/.aws/credentials
[default]
region = ap-northeast-1

[cfn]
aws_access_key_id = ****************VO2A
aws_secret_access_key = ************************************QPgI
region = ap-northeast-1

Cloud Formation テンプレート準備

EC2 + RecoveryAlarm のテンプレートを用意します。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Outputs": {
    "InstanceID" : {
      "Value" : { "Ref" : "Ec2Instance" }
    },
    "RecoveryAlarm" : {
      "Value" : { "Ref" : "RecoveryAlarm" }
    }
  },
  "Resources": {
    "Ec2Instance" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "ImageId" : "ami-374db956",
        "InstanceType" : "t2.micro",
        "BlockDeviceMappings" : [
          {
            "DeviceName": "/dev/xvda",
            "Ebs": {
              "VolumeType": "gp2",
              "VolumeSize": "8"
            }
          }
        ]
      }
    },
    "RecoveryAlarm": {
      "Type": "AWS::CloudWatch::Alarm",
      "Properties": {
        "Namespace": "AWS/EC2" ,
        "MetricName": "StatusCheckFailed",
        "Statistic": "Minimum",
        "Period": "60",
        "EvaluationPeriods": "5",
        "ComparisonOperator": "GreaterThanThreshold",
        "Threshold": "0",
        "AlarmActions": [ {"Fn::Join" : ["", ["arn:aws:automate:", { "Ref" : "AWS::Region" }, ":ec2:recover" ]]} ],
        "Dimensions": [{"Name": "InstanceId","Value": {"Ref": "Ec2Instance"}}]
      }
    }
  }
}

RecoveryAlarm の作成

IAM Role と IAM User それぞれで CreateStack を実行します。

# IAM Role
aws cloudformation create-stack \
  --stack-name ec2-recoveryalarm-role \
  --template-body file://ec2.json
# IAM User
aws cloudformation create-stack \
  --stack-name ec2-recoveryalarm-user \
  --template-body file://ec2.json \
  --profile cfn
  • IAM Role で作成したStack

f:id:htnosm:20160816230228p:plain

  • IAM User で作成したStack

f:id:htnosm:20160816230227p:plain

f:id:htnosm:20160816230229p:plain

  • 作成したRecoveryAlarm

f:id:htnosm:20160816230230p:plain

RecoveryAlarm の実行確認

作成した RecoveryAlarm の状態を ALARM に更新し、挙動を確認します。 (システムステータスチェックのエラーを意図的に起こすことはできません。)

IAM Role で作成した RecoveryAlarm

# IAM Role で作成したRecoveryAlarm
$ aws cloudwatch set-alarm-state \
  --alarm-name "ec2-recoveryalarm-role-RecoveryAlarm-1SXN4O9QLTKH" \
  --state-value ALARM \
  --state-reason "Manual Update to ALARM"

401 エラーが発生し、Action が失敗します。

f:id:htnosm:20160816230231p:plain

IAM User で作成した RecoveryAlarm

# IAM User で作成したRecoveryAlarm
$ aws cloudwatch set-alarm-state \
  --alarm-name "ec2-recoveryalarm-user-RecoveryAlarm-1HG4YDX2YZ90H" \
  --state-value ALARM \
  --state-reason "Manual Update to ALARM"

IAM User で作成した場合は問題無く、 Action が正常に終了します。

f:id:htnosm:20160816230232p:plain

RecoveryAlarm を再作成

IAM Role で作成し、401エラーとなった RecoveryAlarm を IAM User からの実行で作成し直します。

$ diff ec2.json ec2_update.json
30a31
>         "AlarmName": "ec2-recoveryalarm-role-RecoveryAlarm-renew",
$ aws cloudformation update-stack --stack-name ec2-recoveryalarm-role --template-body file://ec2_update.json --profile cfn
  • RecoveryAlarm の再作成(Delete/Update)

f:id:htnosm:20160816230233p:plain

IAM User で "再作成" した RecoveryAlarm

作成し直した RecoveryAlarm の実行確認をします。

$ aws cloudwatch set-alarm-state \
  --alarm-name "ec2-recoveryalarm-role-RecoveryAlarm-renew" \
  --state-value ALARM \
  --state-reason "Manual Update to ALARM"

IAM User で再作成した場合も、Action が正常に終了しました。

f:id:htnosm:20160816230234p:plain



マネジメントコンソール上と、 describe-alarms で RecoveryAlarm を確認してみましたが、どの権限(IAM)で作成されたかの情報は保持していませんでした。
RecoveryAlarm を作成した際は実行確認を行い、権限エラーが発生しない事を確認しておいた方が良さそうです。

Mackerel事始め 将来予測機能

Mackerel の将来予測機能を試してみます。

f:id:htnosm:20160808180208p:plain

機能としては式による監視で特定の関数(線形回帰)を使って実現するようです。

将来予測機能

式による監視の仕様

公式サイトの記載より

  • 監視間隔は5分ごと
  • 設定可能な式は、グラフの系列が1本になるものだけ
  • 式が複雑過ぎる等で値が取れない場合はステータス Unknown としてアラートが発生
  • 監視項目上限数20
  • Free プランでは使用不可

線形回帰関数

使用できる関数は カスタマイズしたグラフを表示する - Mackerel ヘルプ で確認できます。

  • linearRegression(metrics, duration)
    • 現在時刻から duration 前 までのメトリック値をもとに線形回帰したメトリックを返します
  • timeLeftForecast(metrics, duration, threshold)
    • 現在時刻から duration 前 までのメトリック値をもとに線形回帰した値が threshold になるまでの残り秒数を返します

公式にある例では timeLeftForecast を使用しており、日数で閾値設定するため scale 関数を使用しています。

  • scale(metrics, factor)
    • 定数倍したメトリックを返します

1ホストのメモリ使用量について設定例

あまり良い例(グラフ)が無かったのですが、memory.usedに対して設定をしてみます。

対象グラフ

監視対象となる あるホストの1ヶ月のメモリ使用量のグラフ は以下になります。

f:id:htnosm:20160808180202p:plain

監視設定

1ヶ月分のメトリックを使用して、メモリ使用量が 1.5GB となる日数を取得する式を設定してみます。

scale(timeLeftForecast(host('2KcXXXXXXXX', 'memory.used'),'1mo', 1500000000), 1/86400)

12.81日で到達するという予測となりました。

f:id:htnosm:20160808180203p:plain

Excelの線形予測結果との比較

同metricsから値を取得し、Excelで線形予測を出した結果が以下になります。
取得したDatapoint、丸めなどで誤差が出てますが、概ね同じような予測となりました。

f:id:htnosm:20160808180204p:plain

複数ホスト(role)に対しての設定例

hostIdに対しての設定だけだとホストが増えてきた場合に設定が追いつかなくなるので、role に対して設定します。
グラフの系列を1本だけにする という点に注意すれば他は1ホストの時と同じです。

f:id:htnosm:20160808180205p:plain

ディスク使用量の予測設定例

予測機能の用途としては、やはり公式に記載されているディスク使用量に使用する事が多くなりそうです。

  • 公式記載の式の例
scale(timeLeftForecast(host('host_id', 'filesystem.drive.used'),'3mo', 2000000000000), 1/86400)

式の例

  • 特定ホスト/ドライブを対象
    • hostId = 2KcXXXXXXXX 、 ドライブ = xvda3 の場合
host('2KcXXXXXXXX', 'filesystem.xvda3.used')
  • 特定ホスト/全ドライブを対象
    • ホストに紐付く全ドライブの中の最大値
    • ドライブ指定を「*
max(host('2KcXXXXXXXX', 'filesystem.*.used'))
  • 特定ロール/全ドライブを対象
    • ロールに紐付く全ドライブの中の最大値
    • role = 'service:role' の場合
    • ドライブ指定を「*
max(role('service:role', 'filesystem.*.used'))

パーセンテージ設定

上記例だと絶対値で閾値設定を行う必要があるため、異なるディスクサイズの場合にそれぞれ設定する必要が出てきます。 となるとやはりパーセンテージで設定したくなるので、式を調整します。

  • 対象ドライブ構成

f:id:htnosm:20160808180207p:plain

ドライブ毎にディスクサイズが異なるので、ドライブ毎に設定が必要になるかと思います。 ロール内のディスク構成は同一である場合は以下のように定義可能です。

  • ディスク使用率取得例
divide(
   max(role('service:role','filesystem.xvda3.used'))
  ,max(role('service:role','filesystem.xvda3.size'))
)
  • ディスク使用率の将来予測例
    • 使用率が100%に到達する日数
scale(timeLeftForecast(divide(
   max(role('service:role','filesystem.xvda3.used'))
  ,max(role('service:role','filesystem.xvda3.size'))
),'1mo',1), 1/86400)

通知例

通知メッセージには式の内容がそのまま入ります。 (少々見難いので、将来的には通知メッセージをカスタマイズできるようになると嬉しいですね。)

f:id:htnosm:20160808180206p:plain


通常の閾値設定によるアラート発生前に傾向を掴めるというのは中々面白いのではないでしょうか。
長期間のメトリクスを対象にした設定を行えていないので、ディスク使用量の予測設定をして観察してみたいと思います。