Browsing Tag

blog

IT/Linux/Kubernetes

[Linux] Cannot allocate memory, 캐시 메모리 제거 및 swap 생성

안녕하세요. ManVSCloud 김수현입니다.

오늘은 최근 제 블로그가 간헐적 끊김이 있어 확인 후
Memory 부족 원인을 파악하고 해결까지에 대한 내용을 작성해보았습니다.


Symptom & Identify the cause

어제 블로그에 포스팅 후 블로그 페이지가 정상적으로 출력되지 않는 현상이 발생했습니다.
서버 접속 시 Cannot allocate memory 에러가 발생하는 중이었으며 명령어 입출력이 정상적이지않은 상태라 우선 인스턴스 재부팅을 진행하게 되었습니다.

-bash: fork: Cannot allocate memory

대시보드에서 자원 사용률을 살펴보았는데 다른 자원 사용률은 정상인데 유독 Memory만 90% 이상 사용 중!! Memory 문제가 있는 것을 알게되었습니다.

VSZ, RSS 확인 시 Apache, PHP에서만 대량 사용 중이라 웹 로그를 먼저 확인해보았습니다.
1주일 단위로 logrotate가 걸려있어 최근 로그만 파악할 수 있는 상태였는데 우선 공격성으로 대량 접근하는 IP가 존재하는지 확인하였고 의심되는 IP는 WAF에서 추가 검토하였는데 따로 공격성 접근은 아닌 것으로 파악되었습니다.

[root@ip-10-0-1-68 httpd]# cat /var/log/httpd/access_log | awk '{print $1}' | sort -n | uniq -c | grep -Ev "10.0|172.16|192.168|-" | sort -rn | head -n 10
    399 88.150.-.-
    319 121.125.-.-
    200 125.141.-.-
    186 63.141.-.-
     83 66.249.-.-
     75 66.249.-.-
     49 117.111.-.-
     46 52.79.-.-
     37 66.249.-.-
     36 45.146.-.-

sar를 이용하여 추가 검토를 진행하였습니다.
인스턴스 재시작 후에도 메모리 사용량이 80% 이상 계속 유지 중이었습니다.

[root@ip-10-0-1-68 ~]# sar -r
Linux 4.14.193-149.317.amzn2.x86_64 (ip-10-0-1-68.ap-northeast-2.compute.internal) 	05/22/2021 	_x86_64_	(1 CPU)

12:00:02 AM kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
12:10:01 AM     65860    941080     93.46         0    103936   4407680    437.73    785580     28120       120
12:20:01 AM     58924    948016     94.15         0    106820   4414144    438.37    789028     31556       176
12:30:01 AM     66768    940172     93.37         0    103328   4410048    437.97    784968     28244       208
12:40:01 AM     64996    941944     93.55         0    102724   4410256    437.99    787600     26904       232
12:50:01 AM     61056    945884     93.94         0    103356   4414996    438.46    792132     26448        40
01:00:01 AM     68052    938888     93.24         0     96388   4412948    438.25    787572     24756       112
01:10:01 AM     63336    943604     93.71         0    100240   4414108    438.37    789356     27116       112
01:20:02 AM     65780    941160     93.47         0    102172   4408852    437.85    785084     29272       112
01:30:01 AM     61096    945844     93.93         0    108252   4408644    437.83    
.
. [생략]
.
08:26:04 AM       LINUX RESTART
08:36:33 AM       LINUX RESTART
08:40:02 AM kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
08:50:01 AM    192852    814088     80.85      2088    365732   3775100    374.91    401104    280868       296
09:00:01 AM    203284    803656     79.81      2088    378252   3162244    314.04    376764    289192       328
09:10:01 AM    195364    811576     80.60      2088    378620   3171048    314.92    384684    289380       332
09:20:01 AM    194748    812192     80.66      2088    379568   3173428    315.16    383840    290344       328
09:30:01 AM    181256    825684     82.00      2088    379744   3187564    316.56    407336    280344       328
Average:       193501    813439     80.78      2088    376383   3293877    327.12    390746    286026       322

마지막으로 free 명령어로 확인해보니 buff/cache가 비워지지 않고 그대로 남아있는 상태였습니다.

[root@ip-10-0-1-68 ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:            983         448         125          60         408         338
Swap:             0           0           0

우선 아래 명령어를 이용하여 캐시 메모리를 지우고 메모리 확보를 진행 하였습니다.

[root@ip-10-0-1-68 ~]# sync && echo 3 > /proc/sys/vm/drop_caches
[root@ip-10-0-1-68 ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:            983         435         365          60         182         358
Swap:             0           0           0

(sync && echo 3 > /proc/sys/vm/drop_caches 명령어에 대해서는 아래에서 추가적인 설명을 하도록하겠습니다.)

이후 메모리는 정상화 되었으나 추가적인 조치를 하지 않으면 동일한 장애가 다시 발생할 수 있어 우선 swap을 생성하기로 했습니다.


Create Swap

약 2~3G 정도 크기의 볼륨을 생성해줍니다.
swap 볼륨을 추가할 인스턴스와 연결을 시켜줍니다.
[root@ip-10-0-1-68 ~]# fdisk -l
Disk /dev/xvda: 15 GiB, 16106127360 bytes, 31457280 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 66F46060-ED00-4D14-9841-F5CB6310A14A

Device       Start      End  Sectors Size Type
/dev/xvda1    4096 31457246 31453151  15G Linux filesystem
/dev/xvda128  2048     4095     2048   1M BIOS boot

Partition table entries are not in disk order.


Disk /dev/xvdb: 3 GiB, 3221225472 bytes, 6291456 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

연결이 잘됐는지 확인 후 이제 파티션을 아래와 같이 생성해주었습니다.

[root@ip-10-0-1-68 ~]# fdisk /dev/xvdb
Welcome to fdisk (util-linux 2.30.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0xb9e2e385.

Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-6291455, default 2048): 
Last sector, +sectors or +size{K,M,G,T,P} (2048-6291455, default 6291455): 

Created a new partition 1 of type 'Linux' and of size 3 GiB.

Command (m for help): t
Selected partition 1
Hex code (type L to list all codes): 82
Changed type of partition 'Linux' to 'Linux swap / Solaris'.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

[root@ip-10-0-1-68 ~]# fdisk -l | tail -n 9
Disk /dev/xvdb: 3 GiB, 3221225472 bytes, 6291456 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xb9e2e385

Device     Boot Start     End Sectors Size Id Type
/dev/xvdb1       2048 6291455 6289408   3G 82 Linux swap / Solaris

swap 파티션이 잘 생성되었다면 mkswap으로 swap을 만들어주고 blkid에 나온 UUID를 참고하여 /etc/fstab에 추가해줍니다.
이 부분을 잘못 추가하게될 경우 정상 부팅이 되지않아 번거로운 추가 작업이 생길 수 있으니 잘 추가해줍시다.

[root@ip-10-0-1-68 ~]# mkswap /dev/xvdb1
[root@ip-10-0-1-68 ~]# blkid /dev/xvdb1
/dev/xvdb1: UUID="dc082f63-27d8-41b1-ad74-8b019162f766" TYPE="swap" PARTUUID="b9e2e385-01"
[root@ip-10-0-1-68 ~]# vi /etc/fstab

UUID=dc082f63-27d8-41b1-ad74-8b019162f766       swap    swap    defaults        0 0

[root@ip-10-0-1-68 ~]# swapon -a
[root@ip-10-0-1-68 ~]# swapon -s
Filename				Type		Size	Used	Priority
/dev/xvdb1                             	partition	3144700	0	-2

swapon 명령어에 -a 옵션을 주면 /etc/fstab에 있는 swap 파티션을 모두 enable 합니다.
-s 옵션은 사용중인 스왑 장치를 요약하여 볼 수 있습니다.

저는 볼륨을 2개 추가하여 하여 swap 볼륨 하나가 장애가 발생하더라도 나머지 하나로 작동할 수 있도록 해두었습니다.

[root@ip-10-0-1-68 ~]# swapon -s
Filename				Type		Size	Used	Priority
/dev/xvdb1                             	partition	3144700	0	-2
/dev/xvdc1                             	partition	3144700	0	-3

[root@ip-10-0-1-68 ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:            983         461         332          60         188         328
Swap:          6141           0        6141

Swapfile

swap 볼륨을 따로 추가할 수 없을 수도 있습니다.
그때는 swapfile을 만들어서 사용할 수 있는데 간단하게 swapfile을 만드는 방법만 설명하겠습니다.

// dd 명령어를 이용해서 용량 지정 및 swap파일을 만들어줍니다. 
(mkdir로 디렉토리 만들어두는 것 아닙니다.)
[root@manvscloud-test-pub-kr2 ~]# dd if=/dev/zero of=/swapfile bs=1MiB count=4096
4096+0 records in
4096+0 records out
4294967296 bytes (4.3 GB) copied, 12.3267 s, 348 MB/s
//권한 변경
[root@manvscloud-test-pub-kr2 ~]# chmod 600 /swapfile
//스왑 생성
[root@manvscloud-test-pub-kr2 ~]# mkswap /swapfile
Setting up swapspace version 1, size = 4194300 KiB
no label, UUID=4e0d57c0-dd8f-4527-af2d-659ce04921b7

이후 /etc/fstab에 등록해주고 swapon -a 해주는 것은 동일합니다.

/swap01                          swap                swap    defaults        0 0

sync && echo 3 > /proc/sys/vm/drop_caches

Linux에서 아래 명령어를 이용하여 캐시를 비울 수 있습니다.

  • PageCache만 비우기
    sync && echo 1 > /proc/sys/vm/drop_caches
    (= sync; echo 1 > /proc/sys/vm/drop_caches)
  • dentries와 inodes 비우기
    sync && echo 2 > /proc/sys/vm/drop_caches
    (= sync; echo 2 > /proc/sys/vm/drop_caches)
  • PageCache, dentries, inodes 모두 비우기
    sync && echo 3 > /proc/sys/vm/drop_caches
    (= sync; echo 3 > /proc/sys/vm/drop_caches)

sync를 통해 파일 시스템 버퍼를 플러시합니다.
(개발쪽은 잘 모르지만 JAVA나 Python에서 flush() 함수가 버퍼 비우는 용도로 사용되는걸 본 적이 있습니다)

단 echo 3을 사용한다면 모두 비우기가 가능하지만 디스크 I/O가 활발한 시스템에서 사용하게 된다면 CPU 자원 사용량이 일시적으로 상승합니다.

  • pagecache : 리눅스 커널에 의해 사용되는 기본 디스크 캐시.
    대부분의 경우 커널은 디스크에서 읽거나 디스크에 쓸 때 페이지 캐시를 참조합니다.
    I/O 속도와 퍼포먼스를 높이기 위해 시스템이 할당한 임시 메모리 저장소로 임시 메모리에 저장된 파일을 다시 읽을 때 해당 메모리에서 바로 불러오기에 디스크의 읽기,쓰기 속도가 빨라집니다. (Windows에서는 동일한 역할로 페이지 파일이 있습니다.)
  • dentries : dcache, 즉 디렉토리 항목 캐시입니다. 경로/파일명을 특정 dentry로 변환하여 빠른 조회를 제공하며 디스크가 아닌 RAM에 저장됩니다. (inode를 검색하여 수행됩니다.)
  • inodes : 파일과 디렉토리에 관한 정보를 담고 있는 자료 구조
    (물리적 위치, 크기, 생성 일시, 권한)
[root@ip-10-0-1-68 ~]# vmstat -m | head -n 1; vmstat -m | grep proc_inode_cache
Cache                       Num  Total   Size  Pages
proc_inode_cache          13570  13570    688     23

⬆ (위 명령어로 inode 캐시값 확인이 가능합니다.)

※ TIP-1 (Slab) 😎

서비스 프로세스 할당량을 계산해보아도 전체 메모리에 비해 유휴 메모리가 없을 때가 있을 것입니다. 이는 커널에서 메모리를 사용하고 있기때문인데 커널에서 사용하고 있는 메모리는 slab 메모리 영역을 확인하면 얼마나 사용하고 있는지 알 수 있습니다.

[root@ip-10-0-1-68 ~]# cat /proc/meminfo | grep Slab
Slab:              48832 kB

Slab에 대한 내용이 깊이있게 설명되어 있는 곳은 링크로 남겨두었으니 참고바랍니다.

  • Slab : 메모리 영역 중 커널이 직접 사용하는 영역을 Slab영역이며 dentries와 inodes 캐시가 이 영역에 해당됩니다.
    (slabtop 명령어를 이용하여 현재 시스템의 slab 자료 구조 상태를 확인할 수 있습니다.)
[root@ip-10-0-1-68 ~]# slabtop
 Active / Total Objects (% used)    : 232162 / 241799 (96.0%)
 Active / Total Slabs (% used)      : 7662 / 7662 (100.0%)
 Active / Total Caches (% used)     : 74 / 101 (73.3%)
 Active / Total Size (% used)       : 55501.21K / 58750.71K (94.5%)
 Minimum / Average / Maximum Object : 0.01K / 0.24K / 9.50K

  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME                   
 39580  39403  99%    0.20K   1979	 20	 7916K vm_area_struct
 38080  38080 100%    0.06K    595	 64	 2380K anon_vma_chain
 30072  30072 100%    0.19K   1432	 21	 5728K dentry
 19136  19089  99%    0.09K    416	 46	 1664K anon_vma
 15504  14550  93%    0.04K    152	102	  608K Acpi-Namespace
 15210  15210 100%    0.13K    507	 30	 2028K kernfs_node_cache
 13570  13570 100%    0.67K    590	 23	 9440K proc_inode_cache
 10868  10476  96%    0.60K    418	 26	 6688K inode_cache
  7808   6525  83%    0.03K     61	128	  244K kmalloc-32
  5746   4799  83%    0.94K    169	 34	 5408K xfs_inode
  5248   3838  73%    0.06K     82	 64	  328K kmalloc-64
  5180   3660  70%    0.57K    185	 28	 2960K radix_tree_node
  3822   2783  72%    0.09K     91	 42	  364K kmalloc-96
  3264   2517  77%    0.25K    102	 32	  816K kmalloc-256
  3248   3248 100%    0.07K     58	 56	  232K Acpi-Operand
  3072   3072 100%    0.02K     12	256        48K kmalloc-16
  3060   3060 100%    0.05K     36	 85	  144K ftrace_event_field
  2560   2560 100%    0.01K	 5	512        20K kmalloc-8
  2037   1762  86%    0.19K     97	 21	  388K kmalloc-192
  1610   1610 100%    0.09K     35	 46	  140K trace_event_file
  1536   1258  81%    0.50K     48	 32	  768K kmalloc-512
  1344   1046  77%    0.12K     42	 32	  168K kmalloc-128

※ TIP-2 (vfs_cache_pressure) 😎

vfs_cache_pressure는 리눅스 커널의 vm구조와 관련된 파라미터입니다.
vfs_cache_pressure를 이용해서 커널에게 디렉토리와 inode Object에 대한 캐시로 사용된 메모리를 반환하도록하는데 이 값이 0일 경우 커널이 오브젝트에 대한 캐시를 반환하지 않아 Out Of Memory가 발생합니다.
보통 이 값이 100으로 설정되어 있으나 자주 캐시를 반환하기 위해서는 이 값을 변경해주어야하며 값이 높아질수록 메모리에서 캐시를 보관하지 않게되어 dentries와 inodes 캐시를 줄일 수 있습니다.

[root@ip-10-0-1-68 ~]# cat /proc/sys/vm/vfs_cache_pressure
100

기본값은 100으로 되어있습니다.
/proc/sys/vm/vfs_cache_pressure 에 값을 지정해주는 것은 일회성이니 sysctl.conf에 설정해줍니다.

[root@ip-10-0-1-68 ~]# vi /etc/sysctl.conf 이후 아래값을 추가 및 저장, load 해줍니다
vm.vfs_cache_pressure = 10000
[root@ip-10-0-1-68 ~]# sysctl -p

Personal Comments and Wrap-up

아직 캐시 메모리를 비우고 swap 생성만 해두었습니다.
이걸로 완벽하게 해결됐다고 보기는 어렵습니다. 이후 메모리 사용량 경보를 추가하여 알림 설정을 하고 현재 오토스케일링 추가를 생각하고 있습니다.
(우선 오토스케일링 추가 전 서버 내 자원 최적화 작업을 먼저 진행해야겠습니다.)

그리고 이번 메모리 최적화에 도움주신 리눅서님에게 감사의 인사올립니다.
(아… 난 왜 swap을 잊고있었지? 매번 감사합니다…🙇🏻‍♂️ 덕분에 이번 기회로 메모리에 대해 많이 공부한 것같습니다.)

Notice/News

[NOTICE] 블로그 성능 개선 작업 완료 (2021-05-17)

안녕하세요. ManVSCloud 김수현입니다.

2021년 5월 17일 예정되어 있던 블로그 성능 개선 작업이 정상적으로 완료되었습니다.

※ 작업 내용
→ DNS 작업 (작업 시간동안 임시 Parking 페이지로 출력될 예정입니다.)
→ 데이터 백업(Snapshot)
→ 소프트웨어 버전 업그레이드
→ EC2, RDS 인스턴스 재시작
→ DNS 원복

※ 작업 시간
→ 예정 시간 : 2021-05-17 월요일, 00:00 ~ 06:00(KST)
→ 소요 시간 : 2021-05-17 월요일, 00:00 ~ 02:40(KST)


세부 작업 내용

http://parking.manvscloud.com

http://manvscloud.com 또는 https://manvscloud.com 접속 시 http://parking.manvscloud.com 사이트로 리다이렉트 되도록 설정한 뒤 작업하였습니다.

parking 페이지의 경우 cdn을 이용하려고 했는데 이번 작업 준비 기간이 다소 부족하여
제 테스트 서버를 사용하게 되었습니다.

  • 작업 전 스냅샷 (naming-20210517)
  • PHP 버전 업그레이드 (PHP 7.3.23 → 7.4.15)
<작업 전>
[root@ip-10-0-1-68 ~]# php -v
PHP 7.3.23 (cli) (built: Oct 21 2020 18:39:40) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.23, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.23, Copyright (c) 1999-2018, by Zend Technologies

<작업 후>
[root@ip-10-0-1-68 mcrypt-1.0.2]# php -v
PHP 7.4.15 (cli) (built: Feb 11 2021 17:53:39) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.15, Copyright (c), by Zend Technologies
  • PHP 모듈 추가 설치

PHP 7.4 버전으로 설치 후 mcrypt 모듈이 정상적으로 설치되지 않았습니다.
(가끔 버전업 시 해당 버전에서 특정 모듈 지원 중단으로 설치가 안될 때가 있습니다.)
mcrypt 모듈이 설치가 되지않아 수동으로 설치 해주었습니다.

[root@ip-10-0-1-68 ~]# yum install libmcrypt-devel
[root@ip-10-0-1-68 ~]# cd /usr/local/src
[root@ip-10-0-1-68 ~]# wget https://pecl.php.net/get/mcrypt-1.0.2.tgz
[root@ip-10-0-1-68 ~]# tar xvfz mcrypt-1.0.2.tgz
[root@ip-10-0-1-68 ~]# cd mcrypt-1.0.2
[root@ip-10-0-1-68 ~]# phpize
[root@ip-10-0-1-68 ~]# ./configure
[root@ip-10-0-1-68 ~]# make
[root@ip-10-0-1-68 ~]# make install
[root@ip-10-0-1-68 ~]# echo extension=mcrypt >> /etc/php.ini
  • RDS 버전 업그레이드 및 파라미터 그룹 설정

저는 주로 파라미터 그룹 설정을 아래와 같이 사용합니다.
상황에따라 max_allowed_packet, max_heap_table_size, tmp_table_size 등의 값을 조정해주어야겠습니다.

character_set_client utf8mb4
character_set_connection utf8mb4
character_set_database utf8mb4
character_set_filesystem utf8mb4
character_set_results utf8mb4
character_set_server utf8mb4
collation_connection utf8mb4_general_ci
(collation_server은 utf8mb4_general_ci 를 선택할 수 없어서 그냥 빼버렸습니다.)
time_zone asia/seoul
connect_timeout 3600
skip_name_resolve 1

  • WordPress 최신 버전 업데이트 (5.5.2 → 5.7.2)
  • EC2, RDS 인스턴스 재부팅
  • DNS 원복

마무리

오래 미루어왔던 블로그 품질 향상 및 안정화를 위한 작업이 완료되었습니다.
늘 워드프레스 관리자 사이트에서 Site Health에 노란불이 들어왔었는데 작업 후 확실히 깔끔해진 기분입니다.

개인적으로 작업 시간은 (작업에 필요한 시간 + 장애 시 복구 시간)을 다 합쳐서 정하는 편입니다. 이번 작업은 야간 당직 중 남는 시간 틈틈이 작업을 진행하려 했기에 6시간을 예상했었는데 다행히 당직 간에 큰 일이 없어서 빠르게 작업을 끝낼 수 있었던 것같습니다.
아마 중간중간에 일이 없었다면 1시간 내로 작업이 끝났을 것같군요.
작업이 잘 마무리되어 다행입니다.

다음 포스팅은 [NCP] 네이버 클라우드에서의 보안 – Account 으로 찾아뵙겠습니다.

긴 글 읽어주셔서 감사합니다.

Notice/News

[Notice] 블로그 성능 개선 작업 안내 (2021-05-17)

안녕하세요. ManVSCloud 김수현입니다.

최근 블로그 방문자 수가 늘어 사이트 작업 시 공지 후 작업을 진행하기로 하였습니다.

2021년 5월 17일 새벽 간에 안정적인 서비스를 위한 소프트웨어 버전 업그레이드 작업이 예정되어 있습니다.
작업 내용과 일정은 아래에 추가해두었으며 해당 시간동안 블로그 페이지가 정상적으로 출력되지 않을 수 있으니 참고하시기 바랍니다.

※ 작업 내용
→ DNS 작업 (작업 시간동안 임시 Parking 페이지로 출력될 예정입니다.)
→ 데이터 백업(Snapshot)
→ 소프트웨어 버전 업그레이드
→ EC2, RDS 인스턴스 재시작
→ DNS 원복

※ 작업 시간
→ 2021-05-17 월요일, 00:00 ~ 06:00(KST)

블로그의 품질 향상과 안정화를 위한 작업으로 작업 이후 Site Health 개선과 재부팅 작업으로 인한 캐시 메모리 확보로 사이트가 조금이나마 쾌적한 환경으로 운영될 것으로 보입니다.

감사합니다.

AWS

[AWS] Configuration of ManVSCloud Blog (with AWS WAF)

안녕하세요. ManVSCloud 김수현입니다.

최근 AWS Network Online Study 2기에서 AWS의 WAF에 대한 스터디가 진행되었습니다.
스터디 후 제 블로그에도 AWS WAF 기능을 도입하기로 결정하였습니다.

그리하여 오늘은 WAF 설정을 어떻게 했는지와 WAF 기능이 도입된 현재의 제 블로그가 어떻게 구성되어 있는지에 대해 포스팅 해보려합니다.


AWS WAF

사진 출처 : Anlab

우선 WAF란 Web Application Firewall, 웹 어플리케이션에 특화된 방화벽이라 볼 수 있습니다.
2019년 Anlab 자료에 따르면 웹/어플리케이션의 취약점을 이용한 공격이 상당히 많은 비중을 차지하고 있으며 다양한 방법의 공격이 존재합니다.

제 블로그도 AWS의 Security Group에서 80,443 포트는 0.0.0.0/0으로 OPEN 되어 있었기에
어떻게 웹 공격을 최소화 할 수 있을지 많은 고민을 하였습니다.

특히 해외 지역에서 공격성 접근이 많이 존재한다는 것을 알고 있어 해외쪽을 우선 차단하려 했습니다.
EC2 내에서 Apache의 Geoip를 이용하거나 iptables Geoip 등 다른 방법도 존재했지만
해당 인스턴스까지 접근하는 것자체가 조금 찝찝한 부분이 있어 WAF를 도입하기 전까지
제 블로그는 Route53의 지역 기반 라우팅을 이용하여 국내에서만 접근이 가능하도록 설정해둔 상태였었습니다.

현재는 WAF를 설정하였고 Route53의 지역 기반 라우팅을 이용하여 중국만 차단해두고 나머지 나라에서도 접근이 가능하도록 설정해둔 상태입니다.

제 블로그에서는 AWS WAF를 아래와 같이 설정하였습니다.

Associated AWS resouces : Application Load Balancer
Associated AWS resouces : ManVSCloud-Web-ALB-01 (Application Load Balancer)

Rule은 최대 100개까지 생성이 가능하고 Managed Rule Groups과 Own Rule and Rule Groups을 이용하여 추가가 가능합니다.
managed rule groups에는 다양한 보안 업체의 rule groups이 존재했는데 제 WAF에는 “AWS managed rule groups”을 적용했습니다.

AWS managed rule groups

AWS managed rule groups을 펼쳐보니… 아주 많이 있네요…
저는 Anonymous IP list, Core rule set, WordPress application 3개를 추가해둔 상태입니다.
(참고로 선택하실 때에 Capacity가 1500이 넘지 않도록 선택해주세요.)
(WCUs가 1500이 넘어야할 경우 Amazon에 문의하셔야합니다.)

  • Admin protection
    노출된 관리 페이지에 대한 외부 액세스를 차단할 수있는 규칙이 포함되어 있습니다.
    이는 타사 소프트웨어를 실행 중이거나 악의적인 행위자가 애플리케이션에 대한 관리 액세스 권한을 얻을 위험을 줄이려는 경우 유용 할 수 있습니다.
  • Amazon IP reputation list
    이 그룹에는 Amazon 위협 인텔리전스를 기반으로하는 규칙이 포함됩니다. 이는 봇 또는 기타 위협과 관련된 소스를 차단하려는 경우 유용합니다.
  • Anonymous IP list
    이 그룹에는 뷰어 ID의 난독화를 허용하는 서비스의 요청을 차단할 수있는 규칙이 포함되어 있습니다. 여기에는 VPN, 프록시, Tor 노드 및 호스팅 공급자에서 시작된 요청이 포함될 수 있습니다. 이는 애플리케이션에서 신원을 숨기려고 할 수있는 뷰어를 필터링하려는 경우에 유용합니다.
  • Core rule set
    일반적으로 웹 응용 프로그램에 적용되는 규칙을 포함합니다. 이를 통해 OWASP 출판물에 설명된 것과 같은 광범위한 취약성에 대한 공격으로부터 보호할 수 있습니다.
  • Known bad inputs
    잘못된 것으로 알려져 있고 취약성의 악용 또는 발견과 관련된 요청 패턴을 차단할 수있는 규칙을 포함합니다. 이를 통해 악의적 인 공격자가 취약한 애플리케이션을 발견할 위험을 줄일 수 있습니다.
  • Linux operating system
    LFI 공격을 포함하여 Linux에 특정한 취약성 악용과 관련된 요청 패턴을 차단하는 규칙을 포함합니다. 이렇게하면 파일 콘텐츠를 노출하거나 공격자가 액세스 할 수없는 코드를 실행하는 공격을 방지 할 수 있습니다.
  • PHP application
    안전하지 않은 PHP 함수 삽입을 포함하여 PHP 사용과 관련된 취약점 악용과 관련된 요청 패턴을 차단하는 규칙을 포함합니다. 이를 사용하면 공격자가 코드나 명령을 원격으로 실행할 수있는 악용을 방지 할 수 있습니다.
  • POSIX operating system
    LFI 공격을 포함하여 POSIX / POSIX-like OS에 특정한 취약성을 악용하는 것과 관련된 요청 패턴을 차단하는 규칙을 포함합니다. 이렇게하면 파일 콘텐츠를 노출하거나 액세스가 허용되지 않아야하는 코드를 실행하는 공격을 방지 할 수 있습니다.
  • SQL database
    SQL injection 공격과 같은 SQL 데이터베이스 악용과 관련된 요청 패턴을 차단할 수있는 규칙을 포함합니다. 이를 사용하면 권한 없는 쿼리의 원격 주입을 방지 할 수 있습니다.
  • Windows operating system
    Windows와 관련된 취약점 (예 : PowerShell 명령) 악용과 관련된 요청 패턴을 차단하는 규칙을 포함합니다. 이를 통해 공격자가 무단 명령을 실행하거나 악성 코드를 실행할 수있는 익스플로잇을 방지 할 수 있습니다.
  • WordPress application
    WordPress 애플리케이션 그룹에는 WordPress 사이트와 관련된 취약성 악용과 관련된 요청 패턴을 차단하는 규칙이 포함되어 있습니다.

우선 WAF를 적용하고 Anonymous IP list를 제외한 나머지 Action을 Count로 적용해두었습니다. Count 모드로 탐지하여 운영상 문제가 있을 부분이 잡히는지 확인 후에 Block되도록 변경 예정입니다.

그러고보니 Anonymous IP list에 BLOCK된 로그가 있었는데 사진이 어디론가 사라지고 말았습니다. 따로 추가적인 모니터링 구성을 해주지 않으면 샘플 로그가 3시간까지밖에 남지않아 아쉽네요… 그래도 AWS WAF는 참 마음에 드는 기능인 듯합니다. (비용이 괜찮거든요…)


ManVSCloud

자, 그럼 이제 ManVSCloud 블로그가 어떻게 만들어져있고 어떻게 구성되어 있는지 알아보겠습니다.

아시다시피 제 블로그는 Amazon Web Services (AWS)를 이용하여 만들어졌습니다.
Apache + PHP + MariaDB와 WordPress로 웹 서비스를 운영 중입니다.

OS : Amazon Linux 2 x86_64
Apache : 2.4.46
PHP : 7.3.23
MariaDB : 10.4.13
(DB는 RDS 이용중입니다.)

아래 그림을 참고하여 조금 더 자세히 알아보도록 하겠습니다.

ManVSCloud Blog Architecture
(우클릭 후 ‘새 탭에서 이미지 열기’를 이용하면 이미지를 크게 볼 수 있습니다.)

아키텍처를 직접 그려보는 일이 없어 잘 그린 게 맞나싶네요…
일단 WAF를 제외하고 프리 티어를 최대한 활용하려 노력했습니다.
(물론 가용성은 조금 떨어집니다… )

Route53에 manvscloud.com 도메인을 등록하여 GEO Restriction을 이용해 중국에서는 제 블로그로 접속을 할 수 없지만 중국을 제외한 나머지 지역에서는 A레코드 별칭으로 저의 Application Load Balancer로 향하게 됩니다.

ALB 상단에서 WAF가 등록된 Rule에 따라 ALLOW/BLOCK 합니다.
허용된 접속자만 저의 블로그로 접속할 수 있게됩니다.
(참고로 ALB 쪽에서 ACM을 추가하여 SSL 인증서가 적용되도록 해두었습니다.)

ALB-보안그룹

무조건 ALB를 통해서만 접속이 가능하며 EC2 인스턴스로 80/443 접속이 불가능합니다.
현재 제 EC2 인스턴스에는 IAM 역할 하나와 2개의 보안그룹이 연결되어 있습니다.

보안그룹은 접속용 Admin 보안그룹(ssh, ftp 등)과 web 서버용 보안그룹(http,https)으로
나누어두었습니다.

인스턴스의 80,443는 Application Load Balancer만 연결되어있어 ALB를 제외한 다른 곳에서는 직접적인 접근이 접근이 불가능합니다.

그리고 연결되어 있는 IAM은 아래와 같이 되어있습니다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "cloudwatch:PutMetricData",
                "ec2:DescribeTags",
                "cloudwatch:GetMetricStatistics",
                "cloudwatch:ListMetrics",
                "s3:*"
            ],
            "Resource": "*"
        }
    ]
}

CloudWatch가 EC2로 부터 Metric을 수집하여 대시보드로 추가적인 Report를 확인하였으며
경보를 추가하여 SNS에 등록된 구독자에게 E-mail로 알림을 보낼 수 있도록 설정 해둔 상태입니다.
제가 E-mail이 아닌 텔레그램을 이용하여 모바일로 알림을 받도록 설정하려 했는데 일정이 너무 빠듯하여 계속 미뤄지고 있네요…

S3도 추가해두었는데 이는 현재 진행중인 작업으로 mysqldump 와 특정 log 파일들을 S3로 백업하여 아카이브로 저장할 예정입니다.

마지막으로 블로그에 업로드되는 이미지들은 S3에 저장되고 해당 정적 이미지들은 Cloudfront를 이용하여 배포됩니다.

개발자 모드에서 확인해보시면 cdn.manvscloud.com 도메인으로 따로 이미지를 불러오고 있는 것을 알 수 있습니다.
ex) https://cdn.manvscloud.com/wp-content/uploads/2021/03/06075013/ncp-manvscloud-1024×562.png

최근 cloudfront에 Origin Shield를 적용하고 Hit가 상당히 많아졌습니다.
캐시를 이용하여 웹이 조금 더 빨라져 상당히 기분이 좋습니다.

현재 캐시 적중률 늘리기에 대해서도 포스팅 중인데 먼저 해결하려 했던
“Cloudfront + Lambda@Edge를 활용한 Image Resizing” 포스팅이 아직 잘 해결되지 않고 있어 늦춰지고 있습니다.

빠른 시일내에 포스팅 할 수 있도록 해보겠습니다.


Thanks to everyone

이번에 제 블로그 아키텍처를 그리며 참 많은 생각이 들었습니다.
AWS를 시작한지 1년이라는 시간이 지났습니다.

클라우드에 대해 궁금했고 AWS를 배워보고자 한 카카오톡 오픈채팅방에 들리게 되었습니다. 클라우드를 시작하기에 조금의 기반도 갖춰지지 않은 상태에서
“아마존 웹 서비스 AWS Discovery Book” 책을 하나 구매한 채 하나씩 따라해보았고
어느 날 “AWSKRUG – AWS 한국사용자모임 – 강남 비기너”에서 AWS 네트워크에 관련된 내용으로 스터디가 있어 참가하게 되었습니다.

당시 Master Seo님께서 많은 것을 알려주셨습니다.
많은 사람들 앞에서 그렇게 알려주시는 모습이 진짜 너무 멋있었습니다.
아마 그때부터 정말 열심히 해보자는 마음이 들게 되었던 것같습니다.

물론 당시 제 노트북의 OS가 한창 리눅스를 연습하고 싶어 리눅스를 설치해둔 상태라
ssh 접속하는 방법도 모르던 때다보니 제대로 따라가지 못한 것이 생각나네요.

이후 Master Seo님이 운영하시는 Brunch 블로그 URL을 외워가서 몇 번이고 다시 해보았었습니다. 정말 제가 클라우드를 시작할 때 많은 영향을 주신 분입니다.

그리고 또 한 번의 기회가 생깁니다.

제 스승님… 리눅서님이 우리 사당스터디를 모집하셨습니다.
이 스터디에서 참 많은 걸 배우게 됐습니다. 정말 많이요.
스터디원 모두에게 늘 감사하는 마음을 갖고있습니다.

여기서 첫 클라우드 자격증 SAA를 얻게되었으며 이후 참 많은 자격증을 따며 공부하였습니다. NCA, AZ-300, AZ-301, CLF, DBS…

블로그 아키텍처를 그려나가는데 문득 내가 언제 이런걸 할 수 있게 된거지?…라고 생각이 들며 조금 가슴이 뭉클해졌습니다.
1년이라는 시간동안 저를 어디까지 성장을 시켜주신 건지… 😦

그리고 제가 요즘 많이 배우고 있는 AWS Network Online Study!!
제 매주 일요일의 행복입니다.

특히 가시다님의 8주차 WAF 강의는 정말 재밌으면서도 많은 도움되었던 강의 중에 하나입니다! 덕분에 블로그에 WAF를 추가할 수 있게 되었습니다.
벌써 8주차가 끝나버렸습니다.
11주차가 끝나버리면 이젠 매주 일요일 뭐할지 걱정되네요.

이상으로 제가 성장할 수 있도록 가르침을 주신 모든 분들께 감사의 인사올립니다.

긴 글 읽어주셔서 감사합니다.