go/playbooks/redis-deploy.yml
Snider 349e8daa0b feat(prod): add production infrastructure management
Add `core prod` command with full production infrastructure tooling:

- `core prod status` — parallel SSH health checks across all hosts,
  Galera cluster state, Redis sentinel, Docker, LB health
- `core prod setup` — Phase 1 foundation: Hetzner topology discovery,
  managed LB creation, CloudNS DNS record management
- `core prod dns` — CloudNS record CRUD with idempotent EnsureRecord
- `core prod lb` — Hetzner Cloud LB status and creation
- `core prod ssh <host>` — SSH into hosts defined in infra.yaml

New packages:
- pkg/infra: config parsing, Hetzner Cloud/Robot API, CloudNS DNS API
- infra.yaml: declarative production topology (hosts, LB, DNS, SSL,
  Galera, Redis, containers, S3, CDN, CI/CD, monitoring, backups)

Docker:
- Dockerfile.app (PHP 8.3-FPM, multi-stage)
- Dockerfile.web (Nginx + security headers)
- docker-compose.prod.yml (app, web, horizon, scheduler, mcp, redis, galera)

Ansible playbooks (runnable via `core deploy ansible`):
- galera-deploy.yml, redis-deploy.yml, galera-backup.yml
- inventory.yml with all production hosts

CI/CD:
- .forgejo/workflows/deploy.yml for Forgejo Actions pipeline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 03:03:29 +00:00

98 lines
2.9 KiB
YAML

# Redis Sentinel Deployment
# Deploys Redis with Sentinel on de + de2
#
# Usage:
# core deploy ansible playbooks/redis-deploy.yml -i playbooks/inventory.yml
---
- name: Deploy Redis with Sentinel
hosts: app_servers
become: true
vars:
redis_version: "7"
redis_password: "{{ lookup('env', 'REDIS_PASSWORD') | default('', true) }}"
tasks:
- name: Create Redis data directory
file:
path: /opt/redis/data
state: directory
mode: "0755"
- name: Create Redis config directory
file:
path: /opt/redis/conf
state: directory
mode: "0755"
- name: Write Redis configuration
copy:
dest: /opt/redis/conf/redis.conf
content: |
maxmemory {{ redis_maxmemory }}
maxmemory-policy allkeys-lru
appendonly yes
appendfsync everysec
tcp-keepalive 300
timeout 0
{% if redis_password %}
requirepass {{ redis_password }}
masterauth {{ redis_password }}
{% endif %}
- name: Write Sentinel configuration
copy:
dest: /opt/redis/conf/sentinel.conf
content: |
port 26379
sentinel monitor hostuk-redis 116.202.82.115 6379 2
sentinel down-after-milliseconds hostuk-redis 5000
sentinel failover-timeout hostuk-redis 60000
sentinel parallel-syncs hostuk-redis 1
{% if redis_password %}
sentinel auth-pass hostuk-redis {{ redis_password }}
{% endif %}
- name: Stop existing Redis containers
shell: |
docker stop redis redis-sentinel 2>/dev/null || true
docker rm redis redis-sentinel 2>/dev/null || true
changed_when: false
- name: Start Redis container
shell: |
docker run -d \
--name redis \
--restart unless-stopped \
--network host \
-v /opt/redis/data:/data \
-v /opt/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf \
redis:{{ redis_version }}-alpine \
redis-server /usr/local/etc/redis/redis.conf
- name: Start Redis Sentinel container
shell: |
docker run -d \
--name redis-sentinel \
--restart unless-stopped \
--network host \
-v /opt/redis/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf \
redis:{{ redis_version }}-alpine \
redis-sentinel /usr/local/etc/redis/sentinel.conf
- name: Wait for Redis to be ready
shell: |
for i in $(seq 1 30); do
docker exec redis redis-cli ping 2>/dev/null | grep -q PONG && exit 0
sleep 1
done
exit 1
changed_when: false
- name: Check Redis info
shell: docker exec redis redis-cli info replication | head -10
register: redis_info
changed_when: false
- name: Display Redis info
debug:
var: redis_info.stdout_lines