使用 Docker 搭建 PHP + PHP-FPM × Nginx × MySQL 的开发环境

在本文中,我们将使用Docker来构建PHP的开发环境。为了让不熟悉Docker的读者也能够理解,我们还解释了基本概念和术语。

本文假定您的电脑已经安装好并能够正常使用Docker。

如果您尚未安装Docker,请通过以下链接访问Docker官方网站,并安装适用于您正在使用的操作系统的Docker。

开始学习Docker – Docker

Docker 是什么意思?Docker是一种工具,用于在主机上以隔离的环境中运行应用程序和服务。

通过使用Docker,可以在不影响主机环境的情况下构建开发环境并部署应用程序。

容器、镜像和Dockerfile。在开始构建开发环境之前,我们先来解释一下一些重要的术语,即容器、镜像和Dockerfile。另外,我们也会解释一下本文中使用的Docker Compose。

容器 (Container)容器是在主机上运行的隔离环境。容器之间可以独立运行,并且可以在不依赖主机环境的情况下执行操作。

形象图像是用于创建容器的设计蓝图。容器是根据图像创建的。使用docker run命令从图像中创建新的容器。

图像中包含了执行应用程序和服务所需的操作系统、依赖关系和设置等内容。

Dockerfile:Dockerfile 是一个包含创建镜像所需步骤的文本文件。

可以通过从Docker Hub等镜像注册表下载现有的镜像或使用Dockerfile和docker build命令自行创建来获取镜像。

Docker Compose (中文 : Docker 集成)Docker Compose 是一种使用 YAML 文件定义和批量启动/管理多个容器的工具。通过它可以轻松管理由多个容器组成的应用程序。

在本文中,我們將使用Docker Compose創建獨立的容器來建立Nginx、PHP-FPM(+ PHP)和MySQL的開發環境。

总结コンテナは実際にアプリケーションが動作する独立した環境

イメージはコンテナを作成するための設計図

Dockerfile はカスタムのイメージを作成するために、イメージの作成手順を記述するテキストファイル

Docker Compose は複数のコンテナを定義し、一括で起動・管理するためのツール

Docker とイメージとコンテナの関係

使用Docker搭建开发环境

本地环境macOS Ventura v13.0.1(MacBook Air M1 2020)是操作系统版本,Docker v20.10.23是软件版本。

建立开发环境网络服务器:Nginx 1.25
数据库:MySQL 8.0
编程语言:PHP 8.1

整体构造图

構築する環境

    • Web サーバ層(Nginx): Web サーバ層は、クライアントとの間でリクエストとレスポンスを処理を担当します。Web サーバ層は、クライアントからのリクエストを受け取ると、必要な処理をアプリケーション層に委譲します。

 

    • アプリケーション層(PHP-FPM): アプリケーション層は、Web アプリケーションの実行や動的なコンテンツの生成とデータベースとの連携を担当します。PHP-FPM は PHP の実行環境であり、Web サーバから転送されたリクエストを受け取って PHP スクリプトを処理し、結果を Web サーバに返します。

 

    データベース層(MySQL): データベース層はデータの永続化と管理を担当します。アプリケーション層からの要求に応じてデータベースからデータを取得したり、データの更新や削除を行ったりします。

建立目录

php-docker-demo/
    ├── docker/
    │   ├── mysql/
    │   │   └── my.cnf
    │   ├── nginx/
    │   │   └── default.conf
    │   └── php/
    │       ├── Dockerfile
    │       └── php.ini
    ├── src/
    │   └── index.php
    ├── docker-compose.yml
    └── .env

创建项目文件夹在任意的目录下创建一个项目文件夹。
这次我们将在桌面上创建一个名为php-docker-demo的项目文件夹。

打开终端,切换到桌面,创建一个名为 php-docker-demo 的文件夹。

cd desktop

mkdir php-docker-demo

创建docker-compose.yml文件前往项目文件夹并创建docker-compose.yml文件。

cd php-docker-demo

# Mac (ターミナル)の場合
touch docker-compose.yml 

在这个 docker-compose.yml 文件中,我们将描述创建 PHP-FPM(+PHP)、Nginx 和 MySQL 容器的配置。

Nginx容器首先,我们需要编写用于创建 Nginx 容器的配置。
首先,我们需要写下用于创建 Nginx 容器的设置。
首先,我们要记下用于创建 Nginx 容器的设置。

Nginx接受客户端的请求,并扮演提供静态内容(如HTML、CSS、图像等)的角色。此外,它还可以充当反向代理、负载均衡器等多种角色。在诸如VSCode的文本编辑器中打开项目文件夹,并编辑docker-compose.yml文件。

# 特定のバージョンを指定し、このファイル内で使用できる文法が固定
version: '3'

# 起動したいコンテナを定義。本記事では3つのコンテナ(nginx、app、mysql)を定義
services:
  # Nginxの定義
  nginx:
    # Nginxコンテナに使用するイメージを指定
    image: nginx:1.25.0
    # ホストマシンのポートをコンテナのポートにマッピング
    ports:
      # <ホストマシンのポート>:<コンテナのポート>
      - 8000:80
  # app:
  # mysql:

Nginx 的配置

    1. 在services指令中以名为nginx的容器定义。(可以使用任意名称进行定义)

在image指令中指定要使用的Nginx容器的映像。
nginx:1.25.0表示使用版本为1.25.0的Nginx映像。
如果想使用其他版本,请参考nginx – Official Image | Docker Hub的Supported tags。

通过ports键,在主机和容器之间进行端口映射。
由于容器是一个隔离环境,因此需要进行端口映射以从主机(浏览器)访问容器中的Nginx。
通过写入8000:80,所有通过主机上的端口8000发送的请求将被重定向到容器内的端口80。更详细的信息请参考nginx – Official Image | Docker Hub的Exposing external port。

运行 docker-compose 文件使用docker-compose up命令启动容器。

使用 -d 选项以后台模式运行。通过在后台模式下运行,您可以将终端用于其他任务。

docker-compose up -d


[+] Running 7/7
 ⠿ nginx Pulled
    ...
[+] Running 2/2
 ⠿ Network php-docker-demo_default    Created
 ⠿ Container php-docker-demo-nginx-1  Started

在执行命令后,成功地获取了Nginx镜像(下载),创建了一个名为php-docker-demo_default的网络,并且成功地启动了一个名为php-docker-demo-nginx-1的容器。

网络容器是独立的环境,所以要让容器彼此通信,需要Docker的网络。使用Docker Compose启动容器时,默认情况下会创建一个新的网络。在相同的Compose项目中的容器可以通过这个默认网络进行通信。

Nginx のデフォルトの画面我确认了Nginx已经成功启动,现在我将使用docker-compose down命令停止并删除容器。

docker-compose down

[+] Running 2/2
 ⠿ Container php-docker-demo-nginx-1  Removed
 ⠿ Network php-docker-demo_default    Removed
下のコマンドは、Docker Composeで作成したネットワークを停止し、関連するリソース(後述のボリュームなど)を削除するだけでなく、コンテナも停止させます。

PHP-FPM 容器接下来,我们来描述创建 PHP-FPM 容器所需的配置。

PHP-FPM 是与 Nginx 协同运行的应用服务器,负责提供和处理动态内容,这是 Nginx 单独无法实现的。具体而言,它执行 PHP 脚本并将结果作为 HTML 输出。

PHP-FPM配置在创建Nginx容器时,我使用了从Docker Hub获取的现有镜像。对于PHP-FPM容器,我基于自定义镜像,使用Dockerfile以现有镜像为基础进行构建。

创建DockerFile首先,创建并编辑一个DockerFile。

DockerFile应该在php-docker-demo/docker/php目录中创建。

# イメージを取得
FROM php:8.1.18-fpm
# 独自のphp.iniファイル(PHPの設定ファイル)を 
# コンテナ内の/usr/local/etc/php/ディレクトリにコピー
COPY php.ini /usr/local/etc/php/

# パッケージやPHPの拡張モジュールをインストールするコマンド を実行
RUN apt-get update && apt-get install -y \
	git \
	curl \
	zip \
	unzip \
    && docker-php-ext-install pdo_mysql

# 作業ディレクトリを/var/wwwに設定
WORKDIR /var/www
    1. 从基础映像指定。

将主机上的php.ini文件(稍后创建)复制到容器内的/usr/local/etc/php/目录中。

在RUN之后,写下想要执行的命令。

执行apt-get update命令以更新软件包列表。

安装apt-get安装软件包。

-y选项是用于在安装时不显示确认提示的选项。

使用docker-php-ext-install安装PHP应用程序连接到MySQL的扩展模块。

\用于在多行中编写命令时使用。

&&用于仅在前一个命令成功的情况下执行下一个命令。

通过使用WORKDIR将工作目录设置为/var/www。

指定WORKDIR后,接下来的所有命令(包括DockerFile中的CMD和COPY等)都将在指定的目录(在此情况下为/var/www)中执行。

创建一个php.ini文件。创建一个文件,用来描述PHP的运行和功能设置的php.ini文件。文件内的描述是参考Laravel Sail的php.ini文件。
有关php.ini文件的详细信息,请参考PHP:配置文件 – 手册和PHP:php.ini指令列表 – 手册。

[PHP]
post_max_size = 100M
upload_max_filesize = 100M
variables_order = EGPCS

创建一个 PHP 文件在项目文件夹中创建一个名为src的文件夹,并将index.php放置在其中。

<?php

echo '<h1>Hello World</h1>';

编辑docker-compose.yml文件

version: '3'
services:
  nginx:
    image: nginx:1.25.0
    ports:
      - 8000:80
    volumes:
      # ./srcフォルダをコンテナ内の/var/www/htmlにマウント
      - ./src:/var/www/html
      # ./docker/nginxフォルダをコンテナ内の/etc/nginx/conf.dにマウント
      - ./docker/nginx:/etc/nginx/conf.d
    # 依存関係を設定
    depends_on:
      - app
  # PHP-FPMの定義
  app:
    build:
      # Dockerfileを格納するフォルダのパス
      context: ./docker/php
      # Dockerfileのファイル名
      dockerfile: Dockerfile
    volumes:
      # ./srcフォルダをコンテナ内の/var/www/htmlにマウント
     - ./src:/var/www/html   
  # mysql:
    1. nginx 配置的步骤:

在 nginx 中添加 volumes。

使用”主机路径:容器路径”的格式,进行绑定挂载(将主机上的文件或目录与容器内部关联)。

将自定义的 default.conf 文件(稍后创建)放置在 nginx 目录中的 conf.d 目录中。

在 nginx 中添加 depends_on,以设置 nginx 容器在启动之前启动 app 容器的依赖关系。
如果 app 容器未启动,nginx 容器将无法将请求转发给(位于 app 容器内的)Web 应用程序。

app 配置的步骤:

在 services 指令中以 app 为名称进行定义。(可使用任意名称进行定义)

添加 build 到 app 键中,并在 build 键中定义包含 Dockerfile 的文件夹路径和 Dockerfile 文件名。

添加 volumes 到 app 键中。

“./src:/var/www/hml”。

Docker容器在独立的环境中运行,因此在容器内创建的文件在容器被删除时也会被删除。此外,无法直接从容器外部访问容器内的文件,也无法使用主机上的编辑器(如VSCode)来编辑容器内的文件。

通过使用Docker的绑定挂载功能,可以将主机上的文件或目录与容器关联起来,从而可以使用主机上的编辑器编辑容器内的文件,并与其他容器或主机共享数据。

创建nginx的配置文件。最初的配置文件位于Nginx容器内的/etc/nginx/nginx.conf和/etc/nginx/conf.d/default.conf。

如果您想检查文件的内容,请使用docker-compose exec nginx cat [文件路径]命令。
nginx.conf文件中记录了Nginx的基本配置,并设置了加载default.conf文件的配置。

在default.conf中,记录了默认的Web服务器配置等内容,但没有进行与PHP-FPM连接的配置。

因此,我们将创建一个独立的default.conf文件。在docker目录中创建一个nginx目录,并将default.conf文件放置在其中。(已经在docker-compose.yml中配置了将./docker/nginx文件夹挂载到容器内的/etc/nginx/conf.d目录中)

我们将参考 “PHP FastCGI Example | NGINX” 来编写与将NGINX连接到PHP-FPM的设置的默认配置文件default.conf。

server {
  # port 80 で listen
  # docker-compose.ymlでホストマシン上のポート8000を経由するすべてのリクエスト
  # がコンテナ内のポート80にリダイレクトするように設定済み
  listen 80;
  # ドキュメントルートを設定
  # /var/www/htmlはソースコードを配置しているフォルダ
  root /var/www/html;
  # インデックスファイルを設定
  index index.php;

  location / {
    root /var/www/html;
    index index.php;
    try_files $uri $uri/ /index.php?$query_string;
  }

  location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    if (!-f $document_root$fastcgi_script_name) {
        return 404;
    }
    # https://httpoxy.org/ の脆弱性を緩和する
    fastcgi_param HTTP_PROXY "";
    # TCPソケットを使用してPHP-FPMと通信するための設定
    fastcgi_pass app:9000; 
    # スラッシュで終わるURIの後に追加されるファイル名を設定
    fastcgi_index index.php;
    # fastcgi_paramsファイルに含まれるFastCGIパラメータの設定を読み込む
    include fastcgi_params;
    # SCRIPT_FILENAME パラメータは、PHP-FPM がスクリプト名を決定する際に使用する
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  }
}

我来解释一下Docker特有的设置,即fastcgi_pass app:9000;。

Nginx和PHP-FPM可以通过TCP套接字或Unix域套接字进行通信,本次我们将使用TCP套接字。

fastcgi_pass app:9000; 是一个用于通过TCP套接字进行通信的配置。它意味着将请求发送到端口号为9000的FastCGI服务器(PHP-FPM)。

当使用Docker时,fastcgi_pass不是使用127.0.0.1:9000,而是使用服务名:9000。在本文中,将docker-compose.yml文件中PHP-FPM服务的名称设置为app。

请参考以下内容,关于其他设置我不详细解释。

    • Module ngx_http_index_module – NGINX

 

    • Module ngx_http_core_module – NGINX

 

    • Alphabetical index of variables – NGINX

 

    • Module ngx_http_fastcgi_module – NGINX

 

    How to set up PHP, PHP-FPM and NGINX on Docker in Windows 10 + [Tutorial Into] | pascallandau.com

运行docker-compose文件使用docker-compose up命令来启动容器。

使用build选项可以在启动容器之前重新构建docker-compose.yml文件中定义的服务的镜像。

docker-compose up -d --build

请通过主机上的Web浏览器访问 http://localhost:8000,并确保显示了写有”Hello World”的页面。

由于确认Nginx和PHP-FPM之间能够正常协作,因此我们可以使用docker-compose down命令来停止并删除容器。

docker-compose down

MySQL 容器最后,我们将描述创建MySQL容器的配置。

MySQL 数据库
MySQL 是一个关系型数据库管理系统(RDBMS)之一。
在 Web 应用程序中,关系型数据库负责数据的持久化和管理。我们可以在数据库中创建表和关系,进行存储、搜索、更新和删除等操作。

MySQL 的设置和创建Nginx容器时一样,使用从Docker Hub获取的现有镜像。

编辑docker-compose.yml文件

version: '3'
services:
  nginx:
  # ...
  app:
    build:
      context: ./docker/php
      dockerfile: Dockerfile
    # コンテナ内で使用される環境変数を定義
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - ./src:/var/www/html
    # 依存関係を設定
    depends_on:
      - mysql
  # MySQLの定義
  mysql:
    # MySQL コンテナに使用するイメージを指定
    image: mysql:8.0
    # コンテナ内で使用される環境変数を定義
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      # 名前付きボリュームを MySQL コンテナに紐づける
      - mysqldata:/var/lib/mysql
      - ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
      # - ./docker/mysql/sql:/docker-entrypoint-initdb.d
    ports:
      - 3306:3306
volumes:
  # 名前付きボリュームの作成
  mysqldata:
    1. app 的设置

使用 environment 键来定义容器中使用的环境变量。在这里,我们将 MySQL 的根密码、数据库名称、用户名和密码指定为环境变量,以便在 PHP 文件中使用。我们使用 ${环境变量} 来引用在其他地方(.env 文件)定义的变量的值。

在 app 内部添加 depends_on,以设置依赖关系,使得 app 容器在 mysql 容器启动之前启动。如果 app 容器中不存在 mysql 容器,(位于 app 容器内的)Web 应用将无法请求数据库。

MySQL 的设置

在 services 指令内以 mysql 的名称进行定义。
(可以使用任意名称进行定义)
在 image 内指定用于 MySQL 容器的镜像。

使用 environment 键来定义容器内使用的环境变量。在这里,我们指定了 MySQL 的根密码、数据库名称、用户名和密码作为环境变量。
在 app 键内添加 volumes

mysqldata:/var/lib/mysql
命名卷(用于持久化数据)

./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
绑定挂载(将主机上的 my.cnf 与容器内的 my.cnf 关联起来)

使用 ports 键进行端口设置

设置命名卷

添加 volumes。
写入卷名称(此处为 mysqldata)。

(Note: There may be better ways to translate certain technical terms in Chinese. Please consult with a fluent Chinese speaker or a technical specialist for a more accurate translation.)

在Docker容器中运行时,Docker容器是在一个独立的环境中运行的,因此当容器被删除时,容器内保存的数据也会丢失。因此,如果需要永久保存数据,则需要将数据放置在容器的外部。

“名字卷” 是将在Docker上分配的存储空间(卷)挂载(关联)到容器内的特定路径上。使用名字卷,即使删除容器,容器内保存的数据也不会被删除。此外,在多个容器之间共享数据时也非常方便。

相关:匿名卷

创建my.cnf文件在docker文件夹中创建mysql文件夹,并创建my.cnf文件,my.cnf是MySQL的配置文件。

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci

将MySQL服务器的默认字符集设置为utf8mb4,并将排序规则设置为utf8mb4_general_ci。

请参考MySQL :: MySQL 8.0 参考手册 :: 4.2.2.2 使用选项文件以获取详细信息。

创建.env文件在项目的根目录下创建一个.env文件,并在MySQL容器中添加环境变量。MYSQL_ROOT_PASSWORD是必需的环境变量。
更多关于在MySQL容器中使用的环境变量的详细信息,请参考mysql – Official Image | Docker Hub的Environment Variables。

Docker Compose 默认自动读取与 docker-compose.yml 文件相同目录下的 .env 文件。

MYSQL_ROOT_PASSWORD=password
MYSQL_DATABASE=php-docker-db
MYSQL_USER=user
MYSQL_PASSWORD=password

执行 docker-compose 文件
使用docker-compose up命令来启动容器。

docker-compose up -d --build

使用bash命令确认MySQL容器是否正常运行。

# 上記のコマンドを実行すると、MySQLコンテナのBashシェルが起動
# これにより、コンテナ内部でコマンドを実行することができます
docker-compose exec mysql /bin/bash

# コンテナ内にMySQLがインストールされているか確認
bash-4.4# mysql --version
mysql  Ver 8.0.33 for Linux on aarch64 (MySQL Community Server - GPL)

# mysql -u root -p もしくは
# mysql -u <ユーザ名> -p (<ユーザ名>はMYSQL_USERで設定した値)
bash-4.4# mysql -u root -p
# rootの場合はMYSQL_ROOT_PASSWORD、
# <ユーザ名>の場合はMYSQL_PASSWORDで設定した値を入力
Enter password: 

Welcome to the MySQL monitor.  Commands end with ; or \g.
...
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

# データベースの一覧を表示
mysql> SHOW databases;
# MYSQL_DATABASEで設定した値(今回はphp-docker-db)があるか確認
+---------------------+
| Database            |
+---------------------+
| information_schema  |
| performance_schema  |
| php-docker-db             |
+---------------------+
3 rows in set (0.04 sec)

# USE データベース名; でデータベースを切り替え
mysql> USE php-docker-db;
Database changed

# 現在のデータベース内のテーブルの一覧を表示
mysql> SHOW TABLES;
# まだテーブルを作成していないので空
Empty set (0.01 sec)

确认MySQL容器正常运行后,接下来将确认PHP-FPM和MySQL是否能够正常协同工作。

在数据库中创建一个新表,并插入记录。

# データベースに新しいテーブルを作成
# CREATE TABLE テーブル名 ( 列名1 データ型1, 列名2 データ型2, ...);
mysql> CREATE TABLE mytable (id int, name varchar(255));
Query OK, 0 rows affected (0.07 sec)

# テーブルが作成されているか確認
mysql> SHOW TABLES;
+-------------------------+
| Tables_in_php-docker-db |
+-------------------------+
| mytable                 |
+-------------------------+
1 row in set (0.01 sec)

# レコードを挿入
mysql> INSERT INTO mytable (id, name) values (1, 'John Doe');
Query OK, 1 row affected (0.11 sec)

# レコードを取得
mysql> SELECT * FROM mytable;
+------+----------+
| id   | name     |
+------+----------+
|    1 | John Doe |
+------+----------+
1 row in set (0.01 sec)

创建一个名为 db.php 的文件,并在 src 文件夹内,编写从MySQL获取数据的代码。

<?php

$host = 'mysql'; // MySQLコンテナのサービス名
$dbname = $_ENV['MYSQL_DATABASE'];
$username = 'root';
$password = $_ENV['MYSQL_ROOT_PASSWORD'];

# 新しいPDOオブジェクトを作成し、MySQLデータベースに接続
$db = new PDO("mysql:host={$host};dbname={$dbname};charset=utf8", $username, $password);

# SQL 文を実行
$stmt = $db->prepare('SELECT * FROM mytable');
$stmt->execute();

$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

foreach ($results as $result) {
    echo $result['id'] . '. ' . $result['name'] . PHP_EOL;
}

请从主机的Web浏览器访问 http://localhost:8000/db.php,确保屏幕上显示出姓名为John Doe的信息。

现在,我们可以确认PHP-FPM和MySQL已经成功协同工作了。

# exitコマンドでMySQLへの接続を終了
mysql> exit
Bye

# exitコマンドでBashシェルセッションを終了
bash-4.4# exit
exit

由于确认PHP-FPM和MySQL可以正常协同工作,因此使用docker-compose down命令停止并删除容器。

docker-compose down

最后
在本文中,我们使用Docker Compose创建了Nginx容器、PHP-FPM(+ PHP)容器和MySQL容器,搭建了PHP应用程序的开发环境。

如果你想要详细了解Docker,以下的文章可能会对你有所帮助。

 

此外,我也创建了Nginx、PHP和MySQL的配置文件。如果您想要自定义配置文件,请参考我在创建每个配置文件时介绍过的官方网站文档。

请引用这篇文章。 

bannerAds