글 작성자: 모두의 근삼이

본 문서는 2020년 08월 21일, kubernetes 1.17.9버전, konvoy 1.5버전을 기준으로 작성되었습니다.


에러 발생

정상적으로 동작하던 쿠버네티스 클러스터가 갑자기 새로운 deploy를 생성하지도, 삭제하지도, 수정하지도 못하는 이상증세를 보였습니다. 그리고 다음과 같은 메시지를 띄웠습니다.

etcdserver: mvcc: database space exceeded log messages

messages로그 등에서도 해당 에러와 함께 이러한 비슷한 구문을 출력하고 많은 에러를 발생시키는 것을 볼 수 있습니다.

applying raft message exceeded backend quota
compact.go:124] etcd: endpoint ([https://172.16.4.141:2379]) compact failed: etcdserver: mvcc: database space exceeded

에러 원인

쿠버네티스의 모든 클러스터의 데이터와 상태정보를 담는 데이터베이스 역할을 하는 etcd는 cluster형태로 구성할 수 있으며 주로 모든 마스터 노드에 클러스터링 되어 구성이 적용됩니다. etcd는 사용 공간에 대해서 quota를 설정하여 etcd DB 파일의 용량이 비대해 지는 것을 막을 수 있는데, 이번에는 이것이 문제가 된 것 이었습니다.

etcd는 클러스터 구성원 중 하나라도 quota 용량을 초과하게 되면, etcd 클러스터 구성원들을 대상으로 alarm:NOSPACE 알람을 발생시키고, 클러스터를 maintenance 모드(키의 읽기, 삭제만 가능해짐)로 전환하게 됩니다. 이러한 현상이 발생할 경우 다음과 같은 현상들을 볼 수 있게 됩니다.

  • alarm:NOSPACE 이 etcd 모든 구성원에 대해서 알람으로 셋팅 됩니다.
  • etcdserver: mvcc: database space exceeded 와 같은 문구를 messages 로그에서 확인할 수 있습니다.

트러블 슈팅

계속 꺼지는 etcd 컨테이너들

위 문제를 해결하기 위해서는 etcdctl 명령을 이용해서 etcd클러스터 구성원들의 과도한 키 스페이스 데이터들을 제거하고, 데이터베이스 조각모음을 수행해서 quota 범위 내로 크기를 되돌리는 과정이 필요합니다. 하지만, etcd 컨테이너들이 2~3분에 한번씩 죽어대는 바람에 제대로 작업을 진행하기가 불가능했습니다.

컨테이너가 계속 꺼지고 켜지기를 반복하는 이유는 컨테이너에 livenessProbe 설정이 세팅되어 있어서 etcd 컨테이너가 정상동작하지 않으면 healthcheck에 실패한 것으로 보고 컨테이너를 계속 재기동 하기 때문이었습니다. 우선 이 현상을 해결하기 위해서 etcd pod에 세팅되어 있는 livenessProbe 설정을 제거해 주기로 합니다. etcd는 kubernetes를 구성하는 핵심 구성요소 중 하나이기 때문에 /etc/kubernetes/manifests/ 디렉토리에 pod 구성정보가 존재합니다. 찾아서 수정해 줍니다.

# /etc/kubernetes/manifests/etcd.yaml

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    component: etcd
    tier: control-plane
  name: etcd
  namespace: kube-system
spec:
  containers:
  - command:
    - etcd
    - --advertise-client-urls=https://192.168.0.220:2379
    - --cert-file=/etc/kubernetes/pki/etcd/server.crt
    - --client-cert-auth=true
    - --data-dir=/var/lib/etcd
    - --election-timeout=5000
    - --heartbeat-interval=250
    - --initial-advertise-peer-urls=https://192.168.0.220:2380
    - --initial-cluster=k8s-master1=https://192.168.0.220:2380
    - --key-file=/etc/kubernetes/pki/etcd/server.key
    - --listen-client-urls=https://127.0.0.1:2379,https://192.168.0.220:2379
    - --listen-metrics-urls=http://127.0.0.1:2381
    - --listen-peer-urls=https://192.168.0.220:2380
    - --name=k8s-master1
    - --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt
    - --peer-client-cert-auth=true
    - --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
    - --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
    - --snapshot-count=10000
    - --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
    image: k8s.gcr.io/etcd:3.3.15-0
    imagePullPolicy: IfNotPresent
# 컨테이너가 꺼지는 현상을 방지하기 위해 주석처리 해줍니다.
#    livenessProbe:
#      failureThreshold: 8
#      httpGet:
#        host: 127.0.0.1
#        path: /health
#        port: 2381
#        scheme: HTTP
#      initialDelaySeconds: 15
#      timeoutSeconds: 15
    name: etcd
    resources: {}
    volumeMounts:
    - mountPath: /var/lib/etcd
      name: etcd-data
    - mountPath: /etc/kubernetes/pki/etcd
      name: etcd-certs
  hostNetwork: true
  priorityClassName: system-cluster-critical
  volumes:
  - hostPath:
      path: /etc/kubernetes/pki/etcd
      type: DirectoryOrCreate
    name: etcd-certs
  - hostPath:
      path: /var/lib/etcd
      type: DirectoryOrCreate
    name: etcd-data
status: {}

위와같은 주석처리를 모든 master 노드의 해당 경로에 존재한 yaml파일에 작업해주면, etcd 컨테이너가 죽지 않게 됩니다.

etcdctl 명령어

konvoy로 설치한 kubernetes에서 etcdctl 명령어를 사용하기 위해서 가장 정석적인 방법은 etcd 컨테이너 내에 접속해서 etcd 명령어를 사용하는 것이지만, 본인은 귀찮아서 그냥 컨테이너 밖에서 찾아서 사용해보기로 했습니다. (당연히 etcd컨테이너가 동작중인 master노드에서 해야합니다.)

#bash
find / -type f -name etcdctl 2>/dev/null
#출력예시
[root@k8s-master1 manifests]# find / -type f -name etcdctl 2>/dev/null
/run/containerd/io.containerd.runtime.v1.linux/k8s.io/4fc80ceb99dfc0dca39e726d95104f5e424c53e618fd71d201b9b8b9c75a6d5d/rootfs/usr/local/bin/etcdctl
/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/9/fs/usr/local/bin/etcdctl

둘중에 아무거나 선택해서 alias를 걸어서 사용하도록 합니다. alias를 걸어줄때 etcdctl 명령어로 클러스터와 통신할 때 사용하기 위한 인증서등을 함께 세팅해서 걸어줍니다.

#bash

alias etcdctl="\
ETCDCTL_API=3 \
/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/9/fs/usr/local/bin/etcdctl \
--cacert='/etc/kubernetes/pki/etcd/ca.crt' \
--cert='/etc/kubernetes/pki/etcd/server.crt' \
--key='/etc/kubernetes/pki/etcd/server.key' "

테스트

#bash

etcdctl member list

문제 해결

우선, 알람이 설정되어 있는 목록과 현재 클러스터 상태를 확인합니다.

#bash 

etcdctl alarm list
etcdctl -w table endpoint status --cluster

etcd용량을 다이어트 해봅니다. 현재 상태를 제외한 나머지 오래된 revision들을 제거하기 위해 current revision 값을 가져옵니다.

#bash
c_revision=$(etcdctl endpoint status --write-out="json" | egrep -o '"revision":[0-9]*' | egrep -o '[0-9].*')
echo ${c_revision}

오래된 revision들을 날립니다.

#bash
etcdctl --endpoints=$(etcdctl member list | cut -d, -f5 | sed -e 's/ //g' | paste -sd ',') compact $c_revision

조각모음을 합니다. 본인의 경우에는 이 작업에서 용량이 드라마틱하게 줄어들었습니다.

#bash
etcdctl --endpoints=$(etcdctl member list | cut -d, -f5 | sed -e 's/ //g' | paste -sd ',') defrag

클러스터 상태를 확인합니다.

#bash
etcdctl -w table endpoint status --cluster
#출력결과
+----------------------------+------------------+---------+---------+-----------+-----------+------------+
|          ENDPOINT          |        ID        | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
+----------------------------+------------------+---------+---------+-----------+-----------+------------+
| https://192.168.0.221:2379 | 1806ccfb80e73faf |  3.3.15 |  7.8 MB |     false |       602 |   66877835 |
| https://192.168.0.222:2379 | e7c82e12168d0897 |  3.3.15 |  7.8 MB |     false |       602 |   66877835 |
| https://192.168.0.220:2379 | edabb0b65fe02a4c |  3.3.15 |  7.8 MB |      true |       602 |   66877835 |
+----------------------------+------------------+---------+---------+-----------+-----------+------------+

경보를 해제하고 확인합니다.

#bash
etcdctl alarm disarm
etcdctl alarm list
반응형