docker 怎么写php环境的镜像(DockerPHP入门实践)
在连接数据库之前,我们需要确保我们的PHP容器已安装所有必要的扩展。默认情况下,Docker Hub 上的 PHP 镜像都是非常轻量级的,因此它不包含您可能需要的许多PHP扩展或Linux包。您必须根据项目优先级在较小或者更灵活的镜像之间进行权衡,但是我们知道该项目需要用到MySQL,因此让我们在 PHP 镜像中安装所需的拓展吧
使用基础的 PHP 镜像通常是一个很好的起点,但是我也可以在 Github 中查找使用启用常见扩展的 PHP 镜像。查看此存储库了解更多信息。
创建自定义Dockerfile进入到我们的天气应用项目的根目录,创建一个的新文件命名为Dockerfile。Dockerfile没有扩展名,用于配置和设置 Docker 镜像。Docker Hub 上的所有镜像都有有相应的 Dockerfile 文件,通常您可以在 GitHub 上找到它们。PHP的dockerfile在这里,但它们可能会有点混乱,因为它们是相互关联的的。我们的 Dockerfile 会简单得多:
DockerfileFROM php:apacheRUN docker-php-source extract && docker-php-ext-install Mysqli pdo pdo_mysql && docker-php-source delete
解疑小课堂此 Dockerfile 有两行:
- FROM php:apache - 这告诉 Docker 在开始构建时选择哪个镜像。你可以从一个Linux发行版开始 (比如Ubuntu 或者超轻 alpine ),或者您也可以自己构建一个相同的镜像。在这里,我们选择php:apache镜像作为我们的运行容器。
- RUN docker-php-source extract... - 这是添加我们需要的 PHP 扩展。在我们的例子中,我们只想使用 mysqli函数 用于操作 MySQL 数据库。由于这是一个非常常见的扩展,当我们选用 PHP 基础镜像时,PHP镜像中有自动添加它的方法。
查看 官方Docker文档 有关扩展现有镜像的更多配置命令。
构建镜像我们需要通过刚刚创建的 Dockerfile 去构建我们所需的镜像:
$ cd E:/workplace/docker-app/weather-app$ docker build . -t sunmking/weather-app
当我们运行此命令构建 Docker 镜像时,将看到以下输出,具体如下:
[ ] Building 0.3s (6/6) FINISHED=> [internal] load build definition from Dockerfile 0.1s=> => transferring dockerfile: 31B 0.0s=> [internal] load .dockerignore 0.1s=> => transferring context: 2B 0.0s=> [internal] load metadata for docker.io/library/php:apache 0.0s=> [1/2] FROM docker.io/library/php:apache 0.0s=> CACHED [2/2] RUN docker-php-source extract && docker-php-ext-install mysqli && docker-php-source delete 0.0s=> exporting to image 0.1s=> => exporting layers 0.0s=> => writing image sha256:d92e846df4b0210c29f84c8f5ec8affdb8a823b6de34a24ac460564f160e6207 0.0s=> => naming to docker.io/sunmking/weather-app
解疑小课堂Docker build 通过刚刚创建的 Dockerfile 构建一个可用于运行容器的镜像。在该命令中,我们使了两个参数:
- . - 点让Docker知道我们构建的 “content” -- 在这种情况下,它是当前目录。您也可以使用绝对路径,如/用户/用户名/天气-应用如果您知道 Dockerfile 的确切路径。Docker会在包含Dockerfile的目录的上下文中自动构建此镜像,因此请确保 Dockerfile 位于项目的根目录。
- -t sunmking/weather-app - 的-t为您的镜像设置一个 “标签”。此标签可让您更轻松地从镜像中创建容器,或将镜像推送到镜像注册中心以与其他人共享。
我们可以在本地计算机上运行 docker images 来查看所有的 Docker 镜像。当我们运行此命令时,可以在终端中看到如下内容:
REPOSITORY TAG IMAGE ID CREATED SIZEsunmking/weather-app latest d92e846df4b0 2 hours ago 477MBdocker101tutorial latest 870f3cf07e24 4 days ago 28.9MBalpine/git latest b80d2cac43e4 12 days ago 43.6MBsnyk/snyk-docker-desktop-extension 0.6.2 d070900ca2ee 6 weeks ago 71.5kBcomposer latest c8d389ce4877 9 months ago 193MBphp apache b4e8e213b0ec 10 months ago 477MBphp latest 13b9b1961ba3 10 months ago 484MBmysql 5.7 c20987f18b13 10 months ago 448MB
运行MySQL容器在介绍中,我提到Docker容器可以通过 Docker的网络功能 方式 "链接 "起来。为了让我们的 PHP 应用程序从数据库中获取数据,我们需要将其链接到可用的数据库容器。OK,让我们l来启动一个新的 MySQL 容器,以便我们可以将web应用程序链接到它:
$ docker run -d --rm --name weather-db -e MYSQL_USER=admin -e MYSQL_DATABASE=weather -e MYSQL_PASSWORD=wt2022@1019 -e MYSQL_RANDOM_root_PASSWORD=true mysql:5.7
解疑小课堂当我们运行上述 docker run 命令时,Docker 将拉取最新版本的 MySQL 5.7镜像 并启动名为weather-db的容器。跟我们运行 PHP 容器一样,MySQL 在 Docker Hub上有很多版本的 Docker 镜像 ,您可以以几乎相同的方式使用它们。上面的命令包含了一些新的参数,让我来给你们介绍一下:
- -d - 这将在 “Detached” 模式下运行容器,这意味着您不用保持终端与容器连接,你可以直接关闭你的终端窗口,你的容器依旧在后台安稳的运行着。
- --name weather-db -命名我们的数据库容器很重要,因为你可以更加方便的通过名称去连接你创建的容器。我们也可以使用这个名称在我们的 PHP 应用程序中连接到 MySQL 数据库,我们将会在后面遇到。
- -e MYSQL_USER=admin - MySQL镜像 包括几个 -e (环境变量) 选项。第一个设置了我们将在PHP应用程序中使用的 MySQL 用户的用户名。有关环境选择的更多信息,请访问Docker Hub官方镜像。
- -e MYSQL_DATABASE=weather -默认情况下,容器不会创建数据库。虽然您可以通过登录容器来手动创建一个 (这将在下一节中介绍),但是像这样预先创建数据库会更加简便。
- -e MYSQL_PASSWORD=wt2022@1019 -这将设置数据库的初始密码。不管我们是不是在本地开发,都应该设置为强密码。
- -e MYSQL_RANDOM_ROOT_PASSWORD=true - 为 root 用户设置一个随机的密码,不管在什么情况下都不要登录 root 账户。
- mysql:5.7 - 与 PHP 镜像一样,您可以选择要使用的MySQL版本。在这里,我选择使用版本5.7。
就像php:apache镜像,默认的镜像就可以了,所以我们不需要在镜像中添加任何内容。
登录到正在运行的容器此时,我们想检查新容器中的数据库是否正常工作,但是如何访问正在运行的 Docker 容器呢?下面我们将使用docker exec命令进入新容器的终端,然后我们将使用 MySQL命令行界面 创建我们所需要的数据库或者数据表。
我们可以使用以下命令进入正在运行的数据库容器的 bash 终端:
$ docker exec -it weather-db bash
你应该看到一条像root@3cad1127aa0d:/#(容器的ID) 在终端上,表示您现在已登录到正在运行的容器。从容器内 (不是本地主机上) 运行:
$ mysql --user=admin --passwordEnter password:
输入您为上面创建的MySQL容器选择的密码 (在本例中为p23l % v11p),然后点击 “返回” 键。您将看到一些关于MySQL的信息,如下所示:
PS E:\workplace\docker-app\weather-app> docker exec -it weather-db bashroot@4c9a8b9ebce0:/# mysql -u admin -pEnter password:Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 2Server version: 5.7.36 MySQL Community Server (GPL)Copyright (c) 2000, 2021, Oracle and/or its affiliates.Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates. Other names may be trademarks of their respectiveowners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql>
最后,让我们查看一下我们的数据库,是用show databases;命令:
mysql> show databases; -------------------- | Database | -------------------- | information_schema || weather | -------------------- 2 rows in set (0.01 sec)
解疑小课堂在本章的的前面部分中,我们登录到正在运行的数据库容器上,然后通过命令行登录到 MySQL,并且查看了有哪些数据库。由于大家可能不熟悉这些命令,我将逐一介绍它们。
Docker Exec- docker exec - 这是用于执行 Docker 命令是我们可以进入在正在运行的容器上。docker exec 还有有许多选项,这里我们就不一一介绍了,大家可以去官方文档中了解。
- -it - -it这两个标志通常是在一起使用的。i表示交互式的,表示[cmd]是一个有用户输入的程序,比如/bin/bash 和 python 等等。-t 产生一个终端。所以说有-i就必须有-t,不然怎么输入呢。这[cmd] 有的镜像是有默认值的,比如centos的镜像的默认值/bin/bash,而python镜像的默认值是python。所以说[cmd]是可以不写的。
- weather-db -这个是我们要在其上运行命令的正在运行的容器的名称 (您也可以使用容器 ID)。
- bash - 最后,这是我们要在容器中运行的命令。bash 是大多数Linux发行版中安装的shell,允许我们在容器上运行其他程序。如果您的容器没有安装bash,您也可以尝试 sh。
MySQL CLI 还提供了许多选项,以下是我们使用的三个选项:
- mysql - 这是用于从终端访问MySQL的命令。如果您一直在VM或本地计算机上使用MySQL,则没有什么不同。
- --user=admin - 我们在此处指定用户名,以便 MySQL 知道我们希望以特定用户身份访问,而不是 root.
- --password -密码标志指示MySQL给我们密码提示。您也可以直接在命令中输入密码,但它通常不太安全。
我们现在已经完成了MySQL容器,所以让我们退出并停止该容器:
- 要退出MySQL CLI,请键入\ q然后按 “Enter”。
- 通过键入退出容器exit然后按 “Enter”。
- 通过键入docker stop weather-db来停止容器, 然后再次 “Enter”。
完成后,整个命令行输出应如下所示:
mysql> \qByeroot@4c9a8b9ebce0:/# exitexitPS E:\workplace\docker-app\weather-app> docker stop weather-dbweather-db
我们现在已经解决了如何运行 MySQL 容器,并且我们可以登录访问数据库,但是我们将如何保存在容器中的数据?当我们停止这个容器时会发生什么?数据的持久性在现实世界的应用中是很重要的,所以在下一节中,我们将深入研究如何在我们的容器持久化保存我们的数据。
保存们数据库容器中的数据到目前为止,我们已经启动了数据库容器并自动创建了一个数据库和用户。这是很不错的开始,但是当我们把这个数据库连接到我们的 PHP 应用程序时,我们要确保数据库的表和值即使在我们的容器停止后也会被保存。否则,每次我们的数据库容器重新启动时,我们将不得不重建数据库和添加数据。
从容器中保存数据用于本地开发的最好方法是挂载一个数据卷。这对我们的 PHP 代码的本地开发是非常有效的,而我们挂载数据库数据卷的过程也非常相似。在我们启动 PHP 容器是也挂载了一个(在我们的例子中,我们之前创建的weather-app 目录),但你可以从本机系统的任何地方挂载卷,我这里把数据挂载在项目的同级目录 .data 中。
现在我们开始学 如何保存 mysql 容器里的数据, 现在进入到 weather-app 项目下执行以下命令:
$ docker run -d --rm --name weather-db -e MYSQL_USER=admin -e MYSQL_DATABASE=weather -e MYSQL_PASSWORD=wt2022@1019 -e MYSQL_RANDOM_ROOT_PASSWORD=true -v E:/workplace/docker-app/.data:/var/lib/mysql mysql:5.7
解疑小课堂我们在这个docker运行命令中添加的唯一内容是 -v E:/workplace/docker-app/.data:/var/lib/mysql 。该命令是把一个卷从我们的本地目录挂载到 MySQL 容器中。当我们运行上面的命令时在(.data/)目录中会出现很多文件和目录。这些是MySQL用来存储数据的文件,你可以从你的终端查看这些文件。
$ ls -a .data/ # Linux OR MAcor$ cd E:/workplace/docker-app/.data/ # win$ dir # win
显示输出如下:
目录: E:\workplace\docker-app\.dataMode LastWriteTime Length Name---- ------------- ------ ----d----- 2022/10/19 11:09 mysqld----- 2022/10/19 11:09 performance_schemad----- 2022/10/19 11:09 sysd----- 2022/10/19 11:10 weather-a---- 2022/10/19 11:09 56 auto.cnf-a---- 2022/10/19 11:09 1676 ca-key.pem-a---- 2022/10/19 11:09 1112 ca.pem-a---- 2022/10/19 11:09 1112 client-cert.pem-a---- 2022/10/19 11:09 1680 client-key.pem-a---- 2022/10/19 14:36 79691776 ibdata1-a---- 2022/10/19 14:36 12582912 ibtmp1-a---- 2022/10/19 11:14 694 ib_buffer_pool-a---- 2022/10/19 14:36 50331648 ib_logfile0-a---- 2022/10/19 11:09 50331648 ib_logfile1-a---- 2022/10/19 11:09 1680 private_key.pem-a---- 2022/10/19 11:09 452 public_key.pem-a---- 2022/10/19 11:09 1112 server-cert.pem-a---- 2022/10/19 11:09 1676 server-key.pem
不要担心这些都是什么意思(尽管如果你有兴趣,你可以阅读一下MySQL的文档)。在这,我们只关心在 MySQL 容器中使用来自我们主机的数据,允许我们在容器关闭后仍能保持数据库数据。
创建数据表由于这个应用程序需要缓存来自 高德天气 API 的结果,所以我们要创建一个 location 表,并添加列id、weather和last_updated。
如果MySQL容器没有运行,那么使用上面的方法启动它,挂载数据卷:
$ docker run -d --rm --name weather-db -e MYSQL_USER=admin -e MYSQL_DATABASE=weather -e MYSQL_PASSWORD=wt2022@1019 -e MYSQL_RANDOM_ROOT_PASSWORD=true -v E:/workplace/docker-app/.data:/var/lib/mysql mysql:5.7
登录到 Mysql 容器并进入到 MySQL CLI 中。这一次,我们将一步到位创建数据表:
$ docker exec -it weather-db mysql --user=admin --password='wt2022@1019' weather
接下来,运行 Mysql SQL 命令来创建我们上述的数据库表:
mysql> CREATE TABLE locations (id VARCHAR(64) NOT NULL, weather JSON NULL, last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
应该会得到反馈 Query OK, 你也可以通过运行 SHOW TABLES;命令来查看创建的表. 然后输入 \q退出 mysql 终端.
解疑小课堂与上次登录 MySQL 命令行不同的是,这次我们把这个过程缩减到只有一个步骤。记住,当运行 docker run或 docker exec 时,后面的参数部分是我们想在容器中运行的命令。这意味着我们不需要先登录到bash会话,再登录到MySQL,我们可以直接登录到 MySQL CLI。
将 PHP 应用程序中的数据保存到数据库现在,我们的数据库已经准备好了,即使在容器被移除后,它也会保存这些数据,我们需要更新我们的PHP应用程序,以连接到数据库并存储天气结果。
最初,我们是直接从 高德天气 API获取数据,但现在我们已经有了一个存储机制(即:我们的数据库),让我们让应用程序将数据保存到数据库,以便减少 API 请求。
首先进入到项目的根目录 运行 cp .example.env .env 并编辑 .env 文件,内容如下:
APP_DEBUG = true[APP]DEFAULT_TIMEZONE = Asia/Shanghai[DATABASE]TYPE = mysqlHOSTNAME = weather-dbDATABASE = weatherUSERNAME = adminPASSWORD = 'wt2022@1019'HOSTPORT = 3306CHARSET = utf8DEBUG = true[LANG]default_lang = zh-cn
然后,我们开始修改我们的业务逻辑,打开项目根目录下的 app/controller/Weather.php 文件,更新如下:
<?phpnamespace app\controller;use app\BaseController;use Clydecn\Amap\Weather as AmapWeather;use think\facade\Db;class Weather extends BaseController{public $key;public $weather;// 初始化protected function initialize(){$this->key = "xxxxxxxxxxxxxxxx"; // 高德 KEY$this->weather = new AmapWeather($this->key);}public function get(){$rid = $this->request->param('location_id', '310000');// 通过 Mysql 查询天气信息$weather = Db::table('locations')->where(['id' => $rid])->find();// 如果查询到了if($weather){//直接返回return json(json_decode($weather['weather'],true),200);}// 通过 API 查询当天天气$res = $this->weather->getLiveWeather($rid);//将查询的结果插入数据库Db::table('locations')->insert(['id'=>$rid,'weather'=>json_encode($res),'last_updated'=>date('Y-m-d H:i:s'),]);return json($res, 200);}public function delete(){// todoecho "location_id is " . $this->request->param('location_id');}}
解疑小课堂在这里我们做了两步操作:
- 添加数据库配置
ThinkPHP 数据库配置,可以在 .env 里面配置,若果有不明白的可以查看 ThinkPHP6.0完全开发手册
- 添加业务逻辑
- 查询数据是否存在,存在就响应已经存在的数据
- 如果数据不存在,就直接请求接口并存储在数据库中,以待下次查询
虽然代码中的注释可能会有帮助,但如果你不熟悉 ThinkPHP6,还是有一些细节值得关注的:
- $rid=$this->request->param('location_id', '310000'); - ThinkPHP6 中获取参数的方法.
- $weather=Db::table('locations')->where(['id' => $rid])->find(); - 数据库查询.
- returnjson(json_decode($weather['weather'],true),200); - 将结果以 json 的格式展现给用户.
有了代码,现在我们需要运行 PHP 容器,链接到我们刚刚启动的MySQL数据库容器:
$ docker run -d --privileged=true --rm --name=weather-app -p 38000:80 -v E:/workplace/docker-app/weather-app/:/var/www/html --link weather-db sunmking/weather-app
解疑课堂此docker run命令有两个新部分:
- --link weather-db -链接名为weather-db的容器。你也可以给链接的容器一个别名在PHP容器中使用,但是我们不需要在这里这样做。
- sunmking/weather-app -我们使用的是本章开头构建的新 Docker 镜像,而不是使用php:apache。这样做的原因是我们需要在自定义 Docker 镜像中添加的 PHP MySQLi PDO PDO_MYSQL扩展。
现在,PHP容器已启动并正在运行,您应该能够导航到位置ID,如下所示:
GET http://localhost:38000/public/index.php/weather/310000
第一次加载此URL时,加载可能需要一两秒钟,但是如果刷新页面,可能会更快的看到结果。我的加载时间不到150毫秒! 这要归功于我们通过将结果保存在数据库中。
删除数据为了从数据库中删除数据,我们将添加第二个路由。添加以后允许用户从数据库中删除添加的数据:
/*** delete*/public function delete(){$rid = $this->request->param('location_id', '310000');// 通过 Mysql 查询天气信息$weather = Db::table('locations')->where(['id' => $rid])->find();// 如果查询到了if($weather){Db::table('locations')->where(['id' => $rid])->delete();//直接返回return json("Location {$rid} deleted.",200);}else{return json("Location {$rid} Not Found.", 404);}}
现在您可以使用ApiPost或postman发送一个delete请求。例如:
DELETE http://localhost:38000/public/index.php/locations/310000
你应该看到一条信息Location 310000 已删除。现在当你做一个GET 再次请求该URL,您将等待更长的时间,因为结果来自 高德天气 API。
如果一切工作正常,你现在应该能够像以前一样访问该应用程序
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com