目录

  • dnmp环境搭建
  • 1、dnmp目录结构
  • 2、.env 配置文件
  • 3、docker-compose.yml
  • 4、dockerfile
  • 5、启动
  • 6、测试
  • 遇到的问题
  • 1、PHP8 打不开
  • 2、MySQL 不允许主机连接到此MySQL服务器
  • 3、Mysql 无法连接


dnmp环境搭建

本片文章搭建的环境是 nginx1.19 + php7.4 + php8.2 + mysql5.7 + mysql8.0 + redis,多版本 php 和多版本 mysql。
这里就直接使用 docker-compose 搭建环境,也可以找到镜像创建容器再关联映射端口部署。

资源包下载地址
参考文章:docker-compose使用nginx+php+mysql+redis部署fastadmin。

1、dnmp目录结构

.
.env							# 配置参数
├── docker-compose.yml			# docker-compose文件
├── mysql57
│   ├── conf					
│   │   └── my.cnf
│   └── log						
│       ├── mysqld.log
│       ├── tallylog
│       └── yum.log
├── mysql80
│   ├── conf					
│   │   └── my.cnf
│   ├── data					# mysql8.0数据目录(我这里没有挂载)
│   └── log						
├── nginx
│   ├── conf					
│   │   ├── default.conf
│   │   ├── php.conf
│   └── logs					
│       ├── access.log
│       └── error.log
├── php74
│   ├── Dockerfile				# php7.4 Dockerfile文件
│   └── conf
│       ├── ini
│       │   ├── conf.d
│       │   │   ├── docker-php-ext-mysqli.ini
│       │   │   ├── docker-php-ext-pdo_mysql.ini
│       │   │   └── docker-php-ext-sodium.ini
│       │   ├── php.ini-development
│       │   └── php.ini-production
│       └── php-fpm.d
│           ├── docker.conf
│           ├── www.conf
│           ├── www.conf.default
│           └── zz-docker.conf
├── php824
│   ├── Dockerfile				# php8.2.4 Dockerfile文件
│   ├── conf
│   │   ├── php-fpm.conf
│   │   └── php.ini
│   ├── data
│   │   └── composer
│   └── logs
│       ├── fpm.slow.log
│       └── php.error.log
└── redis
    ├── redis.cnf
    └── redis.conf

2、.env 配置文件

我只简单配置了 工作目录docker环境目录,可以根据需要把 docker-compose.yml 中容器参数放到这边。

#
# 项目目录/工作目录
#
PROJECT_DIR=/Users/yunsanmu/work/project

#
# 当前docker开发环境目录
#
DEV_ENV_DIR=/Users/yunsanmu/dev-env

3、docker-compose.yml

为了方便操作,一般都会把容器的配置文件、日志文件等挂载映射。端口映射根据自己需要

version: "2.1"
services:
    nginx:
        image: nginx:1.19.1-alpine
        container_name: nginx
        ports:
            - "80:80"
        volumes:
            - ${PROJECT_DIR}:/usr/share/nginx/html
            - ${DEV_ENV_DIR}/nginx/conf:/etc/nginx/conf.d
            - ${DEV_ENV_DIR}/nginx/logs:/var/log/nginx
        networks:
            - lnmp-network
    php74:
        container_name: php74
        build: ./php74 # 这个目录放上我们刚才做好的定制化php的Dockerfile
        ports:
            - "9001:9000"
        volumes:
            - ${PROJECT_DIR}:/www:rw
            - ${DEV_ENV_DIR}/php74/conf/ini:/usr/local/etc/php
            - ${DEV_ENV_DIR}/php74/conf/php-fpm.d:/usr/local/etc/php-fpm.d
        restart: always
        links:
            - mysql57
        networks:
            - lnmp-network
    php824:
        container_name: php824
        build: ./php824
        ports:
            - "9002:9000"
        volumes:
            - ${PROJECT_DIR}:/www/:rw
            - ${DEV_ENV_DIR}/php824/conf/php.ini:/usr/local/etc/php/php.ini:ro
            - ${DEV_ENV_DIR}/php824/conf/php-fpm.conf:/usr/local/etc/php-fpm.d/www.conf:rw
            - ${DEV_ENV_DIR}/php824/logs:/var/log/php
            - ${DEV_ENV_DIR}/php824/data/composer:/tmp/composer
        restart: always
        networks:
            - lnmp-network
    mysql57:
        image: mysql/mysql-server:5.7.28
        container_name: mysql57
        ports:
            - "3307:3306"
        volumes:
            - ${DEV_ENV_DIR}/mysql57/conf/my.cnf:/etc/my.cnf:ro
            # - ${DEV_ENV_DIR}/mysql57/data:/var/lib/mysql/:rw # 这里我就不挂载了,有需要到容器里看
            - ${DEV_ENV_DIR}/mysql57/log:/var/log/:rw
        environment:
            - MYSQL_ROOT_PASSWORD=root
        restart: always
        networks:
            - lnmp-network
    mysql80:
        image: mysql:8.0
        container_name: mysql80
        ports:
            - "3308:3306"
        volumes:
            - ${DEV_ENV_DIR}/mysql80/conf/my.cnf:/etc/my.cnf:ro
            # - ${DEV_ENV_DIR}/mysql80/data:/var/lib/mysql/:rw
            - ${DEV_ENV_DIR}/mysql80/log:/var/log/:rw
        environment:
            - MYSQL_ROOT_PASSWORD=root
        networks:
            - lnmp-network
    redis:
        image: redis:5.0.3-alpine
        container_name: redis
        ports:
            - "6379:6379"
        volumes:
            - ${DEV_ENV_DIR}/redis/redis.cnf:/etc/redis.conf:ro
        restart: always
        entrypoint: [ "redis-server", "/etc/redis.conf" ]
        networks:
            - lnmp-network
networks:
    lnmp-network:
        driver: bridge
        ipam:
            driver: default

由于我的电脑是 mac 的 m1 芯片 所以我这里使用的都是支持linux/arm64/v8 的镜像。

mac m2 docker 安装 lnmp docker安装mpd_docker

MySQL5.7的最新镜像就是不支持 linux/arm64/v8 平台,创建容器会报如下警告:

WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested

我这里使用 mysql/mysql-server:5.7.28 替代。

4、dockerfile

dockerfile 是自定义镜像的一套规则,由多条指令构成,在docker-compose.yml 中通过 build 参数引入。 上面的 docker-compose.yml 比较简单,我只对两个版本的PHP写了 dockerfile,其他的都只在 compose 中使用image,没有过多配置。

php7.4 的 dockerfile:

# 该装的都装上
 
# 版本7.4
FROM php:7.4-fpm
 
# composer
RUN curl -sS https://getcomposer.org/installer | php \
    && mv composer.phar /usr/bin/composer && chmod +x /usr/bin/composer
 
# 各种依赖库和扩展
RUN apt-get update \
    && apt-get install -y \
    vim \
    git \
    wget \
    build-essential \
    libmagickcore-dev \
    libmagickwand-dev \
    imagemagick \
    libfreetype6-dev \
    libmcrypt-dev \
    libpng-dev \
    libzip-dev \
    libwebp-dev \
    autoconf \
    pkg-config \
    gcc \
    make \
    zlib1g \
    zlib1g-dev \
    && docker-php-ext-configure gd  --with-webp=/usr/include/webp --with-jpeg=/usr/include --with-freetype=/usr/include/freetype2/ \
    && docker-php-ext-install -j$(nproc) gd iconv pdo pdo_mysql pdo_odbc mysqli bcmath calendar exif gettext sockets dba  pcntl  shmop sysvmsg sysvsem sysvshm zip \
    && pecl install redis-5.3.7 \
    && pecl install imagick \
    && pecl install mcrypt \
    && docker-php-ext-enable redis imagick mcrypt \

# 这句很重要,如果和你的nginx用户不同会导致你的服务起不来
RUN groupadd www && useradd -g www www
 
# 如果你需要别的扩展,还可以再加

php8.2.4 的 dockerfile:

# 该装的都装上

# 版本8.2.4
FROM php:8.2.4-fpm

# composer
RUN curl -sS https://getcomposer.org/installer | php \
    && mv composer.phar /usr/bin/composer && chmod +x /usr/bin/composer

# 各种依赖库和扩展
RUN apt-get update \
    && apt-get install -y \
    vim \
    git \
    wget \
    build-essential \
    libmagickcore-dev \
    libmagickwand-dev \
    imagemagick \
    libfreetype6-dev \
    libmcrypt-dev \
    libpng-dev \
    libzip-dev \
    libwebp-dev \
    autoconf \
    pkg-config \
    gcc \
    make \
    zlib1g \
    zlib1g-dev \
    && docker-php-ext-configure gd  --with-webp=/usr/include/webp --with-jpeg=/usr/include --with-freetype=/usr/include/freetype2/ \
    && docker-php-ext-install -j$(nproc) gd iconv pdo pdo_mysql mysqli bcmath calendar exif gettext sockets dba  pcntl  shmop sysvmsg sysvsem sysvshm zip \
    && pecl install redis-5.3.7 \
    && pecl install imagick \
    && pecl install mcrypt \
    && docker-php-ext-enable redis \
    && docker-php-ext-enable imagick \
    && docker-php-ext-enable mcrypt

# 这句很重要,如果和你的nginx用户不同会导致你的服务起不来
RUN groupadd www && useradd -g www www

# 如果你需要别的扩展,还可以再加

5、启动

进入 docker-compose.yml 所在的目录,创建镜像那一步就直接跳过了,执行 docker-compose up -d 启动如下:

yunsanmu@yunsanmudeMacBook-Pro ~ % cd dev-env 
yunsanmu@yunsanmudeMacBook-Pro dev-env % docker-compose up -d
Starting nginx   ... done
Starting redis   ... done
Starting php824  ... done
Starting mysql80 ... done
Starting mysql57 ... done
Starting php74   ... done
yunsanmu@yunsanmudeMacBook-Pro dev-env %

打开docker 客户端如下图:

mac m2 docker 安装 lnmp docker安装mpd_docker_02


启动相关命令:

docker-compose up -d               	# 创建并启动某个容器
docker-compose down                	# 停止并删除所有容器、网络、镜像等
docker-compose stop				   	# 停止所有容器
docker-compose start               	# 启动所有容器
docker-compose restart				# 重启所有容器
docker-compose rm					# 删除容器(删除前必须关闭容器,执行stop)

启动完成后需要做一些操作,mysql默认root账号是没有远程连接权限,先通过命令行在mysql5.7和mysql8.0赋予root用户远程权限,生产环境添加专门的远程账号。

通过docker 客户端进入mysql57和mysql80容器内,都执行一遍下面的命令:

sh-4.2# mysql -uroot -p
Enter password: 
mysql> use mysql;
Database changed

mysql> update user set host = '%' where user = 'root';
Query OK, 1 row affected (0.03 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> flush privileges;
Query OK, 0 rows affected (0.01 sec)

mysql> exit

6、测试

下面的测试显示的都是成功的流程,如果遇到相同问题参考下面的解决方法,其他问题自行百度。

进入nginx配置文件目录添加两个配置文件:php74.confphp82.conf,并且在项目目录下创建两个文件夹:php74php82,分别测试不同版本PHP。

├── nginx
│   ├── conf
│   │   ├── default.conf
│   │   ├── php74.conf
│   │   ├── php82.conf
├── project
│   ├── php74
│   │   ├── index.php
│   ├── php82
│   │   ├── index.php

php74.conf :

server {
    listen  80;
    server_name www.php74.com;

    location / {
        root    /usr/share/nginx/html/php74;
        index   index.html index.htm index.php;
        if (!-e $request_filename) {
            # rewrite ^.*$ /index.php last;
            rewrite ^/(.*)$ /index.php/$1 last;
        }
    }

    location ~ \.php$ {
        fastcgi_pass    php74:9000;
        fastcgi_index   index.php;
        fastcgi_param   SCRIPT_FILENAME /www/php74/$fastcgi_script_name;
        include         fastcgi_params;
    }
}

php82.conf :

server {
    listen  80;
    server_name www.php82.com;

    location / {
        root    /usr/share/nginx/html/php82;
        index   index.html index.htm index.php;
        if (!-e $request_filename) {
            rewrite ^/(.*)$ /index.php/$1 break;
            # rewrite . /index.php last;
        }
    }

    location ~ \.php$ {
        fastcgi_pass    php824:9000;
        fastcgi_index   index.php;
        fastcgi_param   SCRIPT_FILENAME /www/php82/$fastcgi_script_name;
        include         fastcgi_params;
    }
}

修改 hosts,添加以下内容:

127.0.0.1       www.php74.com
127.0.0.1       www.php82.com

虚拟域名访问:

mac m2 docker 安装 lnmp docker安装mpd_php_03

mac m2 docker 安装 lnmp docker安装mpd_docker_04

mysql5.7、mysql8.0和redis连接测试,编辑两个域名下的index.php文件,添加相同测试代码,测试连接:
php74/index.php:

<?php

// redis 测试
$redis = new \Redis();
$redis->connect('redis',6379);
echo "redis is running: ".$redis->ping();
echo '<br>';

// mysql5.7 测试
try {
    # 连接的host要注意,与./nginx/default.conf中的php连接一样,不是ip,是docker-compose中配置的容器名,container_name
    $dsn = "mysql:host=mysql57;dbname=mysql;";
    $username = "root";
    $password = "root";
    $db = new PDO($dsn, $username, $password);

    $sql = "SELECT * FROM user";
    $stmt = $db->prepare($sql);
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
    var_dump($result);
} catch (PDOException $e) {
    echo $e->getMessage() . "<br/>";
    exit;
}
echo '<br>';

// mysql8.0 测试
try {
    # 连接的host要注意,与./nginx/default.conf中的php连接一样,不是ip,是docker-compose中配置的容器名,container_name
    $dsn = "mysql:host=mysql80;dbname=mysql;";
    $username = "root";
    $password = "root";
    $db = new PDO($dsn, $username, $password);

    $sql = "SELECT * FROM user";
    $stmt = $db->prepare($sql);
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
    var_dump($result);
} catch (PDOException $e) {
    echo $e->getMessage() . "<br/>";
    exit;
}

再次访问虚拟域名:

mac m2 docker 安装 lnmp docker安装mpd_php_05


mac m2 docker 安装 lnmp docker安装mpd_docker_06

遇到的问题

1、PHP8 打不开

报错:

[error] 26#26: *1 rewrite or internal redirection cycle while processing "/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/index.php/", client: 172.28.0.1, server: www.php82.com, request: "GET / HTTP/1.1", host: "www.php82.com"
2

修改nginx配置文件的反向代理:

错误配置
if (!-e $request_filename) {
    rewrite ^/(.*)$ /index.php/$1 last;
}

正确配置,替换{}内容:
	rewrite ^/(.*)$ /index.php/$1 break;
	或
	rewrite . /index.php last;

2、MySQL 不允许主机连接到此MySQL服务器

报错:

Host '172.28.0.6' is not allowed to connect to this MySQL server

解决方法: 进入容器执行以下命令。

mysql> use mysql;
mysql> update user set host = '%' where user = 'root';
mysql> flush privileges;
mysql> exit

执行第二个命令时可能会出现 Duplicate entry ‘%-root‘ for key ‘PRIMARY‘ 错误,参考:MySql出现ERROR 1062 (23000): Duplicate entry ‘%-root‘ for key ‘PRIMARY‘

3、Mysql 无法连接

错误:

Error!: could not find driver

原因:
当我们使用 navicat 连接 mysql 时是在容器外进行操作,是用 ip+映射端口 连接;当在 php 代码中使用 pdo 连接 mysql 是在 php 容器内部连接mysql 容器,所以是 容器名+3306端口 连接。

解决办法:

$conn = new PDO('mysql:host=容器名;dbname=数据库;port=端口','用户','密码');