GitLab Shell Runner. Lanzamiento competitivo de servicios de prueba usando Docker Compose


Este artículo será de interés tanto para los probadores como para los desarrolladores, pero está dirigido más a los automatizadores que enfrentan el problema de configurar GitLab CI / CD para pruebas de integración en condiciones de recursos de infraestructura insuficientes y / o falta de una plataforma para la orquestación de contenedores. Le diré cómo configurar la implementación de entornos de prueba utilizando Docker compose en un único corredor de shell GitLab y de modo que al implementar múltiples entornos, los servicios que se inician no interfieran entre sí.



Contenido




Antecedentes


  1. En mi práctica, a menudo sucedía "curar" las pruebas de integración en proyectos. Y a menudo el primer y más importante problema es la canalización de CI, en la que las pruebas de integración de los servicios desarrollados se llevan a cabo en un entorno de desarrollo / etapa. Esto causó bastantes problemas:


    • Debido a defectos en un servicio particular durante las pruebas de integración, el circuito de prueba puede corromperse por datos dañados. Hubo casos en que el envío de una solicitud con un formato JSON roto colgó un servicio, lo que hizo que el soporte quedara completamente inoperativo.
    • Disminuir la velocidad del ciclo de prueba con el crecimiento de los datos de prueba. Creo que no tiene sentido describir un ejemplo con limpiar / deshacer una base de datos. En mi práctica, no he visto un proyecto donde este procedimiento se realizó sin problemas.
    • El riesgo de interrumpir el rendimiento del circuito de prueba al probar la configuración general del sistema. Por ejemplo, política de usuario / grupo / contraseña / aplicación.
    • Los datos de prueba de las pruebas automáticas evitan que vivan los probadores manuales.

    Alguien dirá que las buenas pruebas automáticas deberían limpiar los datos después de sí mismos. Tengo argumentos en contra de:


    • Los soportes dinámicos son muy convenientes de usar.
    • No todos los objetos se pueden eliminar del sistema a través de la API. Por ejemplo, una llamada para eliminar un objeto no se implementa, ya que contradice la lógica empresarial.
    • Al crear un objeto a través de la API, se puede crear una gran cantidad de metadatos, lo cual es problemático de eliminar.
    • Si las pruebas son interdependientes, el proceso de limpieza de datos después de completar las pruebas se convierte en un dolor de cabeza.
    • Llamadas adicionales (y, en mi opinión, no justificadas) a la API.
    • Y el argumento principal: cuando los datos de prueba comienzan a limpiarse directamente de la base de datos. ¡Se convierte en un verdadero circo PK / FK! De los desarrolladores es audible: "Solo agregué / eliminé / renombré la placa de identificación, ¿por qué fallaron las pruebas de integración 100500?"

    En mi opinión, la solución más óptima es un entorno dinámico.


  2. Muchas personas usan docker-compose para ejecutar un entorno de prueba, pero pocas usan docker-compose cuando realizan pruebas de integración en CI / CD. Y aquí no tengo en cuenta kubernetes, enjambre y otras plataformas de orquestación de contenedores. No todas las empresas los tienen. Sería bueno si docker-compose.yml fuera universal.
  3. Incluso si tenemos nuestro propio corredor de control de calidad, ¿cómo podemos asegurarnos de que los servicios lanzados a través de docker-compose no interfieran entre sí?
  4. ¿Cómo recopilar registros de servicios probados?
  5. ¿Cómo limpiar el corredor?

Tengo mi propio corredor GitLab para mis proyectos y me encontré con estos problemas al desarrollar un cliente Java para TestRail . O más bien, cuando se ejecutan pruebas de integración. En adelante, resolveremos estos problemas con ejemplos de este proyecto.


Al contenido



Gitlab shell runner


Para el corredor, recomiendo una máquina virtual Linux con 4 vCPU, 4 GB de RAM, 50 GB de disco duro.
En Internet, mucha información sobre la configuración de gitlab-runner, así que brevemente:


  • Vamos a la máquina en SSH
  • Si tiene menos de 8 GB de RAM, le recomiendo hacer un intercambio de 10 GB para que el asesino OOM no venga y no nos mate por falta de RAM. Esto puede suceder cuando se inician más de 5 tareas simultáneamente. Las tareas serán más lentas, pero estables.


    OOM asesino ejemplo

    Si ve bash: line 82: 26474 Killed en los registros de tareas, simplemente ejecute sudo dmesg | grep 26474 sudo dmesg | grep 26474


     [26474] 1002 26474 1061935 123806 339 0 0 java Out of memory: Kill process 26474 (java) score 127 or sacrifice child Killed process 26474 (java) total-vm:4247740kB, anon-rss:495224kB, file-rss:0kB, shmem-rss:0kB 

    Y si la imagen se parece a esto, entonces agregue el intercambio o deje caer la RAM.




  • Instale gitlab-runner , docker , docker-compose , make.
  • Agregar usuario gitlab-runner al grupo docker
     sudo groupadd docker sudo usermod -aG docker gitlab-runner 
  • Regístrese gitlab-runner.
  • Ábralo para editar /etc/gitlab-runner/config.toml y agregue


     concurrent=20 [[runners]] request_concurrency = 10 

    Esto le permitirá ejecutar tareas paralelas en un corredor. Lee más aquí .
    Si su máquina es más potente, por ejemplo, 8 vCPU, 16 GB de RAM, estos números se pueden hacer al menos 2 veces más grandes. Pero todo depende de qué se lanzará exactamente en este corredor y en qué cantidad.



Eso es suficiente


Al contenido



Preparando docker-compose.yml


La tarea principal es docker-compose.yml, que se usará tanto localmente como en la canalización de CI.


La variable COMPOSE_PROJECT_NAME se usará para iniciar varias instancias del entorno (ver archivo MAKE ).


Un ejemplo de mi docker-compose.yml


 version: "3" #    web (php)  fmt , #      . #   ,   /var/www/testrail volumes: static-content: services: db: image: mysql:5.7.22 environment: MYSQL_HOST: db MYSQL_DATABASE: mydb MYSQL_ROOT_PASSWORD: 1234 SKIP_GRANT_TABLES: 1 SKIP_NETWORKING: 1 SERVICE_TAGS: dev SERVICE_NAME: mysql migration: image: registry.gitlab.com/touchbit/image/testrail/migration:latest links: - db depends_on: - db fpm: image: registry.gitlab.com/touchbit/image/testrail/fpm:latest container_name: "testrail-fpm-${CI_JOB_ID:-local}" volumes: - static-content:/var/www/testrail links: - db web: image: registry.gitlab.com/touchbit/image/testrail/web:latest #   TR_HTTP_PORT  TR_HTTPS_PORTS  , #     80  443  . ports: - ${TR_HTTP_PORT:-80}:80 - ${TR_HTTPS_PORT:-443}:443 volumes: - static-content:/var/www/testrail links: - db - fpm 

Al contenido



Preparación de Makefile


Utilizo Makefile, ya que es muy conveniente tanto para la gestión local del medio ambiente como en CI.


Otros comentarios están en línea


 #           `.indirect`, #     `docker-compose.yml` #  bash   pipefail # pipefail -   ,      SHELL=/bin/bash -o pipefail #   CI_JOB_ID   ifeq ($(CI_JOB_ID),) #   local CI_JOB_ID := local endif #    export COMPOSE_PROJECT_NAME = $(CI_JOB_ID)-testrail #    , , volumes docker-down: docker-compose -f .indirect/docker-compose.yml down #   docker-down () docker-up: docker-down #     docker-registry docker-compose -f .indirect/docker-compose.yml pull #   # force-recreate -    # renew-anon-volumes -   volumes   docker-compose -f .indirect/docker-compose.yml up --force-recreate --renew-anon-volumes -d #  ,   ,           docker ps #    docker-logs: mkdir -p ./logs docker logs $${COMPOSE_PROJECT_NAME}_web_1 >& logs/testrail-web.log || true docker logs $${COMPOSE_PROJECT_NAME}_fpm_1 >& logs/testrail-fpm.log || true docker logs $${COMPOSE_PROJECT_NAME}_migration_1 >& logs/testrail-migration.log || true docker logs $${COMPOSE_PROJECT_NAME}_db_1 >& logs/testrail-mysql.log || true #   docker-clean: @echo   testrail- docker kill $$(docker ps --filter=name=testrail -q) || true @echo    docker rm -f $$(docker ps -a -f --filter=name=testrail status=exited -q) || true @echo  dangling  docker rmi -f $$(docker images -f "dangling=true" -q) || true @echo  testrail  docker rmi -f $$(docker images --filter=reference='registry.gitlab.com/touchbit/image/testrail/*' -q) || true @echo    volume docker volume rm -f $$(docker volume ls -q) || true @echo   testrail  docker network rm $(docker network ls --filter=name=testrail -q) || true docker ps 

Comprobar lanzamiento local
 $ make docker-up docker-compose -f .indirect/docker-compose.yml pull Pulling db ... done Pulling migration ... done Pulling fpm ... done Pulling web ... done docker-compose -f .indirect/docker-compose.yml up --force-recreate --renew-anon-volumes -d Creating network "local-testrail_default" with the default driver Recreating local-testrail_db_1 ... done Recreating local-testrail_migration_1 ... done Recreating local-testrail_fpm_1 ... done Recreating local-testrail_web_1 ... done docker ps CONTAINER ID NAMES 3b8f9d4af29c local-testrail_web_1 5622c7d742d5 local-testrail_fpm_1 b580e3392038 local-testrail_migration_1 e467630bd3a5 local-testrail_db_1 

Comprobación del lanzamiento de CI
 $ export CI_JOB_ID=123456789 $ make docker-up docker-compose -f .indirect/docker-compose.yml pull Pulling db ... done Pulling migration ... done Pulling fpm ... done Pulling web ... done docker-compose -f .indirect/docker-compose.yml up --force-recreate --renew-anon-volumes -d Creating network "123456789-testrail_default" with the default driver Creating volume "123456789-testrail_static-content" with default driver Creating 123456789-testrail_db_1 ... done Creating 123456789-testrail_fpm_1 ... done Creating 123456789-testrail_migration_1 ... done Creating 123456789-testrail_web_1 ... done docker ps CONTAINER ID NAMES ccf1ad33d0e8 123456789-testrail_web_1 bc079964f681 123456789-testrail_fpm_1 10dc9d4d8f2a 123456789-testrail_migration_1 fe98d43c380e 123456789-testrail_db_1 

Comprobar recopilación de registros
 $ make docker-logs mkdir -p ./logs docker logs ${COMPOSE_PROJECT_NAME}_web_1 >& logs/testrail-web.log || true docker logs ${COMPOSE_PROJECT_NAME}_fpm_1 >& logs/testrail-fpm.log || true docker logs ${COMPOSE_PROJECT_NAME}_migration_1 >& logs/testrail-migration.log || true docker logs ${COMPOSE_PROJECT_NAME}_db_1 >& logs/testrail-mysql.log || true 


Al contenido



Preparando .gitlab-ci.yml



Ejecute pruebas de integración


 Integration: stage: test tags: - my-shell-runner before_script: #   registry - docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY} #   TR_HTTP_PORT  TR_HTTPS_PORT - export TR_HTTP_PORT=$(shuf -i10000-60000 -n1) - export TR_HTTPS_PORT=$(shuf -i10000-60000 -n1) script: #    - make docker-up #    jar (  ) - java -jar itest.jar --http-port ${TR_HTTP_PORT} --https-port ${TR_HTTPS_PORT} #    - docker run --network=testrail-network-${CI_JOB_ID:-local} --rm itest after_script: #   - make docker-logs #   - make docker-down artifacts: #   when: always paths: - logs expire_in: 30 days 

Como resultado de iniciar dicha tarea en los artefactos, el directorio de registros contendrá los registros de servicios y pruebas. Lo cual es muy conveniente en caso de errores. Para mí, cada prueba en paralelo escribe su propio registro, pero hablaré sobre esto por separado.



Al contenido



Limpieza del corredor


La tarea se ejecutará solo según lo programado.


 stages: - clean - build - test Clean runner: stage: clean only: - schedules tags: - my-shell-runner script: - make docker-clean 

A continuación, vaya a nuestro proyecto GitLab -> CI / CD -> Horarios -> Nuevo horario y agregue un nuevo horario



Al contenido



Resultado


Ejecute 4 tareas en GitLab CI


En los registros de la última tarea con pruebas de integración, vemos contenedores de diferentes tareas


 CONTAINER ID NAMES c6b76f9135ed 204645172-testrail-web_1 01d303262d8e 204645172-testrail-fpm_1 2cdab1edbf6a 204645172-testrail-migration_1 826aaf7c0a29 204645172-testrail-mysql_1 6dbb3fae0322 204645084-testrail-web_1 3540f8d448ce 204645084-testrail-fpm_1 70fea72aa10d 204645084-testrail-mysql_1 d8aa24b2892d 204644881-testrail-web_1 6d4ccd910fad 204644881-testrail-fpm_1 685d8023a3ec 204644881-testrail-mysql_1 1cdfc692003a 204644793-testrail-web_1 6f26dfb2683e 204644793-testrail-fpm_1 029e16b26201 204644793-testrail-mysql_1 c10443222ac6 204567103-testrail-web_1 04339229397e 204567103-testrail-fpm_1 6ae0accab28d 204567103-testrail-mysql_1 b66b60d79e43 204553690-testrail-web_1 033b1f46afa9 204553690-testrail-fpm_1 a8879c5ef941 204553690-testrail-mysql_1 069954ba6010 204553539-testrail-web_1 ed6b17d911a5 204553539-testrail-fpm_1 1a1eed057ea0 204553539-testrail-mysql_1 

Registro más detallado
 $ docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY} WARNING! Using --password via the CLI is insecure. Use --password-stdin. WARNING! Your password will be stored unencrypted in /home/gitlab-runner/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded $ export TR_HTTP_PORT=$(shuf -i10000-60000 -n1) $ export TR_HTTPS_PORT=$(shuf -i10000-60000 -n1) $ mkdir ${CI_JOB_ID} $ cp .indirect/docker-compose.yml ${CI_JOB_ID}/docker-compose.yml $ make docker-up docker-compose -f ${CI_JOB_ID:-.indirect}/docker-compose.yml kill docker network rm testrail-network-${CI_JOB_ID:-local} || true Error: No such network: testrail-network-204645172 docker network create testrail-network-${CI_JOB_ID:-local} 0a59552b4464b8ab484de6ae5054f3d5752902910bacb0a7b5eca698766d0331 docker-compose -f ${CI_JOB_ID:-.indirect}/docker-compose.yml pull Pulling web ... done Pulling fpm ... done Pulling migration ... done Pulling db ... done docker-compose -f ${CI_JOB_ID:-.indirect}/docker-compose.yml up --force-recreate --renew-anon-volumes -d Creating volume "204645172-testrail_static-content" with default driver Creating 204645172-testrail-mysql_1 ... Creating 204645172-testrail-mysql_1 ... done Creating 204645172-testrail-migration_1 ... done Creating 204645172-testrail-fpm_1 ... done Creating 204645172-testrail-web_1 ... done docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c6b76f9135ed registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" 13 seconds ago Up 1 second 0.0.0.0:51148->80/tcp, 0.0.0.0:25426->443/tcp 204645172-testrail-web_1 01d303262d8e registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 16 seconds ago Up 13 seconds 9000/tcp 204645172-testrail-fpm_1 2cdab1edbf6a registry.gitlab.com/touchbit/image/testrail/migration:latest "docker-entrypoint.s…" 16 seconds ago Up 13 seconds 3306/tcp, 33060/tcp 204645172-testrail-migration_1 826aaf7c0a29 mysql:5.7.22 "docker-entrypoint.s…" 18 seconds ago Up 16 seconds 3306/tcp 204645172-testrail-mysql_1 6dbb3fae0322 registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" 36 seconds ago Up 22 seconds 0.0.0.0:44202->80/tcp, 0.0.0.0:20151->443/tcp 204645084-testrail-web_1 3540f8d448ce registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 38 seconds ago Up 35 seconds 9000/tcp 204645084-testrail-fpm_1 70fea72aa10d mysql:5.7.22 "docker-entrypoint.s…" 40 seconds ago Up 37 seconds 3306/tcp 204645084-testrail-mysql_1 d8aa24b2892d registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" About a minute ago Up 53 seconds 0.0.0.0:31103->80/tcp, 0.0.0.0:43872->443/tcp 204644881-testrail-web_1 6d4ccd910fad registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" About a minute ago Up About a minute 9000/tcp 204644881-testrail-fpm_1 685d8023a3ec mysql:5.7.22 "docker-entrypoint.s…" About a minute ago Up About a minute 3306/tcp 204644881-testrail-mysql_1 1cdfc692003a registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" About a minute ago Up About a minute 0.0.0.0:44752->80/tcp, 0.0.0.0:23540->443/tcp 204644793-testrail-web_1 6f26dfb2683e registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" About a minute ago Up About a minute 9000/tcp 204644793-testrail-fpm_1 029e16b26201 mysql:5.7.22 "docker-entrypoint.s…" About a minute ago Up About a minute 3306/tcp 204644793-testrail-mysql_1 c10443222ac6 registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" 5 hours ago Up 5 hours 0.0.0.0:57123->80/tcp, 0.0.0.0:31657->443/tcp 204567103-testrail-web_1 04339229397e registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 5 hours ago Up 5 hours 9000/tcp 204567103-testrail-fpm_1 6ae0accab28d mysql:5.7.22 "docker-entrypoint.s…" 5 hours ago Up 5 hours 3306/tcp 204567103-testrail-mysql_1 b66b60d79e43 registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" 5 hours ago Up 5 hours 0.0.0.0:56321->80/tcp, 0.0.0.0:58749->443/tcp 204553690-testrail-web_1 033b1f46afa9 registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 5 hours ago Up 5 hours 9000/tcp 204553690-testrail-fpm_1 a8879c5ef941 mysql:5.7.22 "docker-entrypoint.s…" 5 hours ago Up 5 hours 3306/tcp 204553690-testrail-mysql_1 069954ba6010 registry.gitlab.com/touchbit/image/testrail/web:latest "nginx -g 'daemon of…" 5 hours ago Up 5 hours 0.0.0.0:32869->80/tcp, 0.0.0.0:16066->443/tcp 204553539-testrail-web_1 ed6b17d911a5 registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 5 hours ago Up 5 hours 9000/tcp 204553539-testrail-fpm_1 1a1eed057ea0 mysql:5.7.22 "docker-entrypoint.s…" 5 hours ago Up 5 hours 3306/tcp 204553539-testrail-mysql_1 

Todas las tareas completadas exitosamente

Los artefactos de tareas contienen registros de servicio y prueba



Parece que todo es hermoso, pero hay un matiz. La canalización se puede cancelar por la fuerza durante la ejecución de las pruebas de integración, en cuyo caso la ejecución de contenedores no se detendrá. De vez en cuando necesita limpiar el corredor. Desafortunadamente, la tarea de revisión en GitLab CE todavía está en estado Abierto


Pero hemos agregado el inicio de la tarea programada, y nadie nos prohíbe iniciarlo manualmente.
Vaya a nuestro proyecto -> CI / CD -> Horarios y ejecute la tarea Clean runner



Total:


  • Tenemos un corredor de shell.
  • No hay conflictos entre las tareas y el entorno.
  • Tenemos un lanzamiento paralelo de tareas con pruebas de integración.
  • Puede ejecutar pruebas de integración tanto localmente como en el contenedor.
  • Los registros de servicios y pruebas se recopilan y se adjuntan a la tarea de canalización.
  • Es posible limpiar el corredor de viejas imágenes acopladas.

El tiempo de configuración es de ~ 2 horas.
Eso, de hecho, es todo. Estaré encantado de comentarios.


PS
Un agradecimiento especial a freeseacher vvasilenok ivanych . Sus comentarios fueron muy valiosos en el contexto de la publicación.


Al contenido

Source: https://habr.com/ru/post/449910/


All Articles