本文目录导读:

使用PHP项目实现容器化部署,核心是通过 Docker 将你的PHP应用(代码、运行环境、依赖、配置)打包成一个独立的、可移植的镜像,然后运行在容器中。
以下是完整的步骤指南,分为 开发环境 和 生产环境 两个常见场景。
核心准备:准备 Dockerfile
这是容器化的蓝图,根据你的项目类型(纯PHP脚本、Laravel/Symfony框架、或传统LAMP/LEMP),Dockerfile 略有不同。
基础Dockerfile(使用PHP官方镜像 + FPM + Composer)
对于大多数现代PHP框架,推荐使用 php:8.x-fpm 作为基础镜像,并配合 Nginx 容器。
在本地开发时,你通常需要 PHP + Web 服务器 + 数据库 + Redis 等服务。 关键点: 生产环境更注重安全性、多阶段构建、环境变量和健康检查。 只保留运行所需的文件,去掉构建时用的工具(如Composer、Git)。 当你的项目需要多实例、自动扩缩容、滚动更新时,可以转向 Kubernetes: 按照这个流程,你可以将一个 PHP 项目高效且可靠地在任何支持 Docker 的环境中运行。
Dockerfile
# 1. 使用官方PHP 8.2 FPM 镜像作为基础
FROM php:8.2-fpm-alpine AS base
# 2. 安装系统依赖和 PHP 扩展 (根据你的项目需求)
RUN apk add --no-cache \
git \
unzip \
libpng-dev \
libjpeg-turbo-dev \
freetype-dev \
oniguruma-dev \
libxml2-dev \
zip \
curl \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install \
pdo_mysql \
mbstring \
exif \
pcntl \
bcmath \
gd \
opcache
# 3. 安装 Composer (PHP包管理器)
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# 4. 设置工作目录
WORKDIR /var/www/html
# 5. 复制项目文件 (先复制 composer.json 等依赖文件,利用Docker缓存)
COPY composer.json composer.lock ./
# 6. 安装项目依赖 (生产环境不安装 dev 依赖)
RUN composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev
# 7. 复制项目源代码 (这时才复制源码,如果前面没变化会走缓存)
COPY . .
# 8. 设置文件权限 (重要: 确保 PHP-FPM 用户 `www-data` 可以写 storage, cache 等)
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache \
&& chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache
# 9. 暴露 PHP-FPM 的端口 (9000)
EXPOSE 9000
# 10. 启动命令
CMD ["php-fpm"]
开发环境的容器化(Docker Compose)
docker-compose.yml 可以一键搞定。docker-compose.yml (放在项目根目录):version: '3.8'
services:
# PHP 服务 (使用上面的 Dockerfile)
app:
build:
context: .
dockerfile: Dockerfile
container_name: php-app
volumes:
- .:/var/www/html # 挂载本地代码,方便热更新
networks:
- app-network
# Web 服务器 (Nginx)
nginx:
image: nginx:alpine
container_name: php-nginx
ports:
- "8080:80" # 本机 8080 端口映射到容器 80 端口
volumes:
- .:/var/www/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf # 自定义 Nginx 配置
depends_on:
- app
networks:
- app-network
# 数据库 (MySQL)
database:
image: mysql:8.0
container_name: php-mysql
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: my_database
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- "3307:3306"
volumes:
- db_data:/var/lib/mysql
networks:
- app-network
# 缓存 (Redis) - 可选
redis:
image: redis:alpine
container_name: php-redis
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
db_data:
.env 文件中 DB_HOST 和 REDIS_HOST 应填写服务名称:database 和 redis。docker-compose up -d 即可启动所有服务。http://localhost:8080 查看项目。
生产环境的容器化部署
多阶段构建 (减小镜像体积)
# --- 第一阶段:构建阶段 ---
FROM php:8.2-fpm-alpine AS builder
RUN apk add --no-cache git unzip
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
COPY composer.json composer.lock ./
RUN composer install --no-interaction --prefer-dist --no-dev
COPY . .
RUN php artisan optimize # 生产环境优化:路由、配置、视图缓存
# --- 第二阶段:运行阶段 (最终镜像) ---
FROM php:8.2-fpm-alpine
# 只复制最终需要的东西
COPY --from=builder /var/www/html /var/www/html
WORKDIR /var/www/html
USER www-data
EXPOSE 9000
CMD ["php-fpm"]
Docker Compose 生产配置 (
docker-compose.prod.yml)version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.prod # 使用多阶段构建的 Dockerfile
image: my-project/app:latest
environment:
- APP_ENV=production
- APP_DEBUG=false
- DB_HOST=database
- DB_DATABASE=my_db
- DB_USERNAME=user
- DB_PASSWORD=${DB_PASSWORD} # 使用 .env 文件或外部环境变量
# 不挂载卷,而是将镜像推送到仓库
# volumes:
# - .:/var/www/html <-- 生产环境中不要挂载代码!
networks:
- app-network
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./ssl:/etc/ssl # 证书挂载
depends_on:
- app
networks:
- app-network
database:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: my_db
MYSQL_USER: user
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- db_prod_data:/var/lib/mysql
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
db_prod_data:
Nginx 配置示例 (
nginx.conf)server {
listen 80;
server_name localhost; # 生产环境改为你的域名
root /var/www/html/public; # Laravel 入口
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000; # 指向 PHP-FPM 服务
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}
部署流程
.env 文件(docker-compose 会自动读取),或使用 Docker Swarm / Kubernetes 的 Secrets。docker build -t my-project/app:latest -f Dockerfile.prod .
docker tag my-project/app:latest your-registry.com/my-project/app:1.0.0
docker push your-registry.com/my-project/app:1.0.0
# 拉取最新镜像
docker pull your-registry.com/my-project/app:1.0.0
# 使用 docker-compose 启动(生产配置)
docker-compose -f docker-compose.prod.yml up -d
docker build 和 docker push。
常见问题与最佳实践
问题
解决方案
文件权限
确保容器内的
www-data 用户对 storage、bootstrap/cache 有读写权限,使用 chown 或 setfacl。
数据库连接
使用服务名称作为主机名(如
database),而不是 localhost 或 0.0.1。
配置缓存
生产环境需运行
php artisan config:cache 和 php artisan route:cache 以提高性能。
日志
将日志输出到
stdout 和 stderr,以便 Docker 自动收集(php-fpm 默认支持)。
健康检查
在 Dockerfile 中添加
HEALTHCHECK 指令,或使用 docker-compose 的 healthcheck。
进程管理
一个容器只运行一个进程(PHP-FPM),Web 服务器用单独的 Nginx 容器。
镜像大小
使用
Alpine 基础镜像(约100MB)替代 Debian(约300MB+)。
超越 Docker:Kubernetes
# 简单的 Deployment 示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-app
spec:
replicas: 3
selector:
matchLabels:
app: php-app
template:
metadata:
labels:
app: php-app
spec:
containers:
- name: php-app
image: my-project/app:1.0.0
ports:
- containerPort: 9000
env:
- name: APP_ENV
value: production
- name: DB_HOST
value: mysql-service
Dockerfile:定义 PHP 运行环境,安装扩展和依赖。docker-compose.yml:在本地将 PHP + Nginx + DB 等编排在一起,方便开发。.env 或 Kubernetes Secrets 管理敏感信息。