No hace mucho tiempo, el líder del equipo dijo: chicos, quiero que todos tengan el mismo entorno de desarrollo para nuestros proyectos de combate, debemos poder depurar todo, tanto las aplicaciones web como las solicitudes de API y los scripts de consola para salvar nuestros nervios y nuestro tiempo. Y ayúdanos con este acoplador.
Apenas dicho que hecho. Detalles debajo del corte.
Hay muchos manuales de contenedorización en la red, pero ¿cómo aplicarlos al desarrollo de combate real? Para cada proyecto, escriba su propio docker-compose.yml? Pero todos nuestros proyectos se comunican entre sí a través de la API, todos utilizan la pila de tecnología estándar: nginx + php-fpm + mysql.
Por lo tanto, aclaremos las condiciones del problema:
- Trabajamos en una empresa, en equipo, acompañamos varios proyectos de combate. Todos trabajamos bajo Ubuntu + PhpStorm
- Para el desarrollo local, queremos usar la ventana acoplable para tener el mismo entorno de desarrollo para cada miembro del equipo, y también para que cuando llegue un nuevo desarrollador, pueda implementar rápidamente un entorno de trabajo
- Queremos desarrollar con comodidad, queremos debutar todo: aplicaciones web, scripts de consola y solicitudes de API.
Una vez más: queremos llevar
varios proyectos de trabajo a la ventana acoplable.
En los servidores de combate, se usa el paquete estándar nginx + php-fpm + mysql. Y cual es el problema?
Implementamos exactamente el mismo entorno + Xdebug en la máquina local, configuramos nuestros proyectos en PhpStorm y trabajamos. Para la depuración, encienda el "tubo" en PhpStorm, todo funciona de fábrica, todo está bien.

Todo esto es cierto: todo funciona de la caja. Pero intentemos mirar debajo del capó de nuestro entorno de trabajo.
Nginx + php-fpm se comunican a través del socket, xdebug escucha en el puerto 9000, PhpStorm también, por defecto, escucha en el puerto 9000 para la depuración y todo parece estar bien. ¿Y si tenemos varias aplicaciones abiertas en PhpStorm, y las escuchas telefónicas ("tubo") están habilitadas para varias aplicaciones? ¿Qué hará PhpStorm? Él comenzará a jurar que se ha detectado una nueva conexión para Xdebug, ¿quiere ignorarla o no?
Es decir, con la configuración predeterminada en PhpStorm, en un momento determinado, solo puedo presentar una aplicación. Para todas las demás aplicaciones abiertas, la depuración debe estar desactivada. Maldición, pero es inconveniente. Quiero escuchar todas las aplicaciones para la depuración, y si hay un punto de interrupción en una de ellas, quiero que PhpStorm se detenga en esta aplicación, en la línea donde la necesito.
¿Y qué se necesita para esto? Pero necesita que cada aplicación comience con su propia configuración para Xdebug. Para que cada aplicación escuche su puerto, busque su servidor, y no como si tuviéramos todo en común, todo en un montón.
¡Y para esto hay un maravilloso acoplador! Podemos lanzar cada una de nuestras aplicaciones de combate en un contenedor separado, basado en una imagen común, por ejemplo, php: 7.1-fpm. Gracias a la tecnología docker, podemos aislar nuestras aplicaciones con una sobrecarga mínima.
Ok, comencemos nuestros proyectos de combate bajo el docker, ejecutemos cada proyecto en un contenedor separado, configuremos cada proyecto en PhpStorm individualmente para la depuración, todo debería estar bien.
Y, vaya, el primer problema: los contenedores en la ventana acoplable se ejecutan como root, y localmente trabajamos, generalmente como un usuario con uid 1000, gid 1000. Las aplicaciones están luchando, y dar a cada aplicación 777 derechos para todo no es una opción. Nuestras aplicaciones están bajo el git, y si otorgamos los derechos 777 localmente, el git registrará todo esto y lo transferirá al servidor de batalla.
Muletas, aquí hay un ejemplo de imagen php: 7.1-fpm que se compilará.
Actualización
Como la comunidad señaló correctamente, no hay absolutamente ninguna necesidad de muletas.
Por ejemplo 1ntrovert
habrozer en el comentarioEjemplo de imagen php inicial: 7.1-fpm (uid y gid están codificados)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:
Ejemplo de Dockerfile corregido
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"]
Al iniciar un contenedor desde esta imagen, el usuario de www-data obtiene uid = 1000, gid = 1000. Por lo general, el primer usuario creado en Linux tiene estos derechos. Y, exactamente con tales derechos, nuestros contenedores php-fpm funcionarán. Estaría muy agradecido si alguien me dijera cómo trabajar sin muletas con derechos de acceso a la ventana acoplable.Al iniciar un contenedor desde esta imagen, el usuario de www-data obtiene uid y gid, que se transferirán desde el exterior.
También en los comentarios se planteó el tema : por qué cambiar los derechos del usuario de www-data en absoluto, por qué los derechos estándar no son adecuados para 33. Solo una cosa: cuando vamos al contenedor y creamos, por ejemplo, un archivo de migración, no seremos el propietario del archivo en la máquina host. Y cada vez será necesario ejecutar algo como
sudo chown -R user:user ./
Y el segundo pequeño problema: para que Xdebug funcione correctamente, debe registrar la dirección IP correcta para la máquina host. Cada miembro del equipo es diferente. 127.0.0.1 no rueda. Y aquí el mismo acoplador viene en nuestra ayuda. Por ejemplo, podemos configurar explícitamente la red: 192.168.220.0/28. Y entonces nuestra máquina siempre tendrá la dirección 192.168.220.1. Utilizaremos esta dirección para configurar PhpStorm, así como para configurar otras aplicaciones. Por ejemplo, cuando se trabaja con MySql.
Docker-compose.yml en sí, después de considerar los comentarios, se ve así:
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:
Vemos que en esta configuración se crean dos contenedores php71-first y php71-two, basados en una imagen php: 7.1-fpm. Cada contenedor tiene su propia configuración para Xdebug. Cada contenedor individual escuchará, para la depuración, su puerto y su servidor.
Además, llamo su atención sobre las directivas
args: - USER_ID - GROUP_ID
Sin estas variables, la imagen php-fpm no se iniciará. Pregunta: ¿cómo pasarlos a docker-compose.yml? Respuesta: ya que es más conveniente para usted. Puedes al inicio:
USER_ID=$(id -u) GROUP_ID=$(id -g) docker-compose up -d
Puede escribir estas variables en el archivo .env, que se encuentra en el mismo nivel que el archivo docker-compose.yml
USER_ID=1000
GROUP_ID=1000
Me gusta más la versión con el archivo .env. Por supuesto, puedes usar el Makefile. Como más te guste.
El código completo para la versión demo se publica en el
github .
Listado de proyecto de demostración:

Repase brevemente la lista del proyecto.
Alias -> php71 -> directorio bash.bashrc. Momento controvertido. Prefiero comunicarme con contenedores php-fpm a través de alias.
Este archivo se reenvía a docker-compose.yml: - ./aliases/php71/bash.bashrc:/etc/bash.bashrc
Herramienta estándar de Linux.
El directorio de hosts: archivos de configuración para Nginx. Cada configuración tiene su propio contenedor php-fpm. Un ejemplo:
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)(/.+)$;
El directorio de imágenes - instrucciones para ensamblar imágenes php-fpm, el directorio mysql - almacenar bases de datos, el directorio www - todos nuestros proyectos web, en nuestro ejemplo first.loc y two.loc.
Resumamos los resultados intermedios : utilizando las capacidades de la ventana acoplable, lanzamos todos nuestros proyectos de trabajo en un entorno. Todos nuestros proyectos se ven, se registran configuraciones únicas para Xdebug para cada uno de los proyectos.
Queda por configurar correctamente PhpStorm para cada uno de los proyectos. Al configurar, debemos registrar el puerto para la depuración y el nombre del servidor en varios lugares.
Crea un proyecto en PhpStorm



Configuraremos las secciones del menú.
- PHP (debe registrar correctamente el intérprete CLI),
- Depuración (cambie el puerto a 9008, como en el archivo docker-compose.yml),
- Proxy DBGp (clave IDE, Host, Puerto),actualización Gracias al
navegador de hub CrazyLazy por el punto importante. El elemento del menú proxy DBGp no necesita ser configurado.
- Servidores (debe especificar correctamente el nombre del servidor, como en el archivo docker-compose.yml, y usar las asignaciones de ruta)

Ocultaré todas las capturas de pantalla adicionales debajo del spoiler.
Configuración del intérprete de CLI desde el archivo docker-compose.yml Configure la sección del menú DepurarNuevamente, prescribimos todo desde la configuración de docker-compose.yml para un contenedor específico. En el mismo paso, validamos cómo funciona nuestra depuración.


Configurar la sección del menú ServidoresEs importante registrar correctamente las asignaciones de ruta de uso, nuevamente tomamos el nombre del servidor de la configuración


Salimos de la sección de menú Archivo -> Configuración, vamos a la sección de menú Ejecutar -> Editar configuración, creamos una página web de Php Bueno eso es todo. Está escrito muchas letras, parece que no todo es fácil
De hecho, lo principal es entender una cosa muy simple. Gracias a la tecnología Docker, podemos ejecutar todas nuestras aplicaciones de trabajo en un solo espacio, pero con diferentes configuraciones para Xdebug. Cada aplicación funciona en su propio contenedor, y tenemos que prescribir cuidadosamente la configuración de cada aplicación en PhpStorm.
Y a la salida tenemos una foto maravillosa.
1.
Clonamos un repositorio en un
github . Crear un archivo .env con variables
USER_ID= uid
GROUP_ID= gid
2. Registramos los nodos first.loc y two.loc en el archivo / etc / hosts
127.0.0.1 first.loc 127.0.0.1 two.loc
3. En la carpeta git, ejecute el
docker-compose up -d
4. Configuramos ambos proyectos first.loc y two.loc en PhpStorm, como se describió anteriormente, y ejecutamos ambos proyectos en PhpStorm. Es decir Tenemos dos ventanas PhpStorm abiertas, con dos proyectos, cada uno de ellos escucha las conexiones entrantes (el teléfono está encendido).
5. En el proyecto two.loc colocamos un punto de interrupción en la segunda línea, por ejemplo. En el primer proyecto first.loc, comenzamos la solicitud http desde el archivo http.http
¡Y he aquí! Nos lanzan al segundo proyecto, en nuestro punto de quiebre.
Para depurar scripts de consola, hacemos exactamente lo mismo. Activamos las escuchas telefónicas para escuchas telefónicas, establecemos un punto de interrupción, vamos al contenedor correcto, ejecutamos el script correcto.
Algo como:
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$
Donde php71first es el alias en la máquina host:
alias php71first="cd ~/docker_git && docker-compose exec php71-first bash"
cdf
: un alias que funciona en un contenedor. Escribí anteriormente que prefiero usar alias para comunicarme con contenedores.
Eso es todo, críticas constructivas, los comentarios son bienvenidos.
PD: Me gustaría expresar mi profunda gratitud a Denis Bondar por su artículo
PhpStorm + Docker + Xdebug , que fue el punto de partida para escribir este tutorial.