# Hands-on : Azure Kubernetes Service and CI/CD with Azure DevOps
###### tags: `台新` `Workshop` `Azure Kubernetes Service` `Container Registry` `Azure DevOps`
[ToC]
## 1/ 使用 Azure Kubernetes Service 部署 Kubernetes
### 使用 Azure Cloud Shell
- 開啟 [Azure Portal](https://ms.portal.azure.com/#home) 上的 Azure Cloud Shell

- 首次使用會建立 Azure Blob Storage,選擇要使用的訂閱,點選建立即可
- 確定切換置 bash

:::info
:bulb: **Hint :** 使用 Azure Cloud Shell 時需使用 `ctrl` + `c` 複製 及 `shift` + `ctrl` + `v` 貼上
:::
### 建立新的資源群組
- 在整個部署指令碼中會重複使用某些值。舉例來說,須選擇要建立資源群組的區域,例如 `East US`,可自行修改參數值。
```azurecli-interactive
REGION_NAME=southeastasia
RESOURCE_GROUP=cicdworkshop
SUBNET_NAME=cicd-subnet
VNET_NAME=cicd-vnet
```
- 地區也可使用 `REGION_NAME=eastus`
- 可透過 `echo $REGION_NAME` 顯示參數是否確定被輸入正確的值
- 使用 `aksworkshop` 作為名稱來建立新的資源群組。 於此資源群組中部署練習中建立的所有資源
```azurecli-interactive
az group create \
--name $RESOURCE_GROUP \
--location $REGION_NAME
```
### 建立 AKS Cluster
<!-- - 註冊 Monitoring
```
az provider register --namespace Microsoft.OperationsManagement
az provider register --namespace Microsoft.OperationalInsights
az provider register --namespace Microsoft.Insights
``` -->
- 設定 AKS cluster 名稱,此名稱在使用的訂閱中需為唯一
```azurecli-interactive
AKS_CLUSTER_NAME=aks-cicd-$RANDOM
```
- 取得隨機產生的 AKS Cluster 名稱,並將此值記下
```azurecli-interactive
echo $AKS_CLUSTER_NAME
```
- 執行以下命令來建立最新版本的 AKS Cluster,此需花費幾分鐘的時間才能完成
```
az aks create \
--resource-group $RESOURCE_GROUP \
--name $AKS_CLUSTER_NAME \
--location $REGION_NAME \
--enable-managed-identity \
--node-count 1 \
--node-vm-size Standard_D2s_v3 \
--generate-ssh-keys
```
<!-- - 執行以下命令來建立最新版本的 AKS Cluster,此需花費幾分鐘的時間才能完成
```
az aks create \
--resource-group $RESOURCE_GROUP \
--name $AKS_CLUSTER_NAME \
--location $REGION_NAME \
--enable-managed-identity \
--node-count 1 \
--node-vm-size Standard_D2s_v3 \
--enable-addons monitoring \
--enable-msi-auth-for-monitoring \
--generate-ssh-keys
``` -->
### 使用 ```kubectl``` 測試叢集連線能力
kubectl 是用來與 Cluster 互動並可在 Cloud Shell 中使用
- 使用 `az aks get-credentials` 命令來設定 kubectl 的執行個體
```azurecli-interactive
az aks get-credentials \
--resource-group $RESOURCE_GROUP \
--name $AKS_CLUSTER_NAME
```
- 列出所有節點
```azurecli-interactive
kubectl get nodes
```
- 建立 `voting` 的 Namespace
```azurecli-interactive
kubectl create namespace voting
```
## 2/ 建立私人容器登錄 Azure Container Registry
- 設定 ACR 的名稱,在 Azure 中須為唯一,這裡使用 random 的方式為 ACR 命名
```azurecli-interactive
ACR_NAME=acrcicd$RANDOM
```
- 輸出 ACR_NAME 並記錄下來
```azurecli-interactive
echo $ACR_NAME
```
- 透過 `az acr create` 建立與 AKS 資源位於相同資源群組及區域中的 ACR
```azurecli-interactive
az acr create \
--resource-group $RESOURCE_GROUP \
--location $REGION_NAME \
--name $ACR_NAME \
--sku Standard
```
## 3/ 使用 Azure Container Registry 工作建立容器映像
Fruit Smoothies 評等應用程式使用兩個容器映像,一個用於前端網站,另一個用於 RESTful API Web 服務,接下來會使用 Azure Container Registry 以透過標準 Dockerfile 提供建置指示來建置這些容器映像
### 建立 Ratings API 映像
- 先建立一個資料夾提供給今天練習的範例程式碼使用
```azurecli-interactive
mkdir aks-cicd-lab
```
- 索引到 `aks-cicd-lab` 底下
```azurecli-interactive
cd aks-cicd-lab
```
- 下載範例 API 程式碼
```azurecli-interactive
git clone https://github.com/Azure-Samples/azure-voting-app-redis.git
```
- 索引至範例程式碼目錄下
```azurecli-interactive
cd azure-voting-app-redis/azure-vote
```
- 執行 `az acr build`,此指令會使用 Dockerfile 來建置容器映像,完成後將其推送至 ACR 中存放
```azurecli-interactive
az acr build \
--resource-group $RESOURCE_GROUP \
--registry $ACR_NAME \
--image azure-voting-app-redis:v1 .
```
:::info
:bulb: **注意:** 不要忘記上述命令結尾的句點 `.`, 其代表包含 Dockerfile 的來源目錄
:::
### 驗證映像
- 執行以下命令確認映像已建立於 ACR 中
```azurecli-interactive
az acr repository list \
--name $ACR_NAME \
--output table
```
- 輸出
```azurecli-interactive
Result
----------------------
azure-voting-app-redis
```
### 設定 AKS Cluster 與 ACR 間的驗證及身分授權
- 透過自動產生驗證方式 (需為 subscription owner)
需要在容器登錄與 Kubernetes 叢集之間設定驗證,以允許服務之間的通訊
- 執行 `az aks update` 命令來自動設定兩個資源間的必要服務主體驗證,此步驟會花費一些時間做 AKS 設定的更新
```azurecli-interactive
az aks update \
--name $AKS_CLUSTER_NAME \
--resource-group $RESOURCE_GROUP \
--attach-acr $ACR_NAME
```
- **(OPTION)** 自行設定連線 Container Registry secret 方式
- 將 ACR 啟用 admin account
```azurecli-interactive
az acr update -n $ACR_NAME --admin-enabled true
```
- 取得 ACR 連線資訊
```azurecli-interactive
ACR_UNAME=$(az acr credential show -n $ACR_NAME --query="username" -o tsv)
ACR_PASSWD=$(az acr credential show -n $ACR_NAME --query="passwords[0].value" -o tsv)
```
- 將 ACR 連線資訊設定於 K8S
```azurecli-interactive
kubectl create secret docker-registry acr-secret \
--docker-server=$ACR_NAME \
--docker-username=$ACR_UNAME \
--docker-password=$ACR_PASSWD
```
- Assign K8S secret to default service account
```azurecli-interactive
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "acr-secret"}]}'
```
## 4/ 手動部屬服務
- 索引到程式碼目錄下
```azurecli-interactive
cd ~/aks-cicd-lab/azure-voting-app-redis/
```
- 新增後端部屬設定 `azure-vote-back.yaml`,並部屬
- 新增
```azurecli-interactive
code azure-vote-back.yaml
```
- 將以下貼入
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-vote-back
spec:
replicas: 1
selector:
matchLabels:
app: azure-vote-back
template:
metadata:
labels:
app: azure-vote-back
spec:
nodeSelector:
"beta.kubernetes.io/os": linux
containers:
- name: azure-vote-back
image: mcr.microsoft.com/oss/bitnami/redis:6.0.8
env:
- name: ALLOW_EMPTY_PASSWORD
value: "yes"
ports:
- containerPort: 6379
name: redis
---
apiVersion: v1
kind: Service
metadata:
name: azure-vote-back
spec:
ports:
- port: 6379
selector:
app: azure-vote-back
```
- 部屬至 AKS
```azurecli-interactive
kubectl apply -f azure-vote-back.yaml -n voting
```
<!-- - 透過 `azure-vote-all-in-one-redis.yaml` 一次性的部屬所有服務
```azurecli-interactive
kubectl apply -f azure-vote-all-in-one-redis.yaml -n voting
``` -->
- 建立並部屬前端 deployment 檔案
- 新增 `azure-vote-front-deployment.yaml`
```azurecli-interactive
code azure-vote-front-deployment.yaml
```
- 將以下檔案貼上,完成後儲存
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: azure-vote-front
spec:
replicas: 1
selector:
matchLabels:
app: azure-vote-front
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 5
template:
metadata:
labels:
app: azure-vote-front
spec:
nodeSelector:
"beta.kubernetes.io/os": linux
containers:
- name: azure-vote-front
image: <acrname>.azurecr.io/azure-voting-app-redis:v1
ports:
- containerPort: 80
resources:
requests:
cpu: 250m
limits:
cpu: 500m
env:
- name: REDIS
value: "azure-vote-back"
```
- 將 `<acrname>` 取代為先前建立的 ACR 名稱

- 部屬至 AKS
```azurecli-interactive
kubectl apply -f azure-vote-front-deployment.yaml -n voting
```
- 新增前端 service 設定並部屬
- 新增 `azure-vote-front-service.yaml`
```azurecli-interactive
code azure-vote-front-service.yaml
```
- 將以下檔案貼上,完成後儲存
```yaml
apiVersion: v1
kind: Service
metadata:
name: azure-vote-front
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: azure-vote-front
```
- 部屬至 AKS
```azurecli-interactive
kubectl apply -f azure-vote-front-service.yaml -n voting
```
- 取得 External IP,並驗證網頁正常運行
```
kubectl get service -n voting -w
```
## 5/ AKS with Azure DevOps CI/CD
### 版本控制
#### 匯入範例程式碼
- 至 [Azure DevOps](https://dev.azure.com/) 連結至先前建立的 Project,或新增一個
- 點選左方欄位 Repo 底下的 **[Files]**,選擇上方的repo name,點選 **[+New repository]**

- 將 **Repository Name** 填寫成 `azure-voting-app-redis`,並將 **[Add a README]** 的勾取消,再點選 **[Create]**

- 複製該 repo 連結

<!-- - 點選畫面中 **[Import]** 的按鈕,並複製以下連結貼到 **[Clone URL]** 中,完成後點選 **[Import]**
```
https://github.com/MicrosoftDocs/mslearn-aks-workshop-ratings-api.git
```

- 顯示正在進行匯入
 -->
- 回到 Azure Cloud Shell,索引到程式碼目錄
```
cd ~/aks-cicd-lab/azure-voting-app-redis/
```
- 環境中第一次設置 git (# 置換成編號)
```
git config --global user.email "workshop-user-tw2023-#@huier23outlook.onmicrosoft.com"
git config --global user.name "workshop-user-tw2023-#"
```
- 將新的檔案加入版控中
```
git add .
git commit -m "add k8s config"
```
- 將遠端儲存庫設定到本機 git 中
```
git remote add devops https://<organization name>@dev.azure.com/<organization name>/<project name>/_git/azure-voting-app-redis
```
- 輸入以下指令可確認已成功設定
```
git remote -v
```

- 開始推送至 Azure Repo
```
git push devops master
```
- 視窗會顯示詢問密碼,選擇下方兩種方式之一取得 PAT
```
Password for 'https://huier-teamservice@dev.azure.com':
```
1. 使用 PAT
- 回到 Azure DevOps Service,點選右上角的圖示,並選擇 Personal Access Token

- 點選 **[+New Token]**,完成資訊的填寫

- 將 token 複製起來,並記在記事本中(一旦關閉則會消失)

2. 使用 Generate Git Credentials
- 點選 

- 將 token 複製起來,並記在記事本中
- 回到 Azure Cloud Shell 並將 token 貼上 (貼上後不會顯示任何東西)

### 建立 Build Pipeline
- 選擇左方選單列中 Pipelines 下的 **[Pipelines]**,點選 **[Create Pipeline]**

- 選擇畫面下方說明文字中的 **[Use the classic editor]**

- 選擇 `azure-voting-app-redis` 的 repository

- 在右上方的搜尋欄位尋找 `docker`,找到 **Docker Container**,點選 **[Apply]**

- 選到 Build an image,完成內容
- **Azure Subscription** : **<建立 ACR 資源時使用的訂閱>**
- **Azure Container Registry** : 選擇當初建立的 ACR 名稱
- **Docker File** : `**/azure-vote/Dockerfile`
- **Image Name** : `$(Build.Repository.Name):$(Build.BuildId)`

<!-- - **Image Name** : `$(REPO)-api:$(Build.BuildNumber)` -->
<!--  -->
- 選到 Push an image,完成內容
- **Azure Subscription** : **<建立 ACR 資源時使用的訂閱>**
- **Azure Container Registry** : 選擇當初建立的 ACR 名稱
- **Docker File** : `**/azure-vote/Dockerfile`
- **Image Name** : `$(Build.Repository.Name):$(Build.BuildId)`

<!--  -->
- 點選 agent job 的 + 添加一個 task,在搜尋欄搜尋 `copy`,選擇 **Copy files**

- 設定 Copy files
- **Source Folder** : `$(Build.Repository.LocalPath)`
- **Contents** : `**.yaml`
- **Target Folder** : `$(Build.ArtifactStagingDirectory)`

- 再添加一個 task,搜尋 `publish`,選擇 **Publish build artifacts**

- 設定 Publish build artifacts
- **Path to publish** : `$(Build.ArtifactStagingDirectory)`
- **Artifact name** : `drop-$(Build.BuildNumber)`

<!-- - 設定 Variable,選擇 Variables > Variable groups > Manage variable groups

- 點選 ,設定變數,完成後點選 
- **Variable group name**: `aks-lab` (或任意命名)
- ***Variables***
| **Name** | **Value** |
| -------- | -------- |
| `ACR` | `acrxxxx.azurecr.io` |
| `REPO` | `ratings` |

- 回到 Pipeline > Variables > Variable groups >  > 剛剛建立的 variable group > Link
 -->
- 選擇 Pipeline > Agent Specification > ubuntu-latest

- 選擇上方列表的 **[Triggers]** tab,將畫面右方的 **[Enable continuous integration]** 打勾

- 在 Path filters 下選擇 **+Add**,選擇 **Exclude**,並在後方欄位填上 `**.yaml`

- 完成後修改 pipeline 名稱為 `azure-voting-CI`,點選上方的 **[Save & queue]** ,選擇 **[Save & queue]** 後在跳出的視窗點選 **[Save and run]**


- 回到 Azure Portal 上確認有新的 Repository 及 Image

<!-- #### 建立 **mslearn-aks-workshop-ratings-web** 的 Build Pipeline
- 點選右上角  再新增一條提供 web 建置,同樣的步驟再重複一遍完成
- 選擇畫面下方說明文字中的 **[Use the classic editor]**
- 選擇 `mslearn-aks-workshop-ratings-web` 的 repository

- 在右上方的搜尋欄位尋找 `docker`,找到 **Docker Container**,點選 **[Apply]**
- 選到 Build an image,完成內容
- **Azure Subscription** : **<建立 ACR 資源時使用的訂閱>**
- **Azure Container Registry** : `$(ACR)`
- **Image Name** : `$(REPO)-web:$(Build.BuildNumber)`

- 選到 Push an image,完成內容
- **Azure Subscription** : **<建立 ACR 資源時使用的訂閱>**
- **Azure Container Registry** : `$(ACR)`
- **Image Name** : `$(REPO)-web:$(Build.BuildNumber)`

- 點選 agent job 的 + 添加一個 task,在搜尋欄搜尋 `copy`,選擇 **Copy files**
- 設定 Copy files
- **Source Folder** : `$(Build.Repository.LocalPath)`
- **Contents** : `**.yaml`
- **Target Folder** : `$(Build.ArtifactStagingDirectory)`

- 再添加一個 task,搜尋 `publish`,選擇 **Publish build artifacts**
- 設定 Publish build artifacts
- **Path to publish** : `$(Build.ArtifactStagingDirectory)`
- **Artifact name** : `drop-$(Build.BuildNumber)`

- 設定 Variable,選擇 Variables > Variable groups >  > 剛剛建立的 variable group > Link

- 選擇 Task > Pipeline > Agent Specification > ubuntu-latest
- 選擇上方列表的 **[Triggers]** tab,將畫面右方的 **[Enable continuous integration]** 打勾
- 在 Path filters 下選擇 **+Add**,選擇 **Include**,並在後方欄位填上 `src/`

- 完成後修改 pipeline 名稱為 `ratings-web-CI`,點選上方的 **[Save & queue]** ,選擇 **[Save & queue]** 後在跳出的視窗點選 **[Save and run]**
-->
### 建立 Azure DevOps 對 AKS 的 Service Connection
- 點選 **Project Setting**
- 選擇 **Service Connections**
- 點選 
- 選擇 **Kubernetes** 並點選 **Next**

- 設定連線有兩種方式:
- **Authentication method 選擇 Azure Subscription**
1. Azure Subscription 選擇建立 AKS 的訂閱
2. Cluster 選擇建立的 AKS
3. Namespace 設定 `voting`
4. 將 **Use cluster admin credentials** 做勾選
5. Service connection name 自行設定

- **Authentication method 選擇 KubeConfig**
1. 到 Cloud Shell 上,使用以下指令開啟 KubeConfig
```
code /home/workshop-user-tw2023-#/.kube/config
```
2. 複製所有內容並貼到 Azure DevOps 中的 KubeCofig 欄位
3. 點選 ,確認出現 
4. 輸入 Service Connection name 後點選 

### 建立 Release Pipeline
- 在左方選單列選擇 Pipelines 下的 **[Release]**,進入後點選 **[New Pipeline]**

- 選擇 

- 將 Stage name 更改為 `Prod`,完成後點選 **[X]** 關閉視窗

- 選擇 Source Type 為 **Build**,Source (build pipeline) 為先前建立的 Pipeline,記下 Source alias 的資料,完成後點選 **[Add]**

- 記下 source alias 資訊

- 點選 artifact 右上方的  設定 Continuous deployment trigger,設置為 Enabled

- 選擇 `Prod` stage 內的 **[1 job, 0 task]**

- 點選 Agent Job 的 **[+]**,搜尋 `kube`,找到 **Deploy to Kubernetes** 點選 **[Add]**

- 點選  task 進行設定
- **Kubernetes service connection**:先前建立的 service connection
- **Namespace**: `voting`
- **Manifests**: `$(System.DefaultWorkingDirectory)/**/**/azure-vote-front-deployment.yaml`
- **Containers**: `<ACR 名稱>/<ACR 中 repository 的名稱>:$(Release.Artifacts._azure-voting-CI.BuildId)`
(`_azure-voting-CI` 的部份確認與你的 Source alias 一致)
- **Timeout for rollout status**: `60`

<!-- - 設定變數,Variables > variable groups >  > 先前建立的 variable group > Link
 -->
- 點選上方 Pipeline,在 Prod 的 stage 中點選,在出的視窗中將 **[Pre-deployment approvals]** 改為 **[Enabled]**,並在 **[Approvers]** 欄位中選擇自己,完成後點選 **[X]** 關閉視窗

- 將上方 All Pipelines > **[New release pipeline]** 修改為 `Release web to AKS`

- 點選右上角 Save

- 接著選擇 **[Create Release]**
- 在跳出的視窗中點選 **[Create]**
- 視窗上方會有一則訊息,點選 **[release 1]**,可以查看目前部屬進度

- 點選  允許部屬

- 點選 Logs 查看部屬狀況

- 完成後瀏覽到你既有的網站確認網站依然顯示正常
- 回到 Azure Cloud Shell
- 輸入以下命令查看目前的 pods
```
kubectl describe pods azure-vote-front-xxxx -n voting
```
- 查看前端 Pod 的 tag 已有異動
```
kubectl
```

## 6/ 驗證流程
#### 將首頁加上今天日期
- Azure Repo > azure-voting-app-redis > `azure-vote/azure-vote/config_file.cfg`
```cfg
# UI Configurations
TITLE = 'Azure Voting App - <今天日期>'
VOTE1VALUE = 'Cats'
VOTE2VALUE = 'Dogs'
SHOWHOST = 'false'
```

- 完成後 commit 確認 Pipeline 有自動啟動
- 可透過 `describe` 指令確認 image 版本
```
kubectl describe pod azure-vote-front-xxxxx -n voting
```
