Skip to content

πŸš€ Redis + ELK Stack + Spring Boot μ„€μ • κ°€μ΄λ“œΒ #103

@Tae4an

Description

@Tae4an

1. πŸ“‹ 사전 μ€€λΉ„

1.1 μ‹œμŠ€ν…œ μš”κ΅¬μ‚¬ν•­

  • 🐳 Docker와 Docker Compose μ„€μΉ˜
  • β˜• JDK 17 이상
  • πŸ’Ύ μ΅œμ†Œ 4GB RAM (ꢌμž₯ 8GB 이상)
  • πŸ’½ μ΅œμ†Œ 20GB μ—¬μœ  λ””μŠ€ν¬ 곡간

1.2 ν•„μš”ν•œ 포트

  • 6379: Redis
  • 9200: Elasticsearch
  • 5601: Kibana
  • 5044: Logstash
  • 8080: Spring Boot Application

2. πŸ› οΈ ELK + Redis μŠ€νƒ μ„€μ •

2.1 ν”„λ‘œμ νŠΈ ꡬ쑰 생성

mkdir -p elk-stack && cd elk-stack

# μ„œλΉ„μŠ€λ³„ 디렉토리 생성
mkdir -p redis/config \
        elasticsearch/setup \
        logstash/{config,pipeline} \
        kibana/config

# μ„€μ • 파일 생성
touch docker-compose.yml .env

2.2 ν™˜κ²½ λ³€μˆ˜ μ„€μ • (.env)

ELASTIC_PASSWORD=[YOUR_PASSWORD]
KIBANA_SYSTEM_PASSWORD=[YOUR_PASSWORD]
REDIS_PASSWORD=[YOUR_PASSWORD]

2.3 Docker Compose μ„€μ • (docker-compose.yml)

version: '3.8'

services:
  elasticsearch:
    image: elasticsearch:8.12.0
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=true
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"
    networks:
      - elk_network
    restart: unless-stopped

  kibana:
    image: kibana:8.12.0
    container_name: kibana
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=${KIBANA_SYSTEM_PASSWORD}
      - XPACK_ENCRYPTEDSILVEROBJECTS_ENCRYPTIONKEY=something_at_least_32_characters_long
      - XPACK_SECURITY_ENCRYPTIONKEY=something_at_least_32_characters_long
      - XPACK_REPORTING_ENCRYPTIONKEY=something_at_least_32_characters_long
    ports:
      - "5601:5601"
    networks:
      - elk_network
    depends_on:
      - elasticsearch
    restart: unless-stopped

  redis:
    image: redis:7.2
    container_name: redis
    command: redis-server --requirepass ${REDIS_PASSWORD}
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    networks:
      - elk_network
    restart: unless-stopped

  logstash:
    image: logstash:8.12.0
    container_name: logstash
    environment:
      - REDIS_PASSWORD=${REDIS_PASSWORD}
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml
    ports:
      - "5044:5044"
    networks:
      - elk_network
    depends_on:
      - elasticsearch
      - redis
    restart: unless-stopped

networks:
  elk_network:
    driver: bridge

volumes:
  redis_data:
  elasticsearch_data:

2.4 Logstash μ„€μ • (logstash/pipeline/logstash.conf)

input {
  redis {
    host => "redis"
    port => 6379
    password => "${REDIS_PASSWORD}"
    data_type => "list"
    key => "logstash"
    codec => json
  }
}

filter {
  if [message] =~ /^{.*}$/ {
    json {
      source => "message"
    }
  }

  date {
    match => [ "timestamp", "ISO8601" ]
    target => "@timestamp"
  }
}

output {
  elasticsearch {
    hosts => ["http://elasticsearch:9200"]
    user => "elastic"
    password => "${ELASTIC_PASSWORD}"
    index => "logstash-%{+YYYY.MM.dd}"
  }

  # λ””λ²„κΉ…μš© μ½˜μ†” 좜λ ₯
  stdout { codec => rubydebug }
}

3. 🌱 Spring Boot μ„€μ •

3.1 ν”„λ‘œμ νŠΈ 생성

  • ⚑ Spring Initializr μ‚¬μš©
  • πŸ”§ Java 17 선택
  • πŸ“¦ Spring Boot 3.x 선택

3.2 μ˜μ‘΄μ„± μΆ”κ°€ (build.gradle)

dependencies {
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    // Spring Data Redis
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'

    // Logback Redis Appender
    implementation 'com.cwbase:logback-redis-appender:1.1.6'

    // Jedis
    implementation 'redis.clients:jedis:3.1.0'

    // Logback Classic
    implementation 'ch.qos.logback:logback-classic'

    // Logstash Logback Encoder
    implementation 'net.logstash.logback:logstash-logback-encoder:7.4'
}

3.3 application.yml μ„€μ •

spring:
  data:
    redis:
      host: ${SERVER_URL}
      port: 6379
      password: ${REDIS_PASSWORD}

logging:
  config: classpath:logback-spring.xml
  level:
    root: INFO
    com.example: DEBUG

3.4 logback-spring.xml μ„€μ •

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <springProperty scope="context" name="SERVER_URL" source="SERVER_URL"/>

    <!-- μ½˜μ†” 좜λ ₯ μ„€μ • -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Redis Appender -->
    <appender name="REDIS" class="com.cwbase.logback.RedisAppender">
        <host>${SERVER_URL}</host>
        <port>6379</port>
        <password>${REDIS_PASSWORD}</password>
        <key>logstash</key>
        <type>redis</type>
        <layout class="net.logstash.logback.layout.LogstashLayout">
            <customFields>{"application":"test"}</customFields>
        </layout>
    </appender>

    <!-- 비동기 처리λ₯Ό μœ„ν•œ Async Appender -->
    <appender name="ASYNC_REDIS" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="REDIS" />
        <queueSize>512</queueSize>
        <discardingThreshold>0</discardingThreshold>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="ASYNC_REDIS" />
    </root>

    <logger name="com.example" level="DEBUG" additivity="false">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="ASYNC_REDIS" />
    </logger>
</configuration>

3.5 ν…ŒμŠ€νŠΈ 컨트둀러 μž‘μ„±

@RestController
@RequestMapping("/api")
@Slf4j
public class TestController {

    @GetMapping("/log-test")
    public String test() {
        log.info("=== Test Start ===");
        log.debug("Debug level test message");
        log.info("Info level test message with data: {}", new Date());
        log.warn("Warning level test message");
        return "test";
    }
}

4. ▢️ μ‹€ν–‰ 및 ν…ŒμŠ€νŠΈ

4.1 ELK μŠ€νƒ μ‹€ν–‰

docker-compose up -d

4.2 Spring Boot μ‹€ν–‰

./gradlew bootRun

4.3 둜그 생성 ν…ŒμŠ€νŠΈ

curl http://localhost:8080/api/log-test

4.4 둜그 확인 방법

Redis 둜그 확인

docker exec -it redis redis-cli -a ${REDIS_PASSWORD}
LLEN logstash
LRANGE logstash -1 -1

Kibana 둜그 확인

  1. 🌐 http://localhost:5601 접속
  2. πŸ“Š Kibana > Discover 메뉴 이동
  3. πŸ” 인덱슀 νŒ¨ν„΄: logstash-* μ„€μ •
  4. ⏰ μ‹œκ°„ λ²”μœ„ μ„€μ • ν›„ 쑰회

5. πŸ”„ 둜그 μˆ˜μ§‘ ν”„λ‘œμ„ΈμŠ€

graph LR
    A[Spring Boot] -->|둜그 생성| B[Redis LIST]
    B -->|μˆ˜μ§‘| C[Logstash]
    C -->|μ €μž₯| D[Elasticsearch]
    D -->|μ‹œκ°ν™”| E[Kibana]
Loading

6. πŸ’‘ 참고사항

  • πŸ“ λͺ¨λ“  λΉ„λ°€λ²ˆν˜ΈλŠ” λ°˜λ“œμ‹œ μ•ˆμ „ν•˜κ²Œ 관리
  • πŸ”’ ν”„λ‘œλ•μ…˜ ν™˜κ²½μ—μ„œλŠ” λ³΄μ•ˆ μ„€μ • μΆ”κ°€ ν•„μš”
  • πŸ”„ λ©”λͺ¨λ¦¬ 섀정은 μ‹œμŠ€ν…œ ν™˜κ²½μ— 맞게 μ‘°μ •
  • πŸ“Š Kibanaμ—μ„œ λŒ€μ‹œλ³΄λ“œ κ΅¬μ„±μœΌλ‘œ λͺ¨λ‹ˆν„°λ§ κ°•ν™” κ°€λŠ₯

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentation

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions