🛫 들어가며
기존 AWS를 이용해 서버를 구성하면 option 으로 CloudWatch
선택해 log 와 모니터링을 자동으로 관리해주었어요.
그러나 지금 환경은 클라우드 서비스(Saas) 를 사용 할 수 없는 환경으로 window
에서 웹 어플리케이션(Nest.js) 의 로그 관리를 해야 하기 때문에 여러가지 플랫폼을 사용 했는데요
거기에 대해서 정리를 해볼까해요
🧰 사용 플랫폼 (기술 스택)
✔️ Docker
Docker
는 컨테이너 기반 VM 으로 로컬 환경과 별개로 VM 을 사용해서 서버를 띄울 수 있는 기술이예요.
그래서 OS에 의존성이 걸릴 수 있는 플랫폼들을 좀 더 유연하게 사용할 수 있도록 도와주는 기술 입니다
✔️ Nest.js
로그 서버에 로그를 적제 하기 위해서 웹 어플리케이션으로 Nest.js
를 사용했어요.
개인 취양이지만 Node.js
같은 경량화 프레임워크는 코드 자율성이 높기 때문에 그만큼 불안한게 있더라구요
그래서 나름 규격화 되어 있는 프레임워크를 사용해서 코드 구성을 좀 더 탄탄하게 했어요
✔️ Promtail
웹 어플리케이션에서 log 파일을 생성했다면 그 log 파일을 수집해 Loki
로 전달해주는 역할을 하는 플랫폼 입니다.
✔️ Loki
전달 받은 log 들을 저장하고 쿼리를 통해 원하는 log 정보를 얻을 수 있도록 도와주는 플랫폼입니다.
✔️ Grafana
대표적인 모니터링 플랫폼으로 GUI가 깔끔하게 구성되어 있어서 선택 했어요 Grafana
는 Loki
와 연동을 할 수 있어 로그 쿼리를 보다 깔끔하게 요청 할 수 있고, 실시간 로그현황도 볼 수 있는 플랫폼입니다.
🗺️ 구성도
Nest.js
처음으로 로그를 쌓을 웹 어플리케이션이 필요해요
로그를 파일로 쌓기 위해선 winston
이라는 라이브러리가 필요해요 그리고 실질적으로 winston-daily-rotate-file
에서 winston
의 로그를 추출해서 log 파일을 만들게 되요
📜 Logger.class
import winston from 'winston';
import DailyRotateFile from 'winston-daily-rotate-file';
const logger = winston.createLogger({
level: process.env.NODE_ENV === 'prod' ? 'info' : 'debug',
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.printf(({ timestamp, level, ...meta }) => {
const traceId = this.getTraceId();
return `${timestamp} [${traceId}] ${level.toUpperCase()} ${
Object.keys(meta).length ? JSON.stringify(meta) : ''
}`;
}),
),
transports: [
new winston.transports.Console(),
new DailyRotateFile({
dirname: logDir, // 로그 파일이 생성될 디렉토리
filename: '%DATE%.log', // 파일 이름 형식
datePattern: 'YYYY-MM-DD', // 날짜 형식
zippedArchive: false, // 오래된 로그를 압축
maxFiles: '14d', // 최대 보관 기간
}),
],
});
크게 총 3개의 key 가 존재해요
- level
- 로그 레벨은 prod(운영 서버) 에서만
info
레벨 까지 수집하고 나머지개발 서버
,QA 서버
등 에선debug
레벨까지 사용한다는 뜻이예요
- 로그 레벨은 prod(운영 서버) 에서만
- format
winston
를 사용해서 로그를 찍는 다면 다음과 같은 포멧으로 찍어라 라는 뜻이예요.
- transports
- 서버에 찍힌 로그들을 log 파일로 만들기 위한 초기 값이예요
이 세가지 중에서 로그를 찍기 위한 key 는 transports
예요
transports
new winston.transports.Console()
란 format으로 설정한 로그를console
즉 터미널로 출력하는 거예요new DailyRotateFile
란winston-daily-rotate-file
라이브러리에서 사용할 수 있는 class 예요
해당 설정을 해줘야 log 파일을 생성하게 돼요.
만약 서버의 저장용량이 적을시엔maxSize
를 설정 할 수 있는데 그렇게 되면promtail
에서maxSize
이후에 생성되는 로그파일을 인식하지 못해서 로그가 누락되는 경우가 있어 주의 해야 해요.
이러한 설정을 class 에 생성자에 해도 좋고 따로 메서드로 빼서 호출해서 사용해도 좋을 것 같아요
Docker
Docker 를 구성하기 위해 docker-compose.yml
을 사용했어요docker cli
를 사용해도 되지만 저는 스크립트로 관리해 재사용하는 것을 좋아하기 때문에 파일로 관리하게 되었어요
📜 docker-compose.yml
version: '3.8'
services:
loki:
image: grafana/loki:latest
container_name: loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
- app
restart: always
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3111:3000"
volumes:
- ./grafana-data:/var/lib/grafana
networks:
- app
restart: always
promtail:
image: grafana/promtail:latest
container_name: promtail
volumes:
- ./promtail-config.yml:/etc/promtail/config.yml
- C:/ProgramData/Jenkins/.jenkins/workspace:/var/app # please change path
networks:
- app
depends_on:
- loki
command:
- -config.file=/etc/promtail/config.yml
restart: always
networks:
app:
external: true
name: app
크게 보자면 미리 만들어둔 app
네트워크에서 컨테이너를 실행하게 되어 있어요
이렇게 하나의 네트워크로 묶에 두면 네트워크 내에 있는 컨테이너들 간의 데이터 통신이 훨신 편해지게 돼요.
예를 들면 promtail
에서 loki
로 로그 데이터를 넘겨야 한다면 http://loki:3100/loki/api/v1/push
다음과 같이 도메인을 컨테이너 네임으로 설정을 할 수 있어요
promtail
->http://loki:3100/loki/api/v1/push
->loki
✨ 도메인을 컨테이너 네임으로 사용 가능
앞서 설명드린 기술 스텍들을 docker-compose
를 통해 컨테이너를 구성하면 돼요
여기서 중요한것이 promtail
의 config 파일을 생성하여 Nest.js
에서 생성한 log 파일을 트레킹하고 loki
에세 전송하는 스크립트를 구성해야 해요
📜 promtail-config.yml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: your_job_name
static_configs:
- targets:
- localhost
labels:
job: your_job_name
app: backend
__path__: your_log_path
server
내에 작성된 내용들은 서버 포트를 설정하는 거예요http_listen_port
는 promtail
서버의 HTTP 포트를 9080 으로 설정하겠다예요
그러나 docker 컨테이너를 만들 때 따로 외부 포트를 설정 해두지 않았기 때문에 사용하지 않아요
grpc_listen_port
는 grpc 포트를 설정하는 것이지만 0 을 통해서 설정 하지 않았어요
clients
는 로그 데이터를 전송하기 위해 loki
API 를 사용해요
실직적으로 로그를 수집하기 위한 설정은 scrape_configs
여기서 하게 돼요job_name
을 통해 수집한 로그를 그룹화 하고targets
를 localhost
로 하여 promtail
의 로컬에서 log 를 수집 하게 돼요
여기서 잠깐!!
❓ 어떻게promtail
컨네이너 내에서Nest.js
에서 생성된 로그를 수집하는거지?
->docker-compose
에서promtail
컨테이너를 만들 때volume
경로를 통해 local 환경의 log 데이터를promtail
컨테이너 내의 log 데이터와 매핑 시켜놓았기 때문에 가능합니다!
labels
에선 중요한건 __path__
값 입니다. volume
으로 매핑 시켜놓은 로그 데이터를 지정해 놓아야 수집을 하기 때문이예요
이떄 __path__
는 target
이 localhost
이기 때문에 로컬 환경이 아닌 컨테이너 환경의 log 파일 path 를 지정 해야 해요
그리고
docker-compose up -d --build
명령어를 통해 컨테이너를 실행 시켜요
😀 실행
컨테이너에 설정한 grafana 포트로 접속합니다
초기 로그인 정보는 admin / admin 이예요
Loki의 기능을 사용하기 위해서 Data source 를 연결 설정 해야해요
해당 버튼을 눌러 주고
loki 를 찾아 줍니다.
docker network 가 app 으로 묶여 있기 때문에 garafana 역시 loki 컨테이너를 접근 할 때 컨테이너 네임을 도메인으로 사용해도 돼요
버튼을 누르면 loki 셋팅이 돼요
이제 수집한 로그들을 loki를 통해 확인 할 수 있어요 Explore를 들어가세요
좌측상단에 Loki datasource 를 선택해요
Label filters 는 앞서 promtail.config.yml 에 설정한 값을 넣어줘야 해요
Line contains 에 찾고 싶은 문자열을 입력하고 검색 하게 되면 아래에 원하는 로그 데이터가 필터링 되서 보이게 되요
🛬 마치며
AWS 를 사용해서 여러가지 옵션을 통해 로그관리를 자동으로 해주는 서비스를 사용해보는 것도 좋지만 이렇게 직접 커스텀마이징 해서 로그 서버를 구성하니까 좋았고
어떻게 웹 어플리케이션의 log 가 어떤식으로 로그를 관리하는 서버로 넘어가는지 아는 의미 있는 시간이 되었어요
추후엔 Slack 과 logger 서버를 사용해서 에러가 났을때 slack 으로 알람이 가게 되고 Detail 버튼을 slack 에서 눌렀을때 만들어 놓은 log 서버로 이동하게 되는 구성을 포스팅 해볼까 해요
긴글 봐주셔서 감사해요~
'Server' 카테고리의 다른 글
window 에서 Nginx+Nodejs+SSL 설정하기 (0) | 2024.12.26 |
---|---|
window 에서 nginx 설정 하기 (0) | 2024.12.26 |
댓글