1.1 Kubernetes 特性
Kubernetes是一种用于在一组主机上运行和协同容器化应用程序的系统,旨在提供可预测性、可扩展性与高可用性的方法来完全管理容器化应用程序和服务的生命周期的平台。
- 自动装箱
构建于容器之上,基于资源依赖及其他约束自动完成容器部署且不影响其可用性,并通过调度机制混合关键型应用和非关键型应用的工作负载于同一节点以提升资源使用率; - 自我修复(自愈)
支持容器故障后自动重启、节点故障后重新调度容器,以及其他可用节点、健康状态检查失败后关闭容器并重新创建等自我修复机制; - 水平扩展
支持通过简单命令或UI手动水平扩展,以及基于CPU等资源负载率的自动水平扩展机制; - 服务发现和负载均衡
Kubernetes通过其附加组件之一的KubeDNS(或CoreDNS)为系统内置了服务发现功能,它会为每个Service配置DNS名称,并允许集群内的客户端直接使用此名称发出访问请求,而Service则通过iptables或ipvs内建了负载均衡机制; - 自动发布和回滚
Kubernetes支持“灰度”更新应用程序或其配置信息,它会监控更新过程中应用程序的健康状态,以确保它不会在同以时刻杀掉所有实例,而此过程中一旦有故障发生,就会立即自动执行回滚操作; - 密钥和配置管理
Kubernetes的ConfigMap实现了配置数据与Docker镜像解耦,需要时,仅对配置做出变更无需重新构建Docker镜像;此外,对于应用所依赖的一些敏感数据,如用户名和密码、令牌和密钥等信息,Kubernetes专门提供了Secret对象为其解耦,既便利了应用的快速开发和交付,又提供了一定程度上的安全保障; - 存储编排
Kubernetes支持Pod对象按需自动挂载不同类型的存储系统,包括:本地存储、公有云服务商的云存储、网络存储系统(如:NFS、iSCSI、ClusterFS、Ceph等); - 批量处理执行
除了服务型应用,Kubernetes还支持批处理作业及CI;
1.2 Kubernetes 概念和术语
Kubernetes 使用共享网络将多个物理机或虚拟机汇集到一个集群中,在各服务器之间进行通信,该集群是配置Kubernetes的所有组件、功能和工作负载的物理平台;
- Master
Master是集群的网关和中枢,负责诸如为用户和客户端暴露API、跟踪其他服务器的健康状态、以最优方式调度工作负载,以及编排其他组件之间的通信等任务,它是客户端与集群之间的核心联络点,并负责k8s系统的大多数集中式管控逻辑。 - Node
Node是k8s集群的工作节点,负责接收来自Master的工作指令根据指令相应地创建或销毁Pod对象,以及调整网络规则以合理地路由和转发流量等; - Pod
Kubernetes并不直接运行容器,而是使用一个抽象的资源对象来封装一个或者多个容器,这个抽象即为Pod,它是k8s的最小调度单元; - 资源标签
标签(Label)是将资源进行分类的标识符,资源标签其实就是一个键值型(key/value)数据。标签旨在指定对象(如Pod等)辨识性的属性,这些属性仅对用户存在特定的意义,对k8s集群来说并不直接表达核心系统语义。
标签可以在对象创建时附加其上,并能够在创建后的任意时间进行添加和修改。一个对象可以拥有多个标签,一个标签也可以附加于多个对象之上; - 标签选择器
标签选择器(Selector)是一种根据Label来过滤符合条件的资源对象的机制; - Pod控制器
尽管Pod是k8s的最小调度单元,但用户通常并不会直接部署及管理Pod对象,而是借助于另一类抽象—控制器(Controller)对其进行管理。用于工作负载的控制器是一种管理Pod生命周期的资源抽象,它们是k8s上的一类对象,而非单个资源对象,包括ReplicationController、ReplicaSet、Deployment、StatefulSet、Job等; - 服务资源(Service)
Service是建立在一组Pod对象之上的资源抽象,它通过标签选择器选定一组Pod对象,并为这组Pod对象定义一个统一的固定访问入口(通常是一个IP地址),若k8s集群存在DNS附件,它就会在Service创建时为其自动配置一个DNS名称以便客户端进行服务发现。
到达Service IP的请求将被负载均衡至其后端的端点—各个Pod对象之上,因此Service从本质上来讲是一个四层代理服务。另外,Service还可以将集群外部流量引入到集群中。 - 存储卷
存储卷(Volume)是独立于容器文件系统之外的存储空间,常用于扩展容器的存储空间并为它提供持久存储能力。k8s集群上的存储卷大体可分为临时卷、本地卷和网络卷。临时卷和本地卷都位于Node本地,一旦Pod被调度至其他Node,此种类型存储卷将无法访问到,因此临时卷和本地卷通常用于数据缓存,持久化的数据则需要放置于持久卷(presistent volume)上。 - Name和Namespace
名称(Name)是k8s集群中资源对象的标识符,它们的作用域通常是名称空间(Namespace),因此名称空间是名称的额外的限定机制。同一个名称空间中,同一类型资源对象的名称必须具有唯一性。如果不指定,属于默认的名称空间”default”。 - Annotation
Annotation(注解)是另一种附加在对象之上的键值类型的数据,但它拥有更大的数据容量。Annotation常用于将各种非标识型元数据(metadata)附加到对象上,但它不能用于标识和选择对象,通常也不会被k8s直接使用,其主要目的是方便工具或用户的阅读及查找等; - Ingress
k8s将Pod对象和外部网络环境进行了隔离,Pod和Service等对象间的通信都使用其内部专用地址进行,如若需要开放某些Pod对象提供给外部用户访问,则需要为其请求流量打开一个通往k8s集群内部的通道,除了Service外,Ingress也是这类通道的实现方式之一。
1.3 Kubernetes集群组件
1.3.1 Master组件
Kubernetes的集群控制平面由多个组件组成,这些组件可统一运行于单一Master节点,也可以以多副本的方式同时运行于多个节点,来为Master提供高可用功能,甚至还可以运行于Kubernetes集群自身之上。Master主要包含以下几个组件:

- API Server
API Server负责输出RESTful风格的k8s API,它是发往集群的所有REST操作命令的接入点,并负责接收、校验并响应所有的REST请求,结果状态被持久存储于etcd中,因此,API Server是整个集群的网关。 - 集群状态存储(Cluster State Store)
k8s集群的所有状态信息都需要持久存储于存储系统etcd中,不过,etcd是由CoreOS基于Raft协议开发的分布式键值存储,可用于服务发现、共享配置以及一致性保障。因此,etcd是独立的服务组件,并不隶属于k8s集群自身。生产环境中应该以etcd集群的方式运行以确保其服务可用性。
etcd不仅能够提供键值数据存储,还为其提供了监听(watch)机制,用于监听和推送变更。k8s集群系统中,etcd中的键值发生变化时会通知到API Server,并由其通过watch API向客户端输出。 - 控制器管理器(Controller Manager)
k8s中,集群级别的大多数功能都是由几个被称为控制器的进程执行实现的,这几个进程被集成于kube-controller-manager守护进程中。由控制完成的功能主要包括生命周期功能和API业务逻辑:- 生命周期功能:包括Namespace创建和生命周期、Event垃圾回收、Pod终止相关的垃圾回收、级联垃圾回收及Node垃圾回收等;
- API业务逻辑:例如,由ReplicaSet执行的Pod扩展等;
- 调度器(Scheduler)
k8s是用于部署和管理大规模容器应用的平台,根据集群规模的不同,其托管运行的容器很可能会数以千计甚至更多。API Server确认Pod对象的创建请求之后,便需要由Scheduler根据集群内各节点的可用资源状态,以及要运行的容器的资源需求做出调度决策。另外,k8s还支持用户自定义调度器;
1.3.2 Node组件
- Node的核心代理程序kubelet
kubelet是运行于工作节点之上的守护进程,它从API Server接收关于Pod对象的配置信息并确保它们处于期望的状态。kubelet会在API Server上注册当前工作节点,定期向Master汇报节点资源使用情况,并通过cAdvisor监控容器和节点的资源占用状态。 - 容器运行时环境
每个Node都要提供一个容器运行时(Container Runtime)环境,它负责下载镜像并运行容器。kubelet并未固定链接至某容器运行时环境,而是以插件的方式载入配置的容器环境。这种方式清晰地定义了各组件的边界。目前,k8s支持的容器运行环境至少包括Docker、RKT、cri-o和Fraki等; - kube-proxy
每个工作节点都需要运行一个kube-proxy守护进程,它能够按需为Service资源对象生成iptables或ipvs规则,从而捕获访问当前Service的ClusterIP的流量并将其转发至正确的后端Pod对象;
1.3.3 核心附件
- KubeDNS: 在k8s集群中调度运行提供DNS服务的Pod,统一集群中的其他Pod可使用此DNS服务解决主机名。k8s自1.11版本开始默认使用CoreDNS项目为集群提供服务注册和服务发现的动态名称解析服务;
- Kubernetes Dashboard: k8s集群的全部功能都要基于Web的UI,来管理集群中的应用甚至是集群自身;
- Heapster:容器和节点的性能监控与分析系统,它收集并解析多种指标数据,如资源利用率、生命周期事件等。在新版本k8s中,其功能会逐渐有Prometheus结合其他组件所取代;
- Ingress Controller:Service是一种工作于传统层的负载均衡器,而Ingress是在应用层实现的HTTP(s)负载均衡机制。不过,Ingress资源自身并不能进行“流量穿透”,它仅是一组路由规则的集合,这些规则需要通过Ingress控制器(Ingress Controller)发挥作用。目前,此类的可用项目有Nginx、Traefik、Envoy及HAProxy等;
1.4 Kubernetes 网络模型基础
1.4.1 网络模型概述
Kubernetes集群至少应该包含三个网络:
- 各主机(Master、Node和etcd等)自身所属的网络,其地址配置于主机的网络接口,用于各主机之间的通信。此地址配置于k8s集群构建之前,它并不能由k8s管理,管理员需要于集群构建之前自行确定其地址配置及管理方式;
- k8s集群上专用于Pod资源对象的网络,它是一个虚拟网络,用于为各Pod对象设定IP地址等网络参数,其地址配置于Pod中容器的网络接口之上。Pod网络需要借助kubenet插件或CNI插件实现,该插件可独立部署于k8s集群之外,亦可托管于k8s之上,它需要在构建k8s集群时由管理员进行定义,而后在创建Pod对象时由其自动完成各网络参数的动态配置;
- 专用于Service资源对象的网络,它也是一个虚拟网络,用于为k8s集群之中的Service配置IP地址,但此地址并不配置于任何主机或容器的网络接口之上,而是通过Node之上的kube-proxy配置为iptables或ipvs规则,从而将发往此地址的所有流量调度至其后端的各Pod对象之上。

1.4.2 集群上的网络通信
k8s集群的客户端大体可以分为两类:API Server客户端和应用程序(运行为Pod中的容器)客户端。
- 第一类客户端通常包含人类用户和Pod对象两种,它们通过API Server访问k8s集群完成管理任务,例如,管理集群上的各种资源对象。
- 第二类客户端一般也包含人类用户和Pod对象两种,它们的访问目标是Pod上运行于容器中的应用程序提供的各种具体的服务,如redis和nginx等,不过这些访问请求通常要经由Service或Ingress资源对象进行。

1.4.3 常用的网络插件
- Flannel
Flannel实质上是一种“覆盖网络(overlay network)”,也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持UDP、VxLAN、AWS VPC和GCE路由等数据转发方式,默认的节点间数据通信方式是UDP转发;
- 数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡,这是P2P的虚拟网卡,falnneld服务监控在网卡的另一端;
- flannel在etcd服务维护了一张节点间的路由表;
- 源主机的flanneld服务将原本的数据内容UDP封装后根据自己路由表投递给目的节点flanneld服务,数据到达以后被解包,然后直接进入目的节点的flannel0虚拟网卡,然后被转发到目的主机的docker0虚拟网卡,最后就像本机容器通信一下的有docker0路由达到目标容器;
- Calico
Flannel是一种典型的Overlay网络,它将已有的物理网络(Underlay网络)作为基础,在其上建立叠加的逻辑网络,实现网络资源的虚拟化。Overlay网络有一定额外的封包和解包等网络开销,对网络通信的性能有一定损耗。
Calico是纯三层的SDN 实现,它基于BPG 协议和Linux自身的路由转发机制,不依赖特殊硬件,容器通信也不依赖iptables NAT或Tunnel 等技术。能够方便的部署在物理服务器、虚拟机(如 OpenStack)或者容器环境下。同时calico自带的基于iptables的ACL管理组件非常灵活,能够满足比较复杂的安全隔离需求。- Calico创建和管理一个扁平的三层网络,每个容器会分配一个可路由的IP。由于通信时不需要解包和封包,网络性能损耗小,易于排查,且易于水平扩展;
- 小规模部署是可以通过BGP client直接互联,大规模下可通过指定的BGP Route Reflector来完成,这样保证所有的数据流量都是通过IP路由的方式完成互联的;
- Calico基于iptables还提供了丰富而灵活的网络Policy,保证通过各个节点上的ACL来提供Workload的多租户隔离、安全组以及其他可达性限制等功能。
- IPIP网络:
- tunlo设备封装数据,形成隧道,承载流量;
- 适用于互相访问的pod不在同一个网段中,跨网段访问的场景。外层封装的ip能够解决跨网段的路有问题;
- 流量需要tunl0设备封装,效率略低;
- BGP网络:
- 使用路由信息导向流量;
- 适用于互相访问的pod在同一网段,适用于大型网络;
- 原生hostGW,效率高
- 不过,考虑到并非所有的网络都能支持BGP,以及Calico控制平面的设计要求无力网络必须是二层网络,以确保vRouter间均直接可达,路由不能够将物理设备当作下一跳等原因,为了支持三层网络,Calico还推出了IP-in-IP叠加的模型,它也使用Overlay的方式来传输数据。IPIP的包头非常小,而且也是内置的内核中,因此理论上它的速度要比VxLAN快一点;
- Calico 3.x的默认配置使用的是IPIP类型的传输方式而非BGP;

1.5 Kubernetes核心对象
1.5.1 Pod资源对象
Pod资源对象是一种集合了一到多个应用容器、存储资源、专用IP及支撑容器运行的其他选项的逻辑组件。Pod代表着k8s的部署单元及原子运行单元,即一个应用程序的单一运行实例,它通常由共享资源且关系紧密的一个或多个应用容器组成。
k8s的网络模型要求其各Pod对象的IP地址位于同一网络平面内,各Pod之间可使用其IP地址直接进行通信,这些Pod对象都像是运行于同一局域网中的多个主机;
Pod对象中的各进程均运行于彼此隔离的容器中,并于各容器间共享两种关键资源:网络和存储卷
- 网络:每个Pod对象都会被分配一个集群内专用的IP地址,同一Pod内部的所有容器共享Pod对象的Network和UTS名称空间,其中包括主机名、Ip地址和端口等。因此,这些容器间的通信可以基于本地回环接口lo进行,而与Pod外的其他组件的通信则需要使用Service资源对象的ClusterIP及其相应的端口完成。
- 存储卷:用户可以为Pod对象配置一组”存储卷”资源,这些资源可以共享给其内部的所有容器使用,从而完成容器间数据的共享。存储卷还可以确保在容器终止后被重启,甚至是被删除后也能确保数据不会丢失,从而保证了生命周期内的Pod对象数据的持久化存储。
一个Pod对象代表某个引用程序的一个特定实例,如果需要扩展应用程序,则意味着为此应用程序同时创建多个Pod实例,每个实例均代表应用程序的一个运行的“副本”(replica)。这些副本化的Pod对象的创建和管理通常由另一组称之为”控制器”(Controller)的对象实现。
创建Pod时,还可以使用Pod Preset对象为Pod注入特定的信息,如ConfigMap、Secret、存储卷、卷挂载和环境变量等。有了Pod Preset对象,Pod模版的创建者就无须为每个模版显式提供所有信息,也就无须实现了解需要配置的每个应用的细节即可完成模版定义。
1.5.2 Controller
k8s集群的设计中,Pod是有生命周期的对象。用户通过手工创建或Controller(控制器)直接创建的Pod对象会被调度器(Scheduler)调度至集群中的某工作节点运行,待到容器应用进程运行结束之后正常终止,随后就会被删除。另外,节点资源耗尽或故障也会导致Pod对象被回收。
但Pod对象本身并不具有“自愈”功能,若是因为工作节点甚至是调度器自身导致了运行失败,那么它将会被删除;同样,资源耗尽或节点故障导致的回收操作也会删除相关的Pod对象。在设计上,k8s使用controller实现对一次性的Pod的管理操作,例如,要确保部署的应用程序的Pod副本数量严格反映用户期望的数据,以及基于Pod模版来重建Pod对象等,从而实现Pod对象的扩缩容、滚动更新和自愈能力等;
控制器本身也是一种资源类型,有多种实现,如Replication Controller、Deployment、StatefulSet、DaemonSet和Jobs等,也可统称为Pod控制器。Deployment是这类控制器的代表实现,是目前最常用的管理无状态应用的Pod控制器。
需注意的是,在实际的应用场景中,在接收到的请求流量负载显著低于或接近于已有Pod副本的整体承载能力时,用户需要手动修改Pod控制器中的期望副本数量以实现应用规模的扩容或缩容。不过,若集群中部署了HeapSter或Prometheus一类的资源指标监控附件时,还可以使用”HorizontalPodAutoscaler(HPA)”计算出合适的Pod副本数量,并自动修改Pod控制器中期望的副本数以实现应用规模的动态伸缩。
1.5.3 Service
尽管Pod对象可以拥有IP地址,但此地址无法确保在Pod对象启动或被重建后保持不变,这会为集群中的Pod应用间依赖关系的维护带来麻烦。于是,Service资源被用于在被访问的Pod对象中添加一个有着固定IP地址的中间层,客户端向此地址发起访问请求后由相关的Service资源调度并代理至后端的Pod对象。

Service IP是一种虚拟IP,也称为Cluster IP,它专用于集群内通信,通常使用专用的地址段,如”10.96.0.0/12”网络,各Service对象的IP地址在此范围内由系统动态分配。
Service主要有三种常用类型:
- 仅用于集群内部通信的ClusterIP类型;
- 接入集群外部请求的NodePort类型,它工作于每个节点的主机IP之上;
- LoadBalancer类型,它可以把外部请求负载均衡至多个Node的主机IP的NodePort之上。
此三种类型中,每一种都以其前一种为基础才能实现,而且第三种类型中的LoadBalancer需要协同集群外部的组件才能实现,并且此外部组件并不接受K8s的管理。
