문서 목차
컨테이너 이미지 레지스트리 (Harbor / Nexus)
보안 취약점 스캐닝 및 이미지 서명을 지원하는 기업 전용 컨테이너 이미지 레지스트리 를 구성합니다.
QUANTUM C&S
보안 취약점 스캐닝 및 이미지 서명을 지원하는 기업 전용 컨테이너 이미지 레지스트리를 구성합니다.
레지스트리 선택 (택 1)
qks_harbor_enabled: true — 취약점 스캐닝(Trivy)/이미지 서명(Cosign) 내장. 검증 완료.qks_nexus_enabled: true + qks_nexus_proxy_enabled: true — 범용 아티팩트 저장소(Docker/Maven/npm 등 다목적), Docker 레지스트리 기능만 사용. 검증 진행 중.inventory/qks/group_vars/all/all-k8s.yaml에서 원하는 쪽을 활성화한 뒤, 아래 A/B 중 그 옵션에 해당하는 섹션만 읽고 진행하면 됩니다.
A. Harbor
설정값
inventory/qks/group_vars/all/all-k8s.yaml:
qks_harbor_enabled: true
qks_harbor_version: 1.19.2
qks_harbor_app_version: 2.15.2
qks_harbor_release: qks-harbor
qks_harbor_namespace: qks-harbor
qks_harbor_fqdn_core: "registry.{{ kube_default_domain }}"
qks_harbor_pass: "{{ kube_default_passwd }}"
qks_harbor_project: qks
qks_harbor_registry_storage_type: s3 # s3 / filesystem
qks_harbor_database_type: internal # internal / external
실행
./install.sh ubuntu plays/qks-harbor.yaml
설치 흐름
./install.sh ubuntu plays/qks-harbor.yaml
├─ 1. 접속 도메인을 각 노드 /etc/hosts에 등록
├─ 2. Helm 저장소 등록 ('harbor')
├─ 3. (S3 스토리지 사용 시) 클러스터 CA 인증서를 Secret으로 생성 (caBundleSecretName)
├─ 4. Helm으로 Harbor 배포 (core+portal+registry+jobservice+database+redis+trivy)
├─ 5. (S3 스토리지 사용 시) registry/core 배포에 S3 엔드포인트 hostAlias 패치
├─ 6. Ingress에 Traefik 어노테이션 패치
├─ 7. (내장 DB 사용 시) DB Service를 NodePort로 패치
└─ 8. 기본 프로젝트('qks') 생성
설치 확인
kubectl get pods -n <qks_harbor_namespace>
qks-harbor-core · qks-harbor-portal · qks-harbor-registry(2replica) · qks-harbor-jobservice · qks-harbor-database · qks-harbor-redis · qks-harbor-trivy 파드가 모두 Running이어야 합니다.
실제 동작 확인
웹 접속:
curl -k -s -o /dev/null -w "%{http_code}\n" https://<qks_harbor_fqdn_core>/
200이 나오면 정상입니다.
이미지 push (crane 등 OCI 표준 클라이언트 사용):
crane auth login <qks_harbor_fqdn_core> -u admin -p <qks_harbor_pass>
crane copy <원본 이미지> <qks_harbor_fqdn_core>/<qks_harbor_project>/<이미지명>:<태그>
push가 성공하면 정상입니다.
보안 취약점 스캐닝(Trivy):
curl -k -s -u "admin:<qks_harbor_pass>" -X POST \
"https://<qks_harbor_fqdn_core>/api/v2.0/projects/<프로젝트>/repositories/<이미지명>/artifacts/<태그>/scan"
curl -k -s -u "admin:<qks_harbor_pass>" \
"https://<qks_harbor_fqdn_core>/api/v2.0/projects/<프로젝트>/repositories/<이미지명>/artifacts/<태그>?with_scan_overview=true"
scan_overview의 scan_status가 Success이고 severity/summary에 실제 취약점 통계가 나오면 정상입니다.
이미지 서명(Cosign):
cosign generate-key-pair
cosign sign --key cosign.key <qks_harbor_fqdn_core>/<프로젝트>/<이미지명>@<digest>
cosign verify --key cosign.pub <qks_harbor_fqdn_core>/<프로젝트>/<이미지명>@<digest>
curl -k -s -u "admin:<qks_harbor_pass>" \
"https://<qks_harbor_fqdn_core>/api/v2.0/projects/<프로젝트>/repositories/<이미지명>/artifacts/<태그>/accessories"
cosign verify가 서명 검증에 성공하고, accessories 조회 결과에 type: "signature.cosign" 항목이 있으면 정상입니다.
B. Nexus
설정값
inventory/qks/group_vars/all/all-k8s.yaml:
qks_nexus_enabled: true
qks_nexus_proxy_enabled: true # Docker 레지스트리(프록시/호스티드/그룹 레포) 기능 활성화 — 이게 꺼지면 UI만 뜨고 레지스트리 기능은 전혀 설정되지 않음
qks_nexus_version: 64.2.0 # Helm 차트 버전
qks_nexus_app_version: 3.96.3 # Nexus 앱(이미지 태그) 버전
qks_nexus_fqdn: "nexus.{{ kube_default_domain }}"
qks_nexus_push_fqdn: "push.{{ kube_default_domain }}"
qks_nexus_storage_type: s3 # s3 / filesystem
qks_nexus_storage_class: "{{ qks_block_storage_class_name }}" # PVC용 StorageClass(보통 Ceph) — Nexus 자체 데이터 저장용이며 storage_type과 별개
qks_nexus_storage_class를 Local Path 프로비저너(GS 2.5)의 StorageClass로 임시 지정할 수 있습니다. 이건 PVC가 바인딩될 StorageClass가 반드시 존재해야 하기 때문이며, 정상적인 배포에서는 별도 지정 없이 기본값(Ceph)이 그대로 사용됩니다.
실행
./install.sh ubuntu plays/qks-nexus.yaml
설치 흐름
./install.sh ubuntu plays/qks-nexus.yaml
├─ 1. 접속 도메인을 각 노드 /etc/hosts에 등록
├─ 2. Helm 저장소 등록 ('sonatype')
├─ 3. Helm으로 Nexus 배포
└─ 4. (qks_nexus_proxy_enabled=true인 경우) 프록시 초기 설정
├─ Service에 Docker용 포트(8181=프록시, 8182=push용 호스티드) 추가
├─ 초기 admin 비밀번호 확인 후 관리 비밀번호로 변경
├─ EULA 동의, 익명 접속 활성화
├─ PyPI/R/Conda/APT/Raw 프록시 · 호스티드 · 그룹 레포 생성
├─ Docker 프록시(외부 레지스트리 미러)/호스티드(사내 이미지 push용)/그룹 레포 생성
└─ DockerToken 인증 Realm 활성화
설치 확인
kubectl get pods -n <qks_nexus_namespace> -l app.kubernetes.io/name=nexus-repository-manager
kubectl get pvc -n <qks_nexus_namespace> | grep nexus
qks-nexus 파드가 Running, PVC가 Bound 상태여야 합니다.
실제 동작 확인
API 헬스체크:
NEXUS_IP=$(kubectl get svc qks-nexus -n <qks_nexus_namespace> -o jsonpath='{.spec.clusterIP}')
curl -su admin:<qks_nexus_admin_pass> http://$NEXUS_IP:8081/service/rest/v1/system/eula
curl -su admin:<qks_nexus_admin_pass> http://$NEXUS_IP:8081/service/rest/v1/security/anonymous
curl -su admin:<qks_nexus_admin_pass> http://$NEXUS_IP:8081/service/rest/v1/security/realms/active
"accepted":true(EULA), "enabled":true(익명 접속), 응답에 DockerToken이 포함(Realm)되면 정상입니다.
이미지 push/pull (Docker 호스티드 레포를 통해):
docker login <qks_nexus_push_fqdn> -u admin -p <qks_nexus_admin_pass>
docker tag <원본 이미지> <qks_nexus_push_fqdn>/<이미지명>:<태그>
docker push <qks_nexus_push_fqdn>/<이미지명>:<태그>
docker pull <qks_nexus_push_fqdn>/<이미지명>:<태그>
push/pull이 모두 성공하면 정상입니다.