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 단일 애플리케이션¶
- 소스 코드 다운로드
- 내장 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 파일 수정
- 로그 설정
ruoyi-admin/src/main/resources에 logback-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>
- 컴파일
프로젝트 루트 디렉터리에서 다음 명령어를 실행하여 컴파일합니다.
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 재시작¶
DDTrace¶
dd-trace-java를 다운로드합니다. 가능한 최신 버전을 다운로드하세요.
RUM 생성¶
- Guance에 로그인합니다.
사용자 분석을 선택하고애플리케이션 목록을 선택한 후애플리케이션 생성을 클릭합니다.애플리케이션 이름에ruoyi-admin을 입력하고,애플리케이션 ID는 직접 정의하거나랜덤 생성버튼을 클릭할 수 있습니다.애플리케이션 유형은web을 선택합니다. 오른쪽SDK 구성에는 여러 유형이 있으며, 여기서는CDN 동기 로드를 선택합니다. 해당 스크립트 내용을 복사해 두세요. 나중에 사용됩니다.생성버튼을 클릭하여 생성을 완료합니다.
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입니다.
로그¶
로그 상세로 이동하면 현재 로그에 해당하는 분산 추적 정보를 확인할 수 있습니다.
분산 추적 정보¶
분산 추적을 통해 로그 및 메트릭 정보를 확인할 수도 있습니다.







