社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Git

GitLab CI:助力Kubernetes应用完成JUnist测试

Docker • 5 年前 • 570 次点击  

每个人都知道软件测试的重要性和必要性——我相信许多人都坚持这样做。但出乎意料的是,我很难找到一个很好的例子来用GitLab和JUnit配置CI/CD。现在让我来填补这个空白。


背景


首先,让我定义完整的上下文:

  • 由于我们所有的应用程序都在Kubernetes中运行,因此我仅介绍相关架构中的测试

  • 我会使用werf[1]来构建和部署镜像(这意味着Helm也会参与到流水线中)

  • 我不会详细介绍测试本身:在我们的案例中,测试是使用者实施的,我们仅确保测试正常运行(并在合并请求中显示相应的报告)


以下是我们的示例中常见的操作顺序:

  1. 构建应用程序——我们将省略此步骤的描述

  2. 将应用程序部署到Kubernetes集群的独立命名空间并运行测试

  3. 通过GitLab检索artifacts并解析JUnit报告

  4. 删除之前创建的命名空间


让我们开始实践吧!


GitLab CI(Continuous Integration,持续集成)


我们将从 .gitlab-ci.yaml的代码开始,以下部分描述了应用程序的部署和运行测试。代码有点长,我在其中插入了详细的注释:

  1. variables:

  2. # 声明我们将使用的werf的版本

  3. WERF_VERSION: "1.0 beta"

  4. .base_deploy: &base_deploy

  5. script:

  6. # 如果Kubernetes不存在该命名空间则创建命名空间

  7. - kubectl --context="${WERF_KUBE_CONTEXT}" get ns ${CI_ENVIRONMENT_SLUG} || kubectl create ns ${CI_ENVIRONMENT_SLUG}

  8. # 加载werf并部署——详情请查看文档

  9. #(https://werf.io/how_to/gitlab_ci_cd_integration.html#deploy-stage)

  10. - type multiwerf && source multiwerf use ${WERF_VERSION })

  11. - werf version

  12. - type werf && source werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)

  13. - werf deploy --stages-storage :local

  14. --namespace ${CI_ENVIRONMENT_SLUG}

  15. --set "global.commit_ref_slug=${CI_COMMIT_REF_SLUG:-''}"

  16. # 传递变量 `run_tests`

  17. # 这个变量会在渲染Helm版本时使用

  18. --set "global.run_tests=${RUN_TESTS:-no}"

  19. --set "global.env=${CI_ENVIRONMENT_SLUG}"

  20. # 设置超时(某些测试耗时很长)

  21. # 并将其传递给发布

  22. --set "global.ci_timeout=${CI_TIMEOUT:-900}"

  23. --timeout ${CI_TIMEOUT:-900}

  24. dependencies:

  25. - Build

  26. .test-base: & test-base

  27. extends: .base_deploy

  28. before_script:

  29. # 为接下来的报告创建文件夹

  30. # 使用 $CI_COMMIT_REF_SLUG

  31. - mkdir /mnt/tests/${CI_COMMIT_REF_SLUG} || true

  32. # 强制解决方法,因为GitLab需要artifacts在其构建目录中

  33. - mkdir ./tests || true

  34. - ln -s /mnt/tests/${CI_COMMIT_REF_SLUG} ./tests/${CI_COMMIT_REF_SLUG}

  35. after_script:

  36. # 在完成测试后用作业删除发布(以及其基础设施)

  37. - type multiwerf && source multiwerf use ${WERF_VERSION})

  38. - werf version

  39. - type werf && source werf ci-env gitlab --tagging-strategy tag-or-branch -- verbose)

  40. - werf dismiss --namespace ${CI_ENVIRONMENT_SLUG} --with-namespace

  41. # 这里我们定义允许失败发生,但是您也可以自定义

  42. allow_failure: true

  43. variables:

  44. RUN_TESTS: 'yes'

  45. # 设置werf context, 文档请查看以下连接

  46. # (https://werf.io/how_to/gitlab_ci_cd_integration.html#infrastructure)

  47. WERF_KUBE_CONTEXT: 'admin@stage-cluster'

  48. tags:

  49. # 使用带有`werf-runner`标签的runner

  50. - werf-runner

  51. artifacts:

  52. # 首先必须创建一个artifact才能在流水线中查看

  53. # 并下载它(例如,用于进行更深入的研究)

  54. paths:

  55. - ./tests/${CI_COMMIT_REF_SLUG}/*

  56. # 超过一周的artifact将被删除

  57. expire_in: 7 day

  58. # 注意:以下这几行用于GitLab解析报告

  59. reports:

  60. junit: ./tests/${CI_COMMIT_REF_SLUG}/report.xml

  61. # 为了简单起见,这里仅显示两个阶段

  62. # 在现实中,你可能有更多阶段

  63. stages:

  64. - build

  65. - tests

  66. build:

  67. stage: build

  68. script:

  69. # build stage - 请查看werf相关文档:

  70. # (https://werf.io/how_to/gitlab_ci_cd_integration.html#build-stage)

  71. - type multiwerf && source

  72. - werf version

  73. - type werf && source

  74. - werf build-and-publish --stages-storage :local

  75. tags:

  76. - werf-runner

  77. except:

  78. - schedules

  79. run tests:

  80. <<:>

  81. environment:

  82. # 给命名空间命名

  83. # (https://docs.gitlab.com/ce/ci/variables/predefined_variables.html)

  84. name: tests-${CI_COMMIT_REF_SLUG}

  85. stage: tests

  86. except:

  87. - schedules


Kubernetes


现在是时候创建一个YAML文件( tests-job.yaml)了,这个文件用来做两件事:

  1. 描述这个Job了

  2. 描述在 .helm/templates目录中所有必要的Kubernetes资源。


请参阅以下说明:

  1. {{- if eq .Values.global.run_tests "yes" }}

  2. ---

  3. apiVersion: v1

  4. kind: ConfigMap

  5. metadata:

  6. name: tests-script

  7. data:

  8. tests.sh: |

  9. echo "======================"

  10. echo "${APP_NAME} TESTS"

  11. echo "======================"

  12. cd /app

  13. npm run test:ci

  14. cp report.xml /app/test_results/${CI_COMMIT_REF_SLUG}/

  15. echo ""

  16. echo ""

  17. echo ""

  18. chown -R 999:999 / app/test_results/${CI_COMMIT_REF_SLUG}

  19. ---

  20. apiVersion: batch/v1

  21. kind: Job

  22. metadata:

  23. name: {{ .Chart.Name }}-test

  24. annotations:

  25. "helm.sh/hook": post-install,post-upgrade

  26. "helm.sh/hook-weight": "2"

  27. "werf/watch-logs": "true"

  28. spec:

  29. activeDeadlineSeconds: {{ .Values.global.ci_timeout }}

  30. backoffLimit: 1

  31. template:

  32. metadata:

  33. name: {{ .Chart.Name }}-test

  34. spec:

  35. containers:

  36. - name: test

  37. command: ['bash', '-c', '/app/tests.sh']

  38. {{ tuple "application" . | include "werf_container_image" | indent 8 }}

  39. env:

  40. - name: env

  41. value : {{ .Values.global.env }}

  42. - name: CI_COMMIT_REF_SLUG

  43. value: {{ .Values.global.commit_ref_slug }}

  44. - name: APP_NAME

  45. value: {{ .Chart.Name }}

  46. {{ tuple "application" . | include "werf_container_env" | indent 8 }}

  47. volumeMounts:

  48. - mountPath: /app/test_results/

  49. name: data

  50. - mountPath: /app/tests.sh

  51. name: tests-script

  52. subPath: tests.sh

  53. tolerations:

  54. - key : dedicated

  55. operator: Exists

  56. - key: node-role.kubernetes.io/master

  57. operator: Exists

  58. restartPolicy: OnFailure

  59. volumes:

  60. - name: data

  61. persistentVolumeClaim:

  62. claimName: {{ .Chart.Name }}-pvc

  63. - name: tests-script

  64. configMap:

  65. name: tests-script

  66. ---

  67. apiVersion: v1

  68. kind: PersistentVolumeClaim

  69. metadata:

  70. name: {{ .Chart.Name }}-pvc

  71. spec:

  72. accessModes:

  73. - ReadWriteOnce

  74. resources:

  75. requests:

  76. storage: 10Mi

  77. storageClassName: {{ .Chart.Name }}-{{ .Values.global.commit_ref_slug }}

  78. volumeName: {{ .Values.global .commit_ref_slug }}

  79. ---

  80. apiVersion: v1

  81. kind: PersistentVolume

  82. metadata:

  83. name: {{ .Values.global.commit_ref_slug }}

  84. spec:

  85. accessModes:

  86. - ReadWriteOnce

  87. capacity:

  88. storage : 10Mi

  89. local:

  90. path: /mnt/tests/

  91. nodeAffinity:

  92. required:

  93. nodeSelectorTerms:

  94. - matchExpressions:

  95. - key: kubernetes.io/hostname

  96. operator: In

  97. values:

  98. - kube-master

  99. persistentVolumeReclaimPolicy: Delete

  100. storageClassName: {{ .Chart.Name }}-{{ .Values.global.commit_ref_slug }}

  101. {{- end }}


这个配置描述哪些资源呢?我们将会在部署期间为应用程序创建一个唯一的命名空间( .gitlab-ci.yaml文件中定义了命名空间 — tests-${CI_COMMIT_REF_SLUG}),并在其中部署几个组件:

  1. 带有测试脚本的ConfigMap

  2. 带有Pod描述和运行测试的 指令的Job

  3. PV和PVC将存储测试数据


注意清单开头的初始 if语句。为了防止使用应用程序部署Helm图表的其他YAML文件,您必须插入以下相反条件:

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

  2. ---

  3. Hey, I'm another YAML

  4. {{- end }}


但是,如果一些测试需要其他的基础设施 (如Redis,RabbitMQ,Mongo,PostgreSQL等等),那么你可以在相应的YAML文件中启动并部署这些组件到测试环境中。


最后一点


当前,仅通过构建服务器(使用gitlab-runner)支持使用werf进行构建和部署。但是,测试容器在主节点上运行。在这种情况下,你必须在主节点上创建 /mnt/tests文件夹并将其安装到运行器(runner),例如通过NFS。Kubernetes文档中提供了详细示例[2]。

我们将会得到以下结果:

  1. user@kube-master:~$ cat /etc/exports | grep tests

  2. /mnt/tests IP_gitlab-builder/32(rw,nohide,insecure,no_subtree_check,sync,all_squash,anonuid=999,anongid=998)


  3. user@gitlab-runner:~$ cat /etc/fstab | grep tests

  4. IP_kube-master:/mnt/tests /mnt/tests nfs4 _netdev,auto 0 0


另一种可能性是直接在gitlab-runner上创建一个共享的NFS目录,然后将其安装到Pod上。


注释


您可能会问,如果可以轻松地在shell中运行测试脚本,那么创建Job的意义何在?答案很明显:

有些测试需要基础架构(例如MongoDB,RabbitMQ,PostgreSQL等)来检查功能。我的方法是一个统一的解决方案,可以轻松集成其他实例。另外,我们获得了标准的部署方法(即使用NFS和额外的目录)。


结果


如果应用提前准备好的配置会是什么结果?

合并请求中将显示在其先前流水线中执行的测试的摘要:


单击错误获取更多信息:


注意:细心的读者会注意到我们正在测试Node.js应用程序,但是屏幕截图上有一个.NET。不必感到惊讶:虽然我们在原始的应用程序中没有发现任何问题,但在另一个应用程序中暴露出了一些小问题。


结论


如你所见,它是如此简单!

如果你已经有可以使用的Shell脚本并且不需要Kubernetes,则可以通过更简单的方法实现以上例子。请查看GitLab CI 文档[3]。CI文档中提供了Ruby,Go,Gradle,Maven和其他一些产品的示例。

相关链接:

  1. https://github.com/flant/werf

  2. https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistent-volumes

  3. https://docs.gitlab.com/ee/ci/junittestreports.html


原文链接:https://medium.com/flant-com/junit-gitlab-ci-kubernetes-27921ec36823


基于Kubernetes的DevOps实战培训


基于Kubernetes的DevOps实战培训将于2019年12月27日在上海开课,3天时间带你系统掌握Kubernetes,学习效果不好可以继续学习。本次培训包括:容器特性、镜像、网络;Kubernetes架构、核心组件、基本功能;Kubernetes设计理念、架构设计、基本功能、常用对象、设计原则;Kubernetes的数据库、运行时、网络、插件已经落地经验;微服务架构、组件、监控方案等,点击下方图片或者阅读原文链接查看详情。


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/50757
 
570 次点击