GitLab Shell Runner. Konkurrentstart av testtjänster med Docker Compose

GitLab Shell Runner. Konkurrentstart av testtjänster med Docker Compose

Denna artikel kommer att intressera både testare och utvecklare, men är främst riktad till automatiserare som har stött på problem med att konfigurera GitLab CI/CD för att utföra integrationstester i en miljö med otillräckliga infrastruktursresurser och/eller avsaknad av en containerorkestreringsplattform. Jag kommer att berätta hur man konfigurerar distributionen av testmiljöer med hjälp av docker compose på en enda GitLab shell runner, så att de körda tjänsterna inte stör varandra vid distribution av flera miljöer.


Innehåll

Förutsättningar

  1. I min praktik har jag ofta behövt 'läka' integrationstester på projekt. Och ofta är det första och mest betydande problemet CI-pipelinen, i vilken integrationstester av den utvecklade tjänsten/erna utförs i dev/stage-miljö. Detta har orsakat en hel del problem:

    • På grund av defekter i någon tjänst kan testkretsen under integrationstestning bli skadad av ogiltiga data. Det har funnits fall där en begäran med ogiltig JSON-format hängde tjänsten, vilket ledde till att hela standen blev oanvändbar.
    • Fördröjning av testkretsens arbete med ökande testdata. Jag tror att det inte är meningsfullt att beskriva ett exempel med rensning/återställning av databasen. I min praktik har jag aldrig sett ett projekt där denna procedur gick smidigt.
    • Risk för att rubba testkretsens funktionalitet vid testning av gemensamma systeminställningar. Till exempel användare/grupp/lösenord/applikationspolicy.
    • Testdata från automatiska tester stör manuella testare.

    Någon skulle säga att bra automatiska tester bör rensa upp efter sig. Jag har argument som talar emot det:

    • Dynamiska testmiljöer är mycket bekväma att använda.
    • Inte varje objekt kan raderas från systemet via API. Till exempel är anropet för att radera ett objekt inte implementerat, eftersom det strider mot affärslogiken.
    • Vid skapandet av ett objekt via API kan en mängd metadata skapas, vilket är problematiskt att ta bort.
    • Om testen har beroenden mellan sig blir rensningen av data efter testerna en huvudvärk.
    • Ytterligare (och, enligt min åsikt, obefogade) anrop till API.
    • Och det största argumentet: när testdata börjar rensas direkt från databasen. Det förvandlas till en riktig PK/FK-cirkus! Utvecklarna säger: "Jag har bara lagt till/tagit bort/omdöpt en tabell, varför blev 100500 integrationstester drabbade?"

    Enligt min mening är den mest optimala lösningen en dynamisk miljö.

  2. Många använder docker-compose för att starta testmiljöer, men få använder docker-compose vid integrationstestning i CI/CD. Här tar jag inte hänsyn till Kubernetes, Swarm och andra containerorkestreringsplattformar. De finns inte i varje företag. Det skulle vara bra om docker-compose.yml var universell.
  3. Om vi till och med har vår egen QA-runner, hur gör vi så att tjänsterna som startas via docker-compose inte stör varandra?
  4. Hur samlar vi loggar från de testade tjänsterna?
  5. Hur rensar vi runnern?

Jag har en egen GitLab-runner för mina projekt och jag stötte på dessa frågor under utvecklingen. Java-klient för TestRail. Mer specifikt när vi kör integrationstester. Här kommer vi att lösa dessa frågor med exempel från detta projekt.

Till innehållet

GitLab Shell Runner

För runnern rekommenderar jag en Linux-virtuell maskin med 4 vCPU, 4 GB RAM, 50 GB HDD.
Det finns mycket information på internet om hur man konfigurerar gitlab-runner, så här kommer en kort sammanfattning:

  • Logga in på maskinen via SSH
  • Om du har mindre än 8 GB RAM, rekommenderar jag att göra en swap på 10 GB, så att OOM-killer inte kommer och dödar våra uppgifter på grund av brist på RAM. Detta kan hända när mer än 5 uppgifter körs samtidigt. Uppgifterna kommer att köras något långsammare, men stabilt.

    Exempel på OOM-killer

    Om du ser i loggarna för uppgiften bash: line 82: 26474 Killed, kör bara på runnern 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

    Och om bilden ser ut ungefär så här, antingen lägg till swap eller öka RAM.

  • Installera gitlab-runner, docker, docker-compose, gör.
  • Lägg till användaren gitlab-runner till gruppen docker
    sudo groupadd docker
    sudo usermod -aG docker gitlab-runner
  • Registrera gitlab-runner.
  • Öppna för redigering /etc/gitlab-runner/config.toml och lägg till

    concurrent=20
    [[runners]]
      request_concurrency = 10

    Detta kommer att tillåta parallella uppgifter på en runner. Läs mer detaljerat. ). Ett typiskt exempel på användning:.
    Om du har en mer kraftfull maskin, till exempel 8 vCPU, 16 GB RAM, kan dessa siffror göras minst dubbelt så stora. Men allt beror på vad som specifikt kommer att köras på denna runner och i vilken mängd.

Det är tillräckligt.

Till innehållet

Förberedelse av docker-compose.yml

Huvuduppgiften är att skapa en universell docker-compose.yml som utvecklare/testare kan använda både lokalt och i CI-pipeline.

Först skapar vi unika namn för tjänsterna för CI. En av de unika variablerna i GitLab CI är variabeln CI_JOB_ID. Om du anger container_name med värdet "service-${CI_JOB_ID:-local}", så i fallet:

  • om CI_JOB_ID inte är definierad i miljövariablerna,
    blir tjänstens namn service-local
  • om CI_JOB_ID är definierad i miljövariablerna (till exempel 123),
    blir tjänstens namn service-123

För det andra skapar vi ett gemensamt nätverk för de körande tjänsterna. Detta ger oss isolering på nätverksnivå när vi kör flera testmiljöer.

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

Detta är i själva verket det första steget mot framgång =)

Exempel på min docker-compose.yml med kommentarer

version: "3"

# För att web (php) och fmt ska fungera korrekt, 
# måste containrarna ha gemensamt körbart innehåll.
# I vårt fall är detta katalogen /var/www/testrail
volumes:
  static-content:

# Isolera miljön på nätverksnivå
networks:
  default:
    external:
      name: testrail-network-${CI_JOB_ID:-local}

services:
  db:
    image: mysql:5.7.22
    # Varje container_name innehåller ${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}"
    # Om variablerna TR_HTTP_PORT eller TR_HTTPS_PORTS inte är definierade,
    # startar tjänsten på port 80 respektive 443.
    ports:
      - ${TR_HTTP_PORT:-80}:80
      - ${TR_HTTPS_PORT:-443}:443
    volumes:
      - static-content:/var/www/testrail
    links:
      - db
      - fpm
    networks:
      - default

Exempel på lokal körning

docker-compose -f docker-compose.yml up -d
Starting   testrail-mysql-local     ... done
Starting   testrail-migration-local ... done
Starting   testrail-fpm-local       ... done
Recreating testrail-web-local       ... done

Men det är inte så enkelt med körning i CI.

Till innehållet

Förberedelse av Makefile

Jag använder Makefile eftersom det är mycket praktiskt för både lokal hantering av miljön och i CI. Härnäst kommer inline-kommentarer.

# У меня в проектах все вспомогательные вещи лежат в директории `.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

Kontrollerar

make docker-up

$ make docker-up 
docker-compose -f ${CI_JOB_ID:-.indirect}\/docker-compose.yml kill
Dödar testrail-web-local   ... klart
Dödar testrail-fpm-local   ... klart
Dödar testrail-mysql-local ... klart
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
Hämtar db        ... klart
Hämtar migration ... klart
Hämtar fpm       ... klart
Hämtar web       ... klart
docker-compose -f ${CI_JOB_ID:-.indirect}\/docker-compose.yml up --force-recreate --renew-anon-volumes -d
Återställs testrail-mysql-local ... klart
Återställs testrail-fpm-local       ... klart
Återställs testrail-migration-local ... klart
Återställs testrail-web-local       ... klart
docker ps
KONTROLLER ID  PORTAR                                     NAMN
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: kan inte skapa katalog ‘.\/logs’: Katalogen finns
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. Konkurrentstart av testtjänster med Docker Compose

Till innehållet

Förberedelse av .gitlab-ci.yml

Körning av integrationstester

Integration:
  steg: test
  tags:
    - my-shell-runner
  before_script:
    # Loggar in i registry
    - docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
    # Genererar pseudo-unika TR_HTTP_PORT och TR_HTTPS_PORT
    - export TR_HTTP_PORT=$(shuf -i10000-60000 -n1)
    - export TR_HTTPS_PORT=$(shuf -i10000-60000 -n1)
    # Skapar en katalog med uppgiftens ID
    - mkdir ${CI_JOB_ID}
    # Kopierar vår docker-compose.yml till den skapade katalogen
    # för att kontexten ska vara olika för varje uppgift
    - cp .indirect\/docker-compose.yml ${CI_JOB_ID}\/docker-compose.yml
  script:
    # Startar vår miljö
    - make docker-up
    # Kör tester med en körbar jar (så gör jag)
    - java -jar itest.jar --http-port ${TR_HTTP_PORT} --https-port ${TR_HTTPS_PORT}
    # eller i en container
    - docker run --network=testrail-network-${CI_JOB_ID:-local} --rm itest
  after_script:
    # Samlar in loggar
    - make docker-logs
    # Stoppar miljön
    - make docker-kill
  artifacts:
    # Sparar loggar
    när: alltid
    sökvägar:
      - logs
    utgång_in: 30 dagar

Som ett resultat av att denna uppgift körs kommer katalogen logs i artefakterna att innehålla loggar från tjänster och tester. Detta är mycket praktiskt i händelse av fel. Jag har varje test som skriver sina egna loggar parallellt, men det kommer jag att berätta om separat.

GitLab Shell Runner. Konkurrentstart av testtjänster med Docker Compose

Till innehållet

Rensa runner

Uppgiften kommer att köras endast enligt schema.

steg:
- rensa
- bygga
- testa

Rensa körning:
  steg: rensa
  endast:
    - scheman
  tags:
    - my-shell-runner
  script:
    - make docker-clean

Gå vidare till vårt GitLab-projekt -> CI/CD -> Scheman -> Nytt schema och lägg till ett nytt schema

GitLab Shell Runner. Konkurrentstart av testtjänster med Docker Compose

Till innehållet

Resultat

Vi kör 4 uppgifter i GitLab CI
GitLab Shell Runner. Konkurrentstart av testtjänster med Docker Compose

I loggarna för den senaste uppgiften med integrationstester ser vi containrar från olika uppgifter

CONTAINER ID  NAMN
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

Mer detaljerad logg

$ docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
VARNING! Användning av --password via CLI är osäkert. Använd --password-stdin.
VARNING! Ditt lösenord kommer att lagras okrypterat i /home/gitlab-runner/.docker/config.json.
Konfigurera en autentiseringshjälpare för att ta bort denna varning. Se
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Inloggning lyckades
$ 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
Fel: Ingen sådan nätverksanslutning: testrail-network-204645172
docker network create testrail-network-${CI_JOB_ID:-local}
0a59552b4464b8ab484de6ae5054f3d5752902910bacb0a7b5eca698766d0331
docker-compose -f ${CI_JOB_ID:-.indirect}/docker-compose.yml pull
Hämtar webb ... klar
Hämtar fpm ... klar
Hämtar migrering ... klar
Hämtar db ... klar
docker-compose -f ${CI_JOB_ID:-.indirect}/docker-compose.yml up --force-recreate --renew-anon-volumes -d
Skapar volymen "204645172_static-content" med standarddrivrutinen
Skapar testrail-mysql-204645172 ... 
Skapar testrail-mysql-204645172 ... klart
Skapar testrail-migration-204645172 ... klart
Skapar testrail-fpm-204645172       ... klart
Skapar testrail-web-204645172       ... klart
docker ps
KONTROLLERINGS-ID        BILD                                                          KOMMANDO                  SKAPAD              STATUS              PORTAR                                           NAMN
c6b76f9135ed        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   13 sekunder sedan       Up 1 sekund         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 sekunder sedan       Up 13 sekunder       9000/tcp                                        testrail-fpm-204645172
2cdab1edbf6a        registry.gitlab.com/touchbit/image/testrail/migration:latest   "docker-entrypoint.s…"   16 sekunder sedan       Up 13 sekunder       3306/tcp, 33060/tcp                             testrail-migration-204645172
826aaf7c0a29        mysql:5.7.22                                                   "docker-entrypoint.s…"   18 sekunder sedan       Up 16 sekunder       3306/tcp                                        testrail-mysql-204645172
6dbb3fae0322        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   36 sekunder sedan       Up 22 sekunder       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 sekunder sedan       Up 35 sekunder       9000/tcp                                        testrail-fpm-204645084
70fea72aa10d        mysql:5.7.22                                                   "docker-entrypoint.s…"   40 sekunder sedan       Up 37 sekunder       3306/tcp                                        testrail-mysql-204645084
d8aa24b2892d        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   Cirkus en minut sedan   Up 53 sekunder       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…"   Cirkus en minut sedan   Up Cirkus en minut   9000/tcp                                        testrail-fpm-204644881
685d8023a3ec        mysql:5.7.22                                                   "docker-entrypoint.s…"   Cirkus en minut sedan   Up Cirkus en minut   3306/tcp                                        testrail-mysql-204644881
1cdfc692003a        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   Cirkus en minut sedan   Up Cirkus en minut   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…"   Cirkus en minut sedan   Up Cirkus en minut   9000/tcp                                        testrail-fpm-204644793
029e16b26201        mysql:5.7.22                                                   "docker-entrypoint.s…"   Cirkus en minut sedan   Up Cirkus en minut   3306/tcp                                        testrail-mysql-204644793
c10443222ac6        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   För 5 timmar sedan          Up 5 timmar          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…"   För 5 timmar sedan          Up 5 timmar          9000/tcp                                        testrail-fpm-204567103
6ae0accab28d        mysql:5.7.22                                                   "docker-entrypoint.s…"   För 5 timmar sedan          Up 5 timmar          3306/tcp                                        testrail-mysql-204567103
b66b60d79e43        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   För 5 timmar sedan          Up 5 timmar          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…"   För 5 timmar sedan          Up 5 timmar          9000/tcp                                        testrail-fpm-204553690
a8879c5ef941        mysql:5.7.22                                                   "docker-entrypoint.s…"   För 5 timmar sedan          Up 5 timmar          3306/tcp                                        testrail-mysql-204553690
069954ba6010        registry.gitlab.com/touchbit/image/testrail/web:latest         "nginx -g 'daemon of…"   För 5 timmar sedan          Up 5 timmar          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…"   För 5 timmar sedan          Up 5 timmar          9000/tcp                                        testrail-fpm-204553539
1a1eed057ea0        mysql:5.7.22                                                   "docker-entrypoint.s…"   För 5 timmar sedan          Up 5 timmar          3306/tcp                                        testrail-mysql-204553539

Alla uppgifter har slutförts framgångsrikt

Uppgiftens artefakter innehåller loggar från tjänster och tester
GitLab Shell Runner. Konkurrentstart av testtjänster med Docker Compose

GitLab Shell Runner. Konkurrentstart av testtjänster med Docker Compose

Det ser bra ut, men det finns en detalj. Pipelines kan tvingas avbrytas under körning av integrationstester, och i sådana fall kommer de startade containrarna inte att stoppas. Ibland behöver man rengöra runnern. Tyvärr är uppgiften för vidareutveckling i GitLab CE fortfarande i status Öppen

Men vi har lagt till att köra uppgiften enligt schema, och ingen hindrar oss från att köra den manuellt.
Vi går till vårt projekt -> CI/CD -> Scheman och kör uppgiften Rengör runner

GitLab Shell Runner. Konkurrentstart av testtjänster med Docker Compose

Totalt:

  • Vi har en shell runner.
  • Det finns inga konflikter mellan uppgifterna och miljön.
  • Vi har parallell körning av uppgifter med integrationstester.
  • Integrationstester kan köras både lokalt och i en container.
  • Loggar från tjänster och tester samlas in och bifogas pipeline-uppgiften.
  • Det finns möjlighet att rengöra runnern från gamla dockerbilder.

Inställningstiden är cirka 2 timmar.
Och det är egentligen allt. Jag ser fram emot feedback.

Till innehållet

Källa: habr.com

Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar 🔥 Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster