SageMaker HyperPod で FSx Lustre をマウントしてみた

SageMaker HyperPod で FSx Lustre をマウントしてみた

SageMaker HyperPod で Lustre ファイルシステムをマウントしてみました。通信要件が EFA の有無で異なるポイント非常に興味深かったです。
Clock Icon2024.12.20

こんにちは!AWS 事業本部コンサルティング部のたかくに(@takakuni_)です。

みなさん SageMaker HyperPod 使っていますか?

今回は SageMaker HyperPod の共有ストレージ領域に FSx for Lustre を利用したいと思います。

FSx for Lustre

AWS が提供する高性能なフルマネージド分散並列ファイルシステムです。

FSx for Lustre を利用することで 1/100 秒未満のレイテンシ、最大数百 GBps のスループット、最大数百万の IOPS の性能を有したファイルシステムを利用できます。

データリポジトリ

FSx for Lustre では S3 と統合しており、 S3 オブジェクトをファイルとして透過的に書き込み/読み込みできます。

学習や推論で利用されるデータはサイズが大きいことが多々あり、各ノードが起動するタイミングで直接 S3 からダウンロードするのは時間もお金も大きくかかります。

FSx for Lustre を中継させることでコスト効率を高める効果が見込めます。

Untitled(103).png

通信要件

EFA の有無で異なります。

EFA 無し

EFA なしの場合は SageMaker HyperPod ノードおよび自身のセキュリティグループを 988, 1018-1023 ポートで許可するように設定します。

Lustre ファイルシステム側

インバウンドルール

タイプ プロトコル ポート ソース
Custom TCP TCP 988 自身のセキュリティグループ ID
Custom TCP TCP 988 SageMaker HyperPod ノードのセキュリティグループ ID
Custom TCP TCP 1018-1023 自身のセキュリティグループ ID
Custom TCP TCP 1018-1023 SageMaker HyperPod ノードのセキュリティグループ ID

アウトバウンドルール

タイプ プロトコル ポート ソース
Custom TCP TCP 988 自身のセキュリティグループ ID
Custom TCP TCP 988 SageMaker HyperPod ノードのセキュリティグループ ID
Custom TCP TCP 1018-1023 自身のセキュリティグループ ID
Custom TCP TCP 1018-1023 SageMaker HyperPod ノードのセキュリティグループ ID

Lustre クライアント側

インバウンドルール

タイプ プロトコル ポート ソース
Custom TCP TCP 988 自身のセキュリティグループ ID
Custom TCP TCP 988 Lustre ファイルシステムのセキュリティグループ ID
Custom TCP TCP 1018-1023 自身のセキュリティグループ ID
Custom TCP TCP 1018-1023 Lustre ファイルシステムのセキュリティグループ ID

アウトバウンドルール

タイプ プロトコル ポート ソース
Custom TCP TCP 988 自身のセキュリティグループ ID
Custom TCP TCP 988 Lustre ファイルシステムのセキュリティグループ ID
Custom TCP TCP 1018-1023 自身のセキュリティグループ ID
Custom TCP TCP 1018-1023 Lustre ファイルシステムのセキュリティグループ ID
すべてのトラフィック すべて すべて 0.0.0.0/0

※一番最後のすべてのトラフィックは外部接続用のルールのため、 Lustre とのやりとりとは別のルールです。

https://docs.aws.amazon.com/fsx/latest/LustreGuide/limit-access-security-groups.html#lustre-client-inbound-outbound-rules

EFA 有り

EFA 有りの場合はすべての送受信トラフィックを許可する必要があります。

If you are going to create an EFA-enabled FSx for Lustre, you should first create an EFA-enabled security group and specify it as the security group for the file system. An EFA requires a security group that allows all inbound and outbound traffic to and from the security group itself and the security group of the clients if clients reside in a different security group. For more information, see Step 1: Prepare an EFA-enabled security group in the Amazon EC2 User Guide.

https://docs.aws.amazon.com/fsx/latest/LustreGuide/limit-access-security-groups.html#fsx-vpc-security-groups

要約すると以下のルールを追加する必要があります。

Lustre ファイルシステム側

インバウンドルール

タイプ プロトコル ポート ソース
すべてのトラフィック すべて すべて 自身のセキュリティグループ ID
すべてのトラフィック すべて すべて SageMaker HyperPod ノードのセキュリティグループ ID

アウトバウンドルール

タイプ プロトコル ポート ソース
すべてのトラフィック すべて すべて 自身のセキュリティグループ ID
すべてのトラフィック すべて すべて SageMaker HyperPod ノードのセキュリティグループ ID

Lustre クライアント側

インバウンドルール

タイプ プロトコル ポート ソース
すべてのトラフィック すべて すべて 自身のセキュリティグループ ID
すべてのトラフィック すべて すべて Lustre ファイルシステムのセキュリティグループ ID

アウトバウンドルール

タイプ プロトコル ポート ソース
すべてのトラフィック すべて すべて 自身のセキュリティグループ ID
すべてのトラフィック すべて すべて Lustre ファイルシステムのセキュリティグループ ID
すべてのトラフィック すべて すべて 0.0.0.0/0

一番最後のすべてのトラフィックは外部接続用のルールのため、 Lustre とのやりとりとは別のルールです。

0.0.0.0/0 で許可をしていますが、合わせてセキュリティグループ ID も許可してあげる必要があります。

If you want to create a HyperPod cluster with EFA-enabled instances, make sure that you set up a security group to allow all inbound and outbound traffic to and from the security group itself. Note that allowing outbound traffic to 0.0.0.0/0 isn't sufficient and can cause EFA health checks to fail. Make sure that you add an explicit outbound traffic rule to the security group so that the instances in the security group can communicate. To learn more, see Step 1: Prepare an EFA-enabled security group in the Amazon EC2 User Guide.

https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-hyperpod-prerequisites.html#sagemaker-hyperpod-prerequisites-optional-vpc

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/efa-start.html#efa-start-security

SageMaker HyperPod 上で動かす際の注意点

ドキュメントの通りなのですが、ユーザー管理の VPC 上でホストする必要があること、ネットワーク遅延を避けるためになるべく同一 AZ 上でクライアント、ファイルシステムの送受信を行うことが推奨されています。

To start using SageMaker HyperPod and mapping data paths between the cluster and your FSx for Lustre file system, select one of the AWS Regions supported by SageMaker HyperPod. After choosing the AWS Region you prefer, you also should determine which Availability Zone (AZ) to use. If you use SageMaker HyperPod compute nodes in AZs different from the AZs where your FSx for Lustre file system is set up within the same AWS Region, there might be communication and network overhead. We recommend that you to use the same physical AZ as the one for the SageMaker HyperPod service account to avoid any cross-AZ traffic between SageMaker HyperPod clusters and your FSx for Lustre file system. Also, make sure that you have configured it with your VPC. If you want to use Amazon FSx as the main file system for storage, you must configure SageMaker HyperPod clusters with VPC.

https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-hyperpod-prerequisites.html#sagemaker-hyperpod-prerequisites-optional-fsx

やってみる

それでは SageMaker HyperPod のインスタンスを FSx for Lustre にマウントしてみたいと思います。

今回利用したコードは以下に格納されています。

https://github.com/takakuni-classmethod/genai-blog/tree/main/sagemaker_hyperpod_lustre

Security Group

セキュリティグループはドキュメントに従い、 EFA 対応にしてみました。(Lustre 側で EFA 有効化する術がないため、本来だと特定ポートで制御が望ましいですが。)

lustre.tf (抜粋)
###################################################
# Security Group for Lustre File System
###################################################
resource "aws_security_group" "lustre" {
  name        = "${local.prefix}-lustre-sg"
  vpc_id      = module.vpc.vpc_id
  description = "${local.prefix}-hyperpod-sg"

  tags = {
    Name = "${local.prefix}-lustre-sg"
  }
}
# Ingress
resource "aws_vpc_security_group_ingress_rule" "lustre_all_traffic_self" {
  security_group_id            = aws_security_group.lustre.id
  referenced_security_group_id = aws_security_group.lustre.id
  ip_protocol                  = "-1"
}

resource "aws_vpc_security_group_ingress_rule" "lustre_all_traffic_hyperpod" {
  security_group_id            = aws_security_group.lustre.id
  referenced_security_group_id = aws_security_group.hyperpod.id
  ip_protocol                  = "-1"
}

# Egress
resource "aws_vpc_security_group_egress_rule" "lustre_all_traffic_self" {
  security_group_id            = aws_security_group.lustre.id
  referenced_security_group_id = aws_security_group.lustre.id
  ip_protocol                  = "-1"
}

resource "aws_vpc_security_group_egress_rule" "lustre_all_traffic_hyperpod" {
  security_group_id            = aws_security_group.lustre.id
  referenced_security_group_id = aws_security_group.hyperpod.id
  ip_protocol                  = "-1"
}

クライアント(ノード)側も許可してあげましょう。

hyperpod.tf (抜粋)
###################################################
# Security Group for SageMaker HyperPod Cluster
###################################################
resource "aws_security_group" "hyperpod" {
  name        = "${local.prefix}-hyperpod-sg"
  vpc_id      = module.vpc.vpc_id
  description = "${local.prefix}-hyperpod-sg"

  tags = {
    Name = "${local.prefix}-hyperpod-sg"
  }
}

# Ingress
resource "aws_vpc_security_group_ingress_rule" "hyperpod_all_traffic_self" {
  security_group_id            = aws_security_group.hyperpod.id
  referenced_security_group_id = aws_security_group.hyperpod.id
  ip_protocol                  = "-1"
}

+ resource "aws_vpc_security_group_ingress_rule" "hyperpod_all_traffic_lustre" {
+   security_group_id            = aws_security_group.hyperpod.id
+   referenced_security_group_id = aws_security_group.lustre.id
+   ip_protocol                  = "-1"
+ }

# Egress
resource "aws_vpc_security_group_egress_rule" "hyperpod_all_traffic_self" {
  security_group_id            = aws_security_group.hyperpod.id
  referenced_security_group_id = aws_security_group.hyperpod.id
  ip_protocol                  = "-1"
}

+ resource "aws_vpc_security_group_egress_rule" "hyperpod_all_traffic_lustre" {
+   security_group_id            = aws_security_group.hyperpod.id
+   referenced_security_group_id = aws_security_group.lustre.id
+   ip_protocol                  = "-1"
+ }

resource "aws_vpc_security_group_egress_rule" "hyperpod_all_traffic_ipv4" {
  security_group_id = aws_security_group.hyperpod.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "-1"
}

Lustre Fils System

Lustre ファイルシステムの定義です。

書き込みした結果を S3 にも反映したいため、aws_fsx_data_repository_association を定義しました。

lustre.tf (抜粋)
###################################################
# Data Repository for Lustre File System
###################################################
resource "aws_s3_bucket" "data_repository" {
  bucket        = "${local.prefix}-hyperpod-data-${local.account_id}"
  force_destroy = true
}

resource "aws_s3_bucket_public_access_block" "data_repository" {
  bucket                  = aws_s3_bucket.data_repository.bucket
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_s3_bucket_ownership_controls" "data_repository" {
  bucket = aws_s3_bucket.data_repository.bucket
  rule {
    object_ownership = "BucketOwnerPreferred"
  }
}

###################################################
# Lustre File System
###################################################
resource "aws_fsx_lustre_file_system" "this" {
  storage_type                = "SSD"
  file_system_type_version    = "2.15"
  storage_capacity            = 1200
  security_group_ids          = [aws_security_group.lustre.id]
  subnet_ids                  = [module.vpc.private_subnets[0]]
  data_compression_type       = "LZ4"
  deployment_type             = "PERSISTENT_2"
  per_unit_storage_throughput = 250

  metadata_configuration {
    mode = "AUTOMATIC"
  }
}

resource "aws_fsx_data_repository_association" "this" {
  file_system_id       = aws_fsx_lustre_file_system.this.id
  data_repository_path = "s3://${aws_s3_bucket.data_repository.bucket}"
  file_system_path     = "/"

  s3 {
    auto_export_policy {
      events = ["NEW", "CHANGED", "DELETED"]
    }

    auto_import_policy {
      events = ["NEW", "CHANGED", "DELETED"]
    }
  }
}

provisioning_parameters.json

provisioning_parameters.json です。

Lustre をマウントするため、fsx_dns_namefsx_mountname を定義します。

s3.tf
###################################################
# Lifecycle Script Objects for SageMaker HyperPod Cluster
###################################################
resource "aws_s3_object" "life_cycle_scripts" {
  for_each = fileset("./config/", "**")
  bucket   = aws_s3_bucket.life_cycle_scripts.bucket
  key      = "config/${each.value}"
  source   = "./config/${each.value}"
}

resource "aws_s3_object" "life_cycle_scripts_provisioning_parameters" {
  bucket = aws_s3_bucket.life_cycle_scripts.bucket
  key    = "config/provisioning_parameters.json"

  content = jsonencode(
    {
      "version"          = "1.0.0"
      "workload_manager" = "slurm"
      "controller_group" = "controller-group"
      "worker_groups"    = []
+      "fsx_dns_name"     = aws_fsx_lustre_file_system.this.dns_name
+      "fsx_mountname"    = aws_fsx_lustre_file_system.this.mount_name
    }
  )
}

HyperPod

最後に HyperPod クラスターです。

Lustre 周り諸々依存関係を depends_on で明示的に定義してあげるのが重要なポイントです。

hyperpod.tf
###################################################
# SageMaker HyperPod Cluster
###################################################
resource "awscc_sagemaker_cluster" "this" {
  cluster_name = "${local.prefix}-cluster"

  instance_groups = [
    {
      execution_role      = aws_iam_role.hyperpod.arn
      instance_count      = 1
      instance_group_name = "controller-group"
      instance_type       = "ml.t3.medium"
      life_cycle_config = {
        source_s3_uri = "s3://${aws_s3_bucket.life_cycle_scripts.id}/config/"
        on_create     = "on_create.sh"
      }
    }
  ]

  vpc_config = {
    security_group_ids = [aws_security_group.hyperpod.id]
    subnets            = [module.vpc.private_subnets[0]]
  }

  depends_on = [
+    aws_iam_role_policy_attachment.hyperpod_vpc,
+    aws_iam_role_policy_attachment.hyperpod_s3,
+    aws_vpc_endpoint.s3,
+    aws_s3_object.life_cycle_scripts_provisioning_parameters
  ]
}

aws_iam_role_policy_attachment.hyperpod_s3sagemaker- から始まる S3 バケットではないためアタッチしています。

https://dev.classmethod.jp/articles/sagemaker-hyperpod-execution-role-s3-bucket-permissions/

接続確認

接続確認をしてみます。

aws ssm start-session \
  --target sagemaker-cluster:je7plekuheau_controller-group-i-02cb085c72ba19ca6

うまく繋がっていますね。

[cloudshell-user@ip-10-132-77-98 ~]$ aws ssm start-session \
> --target sagemaker-cluster:je7plekuheau_controller-group-i-02cb085c72ba19ca6

Starting session with SessionId: cm-takakuni.shinnosuke-fsfoepusyfrug2ch99lopbchtu
# whoami
root

/fsx 領域に 1.2T の大きさでマウントされてますね。

# df -h
Filesystem               Size  Used Avail Use% Mounted on
/dev/root                 97G   51G   46G  53% /
devtmpfs                 1.9G     0  1.9G   0% /dev
tmpfs                    1.9G     0  1.9G   0% /dev/shm
tmpfs                    386M  1.2M  385M   1% /run
tmpfs                    5.0M     0  5.0M   0% /run/lock
tmpfs                    1.9G     0  1.9G   0% /sys/fs/cgroup
/dev/loop0                64M   64M     0 100% /snap/core20/2379
/dev/loop2                75M   75M     0 100% /snap/core22/1621
/dev/loop1                64M   64M     0 100% /snap/core20/2434
/dev/loop5                92M   92M     0 100% /snap/lxd/29619
/dev/loop3                74M   74M     0 100% /snap/core22/1663
/dev/loop4                39M   39M     0 100% /snap/snapd/21759
/dev/nvme0n1p15          105M  6.1M   99M   6% /boot/efi
tmpfs                    386M   24K  386M   1% /run/user/128
10.0.1.35@tcp:/vtmdtbev  1.2T   38M  1.2T   1% /fsx
#

/fsx 領域に移動し、ファイルを作ってみました。

# cd /fsx
# ls
enroot  ubuntu
# echo hello! from hyperpod node! > hello.txt
# cat hello.txt
hello! from hyperpod node!
#

S3 からも確認できますね。

2024-12-16 at 17.33.35-hyprpd-lustre-hyperpod-data-622809842341 - S3 バケット  S3  ap-northeast-1.png

S3 からもオブジェクトアップロードしてみました。

hello_from_s3.txt
hello! from S3!

ノード側も出現してきました。中身も問題なく確認できますね。

# ls
enroot  hello.txt  hello_from_s3.txt  ubuntu
# cat hello_from_s3.txt
hello! from S3!#

まとめ

以上、「SageMaker HyperPod で FSx Lustre をマウントしてみた」でした。

Lustre はじめて触ったのですが S3 バケットを透過的に操作できる点、素晴らしいですね。

これからもマスターできるようゴリゴリ触っていきたいと思います。

AWS 事業本部コンサルティング部のたかくに(@takakuni_)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.