잡동사니
PV사용량 모니터링하기 본문
안녕하세요. yeTi입니다.
오늘은 Kubernetes의 PV 사용량을 모니터링하려고 합니다.
Kubernetes Storage Management Layer
대략적으로 kubernetes의 storage를 관리하는 구조가 어떻게 이뤄져 있는지에 대해 살펴보겠습니다.
위의 그림과 같이 PVC
에 개발자가 사용하고 싶은 Storage의 스펙을 정의합니다.
그러면 Storage 관리자가 PV
를 정의하여 물리 disk를 할당하고 Storage Class
에 저장소의 특성을 정의합니다.
그러면 Provisioner
에 의해서 다양한 Storage에 접근할 수 있고 최종적으로 실제 데이터는 Storage
에 저장됩니다.
여기서 Storage
로 ceph
을 사용하고 있기 때문에 Provisioner
에 rook-ceph
을 넣어놨습니다.
CLI 환경
CLI 환경에서 현황을 확인해보겠습니다.
PV 설정 용량은 다음과 같이 확인할 수 있습니다.
$ kubectl describe pvc -n [namespace] [pvc name] | grep Capacity
Capacity: 5Gi
$ kubectl describe pv [pv name] | grep Capacity
Capacity: 5Gi
PV 사용량은 다음과 같이 확인할 수 있습니다.
$ sudo du -sh /var/lib/kubelet/pods/[Pod UID]/volumes/kubernetes.io~csi/[PV Name]/mount/logs
Container
의 Volume
사용량은 다음과 같이 서비스 특정에 맞게 확인할 수 있습니다. 본 예제에서는 kafka
를 대상으로 진행했습니다.
$ kubectl exec [Pod Name] --container kafka-broker -n [Namespace] -- du -sh /opt/kafka/data/logs
Prometheus 활용
CLI
환경에서도 PV의 상태를 확인할 수 있지만 Prometheus
를 활용하여 GUI환경에서 모니터링하는 환경을 구축할 수 있습니다.
설치되어 있는 Grafana
의 Explore
메뉴에 접근하면 다음과 같이 PromQL로 조회할 수 있는 환경을 제공합니다.
다음 지표들을 활용해 모니터링 환경을 구축할 수 있습니다.
kube_pod_spec_volumes_persistentvolumeclaims_info
:Pod
이 가지고 있는PVC
정보 제공kube_persistentvolumeclaim_info
:PVC
와PV
맵핑 정보kube_persistentvolume_capacity_bytes
:PV
의 설정 용량kafka_log_log_size
:Kafka
의 토픽별 로그 용량
PromQL
간략하게 Prometheus
에서 사용할 수 있는 PromQL
의 예제를 공유하겠습니다.
컬럼 추가
kube_persistentvolumeclaim_info
에서 volumename
컬럼의 데이터들을 persistentvolume
컬럼으로 추가해라
label_replace(kube_persistentvolumeclaim_info{namespace="kafka"}, "persistentvolume", "$0", "volumename", ".+")
Inner Join
kube_persistentvolumeclaim_info
와 kube_persistentvolume_capacity_bytes
을 persistentvolume
로 Inner Join
해서 kube_persistentvolume_capacity_bytes
의 value
를 사용해라
label_replace(kube_persistentvolumeclaim_info{namespace="kafka"}, "persistentvolume", "$0", "volumename", ".+") * on (persistentvolume) kube_persistentvolume_capacity_bytes
해당 쿼리에서 kube_persistentvolumeclaim_info
의 컬럼을 사용하고 싶다면 group_left()
추가
label_replace(kube_persistentvolumeclaim_info{namespace="kafka"}, "persistentvolume", "$0", "volumename", ".+") * on (persistentvolume) group_left() kube_persistentvolume_capacity_bytes
다른 두 metric의 연산
/ on (pod)
을 기준으로 앞/뒤 메트릭을 연산한다. /
연산을 수행하고 pod
컬럼을 기준으로 연산한다.
((sum(kafka_log_log_size{pod=~"kafka-."}) by (pod) / 1024 / 1024 / 1024) + 1.3) / on (pod) (kube_pod_spec_volumes_persistentvolumeclaims_info{namespace="kafka"} * on (persistentvolumeclaim) group_left() (label_replace(kube_persistentvolumeclaim_info{namespace="kafka"}, "persistentvolume", "$0", "volumename", ".+") * on (persistentvolume) group_left() kube_persistentvolume_capacity_bytes) / 1024 / 1024 / 1024) * 100
Prometheus
의 지표들과 PromQL
을 활용하여 최종적으로 다음과 같은 GUI 환경을 구성할 수 있습니다.
Troubleshooting
java.io.IOException: Disk quota exceeded
Kafka에서 데이터를 처리할때 Disk가 꽉차서 발생하는 오류입니다.
Kafka를 활용함에 있어서 다음과 같이 두가지 경우의 오류를 확인할 수 있었습니다.
ERROR Error while appending records to __consumer_offsets-9 in dir /opt/kafka/data/logs (kafka.server.LogDirFailureChannel)
java.io.IOException: Disk quota exceeded
at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
at sun.nio.ch.FileDispatcherImpl.write(FileDispatcherImpl.java:60)
at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
at sun.nio.ch.IOUtil.write(IOUtil.java:65)
at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:211)
at org.apache.kafka.common.record.MemoryRecords.writeFullyTo(MemoryRecords.java:90)
at org.apache.kafka.common.record.FileRecords.append(FileRecords.java:164)
at kafka.log.LogSegment.append(LogSegment.scala:138)
at kafka.log.Log$$anonfun$append$2.apply(Log.scala:980)
at kafka.log.Log$$anonfun$append$2.apply(Log.scala:857)
at kafka.log.Log.maybeHandleIOException(Log.scala:2058)
at kafka.log.Log.append(Log.scala:857)
at kafka.log.Log.appendAsFollower(Log.scala:837)
at kafka.cluster.Partition$$anonfun$doAppendRecordsToFollowerOrFutureReplica$1.apply(Partition.scala:708)
at kafka.cluster.Partition$$anonfun$doAppendRecordsToFollowerOrFutureReplica$1.apply(Partition.scala:699)
at kafka.utils.CoreUtils$.inLock(CoreUtils.scala:251)
at kafka.utils.CoreUtils$.inReadLock(CoreUtils.scala:257)
at kafka.cluster.Partition.doAppendRecordsToFollowerOrFutureReplica(Partition.scala:698)
at kafka.cluster.Partition.appendRecordsToFollowerOrFutureReplica(Partition.scala:715)
at kafka.server.ReplicaFetcherThread.processPartitionData(ReplicaFetcherThread.scala:157)
at kafka.server.AbstractFetcherThread$$anonfun$kafka$server$AbstractFetcherThread$$processFetchRequest$2$$anonfun$apply$mcV$sp$3$$anonfun$apply$9.apply(AbstractFetcherThread.scala:310)
at kafka.server.AbstractFetcherThread$$anonfun$kafka$server$AbstractFetcherThread$$processFetchRequest$2$$anonfun$apply$mcV$sp$3$$anonfun$apply$9.apply(AbstractFetcherThread.scala:300)
at scala.Option.foreach(Option.scala:257)
at kafka.server.AbstractFetcherThread$$anonfun$kafka$server$AbstractFetcherThread$$processFetchRequest$2$$anonfun$apply$mcV$sp$3.apply(AbstractFetcherThread.scala:300)
at kafka.server.AbstractFetcherThread$$anonfun$kafka$server$AbstractFetcherThread$$processFetchRequest$2$$anonfun$apply$mcV$sp$3.apply(AbstractFetcherThread.scala:299)
at scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59)
at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
at kafka.server.AbstractFetcherThread$$anonfun$kafka$server$AbstractFetcherThread$$processFetchRequest$2.apply$mcV$sp(AbstractFetcherThread.scala:299)
at kafka.server.AbstractFetcherThread$$anonfun$kafka$server$AbstractFetcherThread$$processFetchRequest$2.apply(AbstractFetcherThread.scala:299)
at kafka.server.AbstractFetcherThread$$anonfun$kafka$server$AbstractFetcherThread$$processFetchRequest$2.apply(AbstractFetcherThread.scala:299)
at kafka.utils.CoreUtils$.inLock(CoreUtils.scala:251)
at kafka.server.AbstractFetcherThread.kafka$server$AbstractFetcherThread$$processFetchRequest(AbstractFetcherThread.scala:298)
at kafka.server.AbstractFetcherThread$$anonfun$maybeFetch$1.apply(AbstractFetcherThread.scala:132)
at kafka.server.AbstractFetcherThread$$anonfun$maybeFetch$1.apply(AbstractFetcherThread.scala:131)
at scala.Option.foreach(Option.scala:257)
at kafka.server.AbstractFetcherThread.maybeFetch(AbstractFetcherThread.scala:131)
at kafka.server.AbstractFetcherThread.doWork(AbstractFetcherThread.scala:113)
at kafka.utils.ShutdownableThread.run(ShutdownableThread.scala:82)
ERROR Error while deleting the clean shutdown file in dir /opt/kafka/data/logs (kafka.server.LogDirFailureChannel)
java.io.IOException: Disk quota exceeded
at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
at sun.nio.ch.FileDispatcherImpl.write(FileDispatcherImpl.java:60)
at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
at sun.nio.ch.IOUtil.write(IOUtil.java:65)
at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:211)
at kafka.log.ProducerStateManager$.kafka$log$ProducerStateManager$$writeSnapshot(ProducerStateManager.scala:458)
at kafka.log.ProducerStateManager.takeSnapshot(ProducerStateManager.scala:687)
at kafka.log.Log.rebuildProducerState(Log.scala:717)
at kafka.log.Log.kafka$log$Log$$recoverSegment(Log.scala:499)
at kafka.log.Log.kafka$log$Log$$recoverLog(Log.scala:615)
at kafka.log.Log$$anonfun$2.apply$mcJ$sp(Log.scala:575)
at kafka.log.Log$$anonfun$2.apply(Log.scala:575)
at kafka.log.Log$$anonfun$2.apply(Log.scala:575)
at kafka.log.Log.retryOnOffsetOverflow(Log.scala:2069)
at kafka.log.Log.loadSegments(Log.scala:574)
at kafka.log.Log.<init>(Log.scala:292)
at kafka.log.Log$.apply(Log.scala:2202)
at kafka.log.LogManager.kafka$log$LogManager$$loadLog(LogManager.scala:265)
at kafka.log.LogManager$$anonfun$loadLogs$2$$anonfun$11$$anonfun$apply$15$$anonfun$apply$2.apply$mcV$sp(LogManager.scala:345)
at kafka.utils.CoreUtils$$anon$1.run(CoreUtils.scala:63)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
해결
Kafka
를 직접사용하고 있다면 /opt/kafka/data/logs
, Container
형태로 사용하고 있다면 마운트한 볼륨위치에 가면 토픽명들을 확인할 수 있습니다.
이 파일들은 토픽의 로그들인데 적절하게 지우면 용량을 확보할 수 있습니다.
'IT > Monitoring' 카테고리의 다른 글
[Elasticsearch] 모니터링에 유용한 명령어 모음 (0) | 2019.03.21 |
---|---|
[Zabbix] 모니터링 서버 이관하기 (0) | 2018.12.07 |
[Kibana] 가능한 모니터링 종류 나열 (0) | 2018.11.28 |
[Zabbix] 장애발생시 메일링 설정하기 (0) | 2018.11.28 |
[Zabbix] 메모리 정보가 불일치할때 조치하기 (0) | 2018.11.28 |