For the English version, see README.md.
Azure SRE Agent および Azure Copilot Observability Agent の検証・教材用マイクロサービスアプリケーション。
graph TB
Client[Client / Browser] --> AGC[Application Gateway<br/>for Containers]
subgraph AKS[Azure Kubernetes Service]
ALB[ALB Controller] -.->|config sync| AGC
AGC --> CS[Catalog Service<br/>:5001]
AGC --> OS[Order Service<br/>:5002]
AGC --> NS[Notification Service<br/>:5003]
end
CS --> CosmosDB[(Azure Cosmos DB)]
OS --> CosmosDB
NS --> CosmosDB
CS --> OTEL[OpenTelemetry Collector]
OS --> OTEL
NS --> OTEL
subgraph Auth[Microsoft Entra ID]
EntraID[JWT Bearer Auth]
end
CS -.-> EntraID
OS -.-> EntraID
NS -.-> EntraID
Note: Ingress NGINX は 2026 年 3 月にコミュニティ版が退役しました。本プロジェクトでは Application Gateway for Containers (AGC) と Gateway API を使用しています。
| サービス | ポート | エンドポイント数 | 説明 |
|---|---|---|---|
| CatalogService | 5001 | 10 | 商品カタログ、カテゴリ、在庫管理 |
| OrderService | 5002 | 8 | 注文管理、ステータス追跡 |
| NotificationService | 5003 | 6 | 通知配信、既読管理 |
- Runtime: .NET 10
- Framework: ASP.NET Core Minimal APIs
- 認証: Microsoft Entra ID (JWT ベアラー認証)
- データストア: Azure Cosmos DB (NoSQL API)
- テレメトリ: OpenTelemetry (トレース + メトリクス + ログ)
- ログ: Serilog (構造化ログ)
- コンテナ: Docker (マルチステージビルド)
- オーケストレーション: Kubernetes (AKS)
| Method | Path | 説明 |
|---|---|---|
| GET | /api/products |
商品一覧取得 |
| GET | /api/products/{id} |
商品詳細取得 |
| POST | /api/products |
商品登録 |
| PUT | /api/products/{id} |
商品更新 |
| DELETE | /api/products/{id} |
商品削除 |
| GET | /api/products/search?q={query} |
商品検索 |
| GET | /api/categories |
カテゴリ一覧 |
| GET | /api/categories/{id} |
カテゴリ詳細 |
| GET | /api/inventory/{productId} |
在庫確認 |
| PUT | /api/inventory/{productId} |
在庫更新 |
| Method | Path | 説明 |
|---|---|---|
| GET | /api/orders |
注文一覧 |
| GET | /api/orders/{id} |
注文詳細 |
| POST | /api/orders |
注文作成 |
| PUT | /api/orders/{id}/status |
注文ステータス更新 |
| DELETE | /api/orders/{id} |
注文削除 |
| GET | /api/orders/user/{userId} |
ユーザー別注文 |
| POST | /api/orders/{id}/cancel |
注文キャンセル |
| GET | /api/orders/{id}/total |
注文合計計算 |
| Method | Path | 説明 |
|---|---|---|
| GET | /api/notifications |
通知一覧 |
| GET | /api/notifications/{id} |
通知詳細 |
| POST | /api/notifications |
通知作成 |
| PUT | /api/notifications/{id}/read |
既読マーク |
| GET | /api/notifications/user/{userId} |
ユーザー別通知 |
| DELETE | /api/notifications/{id} |
通知削除 |
各サービスは、Kubernetes の Liveness / Readiness プローブに対応した一貫したヘルスチェック エンドポイントを公開しています。
プロセスが生存しているかどうかのみを確認します。依存関係はチェックしません。
{
"status": "Healthy",
"checks": []
}以下の条件がすべて満たされた場合のみ Healthy を返します。
| チェック | 説明 |
|---|---|
cosmosdb |
CosmosClient.ReadAccountAsync() が成功すること |
startup |
Cosmos DB 初期化とシード投入が完了していること |
{
"status": "Healthy",
"checks": [
{ "name": "cosmosdb", "status": "Healthy", "description": "Cosmos DB is reachable.", "duration": 42.3 },
{ "name": "startup", "status": "Healthy", "description": "Startup initialization complete.", "duration": 0.1 }
]
}- .NET 10 SDK
- Docker Desktop
- (オプション) Azure Cosmos DB Emulator
docker compose up --buildサービスは以下のポートでアクセスできます:
- CatalogService: http://localhost:5001
- OrderService: http://localhost:5002
- NotificationService: http://localhost:5003
- Cosmos DB Emulator: https://localhost:8081
開発モードでは認証がスキップされます (Authentication:DisableAuth=true)。
注意: docker-compose 環境では
CosmosDb:AllowInsecureCertificate=trueが設定されています。 これは Linux Cosmos DB エミュレーターの自己署名証明書を許可するための設定です。接続モード(Gateway)はこの設定とは別にCosmosDb:ConnectionModeで構成できます(未設定かつAllowInsecureCertificate=trueの場合は Gateway、それ以外は Direct がデフォルトです)。 本番環境では絶対に使用しないでください。
dotnet run --project src/CatalogService
dotnet run --project src/OrderService
dotnet run --project src/NotificationServicedotnet build AzureSREAgentDemoLab.slnxこのリポジトリには、教育用途で複数環境へ繰り返しデプロイできる Azure Developer CLI (azd) + Bicep 構成が含まれています。
- Azure Container Apps Environment
- Azure Container Apps x 3
- Catalog
- Order
- Notification
- Azure Container Registry (
Basic) - Azure Cosmos DB for NoSQL (
EnableServerless) - Cosmos SQL Database
- Log Analytics Workspace
- Application Insights
- コスト最適化優先
- AKS ではなく Container Apps を使用
- 非
prod環境はminReplicas = 0 - ACR は
Basic - Cosmos DB は
serverless
- Private Endpoint は未使用
- 既定リージョンは
westus3- IaC 側の既定値として
westus3を使用 azd env new <env> -l westus3で環境作成することを推奨
- IaC 側の既定値として
- 複数環境対応
AZURE_ENV_NAMEベースでリソース名を分離dev/test/prod/workshop-aなどを並行運用可能
- Azure Developer CLI (
azd) - Azure CLI (
az) - Docker Desktop
- .NET 10 SDK
- Azure サブスクリプションへのアクセス
azd は環境ごとに .azure/<environment-name>/.env を作成します。
まず環境を作成します。
azd env new dev -l westus3次に、必須値を環境へ設定します。scripts/setup-entra-app.ps1 を使用すると Entra ID 登録と値の設定が自動化されます (詳細は docs/entra-app-setup.md 参照)。
azd env set ENTRA_TENANT_ID <your-tenant-id>
azd env set ENTRA_CLIENT_ID <your-client-id>
azd env set ENTRA_AUDIENCE <identifier-uri-from-entra-registration>
ENTRA_AUDIENCEには、Entra ID アプリ登録の App ID URI (識別子 URI) を指定します。scripts/setup-entra-app.ps1を使った場合はapi://<your-client-id>形式、Bicep テンプレートを使った場合はapi://<sanitized-display-name>形式になります。JWT のaud検証はこの値と一致する必要があります。
必要に応じて追加設定も可能です。
azd env set ENVIRONMENT_TYPE dev
azd env set DISABLE_AUTH false
azd env set OPEN_TELEMETRY_ENDPOINT <optional-otlp-endpoint>参考: 実際に
azdが読むのは.azure/<environment-name>/.envです。必要な値は任意のメモに控えて管理してください。
ローカル検証として以下は確認済みです。
infra/main.bicep: 診断エラーなしinfra/modules/container-app.bicep: 診断エラーなしdotnet build AzureSREAgentDemoLab.slnx: 成功
ただし、Azure 側の事前確認には実環境の Entra ID 値を設定した azd 環境が必要です。
- 環境を作成
- 必須の
azd env setを投入 - Azure にサインイン
- デプロイを実行
az login
azd upazd env new test -l westus3
azd env new prod -l westus3
azd env new workshop-a -l westus3各環境ごとに .azure/<environment-name>/.env が分離されるため、教育用途の並行検証に向いています。
azure.yamlazdのサービス定義
infra/main.bicep- 共有 Azure リソース + 各サービスのデプロイ
infra/modules/container-app.bicep- 各 API サービス用の共通 Container App モジュール
infra/main.parameters.jsonazd環境変数から Bicep パラメータへ受け渡し
AKS、Application Gateway for Containers、VNet、Cosmos DB、ACR などを一括でプロビジョニングします。
Bash の場合:
# リソースグループを作成
az group create --name rg-azure-sre-agent-demo-lab --location westus3
# Bicep でインフラをデプロイ
az deployment group create \
--name main-aks \
--resource-group rg-azure-sre-agent-demo-lab \
--template-file infra/main-aks.bicep \
--parameters \
environmentName=dev \
entraTenantId=<YOUR_TENANT_ID> \
entraClientId=<YOUR_CLIENT_ID> \
entraAudience=<YOUR_AUDIENCE>PowerShell の場合:
# リソースグループを作成
az group create --name rg-azure-sre-agent-demo-lab --location westus3
# Bicep でインフラをデプロイ
az deployment group create `
--name main-aks `
--resource-group rg-azure-sre-agent-demo-lab `
--template-file infra/main-aks.bicep `
--parameters `
environmentName=dev `
entraTenantId=<YOUR_TENANT_ID> `
entraClientId=<YOUR_CLIENT_ID> `
entraAudience=<YOUR_AUDIENCE>デプロイ完了後、以下の出力値を取得します:
Bash の場合:
# デプロイ出力を確認
az deployment group show \
--resource-group rg-azure-sre-agent-demo-lab \
--name main-aks \
--query properties.outputs -o jsonPowerShell の場合:
# デプロイ出力を確認
az deployment group show `
--resource-group rg-azure-sre-agent-demo-lab `
--name main-aks `
--query properties.outputs -o json主要な出力値:
| 出力 | 説明 |
|---|---|
AKS_CLUSTER_NAME |
AKS クラスター名 |
ACR_LOGIN_SERVER |
ACR ログインサーバー |
AGC_RESOURCE_ID |
Application Gateway for Containers リソース ID |
AGC_FRONTEND_NAME |
Gateway から参照する AGC フロントエンド名 |
AGC_FRONTEND_FQDN |
AGC フロントエンド FQDN |
ALB_IDENTITY_CLIENT_ID |
ALB Controller 用マネージド ID |
COSMOS_ACCOUNT_NAME |
Cosmos DB アカウント名 |
# アプリ登録を作成し、アプリケーション ID を取得
APP_ID=$(az ad app create --display-name "<your-entra-app-name>" --query appId -o tsv)
TENANT_ID=$(az account show --query tenantId -o tsv)
echo "TenantId : $TENANT_ID"
echo "ClientId : $APP_ID"# アプリ登録を作成し、アプリケーション ID を取得
$APP_ID = (az ad app create --display-name "<your-entra-app-name>" --query appId -o tsv)
$TENANT_ID = (az account show --query tenantId -o tsv)
Write-Host "TenantId : $TENANT_ID"
Write-Host "ClientId : $APP_ID"API として保護するために App ID URI を設定します。
Bash の場合:
# App ID URI を設定 (api://<appId> 形式)
az ad app update --id $APP_ID --identifier-uris "api://${APP_ID}"PowerShell の場合:
# App ID URI を設定 (api://<appId> 形式)
az ad app update --id $APP_ID --identifier-uris "api://$APP_ID"Azure CLI のログイン資格情報でユーザー トークンを取得する場合は、委任スコープ (access_as_user) も必要です。infra/modules/entra-app.bicep を使う場合は自動作成されます。手動でアプリ登録した場合は以下のコマンドで追加してください。
Bash の場合:
# アプリ登録の Object ID を取得 (appId とは別)
OBJ_ID=$(az ad app show --id $APP_ID --query id -o tsv)
# 既存の oauth2PermissionScopes を取得し、access_as_user が無い場合のみ追加
APP_JSON=$(az ad app show --id $APP_ID -o json)
EXISTING_SCOPE_ID=$(echo "$APP_JSON" | jq -r '.api.oauth2PermissionScopes[]? | select(.value == "access_as_user") | .id' | head -n 1)
if [ -n "$EXISTING_SCOPE_ID" ]; then
echo "access_as_user scope already exists: $EXISTING_SCOPE_ID"
else
SCOPE_ID=$(python3 -c "import uuid; print(uuid.uuid4())")
SCOPES=$(echo "$APP_JSON" | jq --arg scopeId "$SCOPE_ID" '
(.api.oauth2PermissionScopes // []) + [
{
id: $scopeId,
adminConsentDisplayName: "Access API as user",
adminConsentDescription: "Allows the app to access the API on behalf of the signed-in user",
userConsentDisplayName: "Access API as you",
userConsentDescription: "Allows the app to access the API on your behalf",
value: "access_as_user",
type: "User",
isEnabled: true
}
]')
BODY=$(jq -n --argjson scopes "$SCOPES" '{api: {oauth2PermissionScopes: $scopes}}')
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/applications/$OBJ_ID" \
--headers "Content-Type=application/json" \
--body "$BODY"
fiPowerShell の場合:
# アプリ登録の Object ID を取得 (appId とは別)
$OBJ_ID = (az ad app show --id $APP_ID --query id -o tsv)
# 既存の oauth2PermissionScopes を取得し、access_as_user が無い場合のみ追加
$appJson = az ad app show --id $APP_ID -o json | ConvertFrom-Json
$existingScopes = @($appJson.api.oauth2PermissionScopes)
$existingScope = $existingScopes | Where-Object { $_.value -eq "access_as_user" } | Select-Object -First 1
if ($existingScope) {
Write-Host "access_as_user scope already exists: $($existingScope.id)"
}
else {
$scopeId = [System.Guid]::NewGuid().ToString()
$newScope = [ordered]@{
id = $scopeId
adminConsentDisplayName = "Access API as user"
adminConsentDescription = "Allows the app to access the API on behalf of the signed-in user"
userConsentDisplayName = "Access API as you"
userConsentDescription = "Allows the app to access the API on your behalf"
value = "access_as_user"
type = "User"
isEnabled = $true
}
$patchBody = @{
api = @{
oauth2PermissionScopes = @($existingScopes + $newScope)
}
} | ConvertTo-Json -Depth 10
$tmpScopeFile = [System.IO.Path]::GetTempFileName()
$patchBody | Out-File -FilePath $tmpScopeFile -Encoding utf8NoBOM
try {
az rest --method PATCH `
--uri "https://graph.microsoft.com/v1.0/applications/$OBJ_ID" `
--headers "Content-Type=application/json" `
--body "@$tmpScopeFile"
}
finally {
Remove-Item $tmpScopeFile -ErrorAction SilentlyContinue
}
}スコープ追加後、テナントの同意ポリシーに応じてユーザー同意または管理者同意を実施してください。
続いて、サービス間 (client_credentials) アクセス用の アプリ ロール を追加します。 Azure portal の [アプリの登録] → [アプリ ロール] → [アプリ ロールの作成] で以下の値を入力してください。
| 項目 | 値 |
|---|---|
| 表示名 | Access AzureSREAgentDemoLab API |
| 許可されるメンバーの種類 | アプリケーション |
| 値 | Api.Access |
| 説明 | Allows a service to call the AzureSREAgentDemoLab APIs |
| このアプリ ロールを有効にする | オン |
注:
az ad app updateによる JSON パッチでロールを追加することもできますが、Portal 操作が最も確実です。
Bash の場合:
# サービスプリンシパルを作成
az ad sp create --id $APP_ID
# クライアントシークレットを作成 (有効期限: 1年)
CLIENT_SECRET=$(az ad app credential reset --id $APP_ID --years 1 --query password -o tsv)
echo "ClientSecret を取得しました。安全な場所に保存してください。"
echo "Audience : api://$APP_ID"PowerShell の場合:
# サービスプリンシパルを作成
az ad sp create --id $APP_ID
# クライアントシークレットを作成 (有効期限: 1年)
$CLIENT_SECRET = (az ad app credential reset --id $APP_ID --years 1 --query password -o tsv)
Write-Host "ClientSecret を取得しました。安全な場所に保存してください。"
Write-Host "Audience : api://$APP_ID"
⚠️ CLIENT_SECRETは一度しか表示されません。必ず安全な場所に保存してください。
前提: このステップでは
jqを使用します。 インストールされていない場合:sudo apt-get install jq(Debian/Ubuntu) またはbrew install jq(macOS)。jqが使えない場合の代替コマンドは以下に記載しています。
Bash の場合:
# サービスプリンシパルの Object ID を取得
SP_OID=$(az ad sp show --id $APP_ID --query id -o tsv)
# アプリ ロールの appRoleId を取得
ROLE_ID=$(az ad app show --id $APP_ID \
--query "appRoles[?value=='Api.Access'].id" -o tsv)
# 自身のサービスプリンシパルにロールを付与 (管理者同意) — jq を使う場合
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/servicePrincipals/${SP_OID}/appRoleAssignments" \
--body "$(jq -n \
--arg pid "$SP_OID" \
--arg rid "$SP_OID" \
--arg aid "$ROLE_ID" \
'{principalId: $pid, resourceId: $rid, appRoleId: $aid}')"
# jq がない場合は python3 で JSON を組み立てる
# az rest --method POST \
# --uri "https://graph.microsoft.com/v1.0/servicePrincipals/${SP_OID}/appRoleAssignments" \
# --body "$(python3 -c "
# import json, sys
# print(json.dumps({'principalId': '${SP_OID}', 'resourceId': '${SP_OID}', 'appRoleId': '${ROLE_ID}'}))")"PowerShell の場合:
# サービスプリンシパルの Object ID を取得
$SP_OID = (az ad sp show --id $APP_ID --query id -o tsv)
# アプリ ロールの appRoleId を取得
$ROLE_ID = (az ad app show --id $APP_ID --query "appRoles[?value=='Api.Access'].id" -o tsv)
# 自身のサービスプリンシパルにロールを付与 (管理者同意)
$body = @{
principalId = $SP_OID
resourceId = $SP_OID
appRoleId = $ROLE_ID
} | ConvertTo-Json
$tmpBodyFile = [System.IO.Path]::GetTempFileName()
$body | Out-File -FilePath $tmpBodyFile -Encoding utf8NoBOM
try {
az rest --method POST `
--uri "https://graph.microsoft.com/v1.0/servicePrincipals/$SP_OID/appRoleAssignments" `
--headers "Content-Type=application/json" `
--body "@$tmpBodyFile"
}
finally {
Remove-Item $tmpBodyFile -ErrorAction SilentlyContinue
}アプリ登録の情報を Kubernetes シークレットに反映します。
⚠️ 前提条件: このステップを実行する前に、以下をインストール・確認してください。
前提: kubectl と helm のインストール
AKS クラスターの管理に必須なツールをインストールしてください。
-
kubectl: 公式インストールガイド
- Linux: Linux への kubectl のインストール
- macOS: macOS への kubectl のインストール
- Windows: Windows への kubectl のインストール
-
helm: 公式インストールガイド
- Linux/macOS:
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash - Windows: Windows へのインストール
- Linux/macOS:
ステップ 2-5-1: AKS クラスターへの接続確認
Bicep デプロイで出力された AKS クラスター名を使用して、kubectl コンテキストを設定します。
Bash の場合:
# AKS クラスター名を取得
AKS_NAME=$(az deployment group show -g rg-azure-sre-agent-demo-lab -n main-aks --query properties.outputs.akS_CLUSTER_NAME.value -o tsv)
# AKS クラスターのクレデンシャルを取得
az aks get-credentials --resource-group rg-azure-sre-agent-demo-lab --name $AKS_NAME --overwrite-existing
# 接続確認
kubectl cluster-info
kubectl config current-context
# Secret 登録先の namespace を先に作成
kubectl apply -f k8s/namespace.yamlPowerShell の場合:
# AKS クラスター名を取得
$AKS_NAME = (az deployment group show -g rg-azure-sre-agent-demo-lab -n main-aks --query "properties.outputs.akS_CLUSTER_NAME.value" -o tsv)
# AKS クラスターのクレデンシャルを取得
az aks get-credentials --resource-group rg-azure-sre-agent-demo-lab --name $AKS_NAME --overwrite-existing
# 接続確認
kubectl cluster-info
kubectl config current-context
# Secret 登録先の namespace を先に作成
kubectl apply -f k8s/namespace.yaml接続が成功したら、以下のメッセージが表示されます。失敗した場合は、リソースグループ名、クラスター名、サブスクリプションを確認してください。
ステップ 2-5-2: Kubernetes シークレットの作成
Bash の場合:
kubectl create secret generic entra-id-secret \
--namespace azure-sre-agent-demo-lab \
--from-literal=TenantId="${TENANT_ID}" \
--from-literal=ClientId="${APP_ID}" \
--from-literal=Audience="api://${APP_ID}" \
--dry-run=client -o yaml | kubectl apply -f -
kubectl create secret generic cosmos-db-secret \
--namespace azure-sre-agent-demo-lab \
--from-literal=ConnectionString="$(az cosmosdb keys list \
--name $(az deployment group show -g rg-azure-sre-agent-demo-lab -n main-aks --query properties.outputs.cosmoS_ACCOUNT_NAME.value -o tsv) \
--resource-group rg-azure-sre-agent-demo-lab \
--type connection-strings \
--query connectionStrings[0].connectionString -o tsv)" \
--dry-run=client -o yaml | kubectl apply -f -PowerShell の場合:
# 2-1 の PowerShell 手順で取得した値を利用
# 別セッションで実行する場合は事前に設定してください
# $APP_ID = "<YOUR_CLIENT_ID>"
# $TENANT_ID = (az account show --query tenantId -o tsv)
# Cosmos DB の接続文字列を取得
$COSMOS_ACCOUNT_NAME = (az deployment group show -g rg-azure-sre-agent-demo-lab -n main-aks --query "properties.outputs.COSMOS_ACCOUNT_NAME.value" -o tsv)
$CONNECTION_STRING = (az cosmosdb keys list `
--name $COSMOS_ACCOUNT_NAME `
--resource-group rg-azure-sre-agent-demo-lab `
--type connection-strings `
--query "connectionStrings[0].connectionString" -o tsv)
# entra-id-secret を作成
kubectl create secret generic entra-id-secret `
--namespace azure-sre-agent-demo-lab `
--from-literal="TenantId=$TENANT_ID" `
--from-literal="ClientId=$APP_ID" `
--from-literal="Audience=api://$APP_ID" `
--dry-run=client -o yaml | kubectl apply -f -
# cosmos-db-secret を作成
kubectl create secret generic cosmos-db-secret `
--namespace azure-sre-agent-demo-lab `
--from-literal="ConnectionString=$CONNECTION_STRING" `
--dry-run=client -o yaml | kubectl apply -f -注:
k8s/catalog-service.yamlにはプレースホルダー入りの Secret テンプレートが含まれていましたが、このテンプレートはk8s/secrets.template.yamlに分離されています。 上記kubectlコマンドで実際の値を登録した後は、k8s/secrets.template.yamlを apply しないでください (上書きされます)。
Bash の場合:
# Bicep デプロイ名 (固定値にしない)
DEPLOYMENT_NAME="<YOUR_BICEP_DEPLOYMENT_NAME>"
# ACR をデプロイ出力から取得
ACR_NAME=$(az deployment group show -g rg-azure-sre-agent-demo-lab -n "$DEPLOYMENT_NAME" --query properties.outputs.ACR_NAME.value -o tsv)
ACR_LOGIN_SERVER=$(az deployment group show -g rg-azure-sre-agent-demo-lab -n "$DEPLOYMENT_NAME" --query properties.outputs.ACR_LOGIN_SERVER.value -o tsv)
# az acr login はレジストリ名 (xxxx) を渡す
az acr login --name "$ACR_NAME"
docker build -t catalog-service:latest -f src/CatalogService/Dockerfile .
docker build -t order-service:latest -f src/OrderService/Dockerfile .
docker build -t notification-service:latest -f src/NotificationService/Dockerfile .
docker tag catalog-service:latest "$ACR_LOGIN_SERVER/catalog-service:latest"
docker tag order-service:latest "$ACR_LOGIN_SERVER/order-service:latest"
docker tag notification-service:latest "$ACR_LOGIN_SERVER/notification-service:latest"
docker push "$ACR_LOGIN_SERVER/catalog-service:latest"
docker push "$ACR_LOGIN_SERVER/order-service:latest"
docker push "$ACR_LOGIN_SERVER/notification-service:latest"PowerShell の場合:
# ACR をリソースグループから取得
$ACR_NAME = (az acr list -g rg-azure-sre-agent-demo-lab --query "[0].name" -o tsv)
$ACR_LOGIN_SERVER = (az acr show -g rg-azure-sre-agent-demo-lab -n $ACR_NAME --query "loginServer" -o tsv)
# az acr login はレジストリ名 (xxxx) を渡す
az acr login --name $ACR_NAME
docker build -t catalog-service:latest -f src/CatalogService/Dockerfile .
docker build -t order-service:latest -f src/OrderService/Dockerfile .
docker build -t notification-service:latest -f src/NotificationService/Dockerfile .
docker tag catalog-service:latest "$ACR_LOGIN_SERVER/catalog-service:latest"
docker tag order-service:latest "$ACR_LOGIN_SERVER/order-service:latest"
docker tag notification-service:latest "$ACR_LOGIN_SERVER/notification-service:latest"
docker push "$ACR_LOGIN_SERVER/catalog-service:latest"
docker push "$ACR_LOGIN_SERVER/order-service:latest"
docker push "$ACR_LOGIN_SERVER/notification-service:latest"この Bicep は BYO (Bring Your Own) の AGC リソース と、ALB Controller 用の User Assigned Managed Identity + Federated Credential + 必要 RBAC まで作成します。AKS クラスター側には、Helm で ALB Controller をインストールします。
Bash の場合:
# Bicep デプロイ名 (デフォルト値: main-aks)
DEPLOYMENT_NAME="${DEPLOYMENT_NAME:-main-aks}"
# AKS クレデンシャルを取得
AKS_NAME=$(az deployment group show -g rg-azure-sre-agent-demo-lab -n "$DEPLOYMENT_NAME" --query properties.outputs.AKS_CLUSTER_NAME.value -o tsv)
ALB_CLIENT_ID=$(az deployment group show -g rg-azure-sre-agent-demo-lab -n "$DEPLOYMENT_NAME" --query properties.outputs.ALB_IDENTITY_CLIENT_ID.value -o tsv)
az aks get-credentials --resource-group rg-azure-sre-agent-demo-lab --name $AKS_NAME
# Helm を利用して ALB Controller をインストール
helm upgrade --install alb-controller oci://mcr.microsoft.com/application-lb/charts/alb-controller \
--namespace azure-alb-system \
--create-namespace \
--version 1.9.13 \
--set albController.namespace=azure-alb-system \
--set albController.podIdentity.clientID=$ALB_CLIENT_ID
# インストール確認
kubectl get pods -n azure-alb-system
kubectl get gatewayclass azure-alb-external -o yamlPowerShell の場合:
# Bicep デプロイ名 (デフォルト値: main-aks)
$DEPLOYMENT_NAME = if ($DEPLOYMENT_NAME) { $DEPLOYMENT_NAME } else { "main-aks" }
# AKS クレデンシャルを取得
$AKS_NAME = (az deployment group show -g rg-azure-sre-agent-demo-lab -n $DEPLOYMENT_NAME --query "properties.outputs.AKS_CLUSTER_NAME.value" -o tsv)
$ALB_CLIENT_ID = (az deployment group show -g rg-azure-sre-agent-demo-lab -n $DEPLOYMENT_NAME --query "properties.outputs.ALB_IDENTITY_CLIENT_ID.value" -o tsv)
az aks get-credentials --resource-group rg-azure-sre-agent-demo-lab --name $AKS_NAME
# Helm を利用して ALB Controller をインストール
helm upgrade --install alb-controller oci://mcr.microsoft.com/application-lb/charts/alb-controller `
--namespace azure-alb-system `
--create-namespace `
--version 1.9.13 `
--set albController.namespace=azure-alb-system `
--set albController.podIdentity.clientID=$ALB_CLIENT_ID
# インストール確認
kubectl get pods -n azure-alb-system
kubectl get gatewayclass azure-alb-external -o yaml詳細は ALB Controller (Helm) クイックスタート を参照してください。
注: Secret は手順 2-5 の
kubectl create secretコマンドで登録済みです。k8s/secrets.template.yamlはテンプレートとして提供されていますが、Secret を登録済みの場合は apply しないでください (実際の値がプレースホルダーで上書きされます)。k8s/*-service.yamlの image は<ACR_NAME>プレースホルダーを含むため、リポジトリ上の YAML は編集せず、一時ファイルへ置換してデプロイしてください。
Bash の場合:
./scripts/deploy-workloads-with-acr.sh your-acr-namePowerShell 7 の場合:
./scripts/deploy-workloads-with-acr.ps1 -AcrName your-acr-name上記スクリプトを使わずに適用する場合は、以下の手順でもデプロイできます。
ただし、<ACR_NAME> を実際の ACR ログインサーバー名に置き換える必要があります。
Bash の場合:
# ACR_NAME を設定
ACR_NAME="your-acr-name"
# manifest を置換して適用
sed "s|<ACR_NAME>|${ACR_NAME}|g" k8s/namespace.yaml | kubectl apply -f -
sed "s|<ACR_NAME>|${ACR_NAME}|g" k8s/catalog-service.yaml | kubectl apply -f -
sed "s|<ACR_NAME>|${ACR_NAME}|g" k8s/order-service.yaml | kubectl apply -f -
sed "s|<ACR_NAME>|${ACR_NAME}|g" k8s/notification-service.yaml | kubectl apply -f -PowerShell 7 の場合:
# ACR_NAME を設定
$ACR_NAME = "your-acr-name"
# manifest を置換して適用
(Get-Content k8s/namespace.yaml) -replace '<ACR_NAME>', $ACR_NAME | kubectl apply -f -
(Get-Content k8s/catalog-service.yaml) -replace '<ACR_NAME>', $ACR_NAME | kubectl apply -f -
(Get-Content k8s/order-service.yaml) -replace '<ACR_NAME>', $ACR_NAME | kubectl apply -f -
(Get-Content k8s/notification-service.yaml) -replace '<ACR_NAME>', $ACR_NAME | kubectl apply -f -k8s/gateway.yaml の <AGC_RESOURCE_ID> と <AGC_FRONTEND_NAME> を Bicep デプロイで出力された値に置き換えてから適用します。
Bash の場合:
# Bicep デプロイ名 (デフォルト値: main-aks)
DEPLOYMENT_NAME="${DEPLOYMENT_NAME:-main-aks}"
# AGC リソース ID を取得
AGC_ID=$(az deployment group show -g rg-azure-sre-agent-demo-lab -n "$DEPLOYMENT_NAME" \
--query properties.outputs.AGC_RESOURCE_ID.value -o tsv)
# AGC フロントエンド名を取得
AGC_FRONTEND_NAME=$(az deployment group show -g rg-azure-sre-agent-demo-lab -n "$DEPLOYMENT_NAME" \
--query properties.outputs.AGC_FRONTEND_NAME.value -o tsv)
# gateway.yaml のプレースホルダーを置換して適用
sed -e "s|<AGC_RESOURCE_ID>|${AGC_ID}|g" \
-e "s|<AGC_FRONTEND_NAME>|${AGC_FRONTEND_NAME}|g" \
k8s/gateway.yaml | kubectl apply -f -
# HTTPRoute を適用
kubectl apply -f k8s/httproutes.yamlPowerShell の場合:
# Bicep デプロイ名 (デフォルト値: main-aks)
$DEPLOYMENT_NAME = if ($DEPLOYMENT_NAME) { $DEPLOYMENT_NAME } else { "main-aks" }
# AGC リソース ID を取得
$AGC_ID = (az deployment group show -g rg-azure-sre-agent-demo-lab -n $DEPLOYMENT_NAME --query "properties.outputs.AGC_RESOURCE_ID.value" -o tsv)
# AGC フロントエンド名を取得
$AGC_FRONTEND_NAME = (az deployment group show -g rg-azure-sre-agent-demo-lab -n $DEPLOYMENT_NAME --query "properties.outputs.AGC_FRONTEND_NAME.value" -o tsv)
# gateway.yaml のプレースホルダーを置換して適用
(Get-Content k8s/gateway.yaml) `
-replace '<AGC_RESOURCE_ID>', $AGC_ID `
-replace '<AGC_FRONTEND_NAME>', $AGC_FRONTEND_NAME |
kubectl apply -f -
# HTTPRoute を適用
kubectl apply -f k8s/httproutes.yamlBash の場合:
# Bicep デプロイ名 (デフォルト値: main-aks)
DEPLOYMENT_NAME="${DEPLOYMENT_NAME:-main-aks}"
# Gateway のステータスを確認
kubectl get gateway -n azure-sre-agent-demo-lab
# HTTPRoute のステータスを確認
kubectl get httproute -n azure-sre-agent-demo-lab
# AGC フロントエンド FQDN を取得
AGC_FQDN=$(az deployment group show -g rg-azure-sre-agent-demo-lab -n "$DEPLOYMENT_NAME" \
--query properties.outputs.AGC_FRONTEND_FQDN.value -o tsv)
echo "Endpoint: http://${AGC_FQDN}"PowerShell の場合:
# Bicep デプロイ名 (デフォルト値: main-aks)
$DEPLOYMENT_NAME = if ($DEPLOYMENT_NAME) { $DEPLOYMENT_NAME } else { "main-aks" }
# Gateway のステータスを確認
kubectl get gateway -n azure-sre-agent-demo-lab
# HTTPRoute のステータスを確認
kubectl get httproute -n azure-sre-agent-demo-lab
# AGC フロントエンド FQDN を取得
$AGC_FQDN = (az deployment group show -g rg-azure-sre-agent-demo-lab -n $DEPLOYMENT_NAME --query "properties.outputs.AGC_FRONTEND_FQDN.value" -o tsv)
Write-Host "Endpoint: http://$AGC_FQDN"原因: kubectl が AKS クラスターに接続していません。
解決方法:
Bash の場合:
# 1. 接続しているクラスターを確認
kubectl config current-context
# 2. AKS に接続していない場合、クレデンシャルを取得
DEPLOYMENT_NAME="${DEPLOYMENT_NAME:-main-aks}"
AKS_NAME=$(az deployment group show -g rg-azure-sre-agent-demo-lab -n "$DEPLOYMENT_NAME" --query properties.outputs.AKS_CLUSTER_NAME.value -o tsv)
az aks get-credentials --resource-group rg-azure-sre-agent-demo-lab --name $AKS_NAME --overwrite-existing
# 3. 接続確認
kubectl cluster-info
kubectl get nodesPowerShell の場合:
# 1. 接続しているクラスターを確認
kubectl config current-context
# 2. AKS に接続していない場合、クレデンシャルを取得
$AKS_NAME = (az deployment group show -g rg-azure-sre-agent-demo-lab -n main-aks --query "properties.outputs.akS_CLUSTER_NAME.value" -o tsv)
az aks get-credentials --resource-group rg-azure-sre-agent-demo-lab --name $AKS_NAME --overwrite-existing
# 3. 接続確認
kubectl cluster-info
kubectl get nodes原因: リソースグループ名またはリソース名が正しくない可能性があります。
解決方法:
Bash の場合:
# Bicep デプロイの出力から Cosmos DB アカウント名を確認
az deployment group show \
-g rg-azure-sre-agent-demo-lab \
-n main-aks \
--query "properties.outputs.cosmoS_ACCOUNT_NAME.value" -o tsv
# リソースグループを確認
az group list --query "[].name" -o tablePowerShell の場合:
# Bicep デプロイの出力から Cosmos DB アカウント名を確認
az deployment group show `
-g rg-azure-sre-agent-demo-lab `
-n main-aks `
--query "properties.outputs.cosmoS_ACCOUNT_NAME.value" -o tsv
# リソースグループを確認
az group list --query "[].name" -o table解決方法:
Bash / PowerShell の場合 (どちらも同じコマンド):
# namespace を作成
kubectl create namespace azure-sre-agent-demo-labAKS にデプロイされたサービスはすべて JWT ベアラー認証で保護されています。 以下の手順で Entra ID からアクセストークンを取得し、API を呼び出してください。
Bash の場合:
TENANT_ID="<YOUR_TENANT_ID>" # az account show --query tenantId -o tsv
CLIENT_ID="<YOUR_CLIENT_ID>" # アプリ登録の Application (client) ID
CLIENT_SECRET="<YOUR_CLIENT_SECRET>"
API_SCOPE="api://${CLIENT_ID}/access_as_user"
# AGC フロントエンド FQDN を取得
AGC_FQDN=$(kubectl get gateway azure-sre-agent-demo-lab-gateway \
-n azure-sre-agent-demo-lab \
-o jsonpath='{.status.addresses[0].value}')
if [ -z "$AGC_FQDN" ]; then
AGC_FQDN=$(az deployment group show -g rg-azure-sre-agent-demo-lab -n main-aks \
--query properties.outputs.agC_FRONTEND_FQDN.value -o tsv)
fi
echo "AGC endpoint: ${AGC_FQDN}"
# 空の場合は以下で状態を確認:
# kubectl get gateway azure-sre-agent-demo-lab-gateway -n azure-sre-agent-demo-lab -o widePowerShell の場合:
$TENANT_ID = "<YOUR_TENANT_ID>" # az account show --query tenantId -o tsv
$CLIENT_ID = "<YOUR_CLIENT_ID>" # アプリ登録の Application (client) ID
$CLIENT_SECRET = "<YOUR_CLIENT_SECRET>"
$API_SCOPE = "api://$CLIENT_ID/access_as_user"
$DEPLOYMENT_NAME = if ($DEPLOYMENT_NAME) { $DEPLOYMENT_NAME } else { "main-aks" }
# AGC フロントエンド FQDN を取得
$AGC_FQDN = (kubectl get gateway azure-sre-agent-demo-lab-gateway -n azure-sre-agent-demo-lab -o jsonpath='{.status.addresses[0].value}' 2>$null)
if (-not $AGC_FQDN) {
$AGC_FQDN = (az deployment group show -g rg-azure-sre-agent-demo-lab -n $DEPLOYMENT_NAME --query "properties.outputs.AGC_FRONTEND_FQDN.value" -o tsv)
}
Write-Host "AGC endpoint: $AGC_FQDN"
# 空の場合は以下で状態を確認:
# kubectl get gateway azure-sre-agent-demo-lab-gateway -n azure-sre-agent-demo-lab -o wideBash の場合:
TOKEN=$(curl -s -X POST \
"https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=${CLIENT_ID}" \
-d "client_secret=${CLIENT_SECRET}" \
-d "scope=api://${CLIENT_ID}/.default" \
| jq -r .access_token)
if [ -n "$TOKEN" ]; then
echo "Token acquired: yes"
else
echo "Token acquired: no"
fi
jqがない場合はpython3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])"に置き換えてください。
PowerShell の場合:
# URL エンコード用の関数
function ConvertTo-UrlEncoded($str) {
return [System.Net.WebUtility]::UrlEncode($str)
}
$encodedClientSecret = ConvertTo-UrlEncoded $CLIENT_SECRET
$tokenResponse = Invoke-RestMethod `
-Method Post `
-Uri "https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" `
-ContentType "application/x-www-form-urlencoded" `
-Body "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$encodedClientSecret&scope=api://$CLIENT_ID/.default"
$TOKEN = $tokenResponse.access_token
if ($TOKEN) { Write-Host "Token acquired: yes" } else { Write-Host "Token acquired: no" }このフローでは Azure CLI にログイン済みのユーザー資格情報を使ってアクセストークンを取得します。CLIENT_SECRET は不要です。
前提: API 側に委任スコープ (
access_as_user) が公開され、ユーザー同意または管理者同意が済んでいること。
Bash の場合:
# Azure CLI にログイン (未ログイン時のみ)
az login --tenant "$TENANT_ID"
# 必要に応じて対象サブスクリプションを選択
az account set --subscription "<YOUR_SUBSCRIPTION_ID>"
TOKEN=$(az account get-access-token \
--tenant "$TENANT_ID" \
--scope "$API_SCOPE" \
--query accessToken -o tsv)
if [ -n "$TOKEN" ]; then
echo "Token acquired: yes"
else
echo "Token acquired: no"
fiPowerShell の場合:
# Azure CLI にログイン (未ログイン時のみ)
az login --tenant $TENANT_ID
# 必要に応じて対象サブスクリプションを選択
az account set --subscription "<YOUR_SUBSCRIPTION_ID>"
$TOKEN = (az account get-access-token `
--tenant $TENANT_ID `
--scope $API_SCOPE `
--query accessToken -o tsv)
if ($TOKEN) { Write-Host "Token acquired: yes" } else { Write-Host "Token acquired: no" }
az account get-access-tokenはユーザー委任トークンを返します。バックグラウンド サービスなどユーザー コンテキストなしで実行する場合は、上記のclient_credentialsフローを使用してください。
Bash の場合:
# 商品一覧取得 (CatalogService)
curl -s -H "Authorization: Bearer $TOKEN" \
http://$AGC_FQDN/catalog/api/products | jq .
# カテゴリ一覧取得 (CatalogService)
curl -s -H "Authorization: Bearer $TOKEN" \
http://$AGC_FQDN/catalog/api/categories | jq .
# 注文一覧取得 (OrderService)
curl -s -H "Authorization: Bearer $TOKEN" \
http://$AGC_FQDN/orders/api/orders | jq .
# 通知一覧取得 (NotificationService)
curl -s -H "Authorization: Bearer $TOKEN" \
http://$AGC_FQDN/notifications/api/notifications | jq .トークンなしでアクセスすると HTTP 401 Unauthorized が返ります:
curl -o /dev/null -w "%{http_code}" http://$AGC_FQDN/catalog/api/products # → 401
PowerShell の場合:
$headers = @{ Authorization = "Bearer $TOKEN" }
# 商品一覧取得 (CatalogService)
Invoke-RestMethod -Uri "http://$AGC_FQDN/catalog/api/products" -Headers $headers
# カテゴリ一覧取得 (CatalogService)
Invoke-RestMethod -Uri "http://$AGC_FQDN/catalog/api/categories" -Headers $headers
# 注文一覧取得 (OrderService)
Invoke-RestMethod -Uri "http://$AGC_FQDN/orders/api/orders" -Headers $headers
# 通知一覧取得 (NotificationService)
Invoke-RestMethod -Uri "http://$AGC_FQDN/notifications/api/notifications" -Headers $headersトークンなしでアクセスすると HTTP 401 Unauthorized が返ります:
(Invoke-WebRequest -Uri "http://$AGC_FQDN/catalog/api/products" -SkipHttpErrorCheck).StatusCode # → 401
Bash の場合:
# "premium" クエリで意図的なスローダウンを発生させる
curl -s -H "Authorization: Bearer $TOKEN" \
"http://$AGC_FQDN/catalog/api/products/search?q=premium"PowerShell の場合:
# "premium" クエリで意図的なスローダウンを発生させる
Invoke-RestMethod -Uri "http://$AGC_FQDN/catalog/api/products/search?q=premium" `
-Headers @{ Authorization = "Bearer $TOKEN" }- 期待される挙動: レスポンスに数秒かかる (Thread.Sleep ループ)
- OpenTelemetry でトレースを確認すると
SearchAsyncスパンのdurationが肥大化していることが分かる
Bash の場合:
# 100文字を超えるクエリで ArgumentOutOfRangeException を発生させる
LONG_QUERY=$(python3 -c "print('a' * 150)")
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer $TOKEN" \
"http://$AGC_FQDN/catalog/api/products/search?q=${LONG_QUERY}"
# → 500PowerShell の場合:
# 100文字を超えるクエリで ArgumentOutOfRangeException を発生させる
$LONG_QUERY = "a" * 150
$response = Invoke-WebRequest `
-Uri "http://$AGC_FQDN/catalog/api/products/search?q=$LONG_QUERY" `
-Headers @{ Authorization = "Bearer $TOKEN" } `
-SkipHttpErrorCheck
$response.StatusCode # → 500- 期待される挙動: HTTP 500 が返る
- Application Insights / OpenTelemetry にスタックトレース付きの例外ログが記録される
This repository includes a complete, executable 20-minute demo showing how the Azure SRE Agent detects incidents, forwards them through an Azure relay, and uses memory & reasoning to suggest resolutions for the downstream work item or issue.
-
Setup — Configure SRE Agent resources (5 min)
Bash の場合:
az deployment group create \ --resource-group <resource-group> \ --template-file infra/sre-overlay.bicep \ --parameters \ "environmentName=<environment-name>" \ "location=<base-resource-location>" \ "incidentRelayResourceId=<logic-app-resource-id>" \ "incidentRelayCallbackUrl=<logic-app-callback-url>" \ "responseTimeThresholdMs=500" \ "failedRequestCountThreshold=5"
PowerShell の場合:
az deployment group create ` --resource-group <resource-group> ` --template-file infra/sre-overlay.bicep ` --parameters ` "environmentName=<environment-name>" ` "location=<base-resource-location>" ` "incidentRelayResourceId=<logic-app-resource-id>" ` "incidentRelayCallbackUrl=<logic-app-callback-url>" ` "responseTimeThresholdMs=500" ` "failedRequestCountThreshold=5"
incidentRelayCallbackUrlmust be the full HTTP trigger callback URL returned bylistCallbackUrl, such ashttps://.../triggers/.../paths/invoke?...&sig=.... Do not use the Logic App overview URL.The quotes around each
key=valueargument are required when the callback URL contains query string parameters such as&sp=,&sv=, and&sig=.注意: SRE リソースだけを更新する場合は
infra/sre-overlay.bicepを使ってください。azd deploy後にinfra/main.bicepを再実行すると、Container Apps がプロビジョニング用サンプル image (mcr.microsoft.com/dotnet/samples:aspnetapp) に戻る可能性があります。サンプル image が表示される場合はazd deployを実行してサービス image を再反映してください。 -
Run the Demo — Complete incident cycle (20 min)
✅ Azure Monitor Alerts — Auto-detect latency & error rate anomalies ✅ Azure-native Relay Integration — Logic Apps or Azure Functions receive the Action Group payload ✅ Downstream Ticket Routing — Your relay can create Azure DevOps work items, GitHub issues, or both ✅ SRE Agent Memory & Runbooks — Knowledge base for incident response ✅ Agent Reasoning — Suggests root causes with confidence scores ✅ End-to-End Demo — Executable in 20 minutes
- SRE Agent Setup Guide — Complete setup instructions + prerequisites
- SRE Agent Setup Guide (Japanese) — 日本語版セットアップガイド
- 20-Minute Scenario — Executable demo with expected outputs
- 20-Minute Scenario (Japanese) — 日本語版デモシナリオ
- Entra ID アプリ登録セットアップ — Entra ID アプリケーション登録ガイド
- Azure SRE Agent Official Docs — Full reference
Microservices → OpenTelemetry → Application Insights
↓
Log Analytics (30-day retention)
↓
Alert Rules (latency, error rate)
↓
Action Group → Logic App / Azure Function
↓
Configured downstream work item / issue
↓
SRE Agent (reads incident + memory + telemetry)
↓
Agent Reasoning → Suggests root cause & resolution
- 2 Metric Alert Rules (latency, failed requests)
- 1 Action Group (routes to an Azure-native relay; the relay decides Azure DevOps, GitHub, or both)
- 1 Scheduled Query Alert (custom incident detection)
- Diagnostic Settings on Container Apps Environment
このアプリケーションには 教材目的 で1か所だけ意図的なパフォーマンス問題が埋め込まれています。
src/CatalogService/Services/ProductSearchService.cs の SearchAsync メソッド
-
スローダウン: クエリパラメータ
qにpremiumを含めてリクエストcurl http://localhost:5001/api/products/search?q=premium- 原因:
Thread.Sleep(100)を含む同期ループで全商品を逐次処理 - 結果: レスポンスタイムが数秒に膨張、スレッドプール枯渇
- 原因:
-
500エラー: クエリパラメータ
qに100文字を超える文字列を送信curl "http://localhost:5001/api/products/search?q=$(python3 -c 'print("a"*150)')"- 原因:
String.Substringの境界チェック不備 - 結果:
ArgumentOutOfRangeException→ HTTP 500
- 原因:
各サービス起動時に Cosmos DB へ自動投入:
| データ | 件数 | 説明 |
|---|---|---|
| 商品 | 20 | 電子機器、衣料品、食品、書籍、プレミアム |
| カテゴリ | 5 | Electronics, Clothing, Food, Books, Premium |
| 在庫 | 20 | 各商品に対応 |
| 注文 | 10 | 様々なステータス |
| 通知 | 15 | 注文確認、出荷通知、プロモーション等 |
注: Cosmos DB のデータベース名
GlobalAzureDemoは、プロジェクト名をAzureSREAgentDemoLabに変更した後もランタイム(データプレーン)の値として意図的に維持しています。
本プロジェクトは MIT License の下で公開されています。