시작
앱의 로그를 파일로 관리하라는 피드백을 받았습니다.
현재 애플리케이션을 도커에서 실행중이기 때문에 도커 외부에 저장해야합니다.
왜냐면 컨테이너의 데이터는 휘발성이니까요
앱 내부에서 파일로 저장하는게 더 좋은 방법인 것 같지만 이걸 할 당시에는 내부에서는 저장하지 않고 있었어요
서버에 들어가지 않고 로그를 확인할 수 있는 수단이 필요했기 때문에 일단 crontab과 shell 스크립트를 사용해서 로그를 저장하기로 했습니다.
- 구현 목표
- 1분마다 한 번씩 로그를 출력하고 저장
- 이전에 있는 로그와 중복되지 않도록 한다.
- 로그는 하루에 한번 다른 파일로 교체한다.
- 로그가 없으면 저장하지 않는다.
- 일단 crontab에 대해 대충 설명을 해보면
- 일정 시간마다 지정한 동작을 수행하도록 합니다.
- * * * * * echo “123123” 처럼 작성합니다
- 이건 1분에 한번씩 동작을 수행하는걸 의미합니다.
- 왼쪽부터 *은 분, 시, 일, 월, 요일을 의미합니다.
- 각 자리의 * 은 매분, 매시, 매일, 매월, 모든 요일을 의미하며
- * 1 * * * 은 매일 01시에 수행
- 각 자리의 * 은 매분, 매시, 매일, 매월, 모든 요일을 의미하며
- * * * * * echo “123123” 처럼 작성합니다
- 일정 시간마다 지정한 동작을 수행하도록 합니다.
crontab 설정
- 환경 : ubuntu 24.10
crontab은 기본적으로 깔려있기 때문에 설치 할 필요가 없습니다.
crontab - e
이 명령어를 사용해서 crontab 설장 파일로 들어갑니다.
# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').
#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
- 파일로 들어가면 이런 장면이 나옵니다
- 영어는 어렵지만 긴장하지 말고 목적만 달성하고 나올겁니다 ``` 0 1 * * * /home/logs/main/rotate-logs.sh # 매일 1시에 수행
-
-
-
-
- /home/logs/main/log-reloader.sh # 1분마다 수행 ```
-
-
-
- 일단 구현 목표인 매일 새로운 파일을 생성하기 위해 0 1 * * * 로 추가를 했어요.
- 그리고 1분마다 한 번씩 로그를 갱신하기 위해 * * * * *도 추가를 합니다.
-
뒤에 붙어있는 경로는 쉘 스크립트의 경로입니다 이제 만들러 갈겁니다.
shell script 작성
- crontab 설정을 해놨으니 이제 지정한 시간마다 계속 동작을 수행할거에요.
- 당연히 shell script는 안만들었으니까 crontab은 계속 헛수고만 하고있겠죠
#!/bin/bash
#### log-reloader.sh
# 로그 저장 디렉터리
LOG_DIR="/home/logs/main"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/main-latest.log"
# 마지막 로그 타임스탬프 저장 파일
TIMESTAMP_FILE="$LOG_DIR/last_timestamp"
# 마지막 실행 이후의 로그만 가져오기
if [[ -f "$TIMESTAMP_FILE" ]]; then
LAST_TIMESTAMP=$(cat "$TIMESTAMP_FILE")
docker logs --since "$LAST_TIMESTAMP" main >> "$LOG_FILE" 2>&1
else
docker logs main > "$TMP_LOG_FILE" 2>&1 # 실행한 적 없으면 타임 스탬프가 비어있음
fi
# 현재 타임스탬프 저장 (다음 실행을 위해)
date --utc +%FT%T > "$TIMESTAMP_FILE"
- 1분마다 로그를 갱신하는 스크립트입니다.
- 로그 저장 디렉토리를 만들고 타임 스탬프 기준으로 도커에서 로그를 빼옵니다.
- 마지막에는 항상 실행한 시간을 갱신해줍니다. 그래야 다음 실행에서 이 시간을 기준으로 도커에서 로그를 출력해 가져옵니다.
#!/bin/bash
# rotate-logs.sh
# 로그 저장 디렉터리
LOG_DIR="/home/logs/main"
mkdir -p "$LOG_DIR"
# 새로운 로그 파일 이름 (형식: main-YYYYMMDDHHMMSS.log)
NEW_LOG_FILE="$LOG_DIR/main-$(date +'%Y%m%d%H%M%S').log"
# 마지막 로그 타임스탬프 저장 파일
TIMESTAMP_FILE="$LOG_DIR/last_timestamp"
# 새로운 로그를 임시 파일에 저장
TMP_LOG_FILE=$(mktemp)
# 마지막 실행 이후의 로그만 가져오기
# 타임스탬프가 있으면 이전에 한 번 실행이 되었기 때문에 로그가 있음
if [[ -f "$TIMESTAMP_FILE" ]]; then
LAST_TIMESTAMP=$(cat "$TIMESTAMP_FILE")
cat "$LOG_DIR/main-latest.log" > "$TMP_LOG_FILE" 2>&1
else
date --utc +%FT%T > "$TIMESTAMP_FILE" # 타임스탬프 업데이트
docker logs main > "$TMP_LOG_FILE" 2>&1 # 만약 없으면 전체 로그 저장
fi
# 로그가 비어 있는지 확인
if [[ -s "$TMP_LOG_FILE" ]]; then
# 심볼릭 링크를 최신 로그 파일로 업데이트
ln -sf "$NEW_LOG_FILE" "$LOG_DIR/main-latest.log"
else
# 로그가 없으면 임시 파일 삭제
rm "$TMP_LOG_FILE"
fi
- 하루에 한번 새로운 로그파일로 교체하는 스크립트입니다.
-
여기서는 타임 스탬프가 없을 때 빼고는 타임스탬프를 업데이트 하지 않습니다.
완성
이제 crontab이 계속 지장한 시간마다 shell script를 실행하며 새로운 로그를 생성 할겁니다.
단점
# 마지막 실행 이후의 로그만 가져오기
if [[ -f "$TIMESTAMP_FILE" ]]; then
LAST_TIMESTAMP=$(cat "$TIMESTAMP_FILE")
docker logs --since "$LAST_TIMESTAMP" main >> "$LOG_FILE" 2>&1
else
docker logs main > "$TMP_LOG_FILE" 2>&1 # 실행한 적 없으면 타임 스탬프가 비어있음
fi
######### 로그 100개 ############
# 현재 타임스탬프 저장 (다음 실행을 위해)
date --utc +%FT%T > "$TIMESTAMP_FILE"
- 임시 방편인 만큼 약간 문제가 좀 있습니다.
- 로그를 업데이트하고 타임스탬프를 업데이트 하기전 찰나의 순간에 발생된 로그가 무시 될 가능성이 있습니다.
- 약간의 중복을 허용하는걸로 해결 할 수 있지만 그냥 넘어가기로 했습니다.
-
여기에 쏟은 시간만큼 정이 들었지만 아쉽게도 조만간 버려야 할 것 같습니다,
- 그냥 docker의 volume을 사용 하세요