JUnit di GitLab CI dengan Kubernetes

Terlepas dari kenyataan bahwa semua orang tahu bahwa penting dan perlu untuk menguji perangkat lunak Anda, dan banyak yang telah melakukannya secara otomatis untuk waktu yang lama, tidak ada resep tunggal di ruang terbuka Habr untuk menyiapkan banyak produk populer di ceruk ini sebagai (favorit kami) GitLab dan JUnit . Isi celah ini!



Pendahuluan


Pertama, saya akan menguraikan konteksnya:

  • Karena semua aplikasi kami berfungsi di Kubernetes, kami akan mempertimbangkan untuk menjalankan tes dalam infrastruktur yang sesuai.
  • Untuk perakitan dan penyebaran kami menggunakan werf (dalam arti komponen infrastruktur, ini juga secara otomatis berarti bahwa Helm terlibat).
  • Saya tidak akan masuk ke detail langsung membuat tes: dalam kasus kami, klien menulis tes sendiri, dan kami hanya memastikan bahwa itu dijalankan (dan laporan terkait tersedia dalam permintaan penggabungan).

Seperti apa urutan tindakan secara keseluruhan?

  1. Perakitan aplikasi - kami akan menghilangkan deskripsi tahap ini.
  2. Menyebarkan aplikasi ke namespace cluster Kubernetes terpisah dan meluncurkan pengujian.
  3. Cari artefak dan parsing laporan JUnit oleh GitLab.
  4. Hapus namespace yang sebelumnya dibuat.

Sekarang - untuk implementasi!

Kustomisasi


Gitlab ci


Mari kita mulai dengan fragmen .gitlab-ci.yaml menjelaskan penyebaran aplikasi dan menjalankan tes. Daftar itu ternyata agak tebal, oleh karena itu dilengkapi dengan komentar:

 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


Sekarang di direktori .helm/templates , buat YAML dengan Pekerjaan - tests-job.yaml - untuk menjalankan tes dan sumber daya Kubernet yang dibutuhkan. Penjelasan lihat setelah daftar:

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

Sumber daya apa yang dijelaskan dalam konfigurasi ini? Saat menggunakan, buat namespace unik untuk proyek (ini juga ditunjukkan dalam .gitlab-ci.yaml - tests-${CI_COMMIT_REF_SLUG} ) dan gulung ke dalamnya:

  1. ConfigMap dengan skrip pengujian;
  2. Pekerjaan dengan deskripsi pod dan arahan command ditentukan, yang hanya menjalankan tes;
  3. PV dan PVC , yang memungkinkan Anda untuk menyimpan data uji.

Perhatikan kondisi pengantar dengan if pada awal manifes - oleh karena itu, file YAML lain dari bagan Helm dengan aplikasi harus dibungkus dengan konstruksi terbalik sehingga tidak digunakan selama pengujian. Itu adalah:

 {{- if ne .Values.global.run_tests "yes" }} ---    {{- end }} 

Namun, jika tes memerlukan beberapa infrastruktur (misalnya, Redis, RabbitMQ, Mongo, PostgreSQL ...) - YAML mereka dapat dimatikan. Sebarkan di lingkungan uji ... tentu saja, sesuaikan yang Anda suka.

Sentuhan terakhir


Karena perakitan dan penyebaran menggunakan werf sejauh ini hanya berfungsi pada server build (dengan gitlab-runner), dan pod dengan tes dijalankan pada wizard, Anda harus membuat /mnt/tests pada wizard dan memberikannya kepada pelari, misalnya, melalui NFS . Contoh terperinci dengan penjelasan dapat ditemukan dalam dokumentasi K8s .

Hasilnya adalah:

 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 

Tidak ada yang melarang membuat NFS-bola langsung di gitlab-runner, dan kemudian memasangnya di pod.

Catatan


Anda mungkin bertanya, mengapa mempersulit segalanya dengan penciptaan Ayub, jika Anda bisa menjalankan skrip uji langsung pada shell runner? Jawabannya cukup sepele ...

Beberapa tes memerlukan akses ke infrastruktur (MongoDB, RabbitMQ, PostgreSQL, dll.) Untuk memeriksa kebenaran bekerja dengan mereka. Kami membuat pengujian disatukan - dengan pendekatan ini, menjadi mudah untuk memasukkan entitas tambahan tersebut. Selain itu, kami mendapatkan pendekatan standar dalam penyebaran (bahkan jika menggunakan NFS, pemasangan direktori tambahan).

Hasil


Apa yang akan kita lihat ketika kita menerapkan konfigurasi yang sudah disiapkan?

Permintaan penggabungan akan menampilkan statistik ringkasan tentang tes yang diluncurkan di saluran terakhir:



Anda dapat mengklik setiap kesalahan di sini untuk mendapatkan detail:



NB : Pembaca yang penuh perhatian akan melihat bahwa kami sedang menguji aplikasi NodeJS, dan dalam tangkapan layar - .NET ... Jangan kaget: hanya sebagai bagian dari persiapan artikel, tidak ada kesalahan dalam menguji aplikasi pertama, tetapi mereka ditemukan di yang lain.

Kesimpulan


Ternyata, tidak ada yang rumit!

Pada prinsipnya, jika Anda sudah memiliki shell-builder dan itu bekerja, dan Anda tidak memerlukan Kubernetes, mengacaukan pengujian untuk itu akan menjadi tugas yang lebih sederhana daripada yang dijelaskan di sini. Dan dalam dokumentasi GitLab CI Anda akan menemukan contoh untuk Ruby, Go, Gradle, Maven dan beberapa lainnya.

PS


Baca juga di blog kami:

Source: https://habr.com/ru/post/id460897/


All Articles