Markdown Preview
프라이빗 레지스트리 구축으로 안전하고 효율적인 Docker 이미지 관리하기
Frontmatter
Docker Private Registry를 구축하여 내부에서 안전하게 Docker 이미지를 관리하는 방법을 설명합니다. htpasswd를 사용한 인증 설정, SSL/TLS 적용, 이미지 Push/Pull 방법, Registry API V2를 통한 이미지 조회 방법을 다루며, 트러블슈팅 및 주의사항도 포함되어 있습니다. 프로덕션 환경에서는 SSL을 필수로 적용하고, Self-Signed 인증서를 사용할 경우 클라이언트에 인증서를 등록해야 합니다.
회사에서 프로젝트를 진행하다 보면 Docker 이미지를 빌드하고 배포하는 일이 일상이 됩니다.
문제는 이 이미지들을 Docker Hub 같은 외부 레지스트리에 올리기 곤란한 경우가 꽤 있다는 거죠. 소스 코드가 포함된 이미지라든가, 사내 전용 설정이 들어간 이미지라든가... 보안 정책상 외부 업로드가 금지된 환경도 많습니다.
그래서 필요한 게 바로 Docker Private Registry입니다. 로컬 네트워크에 나만의 이미지 저장소를 만들어서, 외부 유출 걱정 없이 이미지를 관리할 수 있습니다.
이번 글에서는 Registry 설치부터 인증 설정, SSL 적용, 이미지 Push/Pull, 그리고 API를 통한 이미지 조회까지 한 번에 정리해보겠습니다.
이 글에서 다루는 내용
- Docker Private Registry란 무엇인가
- htpasswd 기반 인증(Authentication) 설정
- Registry 컨테이너 실행
- SSL/TLS 인증서 적용으로 HTTPS 통신 구성
- 이미지 Push/Pull 방법
- Registry HTTP API V2로 이미지 목록 조회
- 트러블슈팅 및 주의사항
🤔 Docker Private Registry란?
Docker Registry는 Docker 이미지를 저장하고 배포하기 위한 서버 애플리케이션입니다. Docker Hub가 대표적인 퍼블릭 레지스트리이며, Private Registry는 이를 자체 인프라에서 운영하는 것입니다.
쉽게 말해서, 내 서버에 Docker Hub를 하나 띄우는 것이라고 생각하면 됩니다. Docker에서 공식적으로
registry 이미지를 제공하고 있어서, 컨테이너 하나만 띄우면 바로 사용할 수 있습니다.Private Registry를 사용하는 이유
- 보안: 소스 코드가 포함된 이미지를 외부에 노출하지 않음
- 속도: 로컬 네트워크에서 Pull/Push하므로 Docker Hub보다 훨씬 빠름
- 네트워크 제한 환경: 인터넷 접근이 제한된 폐쇄망에서도 이미지 배포 가능
- 비용: Docker Hub 무료 플랜은 Private Repository 수에 제한이 있음
🔐 인증(Authentication) 설정
Registry를 아무나 접근할 수 있게 열어두면... 보안팀에게 혼나기 딱 좋습니다.
htpasswd를 사용하여 HTTP Basic Authentication을 설정합니다.인증 파일 생성
먼저 인증 정보를 저장할 디렉토리를 만들고,
htpasswd 파일을 생성합니다.mkdir authhttpd 이미지에 포함된 htpasswd 유틸리티를 사용하여 사용자 인증 파일을 생성합니다:docker run --entrypoint htpasswd httpd:2 -Bbn admin <CHANGE_REGISTRY_PASSWORD> > auth/htpasswd명령어 해설
--entrypoint htpasswd: httpd 컨테이너의 기본 엔트리포인트 대신htpasswd명령을 실행
-B: bcrypt 알고리즘으로 암호화 (가장 강력한 옵션)
-b: 커맨드라인에서 비밀번호를 직접 전달
-n: 결과를 stdout으로 출력 (파일에 직접 쓰지 않음)
admin: 사용자 이름
<CHANGE_REGISTRY_PASSWORD>: 비밀번호 (반드시 변경하세요!)
registry:2.7.1 이후 버전 주의!
Registry 이미지 2.7.1 이후 버전에서는 컨테이너 내부에
htpasswd가 포함되지 않아 registry 이미지로 직접 인증 파일을 생성할 수 없습니다. 위처럼 httpd:2 이미지를 사용하거나, 호스트에 apache2-utils를 설치하여 사용하세요:# Ubuntu/Debian
sudo apt-get install apache2-utils
htpasswd -Bbn admin <CHANGE_REGISTRY_PASSWORD> > auth/htpasswd사용자를 추가하고 싶다면
>>(append)를 사용합니다:docker run --entrypoint htpasswd httpd:2 -Bbn developer <CHANGE_DEVELOPER_PASSWORD> >> auth/htpasswd생성된 파일을 확인해보면 이런 형태입니다:
cat auth/htpasswd
# admin:$2y$05$...해시된 비밀번호...
# developer:$2y$05$...해시된 비밀번호...🚀 Registry 컨테이너 실행
기본 실행 (HTTP + 인증)
인증 파일이 준비되었으면 Registry 컨테이너를 띄웁니다:
docker run -d -p 5000:5000 --restart=always --name registry \
-v "$(pwd)"/auth:/auth \
-v registry_data:/var/lib/registry \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:latest옵션 상세 해설
-d: 백그라운드(데몬) 모드로 실행
-p 5000:5000: 호스트의 5000번 포트를 컨테이너의 5000번 포트에 매핑
--restart=always: Docker 데몬이 재시작되어도 자동으로 컨테이너를 시작
-v "$(pwd)"/auth:/auth: 호스트의 auth 디렉토리를 컨테이너에 마운트
-v registry_data:/var/lib/registry: 이미지 데이터를 Docker Volume에 영구 저장 (이게 없으면 컨테이너 삭제 시 모든 이미지가 사라집니다)
REGISTRY_AUTH_HTPASSWD_REALM: 인증 창에 표시될 이름
REGISTRY_AUTH_HTPASSWD_PATH: 컨테이너 내부의 htpasswd 파일 경로
실행 확인:
docker ps | grep registry
# 예상 출력:
# CONTAINER ID IMAGE ... STATUS PORTS NAMES
# abc123def456 registry:latest ... Up 3 seconds 0.0.0.0:5000->5000/tcp registry로그인 테스트
docker login localhost:5000
# Username: admin
# Password: <CHANGE_REGISTRY_PASSWORD>
# Login SucceededLogin Succeeded가 나오면 인증 설정이 정상적으로 된 것입니다. 🎉🔒 SSL/TLS 적용 (HTTPS)
HTTP로만 운영하면 네트워크 상에서 인증 정보가 평문으로 노출됩니다. 해커의 밥상 차려주는 격이죠. 프로덕션 환경에서는 반드시 SSL/TLS를 적용해야 합니다.
인증서 준비
SSL 인증서는 두 가지 방법으로 준비할 수 있습니다:
방법 | 장점 | 단점 | 적합한 환경 |
Let's Encrypt | 무료, 브라우저 신뢰 | 도메인 필요, 갱신 필요 | 외부 접근 가능한 서버 |
Self-Signed | 즉시 생성 가능, 도메인 불필요 | 클라이언트에 인증서 등록 필요 | 내부망, 폐쇄망 |
Self-Signed 인증서 생성
내부 환경에서는 자체 서명 인증서를 사용하는 경우가 많습니다:
# 인증서 저장 디렉토리 생성
mkdir -p auth/ssl
# 자체 서명 인증서 생성 (유효기간 365일)
openssl req -newkey rsa:4096 -nodes -sha256 \
-keyout auth/ssl/server.key \
-x509 -days 365 \
-out auth/ssl/server.crt \
-subj "/C=KR/ST=Seoul/L=Seoul/O=MyCompany/CN=registry.example.com" \
-addext "subjectAltName=DNS:registry.example.com,IP:172.30.0.11"인증서 생성 옵션 설명
-newkey rsa:4096: 4096비트 RSA 키 생성
-nodes: 개인키를 암호화하지 않음 (Registry가 자동으로 읽을 수 있도록)
-sha256: SHA-256 해시 알고리즘 사용
-days 365: 인증서 유효기간 365일
-subj: 인증서 주체 정보 (환경에 맞게 수정)
-addext "subjectAltName=...": SAN(Subject Alternative Name) 추가. Docker 클라이언트가 이 필드를 확인하므로 반드시 설정해야 합니다
SSL 적용하여 Registry 실행
기존 Registry를 중지하고, SSL이 적용된 새 컨테이너를 실행합니다:
# 기존 컨테이너 중지 및 삭제
docker stop registry
docker rm registry# SSL 적용된 Registry 실행
docker run -d -p 5000:5000 --restart=always --name registry \
-v "$(pwd)"/auth:/auth \
-v registry_data:/var/lib/registry \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/auth/ssl/server.crt \
-e REGISTRY_HTTP_TLS_KEY=/auth/ssl/server.key \
registry:latest기존 실행 명령어에 TLS 관련 환경변수 두 줄만 추가된 것입니다:
-e REGISTRY_HTTP_TLS_CERTIFICATE=/auth/ssl/server.crt \
-e REGISTRY_HTTP_TLS_KEY=/auth/ssl/server.key중요! 인증서 파일(
server.crt, server.key)은 반드시 컨테이너 내부 경로를 기준으로 작성해야 합니다. 위 예시에서는 호스트의 auth/ssl/ 디렉토리가 컨테이너의 /auth/ssl/로 마운트되므로 /auth/ssl/server.crt로 지정합니다.클라이언트에 인증서 등록 (Self-Signed 사용 시)
자체 서명 인증서를 사용하면 Docker 클라이언트가 인증서를 신뢰하지 않아 Push/Pull이 실패합니다. 클라이언트 머신에 인증서를 등록해야 합니다:
# 인증서 복사 (registry.example.com을 실제 주소로 변경)
sudo mkdir -p /etc/docker/certs.d/registry.example.com:5000
sudo cp server.crt /etc/docker/certs.d/registry.example.com:5000/ca.crt
# Docker 데몬 재시작
sudo systemctl restart docker디렉토리 구조
/etc/docker/certs.d/ 하위에 <레지스트리주소>:<포트>/ 디렉토리를 만들고, 그 안에 ca.crt 파일을 넣으면 됩니다. Docker 데몬이 이 디렉토리를 자동으로 탐색합니다./etc/docker/certs.d/
└── registry.example.com:5000/
└── ca.crt또는 시스템 전역으로 인증서를 신뢰하도록 등록할 수도 있습니다:
# Ubuntu/Debian
sudo cp server.crt /usr/local/share/ca-certificates/registry.crt
sudo update-ca-certificates
# CentOS/RHEL
sudo cp server.crt /etc/pki/ca-trust/source/anchors/registry.crt
sudo update-ca-trust📦 이미지 Push / Pull
Registry가 준비되었으니 실제로 이미지를 올리고 내려받아 봅시다.
이미지 Push
# 1. 로그인
docker login registry.example.com:5000
# 2. 기존 이미지에 Registry 태그 추가
docker tag my-app:latest registry.example.com:5000/my-app:latest
# 3. Push
docker push registry.example.com:5000/my-app:latest태그 규칙
Private Registry에 Push하려면 이미지 이름 앞에 Registry 주소를 붙여야 합니다:
<registry-host>:<port>/<image-name>:<tag>Docker Hub에 Push할 때
<username>/<image>를 쓰는 것과 같은 원리입니다.이미지 Pull
다른 머신에서 이미지를 내려받으려면:
# 1. 로그인
docker login registry.example.com:5000
# 2. Pull
docker pull registry.example.com:5000/my-app:latestlocalhost 사용 시 (같은 서버에서)
Registry가 돌아가는 같은 서버에서는
localhost로 접근할 수 있습니다:docker tag my-app:latest localhost:5000/my-app:latest
docker push localhost:5000/my-app:latestlocalhost로 접근하는 경우에는 Docker가 SSL 검증을 건너뛰기 때문에 Self-Signed 인증서 등록 없이도 바로 사용할 수 있습니다. 하지만 이건 같은 서버에서만 동작하는 예외이므로, 다른 머신에서 접근할 때는 반드시 인증서를 등록해야 합니다.🔍 Registry API로 이미지 조회
Docker Private Registry는 HTTP API V2를 제공합니다. 이 API를 통해 저장된 이미지 목록이나 태그 정보를 조회할 수 있습니다.
저장된 이미지(Repository) 목록 조회
curl -u admin:<CHANGE_REGISTRY_PASSWORD> -k \
https://registry.example.com:5000/v2/_catalog응답 예시:
{
"repositories": [
"my-app",
"nginx-custom",
"python-ml"
]
}특정 이미지의 태그 목록 조회
curl -u admin:<CHANGE_REGISTRY_PASSWORD> -k \
https://registry.example.com:5000/v2/my-app/tags/list응답 예시:
{
"name": "my-app",
"tags": [
"latest",
"v1.0.0",
"v1.1.0"
]
}이미지 Manifest 조회
특정 태그의 상세 정보(레이어, 다이제스트 등)를 확인하려면:
curl -u admin:<CHANGE_REGISTRY_PASSWORD> -k \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
https://registry.example.com:5000/v2/my-app/manifests/latestAPI 주요 엔드포인트 정리
엔드포인트 | 설명 |
GET /v2/_catalog | 전체 Repository 목록 |
GET /v2/<name>/tags/list | 특정 이미지의 태그 목록 |
GET /v2/<name>/manifests/<ref> | 이미지 Manifest 조회 |
DELETE /v2/<name>/manifests/<digest> | 이미지 삭제 |
GET /v2/ | API 버전 확인 (Health Check 용도) |
Health Check
Registry가 정상 동작하는지 간단히 확인하려면:
curl -k https://registry.example.com:5000/v2/
# 정상 응답: {}🐳 Docker Compose로 관리하기 (선택사항)
매번 긴
docker run 명령어를 치는 건 솔직히 고문입니다. Docker Compose를 사용하면 설정을 파일로 관리할 수 있어서 훨씬 편합니다.# docker-compose.yml
version: '3.8'
services:
registry:
image: registry:latest
container_name: registry
restart: always
ports:
- "5000:5000"
volumes:
- ./auth:/auth
- registry_data:/var/lib/registry
environment:
REGISTRY_AUTH_HTPASSWD_REALM: "Registry Realm"
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_HTTP_TLS_CERTIFICATE: /auth/ssl/server.crt
REGISTRY_HTTP_TLS_KEY: /auth/ssl/server.key
volumes:
registry_data:# 실행
docker compose up -d
# 중지
docker compose down
# 로그 확인
docker compose logs -f registry🔧 트러블슈팅
x509: certificate signed by unknown authority
Self-Signed 인증서를 사용할 때 가장 흔한 에러입니다.
# 해결: 클라이언트에 인증서 등록
sudo mkdir -p /etc/docker/certs.d/<registry-host>:5000
sudo cp server.crt /etc/docker/certs.d/<registry-host>:5000/ca.crt
sudo systemctl restart dockerhttp: server gave HTTP response to HTTPS client
Docker 클라이언트가 HTTPS로 접속하려는데 Registry가 HTTP로만 동작하는 경우입니다.
SSL을 설정하지 않은 상태에서 사용하려면 Docker 데몬에 insecure registry를 등록해야 합니다:
# /etc/docker/daemon.json
{
"insecure-registries": ["registry.example.com:5000"]
}sudo systemctl restart dockerinsecure-registries는 테스트 환경에서만 사용하세요! 이 설정은 SSL 검증을 완전히 비활성화하므로, 프로덕션 환경에서는 반드시 SSL을 적용하는 것을 권장합니다.
Push/Pull이 느린 경우
Docker는 기본적으로 이미지 레이어를 동시에 5개까지 업로드합니다. 대역폭이 제한된 환경에서는 이를 조절할 수 있습니다:
# /etc/docker/daemon.json
{
"max-concurrent-uploads": 2,
"max-concurrent-downloads": 3
}이미지 삭제가 안 되는 경우
Registry의 기본 설정에서는 이미지 삭제 API가 비활성화되어 있습니다. 삭제를 허용하려면 환경변수를 추가합니다:
-e REGISTRY_STORAGE_DELETE_ENABLED=true삭제 후에는 가비지 컬렉션을 실행하여 실제 디스크 공간을 회수해야 합니다:
docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml핵심 정리
✅ Docker Private Registry: 공식
registry 이미지로 컨테이너 하나만 띄우면 내부 이미지 저장소 완성✅ htpasswd 인증:
httpd:2 이미지의 htpasswd 유틸리티로 사용자 인증 파일 생성✅ SSL/TLS 필수: 프로덕션 환경에서는 반드시 SSL을 적용하여 인증 정보 보호
✅ Self-Signed 인증서: 내부망에서는 자체 서명 인증서 사용 가능, 클라이언트에
ca.crt 등록 필요✅ Registry API V2:
/v2/_catalog로 이미지 목록, /v2/<name>/tags/list로 태그 조회✅ Docker Compose: 설정을 YAML로 관리하면 유지보수가 훨씬 편리
주의사항
⚠️ 볼륨 마운트 필수:
-v registry_data:/var/lib/registry 없이 실행하면 컨테이너 삭제 시 모든 이미지 데이터 소실⚠️ insecure-registries 지양: 테스트 환경에서만 사용, 프로덕션에서는 SSL 적용
⚠️ 인증서 SAN 설정: Self-Signed 인증서 생성 시
subjectAltName을 반드시 포함⚠️ 비밀번호 관리: htpasswd 파일에 기록된 비밀번호는 bcrypt로 해시되지만, 파일 자체의 접근 권한을 제한하세요
⚠️ 인증서 갱신: Self-Signed는 유효기간, Let's Encrypt는 90일마다 갱신 필요