はじめての AKS によるサービス公開

## はじめに
自分メモです。
AKS を使って、ちゃちゃっと whoami サービスを立てる流れを記載しています。


## 実行環境
本記事では、以下の環境を使用しました。
異なるバージョンでは、同じ結果が得られない場合がありますが、ご了承ください。

| |Component|Version|
|:---:|:---|:---|
|OS|macOS|Big Sur 11.3.1|
|TOOL|Kubernetes (AKS)|1.19.9|
|TOOL|kubectl|1.19.7|
|TOOL|[Azure CLI](https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli)|2.21.0|
|TOOL|[kubectx, kubens](https://kubectx.dev/)|0.9.3|
|TOOL|[Helm](https://helm.sh/)|3.5.3|
|CHART|[traefik](https://artifacthub.io/packages/helm/traefik/traefik/9.19.0)|9.19.0|
|CHART|[whoami](https://artifacthub.io/packages/helm/cowboysysop/whoami/2.2.0)|2.2.0|
|DOMAIN|[freenom](https://www.freenom.com/) ||

> **注**:
> 記事中の `kc` コマンドは、`kubectl` コマンドのエイリアスです。
> 
> [Newbernetes: kubectl the Kubernetes CLI - Cory O'Daniel
](https://medium.com/coryodaniel/3751fb6e34ca) を参考にしました。

## Kubernetesクラスターのセットアップ
まずは、**Azure Kubernetes Service** (AKS) を使って、Kubernetesクラスターを準備します。

### 1. Kubernetesクラスターを準備する
[Azure Portal](https://portal.azure.com/) にログインし、Kubernetesクラスターを作っていきます。

1.**リソースの作成**から `コンテナー` > `Kubernetes Service` > `作成` を選びます。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/5693d51b-98c2-1488-214e-34ffe4e0bbe2.png)

2.**Kubenetes クラスターを作成**の`基本`項目を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/1149d720-95d7-9cf3-4c89-8a5a94c3b827.png)

**リソース グループ**、**Kubernetes クラスター名**は、今後のステップでも頻繁に出てきますので、分かり易い名前にしておくと良いでしょう。

ノード サイズは、必要なスペックのものを選んでください。ここでは、エントリー向けの`Av2`シリーズを選びました。

- **プロジェクトの詳細**
  - リソース グループ: `aks-learning`
- **クラスターの詳細**
  - Kubernetes クラスター名: `try-traefik-https`
  - 地域: `東日本`
  - 可用性ゾーン: `ゾーン 1, 2, 3`
  - Kubernetes バージョン: `1.19.9`
- **プライマリ ノード プール**
  - ノード サイズ: `Standard A2 v2`
  - ノード数: `3`

3.**Kubenetes クラスターを作成**の`ノードプール`、`認証`、`ネットワーク`項目はデフォルト値のまま使用しました。

4.**Kubenetes クラスターを作成**の`統合`項目を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/90653faa-d7aa-760e-f107-2a2a7d9ef52f.png)

今回のシナリオで、必要なものはありません。

- **Azure Container Registry**
  - コンテナー レジストリ: `なし`
- **Azure Monitor**
  - コンテナーの監視: `無効`
- **Azure Policy**
  - Azure Policy: `無効`

5.**Kubenetes クラスターを作成**の`タグ`項目を必要に応じて設定します。


6.`作成`ボタンを押して、リソースがデプロイされるまで待ちます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/32ec9dfc-a711-3961-0d59-7fb27f19b3b0.png)

### 2. Kubernetesクラスターに接続する
[Azure CLI](https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli) を使用して操作していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/94d0191e-82d3-8d30-9d4d-da070513c574.png)

先ほど、作成した**Kubernetesサービス**リソースを開き、`接続`メニューを押すと表示される**{クラスター名} に接続**の手順に従って進めます。

1.Cloud Shell を開く または Azure CLI

```bash
# Azure へサインイン （Webブラウザーが立ち上がります）
$ az login
⥤ ...
```

2.次のコマンドを実行します

```bash
# 既定のディレクトリを設定する
$ az account set --subscription SUBSCRIPTION＿ID

# 資格情報の取得
$ az aks get-credentials --resource-group RESOURCE_GROUP_NAME --name K8S_CLUSTER_NAME
```

```bash:ex
$ az account set --subscription xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
$ az aks get-credentials --resource-group aks-learning --name try-traefik-https
⥤ Merged "try-traefik-https" as current context in /Users/eternity1984/.kube/config
```

### 3. whoami をデプロイ
`whoami` は、OS情報とHTTPリクエストを出力できるウェブサーバーです。クラスターの動作確認に便利です。

ここでは、クラスターの動作確認が目的なので、[kubectx, kubens](https://kubectx.dev/)や、[helm](https://helm.sh/) を使って、サクッと構築してしまいましょう。

1.クラスターの準備が整っているか事前に確認します。

複数のクラスターを扱うときに、雑にしていると大惨事になるかもしれないので、癖をつけておきましょう。

```bash
# 使用中のコンテキスト
$ kubectx
⥤   docker-desktop
⥤ * try-traefik-https

# 間違ったコンテキストが選ばれている場合は、切り替えます
$ kubectx try-traefik-https
⥤ Switched to context "try-traefik-https".

# 名前空間のリスト
$ kubens
⥤ * default
⥤   kube-node-lease
⥤   kube-public
⥤   kube-system
```

2.**whoami**用の名前空間を作ります。

```bash
# 名前空間の作成
$ kc create ns whoami
⥤ namespace/whoami created

# 名前空間の切り替え
$ kubens whoami
⥤ Context "try-traefik-https" modified.
⥤ Active namespace is "whoami".
```

3.**helm** を使って、`whoami` をインストールします。

```bash
# リポジトリの追加
$ helm repo add cowboysysop https://cowboysysop.github.io/charts/
⥤ "cowboysysop" has been added to your repositories

# cowboysysop/whoami のインストール
# バージョン指定する場合は、--version 2.2.0 をつけてください
$ helm install whoami cowboysysop/whoami
⥤ NAME: whoami
⥤ LAST DEPLOYED: Sat May  8 13:17:17 2021
⥤ NAMESPACE: whoami
⥤ STATUS: deployed
⥤ REVISION: 1
⥤ ...
```

4.正常に動作しているか確認します。

```bash
# ポッドの状態
$ kc get po
⥤ NAME                     READY   STATUS    RESTARTS   AGE
⥤ whoami-6d648779d-kn6k6   1/1     Running   0          21s

# サービスの状態
$ kc get svc
⥤ NAME     TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
⥤ whoami   ClusterIP   10.0.85.25   <none>        80/TCP    35s
```

5.`whoami`サービスは`EXTERNAL-IP`をもっていないため、ポートフォワードを使ってサービスのポート`:80`にアクセスできるようにします。

```bash
$ kc port-forward svc/whoami 8080:80
⥤ Forwarding from 127.0.0.1:8080 -> 80
⥤ Forwarding from [::1]:8080 -> 80
```

6.Webブラウザから http://localhost:8080/ にアクセスしてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/4ce91145-6334-c2dd-4ede-c1ec403825c4.png)

お疲れ様でした!!
Kubernetesクラスターでサービスが動くことを確認できました。


## ドメインを使ってサービスにアクセスする
次に、ドメインを使ってサービスにアクセスできるようにしていきます。
本記事では、**freenom**でドメインを取って**Azure DNS**でホスティングします。

### 1. ドメインの取得

1.[freenom](https://www.freenom.com/)にアクセスして、アカウントにサインインしましょう。
※アカウント作成については省略します。

2.`Services` > `Register a New Domain` を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/4b287642-7874-b290-0734-46c073e9a064.png)

3.使いたいドメイン名が利用可能かチェックします。
使えそうなドメインがあれば、`今すぐ入手！`を押して、`選択`状態にして、`チェックアウト`を押します。ここでは、`try0ut.ml`を選びました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/5f246761-a45b-6b09-4ee5-a02e5367e003.png)

4.`Period`は、`12 Months @ FREE`を選び、`Continue`を押します。
お金を払えば、長期的に確保することもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/a18b153d-eaae-5013-b6c3-3f62a0881556.png)

5.`I have read and agree to the Terms & Conditions`にチェックを入れ、`Complete Order`を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/3bf86920-6422-0d6e-7fff-770edb6b7655.png)

6.この画面が表示されたら、ひとまずokです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/c47d44b3-35d8-03f5-bd8e-c19c567e68f0.png)
すぐに **Azure DNS ゾーン**のネームサーバーに書き換えるので、ページはそのまま置いておきましょう。

### 2. Azure DNS ゾーンを準備する
[Azure Portal](https://portal.azure.com/)に戻り、**Azure DNS ゾーン**のリソースを作ります。

1.**リソースグループ**のリストから、**Kubernetesクラスター**をデプロイしたリソース グループを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/6680f43f-aa16-2df5-8f1a-88c8b3030dbb.png)

2.**リソースグループ**の `概要` > `追加` > `Marketplace` を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/a4ce92fd-f06a-144a-585b-b25f640a2545.png)

3.**検索窓**に`dnszone`と入力します。**DNS ゾーン**が表示されたら、`作成` > `DNS ゾーン` を選びましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/0a436ab9-c09e-052d-7291-2aeba39422f4.png)

4.**DNS ゾーンの作成**の`基本`項目を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/64e762db-f55d-cd7a-192e-844c05a1a3bf.png)

- **プロジェクトの詳細**
  - リソース グループ: `aks-learning`
- **インスタンスの詳細**
  - 名前: `try0ut.ml` (取得したドメイン名を指定) 

5.`確認および作成`に進み、リソースがデプロイされるまで待ちます。

6.**DNS ゾーン**の`概要`を選択し、**ネーム サーバー**を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/874271db-4629-8b99-72ad-a2435fefc2fc.png)

この**DNS ゾーン**では、以下のようになっていました。

||ネームサーバー|
|---:|:---|
|1|ns1-04.azure-dns.com.|
|2|ns2-04.azure-dns.net.|
|3|ns3-04.azure-dns.org.|
|4|ns4-04.azure-dns.info.|

### 3. ネーム サーバーを指定する
開いたままにしている **freenom** のページに戻り、カスタム ネーム サーバーの設定をしていきます。

1.TOP から `Services` > `My Domains` に進み、取得したドメインの `Manage Domain` を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/e22e110d-fe4f-8658-f1c1-2e4cac56db08.png)

2.`Management Tools` > `Nameservers` を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/102e2aa2-c0b3-30fb-2db9-90204a3b7886.png)

3.`Use custom nameservers (enter below)` にチェックを入れ、**DNS ゾーン**に表示されていたネーム サーバーを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/83291ed2-48e6-c024-bbf7-409e2fb4363a.png)

4.`Change Nameservers`を押して、**freenom** での作業は完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/41146391-7e9b-2342-046b-5ad0e3ba3e32.png)

後は、１年おきにドメインの更新をどうするか気にする程度です。

### 4. 静的 IP アドレスを確保する
[Azure Portal](https://portal.azure.com/)に戻り、**パブリック IP アドレス**のリソースを作ります。

**サービス タイプ** を `LoadBalancer` にして `loadBalancerIP` を設定しなければクラウド プロバイダー側で IP アドレスを割り当ててくれますが、何かの拍子に変わっても面倒なので、自分で **パブリック IP アドレス** をデプロイするようにしました。

1.**リソースグループ**のリストから、AKSクラスターが格納されている(`MC_` から始まる)リソース グループを開きます。

2.`概要` > `追加` > `Marketplace` を選んで**リソースの作成**に移動し、**パブリック IP アドレス** を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/afc483c1-684a-0be6-3a8b-aa2407e130a0.png)

3.**パブリック IP アドレスの作成**の項目を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/03b9e4a6-c44d-1216-5857-59f9824d3464.png)

- IP バージョン: `IPv4`
- SKU: `Standard`
- レベル: `地域`
- **IPv4 IP アドレスの構成:**
  - 名前: `try0ut.ml` (DNS Zone と同じ名前だと分かり易い) 
  - ルーティングの優先順位: `Microsoft ネットワーク`
- サブスクリプション: `＊＊＊` (使用中のサブスクリプション)
- リソース グループ: `MC_＊＊＊` (クラスターのリソースグループ)
- 場所: `東日本`
- 可用性： `ゾーン冗長`

ここでは、**パブリック IP アドレス** リソースとして、`20.48.10.146` が割り当てられました。

4.作成したリソースの `構成` > `エイリアス レコードの作成` を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/6faa7dd0-227d-70a9-2b49-1dc99f863cb2.png)

5.**エイリアス レコードの作成**で、Aレコードを **2つ** 作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/f39d01b7-eeca-155e-8a78-e630566c96c2.png)

- DNS ゾーン: `try0ut.ml` (作成済みの DNS ゾーンを指定)
- 名前: `@`.try0ut.ml, `*`.try0ut.ml

6.`dig`コマンドを使って、名前解決できているか確認します。

```bash
$ dig try0ut.ml +short
⥤ 20.48.10.146
```

### 5. Traefik v2 を導入する

`nginx` か `traefik` か迷いましたが、色々できそうだったので`traefik`を選びました。

1.**traefik**用の名前空間を作ります。

```bash
# 名前空間の作成
$ kc create ns traefik
⥤ namespace/traefik created

# 名前空間の切り替え
$ kubens traefik
⥤ Context "try-traefik-https" modified.
⥤ Active namespace is "traefik".
```

3.**helm** を使って、`traefik` をインストールの準備をします。

```bash
# リポジトリの追加
$ helm repo add traefik https://helm.traefik.io/traefik
⥤ "traefik" has been added to your repositories

# 必要に応じて、ワークスペースへ移動
$ cd /path/to/workspace

# values.yaml へ書き出し
$ helm inspect values traefik/traefik > values.yaml
```

4.`values.yaml`の設定を書き換えます。

```diff_yaml:values.yaml
# Options for the main traefik service, where the entrypoints traffic comes
# from.
service:
  enabled: true
  type: LoadBalancer
  # Additional annotations (e.g. for cloud provider specific config)
  annotations: {}
  # Additional service labels (e.g. for filtering Service by custom labels)
  labels: {}
  # Additional entries here will be added to the service spec. Cannot contains
  # type, selector or ports entries.
-   spec: {}
+   spec:
+     loadBalancerIP: "20.48.10.146"
    # externalTrafficPolicy: Cluster
    # loadBalancerIP: "1.2.3.4"
    # clusterIP: "2.3.4.5"
  loadBalancerSourceRanges: []
    # - 192.168.0.1/32
    # - 172.16.0.0/16
  externalIPs: []
    # - 1.2.3.4
```

5.`values.yaml` を使って、`traefik` をインストールします。

```bash
# traefik/traefik のインストール
$ helm install traefik traefik/traefik -f values.yaml
⥤ W0509 08:03:03.104875    1336 warnings.go:70] apiextensions.k8s.io/v1beta1 CustomResourceDefinition is deprecated in v1.16+, unavailable in v1.22+; use apiextensions.k8s.io/v1 CustomResourceDefinition
⥤ ...
⥤ NAME: traefik
⥤ LAST DEPLOYED: Sun May  9 08:03:07 2021
⥤ NAMESPACE: traefik
⥤ STATUS: deployed
⥤ REVISION: 1
⥤ TEST SUITE: None
```

> **Warning**:
> **apiextensions.k8s.io/v1beta1 is DEPRECIATED...** は、 チャートのバージョンが上がれば、改善されるかもしれません。
[traefik/traefik-helm-chart #313](https://github.com/traefik/traefik-helm-chart/issues/303) 

6.指定したパブリック IP アドレス`20.48.10.146`が使用されているか確認します。

```bash
# サービスの状態
$ kc get svc
⥤ NAME      TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)                      AGE
⥤ traefik   LoadBalancer   10.0.50.250   20.48.10.146   80:32292/TCP,443:30555/TCP   95s
```

7.ダッシュボードを開いてみましょう。
`traefik`のダッシュボードを使うには、`deployment`または`pod`に対してポートフォワードします。

```bash
$ kc port-forward deploy/traefik 9000
⥤ Forwarding from 127.0.0.1:9000 -> 9000
⥤ Forwarding from [::1]:9000 -> 9000
```

8.Webブラウザで、http://localhost:9000/dashboard/ にアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/05dbdfc4-a55e-e731-f41e-6e9ad61a5366.png)

理由はよく分かりませんが、最後の`/`がついていないとつながりません。

### 6. IngressRoute で whoami を公開する

http://whoami.try0ut.ml/ で、`whoami`サービスにアクセスできるようにしていきます。

1.適当なフォルダに`whoami-ingress.yaml`を作成します。

```yaml:whoami-ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: whoami
spec:
  entryPoints:
    - web
    - websecure
  routes:
    - match: Host(`whoami.try0ut.ml`)
      kind: Rule
      services:
        - name: whoami
          namespace: whoami
          port: 80
```

> **Information:**
> `IngressRoute` は、`Service` などと違い、ポートに**ポート名**を指定することはできません。素直に**ポート番号**を指定します。

2.**whoami** 名前空間にリソースを作ります。

```bash
# 名前空間の切り替え
$ kubens whoami
⥤ Context "try-traefik-https" modified.
⥤ Active namespace is "whoami".

$ kc apply -f whoami-ingress.yaml
⥤ ingressroute.traefik.containo.us/whoami created
```
3.Webブラウザから、http://whoami.try0ut.ml/ にアクセスしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/eacccd38-3ebf-01ca-e99a-66f52e75102d.png)

4.https://whoami.try0ut.ml/ でアクセスしたときの証明書を確認してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/604208/0fc59775-503e-e195-2ce7-14311b927986.png)


## おわりに
**whoami** サービスを公開することができました!!

マニフェストを自分で書けることも大事ですが、誰かに触ってもらうときには抵抗感の排除が必須かなと思います。helm は、そういった抵抗感をグッと抑えてくれる気がしました。