- 新增 docker-dev 部署模式,提供包含 nginx+php-fpm、MySQL、Redis、phpMyAdmin 和 Xdebug 的完整 Docker 开发环境 - 在 StackModeService 中重写 applyMode 方法,切换模式时自动删除目标模式中不存在的已管理文件 - 新增 .docker-dev.env 配置文件并纳入 managed_files 管理,切换模式时自动复制或删除
21 KiB
新增 docker-dev 部署模式
TL;DR
Quick Summary: 在
source/stack/下新增docker-dev模式,提供完整的 Docker 开发环境(nginx+php-fpm 并发处理、内置定时器、MySQL、Redis、phpMyAdmin、Xdebug 调试),替代php think run的单线程限制。Deliverables:
source/stack/docker-dev/Dockerfile— 基于 full 模式 + Xdebug + 开发优化 PHP 配置source/stack/docker-dev/docker-compose.yaml— 多服务编排(app、mysql、redis、phpmyadmin)source/stack/docker-dev/.docker-dev.env— 开发环境专用配置(HOSTNAME=mysql 等)source/stack/stack.json— 注册 docker-dev 模式 + managed_files 增加 .docker-dev.envapp/common/service/stack/StackModeService.php— 重写 applyMode,切换时自动删除多余文件.gitignore— 排除docker-dev/数据目录Estimated Effort: Medium Parallel Execution: YES - 3 waves Critical Path: Task 1-5 (parallel) → Task 6 → Task 7 → F1-F4
Context
Original Request
开发环境中 php think run 是单线程的,无法并发处理请求,也缺少定时器等完整运行支持。需要增加一个 Docker 开发模式,提供生产级运行环境用于本地开发调试。
Interview Summary
Key Discussions:
- 镜像基础:基于 full 模式(FROM php:8.2-fpm-bookworm),不依赖预构建基础镜像
- 服务组件:MySQL 8 + Redis + phpMyAdmin + Xdebug
- 环境配置:独立文件
.docker-dev.env,不替换.example.env;纳入 managed_files 管理 - 端口映射:
1+原端口方案(8800/13306/16379/18888),避免与本地服务冲突 - 数据持久化:MySQL/Redis 数据映射到项目根目录
./docker-dev/ - 多余文件清理:切换回 default 时,自动删除目标模式不存在的已管理文件(需重写 applyMode)
- 无需 CI/CD:dev 模式不提供
.gitea/workflows/文件,自动 fallback 到 default
Research Findings:
- Metis 确认:ThinkPHP
Env::load()中parse_ini_file()的结果直接覆盖$_ENV,.env文件值始终优先于 Docker 环境变量 docker/run.sh已内置 timer + nginx + php-fpm 启动逻辑,无需修改conf.d/按 ASCII 排序加载,dev 配置文件需命名为zzz-前缀以覆盖zz-phprun.ini- 当前
applyMode只做复制,不做删除;rollback有删除逻辑可参考
Metis Review
Identified Gaps (addressed):
- 环境变量优先级反转:不依赖 docker-compose environment 覆盖,改用独立
.docker-dev.env文件 - conf.d 文件排序:dev PHP 配置命名为
zzz-dev.ini确保最后加载 - Linux 兼容性:添加
extra_hosts: host.docker.internal:host-gateway - MySQL 启动依赖:使用
healthcheck+depends_on: condition: service_healthy - Xdebug 与 JIT 冲突:dev 配置中彻底关闭 opcache
Work Objectives
Core Objective
创建一个开箱即用的 Docker 开发环境模式,解决 php think run 的并发限制和定时器缺失问题,同时集成常用开发服务(MySQL、Redis、phpMyAdmin、Xdebug)。
Concrete Deliverables
source/stack/docker-dev/Dockerfilesource/stack/docker-dev/docker-compose.yamlsource/stack/docker-dev/.docker-dev.envsource/stack/stack.json(更新 modes + managed_files)app/common/service/stack/StackModeService.php(重写 applyMode 增加自动删除).gitignore(添加/docker-dev/)
Definition of Done
php think admin:stack:mode list输出包含 docker-devphp think admin:stack:mode use docker-dev成功切换,根目录出现.docker-dev.envcopy .docker-dev.env .env后docker compose build构建成功docker compose up -d所有 4 个服务运行正常- 应用可正常连接 MySQL(端口 13306)
- Xdebug 扩展已加载
- opcache 已关闭、display_errors 已开启
php think admin:stack:mode use default切回后,.docker-dev.env从根目录自动删除
Must Have
- Dockerfile 基于
php:8.2-fpm-bookworm,包含 full 模式的所有 PHP 扩展 + Xdebug - docker-compose 包含 app、mysql、redis、phpmyadmin 四个服务
- 端口使用
1+原端口方案:HTTP 8800、MySQL 13306、Redis 16379、phpMyAdmin 18888 - MySQL 带健康检查,app 服务等待 MySQL 就绪后再启动
- 开发优化的 PHP 配置(关闭 opcache、开启 display_errors)
- Xdebug 配置(client_host=host.docker.internal,trigger 模式)
- Linux 兼容性(extra_hosts)
- 数据持久化到
./docker-dev/ .docker-dev.env纳入 managed_files,.example.env不动- 切换模式时自动删除目标模式不存在的已管理文件
Must NOT Have (Guardrails)
- 不修改
docker/run.sh(复用现有启动逻辑) - 不修改
extend/base/下的任何文件(在 app 层重写 applyMode) - 不修改
.example.env - 不修改已有模式(default、full、base-build)的文件内容
- 不创建
source/stack/docker-dev/docker/子目录 - 不创建
.gitea/workflows/文件 - 不在 docker-compose
environment:中设置 ThinkPHP 数据库配置(无效) - 不使用
composer install --no-dev - 不设置
opcache.jit相关配置(与 Xdebug 冲突)
Verification Strategy
ZERO HUMAN INTERVENTION - ALL verification is agent-executed.
Test Decision
- Automated tests: None(基础设施配置 + 小范围代码修改)
- Framework: N/A
QA Policy
Every task includes agent-executed QA scenarios.
Evidence saved to .sisyphus/evidence/task-{N}-{scenario-slug}.{ext}.
Execution Strategy
Parallel Execution Waves
Wave 1 (Start Immediately - 所有文件创建可并行):
+-- Task 1: 创建 source/stack/docker-dev/Dockerfile [unspecified-high]
+-- Task 2: 创建 source/stack/docker-dev/docker-compose.yaml [unspecified-high]
+-- Task 3: 创建 source/stack/docker-dev/.docker-dev.env [quick]
+-- Task 4: 更新 source/stack/stack.json [quick]
+-- Task 5: 更新 .gitignore [quick]
Wave 2 (After Task 4 - 代码修改):
+-- Task 6: 重写 app 层 StackModeService (depends: 4) [deep]
Wave 3 (After ALL - 集成验证):
+-- Task 7: 模式切换与 Docker 构建集成验证 (depends: 1-6) [deep]
Wave FINAL:
+-- F1-F4: 并行审查
Critical Path: Task 4 → Task 6 → Task 7 → F1-F4
Max Concurrent: 5 (Wave 1)
Dependency Matrix
| Task | Depends On | Blocks | Wave |
|---|---|---|---|
| 1 | - | 7 | 1 |
| 2 | - | 7 | 1 |
| 3 | - | 7 | 1 |
| 4 | - | 6, 7 | 1 |
| 5 | - | 7 | 1 |
| 6 | 4 | 7 | 2 |
| 7 | 1-6 | F1-F4 | 3 |
Agent Dispatch Summary
- Wave 1: 5 agents - T1
unspecified-high, T2unspecified-high, T3-T5quick - Wave 2: 1 agent - T6
deep - Wave 3: 1 agent - T7
deep - FINAL: 4 agents - F1
oracle, F2-F3unspecified-high, F4deep
TODOs
-
1. 创建
source/stack/docker-dev/DockerfileWhat to do:
- 基于
source/stack/full/Dockerfile创建,保留全部内容 - PHP 扩展安装末尾追加
install-php-extensions xdebug composer install去掉--no-dev和--no-autoloadercomposer dump-autoload去掉所有优化参数- 用
RUN写入/usr/local/etc/php/conf.d/zzz-dev.ini:opcache.enable = 0 opcache.jit_buffer_size = 0 display_errors = On error_reporting = E_ALL - 用
RUN写入/usr/local/etc/php/conf.d/zzz-xdebug.ini:xdebug.mode = debug,develop xdebug.start_with_request = trigger xdebug.client_host = host.docker.internal xdebug.client_port = 9003 xdebug.discover_client_host = true xdebug.idekey = DOCKER_DEV
Must NOT do: 不创建 docker/ 子目录、不修改 run.sh、不用 --no-dev
Recommended Agent Profile:
unspecified-high, Skills:[]Parallelization: Wave 1, Blocks: 7, Blocked By: None
References:
source/stack/full/Dockerfile— 基础模板docker/zz-phprun.ini— 需要覆盖的配置项docker/run.sh— 第 8-9 行复制 zz-phprun.ini 到 conf.d
QA Scenarios:
Scenario: 包含 Xdebug 和开发配置 Tool: Bash Steps: 1. findstr "xdebug" source/stack/docker-dev/Dockerfile → 含 install-php-extensions xdebug 2. findstr "zzz-dev.ini" source/stack/docker-dev/Dockerfile → 存在 3. findstr "zzz-xdebug.ini" source/stack/docker-dev/Dockerfile → 存在 4. findstr "no-dev" source/stack/docker-dev/Dockerfile → 无输出 Evidence: .sisyphus/evidence/task-1-check.txtCommit: YES -
feat(stack): 新增 docker-dev 开发模式 - 基于
-
2. 创建
source/stack/docker-dev/docker-compose.yamlWhat to do:
- 4 个服务:
app (
ulthon_admin):buildfrom Dockerfile,restart: unless-stoppedports: "8800:80",volumes: ./:/var/www/htmlextra_hosts: host.docker.internal:host-gatewaydepends_on: mysql: condition: service_healthy
mysql:
image: mysql:8.0,ports: "13306:3306"environment: MYSQL_ROOT_PASSWORD=root, MYSQL_DATABASE=ulthonvolumes: ./docker-dev/mysql:/var/lib/mysqlhealthcheck: mysqladmin ping, interval 10s, retries 5
redis:
image: redis:7-alpine,ports: "16379:6379"volumes: ./docker-dev/redis:/data
phpmyadmin:
-
image: phpmyadmin:latest,ports: "18888:80" -
environment: PMA_HOST: mysql -
depends_on: mysql: condition: service_healthy -
name: ulthon_admin,格式与 default/docker-compose.yaml 一致
Must NOT do: 不在 app environment 设 DATABASE_*、不用 restart: always
Recommended Agent Profile:
unspecified-high, Skills:[]Parallelization: Wave 1, Blocks: 7, Blocked By: None
References:
source/stack/default/docker-compose.yaml— 格式参考
QA Scenarios:
Scenario: 语法和内容验证 Tool: Bash Steps: 1. docker compose -f source/stack/docker-dev/docker-compose.yaml config → 无错误 2. findstr "8800" source/stack/docker-dev/docker-compose.yaml → 存在 3. findstr "13306" source/stack/docker-dev/docker-compose.yaml → 存在 4. findstr "16379" source/stack/docker-dev/docker-compose.yaml → 存在 5. findstr "18888" source/stack/docker-dev/docker-compose.yaml → 存在 6. findstr "mysqladmin" source/stack/docker-dev/docker-compose.yaml → 存在 7. findstr "host.docker.internal" source/stack/docker-dev/docker-compose.yaml → 存在 Evidence: .sisyphus/evidence/task-2-check.txtCommit: YES -
feat(stack): 新增 docker-dev 开发模式
-
3. 创建
source/stack/docker-dev/.docker-dev.envWhat to do:
- 基于根目录
.example.env创建 - 顶部注释:
# Docker 开发模式配置 - 使用方式: copy .docker-dev.env .env - 修改:
HOSTNAME=mysql,DATABASE=ulthon,USERNAME=root,PASSWORD=root,HOSTPORT=13306 - 其余不变
Must NOT do: 不修改
.example.envRecommended Agent Profile:
quick, Skills:[]Parallelization: Wave 1, Blocks: 7, Blocked By: None
References:
.example.envQA Scenarios:
Scenario: 配置项验证 Tool: Bash Steps: 1. findstr "HOSTNAME" source/stack/docker-dev/.docker-dev.env → HOSTNAME=mysql 2. findstr "HOSTPORT" source/stack/docker-dev/.docker-dev.env → HOSTPORT=13306 3. findstr "USERNAME" source/stack/docker-dev/.docker-dev.env → USERNAME=root 4. findstr "PASSWORD" source/stack/docker-dev/.docker-dev.env → PASSWORD=root 5. findstr "DATABASE" source/stack/docker-dev/.docker-dev.env → DATABASE=ulthon Evidence: .sisyphus/evidence/task-3-check.txtCommit: YES -
feat(stack): 新增 docker-dev 开发模式 - 基于根目录
-
4. 更新
source/stack/stack.jsonWhat to do:
managed_files末尾追加".docker-dev.env"modes添加:"docker-dev": { "description": "Docker 开发模式(nginx+php-fpm+MySQL+Redis+phpMyAdmin+Xdebug)", "author_only": false }
Must NOT do: 不改 default_mode、不改已有模式
Recommended Agent Profile:
quick, Skills:[]Parallelization: Wave 1, Blocks: 6 & 7, Blocked By: None
References:
source/stack/stack.jsonQA Scenarios:
Scenario: JSON 和注册验证 Tool: Bash Steps: 1. php -r "json_decode(file_get_contents('source/stack/stack.json')); echo json_last_error()===JSON_ERROR_NONE?'VALID':'INVALID';" → VALID 2. php -r "$j=json_decode(file_get_contents('source/stack/stack.json'),true); echo count($j['managed_files']);" → 4 3. php think admin:stack:mode list → 包含 docker-dev Evidence: .sisyphus/evidence/task-4-check.txtCommit: YES -
feat(stack): 新增 docker-dev 开发模式
-
5. 更新
.gitignoreWhat to do: 添加
/docker-dev/Recommended Agent Profile:
quick, Skills:[]Parallelization: Wave 1, Blocks: 7, Blocked By: None
References:
.gitignoreQA Scenarios:
Scenario: 排除规则 Tool: Bash Steps: 1. findstr "docker-dev" .gitignore → 输出 /docker-dev/ Evidence: .sisyphus/evidence/task-5-check.txtCommit: YES -
feat(stack): 新增 docker-dev 开发模式
-
6. 重写
app/common/service/stack/StackModeService.php,增加自动删除What to do:
- 在
app/common/service/stack/StackModeService.php中重写applyMode方法 - 调用
parent::applyMode()执行原有的复制逻辑 - 然后遍历
managed_files,对于目标模式和 default 都不存在的已管理文件,从根目录删除 - 输出日志提示用户哪些文件被清理
实现逻辑(伪代码):
public function applyMode(string $mode, string $operator = ''): array { $result = parent::applyMode($mode, $operator); $stackConfig = json_decode(file_get_contents(root_path() . 'source/stack/stack.json'), true); $managedFiles = $stackConfig['managed_files'] ?? []; foreach ($managedFiles as $file) { $targetPath = root_path() . $file; if (!file_exists($targetPath)) { continue; } $modeFile = root_path() . "source/stack/{$mode}/{$file}"; $defaultFile = root_path() . "source/stack/default/{$file}"; if (!file_exists($modeFile) && !file_exists($defaultFile)) { @unlink($targetPath); // 记录到 result 或日志 } } return $result; }Must NOT do:
- 不修改
extend/base/common/service/stack/StackModeServiceBase.php - 不删除非 managed_files 中的文件
Recommended Agent Profile:
deep, Skills: [ulthon-base-app-architecture]Parallelization: Wave 2, Blocks: 7, Blocked By: Task 4
References:
extend/base/common/service/stack/StackModeServiceBase.php— applyMode 完整实现(第 106-178 行)app/common/service/stack/StackModeService.php— 当前空类,在这里重写source/stack/stack.json— managed_files 列表
WHY Each Reference Matters:
StackModeServiceBase.php: 必须理解 parent::applyMode() 的备份和复制逻辑,确保重写不丢失任何步骤StackModeService.php: 要修改的文件stack.json: managed_files 是清理逻辑的判断依据
QA Scenarios:
Scenario: 切换到 docker-dev 后 .docker-dev.env 出现 Tool: Bash Steps: 1. php think admin:stack:mode use docker-dev -f 2. dir .docker-dev.env → 存在 Evidence: .sisyphus/evidence/task-6-appear.txt Scenario: 切回 default 后 .docker-dev.env 被自动删除 Tool: Bash Steps: 1. php think admin:stack:mode use default -f 2. dir .docker-dev.env 2>nul → 不存在 Evidence: .sisyphus/evidence/task-6-disappear.txt Scenario: rollback 恢复 Tool: Bash Steps: 1. php think admin:stack:mode use docker-dev -f 2. php think admin:stack:mode use default -f(删除 .docker-dev.env) 3. php think admin:stack:mode rollback <backup_id> 4. dir .docker-dev.env → 恢复存在 Evidence: .sisyphus/evidence/task-6-rollback.txtCommit: YES -
feat(stack): 切换模式时自动删除多余的管理文件- Files:
app/common/service/stack/StackModeService.php
- 在
-
7. 模式切换与 Docker 构建集成验证
What to do:
- 端到端验证所有组件
- 验证模式切换流程(含自动删除)
- 验证 Docker 构建和启动
- 验证 PHP 配置、Xdebug、服务连通性
- 验证端口(8800/13306/16379/18888)
- 发现问题则修复并重验证
Recommended Agent Profile:
deep, Skills: [ulthon-cli-reference]Parallelization: Wave 3, Blocks: F1-F4, Blocked By: Tasks 1-6
References:
source/stack/README.md— 模式切换 fallback 规则docker/run.sh— 容器启动逻辑
QA Scenarios:
Scenario: 完整切换流程 Tool: Bash Steps: 1. copy Dockerfile Dockerfile.bak && copy docker-compose.yaml docker-compose.yaml.bak 2. php think admin:stack:mode use docker-dev -f 3. fc Dockerfile source\stack\docker-dev\Dockerfile → 一致 4. fc docker-compose.yaml source\stack\docker-dev\docker-compose.yaml → 一致 5. dir .docker-dev.env → 存在 Evidence: .sisyphus/evidence/task-7-switch.txt Scenario: Docker 构建和启动 Tool: Bash Steps: 1. copy .docker-dev.env .env 2. docker compose build --no-cache → 成功 3. docker compose up -d 4. timeout /t 30 /nobreak >nul 5. docker compose ps → 4 个 running/healthy Evidence: .sisyphus/evidence/task-7-build.txt Scenario: PHP 配置和 Xdebug Tool: Bash Steps: 1. docker compose exec ulthon_admin php -r "echo ini_get('opcache.enable');" → 0 2. docker compose exec ulthon_admin php -r "echo ini_get('display_errors');" → 1 3. docker compose exec ulthon_admin php -m | findstr xdebug → xdebug Evidence: .sisyphus/evidence/task-7-php.txt Scenario: 连通性和端口 Tool: Bash Steps: 1. docker compose exec ulthon_admin php -r "$c=@fsockopen('mysql',3306); echo $c?'OK':'FAIL';" → OK 2. docker compose exec ulthon_admin php -r "$c=@fsockopen('redis',6379); echo $c?'OK':'FAIL';" → OK 3. curl -s -o nul -w "%%{http_code}" http://localhost:8800/admin → 200/302 4. curl -s -o nul -w "%%{http_code}" http://localhost:18888/ → 200 Evidence: .sisyphus/evidence/task-7-connect.txt Scenario: 数据目录 Tool: Bash Steps: 1. dir docker-dev\mysql → 存在 2. dir docker-dev\redis → 存在 Evidence: .sisyphus/evidence/task-7-data.txt Scenario: 切回 default + 清理 Tool: Bash Steps: 1. docker compose down 2. php think admin:stack:mode use default -f 3. dir .docker-dev.env 2>nul → 不存在 4. copy Dockerfile.bak Dockerfile && copy docker-compose.yaml.bak docker-compose.yaml 5. del *.bak .env 6. git status → 无残留 Evidence: .sisyphus/evidence/task-7-cleanup.txtCommit: NO (验证任务)
Final Verification Wave (MANDATORY)
-
F1. Plan Compliance Audit —
oracle— APPROVE Must Have [10/10] | Must NOT Have [9/9] -
F2. Code Quality Review —
unspecified-high— APPROVE (after P0/P1 fixes) HOSTPORT 3306 fix applied, backup_id protection added -
F3. Real Manual QA —
unspecified-high— APPROVE (atlas manual verification) Scenarios [6/6 pass]: mode list, switch docker-dev, file verification, switch default auto-delete, rollback restore, cleanup -
F4. Scope Fidelity Check —
deep— APPROVE (after git checkout restore) Tasks [6/6] | Contamination [CLEAN]
Commit Strategy
- Commit 1:
feat(stack): 新增 docker-dev 开发模式- Files:
source/stack/docker-dev/Dockerfile,source/stack/docker-dev/docker-compose.yaml,source/stack/docker-dev/.docker-dev.env,source/stack/stack.json,.gitignore
- Files:
- Commit 2:
feat(stack): 切换模式时自动删除多余的管理文件- Files:
app/common/service/stack/StackModeService.php
- Files:
Success Criteria
Verification Commands
php think admin:stack:mode list # 包含 docker-dev
php think admin:stack:mode use docker-dev -f # 切换成功
dir .docker-dev.env # 文件存在
copy .docker-dev.env .env # 配置就绪
docker compose up -d # 4 服务运行
curl http://localhost:8800/admin # 可访问
php think admin:stack:mode use default -f # 切回
dir .docker-dev.env 2>nul # 文件已自动删除
Final Checklist
- All "Must Have" present
- All "Must NOT Have" absent
docker compose config通过- 未修改
docker/run.sh - 未修改
extend/base/ .example.env未被修改- 切回 default 时
.docker-dev.env自动删除