- 포트충돌 발생
우연치 않게 A 프로그램의 프로세스와 B 프로그램의 프로세스가 포트 충돌이 발생하여 B 프로세스가 기동이 안되는 현상 발생으로 해당 스크립트를 이용하여 포트사용 현황을 파악함.
A 프로그램은 OS 커널 파라메터의 ephemeral port (40000 ~ 50000) 기준으로 프로세스의 port를 랜덤하게 사용.
이슈가 발생할 때 A 프로그램은 50022 포트를 사용하여 기동됨.
B 프로세스는 50022 port를 바인딩하여 기동하는데 A 프로세스가 50022 port를 선점하여 프로세스 기동에 문제 발생.
- 이것을 해결하기 위한 방법들
- Local Address IP를 프로세스 별로 다르게 사용하여 동일 포트를 사용해도 문제가 없게 IP별로 분리.
(netstat 로 확인 시 0.0.0.0 이나 ::: 표기되는 경우 특정 IP로 변경이 필요함. 해당 표기로 된것은 ALL IP 임.) - ephemeral port 범위 변경
- net.ipv4.ip_local_reserved_ports 커널 파라메터에 예외 포트 추가(50022 포트를 추가하여 다른 AP에서 랜덤포트로 사용하지 못하게 함)
- 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 등 다른 값을 모니터링 할 수 있다.