Kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部署,调度和节点集群间扩展。如果你曾经用过Docker容器技术部署容器,那么可以将Docker看成Kubernetes内部使用的低级别组件。Kubernetes不仅仅支持Docker,还支持Rocket,这是另一种容器技术。
使用Kubernetes可以:
  自动化容器的部署和复制
  随时扩展或收缩容器规模
  将容器组织成组,并且提供容器间的负载均衡
  很容易地升级应用程序容器的新版本
  提供容器弹性,如果容器失效就替换它,等等…
核心概念介绍
在这里插入图片描述
Master
集群拥有一个K8s Master(紫色方框)。K8s Master提供集群的独特视角,并且拥有一系列组件,比如K8s API Server。API Server提供可以用来和集群交互的REST 接口。Master节点包括用来创建和复制Pod的Replication Controller。
 K8s API Server,提供了HTTP Rest接口的关键服务进程,是集群中所有资源增、删、改操作的唯一入口,也是控制集群的唯一入口。
 K8s Controller Manager,是集群中所有资源对象的运行指挥中心。
 K8s Scheduler,是负责调度Pod资源的进程。
 etcd服务,提供集群中所有资源对象配置的持久化。

Node
Node作为集群中的工作节点,运行真正的应用程序,在Node上K8s管理的最小运行单元是Pod。Node上运行着K8s的Kubelet、kube-proxy服务进程,这些服务进程负责Pod的创建、启动、监控、重启、销毁、以及实现软件模式的负载均衡。节点(上图橘色方框)是物理或者虚拟机器,作为K8s worker,通常称为Minion。每个节点都运行如下K8s关键组件:
 Kubelet:是主节点代理。
 Kube-proxy:Service使用其将链接路由到Pod,如上文所述。
 Docker或Rocket:K8s使用的容器技术来创建容器。

Pod
Pod是K8s最基本的操作单元,包含一个或多个紧密相关的容器,一个Pod可以被一个容器化的环境看作应用层的“逻辑宿主机”;一个Pod中的多个容器应用通常是紧密耦合的,Pod在Node上被创建、启动或者销毁;每个Pod里运行着一个特殊的被称之为Pause的容器,其他容器则为业务容器,这些业务容器共享Pause容器的网络栈和Volume挂载卷,因此他们之间通信和数据交换更为高效,在设计时我们可以充分利用这一特性将一组密切相关的服务进程放入同一个Pod中。
一个Pod中的应用容器共享同一组资源:
• PID命名空间:Pod中的不同应用程序可以看到其他应用程序的进程ID;
• 网络命名空间:Pod中的多个容器能够访问同一个IP和端口范围;
• IPC命名空间:Pod中的多个容器能够使用System IPC或POSIX消息队列进行通信;
• UTS命名空间:Pod中的多个容器共享一个主机名;
• Volumes(共享存储卷):Pod中的各个容器可以访问在Pod级别定义的Volumes;
Pod的生命周期通过Replication Controller来管理,通过模板进行定义,然后分配到一个Node上运行,在Pod所包含容器运行结束后,Pod结束。K8s为Pod设计了一套独特的网络配置,包括:为每个Pod分配一个IP地址,使用Pod名作为容器间通信的主机名等

RC(Replication Controller)
RC就是复制控制器,新一代的RC叫RS(Replication Set)其主要功能如下:
 确保pod数量:RC用来管理正常运行Pod数量,一个RC可以由一个或多个Pod组成,在RC被创建后,系统会根据定义好的副本数来创建Pod数量。在运行过程中,如果Pod数量小于定义的,就会重启停止的或重新分配Pod,反之则杀死多余的。
 确保pod健康:当pod不健康,运行出错或者无法提供服务时,RC也会杀死不健康的pod,重新创建新的。
 弹性伸缩 :在业务高峰或者低峰期的时候,可以通过RC动态的调整pod的数量来提高资源的利用率。同时,配置相应的监控功能(Hroizontal Pod Autoscaler),会定时自动从监控平台获取RC关联pod的整体资源使用情况,做到自动伸缩。
 滚动升级:滚动升级为一种平滑的升级方式,通过逐步替换的策略,保证整体系统的稳定,在初始化升级的时候就可以及时发现和解决问题,避免问题不断扩大。

Labels
Labels以key/value的形式附加到各种对象上,如Pod、Service、RC、Node等,以识别这些对象,管理关联关系等,如Service和Pod的关联关系,有了这种关联关系,就可以通过选择器(Selector)来进行筛选那个服务和那个pod进行关联。

Label selectors
通过label selectors,用户可以识别一些对象,它是K8s的核心grouping primitive。
 equality-based
 Set-based requirement
部署(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就越多,高可用节点也随之增多.

服务暴露方式
首先Service通常只是集群内部有效,从集群外部是无法访问到的。在kubenetes的世界里,服务要对集群外暴露,通常有这么几种方式:
 Cluseter IP:集群内的私有IP,这是默认值
 NodePort Service
其实质上是通过在集群的每个node上暴漏一个端口,然后将这个端口映射到某个具体的service来实现的,虽然每个node的端口很多,但由于安全性和易用性(服务多了就乱了,端口冲突),实际使用的并不多。
在这里插入图片描述
 LoadBalancer Service
是kubenetes深度结合云平台的一个组件,当使用LoadBalancer service暴露服务时,实际上是通过底层平台申请创建一个负载均衡器来向外暴露服务的。这种方式多数应用于云平台上来使用。
 Ingress
通过Ingress用户可以实现使用nginx等开源的反向代理负载均衡器实现对外暴露服务。它包含有三个组件:反向代理负载均衡器、Ingress Controller 、Ingress。
 反向负载均衡器,简单来说就是nginx、apache等,部署方式也较为自由,可以RC,Deployments、DaemonSet
 Ingress Controller:可以理解为是个监视器,Ingress Crontroller通过不断地跟Kubernetes API打交道,实时感知后端service、pod的变化,比如增加或者减少pod,service的增加或者减少等,当这些信息发生变化时,Ingress Controller再结合上下文的Ingress生成配置,然后更新到反向代理负载均衡器,并重新reload配置,达到服务发现的作用
 Ingress:简单的说就是个规则定义;比如那个域名对应那个service,即那个域名请求进来,转发给那个service。
在这里插入图片描述

我们部署就是采用的Ingress这种方式。在k8s集群中,Ingress 是授权入站连接到达集群服务的规则集合,为其提供七层负载均衡能力,从而可以通过 Ingress 配置提供外部可访问的 URL、负载均衡、SSL、基于名称的虚拟主机等。在部署的时候主要考虑了以下两种部署方式:
共享Ingress
在这里插入图片描述
如上述部署架构图,所有的服务共享一个Ingress实例,该实例作为集群所有服务的流量入口,但问题就是,一旦流量入口出现问题,例如性能问题、可用性问题等,就会影响集群内所有服务的可用性。
独占Ingress
在这里插入图片描述
如上述部署架构图,由多个独占 Ingress 实例组成统一接入层承载集群入口流量,同时可依据后端业务流量水平扩缩容 Ingress 节点。当然如果前期的集群规模并不大,也可以采用将 Ingress 服务与业务应用混部的方式,但建议进行资源限制和隔离。
作为集群流量入口,Ingress的高可靠性显得尤为重要,那么,如何提高其高可靠性?其实高可靠性首先要解决的就是单点故障问题,一般常用的是采用多副本部署的方式,在 Kubernetes 集群中部署高可靠Ingress,因此我们采用独占Ingress节点的方式,以避免业务应用与 Ingress 服务发生资源争抢,以及单点的问题。

弹性伸缩介绍

在这里插入图片描述
众所周知,在k8s中,pod是最基础的调度单位,多个pod 可以组成一个集合,这个集合向外提供服务。这时候,有以下两种情形需要关注:
1)集合中的pod可能会由于某种原因fail,这时候需要某种机制能够创建新的Pod以确保有足够数量的pod在运行。
2)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的指标信息,再通过控制器的计算,从而实现弹性伸缩.当前主要的指标分为以下三种:
 Resource:pod的资源指标,例如cpu、mem等信息
 Custom: 自定义的指标信息,例如:ingress的QPS、应用在线活跃人数等
 External :集群指标的监控指标,通常由云厂商提供

计算的算法
desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]
例如:currentMetricValue 为 200m, desiredMetricValue 为100m ,那么副本的个数就会翻倍,前提是不超过你设置的最大值,如果超过,就把最大值作为当前的目标副本数目。如果算出来的值为0.5这类,就大于等于该数的最小整数。

这里要说明:
在K8s v1.1中首次引入了HPA特性,其第一个版本基于观察到的CPU利用率,后续版不断发展,也支持了基于内存使用。
在K8s 1.6中引入了一个新的API自定义指标API,它允许HPA访问任意指标。
在K8s1.7引入了聚合层,允许第三方应用程序通过注册为API附加组件来扩展K8s API。
自定义指标API以及聚合层使得像Prometheus这样的监控系统可以向HPA控制器公开特定于应用程序的指标。
实践部分:
在这里插入图片描述

需要在Deployment 的yaml文件中定义每个APP使用的资源情况。

Deployment的yaml文件:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: dm-sdmk-kafka-service
  namespace: dm
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
       maxSurge: 1
       maxUnavailable: 1
  template:
    metadata:
      labels:
        app: dm-sdmk-kafka-service
    spec:
      containers:
      - image: bj-yh-oam-docker-hub-001.tendcloud.com/library/dm-sdmk-kafka-service-k8s:Build_1
        imagePullPolicy: Always
        name : dm-sdmk-kafka-service
        resources:
          requests:
            cpu: 1000m
            memory: 2048Mi
          limits:
            cpu: 1000m
            memory: 2048Mi
        ports:
        - containerPort: 80
        env:
          - name: JAVA_OPTS
            value: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
        readinessProbe:
           tcpSocket:
             port: 80
           initialDelaySeconds: 60
           timeoutSeconds: 10

HorizontalPodAutoscaler 的Yaml文件的例子:

---

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: dm-sdmk-kafka-service
  namespace: dm
spec:
  scaleTargetRef:
    apiVersion: extensions/v1beta1
    kind: Deployment
    name: dm-sdmk-kafka-service
  minReplicas: 3
  maxReplicas: 5
  metrics:
  - type: Resource
    resource:
      name: cpu
      targetAverageUtilization: 50
  - type: Resource
    resource:
      name: memory
      targetAverageValue: 1536Mi

引流实践介绍
当前k8s环境下的sdmk已经部署完毕,并经过简单测试,处于可用状态,现在想将特定用户的流量引入到k8s环境下的sdmk,从而使其能够承担起相应的业务流量,并逐渐的将物理机上的流量引入到k8s环境下,最终达到去除物理机的状态。

实现目标
在这里插入图片描述
当前k8s环境下的sdmk与物理机上的sdmk共用物理机上的存储、缓存以及相应消息队列,故当前的迁移设想是:
 将k8s环境下的sdmk也挂在nginx下面,分一特定用户(探针用户)的流量到k8s环境
保持k8s环境与物理机环境并行运行一段时间,如果k8s环境没有问题,则增加到k8s环境的用户个数
 如果有问题,则及时的去除探针用户的流量,使其仍旧打到物理机上即可
 如果k8s环境长时间运行没有任何问题,才可以将流量从物理机上完全切换到k8s环境
物理机上部署
在这里插入图片描述
原理物理机上的部署,是通过物理机上nginx做负载均衡的,下面挂在多个物理机上Gateway节点。
并行部署
在这里插入图片描述
K8s环境与物理机环境并行运行期间的部署情况是这样子的:将k8s环境的入口域名以Locaation的方式也挂在nginx下,通过nginx来将流量区分开来,哪些用户的流量打到k8s环境,哪些用户的流量打到物理机环境。
实现机制
在这里插入图片描述
实现机制:
1、 在Nginx上添加特定的lua脚本,通过lua脚本连接redis
2、 Lua脚本获取打在nginx上每个请求中的userId
3、 再和Redis中配置的用户userId进行匹配,如果是,则进入k8s环境;如果不是,则进入到物理机环境,从而将流量部分引入到k8s环境
4、并行运行一段时间后,如果k8s环境没有问题,再将所有流量引入到k8s环境。

注意事项介绍
 优雅关闭的问题
容器是否能够进行优雅的关闭,接收到SIGTERM信号,而非直接杀掉,从而使得应用程序中能够有效的处理数据、释放资源等,否则就有可能出现数据丢失等现象
 数据库访问限制
 数据库访问限制,是通过授权IP段形式
 Nginx-ingress-Controller的性能问题
如果你才用的共享Ingress的方式,就需要测试一下,这种方式的性能,是否能够满足系统需求。如果采用独占Ingress的方式,也最好上线之前进行测试一下。
 如果设置pod的资源限制,应用程序能否识别容器的资源限制
 是否开启了CGroup资源感知: 指出了JAVASE8u131 和jdk9支持了对容器资源限制的感知能力
https://blogs.oracle.com/java-platform-group/java-se-support-for-docker-cpu-and-memory-limits?spm=a2c4e.11153940.blogcont562440.7.35c7627aTPkKvX
 设置的资源限制是否满足应用程序的基本需要
 如果不满足就会出现,容器起来,杀死,起来杀死的死循环
 HPA的设置条件,应当根据应用程序特定需求,来设置弹性伸缩条件
 计算密集型,那就以CPU使用量为条件
 内存密集型,那就以内存使用量为条件
 二者兼之的,都设置
 自定义metric:例如消息队列,处于等待状态的消息数量数
 特殊镜像新需求
可能运维同事,只提供标准的镜像,如果有特殊需求,就需要自己在Dockerfile中进行一定的处理。例如:
 Springboot2 需要jetty9.4 ,jdk1.8以上镜像
 生成文档应用程序,需要安装特殊的中文字体库
 Jar包冲突,App应用程序,与jetty的jar包冲突
 建议容器探针一定要加
 Liveness probe: 来确定何时重启容器。例如:当应用程序处于运行状态但无法做进一步操作,liveness 探针将捕获到deadlock,就会重启该状态下的容器,使应用程序存在bug得情况下,依然能够继续运行下去。
 Readiness probe: 来确定容器及服务是否已经就绪,从而可以接受流量。只有当pod的状态,被kubelet认定为就绪状态时,才会接受流量。也即控制哪些pod应该作为service的后端。如果pod处于非就绪状态,那么它们将会被从service得load balancer中移除。
 Nginx引流访问k8s环境域名
 需要将域名带过去
 并且添加DNS解析,因为k8s生成的域名,对应的IP是动态变化的

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐