Add production deployment: Dockerfile, docker-compose, deploy script

- Dockerfile: multi-stage build (golang:1.25-alpine → alpine:3.21), linux/amd64
- infra/mariadb: docker-compose + init SQL for reanimator DB/user
- infra/reanimator: docker-compose for reanimator-api (Gitea registry)
- deploy.sh: local build → push to git.mchus.pro → SSH deploy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-03-02 14:00:54 +03:00
parent 4c284505a8
commit 0e72a1d284
7 changed files with 134 additions and 0 deletions

23
Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
FROM golang:1.25-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o reanimator-api ./cmd/reanimator-api
FROM alpine:3.21
RUN apk add --no-cache tzdata ca-certificates
WORKDIR /app
COPY --from=builder /build/reanimator-api ./reanimator-api
COPY --from=builder /build/migrations ./migrations
EXPOSE 9999
ENTRYPOINT ["./reanimator-api"]

55
deploy.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/usr/bin/env bash
set -euo pipefail
IMAGE="git.mchus.pro/reanimator/core"
TAG="${1:-latest}"
DEPLOY_DIR="/appdata/reanimator"
echo "Deploy target:"
echo " 1) local"
echo " 2) remote (SSH)"
read -rp "Choice [1/2]: " choice
case "$choice" in
1)
TARGET="local"
;;
2)
read -rp "SSH target (login@hostname): " SSH_TARGET
TARGET="ssh"
;;
*)
echo "Invalid choice" >&2
exit 1
;;
esac
echo "==> Building and pushing $IMAGE:$TAG (linux/amd64)..."
docker buildx build --platform linux/amd64 -t "$IMAGE:$TAG" --push .
if [ "$TARGET" = "local" ]; then
echo "==> Deploying locally..."
docker pull "$IMAGE:$TAG"
cd "$DEPLOY_DIR"
docker compose pull
docker compose up -d --remove-orphans
echo "-- Status:"
docker compose ps
echo "==> Done."
else
echo "==> Deploying on $SSH_TARGET..."
ssh "$SSH_TARGET" "
set -euo pipefail
echo '-- Pulling image...'
docker pull $IMAGE:$TAG
echo '-- Restarting container...'
cd $DEPLOY_DIR
docker compose pull
docker compose up -d --remove-orphans
echo '-- Status:'
docker compose ps
"
echo "==> Done."
fi

View File

@@ -0,0 +1,17 @@
services:
mariadb:
image: mariadb:11.4
container_name: mariadb
restart: unless-stopped
env_file: /appdata/mariadb/mariadb.env
ports:
- "3306:3306"
volumes:
- /appdata/mariadb/data:/var/lib/mysql
- /appdata/mariadb/init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s

View File

@@ -0,0 +1,9 @@
CREATE DATABASE IF NOT EXISTS reanimator
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'reanimator'@'%' IDENTIFIED BY 'REANIMATOR_PASSWORD_PLACEHOLDER';
GRANT ALL PRIVILEGES ON reanimator.* TO 'reanimator'@'%';
FLUSH PRIVILEGES;

View File

@@ -0,0 +1,9 @@
# Скопировать на сервер: /appdata/mariadb/mariadb.env
# Заменить все значения на реальные
MARIADB_ROOT_PASSWORD=changeme_root
# Пользователь для reanimator (должен совпадать с паролем в init/01_reanimator.sql)
MARIADB_USER=reanimator
MARIADB_PASSWORD=changeme_reanimator
MARIADB_DATABASE=reanimator

View File

@@ -0,0 +1,16 @@
services:
reanimator-api:
image: git.mchus.pro/reanimator/core:latest
container_name: reanimator-api
restart: unless-stopped
env_file: /appdata/reanimator/reanimator.env
ports:
- "9999:9999"
extra_hosts:
- "host.docker.internal:host-gateway"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:9999/health || exit 1"]
interval: 15s
timeout: 5s
retries: 3
start_period: 20s

View File

@@ -0,0 +1,5 @@
# Скопировать на сервер: /appdata/reanimator/reanimator.env
HTTP_ADDR=:9999
DATABASE_DSN=reanimator:PASSWORD@tcp(host.docker.internal:3306)/reanimator?parseTime=true
MIGRATIONS_DIR=/app/migrations