跳至主要内容

疑難排解 etcd 節點

本節包含用於疑難排解具有 etcd 角色節點的命令和提示。

檢查 etcd 容器是否正在執行​

etcd 的容器應該具有 Up 狀態。Up 後顯示的持續時間是容器已執行的時間。

docker ps -a -f=name=etcd$

範例輸出:

CONTAINER ID   IMAGE                                 COMMAND                  CREATED          STATUS          PORTS     NAMES
d26adbd23643 rancher/mirrored-coreos-etcd:v3.5.7 "/usr/local/bin/etcd…" 30 minutes ago Up 30 minutes etcd

etcd 容器日誌記錄​

容器的日誌記錄可能包含有關問題所在的資訊。

docker logs etcd
日誌說明
health check for peer xxx could not connect: dial tcp IP:2380: getsockopt: connection refused無法建立到顯示的地址在連接埠 2380 上的連線。檢查具有顯示地址的主機上是否正在執行 etcd 容器。
xxx is starting a new election at term xetcd 叢集已失去其法定人數並嘗試建立新的領導者。這可能發生在執行 etcd 的大多數節點關閉/無法連線時。
connection error: desc = "transport: Error while dialing dial tcp 0.0.0.0:2379: i/o timeout"; Reconnecting to {0.0.0.0:2379 0 <nil>}主機防火牆阻止了網路通訊。
rafthttp: request cluster ID mismatch記錄 rafthttp: request cluster ID mismatch 的 etcd 實例節點正在嘗試加入已經與另一個對等節點形成的叢集。應該從叢集中移除該節點,然後重新新增。
rafthttp: failed to find member叢集狀態(/var/lib/etcd)包含錯誤的資訊以加入叢集。應該從叢集中移除該節點,清理狀態目錄,然後重新新增該節點。

etcd 叢集和連線檢查​

etcd 偵聽的地址取決於 etcd 執行所在主機的地址配置。如果 etcd 執行所在主機配置了內部地址,則需要明確指定 etcdctl 的端點。如果任何命令回應 Error: context deadline exceeded,則 etcd 實例不健康(法定人數已失去或實例未正確加入叢集)

檢查所有節點上的 etcd 成員​

輸出應包含所有具有 etcd 角色的節點,並且所有節點上的輸出應該相同。

命令:

docker exec etcd etcdctl member list

檢查端點狀態​

RAFT TERM 的值應該相等,並且 RAFT INDEX 不應該相距太遠。

命令:

docker exec -e ETCDCTL_ENDPOINTS=$(docker exec etcd etcdctl member list | cut -d, -f5 | sed -e 's/ //g' | paste -sd ',') etcd etcdctl endpoint status --write-out table

範例輸出:

+-----------------+------------------+---------+---------+-----------+-----------+------------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+-----------------+------------------+---------+---------+-----------+-----------+------------+
| https://IP:2379 | 333ef673fc4add56 | 3.5.7 | 24 MB | false | 72 | 66887 |
| https://IP:2379 | 5feed52d940ce4cf | 3.5.7 | 24 MB | true | 72 | 66887 |
| https://IP:2379 | db6b3bdb559a848d | 3.5.7 | 25 MB | false | 72 | 66887 |
+-----------------+------------------+---------+---------+-----------+-----------+------------+

檢查端點健康狀況​

命令:

docker exec -e ETCDCTL_ENDPOINTS=$(docker exec etcd etcdctl member list | cut -d, -f5 | sed -e 's/ //g' | paste -sd ',') etcd etcdctl endpoint health

範例輸出:

https://IP:2379 is healthy: successfully committed proposal: took = 2.113189ms
https://IP:2379 is healthy: successfully committed proposal: took = 2.649963ms
https://IP:2379 is healthy: successfully committed proposal: took = 2.451201ms

檢查連接埠 TCP/2379 的連線​

命令:

for endpoint in $(docker exec etcd etcdctl member list | cut -d, -f5); do
echo "Validating connection to ${endpoint}/health"
docker run --net=host -v $(docker inspect kubelet --format '{{ range .Mounts }}{{ if eq .Destination "/etc/kubernetes" }}{{ .Source }}{{ end }}{{ end }}')/ssl:/etc/kubernetes/ssl:ro appropriate/curl -s -w "\n" --cacert $(docker inspect -f '{{range $index, $value := .Config.Env}}{{if eq (index (split $value "=") 0) "ETCDCTL_CACERT" }}{{range $i, $part := (split $value "=")}}{{if gt $i 1}}{{print "="}}{{end}}{{if gt $i 0}}{{print $part}}{{end}}{{end}}{{end}}{{end}}' etcd) --cert $(docker inspect -f '{{range $index, $value := .Config.Env}}{{if eq (index (split $value "=") 0) "ETCDCTL_CERT" }}{{range $i, $part := (split $value "=")}}{{if gt $i 1}}{{print "="}}{{end}}{{if gt $i 0}}{{print $part}}{{end}}{{end}}{{end}}{{end}}' etcd) --key $(docker inspect -f '{{range $index, $value := .Config.Env}}{{if eq (index (split $value "=") 0) "ETCDCTL_KEY" }}{{range $i, $part := (split $value "=")}}{{if gt $i 1}}{{print "="}}{{end}}{{if gt $i 0}}{{print $part}}{{end}}{{end}}{{end}}{{end}}' etcd) "${endpoint}/health"
done

範例輸出:

Validating connection to https://IP:2379/health
{"health": "true"}
Validating connection to https://IP:2379/health
{"health": "true"}
Validating connection to https://IP:2379/health
{"health": "true"}

檢查連接埠 TCP/2380 的連線​

命令:

for endpoint in $(docker exec etcd etcdctl member list | cut -d, -f4); do
echo "Validating connection to ${endpoint}/version";
docker run --net=host -v $(docker inspect kubelet --format '{{ range .Mounts }}{{ if eq .Destination "/etc/kubernetes" }}{{ .Source }}{{ end }}{{ end }}')/ssl:/etc/kubernetes/ssl:ro appropriate/curl --http1.1 -s -w "\n" --cacert $(docker inspect -f '{{range $index, $value := .Config.Env}}{{if eq (index (split $value "=") 0) "ETCDCTL_CACERT" }}{{range $i, $part := (split $value "=")}}{{if gt $i 1}}{{print "="}}{{end}}{{if gt $i 0}}{{print $part}}{{end}}{{end}}{{end}}{{end}}' etcd) --cert $(docker inspect -f '{{range $index, $value := .Config.Env}}{{if eq (index (split $value "=") 0) "ETCDCTL_CERT" }}{{range $i, $part := (split $value "=")}}{{if gt $i 1}}{{print "="}}{{end}}{{if gt $i 0}}{{print $part}}{{end}}{{end}}{{end}}{{end}}' etcd) --key $(docker inspect -f '{{range $index, $value := .Config.Env}}{{if eq (index (split $value "=") 0) "ETCDCTL_KEY" }}{{range $i, $part := (split $value "=")}}{{if gt $i 1}}{{print "="}}{{end}}{{if gt $i 0}}{{print $part}}{{end}}{{end}}{{end}}{{end}}' etcd) "${endpoint}/version"
done

範例輸出:

Validating connection to https://IP:2380/version
{"etcdserver":"3.5.7","etcdcluster":"3.5.0"}
Validating connection to https://IP:2380/version
{"etcdserver":"3.5.7","etcdcluster":"3.5.0"}
Validating connection to https://IP:2380/version
{"etcdserver":"3.5.7","etcdcluster":"3.5.0"}

etcd 警報​

etcd 會觸發警報,例如當它的空間用盡時。

命令:

docker exec etcd etcdctl alarm list

觸發 NOSPACE 警報時的範例輸出:

memberID:x alarm:NOSPACE
memberID:x alarm:NOSPACE
memberID:x alarm:NOSPACE

etcd 空間錯誤​

相關的錯誤訊息是 etcdserver: mvcc: database space exceeded 或 applying raft message exceeded backend quota。會觸發 NOSPACE 警報。

解決方法:

壓縮鑰匙空間​

命令:

rev=$(docker exec etcd etcdctl endpoint status --write-out json | egrep -o '"revision":[0-9]*' | egrep -o '[0-9]*')
docker exec etcd etcdctl compact "$rev"

範例輸出:

compacted revision xxx

對所有 etcd 成員進行磁碟重組​

命令:

docker exec -e ETCDCTL_ENDPOINTS=$(docker exec etcd etcdctl member list | cut -d, -f5 | sed -e 's/ //g' | paste -sd ',') etcd etcdctl defrag

範例輸出:

Finished defragmenting etcd member[https://IP:2379]
Finished defragmenting etcd member[https://IP:2379]
Finished defragmenting etcd member[https://IP:2379]

檢查端點狀態​

命令:

docker exec -e ETCDCTL_ENDPOINTS=$(docker exec etcd etcdctl member list | cut -d, -f5 | sed -e 's/ //g' | paste -sd ',') etcd etcdctl endpoint status --write-out table

範例輸出:

+-----------------+------------------+---------+---------+-----------+-----------+------------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+-----------------+------------------+---------+---------+-----------+-----------+------------+
| https://IP:2379 | e973e4419737125 | 3.5.7 | 553 kB | false | 32 | 2449410 |
| https://IP:2379 | 4a509c997b26c206 | 3.5.7 | 553 kB | false | 32 | 2449410 |
| https://IP:2379 | b217e736575e9dd3 | 3.5.7 | 553 kB | true | 32 | 2449410 |
+-----------------+------------------+---------+---------+-----------+-----------+------------+

解除警報​

在驗證資料庫大小在壓縮和磁碟重組後下降後,需要解除警報以允許 etcd 再次寫入。

命令:

docker exec etcd etcdctl alarm list
docker exec etcd etcdctl alarm disarm
docker exec etcd etcdctl alarm list

範例輸出:

docker exec etcd etcdctl alarm list
memberID:x alarm:NOSPACE
memberID:x alarm:NOSPACE
memberID:x alarm:NOSPACE
docker exec etcd etcdctl alarm disarm
docker exec etcd etcdctl alarm list

配置日誌層級​

備註

你無法在 etcd v3.5 或更高版本中動態變更日誌層級。

etcd v3.5 及更高版本​

要配置 etcd 的日誌層級,請編輯叢集 YAML:

services:
etcd:
extra_args:
log-level: "debug"

etcd v3.4 及更早版本​

在較早的 etcd 版本中,你可以使用 API 動態變更日誌層級。使用以下命令配置除錯日誌記錄:

docker run --net=host -v $(docker inspect kubelet --format '{{ range .Mounts }}{{ if eq .Destination "/etc/kubernetes" }}{{ .Source }}{{ end }}{{ end }}')/ssl:/etc/kubernetes/ssl:ro appropriate/curl -s -XPUT -d '{"Level":"DEBUG"}' --cacert $(docker exec etcd printenv ETCDCTL_CACERT) --cert $(docker exec etcd printenv ETCDCTL_CERT) --key $(docker exec etcd printenv ETCDCTL_KEY) $(docker exec etcd printenv ETCDCTL_ENDPOINTS)/config/local/log

要將日誌層級重設回預設值(INFO),你可以使用以下命令。

命令:

docker run --net=host -v $(docker inspect kubelet --format '{{ range .Mounts }}{{ if eq .Destination "/etc/kubernetes" }}{{ .Source }}{{ end }}{{ end }}')/ssl:/etc/kubernetes/ssl:ro appropriate/curl -s -XPUT -d '{"Level":"INFO"}' --cacert $(docker exec etcd printenv ETCDCTL_CACERT) --cert $(docker exec etcd printenv ETCDCTL_CERT) --key $(docker exec etcd printenv ETCDCTL_KEY) $(docker exec etcd printenv ETCDCTL_ENDPOINTS)/config/local/log

etcd 內容​

如果你想要調查 etcd 的內容,你可以監視串流事件或直接查詢 etcd,請參閱下面的範例。

監視串流事件​

命令:

docker exec etcd etcdctl watch --prefix /registry

如果你只想查看受影響的鑰匙(而不是二進位資料),你可以在命令後附加 | grep -a ^/registry 以僅過濾鑰匙。

直接查詢 etcd​

命令:

docker exec etcd etcdctl get /registry --prefix=true --keys-only

你可以處理資料以取得每個鑰匙的計數摘要,使用以下命令:

docker exec etcd etcdctl get /registry --prefix=true --keys-only | grep -v ^$ | awk -F'/' '{ if ($3 ~ /cattle.io/) {h[$3"/"$4]++} else { h[$3]++ }} END { for(k in h) print h[k], k }' | sort -nr

替換不健康的 etcd 節點​

當 etcd 叢集中的節點變得不健康時,建議的方法是在將新 etcd 節點新增到叢集之前,先修復或移除故障或不健康的節點。