JUnit në GitLab CI me Kubernetes

MegjithĂ«se tĂ« gjithĂ« e dinĂ« se Ă«shtĂ« e rĂ«ndĂ«sishme dhe e nevojshme tĂ« testosh softuerin tĂ«nd, dhe shumĂ« e bĂ«jnĂ« kĂ«tĂ« automatikisht, nĂ« hapĂ«sirat e Habra nuk u gjet asnjĂ« recetĂ« pĂ«r konfigurimin e lidhjes sĂ« produkteve kaq tĂ« njohura nĂ« kĂ«tĂ« niĆŸĂ«, siç janĂ« (tĂ« preferuarit tanĂ«) GitLab dhe JUnit. Do ta plotĂ«sojmĂ« kĂ«tĂ« boshllĂ«k!

JUnit në GitLab CI me Kubernetes

Hyrja

Fillimisht do të përcaktoj kontekstin:

  • Duke pasur parasysh se tĂ« gjitha aplikacionet tona punojnĂ« nĂ« Kubernetes, do tĂ« shqyrtohet ekzekutimi i testeve nĂ« infrastrukturĂ«n pĂ«rkatĂ«se.
  • PĂ«r ndĂ«rtimin dhe deklerimin pĂ«rdorim werf (nĂ« kuptimin e komponentĂ«ve tĂ« infrastrukturĂ«s, kjo gjithashtu automatikisht nĂ«nkupton se Helm Ă«shtĂ« i angazhuar).
  • Nuk do tĂ« thellohem nĂ« detajet e krijimit tĂ« testeve: nĂ« rastin tonĂ«, klienti shkruan vetĂ« testet, dhe ne vetĂ«m sigurojmĂ« ekzekutimin e tyre (dhe praninĂ« e raportit pĂ«rkatĂ«s nĂ« merge request).


Si do të duket sekuenca e përgjithshme e veprimeve?

  1. NdĂ«rtimi i aplikacionit — pĂ«rshkrimi i kĂ«tij faze do ta kalojmĂ«.
  2. Deklarimi i aplikacionit në një namespace të veçantë të klasterit Kubernetes dhe ekzekutimi i testimit.
  3. Kërkimi i artefakteve dhe analizimi i raportit JUnit nga GitLab.
  4. Fshirja e namespace të krijuar më parë.

Tani — nĂ« realizim!

Configuration

GitLab CI

Të fillojmë nga fragmente .gitlab-ci.yaml, që përshkruan deklerimin e aplikacionit dhe ekzekutimin e testeve. Listimi doli mjaft voluminoz, ndaj është thelluar ndjeshëm me komente:

variablat:
# shpallim versionin e werf që do të përdorim
  WERF_VERSION: "1.0 beta"

.base_deploy: &base_deploy
  skripti:
# krijojmë namespace në K8s, nëse nuk ekziston
    - kubectl --context="${WERF_KUBE_CONTEXT}" get ns ${CI_ENVIRONMENT_SLUG} || kubectl create ns ${CI_ENVIRONMENT_SLUG}
# dĂ«rgojmĂ« werf dhe deploy — mĂ« shumĂ« rreth kĂ«saj nĂ« dokumentacion
# (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:-''}"
# kalojmë variablën `run_tests`
# do të përdoret në renderimin e Helm-release
      --set "global.run_tests=${RUN_TESTS:-no}"
      --set "global.env=${CI_ENVIRONMENT_SLUG}"
# ndryshojmë timeout-in (ka teste që zgjasin)
# dhe e kalojmë në release
      --set "global.ci_timeout=${CI_TIMEOUT:-900}"
     --timeout ${CI_TIMEOUT:-900}
  vargje:
    - Build

.test-base: &test-base
  zgjeron: .base_deploy
  para_skripti:
# krijojmë një direktor për raportin e ardhshëm, duke u bazuar në $CI_COMMIT_REF_SLUG
    - mkdir /mnt/tests/${CI_COMMIT_REF_SLUG} || true
# njĂ« workaround i domosdoshĂ«m, sepse GitLab dĂ«shiron tĂ« marrĂ« artefaktet nĂ« build-dir’ën e saj
    - mkdir ./tests || true
    - ln -s /mnt/tests/${CI_COMMIT_REF_SLUG} ./tests/${CI_COMMIT_REF_SLUG}
  pas_skripti:
# pas përfundimit të testeve, heqim release-in bashkë me Job-in
# (dhe ndoshta me infrastrukturën e tij)
    - 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
# ne lejojmë dështimet, por ju mund të bëni ndryshe
  lejo_dështimin: true
  variablat:
    RUN_TESTS: 'yes'
# caktojmë kontekstin në werf
# (https://werf.io/how_to/gitlab_ci_cd_integration.html#infrastructure)
    WERF_KUBE_CONTEXT: 'admin@stage-cluster'
  etiketa:
# përdorim runner-in me tag-un `werf-runner`
    - werf-runner
  artefaktet:
# është e nevojshme të krijohet një artefakt që të mund të shihet
# nĂ« pipeline dhe tĂ« shkarkohet — pĂ«r shembull, pĂ«r studim mĂ« tĂ« thellĂ«
    rrugët:
      - ./tests/${CI_COMMIT_REF_SLUG}/*
# artefaktet më të vjetra se një javë do të fshihen
    skadon_në: 7 ditë
# e rëndësishme: këto rreshta janë përgjegjës për analizimin e raportit nga GitLab
    raportet:
      junit: ./tests/${CI_COMMIT_REF_SLUG}/report.xml

# për thjeshtësim këtu janë treguar vetëm dy etapa
# nĂ« realitet do tĂ« keni mĂ« shumĂ« — tĂ« paktĂ«n pĂ«r shkak tĂ« deploy-it
etapat:
  - ndërtimi
  - testet

ndertimi:
  etapa: ndërtimi
  skripti:
# ndĂ«rtimi — sĂ«rish sipas dokumentacionit tĂ« 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
  etiketa:
    - werf-runner
  përjashto:
    - grafiket

kërko teste:
  <<: *test-base
  ambienti:
# "një aftësish" e emërtimit të namespace-it
# (https://docs.gitlab.com/ce/ci/variables/predefined_variables.html)
    emri: teste-${CI_COMMIT_REF_SLUG}
  etapa: testet
  përjashto:
    - grafiket

Kubernetes

Tani nĂ« direktorinĂ« .helm/templates do tĂ« krijojmĂ« njĂ« YAML me njĂ« PunĂ« — tests-job.yaml — pĂ«r tĂ« ekzekutuar testet dhe burimet e nevojshme tĂ« Kubernetes. Shpjegimet shihni pas listĂ«s:

{{- 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\/\n          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\/\n  nodeAffinity:
   required:
     nodeSelectorTerms:
     - matchExpressions:
       - key: kubernetes.io\/hostname
         operator: In
         values:
         - kube-master
  persistentVolumeReclaimPolicy: Delete
  storageClassName: {{ .Chart.Name }}-{{ .Values.global.commit_ref_slug }}
{{- end }}

Cilat janĂ« burimet tĂ« pĂ«rshkruara nĂ« kĂ«tĂ« konfigurim? GjatĂ« implementimit krijojmĂ« njĂ« namespace unik pĂ«r projektin (kjo Ă«shtĂ« e utur edhe nĂ« .gitlab-ci.yaml — tests-${CI_COMMIT_REF_SLUG}) dhe pĂ«r tĂ« dislokojmĂ«:

  1. ConfigMap me skriptin e testit;
  2. Job me përshkrimin e pod-it dhe direktivën e caktuar command, e cila e ekzekuton testet;
  3. PV dhe PVC, që lejojnë ruajtjen e të dhënave të testeve.

Vini re kushtin fillestar me nĂ«se nĂ« fillim tĂ« manifestit — pĂ«rkatĂ«sisht, YAML-fajlet e tjera tĂ« Helm-chart-it me aplikacionin duhet tĂ« mbĂ«shtillen nĂ« strukturĂ«n e kundĂ«rt , nĂ« mĂ«nyrĂ« qĂ« ato tĂ« mos aplikohen gjatĂ« testimit. Pra:

{{- if ne .Values.global.run_tests "yes" }}
---
ka një tjetër yaml
{{- end }}

MegjithatĂ«, nĂ«se testet kĂ«rkojnĂ« njĂ« infrastrukturĂ« tĂ« caktuar (pĂ«r shembull, Redis, RabbitMQ, Mongo, PostgreSQL
) — YAML-tĂ« e tyre mund tĂ« jo opĂ«r dhe ato nĂ« mjedisin e provĂ«s... natyrisht, duke i rregulluar sipas dĂ«shirĂ«s tuaj.

Përfundimi i fundit

Dhe për sa kohë që ndërtimi dhe shpërndarja me anë të werf funksionon ende të në serverin e ndërtimit (me gitlab-runner), dhe podi me testet fillohet në master, do të nevojitet të krijoni një drejtorinë /mnt/tests në master dhe t'ia jepni atë runner-it, për shembull, përmes NFS. Një shembull i zgjeruar me shpjegime mund të gjendet në dokumentacionin K8s.

Rezultati do të jetë:

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

Askush nuk ndalon të krijoni një NFS-share direkt në gitlab-runner, dhe pastaj ta montoni në podë.

Shënim

Mund të pyesni, përse të komplikoni gjithçka duke krijuar një Job nëse mund të thjesht të ekzekutoni skriptin me testet direkt në shell-runner? Përgjigja është mjaft triviale...

Disa teste kërkojnë qasje në infrastrukturën (MongoDB, RabbitMQ, PostgreSQL etj.) për të verifikuar korrektësinë e punës me to. Ne e bëjmë testimin të unifikuar - me këtë qasje, duke përfshirë entitete të tilla bëhet e lehtë. Përveç kësaj, ne marrim nivelin standard një qasje në shpërndarje (edhe pse me përdorimin e NFS, duke montuar katalogë shtesë).

Rezultati

ÇfarĂ« do tĂ« shohim kur aplikojmĂ« konfigurimin e pĂ«rgatitur?

Në kërkesën e bashkimit do të paraqitet një statistikë e përmbledhur për testet që u ekzekutuan në pipeline-n e saj të fundit:

JUnit në GitLab CI me Kubernetes

Për çdo gabim këtu mund të klikoni për të marrë detaje:

JUnit në GitLab CI me Kubernetes

NB: Lexuesi i kujdesshëm do të vërejë se ne po testojmë një aplikacion NodeJS, ndërsa në screenshotet është .NET... Mos u çuditni: thjesht në kuadër të përgatitjes së artikullit nuk pati gabime në testimin e aplikacionit të parë, por u gjetën në një tjetër.

Përfundim

Siç duket, asgjë e komplikuar!

Në parim, nëse ju tashmë keni një ndërtues shell dhe ai funksionon, dhe Kubernetes nuk ju nevojitet - të lidhni testimin me të do të jetë një detyrë edhe më e thjeshtë se sa ajo e përshkruar këtu. Në dokumentacionin GitLab CI do të gjeni shembuj për Ruby, Go, Gradle, Maven e disa të tjerë.

P.S.

Lexoni gjithashtu në blogun tonë:

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster