◾️ ionice
서버에 부하가 걸리는 명령인 경우 ionice
커맨드를 앞에 붙이자.
$ ionice -c 2 -n 7 nice -n 19
-c 2
: 디스크 I/O의 실행 우선 순위 조정-n 7
: 명령 우선 순위 낮추기-n 19
: 프로세스 실행 우선 순위 가장 낮게
◾️ 작업 결과 등의 일시적인 파일 저장은 /tmp와 /var/tmp에 저장하자
/tmp, /var/tmp 경로에 파일을 저장하면 작업 결과를 일부러 지우는(rm -f
) 수고를 덜 수 있다.
- /tmp는 (tmpfs에 마운트 된 경우) 다시 시작하면 파일이 사라짐.
- /var/tmp는 다시 시작해도 파일이 사리지지는 않고 /tmp 보다 오랜 기간 유지됨.
- /tmp (10일), /var/tmp (30일)
- 둘 다 정기적으로 비워짐
more /usr/lib/tmpfiles.d/tmp.conf
으로 확인 가능
◾️ root 계정의 로그인 실패 정보 확인
▪️ 계정별 로그인 실패 건수 확인
아래는 계정별 로그인 실패 건수를 확인한다.
로그인 실패가 1000단위가 넘어갈 경우 주기적으로 ssh 포트 변경, root 계정의 패스워드를 변경해 주도록 하자.
{% include codeHeader.html name="CentOS" %}
$ perl -ne 'print "$1\n" if(/Failed password for (\w.+) from/)' /var/log/secure | sort | uniq -c | sort -rn |head -10
2 server01
{% include codeHeader.html name="Ubuntu" %}
$ grep "Failed password" /var/log/auth.log | grep "from" | awk '{print $9}' | sort | uniq -c | sort -rn | head -10
2 server01
▪️ IP별 로긘 실패 시도 건수
{% include codeHeader.html name="CentOS" %}
$ perl -ne 'print "$1\n" if(/Failed password\D+((\d+\.){3}\d+)/)' /var/log/secure | sort | uniq -c | sort -rn |head -10
2 192.168.219.119
{% include codeHeader.html name="CentOS" %}
$ awk '/Failed password/ {match($0, /([0-9]+\.){3}[0-9]+/); print substr($0, RSTART, RLENGTH)}' /var/log/auth.log | sort | uniq -c | sort -rn | head -10
2 192.168.219.119
◾️ 시스템 오류 메시지 확인
dmesg
를 통해 segfault, oom-killer os 레벨의 오류 메시지 확인
$ dmesg | tail -n 3
[14189.405133] audit: type=1400 audit(1709104147.266:91): apparmor="DENIED" operation="ptrace" profile="docker-default" pid=7557 comm="python3" requested_mask="read" denied_mask="read" peer="unconfined"
[14189.405138] audit: type=1400 audit(1709104147.266:92): apparmor="DENIED" operation="ptrace" profile="docker-default" pid=7557 comm="python3" requested_mask="read" denied_mask="read" peer="unconfined"
[14189.405164] audit: type=1400 audit(1709104147.266:93): apparmor="DENIED" operation="ptrace" profile="docker-default" pid=7557 comm="python3" requested_mask="read" denied_mask="read" peer="unconfined"
messages에서 커널과 OS의 표준 프로세스의 로그를 봄.
(Ubuntu의 경우 /var/log/syslog)
$ cat /var/log/syslog | grep -Ei "emerg|alert|crit|error|warn|fail"
secure 로그를 통해 ssh 연결 실패 정보를 보고 횟수가 많은지 파악하여 패스워드 변경 주기를 앞당기는 근거로 활용하기
(Ubuntu의 경우 /var/log/auth.log)
$ cat /var/log/auth.log | tail
◾️ 메모리 확인 (free)
CentOs7에서 메모리 용량을 확인하는 free
명령으로 얻을 수 있는 버퍼 및 캐시 영역에는 스왑 영역도 포함되어 있으며, 단순히 메모리 사용 용량 = 메모리 전체 - free - buff/cache 식으로 계산하면 메모리 사용 용량을 과소 평가하게 된다.
최신의 linux 커널은 이러한 부분을 고려하여 메모리 정보를 표시하고 있다.
$ free -w
total used free shared buffers cache available
Mem: 28735140 400096 26146720 1232 78920 2109404 27922848
Swap: 8388604 0 8388604
$ cat /proc/meminfo | grep -E 'MemTotal|MemFree|Buffers|Cached'
MemTotal: 28735140 kB
MemFree: 26146720 kB
Buffers: 78928 kB
Cached: 1978564 kB
SwapCached: 0 kB
▪️ 메모리 정보 확인 스크립트
{% include codeHeader.html name="memory-usage.sh" %}
# cat memory-usage-free.sh
#!/bin/bash
export LANG=C, LC_ALL=C
free | awk '
BEGIN{
total=0; used=0; available=0; rate=0;
}
/^Mem:/{
total = $2;
available = $7;
}
END {
used = total - available;
rate= 100 * used / total;
printf("total(KB)\tused(KB)\tavailable(KB)\tused-rate(%)\n");
printf("%d \t %d \t %d \t %.1f\n", total, used, available, rate);
}';
$ bash memory-usage.sh
total(KB) used(KB) available(KB) used-rate(%)
28735140 812632 27922508 2.8
◾️ 파일 시스템 확인 (df)
$ df -Th
Filesystem Type Size Used Avail Use% Mounted on
tmpfs tmpfs 2.8G 1.2M 2.8G 1% /run
/dev/mapper/ubuntu--vg-ubuntu--lv ext4 97G 24G 69G 26% /
tmpfs tmpfs 14G 0 14G 0% /dev/shm
tmpfs tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/sda2 ext4 2.0G 251M 1.6G 14% /boot
tmpfs tmpfs 2.8G 4.0K 2.8G 1% /run/user/1000
◾️ 디스크 사용량 확인 (du)
디스크 사용량 순으로 확인하려면 아래와 같음.
$ ionice -c 2 -n 7 nice -n 19 du -scm /* 2>/dev/null | sort -rn
19812 total
8193 /swap.img
4200 /usr
4102 /home
1407 /snap
1331 /var
324 /opt
251 /boot
...
-scm
옵션은 하위 디렉토리 숨기기 + 전체 디스크 사용량 표시 + M 바이트 형식으로 표시이고 -rn
옵션은 사용량이 많은 순서로 + 수치로 비교.
◾️ 네트워크 상태 확인
▪️ 네트워크 상태 확인
netmon.sh
스크립트를 실행하여 TIMEWAIT가 많을 경우 커널 튜닝을 진행하고 CLOSEWAIT 등이 있을 경우 비정상적인 상황을 모니터링 한다.
{% include codeHeader.html name="netmon.sh" %}
#!/bin/bash
COUNT=10
while :
do
if [ $COUNT = 10 ]
then
printf "+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ \n"
printf "| TIME |ESTAB|LISTN|T_WAT|CLOSD|S_SEN|S_REC|C_WAT|F_WT1|F_WT2|CLOSI|L_ACK| \n"
printf "+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ \n"
COUNT=0
fi
COUNT=`expr $COUNT + 1`
TIME=`/bin/date +%H:%M:%S`
printf "|%s" ${TIME}
netstat -an | \
awk 'BEGIN {
CLOSED = 0;
LISTEN = 0;
SYN_SENT = 0;
SYN_RECEIVED = 0;
ESTABLISHED = 0;
CLOSE_WAIT = 0;
FIN_WAIT_1 = 0;
FIN_WAIT_2 = 0;
CLOSING = 0;
LAST_ACK = 0;
TIME_WAIT = 0;
OTHER = 0;
}
$6 ~ /^CLOSED$/ { CLOSED++; }
$6 ~ /^CLOSE_WAIT$/ { CLOSE_WAIT++; }
$6 ~ /^CLOSING$/ { CLOSING++; }
$6 ~ /^ESTABLISHED$/ { ESTABLISHED++; }
$6 ~ /^FIN_WAIT1$/ { FIN_WAIT_1++; }
$6 ~ /^FIN_WAIT2$/ { FIN_WAIT_2++; }
$6 ~ /^LISTEN$/ { LISTEN++; }
$6 ~ /^LAST_ACK$/ { LAST_ACK++; }
$6 ~ /^SYN_SENT$/ { SYN_SENT++; }
$6 ~ /^SYN_RECV$/ { SYN_RECEIVED++; }
$6 ~ /^TIME_WAIT$/ { TIME_WAIT++; }
END {
printf "| %4d| %4d| %4d| %4d| %4d| %4d| %4d| %4d| %4d| %4d| %4d|\n",ESTABLISHED,LISTEN,TIME_WAIT,CLOSED,SYN_SENT,SYN_RECEIVED,CLOSE_WAIT,FIN_WAIT_1,FIN_WAIT_2,CLOSING,LAST_ACK;
}'
sleep 2
done
$ bash netmon.sh
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| TIME |ESTAB|LISTN|T_WAT|CLOSD|S_SEN|S_REC|C_WAT|F_WT1|F_WT2|CLOSI|L_ACK|
+--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|16:46:46| 2| 7| 0| 0| 0| 0| 0| 0| 0| 0| 0|
...
▪️ 네트워크 포트별 커넥션 수 확인
connections_port.sh
를 실행하여 로컬 서버의 LISTEN 포트별로 커넥션수를 모니터링 할 수 있다.
{% include codeHeader.html name="connections_port.sh" %}
#!/bin/bash
grep -v "rem_address" /proc/net/tcp | awk 'function hextodec(str,ret,n,i,k,c){
ret = 0
n = length(str)
for (i = 1; i <= n; i++) {
c = tolower(substr(str, i, 1))
k = index("123456789abcdef", c)
ret = ret * 16 + k
}
return ret
} {x=hextodec(substr($2,index($2,":")-2,2)); for (i=5; i>0; i-=2) x = x"."hextodec(substr($2,i,2))}{print x":"hextodec(substr($2,index($2,":")+1,4))}' | sort | uniq -c | sort -rn
$ bash connections_port.sh
1 192.168.219.137:49536
1 192.168.219.137:22
1 127.0.0.53:53
1 0.0.0.0:8888
1 0.0.0.0:61208
1 0.0.0.0:22
◾️ 부하 상황 체크
▪️ 실시간 OS 전체의 상황 파악 (top)
$ ionice -c 2 -n 7 nice -n 19 top -c
top - 16:49:39 up 4:37, 1 user, load average: 0.00, 0.00, 0.00
Tasks: 120 total, 1 running, 119 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 2.1 sy, 0.0 ni, 97.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 28061.7 total, 25136.9 free, 422.7 used, 2502.1 buff/cache
MiB Swap: 8192.0 total, 8192.0 free, 0.0 used. 27206.4 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 166304 11888 8456 S 0.0 0.0 0:00.90 /sbin/init
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 [kthreadd]
...
-c
를 쓰면 프로세스 목록 창에 표시되는 프로세스 이름이 인자의 정보도 포함된다.
top 화면으로 이동한 다음 1를 입력하면 각 CPU 코어의 활용도를 개별적으로 볼 수 있다.
us(user)
- OS의 유저에서 사용한 CPU 비율. 응용 프로그램(위의 경우 java, httpd 등)에서 처리 과정에 CPU를 사용하고 있다는 의미.
sy(system)
- OS의 커널에서 사용한 CPU 비율. system이 높은 경우 OS의 자원(파일 디스크립터와 포트 등)을 가진 경우이다. 커널 파라미터 튜닝에 의해 부하를 낮출 수 있다. fork 횟수가 많은 등 부하가 높은 시스템 호출을 응용 프로그램이 했을 가능성이 있고 strace를 통해 더 자세하게 조사할 수 있다.
wa(iowait)
- 디스크 I/O에 사용된 CPU 비율. iowait가 높은 경우는 iostat 명령어를 통해 디스크 I/O 상황을 볼 수 있다.
PR | NI | VIRT | RES | SHR | S | %CPU | %MEM | TIME+ |
---|---|---|---|---|---|---|---|---|
우선 순위 | 상대 우선 순위 | 가상 메모리 | 실제 메모리 | 공유 메모리 | 상태 | CPU 사용률 | 메모리 사용률 | 실행 시간 |
- S : Process Status. 다음 상태인지를 보여줌.
- D : 인터럽트 불가
- R : 실행 중
- S : 잠
- T : 정지 중
- Z : 좀비 프로세스
▪ CPU 사용량, 읽기 및 쓰기, I/O 량, 메모리 사용량 (sar)
$ sar -u 3 3
Linux 5.15.0-97-generic (game) 02/28/2024 _x86_64_ (3 CPU)
04:53:13 PM CPU %user %nice %system %iowait %steal %idle
04:53:16 PM all 0.00 0.00 0.00 0.00 0.00 100.00
04:53:19 PM all 0.00 0.00 0.11 0.00 0.00 99.89
04:53:22 PM all 0.00 0.00 0.00 0.00 0.00 100.00
Average: all 0.00 0.00 0.04 0.00 0.00 99.96
%user
- 사용자 영역에서의 CPU 사용률.
%nice
- 우선 순위 변경된 프로세스를 통해 사용자 영역에서 CPU가 사용된 활용도.
%system
- 커널 영역에서의 CPU 사용률.
%iowiat
- 표시되는 경우 CPU가 I/O 작업을 기다리고 있었음을 나타내는데, 시간의 비율로 보여준다.
%idle
- 디스크 I/O 대기에서 CPU가 기다리던 시간의 비율.
▪️ CPU 사용률, 대기 / 차단된 프로세스 정보(vmstat)
$ vmstat 1 10
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 25737156 175628 2386972 0 0 25 34 67 115 0 0 100 0 0
0 0 0 25737156 175628 2387012 0 0 0 0 53 91 0 0 100 0 0
0 0 0 25737156 175628 2387012 0 0 0 0 64 103 0 0 100 0 0
0 0 0 25737156 175628 2387012 0 0 0 0 41 67 0 0 100 0 0
...
r
- CPU에서 실행 및 순서를 기다리고있는 프로세스의 수. r값이 CPU 수보다 많으면 포화 상태.
b
- 차단된 프로세스 수.
si, so
- 스왑과 스왑. 제로가 아닌 값이 있으면 메모리 부족.
us, sy, id, wa, st
- CPU 시간의 분석에서 모든 CPU에 대한 평균 값. 각 사용자 시간, 시스템(커널) 시간, 유휴, 대기 시간, I/O 지연, steal된 시간.
▪️ 프로세스당 상황 (ps)
$ ps -aux --sort=-%cpu | head -n 3
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 742 0.1 0.3 2878196 96196 ? Ssl 12:12 0:21 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root 5062 0.1 0.1 237836 57344 ? Ssl 13:36 0:12 /usr/local/bin/python /usr/local/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000
▪️ 스토리지 성능 정보 (iostat)
$ iostat -dx 5
Linux 5.15.0-97-generic (game) 02/28/2024 _x86_64_ (3 CPU)
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s wrqm/s %wrqm w_await wareq-sz d/s dkB/s drqm/s %drqm d_await dareq-sz f/s f_await aqu-sz %util
dm-0 3.03 70.03 0.00 0.00 0.21 23.15 3.99 100.71 0.00 0.00 0.94 25.26 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.11
sda 2.30 70.57 0.75 24.45 0.15 30.64 1.21 100.72 2.83 70.09 0.63 83.40 0.00 0.00 0.00 0.00 0.00 0.00 0.16 1.45 0.00 0.11
sr0 0.01 0.18 0.00 0.00 0.22 35.26 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
첫번째의 출력값은 디스크 장치가 활성화되고 나서 현재까지의 누적 값이며, 현재의 상황을 아는 경우는 두번째 이후의 출력에서 보여준다. 보통 IOPS[r/s(초당 읽기 섹터 수), w/s(초당 쓰기 섹터 수)]와 %util을 주의깊게 본다.
◾️ 주기적으로 확인해줘야 할 것
- SSH 포트 변경
- 계정 패스워드 변경
- bashhistory 점검
curl
,mysql
등의 커맨드 실행할때 계정 정보를 넣어서 실행하는 것을 감시한다. 아이디와 패스워드가 bashhistory에 남아 있어 보안 문제가 대두됨.- history에서 제거하려면
history | less
를 통해 행 번호를 알아내고history -d 108
를 통해 해당 행을 삭제
curl
의 경우는read -sp "Please input your password: " __pass; echo
curl -u "user:${__pass}" http://fittobe.com
- 를 통해 패스워드 이력을 남기지 않는다. mysql의 경우는
-p
이후에 패스워드를 입력하지 않고 Enter 다음에 패스워드를 입력하면 된다.