Amazon EC2(Linux)のswap領域ベストプラクティス

AWS
440件のシェア(そこそこ話題の記事)

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ども、大瀧です。
Linuxのメモリ管理で欠かせないのがswapですよね。EC2のディスク管理には独特な仕様があるのでその辺りを踏まえつつ、EC2(Linux)でどのようにswap領域を用意するのが良いのかまとめてみたいと思います。 理屈をこねながらの解説になるので、先に本エントリーの結論を。

  1. 公式AMIにはswap領域が含まれない
  2. m1.small、c1.mediumには900MBのスワップ領域がおまけでついてくる(Amazon Linux AMIであれば、Cloud-initによって自動マウントされる)
  3. swap領域には、インスタンスストア(別名Ephemeral Disk)を使うべし

では、それぞれ見ていきます。

公式Linux AMIの初期構成にはswapパーティションが無い

Amazonから提供される公式のLinux AMIのブートディスクには、swapパーティションが含まれていません。以下にAmazon Linuxでのfdiskコマンドの結果を示します。

[ec2-user@ip-XX-XX-XX-XX ~] $ sudo fdisk -l /dev/sda1

ディスク /dev/sda1: 8589 MB, 8589934592 バイト
ヘッド 255, セクタ 63, シリンダ 1044
Units = シリンダ数 of 16065 * 512 = 8225280 バイト
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O size (minimum/optimal): 512 bytes / 512 bytes
ディスク識別子: 0x00000000

[ec2-user@ip-XX-XX-XX-XX ~] $

一般の物理マシンではブートディスクの末尾付近にswapパーティションを切ることが多いですが、EC2ではEBSのディスク拡張でルートパーティションが拡張できるよう、ブートディスクにはパーティションテーブルが意図して作成されないことが推測できます。また、EC2はインスタンスタイプを変更することでメモリサイズが変化するため、swapパーティションのサイズを事前に決めることが難しいというのも背景としてありそうです。

というわけで、そのままでは万が一メモリフルになるとOOM Killerさんが早々に大ナタを振るうことになってしまいます。何らかの方法でswap領域を確保しましょう。

EC2インスタンスストアスワップボリューム

swap領域を確保する一番手っ取り早い方法として、EC2のインスタンスストアスワップボリュームがあります。これは、EC2のインスタンスタイプがm1.smallとc1.mediumのときのみ/dev/xvda3ないし/dev/xvde3という900MBのmkswap済みディスクがEC2のブート時に自動で提供されます(デバイス名はAMIによりまちまちです)。他のインタンスタイプではm1/c1ファミリーであっても提供されず、またManagement ConsoleやAWS APIでは本ボリュームを確認できないことに注意しましょう。

コメントでmacotomiiさんからご指摘いただいた通り、AWS Marketplaceで提供されるCentOS AMIでは現状インスタンスストアスワップボリュームが検出されないようです。

Amazon Linuxであれば、前回のエントリーでも触れたcloud-initというツールによってこのswap領域をマウントするエントリーが/etc/fstabに追記され、自動でswaponされます。Amazon Linuxでのfreeコマンドの結果を以下に示します。

m1.smallの場合

[ec2-user@ip-XX-XX-XX-XX ~]$ free
             total       used       free     shared    buffers     cached
Mem:       1696516      94700    1601816          0       7000      41032
-/+ buffers/cache:      46668    1649848
Swap:       917500          0     917500

c1.mediumの場合

[ec2-user@ip-XX-XX-XX-XX ~]$ free
             total       used       free     shared    buffers     cached
Mem:       1737192     116856    1620336          0       7084      60808
-/+ buffers/cache:      48964    1688228
Swap:       917500          0     917500

m1.mediumの場合(スワップボリュームの無いタイプ)

[ec2-user@ip-XX-XX-XX-XX ~]$ free
             total       used       free     shared    buffers     cached
Mem:       3844408     105340    3739068          0       7000      43120
-/+ buffers/cache:      55220    3789188
Swap:            0          0          0

c1.xlargeの場合(スワップボリュームの無いタイプ)

[ec2-user@ip-XX-XX-XX-XX ~]$ free
             total       used       free     shared    buffers     cached
Mem:       7118660     149228    6969432          0       7108      60844
-/+ buffers/cache:      81276    7037384
Swap:            0          0          0

自動で使え、かつ無料なので便利ではあるのですが、スケールアップ/ダウンなどでインスタンスタイプを変更すると使えなくなってしまうので、おまけで使えればラッキー☆くらいに見ておくのが良いでしょう。「本番環境の運用時に本ボリュームのないタイプに変更したら急にOOM Killerが発動するようになってしまった」など、ハマる可能性大です。最近は、m3やc3から始まる新しい世代のインスタンスファミリーが出てきており、コストパフォーマンスや性能向上を狙ってインスタンスファミリーを移行(m1→m3、c1→c3)しようとして同様にハマることもありそうです。

swapファイルのススメ

スワップのためにボリュームやパーティーションを追加するのは、EBSがボリュームサイズで課金されることからコスト面で敷居が高いです。そのため、既存ボリュームにswapファイルを追加のがお奨めです。
作成するべきスワップサイズは先人の知恵を拝借しつつ、可能であればインスタンスタイプ毎にスワップファイルサイズを調整できるよう、EC2の起動時にswapファイルを作成、マウントするといいですね。/etc/rc.localやcloud-initの力を借りると良さそうです。以下に例を示します。

/etc/rc.localに追記する例

SWAPFILENAME=/swap.img
MEMSIZE=`cat /proc/meminfo | grep MemTotal | awk '{print $2}'`

if [ $MEMSIZE -lt 2097152 ]; then
  SIZE=${((MEMSIZE * 2))}k
elif [ $MEMSIZE -lt 8388608 ]; then
  SIZE=${MEMSIZE}k
elif [ $MEMSIZE -lt 67108864 ]; then
  SIZE=${((MEMSIZE / 2))}k
else
  SIZE=4194304k
fi

fallocate -l $SIZE $SWAPFILENAME && mkswap $SWAPFILENAME && swapon $SWAPFILENAME

ちょっと工夫したところは、swap用のイメージファイルの作成にddコマンドではなくfallocateコマンドを使っている点です。従来のswapファイルであれば一度作るだけなので実行に時間のかかるddコマンドでも問題なかったのですが、今回はインスタンス起動時に毎回実行するので、より時間のかからない方法にしました。ddコマンドと異なりI/Oも最小限なので、I/O単位で課金が発生するEBSでもお財布に優しいですよね。

インスタンスストアの活用

swapファイルを作成するボリュームは、特に種類を問いません。しかし、EBSだとI/O単位で課金が発生するためスワップアウト/インが頻発する状況だとディスクI/Oの課金がかさんでしまう可能性があります。

そこで、swapファイルはインスタンスストア(通称 : Ephemeral Disk)に作成することをオススメします。インスタンスストアは無料で使えるローカルボリュームで、インスタンスタイプ毎に本数とサイズが決まっています。以下の注意点を見つつ、うまく使いましょう。

  • AMIで事前に設定されていない場合、インスタンス起動時に追加しなければならない
  • インスタンスをStopするタイミングで内容が全て消えるので、インスタンス起動時に毎回swapファイルを作成しなければならない

上記スクリプトのfallocateコマンドはext4ファイルシステムに依存する機能を利用しており、インスタンスストアの既定のファイルシステムがext3のためインスタンスストア上にswapファイルを作成しようとすると、エラーになります。fallocateコマンドを使うためには、起動処理でのマウント(=cloud-initサービスのstart処理)の前にインスタンスストアのファイルシステムをext4に変更する必要があります。これは/etc/rc.localでは対応できないので、独自サービスを定義したパッケージを作成してみました。こちらの記事をどうぞ!

まとめ

というわけで、冒頭の要点を改めて示します。

  1. 公式AMIにはswap領域が含まれない
  2. m1.small、c1.mediumには900MBのスワップ領域がおまけでついてくる(Amazon Linux AMIであれば、Cloud-initによって自動マウントされる)
  3. swap領域には、インスタンスストア(別名Ephemeral Disk)を使うべし

最近はメモリに余裕を持ったシステム構成が多いと思いますが、メモリ管理の予防線としてswapの構成もしっかり検討しましょう!

参考資料

  • macotomii

    m1.smallとc1.mediumの件、CentOSの公式イメージだと、ルートデバイスが/dev/xvda1でなく/dev/xvdaのせいで、認識できなかったはずです(少なくとも6.3の公式ではそうでした)。
    grubとfstab書き換えてAMI化しておけば、自動認識できるようになりましたが。

    • takipone

      確かに、MarketplaceのCentOS AMIでは認識できないようですね、追記しました。コメントありがとうございました!

  • Masahiro Otsuka

    Ubuntu 14LTSで試しましたがうまく行きませんでした。
    エラーが出たのは、swaponの箇所です。
    swapon failed: Invalid argument
    と出ました。
    XFSファイルシステムで試してみました。

    時間のあるときにでも調べてみたいと思いますが、どなた解決方法ご存知であれば教えてください。