Harbor is an open-source solution and graduated CNCF project. It's matured to having a solid helm chart and deployment model. In the past, I have tried to launch instances but due to it's heavier requirements, was unable to get it working. With Kubecon EU, I decide this was the week to sort these issues out and get it running.
We'll be using a simple on-prem K3s cluster (x86) and Nginx ingress to minimize dependencies. We will be creating some DNS entries so plan to have a TLD you can use to create subdomains.
Let's get started!
DNS Entries
Let's begin by getting our DNS entries sorted. I'm going to create a DNS entry for harbor in Route53.
Go to Route53 and create a record:
Next, let's create records for harbor, core and notary:
This might be wrong and i'll remove it.. but trying both core.freshbrewed.science and core.harbor.freshbrewed.science
and the resulting entries
Next we need to ensure cert manager is working so we will create the namespace and label it
builder@DESKTOP-JBA79RT:~$ kubectl create namespace harbor-system
namespace/harbor-system created
builder@DESKTOP-JBA79RT:~$ kubectl label namespace --overwrite harbor-system app=kubed
namespace/harbor-system labeled
In my case, I don't have cluster-issuer watching all namespaces to give certs. So let's create our certs directly
$ kubectl get clusterissuer
NAME READY AGE
letsencrypt-prod True 129d
While i don't think i need "core", i'm going to create it now anyhow. I know i'll need harbor and notary created (fyi, i didn't in the end)
builder@DESKTOP-JBA79RT:~/Workspaces$ cat harbor.fb.science.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: harbor-fb-science
namespace: default
spec:
commonName: harbor.freshbrewed.science
dnsNames:
- harbor.freshbrewed.science
issuerRef:
kind: ClusterIssuer
name: letsencrypt-prod
secretName: harbor.freshbrewed.science-cert
builder@DESKTOP-JBA79RT:~/Workspaces$ cat notary.fb.science.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: notary-fb-science
namespace: default
spec:
commonName: notary.freshbrewed.science
dnsNames:
- notary.freshbrewed.science
issuerRef:
kind: ClusterIssuer
name: letsencrypt-prod
secretName: notary.freshbrewed.science-cert
builder@DESKTOP-JBA79RT:~/Workspaces$ cat core.fb.science.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: core-fb-science
namespace: default
spec:
commonName: core.freshbrewed.science
dnsNames:
- core.freshbrewed.science
issuerRef:
kind: ClusterIssuer
name: letsencrypt-prod
secretName: core.freshbrewed.science-cert
Let's create and wait for the certs to get created
builder@DESKTOP-JBA79RT:~/Workspaces$ kubectl apply -f harbor.fb.science.yaml --validate=false
certificate.cert-manager.io/harbor-fb-science created
builder@DESKTOP-JBA79RT:~/Workspaces$ kubectl apply -f notary.fb.science.yaml --validate=false
certificate.cert-manager.io/notary-fb-science created
builder@DESKTOP-JBA79RT:~/Workspaces$ kubectl apply -f core.fb.science.yaml --validate=false
certificate.cert-manager.io/core-fb-science created
builder@DESKTOP-JBA79RT:~/Workspaces$ kubectl get certificate
NAME READY SECRET AGE
myk8s-tpk-best True myk8s.tpk.best-cert 32d
myk8s-tpk-pw True myk8s.tpk.pw-cert 16d
dapr-react-tpk-pw True dapr-react.tpk.pw-cert 16d
harbor-fb-science False harbor.freshbrewed.science-cert 22s
notary-fb-science False notary.freshbrewed.science-cert 14s
core-fb-science False core.freshbrewed.science-cert 7s
builder@DESKTOP-JBA79RT:~/Workspaces$ kubectl get certificate | grep fresh
harbor-fb-science True harbor.freshbrewed.science-cert 43s
notary-fb-science True notary.freshbrewed.science-cert 35s
core-fb-science False core.freshbrewed.science-cert 28s
builder@DESKTOP-JBA79RT:~/Workspaces$ kubectl get certificate | grep fresh
harbor-fb-science True harbor.freshbrewed.science-cert 48s
notary-fb-science True notary.freshbrewed.science-cert 40s
core-fb-science True core.freshbrewed.science-cert 33s
And here are our certs
$ kubectl get secrets | grep cert | tail -n3
harbor.freshbrewed.science-cert kubernetes.io/tls 2 43s
notary.freshbrewed.science-cert kubernetes.io/tls 2 38s
core.freshbrewed.science-cert kubernetes.io/tls 2 30s
Installing Harbor
First we need to create a values file
$ cat harbor.values.yaml
expose:
type: ingress
tls:
certSource: secret
secret:
secretName: harbor.freshbrewed.science-cert
notarySecretName: notary.freshbrewed.science-cert
ingress:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-production
hosts:
core: harbor.freshbrewed.science
notary: notary.freshbrewed.science
harborAdminPassword: bm90IG15IHJlYWwgcGFzc3dvcmQK
externalURL: https://harbor.freshbrewed.science
secretKey: "bm90IG15IHJlYWwgc2VjcmV0IGVpdGhlcgo="
notary:
enabled: true
metrics:
enabled: true
Add the helm chart repo
builder@DESKTOP-JBA79RT:~/Workspaces$ helm repo add harbor https://helm.goharbor.io
"harbor" has been added to your repositories
builder@DESKTOP-JBA79RT:~/Workspaces$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "harbor" chart repository
...Successfully got an update from the "azure-samples" chart repository
...Successfully got an update from the "dapr" chart repository
...Successfully got an update from the "hashicorp" chart repository
...Successfully got an update from the "kedacore" chart repository
...Successfully got an update from the "nginx-stable" chart repository
...Successfully got an update from the "jetstack" chart repository
...Successfully got an update from the "datadog" chart repository
...Successfully got an update from the "datawire" chart repository
...Successfully got an update from the "openfaas" chart repository
...Successfully got an update from the "bitnami" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈Happy Helming!⎈
Install
$ helm upgrade --install harbor-registry harbor/harbor --values ./harbor.values.yaml
Release "harbor-registry" does not exist. Installing it now.
NAME: harbor-registry
LAST DEPLOYED: Wed May 5 06:42:58 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Please wait for several minutes for Harbor deployment to complete.
Then you should be able to visit the Harbor portal at https://harbor.freshbrewed.science
For more details, please visit https://github.com/goharbor/harbor
And we now can login
https://harbor.freshbrewed.science/harbor/sign-in?redirect_url=%2Fharbor%2Fprojects
Working through issues
Unfortunately we get errors...
In checking, the first issue stemmed from harbor redis failing to launch
harbor-registry-harbor-jobservice-566476477d-9xflj 0/1 ContainerCreating 0 30m
harbor-registry-harbor-redis-0 0/1 ContainerCreating 0 30m
harbor-registry-harbor-trivy-0 0/1 ContainerCreating 0 30m
harbor-registry-harbor-portal-76bdcc7969-p2mgz 1/1 Running 0 30m
harbor-registry-harbor-chartmuseum-9f57db94d-w8lwg 1/1 Running 0 30m
harbor-registry-harbor-registry-8595485889-6dq49 2/2 Running 0 30m
harbor-registry-harbor-exporter-655dd658bb-mk5sj 0/1 CrashLoopBackOff 8 30m
harbor-registry-harbor-core-54875f69d8-6s76v 0/1 CrashLoopBackOff 8 30m
harbor-registry-harbor-notary-server-66ff6dd4c8-kjp8g 0/1 CrashLoopBackOff 9 30m
harbor-registry-harbor-notary-signer-5dff57c8f4-lvfc7 0/1 CrashLoopBackOff 9 30m
harbor-registry-harbor-database-0 0/1 CrashLoopBackOff 10 30m
I checked my PVCs and they all existed
$ kubectl get pvc | grep harbor
bitnami-harbor-registry Bound pvc-05b5d977-baa0-45b1-a421-7d9d79431551 5Gi RWO managed-nfs-storage 128d
bitnami-harbor-chartmuseum Bound pvc-cf46c907-2c05-4b2c-8831-cbb469d7a6d2 5Gi RWO managed-nfs-storage 128d
bitnami-harbor-jobservice Bound pvc-00febdcb-ccfd-4d1b-a6d8-081a4fd22456 1Gi RWO managed-nfs-storage 128d
data-bitnami-harbor-postgresql-0 Bound pvc-39a1c708-ae58-4546-bc2a-70326b5ffab6 8Gi RWO managed-nfs-storage 128d
redis-data-bitnami-harbor-redis-master-0 Bound pvc-73a7e833-90fb-41ab-b42c-7a1e7fd5aad3 8Gi RWO managed-nfs-storage 128d
data-bitnami-harbor-trivy-0 Bound pvc-aa741760-7dd5-4153-bd0e-d796c8e7abba 5Gi RWO managed-nfs-storage 128d
harbor-registry-harbor-jobservice Bound pvc-626f0e31-84fb-4ea2-9ae8-7456d0d65304 1Gi RWO managed-nfs-storage 35m
harbor-registry-harbor-registry Bound pvc-db3cbfd5-9446-4b22-8295-aa960518e539 5Gi RWO managed-nfs-storage 35m
harbor-registry-harbor-chartmuseum Bound pvc-e2f851ff-e95d-45a0-8d17-e6993e4ac066 5Gi RWO managed-nfs-storage 35m
database-data-harbor-registry-harbor-database-0 Bound pvc-1b7d9cd6-bd64-4574-b0a0-ba642aa025cc 1Gi RWO managed-nfs-storage 35m
data-harbor-registry-harbor-redis-0 Bound pvc-49b8855f-1b66-4fa2-b743-44601b44d6fe 1Gi RWO managed-nfs-storage 35m
data-harbor-registry-harbor-trivy-0 Bound pvc-2ca7d1a7-9538-453f-ba2b-11c1facd66de 5Gi RWO managed-nfs-storage 35m
and the redis pod logs:
errors..
Output: Running scope as unit: run-r6b941188356647ecafd13c51979bf2d5.scope
mount: /var/lib/kubelet/pods/484a45bd-1c11-4a29-aab5-71293a7a0268/volumes/kubernetes.io~nfs/pvc-49b8855f-1b66-4fa2-b743-44601b44d6fe: bad option; for several filesystems (e.g. nfs, cifs) you might need a /sbin/mount.<type> helper program.
Warning FailedMount 12m (x7 over 28m) kubelet Unable to attach or mount volumes: unmounted volumes=[data], unattached volumes=[data]: timed out waiting for the condition
Warning FailedMount 3m43s (x17 over 28m) kubelet (combined from similar events): MountVolume.SetUp failed for volume "pvc-49b8855f-1b66-4fa2-b743-44601b44d6fe" : mount failed: exit status 32
Mounting command: systemd-run
Mounting arguments: --description=Kubernetes transient mount for /var/lib/kubelet/pods/484a45bd-1c11-4a29-aab5-71293a7a0268/volumes/kubernetes.io~nfs/pvc-49b8855f-1b66-4fa2-b743-44601b44d6fe --scope -- mount -t nfs 192.168.1.129:/volume1/k3snfs/default-data-harbor-registry-harbor-redis-0-pvc-49b8855f-1b66-4fa2-b743-44601b44d6fe /var/lib/kubelet/pods/484a45bd-1c11-4a29-aab5-71293a7a0268/volumes/kubernetes.io~nfs/pvc-49b8855f-1b66-4fa2-b743-44601b44d6fe
Output: Running scope as unit: run-r0936ebd44e8244afbd8108ad5a2f334a.scope
This clued me in that perhaps the slower NFS PVC storage class i used did not meet needs in time. I rotated the redis pod and it can back just fine
$ kubectl logs harbor-registry-harbor-redis-0
1:C 05 May 12:16:10.167 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 05 May 12:16:10.167 # Redis version=4.0.14, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 05 May 12:16:10.167 # Configuration loaded
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 4.0.14 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 1
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
1:M 05 May 12:16:10.168 # Server initialized
1:M 05 May 12:16:10.168 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 05 May 12:16:10.169 * Ready to accept connections
I then rotated all the remaining hung pods…
$ kubectl get pods
...
harbor-registry-harbor-jobservice-566476477d-9xflj 0/1 ContainerCreating 0 33m
harbor-registry-harbor-trivy-0 0/1 ContainerCreating 0 33m
harbor-registry-harbor-portal-76bdcc7969-p2mgz 1/1 Running 0 33m
harbor-registry-harbor-chartmuseum-9f57db94d-w8lwg 1/1 Running 0 33m
harbor-registry-harbor-registry-8595485889-6dq49 2/2 Running 0 33m
harbor-registry-harbor-database-0 0/1 CrashLoopBackOff 10 33m
harbor-registry-harbor-exporter-655dd658bb-mk5sj 0/1 CrashLoopBackOff 9 33m
harbor-registry-harbor-notary-server-66ff6dd4c8-kjp8g 0/1 CrashLoopBackOff 10 33m
harbor-registry-harbor-core-54875f69d8-6s76v 0/1 CrashLoopBackOff 9 33m
harbor-registry-harbor-redis-0 1/1 Running 0 33s
harbor-registry-harbor-notary-signer-5dff57c8f4-lvfc7 1/1 Running 10 33m
builder@DESKTOP-JBA79RT:~/Workspaces$ kubectl delete pod harbor-registry-harbor-database-0 && kubectl delete pod harbor-registry-harbor-exporter-655dd658bb-mk5sj && kubectl delete pod harbor-registry-harbor-notary-server-66ff6dd4c8-kjp8g && kubectl delete pod harbor-registry-harbor-core-54875f69d8-6s76v && kubectl delete pod harbor-registry-harbor-trivy-0 && kubectl delete pod harbor-registry-harbor-jobservice-566476477d-9xflj && kubectl delete pod harbor-registry-harbor-notary-signer-5dff57c8f4-lvfc7
We are getting better…
harbor-registry-harbor-portal-76bdcc7969-p2mgz 1/1 Running 0 37m
harbor-registry-harbor-chartmuseum-9f57db94d-w8lwg 1/1 Running 0 37m
harbor-registry-harbor-registry-8595485889-6dq49 2/2 Running 0 37m
harbor-registry-harbor-redis-0 1/1 Running 0 4m34s
harbor-registry-harbor-notary-signer-5dff57c8f4-lvfc7 0/1 CrashLoopBackOff 10 37m
harbor-registry-harbor-trivy-0 0/1 ContainerCreating 0 2m24s
harbor-registry-harbor-jobservice-566476477d-hxsnt 0/1 ContainerCreating 0 2m24s
harbor-registry-harbor-database-0 0/1 CrashLoopBackOff 4 2m47s
harbor-registry-harbor-notary-server-66ff6dd4c8-rj285 1/1 Running 3 2m44s
harbor-registry-harbor-core-54875f69d8-xt9pf 0/1 Running 2 2m39s
harbor-registry-harbor-exporter-655dd658bb-btbb7 0/1 Running 2 2m47s
The main issue, and I tested using just an off the shelf postgres helm chart. We will need to use a different storage class to solve this
Switch default SC to local-storage
we can swap with a patch statement
kubectl patch storageclass managed-nfs-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}' && kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' && echo "switched back to local-storage"
Then remove the outstanding PVCs causing issue (mostly database)
$ kubectl delete pvc database-data-harbor-registry-harbor-database-0
$ kubectl delete pvc data-harbor-registry-harbor-trivy-0
$ kubectl delete pvc data-harbor-registry-harbor-redis-0
$ kubectl delete pvc data-bitnami-harbor-postgresql-0
$ kubectl delete pvc data-bitnami-harbor-trivy-0
Then try again
Note, i ended up just patching the harbor template so i wouldn't need to patch storage classes :
$ helm pull harbor/harbor
$ harbor/templates/database$ cat database-ss.yaml | head -n51 | tail -n4
# args: ["-c", "[ -e /var/lib/postgresql/data/postgresql.conf ] && [ ! -d /var/lib/postgresql/data/pgdata ] && mkdir -m 0700 /var/lib/postgresql/data/pgdata && mv /var/lib/postgresql/data/* /var/lib/postgresql/data/pgdata/ || true"]
args: ["-c", "[ ! -d /var/lib/postgresql/data/pgdata ] && mkdir -m 0700 /var/lib/postgresql/data/pgdata && [ -e /var/lib/postgresql/data/postgresql.conf ] && mv /var/lib/postgresql/data/* /var/lib/postgresql/data/pgdata/ || chmod 0700 /var/lib/postgresql/data/pgdata && ls -l /var/lib/postgresql/data || true"]
volumeMounts:
- name: database-data
install
$ helm upgrade --install harbor-registry --values ./harbor.values.yaml ./harbor
In fact, even when i moved to the local-storage class, the other services also seemed to vomit on NFS storage…
harbor-registry-harbor-portal-76bdcc7969-qkpr8 1/1 Running 0 6m36s
harbor-registry-harbor-redis-0 1/1 Running 0 6m34s
harbor-registry-harbor-registry-f448867c4-h92gq 2/2 Running 0 6m35s
harbor-registry-harbor-trivy-0 1/1 Running 0 6m34s
harbor-registry-harbor-notary-server-b864fb677-rwk5d 1/1 Running 2 6m37s
harbor-registry-harbor-database-0 1/1 Running 0 6m37s
harbor-registry-harbor-exporter-655dd658bb-2pv48 1/1 Running 2 6m37s
harbor-registry-harbor-core-69d84dc44c-kjjff 1/1 Running 2 6m37s
harbor-registry-harbor-notary-signer-db48755d7-p8nsc 1/1 Running 0 2m56s
harbor-registry-harbor-jobservice-849cf67686-9k45r 0/1 ContainerCreating 0 110s
harbor-registry-harbor-chartmuseum-578c697bb-xqf5c 0/1 ContainerCreating 0 97s
the errors:
$ kubectl describe pod harbor-registry-harbor-jobservice-849cf67686-9k45r | tail -n7
Output: Running scope as unit: run-r94bd4319dede427ca96bdc606ca97e65.scope
mount: /var/lib/kubelet/pods/5904a5c9-6e9b-4ea9-8a64-e8e48f25d098/volumes/kubernetes.io~nfs/pvc-626f0e31-84fb-4ea2-9ae8-7456d0d65304: bad option; for several filesystems (e.g. nfs, cifs) you might need a /sbin/mount.<type> helper program.
Warning FailedMount 11s kubelet MountVolume.SetUp failed for volume "pvc-626f0e31-84fb-4ea2-9ae8-7456d0d65304" : mount failed: exit status 32
Mounting command: systemd-run
Mounting arguments: --description=Kubernetes transient mount for /var/lib/kubelet/pods/5904a5c9-6e9b-4ea9-8a64-e8e48f25d098/volumes/kubernetes.io~nfs/pvc-626f0e31-84fb-4ea2-9ae8-7456d0d65304 --scope -- mount -t nfs 192.168.1.129:/volume1/k3snfs/default-harbor-registry-harbor-jobservice-pvc-626f0e31-84fb-4ea2-9ae8-7456d0d65304 /var/lib/kubelet/pods/5904a5c9-6e9b-4ea9-8a64-e8e48f25d098/volumes/kubernetes.io~nfs/pvc-626f0e31-84fb-4ea2-9ae8-7456d0d65304
Output: Running scope as unit: run-re78628302ad34275bda8c731c9cb36c0.scope
mount: /var/lib/kubelet/pods/5904a5c9-6e9b-4ea9-8a64-e8e48f25d098/volumes/kubernetes.io~nfs/pvc-626f0e31-84fb-4ea2-9ae8-7456d0d65304: bad option; for several filesystems (e.g. nfs, cifs) you might need a /sbin/mount.<type> helper program.
And my PVCs
harbor-registry-harbor-jobservice Bound pvc-626f0e31-84fb-4ea2-9ae8-7456d0d65304 1Gi RWO managed-nfs-storage 25h
harbor-registry-harbor-registry Bound pvc-db3cbfd5-9446-4b22-8295-aa960518e539 5Gi RWO managed-nfs-storage 25h
harbor-registry-harbor-chartmuseum Bound pvc-e2f851ff-e95d-45a0-8d17-e6993e4ac066 5Gi RWO managed-nfs-storage 25h
local-path-pvc Bound pvc-931eedae-24e1-4958-b8b1-707820fd99f4 128Mi RWO local-path 15m
data-harbor-registry-harbor-redis-0 Bound pvc-61ddc767-92c4-4f1e-96c0-e86ce1d0d9ee 1Gi RWO local-path 6m51s
data-harbor-registry-harbor-trivy-0 Bound pvc-21362cc0-ee72-4c49-afbd-d93d432fb76e 5Gi RWO local-path 6m51s
database-data-harbor-registry-harbor-database-0 Bound pvc-27a306fc-0136-42fc-a8ac-4e868d0d2edf 1Gi RWO local-path 6m53s
To rotate, just delete the old release:
$ helm delete harbor-registry
manifest-15
manifest-16
manifest-17
release "harbor-registry" uninstalled
Then manually remove the PVCs (they wont auto-remove)
$ kubectl delete pvc harbor-registry-harbor-jobservice && kubectl delete pvc harbor-registry-harbor-registry && kubectl delete pvc harbor-registry-harbor-chartmuseum
persistentvolumeclaim "harbor-registry-harbor-jobservice" deleted
persistentvolumeclaim "harbor-registry-harbor-registry" deleted
persistentvolumeclaim "harbor-registry-harbor-chartmuseum" deleted
That finally worked:
$ kubectl get pods | tail -n11
harbor-registry-harbor-redis-0 1/1 Running 0 3m31s
harbor-registry-harbor-registry-86dbbfd48f-khljs 2/2 Running 0 3m31s
harbor-registry-harbor-trivy-0 1/1 Running 0 3m31s
harbor-registry-harbor-portal-76bdcc7969-sg6lv 1/1 Running 0 3m31s
harbor-registry-harbor-chartmuseum-559bd98f8f-t6k4z 1/1 Running 0 3m31s
harbor-registry-harbor-notary-server-779c6bddd5-kgczg 1/1 Running 2 3m31s
harbor-registry-harbor-notary-signer-c97648889-zxct6 1/1 Running 1 3m30s
harbor-registry-harbor-database-0 1/1 Running 0 3m31s
harbor-registry-harbor-core-7b4594d78d-tg5qs 1/1 Running 1 3m31s
harbor-registry-harbor-exporter-655dd658bb-ff8d7 1/1 Running 1 3m31s
harbor-registry-harbor-jobservice-95968c6d9-cdwfh 1/1 Running 1 3m31s
$ kubectl get pvc | grep harbor
redis-data-bitnami-harbor-redis-master-0 Bound pvc-73a7e833-90fb-41ab-b42c-7a1e7fd5aad3 8Gi RWO managed-nfs-storage 129d
data-harbor-registry-harbor-redis-0 Bound pvc-61ddc767-92c4-4f1e-96c0-e86ce1d0d9ee 1Gi RWO local-path 13m
data-harbor-registry-harbor-trivy-0 Bound pvc-21362cc0-ee72-4c49-afbd-d93d432fb76e 5Gi RWO local-path 13m
database-data-harbor-registry-harbor-database-0 Bound pvc-27a306fc-0136-42fc-a8ac-4e868d0d2edf 1Gi RWO local-path 13m
harbor-registry-harbor-registry Bound pvc-3405edb3-1d15-4aba-96eb-52c3297a8e53 5Gi RWO local-path 3m59s
harbor-registry-harbor-jobservice Bound pvc-0bf9d412-8485-4039-88be-54bf69b3efc7 1Gi RWO local-path 3m59s
harbor-registry-harbor-chartmuseum Bound pvc-4d993758-d4e9-49d1-a7cf-4f15c42623b6 5Gi RWO local-path 3m59s
and this time we can login
Using harbor
Once we login, we need to create a user that will push content into our registry.
In Users (/harbor/users) create a new user:
We can just create a standard user:
Next, lets create a project
and now we can see we have that
Testing Harbor
We can login with docker in a WSL command prompt
$ docker login https://harbor.freshbrewed.science/freshbrewedprivate
Username: isaac@freshbrewed.science
Password:
Login Succeeded
Note: You will need to login to the project URL, not the root of harbor.
For instance:
Next, we can tag an image and push:
builder@DESKTOP-JBA79RT:~/Workspaces$ docker images | tail -n10
ijk8senv5cr.azurecr.io/freshbrewed/webapp latest 05bd0d1575ab 6 months ago 429MB
freshbrewed/haproxy 1.6.11 66ce07f6e5f6 6 months ago 207MB
freshbrewed/haproxy 1.6.11-20201029152918 66ce07f6e5f6 6 months ago 207MB
freshbrewed/haproxy latest 66ce07f6e5f6 6 months ago 207MB
<none> <none> 2fbb4b4d7f8d 6 months ago 106MB
127.0.0.1:5000/hello-world latest bf756fb1ae65 16 months ago 13.3kB
127.0.0.1:8080/hello-world latest bf756fb1ae65 16 months ago 13.3kB
hello-world latest bf756fb1ae65 16 months ago 13.3kB
localhost:5000/hello-world latest bf756fb1ae65 16 months ago 13.3kB
us.icr.io/freshbrewedcr/hw_repo 2 bf756fb1ae65 16 months ago 13.3kB
builder@DESKTOP-JBA79RT:~/Workspaces$ docker tag hello-world:latest harbor.freshbrewed.science/freshbrewedprivate/hello-world:latest
builder@DESKTOP-JBA79RT:~/Workspaces$ docker push harbor.freshbrewed.science/freshbrewedprivate/hello-world:latest
The push refers to repository [harbor.freshbrewed.science/freshbrewedprivate/hello-world]
9c27e219663c: Preparing
unauthorized: unauthorized to access repository: freshbrewedprivate/hello-world, action: push: unauthorized to access repository: freshbrewedprivate/hello-world, action: push
Ah, our user needs permission.
We can go to the members section of the project and add the user account:
then add our user as a developer
Now we can push
builder@DESKTOP-JBA79RT:~/Workspaces$ docker push harbor.freshbrewed.science/freshbrewedprivate/hello-world:latest
The push refers to repository [harbor.freshbrewed.science/freshbrewedprivate/hello-world]
9c27e219663c: Pushed
latest: digest: sha256:90659bf80b44ce6be8234e6ff90a1ac34acbeb826903b02cfa0da11c82cbc042 size: 525
And now we can see that reflected in Harbor
If we go to the hello-world repository now, we can scan the image we pushed:
which has a nice scanning animation
and then results:
and saying we wish to pull, we can copy the pull command under the pull command icon as well
docker pull harbor.freshbrewed.science/freshbrewedprivate/hello-world@sha256:90659bf80b44ce6be8234e6ff90a1ac34acbeb826903b02cfa0da11c82cbc042
builder@DESKTOP-JBA79RT:~/Workspaces$ docker pull harbor.freshbrewed.science/freshbrewedprivate/hello-world@sha256:90659bf80b44ce6be8234e6ff90a1ac34acbeb826903b02cfa0da11c82cbc042
harbor.freshbrewed.science/freshbrewedprivate/hello-world@sha256:90659bf80b44ce6be8234e6ff90a1ac34acbeb826903b02cfa0da11c82cbc042: Pulling from freshbrewedprivate/hello-world
Digest: sha256:90659bf80b44ce6be8234e6ff90a1ac34acbeb826903b02cfa0da11c82cbc042
Status: Image is up to date for harbor.freshbrewed.science/freshbrewedprivate/hello-world@sha256:90659bf80b44ce6be8234e6ff90a1ac34acbeb826903b02cfa0da11c82cbc042
harbor.freshbrewed.science/freshbrewedprivate/hello-world@sha256:90659bf80b44ce6be8234e6ff90a1ac34acbeb826903b02cfa0da11c82cbc042
Also, you may wish to setup your outgoing mail server in configuration if you wish to enable users to reset their own password
Supposedly the mail server is used for users to reset password, but i have yet to see that feature actually work.
I've seen notes that /reset_password is a REST endpoint, but when i look at the 2.0 API Swagger, the only endpoint matches the UI with an old/new
Users vs Admins
You can see how the view for users differs from admins, mostly around the whole "Administration" area
Summary
GoHarbor.io is a pretty nice full featured container storage solution. Issues installing it stemmed from it's usage of PostgreSQL and PostgreSQL database not liking my storage class. This likely would not affect a production instance and moreover, the database can be externalized for a real production level deployment.
Using a cluster of old macs as a k3s cluster and local-storage, it worked. It was slow, but it worked and really what i wanted was a usable container store for on-prem work for which this satisfies.
Having user IDs and built-in container scanning with Trivy is a really great benefit:
We will see how this handles storage expansion as i only saw about 5Gb allocated thus far for storage
$ kubectl get pvc | grep harbor-registry
data-harbor-registry-harbor-redis-0 Bound pvc-61ddc767-92c4-4f1e-96c0-e86ce1d0d9ee 1Gi RWO local-path 24h
data-harbor-registry-harbor-trivy-0 Bound pvc-21362cc0-ee72-4c49-afbd-d93d432fb76e 5Gi RWO local-path 24h
database-data-harbor-registry-harbor-database-0 Bound pvc-27a306fc-0136-42fc-a8ac-4e868d0d2edf 1Gi RWO local-path 24h
harbor-registry-harbor-registry Bound pvc-3405edb3-1d15-4aba-96eb-52c3297a8e53 5Gi RWO local-path 24h
harbor-registry-harbor-jobservice Bound pvc-0bf9d412-8485-4039-88be-54bf69b3efc7 1Gi RWO local-path 24h
harbor-registry-harbor-chartmuseum Bound pvc-4d993758-d4e9-49d1-a7cf-4f15c42623b6 5Gi RWO local-path 24h
Harbor is a nice open-source offering, albeit a bit heavy for small team needs.