infra
Platform

모듈 맵

[Network] 통신 장애를 실시간으로 탐지하는 쉘 스크립트 작성

0 / 35 완료

펼치기
0 / 35 완료0%

Networking · 32 / 35

[Network] 통신 장애를 실시간으로 탐지하는 쉘 스크립트 작성

bash 스크립트로 여러 서버의 포트 통신을 자동 점검하고 이상 시 로그를 남깁니다

🚨INCIDENT ALERT
HIGH

배포 전마다 여러 서버의 DB, Redis, API 포트를 사람이 손으로 확인합니다. 어느 날 한 포트를 빼먹어 장애가 났고, 체크리스트는 있었지만 자동 증거는 없었습니다.

반복 점검은 스크립트로 남겨야 합니다. 성공과 실패를 같은 형식으로 기록하면 장애 전후 비교가 쉬워집니다.

네트워크 통신 점검 쉘 스크립트

"어젯밤에 DB 연결이 잠깐 끊겼는데 언제 어느 서버인지 모르겠다." 자동화된 점검 스크립트가 있다면 이런 문제를 사전에 감지하고 기록할 수 있습니다. 이 챕터에서는 nc와 bash를 활용해 여러 서버의 포트 통신을 자동 점검하는 스크립트를 단계적으로 완성합니다.


1. 핵심 도구와 개념

이번 챕터에서 배울 것
  • 1nc -zv -w 옵션과 종료 코드($?)를 이용한 포트 점검 원리
  • 2타임스탬프 로그 기록과 bash 배열 루프 활용
  • 3단일 포트 점검에서 다중 서버 배열 루프 점검으로 확장
  • 4crontab 주기 표현식과 자동 실행 등록
  • 5장애 감지 시 슬랙·이메일 알림 연동
  • 6cron 환경 PATH 문제와 스크립트 실행 권한 트러블슈팅
실습 환경 준비
nc 설치 여부 및 경로 확인
which nc
스크립트 디렉터리 생성
mkdir -p /opt/scripts
cron 서비스 상태 확인
systemctl status crond || systemctl status cron
로그 디렉터리 확인
ls -la /var/log/netcheck.log 2>/dev/null || echo '로그 없음 (첫 실행 후 생성됨)'
💡개념

nc(netcat)와 종료 코드($?)를 이용한 포트 점검

매일 아침 출근하면 외부 API 연동 서비스가 죽어있습니다. 새벽에 외부 포트가 일시적으로 막혔다가 복구됐지만 서비스는 재시작되지 않은 것입니다. 이런 상황을 자동으로 감지하고 알람을 보내려면 포트 점검 스크립트가 필요합니다. nc의 종료 코드를 활용하면 조건 분기 없이 포트 열림/닫힘을 판별하는 스크립트를 깔끔하게 만들 수 있습니다.

nc(netcat)와 종료 코드($?)를 이용한 포트 점검

네트워크 점검 스크립트의 핵심은 nc종료 코드를 활용하는 것입니다.

nc -zv -w 옵션 설명

로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/networking/part6/exam_32 && cd /tmp/networking/part6/exam_32

nc -zv -w 3 192.168.1.200 3306
🔍실행 후 확인할 것
  • 핵심 출력명령 결과에서 성공/실패를 가르는 값을 먼저 확인합니다
  • 대상 식별IP, 포트, 인터페이스, 프로세스명처럼 다음 조치를 결정하는 필드를 봅니다
  • 다음 분기결과가 기대와 다르면 어느 계층을 이어서 점검할지 정합니다
옵션의미
-z데이터 전송 없이 연결 가능 여부만 확인 (zero I/O)
-v상세 출력 (verbose)
-w 3연결 타임아웃 3초 (wait)

종료 코드($?)로 성공/실패 판단

로컬 터미널
# 포트 점검 후 종료 코드 확인
nc -zv -w 3 192.168.1.200 3306
echo $?
# 0   → 연결 성공 (포트 열려 있음)
# 1   → 연결 실패 (포트 닫힘, 방화벽 차단, 서버 다운 등)

if문과 결합

로컬 터미널
nc -zv -w 3 192.168.1.200 3306 2>/dev/null
if [ $? -eq 0 ]; then
  echo "SUCCESS: DB 포트 정상"
else
  echo "FAIL: DB 포트 응답 없음"
fi

# 축약 표현 (더 일반적)
if nc -zv -w 3 192.168.1.200 3306 2>/dev/null; then
  echo "SUCCESS"
else
  echo "FAIL"
fi

2>/dev/null 의미

nc -v는 상세 메시지를 stderr(2)로 출력합니다. 2>/dev/null은 stderr를 /dev/null로 버려서 화면 출력을 억제합니다. 스크립트 로그에는 직접 작성한 메시지만 남도록 할 때 유용합니다.


💡개념

타임스탬프 로그와 배열 루프

포트 점검 스크립트가 실패했습니다. 그런데 언제 실패했는지 로그에 시간이 없어서 새벽 3시에 발생한 장애인지 아침 9시에 발생한 건지 알 수 없습니다. 또 점검 대상이 늘어날수록 서버 목록을 일일이 하드코딩해야 합니다. 타임스탬프 로깅과 배열 루프를 활용하면 스크립트를 실무에서 실제로 쓸 수 있는 수준으로 만들 수 있습니다.

타임스탬프 기록

로컬 터미널
# date 명령으로 현재 시각 형식 지정
date '+%Y-%m-%d %H:%M:%S'
# 출력: 2024-01-15 14:30:25

# 변수에 저장
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$TIMESTAMP] 점검 시작"

# 로그 파일에 기록 (>>: 이어쓰기, >: 덮어쓰기)
echo "[$TIMESTAMP] 포트 점검 결과" >> /var/log/netcheck.log

배열 선언과 루프

로컬 터미널
# bash 배열 선언
SERVERS=("192.168.1.100" "192.168.1.200" "192.168.1.201")
PORTS=(3306 6379 8080)

# 배열 순회
for server in "${SERVERS[@]}"; do
  echo "점검 중: $server"
done

# 인덱스와 함께 순회
for i in "${!SERVERS[@]}"; do
  echo "서버 $i: ${SERVERS[$i]}"
done

# 배열 길이
echo "서버 수: ${#SERVERS[@]}"

로그 파일 관리

로컬 터미널
LOG_FILE="/var/log/netcheck.log"

# 로그 디렉터리 생성 (없으면)
mkdir -p $(dirname $LOG_FILE)

# 로그 크기 제한 (7일 이상 된 항목 삭제)
find /var/log/ -name "netcheck*.log" -mtime +7 -delete

# logrotate 설정 파일 예시
# /etc/logrotate.d/netcheck
# /var/log/netcheck.log {
#     daily
#     rotate 7
#     compress
#     missingok
#     notifempty
# }

2. 기본 5줄 점검 스크립트

쉘 스크립트 작성 시 필수적인 Bash 리스트 핸들링과 logger 활용

네트워크 점검 자동화 스크립트를 작성할 때 다수의 호스트 IP를 일괄 루프로 돌리기 위해 SRE는 다음과 같은 문법적 디테일을 준수해야 합니다.

  1. 올바른 Bash 1차원 배열 선언: SERVERS=("192.168.1.100" "192.168.1.200")
    • 공백으로 항목을 구분하고 소괄호 ()로 감싸 선언해야 루프 내부에서 정상 순회합니다.
    • SERVERS=["192.168.1.100", "192.168.1.200"] (파이썬/JS 리스트 표기법) 또는 SERVERS:192.168.1.100,192.168.1.200 등은 문법 오류를 일으키며, 오직 SERVERS=("값1" "값2") 처럼 소괄호 구조만 정상 인식합니다.
  2. logger 명령어를 통한 시스템 로그 수집: 스크립트 진단 결과나 타임스탬프가 가미된 경고 메시지를 로컬 파일 외에 시스템 표준 중앙 로그 데몬에 전송할 때는 logger 명령어를 사용해 /var/log/syslog (또는 /var/log/messages)에 영구 기록시킵니다.
    로컬 터미널
    # 시스템 syslog에 실시간 네트워크 진단 경고 기록
    $ logger "SYSTEM-ALERT: Network ping check failed for database server!"
    

쉘 스크립트 작성 시 필수적인 Bash 리스트 핸들링과 logger 활용

네트워크 점검 자동화 스크립트를 작성할 때 다수의 호스트 IP를 일괄 루프로 돌리기 위해 SRE는 다음과 같은 문법적 디테일을 준수해야 합니다.

  1. 올바른 Bash 1차원 배열 선언: SERVERS=("192.168.1.100" "192.168.1.200")
    • 공백으로 항목을 구분하고 소괄호 ()로 감싸 선언해야 루프 내부에서 정상 순회합니다.
    • SERVERS=["192.168.1.100", "192.168.1.200"] (파이썬/JS 리스트 표기법) 또는 SERVERS:192.168.1.100,192.168.1.200 등은 문법 오류를 일으키며, 오직 SERVERS=("값1" "값2") 처럼 소괄호 구조만 정상 인식합니다.
  2. logger 명령어를 통한 시스템 로그 수집: 스크립트 진단 결과나 타임스탬프가 가미된 경고 메시지를 로컬 파일 외에 시스템 표준 중앙 로그 데몬에 전송할 때는 logger 명령어를 사용해 /var/log/syslog (또는 /var/log/messages)에 영구 기록시킵니다.
    로컬 터미널
    # 시스템 syslog에 실시간 네트워크 진단 경고 기록
    $ logger "SYSTEM-ALERT: Network ping check failed for database server!"
    

쉘 스크립트 작성 시 필수적인 Bash 리스트 핸들링과 logger 활용

네트워크 점검 자동화 스크립트를 작성할 때 다수의 호스트 IP를 일괄 루프로 돌리기 위해 SRE는 다음과 같은 문법적 디테일을 준수해야 합니다.

  1. 올바른 Bash 1차원 배열 선언: SERVERS=("192.168.1.100" "192.168.1.200")
    • 공백으로 항목을 구분하고 소괄호 ()로 감싸 선언해야 루프 내부에서 정상 순회합니다.
    • SERVERS=["값1", "값2"] (파이썬/JS 리스트 표기법) 또는 SERVERS:192.168.1.100... 등은 Bash에서 문법 오류를 일으킵니다.
  2. logger 명령어를 통한 시스템 로그 수집: 스크립트 진단 결과나 타임스탬프가 가미된 경고 메시지를 로컬 파일 외에 시스템 표준 중앙 로그 데몬에 전송할 때는 logger 명령어를 사용해 /var/log/syslog (또는 /var/log/messages)에 영구 기록시킵니다.
    로컬 터미널
    # 시스템 syslog에 실시간 네트워크 진단 경고 기록
    $ logger "SYSTEM-ALERT: Network ping check failed for database server!"
    
로컬 터미널
mkdir -p /tmp/networking/part6/scripts && cd /tmp/networking/part6/scripts
cat > targets.txt << 'EOF'
8.8.8.8 Google DNS
1.1.1.1 Cloudflare DNS
192.168.1.1 Gateway
EOF
cat > check_base.sh << 'EOF'
#!/bin/bash
# 네트워크 점검 기본 템플릿
TARGETS_FILE="${1:-targets.txt}"
LOG_FILE="network_check_$(date +%Y%m%d_%H%M%S).log"

check_host() {
    local ip="$1"
    local name="$2"
    if ping -c 1 -W 2 "$ip" &>/dev/null; then
        echo "✓ $name ($ip): reachable"
    else
        echo "✗ $name ($ip): UNREACHABLE"
    fi
}

echo "=== Network Check $(date) ===" | tee "$LOG_FILE"
while read -r ip name; do
    check_host "$ip" "$name" | tee -a "$LOG_FILE"
done < "$TARGETS_FILE"
EOF
chmod +x check_base.sh
1단계: 최소한의 포트 점검 스크립트 작성

먼저 가장 단순한 형태의 포트 점검 스크립트를 작성합니다.

로컬 터미널
#!/bin/bash
# 파일: /opt/scripts/netcheck-simple.sh
# 기본 5줄 포트 점검 스크립트

TARGET_HOST="192.168.1.200"
TARGET_PORT="3306"
LOG_FILE="/var/log/netcheck.log"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

nc -zv -w 3 "$TARGET_HOST" "$TARGET_PORT" 2>/dev/null \
  && echo "[$TIMESTAMP] OK   ${TARGET_HOST}:${TARGET_PORT}" >> "$LOG_FILE" \
  || echo "[$TIMESTAMP] FAIL ${TARGET_HOST}:${TARGET_PORT}" >> "$LOG_FILE"

실행 권한 부여 및 테스트

로컬 터미널
chmod +x /opt/scripts/netcheck-simple.sh

# 직접 실행
/opt/scripts/netcheck-simple.sh

# 로그 확인
cat /var/log/netcheck.log
# [2024-01-15 14:30:25] OK   192.168.1.200:3306
# [2024-01-15 14:35:25] FAIL 192.168.1.200:3306  ← 장애 발생 시

&& 와 || 연산자 이해

로컬 터미널
명령A && 명령B   # 명령A가 성공(0)일 때만 명령B 실행
명령A || 명령B   # 명령A가 실패(non-0)일 때만 명령B 실행

# nc 성공 → && 이후 OK 기록
# nc 실패 → || 이후 FAIL 기록
2단계: 다중 서버 배열 루프 점검 스크립트

여러 서버와 포트를 배열로 관리하는 확장 버전을 작성합니다.

로컬 터미널
#!/bin/bash
# 파일: /opt/scripts/netcheck.sh
# 다중 서버 포트 점검 스크립트

LOG_FILE="/var/log/netcheck.log"
TIMEOUT=3

# 점검 대상: "호스트:포트:설명" 형식
TARGETS=(
  "192.168.1.200:3306:MySQL-DB"
  "192.168.1.200:6379:Redis-Cache"
  "192.168.1.100:8080:App-Server"
  "192.168.1.100:80:Nginx"
  "10.0.0.10:5432:PostgreSQL"
)

# 로그 디렉터리 생성
mkdir -p $(dirname "$LOG_FILE")

TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
FAIL_COUNT=0

echo "[$TIMESTAMP] ===== 네트워크 점검 시작 =====" >> "$LOG_FILE"

for target in "${TARGETS[@]}"; do
  # "호스트:포트:설명" 파싱
  HOST=$(echo "$target" | cut -d: -f1)
  PORT=$(echo "$target" | cut -d: -f2)
  DESC=$(echo "$target" | cut -d: -f3)

  # 포트 점검
  if nc -zv -w "$TIMEOUT" "$HOST" "$PORT" 2>/dev/null; then
    echo "[$TIMESTAMP] OK   ${HOST}:${PORT} (${DESC})" >> "$LOG_FILE"
  else
    echo "[$TIMESTAMP] FAIL ${HOST}:${PORT} (${DESC})" >> "$LOG_FILE"
    FAIL_COUNT=$((FAIL_COUNT + 1))
  fi
done

echo "[$TIMESTAMP] ===== 점검 완료 (실패: ${FAIL_COUNT}건) =====" >> "$LOG_FILE"

# 실패가 있으면 비정상 종료 코드 반환 (cron에서 메일 발송 트리거 가능)
exit $FAIL_COUNT

실행 및 로그 확인

로컬 터미널
chmod +x /opt/scripts/netcheck.sh
/opt/scripts/netcheck.sh

# 로그 확인
tail -20 /var/log/netcheck.log
# [2024-01-15 14:30:00] ===== 네트워크 점검 시작 =====
# [2024-01-15 14:30:00] OK   192.168.1.200:3306 (MySQL-DB)
# [2024-01-15 14:30:00] OK   192.168.1.200:6379 (Redis-Cache)
# [2024-01-15 14:30:00] FAIL 192.168.1.100:8080 (App-Server)
# [2024-01-15 14:30:00] OK   192.168.1.100:80 (Nginx)
# [2024-01-15 14:30:00] FAIL 10.0.0.10:5432 (PostgreSQL)
# [2024-01-15 14:30:00] ===== 점검 완료 (실패: 2건) =====
3단계: crontab에 등록해 자동 실행

5분마다 자동으로 점검이 실행되도록 cron에 등록합니다.

로컬 터미널
# crontab 편집
crontab -e

# 다음 내용 추가:
# 5분마다 점검 스크립트 실행
*/5 * * * * /opt/scripts/netcheck.sh

# 매시간 정각에 실행
# 0 * * * * /opt/scripts/netcheck.sh

# 매일 오전 9시에 실행
# 0 9 * * * /opt/scripts/netcheck.sh

cron 표현식 이해

*/5 * * * * 명령
│   │ │ │ └── 요일 (0-7, 0과 7은 일요일)
│   │ │ └──── 월 (1-12)
│   │ └────── 일 (1-31)
│   └──────── 시 (0-23)
└──────────── 분 (0-59)

*/5 = 5의 배수 분마다 (0, 5, 10, 15 ...)

cron 등록 후 확인

로컬 터미널
# 현재 crontab 확인
crontab -l
# */5 * * * * /opt/scripts/netcheck.sh

# cron 서비스 상태 확인
systemctl status crond   # CentOS
systemctl status cron    # Ubuntu

# cron 실행 로그 확인
grep netcheck /var/log/cron        # CentOS
grep netcheck /var/log/syslog      # Ubuntu
journalctl -u cron | grep netcheck # systemd 환경

cron 실행 환경 주의사항

로컬 터미널
# cron은 PATH가 제한되어 있어 전체 경로 사용 필수
# /etc/nc → nc 대신 /usr/bin/nc 또는 /bin/nc
which nc   # nc의 전체 경로 확인

# 스크립트 상단에 PATH 명시하거나 스크립트 내에서 절대 경로 사용
NC=/usr/bin/nc
$NC -zv -w 3 "$HOST" "$PORT"
4단계: 장애 시 알림 추가 (슬랙/이메일)

장애 감지 시 즉시 알림이 가도록 스크립트를 확장합니다.

로컬 터미널
#!/bin/bash
# 파일: /opt/scripts/netcheck-alert.sh
# 장애 감지 시 알림 포함 버전

LOG_FILE="/var/log/netcheck.log"
TIMEOUT=3
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
ALERT_EMAIL="ops-team@company.com"

TARGETS=(
  "192.168.1.200:3306:MySQL-DB"
  "192.168.1.200:6379:Redis-Cache"
  "192.168.1.100:8080:App-Server"
)

TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
HOSTNAME=$(hostname)
FAILED_SERVICES=""

for target in "${TARGETS[@]}"; do
  HOST=$(echo "$target" | cut -d: -f1)
  PORT=$(echo "$target" | cut -d: -f2)
  DESC=$(echo "$target" | cut -d: -f3)

  if nc -zv -w "$TIMEOUT" "$HOST" "$PORT" 2>/dev/null; then
    echo "[$TIMESTAMP] OK   ${HOST}:${PORT} (${DESC})" >> "$LOG_FILE"
  else
    echo "[$TIMESTAMP] FAIL ${HOST}:${PORT} (${DESC})" >> "$LOG_FILE"
    FAILED_SERVICES="${FAILED_SERVICES}\n- ${DESC} (${HOST}:${PORT})"
  fi
done

# 장애 발생 시 알림
if [ -n "$FAILED_SERVICES" ]; then
  ALERT_MSG="[$HOSTNAME] 네트워크 장애 감지 (${TIMESTAMP}):\n${FAILED_SERVICES}"

  # 슬랙 알림
  if command -v curl &>/dev/null; then
    curl -s -X POST "$SLACK_WEBHOOK" \
      -H 'Content-type: application/json' \
      --data "{\"text\":\"$ALERT_MSG\"}" > /dev/null
  fi

  # 이메일 알림 (mailx 필요)
  if command -v mail &>/dev/null; then
    echo -e "$ALERT_MSG" | mail -s "[ALERT] 네트워크 장애 감지" "$ALERT_EMAIL"
  fi
fi

3. 장애 사례 분석

증상

로컬 터미널
/opt/scripts/netcheck.sh
# [2024-01-15 14:30:00] 점검 시작
# [2024-01-15 14:30:00] OK   192.168.1.200:3306 (MySQL-DB)
# (여기서 스크립트가 수 분 동안 멈춤...)

원인 분석

-w 타임아웃 옵션 없이 nc를 사용하면, 방화벽이 패킷을 DROP하는 서버에 대해 기본 타임아웃(수십 초)만큼 대기합니다.

로컬 터미널
# 잘못된 예 — 타임아웃 없음
nc -zv 192.168.1.201 3306   # DROP 구간에서 멈춤

# 올바른 예 — 3초 타임아웃
nc -zv -w 3 192.168.1.201 3306

스크립트 실행 시간 측정

로컬 터미널
# 타임아웃 없이 5개 서버 점검 시
# → 각 DROP 서버에서 ~30초 × 5 = 150초 소요

# -w 3 설정 시
# → 각 DROP 서버에서 3초 × 5 = 15초 소요
# → 10배 빠른 점검 완료

수정 후 스크립트

로컬 터미널
# TIMEOUT 변수를 환경에 맞게 조정
TIMEOUT=3  # 내부망: 3초, 인터넷 구간: 5-10초

nc -zv -w "$TIMEOUT" "$HOST" "$PORT" 2>/dev/null

교훈: 네트워크 점검 스크립트에서 -w(타임아웃)는 선택이 아닌 필수입니다. cron에서 실행되는 스크립트가 멈추면 다음 실행 시 겹쳐서 프로세스가 쌓입니다.

증상

로컬 터미널
# crontab에 등록했는데 로그가 쌓이지 않음
crontab -l
# */5 * * * * /opt/scripts/netcheck.sh

# 5분 후에도 /var/log/netcheck.log 변화 없음

원인 확인 및 해결

위험 명령어

이 명령은 실행 중인 서비스 상태를 바꿔 순간적인 중단이나 설정 반영 실패를 만들 수 있습니다. 운영 트래픽 영향과 재시작 후 확인 명령을 먼저 준비하세요.

로컬 터미널
# [원인 1] 실행 권한 없음
ls -la /opt/scripts/netcheck.sh
# -rw-r--r-- 1 root root ...   ← 실행 권한 없음!

# 해결
chmod +x /opt/scripts/netcheck.sh

# [원인 2] cron 서비스 미실행
systemctl status crond
# inactive (dead)

# 해결
systemctl start crond
systemctl enable crond

# [원인 3] nc가 cron 환경 PATH에 없음
# cron은 /usr/bin:/bin 정도만 PATH에 포함
# 스크립트 첫 줄에 PATH 추가
head -3 /opt/scripts/netcheck.sh
#!/bin/bash
export PATH=/usr/bin:/bin:/usr/local/bin:$PATH
# ...

# [원인 4] cron 실행 오류 로그 확인
grep CRON /var/log/syslog | tail -20    # Ubuntu
journalctl -u crond | tail -20          # CentOS systemd

# [원인 5] cron 출력을 파일로 리다이렉션
# crontab 항목에 출력 리다이렉션 추가
# */5 * * * * /opt/scripts/netcheck.sh >> /var/log/netcheck-cron.log 2>&1

cron 디버깅 팁

로컬 터미널
# cron 환경에서 수동 테스트
env -i HOME=/root SHELL=/bin/bash /bin/bash /opt/scripts/netcheck.sh

# 이렇게 실행해서 오류가 나면 cron에서도 동일하게 오류 발생

교훈: cron 등록 후 반드시 실제로 실행되는지 로그로 확인하세요. 실행 권한, PATH, cron 서비스 상태를 순서대로 점검합니다.


4. 실무 현장 관점

💼
실무 맥락
현업 패턴

실무에서 이 스크립트가 필요한 순간

  1. 정기 배포/패치 후 확인: 배포 후 모든 서비스 간 연결이 정상인지 자동으로 확인하는 스모크 테스트로 활용

  2. 24시간 모니터링: 모니터링 도구(Zabbix, Prometheus 등)가 없는 소규모 환경에서 간단한 가용성 모니터링 역할

  3. 장애 보고서 근거: 장애 발생 시 정확한 시각과 영향 받은 서버를 로그 파일에서 바로 확인 가능

완성된 프로덕션 수준 스크립트

로컬 터미널
#!/bin/bash
# /opt/scripts/netcheck-production.sh
# 프로덕션 수준 네트워크 점검 스크립트

set -euo pipefail

# === 설정 ===
LOG_FILE="/var/log/netcheck.log"
LOG_MAX_DAYS=30
TIMEOUT=3
HOSTNAME=$(hostname -f)

# === 점검 대상 (환경에 맞게 수정) ===
TARGETS=(
  "db-primary.internal:3306:MySQL-Primary"
  "db-replica.internal:3306:MySQL-Replica"
  "cache.internal:6379:Redis"
  "app1.internal:8080:App-Server-1"
  "app2.internal:8080:App-Server-2"
  "lb.internal:80:Load-Balancer"
)

# === 함수 정의 ===
log() {
  local level=$1
  shift
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" >> "$LOG_FILE"
}

check_port() {
  local host=$1
  local port=$2
  nc -zv -w "$TIMEOUT" "$host" "$port" 2>/dev/null
  return $?
}

cleanup_old_logs() {
  find "$(dirname "$LOG_FILE")" -name "netcheck*.log" -mtime +"$LOG_MAX_DAYS" -delete 2>/dev/null
}

# === 메인 로직 ===
mkdir -p "$(dirname "$LOG_FILE")"
cleanup_old_logs

FAIL_COUNT=0
FAIL_LIST=""

log "INFO" "=== 점검 시작 (호스트: $HOSTNAME, 대상: ${#TARGETS[@]}개) ==="

for target in "${TARGETS[@]}"; do
  HOST=$(echo "$target" | cut -d: -f1)
  PORT=$(echo "$target" | cut -d: -f2)
  DESC=$(echo "$target" | cut -d: -f3)

  if check_port "$HOST" "$PORT"; then
    log "OK  " "${HOST}:${PORT} [${DESC}]"
  else
    log "FAIL" "${HOST}:${PORT} [${DESC}]"
    FAIL_COUNT=$((FAIL_COUNT + 1))
    FAIL_LIST="${FAIL_LIST} ${DESC}(${HOST}:${PORT})"
  fi
done

if [ "$FAIL_COUNT" -gt 0 ]; then
  log "WARN" "점검 완료 — 실패 ${FAIL_COUNT}건:${FAIL_LIST}"
else
  log "INFO" "점검 완료 — 전체 정상"
fi

exit "$FAIL_COUNT"

Nagios/Zabbix 등과의 연동

로컬 터미널
# Nagios 플러그인 형태로 변환
# 종료 코드 규칙: 0=OK, 1=WARNING, 2=CRITICAL, 3=UNKNOWN

if [ "$FAIL_COUNT" -eq 0 ]; then
  echo "OK: 전체 ${#TARGETS[@]}개 서버 정상"
  exit 0
elif [ "$FAIL_COUNT" -le 1 ]; then
  echo "WARNING: ${FAIL_COUNT}개 서버 응답 없음"
  exit 1
else
  echo "CRITICAL: ${FAIL_COUNT}개 서버 응답 없음"
  exit 2
fi

5. 핵심 명령어 요약

목적명령어/표현
타임아웃 포함 포트 점검nc -zv -w 3 192.168.1.200 3306
종료 코드 확인echo $? (0=성공, 비0=실패)
타임스탬프 생성$(date '+%Y-%m-%d %H:%M:%S')
로그 파일에 기록echo "메시지" >> /var/log/netcheck.log
bash 배열 선언TARGETS=("host1:port1:desc1" "host2:port2:desc2")
배열 순회for item in "${TARGETS[@]}"; do ...; done
문자열 파싱echo "$item" | cut -d: -f1
cron 5분 주기*/5 * * * * /opt/scripts/netcheck.sh
cron 등록 확인crontab -l

정리

  • nc -zv -w 3-w 3은 타임아웃 3초 설정 — 스크립트 무한 대기 방지 필수
  • $?로 nc의 성공/실패 판단 (0=성공, 1=실패)
  • 배열(TARGETS=())과 for 루프로 여러 서버를 간결하게 점검
  • $(date '+%Y-%m-%d %H:%M:%S')로 타임스탬프를 로그에 함께 기록
  • crontab */5 * * * *으로 5분마다 자동 실행
  • cron 실행 전 실행 권한, PATH, 로그 리다이렉션 확인 필수

지식 확인

퀴즈 — 5문제

Q1

5분마다 10개 DB 서버의 3306 포트를 자동 점검하는 스크립트를 작성했습니다. 실제 운영에서 `nc 192.168.1.200 3306` 명령을 타임아웃 옵션 없이 사용하면 어떤 문제가 발생할 수 있습니까?

Q2

셸 스크립트에서 `$?`의 의미는?

Q3

crontab 항목 `*/5 * * * * /opt/netcheck.sh`의 실행 주기는?

Q4

스크립트에서 여러 서버를 배열로 저장하고 루프로 점검할 때 올바른 bash 배열 문법은?

Q5

네트워크 점검 스크립트에서 로그에 타임스탬프를 추가하는 올바른 방법은?

0 / 5 답변

🧪 실습으로 확인하기

포트는 열렸다는데 왜 안 되지? — ss/netstat/telnet으로 TCP 진단

초급

"포트 8080 열었는데요?"와 "왜 안 돼요?" 사이의 간극을 메우는 실습. ss로 바인딩 상태를 확인하고, telnet/nc으로 원격 연결을 테스트하고, iptables 방화벽을 진단하고, 바인딩 주소(0.0.0.0 vs 127.0.0.1)까지 수정하는 4단계 TCP 포트 진단 플로우를 완성한다.

35📋 4단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

networking고급 · 60
[Network] Docker & Kubernetes 컨테이너 네트워크 실전 디버깅
Networking 트랙 계속
docker입문 · 30
[Docker] 백엔드 개발자에게 Docker와 컨테이너 가상화가 필수인 이유
Docker 트랙 시작점