Kubernetes权威指南:从Docker到Kubernetes实践全接触(第4版)

龚正等

自序

  • Kubernetes是将“一切以服务(Service)为中心,一切围绕服务运转”作为指导思想的创新型产品,这是它的一个亮点。
  • Kubernetes则以Docker为基础打造了一个云计算时代的全新分布式系统架构
  • 以容器为代表的应用形态与以虚拟化为代表的系统形态将会完美融合于OpenStack之上,并与软件定义网络和软件定义存储一起主导下一代数据中心。

第1章 Kubernetes入门

  • 首先,它是一个全新的基于容器技术的分布式架构领先方案
  • 我们不必再费心于负载均衡器的选型和部署实施问题,不必再考虑引入或自己开发一个复杂的服务治理框架,不必再头疼于服务监控和故障处理模块的开发
  • 不论是用Java、Go、C++还是用Python编写的服务,都可以被映射为Kubernetes的Service(服务),并通过标准的TCP通信协议进行交互。
  • Kubernetes具有完备的集群管理能力,包括多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和服务发现机制、内建的智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制,以及多粒度的资源配额管理能力
  • 在Kubernetes中,Service是分布式集群架构的核心,一个Service对象拥有如下关键特征。 ◎ 拥有唯一指定的名称(比如mysql-server)。 ◎ 拥有一个虚拟IP(Cluster IP、Service IP或VIP)和端口号。 ◎ 能够提供某种远程服务能力。 ◎ 被映射到提供这种服务能力的一组容器应用上。

1.2 为什么要用Kubernetes

  • IT行业从来都是由新技术驱动的。
  • 基于Docker的大规模容器化分布式系统解决方案
  • 2015年,谷歌联合20多家公司一起建立了CNCF(Cloud Native Computing Foundation,云原生计算基金会)开源组织来推广Kubernetes,并由此开创了云原生应用(Cloud Native Application)的新时代
  • 在Kubernetes的架构方案中完全屏蔽了底层网络的细节,基于Service的虚拟IP地址(Cluster IP)的设计思路让架构与底层的硬件拓扑无关

1.3 从一个简单的例子开始

  • 在Docker时代,假设我们在一个宿主机上启动了这两个容器,就需要把MySQL容器的IP地址通过环境变量注入Web App容器里;同时,需要将Web App容器的8080端口映射到宿主机的8080端口,以便在外部访问。
  • 需要特别注意的是:这里的labels必须匹配之前的spec.selector,否则此RC每创建一个无法匹配Label的Pod,就会不停地尝试创建新的Pod,陷入恶性循环中。
  • 通常,Cluster IP是在Service创建后由Kubernetes系统自动分配的,其他Pod无法预先知道某个Service的Cluster IP地址,因此需要一个服务发现机制来找到这个服务

1.4 Kubernetes的基本概念和术语

  • Kubernetes其实是一个高度自动化的资源控制系统,它通过跟踪对比etcd库里保存的“资源期望状态”与当前环境中的“实际资源状态”的差异来实现自动控制和自动纠错的高级功能。
  • Kubernetes为每个资源对象都增加了类似数据库表里备注字段的通用属性Annotations,以实现方法1的升级
  • 在每个Node上都运行着以下关键进程。◎ kubelet:负责Pod对应的容器的创建、启停等任务,同时与Master密切协作,实现集群管理的基本功能。◎ kube-proxy:实现Kubernetes Service的通信与负载均衡机制的重要组件。◎ Docker Engine(docker):Docker引擎,负责本机的容器创建和管理工作。
  • kubectl get nodes
  • kubectl describe node 查看某个No
  • 每个Pod都有一个特殊的被称为“根容器”的Pause容器
  • 在Kubernetes里,一个Pod里的容器与另外主机上的Pod容器能够直接通信。
  • Kubernetes里的所有资源对象都可以采用YAML或者JSON格式的文件来定义或描述
  • Pod的IP加上这里的容器端口(containerPort),组成了一个新的概念——Endpoint,它代表此Pod里的一个服务进程的对外通信地址。
  • 对于绝大多数容器来说,一个CPU的资源配额相当大,所以在Kubernetes里通常以千分之一的CPU配额为最小单位,用m来表示。通常一个容器的CPU配额被定义为100~300m,即占用0.1~0.3个CPU
  • Label(标签)是Kubernetes系统中另外一个核心概念。一个Label是一个key=value的键值对,其中key与value由用户自己指定。Label可以被附加到各种资源对象上,例如Node、Pod、Service、RC等,一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上。Label通常在资源对象定义时确定,也可以在对象创建后动态添加或者删除。
  • 管理对象RC和Service则通过Selector字段设置需要关联Pod的Label:
  • Label Selector在Kubernetes中的重要使用场景如下。◎ kube-controller进程通过在资源对象RC上定义的Label Selector来筛选要监控的Pod副本数量,使Pod副本数量始终符合预期设定的全自动控制流程。◎ kube-proxy进程通过Service的Label Selector来选择对应的Pod,自动建立每个Service到对应Pod的请求转发路由表,从而实现Service的智能负载均衡机制。◎ 通过对某些Node定义特定的Label,并且在Pod定义文件中使用NodeSelector这种标签调度策略,kube-scheduler进程可以实现Pod定向调度的特性。
  • RC的定义包括如下几个部分。 ◎ Pod期待的副本数量。 ◎ 用于筛选目标Pod的Label Selector。 ◎ 当Pod的副本数量小于预期数量时,用于创建新Pod的Pod模板(template)。
  • 在我们定义了一个RC并将其提交到Kubernetes集群中后,Master上的Controller Manager组件就得到通知,定期巡检系统中当前存活的目标Pod,并确保目标Pod实例的数量刚好等于此RC的期望值,如果有过多的Pod副本在运行,系统就会停掉一些Pod,否则系统会再自动创建一些Pod。
  • 在运行时,我们可以通过修改RC的副本数量,来实现Pod的动态缩放(Scaling)
  • 删除RC并不会影响通过该RC已创建好的Pod。为了删除所有Pod,可以设置replicas的值为0,然后更新该RC
  • 通过RC机制,Kubernetes很容易就实现了这种高级实用的特性,被称为“滚动升级”(Rolling Update)
  • Replica Set与Deployment这两个重要的资源对象逐步替代了之前RC的作用,是Kubernetes 1.3里Pod自动扩容(伸缩)这个告警功能实现的基础
  • Deployment相对于RC的一个最大升级是我们可以随时知道当前Pod“部署”的进度。
  • Pod的管理对象,除了RC和Deployment,还包括ReplicaSet、DaemonSet、StatefulSet、Job等,分别用于不同的应用场景中
  • Horizontal Pod Autoscaling(Pod横向自动扩容,HPA)
  • HPA有以下两种方式作为Pod负载的度量指标。◎ CPUUtilizationPercentage。◎ 应用程序自定义的度量指标,比如服务在每秒内的相应请求数(TPS或QPS)。
  • kubectl autoscale deployment php-apache --cpu-percent=90--min=1--max=10
  • Kubernetes的Service定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,Service与其后端Pod副本集群之间则是通过Label Selector来实现无缝对接的。RC的作用实际上是保证Service的服务能力和服务质量始终符合预期标准。
  • Service没有共用一个负载均衡器的IP地址,每个Service都被分配了一个全局唯一的虚拟IP地址,这个虚拟IP被称为Cluster IP。这样一来,每个服务就变成了具备唯一IP地址的通信节点,服务调用就变成了最基础的TCP网络通信问题。
  • targetPort属性用来确定提供该服务的容器所暴露(EXPOSE)的端口号,即具体业务进程在容器内的targetPort上提供TCP/IP接入
  • Kubernetes Service支持多个Endpoint,在存在多个Endpoint的情况下,要求每个Endpoint都定义一个名称来区分
  • 大部分分布式系统都通过提供特定的API接口来实现服务发现功能,但这样做会导致平台的侵入性比较强,也增加了开发、测试的难度
  • 后来Kubernetes通过Add-On增值包引入了DNS系统,把服务名作为DNS域名,这样程序就可以直接使用服务名来建立通信连接了
  • Pod IP是每个Pod的IP地址,它是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络
  • NodePort的实现方式是在Kubernetes集群里的每个Node上都为需要外部访问的Service开启一个对应的TCP监听端口,外部系统只要用任意一个Node的IP地址+具体的NodePort端口号即可访问此服务,
  • Kubernetes在1.5版本之后又提供了类似crontab的定时任务——CronJob,解决了某些批处理任务需要定时反复执行的问题。
  • Kubernetes中的Volume与Pod的生命周期相同,但与容器的生命周期不相关,当容器终止或者重启时,Volume中的数据也不会丢失
  • Kubernetes的Volume还扩展出了一种非常有实用价值的功能,即容器配置文件集中化定义与管理,这是通过ConfigMap这种新的资源对象来实现的
  • 之前提到的Volume是被定义在Pod上的,属于计算资源的一部分,而实际上,网络存储是相对独立于计算资源而存在的一种实体资源。
  • 比较重要的是PV的accessModes属性,目前有以下类型。 ◎ ReadWriteOnce:读写权限,并且只能被单个Node挂载。 ◎ ReadOnlyMany:只读权限,允许被多个Node挂载。 ◎ ReadWriteMany:读写权限,允许被多个Node挂载。
  • Namespace在很多情况下用于实现多租户的资源隔离
  • 如果不加参数,则kubectl get命令将仅显示属于default命名空间的资源对象。
  • Annotation(注解)与Label类似,也使用key/value键值对的形式进行定义。不同的是Label具有严格的命名规则,它定义的是Kubernetes对象的元数据(Metadata),并且用于Label Selector。Annotation则是用户任意定义的附加信息,以便于外部工具查找。
  • Kubernetes提供了一种内建机制,将存储在etcd中的ConfigMap通过Volume映射的方式变成目标Pod内的配置文件,不管目标Pod被调度到哪台服务器上,都会完成自动映射

2.2 使用kubeadm工具快速安装Kubernetes集群

  • Kubernetes从1.4版本开始引入了命令行工具kubeadm,致力于简化集群的安装过程,并解决Kubernetes集群的高可用问题
  • kubeadm将配置文件以ConfigMap的形式保存到集群之中,便于后续的查询和升级工作
  • 如果安装失败,则可以执行kubeadm reset命令将主机恢复原状,重新执行kubeadm init命令,再次进行安装。

2.4 Kubernetes集群的安全设置

  • etes提供了基于CA签名的双向数字证书认证方式和简单的基于HTTP Base或Token的认证方式,其中CA证书方式的安全性最高。本节先介绍如
  • kubectl命令行工具比较特殊,它同时支持CA双向认证和简单认证两种模式与API Server通信,其他客户端组件只能配置为双向安全认证或非安全模式与API Server通信。

2.6 内网中的Kubernetes相关配置

  • 由于在Kubernetes中是以Pod而不是以Docker容器为管理单元的,在kubelet创建Pod时,还通过启动一个名为k8s.gcr.io/pause:3.1的镜像来实现Pod的概念。

2.7 Kubernetes的版本升级

  • Kubernetes的版本升级需要考虑到不要让当前集群中正在运行的容器受到影响。应对集群中的各Node逐个进行隔离,然后等待在其上运行的容器全部执行完成,再更新该Node上的kubelet和kube-proxy服务,将全部Node都更新完成后,再更新Master的服务。

2.9 CRI(容器运行时接口)详解

  • CRI包含Protocol Buffers、gRPC API、运行库支持及开发中的标准规范和工具
  • kubelet使用gRPC框架通过UNIX Socket与容器运行时(或CRI代理)进行通信
  • Pod由一组应用容器组成,其中包含共有的环境和资源约束
  • kubelet的职责在于通过RPC管理容器的生命周期,实现容器生命周期的钩子,存活和健康监测,以及执行Pod的重启策略等。

3.1 Pod定义详解

  • 表3.1 对Pod定义文件模板中各属性的详细说明

3.2 Pod的基本用法

  • Kubernetes系统中对长时间运行容器的要求是:其主程序需要一直在前台执行。
  • 对于无法改造为前台执行的应用,也可以使用开源工具Supervisor辅助进行前台运行的功能。Supervisor提供了一种可以同时启动多个后台应用,并保持Supervisor自身在前台执行的机制,可以满足Kubernetes对容器的启动要求。
  • Pod可以由1个或多个容器组合而成
  • 属于同一个Pod的多个容器应用之间相互访问时仅需要通过localhost就可以通信

3.3 静态Pod

  • 静态Pod是由kubelet进行管理的仅存在于特定Node上的Pod
  • 由于静态Pod无法通过API Server直接管理,所以在Master上尝试删除这个Pod时,会使其变成Pending状态,且不会被删除。
  • 删除该Pod的操作只能是到其所在Node上将其定义文件static-web.yaml从/etc/kubelet.d目录下删除。
  • 通过设置kubelet的启动参数“--manifest-url”,kubelet将会定期从该URL地址下载Pod的定义文件,并以.yaml或.json文件的格式进行解析,然后创建Pod。

3.4 Pod容器共享Volume

  • 同一个Pod中的多个容器能够共享Pod级别的存储卷Volume。

3.5 Pod的配置管理

  • 应用部署的一个最佳实践是将应用所需的配置信息与程序进行分离,这样可以使应用程序被更好地复用,通过不同的配置也能实现更灵活的功能
  • ConfigMap供容器使用的典型用法如下。 (1)生成为容器内的环境变量。 (2)设置容器启动命令的启动参数(需设置为环境变量)。 (3)以Volume的形式挂载为容器内部的文件或目录。
  • 不使用YAML文件,直接通过kubectl create configmap也可以创建ConfigMap,可以使用参数--from-file或--from-literal指定内容,并且可以在一行命令中指定多个参数。
  • 通过Volume挂载的方式将ConfigMap中的内容挂载为容器内部的文件或目录。
  • Kubernetes从1.6版本开始,引入了一个新的字段envFrom,实现了在Pod环境中将ConfigMap(也可用于Secret资源对象)中所有定义的key=value自动生成为环境变量:
  • ConfigMap受Namespace限制,只有处于相同Namespace中的Pod才可以引用它。

3.6 在容器内获取Pod信息(Downward API)

  • 在容器内获取Pod信息(Downward API)
  • Downward API可以通过以下两种方式将Pod信息注入容器内部。(1)环境变量:用于单个变量,可以将Pod信息和Container信息注入容器内部。(2)Volume挂载:将数组类信息生成为文件并挂载到容器内部。
  • 在某些集群中,集群中的每个节点都需要将自身的标识(ID)及进程绑定的IP地址等信息事先写入配置文件中,进程在启动时会读取这些信息,然后将这些信息发布到某个类似服务注册中心的地方,以实现集群节点的自动发现功能。

3.8 Pod健康检查和服务可用性检查

  • LivenessProbe探针:用于判断容器是否存活(Running状态)
  • (1)ExecAction:在容器内部执行一个命令,如果该命令的返回码为0,则表明容器健康。
  • HTTPGetAction:通过容器的IP地址、端口号及路径调用HTTP Get方法,如果响应的状态码大于等于200且小于400,则认为容器健康。
  • initialDelaySeconds:启动容器后进行首次健康检查的等待时间,单位为s。

3.9 玩转Pod调度

  • RC也出现了新的继任者——Deployment,用于更加自动地完成Pod副本的部署、版本更新、回滚等功能。
  • 我们不应该直接使用底层的ReplicaSet来控制Pod副本,而应该使用管理ReplicaSet的Deployment对象来控制副本,这是来自官方的建议。
  • 在Kubernates 1.9之前,在RC等对象被删除后,它们所创建的Pod副本都不会被删除;在Kubernates 1.9以后,这些Pod副本会被一并删除。如果不希望这样做,则可以通过kubectl命令的--cascade=false参数来取消这一默认特性
  • Deployment或RC的主要功能之一就是自动部署一个容器应用的多份副本,以及持续监控副本的数量,在集群内始终维持用户指定的副本数量。
  • 用户只需在Pod的定义中使用NodeSelector、NodeAffinity、PodAffinity、Pod驱逐等更加细粒度的调度策略设置,就能完成对Pod的精准调度
  • NodeSelector:定向调度
  • 首先通过kubectl label命令给目标Node打上一些标签
  • 需要注意的是,如果我们指定了Pod的nodeSelector条件,且在集群中不存在包含相应标签的Node,则即使在集群中还有其他可供使用的Node,这个Pod也无法被成功调度。
  • 亲和性调度功能包括节点亲和性(NodeAffinity)和Pod亲和性(PodAffinity)两个维度的设置
  • NodeSelector将会继续使用,随着节点亲和性越来越能够表达nodeSelector的功能,最终NodeSelector会被废弃。
  • NodeAffinity意为Node亲和性的调度策略,是用于替换NodeSelector的全新调度策略
  • NodeAffinity规则设置的注意事项如下。◎ 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能最终运行在指定的Node上。◎ 如果nodeAffinity指定了多个nodeSelectorTerms,那么其中一个能够匹配成功即可。◎ 如果在nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有matchExpressions才能运行该Pod。
  • PodAffinity:Pod亲和与互斥调度策略
  • 根据在节点上正在运行的Pod的标签而不是节点的标签进行判断和调度,要求对节点和Pod两个条件进行匹配。这种规则可以描述为:如果在具有标签X的Node上运行了一个或者多个符合条件Y的Pod,那么Pod应该(如果是互斥的情况,那么就变成拒绝)运行在这个Node上。
  • Pod的亲和性被定义于PodSpec的affinity字段下的podAffinity子字段中。Pod间的互斥性则被定义于同一层次的podAntiAffinity子字段中。
  • 与节点亲和性类似,Pod亲和性的操作符也包括In、NotIn、Exists、DoesNotExist、Gt、Lt。
  • Taints和Tolerations(污点和容忍)
  • Taint需要和Toleration配合使用,让Pod避开那些不合适的Node。在Node上设置一个或多个Taint之后,除非Pod明确声明能够容忍这些污点,否则无法在这些Node上运行。Toleration是Pod的属性,让Pod能够(注意,只是能够,而非必须)运行在标注了Taint的Node上。
  • Kubernetes调度器处理多个Taint和Toleration的逻辑顺序为:首先列出节点中所有的Taint,然后忽略Pod的Toleration能够匹配的部分,剩下的没有忽略的Taint就是对Pod的效果了。
  • Pod Priority Preemption:Pod优先级调度
  • Kubernetes 1.8版本引入了基于Pod优先级抢占(Pod Priority Preemption)的调度策略,此时Kubernetes会尝试释放目标节点上低优先级的Pod,以腾出空间(资源)安置高优先级的Pod,这种调度方式被称为“抢占式调度”。
  • 因此优先级调度不但增加了系统的复杂性,还可能带来额外不稳定的因素。
  • DaemonSet是Kubernetes 1.2版本新增的一种资源对象,用于管理在集群中每个Node上仅运行一份Pod的副本实例

3.10 Init Container(初始化容器)

  • init container与应用容器在本质上是一样的,但它们是仅运行一次就结束的任务,并且必须在成功执行完成后,系统才能继续执行下一个容器
  • init container的运行方式与应用容器不同,它们必须先于应用容器执行完成,当设置了多个init container时,将按顺序逐个运行,并且只有前一个init container运行成功后才能运行后一个init container。当所有init container都成功运行后,Kubernetes才会初始化Pod的各种信息,并开始创建和运行应用容器。

3.11 Pod的升级和回滚

  • 如果Pod是通过Deployment创建的,则用户可以在运行时修改Deployment的Pod定义(spec.template)或镜像名称,并应用到Deployment对象上,系统即可完成Deployment的自动更新操作。如果在更新过程中发生了错误,则还可以通过回滚操作恢复Pod的版本。
  • 可以使用kubectl rollout status命令查看Deployment的更新过程:
  • 当更新Deployment时,系统创建了一个新的ReplicaSet(nginx-deployment-3599678771),并将其副本数量扩展到1,然后将旧的ReplicaSet缩减为2。之后,系统继续按照相同的更新策略对新旧两个ReplicaSet进行逐个调整。最后,新的ReplicaSet运行了3个新版本Pod副本,旧的ReplicaSet副本数量则缩减为0
  • 添加标签选择器是无法向后兼容的,这意味着新的标签选择器不会匹配和使用旧选择器创建的ReplicaSets和Pod,因此添加选择器将会导致所有旧版本的ReplicaSets和由旧ReplicaSets创建的Pod处于孤立状态(不会被系统自动删除,也不受新的ReplicaSet控制)。
  • 对于一次复杂的Deployment配置修改,为了避免频繁触发Deployment的更新操作,可以先暂停Deployment的更新操作,然后进行配置修改,再恢复Deployment,一次性触发完整的更新操作,就可以避免不必要的Deployment更新操作了。
  • 可以看出,RC的滚动升级不具有Deployment在应用版本升级过程中的历史记录、新旧版本数量的精细控制等功能,在Kubernetes的演进过程中,RC将逐渐被RS和Deployment所取代,建议用户优先考虑使用Deployment完成Pod的部署和升级操作。

3.12 Pod的扩缩容

  • HPA控制器基于Master的kube-controller-manager服务启动参数--horizontal-pod-autoscaler-sync-period定义的探测周期(默认值为15s),周期性地监测目标Pod的资源性能指标,并与HPA资源对象中的扩缩容条件进行对比,在满足条件时对Pod副本数量进行调整。

第4章 深入掌握Service

  • 通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上

4.2 Service的基本用法

  • 直接通过Pod的IP地址和端口号可以访问到容器应用内的服务,但是Pod的IP地址是不可靠的,例如当Pod所在的Node发生故障时,Pod将被Kubernetes重新调度到另一个Node,Pod的IP地址将发生变化。更重要的是,如果容器应用本身是分布式的部署方式,通过多个实例共同提供服务,就需要在这些实例的前端设置一个负载均衡器来实现请求的分发。Kubernetes中的Service就是用于解决这些问题的核心组件。
  • Service定义中的关键字段是ports和selector
  • 目前Kubernetes提供了两种负载分发策略:RoundRobin和SessionAffinity
  • 在某些环境中,应用系统需要将一个外部数据库作为后端服务进行连接,或将另一个集群或Namespace中的服务作为服务的后端,这时可以通过创建一个无Label Selector的Service来实现
  • 通过该定义创建的是一个不带标签选择器的Service,即无法选择后端的Pod,系统不会自动创建Endpoint,因此需要手动创建一个和该Service同名的Endpoint,用于指向实际的后端访问地址
  • 访问没有标签选择器的Service和带有标签选择器的Service一样,请求将会被路由到由用户手动定义的后端Endpoint上

4.3 Headless Service

  • Kubernetes提供了Headless Service来实现这种功能,即不为Service设置ClusterIP(入口IP地址),仅通过Label Selector将后端的Pod列表返回给调用的客户端
  • 回顾一下Service的概念。Service通常用作一个负载均衡器,供Kubernetes集群中其他应用(Pod)对属于该Service的一组Pod进行访问

4.4 从集群外部访问Pod或Service

  • 可以将Pod或Service的端口号映射到宿主机,以使客户端应用能够通过物理机访问容器应用。
  • 通过设置容器级别的hostPort,将容器应用的端口号映射到物理机上
  • 通过设置Pod级别的hostNetwork=true,该Pod中所有容器的端口号都将被直接映射到物理机上
  • 通过设置nodePort映射到物理机,同时设置Service的类型为NodePort
  • 通过设置LoadBalancer映射到云服务商提供的LoadBalancer地址

4.6 Ingress:HTTP 7层路由机制

  • 从Kubernetes 1.1版本开始新增Ingress资源对象,用于将不同URL的访问请求转发到后端不同的Service,以实现HTTP层的业务路由机制
  • 在Kubernetes中,Ingress Controller将以Pod的形式运行,监控API Server的/ingress接口后端的backend services,如果Service发生变化,则Ingress Controller应自动更新其转发规则。
  • 该Ingress Controller以daemonset的形式进行创建,在每个Node上都将启动一个Nginx服务。

第5章 核心组件运行机制

  • Kubernetes API Server的核心功能是提供Kubernetes各类资源对象(如Pod、RC、Service等)的增、删、改、查及Watch等HTTP Rest接口,成为集群内各个功能模块之间数据交互和通信的中心枢纽,是整个系统的数据总线和数据中心
  • Kubernetes API Server通过一个名为kube-apiserver的进程提供服务,该进程运行在Master上。
  • 如果只想对外暴露部分REST服务,则可以在Master或其他节点上运行kubectl proxy进程启动一个内部代理来实现。
  • Kubernetes API Server本身也是一个Service,它的名称就是kubernetes,并且它的Cluster IP地址是Cluster IP地址池里的第1个地址
  • 由于API Server是Kubernetes集群数据的唯一访问入口,因此安全性与高性能就成为API Server设计和实现的两大核心目标。通过采用HTTPS安全传输通道与CA签名数字证书强制双向认证的方式,API Server的安全性得以保障。

5.2 Controller Manager原理解析

  • Controller Manager是Kubernetes中各种操作系统的管理者,是集群内部的管理控制中心,也是Kubernetes自动化功能的核心。
  • Replication Controller的核心作用是确保在任何时候集群中某个RC关联的Pod副本数量都保持预设值。
  • 总结一下Replication Controller的职责,如下所述。 (1)确保在当前集群中有且仅有N个Pod实例,N是在RC中定义的Pod副本数量。 (2)通过调整RC的spec.replicas属性值来实现系统扩容或者缩容。 (3)通过改变RC中的Pod模板(主要是镜像版本)来实现系统的滚动升级。
  • 资源配额管理确保了指定的资源对象在任何时候都不会超量占用系统物理资源,避免了由于某些业务进程的设计或实现的缺陷导致整个系统运行紊乱甚至意外宕机,对整个集群的平稳运行和稳定性有非常重要的作用。

5.3 Scheduler原理解析

  • 在整个调度过程中涉及三个对象,分别是待调度Pod列表、可用Node列表,以及调度算法和策略。

5.4 kubelet运行机制解析

  • 每个kubelet进程都会在API Server上注册节点自身的信息,定期向Master汇报节点资源的使用情况,并通过cAdvisor监控容器和节点资源。
  • 所有以非API Server方式创建的Pod都叫作Static Pod
  • kubelet通过API Server Client使用Watch加List的方式监听“/registry/nodes/$”当前节点的名称和“/registry/pods”目录,将获取的信息同步到本地缓存中。
  • kubelet监听etcd,所有针对Pod的操作都会被kubelet监听。如果发现有新的绑定到本节点的Pod,则按照Pod清单的要求创建该Pod。
  • 用“kubernetes/pause”镜像为每个Pod都创建一个容器。该Pause容器用于接管Pod中所有其他容器的网络。每创建一个新的Pod,kubelet都会先创建一个Pause容器,然后创建其他容器。“kubernetes/pause”镜像大概有200KB,是个非常小的容器镜像。
  • 在新的Kubernetes监控体系中,Metrics Server用于提供Core Metrics(核心指标),包括Node和Pod的CPU和内存使用数据。其他Custom Metrics(自定义指标)则由第三方组件(如Prometheus)采集和存储。

5.5 kube-proxy运行机制解析

  • Kubernetes在创建服务时会为服务分配一个虚拟的IP地址,客户端通过访问这个虚拟的IP地址来访问服务,服务则负责将请求转发到后端的Pod上
  • Service只是一个概念,而真正将Service的作用落实的是它背后的kube-proxy服务进程
  • 在Kubernetes集群的每个Node上都会运行一个kube-proxy服务进程,我们可以把这个进程看作Service的透明代理兼负载均衡器,其核心功能是将到某个Service的访问请求转发到后端的多个Pod实例上
  • 当某个Pod以Cluster IP方式访问某个Service的时候,这个流量会被Pod所在本机的iptables转发到本机的kube-proxy进程,然后由kube-proxy建立起到后端Pod的TCP/UDP连接,随后将请求转发到某个后端Pod上,并在这个过程中实现负载均衡功能。
  • iptables模式虽然实现起来简单,但存在无法避免的缺陷:在集群中的Service和Pod大量增加以后,iptables中的规则会急速膨胀,导致性能显著下降,在某些极端情况下甚至会出现规则丢失的情况,并且这种故障难以重现与排查,于是Kubernetes从1.8版本开始引入第3代的IPVS(IP VirtualServer)模式,如图5.16所示。IPVS在Kubernetes 1.11中升级为GA稳定版。

6.2 API Server授权管理

  • 角色只能对命名空间内的资源进行授权

6.3 Admission Control

  • Admission Control配备了一个准入控制器的插件列表,发送给API Server的任何请求都需要通过列表中每个准入控制器的检查,检查不通过,则API Server拒绝此调用请求

6.4 Service Account

  • Service Account也是一种账号,但它并不是给Kubernetes集群的用户(系统管理员、运维人员、租户用户等)用的,而是给运行在Pod里的进程用的,它为Pod里的进程提供了必要的身份证明。

7.1 Kubernetes网络模型

  • 每个Pod都拥有一个独立的IP地址,并假定所有Pod都在一个可以直接连通的、扁平的网络空间中
  • IP-per-Pod模型
  • IP-per-Pod模式和Docker原生的通过动态端口映射方式实现的多节点访问模式有什么区别呢?主要区别是后者的动态端口映射会引入端口管理的复杂性,而且访问者看到的IP地址和端口与服务提供者实际绑定的不同(因为NAT的缘故,它们都被映射成新的地址或端口了),这也会引起应用配置的复杂化。

7.2 Docker网络基础

  • 网络命名空间(Network Namespace)、Veth设备对、网桥、ipatables和路由。
  • Docker正是利用了网络的命名空间特性,实现了不同容器之间的网络隔离。
  • 为了隔离出独立的协议栈,需要纳入命名空间的元素有进程、套接字、网络设备等。
  • Docker容器中的各类网络栈设备都是Docker Daemon在启动时自动创建和配置的。
  • Netfilter负责在内核中执行各种挂接的规则,运行在内核模式中;而iptables是在用户模式下运行的进程,负责协助和维护内核中Netfilter的各种规则表。二者互相配合来实现整个Linux网络协议栈中灵活的数据包处理机制。
  • 路由功能由IP层维护的一张路由表来实现

7.3 Docker的网络实现

  • 在Kubernetes管理模式下通常只会使用bridge模式
  • 在bridge模式下,Docker Daemon第1次启动时会创建一个虚拟的网桥,默认的名称是docker0

7.4 Kubernetes的网络实现

  • 同一个Pod内的容器(Pod内的容器是不会跨宿主机的)共享同一个网络命名空间,共享同一个Linux协议栈。
  • Pod的地址是与docker0在同一个网段的,我们知道docker0网段与宿主机网卡是两个完全不同的IP网段,并且不同Node之间的通信只能通过宿主机的物理网卡进行,因此要想实现不同Node上Pod容器之间的通信,就必须想办法通过主机的这个IP地址进行寻址和通信。

7.6 CNI网络模型

  • CNM模型主要通过Network Sandbox、Endpoint和Network这3个组件进行实现

7.7 Kubernetes网络策略

  • Network Policy的主要功能是对Pod间的网络通信进行限制和准入控制,设置方式为将Pod的Label作为查询条件,设置允许访问或禁止访问的客户端Pod列表

第8章 共享存储原理

  • PersistentVolume(PV)和PersistentVolumeClaim(PVC)两个资源对象来实现对存储的管理子系统。
  • PV是对底层网络共享存储的抽象,将共享存储定义为一种“资源
  • 通过StorageClass的定义,管理员可以将存储资源定义为某种类别(Class),正如存储设备对于自身的配置描述(Profile),例如“快速存储”“慢速存储”“有数据冗余”“无数据冗余”等。

8.2 PV详解

  • PV作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略、后端存储类型等关键信息的设置

8.5 StorageClass详解

  • 基于StorageClass的动态资源供应模式将逐步成为云平台的标准存储配置模式。
  • StorageClass一旦被创建出来,则将无法修改。如需更改,则只能删除原StorageClass的定义重建。

9.1 REST简述

  • REST本身只是为分布式超媒体系统设计的一种架构风格,而不是标准。

第10章 Kubernetes集群管理

  • 需要注意的是,将某个Node脱离调度范围时,在其上运行的Pod并不会自动停止,管理员需要手动停止在该Node上运行的Pod。
  • 使用kubectl的子命令cordon和uncordon也可以实现将Node进行隔离调度和恢复调度操作。

10.4 Kubernetes资源管理

  • LimitRange及ResourceQuota,前者解决request与limit参数的默认值和合法取值范围等问题,后者则解决约束租户的资源配额问题。

10.9 集群统一日志管理

  • Kubernetes推荐采用Fluentd+Elasticsearch+Kibana完成对系统和容器日志的采集、查询和展现工作

11.2 查看容器日志

  • 如果在某个Pod中包含多个容器,就需要通过-c参数指定容器的名称来查看