JUnit дар GitLab CI бо Kubernetes

Сарфи назар аз он, ки ҳама хуб медонанд, ки санҷиши нармафзори шумо муҳим ва зарур аст ва бисёриҳо онро муддати тӯлонӣ ба таври худкор иҷро мекунанд, дар фарохи Ҳабр ягон дорухат барои ташкили комбинатсияи чунин маҳсулоти маъмул вуҷуд надошт. ин чароғ ҳамчун (дӯстдоштаи мо) GitLab ва JUnit. Биёед ин холигоҳро пур кунем!

JUnit дар GitLab CI бо Kubernetes

Муқаддима

Аввалан, биёед чанд контекст диҳам:

  • Азбаски ҳамаи замимаҳои мо дар Kubernetes кор мекунанд, мо санҷишҳоро дар инфрасохтори мувофиқ баррасӣ хоҳем кард.
  • Барои васлкунӣ ва ҷойгиркунӣ мо истифода мебарем верф (дар робита ба ҷузъҳои инфрасохтор, ин инчунин ба таври худкор маънои онро дорад, ки Helm иштирок мекунад).
  • Ман ба тафсилоти эҷоди воқеии санҷишҳо намеравам: дар ҳолати мо, муштарӣ санҷишҳоро худаш менависад ва мо танҳо оғози онҳоро таъмин мекунем (ва мавҷудияти гузориши мувофиқ дар дархости якҷоякунӣ).


Пайдарҳамии умумии амалҳо чӣ гуна хоҳад буд?

  1. Сохтани барнома - мо тавсифи ин марҳиларо сарфи назар мекунем.
  2. Барномаро дар фазои алоҳидаи кластери Kubernetes ҷойгир кунед ва озмоишро оғоз кунед.
  3. Ҷустуҷӯи артефактҳо ва таҳлили гузоришҳои JUnit бо GitLab.
  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

Кубернитель

Ҳоло дар директория .helm/templates биёед 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 ва PVC, ки ба шумо имкон медиҳад, ки маълумоти санҷиширо нигоҳ доред.

Диққат ба ҳолати муқаддимавӣ бо if дар оғози манифест - мувофиқан, дигар файлҳои YAML диаграммаи Helm бо ариза бояд печонида шаванд баръакс тарҳрезӣ кунед, то онҳо ҳангоми санҷиш ҷойгир нашаванд. Яъне:

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

Бо вуҷуди ин, агар санҷишҳо баъзе инфраструктураро талаб мекунанд (масалан, Redis, RabbitMQ, Mongo, PostgreSQL...) - YAML-ҳои онҳо метавонанд не хомӯш кардан. Онҳоро дар муҳити озмоишӣ низ ҷойгир кунед ... онҳоро мувофиқи он, ки шумо мувофиқед, танзим кунед, албатта.

Дастрасии ниҳоӣ

Зеро монтаж ва ҷойгиркунӣ бо истифода аз корҳои werf ҳоло танҳо дар сервери созанда (бо gitlab-runner) ва pod бо санҷишҳо дар усто оғоз карда мешавад, шумо бояд директория эҷод кунед /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... Ҳайрон нашавед: ҳамчун як қисми омодасозии мақола дар санҷиши барномаи аввал ягон хатогӣ пайдо нашуд, вале онхо дар дигар чо пайдо шуданд.

хулоса

Тавре ки шумо мебинед, ҳеҷ чиз мураккаб нест!

Аслан, агар шумо аллакай коллектори снаряд дошта бошед ва он кор мекунад, аммо ба шумо Kubernetes лозим нест, замима кардани санҷиш ба он вазифаи боз ҳам соддатар аз дар ин ҷо тавсифшуда хоҳад буд. Ва дар Ҳуҷҷатҳои GitLab CI шумо мисолҳоро барои Ruby, Go, Gradle, Maven ва баъзеи дигар хоҳед ёфт.

PS

Инчунин дар блоги мо хонед:

Манбаъ: will.com

Илова Эзоҳ