ParallelClusterを作成・管理するためのIAMロール作成方法 (ParallelCluster 2.11.0編)

ParallelClusterの作成で必要なIAMロール、IAMポリシーの作成方法をまとめました。
2021.07.20

先日、ParallelCluster 2.11.0がリリースされ、管理用EC2のIAMロール(IAMポリシー)を見直す機会がありました。ドキュメント通りにIAMポリシーの設定すると悲しい思いをしそうなので、ParallelClusterを作成するためのIAMポリシーの作り方を中心にまとめます。

ParallelClusterでクラスター環境を作成するためにはpclusterコマンドを利用します。pclusterコマンドのインストール先はローカル端末、EC2、Cloud9など、基本的にインストールできればどこでも大丈夫です。

ParallelCluster管理用のEC2を用意し、ParallelCluster作成に必要な権限をもったIAMロールをアタッチして管理する場合を例に必要なIAMロールの作成方法を紹介します。

現時点で最新のParallelCluster 2.11.0で必要な権限を付与しますが、今後のバージョンでも作り方に大きな違いはでないかと思います。

管理コマンドのインストール

pclusterコマンドの管理方法については以下を参照ください。

EC2に必要なIAMロール・IAMポリシー

ParallelClusterのクラスター作成、管理で必要なIAMポリシーは以下のドキュメントのParallelClusterUserPolicy using Slurmの通りなのですが...大きな問題があります。

一見、リージョン(REGION)、アカウントID(AWS ACCOUNT ID)、S3バケット名(RESOURCES S3 BUCKET)を置換すれば使えるように思えます。置換してからIAMポリシーを作成した結果が以下になります。

IAMポリシーの文字数制限を超えてしまい、IAMポリシーを作成できません。

...なん...だと...

これではクラウドHPCの利用を早々に断念してしまうかもしれません。

解決策

  • IAMポリシーの文字数制限を回避のためIAMポリシーを2分割にする
  • 1つのIAMロールに2つ分けたポリシーを紐付ける
  • 管理用のEC2インスタンスにIAMロール(IAMポリシーが2個付いている)をアタッチする

IAMポリシー作成

必要なポリシーのドキュメントをベースに適当なところで2分割したJSONファイルを作成します。サンプルとして私がParallelCluster 2.11.0用に利用しているIAMポリシーを載せます。アカウントID(123456789012)をご自身のアカウントIDに置換すればご利用頂けるかと思います。

1個目のJSONファイル

policy2110-1.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ec2:DescribeKeyPairs",
                "ec2:DescribeRegions",
                "ec2:DescribeVpcs",
                "ec2:DescribeSubnets",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribePlacementGroups",
                "ec2:DescribeImages",
                "ec2:DescribeInstances",
                "ec2:DescribeInstanceStatus",
                "ec2:DescribeInstanceTypes",
                "ec2:DescribeInstanceTypeOfferings",
                "ec2:DescribeSnapshots",
                "ec2:DescribeVolumes",
                "ec2:DescribeVpcAttribute",
                "ec2:DescribeAddresses",
                "ec2:CreateTags",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DescribeAvailabilityZones"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "EC2Describe"
        },
        {
            "Action": [
                "ec2:CreateVpc",
                "ec2:ModifyVpcAttribute",
                "ec2:DescribeNatGateways",
                "ec2:CreateNatGateway",
                "ec2:DescribeInternetGateways",
                "ec2:CreateInternetGateway",
                "ec2:AttachInternetGateway",
                "ec2:DescribeRouteTables",
                "ec2:CreateRoute",
                "ec2:CreateRouteTable",
                "ec2:AssociateRouteTable",
                "ec2:CreateSubnet",
                "ec2:ModifySubnetAttribute"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "NetworkingEasyConfig"
        },
        {
            "Action": [
                "ec2:CreateVolume",
                "ec2:RunInstances",
                "ec2:AllocateAddress",
                "ec2:AssociateAddress",
                "ec2:AttachNetworkInterface",
                "ec2:AuthorizeSecurityGroupEgress",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:CreateNetworkInterface",
                "ec2:CreateSecurityGroup",
                "ec2:ModifyVolumeAttribute",
                "ec2:ModifyNetworkInterfaceAttribute",
                "ec2:DeleteNetworkInterface",
                "ec2:DeleteVolume",
                "ec2:TerminateInstances",
                "ec2:DeleteSecurityGroup",
                "ec2:DisassociateAddress",
                "ec2:RevokeSecurityGroupIngress",
                "ec2:RevokeSecurityGroupEgress",
                "ec2:ReleaseAddress",
                "ec2:CreatePlacementGroup",
                "ec2:DeletePlacementGroup"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "EC2Modify"
        },
        {
            "Action": [
                "autoscaling:DescribeAutoScalingGroups",
                "autoscaling:DescribeAutoScalingInstances"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "AutoScalingDescribe"
        },
        {
            "Action": [
                "autoscaling:CreateAutoScalingGroup",
                "ec2:CreateLaunchTemplate",
                "ec2:CreateLaunchTemplateVersion",
                "ec2:ModifyLaunchTemplate",
                "ec2:DeleteLaunchTemplate",
                "ec2:DescribeLaunchTemplates",
                "ec2:DescribeLaunchTemplateVersions",
                "autoscaling:PutNotificationConfiguration",
                "autoscaling:UpdateAutoScalingGroup",
                "autoscaling:PutScalingPolicy",
                "autoscaling:DescribeScalingActivities",
                "autoscaling:DeleteAutoScalingGroup",
                "autoscaling:DeletePolicy",
                "autoscaling:DisableMetricsCollection",
                "autoscaling:EnableMetricsCollection"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "AutoScalingModify"
        },
        {
            "Action": [
                "dynamodb:DescribeTable",
                "dynamodb:ListTagsOfResource"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "DynamoDBDescribe"
        },
        {
            "Action": [
                "dynamodb:CreateTable",
                "dynamodb:DeleteTable",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Query",
                "dynamodb:TagResource"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "DynamoDBModify"
        },
        {
            "Action": [
                "route53:ChangeResourceRecordSets",
                "route53:ChangeTagsForResource",
                "route53:CreateHostedZone",
                "route53:DeleteHostedZone",
                "route53:GetChange",
                "route53:GetHostedZone",
                "route53:ListResourceRecordSets",
                "route53:ListQueryLoggingConfigs"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "Route53HostedZones"
        },
        {
            "Action": [
                "sqs:GetQueueAttributes"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "SQSDescribe"
        },
        {
            "Action": [
                "sqs:CreateQueue",
                "sqs:SetQueueAttributes",
                "sqs:DeleteQueue",
                "sqs:TagQueue"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "SQSModify"
        },
        {
            "Action": [
                "sns:ListTopics",
                "sns:GetTopicAttributes"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "SNSDescribe"
        },
        {
            "Action": [
                "sns:CreateTopic",
                "sns:Subscribe",
                "sns:Unsubscribe",
                "sns:DeleteTopic"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "SNSModify"
        }
    ]
}
2個目のJSONファイル

policy2110-2.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "cloudformation:DescribeStackEvents",
                "cloudformation:DescribeStackResource",
                "cloudformation:DescribeStackResources",
                "cloudformation:DescribeStacks",
                "cloudformation:ListStacks",
                "cloudformation:GetTemplate"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "CloudFormationDescribe"
        },
        {
            "Action": [
                "cloudformation:CreateStack",
                "cloudformation:DeleteStack",
                "cloudformation:UpdateStack"
            ],
            "Effect": "Allow",
            "Resource": "*",
            "Sid": "CloudFormationModify"
        },
        {
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::*"
            ],
            "Effect": "Allow",
            "Sid": "S3ResourcesBucket"
        },
        {
            "Action": [
                "s3:Get*",
                "s3:List*"
            ],
            "Resource": [
                "arn:aws:s3:::ap-northeast-1-aws-parallelcluster*"
            ],
            "Effect": "Allow",
            "Sid": "S3ParallelClusterReadOnly"
        },
        {
            "Action": [
                "s3:DeleteBucket",
                "s3:DeleteObject",
                "s3:DeleteObjectVersion"
            ],
            "Resource": [
                "arn:aws:s3:::*"
            ],
            "Effect": "Allow",
            "Sid": "S3Delete"
        },
        {
            "Action": [
                "iam:PassRole",
                "iam:CreateRole",
                "iam:CreateServiceLinkedRole",
                "iam:DeleteRole",
                "iam:GetRole",
                "iam:TagRole",
                "iam:SimulatePrincipalPolicy"
            ],
            "Resource": [
                "arn:aws:iam::123456789012:role/ParallelClusterUserRole",
                "arn:aws:iam::123456789012:role/parallelcluster-*",
                "arn:aws:iam::123456789012:role/aws-service-role/*"
            ],
            "Effect": "Allow",
            "Sid": "IAMModify"
        },
        {
            "Action": [
                "iam:CreateInstanceProfile",
                "iam:DeleteInstanceProfile"
            ],
            "Resource": "arn:aws:iam::123456789012:instance-profile/*",
            "Effect": "Allow",
            "Sid": "IAMCreateInstanceProfile"
        },
        {
            "Action": [
                "iam:AddRoleToInstanceProfile",
                "iam:RemoveRoleFromInstanceProfile",
                "iam:GetRolePolicy",
                "iam:GetPolicy",
                "iam:AttachRolePolicy",
                "iam:DetachRolePolicy",
                "iam:PutRolePolicy",
                "iam:DeleteRolePolicy"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "IAMInstanceProfile"
        },
        {
            "Action": [
                "elasticfilesystem:DescribeMountTargets",
                "elasticfilesystem:DescribeMountTargetSecurityGroups",
                "ec2:DescribeNetworkInterfaceAttribute"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "EFSDescribe"
        },
        {
            "Action": [
                "ssm:GetParametersByPath"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "SSMDescribe"
        },
        {
            "Action": [
                "fsx:*"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "FSx"
        },
        {
            "Action": [
                "elasticfilesystem:*"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "EFS"
        },
        {
            "Action": [
                "logs:DeleteLogGroup",
                "logs:PutRetentionPolicy",
                "logs:DescribeLogGroups",
                "logs:CreateLogGroup"
            ],
            "Resource": "*",
            "Effect": "Allow",
            "Sid": "CloudWatchLogs"
        },
        {
            "Action": [
                "lambda:CreateFunction",
                "lambda:DeleteFunction",
                "lambda:GetFunctionConfiguration",
                "lambda:GetFunction",
                "lambda:InvokeFunction",
                "lambda:AddPermission",
                "lambda:RemovePermission"
            ],
            "Resource": [
                "arn:aws:lambda:ap-northeast-1:123456789012:function:parallelcluster-*",
                "arn:aws:lambda:ap-northeast-1:123456789012:function:pcluster-*"
            ],
            "Effect": "Allow",
            "Sid": "Lambda"
        },
        {
            "Sid": "CloudWatch",
            "Effect": "Allow",
            "Action": [
                "cloudwatch:PutDashboard",
                "cloudwatch:ListDashboards",
                "cloudwatch:DeleteDashboards",
                "cloudwatch:GetDashboard"
            ],
            "Resource": "*"
        }
    ]
}

policy2110-1.jsonと、policy2110-2.jsonのファイル名で上記ファイルを保存しました。そのファイルを元にAWS CLIでIAMポリシーを作成します。

1個目のファイル指定

aws iam create-policy \
  --policy-name  ParallelClusterUserPolicy2110-1 \
  --description "for ParallelCluster 2.11.0 1/2" \
  --policy-document file://policy2110-1.json

2個目のファイル指定

aws iam create-policy \
  --policy-name  ParallelClusterUserPolicy2110-2 \
  --description "for ParallelCluster 2.11.0 2/2" \
  --policy-document file://policy2110-2.json

IAMポリシーが2つ作成できました。

IAMロール作成

IAMロールはAWS CLIよりマネジメントコンソールで作成した方が楽そうなのでマネコンからの操作で紹介します。

EC2を選択して進めます。

さきほどAWS CLIで作成したIAMポリシー名を検索し、2個ともチェックを入れます。

タグは未指定で進めても問題はありません。

IAMロール名、説明に対象のParallelClusterバージョン名を入れておくと後々管理しやすいです。最後にIAMポリシーの名前に間違いないか確認しておきましょう。

IAMロールの完成です。

EC2インスタンスへIAMロールをアタッチ

対象のインスタンスを右クリックしてIAMロールを変更します。

IAMロールが対象のEC2インスタンスへ反映されるまで時間かかる場合があります。私の環境はpclusterコマンドでクラスターの管理しかしていなく、誰にも迷惑かけないのでインスタンスを再起動して即反映させています。

以上でParallelCluster 2.11.0に対応した必要な権限を持つEC2インスタンスの完成です。

おわりに

AWSに慣れている方ならIAMポリシーの作成でつまづかないと思うのですが、ParallelClusterを使いたい方がみんなAWSに慣れているかと言われると厳しい気がします。どなたかのお役に立てれば幸いです。

参考