이미지 로딩 중...

여러 서버 로그 통합 수집 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 11. 18. · 3 Views

여러 서버 로그 통합 수집 완벽 가이드

여러 대의 서버에서 로그 파일을 효율적으로 수집하고 통합하는 방법을 다룹니다. SSH, scp, rsync를 활용한 원격 로그 수집부터 병렬 처리와 에러 핸들링까지 실무에서 바로 활용할 수 있는 기법들을 단계별로 배워봅니다.


목차

  1. SSH로 원격 로그 가져오기
  2. scp와 rsync로 로그 동기화
  3. 여러 서버 순회 스크립트
  4. 로그 중앙화 전략
  5. 네트워크 에러 처리
  6. 병렬 로그 수집

1. SSH로 원격 로그 가져오기

시작하며

여러분이 운영 중인 서비스가 여러 서버에 분산되어 있다면, 각 서버에 일일이 접속해서 로그를 확인하는 일이 얼마나 번거로운지 아실 겁니다. 특히 새벽에 장애가 발생했을 때, 서버 5대를 일일이 SSH로 접속해서 로그를 확인하는 것은 정말 고역입니다.

이런 문제는 실제 개발 현장에서 매일 발생합니다. 로그를 확인하는 데만 10분씩 걸리다 보면, 정작 중요한 문제 해결은 뒤로 밀리게 됩니다.

또한 각 서버마다 로그 형식이 다르거나 위치가 다를 경우 더욱 혼란스러워집니다. 바로 이럴 때 필요한 것이 SSH를 활용한 원격 로그 수집입니다.

SSH 명령어 하나로 원격 서버의 로그를 로컬로 가져와서 한 곳에서 분석할 수 있다면, 여러분의 작업 효율이 몇 배는 향상될 것입니다.

개요

간단히 말해서, SSH(Secure Shell)를 통해 원격 서버의 로그 파일을 로컬 컴퓨터로 가져오는 기술입니다. 마치 택배 기사가 물건을 가져오듯이, SSH는 안전하게 암호화된 통로를 통해 원격 서버의 파일을 가져옵니다.

왜 이 기술이 필요한지 실무 관점에서 설명하자면, 서버가 3대, 5대, 10대로 늘어날수록 수동으로 관리하는 것은 불가능해집니다. 예를 들어, 특정 시간대에 발생한 에러를 추적해야 하는 경우, 모든 서버의 로그를 한 곳에 모아야 전체적인 흐름을 파악할 수 있습니다.

기존에는 각 서버에 하나씩 접속해서 tail, grep 명령어로 확인했다면, 이제는 하나의 스크립트로 모든 서버의 로그를 자동으로 수집할 수 있습니다. 이 기술의 핵심 특징은 첫째, 비밀번호 없이 안전하게 접속할 수 있는 SSH 키 인증, 둘째, 원격 명령을 실행하여 필요한 부분만 추출할 수 있는 유연성, 셋째, 스크립트를 통한 자동화 가능성입니다.

이러한 특징들이 중요한 이유는 수동 작업을 최소화하고 사람의 실수를 줄이면서도 보안은 유지할 수 있기 때문입니다.

코드 예제

#!/bin/bash
# SSH를 통해 원격 서버의 로그 파일 가져오기

# 원격 서버 정보
REMOTE_HOST="app-server-01.example.com"
REMOTE_USER="admin"
REMOTE_LOG="/var/log/application/app.log"
LOCAL_DIR="./logs"

# 로컬 저장 디렉토리 생성
mkdir -p "$LOCAL_DIR"

# SSH로 원격 로그의 최근 1000줄 가져오기
ssh "${REMOTE_USER}@${REMOTE_HOST}" "tail -n 1000 ${REMOTE_LOG}" > "${LOCAL_DIR}/app-server-01.log"

# 특정 에러만 필터링해서 가져오기
ssh "${REMOTE_USER}@${REMOTE_HOST}" "grep 'ERROR' ${REMOTE_LOG}" > "${LOCAL_DIR}/errors-01.log"

echo "로그 수집 완료: ${LOCAL_DIR}"

설명

이것이 하는 일: 이 스크립트는 SSH 연결을 통해 원격 서버에 명령어를 보내고, 그 결과를 로컬 파일로 저장합니다. 마치 전화를 걸어서 상대방에게 "3페이지 내용 좀 읽어줘"라고 하면 그 내용을 받아 적는 것과 비슷합니다.

첫 번째로, 스크립트는 원격 서버의 접속 정보와 로그 파일 경로를 변수로 정의합니다. 이렇게 변수로 관리하면 나중에 서버가 바뀌어도 스크립트 상단의 몇 줄만 수정하면 되기 때문에 유지보수가 쉽습니다.

mkdir -p 명령어로 로컬에 로그를 저장할 디렉토리를 미리 만들어두는데, -p 옵션은 이미 디렉토리가 있어도 에러를 발생시키지 않습니다. 두 번째로, ssh 명령어가 실행되면서 원격 서버에 접속하여 tail -n 1000 명령을 실행합니다.

tail은 파일의 끝부분을 읽는 명령어인데, -n 1000 옵션은 마지막 1000줄만 가져오라는 의미입니다. 전체 로그 파일이 수십 GB라도, 최근 로그만 가져오면 네트워크 부담이 줄어듭니다.

결과는 > 리다이렉션을 통해 로컬 파일로 저장됩니다. 세 번째로, 두 번째 ssh 명령은 grep을 활용하여 ERROR라는 문자열이 포함된 줄만 필터링합니다.

이렇게 하면 수천 줄의 로그 중에서 실제 문제가 되는 에러 로그만 골라서 가져올 수 있습니다. 원격 서버에서 필터링을 수행하기 때문에 네트워크로 전송되는 데이터량이 대폭 줄어듭니다.

여러분이 이 스크립트를 사용하면 몇 초 안에 원격 서버의 로그를 로컬에서 확인할 수 있습니다. 여러 서버의 로그를 한 곳에 모아서 grep, awk 같은 도구로 통합 분석이 가능하고, 로그를 로컬에 보관하므로 원격 서버의 디스크 공간을 절약할 수 있으며, 스크립트를 cron에 등록하면 주기적으로 자동 수집할 수 있습니다.

실전 팁

💡 SSH 키 인증을 설정하면 비밀번호 입력 없이 자동화할 수 있습니다. ssh-keygen으로 키를 생성하고 ssh-copy-id로 원격 서버에 공개키를 등록하세요.

💡 원격 서버의 로그 파일이 너무 크면 tail 대신 sed -n '1000,2000p' 같은 명령어로 특정 범위만 가져오세요. 네트워크 타임아웃을 방지할 수 있습니다.

💡 ssh 명령 실행 시 -o ConnectTimeout=10 옵션을 추가하면 응답이 없는 서버에서 오래 기다리지 않습니다. 특히 여러 서버를 순회할 때 중요합니다.

💡 로그 파일명에 날짜와 서버 이름을 포함하세요 (예: app-server-01_2025-11-18.log). 나중에 어느 서버의 어느 시점 로그인지 바로 알 수 있습니다.

💡 원격 명령 실행 시 따옴표를 주의하세요. 큰따옴표 안에 변수를 넣으면 로컬 변수가 적용되고, 작은따옴표는 원격에서 해석됩니다.


2. scp와 rsync로 로그 동기화

시작하며

여러분이 SSH로 로그를 가져올 수 있게 되었다면, 다음 단계는 더 효율적으로 파일을 전송하는 방법입니다. 매번 전체 로그를 다시 받는 것은 시간도 오래 걸리고 네트워크 대역폭도 낭비됩니다.

특히 로그 파일이 하루에 몇 GB씩 쌓이는 환경이라면 더욱 그렇습니다. 이런 문제는 로그 수집을 자동화하려고 할 때 반드시 마주치게 됩니다.

1시간마다 로그를 수집하는데, 매번 10GB씩 전송한다면 네트워크가 버티지 못합니다. 또한 전송 중간에 네트워크가 끊기면 처음부터 다시 시작해야 하는 문제도 있습니다.

바로 이럴 때 필요한 것이 scp와 rsync입니다. scp는 간단하고 안전한 파일 복사를, rsync는 변경된 부분만 동기화하는 지능적인 전송을 제공합니다.

이 두 도구를 상황에 맞게 선택하면 로그 수집이 훨씬 빠르고 안정적으로 됩니다.

개요

간단히 말해서, scp는 SSH를 기반으로 한 안전한 파일 복사 도구이고, rsync는 증분 동기화를 지원하는 고급 파일 전송 도구입니다. scp를 택배에 비유하면, rsync는 "이전에 보낸 것과 달라진 부분만 보내는 스마트 택배"입니다.

왜 이 도구들이 필요한지 실무 관점에서 설명하자면, 로그 파일은 계속 증가하는 특성이 있습니다. 예를 들어, 하루 전에 100MB였던 로그가 오늘은 150MB가 되었다면, rsync는 새로 추가된 50MB만 전송합니다.

이는 전송 시간과 네트워크 비용을 80% 이상 절감할 수 있습니다. 기존에는 FTP나 수동 복사로 전체 파일을 매번 다시 받았다면, 이제는 scp로 간단한 일회성 복사를, rsync로 정기적인 동기화를 수행할 수 있습니다.

이 도구들의 핵심 특징은 첫째, SSH 기반이므로 암호화된 안전한 전송, 둘째, 압축 옵션으로 네트워크 대역폭 절약, 셋째, rsync의 체크섬 기반 증분 전송으로 효율성 극대화입니다. 이러한 특징들이 중요한 이유는 대용량 로그를 다룰 때 전송 시간과 안정성이 서비스 운영의 핵심이 되기 때문입니다.

코드 예제

#!/bin/bash
# scp와 rsync를 활용한 로그 동기화

REMOTE_USER="admin"
REMOTE_HOST="app-server-01.example.com"
REMOTE_LOG_DIR="/var/log/application/"
LOCAL_LOG_DIR="./logs/app-server-01/"

mkdir -p "$LOCAL_LOG_DIR"

# scp: 특정 로그 파일 하나만 복사 (간단한 경우)
echo "scp로 최신 로그 파일 복사 중..."
scp -C "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_LOG_DIR}/app.log" "${LOCAL_LOG_DIR}/"

# rsync: 디렉토리 전체를 증분 동기화 (효율적)
echo "rsync로 로그 디렉토리 동기화 중..."
rsync -avz --progress \
  --include='*.log' \
  --exclude='*.tmp' \
  "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_LOG_DIR}" \
  "${LOCAL_LOG_DIR}/"

# rsync로 7일 이상 된 파일은 제외하고 동기화
echo "최근 7일 로그만 동기화 중..."
rsync -avz --progress \
  --files-from=<(ssh "${REMOTE_USER}@${REMOTE_HOST}" "find ${REMOTE_LOG_DIR} -name '*.log' -mtime -7") \
  "${REMOTE_USER}@${REMOTE_HOST}:/" \
  "${LOCAL_LOG_DIR}/"

echo "동기화 완료!"

설명

이것이 하는 일: 이 스크립트는 원격 서버의 로그 파일을 로컬로 복사하거나 동기화합니다. scp는 단순 복사를, rsync는 이전 전송과 비교하여 변경된 부분만 전송하는 지능적인 동기화를 수행합니다.

첫 번째로, scp 명령은 -C 옵션으로 압축을 활성화하여 파일을 전송합니다. 이는 텍스트 기반 로그 파일에 특히 효과적인데, 압축률이 70-80%에 달하기도 합니다.

scp는 사용법이 간단하고 빠르기 때문에 특정 파일 하나를 급하게 가져올 때 유용합니다. 하지만 같은 파일을 여러 번 전송할 때는 매번 전체를 다시 보내므로 비효율적입니다.

두 번째로, rsync 명령은 -avz 옵션을 사용하는데, 이는 archive mode(권한 보존), verbose(진행 상황 출력), compress(압축)를 의미합니다. --progress 옵션으로 전송 진행률을 실시간으로 볼 수 있어 대용량 파일 전송 시 매우 유용합니다.

--include와 --exclude 옵션으로 .log 파일만 포함하고 임시 파일은 제외하여 불필요한 전송을 방지합니다. 세 번째로, 고급 rsync 예제는 최근 7일 이내에 수정된 로그만 동기화합니다.

이는 ssh로 원격 서버에서 find 명령을 실행하여 파일 목록을 가져오고, --files-from 옵션으로 그 목록에 있는 파일만 전송합니다. 로그가 수개월치 쌓여 있어도 최근 것만 가져올 수 있어 디스크 공간을 절약합니다.

여러분이 이 스크립트를 사용하면 첫 번째 동기화는 전체를 전송하지만, 이후에는 변경된 부분만 전송되어 시간이 몇 분의 1로 단축됩니다. 네트워크가 불안정한 환경에서도 rsync는 중단된 지점부터 재개할 수 있고, 파일 권한과 타임스탬프를 그대로 보존하므로 로그 분석 시 원본 정보를 유지할 수 있으며, --dry-run 옵션으로 실제 전송 전에 시뮬레이션할 수 있어 안전합니다.

실전 팁

💡 rsync 첫 실행 시 --dry-run 옵션을 추가하여 어떤 파일이 전송될지 미리 확인하세요. 실수로 엉뚱한 파일을 삭제하는 것을 방지할 수 있습니다.

💡 대용량 로그 전송 시 --bwlimit=10000 옵션으로 대역폭을 제한하세요 (단위: KB/s). 업무 시간에 네트워크를 독차지하지 않도록 배려할 수 있습니다.

💡 rsync의 --delete 옵션은 주의해서 사용하세요. 원격에서 삭제된 파일을 로컬에서도 삭제하는데, 로그 보관 정책과 충돌할 수 있습니다.

💡 scp는 디렉토리 복사 시 -r 옵션을 사용하지만, rsync가 훨씬 빠르고 안전합니다. 가능하면 rsync를 선택하세요.

💡 전송 속도가 느리면 rsync에 -W 옵션을 추가해 증분 전송을 비활성화하세요. 파일이 크게 변경된 경우 오히려 전체 전송이 더 빠를 수 있습니다.


3. 여러 서버 순회 스크립트

시작하며

여러분이 이제 한 서버에서 로그를 가져오는 방법은 완벽히 익혔습니다. 그런데 실제 운영 환경에서는 서버가 한 대가 아닙니다.

웹 서버 3대, 애플리케이션 서버 5대, 데이터베이스 서버 2대... 이렇게 10대의 서버 로그를 하나씩 수집한다면 스크립트를 10번 실행해야 할까요?

이런 문제는 서비스 규모가 커질수록 더욱 심각해집니다. 서버가 추가될 때마다 새로운 스크립트를 만들거나, 기존 스크립트를 복사해서 수정하다 보면 금방 관리가 불가능해집니다.

또한 한 서버에서 에러가 발생하면 전체 수집이 중단되는 문제도 있습니다. 바로 이럴 때 필요한 것이 여러 서버를 자동으로 순회하는 스크립트입니다.

서버 목록을 배열이나 파일로 관리하고, 반복문으로 각 서버를 차례대로 처리하면 수십 대의 서버도 하나의 스크립트로 관리할 수 있습니다.

개요

간단히 말해서, 여러 서버를 순회하는 스크립트는 반복문(for loop)과 배열을 활용하여 여러 서버에 동일한 작업을 자동으로 수행하는 자동화 도구입니다. 마치 우체부가 주소 목록을 보고 집집마다 우편물을 배달하듯이, 스크립트는 서버 목록을 보고 각 서버에서 로그를 수집합니다.

왜 이 기술이 필요한지 실무 관점에서 설명하자면, 서버가 5대 이상만 되어도 수동 작업은 실용적이지 않습니다. 예를 들어, 새벽에 장애가 발생해서 모든 서버의 로그를 긴급히 확인해야 하는 경우, 스크립트 하나만 실행하면 1분 안에 모든 로그가 수집됩니다.

수동으로 했다면 10분 이상 걸렸을 작업입니다. 기존에는 각 서버마다 개별 스크립트를 만들고 하나씩 실행했다면, 이제는 서버 목록만 업데이트하면 하나의 스크립트로 모든 서버를 처리할 수 있습니다.

이 기술의 핵심 특징은 첫째, 배열이나 외부 파일로 서버 목록을 유연하게 관리, 둘째, 에러 처리를 통해 한 서버의 실패가 전체를 중단시키지 않음, 셋째, 로그에 타임스탬프와 서버 이름을 기록하여 추적 가능성 확보입니다. 이러한 특징들이 중요한 이유는 대규모 인프라에서는 일부 서버의 일시적 장애가 흔하며, 전체 시스템의 가용성을 유지하면서도 로그 수집을 완료해야 하기 때문입니다.

코드 예제

#!/bin/bash
# 여러 서버를 순회하며 로그 수집

# 서버 목록 배열 정의
SERVERS=(
  "app-server-01.example.com"
  "app-server-02.example.com"
  "app-server-03.example.com"
  "db-server-01.example.com"
  "web-server-01.example.com"
)

REMOTE_USER="admin"
REMOTE_LOG="/var/log/application/app.log"
LOCAL_BASE_DIR="./logs"
LOG_FILE="./collection.log"

# 수집 시작 로그
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 로그 수집 시작" | tee -a "$LOG_FILE"

# 각 서버 순회
for server in "${SERVERS[@]}"; do
  echo "==> $server 처리 중..." | tee -a "$LOG_FILE"

  # 서버별 로컬 디렉토리 생성
  LOCAL_DIR="${LOCAL_BASE_DIR}/${server}"
  mkdir -p "$LOCAL_DIR"

  # rsync로 로그 수집 (에러 발생 시에도 계속 진행)
  if rsync -avz --timeout=30 "${REMOTE_USER}@${server}:${REMOTE_LOG}" "${LOCAL_DIR}/" 2>&1 | tee -a "$LOG_FILE"; then
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✓ $server 성공" | tee -a "$LOG_FILE"
  else
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✗ $server 실패" | tee -a "$LOG_FILE"
  fi
done

echo "[$(date '+%Y-%m-%d %H:%M:%S')] 로그 수집 완료" | tee -a "$LOG_FILE"

설명

이것이 하는 일: 이 스크립트는 서버 목록을 배열로 관리하고, for 반복문으로 각 서버에 차례대로 접속하여 로그를 수집합니다. 전체 과정을 로그 파일에 기록하여 나중에 어느 서버가 성공했고 실패했는지 추적할 수 있습니다.

첫 번째로, SERVERS 배열에 모든 서버의 호스트명을 나열합니다. 이렇게 배열로 관리하면 서버를 추가하거나 제거할 때 배열 요소만 수정하면 되므로 유지보수가 매우 간편합니다.

또한 외부 텍스트 파일에서 읽어올 수도 있어 확장성이 뛰어납니다. date 명령으로 현재 시각을 포맷팅하여 수집 시작 시간을 기록하는데, 이는 문제 발생 시 시간대별 추적에 필수적입니다.

두 번째로, for 반복문이 배열의 각 요소를 순회하면서 server 변수에 호스트명을 할당합니다. 각 서버마다 별도의 로컬 디렉토리를 생성하여 로그를 분리 저장하므로, 나중에 특정 서버의 로그만 찾기가 쉽습니다.

mkdir -p 명령은 중첩 디렉토리를 한 번에 생성하고, 이미 존재해도 에러를 발생시키지 않습니다. 세 번째로, rsync 명령에 --timeout=30 옵션을 추가하여 30초 이상 응답이 없으면 해당 서버를 건너뜁니다.

if 문으로 rsync의 성공 여부를 판단하여 성공 시 ✓, 실패 시 ✗ 표시를 로그에 남깁니다. tee -a 명령은 화면에도 출력하고 로그 파일에도 추가하므로, 실시간으로 진행 상황을 보면서도 기록을 남길 수 있습니다.

여러분이 이 스크립트를 사용하면 서버가 10대든 100대든 하나의 명령으로 모든 로그를 수집할 수 있습니다. 새로운 서버가 추가되면 배열에 한 줄만 추가하면 되고, 특정 서버에서 일시적 장애가 발생해도 나머지 서버의 수집은 정상적으로 진행되며, collection.log 파일을 확인하면 언제 어느 서버에서 문제가 있었는지 한눈에 파악할 수 있습니다.

또한 cron에 등록하여 매시간 자동 실행하면 완전 자동화된 로그 수집 시스템을 구축할 수 있습니다.

실전 팁

💡 서버 목록을 외부 파일(servers.txt)에 저장하고 while read로 읽으면 더 유연합니다. 비개발자도 메모장으로 서버 목록을 수정할 수 있습니다.

💡 각 서버별 로그 파일에 날짜를 포함하세요 (예: app-server-01_2025-11-18.log). 여러 날짜의 로그를 보관할 때 덮어쓰기를 방지할 수 있습니다.

💡 반복문 안에 sleep 1을 추가하여 서버 간 1초 간격을 두세요. 동시에 너무 많은 연결을 시도하면 방화벽이나 SSH 서버가 차단할 수 있습니다.

💡 에러가 발생한 서버 목록을 별도로 저장하여 나중에 재시도할 수 있도록 하세요. grep '✗' collection.log로 실패한 서버만 추출할 수 있습니다.

💡 병렬 처리가 필요하면 xargs나 GNU parallel을 사용하세요. 순차 처리보다 몇 배 빠르지만, 서버 부하를 고려해야 합니다.


4. 로그 중앙화 전략

시작하며

여러분이 이제 여러 서버에서 로그를 수집할 수 있게 되었다면, 다음 질문은 "이 로그들을 어떻게 효과적으로 관리하고 분석할 것인가"입니다. 각 서버의 로그가 개별 파일로 흩어져 있으면, 전체 시스템의 흐름을 파악하기 어렵고, 여러 서버에 걸친 에러 패턴을 발견하기도 힘듭니다.

이런 문제는 마이크로서비스 아키텍처나 분산 시스템에서 특히 심각합니다. 사용자 요청 하나가 API Gateway → 인증 서버 → 애플리케이션 서버 → 데이터베이스를 거치는데, 각 단계의 로그가 다른 서버에 있다면 전체 트랜잭션을 추적하기가 매우 어렵습니다.

장애 발생 시 어느 구간에서 문제가 생겼는지 파악하는 데만 수십 분이 걸립니다. 바로 이럴 때 필요한 것이 로그 중앙화 전략입니다.

모든 서버의 로그를 하나의 중앙 저장소에 모으고, 타임스탬프와 메타데이터를 통합하여 전체 시스템을 하나의 시각으로 볼 수 있게 만드는 것입니다.

개요

간단히 말해서, 로그 중앙화는 분산된 여러 서버의 로그를 하나의 중앙 저장소에 수집하고 통합하는 아키텍처입니다. 마치 여러 지점의 CCTV 영상을 통합 관제실에서 한 번에 보는 것처럼, 모든 서버의 로그를 한 곳에서 검색하고 분석할 수 있습니다.

왜 이 전략이 필요한지 실무 관점에서 설명하자면, 현대의 애플리케이션은 대부분 분산 환경에서 동작합니다. 예를 들어, 사용자가 "결제 실패" 에러를 보고했을 때, 프론트엔드 서버, 결제 API 서버, 은행 연동 서버의 로그를 모두 확인해야 원인을 찾을 수 있습니다.

중앙화된 로그 시스템이 있으면 거래 ID로 검색 한 번에 모든 관련 로그를 시간 순으로 볼 수 있습니다. 기존에는 각 서버에 접속해서 grep으로 찾고, 타임스탬프를 수동으로 맞추며 분석했다면, 이제는 중앙 저장소에서 웹 UI나 명령어 하나로 전체 시스템의 로그를 통합 검색할 수 있습니다.

이 전략의 핵심 특징은 첫째, 모든 로그를 시간 순으로 정렬하여 이벤트 흐름 파악 가능, 둘째, 서버명, 서비스명, 로그 레벨 같은 메타데이터 추가로 필터링 용이, 셋째, 중앙 저장소의 백업과 보관 정책으로 로그 손실 방지입니다. 이러한 특징들이 중요한 이유는 로그는 단순한 텍스트가 아니라 시스템의 건강 상태와 장애 원인을 밝혀주는 핵심 자산이기 때문입니다.

코드 예제

#!/bin/bash
# 로그 중앙화를 위한 수집 및 통합 스크립트

SERVERS=("app-01" "app-02" "web-01" "db-01")
REMOTE_USER="admin"
REMOTE_LOG_DIR="/var/log/application"
CENTRAL_LOG_DIR="./central-logs"
UNIFIED_LOG="${CENTRAL_LOG_DIR}/unified.log"

mkdir -p "$CENTRAL_LOG_DIR"
> "$UNIFIED_LOG"  # 통합 로그 파일 초기화

echo "로그 중앙화 시작: $(date)" | tee -a "$UNIFIED_LOG"

# 각 서버에서 로그 수집 및 메타데이터 추가
for server in "${SERVERS[@]}"; do
  echo "==> $server 처리 중..."

  # 임시 로그 파일로 다운로드
  TEMP_LOG="/tmp/${server}_temp.log"
  ssh "${REMOTE_USER}@${server}" "cat ${REMOTE_LOG_DIR}/app.log" > "$TEMP_LOG" 2>/dev/null

  if [ -f "$TEMP_LOG" ]; then
    # 각 로그 라인에 서버 이름 추가하여 통합 파일에 병합
    while IFS= read -r line; do
      echo "[${server}] $line" >> "$UNIFIED_LOG"
    done < "$TEMP_LOG"

    # 서버별 개별 로그도 보관
    cp "$TEMP_LOG" "${CENTRAL_LOG_DIR}/${server}_$(date +%Y%m%d).log"
    rm "$TEMP_LOG"

    echo "✓ $server 완료"
  else
    echo "✗ $server 실패" | tee -a "$UNIFIED_LOG"
  fi
done

# 통합 로그를 타임스탬프 순으로 정렬
echo "타임스탬프 정렬 중..."
sort -t' ' -k1,2 "$UNIFIED_LOG" -o "$UNIFIED_LOG"

echo "로그 중앙화 완료: $(date)"
echo "통합 로그: $UNIFIED_LOG"
echo "서버별 로그: ${CENTRAL_LOG_DIR}/*_$(date +%Y%m%d).log"

설명

이것이 하는 일: 이 스크립트는 여러 서버의 로그를 수집하여 각 로그 라인에 서버 이름을 태깅한 후, 하나의 통합 로그 파일로 병합합니다. 최종적으로 타임스탬프 기준으로 정렬하여 전체 시스템의 이벤트를 시간 순서대로 볼 수 있게 만듭니다.

첫 번째로, 스크립트는 중앙 저장소 디렉토리를 만들고 unified.log 파일을 초기화합니다. > 연산자는 파일을 비워서 이전 실행의 데이터가 섞이지 않도록 합니다.

각 실행마다 새로운 통합 로그를 생성하거나, 날짜별로 파일을 분리하여 관리할 수 있습니다. 두 번째로, 각 서버를 순회하면서 ssh로 로그 내용을 임시 파일로 다운로드합니다.

cat 명령으로 전체 로그를 읽어오는데, 실제 환경에서는 tail -n 10000 같은 제한을 두는 것이 좋습니다. while read 반복문으로 로그의 각 라인을 읽으면서 앞에 [서버명]을 추가합니다.

이 메타데이터가 있으면 나중에 grep "[app-01]" unified.log 같은 명령으로 특정 서버의 로그만 필터링할 수 있습니다. 세 번째로, 통합 로그뿐만 아니라 서버별 개별 로그도 날짜를 포함한 파일명으로 보관합니다.

이는 이중 백업 역할을 하며, 특정 서버만 집중적으로 분석할 때 유용합니다. sort 명령은 공백을 기준으로 첫 번째와 두 번째 필드(날짜와 시간)를 정렬 키로 사용하여 통합 로그를 시간 순으로 정렬합니다.

-o 옵션은 결과를 원본 파일에 덮어씁니다. 여러분이 이 스크립트를 사용하면 10대의 서버에서 각각 1만 줄의 로그가 있어도 하나의 통합 파일로 관리할 수 있습니다.

특정 시간대의 모든 서버 활동을 한눈에 보고, 서버 간 이벤트 순서를 정확히 파악하며, grep, awk 같은 도구로 통합 검색과 패턴 분석이 가능하고, 통합 로그를 Elasticsearch나 Splunk 같은 로그 분석 도구에 바로 로드할 수 있습니다.

실전 팁

💡 로그 라인 앞에 서버명뿐만 아니라 서비스명도 추가하세요 (예: [app-01:payment]). 마이크로서비스 환경에서 서비스별 필터링이 가능해집니다.

💡 통합 로그 파일이 너무 커지면 logrotate를 사용하거나, 날짜별/시간별로 파일을 분리하세요. 수십 GB 파일은 열기도 어렵습니다.

💡 로그 형식이 서버마다 다르다면 정규표현식으로 타임스탬프를 추출하여 ISO 8601 형식으로 통일하세요. 정렬과 검색이 정확해집니다.

💡 ELK Stack(Elasticsearch, Logstash, Kibana)이나 Loki 같은 전문 도구 도입을 고려하세요. 스크립트는 소규모에 적합하고, 대규모는 전문 도구가 효율적입니다.

💡 민감한 정보(비밀번호, 토큰, 개인정보)를 로그에서 자동으로 마스킹하는 단계를 추가하세요. sed나 awk로 정규표현식 치환하면 됩니다.


5. 네트워크 에러 처리

시작하며

여러분이 완벽한 로그 수집 스크립트를 만들었다고 생각했는데, 어느 날 네트워크가 불안정해지면서 스크립트가 중간에 멈춰버리거나, 일부 서버의 로그만 수집되고 나머지는 실패하는 상황을 겪을 수 있습니다. 심지어 에러 메시지도 제대로 남지 않아 어느 서버에서 문제가 생겼는지조차 알 수 없을 때도 있습니다.

이런 문제는 원격 작업에서 피할 수 없는 현실입니다. 네트워크는 언제든 지연될 수 있고, 서버는 재부팅 중일 수 있으며, 방화벽 정책이 갑자기 변경될 수도 있습니다.

로그 수집이 매일 자동으로 실행되는 상황에서, 하나의 에러가 전체 수집을 중단시키면 로그 누락이 발생하고, 이는 장애 분석을 불가능하게 만듭니다. 바로 이럴 때 필요한 것이 견고한 네트워크 에러 처리 전략입니다.

타임아웃 설정, 재시도 로직, 에러 로깅, 부분 실패 허용 등의 기법을 적용하면 불안정한 환경에서도 최대한 많은 로그를 안정적으로 수집할 수 있습니다.

개요

간단히 말해서, 네트워크 에러 처리는 연결 실패, 타임아웃, 전송 중단 같은 예외 상황을 감지하고 적절히 대응하는 방어적 프로그래밍 기법입니다. 마치 자동차에 에어백과 ABS가 있듯이, 스크립트에도 예상치 못한 상황을 처리하는 안전장치가 필요합니다.

왜 이 기법이 필요한지 실무 관점에서 설명하자면, 프로덕션 환경은 완벽하지 않습니다. 예를 들어, 새벽에 자동 실행되는 로그 수집 중에 특정 서버가 유지보수로 일시 중단되어 있을 수 있습니다.

에러 처리가 없으면 전체 수집이 실패하지만, 적절한 처리가 있으면 나머지 서버의 로그는 수집하고 실패한 서버만 나중에 재시도할 수 있습니다. 기존에는 에러가 발생하면 스크립트가 중단되고 수동으로 원인을 찾아 다시 실행했다면, 이제는 자동으로 재시도하고, 실패 시에도 계속 진행하며, 상세한 에러 로그를 남겨 문제를 신속히 파악할 수 있습니다.

이 기법의 핵심 특징은 첫째, 타임아웃으로 무한 대기 방지, 둘째, 지수 백오프(exponential backoff)를 사용한 지능적 재시도, 셋째, 에러 발생 시에도 전체 프로세스는 계속 진행되도록 설계입니다. 이러한 특징들이 중요한 이유는 로그 수집은 중요하지만 서비스 운영을 방해해서는 안 되며, 가능한 한 많은 데이터를 수집하되 불가능한 것에 시간을 낭비하지 않아야 하기 때문입니다.

코드 예제

#!/bin/bash
# 네트워크 에러 처리가 포함된 견고한 로그 수집 스크립트

SERVERS=("app-01" "app-02" "app-03")
REMOTE_USER="admin"
REMOTE_LOG="/var/log/app.log"
LOCAL_DIR="./logs"
ERROR_LOG="./collection_errors.log"
MAX_RETRIES=3
TIMEOUT=30

mkdir -p "$LOCAL_DIR"

# 재시도 로직을 포함한 로그 수집 함수
collect_log() {
  local server=$1
  local retry=0

  while [ $retry -lt $MAX_RETRIES ]; do
    echo "[$(date '+%H:%M:%S')] $server 시도 $((retry+1))/$MAX_RETRIES"

    # rsync with timeout and error handling
    if timeout $TIMEOUT rsync -avz --timeout=$TIMEOUT \
      "${REMOTE_USER}@${server}:${REMOTE_LOG}" \
      "${LOCAL_DIR}/${server}.log" 2>&1 | grep -q "total size"; then

      echo "✓ $server 성공"
      return 0  # 성공
    else
      error_code=$?
      echo "✗ $server 실패 (코드: $error_code)" | tee -a "$ERROR_LOG"

      retry=$((retry+1))
      if [ $retry -lt $MAX_RETRIES ]; then
        # 지수 백오프: 1초, 2초, 4초...
        wait_time=$((2**retry))
        echo "  ${wait_time}초 후 재시도..."
        sleep $wait_time
      fi
    fi
  done

  # 최대 재시도 초과
  echo "[$(date)] $server: 최종 실패 - 최대 재시도 횟수 초과" >> "$ERROR_LOG"
  return 1  # 실패
}

# 메인 수집 루프
success_count=0
fail_count=0

for server in "${SERVERS[@]}"; do
  if collect_log "$server"; then
    ((success_count++))
  else
    ((fail_count++))
  fi
  echo ""
done

# 최종 리포트
echo "======================================"
echo "수집 완료: 성공 $success_count, 실패 $fail_count"
[ $fail_count -gt 0 ] && echo "에러 로그: $ERROR_LOG"

설명

이것이 하는 일: 이 스크립트는 각 서버에 대해 최대 3번까지 자동으로 재시도하며, 실패 시 대기 시간을 점진적으로 늘리는 지수 백오프를 적용합니다. 한 서버의 실패가 다른 서버의 수집에 영향을 주지 않도록 에러를 격리하고, 모든 에러를 별도 파일에 상세히 기록합니다.

첫 번째로, collect_log 함수는 재사용 가능한 수집 로직을 캡슐화합니다. 함수 내부에서 while 반복문으로 재시도를 구현하는데, retry 변수가 MAX_RETRIES에 도달할 때까지 계속 시도합니다.

timeout 명령은 rsync가 지정된 시간 내에 완료되지 않으면 강제 종료하여 무한 대기를 방지합니다. 이는 이중 타임아웃 전략으로, timeout 명령과 rsync 자체의 --timeout 옵션을 모두 사용합니다.

두 번째로, rsync의 성공 여부를 grep으로 판단하는 부분이 중요합니다. rsync는 실패해도 일부 출력을 하기 때문에 단순히 종료 코드만 확인하면 부정확할 수 있습니다.

"total size"라는 문자열은 rsync가 성공적으로 완료되었을 때만 출력되므로, 이를 기준으로 성공을 판단합니다. 실패 시 종료 코드를 error_code 변수에 저장하여 로그에 기록하므로, 나중에 에러 원인(네트워크 타임아웃, 권한 문제, 파일 없음 등)을 파악할 수 있습니다.

세 번째로, 지수 백오프 로직은 2**retry 계산으로 구현됩니다. 첫 번째 재시도는 2초, 두 번째는 4초 후에 시도하는데, 이는 일시적 네트워크 혼잡이나 서버 부하 상황에서 매우 효과적입니다.

즉시 재시도하면 같은 문제에 계속 부딪히지만, 시간을 두면 상황이 개선될 가능성이 높습니다. 최종 실패 시 ERROR_LOG에 타임스탬프와 함께 기록하여 나중에 패턴 분석이 가능합니다.

여러분이 이 스크립트를 사용하면 일시적 네트워크 문제로 인한 실패가 대부분 자동 해결되고, 재시도에도 실패한 서버는 명확히 기록되어 수동 조치가 필요한 것만 처리할 수 있으며, 전체 수집 결과를 요약하여 운영 상황을 한눈에 파악할 수 있습니다. 또한 에러 로그를 모니터링 시스템과 연동하면 실패율이 높아질 때 자동 알림을 받을 수 있습니다.

실전 팁

💡 타임아웃 값은 네트워크 환경과 로그 파일 크기에 따라 조정하세요. 1GB 파일에 30초 타임아웃은 너무 짧습니다.

💡 에러 로그에 더 많은 컨텍스트를 추가하세요 (네트워크 상태, 서버 응답 시간, 파일 크기 등). 문제 패턴 분석에 유용합니다.

💡 실패한 서버 목록을 별도 파일에 저장하고, 나중에 그 목록만 재실행하는 스크립트를 만드세요. 효율적인 복구가 가능합니다.

💡 rsync의 --partial 옵션을 사용하면 전송 중단 시 이어받기가 가능합니다. 대용량 파일 전송 시 매우 유용합니다.

💡 최대 재시도 횟수와 백오프 시간을 환경 변수나 설정 파일로 관리하면 상황에 따라 유연하게 조정할 수 있습니다.


6. 병렬 로그 수집

시작하며

여러분이 지금까지 배운 방법으로 10대의 서버에서 로그를 수집한다면, 각 서버당 30초씩 걸린다고 했을 때 총 5분이 소요됩니다. 만약 서버가 50대라면?

25분이나 기다려야 합니다. 급한 장애 상황에서 25분을 기다리는 것은 용납할 수 없는 시간입니다.

이런 문제는 순차 처리의 근본적인 한계입니다. 한 번에 하나의 서버만 처리하므로, 서버 개수에 비례하여 시간이 늘어납니다.

특히 각 서버의 응답 시간이 불규칙할 때, 느린 한 서버가 전체 프로세스를 지연시키는 병목 현상이 발생합니다. 바로 이럴 때 필요한 것이 병렬 로그 수집입니다.

여러 서버를 동시에 처리하면 전체 소요 시간이 가장 느린 한 서버의 시간으로 단축됩니다. 10대를 동시에 처리하면 30초면 끝나고, 50대도 적절히 병렬화하면 2-3분 안에 완료할 수 있습니다.

개요

간단히 말해서, 병렬 로그 수집은 여러 서버에 대한 수집 작업을 동시에 실행하여 전체 처리 시간을 획기적으로 단축하는 기법입니다. 마치 은행에서 창구가 1개일 때와 10개일 때의 대기 시간 차이처럼, 작업을 병렬화하면 처리량이 극적으로 증가합니다.

왜 이 기법이 필요한지 실무 관점에서 설명하자면, 현대 시스템은 수십 대에서 수백 대의 서버로 구성됩니다. 예를 들어, 장애가 발생했을 때 모든 서버의 로그를 10분 내에 수집해야 한다면, 순차 처리로는 불가능하지만 병렬 처리로는 가능합니다.

또한 CPU 코어가 여러 개인 현대 서버에서는 병렬 처리가 자원을 효율적으로 활용하는 방법입니다. 기존에는 for 반복문으로 한 번에 한 서버씩 처리했다면, 이제는 백그라운드 프로세스(&), xargs, GNU parallel 같은 도구로 여러 작업을 동시에 실행할 수 있습니다.

이 기법의 핵심 특징은 첫째, 작업 시간이 서버 개수에 비례하지 않고 상수 시간에 가까워짐, 둘째, 시스템 자원(CPU, 네트워크)을 최대한 활용, 셋째, 동시 실행 개수를 조절하여 과부하 방지입니다. 이러한 특징들이 중요한 이유는 시간은 돈이고, 특히 장애 상황에서는 1분 1초가 매출과 고객 신뢰에 직결되기 때문입니다.

코드 예제

#!/bin/bash
# 병렬 처리를 통한 고속 로그 수집

SERVERS=("app-01" "app-02" "app-03" "app-04" "app-05" "web-01" "web-02" "db-01")
REMOTE_USER="admin"
REMOTE_LOG="/var/log/app.log"
LOCAL_DIR="./logs"
MAX_PARALLEL=4  # 동시 실행 최대 개수

mkdir -p "$LOCAL_DIR"

# 개별 서버 수집 함수
collect_server() {
  local server=$1
  local start_time=$(date +%s)

  echo "[$(date '+%H:%M:%S')] $server 시작"

  if rsync -avz --timeout=30 \
    "${REMOTE_USER}@${server}:${REMOTE_LOG}" \
    "${LOCAL_DIR}/${server}.log" &>/dev/null; then

    local end_time=$(date +%s)
    local duration=$((end_time - start_time))
    echo "[$(date '+%H:%M:%S')] ✓ $server 완료 (${duration}초)"
    return 0
  else
    echo "[$(date '+%H:%M:%S')] ✗ $server 실패"
    return 1
  fi
}

export -f collect_server  # 함수를 서브셸에서 사용 가능하도록
export REMOTE_USER REMOTE_LOG LOCAL_DIR

echo "병렬 로그 수집 시작 (최대 ${MAX_PARALLEL}개 동시 실행)"
start_total=$(date +%s)

# 방법 1: xargs를 사용한 병렬 처리
printf "%s\n" "${SERVERS[@]}" | xargs -P $MAX_PARALLEL -I {} bash -c 'collect_server "$@"' _ {}

# 방법 2: GNU parallel 사용 (설치되어 있다면)
# parallel -j $MAX_PARALLEL collect_server ::: "${SERVERS[@]}"

end_total=$(date +%s)
duration_total=$((end_total - start_total))

echo "======================================"
echo "전체 수집 완료: ${duration_total}초"
echo "서버 개수: ${#SERVERS[@]}"
echo "평균: $((duration_total / ${#SERVERS[@]}))초/서버 (병렬 처리 덕분)"

설명

이것이 하는 일: 이 스크립트는 여러 서버에 대한 로그 수집 작업을 동시에 여러 개 실행하여 전체 처리 시간을 대폭 단축합니다. 동시 실행 개수를 MAX_PARALLEL 변수로 제한하여 네트워크나 시스템이 과부하되지 않도록 조절합니다.

첫 번째로, collect_server 함수는 한 서버의 수집 작업을 캡슐화하고, 시작 시간과 종료 시간을 측정하여 각 서버별 소요 시간을 출력합니다. export -f로 함수를 export하면 서브셸에서도 이 함수를 호출할 수 있는데, xargs나 parallel은 새로운 프로세스를 생성하므로 이 과정이 필수입니다.

마찬가지로 필요한 환경 변수들도 export하여 서브셸에서 접근 가능하게 만듭니다. 두 번째로, xargs를 사용한 병렬 처리 부분이 핵심입니다.

printf로 서버 목록을 한 줄씩 출력하고, 이를 xargs에 파이프로 전달합니다. -P $MAX_PARALLEL 옵션은 최대 4개의 프로세스를 동시에 실행하라는 의미입니다.

-I {} 는 입력을 {} 위치에 치환하라는 뜻이고, bash -c로 각 서버에 대해 collect_server 함수를 호출합니다. 이렇게 하면 4개씩 묶음으로 병렬 처리되어, 8대 서버는 2번의 웨이브로 완료됩니다.

세 번째로, GNU parallel은 xargs보다 더 강력한 병렬 처리 도구입니다. -j 옵션으로 동시 실행 개수를 지정하고, ::: 다음에 배열을 전달하면 자동으로 각 요소에 대해 함수를 병렬 실행합니다.

parallel은 진행률 표시, 로그 파일 자동 생성, 실패 시 재시도 같은 고급 기능을 제공하지만, 별도 설치가 필요합니다. 최종 시간 측정 부분은 전체 수집 시간을 계산하여 병렬 처리의 효과를 정량적으로 보여줍니다.

여러분이 이 스크립트를 사용하면 10대 서버를 4개씩 병렬 처리하여 순차 처리 대비 3-4배 빠르게 수집할 수 있고, 100대 서버도 10-15분 안에 처리 가능하며, 동시 실행 개수를 조절하여 네트워크 대역폭과 서버 부하를 관리할 수 있습니다. 또한 각 서버별 소요 시간이 출력되므로 특별히 느린 서버를 식별하여 병목을 찾아낼 수 있고, CPU 코어가 많은 서버에서는 MAX_PARALLEL을 늘려 더욱 빠른 처리가 가능합니다.

실전 팁

💡 MAX_PARALLEL 값은 시스템 자원과 네트워크 대역폭에 맞게 조정하세요. CPU 코어 개수의 2배 정도가 적당한 시작점입니다.

💡 병렬 처리 시 화면 출력이 뒤섞일 수 있으므로, 각 서버의 출력을 개별 로그 파일로 리다이렉션하는 것을 고려하세요.

💡 GNU parallel의 --joblog 옵션으로 각 작업의 시작/종료 시간, 종료 코드를 자동 기록할 수 있습니다. 통계 분석에 유용합니다.

💡 원격 서버에 부하를 주지 않으려면 nice와 ionice로 rsync의 우선순위를 낮추세요. 서비스 성능에 영향을 최소화할 수 있습니다.

💡 AWS나 클라우드 환경에서는 동시 연결 수 제한이 있을 수 있습니다. 방화벽 로그나 클라우드 콘솔을 확인하여 제한에 걸리지 않도록 하세요.


#Bash#SSH#Rsync#LogCollection#Automation#Bash,Linux,로그분석,자동화

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.