#!/usr/bin/env bash # ============================================================================= # 03-start-infra.sh — 启动 Docker 基础设施(Redis / Kafka / Etcd / LiveKit) # # 数据目录: .local-dev/docker-data// # 容器日志: .local-dev/docker-logs//-YYYYMMDD.log(每日一文件) # 脚本日志: .local-dev/script-logs/03-start-infra-.log # # 后续步骤:04-build.sh(编译后端服务) # ============================================================================= set -euo pipefail source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh" load_env init_dirs init_script_log # ← 脚本执行日志 require_docker_running # Docker 日志驱动公共参数(JSON 文件,最多 5 个 50MB 轮转) LOG_OPTS=( --log-driver json-file --log-opt max-size=50m --log-opt max-file=5 ) header "步骤 3 / 5 — 启动 Docker 基础设施" # ────────────────────────────────────────────────────────────────────────────── # Redis # ────────────────────────────────────────────────────────────────────────────── step "Redis" if docker ps --format '{{.Names}}' | grep -q '^dev-redis$'; then success "Redis 已在运行 (container=dev-redis) :${REDIS_PORT}" elif docker ps -a --format '{{.Names}}' | grep -q '^dev-redis$'; then info "重新启动已有容器 dev-redis..." docker start dev-redis > /dev/null success "Redis 已启动 :${REDIS_PORT}" else info "创建并启动 Redis 容器..." docker run -d \ --name dev-redis \ --restart unless-stopped \ -p "${REDIS_PORT}:6379" \ -v "${DATA_DIR}/redis:/data" \ "${LOG_OPTS[@]}" \ redis:7-alpine \ redis-server --requirepass "${REDIS_PASSWORD:-openIM123}" --appendonly yes \ > /dev/null success "Redis 容器已创建并启动 :${REDIS_PORT} (密码: ${REDIS_PASSWORD:-openIM123})" fi sleep 1 if docker exec dev-redis redis-cli -a "${REDIS_PASSWORD:-openIM123}" ping 2>/dev/null | grep -q PONG; then success "Redis 连通性: PONG ✓" else warn "Redis 未响应 PING,请查看日志: ./deploy-test/logs.sh redis" fi start_docker_logger "dev-redis" # ────────────────────────────────────────────────────────────────────────────── # Kafka(KRaft 模式) # ────────────────────────────────────────────────────────────────────────────── step "Kafka (KRaft)" if docker ps --format '{{.Names}}' | grep -q '^dev-kafka$'; then success "Kafka 已在运行 (container=dev-kafka) :${KAFKA_PORT}" elif docker ps -a --format '{{.Names}}' | grep -q '^dev-kafka$'; then info "重新启动已有容器 dev-kafka..." docker start dev-kafka > /dev/null info "等待 Kafka 就绪 (8s)..." sleep 8 success "Kafka 已启动 :${KAFKA_PORT}" else info "创建并启动 Kafka 容器(首次拉取镜像可能较慢)..." KAFKA_CLUSTER_ID="MkU3OEVBNTcwNTJENDM2Qk" docker run -d \ --name dev-kafka \ --restart unless-stopped \ -p "${KAFKA_PORT}:9092" \ -v "${DATA_DIR}/kafka:/bitnami/kafka" \ -e KAFKA_CFG_NODE_ID=0 \ -e KAFKA_CFG_PROCESS_ROLES=controller,broker \ -e KAFKA_CFG_LISTENERS="PLAINTEXT://:9092,CONTROLLER://:9093" \ -e KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP="CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" \ -e KAFKA_CFG_CONTROLLER_QUORUM_VOTERS="0@localhost:9093" \ -e KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER \ -e KAFKA_CFG_ADVERTISED_LISTENERS="PLAINTEXT://127.0.0.1:${KAFKA_PORT}" \ -e KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true \ -e KAFKA_KRAFT_CLUSTER_ID="$KAFKA_CLUSTER_ID" \ "${LOG_OPTS[@]}" \ bitnami/kafka:3.7 \ > /dev/null info "等待 Kafka 就绪 (15s)..." sleep 15 success "Kafka 容器已创建并启动 :${KAFKA_PORT}" fi start_docker_logger "dev-kafka" # 初始化必要 Topics step "Kafka Topics 初始化" TOPICS=(toRedis toMongo toPush toOfflinePush) for topic in "${TOPICS[@]}"; do if docker exec dev-kafka kafka-topics.sh \ --bootstrap-server localhost:9092 \ --list 2>/dev/null | grep -q "^${topic}$"; then info " topic 已存在: $topic" else docker exec dev-kafka kafka-topics.sh \ --create \ --topic "$topic" \ --bootstrap-server localhost:9092 \ --partitions 8 \ --replication-factor 1 \ 2>/dev/null \ && success " ✓ 创建 topic: $topic" \ || warn " ✗ 创建失败: $topic(Kafka 未就绪,重试: ./deploy-test/03-start-infra.sh)" fi done # ────────────────────────────────────────────────────────────────────────────── # Etcd # ────────────────────────────────────────────────────────────────────────────── step "Etcd" if docker ps --format '{{.Names}}' | grep -q '^dev-etcd$'; then success "Etcd 已在运行 (container=dev-etcd) :${ETCD_PORT}" elif docker ps -a --format '{{.Names}}' | grep -q '^dev-etcd$'; then info "重新启动已有容器 dev-etcd..." docker start dev-etcd > /dev/null sleep 2 success "Etcd 已启动 :${ETCD_PORT}" else info "创建并启动 Etcd 容器..." docker run -d \ --name dev-etcd \ --restart unless-stopped \ -p "${ETCD_PORT}:2379" \ -v "${DATA_DIR}/etcd:/etcd-data" \ -e ALLOW_NONE_AUTHENTICATION=yes \ -e ETCD_DATA_DIR=/etcd-data \ "${LOG_OPTS[@]}" \ bitnami/etcd:3.5 \ > /dev/null sleep 2 success "Etcd 容器已创建并启动 :${ETCD_PORT}" fi if docker exec dev-etcd etcdctl endpoint health 2>/dev/null | grep -q 'is healthy'; then success "Etcd 连通性: healthy ✓" else warn "Etcd 未返回健康状态,可能仍在启动中" fi start_docker_logger "dev-etcd" # ────────────────────────────────────────────────────────────────────────────── # LiveKit(本地 Docker 容器,复用 dev-redis) # ────────────────────────────────────────────────────────────────────────────── step "LiveKit" LK_CONF="$ROOT_DIR/livekit/livekit.yaml" if [[ ! -f "$LK_CONF" ]]; then warn "livekit/livekit.yaml 不存在,请先执行: ./deploy-test/02-patch-config.sh" else if docker ps --format '{{.Names}}' | grep -q '^dev-livekit$'; then success "LiveKit 已在运行 (container=dev-livekit) :7880" elif docker ps -a --format '{{.Names}}' | grep -q '^dev-livekit$'; then info "重新启动已有容器 dev-livekit..." docker start dev-livekit > /dev/null sleep 2 success "LiveKit 已启动 :7880" else info "创建并启动 LiveKit 容器(首次拉取镜像需要一点时间)..." EXTRA_HOSTS="" [[ "$(uname -s)" == "Linux" ]] && EXTRA_HOSTS="--add-host host.docker.internal:host-gateway" # shellcheck disable=SC2086 docker run -d \ --name dev-livekit \ --restart unless-stopped \ -p 7880:7880 \ -p 7882:7882/tcp \ -p 7882:7882/udp \ -p 50000-51000:50000-51000/udp \ -v "${LK_CONF}:/etc/livekit.yaml:ro" \ "${LOG_OPTS[@]}" \ $EXTRA_HOSTS \ livekit/livekit-server:latest \ --config /etc/livekit.yaml \ > /dev/null sleep 3 if docker ps --format '{{.Names}}' | grep -q '^dev-livekit$'; then success "LiveKit 容器已创建并启动 :7880" info " 公网 IP: ${LIVEKIT_NODE_IP} (WebRTC 媒体流直连)" info " API Key: ${LIVEKIT_API_KEY}" else error "LiveKit 启动失败,查看日志: ./deploy-test/logs.sh livekit" fi fi start_docker_logger "dev-livekit" fi # ────────────────────────────────────────────────────────────────────────────── # 汇总 # ────────────────────────────────────────────────────────────────────────────── echo "" echo -e "${BOLD}基础设施状态:${NC}" print_container_status "Redis" "dev-redis" "${REDIS_PORT}" print_container_status "Kafka" "dev-kafka" "${KAFKA_PORT}" print_container_status "Etcd" "dev-etcd" "${ETCD_PORT}" print_container_status "LiveKit" "dev-livekit" "7880" echo "" echo -e "${BOLD}日志目录:${NC}" echo " Docker 容器日志: $DOCKER_LOG_DIR/" echo " 本脚本执行日志: $_CURRENT_SCRIPT_LOG" echo "" echo -e " LiveKit 公网: ${LIVEKIT_NODE_IP}:50000-51000/udp (WebRTC 媒体流)" echo "" success "Docker 基础设施已就绪!" echo "" echo -e "${BOLD}下一步:${NC}" echo -e " 编译所有后端 Go 服务:" echo -e " ${CYAN}./deploy-test/04-build.sh${NC}"