Amazon FSx for NetApp ONTAPとは

Amazon FSx for NetApp ONTAPはNetApp ONTAPファイルシステム上に構築されたスケーラブルなフルマネージド型のファイルストレージ。

詳しくは以下の記事を参照。

» Amazon FSx for NetApp ONTAP とは何ですか? - FSx for ONTAP

本記事ではTerraformを使用してNFSでマウントできるようにするまでの手順を記載する。

VPCの作成

FSxのインスタンスを設置するVPCを作成する。

Terraformの基本設定とproviderの設定と初期化

terraformブロックでtfstateファイルはローカルに保存するように設定する。(S3に保存する場合は適宜変更する)

AWSを利用するため、awsプロバイダーを設定する。

terraform {
  required_version = ">= 0.12"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }

  backend "local" {
    path = "example.tfstate"
  }
}

provider "aws" {
  region = "ap-northeast-1"
  default_tags {
    tags = {
      Project = "fsx-test"
    }
  }
}

設定したら以下のコマンドでawsプロバイダのインストールを含めた初期化を行なう。

$ terraform init

.terraformディレクトリーや.terraform.lock.hclファイルが作成される。

ネットワークの作成

VPC, サブネット, インターネットゲートウェイ, ルートテーブルを作成する。

# VPCの作成
resource "aws_vpc" "vpc" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
}

# インターネットゲートウェイの作成
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id
}

# サブネットの作成
resource "aws_subnet" "subnet_c" {
  vpc_id                  = aws_vpc.vpc.id
  availability_zone       = "ap-northeast-1c"
  cidr_block              = "10.0.0.0/20"
  map_public_ip_on_launch = true
}

# ルートテーブルの作成
resource "aws_route_table" "route_table_c" {
  vpc_id = aws_vpc.vpc.id
}

resource "aws_route" "route_c" {
  route_table_id         = aws_route_table.route_table_c.id
  gateway_id             = aws_internet_gateway.igw.id
  destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route_table_association" "route_table_association_c" {
  subnet_id      = aws_subnet.subnet_c.id
  route_table_id = aws_route_table.route_table_c.id
}

この段階で一度applyしておく。

$ terraform apply

credentialsの設定は適宜おこななう。

NFSのセキュリティグループの作成

NFSのポート(2049番ポート)を開けるためのセキュリティグループを作成する。
egressは全てのポートを開けておく。
ingressで必要なポートが他にある場合や、egressで全てのポートを開けるのが気になる場合は適宜変更する。

CIDRはVPCのCIDRを指定しており、VPC内からのみアクセスできるようにしている。
更に絞りたい場合は適宜変更する。

resource "aws_security_group" "nfs_sg" {
  name   = "nfs"
  vpc_id = aws_vpc.vpc.id
}

resource "aws_security_group_rule" "ingress_nfs_sg" {
  type              = "ingress"
  from_port         = 2049
  to_port           = 2049
  protocol          = "tcp"
  cidr_blocks       = ["10.0.0.0/16"]
  security_group_id = aws_security_group.nfs_sg.id
}

resource "aws_security_group_rule" "egress_nfs_sg" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.nfs_sg.id
}

ここまでの内容をapplyしておく。

$ terraform apply

Amazon FSx for NetApp ONTAPの作成(シングルAZ構成)

シングルAZ構成のFSxを作成する。

# ファイルシステムの作成
resource "aws_fsx_ontap_file_system" "example" {
  storage_capacity    = 1024
  subnet_ids          = [aws_subnet.subnet_c.id]
  preferred_subnet_id = aws_subnet.subnet_c.id
  deployment_type     = "SINGLE_AZ_1"
  throughput_capacity = 128
  security_group_ids  = [aws_security_group.nfs_sg.id]

  timeouts {
    create = "60m"
    update = "60m"
    delete = "2h"
  }
}

# ストレージ仮想マシン (SVM) の作成
resource "aws_fsx_ontap_storage_virtual_machine" "example" {
  file_system_id = aws_fsx_ontap_file_system.example.id
  name           = "example-svm"
}

# ボリュームの作成
resource "aws_fsx_ontap_volume" "example" {
  name                       = "example_volume"
  junction_path              = "/vol1"
  size_in_megabytes          = 1024
  storage_efficiency_enabled = true
  storage_virtual_machine_id = aws_fsx_ontap_storage_virtual_machine.example.id
}

FSxの構成は「ファイルシステム←ストレージ仮想マシン (SVM)←ボリューム」のような階層構造になる。

ws_fsx_ontap_file_systemは作成に20分程度かかる。
Terraformのデフォルト待機時間は30分でそれほど余裕が無いので、念のためにtimeoutsで待機時間を延長している。

ここまでの内容をapplyしておく。

$ terraform apply

EC2からNFSマウントする

お試しのEC2インスタンスをVPC内に作成し、NFSマウントしてみる。

EC2インスタンスプロファイルの作成

EC2インスタンスにはSSMを使用するためのプロファイルを作成する。

resource "aws_iam_instance_profile" "instance_profile" {
  role = aws_iam_role.instance_profile_role.name
}

resource "aws_iam_role" "instance_profile_role" {
  name               = "ec2-instance-profile"
  assume_role_policy = data.aws_iam_policy_document.instance_profile_assume_role.json
}

data "aws_iam_policy_document" "instance_profile_assume_role" {
  statement {
    effect = "Allow"
    actions = [
      "sts:AssumeRole",
    ]
    principals {
      type = "Service"
      identifiers = [
        "ec2.amazonaws.com",
      ]
    }
  }
}

resource "aws_iam_policy_attachment" "instance_profile_policy_attachment" {
  name       = "ec2-instance-profile-ssm"
  roles      = [aws_iam_role.instance_profile_role.name]
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

» 参考: 【Terraform】Session Manager でログイン可能な EC2 を建てる

EC2インスタンスの作成

EC2のインスタンスを立ち上げる

resource "aws_instance" "host" {
  ami                    = "ami-02eb2602e381117c7" # Amazon Linux 2023 AMI 2023.2.20231011.0 arm64 HVM kernel-6.1
  instance_type          = "t3.nano"
  subnet_id              = aws_subnet.subnet_c.id
  iam_instance_profile   = aws_iam_instance_profile.instance_profile.name
  vpc_security_group_ids = [aws_security_group.ec2_sg.id]

  root_block_device {
    volume_type = "gp2"
    volume_size = 8
  }

  metadata_options {
    instance_metadata_tags = "enabled"
    http_endpoint          = "enabled"
    http_tokens            = "optional"
  }

  user_data = <<-EOF
    #!/bin/bash
    sudo dnf install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
    sudo systemctl enable amazon-ssm-agent --now
  EOF
}

resource "aws_security_group" "ec2_sg" {
  name   = "ec2"
  vpc_id = aws_vpc.vpc.id
}

resource "aws_security_group_rule" "ingress_ec2_sg" {
  type              = "ingress"
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
  cidr_blocks       = ["10.0.0.0/16"]
  security_group_id = aws_security_group.ec2_sg.id
}

resource "aws_security_group_rule" "egress_ec2_sg" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.ec2_sg.id
}

ここまでの内容をapplyしておく。

$ terraform apply

EC2インスタンスにNFSマウントする

立ち上げたEC2インスタンスにSession Managerでログインする。

$ aws ssm start-session --target i-xxxxxxxxxxxxxxxxx

インスタンスのID(i-xxxxxxxxxxxxxxxxx)はterraform applyの結果や、terraform state show aws_instance.hostで確認できる。

ログインできたらNFSマウントを試す。

$ sudo mkdir -p /mnt/fsx
$ sudo mount -t nfs svm-xxxxxxxxxxxxxxxxx.fs-xxxxxxxxxxxxxxxxx.fsx.ap-northeast-1.amazonaws.com:/vol1 /mnt/fsx

svm-xxxxxxxxxxxxxxxxx.fs-xxxxxxxxxxxxxxxxx.fsx.ap-northeast-1.amazonaws.comはFSxのNFSエンドポイント。
実際の値はAWSコンソールで確認するか、Terraformのtfstateを参照すると確認できる。

terraform stateコマンドでaws_fsx_ontap_storage_virtual_machineの値を参照し、nfsdns_nameを確認する。

$ terraform state show aws_fsx_ontap_storage_virtual_machine.example
...
            nfs        = [
                {
                    dns_name     = "svm-xxxxxxxxxxxxxxxxx.fs-xxxxxxxxxxxxxxxxx.fsx.ap-northeast-1.amazonaws.com"
                    ip_addresses = [
                      ...
                    ]
                },
            ]
...

dfコマンドでマウントできているか確認する。

$ df -h
...
svm-xxxxxxxxxxxxxxxxx.fs-xxxxxxxxxxxxxxxxx.fsx.ap-northeast-1.amazonaws.com:/vol1 973M  320K  973M   1% /mnt/fsx

ファイルを作成してみる。

$ sudo echo "hello" > /mnt/fsx/hello.txt
$ ls /mnt/fsx
hello.txt

一度アンマウントする。

$ sudo umount /mnt/fsx
$ ls /mnt/fsx

もう一度マウントしてみる。

$ sudo mount -t nfs svm-xxxxxxxxxxxxxxxxx.fs-xxxxxxxxxxxxxxxxx.fsx.ap-northeast-1.amazonaws.com:/vol1 /mnt/fsx
$ cat /mnt/fsx/hello.txt
hello

NFSでマウントできていることが確認できた。

/etc/fstabでマウントする場合は以下の通り。

svm-xxxxxxxxxxxxxxxxx.fs-xxxxxxxxxxxxxxxxx.fsx.ap-northeast-1.amazonaws.com:/vol1 /mnt/fsx nfs defaults 0 0

/etc/fstabを編集したら以下のコマンドでマウントする。

$ mount -a

マルチAZ構成

マルチAZ構成にしてみる。

サブネットの追加

マルチAZ構成にするためにap-northeast-1aにもサブネットを作成する。
ネットワーク設定に以下を追加する。

...

resource "aws_subnet" "subnet_a" {
  vpc_id                  = aws_vpc.vpc.id
  availability_zone       = "ap-northeast-1a"
  cidr_block              = "10.0.16.0/20"
  map_public_ip_on_launch = true
}

resource "aws_route_table" "route_table_a" {
  vpc_id = aws_vpc.vpc.id
}

resource "aws_route" "route_a" {
  route_table_id         = aws_route_table.route_table_a.id
  gateway_id             = aws_internet_gateway.igw.id
  destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route_table_association" "route_table_association_a" {
  subnet_id      = aws_subnet.subnet_a.id
  route_table_id = aws_route_table.route_table_a.id
}

ファイルシステムの変更

マルチAZにするためにはシングルAZ構成に比べて以下を変更する。

  • aws_fsx_ontap_file_systemdeployment_typeSINGLE_AZ_1からMULTI_AZ_1に変更
  • aws_fsx_ontap_file_systemsubnet_idsにサブネットを追加
  • route_table_idsを追加し、すべてのサブネットでルートテーブルを設定

具体的には以下のような設定となる。

resource "aws_fsx_ontap_file_system" "example" {
  storage_capacity    = 1024
  subnet_ids          = [aws_subnet.subnet_a.id, aws_subnet.subnet_c.id] # subnet_a を追加
  preferred_subnet_id = aws_subnet.subnet_c.id
  deployment_type     = "MULTI_AZ_1" # MULTI_AZ_1 に変更
  throughput_capacity = 128
  security_group_ids  = [aws_security_group.nfs_sg.id]
  route_table_ids     = [aws_route_table.route_table_a.id, aws_route_table.route_table_c.id] # 追加

  timeouts {
    create = "60m"
    update = "60m"
    delete = "2h"
  }
}

VolumeやSVMを誤って削除されないようにガードする

aws_fsx_ontap_file_systemの設定値の一部を修正すると、aws_fsx_ontap_volumeaws_fsx_ontap_storage_virtual_machineが削除される。
不用意に削除されないように念のためlifecycleでガードする。

resource "aws_fsx_ontap_storage_virtual_machine" "example" {
  ...
  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_fsx_ontap_volume" "example" {
  ...
  lifecycle {
    prevent_destroy = true
  }
}

この状態でaws_fsx_ontap_storage_virtual_machineaws_fsx_ontap_volumeが削除されるようなterraform applyするとエラーで止まるようになる。