AWS Storage Gateway の Amazon S3 File Gateway をプライベートサブネットに構築する
先日、EC2 に構成した Storage Gateway アプライアンスで障害が発生した場合を想定し、復旧(再構築)手順を確認する機会がありました。
今まであまり Storage Gateway に触れる機会がなかったため、改めて構築手順をまとめます。
AWS Storage Gateway の種類
Storage Gateway と一口に言っても種類と構成パターンが多岐にわたりますので、まずは簡単に整理します。
2023 年 9 月現在、以下 4 種類の Storage Gateway が提供されています。
- ファイルストレージ :S3 File Gateway、FSx File Gateway
- ブロックストレージ :Volume Gateway
- 仮想テープライブラリ:Tape Gateway
「S3 File Gateway」と「FSx File Gateway」は NFS もしくは SMB、「Volume Gateway」と「Tape Gateway」は iSCSI で、それぞれオンプレミスと S3 を接続します。
オンプレミスのデータを、バックアップやディザスタリカバリといった目的で S3 に保管することが主な用途となります。
「FSx File Gateway」だけは毛色が異なり、FSx for Windows File Server のキャッシュ用途として利用されます。
Storage Gateway アプライアンス
オンプレミス側のクライアント と AWS 側のストレージをつなぐ、Storage Gateway の実体を「Storage Gateway アプライアンス」と呼びます。
アプライアンスは、以下 3 通りの方法で構成することができます。
- オンプレミス側
- 仮想マシン(VMware ESXi、Microsoft Hyper-V、Linux KVM)
- ハードウェアアプライアンス
- AWS側
- EC2 インスタンス
クライアントとのレイテンシー短縮の観点から、仮想マシンやハードウェアアプライアンスを利用してオンプレミス側に配置することが推奨されています。
VMware ESXi については vSphere HA でフェイルオーバーが行えるものの、その他のアプライアンスについては冗長構成をとることはできません。
Storage Gateway そのものがバックアップ用途となるため、シビアに可用性を担保する必要はないといった理由もあるかと考えられます。
とは言え、障害が発生したら復旧させるのが世の常です。
今回は、こちらの Storage Gateway アプライアンスに障害が発生したことを想定し、ひととおりの構築手順を確認してみました。
今回の構成
前述のとおり、Storage Gateway は種類と構成パターンが多岐にわたりますが、今回は以下の構成です。
- オンプレミスのクライアント(Linux)から NFS 接続する Storage Gateway S3 File Gateway
-
Storage Gateway アプライアンスとして EC2 インスタンスを利用
-
Storage Gateway アプライアンス EC2 はプライベートサブネットに配置、パブリック IP アドレスは持たない
今回は、図の赤枠で囲ったコンポーネントを作成していきます。
1. Storage Gateway アプライアンス用 EC2 構築
利用中の Storage Gateway アプライアンス EC2 に障害が発生したことを想定し、再構築をしてみます。
こちらの Storage Gateway はコード管理されていませんでしたので、ひとまずマネジメントコンソールから作成です。
今回はオンプレミス拠点とのプライベート接続環境となるため、パブリック IP アドレスは付与しません。
したがって、[設定をカスタマイズ]を選択し、プライベートサブネットに EC2 を起動します。
2. VPC エンドポイント作成
プライベート接続の場合は VPC エンドポイントが必要です。
既に作成済みであれば既存の VPC エンドポイントを利用可能ですが、無い場合は以下の二種類を作成します。
- Storage Gateway 用 VPC エンドポイント(インターフェース型)
- S3 用 VPC エンドポイント(インターフェース型)
それぞれ、セキュリティグループで以下のとおりインバウンドトラフィックを許可します。
- Storage Gateway 用 VPC エンドポイントのセキュリティグループ
- Storage Gateway アプライアンスからの TCP ポート 443、1026、1027、1028、1031、2222
- S3 用 VPC エンドポイントのセキュリティグループ
- Storage Gateway アプライアンスからの TCP ポート 443
Amazon VPC エンドポイントを使用してゲートウェイをアクティブ化しようとすると、Storage Gateway のアクティブ化が失敗するのはなぜですか?
3. アクティベーションキー取得
今回はパブリック IP アドレスを付与しませんので、「アクティベーションキー」を取得する必要があります。
構築した EC2 インスタンスに HTTP 接続可能な環境を用意します。
構築した EC2 インスタンスのセキュリティグループで HTTP 接続が許可されていることを確認し、curl を実行してアクティベーションキーを取得します。
Getting an activation key for your gateway
上記の公式ドキュメントに従い、実際の値に置き換えて curl を実行します。
curl "http://***.***.***.***/?activationRegion=ap-northeast-1&vpcEndpoint=vpce-*********.storagegateway.ap-northeast-1.vpce.amazonaws.com&no_redirect"
4. キャッシュストレージ追加
無事アクティベーションキーが適用できましたら、Storage Gateway アプライアンス用 EC2 にキャッシュストレージを追加する必要があります。
マネジメントコンソールでは、以下のようにエラーが表示されますので、EBS で 150 GiB 分のボリュームを作成し、アタッチしましょう。
5. ファイル共有作成
デフォルトのクイック作成画面では S3 の VPC エンドポイントを指定してファイル共有を作成できないため、[設定をカスタマイズ] を選択し作成します。
ファイル共有のステータスが「利用可能」となりましたら、mount して S3 と接続できることを確認します。
$ sudo mount -t nfs -o nolock,hard 10.***.***.***:/<接続先S3バケット> /mnt $ ll /mnt/ total 1 drwxrwxrwx. 1 nobody nobody 0 Sep 5 00:55 tmp drwxrwxrwx. 1 nobody nobody 0 Sep 5 00:55 test drwxrwxrwx. 1 nobody nobody 0 Sep 5 00:55 test_new -rw-r--r--. 1 nobody nobody 13 Sep 3 12:26 test.txt
コードで管理
マネジメントコンソールで手順を振り返ってみましたが、素早く再構築するためには、やはりコードで管理するが吉です。
以下は、今回の環境を Terraform で import した場合のサンプルとなります。(Terraform v1.5.6 / provider aws v5.15.0)
あくまでサンプルとなりますので、実運用ではアクセス許可まわりなど厳密に設定する必要があります。
また、VPC、サブネット、S3 バケットについては省略しています。
ec2.tf
data "aws_ami" "sgw_ami" { most_recent = true owners = ["amazon"] filter { name = "name" values = ["aws-storage-gateway-*"] } } resource "aws_instance" "example" { ami = data.aws_ami.sgw_ami.id associate_public_ip_address = false availability_zone = "ap-northeast-1a" instance_type = "m5.xlarge" subnet_id = "<既存のサブネット ID>" tags = { Name = "terraform-sgw" } tags_all = { Name = "terraform-sgw" } vpc_security_group_ids = [aws_security_group.example_sgw_ec2.id] ebs_block_device { delete_on_termination = false device_name = "/dev/sdf" iops = 3000 tags = { Name = "terraform-sgw-cache" } throughput = 125 volume_size = 150 volume_type = "gp3" } root_block_device { delete_on_termination = true iops = 3000 throughput = 125 volume_size = 80 volume_type = "gp3" } }
sg.tf
# ---------------------------------------- # Security Group - storagegateway EC2 # ---------------------------------------- resource "aws_security_group" "example_sgw_ec2" { name = "sgw-ec2-security-group" vpc_id = "<既存のVPC ID>" } resource "aws_security_group_rule" "nfs_tcp" { type = "ingress" from_port = 2049 to_port = 2049 protocol = "tcp" description = "NFS-TCP" cidr_blocks = ["<既存のVPC CIDR>"] security_group_id = aws_security_group.example_sgw_ec2.id } resource "aws_security_group_rule" "nfs_udp" { type = "ingress" from_port = 2049 to_port = 2049 protocol = "udp" description = "NFS-UDP" cidr_blocks = ["<既存のVPC CIDR>"] security_group_id = aws_security_group.example_sgw_ec2.id } resource "aws_security_group_rule" "nfs_portmapper_tcp" { type = "ingress" from_port = 111 to_port = 111 protocol = "tcp" description = "NFS-portmapper-TCP" cidr_blocks = ["<既存のVPC CIDR>"] security_group_id = aws_security_group.example_sgw_ec2.id } resource "aws_security_group_rule" "nfs_portmap_udp" { type = "ingress" from_port = 111 to_port = 111 protocol = "udp" description = "NFS-portmapper-UDP" cidr_blocks = ["<既存のVPC CIDR>"] security_group_id = aws_security_group.example_sgw_ec2.id } resource "aws_security_group_rule" "nfs_v3_tcp" { type = "ingress" from_port = 20048 to_port = 20048 protocol = "tcp" description = "NFSv3-TCP" cidr_blocks = ["<既存のVPC CIDR>"] security_group_id = aws_security_group.example_sgw_ec2.id } resource "aws_security_group_rule" "nfs_v3_udp" { type = "ingress" from_port = 20048 to_port = 20048 protocol = "UDP" description = "NFSv3-UDP" cidr_blocks = ["<既存のVPC CIDR>"] security_group_id = aws_security_group.example_sgw_ec2.id } resource "aws_security_group_rule" "egress" { type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.example_sgw_ec2.id } # ---------------------------------------- # Security Group - storagegateway VPC endpoint # ---------------------------------------- resource "aws_security_group" "example_sgw_vpce" { name = "sgw-vpce-security-group" vpc_id = "<既存のVPC ID>" } resource "aws_security_group_rule" "tcp_1026_1028" { type = "ingress" from_port = 1026 to_port = 1028 protocol = "tcp" source_security_group_id = aws_security_group.example_sgw_ec2.id security_group_id = aws_security_group.example_sgw_vpce.id } resource "aws_security_group_rule" "tcp_1031" { type = "ingress" from_port = 1031 to_port = 1031 protocol = "tcp" source_security_group_id = aws_security_group.example_sgw_ec2.id security_group_id = aws_security_group.example_sgw_vpce.id } resource "aws_security_group_rule" "tcp_2222" { type = "ingress" from_port = 2222 to_port = 2222 protocol = "tcp" source_security_group_id = aws_security_group.example_sgw_ec2.id security_group_id = aws_security_group.example_sgw_vpce.id } resource "aws_security_group_rule" "tcp_443_sgwvpce" { type = "ingress" from_port = 443 to_port = 443 protocol = "tcp" source_security_group_id = aws_security_group.example_sgw_ec2.id security_group_id = aws_security_group.example_sgw_vpce.id } resource "aws_security_group_rule" "egress_sgwvpce" { type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.example_sgw_vpce.id } # ---------------------------------------- # Security Group - S3 VPC endpoint # ---------------------------------------- resource "aws_security_group" "example_s3_vpce" { name = "s3-vpce-security-group" vpc_id = "<既存のVPC ID>" } resource "aws_security_group_rule" "tcp_443_s3vpce" { type = "ingress" from_port = 443 to_port = 443 protocol = "tcp" source_security_group_id = aws_security_group.example_sgw_ec2.id security_group_id = aws_security_group.example_s3_vpce.id } resource "aws_security_group_rule" "egress_s3vpce" { type = "egress" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.example_s3_vpce.id }
storage-gateway.tf
resource "aws_storagegateway_gateway" "example" { activation_key = "<curl で取得したアクティベーションキー>" gateway_name = "terraform-sgw" gateway_timezone = "GMT+9:00" gateway_type = "FILE_S3" gateway_vpc_endpoint = lookup(aws_vpc_endpoint.sgw_example.dns_entry[0], "dns_name") maintenance_start_time { day_of_month = null day_of_week = "2" hour_of_day = 0 minute_of_hour = 10 } } data "aws_storagegateway_local_disk" "sgw" { gateway_arn = aws_storagegateway_gateway.example.arn disk_node = "/dev/sdf" disk_path = "/dev/sdf" } resource "aws_storagegateway_cache" "sgw" { gateway_arn = aws_storagegateway_gateway.example.arn disk_id = data.aws_storagegateway_local_disk.sgw.id }
vpc-endpoint.tf
resource "aws_vpc_endpoint" "s3_example" { ip_address_type = "ipv4" private_dns_enabled = true security_group_ids = [aws_security_group.example_s3_vpce.id] service_name = "com.amazonaws.ap-northeast-1.s3" subnet_ids = ["<既存のサブネット ID>"] tags = { Name = "s3-interface-endpoint" } vpc_endpoint_type = "Interface" vpc_id = "<既存のVPC ID>" dns_options { dns_record_ip_type = "ipv4" } } resource "aws_vpc_endpoint" "sgw_example" { ip_address_type = "ipv4" private_dns_enabled = true security_group_ids = [aws_security_group.example_sgw_vpce.id] service_name = "com.amazonaws.ap-northeast-1.storagegateway" subnet_ids = ["<既存のサブネット ID>"] tags = { Name = "sgw-interface-endpoint" } vpc_endpoint_type = "Interface" vpc_id = "<既存のVPC ID>" dns_options { dns_record_ip_type = "ipv4" } }
file-share.tf
resource "aws_storagegateway_nfs_file_share" "example" { client_list = ["0.0.0.0/0"] gateway_arn = aws_storagegateway_gateway.example.arn location_arn = "<既存のS3バケット ARN>" bucket_region = "ap-northeast-1" role_arn = aws_iam_role.example.arn vpc_endpoint_dns_name = substr("${lookup(aws_vpc_endpoint.s3_example.dns_entry[0], "dns_name")}", 2, -1) } resource "aws_iam_role" "example" { name = "file-share-role" assume_role_policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Principal": { "Service": "storagegateway.amazonaws.com" }, "Effect": "Allow" } ] } EOF } resource "aws_iam_role_policy_attachment" "s3" { role = aws_iam_role.example.name policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess" }
プライベートサブネットに Storage Gateway を構築する場合は EC2 インスタンス作成後にアクティベーションキーを取得するため、段階を踏んで terraform apply する必要があります。
一手間必要ではありますが、効率的で間違いのない運用のためにも、可能な限りコードで管理していきましょう!
テックブログ新着情報のほか、AWSやGoogle Cloudに関するお役立ち情報を配信中!
Follow @twitter2014年入社。 関東近郊の山と銭湯と飲み屋を巡り歩いてます。
Recommends
こちらもおすすめ
-
「AWS Backup for Amazon S3」 の一般提供が公開されました
2022.3.11
-
AWS初心者におすすめの勉強法と運用のポイント
2019.5.30
-
Amazon S3とローカルPCをWinSCPでシンプルにファイル共有してみた
2024.4.30
Special Topics
注目記事はこちら
データ分析入門
これから始めるBigQuery基礎知識
2024.02.28
AWSの料金が 10 %割引になる!
『AWSの請求代行リセールサービス』
2024.07.16