使用 Docker Compose 設定 Laravel 生產環境
本指南演示如何使用 Docker 和 Docker Compose 設定生產就緒的 Laravel 環境。此配置旨在實現精簡、可擴充套件和安全的 Laravel 應用程式部署。
注意要體驗一個可立即執行的配置,請下載 Laravel Docker 示例 倉庫。它包含了預先配置的開發和生產設定。
專案結構
my-laravel-app/
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
├── docker/
│ ├── common/
│ │ └── php-fpm/
│ │ └── Dockerfile
│ ├── development/
│ ├── production/
│ │ ├── php-fpm/
│ │ │ └── entrypoint.sh
│ │ └── nginx
│ │ ├── Dockerfile
│ │ └── nginx.conf
├── compose.dev.yaml
├── compose.prod.yaml
├── .dockerignore
├── .env
├── vendor/
├── ...
此佈局代表了一個典型的 Laravel 專案,Docker 配置統一儲存在 `docker` 目錄中。您會發現 **兩個** Compose 檔案——`compose.dev.yaml`(用於開發)和 `compose.prod.yaml`(用於生產)——以使您的環境保持獨立且易於管理。
為 PHP-FPM 建立 Dockerfile (生產環境)
對於生產環境,`php-fpm` Dockerfile 建立了一個最佳化映像,其中只包含應用程式所需的 PHP 擴充套件和庫。如 GitHub 示例 所示,一個包含多階段構建的 Dockerfile 可以保持開發和生產之間的一致性並減少重複。以下片段僅顯示與生產相關的階段。
# Stage 1: Build environment and Composer dependencies
FROM php:8.4-fpm AS builder
# Install system dependencies and PHP extensions for Laravel with MySQL/PostgreSQL support.
# Dependencies in this stage are only required for building the final image.
# Node.js and asset building are handled in the Nginx stage, not here.
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
unzip \
libpq-dev \
libonig-dev \
libssl-dev \
libxml2-dev \
libcurl4-openssl-dev \
libicu-dev \
libzip-dev \
&& docker-php-ext-install -j$(nproc) \
pdo_mysql \
pdo_pgsql \
pgsql \
opcache \
intl \
zip \
bcmath \
soap \
&& pecl install redis \
&& docker-php-ext-enable redis \
&& apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Set the working directory inside the container
WORKDIR /var/www
# Copy the entire Laravel application code into the container
# -----------------------------------------------------------
# In Laravel, `composer install` may trigger scripts
# needing access to application code.
# For example, the `post-autoload-dump` event might execute
# Artisan commands like `php artisan package:discover`. If the
# application code (including the `artisan` file) is not
# present, these commands will fail, leading to build errors.
#
# By copying the entire application code before running
# `composer install`, we ensure that all necessary files are
# available, allowing these scripts to run successfully.
# In other cases, it would be possible to copy composer files
# first, to leverage Docker's layer caching mechanism.
# -----------------------------------------------------------
COPY . /var/www
# Install Composer and dependencies
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& composer install --no-dev --optimize-autoloader --no-interaction --no-progress --prefer-dist
# Stage 2: Production environment
FROM php:8.4-fpm
# Install only runtime libraries needed in production
# libfcgi-bin and procps are required for the php-fpm-healthcheck script
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq-dev \
libicu-dev \
libzip-dev \
libfcgi-bin \
procps \
&& apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Download and install php-fpm health check script
RUN curl -o /usr/local/bin/php-fpm-healthcheck \
https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/master/php-fpm-healthcheck \
&& chmod +x /usr/local/bin/php-fpm-healthcheck
# Copy the initialization script
COPY ./docker/php-fpm/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
# Copy the initial storage structure
COPY ./storage /var/www/storage-init
# Copy PHP extensions and libraries from the builder stage
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
COPY --from=builder /usr/local/bin/docker-php-ext-* /usr/local/bin/
# Use the recommended production PHP configuration
# -----------------------------------------------------------
# PHP provides development and production configurations.
# Here, we replace the default php.ini with the production
# version to apply settings optimized for performance and
# security in a live environment.
# -----------------------------------------------------------
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# Enable PHP-FPM status page by modifying zz-docker.conf with sed
RUN sed -i '/\[www\]/a pm.status_path = /status' /usr/local/etc/php-fpm.d/zz-docker.conf
# Update the variables_order to include E (for ENV)
#RUN sed -i 's/variables_order = "GPCS"/variables_order = "EGPCS"/' "$PHP_INI_DIR/php.ini"
# Copy the application code and dependencies from the build stage
COPY --from=builder /var/www /var/www
# Set working directory
WORKDIR /var/www
# Ensure correct permissions
RUN chown -R www-data:www-data /var/www
# Switch to the non-privileged user to run the application
USER www-data
# Change the default command to run the entrypoint script
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]
為 PHP-CLI 建立 Dockerfile (生產環境)
對於生產環境,您通常需要一個單獨的容器來執行 Artisan 命令、遷移和其他 CLI 任務。在大多數情況下,您可以透過重用現有的 PHP-FPM 容器來執行這些命令。
$ docker compose -f compose.prod.yaml exec php-fpm php artisan route:list
如果您需要一個具有不同擴充套件或嚴格職責分離的單獨 CLI 容器,請考慮使用 php-cli Dockerfile。
# Stage 1: Build environment and Composer dependencies
FROM php:8.4-cli AS builder
# Install system dependencies and PHP extensions required for Laravel + MySQL/PostgreSQL support
# Some dependencies are required for PHP extensions only in the build stage
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
unzip \
libpq-dev \
libonig-dev \
libssl-dev \
libxml2-dev \
libcurl4-openssl-dev \
libicu-dev \
libzip-dev \
&& docker-php-ext-install -j$(nproc) \
pdo_mysql \
pdo_pgsql \
pgsql \
opcache \
intl \
zip \
bcmath \
soap \
&& pecl install redis \
&& docker-php-ext-enable redis \
&& apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Set the working directory inside the container
WORKDIR /var/www
# Copy the entire Laravel application code into the container
COPY . /var/www
# Install Composer and dependencies
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& composer install --no-dev --optimize-autoloader --no-interaction --no-progress --prefer-dist
# Stage 2: Production environment
FROM php:8.4-cli
# Install client libraries required for php extensions in runtime
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq-dev \
libicu-dev \
libzip-dev \
&& apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Copy PHP extensions and libraries from the builder stage
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
COPY --from=builder /usr/local/bin/docker-php-ext-* /usr/local/bin/
# Use the default production configuration for PHP runtime arguments
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# Copy the application code and dependencies from the build stage
COPY --from=builder /var/www /var/www
# Set working directory
WORKDIR /var/www
# Ensure correct permissions
RUN chown -R www-data:www-data /var/www
# Switch to the non-privileged user to run the application
USER www-data
# Default command: Provide a bash shell to allow running any command
CMD ["bash"]
此 Dockerfile 類似於 PHP-FPM Dockerfile,但它使用 `php:8.4-cli` 映像作為基礎映像,並設定容器以執行 CLI 命令。
為 Nginx 建立 Dockerfile (生產環境)
Nginx 作為 Laravel 應用程式的 Web 伺服器。您可以將靜態資源直接包含到容器中。以下是 Nginx Dockerfile 的一個示例:
# docker/nginx/Dockerfile
# Stage 1: Build assets
FROM debian AS builder
# Install Node.js and build tools
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
nodejs \
npm \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Set working directory
WORKDIR /var/www
# Copy Laravel application code
COPY . /var/www
# Install Node.js dependencies and build assets
RUN npm install && npm run build
# Stage 2: Nginx production image
FROM nginx:alpine
# Copy custom Nginx configuration
# -----------------------------------------------------------
# Replace the default Nginx configuration with our custom one
# that is optimized for serving a Laravel application.
# -----------------------------------------------------------
COPY ./docker/nginx/nginx.conf /etc/nginx/nginx.conf
# Copy Laravel's public assets from the builder stage
# -----------------------------------------------------------
# We only need the 'public' directory from our Laravel app.
# -----------------------------------------------------------
COPY --from=builder /var/www/public /var/www/public
# Set the working directory to the public folder
WORKDIR /var/www/public
# Expose port 80 and start Nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
此 Dockerfile 使用多階段構建將資產構建過程與最終的生產映像分離。第一階段安裝 Node.js 並構建資產,而第二階段設定 Nginx 生產映像,其中包含最佳化的配置和已構建的資產。
為生產環境建立 Docker Compose 配置
要將所有服務整合在一起,請建立一個 `compose.prod.yaml` 檔案,其中定義了生產環境的服務、卷和網路。以下是一個示例配置:
services:
web:
build:
context: .
dockerfile: ./docker/production/nginx/Dockerfile
restart: unless-stopped # Automatically restart unless the service is explicitly stopped
volumes:
# Mount the 'laravel-storage' volume to '/var/www/storage' inside the container.
# -----------------------------------------------------------
# This volume stores persistent data like uploaded files and cache.
# The ':ro' option mounts it as read-only in the 'web' service because Nginx only needs to read these files.
# The 'php-fpm' service mounts the same volume without ':ro' to allow write operations.
# -----------------------------------------------------------
- laravel-storage-production:/var/www/storage:ro
networks:
- laravel-production
ports:
# Map port 80 inside the container to the port specified by 'NGINX_PORT' on the host machine.
# -----------------------------------------------------------
# This allows external access to the Nginx web server running inside the container.
# For example, if 'NGINX_PORT' is set to '8080', accessing 'https://:8080' will reach the application.
# -----------------------------------------------------------
- "${NGINX_PORT:-80}:80"
depends_on:
php-fpm:
condition: service_healthy # Wait for php-fpm health check
php-fpm:
# For the php-fpm service, we will create a custom image to install the necessary PHP extensions and setup proper permissions.
build:
context: .
dockerfile: ./docker/common/php-fpm/Dockerfile
target: production # Use the 'production' stage in the Dockerfile
restart: unless-stopped
volumes:
- laravel-storage-production:/var/www/storage # Mount the storage volume
env_file:
- .env
networks:
- laravel-production
healthcheck:
test: ["CMD-SHELL", "php-fpm-healthcheck || exit 1"]
interval: 10s
timeout: 5s
retries: 3
# The 'depends_on' attribute with 'condition: service_healthy' ensures that
# this service will not start until the 'postgres' service passes its health check.
# This prevents the application from trying to connect to the database before it's ready.
depends_on:
postgres:
condition: service_healthy
# The 'php-cli' service provides a command-line interface for running Artisan commands and other CLI tasks.
# -----------------------------------------------------------
# This is useful for running migrations, seeders, or any custom scripts.
# It shares the same codebase and environment as the 'php-fpm' service.
# -----------------------------------------------------------
php-cli:
build:
context: .
dockerfile: ./docker/php-cli/Dockerfile
tty: true # Enables an interactive terminal
stdin_open: true # Keeps standard input open for 'docker exec'
env_file:
- .env
networks:
- laravel
postgres:
image: postgres:16
restart: unless-stopped
user: postgres
ports:
- "${POSTGRES_PORT}:5432"
environment:
- POSTGRES_DB=${POSTGRES_DATABASE}
- POSTGRES_USER=${POSTGRES_USERNAME}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- postgres-data-production:/var/lib/postgresql/data
networks:
- laravel-production
# Health check for PostgreSQL
# -----------------------------------------------------------
# Health checks allow Docker to determine if a service is operational.
# The 'pg_isready' command checks if PostgreSQL is ready to accept connections.
# This prevents dependent services from starting before the database is ready.
# -----------------------------------------------------------
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:alpine
restart: unless-stopped # Automatically restart unless the service is explicitly stopped
networks:
- laravel-production
# Health check for Redis
# -----------------------------------------------------------
# Checks if Redis is responding to the 'PING' command.
# This ensures that the service is not only running but also operational.
# -----------------------------------------------------------
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
networks:
# Attach the service to the 'laravel-production' network.
# -----------------------------------------------------------
# This custom network allows all services within it to communicate using their service names as hostnames.
# For example, 'php-fpm' can connect to 'postgres' by using 'postgres' as the hostname.
# -----------------------------------------------------------
laravel-production:
volumes:
postgres-data-production:
laravel-storage-production:
注意確保您的 Laravel 專案根目錄中有一個 `.env` 檔案,其中包含必要的配置(例如,資料庫和 Xdebug 設定),以匹配 Docker Compose 設定。
執行生產環境
要啟動生產環境,請執行:
$ docker compose -f compose.prod.yaml up --build -d
此命令將以分離模式構建並啟動所有服務,為您的 Laravel 應用程式提供可擴充套件且生產就緒的設定。
摘要
透過為 Laravel 生產環境設定 Docker Compose,您可以確保您的應用程式針對性能進行了最佳化,具有可擴充套件性,並且安全。此設定使部署保持一致且更易於管理,從而減少由於環境差異而導致錯誤的可能性。