在Kubernetes中部署网站的综合指南

末鹿安然 提交于 2020-10-05 14:43:14

最近,我们做了一个把Grofers的所有服务迁移到Kubernetes上的决定。

Grofers是杂货领域印度最大的低价在线超市,成立于2013年12月01日,由Albinder Dhindsa和Saurabh Kumar创立,总部位于印度德里的卫星城市Gurugram。其主要提供各种类别的产品,如杂货、水果和蔬菜、美容和保健、家庭护理、婴儿护理、宠物护理、面包和肉类以及海鲜等产品。

在迁移前端网站到Kubernetes上的过程中,我们发现默认的滚动更新部署策略并不如理想中那么好,所以我们不得不使用蓝绿策略来代替(后面会详细讨论)。

对于那些想要做同样迁移工作的人,这篇博客可以作为一个指南,因为当前缺乏这块主题相关的资源,所以这可以节省他们的时间和精力。

首先我们会介绍一些基础知识,然后一步一步的介绍怎么实现,最后再通过运行负载测试来考察不同策略的效果。

什么是Kubernetes?

Kubernetes是一个开放源代码的容器编排系统,用于自动化应用程序的部署,扩展和管理。

我不会介绍Kubernetes所有的基础知识(可以在这里查看其它好的资源[1])。这篇文章中我只会介绍两个非常常见的部署策略。

第一个是蓝绿策略。对于部署单页面网站应用(网站资源使用版本控制),推荐使用该策略。

另一个是滚动更新策略,它可能是Kubernetes中最常用的策略了并且也是Grofers所有后端服务正在使用的策略。

蓝绿策略vs滚动更新

滚动更新

滚动更新通过用新的Pod逐渐替换旧的Pod进行部署。

如上图所示,中间黄色菱形是负载均衡器服务,它负责把流量分发到Pod(应用程序的单实例)。

所以在任何时间,你的应用都可能同时存在新旧版本的Pod。

蓝绿策略

蓝绿策略创建一个全新的部署,它和旧的实例处于并行状态。然后配置负载均衡器,让其把流量切到新的部署实例上,同时停止转发流量到旧的部署实例上。最后删除旧的部署实例。

Grofers使用什么部署策略?

由于我们的后端服务都是无状态的,因此滚动更新策略在这里更有意义,因为它提供了更快的部署和分阶段部署的能力。

但是对于我们的网站,我们发现滚动更新策略不是一个零停机(zero-downtime)策略。原因有两个:

  • 如果蓝绿两种Pod同时存在(滚动更新策略的情况),那么在为版本化资源提供服务时会出现问题。在新的部署中与index.html对应的资源可能不存在于运行在旧的部署中的容器中。下图可以很直观的解释这种情况。

  • 我们还有一个和前端服务一起运行的小型Node.js API服务。所以基本上这是我们网站运行的整体情况。现在假设我们在Node服务上增加一个新的API端点(Endpoint)。对于滚动更新部署,运行在新的部署上面的前端代码中的路由不存在于运行在旧的部署上面的后端服务上。

原因 #1,解释

显而易见,我们需要运行仅包含一个版本的Pod的部署,而不是同时运行两个版本(在滚动更新的情况下)。

蓝绿色部署策略是我们部署网站的必由之路。接下来,我们将深入研究一些代码,并研究如何实现它。

详细指南

我们的想法是使用最新的绿色版本部署新部署(我们使用git commit hash进行版本控制)。

然后等到它启动并且运行后,我们再更新服务,让它指向这个新的部署实例,最后删除旧的部署实例。

部署代码段

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: w3g-xxxxxxx
  labels:
    app: w3g
    version: "xxxxxxx"

服务代码段

apiVersion: v1
kind: Service
spec:
   type: LoadBalancer
   selector:
     app: w3g
     version: "xxxxxxx"

Makefile代码段

kube_swap_blue_green:
 $(eval BlueVersion=$(shell kubectl get service ${KUBE_APP_NAME} -o=jsonpath='{.spec.selector.version}' -n ${ENV_KUBE_NAMESPACE}))
 $(eval GreenVersion=${LATEST_GIT_COMMIT_ID})
 kubectl get deployment ${KUBE_APP_NAME}-${BlueVersion} -o=yaml -n ${ENV_KUBE_NAMESPACE} | sed -e "s/${BlueVersion}/${GreenVersion}/g" | kubectl apply -f - -n ${ENV_KUBE_NAMESPACE}
 kubectl rollout status deployment/${KUBE_APP_NAME}-${GreenVersion} -n ${ENV_KUBE_NAMESPACE}
 kubectl get service ${KUBE_APP_NAME} -o=yaml -n ${ENV_KUBE_NAMESPACE} | sed -e "s/${BlueVersion}/${GreenVersion}/g" | kubectl apply -f - -n ${ENV_KUBE_NAMESPACE}
 kubectl delete deployment ${KUBE_APP_NAME}-${BlueVersion} -n ${ENV_KUBE_NAMESPACE}

我们使用make在CI/CD管道中进行构建和部署。无论你使用哪种工具,步骤都是相同的。

首先,获取蓝色版本(以前使用的git commit hash),可以通过查询已经运行的服务以获取其正在使用的部署版本。

$(eval BlueVersion=$(shell kubectl get service ${KUBE_APP_NAME} -o=jsonpath='{.spec.selector.version}' -n ${ENV_KUBE_NAMESPACE}))

绿色版本不过是最新的git commit hash。

$(eval GreenVersion=${LATEST_GIT_COMMIT_ID})

接下来,我们得到当前的部署,通过将蓝色版本替换为绿色版本来对其进行更改。

kubectl get deployment ${KUBE_APP_NAME}-${BlueVersion} -o=yaml -n ${ENV_KUBE_NAMESPACE} | sed -e "s/${BlueVersion}/${GreenVersion}/g" | kubectl apply -f - -n ${ENV_KUBE_NAMESPACE}

然后,我们等待绿色部署完成,然后再继续。

kubectl rollout status deployment/${KUBE_APP_NAME}-${GreenVersion} -n ${ENV_KUBE_NAMESPACE}

再接下来,我们获取当前服务并将其配置为使用绿色版本而不是蓝色版本。

kubectl get service ${KUBE_APP_NAME} -o=yaml -n ${ENV_KUBE_NAMESPACE} | sed -e "s/${BlueVersion}/${GreenVersion}/g" | kubectl apply -f - -n ${ENV_KUBE_NAMESPACE}

最后,我们删除旧的蓝色版本。

kubectl delete deployment ${KUBE_APP_NAME}-${BlueVersion} -n ${ENV_KUBE_NAMESPACE}

以上就是全部操作。

下一步计划?

这就是我们使用蓝绿色部署策略为网站实现零停机时间部署的方式。

下一篇博客中,我们会深入探讨怎么样对我们网站进行负载测试以及验证不同的策略。

我们将使用JMeter之类的工具以及Selenium WebDriver插件来并行打开多个浏览器客户端(目前为止仅支持Firefox),并在进行新部署时进行负载测试。我们还将执行一个简单的操作,例如单击一个对话框,查看在滚动更新和蓝绿部署过程中遇到了多少故障。

敬请关注。

相关链接:

  1. https://dev.to/adipolak/kubernetes-and-virtual-kubelet-in-a-nutshell-gn4

原文链接:https://lambda.grofers.com/a-comprehensive-guide-to-deploying-a-website-in-kubernetes-c5829bfa4519

DDD实战演练工作坊

DDD实战演练工作坊将于2020年10月17日在北京开课。DDD全过程工作坊将以事件风暴为纵贯线,以领域场景为横切面,驱动从战略设计到战术设计的全生命周期的完整开发过程。内容涵盖事件风暴、限界上下文、上下文映射、角色构造型、场景驱动设计和测试驱动开发。整个工作坊围绕一个全真案例进行演练,实践具有实操价值的领域驱动设计方法。点击下方图片或者阅读原文链接查看详情。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!