1. 安装 Docker、使用 PHP 官方镜像运行 PHP 程序;

安装 Docker

# rpm 包安装
cd /usr/local/src
wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.03.0.ce-1.el7.centos.x86_64.rpm
yum install docker-ce-17.03.0.ce-1.el7.centos.x86_64.rpm

Docker 用户组设置、加速器设置:

使用 PHP 官方镜像运行 PHP 程序

# 拉取最新版本 PHP 镜像
# 每个镜像都是一个操作系统,每个操作系统可以根据自己的需求定制和修改
docker pull php

# 拉取特定版本的 PHP 镜像(版本参考上方“版本信息”连接)
# alpine 为精简版本
docker pull php:7.4.5-cli-alpine3.10

# *删除镜像
docker rmi php:latest

# 交互式模式启动容器,最后可加上起始命令
# php -m 显示容器内 PHP 安装的扩展
docker run -it --name myphp php:7.4.5-cli-alpine3.10 php -m

# 由于是交互式方式,容器没有启动出来
docker ps -a
# 删除容器
docker rm myphp
# *删除所有容器
docker rm $(docker ps -a)

# 把一个 PHP 程序放到容器里去运行
# 首先创建 PHP 程序
cd /data/php
echo "<?php echo 'test docker';" > test.php

# 启动容器,命名为 runphp,把 /data/php 挂载到容器里
# 加入 --rm,表示程序运行好,容器自动删除,适用于容器运行临时任务
docker run -it --name runphp --rm -v /data/php:/php php:7.4.5-cli-alpine3.10 \
php /php/test.php
# 输出 test docker

2. Docker 多容器运行 PHP + fpm + Apache;

一些知识点

  • 为什么容器不能 docker run -d --name myphp php:xxx 后台运行?运行后 docker ps 后什么都没有,其实没有出错,通过 docker logs myphp 查询什么错误日志都没有
  • 在 alpine 容器或者镜像里面的设置,它的后台把 PHP 进程作为 id 为 1 的进程,一旦 PHP 进程结束,容器就会停止。和官方的 Redis 镜像一样,把 Redis 服务作为 id 为 1 的进程。一旦 Redis 停止,容器就消失了
# 交互式启动,不跟任何命令
docker run -it --name myphp php:7.4.5-cli-alpine3.10
# 会进入 shell 窗口,就是一个 PHP 进程,作为 id 为 1 的元老进程
# 此时可以做一些操作,比如 echo 一些字符串,phpinfo() 等

PHP + fpm + Apache 联合运行

## 2.1 拉取 fpm 镜像(尽可能的使用基于 alpine 的镜像)
docker pull php:7.4.5-fpm-alpine3.10
# 由于 fpm 是服务,可以后台运行
docker run -d --rm --name fpm php:7.4.5-fpm-alpine3.10

## 2.2 单单 fpm 没有用,还需要配置服务器,比如 Apache
# 参考:https://hub.docker.com/_/httpd
docker pull httpd:2.4.43-alpine

# 写个 html 文件,挂载到容器中
echo "<html>hello world</html>" > index.html
# 把容器的 80 端口映射到宿主机的 8080
docker run -d -p 8080:80 --rm --name myweb -v \
/data/php/:/usr/local/apache2/htdocs/ httpd:2.4.43-alpine
# 访问
curl localhost:8080
# 返回:<html>hello world</html>

## 2.3 联合运行操作
# 2.3.1 首先需要修改配置文件
# 查看容器中是否有配置文件
docker exec -it myweb cat /usr/local/apache2/conf/httpd.conf
# 创建本地存放配置文件的目录
mkdir -p /data/php/conf/
cd /data/php/conf/
# 把容器里的配置文件拷贝到本地
docker cp myweb:/usr/local/apache2/conf/httpd.conf /data/php/conf/
# 修改配置文件
vim httpd.conf

# 做以下修改
# 把下面四行的 # 去掉
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

# 删除以下内容
DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
...
</Directory>

# 添加以下内容后,保存退出
<VirtualHost *:80>
    ServerAdmin admin@xx.cn
    DocumentRoot "/usr/local/apache2/htdocs"
    ServerName localhost
    <Directory "/usr/local/apache2/htdocs">
     Options None
     Require all granted
    </Directory>
    ProxyRequests Off
    ProxyPassMatch ^/(.*\.php)$ fcgi://172.17.0.4:9000/php/$1
</VirtualHost>
# 添加结束
# ProxyPassMatch 的 ip 为 172.17.0.4,下面会有说明
# /php:这是一个文件夹名,可以自己设定,在 fpm 容器是没有的
# 可以把主机的 php 文件夹映射到 fpm 容器的 php 文件夹

# 2.3.2 设置网络相关
# 查看网络
docker network ls
# 检查一下 bridge 网络
docker network inspect bridge
# Containers 中 fpm 的 IPv4Address 为 172.17.0.4,填入上面 ProxyPassMatch 相关
# 主机的 IPAM.Config.Gateway 为 172.17.0.1(ifconfig 查看 docker0 也可以看到)
# 主机和容器都是可以互通的

# 启动 fpm 的时候把 php 文件夹挂载到 fpm 容器中,因此 fpm 的启动要修改
docker stop fpm
docker run -d --rm --name fpm \
-v /data/php/:/php \
php:7.4.5-fpm-alpine3.10

# 把新的 httpd 的配置文件映射到容器里
docker stop myweb
docker run -d -p 8080:80 --name myweb -v /data/php:/usr/local/apache2/htdocs/  \
-v /data/php/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf \
httpd:2.4.43-alpine

# 访问,返回 test docker,成功
curl localhost:8080/test.php

3. 使用 Docker-compose 编排 PHP + fpm + Apache;

之前是手工启动和删除容器,很不方便,所以需要使用 docker compose,一款编排工具

# Docker-compose 安装
# -L:有些网站有多次跳转,加入此参数随着网站跳转而跳转
# -o:把下载内容输出到指定路径的文件
curl -L "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/local/bin/docker-compose

# 对下载的文件赋予可执行权限
sudo chmod +x /usr/local/bin/docker-compose

# 查看文件版本
docker-compose -v

Compose file 参考(Version 3):https://docs.docker.com/compose/compose-file/

# 创建一个“空的”文件夹,再创建默认的 docker-compose.yml 文件
mkdir -p /data/php/compose
touch /data/php/compose/docker-compose.yml

# 网络参考:https://docs.docker.com/compose/networking/
# 注意:以下 192.158.0.x 都是子网
# 在 docker-compose.yml 中写入以下内容
version: "3"
services:
  fpm:
   image: php:7.4.5-fpm-alpine3.10
   container_name: fpm
   volumes:
      - /data/php/:/php
   networks:
      mywebnet:
       ipv4_address: 192.158.0.2
  
  httpd:
   image: httpd:2.4.43-alpine
   container_name: httpd
   ports:
      - 8080:80
   volumes:
      - /data/php/:/usr/local/apache2/htdocs/  
      - /data/php/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf 
   networks:
      mywebnet:
       ipv4_address: 192.158.0.3


networks:
  mywebnet:
     driver: bridge
     ipam:
       config: 
         - subnet: 192.158.0.0/16

# 写入结束

# 修改 httpd 配置文件
vim /data/php/conf/httpd.conf
# 锁定 ProxyPassMatch ^/(.*\.php)$ fcgi://172.17.0.4:9000/php/$1,修改 IP 为
192.158.0.2

# 启动 compose
cd /data/php/compose/
# 启动
# 也可以指定一个名称 -p php:docker-compose -p php -d
# 注意:如果之前创建了相同子网的网络,要先删掉之前的,否则冲突
# 如果指定了-p,那么停止或删除时也要 docker-compose -p php stop 
docker-compose up -d
# 显示
Creating network "compose_mywebnet" with driver "bridge"
Creating fpm   ... done
Creating httpd ... done

# 关闭容器(一个)
docker-compose stop fpm
# 关闭容器(所有)
docker-compose stop
# 同理,删除容器
docker-compose rm

# 查看网络
docker network ls
# 返回
NETWORK ID          NAME                DRIVER              SCOPE
ab368b5af331        bridge              bridge              local
8e8d27f1d706        compose_mywebnet    bridge              local
...
# compose_mywebnet 就是 docker-compose 创建的网络
# 删掉所有没有使用的网络
docker network prune
# 删除指定网络
docker network rm compose_mywebnet

4. Docker 搭建 Nginx + PHP-fpm;

安装 Nginx 镜像,参考:https://hub.docker.com/_/nginx

# 下载(实际生产环境都可以使用 alpine 的操作系统)
docker pull nginx:1.17.10-alpine

# 基本配置:
# 默认网页文件夹是 /usr/share/nginx/html
# 默认配置文件地址是 /etc/nginx/nginx.conf
# 如果没有现成的配置文件,那么可以先胡乱启动下容器,然后拷贝到本地文件夹中
# 启动
docker run --name nginx --rm -d nginx:1.17.10-alpine
# 拷贝
docker cp nginx:/etc/nginx/nginx.conf /data/php/conf

支持普通 HTML 访问

docker run --name nginx -d --rm -v /data/php:/usr/share/nginx/html \
-p 8080:80 nginx:1.17.10-alpine

Nginx + fpm 配置

# 修改 nginx 配置
vim /data/php/conf/nginx.conf
# 修改为以下内容
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

  #  include /etc/nginx/conf.d/*.conf;
  server{
    listen 80;
    location / {
    root  /usr/share/nginx/html;
     index  index.html index.htm index.php;
   }
   location ~ \.php$ {
        root /php;
        fastcgi_pass 192.138.0.2:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
  }

}

启动

# 创建网络
docker network create --driver=bridge --subnet=192.138.0.0/16 nginx_network

# fpm 启动
# 不需要映射端口,Nginx 容器可以通过内网地址(bridge 桥接模式)访问这个容器的 fpm 服务
# 调试的时候 -d 参数可以换成 -it 参数
docker run -d --rm --name fpm \
--network nginx_network --ip 192.138.0.2 \
-v /data/php:/php \
php:7.4.5-fpm-alpine3.10

# 启动 Nginx(自动生成 ip)
docker run --name nginx -d --rm -v /data/php:/usr/share/nginx/html \
-v /data/php/conf/nginx.conf:/etc/nginx/nginx.conf \
--network nginx_network -p 8080:80 \
nginx:1.17.10-alpine

Docker-compose 启动

# 清除没用的网络
docker network prune

# 之前使用过的子网 192.158.x.x 和 192.138.x.x,现在统一改为 192.138.x.x
# 修改 httpd 配置文件
vim /data/php/conf/httpd.conf
# 锁定 ProxyPassMatch ^/(.*\.php)$ fcgi://192.158.0.2:9000/php/$1,修改 IP 为
192.138.0.2

# 修改 docker-compose 文件
vim /data/php/compose/docker-compose.yml
# 修改为以下内容
version: "3"
services:
  fpm:
   image: php:7.4.5-fpm-alpine3.10
   container_name: fpm
   volumes:
      - /data/php:/php
   networks:
      mywebnet:
       ipv4_address: 192.138.0.2
  
  httpd:
   image: httpd:2.4.43-alpine
   container_name: httpd
   ports:
      - 8081:80
   volumes:
      - /data/php/:/usr/local/apache2/htdocs/  
      - /data/php/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf 
   networks:
      mywebnet:
       ipv4_address: 192.138.0.3

  nginx:
   image: nginx:1.17.10-alpine
   container_name: nginx
   ports:
      - 8082:80
   volumes:
      - /data/php:/usr/share/nginx/html
      - /data/php/conf/nginx.conf:/etc/nginx/nginx.conf
   networks:
      mywebnet:
       ipv4_address: 192.138.0.4

networks:
  mywebnet:
     driver: bridge
     ipam:
       config: 
         - subnet: 192.138.0.0/16

# 修改结束
# 启动 
docker-compose up -d
# Apache 和 Nginx 都能访问
curl localhost:8081/test.php
curl localhost:8082/test.php

此时结构如下
在这里插入图片描述

5. MySQL 容器;

5.1 创建 MySQL 容器;

参考:https://hub.docker.com/_/mysql/

# 拉取镜像
docker pull mysql:8.0.20

# 简单运行
# -e 设置环境变量
# MYSQL_ROOT_PASSWORD 是 root 的密码
docker run --name mysql --rm \
-p 3307:3306 \
-e MYSQL_ROOT_PASSWORD=asdf -d mysql:8.0.20

# 连接测试
# 使用容器里的客户端完成
docker exec -it mysql mysql -u root -pasdf

5.2 MySQL 配置文件方式启动、导入数据;

在实际开发的时候,肯定要使用配置文件对 MySQL 进行一些参数的优化,不使用配置文件肯定是不方便的

定位到 “Using a custom MySQL configuration file”:配置文件

  • 注意点 1:在 /etc/mysql/conf.d 目录下也可以加入配置文件,后缀必须是 .cnf
  • 注意点 2:MySQL 5 和 8 版本的一个区别:在 8 版本中,没有了 /etc/mysql/mysql.conf.d/mysqld.cnf 配置文件(5 版本里有),此配置文件里的内容合并到了 /etc/mysql/my.cnf

定位到 “Where to Store Data”:数据存储目录

# 5.2.1 进入容器的命令行,查看容器里的配置文件相关目录
docker exec -it mysql bash
cd /etc/mysql/
ls

# 5.2.2 挂载相关文件
# 根据官网,配置文件在
/etc/mysql/my.cnf
# 数据目录在
/var/lib/mysql
# 先创建一个文件夹 mysql,里面包含几个子文件夹 conf、 data
mkdir -p /data/php/mysql/conf
mkdir -p /data/php/mysql/data

# 创建配置文件(文件名随便)
# 官方的 my.cnf 加载了conf.d 里面的文件(!includedir /etc/mysql/conf.d/)
# 只要创建个带有关键配置部分的配置文件,映射 conf.d 文件夹即可
# 在 conf 文件下,随便创建个文件比如 abc.cnf
touch /data/php/mysql/conf/abc.cnf

# 写入以下内容(一些基础配置)
[mysqld]
# 服务 id 唯一(用于主从同步)
server-id = 1 
port = 3306
# 必须是容器里有的目录
log-error	= /tmp/error.log
# 只能用IP地址
skip_name_resolve 
# 数据库默认字符集
character-set-server = utf8mb4
# 数据库字符集对应一些排序等规则 
collation-server = utf8mb4_general_ci
# 设置 client 连接 mysql 时的字符集,防止乱码
init_connect='SET NAMES utf8mb4'
# 最大连接数
max_connections=300
# default_authentication_plugin=mysql_native_password

# 写入完毕

# 5.2.3 启动挂载目录
# 先停止容器
docker stop mysql && docker rm mysql
docker run --name mysql --rm \
-v /data/php/mysql/conf:/etc/mysql/conf.d \
-v /data/php/mysql/data:/var/lib/mysql \
-p 3307:3306 \
-e MYSQL_ROOT_PASSWORD=asdf \
-d mysql:8.0.20

# 端口设置
sudo iptables -I INPUT -p tcp --dport 3307 -j ACCEPT

# 测试配置
show variables like 'max_connections'

# 注意!MySQL 8 版本,客户端连接出现如下报错解决方式
# ERROR 1045 (28000): Plugin caching_sha2_password could not be loaded: 
# Error loading shared library /usr/lib/mariadb/plugin/caching_sha2_password.so
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'asdf';
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'asdf';

导入数据

# 数据准备
vim /data/php/mysql/test.sql
# 内容如下
SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `users`
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(50) NOT NULL,
  `user_qq` varchar(50) NOT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('1', 'hualaoshuan', 'asdf');
INSERT INTO `users` VALUES ('2', 'zhangsan', '123456');

# 内容结束

# 拷贝
docker cp /data/php/mysql/test.sql mysql:/tmp

# 进入容器客户端
docker exec -it mysql mysql -uroot -pasdf
# 导入
mysql> create database test;
mysql> use test;
mysql> source /tmp/test.sql;

5.3 微容器 alpine 之构建基础镜像、安装 MySQL 客户端;

下载最基础的 alpine 镜像:https://hub.docker.com/_/alpine/

# 下载
docker pull alpine:3.11

# 使用 Dockerfile 构建镜像
# 链接:https://hub.docker.com/_/alpine/,锁定关键词:How to use this image
# 以下为示例
FROM alpine:3.7
# apk add,类似于 apt-get 和 yum
# 从镜像库下载 mysql-client 客户端
# --no-cache:从远程直接下载,不使用本地镜像库
RUN apk add --no-cache mysql-client
# ENTRYPOINT:启动容器的时候执行的命令
# mysql:下载下来后的可执行程序,会主动连接本地的 MySQL Server
ENTRYPOINT ["mysql"]
# 示例结束

# 演示
# 创建 Dockerfile 相关文件夹
mkdir -p /data/php/mydocker
vim Dockerfile
# 写入如下内容
FROM alpine:3.11
RUN apk add --no-cache mysql-client
ENTRYPOINT ["mysql"]
# 写入结束

# 构建新镜像
cd /data/php/mydocker
docker build -t mytool:1.0 .

# 测试运行
docker run -it --name tmp --rm mytool:1.0
# 执行会报错,因为容器启动时默认执行的命令是 mysql
# 如果没任何参数,代表连接它自己本地的 mysql,本地没有 mysql,所以报错

# 正确启动( -it 交互式,直接进入客户端)
docker run -it --name tmp mytool:1.0 mysql -h 192.168.2.214 -uroot -pasdf

# 注意点
# alpine 使用的是 apk 包管理器命令,如 apk add 、apk update 、apk del
# 默认镜像源可能比较慢,常用的有 
# 中科大镜像源:http://mirrors.ustc.edu.cn/alpine/
# 阿里云镜像源:http://mirrors.aliyun.com/alpine/

修改数据源

# 查看源文件
docker start tmp
docker exec -it tmp cat /etc/apk/repositories

# 修改方法 1:手动加入阿里镜像源
docker exec -it tmp sh -c \
"echo \"http://mirrors.aliyun.com/alpine/v3.11/main/\" > /etc/apk/repositories"
docker exec -it tmp sh -c \
"echo \"http://mirrors.aliyun.com/alpine/v3.11/community/\" >> /etc/apk/repositories"
# 设置好后,执行
apk update && apk upgrade

# (推荐)修改方法 2:重新修改 Dockerfile
# 修改内容如下
FROM alpine:3.11
RUN echo http://mirrors.ustc.edu.cn/alpine/v3.11/main > /etc/apk/repositories
RUN echo http://mirrors.ustc.edu.cn/alpine/v3.11/community >> /etc/apk/repositories
RUN apk update && apk upgrade
RUN apk add mysql-client
ENTRYPOINT ["mysql"]

# 重新构造镜像
docker build -t mytool:1.0 .

# 测试运行
docker run -it --name mysqlclient --rm mytool:1.0 \
mysql -h 192.168.2.214 -uroot -P3307 -pasdf

5.4 制作 MySQL 备份专用镜像;

5.4.1 动态参数;

数据库备份命令

mysqldump -u用户名 -p密码 数据库名 > 导出的文件名

# 举例
mysqldump -h192.168.2.214 -uroot -P3307 -pasdf test > test.sql
# 然后把 test.sql 专门生成在一个文件夹中
# 可以使用挂载的方式生成到本地物理机上

基于之前的 mytool:1.0 镜像

# 新 Dockerfile 编写
cd /data/php/mydocker
vim Dockerfile
# 修改为以下内容
FROM mytool:1.0
RUN mkdir /data
ENTRYPOINT mysqldump -h192.168.2.214 -uroot -P3307 -pasdf test > /data/test.sql
# 编写完毕
# 注释:这里的 ENTRYPOINT 可以覆盖上一次的
# ENTRYPOINT 后有“[]”  基于参数,相当于直接运行可执行文件
# 没有 “[]” 相当于在内部启动了一个 shell 进程:sh -c "xxxx",适用于命令比较复杂的情况

# 编译镜像
docker build -t mytool:1.1 .

# 挂载执行
mkdir -p /data/php/mysql/mysqlbak
docker run -it --name bakup --rm -v /data/php/mysql/mysqlbak:/data mytool:1.1

# 优化 Dockerfile
FROM mytool:1.1
ENV mysql_user root
ENV mysql_pass asdf
ENV mysql_host 192.168.2.214
ENV mysql_port 3307
ENV mysql_db test
ENTRYPOINT mysqldump -h$mysql_host -P$mysql_port -u$mysql_user -p$mysql_pass $mysql_db > /data/$mysql_db.sql
# 优化结束

# 编译
docker build -t mytool:1.2 .
 
# 运行,备份本机数据库(3306)数据
docker run -it --name bakup --rm \
-v /data/php/mysql/mysqlbak:/data \
-e mysql_pass=asdf \
-e mysql_host=192.168.2.214 \
-e mysql_port=3306 \
-e mysql_db=test \
-e mysql_user=root \
mytool:1.2

6. ThinkPHP5 + Docker 运行;

6.1 结合 Apache 运行;

准备工作

# 下载 thinkphp:http://www.thinkphp.cn/down.html
# 创建相关目录
# 放程序文件
mkdir -p /data/php/tp5/web/
# 放 docker-compose.yml(compose)
mkdir -p /data/php/tp5/compose/

# 下载后解压解压
unzip thinkphp_5.0.24_with_extend.zip -d /data/php/tp5/web/

设置网站目录
在这里插入图片描述
实际操作

# 6.1.1 删除没用的网络
docker network prune

# 6.1.2 修改要挂载的 Apache 配置文件
vim /data/php/conf/httpd.conf
# 修改 <VirtualHost *:80> 标签里的 ProxyPassMatch 为如下内容
ProxyPassMatch ^/(.*\.php)$ fcgi://192.138.0.2:9000/php/public/$
# 修改完毕后保存退出

# 6.1.3 创建 dock-compose
cd /data/php/tp5/compose
vim docker-compose.yml
# 写入以下内容
version: "3"
services:
  fpm:
   image: php:7.4.5-fpm-alpine3.10
   container_name: fpm
   volumes:
      - /data/php/tp5/web:/php
   networks:
      mywebnet:
       ipv4_address: 192.138.0.2
  httpd:
   image: httpd:2.4.43-alpine
   container_name: httpd
   ports:
      - 8081:80
   volumes:
      - /data/php/tp5/web/public:/usr/local/apache2/htdocs/
      - /data/php/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf
   networks:
      mywebnet:
       ipv4_address: 192.138.0.3


networks:
  mywebnet:
     driver: bridge
     ipam:
       config:
         - subnet: 192.138.0.0/16

# 写入完毕,运行
docker-compose up -d

# 访问
curl http://192.168.2.214:8081/index.php

6.2 配置 Samba 共享;

参考镜像: https://hub.docker.com/r/dperson/samba

# 下载镜像
docker pull dperson/samba

# 开放相关端口
iptables -I INPUT -p tcp --dport 139 -j ACCEPT
iptables -I INPUT -p tcp --dport 445 -j ACCEPT

iptables -I INPUT -p udp --dport 137 -j ACCEPT
iptables -I INPUT -p udp --dport 138 -j ACCEPT

# 测试运行
docker run -it -p 139:139 -p 445:445 --name smb -d --rm  \
 -v /data/php/tp5/web:/mount \
 dperson/samba \
 -u "root;asdf" \
 -s "root;/mount/;yes;no;yes;all;all;all" \
 -w "WORKGROUP" 

## 相关调试
# 进入容器
docker exec -it smb sh

# 查看进程
ps -ef | grep samb

# 进入挂载目录,里面的内容和主机的文件是同步的
cd /mount

# samba 配置
cd /etc/samba

# 重新配置
docker stop smb
# 完整运行(注意下面的 root 是开发机的当前用户)
docker run -it -p 139:139 -p 445:445  --name smb -d --rm  \
 -v /data/php/tp5/web:/mount \
 dperson/samba \
 -u "root;asdf" \
 -s "hua;/mount/;yes;no;yes;all;all;all" \
 -w "WORKGROUP" \
 -g "force user= root" \
 -g "guest account= root" 

6.3 添加控制器、url 重写;

开启 Apache 重写

# 编辑配置文件
vim /data/php/conf/httpd.conf

# 修改以下内容
# 以下配置前的 “#” 去掉
LoadModule rewrite_module modules/mod_rewrite.so
# <VirtualHost *:80> - <Directory> 标签内加入
AllowOverride all
#  ProxyPassMatch 修改为以下
ProxyPassMatch ^/(.*\.php) fcgi://192.138.0.2:9000/php/public/$1

# 重新构建容器
docker-compose restart

# 修改 /web/application/index/controller/Index.php
# 追加方法
public function test() {}
# 访问:http://192.168.2.214:8081/index/index/test

6.4 连接数据库、添加 PHP 扩展;

Docker-compose 编排 MySQL

# 暂停删除之前的 MySQL 容器
docker stop mysql

# 修改 docker-compose.yml
vim /data/php/tp5/compose/docker-compose.yml
# 修改为以下内容
version: "3"
services:
  fpm:
   image: php:7.4.5-fpm-alpine3.10
   container_name: fpm
   volumes:
      - /data/php/tp5/web:/php
   networks:
      mywebnet:
       ipv4_address: 192.138.0.2
  httpd:
   image: httpd:2.4.43-alpine
   container_name: httpd
   ports:
      - 8081:80
   volumes:
      - /data/php/tp5/web/public:/usr/local/apache2/htdocs/
      - /data/php/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf
   networks:
      mywebnet:
       ipv4_address: 192.138.0.3

  mysql:
   image: mysql:8.0.20
   container_name: mysqld
   ports:
     - 3307:3306
   volumes:
     - /data/php/mysql/conf:/etc/mysql/conf.d
     - /data/php/mysql/data:/var/lib/mysql
   environment:
     - MYSQL_ROOT_PASSWORD=asdf
   networks:
      mywebnet:
       ipv4_address: 192.138.0.4

networks:
  mywebnet:
     driver: bridge
     ipam:
       config:
         - subnet: 192.138.0.0/16

# 修改结束

# 运行(追加创建 mysql 容器)
docker-compose up -d mysql

启动容器后情况如下

  • 可以用数据库客户端软件,连接映射端口的主机地址 192.168.2.214:3307

在这里插入图片描述

配置数据库连接

# 数据库连接配置
# 可以填主机地址 192.168.2.214:3307
# 但是一般填写的是 Docker 虚拟出来的 ip
# 查看 Docker 网络
docker network ls
docker network inspect compose_mywebnet

# 修改 /web/application/database.php
# 修改以下内容
return [
    // 数据库类型
    'type'            => 'mysql',
    // 服务器地址(Docker 虚拟的 ip)
    'hostname'        => '192.138.0.4',
    // 数据库名
    'database'        => 'test',
    // 用户名
    'username'        => 'root',
    // 密码
    'password'        => 'asdf',
    // 端口
    'hostport'        => '3306'
]

# 打开调试:修改 /web/application/config.php
return [
    // 应用调试模式
    'app_debug'              => true,
];

# 定义模型,参考:https://www.kancloud.cn/manual/thinkphp5/135187
# 创建 /web/application/index/model/UserModel.php
# 写入以下内容
<?php

namespace app\index\model;

use think\Model;

class UserModel extends Model
{
    protected $table = 'users';
}

# 创建 /web/application/index/controller/UserIndex.php
# 写入以下内容
<?php

namespace app\index\controller;

use app\index\model\UserModel;

class UserIndex
{
    public function index()
    {
        $users = UserModel::all();
        var_dump($users);

        return "this is userIndex";
    }
}

# 返回:could not find driver
# 因为默认的 PHP–FPM 容器没有 PDO_MYSQL 扩展

添加 PHP 扩展

# 进入 PHP–FPM 容器
docker exec -it fpm sh
# 查看 PHP 扩展
php -m
# 没有 pdo_mysql 扩展,需要安装
# 文档:https://github.com/docker-library/docs/blob/master/php/README.md#php-core-extensions
# 以上是核心安装库
# 如果需要安装 swoole 等,查阅 PECL extensions,也可以手工安装
# pdo_mysql 是属于核心扩展,只需要执行 docker-php-ext-install(官方镜像的脚本)

# 安装前需要修改下 alpine 的镜像源,否则下载速度太慢
# 查看镜像源版本
cat /etc/issue
# 查看镜像源设置文件
cat /etc/apk/repositories
# 返回
http://dl-cdn.alpinelinux.org/alpine/v3.10/main
http://dl-cdn.alpinelinux.org/alpine/v3.10/community
# 把官方镜像源替换成阿里云镜像源(注意对应的版本号要准确)
echo http://mirrors.ustc.edu.cn/alpine/v3.10/main > /etc/apk/repositories && \ 
echo http://mirrors.ustc.edu.cn/alpine/v3.10/community >> /etc/apk/repositories
# 更新配置
apk update && apk upgrade

# 安装扩展
docker-php-ext-install pdo_mysql

# 重启容器
cd /data/php/tp5/compose
docker-compose restart fpm

# 再访问
curl http://192.168.2.214:8081/index/user_index/index

持久化

# 刚才的操作是在容器里临时完成的,一旦容器删除了,所有之前的操作就没有了
docker-compose stop fpm
docker-compose rm fpm
docker-compose up -d fpm
# 解决方案,两种方法:
# 1. 再次创建 Dockerfile, 构建一个新镜像
# 那要是下次还有内容修改呢?一直不断的构建会很麻烦

# (推荐)2. 在 docker-compose 文件中进行构建
# 一旦有新内容,不会临时到外部进行构建的,而是直接在 docker-compose 下面执行
# 1) 在当前文件夹下创建一个子文件夹 叫做 build
# 2) 在 build 里面新建一个 Dockerfile 文件,注意文件名,比如叫做 phpfpm
mkdir -p /data/php/tp5/compose/build/
cd /data/php/tp5/compose/build/
# 创建 Dockerfile
vim phpfpm

# 写入如下内容
FROM php:7.4.5-fpm-alpine3.10
RUN echo http://mirrors.ustc.edu.cn/alpine/v3.10/main > /etc/apk/repositories && \
  echo http://mirrors.ustc.edu.cn/alpine/v3.10/community >> /etc/apk/repositories
RUN apk update && apk upgrade
RUN docker-php-ext-install pdo_mysql
# 写入结束

# 修改 docker-compose.yml
vim /data/php/tp5/compose/docker-compose.yml
# 修改如下(添加了 services - fpm - build 内容)
version: "3"
services:
  fpm:
   build: 
    context: ./build
    dockerfile: phpfpm
   image: php:7.4.5-fpm-alpine3.10
   container_name: fpm
   volumes:
      - /data/php/tp5/web:/php
   networks:
      mywebnet:
       ipv4_address: 192.138.0.2
  httpd:
   image: httpd:2.4.43-alpine
   container_name: httpd
   ports:
      - 8081:80
   volumes:
      - /data/php/tp5/web/public:/usr/local/apache2/htdocs/
      - /data/php/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf
   networks:
      mywebnet:
       ipv4_address: 192.138.0.3

  mysql:
   image: mysql:8.0.20
   container_name: mysqld
   ports:
     - 3307:3306
   volumes:
     - /data/php/mysql/conf:/etc/mysql/conf.d
     - /data/php/mysql/data:/var/lib/mysql
   environment:
     - MYSQL_ROOT_PASSWORD=asdf
   networks:
      mywebnet:
       ipv4_address: 192.138.0.4

networks:
  mywebnet:
     driver: bridge
     ipam:
       config:
         - subnet: 192.138.0.0/16

# 修改结束

# 重构
docker-compose build fpm
# 完成后显示
Successfully built 3343fec11dd6
Successfully tagged php:7.4.5-fpm-alpine3.10
# 新构筑了一个镜像并打上标签,原来的镜像废弃
# 停掉原来的 fpm 容器
docker-compose stop fpm 
docker-compose rm fpm 

# 创建新容器
docker-compose up -d fpm

6.5 添加 Redis 扩展;

安装 Redis,参考:https://hub.docker.com/_/redis/

# 获取镜像
docker pull redis:6.0.1-alpine

# 配置文件
# 实例,有各个版本的配置文件:https://redis.io/topics/config
mkdir -p /data/php/tp5/conf
vim redis.conf
## 写入以下内容
bind 0.0.0.0
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300

# 容器方式直接 no
daemonize no
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
databases 16
always-show-logo yes

save 900 1
save 300 10
save 60 10000

stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
# 容器里的工作目录
dir /data

slave-serve-stale-data yes
slave-read-only yes

repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no

slave-priority 100

lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
slave-lazy-flush no

appendonly no

appendfilename "appendonly.aof"

appendfsync everysec
no-appendfsync-on-rewrite no

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

aof-load-truncated yes
aof-use-rdb-preamble no

lua-time-limit 5000

slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0

notify-keyspace-events ""

hash-max-ziplist-entries 512
hash-max-ziplist-value 64

list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512

zset-max-ziplist-entries 128
zset-max-ziplist-value 64

hll-sparse-max-bytes 3000

activerehashing yes

client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

hz 10

aof-rewrite-incremental-fsync yes

## 写入完毕

# 修改 docker-compose.yml
vim /data/php/tp5/compose/
# 添加 redis 部分配置,如下
version: "3"
services:
  fpm:
   build: 
    context: ./build
    dockerfile: phpfpm
   image: php:7.4.5-fpm-alpine3.10
   container_name: fpm
   volumes:
      - /data/php/tp5/web:/php
   networks:
      mywebnet:
       ipv4_address: 192.138.0.2
  httpd:
   image: httpd:2.4.43-alpine
   container_name: httpd
   ports:
      - 8081:80
   volumes:
      - /data/php/tp5/web/public:/usr/local/apache2/htdocs/
      - /data/php/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf
   networks:
      mywebnet:
       ipv4_address: 192.138.0.3

  mysql:
   image: mysql:8.0.20
   container_name: mysqld
   ports:
     - 3307:3306
   volumes:
     - /data/php/mysql/conf:/etc/mysql/conf.d
     - /data/php/mysql/data:/var/lib/mysql
   environment:
     - MYSQL_ROOT_PASSWORD=asdf
   networks:
      mywebnet:
       ipv4_address: 192.138.0.4

  redis:
   image: redis:6.0.1-alpine
   container_name: redis
   ports:
     - 6389:6379
   volumes:
     - /data/php/tp5/conf/redis.conf:/usr/local/etc/redis/redis.conf
     - /data/php/tp5/redisdata:/data
   networks:
      mywebnet:
       ipv4_address: 192.138.0.10


networks:
  mywebnet:
     driver: bridge
     ipam:
       config:
         - subnet: 192.138.0.0/16

# 修改完毕

# 启动
docker-compose up -d
# 进入容器内 redis
docker exec -it redis redis-cli -p 6379

在这里插入图片描述

添加 PHP 的 Redis 扩展

# Redis 扩展可以使用 pecl 来安装
# 参考文档:https://hub.docker.com/_/php/,锁定 “PECL extensions”
# 在 build 目录中 创建 phpredis 文件
vim /data/php/tp5/compose/build/phpredis
# 写入以下内容
FROM php:7.4.5-fpm-alpine3.10
RUN apk add autoconf gcc g++ make
RUN pecl install redis-5.1.1 \
    && docker-php-ext-enable redis

# 写入完毕,修改 docker-compose.yml
vim /data/php/tp5/compose/docker-compose.yml
# services - fpm - build - dockerfile 改成 phpredis

# 重构
docker-compose build fpm

# 完成后显示
Successfully built d7ccf1769bc0
Successfully tagged php:7.4.5-fpm-alpine3.10
# 新构筑了一个镜像并打上标签,原来的镜像废弃
# 停掉原来的 fpm 容器
docker-compose stop fpm 
docker-compose rm fpm 

# 创建新容器
docker-compose up -d fpm

代码实现

# 参考:https://www.kancloud.cn/manual/thinkphp5/118131
# 修改 /web/application/config.php
'cache'                  => [
    // 驱动方式
    'type'   => 'redis',
    // 容器 ip
    'host'   => '192.138.0.10',
    'port'   => '6379',
    // 缓存保存目录
    // 'path'   => CACHE_PATH,
    // 缓存前缀
    'prefix' => '',
    // 缓存有效期 0表示永久缓存
    'expire' => 0,
],

# 修改 /web/application/index/controller/UserIndex.php
<?php

namespace app\index\controller;

use app\index\model\UserModel;
use think\Cache;

class UserIndex
{
    public function index()
    {
        //$users = UserModel::all();
        Cache::set("user", "testuser");
        $users = Cache::get("user");
        print_r($users);

        return "this is userIndex";
    }
}

6.6 上传文件、alpine 环境下的权限问题;

模板设置:https://www.kancloud.cn/manual/thinkphp5/119298

  • 出现的问题
## 问题 1:mkdir(): Permission denied
# 之前是通过“挂载”的方式挂载到容器里,如果 /web/runtime 里没有缓存文件夹,就会出现上述错误
# 这个问题和物理机里的权限是没有关系的

# 查看物理机用户
cat /etc/passwd
# 返回,得到用户组为 1000,是映射到容器里的
# 但是这个 1000 在 fpm 里是没有的
hualaoshuan:x:1000:1000:hualaoshuan:/home/hualaoshuan:/bin/bash

# 进入容器
docker exec -it fpm sh
# 进入到挂载的目录
cd /php
ls -l
# 目录的用户:群组分别是 1000:1000
# 这个 1000 就是指物理机用户

# 查看 php 进程,php-fpm 所有者是 www-data
ps -ef | grep php
# 返回
1 root      0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf)
6 www-data  0:00 php-fpm: pool www

# 查看所有用户
cat /etc/passwd 
# 返回
.
www-data:x:82:82:Linux User,,,:/home/www-data:/sbin/nologin

# 解决方案:把 www-data 的用户和群组改为物理机用户的群组
# www-data 是 php-fpm 启动的默认用户名
# 但是 alpine 没有 usermod 命令
# 参考:https://pkgs.alpinelinux.org/contents?file=usermod&path=&name=&branch=&repo=&arch=
# alpine < 3.4,在数据源中加入
echo http://nl.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories
apk --no-cache add shadow
# alpine > 3.4
apk add shadow

# 修改用户 www-data 的用户以及群组 ID 为 1000
usermod -u 1000 www-data
groupmod -g 1000 www-data

# 退出容器,重启容器
docker-compose restart fpm
  • 创建:/web/application/index/controller/Upload.php
<?php
// 访问:http://192.168.2.214:8081/index/upload/
namespace app\index\controller;

use think\Controller;

class Upload extends Controller
{

    public function index()
    {
        return $this->fetch("up");
    }

	public function up()
	{
	// https://www.kancloud.cn/manual/thinkphp5/155159
	}
}
  • 创建:/web/application/index/view/upload/up.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post" enctype="multipart/form-data" action="/index/user_index/up">
    上传文件:
    <hr/>
    <input type="file" name="myfile"/>
    <hr/>
    <input type="submit" value="上传"/>

</form>
</body>
</html>

7. Laravel + Docker 运行;

7.1 Composer 镜像使用;

# 拉取镜像
docker pull composer
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐