문제 상황
AWS 프리티어를 사용하기 위해 t2.micro 인스턴스를 사용해서 배포를 하고 있었다. 빠른 배포와 편의성을 위해 도커로 mysql과 redis, spring boot 컨테이너 2개를 띄우는데 자꾸 서버가 죽고 자꾸 연결성 실패가 떠서 서버에 접속할 수도 없었다.
과제
인스턴스를 재시작하면 연결성을 통과되는데 컨테이너가 자꾸 죽는게 문제였다.
이전 프로젝트에서도 매번 서버가 죽는 경우가 다분했는데, 이럴 때마다 그냥 인스턴스 재시작후 컨테이너를 다시 띄어주었다.
이번에는 컨테이너를 띄울 때마다 서버가 죽는 근본적인 원인을 알아보고, 이를 해결해보기로 했다.
문제 해결 과정
원인 파악을 위해 아래 과정을 거쳤다.
- EC2 시스템 로그 확인하기
- 시스템 로그에서 원인 분석하기
- 원인 분석 후 해결하기
EC2 시스템 로그 확인
문제의 인스턴스를 선택해서 시스템 로그를 확인했다.
로그 중 일부에 아래 내용이 보였다.
[89493.949747] oom-kill: ...
Out of memory: Killed process...
에러 다음에 컨테이너 관련 프로세스가 다 죽은걸 확인하였다.
컨테이너가 죽는거와 서버 연결성 검사 실패랑 무슨 관련이 있는가?
컨테이너를 띄우면서 메모리 부족이 발생했고, 컨테이너가 죽으면서 도커 가상 브리지 네트워크 인터페이스(docker0) 아래의 가상 이더넷 인터페이스가 비활성화 상태로 전환된다.
이는 컨테이너 간 통신 또는 EC2 외부 연결까지도 영향을 준다고 한다.
필자 상황을 정리해서 설명하면
- EC2 인스턴스에서 Docker 컨테이너가 메모리를 너무 많이 사용
- 시스템 전체 메모리 부족 -> OOM killer 발동
- 중요한 프로세스(Docker 네트워크 일부)가 강제 종료되면서 네트워크 인터페이스까지 영향
- EC2 네트워크 스택이 일시적으로 꼬임
- 도커 네트워크에 붙은 SSH 세션이나 네트워크 경로도 영향을 받음
- EC2 연설 상태 감지(status check) 실패
메모리 부족을 해결하기 위한 방안: Swapping
프리티어라서 메모리를 추가할 수 없기도 하지만 한정된 자원에서 문제를 해결하면서 이론으로만 배우던 OS개념을 실제로 적용하며 해결해 볼 수 있는 기회였다.
로그 분석 결과 메모리가 부족해서 외부 네트워크를 구성하는 컨테이너가 죽고, 네트워크가 꼬여 연결성 실패가 뜬거라고 한다.
물리적으로 메모리 추가하면 해결되는데, 무료 서버를 사용하기 위해서 그럴 수 없다. 제한된 메모리에서 해결해야한다.
사용중인 서버 스펙은 다음과 같다.
RAM이 1GB밖에 안된다. → swap 설정 해서 사용하지 않는 프로세스는 디스크로 내보내야 한다.
💡 swap이란?
메모리 관리와 과련된 개념으로, *degree of multiprogramming이 메인 메모리 공간에 비해 너무 높은 경우(프로세스가 너무 많이 메모리에 로드된 경우) 일부 프로세스를 임시로 디스크에 내보냈다가 메모리에 여유가 생기면 다시 불러오는 걸 swapping이라고 한다.
스와핑하는 시간(swap out, swap in은 대부분 디스크를 읽고 쓰는 transfer 시간이 대부분을 차지한다.
*degree of muliprogramming: 멀티프로그래밍에서 메모리에 적재되어 있는 프로그램 개수
서치를 해보니 프리티어를 사용중인 사람들은 적은 메모리로 인해 이런 문제를 겪는 다는 것을 알게 되었다. AWS 공식 홈페이지에서도 swap 하는 방법에 대한 QnA가 있다. (https://repost.aws/ko/knowledge-center/ec2-memory-swap-file)
OS는 AL2023을 사용중이라 Linux에서 스왑하는 법을 살펴보았다.
AL2023은 리눅스 기반 아마존에서 배포한 OS이다.
스와핑이 필요한 이유 2가지
- 시스템이 물리적으로 사용 가능한 메모리보다 더 많은 메모리를 필요로 할 때 , 커널은 사용 빈도가 낮은 페이지를 스왑 아웃하고 해당 메모리를 즉시 필요로 하는 현재 애플리케이션(프로세스)에 메모리를 할당→ 내 상황
- 애플리케이션이 시작 단계에서 사용하는 페이지 중 상당수는 초기화에만 사용되고 다시는 사용되지 않을 때. 시스템은 이러한 페이지를 스왑 아웃하여 다른 애플리케이션이나 디스크 캐시를 위해 메모리를 확보
스와핑의 단점
- 디스크 IO발생으로 느려짐: 아무래도 페이지를 하드 디스크에 접근하는 시간이 포함되므로 상당 시간이 소요된다. 스와핑이 많이 발생할수록 디스크에 접근하는 빈도가 많아지고, 이와 비례하여 시스템 속도가 느려진다.
- 스레싱(Thrasing) 발생: 페이지 폴트가 많이 발생하면 일어나는건데, 이유가 디스크에 접근하는게 많아지면서 CPU 이용률이 떨어지고 os는 cpu가 한가하다고 생각하여 더 많은 프로세스를 메모리에 올리려고 하여 결국 cpu 이용률이 떨어지는 악순환이 발생한다.
- 과도한 스와핑이나 스래싱이 발생하여 페이지가 swap out 되었다가 곧바로 swap in되었다가 (반복) … 이런 상황에서 시스템은 여유 메모리를 확보하고 동시에 애플리케이션을 실행하는 데 어려움을 겪는다.
Linux에서 두가지의 스왑 공간
- 스왑 파티션: 스왑 용도로만 사용되는 하드디스크의 독립적인 영역, 다른 파일을 포함하지 않는다.
- 스왑 파일: 시스템 파일과 데이터 파일 사이에 있는 파일 시스템의 스왑용도로 사용되는 특수한 파일
두가지 방법 중 한가지를 택해야한다. 필자는 "스왑 파일" 방식을 선택했다.
AWS 게시글에는 아래와 같은 권장 사항이 있다.
참고:모범 사례는 임시 스토리지 인스턴스 스토어 볼륨에만 스왑 공간을 생성하는 것입니다.
https://repost.aws/ko/knowledge-center/ec2-memory-swap-file
스왑 파일을 사용하여 Amazon EC2 인스턴스에서 메모리를 스왑 스페이스로 할당합니다.
Amazon Elastic Compute Cloud(Amazon EC2) 인스턴스에서 스왑 파일로 사용할 메모리를 할당하고 싶습니다. 어떻게 해야 하나요?
repost.aws
스왑을 만들되 "EBS 같은 영구디스크에 만들지 말고", 인스턴스 스토어(임시 디스크)에 만들라는 의미이다. 즉, 파티션 말고 스왑 파일을 서버 내부에 생성하라는 것이다. 그리고 EBS 같은 독립 하드 디스크를 사용하면 물리적인 파티션으로 설정되어야한다. 이는 곧 I/O 비용 증가, 성능 저하, 요금 증가 같은 문제가 생길 수 있으므로 파일 시스템을 사용하는 스와 파일을 사용하기로 했다.
다만, 스왑 파일은 디스크에서 연속된 공간을 할당하는 스와 파티션과 다르게 파일 시스템 내의 파일 형태로 저장디므로 디스크에서 연속된 공간을 할당받지 못한다면 성능은 스와 파티션보다 떨어질 수도 있다.
Swap 적용하기
1. 스왑 공간 크기 지정하기
먼저, 스왑 파일로 사용할 공간을 정해야한다.
스왑 공간을 물리적 RAM(최대 2GB의 물리적 RAM까지)의 2배에 해당하는 크기로 설정하는 것이 가장 좋다고 한다. 필자의 서버의 메모리는 1GB이므로 2배인 2GB로 설정하면 된다.
용량이 2GB를 초과하는 경우 0.5배의 물리적 RAM을 추가하십시오. 또한 스왑 공간이 32MB 미만으로 떨어지지 않도록 하는 것이 좋습니다. 스왑 공간 크기를 계산하려면 다음 표를 참조하십시오.
https://repost.aws/ko/knowledge-center/ec2-memory-partition-hard-drive
시스템 RAM 용량 권장 스왑 공간 2GiB 이하 RAM 용량의 2배이지만 32MB보다 작으면 안 됨 2GiB 초과 64GiB 미만 RAM 용량의 0.5배 64GiB 이상 워크로드 또는 사용 사례에 따라 다름
2. 스왑 공간 확인하기
`swapon -s` 명령어로 서버에 스왑 공간에 존재하는 지 확인한다.
*swapon: 스왑으로 사용하는 파일의 경로 및 이름, 타입, 크기, 사용 중인 부분, 우선순위 등을 보여주는 명령어
3. 현재 서버가 사용중인 운여영체제 파일 시스템에서 스왑 파일 사용을 지원하는 지 확인
참고: 대부분의 일반 파일 시스템(ext3, ext4, XFS)이 지원됩니다. 다른 파일 시스템은 지원되지 않을 수 있습니다. 모범 사례는 최신 파일 시스템 설명서를 검토하는 것입니다
https://repost.aws/ko/knowledge-center/ec2-memory-swap-file
현재 파일 시스템 확인: `df -T /`
4. 스왑 파일 생성 및 마운트하기
4-1. 먼저 스왑으로 사용할 파일 생성
스왑 파일의 장점은 스왑 공간을 추가하기 위해 빈 파티션을 찾거나 디스크를 다시 파티션할 필요가 없다는 것이다.
https://www.linux.com/news/all-about-linux-swap-space/ 중 "Swap file" 첫문단 내용
아래 명령어로 2GB 크기의 스왑을 설정하자
sudo dd if=/dev/zero of=/swapfile bs=128M count=16
`/swapfile`: 스왑 파일의 이름
4-2. mkswap을 사용해서 스왑 파일을 준비하고 스왑 파일의 이름을 사용한다.
mkswap /스왑파일
4-3. swapon 명령어를 사용하여 마운트
swapon /swapfile
4-4. swap 생성 확인: `sudo swapon -s`
시스템 부팅시 스왑 파일을 자동으로 시작하도록 파일 시스템을 수정한다.
vim 편집기로 /etc/fstab 파일에 들어간다.
sudo vi /etc/fstab
아래 내용을 추가한다
/swapfile swap swap defaults 0 0
swap 부분을 확인하면 스왑 메모리가 할당된걸 확인할 수 있다.
4-5. free 명령어로 메모리 상태 확인
결과
4개의 컨테이너를 띄어도 서버가 잘 돌아간다.
느낌점
이전 프로젝트에서 동일한 문제를 만났을 때는 원인 분석보다는 빠르게 해결하고 넘어가기에 급급해서 근본적인 해결책을 찾지 않았지만 이번에는 문제를 분석하고 해결하는 과정을 거쳤다.
운영체제 이론을 활용해서 실제 서버의 문제를 해결하니 감회가 새로웠다. CS지식이 개발의 기본이 되는 지식이라는 걸 다시한번 깨달았다.
메모리 관리 문제를 마주한다면 swap이라는 해결책이 있다는 걸 떠올릴 수 있는 사람이 되었다.
ref
- https://repost.aws/ko/knowledge-center/ec2-memory-partition-hard-drive
- https://repost.aws/ko/knowledge-center/ec2-memory-swap-file
- https://diary-developer.tistory.com/32
- All about Linux swap space - Linux.com