- 포트충돌 발생
우연치 않게 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 등 다른 값을 모니터링 할 수 있다.