本篇介紹如何利用 Docker Compose 搭建支援多個 PHP 版本(5.6 / 7.2 / 7.4 / 8.1 / 8.2)的本地開發環境,並透過 Nginx 依站台切換 PHP 版本。
為什麼需要多 PHP 版本環境?
實務上,不同專案使用的 PHP 版本往往不同:
- 舊專案可能還在跑 PHP 5.6 / 7.2
- 新專案已經使用 PHP 8.1 / 8.2
- 同時維護多個專案時,需要在同一台機器上切換版本
透過 Docker Compose,可以讓每個 PHP 版本各自跑一個獨立的 FPM 容器,Nginx 再根據每個站台的設定,把請求導到對應的 PHP-FPM,完全不互相干擾。
目錄結構
docker-setup/
├── docker-compose.yml
├── nginx/
│ ├── Dockerfile
│ ├── nginx.conf
│ └── sites/
│ ├── blog.conf # 各站台 Nginx 設定
│ ├── php82.conf.example
│ ├── php74.conf.example
│ └── ...
├── php/
│ ├── 5.6/
│ │ ├── Dockerfile
│ │ └── conf.d/xdebug.ini
│ ├── 7.2/
│ ├── 7.4/
│ ├── 8.1/
│ └── 8.2/
├── mysql/
└── redis/
docker-compose.yml
核心設計:每個 PHP 版本獨立一個 service,共用同一個 network 讓 Nginx 可以直接呼叫各版本的 FPM。
networks:
frontend:
driver: bridge
backend:
driver: bridge
volumes:
mysql:
driver: local
redis:
driver: local
services:
nginx:
build:
context: ./nginx
container_name: nginx
volumes:
- /var/www:/var/www # 掛載所有專案程式碼
- ./nginx/sites:/etc/nginx/sites-available
- ./nginx/ssl:/etc/nginx/ssl
- ./logs/nginx:/var/log/nginx
ports:
- "80:80"
- "443:443"
depends_on:
- php82-fpm
- php81-fpm
- php74-fpm
- php72-fpm
- php56-fpm
restart: always
networks:
- backend
php82-fpm:
build:
context: ./php/8.2
dockerfile: Dockerfile
container_name: php82_fpm
volumes:
- /var/www:/var/www
- ./php/8.2/conf.d/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini
restart: always
networks:
- backend
php81-fpm:
build:
context: ./php/8.1
dockerfile: Dockerfile
container_name: php81_fpm
volumes:
- /var/www:/var/www
restart: always
networks:
- backend
php74-fpm:
build:
context: ./php/7.4
dockerfile: Dockerfile
container_name: php74_fpm
volumes:
- /var/www:/var/www
restart: always
networks:
- backend
php72-fpm:
build:
context: ./php/7.2
dockerfile: Dockerfile
container_name: php72_fpm
volumes:
- /var/www:/var/www
restart: always
networks:
- backend
php56-fpm:
build:
context: ./php/5.6
dockerfile: Dockerfile
container_name: php56_fpm
volumes:
- /var/www:/var/www
restart: always
networks:
- backend
mysql:
build:
context: ./mysql
container_name: mysql
environment:
- MYSQL_DATABASE=homestead
- MYSQL_USER=homestead
- MYSQL_PASSWORD=secret
- MYSQL_ROOT_PASSWORD=root
volumes:
- mysql:/var/lib/mysql
ports:
- "3306:3306"
restart: always
networks:
- backend
redis:
image: redis
container_name: redis
command: --requirepass secret
ports:
- "6379:6379"
restart: always
networks:
- backend
PHP Dockerfile 範例
每個版本有獨立的 Dockerfile,可以各自安裝需要的 extension:
# php/8.2/Dockerfile FROM php:8.2-fpm-alpine RUN apk add --no-cache \ git curl zip unzip \ libpng-dev libjpeg-turbo-dev \ oniguruma-dev libxml2-dev RUN docker-php-ext-install \ pdo_mysql mbstring exif pcntl bcmath gd # 安裝 Composer COPY --from=composer:latest /usr/bin/composer /usr/bin/composer # 安裝 Xdebug(選用) RUN pecl install xdebug && docker-php-ext-enable xdebug
# php/7.4/Dockerfile
FROM php:7.4-fpm-alpine
RUN apk add --no-cache \
git curl zip unzip \
libpng-dev libjpeg-turbo-dev \
oniguruma-dev libxml2-dev
RUN docker-php-ext-install \
pdo_mysql mbstring exif pcntl bcmath gd
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
Nginx 站台設定
重點在 fastcgi_pass,指向不同版本的 PHP-FPM container name:
# sites/blog.conf(使用 PHP 8.2) server { listen 80; server_name local-blog.test; root /var/www/blog/public; index index.php index.html; location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { try_files $uri /index.php =404; fastcgi_pass php82_fpm:9000; # 指向 PHP 8.2 容器 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_read_timeout 600; include fastcgi_params; } error_log /var/log/nginx/blog-error.log; access_log /var/log/nginx/blog-access.log; }
# sites/legacy-app.conf(使用 PHP 7.2) server { listen 80; server_name legacy-app.test; root /var/www/legacy/public; index index.php index.html; location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { try_files $uri /index.php =404; fastcgi_pass php72_fpm:9000; # 指向 PHP 7.2 容器 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_read_timeout 600; include fastcgi_params; } }
啟動環境
# 第一次啟動(build image) docker compose up -d --build # 查看所有容器狀態 docker compose ps # 只啟動特定服務 docker compose up -d nginx php82-fpm mysql redis # 停止所有服務 docker compose down # 重啟單一服務 docker compose restart php82-fpm
進入容器執行指令
# 進入 PHP 8.2 容器 docker exec -it php82_fpm sh # 在容器內執行 artisan 指令 docker exec php82_fpm php /var/www/blog/artisan migrate # 查看 PHP 版本確認 docker exec php82_fpm php -v docker exec php72_fpm php -v
各版本容器對應表
| PHP 版本 | Container Name | Nginx fastcgi_pass | 適用情境 |
|---|---|---|---|
| PHP 8.2 | php82_fpm |
php82_fpm:9000 |
Laravel 10/11 新專案 |
| PHP 8.1 | php81_fpm |
php81_fpm:9000 |
Laravel 9/10 |
| PHP 7.4 | php74_fpm |
php74_fpm:9000 |
Laravel 8 |
| PHP 7.2 | php72_fpm |
php72_fpm:9000 |
舊版 Laravel / WordPress |
| PHP 5.6 | php56_fpm |
php56_fpm:9000 |
超舊系統維護 |
小結
透過 Docker Compose,每個 PHP 版本只需要獨立一個 FPM service,Nginx 站台設定只要改一行 fastcgi_pass 就能切換版本,多個專案並行開發不再需要手動切換環境,大幅提升開發效率。
參考文獻: