• 포트충돌 발생

우연치 않게 A 프로그램의 프로세스와 B 프로그램의 프로세스가 포트 충돌이 발생하여 B 프로세스가 기동이 안되는 현상 발생으로 해당 스크립트를 이용하여 포트사용 현황을 파악함.

A 프로그램은 OS 커널 파라메터의 ephemeral port (40000 ~ 50000) 기준으로 프로세스의 port를 랜덤하게 사용.

이슈가 발생할 때 A 프로그램은 50022 포트를 사용하여 기동됨.

B 프로세스는 50022 port를 바인딩하여 기동하는데 A 프로세스가 50022 port를 선점하여 프로세스 기동에 문제 발생.

 

  • 이것을 해결하기 위한 방법들
  1. Local Address IP를 프로세스 별로 다르게 사용하여 동일 포트를 사용해도 문제가 없게 IP별로 분리.
    (netstat 로 확인 시 0.0.0.0 이나 ::: 표기되는 경우 특정 IP로 변경이 필요함. 해당 표기로 된것은 ALL IP 임.)
  2. ephemeral port 범위 변경
  3. net.ipv4.ip_local_reserved_ports 커널 파라메터에 예외 포트 추가(50022 포트를 추가하여 다른 AP에서 랜덤포트로 사용하지 못하게 함)
  4. B 프로세스의 바인딩 포트들 변경

 

위 4가지 방법들 중 해당 이슈의 시스템들은 4번 방법을 이용하기로 함.

그런데 변경하고자 하는 새로운 포트들이 시스템에서 사용되는지 확인이 필요했다.

여러 업무 프로세스가 수 많은 포트들을 사용하는 환경인데 인프라측에서는 서비스 포트 리스트를 정확히 파악하지 못하고 있어서 해당 스크립트를 통해 수일에 걸쳐 모니터링하게 됨.

 

  • 스크립트 설명

    1. Local Address의 특정 포트들의 netstat 현황을 로깅하는 간단한 스크립트 임.
    2. portmonitor.sh, portmonitor.port - 2개의 파일로 구성되어 있음.
    3. port_monitor.sh -h 하면 기본적인 사용법이 출력됨.
    4. port 변경은 portmonitor.port 파일을 수정통해 가능.

 

1. 스크립트 파일

# vim portmonitor.sh

#!/bin/bash

# Port Argument
. ./portmonitor.port

# log dir
LOGDIR=.

PORT=$(echo $PORTS | sed 's/,/$|:/g; s/^/\:/g')

# Default Argument
INTERVAL="600"
BTIME=`date +%Y%m%d%H -d "+1 days"`

# Options Function
function usage()
{
    echo -e "\e[34m\e[1m*** The options are :\e[21\033[0m"
    echo "-h of --help : help"
    echo "-i or --interval : interval (per second)"
    echo "-t or --time : break time"
    echo ""
    echo -e "\e[34m\e[1m*** Usage :\e[21\033[0m"
    echo -e "\033[1;32m*start :::"
    echo "example 1 : $LOGDIR/`basename $0` &"
    echo "            => Per 600 sec, Until after 24 hours. (This is a default)"
    echo ""
    echo "example 2 : $LOGDIR/`basename $0` --interval=60 --time=2018061320 &"
    echo "            => Per 60 sec, Until 2018.06.13. 20 o'clock."
    echo ""
    echo "example 3 : $LOGDIR/`basename $0` --interval=10 &"
    echo "            => Per 10 sec, Until after 24 hours."
    echo -e "\033[0m"
    echo -e "\033[1;31m*stop :::"
    echo "example 1 : killall -9 `basename $0`"
    echo "            => All process kill of `basename $0`"
    echo ""
    echo "example 2 : kill -9 (PID)"
    echo -e "            => 1 process kill of `basename $0`\033[0m"
}

# Options Input
while [ "$1" != "" ]; do
    PARAM=`echo $1 | awk -F= '{print $1}'`
    VALUE=`echo $1 | awk -F= '{print $2}'`
    case $PARAM in
        -h | --help)
            usage
            exit
            ;;
        -i | --interval)
            INTERVAL=$VALUE
            ;;
        -t | --time)
            BTIME=$VALUE
            ;;
        *)
            echo -e "\033[1;31m\e[5mError\e[25m: unknown parameter \"$PARAM\"\033[0m"
            echo ""
            usage
            exit 1
            ;;
    esac
    shift
done

# Time Check
CURRTIME=`date +%Y%m%d%H`
if [ $CURRTIME -gt $BTIME ]; then
    echo -e "\033[1;31m\e[5mError. \e[25mOption break time is older than current time."
    echo -e "Change the time of '--time option'\033[0m"
    echo ""
    echo -e "*Break Time    : \033[1;31m\e[5m$BTIME\e[25m\033[0m";
    echo -e "*Current Time  : $CURRTIME (`date +%Y.%m.%d." "%H" o'clock"`)" 
    exit 0
fi

echo ""
echo "*Interval Time : $INTERVAL(second)";
echo "*Break Time    : $BTIME";
echo "*Log File      : $LOGDIR/`basename $0 | awk -F '.' '{print "log_"$1}'`_$HOSTNAME.log";
echo "                (no detect port, no log file)";

# Logging (until to break time)
while [ $BTIME -ge $CURRTIME ]
do 
LIST=$(netstat -an | awk '{if($4~/'$PORT'/) print}')
if [ -n "$LIST" ]; then
    LOGTIME=`date +%Y-%m-%d"_"%T"_"%Z" UnixTime="%s`
    echo $LOGTIME MonitorPorts=$PORTS >> $LOGDIR/`basename $0 | awk -F '.' '{print "log_"$1}'`_$HOSTNAME.log
    echo "$LIST" >> $LOGDIR/`basename $0 | awk -F '.' '{print "log_"$1}'`_$HOSTNAME.log
    printf -vch "%94s" ""
    printf "%s\n" "${ch// /-}" >> $LOGDIR/`basename $0 | awk -F '.' '{print "log_"$1}'`_$HOSTNAME.log
    ULIST=$(echo "$LIST" | awk '{print $4}' | sed 's/\:\:\:/\:/g' | awk -F: '{print $2}' | sort -n | uniq)
    echo "Using Ports :" $ULIST >> $LOGDIR/`basename $0 | awk -F '.' '{print "log_"$1}'`_$HOSTNAME.log
    printf -vch "%94s" ""
    printf "%s\n" "${ch// /=}" >> $LOGDIR/`basename $0 | awk -F '.' '{print "log_"$1}'`_$HOSTNAME.log
fi
sleep $INTERVAL
CURRTIME=`date +%Y%m%d%H`
done

exit 0

 

 

2. 포트 설정 파일

# vim portmonitor.port 

PORTS="21,22,80"

 

 

3. 실행하기

portmonitor.sh 와 portmonitor.port 파일을 특정 디렉토리에 위치 시킨 후 portmonitor.sh에 실행권한을 주고 실행한다.

1) 기본 실행 :

인터벌 600초(10분) 간격으로 현재시각에서 1일 후 까지 Local Address 의 설정한 포트를 감시하며 로깅한다. (기본값)

[root@testserver ~]# date
Thu Jun 14 13:17:37 KST 2018

[root@testserver ~]# ./portmonitor.sh &
[1] 194155
[root@testserver ~]# 
*Interval Time : 600(second)
*Break Time    : 2018061513
*Log File      : ./log_portmonitor_testserver.log
                (no detect port, no log file)

 

2) 옵션 지정 실행 1 :

인터벌 10초 간격으로 2018.06.14.  16시 까지 수행

[root@testserver ~]# ./portmonitor.sh --interval=10 --time=2018061416 &
[1] 194196
[root@testserver ~]# 
*Interval Time : 10(second)
*Break Time    : 2018061416
*Log File      : ./log_portmonitor_testserver.log
                (no detect port, no log file)

 

3) 옵션 지정 실행 2 :

인터벌 60초 간격으로 수행, 스크립트가 구동 되는 시각은 실행 시각으로 부터 1일 후(기본값)

[root@testserver ~]# ./portmonitor.sh --interval=60 &
[1] 194357
[root@testserver ~]# 
*Interval Time : 60(second)
*Break Time    : 2018061513
*Log File      : ~/log_portmonitor_testserver.log
                (no detect port, no log file)

 

4) 옵션 지정 실행 3 :

스크립트가 구동 되는 시각은 2018.06.14.  15시 까지. 인터벌 600초 간격(기본값)

[root@testserver ~]# ./portmonitor.sh --time=2018061415 &
[2] 194399
[root@testserver ~]# 
*Interval Time : 600(second)
*Break Time    : 2018061415
*Log File      : ./log_portmonitor_testserver.log
                (no detect port, no log file)

 

4) help

[root@testserver ~]# ./portmonitor.sh -h
*** The options are :
-h or --help : help
-i or --interval : interval (per second)
-t or --time : break time

*** Usage :
*start :::
example 1 : ./portmonitor.sh &
            => Per 600 sec, Until after 24 hours. (This is a default)

example 2 : ./portmonitor.sh --interval=60 --time=2018061320 &
            => Per 60 sec, Until 2018.06.13. 20 o'clock.

example 3 : ./portmonitor.sh --interval=10 &
            => Per 10 sec, Until after 24 hours.

*stop :::
example 1 : killall -9 portmonitor.sh
            => All process kill of portmonitor.sh

example 2 : kill -9 (PID)
            => 1 process kill of portmonitor.sh

 

 

4. 로그 확인

# cat /var/log/log_portmonitor_testserver.log

2018-06-14_13:17:40_KST UnixTime=1528949500 MonitorPorts=21,22,80
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      
tcp        0      0 192.168.0.65:22             192.168.0.159:58855         ESTABLISHED 
tcp        0      0 192.168.0.65:22             192.168.0.159:57468         ESTABLISHED 
tcp        0      0 :::80                       :::*                        LISTEN      
tcp        0      0 :::22                       :::*                        LISTEN      
----------------------------------------------------------------------------------------------
Using Ports : 22 80
==============================================================================================

로그는 스크립트를 수행한 위치의 디렉토리에 생성된다.
만약 필터링되는 포트가 없으면 로그파일은 생성되지 않는다.

 

아래 커맨드는 Local Address의 포트 12000 ~ 12900 사이의 포트중에서 현재 사용중인 포트를 출력함.

[root@testserver ~]# netstat -na | awk -F'[[:space:]]+|:' 'NR>2 && $5>=12000 && $5<=12900'

 

  • 해당 스크립트는 서버에 TMOUT 변수가 설정되어 있으면 그 값보다 낮게 interval 값을 조정해서 사용해야 한다. 그렇지 않으면 스크립트가 종료된다.
    또는 쉘 상에서 declare -x TMOUT=0 으로 TMOUT 변수를 0 (disable) 시키고서 스크립트를 수행한다.
  • 스크립트를 조금 수정하면 IP 등 다른 값을 모니터링 할 수 있다.
portmonitor script

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다