データ分析

Dataproc 協調型マルチテナンシー

※この投稿は米国時間 2020 年 11 月 11 日に、Google Cloud blog に投稿されたものの抄訳です。

Dataproc では、データ アナリストが BI ワークロードを実行し、ダッシュボード、レポート、分析情報の生成が行えます。さまざまなチームのデータ アナリストがデータを分析して、多様なレポート、ダッシュボード、分析情報を生成しており、Dataproc ワークロードに対するマルチテナンシーへの需要は高まりつつあります。現在、クラスタ上のすべてのユーザーのワークロードは単一のサービス アカウントとして実行されるため、すべてのワークロードが同じデータアクセスを共有します。Dataproc 協調型マルチテナンシーを使用すると、データアクセスの異なる複数のユーザーが同じクラスタでワークロードを実行できます。

Dataproc クラスタでは通常、ワークロードをクラスタ サービス アカウントとして実行します。Dataproc 協調型マルチテナンシーを使用して Dataproc クラスタを作成すると、Cloud Storage リソースにアクセスするジョブの実行時にユーザー ID を分離できます。サービス アカウントへの Cloud IAM ユーザーのマッピングはクラスタの作成時に指定され、任意のクラスタに対して多くのサービス アカウントを構成することができます。これは、Cloud Storage とのインタラクションが、クラスタ サービス アカウントではなく、ジョブを送信するユーザーにマッピングされるサービス アカウントとして認証されることを意味します。

考慮事項

Dataproc 協調型マルチテナンシーでは以下を考慮する必要があります。

  • dataproc:ataproc.cooperative.multi-tenancy.user.mapping プロパティを有効にして、Cloud IAM ユーザーのサービス アカウントへのマッピングを設定します。ユーザーがクラスタにジョブを送信すると、VM サービス アカウントはこのユーザーにマッピングされたサービス アカウントとして動作し、GCS コネクタを介してそのサービス アカウントとして Cloud Storage とインタラクションを行います。

  • GCS コネクタのバージョンは、少なくとも 2 1 4 である必要があります。

  • Kerberos が有効なクラスタはサポートされていません。

  • Dataproc Jobs API を介して送信されたジョブのみを対象としています。

目標

このブログでは、以下の項目について説明します。

  • Dataproc 協調型マルチテナンシーを有効にして Dataproc クラスタを作成する

  • さまざまなユーザー ID を使用してジョブをクラスタに送信し、Cloud Storage とのインタラクション発生時に適用されるさまざまなアクセスルールを識別およびモニターする

Stackdriver Logging を使用して、さまざまなサービス アカウントで Cloud Storage とのインタラクションが認証されることを確認

始める前に

プロジェクトを作成する

1.Cloud Console のプロジェクト セレクタページで、Cloud プロジェクトを選択または作成します。

2.Google Cloud プロジェクトの課金が有効になっていることを確認します。プロジェクトに対して課金が有効になっていることを確認する方法

3.Dataproc API を有効にします。

4.Stackdriver API を有効にします。

5.Cloud SDK をインストールし、初期化します。

2 番目のユーザーをシミュレートする

通常、2 番目のユーザーとして別のユーザーがいますが、さらに別のサービス アカウントを使用して 2 番目のユーザーをシミュレートすることもできます。異なるユーザーとしてクラスタにジョブを送信するので、2 番目のユーザーをシミュレートするために gcloud 設定でサービス アカウントをアクティブ化できます。

  • まず、gcloud で現在アクティブ化されているアカウントを取得します。ほとんどの場合、これはユーザーの個人アカウントです。

  FIRST_USER=$(gcloud auth list --filter=status:ACTIVE --format="value(account)")

  • サービス アカウントを作成します。

  PROJECT_ID=<your-project-id>
SECOND_USER_SA_NAME=<name-of-service-account-to-simulate-second-user>
gcloud iam service-accounts create ${SECOND_USER_SA_NAME}
SECOND_USER=${SECOND_USER_SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

  • Dataproc クラスタにジョブ送信するための適切なアクセス許可をサービス アカウントに付与します。

  gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${SECOND_USER} --role roles/dataproc.editor
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${SECOND_USER} --role roles/storage.admin

サービス アカウントのキーを作成し、そのキーを使用して gcloud でアクティブ化します。キーファイルは、サービス アカウントがアクティブ化されたら削除できます。

  gcloud iam service-accounts keys create  ./key.json --iam-account ${SECOND_USER}
gcloud auth activate-service-account ${SECOND_USER} --key-file ./key.json
rm ./key.json

ここで、次のコマンドを実行します。

  gcloud auth list --filter=status:ACTIVE --format="value(account)"

すると、このサービス アカウントがアクティブ アカウントとして表示されます。以下の例に進むには、元のアクティブ アカウントに切り替えてください。

  gcloud config set account ${FIRST_USER}

サービス アカウントを構成する

  • 3 つのサービス アカウントを追加で作成します。1 つは Dataproc VM サービス アカウントとして、残りの 2 つはユーザーにマッピングするサービス アカウント(ユーザー サービス アカウント)として作成します。注: クラスタごとに VM サービス アカウントを使用し、特定のクラスタで使用する予定のユーザー サービス アカウントにのみ元のアカウントの代わりになることを許可するようおすすめします。

  VM_SA_NAME=<vm-service-account-name>
USER_SA_ALLOW_NAME=<user-service-account-with-gcs-access>
USER_SA_DENY_NAME=<user-service-account-without-gcs-access>
gcloud iam service-accounts create ${VM_SA_NAME}
gcloud iam service-accounts create ${USER_SA_ALLOW_NAME}
gcloud iam service-accounts create ${USER_SA_DENY_NAME}
VM_SA=${VM_SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
USER_SA_ALLOW=${USER_SA_ALLOW_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
USER_SA_DENY=${USER_SA_DENY_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

2 つのユーザー サービス アカウントに対する VM サービス アカウントに iam.serviceAccountTokenCreator ロールを付与して、元のアカウントの代わりとなるようにします。

  gcloud iam service-accounts add-iam-policy-binding \
    ${USER_SA_ALLOW} \
    --member serviceAccount:${VM_SA} \
    --role roles/iam.serviceAccountTokenCreator

および

  gcloud iam service-accounts add-iam-policy-binding \
    ${USER_SA_DENY} \
    --member serviceAccount:${VM_SA} \
    --role roles/iam.serviceAccountTokenCreator

クラスタ VM で必要なジョブを実行できるように、VM サービス アカウントに dataproc.worker ロールを付与します。

  gcloud projects add-iam-policy-binding \
    ${PROJECT_ID} \
    --member serviceAccount:${VM_SA} \
    --role roles/dataproc.worker

Cloud Storage のリソースを作成しサービス アカウントを構成する

  • バケットを作成します。

  BUCKET=<your bucket name>
gsutil mb gs://${BUCKET}

  • バケットに簡単なファイルを書き込みます。

  echo "This is a simple file" | gsutil cp - gs://${BUCKET}/file

1 番目のユーザー サービス アカウントにのみバケットへの管理者権限である USER_SA_ALLOW を付与します。

  gsutil iam ch serviceAccount:${USER_SA_ALLOW}:admin gs://${BUCKET}

クラスタを作成し、サービス アカウントを構成する

  • この例では、ユーザー「FIRST_USER」(個人ユーザー)を GCS 管理者権限を持つサービス アカウントにマッピングし、ユーザー「SECOND_USER」(サービス アカウントとしてシミュレート)を GCS アクセスを持たないサービス アカウントにマッピングします。

  • 協調型マルチテナンシーが使用できるのは、バージョン 2.1.4 以降の GCS コネクタのみである点に注意してください。これは Dataproc イメージのバージョン 1.5.11 以降にプリインストールされていますが、コネクタの初期化アクションを使用すると、古い Dataproc イメージに特定のバージョンの GCS コネクタをインストールできます。

  • VM サービス アカウントは、generateAccessToken API を呼び出して、ジョブ サービス アカウントのアクセス トークンをフェッチする必要があるため、クラスタに適切なスコープがあることを確認してください。以下の例では、クラウド プラットフォーム スコープを使用します。

  CLUSTER_NAME=<cluster-name>
REGION=us-central1
gcloud dataproc clusters create ${CLUSTER_NAME} \
    --region ${REGION} \
    --image-version=1.5-debian10 \
    --scopes=cloud-platform \
    --service-account=${VM_SA} \
    --properties="^#^dataproc:dataproc.cooperative.multi-tenancy.user.mapping=${FIRST_USER}:${USER_SA_ALLOW},${SECOND_USER}:${USER_SA_DENY}"

注: 

1. ユーザー サービス アカウントは、ジョブを実行するためクラスタに関連付けられた構成バケットへのアクセス権が必要となる場合があるため、必ずユーザー サービス アカウントにアクセス権を付与してください。

  configBucket=$(gcloud dataproc clusters describe ${CLUSTER_NAME} --region ${REGION} \
    --format="value(config.configBucket)")
gsutil iam ch serviceAccount:${USER_SA_ALLOW}:admin gs://${configBucket}
gsutil iam ch serviceAccount:${USER_SA_DENY}:admin gs://${configBucket}

2. 1.5+ のイメージを含む Dataproc クラスタでは、デフォルトで、Spark と MapReduce の履歴ファイルがクラスタに関連付けられた一時バケットに送信されるため、ユーザー サービス アカウントにこのバケットへのアクセスを付与しておくとよいでしょう。

  tempBucket=$(gcloud dataproc clusters describe ${CLUSTER_NAME} --region ${REGION} \
    --format="value(config.tempBucket)")
gsutil iam ch serviceAccount:${USER_SA_ALLOW}:admin gs://${tempBucket}
gsutil iam ch serviceAccount:${USER_SA_DENY}:admin gs://${tempBucket}

サンプルジョブを実行する

Spark ジョブを「FIRST_USER」として実行すると、マッピングされたサービス アカウントが GCS ファイルの gs://${BUCKET}/file にアクセスできるため、ジョブは成功します。

  gcloud config set account ${FIRST_USER}
gcloud dataproc jobs submit spark --region=${REGION} --cluster=${CLUSTER_NAME} \
     --class=org.apache.spark.examples.JavaWordCount \
    --jars=file:///usr/lib/spark/examples/jars/spark-examples.jar -- gs://${BUCKET}/file

ジョブが正常に実行されると、次のような出力が得られます。

  is: 1
a: 1
simple: 1
This: 1
file: 1
...
Job [752712...] finished successfully.
done: true

ここで、「SECOND_USER」と同じジョブを実行します。マッピングされたサービス アカウントが GCS ファイル gs://${BUCKET}/file にアクセスできないことからジョブが失敗し、ドライバの出力には原因として権限エラーが示されます。

  gcloud config set account ${SECOND_USER}
gcloud dataproc jobs submit spark --region=${REGION} --cluster=${CLUSTER_NAME} \
     --class=org.apache.spark.examples.JavaWordCount \
    --jars=file:///usr/lib/spark/examples/jars/spark-examples.jar -- gs://${BUCKET}/file

また、ジョブドライバは、使用されているサービス アカウントに GCS ファイルへの storage.get.access がないことが原因であることも示します。

  GET https://storage.googleapis.com/storage/v1/b/<BUCKET>/o/file
{
  "code" : 403,
  "errors" : [ {
    "domain" : "global",
    "message" : "[USER_SA_DENY] does not have storage.objects.get access to the Google Cloud Storage object.",
    "reason" : "forbidden"
  } ],
  "message" : "[USER_SA_DENY] does not have storage.objects.get access to the Google Cloud Storage object."
}

同様に、Hive ジョブ(GCS で外部テーブルを作成し、レコードを挿入してからレコードを読み取る)で、ユーザー「FIRST_USER」として以下を実行すると、マッピングされたサービス アカウントがバケット <BUCKET> にアクセスできるため成功します。

  gcloud config set account ${FIRST_USER}
gcloud dataproc jobs submit hive --region=${REGION} --cluster=${CLUSTER_NAME} \
  -e "create external table if not exists employee (eid int, name String) location 'gs://${BUCKET}/employee'; insert into employee values (1, 'alice'), (2, 'bob');select * from employee;"

...
Connecting to jdbc:hive2://<CLUSTER_NAME>-m:10000
Connected to: Apache Hive (version 2.3.7)
Driver: Hive JDBC (version 2.3.7)
Transaction isolation: TRANSACTION_REPEATABLE_READ
No rows affected (0.538 seconds)
No rows affected (27.668 seconds)
+---------------+----------------+
| employee.eid  | employee.name  |
+---------------+----------------+
| 1             | alice          |
| 2             | bob            |
+---------------+----------------+
2 rows selected (1.962 seconds)
Beeline version 2.3.7 by Apache Hive
Closing: 0: jdbc:hive2://<CLUSTER_NAME>-m:10000
Job [ea9acf13205a44dd...] finished successfully.
done: true
...

ただし、テーブル employee を別のユーザー 「SECOND_USER」としてクエリすると、ジョブはバケットにアクセスできない 2 番目のユーザー サービス アカウントを使用するので、ジョブは失敗します。

  gcloud config set account ${SECOND_USER}
gcloud dataproc jobs submit hive --region=${REGION} --cluster=${CLUSTER_NAME} \
  -e "select * from employee;"

GET https://storage.googleapis.com/storage/v1/b/<BUCKET>/o/employee
{
  "code" : 403,
  "errors" : [ {
    "domain" : "global",
    "message" : "[USER_SA_DENY] does not have storage.objects.get access to the Google Cloud Storage object.",
    "reason" : "forbidden"
  } ],
  "message" : "[USER_SA_DENY] does not have storage.objects.get access to the Google Cloud Storage object."
}

Stackdriver Logging で、Cloud Storage でのサービス アカウント認証を確認する

まず、バケットにアクセスできる 1 番目のサービス アカウントの使用状況を確認します。

  • gcloud のアクティブ アカウントが個人アカウントであることを確認します。

  gcloud config set account ${FIRST_USER}

  • GCS 権限を持つサービス アカウントを使用してバケットへのアクセスに関するログを検索します。

  gcloud logging read "resource.type=\"gcs_bucket\" AND resource.labels.bucket_name=\"${BUCKET}\" AND protoPayload.authenticationInfo.principalEmail=\"${USER_SA_ALLOW}\""

アクセス許可が常に付与されていることが確認できます。

protoPayload: '@type': type.googleapis.com/google.cloud.audit.AuditLog authenticationInfo: principalEmail: [USER_SA_ALLOW] ... authorizationInfo: - granted: true permission: storage.objects.get resource: projects/_/buckets/[BUCKET]/objects/file

resourceAttributes: {}

バケットにアクセスできないサービス アカウントを確認します。

  gcloud logging read "resource.type=\"gcs_bucket\" AND resource.labels.bucket_name=\"${BUCKET}\"  AND protoPayload.authenticationInfo.principalEmail=\"${USER_SA_DENY}\""

アクセス許可が付与されていないことが確認できます。

  protoPayload:
 '@type': type.googleapis.com/google.cloud.audit.AuditLog
  authenticationInfo:
    principalEmail: [USER_SA_DENY]
    ...
  authorizationInfo:
  - permission: storage.objects.get
    resource: projects/_/buckets/[BUCKET]/objects/employee
    resourceAttributes: {}
  - permission: storage.objects.list
    resource: projects/_/buckets/[BUCKET]
    resourceAttributes: {}

VM サービス アカウントがバケットへのアクセスに直接使用されたことのないことが確認できます(次の gcloud コマンドで 0 のログエントリが返される)。

  gcloud logging read "resource.type=\"gcs_bucket\" AND resource.labels.bucket_name=\"${BUCKET}\" AND protoPayload.authenticationInfo.principalEmail=\"${VM_SA}\""

クリーンアップ

  • クラスタを削除します。

  gcloud dataproc clusters delete ${CLUSTER_NAME} --region ${REGION} --quiet

  • バケットを削除します。

  gsutil rm -r gs://${BUCKET}

  • 2 番目のユーザーのシミュレーションに使用されたサービス アカウントを非アクティブ化します。

  gcloud auth revoke ${SECOND_USER}

  • サービス アカウントを削除します。

  gcloud iam service-accounts delete ${SECOND_USER} --quiet
gcloud iam service-accounts delete ${VM_SA} --quiet
gcloud iam service-accounts delete ${USER_SA_ALLOW} --quiet
gcloud iam service-accounts delete ${USER_SA_DENY} --quiet

1.協調型マルチテナンシー機能は、Kerberos が有効なクラスタではまだ利用できません。

2.サービス アカウントがマッピングされていないユーザーが送信したジョブは、GCS リソースへのアクセス時にフォールバックして VM サービス アカウントを使用します。ただし、`core:fs.gs.auth.impersonation.service.account` プロパティを設定して、フォールバックするときのサービス アカウントを変更できます。このフォールバック サービス アカウントのアクセス トークンをフェッチするため、VM サービス アカウントは `generateAccessToken` を呼び出す必要があります。

以上、このブログでは、Dataproc 協調型マルチテナンシーを使用して、複数のユーザー間で Dataproc クラスタを共有する方法について説明しました。

-データ分析担当プロダクト マネージャー Susheel Kaushik

-ソフトウェア エンジニア Chao Yuan