블록체인 합의 노드 운영기 — 디스크와 무중단 재시작
API 서버는 한 대가 빠져도 로드밸런서가 나머지로 돌려줍니다. 합의 노드는 그 자리를 대신할 것이 없습니다. “잠깐 내렸다 올리죠”의 무게가 다르고, 운영 습관도 거기서부터 갈립니다.
스마트컨트랙트를 짜는 이야기와도 웹 서버 운영과도 결이 달라 따로 적어둡니다.
클레이튼은 2024년 핀시아와 통합되어 Kaia가 되었습니다. 아래 내용은 클레이튼 시절 CN 운영 기준이고, 개념은 대체로 이어지지만 명령 이름과 경로, 파라미터는 반드시 현행 공식 문서에서 확인하세요.
합의 노드는 무엇이 다른가
체인의 노드는 역할에 따라 나뉩니다. 클레이튼은 이 구분이 뚜렷한 편이었습니다.
| 노드 | 역할 | 외부 노출 |
|---|---|---|
| CN (Consensus Node) | 블록 제안·검증, 합의 참여 | 직접 노출하지 않음 |
| PN (Proxy Node) | CN 앞단에서 트랜잭션을 중계 | CN 쪽으로만 |
| EN (Endpoint Node) | RPC 제공, dApp이 붙는 지점 | 공개 |
여기서 운영 관점의 핵심은 CN을 인터넷에 직접 노출하지 않는다는 것입니다. CN은 합의에 참여하는 주체라서, 여기에 RPC를 열어두면 공격 표면이 곧 체인 참여에 대한 위협이 됩니다. 트랜잭션은 PN이 받아서 넘기고, CN은 합의만 합니다.
그리고 앞에서 말한 차이가 여기서 구체적인 제약이 됩니다.
CN이 내려가 있으면 그 시간 동안 합의에 참여하지 못합니다.
이 글의 나머지는 이 한 줄에서 파생됩니다. 디스크를 미리 정리하는 것도, 정상 종료를 고집하는 것도, 결국 노드를 오래 안 내리기 위한 이야기입니다.
디스크가 먼저 문제가 된다
노드를 오래 돌리면 가장 먼저 손드는 자원은 CPU도 메모리도 아니고 디스크입니다. 그리고 이건 트래픽과 무관하게 단조 증가합니다.
노드가 쌓는 데이터는 크게 둘입니다.
- 블록 데이터 — 블록과 트랜잭션 자체. 체인이 진행되는 만큼 늘어납니다.
- 상태(state) 데이터 — 계정 잔액, 컨트랙트 스토리지 같은 현재 상태를 담은 트라이. 여기가 더 빨리 큽니다.
상태 데이터가 빨리 크는 이유는 트라이가 버전마다 노드를 새로 만들기 때문입니다. 블록마다 상태가 조금씩 바뀌는데, 바뀐 경로에 있는 트라이 노드들이 새로 생기고 예전 것도 그대로 남습니다. 과거 블록 시점의 상태를 조회할 수 있는 건 이 덕분이지만, 대부분의 운영에서 몇 달 전 상태를 조회할 일은 없습니다. 그런데도 디스크는 계속 먹습니다.
증상은 이 순서로 옵니다.
- 디스크 사용률이 서서히 오른다 (몇 달에 걸쳐)
- 남은 용량이 줄면서 볼륨을 키운다 — 해결이 아니라 시간을 사는 선택
- 볼륨이 커질수록 비용이 오르고, 스냅샷·복구 시간도 같이 길어진다
그래서 볼륨을 키우기 전에 지울 수 있는 게 있는지 먼저 봅니다.
# 데이터 디렉터리 안에서 무엇이 자리를 차지하는지
du -sh /var/kcnd/data/* | sort -h
# 증가 속도를 재둔다 — 언제 임계에 닿을지 계산할 수 있다
df -h /var/kcnd/data
증가 속도를 기록해두는 게 생각보다 중요합니다. “80%가 되면 알림”만 걸어두면, 알림이 왔을 때 남은 시간이 얼마인지 모릅니다. 하루에 몇 GB씩 느는지 알고 있으면 여유를 가지고 계획할 수 있습니다.
State Migration이 하는 일
클레이튼에는 State Migration이라는 기능이 있었습니다. 하는 일은 간단합니다.
현재 시점에서 도달 가능한 상태 트라이 노드만 남기고, 더 이상 참조되지 않는 과거 버전 노드를 정리합니다.
가비지 컬렉션과 같은 발상입니다. 최신 상태에서 출발해 참조를 따라가며 살아 있는 노드를 표시하고, 표시되지 않은 것을 버립니다.
대가가 있습니다. 정리된 구간의 과거 상태는 더 이상 조회할 수 없습니다. 특정 과거 블록 시점의 잔액을 묻는 조회나, 오래된 블록을 대상으로 하는 실행 추적이 실패하게 됩니다. CN은 애초에 RPC를 공개하지 않으니 문제가 안 되지만, EN에서는 함부로 돌리면 안 됩니다. dApp이 과거 상태를 조회하고 있을 수 있습니다.
실행 자체는 노드가 켜진 상태에서 시작하고, 백그라운드로 진행됩니다.
# 콘솔에 붙어서 시작
kcn attach --datadir /var/kcnd/data
> debug.startStateMigration()
> debug.stateMigrationStatus()
돌리기 전에 확인할 것들입니다.
- 여유 공간이 있어야 합니다. 정리 과정에서 살아남을 데이터를 새로 쓰는 방식이라, 진행 중에는 오히려 사용량이 늘 수 있습니다. 디스크가 이미 꽉 찬 뒤에 시작하면 도중에 멈춥니다. 꽉 차기 전에 하는 작업입니다.
- 시간이 오래 걸립니다. 상태 크기에 비례하고, 그동안 디스크 I/O가 계속 높습니다. 블록 제안 순서가 도는 시점과 겹치면 지연이 생길 수 있으니 여유 있는 시간대를 고릅니다.
- 한 대씩 합니다. 여러 CN에서 동시에 돌리면 그만큼 체인 전체의 여유가 줄어듭니다.
- 중단 가능한지 확인해둡니다. 진행 상황과 중지 방법을 미리 알고 시작해야, 예상보다 오래 걸릴 때 판단할 수 있습니다.
무중단으로 재시작하기
바이너리 업그레이드든 설정 변경이든, 결국 노드를 내렸다 올려야 하는 때가 옵니다. 합의에 참여하는 노드가 여러 대라면 한 번에 한 대씩 처리하는 게 원칙입니다.
기본 절차는 이렇습니다.
- 대상 노드가 지금 블록을 제안할 차례가 아닌지 확인한다
- 다른 노드들이 정상 동기화 중인지 확인한다 (남은 노드로 합의가 성립하는지)
- 대상 노드를 정지한다 — 강제 종료하지 않는다
- 작업하고 다시 올린다
- 완전히 따라잡을 때까지 기다린다
- 다음 노드로 넘어간다
3번이 가장 중요합니다. kill -9로 내리면 쓰던 중이던 데이터가 깨져서, 올릴 때 복구에 오래 걸리거나 아예 재동기화가 필요해집니다.
재동기화는 몇 시간에서 며칠 단위 작업이라, 몇 초 아끼려다 며칠을 씁니다.
# systemd 로 관리한다면 정지 유예 시간을 넉넉히 준다
# /etc/systemd/system/kcnd.service
[Service]
KillSignal=SIGINT
TimeoutStopSec=300
Restart=on-failure
TimeoutStopSec이 짧으면 systemd가 기다리다 강제 종료합니다. 정상 종료에 필요한 시간보다 넉넉히 잡아야 합니다.
5번의 “따라잡았는지”는 눈으로 보지 말고 확인합니다.
kcn attach --datadir /var/kcnd/data --exec 'klay.blockNumber'
# 다른 노드나 공개 엔드포인트의 블록 번호와 비교
두 값의 차이가 0에 수렴하고 계속 유지되는지까지 봐야 합니다. 한 번 같아졌다가 다시 벌어지면 따라잡는 속도가 블록 생성 속도를 못 따라가는 것이고, 그건 사양이나 디스크 성능 문제입니다.
재동기화는 마지막 수단이다
위에서 “재동기화는 몇 시간에서 며칠”이라고 적었는데, 이 비용을 구체적으로 알고 있어야 판단이 빨라집니다.
노드를 처음부터 다시 채우는 방법은 크게 둘입니다.
하나는 제네시스부터 전부 재실행하는 것입니다. 모든 블록의 트랜잭션을 순서대로 다시 실행해 상태를 재구성합니다. 가장 신뢰할 수 있지만 체인이 오래됐을수록 비현실적으로 오래 걸립니다. 검증 목적이 아니면 운영에서 고를 일은 거의 없습니다.
다른 하나는 체인 데이터 스냅샷에서 복원하는 것입니다. 이미 동기화된 데이터를 통째로 받아 얹고, 거기서부터 최신 블록까지만 따라잡습니다. 운영에서 실제로 쓰는 건 대부분 이쪽입니다. 다만 어디서 받은 데이터인지가 곧 신뢰의 근거가 되므로, 재단이나 검증된 운영 주체가 배포하는 것을 쓰고 체크섬을 확인해야 합니다.
어느 쪽이든 공통으로 붙는 시간이 있습니다. 받은 시점부터 현재까지의 간격을 따라잡는 시간입니다. 스냅샷이 하루 전 것이면 하루치를 재생해야 하고, 그동안 체인은 계속 진행되므로 따라잡는 속도가 블록 생성 속도보다 빨라야 언젠가 만납니다. 디스크가 느리면 이 조건이 안 맞아서 영원히 따라잡지 못하는 상태가 되기도 합니다. 노드에 좋은 디스크를 쓰라는 조언이 반복되는 이유입니다.
그래서 실무적으로는 이렇게 정리됩니다.
- 재동기화에 걸리는 시간을 한 번 재두고 기록해둡니다. 장애 상황에서 “얼마나 걸리나요”에 답할 수 있어야 합니다.
- 정상 종료 절차를 지키는 것이 이 비용을 피하는 유일한 방법입니다. 몇 초를 아끼려고
kill -9를 쓰지 않는 이유가 여기 있습니다. - 스냅샷 확보 경로를 미리 확인해둡니다. 장애가 난 뒤에 어디서 받는지 찾기 시작하면 그 시간도 다운타임에 더해집니다.
# 복원 전에 반드시 무결성을 확인한다
sha256sum -c chaindata.tar.gz.sha256
# 기존 데이터를 지우기 전에 키를 먼저 빼둔다
cp -a /var/kcnd/data/keystore /secure/backup/keystore-$(date +%Y%m%d)
두 번째 명령을 습관으로 만들어두면 좋습니다. 데이터 디렉터리를 비우는 복구 절차에서 키가 같이 날아가는 사고가 가장 아픕니다.
살아 있지만 일을 안 하는 상태
노드 감시에서 가장 중요한 사실은 이것입니다. 프로세스는 멀쩡히 떠 있는데 블록은 안 오르는 상태가 실제로 생깁니다. 프로세스 헬스체크만 걸어두면 이 상태를 장애로 인식하지 못하고, 그동안 합의 참여는 계속 빠집니다.
그래서 일반 서버 지표(CPU, 메모리, 디스크, 네트워크) 위에 체인 고유 지표를 얹습니다.
| 지표 | 왜 보는가 |
|---|---|
| 블록 번호의 증가 | 노드가 실제로 일하고 있는지를 말해주는 유일한 지표 |
| 피어 수 | 0으로 떨어지면 네트워크에서 고립된 것. 블록 정체 직전 신호인 경우가 많음 |
| 다른 노드와의 높이 차이 | 체인 전체가 멈춘 것과 우리만 뒤처진 것은 대응이 완전히 다름 |
| 디스크 여유와 증가율 | 앞서 말한 이유로 상시 지표 |
절대 블록 번호보다 차이를 알림 조건으로 잡는 편이 낫습니다. 절대값으로 걸면 체인이 멈춘 날 모든 노드에서 동시에 알림이 울립니다.
# 간단한 체크 스크립트 예시 — 결과를 익스포터나 알림으로 넘긴다
BLOCK=$(kcn attach --datadir /var/kcnd/data --exec 'klay.blockNumber' 2>/dev/null)
PEERS=$(kcn attach --datadir /var/kcnd/data --exec 'net.peerCount' 2>/dev/null)
if [ -z "$BLOCK" ]; then
echo "node_rpc_up 0"
exit 0
fi
echo "node_rpc_up 1"
echo "node_block_number $BLOCK"
echo "node_peer_count $((PEERS))"
키 관리
CN은 블록에 서명하는 주체라 노드 키가 곧 권한입니다. 여기서 실수하면 되돌릴 수 있는 종류의 사고가 아닙니다.
- 키 파일은 노드 데이터 디렉터리와 분리해서 백업합니다. 데이터 볼륨을 통째로 날리는 복구 절차에 키가 같이 사라지면 안 됩니다.
- 백업본을 어디에 두는지, 누가 접근할 수 있는지를 문서로 남깁니다. 담당자 한 사람의 기억에만 있으면 그 사람이 없는 날 복구가 막힙니다.
- 서버 접근 경로를 좁힙니다. CN에 RPC를 열지 않는 것과 같은 이유로, SSH도 배스천을 거치게 두는 편이 낫습니다.
- 로그에 키 관련 값이 찍히지 않는지 확인합니다.
거버넌스 참여
퍼블릭 체인의 합의 노드를 운영한다는 건 거버넌스에 참여할 의무도 진다는 뜻입니다. 이건 인프라 업무보다 운영 프로세스 쪽에 가깝습니다.
- 파라미터 변경 제안이 올라오면 정해진 기한 안에 투표해야 합니다. 놓치면 참여율 지표에 남습니다.
- 하드포크 일정이 잡히면 그 블록 전에 노드 버전을 올려야 합니다. 늦으면 체인에서 갈라져 나갑니다. 이건 되돌리려면 재동기화가 필요합니다.
- 이상 상황 대응 훈련에 참여합니다. 실제 상황에서 연락 체계와 절차가 도는지 확인하는 자리라, 실무자가 직접 들어가야 의미가 있습니다.
기술적으로 어려운 일은 아닌데 일정에서 빠지기 쉬운 일입니다. 하드포크 블록 높이는 달력에 박아두고, 알림은 담당자 한 사람이 아니라 팀 채널로 가게 걸어두는 편이 안전합니다.
운영 체크리스트
정리하면 이렇습니다.
- CN에 공개 RPC를 열지 않았는가
- 디스크 증가율을 기록하고 있는가 (임계 알림만으로는 남은 시간을 모른다)
- State Migration을 디스크가 꽉 차기 전에 계획했는가
- 정지가
SIGKILL이 아니라 정상 종료로 이뤄지는가 - 재시작을 한 번에 한 대씩 하는가
- 블록 번호 정체를 감지하는 알림이 있는가 (프로세스 감시로는 안 잡힌다)
- 노드 키가 데이터 볼륨과 분리 백업돼 있는가
- 하드포크 일정이 팀 달력에 있는가