---
# System prepended metadata

title: etcd 深入探討
tags: [K8S]

---

# etcd 深入探討


## 安裝 etcdctl

* 在 control plane 安裝 etcdctl
```
$ ETCD_RELEASE=$(curl -s https://api.github.com/repos/etcd-io/etcd/releases/latest|grep tag_name | cut -d '"' -f 4)

$ wget https://github.com/etcd-io/etcd/releases/download/${ETCD_RELEASE}/etcd-${ETCD_RELEASE}-linux-amd64.tar.gz

$ tar zxvf etcd-${ETCD_RELEASE}-linux-amd64.tar.gz

$ cp -rp etcd-${ETCD_RELEASE}-linux-amd64/etcdctl /usr/local/bin
```

## 直接進入 etcd 看資料

* 建立一個持續 CrashLoopBackOff 的 pod
```
$ kubectl create deploy crash --image=quay.io/hahappyman/myapp

$ kubectl get pod
NAME                     READY   STATUS             RESTARTS       AGE
crash-85bcfd65f6-mh8bq   1/2     CrashLoopBackOff   1 (108s ago)   <invalid>


# 查看 pod 的相關 event
$ kubectl describe pod -l app=crash
......
Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  34s                default-scheduler  Successfully assigned default/crash-556c4f5dcc-whtz7 to tk8s-worker1
  Normal   Pulled     32s                kubelet            Successfully pulled image "quay.io/hahappyman/myapp" in 739ms (739ms including waiting). Image size: 1302385 byte
s.
  Normal   Pulled     30s                kubelet            Successfully pulled image "quay.io/hahappyman/myapp" in 837ms (837ms including waiting). Image size: 1302385 byte
s.
  Normal   Pulling    15s (x3 over 33s)  kubelet            Pulling image "quay.io/hahappyman/myapp"
  Normal   Created    14s (x3 over 32s)  kubelet            Created container: myapp
  Normal   Started    14s (x3 over 32s)  kubelet            Started container myapp
  Normal   Pulled     14s                kubelet            Successfully pulled image "quay.io/hahappyman/myapp" in 866ms (866ms including waiting). Image size: 1302385 byte
s.
  Warning  BackOff    1s (x4 over 30s)   kubelet            Back-off restarting failed container myapp in pod crash-556c4f5dcc-whtz7_default(5c4fb69e-2678-4d67-8733-64337123
36e2)
```

* 宣告憑證，後續使用 etcdctl 就不用再手動加憑證，需注意自己的 endpoints 位置。
```
$ alias etcdctl="ETCDCTL_API=3 /usr/local/bin/etcdctl \
--endpoints=172.22.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \
--key=/etc/kubernetes/pki/apiserver-etcd-client.key"
```

* 直接看 etcd 儲存 pod 的內容。
* 可以看到 pod 目前狀態是 CrashLoopBackOff，並且這裡也會存放 pod 的 event 資訊
```
# 找 crash pod 的 key 名稱
$ etcdctl get /registry --prefix --keys-only |grep pods
.......
/registry/pods/default/crash-556c4f5dcc-whtz7

$ etcdctl get /registry/pods/default/crash-556c4f5dcc-whtz7
......
CrashLoopBackOffyback-off 2m40s restarting failed container=myapp pod=crash-556c4f5dcc-whtz7_default(5c4fb69e-2678-4d67-8733-6433712336e2)vtComplete:Mcontainerd://988e4e
62096813472d8f055b97319df9322e4ccfdaecd1c31ca3cb13228b86cf (2quay.io/hahappyman/myapp:latest:`quay.io/hahappyman/myapp@sha256:8b5ade73d8c28d7168958e4290479dd746f07c706f1c798
fb732d7336675c208BMcontainerd://988e4e62096813472d8f055b97319df9322e4ccfdaecd1c31ca3cb13228b86cfHbR
kube-api-access-wx56l-/var/run/secrets/kubernetes.io/serviceaccountDisabledJ
BestEffortZb
```
![image](https://hackmd.io/_uploads/HkY5N9Beel.png)


## 問題探討
* 如果 etcd 會存放 pod 生命狀態和 pod 的即時 event，那麼 etcd 對於網路與 Disk I/O 效能都會非常要求。
* etcd 官網要求的網路與 Disk I/O 效能 : https://etcd.io/docs/v3.4/op-guide/performance/
* 如果 etcd 備份了 crash 的 pod，那麼 restore etcd 時，是不是 crash 的 pod 也會一起被復原?


## etcd 效能優化
### Time parameters tuning
* 適當調整 etcd 的 leader 選舉的心跳時間。如果心跳間隔太低，etcd 將發送不必要的訊息，增加 CPU 和網路資源的使用率。
  - `heartbeat-interval`：定義 etcd leader 向 follower 發送心跳的時間間隔，單位為毫秒，預設為 100 毫秒。
  - `election-timeout`：當 follower 在指定時間內（毫秒）沒有收到 leader 的心跳，就會觸發新的 leader 選舉，單位為毫秒，預設為 1000 毫秒。
* 可以使用 ping 來測試每個 etcd 之間的 RTT 延遲時間，如果 ping 是 10ms，那麼 `heartbeat-interval` 參數就要設他的(RTT) 0.5 ~ 1.5 倍(5~15)。
* 而 `election-timeout` 至少為 RTT 的 10 倍(100)。
* `heartbeat-interval` 和 `election-timeout` 要整個叢集一致。如果某些節點設太短，某些太長，會導致整個叢集不穩。



### Snapshots tuning
* 為了避免產生巨大的 log，etcd 會定期進行快照，這些快照會保存系統的當前狀態。etcd 為了避免太頻繁做快照（會耗 CPU 與 I/O），會等到變更次數累積到一定數量才產生快照。
  - `snapshot-count` : 到達 etcd 的累積變更次數後才做快照，預設為 10,000。


### Disk tuning
* 設定 etcd Disk I/O 的優先權，Disk 優先權可以配置為 ionice，使其高於其他 process，但又不能太高以至於壓垮伺服器。
```
# best effort, highest priority
$ sudo ionice -c2 -n0 -p `pgrep etcd`
```


## etcd IO 效能測試
* 根據不同環境調整 `tolerations`、`nodeSelector`
* `quay.io/cloud-bulldozer/etcd-perf` 這張 image 是使用 fio 指令來驗證 etcd 所在的節點，底層的 Disk IO 效能是否足夠。
```
$ echo 'apiVersion: v1
kind: Pod
metadata:
  name: etcd-perf
spec:
  restartPolicy: Never
  initContainers:
    - name: etcd-perf
      image: quay.io/cloud-bulldozer/etcd-perf
      volumeMounts:
        - name: etcd-data
          mountPath: /var/lib/etcd
  containers:
    - name: wait
      image: busybox
      command: ["sh", "-c", "sleep infinity"]
      lifecycle:
          postStart:
              exec:
                command:
                  - /bin/sh
                  - -c
                  - |
                    rm -rf /var/lib/etcd/etcd_perf.0.0
      volumeMounts:
        - name: etcd-data
          mountPath: /var/lib/etcd
  volumes:
    - name: etcd-data
      hostPath:
        path: /var/lib/etcd
        type: Directory
  tolerations:
    - key: "node-role.kubernetes.io/control-plane"
      operator: "Exists"
      effect: "NoSchedule"
  nodeSelector:
    kubernetes.io/hostname: "tk8s-control-plane"' | kubectl apply -f -
```
* 檢查 log，以 10 ms 為基準，如果超過這個數值就會被判定為 etcd IO 效能太差。
```
$ kubectl get pod
NAME        READY   STATUS    RESTARTS   AGE
etcd-perf   1/1     Running   0          108s

$ kubectl logs etcd-perf -c etcd-perf
......
INFO: 99th percentile of fsync is 7176192 ns
INFO: 99th percentile of the fsync is within the recommended threshold: - 10 ms, the disk can be used to host etcd

$ kubectl delete pod etcd-perf
```




## 參考

https://etcd.io/docs/v3.4/tuning/