Kubernetes bilan GitLab CI da JUnit

Har bir inson sizning dasturiy ta'minotingizni sinab ko'rish muhim va zarur ekanligini juda yaxshi bilishiga va ko'pchilik buni uzoq vaqtdan beri avtomatik ravishda amalga oshirayotganiga qaramay, Habrning keng hududida bunday mashhur mahsulotlarning kombinatsiyasini yaratish uchun bitta retsept mavjud emas edi. bu joy (bizning sevimli) GitLab va JUnit sifatida. Keling, bu bo'shliqni to'ldiraylik!

Kubernetes bilan GitLab CI da JUnit

Kirish

Birinchidan, men bir nechta kontekstni keltiraman:

  • Barcha ilovalarimiz Kubernetes-da ishlayotganligi sababli, biz tegishli infratuzilmada sinovlarni o'tkazishni ko'rib chiqamiz.
  • O'rnatish va joylashtirish uchun biz foydalanamiz werf (infratuzilma komponentlari nuqtai nazaridan, bu avtomatik ravishda Helm ishtirok etganligini anglatadi).
  • Men testlarni haqiqiy yaratish tafsilotlariga kirmayman: bizning holatlarimizda mijoz testlarni o'zi yozadi va biz faqat ularning ishga tushirilishini ta'minlaymiz (va birlashtirish so'rovida tegishli hisobot mavjudligi).


Umumiy harakatlar ketma-ketligi qanday bo'ladi?

  1. Ilovani yaratish - biz ushbu bosqichning tavsifini o'tkazib yuboramiz.
  2. Ilovani Kubernetes klasterining alohida nom maydoniga joylashtiring va sinovni boshlang.
  3. GitLab yordamida artefaktlarni qidirish va JUnit hisobotlarini tahlil qilish.
  4. Oldindan yaratilgan nom maydonini o'chirish.

Endi - amalga oshirish uchun!

moslashish

GitLab CI

Bir parcha bilan boshlaylik .gitlab-ci.yaml, bu ilovani joylashtirish va testlarni bajarishni tavsiflaydi. Ro'yxat juda katta bo'lib chiqdi, shuning uchun u sharhlar bilan to'ldirildi:

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

Endi katalogda .helm/templates keling, Job bilan YAML yarataylik - tests-job.yaml - testlarni va unga kerak bo'lgan Kubernetes resurslarini ishga tushirish. Ro'yxatga kiritilgandan keyin tushuntirishlarni ko'ring:

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

Qanday resurslar ushbu konfiguratsiyada tasvirlangan? Joylashtirishda biz loyiha uchun noyob nom maydoni yaratamiz (bu .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG}) va uni yoyib chiqing:

  1. ConfigMap test skripti bilan;
  2. ish pod tavsifi va belgilangan direktiva bilan command, bu faqat testlarni bajaradi;
  3. PV va PVX, bu sizga test ma'lumotlarini saqlash imkonini beradi.

Bilan kirish shartiga e'tibor bering if manifestning boshida - shunga ko'ra, Helm diagrammasining boshqa YAML fayllari ilova bilan o'ralgan bo'lishi kerak. teskari Ular sinov paytida joylashtirilmasligi uchun dizayn. Ya'ni:

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

Biroq, agar testlar bo'lsa ba'zi infratuzilmani talab qiladi (masalan, Redis, RabbitMQ, Mongo, PostgreSQL...) - ularning YAML'lari bo'lishi mumkin yo'q o'chirib qo'yish. Ularni sinov muhitiga ham joylashtiring... ularni o‘zingizga mos ravishda moslashtiring, albatta.

oxirgi teginish

Chunki werf yordamida yig'ish va joylashtirish hozircha ishlaydi faqatgina qurish serverida (gitlab-runner bilan) va masterda testlar bilan pod ishga tushirilsa, siz katalog yaratishingiz kerak bo'ladi. /mnt/tests usta ustiga va yuguruvchiga bering, masalan, NFS orqali. Batafsil misolni tushuntirishlar bilan topish mumkin K8s hujjatlari.

Natija quyidagicha bo'ladi:

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

Hech kim NFS ulushini gitlab-runner-da to'g'ridan-to'g'ri qilishni va keyin uni podkastlarga o'rnatishni taqiqlamaydi.

nota

Siz shunchaki testlar bilan skriptni to'g'ridan-to'g'ri qobiq yuguruvchisida ishga tushirishingiz mumkin bo'lsa, nima uchun Ish yaratish orqali hamma narsani murakkablashtirishingiz kerakligini so'rashingiz mumkin? Javob juda ahamiyatsiz ...

Ba'zi testlar to'g'ri ishlashini tekshirish uchun infratuzilmaga (MongoDB, RabbitMQ, PostgreSQL va boshqalar) kirishni talab qiladi. Biz testni birlashtirilgan qilamiz - bu yondashuv bilan bunday qo'shimcha ob'ektlarni kiritish oson bo'ladi. Bunga qo'shimcha ravishda, biz olamiz standarti tarqatish yondashuvi (hatto NFS-dan foydalansa ham, kataloglarni qo'shimcha o'rnatish).

natija

Tayyorlangan konfiguratsiyani qo'llaganimizda nimani ko'ramiz?

Birlashtirish soʻrovi soʻnggi bosqichda oʻtkazilgan testlar uchun umumiy statistikani koʻrsatadi:

Kubernetes bilan GitLab CI da JUnit

Tafsilotlar uchun har bir xatoni bu yerga bosish mumkin:

Kubernetes bilan GitLab CI da JUnit

NB: Diqqatli o‘quvchi biz NodeJS ilovasini sinovdan o‘tkazayotganimizni payqaydi, skrinshotlarda esa – .NET... Hayron bo‘lmang: shunchaki maqolani tayyorlash jarayonida birinchi dasturni sinab ko‘rishda xatolik topilmadi, lekin ular boshqasida topilgan.

xulosa

Ko'rib turganingizdek, hech qanday murakkab narsa yo'q!

Aslida, agar sizda qobiq kollektoringiz bo'lsa va u ishlayotgan bo'lsa, lekin sizga Kubernetes kerak bo'lmasa, unga sinovni biriktirish bu erda tasvirlanganidan ham oddiyroq vazifa bo'ladi. Va ichida GitLab CI hujjatlari Ruby, Go, Gradle, Maven va boshqalarga misollar topasiz.

PS

Shuningdek, bizning blogimizda o'qing:

Manba: www.habr.com

a Izoh qo'shish