# docker-compose for the yunke data mirror (M6, Part C). # # Five services: # mysql — MySQL 8 data store (volume-backed). # redis — Redis 7 (future cache/queue use; not yet wired into the app). # backend — FastAPI viewing API (uvicorn) talking to mysql + redis. # scheduler — APScheduler loop (python -m app.sync.cli scheduler) that # periodically scrapes yunke CRM into mysql. # frontend — yunkes SPA built with Vite, served by nginx, reverse-proxying # /api to the backend service. # # Auth: the scheduler + backend read yunke creds from .env (the host's .env # is passed through as-is). Session-cookie mode (YK_USERID / YK_COMPANY / # YK_COOKIE) is preferred; pcLogin mode is wired but deferred (see README). # # Usage: # docker-compose --env-file .env up # build + start everything # docker-compose up backend # just backend (+ deps) # docker-compose logs -f scheduler # tail scheduler JSON logs # # The frontend's container exposes port 80; mapped to 5173 on the host so # the same URL (http://localhost:5173) works in dev and prod. services: # ----------------------------------------------------------------------- # # Data plane # ----------------------------------------------------------------------- # mysql: image: mysql:8.0 restart: unless-stopped environment: MYSQL_DATABASE: ${MYSQL_DATABASE:-yunke} MYSQL_USER: ${MYSQL_USER:-yunke} MYSQL_PASSWORD: ${MYSQL_PASSWORD:-yunke} MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root} # Default MySQL settings are fine for dev. The container ships its own # healthcheck (mysqladmin ping); we use it so dependents wait until the # server is actually accepting connections, not just listening on the port. healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 10 volumes: - mysql_data:/var/lib/mysql # 3306 不映射宿主机:容器内通过服务名 mysql:3306 互联;宿主机检查用 # docker compose exec mysql mysql -uyunke -p(2026-08-21 本机 3306 已被 # 本地 MySQL 占用,发布环境也无需对外暴露数据库) redis: image: redis:7-alpine restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 10 volumes: - redis_data:/data # ----------------------------------------------------------------------- # # Application plane — same image, two entrypoints # ----------------------------------------------------------------------- # backend: build: context: ./yunke-server dockerfile: Dockerfile restart: unless-stopped # uvicorn entrypoint (matches the Dockerfile's default CMD, restated so # the role of this service is self-documenting at a glance). command: - uvicorn - app.api.main:app - --host - 0.0.0.0 - --port - "8000" depends_on: mysql: condition: service_healthy redis: condition: service_healthy environment: # DB_URL points at the mysql *service* (not localhost) — the container # resolves it via docker-compose DNS. asyncmy is the async driver the # app expects; the creds match the mysql service block above. DB_URL: mysql+asyncmy://${MYSQL_USER:-yunke}:${MYSQL_PASSWORD:-yunke}@mysql:3306/${MYSQL_DATABASE:-yunke} REDIS_URL: redis://redis:6379/0 MEDIA_DIR: /data/media # Pass through yunke auth + viewing-API token verbatim from .env so the # same .env works in dev and docker. YUNKE_BASE_URL: ${YUNKE_BASE_URL:-https://crm2.yunkecn.com} YUNKE_USERNAME: ${YUNKE_USERNAME:-} YUNKE_PASSWORD: ${YUNKE_PASSWORD:-} YK_USERID: ${YK_USERID:-} YK_COMPANY: ${YK_COMPANY:-} YK_COOKIE: ${YK_COOKIE:-} API_TOKEN: ${API_TOKEN:-} ENCRYPTION_KEY: ${ENCRYPTION_KEY:-} volumes: # 媒体走 bind mount(不占 Docker 虚拟磁盘——本机实测 C 盘被 2.5GB # volume 撑爆导致引擎 500,2026-08-22);服务器上数据直观可见好备份 - ./yunke-server/media:/data/media ports: - "8000:8000" scheduler: build: context: ./yunke-server dockerfile: Dockerfile restart: unless-stopped # Scheduler entrypoint — same image, different command. Cadence is # configurable via env (FULL_SYNC_HOUR / MESSAGE_INTERVAL_HOURS) so # operators can tune without rebuilding. command: - python - -m - app.sync.cli - scheduler depends_on: mysql: condition: service_healthy redis: condition: service_healthy environment: DB_URL: mysql+asyncmy://${MYSQL_USER:-yunke}:${MYSQL_PASSWORD:-yunke}@mysql:3306/${MYSQL_DATABASE:-yunke} REDIS_URL: redis://redis:6379/0 MEDIA_DIR: /data/media YUNKE_BASE_URL: ${YUNKE_BASE_URL:-https://crm2.yunkecn.com} YUNKE_USERNAME: ${YUNKE_USERNAME:-} YUNKE_PASSWORD: ${YUNKE_PASSWORD:-} YK_USERID: ${YK_USERID:-} YK_COMPANY: ${YK_COMPANY:-} YK_COOKIE: ${YK_COOKIE:-} ENCRYPTION_KEY: ${ENCRYPTION_KEY:-} # Scheduler cadence (env-driven; see app.sync.cli._cmd_scheduler). FULL_SYNC_HOUR: ${FULL_SYNC_HOUR:-2} MESSAGE_INTERVAL_HOURS: ${MESSAGE_INTERVAL_HOURS:-4} volumes: - ./yunke-server/media:/data/media # ----------------------------------------------------------------------- # # Frontend — built + served by nginx # ----------------------------------------------------------------------- # frontend: build: context: ./yunkes dockerfile: Dockerfile restart: unless-stopped depends_on: - backend ports: # Map nginx's :80 to the host's :5173 so the same URL # (http://localhost:5173) works in dev (vite) and prod (nginx). - "5173:80" volumes: mysql_data: redis_data: