Docker for prod and staging

This commit is contained in:
Mario Peters
2026-01-04 13:37:22 +01:00
parent 9e06fe694b
commit 9ee87fe4e2
8 changed files with 854 additions and 450 deletions

8
.env
View File

@@ -1,12 +1,12 @@
DB_HOST=localhost
DB_HOST=db
DB_PORT=3306
DB_USER=root
DB_USER=admin
DB_DB=karateturniere
DB_PASS=mmario
HOST=localhost
HOST=0.0.0.0
PORT=8000
JWT_KEY=deiomnfre7309%dsoi(6b%v3vK!nV/VCV
MAIL=m.peters@karatenw.de
MAIL=mario@karateturniere.de
NODE_ENV=development
ACCOUNT_USER='mario@wattsche.de'
ACCOUNT_PASS=mmario84

3
.gitignore vendored
View File

@@ -10,7 +10,8 @@ log.txt
*.sublime-project
*.sublime-workspace
npm-debug.log*
.env.prod
.env.production
.env.staging
.idea/
.sass-cache/

43
docker-compose.prod.yml Normal file
View File

@@ -0,0 +1,43 @@
services:
backend:
image: kt-backend-${NODE_ENV}:latest
container_name: kt-backend-${NODE_ENV}
restart: always
ports:
- '${HOST_PORT}:8000'
env_file:
- .env
depends_on:
db:
condition: service_healthy
networks:
- kt-net
db:
image: mariadb:latest
container_name: kt-db-${NODE_ENV}
restart: always
environment:
MARIADB_ROOT_PASSWORD: root
MARIADB_DATABASE: karateturniere
MARIADB_USER: ${DB_USER}
MARIADB_PASSWORD: ${DB_PASS}
ports:
- '127.0.0.1:${DB_PORT_EXT}:3306'
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: ['CMD-SHELL', 'healthcheck.sh --connect --innodb_initialized']
interval: 5s
timeout: 3s
retries: 10
start_period: 5s
networks:
- kt-net
volumes:
db_data:
networks:
kt-net:
name: kt-net-${NODE_ENV}

View File

@@ -1,27 +1,33 @@
version: '3.8'
services:
backend:
build: .
container_name: kt-backend
ports:
- '8000:8000'
- '8000:80'
env_file:
- ${ENV_FILE:-.env}
restart: unless-stopped
depends_on:
- db
db:
condition: service_healthy
db:
image: mariadb:11
image: mariadb:latest
container_name: kt-db
restart: unless-stopped
environment:
MARIADB_ROOT_PASSWORD: root
MARIADB_DATABASE: karateturniere
MARIADB_USER: admin
MARIADB_PASSWORD: mmario
ports:
- '3306:3306'
- '3308:3306'
healthcheck:
test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost']
interval: 5s
timeout: 3s
retries: 10
start_period: 5s
volumes:
- db_data:/var/lib/mysql

1144
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "kt-backend",
"version": "2.0.0",
"version": "2.0.1",
"description": "Karateturniere.de Backend",
"main": "server.js",
"scripts": {
@@ -16,21 +16,21 @@
"author": "Mario Peters",
"license": "private",
"dependencies": {
"@hapi/hapi": "^21.4.3",
"@hapi/hapi": "^21.4.4",
"bcrypt": "^6.0.0",
"dotenv": "^17.2.3",
"hapi-auth-jwt2": "^11.0.0",
"hapi-plugin-mysql": "^7.2.7",
"hapi-plugin-websocket": "^2.4.11",
"joi": "^18.0.1",
"jsonwebtoken": "^9.0.2",
"joi": "^18.0.2",
"jsonwebtoken": "^9.0.3",
"sendmail": "^1.6.1"
},
"devDependencies": {
"@hapi/basic": "^7.0.2",
"@hapi/code": "^9.0.3",
"@hapi/lab": "^26.0.0",
"eslint": "^9.36.0",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8"
}
}

View File

@@ -15,7 +15,7 @@ cp server.js $DEPLOY_DIR/
cp package.json $DEPLOY_DIR/
cp package-lock.json $DEPLOY_DIR/
cp package-lock.json $DEPLOY_DIR/
cp .env.prod $DEPLOY_DIR/.env
cp .env.production $DEPLOY_DIR/.env
# TODO: remove
cp auth.js $DEPLOY_DIR/
cp routes.js $DEPLOY_DIR/

76
scripts/deploy.sh Executable file
View File

@@ -0,0 +1,76 @@
#!/bin/bash
set -euo pipefail
REMOTE_USER="root"
REMOTE_HOST="it-wattenscheid.de"
MODE="${1:-production}"
REMOTE_DIR="/opt/kt-backend/${MODE}"
ENV_SOURCE="${2:-.env.${MODE}}"
IMAGE_BASENAME="kt-backend-${MODE}"
CONTAINER_NAME="kt-backend-${MODE}"
CONTAINER_PORT="8000"
case "${MODE}" in
staging)
HOST_PORT="8093"
DB_PORT_EXT="3307"
;;
*)
HOST_PORT="8092"
DB_PORT_EXT="3306"
;;
esac
if [[ ! -f "${ENV_SOURCE}" ]]; then
echo "[error] env file '${ENV_SOURCE}' not found" >&2
exit 1
fi
SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=accept-new)
info() {
printf '\033[1;34m[info]\033[0m %s\n' "$1"
}
remote() {
ssh "${SSH_OPTS[@]}" "${REMOTE_USER}@${REMOTE_HOST}" "$@"
}
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
IMAGE_REF="${IMAGE_BASENAME}:${TIMESTAMP}"
TAR_FILE="${IMAGE_BASENAME}-${TIMESTAMP}.tar"
REMOTE_ENV_FILE="${REMOTE_DIR}/.env.${MODE}"
info "Building backend image (${IMAGE_REF})"
docker build --platform linux/amd64 -t "${IMAGE_REF}" --build-arg NODE_ENV="${MODE}" .
TMP_DIR=$(mktemp -d)
trap 'rm -rf "${TMP_DIR}"' EXIT
docker save "${IMAGE_REF}" -o "${TMP_DIR}/${TAR_FILE}"
info "Preparing remote directory"
remote "mkdir -p ${REMOTE_DIR}"
info "Uploading artifact"
scp "${TMP_DIR}/${TAR_FILE}" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/${TAR_FILE}"
info "Uploading env file (${ENV_SOURCE} -> ${REMOTE_DIR}/.env)"
scp "${ENV_SOURCE}" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/.env"
remote "chmod 600 ${REMOTE_DIR}/.env"
info "Uploading docker-compose.prod.yml"
scp "docker-compose.prod.yml" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/docker-compose.yml"
info "Loading image on remote host"
remote "docker load -i ${REMOTE_DIR}/${TAR_FILE} && rm ${REMOTE_DIR}/${TAR_FILE}"
info "Tagging image as latest"
remote "docker tag ${IMAGE_REF} ${IMAGE_BASENAME}:latest"
info "Restarting stack with Docker Compose"
# We pass NODE_ENV and HOST_PORT to the compose command so they can be used in docker-compose.yml
remote "cd ${REMOTE_DIR} && NODE_ENV=${MODE} HOST_PORT=${HOST_PORT} DB_PORT_EXT=${DB_PORT_EXT} docker compose up -d"
info "Deployment complete: http://${REMOTE_HOST}:${HOST_PORT}"