플랫폼은 OCI 컨테이너 레지스트리(Docker Registry HTTP API V2)를 앱 안에 내장하고 있습니다. 앱과 같은 주소의 /v2 로 열려 있어서 docker · containerd · podman 이 별도 레지스트리 서버 없이 push · pull 합니다. 학습 런타임 이미지, 서빙 이미지, 반입한 협력사 이미지가 모두 여기에 저장됩니다.

토큰 발급

  1. 오른쪽 위 아바타 → 계정 설정컨테이너 토큰 으로 갑니다. 모든 멤버가 발급할 수 있습니다.
  2. 이름과 만료 일수를 정하고 발급 을 누릅니다. ① 토큰은 이때 한 번만 보이고, ② 이 서버의 호스트와 사용자명이 채워진 docker login 명령이 함께 나옵니다.
    컨테이너 토큰 발급 직후: ① 토큰은 이때 한 번만 보인다 ② 호스트가 채워진 docker login 명령
판정
pull (GET · HEAD)발급한 사람이 그 테넌트에서 VIEW 이상
push (그 밖의 모든 요청)발급한 사람이 그 테넌트에서 DEVELOPER 이상
테넌트발급할 때의 테넌트가 토큰에 고정됩니다. X-Tenant 헤더는 쓰지 않습니다

권한은 요청마다 다시 확인합니다. VIEWER 도 토큰을 받을 수 있지만 그 토큰으로는 pull 만 됩니다. 발급한 사람의 역할이 나중에 바뀌면 이미 발급한 토큰에도 곧바로 반영됩니다.

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"]}

HTTP 로 열린 레지스트리 (insecure registry)

레지스트리가 HTTPS 뒤에 있으면 설정할 것이 없습니다. 플랫폼이 HTTP 로만 열려 있다면 클라이언트가 HTTP 를 허용하도록 클라이언트 쪽에서 설정해야 합니다. localhost 는 Docker 가 예외로 허용하므로 설정 없이 됩니다.

/etc/docker/daemon.json 에 호스트를 넣고 Docker 를 재시작합니다.

{ "insecure-registries": ["mlops.example.com:10000"] }
sudo systemctl restart docker

노드 쪽 설정은 보통 설치할 때 운영자가 합니다. 설치 를 보세요.

공유 네임스페이스 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 태그만 받을 수 있습니다. 공유 이미지를 누가 무엇을 받는지는 시스템 관리자가 정합니다. 관리자 가이드 를 보세요.

반입(tar 업로드)으로는 mlops/ 에 넣을 수 없습니다. 반입은 언제나 자기 테넌트 공간으로 들어갑니다.

문제 해결

증상원인과 조치
http: server gave HTTP response to HTTPS clientHTTP 레지스트리인데 클라이언트가 HTTPS 로 시도. 위 insecure 설정
unauthorized / 401토큰이 틀렸거나 만료·회수됨. 다시 발급
push 가 403 insufficient permission토큰을 발급한 사람이 그 테넌트에서 DEVELOPER 미만
pull 이 not found다른 테넌트의 이미지이거나, 공유받지 않은 mlops/ 태그, 또는 정말 없음
레이어 push 가 중간에 끊김앞단 프록시의 본문 크기·시간 제한. 운영자에게 알리거나, 파일로 넘겨 화면에서 반입

2026-09-21 기준 플랫폼에 맞춰 작성했습니다.

© Geo-MLOps