JUnit GitLab CI жүйесінде Kubernetes көмегімен

Бағдарламалық жасақтаманы тестілеу маңызды және қажет екенін бәрі жақсы біледі және көпшілігі оны ұзақ уақыт бойы автоматты түрде жасайды, Хабрдың кеңдігінде мұндай танымал өнімдердің комбинациясын орнатудың бірде-бір рецепті болған жоқ. бұл тауашалар (біздің сүйікті) GitLab және JUnit. Осы олқылықтың орнын толтырайық!

JUnit GitLab CI жүйесінде Kubernetes көмегімен

Кіріспе

Алдымен, кейбір контекстті берейін:

  • Біздің барлық қолданбаларымыз Kubernetes жүйесінде жұмыс істейтіндіктен, біз сәйкес инфрақұрылымда сынақтарды іске қосуды қарастырамыз.
  • Біз құрастыру және орналастыру үшін қолданамыз верф (инфрақұрылымдық құрамдас бөліктерге қатысты бұл автоматты түрде Helm қатысатынын білдіреді).
  • Мен сынақтардың нақты жасалуының егжей-тегжейіне тоқталмаймын: біздің жағдайда клиент сынақтарды өзі жазады және біз олардың іске қосылуын ғана қамтамасыз етеміз (және біріктіру сұрауында сәйкес есептің болуы).


Жалпы әрекеттер тізбегі қандай болады?

  1. Қолданбаны құру - біз бұл кезеңнің сипаттамасын өткізбейміз.
  2. Қолданбаны Kubernetes кластерінің бөлек аттар кеңістігіне орналастырып, тестілеуді бастаңыз.
  3. GitLab көмегімен артефактілерді іздеу және JUnit есептерін талдау.
  4. Бұрын жасалған аттар кеңістігін жою.

Енді - іске асыруға!

реттеу

GitLab CI

Фрагменттен бастайық .gitlab-ci.yaml, ол қолданбаны қолдануды және сынақтарды орындауды сипаттайды. Листинг өте көлемді болды, сондықтан ол түсініктемелермен мұқият толықтырылды:

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

Қазір каталогта .helm/templates Job көмегімен YAML жасайық - tests-job.yaml — сынақтарды және оған қажет Kubernetes ресурстарын іске қосу. Тізімнен кейін түсініктемелерді қараңыз:

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

Қандай ресурстар осы конфигурацияда сипатталған? Орналастыру кезінде біз жоба үшін бірегей аттар кеңістігін жасаймыз (бұл .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) және оны шығарыңыз:

  1. ConfigMap сынақ сценарийімен;
  2. жұмыс подкасттың сипаттамасымен және көрсетілген директивамен command, ол тек сынақтарды орындайды;
  3. PV және ПВХ, ол сынақ деректерін сақтауға мүмкіндік береді.

-мен кіріспе шартына назар аударыңыз if манифесттің басында - сәйкесінше, қолданбасы бар Helm диаграммасының басқа YAML файлдары оралуы керек кері тестілеу кезінде орналастырылмайтындай етіп жасаңыз. Яғни:

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

Дегенмен, егер сынақтар болса кейбір инфрақұрылымды қажет етеді (мысалы, Redis, RabbitMQ, Mongo, PostgreSQL...) - олардың YAML болуы мүмкін емес өшіру. Оларды сынақ ортасына да орналастырыңыз... оларды өз қалауыңыз бойынша реттеңіз, әрине.

Соңғы байланыс

Өйткені werf көмегімен құрастыру және орналастыру әзірше жұмыс істейді тек құрастыру серверінде (gitlab-runner көмегімен) және сынақтары бар подкаст мастерде іске қосылса, сізге каталог жасау қажет болады. /mnt/tests қожайынға және оны жүгірушіге беріңіз, мысалы, NFS арқылы. Түсініктемелері бар егжей-тегжейлі мысалды мына жерден табуға болады K8s құжаттамасы.

Нәтиже болады:

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

Ешкім NFS бөлісуін gitlab-runner-де тікелей жасауға, содан кейін оны блоктарға орнатуға тыйым салмайды.

ескерту

Тікелей қабықша жүгіргішінде сынақтары бар сценарийді іске қоса алсаңыз, неге жұмыс жасау арқылы бәрін қиындата аласыз деп сұрайтын шығарсыз? Жауап өте тривиальды ...

Кейбір сынақтар дұрыс жұмыс істейтінін тексеру үшін инфрақұрылымға (MongoDB, RabbitMQ, PostgreSQL, т.б.) кіруді талап етеді. Біз тестілеуді біртұтас етіп жасаймыз - бұл тәсілмен мұндай қосымша нысандарды қосу оңай болады. Бұған қоса, біз аламыз стандартты орналастыру тәсілі (тіпті NFS пайдаланылса да, каталогтарды қосымша орнату).

нәтиже

Дайындалған конфигурацияны қолданғанда не көреміз?

Біріктіру сұрауы соңғы контурда орындалған сынақтар үшін жиынтық статистиканы көрсетеді:

JUnit GitLab CI жүйесінде Kubernetes көмегімен

Әрбір қатені егжей-тегжейлі білу үшін мына жерден басуға болады:

JUnit GitLab CI жүйесінде Kubernetes көмегімен

NB: Мұқият оқырман NodeJS қосымшасын сынап жатқанымызды байқайды, ал скриншоттарда - .NET... Таң қалмаңыз: жай ғана мақаланы дайындау кезінде бірінші қолданбаны тестілеуде қателер табылмады, бірақ олар басқасынан табылды.

қорытынды

Көріп отырғаныңыздай, күрделі ештеңе жоқ!

Негізінде, егер сізде қабық жинағышыңыз болса және ол жұмыс істейді, бірақ сізге Кубернетес қажет болмаса, оған тестілеуді қосу мұнда сипатталғандан да оңайырақ болады. Және ішінде GitLab CI құжаттамасы сіз Ruby, Go, Gradle, Maven және басқаларына мысалдарды таба аласыз.

PS

Біздің блогта да оқыңыз:

Ақпарат көзі: www.habr.com

пікір қалдыру