배경
Tableau Server를 EC2에 직접 설치해서 운영하고 있다. 초기에는 이 도구에 대한 이해도가 낮았는데, 실제 운영 과정에서 30개 이상의 내부 컴포넌트로 구성된 상당히 복잡한 시스템이라는 것을 알게 되었다.
특히 각 컴포넌트가 독립적인 로그 디렉터리를 가져 장애 발생 시 어떤 로그를 확인해야 하는지 직관적으로 판단하기 어렵다. 또한 서버가 노드 2개로 구성되어 있어 각각 SSH로 접속해 로그를 확인해야 했다.
Flow나 Extract 실패 시 종종 BI에서 로그 분석 요청이 들어오는데, 로그를 수작업으로 추적하고 타임라인을 맞추는 과정에서 건당 평균 1~2시간이 소요되었다. 이 요청이 주 2~3회 반복되면서 운영 병목으로 이어졌다.
이 글에서는 이러한 문제를 해결하기 위해 다음과 같은 단계적 개선 과정을 정리한다.
•
SSH + grep 기반 수동 분석
•
ziplog + LLM 반자동 분석
•
CloudWatch + n8n 기반 완전 자동화
Tableau 로그 분석의 구조적 문제
Tableau Server의 로그는 다음 경로에 저장된다.
/var/opt/tableau/tableau_server/data/tabsvc/logs/
Bash
복사
그 안에 디렉터리를 열어보면 다음과 같이 각 컴포넌트별로 로그가 분산되어 저장된다.
tableau@tableau-main:~/data/tabsvc/logs$ ls
activationservice cacheserver floweditor noninteractive vizdatanativeservice
activemqserver clientfileservice flowminerva pgsql vizdataservice
allegro clustercontroller flowprocessor publishedconnections vizportal
analyticsextensions collections httpd querygateway vizqldataservice
apigateway contentexploration hyper siteimportexport vizqlserver
appzookeeper databasemaintenance indexandsearchserver tabadminagent webhooks
authnservice dataprofiling interactive tabadmincontroller
backgrounder dataserver licenseservice tdsnativeservice
backuprestore filestore minerva tdsservice
YAML
복사
각 컴포넌트는 역할에 따라 로그가 나뉘어 있다.
•
Flow 실행 관려 → flowprocessor
•
실제 작업 실행 → backgrounder
•
쿼리 처리 → hyper
•
파일 저장/전달 → filestore
하나의 장애를 분석하려면 여러 디렉터리를 오가며 동일 시간대의 로그를 수집하고 타임스탬프 기준으로 흐름을 재구성해야 했다.
그래서 초기에 로그 분석을 요청받았을때 제일 어려웠던 점이 다음 두가지였다.
1.
어디를 봐야 하는지 판단하기 어렵다
→ 컴포넌트가 많고, 장애 원인이 발생한 위치가 직관적이지 않음
2.
로그를 봐도 컨텍스트가 연결되지 않는다
→ 개별 로그는 존재하지만, 전체 실행 흐름을 사람이 직접 이어붙여야 함
1단계: SSH + grep 으로 찾기
초기에는 전형적인 방식으로 접근했다.
•
SSH 접속
•
flowprocessor 로그 확인
•
grep으로 ERROR 필터링
•
타임스탬프 확인하면서 앞뒤 로그를 cat이나 less로 추적
•
필요 시 backgrounder 등 추가 탐색
하지만 이 방식은 한계가 명확했다.
•
ERROR 로그만으로 원인 파악 불가
•
메모리, 쿼리 실행 흐름 등 컨텍스트 부족
•
시간순 흐름 재구성 필요 (수작업)
결과적으로 grep은 로그를 단순 검색하는데 유용하지만 장애를 이해하기에는 하나의 실행흐름으로 연결해주지는 못했다.
2단계: ziplog + LLM 기반 반자동화
작년 말 Claude Code와 Codex 같은 터미널 기반 LLM 도구들이 본격적으로 실무에 도입이 되면서 이를 이용해 분석을 맡기면 좋을것 같다는 생각이 들었다.
ziplog로 로그 수집 단순화
Tableau는 tsm maintenance ziplogs 명령어로 전체 로그를 한번에 압축하는 기능을 제공한다. 시간 범위와 노드를 지정할수도 있어, 이를 이용해 로그를 하나의 파일로 수집하도록 개선했다.
tsm maintenance ziplogs -f tableau_logs.zip -o \
-sd "01/29/2026 11:00" -ed "01/29/2026 12:00"
Bash
복사
이로인해 디렉터리별 탐색을 할 필요 없이 단일 파일기반으로 분석이 가능해졌다.
LLM을 통한 로그 해석
터미널에서 다운받은 ziplog 결과를 LLM에 전달하여 분석하도록 했다. 결과 품질은 기대 이상으로 너무 좋았다.
•
메모리 사용량 변화 추적
•
쿼리 실행 및 처리 단계 흐름 재구성
•
동시 작업 간 리소스 충돌 식별
•
실제 처리인지 대기때문에 느린지 구분
특정 Flow 실행 시점에 동일 노드에서 다른 대시보드 조회 요청이 동시에 실행되면서
Hyper 메모리를 크게 점유
실패 시점에 노드 메모리가 Tableau total 약 86.6GB, 전체 프로세스 약 121.7GB까지 올라감
flow도 내부 Hyper 쿼리에서 약 30.8GB를 사용하던 중 system cancel이 발생
같은 시각 대시보드 조회 요청도 약 311초 실행되다가 동일하게 system cancel
동시 실행된 고부하 작업 간 리소스 충돌이 원인
이후 동일 flow 성공은 동시에 메모리 점유하는게 없어서 최종적으로 약 1.398억 행, 75.5GB 수준으로 정상 완료
Bash
복사
기존에는 사람이 몇시간에 걸쳐 로그를 읽고 유추해야 했던 내용을 LLM이 로그를 기반으로 실행 흐름 + 리소스 상태 + 시스템 레벨 원인까지 구조적으로 정리해주었다.
그래도 남은 한계
분석 품질은 만족스러웠지만 프로세스 자체는 수동인 부분이 많았다.
1.
분석가가 Slack으로 장애 분석 요청
2.
SSH 접속 → 해당 시간대 찾아 ziplog 생성 → 다운로드
3.
LLM에 로그 경로와 이슈 전달
4.
결과를 다시 정리해 Slack으로 전달
분석 시간은 줄었지만 매번 이 4단계를 거쳐야 한다는 건 변하지 않았다.
3단계: n8n + CloudWatch 기반 완전 자동화
올초 사내에 워크플로우 자동화를 위해 n8n을 설치했다. 기존에도 Airflow에서 Tableau 실패 이벤트를 감지해 Slack으로 알림을 보내고 있었기 때문에, 이 흐름을 확장하면 로그 분석까지 자동화할 수 있겠다고 생각했다.
로그 수집 구조 변경
이 단계에서 가장 큰 변화는 로그 수집 방식이었다. 기존 ziplog는 사후에 특정 시점 로그를 한번에 내려받는 방식이어서 워크플로우에 적합하지 않았다. 대신 CloudWatch Logs를 활용해 로그를 실시간으로 수집하도록 변경했다.
CloudWatch Agent 기존 메트릭 수집 설정에 로그 수집 추가:
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/opt/tableau/tableau_server/data/tabsvc/logs/flowprocessor/*.log",
"log_group_name": "/tableau/flowprocessor",
"log_stream_name": "{hostname}",
"retention_in_days": 7
}
]
}
}
}
}
Bash
복사
전체 워크플로우 흐름
Slack 실패 알림
↓
이모지 트리거
↓
메시지 파싱 (server, job_id)
↓
CloudWatch 로그 조회 (해당 시간대 범위 제한)
↓
로그 수집
↓
LLM 분석
↓
Slack 스레드 답변
Bash
복사
설계 포인트
•
시간 범위 제한
◦
메시지 시각 기준 ±1시간 로그만 조회해 불필요한 로그 최소화
•
ERROR 레벨 위주로 필터링
•
서버별 분기
◦
메시지에서 서버 정보 파싱 → 로그 그룹/스트림 자동 선택
•
프롬프트 작성
◦
실패 원인 / 상세 분석 / 해결방안
분석가가 실패 알림에 분석 이모지를 달면 수십 초 안에 해당 스레드에 분석 결과가 달린다. 분석 결과에는 실패 원인 요약, 에러가 발생한 단계와 메시지, 구체적인 해결 방안이 포함된다.
내가 SSH에 접속할 필요도, ziplog을 받을 필요도, LLM에 수동으로 던질 필요도 없다. 분석가가 직접 이모지를 달고 직접 결과를 받아볼 수 있게 됐다.
한계와 확장 방향
로그 커버리지 한계
가장 큰 제약은 nativeapi 로그다.
여기에 상세한 메모리 사용량 세션별 리소스 할당 정보 같은 게 들어있는데, 이 로그는 수 GB 규모라 CloudWatch로 수집하는 게 현실적으로 어렵다.
아직까지 이 수준의 분석을 요청받은 적은 없지만 만약 온다면 여전히 ziplog + LLM 수동 분석으로 대응해야 한다.
컴포넌트 범위 제한
현재는 flowprocessor의 ERROR 로그를 중심으로 구성되어있다. 실제 운영 병목이 Flow 실패 분석에 집중되어 있었고 해당 로그만으로도 대부분의 장애 원인을 설명할 수 있었기 때문이다.
물론 다음과 같은 컴포넌트까지 확장하면 더 넓은 범위를 커버할 수 있다.
•
backgrounder → 작업 실행 단계
•
hyper → 쿼리 엔진 및 메모리 사용
•
vizqlserver → 사용자 쿼리 및 대시보드 요청
하지만 컴포넌트를 확장 할수록 로그 수집량과 분석 복잡도가 증가하고 LLM에게 로그를 넘기는 부분도 고려해야하기 때문에, 가장 영향도가 높은 곳 flow에 집중했다.
마무리
이번 개선을 통해
•
분석 소요 시간
◦
1~2시간 → 수십 초 수준으로 단축
•
요청 처리 방식
◦
기존: 분석가 → 엔지니어 요청 → 수동 분석 → 전달
◦
개선: 누구나 이모지 트리거로 즉시 결과 조회
•
운영 개입
◦
엔지니어 개입 필수에서 자동으로 처리
•
반복되던 업무 병목 제거
결과적으로 로그 분석은 개별 작업 단위의 대응에서 벗어나 필요 시 즉시 호출 가능한 기능 형태로 전환되었다.
처음 회사에 왔을 때, Tableau 로그를 SSH로 접속해서 grep으로 하나씩 확인하는 방식을 보고 이게 맞는 방식인가? 라는 생각이 들었다. 당시에는 로그 구조도 익숙하지 않았고 장애 하나를 디버깅하는데도 많은 시간이 필요했다.
지금은 여러 개선안을 거쳐 같은 문제를 이모지 하나로 몇십초 안에 확인할 수 있게됐다. 큰 기술 도입했다기보다 불편한 지점을 그대로 두지않고 아이디어를 내고 개선해나가는 과정이 중요하다는 것을 느꼈다.


