Add Swarm MySQL stack for cubex (dev/prod), deploy dev
deploy/jorgecuadros-db.stack.yml: canonical internal MySQL for the platform, targeting the Portainer local endpoint on cubex (3-node Swarm). Parametrized (MYSQL_PORT / MYSQL_SERVER_ID) so one file deploys both environments as two stacks with Swarm-namespaced volumes: dev -> jorgecuadros-dev-db :3307 server-id 11 prod -> jorgecuadros-prod-db :3306 server-id 1 (replication source) Swarm-correct: named volume (no bind mount), pinned to one node via node.labels.jorgecuadros_db==true (cubex labeled), binlog+GTID enabled from the start so prod can be the VPS replication source without reconfigure. Dev deployed and verified: MySQL 8.4.10 reachable at 192.168.4.212:3307, gtid_mode ON, database jorgecuadros present. Secrets live in gitignored deploy/.env.dev, injected via Portainer stack env at deploy time. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# Canonical internal MySQL for the Jorge Cuadros platform.
|
||||
#
|
||||
# Target: Portainer "local" endpoint on cubex (192.168.4.212:9443), which is a
|
||||
# 3-node Docker Swarm. This is the source-of-truth database from the plan's
|
||||
# "internal server" — the migration transform+load and the NestJS API both
|
||||
# point at it, and (later) it becomes the replication SOURCE for the VPS.
|
||||
#
|
||||
# DEV / PROD as two stacks from this one file. Deploy it twice with different
|
||||
# stack names + env_data:
|
||||
# dev : stack jorgecuadros-dev-db MYSQL_PORT=3307 MYSQL_SERVER_ID=11
|
||||
# prod: stack jorgecuadros-prod-db MYSQL_PORT=3306 MYSQL_SERVER_ID=1
|
||||
# Swarm namespaces the named volume by stack name, so the two environments get
|
||||
# fully isolated data (jorgecuadros-dev-db_mysql_data vs -prod-db_...) with no
|
||||
# extra config. Distinct published ports let both run on the same node at once.
|
||||
# server-id must be unique per environment (prod=1 is the replication source).
|
||||
#
|
||||
# Swarm statefulness rules (see portainer-gitea-deploy skill):
|
||||
# - named volume, never a relative bind mount (the API deploy path won't
|
||||
# create host dirs -> "bind source path does not exist").
|
||||
# - a named volume is LOCAL to whichever node the task lands on. With no
|
||||
# shared storage, the DB MUST be pinned to one node or a reschedule would
|
||||
# start against a fresh empty volume. Pinned here via a node label so it is
|
||||
# not tied to a hostname: label exactly one node with
|
||||
# docker node update --label-add jorgecuadros_db=true <node>
|
||||
# and MySQL will always run there, reusing the same volume.
|
||||
#
|
||||
# Secrets (MYSQL_ROOT_PASSWORD, MYSQL_PASSWORD) are injected at deploy time via
|
||||
# Portainer env_data / stack environment, not committed here.
|
||||
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.4
|
||||
command:
|
||||
# (caching_sha2_password is already the default in 8.4; the old
|
||||
# --default-authentication-plugin flag was REMOVED in 8.4 and aborts boot.)
|
||||
# binlog + GTID on from day one so this node can be the replication
|
||||
# SOURCE for the VPS replica later without a restart/reconfigure.
|
||||
- --server-id=${MYSQL_SERVER_ID:-1}
|
||||
- --log-bin=mysql-bin
|
||||
- --binlog-format=ROW
|
||||
- --gtid-mode=ON
|
||||
- --enforce-gtid-consistency=ON
|
||||
environment:
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE:-jorgecuadros}
|
||||
MYSQL_USER: ${MYSQL_USER:-jorgecuadros}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD:?MYSQL_PASSWORD must be set}
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?MYSQL_ROOT_PASSWORD must be set}
|
||||
ports:
|
||||
# Swarm ingress publishes on every node, so this is reachable at
|
||||
# 192.168.4.212:${MYSQL_PORT} regardless of which node the task pins to.
|
||||
- target: 3306
|
||||
published: ${MYSQL_PORT:-3306}
|
||||
protocol: tcp
|
||||
mode: ingress
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
deploy:
|
||||
replicas: 1
|
||||
placement:
|
||||
constraints:
|
||||
- node.labels.jorgecuadros_db == true
|
||||
restart_policy:
|
||||
condition: any
|
||||
update_config:
|
||||
order: stop-first
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 40s
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
Reference in New Issue
Block a user