【K8S】kubernetes 一些概念的介绍

一、kubernetes的前生今生

Kubernetes(简称k8s)是Google在2014年6月开源的一个容器集群管理系统,使用Go语言开发,用于管理云平台中多个主机上的容器化的应用,Kubernetes的目标是让部署容器化的应用简单并且高效,Kubernetes提供了资源调度、部署管理、服务发现、扩容缩容、监控,维护等一整套功能。努力成为跨主机集群的自动部署、扩展以及运行应用程序容器的平台。 它支持一系列容器工具, 包括Docker等。

从Borg到Kubernetes

在Docker 作为高级容器引擎快速发展的同时,Google也开始将自身在容器技术及集群方面的积累贡献出来。在Google内部,容器技术已经应用了很多年,Borg系统运行管理着成千上万的容器应用,在它的支持下,无论是谷歌搜索、Gmail还是谷歌地图,可以轻而易举地从庞大的数据中心中获取技术资源来支撑服务运行。

Borg是集群的管理器,在它的系统中,运行着众多集群,而每个集群可由成千上万的服务器联接组成,Borg每时每刻都在处理来自众多应用程序所提交的成百上千的Job, 对这些Job进行接收、调度、启动、停止、重启和监控。

Borg提供了3大好处:

1)隐藏资源管理和错误处理,用户仅需要关注应用的开发。

2) 服务高可用、高可靠。

3) 可将负载运行在由成千上万的机器联合而成的集群中。

作为Google的竞争技术优势,Borg理所当然的被视为商业秘密隐藏起来,但当Tiwtter的工程师精心打造出属于自己的Borg系统(Mesos)时, Google也审时度势地推出了来源于自身技术理论的新的开源工具。

2014年6月,谷歌云计算专家埃里克·布鲁尔(Eric Brewer)在旧金山的发布会为这款新的开源工具揭牌,它的名字Kubernetes在希腊语中意思是船长或领航员,这也恰好与它在容器集群管理中的作用吻合,即作为装载了集装箱(Container)的众多货船的指挥者,负担着全局调度和运行监控的职责。

虽然Google推出Kubernetes的目的之一是推广其周边的计算引擎(Google Compute Engine)和谷歌应用引擎(Google App Engine)。但Kubernetes的出现能让更多的互联网企业可以享受到连接众多计算机成为集群资源池的好处。

Kubernetes对计算资源进行了更高层次的抽象,通过将容器进行细致的组合,将最终的应用服务交给用户。Kubernetes在模型建立之初就考虑了容器跨机连接的要求,支持多种网络解决方案,同时在Service层次构建集群范围的SDN网络。其目的是将服务发现和负载均衡放置到容器可达的范围,这种透明的方式便利了各个服务间的通信,并为微服务架构的实践提供了平台基础。而在Pod层次上,作为Kubernetes可操作的最小对象,其特征更是对微服务架构的原生支持。

Kubernetes项目来源于Borg,可以说是集结了Borg设计思想的精华,并且吸收了Borg系统中的经验和教训。

Kubernetes作为容器集群管理工具,于2015年7月22日迭代到 v 1.0并正式对外公布,这意味着这个开源容器编排系统可以正式在生产环境使用。与此同时,谷歌联合Linux基金会及其他合作伙伴共同成立了CNCF基金会( Cloud Native Computing Foundation),并将Kuberentes 作为首个编入CNCF管理体系的开源项目,助力容器技术生态的发展进步。Kubernetes项目凝结了Google过去十年间在生产环境的经验和教训,从Borg的多任务Alloc资源块到Kubernetes的多副本Pod,从Borg的Cell集群管理,到Kubernetes设计理念中的联邦集群,在Docker等高级引擎带动容器技术兴起和大众化的同时,为容器集群管理提供独了到见解和新思路。

二、kubernetes的特点

  • 可移植:支持公有云,私有云,混合云,多重云(multi-cloud)

  • 可扩展::模块化, 插件化, 可挂载, 可组合

  • 自动化: 自动部署,自动重启,自动复制,自动伸缩/扩展,自我修复

  • 服务发现和负载均衡

Kubernetes一个核心的特点就是自动化,能够自主的管理容器来保证云平台中的容器按照用户的期望状态运行着(比如用户想让apache一直运行,用户不需要关心怎么去做,Kubernetes会自动去监控,然后去重启,新建,总之,让apache一直提供服务),管理员可以加载一个微型服务,让规划器来找到合适的位置,同时,Kubernetes也系统提升工具以及人性化方面,让用户能够方便的部署自己的应用(就像canary deployments)。

现在Kubernetes着重于不间断的服务状态(比如web服务器或者缓存服务器)和原生云平台应用(Nosql),在不久的将来会支持各种生产云平台中的各种服务,例如,分批,工作流,以及传统数据库。

所有Kubernetes中的资源,比如Pod,都通过一个叫URI的东西来区分,这个URI有一个UID,URI的重要组成部分是:对象的类型(比如pod),对象的名字,对象的命名空间,对于特殊的对象类型,在同一个命名空间内,所有的名字都是不同的,在对象只提供名称,不提供命名空间的情况下,这种情况是假定是默认的命名空间。UID是时间和空间上的唯一。

三、kubernetes的相关概念

使用Kubernetes,需要对pods、services、replication controller等概念了然于心。

1、Pod

Pod是最小部署单元,一个Pod由一个或多个容器组成,Pod中容器共享存储和网络,在同一台Docker主机上运行。每个Pod都会包含一个 “根容器”,还会包含一个或者多个紧密相连的业务容器。

  • Pod运行在节点Node中;

  • Pod是对容器的封装,是k8s最小的调度单元,也是Kubernetes最重要的基本概念;

  • Pause容器简述:每个Pod都有一个特殊的被称为“根容器”的Pause容器,Pause容器对应的镜像属于Kubernetes平台的一部分。Kubernetes设计出全新Pod概念的原因如下。原因一、在一组容器作为一个单位的情况下,我们难以简单地对整组容器的状态进行判断。如果其中一个容器死亡,此时该去定义整组容器都死亡呢?还是定义N/M的死亡率呢?通过引入与业务无关并且不容易死亡的Pause容器作为Pod的根容器,以它的状态代表整组容器的状态,可以简单的解决整个问题。原因二、Pod里的多个业务容器共享Pause容器IP,共享Pause容器挂接的Volume,这样既简化了密切关联的业务容器之间的通信问题,也很好地解决了它们之间的文件共享问题。

  • Kubernetes为每个Pod都分配了唯一的IP地址,称之为Pod IP,一个Pod里的多个容器共享Pod IP。Kubernetes要求底层网络支持集群内任意两个Pod之间的TCP/IP直接通信,因此一个Pod容器与另外主机上的Pod容器能够直接通信。

pod 清单:

apiVersion: v1         # Pod版本号为v1
kind: Pod              # 使用的资源类型为Pod
metadata:    
  name: string         # 当前Pod的名称,名字唯一
  namespace: string    # Pod所属的命名空间,默认值为default
  labels:              # 自定义标签列表
    - name: string     # 当前pod标签为“name=string”
  annotations:         # 自定义注解列表
    - name: string
spec:                  # Pod中的详细定义
  containers:          # Pod的容器列表
  - name: string       # 容器名字
    image: string      # 容器镜像
    imagePullPolicy:          # 容器的镜像拉取策略
    command: [string]         # 容器的启动命令列表
    args: [string]            # 容器的启动命令参数列表
    workingDir: String        容器的工作目录  
    volumeMounts:             # 挂载到容器内部的存储卷配置
    - name: String            # 引用Pod定义的共享存储卷的名,需用Pod.spec.volumes[]部分定义的卷名
      mountPath: String       # 存储卷在容器内的绝对路径
      readOnly: boolean       # 设置为只读模式,默认为false
  volumes:                    # 在该Pod上定义共享存储卷列表
  - name: String              # 共享存储卷名称,必须字母小写
    emptyDir: { }             # 类型为emptyDir的存储卷,与Pod同生命周期的一个临时目录,为空值
    hostPath: String          # 类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
    path: String              # Pod所在宿主机的目录,将被用于同期中Mount的目录
  ports:                      容器需要暴露的端口库号列表
  - name: String              端口号名称
    containerPort: Int        容器需要监听的端口号
    hostPort: Int             可选,容器所在主机需要监听的端口号,默认与Container相同
  env:                # 容器运行前需设置的环境变量列表
  - name: String         # 环境变量名称
    value: String        # 环境变量的值
  resources:              # 资源限制和请求的设置
    limits:              # 资源限制的设置
      cpu: String          # Cpu的限制,单位为Core数,将用于docker run --cpu-shares参数,如果整数后跟m,表示占用权重,1Core=1000m
      memory: String        # 内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
    requests:             # 资源请求的设置
      cpu: string          # CPU请求,容器启动的初始可用数量
      memory: string        # 内存请求,容器启动的初始可用数量
  livenessProbe:         # 对Pod内容器健康检查设置,当探测无响应几次后将自动重启该容器,检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可。
    exec:             # 对Pod容器内检查方式设置为exec方式
      command: [String]   #  exec方式需要制定的命令或脚本
    httpGet:          #  对Pod容器内检查方式设置为HttpGet方式,需要指定path、port
      path: String      #  网址URL路径(去除对应的域名或IP地址的部分)
      port: Int            #  对应端口
      host: String          # 域名或IP地址
      schema: String         # 对应的检测协议,如http
      HttpHeaders:           # 指定报文头部信息
      - name: String
        value: String
    tcpSocket:               # 对Pod容器内检查方式设置为tcpSocket方式
      port: Int
    initialDelaySeconds: Int  # 容器启动完成后首次探测的时间,单位为秒
    timeoutSeconds: Int       # 对容器健康检查探测等待响应的超时时间,单位为秒,默认为1秒
    periodSeconds: Int        # 对容器监控检查的定期探测时间设置,单位为秒,默认10秒一次
    successThreshold: Int     # 探测几次成功后认为成功
    failureThreshold: Int    # 探测几次失败后认为失败
    securityContext:
      privileged: false
  restartPolicy:          # Pod的重启策
  nodeSelector:              设置NodeSelector表示将该Pod调度到包含这个label的node上,以Key:Value的格式指定
    Key: Value               调度到指定的标签Node上
  imagePullSecrets:          Pull镜像时使用的secret名称,以Key:SecretKey格式指定
  - name: String
  hostNetwork: false         是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络

  secret:                    类型为Secret的存储卷,挂载集群与定义的Secret对象到容器内部
    secretname: String
    items:                   当仅需挂载一个Secret对象中的指定Key时使用
    - key: String
     path: String
  configMap:               # 类型为ConfigMap的存储卷,挂载预定义的ConfigMap对象到容器内部
    name: String
    items:                 # 当仅需挂载一个ConfigMap对象中的指定Key时使用
    - key: String
      path: String

2、Service

Service一个应用服务抽象,定义了Pod逻辑集合和访问这个Pod集合的策略。Service代理Pod集合对外表现是为一个访问入口,分配一个集群IP地址,来自这个IP的请求将负载均衡转发后端Pod中的容器。Service通过LableSelector选择一组Pod提供服务。

在K8s集群中,客户端需要访问的服务就是Service对象。每个Service会对应一个集群内部有效的虚拟IP,集群内部通过虚拟IP访问一个服务。在K8s集群中微服务的负载均衡是由Kube-proxy实现的。Kube-proxy是K8s集群内部的负载均衡器。它是一个分布式代理服务器,在K8s的每个节点上都有一个;这一设计体现了它的伸缩性优势,需要访问服务的节点越多,提供负载均衡能力的Kube-proxy就越多,高可用节点也随之增多。与之相比,我们平时在服务器端做个反向代理做负载均衡,还要进一步解决反向代理的负载均衡和高可用问题。

“Service微服务”,kubernetes中的核心。通过分析、识别并建模系统中的所有服务为微服务,最终系统有多个提供不同业务能力而又彼此独立的微服务单元所组成,服务之间通过TCP/IP进行通信。每个Pod都会被分配一个单独的IP地址,而且每个Pod都提供了一个独立的Endpoint以被客户端访问。

客户端想要访问到Pod中的服务需要 部署负载均衡器,为Pod开启对外服务端口,将Pod的Endpoint列表加入转发列表中,客户端通过负载均衡器的对外IP+Port来访问此服务。每个Service都有一个全局唯一的虚拟ClusterIP,这样每个服务就变成了具备唯一IP地址的“通信节点”,服务调用就变成了最基础的TCP网络通信问题。

  • 在K8S的世界里,虽然每个Pod都会被分配一个单独的IP地址,但这个IP地址会随着Pod的销毁而消失

  • Service (服务)就是用来解决这个问题的核心概念

  • 一个Service可以看作- -组提供相同服务的Pod的对外访问接口

  • Service作用于哪些Pod是通过标签选择器来定义的

3、访问方式

A、ClusterIP模式

用于为集群内Pod访问时,提供的固定访问地址,默认是自动分配地址,可使用ClusterIP关键字指定固定IP.

B、NodePort模式

用于为集群外部访问Service后面Pod提供访问接入端口.,这种类型的service工作流程为:Client----->NodeIP:NodePort----->ClusterIP:ServicePort----->PodIP:ContainerPort

C、LoadBalancer模式

用于当K8s运行在一个云环境内时,若该云环境支持LBaaS,则此类型可自动触发创建一个软件负载均衡器用于对Service做负载均衡调度.
因为外部所有Client都访问一个NodeIP,该节点的压力将会很大, 而LoadBalancer则可解决这个问题。
而且它还直接动态监测后端Node是否被移除或新增了,然后动态更新调度的节点数。

D、ExternalName模式

用于将集群外部的服务引入到集群内部,在集群内部可直接访问来获取服务。它的值必须是 FQDN, 此FQDN为集群内部的FQDN, 即: ServiceName.Namespace.Domain.LTD.4

4、Kubernetes Ingress

Ingress 是 Kubernetes 中非常重要的外网流量入口。在Kubernetes中所推荐的默认值为Nginx Ingress,为了与后面Nginx 提供的商业版 Ingress 区分开来,我就称它为Kubernetes Ingress。

5、Volume

数据卷,是Pod中能够被多个容器访问的共享目录。定义在Pod之上,被一个Pod里的多个容器挂载到具体的文件目录之下;与Pod生命周期相同。可以让一个Pod里的多个容器共享文件、让容器的数据写到宿主机的磁盘上或者写文件到 共享存储中

Kubernetes提供了众多的volume类型,现在列出一部分

emptyDir
hostPath
gcePersistentDisk
awsElasticBlockStore
nfs
iscsi
flocker
glusterfs
rbd
cephfs
gitRepo
secret
persistentVolumeClaim
downwardAPI
azureFileVolume
azureDisk
vsphereVolume
Quobyte

下面是比较常用的两个

emptyDir

emptyDir的生命周期与所属的pod相同。pod删除时,其emptyDir中的数据也会被删除。

emptyDir类型的volume在pod分配到node上时被创建,kubernetes会在node上自动分配 一个目录,因此无需指定宿主机node上对应的目录文件。

emptyDir Volume主要用于某些应用程序无需永久保存的临时目录,多个容器的共享目录等

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - image: test-webserver
    name: test-container
    volumeMounts:
    - name: cache-volume
      mountPath: /cache
  volumes:
  - name: cache-volume
    emptyDir: {}

hostPath

pod删除或者是调度到另外一个Node,原先Node上的存储卷还在。

hostPath Volume为pod挂载宿主机上的目录或文件,使得容器可以使用宿主机的文件系统进行存储。缺点是,在k8s中,pod都是动态在各node节点上调度。当一个pod在当前node节点上启动并通过hostPath存储了文件到本地以后,下次调度到另一个节点上启动时,就无法使用在之前节点上存储的文件。

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - image: test-webserver
    name: test-container
    volumeMounts:
    - name: test-volume
      mountPath: /www
  volumes:
  - name: test-volume
    hostPath:
      path: /data

6、持久存储卷(Persistent Volume,PV)和持久存储卷声明(Persistent Volume Claim,PVC)

PV和PVC使得K8s集群具备了存储的逻辑抽象能力,使得在配置Pod的逻辑里可以忽略对实际后台存储技术的配置,而把这项配置的工作交给PV的配置者,即集群的管理者。存储的PV和PVC的这种关系,跟计算的Node和Pod的关系是非常类似的;PV和Node是资源的提供者,根据集群的基础设施变化而变化,由K8s集群管理员配置;而PVC和Pod是资源的使用者,根据业务服务的需求变化而变化,有K8s集群的使用者即服务的管理员来配置。

7、Namespace

命名空间将对象逻辑上分配到不同Namespace,可以是不同的项目、用户等区分管理,并设定控制策略,从而实现多租户。命名空间也称为虚拟集群。

8、Label

标签用于区分对象(比如Pod、Service);每个对象可以有多个标签,通过标签关联对象。是一个key=value的键值对,其中key与value由用户自己指定。可以附加到各种资源对象上,一个资源对象可以定义任意数量的Label。可以通过LabelSelector(标签选择器)查询和筛选资源对象。

  • 给资源打上标签后,可以使用标签选择器过滤指定的标签

  • 标签选择器目前有两个:基于等值关系(等于、不等于)和基于集合关系(属于、不属于、存在)

  • 许多资源支持内嵌标签选择器字段

9、ReplicationController

Replication Controller声明某个Pod的副本数在任意时刻都符合某个预期值。定义包含如下:

(1)Pod期待的副本数(replicas)

(2)用于筛选目标Pod的Label Selector

(3)当Pod副本数小于期望时,用于新的创建Pod的模板template

(4)通过改变RC里的Pod副本数量,可以实现Pod的扩容或缩容功能

(5)通过改变RC里Pod模板中的镜像版本,可以实现Pod的滚动升级功能

10、ReplicaSet

下一代ReplicationController。确保任何给定时间指定的Pod副本数量,并提供声明式更新等功能。

RC与RS唯一区别就是lableselector支持不同,RS支持新的基于集合的标签,RC仅支持基于等式的标签。

在kubernetes1.2的时候,RC就由Replication Controller升级成Replica Set,“下一代RC”。命令兼容适用,Replica Set主要被Deployment这个更高层的资源对象所使用,从而形成一套Pod创建、删除、更新的编排机制。当我们使用Deployment时,无需关心它是如何创建和维护ReplicaSet的,这一切是自动发生的。

11、Deployment

Deployment是一个更高层次的API对象,它管理ReplicaSets和Pod,并提供声明式更新等功能。

官方建议使用Deployment管理ReplicaSets,而不是直接使用ReplicaSets,这就意味着可能永远不需要直接操作ReplicaSet对象。

12、StatefulSet

StatefulSet适合持久性的应用程序,有唯一的网络标识符(IP),持久存储,有序的部署、扩展、删除和滚动更新。

13、DaemonSet

DaemonSet确保所有(或一些)节点运行同一个Pod。当节点加入Kubernetes集群中,Pod会被调度到该节点上运行,当节点从集群中移除时,DaemonSet的Pod会被删除。删除DaemonSet会清理它所有创建的Pod。

14、Job

一次性任务,运行完成后Pod销毁,不再重新启动新容器。还可以任务定时运行。

15、有状态服务集(PetSet)

K8s在1.3版本里发布了Alpha版的PetSet功能。在云原生应用的体系里,有下面两组近义词;

第一组是无状态(stateless)、牲畜(cattle)、无名(nameless)、可丢弃(disposable);

第二组是有状态(stateful)、宠物(pet)、有名(having name)、不可丢弃(non-disposable)。

RC和RS主要是控制提供无状态服务的,其所控制的Pod的名字是随机设置的,一个Pod出故障了就被丢弃掉,在另一个地方重启一个新的Pod,名字变了、名字和启动在哪儿都不重要,重要的只是Pod总数;而PetSet是用来控制有状态服务,PetSet中的每个Pod的名字都是事先确定的,不能更改。PetSet中Pod的名字的作用,是关联与该Pod对应的状态。

对于RC和RS中的Pod,一般不挂载存储或者挂载共享存储,保存的是所有Pod共享的状态,Pod像牲畜一样没有分别(这似乎也确实意味着失去了人性特征);对于PetSet中的Pod,每个Pod挂载自己独立的存储,如果一个Pod出现故障,从其他节点启动一个同样名字的Pod,要挂载上原来Pod的存储继续以它的状态提供服务。

适合于PetSet的业务包括数据库服务MySQL和PostgreSQL,集群化管理服务Zookeeper、etcd等有状态服务。PetSet的另一种典型应用场景是作为一种比普通容器更稳定可靠的模拟虚拟机的机制。传统的虚拟机正是一种有状态的宠物,运维人员需要不断地维护它,容器刚开始流行时,我们用容器来模拟虚拟机使用,所有状态都保存在容器里,而这已被证明是非常不安全、不可靠的。使用PetSet,Pod仍然可以通过漂移到不同节点提供高可用,而存储也可以通过外挂的存储来提供高可靠性,PetSet做的只是将确定的Pod与确定的存储关联起来保证状态的连续性。PetSet还只在Alpha阶段,后面的设计如何演变,我们还要继续观察。

16、集群联邦(Federation)

K8s在1.3版本里发布了beta版的Federation功能。在云计算环境中,服务的作用距离范围从近到远一般可以有:同主机(Host,Node)、跨主机同可用区(Available Zone)、跨可用区同地区(Region)、跨地区同服务商(Cloud Service Provider)、跨云平台。K8s的设计定位是单一集群在同一个地域内,因为同一个地区的网络性能才能满足K8s的调度和计算存储连接要求。而联合集群服务就是为提供跨Region跨服务商K8s集群服务而设计的。

每个K8s Federation有自己的分布式存储、API Server和Controller Manager。用户可以通过Federation的API Server注册该Federation的成员K8s Cluster。当用户通过Federation的API Server创建、更改API对象时,Federation API Server会在自己所有注册的子K8s Cluster都创建一份对应的API对象。在提供业务请求服务时,K8s Federation会先在自己的各个子Cluster之间做负载均衡,而对于发送到某个具体K8s Cluster的业务请求,会依照这个K8s Cluster独立提供服务时一样的调度模式去做K8s Cluster内部的负载均衡。而Cluster之间的负载均衡是通过域名服务的负载均衡来实现的。

所有的设计都尽量不影响K8s Cluster现有的工作机制,这样对于每个子K8s集群来说,并不需要更外层的有一个K8s Federation,也就是意味着所有现有的K8s代码和机制不需要因为Federation功能有任何变化。

17、密钥对象(Secret)

Secret是用来保存和传递密码、密钥、认证凭证这些敏感信息的对象。使用Secret的好处是可以避免把敏感信息明文写在配置文件里。在K8s集群中配置和使用服务不可避免的要用到各种敏感信息实现登录、认证等功能,例如访问AWS存储的用户名密码。为了避免将类似的敏感信息明文写在所有需要使用的配置文件中,可以将这些信息存入一个Secret对象,而在配置文件中通过Secret对象引用这些敏感信息。这种方式的好处包括:意图明确,避免重复,减少暴漏机会。

18、用户帐户(User Account)和服务帐户(Service Account)

顾名思义,用户帐户为人提供账户标识,而服务账户为计算机进程和K8s集群中运行的Pod提供账户标识。用户帐户和服务帐户的一个区别是作用范围;用户帐户对应的是人的身份,人的身份与服务的namespace无关,所以用户账户是跨namespace的;而服务帐户对应的是一个运行中程序的身份,与特定namespace是相关的。

19、RBAC访问授权

K8s在1.3版本中发布了alpha版的基于角色的访问控制(Role-based Access Control,RBAC)的授权模式。相对于基于属性的访问控制(Attribute-based Access Control,ABAC),RBAC主要是引入了角色(Role)和角色绑定(RoleBinding)的抽象概念。在ABAC中,K8s集群中的访问策略只能跟用户直接关联;而在RBAC中,访问策略可以跟某个角色关联,具体的用户在跟一个或多个角色相关联。显然,RBAC像其他新功能一样,每次引入新功能,都会引入新的API对象,从而引入新的概念抽象,而这一新的概念抽象一定会使集群服务管理和使用更容易扩展和重用。

四、Kubernetes架构

 1. etcd保存整个集群的状态
 2. apiserver提供了资源的唯一入口,并提供认证、授权、访问控制、API注册和发现等
 3. controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等
 4. scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上
 5. kubelet负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理
 6. Container runtime负责镜像的管理以及Pod和容器的真正运行(CRI)
 7. kube-poxy负责为Service提供cluster内部的服务发现和负载均衡
 
除了核心组件,还有一些推荐的组件:

 8. kube-dns负责为整个集群提供DNS服务
 9. Ingress Controller 为服务提供外网入口
 10. Heapster提供资源监控
 11. Dashboard提供GUIFederation提供跨可用区的集群
 12. Fluentd-elasticsearch提供集群日志采集,存储与查询

1、Master节点

Master:集群控制管理节点,所有的命令都经由master处理

2、master:kube-apiserver

Kubernetes API,集群的统一入口,各组件协调者,以HTTP API提供接口服务,所有对象资源的增删改查和监听操作都交给APIServer处理后再提交给Etcd存储。

负责处理来自用户的请求,其主要作用就是对外提供RESTful的接口
包括用于查看集群状态的读请求以及改变集群状态的写请求,也是唯一一个于etcd集群通信的组件。

3、master:kube-controller-manager

处理集群中常规后台任务,一个资源对应一个控制器,而ControllerManager就是负责管理这些控制器的。

管理器运行了一系列的控制器进程,这些进程会按照用户的期望状态在后台不断地调节整个集群中的对象,需要有高可用机制
当服务的状态发生改变,控制器就会发现这个改变并且开始向目标状态迁移。
由一系列控制器组成,通过apiserver监控整个集群的状态,并确保集群处于预期的工作状态
Node Controller       #节点控制器
Deployment Controller   #pod控制器
Service Controller    #服务控制器
Volume Controller      #存储卷控制器
Endpoint Controller    #接入点控制器
Garbage Controller    #垃圾回收控制器
Namespace Controller    #名称空间控制器
Job Controller       #任务控制器
Resource quta Controller    #资源配额控制器

4、master:kube-scheduler

根据调度算法为新创建的Pod选择一个Node节点。

调度器其实为kubernetes中运行的Pod选择部署的Worker节点
它会根据用户的需要选择最能满足请求的节点来运行Pod,它会在每次需要调度Pod时执行。
主要功能是接收调度pod到适合的运算节点上
预选策略( predict )
优选策略( priorities )

5、master:kubelet

kubelet是Master在Node节点上的Agent,管理本机运行容器的生命周期,比如创建容器、Pod挂载数据卷、下载secret、获取容器和节点状态等工作。kubelet将每个Pod转换成一组容器。

6、master:kube-proxy

在Node节点上实现Pod网络代理,维护网络规则和四层负载均衡工作。

7、Node节点

Node:是kubernetes集群的工作负载节点。Master为其分配工作,当某个Node宕机时,Master会将其工作负载自动转移到其他节点。 

#1.kube-kubelet(部署容器,监控容器)
kubelet是一个节点上的主要服务,他周期性的从APIServer接受新的或者修改的pod规范并且保证节点上的pod和其容器的正常运行
还会保证节点会向目标状态迁移,该节点仍然会向Master节点发送宿主机的健康状态。
简单地说, kubelet的主要功能就是定时从某个地方获取节点上pod的期望状态(运行什么容器、运行的副本数量、网络或者存储如何配置等等) ,并调用对应的容器平台接口达到这个状态
定时汇报当前节点的状态给apiserver,以供调度的时候使用
镜像和容器的清理工作,保证节点上镜像不会占满磁盘空间,退出的容器不会占用太多资源

#2.kube-proxy(提供容器间的网络)
负责宿主机的子网管理,同时也能将服务暴露给外部
其原理就是在多个隔离的网络中把请求转发给正确的Pod或者容器。
是K8S在每个节点 上运行网络代理, service资源的载体
建立了pod网络和集群网络的关系( clusterip >podip )
常用三种流量调度模式
Userspace (废弃)
Iptables (濒临废弃)(绝大部分公司在用)
Ipvs(推荐)
负责建立和删除包括更新调度规则、通知apiserver自己的更新,或者从apiserver哪里获取其他kube- proxy的调度规则变化来更新自己的

8、docker或rocket(rkt)

9、etcd

 

简介:分布式键值存储系统。用于保持集群状态,比如Pod、Service等对象信息。etcd是受Zookeeper与doozer启发而催生的项目。

存储:etcd的存储分为内部存储和持久化(硬盘)存储两部分。内存中的存储除了顺序化地记录所有用户对节点数据变更的记录外,还会对用户数据进行索引、建堆等方便查询的操作。而持久化则使用WAL进行记录存储。在k8s中,所有数据的存储以及操作记录都在etcd中进行存储,所以对于k8s集群来说,etcd是相当重要的,一旦故障,可能导致整个集群的瘫痪或者数据丢失。

在WAL体系中,所有的数据在提交之前都会进行日志记录。持久化存储的目录分为两个:snap和wal。snapshot相当于数据压缩,默认会将10000条wal操作记录merge成snapshot,节省存储,又保证数据不会丢失。

WAL:存储所有事务的变化记录

Snapshot:用于存放某一时刻etcd所有目录的数据

核心算法:etcd的核心算法是raft算法,强一致性算法。

 

注意:由于etcd是负责存储,所以不建议搭建单点集群,如zookeeper一样,由于存在选举策略,所以一般推荐奇数个集群,如3,5,7。只要集群半数以上的结点存活,那么集群就可以正常运行,否则集群可能无法正常使用。

10、Flanne 网络相关 

Kubernetes为每个Pod都分配了唯一的IP地址,称之为PodIP,一个Pod里的多个容器共享PodIP地址。要求底层网络支持集群内任意两个Pod之间的直接通信,通常采用虚拟二层网络技术来实现(Flannel)。

Kubernetes支持一种特殊的网络模型,Kubernetes创建了一个地址空间,并且不动态的分配端口,它可以允许用户选择任何想使用的端口,为了实现这个功能,它为每个Pod分配IP地址。

现代互联网应用一般都会包含多层服务构成,比如web前台空间与用来存储键值对的内存服务器以及对应的存储服务,为了更好的服务于这样的架构,Kubernetes提供了服务的抽象,并提供了固定的IP地址和DNS名称,而这些与一系列Pod进行动态关联,这些都通过之前提到的标签进行关联,所以我们可以关联任何我们想关联的Pod,当一个Pod中的容器访问这个地址的时候,这个请求会被转发到本地代理(kube proxy),每台机器上均有一个本地代理,然后被转发到相应的后端容器。Kubernetes通过一种轮训机制选择相应的后端容器,这些动态的Pod被替换的时候,Kube proxy时刻追踪着,所以,服务的IP地址(dns名称),从来不变。

五、Kubernetes主要功能

1、数据卷 :  Pod中容器之间共享数据,可以使用数据卷。

2、应用程序健康检查:容器内服务可能进程堵塞无法处理请求,可以设置监控检查策略保证应用健壮性。

3、复制应用程序实例:控制器维护着Pod副本数量,保证一个Pod或一组同类的Pod数量始终可用。

4、弹性伸缩:根据设定的指标(CPU利用率)自动缩放Pod副本数。

5、服务发现:使用环境变量或DNS服务插件保证容器中程序发现Pod入口访问地址。

6、负载均衡:一组Pod副本分配一个私有的集群IP地址,负载均衡转发请求到后端容器。在集群内部其他Pod可通过这个ClusterIP访问应用。

7、滚动更新:更新服务不中断,一次更新一个Pod,而不是同时删除整个服务。

8、服务编排:通过文件描述部署服务,使得应用程序部署变得更高效。

9、资源监控:Node节点组件集成cAdvisor资源收集工具,可通过Heapster汇总整个集群节点资源数据,然后存储到InfluxDB时序数据库,再由Grafana展示。

10、提供认证和授权:支持属性访问控制(ABAC)、角色访问控制(RBAC)认证授权策略。

六、Kubernetes命令行管理工具

一般使用kubectl来管理Kubernetes集群。

Kubernetes UI/dashboard

Kubernetes有一个基于web的用户界面,它可以图表化显示当前集群状态。

Kubernetes界面默认是作为集群插件部署的。要访问它需要进入 https:///ui 这个地址,之后会重定向到 https:///api/v1/proxy/namespaces/kube-system/services/kube-ui/#/dashboard/ 。

如果你发现你无法访问UI,那有可能是Kubernetes UI服务在你的集群上还没有启动。如果是这样,你可以手动开启UI服务:

kubectl create -f cluster/addons/kube-ui/kube-ui-rc.yaml –namespace=kube-system
kubectl create -f cluster/addons/kube-ui/kube-ui-svc.yaml –namespace=kube-system

通常,这些应该通过 kube-addons.sh 脚本自动地被运行在主节点上。

七、Kubernetes相关经验

1、Pod的管理

在Kubernetes中,所有的容器均在Pod中运行,一个Pod可以承载一个或者多个相关的容器,在后边的案例中,同一个Pod中的容器会部署在同一个物理机器上并且能够共享资源。一个Pod也可以包含O个或者多个磁盘卷组(volumes),这些卷组将会以目录的形式提供给一个容器,或者被所有Pod中的容器共享,对于用户创建的每个Pod,系统会自动选择那个健康并且有足够容量的机器,然后创建类似容器的容器,当容器创建失败的时候,容器会被node agent自动的重启,这个node agent叫kubelet,但是,如果是Pod失败或者机器,它不会自动的转移并且启动,除非用户定义了 replication controller。

用户可以自己创建并管理Pod,Kubernetes将这些操作简化为两个操作:基于相同的Pod配置文件部署多个Pod复制品;创建可替代的Pod当一个Pod挂了或者机器挂了的时候。而Kubernetes API中负责来重新启动,迁移等行为的部分叫做“replication controller”,它根据一个模板生成了一个Pod,然后系统就根据用户的需求创建了许多冗余,这些冗余的Pod组成了一个整个应用,或者服务,或者服务中的一层。一旦一个Pod被创建,系统就会不停的监控Pod的健康情况以及Pod所在主机的健康情况,如果这个Pod因为软件原因挂掉了或者所在的机器挂掉了,replication controller 会自动在一个健康的机器上创建一个一摸一样的Pod,来维持原来的Pod冗余状态不变,一个应用的多个Pod可以共享一个机器。

我们经常需要选中一组Pod,例如,我们要限制一组Pod的某些操作,或者查询某组Pod的状态,作为Kubernetes的基本机制,用户可以给Kubernetes Api中的任何对象贴上一组 key:value 的标签,然后,我们就可以通过标签来选择一组相关的Kubernetes Api 对象,然后去执行一些特定的操作,每个资源额外拥有一组(很多) keys 和 values,然后外部的工具可以使用这些keys和vlues值进行对象的检索,这些Map叫做annotations(注释)。

2、网络配置

如何从Internet访问部署的应用?

部署前的服务有一个IP地址,但这个地址仅在Kubernetes集群中可用。这意味着无法通过网络访问该服务!在Google Cloud Engine上运行时,Kubernetes会自动配置一个负载均衡用以访问应用;如果不在Google Cloud Engine上运行(比如我们),那就需要做一些额外的工作来获得负载均衡了。

直接在主机端口上开放服务是一个可行的解决方案(很多人一开始的确是这么做的),但我们发现,这样的做法等于放弃了Kubernetes所提供的许多好处。如果我们依赖主机上的端口,部署多个应用时会遇到端口冲突。另外这样的做法会加大扩展集群和更换主机的难度。

我们发现,解决以上问题的更好办法,是在Kubernetes集群前配置负载均衡器,例如HAProxy或者NGINX。于是我们开始在AWS上的VPN中运行Kubernetes集群,并使用AWS ELB将外部web流量路由到内部HAProxy集群。HAProxy为每个Kubernetes服务配置了“后端”,以便将流量交换到各个pods。

在任何情况下,创建新的Kubernetes服务,我们都需要一种机制动态重新配置负载均衡器(在我们的例子中是HAProxy)。

Kubernetes社区目前正在开发一个名为ingress的功能,用来直接从Kubernetes配置外部负载均衡器。

3、配置负载均衡

首先,我们需要一个地方存储负载均衡器配置。负载均衡器配置可以存储在任何地方,不过因为我们已经有etcd可用,就把这些配置放在了etcd里。我们使用一个名为“confd”的工具观察etcd中的配置变动,并用模板生成了一个新的HAProxy配置文件。当一个新的服务添加到Kubernetes,我们向etcd中添加一个新的配置,一个新的HAProxy配置文件也就此产生。

4、资源限制

使用Kubernetes时,搞清楚资源限制很重要。你可以在每个pod上配置资源请求和CPU/内存限制,也可以控制资源保证和bursting limits。

这些设置对于高效运行多个容器极为重要,防止容器因分配内存不足而意外停止。

建议尽早设置和测试资源限制。没有限制时,看起来运行良好,不代表把重要负载放到容器中不会出现问题。

5、Kubernetes监控

当我们快要搭建好Kubernetes时,我们意识到监控和日志在这个新的动态环境中非常重要。当我们面对大规模服务和节点时,进入服务器查看日志文件是不现实的,建一个中心化的日志和监控系统需要尽快提上议程。

6、日志

有很多用于日志记录的开源工具,我们使用的是Graylog和Apache Kafka(从容器收集摘录日志的消息传递系统)。容器将日志发送给Kafka,Kafka交给Graylog进行索引。我们让应用组件将日志打给Kafka,方便将日志流式化,成为易于索引的格式。也有一些工具可以从容器外收集日志,然后发送给日志系统。

7、监控

Kubernetes具备超强的故障恢复机制,Kubernetes会重启意外停止的pod。我们曾遇到过因内存泄露,导致容器在一天内宕机多次的情况,然而令人惊讶的是,甚至我们自己都没有察觉到。

Kubernetes在这一点上做得非常好,不过一旦问题出现,即使被及时处理了,我们还是想要知道。因此我们使用了一个自定义的运行状况检查仪表盘来监控Kubernetes节点和pod,以及包括数据存储在内的一些服务。可以说在监控方面,Kubernetes API再次证明了其价值所在。

检测负载、吞吐量、应用错误等状态也是同样重要的,有很多开源软件可以满足这一需求。我们的应用组件将指标发布到InfluxDB,并用Heapster去收集Kubernetes的指标。我们还通过Grafana(一款开源仪表盘工具)使存储在InfluxDB中的指标可视化。有很多InfluxDB/Grafana堆栈的替代方案,无论你才用哪一种,对于运行情况的跟踪和监控都是非常有价值的。

8、数据存储和Kubernetes

很多Kubernetes新用户都有一个问题:我该如何使用Kubernetes处理数据?

运行数据存储时(如MangoDB或MySQL),我们很可能会有持久化数据储存的需求。不过容器一但重启,所有数据都会丢失,这对于无状态组件没什么影响,但对持久化数据储存显然行不通。好在,Kubernetes具有Volume机制。

Volume可以通过多种方式备份,包括主机文件系统、EBS(AWS的Elastic Block Store)和nfs等。当我们研究持久数据问题是,这是一个很好的方案,但不是我们运行数据存储的答案。

9、副本问题

在大多数部署中,数据存储也是有副本的。Mongo通常在副本集中运行,而MySQL可以在主/副模式下运行。我们都知道数据储存集群中的每个节点应该备份在不同的volume中,写入相同volume会导致数据损坏。另外,大多数数据存储需要明确配置才能使集群启动并运行,自动发现和配置节点并不常见。同时,运行着数据存储的机器通常会针对某项工作负载进行调整,例如更高的IOPS。扩展(添加/删除节点)对于数据存储来说,也是一个昂贵的操作,这些都和Kubernetes部署的动态本质不相符。

决定不在生产环境数据存储上使用Kubernetes,以我们的情况,在Kubernetes内运行数据存储没有想象中那么完美,设置起来也比其他Kubernetes部署复杂得多。

于是我们决定不在生产环境数据存储上使用Kubernetes,而是选择在不同的机器上手动启动这些集群,我们在Kubernetes内部运行的应用正常连接到数据存储集群。

所以说,没必要运行Kubernetes的一切,按自身情况与其他工具配合使用,会有意想不到的效果,比如我们的数据存储和HAProxy服务器。

10、未来

看看我们现在的部署,可以负责任的说,Kubernetes的表现绝对是梦幻级的,而Kubernetes API更是自动化部署流程的利器。由于不需要处理VM,我们现在的部署相比之前更快、更可靠。更简单的容器测试和交付,也让我们在构建和部署可靠性上得到了巨大提升。

这种新的部署方式迅速高效,让我们得以跟上其他团队的节奏,这绝对是必要的。

11、成本计算

任何事情都有两面性。运行Kubernetes,需要一个etcd集群以及一个Master节点,对于较小的部署来说,这一开销还是比较大的,适合通过一些云服务达成。

对于大规模部署,Kubernetes可以帮助节省大量服务器成本,etcd集群和Master节点这点开销就显得微不足道了。Kubernetes让很多容器在一个主机上运行变得非常容易,最大程度上利用了现有资源,减少了服务器数量,成本自然下降了。不过这样的集群也给运维工作提出了更高的要求,必须要的时候,我们可以选择一些云计算平台提供的云服务来轻松达成。

 

posted @ 2022-04-30 13:34  踏雪无痕SS  阅读(1400)  评论(0编辑  收藏  举报