This page covers the following topics:

Checking if the etcd Container is Running

The container for etcd should have status Up. The duration shown after Up is the time the container has been running.

Example output:

  1. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  2. 605a124503b9 rancher/coreos-etcd:v3.2.18 "/usr/local/bin/et..." 2 hours ago Up 2 hours etcd

etcd Container Logging

The logging of the container can contain information on what the problem could be.

  1. docker logs etcd

etcd Cluster and Connectivity Checks

The address where etcd is listening depends on the address configuration of the host etcd is running on. If an internal address is configured for the host etcd is running on, the endpoint for etcdctl needs to be specified explicitly. If any of the commands respond with Error: context deadline exceeded, the etcd instance is unhealthy (either quorum is lost or the instance is not correctly joined in the cluster)

Output should contain all the nodes with the etcd role and the output should be identical on all nodes.

Command:

  1. docker exec etcd etcdctl member list

Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

  1. docker exec etcd sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT member list"

Example output:

  1. xxx, started, etcd-xxx, https://IP:2380, https://IP:2379,https://IP:4001
  2. xxx, started, etcd-xxx, https://IP:2380, https://IP:2379,https://IP:4001
  3. xxx, started, etcd-xxx, https://IP:2380, https://IP:2379,https://IP:4001

Check Endpoint Status

The values for RAFT TERM should be equal and RAFT INDEX should be not be too far apart from each other.

Command:

    Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

    1. docker exec etcd etcdctl endpoint status --endpoints=$(docker exec etcd /bin/sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT member list | cut -d, -f5 | sed -e 's/ //g' | paste -sd ','") --write-out table

    Example output:

    1. +-----------------+------------------+---------+---------+-----------+-----------+------------+
    2. | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
    3. +-----------------+------------------+---------+---------+-----------+-----------+------------+
    4. | https://IP:2379 | 333ef673fc4add56 | 3.2.18 | 24 MB | false | 72 | 66887 |
    5. | https://IP:2379 | 5feed52d940ce4cf | 3.2.18 | 24 MB | true | 72 | 66887 |
    6. | https://IP:2379 | db6b3bdb559a848d | 3.2.18 | 25 MB | false | 72 | 66887 |
    7. +-----------------+------------------+---------+---------+-----------+-----------+------------+

    Check Endpoint Health

    Command:

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

    Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

    1. docker exec etcd etcdctl endpoint health --endpoints=$(docker exec etcd /bin/sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT member list | cut -d, -f5 | sed -e 's/ //g' | paste -sd ','")

    Example output:

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

    Command:

    1. for endpoint in $(docker exec etcd /bin/sh -c "etcdctl member list | cut -d, -f5"); do
    2. echo "Validating connection to ${endpoint}/health"
    3. done
    1. for endpoint in $(docker exec etcd /bin/sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT member list | cut -d, -f5"); do
    2. echo "Validating connection to ${endpoint}/health";
    3. 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 exec etcd printenv ETCDCTL_CACERT) --cert $(docker exec etcd printenv ETCDCTL_CERT) --key $(docker exec etcd printenv ETCDCTL_KEY) "${endpoint}/health"
    4. done

    Example output:

    Check Connectivity on Port TCP/2380

    Command:

    1. for endpoint in $(docker exec etcd /bin/sh -c "etcdctl member list | cut -d, -f4"); do
    2. echo "Validating connection to ${endpoint}/version";
    3. 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 exec etcd printenv ETCDCTL_CACERT) --cert $(docker exec etcd printenv ETCDCTL_CERT) --key $(docker exec etcd printenv ETCDCTL_KEY) "${endpoint}/version"
    4. done

    Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

    1. for endpoint in $(docker exec etcd /bin/sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT member list | cut -d, -f4"); do
    2. echo "Validating connection to ${endpoint}/version";
    3. 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 exec etcd printenv ETCDCTL_CACERT) --cert $(docker exec etcd printenv ETCDCTL_CERT) --key $(docker exec etcd printenv ETCDCTL_KEY) "${endpoint}/version"
    4. done

    Example output:

    1. Validating connection to https://IP:2380/version
    2. {"etcdserver":"3.2.18","etcdcluster":"3.2.0"}
    3. Validating connection to https://IP:2380/version
    4. {"etcdserver":"3.2.18","etcdcluster":"3.2.0"}
    5. Validating connection to https://IP:2380/version
    6. {"etcdserver":"3.2.18","etcdcluster":"3.2.0"}

    etcd Alarms

    etcd will trigger alarms, for instance when it runs out of space.

    Command:

    1. docker exec etcd etcdctl alarm list

    Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

    1. docker exec etcd sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT alarm list"

    Example output when NOSPACE alarm is triggered:

    1. memberID:x alarm:NOSPACE
    2. memberID:x alarm:NOSPACE
    3. memberID:x alarm:NOSPACE

    etcd Space Errors

    Related error messages are etcdserver: mvcc: database space exceeded or applying raft message exceeded backend quota. Alarm NOSPACE will be triggered.

    Resolutions:

    Compact the Keyspace

    Command:

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

    Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

    1. rev=$(docker exec etcd sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT endpoint status --write-out json | egrep -o '\"revision\":[0-9]*' | egrep -o '[0-9]*'")
    2. docker exec etcd sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT compact \"$rev\""

    Example output:

    1. compacted revision xxx

    Command:

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

    Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

    1. docker exec etcd sh -c "etcdctl defrag --endpoints=$(docker exec etcd /bin/sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT member list | cut -d, -f5 | sed -e 's/ //g' | paste -sd ','")"

    Example output:

    1. Finished defragmenting etcd member[https://IP:2379]
    2. Finished defragmenting etcd member[https://IP:2379]

    Check Endpoint Status

    Command:

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

    Example output:

    1. +-----------------+------------------+---------+---------+-----------+-----------+------------+
    2. | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
    3. +-----------------+------------------+---------+---------+-----------+-----------+------------+
    4. | https://IP:2379 | e973e4419737125 | 3.2.18 | 553 kB | false | 32 | 2449410 |
    5. | https://IP:2379 | 4a509c997b26c206 | 3.2.18 | 553 kB | false | 32 | 2449410 |
    6. | https://IP:2379 | b217e736575e9dd3 | 3.2.18 | 553 kB | true | 32 | 2449410 |
    7. +-----------------+------------------+---------+---------+-----------+-----------+------------+

    Disarm Alarm

    After verifying that the DB size went down after compaction and defragmenting, the alarm needs to be disarmed for etcd to allow writes again.

    Command:

    1. docker exec etcd etcdctl alarm list
    2. docker exec etcd etcdctl alarm disarm
    3. docker exec etcd etcdctl alarm list

    Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

    1. docker exec etcd sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT alarm list"
    2. docker exec etcd sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT alarm disarm"
    3. docker exec etcd sh -c "etcdctl --endpoints=\$ETCDCTL_ENDPOINT alarm list"

    Example output:

    1. docker exec etcd etcdctl alarm list
    2. memberID:x alarm:NOSPACE
    3. memberID:x alarm:NOSPACE
    4. memberID:x alarm:NOSPACE
    5. docker exec etcd etcdctl alarm disarm
    6. docker exec etcd etcdctl alarm list

    Log Level

    The log level of etcd can be changed dynamically via the API. You can configure debug logging using the commands below.

    Command:

    1. 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

    Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

    1. 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_ENDPOINT)/config/local/log

    To reset the log level back to the default (INFO), you can use the following command.

    Command:

    1. 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

    Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

    1. 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_ENDPOINT)/config/local/log

    etcd Content

    If you want to investigate the contents of your etcd, you can either watch streaming events or you can query etcd directly, see below for examples.

    Command:

    1. docker exec etcd etcdctl watch --prefix /registry

    Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

    1. docker exec etcd etcdctl --endpoints=\$ETCDCTL_ENDPOINT watch --prefix /registry

    If you only want to see the affected keys (and not the binary data), you can append | grep -a ^/registry to the command to filter for keys only.

    Query etcd Directly

    Command:

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

    Command when using etcd version lower than 3.3.x (Kubernetes 1.13.x and lower) and --internal-address was specified when adding the node:

    1. docker exec etcd etcdctl --endpoints=\$ETCDCTL_ENDPOINT get /registry --prefix=true --keys-only
    1. 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

    Replacing Unhealthy etcd Nodes

    When a node in your etcd cluster becomes unhealthy, the recommended approach is to fix or remove the failed or unhealthy node before adding a new etcd node to the cluster.