From 109dc45785043453df2a732758e7dd23b8b500f7 Mon Sep 17 00:00:00 2001 From: kangcheolung Date: Fri, 9 Oct 2026 12:24:27 +0900 Subject: [PATCH 1/3] =?UTF-8?q?chore:=20#450=20=EC=9E=84=EB=B2=A0=EB=94=A9?= =?UTF-8?q?=20=EC=84=9C=EB=B2=84=20torch=EB=A5=BC=20CPU=20=EC=A0=84?= =?UTF-8?q?=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=EA=B5=90=EC=B2=B4=ED=95=B4=20NVI?= =?UTF-8?q?DIA=20=EB=8F=85=EC=A0=90=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit x86 리눅스에서 torch==2.4.1을 설치하면 NVIDIA 독점 패키지(nvidia-*) 12개가 함께 설치되어 OSI 승인 라이선스 기준에 걸릴 수 있다. 서버는 GPU 없이 CPU만 사용하므로 PyTorch CPU 전용 저장소를 추가 인덱스로 지정해 torch 2.4.1+cpu를 받는다. 서버 코드와 API는 변경하지 않는다. Co-Authored-By: Claude Sonnet 5.5 --- backend/embedding-server/requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/embedding-server/requirements.txt b/backend/embedding-server/requirements.txt index 7f1a0c93..91685f9f 100644 --- a/backend/embedding-server/requirements.txt +++ b/backend/embedding-server/requirements.txt @@ -1,3 +1,5 @@ +# torch를 NVIDIA 독점 패키지(nvidia-*)가 딸려오는 GPU용이 아닌 CPU 전용 빌드로 받는다. +--extra-index-url https://download.pytorch.org/whl/cpu fastapi==0.115.0 uvicorn==0.30.6 FlagEmbedding==1.2.11 From feea1a60366b87a13c51bbaaa3ad0791cc0334a5 Mon Sep 17 00:00:00 2001 From: kangcheolung Date: Fri, 9 Oct 2026 12:24:27 +0900 Subject: [PATCH 2/3] =?UTF-8?q?docs:=20#450=20=EC=9E=84=EB=B2=A0=EB=94=A9?= =?UTF-8?q?=20=EC=84=9C=EB=B2=84=20torch=20CPU=20=EC=A0=84=EC=9A=A9=20?= =?UTF-8?q?=EA=B5=90=EC=B2=B4=20=EC=84=A4=EA=B3=84=20=EB=AC=B8=EC=84=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 배경, 결정, 변경 범위, 검증 결과(x86 nvidia-* 12개 -> 0개, 이미지 3.16GB -> 2.29GB, arm64 임베딩 값 일치)와 검증하지 못한 범위를 기록한다. Co-Authored-By: Claude Sonnet 5.5 --- ...gcheolung-#450-embedding-torch-cpu-only.md | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 docs/design/kangcheolung-#450-embedding-torch-cpu-only.md diff --git a/docs/design/kangcheolung-#450-embedding-torch-cpu-only.md b/docs/design/kangcheolung-#450-embedding-torch-cpu-only.md new file mode 100644 index 00000000..1d1f59cb --- /dev/null +++ b/docs/design/kangcheolung-#450-embedding-torch-cpu-only.md @@ -0,0 +1,87 @@ +# #450 임베딩 서버 torch를 CPU 전용으로 교체해 NVIDIA 독점 패키지 제거 + +closes #450 + +--- + +## 배경 + +대회의 오픈소스 라이선스 평가 기준은 OSI 승인 라이선스이고, 평가는 각 평가 시점의 저장소 상태로 이루어진다. (#399 참고) + +`backend/embedding-server/requirements.txt`의 `torch==2.4.1`을 linux/amd64에서 설치하면 NVIDIA CUDA 런타임 패키지 12개가 의존성으로 함께 설치된다. + +```text +nvidia-cublas-cu12 12.1.3.1 nvidia-cufft-cu12 11.0.2.54 +nvidia-cuda-cupti-cu12 12.1.105 nvidia-curand-cu12 10.3.2.106 +nvidia-cuda-nvrtc-cu12 12.1.105 nvidia-cusolver-cu12 11.4.5.107 +nvidia-cuda-runtime-cu12 12.1.105 nvidia-cusparse-cu12 12.1.0.106 +nvidia-cudnn-cu12 9.1.0.70 nvidia-nccl-cu12 2.20.5 +nvidia-nvjitlink-cu12 12.9.86 nvidia-nvtx-cu12 12.1.105 +``` + +- 이 패키지들의 라이선스는 NVIDIA 독점(NVIDIA Proprietary Software)이며 OSI 승인이 아니다. (PyPI 메타데이터 기준) +- 개발 PC(Apple Silicon, linux/arm64) 이미지에는 설치되지 않아 드러나지 않았다. 변경 전 이미지에서 `pip list | grep -ci nvidia`는 arm64 `0`, amd64 `12`였다. +- 임베딩 서버는 `docker-compose.yml`에서 GPU 없이 CPU만 사용하므로(`cpus`, `mem_limit`) 이 패키지는 실행에 쓰이지 않는다. (`torch.cuda.is_available()` → `False`) + +--- + +## 결정 + +**torch 버전은 유지하고, PyTorch CPU 전용 저장소를 추가 인덱스로 지정한다.** + +`requirements.txt` 맨 위에 한 줄을 추가한다. + +```text +--extra-index-url https://download.pytorch.org/whl/cpu +``` + +- `torch==2.4.1`이 CPU 전용 빌드 `2.4.1+cpu`로 선택된다. 이 빌드는 `nvidia-*`와 `triton`을 요구하지 않는다. +- `Dockerfile`이 `pip install -r requirements.txt`를 쓰고, `requirements-test.txt`도 `-r requirements.txt`로 이 파일을 참조하므로 **이미지와 로컬 테스트가 같은 설정을 쓴다.** +- 서버 코드(`main.py`)와 API 계약은 변경하지 않는다. + +### 선택하지 않은 방법 + +- **`Dockerfile`의 `pip install`에만 인덱스를 지정**: 로컬 테스트 환경(`requirements-test.txt`)이 GPU용 torch를 받아 이미지와 어긋난다. +- **`--index-url`로 기본 인덱스를 교체**: PyPI의 나머지 패키지를 받지 못한다. 추가 인덱스로 충분하다. +- **torch 버전 변경**: 라이선스 문제가 버전이 아니라 빌드 종류에 있어 불필요하다. + +--- + +## API 명세 + +변경 없음. `/embed`, `/embed/batch`, `/health*`, `/metrics`의 요청·응답·에러 계약은 그대로다. + +--- + +## 변경 범위 + +| 파일 | 변경 | +|---|---| +| `backend/embedding-server/requirements.txt` | CPU 전용 torch 저장소 `--extra-index-url` 추가 | + +--- + +## 검증 + +| 항목 | 결과 | +|---|---| +| linux/amd64 `pip install --dry-run` | 76개 → 63개. `nvidia-*` 12개와 `triton` 제거, `torch 2.4.1+cpu` 선택 | +| linux/amd64 이미지 빌드 | `pip list`의 `nvidia-*` **0개**(변경 전 12개), `torch 2.4.1+cpu`, `import torch` 성공, `cuda_available False` | +| linux/amd64 이미지 크기 | 3.16GB → **2.29GB** (약 0.87GB 감소) | +| 임베딩 값 비교 (linux/arm64, 고정 문장 6개, `/embed/batch`, 1024차원) | 변경 전·후 **모든 값이 완전히 일치** (`cos=1.0`, 최대 절대 오차 `0`) | +| `pytest test_main.py` (변경 후 이미지) | 22 passed | +| 기준 이미지 | 변경 전 이미지는 `HEAD`의 `requirements.txt`로 같은 방식으로 빌드해 비교 | + +### 검증하지 못한 범위 + +- 임베딩 값 비교는 **arm64**에서만 수행했다. arm64 torch는 원래 CPU 빌드라 변경 전후 패키지 구성이 같다. 즉 이 비교는 서버 코드와 모델 동작이 그대로임을 확인한 것이며, **x86 CPU 빌드의 수치 일치**는 확인하지 못했다. amd64는 설치 구성, `import torch`, 이미지 크기까지만 확인했다. (에뮬레이션에서 BGE-M3 추론은 실행하지 않음) +- 배포 환경(GCP VM 등)에서의 실제 기동은 이 이슈에서 확인하지 않았다. 배포 시 `/health/ready`로 확인이 필요하다. + +--- + +## 이 이슈의 범위 밖 + +- `minio/minio`(태그 미고정, AGPL-3.0), `grafana/grafana:13.2.3`(AGPL-3.0): OSI 승인 라이선스이나 별도 컨테이너 실행의 충돌 여부는 OpenUP 멘토링(~10/26)에서 확인한다. +- `docs/images/architecture.png`의 Redis 로고, README 라이선스 표 보강 +- `FlagEmbedding==1.2.11`은 PyPI 라이선스 메타데이터가 비어 있다. +- 라이선스는 POM·PyPI·npm 메타데이터 기준이며 소스 스캔 결과와 다를 수 있다. From 66c922404e191d0e1b6153672aa7345631b1cc8c Mon Sep 17 00:00:00 2001 From: kangcheolung Date: Fri, 9 Oct 2026 12:35:46 +0900 Subject: [PATCH 3/3] =?UTF-8?q?docs:=20#450=20amd64=20=EA=B8=B0=EB=8F=99?= =?UTF-8?q?=C2=B7=EC=9E=84=EB=B2=A0=EB=94=A9=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EB=B0=98=EC=98=81=EA=B3=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=ED=81=AC=EA=B8=B0=20=EC=88=98=EC=B9=98=20?= =?UTF-8?q?=EC=A0=95=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit x86(amd64) 에뮬레이션에서 변경 전·후 이미지를 실제로 기동해 /health/ready와 고정 문장 임베딩 값이 완전히 같음을 확인한 결과를 추가한다. 이미지 크기는 서로 다른 기준을 섞어 적었던 값을 같은 기준으로 바로잡는다. (디스크 사용량 9.29GB -> 2.29GB, 압축 기준 3.16GB -> 0.50GB) Co-Authored-By: Claude Sonnet 5.5 --- ...kangcheolung-#450-embedding-torch-cpu-only.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/design/kangcheolung-#450-embedding-torch-cpu-only.md b/docs/design/kangcheolung-#450-embedding-torch-cpu-only.md index 1d1f59cb..b3d96517 100644 --- a/docs/design/kangcheolung-#450-embedding-torch-cpu-only.md +++ b/docs/design/kangcheolung-#450-embedding-torch-cpu-only.md @@ -67,15 +67,19 @@ nvidia-nvjitlink-cu12 12.9.86 nvidia-nvtx-cu12 12.1.105 |---|---| | linux/amd64 `pip install --dry-run` | 76개 → 63개. `nvidia-*` 12개와 `triton` 제거, `torch 2.4.1+cpu` 선택 | | linux/amd64 이미지 빌드 | `pip list`의 `nvidia-*` **0개**(변경 전 12개), `torch 2.4.1+cpu`, `import torch` 성공, `cuda_available False` | -| linux/amd64 이미지 크기 | 3.16GB → **2.29GB** (약 0.87GB 감소) | -| 임베딩 값 비교 (linux/arm64, 고정 문장 6개, `/embed/batch`, 1024차원) | 변경 전·후 **모든 값이 완전히 일치** (`cos=1.0`, 최대 절대 오차 `0`) | -| `pytest test_main.py` (변경 후 이미지) | 22 passed | -| 기준 이미지 | 변경 전 이미지는 `HEAD`의 `requirements.txt`로 같은 방식으로 빌드해 비교 | +| linux/amd64 이미지 크기 | 디스크 사용량(압축 해제 후, `docker images`) 9.29GB → **2.29GB**, 압축 기준(`docker image inspect`) 3.16GB → **0.50GB** | +| linux/amd64 기동 (에뮬레이션, 변경 전·후 각각) | 모델 로딩 후 `/health/ready` **200**(약 10초), `uname -m`=`x86_64`. 변경 전 `torch 2.4.1+cu121`·`nvidia-*` 12개, 변경 후 `torch 2.4.1+cpu`·`nvidia-*` 0개 | +| 임베딩 값 비교 (linux/amd64, 고정 문장 6개, `/embed/batch`, 1024차원) | 변경 전·후 **모든 값이 완전히 일치** (`cos=1.0`, 최대 절대 오차 `0`) | +| 임베딩 값 비교 (linux/arm64, 같은 문장) | 변경 전·후 **모든 값이 완전히 일치** (`cos=1.0`, 최대 절대 오차 `0`) | +| `pytest test_main.py` (변경 후 이미지, arm64) | 22 passed | +| 기준 이미지 | 변경 전 이미지는 `origin/develop`의 `requirements.txt`로 같은 방식으로 빌드해 비교 | + +참고: 같은 변경 전 이미지에서 arm64와 amd64의 임베딩을 비교하면 `cos=0.9999964`, 최대 절대 오차 `3.0e-4`로 아키텍처 사이에는 미세한 차이가 있다. 이 차이는 이번 변경과 무관한 CPU 아키텍처 차이이므로, 비교 기준은 **같은 아키텍처에서의 변경 전·후**다. ### 검증하지 못한 범위 -- 임베딩 값 비교는 **arm64**에서만 수행했다. arm64 torch는 원래 CPU 빌드라 변경 전후 패키지 구성이 같다. 즉 이 비교는 서버 코드와 모델 동작이 그대로임을 확인한 것이며, **x86 CPU 빌드의 수치 일치**는 확인하지 못했다. amd64는 설치 구성, `import torch`, 이미지 크기까지만 확인했다. (에뮬레이션에서 BGE-M3 추론은 실행하지 않음) -- 배포 환경(GCP VM 등)에서의 실제 기동은 이 이슈에서 확인하지 않았다. 배포 시 `/health/ready`로 확인이 필요하다. +- amd64 확인은 **Apple Silicon Mac의 Docker linux/amd64 에뮬레이션**에서 수행했다. 실제 x86 하드웨어에서 계산한 값은 아니다. 변경 전·후 값이 비트 단위로 같았으므로 CPU 전용 빌드가 결과를 바꾸지 않는다고 판단하지만, 실제 x86 서버에서의 재확인은 배포 때 필요하다. +- 배포 환경(GCP VM 등)에서의 실제 기동은 이 이슈에서 확인하지 않았다. 배포 시 `/health/ready`와 임베딩 값으로 확인이 필요하다. ---