Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 58s
Deploy / Build Web Image (push) Failing after 14s
Deploy / Rollback Production (push) Has been skipped
CI / E2E Tests (push) Has been skipped
Deploy / Build API Image (push) Failing after 3m8s
Deploy / Build AI Services Image (push) Failing after 10s
E2E Tests / Playwright E2E (push) Failing after 1m21s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
- Add Nginx reverse-proxy configs for api.goodgo.vn and platform.goodgo.vn with SSL, gzip, rate limiting, security headers, and WebSocket support - Add Cloudflare DNS setup script for A/AAAA/CNAME records - Add server-setup.sh for Ubuntu provisioning (Docker, fail2ban, UFW, swap, unattended-upgrades) - Add deploy-production.sh for manual production deployments - Add env.production.example with all required environment variables - Bind container ports to 127.0.0.1 in docker-compose.prod.yml (security: prevent direct access bypassing Nginx) - Fix deploy workflow: add -T flag to exec, sync Nginx configs, copy pgbouncer and backup configs to server Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
265 lines
9.5 KiB
Bash
Executable File
265 lines
9.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ==============================================================================
|
|
# GoodGo Platform — VPS Server Setup Script
|
|
# Target: VelikSV01 (185.225.232.65) — Ubuntu 22.04+
|
|
#
|
|
# Usage:
|
|
# scp infra/server-setup.sh ubuntu@185.225.232.65:~/
|
|
# ssh ubuntu@185.225.232.65 'chmod +x ~/server-setup.sh && sudo ~/server-setup.sh'
|
|
# ==============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Colors ────────────────────────────────────────────────────────────────────
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[SETUP]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
|
err() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
|
|
|
# ── Pre-flight checks ────────────────────────────────────────────────────────
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
err "This script must be run as root (sudo)."
|
|
exit 1
|
|
fi
|
|
|
|
DEPLOY_USER="${DEPLOY_USER:-ubuntu}"
|
|
DEPLOY_DIR="/home/${DEPLOY_USER}/goodgo"
|
|
|
|
log "Starting GoodGo Platform server setup..."
|
|
log "Deploy user: ${DEPLOY_USER}"
|
|
log "Deploy dir: ${DEPLOY_DIR}"
|
|
|
|
# ── 1. System Updates ─────────────────────────────────────────────────────────
|
|
log "Updating system packages..."
|
|
apt-get update -qq
|
|
apt-get upgrade -y -qq
|
|
|
|
# ── 2. Install Essential Packages ─────────────────────────────────────────────
|
|
log "Installing essential packages..."
|
|
apt-get install -y -qq \
|
|
apt-transport-https \
|
|
ca-certificates \
|
|
curl \
|
|
gnupg \
|
|
lsb-release \
|
|
software-properties-common \
|
|
git \
|
|
jq \
|
|
htop \
|
|
unzip \
|
|
fail2ban \
|
|
ufw \
|
|
logrotate
|
|
|
|
# ── 3. Install Docker Engine ─────────────────────────────────────────────────
|
|
if command -v docker &>/dev/null; then
|
|
log "Docker already installed: $(docker --version)"
|
|
else
|
|
log "Installing Docker Engine..."
|
|
curl -fsSL https://get.docker.com | sh
|
|
|
|
# Add deploy user to docker group
|
|
usermod -aG docker "${DEPLOY_USER}"
|
|
log "Docker installed: $(docker --version)"
|
|
fi
|
|
|
|
# Ensure Docker starts on boot
|
|
systemctl enable docker
|
|
systemctl start docker
|
|
|
|
# ── 4. Install Docker Compose v2 (plugin) ────────────────────────────────────
|
|
if docker compose version &>/dev/null; then
|
|
log "Docker Compose already installed: $(docker compose version --short)"
|
|
else
|
|
log "Installing Docker Compose v2 plugin..."
|
|
DOCKER_COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r .tag_name)
|
|
mkdir -p /usr/local/lib/docker/cli-plugins
|
|
curl -fsSL "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-linux-$(uname -m)" \
|
|
-o /usr/local/lib/docker/cli-plugins/docker-compose
|
|
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
|
|
log "Docker Compose installed: $(docker compose version --short)"
|
|
fi
|
|
|
|
# ── 5. Install Nginx ──────────────────────────────────────────────────────────
|
|
if command -v nginx &>/dev/null; then
|
|
log "Nginx already installed: $(nginx -v 2>&1)"
|
|
else
|
|
log "Installing Nginx..."
|
|
apt-get install -y -qq nginx
|
|
log "Nginx installed: $(nginx -v 2>&1)"
|
|
fi
|
|
|
|
systemctl enable nginx
|
|
systemctl start nginx
|
|
|
|
# ── 6. Configure Firewall (ufw) ──────────────────────────────────────────────
|
|
log "Configuring firewall (ufw)..."
|
|
ufw --force reset
|
|
ufw default deny incoming
|
|
ufw default allow outgoing
|
|
ufw allow 22/tcp comment 'SSH'
|
|
ufw allow 80/tcp comment 'HTTP — redirect to HTTPS'
|
|
ufw allow 443/tcp comment 'HTTPS'
|
|
ufw --force enable
|
|
log "Firewall configured. Active rules:"
|
|
ufw status verbose
|
|
|
|
# ── 7. Configure fail2ban ─────────────────────────────────────────────────────
|
|
log "Configuring fail2ban..."
|
|
cat > /etc/fail2ban/jail.local << 'F2B_CONF'
|
|
[DEFAULT]
|
|
bantime = 3600
|
|
findtime = 600
|
|
maxretry = 5
|
|
|
|
[sshd]
|
|
enabled = true
|
|
port = ssh
|
|
logpath = %(sshd_log)s
|
|
maxretry = 3
|
|
|
|
[nginx-http-auth]
|
|
enabled = true
|
|
|
|
[nginx-botsearch]
|
|
enabled = true
|
|
F2B_CONF
|
|
|
|
systemctl enable fail2ban
|
|
systemctl restart fail2ban
|
|
log "fail2ban configured and running."
|
|
|
|
# ── 8. Create Directory Structure ────────────────────────────────────────────
|
|
log "Creating deployment directory structure..."
|
|
mkdir -p "${DEPLOY_DIR}"/{monitoring/{prometheus,grafana/{provisioning,dashboards},loki,promtail,alertmanager},scripts/backup,infra/pgbouncer}
|
|
chown -R "${DEPLOY_USER}:${DEPLOY_USER}" "${DEPLOY_DIR}"
|
|
log "Directory structure created at ${DEPLOY_DIR}"
|
|
|
|
# ── 9. Create SSL Directory ──────────────────────────────────────────────────
|
|
log "Creating SSL certificate directory..."
|
|
mkdir -p /etc/ssl/goodgo
|
|
chmod 700 /etc/ssl/goodgo
|
|
log "SSL directory: /etc/ssl/goodgo (place origin.pem + origin-key.pem here)"
|
|
|
|
# ── 10. Nginx Base Configuration ─────────────────────────────────────────────
|
|
log "Creating Nginx base configuration..."
|
|
|
|
# Create sites-available/sites-enabled structure if not exists
|
|
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
|
|
|
# Check if nginx.conf already includes sites-enabled
|
|
if ! grep -q 'sites-enabled' /etc/nginx/nginx.conf; then
|
|
warn "Adding 'include sites-enabled' to nginx.conf"
|
|
sed -i '/http {/a \ include /etc/nginx/sites-enabled/*;' /etc/nginx/nginx.conf
|
|
fi
|
|
|
|
# Remove default site
|
|
rm -f /etc/nginx/sites-enabled/default
|
|
|
|
# Nginx performance tuning
|
|
cat > /etc/nginx/conf.d/performance.conf << 'NGINX_PERF'
|
|
# GoodGo Platform — Nginx Performance Tuning
|
|
# Rate limiting zone (used by API vhost)
|
|
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/s;
|
|
|
|
# Gzip compression
|
|
gzip on;
|
|
gzip_vary on;
|
|
gzip_proxied any;
|
|
gzip_comp_level 6;
|
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
|
|
|
|
# Proxy settings
|
|
proxy_connect_timeout 60s;
|
|
proxy_send_timeout 60s;
|
|
proxy_read_timeout 60s;
|
|
proxy_buffers 32 8k;
|
|
|
|
# Security
|
|
server_tokens off;
|
|
NGINX_PERF
|
|
|
|
nginx -t && systemctl reload nginx
|
|
log "Nginx base configuration ready."
|
|
|
|
# ── 11. Docker Log Rotation ──────────────────────────────────────────────────
|
|
log "Configuring Docker log rotation..."
|
|
mkdir -p /etc/docker
|
|
cat > /etc/docker/daemon.json << 'DOCKER_LOG'
|
|
{
|
|
"log-driver": "json-file",
|
|
"log-opts": {
|
|
"max-size": "10m",
|
|
"max-file": "5"
|
|
},
|
|
"storage-driver": "overlay2"
|
|
}
|
|
DOCKER_LOG
|
|
|
|
systemctl restart docker
|
|
log "Docker log rotation configured."
|
|
|
|
# ── 12. Swap (if <4GB RAM) ───────────────────────────────────────────────────
|
|
TOTAL_MEM=$(free -m | awk '/^Mem:/{print $2}')
|
|
if [ "$TOTAL_MEM" -lt 4096 ]; then
|
|
if [ ! -f /swapfile ]; then
|
|
log "Low RAM (${TOTAL_MEM}MB). Creating 4GB swap..."
|
|
fallocate -l 4G /swapfile
|
|
chmod 600 /swapfile
|
|
mkswap /swapfile
|
|
swapon /swapfile
|
|
echo '/swapfile none swap sw 0 0' >> /etc/fstab
|
|
log "Swap created and enabled."
|
|
else
|
|
log "Swap already exists."
|
|
fi
|
|
else
|
|
log "RAM: ${TOTAL_MEM}MB — swap not needed."
|
|
fi
|
|
|
|
# ── 13. Kernel Tuning (for Docker/PostgreSQL) ────────────────────────────────
|
|
log "Applying kernel tuning..."
|
|
cat >> /etc/sysctl.conf << 'SYSCTL'
|
|
|
|
# GoodGo — Docker + PostgreSQL tuning
|
|
vm.overcommit_memory = 1
|
|
vm.swappiness = 10
|
|
net.core.somaxconn = 65535
|
|
net.ipv4.tcp_max_syn_backlog = 65535
|
|
net.ipv4.ip_local_port_range = 1024 65535
|
|
fs.file-max = 2097152
|
|
SYSCTL
|
|
|
|
sysctl -p >/dev/null 2>&1 || true
|
|
log "Kernel parameters tuned."
|
|
|
|
# ── Summary ──────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
log "=========================================="
|
|
log " Server setup complete!"
|
|
log "=========================================="
|
|
log ""
|
|
log " Next steps:"
|
|
log " 1. Place Cloudflare Origin Certificate:"
|
|
log " /etc/ssl/goodgo/origin.pem"
|
|
log " /etc/ssl/goodgo/origin-key.pem"
|
|
log ""
|
|
log " 2. Copy Nginx vhost configs to:"
|
|
log " /etc/nginx/sites-available/"
|
|
log " Then symlink to /etc/nginx/sites-enabled/"
|
|
log ""
|
|
log " 3. Create .env file at:"
|
|
log " ${DEPLOY_DIR}/.env"
|
|
log ""
|
|
log " 4. Login to GHCR:"
|
|
log " docker login ghcr.io"
|
|
log ""
|
|
log " 5. Deploy:"
|
|
log " cd ${DEPLOY_DIR}"
|
|
log " docker compose -f docker-compose.prod.yml up -d"
|
|
log "=========================================="
|