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

本周四晚上8:30,第二期k3s在线培训如约开播!本期课程将介绍k3s的核心架构,如高可用架构以及containerd。一起来进阶探索k3s吧!

报名及观看链接:http://z-mz.cn/PmwZ

本文来自Rancher Labs

在云原生领域中,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处理这些工作负载的效率。

image

适用于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 tiller
serviceaccount/tiller created

~/.kube
$ kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller
clusterrolebinding.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
  1. 在相同的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:8080
kubectl --namespace default port-forward $POD_NAME 8080:8080

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

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

image

不过,请记住,还有许多配置选项尚未修改,你可以访问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-plugin
xcode-plugin
rich-text-publisher-plugin
jacoco
scoverage
dependency-check-jenkins-plugin
greenballs
shiningpanda
pyenv-pipeline
s3
pipeline-aws
appcenter
multiple-scms
Testng-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 + "-" + tag
println("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.0
ARG CORDOVA_VERSION=8.0.0

# SDK version and build-tools version should be different
ENV SDK_VERSION 25.2.3
ENV BUILD_TOOLS_VERSION 26.0.2
ENV SDK_CHECKSUM 1b35bcb94e9a686dff6460c8bca903aa0281c6696001067f34ec00093145b560
ENV ANDROID_HOME /opt/android-sdk
ENV 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-23
ENV 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内部运作方式的好方法,关键是要留出机动空间,方便将来容易更改。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,348评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,122评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,936评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,427评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,467评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,785评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,931评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,696评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,141评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,483评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,625评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,291评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,892评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,492评论 2 348

推荐阅读更多精彩内容

  • 一、背景 在近几年,Kubernetes迅速成为了容器编排的事实上的开源标准。与虚拟机不同,Kubernetes在...
    JFrog阅读 923评论 0 9
  • 一叶浮沉风雨浅,三亭温酒候秋凉。 月光相对孤芳伴,醉落诗心断寸肠 2019...
    诗人萧入铭阅读 203评论 1 3
  • 看过世界最美的风景 爱过不喜欢自己的人 明白后 丢了灵魂 往前的虚掩 都是 为了往后的不尴尬 原谅这灵魂 原谅这恩怨
    杏林笔客阅读 256评论 0 4
  • iOS中的事件 响应者对象(UIResponder) UIResponder内部提供了以下方法来处理事件触摸事件 ...
    Levi段玉磊阅读 785评论 0 0
  • 今日反思: 昨天吴军老师在直播里说哈佛的学生可以自由的选择各种学科和不选择任何学科,但是有一个功课不能落下:写作。...
    A00小浅阅读 144评论 0 1