Docker 重要性不用多做解释,我们天天吵着云原生,最重要的一门技术,你是否掌握了呢?从今天,一起来Docker吧!
1. 目标
1、利用 docker-compose
编排一个PHP开发环境;
2、PHP
常见扩展安装;
3、Nginx
php-fpm
Mysql
Redis
等容器互联;
4、PHP
程序能够操作 Mysql
Redis
;
2、搜集相关日志便于排查;
2. 工具
1、操作系统:Ubuntu
2、编辑器:VSCode
3、Docker
4、Docker Compose 官方文档
5、Compose 中文手册
6、如果Docker镜像拉取慢,可以尝试这个 www.daocloud.io/mirror
3. 步骤
3.1 检查软件
确保 Docker 和 Docker Compose 已安装好
比如
docker -v Docker version 19.03.1, build 74b1e89
docker-compose -v docker-compose version 1.17.1, build 6d101fb
3.2 创建 docker-compose.yml
文件
在你的文件夹中新增这个文件,这是我们编排的模板文件,格式如下
version: '3'
services:
php-workspace:
image: php:7.3-fpm-alpine
nginx:
image: nginx:alpine
复制代码
注意点:
1、模板中定义的 version 字段声明了模板的格式和支持声明字段,3 是目前比较推荐也用的比较多的的版本
2、注意 key 的层级和空格,我们可以使用命令 docker-compose config
命令检查格式是否正确
3、使用 VSCode
方便的一点是可以看到可以代码可以根据层级折叠
4、alpine是一种小型轻量的Linux,我们使用基于 alpine 的镜像是因为做出的镜像体积比较小,关于后期容器构建的优化,这个是另外一个话题,这里不展开阐述。
3.3 “让子弹飞起来”
docker-compose up --build
构建镜像并启动容器
这时能看到有两个已经在运行的容器和镜像
3.4 让 Nginx 工作起来
刚刚启动的容器可以使用 docker-compose down
来关闭项目,还会自动删除容器。
现在,我们要指定端口让 Nginx 能处理请求,可以参考 Compose 中文手册的模板文件的语法。
3.4.1 增加 ports 参数
指定宿主机端口和容器端口的映射关系,如下;
version: '3'
services:
php-workspace:
image: php:7.3-fpm-alpine
nginx:
image: nginx:alpine
ports:
- "8080:8080"
复制代码
3.4.2 执行 docker-compose up
去浏览器请求 localhost:8080
3.5 PHP文件交给 PHP-FPM 处理
其实选中容器右键能管理容器,
Attach Shell
就能快速进入容器,我们进入容器就能看到内部情况,比如,nginx 的www目录和配置目录,php-fpm 的默认配置等等。
3.5.1 增加 nginx 的站点配置
新增文件 nginx/conf.d/site.conf
文件,内容如下,下面的思路也可以参考上篇《利用Docker搭建PHP开发环境》做法。
server {
listen 8080;
root /usr/share/nginx/html;
index index.php index.html;
server_name localhost;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
location ~ \.php$ {
root /var/www/html; # 这里指向的php容器的项目根目录
fastcgi_pass php-workspace:9000; #php-workspace是模板文件中的php-fpm服务名
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
复制代码
3.5.2 修改 docker-compose.yml
文件
让 nginx 和 php-fpm 服务基于Dockerfile 构建
version: '3'
services:
php-workspace:
build: ./php # 这里php指我们的创建的目录
nginx:
build: ./nginx # 这里nginx指我们的创建的目录
ports:
- "8080:8080"
复制代码
3.5.3 新增 nginx/Dockerfile
文件
FROM nginx:alpine
COPY conf.d /etc/nginx/conf.d
复制代码
3.5.4 新增 php/Dockerfile
文件
FROM php:7.3-fpm-alpine
复制代码
3.5.5 增加code文件夹作为php项目,新增index.php文件
<?php
phpinfo();
复制代码
3.5.6 重新修改docker-compose.yml 文件
让项目文件夹以挂载的形式,让php-fpm容器可以直接读取宿主机文件目录,这样我们可以很方便的修改代码调试
version: '3'
services:
php-workspace:
build: ./php
volumes: # 这里指定数据卷,可以指定多个, 中划线代表值为数组的一个成员
- ./code:/var/www/htmll
nginx:
build: ./nginx #这里nginx指我们的创建的目录
ports:
- "8080:8080"
复制代码
3.5.7 走一下
重启之前,再确定下代码文件大概位置
docker-compose up --build
重新启动项目,我们看到在前台控制器的一些日志
两个容器都构建好了,php也启动了。如果我们现在请求
localhost:8080
,还能看到nginx的请求日志3.6 连接Mysql
用法我们依然参考了镜像的介绍 >> hub.docker.com/_/mysql
3.6.1 添加 db 的服务
version: '3'
services:
php-workspace:
build: ./php
volumes:
- ./code:/var/www/html
nginx:
build: ./nginx
ports:
- "8080:8080"
db:
image: mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment: # 设定环境变量,只给定名称的变量会自动获取运行 Compose 主机上对应变量的值
MYSQL_ROOT_PASSWORD: root
复制代码
3.6.2 重新构建
docker-compose up --build
在
VSCode
容器管理中右键连接shell中进入容器执行 mysql -u root -p
,确认密码 root
,确定我们的数据库可用3.6.3 安装 pdo mysql
在操作之前我们确定下pdo是不支持mysql驱动的
修改 php 的
Dockerfile
文件如下RUN 的第一条命令是修改了 alpine 系统的软件源,方便后续安装会更快点
FROM php:7.3-fpm-alpine
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories \
&& docker-php-ext-install pdo_mysql
复制代码
重新构建项目 docker-compose up --build
3.6.4 修改 index.php 文件,代码如下
<?php
$dsn = 'mysql:host=db;port=3306'; // host=db,这里的db其实是mysql的服务名
$user = 'root';
$password = 'root';
try {
$dbh = new PDO($dsn, $user, $password);
print_r($dbh);
} catch (PDOException $e) {
echo 'Connection failed: '.$e->getMessage();
}
复制代码
3.6.5 测试 Mysql
3.7 连接 Redis
3.7.1 容器安装PHP扩展的两种方式
在Docker Hub 中 php镜像文档中说的很清楚
- 第一种是PHP核心的扩展可以通过 docker-php-ext-install ext_name 安装
- 第二种是PECL
- 第三种通过源码包编译
3.7.2 修改 docker-compose.yml 文件,增加redis服务
version: '3'
services:
php-workspace:
build: ./php
volumes:
- ./code:/var/www/html
nginx:
build: ./nginx
ports:
- "8080:8080"
db:
image: mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
MYSQL_ROOT_PASSWORD: root
redis:
image: redis:5.0.5-alpine
复制代码
3.7.3 重新构建
docker-compose up --build
构建后,我们同样可以进入容器,使用 redis-cli 命令确定redis是否可用
3.7.4 修改 php Dockerfile
因为pecl在alpine容器中安装扩展会少一些东西,所以我们先安装了一个phpize_deps,用后删除即可,保持镜像瘦小
FROM php:7.3-fpm-alpine
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories \
&& docker-php-ext-install pdo_mysql \
&& apk add ${PHPIZE_DEPS} \
&& pecl install redis-4.0.1 \
&& docker-php-ext-enable redis \
&& apk del ${PHPIZE_DEPS}
复制代码
重新构建,我们修改index.php 代码 phpinfo();能看到 redis扩展已打开
3.7.5 连接 Redis
修改 index.php 文件内容如下测试
<?php
$redis = new Redis();
$redis->pconnect('redis', 6379);
$key = 'first';
$redis->incr($key);
echo '页面浏览了:'.$redis->get($key); die;
复制代码
3.8 日志搜集
我们回顾一下 nginx/conf.d/site.conf
的配置
server {
listen 8080;
root /usr/share/nginx/html;
index index.php index.html;
server_name localhost;
# 我们已经把nginx相关日志写到这里了,如何放到宿主机呢?
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
# ... more something
}
复制代码
docker日志采集固然有很多方案,但是暂时不考虑集群管理工具的日志搜集方案,这里以数据挂载的形式,把 nginx 请求日志落地到宿主机。
3.8.1 修改 docker-compose.yml
文件如下
version: '3'
services:
php-workspace:
build: ./php
volumes:
- ./code:/var/www/html
nginx:
build: ./nginx
volumes: # 新增这两行
- ./log/nginx:/var/log/nginx/
ports:
- "8080:8080"
db:
image: mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
MYSQL_ROOT_PASSWORD: root
redis:
image: redis:5.0.5-alpine
复制代码
3.8.2 重启项目
docker-compose up
执行后,就可以看到我们的文件夹多了两个目录,还有两个文件
3.9 优化
depends_on
可以指定依赖项,解决容器启动先后问题,比如一个应用如果依赖数据库,要让数据库容器先启动可以设置
depends_on:
- db
- redis
复制代码
环境变量
当然其实还有其他很多有用的参数设置,比如感敏数据读取,环境变量,配置host等等,后续都可以在开发中尝试使用。
4. 总结
以上思路可以解决本地开发中的很多问题了,如果项目中有使用 memcached 或者其他应用,做法都可以参考Docker Hub中的镜像文档说明中引入。
除此之外,在Laravel 开发中之前早就有一个开源项目叫做 Laradock
来搭建Docker化的PHP应用,不仅如此,还有很多中间件都可以利用 docker-compose 启动,可以参看 >> laradock.io/ ,但是里边有很多很多东西,因为要满足大部分人的扩展性,加了很多环境变量,if else 判断,让原本的 Dockerfile 更为复杂,增加了很多逻辑。但是思路基本上是相同的。
另外还有几点要点出:
1、docker-compose.yml 定义的一个项目会以所在的文件夹名为项目名。
2、一个项目有一个默认网络,项目中的服务彼此可以互通,所以我们刚刚在 site.conf 转发php请求可以直接用 php-workspace:9000 服务名来转发,连接 数据库也是可以用 db redis 等他们的服务名来连接。
5. 完整代码
6. 遗留问题
1、指定数据库挂载的数据卷,比如Mysql,我们可以把其他环境的数据库直接以文件的形式拷贝,然后直接利用。这样比我们手动导入sql会更快迁移数据。
2、Mysql 镜像如何瘦身,现在Mysql镜像文件最大,如图有445M
7. 下篇预告
其实PHP的Docker环境已经有一个开源项目 Laradock,使用方便 易于扩展,但是最大的问题是 感觉臃肿 有时候安装特别慢,特别是要考虑太多扩展插件等等制作出来镜像特别大。所以下一篇的思路可能会围绕如何优化今天做的这个 demo-docker-compose,或者做 Laradock 本地化工作?
其他两个思路:
1、直接进入到 k8s 课题
2、Docker化后如何做CI/CD