什么是argorollouts ArgoRollout是一个KubernetesController和对应一系列的CRD,提供更强大的Deployment能力。包括灰度发布、蓝绿部署、更新测试(experimentation)、渐进式交付(progressivedelivery)等特性。 支持特性如下:蓝绿色更新策略金丝雀更新策略细粒度,加权流量转移自动回rollback和promotion手动判断可定制的指标查询和业务KPI分析入口控制器集成:NGINX,ALB服务网格集成:Istio,Linkerd,SMIMetricprovider集成:Prometheus,Wavefront,Kayenta,Web,KubernetesJobs Argo原理和Deployment差不多,只是加强rollout的策略和流量控制。当spec。template发送变化时,ArgoRollout就会根据spec。strategy进行rollout,通常会产生一个新的ReplicaSet,逐步scaledown之前的ReplicaSet的pod数量。安装 按官方文档进行安装,官方地址为:https:argoproj。github。ioargorolloutsinstallationkubectlplugininstallation (1)在Kubernetes集群中安装argorolloutskubectlcreatenamespaceargorolloutskubectlapplynargorolloutsfhttps:raw。githubusercontent。comargoprojargorolloutsstablemanifestsinstall。yaml (2)安装argorollouts的kubectlplugincurlLOhttps:github。comargoprojargorolloutsreleaseslatestdownloadkubectlargorolloutslinuxamd64chmodx。kubectlargorolloutslinuxamd64mv。kubectlargorolloutslinuxamd64usrlocalbinkubectlargorollouts金丝雀发布 金丝雀发布包含ReplicaShifting和TrafficShifting两个过程。ReplicaShifting:版本替换TrafficShifting:流量接入 这里使用官方的demo来进行测试。例子:https:argoproj。github。ioargorolloutsgettingstartedReplicaShifting部署应用 使用如下命令部署示例:kubectlapplyfhttps:raw。githubusercontent。comargoprojargorolloutsmasterdocsgettingstartedbasicrollout。yamlkubectlapplyfhttps:raw。githubusercontent。comargoprojargorolloutsmasterdocsgettingstartedbasicservice。yaml 我们先看看第一个rollout。yaml的具体内容,如下:apiVersion:argoproj。iov1alpha1kind:Rolloutmetadata:name:rolloutsdemospec:replicas:5strategy:canary:steps:setWeight:20pause:{}setWeight:40pause:{duration:10}setWeight:60pause:{duration:10}setWeight:80pause:{duration:10}revisionHistoryLimit:2selector:matchLabels:app:rolloutsdemotemplate:metadata:labels:app:rolloutsdemospec:containers:name:rolloutsdemoimage:argoprojrolloutsdemo:blueports:name:httpcontainerPort:8080protocol:TCPresources:requests:memory:32Micpu:5m 可以看到除了apiVersion,kind以及strategy之外,其他和Deployment无异。 strategy字段定义的是发布策略,其中:setWeight:设置流量的权重pause:暂停,如果里面没有跟duration:10则表示需要手动更新,如果跟了表示等待多长时间会自动更新。 而service。yaml文件定义的就是普通的service,如下:apiVersion:v1kind:Servicemetadata:name:rolloutsdemospec:ports:port:80targetPort:httpprotocol:TCPname:httpselector:app:rolloutsdemo 执行上面命令部署后,会在default命名空间下创建5个pod,如下:kubectlgetpodNAMEREADYSTATUSRESTARTSAGEnfsclientprosioner598d477ff6fmgwf11Running217drolloutsdemo7bf84f96964glv611Running078srolloutsdemo7bf84f96967kqt611Running078srolloutsdemo7bf84f96968k9hw11Running078srolloutsdemo7bf84f96969cz2r11Running078srolloutsdemo7bf84f9696jvzvd11Running078s 可以使用kubectlargorolloutsgetrolloutrolloutsdemo命令来查看部署状态,如下:kubectlargorolloutsgetrolloutrolloutsdemoName:rolloutsdemoNamespace:defaultStatus:HealthyStrategy:CanaryStep:88SetWeight:100ActualWeight:100Images:argoprojrolloutsdemo:blue(stable)Replicas:Desired:5Current:5Updated:5Ready:5Available:5NAMEKINDSTATUSAGEINFOrolloutsdemoRolloutHealthy114srevision:1rolloutsdemo7bf84f9696ReplicaSetHealthy114sstablerolloutsdemo7bf84f96964glv6PodRunning114sready:11rolloutsdemo7bf84f96967kqt6PodRunning114sready:11rolloutsdemo7bf84f96968k9hwPodRunning114sready:11rolloutsdemo7bf84f96969cz2rPodRunning114sready:11rolloutsdemo7bf84f9696jvzvdPodRunning114sready:11 可以看到该版本被标记为stable,而且STATUS为healthy。还可以在命令后面加一个watch来实时监控服务状态,完整命令为kubectlargorolloutsgetrolloutrolloutsdemowatch。更新应用 接下来对应用进行更新。对应用进行更新和更新用Deployment部署的应用一样,更新镜像即可。argorollouts插件有一个setimage命令来更新镜像,如下:kubectlargorolloutssetimagerolloutsdemorolloutsdemoargoprojrolloutsdemo:yellow 更新过后,我们可以通过观察kubectlargorolloutsgetrolloutrolloutsdemowatch服务状态,如下:Name:rolloutsdemoNamespace:defaultStatus:PausedMessage:CanaryPauseStepStrategy:CanaryStep:18SetWeight:20ActualWeight:20Images:argoprojrolloutsdemo:blue(stable)argoprojrolloutsdemo:yellow(canary)Replicas:Desired:5Current:5Updated:1Ready:5Available:5NAMEKINDSTATUSAGEINFOrolloutsdemoRolloutPaused9m12srevision:2rolloutsdemo789746c88dReplicaSetHealthy44scanaryrolloutsdemo789746c88dl4gmdPodRunning44sready:11revision:1rolloutsdemo7bf84f9696ReplicaSetHealthy9m12sstablerolloutsdemo7bf84f96964glv6PodRunning9m12sready:11rolloutsdemo7bf84f96968k9hwPodRunning9m12sready:11rolloutsdemo7bf84f96969cz2rPodRunning9m12sready:11rolloutsdemo7bf84f9696jvzvdPodRunning9m12sready:11 可以看到多了一个revision:2,而且该版本被标记为canary,而且状态是Status:Paused,canary接入流量为20。 部署之所以处于Paused阶段,是因为我们在rollout。yaml中定义了发布第一个版本后会暂停,这时候需要手动接入接下来的更新。 argorollouts提供了promote来进行后续的更新,命令如下:kubectlargorolloutspromoterolloutsdemo 然后我们可以在watch界面,看到如下的更新过程。Name:rolloutsdemoNamespace:defaultStatus:PausedMessage:CanaryPauseStepStrategy:CanaryStep:38SetWeight:40ActualWeight:40Images:argoprojrolloutsdemo:blue(stable)argoprojrolloutsdemo:yellow(canary)Replicas:Desired:5Current:5Updated:2Ready:5Available:5NAMEKINDSTATUSAGEINFOrolloutsdemoRolloutPaused15mrevision:2rolloutsdemo789746c88dReplicaSetHealthy6m46scanaryrolloutsdemo789746c88dl4gmdPodRunning6m46sready:11rolloutsdemo789746c88d67dwpPodRunning19sready:11revision:1rolloutsdemo7bf84f9696ReplicaSetHealthy15mstablerolloutsdemo7bf84f96964glv6PodRunning15mready:11rolloutsdemo7bf84f96968k9hwPodRunning15mready:11rolloutsdemo7bf84f96969cz2rPodRunning15mready:11rolloutsdemo7bf84f9696jvzvdPodTerminating15mready:11 因为后续的更新在pause阶段只暂停10s,所以会依次自动更新完,不需要手动介入,待更新完后整体的状态如下:Name:rolloutsdemoNamespace:defaultStatus:HealthyStrategy:CanaryStep:88SetWeight:100ActualWeight:100Images:argoprojrolloutsdemo:yellow(stable)Replicas:Desired:5Current:5Updated:5Ready:5Available:5NAMEKINDSTATUSAGEINFOrolloutsdemoRolloutHealthy17mrevision:2rolloutsdemo789746c88dReplicaSetHealthy8m35sstablerolloutsdemo789746c88dl4gmdPodRunning8m35sready:11rolloutsdemo789746c88d67dwpPodRunning2m8sready:11rolloutsdemo789746c88dk7mfkPodRunning106sready:11rolloutsdemo789746c88dglbfbPodRunning94sready:11rolloutsdemo789746c88dd7m4fPodRunning83sready:11revision:1rolloutsdemo7bf84f9696ReplicaSetScaledDown17m 可以看到第一个版本已经下线,第二个版本的状态为Healthy,而且镜像被标记为stable。终止更新 如果在更新应用的过程中,最新的应用有问题,需要终止更新需要怎么做呢? 我们先使用下面命令发布新版本应用,如下:kubectlargorolloutssetimagerolloutsdemorolloutsdemoargoprojrolloutsdemo:red 然后更新动作会在第一次更新的时候处于Paused状态,现在我们可以用abort来终止发布,如下:kubectlargorolloutsabortrolloutsdemo 待执行完命令后,可以在watch页面,看到如下信息:Name:rolloutsdemoNamespace:defaultStatus:DegradedMessage:RolloutAborted:RolloutisabortedStrategy:CanaryStep:08SetWeight:0ActualWeight:0Images:argoprojrolloutsdemo:yellow(stable)Replicas:Desired:5Current:5Updated:0Ready:5Available:5NAMEKINDSTATUSAGEINFOrolloutsdemoRolloutDegraded21mrevision:3rolloutsdemo6f75f48b7ReplicaSetScaledDown90scanaryrevision:2rolloutsdemo789746c88dReplicaSetHealthy13mstablerolloutsdemo789746c88dl4gmdPodRunning13mready:11rolloutsdemo789746c88d67dwpPodRunning6m46sready:11rolloutsdemo789746c88dk7mfkPodRunning6m24sready:11rolloutsdemo789746c88dglbfbPodRunning6m12sready:11rolloutsdemo789746c88dnntc9PodRunning18sready:11revision:1rolloutsdemo7bf84f9696ReplicaSetScaledDown21m 最终应用会回退到稳定版本。 但是我们可以看到Status是Degraded状态而并非Healthy状态,我们有必须要将其变成Healthy状态。最简单的办法就是执行如下命令重新发布一下版本:kubectlargorolloutssetimagerolloutsdemorolloutsdemoargoprojrolloutsdemo:yellow 执行过后,可以看到其状态立即变成Healthy,并且没有创建新的副本、新的版本,如下:Name:rolloutsdemoNamespace:defaultStatus:HealthyStrategy:CanaryStep:88SetWeight:100ActualWeight:100Images:argoprojrolloutsdemo:yellow(stable)Replicas:Desired:5Current:5Updated:5Ready:5Available:5NAMEKINDSTATUSAGEINFOrolloutsdemoRolloutHealthy40mrevision:4rolloutsdemo789746c88dReplicaSetHealthy32mstablerolloutsdemo789746c88dl4gmdPodRunning32mready:11rolloutsdemo789746c88d67dwpPodRunning26mready:11rolloutsdemo789746c88dk7mfkPodRunning25mready:11rolloutsdemo789746c88dglbfbPodRunning25mready:11rolloutsdemo789746c88dnntc9PodRunning19mready:11revision:3rolloutsdemo6f75f48b7ReplicaSetScaledDown20mrevision:1rolloutsdemo7bf84f9696ReplicaSetScaledDown40m回退应用 有时候在应用上线过后,有些BUG并没有发现,这时候要回退怎么办呢?argorollouts有一个undo命令,可以进行回退。 比如我们要将版本回退到第一个版本,则执行一下命令:kubectlargorolloutsundorolloutsdemotorevision1 然后通过watch界面可以看到如下信息:Name:rolloutsdemoNamespace:defaultStatus:PausedMessage:CanaryPauseStepStrategy:CanaryStep:18SetWeight:20ActualWeight:20Images:argoprojrolloutsdemo:blue(canary)argoprojrolloutsdemo:yellow(stable)Replicas:Desired:5Current:5Updated:1Ready:5Available:5NAMEKINDSTATUSAGEINFOrolloutsdemoRolloutPaused45mrevision:5rolloutsdemo7bf84f9696ReplicaSetHealthy45mcanaryrolloutsdemo7bf84f9696bn2lzPodRunning36sready:11revision:4rolloutsdemo789746c88dReplicaSetHealthy36mstablerolloutsdemo789746c88dl4gmdPodRunning36mready:11rolloutsdemo789746c88d67dwpPodRunning30mready:11rolloutsdemo789746c88dk7mfkPodRunning29mready:11rolloutsdemo789746c88dglbfbPodRunning29mready:11revision:3rolloutsdemo6f75f48b7ReplicaSetScaledDown25m 首先revision为1的版本标记没有,重新创建了一个为5的标记,而且第一步处于暂停状态,然后我们执行promote命令继续后续的更新,如下:kubectlargorolloutspromoterolloutsdemo 然后我们可以看到如下信息:Name:rolloutsdemoNamespace:defaultStatus:HealthyStrategy:CanaryStep:88SetWeight:100ActualWeight:100Images:argoprojrolloutsdemo:blue(stable)Replicas:Desired:5Current:5Updated:5Ready:5Available:5NAMEKINDSTATUSAGEINFOrolloutsdemoRolloutHealthy48mrevision:5rolloutsdemo7bf84f9696ReplicaSetHealthy48mstablerolloutsdemo7bf84f9696bn2lzPodRunning3m21sready:11rolloutsdemo7bf84f9696xn6drPodRunning56sready:11rolloutsdemo7bf84f9696w58vmPodRunning44sready:11rolloutsdemo7bf84f9696fns8dPodRunning33sready:11rolloutsdemo7bf84f9696qt6f9PodRunning22sready:11revision:4rolloutsdemo789746c88dReplicaSetScaledDown39mrevision:3rolloutsdemo6f75f48b7ReplicaSetScaledDown27m 从Images可以看到回退到我们最初版本为blue的镜像了。TrafficShifting 上面我们并没有接入外部流量,仅仅是在内部使用展示了金丝雀部署过程,下面我们接入外部流量进行测试。 ArgoRollout主要集成了Ingress和ServiceMesh两种流量控制方法。 目前Ingress支持ALB和NGINXingress。但是我使用的是nginxingress。部署应用 我们依然使用官方的例子进行展示。 首先删除上面的例子。kubectldeletefhttps:raw。githubusercontent。comargoprojargorolloutsmasterdocsgettingstartedbasicrollout。yamlkubectldeletefhttps:raw。githubusercontent。comargoprojargorolloutsmasterdocsgettingstartedbasicservice。yaml 然后重新部署一个官方的例子,如下:kubectlapplyfhttps:raw。githubusercontent。comargoprojargorolloutsmasterdocsgettingstartednginxrollout。yamlkubectlapplyfhttps:raw。githubusercontent。comargoprojargorolloutsmasterdocsgettingstartednginxservices。yamlkubectlapplyfhttps:raw。githubusercontent。comargoprojargorolloutsmasterdocsgettingstartednginxingress。yaml 这个例子包含1个rollout,2个service,1个ingress。 它们的配置文件分别如下。 rollout。yaml,为了便于测试,我将权重改为了50apiVersion:argoproj。iov1alpha1kind:Rolloutmetadata:name:rolloutsdemospec:replicas:1strategy:canary:canaryService:rolloutsdemocanarystableService:rolloutsdemostabletrafficRouting:nginx:stableIngress:rolloutsdemostablesteps:setWeight:50pause:{}revisionHistoryLimit:2selector:matchLabels:app:rolloutsdemotemplate:metadata:labels:app:rolloutsdemospec:containers:name:rolloutsdemoimage:argoprojrolloutsdemo:blueports:name:httpcontainerPort:8080protocol:TCPresources:requests:memory:32Micpu:5m services。yamlapiVersion:v1kind:Servicemetadata:name:rolloutsdemocanaryspec:ports:port:80targetPort:httpprotocol:TCPname:httpselector:app:rolloutsdemoThisselectorwillbeupdatedwiththepodtemplatehashofthecanaryReplicaSet。e。g。:rolloutspodtemplatehash:7bf84f9696apiVersion:v1kind:Servicemetadata:name:rolloutsdemostablespec:ports:port:80targetPort:httpprotocol:TCPname:httpselector:app:rolloutsdemoThisselectorwillbeupdatedwiththepodtemplatehashofthestableReplicaSet。e。g。:rolloutspodtemplatehash:789746c88d ingress。yamlapiVersion:networking。k8s。iov1beta1kind:Ingressmetadata:name:rolloutsdemostableannotations:kubernetes。ioingress。class:nginxspec:rules:host:rolloutsdemo。localhttp:paths:path:backend:ReferencetoaServicename,alsospecifiedintheRolloutspec。strategy。canary。stableServicefieldserviceName:rolloutsdemostableservicePort:80 从配置文件可以看出Rollout里分别用canaryService和stableService分别定义了该应用灰度的ServiceName(rolloutsdemocanary)和当前版本的ServiceName(rolloutsdemostable)。而且rolloutsdemocanary和rolloutsdemostable的service的内容是一样的。selector中暂时没有填上podtemplatehash,ArgoRolloutController会根据实际的ReplicaSethash来修改该值。 当我们创建完ingress后,RolloutController会根据ingressrolloutsdemostable内容,自动创建一个ingress用了灰度的流量,名字为canary,所以这里多了一个ingressrolloutsdemorolloutsdemostablecanary,将流量导向CanaryService(rolloutsdemocanary)。如下:kubectlgetingressNAMEHOSTSADDRESSPORTSAGErolloutsdemorolloutsdemostablecanaryrolloutdemo。coolops。cn809m25srolloutsdemostablerolloutdemo。coolops。cn804m12s rolloutsdemorolloutsdemostablecanary的内容如下:kubectlgetingressrolloutsdemorolloutsdemostablecanaryoyamlapiVersion:extensionsv1beta1kind:Ingressmetadata:annotations:kubernetes。ioingress。class:traefiknginx。ingress。kubernetes。iocanary:truenginx。ingress。kubernetes。iocanaryweight:0creationTimestamp:20201209T02:21:52Zgeneration:2name:rolloutsdemorolloutsdemostablecanarynamespace:defaultownerReferences:apiVersion:argoproj。iov1alpha1blockOwnerDeletion:truecontroller:truekind:Rolloutname:rolloutsdemouid:4e74913b5c8942758f4c768f23c63c34resourceVersion:15681411selfLink:apisextensionsv1beta1namespacesdefaultingressesrolloutsdemorolloutsdemostablecanaryuid:bc66dfc46e98419ba288f67e1233ef3espec:rules:host:rolloutdemo。coolops。cnhttp:paths:backend:serviceName:rolloutsdemocanaryservicePort:80path: 通过域名访问,可以看到如下界面。 更新应用 现在通过以下命令来进行应用更新操作。kubectlargorolloutssetimagerolloutsdemorolloutsdemoargoprojrolloutsdemo:yellow 然后通过状态窗口可以看到如下信息。Name:rolloutsdemoNamespace:defaultStatus:PausedMessage:CanaryPauseStepStrategy:CanaryStep:12SetWeight:50ActualWeight:50Images:argoprojrolloutsdemo:blue(stable)argoprojrolloutsdemo:yellow(canary)Replicas:Desired:1Current:2Updated:1Ready:2Available:2NAMEKINDSTATUSAGEINFOrolloutsdemoRolloutPaused2m13srevision:2rolloutsdemo789746c88dReplicaSetHealthy89scanaryrolloutsdemo789746c88dspn4sPodRunning89sready:11revision:1rolloutsdemo7bf84f9696ReplicaSetHealthy2mstablerolloutsdemo7bf84f96967rwkxPodRunning2mready:11 然后可以看到rolloutsdemorolloutsdemostablecanary的ingress的annotations中新增了两个参数,如下:kubectlgetingressrolloutsdemorolloutsdemostablecanaryoyamlapiVersion:extensionsv1beta1kind:Ingressmetadata:annotations:kubernetes。ioingress。class:nginxnginx。ingress。kubernetes。iocanary:truenginx。ingress。kubernetes。iocanaryweight:50creationTimestamp:20201209T03:01:04Zgeneration:1name:rolloutsdemorolloutsdemostablecanarynamespace:defaultownerReferences:apiVersion:argoproj。iov1alpha1blockOwnerDeletion:truecontroller:truekind:Rolloutname:rolloutsdemouid:4d956f2a9e154453b918926c4a75f884resourceVersion:15686969selfLink:apisextensionsv1beta1namespacesdefaultingressesrolloutsdemorolloutsdemostablecanaryuid:c9242819d0884fc4bd4d8870360fa96espec:rules:host:rolloutdemo。coolops。cnhttp:paths:backend:serviceName:rolloutsdemocanaryservicePort:80path: 然后通过网页,可以看到如下的输出展示。 image。png 然后可以通过验证结果来判断是否继续还是终止。 如果继续使用如下命令:kubectlargorolloutspromoterolloutsdemo 如果终止使用如下命令:kubectlargorolloutsabortrolloutsdemo写在最后 目前我还在测试阶段,并没有实际接入使用。通过测试来看,ArgoRollout提供更加强大的Deployment,包含比较适合运维的金丝雀发布和蓝绿发布功能,要使用蓝绿发布,仅需要配置rollout,如下:apiVersion:argoproj。iov1alpha1kind:Rollout部署完rollout后就有了这个kind资源,这个资源和deployment类似也是管理你的副本集的,所以不能像deployment那样在k8s界面看见,只能通过kubectl命令行metadata:name:rolloutbluegreennamespace:rollouttestspec:template:spec:terminationGracePeriodSeconds:30containers:resources:{}requests:cpu:1memory:2Gilimits:cpu:2memory:2GiterminationMessagePolicy:FileimagePullPolicy:Alwaysname:rolloutbluegreenimage:argoprojrolloutsdemo:greennginx:1。17。1schedulerName:defaultschedulersecurityContext:{}dnsPolicy:ClusterFirstrestartPolicy:Alwaysmetadata:labels:app:rolloutbluegreenselector:matchLabels:app:rolloutbluegreenreplicas:2strategy:blueGreen:蓝绿启用配置activeService:rolloutbluegreenactive生效的服务,需要自己创建建本代码最下面service资源。previewService:rolloutbluegreenpreview配置预览服务,同理需要自己创建autoPromotionEnabled:true是否直接切换,如为true,会在新版本变绿后直接切换到对外服务。scaleDownDelayRevisionLimit:0previewReplicaCount:1新版本的pod数量,设为一个从而控制资源消耗rollingUpdate:maxUnavailable:25maxSurge:25type:RollingUpdaterevisionHistoryLimit:2progressDeadlineSeconds:600apiVersion:v1kind:Servicemetadata:name:rolloutbluegreenactivenamespace:rollouttestspec:sessionAffinity:Noneselector:app:rolloutbluegreenports:protocol:TCPport:80targetPort:8080type:LoadBalancer 整体使用还是比较丝滑,如果测试通过后续考虑集成进CD中。更多内容可以到https:argoproj。github。ioargorollouts进行学习。