Etcd 관리
개요
본 문서는 ETCD3와 함께 설치되는 명령줄 도구인 etcdctl
을 이용한 ETCD3 클러스터의 멤버와 엔드포인트 Health 등 상태를 확인하거나 특정 조건을 만족하는 Key들의 값을 조회하고 클러스터의 읽기 / 쓰기 성능을 테스트하는 방법에 대하여 기술한다.
명령줄 도구 etcdctl 설치 확인
ETCD3 클러스터를 관리하기 위한 명령줄 도구인 etcdctl
은 etcd
서버 바이너리와 함께 제공된다. 노드에 etcdctl
이 설치되어 있는지 여부와 버전을 확인할 수 있다.
$ which etcdctl
/usr/local/bin/etcdctl
$ etcdctl version
etcdctl version: 3.5.6
API version: 3.5
etcdctl
의 API에는 v2
와 v3
두 개의 버전이 있으며 etcdctl
실행 시 환경변수 ETCDCTL_API
를 각각 2
혹은 3
으로 설정하여 어떤 버전의 API를 호출할 지 명시할 수 있다.
v3.4.0 이상부터는 v3
버전의 API가 기본으로 사용되며 본 문서에서도 v3
버전의 API를 기준으로 사용법을 서술한다.
공통 옵션
etcdctl
명령어를 실행할 때 지정할 수 있는 공통 옵션에 대하여 설명한다.
--endpoints
:etcdctl
이 접근할 ETCD3 gRPC 서버의 Endpoint URL들을 쉼표,
로 구분된 목록으로 지정한다. 지정되지 않은 경우 실행되는 환경의 localhost127.0.0.1:2379
를 기본 gRPC Endpoint URL로 인지하여 통신을 시도한다.유효하지 않은 Endpoint URL은 아래와 같이 에러를 반환한다.
$ etcdctl member list Error: dial tcp 127.0.0.1:2379: connect: connection refused
-w, --write-out
: 출력값의 Format을 설정하는 옵션으로fields
,json
,protobuf
,simple
,table
을 허용한다.--key
: https 엔드포인트로 접근하려는 경우 TLS 인증에 사용할 Client Key 파일 경로를 지정한다.--cert
: TLS 인증에 사용할 Client 인증서 파일 경로를 지정한다.--cacert
: TLS 인증에 사용할 인증기관 (CA) 인증서 파일 경로를 지정한다.
예시
$ ETCDCTL_API=3 etcdctl --endpoints="https://192.168.0.10:2379,https://192.168.0.11:2379" \
--key="./etcd-client-key.pem" \
--cert="./etcd-client-crt.pem" \
--cacert="./etcd-ca-crt.pem" \
member list
클러스터 상태 조회
클러스터의 멤버 및 Endpoint URL의 목록과 상태를 조회한다.
클러스터 멤버 조회
etcdctl member list
로 클러스터 노드의 이름, Advertise 된 Peer 통신을 위한 URL 목록과 Client 통신을 위한 URL 목록을 확인할 수 있다.
$ etcdctl member list
670b863301943618, started, node1, http://192.168.0.10:2380, http://192.168.0.10:2379, false
7825d7b04510b842, started, node3, http://192.168.0.11:2380, http://192.168.0.11:2379, false
c8245114d55ec576, started, node2, http://192.168.0.12:2380, http://192.168.0.12:2379, false
$ etcdctl member list -w table
+------------------+---------+-------+--------------------------+--------------------------+------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS | IS LEARNER |
+------------------+---------+-------+----------------------------+------------------------+------------+
| 670b863301943618 | started | node1 | http://192.168.0.10:2380 | http://192.168.0.10:2379 | false |
| 7825d7b04510b842 | started | node3 | http://192.168.0.11:2380 | http://192.168.0.11:2379 | false |
| c8245114d55ec576 | started | node2 | http://192.168.0.12:2380 | http://192.168.0.12:2379 | false |
+------------------+---------+-------+----------------------------+------------------------+------------+
ID
: 해당 노드의 고유한 식별자로 ETCD 리더 노드를 선출하는 Raft 알고리즘에 이용된다.STATUS
: 해당 노드가 부팅되어 클러스터의 리더 선출에 성공적으로 참여 하였는지를 나타내는 값으로started
혹은unstarted
값을 가질 수 있다. 아직 시작된 적이 없는 ETCD3 노드의 경우unstarted
값을 가진다.NAME
: 해당 노드의 고유한 이름으로 ETCD3 서버 프로세스 시작 시--name
인자로 지정한 값이다.PEER ADDRS
: 해당 노드의 Advertise 된 Peer 통신을 위한 URL의 목록이다.CLIENT ADDRS
: 해당 노드의 Advertise 된 Client 통신을 위한 URL의 목록이다.IS LEARNER
: 해당 멤버가 ETCD 클러스터로부터 Snapshot 및 WAL 복제 상태를 유지하지만 리더 선출 Quorum 에는 참여하지 않는 Learner 노드인지를 나타낸다.
클러스터 엔드포인트 조회
etcdctl endpoint status
로 클러스터의 모든 멤버 노드들의 접속 URL, 데이터베이스 크기, Leader 여부 및 Raft 정보를 확인할 수 있다.
$ etcdctl endpoint status -w table
+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| http://192.168.0.10:2379 | 670b863301943618 | 3.5.21 | 168 kB | true | false | 6 | 520 | 520 | |
| http://192.168.0.11:2379 | c8245114d55ec576 | 3.5.21 | 168 kB | false | false | 6 | 520 | 520 | |
| http://192.168.0.12:2379 | 7825d7b04510b842 | 3.5.21 | 168 kB | false | false | 6 | 520 | 520 | |
+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
ENDPOINT
: 해당 노드의 Advertise 된 Client 통신을 위한 URL의 목록이다.ID
:ENDPOINT
와 동일하다.VERSION
: 해당 노드에서 구동중인 ETCD3 서버의 버전을 나타낸다.DB SIZE
: 디스크에 저장된 ETCD3 데이터베이스의 크기를 나타낸다.IS LEADER
: 해당 노드가 이 클러스터의 Raft Leader 인지 여부를 나타낸다.IS LEARNER
:IS LEADER
와 동일하다.RAFT TERM
: Raft 알고리즘에 따른 현재 임기 (Term). 새로운 Leader 선출이 이루어질 때마다 값이 1씩 늘어난다.RAFT INDEX
: 해당 노드에 저장된 쓰기 작업의 Log Entry 위치를 나타낸다. Leader 선출 시 참조하며 Raft Index가 가장 높은 (즉 가장 최신 상태를 유지하고 있는) 노드가 Leader 선출 시 우선권을 갖는다.RAFT APPLIED INDEX
: 해당 노드의 로컬 환경 Key-Value Store에 적용되어 (Applied) 읽기 가능한 상태의 Log Entry 위치를 나타낸다. 노드의 CPU 및 디스크 환경에 따라 Raft Applied Index가 Raft Index 보다 작은 값을 갖는 (즉 데이터 쓰기 작업이 지연되는) 상황이 발생할 수 있으며 이 경우 클라이언트 설정에 따라 Data Consistency가 유지되지 않을 수 있다.etcdctl get
플래그--consistency
값에 따라 동작이 달라진다.--consistency=s
(Serializable, default) 인 경우 과거 시점의 데이터를 불러온다.--consistency=l
(Linearizable) 인 경우 질의하는 서버가 최신 Raft Index를 따라잡을 때까지 대기한 후 최신 시점의 데이터를 불러온다.
ERRORS
: 해당 노드의 Endpoint에서 감지된 문제를 메세지 형태로 표현한다.
데이터 조회
ETCD3 클러스터에 저장된 데이터를 조회한다.
Prefix로 조회
etcdctl get
명령어 인자로 --prefix
옵션을 지정해 선행하는 특정 문자열과 매칭되는 Key들을 조회할 수 있다.
$ etcdctl get --prefix "/opensql/opensql/members" -w simple
/opensql/opensql/members/pg-1
{"conn_url":"postgres://192.168.131.12:5432/postgres","api_url":"http://192.168.131.12:8008/patroni","state":"running","role":"replica","version":"4.0.5","proxy_url":"postgres://192.168.131.15:6432/postgres","xlog_location":223510016,"replication_state":"streaming","timeline":3}
/opensql/opensql/members/pg-2
{"conn_url":"postgres://192.168.131.13:5432/postgres","api_url":"http://192.168.131.13:8008/patroni","state":"running","role":"primary","version":"4.0.5","proxy_url":"postgres://192.168.131.15:6432/postgres","xlog_location":223510016,"timeline":3}
/opensql/opensql/members/pg-3
{"conn_url":"postgres://192.168.131.14:5432/postgres","api_url":"http://192.168.131.14:8008/patroni","state":"running","role":"replica","version":"4.0.5","proxy_url":"postgres://192.168.131.15:6432/postgres","xlog_location":223510016,"replication_state":"streaming","timeline":3}
Range로 조회
etcdctl get
명령어 인자로 특정 Key 값 key
와 다른 Key range_end
를 지정하여 Key 인덱스 공간의 두 지점 사이, 정확히는 [key, range_end)
에 해당하는 Key의 목록을 조회할 수 있다.
ETCD3에 저장되는 모든 Key들은 Byte의 배열로 치환되어 Index 공간에 저장되며 대소를 비교하여 색인된다.
aa
<ab
a\xff
<b
range_end
와 일치하는 Key는 포함되지 않는다. (Exclusive)
$ etcdctl get "/opensql/opensql/members/pg-1" "/opensql/opensql/members/pg-3"
/opensql/opensql/members/pg-1
{"conn_url":"postgres://192.168.131.12:5432/postgres","api_url":"http://192.168.131.12:8008/patroni","state":"running","role":"replica","version":"4.0.5","proxy_url":"postgres://192.168.131.15:6432/postgres","xlog_location":223510016,"replication_state":"streaming","timeline":3}
/opensql/opensql/members/pg-2
{"conn_url":"postgres://192.168.131.13:5432/postgres","api_url":"http://192.168.131.13:8008/patroni","state":"running","role":"primary","version":"4.0.5","proxy_url":"postgres://192.168.131.15:6432/postgres","xlog_location":223510016,"timeline":3}
$ etcdctl get "/opensql/opensql/failover" "/opensql/opensql/historz"
/opensql/opensql/failover
{}
/opensql/opensql/history
[[1,223438192,"no recovery target specified","2025-04-18T14:27:03.269756+09:00","pg-1"],[2,223438824,"no recovery target specified","2025-04-21T15:13:37.632010+09:00","pg-2"]]
데이터베이스 유지 보수
ETCD3는 동시성 있는 클라이언트 접근을 제어하며 데이터 일관성을 유지하기 위해 Revision 정보를 기반으로 한 MVCC (Multi-Version Concurrency Control) 를 이용한다. 클러스터가 오랜 기간 유지되며 Patroni Switchover / Failover 등의 동작이 많이 일어난 경우 실제 Patroni에서 ETCD3 클러스터에 저장하는 데이터 사이즈가 작음에도 DB 사이즈가 계속해서 증가하는 문제가 발생할 수 있다.
정리할 대상 Revision 지정
etcdctl compact
명령어로 특정 Revision을 지정해 그 이전 시점의 Revision을 더 이상 참조되지 않는 상태로 정의한다. 해당 동작은 클러스터 단위의 작업이므로 별도의 endpoint를 지정하지 않고 실행할 수 있다.
## Endpoint Status를 확인해 각 노드에서 유지하고 있는 가장 최신 Revision 정보를 가져온다.
$ etcdctl endpoint status -w json | jq | grep 'revision'
"revision": 447871,
"revision": 447871,
"revision": 447871,
## 해당 Revision 이전 시점의 Revision들을 더 이상 참조되지 않는 상태로 지정한다.
$ rev=447871
$ etcdctl compact $rev
compacted revision 447871
데이터 조각모음
etcdctl defrag
명령어로 참조되지 않는 Revision을 DB File에서 삭제해 디스크 공간을 확보한다. 별도 --endpoints
옵션이 없으면 로컬 노드의 디스크 공간만을 확보한다. 클러스터의 모든 Endpoint URL을 --endpoints
옵션으로 지정하면 각 호스트들의 디스크 공간을 모두 확보한다.
$ etcdctl defrag --endpoints "http://192.168.0.10:2379,http://192.168.0.11:2379,http://192.168.0.12:2379"
Finished defragmenting etcd member[http://192.168.0.10:2379]
Finished defragmenting etcd member[http://192.168.0.11:2379]
Finished defragmenting etcd member[http://192.168.0.12:2379]
$ etcdctl endpoint status --endpoints "..."
http://192.168.0.10:2379, 670b863301943618, 3.5.21, 25 kB, true, false, 8, 448000, 448000,
http://192.168.0.11:2379, c8245114d55ec576, 3.5.21, 25 kB, false, false, 8, 448000, 448000,
http://192.168.0.12:2379, 7825d7b04510b842, 3.5.21, 25 kB, false, false, 8, 448000, 448000,
쓰기 성능 테스트
etcdctl check perf
명령어로 1분 동안 클러스터의 쓰기 전체 처리량 (Throughput)을 테스트해볼 수 있다.
$ etcdctl check perf
60 / 60 Booooooooooooooooooooooooooooooooooooooooooooooooooooooom! 100.00% 1m0s
PASS: Throughput is 150 writes/s
PASS: Slowest request took 0.291186s
PASS: Stddev is 0.029467s
PASS
--load
옵션으로 요청하는 클라이언트 수 및 초당 요청 횟수를 다르게 지정할 수 있다.
s
: 50 Clientsm
: 200 Clientsl
: 500 Clientsxl
: 1000 Clients
출력되는 결과에는 PASS / FAIL Criteria가 존재한다.
생성된 요청의 90% 이상 Throughput이 나올 것이다.
모든 요청은 500 ms 안에 처리될 것이다.
요청의 처리에 걸린 시간의 표준 편차 (stddev) 가 100 ms 이하일 것이다.
$ etcdctl check perf
60 / 60 Booooooooooooooooooooooooooooooooooooooooooooooooooooooom! 100.00% 1m0s
PASS: Throughput is 150 writes/s
Slowest request took too long: 0.535645s
PASS: Stddev is 0.079037s
FAIL
$ etcdctl check perf --load="xl"
60 / 60 Booooooooooooooooooooooooooooooooooooooooooooooooooooooom! 100.00% 1m0s
FAIL: Throughput too low: 3668 writes/s
Slowest request took too long: 0.645609s
Stddev too high: 0.105516s
FAIL
성능 테스트로 가해진 부하가 많은 경우 DB Revision History로 인해 ETCD 데이터베이스 사이즈가 크게 증가할 수 있으므로 compact
및 defrag
작업을 수행할 것을 권장한다.
복구
ETCD3 는 클러스터 노드의 접속 URL, 클러스터 토큰, Listen URL 등 자신을 포함한 클러스터의 모든 구성을 데이터베이스 스냅샷과 같이 관리한다. 이미 구성되어 있는 ETCD3 클러스터의 노드의 환경설정을 변경하여 Key-Value 데이터만 보존한 채로 재시작하는 경우에는 일반적으로 동작하지 않는다.
서비스 중단으로 인해 발생한 장애로 Scale-In이 요구되거나, 새로운 노드를 추가해 Scale-Out을 진행하거나, 호스트의 IPv4 주소가 변경되어 클러스터를 재설정해야 하는 등 이미 구성된 ETCD3 클러스터의 환경 구성을 변경하여 재시작하고자 하는 경우, 명령줄 도구 etcdctl
을 이용해 복구를 진행해야 한다.
스냅샷 확인
복구를 위해서는 가져오고자 하는 Key-Value 데이터가 포함된 ETCD3 노드의 스냅샷 (Snapshot) 이 필요하다. ETCD3의 스냅샷은 관계형 데이터베이스의 데이터 파일에 대응되며, 특정 시점에 ETCD3 클러스터에 저장된 Key-Value 데이터뿐만 아니라 클러스터의 구성 정보 및 상태 (Raft State) 를 같이 저장한다. 파일 시스템에 저장된 ETCD3의 db
파일을 가져오거나, 구동 중인 ETCD3 클러스터로부터 생성할 수 있다.
파일시스템에서 확인
ETCD3 데이터 경로 ETCD_DATA_DIR
하부 경로 member/snap/db
에 위치한다.
$ ls -l $ETCD_DATA_DIR/member/snap/
total 168
-rw-------. 1 opensql opensql 16805888 Apr 18 14:29 db
$ cp $ETCD_DATA_DIR/member/snap/db ./mysnapshot.db
구동중인 ETCD3로부터 생성
명령줄 도구 etcdctl
의 snapshot save
명령어를 이용해 ETCD3 서버로부터 현재 시점의 Snapshot을 생성할 수 있다.
여러 노드로 구성된 클러스터가 동작중이어도
ENDPOINT
인자로는 하나의 ETCD3 서버 URL만 주어져야 한다.
$ ETCDCTL_API=3 etcdctl --endpoints=${ENDPOINT} snapshot save mysnapshot.db
{"level":"info","ts":"2025-04-21T12:18:30.406283+0900","caller":"snapshot/v3_snapshot.go:65","msg":"created temporary db file","path":"mysnapshot.db.part"}
{"level":"info","ts":"2025-04-21T12:18:30.407142+0900","logger":"client","caller":"[email protected]/maintenance.go:212","msg":"opened snapshot stream; downloading"}
{"level":"info","ts":"2025-04-21T12:18:30.407162+0900","caller":"snapshot/v3_snapshot.go:73","msg":"fetching snapshot","endpoint":"192.168.131.12:2379"}
{"level":"info","ts":"2025-04-21T12:18:30.432538+0900","logger":"client","caller":"[email protected]/maintenance.go:220","msg":"completed snapshot read; closing"}
{"level":"info","ts":"2025-04-21T12:18:30.480983+0900","caller":"snapshot/v3_snapshot.go:88","msg":"fetched snapshot","endpoint":"192.168.131.12:2379","size":"168 kB","took":"now"}
{"level":"info","ts":"2025-04-21T12:18:30.481039+0900","caller":"snapshot/v3_snapshot.go:97","msg":"saved","path":"mysnapshot.db"}
Snapshot saved at mysnapshot.db
스냅샷으로부터 데이터 복구
명령줄 도구 etcdctl
의 snapshot restore
명령어를 이용해 Snapshot으로부터 ETCD3 클러스터 데이터를 생성할 수 있다.
ETCD3 클러스터를 초기 구성할 때와 마찬가지로 새롭게 구성할 클러스터의 초기 설정 중 Database Snapshot에 포함되는 인자들이 명령줄 인자로 모두 주어져야 한다.
--name
: 복구할 ETCD3 서버의 노드 이름--initial-cluster
: 클러스터 내 모든 ETCD3 서버의 노드 이름 및 Peer 통신을 위한 Endpoint URL--initial-cluster-token
: 클러스터에 참여하기 위한 Initialize Token--initial-advertise-peer-urls
: 클러스터의 다른 노드에 알릴 이 노드의 Peer 통신을 위한 Endpoint URL
--skip-hash-check
옵션은 복구될 원본 스냅샷의 데이터 무결성을 검증하는 과정을 생략하기 위한 옵션이다.snapshot save
명령어로 구동중인 ETCD3 서버에서 생성한 스냅샷의 경우 데이터 무결성이 유지되므로 해당 옵션 없이도 정상적으로 복구할 수 있다.파일시스템에서 복사해 온 ETCD3 데이터
db
파일의 경우 실행 시점의 클러스터 메타 정보를 포함하므로 해당 옵션 없이는 정상적으로 복구할 수 없다.--skip-hash-check
옵션을 부여해 데이터 무결성 검증을 생략한다.
$ ETCDCTL_API=3 etcdctl snapshot restore ./mysnapshot.db \
--name node3 \
--initial-cluster node1=http://192.168.0.8:2380,node2=http://192.168.0.9:2380,node3=http://192.168.0.10:2380 \
--initial-cluster-token new-etcd-cluster \
--initial-advertise-peer-urls http://192.168.0.10:2380 \
--skip-hash-check \
# ...
Deprecated: Use `etcdutl snapshot restore` instead.
snapshot/v3_snapshot.go:248 restoring snapshot {"path": "member/snap/db", "wal-dir": "node3.etcd/member/wal", "data-dir": "node3.etcd", "snap-dir": "node3.etcd/member/snap", "stack": "go.etcd.io/..." }
membership/store.go:141 Trimming membership information from the backend...
membership/cluster.go:421 added member {"cluster-id": "154dfe96307df6f0", "local-member-id": "0", "added-peer-id": "3dfe6fc7fff49d22", "added-peer-peer-urls": ["http://192.168.0.8:2380"]}
membership/cluster.go:421 added member {"cluster-id": "154dfe96307df6f0", "local-member-id": "0", "added-peer-id": "7f846315e3b9872d", "added-peer-peer-urls": ["http://192.168.0.9:2380"]}
membership/cluster.go:421 added member {"cluster-id": "154dfe96307df6f0", "local-member-id": "0", "added-peer-id": "c0ea9022befd3eaa", "added-peer-peer-urls": ["http://192.168.0.10:2380"]}
snapshot/v3_snapshot.go:269 restored snapshot {"path": "member/snap/db", "wal-dir": "node3.etcd/member/wal", "data-dir": "node3.etcd", "snap-dir": "node3.etcd/member/snap"}
데이터가 성공적으로 복구된 경우
${ETCD_NAME}.etcd
이름의 디렉토리 안에 새로운 클러스터가 구성된다.
$ ls -l
total 0
drwx------. 3 root root 20 Mar 17 14:48 node1.etcd
$ ls -l node1.etcd/
drwx------. 2 root root 246 Mar 17 14:41 snap
drwx------. 2 root root 257 Mar 17 14:41 wal
복구된 데이터 디렉토리를 기반으로 ETCD3 서비스를 재시작한다. 데이터 복구 시 사용한 인자와 서비스 재시작 시 사용하는 인자가 일치해야 한다.
생성된
${ETCD_NAME}.etcd
디렉토리는 기존 ETCD3 데이터 경로 밑의member/
서브디렉토리와 대응된다.
$ rm -rf $ETCD_DATA_DIR/member
$ cp -r node1.etcd $ETCD_DATA_DIR/member
## Service 정의에 참조된 etcd.env 파일 내용 확인
$ vi /etc/etcd/etcd.env
ETCD_NAME=node1
ETCD_INITIAL_CLUSTER=node1=http://192.168.0.8:2380,node2=http://192.168.0.9:2380,node3=http://192.168.0.10:2380
ETCD_INITIAL_CLUSTER_TOKEN=new-etcd-cluster
ETCD_INITIAL_CLUSTER_STATE=new
--initial-cluster-token new-etcd-cluster \
--initial-advertise-peer-urls http://192.168.0.8:2380 \
$ systemctl restart etcd.service
Last updated