写点什么

实例演示:如何在 Kubernetes 上大规模运行 CI/CD

2020 年 5 月 25 日

实例演示:如何在Kubernetes上大规模运行CI/CD

在云原生领域中,Kubernetes 累积了大量用例。它能够在云中部署应用容器、安排批处理 job、处理工作负载以及执行逐步升级。Kubernetes 使用高效的编排算法来处理这些操作,即便是大规模集群这些算法依旧表现良好。


此外,Kubernetes 主要用例之一是运行持续集成或持续交付(CI/CD)流水线。也就是说,我们部署一个 CI/CD 容器的唯一实例,该实例将监控代码版本控制系统。所以,每当我们推送到该仓库时,该容器都会运行流水线步骤。其最终目标是达到一个“true or false”的状态。True 即在集成阶段 commit 通过了各种测试,False 即未通过测试。


除了以上描述的 CI 流水线之外,在 CI 测试通过之后,另一个流水线可以接管余下的过程,以处理发布过程的 CD 部分。在这一阶段,流水线将尝试将应用程序容器交付到生产中。


需要明白的是,这些操作是按需运行或者是由各种行为(如代码 check-in、测试触发器、流程中上一步的结果等)自动触发的。因此我们需要一种机制来增加单个节点以运行那些流水线的步骤,并在不需要它们时将其淘汰。这种管理不可变基础架构的方法有助于我们节省资源并降低成本。


当然,最关键的机制就是 Kubernetes,它具有声明式的结构和可定制性,因此可以让你在任何场景下高效地调度 job、节点以及 pod。


本文包括 3 个部分:第一部分我们将探讨目前在 Kubernetes 上运行最受欢迎的 CI/CD 平台。


接着我们将会看两个用例:第一个例子中,我们将简单地在 Kubernetes 上安装 Jenkins 以及对其进行配置以让我们可以在 Kubernetes 上使用这个流行的开源工具来运行我们的 CI 流水线;第二个例子中,我们将把这个 Jenkins 部署提高到一个新的水平。我们将会提供一些在 Kubernetes 中扩展 CI/CD 流水线的 tips 和建议。


最后,我们将会讨论在 Kubernetes 上大规模运行 CI/CD 的最合理的方法和实践。


本文的目标是让你彻底了解 Kubernetes 处理这些工作负载的效率。


适用于 Kubernetes 的 CI/CD 平台

Kubernetes 是一个运行 CI/CD 的理想平台,因为它拥有许多特性使得在上面运行 CI/CD 更为简单。那么,到底有多少 CI/CD 的平台可以在 Kubernetes 上运行呢?可以这么说,只要它们能够被打包为一个容器,Kubernetes 都能够运行它们。以下是几个最为流行的 CI/CD 平台:


  • Jenkins :Jenkins 是最为流行也最为稳定的 CI/CD 平台。在世界范围内有数以千计的企业都在使用它,因为它拥有强大的生态和可扩展性。如果你打算要在 Kubernetes 上使用它,非常建议你安装它的官方插件。JenkinsX 是专门为云原生领域设计的 Jenkins 版本。它与 Kubernetes 更加兼容,并且提供了更好的集成功能,如 GitOps、自动 CI/CD 和预览环境。

  • Spinnaker :Spinnaker 是一个可扩展的多云部署的 CD 平台,得到了 Netflix 的支持。使用相关的 Helm Chart 即可安装它。

  • https://github.com/helm/charts/tree/master/stable/spinnaker

  • Drone :这是有多种功能的通用云原生 CD 平台。可以使用关联的 Runner 在 Kubernetes 中运行它。

  • GoCD :Thoughtworks 的另一个 CI/CD 平台,提供了适用于云原生部署的各种工作流程和功能。它可以在 Kubernetes 中作为 Helm Chart 运行。


此外,还有一些与 Kubernetes 紧密合作的云服务,并提供诸如 CircleCI 和 Travis 的 CI/CD 流水线。如果你不打算托管 CI/CD 平台,那么这些也十分有用。


现在,我们来看看如何在 Kubernetes 集群上安装 Jenkins。


如何在 Kubernetes 上安装 Jenkins

首先,我们需要安装 Helm,它是 Kubernetes 的软件包管理器:


$ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 > get_helm.sh$ chmod 700 get_helm.sh$ ./get_helm.sh -v v2.15.0
复制代码


同样,我们还需要安装 Tiller,以让 Helm 正常运行:


$ kubectl -n kube-system create serviceaccount tillerserviceaccount/tiller created
~/.kube$ kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tillerclusterrolebinding.rbac.authorization.k8s.io/tiller created
~/.kube$ helm init --service-account tiller$HELM_HOME has been configured at /Users/itspare/.helm.
复制代码


完成这些步骤之后,我们需要运行检查命令,以查看 deployment 的配置值:


$ helm inspect values stable/jenkins > values.yml
复制代码


仔细检查配置值并在需要的时候进行更改。然后安装 Chart:


$ helm install stable/jenkins --tls \--name jenkins \--namespace jenkins
复制代码


安装过程中会有一些关于下一步操作的说明:


注意:


  1. 行以下命令获取”admin“用户的密码:


printf $(kubectl get secret --namespace default my-jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
复制代码


在相同的 shell 中获取 Jenkins URL 以访问这些命令:


export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/component=jenkins-master" -l "app.kubernetes.io/instance=my-jenkins" -o jsonpath="{.items[0].metadata.name}")echo http://127.0.0.1:8080kubectl --namespace default port-forward $POD_NAME 8080:8080
复制代码


遵循这些步骤,它们将在http://127.0.0.1:8080启动代理服务器。


到那里输入你的用户名和密码。你将会拥有自己的 Jenkins 服务器:



不过,请记住,还有许多配置选项尚未修改,你可以访问 chart 文档以了解更多信息:


https://github.com/helm/charts/tree/master/stable/jenkins


在默认情况下,服务器会安装好最基本的插件,如 Git 和 Kubernetes-Jenkins,我们可以根据自己的需要安装其他插件。


总而言之,使用 Helm 安装 Jenkins 十分轻松。


使用 K8S 扩展 CI/CD Jenkins 流水线

既然我们已经大致了解 CI/CD 如何在 Kubernetes 上运行的,那么我们来看一个在 Kubernetes 中部署高度可扩展的 Jenkins 部署的示例用例。人们通常用它(进行了少量修改)来处理基础结构的 CI/CD,开始吧!


使用 Jenkins 固定发行版

虽然官方 Jenkins 镜像很适合入门,但它需要的配置超出了我们的期望。许多用户会选择一个固定的发行版,如 my-bloody-jenkins(https://github.com/odavid/my-bloody-jenkins),它提供了一个较为完整的预安装插件以及配置选项。在可用的插件中,我们使用 saml 插件、SonarQubeRunner、Maven 和 Gradle。


它能够使用以下命令通过 Helm Chart 安装:


$ helm repo add odavid https://odavid.github.io/k8s-helm-charts$ helm install odavid/my-bloody-jenkins
复制代码


我们选择使用以下 Dockerfile 部署自定义镜像:


FROM odavid/my-bloody-jenkins:2.190.2-161
USER jenkins
COPY plugins.txt /usr/share/jenkins/ref/RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt
USER root
复制代码


其中 plugins.txt 文件是我们要预安装到镜像中的其他插件列表:


build-monitor-pluginxcode-pluginrich-text-publisher-pluginjacocoscoveragedependency-check-jenkins-plugingreenballsshiningpandapyenv-pipelines3pipeline-awsappcentermultiple-scmsTestng-plugin
复制代码


然后,只要 dockerfile 发生更改,我们就使用此通用 Jenkinsfile 来构建 master:


#!/usr/bin/env groovy
node('generic') {try {
def dockerTag, jenkins_master
stage('Checkout') {checkout([$class: 'GitSCM',branches: scm.branches,doGenerateSubmoduleConfigurations: scm.doGenerateSubmoduleConfigurations,extensions: [[$class: 'CloneOption', noTags: false, shallow: false, depth: 0, reference: '']],userRemoteConfigs: scm.userRemoteConfigs,])
def version = sh(returnStdout: true, script: "git describe --tags `git rev-list --tags --max-count=1`").trim()def tag = sh(returnStdout: true, script: "git rev-parse --short HEAD").trim()dockerTag = version + "-" + tagprintln("Tag: " + tag + " Version: " + version)}
stage('Build Master') {jenkins_master = docker.build("jenkins-master", "--network=host .")}
stage('Push images') {docker.withRegistry("https://$env.DOCKER_REGISTRY", 'ecr:eu-west-2:jenkins-aws-credentials') {jenkins_master.push("${dockerTag}")}}
if(env.BRANCH_NAME == 'master') {
stage('Push Latest images') {docker.withRegistry("https://$env.DOCKER_REGISTRY", 'ecr:eu-west-2:jenkins-aws-credentials') {jenkins_master.push("latest")}}
stage('Deploy to K8s cluster') {withKubeConfig([credentialsId: 'dev-tools-eks-jenkins-secret',serverUrl: env.TOOLS_EKS_URL]) {sh "kubectl set image statefulset jenkins jenkins=$env.DOCKER_REGISTRY/jenkins-master:${dockerTag}"}}}currentBuild.result = 'SUCCESS'} catch(e) {currentBuild.result = 'FAILURE'throw e}}
复制代码


我们所使用的专用集群由 AWS 中的一些大中型实例组成,用于 Jenkins jobs。接下来,我们进入下一个部分。


使用专用的 Jenkins Slaves 和标签(label)

为了扩展我们的一些 Jenkins slaves,我们使用 Pod 模板并将标签分配给特定的 agent。因此在我们的 Jenkinsfiles 中,我们可以为 jobs 引用它们。例如,我们有一些需要构建安卓应用程序的 agent。因此,我们引用以下标签:


pipeline {agent { label "android" }
复制代码


并且将使用特定于安卓的 pod 模板。我们使用这一 Dockerfile,例如:


FROM dkr.ecr.eu-west-2.amazonaws.com/jenkins-jnlp-slave:latest
RUN apt-get update && apt-get install -y -f --no-install-recommends xmlstarlet
ARG GULP_VERSION=4.0.0ARG CORDOVA_VERSION=8.0.0
# SDK version and build-tools version should be differentENV SDK_VERSION 25.2.3ENV BUILD_TOOLS_VERSION 26.0.2ENV SDK_CHECKSUM 1b35bcb94e9a686dff6460c8bca903aa0281c6696001067f34ec00093145b560ENV ANDROID_HOME /opt/android-sdkENV SDK_UPDATE tools,platform-tools,build-tools-25.0.2,android-25,android-24,android-23,android-22,android-21,sys-img-armeabi-v7a-android-26,sys-img-x86-android-23ENV LD_LIBRARY_PATH ${ANDROID_HOME}/tools/lib64/qt:${ANDROID_HOME}/tools/lib/libQt5:$LD_LIBRARY_PATH/ENV PATH ${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
RUN curl -SLO "https://dl.google.com/android/repository/tools_r${SDK_VERSION}-linux.zip" \&& echo "${SDK_CHECKSUM} tools_r${SDK_VERSION}-linux.zip" | sha256sum -c - \&& mkdir -p "${ANDROID_HOME}" \&& unzip -qq "tools_r${SDK_VERSION}-linux.zip" -d "${ANDROID_HOME}" \&& rm -Rf "tools_r${SDK_VERSION}-linux.zip" \&& echo y | ${ANDROID_HOME}/tools/android update sdk --filter ${SDK_UPDATE} --all --no-ui --force \&& mkdir -p ${ANDROID_HOME}/tools/keymaps \&& touch ${ANDROID_HOME}/tools/keymaps/en-us \&& yes | ${ANDROID_HOME}/tools/bin/sdkmanager --update
RUN chmod -R 777 ${ANDROID_HOME} && chown -R jenkins:jenkins ${ANDROID_HOME}
复制代码


我们还使用了 Jenkinsfile,该文件与上一个文件类似,用于构建 master。每当我们对 Dockerfile 进行更改时,agent 都会重建镜像。这为我们的 CI/CD 基础架构提供了极大的灵活性。


使用自动伸缩

尽管我们为 deployment 分配了特定数量的节点,但我们还可以通过启用 cluster autoscaling,来完成更多的事情。这意味着在工作负载增加和峰值的情况下,我们可以增加额外的节点来处理 job。目前,如果我们有固定数量的节点,那么我们只能处理固定数量的 job。基于以下事实,我们可以进行粗略地估计:每个 slave 通常分配 500ms CPU 和 256MB 内存,并且设置一个很高的并发。这根本不现实。


举个例子,当你的版本被大幅削减并且需要部署大量微服务时,可能会发生上述情况。然后,大量的 job 堆积在流水线,造成严重的延误。


在这种情况下,我们可以增加该阶段的节点数。例如,我们可以添加额外的 VM 实例,然后在过程结束时将其删除。


我们可以在命令行中使用自动伸缩选项来配置“Vertical”或“集群”自动伸缩选项。但是,此方法需要仔细计划和配置,因为有时会发生以下情况:


  1. 越来越多的 job 达到平稳阶段

  2. Autoscaler 增加新的节点,但是需要 10 分钟来进行部署和分配

  3. 旧的 job 已经完成任务,新的 job 将填补空白,进而减少了对新节点的需求

  4. 新节点可用,但需要 X 分钟保持稳定且未利用,X 由–scale-down-unneeded-time 标志定义

  5. 同样的事情每天发生很多次


在这种情况下,最好是根据我们的特定需求进行配置,或者只是增加当天的节点数,并在流程结束后将其还原。所有这些都与寻找最佳方法来利用所有资源并使成本最小化有关。


在任何情况下,我们都应该有一个可伸缩且易于使用的 Jenkins 集群。对于每个 job,都会创建一个 pod 来运行特定的流水线,并在完成后将其销毁。


大规模使用 K8s 进行 CI / CD 的最佳实践

现在我们已经了解了 Kubernetes 有哪些 CI/CD 平台以及如何在你的集群上安装一个平台。接下来,我们将讨论一些大规模运行它们的方法。


首先,选择 Kubernetes 发行版是我们需要考虑的最关键因素之一。找到最合适的解决方案才能够进行下一步。


其次,选择合适的 Docker 镜像仓库和应用程序包管理器同样重要。我们需要寻找可以按需快速检索的安全可靠的镜像管理。至于软件包管理器,Helm 是一个不错的选择,因为它可以发现、共享和使用为 Kubernetes 构建的软件。


第三,使用现代集成流程,如 GitOps 和 ChatOps,在易用性和可预测性方面提供了显著优势。将 Git 用作单一数据源,使我们可以运行“通过拉取请求进行操作”,从而简化了对基础架构和应用程序的部署控制。使用诸如企业微信或钉钉之类的团队协作工作来触发 CI/CD 流水线的自动化任务,有助于我们消除重复劳动并简化集成。


总体而言,如果我们想更深入地了解,你可以自定义或开发自己的 K8S Operator,与 K8S API 配合更紧密。使用自定义 operator 的好处很多,因为它们可以建立更好的自动化体验。


最后,我们可以说 Kubernetes 和 CI/CD 平台是天合之作。如果你刚刚入门 Kubernetes 生态系统,那么你可以尝试集成一个 CI/CD 流水线。这是了解 Kubernetes 内部运作方式的好方法,关键是要留出机动空间,方便将来容易更改。


2020 年 5 月 25 日 16:40187

评论

发布
暂无评论
发现更多内容

面试加分项!Android项目开发如何设计整体架构?学习路线+知识点梳理

欢喜学安卓

android 程序员 面试 移动开发

第二章作业

白知之明

全网首发!普通程序员快速成为架构师的不传之秘,阿里年薪20W——200W的学习计划路线终开源

程序员小毕

Java 人工智能 大数据 架构 分布式

【百度官方技术分享】百度智能小程序框架性能优化实践

百度Geek说

百度智能小程序 小程序云开发 百度 前端开发 技术宅

玩遍博客网站,我整理了全套的建站技术栈

老魚

程序员 博客 网站搭建 建站

Mybatis系列全解(五):全网最全!详解Mybatis的Mapper映射文件

潘潘和他的朋友们

Java 后端 mybatis 后端开发 mybatis源码

深入了解HashMap!

xcbeyond

Java hashmap HashMap底层原理 28天写作

【并发编程的艺术】Java内存模型的顺序一致性

程序员架构进阶

架构 JMM Java内存模型 28天写作

《程序员修炼之道》- 务实的哲学(2)

石云升

读书笔记 28天写作 程序员修炼之道 程序员的务实 软件的熵

面试学习!我们究竟还要学习哪些Android知识?讲的明明白白!

欢喜学安卓

android 程序员 面试 移动开发

产品训练营-第二周-作业

邹小胖

产品经理训练营

第四次工业革命与龙的故事

脑极体

维基百科技术架构演进分析

andy

追根溯源的产品思维

产品经理训练营

第二次作业提交

涅米丶

产品经理的大局观——

小匚

产品经理 产品经理训练营

阿里开源Redis“神级”手册我粉了!理论源码实战起飞(2021最新)

程序员小毕

Java redis 源码 架构 面试

作业:挑一个你喜欢的产品平台,列出产品的利益相关方。

嫉妒的耗子

阿里发布2021年Redis“神级”手册:基础+原理+应用+集群+拓展+源码,六管齐下

Java架构追梦

Java redis 阿里巴巴 源码 架构

「Android渲染」图像是怎样显示到屏幕上的?

李小四

Android渲染 AndroidUI RenderingPipeline

拆解 抽奖助手 的利益相关者

小匚

产品经理 产品经理训练营 无码科技

Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解

潘潘和他的朋友们

Java 后端 mybatis 后端开发 mybatis源码

抽奖小助手——利益相关者

墨狂之逸才

产品经理训练营第二周作业

产品经理训练营

70 张图带你彻底掌握红黑树

云流

Java 数据结构 红黑树

Java9模块化指南

程序员小毕

Java 编程 程序员 面试 开发

产品经理课程-第二周

novaln🍉

如何做好一场技术分享,100%纯干货输出

架构精进之路

技术分享 七日更 28天写作

阿里开始“拆”中台?!中台建设何去何从?

博文视点Broadview

Springboot使用jasypt需要注意的一个小地方

Sky彬

springboot jasypt

【网络篇】- https

双木之林

实例演示:如何在Kubernetes上大规模运行CI/CD-InfoQ