ProHoster > Blog > administración > Corredor de shell de GitLab. Lance de forma competitiva servicios comprobables con Docker Compose
Corredor de shell de GitLab. Lance de forma competitiva servicios comprobables con Docker Compose
Este artículo será de interés tanto para probadores como para desarrolladores, pero está destinado más a los automatizadores que se enfrentan al problema de configurar GitLab CI/CD para pruebas de integración frente a recursos de infraestructura insuficientes y/o la ausencia de una orquestación de contenedores. plataforma. Te diré cómo configurar el despliegue de entornos de prueba usando docker compose en un solo runner de GitLab shell y para que al desplegar varios entornos, los servicios lanzados no interfieran entre sí.
En mi práctica, a menudo sucedió que "trataba" 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 desarrollado los servicios están alojados en el entorno dev/stage. Esto causó bastantes problemas:
Debido a defectos en un servicio en particular durante la prueba de integración, el bucle de prueba puede corromperse con datos rotos. Hubo casos en los que el envío de una solicitud con un formato JSON roto colgó el servicio, lo que provocó que el soporte quedara completamente inoperativo.
La desaceleración del circuito de prueba con el crecimiento de los datos de prueba. Creo que no tiene sentido describir un ejemplo con la limpieza/deshacer la base de datos. En mi práctica, no he conocido un proyecto en el que este procedimiento transcurriera sin problemas.
El riesgo de interrumpir el rendimiento del circuito de prueba al probar la configuración general del sistema. Por ejemplo, usuario/grupo/contraseña/política de aplicación.
Los datos de prueba de las pruebas automáticas dificultan la vida de los probadores manuales.
Alguien dirá que las buenas pruebas automáticas deberían limpiar los datos por sí mismas. Tengo argumentos en contra:
Los soportes dinámicos son muy cómodos de usar.
No todos los objetos se pueden eliminar del sistema a través de la API. Por ejemplo, no se implementa una llamada para eliminar un objeto, ya que contradice la lógica comercial.
Al crear un objeto a través de la API, se puede crear una gran cantidad de metadatos que es problemático eliminar.
Si las pruebas dependen unas de otras, el proceso de limpiar los datos después de ejecutar 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. ¡Esto se está convirtiendo en un verdadero circo PK/FK! Escuchamos de los desarrolladores: "Acabo de agregar/eliminar/cambiar el nombre del letrero, ¿por qué fracasaron las pruebas de integración 100500?"
En mi opinión, la solución más óptima es un entorno dinámico.
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, swarm y otras plataformas de orquestación de contenedores. No todas las empresas los tienen. Sería bueno si docker-compose.yml fuera universal.
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í?
¿Cómo recopilar registros de servicios probados?
¿Cómo limpiar el corredor?
Tengo mi propio ejecutor de GitLab para mis proyectos y me encontré con estos problemas mientras desarrollaba Cliente Java para carril de prueba. Más específicamente, al ejecutar pruebas de integración. Aquí continuaremos resolviendo estos problemas con ejemplos de este proyecto.
Para un corredor, recomiendo una máquina virtual Linux con 4 vCPU, 4 GB de RAM, 50 GB de disco duro.
Hay mucha información sobre cómo configurar gitlab-runner en Internet, así que en resumen:
Vamos a la máquina vía SSH
Si tiene menos de 8 GB de RAM, le recomiendo hacer swap 10 GBpara que no venga el OOM killer y nos mate tareas por falta de memoria RAM. Esto puede suceder cuando se ejecutan más de 5 tareas al mismo tiempo. Las tareas serán más lentas, pero estables.
Ejemplo con asesino OOM
Si en los registros de tareas ves bash: line 82: 26474 Killed, luego solo ejecuta en el corredor 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, agregue intercambio o agregue RAM.
Esto le permitirá ejecutar tareas paralelas en el mismo corredor. Leer más aquí.
Si tiene una máquina más poderosa, por ejemplo, 8 vCPU, 16 GB de RAM, entonces 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.
La tarea principal es un archivo docker-compose.yml universal que los desarrolladores/evaluadores pueden usar tanto localmente como en la canalización de CI.
En primer lugar, creamos nombres de servicio únicos para CI. Una de las variables únicas en GitLab CI es la variable CI_JOB_ID. si especificas container_name con valor "service-${CI_JOB_ID:-local}", entonces en el caso:
si CI_JOB_ID no definido en las variables de entorno,
entonces el nombre del servicio será service-local
si CI_JOB_ID definido en variables de entorno (por ejemplo, 123),
entonces el nombre del servicio será service-123
En segundo lugar, creamos una red común para ejecutar servicios. Esto nos brinda aislamiento a nivel de red cuando se ejecutan múltiples entornos de prueba.
En realidad, este es el primer paso hacia el éxito =)
Un ejemplo de mi docker-compose.yml con comentarios
version: "3"
# Для корректной работы web (php) и fmt нужно,
# чтобы контейнеры имели общий исполняемый контент.
# В нашем случае, это директория /var/www/testrail
volumes:
static-content:
# Изолируем окружение на сетевом уровне
networks:
default:
external:
name: testrail-network-${CI_JOB_ID:-local}
services:
db:
image: mysql:5.7.22
# Каждый container_name содержит ${CI_JOB_ID:-local}
container_name: "testrail-mysql-${CI_JOB_ID:-local}"
environment:
MYSQL_HOST: db
MYSQL_DATABASE: mydb
MYSQL_ROOT_PASSWORD: 1234
SKIP_GRANT_TABLES: 1
SKIP_NETWORKING: 1
SERVICE_TAGS: dev
SERVICE_NAME: mysql
networks:
- default
migration:
image: registry.gitlab.com/touchbit/image/testrail/migration:latest
container_name: "testrail-migration-${CI_JOB_ID:-local}"
links:
- db
depends_on:
- db
networks:
- default
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
networks:
- default
web:
image: registry.gitlab.com/touchbit/image/testrail/web:latest
container_name: "testrail-web-${CI_JOB_ID:-local}"
# Если переменные 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
networks:
- default
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)
# создаем директорию с идентификатором задачи
- mkdir ${CI_JOB_ID}
# копируем в созданную директорию наш docker-compose.yml
# чтобы контекст был разный для каждой задачи
- cp .indirect/docker-compose.yml ${CI_JOB_ID}/docker-compose.yml
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-kill
artifacts:
# сохраняем логи
when: always
paths:
- logs
expire_in: 30 days
Como resultado de ejecutar dicha tarea, el directorio de registros en artefactos contendrá registros de servicios y pruebas. Lo cual es muy útil en caso de errores. Cada prueba en paralelo escribe su propio registro, pero hablaré de esto por separado.
$ 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_static-content" with default driver
Creating testrail-mysql-204645172 ...
Creating testrail-mysql-204645172 ... done
Creating testrail-migration-204645172 ... done
Creating testrail-fpm-204645172 ... done
Creating testrail-web-204645172 ... 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 testrail-web-204645172
01d303262d8e registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 16 seconds ago Up 13 seconds 9000/tcp testrail-fpm-204645172
2cdab1edbf6a registry.gitlab.com/touchbit/image/testrail/migration:latest "docker-entrypoint.s…" 16 seconds ago Up 13 seconds 3306/tcp, 33060/tcp testrail-migration-204645172
826aaf7c0a29 mysql:5.7.22 "docker-entrypoint.s…" 18 seconds ago Up 16 seconds 3306/tcp testrail-mysql-204645172
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 testrail-web-204645084
3540f8d448ce registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 38 seconds ago Up 35 seconds 9000/tcp testrail-fpm-204645084
70fea72aa10d mysql:5.7.22 "docker-entrypoint.s…" 40 seconds ago Up 37 seconds 3306/tcp testrail-mysql-204645084
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 testrail-web-204644881
6d4ccd910fad registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" About a minute ago Up About a minute 9000/tcp testrail-fpm-204644881
685d8023a3ec mysql:5.7.22 "docker-entrypoint.s…" About a minute ago Up About a minute 3306/tcp testrail-mysql-204644881
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 testrail-web-204644793
6f26dfb2683e registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" About a minute ago Up About a minute 9000/tcp testrail-fpm-204644793
029e16b26201 mysql:5.7.22 "docker-entrypoint.s…" About a minute ago Up About a minute 3306/tcp testrail-mysql-204644793
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 testrail-web-204567103
04339229397e registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 5 hours ago Up 5 hours 9000/tcp testrail-fpm-204567103
6ae0accab28d mysql:5.7.22 "docker-entrypoint.s…" 5 hours ago Up 5 hours 3306/tcp testrail-mysql-204567103
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 testrail-web-204553690
033b1f46afa9 registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 5 hours ago Up 5 hours 9000/tcp testrail-fpm-204553690
a8879c5ef941 mysql:5.7.22 "docker-entrypoint.s…" 5 hours ago Up 5 hours 3306/tcp testrail-mysql-204553690
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 testrail-web-204553539
ed6b17d911a5 registry.gitlab.com/touchbit/image/testrail/fpm:latest "docker-php-entrypoi…" 5 hours ago Up 5 hours 9000/tcp testrail-fpm-204553539
1a1eed057ea0 mysql:5.7.22 "docker-entrypoint.s…" 5 hours ago Up 5 hours 3306/tcp testrail-mysql-204553539
Todas las tareas completadas con éxito
Los artefactos de tareas contienen registros de servicios y pruebas
Todo parece ser hermoso, pero hay un matiz. La canalización se puede cancelar por la fuerza mientras se ejecutan las pruebas de integración, en cuyo caso no se detendrá la ejecución de contenedores. De vez en cuando es necesario limpiar el corredor. Desafortunadamente, la tarea de revisión en GitLab CE todavía está en el estado Abierto
Pero hemos agregado un inicio de tarea programado, 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 conchas.
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 un contenedor.
Los registros de servicio y prueba se recopilan y adjuntan a la tarea de canalización.
Es posible limpiar el corredor de imágenes acoplables antiguas.
El tiempo de preparación es de ~2 horas.
Eso, de hecho, es todo. Estaré encantado de comentarios.