개발 기록

오픈소스를 fork 하지 않고 서브모듈로 관리하기 — GPU 서버 여덟 개를 굴리며

TTS, LLM, STT, OCR 서버를 한 저장소에서 관리한다. 오픈소스를 복사하거나 fork 하는 대신 서브모듈로 묶은 이유와 그 구조를 정리했다.

로컬 GPU 서버에 여러 AI 모델 서버를 띄워 쓰고 있다. TTS(음성 합성), LLM, STT(음성 인식), OCR, 영상·3D 생성, 음악 생성, 보컬 변환까지 종류가 늘었다.

전부 오픈소스를 가져다 쓰는데, 그대로는 못 쓴다. API 형태를 맞춰야 하고, 설치 절차를 스크립트로 만들어둬야 하고, 설정도 내 환경에 맞게 고쳐야 한다.

여기서 “오픈소스를 어떻게 관리할 것인가” 문제가 생긴다.

세 가지 방법과 각각의 문제

1. 소스를 복사해서 내 저장소에 넣는다

가장 간단하다. 그리고 가장 빨리 망가진다. 업스트림이 업데이트되면 내가 어디를 고쳤는지 알 수 없어서 병합할 수가 없다. 몇 달 지나면 사실상 다른 프로젝트가 된다.

2. fork 해서 내 저장소에서 고친다

업스트림을 따라갈 수는 있다. 그런데 프로젝트가 여덟 개면 fork도 여덟 개다. 각각 별도 저장소이고, 각각 rebase를 해야 하고, 내 커스텀 코드가 남의 코드 히스토리에 섞인다.

3. 서브모듈로 묶고, 내 코드는 밖에 둔다

이걸 택했다.

구조

gpu_servers/
├── tts/          # Text-to-Speech 서버
├── llm/          # LLM 서버
├── stt/          # Speech-to-Text 서버
├── ocr/          # OCR 서버
├── video/        # 영상·3D 생성 서버
├── music/        # 음악 생성 서버
├── vocal/        # 보컬 합성·변환 서버
└── scripts/      # 공통 스크립트

각 프로젝트 안은 이렇게 생겼다.

<project>/
├── _src/         # Git Submodule (오픈소스 원본, 손대지 않는다)
├── scripts/      # 설치 스크립트 (00_setup_python.sh 등)
├── config/       # 설정 파일
├── server.py     # 내가 쓴 서버 코드
└── .venv/        # 파이썬 가상환경 (gitignore)

핵심은 _src/와 나머지의 분리다. 오픈소스 원본은 _src/ 안에 그대로 두고 절대 고치지 않는다. 내가 필요한 것은 전부 바깥에 쓴다.

이 구조의 실질적 이점

업데이트가 명시적이다. 서브모듈 업데이트는 커밋 해시를 옮기는 일이다. 부모 저장소에는 “이 시점의 업스트림으로 옮겼다”가 커밋 하나로 남는다. 새 버전에서 문제가 생기면 해시만 되돌리면 된다.

내 코드와 남의 코드가 안 섞인다. git log를 보면 내가 쓴 코드의 히스토리만 보인다. 업스트림 커밋 수천 개에 파묻히지 않는다.

저장소가 가볍다. 서브모듈은 참조만 저장한다. 모델 가중치나 예제 데이터가 잔뜩 든 오픈소스 여덟 개를 통째로 넣으면 클론이 불가능해진다.

“안 고친다”가 강제된다. _src/ 안을 고치면 서브모듈에 변경사항이 뜬다. 눈에 보이니까 안 하게 된다. 복사 방식에서는 슬금슬금 고치다가 되돌릴 수 없게 된다.

클론할 때 주의할 점

서브모듈 방식의 유일한 불편은 클론 절차가 한 단계 는다는 것이다.

git clone --recursive https://github.com/.../gpu_servers.git

--recursive를 빠뜨리면 _src/가 빈 디렉토리로 온다. 이미 클론한 뒤라면 이렇게 채운다.

git submodule update --init --recursive

이걸 몰라서 “왜 소스가 없지” 하고 헤매는 일이 처음 몇 번 있었다. 설치 스크립트 첫 줄에 넣어두는 게 낫다.

설치 스크립트에 번호를 붙인 이유

각 프로젝트의 scripts/ 안에는 00_setup_python.sh 같은 식으로 번호가 붙은 스크립트가 있다.

GPU 서버 설치는 순서가 중요하다. 파이썬 환경을 먼저 만들고, CUDA 버전에 맞는 torch를 깔고, 그다음 프로젝트 의존성을 깔아야 한다. 순서가 틀리면 torch가 CPU 버전으로 덮이거나 의존성 충돌이 난다.

번호를 붙여두면 순서가 파일 이름에 남는다. 반년 뒤에 다시 설치할 때 순서를 기억할 필요가 없다. 이건 문서에 적어두는 것보다 확실했다.

정리

  • 오픈소스를 고쳐 써야 하면 복사하지 말고 서브모듈로 두고 내 코드는 바깥에 쓴다
  • _src/ 를 손대지 않는 규칙은 구조가 강제해준다. 고치면 눈에 보인다
  • 서브모듈 업데이트는 커밋 해시 이동이라 되돌리기 쉽다
  • --recursive 를 빠뜨리면 빈 디렉토리가 온다. 설치 스크립트가 챙기게 한다
  • 순서가 중요한 설치 스크립트는 파일 이름에 번호를 붙인다. 문서보다 확실하다