Apache Kafka 공식 지원 릴리스의 binary archive·asc·sha512와 KEYS
EVENT STREAMING
Kafka KRaft 단일·다중 노드 구성
Apache Kafka 4.3 지원 릴리스를 서명 검증해 설치하고 단일 combined와 운영용 3 controller·분리 broker를 나눠, Cluster ID·스토리지 포맷·PKCS12 mTLS·StandardAuthorizer ACL을 안전하게 구성합니다.BEFORE YOU START
시작 전에 준비하세요
운영 다중 노드용 3개 controller와 별도 broker, 고유 node.id·고정 사설 DNS
각 노드의 빈 전용 metadata.log.dir·log.dirs와 복구 가능한 볼륨
노드별 TLS 인증서·truststore, 별도 break-glass 관리자·모니터 인증서와 비밀 저장소
인증서 DN별 node·관리자·앱·모니터 역할표, 방화벽·복구 콘솔
권장 대상 ZooKeeper 없는 신규 Kafka를 만들면서 단일 개발 구성과 운영 다중 노드의 쿼럼·스토리지·TLS 경계를 정확히 분리하려는 플랫폼 운영자
FOLLOW THE RECIPE
8단계 구성·점검 레시피
단일 개발과 다중 운영 토폴로지 분리
단일 combined 노드는 개발·비핵심 검증에만 사용합니다. 운영은 controller와 broker 역할을 분리하고 3 또는 5 controller로 과반 쿼럼을 구성합니다.
cat /etc/os-release
uname -m
java -versionnproc
free -h
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
df -hT /var/libhostname -f
getent hosts <CONTROLLER_1> <CONTROLLER_2> <CONTROLLER_3>timedatectl status- 단일 combined 모드는 운영 핵심 서비스에 사용하지 않습니다.
- 운영 controller를 3개 이상 홀수로 배치하고 broker 역할과 분리했습니다.
- 모든 node.id·사설 DNS·listener 포트를 중복 없이 자산 기록에 확정했습니다.
- metadata와 broker log 볼륨의 백업·복구 목표를 정했습니다.
KRaft는 controller 과반이 살아야 메타데이터 쓰기가 가능합니다. 3 controller는 1대 장애, 5 controller는 2대 장애를 견디지만 broker 복제 계수는 별도 설계입니다.
기존 Kafka·meta.properties·포트와 데이터 경로를 사전 점검합니다.
기존 Cluster ID·스토리지·포트 점검
가장 중요한 중단 조건은 기존 meta.properties입니다. 하나라도 있으면 신규 Cluster ID 생성·스토리지 포맷을 금지하고 기존 클러스터 복구 또는 노드 추가 절차로 전환합니다.
systemctl status kafka.service --no-pager
pgrep -a -u kafka javasudo ss -lntup | grep -E ':(9092|9093)[[:space:]]'sudo find /var/lib/kafka -name meta.properties -type f -print한 개라도 나오면 신규 포맷 절차를 즉시 중단합니다. 파일 내용은 아직 바꾸지 않습니다.sudo find /var/lib/kafka -maxdepth 3 -type d -printf '%U %G %m %p
'findmnt -T /var/lib/kafka
df -hT /var/lib/kafka
df -i /var/lib/kafka- 모든 신규 대상 경로에 meta.properties가 없음을 노드별로 확인했습니다.
- 기존 클러스터 노드는 신규 설치 절차에서 제외했습니다.
- metadata와 broker log가 임시·공유 충돌 경로가 아닙니다.
- 9092·9093의 기존 소유 프로세스가 없습니다.
meta.properties가 있는 경로를 비우거나 다른 Cluster ID로 다시 포맷하면 기존 클러스터와 데이터가 분리·손상될 수 있습니다. 자동 복구 명령을 실행하지 않습니다.
공식 KEYS·서명·SHA-512를 검증한 아카이브만 배치합니다.
공식 서명 검증과 전용 계정 설치
Kafka Downloads에서 같은 지원 릴리스의 binary archive, asc, sha512와 KEYS를 받습니다. KEYS의 서명자 지문을 공식 Apache 배포 절차와 대조한 뒤 서명과 해시를 모두 검증합니다.
ls -lh kafka_2.13-<KAFKA_VERSION>.tgz kafka_2.13-<KAFKA_VERSION>.tgz.asc kafka_2.13-<KAFKA_VERSION>.tgz.sha512 KEYSmkdir -p -m 0700 ./kafka-gpg-verify
gpg --homedir ./kafka-gpg-verify --import ./KEYSgpg --homedir ./kafka-gpg-verify --verify kafka_2.13-<KAFKA_VERSION>.tgz.asc kafka_2.13-<KAFKA_VERSION>.tgzsha512sum -c kafka_2.13-<KAFKA_VERSION>.tgz.sha512sudo useradd --system --home-dir /var/lib/kafka --create-home --shell /usr/sbin/nologin kafka
sudo useradd --system --home-dir /var/lib/kafka-monitor --create-home --shell /usr/sbin/nologin kafka-monitor계정이 이미 있으면 UID·그룹·홈·셸을 확인하고 다시 만들지 않습니다. 두 계정을 서로의 그룹에 넣지 않습니다.sudo install -d -m 0755 -o root -g root /opt/kafka /etc/kafka
sudo install -d -m 0750 -o kafka -g kafka /var/lib/kafka/meta /var/lib/kafka/logs /var/log/kafkasudo test -z "$(sudo find /opt/kafka -mindepth 1 -maxdepth 1 -print -quit)"sudo tar --no-overwrite-dir --strip-components=1 -xzf kafka_2.13-<KAFKA_VERSION>.tgz -C /opt/kafka
sudo chown -R root:root /opt/kafka- Downloads의 지원 릴리스를 선택하고 asc·SHA-512를 모두 검증했습니다.
- 기존 /opt/kafka 또는 데이터 경로를 덮어쓰지 않았습니다.
- kafka 계정은 로그인할 수 없고 데이터·로그 경로만 씁니다.
- kafka-monitor 계정은 Kafka 서비스·관리자 그룹과 분리했습니다.
- 모든 노드에 같은 Kafka 패치 버전과 호환 JDK를 설치했습니다.
Good signature만 보고 끝내지 않고 KEYS 지문 출처와 SHA-512까지 확인합니다. 검증 실패 파일을 실행하거나 미러를 임의로 바꾸지 않습니다.
단일·다중 설정 파일, TLS와 systemd 실행 경계를 구성합니다.
역할별 KRaft·TLS·systemd 구성
단일 개발은 server.properties, 운영은 controller.properties와 broker.properties를 분리합니다. 각 노드의 node.id는 고유해야 하고 모든 노드는 동일한 controller.quorum.bootstrap.servers 목록을 사용합니다.
sudo cp --archive --no-clobber /etc/kafka/server.properties /etc/kafka/server.properties.<BACKUP_SUFFIX>.bak기존 파일이 있을 때만 실행합니다.sudoedit /etc/kafka/server.propertiessudo cp --archive --no-clobber /etc/kafka/controller.properties /etc/kafka/controller.properties.<BACKUP_SUFFIX>.bak운영 다중 노드에서 기존 파일이 있을 때만 실행합니다.sudoedit /etc/kafka/controller.propertiessudo cp --archive --no-clobber /etc/kafka/broker.properties /etc/kafka/broker.properties.<BACKUP_SUFFIX>.bak운영 broker에서 기존 파일이 있을 때만 실행합니다.sudoedit /etc/kafka/broker.propertiessudoedit /etc/kafka/secrets.propertieskeystore·truststore 비밀번호를 대화형 편집기에 입력하고 화면·클립보드 기록을 지웁니다.sudo install -d -m 0750 -o root -g kafka /etc/kafka/tls
sudo stat -c '%U %G %a %n' /etc/kafka/tls/node.p12 /etc/kafka/tls/truststore.p12노드마다 고유한 node.p12와 신뢰 저장소를 배치합니다. node 인증서를 관리·앱 CLI에 재사용하지 않습니다.sudo install -d -m 0700 -o root -g root /root/kafka-admin
sudo stat -c '%U %G %a %n' /root/kafka-admin/admin-client.p12 /root/kafka-admin/truststore.p12별도 break-glass 관리자 인증서와 truststore를 사전 발급·배치한 뒤 실행합니다.sudoedit /root/kafka-admin/client.properties
sudoedit /root/kafka-admin/secrets.propertiesroot만 읽는 파일에 관리자 인증서 비밀번호를 넣고 일상 모니터링에는 사용하지 않습니다.sudo install -d -m 0750 -o root -g kafka-monitor /etc/kafka/monitor
sudoedit /etc/kafka/monitor/client.properties
sudoedit /etc/kafka/monitor/secrets.properties
sudo stat -c '%U %G %a %n' /etc/kafka/monitor/monitor-client.p12 /etc/kafka/monitor/truststore.p12별도 모니터 인증서를 사용하며 아직 ACL을 부여하기 전에는 접속이 거부되는 것이 정상입니다.sudo chown root:kafka /etc/kafka/<ROLE_CONFIG>.properties /etc/kafka/secrets.properties /etc/kafka/tls/node.p12 /etc/kafka/tls/truststore.p12
sudo chmod 640 /etc/kafka/<ROLE_CONFIG>.properties /etc/kafka/secrets.properties /etc/kafka/tls/node.p12 /etc/kafka/tls/truststore.p12
sudo chown root:root /root/kafka-admin/admin-client.p12 /root/kafka-admin/truststore.p12 /root/kafka-admin/client.properties /root/kafka-admin/secrets.properties
sudo chmod 600 /root/kafka-admin/admin-client.p12 /root/kafka-admin/truststore.p12 /root/kafka-admin/client.properties /root/kafka-admin/secrets.properties
sudo chown root:kafka-monitor /etc/kafka/monitor/monitor-client.p12 /etc/kafka/monitor/truststore.p12 /etc/kafka/monitor/client.properties /etc/kafka/monitor/secrets.properties
sudo chmod 640 /etc/kafka/monitor/monitor-client.p12 /etc/kafka/monitor/truststore.p12 /etc/kafka/monitor/client.properties /etc/kafka/monitor/secrets.properties<ROLE_CONFIG>는 이 호스트의 server·controller·broker 중 하나입니다. kafka 서비스 계정은 관리자·모니터 파일을 읽을 수 없어야 합니다.sudo cp --archive --no-clobber /etc/systemd/system/kafka.service /etc/systemd/system/kafka.service.<BACKUP_SUFFIX>.bak기존 파일이 있을 때만 실행합니다.sudoedit /etc/systemd/system/kafka.serviceprocess.roles=broker,controller
node.id=1
controller.quorum.bootstrap.servers=<DEV_PRIVATE_HOST>:9093
controller.listener.names=CONTROLLER
listeners=CONTROLLER://<DEV_PRIVATE_HOST>:9093,CLIENT://127.0.0.1:9092
advertised.listeners=CLIENT://127.0.0.1:9092
listener.security.protocol.map=CONTROLLER:SSL,CLIENT:SSL
inter.broker.listener.name=CLIENT
metadata.log.dir=/var/lib/kafka/meta
log.dirs=/var/lib/kafka/logs
auto.create.topics.enable=false
authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer
allow.everyone.if.no.acl.found=false
super.users=User:<DEV_NODE_CERT_DN>;User:<BREAK_GLASS_ADMIN_CERT_DN>
config.providers=file
config.providers.file.class=org.apache.kafka.common.config.provider.FileConfigProvider
config.providers.file.param.allowed.paths=/etc/kafka/secrets.properties
ssl.keystore.type=PKCS12
ssl.keystore.location=/etc/kafka/tls/node.p12
ssl.keystore.password=${file:/etc/kafka/secrets.properties:ssl.keystore.password}
ssl.key.password=${file:/etc/kafka/secrets.properties:ssl.key.password}
ssl.truststore.type=PKCS12
ssl.truststore.location=/etc/kafka/tls/truststore.p12
ssl.truststore.password=${file:/etc/kafka/secrets.properties:ssl.truststore.password}
listener.name.controller.ssl.client.auth=required
listener.name.client.ssl.client.auth=required개발 단일 노드 전용입니다. 운영에는 사용하지 않습니다.process.roles=controller
node.id=<UNIQUE_CONTROLLER_ID>
controller.quorum.bootstrap.servers=<CONTROLLER_1>:9093,<CONTROLLER_2>:9093,<CONTROLLER_3>:9093
controller.listener.names=CONTROLLER
listeners=CONTROLLER://<THIS_CONTROLLER_PRIVATE_HOST>:9093
listener.security.protocol.map=CONTROLLER:SSL
metadata.log.dir=/var/lib/kafka/meta
authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer
allow.everyone.if.no.acl.found=false
super.users=User:<CONTROLLER_1_CERT_DN>;User:<CONTROLLER_2_CERT_DN>;User:<CONTROLLER_3_CERT_DN>;<BROKER_NODE_CERT_DNS_AS_USER_ENTRIES>;User:<BREAK_GLASS_ADMIN_CERT_DN>
config.providers=file
config.providers.file.class=org.apache.kafka.common.config.provider.FileConfigProvider
config.providers.file.param.allowed.paths=/etc/kafka/secrets.properties
ssl.keystore.type=PKCS12
ssl.keystore.location=/etc/kafka/tls/node.p12
ssl.keystore.password=${file:/etc/kafka/secrets.properties:ssl.keystore.password}
ssl.key.password=${file:/etc/kafka/secrets.properties:ssl.key.password}
ssl.truststore.type=PKCS12
ssl.truststore.location=/etc/kafka/tls/truststore.p12
ssl.truststore.password=${file:/etc/kafka/secrets.properties:ssl.truststore.password}
listener.name.controller.ssl.client.auth=required<BROKER_NODE_CERT_DNS_AS_USER_ENTRIES>는 각 broker 인증서의 정확한 DN을 User:<DN> 형식으로 세미콜론 구분해 펼칩니다. app·monitor 인증서는 super.users에 넣지 않습니다.process.roles=broker
node.id=<UNIQUE_BROKER_ID>
controller.quorum.bootstrap.servers=<CONTROLLER_1>:9093,<CONTROLLER_2>:9093,<CONTROLLER_3>:9093
controller.listener.names=CONTROLLER
listeners=CLIENT://<THIS_BROKER_PRIVATE_HOST>:9092
advertised.listeners=CLIENT://<THIS_BROKER_CLIENT_DNS>:9092
listener.security.protocol.map=CONTROLLER:SSL,CLIENT:SSL
inter.broker.listener.name=CLIENT
metadata.log.dir=/var/lib/kafka/meta
log.dirs=/var/lib/kafka/logs
auto.create.topics.enable=false
authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer
allow.everyone.if.no.acl.found=false
super.users=User:<CONTROLLER_1_CERT_DN>;User:<CONTROLLER_2_CERT_DN>;User:<CONTROLLER_3_CERT_DN>;<BROKER_NODE_CERT_DNS_AS_USER_ENTRIES>;User:<BREAK_GLASS_ADMIN_CERT_DN>
config.providers=file
config.providers.file.class=org.apache.kafka.common.config.provider.FileConfigProvider
config.providers.file.param.allowed.paths=/etc/kafka/secrets.properties
ssl.keystore.type=PKCS12
ssl.keystore.location=/etc/kafka/tls/node.p12
ssl.keystore.password=${file:/etc/kafka/secrets.properties:ssl.keystore.password}
ssl.key.password=${file:/etc/kafka/secrets.properties:ssl.key.password}
ssl.truststore.type=PKCS12
ssl.truststore.location=/etc/kafka/tls/truststore.p12
ssl.truststore.password=${file:/etc/kafka/secrets.properties:ssl.truststore.password}
listener.name.client.ssl.client.auth=required모든 노드가 같은 정확한 super.users 목록을 사용하되 app·monitor 인증서는 제외하고 ACL로만 허용합니다.security.protocol=SSL
ssl.endpoint.identification.algorithm=https
ssl.keystore.type=PKCS12
ssl.keystore.location=/root/kafka-admin/admin-client.p12
ssl.keystore.password=${file:/root/kafka-admin/secrets.properties:ssl.keystore.password}
ssl.key.password=${file:/root/kafka-admin/secrets.properties:ssl.key.password}
ssl.truststore.type=PKCS12
ssl.truststore.location=/root/kafka-admin/truststore.p12
ssl.truststore.password=${file:/root/kafka-admin/secrets.properties:ssl.truststore.password}
config.providers=file
config.providers.file.class=org.apache.kafka.common.config.provider.FileConfigProvider
config.providers.file.param.allowed.paths=/root/kafka-admin/secrets.propertiessuper.users에 정확히 일치하는 관리자 인증서 DN을 등록하지만 파일은 root 0600으로 유지하고 초기 ACL·비상 작업에만 사용합니다.security.protocol=SSL
ssl.endpoint.identification.algorithm=https
ssl.keystore.type=PKCS12
ssl.keystore.location=/etc/kafka/monitor/monitor-client.p12
ssl.keystore.password=${file:/etc/kafka/monitor/secrets.properties:ssl.keystore.password}
ssl.key.password=${file:/etc/kafka/monitor/secrets.properties:ssl.key.password}
ssl.truststore.type=PKCS12
ssl.truststore.location=/etc/kafka/monitor/truststore.p12
ssl.truststore.password=${file:/etc/kafka/monitor/secrets.properties:ssl.truststore.password}
config.providers=file
config.providers.file.class=org.apache.kafka.common.config.provider.FileConfigProvider
config.providers.file.param.allowed.paths=/etc/kafka/monitor/secrets.propertiesmonitor 인증서 DN은 super.users에 넣지 않고 다음 보안 단계에서 Describe만 허용합니다.[Unit]
Description=Apache Kafka KRaft
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=kafka
Group=kafka
Environment="KAFKA_HEAP_OPTS=-Xms1g -Xmx1g"
ExecStartPre=/usr/bin/test -f /var/lib/kafka/meta/meta.properties
ExecStart=/opt/kafka/bin/kafka-server-start.sh /etc/kafka/<ROLE_CONFIG>.properties
ExecStop=/bin/kill -TERM $MAINPID
Restart=on-failure
RestartSec=10
LimitNOFILE=100000
NoNewPrivileges=true
TimeoutStopSec=180
[Install]
WantedBy=multi-user.target<ROLE_CONFIG>는 단일 server, 운영 controller 또는 broker 중 이 노드 역할 하나로 바꿉니다.- 단일 개발과 운영 다중 노드 설정을 같은 클러스터에 섞지 않았습니다.
- 운영 controller는 process.roles=controller, broker는 process.roles=broker로 분리했습니다.
- 모든 node.id가 고유하고 controller bootstrap 주소가 전 노드에서 동일합니다.
- CLIENT·CONTROLLER listener가 TLS이고 인증서 hostname과 advertised listener가 일치합니다.
- PKCS12 형식을 노드·관리자·모니터 설정에 명시했습니다.
- node·break-glass 관리자·모니터 인증서와 OS 파일 권한을 서로 분리했습니다.
- 비밀번호는 서버 설정이나 명령 인자에 직접 넣지 않았습니다.
설정 파일이 올바라도 아직 스토리지는 초기화되지 않았습니다. 다음 단계의 비가역 승인 전에는 서비스를 시작하지 않습니다.
기존 meta.properties 부재와 Cluster ID 변경기록을 다시 확인한 뒤 단 한 번의 수동 포맷을 승인합니다.
Cluster ID 단일 생성·분기별 포맷·기동
신규 클러스터에서만 Cluster ID를 한 번 생성해 변경기록에 저장합니다. 포맷은 되돌릴 수 없는 경계입니다. 두 사람이 빈 경로·역할·ID를 대조한 뒤 개발 단일, 최초 운영 controller, 기존 쿼럼에 추가할 broker·controller 중 정확히 한 분기만 실행합니다.
sudo find /var/lib/kafka -name meta.properties -type f -print
sudo -u kafka /opt/kafka/bin/kafka-storage.sh info -c /etc/kafka/<ROLE_CONFIG>.propertiesmeta.properties가 나오거나 info가 기존 포맷을 보고하면 즉시 중단합니다.sudo -u kafka /opt/kafka/bin/kafka-storage.sh random-uuid출력을 운영 변경기록 한 곳에 저장합니다. 각 노드에서 다시 생성하지 않습니다.sudo -u kafka /opt/kafka/bin/kafka-storage.sh random-uuid최초 다중 controller 분기에서 controller마다 한 번씩 생성해 node.id·DNS·directory ID 대응표를 승인합니다. Cluster ID와 혼동하지 않습니다. 개발 단일과 broker 전용 분기에서는 생략합니다.sudo test ! -e /var/lib/kafka/meta/meta.properties && sudo -u kafka /opt/kafka/bin/kafka-storage.sh format --cluster-id <APPROVED_CLUSTER_ID> --standalone --config /etc/kafka/server.properties개발 전용 combined 노드에서만 실행합니다. <APPROVED_CLUSTER_ID>를 변경기록의 값으로 교체하고 운영 다중 노드에서는 실행하지 않습니다.sudo test ! -e /var/lib/kafka/meta/meta.properties && sudo -u kafka /opt/kafka/bin/kafka-storage.sh format --cluster-id <APPROVED_CLUSTER_ID> --initial-controllers "<CONTROLLER_ID_1>@<CONTROLLER_1>:9093:<CONTROLLER_DIRECTORY_ID_1>,<CONTROLLER_ID_2>@<CONTROLLER_2>:9093:<CONTROLLER_DIRECTORY_ID_2>,<CONTROLLER_ID_3>@<CONTROLLER_3>:9093:<CONTROLLER_DIRECTORY_ID_3>" --config /etc/kafka/controller.properties최초 3개 controller에서만 실행하며 세 노드 모두 같은 Cluster ID와 완전히 동일한 initial-controllers 목록을 사용합니다. 각 노드의 node.id와 대응하는 directory ID를 먼저 대조합니다.sudo test ! -e /var/lib/kafka/meta/meta.properties && sudo -u kafka /opt/kafka/bin/kafka-storage.sh format --cluster-id <EXISTING_CLUSTER_ID> --no-initial-controllers --config /etc/kafka/<ROLE_CONFIG>.properties건강한 기존 쿼럼의 ClusterId를 대조한 broker 또는 추가 controller에서만 실행합니다. 추가 controller는 기동·catch-up 뒤 공식 add-controller 절차가 별도로 필요합니다.sudo -u kafka /opt/kafka/bin/kafka-storage.sh info -c /etc/kafka/<ROLE_CONFIG>.properties선택한 한 분기의 포맷 후 실행합니다. 모든 노드에서 같은 Cluster ID와 예상 경로인지 대조합니다.sudo systemd-analyze verify /etc/systemd/system/kafka.servicesudo -u kafka test -r /var/lib/kafka/meta/meta.properties
sudo stat -c '%U %G %a %n' /var/lib/kafka/meta/meta.propertiessudo systemctl daemon-reload
sudo systemctl enable kafka.service
sudo systemctl start kafka.service운영 다중 노드는 controller 과반을 먼저 시작·검증한 뒤 broker를 한 대씩 시작합니다.systemctl status kafka.service --no-pager
sudo journalctl -u kafka.service -n 180 --no-pager- 기존 marker가 없는 신규 클러스터에서만 Cluster ID를 생성했습니다.
- 하나의 Cluster ID를 모든 노드에 동일하게 사용하고 다시 생성하지 않았습니다.
- 단일 모드는 standalone, 다중 초기 controller는 동일 initial-controller 집합을 사용하는 현재 공식 절차로 수동 승인했습니다.
- 다중 초기 controller는 각 노드의 고유 directory ID를 한 번씩 만들고, 모든 노드에서 동일한 node@host:port:directory-id 목록을 대조했습니다.
- 추가 broker·controller는 기존 Cluster ID와 공식 no-initial-controller 절차를 사용했습니다.
- 운영 controller 과반이 정상인 뒤 broker를 한 대씩 시작했습니다.
format은 빈 디렉터리 자동 복구 명령이 아닙니다. 기존 클러스터의 빈 경로나 손실된 metadata를 새 Cluster ID로 포맷하면 committed metadata를 잃을 수 있습니다.
쿼럼·broker API·두 번째 TLS 클라이언트 연결을 검증합니다.
KRaft 쿼럼과 TLS 클라이언트 검증
controller 리더·voter·lag를 확인하고 broker API를 TLS client 설정으로 조회합니다. 단일 개발과 다중 운영의 기대 voter 수를 다르게 판정합니다.
sudo /opt/kafka/bin/kafka-features.sh --command-config /root/kafka-admin/client.properties --bootstrap-controller <CONTROLLER_1>:9093 describesudo /opt/kafka/bin/kafka-metadata-quorum.sh --command-config /root/kafka-admin/client.properties --bootstrap-controller <CONTROLLER_1>:9093 describe --statussudo /opt/kafka/bin/kafka-metadata-quorum.sh --command-config /root/kafka-admin/client.properties --bootstrap-controller <CONTROLLER_1>:9093 describe --replicationsudo /opt/kafka/bin/kafka-broker-api-versions.sh --command-config /root/kafka-admin/client.properties --bootstrap-server <BROKER_1>:9092sudo /opt/kafka/bin/kafka-metadata-quorum.sh --command-config /root/kafka-admin/client.properties --bootstrap-controller <CONTROLLER_2>:9093 describe --status단일 개발 모드에서는 생략합니다.systemctl is-active kafka.service
sudo ss -lntp | grep -E ':(9092|9093)[[:space:]]'- 단일 개발은 voter 1개, 운영은 계획한 3개 이상 voter와 leader를 확인했습니다.
- 모든 controller의 ClusterId가 같고 follower lag가 안정적으로 따라옵니다.
- broker API가 TLS client 설정으로 응답합니다.
- 두 번째 controller bootstrap에서도 같은 ClusterId·leader가 보입니다.
- 테스트 topic의 replication factor·min.insync.replicas를 운영 기준으로 별도 검증했습니다.
쿼럼 상태가 보인다고 broker 데이터 복제가 자동으로 안전한 것은 아닙니다. topic replication factor, ISR, rack awareness와 producer acks를 별도로 점검합니다.
listener 인증·ACL·모니터링과 백업 정책을 강화합니다.
인증서 주체별 최소 ACL·네트워크 강화
StandardAuthorizer의 기본 거부를 유지하고 root 전용 break-glass 인증서로 앱·모니터 주체에 필요한 권한만 부여합니다. controller 9093은 controller·broker 사설망에서만, 9092는 승인된 client CIDR에서만 접근하게 합니다.
sudo ss -lntup | grep -E ':(9092|9093)[[:space:]]'sudo -u kafka keytool -list -v -storetype PKCS12 -keystore /etc/kafka/tls/node.p12비밀번호는 대화형 프롬프트에만 입력하고 출력의 내부 이름은 외부 공유 전 가립니다.sudo /opt/kafka/bin/kafka-acls.sh --bootstrap-server <BROKER_1>:9092 --command-config /root/kafka-admin/client.properties --list초기 빈 클러스터 또는 승인된 기존 ACL과 대조하고, 예상하지 못한 wildcard principal을 발견하면 변경을 중단합니다.sudo /opt/kafka/bin/kafka-acls.sh --bootstrap-server <BROKER_1>:9092 --command-config /root/kafka-admin/client.properties --add --allow-principal 'User:<APP_PRODUCER_CERT_DN>' --operation Write --operation Describe --topic '<APP_TOPIC_PREFIX>' --resource-pattern-type prefixed
sudo /opt/kafka/bin/kafka-acls.sh --bootstrap-server <BROKER_1>:9092 --command-config /root/kafka-admin/client.properties --add --allow-principal 'User:<APP_PRODUCER_CERT_DN>' --operation IdempotentWrite --cluster인증서의 RFC2253 DN과 Kafka가 표시한 principal을 정확히 대조합니다. topic 생성은 별도 관리자 변경으로 수행합니다.sudo /opt/kafka/bin/kafka-acls.sh --bootstrap-server <BROKER_1>:9092 --command-config /root/kafka-admin/client.properties --add --allow-principal 'User:<APP_CONSUMER_CERT_DN>' --operation Read --operation Describe --topic '<APP_TOPIC_PREFIX>' --group '<APP_GROUP_PREFIX>' --resource-pattern-type prefixedsudo /opt/kafka/bin/kafka-acls.sh --bootstrap-server <BROKER_1>:9092 --command-config /root/kafka-admin/client.properties --add --allow-principal 'User:<MONITOR_CERT_DN>' --operation Describe --cluster
sudo /opt/kafka/bin/kafka-acls.sh --bootstrap-server <BROKER_1>:9092 --command-config /root/kafka-admin/client.properties --add --allow-principal 'User:<MONITOR_CERT_DN>' --operation Describe --operation DescribeConfigs --topic '<APP_TOPIC_PREFIX>' --resource-pattern-type prefixed
sudo /opt/kafka/bin/kafka-acls.sh --bootstrap-server <BROKER_1>:9092 --command-config /root/kafka-admin/client.properties --add --allow-principal 'User:<MONITOR_CERT_DN>' --operation Describe --group '<APP_GROUP_PREFIX>' --resource-pattern-type prefixedTopicCommand의 상세 조회에는 topic DescribeConfigs도 필요합니다. group에는 지원되지 않으므로 topic·group ACL을 반드시 별도 명령으로 적용합니다.sudo /opt/kafka/bin/kafka-acls.sh --bootstrap-server <BROKER_1>:9092 --command-config /root/kafka-admin/client.properties --listsudo -u kafka-monitor /opt/kafka/bin/kafka-broker-api-versions.sh --command-config /etc/kafka/monitor/client.properties --bootstrap-server <BROKER_1>:9092
sudo -u kafka-monitor /opt/kafka/bin/kafka-topics.sh --command-config /etc/kafka/monitor/client.properties --bootstrap-server <BROKER_1>:9092 --describe --topic '<APP_TOPIC_PREFIX>.*'monitor 주체의 produce·consume·ACL 변경과 접두사 밖 topic 조회가 거부되는지도 별도 테스트합니다.sudo -u kafka test ! -r /root/kafka-admin/client.properties
sudo -u kafka test ! -r /etc/kafka/monitor/client.properties
sudo -u kafka-monitor test ! -r /etc/kafka/secrets.properties
sudo stat -c '%U %G %a %n' /etc/kafka/tls /root/kafka-admin /etc/kafka/monitorsudo journalctl -u kafka.service -p warning --since today --no-pager- PLAINTEXT·SASL_PLAINTEXT listener를 공인·공유망에 노출하지 않았습니다.
- controller 9093과 broker 9092의 소스 CIDR을 분리했습니다.
- StandardAuthorizer와 allow.everyone.if.no.acl.found=false가 모든 역할 설정에 있습니다.
- node와 break-glass 관리자만 super.users이며 앱·모니터 주체는 최소 ACL만 가집니다.
- node·관리자·모니터 keystore와 비밀 파일을 서로 다른 OS 권한 경계에 보관합니다.
- 관리자 ACL 목록과 모니터의 허용·거부 동작을 모두 검증했습니다.
- controller lag, under-replicated partition, 디스크·JVM·인증서 만료 경보를 운영합니다.
Kafka ACL을 켠 뒤 super.users나 broker·controller 주체가 잘못되면 클러스터 자체가 통신하지 못할 수 있습니다. 테스트 클러스터와 롤링 적용·두 번째 연결 검증이 필요합니다.
장애 시 재포맷 없이 서비스를 격리하고 설정·데이터를 보존합니다.
서비스 격리와 Cluster ID·데이터 보존
문제가 생기면 producer 신규 쓰기를 중지하고 controller 과반을 함부로 동시에 내리지 않습니다. 해당 노드만 정상 중지하고 meta.properties와 log dirs를 그대로 보존합니다.
sudo /opt/kafka/bin/kafka-metadata-quorum.sh --command-config /root/kafka-admin/client.properties --bootstrap-controller <HEALTHY_CONTROLLER>:9093 describe --statussudo systemctl stop kafka.service
systemctl is-active kafka.service운영 controller는 중지해도 과반이 유지되는지 먼저 확인합니다.sudo cp --preserve=all /etc/kafka/<ROLE_CONFIG>.properties.<BACKUP_SUFFIX>.bak /etc/kafka/<ROLE_CONFIG>.properties기존 설정의 같은 변경 시점 백업이 있을 때만 실행합니다.sudo cp --preserve=all /etc/systemd/system/kafka.service.<BACKUP_SUFFIX>.bak /etc/systemd/system/kafka.service기존 설치 분기에서만 실행합니다.sudo systemctl disable kafka.service최초 설치 롤백 분기에서 실행합니다.sudo mv --no-clobber /etc/systemd/system/kafka.service /etc/systemd/system/kafka.service.disabled.<BACKUP_SUFFIX>최초 설치 분기에서만 실행하며 meta·log·설정·TLS 파일은 삭제하지 않습니다.sudo systemctl daemon-reloadsudo -u kafka /opt/kafka/bin/kafka-storage.sh info -c /etc/kafka/<ROLE_CONFIG>.properties
sudo du -sh /var/lib/kafka/meta /var/lib/kafka/logs- producer 쓰기를 중지하고 영향 topic·partition을 기록했습니다.
- controller 과반을 유지한 채 한 노드만 중지했습니다.
- Cluster ID를 다시 생성하거나 storage format을 재실행하지 않았습니다.
- meta.properties, metadata log, broker log를 삭제·이동하지 않았습니다.
- 기존 설정·유닛만 복원하고 같은 Cluster ID를 확인한 뒤 재시작 여부를 별도 승인했습니다.
빈 디렉터리처럼 보여도 새로 포맷하지 않습니다. 손실 노드 추가·controller 교체는 현재 KRaft membership 절차와 기존 Cluster ID를 사용해야 합니다.
건강한 쿼럼을 기준으로 복구 계획을 세우고 복제 catch-up 확인 후 한 노드씩 재합류합니다.
SECURITY CHECK
운영 전 마지막 보안 점검
- 운영은 combined 모드를 피하고 3개 이상 controller와 broker 역할을 분리한다.
- Cluster ID는 신규 클러스터에서 한 번만 생성하고 모든 노드에 동일하게 사용한다.
- 기존 meta.properties가 있으면 format·재포맷·새 UUID 생성을 금지한다.
- 모든 역할에 StandardAuthorizer·기본 거부와 명시적 PKCS12 형식을 적용한다.
- node·root 전용 break-glass 관리자·별도 모니터·앱 인증서와 파일 권한을 분리한다.
- controller와 client listener에 mTLS·최소 ACL을 적용하고 포트를 사설 CIDR로 제한한다.
- controller 쿼럼과 topic 데이터 복제를 별도 지표·복구 절차로 운영한다.
COMMON ERRORS
자주 막히는 지점
Cluster ID 불일치
- 증상
- 노드가 InconsistentClusterIdException으로 시작하지 않습니다.
- 가능한 원인
- 각 노드에서 UUID를 따로 생성했거나 기존 meta.properties와 다른 ID로 구성했습니다.
- 확인 순서
- 재포맷하지 말고 건강한 클러스터의 ClusterId와 대상 meta.properties·변경기록을 대조합니다.
controller 쿼럼 없음
- 증상
- broker가 controller를 찾지 못하거나 leader가 선출되지 않습니다.
- 가능한 원인
- controller 과반 미기동, DNS·TLS·9093 방화벽 또는 bootstrap 목록 불일치입니다.
- 확인 순서
- 건강한 노드의 metadata-quorum 상태, DNS, 인증서 SAN, 사설 포트 연결을 순서대로 확인합니다.
클라이언트가 연결 후 실패
- 증상
- bootstrap에는 연결되지만 advertised listener 주소에서 실패합니다.
- 가능한 원인
- advertised.listeners가 클라이언트가 해석·접근할 수 없는 이름이거나 인증서 SAN과 다릅니다.
- 확인 순서
- broker API와 TLS hostname 검증을 클라이언트 네트워크에서 확인하고 광범위 bind로 우회하지 않습니다.
PRIMARY REFERENCES
공식 문서
- Apache Kafka — 지원 릴리스와 서명 다운로드
- Apache Kafka 4.3 — KRaft 구성과 프로비저닝
- Apache Kafka 4.3 — Broker 필수 설정
- Apache Kafka 4.3 — TLS·인증·권한 보안
- Apache Kafka 4.3 — KRaft 권한 부여와 ACL
- Apache Kafka 4.3 — Configuration Providers
설치 저장소와 지원 버전은 바뀔 수 있습니다. 검토일 이후에는 링크된 공식 문서와 현재 서버의 패키지 후보 버전을 함께 확인하세요.