🐏

【AKS】Azure Kubernetes Service で Azure App Configuration を使用する をやってみた

2023/11/28に公開

先日まで、AKSで App Configuration プロバイダーを使用するのがPreviewだったと思うんですが、GAされたようなので、これを機にクイックスタートをやってみました。

実行環境

ローカル Azure Cloud Shell[1]

【事前準備】Azure リソースの作成

こちらはすでに作成していたらskipして大丈夫です。

リソースグループの作成

名前はご自由に。

Azure Cloud Shell
$ RGNAME=aks-app-configuration-quickstart
$ az group create --location japaneast --name $RGNAME

App Configuration ストア の作成

App Configuration ストアを作成する を元に作成します。
ストア名はグローバルに一意である必要があるので各自変更してください。

Azure Cloud Shell
$ az appconfig create --location japaneast --name app-configuration-quickstart-1986 --resource-group $RGNAME --sku Free

Azure Container Registry の作成

Azure Cloud Shell
$ ACRNAME=aksappconfigurationquickstartacrh58lsi
$ az acr create --resource-group $RGNAME --name $ACRNAME --sku Basic --admin-enabled
  • (--admin-enabled をつけ忘れた場合)管理者ユーザの有効

Portal から コンテナレジストリの画面の、アクセスキーから管理者ユーザのチェックボックスにチェックを入れて、管理者ユーザを有効化します。作成時にオプション付けないとこれが必要です。

AKS クラスターを作成する

Kubernetes クラスターを作成する を元に作成します。

Azure Cloud Shell
$ AKSNAME=aks-app-configuration-cluster
$ az aks create --resource-group $RGNAME --name $AKSNAME --node-count 2 --generate-ssh-keys --attach-acr $ACRNAME

AKS で実行されているアプリケーションを作成する

アプリケーションの作成

  1. .NET7アプリケーションを作成します。
    今回は作業用のディレクトリを作ってから、その中にアプリケーションを作成しています。
Azure Cloud Shell
$ dotnet new webapp --output MyWebApp --framework net7.0

Welcome to .NET 7.0!
---------------------
SDK Version: 7.0.403

Telemetry
---------
The .NET tools collect usage data in order to help us improve your experience. It is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the DOTNET_CLI_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell.

Read more about .NET CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry

----------------
Installed an ASP.NET Core HTTPS development certificate.
To trust the certificate run 'dotnet dev-certs https --trust' (Windows and macOS only).
Learn about HTTPS: https://aka.ms/dotnet-https
----------------
Write your first app: https://aka.ms/dotnet-hello-world
Find out what's new: https://aka.ms/dotnet-whats-new
Explore documentation: https://aka.ms/dotnet-docs
Report issues and find source on GitHub: https://github.com/dotnet/core
Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
--------------------------------------------------------------------------------------
The template "ASP.NET Core Web App" was created successfully.
This template contains technologies from parties other than Microsoft, see https://aka.ms/aspnetcore/7.0-third-party-notices for details.

Processing post-creation actions...
Restoring /usr/cloudshell/MyWebApp/MyWebApp.csproj:
  Determining projects to restore...
  Restored /usr/cloudshell/MyWebApp/MyWebApp.csproj (in 52 ms).
Restore succeeded.
dotnetバージョンについて

公式は6.0 でしたが、2023/11/24 時点 6.0で作成しようとすると7.0 で作りなさいというエラーになりました。Azure Cloud Shell でのSDKのバージョンが7系になったからのようです。

Azure Cloud Shell
$ dotnet new webapp --output MyWebApp --framework net6.0

Welcome to .NET 7.0!
---------------------
SDK Version: 7.0.403

Telemetry
---------
The .NET tools collect usage data in order to help us improve your experience. It is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the DOTNET_CLI_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell.

Read more about .NET CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry

----------------
Installed an ASP.NET Core HTTPS development certificate.
To trust the certificate run 'dotnet dev-certs https --trust' (Windows and macOS only).
Learn about HTTPS: https://aka.ms/dotnet-https
----------------
Write your first app: https://aka.ms/dotnet-hello-world
Find out what's new: https://aka.ms/dotnet-whats-new
Explore documentation: https://aka.ms/dotnet-docs
Report issues and find source on GitHub: https://github.com/dotnet/core
Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
--------------------------------------------------------------------------------------
Error: Invalid option(s):
--framework net6.0
   'net6.0' is not a valid value for --framework. The possible values are:
      net7.0   - Target net7.0

For more information, run:
   dotnet new webapp -h

For details on the exit code, refer to https://aka.ms/templating-exit-codes#127
  1. Pages/Index.cshtml のコードを以下に置き換えます。
Azure Cloud Shell
$ cd MyWebApp
$ vi Pages/Index.cshtml
Pages/Index.cshtml
@page
@model IndexModel
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
    ViewData["Title"] = "Home page";
}

<style>
    h1 {
        color: @Configuration["Settings:FontColor"];
    }
</style>

<div class="text-center">
    <h1>@Configuration["Settings:Message"]</h1>
</div>
  1. プロジェクト ディレクトリのルートに mysettings.json という名前 のファイルを作成し、次の内容を入力します。
Azure Cloud Shell
# touch mysettings.json
# vi mysettings.json
mysettings.json
{
  "Settings": {
    "FontColor": "Black",
    "Message": "Message from the local configuration"
  }
}
  1. Program.cs を変更してJSONファイルを読み込むようにします。
Azure Cloud Shell
$ vi Program.cs
Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();

+ // Add a JSON configuration source 
+ builder.Configuration.AddJsonFile("mysettings.json"); 

var app = builder.Build();

アプリケーションのコンテナー格納

  1. アプリをビルドしてアセットファイルを作成します。
Azure Cloud Shell
$ dotnet publish -c Release -o published
MSBuild version 17.7.3+4fca21998 for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
  MyWebApp -> /usr/cloudshell/MyWebApp/bin/Release/net7.0/MyWebApp.dll
  MyWebApp -> /usr/cloudshell/MyWebApp/published/
  1. プロジェクト ディレクトリのルートに Dockerfile を作成します。Dockerfileの中身は以下のようにします。
Azure Cloud Shell
$ touch Dockerfile
$ vi Dockerfile
Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS runtime
WORKDIR /app
COPY published/ ./
ENTRYPOINT ["dotnet", "MyWebApp.dll"]
  1. aspnetapp という名前のコンテナー イメージをビルドします。
Azure Cloud Shell
$ docker build --tag aspnetapp .
[+] Building 5.1s (8/8) FINISHED
 => [internal] load .dockerignore                                                                                  0.1s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load build definition from Dockerfile                                                               0.1s
 => => transferring dockerfile: 159B                                                                               0.0s
 => [internal] load metadata for mcr.microsoft.com/dotnet/aspnet:7.0                                               0.5s
 => [1/3] FROM mcr.microsoft.com/dotnet/aspnet:7.0@sha256:567be43cf97d21523845af3cadf576517bc541cbf18d4a5d11b3687  4.0s
 => => resolve mcr.microsoft.com/dotnet/aspnet:7.0@sha256:567be43cf97d21523845af3cadf576517bc541cbf18d4a5d11b3687  0.0s
 => => sha256:567be43cf97d21523845af3cadf576517bc541cbf18d4a5d11b36873d2b025c4 1.79kB / 1.79kB                     0.0s
 => => sha256:f60c55ee78a09d0047fad6ac7638ad83368fe660e95e2f73848ff2f051b6f4d8 1.37kB / 1.37kB                     0.0s
 => => sha256:c4ee1b0c8d5d61209773fde896aad6dc5335320d5d60355208dfffb1b3c710b7 2.36kB / 2.36kB                     0.0s
 => => sha256:b7f91549542cca4b35a34cdee5364339f17468971ea730bb072864d3e78c8b94 31.42MB / 31.42MB                   1.6s
 => => sha256:a976ebe67a5a13f3b88541bec8be0171b680df446365c3d24140a2892b92eb34 14.97MB / 14.97MB                   0.5s
 => => sha256:01f22fa85c0ae740e3132b4e518b3dcf1ca9fe8bf540c5cd072d5047295742bf 32.46MB / 32.46MB                   1.3s
 => => sha256:ef7250e794662a50cc3b6a01908c0f71cd794bbcff7f85f1b8857513ecad2888 155B / 155B                         0.6s
 => => sha256:f16f1898acfe5c94c7831885068d3371c10b2df2e3dc3774f8ab04ae03413eeb 10.12MB / 10.12MB                   1.1s
 => => extracting sha256:b7f91549542cca4b35a34cdee5364339f17468971ea730bb072864d3e78c8b94                          1.2s
 => => extracting sha256:a976ebe67a5a13f3b88541bec8be0171b680df446365c3d24140a2892b92eb34                          0.2s
 => => extracting sha256:01f22fa85c0ae740e3132b4e518b3dcf1ca9fe8bf540c5cd072d5047295742bf                          0.4s
 => => extracting sha256:ef7250e794662a50cc3b6a01908c0f71cd794bbcff7f85f1b8857513ecad2888                          0.0s
 => => extracting sha256:f16f1898acfe5c94c7831885068d3371c10b2df2e3dc3774f8ab04ae03413eeb                          0.1s
 => [internal] load build context                                                                                  0.1s
 => => transferring context: 8.40MB                                                                                0.0s
 => [2/3] WORKDIR /app                                                                                             0.2s
 => [3/3] COPY published/ ./                                                                                       0.1s
 => exporting to image                                                                                             0.1s
 => => exporting layers                                                                                            0.1s
 => => writing image sha256:a4e49d52258c9473ef2a5a2c3fddc3a5edb84ac4c6cd95cbcaf5d20195f7ba37                       0.0s
 => => naming to docker.io/library/aspnetapp:latest                                                                0.0s

Azure Container Registry にイメージをプッシュする

  1. az acr login でレジストリにログインします。
Azure Cloud Shell
$ az acr login --name $ACRNAME
Login Succeeded
  1. イメージ用のタグを作成します
Azure Cloud Shell
$ docker tag aspnetapp $ACRNAME.azurecr.io/aspnetapp:v1
  1. イメージをコンテナー レジストリにアップロードします。
Azure Cloud Shell
$ docker push $ACRNAME.azurecr.io/aspnetapp:v1
The push refers to repository [aksappconfigurationquickstartacrh58lsi.azurecr.io/aspnetapp]
3c9e99900b64: Pushed
12613b9b3d18: Pushed
ee7107da446b: Pushed
9165c11d69c3: Pushed
db3084a64bb8: Pushed
d3a323cd3227: Pushed
1b6fd3ad4ce6: Pushed
v1: digest: sha256:dc020484dfd734052ba76bd726625d8befc5a4ad790c8299fa0fcdb99faae0b0 size: 1789

アプリケーションのデプロイ

  1. Deployment ディレクトリを作成します。
Azure Cloud Shell
$ mkdir Deployment
  1. deployment.yaml ファイルを追加します。ファイルの内容は以下のようにします。
Azure Cloud Shell
$ cd Deployment
$ touch deployment.yaml
$ vi deployment.yaml
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: aspnetapp-demo
  labels:
    app: aspnetapp-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: aspnetapp-demo
  template:
    metadata:
      labels:
        app: aspnetapp-demo
    spec:
      containers:
      - name: aspnetapp
        image: aksappconfigurationquickstartacrh58lsi.azurecr.io/aspnetapp:v1 # 自分のACR名に変更する
        ports:
        - containerPort: 80
  1. service.yaml ファイルを Deployment ディレクトリに追加します。LoadBalancerの設定を記述します。
Azure Cloud Shell
$ touch service.yaml
$ vi service.yaml
service.yaml
apiVersion: v1
kind: Service
metadata:
  name: aspnetapp-demo-service
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: aspnetapp-demo
  1. アプリケーションを AKS クラスターにデプロイします。
Azure Cloud Shell
$ cd ../
[ /usr/cloudshell/MyWebApp ]# kubectl create namespace appconfig-demo
namespace/appconfig-demo created
[ /usr/cloudshell/MyWebApp ]# kubectl apply -f ./Deployment -n appconfig-demo
deployment.apps/aspnetapp-demo created
service/aspnetapp-demo-service created
  1. LoadBalancer によって公開される外部 IP アドレスを取得します。
$ kubectl get service aspnetapp-demo-service -n appconfig-demo
NAME                     TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
aspnetapp-demo-service   LoadBalancer   10.0.168.44   4.241.27.99   80:30783/TCP   67s
  1. ブラウザで EXTERNAL-IP にアクセスします。

    表示されました~!

App Configuration Kubernetes プロバイダー を使用する

Azure App Configuration ストアのセットアップ

KeyとValueをストアに追加します。

Key Value
Settings:FontColor green
Settings:Message "Hello from Azure App Configuration"

※ダブルクォートはなくてもいいみたいです。

App Configuration Secret マニュフェストの作成

公式docsだとワークロードID を使ったやり方で接続させるみたいなんですが、うまくいかなかったので、おとなしく App Configuration の接続文字列を使ったやり方をします。
接続文字列の使用

そのために、接続文字列を保持するsecretを作成します。
なんだか本末転倒感もありますが、背に腹は代えられぬ。

まずは、Deployment ディレクトリの下に appConfigurationSecret.yaml を作成します。

appConfigurationSecret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: app-configuration-secret
type: Opaque
data:
  azure_app_configuration_connection_string: <接続文字列をbase64エンコードした文字列>

接続文字列は App Configuration の [アクセスキー] から、読み取り専用キーの接続文字列をコピーします。その値を丸ごと base64 エンコードしてください。

App Configuration Kubernetes プロバイダーをセットアップする

  1. AKS クラスターのアクセス資格情報を取得します。
Azure Cloud Shell
$ az aks get-credentials --resource-group $RGNAME --name $AKSNAME
  1. helm を使用して、Azure App Configuration Kubernetes プロバイダーを AKS クラスターにインストールします。
Azure Cloud Shell
$ helm install azureappconfiguration.kubernetesprovider \
     oci://mcr.microsoft.com/azure-app-configuration/helmchart/kubernetes-provider \
     --namespace azappconfig-system \
     --create-namespace
Pulled: mcr.microsoft.com/azure-app-configuration/helmchart/kubernetes-provider:1.0.0
Digest: sha256:e4634b18d6a151bf81a92114c397549cd2b14a03ead50b3ea7a06b038fe6ad89
NAME: azureappconfiguration.kubernetesprovider
LAST DEPLOYED: Mon Nov 27 01:21:56 2023
NAMESPACE: azappconfig-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
  1. appConfigurationProvider.yaml ファイルを Deployment ディレクトリに追加して、AzureAppConfigurationProvider リソースを作成します。定義は以下の通り。
appConfigurationProvider.yaml
apiVersion: azconfig.io/v1
kind: AzureAppConfigurationProvider
metadata:
  name: appconfigurationprovider-sample
spec:
  connectionStringReference: app-configuration-secret # secret の名前
  target:
    configMapName: configmap-created-by-appconfig-provider
    configMapData:
      type: json
      key: mysettings.json
  1. Deployment ディレクトリ内の deployment.yaml ファイルを更新します。ConfigMap configmap-created-by-appconfig-provider をマウントされたデータ ボリュームとして使用します。
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: aspnetapp-demo
  labels:
    app: aspnetapp-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: aspnetapp-demo
  template:
    metadata:
      labels:
        app: aspnetapp-demo
    spec:
      containers:
      - name: aspnetapp
        image: aksappconfigurationquickstartacrh58lsi.azurecr.io/aspnetapp:v1
        ports:
        - containerPort: 80
+        volumeMounts:
+        - name: config-volume
+          mountPath: /app/mysettings.json # Dockerfile の WORKDIR のパス + mysettings.json
+          subPath: mysettings.json
+      volumes:
+      - name: config-volume 
+        configMap:
+          name: configmap-created-by-appconfig-provider 
+          items:
+          - key: mysettings.json
+            path: mysettings.json
  1. 変更をデプロイします。
Azure Cloud Shell
$ kubectl apply -f ./Deployment -n appconfig-demo

文字が変わりました!!!

おわりに

なんか簡単そうにできそうだなーと軽い気持ちでやってみたら、色々ハマりました。。
ワークロードIDでの接続はまた気合入れてできるようにしないと。
それでは。


おまけ

せっかく書いたけど、今回は使えなかったワークロードID の有効化も悔しいので記述しておきます。なんでconfigMapが読めなかったのかは、また検証しよう。。

ユーザマネージドIDを作成し、AKSでワークロードIDを有効にする

AzureAppConfiguration と AKS を接続するために、ワークロード ID を使う を元に作成、有効にしていきます。

  1. Azure Kubernetes Service (AKS) クラスターでワークロード ID を有効にします。
    既存の AKS クラスターを更新する
Azure Cloud Shell
$ az aks update -g $RGNAME -n $AKSNAME --enable-oidc-issuer --enable-workload-identity
  1. AKS クラスターの OIDC 発行者 URL のを取得します。
    OIDC 発行者 URL を取得する
Azure Cloud Shell
$ export AKS_OIDC_ISSUER="$(az aks show -n $AKSNAME -g $RGNAME --query "oidcIssuerProfile.issuerUrl" -otsv)"
$ echo $AKS_OIDC_ISSUER
https://eastus.oic.prod-aks.azure.com/00000000-0000-0000-0000-000000000000/00000000-0000-0000-0000-000000000000/ # この形式のURLが表示されればよい
  1. ユーザー割り当てマネージド ID を作成し、作成後にそのクライアント ID をメモします。
    ユーザー割り当てマネージド ID を作成する
    マネージドIDの追加画面から、こんな感じで入力して作成します。

作成されたら、マネージドIDの概要ページに遷移して、クライアントIDをコピーしておきます。

  1. マネージド ID、OIDC 発行者、サブジェクト間のフェデレーション ID 資格情報を作成します。言っていることが難しい。
    name に指定するのはなんでもよいと思います。
    identity-name は作成したユーザマネージドIDの名前です。
    issuerは上記で取得したAKS_OIDC_ISSUERです。
Azure Cloud Shell
$ az identity federated-credential create --name aksAppConfigAccessCredential --identity-name aks-ap-configuration-managed-id --resource-group $RGNAME --issuer $AKS_OIDC_ISSUER --subject system:serviceaccount:azappconfig-system:az-appconfig-k8s-provider --audience api://AzureADTokenExchange
  1. App Configuration にユーザ割り当てマネージドIDを付与し、App Configuration データ閲覧者ロールを付与します。
    AppConfigurationの画面に遷移し、[設定] -> [ID] の順で表示します。
    ユーザ割り当て済みのタブを選択します。

ユーザ割り当てマネージドIDの追加 から、先ほど作成したマネージドIDを選択して、追加します。

  1. ユーザ割り当てマネージドIDの画面に戻り、[Azure ロールの割り当て] を選択します。

[+ロールの割り当ての追加] を選択し、リソースグループをAKSがあるリソースグループにし、『App Configuration データ閲覧者』の役割にして保存します。

※今回はAKSとAppConfigurationは同じリソースグループにしたので、これで大丈夫だと思いますが、別のリソースグループの場合だと、サブスクリプションレベルにしないといけないかもしれません。

脚注
  1. 途中でdockerコマンドを使うので、ローカル環境じゃないとできません。途中で詰みました。WSLでAzure Cloud Shell dockerを使うのが便利です。https://level69.net/archives/31099 ↩︎

Discussion