Laradock DNMP 环境搭建

Laradock 是什么?

Laradock.io 是一堆 Dockfiledocker-compose.yml 的文件集合,用于一键构建 DNMP 环境。

Laradock 封装了一个 Workspace 工作区镜像作为开发环境,里面包含了丰富且实用的工具集:PHP-CLI、Composer、Git、Linuxbrew、Node、V8JS、Gulp、SQLite、xDebug、Envoy、Deployer、Vim、Yarn、SOAP、Drush 等等。

CentOS 中 Laradock 的使用

1. 安装 Docker

http://www.runoob.com/docker/centos-docker-install.html

2. 安装 Docker-Compose

# 注意 URL 里的版本号
curl -L https://github.com/docker/compose/releases/download/1.23.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

3. 下载 Laradock

A. 单应用
cd /home/wwwroot/ai_gmall_server
git submodule add https://github.com/Laradock/laradock.git

目录结构:

- ai_gmall_server
    - laradock
    - app
    - public
    - ...
B. 多应用
cd /home/wwwroot/
git clone https://github.com/Laradock/laradock.git

目录结构:

- wwwroot
    - laradock
    - ai_gmall_server
    - ai_stadium_server

注:多应用时,需要配置 nginx vhost 才能访问到,配置文件在 laradock/nginx/sites/*.conf

4. Laradock 环境配置

初始化 laradock/.env 配置:

cd laradock
cp env-example .env
vim .env

注意修改其中的以下关键选项:

# 应用持久化数据保存目录(映射在宿主机的数据卷)
# 用于保存 MySQL 产生的数据、Redis 快照文件等
DATA_PATH_HOST=~/.laradock/ai_gmall/data

# 定义容器前缀
# 当一台宿主机有多个 laradock 单应用时,需要设置前缀进行区分
COMPOSE_PROJECT_NAME=laradock_ai_gmall

# 端口修改(按需)
NGINX_HOST_HTTP_PORT=1322
NGINX_HOST_HTTPS_PORT=13443

# 关闭 PHPRedis 扩展,我们用 Predis
WORKSPACE_INSTALL_PHPREDIS=false
PHP_FPM_INSTALL_PHPREDIS=false

# MySQL 版本
# 注意默认 latest 为 8.0,这里改为 5.7
MYSQL_VERSION=5.7
MYSQL_DATABASE=ai_gmall
MYSQL_USER=gmall
MYSQL_PASSWORD=gmall.1322
MYSQL_PORT=3306
MYSQL_ROOT_PASSWORD=gmall.1322

# 设置中国镜像源
CHANGE_SOURCE=true
WORKSPACE_NPM_REGISTRY=https://registry.npm.taobao.org
WORKSPACE_COMPOSER_REPO_PACKAGIST=https://packagist.phpcomposer.com

# 设置中国时区
WORKSPACE_TIMEZONE=Asia/Shanghai

# 安装 NodeJS
WORKSPACE_INSTALL_NODE=true

# 启用 PHP OPCache
PHP_FPM_INSTALL_OPCACHE=true

以下可选配置:

# 安装 Swoole
WORKSPACE_INSTALL_SWOOLE=true
PHP_FPM_INSTALL_SWOOLE=true

# phpMyAdmin
PMA_USER=default
PMA_PASSWORD=secret
PMA_ROOT_PASSWORD=secret
PMA_PORT=8080

# phpRedisAdmin
REDIS_WEBUI_USERNAME=laradock
REDIS_WEBUI_PASSWORD=laradock
REDIS_WEBUI_CONNECT_HOST=redis
REDIS_WEBUI_CONNECT_PORT=6379
REDIS_WEBUI_PORT=9987

5. Nginx 虚机配置

打开 laradock/nginx/sites/default.conf,修改 server_name 为实际域名,去除 default_server 指定。

如需启用 HTTPS,则把证书放到 laradock/nginx/ssl/ 目录里,并在对应的配置中引入。

如果一套环境包含多应用,那么还需要给 Nginx 增加 vhost 配置:

# 里面可以添加 server 段
vim laradock/nginx/sites/new-app.conf

# 修改完配置必须重启 Nginx
docker-compose restart nginx

6. PHP-FPM 设置

(1) 修改 laradock/php-fpm/phpX.X.ini (X.X 为 PHP 版本号) 设置 disable_functions 等参数。

(2) 修改 laradock/php-fpm/laravel.ini 设置 memory_limit 等参数。

(3) 修改 laradock/php-fpm/opcache.ini 确保参数按如下设置:

opcache.enable="1"
opcache.memory_consumption="256"
opcache.use_cwd="1"
opcache.max_file_size="0"
opcache.max_accelerated_files = 30000
opcache.validate_timestamps="0"
opcache.save_comments=0
opcache.revalidate_freq="0"

强烈建议:生产环境 validate_timestamps=0,每次 PHP 文件变更都应平滑重启 PHP-FPM。

# 平滑重启
docker-compose exec php-fpm bash -c "kill -USR2 1"

# 强制重启(不推荐)
docker-compose restart php-fpm

7. Laravel 应用内配置

修改项目里的 ai_gmall_server/.env 文件,将其中的 DB_HOSTREDIS_HOST 改为以下值:

DB_HOST=mysql
REDIS_HOST=redis
QUEUE_HOST=beanstalkd

然后进行应用初始化(必做步骤):

cd /home/wwwroot/ai_gmall_server
chmod -R 777 storage bootstrap/cache

cd laradock
docker-compose exec [--user=laradock] workspace \
    bash -c "composer install --optimize-autoloader"

7. Redis 配置

配置文件 laradock/redis/redis.conf,修改以下项并重启 Redis

# 设置密码
requirepass 密码

# 绑定 IP
# 也可以注释掉
bind 0.0.0.0

# 修改持久化频率
save 900 1
save 300 10
save 60 50000

重启 Redis 的命令是:

docker-compose restart redis

8. MySQL 配置

配置文件 laradock/mysql/my.cnf,增加或修改以下项并重启 MySQL

# @see http://dev.mysql.com/doc/mysql/en/server-system-variables.html

[client]
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4
max_allowed_packet = 256M

[mysqld]
init_connect = 'SET NAMES utf8mb4'
group_concat_max_len = 102400
character-set-server = utf8mb4
sql-mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"

重启 MySQL 的命令是:

docker-compose restart mysql

在目录 laradock/mysql/docker-entrypoint-initdb.d/ 里的 *.sql 文件,会在MySQL 容器初次创建时自动执行。 我们可以把应用初始化所需的 SQL 放在里面。

容器初次创建:指 $DATA_PATH_HOST/data/mysql 文件夹第一次创建时,如果该文件夹已存在,则不会执行。

如果就是需要手动执行一些 SQL,那么可以进入 MySQL 容器:

docker-compose exec mysql bash
mysql -u root -p < /xxx.sql

MySQL 默认安装的版本是 latest,即 8.0,如想更换,可以修改 laradock/.env 中的 MYSQL_VERSION 常量,例如改为 MYSQL_VERSION=5.7

更换 MySQL 版本的坑

如果之前已装了 latest 版本,现在换成 5.7,那么在重新构建新的 MySQL 镜像前,必须先清空旧的数据卷:$DATA_PATH_HOST/data/mysql,否则新的 MySQL 会启动失败。

或者一并删除旧容器及其数据卷,命令如下:

# 把旧容器和数据卷一并删除
rm -rf ~/.laradock/ai_gmall/data/mysql

# 修改 `MYSQL_VERSION` 常量
cd laradock
vim .env

# 构建新的镜像
docker-compose build --no-cache mysql

# 启动新的容器
docker-compose up --no-deps mysql

9. Laravel WebSocket Echo Server

修改 laradock/laravel-echo-server/laravel-echo-server.json 文件:

{
    "authHost": "localhost",
    "authEndpoint": "/broadcasting/auth",
    "clients": [],
    "database": "redis",
    "databaseConfig": {
        "redis": {
            "port": "6379",
            "host": "redis"
        }
    },
    "devMode": true,
    "host": null,
    "port": "6001",
    "protocol": "http",
    "socketio": {},
    "sslCertPath": "",
    "sslKeyPath": ""
}

每次修改完都需重启该容器:

docker-compose restart laravel-echo-server

另外,容器版的 laravel-echo-server 无需 Supervisord 来守护,因为:

  1. 容器的主进程只要发生中断,容器也会自动终止。
  2. docker-compose.ymllaravel-echo-server 的『退出后的重启策略』是始终重启:restart: always

以上就可以确保 laravel-echo-server 服务会一直保持,达到了和 Supervisord 一样的目的。

10. Supervisord + Laravel Horizon

Laradock 并没有单独的 Supervisord 容器,而是顺便夹带在 laravel-horizonphp-worker 两个容器里,这样会导致无法监听其他进程(例如 PHP iNotify),这并不是一个好的设计。解决办法是把 Supervisord 以及待监听的进程都装到 Workspace 容器里。

在目录 laradock/workspace/supervisor.d/ 里新建 *.conf 文件,格式如下:

[program:AI_GMall_Server_Horizon]
process_name=%(program_name)s_%(process_num)02d
autostart=true
autorestart=true
redirect_stderr=true
command=php /var/www/artisan horizon
stdout_logfile=/var/www/storage/logs/supervisord_horizon.out

重启 Supervisord 的命令是:

docker-compose exec workspace bash -c 'supervisorctl reload'

重启 Laravel Horizon 的命令是:

docker-compose exec workspace bash -c 'php artisan horizon:terminate'

11. 计划任务

Laradock 的计划任务有两种实现方式:

A. 通过系统 Crond 实现

打开文件 laradock/workspace/crontab/laradock,确保里面每条命令的 执行者 和项目文件的拥有者一样,否则会导致无权执行:

# 以下执行者为 root 用户
* * * * * root /usr/bin/php /var/www/artisan schedule:run >> /dev/null 2>&1

原理:在 Workspace 容器创建时,会把 laradock/workspace/crontab/ 里的所有文件(文件名随意)都复制到容器的 /etc/cron.d/ 目录里(注:Linux 系统里,/etc/cron.d//etc/crontab 的扩展目录,都是系统级计划任务),系统会每分钟都会扫描这些文件并按策略执行。

扩展阅读:Linux Crontab 计划任务拾遗

B. 通过 Supervisord 实现

新增文件 laradock/workspace/supervisord.d/laravel-scheduler.conf

[program:laravel-scheduler]
process_name=%(program_name)s_%(process_num)02d
command=/bin/sh -c "while [ true ]; do (php /var/www/artisan schedule:run --verbose --no-interaction &); sleep 60; done"
autostart=true
autorestart=true
numprocs=1
user=laradock
redirect_stderr=true

疑问待验证:这种单进程阻塞的写法,假设一个任务执行超过10分钟,那么期间本该执行的其他任务是否会被遗漏?

99. 正式启动 Laradock

docker-compose up -d \
    nginx \
    mysql phpmyadmin \
    redis redis-webui \
    laravel-echo-server

注:php-fpmnginxdepends_on 依赖项,Workspacephp-fpm 的依赖项,所以这两个容器会自动创建并启动,不需要写出来。

100. 应用初始化

在项目目录先执行:

cd /home/wwwroot/ai_gmall_server
chmod -R 777 storage bootstrap/cache

cd laradock
docker-compose exec workspace \
    bash -c "composer install --optimize-autoloader"

经验:如果访问出现 500 错误,那大多数情况是没有执行 composer install 导致。

相关访问 URL 入口:

  • WebServer: http://localhost:80
  • WebSocketServer http://localhost:6001
  • phpMyAdmin: http://localhost:8080
  • phpRedisAdmin: http://localhost:9987

日常使用手册

1. 执行 Compose 命令的位置

执行 docker-compose 命令默认会在当前目录查找 docker-compose.yml,如果找不到就会报执行失败,否则请用 -f 参数指定位置,例如:

docker-compose -f docker-compose-XXXX.yml up -d ...

2. 如何执行 php artisan 命令?

先进入 Workspace 工作区容器:

docker-compose exec [--user=laradock] workspace bash

缺省是以 root 身份进入容器,用 --user=laradock 可以指定为其他用户。要求项目工作区文件的拥有者必须与进入者身份一致,否则会导致一些文件读写权限的问题。

进入 Workspace 容器后,就可自由运行 php artisan 命令了。

3. 如何更新 PHP 代码?

应用代码仍然是放在宿主机的 /home/wwroot 目录里,是以数据卷形式挂载在容器里的,所以对文件的操作还是和之前一样。

例如应用初始化时,我们可以直接在宿主机的 /home/wwwroot/ai_gmall_server 目录里执行:

chmod -R 777 storage bootstrap/cache

更新脚本 ~/sh/update_ai_gmall_server.sh 如下:

#! /bin/bash
CODE_DIR=/home/wwwroot/ai_gmall_server

# 更新代码
cd $CODE_DIR
git checkout .
git pull origin
git submodule update

\cp envs/all_in_one/.env .env

cd laradock
docker-compose exec workspace \
    bash -c "composer install --optimize-autoloader && php artisan horizon:terminate"

# 平滑重启 PHP-FPM
docker-compose exec php-fpm bash -c "kill -USR2 1"

4. 什么时候必须重新构建镜像?

如果修改了 laradock/docker-compose.ymllaradock/.env 或相关组件的 Dockerfile 文件,那么就需要重建指定的镜像和容器使之生效。

假设我们修改了 laradock/workspace/Dockerfile,然后重建并重启:

docker-compose build workspace
docker-compose up --no-deps --force-recreate -d workspace

或者:
docker-compose up --build --no-deps --force-recreate -d workspace

注意:重建会一并重建 depends_on 依赖镜像,加上参数 --no-deps 表示只需重建指定镜像,不要牵连依赖镜像。如果重建时想忽略缓存,也可以加上 --no-cache 参数。

此时切忌用 docker-compose down,因为 down停止并删除所有 up 启动的容器,即使这些容器没有修改过。

5. 如何安装更多 PHP 扩展?

安装 PHP 扩展需要同时修改 PHP-FPMPHP-CLI 两个容器,并重建镜像:

  • PHP-FPM : php-fpm/Dockerfile-XX (XX 是 PHP 版本号)
  • PHP-CLI : workspace/Dockerfile

6. 生产环境的注意事项

生产环境应该用另一份 docker-compose-prod.yml,移除 docker-compose.yml 中的相关 ports 选项,保证的 MySQL/Redis 端口不暴露在公网。

查看文档:多个 docker-compose.yml 文件共享配置

参考资料