AWS-CDKを使い爆速で機械学習環境SageMakerStudioを構築する。with TypeScript

## はじめに
今回実現したいことは以下の点です。
* 機械学習開発環境にSageMakerStudioを利用する。
* SageMakerStudioの環境構築をAWS-CDKを用いて爆速に構築する。
* 構築された環境は実際のプロジェクト開発でも利用されることを想定し、IAMロールの設定なども行う。
* 複数のDeveloerが参画するプロジェクトを意識した開発環境の構築をする。

## 参考サイト
* https://aws.amazon.com/jp/builders-flash/202211/chococones-as-code/?awsf.filter-name=*all


## 構築アーキテクチャ
![SageMakerStudio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/fba1e470-0ec3-39dc-4bcb-b48b97a9459d.png)
([参考サイトから](!https://aws.amazon.com/jp/builders-flash/202211/chococones-as-code/?awsf.filter-name=*all)一部編集して掲載)

今回は以下の2ユーザーのがSageMakerStudioを利用するようなイメージで作成していきます。
* sukeken : 環境構築者 + SageMakerStudio開発者
* kenji   : SageMakerStudio開発者

## 構築開始
### 作業環境の用意
今回はCloud9を利用していきます。Cloud9を立ち上げたら、以下の操作を実施します。
```
$ # CDK をインストール
$ npm install -g aws-cdk

$ # AWS CLI の設定(アクセスキーやシークレットを入力)
$ aws configure
```
尚、ここで設定しているアクセスキーにはAdministrator権限を有しているIAMユーザーのアクセスキーをセットしています。（このIAMユーザーで色々環境を構築していく）

### CDKサンプルのリポジトリをクローンする
いきなり楽をします。上記のアーキテクチャがほぼほぼ記載されているaws-samplesをクローンしていきます。
```
$ # リポジトリをクローン
$ git clone https://github.com/aws-samples/aws-ml-jp
```
今回利用するサンプルcdkが記述されているフォルダに移動し、依存ライブラリをインストールします。
```
$ cd sagemaker/sagemaker-studio/IaC/case01/cdk-typescript
$ # 依存ライブラリインストール
$ npm install
```
### CDKを編集していく
ここから、今回の要件に合わせてカスタマイズしていきます。と言ってもこのサンプルコードではほぼ掲載のアーキテクチャは満たされているので、今回は以下のみ修正します。
1. SageMaker Domain名の修正
1. CodeCommit　リポジトリ名の修正
1. 作成するユーザー名の修正（sukekenとkenjiを利用する）

以下が修正したCDKコードです。
```typescript:chococones-stack.ts
import * as cdk from 'aws-cdk-lib'
import { Construct } from 'constructs'
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as s3 from 'aws-cdk-lib/aws-s3'
import * as codecommit from 'aws-cdk-lib/aws-codecommit'
import * as iam from 'aws-cdk-lib/aws-iam'
import * as sagemaker from 'aws-cdk-lib/aws-sagemaker'
import { User } from './constructs/user'

export class ChococonesStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props)

    // SageMaker Domain 名
    const domainName = 'ml-demoprojecl-01'

    // CodeCommit リポジトリ名
    const repositoryName = domainName

    // Domain 用の VPC
    const vpc = new ec2.Vpc(this, 'Vpc', { maxAzs: 2 })

    // S3 と Domain に設定する Removal Policy
    const removalPolicy = cdk.RemovalPolicy.DESTROY

    // 共用 Bucket
    const sharedBucket = new s3.Bucket(this, 'SharedBucket', {
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      encryption: s3.BucketEncryption.S3_MANAGED,
      enforceSSL: true,
      removalPolicy,
    })

    // 共用リポジトリ
    const repository = new codecommit.Repository(this, 'Repository', {
      repositoryName,
    })

    // SageMaker Domain 用の default execution Role
    const defaultRole = new iam.Role(this, 'DefaultRole', {
      assumedBy: new iam.ServicePrincipal('sagemaker.amazonaws.com'),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSageMakerFullAccess'),
      ],
    })

    // Studio Domain
    const domain = new sagemaker.CfnDomain(this, 'Domain', {
      authMode: 'IAM',
      defaultUserSettings: {
        executionRole: defaultRole.roleArn,
      },
      domainName,
      vpcId: vpc.vpcId,
      // VPC　の private subnets を使用
      subnetIds: vpc.privateSubnets.map((subnet) => subnet.subnetId),
    })

    domain.applyRemovalPolicy(removalPolicy)

    // 全員の IAM User・IAM Role につける共用 Policy
    const policy = new iam.ManagedPolicy(this, 'SharedPolicy')

    // 共用 CodeCommit リポジトリへの読み書き許可
    policy.addStatements(
      new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: ['codecommit:*'],
        resources: [repository.repositoryArn],
      })
    )

    const users: User[] = [
      // ユーザーの作成
      new User(this, 'Sukeken', {
        name: 'sukeken',
        domainId: domain.attrDomainId,
        policy,
        removalPolicy,
      }),

      new User(this, 'Kenji', {
        name: 'kenji',
        domainId: domain.attrDomainId,
        policy,
        removalPolicy,
      }),
    ]

    users.forEach((user) => {
      // 共用バケットへの読み書き権限
      sharedBucket.grantReadWrite(user.user)
      sharedBucket.grantReadWrite(user.role)

      users.forEach((bucketOwner) => {
        // 他のユーザーのバケットへの読み込み権限
        bucketOwner.bucket.grantRead(user.user)
        bucketOwner.bucket.grantRead(user.role)
      })
    })
  }
}
```
上記コードの記述内容詳細を確認したい方は、[是非参考サイト](!https://aws.amazon.com/jp/builders-flash/202211/chococones-as-code/?awsf.filter-name=*all)を参照ください。

### デプロイする
以下のコマンドでデプロイしていきます。
```
$ # cdk の設定
$ cdk bootstrap
$ # デプロイ
$ cdk deploy
```
初回のデプロイ時のみ`cdk bootstrap`というコマンドでcdk実行の準備をします。こちらの実行で用意されるスタックには、CDKで利用されるリソース（例えばCloudFormationテンプレートを保持するS3バケットなど）が含まれています。

色々とTerminalに表示されたあと、最後に以下のメッセージが確認できると思いますので`y` + `enter`をします。
```
Do you wish to deploy these changes (y/n)? y
```
作成されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/161c946b-2c25-0c70-d0f9-7562b21e21a1.png)

### 構築内容の確認
コンソールからいくつかのサービスを確認してみます。
まずはSageMaker。Domainが作成されており、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/a38da1c4-3335-e8f2-1e68-6e017a1933dc.png)
ユーザー毎にSageMakerStudioを利用できるように構築されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/ee324bc1-4724-403e-b06f-236ff018bf97.png)

次にVPCを確認してみます。2AZにまたがるSubnetsとともに作成されています！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/6b8f78de-d317-38cb-6db0-41d880c96941.png)

IAMユーザーもしっかり作成されていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/ecabc32e-3973-6af6-f8b2-6efe175912b9.png)

長くなってしまうので割愛しますが以下のようなサービスも無事に構築されています。恐るべしCDKの魅力。。！
* IAMロール、IAMポリシー
* S3バケット
* CodeCommitリポジトリ
* IAM User用のパスワードをシークレットマネージャーに追加

### サービスの利用開始
では実際にサービスを利用開始できるのか、sukekenになってトライしてみようと思います。
まずはsukekenのIMAユーザーとしてのパスワードを取得する必要がありますが、これは管理者がSecretManagerから確認する必要があります。SecretManagerを確認すると、パスワードが生成されていました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/0ae9afc0-7779-8ee6-b9ee-bab39d89dfd2.png)
こちらを使ってsukekenでログイン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/da687860-9a1a-722f-cf7c-1fe246a377ac.png)
チェンジパスワードが表示されたので、パスワードを変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/7f7e7c31-7e4a-d3ce-4ba9-65e4604fad56.png)
SageMakerコンソールから無事にStudioを起動できました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484097/1dc6f513-e757-9c6b-22ec-e8797af2537f.png)

### その他
仮にプロジェクトメンバーが入れ替わる際も、cdkのIAMユーザーを作成している部分を変更して`cdk diff`, `cdk deploy`すればあっという間に反映されますね！

### おわりに
今回は用意されているサンプルのCDKコードを用いて爆速に機械学習用の環境を用意できました。サンプルのコードといってもかなり実用されるシーンに適したサンプルになっており、ほとんどそのまま利用できるようなイメージでした。IAMの制限などは、プロジェクトごとに利用サービスなども異なると思うのでもう少しカスタマイズが必要になるかもしれませんが、その部分のカスタマイズも大した工数にはならないでしょう。これで開発者も環境運用者もよりハッピーな環境構築ができると思います！Thank you, AWS-CDK!