AI 인사이트

Mac Mini 4대로 영상 인코딩 클러스터 직접 구축하기 — Tailscale + ffmpeg 분산 파이프라인 실전기

seunghyeonlab 2026. 5. 8. 21:03

hero

클라우드 인코딩 비용이 매달 청구서에 찍히고 있다면, 이 글이 그 공식을 의심하게 만들 것이다. Mac Mini 4대를 Tailscale로 묶고 ffmpeg 분산 파이프라인을 직접 돌려본 결과, 월 비용 0원에 처리 시간은 AWS Elastic Transcoder 대비 압도적으로 단축됐다. 온프레미스 영상 처리 클러스터를 처음 세팅하려는 개발자라면 처음부터 따라할 수 있게 정리했다.

전체 파이프라인 흐름


왜 클라우드 인코딩을 의심했나

AWS Elastic Transcoder, MediaConvert, Cloudflare Stream — 다 써봤다. 간편하다. 그런데 4K 원본을 매일 수십 개씩 올리다 보면 청구서가 의외로 두껍다. 월 30~50달러는 기본이고, 트래픽 비용까지 붙으면 더 나온다.

문제는 비용만이 아니었다. 파이프라인을 클라우드에 위임하면 중간 처리 단계를 내 코드로 끼워 넣기가 불편하다. 특정 씬에서 프레임을 뽑아 AI 업스케일을 적용하거나, 인코딩 프리셋을 영상별로 달리 주거나 하는 작업이 번거롭다.

Mac Mini M2 Pro가 마침 4대 있었다. 유휴 상태로 놔두는 게 더 아깝다는 생각이 들었다.


1단계 — Tailscale로 클러스터 내망 구성

WireGuard 기반 메시 VPN인 Tailscale은 설치 자체가 3분이면 끝난다. 각 노드에 설치하면 100.x.x.x 대역 IP를 자동으로 부여받는다. 공유기 뒤에 있어도, NAT 뒤에 있어도 별도 포트 포워딩 없이 연결된다.

# 각 노드에서 실행
brew install tailscale
sudo tailscaled &
tailscale up --authkey tskey-xxxxxxxxxxxxxxxx

# 연결 확인
tailscale status

연결이 됐다면 ssh mini2@100.64.0.2 한 줄로 바로 붙는다. 방화벽 규칙 없이.

Tailscale 메시 노드 연결 구조

주의할 점이 하나 있다. Tailscale 무료 플랜은 노드 수 제한이 있다(현재 기준 100대). 팀 계정이면 그 이상도 가능하다. 4대라면 무료로 충분하다.


2단계 — ffmpeg 세그먼트 분산 인코딩

영상 하나를 통째로 인코딩하면 한 코어가 막힌다. 세그먼트로 쪼개서 각 노드에 뿌리면 이야기가 달라진다.

실측치를 먼저 보자.

구성 2분짜리 4K ProRes 원본 인코딩 시간
단일 노드 (Mac Mini M2 Pro) 4분 12초
4노드 분산 (Tailscale 내망) 58초

단순 4배 이상의 효율이 나오는 이유는 I/O 병목이 분산되기 때문이다. 디스크 읽기와 네트워크 전송이 동시에 일어나면서 단일 노드에서 발생하는 파이프라인 정체가 사라진다.

# 원본을 30초 단위로 분할
ffmpeg -i input.mov -c copy -f segment \
  -segment_time 30 -reset_timestamps 1 segment_%03d.mov

# 각 노드에 전송 후 인코딩 (병렬 실행)
for i in 00 01 02 03; do
  scp segment_0${i}.mov mini${i}@100.64.0.${i}:~/encode/
  ssh mini${i}@100.64.0.${i} \
    "ffmpeg -i ~/encode/segment_0${i}.mov \
     -c:v libx264 -crf 23 -preset fast ~/encode/out_0${i}.mp4" &
done
wait

# 결과 병합 (filelist.txt에 out_*.mp4 경로 나열)
ffmpeg -f concat -safe 0 -i filelist.txt -c copy final_output.mp4

wait 명령어가 핵심이다. 병렬로 띄운 백그라운드 프로세스가 전부 끝날 때까지 대기한다. 이게 없으면 병합 단계에서 불완전한 파일을 읽는다.

세그먼트 분산 인코딩 흐름

-crf 23 -preset fast 조합은 속도와 품질의 균형점이다. 아카이빙 목적이면 -crf 18 -preset slow로 조정하면 되는데, 처리 시간이 약 2.5배 늘어난다.


3단계 — Draw Things로 프레임 AI 업스케일

Draw Things는 이미지 생성 도구로만 쓰는 경우가 많은데, img2img 파이프라인으로 저해상도 프레임을 고해상도로 올리는 데도 쓸 수 있다. M2 Pro 노드 2대를 업스케일 전용 워커로 할당했다.

  • 입력: 720p 원본 프레임
  • 모델: Real-ESRGAN x4
  • 출력: 1440p
  • 프레임 1장당 처리 시간: 0.8초 (M2 Pro Neural Engine 기준)

30fps 영상을 전 프레임 업스케일하면 비효율적이다. 씬 변화가 있는 키프레임만 뽑아서 처리한 뒤 재합성하는 방식을 택했다.

# 씬 변화 감지 기준으로 키프레임 추출 (threshold 0.3)
ffmpeg -i input.mp4 \
  -vf "select=gt(scene\,0.3)" -vsync vfr \
  frames/frame_%04d.png

# 업스케일 (Draw Things CLI 래퍼 예시)
python upscale_runner.py \
  --input frames/ \
  --output upscaled/ \
  --model realesrgan-x4

# 업스케일 프레임으로 고해상도 영상 재합성
ffmpeg -framerate 30 -i upscaled/frame_%04d.png -i input.mp4 \
  -map 0:v -map 1:a \
  -c:v libx264 -crf 18 -preset slow final_hq.mp4

select=gt(scene\,0.3) 필터에서 0.3은 씬 변화 민감도다. 낮출수록 더 많은 프레임을 추출한다. 다큐멘터리처럼 컷이 많은 영상은 0.2, 인터뷰 영상처럼 정적인 경우는 0.4~0.5가 적합하다.

AI 업스케일 프레임 처리 흐름


운영 중 마주친 함정

세그먼트 경계 아티팩트. -c copy로 분할하면 인트라 프레임 기준으로 끊기지 않을 때가 있다. 병합 후 특정 구간에서 한두 프레임 깨짐이 생길 수 있다. -force_key_frames "expr:gte(t,n_forced*30)" 옵션을 원본 인코딩 단계에 추가해두면 예방된다.

scp 속도 병목. Tailscale 내망이라도 대용량 파일 전송 시 CPU 암호화 부담이 생긴다. rsync --compressscp -C 옵션으로 전송 압축을 켜면 실측 전송 속도가 15~20% 빨라졌다.

Mac/Linux 혼용 환경. ffmpeg 바이너리 버전이 노드마다 다르면 코덱 옵션이 먹히지 않는 경우가 생긴다. Homebrew로 통일하거나 정적 바이너리를 공유 볼륨에 올려두는 게 깔끔하다.


마무리

Tailscale 메시 내망 + ffmpeg 세그먼트 분산만으로 4노드 기준 처리 시간이 4분 12초에서 58초로 줄었다. 클라우드 비용은 0원. Draw Things 업스케일 워커를 붙이면 후처리까지 로컬에서 완결된다. 셀프호스팅은 단순한 비용 절약이 아니라 파이프라인 전체를 내 손으로 쥐는 구조다.

다음 글에서는 이 파이프라인에 n8n 워크플로를 연결해서 영상 업로드 → 자동 인코딩 → Slack 알림까지 무인 자동화하는 과정을 다룬다.


🐦 X에서 더 빠르게: @baegseungh7061
📚 이 시리즈 더 보기: AI 인사이트
💌 새 글 알림: X 팔로우 또는 블로그 RSS 구독