
Node.js와 FFmpeg를 사용해 텍스트 입력을 받아 영상으로 인코딩해주는 간단한 웹 애플리케이션을 개발했습니다.(https://petnyang.shop/)
이 앱은 Docker로 컨테이너화하여 배포했으며, 오라클vm에 올렸는데요..
nginx와 연결하고 웹ui로 인코딩을 실행했더니, 서버 전체가 뻗어버리는 현상이 발생했습니다.

이상태로 사망..
제가 올린 vm은 프리티어로 메모리가 1G짜리여서 FFmpeg 실행 옵션을 조정해서 시스템 부담을 줄이려고 했습니다.
예를 들어 -threads 옵션을 1로 제한하거나, CPU/IO 우선순위를 낮추는 시도를 했죠...
function genFFmpegArgs(args) {
const safeArgs = args.includes("-threads")
? args
: [...args, "-threads", "1"];
const process = spawn("nice", [
"-n", "19", // CPU 최저 우선순위
"ionice", "-c3", // IO 최저 우선순위
"ffmpeg",
...safeArgs,
]);
return process;
}
하지만 문제가 계속되어, 서버가 다운된 후 가장 먼저 의심한 건
메모리 부족(OOM).
dmesg | grep -i ffmpeg 명령어로 시스템 로그를 확인해보니, 다음과 같은 메시지가 보이네요.
dmesg | grep -i ffmpeg

FFmpeg가 포함된 컨테이너 프로세스가 OOM Killer에 의해 종료된 것을 확인했습니다.
성능을 포기해서 느려져도 괜찮으니 메모리가 부족하면 swap메모리를 쓰도록 설정했습니다.
docker run --cpus="1" --memory="512m" --memory-swap=1g ...
확인해보니 도커가 있는 호스트os 설정에서도 스왑이 설정되어 있어야 하므로 다음과 같이 스왑 공간 생성:
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
실행전

실행후 (죽지마...)

다행히 서버는 인코딩 진행중에도 웹요청을 받을 수 있었고, 다만 인코딩 시간이 너무길어 응답처리를 비동기로 변경하기로 했습니다.
ffmpeg는 인코딩작업중에 여러 로그를 남기는데요.
처음에는 요청된 영상의 메타데이터 등을 남기고, 그 후는 현재 몇초나 인코딩되었는지 로그를 찍게됩니다.
...
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/app/src/videos/cat-concat.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf61.0.100
Duration: 00:00:18.76, start: 0.000000
...
frame= 203 fps=2.5 q=28.0 size= 512kB time=00:00:08.03 bitrate= 522.1kbits/s dup=0 drop=38 speed=0.0974x
...
frame= 274 fps=2.5 q=28.0 size= 768kB time=00:00:10.91 bitrate= 576.5kbits/s dup=0 drop=52 speed= 0.1x
...
그러면 정규식으로
Duration: 00:00:18.76, start: 0.000000
time=00:00:08.03
둘을 뽑아와 현재 진행률을 구해서 현재 진행상태를 ws나 SSE등으로(polling도 괜찮습니다) 프론트가 표현해주면 됩니다.
로컬노트북에선 잘되던 SSE 메시지가 배포후에 실시간 반영되지 않고 모여서 전송되는 문제를 확인했습니다.

(1초마다 data: xxx 가 한줄씩 와야하는데 5분뒤에 한방에 오네요)
로컬에서 잘되는데 라이브에서 다른건 앞단에 Nginx 설정이라 생각해서 검색해보니 버퍼링 옵션을 수정하라는군요.
location /api/stream {
...
proxy_buffering off;
}


(새로고침 해도 진행률 상태를 1초마다 SSE로 프론트로 전달)
FFmpeg 인코딩은 고사양 연산이므로, VM 환경에서는 메모리와 우선순위 조절이 필수입니다.
시스템 로그와 도커 리소스 설정을 활용해 문제를 해결할 수 있었습니다.