Azure Kubernates ServiceへのdockerコンテイナーのデプロイメントをAzure DevOps Pipelineで自動化してみる。


# はじめに

AKSへのデプロイメントはそれぞれのリソース(AKS, ACR)を作ってしまえば、 `kubectl` コマンドを使い簡単に実行することができる。実際の開発において、わざわざコマンドを叩いてデプロイメントを実行するとなると、迅速な機能のリリースができなくなる。したがって、CI/CD pipelineを実装して、AKSへのデプロイを自動化するのが良い。今回の記事では、Azure Pipeline と Github を用いて、docker コンテイナーのAKSへのデプロイメントを自動化してみる。

# Azure Kubernetes Service と Azure Container Registry のリソースの作成する。

それぞれのリソースの作り方は、以下の記事で説明している。

[Azure Kubernetes Service にコンテイナー化したSpring Bootのアプリをデプロイしてみる - Qiita](https://qiita.com/ryuichi-f/items/5f6c86abcb7a16e0718d#%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B0%E3%83%AB%E3%83%BC%E3%83%97%E3%82%92%E4%BD%9C%E3%82%8B)

# Azure DevOpsの作成

## Projectの作成

![1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/469ac591-616e-4967-22f7-a0e5fe3f2e0a.jpeg)

## Service connection の追加

Azure DevOps からAzureリソースにアクセスするために、それぞれのリソースごとにservice connection の作成が必要。

![2.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/863c0515-e416-8970-2865-bf3a0ba32974.jpeg)
![3.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/c90b30be-a400-7064-ac73-4b5d093fb527.jpeg)
![4.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/2cd0f027-bf10-b2ff-6807-604c05f9f76e.jpeg)



## Docker Container Registry の service connection の追加

![5.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/6337422e-516f-f2ff-75f0-2a824294dbd9.jpeg)
![6.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/b88c6ef2-df8e-1f58-0a3e-edbfa3a2fd09.jpeg)


Registry typeでAzure Container Registry を選択すると、別でポップアップウインドウが表示される。ブラウザーでポップアップウインドウをブロックしている場合は出てこないので、設定を変える必要がある。 またブロックしていないにもかかわらず、ポップアップが出てこない場合はブラウザーの更新が解決する場合がある。

![7.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/b1cb8f25-24b4-13d1-e386-c01a11133d68.jpeg)

ポップアップから、Azureに無事ログインができたら、SubscriptionとAzure Container registryそれぞれのドロップダウンメニューにAzure側のSubscription及び、ACRのリソースが表示される。それぞれ任意のアイテムを選択して、service connection nameを決め、登録する。

## Azure Kubernates Service の service connection の追加

![8.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/f24abe56-9d41-0d9b-8483-1fed3abf31c9.jpeg)
![9.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/1ede311c-5eb0-fab7-1246-45eb9ed84ed1.jpeg)


## Azure Pipeline の作成

![10.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/17103189-40a2-299e-aefe-91d2d5ed20ae.jpeg)
![11.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/2594c1e0-1bb9-4d6d-f712-3593f957c789.jpeg)
![12.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/2bc6214f-e412-b232-9e95-913fb669d739.jpeg)


今回はGithubと連携して、特定のブランチに新たなコードがプッシュされたらAzure Pipelineを実行するように設定していく。

![13.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/1b0be428-4802-3ac8-3644-d27faa8f73a0.jpeg)

GithubとAzure Pipelineの連携がされていない場合は自動的にGithubのOAuthページにリダイレクトされるので、要求されているアクセス権を許可する。

![14.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/cd40d639-6733-39ca-6864-e727546cdd01.jpeg)

Githubとの連携が成功したら、任意のレポジトリを選択する。今回のデモのアプリケーションは、前回の[Azure Kubernetes Service にコンテイナー化したSpring Bootのアプリをデプロイしてみる](https://qiita.com/ryuichi-f/items/5f6c86abcb7a16e0718d#spring-boot-rest-api-%E3%81%AE%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E4%BD%9C%E3%82%8B)で作ったSpring Bootのアプリケーションと同じもの。実際のGithub repositoryはこちら → [https://github.com/ryuichi-f/deploy-aks](https://github.com/ryuichi-f/deploy-aks)

k8s/****deployaks-app-depl.yml に一箇所だけ変更点がある。spec.template.spec.containers.image にazure container registry のURLとimage ネームを追加した。****

```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: nicetestacr.azurecr.io/deploy-aks # <- 変更

---
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
```

![15.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/da0a5788-0467-6d16-b8c8-097c0f8259fb.jpeg)

pipeline YAMLのテンプレートの選択なので、一番シンプルな Starter pipelineを選ぶ。

![16.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/95001879-ab80-4206-c8e5-0a6afa18a6e4.jpeg)

pipeline YAML に以下のコードを貼り付ける。そして、”Save and run” をクリック。

```yaml
trigger:
- main

variables:
  ACRServiceConnection: 'niceTestACRServiceConnection'
  AKSServiceConnection: 'niceTestAKCServiceConnection'
  dockerRegistryURL: 'nicetestacr.azurecr.io'
  imageRepo: deploy-aks
  tag: '$(Build.BuildId)'

stages:
  - stage: build_image
    displayName: build an image
    jobs:

      - job: build_container_image_job
        pool:
          vmImage: ubuntu-latest
        steps:

        - task: Docker@2
          inputs:
            containerRegistry: $(ACRServiceConnection)
            repository: '$(imageRepo)'
            command: buildAndPush
            Dockerfile: '$(Build.SourcesDirectory)/Dockerfile'
            buildContext: '$(Build.SourcesDirectory)'
            tags: |
              $(tag)

      - job: publish_k8s_manifest_files_job
        pool:
          vmImage: ubuntu-latest
        steps:
          - task: PublishPipelineArtifact@1
            inputs:
              targetPath: '$(Pipeline.Workspace)/s/k8s'
              artifact: 'manifests'
              publishLocation: 'pipeline'

  - stage: deploy_container
    dependsOn: build_image
    displayName: deploy a container
    jobs:

      - job: deployment_job
        pool:
          vmImage: ubuntu-latest
        variables:
          aksSecretName: testAKSSecretName
        steps:

        - task: DownloadPipelineArtifact@2
          inputs:
            buildType: 'current'
            artifactName: 'manifests'
            targetPath: '$(Pipeline.Workspace)/manifests'

        - task: KubernetesManifest@0
          inputs:
            action: 'deploy'
            kubernetesServiceConnection: $(AKSServiceConnection)
            namespace: 'default'
            containers: |
              $(dockerRegistryURL)/$(imageRepo):$(tag)
            manifests: |
              $(Pipeline.Workspace)/manifests/deployaks-app-depl.yml
```

### Pipeline YAML のそれぞれのセクションの説明

```yaml
trigger:
- main
```

`trigger`  で指定したブランチにコードが push された際に、Azure pipelineを実行がされる。

```yaml
variables:
  ACRServiceConnection: 'niceTestACRServiceConnection'
  AKSServiceConnection: 'niceTestAKCServiceConnection'
  dockerRegistryURL: 'nicetestacr.azurecr.io'
  imageRepo: deploy-aks
  tag: '$(Build.BuildId)'
```

`variables` でpipeline上で使用したい変数を定義できる。定義する場所の階層によって、変数のスコープが変わる。今回はもっとも上の階層で定義したので、すべての階層から変数にアクセスできる。

```yaml
stages:
    - stage: build_image
    # 以下省略...
    - stage: deploy_container
    # 以下省略...
```

azure pipelineでは、それぞれのプロセスを段階に分けて定義していける。大きく分けて、 `stage`, `job` `step`　の３種類ある。その中でも、 `stage` は最も上の階層の区分にあたる。今回は、build_image stageとdeploy_container stageの２つを定義している。

```yaml
 jobs:
      - job: build_container_image_job
      # 以下省略...
      - job: publish_k8s_manifest_files_job
      # 以下省略...
```

`job` は  `stage` の区分の中をさらに分けた区分にあたる。それぞれの `job` は別々の `agent` にパラレルに実行される。

```yaml
steps:
    - task: DownloadPipelineArtifact@2
    # 以下省略...
    - task: KubernetesManifest@0
    # 以下省略...
```

`step` は `job` の区分の中をさらに分けた区分にあたる。それぞれの`step` は上から順番に実行される。

```yaml
- task: Docker@2
          inputs:
            containerRegistry: $(ACRServiceConnection)
            repository: '$(imageRepo)'
            command: buildAndPush
            Dockerfile: '$(Build.SourcesDirectory)/Dockerfile'
            buildContext: '$(Build.SourcesDirectory)'
            tags: |
              $(tag)
```

`Docker@2` は container registry にimageをビルドしてプッシュを行うためのtask.

- containerRegistry: Azure container registry のservice connectionを指定
- repository: docker dockrtイメージのrepositoryの名前を指定
- command: Docker@2 のコマンドを指定。指定できるコマンドは以下の通り:
    - 'buildAndPush' | 'build' | 'push' | 'login' | 'logout' | 'start' | 'stop’
- Dockefile: Docker ファイルの場所の指定。
- buildContext: docker イメージのビルドコンテキストを指定。
- tags: dockerイメージのタグを指定。リストとして複数指定できる。

```yaml
 - task: PublishPipelineArtifact@1
            inputs:
              targetPath: '$(Pipeline.Workspace)/s/k8s'
              artifact: 'manifests'
              publishLocation: 'pipeline'
```

`PublishPipelineArtifact@1` は指定したartifact(ファイルなど)を、特定のフォルダーにアップロードするためのタスク。アップロードされたartifactは  `DownloadPipelineArtifact@2` タスクで別の`stage` にダウンロードして、使用することができる。

- inputs:
    - targetPath: artifactの場所を指定。
    - artifact: artifactを参照するための任意の名前を指定。
    - publishLocation: アップロード先の任意のフォルダーの名前を指定。

```yaml
- task: DownloadPipelineArtifact@2
          inputs:
            buildType: 'current'
            artifactName: 'manifests'
            targetPath: '$(Pipeline.Workspace)/manifests'
```

`PublishPipelineArtifact@1`　でアップロードされたartifactをダウンロードするためのタスク。

- inputs:
    - buildType: artifactのソースのタイプを指定する。 指定できるタイプは以下の通り:
        - 'current' | 'specific'
    - artifactName: `PublishPipelineArtifact@1` 指定したartifactの名前を指定。
    - targetPath: アップロードされたartifactの場所を指定。

```yaml
- task: KubernetesManifest@0
          inputs:
            action: 'deploy'
            kubernetesServiceConnection: $(AKSServiceConnection)
            namespace: 'default'
            containers: |
              $(dockerRegistryURL)/$(imageRepo):$(tag)
            manifests: |
              $(Pipeline.Workspace)/manifests/deployaks-app-depl.yml
```
k8s manifestファイルをk8s clusterにデプロイメントするためのタスク。
- inputs:
    - action: 合計8個の指定できるアクションがある。それぞれ指定するアクションによって、それ以降の指定できるinputsの種類が変化する。今回は’deploy’アクションを指定する。指定できるアクションは以下の通り:
        - 'deploy' | 'promote' | 'reject' | 'createSecret' | 'bake | scale' | 'patch’ | ‘delete’
    - namespace: k8sのnamespaceを指定。
    - containers: k8s manifestファイルの  ****`spec.template.spec.containers.image`** セクションのdocker イメージURLを置換するためのURLを指定。
    - manifest: k8sのmanifestファイルの場所を指定。

![17.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/a0a86e3b-b524-19fb-327b-60e6c526569c.jpeg)
![18.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/95968ec3-7df4-c326-3387-96d18e36a063.jpeg)

Azure pipelineがGithub repositoryの新しいコミットを検知して、自動的にpipelineが実行される。

![19.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/1e11490a-795d-759d-df5f-2e547b7f306a.jpeg)

すべてのステージ及び、ジョブが緑色のチェックマークに変化したらpipelineの実行が無事成功したことになる。次回以降、mainブランチに新しいコードがプッシュされるたびに、pipelineが実行され、アプリケーションがデプロイメントされる。

![20.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/18450345-e643-897e-ce29-f0d747d2e5c0.jpeg)

実際にデプロイされたアプリケーションへのIPアドレスは、AKSリソースの”Services and ingresses”で確認できる。

![21.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/6ac608a7-d55c-aff4-159e-b55f6f7bc1d9.jpeg)

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

![23.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2822052/d7ed7969-4354-3809-7f33-881916c87fda.jpeg)

# おわり

Docker コンテイナーのデプロイメントをAzure Pipelineを用いて自動化することに成功した。一度Pipeline YAML でCI/CDで設定すると、それ以降のデプロイメントがシームレスになるので、迅速な機能のリリースを実現することができる。