まるでCloud9なSageMaker Studio Code Editorを一撃で構築する

Xでポストされていたこちらを紹介します。

https://x.com/toshikwa/status/1820749343705698531

素敵です。


GitHubはこちらです。

https://github.com/aws-samples/sagemaker-studio-code-editor-template

## 特徴

デフォルトVPCを使用してSageMaker StudioとCode Editorを構築するCloudFormationテンプレートが提供されています。
未使用時に自動で停止するスクリプトが組み込まれていますので、Cloud9のような使い勝手で利用が可能です。


## 早速やってみた。

バージニア北部リージョンで試しました。

1. マネジメントコンソールにログインします

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/afb9e7e6-3694-bfc0-d984-ed6174df5b19.png)

1. 別タブを開き、GitHubのリポジトリ（ https://github.com/aws-samples/sagemaker-studio-code-editor-template ）にアクセスします

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/8e62da9b-2ac6-32ad-ac32-453cd4a45b68.png)

1. 「Deployment (1-click)」のセクションを表示します
    使用するAWSリージョンの横の「Launch Stack」ボタンをクリックします

    ![github.com_aws-samples_sagemaker-studio-code-editor-template (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/d88ef9bd-232e-eaac-0c86-f2fdf235719f.png)

1. CloudFormationのスタック作成画面が開きます
    「次へ」をクリックします
    
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/65b6aa0a-7fee-e5e5-9181-06857a9c211a.png)

1. スタックの詳細を指定します
    指定できるパラメーターは以下のとおりです

    | パラメーター名 | 内容 |
    | --- | --- |
    | AutoStopIdleTimeInMinutes | Code Editorの自動停止までのアイドル時間。0の場合は無効になります (これは、ターミナルのアクティビティではなく、ファイルの変更に基づきます。このパラメーターは作成後に更新できません。) |
    | EbsSizeInGb | Code EditorのEBSボリュームサイズ (このパラメーターは作成後に減らすことはできません。) |
    | InstanceType | Code Editorのインスタンスタイプ (高速起動は、ml.t3.medium、ml.t3.large、ml.m5.large、ml.m5.2xlarge、ml.c5.large でサポートされています。)

    パラメーターを指定した後、「次へ」をクリックします
    
    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/dbc244b9-cf8f-59c0-c98c-1366d8d8b1df.png)

1. スタックオプションの設定画面では設定の変更はしないので、画面下部の「次へ」をクリックします

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/50b36c75-6ba4-8958-c092-6236b2db93e9.png)

1. 確認して作成画面です

     ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/82ff63a9-33fe-3dc7-2f3f-cf9fda507f0e.png)

    最下部にある「AWS CloudFormation によって IAM リソースがカスタム名で作成される場合があることを承認します。」にチェックをいれ、「送信」ボタンをクリックします

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/61f05bec-f1ae-e1e3-e725-abffadeffb0f.png)

1. CloudFormationスタックの作成が始まります

    ![us-east-1.console.aws.amazon.com_cloudformation_home_region=us-east-1 (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/93a6b8e4-7d69-b790-a662-bcd90b9f0fba.png)

1. 10分ほど待つと、スタックの作成が完了します
    （StackSet-AWS-QuickSetupで始まるスタックが２つ増えていますが、これは気にしないでください）

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/3bef6339-a2c6-36e4-05f3-72b37bf5b3ad.png)

1. 「出力」タブに表示されているURLをクリックします

    ![us-east-1.console.aws.amazon.com_cloudformation_home_region=us-east-1 (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/96910fa5-e1f9-0529-5c1f-a62aac9e008e.png)

1. SageMaker Studioが表示されます

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/4702edf9-517c-c907-81c8-7a768803cef6.png)


## Code Editorにアクセスする

1. 「Skip Tour for now」をクリックし、モーダルを閉じます
    左に並んだアイコンの「Code Editor」をクリックします

    ![studio-d-gneuavvkqx2h.studio.us-east-1.sagemaker.aws_home_tab=Overview (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/2adf9b1e-7093-36f7-73d7-4873a3eb1149.png)

1. 「default」をクリックします

    ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/8e4e521d-ca62-bfd1-32ba-b70d0d1e9824.png)

1. 「Open Code Editor」をクリックします

![studio-d-gneuavvkqx2h.studio.us-east-1.sagemaker.aws_home_tab=Overview (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/c2ff4e23-44dd-89a0-3b32-76c632c5c7c8.png)

1. Code Editorが表示されます

    ![eof38va3i1htdpd.studio.us-east-1.sagemaker.aws_codeeditor_default.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/a86821e9-dc83-ab25-2afc-4d8b7dda0d5a.png)


## Cloud9との違い

* 自動起動はない

    自動停止はありますが、自動起動はありません。SageMaker Studioの画面にて起動する必要があります
    SageMaker Studioの画面へは、以下のいずれかの方法でアクセスします
    
    * CloudFormationの出力のURLをクリックする
    * SageMakerの管理画面で、左メニューにある「Applications and IDEs」セクションの「Studio」を選択し、右側の「Studioを開く」ボタンをクリックする
    
        ![us-east-1.console.aws.amazon.com_sagemaker_home_region=us-east-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/d0c26f8b-3067-ee21-7619-b20e8c8472a7.png)

* OSの種類

    Cloud9はAmazon Linux 2、Amazon Linux 2023、Ubuntu 22.04ですが、SageMaker Studio Code EditorはUbuntu 22.04です。

## おまけ：自動停止はどういう仕組でできているの？

CloudFormationスタックを作成した際に、SageMaker Studioのライフサイクル設定が作成されます。
ライフサイクル設定では、PHPやJava、Dockerなどインストールが行われます。
他に、Cronもインストールされ、2分ごとにPythonスクリプトが実行されるように設定されます。

```
*/2 * * * * /bin/bash -ic '$CONDA_HOME/python $PYTHON_SCRIPT_PATH --time 600 --region $AWS_DEFAULT_REGION >> $LOG_FILE'
```

PythonスクリプトはこちらのGitHubリポジトリで公開されている「auto_stop_idle.py」です。

https://github.com/aws-samples/sagemaker-studio-apps-lifecycle-config-examples


「auto_stop_idle.py」では、ディレクトリの最終更新日をチェックし、利用中かどうかを判定する「check_user_activity」関数が用意されています。

```python:check_user_activity
def check_user_activity(workspace_dir, idle_threshold):
    # Get the timestamp of the most recently modified file or folder
    recent_item = max(
        (os.path.join(root, file) for root, _, files in os.walk(workspace_dir) for file in files),
        key=lambda x: os.lstat(x).st_mtime,
        default=None
    )

    # Get the current time
    current_time = time.time()

    # Calculate the time difference
    time_diff = current_time - os.stat(recent_item).st_mtime if recent_item else float('inf')
    log_message(f"[auto-stop-idle] - Logging time difference between current time and time files were last changed {time_diff}.")

    # Check if the user is idle based on the idle time threshold
    if time_diff > idle_threshold:
        return "idle"
    else:
        return "active"
```

Cronで２分ごとに以下の処理が呼び出され、監視対象ディレクトリ配下のすべてのディレクトリの利用を監視し、すべてのディレクトリがidleと判定された場合に、Code Editorを停止させています。（Code Editor自身がCode Editorを終了させています）

```python
# Monitor workspace_dirs for changes to implement auto-shutdown, as these paths track updates to both unsaved and saved editor content, covering all user activity scenarios.
workspace_dirs = ["/opt/amazon/sagemaker/sagemaker-code-editor-server-data/data/User/History", "/opt/amazon/sagemaker/sagemaker-code-editor-server-data/data/User/Backups/empty-window/untitled"]
idle_threshold = args.time  # this is in seconds. for ex: 1800 seconds for 30 minutes
aws_region = args.region # get the region.

# Track the activity status for each directory
activity_status = [check_user_activity(directory, idle_threshold) for directory in workspace_dirs]

# Terminate the SageMaker Studio app if all directories are idle and no activity is observed.
if all(status == "idle" for status in activity_status):
    # Load the resource metadata from the file
    with open('/opt/ml/metadata/resource-metadata.json') as f:
        resource_metadata = json.load(f)

    # Extract the required details for deleting the app
    domain_id = resource_metadata['DomainId']
    space_name = resource_metadata['SpaceName']
    app_name = resource_metadata['ResourceName']
    app_type = resource_metadata['AppType']
    resource_arn = resource_metadata["ResourceArn"]

    # Use boto3 api call to delete the app. 
    sm_client = boto3.client('sagemaker',region_name=aws_region)
    response = sm_client.delete_app(
        DomainId=domain_id,
        AppType=app_type,
        AppName=app_name,
        SpaceName=space_name
    )
    log_message(f"[auto-stop-idle] - Deleting app {app_type}-{app_name}. Domain ID: {domain_id}. Space name: {space_name}. Resource ARN: {resource_arn}.")
    log_message("[auto-stop-idle] - SageMaker Code Editor app terminated due to being idle for given duration.")
else:
    log_message("[auto-stop-idle] - SageMaker Code Editor app is not idle. Passing check.")
```