From fddc224e3b950f9449fe60ea08e95ec2292d770c Mon Sep 17 00:00:00 2001 From: continuist Date: Sun, 3 Aug 2025 11:22:59 -0400 Subject: [PATCH] Consolidate docker registry stuff --- CI_CD_PIPELINE_SETUP_GUIDE.md | 188 +++++++++++++-------------- registry/Caddyfile | 2 +- registry/docker-compose.registry.yml | 7 +- registry/docker-registry.service | 17 +++ 4 files changed, 113 insertions(+), 101 deletions(-) create mode 100644 registry/docker-registry.service diff --git a/CI_CD_PIPELINE_SETUP_GUIDE.md b/CI_CD_PIPELINE_SETUP_GUIDE.md index ea2c45c..811744a 100644 --- a/CI_CD_PIPELINE_SETUP_GUIDE.md +++ b/CI_CD_PIPELINE_SETUP_GUIDE.md @@ -66,6 +66,7 @@ This guide covers setting up a complete Continuous Integration/Continuous Deploy - Forgejo Actions runner for automated builds - **Docker-in-Docker (DinD) container** for isolated CI operations - Docker Registry with Caddy reverse proxy for image storage +- **FHS-compliant directory structure** for data, certificates, and logs - Unauthenticated pulls, authenticated pushes - Automatic HTTPS with Caddy - Secure SSH communication with production @@ -657,30 +658,29 @@ sudo usermod -aG docker CI_SERVICE_USER We'll set up a basic Docker Registry with Caddy as a reverse proxy, configured to allow unauthenticated pulls but require authentication for pushes. -#### 5.1 Configure Registry Directory for CI_SERVICE_USER +#### 5.1 Configure FHS-Compliant Registry Directories ```bash -# Create registry directory structure -sudo mkdir -p /opt/registry -sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry -sudo chmod 755 /opt/registry +# Create FHS-compliant directories for registry data and certificates +sudo mkdir -p /var/lib/registry +sudo mkdir -p /etc/registry/certs +sudo mkdir -p /var/log/registry +sudo chown CI_SERVICE_USER:CI_SERVICE_USER /var/lib/registry +sudo chown CI_SERVICE_USER:CI_SERVICE_USER /etc/registry/certs +sudo chown CI_SERVICE_USER:CI_SERVICE_USER /var/log/registry +sudo chmod 755 /var/lib/registry +sudo chmod 755 /etc/registry/certs +sudo chmod 755 /var/log/registry ``` #### 5.2 Create Docker Compose Setup ```bash -# Create registry directory structure (if not already created) -sudo mkdir -p /opt/registry -sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry -cd /opt/registry - -# Copy registry configuration from repository -# The registry folder contains the Docker Compose and Caddy configuration files -sudo cp /opt/APP_NAME/registry/docker-compose.registry.yml docker-compose.yml -sudo cp /opt/APP_NAME/registry/Caddyfile Caddyfile +# Navigate to the cloned application directory +cd /opt/APP_NAME/registry # Update Caddyfile with your actual IP address -sudo sed -i "s/YOUR_CI_CD_IP/YOUR_ACTUAL_IP_ADDRESS/g" Caddyfile +sudo sed -i "s/YOUR_CI_CD_IP/YOUR_ACTUAL_IP_ADDRESS/g" /opt/APP_NAME/registry/Caddyfile # Create environment file for registry authentication # First, create a secure password hash @@ -700,21 +700,14 @@ sudo chmod 600 .env #### 5.3 Configure Docker Registry ```bash -# Create registry data directory -sudo mkdir -p /opt/registry/data -sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/data - -# Copy registry configuration from repository -sudo cp /opt/APP_NAME/registry/config.yml /opt/registry/config.yml - # Update the baseurl with your actual IP address -sudo sed -i "s/YOUR_CI_CD_IP/YOUR_ACTUAL_IP_ADDRESS/g" /opt/registry/config.yml +sudo sed -i "s/YOUR_CI_CD_IP/YOUR_ACTUAL_IP_ADDRESS/g" /opt/APP_NAME/registry/config.yml # Note: For Option B (domain-based setup), you'll need to update this again later -# with: sudo sed -i "s/YOUR_ACTUAL_IP_ADDRESS/YOUR_DOMAIN_NAME/g" /opt/registry/config.yml +# with: sudo sed -i "s/YOUR_ACTUAL_IP_ADDRESS/YOUR_DOMAIN_NAME/g" /opt/APP_NAME/registry/config.yml # Set proper permissions -sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/config.yml +sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/APP_NAME/registry/config.yml ``` #### 5.4 Generate TLS Certificate and Install in Docker Trust Store @@ -729,9 +722,7 @@ sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/config.yml ```bash # 1. Generate self-signed certificate with proper CA chain -sudo mkdir -p /opt/registry/certs -sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/certs -cd /opt/registry/certs +cd /etc/registry/certs # Generate CA private key sudo -u CI_SERVICE_USER openssl genrsa -out ca.key 4096 @@ -751,8 +742,8 @@ sudo -u CI_SERVICE_USER openssl req -new -key registry.key \ -subj "/C=US/ST=State/L=City/O=Organization/OU=IT/CN=YOUR_ACTUAL_IP_ADDRESS" # Copy and customize the OpenSSL configuration file -sudo cp /opt/APP_NAME/registry/openssl.conf /opt/registry/certs/ -sudo sed -i "s/YOUR_ACTUAL_IP_ADDRESS/YOUR_ACTUAL_IP_ADDRESS/g" /opt/registry/certs/openssl.conf +sudo cp /opt/APP_NAME/registry/openssl.conf /etc/registry/certs/ +sudo sed -i "s/YOUR_ACTUAL_IP_ADDRESS/YOUR_ACTUAL_IP_ADDRESS/g" /etc/registry/certs/openssl.conf # Sign server certificate with CA sudo -u CI_SERVICE_USER openssl x509 -req -in registry.csr \ @@ -760,7 +751,7 @@ sudo -u CI_SERVICE_USER openssl x509 -req -in registry.csr \ -out registry.crt \ -days 365 \ -extensions v3_req \ - -extfile /opt/registry/certs/openssl.conf + -extfile /etc/registry/certs/openssl.conf # Set proper permissions sudo chmod 600 ca.key registry.key @@ -769,8 +760,8 @@ sudo -u CI_SERVICE_USER openssl x509 -in registry.crt -text -noout # 2. Install CA certificate into Docker trust store sudo mkdir -p /etc/docker/certs.d/registry -sudo cp /opt/registry/certs/ca.crt /etc/docker/certs.d/registry/ca.crt -sudo cp /opt/registry/certs/ca.crt /usr/local/share/ca-certificates/registry-ca.crt +sudo cp /etc/registry/certs/ca.crt /etc/docker/certs.d/registry/ca.crt +sudo cp /etc/registry/certs/ca.crt /usr/local/share/ca-certificates/registry-ca.crt sudo update-ca-certificates sudo systemctl restart docker ``` @@ -791,18 +782,16 @@ sudo certbot certonly --standalone \ --no-eff-email \ -d YOUR_DOMAIN_NAME sudo certbot certificates -sudo mkdir -p /opt/registry/certs -sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/certs -sudo cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/fullchain.pem /opt/registry/certs/registry.crt -sudo cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/privkey.pem /opt/registry/certs/registry.key -sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/certs/registry.crt -sudo chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/certs/registry.key -sudo chmod 644 /opt/registry/certs/registry.crt -sudo chmod 600 /opt/registry/certs/registry.key +sudo cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/fullchain.pem /etc/registry/certs/registry.crt +sudo cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/privkey.pem /etc/registry/certs/registry.key +sudo chown CI_SERVICE_USER:CI_SERVICE_USER /etc/registry/certs/registry.crt +sudo chown CI_SERVICE_USER:CI_SERVICE_USER /etc/registry/certs/registry.key +sudo chmod 644 /etc/registry/certs/registry.crt +sudo chmod 600 /etc/registry/certs/registry.key # 2. Install certificate into Docker trust store sudo mkdir -p /etc/docker/certs.d/YOUR_DOMAIN_NAME -sudo cp /opt/registry/certs/registry.crt /etc/docker/certs.d/YOUR_DOMAIN_NAME/ca.crt +sudo cp /etc/registry/certs/registry.crt /etc/docker/certs.d/YOUR_DOMAIN_NAME/ca.crt sudo systemctl restart docker ``` @@ -853,7 +842,7 @@ sudo certbot renew --dry-run # Set up automatic renewal cron job sudo crontab -e # Add this line to renew certificates twice daily (Let's Encrypt allows renewal 30 days before expiry): -# 0 12,18 * * * /usr/bin/certbot renew --quiet --post-hook "cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/fullchain.pem /opt/registry/certs/registry.crt && cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/privkey.pem /opt/registry/certs/registry.key && chown CI_SERVICE_USER:CI_SERVICE_USER /opt/registry/certs/registry.* && chmod 644 /opt/registry/certs/registry.crt && chmod 600 /opt/registry/certs/registry.key && systemctl restart docker-registry.service" +# 0 12,18 * * * /usr/bin/certbot renew --quiet --post-hook "cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/fullchain.pem /etc/registry/certs/registry.crt && cp /etc/letsencrypt/live/YOUR_DOMAIN_NAME/privkey.pem /etc/registry/certs/registry.key && chown CI_SERVICE_USER:CI_SERVICE_USER /etc/registry/certs/registry.* && chmod 644 /etc/registry/certs/registry.crt && chmod 600 /etc/registry/certs/registry.key && systemctl restart docker-registry.service" echo "Automatic certificate renewal configured!" echo "Certificates will be renewed automatically and the registry service will be restarted" @@ -865,49 +854,27 @@ echo "Certificates will be renewed automatically and the registry service will b # Switch to CI_SERVICE_USER sudo su - CI_SERVICE_USER -# Navigate to registry directory -cd /opt/registry +# Navigate to the application directory +cd /opt/APP_NAME/registry -# Copy updated Docker Compose and Caddy configuration with certificate support -sudo cp /opt/APP_NAME/registry/docker-compose.registry.yml docker-compose.yml -sudo cp /opt/APP_NAME/registry/Caddyfile Caddyfile +# Update Caddyfile to use FHS-compliant certificate paths +sudo sed -i "s|/opt/registry/certs|/etc/registry/certs|g" /opt/APP_NAME/registry/Caddyfile -# Update Caddyfile with your actual IP address -sudo sed -i "s/YOUR_CI_CD_IP/YOUR_ACTUAL_IP_ADDRESS/g" Caddyfile - -# Start the Docker Registry and Caddy services -docker compose up -d +# Start the Docker Registry and Caddy services using the registry compose file +docker compose -f docker-compose.registry.yml up -d # Verify services are running -docker compose ps +docker compose -f docker-compose.registry.yml ps # Exit CI_SERVICE_USER shell exit ``` -#### 5.8 Create Systemd Service for Docker Compose +#### 5.8 Install Systemd Service for Docker Registry ```bash -# Create systemd service file for Docker Registry with Docker Compose -sudo tee /etc/systemd/system/docker-registry.service << EOF -[Unit] -Description=Docker Registry with Caddy -After=docker.service -Requires=docker.service - -[Service] -Type=oneshot -RemainAfterExit=yes -User=CI_SERVICE_USER -Group=CI_SERVICE_USER -WorkingDirectory=/opt/registry -ExecStart=/usr/bin/docker compose up -d -ExecStop=/usr/bin/docker compose down -ExecReload=/usr/bin/docker compose down && /usr/bin/docker compose up -d - -[Install] -WantedBy=multi-user.target -EOF +# Install systemd service from repository +sudo cp /opt/APP_NAME/registry/docker-registry.service /etc/systemd/system/docker-registry.service # Enable and start Docker Registry service sudo systemctl daemon-reload @@ -926,6 +893,9 @@ sudo journalctl -u docker-registry.service -f # Switch to CI_SERVICE_USER for testing (CI_SERVICE_USER runs CI pipeline and Docker operations) sudo su - CI_SERVICE_USER +# Navigate to the application directory +cd /opt/APP_NAME + # Test Docker login and push using IP address with self-signed certificate echo "your-secure-registry-password" | docker login YOUR_ACTUAL_IP_ADDRESS -u registry-user --password-stdin @@ -956,6 +926,9 @@ exit # Switch to CI_SERVICE_USER for testing (CI_SERVICE_USER runs CI pipeline and Docker operations) sudo su - CI_SERVICE_USER +# Navigate to the application directory +cd /opt/APP_NAME + # Test Docker login and push (now using Let's Encrypt certificate with domain) echo "your-secure-registry-password" | docker login YOUR_DOMAIN_NAME -u registry-user --password-stdin @@ -984,7 +957,7 @@ exit ```bash # Update registry config for domain-based setup -sudo sed -i "s/YOUR_ACTUAL_IP_ADDRESS/YOUR_DOMAIN_NAME/g" /opt/registry/config.yml +sudo sed -i "s/YOUR_ACTUAL_IP_ADDRESS/YOUR_DOMAIN_NAME/g" /opt/APP_NAME/registry/config.yml ``` **Expected behavior**: @@ -1017,7 +990,6 @@ openssl s_client -connect YOUR_ACTUAL_IP_ADDRESS:443 -servername YOUR_ACTUAL_IP_ # Test Docker login again echo "your-secure-registry-password" | docker login YOUR_ACTUAL_IP_ADDRESS -u registry-user --password-stdin -``` **Configure Caddy to Use Our Certificates** @@ -1025,9 +997,9 @@ Since we're creating our own certificate chain, we need to configure Caddy to us ```bash # Update Caddyfile to use our certificates -sudo tee /opt/registry/Caddyfile << EOF +sudo tee /opt/APP_NAME/registry/Caddyfile << EOF YOUR_ACTUAL_IP_ADDRESS { - tls /opt/registry/certs/registry.crt /opt/registry/certs/registry.key + tls /etc/registry/certs/registry.crt /etc/registry/certs/registry.key reverse_proxy registry:5000 { header_up Host {host} @@ -1039,9 +1011,9 @@ YOUR_ACTUAL_IP_ADDRESS { EOF # Restart the registry services -cd /opt/registry -docker compose down -docker compose up -d +cd /opt/APP_NAME +docker compose -f docker-compose.registry.yml down +docker compose -f docker-compose.registry.yml up -d # Test Docker login echo "your-secure-registry-password" | docker login YOUR_ACTUAL_IP_ADDRESS -u registry-user --password-stdin @@ -1284,7 +1256,7 @@ sudo journalctl -u forgejo-runner.service -f --no-pager ### Step 7: Set Up Docker-in-Docker (DinD) for CI Operations -**Important**: This step sets up a Docker-in-Docker container that provides an isolated environment for CI/CD operations, eliminating resource contention with Harbor and simplifying cleanup. +**Important**: This step sets up a Docker-in-Docker container that provides an isolated environment for CI/CD operations, eliminating resource contention with Docker Registry and simplifying cleanup. #### 7.1 Create Containerized CI/CD Environment @@ -1350,7 +1322,7 @@ sudo chmod 755 /tmp/ci-workspace ls -la /tmp/ci-workspace ``` -**What this does**: +**What this does**: - **Creates workspace**: Provides a dedicated directory for CI operations - **Proper ownership**: CI_SERVICE_USER owns the directory for write access - **Appropriate permissions**: 755 allows read/write for owner, read for others @@ -1362,12 +1334,33 @@ ls -la /tmp/ci-workspace - `/var/lib/ci-workspace` - System-managed location **Note**: The CI workflow will use this directory for code checkout and then copy the contents to the DinD container. + +### FHS-Compliant Directory Structure + +The Docker Registry setup now follows the Filesystem Hierarchy Standard (FHS) for better organization and security: + +**Application Files** (in `/opt/APP_NAME/registry/`): +- `docker-compose.registry.yml` - Docker Compose configuration +- `Caddyfile` - Caddy reverse proxy configuration +- `config.yml` - Docker Registry configuration +- `.env` - Environment variables for authentication + +**System Files** (FHS-compliant locations): +- `/var/lib/registry/` - Registry data storage +- `/etc/registry/certs/` - SSL/TLS certificates +- `/var/log/registry/` - Registry and Caddy logs + +**Benefits of FHS Compliance**: +- **Data persistence**: Registry data stored in `/var/lib/registry/` survives container restarts +- **Certificate security**: Certificates in `/etc/registry/certs/` with proper permissions +- **Log management**: Logs in `/var/log/registry/` for centralized logging +- **Configuration separation**: App configs in app directory, system data in system directories ``` **What this does**: -- **Configures certificate trust**: Properly sets up Harbor certificate trust in DinD +- **Configures certificate trust**: Properly sets up Docker Registry certificate trust in DinD - **Fixes ownership issues**: Ensures certificate has correct ownership for CA trust -- **Tests connectivity**: Verifies DinD can pull, tag, and push images to Harbor +- **Tests connectivity**: Verifies DinD can pull, tag, and push images to Docker Registry - **Validates setup**: Ensures the complete CI/CD pipeline will work #### 7.3 CI/CD Workflow Architecture @@ -1506,20 +1499,20 @@ docker --version docker compose version ``` -#### 9.2 Check Harbor Status +#### 9.2 Check Docker Registry Status ```bash -cd /opt/harbor/harbor -docker compose ps +cd /opt/APP_NAME/registry +docker compose -f docker-compose.registry.yml ps ``` -#### 9.3 Test Harbor Access +#### 9.3 Test Docker Registry Access ```bash -# Test Harbor API -curl -k https://localhost/api/v2.0/health +# Test Docker Registry API +curl -k https://localhost/v2/_catalog -# Test Harbor UI +# Test Docker Registry UI curl -k -I https://localhost ``` @@ -2115,7 +2108,7 @@ docker --version docker compose --version ``` -#### 16.2 Test Harbor Access +#### 16.2 Test Docker Registry Access ```bash # Test pulling an image from the CI/CD Docker Registry @@ -2327,7 +2320,8 @@ You have successfully set up a complete CI/CD pipeline with: - ✅ **Backup and cleanup** automation - ✅ **Security hardening** with proper user separation - ✅ **SSL/TLS support** for production (optional) -- ✅ **Zero resource contention** between CI/CD and Harbor +- ✅ **Zero resource contention** between CI/CD and Docker Registry +- ✅ **FHS-compliant directory structure** for better organization and security Your application is now ready for continuous deployment with proper security, monitoring, and maintenance procedures in place! @@ -2341,8 +2335,8 @@ Your application is now ready for continuous deployment with proper security, mo **How it works:** - **Test:** The workflow spins up a full test environment using `docker-compose.test.yml` (Postgres, backend, frontend, etc.) and runs all tests inside containers. -- **Build:** If tests pass, the workflow uses direct Docker commands (no compose file) to build backend and frontend images and push them to Harbor. -- **Deploy:** The production runner pulls images from Harbor and deploys the stack using `docker-compose.prod.yml`. +- **Build:** If tests pass, the workflow uses direct Docker commands (no compose file) to build backend and frontend images and push them to Docker Registry. +- **Deploy:** The production runner pulls images from Docker Registry and deploys the stack using `docker-compose.prod.yml`. **Expected Output:** - Each stage runs in its own isolated environment. diff --git a/registry/Caddyfile b/registry/Caddyfile index f6bde40..0eadc6c 100644 --- a/registry/Caddyfile +++ b/registry/Caddyfile @@ -7,7 +7,7 @@ # Option A: Self-signed certificates (IP address) YOUR_ACTUAL_IP_ADDRESS { # Use our generated TLS certificate - tls /opt/registry/certs/registry.crt /opt/registry/certs/registry.key + tls /etc/caddy/certs/registry.crt /etc/caddy/certs/registry.key # Security headers header { diff --git a/registry/docker-compose.registry.yml b/registry/docker-compose.registry.yml index 05145ca..b3c32ae 100644 --- a/registry/docker-compose.registry.yml +++ b/registry/docker-compose.registry.yml @@ -7,7 +7,8 @@ services: networks: - sharenet-ci volumes: - - registry-data:/var/lib/registry + - /var/lib/registry:/var/lib/registry + - ./config.yml:/etc/docker/registry/config.yml ports: - "127.0.0.1:5000:5000" # Localhost only @@ -23,14 +24,14 @@ services: - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile - - ./certs:/etc/caddy/certs + - /etc/registry/certs:/etc/caddy/certs + - /var/log/registry:/var/log/caddy - caddy_data:/data - caddy_config:/config env_file: - .env volumes: - registry-data: caddy_data: caddy_config: diff --git a/registry/docker-registry.service b/registry/docker-registry.service new file mode 100644 index 0000000..b16ed11 --- /dev/null +++ b/registry/docker-registry.service @@ -0,0 +1,17 @@ +[Unit] +Description=Docker Registry with Caddy +After=docker.service +Requires=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +User=CI_SERVICE_USER +Group=CI_SERVICE_USER +WorkingDirectory=/opt/APP_NAME/registry +ExecStart=/usr/bin/docker compose -f docker-compose.registry.yml up -d +ExecStop=/usr/bin/docker compose -f docker-compose.registry.yml down +ExecReload=/usr/bin/docker compose -f docker-compose.registry.yml down && /usr/bin/docker compose -f docker-compose.registry.yml up -d + +[Install] +WantedBy=multi-user.target \ No newline at end of file