Docker 部署 Nginx+MariaDB(MySQL)+PHP 记录,与对应 docker-compose 实现

Docker 火了这么多年,我也要学习体验一下。就在阿里云的服务器上部署一个 Nginx+MariaDB+PHP 环境吧。

安装

不同系统的安装方法见官方文档,包括了 Linux 的几个发行版和 Windows、MacOS 的详细步骤。我在 Linux 系统上安装。装完记得运行systemctl enable docker把 docker 调成自动启动。

Docker Hub

Docker 的各种 images 会发布在 Docker Hub,要善用这个网站来搜索想要的资源。

网络

在创建 docker 容器(container)之前,先考虑一下网络的架构。我打算创建 3 个容器:nginx、mariadb、php,其中,需要暴露的端口只有 nginx 容器的 80 (443) 端口。根据这篇文档中的:

User-defined bridge networks are best when you need multiple containers to communicate on the same Docker host.

我应该选择 bridge 网络,bridge 的具体的使用见此文档。在宿主机上运行

docker network create --driver bridge my_bridge

创建一个名称为 my_bridge 的bridge。

Nginx

先把 Nginx image 拉下来:docker pull nginx,默认会拉 latest 这个 tag (tags 可以在 Docker Hub 先搜索 nginx 然后点进去找到)。

接下来创建容器:

docker run --name my_nginx --network my_bridge -p 80:80 -v /var/www/php_env:/usr/share/nginx/html -e TZ="Asia/Shanghai" -d --restart always nginx

参数解释:(详情见文档

--name my_nginx           容器命名为my_nginx
--network my_bridge       连接到my_bridge网络
-p 80:80                  把容器的80端口(后)暴露为宿主机的80端口(前)
-v /var/www/php_env:/usr/share/nginx/html   把宿主机的目录(前)mount到容器的指定路径
-e TZ="Asia/Shanghai"     设置环境变量
-d                        在后台运行
--restart always          自动启动、重启
nginx                     image名称

如无意外,浏览器可以访问http://服务器IP的网页了。

PHP

pull image:docker pull php:7.4.3-fpm
看别人的教程,都是用 -fpm 的 PHP,我这里选择目前最新版本的 7.4.3-fpm。

创建容器:

docker run --name my_php --network my_bridge -v /var/www/php_env:/var/www/html -d --restart always php:7.4.3-fpm

参数解释:

--name my_php             容器命名
--network my_bridge       连接到my_bridge网络
-v /var/www/php_env:/var/www/html   把宿主机的目录(前)mount到容器的指定路径
-d                        在后台运行
--restart always          自动启动、重启
php:7.4.3-fpm             image与tag

然后配置 Nginx,让Nginx 可以识别 .php 脚本。首先进入 my_nginx 的容器

docker exec -i -t my_nginx bash

执行之后可以发现,终端里显示的机器变了,例如变成root@40f3a8c8ae81:/#开头,证明已经进入了这个容器。接下来

cd /etc/nginx/conf.d
尝试修改 default.conf,然而竟然没有编辑器,我们马上安装一个
apt-get update
apt-get install nano  # 或者你喜欢vim
nano default.conf

打开 default.conf 之后,修改 index 相关的一处和 php 相关的一段:

    location / {
        root   /usr/share/nginx/html;  # my_nginx容器的路径
        index  index.php index.html index.htm;  # 添加index.php
    }

    location ~ \.php$ {
        root           /var/www/html;  # my_php容器的路径
        fastcgi_pass   my_php:9000;   # 使用my_php的名称来指定远程机器
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

保存文件,service nginx reload使配置生效,exit回到宿主机,运行docker restart my_nginx随手再重启一下容器。

宿主机切换到/var/www/php_env目录,创建一个xxxxxxxx.php,里面写

<?php
echo phpinfo();
?>

赶紧去浏览器试一下http://服务器IP/xxxxxxxx.php吧。

(备注:在我后来的使用过程中,我遇到了一些文件权限的问题,例如我的 php 应用缺少文件写入权限,即写入不了宿主机的/var/www/php_env/*,稍微找了一下资料,最科学的做法应该是手动指定容器运行的用户的。我比较懒,ps -ef | grep php看到 php-fpm 是由 UID=33 的用户运行的,然后我chown -R 33:33 /var/www/php_env暂时解决了。这里应该作为未来改进的地方)

MariaDB

docker pull mariadb
docker run --name my_mariadb --network my_bridge --restart always -e MYSQL_ROOT_PASSWORD=123456 -d mariadb

参数解释:

--name my_mariadb         容器命名
--network my_bridge       连接到my_bridge网络
--restart always          自动启动、重启
-e MYSQL_ROOT_PASSWORD=123456   设置环境变量
-d                        在后台运行
mariadb                   image

启动容器之后,docker exec -i -t my_mariadb bash进入容器,mysql -u root -p然后输入密码(刚才设定的123456),就可以连入 MariaDB 了。

视乎具体的应用,可能还需要使用docker-php-ext-install安装一些 PHP 的扩展。

使用时,数据库的主机(host)应填my_mariadb。由于一开始只有一个 root 用户,可能需要做一些权限配置。

总结

我们先创建了 1 个网络,然后是 3 个容器,它们都在同一个网络中,可以自由地互相通信,并且只暴露一个 80 端口到外部。运行docker ps的输出大概是这样的:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
ade1d8dc3c88        mariadb             "docker-entrypoint.s…"   30 minutes ago      Up 2 minutes        3306/tcp             my_mariadb
dc089caca7bd        php:7.4.3-fpm       "docker-php-entrypoi…"   14 hours ago        Up 2 minutes        9000/tcp             my_php
3704c48d500b        nginx               "nginx -g 'daemon of…"   15 hours ago        Up 2 minutes        0.0.0.0:80->80/tcp   my_nginx

一些参考

  1. https://www.runoob.com/docker/docker-container-usage.html
  2. https://www.cnblogs.com/Felix-DoubleKing/p/11583673.html

更新 docker-compose

为了更好地管理这些容器,学习使用 docker-compose,重新实现了上述的操作。感受到 docker-compose 的内在精神和写代码的“动静分离”是有点像的,即不变的逻辑可以写死,可变的输入成为参数或配置文件,这样可以灵活地用不同的参数来调用业务逻辑。下面为 docker-compose.yml:

version: "3.8"

services:
  web:
    image: nginx
    restart: always
    ports:
      - 443:443
      - 80:80
    environment:
      TZ: Asia/Shanghai
    volumes:
      - ./nginx_default.conf:/etc/nginx/conf.d/default.conf
      - /path/to/certs:/etc/nginx/certs
      - /var/www/php_env:/usr/share/nginx/html

  php:
    build:
      context: .
      dockerfile: Dockerfile.php-fpm.txt
    restart: always
    volumes:
      - /var/www/php_env:/var/www/html

  db:
    image: mariadb:10.4
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456
    volumes:
      - ./mariadb_my.cnf:/etc/mysql/my.cnf
      - ./mariadb_data:/var/lib/mysql

其中 php 用到的 Dockerfile:

FROM php:7.4.7-fpm

RUN apt-get update && \
    apt-get install -y libzip-dev && \
    apt-get install -y libmagickwand-dev --no-install-recommends && \
    rm -rf /var/lib/apt/lists/*

RUN docker-php-ext-install -j$(nproc) bcmath calendar exif gettext sockets dba mysqli pcntl pdo_mysql shmop sysvmsg sysvsem sysvshm iconv zip

RUN printf "\n" | pecl install imagick
RUN docker-php-ext-enable imagick

发表评论