Consolidate docker registry stuff
Some checks failed
CI/CD Pipeline (Fully Isolated DinD) / Run Tests (DinD) (push) Has been cancelled
CI/CD Pipeline (Fully Isolated DinD) / Build and Push Docker Images (DinD) (push) Has been cancelled
CI/CD Pipeline (Fully Isolated DinD) / Deploy to Production (push) Has been cancelled

This commit is contained in:
continuist 2025-08-03 11:22:59 -04:00
parent 491deea461
commit fddc224e3b
4 changed files with 113 additions and 101 deletions

View file

@ -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
@ -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.

View file

@ -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 {

View file

@ -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:

View file

@ -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