描述

kubernetes集群将所有节点上的资源(CPU,内存,磁盘等计算资源)全都整合到一个大的虚拟资源池里,用于替代一个个单独的服务器,然后将这些基本资源用于其Pod资源对象,Pod中的容器运行着隔离的任务,它们以Pod为原子单位,并根据其资源需求从虚拟资源池里为其动态分配资源,kubernetes就类似于操作系统的内核,主要职责是负责任务的调度,资源的分配,而Pod资源对象就是那些运行于用户空间中的进程,于是,传统意义上向单节点或者集群直接部署配置的应用的模型日渐甚微,取而代之的是向kubernetes的API server提交运行Pod对象

API Server是负责接收并响应客户端提交任务的接口,其中kubectl是最为常用的交互式命令工具,想快速了解K8S的办法之一就是部署一个集群测试

kubernetes的核心资源对象

Pod资源对象

  • Pod资源对象是一种集合了一到多个应用容器,存储资源,专用IP及支撑容器运行的其他选项的逻辑组件,Pod代表着k8s的部署单元及原子运行单元,一个应用程序的单一运行实例,K8S的网络模型要求各Pod对象的IP地址能够直接通信,无论它们运行于集群内的哪个工作节点,这些Pod对象都像在同一个局域网中的多个主机
  • 我们可以将每个Pod对象都想像成一个逻辑主机,它类似于现实里面的物理主机或者vm虚拟机,运行于同一个Pod对象中的多个进程也类似于物理机或者vm上独立运行的进程,不过Pod对象中的各个进程均运行在彼此隔离的容器中,并且各个容器之间共享两种资源,网络和存储卷
  • 网络:每个Pod对象都会被分配一个集群内专用的IP地址,也称为Pod IP,同一Pod内部的所有容器共享Pod对象的Network和UTS名称空间,其中包括主机名,IP地址,和端口等,因此这些容器间的通信可以基于本地回环接口通信,而与Pod外的其他组件通信则需要使用Service资源对象及Cluster IP及其相应的端口完成
  • 存储卷:用户可以为Pod对象配置一组“存储卷”资源,这些资源可以共享Pod内部的所有容器使用,从而完成容器间的数据共享,存储卷还可以确保在容器被终止后被重启,甚至是容器被删除后也能保证数据不会丢失,从而保证了生命周期内的Pod对象数据的持久化存储
  • 一个Pod对象代表某个应用程序的一个特定实例,如果需要扩展应用程序,则意味着为此应用程序同时创建多个Pod实例,每个实例均代表应用程序的一个运行的“副本”,这些副本化的Pod对象的创建和管理由另一组称之为“控制器”的对象实现
  • 创建Pod时,还可以使用Pod Preset对象为Pod注入特定的信息,如ConfigMap,secret,存储卷,挂载卷,和环境变量等,有了Pod Preset对象,Pod模板的创建者就无须为每个模板提供所有信息,因此,也就无须事先了解需要配置的每个应用的细节即可完成
  • 基于期望的状态和各节点的资源可用性,master会将Pod对象调度至某个选定的工作节点运行,工作节点去指向的镜像仓库下载镜像,并于本地的容器环境中启动容器,master会将整个集群的状态保存到etcd中,并通过API Server共享给集群内的各个组件和客户端

Controller(控制器)

  • Pod是属于有生命周期的对象,用户通过手工创建或者由controller直接创建的Pod对象都会被调度器调度至集群中的某个工作节点运行,待到容器应用程序运行结束之后正常终止,随后就会被删除,另外节点资源耗尽或者故障也会导致Pod对象被回收
  • Pod对象本身并不具备“自愈”功能,若是因为工作节点甚至是调度器自身导致了运行失败,那么它将会被删除,同样,资源耗尽或者节点故障导致的回收操作也会删除相关的Pod对象,在设计上,K8S使用“控制器”实现对一次性的Pod对象实现管理操作,例如,要确保部署的应用程序的Pod副本数量严格反映用户期望的数目,以及基于Pod模板来重建Pod对象等,从而实现Pod对象的扩缩容,滚动更新和自愈能力等,例如某节点发生故障时,相关的控制器就会将此节点上运行的Pod重新调度到其他节点进行重建
  • 控制器本身也是一种资源类型,它有着多种实现,其中与工作负载相关的实现如replication controller,Deployment,StatefulSet,DaemonSet,和jobs等,也可以统称它们为Pod控制器
  • Pod控制器的定义通常有期望的副本数量,Pod模板和标签选择器组成,Pod控制器会根据标签选择器对Pod对象的标签进行匹配检查,所有满足条件的Pod对象都将受控于当前控制器并计入副本总数,并确保此数目能够精确反映期望的副本数
  • 需要注意的是,在实际的应用场景中,在接收到的请求流量负载显著低于或接近于已有Pod副本的整体承载能力时,用户需要手动修改Pod控制器中的期望副本数量以实现应用规模的扩缩容,不过,若集群中部署了HeapSter或Prometheus一类的资源监控指标附件时,用户还可以使用“HorizontalIPodAutoscaler”(HPA)计算出合适的Pod副本数量,并自动修改Pod控制器中期望的副本数以实现应用规模的动态扩缩容
  • K8S集群中的每个节点都运行着Cadvisor以收集容器及节点的CPU,内存及磁盘资源的利用率指标数据,这些统计数据由Heapster聚合后可以通过API Server访问,HorizontaIPodAutoscaler基于这些数据监控容器的健康状态并作出扩展决策

Service

  • 尽管Pod对象可以拥有IP地址,但是此地址无法确保在Pod对象重启或者重建之后保持不变,这样会造成,如果Pod应用之间有依赖关系的话维护起来就会特别麻烦,前端Pod应用无法基于固定的地址将请求转发给后端的Pod,因为此时后端Pod的IP地址可能已经发生改变,于是Service资源就被用于在Pod对象中添加一个有着固定IP地址的中间层,前端Pod像这个Service地址发出请求,然后该Service将请求转发至后端Pod

换言之,Service是“微服务”的一种实现,事实上它是一种抽象,通过规则定义出由多个Pod对象组合而成的逻辑集合,并附带访问这组Pod对象的策略,Service对象挑选关联Pod对象的方式于Pod控制器一样,都是基于标签选择器进行定义

Service IP是一种虚拟IP,也称为Cluster IP,它专用于集群内通信,通常使用专用的地址端,各Service对象的IP地址范围由系统动态分配,集群内的Pod对象可以直接请求cluster IP,但集群网络属于私有网络,它们仅在集群内部可达,将集群外部的流量引入到集群内部的常用方法是通过节点网络进行,实现方法是通过工作节点的IP地址和端口(NodePort)接入请求,并将其代理至Service对象(Cluster IP)的IP地址和端口,而后由Service对象将请求代理至后端的Pod对象的IP地址和端口(Pod IP),因此集群外部的流量也无法直接到达Service提供的服务,而是需要事先经由一个工作节点的IP地址进行,需要经过两次转发才能到达Pod对象

事实上,Nodeport会部署在集群中的每一个节点上面,这意味着,集群外部的客户端可以通过任何一个工作节点的IP地址来访问定义好的Nodeport都可以访问到Service对象,此种场景中,如果存在集群外部的一个负载均衡器,即可将用户请求负载均衡至集群中的部分或者所有节点,这是一种称为“LoadBanlancer”类型的Service,它通常是由cloud provider自动创建并提供服务的软件负载均衡器,不过,它也可以是由管理员手工配置的吗,诸如F5 Big-IP这一类的硬件设备

Service主要有四种常用类型

  • 第一种是仅用于集群内部通信的Cluster IP类型,也是默认类型,自动分配一个仅集群内部可以访问的虚拟IP
  • 第二种是接入集群外部请求的NodePort类型,它工作于每个节点的主机IP之上,在Cluster IP基础上为Service在每台机器上绑定一个端口,这样就可以通过<nodeIP>:<nodePort>的方式来访问服务,如果kube-proxy设置了--nodeport-addresses=10.244.0.0/16(v1.10+支持),那么该Nodeport仅对设置在范围内的IP有效
  • 第三种是Loadbanlancer类型,它可以把外部的请求流量负载均衡到多个Node主机的NodePort之上,,在NodePort的基础上,接触cloud provider创建一个外部的负载均衡器,并将请求转发到<NodeIP>:<NodePort>
  • 第四种是ExternalName,将服务通过DNS CNAME记录方式转发到指定的域名(通过spec.externlName设定)需要kube-dns在1.7以上支持
  • 此四种类型中,除第四种意外,每一种都以前一种为基础才能实现,而且第三种类型中的LoadBanlancer需要协同集群外部的组件才能实现,并且此组件并不接收k8s的管理
  • 另外,也可以将已有的服务以Service的形式加入到K8S集群中,只需要在创建Service的时候不指定label selector,而是在Service创建好以后手动为其添加endpoint

K8S部署

既然对K8S有了一个基本的了解,那么就开始先部署一个K8S集群吧,K8S集群有三种部署方式,我写过其中两种的部署方式,第三种我们不适用