k8s spec_k8s解决了什么问题

(5) 2024-06-07 18:23

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说
k8s spec_k8s解决了什么问题,希望能够帮助你!!!。

前一篇我们介绍了Spinnaker是如何对接和管理实例云的,本篇我们将介绍Spinnaker如何对接k8s

Spinnaker在对接实例云时已经为我们做过很好的铺垫,让我们接触到Immutable的发布方式,并体验到随之而来的好处。K8s将Immutable发挥到极致,将版本的创建与销毁由分钟级提高到秒级。

 

Spinnaker管理K8S的前世今生

Spinnaker曾经按照管理实例的方式推出过一版对接k8s的管理机制,我们这里叫他V1,设计思路与上一篇中的pipeline一模一样,也是拆分为bake、deploy、scaledown、destroy等。

后来Spinnaker推出了V2版,也就是现在主推的版本,基于manifest的管理思路,以声明式的设计彻底颠覆了V1版的思想,使pipeline变得简单高效、便于管理。

以下所有内容都是基于V2版本展开介绍。

 

Spinnaker对接K8S的条件

必要条件:config+kubelet

Spinnaker管理云平台肯定需要配置认证信息来得到云平台的授权,K8S也不例外,对于K8S的授权配置只需要在.kube目录下存放容器云的config认证文件既可,跟aws有点类似,aws的认证是将key和secret存放在.aws目录下。

Spinnaker操作实例云平台是通过SDK,代码中直接调用云平台的各个接口;而Spinnkaer操作K8S是通过kubelet,代码转化成本地的kubelet命令的方式来实现的。所以一定要在spinnaker部署的机器上预先安装好kubelet。

推荐条件:交付仓库(Artifact)

Spinnaker支持S3、Http、Github、Gitlab等各种交付仓库,在对接K8S时交付仓库主要用来存放manifest文件的。虽然Spinnaker的Pipeline中可以直接读写manifest,如下图:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第1张

但是不适用于代码配置分离或者企业具有CMDB的场景,我们只让spinnaker运行manifest,而自身并不维护manifest时,将manifest的配置和管理交给Artifact,将采用这种方式:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第2张

Spinnaker如何干预K8S

了解K8S的同学都知道,manifest是声明式的,不管现状是怎样的,manifest文件中代表的是最终的目的和结果。对于manifest我计划下个月写一篇博文详细解读下,这里先看一个简单的例子:

apiVersion: apps/v1 kind: Deployment metadata: annotations: artifact.spinnaker.io/location: devops artifact.spinnaker.io/name: ci-gateway artifact.spinnaker.io/type: kubernetes/deployment moniker.spinnaker.io/application: ci moniker.spinnaker.io/cluster: ci-gateway strategy.spinnaker.io/max-version-history: '1' strategy.spinnaker.io/use-source-capacity: 'true' labels: app.kubernetes.io/managed-by: spinnaker app.kubernetes.io/name: ci-gateway name: ci-gateway namespace: devops spec: replicas: 1 selector: matchLabels: k8s-app: ci-gateway strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: annotations: artifact.spinnaker.io/location: devops artifact.spinnaker.io/name: ci-gateway artifact.spinnaker.io/type: kubernetes/deployment moniker.spinnaker.io/application: ci moniker.spinnaker.io/cluster: ci-gateway labels: app.kubernetes.io/managed-by: spinnaker app.kubernetes.io/name: ci-gateway configMap.version: '${ parameters.config_version}' k8s-app: ci-gateway task: monitoring spec: containers: - args: - '--spring.config.location=application.yml' image: 'hub.imgo.tv/spinnaker/gateway:${ parameters.image_version}' imagePullPolicy: IfNotPresent livenessProbe: httpGet: path: / port: 9000 scheme: HTTP initialDelaySeconds: 30 timeoutSeconds: 5 name: ci-gateway volumeMounts: - mountPath: /usr/lib/application.yml name: app-conf subPath: application.yml - mountPath: /usr/lib/iplist.txt name: app-conf subPath: iplist.txt volumes: - configMap: items: - key: application.yml path: application.yml - key: iplist.txt path: iplist.txt name: 'ci-gateway-configmap-${ parameters.config_version}' name: app-conf 

这是一个deployment的manifest,采用滚筒发布,其中${}内是spinnaker的pipeline中定义的一些动态变量。

那么问题来了,Spinnaker最高理想是全自动,pipeline在初次配置后不需要再维护了,假如我生产某个replicaSet配有自动容缩的能力,一旦需要更换镜像岂不是被manifest强行恢复到pipeline中预设的副本数了么?Spinnaker的设计者早就想到了这个问题,解决思路是通过annotation。

strategy.spinnaker.io/use-source-capacity这个annotation就是为了专门解决上面的问题而设计的,默认为false,如果配置为true代表着直接使用replicaSet现有的容量,忽视manifest中配置的副本数!

类似的annotation还有很多,这一个个annotation解决了对接中的很多痛点。

moniker.spinnaker.io/application:资源属于哪个app

moniker.spinnaker.io/cluster:资源属于哪个cluster

strategy.spinnaker.io/max-version-history:replicaSet保留几个历史版本

strategy.spinnaker.io/recreate:deployment的时候是否每次都要重建pod

我猜想它的原理有点像Java开发Spring的AOP,有一个切面就是专门处理各种annotation的,有些annotation是前置加强,有些是后续加强,有些是循环加强。像strategy.spinnaker.io/use-source-capacity这个annotation应该就是前置加强了,spinnaker看到manifest中有这个annotation,先去k8s中获取到真实的容量,然后覆盖掉manifest中的副本数,才有了我们现在看到的这种“反声明式”的神奇效果。

 

Spinnaker管理K8S初级pipeline

这里所谓初级,就是代码和配置在一起的独立镜像,spinnaker只负责deployment就好,不需要关心镜像内容。Pipeline如下

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第3张

图中有2个service,test_service为测试环境提供服务,product_service为生产环境提供服务。

图中有4条pipeline,前两条为测试pipeline,第三条为灰度发布pipeline,第四条为生产发布pipeline。以下是详细介绍:

第一条:测试Pipeline

manifest挂在外存储中,spinnaker自身不关心资源文件,当外存储manifest发生变化时通过webhook自动触发测试环境更新操作。

Start:传入包含manifest文件的git配置,由git的webhook来自动触发

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第4张

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第5张

Deployment:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第6张

第二条:测试Pipeline(推荐)

manifest在spinnaker中维护,外系统传入镜像tag触发测试环境镜像更新操作。

Start:传入镜像的tag,可以由webhook来自动触发,也可以被Jenkins任务触发

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第7张

Deployment:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第8张

第三条:灰度Pipeline

触发时传入镜像tag,新建副本数1的一个replicaSet挂载到生产service,观察结束后销毁灰度set,根据灰度观察结果选择是否触发生产Pipeline

Destroy环节:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第9张

根据灰度deployment时设置的标签,当灰度结束后可以删除这些pod

触发生产的Pipeline:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第10张

当灰度发布结果为yes时,触发下一级pipeline,并将镜像tag参数透传下去。

第四条:生产Pipeline

触发时传入镜像tag,按照生产现有副本数创建replicaSet,采用RollingUpdate的方式更新pod

这一步就很简单了,注意两点。

1 Deployment采用RollingUpdate的发布方式

2 strategy.spinnaker.io/use-source-capacity: 'true'这个annotation保证生产pod的容量。

 

Spinnaker对接K8S高阶pipeline

像我们这种有CMDB的企业,配置和代码都是分离的,研发只负责开发代码,敏感信息和软件配置信息是由业务运维来维护的。这种场景就需要用到K8S的configMap和Secret,Pipeline的设计和产品发布系统就需要重新设计,如下:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第11张

设计上的改变:

1 Harbor中镜像tag要显示的区分是内测版、公测版、发布版本

2 configMap和secret需要区分测试环境还是生产环境,其中configMap需要有版本的概念,secret无需版本的概念

其中ConfigMap和Secret的manifest直接通过http方式向cmdb获取,spinnaker本身来维护deployment的manifest。

Start:传入镜像tag和configMap版本动态参数,同时向cmdb去索取configMap和secret的manifest。可以由jenkins触发、harbor触发、webhook触发。

获取configMap:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第12张

获取secret:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第13张

动态参数:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第14张

Deploy ConfigMap:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第15张

Deploy Secret:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第16张

Deploy Pod:

k8s spec_k8s解决了什么问题_https://bianchenghao6.com/blog__第17张

配置详情:

secret的manifest:

apiVersion: v1 data: youdu_appId: my_youdu_app_id youdu_buin: my_youdu_buin youdu_encodingaesKey: my_youdu_encodingaes_key kind: Secret metadata: name: ci-youdu-secret namespace: devops type: Opaque

configMap的manifest:

apiVersion: v1 kind: ConfigMap metadata: name: ci-youdu-configmap-v1 namespace: devops data: application.yml: | server: port: 8098 spring: application: name: ci-youdu eureka: instance: preferIpAddress: true instanceId: ${spring.cloud.client.ipAddress}:${server.port} hostname: ci-register client: serviceUrl: defaultZone: http://ci-register:8111/eureka/ youdu: address: im.imgo.tv:7080 swagger: enabled: true

deployment的manifest:

apiVersion: apps/v1beta1 kind: Deployment metadata: annotations: artifact.spinnaker.io/location: devops artifact.spinnaker.io/name: ci-youdu artifact.spinnaker.io/type: kubernetes/deployment moniker.spinnaker.io/application: ci moniker.spinnaker.io/cluster: ci-youdu strategy.spinnaker.io/max-version-history: '1' strategy.spinnaker.io/use-source-capacity: 'true' labels: app.kubernetes.io/managed-by: spinnaker app.kubernetes.io/name: ci-youdu name: ci-youdu namespace: devops spec: replicas: 1 selector: matchLabels: k8s-app: ci-youdu strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: annotations: artifact.spinnaker.io/location: devops artifact.spinnaker.io/name: ci-youdu artifact.spinnaker.io/type: kubernetes/deployment moniker.spinnaker.io/application: ci moniker.spinnaker.io/cluster: ci-youdu labels: app.kubernetes.io/managed-by: spinnaker app.kubernetes.io/name: ci-youdu configMap.version: '${ parameters.config_version}' k8s-app: ci-youdu task: monitoring spec: containers: - args: - '--spring.config.location=application.yml' env: - name: youdu_appId valueFrom: secretKeyRef: key: youdu_appId name: ci-youdu-secret - name: youdu_buin valueFrom: secretKeyRef: key: youdu_buin name: ci-youdu-secret - name: youdu_encodingaesKey valueFrom: secretKeyRef: key: youdu_encodingaesKey name: ci-youdu-secret image: 'hub.imgo.tv/spinnaker/youdu:${ parameters.image_version}' imagePullPolicy: IfNotPresent livenessProbe: httpGet: path: / port: 8098 scheme: HTTP initialDelaySeconds: 30 timeoutSeconds: 5 name: ci-youdu volumeMounts: - mountPath: /usr/lib/application.yml name: app-conf subPath: application.yml volumes: - configMap: items: - key: application.yml path: application.yml name: 'ci-youdu-configmap-${ parameters.config_version}' name: app-conf 

Spinnaker对接K8S的不足

Spinnaker对于K8S的定位是持续部署工具,并不是核心的K8S管理工具,比起rancher这种专业的工具在管理上存在很大的差距。例如镜像授权、namespace管理、用户管理、性能监控、容器日志输出和控制台登陆等spinnaker都不支持。所以对于spinnaker的定位很重要,如果你们企业缺少一个自动发布的持续部署工具,请选择spinnaker;如果你们企业缺少一个专业的K8S管理工具,请选择Rancher。当然你可以两个都选(我们公司就是这么用的),因为spinnaker与rancher是可以兼容的,因为它们的数据来源都来自K8S本身。Spinnaker负责管理pod、service、ingress和发布流程,rancher负责管理namespace等其它spinnaker管理不了的资源。

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

上一篇

已是最后文章

下一篇

已是最新文章

发表回复