ちょっと話題の記事

Amazon Linux 2のswapファイル作成にfallocate、ダメ、ゼッタイ

2018.02.04

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

今回、Amazon Linux 2環境でrootボリューム上にswap領域を作る際に、従来のベスト・プラクティスで利用しているfallocateswapon時にエラーが起きてしまうことを確認したのでシェアします。情報としては古い話をひっぱり出していますが、今後、LTS(Long-Term Support)ビルドがリリースされれば、Amazon Linux 2を利用されるユーザーも多くなると思いますのでブログに書いた次第です。

動作確認環境

Amazon Linux 2は現在、RC(Release Candidate)なので、LTSビルドでは動作が変わる可能性もあります。

  • AMI ID:ami-c2680fa4(Amazon Linux 2 LTS Candidate AMI 2017.12.0 (HVM), SSD Volume Type)
  • Kernel:4.9.76-38.79.amzn2.x86_64

Amazon Linux 2でfallocateを使うと、どうなる?

まず、従来どおりにfallocateでswapファイルを作成した場合、どんなエラーになるか、あえて踏んでみましょう。

$ sudo fallocate -l 3855176K /swap.img
$ sudo mkswap /swap.img
スワップ空間バージョン1を設定します、サイズ = 3855172 KiB
ラベルはありません, UUID=6dcdf231-55f7-4601-b5f2-b12b9252b366
$ sudo chmod 600 /swap.img
$ sudo swapon /swap.img
swapon: /swap.img swaponは失敗しました: Invalid argument

と、こうなります。 /var/log/messagesを見るとswapfile has holes(スパースファイル)と判断されているのが問題のようです。

$ sudo tail /var/log/messages
 :
Jan 27 04:37:46 localhost kernel: swapon: swapfile has holes

dufilefragでスパースの具合を見てみました。

$ du /swap.img
3855176	/swap.img
$ filefrag -v /swap.img
Filesystem type is: 58465342
File size of /swap.img is 3947700224 (963794 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..  454752:      69406..    524158: 454753:             unwritten
   1:   454753..  963793:    2096653..   2605693: 509041:     524159: unwritten,eof

出力結果を見るかぎり、論理的なディスク領域は確保されており、スパースファイルにはなっていないように見えますが、swaponは有効なファイルとして判断してくれないようです。Bugzillaの情報によれば

the problem with fallocate(1) or truncate(1) is that it uses filesystem ioctls to make the allocation fast and effective, the disadvantage is that it does not physically allocate the space -- but swapon(2) syscall requires a real space.

sawaponは実スペースを要求するのでfallocateのように実スペースを持っていないファイルはダメ!ということのようですが、EXT4では作成できていたので、なんで?という疑問は残りますが、これ以上深く掘るのは専門の方におまかせします。。

問題はわかった。じゃぁ、どーすりゃいいのよ?ということですね。

ddでやるっきゃない!

先述のとおりfallocateはswapファイルとして利用できないため、ddで対応します。 また、これまでは/etc/rc.localにて、毎起動時にswapファイルの作成処理を仕込んでいましたが、Amazon Linux 2からsystemdが採用されたことにより、デフォルトで/etc/rc.localは起動しなくなっています。解決方法は/etc/rc.d/rc.localのコメントに書いてました。

/etc/rc.local

#!/bin/bash
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
#
# It is highly advisable to create own systemd services or udev rules
# to run scripts during boot instead of using this file.
#
# In contrast to previous versions due to parallel execution during boot
# this script will NOT be run after all other services.
#
# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure
# that this script will be executed during boot.

下2行を見ると、「/etc/rc.d/rc.localに実行権を付与すれば動くよ」と記載されていますが、それより先に「systemdサービスか、udevルールを作成してrunさせるのがイチ推しっす!」というコメントが目に入ってしまうので、今回は起動時スクリプトをsystemdサービスとして作成します。 起動時に実行する方法として、他にもユーザーデータが考えられますが、並列処理と依存関係が設定できる点でsystemdはなかなか良いと思いました。Amazon Linux 2のLTSビルドがリリースされれば、利用機会も増えることは間違いないので、systemdの仕組みについても、じっくり勉強しておきたいですね。

起動時スクリプトを拝借してdd対応に書き換える

起動時スクリプトは先人の知恵を拝借しまして、以下のようにdd対応させました。論理的なディスク領域を確保するfallocateとは違い、物理的なディスク領域を確保するddは遅いです。。そのままだと毎起動時にddのファイル作成待ちによって起動時間が長くなってしまうので、既にswapファイルがある場合はサイズを比較し、メモリサイズと異なる場合は、インスタンスタイプが変更されたものとして、ddで新たなメモリサイズに適したサイズでswapファイルを再作成。メモリサイズと同サイズの場合は、dd処理をスキップするようにしました。今回はインスタンスタイプの変更などにも対応できるように、このような対応をとっていますが、もちろんswapファイルの再作成が不要であれば、/etc/fstabに書いてしまうのが良いと思います。

/usr/local/bin/custom_mkswap.sh

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

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

if [ -e $SWAPFILENAME ] ; then
  SWAPSIZE=`du $SWAPFILENAME | awk '{print $1}'`
else
  SWAPSIZE=0
fi

if [ $SIZE -ne $SWAPSIZE ]; then
  dd if=/dev/zero of=$SWAPFILENAME count=$SIZE bs=1K && chmod 600 $SWAPFILENAME && mkswap $SWAPFILENAME && swapon $SWAPFILENAME
else
  swapon $SWAPFILENAME
fi

作成しましたら、実行権を与えておきます。

$ sudo chmod 744 /usr/local/bin/custom_mkswap.sh

起動時スクリプト用のUnitファイルを作成

次に起動時スクリプトをsystemdで管理するため、/etc/systemd/system以下にServiceタイプでオリジナルのUnitファイルを作成し、さきほどのcustom_mkswap.shスクリプトを実行するように設定します。

/etc/systemd/system/custom_mkswap.service

[Unit]
Description=Make swapfile and swapon on boot.
After=local-fs.target
RequiresMountsFor=/

[Service]
RemainAfterExit=true
Type=oneshot
ExecStart=/usr/local/bin/custom_mkswap.sh

[Install]
WantedBy=local-fs.target

作成しましたら、systemctlcustom_mkswap.serviceを有効化します。

$ sudo systemctl enable custom_mkswap.service
Created symlink from /etc/systemd/system/local-fs.target.wants/custom_mkswap.service to /etc/systemd/system/custom_mkswap.service.
$ systemctl list-unit-files --type=service
 :
custom_mkswap.service                         enabled
 :

有効化されたことを確認して、再起動します。

再起動後の確認

再起動後、swapが有効になっていることを確認します。

 $ free
              total        used        free      shared  buff/cache   available
Mem:        3855176       74388     3619320       16624      161468     3604308
Swap:       3855172           0     3855172

systemctlsystemdにより実行されたログを見ることができますので、見てみます。

 systemctl status custom_mkswap.service -l
● custom_mkswap.service - Make swapfile and swapon on boot.
   Loaded: loaded (/etc/systemd/system/custom_mkswap.service; enabled; vendor preset: disabled)
   Active: active (exited) since 土 2018-01-27 14:28:13 UTC; 10min ago
  Process: 2677 ExecStart=/usr/local/bin/custom_mkswap.sh (code=exited, status=0/SUCCESS)
 Main PID: 2677 (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/custom_mkswap.service

 1月 27 14:28:13 ip-10-0-0-190.ap-northeast-1.compute.internal systemd[1]: Started Make swapfile and swapon on boot..
 1月 27 14:28:13 ip-10-0-0-190.ap-northeast-1.compute.internal systemd[1]: Starting Make swapfile and swapon on boot....

問題なく動作していることが確認できました!

インスタンスストアの場合、どうすんの?

ちなみに、インスタンスストアにswapファイルを作成する場合は、Amazon Linuxと同じように以下の記事が利用できました。

『Amazon EC2(Linux)のswap自動作成を行うRPMパッケージ ec2-swap を作ってみた』

1点だけ注意しておくポイントは、インスタンスストアのマウントポイントが/mediaから/mntに変更されていますので、デフォルトのマウントポイントのまま使用する場合は、設定ファイル/etc/sysconfig/ec2-swapINSTANCEVOL_MOUNTPOINTを以下のように変更してください。

$ sudo vi /etc/sysconfig/ec2-swap
INSTANCEVOL_VIRTNAME=ephemeral0
INSTANCEVOL_MOUNTPOINT=/mnt/$INSTANCEVOL_VIRTNAME
SWAPFILENAME=$INSTANCEVOL_MOUNTPOINT/swap.img

そもそもswap設定いるんか問題

パフォーマンスに大きな影響を与えるため、swapをアテにしたような運用は避けるべきだと思います。十分なメモリサイズを持たせ、swapにこぼれないようなインスタンスサイズを選択するほうが良いでしょう。しかしながら、「一時的な負荷のためにサイズアップはしたくない」「スパイク的なメモリ消費でプロセスを落としたくない」など、swapで対応したいケースはあると思います。

さいごに

今回、fallocateのエラーを調べるなかで、ベストプラクティス記事が大変多くの方に参照いただいてることが判りました。私もDevelopers.IOの一員として、誰かのお役にたてるような記事をバシバシ書けるように、日々精進していきたいと思います!今後とも、よろしくお願いします!

以上、丸毛でした。