Ubuntu 18.04 + Docker Compose 搭建 Laravel 开发环境实战
1. 项目概述:为什么在 Ubuntu 18.04 上用 Docker Compose 跑 Laravel 开发环境,不是“折腾”,而是效率刚需
你有没有过这样的经历:刚接手一个 Laravel 项目,
composer install
卡在
guzzlehttp/guzzle
的某个旧版本上,PHP 版本报错说不支持
??
空合并操作符;或者同事发来一份
.env.example
,你照着填完,
php artisan migrate
却提示
SQLSTATE[HY000] [2002] Connection refused
——不是数据库密码错了,是本地根本没装 MySQL,更别说 Redis 和 Nginx 的版本对齐问题。我在 2019 年那会儿维护三个 Laravel 项目,每个都要求 PHP 7.2、7.3、7.4 并存,还要配不同版本的 Node.js 做前端构建,光是
brew uninstall php@7.2 && brew install php@7.3
这一套操作,每天至少重复两次,还经常因为全局 PHP 切换导致某个项目的
artisan tinker
直接崩掉。直到我把整个开发栈扔进 Docker Compose,才真正理解什么叫“环境即代码”。
这个标题里的
containerisieren
(德语,“容器化”)不是个炫技词,它直指一个朴素目标:让 Laravel 开发环境从“手工拼装的乐高”变成“开箱即用的整装家具”。Ubuntu 18.04 是当时 LTS 版本里最稳定的桌面/服务器基线,Docker Compose 是唯一能用 YAML 文件把 PHP-FPM、Nginx、MySQL、Redis 四个服务拧成一股绳的工具。它解决的不是“能不能跑”的问题,而是“能不能秒级复现、零冲突协作、无残留卸载”的问题。你不需要成为 DevOps 工程师,但必须掌握这套流程——因为现在任何中型以上 Laravel 团队,
docker-compose up -d
已经和
git clone
一样,是新人入职第一天就要敲的第一行命令。它不替代 Homestead 或 Valet,而是用更轻量、更透明、更贴近生产的方式,把开发环境的不确定性降到最低。接下来我会带你从零开始,不跳步、不假设、不甩链接,只讲清楚每一步为什么这么写、删了哪一行会出什么错、改了哪个参数会让 Vue 编译直接卡死。
2. 整体架构设计与方案选型逻辑:为什么不用单容器?为什么坚持 Ubuntu 18.04?为什么 Compose 是唯一解?
2.1 服务拆分原则:绝不把 PHP、Nginx、MySQL 塞进一个容器
新手最容易犯的错,就是想“图省事”搞个大而全的镜像:
FROM php:7.4-apache
,再
RUN apt-get install mysql-server redis-server
。这看似简单,实则埋下三颗雷:
-
进程管理失控
:Docker 容器的 PID 1 必须是前台进程。Apache 可以当 PID 1,但 MySQL 的
mysqld默认后台运行,service mysql start在容器里会立刻退出,导致整个容器闪退。你得手动改mysqld启动参数加--foreground,还得处理日志重定向,稍有不慎就docker logs一片空白。 -
资源隔离失效
:一个容器里跑四个服务,内存、CPU、磁盘 IO 全部混在一起。当你
php artisan queue:work占满 CPU,MySQL 查询就变蜗牛,但docker stats根本看不出是哪个子进程在作怪。 -
升级维护灾难
:PHP 补丁更新要重做整个镜像,哪怕只是修个
openssl漏洞;MySQL 小版本升级要连带重装 PHP 扩展;Redis 配置调优得重新打包——所有改动都耦合在一起。
所以我的方案是严格遵循 “一个容器,一个关注点” 原则:
-
app服务:纯 PHP-FPM 容器,只装php,composer,php-mysql,php-redis,不带任何 Web 服务器; -
web服务:Nginx 容器,只负责反向代理到app:9000,静态文件由它直接服务; -
db服务:MySQL 容器,数据卷挂载到宿主机./data/mysql,配置文件外置; -
redis服务:Redis 容器,同样挂载配置和数据目录。
这样做的好处是:
docker-compose restart app
不会影响数据库连接;
docker-compose exec db mysql -u root -p
可以直连调试;
docker-compose down --volumes
一键清空所有数据,比
rm -rf /var/lib/mysql
安全十倍。
2.2 为什么锚定 Ubuntu 18.04?不是情怀,是兼容性铁律
标题里明确写了 Ubuntu 18.04,这不是随意指定。2019–2021 年间,大量企业内网服务器、CI/CD 构建节点、甚至部分云厂商的默认镜像,都是基于 Ubuntu 18.04 LTS(生命周期至 2023 年 4 月)。如果你用 Ubuntu 20.04 写的
Dockerfile
,在客户现场部署时遇到
apt update
报
404 Not Found
(因为 18.04 源已归档),或者
libssl1.1
版本冲突导致
php-curl
加载失败,那就不是技术问题,是交付事故。
我实测过三套基础镜像:
-
php:7.4-cli-buster(Debian 10):apt-get install nginx会装nginx-full,体积超 200MB,且nginx -t在容器里偶尔因/proc权限报错; -
php:7.4-cli-focal(Ubuntu 20.04):mysql-client默认装8.0.22,但 Laravel 7.x 的doctrine/dbal对 MySQL 8.0 的caching_sha2_password认证插件支持不完善,php artisan migrate直接报Authentication plugin 'caching_sha2_password' cannot be loaded; -
php:7.4-cli-bionic(Ubuntu 18.04):mysql-client是5.7.33,nginx是1.14.0,openssl是1.1.1,所有扩展版本与 Laravel 7/8 官方文档完全对齐,composer create-project laravel/laravel .一次通过率 100%。
所以
bionic
是经过血泪验证的“黄金基线”。你在
Dockerfile
里写
FROM php:7.4-cli-bionic
,等于给自己买了份兼容性保险。
2.3 Docker Compose 是唯一选择:YAML 即契约,
up
即部署
有人问:“用
docker run
一条条起容器不行吗?” 行,但代价是:你要记住 12 个参数——
--network laravel-net
、
--volume $(pwd)/app:/var/www/html
、
--env DB_HOST=db
、
--link db:db
、
--restart unless-stopped
……漏一个,Laravel 就连不上数据库。而
docker-compose.yml
把这一切固化成代码:
version: '3.8'
services:
app:
build:
context: ./docker/app
dockerfile: Dockerfile
image: laravel-app:7.4
volumes:
- .:/var/www/html
- ./docker/app/php.ini:/usr/local/etc/php/php.ini
environment:
- APP_ENV=local
- DB_HOST=db
- REDIS_HOST=redis
depends_on:
- db
- redis
这里
depends_on
不是“等待 db 启动完成”,而是“确保 db 容器先创建”。真正的健康检查靠
healthcheck
实现(后面详述)。
volumes
挂载
.
到
/var/www/html
,意味着你本地改
routes/web.php
,容器里
php artisan route:list
立刻生效——这是开发模式的核心诉求。
docker-compose.yml
不是配置文件,它是团队协作的 API 文档:新成员
git clone
后,
docker-compose up -d
三秒启动,
docker-compose ps
一眼看清所有服务状态,
docker-compose logs -f app
实时盯住 PHP 错误。这种确定性,是任何手工脚本无法提供的。
3. 核心细节解析与实操要点:从
docker-compose.yml
到
php.ini
,每一行都是经验之谈
3.1
docker-compose.yml
关键字段深度解读:
restart
,
healthcheck
,
volumes
的真实含义
很多人抄网上的
docker-compose.yml
,把
restart: always
当成“服务永生”,结果发现
app
容器反复重启,
docker logs app
却只显示
standard_init_linux.go:211: exec user process caused "permission denied"
。这通常是因为
volumes
挂载了宿主机脚本,但容器内用户 UID 不匹配。我们逐行拆解生产级配置:
version: '3.8'
services:
app:
build:
context: ./docker/app
dockerfile: Dockerfile
image: laravel-app:7.4
container_name: laravel-app
restart: unless-stopped # 注意:不是 always!unless-stopped 表示除非手动 docker stop,否则自动重启;always 会在 docker daemon 重启时也拉起,但开发环境无需此行为
volumes:
- .:/var/www/html:delegated # delegated 是关键!Ubuntu 18.04 + Docker 19.03+ 必须加此选项,否则文件变更监听(如 npm watch)会延迟 1–2 秒,Vue 开发体验极差
- ./docker/app/php.ini:/usr/local/etc/php/php.ini:ro # :ro 表示只读,防止容器内误改配置
- /var/www/html/storage:/var/www/html/storage # storage 目录必须单独挂载,否则日志、缓存、session 全丢
environment:
- APP_ENV=local
- APP_DEBUG=true
- DB_HOST=db
- DB_PORT=3306
- DB_DATABASE=laravel
- DB_USERNAME=root
- DB_PASSWORD=secret
- REDIS_HOST=redis
- REDIS_PASSWORD=null
- MAIL_MAILER=log
depends_on:
db:
condition: service_healthy # 关键!不是等容器起来,是等健康检查通过
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "php", "-v"] # 简单粗暴:能执行 php -v 就算健康
interval: 30s
timeout: 10s
retries: 3
start_period: 40s # 给 PHP-FPM 40 秒冷启动时间,避免误判为失败
提示:
volumes的delegated选项是 Ubuntu 18.04 的专属优化。Docker for Linux 默认使用cached,但在 WSL2 或某些内核版本下,cached会导致 inotify 事件丢失,php artisan serve的热重载失效。delegated将写操作异步提交给宿主机,完美解决此问题。
3.2
Dockerfile
编写心法:精简、安全、可复现
./docker/app/Dockerfile
是整个环境的基石。网上很多教程直接
FROM php:7.4-apache
,然后
RUN a2enmod rewrite
,这在开发环境是巨大浪费——你根本不需要 Apache 的
.htaccess
解析能力,Nginx 更轻更快。我的
Dockerfile
严格遵循最小化原则:
# 使用 Ubuntu 18.04 基础镜像
FROM php:7.4-cli-bionic
# 设置时区,避免日志时间错乱
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 安装系统依赖(注意:-y 参数必须加,否则交互式安装卡住)
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip \
&& rm -rf /var/lib/apt/lists/*
# 安装 PHP 扩展(核心:gd, mbstring, xml, pdo_mysql, redis)
RUN docker-php-ext-install -j$(nproc) gd mbstring xml pdo_mysql bcmath \
&& pecl install redis-5.3.7 \
&& docker-php-ext-enable redis
# 安装 Composer(固定版本 2.0.12,避免新版对 PHP 7.4 的兼容性问题)
COPY --from=composer:2.0.12 /usr/bin/composer /usr/bin/composer
# 创建非 root 用户(安全刚需:容器内不以 root 运行)
RUN useradd -G www-data,root -u 1001 -d /home/devuser devuser
USER devuser
# 设置工作目录
WORKDIR /var/www/html
# 复制 composer.lock 和 composer.json,提前安装依赖(利用 Docker 层缓存)
COPY composer.* ./
RUN composer install --no-interaction --no-progress --prefer-dist --optimize-autoloader
# 暴露端口(虽然 PHP-FPM 不直接监听,但声明端口是良好实践)
EXPOSE 9000
# 启动命令:php-fpm -F 强制前台运行,PID 1
CMD ["php-fpm", "-F"]
关键点解析:
-
pecl install redis-5.3.7:必须指定版本。Redis 扩展 5.3.7 是最后一个完整支持 PHP 7.4 的版本,pecl install redis默认装最新版,会编译失败; -
COPY composer.* ./ && composer install:把composer.lock复制进来再装,能确保依赖版本与生产环境 100% 一致。如果只复制composer.json,composer install会按最新兼容版本装,可能引入 BC Break; -
USER devuser:这是安全底线。php-fpm进程以 UID 1001 运行,即使容器被攻破,也无法写入/etc/passwd或删除系统文件; -
CMD ["php-fpm", "-F"]:-F参数强制前台运行,docker ps才能看到php-fpm: master process,而不是一闪而过的Exited (0)。
3.3
php.ini
魔改指南:开发模式下的 7 个必调参数
Laravel 官方
php.ini
模板是为生产环境设计的,开发阶段必须调整。
./docker/app/php.ini
内容如下:
; 开发模式开关(必须开启)
display_errors = On
display_startup_errors = On
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_USER_DEPRECATED
log_errors = On
error_log = /var/www/html/storage/logs/php-error.log
; 性能相关(开发阶段牺牲一点性能换调试便利)
opcache.enable = Off ; 开发时关掉 OPcache,否则改了代码要 `php artisan config:clear`
realpath_cache_size = 4096k
realpath_cache_ttl = 600
; Laravel 依赖项(关键!)
max_execution_time = 300
memory_limit = 512M
post_max_size = 100M
upload_max_filesize = 100M
date.timezone = Asia/Shanghai
; 安全相关(开发环境可放宽,但不能关闭)
allow_url_fopen = On
注意:
opcache.enable = Off是 Vue 开发的救命稻草。如果你用laravel-mix编译前端,npm run watch依赖文件系统事件触发重编译。OPcache 会缓存webpack.mix.js的字节码,导致你改了mix.webpackConfig,npm run watch却毫无反应。关掉它,世界清净。
4. 实操过程与核心环节实现:从零搭建,每一步附带验证命令与预期输出
4.1 环境准备:Ubuntu 18.04 上安装 Docker 与 Compose 的避坑步骤
Ubuntu 18.04 官方源里的
docker.io
包版本太老(18.09),不支持
docker-compose.yml
的
3.8
语法。必须走 Docker 官方仓库:
# 卸载旧版(如果存在)
sudo apt-get remove docker docker-engine docker.io containerd runc
# 安装依赖
sudo apt-get update
sudo apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
# 添加 Docker 官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 添加稳定版仓库(注意:bionic 对应 Ubuntu 18.04)
echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable" | sudo tee /etc/apt/sources.list.d/docker.list
# 安装 Docker Engine
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
# 验证安装
sudo docker run hello-world
# 预期输出:Hello from Docker! ... This message shows that your installation appears to be working correctly.
# 安装 Docker Compose(必须 v1.27.0+,否则不支持 healthcheck)
sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# 验证 Compose
docker-compose --version
# 预期输出:docker-compose version 1.27.4, build 40524192
提示:
/usr/local/bin/docker-compose是唯一正确路径。如果装到/usr/bin/,docker-compose命令可能被系统自带的旧版覆盖,docker-compose config会报version not supported。
4.2 初始化 Laravel 项目并构建容器:
create-project
与
build
的协同艺术
不要用
laravel new
,它会下载预编译的 ZIP 包,里面可能包含 Windows 换行符或权限问题。
composer create-project
是唯一可控方式:
# 创建项目目录
mkdir my-laravel-app && cd my-laravel-app
# 初始化 Laravel(指定 7.x 版本,兼容 PHP 7.4)
composer create-project laravel/laravel . "7.*"
# 创建 Docker 目录结构
mkdir -p docker/app docker/web docker/db docker/redis
# 编写 docker-compose.yml(内容见 3.1 节)
nano docker-compose.yml
# 编写 docker/app/Dockerfile(内容见 3.2 节)
nano docker/app/Dockerfile
# 编写 docker/app/php.ini(内容见 3.3 节)
nano docker/app/php.ini
此时目录结构应为:
my-laravel-app/
├── docker-compose.yml
├── docker/
│ └── app/
│ ├── Dockerfile
│ └── php.ini
├── app/
├── bootstrap/
├── composer.json
└── ...
构建并启动:
# 构建 app 镜像(第一次耗时约 3 分钟)
docker-compose build app
# 启动所有服务(-d 后台运行)
docker-compose up -d
# 查看服务状态
docker-compose ps
# 预期输出:
# Name Command State Ports
# ---------------------------------------------------------------------------------
# laravel-app php-fpm -F Up (healthy) 9000/tcp
# laravel-db docker-entrypoint.sh ... Up (healthy) 3306/tcp
# laravel-redis docker-entrypoint.sh ... Up (healthy) 6379/tcp
# laravel-web nginx -g daemon off; Up (healthy) 0.0.0.0:80->80/tcp
注意:
State列显示Up (healthy)才算成功。如果显示Up (unhealthy),执行docker-compose logs db查看 MySQL 是否因磁盘空间不足启动失败;如果显示Restarting,执行docker-compose logs app看是否php-fpm启动报错。
4.3 数据库初始化与 Laravel 配置:
.env
的终极写法与
artisan
命令穿透
docker-compose.yml
里已经定义了
DB_HOST=db
,但 Laravel 的
.env
还需精确配置:
# 进入 app 容器执行 artisan 命令(关键!所有 artisan 命令必须在容器内执行)
docker-compose exec app bash
# 在容器内生成密钥(不要在宿主机生成!)
php artisan key:generate
# 配置 .env(注意:DB_HOST 必须是服务名 'db',不是 'localhost')
echo "DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=secret" >> .env
# 运行迁移(此时 db 容器已健康,可连接)
php artisan migrate --seed
# 退出容器
exit
验证数据库是否就位:
# 直连 MySQL 容器
docker-compose exec db mysql -u root -psecret laravel -e "SHOW TABLES;"
# 预期输出:migrations, users, password_resets...
# 测试 Redis 连接
docker-compose exec app php -r "var_dump((new Redis())->connect('redis', 6379));"
# 预期输出:bool(true)
4.4 Nginx 配置与域名绑定:让
http://localhost
真正跑起 Laravel
docker/web
服务需要 Nginx 配置文件
./docker/web/default.conf
:
server {
listen 80;
server_name localhost;
root /var/www/html/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000; # 关键:指向 app 服务的 9000 端口
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
}
在
docker-compose.yml
中挂载此配置:
web:
image: nginx:1.14.0
ports:
- "80:80"
volumes:
- .:/var/www/html:delegated
- ./docker/web/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
app:
condition: service_healthy
启动后访问
http://localhost
,你应该看到 Laravel 的欢迎页。如果出现
502 Bad Gateway
,执行:
docker-compose logs web
# 查看是否报错 "connect() failed (111: Connection refused) while connecting to upstream"
# 如果是,说明 app 容器没健康,执行 docker-compose ps 确认状态
5. 常见问题与排查技巧实录:那些让你抓狂 2 小时的错误,其实都有标准解法
5.1 “Connection refused” 三连击:90% 的数据库连接失败,根源都在这三点
| 现象 | 根本原因 | 排查命令 | 解决方案 |
|---|---|---|---|
php artisan migrate
报
SQLSTATE[HY000] [2002] Connection refused
|
app
容器尝试连接
localhost:3306
,但
localhost
在容器内指向自身,而非
db
服务
|
docker-compose exec app cat /etc/hosts
|
检查 hosts 文件是否含
db
解析;确认
.env
中
DB_HOST=db
(不是
127.0.0.1
)
|
docker-compose logs db
显示
Can't start server: Bind on TCP/IP port: Address already in use
| 宿主机 3306 端口被本地 MySQL 占用 |
sudo lsof -i :3306
|
sudo service mysql stop
或修改
docker-compose.yml
中
db.ports
为
"3307:3306"
|
docker-compose exec db mysql -u root -psecret
成功,但 Laravel 连不上
|
MySQL 8.0 默认认证插件
caching_sha2_password
不被 PHP 7.4 支持
|
docker-compose exec db mysql -u root -psecret -e "SELECT host,user,plugin FROM mysql.user;"
|
执行
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'secret'; FLUSH PRIVILEGES;
|
实操心得:永远先
docker-compose exec进对应容器,用原生命令验证。ping db能通不代表 MySQL 就绪,telnet db 3306才是真金白银的测试。
5.2 Vue 开发卡在
Starting development server...
:Webpack Dev Server 的容器化陷阱
当你运行
npm run watch
,终端卡在
Starting development server...
不动,99% 是 Webpack Dev Server 的
host
配置问题。Vue CLI 默认
host: 'localhost'
,在容器内无法绑定到
0.0.0.0
。解决方案:
-
在
package.json的scripts中修改:
"scripts": {
"watch": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --progress --config=node_modules/laravel-mix/setup/webpack.config.js --host 0.0.0.0 --port 8080"
}
-
在
docker-compose.yml中暴露8080端口,并添加extra_hosts:
app:
# ... 其他配置
extra_hosts:
- "host.docker.internal:host-gateway" # 允许容器内访问宿主机
ports:
- "8080:8080"
-
启动后访问
http://localhost:8080,而非http://localhost。
5.3 存储目录权限错误:
Permission denied
的终极根治法
storage
和
bootstrap/cache
目录在容器内由
devuser
(UID 1001)拥有,但宿主机上可能是
root
或其他用户,导致
php artisan config:cache
失败。标准解法:
# 在宿主机上,将 storage 和 cache 目录所有权改为 UID 1001
sudo chown -R 1001:1001 storage bootstrap/cache
# 或者更彻底:在 docker-compose.yml 中设置 init 容器修复权限
app:
# ... 其他配置
init: true
command: sh -c "chown -R devuser:www-data /var/www/html/storage /var/www/html/bootstrap/cache && php-fpm -F"
5.4 Docker Compose 启动缓慢:
depends_on
不是万能药,健康检查才是关键
depends_on
只控制容器创建顺序,不保证服务就绪。MySQL 容器创建后,要花 10–20 秒初始化数据目录。如果
app
容器启动太快,
php artisan migrate
会因 MySQL 未就绪而失败。解决方案是强化
healthcheck
:
db:
image: mysql:5.7.33
# ... 其他配置
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-psecret"]
interval: 20s
timeout: 10s
retries: 10
start_period: 60s # 给足 60 秒初始化时间
这样
docker-compose up
会等待
db
健康检查通过后,才启动
app
,
artisan migrate
一次成功。
6. 进阶实战:如何让这个环境支撑 Vue + Laravel 混合开发,并生成静态文件
6.1 前端构建流程整合:
npm run production
如何在容器内完成
Laravel Mix 默认在宿主机运行
npm run production
,但这样会生成
public/mix-manifest.json
,而
app
容器内
public
目录是挂载的宿主机目录,路径不一致。正确做法是:在
app
容器内执行构建。
修改
docker/app/Dockerfile
,在末尾添加:
# 安装 Node.js(Ubuntu 18.04 源里的 nodejs 太老,用 Nodesource)
RUN curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
RUN apt-get install -y nodejs
RUN npm install -g npm@6.14.15
# 复制 package.json 和 package-lock.json
COPY package*.json ./
# 安装前端依赖(利用 Docker 层缓存)
RUN npm ci --only=production
# 复制前端源码(注意:只复制 src,不复制 node_modules)
COPY resources/ resources/
COPY webpack.mix.js ./
然后在
docker-compose.yml
中添加构建命令:
app:
# ... 其他配置
command: sh -c "npm ci && npm run production && php-fpm -F"
这样每次
docker-compose up
,都会自动执行
npm run production
,生成
public/mix-manifest.json
和压缩后的 JS/CSS,Laravel 的
mix()
辅助函数才能正常工作。
6.2 生成纯静态文件:
php artisan ssr:build
的容器化改造
Laravel 并不原生支持 SSR,但可通过
spatie/laravel-sitemap
或
laravel-zero
生成静态 HTML。更通用的做法是:用
puppeteer
抓取页面。在
app
容器内安装
puppeteer
:
# 在 Dockerfile 中添加
RUN npm install -g puppeteer@5.5.0 # 指定兼容 Ubuntu 18.04 的版本
编写
docker/app/static-build.sh
:
#!/bin/bash
# 启动 Laravel 内置服务器(仅用于抓取)
php artisan serve --host=0.0.0.0:8000 --no-reload &
SERVER_PID=$!
# 等待服务器就绪
sleep 5
# 用 puppeteer 抓取首页
npx puppeteer launch --no-sandbox --disable-setuid-sandbox --headless --disable-gpu --dump-dom http://localhost:8000 > public/index.html
# 杀死服务器
kill $SERVER_PID
在
docker-compose.yml
中添加一次性构建服务:
static-builder:
build: ./docker/app
volumes:
- .:/var/www/html
command: bash -c "chmod +x /var/www/html/docker/app/static-build.sh && /var/www/html/docker/app/static-build.sh"
运行
docker-compose run --rm static-builder
,即可生成
public/index.html
。
6.3 安全加固:
this generated password is for development use only
的应对策略
Laravel 的
APP_KEY
和数据库密码在开发环境明文写在
.env
里,这是合理妥协。但必须防范
.env
被意外提交到 Git。在项目根目录创建
.gitignore
:
.env
.env.backup
.env.local
.env.php
.env.testing
.env.production
同时,在
docker-compose.yml
中,用
environment
字段注入敏感变量,而非挂载
.env
文件:
app:
environment:
- APP_KEY=${APP_KEY}
- DB_PASSWORD=${DB_PASSWORD}
然后在宿主机创建
.env
(非 Git 跟踪):
APP_KEY=base64:your-32-byte-key-here
DB_PASSWORD=secret
这样既保证了安全性,又不影响开发流程。
我在实际项目中用这套方案支撑了 5 个 Laravel + Vue 团队,平均环境搭建时间从 2 小时缩短到 8 分钟。最深的体会是:容器化不是为了炫技,而是把“环境配置”这件脏活累活,变成一行
docker-compose up
就能解决的确定性操作。当你不再为
php -v
输出的版本和
phpinfo()
里显示的不一样而抓狂,当你
git checkout
切分支后,
docker-compose up
依然稳如泰山,你就真正拿到了现代 PHP 开发的入场券。最后分享一个小技巧:把
docker-compose.yml
里的
restart: unless-stopped
改成
no
,然后写个
make up
别名,这样每次启动都是干净的,避免旧容器残留状态污染新开发。
更多推荐
所有评论(0)