VPC only(閉域環境)のSageMakerドメインから、CodeArtifact経由でPythonパッケージをインストールする構成を実装しました。
(カスタムイメージではなく、指定したパッケージやバージョンを必要に応じてインストールできる仕組みが欲しかった)
以下の手順で実装しています。
- 個人用のCodeArtifact作成
- VPC関連のリソース作成
- SagaMakerのドメイン作成
- パッケージの登録
- 動作確認
各項目について詳細を記載します。
1. 個人用のCodeArtifact作成
最初にPythonパッケージを入れるリポジトリを作成します。(後でもいい)
CodeArtifactは、まずパッケージのデータを格納するドメインを作成し、その後でパッケージを登録するリポジトリを作成します。
コンソールでの作成手順は特に困る部分もなかったので、説明は省略します。好きな名前で作成して完了です。ドメインにはKMSキーを設定できます。(今回はAWSマネージドを利用)
作成したドメインの詳細を見ると、S3バケットARNがあります。(パッケージのデータ保存用)
S3のコンソールからは見られず、CodeArtifactやAWS CLI経由での操作となるようです。
2. VPC関連のリソース作成
VPC onlyのSageMakerドメインを作成するため、各種リソースを作成します。
VPC
VPC onlyでIPv4 CIDRを10.0.0.0/16、テナンシーをデフォルトにして作成。 (作成後にVPCの設定を開き、DNS解決とDNSホスト名を有効化)
サブネット, ルートテーブル
SageMakerのドメインをVPC onlyで作成する場合、2つ以上のサブネットが推奨されるため、2つ作成します。(IPv4 CIDRは10.0.1.0/24と10.0.2.0/24で設定)
またルートテーブルについては、2つのサブネットが同じVPC Endpointを利用することになるので、1つのルートテーブルに2つのサブネットを関連付けるようにします。
最初のVPC作成時にサブネットやルートテーブルをまとめて作ると、サブネットごとのルートテーブルができるので、修正が必要。
セキュリティグループ
SageMaker Studio用のセキュリティグループ
インバウンド:NFS (TCP/2049)およびカスタムTCP (TCP/8192~65535)を許可し、ソースにはStudio用のセキュリティグループ(自分自身)を指定
アウトバウンド:HTTPS (TCP/443)を許可し、宛先にEndpoints用のセキュリティグループを指定
Endpoint用のセキュリティグループ
インバウンド:HTTPS (TCP/443)を許可し、ソースは自分自身のセキュリティグループを指定
アウトバウンド:指定なし
Endpoint作成
閉域環境でのSageMakerの利用を可能にするため、下記サービスのEndpointを作成。(S3はルートテーブル、その他は2つのサブネットとEndpoint用のセキュリティグループを関連付けして作成)
- com.amazonaws.ap-northeast-1.s3(Gateway)
- com.amazonaws.ap-northeast-1.sagemaker.api
- com.amazonaws.ap-northeast-1.sagemaker.runtime
- com.amazonaws.ap-northeast-1.servicecatalog
- com.amazonaws.ap-northeast-1.codeartifact.api
- com.amazonaws.ap-northeast-1.codeartifact.repositories
- com.amazonaws.ap-northeast-1.sts
- com.amazonaws.ap-northeast-1.ecr.api
- com.amazonaws.ap-northeast-1.ecr.dkr
- com.amazonaws.ap-northeast-1.logs
3. SagaMakerのドメイン作成
インターネット接続のない、VPC onlyのドメインを作成します。
- ロールは既存のものがあればそれを利用、なければ新規ロールを作成し、CodeArtifactを利用するための下記ポリシーを追記
- アプリケーションやインターフェイスはお好み (今回はJupyterLabとCodeEditorを使えるよう設定)
- ネットワーク設定で「VPCのみ」を選び、作成したVPCと2つのサブネット、Studio用のセキュリティグループを設定
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CodeArtifactReadForPip",
"Effect": "Allow",
"Action": [
"codeartifact:GetAuthorizationToken",
"codeartifact:GetRepositoryEndpoint",
"codeartifact:ReadFromRepository"
],
"Resource": "*"
},
{
"Sid": "STSServiceBearerTokenForCodeArtifact",
"Effect": "Allow",
"Action": "sts:GetServiceBearerToken",
"Resource": "*",
"Condition": {
"StringEquals": {
"sts:AWSServiceName": "codeartifact.amazonaws.com"
}
}
}
]
}
4. パッケージの登録
1で作成したリポジトリにパッケージを登録します。
今回はCodeCommitのリポジトリに登録したpyproject.tomlとuv.lockを入力とし、CodeBuildでwheelを集めてCodeArtifactに投入する形を取りました。
まずCodeCommitにリポジトリを作成し、以下のファイルを置きます。
- pyproject.toml
- uv.lock
- buildspec.yml
buildspec.ymlの中身は以下の通り。
version: 0.2
env:
variables:
AWS_REGION: ap-northeast-1
DOMAIN: <作成したArtifactのDomain名>
DOMAIN_OWNER: "<アカウントID>"
DEST_REPO: <作成したArtifactのリポジトリ名>
phases:
install:
runtime-versions:
python: 3.12
commands:
# venv作成・ツール導入(uv/twine)
- |
set -eu
python3 --version
python3 -m venv venv
. venv/bin/activate
pip install -U pip wheel twine uv
uv --version
build:
commands:
# uv.lock → requirements.txt → wheel収集 → CodeArtifact投入
- |
set -eu
. venv/bin/activate
# wheel格納場所
mkdir -p wheelhouse
# uv.lockを元に固定requirementsを生成
uv export --format requirements.txt --output-file requirements.txt
head -n 50 requirements.txt
# requirements.txtを元にwheel収集(依存込み)
pip download -d wheelhouse --only-binary=:all: -r requirements.txt
# CodeArtifactへupload
export AWS_STS_REGIONAL_ENDPOINTS=regional
CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \
--domain "$DOMAIN" --domain-owner "$DOMAIN_OWNER" \
--query authorizationToken --output text --region "$AWS_REGION")
REPO_URL=$(aws codeartifact get-repository-endpoint \
--domain "$DOMAIN" --domain-owner "$DOMAIN_OWNER" \
--repository "$DEST_REPO" --format pypi \
--query repositoryEndpoint --output text --region "$AWS_REGION")
# 誤爆防止(空なら失敗させる)
test -n "$REPO_URL"
twine upload --repository-url "$REPO_URL" -u aws -p "$CODEARTIFACT_AUTH_TOKEN" wheelhouse/*
CodeCommitのリポジトリに上記3つのファイルを追加できたら、CodeBuildでプロジェクトを作成します。
- ソースプロバイダにCodeCommitを選択し、↑で作成したリポジトリを設定
- 環境はデフォルトのものを選択 (本来はversion等を固定すべき?)
- ロールは下記ポリシーを持つものを選択
- Buildspecは「buildspecファイルを使用する」を選択
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CodeCommitGitPull",
"Effect": "Allow",
"Action": "codecommit:GitPull",
"Resource": "arn:aws:codecommit:ap-northeast-1:<アカウントID>:<リポジトリ名>"
},
{
"Sid": "CloudWatchLogsForCodeBuild",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
},
{
"Sid": "CodeArtifactPublish",
"Effect": "Allow",
"Action": [
"codeartifact:GetAuthorizationToken",
"codeartifact:GetRepositoryEndpoint",
"codeartifact:PublishPackageVersion"
],
"Resource": "*"
},
{
"Sid": "STSServiceBearerTokenForCodeArtifact",
"Effect": "Allow",
"Action": "sts:GetServiceBearerToken",
"Resource": "*",
"Condition": {
"StringEquals": {
"sts:AWSServiceName": "codeartifact.amazonaws.com"
}
}
}
]
}
プロジェクトが作成できたら、「ビルドを開始」してCodeCommitの内容を元にCodeArtifactへの登録が行われるのを確認。
無事に登録ができていれば、Artifactのリポジトリの中にpyproject.tomlとuv.lockに記載していたパッケージが入っていることが確認できます。
5. 動作確認
Artifactへの登録が完了したら、SageMakerでインストールができるかを確認します。
3で作成したドメインからStudioを開き、スペースを作成してJupyterLabを起動します。
起動できたらターミナルを開き、CodeArtifactにログイン (pipの参照先を固定)します。
export AWS_REGION=ap-northeast-1
export AWS_STS_REGIONAL_ENDPOINTS=regional
aws codeartifact login \
--tool pip \
--domain <作成したArtifactのDomain名> \
--domain-owner <アカウントID> \
--repository <作成したArtifactのリポジトリ名> \
--region $AWS_REGION
ログインできたら、まず以下のコマンドでPyPIへの通信が失敗することを確認します。(Privateサブネットなので、PyPIにはアクセスできない)
curl -I https://pypi.org/ -m 5
# curl: (28) Connection timed out after 5001 milliseconds
そして、Artifactに登録したパッケージがインストールできるか確認します。
pip install ***
# Looking in indexes: https://aws:****@my-domain-...
# Collecting ***
# ...
Looking in indexes...で作成したCodeArtifactを参照し、installが進んでいます。無事にPrivate環境からArtifactに登録したパッケージだけをインストールできる環境ができました。
まとめ
閉域環境のSageMakerドメインから、CodeArtifact経由でPythonパッケージをインストールする構成を実装しました。
これで解析環境を閉域にしつつ、指定したパッケージや自作パッケージを必要に応じてインストールすることができます。
今回はbuildspec側でPythonのversionを固定してますが、SageMaker側のpythonのversionに合わせて、pyproject.tomlやbuildspec.ymlのpythonのversionも追従する仕組みがあると、より便利かもしれません。
他にもCodeCommitのリポジトリの変更をトリガーとしたパッケージ更新の自動化も考えられますが、そこは運用の仕方にもよると思うので、今回は割愛します。