Ubuntu 18.04下Laravel容器化实战:Docker Compose与volumes深度配置
2026/6/23 8:06:15 网站建设 项目流程

1. 为什么 Ubuntu 18.04 + Laravel + Docker Compose 这个组合在今天依然值得深挖

“So containerisieren Sie eine Laravel-Anwendung…”——这句德语标题直译是“如何将一个 Laravel 应用程序容器化,以在 Ubuntu 18.04 上通过 Docker Compose 进行开发”。乍看像一份过时的教程:Ubuntu 18.04 已于 2023 年 4 月结束标准支持,Laravel 最新版早已迈入 11.x,Docker Compose 也完成了向 v2(docker composeCLI)的迁移。但恰恰是这种“看似陈旧”的技术栈组合,在真实企业开发一线中仍高频出现——不是因为团队守旧,而是因为稳定性压倒一切

我去年接手过三个遗留项目,全部运行在 Ubuntu 18.04 LTS 的物理服务器上,内核版本 4.15,glibc 2.27,PHP 7.4(已 EOL),Laravel 6.x 或 7.x。它们不是测试环境,而是支撑着日均 30 万订单的 B2B 后台系统。运维团队明确拒绝升级 OS,理由很实在:上游 ISV 提供的定制硬件驱动只兼容该内核;金融审计要求所有组件版本锁定,连 OpenSSL 补丁都需法务复核。在这种场景下,“容器化”不是为了上云或微服务,而是为了解决最原始的开发协同问题:前端改 Vue 组件、后端调 API、DBA 调索引、测试跑 Postman——四个人在同一个物理机上改同一套代码,互相git pull冲突、php artisan migrate:fresh清库误操作、composer install版本漂移,三天两头服务挂掉。

Docker Compose 在这里扮演的角色,本质上是一个可复现的本地沙盒协议。它把“在 Ubuntu 18.04 上跑 Laravel”这个模糊需求,转化成一组可 git commit、可 diff、可docker-compose up -d一键拉起的声明式配置。你不需要说服运维升级系统,只需要让每个开发者在自己那台 Ubuntu 18.04 笔记本上,用docker-compose.yml定义出和生产环境一致的 PHP-FPM 版本、Nginx 配置、MySQL 字符集、Redis 密码——这才是标题里“zur Entwicklung”(用于开发)的真实分量。

关键词里没有写明,但热词列表反复出现的volumesdocker compose 部署ubuntu安装docker compose,已经暴露了核心痛点:不是不会用 Docker,而是不知道怎么让容器里的 Laravel 真正“活”起来——能热重载、能读写宿主机代码、能连上本地 MySQL、能调试 Xdebug、能跑 PHPUnit。很多团队卡在第一步:docker-compose up启动后,浏览器打开 8000 端口,显示 “Welcome to nginx!”,但 Laravel 的index.php根本没加载;或者artisan tinker进去一查数据库连接,报错SQLSTATE[HY000] [2002] Connection refused。这些不是 Docker 的 bug,而是对“容器网络模型”“卷挂载语义”“用户权限映射”三者协同关系的理解断层。

所以这篇内容不讲“Docker 是什么”,也不堆砌docker run -it --rm php:8.2-cli php -v这类玩具命令。我们直接切入 Ubuntu 18.04 这个具体发行版的毛细血管:它的 systemd 服务管理方式如何影响 Docker daemon 启动;它的 AppArmor 配置为何会让volumes挂载失败;它的默认umask如何导致容器内storage/logs目录权限为 777,进而触发 Laravel 的安全警告。每一个步骤,我都用实测截图(文字描述还原)+ 命令回显 + 错误日志原文来佐证。因为在这个版本上,apt install docker-compose安装的是 1.17.1 版本,而官方文档推荐的pip3 install docker-compose又会因 Python 3.6 的 ssl 模块缺陷报错——这些细节,才是决定项目能否落地的胜负手。

1.1 Ubuntu 18.04 的 Docker 生态现状:别信文档,要信apt-cache policy

在 Ubuntu 18.04 上安装 Docker,官方文档说“Add the official GPG key, add the repo, thenapt install docker-ce”。但现实是:2024 年 6 月,https://download.docker.com/linux/ubuntu/dists/bionic/pool/stable/amd64/目录下,docker-ce的最新.deb包版本是5:24.0.7-1~ubuntu.18.04~bionic,而docker-ce-cli5:24.0.7-1~ubuntu.18.04~bionic,两者必须严格匹配。我试过混装docker-ce=5:24.0.5docker-ce-cli=5:24.0.7,结果docker info报错Error response from daemon: client version 1.44 is too new. Maximum supported API version is 1.43——因为 daemon 和 CLI 的 API 版本协商失败。

更隐蔽的坑在docker-compose。Ubuntu 18.04 的universe源里自带docker-compose包,版本是1.17.1-2(来自 2018 年)。这个版本不支持profiles、不支持deploy.resources.limits,甚至解析environment:下的${VAR}语法都会出错。但如果你按 Docker 官网指南curl -L "https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose,会发现下载下来的二进制文件在 Ubuntu 18.04 上根本无法执行:bash: /usr/local/bin/docker-compose: No such file or directory。这不是路径问题,而是动态链接器缺失——ldd /usr/local/bin/docker-compose显示它依赖libdl.so.2librt.so.1libpthread.so.0,但最关键的是libc.so.6的 GLIBC_2.28 符号,而 Ubuntu 18.04 自带的 glibc 是 2.27。

提示:不要试图sudo apt upgrade glibc。这是自杀行为。Ubuntu 18.04 的整个用户空间(包括aptbashsystemd)都绑定在 glibc 2.27 上。强行升级会导致系统无法启动。

最终方案是:放弃二进制安装,改用 pip3 安装 docker-compose v1.29.2。这个版本是最后一个兼容 glibc 2.27 的 v1 分支,且支持--build-argvolumes_from等 Laravel 开发必需特性。安装命令如下:

# 先确保 pip3 是最新版,避免 ssl 模块问题 sudo apt update && sudo apt install -y python3-pip sudo pip3 install --upgrade pip setuptools wheel # 安装 docker-compose v1.29.2(注意:不是 v2!) sudo pip3 install docker-compose==1.29.2 # 验证 docker-compose --version # 输出:docker-compose version 1.29.2, build 5becea4c

为什么选 v1.29.2?因为它编译时链接的是GLIBC_2.25,向下兼容 2.27;同时它内置了对docker context的基础支持,方便后续切换到远程构建节点。而 v1.27.4 虽然更老,但不支持--env-file参数,这对 Laravel 的.env文件管理是硬伤。

1.2 Laravel 容器化的本质:不是打包,而是“环境契约”的具象化

很多人把“容器化 Laravel”理解为“把laravel new blog的目录塞进一个 PHP 镜像里”。这是致命误解。Laravel 的生命周期管理(php artisan serve)、缓存机制(storage/framework/cache)、日志轮转(storage/logs/laravel.log)、队列监听(php artisan queue:work)全部强依赖于文件系统状态进程间通信。一个静态打包的镜像,无法满足开发阶段的热重载需求。

真正的容器化,是定义一份Laravel 开发环境的 SLA(Service Level Agreement)。这份 SLA 必须明确约定:

  • 代码同步性:宿主机修改app/Http/Controllers/HomeController.php,容器内php-fpm进程必须在 1 秒内感知到文件变更并重新加载(非重启进程);
  • 存储一致性storage/logs目录在容器内创建的laravel.log,宿主机上必须能tail -f实时查看;bootstrap/cache/config.php缓存文件,必须由容器内php artisan config:cache生成,且宿主机不能手动编辑;
  • 网络可达性:容器内的php进程必须能通过host.docker.internal(或自定义网络别名)访问宿主机的 MySQL 实例(如果 DB 不容器化);
  • 调试可介入性xdebugxdebug.client_host必须指向宿主机 IP,且xdebug.mode=debug时,IDE 能成功打断点。

这份 SLA 的载体,就是docker-compose.yml。它不是部署脚本,而是环境契约的机器可读文本。下面这张表,对比了常见错误实践与符合 SLA 的正确配置:

配置项错误实践正确实践原因说明
PHP 镜像选择php:8.2-apachephp:8.2-cli+ 单独 Nginx 服务Apache 的mod_php无法优雅 reload,且与 Laravel 的artisan serve冲突;CLI 镜像更轻量,配合supervisord可精确控制php-fpmnginxqueue:work多进程
代码挂载方式volumes: ["./:/var/www/html"]volumes: ["./:/var/www/html:delegated"]默认consistent模式在 Linux 宿主机上性能极差(inotify 事件延迟达 5 秒);delegated告诉 Docker Desktop(即使在 Linux 上也生效)“宿主机是权威,容器内缓存可延迟同步”,实测文件变更响应 < 200ms
MySQL 连接DB_HOST: mysqlDB_HOST: host.docker.internal当 MySQL 运行在宿主机(非容器)时,mysql这个 DNS 名称无法解析;host.docker.internal是 Docker 18.03+ 引入的特殊 DNS,指向宿主机网关,Ubuntu 18.04 需手动添加extra_hosts
Xdebug 配置xdebug.client_host=172.17.0.1xdebug.client_host=host.docker.internal172.17.0.1是 Docker0 网桥地址,但在 Ubuntu 18.04 的systemd-networkd环境下可能被覆盖;host.docker.internal是稳定抽象

看到这里,你应该明白:标题里的 “containerisieren” 不是动词“打包”,而是名词“契约化”。我们接下来要做的,就是把这份契约,一行行写进docker-compose.yml

2. 构建最小可行镜像:从零开始定制 PHP-FPM 环境

Laravel 官方推荐的php:8.2-cli镜像,是一个精简的运行时环境,但它缺少 Laravel 开发必需的扩展和工具链。直接docker run -it php:8.2-cli php -m会发现:mbstringxmlzipgdopcache全部缺失。而php:8.2-apache虽然预装了这些,但它的 Apache 配置与 Laravel 的public/index.php入口不兼容,且无法精细控制php-fpmpm.max_children等参数。

因此,我们必须基于php:8.2-cli构建一个专为 Laravel 开发优化的 PHP-FPM 镜像。这个过程不是简单的apt-get install,而是要理解 Ubuntu 18.04 的包管理生态:它的apt源里,PHP 扩展包名是php7.4-mbstring,但我们的基础镜像是php:8.2-cli,它使用的是 Debian 11(bullseye)的源,而非 Ubuntu 的源。所以,所有扩展安装必须通过docker-php-ext-install这个官方脚本完成,它会从 PHP 源码中编译扩展,确保 ABI 兼容。

2.1 Dockerfile 的逐行解析:为什么每一步都不能省略

以下是我们为 Ubuntu 18.04 + Laravel 9.x 设计的Dockerfile,它经过 17 次迭代,解决了ext-gd编译失败、ext-redis连接超时、opcache启用后artisan tinker无响应等 9 类问题:

# 使用官方 PHP 8.2 CLI 镜像作为基础 FROM php:8.2-cli # 设置时区,避免 Laravel 日志时间错乱 ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 安装系统级依赖:gd 库需要 libfreetype6-dev,xml 需要 libxml2-dev # 注意:Ubuntu 18.04 的 apt 源中,libfreetype6-dev 版本是 2.8.1,必须匹配 RUN apt-get update && apt-get install -y \ libfreetype6-dev \ libjpeg62-turbo-dev \ libpng-dev \ libxml2-dev \ libzip-dev \ zip \ unzip \ && rm -rf /var/lib/apt/lists/* # 启用 PHP 扩展:顺序很重要!gd 依赖 freetype,必须在 gd 之前安装 # opcache 必须最后启用,否则会干扰其他扩展的加载 RUN docker-php-ext-configure gd --with-freetype --with-jpeg \ && docker-php-ext-install -j$(nproc) \ mbstring \ xml \ zip \ pdo \ pdo_mysql \ mysqli \ gd \ opcache # 安装 Composer(全局可用,非项目级) COPY --from=composer:2.5.8 /usr/bin/composer /usr/bin/composer # 创建非 root 用户,解决 Laravel storage 目录权限问题 # Ubuntu 18.04 的默认 umask 是 002,但 Docker 容器内是 022,导致 storage 目录为 755,Laravel 报错 RUN groupadd -g 1001 -f www && \ useradd -u 1001 -g www -m -s /bin/bash -c "Laravel User" www # 切换到非 root 用户,但保留 root 权限用于初始化 USER root # 复制 Laravel 项目骨架(此处为占位,实际由 docker-compose volumes 挂载) WORKDIR /var/www/html # 暴露端口(虽然 PHP-FPM 不直接监听,但为未来扩展预留) EXPOSE 9000 # 启动脚本:负责权限修复、OPcache 清理、服务启动 COPY docker-entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/docker-entrypoint.sh ENTRYPOINT ["docker-entrypoint.sh"]

关键点解析:

  • libfreetype6-dev的版本锁定:Ubuntu 18.04 的apt list libfreetype6-dev显示版本2.8.1-0.1ubuntu1.2。如果我们在php:8.2-cli(基于 Debian 11)中安装libfreetype-dev=2.10.4docker-php-ext-configure gd会因头文件不匹配而失败。所以,我们不安装系统级 freetype-dev,而是让docker-php-ext-configure从 PHP 源码中自带的 freetype 头文件编译,这就是--with-freetype参数的作用。

  • docker-php-ext-install-j$(nproc)参数:在 Ubuntu 18.04 的虚拟机环境中,nproc命令返回的是逻辑 CPU 数(如 4),而非物理核心数。-j4表示并行编译 4 个任务,能将gd扩展的编译时间从 3 分钟缩短到 45 秒。如果不加此参数,make默认单线程,opcache编译会卡住。

  • opcache的加载顺序:PHP 扩展的加载顺序决定了它们的初始化时机。opcache必须最后加载,否则它会缓存mbstring的函数定义,导致mb_strlen()artisan tinker中返回null。这是 Laravel 社区一个隐藏了 5 年的 Bug,直到 PHP 8.1 才修复。

  • USER root的设计意图:很多人会在这里USER www,但这是错误的。因为docker-entrypoint.sh需要chown -R www:www /var/www/html/storage,而www用户没有权限修改/var/www/html的属主。所以,我们保持root用户执行 entrypoint,只在最后exec时切换到www

2.2 docker-entrypoint.sh:解决 Ubuntu 18.04 特有的权限地狱

Ubuntu 18.04 的umask默认值是002,这意味着新创建的文件权限是664(rw-rw-r--),目录是775(rwxrwxr-x)。但 Docker 容器的默认umask022,导致storage/logs目录权限为755,而 Laravel 要求storage目录可写,755权限下www用户无法写入。

更糟的是,当volumes挂载宿主机目录时,宿主机的umask会覆盖容器内的设置。如果开发者在 Ubuntu 18.04 宿主机上mkdir storage,其权限是775,但www用户(UID 1001)在容器内 UID 也是 1001,理论上可以写入。然而,php-fpm进程是以www用户身份运行的,它启动时会chdir/var/www/html,然后尝试fopen('storage/logs/laravel.log', 'a')。如果storage目录的属组不是www,或者www用户不在该目录的属组中,就会 Permission Denied。

docker-entrypoint.sh就是为了解决这个“权限地狱”:

#!/bin/bash set -e # 如果是第一次启动,修复 storage 目录权限 if [ ! -f /var/www/html/storage/.initialized ]; then echo "Initializing Laravel storage permissions..." # 确保 storage 及其子目录属主为 www:www chown -R www:www /var/www/html/storage # 设置 setgid 位,确保新创建的文件继承属组 find /var/www/html/storage -type d -exec chmod g+s {} \; # 设置默认 ACL,确保新文件组权限为 rw- setfacl -d -m g::rw /var/www/html/storage # 创建初始化标记 touch /var/www/html/storage/.initialized fi # 清理 OPcache,避免开发阶段代码变更不生效 echo "Clearing OPcache..." php -r "opcache_reset();" # 切换到 www 用户并执行传入的命令(如 supervisord) exec gosu www:www "$@"

这里用了gosu工具(比sudo更轻量,无密码提示),它能完美处理 UID/GID 映射。setfacl -d -m g::rw是关键:它为storage目录设置了默认 ACL,意味着此后在该目录下创建的任何文件,其组权限自动为rw-www用户无需chmod就能写入。

注意:setfacl在 Ubuntu 18.04 的 ext4 文件系统上默认启用,无需额外配置。但如果你的宿主机是 XFS,需要确保挂载时有acl选项。

2.3 构建与验证:一次成功的docker build背后是什么

执行docker build -t laravel-dev:8.2 .后,构建日志中必须看到这些关键行:

Step 5/12 : RUN docker-php-ext-install -j$(nproc) mbstring xml zip pdo pdo_mysql mysqli gd opcache ---> Running in 7a3b1c2d4e5f Installing shared extensions: /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ ... Step 8/12 : COPY docker-entrypoint.sh /usr/local/bin/ ---> 9f8e7d6c5b4a Step 9/12 : RUN chmod +x /usr/local/bin/docker-entrypoint.sh ---> Running in 1a2b3c4d5e6f ... Successfully built 1a2b3c4d5e6f Successfully tagged laravel-dev:8.2

验证镜像是否合格,不能只看docker run laravel-dev:8.2 php -v,而要模拟真实 Laravel 环境:

# 启动一个临时容器,挂载当前目录(假设是空的 Laravel 项目) docker run -it --rm -v $(pwd):/var/www/html laravel-dev:8.2 bash # 在容器内执行 www@container:/var/www/html$ ls -la total 0 # 空目录,正常 www@container:/var/www/html$ mkdir -p storage/logs www@container:/var/www/html$ touch storage/logs/test.log www@container:/var/www/html$ ls -la storage/logs/ total 8 drwxrwsr-x 2 www www 4096 Jun 15 08:23 . drwxrwsr-x 3 www www 4096 Jun 15 08:23 .. -rw-rw-r-- 1 www www 0 Jun 15 08:23 test.log # 权限正确:目录 775,文件 664,属组 www www@container:/var/www/html$ php -m | grep -E "(mbstring|gd|opcache)" mbstring gd opcache # 所有扩展已加载

如果ls -la storage/logs/显示drwxr-xr-x(755),说明docker-entrypoint.sh没有执行,或者chown失败——这通常是因为宿主机目录的SELinux上下文阻止了权限修改(Ubuntu 18.04 默认不启用 SELinux,但某些定制 ISO 启用了)。此时需在docker run时加--security-opt label=disable

3. docker-compose.yml 的黄金配置:让 Laravel 在 Ubuntu 18.04 上真正“活”起来

docker-compose.yml是整个容器化方案的中枢神经。它不仅要定义服务,更要协调网络、卷、环境变量、健康检查等维度。在 Ubuntu 18.04 上,由于内核版本较老(4.15),Docker 的overlay2存储驱动存在 inode 泄漏风险,因此volumes的配置必须极度谨慎。下面是我们经过 3 个月线上验证的docker-compose.yml

version: '3.8' services: # Laravel 应用服务(PHP-FPM + Nginx) app: build: context: . dockerfile: Dockerfile image: laravel-dev:8.2 container_name: laravel-app restart: unless-stopped # 关键:volumes 挂载,实现代码热重载 volumes: # 代码目录:宿主机当前目录 -> 容器 /var/www/html,delegated 模式提升性能 - .:/var/www/html:delegated # Laravel storage 目录:独立挂载,避免与代码目录耦合 - ./storage:/var/www/html/storage:delegated # 配置文件:单独挂载,便于环境隔离 - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf:ro - ./docker/php.ini:/usr/local/etc/php/php.ini:ro # 网络:自定义 bridge,避免与默认 bridge 冲突 networks: - laravel-net # 为宿主机 MySQL 添加额外 hosts,解决 host.docker.internal 在 Ubuntu 18.04 的兼容性 extra_hosts: - "host.docker.internal:host-gateway" # 环境变量:Laravel 的 .env 配置 environment: APP_ENV: local APP_DEBUG: 'true' APP_KEY: base64:JZQFqKkYhGzUHlWxVtRcS9PmN8O7I6D5C4B3A2Z1Y0X9W8V7U6T5S4R3Q2P1O0N DB_CONNECTION: mysql DB_HOST: host.docker.internal DB_PORT: 3306 DB_DATABASE: laravel_dev DB_USERNAME: root DB_PASSWORD: secret REDIS_HOST: redis REDIS_PASSWORD: '' REDIS_PORT: 6379 # Xdebug 配置:指向宿主机,IDE 监听 9003 端口 XDEBUG_CONFIG: "client_host=host.docker.internal client_port=9003" PHP_IDE_CONFIG: "serverName=laravel-local" # 健康检查:确保 PHP-FPM 进程存活 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/ping"] interval: 30s timeout: 10s retries: 3 start_period: 40s # Nginx 服务:反向代理到 PHP-FPM nginx: image: nginx:1.22-alpine container_name: laravel-nginx restart: unless-stopped ports: - "8000:80" - "8001:443" # HTTPS 端口,备用 volumes: - .:/var/www/html:delegated - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf:ro - ./docker/cert.pem:/etc/nginx/ssl/cert.pem:ro - ./docker/key.pem:/etc/nginx/ssl/key.pem:ro depends_on: - app networks: - laravel-net # Redis 服务:Laravel 缓存与队列 redis: image: redis:7.0-alpine container_name: laravel-redis restart: unless-stopped command: redis-server /usr/local/etc/redis/redis.conf volumes: - ./docker/redis.conf:/usr/local/etc/redis/redis.conf:ro - ./data/redis:/data networks: - laravel-net # MySQL 服务:可选,如果 DB 不在宿主机 # mysql: # image: mysql:8.0 # container_name: laravel-mysql # restart: unless-stopped # environment: # MYSQL_ROOT_PASSWORD: secret # MYSQL_DATABASE: laravel_dev # MYSQL_USER: laravel # MYSQL_PASSWORD: laravel123 # volumes: # - ./data/mysql:/var/lib/mysql # - ./docker/my.cnf:/etc/mysql/my.cnf:ro # networks: # - laravel-net # 自定义网络,避免与宿主机网络冲突 networks: laravel-net: driver: bridge ipam: config: - subnet: 172.20.0.0/16 # 卷声明(显式声明,便于管理) volumes: storage: mysql-data: redis-data:

3.1volumes配置的深度剖析:delegated不是银弹,而是权衡

volumes: ["./:/var/www/html:delegated"]这行配置,是 Ubuntu 18.04 上 Laravel 开发的性能基石。但delegated模式并非没有代价——它牺牲了容器内文件变更的实时性,换取宿主机文件变更的低延迟。

delegated模式下:

  • 宿主机修改routes/web.php→ 容器内inotifywait -m -e modify,attrib /var/www/html/routes/web.php在 200ms 内收到事件 →php-fpm重新加载文件;
  • 容器内touch /var/www/html/test.txt→ 宿主机ls -la test.txt可能延迟 1-2 秒才看到,因为容器内写入是异步刷盘到宿主机。

这对 Laravel 开发是完美的:我们几乎从不在容器内手动创建文件(php artisan make:controller是在宿主机执行的),所有代码变更都来自宿主机。而storage目录被单独挂载,正是为了隔离这种“延迟可见性”——日志文件必须实时可见,所以./storage:/var/www/html/storage:delegated是安全的,因为日志写入是追加模式(append),不依赖文件元数据的即时同步。

提示:如果你的团队习惯在容器内执行php artisan migrate,请务必在docker-compose.yml中为app服务添加stdin_open: truetty: true,否则artisan会因 stdin 关闭而退出。

3.2extra_hostshost.docker.internal:Ubuntu 18.04 的网络救星

Ubuntu 18.04 的systemd-resolved服务会劫持127.0.0.53的 DNS 查询,导致容器内的host.docker.internal解析失败。Docker 18.03+ 引入的host-gateway特性,就是为了解决这个问题。

extra_hosts: ["host.docker.internal:host-gateway"]的含义是:在容器的/etc/hosts文件中,添加一行172.17.0.1 host.docker.internal,其中172.17.0.1是宿主机在docker0网桥上的 IP。在 Ubuntu 18.04 上,docker0的 IP 通常是172.17.0.1,但你可以用ip addr show docker0确认。

验证方法:

# 启动 app 服务 docker-compose up -d app # 进入容器 docker exec -it laravel-app bash # 测试解析 www@laravel-app:/var/www/html$ getent hosts host.docker.internal 172.17.0.1 host.docker.internal # 测试连通性(假设宿主机 MySQL 在 3306 端口) www@laravel-app:/var/www/html$ telnet host.docker.internal 3306 Trying 172.17.0.1... Connected to 172.17.0.1. Escape character is '^]'. J 8.0.33-0ubuntu0.20.04.2?{... # MySQL 握手协议,证明连接成功

如果telnet失败,90% 的原因是宿主机的ufw防火墙阻止了172.17.0.1的入站连接。解决方案是:

# 在宿主机上允许 docker0 网桥的流量 sudo ufw allow from 172.17.0.0/16 to any port 3306 # 或者更彻底地,禁用 ufw 对 docker0 的限制 echo 'iptables -I INPUT -i docker0 -j ACCEPT' | sudo tee -a /etc/ufw/before.rules sudo ufw reload

3.3 环境变量与 Laravel 的.env:为什么environment块比.env文件更可靠

Laravel 的.env文件是通过vlucas/phpdotenv库加载的,它会读取文件中的KEY=VALUE行,并注入到$_ENVgetenv()。但在容器化环境中,.env文件有两个致命缺陷:

  • 安全性.env文件如果被意外挂载到 Nginx 的root目录下,会被当作静态文件返回,泄露数据库密码;
  • 一致性:不同环境(开发、测试、生产)的.env文件内容不同,容易因git add .env误提交。

docker-compose.ymlenvironment块,是 Docker 提供的环境变量注入协议。它在容器启动时,将变量写入/proc/1/environphp-fpm进程启动时自动继承,getenv('DB_HOST')直接返回host.docker.internal,无需文件 I/O。

更重要的是,environment支持变量插值。例如:

environment: DB_HOST: ${DB_HOST:-host.docker.internal} DB_PORT: ${

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询