Amazon S3ファイルゲートウェイをTerraformで作ってみる
はじめに
業務上LinuxとWindowsのインスタンスでファイルの共有を簡単にする必要がありました。
元々、頻繁にファイルの共有しないつもりだったのでS3でファイルを管理していました。
そのため、S3をEC2にマウントする方向性で試してみました。
構成
元々 Private Subnet のみで構成しようしました。が、BastionサーバーにSSMマネージャーで接続したかったので、VPC外のAWSサービス接続のためにNATとInternetGatewayを用意しています。
VPC 設定
今回は、Subnet を Bastion と StorageGateway サーバーで分けているので、明示的にサブネットを関連づけています。
### Route Table ###
resource "aws_route_table" "private_table" {
vpc_id = aws_vpc.main.id
}
resource "aws_route_table_association" "private_association_a_1" {
subnet_id = aws_subnet.pri_a_1.id
route_table_id = aws_route_table.private_table.id
}
resource "aws_route_table_association" "private_association_a_2" {
subnet_id = aws_subnet.pri_a_2.id
route_table_id = aws_route_table.private_table.id
}
Bastion サーバー設定
SSMマネージャでの接続のために IAM Role を作成しています。
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
resource "aws_iam_role" "main" {
name = "${var.name}-role"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
resource "aws_iam_role_policy_attachment" "main" {
role = aws_iam_role.main.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
resource "aws_iam_instance_profile" "main" {
name = "${var.name}-profile"
role = aws_iam_role.main.name
}
Amazon Linux 2023 の最新のAMIをパラメータストアから取得してインスタンスを作成します。
IAM Roleを指定する以外、特段変わった指定はありません。
data "aws_ssm_parameter" "amzn_linux" {
name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64" # x86_64
}
resource "aws_security_group" "main" {
name = "${var.name}-sg"
vpc_id = var.vpc_id
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "All traffic is denied"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "All traffic is allowed"
}
}
resource "aws_instance" "bastion" {
ami = data.aws_ssm_parameter.amzn_linux.insecure_value
instance_type = "t3.micro"
subnet_id = var.subnet_id
iam_instance_profile = aws_iam_instance_profile.main.name
vpc_security_group_ids = [
aws_security_group.main.id
]
root_block_device {
volume_type = "gp3"
volume_size = 8
delete_on_termination = true
}
}
Storage Gateway サーバーの設定
今回はNFSでのファイル共有を試すため、セキュリティグループの設定は以下とします。
80 ポートはこの後のアクティベーションキーの設定のために必要です。
NFS 向けのポートとして、111、2049、20048のポートのアクセスを可能にしておきます。
SMB での利用の場合は、139、445のポートを指定してください。
resource "aws_security_group" "main" {
name = var.name
vpc_id = var.vpc_id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
description = "internal for s3-filegateway"
cidr_blocks = [var.vpc_cidr]
}
ingress {
from_port = 111
to_port = 111
protocol = "tcp"
description = "internal for nfs111"
cidr_blocks = [var.vpc_cidr]
}
ingress {
from_port = 2049
to_port = 2049
protocol = "tcp"
description = "internal for nfs2049"
cidr_blocks = [var.vpc_cidr]
}
ingress {
from_port = 20048
to_port = 20048
protocol = "tcp"
description = "internal for nfs20048"
cidr_blocks = [var.vpc_cidr]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
description = "external all"
cidr_blocks = ["0.0.0.0/0"]
}
}
インスタンスの必要要件として以下があります。
- AMI : S3FileStorageGateway専用のAMIを指定
- インスタンスタイプ : 最低でも m4.xlarge 以上が必要
- ルートブロックサイズ : 最低 80 GB
- キャッシュストレージサイズ : 最低 150 GB
これを踏まえて、インスタンスの設定は以下とします。
# Storage Gateway Instance
data "aws_ssm_parameter" "s3fg_ami" {
name = "/aws/service/storagegateway/ami/FILE_S3/latest"
}
# インスタンス設定
resource "aws_instance" "default" {
ami = data.aws_ssm_parameter.s3fg_ami.insecure_value
instance_type = "m4.xlarge" # 大体 xlarge 以上のインスタンスが必要になる
key_name = aws_key_pair.main.key_name
subnet_id = var.subnet_id
vpc_security_group_ids = [
aws_security_group.main.id
]
root_block_device {
volume_type = "gp3"
volume_size = 80 # 最低 80GB必要
delete_on_termination = true
}
}
# EBS追加ボリューム。Cacheストレージはサイズ変更してはならず、アタッチで追加しなければならない。
resource "aws_ebs_volume" "sdf" {
availability_zone = aws_instance.default.availability_zone
# ボリュームサイズ(GiB)
size = 150 # 最低 150GB必要
}
resource "aws_volume_attachment" "sdf" {
device_name = "/dev/sdf"
volume_id = aws_ebs_volume.sdf.id
instance_id = aws_instance.default.id
}
StorageGateway有効化のためにパラメータストアを用意しておきます。
resource "aws_ssm_parameter" "activation_key" {
name = "/${var.name}/filegateway/activation_key"
value = "password"
type = "SecureString"
description = "activation_key for s3 file gateway"
lifecycle {
ignore_changes = [value] # 上書きして変更するので無視する
}
}
BastionサーバーにSSMで接続してアクティベーションキーを取得します。
# パブリックエンドポイントのアクティベーションキーを取得する
$ curl "http://gateway_ip_address/?activationRegion=region_code&no_redirect"
# VPC エンドポイントのアクティベーションキーを取得する
$ curl "http://gateway_ip_address/?activationRegion=region_code&vpcEndpoint=vpc_endpoint&no_redirect"
公式によると上記を実行することで取得が可能です。
$ curl "http://gateway_ip_address/?activationRegion=ap-northeast-1&no_redirect"
ACTIVATION_KEY
コンソールからでもAWS CLIからでも良いので作成済みのパラメータストアの値を更新します。
$ aws ssm put-parameter --name /s3fg-instance/filegateway/activation_key --value ACTIVATION_KEY --overwrite
アクティベーションキーは30分間有効なので、取得後は StorageGateway の設定に移ります。
Storage Gateway の設定
上記で取得した際のアクティベーションキーを引き渡し、Storage Gateway を設定します。
ゲストアクセスとして簡単にSMBの設定もしているので、SMB で利用したい場合はパスワードの設定は見直してください。
resource "aws_storagegateway_gateway" "main" {
activation_key = var.activation_key
gateway_name = var.name
gateway_timezone = "GMT"
gateway_type = "FILE_S3"
smb_guest_password = "password"
lifecycle {
ignore_changes = [smb_guest_password]
}
}
# ディスクIDの取得のために aws_storage gateway_local_disk を使う
# 「disk_node」を以下の様に記載する
data "aws_storagegateway_local_disk" "main" {
gateway_arn = aws_storagegateway_gateway.main.arn
disk_node = "/dev/sdf"
}
resource "aws_storagegateway_cache" "main" {
gateway_arn = aws_storagegateway_gateway.main.arn
# aws_storage gateway_local_diskからディスクIDを設定
disk_id = data.aws_storagegateway_local_disk.main.id
}
NFSファイル共有に対してIAM Roleを用意します。
Storage GatewayがS3へのアクセスできるように設定しています。
data "aws_iam_policy_document" "assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["storagegateway.amazonaws.com"]
}
}
}
resource "aws_iam_role" "main" {
name = "file-share-role"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
resource "aws_iam_role_policy_attachment" "s3" {
role = aws_iam_role.main.name
policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
}
resource "aws_storagegateway_nfs_file_share" "nfs" {
client_list = ["0.0.0.0/0"]
gateway_arn = aws_storagegateway_gateway.main.arn
location_arn = "s3 bucket の ARN"
role_arn = aws_iam_role.main.arn
}
動作確認
Bastion サーバーに SSM セッションマネージャーで接続してマウントします。
$ sudo mkdir /mnt/shared-bucket
# IPアドレスはストレージゲートウェイのインスタンスのプライベートアドレスを指定
$ sudo mount -t nfs -o nolock,hard 10.0.1.xxx:/s3fg-shared-bucket /mnt/shared-bucket
以下のようにファイルを作ると
きちんと S3 にアップロードされます。注意としてはキャッシュは EC2 インスタンス側に持つので、 S3 からアップロードしても反映されません。
WindowsServer で利用する場合
NFS でそのままマウントしたい場合は、PowerShell で NFS のクライアントをインストールしてください。
$ Install-WindowsFeature NFS-Client
ファイル共有で NFS と SMB の両方を同一のバケットを指定すれば良さそうとも思うのですが、同一のバケットを指定しても異なるファイル共有ではうまく扱えません。
なので、 Linux と Windows の EC2 インスタンスで同じファイルを共有したいのであれば、プロトコルを統一し、1つのファイル共有をマウントする必要があります。
おわりに
S3 を簡単にマウントできるのかなと思い試したのですが、設定も多くて面倒でした。
加えて、 xlarge 以上の EC2 インスタンスを立てておかないといけないので、コスト面も安いとは言えないかなと思います。
次は Amazon FSx の方を試してみたいと思います。
参考
Discussion