두 대의 노드로 구성된 홈 서버에 Kafka를 설치했더니 제한된 자원 때문에 서버가 그냥 다운되거나 Pod에 메모리 제한을 설정해도 OOMKilled 오류가 발생했습니다.
그래서 Pod가 아닌 컨테이너 자체의 메모리 할당을 조정하는 설정 방법을 찾게 되었습니다.
이 글에서는 Kubernetes 환경에서 Helm을 사용하여 Bitnami Kafka를 설치하고, NFS 스토리지 설정을 통해 Volume을 설정하는 방법을 설명합니다.
*Kafka는 Helm으로 쉽게 다운 가능한 Bitnami Kafka를 이용하였습니다.
1. 메모리 최적화 설정
한정된 메모리 환경에서 Kafka의 안정성을 유지하려면 JVM의 힙 메모리를 적절하게 제한해야 합니다.
이를 위해 Bitnami Kafka Helm Chart의 kafka.heapOpts
를 설정하여 JVM 힙 크기를 조정할 수 있습니다.
helm install kafka bitnami/kafka \
--set kafka.heapOpts="-Xms256M -Xmx512M" \
--set podSecurityContext.runAsUser=1001 \
--set podSecurityContext.fsGroup=1001 \
--set containerSecurityContext.runAsUser=1001 \
--set containerSecurityContext.runAsNonRoot=true
위 설정은 JVM이 최소 256MB, 최대 512MB의 메모리를 사용하도록 제한하여 OOMKilled 오류를 방지합니다.
- Xms256M: JVM(Java Virtual Machine)이 시작 시 할당하는 최소 힙 메모리 크기입니다. 여기서는 256MB로 설정되어 JVM이 시작할 때 이 메모리를 즉시 예약합니다.
- Xmx512M: JVM이 사용할 수 있는 최대 힙 메모리 크기입니다. 512MB로 설정하여 이 이상의 메모리는 할당되지 않도록 제한합니다.
Kafka는 Java와 Scala로 작성된 시스템이므로 JVM 위에서 실행되며, 이러한 설정을 통해 JVM 힙 메모리 사용량을 제어하여 서버 메모리를 효율적으로 관리할 수 있습니다.
2. NFS 스토리지 연결
원래는 PVC가 자동으로 생성되어야 하지만, NFS를 사용해서 그런지 Storage가 자동으로 연결되지 않았습니다.
그래서 직접 storageClass를 연결해 주어야 했습니다.
- 현재 사용 가능한 스토리지 클래스를 확인합니다.
kubectl get storageclass
- PVC 설정을 수정하여
storageClassName
필드에 NFS 스토리지 클래스를 추가합니다.
k edit pvc data-kafka-controller-0
k edit pvc data-kafka-controller-1
k edit pvc data-kafka-controller-2
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-kafka-controller-0
# ... 중략
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
volumeMode: Filesystem
storageClassName: nfs-storage # 이 부분 추가: NFS 스토리지 클래스 지정
3. NFS 설정 및 노드 접근 허용
저의 홈서버의 경우 한정된 자원으로 인해 마스터 노드와 워커 노드 모두에서 파드가 실행될 수 있도록 설정되어 있습니다.
하지만 NFS 서버의 설정에서 마스터 노드에 떠 있는 Pod의 경우 NFS 접근이 불가능한 문제가 발생하였습니다.
이를 해결하기 위해 /etc/exports
파일을 확인하고 수정하여 NFS에 마스터 노드 접근을 허용했습니다.
# 파일 내용 확인
cat /etc/exports
# 연결 설정 추가
sudo vi /etc/exports
# 마스터 노드와 워커 노드에 접근 허용
/srv/nfs <마스터_노드_IP>(rw,sync,no_subtree_check,no_root_squash) <워커_노드_IP>(rw,sync,no_subtree_check,no_root_squash)
# 변경 사항 적용
sudo exportfs -ra
4. Kafka 포트 설정 및 Readiness Probe 문제 해결
이후 Kafka 파드가 스케줄링되었으나, Readiness probe
실패로 인해 계속 재시작을 반복하는 문제가 나타났습니다.
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 11m default-scheduler 0/2 nodes are available: pod has unbound immediate PersistentVolumeClaims. preemption: 0/2 nodes are available: 2 Preemption is not helpful for scheduling..
Warning FailedScheduling 8m56s (x2 over 11m) default-scheduler 0/2 nodes are available: pod has unbound immediate PersistentVolumeClaims. preemption: 0/2 nodes are available: 2 Preemption is not helpful for scheduling..
Normal Scheduled 8m51s default-scheduler Successfully assigned kafka/kafka-controller-0 to k8s-worker01
Normal Pulled 8m51s kubelet Container image "docker.io/bitnami/kafka:3.8.0-debian-12-r5" already present on machine
Normal Created 8m51s kubelet Created container kafka-init
Normal Started 8m51s kubelet Started container kafka-init
Warning Unhealthy 5m41s (x3 over 8m41s) kubelet Readiness probe failed: dial tcp 10.244.1.166:9093: connect: connection refused
Normal Pulled 3m55s (x4 over 8m51s) kubelet Container image "docker.io/bitnami/kafka:3.8.0-debian-12-r5" already present on machine
Normal Created 3m55s (x4 over 8m51s) kubelet Created container kafka
Normal Started 3m55s (x4 over 8m51s) kubelet Started container kafka
Warning BackOff 2m31s (x8 over 6m9s) kubelet Back-off restarting failed container kafka in pod kafka-controller-0_kafka(2b09863b-284b-4e9c-8bba-e292980061b4)
이는 Kafka 서버가 포트 9093
에서 연결 요청을 처리하지 못해 발생하는 오류입니다.
이를 해결하기 위해 advertised.listeners
와 listeners
값을 설정하여 Kafka의 통신을 조정해 주었습니다.
helm upgrade kafka bitnami/kafka \
--set kafka.listeners="PLAINTEXT://:9093" \
--set kafka.advertisedListeners="PLAINTEXT://kafka-controller-0.kafka-controller-headless.kafka.svc.cluster.local:9093" \
--set kafka.configurationOverrides.auto.create.topics.enable=true
helm upgrade kafka bitnami/kafka --set kafka.readinessProbe.port=9092
이 설정을 적용하면, Kafka 파드들이 정상적으로 실행되고, 아래와 같이 모두 Running
상태를 확인할 수 있습니다.
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/kafka-controller-0 1/1 Running 1 (29m ago) 3h16m
pod/kafka-controller-1 1/1 Running 1 (19m ago) 3h16m
pod/kafka-controller-2 1/1 Running 1 (15m ago) 3h16m
5. 인증 비활성화 설정
테스트 파드를 만들어서 Kafka에 연결을 시도하니 SASL 인증 정보 오류가 계속 발생했습니다.
2024-10-25 11:50:14,923] INFO [SocketServer listenerType=BROKER, nodeId=0] Failed authentication with /10.244.1.168 (channelId=10.244.1.167:9092-10.244.1.168:41296-7) (Unexpected Kafka request of type METADATA during SASL handshake.) (org.apache.kafka.common.network.Selector)
[2024-10-25 11:50:16,180] INFO [SocketServer listenerType=BROKER, nodeId=0] Failed authentication with /10.244.1.168 (channelId=10.244.1.167:9092-10.244.1.168:41312-8) (Unexpected Kafka request of type METADATA during SASL handshake.) (org.apache.kafka.common.network.Selector)
Kafka 인증을 비활성화하려면 auth.enabled=false
옵션을 사용하여 인증 요구 사항을 비활성화할 수 있다고 합니다.
helm upgrade kafka bitnami/kafka --set auth.enabled=false
이렇게 인증 불필요하게 만들려고 설정을 주어 보았지만, 인증은 계속 요구되었습니다.
해결을 못한 것입니다...
6. Kafka 클라이언트 테스트
다시 처음부터 돌아와 Helm으로 Kafka를 설치하고 터미널에 출력된 메시지를 확인해 보겠습니다.
완료 했을 때 출력된 메시지는 다음과 같았습니다.
Release "kafka" has been upgraded. Happy Helming!
NAME: kafka
LAST DEPLOYED: Fri Oct 25 21:03:04 2024
NAMESPACE: kafka
STATUS: deployed
REVISION: 6
TEST SUITE: None
NOTES:
CHART NAME: kafka
CHART VERSION: 30.1.6
APP VERSION: 3.8.0
** Please be patient while the chart is being deployed **
Kafka can be accessed by consumers via port 9092 on the following DNS name from within your cluster:
kafka.kafka.svc.cluster.local
Each Kafka broker can be accessed by producers via port 9092 on the following DNS name(s) from within your cluster:
kafka-controller-0.kafka-controller-headless.kafka.svc.cluster.local:9092
kafka-controller-1.kafka-controller-headless.kafka.svc.cluster.local:9092
kafka-controller-2.kafka-controller-headless.kafka.svc.cluster.local:9092
The CLIENT listener for Kafka client connections from within your cluster have been configured with the following security settings:
- SASL authentication
To connect a client to your Kafka, you need to create the 'client.properties' configuration files with the content below:
security.protocol=SASL_PLAINTEXT
sasl.mechanism=SCRAM-SHA-256
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required \
username="user1" \
password="$(kubectl get secret kafka-user-passwords --namespace kafka -o jsonpath='{.data.client-passwords}' | base64 -d | cut -d , -f 1)";
To create a pod that you can use as a Kafka client run the following commands:
kubectl run kafka-client --restart='Never' --image docker.io/bitnami/kafka:3.8.0-debian-12-r5 --namespace kafka --command -- sleep infinity
kubectl cp --namespace kafka /path/to/client.properties kafka-client:/tmp/client.properties
kubectl exec --tty -i kafka-client --namespace kafka -- bash
PRODUCER:
kafka-console-producer.sh \
--producer.config /tmp/client.properties \
--bootstrap-server kafka.kafka.svc.cluster.local:9092 \
--topic test
CONSUMER:
kafka-console-consumer.sh \
--consumer.config /tmp/client.properties \
--bootstrap-server kafka.kafka.svc.cluster.local:9092 \
--topic test \
--from-beginning
WARNING: There are "resources" sections in the chart not set. Using "resourcesPreset" is not recommended for production. For production installations, please set the following values according to your workload needs:
- controller.resources
+info https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
다시 읽어보니 설치 완료 후 테스트 방법이 모두 안내되어 있어, 이를 참고하여 다시 진행했습니다.
1. client.properties
파일을 생성합니다.
1.1 Secret 값 추출
kubectl get secret kafka-user-passwords --namespace kafka -o jsonpath='{.data.client-passwords}' | base64 -d | cut -d , -f 1
1.2. client.properties
파일 생성
security.protocol=SASL_PLAINTEXT
sasl.mechanism=SCRAM-SHA-256
sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username="user1" password="<위의 명령어를 통해 나온 값 넣기>";
2. Kafka-client 파드를 만들어서 실행합니다.
kubectl run kafka-client --restart='Never' --image docker.io/bitnami/kafka:3.8.0-debian-12-r5 --namespace kafka --command -- sleep infinity
kubectl cp --namespace kafka /path/to/client.properties kafka-client:/tmp/client.properties
kubectl exec --tty -i kafka-client --namespace kafka -- bash
3. 두 개의 터미널을 이용해서 Producer와 Consumer 생성해 보겠습니다.
- Producer 생성하기 → test라는 Topic이 생성됩니다.
kafka-console-producer.sh \
--producer.config /tmp/client.properties \
--bootstrap-server kafka.kafka.svc.cluster.local:9092 \
--topic test
- Consumer 생성하기
kafka-console-consumer.sh \
--consumer.config /tmp/client.properties \
--bootstrap-server kafka.kafka.svc.cluster.local:9092 \
--topic test \
--from-beginning
왼쪽이 Consumer, 오른쪽이 Producer이며, 통신이 잘 되는 모습을 볼 수 있습니다.
'프로젝트 > 프로젝트 과정' 카테고리의 다른 글
[Rocky Linux9] 한정된 리소스 환경에서 Helm으로 Redis 설치하기 (1) | 2024.10.28 |
---|---|
[Prometheus] Kubernetes 환경에서 Kafka 모니터링 구축하기 (by Kafka Exporter) (0) | 2024.10.26 |
[사이드 프로젝트] 한정 수량 인기 상품 구매 서비스 만들기: 요구 사항 (0) | 2024.09.26 |
GlobalExceptionHandler와 ExceptionHandlingFilter의 차이점 (0) | 2024.09.19 |
[실패 2회차] Ollama를 이용한 코드 리뷰 봇 만들기 (0) | 2024.09.07 |