동작하는 Docker Engine·Compose plugin, sudo 권한과 콘솔 복구 경로
CONTAINER REGISTRY · 설치·설정 레시피
사설 Docker Registry·TLS·인증 구성
CNCF Distribution Registry 3 이미지를 검증된 digest로 고정하고, 전용 데이터 경로·CA 서명 TLS·bcrypt htpasswd·사설 IP 바인딩·CIDR 방화벽을 적용한 뒤 로그인·push·pull과 데이터 백업 기준을 검증합니다.BEFORE YOU START
시작 전에 준비하세요
사설 Registry DNS 이름·고정 사설 IP·허용할 빌드/배포 클라이언트 CIDR
공인 CA 또는 조직 CA 서버 인증서·개인 키·CA 체인과 정확한 SAN
이미지 데이터 백업·복원과 보존 정책, 저장 용량 경보 계획
권장 대상 사설망 CI/CD와 서버에서 사용할 인증된 컨테이너 이미지 저장소를 직접 운영하는 개발자·운영자
FOLLOW THE RECIPE
8단계 구성·점검 레시피
Docker 호스트와 Registry 버전 기준 확인
Docker와 Compose 버전, 호스트 자원, 현재 컨텍스트를 확인합니다. Registry 이미지 태그만 신뢰하지 않고 실제 digest를 변경 기록에 고정합니다.
cat /etc/os-releasedocker version
docker compose versiondocker context show
docker infodf -hT /srv /var/lib/docker- 의도한 운영 Docker 컨텍스트에 연결되어 있다.
- Registry 데이터와 Docker 런타임에 충분한 별도 용량이 있다.
- 고정할 Registry 3 이미지 digest의 승인 절차가 있다.
원격 Docker 컨텍스트나 개발용 데몬이면 잘못된 호스트에 배포할 수 있습니다. 컨텍스트와 대상 호스트를 먼저 고정합니다.
기존 443 포트·컨테이너·데이터와 인증서를 점검합니다.
기존 Registry·포트·인증서 사전 점검
동일 이름 컨테이너나 기존 이미지 데이터가 있는지 확인합니다. 기존 배포를 덮어쓰지 않고 소유 팀과 백업·복원 상태를 확인합니다.
sudo ss -ltnp | grep ':443 'docker ps -a --filter name=private-registrysudo find /srv/registry -maxdepth 2 -mindepth 1 -printopenssl verify -CAfile <REGISTRY_CA_CERT> <REGISTRY_SERVER_CERT>
openssl x509 -in <REGISTRY_SERVER_CERT> -noout -subject -issuer -dates -ext subjectAltNamesudo ufw status numbered- 443을 다른 서비스가 사용하지 않거나 승인된 프록시 설계가 있다.
- 기존 /srv/registry 데이터가 있으면 배포 소유자와 형식을 확인했다.
- 인증서 SAN에 <REGISTRY_PRIVATE_DNS>가 있고 CA 검증이 성공한다.
- 기존 Registry가 있다면 pull 복원 시험과 백업이 있다.
기존 데이터나 컨테이너가 보이면 신규 설치가 아니라 마이그레이션입니다. 같은 데이터 경로에 서로 다른 Registry 인스턴스를 동시에 연결하지 않습니다.
공식 Registry 이미지를 내려받아 digest를 확인합니다.
Registry 3 이미지 digest 확인
공식 registry:3 이미지를 가져오고 RepoDigest를 검토해 Compose 파일에 고정합니다. 암호 파일 생성에 사용할 공식 httpd 이미지도 버전 태그를 고정합니다.
docker pull registry:3docker image inspect registry:3 --format '{{json .RepoDigests}}'docker pull httpd:2docker image inspect httpd:2 --format '{{json .RepoDigests}}'- Registry와 httpd 이미지가 공식 저장소에서 내려왔다.
- 두 이미지의 RepoDigest를 변경 기록에 적었다.
- Registry Compose에는 확인한 digest를 사용한다.
태그는 가리키는 이미지가 바뀔 수 있습니다. 운영 재현성과 공급망 검토를 위해 digest를 고정하고 정기 갱신은 별도 변경으로 처리합니다.
전용 경로·TLS·bcrypt 인증과 Compose 파일을 준비합니다.
데이터·TLS·bcrypt 인증과 Compose 구성
TLS가 준비된 상태에서만 Basic 인증을 사용합니다. htpasswd는 bcrypt 형식으로 대화형 생성하고, 서버 키와 인증 파일 접근을 제한합니다.
date -u +%Y%m%dT%H%M%SZsudo test ! -f /srv/registry/compose.yaml || sudo cp --preserve=all --no-clobber /srv/registry/compose.yaml /srv/registry/compose.yaml.<BACKUP_SUFFIX>.baksudo 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/datasudo 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.crtsudo 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/htpasswdsudoedit /srv/registry/compose.yamlsudo docker compose -f /srv/registry/compose.yaml config --quietservices:
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 응답을 확인합니다.
Registry 시작과 컨테이너 상태 확인
Compose 구성 검사가 성공한 뒤 서비스를 시작합니다. 실패하면 컨테이너를 반복 재생성하지 말고 마운트와 첫 로그 오류를 확인합니다.
sudo docker compose -f /srv/registry/compose.yaml config --quietsudo docker compose -f /srv/registry/compose.yaml up -dsudo docker compose -f /srv/registry/compose.yaml pssudo docker compose -f /srv/registry/compose.yaml logs --tail 120 registrysudo ss -ltnp | grep ':443 '- private-registry 컨테이너가 실행 중이다.
- 로그에 인증 파일·TLS 키·데이터 권한 오류가 없다.
- 443은 지정한 사설 IP에서만 리슨한다.
permission denied는 bind mount의 호스트 권한부터 확인합니다. 인증이나 TLS를 빼서 기동시키는 방식으로 우회하지 않습니다.
무인증 거부, 로그인, push·pull을 승인 클라이언트에서 검증합니다.
401 인증 요구와 push·pull 검증
보호된 Registry는 무인증 /v2/ 요청에 401과 인증 challenge를 반환해야 합니다. CIDR 규칙을 추가한 후 Docker login 프롬프트와 승인된 테스트 이미지로 실제 왕복을 확인합니다.
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 헤더가 기대 결과입니다.sudo ufw allow from <REGISTRY_CLIENT_CIDR> to <REGISTRY_PRIVATE_IP> port 443 proto tcp현재 SSH 세션·콘솔과 상위 ACL을 유지한 채 같은 CIDR로 제한합니다.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 login <REGISTRY_PRIVATE_DNS>프롬프트로 자격 증명을 입력합니다. 공용·다중 사용자 호스트에서는 Docker credential helper를 구성해 평문 설정 파일 저장을 피합니다.docker tag <APPROVED_TEST_IMAGE> <REGISTRY_PRIVATE_DNS>/verification/<CHANGE_ID>:1
docker push <REGISTRY_PRIVATE_DNS>/verification/<CHANGE_ID>:1docker pull <REGISTRY_PRIVATE_DNS>/verification/<CHANGE_ID>:1sudo ufw status numbered- 무인증 /v2/ 요청이 401과 인증 challenge를 반환한다.
- 클라이언트는 서버 인증서를 정확한 DNS와 CA로 검증한다.
- 승인 CIDR에서 로그인·push·별도 pull이 모두 성공한다.
- 비승인 네트워크에서는 443에 접근할 수 없다.
x509 오류는 클라이언트 CA 경로·SAN·체인 문제입니다. Docker daemon의 보안 검증을 약화시키지 말고 신뢰 체인을 바로잡습니다.
계정·보존·백업·스캔과 인증서 갱신 운영을 마감합니다.
계정·보존·백업·공급망 운영 점검
내장 htpasswd는 간단한 인증에 적합하지만 프로젝트별 세밀한 권한은 제공하지 않습니다. 팀별 권한이 필요하면 검증된 토큰 서비스나 별도 제품을 설계합니다.
docker inspect private-registry --format '{{.Image}}'sudo du -sh /srv/registry/data
df -hT /srv/registry/dataopenssl x509 -checkend 2592000 -noout -in /srv/registry/certs/domain.crtsudo docker compose -f /srv/registry/compose.yaml logs --since 24h registry | grep -Ei 'unauthorized|denied|error'- 공용 계정 대신 사용자·자동화별 자격 증명을 발급하고 교체한다.
- 프로젝트별 권한이 필요하면 Basic 인증의 한계를 문서화하고 토큰 기반 권한을 검토한다.
- 승인된 이미지 digest·서명·취약점 검사 정책을 배포 파이프라인에 둔다.
- 데이터 경로 백업과 별도 호스트 pull 복원 시험을 정기 실행한다.
- 인증서 만료·디스크 용량·오류율·고정 이미지 갱신을 모니터링한다.
Registry에 push할 수 있다는 사실이 이미지가 신뢰 가능함을 뜻하지는 않습니다. 빌드 출처, digest, 서명, 검사와 배포 승인 체계를 함께 운영합니다.
데이터를 보존하면서 Compose와 방화벽을 되돌릴 절차를 확인합니다.
Compose 복원과 접근 경로 롤백
push를 중단하고 상위 ACL을 먼저 닫습니다. 컨테이너를 중지한 뒤 이번 변경의 Compose 백업을 명시적으로 선택해 복원하며 Registry 데이터는 삭제하지 않습니다.
sudo docker compose -f /srv/registry/compose.yaml stop registrysudo find /srv/registry -maxdepth 1 -type f -name 'compose.yaml.*.bak' -printf '%TY-%Tm-%Td %TH:%TM:%TS %p\n' | sortsudo test -f /srv/registry/compose.yaml.<BACKUP_SUFFIX>.bak기존 설치 복원 분기입니다. 변경 기록으로 이번 작업 직전 백업인지 확인합니다.sudo cp --preserve=all --no-clobber /srv/registry/compose.yaml /srv/registry/compose.yaml.failed.<ROLLBACK_SUFFIX>sudo cp --preserve=all /srv/registry/compose.yaml.<BACKUP_SUFFIX>.bak /srv/registry/compose.yamlsudo 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 파일이 없던 신규 설치인 경우에만 다음 명령을 사용합니다.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
공식 문서
설치 저장소와 지원 버전은 바뀔 수 있습니다. 검토일 이후에는 링크된 공식 문서와 현재 서버의 패키지 후보 버전을 함께 확인하세요.