Engineering Note

Strimzi를 안전하게 확장하는 Kafka 이미지는 무엇을 지켜야 할까

Pletor Kafka 이미지를 사례로 Strimzi의 실행 스크립트·수명주기·JMX Exporter 계약을 보존하면서 node-metrics-agent를 추가하는 방법을 설명합니다.

2026년 8월 24일 · Pletor Engineering kafkastrimzikubernetesmonitoringobservabilitycontainers

Strimzi 환경에서 사용자 이미지를 만든다고 해서 Operator의 역할을 다시 구현해서는 안 됩니다. Strimzi는 리스너, 인증서, Secret, 저장소, 롤링 업데이트 같은 복잡한 수명주기를 관리합니다. 안전한 확장은 이 경계를 보존하면서 필요한 기능만 더하는 일입니다.

이 글에서는 pletorco/kafka를 사례로 사용합니다. 이 이미지는 공식 Strimzi 실행 이미지를 다이제스트로 고정해 기반으로 삼고, Pletor node-metrics-agent만 추가합니다. Strimzi의 시작점·Kafka/Connect/MM2 실행 스크립트·내장 Prometheus JMX Exporter는 교체하지 않습니다.

목표는 이미지 하나를 소개하는 데 있지 않습니다. 어떤 기능이 JVM 안에 있어야 하는지, 어떤 책임을 Strimzi에 남겨야 하는지, 추가 레이어를 어떻게 감사할지를 구분하는 안전한 확장의 기준을 설명합니다. node-metrics-agent는 그 기준을 적용한 사례입니다.

Strimzi의 Kafka, KafkaConnect, KafkaMirrorMaker2 리소스가 공통 Pletor Kafka 이미지를 사용하고, 이미지 안의 node-metrics-agent가 JMX MBean을 등록하면 Strimzi JMX Exporter가 이를 Prometheus 메트릭으로 노출하는 구조도
안전한 확장은 JVM 안에 필요한 기능만 더하고, 메트릭 노출과 Pod 수명주기는 기존 Strimzi 경로를 따른다.

호환성을 지키는 세 가지 경계

호환 이미지는 Kafka 배포판이나 별도 Operator가 아닙니다. Strimzi가 기대하는 실행 이미지에 관측 기능을 얇게 더할 뿐입니다. 다음 경계를 지키면 기능을 추가해도 Operator의 운영 모델을 바꾸지 않습니다.

책임담당이 이미지가 하는 일
Pod 생성·설정·갱신Strimzi Operator와 CR바꾸지 않습니다. 리스너, KRaft, Secret, 인증서, 저장소, 롤링 업데이트는 계속 Strimzi가 관리합니다.
컨테이너 안 메트릭 수집node-metrics-agent같은 JVM에 붙어 프로세스·cgroup·파일시스템 등 컨테이너에서 보이는 신호를 co.pletor.* JMX MBean으로 등록합니다.
Prometheus 노출Strimzi 기반 이미지의 JMX Exporter기존 metricsConfig가 가리키는 ConfigMap 규칙으로 JMX MBean을 pletor_* 메트릭으로 바꿉니다.

node-metrics-agent 자체는 HTTP 엔드포인트를 열지 않습니다. 이 점이 핵심입니다. 에이전트는 JMX MBean만 등록하고, Prometheus 엔드포인트는 Strimzi 이미지에 이미 들어 있는 JMX Exporter가 계속 맡습니다. 별도의 JMX Exporter JAR를 한 번 더 넣거나, 에이전트 전용 포트를 새로 열지 않습니다.

따라서 기존 Kafka·Connect 메트릭이 사라지고 별도 메트릭으로 대체되는 것이 아닙니다. 기존 JMX MBean을 변환하던 Exporter에 co.pletor.* MBean을 변환하는 규칙을 추가하는 방식입니다. 하나의 metricsConfig와 하나의 :9404 엔드포인트에서 기존 kafka_*·connect_* 계열과 새 pletor_* 계열을 함께 수집합니다.

왜 사이드카만으로는 같은 메트릭을 얻기 어려운가

사이드카는 유용한 배포 방식이지만, 이 수집 목표에는 맞지 않습니다. node-metrics-agent가 확인하려는 대상은 Kafka 프로세스가 실행되는 같은 JVM과 같은 컨테이너의 실행 환경입니다. Kafka 컨테이너 자체의 JVM·cgroup·프로세스·파일시스템 메트릭을 정확히 수집한다는 목표라면, 일반적인 사이드카만으로는 불가능합니다. co.pletor.* MBean도 그 JVM 안에 등록해야 기존 JMX Exporter가 바로 읽을 수 있습니다.

사이드카 컨테이너는 Pod의 네트워크는 공유할 수 있어도, 기본적으로 Kafka 컨테이너의 프로세스·/proc/self·파일 디스크립터 제한·컨테이너 파일시스템을 자기 것처럼 보지 못합니다. 따라서 사이드카가 자기 cgroup을 읽으면 Kafka가 아니라 수집기 자신의 CPU·메모리 제한과 사용량을 측정하게 됩니다. Pod 단위 cgroup이나 호스트의 cgroup 계층을 별도로 해석하는 방식은 가능하지만, Kafka 컨테이너 단위 수치를 간단하고 이식성 있게 얻는 방법은 아닙니다.

볼륨은 조금 다릅니다. Kafka가 쓰는 PVC를 사이드카에도 같은 볼륨으로 마운트하면, 그 공유 볼륨 자체의 용량과 남은 공간은 읽을 수 있습니다. 그러나 Kafka 컨테이너의 이미지 파일시스템이나 사이드카에 마운트하지 않은 경로까지 보이는 것은 아니며, 볼륨을 하나 더 마운트하는 설정과 권한 검토가 필요합니다. Kafka JVM의 MBean까지 읽으려면 여기에 원격 JMX 엔드포인트와 인증을 추가로 열거나, 프로세스 네임스페이스·경로·재시작 순서를 별도로 설계해야 합니다.

원격 JMX, 호스트 수준 수집기, 공유 프로세스 네임스페이스와 cgroup 접근 같은 장치를 조합하면 일부 경계를 우회할 수 있습니다. 그러나 그것은 더 이상 일반적인 사이드카만으로 수집하는 방식이 아니며, Strimzi가 관리하는 Pod에 별도 권한·연결 지점·수집 경로를 추가하는 설계입니다.

더 직접적인 문제는 Strimzi가 사이드카를 CR 하나로 관리하는 일반 확장 지점을 제공하지 않는다는 점입니다. template은 Pod의 메타데이터·보안·볼륨과 Strimzi가 생성하는 Kafka/Connect 컨테이너의 일부 설정을 조정할 수 있지만, 임의의 추가 컨테이너 목록을 선언하는 일반 PodSpec은 아닙니다. Operator가 관리하는 Pod를 직접 수정해도 다음 조정 과정에서 되돌아갑니다. admission webhook으로 사이드카를 주입하거나 별도 컨트롤러를 두는 방법은 가능하지만, Kafka·Connect·MM2 각각의 볼륨 마운트, 리소스, 보안 정책, 롤링 업데이트 호환성을 Strimzi 밖에서 계속 관리해야 합니다.

반면 Java 에이전트는 Strimzi가 원래 시작하는 JVM에 한 번 붙고, 표준 image 속성·기존 metricsConfig·:9404 엔드포인트를 그대로 활용합니다. 이미지 지정과 ConfigMap 참조를 Strimzi CR에 명시할 수 있으므로, 이 이미지가 에이전트 방식을 선택한 이유입니다.

실행 스크립트를 선택하지 않는다

Strimzi의 Kafka, KafkaConnect, KafkaMirrorMaker2는 Kafka 계열의 공통 실행 이미지를 사용하지만, 실제로 실행할 스크립트는 CR 종류에 따라 다릅니다. 공통 이미지가 실행 스크립트를 고르거나 Connect를 흉내 내면 호환성 계약이 깨지기 쉽습니다.

pletorco/kafka는 이 역할을 가져오지 않습니다. 공식 이미지의 실행 스크립트를 그대로 두고 KAFKA_OPTS에 Java 에이전트 하나를 추가합니다.

KAFKA_OPTS=-javaagent:/opt/pletor/agents/node-metrics-agent-0.8.0-all.jar=/opt/pletor/config/node-metrics.yml

그 결과 어떤 JVM이 시작되든 에이전트는 같은 방식으로 붙습니다. Kafka 브로커 JVM에는 브로커의 실행 환경 신호가, Kafka Connect와 MirrorMaker 2 JVM에는 Connect worker의 실행 환경 신호가 등록됩니다. MirrorMaker 2는 Connect 런타임 위에서 동작하므로 Connect용 JMX 규칙을 사용합니다.

이미지는 다음 파일만 추가합니다.

/opt/pletor/agents/node-metrics-agent-<version>-all.jar
/opt/pletor/config/node-metrics.yml
/opt/pletor/share/node-metrics/{kafka,connect,mm2}.yml
/opt/pletor/share/jmx/{kafka,connect,mm2}.yml

추가 레이어는 감사할 수 있어야 한다

이런 이미지는 무엇을 추가하는지 확인할 수 있어야 합니다. 공개 Dockerfile로 Strimzi 기반 이미지 위에 놓이는 파일과 JVM 옵션을 확인하고, 공개된 node-metrics-agent 소스로 에이전트의 MBean 등록과 동작 범위를 검토할 수 있습니다. 운영에서는 정확한 이미지 매니페스트 다이제스트, 에이전트 JAR의 SHA-256, SBOM·provenance·서명을 함께 대조하면 Dockerfile에 적힌 추가 레이어와 실제 배포물을 연결할 수 있습니다.

이 과정이 모든 결함이나 악의적인 동작이 없음을 절대적으로 증명하지는 않습니다. 다만 이미지가 무엇을 더하는지와 그 의존성이 어디서 왔는지를 독립적으로 감사할 수 있게 합니다.

반대로 실행 스크립트, 시작점, UID 1001, 그리고 JMX Exporter는 Strimzi 기반 이미지의 것을 유지합니다. Strimzi도 CR의 image 속성으로 호환 가능한 사용자 이미지를 지정할 수 있다고 설명합니다. 호환되지 않는 이미지는 정상 동작하지 않을 수 있으므로, Strimzi 실행 계약을 보존하는 일이 이미지의 첫 번째 조건입니다. Strimzi의 이미지 설정 문서도 이 점을 명시합니다.

기존 메트릭 경로를 분리하지 않는다

에이전트가 읽는 범위는 JVM 내부 메트릭만이 아닙니다. 컨테이너 안에서 보이는 CPU·메모리·cgroup 메모리·파일시스템·I/O·파일 디스크립터와 에이전트 자체의 큐 압력·지연 상태를 JMX MBean으로 등록합니다. 자세한 MBean 목록과 장애가 있어도 JVM 시작을 막지 않는 fail-open 동작은 node-metrics-agent 저장소에서 확인할 수 있습니다.

Strimzi의 metricsConfig는 JMX Exporter의 ConfigMap을 참조합니다. 그 ConfigMap에 Kafka 또는 Connect 기존 규칙과 co.pletor.* 규칙을 함께 넣으면, JMX Exporter가 같은 포트 9404에서 기존 Kafka·Connect 메트릭과 pletor_* 메트릭을 함께 노출합니다. Strimzi의 현재 문서는 metricsConfig가 없으면 Prometheus 메트릭도 활성화되지 않으며, JMX Prometheus Exporter와 Strimzi Metrics Reporter 중 하나를 선택한다고 설명합니다. Strimzi metricsConfig 참조

다음은 Kafka용 ConfigMap에 추가하는 부분입니다. 이것만으로 완전한 metrics 설정 파일이 되는 것은 아닙니다. 기존 Kafka JMX 규칙을 유지한 채 includeObjectNamesco.pletor.* 규칙을 합쳐야 합니다.

includeObjectNames:
  - kafka.*:*
  - co.pletor.cgroup:*
  - co.pletor.node:*
  - co.pletor.proc:*
  - co.pletor.agent:*

rules:
  - pattern: 'co.pletor.cgroup<type=(.+?)(?:,(.+))?><>(.+):'
    name: pletor_cgroup_$1_$3
    type: GAUGE
    cache: true
  - pattern: 'co.pletor.node<type=FsMetrics, path=(.+)><>(.+):'
    name: pletor_node_fsmetrics_$2
    labels:
      path: '$1'
    type: GAUGE
    cache: true
  - pattern: 'co.pletor.node<type=(.+?)(?:,(.+))?><>(.+):'
    name: pletor_node_$1_$3
    type: GAUGE
    cache: true
  - pattern: 'co.pletor.proc<type=(.+?)(?:,(.+))?><>(.+):'
    name: pletor_proc_$1_$3
    type: GAUGE
    cache: true
  - pattern: 'co.pletor.agent<type=(.+?)(?:,(.+))?><>(.+):'
    name: pletor_agent_$1_$3
    type: GAUGE
    cache: true

Kafka Connect와 MirrorMaker 2에는 Connect의 기존 규칙을 유지하고 같은 co.pletor.* 부분을 추가합니다. 이미지의 /opt/pletor/share/jmx/ 파일은 검토할 때 참고할 출발점일 뿐, Operator가 관리하는 ConfigMap을 자동으로 만들거나 덮어쓰지는 않습니다. 이 구분 덕분에 메트릭 이름과 레이블은 GitOps로 관리하면서 이미지는 여러 클러스터에서 재사용할 수 있습니다.

CR에서는 이미지와 설정 참조를 명시한다

Kafka CR에서는 설치한 Strimzi 버전과 Kafka 버전에 맞는 고정 태그를 지정합니다. latest 같은 가변 태그는 사용하지 않습니다. 아래는 완전한 Kafka CR이 아니라, 기존 CR에서 이미지와 메트릭 설정을 지정하는 위치를 보여 주는 예시입니다. 실제 적용 전에는 Docker Hub의 pletorco/kafka 태그와 설치한 Strimzi의 지원 Kafka 버전을 함께 확인해야 합니다.

apiVersion: kafka.strimzi.io/v1
kind: Kafka
metadata:
  name: orders
spec:
  kafka:
    version: 4.3.0
    image: pletorco/kafka:1.1.0-kafka-4.3.0-pletor.1
    metricsConfig:
      type: jmxPrometheusExporter
      valueFrom:
        configMapKeyRef:
          name: orders-kafka-metrics
          key: metrics-config.yml

태그 형식은 다음과 같습니다.

<strimzi-version>-kafka-<kafka-version>-pletor.<image-release>

이 값에는 Strimzi 호환성 축, Kafka 버전, Pletor 이미지 레이어 개정이 모두 들어갑니다. 이미지 공개 파이프라인은 선택한 Strimzi 릴리스의 supported: true Kafka 버전만 골라 linux/amd64와 linux/arm64 매니페스트를 만듭니다. 운영 배포에서는 태그뿐 아니라 매니페스트 다이제스트까지 고정하는 편이 더 안전합니다.

Kafka Connect와 MirrorMaker 2에서도 이미지 위치만 각 CR의 spec.image로 바뀝니다. 세 CR에 같은 이미지 태그를 사용하되, Kafka는 Kafka 규칙을, Connect와 MM2는 Connect 규칙을 가리켜야 합니다.

기본 파일시스템 설정의 범위

공통 이미지의 기본 node-metrics.yml/만 수집 대상으로 둡니다. 어떤 볼륨이 실제 Kafka 로그·Connect 플러그인·애플리케이션 로그를 담는지 알 수 없는 공통 이미지에서 가장 이식성 높은 값이기 때문입니다.

이미지에는 Kafka용 /var/lib/kafka, Connect용 /tmp처럼 실행 대상별 경로를 적은 참고 설정도 들어 있습니다. 그러나 현재 공개 이미지가 Java 에이전트에 넘기는 설정 파일은 /opt/pletor/config/node-metrics.yml로 고정되어 있어, 이 참고 설정이 자동으로 적용되지는 않습니다.

# Kafka용 참고 설정 파일의 내용
fsmetrics_max_partitions: 32
fsmetrics_paths:
  - /var/lib/kafka

이 점은 적용 전에 알아야 할 현재의 제약입니다. Strimzi의 추가 볼륨 마운트는 /mnt 아래에서만 지원하므로, CR 템플릿으로 /opt/pletor/config/node-metrics.yml을 덮어쓰는 방식은 사용하면 안 됩니다. 또한 KAFKA_* 환경 변수는 Strimzi 내부 변수라 CR에서 재정의할 수 없습니다. 특정 데이터 볼륨을 정밀하게 관찰해야 한다면, 해당 설정을 포함하는 호환 파생 이미지를 사용하거나 /mnt 경로의 설정을 선택할 수 있도록 이미지를 개선한 뒤 적용해야 합니다.

어느 경우든 설정 경로는 Pod의 실제 볼륨 마운트와 컨테이너 경로를 기준으로 정해야 합니다. 이 이미지는 Kubernetes 노드 전체의 디스크나 다른 Pod의 파일시스템을 보여 주지 않고, 해당 JVM이 볼 수 있는 실행 환경 신호를 제공합니다. 노드 전체 관측에는 node_exporter·kubelet·cAdvisor 계열 메트릭이 여전히 필요할 수 있습니다.

적용 뒤에는 포트가 아니라 메트릭을 확인한다

JMX Exporter 포트가 열렸다는 사실만으로 에이전트 연동이 끝난 것은 아닙니다. 실제 pletor_* 계열 메트릭이 나오는지 확인해야 합니다.

먼저 한 터미널에서 포트 포워딩을 실행합니다.

pod="$(kubectl get pod \
  -l strimzi.io/cluster=orders \
  -o jsonpath='{.items[0].metadata.name}')"

kubectl port-forward "pod/$pod" 9404:9404

포트 포워딩을 실행한 채 다른 터미널에서 다음을 실행합니다.

curl -fsS http://127.0.0.1:9404/metrics \
  | grep -E '^pletor_(node|cgroup|proc|agent)'

이후에는 다음을 함께 확인합니다.

  • Strimzi 조정(reconciliation)과 Pod 준비 상태(readiness)가 성공하는가?
  • Kafka·Connect·MM2 JVM마다 에이전트가 한 번만 붙는가?
  • 기존 리스너, Secret, CA, 저장소, 커넥터 설정이 그대로 유지되는가?
  • Kafka와 Connect/MM2의 메트릭 ConfigMap이 각각 올바른 규칙을 포함하는가?
  • 메트릭에 pletor_* 신호가 실제로 보이는가?

이미지는 다이제스트로 고정한 Strimzi 기반 이미지와 SHA-256으로 검증한 에이전트 아티팩트로 빌드합니다. 또한 CI에서 Kafka/Connect 실행 스크립트, MM2 플러그인, 기반 JMX Exporter, UID, 에이전트 중복 연결 여부를 검사합니다. 하지만 정적 검사가 실제 Cluster의 저장소 마운트와 권한, Prometheus 수집 설정까지 보장하지는 않습니다. 마지막 검증은 운영과 가까운 Strimzi 환경에서 해야 합니다.

Strimzi에 남겨 둘 책임

관측 기능을 추가한다고 해서 Strimzi의 다른 책임을 가져오지는 않습니다.

  • 리스너, KRaft 노드 풀, 인증·인가, Secret, CA와 인증서는 Strimzi가 관리합니다.
  • 사용자별 설정과 자격 증명, 커넥터 플러그인, VM 설치 도구는 이미지에 넣지 않습니다.
  • node-metrics-agent는 독자적인 HTTP 엔드포인트를 열지 않습니다. metricsConfig와 JMX Exporter 설정이 있어야 Prometheus가 읽을 수 있습니다.
  • 컨테이너 내부에서 보이는 신호는 Kubernetes node 전체 상태와 다릅니다.

Kafka Connect에 커넥터 플러그인이 필요하면 KafkaConnect.spec.build.plugins 또는 승인된 파생 이미지를 사용해야 합니다. Pletor 공통 이미지는 관측 기반을 제공할 뿐, 모든 런타임 확장을 한 이미지에 모으려는 용도가 아닙니다.

Kafka 운영에서는 브로커 지표만 보고 장애를 판단하기 어렵습니다. JVM·cgroup·파일시스템·I/O 신호를 함께 읽어야 저장소 압력이나 실행 환경 제약을 더 빨리 좁힐 수 있습니다. Konduo는 Kafka 같은 운영 대상을 플러그인으로 연결해 상태와 메트릭 근거, 알림 대응을 한 흐름에서 다루도록 설계된 통합 관리 운영 플랫폼입니다. Strimzi 환경에서도 이런 관측 신호를 CR·Pod·실제 저장 경로와 함께 해석하는 것이 중요합니다.

마무리: 안전한 확장은 계약을 보존한다

Strimzi 호환 이미지는 Operator를 대신하는 이미지가 아닙니다. 잘 만든 이미지는 Strimzi가 맡은 실행 스크립트와 수명주기를 보존하면서, 필요한 기능만 JVM에 더합니다.

새 이미지를 검토할 때는 다음 네 가지를 확인하면 됩니다.

  • 시작과 수명주기를 보존하는가? Strimzi의 시작 스크립트, 실행 사용자, 종료와 재시작의 흐름을 바꾸지 않아야 합니다.
  • Operator의 책임을 가져오지 않는가? 리스너, 인증서, Secret, 롤링 업데이트처럼 Strimzi가 관리할 영역을 이미지가 대신하지 않아야 합니다.
  • 기존 관측 경로를 확장하는가? 별도 수집 체계를 늘리기보다 기존 JMX Exporter와 metricsConfig를 통해 새 신호를 노출하는 편이 운영에 유리합니다.
  • 추가 레이어를 추적할 수 있는가? 기반 이미지 digest, 추가 artifact의 버전과 검증값, 공개된 소스와 빌드 정보를 확인할 수 있어야 합니다.

pletorco/kafka는 그 원칙으로 node-metrics-agent를 붙입니다. 에이전트는 MBean을 등록하고, 기존 JMX Exporter는 그 MBean을 Prometheus로 노출하며, Strimzi는 계속 Pod와 Kafka 수명주기를 관리합니다. 실행 스크립트를 교체하지 않고, 별도 수집 경로를 만들지 않으며, 추가 레이어를 감사할 수 있게 하면 Kafka·Connect·MirrorMaker 2의 관측 범위를 넓혀도 Strimzi 운영 모델을 유지할 수 있습니다.

함께 읽기 좋은 글