SDMK(Smart Data Market)是TalkingData的数据智能市场,该平台提供了多样化的数据服务,包含API服务、SaaS服务以及Lookalike、预测引擎等人工智能算法模型服务,目地在于下降数据应用场景的难度,帮助更多的企业发现数据的深层价值。java
SDMK数据平台是采用微服务架构来设计实现的,主要APP模块近二十几个,再加上第三方数据服务的适配器以及其它APP模块近七十多个,提供的REST API近成百上千个。微服务架构的应用使得每一个服务均可以有专门的开发团队(或我的)来开发,开发者能够自由选择开发技术,而且每一个微服务均可以独立开发、升级、拓展。所以系统具有很高的稳定性和快速迭代能力,但同时也会遇到一些问题,如负载均衡、故障重启、弹性伸缩、资源隔离等等。node
由于一个微服务背后可能有多个副本实例在支撑,如何作到自动负载均衡?当其中一个副本实例宕机了,是否须要人工重启?当流量增长的时候,如何方便或者自动的增长节点,以知足相应的SLA?面对以上等等的这些问题,Kubernetes(K8s)可以很好解决,从而也使其成为企业微服务容器化的首选解决方案。nginx
本文将简单介绍K8s核心概念,并重点介绍TalkingData SDMK采用K8s的容器化部署实践。redis
首先,Kubernetes(K8s)是自动化容器操做的开源平台,这些操做包括部署、调度和节点集群间扩展。若是你曾经用过Docker容器技术部署容器,那么能够将Docker当作Kubernetes内部使用的低级别组件。Kubernetes不只仅支持Docker,还支持另外一种容器技术Rocket。算法
使用Kubernetes能够:docker
Master数据库
集群拥有一个K8s Master(上图中紫色方框)。K8s Master提供集群的独特视角,而且拥有一系列组件,好比K8s API Server。API Server提供能够用来和集群交互的REST 接口。Master节点包括用来建立和复制Pod的Replication Controller。apache
Node后端
Node做为集群中的工做节点,运行真正的应用程序,在Node上K8s管理的最小运行单元是Pod。Node上运行着K8s的Kubelet、kube-proxy服务进程,这些服务进程负责Pod的建立、启动、监控、重启、销毁、以及实现软件模式的负载均衡。节点(上图橘色方框)是物理或者虚拟机器,做为K8s worker,一般称为Minion。每一个节点都运行以下K8s关键组件:api
Pod
Pod是K8s最基本的操做单元,包含一个或多个紧密相关的容器,一个Pod能够被一个容器化的环境看做应用层的“逻辑宿主机”;一个Pod中的多个容器应用一般是紧密耦合的,Pod在Node上被建立、启动或者销毁;每一个Pod里运行着一个特殊的被称之为Pause的容器,其余容器则为业务容器,这些业务容器共享Pause容器的网络栈和Volume挂载卷,所以他们之间通讯和数据交换更为高效,在设计时咱们能够充分利用这一特性将一组密切相关的服务进程放入同一个Pod中。
Pod的生命周期经过Replication Controller来管理,经过模板进行定义,而后分配到一个Node上运行,在Pod所包含容器运行结束后,Pod结束。K8s为Pod设计了一套独特的网络配置,包括:为每一个Pod分配一个IP地址,使用Pod名做为容器间通讯的主机名等。
RC(Replication Controller)
RC就是复制控制器,新一代的RC叫RS(Replication Set)其主要功能以下:
Labels
Labels以key/value的形式附加到各类对象上,如Pod、Service、RC、Node等,以识别这些对象,管理关联关系等,如Service和Pod的关联关系,有了这种关联关系,就能够经过选择器(Selector)来进行筛选那个服务和那个Pod进行关联。
Label selectors
经过label selectors,用户能够识别一些对象,它是K8s的核心Grouping Primitive。其选择器主要有如下两种:
Deployment
部署表示用户对K8s集群的一次更新操做。部署是一个比RS应用模式更广的API对象,能够是建立一个新的服务,更新一个新的服务,也能够是滚动升级一个服务。滚动升级一个服务,实际是建立一个新的RS,而后逐渐将新RS中副本数增长到理想状态,将旧RS中的副本数减少到0的复合操做;这样一个复合操做用一个RS是不太好描述的,因此用一个更通用的Deployment来描述。以K8s的发展方向,将来对全部长期伺服型的的业务的管理,都会经过Deployment来管理。
Service
RC、RS和Deployment只是保证了支撑服务的Pod的数量,可是没有解决如何访问这些服务的问题。一个Pod只是一个运行服务的实例,随时可能在一个节点上中止,在另外一个节点以一个新的IP启动一个新的Pod,所以不能以肯定的IP和端口号来提供服务。要稳定地提供服务须要服务发现和负载均衡能力。服务发现完成的工做,是针对客户端访问的服务,找到对应的后端服务实例。在K8s集群中,客户端须要访问的服务就是Service对象。每一个Service会对应一个集群内部有效的虚拟IP,集群内部经过虚拟IP访问一个服务。在K8s集群中服务的负载均衡是由Kube-proxy实现的。Kube-proxy是K8s集群内部的负载均衡器。它是一个分布式代理服务器,在K8s的每一个节点上都有一个;这一设计体现了它的伸缩性优点,须要访问服务的节点越多,提供负载均衡能力的Kube-proxy就越多,高可用节点也随之增多。
1、服务暴露实践
上文概念中所说Service一般只是集群内部有效,在K8s的世界里,服务要对集群外暴露,一般有这么几种方式:
1. Cluseter IP
集群内的私有IP,这是默认值。
2. NodePort Service
其实质上是经过在集群的每一个node上暴露一个端口,而后将这个端口映射到某个具体的service来实现的,虽然每一个node的端口不少,但因为安全性和易用性(服务多了就乱了,易产生端口冲突),实际使用的并很少。
3. LoadBalancer Service
是K8s深度结合云平台的一个组件,当使用LoadBalancer service暴露服务时,其实是经过底层平台申请建立一个负载均衡器来向外暴露服务的。这种方式多数应用于云平台上来使用。
4. Ingress Service
经过Ingress用户能够实现使用nginx等开源的反向代理负载均衡器实现对外暴露服务。它包含有三个组件:反向代理负载均衡器、Ingress Controller、Ingress。
SDMK部署就是采用的Ingress这种方式。在K8s集群中,Ingress 是受权入站链接到达集群服务的规则集合,为其提供七层负载均衡能力,从而能够经过 Ingress 配置提供外部可访问的 URL、负载均衡、SSL、基于名称的虚拟主机等。
SDMK在部署的时候主要考虑了如下两种部署方式:
01共享Ingress
如上述部署架构图,全部的服务共享一个Ingress实例,该实例做为集群全部服务的流量入口,但问题就是,一旦流量入口出现问题,例如性能问题、可用性问题等,就会影响集群内全部服务的可用性。
02独占Ingress
如上述部署架构图,由多个独占 Ingress 实例组成统一接入层承载集群入口流量,每一个微服务模块独占一个Ingress,同时也可依据后端业务流量水平扩缩容 Ingress 节点。固然若是前期的集群规模并不大,也能够采用将 Ingress 服务与业务应用混部的方式,但建议进行资源限制和隔离。
做为集群流量入口,Ingress的高可靠性显得尤其重要,出于这样的考虑,SDMK采用独占Ingress的同时,并将nginx-ingress-controller采用多副本部署的方式以及将其与后端service进行资源隔离,这样能够避免业务应用与 Ingress 服务发生资源争抢,以及单点的问题。
2、弹性伸缩实践
在K8s中,Pod是最基础的调度单位,多个Pod 能够组成一个集合,这个集合向外提供服务。这时候,有如下两种情形须要关注:
若是人肉来实时监控实例的运行状态,手动启动新的Pod以替代fail的Pod,监控实例的负载状况,手动建立或者删除Pod,这个工做繁琐且工做量大,好在K8s已经有相应的机制来应对这种变化,这个机制就是弹性伸缩。
弹性伸缩式一种根据资源使用状况自动伸缩工做负载的方法。弹性伸缩在K8s中有两个维度:Cluster Autoscaler 和 Horizontal Pod Autoscaler ,两者一块儿可用于动态调整计算能力以及并行性的水平扩展能力,从而知足系统的SLA。Cluster Autaoscaler 依赖于托管集群的云提供商的底层功能,HPA就能够独立于Iaas/Paas提供商进行操做了。
简单的来说,就是HPA实现了一个控制环,能够周期性的经过资源指标API查询特定应用的CPU、MEM等信息,而Metrics server 负责收集各个应用Pod的指标信息,再经过控制器的计算,从而实现弹性伸缩。当前主要的指标分为如下三种:
计算的算法:
desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]
例如:
currentMetricValue 为 200m, desiredMetricValue 为100m ,那么副本的个数就会翻倍,前提是不超过你设置的最大值,若是超过,就把最大值做为当前的目标副本数目。若是算出来的值为0.5,就大于等于该数的最小整数。
在这里要说明是注意你所采用的K8s版本:
在K8s v1.1中首次引入了HPA特性,其第一个版本基于观察到的CPU利用率,后续版不断发展,也支持了基于内存使用。在K8s 1.6中引入了一个新的API自定义指标API,它容许HPA访问任意指标。在K8s1.7引入了聚合层,容许第三方应用程序经过注册为API附加组件来扩展K8s API。自定义指标API以及聚合层使得像Prometheus这样的监控系统能够向HPA控制器公开特定于应用程序的指标。
实践部分
须要在Deployment 的yaml文件中定义每一个APP使用的资源状况,至于资源的限制大小须要根据实际的业务需求而定,并添加上对应探针策略。
1apiVersion: autoscaling/v2beta1 2kind: HorizontalPodAutoscaler 3metadata: 4 name: dm-sdmk-kafka-service 5 namespace: dm 6spec: 7 scaleTargetRef: 8 apiVersion: extensions/v1beta1 9 kind: Deployment 10 name: dm-sdmk-kafka-service 11 minReplicas: 3 12 maxReplicas: 5 13 metrics: 14 - type: Resource 15 resource: 16 name: cpu 17 targetAverageUtilization: 50 18 - type: Resource 19 resource: 20 name: memory 21 targetAverageValue: 1536Mi
HPA的实现须要在HorizontalPodAutoscaler 的Yaml文件的定义具体弹性伸缩条件以及阈值,即最少的Pod个数、最多的Pod个数,根据什么条件来伸缩,例如CPU平均利用率达到50%,或者内存平均使用超过1536M等,当相应服务的监控条件达到对应的阈值时,就会伸缩Pod。具体的设置要根据相应业务模块类型来进行设置。SDMK在部署的时候多数根据内存条件来进行伸缩的,由于多数的APP应用在流量上来的时候消耗内存比较多。
3、引流实践
当K8s环境下的SDMK已经部署完毕,并通过简单测试,处于可用状态,如今想将特定用户的流量引入到K8s环境下的SDMK,从而使其可以承担起必定的业务流量,达到物理机环境下的SDMK与K8s环境下的SDMK混合部署效果,如上图所示。
01物理机部署
如上图所示,原理物理机上的部署,是经过物理机上nginx作负载均衡的,下面挂载多个物理机上Gateway(网关)节点,Gateway是SDMK的一个网关,用于接收并处理服务调用请求的。
01并行部署
并行部署就如上图所示,将K8s环境的入口域名以Location的方式也挂载在nginx下,在nginx处来将流量区分开来,哪些用户的流量打到K8s环境,哪些用户的流量打到物理机环境。SDMK是经过在nginx中添加lua脚原本实现的,其部署状况以下图所示:
实现步骤:
实践部分
Lua脚本实现脚本:
1local _M = {} 2_M._VERSION = ‘0.01’ 3local function tryExec(prod,gray) 4local redis = require ‘redis’ 5local red = redis.connect(‘172.20.33.5’, 6379, 0.2) 6token = ngx.req.get_headers()["userId"] 7if token == nil then 8 return prod 9end 10 11local temp , userId = token:match("([^,]+)-([^,]+)") 12local res = red:get(userId) 13if not res then 14 return prod 15end 16 17if res == ngx.null then 18 return prod 19else 20 return gray 21end 22end 23 24 25function _M.execByToken(prod,gray) 26local ok , msg = pcall(tryExec,prod,gray) 27if ok then 28ngx.exec(msg) 29else 30ngx.exec(prod) 31end 32end 33return _M
Nginx中添加配置:
1location /data { 2 set $target ''; 3 set $header_host $host; 4 access_by_lua ' 5 package.path = "/usr/local/nginx/conf/lua/?.lua" 6 local sdmk = require "sdmk" 7 ngx.var.target=sdmk.tryExec("dmkdataProd","dmkdataK8s") 8 if ngx.var.target == "dmkdataK8s" then 9 ngx.var.header_host = "nginx-dm-talkingdata-datamarket-gateway-k8s.dm.svc.sc.tendcloud.com" 10 end 11 '; 12 13 proxy_http_version 1.1; 14 proxy_set_header Connection ""; 15 proxy_set_header Host $header_host; 16 proxy_set_header X-Real-IP $remote_addr; 17 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 18 proxy_set_header X-Forwarded-Server $host; 19 proxy_pass http://$target; 20 }
这里须要注意的是须要在header_host中将域名带到K8s环境中。
4、注意事项
1.容器优雅关闭的问题
容器是否可以进行优雅的关闭,接收到SIGTERM信号,而非直接杀掉,从而使得应用程序中可以有效的处理数据、释放资源等,不然就有可能出现数据丢失等现象。
2.数据库访问限制
生产环境的数据库都有相应的访问限制,当前SDMK的数据库访问限制,是经过受权IP段形式来实现的。
3.Nginx-ingress-Controller的性能问题
不管采用共享Ingress的方式,仍是采用独占Ingress方式,都须要在上生产环境以前进行压测一番,看是否可以知足系统的要求。
4.资源限制问题
5.HPA的设置条件,应当根据应用程序特定需求,来设置弹性伸缩条件
6.特殊镜像新需求
7.建议容器探针必定要加
8.Nginx引流访问K8s环境域名
本文做者:TalkingData 杨双亮