JUnit GitLab CI su Kubernetes

Nepaisant to, kad visi puikiai žino, kad jūsų programinės įrangos testavimas yra svarbus ir būtinas, o daugelis jau ilgą laiką tai daro automatiškai, Habro platybėse nebuvo vieno recepto, kaip sukurti tokių populiarių produktų derinį. ši niša kaip (mūsų mėgstamiausia) „GitLab“ ir „JUnit“. Užpildykime šią spragą!

JUnit GitLab CI su Kubernetes

Įžanginis

Pirmiausia leiskite man pateikti šiek tiek konteksto:

  • Kadangi visos mūsų programos veikia Kubernetes, apsvarstysime galimybę atlikti atitinkamos infrastruktūros bandymus.
  • Surinkimui ir diegimui naudojame werf (Kalbant apie infrastruktūros komponentus, tai taip pat automatiškai reiškia, kad Helmas dalyvauja).
  • Į faktinio testų kūrimo detales nesileisiu: mūsų atveju klientas pats rašo testus, o mes tik užtikriname jų paleidimą (ir atitinkamos ataskaitos buvimą sujungimo užklausoje).


Kaip atrodys bendra veiksmų seka?

  1. Programos kūrimas – šio etapo aprašymo praleisime.
  2. Įdiekite programą į atskirą Kubernetes klasterio vardų sritį ir pradėkite testavimą.
  3. Artefaktų paieška ir JUnit ataskaitų analizavimas naudojant „GitLab“.
  4. Anksčiau sukurtos vardų erdvės ištrynimas.

Dabar – prie įgyvendinimo!

reguliavimas

„GitLab CI“

Pradėkime nuo fragmento .gitlab-ci.yaml, kuriame aprašomas programos diegimas ir bandymų vykdymas. Sąrašas pasirodė gana didelis, todėl buvo kruopščiai papildytas komentarais:

variables:
# объявляем версию werf, которую собираемся использовать
  WERF_VERSION: "1.0 beta"

.base_deploy: &base_deploy
  script:
# создаем namespace в K8s, если его нет
    - kubectl --context="${WERF_KUBE_CONTEXT}" get ns ${CI_ENVIRONMENT_SLUG} || kubectl create ns ${CI_ENVIRONMENT_SLUG}
# загружаем werf и деплоим — подробнее об этом см. в документации
# (https://werf.io/how_to/gitlab_ci_cd_integration.html#deploy-stage)
    - type multiwerf && source <(multiwerf use ${WERF_VERSION})
    - werf version
    - type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
    - werf deploy --stages-storage :local
      --namespace ${CI_ENVIRONMENT_SLUG}
      --set "global.commit_ref_slug=${CI_COMMIT_REF_SLUG:-''}"
# передаем переменную `run_tests`
# она будет использоваться в рендере Helm-релиза
      --set "global.run_tests=${RUN_TESTS:-no}"
      --set "global.env=${CI_ENVIRONMENT_SLUG}"
# изменяем timeout (бывают долгие тесты) и передаем его в релиз
      --set "global.ci_timeout=${CI_TIMEOUT:-900}"
     --timeout ${CI_TIMEOUT:-900}
  dependencies:
    - Build

.test-base: &test-base
  extends: .base_deploy
  before_script:
# создаем директорию для будущего отчета, исходя из $CI_COMMIT_REF_SLUG
    - mkdir /mnt/tests/${CI_COMMIT_REF_SLUG} || true
# вынужденный костыль, т.к. GitLab хочет получить артефакты в своем build-dir’е
    - mkdir ./tests || true
    - ln -s /mnt/tests/${CI_COMMIT_REF_SLUG} ./tests/${CI_COMMIT_REF_SLUG}
  after_script:
# после окончания тестов удаляем релиз вместе с Job’ом
# (и, возможно, его инфраструктурой)
    - type multiwerf && source <(multiwerf use ${WERF_VERSION})
    - werf version
    - type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
    - werf dismiss --namespace ${CI_ENVIRONMENT_SLUG} --with-namespace
# мы разрешаем падения, но вы можете сделать иначе
  allow_failure: true
  variables:
    RUN_TESTS: 'yes'
# задаем контекст в werf
# (https://werf.io/how_to/gitlab_ci_cd_integration.html#infrastructure)
    WERF_KUBE_CONTEXT: 'admin@stage-cluster'
  tags:
# используем раннер с тегом `werf-runner`
    - werf-runner
  artifacts:
# требуется собрать артефакт для того, чтобы его можно было увидеть
# в пайплайне и скачать — например, для более вдумчивого изучения
    paths:
      - ./tests/${CI_COMMIT_REF_SLUG}/*
# артефакты старше недели будут удалены
    expire_in: 7 day
# важно: эти строки отвечают за парсинг отчета GitLab’ом
    reports:
      junit: ./tests/${CI_COMMIT_REF_SLUG}/report.xml

# для упрощения здесь показаны всего две стадии
# в реальности же у вас их будет больше — как минимум из-за деплоя
stages:
  - build
  - tests

build:
  stage: build
  script:
# сборка — снова по документации по werf
# (https://werf.io/how_to/gitlab_ci_cd_integration.html#build-stage)
    - type multiwerf && source <(multiwerf use ${WERF_VERSION})
    - werf version
    - type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
    - werf build-and-publish --stages-storage :local
  tags:
    - werf-runner
  except:
    - schedules

run tests:
  <<: *test-base
  environment:
# "сама соль" именования namespace’а
# (https://docs.gitlab.com/ce/ci/variables/predefined_variables.html)
    name: tests-${CI_COMMIT_REF_SLUG}
  stage: tests
  except:
    - schedules

Kubernetes

Dabar kataloge .helm/templates sukurkime YAML su Job - tests-job.yaml — atlikti testus ir jai reikalingus Kubernetes išteklius. Žr. paaiškinimus po įtraukimo į sąrašą:

{{- if eq .Values.global.run_tests "yes" }}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: tests-script
data:
  tests.sh: |
    echo "======================"
    echo "${APP_NAME} TESTS"
    echo "======================"

    cd /app
    npm run test:ci
    cp report.xml /app/test_results/${CI_COMMIT_REF_SLUG}/

    echo ""
    echo ""
    echo ""

    chown -R 999:999 /app/test_results/${CI_COMMIT_REF_SLUG}
---
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ .Chart.Name }}-test
  annotations:
    "helm.sh/hook": post-install,post-upgrade
    "helm.sh/hook-weight": "2"
    "werf/watch-logs": "true"
spec:
  activeDeadlineSeconds: {{ .Values.global.ci_timeout }}
  backoffLimit: 1
  template:
    metadata:
      name: {{ .Chart.Name }}-test
    spec:
      containers:
      - name: test
        command: ['bash', '-c', '/app/tests.sh']
{{ tuple "application" . | include "werf_container_image" | indent 8 }}
        env:
        - name: env
          value: {{ .Values.global.env }}
        - name: CI_COMMIT_REF_SLUG
          value: {{ .Values.global.commit_ref_slug }}
       - name: APP_NAME
          value: {{ .Chart.Name }}
{{ tuple "application" . | include "werf_container_env" | indent 8 }}
        volumeMounts:
        - mountPath: /app/test_results/
          name: data
        - mountPath: /app/tests.sh
          name: tests-script
          subPath: tests.sh
      tolerations:
      - key: dedicated
        operator: Exists
      - key: node-role.kubernetes.io/master
        operator: Exists
      restartPolicy: OnFailure
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: {{ .Chart.Name }}-pvc
      - name: tests-script
        configMap:
          name: tests-script
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: {{ .Chart.Name }}-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Mi
  storageClassName: {{ .Chart.Name }}-{{ .Values.global.commit_ref_slug }}
  volumeName: {{ .Values.global.commit_ref_slug }}

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: {{ .Values.global.commit_ref_slug }}
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 10Mi
  local:
    path: /mnt/tests/
  nodeAffinity:
   required:
     nodeSelectorTerms:
     - matchExpressions:
       - key: kubernetes.io/hostname
         operator: In
         values:
         - kube-master
  persistentVolumeReclaimPolicy: Delete
  storageClassName: {{ .Chart.Name }}-{{ .Values.global.commit_ref_slug }}
{{- end }}

Kokie ištekliai aprašyta šioje konfigūracijoje? Diegdami sukuriame unikalią projekto vardų erdvę (tai nurodyta .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) ir išskleiskite:

  1. „ConfigMap“ su testo scenarijumi;
  2. darbas su ankšties aprašymu ir nurodyta direktyva command, kuri tiesiog atlieka testus;
  3. PV ir PVC, kurios leidžia saugoti bandymo duomenis.

Atkreipkite dėmesį į įvadinę sąlygą su if manifesto pradžioje - atitinkamai kiti Helm diagramos YAML failai su programa turi būti suvynioti į atvirkščiai suprojektuoti taip, kad bandymo metu jie nebūtų naudojami. Tai yra:

{{- if ne .Values.global.run_tests "yes" }}
---
я другой ямлик
{{- end }}

Tačiau jei bandymai reikia tam tikros infrastruktūros (pvz., Redis, RabbitMQ, Mongo, PostgreSQL...) – jų YAML gali būti ne Išjunk. Įdiekite juos ir į bandomąją aplinką... žinoma, pakoreguokite, kaip jums atrodo tinkama.

Galutinis prisilietimas

Nes surinkimas ir diegimas naudojant werf darbus tik kūrimo serveryje (su gitlab-runner), o pod su testais paleidžiamas pagrindiniame kompiuteryje, turėsite sukurti katalogą /mnt/tests ant šeimininko ir atiduok bėgikui, pavyzdžiui, per NFS. Išsamų pavyzdį su paaiškinimais galite rasti K8s dokumentacija.

Rezultatas bus:

user@kube-master:~$ cat /etc/exports | grep tests
/mnt/tests    IP_gitlab-builder/32(rw,nohide,insecure,no_subtree_check,sync,all_squash,anonuid=999,anongid=998)

user@gitlab-runner:~$ cat /etc/fstab | grep tests
IP_kube-master:/mnt/tests    /mnt/tests   nfs4    _netdev,auto  0       0

Niekas nedraudžia kurti NFS bendrinimo tiesiogiai „gitlab-runner“, o vėliau jį prijungti prie lizdų.

Atkreipti dėmesį

Galbūt klausiate, kam viską komplikuoti kuriant darbą, jei galite tiesiog paleisti scenarijų su testais tiesiogiai apvalkalo vykdyklyje? Atsakymas gana trivialus...

Kai kuriems bandymams reikalinga prieiga prie infrastruktūros (MongoDB, RabbitMQ, PostgreSQL ir kt.), kad būtų patikrinta, ar jie veikia tinkamai. Testavimą suvienodiname – taikant šį metodą tampa lengva įtraukti tokius papildomus objektus. Be to, mes gauname standartas diegimo metodas (net jei naudojamas NFS, papildomas katalogų montavimas).

Rezultatas

Ką matysime, kai pritaikysime paruoštą konfigūraciją?

Sujungimo užklausoje bus rodoma naujausio bandymo atliktų bandymų statistikos suvestinė:

JUnit GitLab CI su Kubernetes

Kiekvieną klaidą galite spustelėti čia, kad gautumėte daugiau informacijos:

JUnit GitLab CI su Kubernetes

NB: Dėmesingas skaitytojas pastebės, kad mes testuojame NodeJS aplikaciją, o ekrano nuotraukose - .NET... Nenustebkite: tiesiog rengiant straipsnį, testuojant pirmąją aplikaciją klaidų nerasta, tačiau jos buvo rasti kitame.

išvada

Kaip matote, nieko sudėtingo!

Iš principo, jei jau turite apvalkalo kolekcionierių ir jis veikia, bet jums nereikia Kubernetes, prie jo pritvirtinti testavimą bus dar paprastesnė užduotis nei aprašyta čia. Ir į GitLab CI dokumentacija rasite Ruby, Go, Gradle, Maven ir kai kurių kitų pavyzdžių.

PS

Taip pat skaitykite mūsų tinklaraštyje:

Šaltinis: www.habr.com

Добавить комментарий