| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- #!/usr/bin/env bash
- set -euo pipefail
- if ! docker info >/dev/null 2>&1; then
- echo "❌ Docker daemon is not running."
- echo "Please start Docker first:"
- echo " sudo systemctl start docker"
- exit 1
- fi
- NETWORK="box-network"
- MONGO_CONTAINER="box-mongodb"
- MONGO_INIT_CONTAINER="box-mongodb-init"
- REDIS_CONTAINER="box-redis"
- RABBIT_CONTAINER="box-rabbitmq"
- MYSQL_CONTAINER="box-mysql"
- MONGO_VOLUME="box_mongo_data"
- REDIS_VOLUME="box_redis_data"
- RABBIT_VOLUME="box_rabbitmq_data"
- MYSQL_VOLUME="box_mysql_data"
- MONGO_USER="boxadmin"
- MONGO_PASS="boxpass"
- MONGO_RS="rs0"
- MONGO_DB="box_admin"
- MYSQL_ROOT_PASS="rootpass"
- MYSQL_DB="box_admin"
- KEYFILE_HOST_PATH="docker/mongo/mongo-keyfile"
- KEYFILE_CONTAINER_PATH="/etc/mongo-keyfile/mongo-keyfile"
- INIT_JS_HOST_PATH="docker/mongo/init-repl.js"
- INIT_JS_CONTAINER_PATH="/init-repl.js"
- die() {
- echo "ERROR: $*" >&2
- exit 1
- }
- ensure_network() {
- if ! docker network inspect "$NETWORK" >/dev/null 2>&1; then
- docker network create "$NETWORK" >/dev/null
- fi
- }
- ensure_volume() {
- local v="$1"
- if ! docker volume inspect "$v" >/dev/null 2>&1; then
- docker volume create "$v" >/dev/null
- fi
- }
- rm_if_exists() {
- local c="$1"
- if docker ps -a --format '{{.Names}}' | grep -qx "$c"; then
- docker rm -f "$c" >/dev/null || true
- fi
- }
- container_running() {
- local c="$1"
- docker ps --format '{{.Names}}' | grep -qx "$c"
- }
- require_file() {
- local p="$1"
- [ -f "$p" ] || die "missing file: $p"
- }
- fix_keyfile_perms() {
- # Mongo requires keyfile permissions to be restricted.
- # In containers, mongod typically runs as uid 999 ("mongodb").
- # If host keyfile is 600 but owned by root, uid 999 can't read it.
- require_file "$KEYFILE_HOST_PATH"
- echo "== ensuring mongo keyfile perms (uid 999 can read, not world-readable) =="
- # Try to set ownership to 999:999 when sudo is available (best reliability)
- if command -v sudo >/dev/null 2>&1; then
- sudo chown 999:999 "$KEYFILE_HOST_PATH" || true
- sudo chmod 400 "$KEYFILE_HOST_PATH" || true
- else
- # fallback: at least restrict perms; ownership may still be an issue
- chmod 400 "$KEYFILE_HOST_PATH" || true
- fi
- echo "keyfile perms:"
- ls -l "$KEYFILE_HOST_PATH" || true
- }
- wait_for_container_or_fail_fast() {
- local c="$1"
- local seconds="${2:-10}"
- for _ in $(seq 1 "$seconds"); do
- if container_running "$c"; then
- return 0
- fi
- sleep 1
- done
- echo "== container '$c' is not running. last logs: =="
- docker logs "$c" --tail=200 || true
- return 1
- }
- wait_mongo_ping() {
- echo "== waiting for mongo ping =="
- for i in $(seq 1 90); do
- if docker exec "$MONGO_CONTAINER" mongosh --quiet \
- "mongodb://${MONGO_USER}:${MONGO_PASS}@127.0.0.1:27017/admin?authSource=admin" \
- --eval 'quit(db.adminCommand({ ping: 1 }).ok ? 0 : 1)' >/dev/null 2>&1; then
- echo "mongo ping ok"
- return 0
- fi
- # Fail fast if container died
- if ! container_running "$MONGO_CONTAINER"; then
- echo "== mongo container stopped. last logs: =="
- docker logs "$MONGO_CONTAINER" --tail=200 || true
- return 1
- fi
- sleep 1
- done
- echo "mongo did not become ready (ping) in time"
- echo "== last mongo logs: =="
- docker logs "$MONGO_CONTAINER" --tail=200 || true
- return 1
- }
- wait_mongo_rs_ok() {
- echo "== waiting for mongo replica set (rs.status().ok == 1) =="
- for i in $(seq 1 90); do
- if docker exec "$MONGO_CONTAINER" mongosh --quiet \
- "mongodb://${MONGO_USER}:${MONGO_PASS}@127.0.0.1:27017/admin?authSource=admin" \
- --eval 'try{quit(rs.status().ok===1?0:1)}catch(e){quit(1)}' >/dev/null 2>&1; then
- echo "mongo rs ok"
- return 0
- fi
- sleep 1
- done
- echo "mongo replica set not ok in time"
- docker exec "$MONGO_CONTAINER" mongosh --quiet \
- "mongodb://${MONGO_USER}:${MONGO_PASS}@127.0.0.1:27017/admin?authSource=admin" \
- --eval 'try{printjson(rs.status())}catch(e){print(e)}' || true
- return 1
- }
- wait_mysql_ping() {
- echo "== waiting for mysql ping =="
- for i in $(seq 1 90); do
- if docker exec "$MYSQL_CONTAINER" mysqladmin ping -h 127.0.0.1 -p"${MYSQL_ROOT_PASS}" --silent >/dev/null 2>&1; then
- echo "mysql ping ok"
- return 0
- fi
- if ! container_running "$MYSQL_CONTAINER"; then
- echo "== mysql container stopped. last logs: =="
- docker logs "$MYSQL_CONTAINER" --tail=200 || true
- return 1
- fi
- sleep 1
- done
- echo "mysql did not become ready in time"
- docker logs "$MYSQL_CONTAINER" --tail=200 || true
- return 1
- }
- start_redis() {
- echo "== starting redis =="
- rm_if_exists "$REDIS_CONTAINER"
- docker run -d \
- --name "$REDIS_CONTAINER" \
- --restart unless-stopped \
- --network "$NETWORK" \
- -p 6379:6379 \
- -v "${REDIS_VOLUME}:/data" \
- redis:7 >/dev/null
- }
- start_rabbitmq() {
- echo "== starting rabbitmq =="
- rm_if_exists "$RABBIT_CONTAINER"
- docker run -d \
- --name "$RABBIT_CONTAINER" \
- --restart unless-stopped \
- --network "$NETWORK" \
- -p 5672:5672 \
- -p 15672:15672 \
- -e RABBITMQ_DEFAULT_USER=boxrabbit \
- -e RABBITMQ_DEFAULT_PASS=BoxRabbit2025 \
- -e RABBITMQ_DEFAULT_VHOST=/ \
- -v "${RABBIT_VOLUME}:/var/lib/rabbitmq" \
- rabbitmq:3.12-management >/dev/null
- }
- start_mongodb() {
- echo "== starting mongodb (replSet + auth + keyFile) =="
- require_file "$KEYFILE_HOST_PATH"
- require_file "$INIT_JS_HOST_PATH"
- fix_keyfile_perms
- rm_if_exists "$MONGO_CONTAINER"
- docker run -d \
- --name "$MONGO_CONTAINER" \
- --restart unless-stopped \
- --network "$NETWORK" \
- -p 27017:27017 \
- -e MONGO_INITDB_ROOT_USERNAME="$MONGO_USER" \
- -e MONGO_INITDB_ROOT_PASSWORD="$MONGO_PASS" \
- -e MONGO_INITDB_DATABASE="$MONGO_DB" \
- -v "${MONGO_VOLUME}:/data/db" \
- -v "$(pwd)/${KEYFILE_HOST_PATH}:${KEYFILE_CONTAINER_PATH}:ro" \
- mongo:7 \
- mongod --replSet "$MONGO_RS" --bind_ip_all --keyFile "$KEYFILE_CONTAINER_PATH" >/dev/null
- wait_for_container_or_fail_fast "$MONGO_CONTAINER" 8
- wait_mongo_ping
- }
- init_mongodb_rs() {
- echo "== initializing mongodb replica set (one-shot) =="
- # Run init script (should be idempotent)
- rm_if_exists "$MONGO_INIT_CONTAINER"
- docker run --rm \
- --name "$MONGO_INIT_CONTAINER" \
- --network "$NETWORK" \
- -v "$(pwd)/${INIT_JS_HOST_PATH}:${INIT_JS_CONTAINER_PATH}:ro" \
- mongo:7 \
- mongosh "mongodb://${MONGO_USER}:${MONGO_PASS}@${MONGO_CONTAINER}:27017/admin?authSource=admin" \
- --quiet "$INIT_JS_CONTAINER_PATH"
- wait_mongo_rs_ok
- }
- start_mysql() {
- echo "== starting mysql =="
- rm_if_exists "$MYSQL_CONTAINER"
- docker run -d \
- --name "$MYSQL_CONTAINER" \
- --restart unless-stopped \
- --network "$NETWORK" \
- -p 3306:3306 \
- -e MYSQL_ROOT_PASSWORD="$MYSQL_ROOT_PASS" \
- -e MYSQL_DATABASE="$MYSQL_DB" \
- -v "${MYSQL_VOLUME}:/var/lib/mysql" \
- mysql:8 >/dev/null
- wait_for_container_or_fail_fast "$MYSQL_CONTAINER" 10
- wait_mysql_ping
- }
- print_status() {
- echo
- echo "== infra status =="
- docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" \
- | grep -E 'box-(redis|rabbitmq|mongodb|mysql)' || true
- }
- main() {
- echo "== ensuring network + volumes =="
- ensure_network
- ensure_volume "$MONGO_VOLUME"
- ensure_volume "$REDIS_VOLUME"
- ensure_volume "$RABBIT_VOLUME"
- ensure_volume "$MYSQL_VOLUME"
- start_redis
- start_rabbitmq
- start_mongodb
- init_mongodb_rs
- start_mysql
- print_status
- echo
- echo "DONE. Now run:"
- echo " docker compose --env-file .env.docker up -d --build"
- }
- main "$@"
|