Skip to main content

Docs

God shall bless us; and all the ends of the earth shall fear him.

harbor 쿠버네티스에 Harbor 설치하기

목차


요구사항​

  • 최소사양: 2 CPU/4GB 메모리/40GB 디스크
  • 권장사양: 4 CPU/8GB 메모리/160GB 디스크
  • 도커, 도커-컴포즈, (option)OpenSSL
  • (또는) 쿠버네티스 클러스터

하버 설치​

이 글은 쿠버네티스 에서 헬름 을 사용한 하버 배포와 문제 해결을 주로 다룹니다.
바로가기

아래는 도커 컴포즈를 사용한 하버 설치 방법을 간단히 기술했습니다. 읽어 보시면 하버 구조파악에 도움이 될 것입니다!

Alt
간단히가 아니라 대충이겠지!

하버 인스톨러 다운로드​

  1. 하버 릴리즈 페이지 에서 오프라인/온라인 인스톨러를 받습니다.
# 21.07.21 최신 버전 기준
$ wget https://github.com/goharbor/harbor/releases/download/v2.3.0/harbor-offline-installer-v2.3.0.tgz

  1. (선택) 검증하기

정상적인 설치 파일인지 검증해볼 수 있습니다.

# *.asc 파일을 위해 퍼블릭 키를 얻습니다
$ gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 644FF454C0B4115C
gpg: /home/cwadmin/.gnupg/trustdb.gpg: trustdb created
gpg: key 644FF454C0B4115C: public key "Harbor-sign (The key for signing Harbor build) <jiangd@vmware.com>" imported
gpg: Total number processed: 1
gpg: imported: 1

public key "Harbor-sign (The key for signing Harbor build) <jiangd@vmware.com>" imported 를 확인해야 합니다.

# *.asc를 이용해 검증합니다
# 온라인 인스톨러
$ gpg -v --keyserver hkps://keyserver.ubuntu.com --verify harbor-online-installer-version.tgz.asc
# 오프라인 인스톨러
$ gpg -v --keyserver hkps://keyserver.ubuntu.com --verify harbor-offline-installer-version.tgz.asc
gpg: assuming signed data in 'harbor-offline-installer-v2.2.3.tgz'
gpg: Signature made 2021년 07월 05일 (월)
gpg: using RSA key 7722D168DAEC457806C96FF9644FF454C0B4115C
gpg: using pgp trust model
gpg: Good signature from "Harbor-sign (The key for signing Harbor build) <jiangd@vmware.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 7722 D168 DAEC 4578 06C9 6FF9 644F F454 C0B4 115C
gpg: binary signature, digest algorithm SHA512, key algorithm rsa4096

Good signature from "Harbor-sign (The key for signing Harbor build) <jiangd@vmware.com>" [unknown] 를 확인해야 합니다.


  1. tar 풀기
$ tar xzvf harbor-[online/offline]-installer-[version].tgz

이 글 에서 tar 파일을 자세히 다루고 있습니다.


SSL​

안타깝지만 일정에 치여서 다루지 않겠습니다.

간단히 설명 드리자면 https를 위한 SSL/TLS 인증서를 발급받는 절차입니다. 외부 도메인이 있다면 Let's Encript 에서 무료로 인증서를 발급받을 수 있습니다.
내부적으로 사용한다면 OpenSSL 을 사용하여 인증서를 생성합니다.

[공식]Configure HTTPS Access to Harbor


TLS 설정​

내부 TLS 설정을 다룹니다.
[공식]Configure Internal TLS


YML 설정​

하버를 커스터마이징 할 수 있는 harbor.yml 파일의 설정을 다룹니다.
[공식]Configure the Harbor YML File


인스톨러 스크립트 실행​

하버는 다음 설정으로 설치할 수 있습니다.

  • 하버만
  • Notary 와 하버
  • Trivy 와 하버
  • (helm)ChartMuseum 와 하버
  • 위의 세개 중 두개나 전부 설치하기

Notary: 서버와 클라이언트 사이에 삼자로 신뢰할 수 있는 통신을 만들어 줍니다(CNCF)
Trivy: 이미지의 취약점을 찾습니다

각각의 설정은 다음 플래그를 이용합니다.

  • Notary: --with-notary
  • Trivy: --with-trivy
  • ChartMuseum: --with-chartmuseum

쉘 스크립트를 실행할 때 여러 권한이 필요합니다. sudo로 권한을 부여해 줍시다.

$ sudo ./install.sh [flags]

CLI​

Harbor API v2.0을 사용하는 간단한 CLI 명령어를 테스트 할 수 있습니다.

# 이미지 목록 확인
$ curl -X GET http://[$id:$passwd@]${endpoint}:5000/v2/_catalog
$ curl -u "$id:$pw"
{"repositories":["ubuntu"]}

$ curl -X GET http://[$id:$passwd@]${endpoint}:5000/v2/${repository}/tags/list
{"name":"ubuntu","tags":["18.04"]}

축하드립니다!🎉 설치에 성공하셨군요!


헬름차트로 배포​

요구사항

  • 쿠버네티스 1.10+
  • 헬름 2.8.0+
  • 쿠버네티스 활용 지식
  • PostgreSQL 능력자
  • Redis 능력자
  • PVC와 외부 오브젝트 스토리지 능력자

사실 지식은 필요 없어도 됩니다

헬름차트가 없다면 이 글 을 보고 설치할 수 있습니다.

차트 다운받기​

$ helm repo add harbor https://helm.goharbor.io
"harbor" has been added to your repositories

$ helm fetch harbor/harbor --untar

⚠주의 helm repo는 계정마다 관리됩니다. sudo를 사용해 fetch 할 때 주의하세요


설정하기​

레포지토리를 받은 폴더에 values.yaml 파일이 있습니다. 이 파일을 수정하거나, 배포 명령어에 --set플래그를 지정해 값을 변경할 수 있습니다.

해당 파일에도 주석이 잘 적혀있습니다만, 공식 사이트를 확인해 보는 것도 좋을 것입니다. 😁
[공식]Configuration

주요 플래그

  • externalURL: FQDN(Fully Qualified Domain Name)입니다. http 프로토콜을 사용해도 https로 작성하세요. 80이나 443이 아닌 포트로 노출된다면, 포트를 명시해 주세요.

  • expose.tls.enabled: TLS 사용여부를 설정합니다.(ture/false)

  • expose.ingress.hosts.core/notary: 포트를 제외한 노출 url을 작성합니다. *와 같은 wildcard를 사용할 수 있고, ip 형식일 수 없습니다. 무료 와일드카드 DNS

  • persistence.enabled: 삭제시 pv 처리 방법을 설정합니다.(ture/false)

  • harborAdminPassword: 관리자(admin) 계정의 비밀번호를 설정합니다. 기본값은 Harbor12345입니다


배포하기​

다음 명령어로 쿠버네티스에 배포할 수 있습니다.

# (Helm2) helm install --name $name $chart $flags
$ helm install --name my-release chart/path/ --set key=value

# (Helm3) helm install $name $chart $flags
$ helm install --namespace harbor harbor-v1 .

헬름으로 배포하면 아래 pvc가 생성됩니다. 각각에 맞는 적절한 pv를 생성해 줍시다.

$ kubectl get pvc -n harbor -o custom-columns=NAME:.metadata.name,STORAGE:.spec.resources.requests.storage
NAME STORAGE
data-harbor-redis-0 1GB
data-harbor-trivy-0 5GB
database-data-harbor-database-0 1GB
harbor-chartmuseum 5GB
harbor-jobservice 1GB
harbor-registry 5GB

총 18GB를 필요로 하지만 요구사항 에 맞게 저는 레지스트리에 160GB를 할당했습니다.

pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: harbor-redis
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
claimRef:
name: data-harbor-redis-0
namespace: harbor
local:
path: /var/lib/docker/volumes/harbor/redis
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- cwdev07
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
volumeMode: Filesystem
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: harbor-trivy
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 5Gi
claimRef:
name: data-harbor-trivy-0
namespace: harbor
local:
path: /var/lib/docker/volumes/harbor/trivy
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- cwdev07
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
volumeMode: Filesystem
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: harbor-database
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
claimRef:
name: database-data-harbor-database-0
namespace: harbor
local:
path: /var/lib/docker/volumes/harbor/database
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- cwdev07
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
volumeMode: Filesystem
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: harbor-chartmuseum
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 5Gi
claimRef:
name: harbor-chartmuseum
namespace: harbor
local:
path: /var/lib/docker/volumes/harbor/chartmuseum
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- cwdev07
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
volumeMode: Filesystem
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: harbor-jobservice
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
claimRef:
name: harbor-jobservice
namespace: harbor
local:
path: /var/lib/docker/volumes/harbor/jobservice
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- cwdev07
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
volumeMode: Filesystem
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: harbor-registry
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 160Gi
claimRef:
name: harbor-registry
namespace: harbor
local:
path: /var/lib/docker/volumes/harbor/registry
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- cwdev07
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
volumeMode: Filesystem

서비스 노출​

pv 생성까지 완료하면 파드들이 정상적으로 실행됩니다. 이후 지정한 노출 방식(expose.type)에 따라 서비스 설정을 합니다. 예를 들어 ingress는 ingress controller를 쿠버네티스에 배포해야 합니다.
ingress-nginx

완료되면 설정한 externalURL로 접속해 봅시다!

헬름 차트로 값을 변경하지 않고 배포하셨다면, 하버 포탈의 관리자 id/pw는 다음과 같습니다.

  • id: admin
  • password: Harbor12345

자, 이제 당신도 개인 도커 레지스트리를 가지고 있습니다!


문제 해결​

1. 로그인이 안되요​

Invalid user name or password 에러가 뜨며 로그인이 안됩니다.
당황하지 말고 브라우저 개발자 도구 (chrome F12)를 눌러서 확인해 봅시다.

a: 405 Method Not Allowed​

NodePort, ClusterIP, LoadBalancer로 노출할 때 api 서버와 연동이 안되어 발생하는 문제로 생각됩니다.
ingress를 살펴볼까요?

... 전략 ...
spec:
rules:
- host: { 도메인 이름 }
http:
paths:
- backend:
serviceName: harbor-v1-portal
servicePort: 80
path: /
- backend:
serviceName: harbor-v1-core
servicePort: 80
path: /api/
- backend:
serviceName: harbor-v1-core
servicePort: 80
path: /service/
- backend:
serviceName: harbor-v1-core
servicePort: 80
path: /v2
- backend:
serviceName: harbor-v1-core
servicePort: 80
path: /chartrepo/
- backend:
serviceName: harbor-v1-core
servicePort: 80
path: /c/
... 후략 ...

/는 ui서버(portal), /api/, /service/, /v2, /chartrepo/, /c/는 core로 연결(proxy)되고 있습니다. nginx 서비스가 실행중인지, 설정이 잘 되었는지 확인해 보시면 되겠습니다.

b: 403 Forbidden​

잘못된 프로토콜(http)로 접근했을 가능성이 큽니다. 인증서를 따로 설정해 주지 않으셨더라도, https로 접근해야 합니다.

2. 접속이 안되요(404 Not Found)​

ingress를 사용하셨다면 host를 제거합시다(주석처리).

$ kubectl edit ingress $harbor-ingress

... 전략 ...
spec:
rules:
#- host: { 도메인 이름 }
- http:
... 후략 ...

호롤리한 하루

TMI

대학 동기인데, 저와 비슷한 일을 하는 걸 보니 신기하네요.
사실 친하지도 않고 안면식도 있을지 모릅니다
저는 실무적인 일을 많이 하는데, 이론적인 공부를 탄탄히 쌓아가는 걸 보면 부럽습니다.

3. docker login이 안되요​

a: unsupported protocol scheme ""​

$ docker login ${endpoint}
Error response from daemon: Get ${endpoint\}/v2/: Get ${endpoint}/service/token?account=apannwitz&client_id=docker&offline_token=true&service=harbor-registry: unsupported protocol scheme ""

인증서를 사용하지 않을 때 서버에서 발생하는 문제입니다.
http 프로토콜을 사용하더라도 externalURL은 https로 작성해야 합니다.

$ helm upgrade -n harbor ${release-name} . --set externalURL=https://${endpoint}

[깃헙]Set protocol of URL as "http"


b: x509 certificate​

$ docker login ${endpoint}
Error response from daemon: Get ${endpoint}/v2/: x509: certificate is valid for ingress.local, not ${endpoint}

마찬가지로 인증서를 사용하지 않을 때 로컬에서 발생하는 문제입니다.
docker의 daemon.json을 수정하고 재시작 합니다. (/etc/docker/daemon.json)

{
... 전략 ...

"insecure-registries": ["your-registry-addr"],

... 후략 ...
}

데몬 재시작 명령어

$ systemctl restart docker
# 또는
$ service docker restart

[공식]Connecting to Harbor via HTTP
[깃헙]CNCF distribution 무려 800 이상의 반응이...
[stackoverflow]Can not pull/push images after update docker to 1.12


c: dial tcp 443 connect connection refused​

$ docker login ${endpoint}
Error response from daemon: Get https://${url}:${port}/v2/: Get https://${url}/service/token?account=harbor_registry_user&client_id=docker&offline_token=true&service=harbor-registry: connect: connection refused
# 또는
Error response from daemon: login attempt to http://${url}:${port}/v2/ failed with status: 400 Bad Request
# 또는
Error response from daemon: Get https://${url}/v2/: dial tcp ${url}:443: connect: connection refused

첫번째 경우는 harbor-core에서 발생하는 문제입니다.
/v2/에서 externalURL로 /service/ api를 호출하는데, port가 지정되어 있지 않으면 https 프로토콜의 기본값 443번으로 연결됩니다.

$ helm upgrade -n harbor ${release-name} . --set externalURL=https://${url\}:${port}

두번째로 /v2/에서 400 Bad Request가 발생한다면 ingress의 호스트 문제일 가능성이 큽니다. 2. 접속이 안되요(404 Not Found) 를 참고해 호스트를 제거합니다.

세번째로 /v2/에서 바로 connection refused가 발생한다면, docker login ${endpoint}에서 포트를 누락시켰을 가능성이 큽니다.

[깃헙]Not able to connect to harbor repo


4. push/pull 이 안되요​

$ docker push ${endpoint}/${repository}/${image-name}:${tag}
Error response from daemon: Get ${endpoint}/v2/: http: server gave HTTP response to HTTPS client

이 역시 인증서를 사용하지 않았을 때 로컬에서 생기는 문제입니다.
이쯤되면 그냥 인증서 쓰고 말자
b: x509 certificate 를 참고해 로컬 도커의 daemon.json을 수정해 줍시다.

[stackoverflow]Docker repository server gave HTTP response to HTTPS client

Tips​

6월 25일 도입을 결정했으나, 시기를 계속 늦춰 만 한달이 지난 7월 19일부터 작업을 했습니다. 설치와 배포는 생각보다 빠르게 됬네요:)

Reference​

[공식]Harbor Installation and Configuration
[공식]Deploying Harbor with High Availability via Helm