앞서 얘기한 것처럼 본 프로젝트의 배포 환경은 그야말로 ‘극한의 환경’이라고 할 수 있을 것이다. 다시 한번 배포 환경에 대해 정리하자면 다음과 같다.

환경 메모리
EC2 (t2.micro) 1024MiB
API Gateway 컨테이너 128MiB
Integrated Services 컨테이너 256MiB
Direct Message Service 컨테이너 256MiB
Drive Service 컨테이너 320MiB
총합 960MiB

단순히 각 서비스 컨테이너가 사용하는 메모리 뿐만 아니라 Host 예약 메모리까지 고려하면 실제로 가용한 메모리 용량은 64MiB(1024MiB - 960MiB) 이하이다. 실제로 EC2 인스턴스에 SSH 접속하여 예약 메모리 값을 확인해보자.

😢 왜 ‘극한 환경’인가?

$ ps --sort=-rss -eo pid,comm,rss

    PID COMMAND           RSS
 363963 java            239592
 224454 java            118956
 176970 java            93272
 220656 java            77000
   3758 dockerd         37028
   1095 systemd-journal 35092
   3747 containerd      17768
 369342 systemd         12844
 363903 containerd-shim 10132
   2152 amazon-ssm-agen  9660
 369324 sshd             9324
 224392 containerd-shim  9140
 220596 containerd-shim  8348
      1 systemd          8108
 369427 sudo             7660
 176912 containerd-shim  7480
 371540 systemd-userwor  6652
 371662 systemd-userwor  6644
 371661 systemd-userwor  6616
 369397 sshd             5524
 369431 bash             4924
 369430 su               4284
 369398 bash             4196
   1966 systemd-logind   2840
   1763 systemd-udevd    2728
 369365 (sd-pam)         2632
 371674 ps               2624
   1862 systemd-resolve  2556
   1971 systemd-network  2548
 369429 sudo             2472
 363881 docker-proxy     2100
 363886 docker-proxy     2060
   1965 systemd-homed    1716
   2156 sshd             1208
   3620 systemd-userdbd  1008
   1866 auditd            648
   1957 dbus-broker       640
 224377 docker-proxy      584
 224372 docker-proxy      496
   1961 lsmd              392
   1999 gssproxy          376
   1963 rngd              348
   1956 dbus-broker-lau   320
   2185 chronyd           196
   1958 systemd-inhibit    80
 176880 docker-proxy       80
   2163 atd                56
   2167 agetty             24
   2168 agetty             12

위의 결과를 보면 java 명령어가 실행되는 컨테이너 당 containerd-shim 프로세스가 실행되고 있고, 각 containerd-shim 프로세스는 약 7 ~ 10MiB의 메모리를 추가로 사용하고 있다.

그리고 아래 명령어를 통해 각 서비스 컨테이너가 현재 사용 중인 메모리 사용량을 보자.

$ docker stats zylo-integrated-services zylo-drive-service zylo-api-gateway zylo-chat-service --no-stream

CONTAINER ID   NAME                       CPU %     MEM USAGE / LIMIT   MEM %     NET I/O           BLOCK I/O         PIDS
db0a13e292c5   zylo-integrated-services   0.10%     230.6MiB / 256MiB   90.09%    54.7kB / 58.6kB   86.7MB / 58.5MB   35
a5f47ed2050f   zylo-drive-service         0.08%     116.8MiB / 320MiB   36.49%    205MB / 24.8MB    272MB / 510MB     50
87c72833c92d   zylo-api-gateway           0.06%     69.36MiB / 128MiB   54.18%    10.7MB / 13.7MB   413MB / 1.15GB    24
b252c374c701   zylo-chat-service          0.06%     91.74MiB / 256MiB   35.84%    275MB / 53.8MB    590MB / 817MB     50

docker stats 명령어를 통해 확인된 각 서비스 컨테이너가 사용 중인 메모리가 ps 명령어 결과와는 사뭇 다르다. 이는 docker stats 명령어가 아래처럼 page cache 값을 제외하고 실제 점유 중인 메모리만 표시하기 때문이다. 여기서 memory_stats.usage_in_bytescgroup이 보고하는 전체 메모리 사용량이며 memory_stats.stats.total_inactive_file이란 reclaimable(돌려받을 수 있는) page cache를 말한다(Docker Docs > 첫번째 Notecgroup Document 참고).

displayed_usage = memory_stats.usage_in_bytes − memory_stats.stats.total_inactive_file

<aside> 💡

Page cache란?

하드 드라이브에 저장된 파일의 캐시를 저장하는 RAM의 일부분. 주로 OS 커널이 사용한다.

cgroup?

Linux 커널이 제공하는 기능 중 하나. 컴퓨터의 자원(CPU, 메모리, 디스크 I/O 등)을 프로세스에 할당하는 역할을 담당한다.

</aside>

이 때문에 docker stats의 결과와 ps의 결과가 상이한 것이다. 여기서 우리가 알 수 있는 부분은, 각 서비스 컨테이너가 사용하는 메모리는 docker stats 명령어를 통해 표시되는 것보다 크다는 것이다. 뿐만 아니라 OOM 킬은 page cache를 포함한 메모리 사용량을 기준으로 트리거(trigger)된다.

결론적으로, 실제 사용 중인 메모리는 다음과 같다.

구성 메모리(MiB)
예약 메모리 ≈ 187
컨테이너 4종
API-Gateway 128
Integrated-Svc 256
Direct-Msg-Svc 256
Drive-Svc 320
컨테이너 오버헤드 (containerd-shim 등) ≈ 34
컨테이너 페이지 캐시 ≈ 40
컨테이너 합계 1034
총 예상 소비 ≈ 1221

따라서 최종적으로 정리하면 다음과 같다

<aside> 💡

  1. Docker container는 실제로 더 많은 메모리를 사용한다. (containerd-shim + memory_stats.total_inactive_file)
  2. Docker Rontainer를 제외하고도 사용되는 메모리는 page cache와 그 외 예약 메모리(containerd-shim 등)를 포함하여 약 260 MiB

결론

t2.micro EC2 인스턴스가 제공하는 1024 MiB 크기의 메모리보다 약 200 MiB가 더 필요하므로 메모리 최적화가 필요하다.

</aside>