
Este artigo será do interesse de testadores e desenvolvedores, mas é voltado para as montadoras que enfrentam o problema de configurar o GitLab CI / CD para testes de integração em condições de recursos de infraestrutura insuficientes e / ou falta de uma plataforma de orquestração de contêiner. Vou explicar como configurar a implantação de ambientes de teste usando o docker compose em um único shell run do GitLab e, para implantar vários ambientes, os serviços lançados não interferem entre si.
Conteúdo
Antecedentes
Na minha prática, costumava "curar" testes de integração em projetos. E, freqüentemente, o primeiro e mais significativo problema é o pipeline de IC, no qual os testes de integração dos serviços desenvolvidos são realizados em um ambiente de desenvolvimento / estágio. Isso causou alguns problemas:
- Devido a defeitos em um serviço específico durante o teste de integração, o circuito de teste pode ser corrompido por dados quebrados. Houve casos em que o envio de uma solicitação com um formato JSON quebrado interrompeu um serviço, o que tornou o suporte completamente inoperante.
- Retardando o ciclo de teste com o crescimento dos dados de teste. Eu acho que não faz sentido descrever um exemplo com a limpeza / reversão de um banco de dados. Na minha prática, não vi um projeto em que esse procedimento tenha ocorrido sem problemas.
- O risco de interromper o desempenho do circuito de teste ao testar configurações gerais do sistema. Por exemplo, política de usuário / grupo / senha / aplicativo.
- Os dados de teste dos autotestes impedem a vida dos testadores manuais.
Alguém dirá que bons autotestes devem limpar os dados depois de si mesmos. Eu tenho argumentos contra:
- Os suportes dinâmicos são muito convenientes de usar.
- Nem todo objeto pode ser excluído do sistema por meio da API. Por exemplo, uma chamada para excluir um objeto não é implementada, pois contradiz a lógica de negócios.
- Ao criar um objeto por meio da API, é possível criar uma grande quantidade de metadados, o que é problemático para excluir.
- Se os testes forem interdependentes, o processo de limpeza de dados após a conclusão dos testes se transformará em uma dor de cabeça.
- Chamadas adicionais (e, na minha opinião, não justificadas) para a API.
- E o argumento principal: quando os dados de teste começam a ser limpos diretamente do banco de dados. Ele se transforma em um verdadeiro circo PK / FK! Dos desenvolvedores, é audível: "Eu apenas adicionei / excluí / renomeei a placa de identificação, por que os testes de integração 100500 falharam?"
Na minha opinião, a solução mais ideal é um ambiente dinâmico.
- Muitas pessoas usam o docker-compose para executar um ambiente de teste, mas poucas usam o docker-compose ao realizar testes de integração no CI / CD. E aqui não levo em consideração kubernetes, enxame e outras plataformas de orquestração de contêineres. Nem toda empresa os possui. Seria bom se o docker-compose.yml fosse universal.
- Mesmo se tivermos nosso próprio corredor de controle de qualidade, como podemos garantir que os serviços iniciados pelo docker-compose não interfiram entre si?
- Como coletar logs de serviços testados?
- Como limpar o corredor?
Eu tenho meu próprio corredor GitLab para meus projetos e me deparei com esses problemas ao desenvolver um cliente Java para TestRail . Ou melhor, ao executar testes de integração. A seguir, resolveremos esses problemas com exemplos deste projeto.
Para o conteúdo
Corredor shell Gitlab
Para o corredor, recomendo uma máquina virtual Linux com 4 vCPU, 4 GB de RAM, 50 GB de disco rígido.
Na Internet, muitas informações sobre a configuração do gitlab-runner, de forma resumida:
- Vamos para a máquina no SSH
Se você tiver menos de 8 GB de RAM, recomendo fazer uma troca de 10 GB para que o assassino do OOM não venha e não nos mate devido à falta de RAM. Isso pode acontecer quando mais de 5 tarefas são iniciadas simultaneamente. As tarefas serão mais lentas, mas estáveis.
Exemplo de assassino de OOMSe você bash: line 82: 26474 Killed
nos logs de tarefas, simplesmente execute 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
E se a imagem se parecer com isso, adicione swap ou descarte a RAM.
- Instale o gitlab-runner , docker , docker-compose , make.
- Adicionar usuário
gitlab-runner
ao grupo de gitlab-runner
docker
sudo groupadd docker sudo usermod -aG docker gitlab-runner
- Registre o gitlab-runner.
Abra para editar o /etc/gitlab-runner/config.toml
e adicione
concurrent=20 [[runners]] request_concurrency = 10
Isso permitirá que você execute tarefas paralelas em um corredor. Leia mais aqui .
Se sua máquina for mais poderosa, por exemplo, 8 vCPU, 16 GB de RAM, esses números poderão ser feitos pelo menos duas vezes maior. Mas tudo depende do que exatamente será lançado nesse corredor e em que quantidade.
Isso é o suficiente.
Para o conteúdo
Preparando o docker-compose.yml
A tarefa principal é o docker-compose.yml, que será usado localmente e no pipeline do IC.
A variável COMPOSE_PROJECT_NAME será usada para iniciar várias instâncias do ambiente (consulte makefile ).
Um exemplo do meu 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
Para o conteúdo
Preparação de Makefile
Eu uso o Makefile, pois é muito conveniente para o gerenciamento local do ambiente e no IC.
Comentários adicionais estão em linha
# `.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
Verifique o lançamento 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
Verificando o lançamento do IC $ 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
Verificar coleção de logs $ 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

Para o conteúdo
Preparando .gitlab-ci.yml
Executar testes de integração
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 do lançamento de uma tarefa nos artefatos, o diretório logs conterá os logs de serviços e testes. O que é muito conveniente em caso de erros. Para mim, cada teste em paralelo grava seu próprio log, mas falarei sobre isso separadamente.

Para o conteúdo
Limpeza de corredores
A tarefa será executada apenas dentro do cronograma.
stages: - clean - build - test Clean runner: stage: clean only: - schedules tags: - my-shell-runner script: - make docker-clean
Em seguida, acesse nosso projeto GitLab -> CI / CD -> Agendamentos -> Novo agendamento e adicione um novo agendamento

Para o conteúdo
Resultado
Execute 4 tarefas no IC do GitLab

Nos logs da última tarefa com testes de integração, vemos contêineres de diferentes tarefas
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 mais detalhado $ 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 as tarefas concluídas com sucessoOs artefatos de tarefas contêm logs de serviço e teste


Parece que tudo é lindo, mas há uma nuance. O pipeline pode ser cancelado à força durante a execução de testes de integração; nesse caso, a execução de contêineres não será interrompida. De tempos em tempos, você precisa limpar o corredor. Infelizmente, a tarefa de revisão no GitLab CE ainda está no status Aberto
Mas adicionamos o lançamento da tarefa agendada e ninguém nos proíbe de iniciá-lo manualmente.
Vá para o nosso projeto -> CI / CD -> Agendamentos e execute a tarefa Clean runner

Total:
- Nós temos um corredor de concha.
- Não há conflitos entre tarefas e o ambiente.
- Temos um lançamento paralelo de tarefas com testes de integração.
- Você pode executar testes de integração localmente e no contêiner.
- Logs de serviços e testes são coletados e anexados à tarefa de pipeline.
- É possível limpar o corredor a partir de imagens antigas do docker.
O tempo de configuração é de ~ 2 horas.
Isso, de fato, é tudo. Terei o maior prazer em feedback.
PS
Agradecimentos especiais a freeseacher vvasilenok ivanych . Seus comentários foram muito valiosos no contexto da publicação.
Para o conteúdo