Haru Utils

CONTAINER REGISTRY · 설치·설정 레시피

사설 Docker Registry·TLS·인증 구성

CNCF Distribution Registry 3 이미지를 검증된 digest로 고정하고, 전용 데이터 경로·CA 서명 TLS·bcrypt htpasswd·사설 IP 바인딩·CIDR 방화벽을 적용한 뒤 로그인·push·pull과 데이터 백업 기준을 검증합니다.
Docker Registry 설치사설 레지스트리Registry TLSDocker htpasswd컨테이너 이미지 저장소
지원 환경Ubuntu Server 24.04 LTS
예상 시간55
난이도중급
검토일2026-08-28

BEFORE YOU START

시작 전에 준비하세요

01

동작하는 Docker Engine·Compose plugin, sudo 권한과 콘솔 복구 경로

02

사설 Registry DNS 이름·고정 사설 IP·허용할 빌드/배포 클라이언트 CIDR

03

공인 CA 또는 조직 CA 서버 인증서·개인 키·CA 체인과 정확한 SAN

04

이미지 데이터 백업·복원과 보존 정책, 저장 용량 경보 계획

권장 대상 사설망 CI/CD와 서버에서 사용할 인증된 컨테이너 이미지 저장소를 직접 운영하는 개발자·운영자

FOLLOW THE RECIPE

8단계 구성·점검 레시피

1
확인시스템을 변경하지 않는 점검 단계

Docker 호스트와 Registry 버전 기준 확인

Docker와 Compose 버전, 호스트 자원, 현재 컨텍스트를 확인합니다. Registry 이미지 태그만 신뢰하지 않고 실제 digest를 변경 기록에 고정합니다.

운영체제
cat /etc/os-release
Docker·Compose
docker version
docker compose version
Docker 컨텍스트
docker context show
docker info
스토리지 여유
df -hT /srv /var/lib/docker
  • 의도한 운영 Docker 컨텍스트에 연결되어 있다.
  • Registry 데이터와 Docker 런타임에 충분한 별도 용량이 있다.
  • 고정할 Registry 3 이미지 digest의 승인 절차가 있다.
결과 읽기

원격 Docker 컨텍스트나 개발용 데몬이면 잘못된 호스트에 배포할 수 있습니다. 컨텍스트와 대상 호스트를 먼저 고정합니다.

다음 판단

기존 443 포트·컨테이너·데이터와 인증서를 점검합니다.

2
확인시스템을 변경하지 않는 점검 단계

기존 Registry·포트·인증서 사전 점검

동일 이름 컨테이너나 기존 이미지 데이터가 있는지 확인합니다. 기존 배포를 덮어쓰지 않고 소유 팀과 백업·복원 상태를 확인합니다.

443 포트 점유
sudo ss -ltnp | grep ':443 '
기존 Registry 컨테이너
docker ps -a --filter name=private-registry
기존 디렉터리
sudo find /srv/registry -maxdepth 2 -mindepth 1 -print
인증서 검증
openssl verify -CAfile <REGISTRY_CA_CERT> <REGISTRY_SERVER_CERT>
openssl x509 -in <REGISTRY_SERVER_CERT> -noout -subject -issuer -dates -ext subjectAltName
방화벽 현재 상태
sudo ufw status numbered
  • 443을 다른 서비스가 사용하지 않거나 승인된 프록시 설계가 있다.
  • 기존 /srv/registry 데이터가 있으면 배포 소유자와 형식을 확인했다.
  • 인증서 SAN에 <REGISTRY_PRIVATE_DNS>가 있고 CA 검증이 성공한다.
  • 기존 Registry가 있다면 pull 복원 시험과 백업이 있다.
결과 읽기

기존 데이터나 컨테이너가 보이면 신규 설치가 아니라 마이그레이션입니다. 같은 데이터 경로에 서로 다른 Registry 인스턴스를 동시에 연결하지 않습니다.

다음 판단

공식 Registry 이미지를 내려받아 digest를 확인합니다.

3
변경패키지·설정·서비스 상태가 달라지는 단계

Registry 3 이미지 digest 확인

공식 registry:3 이미지를 가져오고 RepoDigest를 검토해 Compose 파일에 고정합니다. 암호 파일 생성에 사용할 공식 httpd 이미지도 버전 태그를 고정합니다.

변경 단계입니다. 대상 서버, 백업 파일, 서비스 중단 영향과 바로 이전 상태로 돌아가는 방법을 다시 확인하세요.
Registry 이미지 가져오기
docker pull registry:3
Registry digest 확인
docker image inspect registry:3 --format '{{json .RepoDigests}}'
htpasswd 도구 이미지
docker pull httpd:2
htpasswd 이미지 digest 확인
docker image inspect httpd:2 --format '{{json .RepoDigests}}'
  • Registry와 httpd 이미지가 공식 저장소에서 내려왔다.
  • 두 이미지의 RepoDigest를 변경 기록에 적었다.
  • Registry Compose에는 확인한 digest를 사용한다.
결과 읽기

태그는 가리키는 이미지가 바뀔 수 있습니다. 운영 재현성과 공급망 검토를 위해 digest를 고정하고 정기 갱신은 별도 변경으로 처리합니다.

다음 판단

전용 경로·TLS·bcrypt 인증과 Compose 파일을 준비합니다.

4
주의권한·연결·중단 영향을 확인할 단계

데이터·TLS·bcrypt 인증과 Compose 구성

TLS가 준비된 상태에서만 Basic 인증을 사용합니다. htpasswd는 bcrypt 형식으로 대화형 생성하고, 서버 키와 인증 파일 접근을 제한합니다.

고유 백업 접미사
date -u +%Y%m%dT%H%M%SZ
기존 Compose 백업
sudo test ! -f /srv/registry/compose.yaml || sudo cp --preserve=all --no-clobber /srv/registry/compose.yaml /srv/registry/compose.yaml.<BACKUP_SUFFIX>.bak
전용 디렉터리 생성
sudo install -d -o root -g root -m 0750 /srv/registry /srv/registry/auth /srv/registry/certs
sudo install -d -o root -g root -m 0750 /srv/registry/data
TLS 파일 배치
sudo install -o root -g root -m 0644 <REGISTRY_SERVER_CERT> /srv/registry/certs/domain.crt
sudo install -o root -g root -m 0600 <REGISTRY_SERVER_KEY> /srv/registry/certs/domain.key
sudo install -o root -g root -m 0644 <REGISTRY_CA_CERT> /srv/registry/certs/ca.crt
bcrypt 사용자 대화형 생성
sudo docker run --rm -it \
  --mount type=bind,src=/srv/registry/auth,dst=/auth \
  --entrypoint htpasswd httpd:2 \
  -B -c /auth/htpasswd <REGISTRY_USER>
비밀번호는 프롬프트로 입력합니다. 추가 사용자는 -c 없이 같은 방식으로 등록하며 공용 계정을 만들지 않습니다.
인증 파일 권한
sudo chown root:root /srv/registry/auth/htpasswd
sudo chmod 640 /srv/registry/auth/htpasswd
Compose 파일 편집
sudoedit /srv/registry/compose.yaml
Compose 문법 확인
sudo docker compose -f /srv/registry/compose.yaml config --quiet
설정 예시 · /srv/registry/compose.yaml
TLS·bcrypt·사설 IP Registry Compose
services:
  registry:
    image: registry:3@sha256:<VERIFIED_REGISTRY_DIGEST>
    container_name: private-registry
    restart: unless-stopped
    ports:
      - "<REGISTRY_PRIVATE_IP>:443:5000"
    environment:
      REGISTRY_HTTP_ADDR: ":5000"
      REGISTRY_HTTP_TLS_CERTIFICATE: "/certs/domain.crt"
      REGISTRY_HTTP_TLS_KEY: "/certs/domain.key"
      REGISTRY_AUTH: "htpasswd"
      REGISTRY_AUTH_HTPASSWD_REALM: "Private Registry"
      REGISTRY_AUTH_HTPASSWD_PATH: "/auth/htpasswd"
      REGISTRY_STORAGE_DELETE_ENABLED: "false"
    volumes:
      - "/srv/registry/data:/var/lib/registry"
      - "/srv/registry/certs:/certs:ro"
      - "/srv/registry/auth:/auth:ro"
    security_opt:
      - "no-new-privileges:true"
    cap_drop:
      - ALL
<VERIFIED_REGISTRY_DIGEST>는 install 단계에서 확인한 sha256 값으로 교체합니다. 삭제 API가 필요하면 보존·복구·승인 정책을 먼저 설계합니다.
  • htpasswd가 bcrypt 형식이고 사용자별 계정을 사용한다.
  • TLS 개인 키는 root만 읽을 수 있다.
  • Registry 이미지가 검증한 digest로 고정됐다.
  • Compose가 실제 사설 IP의 443에만 게시된다.
결과 읽기

Basic 인증은 TLS 없이는 자격 증명을 보호하지 못합니다. 인증서·DNS·Compose 검증이 모두 끝나기 전에는 서비스를 시작하지 않습니다.

다음 판단

Compose로 시작하고 로그와 TLS 응답을 확인합니다.

5
변경패키지·설정·서비스 상태가 달라지는 단계

Registry 시작과 컨테이너 상태 확인

Compose 구성 검사가 성공한 뒤 서비스를 시작합니다. 실패하면 컨테이너를 반복 재생성하지 말고 마운트와 첫 로그 오류를 확인합니다.

변경 단계입니다. 대상 서버, 백업 파일, 서비스 중단 영향과 바로 이전 상태로 돌아가는 방법을 다시 확인하세요.
구성 재검증
sudo docker compose -f /srv/registry/compose.yaml config --quiet
Registry 시작
sudo docker compose -f /srv/registry/compose.yaml up -d
컨테이너 상태
sudo docker compose -f /srv/registry/compose.yaml ps
최근 로그
sudo docker compose -f /srv/registry/compose.yaml logs --tail 120 registry
443 리스닝
sudo ss -ltnp | grep ':443 '
  • private-registry 컨테이너가 실행 중이다.
  • 로그에 인증 파일·TLS 키·데이터 권한 오류가 없다.
  • 443은 지정한 사설 IP에서만 리슨한다.
결과 읽기

permission denied는 bind mount의 호스트 권한부터 확인합니다. 인증이나 TLS를 빼서 기동시키는 방식으로 우회하지 않습니다.

다음 판단

무인증 거부, 로그인, push·pull을 승인 클라이언트에서 검증합니다.

6
주의권한·연결·중단 영향을 확인할 단계

401 인증 요구와 push·pull 검증

보호된 Registry는 무인증 /v2/ 요청에 401과 인증 challenge를 반환해야 합니다. CIDR 규칙을 추가한 후 Docker login 프롬프트와 승인된 테스트 이미지로 실제 왕복을 확인합니다.

로컬 TLS·인증 challenge
curl --silent --show-error --dump-header - --output /dev/null --cacert /srv/registry/certs/ca.crt https://<REGISTRY_PRIVATE_DNS>/v2/
401, WWW-Authenticate, Docker-Distribution-API-Version 헤더가 기대 결과입니다.
승인 CIDR 방화벽 규칙
sudo ufw allow from <REGISTRY_CLIENT_CIDR> to <REGISTRY_PRIVATE_IP> port 443 proto tcp
현재 SSH 세션·콘솔과 상위 ACL을 유지한 채 같은 CIDR로 제한합니다.
클라이언트 CA 신뢰 경로 준비
sudo install -d -o root -g root -m 0755 /etc/docker/certs.d/<REGISTRY_PRIVATE_DNS>
sudo install -o root -g root -m 0644 <REGISTRY_CA_CERT> /etc/docker/certs.d/<REGISTRY_PRIVATE_DNS>/ca.crt
sudo ls -l /etc/docker/certs.d/<REGISTRY_PRIVATE_DNS>/ca.crt
승인된 각 Docker 클라이언트에서 조직 CA를 배치합니다. 시스템 CA나 Docker의 TLS 검증을 끄지 않습니다.
Docker 대화형 로그인
docker login <REGISTRY_PRIVATE_DNS>
프롬프트로 자격 증명을 입력합니다. 공용·다중 사용자 호스트에서는 Docker credential helper를 구성해 평문 설정 파일 저장을 피합니다.
승인 테스트 이미지 태그와 push
docker tag <APPROVED_TEST_IMAGE> <REGISTRY_PRIVATE_DNS>/verification/<CHANGE_ID>:1
docker push <REGISTRY_PRIVATE_DNS>/verification/<CHANGE_ID>:1
별도 클라이언트에서 pull
docker pull <REGISTRY_PRIVATE_DNS>/verification/<CHANGE_ID>:1
방화벽 확인
sudo ufw status numbered
  • 무인증 /v2/ 요청이 401과 인증 challenge를 반환한다.
  • 클라이언트는 서버 인증서를 정확한 DNS와 CA로 검증한다.
  • 승인 CIDR에서 로그인·push·별도 pull이 모두 성공한다.
  • 비승인 네트워크에서는 443에 접근할 수 없다.
결과 읽기

x509 오류는 클라이언트 CA 경로·SAN·체인 문제입니다. Docker daemon의 보안 검증을 약화시키지 말고 신뢰 체인을 바로잡습니다.

다음 판단

계정·보존·백업·스캔과 인증서 갱신 운영을 마감합니다.

7
주의권한·연결·중단 영향을 확인할 단계

계정·보존·백업·공급망 운영 점검

내장 htpasswd는 간단한 인증에 적합하지만 프로젝트별 세밀한 권한은 제공하지 않습니다. 팀별 권한이 필요하면 검증된 토큰 서비스나 별도 제품을 설계합니다.

실행 이미지 digest
docker inspect private-registry --format '{{.Image}}'
데이터 사용량
sudo du -sh /srv/registry/data
df -hT /srv/registry/data
TLS 만료
openssl x509 -checkend 2592000 -noout -in /srv/registry/certs/domain.crt
최근 인증 오류
sudo docker compose -f /srv/registry/compose.yaml logs --since 24h registry | grep -Ei 'unauthorized|denied|error'
  • 공용 계정 대신 사용자·자동화별 자격 증명을 발급하고 교체한다.
  • 프로젝트별 권한이 필요하면 Basic 인증의 한계를 문서화하고 토큰 기반 권한을 검토한다.
  • 승인된 이미지 digest·서명·취약점 검사 정책을 배포 파이프라인에 둔다.
  • 데이터 경로 백업과 별도 호스트 pull 복원 시험을 정기 실행한다.
  • 인증서 만료·디스크 용량·오류율·고정 이미지 갱신을 모니터링한다.
결과 읽기

Registry에 push할 수 있다는 사실이 이미지가 신뢰 가능함을 뜻하지는 않습니다. 빌드 출처, digest, 서명, 검사와 배포 승인 체계를 함께 운영합니다.

다음 판단

데이터를 보존하면서 Compose와 방화벽을 되돌릴 절차를 확인합니다.

8
주의권한·연결·중단 영향을 확인할 단계

Compose 복원과 접근 경로 롤백

push를 중단하고 상위 ACL을 먼저 닫습니다. 컨테이너를 중지한 뒤 이번 변경의 Compose 백업을 명시적으로 선택해 복원하며 Registry 데이터는 삭제하지 않습니다.

Registry 중지
sudo docker compose -f /srv/registry/compose.yaml stop registry
백업 후보 확인
sudo find /srv/registry -maxdepth 1 -type f -name 'compose.yaml.*.bak' -printf '%TY-%Tm-%Td %TH:%TM:%TS %p\n' | sort
선택 백업 검증
sudo test -f /srv/registry/compose.yaml.<BACKUP_SUFFIX>.bak
기존 설치 복원 분기입니다. 변경 기록으로 이번 작업 직전 백업인지 확인합니다.
현재 Compose 보존
sudo cp --preserve=all --no-clobber /srv/registry/compose.yaml /srv/registry/compose.yaml.failed.<ROLLBACK_SUFFIX>
선택 Compose 복원
sudo cp --preserve=all /srv/registry/compose.yaml.<BACKUP_SUFFIX>.bak /srv/registry/compose.yaml
복원 구성 검증과 시작
sudo docker compose -f /srv/registry/compose.yaml config --quiet && sudo docker compose -f /srv/registry/compose.yaml up -d
기존 설치 복원 분기에서만 실행하고 401 challenge와 기존 이미지 pull을 다시 검증합니다.
신규 설치 분기 확인
sudo test ! -f /srv/registry/compose.yaml.<BACKUP_SUFFIX>.bak
변경 기록상 이전 Compose 파일이 없던 신규 설치인 경우에만 다음 명령을 사용합니다.
신규 Compose 비활성 보존
sudo mv --no-clobber /srv/registry/compose.yaml /srv/registry/compose.yaml.disabled.<ROLLBACK_SUFFIX>
신규 설치 롤백 분기입니다. Registry를 다시 시작하지 않고 데이터·인증서·인증 파일은 보존합니다.
이번 방화벽 규칙 제거
sudo ufw delete allow from <REGISTRY_CLIENT_CIDR> to <REGISTRY_PRIVATE_IP> port 443 proto tcp
  • push를 중단하고 상위 ACL을 닫았다.
  • 복원할 Compose 백업을 변경 기록으로 명시적으로 선택했다.
  • 현재 실패 Compose도 별도로 보존했다.
  • 복원 후 401 challenge와 기존 이미지 pull을 확인했다.
  • /srv/registry/data를 삭제하거나 다른 Registry와 동시에 공유하지 않았다.
결과 읽기

복원 후 이미지가 보이지 않으면 데이터 삭제·초기화를 하지 말고 storage driver, 마운트 경로와 기존 manifest 상태를 확인합니다.

다음 판단

컨테이너 재시작·디스크·TLS·권한 장애 대응 가이드로 이어갑니다.

SECURITY CHECK

운영 전 마지막 보안 점검

  • Registry는 CA 검증 TLS와 bcrypt 인증 없이 외부에 노출하지 않는다.
  • 443은 사설 IP와 승인된 빌드·배포 CIDR만 허용한다.
  • 사용자·자동화별 자격 증명을 분리하고 비밀번호를 명령 인자에 넣지 않는다.
  • 운영 이미지는 digest로 고정하고 서명·취약점 검사·배포 승인을 적용한다.
  • 데이터 백업·pull 복원 시험·디스크·인증서·이미지 버전 갱신을 운영한다.

COMMON ERRORS

자주 막히는 지점

Docker client x509 오류

증상
docker login 또는 pull에서 unknown authority나 hostname mismatch가 발생합니다.
가능한 원인
클라이언트의 certs.d 경로, CA 체인 또는 서버 인증서 SAN이 Registry DNS와 다릅니다.
확인 순서
정확한 DNS 디렉터리에 CA를 배치하고 SAN·체인을 재검증합니다. 검증을 끄지 않습니다.
트러블슈팅으로 이어보기

인증 후에도 401 Unauthorized

증상
로그인은 반복해서 실패하고 Registry 로그에 authorization 오류가 나타납니다.
가능한 원인
htpasswd가 bcrypt가 아니거나 파일 마운트·권한·사용자 이름이 잘못됐습니다.
확인 순서
bcrypt 형식과 컨테이너 내부 인증 파일 경로를 확인하고 대화형으로 사용자 비밀번호를 다시 설정합니다.
트러블슈팅으로 이어보기

push 중 disk full

증상
레이어 업로드가 실패하고 Registry 로그와 호스트에 no space left 오류가 보입니다.
가능한 원인
이미지 보존 정책과 용량 경보 없이 데이터 경로가 가득 찼습니다.
확인 순서
임의 파일 삭제를 중단하고 참조 상태·백업·보존 정책을 확인한 뒤 공식 garbage collection 절차를 별도 유지보수로 수행합니다.
트러블슈팅으로 이어보기

PRIMARY REFERENCES

공식 문서

설치 저장소와 지원 버전은 바뀔 수 있습니다. 검토일 이후에는 링크된 공식 문서와 현재 서버의 패키지 후보 버전을 함께 확인하세요.

도구 빠른 검색

최근 사용한 도구를 다시 열거나, 이름과 기능으로 검색하세요.

검색어와 도구의 입력·결과는 저장하지 않습니다.

↑↓ 이동 · Enter 열기 · Esc 닫기