| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- AOP
- SEQUENCE
- 커뮤니티서버
- OOP
- 전전긍긍
- DDD
- Ajax
- 사면초가
- springsecurity
- MVC요청플로우
- 디스크i/o
- 클린아키텍처
- JDBC
- 공통기술
- 로그백
- TDD
- SpringLegacy
- string
- MVC
- 설정세팅
- 냄새라도
- model
- 단위테스트
- 이벤트핸들러등록
- 문제선택근거
- Transaction
- 공통규약
- JPA
- 이벤트핸들러this
- index
- Today
- Total
개발이군고구마
서버 디스크가 100% — 로그 파이프라인 본문
Jenkins 빌드 배포가 먹통이 되어 서버에 들어가보니 /engine 디렉토리 용량이 100%.
실제 발생한 이슈를 바탕으로, ELK 스택과 Jenkins가 함께 올라가 있는 서버에서 로그가 어떻게 흐르는지, 각 도구의 역할이 뭔지 분석.
1. stdout과 stderr — 로그는 어디서 시작되는가
로그의 출발점 : 어떤 프로그램이 실행되면, 운영체제는 그 프로세스에 기본적으로 세 가지 통로(파일 디스크립터)를 오픈
모든 프로세스는 태어날 때부터 세 개의 통로를 가지고 있고, 우리가 System.out.println()을 쓰든 Python의 print()를 쓰든 결국 그건 다 stdout으로 나오는 거다. 하지만, 어플리케이션이 로그를 "어떤 파일에 쓴다"고 설정했을 때는, 그 라이브러리(log4j, logback 등)가 내부적으로 파일 디스크립터를 직접 파일로 연결한 것이다. stdout/stderr를 거치지 않는다.
stderr → 오류 출력. ERROR, 예외 스택 트레이스가 주로 여기로
그리고 이 둘의 목적지는 실행 방법(터미널, nohup, systemd)에 따라 완전히 달라짐
2. 핵심 개념 4가지 — nohup / systemd / logrotate / journald
이 네 가지는 서로 비교 대상이 아니다. 각각 다른 문제를 해결하는 도구다.
nohup
SSH 접속이 끊기면 그 세션에서 실행된 프로세스도 같이 죽어버린다. nohup은 이걸 막기 위한 단순한 명령어다. 터미널 세션과 프로세스를 분리하는 것이 전부고, 로그 관리 기능은 없다. stdout/stderr를 nohup.out에 쏟아내는 것도 그냥 기본 동작이다.
systemd
Linux의 init 시스템이다. 서버가 부팅될 때 가장 먼저 뜨는 프로세스(PID 1)로, 이후 모든 서비스를 관리한다. 부팅 시 자동 시작, 프로세스가 죽으면 자동 재시작, 의존성 관리가 가능하고, stdout/stderr를 자동으로 journald에 넘긴다. nohup과 비교했을 때 관리 기능이 훨씬 많다.
journald
systemd가 관리하는 서비스들의 로그를 받아서 저장하는 로그 수집기다. /var/log/journal/에 바이너리 형태로 저장하고, 용량과 기간 설정으로 자동 로테이션해준다. systemd로 실행된 서비스는 별도 설정 없이 자동으로 여기로 로그가 들어간다.
logrotate
파일 기반 로그를 주기적으로 잘라주는 도구다. nohup.out처럼 파일에 무한정 쌓이는 로그를 날짜별로 분리하고, 오래된 건 압축하거나 삭제한다. cron으로 주기적으로 실행된다.
nohup.out 파일 디스크립터를 계속 물고 있다. 그냥 파일을 삭제하거나 교체하면 프로세스는 이미 없어진 파일에 계속 쓰려고 한다. copytruncate 옵션이 이걸 해결해준다. 내용을 복사해두고 원본을 비워버려서(truncate) 프로세스 재시작 없이 동작한다.3. 이슈 상황 — 서버 구성과 문제
테스트 스테이지 솔루션 서버 한 대에 아래 프로그램들이 다 함께 올라가 있었다.
PC 앱, MO 앱, 관리자 앱, 파트너사 앱 × 4개 stage = 총 16개 앱 인스턴스가 로그를 이 서버로 전송
/engine 디렉토리 용량이 100%에 도달하면서 Jenkins 빌드가 실패하기 시작했다. 원인을 추적해보니 두 곳에서 로그가 관리 없이 무한정 쌓이고 있었다.
- Jenkins 서버 로그(
jenkins.log) — 로테이션 없이 append - Vector 프로세스 로그(
nohup.out) — nohup 방식으로 실행 중, 무한 증가
4. ELK 로그 파이프라인 전체 흐름
앱 로그 데이터 흐름과 Vector 자체 서버 로그는 완전히 별개의 채널이다.
핵심: nohup.out을 지워도 앱 로그는 사라지지 않는다
Vector가 Kafka에서 읽어온 앱 로그 데이터는 Vector 프로세스의 메모리(RAM) 안에서 처리돼서 Elasticsearch로 전송된다. nohup.out은 이 흐름과 전혀 무관하다. nohup.out에는 Vector 프로세스 자신의 상태 메시지(INFO: 연결됨, ERROR: 전송 실패 등)만 담겨 있다.
nohup.out에는 Vector 자신의 상태 로그만 있을 뿐, Elasticsearch로 가는 데이터와 무관하다.
5. Jenkins 로그 구조 — systemd인데 왜 파일에 쌓이나
Jenkins는 systemd로 실행되고 있었다. 그런데 로그는 journald가 아니라 jenkins.log 파일에 쌓이고 있었다. 일반적인 상식으로는 이해가 안 가는 상황이다.
이유는 JVM 때문이다. Jenkins는 JVM 위에서 동작하고, 내부적으로 java.util.logging 프레임워크가 stdout으로 내보내기 전에 로그를 가로채서 파일에 직접 써버린다. systemd 입장에서는 Jenkins가 거의 아무것도 stdout에 출력하지 않는 프로세스처럼 보인다. 그래서 journald에는 Jenkins 서버 로그가 거의 들어오지 않는다.
/etc/default/jenkins 또는 /etc/sysconfig/jenkins 파일을 보면 이런 설정이 있다.
# Jenkins 서버 로그 경로 설정 JENKINS_LOG=/var/log/jenkins/jenkins.log
JVM이 이 경로를 읽어서 직접 파일에 쓰는 구조다. Vector(단순 프로세스)와 Jenkins(JVM 앱)가 같은 systemd 위에 있어도 로그 목적지가 달라지는 이유가 여기 있다.
| 항목 | 일반 프로세스 (Vector 등) | Jenkins (JVM) |
|---|---|---|
| stdout/stderr 처리 | systemd → journald 자동 수집 | JVM이 직접 파일에 write (우회) |
| 서버 로그 위치 | journaldjournalctl -u vector -f |
/var/log/jenkins/jenkins.log |
| 로그 관리 방법 | journald.conf 설정 | logrotate 별도 설정 |
| 빌드 로그 관리 | 해당 없음 | config.xml 보존 기간/개수 설정 |
6. 실제 조치 순서
용량 확보가 급선무였다. Vector를 종료하고 수십 GB까지 불어난 nohup.out을 삭제했다. 앱 로그 데이터 파이프라인과 무관한 파일이라 삭제해도 데이터 손실이 없다.
재시작 시 서버 로그를 아예 버리도록 했다.
nohup /bin/vector -C config > /dev/null 2>&1 &
빠르게 공간을 확보할 수 있지만, Vector 내부 에러를 아예 볼 수 없어진다는 단점이 있어서 어디까지나 임시 처치다.
/etc/systemd/system/vector.service 파일을 작성한다.
[Unit] Description=Vector log pipeline After=network.target [Service] ExecStart=/bin/vector -C /etc/vector/config Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target
systemctl daemon-reload systemctl enable vector # 부팅 시 자동 시작 systemctl start vector
이제 Vector의 서버 로그는 journald가 자동으로 수집하고 관리한다.
# /etc/systemd/journald.conf SystemMaxUse=500M MaxRetentionSec=30day
systemctl restart systemd-journald
Jenkins 관리 → 각 Job 설정 → 오래된 빌드 삭제 → 보존 일수(30일), 최대 빌드 수(100) 설정. config.xml을 직접 수정하는 방법도 있다.
앱 로그가 ES에 쌓이는 것도 결국 디스크를 먹는다. Index Lifecycle Management(ILM) 정책이 없으면 이것도 결국 터진다. 오래된 인덱스 자동 삭제 정책을 반드시 확인해야 한다.
7. nohup + logrotate도 유효한 선택이다
systemd 전환이 표준 권장 방식이지만, 기존 실행 스크립트를 건드리기 어렵거나 환경 제약이 있다면 nohup 방식을 유지하면서 logrotate로 관리하는 것도 완전히 유효한 선택이다.
# /etc/logrotate.d/vector /engine/.../nohup.out { daily rotate 7 compress missingok notifempty copytruncate # 실행 중인 프로세스 재시작 없이 동작하는 핵심 옵션 }
| 방식 | 장점 | 단점 |
|---|---|---|
| nohup + logrotate | 기존 방식 유지, 설정 간단 | 재부팅 자동시작 없음, 프로세스 죽어도 재시작 없음 |
| systemd + journald | 자동 재시작, 부팅 자동시작, 로그 자동 관리 | 서비스 파일 작성 필요, 기존 스크립트 수정 |
- 젠킨스 실행/빌드 로그

'SERVER > Architecture' 카테고리의 다른 글
| 프록시·NAT·네트워크 계층 기초 정리 (0) | 2026.07.03 |
|---|---|
| 모바일 HTTP 이미지 요청과 /runtime/exception 추적 정리 (1) | 2026.03.18 |
| 특정 환경/IP 차단 이슈 - 네트워크 경로 (0) | 2026.03.11 |
| 프록시 서버 (0) | 2026.03.11 |
| Strangler Fig Pattern : 테스트 하지 않는 환경에서 TDD 흐름 만들기 (0) | 2026.01.02 |