今回は、前回のEFSを使ったパターンからEFS関連のリソースを除いて、単一のEC2内に構築したNFSサーバーを使うテンプレートを作成しました。
- ロードバランサーとは関係ないとこのEC2インスタンス1台にNFSサーバーを構築
- ロードバランサーによってアクセスが分散されるn台のEC2インスタンスはNFSクライアントになる
- 起動時のスクリプト内でNFSサーバーをマウント
EFS版とのテンプレートの変更箇所抜粋
すべての変更内容やテンプレートの全文は以下のGitHubで確認できます。
GitHub:aws-cloudformation-templates/wordpress_nfs.json at d73d9042b2468d03c0f416536a80057f53cebaff
NFSサーバー用のパラメータを追加しました。
"Parameters": { "WordPressInstallDirectoryName": { "Description": "enter WordPress install directory name. If not entered, it will be installed directly under /var/www/html", "Type": "String", "Default": "wordpress" }, + "NfsServerVolumeSize": { + "Description": "input NFSServer volume size(min:8GB, max:1024GB)", + "Type": "Number", + "MinValue": "8", + "MaxValue": "1024", + "Default": "8" + } },
マウントコマンド組み立ててるところ。
EFS版はElasticFileSystemリソースの戻り値をセットしてるだけ。
NFS版は/etc/fstabにマウントポイントの情報を書き込んでから、mount -aでマウントしています。
"mount -t efs ", { "Ref": "ElasticFileSystem" }, ":/ /var/www/html/", { "Fn::If": [ "WordPressInstallDirectoryNameIsEmpty", "", { "Fn::Join": [ "", [ { "Ref": "WordPressInstallDirectoryName" }, "/" ] ] } ] }, "wp-content/uploads\n",
"echo '", { "Fn::GetAtt": [ "NFSServer", "PrivateIp" ] }, ":/mnt/data /var/www/html/", { "Fn::If": [ "WordPressInstallDirectoryNameIsEmpty", "", { "Fn::Join": [ "", [ { "Ref": "WordPressInstallDirectoryName" }, "/" ] ] } ] }, "wp-content/uploads nfs4 defaults 0 0' >> /etc/fstab\n", "mount -a\n",
NFSサーバーを立てるEC2インスタンスリソースのテンプレートの抜粋です。
AWS::EC2::SecurityGroup、AWS::EC2::Instanceの2リソースを作成しています。
サーバーのスペックやボリュームタイプなんかは固定にしておきました。
ルートボリュームとは別に/dev/xvdfにボリュームをアタッチしています。
nfsは通信に2049ポートを使用するので固定で開放しています。
"NFSSecurityGroup": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "SecurityGroup created by CloudFormation", "SecurityGroupIngress": [ { "IpProtocol": "tcp", "FromPort": "2049", "ToPort": "2049", "SourceSecurityGroupId": { "Ref": "WebServerSecurityGroup" } }, { "IpProtocol": "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": { "Ref": "AccessAllowIP" } } ], "VpcId": { "Ref": "NewVPC" }, "GroupName": { "Fn::Sub": "${ProjectPrefix}-sg-nfs" }, "Tags": [ { "Key": "Name", "Value": { "Fn::Sub": "${ProjectPrefix}-sg-nfs" } } ] } }
"NFSServer": { "Type": "AWS::EC2::Instance", "Metadata": { "AWS::CloudFormation::Init": { ~~~省略~~~ "install_nfs_server": { "files": { "/tmp/create-nfs-server": { "content": { "Fn::Join": [ "", [ "#!/bin/bash -xe\n", "\n", "# mount ebs.\n", "yum -y install xfsprogs\n", "mkfs -t xfs /dev/xvdf\n", "mkdir -p /mnt/data\n", "mount /dev/xvdf /mnt/data\n", "\n", "# setup nfs.\n", "echo '/mnt/data 172.31.0.0/16(rw,no_root_squash)' >> /etc/exports\n", "chkconfig nfs on\n", "/etc/init.d/nfs start\n", "# exportfs -ar\n" ] ] }, "mode": "000500", "owner": "root", "group": "root" } }, "services": { "sysvinit": { "nfs": { "enabled": "false", "ensureRunning": "false" } } } }, "configure_nfs_server": { "commands": { "01_configure_nfs_server": { "command": "/tmp/create-nfs-server", "cwd": "~" } } } } }, "Properties": { "BlockDeviceMappings": [ { "DeviceName": "/dev/xvda", "Ebs": { "DeleteOnTermination": "true", "VolumeSize": "8", "VolumeType": "gp2" } }, { "DeviceName": "/dev/xvdf", "Ebs": { "DeleteOnTermination": "true", "VolumeSize": { "Ref": "NfsServerVolumeSize" }, "VolumeType": "gp2" } } ], "ImageId": { "Fn::FindInMap": [ "AWSRegionArch2AMI", { "Ref": "AWS::Region" }, { "Fn::FindInMap": [ "AWSInstanceType2Arch", "t2.micro", "Arch" ] } ] }, "InstanceType": "t2.micro", "SubnetId": { "Ref": "PublicSubnetA" }, "SecurityGroupIds": [ { "Fn::GetAtt": [ "NFSSecurityGroup", "GroupId" ] } ], "KeyName": { "Ref": "KeyName" }, "UserData": { "Fn::Base64": { "Fn::Join": [ "", [ "#!/bin/bash -xe\n", "yum update -y aws-cfn-bootstrap\n", "/opt/aws/bin/cfn-init -v ", " --stack ", { "Ref": "AWS::StackName" }, " --resource NFSServer ", " --configsets nfs_server_setup ", " --region ", { "Ref": "AWS::Region" }, "\n", "/opt/aws/bin/cfn-signal -e $? ", " --stack ", { "Ref": "AWS::StackName" }, " --resource NFSServer ", " --region ", { "Ref": "AWS::Region" }, "\n" ] ] } }, "Tags": [ { "Key": "Name", "Value": { "Fn::Sub": "${ProjectPrefix}-nfs" } } ] }, "CreationPolicy": { "ResourceSignal": { "Timeout": "PT15M" } } }
スタックの構成図
前回よりEFSがなくなった分シンプルになるかと思ってましたがそんなことはありませんでした。
VPC、サブネットがスタックの構成に入ると枠がいろいろできちゃってぱっと見でよくわからない構成図になってしまいました。。
デザイナ画面で各リソースをぐりぐり動かしたりもできるのですが諦めました。
今回作成したテンプレートの問題点
今回作成したテンプレートではNFSサーバーはap-northeast-1a上に固定して存在するようになっています。
今年の8月23日に発生したAWSの大規模な障害のように特定のアベイラビリティゾーンに所属するインスタンスが使えなくなるようなときに、正常稼働している側でも共有しているファイルが参照できなくなってしまうことが考えられます。
CloudFrontを使って/wp-content/uploadsディレクトリのリクエストに対してのCDNを構築することで回避できそうな気がする。CloudFrontの設定自体が勉強不足でよくわかっていないので今回は省略(^q^
まとめ
今回のような構成はEFSの国内リージョンが存在しなかった頃(たぶん2~3年前)にたしか費用を抑えるという名目で使ったことがあります。
現在も正常稼働しているシステムですが8月の障害発生時にはもれなく落ちてしまってたような気がします。
EFSを組み込んだシステムの運用経験はゼロなので料金とか使い心地は正直よくわかりません;;
現在は東京リージョンでも使用でき、料金も思ってたより高くない?ような気がしなくもない??ですし、AWSが提供しているサービスがあるのであればそちらを使った方が良いのかなーと思います(^q^