Azure Kubernetes Service 環境で MicroProfile Config 動作検証

## 1. Azure Kubernetes Service 環境で MicroProfile Config 動作検証

アプリケーションは、外部システムとの連携のための接続情報 (DB や、外部の HTTP エンドポイント)や、開発環境、テスト環境、本番環境などの環境設定の差を、プログラム・ソースコードから切り離し、外部の設定ファイル等に書き出すことで容易に接続先や設定を切り替えることができます。
また、外部の設定ファイルなどに書き出しておくことで、接続先を切り替えるためにアプリケーションのソースコードの編集やビルドは不要で、同一のソースコードや実行ライブラリを利用できます。


クラウド・ネィティブなアプリケーションを構築していくために、設定情報の外だしはとても重要です。
「[参照：The Twelve Factors](https://12factor.net/ja/)  の[III. 設定](https://12factor.net/ja/config) : 設定を環境変数に格納する」

```
Twelve-Factorは 設定をコードから厳密に分離すること を要求する。
```

MicroProfile Config を利用すると、設定情報を下記のようなさまざまな場所から取得できます。
これらの設定場所を ConfigSources と呼び、 同じプロパティが複数の ConfigSource で定義されている場合、ポリシーを適用しどの値が有効かを指定します。

* Java VM のシステム ・プロパティから
* OS の環境変数
* 外部構成ファイル (.properties, .xml)から
* LDAP, DB, Key-Value ストア などの外部データそ＝す


また状況によっては、一部のデータソースを動的に切り替えたい場合があります。そして変更した値は、アプリケーションを再起動することなく、プログラム上から更新した内容を利用する必要があります。こうしたニーズに応えるため、MicroProfile Config では、構成した値を変更直後から利用できるようになっています。


## MicroProfile Config の実装について

[Microprofile Config は API](https://javadoc.io/doc/org.eclipse.microprofile.config/microprofile-config-api/latest/index.html) のみを規定しており実装は含まれていません。
MicroProfile Config の実装は、各 MicroProfile の実装プロバイダから個別に提供されています。

* [Apache Geronimo Config](https://svn.apache.org/repos/asf/geronimo/components/config/trunk)
* [WebSphere Liberty 2017 March-June Beta so far](https://developer.ibm.com/wasdev/)
* [Payara Server 173 and Payara Micro 173](https://docs.payara.fish/documentation/microprofile/config.html)
* [WildFly & Thorntail](https://github.com/smallrye/smallrye-config)
* [microBean™ MicroProfile Config](https://microbean.github.io/microbean-microprofile-config/)


## MicroProfile Config の概要

MicroProfile Config は数少ない API から構成されています。

[MicroProfile Config API 1.4 の一覧](https://javadoc.io/doc/org.eclipse.microprofile.config/microprofile-config-api/latest/index.html)

* [Config](https://javadoc.io/doc/org.eclipse.microprofile.config/microprofile-config-api/latest/org/eclipse/microprofile/config/Config.html)
* [ConfigBuilder](https://javadoc.io/doc/org.eclipse.microprofile.config/microprofile-config-api/latest/org/eclipse/microprofile/config/spi/ConfigBuilder.html)
* [ConfigProperty](https://javadoc.io/doc/org.eclipse.microprofile.config/microprofile-config-api/latest/org/eclipse/microprofile/config/inject/ConfigProperty.html)
* [ConfigProvider](https://javadoc.io/doc/org.eclipse.microprofile.config/microprofile-config-api/latest/org/eclipse/microprofile/config/ConfigProvider.html)
* [ConfigProviderResolver](https://javadoc.io/doc/org.eclipse.microprofile.config/microprofile-config-api/latest/org/eclipse/microprofile/config/spi/ConfigProviderResolver.html)
* [ConfigSource](https://javadoc.io/doc/org.eclipse.microprofile.config/microprofile-config-api/latest/org/eclipse/microprofile/config/spi/ConfigSource.html)
* [ConfigSourceProvider](https://javadoc.io/doc/org.eclipse.microprofile.config/microprofile-config-api/latest/org/eclipse/microprofile/config/spi/ConfigSourceProvider.html)
* [Converter](https://javadoc.io/doc/org.eclipse.microprofile.config/microprofile-config-api/latest/org/eclipse/microprofile/config/spi/Converter.html)


## ConfigSource の優先順位
Configは、登録されたorg.eclipse.microprofile.config.spi.ConfigSourceから収集された情報で構成されます。 これらのConfigSourceは、順序に従ってソートされます。 これにより、外部から重要度の低い設定を上書きできます。

デフォルトでは、3つのデフォルトConfigSourceがあります。

* System.getProperties()（優先順位 = 400）
* System.getenv()（優先順位= 300）
* ClassPath上の META-INF/microprofile-config.properties ファイル（デフォルト優先順位 = 100、各ファイル中に config_ordinal プロパティを設定して個別に優先順位を設定可能）

デフォルト値は、アプリケーションのパッケージ時にファイル内で指定でき、値はデプロイメントごとに後から上書きできます。 ***「優先順位は値が大きいほど優先されます。」***


## 設定情報の取得例

MicroProfile Config 仕様では、設定値を読み取るために 2種類の方法を用意しています。

* プログラム的な設定情報の取得
* アノテーションを利用した設定情報の取得


### 1. プログラム的な設定情報の取得

プログラム的に Config インスタンスを取得し設定情報を取得するサンプルを下記に示します。

```
public class  MyAppWithGetConfigFromProgram {

    public Response invokeMicroserviceWithConfig() {
        // Config インスタンスの取得
        Config config = ConfigProvider.getConfig();
        // マイクロサービス A の URL を取得
        String microserviceA = config.getValue("URL_OF_MICROSERVICE_A", String.class);
        // マイクロサービス A の呼び出し
        return invokeMicroservice(microserviceA);
    }
}
```

設定情報を取得するためには、最初に [Config](https://javadoc.io/doc/org.eclipse.microprofile.config/microprofile-config-api/latest/org/eclipse/microprofile/config/Config.html) インスタンスを取得しなければなりません。
プログラム的に Config インスタンスを取得するためには、[ConfigProvider#getConfig()](https://javadoc.io/static/org.eclipse.microprofile.config/microprofile-config-api/1.4/org/eclipse/microprofile/config/ConfigProvider.html#getConfig--) を呼び出して取得できます。  
（Config クラスのインスタンスは、生成されたのちコンテキストクラスローダーに登録されます。）




### 2. アノテーションを利用した設定情報の取得 (推奨)

アノテーションを利用し Config インスタンスを取得し、@ConfigProperty で設定情報を取得するサンプルを下記に示します。

```
@ApplicationScoped
public class MyAppWithGetConfigFromAnnotation {

    @Inject
    private Config config;

    //The property myprj.some.url must exist in one of the configsources, otherwise a
    //DeploymentException will be thrown.
    @Inject
    @ConfigProperty(name="myprj.some.url")
    private String someUrl;

    //The following code injects an Optional value of myprj.some.port property.
    //Contrary to natively injecting the configured value, this will not lead to a
    //DeploymentException if the value is missing.
    @Inject
    @ConfigProperty(name="myprj.some.port")
    private Optional<Integer> somePort;
}
```


## MicroProfile Config サンプル・アプリケーション


### 1. MicroProfile Config サンプル・プロジェクトの作成

[MicroProfile Starter](https://start.microprofile.io/) にアクセスし、MicroProfile のプロジェクトを作成します。

![](https://live.staticflickr.com/65535/49650925377_1310626472_z.jpg)

(DOWNLOAD) のリンクを押下すると MPConfigSample.zip ファイルがダウンロードできます。ファイルを展開すると下記のようなファイル・ディレクトリ構成が自動的に生成されています。

```
.
├── pom.xml
├── readme.md
└── src
    └── main
        ├── java
        │   └── com
        │       └── yoshio3
        │           └── MPConfigSample
        │               ├── HelloController.java
        │               ├── MPConfigSampleRestApplication.java
        │               └── config
        │                   └── ConfigTestController.java
        ├── resources
        │   └── META-INF
        │       └── microprofile-config.properties
        └── webapp
            ├── WEB-INF
            │   └── beans.xml
            └── index.html
11 directories, 8 files
```

そして、MicroProfile Config のサンプルコードが ConfigTestController.java に下記のように記載されています。

```
package com.yoshio3.MPConfigSample.config;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/config")
@RequestScoped
public class ConfigTestController {

    @Inject
    @ConfigProperty(name = "injected.value")
    private String injectedValue;

    @Path("/injected")
    @GET
    public String getInjectedConfigValue() {
        return "Config value as Injected by CDI " + injectedValue;
    }

    @Path("/lookup")
    @GET
    public String getLookupConfigValue() {
        Config config = ConfigProvider.getConfig();
        String value = config.getValue("value", String.class);
        return "Config value from ConfigProvider " + value;
    }
}
```

上記のコードでは、プロパティに記載された値を HTTP のレスポンスとして返す簡単なコードです。  
下記のように HTTP の GET メソッドで呼び出すと return 文で記載される文字列が返ってきます。

```
$ curl -X GET http://localhost:8080/data/config/injected
$ curl -X GET http://localhost:8080/data/config/lookup
```

実際の設定内容は、META_INF ディレクトリ配下の microprofile-config.properties ファイルに記載されています。

```
# プロパティ・ファイルの場所
└── src
    └── main
        ├── resources
        │   └── META-INF
        │       └── microprofile-config.properties
```

デフォルトで下記のプロパティが設定されています。

```
# プロパティ・ファイルに設定された値
injected.value=Injected value
value=lookup value
```


### 2. サンプル・プロジェクトのビルドと実行

MicroProfile Config の動作確認を行うため、プロジェクトをビルドし、アプリケーションを起動します。

```
# プロジェクトのビルド
$ mvn clean package

# アプリケーションの実行
$ java -jar target/MPConfigSample-microbundle.jar 

......
Payara Micro URLs:
http://192.168.100.7:8080/

'ROOT' REST Endpoints:
GET     /data/application.wadl
GET     /data/config/injected
GET     /data/config/lookup
GET     /data/hello
GET     /openapi/
GET     /openapi/application.wadl
]]
[2020-03-10T22:19:06.610+0900] [] [情報] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1583846346610] [levelValue: 800] Payara Micro  5.194 #badassmicrofish (build 327) ready in 32,755 (ms)
[2020-03-10T22:19:33.646+0900] [] [情報] [] [javax.enterprise.system.container.web.com.sun.web.security] [tid: _ThreadID=29 _ThreadName=http-thread-pool::http-listener(1)] [timeMillis: 1583846373646] [levelValue: 800] Context path from ServletContext:  differs from path from bundle: /
```

上記のようにアプリケーションが起動したのち、curl コマンドを実行し動作確認を行います。
正しく動作している場合、下記のようにプロパティ・ファイルから取得した設定 (Injected value, value) の文字列が表示されます。

```
# アノテーションで実装されたエンドポイントへの呼び出し
$ curl localhost:8080/data/config/injected
Config value as Injected by CDI Injected value

# プログラムで実装されたエンドポイントへの呼び出し
$ curl localhost:8080/data/config/lookup
Config value from ConfigProvider lookup value
```

MicroProfile ではプロパティ・ファイルの設定値をシステム・プロパティで上書き設定することができます。そこで環境変数を設定し、環境変数の値を Java のシステム・プロパティに代入して実行します。すると "microprofile-config.properties" ファイルに設定した値を上書きし、環境変数に設定した値が表示されている事を確認できます。　


```
# 環境変数の設定 [.(ドット）を _(アンダーバー）に置き換えて設定]
$ export injected_value="Environment Value"

# 環境変数を Java のシステム・プロパティに設定してアプリを実行
$ java -D"$injected_value" -jar target/MPConfigSample-microbundle.jar

# アプリケーションの動作確認
$ curl http://localhost:8080/data/config/injected
Config value as Injected by CDI Environment Value
```

***ご注意：　properties ファイル中では . (ドット）表記で記載していますが、環境変数は OS によっては . (ドット）表記が使えません。そこで、環境変数の設定では . (ドット）表記箇所を _ (アンダーバー）に置き換えて設定してください。実装内部で自動的に変換をしています。***


### 3. ローカルの Docker 環境での実行

ローカルの環境でアプリケーションの動作確認ができたので、次にローカルの Docker 環境で MicroProfile を動作させます。Payara Micro の Docker コンテナのイメージを作成するため、下記のような Dockerfile を作成してください。

```
FROM payara/micro:5.201

USER payara
WORKDIR ${PAYARA_HOME}

# Deploy Artifact
COPY ./target/MPConfigSample.war $DEPLOY_DIR

CMD ["--nocluster","--deploymentDir", "/opt/payara/deployments", "--contextroot", "app"]
```

次に、この Dockerfile を利用してコンテナのイメージを作成します。docker build コマンドを実行しコンテナのイメージを作成してください。

```
$ docker build -t tyoshio2002/payara-config-sample:1.0 .

# コマンド実行時のコンソール出力例
Sending build context to Docker daemon  151.2MB
Step 1/5 : FROM payara/micro:5.201
5.201: Pulling from payara/micro
050382585609: Already exists 
59f5185426ac: Already exists 
4d95208cd9c0: Pull complete 
c1409397cf71: Pull complete 
Digest: sha256:3ff92627d0d9b67454ee241cc7d5f2e485e46db81a886c87cf16035df7c80cc8
Status: Downloaded newer image for payara/micro:5.201
 ---> a11a548b0a25
Step 2/5 : USER payara
 ---> Running in cb755e484e79
Removing intermediate container cb755e484e79
 ---> 564283252ae4
Step 3/5 : WORKDIR ${PAYARA_HOME}
 ---> Running in f26dd5cd172c
Removing intermediate container f26dd5cd172c
 ---> f2bf88b18a77
Step 4/5 : COPY ./target/MPConfigSample.war $DEPLOY_DIR
 ---> 1b54373fe95a
Step 5/5 : CMD ["--nocluster","--deploymentDir", "/opt/payara/deployments", "--contextroot", "app"]
 ---> Running in 3eb731eb77c3
Removing intermediate container 3eb731eb77c3
 ---> 1d11549e99b8
Successfully built 1d11549e99b8
Successfully tagged tyoshio2002/payara-config-sample:1.0
```

コンテナのイメージが作成できたのち、コンテナを起動します。下記のコマンドを実行してコンテナを起動してください。

```
$ docker run -p 8080:8080 -e injected_value=hogehoge -it tyoshio2002/payara-config-sample:1.0

# コマンド実行時のコンソール出力例
..... (中略)
[2020-03-11T07:46:59.119+0000] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1583912819119] [levelValue: 800] [[ 
{
    "Instance Configuration": {
        "Host": "3877abb54d57",
        "Http Port(s)": "8080",
        "Https Port(s)": "",
        "Instance Name": "payara-micro",
        "Instance Group": "no-cluster",
        "Deployed": [
            {
                "Name": "MPConfigSample",
                "Type": "war",
                "Context Root": "/app"
            }
        ]
    }
}]]
[2020-03-11T07:46:59.131+0000] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1583912819131] [levelValue: 800] [[
Payara Micro URLs:
http://3877abb54d57:8080/app
'MPConfigSample' REST Endpoints:
GET	/app/data/application.wadl
GET	/app/data/config/injected
GET	/app/data/config/lookup
GET	/app/data/hello
]]
[2020-03-11T07:46:59.131+0000] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1583912819131] [levelValue: 800] Payara Micro  5.201 #badassmicrofish (build 512) ready in 31,286 (ms)
```

作成したコンテナの起動が完了したので、コンテナ上で動作するアプリケーションに対して接続をします。  
今回は、起動時にコンテナ内部の 8080番ポートを、ローカルの 8080番ポートにマッピングしていますので、ローカル環境の 8080 番ポートにアクセスすることで、コンテナのアプリケーションに接続できます。 
下記のコマンドを実行してください。

```
$ curl http://localhost:8080/app/data/config/injected
Config value as Injected by CDI hogehoge
```

コンテナの起動時に引数として環境変数 (***-e injected_value=hogehoge***) を与えているため、起動時に入力した文字列が表示されます。


### 4. Azure Kubernetes Service 環境での実行

ローカルの Docker 環境で動作確認ができたので、[Azure Kubernetes Service](https://aka.ms/docs-aks-tutorial) 環境で動作確認を行います。下記の手順に従い動作確認を行います。

1. Azure Container Registry 用のリソース・グループを作成  
2. Azure Container Registry を作成  
3. Azure Container Registry のパスワードの確認  
4. Azure Container Registry にログインしイメージを Push  
5. Azure Container Registry に Push したイメージの確認  
6. Azure Kubernetes Service 用のリソース・グループを作成   
7. Azure Kubernetes Service 用のサービス・プリンシパル作成  
8. AKS から ACR のリソースを参照できるように ACR に参照権限を付与  
9. AKS の作成  
10. kubectl コマンドのインストールと AKS 接続情報の取得  
11. Deployment YAML の作成  
12. Deployment YAML の適用  
13. Pod の動作確認  
14. 環境変数を設定し更新  
15. Kubernetes の Config Map からの設定情報の取得  
16. Config Map 値の更新と Pod の再起動  

---

#### 4.1. Azure Container Registry 用のリソース・グループを作成

まずは、[Azure Container Registry](https://aka.ms/docs-container-registry) を作成し、ローカルで作成した Docker コンテナのイメージをアップロードします。そこで、Azure Container Registry を作成するためのリソース・グループを作成します。

```
$ az group create --name WebApp-Containers --location "Japan East"
{
  "id": "/subscriptions/f77aafe8-****-****-****-d0c37687ef70/resourceGroups/WebApp-Containers",
  "location": "japaneast",
  "managedBy": null,
  "name": "WebApp-Containers",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null,
  "type": "Microsoft.Resources/resourceGroups"
}
```

#### 4.2. Azure Container Registry を作成

次に、Azure Container Registry を作成します。
  
* --name にコンテナ・レジストリ名を指定します  
* --resource-group に上記で作成したリソース・グループ名を指定します  
* --sku は "Basic", "Standard", "Premium" の何れかを指定します  
* --admin-enabled true に設定する事で、コンテナ・レジストリに docker コマンドでアクセスできるようにします  

```
$ az acr create --name containerreg4yoshio --resource-group WebApp-Containers --sku Basic --admin-enabled true
{
  "adminUserEnabled": true,
  "creationDate": "2020-03-12T02:27:59.357654+00:00",
  "id": "/subscriptions/f77aafe8-****-****-****-d0c37687ef70/resourceGroups/WebApp-Containers/providers/Microsoft.ContainerRegistry/registries/containerreg4yoshio",
  "location": "japaneast",
  "loginServer": "containerreg4yoshio.azurecr.io",
  "name": "containerreg4yoshio",
  "networkRuleSet": null,
  "policies": {
    "quarantinePolicy": {
      "status": "disabled"
    },
    "retentionPolicy": {
      "days": 7,
      "lastUpdatedTime": "2020-03-12T02:28:01.654662+00:00",
      "status": "disabled"
    },
    "trustPolicy": {
      "status": "disabled",
      "type": "Notary"
    }
  },
  "provisioningState": "Succeeded",
  "resourceGroup": "WebApp-Containers",
  "sku": {
    "name": "Basic",
    "tier": "Basic"
  },
  "status": null,
  "storageAccount": null,
  "tags": {},
  "type": "Microsoft.ContainerRegistry/registries"
}
```

#### 4.3. Azure Container Registry のパスワードの確認

次に、Azure Container Registry に接続するためのパスワードを確認します。
  
* --name にコンテナ・レジストリ名を指定します  
* --resource-group に上記で作成したリソース・グループ名を指定します  

```
$ az acr credential show --name containerreg4yoshio --resource-group WebApp-Containers
{
  "passwords": [
    {
      "name": "password",
      "value": "4zaIiLk*************+H1XO4AlYFvN"
    },
    {
      "name": "password2",
      "value": "fT03XPs*************Oq2cAZiVHV+L"
    }
  ],
  "username": "containerreg4yoshio"
}
```

#### 4.4. Azure Container Registry にログインしイメージを Push

次に、docker login コマンドを実行し Azure Container Registry に接続します。  
(パスワードは上記で取得したパスワードを入力してください。)  

ログインが完了すると、docker tag コマンドでイメージのタグ付けを行います。ローカルで作成した Docker コンテナのイメージ名に、コンテナ・レジストリの   "loginServer" 名 ( 例："containerreg4yoshio.azurecr.io") を付け加えた名前でタグ付けします。  

最後に、docker push コマンドを実行し、Azure Container Registry にイメージを Push します。

```
# Azure Container Registry にログイン
$ docker login containerreg4yoshio.azurecr.io -u containerreg4yoshio
Password: 
Login Succeeded

# Docker コンテナのタグ付け
$ docker tag tyoshio2002/payara-config-sample:1.0 containerreg4yoshio.azurecr.io/tyoshio2002/payara-config-sample:1.0

# Azure Container Registry にタグ付けしたイメージを Push
$ docker push containerreg4yoshio.azurecr.io/tyoshio2002/payara-config-sample:1.0

The push refers to repository [containerreg4yoshio.azurecr.io/tyoshio2002/payara-config-sample]
bbd197848553: Pushed 
ec40a5d738cc: Pushed 
f95fe3528c56: Pushed 
bded2364df91: Pushed 
1bfeebd65323: Pushed 
1.0: digest: sha256:689dbacc212d37afe09c43417bc79d8e241c3fa7b5cf71c27097ef535cf77f76 size: 1368
```

#### 4.5. Azure Container Registry に Push したイメージの確認

Azure Container Registry に正しくイメージが Push されていることを確認します。

```
$ az acr repository list -n containerreg4yoshio -g WebApp-Containers
Argument 'resource_group_name' has been deprecated and will be removed in a future release.
[
  "tyoshio2002/payara-config-sample"
]

$ az acr repository show-tags --name containerreg4yoshio  --repository tyoshio2002/payara-config-sample
[
  "1.0"
]
```


#### 4.6. Azure Kubernetes Service 用のリソース・グループを作成

次に Azure Kubernetes Service (AKS)  を作成します。まず、AKS を作成するリソース・グループを作成します。

```
$ az group create --name MC_yoshio-aks --location "Japan East"
{
  "id": "/subscriptions/f77aafe8-****-****-****-d0c37687ef70/resourceGroups/MC_yoshio-aks",
  "location": "japaneast",
  "managedBy": null,
  "name": "MC_yoshio-aks",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null,
  "type": "Microsoft.Resources/resourceGroups"
}
$ 
```

#### 4.7. Azure Kubernetes Service 用のサービス・プリンシパル作成

まず AKS で利用するサービスプリンシパルを作成します。作成時には既定のロールはアサインしていません。

```
$ az ad sp create-for-rbac --skip-assignment
{
  "appId": "884ac0da-****-****-****-c9e2d3fce495",
  "displayName": "azure-cli-2020-03-13-03-12-59",
  "name": "http://azure-cli-2020-03-13-03-12-59",
  "password": "03a0e079-****-****-****-a760333af0b0",
  "tenant": "72f988bf-****-****-****-2d7cd011db47"
}
```

#### 4.8. AKS から ACR のリソースを参照できるように ACR に参照権限を付与

Azure を利用する場合、Kubernetes 内でコンテナ・レジストリへの接続情報を登録せず (通常は Secret 内に接続情報を登録し利用)、Azure の Azure Active Directory (Azure AD) のサービス プリンシパルを利用して、Azure Container Registry 内に存在するコンテナのイメージを取得することができます。  

[参照：サービス プリンシパルによる Azure Container Registry 認証](https://aka.ms/serviceprincipal-auth-for-acr)

まず、ACR に権限を付与するため、az acr show コマンドで ACR のリソース ID を取得します。

次に、az role assignment create コマンドで ACR に対して、AKS で利用するサービスプリンシパル(上記で作成)に対して acrpull を許可します。

* --assignee は appId を指定 ("appId": "884ac0da-****-****-****-c9e2d3fce495",)
* --scope は 上記 ACR の ID を指定
* --role acrpull ロールを指定すると ACR からイメージをプル可能 (YAML 内でセキュリティ設定不要)


```
$ az acr show --resource-group WebApp-Containers --name containerreg4yoshio --query "id" --output tsv
/subscriptions/f77aafe8-****-****-****-d0c37687ef70/resourceGroups/WebApp-Containers/providers/Microsoft.ContainerRegistry/registries/containerreg4yoshio

$ az role assignment create --assignee 884ac0da-****-****-****-c9e2d3fce495 --scope /subscriptions/f77aafe8-****-****-****-d0c37687ef70/resourceGroups/WebApp-Containers/providers/Microsoft.ContainerRegistry/registries/containerreg4yoshio --role acrpull
{
  "canDelegate": null,
  "id": "/subscriptions/f77aafe8-****-****-****-d0c37687ef70/resourceGroups/WebApp-Containers/providers/Microsoft.ContainerRegistry/registries/containerreg4yoshio/providers/Microsoft.Authorization/roleAssignments/72cbc68a-****-****-****-1a66c8568cba",
  "name": "72cbc68a-****-****- ****-1a66c8568cba",
  "principalId": "33472555-****-****-****-31e2064fb702",
  "principalType": "ServicePrincipal",
  "resourceGroup": "WebApp-Containers",
  "roleDefinitionId": "/subscriptions/f77aafe8-****-****-****-d0c37687ef70/providers/Microsoft.Authorization/roleDefinitions/7f951dda-****-****-****-43fe172d538d",
  "scope": "/subscriptions/f77aafe8-****-****-****-d0c37687ef70/resourceGroups/WebApp-Containers/providers/Microsoft.ContainerRegistry/registries/containerreg4yoshio",
  "type": "Microsoft.Authorization/roleAssignments"
}
```
#### 4.9. AKS の作成

最後に、AKS を作成します。

* --resource-group は 4.6 で作成したリソース・グループ名を指定します
* --name は AKS クラスターの名前を指定します
* --kubernetes-version は AKS クラスターの Kubernetes のバージョンを指定します
* --node-vm-size は VM サイズを指定します
* --node-count は Kubernetes のワーカー・ノードの数を指定します
* --location は東日本を指定します
* --service-principal は 4.7 のサービスプリンシパル作成時の appId を指定します
* --client-secret  は 4.7 のサービスプリンシパル作成時の password を指定します
* --generate-ssh-keys は作成時に SSH キー(Public,Private)がユーザーディレクトリの .ssh に作成されます

```
az aks create \
    --resource-group MC_yoshio-aks \
    --name yoshioAKSCluster1164 \
    --kubernetes-version 1.16.4 \
    --node-vm-size Standard_DS2_v2 \
    --node-count 3 \
    --location japaneast \
    --service-principal "884ac0da-****-****-****-c9e2d3fce495" \
    --client-secret  "03a0e079-****-****-****-a760333af0b0" \
    --generate-ssh-keys
```

注意：上記の AKS の作成時のコマンドは開発環境用、検証用として作成しています。AKS の本番環境を構築するためには、ネットワーク周りで仮想ネットワークを利用したり、外部サービス連携用のサブネットを切ったり、アベイラビリティ・ゾーンとして構築する必要があるため、本番環境構築のためにはより多くのオプション指定が必要になります。az aks create コマンドの詳細は下記をご参照ください。  

[参考：az aks コマンドの詳細](http://aka.ms/detail-az-aks-options)

#### 4.10. kubectl コマンドのインストールと AKS 接続情報の取得

AKS を操作するためには、kubectl コマンドを利用します。初めて Kubernetes を操作する場合、操作環境に kubectl コマンドがインストールされていないかもしれません。kubectl コマンドがインストールされていない場合は az aks install-cli コマンドを実行して入手します。  

kubectl コマンドを入手後、kubectl コマンドを利用して AKS を操作します。作成した AKS に接続するためには、AKS への接続情報が必要になります。そこで AKS への接続情報（クレデンシャル）を az aks get-credentials コマンドを実行して取得してください。

```
# kubectl コマンドのインストール
$ az aks install-cli
Downloading client to "/usr/local/bin/kubectl" from "https://storage.googleapis.com/kubernetes-release/release/v1.17.4/bin/darwin/amd64/kubectl"
Please ensure that /usr/local/bin is in your search PATH, so the `kubectl` command can be found.
$ which kubectl 
/usr/local/bin/kubectl

# AKS の接続情報（クレデンシャル）の取得
$ az aks get-credentials --resource-group MC_yoshio-aks --name yoshioAKSCluster1164
Merged "yoshioAKSCluster1164" as current context in /Users/yoterada/.kube/config

# kubernetes のクライアントとサーバのバージョン確認
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.4", GitCommit:"8d8aa39598534325ad77120c120a22b3a990b5ea", GitTreeState:"clean", BuildDate:"2020-03-12T21:03:42Z", GoVersion:"go1.13.8", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.4", GitCommit:"224be7bdce5a9dd0c2fd0d46b83865648e2fe0ba", GitTreeState:"clean", BuildDate:"2019-12-13T20:40:52Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/amd64"}
```

#### 4.11. Deployment YAML の作成

次に、AKS 上で作成したコンテナを動作させるために、Deployment の YAML を作成します。
下記の YAML ファイルを作成し deployment.yaml として保存してください。

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payara-config-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: payara-config-service
  template:
    metadata:
      labels:
        app: payara-config-service
        version: v1
        stage: develop
    spec:
      securityContext:
        runAsUser: 1000 
      containers:
      - name: payara-config-service
        image: containerreg4yoshio.azurecr.io/tyoshio2002/payara-config-sample:1.0
        resources:
          requests:
            cpu: 100m
            memory: 1Gi
          limits:
            cpu: 200m
            memory: 1Gi
```

#### 4.12. Deployment YAML の適用

Deployment の YAML ファイルを作成したのち、AKS 環境に Deploy します。下記のコマンドを実行して YAML ファイルを適用してください。

```
$ kubectl apply -f deployment.yaml 
deployment.apps/payara-config-service created
```

#### 4.13. Pod の動作確認

正しく、Deployment YAML ファイルが適用されると kubectl get po のコマンドで STATUS が Running になっていることを確認できます。  

```
$ kubectl get po
NAME                                     READY   STATUS    RESTARTS   AGE
payara-config-service-85f5cdd768-42sh8   1/1     Running   0          28s
payara-config-service-85f5cdd768-fhjrh   1/1     Running   0          28s
```

仮に Running のステータスが表示されていない場合は、下記のコマンドを実行して原因を確認してください

```
$ kubectl describe po payara-config-service-85f5cdd768-42sh8  

もしくは
$ kubectl logs payara-config-service-85f5cdd768-42sh8  
```

ステータスが Running になっている場合、アプリケーションが動作しています。ローカルの環境からアプリケーションの動作確認を行うために、下記の port-forard コマンドを実行してください。

```
$ kubectl port-forward payara-config-service-85f5cdd768-42sh8 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
```

port-forward コマンドを実行した後、別のターミナルを開き curl コマンド、もしくはブラウザを起動しアプリケーションのエンドポイントにアクセスしてください。  
下記のように文字列が表示されていればアプリケーションが正しく動作しています。

```
$ curl localhost:8080/app/data/config/injected
Config value as Injected by CDI Injected value
```

#### 4.14. 環境変数を設定し更新

上記で、アプリケーションの動作確認ができましたが現時点で環境変数を設定していないため、アプリケーション内の properties ファイルに設定した値が表示されています。  
アプリケーション外部の環境変数から値をとるために、Kubernetes の Deployment YAML を修正し再度デプロイしてください。

追記する YAML の設定は下記の箇所です。

```
        env:
          - name: injected_value
            value: ENVIRONMENT VALUE
```

修正後の YAML ファイルは下記のようになります。

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payara-config-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: payara-config-service
  template:
    metadata:
      labels:
        app: payara-config-service
        version: v1
        stage: develop
    spec:
      securityContext:
        runAsUser: 1000 
      containers:
      - name: payara-config-service
        image: containerreg4yoshio.azurecr.io/tyoshio2002/payara-config-sample:1.0
        env:
          - name: injected_value
            value: ENVIRONMENT VALUE
        resources:
          requests:
            cpu: 100m
            memory: 1Gi
          limits:
            cpu: 200m
            memory: 1Gi
```

上記の YAML ファイルを編集した後、YAML ファイルを適用してください。

```
$ kubectl apply -f  deployment.yaml 
deployment.apps/payara-config-service configured
```

YAML を適用すると、ローリング・アップグレードで新しい Pod が起動されます。新しく起動した Pod 名を取得し port-foward コマンドを実行し、ローカルからアクセスできるようにしてください。

```
# Pod の一覧を取得
$ kubectl get po
NAME                                    READY   STATUS    RESTARTS   AGE
payara-config-service-58cdfd6c7-gfvcl   1/1     Running   0          37s
payara-config-service-58cdfd6c7-hdm6c   1/1     Running   0          48s

# Port Forawrd で k8s 上で動作する Pod の Port をローカルにフォワード
$ kubectl port-forward payara-config-service-58cdfd6c7-gfvcl 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
```

新しい Pod をローカルにフォワードした後、curl コマンドもしくはブラウザからエンドポイントにアクセスしてください。  

すると、YAML ファイル中に定義した環境変数が表示されるようになります。

```
$ curl http://localhost:8080/app/data/config/injected
Config value as Injected by CDI ENVIRONMENT VALUE
```

#### 4.15. Kubernetes の Config Map からの設定情報の取得

Kubernetes では、コンテナーの管理 (deployment.yaml) から設定を切り離して、一元管理できる [Config Map](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/) という仕組みが用意されています。そこで、Deployment YAML 中に設定した環境変数の設定を Config Map に外だしして Config Map 内で設定情報を管理するようにします。

下記の Config Map の YAML ファイルを作成し configmap.yaml  として保存してください。

```
apiVersion: v1
kind: ConfigMap
metadata:
  name: microprofile-service-config
data:
  injected_value: "CONFIG MAP VALUE"
```

次に、Config Map の設定を適用します。

```
# Config Map を適用
$ kubectl apply -f configmap.yaml 
configmap/microprofile-service-config createds

# Config Map の設定が追加されている事の確認
$ kubectl get cm
NAME                          DATA   AGE
microprofile-service-config   1      37m
```

次に、Kubernetes の Pod から Config Map の値を参照するように Deployment YAML ファイルの内容を修正します。  

修正箇所は下記になります。

```
        env:
          - name: injected_value
            valueFrom:
              configMapKeyRef:
                name: microprofile-service-config
                key: injected_value
```

修正した Deployment YAML は下記になります。

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payara-config-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: payara-config-service
  template:
    metadata:
      labels:
        app: payara-config-service
        version: v1
        stage: develop
    spec:
      securityContext:
        runAsUser: 1000 
      containers:
      - name: payara-config-service
        image: containerreg4yoshio.azurecr.io/tyoshio2002/payara-config-sample:1.0
        env:
          - name: injected_value
            valueFrom:
              configMapKeyRef:
                name: microprofile-service-config
                key: injected_value
        resources:
          requests:
            cpu: 100m
            memory: 1Gi
          limits:
            cpu: 200m
            memory: 1Gi

```

Deployment YAML を修正した後適用してください。  
適用した後 STATUS が Running になっていることを確認してください。  
再起動した Pod をローカルからアクセスできるように Port Forward してください。

```
# 修正した Deployment YAML の適用
$ kubectl apply -f  deployment.yaml 
deployment.apps/payara-config-service configured

# 再起動した Pod の一覧を取得
$ kubectl get po
NAME                                     READY   STATUS    RESTARTS   AGE
payara-config-service-68bb7c8dfb-8tcbx   1/1     Running   0          22s
payara-config-service-68bb7c8dfb-f67x6   1/1     Running   0          20s

# ローカルからアクセスするためにポート・フォワードを実行
$ kubectl port-forward payara-config-service-68bb7c8dfb-8tcbx 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
```

ポート・フォワードを実行した後、アプリケーションのエンドポイントにアクセスしてください。すると Config Map で指定した値 (CONFIG MAP VALUE) が表示されていることを確認できます。

```
$ curl http://localhost:8080/app/data/config/injected
Config value as Injected by CDI CONFIG MAP VALUE
```

上記で、Config Map から設定値を取り出しアプリケーションから参照できるようになりました。  

#### 4.16. Config Map 値の更新と Pod の再起動

最後に、設定値を変更する方法について確認します。  

4.15 で設定した Config Map の値を更新 (CONFIG MAP VALUE NEW") します。

```
apiVersion: v1
kind: ConfigMap
metadata:
  name: microprofile-service-config
data:
  injected_value: "CONFIG MAP VALUE NEW"
```

ファイルを保存した後、更新した Config Map を再度適用します。  
適用した後 describe コマンドで正しく修正されているかを確認します。

```
# 修正した Config Map を適用
$ kubectl apply -f configmap.yaml 
configmap/microprofile-service-config configured

# Config Map の値が変わっているか否かの確認
$ kubectl describe cm microprofile-service-config
Name:         microprofile-service-config
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","data":{"injected_value":"CONFIG MAP VALUE NEW"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"microprofile-...

Data
====
injected_value:
----
CONFIG MAP VALUE NEW
Events:  <none>
```

Config Map の設定を反映した直後、アプリケーションのエンドポイントにアクセスしてみてください。  
すると、新しい設定は反映されていない事が確認できます。  
これは、Kubernetes の Pod は Config Map に設定した値を起動時にのみ読みにいくためです。Config Map の変更をチェックし自動的に再読み込みは行われないため、すでに起動している Pod にアクセスすると古い設定情報が表示されます。

```
curl http://localhost:8080/app/data/config/injected
Config value as Injected by CDI CONFIG MAP VALUE
```

つまり、設定情報を反映するためには Pod の再起動が必要になります。　　
Deployment で Pod をインストールしている場合、Pod の再起動を行うためには対象の Pod を削除すれば自動的に新しい Pod が起動されます。(Deployment YAML 中の replicas の数分だけ Pod は起動することが保証されるため)  

そこで、Pod の一覧を取得した後、Pod を delete して新しい Pod が再起動されるのを待ちます。

```
# Pod の一覧を取得
$ kubectl get po
NAME                                     READY   STATUS    RESTARTS   AGE
payara-config-service-68bb7c8dfb-8tcbx   1/1     Running   0          4m24s
payara-config-service-68bb7c8dfb-f67x6   1/1     Running   0          4m22s

# 特定の Pod を削除
$ kubectl delete po payara-config-service-68bb7c8dfb-8tcbx
pod "payara-config-service-68bb7c8dfb-8tcbx" deleted

# Pod の一覧を取得（payara-config-service-68bb7c8dfb-dx7sg が新しい Pod）
$ kubectl get po
NAME                                     READY   STATUS    RESTARTS   AGE
payara-config-service-68bb7c8dfb-dx7sg   1/1     Running   0          26s
payara-config-service-68bb7c8dfb-f67x6   1/1     Running   0          5m27s
```

再起動した Pod を Port Forwad してローカルからアクセスできるようにします。

```
$ kubectl port-forward payara-config-service-68bb7c8dfb-dx7sg 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
```

Port Foward 後にアプリケーションのエンドポイントにアクセスすると Config Map で更新した値 (CONFIG MAP VALUE NEW) が表示されていることを確認できます。

```
$ curl http://localhost:8080/app/data/config/injected
Config value as Injected by CDI CONFIG MAP VALUE NEW
```

以上で、AKS 環境での MicroProfile Config の動作確認ができました。  

設定を外部に出して管理する事で、ソースコードの修正なしに外部の接続先などを動的に変更することができます。これによりアプリケーションのテストや管理などコストが大幅に削減され、クラウド・ネィティブのアプリに一歩近づきます。ぜひ、ご適用ください。