- 1. 安装Docker Compose
- 2. Dockerfile编写
- Dockerfile-php
- Dockerfile-nginx
- nginx配置文件
- Dockerfile-mariadb
- Dockerfile-phpmyadmin
- 3. 使用Compose实现多容器运行机制
- 项目结构
- docker-compose.yml
- 构建运行
- 4. 服务测试
- nginx+php测试
- mariadb测试
- php对数据库操作测试
- 5. PhpMyAdmin
- 附录:一键删除命令
- 用时和体会
- 参考
项目已上传至GitHub
1. 安装Docker Compose
参考官方教程
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose # 下载安装
# 这一步不比较慢, 可以去直接github下载好拷贝到/usr/local/bin/docker-compose目录下
sudo chmod +x /usr/local/bin/docker-compose # 应用权限
docker-compose --version # 测试是否安装成功
2. Dockerfile编写
考虑到树莓派无法使用MySql
,选择了Nginx
+MariaDb
+PHP
+Pdo
。
Dockerfile-php
参考官方readme:
FROM php:7.4-fpm
LABEL author=qyanzh
RUN apt-get update && apt-get install -y \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
&& docker-php-ext-install pdo pdo_mysql \ # 新增行,安装pdo
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) gd
5/7更新
如果发现apt-get update
过程很慢, 可以参考docker php-fpm 镜像修改apt源 - 简书
Dockerfile-nginx
只声明了一个端口号,配置文件的挂载放在yml
中进行。
FROM nginx
LABEL author=qyanzh
EXPOSE 2420 # 声明暴露端口号2420
nginx配置文件
由于不熟悉nginx
,在这一步花了很长时间,遇到了很多错误:
以上错误基本都是由于路径配置不正确。
正确的配置:
server {
listen 2420;
server_name localhost;
location / {
root /usr/share/my_web/html; # nginx容器中web文件存放目录,和yml对应
index index.html index.htm;
}
location ~ \.php$ {
root /usr/share/my_web/php; # php容器中web文件存放目录,和yml对应
fastcgi_pass cphp:9000; # php服务器默认端口9000,后面会解释为什么是cphp
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
访问localhost
时,首先nginx
会根据index
指定的顺序依次检查web目录下的对应文件,如果是html文件
,由nginx
直接展示。如果访问到php文件
,nginx
会发送一个请求到cphp:9000
,cphp
将其web目录下对应的php文件
解析为html文件
后返回给nginx
,再由nginx
返回给浏览器,nginx
在这里充当反向代理。(具体的原理和location匹配规则在文末的参考资料给出。)
访问两个页面的响应头:
Dockerfile-mariadb
较简单,直接在docker-compose.yml
中给出。
Dockerfile-phpmyadmin
FROM phpmyadmin/phpmyadmin
LABEL author=qyanzh
EXPOSE 8080
3. 使用Compose实现多容器运行机制
项目结构
docker-compose.yml
参考官方教程以及twang2218/docker-lnmp(强烈推荐看一看):
version: "3"
services:
php:
image: my-php # 镜像名
container_name: cphp # 容器名
build:
context: .
dockerfile: Dockerfile-php # 指定自定义的Dockerfile
environment:
MYSQL_PASSWORD: 1234 # 方便直接在php代码中引用
volumes:
- ./my_web_dir:/usr/share/my_web/php # 挂载到本机web目录,相当于-v
networks:
- front_end
- back_end
nginx:
image: my-nginx
container_name: cngx
build:
context: .
dockerfile: Dockerfile-nginx
ports:
- "80:2420" # 暴露80端口,相当于 -p 80:2420
volumes:
- ./my_web_dir:/usr/share/my_web/html
- ./default.conf:/etc/nginx/conf.d/default.conf # 挂载配置文件
networks:
- front_end
mariadb:
image: mariadb
container_name: cdb
restart: always
volumes:
- mariadb-data:/bitnami/mariadb # 挂载volumn实现数据持久化
environment:
MYSQL_ROOT_PASSWORD: 1234
networks:
- back_end
phpmyadmin:
image: my-phpmyadmin
container_name: cadm
build:
context: .
dockerfile: Dockerfile-phpmyadmin
ports:
- "8080:80" # phpmyadmin默认监听80
environment:
PMA_HOST: cdb # 指定mysql服务所在的host
networks:
- back_end
volumes:
mariadb-data:
# 自定义网络的原因参见下方php访问数据库测试
networks:
front_end:
back_end:
通过volumn
将容器目录挂载本地,在本地的修改可以直接被容器读取,非常方便。:
前面的表示要挂载的本地目录/文件,:
后面表示对应的容器内目录/文件。
构建运行
docker-compose down # 移除之前运行的
docker-compose up -d --build # 这也是个坑点,修改过后要加--build否则用的还是之前的镜像
4. 服务测试
nginx+php测试
mariadb测试
mysql -h localhost -u root -p
然而报错
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
经查是因为mariadb
是运行在cdb
中的,而cdb
作为独立的容器有自己的IP,所以就无法通过宿主机IPlocalhost
来访问,需要先查一下容器IP。
docker inspect cdb
mysql -h 172.20.0.2 -u root -p
php对数据库操作测试
参考菜鸟教程的代码,做一些必要的修改:
由于容器间互相隔离,localhost
需要改掉。根据mariadb测试,应该把$host
的值改为cdb的IP
,但是每次容器重启后这个IP是会变化的,就需要每次去改测试代码。容易想到的方式是给其分配静态IP,但这样太不优雅了。经过查阅资料,发现位于同一网络下的容器可以通过容器名相互访问,为了隔离性,应把不同职责的容器分散在不同网络中。cdb
和cphp
同属于back_end
网络,所以将$host
的值改为容器名cdb
即可。上面的nginx配置文件
中fastcgi_pass
的取值同理。
";
$dbh = null;
} catch (PDOException $e) {
die("Error!: " . $e->getMessage() . "
");
}
//默认这个不是长连接,如果需要数据库长连接,需要最后加一个参数:array(PDO::ATTR_PERSISTENT => true) 变成这样:
$db = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));
$db->exec("create database if not exists test_db;");
$db->exec("use test_db;");
$db->exec("create table if not exists t(num integer);");
$db->exec("delete from t;");
// 增
$db->exec("insert into t(num) values(2420);");
showDb($db, "insert:\n");
// 改
$db->exec("update t set num=031702420 where num=2420;");
showDb($db, "update:\n");
// 删
$db->exec("delete from t;");
showDb($db, "delete:\n");
// 查
function showDb($db, $op)
{
print_r($op);
$sql = "select * from t";
$result = $db->query($sql);
while ($arr = $result->fetch()) {
print_r($arr);
}
echo "
";
}
如果出现Connection Refused
,可能是因为数据库未启动完毕,大约需要10秒。可以将up
指令中的-d
去掉来看容器的状态(或者docker logs
),直到出现:
cdb | Version: '10.4.12-MariaDB-1:10.4.12+maria~bionic' socket: '/var/run/mysqld/mysqld.sock' port: 3306
5. PhpMyAdmin
参考通过Docker为MySQL安装phpMyAdmin管理界面_运维_嘿客的技术闲笔-CSDN博客,具体已在yml
中给出。
第一次没设置PMA_HOST
环境变量,导致无法访问。错误信息也说明是主机名找不到,在yml
中设置为cdb
即可。
附录:一键删除命令
# 删除所有容器
docker rm -f $(docker ps -aq)
# 删除所有镜像
docker rmi $(docker images -q)
# 删除所有镜像
docker rmi $(docker images | grep "none" | awk '{print $3}')
用时和体会
加上博客约14小时。做完这次实践对Docker的理解深了不少,相比前两次蜻蜓点水般的使用,这次更有完整项目的感觉。面对没学过的东西,搜集资料和debug的过程是冗长且痛苦的,但这正是锻炼自学能力的机会。
参考
php - Docker Hub
docker php-fpm 镜像修改apt源 - 简书
Nginx+Php-fpm运行原理详解_运维_一路向前ylc-CSDN博客
Nginx location 匹配顺序整理 - python修行路 - 博客园
Get started with Docker Compose | Docker Documentation
twang2218/docker-lnmp: Docker example of LNMP setup (Compose, Swarm)
windows下如何查看和修改MySQL的端口号
docker-compose 安装 mariadb数据库_运维_jiangbenchu的博客-CSDN博客
PHP 连接 MySQL | 菜鸟教程
PHP PDO | 菜鸟教程
PHP: 用户自定义函数 - Manual
PDO 基本使用(简)_PHP_jia_1418422386的博客-CSDN博客
docker 如何删除none镜像_运维_李留白-CSDN博客
phpmyadmin/phpmyadmin - Docker Hub
使用docker-compose编写常规的lnmp容器,pdo连接mysql失败。 - 大步向前blue - 博客园
通过Docker为MySQL安装phpMyAdmin管理界面_运维_嘿客的技术闲笔-CSDN博客