본문 바로가기
DevOps/Docker

Harbor Private Registry 소개 및 설치

by 비어원 2023. 10. 9.
728x90

Harbor는 CNCF를 졸업한 프로젝트로, 대표적인 사설 레지스트리 (Private Registry) 오픈소스이다.

Private Registry

보통 hub.docker.com에서 제공하거나 오픈소스 프로젝트에서 제공하는 컨테이너 이미지는 인터넷이 되는 모든 곳에서 풀을 받아 사용이 가능하다. 이러한 컨테이너 이미지를 제공해주는 레지스트리를 Public Registry라고 한다.

 

하지만, 회사와 같이 프로젝트가 오픈되지 않아야 하는 환경에서는 회사 환경 내부에서만 접근이 가능해야 하고, 큰 회사의 경우에는 특정 부서에서만 특정 이미지를 푸쉬하거나 풀 할 수 있도록 권한을 제어해야 한다. 이렇게 특정 환경에서만 접근이 가능해야 하는 레지스트리를 Private Registry라고 한다.

 

Harbor

Harbor는 대표적인 Private registry 오픈소스이며, 컨테이너 이미지 저장 외에도 여러가지의 기능을 제공하고 있다.

 

  • 컨테이너 이미지에 대한 보안 및 취약점 분석
  • RBAC (Role based access control)
  • 정책 기반 복제 (Replication)
  • 여러 방식의 인증기능 제공 (LDAP / AD / OIDC)
  • 이미지 삭제 및 가비지 컬렉션을 통해 주기적으로 리소스 확보
  • Web 기반의 GUI
  • 감사(Audit)기능 제공
  • RESTful API 제공
  • docker-compose, helm 등 여러가지 배포 방식 제공

 

Helm으로 설치

Harbor 설치 방법 중 Helm을 사용하여 쿠버네티스로 harbor를 쉽게 설치할 수 있다. 먼저 helm repo를 등록하고 기본 values.yaml을 받자.

# harbor를 배포할 네임스페이스 생성
$ kubectl create ns harbor

# helm repo 등록
$ helm repo add harbor https://helm.goharbor.io

$ curl -O "https://raw.githubusercontent.com/goharbor/harbor-helm/main/values.yaml"

 

values.yaml

일단 기본적으로 설정해줘야 하는 값을 소개하겠다. 나는 이렇게 설정했다.

expose:
  ingress:
    hosts:
      core: harbor.beer1.com # 원하는 도메인으로 설정
    className: "nginx"

externalURL: https://harbor.beer1.com # 원하는 도메인으로 설정

persistence:
  persistentVolumeClaim:
    registry:
      existingClaim: "harbor-registry-local"
      storageClass: ""
    jobservice:
      jobLog:
        existingClaim: "harbor-job-service-local"
        storageClass: ""
    database:
      existingClaim: "harbor-database-local"
      storageClass: ""
    redis:
      existingClaim: "harbor-redis-local"
      storageClass: ""
    trivy:
      existingClaim: "harbor-trivy-local"
      storageClass: ""

harborAdminPassword: "password" # admin 계정 비밀번호
  • expose.ingress.hosts.core, externalURL: Harbor 웹앱에서 사용할 도메인을 설정한다.
  • harborAdminPassword: Harbor 어드민 계정의 비밀번호. 원하는 값으로 설정하자.
  • persistence Persistent Volume을 사용하려면 설정해야 한다. 클러스터마다, 환경마다 사용할 수 있는 볼륨이 다르기 때문에 각자 상황에 따라 맞춰야 한다.
    • 필자는 Virtual Box로 리눅스 OS를 띄워서 컨트롤플레인 1대, 워커노드 두대를 띄웠기 때문에 별도의 스토리지 서버가 없다. 그래서 로컬 스토리지를 사용하려면 PV, PVC를 수동으로 만들어줘야 한다.
    • Cloud에서 제공해주는 스토리지 리소스를 사용한다면 동적 프로비저닝을 활용하면 된다. 각 PVC에 대한 storageClass를 적절한 값으로 설정해주자. 이 경우에는 existingClaim은 빈 값으로 넣어주면 된다.

 

로컬 스토리지를 사용하는 경우

로컬 스토리지를 사용하는 경우 따로 스토리지클래스와 PV, PVC를 생성해줘야 한다.

 

storageClass.yaml

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

 

harbor-pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: harbor-registry-local
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /home/beer1/pv/harbor-registry
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-wn1
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: harbor-job-service-local
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /home/beer1/pv/harbor-job-service
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-wn1
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: harbor-database-local
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /home/beer1/pv/harbor-database
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-wn2
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: harbor-redis-local
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /home/beer1/pv/harbor-redis
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-wn2
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: harbor-trivy-local
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /home/beer1/pv/harbor-trivy
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-wn2

 

harbor-pvc.yaml

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: harbor-registry-local
  namespace: harbor
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: local-storage
  resources:
    requests:
      storage: 5Gi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: harbor-job-service-local
  namespace: harbor
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: local-storage
  resources:
    requests:
      storage: 1Gi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: harbor-database-local
  namespace: harbor
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: local-storage
  resources:
    requests:
      storage: 1Gi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: harbor-redis-local
  namespace: harbor
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: local-storage
  resources:
    requests:
      storage: 1Gi
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: harbor-trivy-local
  namespace: harbor
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: local-storage
  resources:
    requests:
      storage: 5Gi

 

모든 설정을 마쳤다면 helm 배포를 진행하자.

$ helm install harbor harbor/harbor -n harbor -f values.yaml

 

배포에 성공했다면 다음과 같이 파드가 정상적으로 뜰 것이다.

$ kubectl get po -n harbor
NAME                                 READY   STATUS    RESTARTS      AGE
harbor-core-7685fd6885-jlkpw         1/1     Running   0             48m
harbor-database-0                    1/1     Running   0             60m
harbor-jobservice-677c65df7f-g2cvk   1/1     Running   2 (48m ago)   48m
harbor-portal-58fdcfdb69-2xtb8       1/1     Running   0             60m
harbor-redis-0                       1/1     Running   0             60m
harbor-registry-86c647cff8-dvvbz     2/2     Running   0             48m
harbor-trivy-0                       1/1     Running   0             60m

 

Harbor 접속

일단 하버에 접속해보자. 하지만 CA 인증서가 잘 알려지지 않은 기관이기 때문에 다음과 같은 에러가 발생한다.

 

helm을 통해 자동으로 TLS 설정을 했다면 self-signed certificate를 자동으로 생성하여 적용시키는데, 자동으로 생성된 CA 인증서가 잘 알려지지 않은 기관이기 때문에 브라우저에서 접근 시도를 막는다. 이를 해결하려면 해당 인증서를 신뢰하는 인증서로 만들어버리면 되는데 Mac의 경우에는 키체인에 해당 CA 인증서를 추가하면 된다. (윈도우는.. 죄송합니다..)

 

먼저 쿠버네티스 시크릿으로부터 ca 인증서를 추출하자.

$ kubectl get secret harbor-ingress -n harbor -o jsonpath='{.data.ca\.crt}' | base64 -d > ca.crt
$ open .

 

그 다음 키체인 설정을 열고 시스템을 선택한 다음 추출한 ca.crt 파일을 키체인에 드래그한다.

 

그 다음 harbor-ca를 찾은 후 클릭한 다음 인증서를 항상 신뢰로 만들면 끝!

  • 여려 사람이 함께 사용하는 레지스트리인 경우에는 잘 알려진 인증기관에서 만든 인증서 파일을 사용하도록 하자!

 

이제 admin 계정으로 하버에 로그인을 해보자!

 

이미지 푸쉬

하버 로그인에 성공하면 프로젝트가 보일 것이다. 하버에서는 프로젝트 단위로 이미지를 관리하기 때문에 이미지를 저장하기 위해서는 프로젝트를 생성해야 한다. 이미지를 저장할 새 프로젝트를 하나 생성하자.

 

그 다음에는 로컬에서 하버로 이미지를 푸쉬해보자. 그러기 전에 하버는 private registry기 때문에 이미지를 푸쉬하기 전에 로그인을 해야 한다.

$ docker login harbor.beer1.com -u admin
Password:
Login Succeeded

 

로그인에 성공했다면 기존의 이미지를 하버 레지스트리의 이미지로 이름을 변경한 다음 푸쉬를 날려주면 끝이다. 이 떄 이미지 이름은 ${harbor-registry-domain}/${project-name}/${image-name} 으로 하자.

$ docker tag beer1/todo-server-kotlin harbor.beer1.com/app/todo-server-kotlin
$ docker push harbor.beer1.com/app/todo-server-kotlin

 

푸쉬가 끝난 후 하버 웹앱으로 접속하면 이미지가 잘 저장된 것을 확인할 수 있다.

 

취약점 점검

harbor의 강력한 기능 중 하나는 저장된 이미지에 대해 취약점을 점검할 수 있다. 하버를 배포할 떄 Trivy도 같이 설치했기 때문에 취약점 점검을 할 수 있게 된다. 이를 통해 보안 위협에 대해 잘 대처할 수 있게 되어 보다 안전한 이미지를 사용하도록 할 수 있다.

 

일단 프로젝트에서 이미지 이름을 클릭하면 Artifact 정보들이 나오는데, 여기서 취약점을 점검할 artifact를 선택한 다음 Scan 버튼을 누르기만 하면 간단히 취약점 점검을 할 수 있다.

 

스캔 도중에는 Vulnerabilities에 Scanning 으로 보이기 떄문에 취약점 검사 중인 것을 확인할 수 있다. 취약점 스캔이 완료되면 Vulnerabilities에 간단히 취약점 위험도와 갯수를 보여준다.

 

Artifact를 클릭하면 취약점에 대한 자세한 내용도 확인할 수 있다.

 

취약점 자동 점검 및 Webhook

Harbor에는 Project별로 이미지 푸쉬 후 자동으로 취약점을 점검할 수 있는 설정을 할 수 있다. 아무 Project를 클릭하고 Configuration 탭을 클릭하면 프로젝트 설정을 할 수 있는데 여기서 Vulnerability scanning 에서 Automatically scan images on push 를 체크하면 해당 프로젝트에 푸쉬되는 모든 이미지는 푸쉬된 후 자동으로 취약점 점검을 할 수 있다.

 

그리고 Webhook 탭을 클릭하면 여러 웹훅을 설정할 수 있는데 NEW WEBHOOK 버튼을 누르면 특정 이벤트가 발생했을 때 웹훅을 날릴 수 있도록 구성을 할 수 있다. 웹훅을 잘 사용한다면 다양한 자동화 및 알림 기능을 사용할 수 있다. 예를 들어, Scanning finished 이벤트 타입의 웹훅을 설정한다면 스캐닝이 완료된 후 Slack 등으로 알림을 보내는 기능을 도입할 수 있다.

728x90

'DevOps > Docker' 카테고리의 다른 글

7. 도커 컨테이너 로그와 자원 할당 제한  (0) 2022.05.17
6. Dockerfile  (0) 2022.05.14
5. Docker Image  (0) 2022.05.14
4. Docker Network  (0) 2022.05.14
3. Docker Volume  (0) 2022.04.24

댓글