DatabricksでCustemer Managed VPCにClusterをプロビジョニングする
ナカヤマです。
DatabricksというSaaSサービスでユーザーの所有するAWSアカウントにあるVPCにClusterをデプロイしてみました。 その流れを記録します。
Databricksとは?
Databricksは、データサイエンティスト・データエンジニア・ビジネスアナリストのためのLakehouse Platformです。
Databricks - The Data and AI Company
このサービスがどのような課題を解決するかについては、AWS Summit Online (2021) のプレゼンテーションを見て頂くのが早いと思いますので、ここでは割愛します。
PAR-25 データ分析におけるデータサイロ、機械学習モデル管理、BI の制限 − これらの課題を解決します!
Databricksは、並列分散処理を行うワーカーノードやドライバーノード、Delta lakeを活用したデータレイクのプロビジョニングや管理等を行います。 その他、可視化やデータへのアクセス手段を提供するためのWorkspaceや機械学習モデルを管理するための仕組みも提供しています。
Databricks Data Science & Engineering concepts
このワーカーノードやドライバーノードによって構成されるクラスターはユーザーの所有するAWSアカウント上に、なおかつ既存のVPCにプロビジョニングすることも可能です。 既存のデータソースを活用したり、Amazon Kinesisと連携したストリーミングデータの取り扱いが可能なようです。
この記事では、Databricksアカウントのサインアップからユーザーが独自に作成したVPCにクラスターを作成して簡単なクエリを実行するまでの手順を確認したいと思います。
やってみた
大まかな手順は以下の通りです。
- アカウントのサインアップ
- Network configurationの作成
- Credential configurationの作成
- Storage configurationの作成
- Workspaceの作成
- Clusterの作成
- Notebookの作成
- 動作確認
アカウントのサインアップ
まずはDatabricksアカウントのサインアップを行います。 Databricksは、無料で2週間のトライアルを行うことが可能です。
DatabricksのWebサイトにアクセスし、TRY DATABRICKSボタンをクリックします。
必要事項を入力します。
有償版の機能を利用する場合は、どのクラウドプラットフォームを利用するか選択します。 今回はAWSを選択します。
選択すると設定したメールアドレスにメールが届くため、リンクをクリックしてパスワードを設定します。
パスワード設定後、プランを選択します。トライアル中はいずれのプランでも無料で使えるようです。 今回は最上位のEnterpriseを選択します。
これでサインアップは完了です。
Network configurationの作成
次に、ノードをプロビジョニングするVPCを指定します。
必要事項を入力するダイアログには、VPCが満たすべき要件が記載されています。 現時点ではプロビジョニング先のVPCがないので、要件を確認しつつVPCを作成したいと思います。
このドキュメントを確認すると、DataBricksにVPCの作成を含めて委任するパターン(Databricks-Managed VPC)とユーザーの管理下にあるVPCを利用する2つのパターンがあるようです。 今回は後者のパターンで作業を進めます。
VPCが満たすべき要件は以下のリンク先を確認してください。
この要件を踏まえてVPCおよび関連リソースを作成します。 手作業はめんどくさいので、今回はCloudFormationで作成します。 以下のテンプレートを作成の上でリソースをプロビジョニングしました。
AWSTemplateFormatVersion: "2010-09-09" Description: A sample template for Databricks (Customer-managed VPC / for ap-northeast-1) Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: Databricks-VPC IGW: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: Databricks-IGW AttachIGW: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref VPC InternetGatewayId: !Ref IGW PrivateSubnetA: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a VpcId: !Ref VPC CidrBlock: 10.0.0.0/24 Tags: - Key: Name Value: Databricks-Private-Subnet-a PrivateSubnetC: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1c VpcId: !Ref VPC CidrBlock: 10.0.1.0/24 Tags: - Key: Name Value: Databricks-Private-Subnet-c PublicSubnetA: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a VpcId: !Ref VPC CidrBlock: 10.0.2.0/24 Tags: - Key: Name Value: Databricks-Public-Subnet-a PublicSubnetC: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1c VpcId: !Ref VPC CidrBlock: 10.0.3.0/24 Tags: - Key: Name Value: Databricks-Public-Subnet-c EIPA: Type: AWS::EC2::EIP Properties: Domain: vpc NATGWA: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt EIPA.AllocationId ConnectivityType: public SubnetId: !Ref PublicSubnetA Tags: - Key: Name Value: Databricks-NATGW-a EIPC: Type: AWS::EC2::EIP Properties: Domain: vpc NATGWC: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt EIPC.AllocationId ConnectivityType: public SubnetId: !Ref PublicSubnetC Tags: - Key: Name Value: Databricks-NATGW-c PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: Databricks-Public-RouteTable PrivateRouteTableA: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: Databricks-Private-RouteTable-a PrivateRouteTableC: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: Databricks-Private-RouteTable-c PublicDefaultRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref IGW PrivateDefaultRouteA: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTableA DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NATGWA PrivateDefaultRouteC: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTableC DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NATGWC PublicRouteTableAssocA: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnetA RouteTableId: !Ref PublicRouteTable PublicRouteTableAssocC: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnetC RouteTableId: !Ref PublicRouteTable PrivateRouteTableAssocA: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnetA RouteTableId: !Ref PrivateRouteTableA PrivateRouteTableAssocC: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnetC RouteTableId: !Ref PrivateRouteTableC SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: DataBricks-SecurityGroup GroupDescription: "Databricks SecurityGroup" VpcId: !Ref VPC Tags: - Key: Name Value: Databricks-SecurityGroup Outputs: VPCID: Value: !Ref VPC Export: Name: DataBricksVPCID PrivateSubnerIDA: Value: !Ref PrivateSubnetA Export: Name: DataBricksPrivateSubnetIDA PrivateSubnerIDC: Value: !Ref PrivateSubnetC Export: Name: DataBricksPrivateSubnetIDC SecurityGroupID: Value: !Ref SecurityGroup Export: Name: DatabricksSecurityGroupID
(要件に応じてVPC Endpointを構成するケースもありますが、今回は割愛します)
スタックの作成が完了したら、リソースIDを設定します。
Network Configurationは以上です。
Credential configurationの作成
次にCredential configurationを作成します。
DatabricksはユーザーのAWSアカウントにクラスターをプロビジョニングするなどコントロールプレーンとして機能します。 ここではそのために必要な権限を設定します。 具体的にはDatabricksの所有する特定のAWSアカウントに対して権限を委任します。
まず、以下の画面に遷移して設定を開始します。
すると、IAM Roleに設定するための外部IDが表示されます。
この外部IDを利用してIAM Roleを作成します。 作成手順は以下のドキュメントを参照してください(作成手順自体は2つめのリンク先に記載されています)。
Manage delegated credential configurations using the account console (E2)
Create a cross-account IAM role
信頼するAWSアカウントはドキュメントに記載されたものを指定します。 また、付与する権限は [Your VPC, custom] を利用してください。 ここに記載されているポリシーは、一部の変数をユーザーが置き換える必要があります。
- ACCOUNTID
- クラスターやデータレイクをプロビジョニングするAWSアカウント
- VPCID
- クラスターをプロビジョニングするVPC ID(CloudFormationでプロビジョニングしたVPC)
- REGION
- クラスターやデータレイクをプロビジョニングするAWSリージョン
- SECURITYGROUPID
- VPC上のノードに割り当てるSecurity Group
置き換えた結果は以下の通りです(ACCOUNTIDのみ伏せています)。
{ "Version":"2012-10-17", "Statement":[ { "Sid":"NonResourceBasedPermissions", "Effect":"Allow", "Action":[ "ec2:CancelSpotInstanceRequests", "ec2:DescribeAvailabilityZones", "ec2:DescribeIamInstanceProfileAssociations", "ec2:DescribeInstanceStatus", "ec2:DescribeInstances", "ec2:DescribeInternetGateways", "ec2:DescribeNatGateways", "ec2:DescribeNetworkAcls", "ec2:DescribePrefixLists", "ec2:DescribeReservedInstancesOfferings", "ec2:DescribeRouteTables", "ec2:DescribeSecurityGroups", "ec2:DescribeSpotInstanceRequests", "ec2:DescribeSpotPriceHistory", "ec2:DescribeSubnets", "ec2:DescribeVolumes", "ec2:DescribeVpcAttribute", "ec2:DescribeVpcs", "ec2:CreateTags", "ec2:DeleteTags", "ec2:RequestSpotInstances" ], "Resource":[ "*" ] }, { "Sid":"InstancePoolsSupport", "Effect":"Allow", "Action":[ "ec2:AssociateIamInstanceProfile", "ec2:DisassociateIamInstanceProfile", "ec2:ReplaceIamInstanceProfileAssociation" ], "Resource":"arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/*", "Condition":{ "StringEquals":{ "ec2:ResourceTag/Vendor":"Databricks" } } }, { "Sid":"AllowEc2RunInstancePerTag", "Effect":"Allow", "Action":"ec2:RunInstances", "Resource":[ "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:volume/*", "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/*" ], "Condition":{ "StringEquals":{ "aws:RequestTag/Vendor":"Databricks" } } }, { "Sid":"AllowEc2RunInstanceImagePerTag", "Effect":"Allow", "Action":"ec2:RunInstances", "Resource":[ "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:image/*" ], "Condition":{ "StringEquals":{ "aws:ResourceTag/Vendor":"Databricks" } } }, { "Sid":"AllowEc2RunInstancePerVPCid", "Effect":"Allow", "Action":"ec2:RunInstances", "Resource":[ "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:network-interface/*", "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:subnet/*", "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:security-group/*" ], "Condition":{ "StringEquals":{ "ec2:vpc":"arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:vpc/vpc-008706f8895dd6fa6" } } }, { "Sid":"AllowEc2RunInstanceOtherResources", "Effect":"Allow", "Action":"ec2:RunInstances", "NotResource":[ "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:image/*", "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:network-interface/*", "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:subnet/*", "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:security-group/*", "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:volume/*", "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/*" ] }, { "Sid":"EC2TerminateInstancesTag", "Effect":"Allow", "Action":[ "ec2:TerminateInstances" ], "Resource":[ "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/*" ], "Condition":{ "StringEquals":{ "ec2:ResourceTag/Vendor":"Databricks" } } }, { "Sid":"EC2AttachDetachVolumeTag", "Effect":"Allow", "Action":[ "ec2:AttachVolume", "ec2:DetachVolume" ], "Resource":[ "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/*", "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:volume/*" ], "Condition":{ "StringEquals":{ "ec2:ResourceTag/Vendor":"Databricks" } } }, { "Sid":"EC2CreateVolumeByTag", "Effect":"Allow", "Action":[ "ec2:CreateVolume" ], "Resource":[ "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:volume/*" ], "Condition":{ "StringEquals":{ "aws:RequestTag/Vendor":"Databricks" } } }, { "Sid":"EC2DeleteVolumeByTag", "Effect":"Allow", "Action":[ "ec2:DeleteVolume" ], "Resource":[ "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:volume/*" ], "Condition":{ "StringEquals":{ "ec2:ResourceTag/Vendor":"Databricks" } } }, { "Effect":"Allow", "Action":[ "iam:CreateServiceLinkedRole", "iam:PutRolePolicy" ], "Resource":"arn:aws:iam::*:role/aws-service-role/spot.amazonaws.com/AWSServiceRoleForEC2Spot", "Condition":{ "StringLike":{ "iam:AWSServiceName":"spot.amazonaws.com" } } }, { "Sid":"VpcNonresourceSpecificActions", "Effect":"Allow", "Action":[ "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", "ec2:RevokeSecurityGroupEgress", "ec2:RevokeSecurityGroupIngress" ], "Resource":"arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:security-group/sg-04baecd6fd67b711f", "Condition":{ "StringEquals":{ "ec2:vpc":"arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:vpc/vpc-008706f8895dd6fa6" } } } ] }
これらを利用してIAM Roleを作成します。
IAM Roleを作成したら、ARNを指定してCredential Configurationの設定は完了となります。
Storage configurationの作成
次にStorage Configurationを作成します。
DatabricksではDelta Lakeという独自のストレージレイヤーによって「トランザクションによる高信頼性」「インデクシングによる高速なデータ処理」「きめ細かいアクセスコントロールによるデータガバナンス」を実現しています。 実際のデータの保存にはS3を利用できるようになっており、ここではそのための設定を行います。
ここでは、予め新しく作成しておいたS3バケットの指定と、DatabricksがS3バケットを利用するためのバケットポリシーを設定します。
Workspaceの作成
次にWorkspacesを作成します。
WorkspaceはDatabricksのアセットにアクセスするための環境で、これ以降に作成するClusterやNotebook等のリソースはWorkspace内で作成します。
今回は、既に作成済みのVPCを利用します。
Workspace名の他、ここまでに作成した3種類のConfigurationを指定してWorkspaceを作成します。
Provisioningが行われるのです少し待ちます。
完了したら、Workspaceを開きます。
Workspaceに入る際には改めて認証が必要となるようです。
Workspaceのコンソールはこのような感じです。
Clusterの作成
それでは、Workspace上でClusterを作成します。
Clusterの作成にあたり、必要となるパラメーターを設定します。 今回はデフォルト設定とします。
細かいパラメーターとして、Spotインスタンスをどの程度利用するかも指定できます。 今回は詳細は割愛します。
Clusterの作成を開始すると、AWSアカウントにEC2インスタンスが作成されました。
しばらく待つと、Clusterを利用できるようになります。
Notebookの作成
最後に、Notebookを作成します。
デフォルトの言語やどのCluster用のNotebookなのか、等を指定します。
作成できると、以下のような画面が表示されます。
構築作業は以上です。
動作確認
それでは簡単ではありますが動作を確認します。
以下のドキュメントの参考に、サンプルデータのロード・テーブルの作成を行います。
DROP TABLE IF EXISTS diamonds; CREATE TABLE diamonds USING CSV OPTIONS (path "/databricks-datasets/Rdatasets/data-001/csv/ggplot2/diamonds.csv", header "true")
成功したらクエリを行います。
SELECT * FROM diamonds LIMIT 10
なお、クエリを行ったデータに対して各種Chartを表示させることもできます。
SELECT color, avg(price) AS price FROM diamonds GROUP BY color ORDER BY COLOR
簡単ですが、確認は以上です。
まとめ
ユーザーの所有するAWSアカウントかつユーザー管理下のVPCにClusterをプロビジョニングしてみました。 インフラエンジニアの視点で所感をまとめます。
まず、ユーザーインタフェースがシンプルで非常にわかりやすいという印象を受けました。 ドキュメントにはチュートリアル(Getting started)があり、一通り触ってみればどのような概念・リソースが登場してそのような関係性なのかはすぐに分かるんじゃないかと思います。
また、ドキュメントも丁寧に書かれており、知りたいことはきちんと書かれているように思います。 IAM Roleに付与する権限はCustomer-managed VPCの場合にはリソースレベルで制御できるポリシーがドキュメントに記載されていましたので安心感がありましたし、VPCの要件も明確でVPCの構築について迷うことはほぼありませんでした。
Clusterの作成に関してもSpotインスタンスの利用が簡単に行えるようになっており、コストのコントロールもしやすそうでした。
今回はClusterをプロビジョニングしてみるだけでしたが、機会があれば機械学習やデータパイプライン関連の機能も試してみたいと思います。