Azure Kubernetes Service にコンテイナー化したSpring Bootのアプリをデプロイしてみる



# この記事のゴール

Azure Kubernetes Service (AKS) に Spring Boot Application のdocker コンテナーをデプロイする。

# 目次

1. az CLI をインストールする
2. kubectl CLI をインストールする
3. リソースグループを作る
4. Azure Container registry のリソースを作る
5. Azure Kubernetes Service のリソースを作る
6. Spring boot REST API のアプリケーションを作る
7. アプリケーションのdocker イメージを作る
8. docker イメージを ACS にプッシュする
9. AKS クラスターに接続する
10. AKS クラスターにデプロイする
11. アプリケーションに接続する
12. おわり

# az CLI をインストールする

[Azure CLI をインストールする方法](https://learn.microsoft.com/ja-jp/cli/azure/install-azure-cli)

## Macの場合

[Homebrew](https://brew.sh/)

macユーザーはhomebrewを使ってaz CLIをインストールのがおすすめ。上のリンクから  `homebrew` をインストールする。

```bash
brew install azure-cli
```

`homebrew` がインストールできたら、上のコマンドを実行する。

## Windowsの場合

[Windows での Azure CLI のインストール](https://learn.microsoft.com/ja-jp/cli/azure/install-azure-cli-windows?tabs=azure-cli)

## マニュアルでインストール

[インストーラーダウンロードリンク](https://aka.ms/installazurecliwindows)

上のダウンロードリンクからインストーラーをダウンロードし、az CLIのインストールを行う。

## wingetでインストール

```bash
winget install -e --id Microsoft.AzureCLI
```

Windows 11 または最新の Windows 10場合、 `winget` コマンドが PowerShell から使えるので、コマンドでも簡単にaz CLIをインストールできる。

## az CLI の確認

```bash
az --version
```

上記のコマンドを実行後、 `az` CLIのヴァージョン情報が取得できていたら、インストールは成功。

## az CLI で azure にログインする

```bash
az login
```

上記のコマンドで、azureに`az`CLIからログインできる。

```bash
az account list
```

ログインが成功したら、上記のコマンドで自分のアカウント情報を確認できる。

# kubectl CLI をインストールする

[Install Tools](https://kubernetes.io/docs/tasks/tools/)

## Macの場合

```bash
brew install kubectl
```

`kubectl` も `homebrew` を使えば、簡単にインストールできる。

## Windowsの場合

## Chocolatelyでインストール

[Installing Chocolatey](https://chocolatey.org/install)

上のリンクから `Chocolately` をインストールする。

```bash
choco install kubernetes-cli
```

`Chocolately` のインストールができたら、上記のコマンドで `kubectl` をインストールする。

## kubectl の確認

```bash
kubectl version --client --output=yaml
```

上記のコマンドを実行後、yaml形式でクライアントの情報が取得できていたら、インストールは成功。

# リソースグループを作る

![Screen_Shot_2022-10-07_at_9.50.36.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/d47cdf85-30e8-9dad-afc0-e76f20fc40ff.jpeg)

![Screen_Shot_2022-10-07_at_9.52.30.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/b196f6c9-ee64-6660-a303-7474d912d16a.jpeg)

![Screen_Shot_2022-10-07_at_10.12.51.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/281e3de7-4a25-340c-9230-c158c620b57e.jpeg)

![Screen_Shot_2022-10-07_at_10.15.12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/bb95bdb6-ae86-7405-e56b-7d04bcc39e16.jpeg)

![Screen_Shot_2022-10-07_at_10.17.44.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/5aa7b729-e1c5-84cf-1744-18c6ea36067c.jpeg)


# Azure Container registry のリソースを作る

[クイック スタート:Azure portal を使用して Azure コンテナー レジストリを作成する](https://learn.microsoft.com/ja-jp/azure/container-registry/container-registry-get-started-portal?tabs=azure-cli)

![Screen_Shot_2022-10-07_at_10.20.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/8ad90f92-620b-0827-72f3-88f461f59495.png)

![Screen_Shot_2022-10-07_at_10.22.27.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/d8a54740-f657-97e3-7840-6d44eef2829c.jpeg)

![Screen_Shot_2022-10-07_at_10.26.37.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/193bfef8-b642-a508-47d2-ea9ac2baeebf.jpeg)

![Screen_Shot_2022-10-07_at_10.29.11.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/7fdbea2c-548e-a4c2-29e9-e35480f11afe.jpeg)

![Screen_Shot_2022-10-07_at_10.41.46.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/4973aa8c-e4be-d8b8-f672-7606a14108ec.jpeg)

![Screen_Shot_2022-10-07_at_10.44.24.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/4667e1e1-440d-7f02-a587-36d8f3b62728.jpeg)

`Login Server` の値は後で使うため、どこかにメモっておく。 `Repositories` のセクションで実際にプッシュしたdockerイメージを確認することができる。

# Azure Kubernetes Service のリソースを作る

[クイック スタート:Azure portal を使用して Azure コンテナー レジストリを作成する](https://learn.microsoft.com/ja-jp/azure/aks/learn/quick-kubernetes-deploy-portal?tabs=azure-cli)

![Screen_Shot_2022-10-07_at_10.46.50.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/6a9d5c96-0d33-4471-7b09-8ec676f221dd.jpeg)

![Screen_Shot_2022-10-07_at_10.51.46.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/33cbfc92-48e9-fe52-a8e7-a9d3f8ce2a8e.jpeg)

![Screen_Shot_2022-10-07_at_11.01.23.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/febb560a-0f89-1fbb-b0db-e0b864dc66c6.jpeg)

![Screen_Shot_2022-10-07_at_11.01.24.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/5ca83679-6a43-5caa-8eda-6037d061fc64.jpeg)


Node size（VM) は一番安いのを選んだ。Scale methodとしてAutoscaleを選んでしまうと、フリープランの場合、  `exceeding approved Total Cores quota...`   などと怒られ、リソースを作る事が出来ないので、Manualを選びNode countを1にする。

![Screen_Shot_2022-10-07_at_11.12.10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/518b55c6-2fd6-96f6-4d31-42bc7ed1a95d.jpeg)

![Screen_Shot_2022-10-07_at_11.14.58.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/a13c2257-57a6-00dc-d864-ff486140c23e.jpeg)


Integration セクションで、先程作ったACRを選択しておくと、AKSからACRのイメージをプルする時に、認証エラーに悩まされなくなる。

![Screen_Shot_2022-10-07_at_12.11.18.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/de0511db-b337-199c-9501-6481286b5c6b.jpeg)

![Screen_Shot_2022-10-07_at_12.21.36.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/2f8262b8-2c7b-15f6-63fa-bbc04e9051ac.jpeg)

![Screen_Shot_2022-10-07_at_12.25.43.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/449453ab-0f41-7220-64a6-ddaa377e3f78.jpeg)


後で何度か使用するから、AKSの名前をメモっておく。Service and ingress セクションで、AKSに作られたサービス達を確認する事が出来る。アプリケーションの前に立たせるロードバランサーのパブリックIPアドレスも確認できる。

# Spring boot REST API のアプリケーションを作る

## Spring initializrでSpring Bootプロジェクトを生成する

[Spring Initializr](https://start.spring.io/)

## フォルダー階層図

```bash
.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── deployaks
    │   │               ├── DeployAksApplication.java
    │   │               └── controller
    │   │                   └── HelloWorldController.java # <- 追加
    │   └── resources
    │       ├── application.yml
    │       ├── static
    │       └── templates
    └── test
        └── java
            └── com
                └── example
                    └── deployaks
                        └── DeployAksApplicationTests.java
```

今回の記事のメインの目的はAKSにデプロイするすることなので、Spring Bootのアプリケーションの内容はかなり適当。

## HelloWorldController.java

```java
package com.example.deployaks.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorld {

    @GetMapping("/hello-world")
    public String helloWorld() {
        return "Hello World";
    }
}
```

コントローラーは一つだけ。`HelloWorldController.java`  ただ、”Hello World”のテキストを返すだけの、API サーバー。

# アプリケーションのdocker イメージを作る

## フォルダー階層図

```bash
.
├── Dockerfile  # <- 追加
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── deployaks
    │   │               ├── DeployAksApplication.java
    │   │               └── controller
    │   │                   └── HelloWorldController.java
    │   └── resources
    │       ├── application.yml
    │       ├── static
    │       └── templates
    └── test
        └── java
            └── com
                └── example
                    └── deployaks
                        └── DeployAksApplicationTests.java
```

ルートフォルダーに、Dockerfileを追加。内容は以下を参照。

```docker
FROM gradle:jdk17 AS build

ARG BUILD_DIR=/build

COPY . ${BUILD_DIR}
WORKDIR ${BUILD_DIR}

RUN gradle build --no-daemon

FROM openjdk:17.0

RUN mkdir /app

EXPOSE 8080

COPY --from=build "/build/build/libs/deploy-aks-0.0.1-SNAPSHOT.jar" /app/app.jar

ENTRYPOINT ["java", "-jar", "/app/app.jar"]
```

```bash
docker build -t <ACR Login Server>/deployaks:1.0 .
```

docker イメージを作成する。先程メモっといた、 `Login server` の値を  `<ACR Login Server>`　に代入してコマンドを実効する。一応、ACR の `Login server` の値は、azure portalの `Home` → `resource groups` → `<Resource Group Namw>` → `<ACR Name>` → `Overview` → `Login server` で確認できる。

注意: M1 mac または arm64 アーキテクチャで、docker イメージをビルドした場合、アーキテクチャーの違いで、AKSにデプロイができない。別途 amd64 アーキテクチャの intel mac、windowsを用意するか、github actions などのパイプラインを使用して、イメージをビルド、レジストリにプッシュするのが良い。やり方は、今回の記事では紹介しない。ごめんね。

# docker イメージを ACS にプッシュする

```bash
az acr login --name <Container Registry Name>
```

ACRに`az` CLIからログインする。

```bash
az acr login --name niceTestACR
Uppercase characters are detected in the registry name. When using its server url in docker commands, to avoid authentication errors, use all lowercase.
Login Succeeded
```

```bash
docker push <ACR Login Server>/deployaks:1.0
```

あとは先程ビルドしたイメージ名を指定して、ACRにプッシュする。あとタグの付け忘れには注意。

```bash
❯ docker push nicetestacr.azurecr.io/deployaks:1.0
The push refers to repository [nicetestacr.azurecr.io/deployaks]
0f006961ecf0: Pushed 
1a52b2c97567: Pushed 
e017d39c755a: Pushed 
03ee828ef0e4: Pushed 
b0d4c4485e7e: Pushed 
1.0: digest: sha256:12e0da0e29344f38bfc7c6ac472530d4dc2be3b92116c2d240b64e4bc2038f8f size: 1373
```

![Screen_Shot_2022-10-07_at_13.57.05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/8e1012b0-2267-5142-9e61-1b1358305e4a.jpeg)


ACRの`Repositories` のセクションに行くと、プッシュしたdocker イメージを確認できる。

# AKS クラスターに接続する

```bash
az account subscription list
```

上記のコマンドで  `Subscription ID` を確認する。初めてこのコマンドを実行した場合は、extension のインストールを促されるので、 `y` と答え、速やかにインストールする。

```bash
az account set --subscription <Subscriptiomn ID>
```

取得した`Subscription ID` を代入してコマンドを実行する。

```bash
az aks get-credentials --resource-group <Resource Group Name> --name <AKS Cluster Name>
```

リソースグループ名と、メモっておいたAKSの名前を代入し、上記のコマンドを実行する事によって、`kubectl`  から、AKS clusterに接続する事が出来るようになる。

```bash
kubectl get nodes
```

AKSで動いているnodeを確認する。

```bash
❯ kubectl get nodes
NAME                                STATUS   ROLES   AGE    VERSION
aks-agentpool-38233418-vmss000000   Ready    agent   120m   v1.23.12
```

# AKS クラスターにデプロイする

## k8sのマニフェストファイルを作る

```yaml
.
├── Dockerfile 
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── k8s
│   └── deployaks-app-depl.yml # <- 追加
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── deployaks
    │   │               ├── DeployAksApplication.java
    │   │               └── controller
    │   │                   └── HelloWorldController.java
    │   └── resources
    │       ├── application.yml
    │       ├── static
    │       └── templates
    └── test
        └── java
            └── com
                └── example
                    └── deployaks
                        └── DeployAksApplicationTests.java
```

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployaks-app-depl
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deployaks-app
  template:
    metadata:
      labels:
        app: deployaks-app
    spec:
      containers:
        - name: deployaks-app
          image: __DOCKER_IMAGE__ # sed コマンドで入れ替えるためのトークン

---
apiVersion: v1
kind: Service
metadata:
  name: deployaks-app-loadbalancer
spec:
  type: LoadBalancer
  selector:
    app: deployaks-app
  ports:
    - name: deployaks-port
      protocol: TCP
      port: 80
      targetPort: 8080
```

k8sのmanifestファイルの内容の説明は割愛する。spec.template.spec.containers.imageの `__DOCKER_IMAGE__` は `sed` コマンドですり替えを行うためのトークン。

```yaml
sed "s,__DOCKER_IMAGE__,<ACR Login Server>/deployaks:1.0," k8s/deployaks-app-depl.yml | kubectl apply -f -
```

`Login server` の値を  `<ACR Login Server>`   に代入して実行する。

```bash
❯ sed "s,__DOCKER_IMAGE__,nicetestacr.azurecr.io/deployaks:1.0," k8s/deployaks-app-depl.yml | kubectl apply -f -
deployment.apps/deployaks-app-depl created
service/deployaks-app-loadbalancer created
```

`created` と表示されていれば、ひとまずは成功。

```yaml
kubectl get deployments.apps
```

```bash
❯ kubectl get deployments.apps
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
deployaks-app-depl   1/1     1            1           77s
```

実際にデプロイが成功していることを確認。

```bash
kubectl get pods
```

```bash
❯ kubectl get pods
NAME                                  READY   STATUS    RESTARTS   AGE
deployaks-app-depl-869c58dd44-cvw5v   1/1     Running   0          91s
```

podsが無事に作成され、動いていることを確認。

# アプリケーションに接続する

```yaml
kubectl get services
```

```bash
❯ kubectl get services
NAME                         TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)        AGE
deployaks-app-loadbalancer   LoadBalancer   10.0.145.80   20.27.190.204   80:32702/TCP   3m30s
kubernetes                   ClusterIP      10.0.0.1      <none>          443/TCP        3h27m
```

サービスを確認して、ロードバランサーの  `EXTERNAL-IP` を確認する。今回の パブリックIPは `20.27.190.204` なので、[http://20.27.190.204/hello-world](http://20.27.190.204/hello-world)　で Spring Boot のアプリケーションにアクセスできる。

![Screen Shot 2022-10-07 at 15.45.15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/18c5b8a1-88fb-a8b1-a748-5e737b72dd2c.jpeg)
無事、アプリケーションにアクセスすることができた。

![Screen Shot 2022-10-07 at 15.47.56.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/8d4280de-3cd1-a7f2-8c8c-80446285d40b.jpeg)
ちなみに、azure portal の方からも、EXTERNAL-IP　を確認することができる。

# おわり

今回の記事では、Azure Kubernetes Service (AKS) に Spring Boot Application のdocker コンテナーをデプロイした。Azure Portal からクリックだけで、k8s cluster を簡単に立ち上げることができるのはとても便利。

Azure Pipelineをつなげて、AKSへのデプロイを自動化していくのも、面白いをと思う。