Kubernetes - 쿠버네티스 클러스터 로깅(logging, fluentd + kafka + elk)
이번 포스팅에서는 쿠버네티스 로깅 파이프라인 구성에 대해 다루어볼 것이다. 저번 포스팅에서는 Fluentd + ES + Kibana 조합으로 클러스터 로깅 시스템을 구성했었는데, 이번 시간에는 Fluentd + kafka + ELK 조합으로 구성해본다.
<fluentd + ES + kibana logging>
Kubernetes - Kubernetes 로깅 운영(logging), Fluentd
중간에 카프카를 두는 이유는 여러가지가 있을 수 있을 것 같다. 첫번째 버퍼역할을 하기때문에 어느정도 파이프라인의 속도 조절이 가능하다. 두번째 로그를 카프카 큐에 담아두고, 여러 컨슈머 그룹이 각기의 목적으로 로그데이터를 사용가능하다. 바로 실습에 들어가보자.
구성
구성은 위 그림과 같다. fluentd는 컨테이너 로그를 tail하고 있고, tail한 데이터를 카프카로 프로듀싱한다. 그리고 아웃풋으로 로그스태시로 보내고 로그 스태시는 엘라스틱서치에 색인을하게 된다.
실습이전에 본 실습에서 진행하는 예제중 카프카 구성과 엘라스틱서치의 구성은 별도로 옵션 튜닝 및 물리머신에 구성하는 것이 좋다. 필자는 구성의 편의를 위해 아무런 옵션을 튜닝하지 않은채 같은 쿠버네티스 클러스터에 카프카와 엘라스틱서치를 구성하였다.
kafka install & deploy on kubernetes unsing helm
<헬름 설치>
> curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash
> kubectl --namespace kube-system create sa tiller
> kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller
> helm init --service-account tiller
> helm repo update
위 명령어로 헬름을 다운로드 받는다.
<카프카 헬름 차트 설치 및 배포>
> kubectl create ns kafka
> helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
> helm install --name my-kafka --namespace kafka incubator/kafka
kafka라는 별도의 네임스페이스를 생성하여 그 안에 카프카를 배포하였다.
<헬름차트 삭제>
차트 삭제가 필요하면 아래 명령어를 이용하자.
# --purge 옵션으로 관련된 모든 정보를 지운다.
helm delete my-kafka --purge
<fluentd가 데이터를 보낼 토픽생성>
> kubectl -n kafka exec my-kafka-0 -- /usr/bin/kafka-topics \
--zookeeper my-kafka-zookeeper:2181 --topic fluentd-container-logging \
--create --partitions 3 --replication-factor 3
Created topic "fluentd-container-logging".
"fluentd-container-logging"이라는 이름으로 토픽을 생성하였다.
<생성된 topic 확인>
> kubectl -n kafka exec my-kafka-0 -- /usr/bin/kafka-topics --zookeeper my-kafka-zookeeper:2181 --list
fluentd-container-logging
토픽리스트를 조회해서 우리가 생성한 토픽이 있는지 조회해본다.
<fluentd가 보낸 데이터가 큐로 잘들어오는지 확인하기 위해 컨슘머 실행>
> kubectl -n kafka exec -ti my-kafka-0 -- /usr/bin/kafka-console-consumer \
--bootstrap-server my-kafka:9092 --topic fluentd-container-logging --from-beginning
이제 실제로 카프카와 주키퍼가 쿠버네티스에 잘 떠있는지 확인해보자 !
> kubectl get pod,svc -n kafka
NAME READY STATUS RESTARTS AGE
pod/my-kafka-0 1/1 Running 2 4m14s
pod/my-kafka-1 1/1 Running 0 116s
pod/my-kafka-2 1/1 Running 0 78s
pod/my-kafka-zookeeper-0 1/1 Running 0 4m14s
pod/my-kafka-zookeeper-1 1/1 Running 0 3m32s
pod/my-kafka-zookeeper-2 1/1 Running 0 3m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/my-kafka ClusterIP 10.108.104.66 <none> 9092/TCP 4m14s
service/my-kafka-headless ClusterIP None <none> 9092/TCP 4m14s
service/my-kafka-zookeeper ClusterIP 10.97.205.63 <none> 2181/TCP 4m14s
service/my-kafka-zookeeper-headless ClusterIP None <none> 2181/TCP,3888/TCP,2888/TCP 4m14s
위와 같이 팟과 서비스 목록이 보인다면 다음으로 넘어간다.
ELK Stack 구성
<elasticsearch 실행>
아래 deployment와 service 설정파일을 이용하여 쿠버네티스 위에 엘라스틱서치를 구성한다.
apiVersion: v1
kind: Service
metadata:
name: elasticsearch
namespace: elk-stack
spec:
selector:
app: elasticsearch
ports:
- port: 9200
protocol: TCP
targetPort: 9200
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: elasticsearch
namespace: elk-stack
labels:
app: elasticsearch
spec:
replicas: 1
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
spec:
containers:
- name: elasticsearch
image: elastic/elasticsearch:6.8.6
ports:
- containerPort: 9200
name: http
- containerPort: 9300
name: tcp
위 설정 파일은 볼륨을 구성하지 않아서 일회성(테스트)로만 가능하다. 실제로 운영환경에서는 물리머신에 클러스터를 구성하던가, 혹은 쿠버네티스 볼륨을 붙여서 구성하자.
> kubectl apply -f ./kube-logging/fluentd-elasticsearch/elasticsearch.yaml
> kubectl get pod,svc -n elk-stack
NAME READY STATUS RESTARTS AGE
pod/elasticsearch-654c5b6b77-l8k2z 1/1 Running 0 50s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/elasticsearch ClusterIP 10.101.27.73 <none> 9200/TCP 50s
<kibana 실행>
키바나는 아래 설정파일을 예제로 구성하였다.
apiVersion: v1
kind: Service
metadata:
name: kibana
namespace: elk-stack
spec:
selector:
app: kibana
ports:
- protocol: TCP
port: 5601
targetPort: 5601
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana
namespace: elk-stack
labels:
app: kibana
spec:
replicas: 1
selector:
matchLabels:
app: kibana
template:
metadata:
labels:
app: kibana
spec:
containers:
- name: kibana
image: elastic/kibana:6.8.6
ports:
- containerPort: 5601
name: http
위 설정중 조금 살펴봐야할 것은 서비스 타입을 NodePort로 준 점이다. 실제로 외부로 포트를 개방해 localhost로 접근 가능하다. 실제 운영환경에서는 ingress까지 구성하여 배포하자.
> kubectl apply -f ./kube-logging/fluentd-elasticsearch/kibana.yaml
> kubectl get pod,svc -n elk-stack | grep kibana
NAME READY STATUS RESTARTS AGE
pod/kibana-6d474df8c6-fsfc7 1/1 Running 0 24s
NAME READY STATUS RESTARTS AGE
service/kibana NodePort 10.97.240.55 <none> 5601:30578/TCP 24s
http://localhost:30578로 접근해 키바나가 잘 떠있는지와 엘라스틱서치와 잘 연동되었는지 확인하자.
<logstash 실행>
로그스태시는 아래 예시 설정 파일로 구성하였다.
apiVersion: v1
kind: ConfigMap
metadata:
name: logstash-configmap
namespace: elk-stack
data:
logstash.yml: |
http.host: "127.0.0.1"
path.config: /usr/share/logstash/pipeline
pipeline.workers: 2
logstash.conf: |
# all input will come from filebeat, no local logs
input {
kafka {
bootstrap_servers => "my-kafka.kafka.svc.cluster.local:9092"
topics => "fluentd-container-logging"
group_id => "fluentd-consumer-group"
enable_auto_commit => "true"
auto_offset_reset => "latest"
consumer_threads => 4
codec => "json"
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch.elk-stack.svc.cluster.local:9200"]
manage_template => false
index => "kubernetes-container-log-%{+YYYY-MM-dd}"
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: logstash-deployment
namespace: elk-stack
spec:
replicas: 1
selector:
matchLabels:
app: logstash
template:
metadata:
labels:
app: logstash
spec:
containers:
- name: logstash
image: docker.elastic.co/logstash/logstash:5.6.0
ports:
- containerPort: 5044
volumeMounts:
- name: config-volume
mountPath: /usr/share/logstash/config
- name: logstash-pipeline-volume
mountPath: /usr/share/logstash/pipeline
volumes:
- name: config-volume
configMap:
name: logstash-configmap
items:
- key: logstash.yml
path: logstash.yml
- name: logstash-pipeline-volume
configMap:
name: logstash-configmap
items:
- key: logstash.conf
path: logstash.conf
---
apiVersion: v1
kind: Service
metadata:
name: logstash-service
namespace: elk-stack
spec:
selector:
app: logstash
ports:
- protocol: TCP
port: 5044
targetPort: 5044
type: ClusterIP
설정에서 잘 살펴볼 것은 input과 output의 호스트 설정이다. 우리는 모든 모듈을 같은 클러스터에 설치할 것이기 때문에 쿠버네티스 내부 DNS를 사용하였다.(실습에 편의를 위한 것이기도 하지만, 실제 운영환경에서도 내부 시스템은 종종 클러스터 내부 DNS를 사용하기도 한다. 그러면 실제로 통신하기 위해 클러스터 밖으로 나갔다 오지 않는다.)
또 한가지 설정은 Deployment에 볼륨을 마운트 하는 부분이다. 실제 쿠버네티스에서 ConfigMap은 볼륨으로 잡히기 때문에 그 ConfigMap을 logstash pod 내부로 마운트하여 실행시점에 해당 설정파일을 물고 올라가도록 하였다.
> kubectl apply -f ./kube-logging/fluentd-elasticsearch/logstash.yaml
> kubectl get pod,svc -n elk-stack | grep logstash
NAME READY STATUS RESTARTS AGE
pod/logstash-deployment-556cfb66b5-6xrs6 1/1 Running 0 34s
service/logstash-service ClusterIP 10.96.13.170 <none> 5044/TCP 33s
<fluentd 실행>
이제는 실제 컨테이너 로그를 tail하여 수집하는 fluentd를 실행시켜보자.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
labels:
app: fluentd-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
selector:
matchLabels:
app: fluentd-logging
template:
metadata:
labels:
app: fluentd-logging
version: v1
kubernetes.io/cluster-service: "true"
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: 1223yys/fluentd-kafka:latest
imagePullPolicy: Always
env:
- name: FLUENT_KAFKA_BROKERS
value: "my-kafka.kafka.svc.cluster.local:9092"
- name: FLUENT_KAFKA_DEFAULT_TOPIC
value: "fluentd-container-logging"
- name: FLUENT_KAFKA_OUTPUT_DATA_TYPE
value: "json"
- name: FLUENT_KAFKA_COMPRESSION_CODEC
value: "snappy"
- name: FLUENT_KAFKA_MAX_SEND_LIMIT_BYTES
value: "4096"
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
fluentd 설정파일은 몇가지 짚고 넘어갈 것들이 있다. 첫번째는 컨테이너를 tail하기 위해 마운트한 설정이다. /var/log, /var/lib/docker/container를 마운트하였다. 실제 호스트머신에 해당 디렉토리에 들어가면 파일이 보이지 않을 것이다. 만약 파일을 보고 싶다면 아래 설정을 통해 도커 컨테이너를 실행시키고 볼 수 있다.
> docker run -it --rm -v /var/lib/docker/containers:/json-log alpine ash
위 도커이미지를 실행한후 /json-log 디렉토리에 들어가면 호스트머신에 쌓인 컨테이너 로그들을 볼 수 있다.
두번째, tail한 로그를 내보내기 위한 env 설정이다. 아웃풋은 카프카로 두었고, 역시 도메인은 내부 클러스터 DNS로 잡아주었다. 그리고, 우리가 미리 생성한 토픽에 데이터를 보내고 있고 타입은 json으로 보내고 있다.(사실상 튜닝할 설정은 많지만 실습의 편의를 위해 대부분 기본 설정으로 잡았다.)
그리고 필자가 fluentd 이미지를 새로 빌드한 이유는 카프카로 보내는 로그 포맷을 수정하기 위하여 fluentd 설정파일들을 조금 수정하였기 때문이다. 혹시나 fluentd 설정 파일들이 궁금하다면 포스팅 마지막 Github을 참조하자.(https://github.com/yoonyeoseong/kubernetes-sample/tree/master/kube-logging/fluentd-kafka)
> kubectl apply -f ./kube-logging/fluentd-kafka/fluentd-kafka-daemonset.yaml
> kubectl get pod,daemonset -n kube-system | grep fluentd
NAME READY STATUS RESTARTS AGE
pod/fluentd-bqmnl 1/1 Running 0 34s
daemonset.extensions/fluentd 1 1 1 1 1 <none> 34s
이제 로그 출력을 위해 샘플 앱을 실행시켜보자. 로그 출력을 위한 앱은 꼭 아래 필자가 빌드한 웹 어플리케이션을 실행시킬 필요는 없다. 만약 아래 애플리케이션을 실행시키려면 ingress 설정 혹은 service node port를 설정하자.
> kubectl apply -f ./kube-resource/deployment-sample.yaml
> kubectl get pod
NAME READY STATUS RESTARTS AGE
sample-deployment-5fbf569554-4pzrf 0/1 Running 0 17s
이제 요청을 보내보자.
> kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.97.27.106 <none> 80:30431/TCP,443:31327/TCP 21d
ingress-nginx-controller-admission ClusterIP 10.96.76.113 <none> 443/TCP 21d
> curl localhost:30431/api
이제 키바나에 접속해보면 앱에서 출력하고 있는 로그 데이터를 볼 수 있다. 모든 예제 설정 및 코드는 아래 깃헙을 참고하자 !
출처: https://coding-start.tistory.com/383?category=761720 [코딩스타트]
'컨테이너 > Kubernetes' 카테고리의 다른 글
쿠버네티스란 무엇인가 (0) | 2023.04.25 |
---|---|
Kubernetes - kustomize를 이용한 쿠버네티스 오브젝트 관리 (0) | 2021.04.26 |
Kubernetes - 볼륨(Volume),퍼시스턴트 볼륨&볼륨 클레임(persistent volume&claim) (0) | 2021.04.26 |
Kubernetes - ingress nginx 설치 및 사용법 (0) | 2021.04.26 |
Kubernetes - kubernetes(쿠버네티스) resources(cpu/momory) 할당 및 관리 (0) | 2021.04.26 |
Kubernetes - Kubernetes 로깅 운영(logging), Fluentd (0) | 2021.04.25 |
Kubernetes - Kubernetes 아키텍쳐 (0) | 2021.04.25 |
Kubernetes - Kubernetes 용어설명 (0) | 2021.04.25 |