KRaft 모드 기준이다. Kafka 4.x 는 KRaft 전용이므로 ZooKeeper 유닛은 필요 없다.
[Unit]
Description=Apache Kafka
Documentation=https://kafka.apache.org/documentation/
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=kafka
Group=kafka
Environment=JAVA_HOME=/usr/lib/jvm/java-21-openjdk
Environment=LOG_DIR=/var/log/kafka
Environment=KAFKA_HEAP_OPTS=-Xms6g -Xmx6g
ExecStart=/opt/kafka/bin/kafka-server-start.sh /opt/kafka/config/server.properties
ExecStop=/opt/kafka/bin/kafka-server-stop.sh
Restart=on-failure
RestartSec=10
TimeoutStopSec=180
LimitNOFILE=100000
[Install]
WantedBy=multi-user.target
Type=simple 을 쓰고 kafka-server-start.sh 를 데몬 옵션 없이 실행한다. -daemon 을 붙이면 스크립트가 백그라운드로 보내고 즉시 반환하므로 systemd 가 프로세스를 잃는다.
TimeoutStopSec 을 넉넉히 둔다. 브로커는 종료할 때 리더십을 넘기고 로그를 정리하므로 시간이 걸린다. 짧으면 SIGKILL 로 강제 종료되어 비정상 종료 복구를 하게 된다.
힙은 KAFKA_HEAP_OPTS 로 준다. Kafka 는 페이지 캐시를 적극적으로 쓰므로 힙을 크게 잡지 않는다. 물리 메모리의 4 분의 1 이하, 대개 6~8 GB 로 두고 나머지는 OS 캐시로 남긴다.
systemctl daemon-reload
systemctl enable --now kafka
systemctl status kafka
journalctl -u kafka -f
LOG_DIR 환경변수가 Kafka 의 서버 로그(server.log, controller.log) 위치를 정한다. 데이터가 쌓이는 곳은 server.properties 의 log.dirs 로 전혀 다른 것이다. 이름이 비슷해 혼동하기 쉽다.
| 항목 | 정하는 곳 | 내용 |
|---|---|---|
| 서버 로그 | LOG_DIR 환경변수 |
server.log · controller.log 등 |
| 데이터 | server.properties 의 log.dirs |
토픽 세그먼트 |
로그 레벨과 롤링 정책은 설정 파일에서 조정한다. Kafka 3.x 까지는 config/log4j.properties, Kafka 4.x 는 log4j2 로 바뀌어 config/log4j2.yaml 을 쓴다. 자동 변환 도구가 제공되지만, 옛 log4j.properties 를 그대로 두면 무시된다.
Configuration:
Properties:
Property:
- name: 'logPattern'
value: '[%d] %p %m (%c)%n'
Appenders:
RollingFile:
- name: KafkaAppender
fileName: '${sys:kafka.logs.dir}/server.log'
filePattern: '${sys:kafka.logs.dir}/server.log.%d{yyyy-MM-dd-HH}'
PatternLayout:
pattern: '${logPattern}'
TimeBasedTriggeringPolicy:
interval: 1
Loggers:
Root:
level: INFO
AppenderRef:
- ref: KafkaAppender
listeners=PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
advertised.listeners=PLAINTEXT://broker01.example.com:9092
controller.listener.names=CONTROLLER
inter.broker.listener.name=PLAINTEXT
listeners 는 바인딩 주소이므로 0.0.0.0 이 맞는다. advertised.listeners 는 클라이언트에게 알려 주는 접속 주소이므로 0.0.0.0 을 쓰면 안 된다. 클라이언트가 그 값을 그대로 받아 접속을 시도하기 때문이다.
컨트롤러 리스너는 advertised.listeners 에 넣지 않는다.
BS=broker01.example.com:9092
kafka-broker-api-versions.sh --bootstrap-server $BS | head -3
kafka-metadata-quorum.sh --bootstrap-server $BS describe --status
kafka-metadata-quorum.sh --bootstrap-server $BS describe --replication
kafka-cluster.sh cluster-id --bootstrap-server $BS
kafka-topics.sh --bootstrap-server $BS --describe --under-replicated-partitions
describe --status 에서 LeaderId 와 각 노드의 LogEndOffset 을 보면 컨트롤러 쿼럼이 건강한지 알 수 있다. 뒤처진 노드가 있으면 그 노드의 로그를 본다.
포트와 프로세스 확인은 다음과 같다.
ss -tlnp | grep -E '9092|9093'
journalctl -u kafka --since '10 min ago' | grep -iE 'error|shutdown'
tail -n 100 /var/log/kafka/server.log