Skip to content

Docker compose를 이용해서 메모리 사용률을 줄여보자

Seungheon Han edited this page Dec 3, 2024 · 1 revision

미디어 서버 배포 후 메모리 사용량

스크린샷 2024-12-03 00 27 28

미디어 서버 + 레코드 서버 배포 후 메모리 사용량

스크린샷 2024-12-03 00 29 14

현재 우리 서비스에서 사용하고 있는 인스턴스는 1대이고, 해당 인스턴스에서 Api, Media, Chat, Record 서버 등을 실행시키고 있다. Api 서버와 Chat 서버를 실행 시킬 때는 메모리 사용량에 큰 변화가 없지만, Media, Record 서버를 실행시키면 메모리 사용량이 60퍼센트까지 치솟고 있다. 이렇게 되면 메모리 사용량이 서버의 한계에 가까워져 여러 가지 문제가 발생할 수 있다.

  • 메모리가 60% 이상 지속적으로 사용될 경우, 시스템 여유 자원이 부족해지고, 프로세스가 제대로 동작하지 않을 가능성이 높아짐.
  • Media 서버와 Record 서버는 실시간 데이터 처리를 요구하며, 메모리와 CPU 사용량이 높음. 메모리 부족 상황에서 응답 속도가 느려지거나 서버가 중단될 위험이 있음

원인

sudo docker run -d --name media-camon \\
  -p 3001:3001 \\
  -p 30000-31000:30000-31000 \\

sudo docker run -d --name record-camon \\
	-p 3003:3003 \\
	-p 10000-10300:10000-10300/udp ${{ secrets.NCLOUD_REGISTRY_URL }}/record-camon:latest

위 코드는 우리 서비스의 git action script의 일부로, 각각 미디어 서버와 레코드 서버를 도커 컨테이너로 실행시키는 명령어이다. 코드에서 보이듯이, 각 컨테이들마다 300개에서 1000개까지의 포트를 할당하여 데이터 송수신을 처리하고 있으며, 이게 메모리 사용량이 급증하게 된 원인이었다.

각 컨테이들마다 포트를 매핑시켜주면 Docker Proxy라는 녀석이 포트 별로 생성된다. 때문에 약 1300개 가량의 Docker proxy가 생성되게 되면서, 메모리 사용량이 급증하게 된 것이었다.

Docker Proxy의 역할

포트 매핑 관리

  • Docker는 -p 옵션으로 컨테이너의 내부 포트를 호스트의 외부 포트로 노출할 때, Docker Proxy를 생성하여 트래픽을 중계한다.
  • 예를 들어, -p 3001:3001을 지정하면, 호스트의 3001 포트로 들어온 트래픽은 Docker Proxy를 통해 컨테이너의 3001 포트로 전달된다.

TCP/UDP 포트 관리

  • Docker Proxy는 TCP와 UDP 모두에 대해 동작하며, 각각의 프로토콜별로 별도의 프로세스를 생성하여 요청을 처리한다.
  • UDP 프로토콜의 경우, 다량의 포트를 매핑하면 Docker Proxy 프로세스가 폭발적으로 증가하여 메모리와 CPU 사용량이 높아질 수 있다.

Bridge 네트워크 트래픽 라우팅

  • bridge 네트워크 모드에서는 컨테이너 간의 통신이 NAT(Network Address Translation)를 통해 이루어지며, Docker Proxy가 이를 지원한다.

Docker Proxy의 동작 조건

Bridge 네트워크 모드(기본값)

  • Docker가 별도의 네트워크를 명시하지 않으면 기본적으로 bridge 모드에서 실행됩니다. 이 모드에서는 Docker Proxy가 생성됩니다.

Host 네트워크 모드

  • -network host를 사용하면 컨테이너가 호스트 네트워크를 직접 사용하므로 Docker Proxy가 필요 없습니다. 이 모드에서는 호스트와 컨테이너 간의 트래픽 중계가 불필요해 메모리와 CPU 사용량이 줄어듭니다.

Docker Proxy가 메모리에 미치는 영향

포트 매핑 증가

  • 컨테이너에서 수백 개 이상의 포트를 매핑하면, 각 포트마다 별도의 Docker Proxy 프로세스가 생성된다
  • 예를 들어, 1000개의 포트를 매핑하면 1000개의 Docker Proxy가 생성되며, 이는 메모리 사용량 증가로 이어진다

UDP 포트의 높은 자원 소모

  • UDP는 상태를 유지하지 않는 통신 방식이지만, 포트별 Docker Proxy는 메모리와 CPU를 추가로 소비합니다. 이로 인해 UDP 포트 매핑이 많을수록 자원 소모가 커진다

해결 방법

문제 해결 방향

Docker Proxy의 과도한 생성으로 인해 발생한 메모리 사용량 증가 문제를 해결하기 위해 다음 두 가지 해결 방향을 설정했습니다.

  1. 컨테이너 네트워크 통합

    서로 다른 네트워크에 속한 컨테이너가 직접 통신하지 못했던 문제를 해결하기 위해, Docker Compose를 사용하여 모든 컨테이너를 동일한 네트워크에 포함시켰습니다.

    이를 통해 포트 매핑 없이도 컨테이너 간의 통신이 가능해지고, Docker Proxy 생성이 필요 없게 되었습니다.

  2. 불필요한 포트 매핑 축소

    미디어 서버는 외부 클라이언트와 통신해야 하므로 필수적인 포트 매핑을 유지했습니다.

    그러나 레코드 서버는 미디어 서버와의 내부 통신만 필요하므로, 레코드 서버의 외부 포트 매핑을 제거하여 Docker Proxy를 최소화했습니다.


적용 방법

컨테이너 네트워크 통합

Docker Compose를 활용하여, mediarecord 컨테이너를 하나의 네트워크에 포함시켰습니다.

이를 통해 컨테이너 간에는 서비스 이름만으로 직접 통신할 수 있어 별도의 포트 매핑이 필요 없어졌습니다.

Docker Compose 설정 예시

version: "3.9"

services:
  media:
  container_name: media-camon
  image: media-camon
  networks:
    - camon
  ports:
    - "3001:3001"
    - "30000-31000:30000-31000"

  record:
  container_name: record-camon
  image: record-camon
  networks:
    - camon:
  ports:
    - "3003:3003"

networks:
  camon:
  driver: bridge

결과

  • Docker Proxy의 생성 개수가 대폭 감소하여, 메모리 사용량이 안정화되었다.
  • Media와 Record 서버 간 통신은 포트 매핑 없이 내부 네트워크를 통해 처리되므로, 메모리 사용량을 약 30% 이상 감소시키는 성과를 얻었다
스크린샷 2024-12-03 01 00 18

👥 팀 강점

🧑‍💻 개발 일지

📌 ALL

📌 FE

📌 BE

💥 트러블 슈팅

📌 FE

📌 BE

🤔 고민

📚 학습 정리

📌 김광현

📌 백지연

📌 전희선

📌 한승헌

🤝 회의록

🗒️ 데일리 스크럼

💬 팀 회고


👨‍👩‍👧‍👦 소개

🌱 문화

🔨 기술 스택

⚙️ 서비스 아키텍쳐

🚧 CI/CD

🌊 Flow

💭 6주를 보내면서

Clone this wiki locally