파란하늘의 지식창고
article thumbnail
반응형

공부 목적으로 설치해 보는 과정을 정리한 것이기 때문에 실제 운영과 다릅니다.


Observability, Monitoring에 대하여

 

Observability(관측 가능성)는 시스템의 출력 변수를 사용하여 상태 변수에 대한 정보를 알아낼 수 있는지를 나타내는 용어이다.

이런 정보를 확인하기 위해서는 데이터를 수집하고 저장하여 지속적으로 관찰, 감시할 수 있는 모니터링이 필요하다.

저장할 데이터의 종류는 크게 log, metric, trace (추가로 event?)으로 나누어 볼 수 있다.

log는 일반적으로 개발하면서 logger를 통해 남기는 데이터라고 생각하면 된다.

metric은 현재 application의 상태에 대한 지표를 주기적으로 수집하여 시간별 상태를 확인할 수 있도록 시계열 데이터(time series data)로 저장한다.
시계열 데이터는 일정한 시간 동안 수집된 일련의 순차적으로 정해진 데이터 셋의 집합이다.

trace는 특정 application 작업에 대한 정보를 추적하는 데이터이다. msa로 구성된 경우 여러 msa를 거쳐 사용자의 요청에 대한 응답이 만들어진다. 이 흐름을 추적하여 어느 부분에 병목이 발생하는지에 대해 파악을 할 수 있어야 한다.

Grafana? Kibana?

ELK (ElasticSearch + Logstash + Kibana), EFK (ElasticSearch + Fluentd + Kibana) stack에서 Logstash, Fleuntd, Beats를 통해 수집한 데이터를 ElasticSearch에 저장을 하고 이를 Kibana를 통해 시각화하는 과정을 둘러본 적이 있다.

이와 유사하게 PLG (Promtail + Loki + Grafana) stack이 있는데 어떤 차이가 있는지 찾아보았다.

https://signoz.io/blog/kibana-vs-grafana/

Grafana와 Kibana는 각각의 stack에서 dashboard를 제공하는 시각화 도구 부분을 담당하고 있다.

Kibana의 경우 ElasticSearch의 데이터를 탐색, 분석 및 시각화할 수 있다.
ElasticSearch만 지원하는 대신 검색에 장점을 가지고 있다.
ElasticSearch 검색 엔진은 로그 데이터를 저장하고 검색하는데 장점을 가지고 있다.
ElasticSearch + Kibana 조합은 로그 시각화에 뛰어나다.

Grafana는 Kibana와 유사해 보이지만 시계열 데이터베이스(time series database, tsdb)의 지표를 시각화하는데 중점을 두고 있다.

Kibana가 log 데이터를 검색하여 시각화하는 반면 Grafana는 서버의 상태(CPU, 메모리 사용량, 응답 속도)의 변화 같은 metric 시계열 데이터를 시각화하여 보여준다.
Kibana가 ElasticSearch만 DataSource로 지원하는 것과 달리 Grafana는 InfluxDB, OpenTSDB, Prometheus 등 다양한 Time series database를 지원한다.

여기까지만 보면 log 데이터는 Kibana, 시계열 데이터 metric은 Grafana를 사용하는 게 좋아 보인다.
하지만 시간이 흐르면서 Grafana에서도 ElasticSearch나 Loki로 수집된 log 데이터의 시각화를 지원하고 Kibana 또한 metrictrace을 지원하는 등 두 도구는 점점 그 역할을 확장해나가고 있다.

Grafana의 경우 현재 Prometheus, Graphite, InfluxDB, OpenTSDB 같은 metric 시계열 데이터 (Time series database) 뿐만 아니라 Loki, Elasticsearch와 같은 log 데이터를 비롯하여 document database, Jaeger, Tempo, Zipkin과 같은 Distributed tracing 데이터, MySQL, PostgrSQL, Microsoft SQL Server와 같은 SQL, Azure Monitor, CloudWatch, Google Cloud Monitoring, Grafana Cloud와 같은 Cloud를 비롯하여 DataDog, GitLab, Honeycomb, Jira 등 다양한 Enterprise plugin을 지원하여 확장성이 뛰어나다.

또한 이러한 log, metric, trace 데이터를 수집하여 내보내는 역할에 OpenTelemetry가 주목받고 있는 추세이다.
https://opentelemetry.io/

기존에 Elasticsearch (log 저장), Kibana를 설치하여 살펴본 적이 있으니 이번엔 Prometheus (metric 저장)와 Grafana를 살펴보려고 한다.
prometheus는 metric 정보를 수집하고 시계열 데이터를 저장한다.
수집과 저장의 두 가지를 같이 하며 application에서 push를 하는 게 아닌 prometheus에서 pull 해서 수집을 한다.
application에서는 prometheu가 주기적으로 호출해서 가져갈 수 있는 metric 정보를 제공해 주면 된다.
또한 alertmanager를 통해 상태 변화에 대한 alert을 발생시킬 수도 있다.

Prometheus 설치해 보기

Prometheus의 경우 Docker 설치에 대한 가이드 문서가 있다.
https://prometheus.io/docs/prometheus/latest/installation/#using-docker

kubernetes deployment, service, pv, pvc yaml을 만들면 다음과 같다.
설치 시엔 ClusterRole, ClusterRoleBinding, ServiceAccount 같은 설정은 추가하지 않았고 루트 권한으로 지정하였다.
(실제 사용 시 이렇게 쓰지 않지만 개발 공부용으로 설치해 보는 거라 이렇게 설정함)

apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: prometheus-deployment 
  labels: 
    app: prometheus 
spec: 
  replicas: 1 
  selector: 
    matchLabels: 
      app: prometheus 
  template: 
    metadata: 
      labels: 
        app: prometheus 
    spec: 
      securityContext: 
        runAsUser: 0 
      containers: 
      - name: prometheus 
        image: prom/prometheus:latest 
        args: 
        - "--web.enable-lifecycle" 
        ports: 
        - name: prometheus 
          containerPort: 9090 
        volumeMounts: 
        - name: prometheus-data 
          mountPath: /prometheus/ 
      volumes: 
      - name: prometheus-data 
        persistentVolumeClaim: 
          claimName: prometheus-data-pvc
---
apiVersion: v1 
kind: Service 
metadata: 
  name: prometheus-service 
  labels: 
    app: prometheus  
spec: 
  selector: 
    app: prometheus 
  ports: 
    - port: 9090 
      targetPort: 9090
---
apiVersion: v1 
kind: PersistentVolume 
metadata: 
  name: prometheus-data-pv 
  labels: 
    app: prometheus-data-pv 
spec: 
  storageClassName: "" 
  capacity: 
    storage: 2Gi 
  accessModes: 
    - ReadWriteOnce 
  hostPath: 
    path: /mnt/c/data/prometheus-data
---
apiVersion: v1 
kind: PersistentVolumeClaim 
metadata: 
  name: prometheus-data-pvc 
spec: 
  volumeName: prometheus-data-pv 
  storageClassName: "" 
  accessModes: 
    - ReadWriteOnce 
  resources: 
    requests: 
      storage: 2Gi

데이터 생성 시 권한 문제가 있어서 루트 권한으로 생성하였다.

내 경우 ingress를 사용하였기 때문에 다음과 같이 ingress rule 설정을 추가하였다.

  - host: prometheus.bluesky.local 
    http: 
      paths: 
      - path: / 
        pathType: Prefix 
        backend: 
          service: 
            name: prometheus-service 
            port: 
              number: 9090

pv에서 지정한 path에 prometheus.yml을 다음과 같이 만들었다.

prometheus.yml에 설정된 scrape_configs의 job을 prometheus에서 주기적으로 수집하게 된다.
아래의 설정은 간단하게 static config로 주소를 지정하여 호출한 경우이다.

global: 
  scrape_interval: 10s 
  scrape_timeout: 9s 
  evaluation_interval: 10s 
scrape_configs: 
- job_name: bluesky-cloud 
  metrics_path: /actuator/prometheus 
  static_configs: 
  - targets: 
    - bluesky-cloud-admin-server-service.default.svc.cluster.local:30102 
    - bluesky-cloud-config-server-service.default.svc.cluster.local:30101 
    - bluesky-cloud-gateway-service.default.svc.cluster.local:30103 
    - bluesky-cloud-netflix-eureka-server-1-service.default.svc.cluster.local:30100 
    - bluesky-cloud-netflix-eureka-server-2-service.default.svc.cluster.local:30100 
- job_name: bluesky-project 
  metrics_path: /actuator/prometheus 
  static_configs: 
  - targets: 
    - bluesky-api-blog-service.default.svc.cluster.local:30132 
    - bluesky-api-board-service.default.svc.cluster.local:30133 
    - bluesky-api-bookkeeping-service.default.svc.cluster.local:30130 
    - bluesky-api-user-service.default.svc.cluster.local:30131 
    - bluesky-web-gate-service.default.svc.cluster.local:30122 
    - bluesky-web-swagger-ui-service.default.svc.cluster.local:30120

호출하여 prometheus가 잘 뜨는지 확인하면 된다.
prometheus.yml에 설정한 job을 잘 모니터링하는지는 status -> targets에서 확인할 수 있고 설정을 잘 읽는지는 status -> configuration에서 확인하면 된다.

prometheus.yml을 수정한 경우 해당 변경 사항을 서버 재시작 없이 바로 적용하게 하려면 "--web.enable-lifecycle" 옵션을 활성화해주어야 한다.
활성화되면 `POST /-/reolad` 주소를 호출하여 변경 사항을 바로 적용할 수 있다.

fetch("/-/reload", { method: "POST"})

Spring Boot Application에서 prometheus에 제공하는 metric 정보 설정하기

application에서는 prometheus가 수집해갈 수 있도록 데이터를 제공하면 된다.

spring boot에서는 actuator에 prometheus 관련 설정을 추가하면 된다.

다음과 같이 의존성을 추가해 준다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

management.endpoints.web.exposure.include 설정이 전체 노출(*)이 아니라면 prometheus를 추가하면 된다.

이렇게 추가하면 해당 프로젝트의 /actuator/prometheus 주소를 호출해 보면 prometheus에서 저장할 metric 데이터를 확인할 수 있다.

# HELP spring_security_filterchains_LogoutFilter_after_total  
# TYPE spring_security_filterchains_LogoutFilter_after_total counter
spring_security_filterchains_LogoutFilter_after_total{security_security_reached_filter_section="after",spring_security_filterchain_position="0",spring_security_filterchain_size="0",} 8007.0
# HELP feign_Feign_seconds_max  
# TYPE feign_Feign_seconds_max gauge
feign_Feign_seconds_max{client="net.luversof.web.gate.board.client.BoardArticleClient",host="bluesky-api-board",method="findByBoardAlias",} 0.0
feign_Feign_seconds_max{client="net.luversof.web.gate.user.client.OAuth2AuthorizedClientClient",host="bluesky-api-user",method="saveAuthorizedClient",} 0.0
# HELP feign_Feign_seconds  
# TYPE feign_Feign_seconds summary
feign_Feign_seconds_count{client="net.luversof.web.gate.board.client.BoardArticleClient",host="bluesky-api-board",method="findByBoardAlias",} 5.0
feign_Feign_seconds_sum{client="net.luversof.web.gate.board.client.BoardArticleClient",host="bluesky-api-board",method="findByBoardAlias",} 2.0867994
feign_Feign_seconds_count{client="net.luversof.web.gate.user.client.OAuth2AuthorizedClientClient",host="bluesky-api-user",method="saveAuthorizedClient",} 9.0
feign_Feign_seconds_sum{client="net.luversof.web.gate.user.client.OAuth2AuthorizedClientClient",host="bluesky-api-user",method="saveAuthorizedClient",} 3.5686061

// 이하 생략

prometheus의 config 설정에 수집할 대상 application을 추가하면 주기적으로 수집하게 된다.

Grafana 설치해 보기

grafana의 kubernetes deployment, service는 다음과 같다.

apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: grafana-deployment 
  labels: 
    app: grafana 
spec: 
  selector: 
    matchLabels: 
      app: grafana 
  template: 
    metadata: 
      labels: 
        app: grafana 
    spec: 
      securityContext: 
        runAsUser: 0 
      containers: 
      - name: grafana 
        image: grafana/grafana:latest 
        ports: 
        - containerPort: 3000 
        volumeMounts: 
        - name: grafana-data 
          mountPath: /var/lib/grafana/ 
      volumes: 
      - name: grafana-data 
        persistentVolumeClaim: 
          claimName: grafana-data-pvc
---
apiVersion: v1 
kind: Service 
metadata: 
  name: grafana-service 
  labels: 
    app: grafana 
spec: 
  selector: 
    app: grafana 
  ports: 
    - port: 3000 
      targetPort: 3000
---
apiVersion: v1 
kind: PersistentVolume 
metadata: 
  name: grafana-data-pv 
  labels: 
    app: grafana-data-pv 
spec: 
  storageClassName: "" 
  capacity: 
    storage: 2Gi 
  accessModes: 
    - ReadWriteOnce 
  hostPath: 
    path: /mnt/c/data/grafana-data
---
apiVersion: v1 
kind: PersistentVolumeClaim 
metadata: 
  name: grafana-data-pvc 
spec: 
  volumeName: grafana-data-pv 
  storageClassName: "" 
  accessModes: 
    - ReadWriteOnce 
  resources: 
    requests: 
      storage: 2Gi

Ingress Rule에 다음과 같이 추가하였다.

  - host: grafana.bluesky.local 
    http: 
      paths: 
      - path: / 
        pathType: Prefix 
        backend: 
          service: 
            name: grafana-service 
            port: 
              number: 3000

Grafana에서 Prometheus 호출해 보기

이제 grafana에 접속한다.
default account/password는 admin/admin이다.
초기 비번을 사용하면 최초 비밀번호를 변경하라는 안내가 뜬다.

우측 하단 Configuration의 Data sources 탭에서 Add new data source로 prometheus를 추가한다.

내 경우 위 kubernetes yaml설정대로 진행하였다면 호출 주소는 http://prometheus-service:9090이다.


save test로 연결이 정상적인지 확인 및 저장을 한다.

grafana는 dashboard를 사용자가 일일이 만들지 않고 이미 만들어진 dashboard를 사용할 수 있도록 import export를 제공한다.


아래 사이트에서 내가 사용하기 원하는 대시보드를 찾아 import 하면 된다.
https://grafana.com/grafana/dashboards/

내 경우 spring boot를 사용한 application을 prometheus의 prometheus.yml에 job으로 추가하였었다.
grafana dashboard를 찾아보니 Spring Boot 2.1 System Monitor란 dashboard가 보인다.


해당 dashboard의 id를 복사하여 grafana의 dashboard -> import 메뉴에서 import를 하면 손쉽게 dashboard가 생성되는 것을 확인할 수 있다.

(더 많은 내용이 dashboard에 보이지만 스샷은 상단 부분만 가져왔다.)

좀 더 설정해 보기

위에 prometheus.yml에 설정한 job의 주소는 kubernetes service 기준 호출 주소이다.
hpa 설정으로 여러 pod로 떠있는 경우 제대로 수집을 하지 못한다.

grafana dashboard에서 application과 instance로 구분된 조회 기준을 제공하는데 현재 설정으로는 application은 존재하지 않고 instance로 대상을 선택할 수 있는 상태이다.

kubernete의 deployment 단위로 application을 묶고 pod 단위로 job을 처리하도록 구성하는 것이 좋다.

기존의 prometheus.yml 설정을

global:  
  scrape_interval: 10s  
  scrape_timeout: 9s  
  evaluation_interval: 10s  
scrape_configs:  
- job_name: bluesky-cloud  
  metrics_path: /actuator/prometheus  
  static_configs:  
  - targets:  
    - bluesky-cloud-admin-server-service.default.svc.cluster.local:30102  
    - bluesky-cloud-config-server-service.default.svc.cluster.local:30101  
    - bluesky-cloud-gateway-service.default.svc.cluster.local:30103  
    - bluesky-cloud-netflix-eureka-server-1-service.default.svc.cluster.local:30100  
    - bluesky-cloud-netflix-eureka-server-2-service.default.svc.cluster.local:30100  
- job_name: bluesky-project  
  metrics_path: /actuator/prometheus  
  static_configs:  
  - targets:  
    - bluesky-api-blog-service.default.svc.cluster.local:30132  
    - bluesky-api-board-service.default.svc.cluster.local:30133  
    - bluesky-api-bookkeeping-service.default.svc.cluster.local:30130  
    - bluesky-api-user-service.default.svc.cluster.local:30131  
    - bluesky-web-gate-service.default.svc.cluster.local:30122  
    - bluesky-web-swagger-ui-service.default.svc.cluster.local:30120  
    - bluesky-web-swagger-ui2-service.default.svc.cluster.local:30121

다음과 같이 변경하였다.

global: 
  scrape_interval: 10s 
  scrape_timeout: 9s 
  evaluation_interval: 10s 
scrape_configs: 
- job_name: bluesky-spring-job 
  metrics_path: /actuator/prometheus 
  kubernetes_sd_configs: 
  - role: pod 
  relabel_configs: 
  - source_labels: [__meta_kubernetes_pod_container_name] 
    action: keep 
    regex: spring 
  - source_labels: [__meta_kubernetes_pod_label_app] 
    target_label: application
  - source_labels: [__meta_kubernetes_pod_name] 
    target_label: instance

prometheus는 다양한 service discovery 설정을 제공한다.
https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config

그중 kubernetes_sd_configs의 설정으로 pod별 설정을 선언하였고 pod container name이 spring인 모든 pod에 대해 application label을 pod label app으로 지정하였다.

이렇게 하면 pod label app을 기준으로 grafana application이 표시되고 각 application의 instance는 pod가 표시된다.

개인적으로 공부하는 것이기 때문에 spring boot application pod가 추가되면 prometheus에서 별다른 설정 없이 지표에 바로 추가되도록 설정하였다.

이제 기존의 검색조건은 다음과 같이 변경되었다.

각 application 별로 pod가 여러 개 뜨더라도 각 pod별로 지표가 수집되게 된다.


개인적으로 elasticsearch + kibana와 prometheus + grafana를 설치하고 사용해 보면서 아무래도 확장성은 grafana가 더 좋다고 느꼈다.

다만 아직 grafana에서 Loki를 추가하고 로그 데이터 검색을 해보지 않았기 때문에 로그 검색에 대해 grafana가 더 좋은지는 알 수 없다.

시간이 되면 Loki, Tempo를 추가하고 OpenTelemtry로 log, trace를 저장해 보면 좋을 것 같다.

반응형
profile

파란하늘의 지식창고

@Bluesky_

도움이 되었다면 광고를 클릭해주세요