Compare commits
7 Commits
75fc650ba8
...
dev_cluste
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef249b1745 | ||
|
|
6c6de37419 | ||
|
|
47fe502dc1 | ||
|
|
a56213569a | ||
|
|
8d1c27471f | ||
|
|
d1ed06a4c4 | ||
|
|
3c6b67b149 |
55
.env.default
55
.env.default
@@ -79,9 +79,27 @@ AE_DB_USERNAME=aether_dev
|
|||||||
AE_DB_PASSWORD=XXXX
|
AE_DB_PASSWORD=XXXX
|
||||||
|
|
||||||
# Connection Tuning
|
# Connection Tuning
|
||||||
|
# Seconds to wait when establishing a new connection before giving up.
|
||||||
|
# Lower values fail fast on DB outage rather than hanging requests.
|
||||||
AE_DB_CONNECTION_TIMEOUT=7
|
AE_DB_CONNECTION_TIMEOUT=7
|
||||||
|
|
||||||
|
# Seconds before a pooled connection is recycled (closed and reopened).
|
||||||
|
# Prevents "MySQL server has gone away" errors from MariaDB's wait_timeout.
|
||||||
|
# Must be less than MariaDB's wait_timeout (default 28800s / 8 hours).
|
||||||
|
# 900s (15 min) is a safe conservative value for active workloads.
|
||||||
AE_DB_POOL_RECYCLE=900
|
AE_DB_POOL_RECYCLE=900
|
||||||
|
|
||||||
|
# Connections held open per API replica at idle (the "warm" pool).
|
||||||
|
# Each replica maintains this many persistent connections to MariaDB.
|
||||||
|
AE_DB_POOL_SIZE=10
|
||||||
|
|
||||||
|
# Additional connections a replica can open beyond AE_DB_POOL_SIZE under burst load.
|
||||||
|
# These are created on demand and closed when the burst subsides.
|
||||||
|
# Max connections per replica = AE_DB_POOL_SIZE + AE_DB_POOL_MAX_OVERFLOW.
|
||||||
|
# Total max DB connections across all replicas = AE_API_REPLICAS × (AE_DB_POOL_SIZE + AE_DB_POOL_MAX_OVERFLOW).
|
||||||
|
# Example: 3 replicas × (10 + 20) = 90 max connections. MARIADB_MAX_CONNECTIONS must exceed this.
|
||||||
|
AE_DB_POOL_MAX_OVERFLOW=20
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# REDIS SETTINGS
|
# REDIS SETTINGS
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@@ -92,23 +110,44 @@ AE_REDIS_PORT=6379
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# API SETTINGS (FastAPI)
|
# API SETTINGS (FastAPI)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Number of API container instances to run (Docker Compose scaling)
|
|
||||||
|
# Number of API container instances (Docker Compose replica scaling).
|
||||||
|
# Each replica is an independent container with its own Gunicorn process and
|
||||||
|
# connection pool. Total DB connections = AE_API_REPLICAS × (AE_DB_POOL_SIZE + AE_DB_POOL_MAX_OVERFLOW).
|
||||||
|
# Increase for horizontal scaling across CPU cores. On a single-node Linode,
|
||||||
|
# 2-4 replicas is typical; more replicas won't help if the DB is the bottleneck.
|
||||||
AE_API_REPLICAS=3
|
AE_API_REPLICAS=3
|
||||||
|
|
||||||
# Gunicorn / Uvicorn Tuning
|
# --- Gunicorn / Uvicorn Tuning ---
|
||||||
# AE_API_GUNICORN_TIMEOUT: worker timeout in seconds. Default in gunicorn_conf.py
|
|
||||||
# is 120s. Raise for endpoints that run long ffmpeg operations (clip_video, etc.)
|
# Internal port Gunicorn listens on inside the container. Nginx proxies to this.
|
||||||
# The dev .env typically sets this to 900 to accommodate 5-15 min video jobs.
|
# Each replica uses this same port within its own network namespace.
|
||||||
AE_API_GUNICORN_PORT=5065
|
AE_API_GUNICORN_PORT=5065
|
||||||
|
|
||||||
|
# Worker timeout in seconds. A request that takes longer than this causes Gunicorn
|
||||||
|
# to kill and restart the worker. Default in gunicorn_conf.py is 120s.
|
||||||
|
# Raise for endpoints that run long ffmpeg operations (clip_video, convert_file, etc.).
|
||||||
|
# Dev typically uses 900s to accommodate 5-15 min video jobs.
|
||||||
AE_API_GUNICORN_TIMEOUT=900
|
AE_API_GUNICORN_TIMEOUT=900
|
||||||
AE_API_GUNICORN_WORKERS=2
|
|
||||||
|
# Uvicorn worker processes per replica. Each worker handles requests independently
|
||||||
|
# using async I/O, but SQLAlchemy DB calls are synchronous and block the worker.
|
||||||
|
# More workers = more parallel DB queries. Recommended: 2-4 per replica.
|
||||||
|
# Total parallel DB query capacity ≈ AE_API_REPLICAS × AE_API_GUNICORN_WORKERS.
|
||||||
|
# Stress testing at 4 workers/replica yielded ~2x throughput vs 2 workers (14 req/s vs 7.5 req/s).
|
||||||
|
# Rule of thumb: (2 × CPU cores) + 1 per replica, but DB throughput caps before CPU becomes the limit.
|
||||||
|
AE_API_GUNICORN_WORKERS=4
|
||||||
|
|
||||||
|
# Threads per Gunicorn worker. Uvicorn workers use async I/O, so threading provides
|
||||||
|
# minimal benefit here. Leave at 1 unless explicitly benchmarked otherwise.
|
||||||
AE_API_GUNICORN_THREADS=1
|
AE_API_GUNICORN_THREADS=1
|
||||||
|
|
||||||
# Security & CORS
|
# Security & CORS
|
||||||
# JWT_KEY should be a 22+ character secret string
|
# JWT_KEY should be a 22+ character secret string. Rotate if compromised.
|
||||||
AE_API_JWT_KEY=XXXX
|
AE_API_JWT_KEY=XXXX
|
||||||
|
|
||||||
# Regex for allowed CORS origins
|
# Regex for allowed CORS origins. Requests from non-matching origins are blocked.
|
||||||
|
# Extend the pattern if adding new domains or local dev ports.
|
||||||
AE_API_ORIGINS_REGEX="(https://.*\.oneskyit\.com)|(http://.*\.oneskyit\.com)|(http://.*.localhost)|(http://.*.localhost:5173)"
|
AE_API_ORIGINS_REGEX="(https://.*\.oneskyit\.com)|(http://.*\.oneskyit\.com)|(http://.*.localhost)|(http://.*.localhost:5173)"
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ workstation:3001 workstation:5060
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Key Improvements:**
|
**Key Improvements:**
|
||||||
|
- **Timezone Support:** All containers use the `TZ` variable from `.env` for consistent logging and database timestamps.
|
||||||
- **Scalable Routing:** Nginx uses Regex (`~^(dev|test|bak|sr|prod)?-?...`) to automatically handle any environment prefix without configuration changes.
|
- **Scalable Routing:** Nginx uses Regex (`~^(dev|test|bak|sr|prod)?-?...`) to automatically handle any environment prefix without configuration changes.
|
||||||
- **Isolated Stacks:** Each deployment uses a unique `AE_NETWORK_NAME` and `CONTAINER_` prefix to prevent collisions.
|
- **Isolated Stacks:** Each deployment uses a unique `AE_NETWORK_NAME` and `CONTAINER_` prefix to prevent collisions.
|
||||||
- **Shared Services:** Core infrastructure (DB/Redis) resides on the `aether_shared_net` which must be created manually once.
|
- **Shared Services:** Core infrastructure (DB/Redis) resides on the `aether_shared_net` which must be created manually once.
|
||||||
@@ -42,7 +43,7 @@ Create the base directory and clone this environment:
|
|||||||
```bash
|
```bash
|
||||||
sudo mkdir -p /srv/env/aether
|
sudo mkdir -p /srv/env/aether
|
||||||
sudo chown -R $USER:$USER /srv/env/aether
|
sudo chown -R $USER:$USER /srv/env/aether
|
||||||
git clone https://bitbucket.org/oneskyit/one-sky-it-container-environment.git /srv/env/aether/container_env
|
git clone git@bitbucket.org:oneskyit/one-sky-it-container-environment.git /srv/env/aether/container_env
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Configure Environment Settings
|
### 3. Configure Environment Settings
|
||||||
@@ -119,7 +120,7 @@ These scripts are located in the root directory:
|
|||||||
## 📂 Directory Map
|
## 📂 Directory Map
|
||||||
|
|
||||||
* **`conf/`**: Configuration templates for Nginx and Gunicorn. API config now lives in the `aether_api_fastapi` repo as `app/config.py` and reads settings directly from env vars.
|
* **`conf/`**: Configuration templates for Nginx and Gunicorn. API config now lives in the `aether_api_fastapi` repo as `app/config.py` and reads settings directly from env vars.
|
||||||
* **`logs/`**: Centralized logging for all containers.
|
* **`logs/`**: Centralized logging for all containers. Automatic rotation is managed by the `ae_ops` service (7-day retention).
|
||||||
* **`srv/`**: Mount points for data and source code (managed via symlinks).
|
* **`srv/`**: Mount points for data and source code (managed via symlinks).
|
||||||
* **`scripts/`**: Internal automation logic.
|
* **`scripts/`**: Internal automation logic.
|
||||||
* **`backups/`**: Storage for MariaDB snapshots.
|
* **`backups/`**: Storage for MariaDB snapshots.
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
55 * * * * bash /scripts/backup_internal.sh >> /logs/backup_cron.log 2>&1
|
55 * * * * bash /scripts/backup_internal.sh >> /logs/backup_cron.log 2>&1
|
||||||
|
0 0 * * * /usr/sbin/logrotate /etc/logrotate.internal.conf
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Logrotate configuration for Aether Docker Logs
|
# Logrotate configuration for Aether Docker Logs (Internal container version)
|
||||||
# To use: sudo ln -s /home/scott/OSIT_dev/aether_container_env/conf/logrotate.conf /etc/logrotate.d/aether
|
|
||||||
|
|
||||||
/home/scott/OSIT_dev/aether_container_env/logs/*/*.log {
|
/logs/*/*.log /logs/web/*/*.log {
|
||||||
|
su aether aether
|
||||||
daily
|
daily
|
||||||
rotate 7
|
rotate 7
|
||||||
missingok
|
missingok
|
||||||
|
|||||||
15
deploy.sh
15
deploy.sh
@@ -16,6 +16,18 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
ensure_bitbucket_ssh_remote() {
|
||||||
|
local repo_path=$1
|
||||||
|
local remote_url
|
||||||
|
local remote_path
|
||||||
|
|
||||||
|
remote_url=$(git -C "$repo_path" remote get-url origin)
|
||||||
|
if [[ "$remote_url" =~ ^https://([^@/]+@)?bitbucket\.org/(.+)$ ]]; then
|
||||||
|
remote_path=${BASH_REMATCH[2]}
|
||||||
|
git -C "$repo_path" remote set-url origin "git@bitbucket.org:${remote_path}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
ENV=${1:-}
|
ENV=${1:-}
|
||||||
if [ -z "$ENV" ]; then
|
if [ -z "$ENV" ]; then
|
||||||
echo "Usage: $0 <prod|test> [app_branch] [api_branch]"
|
echo "Usage: $0 <prod|test> [app_branch] [api_branch]"
|
||||||
@@ -55,14 +67,17 @@ echo ""
|
|||||||
|
|
||||||
# --- Pull repos ---
|
# --- Pull repos ---
|
||||||
echo "[1/4] Pulling container env..."
|
echo "[1/4] Pulling container env..."
|
||||||
|
ensure_bitbucket_ssh_remote "$COMPOSE_DIR"
|
||||||
git -C "$COMPOSE_DIR" pull --ff-only
|
git -C "$COMPOSE_DIR" pull --ff-only
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "[2/4] Pulling app ($APP_BRANCH)..."
|
echo "[2/4] Pulling app ($APP_BRANCH)..."
|
||||||
|
ensure_bitbucket_ssh_remote "$APP_DIR"
|
||||||
git -C "$APP_DIR" pull --ff-only origin "$APP_BRANCH"
|
git -C "$APP_DIR" pull --ff-only origin "$APP_BRANCH"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "[3/4] Pulling API ($API_BRANCH)..."
|
echo "[3/4] Pulling API ($API_BRANCH)..."
|
||||||
|
ensure_bitbucket_ssh_remote "$API_DIR"
|
||||||
git -C "$API_DIR" pull --ff-only origin "$API_BRANCH"
|
git -C "$API_DIR" pull --ff-only origin "$API_BRANCH"
|
||||||
|
|
||||||
# --- Build and deploy ---
|
# --- Build and deploy ---
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- PUID=1000
|
- PUID=1000
|
||||||
- PGID=1000
|
- PGID=1000
|
||||||
- TZ=US/Eastern
|
- TZ=${TZ}
|
||||||
|
|
||||||
- NGINX_SERVER_NAMES="demo.localhost dev.localhost dev.oneskyit.com dev-app.oneskyit.com dev-connect.oneskyit.com dev-demo.oneskyit.com dev-aacc.oneskyit.com dev-aapor.oneskyit.com dev-ascm.oneskyit.com dev-businessgroup.oneskyt.com dev-chow.oneskyit.com dev-cmsc.oneskyit.com dev-idaa.oneskyit.com dev-ishlt.oneskyit.com dev-lci.oneskyit.com dev-ncsd.oneskyit.com dev-npa.oneskyit.com dev-rli.oneskyit.com test-app.oneskyit.com test-api.oneskyit.com test-demo.oneskyit.com test-lci.oneskyit.com test-idaa.oneskyit.com scott.oneskyit.com dgr.oneskyit.com"
|
- NGINX_SERVER_NAMES="demo.localhost dev.localhost dev.oneskyit.com dev-app.oneskyit.com dev-connect.oneskyit.com dev-demo.oneskyit.com dev-aacc.oneskyit.com dev-aapor.oneskyit.com dev-ascm.oneskyit.com dev-businessgroup.oneskyt.com dev-chow.oneskyit.com dev-cmsc.oneskyit.com dev-idaa.oneskyit.com dev-ishlt.oneskyit.com dev-lci.oneskyit.com dev-ncsd.oneskyit.com dev-npa.oneskyit.com dev-rli.oneskyit.com test-app.oneskyit.com test-api.oneskyit.com test-demo.oneskyit.com test-lci.oneskyit.com test-idaa.oneskyit.com scott.oneskyit.com dgr.oneskyit.com"
|
||||||
ports:
|
ports:
|
||||||
@@ -30,7 +30,6 @@ services:
|
|||||||
- ./conf/nginx/site.conf:/etc/nginx/conf.d/0_site.conf
|
- ./conf/nginx/site.conf:/etc/nginx/conf.d/0_site.conf
|
||||||
- ./conf/nginx/site-enabled_aether_fastapi_gunicorn.conf:/etc/nginx/templates/site-enabled_aether_fastapi_gunicorn.conf.template
|
- ./conf/nginx/site-enabled_aether_fastapi_gunicorn.conf:/etc/nginx/templates/site-enabled_aether_fastapi_gunicorn.conf.template
|
||||||
- ./conf/nginx/site-enabled_aether_app_svelte_node.conf:/etc/nginx/templates/site-enabled_aether_app_svelte_node.conf.template
|
- ./conf/nginx/site-enabled_aether_app_svelte_node.conf:/etc/nginx/templates/site-enabled_aether_app_svelte_node.conf.template
|
||||||
# - ./conf/nginx/site-enabled_aether_flask_gunicorn.conf:/etc/nginx/templates/site-enabled_aether_flask_gunicorn.conf.template
|
|
||||||
- ./conf/certs/oneskyit_wild_fullchain.pem:/etc/certs/fullchain_wild.pem
|
- ./conf/certs/oneskyit_wild_fullchain.pem:/etc/certs/fullchain_wild.pem
|
||||||
- ./conf/certs/oneskyit_wild_privkey.pem:/etc/certs/privkey_wild.pem
|
- ./conf/certs/oneskyit_wild_privkey.pem:/etc/certs/privkey_wild.pem
|
||||||
- ./conf/certs/oneskyit.com_fullchain.pem:/etc/certs/fullchain.pem
|
- ./conf/certs/oneskyit.com_fullchain.pem:/etc/certs/fullchain.pem
|
||||||
@@ -40,7 +39,6 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- ae_api
|
- ae_api
|
||||||
- ae_app
|
- ae_app
|
||||||
# - aether_app_gunicorn
|
|
||||||
logging:
|
logging:
|
||||||
driver: "json-file"
|
driver: "json-file"
|
||||||
options:
|
options:
|
||||||
@@ -54,6 +52,8 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- default
|
- default
|
||||||
- shared
|
- shared
|
||||||
|
environment:
|
||||||
|
- TZ=${TZ}
|
||||||
command: redis-server --save "" --loglevel warning
|
command: redis-server --save "" --loglevel warning
|
||||||
logging:
|
logging:
|
||||||
driver: "json-file"
|
driver: "json-file"
|
||||||
@@ -82,6 +82,7 @@ services:
|
|||||||
MYSQL_DATABASE: ${AE_DB_NAME}
|
MYSQL_DATABASE: ${AE_DB_NAME}
|
||||||
MYSQL_USER: ${AE_DB_USERNAME}
|
MYSQL_USER: ${AE_DB_USERNAME}
|
||||||
MYSQL_PASSWORD: ${AE_DB_PASSWORD}
|
MYSQL_PASSWORD: ${AE_DB_PASSWORD}
|
||||||
|
TZ: ${TZ}
|
||||||
ports:
|
ports:
|
||||||
- "${AE_DB_EXTERNAL_PORT}:3306"
|
- "${AE_DB_EXTERNAL_PORT}:3306"
|
||||||
volumes:
|
volumes:
|
||||||
@@ -103,6 +104,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
PMA_HOST: mariadb
|
PMA_HOST: mariadb
|
||||||
UPLOAD_LIMIT: 64M
|
UPLOAD_LIMIT: 64M
|
||||||
|
TZ: ${TZ}
|
||||||
ports:
|
ports:
|
||||||
- "${AE_PMA_PORT}:80"
|
- "${AE_PMA_PORT}:80"
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -180,10 +182,8 @@ services:
|
|||||||
home.oneskyit.com: "71.126.159.102"
|
home.oneskyit.com: "71.126.159.102"
|
||||||
static.oneskyit.com: "104.237.143.4"
|
static.oneskyit.com: "104.237.143.4"
|
||||||
dev.oneskyit.com: "192.168.32.7"
|
dev.oneskyit.com: "192.168.32.7"
|
||||||
# volumes:
|
volumes:
|
||||||
# # In production, the build happens INSIDE the container.
|
- ./logs/ae_app:/logs
|
||||||
# # Mounting the host source here would override the internal build.
|
|
||||||
# # - ${AE_APP_SRC}:/app
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- ae_api
|
- ae_api
|
||||||
- redis
|
- redis
|
||||||
@@ -197,10 +197,12 @@ services:
|
|||||||
dozzle:
|
dozzle:
|
||||||
container_name: ${CONTAINER_DOZZLE:-ae_dozzle_dev}
|
container_name: ${CONTAINER_DOZZLE:-ae_dozzle_dev}
|
||||||
image: amir20/dozzle:latest
|
image: amir20/dozzle:latest
|
||||||
|
environment:
|
||||||
|
- TZ=${TZ}
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
ports:
|
ports:
|
||||||
- "${AE_DOZZLE_PORT:-8881}:8080"
|
- "127.0.0.1:${AE_DOZZLE_PORT:-8881}:8080"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging:
|
logging:
|
||||||
driver: "json-file"
|
driver: "json-file"
|
||||||
@@ -209,7 +211,6 @@ services:
|
|||||||
max-file: "3"
|
max-file: "3"
|
||||||
|
|
||||||
ae_ops:
|
ae_ops:
|
||||||
# ... (same as before) ...
|
|
||||||
container_name: ${CONTAINER_AE_OPS:-ae_ops_dev}
|
container_name: ${CONTAINER_AE_OPS:-ae_ops_dev}
|
||||||
image: alpine:latest
|
image: alpine:latest
|
||||||
restart: always
|
restart: always
|
||||||
@@ -224,7 +225,8 @@ services:
|
|||||||
- ./scripts:/scripts
|
- ./scripts:/scripts
|
||||||
- ./logs:/logs
|
- ./logs:/logs
|
||||||
- ./conf/crontab:/etc/crontabs/root
|
- ./conf/crontab:/etc/crontabs/root
|
||||||
command: sh -c "apk add --no-cache docker-cli bash && crond -f -l 2"
|
- ./conf/logrotate.conf:/etc/logrotate.conf
|
||||||
|
command: sh -c "apk add --no-cache docker-cli bash logrotate && adduser -u 1000 -D aether && cp /etc/logrotate.conf /etc/logrotate.internal.conf && chown root:root /etc/logrotate.internal.conf && crond -f -l 2"
|
||||||
depends_on:
|
depends_on:
|
||||||
- mariadb
|
- mariadb
|
||||||
logging:
|
logging:
|
||||||
|
|||||||
Reference in New Issue
Block a user