Drone CIを使ったAzure Kubernetes ServiceへのCI/CD環境実装

# Droneって？
コンテナと親和性の高いCIツールです。
[drone.io](https://drone.io/)

# Drone動作環境
オンプレ(プライベートクラウド)のDocker上で稼働させています。

# やりたいこと
1. リポジトリがmasterにマージされたらAKS（ステージング環境）に自動デプロイする
2. リポジトリでgit tagが打たれたら（リリース用タグ）、AKS（本番環境）に自動デプロイする

# 全体像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/81242dcc-54e9-3616-1d16-3d17bf1e8573.png)

# Azure AD サービスプリンシパルを作成する

* Azure CLIでサービスプリンシパルを作成します。（ポータルで作ってもよいです）

```
$ az login
$ az ad sp create-for-rbac -n "[Your SP Name]" --skip-assignment --years 10
```

* このとき表示される、[appId],[password],[tenant]は後で使用するので控えておいてください。

* ポータルでACRのIAMを選択し、[+Add]を選択します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/df9589db-b475-46a7-7e09-a29cc20e650a.png)

* [Add role assingment]を選択します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/1835b22b-8273-a1a5-f334-f072e00340ed.png)

* 作成したSPに対して、[AcrPull]の権限を付与します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/02e8a8e1-0a6e-6e61-07a8-2a59ab534d9d.png)

* 同じように、[AcrPush]の権限を付与します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/aab92004-af03-94df-c0f2-55fb432a83f4.png)

* 次に、MC_[Your AKS Name]_[Your AKS Name][Region Name]というAKSのVMやDiskが作成されたリソースグループがあるので、このリソースグループのIAM設定で、作成したSPに対して、[共同作成者]権限を付与します。

# Drone Secret作成
* Droneのリポジトリから[SETTINGS]を選択します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/e7e6903e-54b0-088b-ced2-a6a7833bd22c.png)

* Secret名とSecret Valueを入れて、[ADD A SECRET]を選択します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/9adb67fb-6c43-b577-0247-00871f2c8250.png)

* 以下3つのSECRETを作成します。
    * AZURE_APPLICATION_ID
        * 事前にメモした[appId]の値
    * AZURE_SECRET
        * 事前にメモした[password]の値
    * AZURE_TENANT_ID
        * 事前にメモした[tenant]の値

# Azure CLIコンテナイメージ作成
* 以下の記事を参考に、Drone CIが動作しているサーバのDockerリポジトリにAzure CLIコンテナイメージを作ります。  
[Azure CLIコマンドをdocker runと同時に実行して、実行が終わったらコンテナを破棄する](https://qiita.com/shingo_kawahara/items/685313f4dbc0b8de60e2)

* イメージ名は[azurecli]、タグは[latest]で作成します。これは後ほどDroneでAKSにデプロイするときに使います。

# .drone.yaml作成
* .drone.yamlをGitHubリポジトリに作成します。アプリケーションのビルドやテストのステップは割愛して、AKSデプロイ部分だけ記載しています。  

```
kind: pipeline
name: staging

steps:
- name: deploy
  image: azurecli:latest
  pull: if-not-exists
  environment:
    AZURE_APPLICATION_ID:
      from_secret: AZURE_APPLICATION_ID
    AZURE_SECRET:
      from_secret: AZURE_SECRET
    AZURE_TENANT_ID:
      from_secret: AZURE_TENANT_ID
    IMAGE_TAG: ${DRONE_COMMIT_SHA:0:8}
  commands:
    - echo $IMAGE_TAG
    - /usr/bin/az login --service-principal -u $AZURE_APPLICATION_ID -p $AZURE_SECRET --tenant $AZURE_TENANT_ID
    - /usr/bin/az aks install-cli
    - /usr/bin/az aks get-credentials -g [Staging ResourceGroup Name] -n [Staging AKS Name]
    - kubectl apply -f [k8s Deployment Manifest] -n [k8s NameSpace]
    - kubectl get po -n [k8s NameSpace]
    - kubectl set image deployment.apps/[Pod Name] [Pod Name]=[ACR Server URL]/[Repository Name]:$IMAGE_TAG -n [k8s NameSpace]
  when:
    branch:
      - master
    event:
      - push

trigger:
  event:
    exclude:
      - tag
---
kind: pipeline
name: production

steps:
- name: deploy
  image: azurecli:latest
  pull: if-not-exists
  environment:
    AZURE_APPLICATION_ID:
      from_secret: AZURE_APPLICATION_ID
    AZURE_SECRET:
      from_secret: AZURE_SECRET
    AZURE_TENANT_ID:
      from_secret: AZURE_TENANT_ID
    IMAGE_TAG: ${DRONE_TAG}
  commands:
    - echo $IMAGE_TAG
    - /usr/bin/az login --service-principal -u $AZURE_APPLICATION_ID -p $AZURE_SECRET --tenant $AZURE_TENANT_ID
    - /usr/bin/az aks install-cli
    - /usr/bin/az aks get-credentials -g [Production ResourceGroup Name] -n [Production AKS Name]
    - kubectl apply -f [k8s Deployment Manifest] -n [k8s NameSpace]
    - kubectl get po -n [k8s NameSpace]
    - kubectl set image deployment.apps/[Pod Name] [Pod Name]=[ACR Server URL]/[Repository Name]:$IMAGE_TAG -n [k8s NameSpace]
  when:
    branch:
      - master

trigger:
  event:
    - tag
```

* kubectl set imageでイメージタグを変更している理由は、deploymentのマニフェストファイルでイメージタグをlatest固定にしたかったからです。本当はマニフェストファイルのタグを可変にできたらよかったのですが、Droneからだとどうにもできなかったので、一度latestで起動して、対象のタグに変更するという強引な手法を取っています。

* イメージのタグは、ステージングだとGitのコミットIDが入って、本番だとリリースタグ（例えばv1.0.0など）が入ります。

# リストア方法
* リリース後に不具合が見つかって、前回リリースバージョンに戻したい場合、以下のような前回リリース時のタグが打たれたDroneジョブを見つけて選択します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/14ca944a-4f09-9638-8155-7f88e092ad81.png)

* [RESTART]ボタンをクリックするだけで前回バージョンに戻ります。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180335/2dfcc3e1-9033-fb76-9a6d-caec74227c9c.png)



# まとめ
CI/CD最高です。この仕組みを作るまでは手動でデプロイしていましたが、生産性段違いです。
Gitリポジトリをオンプレに持っている都合上、オンプレのCIツールDroneを選択しましたが、Azure DevOpsでも同じことができると思います。（おそらくとても楽に）
今回かなり苦労しましたが、CI/CD環境は無理してでもリリース前に作る価値があると思いました。