本文档详细记录了如何将 “土豆食堂” (Lynx) 这个 Laravel 12 项目通过 Docker 进行容器化打包、部署、运行及日常维护。
个人项目,已开源:https://github.com/NightingaleWK/lynx
一、环境准备
在开始之前,请确保您的部署目标机器(例如 Windows 11)已安装并运行 Docker Desktop。
二、项目改造(首次部署需执行)
以下文件需要在您本地的项目代码中创建。
1. 目录结构
为了清晰地区分生产环境与 Sail 开发环境的配置,我们将所有生产部署相关的文件都存放在一个新的 docker-prod 目录中。
在项目根目录下,创建如下的目录和文件结构:
1 2 3 4 5 6 7 8 9
| your-laravel-project/ ├── docker-prod/ # <-- 新建,存放所有生产环境配置 │ ├── app/ │ │ └── Dockerfile │ └── nginx/ │ └── default.conf ├── docker/# <-- 这是 Sail 的开发环境目录 (保留不变) ├── docker-compose.prod.yml# <-- 新建 (注意文件名) └── .dockerignore# <-- 新建
|
2. 创建核心配置文件
请根据后续提供的文件内容,在您的项目中创建或替换以下文件。
- docker-compose.prod.yml (服务编排文件)
- docker-prod/app/Dockerfile (PHP 应用镜像构建文件)
- docker-prod/nginx/default.conf (Nginx 配置文件)
- .dockerignore (Docker 忽略文件)
3. 修改 Laravel 配置 (.env)
- 复制 .env.example 为 .env 文件。
- 关键修改:确保数据库和 Redis 的 HOST 指向 Docker Compose 中定义的服务名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| APP_ENV=production APP_DEBUG=false APP_KEY= # 留空,后续会通过命令生成
DB_CONNECTION=mysql DB_HOST=mysql # <-- 必须是 mysql DB_PORT=3306 DB_DATABASE=lynx# 您的数据库名 DB_USERNAME=sail# 您的用户名 DB_PASSWORD=password# 您的密码
CACHE_DRIVER=redis QUEUE_CONNECTION=redis SESSION_DRIVER=redis
REDIS_HOST=redis# <-- 必须是 redis REDIS_PASSWORD=null REDIS_PORT=6379
|
4. 生成 SSL 证书 (局域网 HTTPS)
- 在项目根目录下创建目录 docker-prod/certs。
- 打开终端 (如 Git Bash 或 WSL),进入项目根目录,执行以下命令:
1
| openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout docker-prod/ certs/lynx-key.pem -out docker-prod/certs/lynx.pem
|
- 重要提示:在执行过程中,当提示输入 Common Name 时,请输入您部署机器的局域网 IP 地址(例如 192.168.1.100)。
- 执行后,请检查 docker-prod/certs 目录下是否已生成 lynx.pem 和 lynx-key.pem 两个文件。
三、部署与初始化流程
步骤 1:构建并启动所有容器
在项目根目录下,打开终端,执行以下命令。此命令会根据 Dockerfile 构建应用镜像,并以后台模式启动所有服务。
1 2 3
|
docker-compose -f docker-compose.prod.yml up -d --build --no-cache
|
步骤 2:初始化 Laravel 应用
容器首次启动后,数据库是空的,应用也没有配置密钥。我们需要进入 app 容器执行一系列初始化命令。
- 生成应用密钥 (APP_KEY):
1
| docker-compose -f docker-compose.prod.yml exec app php artisan key:generate
|
- 运行数据库迁移:
此命令会根据 database/migrations 目录下的文件,在数据库中创建所有需要的数据表(包括 cache 表等)。\
1
| docker-compose -f docker-compose.prod.yml exec app php artisan migrate
|
- 修正 storage 目录权限:
这是解决 “Permission denied” 错误的关键步骤。此命令将容器内的 storage 和 bootstrap/cache 目录的所有权交给 php-fpm 的运行用户 www-data。
1
| docker-compose -f docker-compose.prod.yml exec app chown -R www-data:www-data storage bootstrap/cache
|
- (可选)填充测试数据:
如果您需要在测试时填充数据,可以运行此命令。请注意,生产环境不应执行此操作。
1 2
| docker-compose -f docker-compose.prod.yml exec app php artisan db:seed --force
|
注:若需填充数据,请确保 Dockerfile 中 composer install 命令没有使用 –no-dev 参数,以保证 Faker 等开发包被安装。
步骤 3:访问您的应用
完成以上步骤后,您的应用已经成功部署并运行。
- 打开浏览器,访问 https://<您的部署机器 IP>。
- 浏览器会提示证书不安全,请选择“高级” -> “继续前往”。
- 您应该能看到 Laravel 的欢迎页面或您的应用首页。
四、日常运维命令
启动/停止服务
1
| docker-compose -f docker-compose.prod.yml up -d
|
1
| docker-compose -f docker-compose.prod.yml down
|
此操作不会删除数据库和 Redis 的持久化数据。
查看状态与日志
1
| docker-compose -f docker-compose.prod.yml ps
|
1
| docker-compose -f docker-compose.prod.yml logs -f
|
- 只看特定服务的日志 (例如 app 或 queue-worker):
1 2
| docker-compose -f docker-compose.prod.yml logs -f app docker-compose -f docker-compose.prod.yml logs -f queue-worker
|
代码更新流程
当您通过 git pull 更新了项目代码后:
- 重新构建镜像并重启服务:
1
| docker-compose -f docker-compose.prod.yml up -d --build
|
- (如果需要)运行新的数据库迁移:
1
| docker-compose -f docker-compose.prod.yml exec app php artisan migrate --force
|
- (推荐)清理并重建配置缓存:
1
| docker-compose -f docker-compose.prod.yml exec app php artisan optimize:clear
|
执行 Artisan 命令
您可以在 app 容器内执行任何 Artisan 命令,语法如下:
1
| docker-compose -f docker-compose.prod.yml exec app php artisan <您的命令>
|
五、完整配置文件代码
为了方便您快速部署,以下提供了文中提及的所有配置文件的完整代码。
1. docker-prod/app/Dockerfile
此文件定义了 PHP 应用容器的构建规则,基于 PHP 8.4 FPM Alpine 镜像,并安装了 Laravel 所需的所有扩展。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| FROM php:8.4-fpm-alpine
WORKDIR /var/www/html
RUN apk add --no-cache \ zip \ unzip \ libzip-dev \ libpng-dev \ jpeg-dev \ freetype-dev \ icu-dev \ && apk add --no-cache --virtual .build-deps \ $PHPIZE_DEPS \ zlib-dev \ linux-headers
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \ && docker-php-ext-install -j$(nproc) \ pdo_mysql \ bcmath \ gd \ exif \ intl \ zip \ pcntl \ sockets \ && pecl install redis \ && docker-php-ext-enable redis
RUN apk del .build-deps
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
COPY composer.json composer.lock ./
RUN composer install --no-interaction --no-plugins --no-scripts --prefer-dist --no-dev -o
COPY . .
EXPOSE 9000
|
2. docker-prod/nginx/default.conf
此文件定义了 Nginx 的配置规则,实现了 HTTP 到 HTTPS 的自动重定向,并配置了 SSL 证书和 PHP-FPM 的转发规则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| server { listen 80; server_name _; return 301 https://$host$request_uri; }
server { listen 443 ssl; server_name _;
ssl_certificate /etc/nginx/certs/lynx.pem; ssl_certificate_key /etc/nginx/certs/lynx-key.pem; ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/html/public; index index.php index.html;
location / { try_files $uri $uri/ /index.php?$query_string; }
location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass app:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; }
location ~ /\.ht { deny all; } }
|
3. docker-compose.prod.yml
此文件是 Docker Compose 的服务编排配置,定义了应用所需的所有服务:PHP 应用、Nginx、MySQL、Redis 和队列处理器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| services: app: build: context: . dockerfile: docker-prod/app/Dockerfile container_name: lynx_app restart: unless-stopped working_dir: /var/www/html volumes: - ./:/var/www/html - /var/www/html/vendor - /var/www/html/node_modules networks: - lynx-network depends_on: mysql: condition: service_healthy redis: condition: service_healthy
nginx: image: nginx:alpine container_name: lynx_nginx restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./:/var/www/html - ./docker-prod/nginx/default.conf:/etc/nginx/conf.d/default.conf - ./docker-prod/certs:/etc/nginx/certs depends_on: - app networks: - lynx-network
mysql: image: mysql:8.0 container_name: lynx_mysql restart: unless-stopped environment: MYSQL_DATABASE: ${DB_DATABASE} MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} MYSQL_PASSWORD: ${DB_PASSWORD} MYSQL_USER: ${DB_USERNAME} volumes: - lynx_mysql_data:/var/lib/mysql ports: - "3306:3306" networks: - lynx-network healthcheck: test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"] retries: 3 timeout: 5s
redis: image: redis:alpine container_name: lynx_redis restart: unless-stopped volumes: - lynx_redis_data:/data networks: - lynx-network healthcheck: test: ["CMD", "redis-cli", "ping"] retries: 3 timeout: 5s
queue-worker: build: context: . dockerfile: docker-prod/app/Dockerfile container_name: lynx_queue_worker restart: unless-stopped command: php artisan queue:work --verbose --tries=3 --timeout=90 working_dir: /var/www/html volumes: - ./:/var/www/html - /var/www/html/vendor - /var/www/html/node_modules networks: - lynx-network depends_on: mysql: condition: service_healthy redis: condition: service_healthy
networks: lynx-network: driver: bridge
volumes: lynx_mysql_data: driver: local lynx_redis_data: driver: local
|
4. .dockerignore
此文件用于指定在构建 Docker 镜像时需要忽略的文件和目录,可以显著减小镜像体积并提高构建速度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| .git .github .gitignore
.env .env.example
/vendor/ /node_modules/
docker-compose.yml /docker/
.idea .vscode *.DS_Store
|
六、总结
至此将 Laravel 项目通过 Docker 进行生产环境部署的完整流程已介绍完毕。这套方案具有以下特点:
- ✅ 容器化:所有服务通过 Docker 隔离运行,环境一致性强
- ✅ 易于部署:一键构建和启动,减少环境配置问题
- ✅ 生产优化:包含 SSL 支持、健康检查、自动重启等生产级特性
- ✅ 易于维护:提供完整的日常运维命令,代码更新流程清晰
- ✅ 可扩展性:基于 Docker Compose,方便后续添加其他服务
如果在部署过程中遇到问题,请检查:
- Docker Desktop 是否正常运行
- 端口 80、443、3306 是否被占用
- .env 文件中的配置是否正确
- storage 目录权限是否正确设置