云原生Devops最佳实践(1):持续集成(CI)+ OpenKruise镜像预热

什么是Devops?

DevOps 就是开发(Development)、测试(QA)、运维(Operations)这三个领域的合并。DevOps是一种思想、一组最佳实践、以及一种文化。 DevOps是CI/CD思想的延伸,CI/CD是DevOps的基础核心,如果没有CI/CD自动化的工具和流程,DevOps是没有意义的。

ci/cd pipeline

持续集成(CI)+ OpenKruise镜像预热

核心概念

  • 持续集成(CI)是一个将集成提前至开发周期的早期阶段的实践方式,让构建、测试和集成代码更经常反复地发生。
  • 镜像预热是OpenKruise提供的在应用部署之前提前将应用镜像拉取到具体的Node节点上面,进而达到镜像预热的目的,能够极大的提升应用的部署效率。

整体架构

ci+image pre-download

使用场景

  • 长期预热公共sidecar镜像、基础镜像,例如:istio envoy、日志采集容器
  • 大规模场景下,提前预热业务app镜像到特定的K8s Node,进而降低部署过程中对镜像仓库的压力,主要针对Deployment、StatefulSet等k8s原生资源
  • OpenKruise CloneSet原地升级时,内置了镜像预热能力,参考CloneSet文档

注意:OpenKruise镜像预热能力只能针对常规的Kubelet节点,而virtual kubelet则不能适用。

Tekton(CI)+ 镜像预热实践

前置条件

  • Kubernetes集群,从v1.0.0(alpha/beta)开始,OpenKruise要求在Kubernetes >= 1.16以上版本的集群中安装和使用。
  • 安装Tekton, 参考官方文档。Tekton是一种适用于创建持续集成和持续部署/交付(CI/CD)系统的谷歌开源的Kubernetes原生框架。
  • Helm安装OpenKruise,需要v0.9.0及更新的版本,参考安装文档

构建-测试-镜像推送

1. Git Repo:本文提供了一个helloworld http服务demo,其中包含Code、Dockerfile以及Unit Test,如下: git helloworld

2. Tekton构建-测试-推送镜像Task,并且需要生成docker registry secret(用于push image),如下:

  1. # docker registry密钥,用于docker push image
  2. apiVersion: v1
  3. data:
  4. .dockerconfigjson: xxxxxx
  5. kind: Secret
  6. metadata:
  7. name: dockersecret
  8. type: kubernetes.io/dockerconfigjson
  9. ---
  10. apiVersion: tekton.dev/v1beta1
  11. kind: Task
  12. metadata:
  13. labels:
  14. app: helloworld
  15. name: helloworld-build-push
  16. spec:
  17. stepTemplate:
  18. workingDir: /workspace
  19. params:
  20. - name: gitrepositoryurl
  21. type: string
  22. - name: branch
  23. type: string
  24. - name: short_sha
  25. type: string
  26. - name: docker_repo
  27. type: string
  28. - name: app_name
  29. type: string
  30. steps:
  31. # git clone
  32. - name: git-clone-and-checkout
  33. image: bitnami/git:latest
  34. command: ["sh", "-ce"]
  35. args:
  36. - >
  37. set -e
  38. echo $(params.gitrepositoryurl)
  39. git clone $(params.gitrepositoryurl) ./ && git checkout $(params.branch)
  40. # unit test
  41. - name: auto-test
  42. image: golang:1.16
  43. command: [ "sh", "-ce" ]
  44. args:
  45. - >
  46. set -e
  47. cp -R /workspace/$(params.app_name) /go/src/ && cd /go/src/$(params.app_name) && pwd;
  48. go test
  49. # docker build & push registry
  50. - name: push-to-registry
  51. image: gcr.io/kaniko-project/executor:latest
  52. args:
  53. - --dockerfile=Dockerfile
  54. - --destination=$(params.docker_repo):$(params.branch)-$(params.short_sha)
  55. - --context=./$(params.app_name)
  56. - --cache=true
  57. - --cache-dir=/cache
  58. - --use-new-run
  59. volumeMounts:
  60. - name: kaniko-secret
  61. mountPath: "/kaniko/.docker"
  62. volumes:
  63. # docker push secret
  64. - name: kaniko-secret
  65. secret:
  66. secretName: dockersecret
  67. items:
  68. - key: .dockerconfigjson
  69. path: config.json

镜像预热

Kruise CloneSet & Advanced StatefulSet原地升级内置镜像预热能力

Note:此种场景不再需要下发ImagePullJob CRD资源

如果你在安装或升级 Kruise 的时候启用了 PreDownloadImageForInPlaceUpdate feature-gate, CloneSet & Advanced StatefulSet 控制器会自动在所有旧版本 pod 所在 node 节点上预热你正在灰度发布的新版本镜像。 这对于应用发布加速很有帮助。

  1. # Firstly add openkruise charts repository if you haven't do this.
  2. $ helm repo add openkruise https://openkruise.github.io/charts/
  3. # [Optional]
  4. $ helm repo update
  5. # Install the latest version.
  6. $ helm install kruise openkruise/kruise --set featureGates="PreDownloadImageForInPlaceUpdate=true"
  7. # Those that have been installed need to be upgraded
  8. $ helm upgrade kruise openkruise/kruise --set featureGates="PreDownloadImageForInPlaceUpdate=true"

默认情况下 CloneSet & Advanced StatefulSet 每个新镜像预热时的并发度都是 1,也就是一个个节点拉镜像。如果需要调整,你可以在 annotation 上设置并发度:

  1. apiVersion: apps.kruise.io/v1alpha1
  2. kind: CloneSet/StatefulSet
  3. metadata:
  4. annotations:
  5. apps.kruise.io/image-predownload-parallelism: "5"

Kubernetes原生Workload,例如:Deployment, StatefulSet, DaemonSet, Job等

1. 配置镜像预热 ImagePullJob CRD资源,并配置到k8s configmap资源中,如下:

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: imagePullJob
  5. data:
  6. imagepulljob.yaml: |
  7. apiVersion: apps.kruise.io/v1alpha1
  8. kind: ImagePullJob
  9. metadata:
  10. name: APP_NAME
  11. spec:
  12. # 需要预热的镜像地址
  13. image: APP_IMAGE
  14. parallelism: 10
  15. # 你可以在 selector 字段中指定节点的 名字列表 或 标签选择器 (只能设置其中一种),如果没有设置 selector 则会选择所有节点做预热。
  16. selector:
  17. names:
  18. - node-1
  19. - node-2
  20. matchLabels:
  21. node-type: xxx
  22. completionPolicy:
  23. type: Always
  24. activeDeadlineSeconds: 1200
  25. ttlSecondsAfterFinished: 300
  26. pullPolicy:
  27. backoffLimit: 3
  28. timeoutSeconds: 300

2. 下发镜像预热 ImagePullJob CRD资源TASK,其中kubeconfig配置存储在secret中,如下:

  1. # kubeconfig配置
  2. apiVersion: v1
  3. data:
  4. kubeconfig: xxxxxx
  5. kind: Secret
  6. metadata:
  7. name: kubeconfig
  8. ---
  9. apiVersion: tekton.dev/v1beta1
  10. kind: Task
  11. metadata:
  12. labels:
  13. app: helloworld
  14. name: helloworld-image-predownload
  15. spec:
  16. params:
  17. - name: branch
  18. type: string
  19. - name: short_sha
  20. type: string
  21. - name: docker_repo
  22. type: string
  23. - name: app_name
  24. type: string
  25. steps:
  26. - name: image-predownload
  27. image: bitnami/kubectl:latest
  28. command: [ "sh", "-ce" ]
  29. args:
  30. - >
  31. set -e
  32. echo "pre download image"
  33. cat /var/crd/imagepulljob.yaml | sed 's#JOB_NAME#$(params.app_name)-$(params.short_sha)#' | sed 's#APP_IMAGE#$(params.docker_repo):$(params.branch)-$(params.short_sha)#' | kubectl apply --kubeconfig=/var/kube/kubeconfig -f -
  34. volumeMounts:
  35. - name: kubeconfig
  36. mountPath: "/var/kube"
  37. - name: imagepulljob
  38. mountPath: "/var/crd"
  39. volumes:
  40. - name: kubeconfig
  41. secret:
  42. secretName: kubeconfig
  43. - name: imagepulljob
  44. configmap:
  45. name: imagepulljob

一键执行Tekton Pipeline

1. 定义tekton pipeline,总共由上面两个task任务组成,并且执行完 构建-测试-推送镜像Task之后,再执行镜像预热Task如下:

  1. apiVersion: tekton.dev/v1beta1
  2. kind: Pipeline
  3. metadata:
  4. name: helloworld-pipeline
  5. spec:
  6. params:
  7. - name: gitrepositoryurl
  8. type: string
  9. - name: branch
  10. type: string
  11. - name: short_sha
  12. type: string
  13. - name: docker_repo
  14. type: string
  15. - name: app_name
  16. type: string
  17. tasks:
  18. - name: helloworld-build-push
  19. taskRef:
  20. name: helloworld-build-push
  21. params:
  22. - name: gitrepositoryurl
  23. value: $(params.gitrepositoryurl)
  24. - name: short_sha
  25. value: $(params.short_sha)
  26. - name: branch
  27. value: $(params.branch)
  28. - name: docker_repo
  29. value: $(params.docker_repo)
  30. - name: app_name
  31. value: $(params.app_name)
  32. - name: helloworld-image-predownload
  33. taskRef:
  34. name: helloworld-image-predownload
  35. params:
  36. - name: short_sha
  37. value: $(params.short_sha)
  38. - name: branch
  39. value: $(params.branch)
  40. - name: docker_repo
  41. value: $(params.docker_repo)
  42. - name: app_name
  43. value: $(params.app_name)
  44. runAfter:
  45. - helloworld-build-push

2. 定义PipelineRun CRD资源,并kubectl apply -f 到k8s集群执行Pipeline,如下:

  1. apiVersion: tekton.dev/v1beta1
  2. kind: PipelineRun
  3. metadata:
  4. name: helloworld-pipeline-run-1
  5. spec:
  6. pipelineRef:
  7. name: helloworld-pipeline
  8. params:
  9. - name: gitrepositoryurl
  10. value: https://github.com/zmberg/samples.git
  11. - name: branch
  12. value: hello_world
  13. - name: short_sha
  14. value: d92ae174b
  15. - name: docker_repo
  16. value: zhaomingshan/kruise
  17. - name: app_name
  18. value: helloworld

3. 可以通过tekton命令行工具tkn查看执行结果,如下:

tekton pipeline

总结

Tekton是google开源的目前社区非常流行的云原生CI Pipeline工具,本篇文章旨在将OpenKruise提供的镜像预热能力与CI Pipeline结合,进而能够极大的提升用户在应用部署阶段的部署效率,并且能够降低在大规模部署下对于镜像仓库的压力。 后面一篇文章将会聚焦在CD Pipeline应用部署阶段,敬请期待。