---
# System prepended metadata

title: K8S lab5(Deployment)
tags: [k8s]

---

---
title: K8S lab5(Deployment)
tags: k8s
---
K8S lab5(Deployment)
===
[TOC]

## What is Deployment
Deployment 為 Pod 與 Replicaset 提供了一個宣告式的設定 及更新方式。
Deployment 運行應用程序的多個副本，並會自動替換失敗或無響應的 Pod。
通過這種方式，Deployment 有助於確保部屬的 AP 有一個或多個 Pod 可用於服務用戶請求。
Deployment由 Kubernetes Deployment Controller 管理。
Deployment 使用 Pod 模板，其中包含其 Pod 的規範。 Pod 規範決定了每個 Pod 的外觀：哪些應用程序應該在其容器中運行，Pod 應該掛載哪些 Volumes，它的 Label 等等。
當 Deployment 的 Pod 模板發生變化時，會一次自動創建一個新的 Pod。

## Why Deployment
- Create a Deployment to rollout a ReplicaSet
- Declare the new state of the Pods
- Rollback to an earlier Deployment revision
- Scale up the Deployment to facilitate more load
- Pause the rollout of a Deployment
- Use the status of the Deployment
- Clean up older ReplicaSets

## How Deployment

### Deployment 範例
```yaml=
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
```
參數說明:
- `meta.name`: Deplyment 名稱
- `spec.replicas`: 會建幾個 pod (此範例為:3個)
- `spec.selector`: 指定選擇哪些 label 的 pod
- `spec.template`: 定義 Pod 的資訊
- `spec.template.metadata`: 定義 pod Label
- `spec.template.spec`: 定義 pod 規格，Ex: Containers、Volumes等

### 建立 Deposit Deployment
1. 製作 deployment.yml
    ```yaml=
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: lab-my-deposit
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: lab-my-deposit
      template:
        metadata:
          labels:
            app: lab-my-deposit
        spec:
          containers:
          - name: my-deposit
            image: <dockerhub-id>/deposit:v1.0.0
            ports:
            - containerPort: 8080
            volumeMounts:
            - name: app-config
              mountPath: /app/config
              readOnly: true
          volumes:
          - name: app-config
            configMap:
              name: <configmap-name>
    ```
2. 刪除舊的Pod
    ```shell=
    kubectl delete pods <pod-name> -n <namespace-name>
    ```
3. 部屬 Deployment
    ```shell=
    kubectl apply -f <path-to-deployment.yml> -n <namespace-name>
    ```
4. 加上註解
    使用`kubectl annote` 指令，為這次的創立或是更新增加註解
    ```shell=
    kubectl annotate deployment/<depolyment-name> kubernetes.io/change-cause="註解內容" -n <namespace-name>
    ```
    查看 Deployment 紀錄
    ```shell=
    kubectl rollout history deployment/<depolyment-name> -n <namespace-name>
    ```
    ```
    $ kubectl rollout history deployment/lab-my-deposit -n my-namespace
    deployment.apps/lab-my-deposit
    REVISION  CHANGE-CAUSE
    1         create lab-my-deposit
    ```
5. 確認 Deployment
    ```shell=
    kubectl get deployments -n <namespace-name>
    ```
    ```
    $ kubectl get deployments -n my-namespace
    NAME             READY   UP-TO-DATE   AVAILABLE   AGE
    lab-my-deposit   3/3     3            3           4m56s
    ```
6. 確認 Pod
    ```shell=
    kubectl get pods -n <namespace-name>
    ```
    ```
    $ kubectl get pods -n my-namespace
    NAME                              READY   STATUS    RESTARTS   AGE
    lab-my-deposit-744598d9fb-hkvkr   1/1     Running   0          6m34s
    lab-my-deposit-744598d9fb-m8vhg   1/1     Running   0          6m34s
    lab-my-deposit-744598d9fb-s45l6   1/1     Running   0          6m34s
    ```
    ![](https://hackmd.io/_uploads/HyziLhaZ5.png)
7. 測試 API
    使用相同 namespace 的 net-tool 進行測試
    ```
    /app # curl --location --request GET "http://<deposit-service-ip>:8080/api/version"
    {"version":"v1.0.3"}
    ```
    總共打了4次，分別可以在三個 Pod 上找到 Log
    ![](https://hackmd.io/_uploads/Syt4hn6Wq.png)
    ![](https://hackmd.io/_uploads/rJ6Bh2pW5.png)
    ![](https://hackmd.io/_uploads/B1HIf66W5.png)

### 更新 Deposit Deployment
1. 更新 image
    請將新版的程式碼 pull 下來後，包成 jar 後建成 image 並 push 到 dockerhub
    **ps.版號記得調整，範例為v1.0.0 -> v.1.0.1**
    **可參考前面lab**
2. 調整 Deployment
    調整`spec.template.spec.containers.images`
    ```yaml=
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: lab-my-deposit
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: lab-my-deposit
      template:
        metadata:
          labels:
            app: lab-my-deposit
        spec:
          containers:
          - name: my-deposit
            image: <dockerhub-id>/deposit:v1.0.1
            ports:
            - containerPort: 8080
            volumeMounts:
            - name: app-config
              mountPath: /app/config
              readOnly: true
          volumes:
          - name: app-config
            configMap:
              name: <configmap-name>
    ```
3. 部屬 Deployment
    ```shell=
    kubectl apply -f <path-to-deployment.yml> -n <namespace-name>
    ```
4. 加上註解
    ```shell=
    kubectl annotate deployment/<depolyment-name> kubernetes.io/change-cause="註解內容" -n <namespace-name>
    ```
    查看 Deployment 紀錄
    ```shell=
    kubectl rollout history deployment/<depolyment-name> -n <namespace-name>
    ```
    ```
    $ kubectl rollout history deployment/lab-my-deposit -n my-namespace
    deployment.apps/lab-my-deposit
    REVISION  CHANGE-CAUSE
    1         create lab-my-deposit
    2         update lab-my-deposit to v1.0.1
    ```
5. 確認 Deployment
    ```shell=
    kubectl get deployments -n <namespace-name>
    ```
    ```
    $ kubectl get deployments -n my-namespace
    NAME             READY   UP-TO-DATE   AVAILABLE   AGE
    lab-my-deposit   3/3     3            3           4m56s
    ```
6. 查看 ReplicaSet
    - ReplicaSet 是用來確保在資源允許的前提下，運行的 pod 的數量會跟使用者所指令的一致
    使用以下指令可以查看 ReplicaSet
    ```shell=
    kubectl get rs -n <namespace-name>
    ```
    ```
    kubectl get rs -n my-namespace
    NAME                        DESIRED   CURRENT   READY   AGE
    lab-my-deposit-744598d9fb   0         0         0       91m
    lab-my-deposit-7dcf8f6bd9   3         3         3       90m
    ```
    可以看到在新的 ReplicaSet 中有三個 Pod 正在運行且舊的 ReplicaSet 已經沒有 Pod 了。
7. 確認 Pod
    ```shell=
    kubectl get pods -n <namespace-name>
    ```
    ```
    $ kubectl get pods -n my-namespace
    NAME                              READY   STATUS    RESTARTS   AGE
    lab-my-deposit-7dcf8f6bd9-49jt7   1/1     Running   0          6m51s
    lab-my-deposit-7dcf8f6bd9-5m6v8   1/1     Running   0          6m41s
    lab-my-deposit-7dcf8f6bd9-jfl9c   1/1     Running   0          6m45s
    ```
    可以之前建立的 pod 比較會發現，pod name 已經不一樣了，並且也有維持在 deployment.yml 中設定的數量。
8. 測試 API
    ```
    /app # curl --location --request GET "http://10.8.11.8:8080/api/version"
    {"version":"v1.0.3","datetime":"2022/03/15 18:33:46"}
    ```

### 更新 Pod
情境: 當 ConfigMap 更新時，Pod 需要重啟才能用新的 Config
以往: 要人工將 pod 刪除並新建一個 Pod
Deployment: 使用 `kubectl restart` 指令

1. 調整 ConfigMap
    此處是將 data.application.version 從 v1.0.3 -> v1.0.1
    ```yaml=
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: <config-name>
    data:
      application.yml: |-
        server:
          port: 8080
          shutdown: graceful

        spring:
          application:
            name: deposit

        management:
          endpoint:
            shutdown:
              enabled: true
          endpoints:
            web:
              exposure:
                include: '*'

        endpoints:
          shutdown:
            enabled: true

        version: 'v1.0.1'

        backend-endpoint: 'http://<external-service-name>:8080/api/customer'
    ```
2. 部屬 ConfigMap
    ```shell=
    kubectl apply -f <path-to-config> -n <namespace-name>
    ```
3. 確認 ConfigMap
    ```shell=
    kubectl get configmaps -n <namespace-name> <configmap-name> -o yaml
    ```
    ```
    $ kubectl get configmaps -n my-namespace deposit-config -o yaml
    apiVersion: v1
    data:
      application.yml: |-
        server:
          port: 8080
          shutdown: graceful

        spring:
          application:
            name: deposit

        management:
          endpoint:
            shutdown:
              enabled: true
          endpoints:
            web:
              exposure:
                include: '*'

        endpoints:
          shutdown:
            enabled: true

        version: 'v1.0.1'

        backend-endpoint: 'http://my-external-service:8080/api/customer'
    kind: ConfigMap
    ...以下略
    ```
4. 將 Pod 重啟
    ```shell=
    kubectl rollout restart deployment/<deployment-name>
    ```

5. 調整註解
    ```shell=
    kubectl annotate deployment/<depolyment-name> kubernetes.io/change-cause="註解內容" -n <namespace-name>
    ```
6. 查看 ReplicaSet
    ```
    kubectl get rs -n <namespace-name>
    ```
    ```
    $ kubectl get rs -n my-namespace
    NAME                        DESIRED   CURRENT   READY   AGE
    lab-my-deposit-6ddbc9b55    3         3         3       52m
    lab-my-deposit-744598d9fb   0         0         0       141m
    lab-my-deposit-7dcf8f6bd9   0         0         0       141m
    ```
    已經有新的 ReplicaSet 了

6. 透過describe查看Deploy更換pod的行為
    ```shell=
    kubectl describe deployment -n <namespace-name> <deployment-name>
    ```
    ```
    Name:                   lab-my-deposit-deployment
    Namespace:              default
    CreationTimestamp:      Wed, 16 Mar 2022 15:18:23 +0800
    Labels:                 <none>
    Annotations:            deployment.kubernetes.io/revision: 2
                            kubernetes.io/change-cause: update version1.0.1
    Selector:               app=deposit-app
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=deposit-app
      Containers:
       my-deposit:
        Image:        asaeeen/deposit1:v1.0.1
        Port:         8080/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:
          /app/config from app-config (ro)
      Volumes:
       app-config:
        Type:      ConfigMap (a volume populated by a ConfigMap)
        Name:      deposit-config
        Optional:  false
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   lab-my-deposit-deployment-589bb7c95 (3/3 replicas created)
    Events:
      Type    Reason             Age    From                   Message
      ----    ------             ----   ----                   -------
      Normal  ScalingReplicaSet  12m    deployment-controller  Scaled up replica set lab-my-deposit-deployment-c5b476d66 to 3
      Normal  ScalingReplicaSet  3m54s  deployment-controller  Scaled up replica set lab-my-deposit-deployment-589bb7c95 to 1
      Normal  ScalingReplicaSet  3m50s  deployment-controller  Scaled down replica set lab-my-deposit-deployment-c5b476d66 to 2
      Normal  ScalingReplicaSet  3m50s  deployment-controller  Scaled up replica set lab-my-deposit-deployment-589bb7c95 to 2
      Normal  ScalingReplicaSet  3m47s  deployment-controller  Scaled down replica set lab-my-deposit-deployment-c5b476d66 to 1
      Normal  ScalingReplicaSet  3m47s  deployment-controller  Scaled up replica set lab-my-deposit-deployment-589bb7c95 to 3
      Normal  ScalingReplicaSet  3m45s  deployment-controller  Scaled down replica set lab-my-deposit-deployment-c5b476d66 to 0
    ```

    可從Events可觀察出:在替換時是會將新的pod啟用後,在殺掉一個舊的pod,如此往復，直到新的set裡面的pod數量達到預期的數量，預設StrategyType為RollingUpdate所形成的行為。
    RollingUpdate:
    ![](https://hackmd.io/_uploads/By3s8zgG9.png)
    
    RollingUpdateStrategy:
      maxUnavailable: 最大不可用數量
      　　預設為２５％，即更新過程中不可用的 Pod 的個數上限
      maxSurge : 最大峰值數量
    
   

6. 測試 API
    ```
    /app # curl --location --request GET "http://10.8.11.8:8080/api/version"
    {"version":"v1.0.1","datetime":"2022/03/17 09:59:25"}
    ```

### 回溯 Deployment 版本
情境: 當發現這次的上版有問題，或是需要退回之前部屬的版本
可以使用 `kubectl rollout undo` 這個指令達成

1. 查看 Deployment 紀錄
    ```shell=
    kubectl rollout history deployment/<depolyment-name> -n <namespace-name>
    ```
    ```
    $ kubectl rollout history -n my-namespace deployment/lab-my-deposit
    deployment.apps/lab-my-deposit
    REVISION  CHANGE-CAUSE
    1         create lab-my-deposit
    2         update lab-my-deposit to v1.0.1
    3         update configmap
    ```
2. 回溯到之前版本
    ```shell=
    kubectl rollout undo deployment/<deployment-name> -n <namespace-name> --to-revision=版本
    ```
3. 查看 Deployment 紀錄
    ```
    $ kubectl rollout history -n my-namespace deployment/lab-my-deposit
    deployment.apps/lab-my-deposit
    REVISION  CHANGE-CAUSE
    2         update lab-my-deposit to v1.0.1
    3         update configmap
    4         create lab-my-deposit
    ```
    可以發現 `create lab-my-deposit` 已經變成最新版了
4. 測試 API
    ```
    /app # curl --location --request GET "http://10.8.11.8:8080/api/version"
    {"version":"v1.0.1"}
    ```
    已成功退回之前的版本
    
<!-- 待更新其他 Strategy k8: create 

使用k8 模擬 cannary , blue-green

-->

### 刪除 Deployment
1. 刪除 Deployment 指令 
    ```shell=
    kubectl delete deployments -n <namespace-name> <deployment-name>
    ```
2. 確認 Deployment
    ```shell=
    kubectl get deployments -n <namespace-name>
    ```
    ```
    $ kubectl get deployments -n my-namespace
    No resources found in my-namespace namespace.
    ```
3. 確認 Pod
    ```shell=
    kubectl get pods -n <namespace-name>
    ```
    ```
    $ kubectl get pods -n my-namespace
    NAME       READY   STATUS    RESTARTS   AGE
    net-tool   1/1     Running   0          5d17h
    ```
    可以發現 Pod 裡面已經沒有 deposit 了

## 參考
- [Kubernetes - Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)
- [Kubernates - Replicaset](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/) 
- [GKE - Deployment ](https://cloud.google.com/kubernetes-engine/docs/concepts/deployment)
- [Kubernetes Deployment Strategies](https://www.weave.works/blog/kubernetes-deployment-strategies)
