使用 Docker Compose 构建本地技术栈
在本地同时运行 PHP、数据库和缓存
使用 Docker Compose 构建本地技术栈 是 CoddyKit 上的免费 PHP Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 PHP Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 PHP Academy 课程共包含 4 节课。
使用 Compose 构建本地服务栈
真正的 PHP 应用从来不只是 PHP——它们还需要数据库、缓存,有时还需要队列和邮件捕获服务。Docker Compose 会在一个文件中将这些组件声明为服务,并将它们连接到共享网络,使服务可以通过名称相互访问。
本课将构建一个完整的本地服务栈:PHP-FPM + nginx + MySQL + Redis,并配置卷、健康检查和依赖顺序。
服务骨架
Compose 文件会在 services: 下列出各项服务。每项服务都可以从 Dockerfile build,也可以拉取一个 image。Compose 会创建一个默认网络,其中每项服务都可以通过自己的键访问——您的 PHP 应用应通过主机名 db 连接 MySQL,而不是使用 localhost。
services:
app:
build:
context: .
target: dev # multi-stage dev target
volumes:
- ./:/app # live code mount
depends_on:
db:
condition: service_healthy
db:
image: mysql:8.4
redis:
image: redis:7-alpine让 nginx 位于 FPM 前端
FPM 在 9000 端口上使用 FastCGI 通信,而不是使用 HTTP。nginx 负责终止 HTTP 连接,并将 .php 请求代理到 app:9000。nginx 配置通过 Compose 服务名称引用 PHP 服务。
# docker/nginx/default.conf
server {
listen 80;
root /app/public;
index index.php;
location / {
try_files $uri /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000; # service name from compose
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}连接 Web 服务
nginx 服务会挂载相同的代码(用于解析 SCRIPT_FILENAME)和配置文件,发布 80 端口,并依赖于应用服务。两者共享 /app,因此 FastCGI 两端的路径能够保持一致。
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
volumes:
- ./:/app:ro
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- app持久化卷
容器文件系统是临时的。命名卷可以在执行 docker compose down 后保留数据库文件。绑定挂载(./:/app)可以在开发期间实时同步主机代码。请对有状态数据使用命名卷,对源代码使用绑定挂载。
services:
db:
image: mysql:8.4
environment:
MYSQL_DATABASE: app
MYSQL_USER: app
MYSQL_PASSWORD: secret
MYSQL_ROOT_PASSWORD: rootsecret
volumes:
- dbdata:/var/lib/mysql
volumes:
dbdata: # survives `down`, removed only by `down -v`健康检查与依赖关系
单独使用 depends_on 时,只会等待容器启动,不会等待 MySQL 接受连接。请添加 healthcheck,并使用 condition: service_healthy,这样应用会一直等待到数据库真正准备就绪。
services:
db:
image: mysql:8.4
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-prootsecret"]
interval: 5s
timeout: 3s
retries: 10
app:
build: .
depends_on:
db:
condition: service_healthy # waits for healthcheck pass环境变量与 .env
Compose 会自动加载同级的 .env 以进行变量插值,您可以通过 environment 或 env_file 传入应用配置。请不要将密钥放入已提交的 Compose 文件中,而应改为引用变量。
services:
app:
build: .
env_file:
- .env
environment:
DATABASE_URL: "mysql://app:secret@db:3306/app"
REDIS_URL: "redis://redis:6379"
# .env (gitignored) provides ${...} substitutions
# MYSQL_PASSWORD=secret从 PHP 连接服务
在网络内部,主机名就是服务名称。您的 PHP 代码可以直接连接 db 和 redis。这个片段以配置层可能采用的方式解析 DSN,展示 Compose 服务名称如何流入连接字符串。
<?php
$dsn = 'mysql://app:secret@db:3306/app';
$p = parse_url($dsn);
printf("driver: %s\n", $p['scheme']);
printf("host: %s\n", $p['host']); // 'db' resolves via compose DNS
printf("port: %d\n", $p['port']);
printf("db: %s\n", ltrim($p['path'], '/'));
$pdoDsn = sprintf('mysql:host=%s;port=%d;dbname=%s', $p['host'], $p['port'], ltrim($p['path'], '/'));
echo $pdoDsn . PHP_EOL;
?>用于本地开发的 Xdebug
只在本地挂载 Xdebug 配置(通过 dev 构建目标)。请将 client_host 指向 host.docker.internal,这样调试器就能连接主机上的 IDE。绝不要将 Xdebug 部署到生产环境,因为它会严重降低性能。
; docker/php/xdebug.ini (mounted only in dev)
zend_extension=xdebug
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003运行一次性命令
docker compose run 会为迁移、测试或 Composer 启动一个临时容器;exec 则会在已经运行的服务内部执行命令。请对一次性任务使用 run --rm,以便清理用完即弃的容器。
# Run migrations against the running db
docker compose exec app php bin/console doctrine:migrations:migrate
# One-off: install deps without a long-lived container
docker compose run --rm app composer install
# Tail logs of just the web service
docker compose logs -f web覆盖文件
Compose 会自动合并 docker-compose.yml 与 docker-compose.override.yml。请将面向生产环境的默认配置保留在基础文件中,并将仅供开发使用的卷、端口和 Xdebug 放入覆盖文件中——这样 CI 使用基础配置,开发人员则可以直接获得额外功能。
# docker-compose.override.yml (auto-merged, dev only)
services:
app:
volumes:
- ./docker/php/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini:ro
environment:
APP_ENV: dev
# CI runs: docker compose -f docker-compose.yml up (no override)快速检查
在运行迁移之前,为什么单纯的 depends_on: [db] 不够?
回顾
您使用 Compose 组装了一个本地服务栈:nginx 后面的 PHP-FPM(通过 FastCGI 连接到 app:9000)、可通过服务名称访问的 MySQL 和 Redis、用于持久化的命名卷、控制 depends_on 的健康检查、由环境变量驱动的配置、仅供开发使用的覆盖配置中的 Xdebug,以及通过 run/exec 执行的一次性命令。
请记住:服务名称就是 DNS 主机名,覆盖文件可以将开发环境的额外配置排除在 CI 之外,而健康检查比简单的启动顺序更可靠。
常见问题解答
「使用 Docker Compose 构建本地技术栈」课时是免费的吗?
是的 — 「使用 Docker Compose 构建本地技术栈」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 PHP Academy 课程的其余内容,请升级到 CoddyKit PRO。 PHP Academy 课程共包含 4 节课。
「使用 Docker Compose 构建本地技术栈」这节课中我会学到什么?
在本地同时运行 PHP、数据库和缓存 你通过在浏览器中直接运行的动手代码来练习 PHP Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 PHP Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 PHP Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「使用 Docker Compose 构建本地技术栈」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 PHP Academy 课中编写并运行代码吗?
能。每节 PHP Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 将 PHP 应用容器化
- 多阶段构建与优化
- 使用 Docker Compose 构建本地技术栈
- 使用 GitHub Actions 实现 CI/CD