카테고리 없음
[모니터링] OpenTelemetry, Tempo, Prometheus, Loki 적용하기
늘이
2024. 11. 9. 18:15
1. Opentelemetry 및 Prometetheus 의존성 추가
// Opentelemetry
implementation("io.opentelemetry:opentelemetry-api")
// Prometheus
implementation 'io.micrometer:micrometer-registry-prometheus'
2. PrometheusExemplarSamplerConfiguration.java
- Prometheus는 메트릭 지표를 수집하는 도구입니다. 예를 들어, 애플리케이션의 CPU 사용량, 메모리 사용량, 요청 속도 등의 성능 지표를 모니터링합니다.
- OpenTelemetry는 트레이스를 추적하여 분산 시스템에서 요청이 어떻게 처리되는지 파악할 수 있게 도와줍니다.
- Prometheus가 수집한 메트릭 지표에 대해 OpenTelemetry의 트레이스 정보를 추가하여 지표와 트레이스를 연결할 수 있도록 설정하는 역할을 합니다.
- 이 설정을 통해 지표와 분산 추적 데이터를 쉽게 결합하여 분석할 수 있게 됩니다.
import io.prometheus.client.exemplars.tracer.otel_agent.OpenTelemetryAgentSpanContextSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PrometheusExemplarSamplerConfiguration {
@Bean
public OpenTelemetryAgentSpanContextSupplier openTelemetryAgentSpanContextSupplier() {
return new OpenTelemetryAgentSpanContextSupplier();
}
}
1) 클래스 설명
- OpenTelemetry Agent 설정 클래스
- Prometheus와 OpenTelemetry를 연동하기 위해 작성된 클래스
- 주로 Exemplars라는 개념을 활용하여 Prometheus 지표와 OpenTelemetry 트레이스 간에 연결 고리를 만들기 위한 설정
2) 사용된 라이브러리 및 의존성
- io.prometheus.client.exemplars.tracer.otel_agent.OpenTelemetryAgentSpanContextSupplier:
- Prometheus에서 Exemplar 데이터를 지원하기 위한 클래스입니다.
- Exemplar는 지표 데이터에서 특정 지점에 대한 추가적인 컨텍스트 정보를 제공합니다. 이를 통해 트레이스나 로그와 같은 데이터에 연결할 수 있습니다.
- 이 클래스는 OpenTelemetry와 Prometheus 사이에서 트레이스 데이터를 연동하는 역할을 합니다.
- org.springframework.context.annotation.Bean:
- Spring에서 사용되는 애노테이션으로, 이 메서드가 Spring 컨테이너에 의해 관리되는 **빈(Bean)**임을 나타냅니다.
- org.springframework.context.annotation.Configuration:
- 이 애노테이션이 클래스에 붙어 있으면, 해당 클래스가 하나 이상의 빈을 정의하고 있으며, Spring 컨테이너에 의해 빈 관리가 될 것이라는 의미입니다.
- 즉, 이 클래스는 Spring 애플리케이션 설정을 담당하는 설정 클래스입니다.
3) 메서드 설명
메서드 이름: openTelemetryAgentSpanContextSupplier
- 이 메서드는 OpenTelemetryAgentSpanContextSupplier 객체를 Spring 컨테이너에 빈(Bean)으로 등록합니다.
- OpenTelemetryAgentSpanContextSupplier는 Prometheus와 OpenTelemetry의 트레이스 컨텍스트를 연동하기 위한 객체로, Exemplar 지표를 설정하는 데 필요합니다.
3. lagback-spring.xml
- 로그 패턴 설정
- 로그를 추적하고 적절하게 관리하기 위해 logback-spring.xml 파일에서 로그 패턴을 설정합니다.
- 로그 형식, 로깅 레벨, 로그가 기록될 위치 등을 정의합니다.
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} trace_id=%X{trace_id} span_id=%X{span_id} trace_flags=%X{trace_flags} %-5level %logger{36} - %msg%n" />
4. application.properties
- Metric 설정 및 Log 레벨 설정
- 메트릭 수집 및 로그 레벨을 설정하기 위해 application.properties 파일에서 관련 설정 추가
- OpenTelemetry와 Prometheus가 수집하는 메트릭의 기본 동작을 정의하고 로그 레벨을 설정합니다.
# Logging
logging.level.root=INFO
# Management Endpoints Configuration
management.endpoints.web.exposure.include=prometheus
# Metrics Configuration
management.metrics.tags.application=dewsconsultant_backend
management.metrics.distribution.percentiles-histogram.http.server.requests=true
5. Dockerfile
FROM curlimages/curl:8.2.1 AS download
ARG OTEL_AGENT_VERSION="1.33.2"
RUN curl --silent --fail -L "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OTEL_AGENT_VERSION}/opentelemetry-javaagent.jar" \
-o "$HOME/opentelemetry-javaagent.jar"
FROM eclipse-temurin:17-jdk-focal AS builder
COPY gradlew .
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .
COPY src src
RUN chmod +x ./gradlew
RUN chmod 755 ./gradlew
RUN ./gradlew bootJar --warning-mode all --stacktrace
FROM eclipse-temurin:17-jdk-focal
COPY --from=builder build/libs/*.jar app.jar
COPY --from=download /home/curl_user/opentelemetry-javaagent.jar /opentelemetry-javaagent.jar
ENTRYPOINT ["java", \
"-javaagent:/opentelemetry-javaagent.jar", \
"-jar", "/app.jar" \
]
1) OpenTelemetry Java 에이전트 다운로드 단계
OpenTelemetry Java 에이전트를 다운로드하여 나중에 Java 애플리케이션에 적용
FROM curlimages/curl:8.2.1 AS download
ARG OTEL_AGENT_VERSION="1.33.2"
RUN curl --silent --fail -L "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OTEL_AGENT_VERSION}/opentelemetry-javaagent.jar" \
-o "$HOME/opentelemetry-javaagent.jar"
- FROM curlimages/curl:8.2.1 AS download: curl을 이용하기 위해 curl 이미지를 사용합니다. 이 단계에서는 OpenTelemetry Java 에이전트를 다운로드합니다.
- ARG OTEL_AGENT_VERSION="1.33.2": OTEL_AGENT_VERSION이라는 빌드 시간 변수를 선언하여 OpenTelemetry Java 에이전트의 버전을 지정합니다.
- RUN curl --silent --fail -L: 지정된 버전의 OpenTelemetry Java 에이전트 JAR 파일을 GitHub Releases에서 다운로드합니다. --silent --fail 옵션은 에러가 발생하거나 연결이 실패할 경우 Docker 빌드를 중단하게 합니다.
- -o "$HOME/opentelemetry-javaagent.jar": 에이전트를 $HOME 디렉토리에 저장합니다.
2) 애플리케이션 빌드 단계
FROM eclipse-temurin:17-jdk-focal AS builder
COPY gradlew .
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .
COPY src src
RUN chmod +x ./gradlew
RUN chmod 755 ./gradlew
RUN ./gradlew bootJar --warning-mode all --stacktrace
- FROM eclipse-temurin:17-jdk-focal AS builder: Java 17 JDK가 설치된 Eclipse Temurin 이미지를 사용하여 애플리케이션을 빌드합니다. AS builder는 빌드 단계를 의미하며, 최종 이미지에는 포함되지 않는 빌드 작업을 위한 임시 레이어입니다.
- COPY gradlew .: gradlew 스크립트를 컨테이너로 복사합니다. 이 스크립트는 프로젝트를 빌드하는 데 필요한 Gradle Wrapper입니다.
- COPY gradle gradle: gradle 폴더를 복사합니다. 이 폴더에는 Gradle Wrapper 관련 설정 파일들이 포함됩니다.
- COPY build.gradle . 및 COPY settings.gradle .: Gradle 빌드 파일 및 프로젝트 설정 파일을 복사합니다.
- COPY src src: Java 소스 코드를 src 디렉토리로 복사합니다.
- RUN chmod +x ./gradlew 및 chmod 755 ./gradlew: gradlew 파일에 실행 권한을 부여합니다.
- RUN ./gradlew bootJar --warning-mode all --stacktrace: Spring Boot 애플리케이션을 빌드하여 실행 가능한 JAR 파일을 생성합니다. --warning-mode all은 모든 경고를 출력하고, --stacktrace는 에러 발생 시 전체 스택 트레이스를 출력합니다.
3) 최종 이미지 빌드 및 실행 단계
FROM eclipse-temurin:17-jdk-focal
COPY --from=builder build/libs/*.jar app.jar
COPY --from=download /home/curl_user/opentelemetry-javaagent.jar /opentelemetry-javaagent.jar
ENTRYPOINT ["java", \
"-javaagent:/opentelemetry-javaagent.jar", \
"-jar", "/app.jar" \
]
- FROM eclipse-temurin:17-jdk-focal: 다시 Java 17 JDK 이미지를 사용하여 최종 애플리케이션을 실행합니다.
- COPY --from=builder build/libs/*.jar app.jar: 빌드 단계에서 생성된 Spring Boot JAR 파일을 최종 이미지로 복사합니다. 빌드 단계(builder)에서 생성된 JAR 파일을 **app.jar**로 복사합니다.
- COPY --from=download /home/curl_user/opentelemetry-javaagent.jar /opentelemetry-javaagent.jar: 다운로드 단계(download)에서 받은 OpenTelemetry 에이전트를 최종 이미지로 복사합니다.
- ENTRYPOINT:
- java -javaagent:/opentelemetry-javaagent.jar -jar /app.jar: 애플리케이션을 실행할 때, OpenTelemetry Java 에이전트를 적용하여 Java 애플리케이션의 성능 추적 데이터를 수집하도록 설정합니다.
- -javaagent:/opentelemetry-javaagent.jar: Java 에이전트가 app.jar 실행 시 함께 로드되어 OpenTelemetry 트레이싱 기능을 활성화합니다.
- -jar /app.jar: Spring Boot 애플리케이션을 JAR 파일로 실행합니다.
6 .docker-compose.yaml
- OpenTelemetry와 Loki를 통한 로그 수집 및 모니터링을 지원하기 위해 docker-compose.yaml 파일을 사용한다.
- 아래 구성은 Loki를 사용하여 Docker 컨테이너의 로그를 수집하고, OpenTelemetry를 통해 트레이스 데이터를 보내는 구성입니다.
- Docker 컨테이너의 로그를 Loki로 전송하고, OpenTelemetry를 통해 Tempo로 트레이스를 전송합니다.
# docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions
x-logging: &default-logging
driver: loki
options:
loki-url: 'http://<LOKI_SERVER_IP>:50007/api/prom/push'
loki-pipeline-stages: |
- multiline:
firstline: '^\d{4}-\d{2}-\d{2} \d{1,2}:\d{2}:\d{2}'
max_wait_time: 3s
- regex:
expression: '^(?P<time>\d{4}-\d{2}-\d{2} \d{1,2}:\d{2}:\d{2},d{3}) (?P<message>(?s:.*))$$'
services:
example_backend:
image: <IMAGE_REGISTRY_URL>/example/backend
container_name: example_backend
restart: unless-stopped
ports:
- "50000:8080"
environment:
- TZ=Asia/Seoul
- OTEL_EXPORTER_OTLP_ENDPOINT=http://<TEMPO_SERVER_IP>:50008 # send traces to Tempo
- OTEL_SERVICE_NAME=example_backend # service name for Span
- OTEL_RESOURCE_ATTRIBUTES=compose_service=example_backend # add custom resource attribute to Span for Grafana Trace to Logs feature
- OTEL_METRICS_EXPORTER=none # disable metrics exporter
- MANAGEMENT_METRICS_TAGS_APPLICATION=example_backend # override default value of management.metrics.tags.application in app/src/main/resources/application.properties
logging: *default-logging
1) Loki Docker Loggin Driver 설정 상세 설명
# docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions
x-logging: &default-logging
driver: loki
options:
loki-url: 'http://<LOKI_SERVER_IP>:50007/api/prom/push'
loki-pipeline-stages: |
- multiline:
firstline: '^\d{4}-\d{2}-\d{2} \d{1,2}:\d{2}:\d{2}'
max_wait_time: 3s
- regex:
expression: '^(?P<time>\d{4}-\d{2}-\d{2} \d{1,2}:\d{2}:\d{2},d{3}) (?P<message>(?s:.*))$$'
- driver: loki: Docker 컨테이너의 로그를 Loki로 전송하도록 설정하는 부분입니다. Loki는 로그를 수집하고, 이를 Grafana와 같은 도구를 통해 시각화할 수 있습니다.
- loki-url: Loki가 로그를 수집하는 URL. 이 경우, http://<LOKI_SERVER_IP>:50007/api/prom/push로 로그를 전송합니다.
- loki-pipeline-stages: 로그 파이프라인을 정의하는 부분입니다. 로그 데이터를 수집하고 처리할 때, 각 로그가 어떻게 처리되는지를 정의합니다.
- multiline: 다중 라인 로그 처리를 위한 설정입니다. 로그가 여러 줄로 나뉠 수 있을 때 이를 하나의 로그로 묶어 처리합니다.
- firstline: 첫 번째 줄을 정의하는 정규 표현식입니다. 여기서는 로그의 첫 번째 줄이 날짜와 시간 형식 (YYYY-MM-DD HH:MM:SS)을 포함할 때 이를 첫 번째 줄로 인식합니다.
- max_wait_time: 로그가 묶이는 최대 대기 시간을 정의합니다. 여기서는 3초로 설정되어 있습니다.
- regex: 로그에서 데이터를 추출하기 위한 정규 표현식입니다. 여기서는 로그의 시간과 메시지를 추출하도록 설정되어 있습니다.
- multiline: 다중 라인 로그 처리를 위한 설정입니다. 로그가 여러 줄로 나뉠 수 있을 때 이를 하나의 로그로 묶어 처리합니다.
2) 서비스 정의 (dewsconsultant_backend)
services:
example_backend:
image: <IMAGE_REGISTRY_URL>/example/backend
container_name: example_backend
restart: unless-stopped
ports:
- "50000:8080"
environment:
- TZ=Asia/Seoul
- OTEL_EXPORTER_OTLP_ENDPOINT=http://<TEMPO_SERVER_IP>:50008 # send traces to Tempo
- OTEL_SERVICE_NAME=example_backend # service name for Span
- OTEL_RESOURCE_ATTRIBUTES=compose_service=example_backend # add custom resource attribute to Span for Grafana Trace to Logs feature
- OTEL_METRICS_EXPORTER=none # disable metrics exporter
- MANAGEMENT_METRICS_TAGS_APPLICATION=example_backend # override default value of management.metrics.tags.application in app/src/main/resources/application.properties
logging: *default-logging
- image: dewsconsultant_backend라는 이름의 컨테이너는 지정된 이미지로 실행됩니다. 여기서는 <IMAGE_REGISTRY_URL>/example/backend에서 백엔드 애플리케이션 이미지를 사용합니다.
- container_name: 컨테이너의 이름을 example_backend로 설정합니다.
- restart: unless-stopped: 컨테이너가 비정상적으로 종료될 경우 자동으로 다시 시작됩니다.
- ports: 로컬 포트 50000을 컨테이너의 8080 포트로 매핑하여 애플리케이션에 접근할 수 있습니다.
OpenTelemetry 환경 변수 설정
- TZ=Asia/Seoul:
- 이 변수는 컨테이너의 시간대를 설정하는 환경 변수입니다. 여기서는 Asia/Seoul 시간대를 사용하도록 지정되어 있습니다. 이는 로그나 메트릭에 기록되는 시간대를 현지 시간에 맞추는 데 중요한 역할을 합니다.
- OTEL_EXPORTER_OTLP_ENDPOINT=http://<TEMPO_SERVER_IP>:50008:
- OpenTelemetry Exporter Endpoint를 설정하는 환경 변수입니다.
- OpenTelemetry에서 수집된 트레이스 데이터를 지정된 엔드포인트로 전송합니다.
- 여기서 <TEMPO_SERVER_IP>:50008는 Tempo 서버의 IP 주소와 포트를 나타내며, 트레이스 데이터를 Tempo 서버로 전송하기 위해 사용됩니다.
- OTEL_SERVICE_NAME=example_backend:
- OpenTelemetry에서 생성되는 스팬(Span)의 서비스 이름을 지정하는 환경 변수입니다.
- example_backend는 이 서비스에서 생성된 트레이스를 구분하기 위해 사용됩니다.
- 나중에 Grafana 같은 도구를 통해 트레이스를 시각화할 때, 이 서비스 이름을 기준으로 트레이스를 그룹화하고 분석할 수 있습니다.
- OTEL_RESOURCE_ATTRIBUTES=compose_service=example_backend:
- OpenTelemetry 리소스 속성을 추가하는 환경 변수입니다.
- 여기서는 compose_service=example_backend라는 커스텀 리소스 속성을 추가하여 Grafana의 Trace to Logs 기능에서 Span과 로그를 연동할 때 추가적인 정보로 사용할 수 있게 됩니다.
- 이를 통해 특정 스팬이 example_backend 서비스와 연관되어 있다는 메타데이터를 추가로 제공합니다.
- OTEL_METRICS_EXPORTER=none:
- OpenTelemetry의 메트릭 수집기(Exporter)를 비활성화하는 설정입니다.
- none으로 설정하여 메트릭 수집을 하지 않도록 하고 있습니다. 즉, 이 서비스에서는 메트릭 데이터를 OpenTelemetry로 전송하지 않습니다. 트레이스에만 집중하고 메트릭 데이터는 제외하는 경우 유용합니다.
- MANAGEMENT_METRICS_TAGS_APPLICATION=example_backend:
- Spring Boot 애플리케이션에서 관리용 메트릭의 태그를 설정하는 변수입니다.
- 여기서 example_backend라는 값을 주어, 메트릭 데이터를 조회하거나 분석할 때 이 애플리케이션에 관련된 메트릭임을 명확하게 표시합니다.
- Spring Boot의 management.metrics.tags.application 기본값을 덮어쓰고, 해당 애플리케이션과 관련된 태그를 명시적으로 정의합니다.
7. Jenkinsfile
기존에 Dockerfile로 이미지 빌드, 배포 했던 것을 docker-compose로 실행하도록 수정
1) 수정전
sh 'ssh -o StrictHostKeyChecking=no -p 2022 user@{IP} "docker rm -f example_backend || true"' // 삭제하려는 컨테이너가 없을 경우에도 스크립트가 계속 실행됩니다.
sh 'ssh -o StrictHostKeyChecking=no -p 2022 user@{IP} "docker rmi -f {IP}:50003/example/backend || true"' // 삭제하려는 이미지가 없을 경우에도 스크립트가 계속 실행됩니다.
sh 'ssh -o StrictHostKeyChecking=no -p 2022 user@{IP} "docker run --name example_backend -d --restart always -p 50000:8080 {IP}:50003/example/backend:latest"'
2) 수정 후
sh 'scp -o StrictHostKeyChecking=no -P {ssh포트번호} docker-compose.yaml user@{서버IP}:/home/user/'
sh 'ssh -o StrictHostKeyChecking=no -p {ssh포트번호} user@{서버IP} "cd /home/user"'
sh 'ssh -o StrictHostKeyChecking=no -p {ssh포트번호} user@{서버IP} "docker rm -f {기존컨테이너명}"'
sh 'ssh -o StrictHostKeyChecking=no -p {ssh포트번호} user@{서버IP} "docker rmi -f {이미지저장위치}"'
sh 'ssh -o StrictHostKeyChecking=no -p {ssh포트번호} user@{서버IP} "docker compose up -d"'
sh 'ssh -o StrictHostKeyChecking=no -p {ssh포트번호} user@{서버IP} "docker compose ps"'
sh 'ssh -o StrictHostKeyChecking=no -p {ssh포트번호} user@{서버IP} "rm -f docker-compose.yaml"'