콘텐츠로 이동

Tomcat에서 RuoYi 단일 애플리케이션 배포 시 관측 가능성 모범 사례

RuoYi 단일 애플리케이션을 기반으로 외부 Tomcat 환경에서 관측 가능성을 구현하는 모범 사례입니다.

구현 목표

  • 메트릭 정보 수집
  • 분산 추적(분산 추적) 정보 수집
  • 로그 정보 수집
  • RUM 정보 수집
  • 세션 리플레이
    • 사용자가 프런트엔드에 접속하는 일련의 과정에 대한 세션 녹화 정보로, 버튼 클릭, UI 조작, 체류 시간 등이 포함됩니다. 이를 통해 사용자의 실제 의도와 동작 재현을 파악할 수 있습니다.

버전 정보

  • Tomcat (9.0.81)
  • Spring Boot (2.6.2)
  • JDK (>=8)
  • DDTrace (>=1.0)
특별 안내

Spring Boot 프로젝트의 경우 Tomcat 메이저 버전이 Spring Boot에 내장된 Tomcat 메이저 버전과 일치해야 합니다. 그렇지 않으면 시작 시 오류가 발생할 수 있습니다.

RuoYi 단일 애플리케이션

  • 소스 코드 다운로드

RuoYi 단일 애플리케이션

git clone https://gitee.com/y_project/RuoYi.git
  • 내장 Tomcat 제거

프로젝트 루트 디렉터리의 pom.xml 수정

......
    <dependencyManagement>
        <dependencies>

            <!-- SpringBoot의 의존성 설정 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.5.15</version>
                <type>pom</type>
                <scope>import</scope>
                <!-- 내장 Tomcat 제거 -->
                <exclusions>
                    <exclusion>
                        <artifactId>spring-boot-starter-tomcat</artifactId>
                        <groupId>org.springframework.boot</groupId>
                    </exclusion>
                </exclusions>
            </dependency>
......
  • war 출력

ruoyi-admin 모듈의 pom.xml 파일 수정

<packaging>war</packaging>
  • 로그 설정

ruoyi-admin/src/main/resourceslogback-spring.xml을 추가합니다. 내용은 다음과 같습니다.

logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 로그 저장 경로 -->
    <property name="log.path" value="/home/root/ruoyi/logs" />
    <!-- 로그 출력 형식 -->
    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - [%method,%line] %X{dd.service} %X{dd.trace_id} %X{dd.span_id} - %msg%n" />

    <!-- 콘솔 출력 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>

    <!-- 시스템 로그 출력 -->
    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-info.log</file>
        <!-- 롤링 정책: 시간 기반 로그 파일 생성 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 로그 파일명 형식 -->
            <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 로그 최대 보관 기간 60일 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 필터링 레벨 -->
            <level>INFO</level>
            <!-- 일치 시 동작: 수신(기록) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 불일치 시 동작: 거부(기록 안 함) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-error.log</file>
        <!-- 롤링 정책: 시간 기반 로그 파일 생성 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 로그 파일명 형식 -->
            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 로그 최대 보관 기간 60일 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 필터링 레벨 -->
            <level>ERROR</level>
            <!-- 일치 시 동작: 수신(기록) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 불일치 시 동작: 거부(기록 안 함) -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 사용자 접근 로그 출력 -->
    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/sys-user.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 일 단위 롤백 -->
            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 로그 최대 보관 기간 60일 -->
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>

    <!-- 시스템 모듈 로그 레벨 제어 -->
    <logger name="com.ruoyi" level="info" />
    <!-- Spring 로그 레벨 제어 -->
    <logger name="org.springframework" level="warn" />

    <root level="info">
        <appender-ref ref="console" />
    </root>

    <!-- 시스템 동작 로그 -->
    <root level="debug">
        <appender-ref ref="file_info" />
        <appender-ref ref="file_error" />
    </root>

    <!-- 시스템 사용자 동작 로그 -->
    <logger name="sys-user" level="info">
        <appender-ref ref="sys-user"/>
    </logger>
</configuration> 
  • 컴파일

프로젝트 루트 디렉터리에서 다음 명령어를 실행하여 컴파일합니다.

mvn clean package

Maven이 설치되어 있지 않다면 먼저 Maven을 설치한 후 컴파일하세요.

[INFO] --- spring-boot:2.5.15:repackage (default) @ ruoyi-admin ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for ruoyi 4.7.7:
[INFO] 
[INFO] ruoyi .............................................. SUCCESS [  0.179 s]
[INFO] ruoyi-common ....................................... SUCCESS [  4.622 s]
[INFO] ruoyi-system ....................................... SUCCESS [  0.770 s]
[INFO] ruoyi-framework .................................... SUCCESS [  0.950 s]
[INFO] ruoyi-quartz ....................................... SUCCESS [  0.388 s]
[INFO] ruoyi-generator .................................... SUCCESS [  0.378 s]
[INFO] ruoyi-admin ........................................ SUCCESS [  4.554 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  12.287 s
[INFO] Finished at: 2023-10-13T16:30:12+08:00
[INFO] ------------------------------------------------------------------------

DataKit

  • DataKit 설치
  • 수집기 활성화

DataKit 설치

DataKit 설치 문서를 참조하세요.

DataKit에서 DDTrace 수집기 활성화

DDTrace 수집기는 애플리케이션 분산 추적 정보를 수집하는 데 사용됩니다. DDTrace 수집기 연동 문서를 참조하세요.

DataKit에서 로그 수집기 활성화

로그 수집기는 로그 정보를 수집하는 데 사용됩니다. 로그 수집기 연동 문서를 참조하세요.

다음 정보를 설정해야 합니다.

 logfiles = [
    "/home/liurui/ruoyi/logs/*.log",
  ]
  ## Add service tag, if it's empty, use $source.
  service = "ruoyi"

  ## Grok pipeline script name.
  pipeline = "ruoyi.p"
  • logfiles: 수집할 로그 파일 경로
  • service: 서비스 이름
  • pipeline: 로그 파싱

Pipeline 설정

Pipeline은 데이터 거버넌스에 사용되며, 주로 로그 정보를 추출하여 분산 추적 정보와 연결하는 데 사용됩니다.

datakit/pipeline/ 디렉터리에 ruoyi.p 파일을 생성합니다. 내용은 다음과 같습니다.

grok(_, "%{TIMESTAMP_ISO8601:time} %{NOTSPACE:thread_name} %{LOGLEVEL:status}%{SPACE}%{NOTSPACE:class_name} - \\[%{NOTSPACE:method_name},%{NUMBER:line}\\] %{DATA:service_name2} %{DATA:trace_id} %{DATA:span_id} - %{GREEDYDATA:msg}")

default_time(time,"Asia/Shanghai")

DataKit에서 StatsD 수집기 활성화

StatsD 수집기는 메트릭 정보를 수집하는 데 사용됩니다. StatsD 수집기 연동 문서를 참조하세요.

DataKit에서 RUM 수집기 활성화

RUM 수집기: RUM(Real User Monitor) 수집기는 웹 또는 모바일에서上报된 실제 사용자 모니터링(RUM) 데이터를 수집하는 데 사용됩니다. RUM 수집기 연동 문서를 참조하세요.

DataKit 재시작

DataKit 재시작

DDTrace

dd-trace-java를 다운로드합니다. 가능한 최신 버전을 다운로드하세요.

RUM 생성

  1. Guance에 로그인합니다.
  2. 사용자 분석을 선택하고 애플리케이션 목록을 선택한 후 애플리케이션 생성을 클릭합니다.
  3. 애플리케이션 이름ruoyi-admin을 입력하고, 애플리케이션 ID는 직접 정의하거나 랜덤 생성 버튼을 클릭할 수 있습니다.
  4. 애플리케이션 유형web을 선택합니다. 오른쪽 SDK 구성에는 여러 유형이 있으며, 여기서는 CDN 동기 로드를 선택합니다. 해당 스크립트 내용을 복사해 두세요. 나중에 사용됩니다.
  5. 생성 버튼을 클릭하여 생성을 완료합니다.

Tomcat

Tomcat 다운로드

해당 버전의 Tomcat을 다운로드합니다.

DDTrace 설정

Tomcat bin 디렉터리에 setenv.sh 스크립트 파일을 추가합니다.

export CATALINA_OPTS="-javaagent:/home/root/agent/dd-java-agent-1.14.0-guance.jar \
                      -Ddd.tags=env:test \
                      -Ddd.jmxfetch.enabled=true \
                      -Ddd.jmxfetch.statsd.host=localhost \
                      -Ddd.jmxfetch.statsd.port=8125 \
                      -Ddd.jmxfetch.tomcat.enabled=true\
                      -Dlogging.config=classpath:logback-spring.xml"
  • javaagent: ddtrace 경로 지정
  • Dlogging.config: 애플리케이션 로그를 logback 로그로 출력하도록 지정합니다. 애플리케이션 내부에서 log4j를 사용하는 경우 해당 파일을 지정하면 됩니다.

애플리케이션 배포

이미 패키징된 애플리케이션 RuoYi/ruoyi-admin/target/ruoyi-admin.war를 Tomcat의 webapps 디렉터리에 복사합니다.

Tomcat 시작

bin/startup.sh 실행

apache-tomcat-9.0.81/bin$ ./startup.sh 
Using CATALINA_BASE:   /home/root/middleware/apache-tomcat-9.0.81
Using CATALINA_HOME:   /home/root/middleware/apache-tomcat-9.0.81
Using CATALINA_TMPDIR: /home/root/middleware/apache-tomcat-9.0.81/temp
Using JRE_HOME:        /home/root/middleware/jdk/jdk-11.0.18
Using CLASSPATH:       /home/root/middleware/apache-tomcat-9.0.81/bin/bootstrap.jar:/home/root/middleware/apache-tomcat-9.0.81/bin/tomcat-juli.jar
Using CATALINA_OPTS:   -javaagent:/home/root/agent/dd-java-agent-1.14.0-guance.jar                       -Ddd.tags=env:test                       -Ddd.jmxfetch.enabled=true                       -Ddd.jmxfetch.statsd.host=localhost                       -Ddd.jmxfetch.statsd.port=8125                       -Ddd.jmxfetch.tomcat.enabled=true                      -Dlogging.config=classpath:logback-spring.xml
Tomcat started.

RUM 추가

Tomcat이 시작되면 자동으로 war 애플리케이션이 압축 해제됩니다. /webapps/ruoyi-admin/WEB-INF/classes/templates 디렉터리로 이동하여 include.html을 수정하고, 이전 단계에서 복사한 스크립트 코드를 head에 붙여넣습니다.

<head th:fragment=header(title)>
...
<script src="https://static.guance.com/browser-sdk/v3/dataflux-rum.js" type="text/javascript"></script>
<script>
  window.DATAFLUX_RUM &&
    window.DATAFLUX_RUM.init({
      applicationId: 'APP_ID',
      datakitOrigin: 'http://localhost:9529', // 프로토콜(// 포함), 도메인(또는 IP 주소)[및 포트 번호]
      env: 'production',
      version: '1.0.0',
      service: 'browser',
      sessionSampleRate: 100,
      sessionReplaySampleRate: 70,
      trackInteractions: true,
      traceType: 'ddtrace', // 선택 사항, 기본값 ddtrace, 현재 ddtrace, zipkin, skywalking_v3, jaeger, zipkin_single_header, w3c_traceparent 6가지 유형 지원
      allowedTracingOrigins: ['http://localhost:8080','http://localhost:8080/ruoyi-admin'],  // 선택 사항, trace 수집기에 필요한 헤더를 주입할 모든 요청 목록입니다. 요청의 origin 또는 정규식일 수 있습니다.
    });
    window.DATAFLUX_RUM && window.DATAFLUX_RUM.startSessionReplayRecording()
</script>
...
</head>
  • applicationId: 복사한 내용을 그대로 사용하면 됩니다.
  • datakitOrigin: RUM 데이터 보고를 수신할 DataKit 주소입니다.
  • allowedTracingOrigins: 백엔드 APM과 연결하기 위한 것입니다. 프런트엔드가 API를 호출하면 해당 인터페이스에 Trace에 필요한 헤더 정보가 추가됩니다.

결과

http://localhost:8080/ruoyi-admin에 접속합니다. 기본 사용자 이름: admin, 비밀번호: admin123입니다.

Img

로그

Img

로그 상세로 이동하면 현재 로그에 해당하는 분산 추적 정보를 확인할 수 있습니다.

Img

분산 추적 정보

Img

분산 추적을 통해 로그 및 메트릭 정보를 확인할 수도 있습니다.

Img

메트릭 정보

Img

RUM 대시보드

Img

세션 리플레이

Img

문서 평가

이 페이지가 도움이 되었나요?