Kubernetes ilə GitLab CI-də JUnit

Proqramınızın sınaqdan keçirilməsinin vacib və zəruri olduğunu hər kəs mükəmməl bilməsinə və bir çoxunun bunu uzun müddətdir avtomatik həyata keçirməsinə baxmayaraq, Habrın genişliyində belə məşhur məhsulların birləşməsini qurmaq üçün bir resept yox idi. bu niş (sevdiyimiz) GitLab və JUnit kimi. Gəlin bu boşluğu dolduraq!

Kubernetes ilə GitLab CI-də JUnit

Giriş

Əvvəlcə bəzi kontekstləri verim:

  • Bütün tətbiqlərimiz Kubernetes-də işlədiyindən, müvafiq infrastrukturda sınaqların keçirilməsini nəzərdən keçirəcəyik.
  • Quraşdırma və yerləşdirmə üçün istifadə edirik werf (infrastruktur komponentləri baxımından bu, avtomatik olaraq Helmin də iştirak etdiyini bildirir).
  • Testlərin faktiki yaradılmasının təfərrüatlarına girməyəcəyəm: bizim vəziyyətimizdə müştəri testləri özü yazır və biz yalnız onların işə salınmasını (və birləşmə sorğusunda müvafiq hesabatın olmasını) təmin edirik.


Hərəkətlərin ümumi ardıcıllığı necə olacaq?

  1. Tətbiqin qurulması - bu mərhələnin təsvirini buraxacağıq.
  2. Tətbiqi Kubernetes klasterinin ayrıca ad sahəsinə yerləşdirin və sınaqdan keçirməyə başlayın.
  3. Artefaktların axtarışı və GitLab ilə JUnit hesabatlarının təhlili.
  4. Əvvəllər yaradılmış ad sahəsinin silinməsi.

İndi - həyata keçirməyə!

nizamlama

GitLab CI

Bir fraqmentlə başlayaq .gitlab-ci.yaml, bu proqramın yerləşdirilməsi və testlərin icrasını təsvir edir. Siyahı olduqca həcmli oldu, buna görə də şərhlərlə hərtərəfli əlavə edildi:

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

İndi kataloqda .helm/templates Gəlin İşlə YAML yaradaq - tests-job.yaml — testləri və ehtiyac duyduğu Kubernetes resurslarını işə salmaq. Siyahıdan sonra izahatlara baxın:

{{- 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 }}

Nə cür resurslar bu konfiqurasiyada təsvir edilmişdir? Yerləşdirmə zamanı biz layihə üçün unikal ad sahəsi yaradırıq (bu, .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) və yuvarlayın:

  1. ConfigMap test skripti ilə;
  2. podun təsviri və müəyyən edilmiş direktivlə command, yalnız testləri həyata keçirir;
  3. PV və PVC, test məlumatlarını saxlamağa imkan verir.

İlə giriş vəziyyətinə diqqət yetirin if manifestin əvvəlində - müvafiq olaraq, tətbiq ilə Helm diaqramının digər YAML faylları sarılmalıdır. tərs sınaq zamanı yerləşdirilməməsi üçün dizayn edin. Yəni:

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

Ancaq testlər olsa müəyyən infrastruktur tələb edir (məsələn, Redis, RabbitMQ, Mongo, PostgreSQL...) - onların YAML-ləri ola bilər heç bir söndür. Onları sınaq mühitinə də yerləşdirin... təbii ki, uyğun gördüyünüz kimi tənzimləyin.

Final toxunuşu

Çünki werf istifadə edərək montaj və yerləşdirmə hələlik işləyir yalnız qurma serverində (gitlab-runner ilə) və testləri olan pod masterdə işə salındıqda, kataloq yaratmalı olacaqsınız /mnt/tests ustanın üstünə qoyun və qaçışçıya verin, məsələn, NFS vasitəsilə. Təfərrüatlı bir nümunə ilə izahat tapa bilərsiniz K8s sənədləri.

Nəticə belə olacaq:

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

Heç kim NFS paylaşımını birbaşa gitlab-runner-da etməyi və sonra onu podlara quraşdırmağı qadağan etmir.

Qeyd

Siz sadəcə olaraq testlərlə skripti birbaşa shell runner üzərində işlədə bilirsinizsə, niyə İş yaratmaqla hər şeyi çətinləşdirdiyinizi soruşa bilərsiniz? Cavab olduqca mənasızdır...

Bəzi testlər onların düzgün işlədiyini yoxlamaq üçün infrastruktura (MongoDB, RabbitMQ, PostgreSQL və s.) giriş tələb edir. Testi vahid edirik - bu yanaşma ilə bu cür əlavə obyektləri daxil etmək asan olur. Bundan əlavə, biz əldə edirik standart yerləşdirmə yanaşması (NFS istifadə edilsə belə, kataloqların əlavə quraşdırılması).

Nəticə

Hazırlanmış konfiqurasiyanı tətbiq edərkən nə görəcəyik?

Birləşmə sorğusu ən son boru xəttində həyata keçirilən testlər üçün ümumi statistikanı göstərəcək:

Kubernetes ilə GitLab CI-də JUnit

Hər bir səhv təfərrüat üçün buraya kliklənə bilər:

Kubernetes ilə GitLab CI-də JUnit

NB: Diqqətli oxucu qeyd edəcək ki, biz NodeJS proqramını sınaqdan keçiririk, ekran görüntülərində isə - .NET... Təəccüblənməyin: sadəcə olaraq, məqaləni hazırlayarkən birinci proqramın sınaqdan keçirilməsində heç bir xəta aşkar olunmayıb, lakin onlar başqasında tapılmışdır.

Nəticə

Gördüyünüz kimi, mürəkkəb bir şey yoxdur!

Prinsipcə, əgər artıq bir qabıq kollektorunuz varsa və o işləyirsə, lakin Kubernetlərə ehtiyacınız yoxdursa, ona sınaq əlavə etmək burada təsvir ediləndən daha sadə bir iş olacaq. Və içində GitLab CI sənədləri Ruby, Go, Gradle, Maven və digərləri üçün nümunələr tapa bilərsiniz.

PS

Bloqumuzda da oxuyun:

Mənbə: www.habr.com

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