GitLab Shell Runner. Execução concorrente de serviços testados utilizando Docker Compose

GitLab Shell Runner. Execução concorrente de serviços testados utilizando Docker Compose

Este artigo será interessante tanto para testadores quanto para desenvolvedores, mas é mais voltado para automatizadores que enfrentaram o problema de configurar o GitLab CI/CD para a realização de testes de integração em condições de escassez de recursos de infraestrutura e/ou na ausência de uma plataforma de orquestração de contêineres. Vou mostrar como configurar a implantação de ambientes de teste usando o docker compose em um único runner do GitLab shell, garantindo que a implantação de vários ambientes não interfira nos serviços em execução entre si.


Conteúdo

Premissas

  1. Na minha experiência, frequentemente precisei "curar" testes de integração em projetos. E muitas vezes, o primeiro e mais significativo problema é o pipeline CI, no qual o teste de integração do serviço(s) em desenvolvimento ocorre no ambiente dev/stage. Isso trouxe muitos problemas: Devido a falhas em um ou outro serviço durante o teste de integração, o contorno de teste pode ser corrompido por dados inválidos. Já houve casos em que o envio de uma solicitação com formato JSON corrompido travava o serviço, levando o ambiente a um estado completamente inoperante.

    • O desempenho do contorno de teste diminui com o aumento dos dados de teste. Acredito que descrever um exemplo de limpeza/rollback de BD não faz sentido. Na minha prática, nunca encontrei um projeto onde esse procedimento corresse suavemente.
    • O risco de prejudicar o funcionamento do contorno de teste ao testar as configurações gerais do sistema. Por exemplo, user/group/password/application policy.
    • Os dados de teste dos testes automatizados dificultam a vida dos testadores manuais.
    • Alguém pode dizer que bons testes automatizados devem limpar os dados após a execução. Tenho argumentos contrários:

    Ambientes dinâmicos são muito convenientes de usar.

    • Nem todos os objetos podem ser removidos do sistema através da API. Por exemplo, o chamado para deletar um objeto não foi implementado, pois contraria a lógica de negócios.
    • Ao criar um objeto através da API, pode ser gerada uma enorme quantidade de metadados, que são difíceis de remover.
    • Se os testes têm dependência entre si, o processo de limpeza de dados após a execução dos testes se transforma em uma dor de cabeça.
    • Chamadas adicionais (e, na minha opinião, injustificadas) à API.
    • Chamadas adicionais (e, na minha opinião, não justificadas) à API.
    • E o argumento principal: quando os dados de teste começam a ser limpos diretamente do BD. Isso se transforma em um verdadeiro circo de PK/FK! Dos desenvolvedores, ouve-se: "Eu só adicionei/removi/renomeei uma tabela, por que 100500 testes de integração falharam?"

    Na minha opinião, a solução mais otimizada é um ambiente dinâmico.

  2. Muita gente usa docker-compose para iniciar ambientes de teste, mas poucos o utilizam durante os testes de integração em CI/CD. E aqui não estou considerando kubernetes, swarm e outras plataformas de orquestração de contêineres. Nem toda empresa as tem. Seria bom se o docker-compose.yml fosse universal.
  3. Se temos nosso próprio runner de QA, como podemos garantir que os serviços iniciados através do docker-compose não interfiram uns com os outros?
  4. Como coletar logs dos serviços testados?
  5. Como limpar o runner?

Eu tenho meu próprio runner GitLab para meus projetos e eu enfrentei essas questões durante o desenvolvimento do cliente Java para TestRail. E, mais especificamente, ao executar testes de integração. A partir daqui, vamos resolver essas questões com exemplos deste projeto.

Para o conteúdo

GitLab Shell Runner

Para o runner, recomendo uma VM Linux com 4 vCPU, 4 GB de RAM e 50 GB de HDD.
Na vastidão da internet, há muita informação sobre como configurar o gitlab-runner, então, de forma breve:

  • Acesse a máquina via SSH
  • Se você tiver menos de 8 GB de RAM, recomendo criar um swap de 10 GB, para que o OOM killer não venha e mate nossas tarefas por falta de RAM. Isso pode acontecer quando mais de 5 tarefas são executadas ao mesmo tempo. As tarefas passarão mais devagar, mas de forma estável.

    Exemplo com OOM killer

    Se nos logs da tarefa você ver bash: line 82: 26474 Killed, basta executar no runner sudo dmesg | grep 26474

    [26474]  1002 26474  1061935   123806     339        0             0 java
    Fora da memória: Matar o processo 26474 (java) com pontuação 127 ou sacrificar o filho
    Matou o processo 26474 (java) total-vm:4247740kB, anon-rss:495224kB, file-rss:0kB, shmem-rss:0kB

    E se a imagem parecer mais ou menos assim, adicione swap ou aumente a RAM.

  • Instalando gitlab-runner, docker, docker-compose, faça.
  • Adicione o usuário gitlab-runner ao grupo docker
    sudo groupadd docker
    sudo usermod -aG docker gitlab-runner
  • Registramos gitlab-runner.
  • Abra para edição /etc/gitlab-runner/config.toml e adicione

    concurrent=20
    [[runners]]
      request_concurrency = 10

    Isso permitirá que tarefas paralelas sejam executadas em um único runner. Para mais detalhes, leia aqui.
    Se você tiver uma máquina mais poderosa, como 8 vCPU, 16 GB de RAM, esses números podem ser aumentados em pelo menos duas vezes. Mas tudo depende do que exatamente será executado neste runner e em que quantidade.

Isso é suficiente.

Para o conteúdo

Preparação do docker-compose.yml

A principal tarefa é criar um docker-compose.yml universal que os desenvolvedores/testadores possam usar tanto localmente quanto no CI pipeline.

Primeiro, criamos nomes exclusivos para os serviços no CI. Uma das variáveis exclusivas no GitLab CI é a variável CI_JOB_ID. Se definir container_name com o valor "service-${CI_JOB_ID:-local}", então no caso:

  • se CI_JOB_ID não estiver definida nas variáveis de ambiente,
    o nome do serviço será service-local
  • se CI_JOB_ID definida nas variáveis de ambiente (por exemplo, 123),
    o nome do serviço será service-123

Em segundo lugar, criamos uma rede comum para os serviços em execução. Isso nos proporciona isolamento a nível de rede ao executar vários ambientes de teste.

networks:
  default:
    external:
      name: service-network-${CI_JOB_ID:-local}

Esse é o primeiro passo para o sucesso =)

Exemplo do meu docker-compose.yml com comentários

version: "3"

# Para o funcionamento correto do web (php) e fmt, 
# os containers precisam ter um conteúdo executável compartilhado.
# Neste caso, é o diretório /var/www/testrail
volumes:
  static-content:

# Isolamos o ambiente a nível de rede
networks:
  default:
    external:
      name: testrail-network-${CI_JOB_ID:-local}

services:
  db:
    image: mysql:5.7.22
    # Cada container_name contém ${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}"
    # Se as variáveis TR_HTTP_PORT ou TR_HTTPS_PORT não estiverem definidas,
    # o serviço será levantado nas portas 80 e 443, respectivamente.
    ports:
      - ${TR_HTTP_PORT:-80}:80
      - ${TR_HTTPS_PORT:-443}:443
    volumes:
      - static-content:/var/www/testrail
    links:
      - db
      - fpm
    networks:
      - default

Exemplo de execução local

docker-compose -f docker-compose.yml up -d
Iniciando   testrail-mysql-local     ... concluído
Iniciando   testrail-migration-local ... concluído
Iniciando   testrail-fpm-local       ... concluído
Recriando testrail-web-local       ... concluído

Mas nem tudo é tão simples ao executar no CI.

Para o conteúdo

Preparação do Makefile

Eu uso Makefile, pois é bastante conveniente tanto para gerenciamento local do ambiente quanto no CI. A seguir, comentários em linha

# У меня в проектах все вспомогательные вещи лежат в директории `.indirect`,
# в том числе и `docker-compose.yml`

# Использовать bash с опцией pipefail 
# pipefail - фейлит выполнение пайпа, если команда выполнилась с ошибкой
SHELL=/bin/bash -o pipefail

# Останавливаем контейнеры и удаляем сеть
docker-kill:
    docker-compose -f $${CI_JOB_ID:-.indirect}/docker-compose.yml kill
    docker network rm network-$${CI_JOB_ID:-testrail} || true

# Предварительно выполняем docker-kill 
docker-up: docker-kill
    # Создаем сеть для окружения 
    docker network create network-$${CI_JOB_ID:-testrail}
    # Забираем последние образы из docker-registry
    docker-compose -f $${CI_JOB_ID:-.indirect}/docker-compose.yml pull
    # Запускаем окружение
    # force-recreate - принудительное пересоздание контейнеров
    # renew-anon-volumes - не использовать volumes предыдущих контейнеров
    docker-compose -f $${CI_JOB_ID:-.indirect}/docker-compose.yml up --force-recreate --renew-anon-volumes -d
    # Ну и, на всякий случай, вывести что там у нас в принципе запущено на машинке
    docker ps

# Коллектим логи сервисов
docker-logs:
    mkdir ./logs || true
    docker logs testrail-web-$${CI_JOB_ID:-local}       >& logs/testrail-web.log
    docker logs testrail-fpm-$${CI_JOB_ID:-local}       >& logs/testrail-fpm.log
    docker logs testrail-migration-$${CI_JOB_ID:-local} >& logs/testrail-migration.log
    docker logs testrail-mysql-$${CI_JOB_ID:-local}     >& logs/testrail-mysql.log

# Очистка раннера
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

Verificamos

make docker-up

$ make docker-up 
docker-compose -f ${CI_JOB_ID:-.indirect}\/docker-compose.yml kill
Killing testrail-web-local   ... concluído
Killing testrail-fpm-local   ... concluído
Killing testrail-mysql-local ... concluído
docker network rm network-${CI_JOB_ID:-testrail} || true
network-testrail
docker network create network-${CI_JOB_ID:-testrail}
d2ec063324081c8bbc1b08fd92242c2ea59d70cf4025fab8efcbc5c6360f083f
docker-compose -f ${CI_JOB_ID:-.indirect}\/docker-compose.yml pull
Pulling db        ... concluído
Pulling migration ... concluído
Pulling fpm       ... concluído
Pulling web       ... concluído
docker-compose -f ${CI_JOB_ID:-.indirect}\/docker-compose.yml up --force-recreate --renew-anon-volumes -d
Recreating testrail-mysql-local ... concluído
Recreating testrail-fpm-local       ... concluído
Recreating testrail-migration-local ... concluído
Recreating testrail-web-local       ... concluído
docker ps
CONTAINER ID  PORTS                                     NAMES
a845d3cb0e5a  0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp  testrail-web-local
19d8ef001398  9000/tcp                                  testrail-fpm-local
e28840a2369c  3306/tcp, 33060/tcp                       testrail-migration-local
0e7900c23f37  3306/tcp                                  testrail-mysql-local

make docker-logs

$ make docker-logs
mkdir .\/logs || true
mkdir: não é possível criar o diretório ‘.\/logs’: O diretório já existe
docker logs testrail-web-${CI_JOB_ID:-local}       >& logs\/testrail-web.log
docker logs testrail-fpm-${CI_JOB_ID:-local}       >& logs\/testrail-fpm.log
docker logs testrail-migration-${CI_JOB_ID:-local} >& logs\/testrail-migration.log
docker logs testrail-mysql-${CI_JOB_ID:-local}     >& logs\/testrail-mysql.log

GitLab Shell Runner. Execução concorrente de serviços testados utilizando Docker Compose

Para o conteúdo

Preparação do .gitlab-ci.yml

Execução de testes de integração

Integração:
  estágio: teste
  tags:
    - my-shell-runner
  before_script:
    # Autenticando no registro
    - docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
    # Gerando TR_HTTP_PORT e TR_HTTPS_PORT pseudo-únicos
    - export TR_HTTP_PORT=$(shuf -i10000-60000 -n1)
    - export TR_HTTPS_PORT=$(shuf -i10000-60000 -n1)
    # criando diretório com o ID da tarefa
    - mkdir ${CI_JOB_ID}
    # copiando nosso docker-compose.yml para o diretório criado
    # para que o contexto seja diferente para cada tarefa
    - cp .indirect\/docker-compose.yml ${CI_JOB_ID}\/docker-compose.yml
  script:
    # levantando nosso ambiente
    - make docker-up
    # executando testes com jar executável (é assim que faço)
    - java -jar itest.jar --http-port ${TR_HTTP_PORT} --https-port ${TR_HTTPS_PORT}
    # ou no contêiner
    - docker run --network=testrail-network-${CI_JOB_ID:-local} --rm itest
  after_script:
    # coletando logs
    - make docker-logs
    # parando o ambiente
    - make docker-kill
  artifacts:
    # salvando logs
    when: always
    paths:
      - logs
    expire_in: 30 dias

Como resultado da execução dessa tarefa, o diretório logs nos artefatos conterá os logs dos serviços e testes. Isso é muito conveniente em caso de erros. Cada teste, em paralelo, escreve seu próprio log, mas falarei sobre isso separadamente.

GitLab Shell Runner. Execução concorrente de serviços testados utilizando Docker Compose

Para o conteúdo

Limpeza do runner

A tarefa será executada apenas conforme agendado.

stages:
- clean
- build
- test

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

Em seguida, vamos ao nosso projeto no GitLab -> CI/CD -> Schedules -> New Schedule e adicionamos um novo agendamento

GitLab Shell Runner. Execução concorrente de serviços testados utilizando Docker Compose

Para o conteúdo

Resultado

Executamos 4 tarefas no GitLab CI
GitLab Shell Runner. Execução concorrente de serviços testados utilizando Docker Compose

Nos logs da última tarefa com os testes de integração, vemos contêineres de diferentes tarefas.

ID DO CONTÊINER  NOME
c6b76f9135ed  testrail-web-204645172
01d303262d8e  testrail-fpm-204645172
2cdab1edbf6a  testrail-migration-204645172
826aaf7c0a29  testrail-mysql-204645172
6dbb3fae0322  testrail-web-204645084
3540f8d448ce  testrail-fpm-204645084
70fea72aa10d  testrail-mysql-204645084
d8aa24b2892d  testrail-web-204644881
6d4ccd910fad  testrail-fpm-204644881
685d8023a3ec  testrail-mysql-204644881
1cdfc692003a  testrail-web-204644793
6f26dfb2683e  testrail-fpm-204644793
029e16b26201  testrail-mysql-204644793
c10443222ac6  testrail-web-204567103
04339229397e  testrail-fpm-204567103
6ae0accab28d  testrail-mysql-204567103
b66b60d79e43  testrail-web-204553690
033b1f46afa9  testrail-fpm-204553690
a8879c5ef941  testrail-mysql-204553690
069954ba6010  testrail-web-204553539
ed6b17d911a5  testrail-fpm-204553539
1a1eed057ea0  testrail-mysql-204553539

Log mais detalhado

$ docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
AVISO! Usar --password via CLI é inseguro. Utilize --password-stdin.
AVISO! Sua senha será armazenada sem criptografia em /home/gitlab-runner/.docker/config.json.
Configure um auxiliar de credenciais para remover este aviso. Veja
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login realizado com sucesso
$ 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
Erro: Rede não encontrada: testrail-network-204645172
docker network create testrail-network-${CI_JOB_ID:-local}
0a59552b4464b8ab484de6ae5054f3d5752902910bacb0a7b5eca698766d0331
docker-compose -f ${CI_JOB_ID:-.indirect}/docker-compose.yml pull
Baixando web       ... concluído
Baixando fpm       ... concluído
Baixando migration ... concluído
Baixando db        ... concluído
docker-compose -f ${CI_JOB_ID:-.indirect}/docker-compose.yml up --force-recreate --renew-anon-volumes -d
Criando volume "204645172_static-content" com driver padrão
Criando testrail-mysql-204645172 ... 
Criando testrail-mysql-204645172 ... concluído
Criando testrail-migration-204645172 ... concluído
Criando testrail-fpm-204645172       ... concluído
Criando testrail-web-204645172       ... concluído
docker ps
ID DO CONTÊINER        IMAGEM                                                          COMANDO                  CRIADO              STATUS              PORTAS                                           NOMES
c6b76f9135ed        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   há 13 segundos       Em execução há 1 segundo         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…"   há 16 segundos       Em execução há 13 segundos       9000/tcp                                        testrail-fpm-204645172
2cdab1edbf6a        registry.gitlab.com/touchbit/image/testrail/migration:latest   "docker-entrypoint.s…"   há 16 segundos       Em execução há 13 segundos       3306/tcp, 33060/tcp                             testrail-migration-204645172
826aaf7c0a29        mysql:5.7.22                                                   "docker-entrypoint.s…"   há 18 segundos       Em execução há 16 segundos       3306/tcp                                        testrail-mysql-204645172
6dbb3fae0322        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   há 36 segundos       Em execução há 22 segundos       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…"   há 38 segundos       Em execução há 35 segundos       9000/tcp                                        testrail-fpm-204645084
70fea72aa10d        mysql:5.7.22                                                   "docker-entrypoint.s…"   há 40 segundos       Em execução há 37 segundos       3306/tcp                                        testrail-mysql-204645084
d8aa24b2892d        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   há cerca de um minuto   Em execução há 53 segundos       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…"   há cerca de um minuto   Em execução há cerca de um minuto   9000/tcp                                        testrail-fpm-204644881
685d8023a3ec        mysql:5.7.22                                                   "docker-entrypoint.s…"   há cerca de um minuto   Em execução há cerca de um minuto   3306/tcp                                        testrail-mysql-204644881
1cdfc692003a        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   há cerca de um minuto   Em execução há cerca de um minuto   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…"   há cerca de um minuto   Em execução há cerca de um minuto   9000/tcp                                        testrail-fpm-204644793
029e16b26201        mysql:5.7.22                                                   "docker-entrypoint.s…"   há cerca de um minuto   Em execução há cerca de um minuto   3306/tcp                                        testrail-mysql-204644793
c10443222ac6        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   há 5 horas          Em execução há 5 horas          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…"   há 5 horas          Em execução há 5 horas          9000/tcp                                        testrail-fpm-204567103
6ae0accab28d        mysql:5.7.22                                                   "docker-entrypoint.s…"   há 5 horas          Em execução há 5 horas          3306/tcp                                        testrail-mysql-204567103
b66b60d79e43        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   há 5 horas          Em execução há 5 horas          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…"   há 5 horas          Em execução há 5 horas          9000/tcp                                        testrail-fpm-204553690
a8879c5ef941        mysql:5.7.22                                                   "docker-entrypoint.s…"   há 5 horas          Em execução há 5 horas          3306/tcp                                        testrail-mysql-204553690
069954ba6010        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   há 5 horas          Em execução há 5 horas          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…"   há 5 horas          Em execução há 5 horas          9000/tcp                                        testrail-fpm-204553539
1a1eed057ea0        mysql:5.7.22                                                   "docker-entrypoint.s…"   há 5 horas          Em execução há 5 horas          3306/tcp                                        testrail-mysql-204553539

Todas as tarefas foram concluídas com sucesso

Os artefatos da tarefa contêm logs dos serviços e testes
GitLab Shell Runner. Execução concorrente de serviços testados utilizando Docker Compose

GitLab Shell Runner. Execução concorrente de serviços testados utilizando Docker Compose

Parece que tudo está em ordem, mas existe um detalhe. O pipeline pode ser cancelado manualmente durante a execução dos testes de integração, e nesse caso, os contêineres em execução não serão parados. De vez em quando, é necessário limpar o runner. Infelizmente, a tarefa de aprimoramento no GitLab CE ainda está pendente Abrir

Mas nós adicionamos a execução da tarefa de acordo com um cronograma, e ninguém nos impede de executá-la manualmente.
Vamos ao nosso projeto -> CI/CD -> Agendas e executamos a tarefa Limpar runner

GitLab Shell Runner. Execução concorrente de serviços testados utilizando Docker Compose

No total:

  • Temos um shell runner.
  • Não há conflitos entre as tarefas e o ambiente.
  • Temos a execução paralela de tarefas com testes de integração.
  • É possível executar os testes de integração tanto localmente quanto no contêiner.
  • Os logs dos serviços e testes são coletados e anexados à tarefa do pipeline.
  • Há a possibilidade de limpar o runner de imagens Docker antigas.

O tempo de configuração é de aproximadamente 2 horas.
E é isso. Agradeço feedback.

Para o conteúdo

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster