개발/스프링

API 게이트웨이 및 ELK를 통해 간단한 중앙 관리자 서버를 만들어보자

brobro332 2025. 4. 17. 21:52
반응형

API 게이트웨이

# build.gradle
implementation 'org.springframework.cloud:spring-cloud-starter-gateway:4.2.0'

# application.yml
spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: 332-project-management-service
          uri: http://127.0.0.1:8081
          predicates:
            - Path=/project-management/**
          filters:
            - StripPrefix=1

        - id: 332-service-desk
          uri: http://127.0.0.1:8082
          predicates:
            - Path=/service-desk/**
          filters:
            - StripPrefix=1

server:
  port: 8080
  • `Spring Cloud Gateway`를 사용했다.
  • `uri`에 `localhost`를 입력할 경우 도메인을 인식 못하는 현상 발생
  • `predicates` 키를 통해 경로 엔드포인트 설정
  • `filters` 키의 `StripPrefix=1` 값을 통해 `predicates` 키에서 설정한 엔드포인트의 `/**`을 실제 API 요청 경로로 지정
  • 가령 `http://localhost:8080/project-management/project`로 `POST` 따위의 요청이 들어오면 `API` 게이트웨이가 요청을 `http://localhost:8081/project`로 리다이렉트 한다. 
  • 왜 쓰는가?
    • ⇒ `JWT` 따위의 인증 및 인가를 전담할 수 있다.
      ⇒ 너무 많은 요청을 보내는 클라이언트를 차단 및 제어 가능
      ⇒ `API` 버전 관리 가능

      ⇒ 서비스 별 상이한 `API` 계약을 표준화하여 외부에 제공

 

ELK + Logback

# docker-compose.yml
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.17.18
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    volumes:
      - esdata:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"
    networks:
      - efk

  kibana:
    image: docker.elastic.co/kibana/kibana:7.17.18
    container_name: kibana
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    depends_on:
      - elasticsearch
    networks:
      - efk

  logstash:
    image: docker.elastic.co/logstash/logstash:7.17.18
    container_name: logstash
    volumes:
      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml
      - ./logstash/pipeline:/usr/share/logstash/pipeline
      - ./sentinel-app/logs:/app/logs
    ports:
      - "5044:5044"
      - "9600:9600"
    depends_on:
      - elasticsearch
    networks:
      - efk

  gateway:
    build:
      context: ./sentinel-app
    container_name: sentinel-app
    ports:
      - "8080:8080"
    volumes:
    - ./sentinel-app/logs:/app/logs
    networks:
      - efk

volumes:
  esdata:

networks:
  efk:
  
# logstash.yml 
http.host: "0.0.0.0"
xpack.monitoring.enabled: false

# logstash.conf
input {
  file {
    path => "/app/logs/*.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
    codec => json
  }
}

filter {
  mutate {
    add_field => {
      "log_message" => "%{message}"
      "server_role" => "gateway"
    }
    remove_field => ["parsed", "host", "path", "message"]
  }
}

output {
  elasticsearch {
    hosts => ["http://elasticsearch:9200"]
    index => "sentinel-logs-%{+YYYY.MM.dd}"
  }

  stdout {
    codec => rubydebug
  }
}

# logback-spring.xml
<configuration>
    <include resource="net/logstash/logback/logback.xml"/>
    <property name="LOG_PATH" value="./logs"/>

    <appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/app-%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <fieldNames>
                <timestamp>@timestamp</timestamp>
                <level>level</level>
                <logger>logger</logger>
                <thread>thread</thread>
                <message>message</message>
            </fieldNames>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="JSON_FILE"/>
    </root>
</configuration>
  • 우선 `Logback`을 사용하여 로그 파일을 생성한다.
  • `<include resource="net/logstash/logback/logback.xml"/>` 라인을 통해 로그스태시에 맞는 `JSON` 형식의 로그 파일을 출력한다.
  • `<encoder class="net.logstash.logback.encoder.LogstashEncoder"> ... </encoder>` 라인을 통해 로그의 각 항목을 나타낸다.
  • 이 항목들은 엘라스틱서치에서 필터링 할 수 있는 메타데이터가 되는 것이다.
  • `Logstash`는 로그 파일을 읽어 엘라스틱서치로 전달한다.
  • `logstash.yml`의 `http.host` 키를 통해 타 서버에서도 로그를 전송할 수 있는데, `0.0.0.0`은 사실 보안 측면에서 조금 위험하다.
  • `logstash.conf` 설정을 통해 가공 및 필터링하여 인덱스로 생성 및 데이터를 주입한다.

 

프로젝트 내 로그 파일들

 

`Kibana` 화면

  • 위 화면에서 로그 파일과 동일한 일자에 데이터가 존재하는 것을 확인할 수 있다.

 

  • 위의 `logback-spring.xml`에서 `level`을 필드에 추가했기 때문에 엘라스틱서치 및 키바나에서 로그레벨 별로 필터링할 수 있게 되었다.

 

개선사항

  • 기존 프로젝트 관리 서비스에서 제공하던 `JWT` 인증 로직 게이트웨이 서버로 옮기기
  • 타 서비스의 로그 게이트웨이 서버로 전송하여 로그 통합 관리

 

이미지 출처

 

Contents

스프링 클라우드 게이트웨이 레퍼런스를 한글로 번역한 문서입니다. 버전은 3.0.2 기준입니다.

godekdls.github.io

 

[MDC/Filebeat/ELK stack] Webflux에서 로그에 UUID 표기 후 ELK stack으로 전송하기 (3)

Docker compose로 ELK stack + filebeat 연동&모니터링

medium.com