Azure 環境で CI/CD パイプライン構築

# はじめに
前回の記事で CI/CD パイプラインの概念について説明しました。
今回の記事では Azure で直接 CI/CD パイプラインを構築します。

# 構築で使う技術

- Azure
    - Azure VM
    - Azure Container Registry
    - Azure Kubernetes Service
- GitHub
- Jenkins
- Docker
- Kubernetes
 
# 構築手順
## 前提条件
- Azure VM が作成されていること
- ACR の role を作成できる権限があること
- root で作業を行う

## Azure VM
### Azure cli インストール
```
apt-get install ca-certificates curl apt-transport-https lsb-release gnupg -y
mkdir -p /etc/apt/keyrings
curl -sLS https://packages.microsoft.com/keys/microsoft.asc |
gpg --dearmor |
sudo tee /etc/apt/keyrings/microsoft.gpg > /dev/null
chmod go+r /etc/apt/keyrings/microsoft.gpg
AZ_REPO=$(lsb_release -cs)
echo "deb [arch=`dpkg --print-architecture` signed-by=/etc/apt/keyrings/microsoft.gpg] 
https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" |
sudo tee /etc/apt/sources.list.d/azure-cli.list
apt-get update
apt-get install azure-cli -y
```
### Jenkins インストール
```
apt-get install openjdk-11-jre -y
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee /usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo 'deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/' | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null
apt-get update && sudo apt-get install jenkins -y
service jenkins restart

# Jenkins が動作しているか確認
service jenkins status
```
### Jenkins のポート開放
```
az vm open-port \
--resource-group {リソースグループ名} \
--name {VM 名}  \
--port 8080 --priority 1010
```
### Docker インストール
```
# Add Docker's official GPG key:
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```
### AKS リソース作成
```
# AKS リソース作成
az aks create -g {リソースグループ名} -n {aks 名} --enable-managed-identity --node-count 1 --enable-addons monitoring --enable-msi-auth-for-monitoring  --generate-ssh-keys

# kubectl をインストール
az aks install-cli
# kubectl を構成
az aks get-credentials -g {リソースグループ名} -n {ak s名} 
# AKSへの接続を確認
kubectl get nodes
# 出力例
NAME                                STATUS   ROLES   AGE    VERSION
aks-nodepool1-32903650-vmss000000   Ready    agent   3m6s   v1.27.7

# Jenkins が kubenetes にアクセスできるように構成
※もし、ディレクトリがなければ作成
sudo usermod -aG docker jenkins;
sudo usermod -aG docker azureuser;
sudo touch /var/lib/jenkins/jenkins.install.InstallUtil.lastExecVersion;
sudo service jenkins restart;
sudo cp -R ~/.kube /var/lib/jenkins/
sudo chmod 755 /var/lib/jenkins/
sudo chown -R jenkins:jenkins /var/lib/jenkins

```

### ACR リソース作成
```
# ACR リソース作成
az acr create --name {acr 名} -g {リソースグループ名} --sku Basic --admin-enabled true
# AKS に ACR をアタッチ
az aks update -n {acr 名} -g {リソースグループ名} --attach-acr $(az acr list -g {リソースグループ名} --query "[0].id" --output tsv)
# サービス プリンシパルを作成
az ad sp create-for-rbac
# 出力例
{
  "appId": "", ※ロールの割り当てるときと Jenkins に資格情報を登録するときに使う
  "displayName": "",
  "name": "",
  "password": "", ※Jenkins に資格情報を登録するときに使う
  "tenant": ""
}
# ACR のリソース ID を取得
ACR_ID=$(az acr show -g {リソースグループ名} -n {acr 名} --query "id" --output tsv)
# ロールを割り当てる
az role assignment create --assignee <appID> --role Contributor --scope $ACR_ID
```
## GitHub
1. GitHub ページに移動
1. Settings ページに移動
1. Developer settings ページに移動
1. Tokens (classic) を選択し、repo の全権限を持つ Personal access Token を作成
1. 作成して出た値をメモ

## Jenkins
### Jenkins の初期設定
1. 自動作成された Jenkins パスワードを確認
   ```
   cat /var/lib/jenkins/secrets/initialAdminPassword
   ```
2. ブラウザーで http://<ip_address>:8080 を開く
3. 確認したパスワードを入力し、Continue を選択
4. Select plugins to install を選択
5. フィルターボックスに githubを入力する。GitHub のプラグインを選択し、 Install を選択
5. アカウントの設定を入力し、Save and Continue を選択
5. Save and Finish を選択


### Global properties 設定
1. Jenkins の管理 > Systemを選択
1. Global properties の Environment variables で以下の環境変数を追加
    ```
    Name: ACR_LOGINSERVER
    Value: ACR の URL
    ```
3. Jenkins の管理 > System > Global credentials (unrestricted) > Add Credentialsを選択
3. 以下の通り作成
    ```
    Kind: Username with password
    Scope: Global
    Username: ACR の appId
    Password: サービスプリンシパルのパスワード ※ ACR リソース作成のパスワードを確認
    ID: 資格情報の識別子
    Description: 説明　※省略可能
    ```
5. GitHub の Tokenも作成
    ```
    Kind: Username with password
    Scope: Global
    Username: GitHub のユーザ名
    Password: Token を作成するさいに出た値
    ID: Token の識別子
    Description: 説明　※省略可能
    ```
### GitHub Webhook 作成
1. CI/CD に使う GitHub repository ページに移動
1. Setting を選択し、Webhooks を選択
1. 以下の通り Webhook を追加
    ```
    Payload URL: http://<ip_address:8080>/github-webhook/ ※Jenkins の ip_address
    ```
### プロジェクト作成
1. Job 名を入力し、Freestyle プロジェクトで作成
1. General の GitHub project を選択。CI/CD に使う GitHub repository のURLを入力
1. Source code management で Git を選択。CI/CD に使う GitHub repository のURLを入力。 Credentialsは GitHub の Tokenを選択
1. Build Triggers で GitHub hook trigger for GITscm polling を選択
1. Build Environment で、Use secret texts or files を選択
1. Bindings で、add > Username and password (separated) を選択し、以下のように入力
    ```
    Username Variable: ACR_ID
    Password Variable: ACR_PASSWORD
    Credentials: Specific credentials ※ACR の Credentials を使う
    ```
1. ビルドステップを2つ追加し、以下のコマンドを実行
    ```
    # 1つ目
    WEB_IMAGE_NAME="${ACR_LOGINSERVER}/ping:${BUILD_NUMBER}"
    docker build -t $WEB_IMAGE_NAME ./ping
    docker login ${ACR_SERVER} -u ${ACR_ID} -p ${ACR_PASSWORD}
    docker push $WEB_IMAGE_NAME
    
    # 2つ目
    WEB_IMAGE_NAME="${ACR_LOGINSERVER}/azure-vote-front:kube${BUILD_NUMBER}"
    kubectl set image deployment/azure-vote-front azure-vote-front=$WEB_IMAGE_NAME
    ```
1. Save する

## CI/CD パイプラインテスト
コードを GitHub に Push することで、CI/CD パイプラインの動作確認ができます。
Push 後、Jenkins のビルドヒストリーを確認して結果を確認します。
ビルドが成功し、実際に GitHub のコード通り動作することも確認します。
問題なく動作したら、作成した Azure リソースを削除します。
```
az group delete -g {リソースグループ名}
```