설정 (환경변수)
서버 설정은 환경변수로만 받습니다. 키는 대소문자를 구분하지 않고, 빈 값은 미설정으로 취급하며, flag는 1/true/yes/on만 참입니다. 범위나 조합이 잘못되면 시작 단계에서 중단합니다.
공통과 자원 상한
| 변수 | 기본값 | 허용 범위 | 내용 |
|---|---|---|---|
SERVER_PORT | 5225 | 유효한 u16 port | single client port. non-Docker cluster는 CLUSTER_SELF, Docker cluster는 내부 5225 사용 |
SOCKET_BIND | 0.0.0.0 | bind 가능한 주소 | client와 Raft listener의 bind host |
DEBUG_MODE | 빌드 설정 | flag | debug log 기본값 |
DOCKER_MODE | false | flag | cluster 내부 bind port를 5225/6225로 고정. 공식 image는 true |
MAX_CONNECTIONS | 1024 | 1..8192 | 동시 client connection. 초과 connection은 즉시 닫음 |
MAX_RAFT_CONNECTIONS | 64 | 1..256 | 동시 Raft/control connection. client와 별도 예약 |
MAX_WAITERS | 2048 | 1..16384 | key당 waiter. 초과 acquire는 B |
MAX_TOTAL_WAITERS | 16384 | MAX_WAITERS..65536 | 전체 waiter. 초과 acquire는 B |
설정으로 바꿀 수 없는 안전 상한도 있습니다.
| 항목 | 값 | 포화 시 동작 |
|---|---|---|
| connection당 in-flight acquire | 64 | 해당 acquire에 B |
| 전체 in-flight acquire | 4096 | 해당 acquire에 B |
| 전체 in-flight release | 512 별도 lane | connection 종료 전까지 bounded 대기 |
| connection당 밀린 reply | 256 | 느린 client connection 종료 |
| coordinator channel | 4096 | sender에 backpressure |
| cluster active key | 65536 | 신규 key acquire에 B |
| Raft frame | 64MiB | RPC 거부 |
| snapshot encoded bytes | 64MiB - 64KiB | build/install 거부 |
| 동시 Raft decode budget | 72MiB | permit이 생길 때까지 bounded 대기 또는 timeout |
기본 profile은 512MiB memory limit에서 시작하는 구성입니다. 최대 snapshot, TLS connection과 learner catch-up이 겹치는 peak RSS는 workload로 다시 확인해야 합니다. hard ceiling을 넘겨 시작할 수 없으며 무제한 값은 없습니다. 전체 in-flight acquire, 신규 key와 전체 waiter admission은 각 hard limit의 90%에서 닫히고 사용량이 75% 미만으로 내려온 뒤 다시 열립니다. 이 high/low watermark는 한계 부근의 accept/reject 진동을 막습니다. wire에는 모두 상관 가능한 B로 보이지만 서버는 key_busy, per_key_limit, connection_limit, global_overload 누적값과 현재 사용량을 분당 통계에 남기고, 원인별 경고는 10초에 한 번으로 제한합니다.
client 인증과 TLS
| 변수 | 기본값 | 내용 |
|---|---|---|
CLIENT_TOKENS | 빈 token 1개 | comma-separated client 인증 token. production은 nonblank secret 권장 |
TLS_CERT / TLS_KEY | 없음 | 함께 설정하는 server certificate chain과 private key PEM 경로 |
TLS_CA | system roots | 이 서버가 다른 peer의 인증서를 검증할 때 쓰는 CA PEM |
TLS_SKIP_VERIFY | false | peer 인증서 chain/hostname 검증 생략. 테스트 전용 |
CLUSTER_ALLOW_PLAINTEXT_PRIVATE | false | TLS 없는 cluster를 명시적으로 허용. 격리된 private network 전용 |
현재 TLS는 server certificate를 검증하는 단방향 TLS이고 peer 권한은 별도 challenge-response token으로 검증합니다. plaintext opt-out은 기밀성이나 인증서 기반 node identity를 제공하지 않습니다.
안정 voter identity
cluster identity 항목은 함께 설정하거나 모두 생략해야 합니다. 생략하면 single 모드입니다.
| 변수 | 조건 | 내용 |
|---|---|---|
CLUSTER_ID | 1~128자의 ASCII 영숫자, ., _, - | fencing domain을 구분하는 이름 |
CLUSTER_NODE_ID | u64 | 이 process의 Raft ID. 종료 뒤 재사용 금지 |
CLUSTER_INCARNATION | non-zero u128 | process마다 외부 start authority가 새로 발급. stale env 재사용 금지 |
CLUSTER_SELF | host:client-port | stable client address slot과 redirect 주소 |
CLUSTER_RAFT_SELF | host:raft-port | 이 process만 쓰는 Raft/control 주소 |
CLUSTER_PEERS | 정확히 3개 또는 5개 | NodeId@incarnation@client-address@raft-address 목록 |
client port와 Raft port는 각각 명시하며 일반적인 +1000 규칙은 없습니다. 단 DOCKER_MODE=true인 공식 image 내부 bind만 5225와 6225로 고정됩니다. 모든 NodeId, incarnation, client address, Raft address는 중복되면 안 되며 자기 identity가 목록과 정확히 일치해야 합니다.
무디스크 서버는 동일 환경변수로 재실행된 process와 원래 process를 로컬에서 구분할 수 없습니다. 따라서 자동 restart를 끄고 외부 operator가 매 process start마다 새 incarnation을 발급해야 합니다. 기존 quorum은 membership과 incarnation이 다른 stale process를 거부합니다. 이 start authority를 지키지 않는 same-NodeId/same-incarnation 재실행은 지원 failure model 밖이며 Raft 안전성을 주장할 수 없습니다.
서버 내부의 startup_admission은 이 외부 불변식을 방어하는 추가 gate입니다. 최초 cluster는 모든 설정 peer가 서로 빈 membership·미사용 bootstrap 상태임을 확인하고 Prepared barrier를 통과해야 Open이 됩니다. replacement는 leader가 one-shot Join을 승인한 뒤에만 Open이 됩니다. Pending이나 Prepared에서는 status/Join 같은 control 요청만 허용하고 Vote, Append, Snapshot, client mutation은 거부합니다. peer unreachable을 빈 상태로 간주하는 fallback은 없습니다. Rejected는 terminal이며 새 NodeId와 incarnation으로 다시 준비해야 합니다.
Raft와 clock
| 변수 | 기본값 | 제약 |
|---|---|---|
CLUSTER_HEARTBEAT_MS | 500 | 10 이상 |
CLUSTER_ELECTION_TIMEOUT_MS | 2000 | 600 이상, heartbeat의 4배 이상 |
CLUSTER_SNAPSHOT_TIMEOUT_MS | 120000 | 1000 이상 |
RAFT_PROGRESS_TIMEOUT_MS | 10000 | 1000 이상 |
CLUSTER_CLOCK_AGENT_ENDPOINT | 없음 | cluster에서 필수인 numeric IP:port |
CLUSTER_CLOCK_AGENT_SECRET | 없음 | cluster에서 필수, 32바이트 이상, 다른 token과 분리 |
CLUSTER_CLOCK_PAIRWISE_DELTA_MS | 20 | 2..=60000, 모든 voter/learner가 동일 |
CLUSTER_CLOCK_MAX_STALENESS_MS | 5000 | sample interval + timeout보다 큼 |
CLUSTER_CLOCK_SAMPLE_INTERVAL_MS | 1000 | 0보다 크고 staleness보다 작음 |
CLUSTER_CLOCK_TIMEOUT_MS | 250 | 0보다 크고 staleness보다 작음 |
CLUSTER_CLOCK_MAX_RTT_MS | 50 | 0보다 크고 timeout 이하 |
CLUSTER_CLOCK_MAX_UNCERTAINTY_MS | 2 | pairwise delta / 2 이하 |
CLUSTER_CLOCK_MAX_SOURCE_AGE_MS | 2000 | 0보다 큼 |
clock endpoint는 인증된 외부 기준시각, uncertainty, sync 여부와 source age를 반환해야 합니다. startup, Grant/Expire 직전 또는 주기 probe에서 인증·freshness·RTT·Δ·wall-step 검증이 실패하면 node는 새 mutation을 멈추고 client를 drain한 뒤 Raft를 종료합니다. 정확한 TCKCLK 2 계약은 clock health를 따릅니다.
cluster와 control 인증
| 변수 | 조건 | 내용 |
|---|---|---|
CLUSTER_TOKENS | cluster에서 nonblank 목록 필수 | 첫 token으로 송신, 목록 전체로 peer 수신 검증 |
CLUSTER_CONTROL_TOKENS | cluster에서 nonblank 목록 필수 | status와 replacement 관리 권한 |
CLUSTER_BOOTSTRAP_CREDENTIAL | 첫 cluster process에만 설정 | 복제 상태에 한 번 기록되는 bootstrap 자격 |
cluster token과 control token은 서로 겹치면 안 됩니다. clock secret, bootstrap credential과 join credential도 다른 인증 domain의 token을 재사용하면 시작을 거부합니다.
leader가 출력하는 join 설정
다음 값은 수동으로 만들지 않습니다. prepare-replacement가 발급한 출력을 그대로 replacement process에 넣습니다.
CLUSTER_JOIN_LEADERCLUSTER_JOIN_LEADER_NODE_IDCLUSTER_JOIN_LEADER_INCARNATIONCLUSTER_JOIN_CREDENTIALCLUSTER_JOIN_SEED_PEERS- 새
CLUSTER_NODE_ID,CLUSTER_INCARNATION,CLUSTER_SELF,CLUSTER_RAFT_SELF,CLUSTER_PEERS
join 관련 다섯 값은 전부 함께 있어야 하고 bootstrap credential과 함께 쓸 수 없습니다. join credential은 one-shot이며 기본 60초, 허용 TTL은 5~300초입니다. old/new process만 같은 stable client slot을 공유하고 Raft address와 incarnation은 반드시 달라야 합니다.
bootstrap과 교체 명령은 무정지 새 NodeId learner 교체에 정리되어 있습니다. peer wire는 CLUSTER_WIRE_VERSION=1 exact match이고 server package version은 호환 판정에 사용하지 않습니다.