﻿# 使用 Dapr 與 Kubernetes 打造微服務架構：以 AKS 為例

我昨天在 [Kubernetes Summit 2021](https://k8s.ithome.com.tw/) 舉行了一場實戰工作坊，詳細的介紹 [Dapr](https://dapr.io/) 這套工具如何部署到 Kubernetes，以及如何透過 Dapr 大幅降低導入微服務架構的複雜度。今天這篇文章我打算以 Azure Kubernetes Services (AKS) 為例，將我昨天的實戰工作坊完整實作一遍。

### 準備 Azure 資源

1.  安裝 [Azure CLI](https://learn.microsoft.com/zh-tw/cli/azure/install-azure-cli?WT.mc_id=DT-MVP-4015686) 工具
    
2.  透過 Azure CLI 登入帳號
    
    ```bash
    az login --use-device-code
    ```
    
    開啟 [https://microsoft.com/devicelogin](https://microsoft.com/devicelogin) 網頁，並輸入你在 CLI 看到的 Device Code
    
3.  列出目前使用的帳號與可使用的訂用帳戶(Subscription)
    
    ```bash
    az account list -o table
    ```
    
4.  選擇要建立資源的訂用帳戶(Subscription)
    
    ```bash
    az account set --subscription [subscription-id]
    ```
    
5.  建立資源群組 (Resource Group)
    
    ```bash
    az group create -n k8ssummitlab -l westus2
    az group list
    ```
    
6.  調整 Azure CLI 使用預設資源群組與區域
    
    ```bash
    az configure --defaults location=westus2 group=k8ssummitlab
    az configure --list-defaults
    ```
    
7.  註冊 Azure CLI 提供者
    
    ```bash
    # 註冊 Microsoft.Cache 提供者，讓你可以用 az redis 命令
    az provider register --namespace Microsoft.Cache
    
    # 啟用叢集監控功能 (Enable cluster monitoring)
    az provider register --namespace Microsoft.OperationsManagement
    az provider register --namespace Microsoft.OperationalInsights
    
    # 檢查是否已經啟用監控功能
    az provider show -n Microsoft.OperationsManagement -o table
    az provider show -n Microsoft.OperationalInsights -o table
    ```
    
8.  註冊 `AKS-ExtensionManager` 與 `AKS-Dapr` 預覽功能
    
    ```bash
    az feature register --namespace "Microsoft.ContainerService" --name "AKS-ExtensionManager"
    az feature register --namespace "Microsoft.ContainerService" --name "AKS-Dapr"
    ```
    
    檢查是否註冊成功 (可能要等十幾分鐘才會註冊好)
    
    ```bash
    az feature list -o table --query "[?contains(name, 'Microsoft.ContainerService/AKS-ExtensionManager')].{Name:name,State:properties.state}"
    az feature list -o table --query "[?contains(name, 'Microsoft.ContainerService/AKS-Dapr')].{Name:name,State:properties.state}"
    ```
    
    更新功能旗標狀態 (上面的步驟確實完成才能執行以下這兩行)
    
    ```bash
    az provider register --namespace Microsoft.KubernetesConfiguration
    az provider register --namespace Microsoft.ContainerService
    ```
    
    更新功能旗標狀態 (上面的步驟確實完成才能執行以下這兩行)
    
    ```bash
    az provider show -n Microsoft.KubernetesConfiguration
    az provider show -n Microsoft.ContainerService
    ```
    

### 建立 AKS 叢集環境

1.  查詢可使用的 Kubernetes 版本
    
    ```bash
    az aks get-versions -o table
    ```
    
    ```
    KubernetesVersion    Upgrades
    -------------------  ------------------------
    1.22.4               None available
    1.22.2               1.22.4
    1.21.7               1.22.2, 1.22.4
    1.21.2               1.21.7, 1.22.2, 1.22.4
    1.20.13              1.21.2, 1.21.7
    1.20.9               1.20.13, 1.21.2, 1.21.7
    1.19.13              1.20.9, 1.20.13
    1.19.11              1.19.13, 1.20.9, 1.20.13
    ```
    
2.  建立 Kubernetes 叢集
    
    Bash
    
    ```bash
    az aks create -n k8ssummitaks -l westus2 -g k8ssummitlab \
        --kubernetes-version 1.21.7 \
        --node-vm-size Standard_DS2_v2 \
        --enable-cluster-autoscaler \
        --node-count 3 \
        --min-count 3 \
        --max-count 6 \
        --network-plugin azure \
        --network-policy calico \
        --enable-addons monitoring \
        --enable-managed-identity \
        --generate-ssh-keys \
        -o json
    ```
    
    PowerShell
    
    ```bash
    az aks create -n k8ssummitaks -l westus2 -g k8ssummitlab `
        --kubernetes-version 1.21.7 `
        --node-vm-size Standard_DS2_v2 `
        --enable-cluster-autoscaler `
        --node-count 3 `
        --min-count 3 `
        --max-count 6 `
        --network-plugin azure `
        --network-policy calico `
        --enable-addons monitoring `
        --enable-managed-identity `
        --generate-ssh-keys `
        -o json
    ```
    
3.  檢查叢集是否建立成功
    
    ```bash
    az aks list -o table
    ```
    
4.  安裝 `kubectl` 工具 (請安裝與 Server 一樣的版本)
    
    Bash
    
    ```powershell
    sudo az aks install-cli --client-version=1.21.7
    ```
    
    PowerShell
    
    ```powershell
    az aks install-cli --client-version=1.21.7
    
    $env:PATH = "$env:USERPROFILE\.azure-kubectl;$env:USERPROFILE\.azure-kubelogin;" + $env:PATH
    ```
    
5.  連接 AKS 叢集 (取得 `KUBECONFIG` 檔案)
    
    ```bash
    az aks get-credentials -n k8ssummitaks
    ```
    
6.  測試 AKS 是否可以正常連接
    
    ```bash
    # 請確保 Client 與 Server 為相同版號
    kubectl version --short
    
    # 取得叢集資訊
    kubectl cluster-info
    
    # 取得叢集節點資訊
    kubectl get node -o wide
    
    # 取得叢集所有 namespace 與 pod 清單
    kubectl get pod --all-namespaces
    ```
    

### 初始化 Dapr 到 Kubernetes 叢集 ([Deploy Dapr on a Kubernetes cluster](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/))

1.  安裝 [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) 工具
    
2.  初始化 Dapr 並安裝至 Kubernetes 叢集中
    
    啟用 Dapr 高可用模式
    
    ```bash
    dapr init -k --enable-ha=true --wait
    ```
    
    啟用 Dapr 單點模式
    
    ```bash
    dapr init -k --wait
    ```
    
    ```
    Making the jump to hyperspace...
    Note: To install Dapr using Helm, see here: https://docs.dapr.io/getting-started/install-dapr-kubernetes/#install-with-helm-advanced
    
    Deploying the Dapr control plane to your cluster...
    Success! Dapr has been installed to namespace dapr-system. To verify, run `dapr status -k' in your terminal. To get started, go here: https://aka.ms/dapr-getting-started
    ```
    
    初始化需要 1 分鐘左右，請用以下命令確認所有 `STATUS` 都是 `Running` 狀態
    
    ```bash
    dapr status -k
    ```
    
    ```
    NAME                   NAMESPACE    HEALTHY  STATUS   REPLICAS  VERSION  AGE  CREATED
    dapr-dashboard         dapr-system  True     Running  1         0.9.0    2m   2021-12-21 18:58.28
    dapr-operator          dapr-system  True     Running  3         1.5.1    2m   2021-12-21 18:58.28
    dapr-sentry            dapr-system  True     Running  3         1.5.1    2m   2021-12-21 18:58.28
    dapr-sidecar-injector  dapr-system  True     Running  3         1.5.1    2m   2021-12-21 18:58.28
    dapr-placement-server  dapr-system  True     Running  3         1.5.1    2m   2021-12-21 18:58.28
    ```
    
3.  重新部署 `deploy/dapr-operator` ([參考資訊](https://github.com/dapr/dapr/issues/2180#issuecomment-702987068))
    
    ```powershell
    kubectl rollout restart deploy/dapr-operator -n dapr-system
    ```
    
4.  建立 Azure Cache for Redis 服務
    
    先建立 Azure Cache for Redis 服務 ([az redis create](https://learn.microsoft.com/zh-tw/cli/azure/redis?view=azure-cli-latest&WT.mc_id=DT-MVP-4015686#az_redis_create))
    
    ```bash
    az redis create -n k8ssummitredis --sku Basic --vm-size c0 -o json
    ```
    
    取得 Redis 金鑰 (`your-redis-password`)
    
    ```bash
    az redis list-keys -n k8ssummitredis
    ```
    
5.  建立一個狀態儲存元件 (state store)
    
    建立 Secret 儲存 Redis 金鑰
    
    ```bash
    kubectl create secret generic redis --from-literal=redis-password=<your-redis-password>
    ```
    
    設定 Dapr 元件 (Dapr components)
    
    ```yaml
    apiVersion: dapr.io/v1alpha1
    kind: Component
    metadata:
      name: statestore
    spec:
      type: state.redis
      version: v1
      metadata:
      # These settings will work out of the box if you use `helm install
      # bitnami/redis`.  If you have your own setup, replace
      # `redis-master:6379` with your own Redis master address, and the
      # Redis password with your own Secret's name. For more information,
      # see https://docs.dapr.io/operations/components/component-secrets .
      - name: redisHost
        value: k8ssummitredis.redis.cache.windows.net:6380
      - name: redisPassword
        secretKeyRef:
          name: redis
          key: redis-password
      - name: enableTLS
        value: true
    auth:
      secretStore: kubernetes
    ```
    
    安裝元件
    
    ```powershell
    kubectl apply -f ./deploy/redis.yaml
    ```
    

### 部署 Dapr 範例應用程式

1.  下載原始碼
    
    ```bash
    git clone https://github.com/dapr/quickstarts.git
    cd quickstarts/hello-kubernetes
    ```
    
2.  部署 Node.js 應用程式
    
    ```bash
    # 部署 Node.js 應用程式
    kubectl apply -f ./deploy/node.yaml
    ```
    
3.  查看 `dapr-sidecar-injector` 是否正常運作
    
    透過以下命令取得 Dapr 的 Sidecar Injector Logs
    
    ```bash
    kubectl logs -l app=dapr-sidecar-injector -n dapr-system
    ```
    
    你必須看到 `Sidecar injector succeeded injection for app 'nodeapp'` 這個訊息：
    
    ```
    time="2021-12-21T20:10:30.577411865Z" level=info msg="log level set to: info" instance=dapr-sidecar-injector-6f749dbf87-vvrnz scope=dapr.injector type=log ver=unknown
    time="2021-12-21T20:10:30.578115669Z" level=info msg="metrics server started on :9090/" instance=dapr-sidecar-injector-6f749dbf87-vvrnz scope=dapr.metrics type=log ver=unknown
    time="2021-12-21T20:10:30.578207469Z" level=info msg="starting Dapr Sidecar Injector -- version 1.5.1 -- commit c6daae8e9b11b3e241a9cb84c33e5aa740d74368" instance=dapr-sidecar-injector-6f749dbf87-vvrnz scope=dapr.injector type=log ver=unknown
    time="2021-12-21T20:10:30.58025808Z" level=info msg="Healthz server is listening on :8080" instance=dapr-sidecar-injector-6f749dbf87-vvrnz scope=dapr.injector type=log ver=unknown
    time="2021-12-21T20:10:31.654958547Z" level=info msg="Sidecar injector is listening on :4000, patching Dapr-enabled pods" instance=dapr-sidecar-injector-6f749dbf87-vvrnz scope=dapr.injector type=log ver=unknown
    time="2021-12-21T20:12:31.613696722Z" level=info msg="AdmissionReview for Kind=/v1, Kind=Pod, Namespace=default Name= () UID=e49c2a43-503d-4607-a2f3-0fedf0e6f76f patchOperation=CREATE UserInfo={system:serviceaccount:kube-system:replicaset-controller b195fb68-b478-4954-992c-8653f2009acb [system:serviceaccounts system:serviceaccounts:kube-system system:authenticated] map[]}" instance=dapr-sidecar-injector-6f749dbf87-vvrnz scope=dapr.injector type=log ver=unknown
    time="2021-12-21T20:12:31.640646979Z" level=info msg="ready to write response ..." instance=dapr-sidecar-injector-6f749dbf87-vvrnz scope=dapr.injector type=log ver=unknown
    time="2021-12-21T20:12:31.641067781Z" level=info msg="Sidecar injector succeeded injection for app 'nodeapp'" instance=dapr-sidecar-injector-6f749dbf87-vvrnz scope=dapr.injector type=log ver=unknown
    ```
    
4.  取得 Node.js 應用程式的服務端點 (EXTERNAL-IP)
    
    ```bash
    kubectl get svc nodeapp
    ```
    
    > 假設為 `20.112.54.56`
    
    PowerShell
    
    ```powershell
    $EXTERNAL_IP='http://20.112.54.56'
    ```
    
    Bash
    
    ```bash
    export EXTERNAL_IP='http://20.112.54.56'
    ```
    
5.  測試應用程式 (讀取 `DAPR_HTTP_PORT` 與 `DAPR_GRPC_PORT` 環境變數)
    
    ```bash
    curl "$EXTERNAL_IP/ports"
    ```
    
6.  執行建立訂單 (透過 Dapr 與 state store 寫入狀態)
    
    ```bash
    curl --request POST --data "@sample.json" --header 'Content-Type: application/json' "$EXTERNAL_IP/neworder"
    ```
    
7.  查看訂單編號 (透過 Dapr 與 state store 讀取狀態)
    
    ```bash
    curl "$EXTERNAL_IP/order"
    ```
    
8.  部署 Python 應用程式 (透過 Dapr 呼叫 nodeapp 的 neworder 方法)
    
    ```bash
    kubectl apply -f ./deploy/python.yaml
    ```
    
    查看 Python 應用程式的 Logs
    
    ```bash
    kubectl logs --selector=app=node -c node --tail=-1 -f
    ```
    
9.  查看訂單編號 (透過 Dapr 與 state store 讀取狀態)
    
    ```bash
    curl "$EXTERNAL_IP/order"
    ```
    
10.  開啟 Dapr Dashboard 儀表版介面
     
     先找出 `dapr-dashboard` 的 Pod 名稱
     
     ```bash
     kubectl get pod --namespace=dapr-system -l 'app.kubernetes.io/component=dashboard'
     ```
     
     開啟 Port Forwarding
     
     ```bash
     kubectl port-forward dapr-dashboard-57b4db56fc-pxvs7 8080:8080 --namespace=dapr-system
     ```
     
     開啟 [http://localhost:8080/](http://localhost:8080/) 網頁
     

### 清除應用程式與 Dpar 相關資源

1.  刪除 Deployments, Pods, Services
    
    ```bash
    kubectl delete -f ./deploy/node.yaml
    kubectl delete -f ./deploy/python.yaml
    ```
    
2.  從 Kubernetes 清除 Dapr 相關資源
    
    ```bash
    dapr uninstall -k
    ```
    

### 暫停 AKS 叢集 (將 Node Count 調整為 0)

```bash
az aks stop -n k8ssummitaks --no-wait
```

### 重啟 AKS 叢集 (將 Node 調整回暫停前的狀態)

```bash
az aks start -n k8ssummitaks --no-wait
```

### 刪除 k8ssummitlab 資源群組 (連同 AKS 叢集一併刪除)

```bash
az group delete --name k8ssummitlab --yes --no-wait
```

### 相關連結

-   [Dapr - Distributed Application Runtime](https://dapr.io/)
    -   [Dapr overview](https://learn.microsoft.com/en-us/azure/aks/dapr-overview)
-   AKS
    -   [Dapr extension for Azure Kubernetes Service (AKS) (preview)](https://learn.microsoft.com/en-us/azure/aks/dapr?WT.mc_id=DT-MVP-4015686)
    -   [Quickstart: Deploy an application using the Dapr cluster extension (preview) for Azure Kubernetes Service (AKS)](https://learn.microsoft.com/en-us/azure/aks/quickstart-dapr?WT.mc_id=DT-MVP-4015686#deploy-the-nodejs-app-with-the-dapr-sidecar)
-   KEDA
    -   [Hello World to Dapr and Keda](https://billtcheng2013.medium.com/hello-world-to-dapr-and-keda-a3c5bc6c0e26)
