Docker + php-fpm + PhpStorm + Xdebug

不久前,团队负责人说:伙计们,我希望每个人都可以为我们的战斗项目提供相同的开发环境+我们必须能够调试所有内容-Web应用程序,API请求和控制台脚本,以节省我们的精力和时间。 并帮助我们使用此泊坞窗。

言归正传。 细节剪下。

网络上有许多集装箱化手册,但是如何将其应用于实际战斗发展呢? 对于每个项目,编写您自己的docker-compose.yml? 但是我们所有的项目都通过api相互通信,它们都使用标准技术堆栈:nginx + php-fpm + mysql。

因此,让我们澄清问题的条件:

  1. 我们在一个公司,一个团队中工作,并陪同多个战斗项目。 我们都在Ubuntu + PhpStorm下工作
  2. 对于本地开发,我们希望使用docker以便为每个团队成员拥有相同的开发环境,并且还使新开发人员到达时,他可以快速部署工作环境
  3. 我们想要舒适地进行开发,我们想要发布所有内容:Web应用程序,控制台脚本和api请求。

再一次:我们想将几个工作项目引入docker。

在实战服务器上,使用标准的nginx + php-fpm + mysql捆绑包。 那是什么问题呢?

我们在本地计算机上部署完全相同的环境+ Xdebug,在PhpStorm中配置我们的项目,然后工作。 要进行调试,请打开PhpStorm中的“ tube”,一切正常可用,一切都很好。



所有这些都是正确的-一切开箱即用。 但是,让我们尝试在我们的工作环境中进行观察。

Nginx + php-fpm通过套接字进行通信,xdebug侦听端口9000,默认情况下,PhpStorm侦听端口9000进行调试,一切似乎都很好。 并且,如果我们在PhpStorm中打开了多个应用程序,并且对多个应用程序启用了窃听(“ tube”)功能? PhpStorm会做什么? 他将开始发誓已经检测到Xdebug的新连接,您是否要忽略它?

也就是说,使用PhpStorm中的默认设置,在特定的时间点,我只能启动一个应用程序。 对于所有其他打开的应用程序,必须关闭调试。 该死的,但是很不方便。 我想听所有应用程序进行调试,如果其中之一有断点,那么我希望PhpStorm在需要它的那一行中停止在该应用程序中。

而这需要什么呢? 但是您需要每个应用程序以其自己的Xdebug设置开始。 这样,每个应用程序都可以侦听其端口,查找其服务器,而不是像我们在一个堆中一样拥有所有公共属性。

为此,有一个很棒的码头工人! 我们可以基于一个通用映像(例如php:7.1-fpm)在单独的容器中启动每个战斗应用程序。 借助Docker技术,我们可以以最小的开销隔离应用程序。

好的,让我们在docker下启动我们的战斗项目,在单独的容器中运行每个项目,在PhpStorm中配置每个项目以进行单独调试,一切都应该很好。

而且,哎呀,第一个问题:泊坞窗中的容器以root身份运行,我们通常在本地工作,通常是uid 1000,gid 1000的用户。应用程序在争斗,并且对每个应用程序授予所有权限777的权限不是一种选择。 我们的应用程序在git下,如果我们在本地授予777的权限,则git将记录所有这些内容并将其转移到战斗服务器。

拐杖,这是一个示例php图像:7.1-fpm将被编译。

更新资料


正如社区正确指出的那样,绝对没有必要cru着拐杖。
例如,注释中的 1ntrovert habrozer

初始php图像示例:7.1-fpm(对uid和gid进行了硬编码)
FROM php:7.1-fpm RUN apt-get update && apt-get install -y \ git \ curl \ wget \ libfreetype6-dev \ libjpeg62-turbo-dev \ libmcrypt-dev \ libpng-dev zlib1g-dev libicu-dev g++ libmagickwand-dev libxml2-dev \ && docker-php-ext-configure intl \ && docker-php-ext-install intl \ && docker-php-ext-install mbstring zip xml gd mcrypt pdo_mysql \ && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \ && docker-php-ext-install -j$(nproc) gd \ && pecl install imagick \ && docker-php-ext-enable imagick \ && pecl install xdebug \ && docker-php-ext-enable xdebug ADD ./php.ini /usr/local/etc/php/php.ini RUN wget https://getcomposer.org/installer -O - -q \ | php -- --install-dir=/bin --filename=composer --quiet RUN usermod -u 1000 www-data && groupmod -g 1000 www-data WORKDIR /var/www USER 1000:1000 CMD ["php-fpm"] 



更正的Dockerfile示例

 FROM php:7.1-fpm ARG USER_ID ARG GROUP_ID RUN apt-get update && apt-get install -y \ git \ curl \ wget \ libfreetype6-dev \ libjpeg62-turbo-dev \ libmcrypt-dev \ libpng-dev zlib1g-dev libicu-dev g++ libmagickwand-dev --no-install-recommends libxml2-dev \ && docker-php-ext-configure intl \ && docker-php-ext-install intl \ && docker-php-ext-install mbstring zip xml gd mcrypt pdo_mysql \ && pecl install imagick \ && docker-php-ext-enable imagick \ && pecl install xdebug-2.5.0 \ && docker-php-ext-enable xdebug ADD ./php.ini /usr/local/etc/php/php.ini RUN wget https://getcomposer.org/installer -O - -q \ | php -- --install-dir=/bin --filename=composer --quiet RUN usermod -u ${USER_ID} www-data && groupmod -g ${GROUP_ID} www-data WORKDIR /var/www USER "${USER_ID}:${GROUP_ID}" CMD ["php-fpm"] 


从此图像启动容器时,www数据用户将获得uid = 1000,gid = 1000。 通常,在Linux上创建的第一个用户拥有这些权限。 并且,正是凭借这样的权利,我们的php-fpm容器才能工作。 如果有人告诉我如何在不使用Docker访问权的情况下工作,我将非常感激。

从该图像启动容器时,www数据用户将获得uid和gid,它们将从外部进行传输。

在评论中还提出了一个话题 :为什么要更改www-data用户的权限,为什么标准权限不适合33。只有一件事:当我们进入容器并创建例如迁移文件时,我们将不是主机上文件的所有者。 而且每次都需要运行类似
  sudo chown -R user:user ./ 


第二个小问题是:要使Xdebug正常工作,必须为主机注册正确的IP地址。 团队的每个成员都是不同的。 127.0.0.1无法滚动。 在这里,码头工人本人将向我们提供帮助。 例如,我们可以显式配置网络-192.168.220.0/28。 然后我们的机器将始终具有地址192.168.220.1。 我们将使用该地址来配置PhpStorm以及配置其他应用程序。 例如,使用MySql时。

在考虑了注释之后,Docker-compose.yml本身看起来像这样:

 version: '3' services: php71-first: build: context: ./images/php71 args: - USER_ID - GROUP_ID volumes: - ./www:/var/www - ./aliases/php71/bash.bashrc:/etc/bash.bashrc environment: XDEBUG_CONFIG: "remote_host=192.168.220.1 remote_enable=1 remote_autostart=off remote_port=9008" PHP_IDE_CONFIG: "serverName=first" networks: - test-network php71-two: build: context: ./images/php71 args: - USER_ID - GROUP_ID volumes: - ./www:/var/www - ./aliases/php71/bash.bashrc:/etc/bash.bashrc environment: XDEBUG_CONFIG: "remote_host=192.168.220.1 remote_enable=1 remote_autostart=off remote_port=9009" PHP_IDE_CONFIG: "serverName=two" networks: - test-network nginx-test: image: nginx volumes: - ./hosts:/etc/nginx/conf.d - ./www:/var/www - ./logs:/var/log/nginx ports: - "8080:80" depends_on: - php71-first - php71-two networks: test-network: aliases: #         . ,    api - first.loc - two.loc # mysql: # image: mysql:5.7 # ports: # - "3306:3306" # volumes: # - ./mysql/data:/var/lib/mysql # environment: # MYSQL_ROOT_PASSWORD: secret # networks: # - test-network networks: test-network: driver: bridge ipam: driver: default config: - subnet: 192.168.220.0/28 


我们看到在此配置中,基于一个php映像:7.1-fpm创建了两个容器php71-first和php71-two。 每个容器都有自己的Xdebug设置。 每个单独的容器都将侦听其端口和服务器以进行调试。

另外,我请您注意这些指令
  args: - USER_ID - GROUP_ID 


没有这些变量,php-fpm映像将无法启动。 问题:如何将它们传递给docker-compose.yml? 答:因为它对您来说更方便。 您可以在启动时:
 USER_ID=$(id -u) GROUP_ID=$(id -g) docker-compose up -d 

您可以在.env文件中写入这些变量,该文件与docker-compose.yml文件位于同一级别
USER_ID=1000
GROUP_ID=1000

我更喜欢带有.env文件的版本。 当然,您可以使用Makefile。 如您所愿。

演示版本的完整代码发布在github上

清单演示项目:



简要查看项目清单。

别名-> php71-> bash.bashrc目录。 有争议的时刻。 我更喜欢通过别名与php-fpm容器通信。

该文件转发到docker-compose.yml:-./aliases/php71/bash.bashrc:/etc/bash.bashrc
Linux标准工具。

hosts目录-Nginx的配置文件。 每个配置都有其自己的php-fpm容器。 一个例子:

 server { listen 80; index index.php; server_name first.loc; error_log /var/log/nginx/first_error.log; root /var/www/first.loc; location / { try_files $uri /index.php?$args; } location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; #  php-fpm fastcgi_pass php71-first:9000; fastcgi_index index.php; fastcgi_read_timeout 1000; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } } 

images目录-组装php-fpm映像的说明,mysql目录-我们存储数据库,www目录-所有Web项目,在示例first.loc和two.loc中。

让我们总结一下中间结果 :使用docker的功能,我们在一个环境中启动了所有工作项目。 我们所有的项目都可以看到对方,Xdebug的唯一设置已为每个项目注册。

仍然需要为每个项目正确配置PhpStorm。 设置时,我们必须在多个位置注册调试端口和服务器名称。

在PhpStorm中创建一个项目







我们将配置菜单部分
-PHP(您必须正确注册CLI解释器),
-调试(将端口更改为9008,如docker-compose.yml文件中所示),
-DBGp代理(IDE密钥,主机,端口),
更新感谢CrazyLazy集线器浏览器的重点。 不需要配置DBGp代理菜单项。
-服务器(您必须正确指定服务器名称(如docker-compose.yml文件中的名称,并使用路径映射))



我将所有其他屏幕截图隐藏在“扰流板”下。

从docker-compose.yml文件配置CLI解释器
没什么棘手的-重要的是,在设置时,选择所需的图像并正确注册服务器名称。 默认情况下,我们有Docker服务器的名称。



















设置调试菜单部分
同样,我们为特定容器规定了docker-compose.yml设置中的所有内容。 在同一步骤中,我们验证调试的工作方式。






设置服务器菜单部分
正确注册使用路径映射很重要,同样,我们从设置中获取服务器名称





我们离开菜单部分File-> Settings,转到菜单部分Run-> Edit Configuration,创建一个Php网页
我们选择在上一步中创建的服务器。







好,仅此而已。 它写了很多封信,看来一切都不容易


实际上,最主要的是了解一个非常简单的东西。 多亏了docker技术,我们可以在单个空间中运行所有工作的应用程序,但是Xdebug的设置不同。 每个应用程序都在其自己的容器中运行,我们必须仔细规定PhpStorm中每个应用程序的设置。

在出口处,我们得到了一幅美妙的图画。

1.我们在github上克隆一个存储库。 用变量创建一个.env文件
USER_ID= uid
GROUP_ID= gid


2.我们在文件/ etc / hosts中注册first.loc和two.loc节点

 127.0.0.1 first.loc 127.0.0.1 two.loc 

3.在git文件夹中,运行docker-compose up -d

4.如上所述,我们在PhpStorm中配置了first.loc和two.loc两个项目,并在PhpStorm中运行了两个项目。 即 我们打开了两个PhpStorm窗口,其中有两个项目,每个项目都侦听传入的连接(听筒已打开)。

5.在项目two.loc中,我们在第二个行上放置一个断点,例如line。 在第一个项目first.loc中,我们从文件http.http启动http请求。

瞧! 我们在断点处进入第二个项目。

要调试控制台脚本,我们做完全相同的事情。 我们打开窃听进行窃听,设置断点,转到正确的容器,运行正确的脚本。

类似于:

 alex@alex-Aspire-ES1-572 ~ $ php71first www-data@a0e771cfac72:~$ cdf www-data@a0e771cfac72:~/first.loc$ php index.php I'am first host www-data@a0e771cfac72:~/first.loc$ 

其中php71first是主机上的别名:

 alias php71first="cd ~/docker_git && docker-compose exec php71-first bash" 

cdf在容器中工作的别名。 我在上面写道,我更喜欢使用别名与容器进行通信。

仅此而已,建设性批评,欢迎评论。

PS:我要对Denis Bondar的文章PhpStorm + Docker + Xdebug表示深深的谢意,这是编写本教程的起点。

Source: https://habr.com/ru/post/zh-CN473184/


All Articles