컨테이너 레지스트리 직접 사용
플랫폼 내장 OCI 레지스트리의 docker login · push · pull, HTTP 레지스트리 설정, 테넌트 격리와 공유 네임스페이스
필요 권한: 조회
플랫폼은 OCI 컨테이너 레지스트리(Docker Registry HTTP API V2)를 앱에 내장. 앱과 같은 주소의 /v2 경로로 제공되므로 docker · containerd · podman 이 별도 레지스트리 서버 없이 push · pull 가능. 학습 런타임 이미지, 서빙 이미지, 반입한 협력사 이미지 모두 여기에 저장.
토큰 발급
- 오른쪽 위 아바타 → 계정 설정 → 컨테이너 토큰 관리 로 이동. 모든 멤버 발급 가능.
- 이름과 만료 일수(기본 90일, 만료 없이 사용하려면 무기한) 지정 후 발급 클릭. ① 토큰은 이때 한 번만 표시되고, ② 이 서버의 호스트와 사용자명이 채워진
docker login명령도 함께 표시.컨테이너 토큰 발급 직후: ① 이때 한 번만 표시되는 토큰 ② 호스트가 채워진 docker login 명령
| 판정 | |
|---|---|
pull (GET · HEAD) | 발급자가 그 테넌트에서 VIEW 이상 |
| push (그 밖의 모든 요청) | 발급자가 그 테넌트에서 DEVELOPER · OPERATOR · ADMIN |
| 테넌트 | 발급 시의 테넌트가 토큰에 고정. X-Tenant 헤더는 사용하지 않음. |
권한은 요청마다 다시 확인. VIEWER 도 토큰 발급은 가능하지만 그 토큰으로는 pull 만 가능. 발급자의 역할이 나중에 변경되면 이미 발급한 토큰에도 즉시 반영. 목록에는 ADMIN 이면 테넌트 전체, 그 밖에는 자기 토큰만 표시되고, 회수는 발급자나 ADMIN 이 수행.
API 로도 발급 가능.
curl -sS -X POST "$API/api/v1/registry/tokens" \
-H "Authorization: Bearer $JWT" -H "X-Tenant: DEMO" \
-H 'Content-Type: application/json' -d '{"name": "ci-push", "expires_in_days": 7}'
# -> {"id": "…", "token": "<secret>", "tenant": "DEMO", "username": "demo", "registry_host": "mlops.example.com"}
로그인 · push · pull
echo "<발급한 토큰>" | docker login mlops.example.com -u demo --password-stdin
docker tag example/mnist-trainer:v1 mlops.example.com/demo/example/mnist-trainer:v1
docker push mlops.example.com/demo/example/mnist-trainer:v1
docker pull mlops.example.com/demo/example/mnist-trainer:v1
- 사용자명 제한 없음. 토큰이 비밀번호. 식별하기 쉽도록 테넌트 코드(소문자) 권장.
- 이미지 주소에는
http://같은 스킴을 포함하지 않음. 항상호스트[:포트]/경로:태그형식. - 저장소는 테넌트별로 완전히 분리. 경로의 첫 구간은 이름일 뿐이고, 어떤 이름으로 push 하든 토큰의 테넌트 공간에 저장. 관례상 첫 구간에 테넌트 코드를 지정하면(
demo/…) 화면에서 반입한 이미지와 경로 형식 일치. - 다른 테넌트의 이미지는 존재하지 않는 것으로 처리(
not found). "있지만 권한 없음" 과 "없음" 을 구분할 수 없도록 의도적으로 404 반환.
목록은 표준 API 로 조회.
curl -u "demo:<토큰>" https://mlops.example.com/v2/_catalog
curl -u "demo:<토큰>" https://mlops.example.com/v2/demo/example/mnist-trainer/tags/list
# {"name":"demo/example/mnist-trainer","tags":["v1"]}
_catalog 에는 자기 테넌트 저장소만 표시. 공유받은 mlops/… 이미지는 목록에 없어도 태그를 알면 pull 가능.
이미지 삭제
/v2 는 삭제 요청을 지원하지 않아 docker 로는 삭제 불가. 화면의 컨테이너 레지스트리에서 삭제하거나, 개발DEVELOPER · OPERATOR · ADMIN 권한으로 REST API 호출.
curl -sS -X DELETE -H "Authorization: Bearer $JWT" -H "X-Tenant: DEMO" \
"$API/api/v1/registry/repositories/demo/example/mnist-trainer/tags/v1"
# {"name":"demo/example/mnist-trainer","tag":"v1","repository_deleted":true}
- 마지막 태그를 삭제하면 저장소도 삭제. 저장소 전체를 삭제하려면
DELETE /api/v1/registry/repositories/{name}호출. - 학습 런타임 변형, 모델 이미지, 배포, 서빙처럼 플랫폼이 그 이미지를 사용 중이면
409,detail.references에 참조하는 대상 포함. - 공유받은
mlops/…이미지는 삭제 불가.
HTTP 로 제공되는 레지스트리 (insecure registry)
레지스트리가 HTTPS 로 제공되면 별도 설정 불필요. 플랫폼이 HTTP 로만 제공되면 클라이언트가 HTTP 를 허용하도록 클라이언트 측에서 설정 필요. localhost 는 Docker 가 예외로 허용하므로 설정 불필요.
/etc/docker/daemon.json 에 호스트를 추가하고 Docker 재시작.
{ "insecure-registries": ["mlops.example.com:10000"] }sudo systemctl restart dockerpodman login --tls-verify=false mlops.example.com:10000 -u demo
podman push --tls-verify=false mlops.example.com:10000/demo/example/mnist-trainer:v1학습·서빙 파드가 이미지를 내려받는 노드에 설정. /etc/rancher/k3s/registries.yaml:
mirrors:
"mlops.example.com:10000":
endpoint: ["http://mlops.example.com:10000"]
configs:
"mlops.example.com:10000":
tls:
insecure_skip_verify: true저장 후 k3s 재시작(sudo systemctl restart k3s, 에이전트 노드는 k3s-agent).
노드 측 설정은 보통 설치 시 운영자가 수행. 절차는 설치 참고.
공유 네임스페이스 mlops
경로가 mlops/ 로 시작하는 저장소는 예약된 공유 네임스페이스. 시스템 관리자가 학습 런타임 · 데이터셋 스테이저 · 서빙 빌더 이미지를 한 번 빌드해 여러 테넌트에 공유할 때 사용.
| 테넌트 토큰 | 플랫폼 토큰(시스템 관리자) | |
|---|---|---|
mlops/… pull | 공유받은 태그만 가능. 공유받지 않은 태그·저장소는 not found. | 모두 |
mlops/… push | 거부(shared namespace is read-only for tenant tokens) | 가능 |
| 자기 테넌트 공간 | 가능 | 없음 |
즉 공유는 태그 단위. 같은 mlops/training-runtime 저장소라도 테넌트 A 는 yolo-cuda 태그만, 테넌트 B 는 rfdetr-cpu 태그만 pull 가능. 어느 테넌트에 어떤 공유 이미지를 제공할지는 시스템 관리자가 결정(관리자 가이드).
반입(tar 업로드)으로는 mlops/ 에 저장 불가. 반입한 이미지는 항상 자기 테넌트 공간에 저장.
문제 해결
| 증상 | 원인과 조치 |
|---|---|
http: server gave HTTP response to HTTPS client | HTTP 레지스트리인데 클라이언트가 HTTPS 로 시도. 위 insecure 설정 |
unauthorized / 401 | 토큰이 잘못되었거나 만료·회수됨. 다시 발급 |
push 가 403 insufficient permission | 토큰을 발급한 사람에게 그 테넌트의 개발DEVELOPER · OPERATOR · ADMIN 권한이 없음(VIEWER · APPROVER) |
push 가 denied: shared namespace is read-only for tenant tokens | 경로가 mlops/ 로 시작함. 첫 구간을 다른 이름으로 변경 |
레이어 push 가 413 BLOB_UPLOAD_INVALID | 레이어 하나가 서버 상한(기본 10 GiB, GEO_MLOPS_REGISTRY_MAX_BLOB_BYTES)을 초과함 |
pull 이 not found | 다른 테넌트의 이미지이거나, 공유받지 않은 mlops/ 태그, 또는 실제로 없음 |
| 레이어 push 가 중간에 중단됨 | 앞단 프록시의 본문 크기·시간 제한. 운영자에게 문의하거나, 파일로 저장해 화면에서 반입 |