第一部分阐述如何通过 Docker 在本地运行 Laravel 应用。第二部分将完整地展示如何在生产中运行同样的应用。
本文的目标是创建一个可以复用的开发环境。快速,并且不依赖本地计算机的任何全局安装(除了 Docker 本身)。
所以,我们来实现以下目标:
- 没有 Mamp或类似的程序
- 没有 Vagrant或类似的VM设置
- 没有 全局安装的PHP
- 没有 全局安装的Composer
获取最新的Laravel
使用 curl 来从 Github 获取最新的 Laravel 版本,但是也可以以你喜欢的方式轻松获取源码——可以使用 git clone
,如果这样做的话,别忘了删除 .git
目录。
我们没有遵循设置 Laravel 的官方指南,因为我们不希望在我们的开发机器上全局安装 PHP/Composer 的麻烦
curl -L https://github.com/laravel/laravel/archive/v5.6.12.tar.gz | tar xz
这将创建一个名为的目录 laravel-5.6.12
——您应该将其重命名为您想要的项目。例如 mv laravel-5.6.12 my-site
然后 cd
进入它。
安装依赖
我们需要运行 composer install
来获取组成 Laravel 的所有库——我们可以从 docker hub 获取composer/composer
镜像来实现。
我们通过以下命令来创建一个用完即弃的容器:
docker run --rm -v $(pwd):/app composer/composer install
注意:
- 我们使用
-rm
标识来确保安装后不会停留 -
-v $(pwd):/app
将宿主机上的当前目录挂载到容器的/app
目录——这是运行在容器内的 composer 期望找到的composer.json
位置。 -
-v $(pwd):/app
也确保了由 composer 在容器内部创建的文件夹在宿主机也能看得见。
创建docker-compose.yml
我们使用两个单独的文件来定义环境如何运行。一个用于开发,另一个用于生产。现在 docker-compose 支持使用多个输入文件(input files),允许覆盖特定的键——但是由于它合并数组的方式,并不适合我们的特定用例,我们只能在两个文件中忍受一些重复。
无论如何,我们都需要创建文件 docker-compose.yml
。它的开头是这样的:
version: '3'
services:
... our services will go here
PHP-FPM
这将处理在应用中的执行代码,我们使用此服务来执行任意的 php 脚本,例如运行 Laravel 附带的 CLI 工具 artisan
。
version: '3'
services:
# The Application
app:
build:
context: ./
dockerfile: app.dockerfile
working_dir: /var/www
volumes:
- ./:/var/www
environment:
- "DB_PORT=3306"
- "DB_HOST=database"
注意:
- 我们将使用单独的
app.dockerfile
来构建我们的镜像,因为我们想要精确控制 PHP 正在使用的模块。 - 我们将工作目录设置为
/var/www
—— 应用程序代码将在容器内的位置。 - 我们使用单个卷定义
./:/var/www
将主机上当前目录中的所有内容挂载到/var/www
容器中。这将允许我们对源代码进行更改,并将它们立即反映在正在运行的应用程序中。这将使您的应用程序在浏览器中感觉迟钝 - 几百毫秒滞后(特别是在OSX上),但不要担心——在第2部分,当我们转换到生产设置时,速度问题将不再存在。 - 环境变量
DB_PORT
和DB_HOST
在此处设置相匹配了,我们稍后会创建数据库容器。
现在我们需要创建 app.dockerfile
我们在 build
上面的设置中引用的内容。
app.dockerfile
FROM php:7.2-fpm
RUN apt-get update && apt-get install -y libmcrypt-dev \
mysql-client libmagickwand-dev --no-install-recommends \
&& pecl install imagick \
&& docker-php-ext-enable imagick \
&& docker-php-ext-install mcrypt pdo_mysql
注意:
- 这里使用
php:7.2-fpm
,当然,也可以选择其他版本。 - 接下来是典型的 Laravel CRUD 应用所需的基本知识
Nginx
接下来,我们需要配置处理静态文件的 Web 服务器,以及需要由 Laravel 应用处理的请求传递。我们将遵循与之前相同的模式,这次命名服务 web
及其随附文件 web.dockerfile
。
docker-compose.yml
# The Web Server
web:
build:
context: ./
dockerfile: web.dockerfile
working_dir: /var/www
volumes_from:
- app
ports:
- 8080:80
注意:
- 我们
volumes_from
在这里使用它来重用我们在上面的 PHP-FPM 服务中定义的内容。这意味着这个 Nginx 容器将继承该/var/www
目录(该目录又安装到我们的开发机器上)。 - 我们
8080
将主机上的端口映射到80容器中。这样我们就可以0.0.0.0:8080
在开发过程中访问,而不需要乱用主机名。
web.dockerfile
FROM nginx:1.15
ADD -v nginx.conf:/etc/nginx/nginx.conf
nginx.conf
server {
listen 80;
index index.php index.html;
root /var/www/public;
location / {
try_files $uri /index.php?$args;
}
location ~ \.php$ {
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;
}
}
注意:
- 第12行处理请求
app:9000
,这是有效的,因为 docker-compose 会自动链接我们的服务,允许它们通过简单的主机相互talk
。 - 其余的只是非常基本的nginx配置 - 它没有以任何方式调整性能或安全——我们将在另一篇文章中处理这些!
MySQL
接下来,我们将配置数据库,但我们需要处理这个与以前的服务略有不同。使用 PHP-FPM 和 Nginx ,我们希望可以在容器内访问本地目录中的文件,以帮助加快开发过程。但是数据库不是这种情况相反,我们希望容器中创建的文件能够持久存在,从而允许我们在不丢失数据的情况下停止并重新启动服务。这也可以通过卷来实现,只是这次不需要它与我们的主机文件同步。
docker-compose.yml
version: '3'
services:
database:
image: mysql:5.6
volumes:
- dbdata:/var/lib/mysql
environment:
- "MYSQL_DATABASE=homestead"
- "MYSQL_USER=homestead"
- "MYSQL_PASSWORD=secret"
- "MYSQL_ROOT_PASSWORD=secret"
ports:
- "33061:3306"
volumes:
dbdata:
注意:
- 第16行创建了一个卷
dbdata
,最后的冒号:
是故意的,不需要担心。 - 第7行引用了该卷,也就是说从
dbdata
卷加载/var/lib/mysql
目录。 - 第9~12行设置了MySQL的环境
- 第13行,我们
33061
在主机上创建了一个添加端口映射到3306
容器内的常规。这样做只是为了让外部工具在开发过程中更容易访问数据库——在生产设置中不需要它。
docker-compose.yml
version: '3'
services:
# The Application
app:
build:
context: ./
dockerfile: app.dockerfile
working_dir: /var/www
volumes:
- ./:/var/www
environment:
- "DB_PORT=3306"
- "DB_HOST=database"
# The Web Server
web:
build:
context: ./
dockerfile: web.dockerfile
working_dir: /var/www
volumes_from:
- app
ports:
- 8080:80
# The Database
database:
image: mysql:5.6
volumes:
- dbdata:/var/lib/mysql
environment:
- "MYSQL_DATABASE=homestead"
- "MYSQL_USER=homestead"
- "MYSQL_PASSWORD=secret"
- "MYSQL_ROOT_PASSWORD=secret"
ports:
- "33061:3306"
volumes:
dbdata:
启动服务
如果跟着我们的步骤做下来,会有如下文件:
- docker-compose.yml
- app.dockerfile
- web.dockerfile
- nginx.conf
一旦完成所有这些操作,您就可以继续执行以下命令,该命令将启动所有 3 个服务。
docker-compose up
准备Laravel应用程序
环境配置文件
cp .env.example .env
应用密钥和优化
接下来,我们需要设置应用程序密钥并运行optimize命令。两者都由 artisan
处理,但因为我们有 PHP 和整个 Laravel 应用程序在容器内运行,我们不能像往常一样在我们的本地机器上运行 php artisan key:generate
——我们需要直接将这些命令发送到容器中。
幸运的是,docker-compose 有一个非常好的抽象来处理这个,所需的两个命令看起来像:
docker-compose exec app php artisan key:generate
docker-compose exec app php artisan optimize

你需要在任何时候使用这种模式 artisan
—— 记住使用 docker 的目的是避免在本地机器上安装 PHP 版本的麻烦,这就是我们如何解决它 - 通过将命令发送到容器中,而不是直接运行它们。
您将在Laravel项目中经常运行的其他一些命令:
docker-compose exec app php artisan migrate --seed
docker-compose exec app php artisan make:controller MyController
提示:创建一个别名,这样
phpd
就不需要输入完整的命令,例如:phpd artisan migrate --seed
一旦你执行了前面提到的两个命令(artisan key:generate
&artisan optimize
),应用程序现在就可以使用了 - 继续在浏览器中点击http://0.0.0.0:8080,你就会看到这个可爱的屏幕。

部署成功!
修改设置到生产环境
目前已经有一些博客涉及 Laravel 这样的开发环境,但到目前为止我还没有发现如何完成下一个重要步骤 —— 采用这种环境并为生产使用做好准备。我期待很快分享下一篇文章。
资源:
- Git repo
- Git commit 显示在这篇文章中创建的文件
喜欢这个?如果你这样做,并且你发现自己在做任何前端工作,也许你会喜欢我在 https://egghead.io/instructors/shane-osbourn 上的一些课程,很多都是免费的,我会报道 Vanilla JS,Typescript ,RxJS 等。
网友评论