꿀벌 커뮤니티 프로젝트를 진행하면서 elk 로깅 환경 구축을 위해 테스트한 내용을 기록하려고 한다.
개발 환경
Python
Fast API
ElasticSearch 7.16
Logstash 7.16
Kibana 7.16
하나의 서버에 ELK 기술을 하나의 도커 컴포즈로 묶어서 세팅을 했다.
클라우드 환경 즉, 리눅스 환경에서 ELK를 docker로 띄워야 한다.
1. ELK docker-compose.yml 생성
# docker-compose.yml
version: '3'
services:
elasticsearch:
restart: unless-stopped
build: ./elasticsearch
container_name: elasticsearch
volumes:
- ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro,z
- ./elasticsearch/data:/usr/share/elasticsearch/data
environment:
- TZ=Asia/Seoul
- ELASTIC_PASSWORD=elasticpassword
- node.name=elasticsearch ## 이 이름에 큰 의미 없음, 왜냐하면 싱글노드로 운영할 계획이기 때문에
- bootstrap.memory_lock=true
- discovery.type=single-node ## 단일노드모드로 사용하겠다는 뜻, 엘라스틱서치는 실제로는 멀티노드로 많이 사용한다.
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
ports:
- 9200:9200 ## 엘라스틱 서치의 기본포트는 9200 이다.
kibana:
restart: unless-stopped
build: ./kibana
container_name: kibana
environment:
- TZ=Asia/Seoul
volumes:
- ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml:ro,z
ports:
- "5601:5601" ## 키바나의 기본포트는 5601 이다.
depends_on:
- elasticsearch
logstash:
restart: unless-stopped
build: ./logstash
container_name: logstash
volumes:
- ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro,z
- ./logstash/pipeline:/usr/share/logstash/pipeline:ro,z
environment:
- TZ=Asia/Seoul
- "LS_JAVA_OPTS=-Xmx256m -Xms256m"
depends_on:
- elasticsearch
ports:
- "5044:5044"
기본적인 docker-compose.yml 파일 세팅은 위와 같고 나머지 세팅은 이전의 ELK 세팅을 참고.
# logstash.conf
input {
tcp {
port => 5044
}
}
output {
elasticsearch {
hosts => "elasticsearch:9200"
user => "elastic"
password => "elasticpassword"
index => "server-test"
}
}
logsatsh.conf 파일을 통해서 로그스태시 로그의 파이프라인을 설정해야 한다.
테스트에서는 TCP를 이용해서 로그를 전달하였고, elasticsearch에서 server-test 라는 인덱스에 전달되는 로그를 담는 파이프라인이다.
2. Python (Fast API) 로깅 생성
ELK에서 로그를 수집하고 시각화 해주기 위해서는 백엔드 즉, 서버쪽에서 로그를 생성해주어야 한다.
원래는 Nginx의 로그를 사용하면 되지만 세부적으로 로그를 관리하기 위해서는 백엔드 내에서 코드를 통해서 로그를 직접 생성해주면 된다.
import logging, logstash, re
def create_logger(logger_name):
logger = logging.getLogger(logger_name)
if len(logger.handlers) > 0: # 로거가 이미 존재하는 경우
return logger
logger.setLevel(logging.DEBUG)
log_format = logging.Formatter('\n[%(levelname)s|%(name)s|%(filename)s:%(lineno)s] %(asctime)s > %(message)s')
# 콘솔 스트림, 로그 파일 생성
console = logging.StreamHandler()
# file_handler = logging.FileHandler(filename='./test_elk.log')
# handler 별로 다른 level 설정
console.setLevel(logging.INFO)
# file_handler.setLevel(logging.DEBUG)
# handler 출력 format 지정
console.setFormatter(log_format)
# file_handler.setFormatter(log_format)
# logger에 handler 추가
logger.addHandler(console)
# logger.addHandler(file_handler)
# logstash에 TCP로 전송하는 핸들러
# stash = logstash.TCPLogstashHandler('{host}', 5044, version=1)
# stash.setFormatter(log_format)
# logger.addHandler(stash)
logger.addHandler(logstash.TCPLogstashHandler('honeybees.community', 5044, version=1))
return logger
router = APIRouter()
board_logger = create_logger('Board-log')
@router.get("", response_model=List[BoardRead], status_code=status.HTTP_200_OK)
def read_boards(
session: Session = Depends(get_session),
offset: int = 0,
limit: int = Query(default=100, lte=100),
request: Request = Request,
user_agent: str = Header(None)
):
# 로그 생성
msg = {
'information': ip_browser(request),
'message': scripts.board_find_all(json(session), 'Board Find All')
}
board_logger.info(msg)
# print(user_agent)
return board_service.find_all(session, offset=offset, limit=limit)
logstash.TCPLogstashHandler() 메서드를 이용해서
logstash가 있는 서버의 host와 port, version을 인자로 넘겨주어서 로그를 생성 후 로그스태시로 보낼 수 있다.
3. 결과
'Logging' 카테고리의 다른 글
[Logging] 로그 레벨 (1) | 2022.10.15 |
---|---|
좋은 로그(Log)에 대하여 (2) | 2022.10.07 |