Harbor 레지스트리 구성하기 (feat. Helm Chart 연동)

2025. 5. 26. 04:10·[DevOps] - Kubernetes

개요


클라우드 혹은 퍼블릭 이미지 레지스트리로의 도커 이미지 형상 관리는 네트워크 비용(혹은 기타 이미지 저장 비용)이 청구되기 때문에 프라이빗 레지스트리를 운영하는 사례가 있다. 물론 반드시 비용뿐만 아니라 보안을 위해서라도 온프레미스 환경에서 관리하기 위한 경우도 포함이다. (필자는 저번에 구현한 라즈베리 파이 쿠버네티스 클러스터에서 운영중..)
 

ArtifactHub에서도 oci:// (OCI 프로토콜)를 통해 Helm Chart의 형상을 관리하는 모습

 
본 포스팅에서는 OCI (Open Container Initiative) 레지스트리인 Harbor의 배포 및 도커 레지스트리로써의 사용법을 공유한다. 기본적으로 OCI 프로토콜을 따르기 때문에 Helm Chart 레지스트리로써도 활용 가능하다.
 


 

Harbor Helm Chart 구성 과정


# Bitnami Helm Chart Repository 등록
$ helm repo add bitnami https://charts.bitnami.com/bitnami

# bitnami 레포지토리로부터 harbor Helm Chart Pull 과 동시에 압축해제
$ helm pull bitnami/harbor --version 25.0.0 --untar

# 원본 values.yaml은 유지한 상태로 오버라이딩할 설정값 파일 복사
$ cd harbor && cp values.yaml custom-values.yaml
  • 본 포스팅에서 Harbor는 25.0.0 버전의 Helm Chart를 기준으로 설명한다.

 

storageClass 설정 (좌 : 수정 후 / 우 : 수정 전)
## @param global.imageRegistry Global Docker image registry
## @param global.imagePullSecrets Global Docker registry secret names as an array
## @param global.defaultStorageClass Global default StorageClass for Persistent Volume(s)
## @param global.storageClass DEPRECATED: use global.defaultStorageClass instead
##
global:
  imageRegistry: ""
  ## E.g.
  ## imagePullSecrets:
  ##   - myRegistryKeySecretName
  ##
  imagePullSecrets: []
  defaultStorageClass: ""
  storageClass: "nfs-client"
  
  ...
  • 우선 Harbor에 저장될 도커 이미지, Helm Chart, 아티팩트 등을 영구적으로 저장할 PV를 프로비저닝하기 위한 준비를 해야 한다. 필자는 RWX PV를 동적으로 프로비저닝하기 위한 NFS 전용 storageClass를 준비해두었기 global.storageClass에 nfs-client를 입력했다. 스토리지 클래스는 일종의 PV를 프로비저닝하기 위한 청사진 역할을 수행한다.
  • 물론 PV를 사용하지 않거나 RWO 전용의 storageClass를 사용할 것이라면 반드시 위처럼 수정해야 하는 것은 아니다.
    • e.g. OpenEBS를 사용한다면 openebs-hostpath 와 같은 스토리지 클래스를 대체할 수도 있다.

 

패스워드 설정 및 노출시킬 도메인 설정
## @param adminPassword The initial password of Harbor admin. Change it from portal after launching Harbor
##
adminPassword: "alpine00!@"
## @param externalURL The external URL for Harbor Core service
## It is used to
## 1) populate the docker/helm commands showed on portal
##
## Format: protocol://domain[:port]. Usually:
## 1) if "exposureType" is "ingress", the "domain" should be
## the value of "ingress.hostname"
## 2) if "exposureType" is "proxy" and "service.type" is "ClusterIP",
## the "domain" should be the value of "service.clusterIP"
## 3) if "exposureType" is "proxy" and "service.type" is "NodePort",
## the "domain" should be the IP address of k8s node
## 4) if "exposureType" is "proxy" and "service.type" is "LoadBalancer",
## the "domain" should be the LoadBalancer IP
##
externalURL: https://192.168.45.242
  • 사실 주석을 잘 읽으면 크게 어려울 부분이 없다! adminPassword는 말그대로 Harbor에 접속하기 위한 관리자의 비밀번호이다. (아이디는 admin이 기본 세팅되어 있음)
  • 두 번째 필드인 externalURL은 Harbor를 배포해보고나면 알지만 Harbor Core 서비스인 harbor-core Pod를 노출시키기 위한 도메인을 지정한다. 즉, 외부로부터 해당 도메인으로 들어온 요청에 대해 리스닝하는 것으로 이해했다.

 

Nginx에 대한 TLS 설정
nginx:

...

  tls:
    ## @param nginx.tls.enabled Enable TLS termination
    ##
    enabled: true
    ## @param nginx.tls.existingSecret Existing secret name containing your own TLS certificates.
    ## The secret must contain the keys:
    ## `tls.crt` - the certificate (required),
    ## `tls.key` - the private key (required),
    ## `ca.crt` - CA certificate (optional)
    ## Self-signed TLS certificates will be used otherwise.
    ##
    existingSecret: ""
    ## @param nginx.tls.commonName Thtlse common name used to generate the self-signed TLS certificates
    ##
    commonName: 192.168.45.242
  • 인증서에 관련된 내용은 해당 포스트를 읽고오면 도움이 된다.
  • Harbor를 향한 트래픽이 곧바로 Harbor Core 서비스로 인입되는 것이 아니라 Nginx 프록시를 통해 거쳐간다.
  • OCI 레지스트리에 대해서 도커 데몬에서 접근할 때, 암호화된 세션을 통해 접근하는 것을 권장하므로 tls를 활성화 시켜주는 편이 좋다. nginx.tls.commonName은 클라이언트(e.g. 도커 데몬)으로부터 Harbor에 접근할 때, 사용할 인증서의 CN 필드에 입력할 값을 명시해주는 역할을 수행한다.
    • 물론 도커 데몬 설정 /etc/docker/daemon.json에서 insecure-registries 설정을 하면 대상 레지스트리에 대해서 인증서와 같은 암호화를 강제하지 않을 수 있다.
  • 하지만, 비용을 최대한 아끼고 개인이 프라이빗 레지스트리를 운영해야 하는 입장에서는 인증서를 CA를 통해 발급받을 의사가 없기 때문에 자체 서명된 인증서(self-signed certificate)를 발급받는다. FM대로 하려면 공인 CA로부터 인증서를 발급받는다.
  • 참고) 온프레미스 환경이라면 MetalLB를 세팅해두는 편이 좋다. 위 192.168.45.242 주소는 MetalLB로 인해 할당된 LoadBalancer 타입의 서비스 객체의 External IP이다.

 

$ docker login 192.168.45.242 -u admin            

i Info → A Personal Access Token (PAT) can be used instead.
         To create a PAT, visit https://app.docker.com/settings
         
         
Password: 
time="2025-05-26T03:05:37+09:00" level=info msg="Error logging in to endpoint, trying next endpoint" error="Get \"https://192.168.45.242/v2/\": tls: failed to verify certificate: x509: “core.harbor.domain” certificate is not trusted"
Get "https://192.168.45.242/v2/": tls: failed to verify certificate: x509: “core.harbor.domain” certificate is not trusted
  • 만약 nginx.tls.commonName을 externalURL에서 노출한 도메인으로 지정하지 않는 경우, 인증서에서 보호하고자 하는 대상 도메인(CN)이 클라이언트에서 접근하고자 하는 호스트와 일치하지 않으므로 신뢰하지 않아 접근할 수 없게 된다.
  • 참고) SAN 필드를 통해 단일 인증서에서 멀티 도메인에 대해 등록이 가능하다.

 

$ helm install harbor . -f custom-values.yaml -n harbor --create-namespace
  • 이제 최종적으로 오버라이딩한 custom-values.yaml을 통해 Harbor Helm Release가 배포되는 것을 기다리자~..

 


 

Harbor 레지스트리 구성 및 도커 이미지 Push


프로젝트 생성
  • Harbor가 배포되고나서 어드민으로 로그인하고나면 새로운 프로젝트를 생성해주어야 한다. Harbor에서 프로젝트 내에서 복수개의 레지스트리 단위로 아티팩트가 관리된다.

 

  • 비어있는 레지스트리를 생성하고 나면 [레지스트리 인증서] 버튼을 클릭하여 해당 레지스트리에 대한 ca.crt 인증서 파일을 다운로드 받을 수 있다.

 

# 도커 데몬에 인증서를 등록할 디렉터리를 생성
# e.g. mkdir -p ~/.docker/certs.d/192.168.45.242
$ mkdir -p ~/.docker/certs.d/<레지스트리 도메인>

# 생성된 경로에 레지스트리 인증서 이동
$ mv ~/Downloads/ca.crt ~/.docker/certs.d/<레지스트리 도메인>/
  • 위에서 언급한대로 도커 데몬으로부터 접근할 레지스트리에 대해 인증서를 등록해주는 과정이 필요하다. (insecure-registries는 임시방편)

 

# 기존에 빌드된 도커 이미지에 대해 푸시할 레지스트리 도메인이 포함되도록 태깅
$ docker tag <레지스트리>:<태그> <레지스트리 도메인>/<프로젝트 이름>/<레지스트리>:<태그>

# Harbor 레지스트리 로그인 (인증서 세팅 필수)
$ docker login <레지스트리 호스트> -u <사용자명>

# 로그인한 레지스트리에 태깅된 이미지 푸시
$ docker push <레지스트리 도메인>/<프로젝트 이름>/<레지스트리>:<태그>

# 예시
✘ shawn 🦁 ~/manifests/clusters/microk8s/harbor main  docker push 192.168.45.242/sample/airflow:2.8.0
The push refers to repository [192.168.45.242/sample/airflow]
e815c424807b: Pushed 
4f4fb700ef54: Pushed 
0b6c97b67717: Pushed 
fb6b878cd61a: Pushed 
d4119e243fd8: Pushed 
4585119f7367: Pushed 
21907deef3fc: Pushed 
03199f53d176: Pushed 
b0c9ce5602f8: Pushing [==============>                                    ]  57.67MB/198.3MB
b50d7e3cba32: Pushed 
cf2ae8de2555: Pushed 
30708f193ee5: Pushed 
a2c89e4b0507: Pushing [==================================================>]  71.96MB/71.96MB
0f6a848285d8: Pushed 
df77943e8fbc: Pushed 
9d1b14756508: Pushed 
24e221e92a36: Pushed 
6b9513afbd99: Pushed 
eba5d43ae6b3: Pushed 
d6ab1c6c4d4b: Pushed 
abf0bfe6fbab: Pushed 
ed9d1836a632: Pushed 
07605975c1c1: Pushed 
79b482f0125e: Pushed
  • 이제 도커 이미지를 Harbor 프로젝트 내의 레지스트리에 푸시하기만 하면 된다. 레지스트리는 별도로 생성할 필요없이 푸시하면 프로젝트 내에 자동으로 레지스트리가 생성되며 아티팩트가 등록된다.

 

레지스트리 생성 및 이미지 푸시 완료




 


 

(참고) Helm Chart에서 Harbor 레지스트리에 있는 이미지 Pull


https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/

Pull an Image from a Private Registry

This page shows how to create a Pod that uses a Secret to pull an image from a private container image registry or repository. There are many private registries in use. This task uses Docker Hub as an example registry. 🛇 This item links to a third party

kubernetes.io

  • 사실 쿠버네티스에서 프라이빗 레지스트리로부터 이미지를 Pull 하기 위한 공식 문서가 있긴 하다.
  • 하지만, 이 글에서 다루는 케이스는 Harbor로부터 저장된 이미지를 Pull 하여 쿠버네티스 Helm Chart에서 사용하는 것이 목적이다. 이 목적을 달성하기 위해 해야할 것은 크게 2가지가 있다.
    • 레지스트리에 대한 접속 정보를 Kubernetes Secret 객체로 생성 (imagePullSecret으로 활용)
    • 클러스터의 각 노드에 레지스트리 인증서 복사 (TLS)

 

$ kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>
  • 공식 문서에 따르면, 이미지를 프라이빗 레지스트리로부터 Pull 하기위해 해당 레지스트리에 대한 접속 정보를 Secret 객체로 생성해야 한다.

 

$ kubectl create secret docker-registry regcred \
  --docker-server=<레지스트리 도메인> \
  --docker-username=<사용자명> \
  --docker-password=<비밀번호> \
  -n <Helm Chart를 배포할 네임스페이스>
  • 하지만, Harbor에는 email을 입력할 것이 없기 때문에 위 정보만 입력해도 충분하다.

 

Airflow Helm Chart에서 Harbor 레지스트리 이미지 Pull 하는 예시
  • Helm Chart 마다 이미지 Pull을 설정하는 방식은 다르겠지만, 결국 레지스트리에 접근하기 위한 접속 정보 Secret을 imagePullSecret으로 설정해주어야 한다. 

 
 

$ k node-shell berry1-desktop
spawning "nsenter-vkov1l" on "berry1-desktop"
If you don't see a command prompt, try pressing enter.

root@berry1-desktop:/# cd /var/snap/microk8s/current/args/certs.d/

root@berry1-desktop:/var/snap/microk8s/current/args/certs.d# mkdir <레지스트리 도메인>

root@berry1-desktop:/var/snap/microk8s/current/args/certs.d# l
<레지스트리 도메인>/  docker.io/  localhost:32000/

root@berry1-desktop:/var/snap/microk8s/current/args/certs.d# vi <레지스트리 도메인>/ca.crt
  • 이후, 위에서 다운로드 받은 Harbor 레지스트리의 인증서를 각 노드에 복사해주면 된다.
    • 필자는 SSH에 접속하기 귀찮아서.. node-shell로 직접 파일 내용 복붙
    • 위 예시는 microk8s 기준(containerd CRI 기본 사용)으로 작성된 예시다. 도커 데몬을 사용한다면 /etc/docker 아래에 certs.d와 같은 경로에 옮기기만 하면된다.

 

Harbor 레지스트리로부터 Pull한 이미지를 정상적으로 Pod에서 컨테이너로 구동한 모습

 

'[DevOps] - Kubernetes' 카테고리의 다른 글

Istio 톺아보기 #2 - 트래픽 관리 관련 CRD  (0) 2025.05.17
Istio 톺아보기 #1 - 개요  (0) 2025.05.17
라즈베리 파이로 홈 쿠버네티스 클러스터 구축 매뉴얼  (1) 2025.04.20
쿠버네티스 환경의 서비스 디스커버리  (0) 2024.05.26
'[DevOps] - Kubernetes' 카테고리의 다른 글
  • Istio 톺아보기 #2 - 트래픽 관리 관련 CRD
  • Istio 톺아보기 #1 - 개요
  • 라즈베리 파이로 홈 쿠버네티스 클러스터 구축 매뉴얼
  • 쿠버네티스 환경의 서비스 디스커버리
Bebsae
Bebsae
  • Bebsae
    뱁새zip
    Bebsae
  • 전체
    오늘
    어제
    • 분류 전체보기 (108)
      • [DevOps] - Kubernetes (5)
      • [DevOps] - AWS (1)
      • [AI] - Machine Learning (19)
      • [AI] - Neural Network (7)
      • [CS] - Network (2)
      • [CS] - Data Structure (3)
      • [CS] - Design Pattern (6)
      • [Language] - Python (15)
      • [Library] - Numpy (7)
        • Quick Start (5)
        • API (2)
      • [Framework] - Django (3)
      • [Framework] - QGIS (6)
      • [Framework] - PyQT (4)
      • [Mathematics] - Linear Alge.. (14)
      • [Mathematics] - Statistical (2)
      • [ETC] - Python (3)
      • [ETC] - C++ (1)
      • [ETC] - Linux (1)
      • 논문 (5)
      • 회고록 (3)
      • 생산성 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

  • 인기 글

  • 태그

    교차검증
    Linear
    algebra
    Python
    Convolution
    선형대수
    신경망
    디자인패턴
    파이썬
    decomposition
    머신러닝
    QGIS
    RNN
    DEEPLEARNING
    linearalgebra
    Learning
    분해
    numpy
    Machine
    MachineLearning
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Bebsae
Harbor 레지스트리 구성하기 (feat. Helm Chart 연동)
상단으로

티스토리툴바