diff --git a/documentation/harbor-registry.md b/documentation/harbor-registry.md new file mode 100644 index 0000000..a5b2fdc --- /dev/null +++ b/documentation/harbor-registry.md @@ -0,0 +1,427 @@ +# Harbor Registry Deployment + +## Overview + +This guide covers building and pushing FlaskPaste container images to a Harbor +registry, and deploying from Harbor to Kubernetes. + +Harbor is an open-source container registry with security features including +vulnerability scanning, image signing, and role-based access control. + +--- + +## Prerequisites + +- Harbor registry deployed and accessible +- Container tools: `podman`, `docker`, or `ctr` (containerd) +- Network access to Harbor (direct or via SSH tunnel) + +--- + +## Registry Configuration + +### Harbor Details + +``` +Registry: 192.168.122.154:30443 +Project: library (public) +Repository: library/flaskpaste +``` + +### Authentication + +```bash +# Default credentials (change in production!) +Username: admin +Password: Harbor12345 +``` + +--- + +## Building Images + +### Local Build + +```bash +# Build with Podman +podman build -t flaskpaste:latest . + +# Build with Docker +docker build -t flaskpaste:latest . + +# Verify the image +podman images | grep flaskpaste +``` + +### Multi-architecture Build + +```bash +# Build for multiple architectures +podman manifest create flaskpaste:latest +podman build --platform linux/amd64 -t flaskpaste:amd64 . +podman build --platform linux/arm64 -t flaskpaste:arm64 . +podman manifest add flaskpaste:latest flaskpaste:amd64 +podman manifest add flaskpaste:latest flaskpaste:arm64 +``` + +--- + +## Pushing to Harbor + +### Direct Push (Network Access) + +```bash +# Login to Harbor (skip TLS verify for self-signed certs) +podman login 192.168.122.154:30443 \ + -u admin -p Harbor12345 \ + --tls-verify=false + +# Tag for Harbor +podman tag localhost/flaskpaste:latest \ + 192.168.122.154:30443/library/flaskpaste:latest + +# Push +podman push 192.168.122.154:30443/library/flaskpaste:latest \ + --tls-verify=false +``` + +### Push via SSH Tunnel + +When Harbor is on an internal network: + +```bash +# Set up SSH port forward +ssh -f -N -L 30443:192.168.122.154:30443 jumphost + +# Login and push via localhost +podman login localhost:30443 -u admin -p Harbor12345 --tls-verify=false +podman tag localhost/flaskpaste:latest localhost:30443/library/flaskpaste:latest +podman push localhost:30443/library/flaskpaste:latest --tls-verify=false +``` + +### Push via Image Transfer + +When direct push isn't possible: + +```bash +# Save image to tar +podman save localhost/flaskpaste:latest -o flaskpaste.tar + +# Copy to machine with Harbor access +scp flaskpaste.tar user@k8s-master:/tmp/ + +# On k8s-master: Import and push via containerd +ssh user@k8s-master ' + sudo ctr -n k8s.io images import /tmp/flaskpaste.tar + sudo ctr -n k8s.io images tag \ + localhost/flaskpaste:latest \ + 192.168.122.154:30443/library/flaskpaste:latest + sudo ctr -n k8s.io images push --skip-verify \ + --user admin:Harbor12345 \ + 192.168.122.154:30443/library/flaskpaste:latest +' +``` + +--- + +## Pulling from Harbor + +### Podman/Docker + +```bash +podman pull 192.168.122.154:30443/library/flaskpaste:latest \ + --tls-verify=false +``` + +### Containerd (Kubernetes nodes) + +```bash +sudo ctr -n k8s.io images pull --skip-verify \ + --user admin:Harbor12345 \ + 192.168.122.154:30443/library/flaskpaste:latest +``` + +--- + +## Kubernetes Deployment + +### Configure Registry Trust + +For nodes to pull from Harbor with self-signed certificates: + +**containerd** (`/etc/containerd/config.toml`): + +```toml +[plugins."io.containerd.grpc.v1.cri".registry.configs] + [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.122.154:30443"] + [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.122.154:30443".tls] + insecure_skip_verify = true + [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.122.154:30443".auth] + username = "admin" + password = "Harbor12345" +``` + +Restart containerd after changes: +```bash +sudo systemctl restart containerd +``` + +### Create Image Pull Secret + +```bash +kubectl create secret docker-registry harbor-creds \ + --docker-server=192.168.122.154:30443 \ + --docker-username=admin \ + --docker-password=Harbor12345 \ + --docker-email=admin@example.com +``` + +### Deployment Manifest + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: flaskpaste + labels: + app: flaskpaste +spec: + replicas: 2 + selector: + matchLabels: + app: flaskpaste + template: + metadata: + labels: + app: flaskpaste + spec: + imagePullSecrets: + - name: harbor-creds + containers: + - name: flaskpaste + image: 192.168.122.154:30443/library/flaskpaste:latest + ports: + - containerPort: 5000 + env: + - name: FLASK_ENV + value: "production" + - name: FLASKPASTE_DB + value: "/app/data/pastes.db" + volumeMounts: + - name: data + mountPath: /app/data + livenessProbe: + httpGet: + path: /health + port: 5000 + initialDelaySeconds: 10 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /health + port: 5000 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "500m" + volumes: + - name: data + persistentVolumeClaim: + claimName: flaskpaste-data +--- +apiVersion: v1 +kind: Service +metadata: + name: flaskpaste +spec: + selector: + app: flaskpaste + ports: + - port: 80 + targetPort: 5000 + type: ClusterIP +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: flaskpaste-data +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +``` + +### Deploy + +```bash +kubectl apply -f flaskpaste-k8s.yaml + +# Verify deployment +kubectl get pods -l app=flaskpaste +kubectl logs -l app=flaskpaste +``` + +--- + +## CI/CD Integration + +### Gitea Actions Example + +```yaml +name: Build and Push to Harbor + +on: + push: + branches: [main] + tags: ['v*'] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + run: | + git clone --depth 1 --branch "${GITHUB_REF_NAME}" \ + "https://oauth2:${{ github.token }}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git" . + + - name: Build image + run: | + podman build -t flaskpaste:${{ github.sha }} . + podman tag flaskpaste:${{ github.sha }} flaskpaste:latest + + - name: Push to Harbor + env: + HARBOR_USER: ${{ secrets.HARBOR_USER }} + HARBOR_PASS: ${{ secrets.HARBOR_PASS }} + run: | + podman login 192.168.122.154:30443 \ + -u "$HARBOR_USER" -p "$HARBOR_PASS" --tls-verify=false + + podman tag flaskpaste:${{ github.sha }} \ + 192.168.122.154:30443/library/flaskpaste:${{ github.sha }} + podman tag flaskpaste:latest \ + 192.168.122.154:30443/library/flaskpaste:latest + + podman push 192.168.122.154:30443/library/flaskpaste:${{ github.sha }} \ + --tls-verify=false + podman push 192.168.122.154:30443/library/flaskpaste:latest \ + --tls-verify=false +``` + +--- + +## Harbor API + +### Check Image Exists + +```bash +curl -k -s -u admin:Harbor12345 \ + "https://192.168.122.154:30443/api/v2.0/projects/library/repositories/flaskpaste/artifacts" \ + | jq '.[] | {digest: .digest, tags: [.tags[].name], size: .size}' +``` + +### List Tags + +```bash +curl -k -s -u admin:Harbor12345 \ + "https://192.168.122.154:30443/api/v2.0/projects/library/repositories/flaskpaste/artifacts" \ + | jq -r '.[].tags[].name' +``` + +### Delete Old Tags + +```bash +# Delete specific tag +curl -k -X DELETE -u admin:Harbor12345 \ + "https://192.168.122.154:30443/api/v2.0/projects/library/repositories/flaskpaste/artifacts/v1.0.0" +``` + +--- + +## Troubleshooting + +### Connection Refused + +```bash +# Check Harbor is running +kubectl get pods -n harbor + +# Check service is exposed +kubectl get svc -n harbor + +# Test connectivity +curl -k https://192.168.122.154:30443/api/v2.0/health +``` + +### Authentication Failed + +```bash +# Verify credentials via API +curl -k -u admin:Harbor12345 \ + https://192.168.122.154:30443/api/v2.0/users/current + +# Check if project exists +curl -k -u admin:Harbor12345 \ + https://192.168.122.154:30443/api/v2.0/projects +``` + +### TLS Certificate Errors + +```bash +# For podman/docker: use --tls-verify=false +# For ctr: use --skip-verify +# For curl: use -k + +# To properly trust the cert, export Harbor's CA: +kubectl get secret -n harbor harbor-nginx \ + -o jsonpath='{.data.ca\.crt}' | base64 -d > harbor-ca.crt +sudo cp harbor-ca.crt /etc/pki/ca-trust/source/anchors/ +sudo update-ca-trust +``` + +### Image Pull Failures in Kubernetes + +```bash +# Check secret exists +kubectl get secret harbor-creds -o yaml + +# Verify pod can reach Harbor +kubectl run test --rm -it --image=alpine -- \ + wget -qO- --no-check-certificate https://192.168.122.154:30443/api/v2.0/health + +# Check events +kubectl describe pod | grep -A10 Events +``` + +--- + +## Version Tagging Strategy + +``` +latest - Most recent build from main branch +v1.2.3 - Semantic version releases +sha-abc123 - Git commit SHA (for traceability) +main-20260117 - Branch + date (for nightly builds) +``` + +Example workflow: +```bash +VERSION="1.0.0" +COMMIT=$(git rev-parse --short HEAD) + +podman build -t flaskpaste . +podman tag flaskpaste 192.168.122.154:30443/library/flaskpaste:v${VERSION} +podman tag flaskpaste 192.168.122.154:30443/library/flaskpaste:sha-${COMMIT} +podman tag flaskpaste 192.168.122.154:30443/library/flaskpaste:latest + +for tag in v${VERSION} sha-${COMMIT} latest; do + podman push 192.168.122.154:30443/library/flaskpaste:${tag} --tls-verify=false +done +```