
Esta publicación está escrita a solicitud de los trabajadores que periódicamente preguntan sobre "Cómo ejecutar la aplicación Illuminate / Symfony / MyOwn Psr7 en la ventana acoplable". No tengo ganas de dar un enlace a una publicación escrita anteriormente , porque mis puntos de vista sobre cómo resolver el problema han cambiado bastante.
Todo lo que se escribirá a continuación es una experiencia subjetiva, que (como siempre) no pretende considerarse la única decisión correcta, pero algunos enfoques y soluciones pueden parecerle interesantes y útiles.
También usaré Laravel como una aplicación, ya que me es más familiar y bastante extendido. La adaptación a otros marcos / componentes basados en PSR-7 es posible, pero esta historia no se trata de eso.
Manejo de errores
Me gustaría comenzar con lo que resultó ser "no las mejores prácticas" en el contexto del artículo anterior :
- La necesidad de cambiar la estructura de los archivos en el repositorio
- Usando FPM. Si queremos el rendimiento de nuestras aplicaciones, quizás una de las mejores soluciones, incluso en la etapa de selección de tecnología, es abandonarlo en favor de algo más rápido y "adaptado" al hecho de que la memoria puede perder. RoadRunner by lachezis está aquí como nunca antes
- Una imagen separada con fuente y activos. A pesar del hecho de que con este enfoque, podemos reutilizar la misma imagen para generar un enrutamiento más complejo de las solicitudes entrantes (nginx en la parte delantera para devolver estática; las solicitudes de dinámica son atendidas por otro contenedor en el que el volumen se lanza con las mismas fuentes, para mejor escala): este esquema ha resultado ser bastante complicado en la operación del producto. Y lo que es más, RR en sí mismo representa perfectamente las estadísticas, y si hay muchas estadísticas (o el recurso puede cargar y mostrar contenido generado por el usuario) , lo llevamos a CDN (el paquete S3 + CloudFront + CloudFlare funciona bien) y, en principio, nos olvidamos de este problema
- CI complejo. Esto se convirtió en un problema real cuando el período de "construcción de carne" activa comenzó en las etapas de montaje y pruebas automáticas. Un tipo que no apoyó este CI antes, se hace muy difícil hacer cambios sin temor a romper nada.
Ahora, sabiendo qué problemas deben corregirse y con una comprensión de cómo hacer esto, propongo proceder con su eliminación. El conjunto de "herramientas de desarrollo" no ha cambiado: es el mismo docker-ce
, docker-compose
y el poderoso Makefile
.
Como resultado, obtenemos:
- Un contenedor independiente con una aplicación sin la necesidad de montar un volumen adicional.
- Un ejemplo de uso de git-hooks: colocaremos las dependencias necesarias después de que
git pull
automáticamente y prohibiremos presionar el código si las pruebas fallan (los ganchos se almacenarán debajo del git, naturalmente) - RoadRunner manejará las solicitudes HTTP (s)
- Los desarrolladores aún podrán ejecutar
dd(..)
y dump(..)
para la depuración, mientras que nada se bloqueará en su navegador - Las pruebas se pueden ejecutar directamente desde el IDE de PHPStorm, mientras que se ejecutarán en el contenedor con la aplicación
- CI recopilará imágenes para nosotros cuando publique una nueva etiqueta de versión de la aplicación
- Tomemos la estricta regla de mantener los archivos
CHANGELOG.md
y ENVIRONMENT.md
Introducción visual de un nuevo enfoque.
Para una demostración visual, dividiré todo el proceso en varias etapas, los cambios dentro de los cuales se realizarán como MR separadas (después de la fusión, todos los brunches permanecerán en sus lugares; referencias a MR en los títulos de los "pasos"). El punto de partida es el esqueleto de Laravel de una aplicación creada usando composer create-project laravel/laravel
:
$ docker run \ --rm -i \ -v "$(pwd):/src" \ -u "$(id -u):$(id -g)" \ composer composer create-project --prefer-dist laravel/laravel \ /src/laravel-in-docker-with-rr "5.8.*"
El primer paso es enseñarle a la aplicación a ejecutarse en el contenedor. Para hacer esto, necesitamos un Dockerfile
, docker-compose.yml
para la descripción de "cómo levantar y vincular contenedores", y un Makefile
para reducir un proceso ya simplificado a uno o dos comandos.
Dockerfile
La imagen básica que uso php:XXX-alpine
es la más fácil y contiene lo que necesitas para ejecutar. Además, todas las actualizaciones posteriores al intérprete se reducen a simplemente cambiar el valor en esta línea (actualizar PHP ahora es más fácil que nunca).
Composer y el archivo binario RoadRunner se entregan en el contenedor usando varias etapas y COPY --from=...
- esto es muy conveniente, y todos los valores asociados con las versiones no están "dispersos", sino que están al comienzo del archivo. Esto funciona rápido y sin dependencias de curl
/ git clone
/ make build
. Las imágenes de 512k / roadrunner son compatibles conmigo, si lo desea, puede ensamblar el archivo binario usted mismo.
Una historia interesante sucedió con la variable de entorno PS1
(responsable de la solicitud en el shell): resulta que puedes usar emoji y todo funciona localmente, pero si intentas iniciar la imagen con una variable que contiene emoji en, digamos, ranchero, se bloqueará (enjambre todo funciona sin problemas).
En Dockerfile
comienzo a generar un certificado SSL autofirmado para usarlo para las solicitudes HTTPS entrantes. Naturalmente, nada impide el uso de un certificado "normal".
También me gustaría decir sobre:
COPY ./composer.* /app/ RUN set -xe \ && composer install --no-interaction --no-ansi --no-suggest --prefer-dist \ --no-autoloader --no-scripts \ && composer install --no-dev --no-interaction --no-ansi --no-suggest \ --prefer-dist --no-autoloader --no-scripts
Aquí el significado es el siguiente: los archivos composer.lock
y composer.json
se entregan en una capa separada de la imagen, después de lo cual se instalan todas las dependencias descritas en ellos. Esto se hace para que durante las compilaciones posteriores de la imagen usando --cache-from
, si la composición y las versiones de las dependencias instaladas no hayan cambiado, la composer install
no se ejecute, tomando esta capa del caché, ahorrando así el tiempo de compilación y el tráfico (gracias por la idea jetexe ).
composer install
se ejecuta dos veces (la segunda vez con --no-dev
) para "calentar" el caché de las dependencias de desarrollo, de modo que cuando colocamos todas las dependencias en el CI para ejecutar las pruebas, se colocan desde el caché del compositor, que ya está en la imagen, y no se extendía desde galaxias distantes.
Con la última instrucción RUN
, mostramos las versiones del software instalado y la composición de los módulos PHP tanto para el historial en los registros de compilación como para asegurarnos de que "al menos existe y de alguna manera comienza".
También utilizo mi Entrypoint, porque antes de iniciar la aplicación en algún lugar del clúster realmente quiero verificar la disponibilidad de servicios dependientes: DB, redis, rabbit y otros.
Correcaminos
Para integrar RoadRunner con una aplicación Laravel, se escribió un paquete que reduce toda la integración a un par de comandos en el shell (ejecutando docker-compose run app sh
):
$ composer require avto-dev/roadrunner-laravel "^2.0" $ ./artisan vendor:publish --provider='AvtoDev\RoadRunnerLaravel\ServiceProvider' --tag=rr-config
Agregue APP_FORCE_HTTPS=true
al archivo ./docker/docker-compose.env
y especifique la ruta al certificado SSL en el contenedor en los .rr*.yaml
.
Para poder usar dump(..)
y dd(..)
y todo funcionaría, hay otro paquete: avto-dev/stacked-dumper-laravel
. Todo lo que se requiere es agregar un pefix a estos ayudantes, a saber, \dev\dd(..)
y \dev\dump(..)
respectivamente. Sin esto, observará un error de la forma:
worker error: invalid data found in the buffer (possible echo)
Después de todas las manipulaciones, haga docker-compose up -d
y listo:

La base de datos PostgeSQL, redis y los trabajadores de RoadRunner se ejecutaron con éxito en contenedores.
Como escribí anteriormente, un Makefile es un elemento muy subestimado. Los objetivos dependientes, su propio azúcar sintáctico, el 99% de posibilidades de que ya se encuentre en la máquina de desarrollo de Linux / mac, autocompletar "fuera de la caja", una pequeña lista de sus ventajas.
Agregándolo a nuestro proyecto y haciendo make
sin parámetros, podemos observar:

Para ejecutar pruebas unitarias, podemos hacer una make test
, u obtener un shell dentro del contenedor con la aplicación ( make shell
), ejecutar composer phpunit
. Para obtener el informe de cobertura, solo haga make test-cover
, y antes de ejecutar las pruebas, xdebug con sus dependencias se entregará al contenedor y se iniciarán las pruebas (ya que este procedimiento no se realiza a menudo y no por CI; esta solución parece ser mejor que mantener una imagen separada con todo dev-lociones).
Ganchos Git
Los ganchos en nuestro caso cumplirán 2 roles importantes: no permitir que el código ingrese al origen cuyas pruebas no tienen éxito; y coloca automáticamente todas las dependencias necesarias si, al realizar los cambios en su máquina, resulta que composer.lock
ha cambiado. En el Makefile
, hay un objetivo separado para esto:
cwd = $(shell pwd) git-hooks: ## Install (reinstall) git hooks (required after repository cloning) -rm -f "$(cwd)/.git/hooks/pre-push" "$(cwd)/.git/hooks/pre-commit" "$(cwd)/.git/hooks/post-merge" ln -s "$(cwd)/.gitlab/git-hooks/pre-push.sh" "$(cwd)/.git/hooks/pre-push" ln -s "$(cwd)/.gitlab/git-hooks/pre-commit.sh" "$(cwd)/.git/hooks/pre-commit" ln -s "$(cwd)/.gitlab/git-hooks/post-merge.sh" "$(cwd)/.git/hooks/post-merge"
Hacer make git-hooks
simplemente quita los ganchos existentes y los coloca en el .gitlab/git-hooks
en su lugar. Su fuente se puede ver en este enlace .
Ejecución de pruebas desde PhpStorm
A pesar de que es bastante simple y conveniente, lo usé durante mucho tiempo ./vendor/bin/phpunit --group=foo
lugar de simplemente presionar la tecla de ./vendor/bin/phpunit --group=foo
directamente mientras escribía una prueba o código asociado con él.
Haga clic en File > Settings > Languages & Frameworks > PHP > CLI interpreter > [...] > [+] > From Docker, Vargant, VM, Remote
. Seleccione Docker compose y el nombre del servicio de la aplicación .

El segundo paso es decirle a phpunit que use el intérprete desde el contenedor: File > Settings > Test frameworks > [+] > PHPUnit by remote interpreter
Marcos de File > Settings > Test frameworks > [+] > PHPUnit by remote interpreter
y seleccione el intérprete remoto creado previamente. En el /app/vendor/autoload.php
Path to script
/app/vendor/autoload.php
Path to script
, especifique /app/vendor/autoload.php
, y en las Path mappings
especificamos el directorio raíz del proyecto como está montado en /app
.

Y ahora podemos ejecutar pruebas directamente desde el IDE usando el intérprete dentro de la imagen con la aplicación, presionando (por defecto, Linux) Ctrl + Shift + F10.
Todo lo que nos queda por hacer es automatizar el proceso de ejecutar pruebas y ensamblar la imagen. Para hacer esto, cree el .gitlab-ci.yml
en el directorio raíz de la aplicación, rellenándolo con los siguientes contenidos . La idea principal de esta configuración es ser lo más simple posible, pero no perder funcionalidad al mismo tiempo.
La imagen se ensambla en cada brunch, en cada commit. Usando --cache-from
ensamblar una imagen al volver a confirmar es muy rápido. La necesidad de reconstrucción se debe al hecho de que en cada brunch tenemos una imagen con los cambios que se realizaron como parte de este brunch y, como resultado, nada nos impide extenderlo a enjambre / k8s / etc. para asegurarnos de "vivir" que todo funciona, y funciona como debería incluso antes de la fusión con la luz master
.
Después del ensamblaje, ejecutamos pruebas unitarias y verificamos el inicio de la aplicación en el contenedor, enviando solicitudes de curvatura al punto final de verificación de estado (esta acción es opcional, pero varias veces esta prueba me ayudó mucho).
Para el "lanzamiento de la versión", solo publique una etiqueta del formulario vX.XX
(si aún se vX.XX
a las versiones semánticas, será genial): CI recopilará la imagen, ejecutará las pruebas y realizará las acciones que especifique en la deploy to somewhere
.
No olvide en la configuración del proyecto (si es posible) limitar la capacidad de publicar etiquetas solo a las personas a las que se les permite "liberar versiones".
CHANGELOG.md
y ENVIRONMENT.md
Antes de aceptar uno u otro MR, el inspector debe, sin falta, verificar el ENVIRONMENT.md
archivos CHANGELOG.md
y ENVIRONMENT.md
. Si el primero es más y menos claro , entonces el segundo relativo daré una explicación. Este archivo se utiliza para describir todas las variables de entorno a las que responde el contenedor con la aplicación. Es decir Si el desarrollador agrega o elimina el soporte para una u otra variable de entorno, esto debe reflejarse en este archivo. Y en el momento en que surge la pregunta "Necesitamos redefinir urgentemente esto y aquello", nadie comienza a profundizar frenéticamente en la documentación o los códigos fuente, sino que busca en un solo archivo. Muy comodo
Conclusión
En este artículo, examinamos el proceso bastante sencillo de transferir el desarrollo y el lanzamiento de aplicaciones al entorno Docker, RoadRunner integrado y el uso de un simple script CI automatizó el ensamblaje y la prueba de la imagen con nuestra aplicación.
Después de clonar el repositorio, los desarrolladores tienen que make git-hooks && make install && make up
y comenzar a escribir código útil. Camaradas * ops-am: tome la imagen con la etiqueta deseada y enróllela en sus grupos.
Naturalmente, este esquema también se simplifica, y en los proyectos de "combate" estoy terminando mucho más, pero si el enfoque descrito en el artículo ayuda a alguien, sabré que perdí mi tiempo.