Add CI/CD and production flow
Some checks are pending
Some checks are pending
This commit is contained in:
parent
a14f29423d
commit
7eee42cea8
7 changed files with 2343 additions and 0 deletions
144
.forgejo/workflows/ci.yml
Normal file
144
.forgejo/workflows/ci.yml
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
name: CI/CD Pipeline
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main, develop ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ${{ secrets.CI_HOST }}:5000
|
||||||
|
IMAGE_NAME: sharenet
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-backend:
|
||||||
|
name: Test Backend
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15
|
||||||
|
env:
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: sharenet_test
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Cache Rust dependencies
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
backend/target
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cargo-
|
||||||
|
|
||||||
|
- name: Run backend tests
|
||||||
|
working-directory: ./backend
|
||||||
|
run: |
|
||||||
|
cargo test --all
|
||||||
|
cargo clippy --all -- -D warnings
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
|
||||||
|
test-frontend:
|
||||||
|
name: Test Frontend
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: frontend/package-lock.json
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
working-directory: ./frontend
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run frontend tests
|
||||||
|
working-directory: ./frontend
|
||||||
|
run: |
|
||||||
|
npm run lint
|
||||||
|
npm run type-check
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
build-and-push:
|
||||||
|
name: Build and Push Docker Images
|
||||||
|
needs: [test-backend, test-frontend]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Configure Docker for local registry
|
||||||
|
run: |
|
||||||
|
echo '{"insecure-registries": ["${{ secrets.CI_HOST }}:5000"]}' | sudo tee /etc/docker/daemon.json
|
||||||
|
sudo systemctl restart docker
|
||||||
|
|
||||||
|
- name: Build and push backend image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: ./backend
|
||||||
|
file: ./backend/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/backend:${{ github.sha }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
- name: Build and push frontend image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: ./frontend
|
||||||
|
file: ./frontend/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/frontend:${{ github.sha }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
name: Deploy to Production
|
||||||
|
needs: build-and-push
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Deploy to production server
|
||||||
|
uses: appleboy/ssh-action@v1.0.3
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.PROD_HOST }}
|
||||||
|
username: ${{ secrets.PROD_USER }}
|
||||||
|
key: ${{ secrets.PROD_SSH_KEY }}
|
||||||
|
script: |
|
||||||
|
cd /opt/sharenet
|
||||||
|
echo "IMAGE_TAG=${{ github.sha }}" > .env
|
||||||
|
echo "REGISTRY=${{ secrets.CI_HOST }}:5000" >> .env
|
||||||
|
echo "IMAGE_NAME=sharenet" >> .env
|
||||||
|
docker-compose pull
|
||||||
|
docker-compose up -d
|
||||||
|
docker system prune -f
|
819
CI_CD_SETUP_GUIDE.md
Normal file
819
CI_CD_SETUP_GUIDE.md
Normal file
|
@ -0,0 +1,819 @@
|
||||||
|
# CI/CD Linode Setup Guide
|
||||||
|
|
||||||
|
This guide covers setting up your CI/CD Linode with Forgejo Actions runner and a local Docker registry for automated deployments.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ Forgejo Host │ │ CI/CD Linode │ │ Production Linode│
|
||||||
|
│ (Repository) │ │ (Actions Runner)│ │ (Docker Deploy) │
|
||||||
|
│ │ │ + Docker Registry│ │ │
|
||||||
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||||
|
│ │ │
|
||||||
|
│ │ │
|
||||||
|
└─────────── Push ──────┼───────────────────────┘
|
||||||
|
│
|
||||||
|
└─── Deploy ────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Ubuntu 24.04 LTS Linode with root access
|
||||||
|
- Basic familiarity with Linux commands and SSH
|
||||||
|
- Forgejo repository with Actions enabled
|
||||||
|
- Production Linode IP address (for SSH key exchange)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. **Follow this complete CI/CD setup guide**
|
||||||
|
2. **Set up SSH keys** for secure communication with Production server
|
||||||
|
3. **Configure Forgejo Actions runner**
|
||||||
|
4. **Test registry and runner functionality**
|
||||||
|
5. **Exchange SSH keys** with Production server
|
||||||
|
6. **Configure Forgejo repository secrets**
|
||||||
|
|
||||||
|
## What's Included
|
||||||
|
|
||||||
|
### 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
|
||||||
|
- Secure SSH communication with production
|
||||||
|
|
||||||
|
## Prerequisites and Initial Setup
|
||||||
|
|
||||||
|
### What's Already Done (Assumptions)
|
||||||
|
|
||||||
|
This guide assumes you have already:
|
||||||
|
|
||||||
|
1. **Created an Ubuntu 24.04 LTS Linode** with root access
|
||||||
|
2. **Set root password** for the Linode
|
||||||
|
3. **Have SSH client** installed on your local machine
|
||||||
|
4. **Have Production Linode IP address** ready for SSH key exchange
|
||||||
|
|
||||||
|
**Note**: The CI/CD Linode will be accessed via IP address only, as it's primarily used for internal services (Docker registry, Forgejo Actions runner) that don't require public web access.
|
||||||
|
|
||||||
|
### Step 0: Initial SSH Access and Verification
|
||||||
|
|
||||||
|
Before proceeding with the setup, you need to establish initial SSH access to your CI/CD Linode.
|
||||||
|
|
||||||
|
#### 0.1 Get Your Linode IP Address
|
||||||
|
|
||||||
|
From your Linode dashboard, note the IP address for:
|
||||||
|
- **CI/CD Linode**: `YOUR_CI_CD_IP` (IP address only, no domain needed)
|
||||||
|
|
||||||
|
#### 0.2 Test Initial SSH Access
|
||||||
|
|
||||||
|
Test SSH access to your CI/CD Linode:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test CI/CD Linode (IP address only)
|
||||||
|
ssh root@YOUR_CI_CD_IP
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**: SSH login prompt asking for root password.
|
||||||
|
|
||||||
|
**If something goes wrong**:
|
||||||
|
- Verify the IP address is correct
|
||||||
|
- Check that SSH is enabled on the Linode
|
||||||
|
- Ensure your local machine can reach the Linode (no firewall blocking)
|
||||||
|
|
||||||
|
#### 0.3 Choose Your Names
|
||||||
|
|
||||||
|
Before proceeding, decide on:
|
||||||
|
|
||||||
|
1. **Service Account Name**: Choose a username for the service account (e.g., `appuser`, `deploy`, `service`)
|
||||||
|
- Replace `SERVICE_USER` in this guide with your chosen name
|
||||||
|
|
||||||
|
2. **Application Name**: Choose a name for your application (e.g., `myapp`, `webapp`, `api`)
|
||||||
|
- Replace `APP_NAME` in this guide with your chosen name
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
- If you choose `appuser` as service account and `myapp` as application name:
|
||||||
|
- Replace all `SERVICE_USER` with `appuser`
|
||||||
|
- Replace all `APP_NAME` with `myapp`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CI/CD Linode Setup
|
||||||
|
|
||||||
|
### Step 1: Initial System Setup
|
||||||
|
|
||||||
|
#### 1.1 Update the System
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update && sudo apt upgrade -y
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Updates package lists and upgrades all installed packages to their latest versions.
|
||||||
|
|
||||||
|
**Expected output**: A list of packages being updated, followed by completion messages.
|
||||||
|
|
||||||
|
**If something goes wrong**:
|
||||||
|
- Check your internet connection
|
||||||
|
- Try running `sudo apt update` first, then `sudo apt upgrade -y` separately
|
||||||
|
- If you get package conflicts, run `sudo apt --fix-broken install`
|
||||||
|
|
||||||
|
#### 1.2 Install Essential Packages
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install -y \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
git \
|
||||||
|
build-essential \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
ca-certificates \
|
||||||
|
apt-transport-https \
|
||||||
|
software-properties-common \
|
||||||
|
apache2-utils
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Installs development tools, SSL libraries, and utilities needed for Docker and application building.
|
||||||
|
|
||||||
|
**Expected output**: Package installation progress, ending with completion messages.
|
||||||
|
|
||||||
|
**If something goes wrong**:
|
||||||
|
- Check if any packages failed to install
|
||||||
|
- Run `sudo apt install -f` to fix broken dependencies
|
||||||
|
- Ensure you have sufficient disk space: `df -h`
|
||||||
|
|
||||||
|
### Step 2: Create Service Account
|
||||||
|
|
||||||
|
#### 2.1 Create the SERVICE_USER User
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo useradd -r -s /bin/bash -m -d /home/SERVICE_USER SERVICE_USER
|
||||||
|
sudo usermod -aG sudo SERVICE_USER
|
||||||
|
echo "SERVICE_USER:$(openssl rand -base64 32)" | sudo chpasswd
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**:
|
||||||
|
- Creates a dedicated service account named `SERVICE_USER`
|
||||||
|
- Gives it sudo privileges for administrative tasks
|
||||||
|
- Generates a random 32-character password
|
||||||
|
|
||||||
|
**Expected output**: No output (successful user creation is silent).
|
||||||
|
|
||||||
|
**If something goes wrong**:
|
||||||
|
- If user already exists: `sudo userdel -r SERVICE_USER` then retry
|
||||||
|
- Check user creation: `id SERVICE_USER`
|
||||||
|
- Verify sudo access: `sudo -u SERVICE_USER sudo -l`
|
||||||
|
|
||||||
|
#### 2.2 Verify Service Account
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo su - SERVICE_USER
|
||||||
|
whoami
|
||||||
|
pwd
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Switches to the SERVICE_USER user and verifies the setup.
|
||||||
|
|
||||||
|
**Expected output**:
|
||||||
|
```
|
||||||
|
SERVICE_USER
|
||||||
|
/home/SERVICE_USER
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Install Docker
|
||||||
|
|
||||||
|
#### 3.1 Add Docker Repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
sudo apt update
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Adds Docker's official repository to your system for the latest Docker versions.
|
||||||
|
|
||||||
|
**Expected output**: GPG key import confirmation and package list update.
|
||||||
|
|
||||||
|
#### 3.2 Install Docker Packages
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Installs Docker Engine, CLI, container runtime, and Docker Compose.
|
||||||
|
|
||||||
|
**Expected output**: Package installation progress, ending with completion messages.
|
||||||
|
|
||||||
|
#### 3.3 Configure Docker for Service Account
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo usermod -aG docker SERVICE_USER
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Adds the SERVICE_USER user to the docker group so it can run Docker commands without sudo.
|
||||||
|
|
||||||
|
### Step 4: Install Docker Compose
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
|
sudo chmod +x /usr/local/bin/docker-compose
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Downloads the latest Docker Compose binary and makes it executable.
|
||||||
|
|
||||||
|
### Step 5: Configure Docker for Local Registry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo '{"insecure-registries": ["localhost:5000"]}' | sudo tee /etc/docker/daemon.json
|
||||||
|
sudo systemctl restart docker
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Configures Docker to allow connections to the local registry without SSL verification.
|
||||||
|
|
||||||
|
### Step 6: Install Development Tools
|
||||||
|
|
||||||
|
#### 6.1 Install Rust
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo su - SERVICE_USER
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
source $HOME/.cargo/env
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Installs Rust programming language and Cargo package manager for building the backend.
|
||||||
|
|
||||||
|
#### 6.2 Install Node.js
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||||
|
sudo apt install -y nodejs
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Installs Node.js version 20 for building the frontend.
|
||||||
|
|
||||||
|
### Step 7: Set Up Docker Registry
|
||||||
|
|
||||||
|
#### 7.1 Create Registry Directory
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /opt/registry
|
||||||
|
sudo chown SERVICE_USER:SERVICE_USER /opt/registry
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Creates a directory for the Docker registry and sets ownership to the SERVICE_USER user.
|
||||||
|
|
||||||
|
#### 7.2 Create Docker Compose File for Registry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > /opt/registry/docker-compose.yml << 'EOF'
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
registry:
|
||||||
|
image: registry:2
|
||||||
|
container_name: docker-registry
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
environment:
|
||||||
|
REGISTRY_STORAGE_DELETE_ENABLED: "true"
|
||||||
|
REGISTRY_STORAGE_FILESYSTEM_MAXTHREADS: "100"
|
||||||
|
volumes:
|
||||||
|
- registry_data:/var/lib/registry
|
||||||
|
- ./config.yml:/etc/docker/registry/config.yml:ro
|
||||||
|
networks:
|
||||||
|
- registry-network
|
||||||
|
|
||||||
|
registry-ui:
|
||||||
|
image: joxit/docker-registry-ui:latest
|
||||||
|
container_name: registry-ui
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
environment:
|
||||||
|
REGISTRY_URL: http://registry:5000
|
||||||
|
DELETE_IMAGES: "true"
|
||||||
|
depends_on:
|
||||||
|
- registry
|
||||||
|
networks:
|
||||||
|
- registry-network
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
registry_data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
registry-network:
|
||||||
|
driver: bridge
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 7.3 Create Registry Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > /opt/registry/config.yml << 'EOF'
|
||||||
|
version: 0.1
|
||||||
|
log:
|
||||||
|
fields:
|
||||||
|
service: registry
|
||||||
|
storage:
|
||||||
|
delete:
|
||||||
|
enabled: true
|
||||||
|
cache:
|
||||||
|
blobdescriptor: inmemory
|
||||||
|
filesystem:
|
||||||
|
rootdirectory: /var/lib/registry
|
||||||
|
http:
|
||||||
|
addr: :5000
|
||||||
|
headers:
|
||||||
|
X-Content-Type-Options: [nosniff]
|
||||||
|
health:
|
||||||
|
storagedriver:
|
||||||
|
enabled: true
|
||||||
|
interval: 10s
|
||||||
|
threshold: 3
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 7.4 Start the Registry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/registry
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Starts the Docker registry and web UI in detached mode.
|
||||||
|
|
||||||
|
**Expected output**: Container creation and startup messages.
|
||||||
|
|
||||||
|
### Step 8: Set Up SSH Keys
|
||||||
|
|
||||||
|
#### 8.1 Create SSH Directory
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
chmod 700 ~/.ssh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 8.2 Generate SSH Key Pair
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh-keygen -t ed25519 -C "ci-cd-server" -f ~/.ssh/id_ed25519 -N ""
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Generates an Ed25519 SSH key pair for secure communication with the production server.
|
||||||
|
|
||||||
|
**Expected output**: Key generation messages and file creation confirmation.
|
||||||
|
|
||||||
|
#### 8.3 Create SSH Config
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > ~/.ssh/config << 'EOF'
|
||||||
|
Host production
|
||||||
|
HostName YOUR_PRODUCTION_IP
|
||||||
|
User SERVICE_USER
|
||||||
|
IdentityFile ~/.ssh/id_ed25519
|
||||||
|
StrictHostKeyChecking no
|
||||||
|
UserKnownHostsFile /dev/null
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod 600 ~/.ssh/config
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Creates SSH configuration for easy connection to the production server.
|
||||||
|
|
||||||
|
**Note**: You'll update `YOUR_PRODUCTION_IP` with the actual production server IP later.
|
||||||
|
|
||||||
|
### Step 9: Install Forgejo Actions Runner
|
||||||
|
|
||||||
|
#### 9.1 Download Runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~
|
||||||
|
wget https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz
|
||||||
|
tar -xzf actions-runner-linux-x64-2.311.0.tar.gz
|
||||||
|
rm actions-runner-linux-x64-2.311.0.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Downloads and extracts the Forgejo Actions runner.
|
||||||
|
|
||||||
|
#### 9.2 Create Systemd Service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo tee /etc/systemd/system/github-runner.service > /dev/null << 'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=GitHub Actions Runner
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=SERVICE_USER
|
||||||
|
WorkingDirectory=/home/SERVICE_USER/actions-runner
|
||||||
|
ExecStart=/home/SERVICE_USER/actions-runner/run.sh
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Creates a systemd service to automatically start and manage the Actions runner.
|
||||||
|
|
||||||
|
#### 9.3 Enable Service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable github-runner.service
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Enables the Actions runner service to start automatically on boot.
|
||||||
|
|
||||||
|
### Step 10: Set Up Monitoring and Cleanup
|
||||||
|
|
||||||
|
#### 10.1 Create Monitoring Script
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > ~/monitor.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "=== CI/CD Server Status ==="
|
||||||
|
echo "Date: $(date)"
|
||||||
|
echo "Uptime: $(uptime)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== Docker Status ==="
|
||||||
|
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== Registry Status ==="
|
||||||
|
cd /opt/registry
|
||||||
|
docker-compose ps
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== Actions Runner Status ==="
|
||||||
|
sudo systemctl status github-runner.service --no-pager
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== System Resources ==="
|
||||||
|
echo "CPU Usage:"
|
||||||
|
top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1
|
||||||
|
echo "Memory Usage:"
|
||||||
|
free -h | grep Mem
|
||||||
|
echo "Disk Usage:"
|
||||||
|
df -h /
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== Recent Logs ==="
|
||||||
|
docker-compose logs --tail=10
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x ~/monitor.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Creates a monitoring script to check the status of all CI/CD services.
|
||||||
|
|
||||||
|
#### 10.2 Create Cleanup Script
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > ~/cleanup.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "Cleaning up old Docker images..."
|
||||||
|
|
||||||
|
# Remove unused images
|
||||||
|
docker image prune -f
|
||||||
|
|
||||||
|
# Remove unused volumes
|
||||||
|
docker volume prune -f
|
||||||
|
|
||||||
|
# Remove unused networks
|
||||||
|
docker network prune -f
|
||||||
|
|
||||||
|
# Remove old registry images (keep last 10 tags per repository)
|
||||||
|
cd /opt/registry
|
||||||
|
docker-compose exec registry registry garbage-collect /etc/docker/registry/config.yml
|
||||||
|
|
||||||
|
echo "Cleanup complete!"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x ~/cleanup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Creates a cleanup script to remove old Docker images and registry data.
|
||||||
|
|
||||||
|
#### 10.3 Set Up Automated Cleanup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(crontab -l 2>/dev/null; echo "0 3 * * * /home/SERVICE_USER/cleanup.sh") | crontab -
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Schedules daily cleanup at 3 AM.
|
||||||
|
|
||||||
|
### Step 11: Configure Firewall
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
sudo ufw allow 8080/tcp # Registry UI
|
||||||
|
```
|
||||||
|
|
||||||
|
**What this does**: Configures firewall to allow only necessary ports.
|
||||||
|
|
||||||
|
### Step 12: Test CI/CD Setup
|
||||||
|
|
||||||
|
#### 12.1 Test Docker Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker --version
|
||||||
|
docker-compose --version
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**: Version information for both Docker and Docker Compose.
|
||||||
|
|
||||||
|
#### 12.2 Check Registry Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/registry
|
||||||
|
docker-compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**: Status of registry and registry-ui containers showing "Up".
|
||||||
|
|
||||||
|
#### 12.3 Test Registry Access
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:5000/v2/_catalog
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**: `{"repositories":[]}` (empty initially).
|
||||||
|
|
||||||
|
#### 12.4 Get Public Key for Production Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat ~/.ssh/id_ed25519.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**: Copy this public key - you'll need it for the production server setup.
|
||||||
|
|
||||||
|
### Step 13: Configure Forgejo Actions Runner
|
||||||
|
|
||||||
|
#### 13.1 Get Runner Token
|
||||||
|
|
||||||
|
1. Go to your Forgejo repository
|
||||||
|
2. Navigate to Settings → Actions → Runners
|
||||||
|
3. Click "New runner"
|
||||||
|
4. Copy the registration token
|
||||||
|
|
||||||
|
#### 13.2 Configure Runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/actions-runner
|
||||||
|
./config.sh --url https://your-forgejo-instance/your-username/APP_NAME --token YOUR_TOKEN
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 13.3 Start Runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl start github-runner.service
|
||||||
|
sudo systemctl status github-runner.service
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SSH Key Exchange
|
||||||
|
|
||||||
|
After setting up both servers, you need to exchange SSH public keys for secure communication:
|
||||||
|
|
||||||
|
### Step 1: Get Public Keys from Both Servers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On CI/CD server (get your public key)
|
||||||
|
cat ~/.ssh/id_ed25519.pub
|
||||||
|
|
||||||
|
# On Production server (get their public key)
|
||||||
|
# You'll need to get this from the Production server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Add Production Server's Public Key
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On CI/CD server (add Production's public key)
|
||||||
|
echo "PRODUCTION_PUBLIC_KEY_HERE" >> ~/.ssh/authorized_keys
|
||||||
|
sed -i 's/YOUR_PRODUCTION_IP/YOUR_ACTUAL_PRODUCTION_IP/g' ~/.ssh/config
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Test SSH Connection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test from CI/CD to Production
|
||||||
|
ssh production 'echo Connection successful'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**: `Connection successful`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Registry Configuration
|
||||||
|
|
||||||
|
The CI/CD registry is configured to allow connections from the production server.
|
||||||
|
|
||||||
|
### Step 1: Verify Registry Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check registry is running
|
||||||
|
cd /opt/registry
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
# Test registry API
|
||||||
|
curl http://localhost:5000/v2/_catalog
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**:
|
||||||
|
- Registry containers showing "Up"
|
||||||
|
- Registry API: `{"repositories":[]}` or list of images
|
||||||
|
|
||||||
|
### Step 2: Verify Registry UI Access
|
||||||
|
|
||||||
|
You can access the registry web interface to manage images:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test registry UI
|
||||||
|
curl -I http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**: HTTP response (may be 404 initially, which is normal).
|
||||||
|
|
||||||
|
**Note**: The registry UI is accessible at `http://YOUR_CI_CD_IP:8080` for administrative purposes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Forgejo Configuration
|
||||||
|
|
||||||
|
### Step 1: Add Repository Secrets
|
||||||
|
|
||||||
|
Go to your Forgejo repository → Settings → Secrets and Variables → Actions, and add:
|
||||||
|
|
||||||
|
- `CI_HOST`: Your CI/CD Linode IP address
|
||||||
|
- `PROD_HOST`: Your production Linode IP
|
||||||
|
- `PROD_USER`: SSH username for production server (should be `SERVICE_USER`)
|
||||||
|
- `PROD_SSH_KEY`: SSH private key for deployment
|
||||||
|
|
||||||
|
### Step 2: Generate SSH Key for Deployment
|
||||||
|
|
||||||
|
The setup automatically generates SSH keys for the SERVICE_USER service account. For Forgejo Actions deployment, use the CI/CD server's private key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On CI/CD server
|
||||||
|
cat ~/.ssh/id_ed25519
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy the entire private key content (including the BEGIN and END lines) for the `PROD_SSH_KEY` secret.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing and Verification
|
||||||
|
|
||||||
|
### Step 1: Test Registry Functionality
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test registry API
|
||||||
|
curl http://localhost:5000/v2/_catalog
|
||||||
|
|
||||||
|
# Test registry UI (optional)
|
||||||
|
curl -I http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**:
|
||||||
|
- Registry API: `{"repositories":[]}` (empty initially)
|
||||||
|
- Registry UI: HTTP response (may be 404 initially, which is normal)
|
||||||
|
|
||||||
|
### Step 2: Test Actions Runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check runner status
|
||||||
|
sudo systemctl status github-runner.service
|
||||||
|
|
||||||
|
# Check runner logs
|
||||||
|
sudo journalctl -u github-runner.service -f
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**: Runner service showing as active and running.
|
||||||
|
|
||||||
|
### Step 3: Test Monitoring Script
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./monitor.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**: Status information for all CI/CD services.
|
||||||
|
|
||||||
|
### Step 4: Test SSH Connection to Production
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test from CI/CD to Production
|
||||||
|
ssh production 'echo Connection successful'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected output**: `Connection successful`.
|
||||||
|
|
||||||
|
### Step 5: Trigger First Deployment
|
||||||
|
|
||||||
|
#### 5.1 Push Code Changes
|
||||||
|
|
||||||
|
Make a small change to your code and push to trigger the CI/CD pipeline:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In your local repository
|
||||||
|
echo "# Test deployment" >> README.md
|
||||||
|
git add README.md
|
||||||
|
git commit -m "Test CI/CD pipeline"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.2 Monitor Pipeline
|
||||||
|
|
||||||
|
1. Go to your Forgejo repository
|
||||||
|
2. Navigate to Actions tab
|
||||||
|
3. Monitor the workflow execution
|
||||||
|
4. Check for any errors or issues
|
||||||
|
|
||||||
|
#### 5.3 Verify Deployment
|
||||||
|
|
||||||
|
After successful pipeline execution:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check if images were pushed to registry
|
||||||
|
curl http://localhost:5000/v2/_catalog
|
||||||
|
|
||||||
|
# Check registry UI for new images
|
||||||
|
# Open http://YOUR_CI_CD_IP:8080 in browser
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **Docker permission denied**:
|
||||||
|
```bash
|
||||||
|
sudo usermod -aG docker SERVICE_USER
|
||||||
|
newgrp docker
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Registry not accessible**:
|
||||||
|
```bash
|
||||||
|
cd /opt/registry
|
||||||
|
docker-compose logs
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Actions runner not starting**:
|
||||||
|
```bash
|
||||||
|
sudo systemctl status github-runner.service
|
||||||
|
sudo journalctl -u github-runner.service -f
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **SSH key issues**:
|
||||||
|
```bash
|
||||||
|
chmod 600 ~/.ssh/id_ed25519
|
||||||
|
chmod 700 ~/.ssh
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Registry connection failed**:
|
||||||
|
```bash
|
||||||
|
curl -v http://localhost:5000/v2/_catalog
|
||||||
|
```
|
||||||
|
|
||||||
|
### Useful Commands
|
||||||
|
|
||||||
|
- **Check system resources**: `htop`
|
||||||
|
- **Check disk space**: `df -h`
|
||||||
|
- **Check memory usage**: `free -h`
|
||||||
|
- **Check network**: `ip addr show`
|
||||||
|
- **Check firewall**: `sudo ufw status`
|
||||||
|
- **Check logs**: `sudo journalctl -f`
|
||||||
|
|
||||||
|
### Security Best Practices
|
||||||
|
|
||||||
|
1. **Service Account**: Use dedicated `SERVICE_USER` user with limited privileges
|
||||||
|
2. **SSH Keys**: Use Ed25519 keys with proper permissions (600/700)
|
||||||
|
3. **Firewall**: Configure UFW to allow only necessary ports
|
||||||
|
4. **Container Isolation**: Registry runs in isolated Docker containers
|
||||||
|
5. **Regular Cleanup**: Automated daily cleanup of old images
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Your CI/CD Linode is now ready to handle automated builds and deployments! The setup includes:
|
||||||
|
|
||||||
|
- **Forgejo Actions runner** for automated builds
|
||||||
|
- **Local Docker registry** with web UI for image management
|
||||||
|
- **Secure SSH communication** with production server
|
||||||
|
- **Monitoring and cleanup** scripts
|
||||||
|
- **Firewall protection** for security
|
||||||
|
|
||||||
|
For ongoing maintenance and troubleshooting, refer to the troubleshooting section and monitoring scripts provided in this guide.
|
1070
PRODUCTION_SETUP_GUIDE.md
Normal file
1070
PRODUCTION_SETUP_GUIDE.md
Normal file
File diff suppressed because it is too large
Load diff
61
backend/Dockerfile
Normal file
61
backend/Dockerfile
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# Multi-stage build for Rust backend
|
||||||
|
FROM rust:1.75-slim as builder
|
||||||
|
|
||||||
|
# Install build dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy Cargo files
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY crates/ ./crates/
|
||||||
|
|
||||||
|
# Build dependencies first (for better caching)
|
||||||
|
RUN cargo build --release --bin sharenet-api-postgres
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN cargo build --release --bin sharenet-api-postgres
|
||||||
|
|
||||||
|
# Runtime stage
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
# Install runtime dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
ca-certificates \
|
||||||
|
libssl3 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN useradd -r -s /bin/false sharenet
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy binary from builder
|
||||||
|
COPY --from=builder /app/target/release/sharenet-api-postgres /app/sharenet-api
|
||||||
|
|
||||||
|
# Copy configuration files
|
||||||
|
COPY --from=builder /app/config/ ./config/
|
||||||
|
|
||||||
|
# Change ownership
|
||||||
|
RUN chown -R sharenet:sharenet /app
|
||||||
|
|
||||||
|
# Switch to non-root user
|
||||||
|
USER sharenet
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 3001
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:3001/health || exit 1
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
CMD ["./sharenet-api"]
|
89
docker-compose.yml
Normal file
89
docker-compose.yml
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: sharenet-postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: sharenet
|
||||||
|
POSTGRES_USER: sharenet
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme}
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
- ./migrations:/docker-entrypoint-initdb.d
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U sharenet"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
networks:
|
||||||
|
- sharenet-network
|
||||||
|
|
||||||
|
backend:
|
||||||
|
image: ${REGISTRY:-ghcr.io}/${IMAGE_NAME:-your-username/sharenet}/backend:${IMAGE_TAG:-latest}
|
||||||
|
container_name: sharenet-backend
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql://sharenet:${POSTGRES_PASSWORD:-changeme}@postgres:5432/sharenet
|
||||||
|
RUST_LOG: info
|
||||||
|
RUST_BACKTRACE: 1
|
||||||
|
ports:
|
||||||
|
- "3001:3001"
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
networks:
|
||||||
|
- sharenet-network
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: ${REGISTRY:-ghcr.io}/${IMAGE_NAME:-your-username/sharenet}/frontend:${IMAGE_TAG:-latest}
|
||||||
|
container_name: sharenet-frontend
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
NEXT_PUBLIC_API_HOST: backend
|
||||||
|
NEXT_PUBLIC_API_PORT: 3001
|
||||||
|
NODE_ENV: production
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
depends_on:
|
||||||
|
backend:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
networks:
|
||||||
|
- sharenet-network
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: sharenet-nginx
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
- ./nginx/ssl:/etc/nginx/ssl:ro
|
||||||
|
depends_on:
|
||||||
|
- frontend
|
||||||
|
- backend
|
||||||
|
networks:
|
||||||
|
- sharenet-network
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
sharenet-network:
|
||||||
|
driver: bridge
|
48
frontend/Dockerfile
Normal file
48
frontend/Dockerfile
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Multi-stage build for Next.js frontend
|
||||||
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm ci --only=production
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production stage
|
||||||
|
FROM node:20-alpine AS runner
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy necessary files from builder
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder /app/.next/standalone ./
|
||||||
|
COPY --from=builder /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
# Change ownership
|
||||||
|
RUN chown -R nextjs:nodejs /app
|
||||||
|
|
||||||
|
# Switch to non-root user
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:3000/api/health || exit 1
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
CMD ["node", "server.js"]
|
112
nginx/nginx.conf
Normal file
112
nginx/nginx.conf
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
upstream frontend {
|
||||||
|
server frontend:3000;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream backend {
|
||||||
|
server backend:3001;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rate limiting
|
||||||
|
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
|
||||||
|
limit_req_zone $binary_remote_addr zone=frontend:10m rate=30r/s;
|
||||||
|
|
||||||
|
# Gzip compression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_types
|
||||||
|
text/plain
|
||||||
|
text/css
|
||||||
|
text/xml
|
||||||
|
text/javascript
|
||||||
|
application/json
|
||||||
|
application/javascript
|
||||||
|
application/xml+rss
|
||||||
|
application/atom+xml
|
||||||
|
image/svg+xml;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Redirect HTTP to HTTPS
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# SSL configuration
|
||||||
|
ssl_certificate /etc/nginx/ssl/cert.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
|
||||||
|
ssl_prefer_server_ciphers off;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_timeout 10m;
|
||||||
|
|
||||||
|
# Frontend routes
|
||||||
|
location / {
|
||||||
|
limit_req zone=frontend burst=20 nodelay;
|
||||||
|
|
||||||
|
proxy_pass http://frontend;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API routes
|
||||||
|
location /api/ {
|
||||||
|
limit_req zone=api burst=10 nodelay;
|
||||||
|
|
||||||
|
proxy_pass http://backend/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# CORS headers
|
||||||
|
add_header Access-Control-Allow-Origin * always;
|
||||||
|
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
|
||||||
|
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization" always;
|
||||||
|
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
|
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
|
||||||
|
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization";
|
||||||
|
add_header Access-Control-Max-Age 1728000;
|
||||||
|
add_header Content-Type 'text/plain; charset=utf-8';
|
||||||
|
add_header Content-Length 0;
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Health check endpoint
|
||||||
|
location /health {
|
||||||
|
access_log off;
|
||||||
|
return 200 "healthy\n";
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue