Docker 容器化:PHP-FPM + Nginx + 队列
High Contrast
Dark Mode
Light Mode
Sepia
Forest
1 min read161 words

Docker 容器化:PHP-FPM + Nginx + 队列

Docker 让开发环境和生产环境保持一致——本地 docker compose up 得到和生产完全相同的运行环境。配合 GitHub Actions,每次推送自动构建镜像并部署。


项目目录结构

taskflow/
├── docker/
│   ├── php/
│   │   ├── Dockerfile          # PHP-FPM 镜像
│   │   └── php.ini             # PHP 配置
│   ├── nginx/
│   │   └── default.conf        # Nginx 配置
│   └── supervisor/
│       └── supervisord.conf    # 队列 Worker 配置
├── docker-compose.yml          # 开发环境
├── docker-compose.prod.yml     # 生产环境覆盖配置
└── ...

PHP-FPM Dockerfile

# docker/php/Dockerfile
FROM php:8.3-fpm-alpine AS base
# 安装系统依赖
RUN apk add --no-cache \
git \
curl \
libpng-dev \
libzip-dev \
oniguruma-dev \
icu-dev \
redis \
supervisor
# 安装 PHP 扩展
RUN docker-php-ext-install \
pdo_mysql \
mbstring \
exif \
pcntl \
bcmath \
gd \
zip \
intl \
opcache
# 安装 Redis 扩展
RUN pecl install redis && docker-php-ext-enable redis
# 安装 Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
# 复制 PHP 配置
COPY docker/php/php.ini /usr/local/etc/php/conf.d/app.ini
WORKDIR /var/www/html
# ---- 开发阶段 ----
FROM base AS development
# 安装 Xdebug(仅开发环境)
RUN pecl install xdebug && docker-php-ext-enable xdebug
COPY docker/php/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
# ---- 生产阶段 ----
FROM base AS production
# 复制应用代码
COPY --chown=www-data:www-data . .
# 安装依赖(不含 dev 依赖)
RUN composer install --no-dev --optimize-autoloader --no-interaction
# 设置文件权限
RUN chown -R www-data:www-data storage bootstrap/cache \
&& chmod -R 775 storage bootstrap/cache
# 以非 root 用户运行
USER www-data
EXPOSE 9000

PHP 配置

; docker/php/php.ini
[PHP]
upload_max_filesize = 20M
post_max_size = 21M
memory_limit = 256M
max_execution_time = 60
[opcache]
opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 8
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 0        ; 生产环境设 0(文件变更靠重启生效)
opcache.validate_timestamps = 0    ; 生产环境不检查文件时间戳
opcache.save_comments = 1          ; Laravel 注解需要

Nginx 配置

# docker/nginx/default.conf
server {
listen 80;
server_name _;
root /var/www/html/public;
index index.php;
client_max_body_size 20M;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000;      # app = PHP-FPM 容器名
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 60;
}
location ~ /\.(?!well-known).* {
deny all;
}
# 健康检查端点(K8s/ELB 用)
location /health {
access_log off;
return 200 "ok\n";
add_header Content-Type text/plain;
}
}

docker-compose.yml(开发环境)

# docker-compose.yml
services:
app:
build:
context: .
dockerfile: docker/php/Dockerfile
target: development          # 使用开发阶段(含 Xdebug)
volumes:
- .:/var/www/html            # 挂载代码,热更新
- /var/www/html/vendor       # vendor 不挂载(性能优化)
environment:
- APP_ENV=local
- XDEBUG_MODE=debug
depends_on:
- mysql
- redis
nginx:
image: nginx:1.25-alpine
ports:
- "8000:80"
volumes:
- .:/var/www/html
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
worker:
build:
context: .
dockerfile: docker/php/Dockerfile
target: development
command: php artisan queue:work redis --sleep=3 --tries=3 --verbose
volumes:
- .:/var/www/html
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: taskflow
MYSQL_USER: taskflow
MYSQL_PASSWORD: secret
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
# Mailpit:本地邮件捕获
mailpit:
image: axllent/mailpit
ports:
- "1025:1025"    # SMTP
- "8025:8025"    # Web UI
volumes:
mysql_data:
redis_data:

docker-compose.prod.yml(生产环境覆盖)

# docker-compose.prod.yml
# 使用:docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
services:
app:
build:
target: production           # 使用生产阶段(无 Xdebug,含代码)
restart: unless-stopped
environment:
- APP_ENV=production
- APP_DEBUG=false
# 生产环境不挂载代码(代码已打包进镜像)
volumes: []
nginx:
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
- /etc/letsencrypt:/etc/letsencrypt:ro  # SSL 证书
worker:
build:
target: production
restart: unless-stopped
volumes: []

GitHub Actions:自动构建与部署

# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- run: composer install --no-interaction
- run: cp .env.testing .env && php artisan key:generate
- run: php artisan test --parallel
build-push:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
file: docker/php/Dockerfile
target: production
push: true
tags: |
yourusername/taskflow:latest
yourusername/taskflow:${{ github.sha }}
cache-from: type=registry,ref=yourusername/taskflow:latest
cache-to: type=inline
deploy:
needs: build-push
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: forge
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /home/forge/taskflow.app
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps app worker
docker compose exec -T app php artisan migrate --force
docker compose exec -T app php artisan optimize
docker compose exec -T app php artisan queue:restart
docker image prune -f

常用 Docker 命令

# 开发环境启动
docker compose up -d
# 进入 PHP 容器
docker compose exec app bash
# 运行 Artisan 命令
docker compose exec app php artisan migrate
docker compose exec app php artisan tinker
# 运行测试
docker compose exec app php artisan test
# 查看队列 Worker 日志
docker compose logs -f worker
# 生产:拉取新镜像并重启(零停机)
docker compose pull app
docker compose up -d --no-deps --build app
# 清理旧镜像
docker image prune -f

下一节Telescope、Debugbar 与零停机发布——部署好之后,如何监控生产环境、排查 Bug、以及实现发布时不中断用户请求。