From ddaa0ca5a9f50c0100d2b6b6c00aa7e097d22acd Mon Sep 17 00:00:00 2001 From: augushong Date: Mon, 1 Jun 2026 22:32:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(stack):=20=E6=96=B0=E5=A2=9E=20docker-dev-?= =?UTF-8?q?sync=20=E6=A8=A1=E5=BC=8F=EF=BC=8C=E4=BC=98=E5=8C=96=20Windows?= =?UTF-8?q?=20=E4=B8=8B=20Docker=20=E5=BC=80=E5=8F=91=20I/O=20=E6=80=A7?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 source/stack/docker-dev-sync/ 模式目录 - 宿主代码映射到 /var/www/source(bind mount 中转) - 容器内 rsync 定时同步到 /var/www/html(原生文件系统) - vendor 由 Docker build 管理,不参与同步 - rsync 使用 --no-perms 避免保留 Windows 源文件权限 - 排除 docker-dev/、runtime/、.git/ 等无关目录 - SYNC_INTERVAL 环境变量可配置轮询间隔(默认 3 秒) - 更新 stack.json 注册模式并声明 sync.sh 为托管文件 - 调整 CI 构建流程:先安装依赖再切换 stack 模式 --- .gitea/workflows/build-and-deploy.yml | 18 ++- source/stack/README.md | 1 + source/stack/docker-dev-sync/.docker-dev.env | 66 +++++++++ source/stack/docker-dev-sync/.dockerignore | 7 + source/stack/docker-dev-sync/Dockerfile | 86 ++++++++++++ .../stack/docker-dev-sync/docker-compose.yaml | 59 ++++++++ .../docker-dev-sync/source/docker/run.sh | 62 +++++++++ .../docker-dev-sync/source/docker/sync.sh | 129 ++++++++++++++++++ .../docker-dev-sync/source/docker/zzz-dev.ini | 9 ++ .../source/docker/zzz-xdebug.ini | 7 + source/stack/stack.json | 7 +- 11 files changed, 443 insertions(+), 8 deletions(-) create mode 100644 source/stack/docker-dev-sync/.docker-dev.env create mode 100644 source/stack/docker-dev-sync/.dockerignore create mode 100644 source/stack/docker-dev-sync/Dockerfile create mode 100644 source/stack/docker-dev-sync/docker-compose.yaml create mode 100644 source/stack/docker-dev-sync/source/docker/run.sh create mode 100644 source/stack/docker-dev-sync/source/docker/sync.sh create mode 100644 source/stack/docker-dev-sync/source/docker/zzz-dev.ini create mode 100644 source/stack/docker-dev-sync/source/docker/zzz-xdebug.ini diff --git a/.gitea/workflows/build-and-deploy.yml b/.gitea/workflows/build-and-deploy.yml index ae11379..5937bad 100644 --- a/.gitea/workflows/build-and-deploy.yml +++ b/.gitea/workflows/build-and-deploy.yml @@ -22,10 +22,6 @@ jobs: with: fetch-depth: 1 - - name: 切换到 docker-serve 模式 - shell: bash - run: php think admin:stack:mode use docker-serve -f - - name: 生成 .env shell: bash env: @@ -55,6 +51,17 @@ jobs: mv .env.tmp .env + - name: 安装依赖 + shell: bash + run: | + set -euo pipefail + composer config -g repos.packagist composer https://nexus.hl7.top:1243/repository/composer-proxy/ + composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader + + - name: 切换到 docker-serve 模式 + shell: bash + run: php think admin:stack:mode use docker-serve -f + - name: 打包发布文件 shell: bash env: @@ -62,9 +69,6 @@ jobs: run: | set -euo pipefail - composer config -g repos.packagist composer https://nexus.hl7.top:1243/repository/composer-proxy/ - composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader - TMP_PACKAGE="/tmp/${PACKAGE_NAME}" rm -f "$TMP_PACKAGE" "$PACKAGE_NAME" diff --git a/source/stack/README.md b/source/stack/README.md index 6337d62..6f3abc5 100644 --- a/source/stack/README.md +++ b/source/stack/README.md @@ -32,6 +32,7 @@ | `full` | 全量构建模式(兼容历史行为,从 PHP 镜像从头构建) | | `base-build` | 基础镜像 + 应用构建模式(author_only) | | `docker-dev` | Docker 开发模式(nginx+php-fpm+MySQL+Redis+phpMyAdmin+Xdebug) | +| `docker-dev-sync` | Docker 开发模式 - Windows I/O 优化(rsync 定时同步,避免 bind mount 慢速问题) | ## 基础镜像说明 diff --git a/source/stack/docker-dev-sync/.docker-dev.env b/source/stack/docker-dev-sync/.docker-dev.env new file mode 100644 index 0000000..47f30d9 --- /dev/null +++ b/source/stack/docker-dev-sync/.docker-dev.env @@ -0,0 +1,66 @@ +# Docker 开发模式配置(sync 优化版) - 使用方式: copy .docker-dev.env .env +APP_DEBUG=true + +[APP] +DEFAULT_TIMEZONE=Asia/Shanghai +AUTO_CACHE_LOG=false +AUTO_PARSE_API=true + +[DATABASE] +MAIN=main +TYPE=mysql +HOSTNAME=mysql +DATABASE=ulthon +USERNAME=root +PASSWORD=root +HOSTPORT=3306 +CHARSET=utf8mb4 +DEBUG=true +PREFIX=ul_ +FIELDS_CACHE=false + +[LOG] +CHANNEL=debug_mysql + +[LANG] +default_lang=zh-cn + +# 后台配置项组 +[ADMINSYSTEM] +# 后台地址后缀名称 +ADMIN=admin + +# 后台登录验证码开关 +CAPTCHA=false + +# 是否为演示环境 +IS_DEMO=false + +# CDN配置项组 +CDN= +EXAMPLE=true + +# 是否开启CSRF过滤 +IS_CSRF=false + +# 静态文件路径前缀 +STATIC_PATH=/static + +# OSS静态文件路径前缀 +OSS_STATIC_PREFIX=static_ulthon_admin + +# 没有节点控制的是否放行 +DEFAULT_AUTH_CHECK=false + +# 严格要求部分事件的返回数据,不符则抛出异常 +STRICT_EVENT=true + +# 严格要求每个页面都建立js文件 +STRICT_VIEW_JS=true + +MAKE_VIEW_WHILE_MISSING=false + +UPDATE_LEVEL=production + +# Sync polling interval (seconds) +SYNC_INTERVAL=3 diff --git a/source/stack/docker-dev-sync/.dockerignore b/source/stack/docker-dev-sync/.dockerignore new file mode 100644 index 0000000..1467d9e --- /dev/null +++ b/source/stack/docker-dev-sync/.dockerignore @@ -0,0 +1,7 @@ +.git +.sisyphus +node_modules +runtime +docker-dev +docker-dev-sync +*.sock diff --git a/source/stack/docker-dev-sync/Dockerfile b/source/stack/docker-dev-sync/Dockerfile new file mode 100644 index 0000000..cafdf20 --- /dev/null +++ b/source/stack/docker-dev-sync/Dockerfile @@ -0,0 +1,86 @@ +FROM php:8.2-fpm-bookworm + +RUN rm -rf /etc/apt/sources.list.d/* \ + && echo "deb http://mirrors.ustc.edu.cn/debian/ bookworm main contrib non-free non-free-firmware" > /etc/apt/sources.list \ + && echo "deb http://mirrors.ustc.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware" >> /etc/apt/sources.list \ + && echo "deb http://mirrors.ustc.edu.cn/debian-security bookworm-security main contrib non-free non-free-firmware" >> /etc/apt/sources.list + +RUN apt-get update + +# Install nginx and rsync (for sync mechanism) +RUN apt-get install -y nginx rsync + +ADD --chmod=0755 https://nexus.hl7.top:1243/repository/github-raw-proxy/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/ + +# Configure proxy +RUN sed -i 's|aomedia.googlesource.com|nexus.hl7.top:1243/repository/raw-aomedia.googlesource.com|g' /usr/local/bin/install-php-extensions +RUN sed -i 's|chromium.googlesource.com|nexus.hl7.top:1243/repository/raw-chromium.googlesource.com|g' /usr/local/bin/install-php-extensions +RUN sed -i 's|https://github.com|https://nexus.hl7.top:1243/repository/github-raw-proxy|g' /usr/local/bin/install-php-extensions + +RUN install-php-extensions pdo_mysql +RUN install-php-extensions gd +RUN install-php-extensions fileinfo +RUN install-php-extensions opcache +RUN install-php-extensions redis +RUN install-php-extensions event +RUN install-php-extensions imagick +RUN install-php-extensions zip +RUN install-php-extensions pcntl +RUN install-php-extensions xdebug + +# Clean default Nginx config +RUN rm /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default + + +# Install other dependencies +# RUN apt-get install -y ffmpeg +# RUN apt-get install -y libreoffice +# RUN apt-get install -y redis-server +# RUN apt-get install -y git + +# Set working directory +WORKDIR /var/www/html + +# Install Composer +COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer +RUN chmod +x /usr/local/bin/composer +RUN composer config -g repos.packagist composer https://nexus.hl7.top:1243/repository/composer-proxy/ + +# Set working directory +WORKDIR /var/www/html + +# Pre-copy composer files and install dependencies (with dev packages) +COPY composer.json composer.lock /var/www/html/ +RUN composer install --no-interaction --no-scripts + +# Copy all files to working directory +COPY . /var/www/html + +# Generate autoloader (without --no-dev optimizations) +RUN composer dump-autoload + +# Internal install composer and dependencies, comment out if not needed +# RUN install-php-extensions @composer + +# Dev mode PHP config (overrides zz-phprun.ini) +COPY source/docker/zzz-dev.ini /usr/local/etc/php/conf.d/zzz-dev.ini +COPY source/docker/zzz-xdebug.ini /usr/local/etc/php/conf.d/zzz-xdebug.ini + +# Create source mount point (host code staging area) +RUN mkdir -p /var/www/source + +VOLUME /var/www/html/runtime +VOLUME /var/www/html/public/storage +VOLUME /var/www/html/public/build +VOLUME /var/www/html/storage + +# Expose Nginx port +EXPOSE 8000 + +RUN chmod +x /var/www/html/source/docker/sync.sh +RUN chmod +x /var/www/html/source/docker/run.sh + +# Start Nginx and PHP-FPM +ENTRYPOINT ["/bin/bash", "/var/www/html/source/docker/run.sh"] + +CMD ["server"] diff --git a/source/stack/docker-dev-sync/docker-compose.yaml b/source/stack/docker-dev-sync/docker-compose.yaml new file mode 100644 index 0000000..931cbe7 --- /dev/null +++ b/source/stack/docker-dev-sync/docker-compose.yaml @@ -0,0 +1,59 @@ + +# 一般不需要配置name,默认是目录名,但是如果你的系统中有同名的目录,需要手动配置 +# name: ulthon_admin + +services: + ulthon_admin: + build: + context: . + dockerfile: Dockerfile + restart: unless-stopped + ports: + - "8000:8000" + volumes: + # Host code mounted to staging area (not the runtime directory) + # sync.sh will rsync from here to /var/www/html + - ./:/var/www/source + environment: + # Sync polling interval in seconds (default: 3) + - SYNC_INTERVAL=${SYNC_INTERVAL:-3} + extra_hosts: + - "host.docker.internal:host-gateway" + depends_on: + mysql: + condition: service_healthy + + mysql: + image: mysql:8.0 + restart: unless-stopped + ports: + - "13306:3306" + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: ulthon + volumes: + - ./docker-dev/mysql:/var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + restart: unless-stopped + ports: + - "16379:6379" + volumes: + - ./docker-dev/redis:/data + + phpmyadmin: + image: phpmyadmin:latest + restart: unless-stopped + ports: + - "18888:80" + environment: + PMA_HOST: mysql + depends_on: + mysql: + condition: service_healthy diff --git a/source/stack/docker-dev-sync/source/docker/run.sh b/source/stack/docker-dev-sync/source/docker/run.sh new file mode 100644 index 0000000..70ea356 --- /dev/null +++ b/source/stack/docker-dev-sync/source/docker/run.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# ============================================ +# run.sh - docker-dev-sync entrypoint +# ============================================ +# 1. Syncs host code from /var/www/source to /var/www/html +# 2. Configures nginx and php-fpm +# 3. Starts background sync polling +# 4. Starts application services +# ============================================ + +# ---- Step 1: Initial code sync (foreground, blocking) ---- +echo "[run] Checking source mount..." + +if [ -d "/var/www/source" ] && [ "$(ls -A /var/www/source 2>/dev/null)" ]; then + echo "[run] Source directory mounted, performing initial sync..." + /bin/bash /var/www/html/source/docker/sync.sh init + echo "[run] Initial sync finished." + + # Start background polling + /bin/bash /var/www/html/source/docker/sync.sh watch & + SYNC_PID=$! + echo "[run] Background sync polling started (PID: ${SYNC_PID}, interval: ${SYNC_INTERVAL:-3}s)." +else + echo "[run] WARNING: /var/www/source is not mounted or empty." + echo "[run] Running with build-time files only (no live sync)." +fi + +# ---- Step 2: Configure services ---- + +# Copy nginx config +cp /var/www/html/source/docker/nginx.conf /etc/nginx/sites-available/default +ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default + +# Copy php config files +cp /var/www/html/source/docker/zz-phprun.ini /usr/local/etc/php/conf.d +cp /var/www/html/source/docker/zz-phpfpm.conf /usr/local/etc/php-fpm.d + +# Dev mode: override production PHP config (Opcache, error display, Xdebug) +cp /var/www/html/source/docker/zzz-dev.ini /usr/local/etc/php/conf.d +cp /var/www/html/source/docker/zzz-xdebug.ini /usr/local/etc/php/conf.d + +# ---- Step 3: Set directory permissions ---- +mkdir -p /var/www/html/runtime && chmod -R 777 /var/www/html/runtime +mkdir -p /var/www/html/public/storage && chmod -R 777 /var/www/html/public/storage +mkdir -p /var/www/html/public/build && chmod -R 777 /var/www/html/public/build +mkdir -p /var/www/html/storage && chmod -R 777 /var/www/html/storage +mkdir -p /var/www/html/memoryspace && chown -R www-data:www-data /var/www/html/memoryspace + +# ---- Step 4: Output parameters and start services ---- +echo "参数为:$@" + +if [ "$1" = "server" ] || [ "$1" = "" ]; then + # Run timer (background) + su -www-data -c "nohup php /var/www/html/think timer --local --quit &" + # Run nginx + service nginx start + # Run php-fpm (foreground, keeps container alive) + php-fpm +else + php "/var/www/html/""$@" +fi diff --git a/source/stack/docker-dev-sync/source/docker/sync.sh b/source/stack/docker-dev-sync/source/docker/sync.sh new file mode 100644 index 0000000..7d08798 --- /dev/null +++ b/source/stack/docker-dev-sync/source/docker/sync.sh @@ -0,0 +1,129 @@ +#!/bin/bash + +# ============================================ +# sync.sh - Host-to-Container code sync script +# ============================================ +# Syncs code from /var/www/source (bind mount from host) +# to /var/www/html (container-native filesystem, fast I/O). +# +# Usage: +# sync.sh init - Perform initial full sync, then exit +# sync.sh watch - Run periodic incremental sync (blocking) +# sync.sh - Same as: init + watch +# +# Environment variables: +# SYNC_INTERVAL - Polling interval in seconds (default: 3) +# SYNC_EXTRAS - Additional rsync exclude patterns (space-separated) +# ============================================ + +SOURCE_DIR="/var/www/source/" +TARGET_DIR="/var/www/html/" +SYNC_INTERVAL=${SYNC_INTERVAL:-3} + +# Build rsync exclude patterns +build_exclude_args() { + EXCLUDE_ARGS=( + --exclude='.git' + --exclude='.gitignore' + --exclude='.sisyphus' + --exclude='.opencode' + --exclude='.agents' + --exclude='.docker-dev-sync' + --exclude='.docker-dev.env' + --exclude='.docker-dev' + --exclude='docker-dev' + --exclude='docker-dev-sync' + --exclude='node_modules' + --exclude='runtime' + --exclude='.idea' + --exclude='.vscode' + --exclude='*.sock' + --exclude='.env' + # vendor is managed by Docker build (composer install), do not overwrite + --exclude='vendor' + ) + + # Add extra excludes if provided + if [ -n "$SYNC_EXTRAS" ]; then + for pattern in $SYNC_EXTRAS; do + EXCLUDE_ARGS+=(--exclude="$pattern") + done + fi +} + +# Sync .env separately (excluded from rsync to avoid overwriting on rebuild) +sync_env() { + if [ -f "/var/www/source/.env" ]; then + cp /var/www/source/.env /var/www/html/.env 2>/dev/null + fi +} + +# rsync common flags +# --no-perms --no-owner --no-group: don't preserve source permissions (Windows mounts have bad perms) +RSYNC_FLAGS="-a --no-perms --no-owner --no-group" + +# ---- Initial full sync ---- +do_init() { + build_exclude_args + + echo "============================================" + echo "[sync] Starting initial full sync..." + echo "[sync] Source: ${SOURCE_DIR}" + echo "[sync] Target: ${TARGET_DIR}" + echo "============================================" + + rsync ${RSYNC_FLAGS} --delete "${EXCLUDE_ARGS[@]}" "$SOURCE_DIR" "$TARGET_DIR" + + if [ $? -eq 0 ]; then + echo "[sync] Initial sync complete." + else + echo "[sync] WARNING: Initial sync had errors (source may not be mounted yet)." + fi + + sync_env +} + +# ---- Periodic incremental sync (blocking) ---- +do_watch() { + build_exclude_args + + echo "[sync] Starting periodic sync (interval: ${SYNC_INTERVAL}s)..." + + while true; do + sleep "$SYNC_INTERVAL" + + # Check if source directory is mounted and accessible + if [ ! -d "/var/www/source" ]; then + continue + fi + + # Incremental sync (only changed files) + rsync ${RSYNC_FLAGS} --delete "${EXCLUDE_ARGS[@]}" "$SOURCE_DIR" "$TARGET_DIR" 2>/dev/null + + # Sync .env separately + sync_env + done +} + +# ---- Main ---- +MODE="${1:-all}" + +case "$MODE" in + init) + do_init + ;; + watch) + do_watch + ;; + all|"") + do_init + do_watch + ;; + *) + echo "Usage: $0 [init|watch]" + echo " init - Initial full sync, then exit" + echo " watch - Periodic incremental sync (blocking)" + echo " (no args) - init + watch" + exit 1 + ;; +esac diff --git a/source/stack/docker-dev-sync/source/docker/zzz-dev.ini b/source/stack/docker-dev-sync/source/docker/zzz-dev.ini new file mode 100644 index 0000000..23d6bf1 --- /dev/null +++ b/source/stack/docker-dev-sync/source/docker/zzz-dev.ini @@ -0,0 +1,9 @@ +; Dev mode PHP config (overrides zz-phprun.ini) +opcache.enable = 1 +opcache.memory_consumption = 128 +opcache.validate_timestamps = 1 +opcache.revalidate_freq = 3 +opcache.interned_strings_buffer = 16 +opcache.max_accelerated_files = 20000 +display_errors = On +error_reporting = E_ALL diff --git a/source/stack/docker-dev-sync/source/docker/zzz-xdebug.ini b/source/stack/docker-dev-sync/source/docker/zzz-xdebug.ini new file mode 100644 index 0000000..040328a --- /dev/null +++ b/source/stack/docker-dev-sync/source/docker/zzz-xdebug.ini @@ -0,0 +1,7 @@ +; Xdebug dev config +xdebug.mode = debug,develop +xdebug.start_with_request = yes +xdebug.client_host = host.docker.internal +xdebug.client_port = 9003 +xdebug.discover_client_host = true +xdebug.idekey = DOCKER_DEV diff --git a/source/stack/stack.json b/source/stack/stack.json index 2a7e6e4..62b75b8 100644 --- a/source/stack/stack.json +++ b/source/stack/stack.json @@ -8,7 +8,8 @@ ".dockerignore", "source/docker/zzz-dev.ini", "source/docker/zzz-xdebug.ini", - "source/docker/run.sh" + "source/docker/run.sh", + "source/docker/sync.sh" ], "modes": { "default": { @@ -30,6 +31,10 @@ "docker-dev": { "description": "Docker 开发模式(nginx+php-fpm+MySQL+Redis+phpMyAdmin+Xdebug)", "author_only": false + }, + "docker-dev-sync": { + "description": "Docker 开发模式 - Windows I/O 优化(rsync 同步,避免 bind mount 慢速问题)", + "author_only": false } } }