🥞 BE
home

Ch17. 로깅과 모니터링

목차
클러스터를 운영하면서 현재 상태가 어떤지 확인하는 것은 중요한 일이다. 여러 대 서버에 분산되어 있는 로그를 어떻게 수집하는지 살펴보고, 쿠버네티스에서 서버나 파드 상태를 확인하는 모니터링 구조를 살펴보자.

17.1 로깅

클러스터 환경에서 앱 컨테이너를 운영할 때 주의해야 할 것 중 하나가 로그 처리이다. 컨테이너 오케스트레이터를 사용하는 환경에서 로그를 수집할 때 주의해야 할 점 중 하나는 특별한 상황을 제외하고는 로그를 로컬 디스크에 파일로 저장하지 않아야 한다는 것이다.
전통적인 애플리케이션 운영 환경에서는 로컬 파일 시스템의 지정된 위치에 로그를 저장하고 로그로테이트(logrotate)와 같은 프로그램을 써서 디스크 용량을 관리했다. 그러나 컨테이너는 상황에 따라 클러스터 안의 여러 노드를 옮겨다니기 때문에 로그 확인이 더 어렵다. 쿠버네티스는 다음의 기능으로 이러한 작업을 쉽게 완료한다.

17.1.1 파드 로그 확인

쿠버네티스의 kubectl은 개별 노드에 접근하지 않고 직접 파드의 로그를 확인할 수 있다. 이때 kubectl logs -f 파드이름 명령어를 사용한다. 여기서는 앞서 실행해 두었던 kubernetes-simple-app 디플로이먼트가 생성한 파드의 로그를 확인한다.
먼저 kubectl get pods 로 파드 이름을 확인한 후 kubectl port-forward pods/파드이름 8080:8080 명령으로 파드에 접근할 수 있는 포트 번호를 8080으로 설정한다. 그리고 새로운 셸을 열어 kubectl logs -f 파드이름 명령어를 실행한다.
-f 옵션은 실행 중인 로그를 지속해서 수집하는 테일링을 실행한다. 웹 브라우저에서 해당 앱 컨테이너에 접속 후 새로고침을 하면 로그의 업데이트를 확인할 수 있다. 상세 옵션은 -h으로 확인 가능!

17.1.2 ElasticSearch 활용 로그 수집 및 모아보기

로컬 디스크에 로그를 저장하면 용량 문제로 삭제한 예전 로그들을 확인할 수 없다. 트래픽이 많은 서버는 저장하는 로그 용량도 많으므로 한 시간 단위, 어쩌면 그보다 더 빨리 로그를 삭제할 수도 있다. 이런 문제점을 개선하려고 로그를 모아 살펴보도록 하는 오픈 소스 도구로 Kafka, Fluentd, Logstash, Elasticsearch, Kibana 등이 있다.
퍼블릭 클라우드 서비스를 사용한다면, 각각에서 제공하는 로그 수집 도구를 사용할 수 있겠지만 그런 서비스를 이용하지 못한다면 오픈 소스 도구로 직접 로그 수집을 해야한다. 여기서는 클러스터 안에 직접 Elasticsearch로 로그 수집 시스템을 구축한 후 로그를 수집 및 확인한다.
apiVersion: apps/v1 kind: Deployment metadata: name: elasticsearch labels: app: elasticsearch spec: replicas: 1 selector: matchLabels: app: elasticsearch template: metadata: labels: app: elasticsearch spec: containers: - name: elasticsearch image: elastic/elasticsearch:6.4.0 env: - name: discovery.type value: "single-node" ports: - containerPort: 9200 - containerPort: 9300 --- apiVersion: v1 kind: Service metadata: labels: app: elasticsearch name: elasticsearch-svc namespace: default spec: ports: - name: elasticsearch-rest nodePort: 30920 port: 9200 protocol: TCP targetPort: 9200 - name: elasticsearch-nodecom nodePort: 30930 port: 9300 protocol: TCP targetPort: 9300 selector: app: elasticsearch type: NodePort
YAML
복사
logging/elasticsearch.yaml
노드 하나로만 구성되므로 .spec.replicas 필드는 1
.spec.template.spec.containers[].image 필드 값은 Elasticsearch로 지정.
.spec.template.spec.containers[].env[] 의 하위 필드는 .name 필드로 discovery.type을 설정했고, .value 필드 값으로 single-node를 설정해서 노드가 하나인 Elasticsearch를 실행하도록 설정.
.spec.template.spec.containers[].ports[]의 하위 .containerPort 필드 값 2개로는 Elasticsearch가 사용하는 포트인 9200, 9300을 설정.
서비스는 9200, 9300 포트를 각각 30920, 30930으로 사용해서 접근할 수 있게 .spec.ports[].nodePort 필드 2개를 설정.
클러스터에 적용 후 30920포트로 실행시켰을 때 아래와 같이 출력되면 Elasticsearch가 정상적으로 실행된 것.
Elasticsearh를 실행만 해도 REST API를 이용하면 로그 데이터를 저장하고 검색해서 확인할 수 있다. 이런 로그 데이터를 시각화 하기위해 전용 대시보드 UI인 Kibana를 함께 사용한다.
apiVersion: apps/v1 kind: Deployment metadata: name: kibana labels: app: kibana spec: replicas: 1 selector: matchLabels: app: kibana template: metadata: labels: app: kibana spec: containers: - name: kibana image: elastic/kibana:6.4.0 env: - name: SERVER_NAME value: "kibana.kubenetes.example.com" - name: ELASTICSEARCH_URL value: "http://elasticsearch-svc.default.svc.cluster.local:9200" ports: - containerPort: 5601 --- apiVersion: v1 kind: Service metadata: labels: app: kibana name: kibana-svc namespace: default spec: ports: - nodePort: 30561 port: 5601 protocol: TCP targetPort: 5601 selector: app: kibana type: NodePort
YAML
복사
logging/kibana.yaml
Kibana는 Elasticsearch의 데이터를 검색하므로 접근할 도메인을 알아야한다. 따라서 .spec.template.spec.contaioners[].env.name.value 필드 값을 따로 설정했다.
.value 필드 값의 도메인은 쿠버네티스 안에서 제공하는 도메인이다. 도메인 체계를 살펴보면 로컬(local)이라는 클러스터(cluster)에 있는 서비스(svc) 중 default namespace의 elasticsearch-svc를 가리킨다. 이 주소로 elasticsearch-svc 서비스에 있는 클러스터 IP로 Elasticsearch 파드에 접근할 수 있다.
클러스터에 적용 후, 30561포트로 실행시킨다. 처음 실행해 초기화 하는데 어느정도의 시간이 걸린다.

17.1.3 클러스터 레벨 로깅

컨테이너가 비정상 종료되거나 노드에 장애가 있더라도 앱 컨테이너의 로그를 확인할 수 있어야 한다. 따라서 컨테이너, 파드, 노드의 생명 주기와 분리된 스토리지를 구축해야 한다.
이를 클러스터 레벨 로깅이라고 하며, 다양한 외부 도구로 클러스터 레벨 로깅 시스템을 구축한다.

컨테이너 로그

컨테이너의 로그 수집은 컨테이너 런타임이 담당한다. 앱 컨테이너가 stdout과 stderr이라는 표준 스트림으로 로그를 출력한다면, 컨테이너 런타임은 표준 스트림 2개를 특정 로그 드라이버로 redirect하도록 설정되어 있다.
도커는 다양한 로그 드라이버를 지원하며 기본으로 json-file을 사용한다.
docker ps로 현재 실행중인 도커 컨테이너를 확인 후, docker inspect <컨테이너ID> 명령으로 컨테이너 런타임이 로그를 어떻게 다루는지 확인한다.
LogPath : 컨테이너 관련 메타 데이터를 저장한 심볼릭 링크
LogConfig : 로그 설정
Type : json-file 설정
Config : max-file, max-size 항목이 존재. 아직 설정 x
kubelet은 /var/lib/docker/container/컨테이너ID/컨테이너ID-json.log 파일에 관해 다음의 심볼릭 링크를 생성하고 관리한다.
/var/log/containers/파드이름_파드네임스페이스이름_컨테이너이름컨테이너ID.log
/var/log/pods/파드UID/컨테이너이름/0.log
Fluentd 등의 로그 수집기는 앞 심볼릭 링크들을 테일링해 로그 내용을 수집하고, 파일 이름으로부터 필요한 메타데이터(파드, 컨테이너, 네임스페이스) 정보를 얻는다.
kubectl apply -f https://raw.githubusercontent.com/fluent/fluentd-kubernetes-daemonset/master/fluentd-daemonset-syslog.yaml 로 Fluentd를 설치해둔다.
이제 kubectl get pods -n kube-system 명령으로 Fluentd 파드 이름 하나를 기억해둔다. 그리고 /var/log/containers 디렉토리로 이동해 tail -n 1 파드이름_kube-system_fluentd-XXX~.log 명령으로 파일 내용을 확인한다.
로컬이 아니라 linux vm에서 구동했을 경우 /var/log/containers 경로가 존재. 해당 부분은 나중에 실습하기로..
로그를 출력하면 로그 내용(log), 표준 스트림 종류(stream : stderr), RFC3339Nano 시간 정보(time)를 JSON 형식으로 출력한다.
로그가 노드의 스토리지를 과도하게 사용하는 문제가 발생할 경우, 런타임에서 로그로테이트 관련 설정을 확인해야한다.

시스템 컴포넌트 로그

쿠버네티스의 시스템 구성 요소 중 일부(kubelet, docker 등)는 컨테이너 기반으로 동작하지 않는다. 따라서 반드시 노드에 로그로테이트 관련 설정이 있어야한다.
시스템 구성 요소들의 로그 관련 동작은 컴포넌트별로 차이가 있다. 예를 들어 kubelet이라면 systemctl status kubelet.service 명령으로 확인이 가능하다.

17.1.4 Fluentd 활용 로그 수집

Fluentd는 쿠너네티스와 같은 CNCF에서 관리하는 범용 로그 수집용 오픈소스 프로젝트이다. 루비 기반이며 다양한 플러그인을 사용할 수 있다.
Fluentd를 쿠버네티스에서 사용할때는 Fluentd Daemonset for Kubernetes를 참고한다.
fluentd-kubernetes-daemonset
fluent
여기서는 Elasticsearch용 Fluentd 설정 템플릿을 yaml파일로 작성한다.
apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd namespace: kube-system labels: k8s-app: fluentd-logging version: v1 kubernetes.io/cluster-service: "true" spec: selector: matchLabels: k8s-app: fluentd-logging template: metadata: labels: k8s-app: fluentd-logging version: v1 kubernetes.io/cluster-service: "true" spec: tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: fluentd image: fluent/fluentd-kubernetes-daemonset:elasticsearch env: - name: FLUENT_ELASTICSEARCH_HOST value: "elasticsearch-svc.default.svc.cluster.local" - name: FLUENT_ELASTICSEARCH_PORT value: "9200" - name: FLUENT_ELASTICSEARCH_SCHEME value: "http" - name: FLUENT_UID value: "0" 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
YAML
복사
logging/fluentd-kubernetes-daemonset.yaml
.kind 필드 값으로는 daemonset을 설정한다. 로그 수집기는 여러 대 노드로 구성된 클러스터 모두에 실행해서 로그를 수집해야 한다. 이때 로그 수집기인 Fluentd를 처음 실행할 때 데몬세트 컨트롤러가 관리하면 클러스터의 노드가 늘어나더라도 로그 수집기가 자동으로 새로 추가된 노드에 실행된다.
.spec.template.spec.containers[].image 필드 값으로는 컨테이너 이미지인 fluent/fluentd-kubernetes-daemonset:elasticsearch 를 설정했다. 별도의 설정 없이 기본 이미지를 사용한 것이다.
.spec.template.spec.containers[].env[]의 하위 필드는 .name.value 필드쌍 4개가 있다. 첫 번째 .name.value 필드 값으로는 FLUENT_ELASTICSEARCH_HOST 환경 변수와 elasticsearch-svc.default.svc.cluster.local이라는 도메인을 설정했다. 기존 설정한 Elasticsearch 디플로이먼트에 로그를 보내는 것이다. 네 번째 .name.value 필드 값으로는 FLUENT_UID 환경 변수와 “0”을 설정했다. Fluentd를 실행하는 사용자 ID를 0으로 설정하면 로그를 저장하는 디렉토리에 접근할 권한이 있는 것이다.
이외에도 /var/lib/docker/containers에 프로세스들의 로그를 저장할 수 있게하는 설정이 추가된다.
kubectl apply -f fluentd-kubernetes-daemonset.yaml로 클러스터에 적용한다.
kubectl get pods -n kube-system 명령으로 생성한 파드 이름을 확인하고, kubectl exec -it fluentd-txxfb -n kube-system -- sh 명령을 실행하면 Fluentd를 실행하는 파드에 접속해서 실제로 로그를 저장하는지 확인할 수 있다.
위와 같이 /var/lib/docker/containers의 하위 디렉터리들은 실제 컨테이너들이 데이터를 저장하려고 사용한다.
이제 저장한 로그를 Kibana에서 조회한다. Kibana에서 Elasticsearch의 특정 인덱스 데이터들을 조회하는 인덱스 패턴을 만들어야 한다. Index pattern 항목에 logstash-* 을 입력한다. 키바나에서 logstash라는 접두어가 있는 모든 인덱스를 한꺼번에 조회할 수 있는 인덱스 패턴을 만드는 것이다.
Elasticsearch와 Kibana를 함께 사용할 때는 주로 날짜별 인덱스를 조회하는 인덱스 패턴을 만든다. Elasticsearch에 날짜 단위로 로그를 저장하면 로그가 많을 때 오래된 날짜의 인덱스만 삭제해서 디스크 용량을 확보한다.
검색 시간대를 옵션으로 사용할 수 있도록 @timestamp를 선택하고 인덱스 패턴을 Create한다.
로그 내용을 확인해보면 쿠버네티스 클러스터 정보들이 kubernetes.* 항목으로 저장되어 있다.

17.1.5 Stern 활용 실시간 로그 모니터링

실무에서는 실시간으로 여러 개 파드의 로그를 지켜보고 싶을 때가 있다.
이를 위한 실시간 로그 모니터링 도구로 Stern(스턴)이 있다. Stern은 셸에서 바로 여러 개 파드의 로그를 실시간으로 볼 수 있어 서비스 개발을 진행하는 도중 파드 상태를 확인할 때 유용하다.
stern kube -n kube-system 명령을 실행하면 로그를 확인할 수 있다.
-n 옵션으로 네임스페이스를 설정할 수 있다. 이외에도 다양한 옵션이 있는데,
--all-namespaces : 모든 네임스페이스에 있는 로그를 한번에 출력.
-l , --label : 특정 레이블의 파드들 로그만 선택해서 출력.
-o : 출력 형식을 변경할 수 있다. -o json을 사용하면 JSON 형식으로 로그를 출력.
--template : 출력 형식을 마음대로 변경해서 원하는 항목만 확인할 수 있다. stern --template ‘{{.Message}} ({{.Namespace}}/{{.PodName}}/{{.ContainerName}})’ kube -n kube-system 과 같은 형태로 명령을 실행한다.

17.2 쿠버네티스 대시보드

쿠버네티스는 웹 UI 기반인 쿠버네티스 대시보드를 제공한다. 쿠버네티스 대시보드를 사용하려면 먼저 쿠버네티스 대시보드 깃허브에서 recommended.yaml을 다운로드한 후 dashboard-recommended.yaml로 이름을 바꾼다. 그리고 다음의 yaml코드 처럼 추가한다.
apiVersion: v1 kind: Namespace metadata: name: kubernetes-dashboard --- apiVersion: v1 kind: ServiceAccount metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard --- apiVersion: v1 kind: ServiceAccount metadata: name: admin-user namespace: kubernetes-dashboard --- kind: Service apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard spec: ports: - port: 443 targetPort: 8443 selector: k8s-app: kubernetes-dashboard --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-certs namespace: kubernetes-dashboard type: Opaque --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-csrf namespace: kubernetes-dashboard type: Opaque data: csrf: "" --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-key-holder namespace: kubernetes-dashboard type: Opaque --- kind: ConfigMap apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-settings namespace: kubernetes-dashboard --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard rules: # Allow Dashboard to get, update and delete Dashboard exclusive secrets. - apiGroups: [""] resources: ["secrets"] resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs", "kubernetes-dashboard-csrf"] verbs: ["get", "update", "delete"] # Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map. - apiGroups: [""] resources: ["configmaps"] resourceNames: ["kubernetes-dashboard-settings"] verbs: ["get", "update"] # Allow Dashboard to get metrics. - apiGroups: [""] resources: ["services"] resourceNames: ["heapster", "dashboard-metrics-scraper"] verbs: ["proxy"] - apiGroups: [""] resources: ["services/proxy"] resourceNames: ["heapster", "http:heapster:", "https:heapster:", "dashboard-metrics-scraper", "http:dashboard-metrics-scraper"] verbs: ["get"] --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard rules: # Allow Metrics Scraper to get metrics from the Metrics server - apiGroups: ["metrics.k8s.io"] resources: ["pods", "nodes"] verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: kubernetes-dashboard subjects: - kind: ServiceAccount name: kubernetes-dashboard namespace: kubernetes-dashboard --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: kubernetes-dashboard roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: kubernetes-dashboard subjects: - kind: ServiceAccount name: kubernetes-dashboard namespace: kubernetes-dashboard --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-user roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: admin-user namespace: kubernetes-dashboard --- kind: Deployment apiVersion: apps/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard spec: replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: k8s-app: kubernetes-dashboard template: metadata: labels: k8s-app: kubernetes-dashboard spec: securityContext: seccompProfile: type: RuntimeDefault containers: - name: kubernetes-dashboard image: kubernetesui/dashboard:v2.6.1 imagePullPolicy: Always ports: - containerPort: 8443 protocol: TCP args: - --auto-generate-certificates - --namespace=kubernetes-dashboard # Uncomment the following line to manually specify Kubernetes API server Host # If not specified, Dashboard will attempt to auto discover the API server and connect # to it. Uncomment only if the default does not work. # - --apiserver-host=http://my-address:port volumeMounts: - name: kubernetes-dashboard-certs mountPath: /certs # Create on-disk volume to store exec logs - mountPath: /tmp name: tmp-volume livenessProbe: httpGet: scheme: HTTPS path: / port: 8443 initialDelaySeconds: 30 timeoutSeconds: 30 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 1001 runAsGroup: 2001 volumes: - name: kubernetes-dashboard-certs secret: secretName: kubernetes-dashboard-certs - name: tmp-volume emptyDir: {} serviceAccountName: kubernetes-dashboard nodeSelector: "kubernetes.io/os": linux # Comment the following tolerations if Dashboard must not be deployed on master tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule --- kind: Service apiVersion: v1 metadata: labels: k8s-app: dashboard-metrics-scraper name: dashboard-metrics-scraper namespace: kubernetes-dashboard spec: ports: - port: 8000 targetPort: 8000 selector: k8s-app: dashboard-metrics-scraper --- kind: Deployment apiVersion: apps/v1 metadata: labels: k8s-app: dashboard-metrics-scraper name: dashboard-metrics-scraper namespace: kubernetes-dashboard spec: replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: k8s-app: dashboard-metrics-scraper template: metadata: labels: k8s-app: dashboard-metrics-scraper spec: securityContext: seccompProfile: type: RuntimeDefault containers: - name: dashboard-metrics-scraper image: kubernetesui/metrics-scraper:v1.0.8 ports: - containerPort: 8000 protocol: TCP livenessProbe: httpGet: scheme: HTTP path: / port: 8000 initialDelaySeconds: 30 timeoutSeconds: 30 volumeMounts: - mountPath: /tmp name: tmp-volume securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 1001 runAsGroup: 2001 serviceAccountName: kubernetes-dashboard nodeSelector: "kubernetes.io/os": linux # Comment the following tolerations if Dashboard must not be deployed on master tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule volumes: - name: tmp-volume emptyDir: {}
YAML
복사
addon/dashboard-recommended.yaml
볼드 표시한 부분은 쿠버네티스 대시보드를 클러스터에 적용한 후 베어러(Bearer) 토큰을 만들어 관리자 로그인하는 데 필요한 서비스 계정과 클러스터롤바인딩 설정 부분이다.
다음으로 로그인에 필요한 토큰을 만들 차례이다. 다음 명령을 실행하면 토큰이 생성된다.
kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"
Shell
복사
그런데 왜인지.. 다음과 같은 오류가 떴고
error: error executing template "{{.data.token | base64decode}}": template: output:1:16: executing "output" at <base64decode>: invalid value; expected string
kubectl -n kubernetes-dashboard create token admin-user 를 입력하여 문제를 해결했다.
접속하면 다음과 같은 화면이 뜨고, 위부터 차례로 워크로드 관련 메뉴, 서비스 관련 메뉴, 컨피그 및 스토리지 확인 메뉴 등이 있다.
파드를 직접 실행할 수도 있고, 아래와 같이 현재 실행중인 파드 설정을 수정할 수도 있다.
그리고 워크로드의 파드에서 EXEC, 로그확인 등으로 컨테이너의 로그를 확인할 수 있다.

17.3 쿠버네티스 클러스터 모니터링

클러스터에서 애플리케이션을 운영하다 보면 기본적인 시스템 메트릭인 CPU, 메모리부터 애플리케이션의 특수한 메트릭 정보까지 많은 것을 모니터링해야 한다.

17.3.1 쿠버네티스 모니터링 아키텍처

쿠버네티스 모니터링은 시스템 메트릭서비스 메트릭을 수집해서 확인할 수 있다.
쿠버네티스 구성 요소들을 직접 관리하는 코어 메트릭 파이프라인과 클러스터 사용자들에게 필요한 정보를 모니터링하는 모니터링 파이프라인을 살펴보자.

시스템 메트릭

시스템 메트릭노드나 컨테이너의 CPU, 메모리 사용량 같은 시스템 관련 메트릭이다. 시스템 메트릭은 다시 코어 메트릭비코어 메트릭으로 나뉜다.
코어 메트릭은 쿠버네티스 내부 컴포넌트들이 사용하는 메트릭이다. 현재 클러스터 안이나 내장 오토스케일링에서 사용할 수 있는 자원이 얼마인지 파악한다.
코어 메트릭 : 대시보드나 자원 사용량을 알려주는 kubectl top 같은 명령에서 보여주는 CPU/메모리 사용량, 파드/컨테이너의 디스크 사용량 등
비코어 메트릭 : 쿠버네티스가 직접 사용하지 않는 다른 시스템 메트릭

서비스 메트릭

서비스 메트릭애플리케이션을 모니터링할 때 필요한 메트릭이다. 서비스 메트릭은 다시 쿠버네티스 인프라용 컨테이너에서 수집하는 메트릭과 사용자 애플리케이션에서 수집하는 메트릭으로 나뉜다.
쿠버네티스 인프라 컨테이너에서 수집하는 메트릭은 클러스터를 관리할 때 참고해서 사용한다.
웹 응답시간 관련값, 시간당 HTTP 500 에러가 몇 번 나는지 등 서비스 관련 정보를 파악한다.

코어 메트릭 파이프라인

코어 메트릭 파이프라인은 쿠버네티스 관련 구성 요소를 직접 관리하는 파이프라인이다. 코어 시스템 메트릭을 수집해 핵심 요소의 모니터링을 담당한다. kubelet, 메트릭 서버, 메트릭 API 등으로 구성된다.
1.
kubelet에 내장된 cAdvisor가 노드/파드/컨테이너의 사용량 정보를 수집
2.
메트릭 서버는 이 정보들을 kubelet에서 불러와 메모리에 저장
3.
이렇게 저장된 메트릭 정보는 마스터의 메트릭 API를 이용해 다른 시스템 컴포넌트가 조회 (단 메모리에 저장하기에 저장 용량에 한계가 있어 짧은 기간 정보만 저장 가능)

모니터링 파이프라인

모니터링 파이프라인은 기본 메트릭을 포함한 여러가지 메트릭을 수집한다. 여기서 수집한 메트릭은 쿠버네티스 시스템보다는 클러스터 사용자에게 필요한 모니터링에 사용한다.
모니터링 파이프라인은 시스템 메트릭과 서비스 메트릭 모두 수집할 수 있다. 코어 메트릭 파이프라인과는 분리되었으므로 원하는 모니터링 도구를 조합해서 사용하면 된다.
조합 예시 cAdvisor + 힙스터 + 인플럭스DB cAdvisor + 프로메테우스 cAdvisor + collectd + 힙스터 snapd + SNAP cluster-level agent Sysdig

모니터링 아키텍처의 전체 구조

17.3.2 메트릭 서버

메트릭 서버는 쿠버네티스 모니터링 아키텍처에서 코어 메트릭 파이프라인을 효율적으로 사용하려고 힙스터 대신 모니터링 표준으로 도입한 것이다.
메트릭 서버는 kubelet으로 수집해 메모리에 저장한 파드나 노드의 메트릭 데이터를 kube-apiserver로 조회하는 메트릭 API를 제공한다. 사실 쿠버네티스에 필요한 핵심 데이터들 대부분은 etcd에 저장된다. 그런데 메트릭 데이터까지 etcd에 저장하면 etcd의 부하가 너무 커지므로 메모리에 저장하는 것이다.
단, 메트릭 서버용 파드를 재시작하면 데이터가 날아간다. 데이터 보관 주기를 길게 하려면 별도의 외부 스토리지를 써야한다.

17.3.3 프로메테우스

모니터링 도구로 최근 가장 많이 주목받는 것은 프로메테우스이다. 프로메테우스는 사운드클라우드에서 처음 개발했고 현재는 CNCF에서 관리하는 프로젝트이다.

프로메테우스의 주요 기능

프로메테우스에는 시계열 데이터를 저장할 수 있는 다차원 데이터 모델과 이를 효과적으로 활용할 수 있는 PromQL이라는 쿼리가 있다.
기본적인 모니터링 데이터 수집은 프로메테우스 서버가 수집하려는 대상에서 데이터를 가져오는 pull 구조이다.
수집 대상은 정적 혹은 service discovery를 이용해 동적으로 설정할 수 있다. 외부에서 직접 push한 모니터링 데이터를 push gateway로 받아서 저장할 수 있다. 모니터링 데이터는 단순히 디스크에 저장된다.
수집한 데이터의 시각화는 내장 웹 UI를 사용하거나, Grafana와 연결해 볼 수도 있다.

프로메테우스의 아키텍처

Prometheus server : 시계열 데이터를 수집해서 저장
Client library : 애플리케이션을 개발할 때 프로메테우스에서 데이터를 수집하도록 만드는 라이브러리
Push gateway : Client에서 직접 프로메테우스로 데이터를 보낼 때 받는 역할
Exporter : 프로메테우스 클라이언트 라이브러리를 내장해 만들지 않은 애플리케이션에서 데이터를 수집
Altermanager : 알람을 보낼 때 중복 처리, 그룹화 등을 하며 알람을 어디로 보낼지 관리

프로메테우스 사용하기

프로메테우스를 실행하려면 먼저 쿠버네티스용 설정 파일이 있어야 한다.
prometheus-kubernetes-config.yaml
kubectl create config map prometheus-kubernetes --from-file=./prometheus-kubernetes-config.yaml 명령어로 컨피그맵을 생성한다.
다음 프로메테우스용 디플로이먼트와 서비스를 설정한다.
apiVersion: apps/v1 kind: Deployment metadata: name: prometheus-app labels: app: prometheus-app spec: replicas: 1 selector: matchLabels: app: prometheus-app template: metadata: labels: app: prometheus-app spec: containers: - name: prometheus-app image: prom/prometheus:v2.3.2 args: - "--config.file=/etc/prometheus/prometheus-kubernetes-config.yaml" ports: - containerPort: 9090 volumeMounts: - name: config-volume mountPath: /etc/prometheus - name: storage-volume mountPath: /prometheus/ volumes: - name: config-volume configMap: name: prometheus-kubernetes - name: storage-volume emptyDir: {} --- apiVersion: v1 kind: Service metadata: labels: app: prometheus-app name: prometheus-app-svc namespace: default spec: ports: - nodePort: 30990 port: 9090 protocol: TCP targetPort: 9090 selector: app: prometheus-app type: NodePort
YAML
복사
monitoring/prometheus-deployment.yaml
.image 필드에서 컨테이너 이미지로 prom/prometheus:v2.3.2를 설정했다.
.args[] 필드 값은 프로메테우스의 설정 파일로 --config.file=/etc/prometheus/prometheus-kubernetes-config.yaml 을 지정했다.
.volumes[] 의 하위 필드에 볼륨을 설정했다. .name 값으로 config-volume을 설정했고 .configMap.name 필드 값으로 prometheus-kubernetes를 설정해 컨피그맵을 사용하게 했다. 두 번째 .name 필드 값으로 storage-volume을 설정했다. .emptyDir 필드 값은 {}로 설정했다. 즉, emptyDir 볼륨을 사용하겠다는 것이다.
.volumeMounts[]로 위에서 설정한 볼륨을 컨테이너에 마운트
.nodePort 필드에서 30990 포트를 설정
kubectl apply -f prometheus-deployment.yaml 로 클러스터 적용 후, localhost:30990으로 접속하면 프로메테우스의 웹 UI를 확인할 수 있다.

프로메테우스와 그라파나 연동

프로메테우스의 기본 웹 UI 이외에 저장된 모니터링 데이터를 조회할 때는 대시보드 도구인 그라파나를 많이 사용한다.
apiVersion: app/v1 kind: Deployment metadata: name: grafana-app spec: selector: matchLabels: k8s-app: grafana replicas: 1 template: metadata: labels: k8s-app: grafana spec: containers: - name: grafana image: grafana/grafana:5.2.3 ports: - containerPort: 3000 protocol: TCP env: - name: GF_SERVER_HTTP_PORT value: "3000" - name: GF_AUTH_BASIC_ENABLED value: "false" - name: GF_AUTH_ANONYMOUS_ENABLED value: "true" - name: GF_AUTH_ANONYMOUS_ORG_ROLE value: Admin - name: GF_SERVER_ROOT_URL value: / --- apiVersion: v1 kind: Service metadata: labels: kubernetes.io/name: grafana-app name: grafana-app spec: ports: - port: 3000 targetPort: 3000 nodePort: 30300 selector: k8s-app: grafana type: NodePort
YAML
복사
.env[] 의 하위 필드에는 여러 .name.value 필드 값으로 그라파나의 기본 환경 변수들을 설정했다.
GF_SERVER_HTTP_PORT - 사용할 포트인 3000을 설정.
GF_AUTH_BASIC_ENABLED - 그라파나를 인증없이 사용하도록 설정.
GF_AUTH_ANONYMOUS_ENABLED - 익명 사용자가 그라파나를 사용하도록 만드는 옵션으로 true 설정. 인증없이 그라파나 사용 가능.
GF_AUTH_ANONYMOUS_ORG_ROLE - 익명 사용자가 관리자 권한으로 그라파나를 사용
GF_SERVER_ROOT_URL - 루트 경로를 지정하는 옵션으로 / 설정.
kubectl apply -f grafana.yaml 로 클러스터에 적용 후, localhost:30300으로 접속.

Reference

Fluentd yaml파일 작성 오류 해결
Dashboard 오류 해결