Cloudera AI(CAI) 1.5.5 SP3 폐쇄망 환경에서 Hugging Face 모델을 쓰려면, 인터넷이 되는 호스트에서 모델을 내려받아 클러스터의 Ozone S3 버킷에 올린 뒤 AI Registry / Model Hub 에 등록해야 한다. Model Hub 카탈로그에 모델 이름이 보이더라도 가중치는 내장돼 있지 않다. 이 문서는 Cloudera 가 제공하는 import_to_airgap.py 스크립트(GitHub cloudera/Model-Hub)로 다운로드 → 업로드 → 등록 → 엔드포인트 생성까지 리허설한 결과를 절차로 정리한 것이다. NIM(NGC) 모델 반입은 Cloudera AI 폐쇄망 NIM 모델 반입 — nimcli 다운로드에 따로 둔다.
폐쇄망 반입이 실제로 필요한지는 CoreDNS 로 huggingface.co, api.ngc.nvidia.com 등을 차단한 뒤 Model Hub 에서 import 를 눌러 확인했다. NIM SDK 가 api.ngc.nvidia.com 으로 매니페스트를 받으러 나가다 DNS 실패로 죽었으므로, 카탈로그는 목록일 뿐이고 가중치는 반입해야 한다. 노드의 /etc/hosts 를 고쳐도 파드는 CoreDNS 를 쓰기 때문에 검증이 되지 않는다.
# Ranger 가 있으면 cm_ozone 정책에 작업 계정의 s3v 볼륨 read/list/create/write 를 먼저 부여
kinit -kt /cdep/keytabs/om.keytab om # OM 키탭으로 인증 (scm 계정 권한 문제 회피)
# 버킷은 반드시 OBJECT_STORE(OBS) 레이아웃으로 만든다
ozone sh bucket create --layout OBJECT_STORE /s3v/<bucket>
ozone sh bucket info /s3v/<bucket> | grep -i layout # bucketLayout: OBJECT_STORE
# S3 게이트웨이 엔드포인트: CM → Ozone → S3 Gateway Web UI (예: http://<ozone-host>:9878)
# S3 자격증명 — 처음 실행 시 생성되고 이후에는 같은 값을 반환한다
ozone s3 getsecret --om-service-id=<ozone.om.service.ids 값>
aws configure # awsAccessKey / awsSecret 입력, region·format 은 비워도 된다
aws s3 ls --endpoint-url http://<ozone-host>:9878
--om-service-id 는 ozone.om.service.ids 에 정의된 값이어야 하며 문서 예시의 ozone1 을 그대로 쓰면 Service ID specified does not match 오류가 난다. Hue 등 GUI 로 만든 버킷은 FSO(File System Optimized) 가 되어 모델 경로의 콜론(:) 을 키 이름으로 쓰지 못하고 PutObject 가 500 으로 실패하므로 CLI 로 OBS 를 명시한다. AI Registry 가 자동 생성하는 ai-registry 버킷이 FSO 로 만들어지는 환경이면, CM → Ozone → ozone.default.bucket.layout 을 OBJECT_STORE 로 바꾸고 Ozone 을 재시작한 뒤 Registry 를 다시 만드는 것이 가장 깔끔하다(기본값 변경은 새 버킷에만 적용되므로 기존 FSO 버킷과 Registry 는 먼저 지운다).
python3.10 -m venv venv && source venv/bin/activate
pip install "huggingface_hub[cli]" # click 8.4.2 이상 유지 — 8.2.1 로 내리면 hf 가 깨진다
pip install awscli==1.35.0 requests
hf version
git clone https://github.com/cloudera/Model-Hub.git # import_to_airgap.py 와 manifests/, models/airgapped/*.yaml
python import_to_airgap.py --configure
--configure 에서는 다운로드 경로, cloud provider = pvc(온프레미스 Ozone. aws 를 고르면 실제 Amazon S3 로 향한다), endpoint = http://<ozone-host>:9878, 목적지 = s3://<bucket>/secured-models 를 넣고 토큰은 비워 둔다. 설정은 ~/.airgap/config.json 에 평문으로 저장되므로 토큰은 런타임에 환경 변수로만 준다. 소스 빌드 Python 이면 libffi-devel 없이 빌드된 경우 _ctypes 가 없어 hf 가 죽으니 python -c "import ctypes" 로 먼저 확인한다. HF 모델만 받더라도 스크립트는 NGC CLI 존재를 검사하므로 /opt/ngc-cli 에 NGC CLI 를 두고 PATH 에 넣어 둔다.
# 다운로드 (-do = download only). 게이트 모델(Llama-Guard 등)은 HF 승인이 필요하다
python import_to_airgap.py -do -rt hf -ri BAAI/bge-m3
# → <download_path>/hf/BAAI/bge-m3/artifacts/ 와 metadata/metadata.json 이 생긴다
# 업로드 (Ozone 에 가까운 호스트에서 하는 편이 빠르다)
python import_to_airgap.py -rt hf -ri BAAI/bge-m3
aws s3 ls s3://<bucket>/secured-models/hf/ --endpoint-url http://<ozone-host>:9878 --recursive
python import_to_airgap.py --list-local-models # 로컬에 받아 둔 모델 목록
여러 개를 올릴 때는 for id in BAAI/bge-m3 Qwen/Qwen3.6-27B ...; do python import_to_airgap.py -rt hf -ri "$id"; done 로 돌린다. 업로드에는 nimcli · NGC CLI 가 필요 없고 aws cli 와 S3 자격증명만 있으면 된다. 업로드 중 500 이 나면 버킷 레이아웃(FSO)과 Ozone 데이터노드 디스크 여유부터 본다.
Ready 이고 도메인이 발급됐는지 확인한다. Registered Models 화면에서만 통신 오류가 나면 Registry 도메인 라우팅(HTTPRoute/xlistenerset, Ambient Istio) 문제이며 UI 든 API 든 같은 경로를 타므로 환경 복구 전에는 등록이 되지 않는다.s3://<bucket>/secured-models) 지정. 이 옵션이 폐쇄망 정식 경로이며, 체크하지 않으면 외부 저장소로 나간다. API 로 등록할 때는 metadata.model_repo_type: "HF" 와 remoteObjectStoragePath, repo_id: hf/<org>/<model> 를 준다.SOURCE: HuggingFace, STATUS: Ready 이면 Deploy Model 로 엔드포인트를 만든다. bge-m3 같은 임베딩 모델은 Task 를 embedding, 리랭커는 rerank 로 지정한다.model of format PYTORCH_PYTHON is not supported for text generating models 는 모델이 HF 저장소가 아니라 MLflow PyTorch 형식으로 등록됐다는 뜻이다. Model Hub 경유로 다시 등록하거나 model_repo_type: HF 를 명시한다. 서빙 이미지가 파일을 스캔하므로 pytorch_model.bin 만 있는 모델은 safetensors 형식으로 변환해 올려야 한다.gpt-oss-120b 처럼 min_vram 이 GPU 합계를 넘는 모델은 H100 2장으로 배포되지 않는다. 배포 전에 프로파일의 min_vram_per_device_gb 를 확인한다.0/3 nodes are available: Insufficient cpu / didn't match node affinity / untolerated taint 는 노드마다 사유가 다르다. kubectl describe node <gpu-node> | grep -A8 "Allocated resources" 로 GPU 노드의 남은 CPU 를 보고, 서빙 파드의 nodeSelector 와 GPU 노드 taint 를 대조한다.secured-models/<repo_type>/<repo_id> 인지 한 개만 먼저 올려 확인한 뒤 나머지를 돌린다. 스크립트가 dst 뒤에 / 를 더 붙여 secured-models//hf/... 처럼 슬래시가 겹쳐도 S3 도구는 무시하지만 등록 단계에서 경로를 한 번 더 확인한다.