如何用Docker建立一个现代的PHP开发环境

265 阅读14分钟

在这篇文章中,我将阐述如何最好地开始建立一个PHP开发环境,指导你如何使用Docker进行设置。建立PHP开发环境的方法有很多,但使用Docker是目前最好的做法。

我将从多年来人们如何建立他们的PHP开发环境的简要历史开始,以达到我们现在的目的。但如果你想跳过这些,只想让你的服务器运行,你可以直接跳到配置步骤

一个小小的背景

网络开发的一个问题是,事情变化的速度很快。CSS的最佳实践随着新的属性被添加到规范中而改变。(如果没有CSS Grid,我们是如何应对的?)PHP现在已经是第8版了,甚至我们用来执行PHP脚本的工具也在不断完善。因此,很多教程很快就过时了。

直到几年前,我把我教的每个人都送到Bruno Skvorc的优秀文章Re-introducing Vagrant。开始使用 PHP 的正确方法。在当时,这篇文章是对(当时)建立本地开发环境的最佳方式的精彩介绍。

那篇文章只是从2015年开始的,但在不断变化的Web开发时间尺度中,五六年是一个漫长的过程。从那时起,"正确的方法 "已经有了相当大的发展。

我将快速回顾一下这些年来事情的变化。

1.手动安装PHP、MySQL和Apache

如果你像我一样,在90年代开发过网站,你会记得那是多么令人沮丧的经历。那时,如果你是少数人,没有在实时网络服务器上开发(是的,我们真的这样做了,是的,这是个糟糕的主意),你会在你的开发机器上手动安装Apache、PHP和MySQL。

建立一个开发环境需要大量的专业知识。你需要知道如何配置Web服务器,如何配置PHP,而且你必须经历手动安装和配置所有你使用的软件的过程。对于新手开发者来说,这本身就是一项耗时而艰巨的任务。

2.预配置的软件包,如XAMPP

到了2000年早期到中期,人们已经开始把所有需要的软件放在一个单一的软件包中,这个软件包可以安装和配置所有你需要的软件。这些软件包是像XAMPP和WAMP这样的东西,只要点击一下按钮,就能给你一个可用的开发环境。

如果你在各种PHP的FACEBOOK群里闲逛,你会发现相当一部分新的开发者仍然遵循这个时代的教程,而且大量的现有开发者也没有继续前进,所以XAMPP仍然被广泛使用。如果这是对你的描述,是时候继续前进了。

使用XAMPP使得在你的机器上建立和运行一个Web开发环境变得非常容易。布鲁诺的文章概述了这种方法的问题,但当你想把你的网站放在网上时,主要的问题就来了。PHP、MySQL和Apache(或NGINX)的版本可能与你作为XAMPP包的一部分安装的版本不同。此外,Windows和Linux之间有一些微小的、但令人沮丧的差异。如果你在Windows机器上开发你的网站,并把它上传到Linux服务器上,你的一些代码可能在上传后根本无法工作。

3.虚拟机和Vagrant

在2000年代末和2010年代初,开发人员的趋势是转移到虚拟机上。这个想法是,你可以运行一个真正的网络服务器操作系统的副本,并安装所有的程序--与你最终要部署网站的实际网络服务器的配置和设置完全一样。这样一来,当你的网站上线时,就不可能出现不工作的情况。

虽然许多程序员看到了这种环境的好处,但设置这种环境所需的难度和时间意味着很少有人这样做。直到Vagrant(以及Puphpet等相关工具)的出现,将所有的麻烦都解决了。

请看我前面链接的文章,其中对Vagrant、虚拟机和以这种方式建立开发环境的好处有很好的描述。

4.Docker

所有这些背景给我们带来了今天和这篇文章的原因。如果Vagrant这么好,为什么要用别的东西来代替呢?

使用Vagrant设置的虚拟环境的主要好处是。

  1. 你的开发电脑不会被束缚在一个特定的环境中。你可以托管多个网站:一个使用Apache,一个使用NGINX,一个使用PHP7和一个使用PHP8。

  2. 当网站上线时,网站被上传到与开发时完全相同的环境中。

这很容易理解为什么开发人员想要这样做。下一步升级到Docker,可以保持这些优点,同时避免Vagrant/虚拟机环境的一些缺点。

Vagrant有什么问题?

尽管有这些好处,基于Vagrant的开发环境还是引入了自己的一系列限制和问题。

  1. 系统资源。Vagrant需要运行一个完全不同的操作系统。你需要下载并安装正在你的网络服务器上运行的操作系统,以及它所安装的所有软件包。这需要消耗大量的磁盘空间和内存。一个虚拟机通常需要至少512MB的内存。这对今天的计算机来说不是很多,但它很快就会增加。如果你想用PHP7托管一个网站,用PHP8托管一个网站,你需要在电脑上安装和配置两个不同的虚拟机实例。

  2. 你必须确保虚拟机和服务器是同步的。每当你更新服务器或改变服务器的配置时,你必须记得用同样的改变来更新你的本地开发环境。

  3. 它把你紧紧地锁在一个服务器操作系统和配置中。把一个网站从一个服务器移到另一个服务器上是一个困难的任务。一个网站不仅仅是由PHP脚本、图像和CSS组成的。一个特定的服务器配置(如安装的PHP扩展和nginx.conf/httpd.conf )也是网站正常运行所需要的。

  4. 可用的软件包的选择非常有限。根据你的 web 服务器所运行的 Linux 发行版,你可能无法选择运行哪个版本的 PHP。除非从第三方软件库中安装软件包,否则无法使用最新、最先进的 PHP 版本。在写这篇文章的时候,PHP 8 最近已经上市了。如果你使用 CentOS 8/RHEL 8,在得到新版本的操作系统之前,你只能使用 PHP 7.3。如果你用的是 Debian,最新的可用版本是 7.3。其他发行版会有不同的版本可用。

  5. 服务器的配置是全局的。PHP 有一个名为php.ini 的设置文件。改变这个文件会将更新后的配置应用到服务器上的每个网站。NGINX的nginx.conf 或Apache的httpd.conf 也是如此。MySQL数据库实例拥有服务器上所有网站的数据库。进行任何大规模的数据库配置改变都是深远的。更新一个MySQL设置将影响到使用该MySQL服务器的每个网站!

  6. 包的版本在真实服务器上是全局的。虽然可以在同一台服务器上运行多个PHP版本,但这很难配置,而且可能会产生奇怪的副作用,这取决于你的脚本在做什么(比如当你有一个脚本想在systemd单元/cronjob中运行,却忘记了应该使用/bin/php72 ,而不是/bin/php )。

虽然第5点和第6点可以通过运行不同的Vagrant虚拟机在开发机器上克服,但你需要一个真正的Web服务器来反映你正在运行的每一个配置,这样当你上传网站时就能正常工作。

介绍一下Docker

Docker解决了上面列出的所有问题。但究竟什么是Docker,它是如何工作的?

让我们从维基百科的介绍开始。

Docker是一套平台即服务(PaaS)产品,它使用操作系统级的虚拟化来提供被称为容器的软件包。容器之间相互隔离,并捆绑自己的软件、库和配置文件;它们可以通过明确定义的通道相互通信。

在谈及太多技术问题之前,作为网站开发者,Docker给我们带来的实际好处是,我们可以将网站需要的一切打包,所有的PHP代码连同PHP可执行文件、MySQL服务器和NGINX服务器,以及这些程序使用的配置文件。

所有网站的代码,以及运行这些代码所需的程序的确切版本,都被打包在一起,有效地成为一个单一的应用程序。然后,这整个应用程序可以在任何操作系统上运行。当有人运行打包后的应用程序时,PHP、MySQL、NGINX和你写的所有PHP文件都被嵌入到应用程序本身。更好的是,确切的MySQL、NGINX和PHP版本是软件包的一部分。当你运行应用程序时,应用程序所开发的这些工具的确切版本被下载和安装。

"这不就是虚拟机已经做的事情吗?"我听到你问。是的,但Vagrant和Docker处理软件安装的方式有很大区别。

通过Vagrant,运行一个虚拟机,完整的操作系统与特定的PHP版本、MySQL版本和(通常)服务器配置是从真正的Web服务器上克隆出来的。当服务器被更新时,虚拟机也必须被更新。

然而,当使用Docker时,PHP/MySQL/NGINX版本被作为一个单独的软件包提供,称为镜像,服务器可以根据你的需要运行不同的镜像。

这里的好处是,Web服务器和你的开发机器都在运行完全相同的镜像。你只需将你的图像上传到网络服务器,在那里运行整个应用程序,你的网站就可以运行了,根本不需要任何网络服务器配置。

此外,每个图像与服务器上的其他图像是完全分开的。每个图像(在这个简化的例子中每个网站一个)都是相互独立的。每个网站将有自己的NGINX配置,自己的php.ini ,以及自己的PHP和MySQL安装。每个网站可以运行完全不同的PHP版本。你甚至可以让一个网站在Apache上运行,一个网站在NGINX上运行,同时在同一台机器上。即使你在运行两个不同的NGINX网站,你也会有两个不同的NGINX进程,有各自的配置,同时运行。

这有一个小的内存开销,但它所赋予的灵活性使这是一个非常值得的权衡。

  1. 整个网站,包括所需的PHP/MySQL版本,所有的配置和所有的代码都可以轻松地移动。把网站移到一个新的服务器上只需要复制一个文件夹。你不需要对新服务器上的PHP或NGINX配置做任何改变。你甚至不需要在服务器本身安装PHP或NGINX。当你启动应用程序时,它们会被Docker自动安装。

  2. 你可以在你的开发机器上运行完全相同的镜像。使用Vagrant,你实际上是在同一台机器上运行服务器的配置/安装的软件包的副本。使用Docker,在你的个人电脑上运行完全相同的镜像进行开发,就像在服务器上运行一样。

  3. php.ini 调整、 配置变化或将PHP更新到最新版本的处理方式与将更新的PHP代码上传到服务器上是一样的。你更新nginx.conf 应用程序,不管是改变一些PHP代码还是更新 ,都没有关系。php.ini

  4. 每个镜像都是自成一体的,被称为 "容器"。在一个镜像中运行的PHP脚本不能访问在另一个镜像中运行的文件。想想看open_basedir ,但要严格得多。容器就像一个非常轻量级的虚拟机。它就像它自己的操作系统,在容器中运行的代码甚至不知道它是在容器中运行的,而不能看到容器外的任何东西。如果你的一个PHP脚本不安全,给了别人有效的shell访问权,他们只能访问你给容器访问权的服务器上的文件。

  5. 与虚拟机不同,如果两个不同的网站在完全不同的容器中,但使用相同的NGINX或PHP版本,磁盘空间和内存在两个容器之间共享。

  6. 因为每个镜像都是独立的,所以将网站移动到不同的服务器是很容易的。该应用程序不依赖服务器上安装的PHP版本,也不关心服务器上安装了什么包。如果你想把一个Docker化的应用程序转移到不同的服务器上,就像复制所有的网站文件和启动应用程序一样简单。

  7. 你可以在服务器上运行任意多的Docker镜像,每个镜像都有自己的PHP版本、Web服务器软件、数据库和相关文件。

设置东西

这就是理论上的问题了。现在让我们开始使用Docker创建一个服务器。

前提条件

在我们开始之前,你需要下载并安装Docker。前往Docker网站,然后为你的操作系统下载并安装它

如果你使用的是Linux,你应该通过你的发行版的软件包管理器安装dockerdocker-compose 包。根据你的发行版,你可能需要。

  1. 如Docker手册中所述,将你的用户添加到docker 组。

  2. 启动docker 服务systemctl start docker.service 并通过systemctl enable docker 启用它。

如果你是在Windows或macOS上,安装程序会帮你做这些。

其次,因为我们将在Docker中运行一个Web服务器,并转发一些端口,如果你的机器上已经有一个Web服务器(Apache、NGINX、XAMPP、IIS等)或MySQL在运行,请在继续之前停止它们。

开始使用

一个Web服务器通常由多个不同的程序组成--比如NGINX、PHP和MySQL。用Docker的术语来说,你想安装的每个程序都是一个服务

在Docker中,有几种创建这些服务的方法。我将介绍最方便用户的方法。Docker支持使用YAML(Yet Another Markup Language)创建一个配置文件。

虽然你可以在命令行上键入所有的选项,但我推荐使用YAML配置文件,原因有以下几点。

  1. 它更容易阅读/理解。

  2. 你不必在每次运行服务器时重新输入几个长的命令。

  3. 你可以用Git来跟踪文件的变化。

docker-compose.yml 用于NGINX

Docker提供了一个名为docker-compose 的工具,它接收一个名为docker-compose.yml 的配置文件,并启动其中列出的服务。让我们开始添加一个网络服务器,NGINX。

首先,在你的电脑上某个地方创建一个文件夹,用来存放你的网站。你将需要定期回到这个文件夹,所以要记住它的位置。创建docker-compose.yml ,内容如下。

version: '3'
services:
    web:
        image: nginx:latest
        ports:
            - "80:80"

让我们一行一行地看一下这个配置。

version: '3'

这告诉docker-compose 使用哪个版本的YAML规范。3 是最新的,不同版本的规范、关键字和结构略有不同。

下一行,services: ,后面将是你想运行的所有服务的列表。

在我们的例子中,到目前为止,只有一个名为web 的服务(你可以叫它任何你喜欢的名字),使用官方的NGINX镜像nginx:latest 。请注意,使用空格(不是制表符!)的缩进很重要。YAML 依赖于嵌套级别来决定文件的结构。

如果你想指定一个不同的NGINX版本,你可以像这样在这里指定。

version: '3'
services:
    web:
        image: nginx:1.18.0
        ports:
            - "80:80"

我建议使用latest ,除非你有充分的理由使用更早的版本。

ports 块设置了端口转发。它把本地机器上的80 转发到镜像上的80 。主机上对http://127.0.0.1 的任何请求将被转发到运行在容器中的NGINX服务器。

继续阅读:在SitePoint 上用Docker建立一个现代的PHP开发环境