K8S常用组件

Kubernetes(K8S)中文文档_Kubernetes中文社区

K8S是什么?

Kubernetes是容器集群管理系统,是一个开源的平台,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。

节点:Kubernetes集群中的一台物理机或者虚拟机。
集群:位于Internet防火墙后的节点,这是kubernetes管理的主要计算资源。
边界路由器:为集群强制执行防火墙策略的路由器。 这可能是由云提供商或物理硬件管理的网关。
集群网络:一组逻辑或物理链接,可根据Kubernetes网络模型实现群集内的通信。 集群网络的实现包括Overlay模型的 flannel 和基于SDN的OVS。
服务:使用标签选择器标识一组pod成为的Kubernetes服务。 除非另有说明,否则服务假定在集群网络内仅可通过虚拟IP访问。

Master组件

Master组件提供集群的管理控制中心。

Master组件可以在集群中任何节点上运行。但是为了简单起见,通常在一台VM/机器上启动所有Master组件,并且不会在此VM/机器上运行用户容器。

kube-apiserver

kube-apiserver用于暴露Kubernetes API。任何的资源请求/调用操作都是通过kube-apiserver提供的接口进行。

ETCD

etcd是Kubernetes提供默认的存储系统,保存所有集群数据,使用时需要为etcd数据提供备份计划。

kube-controller-manager

kube-controller-manager运行管理控制器,它们是集群中处理常规任务的后台线程。逻辑上,每个控制器是一个单独的进程,但为了降低复杂性,它们都被编译成单个二进制文件,并在单个进程中运行。

常见控制器:

节点(Node)控制器。
副本(Replication)控制器:负责维护系统中每个副本中的pod。
端点(Endpoints)控制器:填充Endpoints对象(即连接Services&Pods)。
Service Account和Token控制器:为新的Namespace 创建默认帐户访问API Token。

cloud-controller-manager

云控制器管理器负责与底层云提供商的平台交互。云控制器管理器是Kubernetes版本1.6中引入的,目前还是Alpha的功能。

云控制器管理器仅运行云提供商特定的(controller loops)控制器循环。可以通过将--cloud-provider flag设置为external启动kube-controller-manager ,来禁用控制器循环。

cloud-controller-manager 具体功能:
节点(Node)控制器
路由(Route)控制器
Service控制器
卷(Volume)控制器

kube-scheduler

kube-scheduler 监视新创建没有分配到Node的Pod,为Pod选择一个Node。

插件addons

插件(addon)是实现集群pod和Services功能的 。Pod由Deployments,ReplicationController等进行管理。Namespace 插件对象是在kube-system Namespace中创建。

DNS

虽然不严格要求使用插件,但Kubernetes集群都应该具有集群 DNS。
群集 DNS是一个DNS服务器,能够为 Kubernetes services提供 DNS记录。
由Kubernetes启动的容器自动将这个DNS服务器包含在他们的DNS searches中。

用户界面

kube-ui提供集群状态基础信息查看。

容器资源监测

容器资源监控提供一个UI浏览监控数据。

Cluster-level Logging

Cluster-level logging,负责保存容器日志,搜索/查看日志。

Node组件

节点组件运行在Node,提供Kubernetes运行时环境,以及维护Pod。

kubelet

kubelet是主要的节点代理,它会监视已分配给节点的pod,具体功能:
安装Pod所需的volume。
下载Pod的Secrets。
Pod中运行的 docker(或experimentally,rkt)容器。
定期执行容器健康检查。
如有必要,通过创建镜像 pod,将 pod 的状态报告回系统的其余部分。
将节点的状态报告回系统的其余部分。

kube-proxy

kube-proxy通过在主机上维护网络规则并执行连接转发来实现Kubernetes服务抽象。

docker

docker用于运行容器。

RKT

rkt运行容器,作为docker工具的替代方案。

supervisord

supervisord是一个轻量级的监控系统,用于保障kubelet和docker运行。

fluentd

fluentd是一个守护进程,可提供cluster-level logging.。

K8S对象

Kubernetes对象是Kubernetes系统中的持久实体。Kubernetes使用这些实体来表示集群的状态。具体来说,他们可以描述:

容器化应用正在运行(以及在哪些节点上)
这些应用可用的资源
关于这些应用如何运行的策略,如重新策略,升级和容错
Kubernetes对象是“record of intent”,一旦创建了对象,Kubernetes系统会确保对象存在。通过创建对象,可以有效地告诉Kubernetes系统你希望集群的工作负载是什么样的。

要使用Kubernetes对象(无论是创建,修改还是删除),都需要使用Kubernetes API。例如,当使用kubectl命令管理工具时,CLI会为提供Kubernetes API调用。你也可以直接在自己的程序中使用Kubernetes API,Kubernetes提供一个golang客户端库 (其他语言库正在开发中-如Python)。

对象(Object)规范和状态

每个Kubernetes对象都包含两个嵌套对象字段,用于管理Object的配置:Object Spec和Object Status。Spec描述了对象所需的状态 - 希望Object具有的特性,Status描述了对象的实际状态,并由Kubernetes系统提供和更新。

描述Kubernetes对象

在Kubernetes中创建对象时,必须提供描述其所需Status的对象Spec,以及关于对象(如name)的一些基本信息。当使用Kubernetes API创建对象(直接或通过kubectl)时,该API请求必须将该信息作为JSON包含在请求body中。通常,可以将信息提供给kubectl .yaml文件,在进行API请求时,kubectl将信息转换为JSON

K8S Labels 和 Selectors

Labels其实就一对 key/value ,被关联到对象上,标签的使用我们倾向于能够标示对象的特殊特点,并且对用户而言是有意义的(见名知意),但是标签对内核系统是没有直接意义的。标签可以用来划分特定组的对象(比如,所有女的),标签可以在创建一个对象的时候直接给与,也可以在后期随时修改,每一个对象可以拥有多个标签,但是,key值必须是唯一的

Labels的作用

Labels可以让用户将他们自己的有组织目的的结构以一种松耦合的方式应用到系统的对象上,且不需要客户端存放这些对应关系(mappings)。

Lables语法和字符集

Label其实是一对 key/value。有效的标签键有两个段:可选的前缀和名称,用斜杠(/)分隔,名称段是必需的,最多63个字符,以[a-z0-9A-Z]带有虚线(-)、下划线(_)、点(.)和开头和结尾必须是字母或数字(都是字符串形式)的形式组成。前缀是可选的。如果指定了前缀,那么必须是DNS子域:一系列的DNSlabel通过”.”来划分,不超过253个字符,以斜杠(/)结尾。如果前缀被省略了,这个Label的key被假定为对用户私有的。自动化系统组件有(例如kube-scheduler,kube-controller-manager,kube-apiserver,kubectl,或其他第三方自动化),这些添加标签终端用户对象都必须指定一个前缀。Kuberentes.io 前缀是为Kubernetes 内核部分保留的。

有效的标签值最长为63个字符。要么为空,要么使用[a-z0-9A-Z]带有虚线(-)、下划线(_)、点(.)和开头和结尾必须是字母或数字(都是字符串形式)的形式组成。

Labels选择器

与Name和UID 不同,标签不需要有唯一性。一般来说,我们期望许多对象具有相同的标签。
通过标签选择器(Labels Selectors),客户端/用户 能方便辨识出一组对象。标签选择器是kubernetes中核心的组成部分。

API目前支持两种选择器:equality-based(基于平等)和set-based(基于集合)的。标签选择器可以由逗号分隔的多个requirements 组成。在多重需求的情况下,必须满足所有要求,因此逗号分隔符作为AND逻辑运算符。

一个为空的标签选择器(即有0个必须条件的选择器)会选择集合中的每一个对象。
一个null型标签选择器(仅对于可选的选择器字段才可能)不会返回任何对象。
注意:两个控制器的标签选择器不能在命名空间中重叠。

equality-based

基于相等的或者不相等的条件允许用标签的keys和values进行过滤。匹配的对象必须满足所有指定的标签约束,尽管他们可能也有额外的标签。有三种运算符是允许的,“=”,“==”和“!=”。前两种代表相等性(他们是同义运算符),后一种代表非相等性。

set-based

set-based 的标签条件允许用一组value来过滤key。支持三种操作符: in , notin 和 exists(仅针对于key符号) 。
类似的,set-based 中的逗号操作符相当于一个AND操作符。

Set-based的条件可以与Equality-based的条件结合。

Volume

默认情况下容器中的磁盘文件是非持久化的,对于运行在容器中的应用来说面临两个问题,第一:当容器挂掉kubelet将重启启动它时,文件将会丢失;第二:当Pod中同时运行多个容器,容器之间需要共享文件时。Kubernetes的Volume解决了这两个问题。

要使用Volume,pod需要指定Volume的类型和内容(spec.volumes字段),和映射到容器的位置(spec.containers.volumeMounts字段)。

Annotations

可以使用Kubernetes Annotations将任何非标识metadata附加到对象。客户端(如工具和库)可以检索此metadata。

可以使用Labels或Annotations将元数据附加到Kubernetes对象。标签可用于选择对象并查找满足某些条件的对象集合。相比之下,Annotations不用于标识和选择对象。Annotations中的元数据可以是small 或large,structured 或unstructured,并且可以包括标签不允许使用的字符。

Annotations就如标签一样,也是由key/value组成:
构建、发布的镜像信息,如时间戳,发行ID,git分支,PR编号,镜像hashes和注Registry地址。
一些日志记录、监视、分析或audit repositories。
一些工具信息:例如,名称、版本和构建信息。
用户或工具/系统来源信息,例如来自其他生态系统组件对象的URL。
负责人电话/座机,或一些信息目录。

注意:Annotations不会被Kubernetes直接使用,其主要目的是方便用户阅读查找。

Node是什么

Node

Node是Kubernetes中的工作节点,最开始被称为minion。一个Node可以是VM或物理机。每个Node(节点)具有运行pod的一些必要服务,并由Master组件进行管理,Node节点上的服务包括Docker、kubelet和kube-proxy。

Node Status

节点的状态信息包含:
Addresses
Phase (已弃用)
Condition
Capacity
Info

Addresses

这些字段的使用取决于云提供商或裸机配置。
HostName:可以通过kubelet 中 --hostname-override参数覆盖。
ExternalIP:可以被集群外部路由到的IP。
InternalIP:只能在集群内进行路由的节点的IP地址。

Phase

不推荐使用,已弃用。

Condition

conditions字段描述所有Running节点的状态。

Capacity

描述节点上可用的资源:CPU、内存和可以调度到节点上的最大pod数。

Info

关于节点的一些基础信息,如内核版本、Kubernetes版本(kubelet和kube-proxy版本)、Docker版本(如果有使用)、OS名称等。信息由Kubelet从节点收集。

Management

与 pods 和 services 不同,节点不是由Kubernetes 系统创建,它是由Google Compute Engine等云提供商在外部创建的,或使用物理和虚拟机。这意味着当Kubernetes创建一个节点时,它只是创建一个代表节点的对象,创建后,Kubernetes将检查节点是否有效。

Pod是什么

Pod

Pod是Kubernetes创建或部署的最小/最简单的基本单位,一个Pod代表集群上正在运行的一个进程。

一个Pod封装一个应用容器(也可以有多个容器),存储资源、一个独立的网络IP以及管理控制容器运行方式的策略选项。Pod代表部署的一个单位:Kubernetes中单个应用的实例,它可能由单个容器或多个容器共享组成的资源。

Kubernetes中的Pod使用可分两种主要方式:

Pod中运行一个容器。“one-container-per-Pod”模式是Kubernetes最常见的用法; 在这种情况下,你可以将Pod视为单个封装的容器,但是Kubernetes是直接管理Pod而不是容器。
Pods中运行多个需要一起工作的容器。Pod可以封装紧密耦合的应用,它们需要由多个容器组成,它们之间能够共享资源,这些容器可以形成一个单一的内部service单位 - 一个容器共享文件,另一个“sidecar”容器来更新这些文件。Pod将这些容器的存储资源作为一个实体来管理。

每个Pod都是运行应用的单个实例,如果需要水平扩展应用(例如,运行多个实例),则应该使用多个Pods,每个实例一个Pod。在Kubernetes中,这样通常称为Replication。Replication的Pod通常由Controller创建和管理。

Pods如何管理多个容器

Pods的设计可用于支持多进程的协同工作(作为容器),形成一个cohesive的Service单位。Pod中的容器在集群中Node上被自动分配,容器之间可以共享资源、网络和相互依赖关系,并同时被调度使用。

Pods提供两种共享资源:网络和存储。

网络

每个Pod被分配一个独立的IP地址,Pod中的每个容器共享网络命名空间,包括IP地址和网络端口。Pod内的容器可以使用localhost相互通信。当Pod中的容器与Pod 外部通信时,他们必须协调如何使用共享网络资源(如端口)。

存储

Pod可以指定一组共享存储volumes。Pod中的所有容器都可以访问共享volumes,允许这些容器共享数据。volumes 还用于Pod中的数据持久化,以防其中一个容器需要重新启动而丢失数据。有关Kubernetes如何在Pod中实现共享存储的更多信息,请参考Volumes。

使用Pod

你很少会直接在kubernetes中创建单个Pod。因为Pod的生命周期是短暂的,用后即焚的实体。当Pod被创建后(不论是由你直接创建还是被其他Controller),都会被Kuberentes调度到集群的Node上。直到Pod的进程终止、被删掉、因为缺少资源而被驱逐、或者Node故障之前这个Pod都会一直保持在那个Node上。

注意:重启Pod中的容器跟重启Pod不是一回事。Pod只提供容器的运行环境并保持容器的运行状态,重启容器不会造成Pod重启。

Pod不会自愈。如果Pod运行的Node故障,或者是调度器本身故障,这个Pod就会被删除。同样的,如果Pod所在Node缺少资源或者Pod处于维护状态,Pod也会被驱逐。Kubernetes使用更高级的称为Controller的抽象层,来管理Pod实例。虽然可以直接使用Pod,但是在Kubernetes中通常是使用Controller来管理Pod的。

Pod和Controller

Controller可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力。例如,如果一个Node故障,Controller就能自动将该节点上的Pod调度到其他健康的Node上。

Kubernetes Replica Sets

ReplicaSet(RS)是Replication Controller(RC)的升级版本。ReplicaSet 和  Replication Controller之间的唯一区别是对选择器的支持。ReplicaSet支持labels user guide中描述的set-based选择器要求, 而Replication Controller仅支持equality-based的选择器要求。

如何使用ReplicaSet

大多数kubectl 支持Replication Controller 命令的也支持ReplicaSets。rolling-update命令除外,如果要使用rolling-update,请使用Deployments来实现。

虽然ReplicaSets可以独立使用,但它主要被 Deployments用作pod 机制的创建、删除和更新。当使用Deployment时,你不必担心创建pod的ReplicaSets,因为可以通过Deployment实现管理ReplicaSets。

何时使用ReplicaSet

ReplicaSet能确保运行指定数量的pod。然而,Deployment 是一个更高层次的概念,它能管理ReplicaSets,并提供对pod的更新等功能。因此,我们建议你使用Deployment来管理ReplicaSets,除非你需要自定义更新编排。

这意味着你可能永远不需要操作ReplicaSet对象,而是使用Deployment替代管理 。

ReplicaSet as an Horizontal Pod Autoscaler target

ReplicaSet也可以作为 Horizontal Pod Autoscalers (HPA)的目标 。也就是说,一个ReplicaSet可以由一个HPA来自动伸缩。

Kubernetes Deployment

Deployment为Pod和Replica Set(升级版的 Replication Controller)提供声明式更新。

你只需要在 Deployment 中描述您想要的目标状态是什么,Deployment controller 就会帮您将 Pod 和ReplicaSet 的实际状态改变到您的目标状态。您可以定义一个全新的 Deployment 来创建 ReplicaSet 或者删除已有的 Deployment 并创建一个新的来替换。

注意:您不该手动管理由 Deployment 创建的 Replica Set,否则您就篡越了 Deployment controller 的职责!

Deployment典型的用例如下:

使用Deployment来创建ReplicaSet。ReplicaSet在后台创建pod。检查启动状态,看它是成功还是失败。
然后,通过更新Deployment的PodTemplateSpec字段来声明Pod的新状态。这会创建一个新的ReplicaSet,Deployment会按照控制的速率将pod从旧的ReplicaSet移动到新的ReplicaSet中。
如果当前状态不稳定,回滚到之前的Deployment revision。每次回滚都会更新Deployment的revision。
扩容Deployment以满足更高的负载。
暂停Deployment来应用PodTemplateSpec的多个修复,然后恢复上线。
根据Deployment 的状态判断上线是否hang住了。
清除旧的不必要的 ReplicaSet。

创建 Deployment

kubectl create -f https://kubernetes.io/docs/user-guide/nginx-deployment.yaml --record
deployment "nginx-deployment" created

将kubectl的 --record 的 flag 设置为 true可以在 annotation 中记录当前命令创建或者升级了该资源。这在未来会很有用,例如,查看在每个 Deployment revision 中执行了哪些命令。

更新Deployment

注意: Deployment 的 rollout 当且仅当 Deployment 的 pod template(例如.spec.template)中的label更新或者镜像更改时被触发。其他更新,例如扩容Deployment不会触发 rollout。

kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
#让 nginx pod 使用nginx:1.9.1的镜像来代替原来的nginx:1.7.9的镜像。

kubectl edit deployment/nginx-deployment
#可以使用edit命令来编辑 Deployment,修改 .spec.template.spec.containers[0].image ,将nginx:1.7.9 改写成 nginx:1.9.1。

kubectl rollout status deployment/nginx-deployment
#查看 rollout 的状态,只要执行:

Rollover(多个rollout并行)

每当 Deployment controller 观测到有新的 deployment 被创建时,如果没有已存在的 ReplicaSet 来创建期望个数的 Pod 的话,就会创建出一个新的 ReplicaSet 来做这件事。已存在的 ReplicaSet 控制 label 与.spec.selector匹配但是 template 跟.spec.template不匹配的 Pod 缩容。最终,新的 ReplicaSet 将会扩容出.spec.replicas指定数目的 Pod,旧的 ReplicaSet 会缩容到0。

如果您更新了一个的已存在并正在进行中的 Deployment,每次更新 Deployment都会创建一个新的 ReplicaSet并扩容它,同时回滚之前扩容的 ReplicaSet ——将它添加到旧的 ReplicaSet 列表中,开始缩容。

例如,假如您创建了一个有5个niginx:1.7.9 replica的 Deployment,但是当还只有3个nginx:1.7.9的 replica 创建出来的时候您就开始更新含有5个nginx:1.9.1 replica 的 Deployment。在这种情况下,Deployment 会立即杀掉已创建的3个nginx:1.7.9的 Pod,并开始创建nginx:1.9.1的 Pod。它不会等到所有的5个nginx:1.7.9的 Pod 都创建完成后才开始改变航道。

Label selector 更新

我们通常不鼓励更新 label selector,我们建议实现规划好您的 selector。

任何情况下,只要您想要执行 label selector 的更新,请一定要谨慎并确认您已经预料到所有可能因此导致的后果。

增添 selector 需要同时在 Deployment 的 spec 中更新新的 label,否则将返回校验错误。此更改是不可覆盖的,这意味着新的 selector 不会选择使用旧 selector 创建的 ReplicaSet 和 Pod,从而导致所有旧版本的 ReplicaSet 都被丢弃,并创建新的 ReplicaSet。
更新 selector,即更改 selector key 的当前值,将导致跟增添 selector 同样的后果。
删除 selector,即删除 Deployment selector 中的已有的 key,不需要对 Pod template label 做任何更改,现有的 ReplicaSet 也不会成为孤儿,但是请注意,删除的 label 仍然存在于现有的 Pod 和 ReplicaSet 中。

回退Deployment

有时候您可能想回退一个 Deployment,例如,当 Deployment 不稳定时,比如一直 crash looping。

默认情况下,kubernetes 会在系统中保存前两次的 Deployment 的 rollout 历史记录,以便您可以随时回退(您可以修改revision history limit来更改保存的revision数)。

注意: 只要 Deployment 的 rollout 被触发就会创建一个 revision。也就是说当且仅当 Deployment 的 Pod template(如.spec.template)被更改,例如更新template 中的 label 和容器镜像时,就会创建出一个新的 revision。

其他的更新,比如扩容 Deployment 不会创建 revision——因此我们可以很方便的手动或者自动扩容。这意味着当您回退到历史 revision 是,直有 Deployment 中的 Pod template 部分才会回退。

假设我们在更新 Deployment 的时候犯了一个拼写错误,将镜像的名字写成了nginx:1.91,而正确的名字应该是nginx:1.9.1;Rollout 将会卡住(kubectl rollout status deployments nginx-deployment)。按住 Ctrl-C 停止上面的 rollout 状态监控。您会看到旧的 replica(nginx-deployment-1564180365 和 nginx-deployment-2035384211)和新的 replica (nginx-deployment-3066724191)数目都是2个。
看下创建 Pod,您会看到有两个新的 ReplicaSet 创建的 Pod 处于 ImagePullBackOff 状态,循环拉取镜像。
注意,Deployment controller会自动停止坏的 rollout,并停止扩容新的 ReplicaSet。为了修复这个问题,我们需要回退到稳定的 Deployment revision。

检查 Deployment 升级的历史记录

 kubectl rollout history deployment/nginx-deployment
#检查下 Deployment 的 revision
#因为我们创建 Deployment 的时候使用了--recored参数可以记录命令,我们可以很方便的查看每次 revision 的变化。

kubectl rollout history deployment/nginx-deployment --revision=2
#查看单个revision 的详细信息:

回退到历史版本

kubectl rollout undo deployment/nginx-deployment
#回退当前的 rollout 到之前的版本

kubectl rollout undo deployment/nginx-deployment --to-revision=2
#也可以使用 --revision参数指定某个历史版本

清理 Policy

您可以通过设置.spec.revisonHistoryLimit项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment就不允许回退了。

Deployment 扩容

kubectl scale deployment nginx-deployment --replicas 10
#您可以使用以下命令扩容 Deployment

kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80
#假设您的集群中启用了horizontal pod autoscaling,您可以给 Deployment 设置一个 autoscaler,基于当前 Pod的 CPU 利用率选择最少和最多的 Pod 数。

比例扩容

RollingUpdate Deployment 支持同时运行一个应用的多个版本。或者 autoscaler 扩 容 RollingUpdate Deployment 的时候,正在中途的 rollout(进行中或者已经暂停的),为了降低风险,Deployment controller 将会平衡已存在的活动中的 ReplicaSet(有 Pod 的 ReplicaSet)和新加入的 replica。这被称为比例扩容。

例如,您正在运行中含有10个 replica 的 Deployment。maxSurge=3,maxUnavailable=2。您更新了一个镜像,而在集群内部无法解析。镜像更新启动了一个包含ReplicaSet nginx-deployment-1989198191的新的rollout,但是它被阻塞了,因为我们上面提到的maxUnavailable。

然后发起了一个新的Deployment扩容请求。autoscaler将Deployment的repllica数目增加到了15个。Deployment controller需要判断在哪里增加这5个新的replica。如果我们没有谁用比例扩容,所有的5个replica都会加到一个新的ReplicaSet中。如果使用比例扩容,新添加的replica将传播到所有的ReplicaSet中。大的部分加入replica数最多的ReplicaSet中,小的部分加入到replica数少的ReplciaSet中。0个replica的ReplicaSet不会被扩容。

在我们上面的例子中,3个replica将添加到旧的ReplicaSet中,2个replica将添加到新的ReplicaSet中。rollout进程最终会将所有的replica移动到新的ReplicaSet中,假设新的replica成为健康状态。

暂停和恢复Deployment

您可以在发出一次或多次更新前暂停一个 Deployment,然后再恢复它。这样您就能多次暂停和恢复 Deployment,在此期间进行一些修复工作,而不会发出不必要的 rollout。

kubectl get deploy
kubectl get rs
#查看 Deployment

kubectl rollout pause deployment/nginx-deployment
#暂停 Deployment

kubectl set image deploy/nginx nginx=nginx:1.9.1
#更新 Deplyment中的镜像

kubectl rollout history deploy/nginx
#注意新的 rollout 启动了

kubectl set resources deployment nginx -c=nginx --limits=cpu=200m,memory=512Mi
#您可以进行任意多次更新,例如更新使用的资源

kubectl rollout resume deploy nginx
#恢复这个 Deployment

Deployment 暂停前的初始状态将继续它的功能,而不会对 Deployment 的更新产生任何影响,只要 Deployment是暂停的。

注意: 在恢复 Deployment 之前您无法回退一个已经暂停的 Deployment。

Deployment 状态

Kubernetes 将执行过下列任务之一的 Deployment 标记为 progressing 状态:
Deployment 正在创建新的ReplicaSet过程中。
Deployment 正在扩容一个已有的 ReplicaSet。
Deployment 正在缩容一个已有的 ReplicaSet。
有新的可用的 pod 出现。
您可以使用kubectl rollout status命令监控 Deployment 的进度。

完成的 Deployment

Kubernetes 将包括以下特性的 Deployment 标记为 complete 状态:
Deployment 最小可用。最小可用意味着 Deployment 的可用 replica 个数等于或者超过 Deployment 策略中的期望个数。
所有与该 Deployment 相关的replica都被更新到了您指定版本,也就说更新完成。
该 Deployment 中没有旧的 Pod 存在。

kubectl rollout status deploy/nginx
#您可以用kubectl rollout status命令查看 Deployment 是否完成。如果 rollout 成功完成,kubectl rollout status将返回一个0值的 Exit Code。

失败的 Deployment

您的 Deployment 在尝试部署新的 ReplicaSet 的时候可能卡住,用于也不会完成。这可能是因为以下几个因素引起的:
无效的引用
不可读的 probe failure
镜像拉取错误
权限不够
范围限制
程序运行时配置错误

探测这种情况的一种方式是,在您的 Deployment spec 中指定spec.progressDeadlineSeconds。spec.progressDeadlineSeconds 表示 Deployment controller 等待多少秒才能确定(通过 Deployment status)Deployment进程是卡住的。

kubectl patch deployment/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'
#kubectl命令设置progressDeadlineSeconds 使 controller 在 Deployment 在进度卡住10分钟后报告

当超过截止时间后,Deployment controller 会在 Deployment 的 status.conditions中增加一条DeploymentCondition,它包括如下属性:
Type=Progressing
Status=False
Reason=ProgressDeadlineExceeded

注意: kubernetes除了报告Reason=ProgressDeadlineExceeded状态信息外不会对卡住的 Deployment 做任何操作。更高层次的协调器可以利用它并采取相应行动,例如,回滚 Deployment 到之前的版本。

注意: 如果您暂停了一个 Deployment,在暂停的这段时间内kubernetnes不会检查您指定的 deadline。您可以在 Deployment 的 rollout 途中安全的暂停它,然后再恢复它,这不会触发超过deadline的状态。

您可能在使用 Deployment 的时候遇到一些短暂的错误,这些可能是由于您设置了太短的 timeout,也有可能是因为各种其他错误导致的短暂错误。

操作失败的 Deployment

所有对完成的 Deployment 的操作都适用于失败的 Deployment。您可以对它扩/缩容,回退到历史版本,您甚至可以多次暂停它来应用 Deployment pod template。

清理Policy

您可以设置 Deployment 中的 .spec.revisionHistoryLimit 项来指定保留多少旧的 ReplicaSet。 余下的将在后台被当作垃圾收集。默认的,所有的 revision 历史就都会被保留。在未来的版本中,将会更改为2。

注意: 将该值设置为0,将导致所有的 Deployment 历史记录都会被清除,该 Deployment 就无法再回退了。

Kubernetes Replication Controller

注意:建议使用Deployment 配置 ReplicaSet (简称RS)方法来控制副本数。

ReplicationController(简称RC)是确保用户定义的Pod副本数保持不变。

ReplicationController 工作原理

在用户定义范围内,如果pod增多,则ReplicationController会终止额外的pod,如果减少,RC会创建新的pod,始终保持在定义范围。例如,RC会在Pod维护(例如内核升级)后在节点上重新创建新Pod。

注意:
ReplicationController会替换由于某些原因而被删除或终止的pod,例如在节点故障或中断节点维护(例如内核升级)的情况下。因此,即使应用只需要一个pod,我们也建议使用ReplicationController。
RC跨多个Node节点监视多个pod。

示例

replication.yaml
apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

kubectl create -f ./replication.yaml
#运行replication.yaml

kubectl describe replicationcontrollers/nginx
#检查ReplicationController状态

pods=$(kubectl get pods --selector=app=nginx --output=jsonpath={.items..metadata.name})
echo $pods
#列出属于ReplicationController的所有pod

删除ReplicationController及其Pods

使用kubectl delete命令删除ReplicationController及其所有pod。

当使用REST API或客户端库时,需要明确地执行这些步骤(将副本缩放为0,等待pod删除,然后删除ReplicationController)。

只删除 ReplicationController

在删除ReplicationController时,可以不影响任何pod。

使用kubectl,为kubectl delete指定- cascade = false选项。

使用REST API或go客户端库时,只需删除ReplicationController对象即可。

原始文件被删除后,你可以创建一个新的ReplicationController来替换它。只要旧的和新.spec.selector 相匹配,那么新的将会采用旧的Pod。

ReplicationController隔离pod

可以通过更改标签来从ReplicationController的目标集中删除Pod。

RC常用方式

Rescheduling(重新规划)
扩展
滚动更新
多版本跟踪
使用ReplicationControllers与关联的Services

RC 替代方法

ReplicaSet

ReplicaSet是支持新的set-based选择器要求的下一代ReplicationController 。它主要用作Deployment协调pod创建、删除和更新。请注意,除非需要自定义更新编排或根本不需要更新,否则建议使用Deployment而不是直接使用ReplicaSets。

Deployment(推荐)

Deployment是一个高级的API对象,以类似的方式更新其底层的副本集和它们的Pods kubectl rolling-update。如果您希望使用这种滚动更新功能,建议您进行部署,因为kubectl rolling-update它们是声明式的,服务器端的,并具有其他功能。

Bare Pods(最佳建议)

与用户直接创建pod的情况不同,ReplicationController会替换由于某些原因而被删除或终止的pod,例如在节点故障或中断节点维护(例如内核升级)的情况下。因此,即使应用只需要一个pod,我们也建议使用ReplicationController。

Kubernetes StatefulSets

StatefulSets(有状态系统服务设计)在Kubernetes 1.7中还是beta特性,同时StatefulSets是1.4 版本中PetSets的替代品。PetSets的用户参考1.5 升级指南 。

使用StatefulSets

在具有以下特点时使用StatefulSets:
稳定性,唯一的网络标识符。
稳定性,持久化存储。
有序的部署和扩展。
有序的删除和终止。
有序的自动滚动更新。

Pod调度运行时,如果应用不需要任何稳定的标示、有序的部署、删除和扩展,则应该使用一组无状态副本的控制器来部署应用,例如 Deployment 或 ReplicaSet更适合无状态服务需求。

StatefulSets的限制

StatefulSet还是beta特性,在Kubernetes 1.5版本之前任何版本都不可以使用。
与所有alpha / beta 特性的资源一样,可以通过apiserver配置-runtime-config来禁用StatefulSet。
Pod的存储,必须基于请求storage class的PersistentVolume Provisioner或由管理员预先配置来提供。
基于数据安全性设计,删除或缩放StatefulSet将不会删除与StatefulSet关联的Volume。
StatefulSets需要Headless Service负责Pods的网络的一致性(必须创建此服务)。

Ingress是什么

通常情况下,service和pod仅可在集群内部网络中通过IP地址访问。所有到达边界路由器的流量或被丢弃或被转发到其他地方。从概念上讲,可能像下面这样:

    internet
        |
  ------------
  [ Services ]

Ingress是授权入站连接到达集群服务的规则集合。

   internet
        |
   [ Ingress ]
   --|-----|--
   [ Services ]

你可以给Ingress配置提供外部可访问的URL、负载均衡、SSL、基于名称的虚拟主机等。用户通过POST Ingress资源到API server的方式来请求ingress。 Ingress controller负责实现Ingress,通常使用负载平衡器,它还可以配置边界路由和其他前端,这有助于以HA方式处理流量。

先决条件

在使用Ingress resource之前,有必要先了解下面几件事情。Ingress是beta版本的resource,在kubernetes1.1之前还没有。你需要一个Ingress Controller来实现Ingress,单纯的创建一个Ingress没有任何意义。

GCE/GKE会在master节点上部署一个ingress controller。你可以在一个pod中部署任意个自定义的ingress controller。你必须正确地annotate每个ingress,比如 运行多个ingress controller 和 关闭glbc.

确定你已经阅读了Ingress controller的beta版本限制。在非GCE/GKE的环境中,你需要在pod中部署一个controller。

Ingress Resource

最简化的Ingress配置:

1: apiVersion: extensions/v1beta1
2: kind: Ingress
3: metadata:
4:   name: test-ingress
5: spec:
6:   rules:
7:   - http:
8:       paths:
9:       - path: /testpath
10:        backend:
11:           serviceName: test
12:           servicePort: 80

如果你没有配置Ingress controller就将其POST到API server不会有任何用处

配置说明:
1-4行:跟Kubernetes的其他配置一样,ingress的配置也需要apiVersion,kind和metadata字段。配置文件的详细说明请查看部署应用, 配置容器和 使用resources.

5-7行: Ingress spec 中包含配置一个loadbalancer或proxy server的所有信息。最重要的是,它包含了一个匹配所有入站请求的规则列表。目前ingress只支持http规则。

8-9行:每条http规则包含以下信息:一个host配置项(比如for.bar.com,在这个例子中默认是*),path列表(比如:/testpath),每个path都关联一个backend(比如test:80)。在loadbalancer将流量转发到backend之前,所有的入站请求都要先匹配host和path。

10-12行:正如 services doc中描述的那样,backend是一个service:port的组合。Ingress的流量被转发到它所匹配的backend。

全局参数:为了简单起见,Ingress示例中没有全局参数,请参阅资源完整定义的api参考。 在所有请求都不能跟spec中的path匹配的情况下,请求被发送到Ingress controller的默认后端,可以指定全局缺省backend。

Kubernetes Service

Kubernetes Pod 是有生命周期的,它们可以被创建,也可以被销毁,然而一旦被销毁生命就永远结束。 通过 ReplicationController 能够动态地创建和销毁 Pod(例如,需要进行扩缩容,或者执行 滚动升级)。 每个 Pod 都会获取它自己的 IP 地址,即使这些 IP 地址不总是稳定可依赖的。 这会导致一个问题:在 Kubernetes 集群中,如果一组 Pod(称为 backend)为其它 Pod (称为 frontend)提供服务,那么那些 frontend 该如何发现,并连接到这组 Pod 中的哪些 backend 呢?

Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。 这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector(查看下面了解,为什么可能需要没有 selector 的 Service)实现的。

举个例子,考虑一个图片处理 backend,它运行了3个副本。这些副本是可互换的 —— frontend 不需要关心它们调用了哪个 backend 副本。 然而组成这一组 backend 程序的 Pod 实际上可能会发生变化,frontend 客户端不应该也没必要知道,而且也不需要跟踪这一组 backend 的状态。 Service 定义的抽象能够解耦这种关联。

对 Kubernetes 集群中的应用,Kubernetes 提供了简单的 Endpoints API,只要 Service 中的一组 Pod 发生变更,应用程序就会被更新。 对非 Kubernetes 集群中的应用,Kubernetes 提供了基于 VIP 的网桥的方式访问 Service,再由 Service 重定向到 backend Pod。

定义 Service

一个 Service 在 Kubernetes 中是一个 REST 对象,和 Pod 类似。 像所有的 REST 对象一样, Service 定义可以基于 POST 方式,请求 apiserver 创建新的实例。 例如,假定有一组 Pod,它们对外暴露了 9376 端口,同时还被打上 "app=MyApp" 标签。

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

上述配置将创建一个名称为 “my-service” 的 Service 对象,它会将请求代理到使用 TCP 端口 9376,并且具有标签 "app=MyApp" 的 Pod 上。 这个 Service 将被指派一个 IP 地址(通常称为 “Cluster IP”),它会被服务的代理使用(见下面)。 该 Service 的 selector 将会持续评估,处理结果将被 POST 到一个名称为 “my-service” 的 Endpoints 对象上。

需要注意的是, Service 能够将一个接收端口映射到任意的 targetPort。 默认情况下,targetPort 将被设置为与 port 字段相同的值。 可能更有趣的是,targetPort 可以是一个字符串,引用了 backend Pod 的一个端口的名称。 但是,实际指派给该端口名称的端口号,在每个 backend Pod 中可能并不相同。 对于部署和设计 Service ,这种方式会提供更大的灵活性。 例如,可以在 backend 软件下一个版本中,修改 Pod 暴露的端口,并不会中断客户端的调用。

Kubernetes Service 能够支持 TCP 和 UDP 协议,默认 TCP 协议。

没有 selector 的 Service

Servcie 抽象了该如何访问 Kubernetes Pod,但也能够抽象其它类型的 backend,例如:
希望在生产环境中使用外部的数据库集群,但测试环境使用自己的数据库。
希望服务指向另一个 Namespace 中或其它集群中的服务。
正在将工作负载转移到 Kubernetes 集群,和运行在 Kubernetes 集群之外的 backend。
在任何这些场景中,都能够定义没有 selector 的 Service :

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

由于这个 Service 没有 selector,就不会创建相关的 Endpoints 对象。可以手动将 Service 映射到指定的 Endpoints:

kind: Endpoints
apiVersion: v1
metadata:
  name: my-service
subsets:
  - addresses:
      - ip: 1.2.3.4
    ports:
      - port: 9376

注意:Endpoint IP 地址不能是 loopback(127.0.0.0/8)、 link-local(169.254.0.0/16)、或者 link-local 多播(224.0.0.0/24)。

访问没有 selector 的 Service,与有 selector 的 Service 的原理相同。请求将被路由到用户定义的 Endpoint(该示例中为 1.2.3.4:9376)。

ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。 相反地,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。

kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

当查询主机 my-service.prod.svc.CLUSTER时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。 如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 Service 的 type。

VIP 和 Service 代理

在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。 在 Kubernetes v1.0 版本,代理完全在 userspace。在 Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。 从 Kubernetes v1.2 起,默认就是 iptables 代理。

在 Kubernetes v1.0 版本,Service 是 “4层”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了 Ingress API(beta 版),用来表示 “7层”(HTTP)服务。

userspace 代理模式

这种模式,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会在本地 Node 上打开一个端口(随机选择)。 任何连接到“代理端口”的请求,都会被代理到 Service 的backend Pods 中的某个上面(如 Endpoints 所报告的一样)。 使用哪个 backend Pod,是基于 Service 的 SessionAffinity 来确定的。 最后,它安装 iptables 规则,捕获到达该 Service 的 clusterIP(是虚拟 IP)和 Port 的请求,并重定向到代理端口,代理端口再代理请求到 backend Pod。

网络返回的结果是,任何到达 Service 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、Service、或 Pod 的任何信息。

默认的策略是,通过 round-robin 算法来选择 backend Pod。 实现基于客户端 IP 的会话亲和性,可以通过设置 service.spec.sessionAffinity 的值为 "ClientIP" (默认值为 "None")。

iptables 代理模式

这种模式,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会安装 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某个上面。 对于每个 Endpoints 对象,它也会安装 iptables 规则,这个规则会选择一个 backend Pod。

默认的策略是,随机选择一个 backend。 实现基于客户端 IP 的会话亲和性,可以将 service.spec.sessionAffinity 的值设置为 "ClientIP" (默认值为 "None")。

和 userspace 代理类似,网络返回的结果是,任何到达 Service 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、Service、或 Pod 的任何信息。 这应该比 userspace 代理更快、更可靠。然而,不像 userspace 代理,如果初始选择的 Pod 没有响应,iptables 代理能够自动地重试另一个 Pod,所以它需要依赖 readiness probes。

多端口 Service

很多 Service 需要暴露多个端口。对于这种情况,Kubernetes 支持在 Service 对象中定义多个端口。 当使用多个端口时,必须给出所有的端口的名称,这样 Endpoint 就不会产生歧义,例如:

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
    selector:
      app: MyApp
    ports:
      - name: http
        protocol: TCP
        port: 80
        targetPort: 9376
      - name: https
        protocol: TCP
        port: 443
        targetPort: 9377

选择自己的 IP 地址

在 Service 创建的请求中,可以通过设置 spec.clusterIP 字段来指定自己的集群 IP 地址。 比如,希望替换一个已经已存在的 DNS 条目,或者遗留系统已经配置了一个固定的 IP 且很难重新配置。 用户选择的 IP 地址必须合法,并且这个 IP 地址在 service-cluster-ip-range CIDR 范围内,这对 API Server 来说是通过一个标识来指定的。 如果 IP 地址不合法,API Server 会返回 HTTP 状态码 422,表示值不合法。

为何不使用 round-robin DNS?

一个不时出现的问题是,为什么我们都使用 VIP 的方式,而不使用标准的 round-robin DNS,有如下几个原因:

长久以来,DNS 库都没能认真对待 DNS TTL、缓存域名查询结果
很多应用只查询一次 DNS 并缓存了结果
就算应用和库能够正确查询解析,每个客户端反复重解析造成的负载也是非常难以管理的
我们尽力阻止用户做那些对他们没有好处的事情,如果很多人都来问这个问题,我们可能会选择实现它。

服务发现

Kubernetes 支持2种基本的服务发现模式 —— 环境变量和 DNS。

环境变量

当 Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量。 它同时支持 Docker links兼容 变量(查看 makeLinkVariables)、简单的 {SVCNAME}_SERVICE_HOST 和 {SVCNAME}_SERVICE_PORT 变量,这里 Service 的名称需大写,横线被转换成下划线。

举个例子,一个名称为 "redis-master" 的 Service 暴露了 TCP 端口 6379,同时给它分配了 Cluster IP 地址 10.0.0.11,这个 Service 生成了如下环境变量:

REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11

这意味着需要有顺序的要求 —— Pod 想要访问的任何 Service 必须在 Pod 自己之前被创建,否则这些环境变量就不会被赋值。DNS 并没有这个限制。

DNS

一个可选(尽管强烈推荐)集群插件 是 DNS 服务器。 DNS 服务器监视着创建新 Service 的 Kubernetes API,从而为每一个 Service 创建一组 DNS 记录。 如果整个集群的 DNS 一直被启用,那么所有的 Pod 应该能够自动对 Service 进行名称解析。

例如,有一个名称为 "my-service" 的 Service,它在 Kubernetes 集群中名为 "my-ns" 的 Namespace 中,为 "my-service.my-ns" 创建了一条 DNS 记录。 在名称为 "my-ns" 的 Namespace 中的 Pod 应该能够简单地通过名称查询找到 "my-service"。 在另一个 Namespace 中的 Pod 必须限定名称为 "my-service.my-ns"。 这些名称查询的结果是 Cluster IP。

Kubernetes 也支持对端口名称的 DNS SRV(Service)记录。 如果名称为 "my-service.my-ns" 的 Service 有一个名为 "http" 的 TCP 端口,可以对 "_http._tcp.my-service.my-ns" 执行 DNS SRV 查询,得到 "http" 的端口号。

Kubernetes DNS 服务器是唯一的一种能够访问 ExternalName 类型的 Service 的方式。 更多信息可以查看DNS Pod 和 Service。

Headless Service

有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 "None" 来创建 Headless Service。

这个选项允许开发人员自由寻找他们自己的方式,从而降低与 Kubernetes 系统的耦合性。 应用仍然可以使用一种自注册的模式和适配器,对其它需要发现机制的系统能够很容易地基于这个 API 来构建。

对这类 Service 并不会分配 Cluster IP,kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。 DNS 如何实现自动配置,依赖于 Service 是否定义了 selector。

配置 Selector

对没有定义 selector 的 Headless Service,Endpoint 控制器不会创建 Endpoints 记录。 然而 DNS 系统会查找和配置,无论是:

ExternalName 类型 Service 的 CNAME 记录
记录:与 Service 共享一个名称的任何 Endpoints,以及所有其它类型

发布服务 —— 服务类型

对一些应用(如 Frontend)的某些部分,可能希望通过外部(Kubernetes 集群外部)IP 地址暴露 Service。

Kubernetes ServiceTypes 允许指定一个需要的类型的 Service,默认是 ClusterIP 类型。

Type 的取值以及行为如下:

ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType。
NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。
LoadBalancer:使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。
ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。 没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。

NodePort 类型

如果设置 type 的值为 "NodePort",Kubernetes master 将从给定的配置范围内(默认:30000-32767)分配端口,每个 Node 将从该端口(每个 Node 上的同一端口)代理到 Service。该端口将通过 Service 的 spec.ports[*].nodePort 字段被指定。

如果需要指定的端口号,可以配置 nodePort 的值,系统将分配这个端口,否则调用 API 将会失败(比如,需要关心端口冲突的可能性)。

这可以让开发人员自由地安装他们自己的负载均衡器,并配置 Kubernetes 不能完全支持的环境参数,或者直接暴露一个或多个 Node 的 IP 地址。

需要注意的是,Service 将能够通过 <NodeIP>:spec.ports[*].nodePort 和 spec.clusterIp:spec.ports[*].port 而对外可见。

LoadBalancer 类型

使用支持外部负载均衡器的云提供商的服务,设置 type 的值为 "LoadBalancer",将为 Service 提供负载均衡器。 负载均衡器是异步创建的,关于被提供的负载均衡器的信息将会通过 Service 的 status.loadBalancer 字段被发布出去。

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
      nodePort: 30061
  clusterIP: 10.0.171.239
  loadBalancerIP: 78.11.24.19
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
      - ip: 146.148.47.155

来自外部负载均衡器的流量将直接打到 backend Pod 上,不过实际它们是如何工作的,这要依赖于云提供商。 在这些情况下,将根据用户设置的 loadBalancerIP 来创建负载均衡器。 某些云提供商允许设置 loadBalancerIP。如果没有设置 loadBalancerIP,将会给负载均衡器指派一个临时 IP。 如果设置了 loadBalancerIP,但云提供商并不支持这种特性,那么设置的 loadBalancerIP 值将会被忽略掉。

AWS 内部负载均衡器

在混合云环境中,有时从虚拟私有云(VPC)环境中的服务路由流量是非常有必要的。 可以通过在 Service 中增加 annotation 来实现,如下所示:

[...]
metadata: 
    name: my-service
    annotations: 
        service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
[...]

在水平分割的 DNS 环境中,需要两个 Service 来将外部和内部的流量路由到 Endpoint 上。

AWS SSL 支持

对运行在 AWS 上部分支持 SSL 的集群,从 1.3 版本开始,可以为 LoadBalancer 类型的 Service 增加两个 annotation:

metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012

第一个 annotation 指定了使用的证书。它可以是第三方发行商发行的证书,这个证书或者被上传到 IAM,或者由 AWS 的证书管理器创建。

  metadata:
      name: my-service
      annotations:
         service.beta.kubernetes.io/aws-load-balancer-backend-protocol: (https|http|ssl|tcp)

第二个 annotation 指定了 Pod 使用的协议。 对于 HTTPS 和 SSL,ELB 将期望该 Pod 基于加密的连接来认证自身。

HTTP 和 HTTPS 将选择7层代理:ELB 将中断与用户的连接,当转发请求时,会解析 Header 信息并添加上用户的 IP 地址(Pod 将只能在连接的另一端看到该 IP 地址)。

TCP 和 SSL 将选择4层代理:ELB 将转发流量,并不修改 Header 信息。

外部 IP

如果外部的 IP 路由到集群中一个或多个 Node 上,Kubernetes Service 会被暴露给这些 externalIPs。 通过外部 IP(作为目的 IP 地址)进入到集群,打到 Service 的端口上的流量,将会被路由到 Service 的 Endpoint 上。 externalIPs 不会被 Kubernetes 管理,它属于集群管理员的职责范畴。

根据 Service 的规定,externalIPs 可以同任意的 ServiceType 来一起指定。 在上面的例子中,my-service 可以在 80.11.12.10:80(外部 IP:端口)上被客户端访问。

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
  externalIPs: 
    - 80.11.12.10

不足之处

为 VIP 使用 userspace 代理,将只适合小型到中型规模的集群,不能够扩展到上千 Service 的大型集群。 查看 最初设计方案 获取更多细节。

使用 userspace 代理,隐藏了访问 Service 的数据包的源 IP 地址。 这使得一些类型的防火墙无法起作用。 iptables 代理不会隐藏 Kubernetes 集群内部的 IP 地址,但却要求客户端请求必须通过一个负载均衡器或 Node 端口。

Type 字段支持嵌套功能 —— 每一层需要添加到上一层里面。 不会严格要求所有云提供商(例如,GCE 就没必要为了使一个 LoadBalancer 能工作而分配一个 NodePort,但是 AWS 需要 ),但当前 API 是强制要求的。

未来工作

未来我们能预见到,代理策略可能会变得比简单的 round-robin 均衡策略有更多细微的差别,比如 master 选举或分片。 我们也能想到,某些 Service 将具有 “真正” 的负载均衡器,这种情况下 VIP 将简化数据包的传输。

我们打算为 L7(HTTP)Service 改进我们对它的支持。

我们打算为 Service 实现更加灵活的请求进入模式,这些 Service 包含当前 ClusterIP、NodePort 和 LoadBalancer 模式,或者更多。

VIP 的那些骇人听闻的细节

对很多想使用 Service 的人来说,前面的信息应该足够了。 然而,有很多内部原理性的内容,还是值去理解的。

避免冲突

Kubernetes 最主要的哲学之一,是用户不应该暴露那些能够导致他们操作失败、但又不是他们的过错的场景。 这种场景下,让我们来看一下网络端口 —— 用户不应该必须选择一个端口号,而且该端口还有可能与其他用户的冲突。 这就是说,在彼此隔离状态下仍然会出现失败。

为了使用户能够为他们的 Service 选择一个端口号,我们必须确保不能有2个 Service 发生冲突。 我们可以通过为每个 Service 分配它们自己的 IP 地址来实现。

为了保证每个 Service 被分配到一个唯一的 IP,需要一个内部的分配器能够原子地更新 etcd 中的一个全局分配映射表,这个更新操作要先于创建每一个 Service。 为了使 Service能够获取到 IP,这个映射表对象必须在注册中心存在,否则创建 Service 将会失败,指示一个 IP 不能被分配。 一个后台 Controller 的职责是创建映射表(从 Kubernetes 的旧版本迁移过来,旧版本中是通过在内存中加锁的方式实现),并检查由于管理员干预和清除任意 IP 造成的不合理分配,这些 IP 被分配了但当前没有 Service 使用它们。

IP 和 VIP

不像 Pod 的 IP 地址,它实际路由到一个固定的目的地,Service 的 IP 实际上不能通过单个主机来进行应答。 相反,我们使用 iptables(Linux 中的数据包处理逻辑)来定义一个虚拟IP地址(VIP),它可以根据需要透明地进行重定向。 当客户端连接到 VIP 时,它们的流量会自动地传输到一个合适的 Endpoint。 环境变量和 DNS,实际上会根据 Service 的 VIP 和端口来进行填充。

Userspace

作为一个例子,考虑前面提到的图片处理应用程序。 当创建 backend Service 时,Kubernetes master 会给它指派一个虚拟 IP 地址,比如 10.0.0.1。 假设 Service 的端口是 1234,该 Service 会被集群中所有的 kube-proxy 实例观察到。 当代理看到一个新的 Service, 它会打开一个新的端口,建立一个从该 VIP 重定向到新端口的 iptables,并开始接收请求连接。

当一个客户端连接到一个 VIP,iptables 规则开始起作用,它会重定向该数据包到 Service代理 的端口。 Service代理 选择一个 backend,并将客户端的流量代理到 backend 上。

这意味着 Service 的所有者能够选择任何他们想使用的端口,而不存在冲突的风险。 客户端可以简单地连接到一个 IP 和端口,而不需要知道实际访问了哪些 Pod。

Iptables

再次考虑前面提到的图片处理应用程序。 当创建 backend Service 时,Kubernetes master 会给它指派一个虚拟 IP 地址,比如 10.0.0.1。 假设 Service 的端口是 1234,该 Service会被集群中所有的 kube-proxy 实例观察到。 当代理看到一个新的 Service, 它会安装一系列的 iptables 规则,从 VIP 重定向到 per-Service 规则。 该 per-Service 规则连接到 per-Endpoint 规则,该 per-Endpoint 规则会重定向(目标 NAT)到 backend。

当一个客户端连接到一个 VIP,iptables 规则开始起作用。一个 backend 会被选择(或者根据会话亲和性,或者随机),数据包被重定向到这个 backend。 不像 userspace 代理,数据包从来不拷贝到用户空间,kube-proxy 不是必须为该 VIP 工作而运行,并且客户端 IP 是不可更改的。 当流量打到 Node 的端口上,或通过负载均衡器,会执行相同的基本流程,但是在那些案例中客户端 IP 是可以更改的。

  • 13
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值