あるアカウントでS3からデータを読み取り、別のアカウントに書き込む必要があるEMR Sparkジョブがあります。
仕事を2つのステップに分けました。
s3からデータを読み取ります(私のEMRクラスターは同じアカウントにあるため、資格情報は必要ありません)。
手順1で作成したローカルHDFSのデータを読み取り、別のアカウントのS3バケットに書き込みます。
hadoopConfiguration
を設定しようとしました:
sc.hadoopConfiguration.set("fs.s3n.awsAccessKeyId", "<your access key>")
sc.hadoopConfiguration.set("fs.s3n.awsSecretAccessKey","<your secretkey>")
そして、クラスター上のキーをエクスポートします。
$ export AWS_SECRET_ACCESS_KEY=
$ export AWS_ACCESS_KEY_ID=
clusterモードとclientモードの両方、およびspark-Shellを試しましたが運がありませんでした。
それらのそれぞれがエラーを返します:
ERROR ApplicationMaster: User class threw exception: com.Amazon.ws.emr.hadoop.fs.shaded.com.amazonaws.services.s3.model.AmazonS3Exception:
Access Denied
解決策は実際には非常に簡単です。
まず、EMRクラスターには2つの役割があります。
EMR_DefaultRole
)EMRサービスにアクセス許可を付与します(例:Amazon EC2インスタンスの起動用)EMR_EC2_DefaultRole
)( IAMロールを使用して実行中のアプリケーションにアクセス許可を付与するを参照) Amazon EC2インスタンスの場合 )これらのロールについては、次のように説明されています。 Amazon EMRのデフォルトのIAMロール
したがって、クラスターで起動された各EC2インスタンスにはEMR_EC2_DefaultRole
ロールが割り当てられます。これにより、インスタンスメタデータサービスを介して一時的な認証情報を利用できるようになります。 (これがどのように機能するかの説明については、以下を参照してください: Amazon EC2のIAMロール 。)Amazon EMRノードは、これらの資格情報を使用して、S3、SNS、SQS、CloudWatch、DynamoDBなどのAWSサービスにアクセスします。
次に、EMR_EC2_DefaultRole
ロールを介したアクセスを許可するには、他のアカウントのAmazonS3バケットにアクセス許可を追加する必要があります。これは、次のようにS3バケット(ここではother-account-bucket
という名前)にバケットポリシーを追加することで実行できます。
{
"Id": "Policy1",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1",
"Action": "s3:*",
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::other-account-bucket",
"arn:aws:s3:::other-account-bucket/*"
],
"Principal": {
"AWS": [
"arn:aws:iam::ACCOUNT-NUMBER:role/EMR_EC2_DefaultRole"
]
}
}
]
}
このポリシーは、ポリシー内のs3:*
に一致するアカウント(EMRクラスターが起動されたアカウントである必要があります)に属するEMR_EC2_DefaultRole
ロールにすべてのS3アクセス許可(ACCOUNT-NUMBER
)を付与します。このようなアクセス許可を付与するときは注意してください。すべてのS3アクセス許可を付与するのではなく、GetObject
にのみアクセス許可を付与することをお勧めします。
これですべてです!他のアカウントのバケットは、EMRノードがEMR_EC2_DefaultRole
ロールを使用しているため、EMRノードからのリクエストを受け入れるようになりました。
免責事項:アカウントAでバケットを作成し、アカウントBのロールに権限(上記のとおり)を割り当てることで、上記をテストしました。 EC2インスタンスは、そのロールでAccount-Bで起動されました。 AWSコマンドラインインターフェイス(CLI) を介してEC2インスタンスからバケットにアクセスできました。私はEMR内でそれをテストしませんでした、しかしそれは同じように動作するはずです。
sparkを使用すると、assume roleを使用して別のアカウントのs3バケットにアクセスできますが、他のアカウントでIAM Roleを使用できます。これにより、他のアカウント所有者がに提供されたアクセス許可を管理しやすくなります。 sparkジョブ。s3バケットポリシーを介したアクセスの管理は、アクセス権が単一のIAMロールにすべて含まれているのではなく、複数の場所に分散されているため、面倒な場合があります。
これがhadoopConfiguration
です:
"fs.s3a.credentialsType" -> "AssumeRole",
"fs.s3a.stsAssumeRole.arn" -> "arn:aws:iam::<<AWSAccount>>:role/<<crossaccount-role>>",
"fs.s3a.impl" -> "com.databricks.s3a.S3AFileSystem",
"spark.hadoop.fs.s3a.server-side-encryption-algorithm" -> "aws:kms",
"spark.hadoop.fs.s3a.server-side-encryption-kms-master-key-id" -> "arn:aws:kms:ap-southeast-2:<<AWSAccount>>:key/<<KMS Key ID>>"
外部IDはパスフレーズとしても使用できます。
"spark.hadoop.fs.s3a.stsAssumeRole.externalId" -> "GUID created by other account owner"
上記のデータブリックスを使用していましたが、EMRの使用はまだ試していません。
コンピューティングノードにIAMロールを割り当てる必要があると思います(おそらくすでにこれを行っています)。次に、「リモート」アカウントのIAMを介してそのロールへのクロスアカウントアクセスを許可します。詳細については、 http://docs.aws.Amazon.com/IAM/latest/UserGuide/tutorial_cross-account-with-roles.html を参照してください。