Running an ARM port of Harbor Registry and Portainer on Raspberry PI4 with RockyLinux 8.4

Author: Ananda Kammampati

Dated : November 2021

Raspberry Pi4

Harbor Registry Portal

logged-in

Portainer Dashboard

portainer-dashboard-mainpage

Harbor Images in Portainer

portainer-mainpage

Credits:

  • All credits on porting Project Harbor Registry to ARM Architecture goes to Bin Liang . ( A Big Thank You! )

Goal:

  • Running ARM port of Project Harbor Registry on Raspberry Pi4 with RockyLinux 8.4
  • Running Portainer Commuity Edition on the same platform to administer the Containers that makes up the Harbor Registry

Scope:

  • Document and sharing all the required steps

Out of Scope:

  • Administration and detailed usage of Harbor Registry
  • Administration and detailed usage of Portainer
  • Note that the version of Harbor Registry is relatively older
  • Extensive end-to-end testing

References:

Videos:

  • Video is also for reference

Making a Wish:

  • Dear Harbor Stakeholders@VMware - PLEEEEASE consider promoting ARM builds for Project Harbor Registry
  • ARM SBC Enthusiasts all over the world will be thankful to you for Eternity 🙂

Step 01: Platform Verification

# uname -a
Linux raspi4-rocky 5.10.52-v8.1.el8 #1 SMP PREEMPT Wed Sep 29 23:57:05 UTC 2021 aarch64 aarch64 aarch64 GNU/Linux

# cat /etc/*release
-----
Rocky Linux release 8.4 (Green Obsidian)
NAME="Rocky Linux"
VERSION="8.4 (Green Obsidian)"
ID="rocky"
ID_LIKE="rhel centos fedora"
VERSION_ID="8.4"
PLATFORM_ID="platform:el8"
PRETTY_NAME="Rocky Linux 8.4 (Green Obsidian)"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:rocky:rocky:8.4:GA"
HOME_URL="https://rockylinux.org/"
BUG_REPORT_URL="https://bugs.rockylinux.org/"
ROCKY_SUPPORT_PRODUCT="Rocky Linux"
ROCKY_SUPPORT_PRODUCT_VERSION="8"
Rocky Linux release 8.4 (Green Obsidian)
Rocky Linux release 8.4 (Green Obsidian)
Rocky Linux release 8.4 (Green Obsidian)
-----

Step 02: Installing docker

# dnf install -y docker-ce

# docker --version
Docker version 20.10.10, build b485636

# systemctl start docker.service

# systemctl status -l docker.service
-----
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
Active: active (running) since Wed 2021-10-27 23:43:33 UTC; 10s ago
Docs: https://docs.docker.com
Main PID: 2879 (dockerd)
Tasks: 9
CGroup: /system.slice/docker.service
└─2879 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

Oct 27 23:43:29 raspi4-rocky dockerd[2879]: time="2021-10-27T23:43:29.244477824Z" level=warning msg="Your kernel does not support CPU realtime scheduler"
Oct 27 23:43:29 raspi4-rocky dockerd[2879]: time="2021-10-27T23:43:29.244503509Z" level=warning msg="Your kernel does not support cgroup blkio weight"
Oct 27 23:43:29 raspi4-rocky dockerd[2879]: time="2021-10-27T23:43:29.244529064Z" level=warning msg="Your kernel does not support cgroup blkio weight_device"
Oct 27 23:43:29 raspi4-rocky dockerd[2879]: time="2021-10-27T23:43:29.245940749Z" level=info msg="Loading containers: start."
Oct 27 23:43:30 raspi4-rocky dockerd[2879]: time="2021-10-27T23:43:30.378531331Z" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0/16. Daemon o>
Oct 27 23:43:31 raspi4-rocky dockerd[2879]: time="2021-10-27T23:43:31.010636585Z" level=info msg="Loading containers: done."
Oct 27 23:43:32 raspi4-rocky dockerd[2879]: time="2021-10-27T23:43:32.645814347Z" level=info msg="Docker daemon" commit=e2f740d graphdriver(s)=overlay2 version=20.10.10
Oct 27 23:43:32 raspi4-rocky dockerd[2879]: time="2021-10-27T23:43:32.646210032Z" level=info msg="Daemon has completed initialization"
Oct 27 23:43:33 raspi4-rocky systemd[1]: Started Docker Application Container Engine.
Oct 27 23:43:33 raspi4-rocky dockerd[2879]: time="2021-10-27T23:43:33.194973416Z" level=info msg="API listen on /var/run/docker.sock"
-----

# systemctl enable docker.service

# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE

# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Step 03: Installing docker-compose

# wget https://github.com/docker/compose/releases/download/v2.0.1/docker-compose-linux-aarch64

# chmod 755 ./docker-compose-linux-aarch64

# mv ./docker-compose-linux-aarch64 /usr/local/bin/docker-compose

# docker-compose --version
Docker Compose version v2.0.1

Step 04: Have enough storage for container images

I have a 1 TB NVMe SSD connected to USB port of Raspberry Pi4, formatted with EXT4 filesystem type and mounted at /data

# vi /etc/fstab
-----
/dev/sda1 /data ext4 defaults 0 0
-----


# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 931.5G 0 disk
└─sda1 8:1 0 931.5G 0 part /data ------------>> 1TB SSD mounted at /data
mmcblk0 179:0 0 14.9G 0 disk
├─mmcblk0p1 179:1 0 286M 0 part /boot
├─mmcblk0p2 179:2 0 488M 0 part [SWAP]
└─mmcblk0p3 179:3 0 14.1G 0 part /


# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/root 14G 7.6G 6.3G 55% /
devtmpfs 3.8G 0 3.8G 0% /dev
tmpfs 3.9G 25M 3.8G 1% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
tmpfs 782M 0 782M 0% /run/user/0
/dev/mmcblk0p1 286M 66M 220M 24% /boot
/dev/sda1 916G 1.4G 868G 1% /data ------------>> This is where Harbor Registry components go

Step 05: Download ARM Harbor Offline installer (Version 1.9.3)  from Bin Liang's Github Repository

# mkdir /var/log/harbor

# pwd
/root

# wget https://github.com/hzliangbin/harbor-arm64/releases/download/v1.9.3/harbor-offline-installer-v1.9.3.tgz

# tar zxvf harbor-offline-installer-v1.9.3.tgz

# cd harbor

# vi harbor.yml
-----
hostname: 192.168.1.45  --------------------->> Change hostname, port, data_volume according to your setup
port: 8080
data_volume: /data
location: /var/log/harbor
-----

Step 06: Run the installer

# ./install.sh -------------------->> Click the button 'install.sh output' below to see its output

Step 07: Verify Harbor Registry docker images are downloaded successfully

# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
goharbor/redis-photon v1.9.3 a74eb7f8d1f5 16 months ago 93MB
goharbor/clair-photon v2.1.0-v1.9.3 590f2641528f 16 months ago 215MB
goharbor/harbor-registryctl v1.9.3 2e601207c097 16 months ago 95.5MB
goharbor/registry-photon v2.7.1-patch-2819-2553-v1.9.3 6dc433588070 16 months ago 79.4MB
goharbor/nginx-photon v1.9.3 3be597f805f7 16 months ago 39.4MB
goharbor/harbor-log v1.9.3 813238123c15 16 months ago 102MB
goharbor/harbor-jobservice v1.9.3 5002c9a379b9 16 months ago 131MB
goharbor/harbor-core v1.9.3 243a33e77535 16 months ago 144MB
goharbor/harbor-portal v1.9.3 348475a4cd83 16 months ago 46.8MB
goharbor/harbor-db v1.9.3 1ee674580fdd 16 months ago 139MB
goharbor/prepare v1.9.3 29f7481983a0 16 months ago 145MB

Step 08: Verify if all the containers are running fine after installation

# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1d31ed98e176 goharbor/nginx-photon:v1.9.3 "nginx -g 'daemon of…" 14 minutes ago Created nginx
a1a5a3cb8db8 goharbor/harbor-jobservice:v1.9.3 "/harbor/harbor_jobs…" 14 minutes ago Created harbor-jobservice
5373bf506684 goharbor/harbor-core:v1.9.3 "/harbor/harbor_core" 14 minutes ago Created harbor-core
b3428728fad4 goharbor/registry-photon:v2.7.1-patch-2819-2553-v1.9.3 "/entrypoint.sh /etc…" 15 minutes ago Created 5000/tcp registry
f75657777ca5 goharbor/harbor-db:v1.9.3 "/docker-entrypoint.…" 15 minutes ago Created 5432/tcp harbor-db
a3ee639d242b goharbor/harbor-registryctl:v1.9.3 "/harbor/start.sh" 15 minutes ago Exited (1) 14 minutes ago registryctl
e20925da8095 goharbor/redis-photon:v1.9.3 "docker-entrypoint.s…" 15 minutes ago Created 6379/tcp redis
d9f43d7de9a8 goharbor/harbor-portal:v1.9.3 "nginx -g 'daemon of…" 15 minutes ago Created 8080/tcp harbor-portal
5213157448fa goharbor/harbor-log:v1.9.3 "/bin/sh -c /usr/loc…" 15 minutes ago Restarting (1) 4 seconds ago harbor-log

Step 09: Fixing containers - registry, registrctl, harbor-log

  • You will notice the above mentioned Containers in Red will fail to run and restart
  • The explanation and a few workarounds are suggested in this Github thread

Reason for failure:

  • The password time limit got expired

One of the suggested workarounds I followed (and worked):

  • Create a tar file of the container named registry
  • Extract its /etc/shadow file
  • Change the timelimit expiry date from 90 days to a larger number (99999 days)
  • Copy that /etc/shadow file to a know location (/opt/harbor-fix)
  • Modify docker-compose.yml file , pass /opt/harbox-fix/shadow file to the failing containers namely regsitry, registryctl and harbor-log as voume mount
  • Restart harbor registry application stack with 'docker-compose' command
# mkdir -p /tmp/harbor-fix /opt/harbor-fix

# cd /tmp/harbor-fix

# docker export registry -o registry.tar

# tar xvfp registry.tar

# sed -i 's/:90:/:99999:/g' /tmp/harbor-fix/etc/shadow

# cp /tmp/harbor-fix/etc/shadow /opt/harbor-fix
# cd /root/harbor

# vi docker-compose.yml
-----
...
...
container_name: harbor-log -----> add lines below to this Container
...
...
volumes:
- /var/log/harbor/:/var/log/docker/:z
- ./common/config/log/logrotate.conf:/etc/logrotate.d/logrotate.conf:z
- ./common/config/log/rsyslog_docker.conf:/etc/rsyslog.d/rsyslog_docker.conf:z
- type: bind ------------------------------------------------------------------>> Add this line
source: /opt/harbor-fix/shadow ------------------------------------------------>> Add this line
target: /etc/shadow ----------------------------------------------------------->> Add this line
...
...
container_name: registry -----> add lines below to this Container
...
...
volumes:
- /data/registry:/storage:z
- ./common/config/registry/:/etc/registry/:z
- type: bind
source: /data/secret/registry/root.crt
target: /etc/registry/root.crt
- type: bind ------------------------------------------------------------------>> Add this line
source: /opt/harbor-fix/shadow ------------------------------------------------>> Add this line
target: /etc/shadow ----------------------------------------------------------->> Add this line
...
...
container_name: registryctl -----> add lines below to this Container
...
...
volumes:
- /data/registry:/storage:z
- ./common/config/registry/:/etc/registry/:z
- type: bind
source: ./common/config/registryctl/config.yml
target: /etc/registryctl/config.yml
- type: bind ------------------------------------------------------------------>> Add this line
source: /opt/harbor-fix/shadow ------------------------------------------------>> Add this line
target: /etc/shadow ----------------------------------------------------------->> Add this line
...
...
-----

Step 10: Restart Harbor Registry application stack with docker-compose command

# docker-compose down

# docker-compoe up -d

[root@raspi4-rocky harbor]# docker-compose up -d
[+] Running 10/10
⠿ Network harbor_harbor Created 0.3s
⠿ Container harbor-log Started 9.7s
⠿ Container harbor-db Started 11.8s
⠿ Container redis Started 12.5s
⠿ Container harbor-portal Started 12.7s
⠿ Container registry Started 12.7s
⠿ Container registryctl Started 11.1s
⠿ Container harbor-core Started 12.3s
⠿ Container nginx Started 16.1s
⠿ Container harbor-jobservice Started 16.0s

Step 11: Verify all the Containers are running fine with the workaround in place

# docker ps -a

root@rockpi4c:~/harbor# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
98be1d472ed9 goharbor/nginx-photon:v1.9.3 "nginx -g 'daemon of…" 28 seconds ago Up 20 seconds (health: starting) 0.0.0.0:8080->8080/tcp nginx
39da2b2ce6bb goharbor/harbor-jobservice:v1.9.3 "/harbor/harbor_jobs…" 28 seconds ago Up 20 seconds (health: starting) harbor-jobservice
ef4332b973a7 goharbor/harbor-core:v1.9.3 "/harbor/harbor_core" 29 seconds ago Up 22 seconds (health: starting) harbor-core
88c594e06aa1 goharbor/redis-photon:v1.9.3 "docker-entrypoint.s…" 29 seconds ago Up 24 seconds (health: starting) 6379/tcp redis
d1a8af7558a7 goharbor/harbor-portal:v1.9.3 "nginx -g 'daemon of…" 29 seconds ago Up 23 seconds (health: starting) 8080/tcp harbor-portal
b181227c237b goharbor/registry-photon:v2.7.1-patch-2819-2553-v1.9.3 "/entrypoint.sh /etc…" 29 seconds ago Up 24 seconds (health: starting) 5000/tcp registry
bea097cb3417 goharbor/harbor-db:v1.9.3 "/docker-entrypoint.…" 29 seconds ago Up 24 seconds (health: starting) 5432/tcp harbor-db
65d3ad835462 goharbor/harbor-registryctl:v1.9.3 "/harbor/start.sh" 29 seconds ago Up 23 seconds (health: starting) registryctl
7b181b99a0e9 goharbor/harbor-log:v1.9.3 "/bin/sh -c /usr/loc…" 29 seconds ago Up 26 seconds (health: starting) 127.0.0.1:1514->10514/tcp harbor-log

Video capturing output of 'docker-compose up -d'

Step 12: Install Portainer with a single command line

# docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce
Unable to find image 'portainer/portainer-ce:latest' locally
latest: Pulling from portainer/portainer-ce
7721cab3d696: Pull complete
0645e7e2a110: Pull complete
398e449ccacc: Pull complete
Digest: sha256:af387baba14e0342e40d274c0c894fd333d3cca0d6737a8e1e0d6d9523c87a8a
Status: Downloaded newer image for portainer/portainer-ce:latest
378d7204cd69279d823ab9ae9aa9f31a512fd84d9608cbd377bb332fdd721f75

Log into Harbor Registry portal with the default credentials : admin / Harbor12345

Log into Portainer portal

Output of 'htop' command with all the Containers running

portainer-installed