Replace Docker Registry with Harbor
Some checks are pending
CI/CD Pipeline / Test Backend (push) Waiting to run
CI/CD Pipeline / Test Frontend (push) Waiting to run
CI/CD Pipeline / Build and Push Docker Images (push) Blocked by required conditions
CI/CD Pipeline / Deploy to Production (push) Blocked by required conditions

This commit is contained in:
continuist 2025-06-29 00:56:05 -04:00
parent 88b07e2930
commit d26fc3df93
6 changed files with 695 additions and 192 deletions

View file

@ -8,7 +8,7 @@ This guide covers setting up a complete Continuous Integration/Continuous Deploy
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Forgejo Host │ │ CI/CD Linode │ │ Production Linode│
│ (Repository) │ │ (Actions Runner)│ │ (Docker Deploy) │
│ │ │ + Docker Registry│ │ │
│ │ │ + Harbor Registry│ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ │ │
@ -22,7 +22,7 @@ This guide covers setting up a complete Continuous Integration/Continuous Deploy
1. **Code Push**: Developer pushes code to Forgejo repository
2. **Automated Testing**: CI/CD Linode runs tests on backend and frontend
3. **Image Building**: If tests pass, Docker images are built
4. **Registry Push**: Images are pushed to private registry on CI/CD Linode
4. **Registry Push**: Images are pushed to Harbor registry on CI/CD Linode
5. **Production Deployment**: Production Linode pulls images and deploys
6. **Health Check**: Application is verified and accessible
@ -45,9 +45,10 @@ This guide covers setting up a complete Continuous Integration/Continuous Deploy
### CI/CD Linode Features
- Forgejo Actions runner for automated builds
- Local Docker registry for image storage
- Registry web UI for image management
- Automated cleanup of old images
- Harbor container registry for image storage
- Harbor web UI for image management
- Built-in vulnerability scanning with Trivy
- Role-based access control and audit logs
- Secure SSH communication with production
### Production Linode Features
@ -452,9 +453,9 @@ sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker SERVICE_USER
```
### Step 5: Set Up Docker Registry
### Step 5: Set Up Harbor Container Registry
#### 5.1 Create Registry Directory
#### 5.1 Create Harbor Directory
```bash
sudo mkdir -p /opt/registry
@ -464,7 +465,7 @@ sudo chown SERVICE_USER:SERVICE_USER /opt/registry
#### 5.2 Generate SSL Certificates
```bash
# Create system SSL directory for registry certificates
# Create system SSL directory for Harbor certificates
sudo mkdir -p /etc/ssl/registry
# Get your actual IP address
@ -485,23 +486,7 @@ sudo chmod 644 /etc/ssl/registry/registry.crt
- `registry.key`: `600` (owner read/write only) - private key must be secure
- `registry.crt`: `644` (owner read/write, group/others read) - certificate can be read by services
#### 5.3 Create Authentication File
```bash
# Create system auth directory for registry authentication
sudo mkdir -p /etc/registry/auth
# Create htpasswd file for authentication (required for push operations only)
sudo htpasswd -Bbn push-user "$(openssl rand -base64 32)" > /tmp/auth.htpasswd
sudo mv /tmp/auth.htpasswd /etc/registry/auth/auth.htpasswd
```
**What this does**: Creates user credentials for registry authentication in the system auth directory.
- `push-user`: Can push images (used by CI/CD pipeline for deployments)
**Note**: Pull operations are public and don't require authentication, but push operations require these credentials.
#### 5.4 Update Configuration with Actual IP Address and Application Name
#### 5.3 Update Harbor Configuration with Actual IP Address
```bash
# Switch to SERVICE_USER (registry directory owner)
@ -513,9 +498,9 @@ cd /opt/APP_NAME/registry
YOUR_ACTUAL_IP=$(curl -4 -s ifconfig.me)
echo "Your IP address is: $YOUR_ACTUAL_IP"
# Replace placeholder IP addresses in configuration files
# Replace placeholder IP addresses in Harbor configuration files
sed -i "s/YOUR_CI_CD_IP/$YOUR_ACTUAL_IP/g" harbor.yml
sed -i "s/YOUR_CI_CD_IP/$YOUR_ACTUAL_IP/g" docker-compose.yml
sed -i "s/YOUR_CI_CD_IP/$YOUR_ACTUAL_IP/g" nginx.conf
# Replace placeholder application name in configuration files
sed -i "s/APP_NAME/ACTUAL_APP_NAME/g" docker-compose.yml
@ -524,16 +509,26 @@ sed -i "s/APP_NAME/ACTUAL_APP_NAME/g" docker-compose.yml
exit
```
**Important**: This step replaces all instances of `YOUR_CI_CD_IP` with your actual CI/CD Linode IP address and all instances of `APP_NAME` with the actual application name in both the docker-compose.yml and nginx.conf files in the repository.
**Important**: This step replaces all instances of `YOUR_CI_CD_IP` with your actual CI/CD Linode IP address and all instances of `APP_NAME` with the actual application name in the Harbor configuration files.
#### 5.5 Install Required Tools
#### 5.4 Set Harbor Environment Variables
```bash
# Install htpasswd utility
sudo apt install -y apache2-utils
# Set environment variables for Harbor
export HARBOR_HOSTNAME=$YOUR_ACTUAL_IP
export HARBOR_ADMIN_PASSWORD="Harbor12345"
export DB_PASSWORD="your-db-password"
# Update Harbor configuration with secure passwords
cd /opt/APP_NAME/registry
sed -i "s/Harbor12345/$HARBOR_ADMIN_PASSWORD/g" harbor.yml
sed -i "s/your-db-password/$DB_PASSWORD/g" harbor.yml
sed -i "s/your-db-password/$DB_PASSWORD/g" docker-compose.yml
```
#### 5.6 Start Registry
**Important**: Change the default passwords for production use. The default admin password is `Harbor12345` - change this immediately after first login.
#### 5.5 Start Harbor
```bash
# Switch to SERVICE_USER (registry directory owner)
@ -546,111 +541,133 @@ docker compose up -d
exit
```
**Important**: The registry uses standard authentication, but nginx provides intelligent routing to enable public read access for specific operations (manifests, blobs, tags) while requiring authentication for write operations (push, delete). This implements the "public read, authenticated write" model through nginx configuration. The health check is performed on nginx (port 8080) since it handles the public access logic.
**Important**: Harbor startup can take 2-3 minutes as it initializes the database and downloads vulnerability databases. The health check will ensure all services are running properly.
#### 5.7 Test Registry Setup
#### 5.6 Wait for Harbor Startup
```bash
# Check if containers are running
# Monitor Harbor startup progress
cd /opt/APP_NAME/registry
docker compose logs -f
```
**Expected output**: You should see logs from all Harbor services (core, database, redis, registry, portal, nginx, jobservice, trivy) starting up. Wait until you see "Harbor has been installed and started successfully" or similar success messages.
#### 5.7 Test Harbor Setup
```bash
# Check if all Harbor containers are running
cd /opt/APP_NAME/registry
docker compose ps
# Test registry API (HTTPS via nginx)
curl -k https://localhost:8080/v2/_catalog
# Test Harbor API (HTTPS)
curl -k https://localhost:8080/api/v2.0/health
# Test registry UI (HTTPS via nginx)
curl -I https://localhost:8080
# Test Harbor UI (HTTPS)
curl -k -I https://localhost:8080
# Test Docker push/pull (optional but recommended)
# Create a test image
echo "FROM alpine:latest" > /tmp/test.Dockerfile
echo "RUN echo 'Hello from test image'" >> /tmp/test.Dockerfile
# Build and tag test image
docker build -f /tmp/test.Dockerfile -t localhost:8080/test:latest /tmp
# Push to registry
docker push localhost:8080/test:latest
# Verify image is in registry
curl -k https://localhost:8080/v2/_catalog
curl -k https://localhost:8080/v2/test/tags/list
# Pull image back (verifies pull works)
docker rmi localhost:8080/test:latest
docker pull localhost:8080/test:latest
# Clean up test image completely
# Remove from local Docker
docker rmi localhost:8080/test:latest
# Clean up test file
rm /tmp/test.Dockerfile
# Get the manifest digest for the 'latest' tag
curl -k -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
https://localhost:8080/v2/test/manifests/latest
# Copy the "config.digest" value from the output above (starts with "sha256:")
# Then delete the tag using that digest:
curl -k -X DELETE https://localhost:8080/v2/test/manifests/<digest>
# Run garbage collection to remove orphaned blobs
docker compose exec registry /bin/registry garbage-collect /etc/docker/registry/config.yml --delete-untagged
# Remove the repository directory structure
docker compose exec registry rm -rf /var/lib/registry/docker/registry/v2/repositories/test
# Verify registry is empty
echo "Verifying registry is now empty..."
curl -k https://localhost:8080/v2/_catalog
# Exit SERVICE_USER shell
exit
# Expected output: HTTP/1.1 200 OK
```
**Important Notes:**
- **Registry API**: Uses HTTPS on port 5000 (secure)
- **Registry UI**: Uses HTTPS on port 8080 (secure, via nginx reverse proxy)
- **Access URLs**:
- Registry UI: `https://YOUR_CI_CD_IP:8080` (use HTTPS)
- Registry API: `https://YOUR_CI_CD_IP:5000`
- **Browser Access**: Both services now use HTTPS for secure communication
**Important**: All Harbor services should show as "Up" in the `docker compose ps` output. The health check should return a JSON response indicating all services are healthy.
**Expected Output**:
- `docker-compose ps` should show both `registry` and `registry-ui` as "Up"
- `curl -k https://localhost:5000/v2/_catalog` should return `{"repositories":[]}` (empty initially)
- `curl -I https://localhost:8080` should return HTTP 200
- Push/pull test should complete successfully
#### 5.8 Access Harbor Web UI
**If something goes wrong**:
- Check container logs: `docker compose logs`
- Verify ports are open: `netstat -tlnp | grep :5000`
- Check Docker daemon config: `cat /etc/docker/daemon.json`
- Restart registry: `docker compose restart`
1. **Open your browser** and navigate to: `https://YOUR_CI_CD_IP:8080`
2. **Login with default credentials**:
- Username: `admin`
- Password: `Harbor12345` (or your configured password)
3. **Change the admin password** when prompted (required on first login)
### Step 6: Configure Docker for Registry Access
#### 5.9 Configure Harbor for Public Read, Authenticated Write
#### 6.1 Configure Docker for Registry Access
1. **Create a Public Project**:
- Go to **Projects** → **New Project**
- Set **Project Name**: `public`
- Set **Access Level**: `Public`
- Click **OK**
2. **Create a Private Project** (for authenticated writes):
- Go to **Projects** → **New Project**
- Set **Project Name**: `private`
- Set **Access Level**: `Private`
- Click **OK**
3. **Create a User for CI/CD**:
- Go to **Administration****Users** → **New User**
- Set **Username**: `ci-user`
- Set **Email**: `ci@example.com`
- Set **Password**: `your-secure-password`
- Set **Role**: `Developer`
- Click **OK**
#### 5.10 Test Harbor Authentication and Access Model
```bash
# Get the push user credentials
PUSH_USER="push-user"
PUSH_PASSWORD=$(grep push-user /etc/registry/auth/auth.htpasswd | cut -d: -f2)
# Test Docker login to Harbor
docker login YOUR_CI_CD_IP:8080
# Enter: ci-user and your-secure-password
# Create a test image
echo "FROM alpine:latest" > /tmp/test.Dockerfile
echo "RUN echo 'Hello from Harbor test image'" >> /tmp/test.Dockerfile
# Build and tag test image for public project
docker build -f /tmp/test.Dockerfile -t YOUR_CI_CD_IP:8080/public/test:latest /tmp
# Push to Harbor (requires authentication)
docker push YOUR_CI_CD_IP:8080/public/test:latest
# Verify image is in Harbor
curl -k https://localhost:8080/v2/_catalog
# Test public pull (no authentication required)
docker logout YOUR_CI_CD_IP:8080
docker pull YOUR_CI_CD_IP:8080/public/test:latest
# Clean up test image
docker rmi YOUR_CI_CD_IP:8080/public/test:latest
```
**Expected behavior**:
- ✅ **Push requires authentication**: `docker push` only works when logged in
- ✅ **Pull works without authentication**: `docker pull` works without login for public projects
- ✅ **Web UI accessible**: Harbor UI is available at `https://YOUR_CI_CD_IP:8080`
#### 5.11 Harbor Access Model Summary
Your Harbor registry is now configured with the following access model:
**Public Projects** (like `public`):
- ✅ **Pull (read)**: No authentication required
- ✅ **Push (write)**: Requires authentication
- ✅ **Web UI**: Accessible to view images
**Private Projects** (like `private`):
- ✅ **Pull (read)**: Requires authentication
- ✅ **Push (write)**: Requires authentication
- ✅ **Web UI**: Requires authentication
**Security Features**:
- ✅ **Vulnerability scanning**: Automatic CVE scanning with Trivy
- ✅ **Role-based access control**: Different user roles (admin, developer, guest)
- ✅ **Audit logs**: Complete trail of all operations
- ✅ **Image signing**: Content trust features available
### Step 6: Configure Docker for Harbor Access
#### 6.1 Configure Docker for Harbor Access
```bash
# Copy the certificate to Docker's trusted certificates
sudo cp /etc/ssl/registry/registry.crt /usr/local/share/ca-certificates/registry.crt
sudo update-ca-certificates
# Configure Docker to trust Harbor registry
sudo tee /etc/docker/daemon.json << EOF
{
"insecure-registries": ["YOUR_CI_CD_IP:8080"],
"registry-mirrors": [],
"auths": {
"YOUR_CI_CD_IP:8080": {
"auth": "$(echo -n "${PUSH_USER}:${PUSH_PASSWORD}" | base64)"
}
}
"registry-mirrors": []
}
EOF
```
@ -663,40 +680,52 @@ EOF
sudo systemctl restart docker
```
### Public Registry Access Model
### Harbor Access Model
Your registry is now configured with the following access model:
Your Harbor registry is now configured with the following access model:
#### **Public Read Access**
Anyone can pull images without authentication:
Anyone can pull images from public projects without authentication:
```bash
# From any machine (public access)
docker pull YOUR_CI_CD_IP:5000/APP_NAME/backend:latest
docker pull YOUR_CI_CD_IP:5000/APP_NAME/frontend:latest
# From any machine (public access to public projects)
docker pull YOUR_CI_CD_IP:8080/public/backend:latest
docker pull YOUR_CI_CD_IP:8080/public/frontend:latest
```
#### **Authenticated Write Access**
Only the CI/CD Linode can push images (using credentials):
Only authenticated users can push images:
```bash
# From CI/CD Linode only (authenticated)
docker push YOUR_CI_CD_IP:5000/APP_NAME/backend:latest
docker push YOUR_CI_CD_IP:5000/APP_NAME/frontend:latest
# Login to Harbor first
docker login YOUR_CI_CD_IP:8080
# Enter: ci-user and your-secure-password
# Then push to Harbor
docker push YOUR_CI_CD_IP:8080/public/backend:latest
docker push YOUR_CI_CD_IP:8080/public/frontend:latest
```
#### **Registry UI Access**
Public web interface for browsing images:
#### **Harbor Web UI Access**
Modern web interface for managing images:
```
https://YOUR_CI_CD_IP:8080
```
#### **Client Configuration**
For other machines to pull images, they only need:
For other machines to pull images from public projects, they only need:
```bash
# Add to /etc/docker/daemon.json on client machines
{
"insecure-registries": ["YOUR_CI_CD_IP:5000"]
"insecure-registries": ["YOUR_CI_CD_IP:8080"]
}
# No authentication needed for pulls
# No authentication needed for pulls from public projects
```
#### **CI/CD Pipeline Configuration**
For automated deployments, use the `ci-user` credentials:
```bash
# In CI/CD pipeline
echo "ci-user:your-secure-password" | docker login YOUR_CI_CD_IP:8080 --username ci-user --password-stdin
docker push YOUR_CI_CD_IP:8080/public/backend:latest
```
### Step 7: Set Up SSH for Production Communication
@ -894,7 +923,7 @@ crontab -l
- **Runs automatically**: The cleanup script runs every day at 3:00 AM
- **Frequency**: Daily cleanup to prevent disk space issues
- **Logging**: All cleanup output is logged to `/tmp/cleanup.log`
- **What it cleans**: Unused Docker images, volumes, networks, and registry images
- **What it cleans**: Unused Docker images, volumes, networks, and Harbor images
### Step 10: Configure Firewall
@ -903,13 +932,11 @@ sudo ufw --force enable
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 5000/tcp # Docker registry (public read access)
sudo ufw allow 8080/tcp # Registry UI (public read access)
sudo ufw allow 8080/tcp # Harbor registry (public read access)
```
**Security Model**:
- **Port 5000 (Registry)**: Public read access, authenticated write access
- **Port 8080 (UI)**: Public read access for browsing images
- **Port 8080 (Harbor)**: Public read access for public projects, authenticated write access
- **SSH**: Restricted to your IP addresses
- **All other ports**: Blocked
@ -922,17 +949,21 @@ docker --version
docker compose --version
```
#### 11.2 Check Registry Status
#### 11.2 Check Harbor Status
```bash
cd /opt/registry
cd /opt/APP_NAME/registry
docker compose ps
```
#### 11.3 Test Registry Access
#### 11.3 Test Harbor Access
```bash
curl http://localhost:5000/v2/_catalog
# Test Harbor API
curl -k https://localhost:8080/api/v2.0/health
# Test Harbor UI
curl -k -I https://localhost:8080
```
#### 11.4 Get Public Key for Production Server
@ -1142,7 +1173,7 @@ The repository doesn't include a `.env.example` file for security reasons. The C
cat > /opt/APP_NAME/.env << 'EOF'
# Production Environment Variables
POSTGRES_PASSWORD=your_secure_password_here
REGISTRY=YOUR_CI_CD_IP:5000
REGISTRY=YOUR_CI_CD_IP:8080
IMAGE_NAME=APP_NAME
IMAGE_TAG=latest
@ -1160,14 +1191,14 @@ EOF
**Important**: Replace `YOUR_CI_CD_IP` with your actual CI/CD Linode IP address and `your_secure_password_here` with a strong password.
#### 18.4 Configure Docker for Registry Access
#### 18.4 Configure Docker for Harbor Access
```bash
# Add the CI/CD registry to Docker's insecure registries
# Add the CI/CD Harbor registry to Docker's insecure registries
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json << EOF
{
"insecure-registries": ["YOUR_CI_CD_IP:5000"]
"insecure-registries": ["YOUR_CI_CD_IP:8080"]
}
EOF
@ -1212,11 +1243,11 @@ docker --version
docker compose --version
```
#### 20.2 Test Registry Access
#### 20.2 Test Harbor Access
```bash
# Test pulling an image from the CI/CD registry
docker pull YOUR_CI_CD_IP:5000/APP_NAME/backend:latest
# Test pulling an image from the CI/CD Harbor registry
docker pull YOUR_CI_CD_IP:8080/public/backend:latest
```
**Important**: Replace `YOUR_CI_CD_IP` with your actual CI/CD Linode IP address.
@ -1431,7 +1462,7 @@ crontab -l
- **Runs automatically**: The cleanup script runs every day at 3:00 AM
- **Frequency**: Daily cleanup to prevent disk space issues
- **Logging**: All cleanup output is logged to `/tmp/cleanup.log`
- **What it cleans**: Unused Docker images, volumes, networks, and registry images
- **What it cleans**: Unused Docker images, volumes, networks, and Harbor images
### Step 23: Test Complete Pipeline
@ -1453,7 +1484,7 @@ The pipeline should execute these steps in order:
6. **Push to Registry**: Push images to your private registry
7. **Deploy to Production**: Deploy to production server
#### 23.3 Check Registry
#### 23.3 Check Harbor
```bash
# On CI/CD Linode
@ -1463,8 +1494,8 @@ cd /opt/APP_NAME/registry
curl -k https://localhost:8080/v2/_catalog
# Check specific repository tags
curl -k https://localhost:8080/v2/APP_NAME/backend/tags/list
curl -k https://localhost:8080/v2/APP_NAME/frontend/tags/list
curl -k https://localhost:8080/v2/public/backend/tags/list
curl -k https://localhost:8080/v2/public/frontend/tags/list
```
#### 23.4 Verify Production Deployment
@ -1600,7 +1631,7 @@ tail -f /tmp/monitor.log
You have successfully set up a complete CI/CD pipeline with:
- ✅ **Automated testing** on every code push
- ✅ **Docker image building** and registry storage
- ✅ **Docker image building** and Harbor registry storage
- ✅ **Automated deployment** to production
- ✅ **Health monitoring** and logging
- ✅ **Backup and cleanup** automation

View file

@ -50,3 +50,188 @@ The setup process will:
- Configuration is version controlled and easily auditable
- All communication uses HTTPS
- Clear separation between config, auth, and data
# Harbor Registry Setup
This directory contains the configuration for Harbor, an enterprise-grade container registry that provides:
- **Public read access** - Anyone can pull images without authentication
- **Authenticated write access** - Only authenticated users can push images
- **Web UI** - Modern web interface for managing images
- **Vulnerability scanning** - Built-in security scanning with Trivy
- **Role-based access control** - Fine-grained permissions
- **Multi-tenancy** - Project-based organization
## Prerequisites
1. Docker and Docker Compose installed
2. SSL certificates for HTTPS (recommended for production)
3. At least 4GB RAM and 10GB disk space
## Configuration
### 1. Update Configuration Files
Before starting Harbor, update the following files:
- `harbor.yml`: Update `hostname` and `harbor_admin_password`
- `docker-compose.yml`: Update secrets and passwords
### 2. SSL Certificates
Place your SSL certificates in `/etc/ssl/registry/`:
- `registry.crt` - SSL certificate
- `registry.key` - SSL private key
### 3. Environment Variables
Set the following environment variables:
```bash
export HARBOR_HOSTNAME=YOUR_CI_CD_IP
export HARBOR_ADMIN_PASSWORD=your-secure-password
export DB_PASSWORD=your-db-password
```
## Installation
1. **Stop existing registry** (if running):
```bash
docker compose down
```
2. **Start Harbor**:
```bash
docker compose up -d
```
3. **Wait for startup** (can take 2-3 minutes):
```bash
docker compose logs -f
```
## Initial Setup
1. **Access Harbor UI**: https://YOUR_CI_CD_IP:8080
2. **Login with default credentials**:
- Username: `admin`
- Password: `Harbor12345` (or your configured password)
3. **Change admin password** on first login
## Configuration for Public Read, Authenticated Write
### 1. Create a Public Project
1. Go to **Projects** → **New Project**
2. Set **Project Name**: `public`
3. Set **Access Level**: `Public`
4. Click **OK**
### 2. Create a Private Project (for authenticated writes)
1. Go to **Projects** → **New Project**
2. Set **Project Name**: `private`
3. Set **Access Level**: `Private`
4. Click **OK**
### 3. Create Users
1. Go to **Administration****Users** → **New User**
2. Create users with appropriate roles:
- **Developer**: Can push/pull to private projects
- **Guest**: Can only pull from public projects
## Usage
### Docker Login
```bash
docker login YOUR_CI_CD_IP:8080
```
### Push Images
```bash
# Tag your image
docker tag myimage:latest YOUR_CI_CD_IP:8080/public/myimage:latest
# Push to public project (requires authentication)
docker push YOUR_CI_CD_IP:8080/public/myimage:latest
```
### Pull Images
```bash
# Pull from public project (no authentication required)
docker pull YOUR_CI_CD_IP:8080/public/myimage:latest
```
## Security Features
- **Vulnerability Scanning**: Automatic CVE scanning with Trivy
- **Image Signing**: Content trust and image signing
- **RBAC**: Role-based access control
- **Audit Logs**: Complete audit trail of all operations
## Maintenance
### Backup
```bash
# Backup Harbor data
docker compose exec harbor-db pg_dump -U postgres registry > backup.sql
```
### Update
```bash
# Pull latest images
docker compose pull
# Restart services
docker compose up -d
```
### Logs
```bash
# View all logs
docker compose logs
# View specific service logs
docker compose logs harbor-core
```
## Troubleshooting
### Common Issues
1. **Startup takes too long**: Harbor needs time to initialize database and download vulnerability databases
2. **SSL certificate errors**: Ensure certificates are properly placed and have correct permissions
3. **Authentication issues**: Check user permissions and project access levels
### Health Check
```bash
# Check service status
docker compose ps
# Check Harbor health
curl -k https://YOUR_CI_CD_IP:8080/api/v2.0/health
```
## Migration from Docker Registry
If migrating from the previous Docker Registry setup:
1. **Backup existing images**:
```bash
docker pull YOUR_OLD_REGISTRY/image:tag
```
2. **Push to Harbor**:
```bash
docker tag YOUR_OLD_REGISTRY/image:tag YOUR_CI_CD_IP:8080/public/image:tag
docker push YOUR_CI_CD_IP:8080/public/image:tag
```
3. **Update CI/CD pipelines** to use new registry URL
## Resources
- [Harbor Documentation](https://goharbor.io/docs/)
- [Harbor GitHub](https://github.com/goharbor/harbor)
- [CNCF Harbor](https://landscape.cncf.io/card-mode?category=container-registry&grouping=category&selected=harbor)

View file

@ -1,51 +1,139 @@
version: '3.8'
services:
registry:
image: registry:2
volumes:
- /opt/APP_NAME/registry/config.yml:/etc/docker/registry/config.yml:ro
- /etc/ssl/registry/registry.crt:/etc/docker/registry/ssl/registry.crt:ro
- /etc/ssl/registry/registry.key:/etc/docker/registry/ssl/registry.key:ro
- registry_data:/var/lib/registry
harbor-core:
image: goharbor/harbor-core:v2.10.0
container_name: harbor-core
restart: unless-stopped
networks:
- registry_network
registry-ui:
image: joxit/docker-registry-ui:latest
expose:
- "80"
depends_on:
- harbor-db
- harbor-redis
- harbor-registry
environment:
- REGISTRY_TITLE=ShareNet Registry
- REGISTRY_URL=https://YOUR_CI_CD_IP:8080
depends_on:
- registry
restart: unless-stopped
networks:
- registry_network
nginx:
image: nginx:alpine
ports:
- "8080:443"
- CONFIG_PATH=/etc/harbor/app.conf
- CORE_SECRET=your-core-secret
- JOBSERVICE_SECRET=your-jobservice-secret
- TRIVY_ADAPTER_URL=http://harbor-trivy:8080
- TRIVY_ADAPTER_URL_INSECURE=true
volumes:
- /etc/ssl/registry:/etc/nginx/ssl:ro
- /etc/registry/auth/auth.htpasswd:/etc/nginx/auth/auth.htpasswd:ro
- /opt/APP_NAME/registry/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- registry-ui
restart: unless-stopped
- ./harbor.yml:/etc/harbor/app.conf:ro
- harbor_core:/var/log/harbor
- harbor_data:/data
networks:
- registry_network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "https://localhost:443/v2/_catalog", "--no-check-certificate"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
- harbor
harbor-db:
image: goharbor/harbor-db:v2.10.0
container_name: harbor-db
restart: unless-stopped
environment:
- POSTGRES_DB=registry
- POSTGRES_PASSWORD=your-db-password
- POSTGRES_USER=postgres
volumes:
- harbor_db:/var/lib/postgresql/data
networks:
- harbor
harbor-redis:
image: goharbor/redis-photon:v2.10.0
container_name: harbor-redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- harbor_redis:/data
networks:
- harbor
harbor-registry:
image: goharbor/registry-photon:v2.10.0
container_name: harbor-registry
restart: unless-stopped
depends_on:
- harbor-core
environment:
- REGISTRY_STORAGE_DELETE_ENABLED=true
- REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR=inmemory
- REGISTRY_STORAGE_FILESYSTEM_MAXTHREADS=100
volumes:
- harbor_registry:/storage
- ./registry-config.yml:/etc/registry/config.yml:ro
networks:
- harbor
harbor-portal:
image: goharbor/harbor-portal:v2.10.0
container_name: harbor-portal
restart: unless-stopped
depends_on:
- harbor-core
environment:
- CONFIG_PATH=/etc/harbor/app.conf
volumes:
- ./harbor.yml:/etc/harbor/app.conf:ro
networks:
- harbor
harbor-nginx:
image: goharbor/nginx-photon:v2.10.0
container_name: harbor-nginx
restart: unless-stopped
ports:
- "8080:8080"
depends_on:
- harbor-core
- harbor-portal
- harbor-registry
volumes:
- ./harbor.yml:/etc/harbor/app.conf:ro
- /etc/ssl/registry:/etc/ssl/certs:ro
- harbor_nginx:/var/log/harbor
networks:
- harbor
harbor-jobservice:
image: goharbor/harbor-jobservice:v2.10.0
container_name: harbor-jobservice
restart: unless-stopped
depends_on:
- harbor-core
- harbor-redis
environment:
- CONFIG_PATH=/etc/harbor/app.conf
- CORE_SECRET=your-core-secret
- JOBSERVICE_SECRET=your-jobservice-secret
volumes:
- ./harbor.yml:/etc/harbor/app.conf:ro
- harbor_jobservice:/var/log/harbor
networks:
- harbor
harbor-trivy:
image: goharbor/trivy-adapter-photon:v2.10.0
container_name: harbor-trivy
restart: unless-stopped
depends_on:
- harbor-core
environment:
- CONFIG_PATH=/etc/harbor/app.conf
- CORE_SECRET=your-core-secret
volumes:
- ./harbor.yml:/etc/harbor/app.conf:ro
- harbor_trivy:/var/log/harbor
networks:
- harbor
volumes:
registry_data:
harbor_core:
harbor_data:
harbor_db:
harbor_redis:
harbor_registry:
harbor_portal:
harbor_nginx:
harbor_jobservice:
harbor_trivy:
networks:
registry_network:
harbor:
driver: bridge

177
registry/harbor.yml Normal file
View file

@ -0,0 +1,177 @@
# Configuration file of Harbor
# The IP address or hostname to access admin UI and registry service.
# DO NOT use localhost or 127.0.0.1, because Harbor needs to be accessed by external clients.
hostname: YOUR_CI_CD_IP
# http related config
http:
# port for http, default is 80. If https enabled, this port will redirect to https port
port: 8080
# https related config
https:
# https port for harbor, default is 443
port: 8080
# The path of cert and key files for nginx
certificate: /etc/ssl/certs/registry.crt
private_key: /etc/ssl/certs/registry.key
# Uncomment external_url if you want to enable external proxy
# And when it enabled the hostname will no longer used
# external_url: https://reg.mydomain.com:8433
# The initial password of Harbor admin
# Change it from default after updating for the first time
harbor_admin_password: Harbor12345
# Harbor DB configuration
database:
# The password for the root user of Harbor DB. Change this before any production use.
password: your-db-password
# The maximum number of connections in the idle connection pool. If it <=0, no idle connections are retained.
max_idle_conns: 50
# The maximum number of open connections to the database. If it <= 0, then there is no limit on the number of open connections.
# Note: the default number of connections is 100 for postgres.
max_open_conns: 100
# The default data volume
data_volume: /data
# Harbor Storage settings by default is using /data as default volume.
# Uncomment storage_service setting If you want to using external storage.
# storage_service:
# # ca_bundle is the path to the custom root ca certificate, which will be injected into the truststore
# # of registry's and chart repository's containers. This is usually needed when the user hosts a internal storage with self signed certificate.
# ca_bundle:
# # storage backend, default is filesystem, options include filesystem, azure, gcs, s3, swift and oss
# # for more info about this configuration please refer https://docs.docker.com/registry/configuration/
# filesystem:
# maxthreads: 100
# # set disable_cache to true want to disable cache of redis in registry
# # set disable_cache to false want to enable cache of redis in registry
# disable_cache: true
# Trivy configuration
trivy:
# enabled the flag to enable Trivy scanner
enabled: true
# ignore update if the CVEs are already in the whitelist. It only works when scanner is V1
ignore_unfixed: false
# skip update if the CVEs are already in the whitelist, It only works when scanner is V2
skip_update: false
# generate a scan report in the JSON format
json_output: false
# in online mode, Trivy will download the latest database from GitHub and scan it offline
# in offline mode, if the database does not exist locally, Trivy will exit with an error
offline_scan: false
# insecure Skip tls certificate verification
insecure: false
# github_token the github access token to download Trivy DB (see https://github.com/settings/tokens)
# This is required only when the GitHub rate limiting is exceeded
github_token: ""
# Jobservice configuration
jobservice:
# Maximum number of job workers in job service
max_job_workers: 10
# Notification configuration
notification:
# Maximum retry count for webhook job
webhook_job_max_retry: 10
# Log configurations
log:
# options are debug, info, warning, error, fatal
level: info
# configs for logs in local storage
local:
# Log files are rotated log_rotate_count times before being removed. If count is 0, old versions are removed rather than rotated.
rotate_count: 50
# Log files are rotated only if they grow bigger than log_rotate_size bytes. If size is followed by k, the size is assumed to be in kilobytes.
# If the M is followed by M, the size is assumed to be in megabytes. If the G is followed by G, the size is assumed to be in gigabytes. So size 100, size 100k, size 100M and size 100G are all valid.
rotate_size: 200M
# The directory on your host that store log
location: /var/log/harbor
# Uncomment following lines to enable external syslog endpoint.
# external_endpoint:
# # protocol used to transmit log to external endpoint, options is tcp or udp
# protocol: tcp
# # The host of external endpoint
# host: localhost
# # Port of external endpoint
# port: 5140
#This attribute is for migrator to detect the version of the .cfg file, DO NOT MODIFY!
_version: 2.10.0
# Uncomment external_database if using external database.
# external_database:
# harbor:
# host: harbor_db_host
# port: harbor_db_port
# db_name: harbor
# username: user
# password: password
# ssl_mode: disable
# max_idle_conns: 2
# max_open_conns: 0
# notary_signer:
# host: notary_signer_db_host
# port: notary_signer_db_port
# db_name: notary_signer
# username: user
# password: password
# ssl_mode: disable
# notary_server:
# host: notary_server_db_host
# port: notary_server_db_port
# db_name: notary_server
# username: user
# password: password
# ssl_mode: disable
# Uncomment redis if using external Redis server
# redis:
# host: redis_host
# port: redis_port
# password: redis_password
# # db_index 0 is for core, it's unchangeable
# registry_db_index: 1
# jobservice_db_index: 2
# chartmuseum_db_index: 3
# trivy_db_index: 5
# idle_timeout_seconds: 30
# Uncomment uaa for trusting the certificate of uaa instance that is hosted via self-signed cert.
# uaa:
# ca_file: /path/to/ca
# Global proxy settings
# http_proxy:
# https_proxy:
# no_proxy:
# - 127.0.0.1
# - localhost
# - core
# - redis
# - postgresql
# - notary-db
# - notary-signer
# - clair
# - trivy-adapter
# - trivy
# - chartmuseum
# - jobservice
# - registry
# - portal
# - log
# - nginx
# metric:
# enabled: false
# port: 9090
# path: /metrics

View file

@ -20,13 +20,15 @@ http {
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Public read access for all GET requests to registry API
# Registry API - require auth for non-GET requests only
location /v2/ {
# Require authentication for all non-GET requests (push, delete, etc.)
limit_except GET {
auth_basic "Registry Realm";
auth_basic_user_file /etc/nginx/auth/auth.htpasswd;
}
# Pass through to registry
proxy_pass https://registry_api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;

View file

@ -0,0 +1,20 @@
version: 0.1
log:
level: info
storage:
filesystem:
rootdirectory: /storage
delete:
enabled: true
cache:
blobdescriptor: inmemory
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
X-Frame-Options: [DENY]
X-XSS-Protection: [1; mode=block]
auth:
htpasswd:
realm: basic-realm
path: /etc/registry/auth/htpasswd