阅读更多
1 Overview
1.1 Kubernetes Components
1.1.1 Master Components
Master Component
构成了集群的控制面板。Master Component
是整个集群的决策者,检测并响应集群消息
Master Component
可以运行在集群中的任意一台机器上,不要在Master
机器上运行User Container
1.1.1.1 kube-apiserver
kube-apiserver
是整个控制面板的最前端,将Master Component
通过Kubernetes API
露出,它被设计成易于水平扩展
1.1.1.2 etcd
etcd
是高一致性、高可用的key-value
存储,用于存储Kubernetes
所有的集群数据
1.1.1.3 kube-shceduler
kube-shceduler
会观测新创建且尚未被分配的Pod
,并为其选择一个Node
来运行
1.1.1.4 kube-controller-manager
kube-controller-manager
用于在Master
上运行Controller
。逻辑上讲,每个Controller
都是一个独立的进程,但是为了减小实现复杂度,这些被编译进了一个二进制中,运行在同一个进程中
Controller
包括
Node Controller
: 观测节点,并在节点退出集群时响应Replication Controller
: 为系统中每个Replication Controller Object
保持一定数量的Pod
Endpoints Controller
:Service Account & Token Controller
: 为新Namespace
创建默认的账号以及API访问的token
1.1.1.5 cloud-controller-manager
1.1.2 Node
Node component
运行在每个Node
之上,保持Pod
的运行,并提供Kubernetes
运行环境
1.1.2.1 kubelet
kubelet
是运行在集群的每个Node
上的代理。它确保Container
在Pod
中正常运行
kubelet
采用通过各种机制提供的PodSpecs
来确保Container
以期望的方式健康运行
kubelet
不会管理不由Kubernetes
创建的Container
1.1.2.2 kube-proxy
kube-proxy
通过维护网络规则并转发网络数据包,来支持Kubernetes Service
的抽象机制
1.1.2.3 Container Runtime
Kubernetes
支持多种运行时
Docker
rkt
runc
1.1.3 Addons
???
1.1.4 参考
1.2 Kubernetes Object
Kubernetes Object
作为持久化的实体,存在于Kubernetes
系统中,Kubernetes
用这些实体来表示集群的状态,具体来说
- 运行着什么容器化的应用程序
- 可以使用的资源清单
- 应用程序的策略,包括重启策略、更新、容错等
一个Kubernetes Object
用于记录我们的意图,一旦我们创建了一个Kubernetes Object
,那么Kubernetes
系统就会努力保证对象的存活,并朝着我们预期的状态推进。换言之,创建一个Kubernetes Object
,意味着告诉Kubernetes
系统,我们期望它以何种方式运行
我们必须通过Kubernetes API
来使用Kubernetes Object
,包括创建、修改、删除等。我们也可以使用kubectl
命令行工具,本质上,kubectl
为我们封装了一个或多个Kubernetes API
的调用。
每个Kubernetes Object
都至少包含两个object field
,即spec
以及status
spec
:描述了Kubernetes Object
的期望状态status
:描述了Kubernetes Object
的实际状态,这个状态由Kubernetes system
更新
当我们需要创建一个Kubernetes Object
时,我们需要提供spec
来描述这个Kubernetes Object
的期望状态,同时,还需要提供一些基本的信息来描述这个Kubernetes Object
,例如name
等。当我们利用Kubernetes API
来创建object
时,API request
中的request body
必须包含这些信息。通常我们将这些信息记录在.ymal
文件中,并将文件作为参数传递给Kubernetes API
注意,一个.yaml
文件中,必须包含如下字段
apiVersion
:指定Kubernetes API
的版本kind
:指定待创建object
的类型metadata
:object
的元数据,包括name
UID
namespace
1.2.1 Name
所有的Kubernetes Object
都用一个Name
和一个UID
精确确定
1.2.1.1 Nmaes
Names
是一种用户提供的字符串,表示了一个资源的路径,例如/api/v1/pods/some-name
在同一时刻,可以为同一种类型的Kubernetes Object
分配一个名字。当然,如果我们删除了这个Kubernetes Object
,我们还可以创建一个同名的Kubernetes Object
通常,Name
最大不超过253个字符,只允许包含小写的字母以及-
和.
,具体的类型可能还有更加严格的限制
1.2.1.2 UIDs
UID
是Kubernetes
系统创建的用于唯一标志Kubernetes Object
的字符串。每个Kubernetes Object
在整个生命周期中都会有一个唯一的UID
,将Kubernetes Object
先删除再重新创建,会得到一个新的UID
1.2.2 Namespaces
Kubernetes
支持同一个物理集群支持多个虚拟集群,这些虚拟集群被称为Namespaces
。如果一个Kubernetes
集群仅有数十个用户,那么我们完全不必考虑使用Namespaces
1.2.2.1 When to use Multiple Namespaces
Namespaces
用于为使用同一集群的多个不同的小组、项目提供一定程度的隔离
在同一个Namespaces
中的资源的名称必须唯一,但是在不同Namespaces
中的资源名称可以重复
如果仅仅为了隔离资源,例如同一个软件的不同版本,可以使用Label
而不需要使用Namespaces
1.2.2.2 Working with Namespaces
可以通过如下命令查看系统中的Namespaces
1 | kubectl get namespaces |
Kubernetes
默认包含三个Namespaces
default
: 在未指定Namespace
时,Kubernetes Object
默认属于这个Namespace
kube-system
: 由Kubernetes
系统创建的Kubernetes Object
所属的Namespace
kube-public
: 该Namespace
对所有的用户可见,通常用于共享一些资源
在使用kubectl
时,我们可以加上--namespace
来暂时指定Namespace
我们可以为kubectl
设定Namespace
上下文,后续所有的命令都默认指定该Namespace
1 | kubectl config set-context $(kubectl config current-context) --namespace=<insert-namespace-name-here> |
1.2.2.3 Namespaces and DNS(未完成)
当我们创建一个Service
,它会创建一个相关的DNS entry
,其格式为<service-name>.<namespace-name>.svc.cluster.local
1.2.2.4 Not All Objects are in a Namespace
大部分Kubernetes
资源位于一个Namespace
中。某些底层次的资源,例如Node
以及PersistentVolume
不属于任何Namespace
以下命令用于查看哪些资源位于/不位于Namespace
中
1 | # In a namespace |
1.2.3 Labels and Selectors
Labels
是一些依附在Kubernetes Object
上的键值对。Lebels
用于为一些对用户有特殊意义的Kubernetes Object
打标,这些标在Kubernetes
内核中并无其他含义。Label
用于组织和选择Kubernetes Object
。我们可以再任何时刻为Kubernetes Object
打上Label
。对于一个Kubernetes Object
来说,key
必须是唯一的
1.2.3.1 Motivation
Lebal
允许用户以一种松耦合的方式将组织结构与Kubernetes Object
关联起来,用户不必自己存储这些映射关系
服务部署以及一些批处理流水线通常是一个多维度的实体,通常需要交叉式的管理,这会破坏层次结构的严格封装,通常这些封装是由基础设施而非用户完成的
1.2.3.2 Label selectors
与Name
与UID
不同,Label
并不需要保证唯一性。而且,在通常情况下,我们期望多个Kubernetes Object
共享同一个Label
通过Label Selector
,用户/客户端就可以识别出这些Kubernetes Object
,因此Label Selector
是Kubernetes
管理这些对象的核心
目前,Kubernetes
支持两种类型的Selector
:equality-based
和set-based
。Label Selector
由多个requirement
组成,以逗号分隔,多个requirement
之间的关系是逻辑与&&
。这两种类型可以混用
Equality-based requirement
允许通过Label key
以及Label value
进行过滤,允许的比较操作包括:=
、==
、!=
,其中=
与==
都表示相等型比较
1 | environment = production |
- 第一个规则表示:匹配
key
等于environment
且value
等于production
的所有Kubernetes Object
- 第二个规则表示:匹配
key
等于tier
且value
不等于frontend
的所有Kubernetes Object
- 第三个规则表示:前两个规则的逻辑与关系
Set-based requirement
允许根据一组集合来进行过滤,允许的操作包括:in
、notin
、exists
。Set-based requirement
表达能力要大于Equality-based requirement
,即Equality-based requirement
可以用Set-based requirement
的方式表示出来
1 | environment in (production, qa) |
- 第一个规则表示:匹配
key
等于environment
且value
等于production
或qa
的所有Kubernetes Object
- 第二个规则表示:匹配
key
等于tier
且value
不等于frontend
且value
不等于backend
的所有Kubernetes Object
- 第三个规则表示:匹配含有
partition
这个key
的所有Kubernetes Object
- 第四个规则表示:匹配不含有
partition
这个key
的所有Kubernetes Object
selector配置格式
1 | selector: |
matchLabels
是一个key-value
的mapmatchExpressions
是一系列的selector requirement
- 所有的条件会以逻辑与的方式组合(包括
matchLabels
和matchExpressions
)
1.2.3.3 API
1 | kubectl get pods -l environment=production,tier=frontend |
1.2.4 Annotations
我们可以通过Label
以及Annotation
为Kubernetes Object
添加一些元数据。Label
通常被用于Kubernetes Object
的匹配,而Annotation
通常用于为Kubernetes Object
添加一些配置,这些配置可以包含一些Label
不允许的字符
1.2.5 Field Selectors
Field Selector
允许我们基于Kubernetes Object
的字段匹配来过滤Kubernetes Object
,支持的匹配操作包括:=
、==
、!=
,其中=
与==
都表示相等型比较
1 | kubectl get pods --field-selector status.phase=Running |
1.2.6 Recommended Labels
1.2.7 参考
- Understanding Kubernetes Objects
- Imperative Management of Kubernetes Objects Using Configuration Files
2 Architecture
2.1 Node
Node
是Kubernetes
中的一个工作者。Node
可能是一个虚拟机或者物理机。每个Node
被Master Component
管理,包含了一些运行Pod
所必须的服务,包括Container runtime
、kubelet
、kube-proxy
2.1.1 Node Status
Node
的状态包括以下几部分
Address
Condition
Capacity
Info
2.1.1.1 Addresses
Address
包含的字段与服务提供方或者裸机配置有关
HostName
:hostname
由Node
的内核上报。可以通过kubelet
参数--hostname-override
覆盖ExternalIP
: 公网IPInternalIP
: 集群内的IP
2.1.1.2 Condition
conditions
字段描述了Node
的状态,包括
OutOfDisk
: 如果剩余空间已经无法容纳新的Pod
时,为Frue
;否则False
Ready
: 如果Node
可以接收新的Pod
时,为True
;如果Node
无法接纳新的Pod
时,为False
;如果Controller
与该Node
断连一定时间后(由node-monitor-grace-period
字段指定,默认40s),为Unknown
MemoryPressure
: 存在内存压力时,为Ture
;否则False
PIDPressure
: 存在进程压力时,为True
;否则False
DiskPressure
: 存在磁盘压力时,为True
;否则False
NetworkUnavailable
: 网络异常时,为True
;否则False
2.1.1.3 Capacity
描述了可用资源的数量,包括CPU、内存、最大可运行的Pod
数量
2.1.1.4 Info
描述了节点的通用信息,包括内核版本,Kubernetes
版本,Docker
版本,OS
名称等等
2.1.2 Management
与Pod
与Service
不同,Pod
与Service
是由Kubernetes
负责创建的,而Node
是由云服务商或者使用者提供的。当Kubernetes
创建了一个Node
仅仅意味着创建了一个Kubernetes Object
来描述这个Node
目前,存在三个与Node
交互的组件,他们分别是:Node Controller
、kubelet
、kubectl
2.1.2.1 Node Controller
Node Controller
是Kubernetes master component
,用于管理Node
的生命周期
- 当
Node
注册时,为Node
分配一个CIDR block
- 保持
Node
列表与云服务提供方的可用机器列表一致,当Node
变得unhealthy
后,Node Controller
会询问云服务提供商该VM
是否仍然可用,若不可用,则会将其从Node
列表中删除 - 监视
Node
的健康状况,Node Controller
会在Node
变得unreachable
后将状态从NodeReady
变为ConditionUnknown
,然后以优雅的方式移除该Node
上的所有Pod
在Kubernetes 1.4
版本中,对集群中大批Node
同时出故障这一情况的处理做了一些优化,Controller
会观测集群中所有Node
的状态,来决策Pod
以何种方式、何种规模进行移除
在大多数情况下,Node Controller
限制了移除率--node-eviction-rate
(默认是0.1),意味着,10秒之内最多只有一个Node
节点上的Pod
会被移除
当一个可用区(Zone
)中的某个Node
发生故障时,Node
移除的行为发生了改变。Node Controller
会检测在当前可用区中发生故障的Node
的占比,如果这个比例最少为--unhealthy-zone-threshold
(默认0.55)时,移除率会降低。如果集群很小(节点数量小于),移除过程会直接停止
将Node
分布于不同的可用区的原因是,当一个可用区变得完全不可用时,那这些Pod
可以迁移到其他的可用区中
2.1.3 Node capacity
Node
容量是Node Object
的一部分。通常Node
在向Master
注册时,需要上报容量信息。如果我们是手动创建和管理Node
,那么就需要手动设置容量信息
Kubernetes Scheduler
会保证Node
上的资源一定大于所有Pod
占用的资源。注意到,它仅仅会计算通过kubelet
创建的Container
所占用的资源数量,而不会计算由Container runtime
或者手动创建的Containter
所占用的资源数量
2.2 Master-Node communication
2.2.1 Cluster to Master
Node
与Master
之间的通信全部依靠Api Server
,除此之外,其他Master component
不会提供远程服务。在一个典型的部署场景中,Api Server
会在443
端口上监听
应该为Node
配置公共根证书,以便它们可以安全地连接到Api Server
Pod
可以借助Service Account
来与Api Server
进行安全通信,Kubernetes
会在Pod
实例化的时候,将根证书以及令牌注入到Pod
中去
Kubernetes
会为每个Server
分配一个虚拟IP,kube-proxy
会将其重定向为Api Server
的具体IP
因此,Node
与Api Server
之间的通信可以在可靠/不可靠的网络下进行
2.2.2 Master to Cluster
Master(Api server)
与Node
的通信的方式有两种:其一,Api Server
通过与每个Node
上的kubelet
来完成通信;其二,Api Server
通过Proxy
来完成与Node
、Pod
、Server
的通信
2.2.2.1 Api server to kubelet
Api Server
与kubelet
之间的通信主要用于以下几个用途
- 获取
Pod
的日志 - 与运行时的
Pod
进行交互 - 为
kubelet
提供端口转发服务(port-forwarding functionality
)
默认情况下,Api Server
不会校验kubelet
的服务端证书,因此有可能受到中间人攻击(man-in-the-middle
),因此在不可信的网络中或者公网上是不安全的。我们可以通过--kubelet-certificate-authority
来为Api Server
提供一个根证书,用来校验kubelet
的证书合法性
2.2.2.2 Api server to nodes, pods, and services
Api Server
与Node
、Pod
、Service
之间的通信默认用的是HTTP协议,显然这是不安全的。我们可以为Node
、Pod
、Service
的API URL
指定https
前缀,来使用HTTPS协议。但是,Api Server
仍然不会校验证书的合法性,也不会为客户端提供任何凭证,因此在不可信的网络中或者公网上是不安全的
2.3 Concepts Underlying the Cloud Controller Manager
最初提出Cloud Controller Manager(CCM)
概念是为了能够让云服务商与Kubernetes
可以相互独立地发展
CCM
是的设计理念是插件化的,这样云服务商就可以以插件的方式与Kubernetes
进行集成
2.3.1 Design
如果没有CCM
,Kubernetes
的架构如下:
在上面的架构图中,Kubernetes
与Cloud Provider
通过几个不同的组件进行集成
kubelet
Kubernetes Controller Manager(KCM)
Kubernetes API server
在引入CCM
后,整个Kubernetes
的架构变为:
2.3.2 Components of the CCM
CCM
打破了KCM
的一些功能,并且作为一个独立的进程运行。具体来说,CCM
打破了KCM
那些依赖于云的Controller
KCM
具有如下Controller
:
Node Controller
Volume Controller
Route Controller
Service Controller
CCM
包含如下Controller
Node Controller
Route Controller
Service Controller
PersistentVolumeLabels Controller
2.3.3 Functions of the CCM
2.3.3.1 Kubernetes Controller Manager
CCM
中大部分的功能都是从KCM
中继承过来的,包括如下Controller
Node Controller
Route Controller
Service Controller
PersistentVolumeLabels Controller
2.3.3.1.1 Node Controller
Node Controller
负责初始化Node
,它会从Cloud Provier
中获取有关Node
的一些信息,具体包括
- 初始化
Node
时,为其打上zone/region
的标签 - 初始化
Node
时,记录一些定制化的信息,包括type
和size
- 获取
Node
的hostname
和address
- 当
Node
失联时,询问Cloud Provider
该Node
是否已被其删除,如果已被删除,那么删除该Node
对应的Kubernetes Node Object
2.3.3.1.2 Route Controller
Route Controller
负责配置路由信息,以便位于不同Node
节点上的Container
能够进行相互通信,目前只与Google Compute Engine Cluster
兼容
2.3.3.1.3 Service Controller
Service Controller
负责监听Service
的创建、更新、删除对应的事件,确保负载均衡可以时时感知服务的状态
2.3.3.1.4 PersistentVolumeLabels Controller
PersistentVolumeLabels Controller
在AWS EBS/GCE PD Volume
创建时,为其打上标签。这些标签对于Pod
的调度至关重要,因为这些Volume
只在特定的区域,因此被调度的Pod
也必须位于这些区域才行
PersistentVolumeLabels Controller
仅用于CCM
中
2.3.3.2 Kubelet
Node controller
包含了kubelet
中依赖于云的功能。在引入CCM
之前,kubelet
在初始化Node
时,还需要负责初始化IP
地址、区域标签以及实例类型等信息。引入CCM
后,这些初始化操作将从kubelet
中被移除,完全由CCM
负责初始化
在新模式下,kubelet
可以单纯地创建一个Node
,而不用关心与云相关的一些依赖信息。在CCM
完成初始化之前,该Node
是无法被调度的
2.3.3.3 Kubernetes API server
同样地,PersistentVolumeLabels
也将Api Server
中与云相关的部分迁移到CCM
中
2.3.4 Plugin mechanism
CCM
定义了一系列的接口(Go
接口),交由云服务商自行提供实现
2.3.5 Authorization
2.3.5.1 Node Controller
Node Controller
只与Node Object
进行交互,可以进行如下操作
Get
List
Create
Update
Patch
Watch
Delete
2.3.5.2 Route Controller
Route Controller
监听Node Object
的创建以及配置路由规则,可以进行如下操作
Get
2.3.5.3 Service Controller
Service Controller
监听Service Object
的创建、更新、删除,以及配置endpoint
,可以进行如下操作
List
Get
Watch
Patch
Update
2.3.5.4 PersistentVolumeLabels Controller
PersistentVolumeLabels Controller
监听PersistentVolume (PV)
的创建,可以进行如下操作
Get
List
Watch
Update
2.4 参考
3 Workloads
3.1 Pods
3.1.1 Pod Overview
3.1.1.1 Understanding Pods
Pod
是Kubernetes中,用户能够创建或部署的最小单元,一个Pod
代表了一个进程(可能是高耦合的一组进程)。Pod
封装了以下资源
Container
:一个或多个应用容器volume
:存储资源IP
:一个pod会被分配一个IP,在所属的namespace下唯一options
:控制容器运行的参数
通常,Pod
在Kubernetes
集群中有两种主要用法
Pods that run a single container
: 该模式是Kubernetes
最常用的模式。在这种情况下,我们可以认为Pod
对Container
做了一层封装,Kubernetes
直接管理Pod
而不是Container
Pods that run multiple containers that need to work together
: 在这种模式下,Pod
封装了由一组高耦合的Container
构成的应用,这些Container
需要共享资源
每一个Pod
都运行着一个应用的一个实例。如果我们想要水平扩展,我们必须使用多个Pod
,同样,每个Pod
运行一个实例。在Kubernetes
中,这称为replication
。通常,Replicated Pod
由Controller
统一创建和管理
Pod
为Container
提供了两种共享资源的方式
networking
: 每个Pod
被分配了一个唯一的IP
地址,每个Container
共享网络的Namespace
,包括IP
地址以及端口号。位于同一个Pod
中的Container
可以通过localhost
来进行通信。位于不同Pod
中的Container
需要借助host
以及port
来通信storage
:Pod
可以分享一些存储卷。位于同一个Pod
中的所有Container
共享这些卷
3.1.1.2 Working with Pods
在Kubernetes
中,我们很少直接创建独立的Pod
,因为Pod
被设计成一种相对短暂的、一次性的实体。当Pod
被创建后(被用户直接创建,或者被Controller
创建),它就会被调度到某个节点上开始运行。Pod
会一直在Node
上运行,直至被终结、Pod Object
被删除、Node
宕机
Pod
自身并不会自我恢复。也就是说,当Pod
部署失败或者挂了,这个Pod
的生命周期就结束了。Kubernetes用一个高层次的概念,即Controller
,来管理Pod
的生命周期,包括Pod
的创建、部署、副本、恢复等工作
Controller
可以为我们创建和管理多个Pod
,水平扩容、提供自我修复能力。例如,当一个Node
宕机后,Controller
会自动地在另一个Node
上重新启动一个新的Pod
3.1.1.3 Pod Templates
Pod Template
是Pod
的一份声明(如下),可以被其他Kubernetes Object
(包括Replication Controllers
、Job
等)引用
1 | apiVersion: v1 |
Pod Template
更像一个饼干模具,而不是指定所有副本的当前期望状态。当一个饼干制作完成后,它与饼干模具毫无关系。Pod Template
杜绝了量子纠缠,更新或者替换Pod Template
对于已经生产的Pod
不会产生任何影响
3.1.2 Pods
3.1.2.1 What is a Pod?
Pod
包含一个或者一组Container
,一些共享的存储/网络组件,以及运行应用的方式。Pod
中的共享上下文包括:Linux namespaces
,cgroups
,以及其他可能的隔离因素
在一个Pod
中的所有Container
共享一个IP
地址,以及端口空间,Container
之间可以通过localhost
进行通信,或者其他IPC
方式。不同Pod
中的Container
具有不同的IP
地址,因此,仅能通过Pod IP
进行通信
在同一个Pod
中的Container
可以访问共享卷,共享卷是Pod
的一部分,并且可以挂载到文件系统上
Pod
被设计成一种短暂的、一次性的实体。Pod
在创建时会被赋予一个唯一的UID
,并且被调度到某个Node
上一直运行直至被销毁。如果一个Node
宕机了,那些被调度到该Node
上的Pod
会被删除。一个特定的Pod
(UID
层面)不会被重新调度到新的Node
上,相反,它会被一个新的Pod
替代,这个新Pod
可以复用之前的名字,但是会被分配一个新的UID
,因此那么些共享的卷也会重新创建(旧数据无法保留)
3.1.2.2 Motivation for pods
通常一个服务由多个功能单元构成,各模块相互协作,而Pod
就是这种多协作过程的模型。Pod
通过提供一个高阶抽象来简化应用的部署、管理。Pod
是部署,水平扩展和复制的最小单元
Pod
允许内部的组件共享数据以及相互通信。在一个Pod
中的所有组件共享同一个网络Namespace
(IP
和port
),他们之间可以通过localhost
相互通信,因此在一个Pod
中的所有组件需要共享port
,同时,Pod
也允许其组件与外部通信
Pod
的hostname
被设置为Pod
的名字
3.1.2.3 Durability of pods (or lack thereof)
Pod
并不是一个高可用的实体。Pod
不会从调度失败、节点宕机或其他错误中恢复
通常情况下,用户不需要直接创建Pod
,而应该使用Controller
,Controller
提供了一个集群层面上的自我修复能力,以及水平扩容能力和删除能力
Pod
对外直接露出,其原因如下
- 提供可插拔式的调度和控制方式
- 支持
Pod-level
的操作,而不需要通过Controller API
来代理 - 解耦
Pod
生命周期与Controller
生命周期 - 解耦
Controller
和Service
- 解耦
Kubelet-level
的功能与cluster-level
的功能 - 高可用性
3.1.2.4 Termination of Pods
由于Pod
代表了集群中一组运行的进程,因此允许Pod
优雅地终结是很有必要的。用户需要发出删除指令,需要了解终结时间,需要确保Pod
真的被终结了。当用户发出删除Pod
的请求时,系统会记录一个宽限期(发出删除请求到被强制清除的时间间隔),然后向Pod
中的所有Container
发出TERM signal
,如果超过宽限期,Pod
还未终结,那么会向Pod
中的所有Container
发出KILL signal
,当Pod
终结后,它会被API server
删除
一个具体的例子
- 用户发出指令删除
Pod
,默认的宽限期是30s API server
会更新该Pod
的状态,并且设定宽限期- 当用户通过命令查看
Pod
状态时,该被删除的Pod
会显示Terminating
状态 Kubelet
观测到Pod
被标记为Terminating
状态,以及宽限期后,就开始终结流程- 如果有
Container
定义了preStop hook
,那么会运行该钩子方法。如果超过宽限期后,preStop hook
方法还在执行,那么步骤2将会被执行,并且会指定一个更小的宽限期(2s) - 向
Container
发送TERM
信号
- 如果有
Pod
被移除服务节点列表。因此,那些终止过程十分缓慢的Pod
,在此时也不会继续提供服务Kubelet
通过API server
将宽限期设置为0
来结束删除过程。之后该Pod
就对用户彻底不可见了,即彻底被删除了
Kubernetes
允许强制删除Pod
,强制删除意味着将该Pod
从集群状态以及etcd
中立即删除。当强制删除执行时,Api Server
不会等待kubelet
确认删除Pod
,而是直接将该Pod
删除,这样一来,一个新的复用了原来名字的Pod
就可以被立即创建。而那个被删除的Pod
仍然会给定一个比较小的宽限期来进行上述删除操作。尽量不要使用这种方式
3.1.3 Pod Lifecycle
3.1.3.1 Pod phase
Pod
的status
字段是一个PodStatus
对象,该对象包含一个phase
字段
phase
是对Pod
生命周期中的状态的高度抽象,仅包含以下几个值
Pending
:该Pod
已被Kubernetes system
接管,但是Container
尚未创建完毕。可能处于尚未被调度的状态,或者正在下载容器镜像Running
:所有Container
已经启动完毕,并且至少有一个Container
处于运行状态,或者处于启动或者重启的过程中Succeeded
:所有Container
成功终止,且不会重启Failed
:至少有一个Container
终止失败Unkown
:系统错误
可以通过如下命令查看phase
1 | kubectl get pod -n <namespace> <pod-name> -o yaml |
3.1.3.2 Pod conditions
Pod
的status
字段是一个PodStatus
对象,该对象包含一个conditions
字段,该字段对应的值是一个PodConditions
对象的数组
每个PodConditions
对象包含如下字段
lastProbeTime
:上一次进行状态监测的时刻lastTransitionTime
:上一次发生状态变更的时刻message
:状态变更的描述,一个human-readable
的描述reason
:状态变更的原因,一个较为精确的描述status
:True
、False
、Unknown
中的一个type
:以下几种可能值中的一个PodScheduled
:Pod
已被调度到一个node
上Ready
:Pod
已经能够提供服务,应该被添加到load balancing pool
中去Initialized
:所有的Init Container
已经执行完毕Unschedulable
:调度失败,可能原因是资源不足ContainersReady
:Pod
中的所有Container
已经就绪
3.1.3.3 Container probes
probe
是kubelet
对Container
定期进行的诊断。为了实现诊断,kubelet
通过调用一个handler
来完成,该handler
由容器实现,以下是三种handler
的类型
ExecAction
:在容器中执行一个命令,当命令执行成功(返回状态码是0)时,诊断成功TCPSocketAction
:通过Container
的IP
以及指定的port
来进行TCP
检测,当检测到port
开启时,诊断成功HTTPGetAction
:通过Container
的IP
以及指定的port
来进行HTTP Get
检测,当HTTP
返回码在200至400之间时,诊断成功
诊断结果如下
Success
Failure
Unknown
kubelet
可以对运行状态下的Container
进行如下两种probe
livenessProbe
:检测Container
是否存活(健康检查),若不存活,将杀死这个Container
readinessProbe
:检测Container
是否准备好提供服务,若检查不通过,那么会将这个Pod
从service
的endpoint
列表中移除
我们如何决定该使用livenessProbe
还是readinessProbe
- 如果我们的
Container
在碰到异常情况时本身就会宕机,那么我们就不需要livenessProbe
,kubelet
会自动根据restartPolicy
采取相应的动作 - 如果我们想要在
probe
失败时杀死或者重启Container
,那么,我们需要使用livenessProbe
,同时将restartPolicy
指定为Always
或OnFailure
模式 - 如果我们想要在
Pod
可读时才向其转发网络数据包,那么,我们需要使用readinessProbe
- 如果我们的
Container
需要读取大量数据,配置文件或者需要在启动时做数据迁移,那么需要readinessProbe
- 如果我们仅仅想要避免流量打到被删除的
Pod
上来,我们无需使用readinessProbe
。在删除过程中,Pod
会自动将自己标记为unready
状态,无论readinessProbe
是否存在
3.1.3.4 Pod readiness gate
为了织入一些回调逻辑或者信号到PodStatsu
中来增强Pod readiness
的可扩展性,Kubernetes
在1.11
版本之后引入了一个性特性,称为Pod ready++
,我们可以在PodSpec
中使用ReadinessGate
来增加一些额外的用于判断Pod readiness
的条件。如果Kubernetes
在status.conditions
中没有找到对应于ReadinessGate
中声明的条件类型,那么检测结果默认是Flase
1 | Kind: Pod |
3.1.3.5 Restart policy
PodSpec
有一个restartPolicy
字段,其可选值为Always
、OnFailure
、Never
。默认值为Always
。restartPolicy
对Pod
中的所有Container
都会生效。restartPolicy
仅针对在同一个Node
中重启Container
。多次启动之间的时间间隔以指数方式增长(10s、20s、40s…),最多不超过5分钟。在成功重启后10分钟后,时间间隔会恢复默认值
3.1.3.6 Pod lifetime
通常情况下,Pod
一旦创建就会永远存在,直至被某人或Controller
销毁。唯一例外就是,当Pod
的status.phase
字段为Succeeded
或Failed
且超过一段时间后(该时间由terminated-pod-gc-threshold
设定),该Pod
会被自动销毁
有三种类型的Controller
可供选择
- 使用
Job
,这类Pod
预期会终止,例如batch computations
。此时restartPolicy
通常设置为OnFailure
或Never
- 使用
ReplicationController
、ReplicaSet
、Deployment
,这类Pod
预期会一直运行,例如web servers
。此时restartPolicy
通常设置为Always
- 使用
DaemonSet
,这类Pod
通常每台机器都会运行一个
这三种类型的Controller
都含有一个PodTemplate
。通常,创建一个Controller
,然后利用它来创建Pod
是最常规的选择,而不是自己创建Pod
,因为Pod
自身是不具有错误恢复能力的,但是Controller
具备该能力
当某个Node
宕机或者失联后,Kubernetes
会将位于这些Node
上的Pod
全部标记为Failed
3.1.4 Init Containers
3.1.4.1 Understanding Init Containers
一个Pod
中可以运行一个或多个Container
,同时,一个Pod
中也可以运行一个或多个Init Container
(Init Container
会优先于Container
运行)
Init Container
具有如下特性
- 他们总会结束运行,即并不是一个常驻进程
Init Container
总是一个接一个地运行,上一个Init Container
运行结束后,下一个Init Container
才开始运行
如果Init Container
执行失败,Kubernetes
会重启Pod
直至Init Container
成功执行(至于Pod
是否重启依赖于restartPolicy
)
为了将一个Container
指定为Init Container
,我们需要在PodSpec
中添加initContainers
字段
Init Container
支持所有普通Container
的字段,唯独不支持readiness probe
,因为Init Container
在ready
之前已经结束了
3.1.4.2 Detailed behavior
在Pod
的启动过程中,在网络以及磁盘初始化后,Init Container
以配置的顺序启动。Init Container
会一个接一个地启动,且只有前一个启动成功后,后一个才会启动。如果Init Container
启动失败,会根据restartPolicy
来决定是否重新启动。特别地,如果Pod
的restartPolicy
被设置为Always
,那么对于Init Container
而言,就为OnFailure
如果Pod
重启(可能仅仅指重新启动container?并不是销毁Pod
后再创建一个新的Pod
),那么所有的Init Container
都会重新执行
3.1.5 Pod Preset
Pod Preset
用于在Pod
创建时注入一些运行时的依赖项,我们可以使用Label Selector
来指定需要注入的Pod
原理:Kubernetes提供了一个准入控制器(PodPreset
)。在创建Pod
时,系统会执行以下操作:
- 获取所有的
PodPreset
- 检查正在创建的
Pod
的Label
是否匹配某个或某些PodPreset
的Label Selector
- 将匹配的
PodPreset
所指定的资源注入到待创建的Pod
中去 - 如果发生错误,抛出一个事件,该事件记录了错误信息,然后以纯净的方式创建
Pod
(无视所有PodPreset
中的资源) - 修改
Pod
的status
字段,记录其被PodPreset
修改过。描述信息如下podpreset.admission.kubernetes.io/podpreset-<pod-preset name>: "<resource version>".
一个Pod Preset
可以应用于多个Pod
,同样,一个Pod
也可以关联多个Pod Preset
3.1.6 Disruptions
3.1.6.1 Voluntary and Involuntary Disruptions
Pod
会一直存在,直到某人或者Controller
摧毁它,或者碰到一个无法避免的硬件或者系统问题。我们将这些异常称为involutary disruption
,包括
- 硬件错误
- 集群管理员误删VM实例
- 内核错误
- 网络抖动
- 由于资源耗尽而导致
Pod
被移出Node
对于其他的情况,我们称之为voluntary disruption
,包括
- 删除管理
Pod
的Node
- 更新了
PodSpec
而导致的Pod
重启 - 删除
Pod
- 将
Node
从集群中移出,对其进行维护或升级 - 将
Node
从集群中移出,进行缩容
3.1.6.2 Dealing with Disruptions
下列方法可以减轻involuntary disruption
造成的影响
- 确保
Pod
只拿它需要的资源 - 复制应用,以获得高可用性
- 将应用副本分别部署到不同的区域的节点上,以获得更高的可用性
3.1.6.3 How Disruption Budgets Work
应用Owner
可以为每个应用创建一个PodDisruptionBudget(PDB)
对象。PDB
限制了应用同时缩容的最大数量,即保证应用在任何时候都有一定数量的Pod
在运行
当集群管理员利用kubectl drains
命令将某个Node
移除时,Kubernetes
会尝试移除该Node
上的所有Pod
,但是这个移除请求可能会被拒绝,然后Kubernetes
会周期性的重试,直至所有Pod
都被终结或者超过配置的时间(超过后,直接发送KILL signal
)
PDB
无法阻止involuntary disruptions
。Pod
由于灰度升级造成的voluntary disruption
可以被PDB
管控。但是Controller
灰度升级不会被PDB
管控
3.1.7 参考
3.2 Controller
3.2.1 ReplicaSet
ReplicaSet
是新一代的Replication Controller
,ReplicaSet
与Replication Controller
之间的唯一区别就是select
的方式不同。ReplicaSet
支持set-based selector
,而Replication Controller
仅支持equality-based selector
3.2.1.1 How to use a ReplicaSet
大部分支持Replication Controller
的kubectl
命令都支持ReplicaSet
,其中一个例外就是rolling-update
命令。如果我们想要进行灰度升级,更推荐使用Deploymenet
尽管ReplicaSet
可以独立使用,但到目前为止,它主要的用途就是为Deployment
提供一种机制—编排Pod
创建、删除、更新
3.2.1.2 When to use a ReplicaSet
ReplicaSet
保证了在任何时候,都运行着指定数量的Pod
副本。然而,Deployment
是一个更高阶的概念,它管理着ReplicaSet
、提供声明式的Pod
升级方式,以及一些其他的特性。因此,推荐使用Deployment
而不是直接使用ReplicaSet
,除非我们要使用自定义的升级策略或者根本不需要升级
3.2.1.3 Writing a ReplicaSet manifest
示例
1 | apiVersion: apps/v1 |
与其他Kubernetes Object
相同,ReplicaSet
同样需要apiVersion
、kind
、metadata
三个字段。除此之外,还需要以下字段
.spec.template
: 该字段是.spec
字段的唯一要求的字段,.spec.template
描述了一个Pod Template
,其格式与Pod
几乎完全一致,除了不需要apiVersion
、kind
这两个字段.spec.selector
:ReplicaSet
会管理所有匹配该Selector
的Pod
。注意到ReplicaSet
并不会区分由它创建或删除的Pod
与其他人或过程创建或删除的Pod
,因此,我们可以替换掉ReplicaSet
,而不会影响到正在执行的Pod
,它们仍然会被这个新的ReplicaSet
所管理。此外,.spec.template.metadata.labels
必须与.spec.selector
匹配,否则会被API
拒绝.metadata.labels
:ReplicaSet
还允许拥有自己的Label
,通常来说,.spec.template.metadata.labels
与.metadata.labels
是一致的。同样,它们也可以不一致,但要注意的是,.metadata.labels
与.spec.selector
无关.spec.replicas
: 该字段指定了Pod
副本的数量,默认为1
3.2.1.4 Working with ReplicaSets
3.2.1.4.1 Deleting a ReplicaSet and its Pods
如果我们要删除ReplicaSet
以及所有相关的Pod
,我们只需要使用kubectl delete
来删除ReplicaSet
。Garbage Controller
默认会自动删除所有相关的Pod
3.2.1.4.2 Deleting just a ReplicaSet
如果我们仅仅要删除ReplicaSet
,那么需要在kubectl delete
命令加上--cascade=false
参数
一旦ReplicaSet
被删除后,我们就可以创建一个新的ReplicaSet
,如果新的ReplicaSet
名字与原来的相同,那么它将会接管之前由原来的ReplicaSet
创建的Pod
。尽管如此,它不会要求已存在的Pod
来满足新的Pod Template
。如果要更新Pod
使其匹配新的Spec
,那么需要使用Rolling Update
3.2.1.4.3 Isolating Pods from a ReplicaSet
我们可以通过改变Pod
的Label
来将其与ReplicaSet
分离(通常用于分离那些用于Debug
、Data recovery
的Pod
),通过这种方式移除Pod
后,新的Pod
随之会被创建出来
3.2.1.4.4 Scaling a ReplicaSet
我们可以简单的修改ReplicaSet
的.spec.replicas
字段来进行扩容或缩容。ReplicaSet Controller
会保证Pod
的数量与ReplicaSet
保持一致
3.2.1.4.5 ReplicaSet as a Horizontal Pod Autoscaler Target
ReplicaSet
也可以是Horizontal Pod Autoscalers(HPA)
的目标。也就是说,HPA
可以自动缩放ReplicaSet
1 | apiVersion: autoscaling/v1 |
3.2.1.5 Alternatives to ReplicaSet
3.2.1.5.1 Deployment(recommended)
Deployment
是一个Kubernetes Object
,它可以包含ReplicaSet
,并且可以更新ReplicaSet
以及相关的Pod
。尽管ReplicaSet
可以独立使用,但是通常ReplicaSet
都作为Deployment
的一种机制来组织Pod
的创建、删除和更新。当我们使用Deployment
时,ReplicaSet
的创建完全由Deployment
来管控
3.2.1.5.2 Bare Pods
与直接创建Pod
不同,ReplicaSet
会在Pod
删除或者不可用时创建新的Pod
来替换原有的Pod
,例如Node
宕机,或者进行破坏性的维护,例如内核升级。即便我们的应用只需要一个Pod
,也应该使用ReplicaSet
而不是直接管理Pod
3.2.1.5.3 Job
对于预期会终止的Pod
而言,推荐使用Job
而不是ReplicaSet
3.2.1.5.4 DaemonSet
DaemonSet
可以提供机器级别的功能,例如机器监控,机器日志等。这些Pod
有着与机器强相关的生命周期,且优先于其他普通Pod
运行。当机器重启或者关机时,可以安全地终止这些Pod
3.2.1.5.5 ReplicationController
ReplicaSet
可以看做是新一代的ReplicationController
,他们有着相同的使命,且行为相同。此外,ReplicationController
不支持set-based Selector
3.2.2 ReplicationController
ReplicationController
确保:在任何时候,应用都运行着一定数量的副本
3.2.2.1 How a ReplicationController Works
如果Pod
过多,ReplicationController
会停止多余的Pod
。如果Pod
过少,ReplicationController
会启动更多的Pod
。与人工创建的Pod
不同,如果Pod
终结了,或者被删除了,那么ReplicationController
重新启动一个新的Pod
来代替它
在Kubernetes
中,我们用缩写rc
或rcs
来表示ReplicationController
3.2.2.2 Writing a ReplicationController Spec
1 | apiVersion: v1 |
与其他Kubernetes Config
类似,ReplicationController
包含apiVersion
、kind
、metadata
以及spec
这四个字段
.spec.template
: 该字段是.spec
字段的唯一要求的字段。该字段描述的是一个Pod Template
,它拥有与Pod
几乎完全一样的schema
(没有apiVersion
与kind
)。除此之外,必须制定Label
以及Restart Policy
(默认是Always
).metadata.labels
: 通常该字段的值与.spec.template.metadata.labels
一致。如果.metadata.labels
未设置,那么其值默认与.spec.template.metadata.labels
相同。尽管如此,这两个字段的值可以不同,但是.metadata.labels
不影响ReplicationController
的行为.spec.selector
: 该字段定义了一个Label Selector
,ReplicationController
会管理所有与该Label Selector
匹配的Pod
(无论该Pod
是否由ReplicationController
创建,因此要特别注意重叠问题),这就允许在Pod
运行时替换ReplicationController
。如果指定了该字段,那么.spec.template.metadata.labels
与.spec.selector
的值必须相同,否则会被Api Server
拒绝,若.spec.selector
未指定,那么默认与.spec.template.metadata.labels
相同.spec.replicas
: 指定同时运行的副本数量,默认为1
3.2.2.3 Working with ReplicationControllers
3.2.2.3.1 Deleting a ReplicationController and its Pods
若要删除一个ReplicationController
以及相关联的Pod
,那么使用kubectl delete
命令,kubectl
会负责删除所有相关的Pod
3.2.2.3.2 Deleting just a ReplicationController
若仅仅要删除ReplicationController
,那么在使用kubectl delete
命令时,需要加上--cascade=false
选项。当ReplicationController
被删除后,我们便可以创建一个新的ReplicationController
,若新旧ReplicationController
的.spec.selector
也相同的话,那么新的ReplicationController
会接管先前的Pod
,且不会产生影响(即便Pod Template
不同)。若要更新Pod
使其匹配新的Pod Template
,那么需要使用Rolling Update
3.2.2.3.3 Isolating pods from a ReplicationController
我们可以通过改变Pod
的Label
来将其与ReplicationController
分离,这种方式通常用于分离那些用于Debug
或Data Recovery
的Pod
,移除后,ReplicationController
会重新补足Pod
3.2.2.4 Common usage patterns
3.2.2.4.1 Rescheduling
ReplicationController
会保证运行一定数量的副本
3.2.2.4.2 Scaling
我们可以通过修改replicas
字段来进行扩容和缩容
3.2.2.4.3 Rolling updates
ReplicationController
旨在通过逐个替换Pod
来对Service
进行滚动更新
建议的方法是创建一个具有1个副本的新ReplicationController
,逐个扩展新的(+1)和旧的(-1)ReplicationController
,然后在旧的ReplicationController
达到0个副本后删除它
ReplicationController
需要考虑到应用的准备情况,且保证在任意时刻都运行着一定数量的副本。因此这两个ReplicationController
创建的Pod
的Label
需要有区分度,例如image tage
的差异
我们可以通过kubectl rolling-update
命令来进行滚动更新
3.2.2.4.4 Multiple release tracks
在进行滚动升级时,可能同时运行着多个不同的版本,且通常会持续一段时间,我们要对这多个不同版本进行追踪,追踪将依据Label
来进行区分
举例来说,最初一个Service
中的所有Pod
(10个),其Label
都是tier in (frontend), environment in (prod)
。此时,我们需要引入一个新的版本canary
。我们可以创建一个ReplicationController
,将其副本数量设定为9,且Label
为tier=frontend, environment=prod, track=stable
;另一个ReplicationController
,将其副本数量设定为1,Label
为tier=frontend, environment=prod, track=canary
。于是该Service
包含了canary
以及non-canary
两部分
3.2.2.4.5 Using ReplicationControllers with Services
同一个Service
可以有多个不同的ReplicationController
,这样一来,流量可以根据版本进行分流
ReplicationController
不会自我终结,但是它的生命周期与Service
不同。一个Service
包含了由不同ReplicationController
创建的Pod
,且在一个Service
的生命中期中可能会有很多ReplicationController
被创建和销毁,这些对于Client
来说是不感知的
3.2.2.5 Writing programs for Replication
由ReplicationController
创建的Pod
是可替换的,且在语义上是等价的,尽管随着时间的推移,它们会产生一些差异性。显然,这种特性非常适用于无状态的微服务架构。但是ReplicationController
同样可以保持有状态服务架构的高可用性,例如master-elected
、shared
、worker-pool
架构的应用。这些应用需要使用动态的分配机制,而不是静态的一次性的配置。这些动态分配的动作应该由另一个控制器来完成(该控制器是应用的一部分)而不是由ReplicationController
来完成
3.2.2.6 Responsibilities of the ReplicationController
ReplicationController
仅确保Pod
保持额定的数量。在未来,可能会在Replacement Policy
上增加更多的控制,同时引入可供外部用户使用的事件,用来处理更为复杂的Replacement
ReplicationController
的责任仅限于此,ReplicationController
自身并不会引入Readiness Probe
或Liveness Probe
。它通过外部缩放控制器来修改replicas
字段,而不是提供自动缩放的机制。ReplicationController
不会引入Scheduling Policy
3.2.2.7 API Object
ReplicationController
是一个顶层的Kubernetes Rest API
3.2.2.8 Alternatives to ReplicationController
3.2.2.8.1 ReplicaSet
ReplicaSet
可以看做是下一代的ReplicationController
。ReplicaSet
支持set-based Label Selector
。ReplicaSet
作为Deployment
的一种机制来组织Pod
的创建、删除和更新。我们强烈推荐使用Deployement
而不是直接使用ReplicaSet
3.2.2.8.2 Deployment(Recommended)
Deployment
是一种高阶的API Object
,用于以一种非常简单的方式更新ReplicaSet
以及Pod
。Deployment
具有Rolling Update
的能力,且它是声明式的,服务级别的,且拥有额外的功能
3.2.2.8.3 Bare Pods
与直接创建Pod
不同,ReplicationController
会在Pod
删除或者不可用时创建新的Pod
来替换原有的Pod
,例如Node
宕机,或者进行破坏性的维护,例如内核升级。即便我们的应用只需要一个Pod
,也应该使用ReplicationController
而不是直接管理Pod
3.2.2.8.4 Job
对于预期会终止的Pod
而言,推荐使用Job
而不是ReplicationController
3.2.2.8.5 DaemonSet
DaemonSet
可以提供机器级别的功能,例如机器监控,机器日志等。这些Pod
有着与机器强相关的生命周期,且优先于其他普通Pod
运行。当机器重启或者关机时,可以安全地终止这些Pod
3.2.3 Deployments
Deployment
提供了一种声明式的更新Pod
或ReplicaSet
的方式
3.2.3.1 Creating a Deployment
1 | apiVersion: apps/v1 |
- 通过
.metadata.name
字段来指定Deployment
的名字 - 通过
.spec.replicas
字段来指定副本数量 - 通过
.sepc.selector
字段来指定匹配何种标签的Pod
- 通过
.spec.template
字段来描述一个Pod Template
1 | # create deployment |
3.2.3.2 Updating a Deployment
Deployment
会保证同时只有一小部分的Pod
处于升级的过程中(可能会暂时无法提供服务),最多25%
Deployment
会保证同时只有一小部分的Pod
处于创建过程中,最多25%
Deployment
不建议更新Label Selector
,这意味着,我们最初就需要定义好Label Selector
。但Deployment
仍允许我们更新Label Selector
- 增加
Label Selector
,那么Deployment
要求Pod Template
的Label
必须匹配Label Selector
。这个操作是非重叠的,意味着所有旧有的ReplicaSet
以及Pod
将会被孤立,新的ReplicaSet
以及Pod
会被创建出来 - 修改
Label Selector
,会导致增加Label Selector
相同的结果 - 删除
Label Selector
中的key
,不需要修改Pod Template
的Label
。现有的ReplicaSet
不会被孤立,新的ReplicaSet
不会被创建
3.2.3.3 Rolling Back a Deployment
有时候,我们会想要进行回滚,比如新版本不够稳定,或者出现了很严重的错误。Deployment
允许我们回滚到任意一次版本
注意到,只有在Deployment rollout
触发时,才会创建revision
,这意味着,当且仅当Pod Template
发生变化时(例如修改Label
或者镜像),才会创建revision
。其他的更新,例如进行缩容或者扩容,不会创建revision
。这意味着回滚到一个早期的版本,回滚的仅仅是Pod Template
1 | # check the revisions of this deployment |
3.2.3.4 Scaling a Deployment
1 | # scale a deployment |
3.2.3.5 Pausing and Resuming a Deployment
我们可以暂停一个Deployment
,然后进行一些更新操作,再还原该Deployment
1 | # pause deployment |
3.2.3.6 Deployment Status
Deployment
在生命周期中会经历多个不同的状态
progressing
complete
failed
progressing
- 正在创建
ReplicaSet
- 正在扩容
- 正在缩容
complete
- 所有副本都更新到期望的版本了
- 所有副本都可用了
- 没有正在运行的旧版本的副本
failed
- 配额不足
Readiness Probe
失败了- 拉取镜像失败
- 权限不足
- 应用配置有问题
3.2.3.7 Clean up Policy
我们可以通过设置.spec.revisionHistoryLimit
来控制Deployment
保留多少个历史版本,默认是10,超过该数值的ReplicaSet
会被回收
3.2.3.8 Writing a Deployment Spec
与其他Kubernetes Config
类似,Deploymenet
必须包含apiVersion
、kind
、metadata
、spec
四个字段
Pod Template
.spec.template
:.spec
的唯一必须的字段,它描述了一个Pod Template
,Pod Tempalte
的schema与Pod
几乎完全一致(不需要apiVersion
和kind
)Pod Template
必须指定Label
以及Restart Policy
,其中.spec.template.spec.restartPolicy
只允许设置成Always
,默认就是Always
Replicas
.spec.replicas
: 指定副本数量,默认1
Selector
.spec.selector
: 定义了一个Label Selector
.spec.selector
与.spec.template.metadata.labels
必须匹配,否则会被拒绝
Strategy
.spec.strategy
: 定义了新老Pod
更替的策略,可选项有Recreate
、RollingUpdate
,默认是RollingUpdate
Recreate
在更新时,旧的Pod
全部被终结后,新的Pod
才会创建RollingUpdate
: 灰度更新,仅允许一小部分的Pod
进行更替,始终保持服务的可用状态
3.2.3.9 Alternative to Deployments
使用kubectl rolling update
来滚动升级Pod
和ReplicationController
与Deployment
是类似的。但是Deployment
是更推荐的方式,因为它是声明式的,服务级别的,未来可能会有新的功能
3.2.4 StatefulSets
与Deployment
相同,StagefulSet
管理基于相同容器规范的Pod
。与Deployment
不同,StagefulSet
为每个Pod
都生成一个唯一且不可变的标志符,这些标志符全局唯一
3.2.4.1 Using StatefulSets
StagefulSet
有如下特点
- 稳定,唯一的网络标志
- 稳定的持久化存储
- 有序且优雅的部署以及扩缩容
- 有序自动的滚动更新
3.2.4.2 Limitations
3.2.4.3 Components
3.2.4.4 Pod Selector
3.2.4.5 Pod Indentity
3.2.4.6 Deployment and Scaling Guarantees
3.2.4.7 Update Strategies
3.2.5 DaemonSet
3.2.6 Garbage Collection
3.2.7 TTL Controller for Finished Resources
3.2.8 Jobs - Run to Completion
3.2.9 CronJob
4 Services, Load Balancing and Networking
4.1 Services
Pod
是无法再生的。ReplicaSet
可以动态地创建或删除Pod
,每个Pod
都会分配一个IP
,显然随着Pod
的新老更替,这些IP
是不固定的。这就导致了一个问题,如果一个Pod
提供了某种服务给其他位于同一集群中的Pod
,那么这些Pod
如何找到服务提供方呢
Service
的引入解决了这个问题,Service
是一组Pod
以及他们访问方式的抽象。Service
通过Label Selector
来匹配对应的Pod
。Service
解耦了Consumer
和Provider
(从这个角度来说,Service
与RPC框架解决了类似的问题)
4.1.1 Defining a service
与Pod
类似,Service
也是一个Rest Object
,可以通过Api Server
来创建实例,例如
1 | kind: Service |
通过上面这份配置,会创建一个名为my-service
,该Service
会将80
端口的流量路由到任意包含标签app=MyApp
的Pod
的9376
端口
每个Service
都会被分配一个Cluster IP
,Service Proxy
会用到这个Cluster IP
,Service
的Label Selector
匹配会持续进行,其结果过会同步到同名的Endpoint
,Endpoint
维护了Service
与Pod
的映射关系
Service
可以将一个入口端口映射成任意targetPort
,targetPort
默认与port
相同,此外targetPort
还可以是一个字符串,指代端口的名称,不同的Pod
包含的端口名与端口的映射关系可以不同,这提供了非常多的灵活性
Service
支持TCP
、UDP
、SCTP
协议,默认是TCP
协议
4.1.1.1 Services without selectors
Service
通常抽象了访问Pod
的方式,但是它也可以抽象其他后端实体的访问方式,例如
- 在生产环境以及测试环境中使用的
database
是不同的 - 我们想将
Service
暴露给另一个Namespace
或者另一个集群 - 应用的多分实例中,一部分部署在
Kubernetes
集群中,另一部分部署在Kubernetes
集群外
在以上这些情况中,我们可以定义一个没有Label Selector
的Service
,如下
1 | kind: Service |
由于这个Service
没有配置Label Selector
,因此不会有Endpoint
对象生成。但是,我们可以手动配置Endpoint
,如下
1 | kind: Endpoints |
在无Label Selector
的场景下,Service
的工作方式还是一样的。流量会被路由到Endpoint
中
ExternalName Service
是无Label Selector
的,且使用的是DNS
4.1.2 Virtual IPs and service proxies
每个Node
都运行着kube-proxy
这个组件。kube-proxy
为除了ExternalName
类型之外的Service
提供了一种虚拟IP
4.1.2.1 Proxy-mode: userspace
- 该模式最主要的特征是:流量重定向工作是由
kube-proxy
完成的,也就是在用户空间完成的 kube-proxy
会监听Service
的创建和删除,当发现新的Service
创建出来后,kube-proxy
会在localhost
网络开启一个随机端口(记为loPort
)进行监听,同时向iptable
写入路由规则(Cluster IP:Port
->localhost:loPort
),即将流向Service
的流量转发到本地监听的端口上来kube-proxy
会监听Endpoint
的变更,并将Service
及其对应的Pod
列表保存起来
在Pod
中访问Service
的时序图如下
4.1.2.2 Proxy-mode: iptables
- 该模式最主要的特征是:流量重定向的工作是由
iptable
完成的,也就是在内核空间完成的 kube-proxy
会监听Service
、Endpoint
的变化,并且更新iptable
的路由表- 更高效、安全,但是灵活性较差(当某个
Pod
没有应答时,不会尝试其他Pod
)
在Pod
中访问Service
的时序图如下
4.1.2.3 Proxy-mode: ipvs
与iptable
模式类似,ipvs
也是利用netfilter
的hook function
来实现的,但是ipvs
利用的是哈希表,且工作在内核空间,因此效率非常高,同时ipvs
还支持多种负载均衡算法
rr
: round-roginlc
: least connectiondh
: destination hashingsh
: source hashingsed
: shortest expected delaynq
: never queue
在以上任何一种模式中,来自Cluster IP:Port
的流量都会被重定向到其中一个后端Pod
中,且用户不感知这些过程
4.1.3 Multi-Port Services
很多Service
需要暴露多个端口。Kubernetes
支持一个Service
暴露多个端口,在这种方式下,我们必须为每个端口定义一个端口名(端口名只允许包含数字、小写字母以及-
,且必须以数字或小写字母开头和记为)
1 | kind: Service |
4.1.4 Choosing your own IP address
我们可以通过.spec.clusterIP
字段为Service
分配静态的Cluster IP
。该IP
必须是一个合法的Cluster IP
,否则会被Api Server
拒绝(返回422
错误码)
4.1.5 Discovering services
Kubernetes
提供了两种服务发现的方式
Environment Variables
DNS
4.1.5.1 Environment Variables
kubelet
会为每个Service
设置一系列的环境变量,格式为<SERVICE_NAME>_<VARIABLE_NAME>
,服务名和变量名都会被转成大写+下划线的方式
这种方式会引入顺序的问题,如果Pod
想要访问一个Service
,那么这个Service
必须优先于Pod
创建
4.1.5.2 DNS
DNS Server
会监听Service
的创建,并为其创建DNS Record
,如此一来,Pod
就可以通过服务名来访问服务了
举个例子,如果我们有一个Service
,起名字为my-service
,其对应的Namesapce
为my-ns
,那么DNS Server
会为这个Service
创建一个my-service.my-ns
的记录。所有在my-ns
下的Pod
可以通过服务名来访问,即my-service
;对于在其他Namespace
下的Pod
必须通过my-service.my-ns
来访问
4.1.6 Headless services
有时候,我们不需要负载均衡,也不需要Service IP
,我们可以通过.spec.clusterIP = None
来进行配置。这种方式允许开发者与Kubernetes
的服务发现机制解耦,允许开发者使用其他的服务发现机制
在这种模式下,Service
不会被分配Cluster IP
,kube-proxy
也不会处理这些Service
如果该模式的Service
包含Selector
,Endpoint Controller
会创建Endpoint
来记录这个Service
,并且会修改DNS
记录(ServiceName
->Backend Pod IP
)
如果该模式的Service
不包含Selector
,那么Endpoint Controller
不会为Service
创建任何Endponit
4.1.7 Publishing services - service types
有时候,我们的服务需要对外暴露(不仅仅对其他Pod
提供服务,而是对整个Internet提供服务),Kubernetes
允许我们为Service
指定ServiceType
,默认的类型是ClusterIP
,所有的可选类型如下
ClusterIP
: 通过Cluster IP
暴露该Service
,意味着只有在集群内才能访问这个Service
。这是默认的类型NodePort
: 通过NodePort
暴露该Service
,即在集群中所有Node
上都分配一个静态的端口。在该类型下,会自动为Service
创建Cluster IP
用于路由,我们也可以从外部通过<NodeIP>:<NodePort>
来访问这个Service
LoadBalancer
: 通过Load Balancer
对外暴露该Service
。在该类型下,会自动为Service
创建Cluster IP
以及NodePort
用于路由ExternalName
: 通过CNAME
暴露该服务,将服务映射到externalName
字段对应的域名中,完全由DNS
负责路由
4.1.7.1 Type NodePort
对于这种模式的Service
,集群中所有的Node
都会代理该相同的Port
,该端口号对应于Service
的.spec.ports[*].nodePort
配置项
我们可以通过--nodeport-addresses
选项来指定一个或一组IP
的范围(多个的话,以,
分隔),该选项的默认是是空[]
,意味着最大的IP
范围
我们可以通过nodePort
来指定暴露的Port
,因此我们需要注意端口冲突的问题,且必须属于合法的NodePort
范围
这种方式给予了开发者更多的自由度,允许配置自己的负载均衡服务,允许在精简版的Kubernetes
环境中使用Service
,允许我们直接暴露Node
的IP
来使用Service
服务可以通过<NodeIP>:spec.ports[*].nodePort
或者.spec.clusterIP:spec.ports[*].port
两种方式进行访问
4.1.7.2 Type LoadBalancer
对于提供负载均衡服务的云环境,我们可以将Service
指定为LoadBalancer
类型,Kubernetes
会为Service
创建LoadBalancer
,事实上,LoadBalancer
的创建过程与Service
的创建过程是异步的。当LoadBalancer
发布后,会更新Service
的.status.loadBalancer
字段
1 | kind: Service |
LoadBalancer
接收到来自外部的流量后,会直接路由到提供服务的Pod
,具体方式依赖于云服务提供商的实现。一些云服务提供商允许指定loadBalancerIP
,在这种情况下会以配置的IP
来创建LoadBalancer
,若loadBalancerIP
未配置,则会分配一个随机的IP。如果云服务商不提供这个功能,那么这个字段将会被忽略
4.1.8 The gory details of virtual IPs
本小节将介绍Service
的一些细节问题
4.1.8.1 Avoiding collisions
Kubernetes
的哲学之一就是尽量避免用户因为自身以外的因素,导致应用无法正常工作。对于端口而言,用户自己选择端口号,将会有一定概率导致冲突,因此,Kubernetes
为用户自动分配端口号
Kubernetes
确保每个Service
分配到的IP
在集群中是唯一的。具体做法是,Kubernetes
会在etcd
中维护一个全局的分配映射表
4.1.8.2 IPs and VIPs
Pod IP
通常可以定位到一个确定的Pod
,但是Service
的Cluster IP(Virtual IP, VIP)
通常映射为一组Pod
,因此,Kubernetes
利用iptables
将Cluster IP
进行重定向。因此,所有导入VIP
的流量都会自动路由到一个或一组Endpoint
中去。Service
的环境变量和DNS实际上是根据服务的VIP
和端口填充的
Kubernetes
支持三种不同的模式,分别是userspace
、iptables
、ipvs
,这三者之间有微小的差异
4.2 DNS for Services and Pods
4.2.1 Introduction
每个Service
都会分配一个DNS Name
。通常情况下,一个Pod
的DNS
搜索范围包括Pod
所在的Namespace
以及集群的默认domain
。举例来说,Namspace bar
中包含Service foo
,一个运行在Namspace bar
中的Pod
可以通过foo
来搜索这个Service
,一个运行在Namspace quux
中的Pod
可以通过foo.bar
来搜索这个Service
4.2.2 Services
4.2.2.1 A Records
Pod
会被分配一个A Record
,其格式为<pod-ip-address>.<my-namespace>.pod.cluster.local
- 例如一个
Pod
,其IP
为1.2.3.4
,其Namespace
为default
,且DNS Name
为cluster.locals
,那么对应的A Record
为1-2-3-4.default.pod.cluster.local
4.2.3 Pods
4.2.3.1 Pod’s hostname and subdomain fields
- 当一个
Pod
创建时,它的hostname
就是metadata.name
的值 Pod
还可以指定spec.hostname
,若spec.hostname
与metadata.name
同时存在时,以spec.hostname
为准Pod
还可以指定spec.subdomain
。例如,若一个Pod
,其spec.hostname
为foo
,spec.subdomain
为bar
,Namespace
为my-namespace
,则对应的FQDN
为foo.bar.my-namespace.pod.cluster.local
1 | apiVersion: v1 |
4.2.3.2 Pod’s DNS Policy
可以基于每个Pod
设置DNS Policy
。目前,Kubernetes
支持以下几种DNS Policy
Default
: 从Node
中继承DNS
配置ClusterFirst
: 任何不匹配集群域名后缀的DNS Query
都会转发到从Node
中继承而来的上游DNS
服务器ClusterFirstWithHostNet
: 若Pod
以hostNetwork
模式运行,那么DNS
必须设置为ClusterFirstWithHostNet
None
: 忽略Kubernetes
的DNS Policy
,同时依赖spec.dnsConfig
提供更细粒度的配置
1 | apiVersion: v1 |
4.2.3.3 Pod’s DNS Config
Kubernetes
在v1.9
版本后允许用户进行更细粒度的DNS
配置,需要通过--feature-gates=CustomPodDNS=true
选项来开启该功能。开启功能后,我们就可以将spec.dnsPolicy
字段设置为None
,并且新增一个字段dnsConfig
,来进行更细粒度的配置
dnsConfig
支持以下几项配置
nameservers
:DNS
服务器列表,最多可以设置3个,最少包含一个searches
:DNS Search Domain
列表,最多支持6个options
: 一些键值对的列表,每个键值对必须包含Key
,但是Value
可以没有
1 | apiVersion: v1 |
4.3 Connecting Applications with Services
在讨论Kubernetes
的网络通信之前,我们先对比一下Docker
的常规网络通信方式
默认情况下,Docker
使用的是host-private
网络,因此,只有处于同一个机器上的不同Container
之间才可以通信。因此要想跨Node
进行通信,那么必须为Container
所在的机器分配IP
用以代理该Container
,这样一来就必须处理IP
以及Port
冲突的问题
在不同的开发者之间进行Port
的统一分配是一件非常困难的事情,并且会增加扩容/缩容的复杂度。Kubernetes
首先假设Pod
可以与其他Pod
进行通信,且无视他们所属的Node
,Kubernetes
会为每个Pod
分配一个cluster-private-IP
,且我们无需处理这些映射关系。这意味着,位于同一个Node
上的Pod
自然可以通过localhost
进行通信,位于不同Node
上的Pod
无需使用NAT
也可以进行通信,下面将详细介绍Kubernetes
的实现方式
4.3.1 Exposing pods to the cluster
下面,我们用一个Nginx Pod
作为例子,进行介绍
1 | apiVersion: apps/v1 |
下面创建一个Deployment Object
1 | # 创建 Deployment |
注意到,这些Pod
并没有用附属Node
的80
端口,也没有配置任何NAT
规则来路由流量,这意味着,我们可以在同一个Node
上部署多个Pod
,并且利用IP
来访问这些Pod
登录Pod
的命令如下
1 | kubectl exec -it <pod-name> -n <namespace> -- bash |
4.3.2 Create a Service
理论上,我们可以直接使用这些Pod
的IP
来与之通信,但是一旦Node
挂了之后,又会被Deployment
部署到其他健康的Node
中,并分配一个新的Pod IP
,因此,会带来非常大的复杂度
Serivce
抽象了一组功能相同的Pod
。每个Service
在创建之初就会被分配一个独有的IP
,称为Cluster IP
,该IP
在Service
的生命周期中保持不变,进入Service
的流量会通过负载均衡后,路由到其中一个Pod
上
接着上面的例子,我们可以通过kubectl expose
来创建一个Service
1 | # 创建service |
kubectl expose
等价于kubectl create -f <如下配置文件>
1 | apiVersion: v1 |
根据上面的定义,该Serivce
会代理所有匹配run: my-nginx
的Pod
。其中port
是Serivce
的流量入口端口;targetPort
是Pod
的流量入口端口,默认与port
相同
这些Pod
通过Endpoint
露出,Service
会持续筛选匹配Selector
的Pod
,并将结果输送到与Pod
同名的Endpoint
对象中。当一个Pod
挂了之后,它会自动从Endpoint
中被移除,新的Pod
随即会被创建,并添加到Endpoint
中
4.3.3 Accessing the Service
Kubernetes
提供了两种服务发现的方式:Environment Variables
以及DNS
4.3.3.1 Environment Variables
当Pod
运行在Node
之后,kubectl
会为每个Service
设置一些环境变量。这种方式会引入顺序问题
首先,我们查看一下现有Pod
的环境变量
1 | kubectl exec <pod name> -- printenv | grep SERVICE |
可以看到,这里没有我们创建的Service
的相关信息,这是因为我们在创建Service
之前,首先创建了Pod
。另一个弊端是,Scheduler
可能会将上述两个Pod
部署到同一个Node
中,这会导致整个Service
不可用,我们可以通过杀死这两个Pod
并等待Deployment
重新创建两个新的Pod
来修复这个问题
1 | # 杀死现有的Pod,并创建新的Pod |
4.3.3.2 DNS
Kubernetes
提供了一个DNS cluster addon Service
,它会为每个Service
分配一个DNS Name
,我们可以通过如下命令查看
1 | kubectl get services kube-dns --namespace=kube-system |
在集群中的任何Pod
都可以用标准的方式来访问Service
,我们运行另一个curl
应用来进行测试
1 | # 以交互的方式运行一个 container |
4.3.4 Securing the Service
对于需要对外露出的Service
,我们可以为其添加TLS/SSL
1 | #create a public private key pair |
下面创建一个Secret
,配置如下
1 | apiVersion: "v1" |
1 | kubectl create -f nginxsecrets.yaml |
现在需要替换掉之前的nginx
服务,配置如下
1 | apiVersion: v1 |
上述的配置清单包括
Deployment
以及Service
nginx
暴露了80
以及443
端口,Service
露出了这两个端口,分别是8080
以及443
端口Container
通过挂载到/etc/nginx/ssl
上的卷来获取secret key
利用上述配置,替换原先的nginx
1 | kubectl delete deployments,svc my-nginx; kubectl create -f ./nginx-secure-app.yaml |
于是我们就能通过Service
来访问Nginx Server
了
1 | # 查询Server的Cluster Ip |
1 | apiVersion: apps/v1 |
1 | # 创建另一个curl pod |
4.3.5 Exposing the Service(未完成)
如果我们的应用想要对外露出,Kubernetes
提供了两种方式,即NodePort
以及LoadBalancer
,上面的例子中,使用的是NodePort
方式,因此如果Node
本身就有Public IP
,那么就可以对外提供服务了
1 | # 查看nodePort |
4.4 Ingress
Ingress
用于管理Service
的访问方式(通常是HTTP
)
Ingress
可以提供Load Balancing
、SSL Termination
以及Virtual Host
等服务
4.4.1 Terminology
涉及到的相关术语
Node
:Kubernetes
集群中的虚拟机或者物理机Cluster
: 由一组Node
组成,通常它们由Kubernetes
进行管理Edge Router
: 用于执行防火墙策略的路由器,通常形态是云服务商提供的网关或者是一个硬件Cluster Network
: 用于进群内通信的网络基础设施Service
: 定义了一组满足特定Label Selector
的Pod
,Serivce
含有一个仅在集群内有效的Virtual IP
4.4.2 What is Ingress?
Ingress
定义从Internet
到Service
的路由规则,因此Ingress
可以控制外来访问流量
Ingress
通常包含LoadBalancer
、Edge Router
以及一些其他用于处理流量的组件
Ingress
不露出任何协议以及端口,要想暴露Service
而不是HTTP/HTTPS
的话,应该使用Service.Type
(设置成NodePort
或者LoadBalancer
方式)
4.4.3 Ingress controllers
为了使得Ingress
能够正常工作,必须要在集群运行一个Ingress Controller
,该Ingress Controller
与其他Controller
不同,它不属于kube-controller-manager
的一部分,且不会自动启动
4.4.4 The Ingress Resource
1 | apiVersion: extensions/v1beta1 |
与其他Kubernetes Object
相同,Ingress
需要apiVersion
、kind
、metadata
三个字段
每个HTTP Rule
都包含了如下的信息
host
: 匹配指定的host
path
: 匹配指定的path
,每个path
都包含了一个后端的serviceName
以及servicePort
backend
: 任何匹配host
以及path
的请求,都会被路由到backend
对应的Service
中
如果一个Ingress
没有配置任何的rule
,那么所有流量都会被路由到一个default backend
;如果流量不匹配任何的host
以及path
,那么该流量也会被路由到default backend
default backend
可以在Ingress Controller
中进行配置
4.4.5 Types of Ingress
4.4.5.1 Single Service Ingress
一个Ingress
只对应了一个后端的Service
1 | apiVersion: extensions/v1beta1 |
4.4.5.2 Simple fanout
一个Ingress
对应着多个Service
1 | foo.bar.com -> 178.91.123.132 -> / foo service1:4200 |
1 | apiVersion: extensions/v1beta1 |
4.4.5.3 Name based virtual hosting
该类型常用于将多个Service
通过同一个IP暴露出去,且对外的域名是不同的
1 | foo.bar.com --| |-> foo.bar.com s1:80 |
1 | apiVersion: extensions/v1beta1 |
4.4.5.4 TLS
我们可以在Ingress
之上增加TSL/SSL
协议
1 | apiVersion: v1 |
1 | apiVersion: extensions/v1beta1 |
4.4.5.5 Loadbalancing
一个Ingress Controller
通常支持一些负载均衡的设置,包括负载均衡算法、权重策略等,目前尚不支持一些负载均衡的高级配置
4.4.6 Updating an Ingress
1 | kubectl edit ingress test |
4.5 Ingress Controller
为了使得Ingress
能够生效,我们必须运行一个Ingress Controller
。与其他Controller
不同,Ingress Controller
不会默认启动
4.5.1 Additional controllers
Ingress Controller
有许多不同的实现
Ambassador
AppsCode Inc
Contour
Citrix
F5 Networks
Gloo
HAProxy
Istio
Kong
NGINX, Inc
Traefik
4.6 Network Policies
Network Policy
定义了Pod
之间或者Pod
与其他Endpoint
之间的通信方式
4.6.1 Isolated and Non-isolated Pods
默认情况下,Pod
都是non-isolated
,意味着,它可以接收来自任何源的流量
当Pod
匹配某个NetworkPolicy
后,它就变成isolated
的了,于是,它会拒绝所有不满足NetworkPolicy
规则的流量
4.6.2 The NetworkPolicy Resource
1 | apiVersion: networking.k8s.io/v1 |
- 与其他
Kubernetes Object
相同,NetworkPolicy
需要apiVersion
、Kind
、metadata
三个字段 spec
: 描述NetworkPolicy
的最主要的字段spec.podSelector
: 用于匹配Pod
的Selector
。一个空的podSelector
会选择当前Namespace
下的所有Pod
spec.policyTypes
: 可以是Ingress
、Egress
或者两者。默认情况下,会包含Ingress
,且如果包含任何Egress
规则,那么也会包含Egress
-
ingress
: 每个NetworkPolicy
都包含了一个ingress rule
列表,每项规则包含from
以及ports
两项。其类型可以是ipBlock
、namespaceSelector
或者podSelector
-
egress
: 每个NetworkPolicy
都包含另一个egress rule
列表,每项规则包含to
以及ports
两项
-
4.6.3 Behavior of to and from selectors
igress
的from
部分与egress
的to
部分可以包含如下四种类型
podSelector
: 在NetworkPolicy
所在的Namespace
下选择特定的Pod
namespaceSelector
: 选择特定的Namespace
下的所有Pod
podSelector
和namespaceSelector
: 选择特定Namespace
下的特定Pod
ipBlock
: 选择特定的IP CIDR
范围,且必须是cluster-external IP
区分以下两种配置的区别
1 | ... |
这种配置包含一个规则: podSelector
和namespaceSelector
1 | ... |
这种配置包含两种规则: podSelector
或namespaceSelector
4.6.4 Default policies
默认情况下,不存在任何Policy
,但是我们可以修改默认的行为
4.6.4.1 Default deny all ingress traffic
1 | apiVersion: networking.k8s.io/v1 |
4.6.4.2 Default allow all ingress traffic
1 | apiVersion: networking.k8s.io/v1 |
4.6.4.3 Default deny all egress traffic
1 | apiVersion: networking.k8s.io/v1 |
4.6.4.4 Default allow all egress traffic
1 | apiVersion: networking.k8s.io/v1 |
4.6.4.5 Default deny all ingress and all egress traffic
1 | apiVersion: networking.k8s.io/v1 |
4.7 Adding entries to Pod /etc/hosts with HostAliases
当没有DNS的时候,我们可以通过配置/etc/hosts
来提供一种Pod-Level
的域名解决方案
4.7.1 Default Hosts File Content
1 | kubectl run nginx --image nginx --generator=run-pod/v1 |
4.7.2 Adding Additional Entries with HostAliases
通过为Pod
配置.spec.hostAliases
属性,可以增加额外的域名解析规则,如下
1 | apiVersion: v1 |
1 | kubectl apply -f hostaliases-pod.yaml |
4.7.3 Why Does Kubelet Manage the Hosts File?
kubelet
为每个Container
管理host
文件,是为了避免Docker
在启动容器后修改该文件
由于文件托管的性质,只要在Container
重新启动或者Pod
重新被调度的情况下,kubelet
都会重新载入host
文件,任何用户编写的内容都将被覆盖,因此,不建议修改文件的内容
5 Storage
5.1 Volumes
Container
中的磁盘文件是短暂的,这会带来一些问题。首先,当一个Container
崩溃之后,kubelet
会重启该Container
,但是这些磁盘文件会丢失。其次,在一个Pod
中运行的不同Container
之间可能需要共享一些文件。因此Kubernetes
利用Volume
来解决上述问题
5.1.1 Backgroud
Docker
也有Volume
的相关概念,但是其能力相比于Kubernetes
较弱。在Docker
中,一个Volume
就是磁盘中或者其他Container
中的一个目录,Volume
的生命周期不受管控。Docker
现在还提供了Volume Driver
,但是其功能还是非常薄弱
Kubernetes
中的Volume
有明确的生命周期,Volume
的生命周期比Pod
中任何Container
的生命周期更长,因此数据能够在Container
重启时保留。当然,如果一个Pod
停止了,那么Volume
也会相应停止。此外,Kubernetes
提供了多种类型的Volume
,且Pod
可以同时使用任意类型,任意数量的Volume
本质上而言,Volume
就是一个包含数据的可被Container
访问的目录,至于该目录是如何形成的,支持它的介质以及存储的内容是由具体的类型决定的
我们可以通过配置.spec.volumes
字段来指定Volume
的类型以及相应的参数,通过.spec.containers.volumeMounts
来指定具体的挂载目录
在Container
中的应用可以看到由Docker Image
以及Volume
组成的文件系统视图。Docker Image
位于文件系统的顶层,所有的Volume
必须挂载到Image
中。Volume
不能挂载到其他Volume
中或者与其他Volume
存在hard link
。在Pod
中的每个Container
必须独立地指定每个Volume
的挂载目录
5.1.2 Types of Volumes
- awsElasticBlockStore
- azureDisk
- azureFile
- cephfs
- configMap
- csi
- downwardAPI
- emptyDir:由容器运行时管理,容器退出就销毁了
- fc (fibre channel)
- flexVolume
- flocker
- gcePersistentDisk
- gitRepo (deprecated)
- glusterfs
- hostPath
- iscsi
- local
- nfs
- persistentVolumeClaim
- projected
- portworxVolume
- quobyte
- rbd
- scaleIO
- secret
- storageos
- vsphereVolume
5.2 Persistent Volumes
5.3 Storage Classes
5.4 Volume Snapshot Classes
5.5 Dynamic Volume Provisioning
5.6 Node-specific Volume Limits
6 Network
6.1 Overview
首先,我们来明确一下,Kubernetes面临的网络问题
- Highly-coupled Container-to-Container communications:高耦合的
Container
之间的网络通信,通过pods
以及localhost
通信来解决 - Pod-to-Pod communications:本小节将详细展开说明
- Pod-to-Service communications:通过
services
来解决 - External-to-Service communications:通过
services
来解决
6.2 Docker Model
我们先来回顾一下Docker的网络模型,这对于理解Kubernetes的网络模型是很有必要的。在默认情况下,Docker
利用host-private networking
,Docker
创建了一个虚拟网桥(virtual bridge),默认为docker0
。对于Docker
创建的每个Container
都会有一个连接到网桥的虚拟以太网卡(virtual Ethernet device)veth
,从Container
内部来看,veth
就被映射成了eth0
网卡
在这种网络模型下,只要位于同一个物理机上(或者同一个虚拟网桥上),所有的Container
之间可以进行通信。但是位于不同物理机上的Container
是无法进行通信的
为了让Container
可以跨node
进行交互,必须为它们分配一个宿主物理机的ip
。这样一来,我们就需要付出额外的精力维护ip
以及port
6.3 Kubernetes model
Kubernetes
要求网络模型必须满足如下条件
- 所有
Container
之间的通信不能依赖NAT - 所有
node
与Container
之间的通信不能依赖NAT - 某个
Container
在内部、外部看到的ip
一致
这种模式不仅总体上不那么复杂,而且主要与Kubernetes希望将应用程序从VM轻松移植到容器的愿望兼容
到目前为止,都在讨论Container
,但事实上,Kubernetes
在Pod
范围上使用ip
地址,因此,在一个Pod
内的所有Container
共享网络命名空间(network namespaces),当然包括ip地址。这意味着,在一个Pod
内的Container
可以通过localhost
与其他Container
进行通信。这称为“IP-per-pod”模式,在这种模式下,一个Pod
需要有一个pod contaner
来管理网络命名空间,其他app container
利用Docker
的--net=container:<id>
参数来加入这个网络命名空间即可
6.4 Kubernetes networking model implements
Kubernetes的网络模型有很多种实现方式,包括但不仅限如下几种
- ACI
- AOS from Apstra
- Big Cloud Fabric from Big Switch Networks
- Cilium
- CNI-Genie from Huawei
- Contiv
- Contrail
- Flannel
- Google Compute Engine (GCE)
- Kube-router
- L2 networks and linux bridging
- Multus (a Multi Network plugin)
- NSX-T
- Nuage Networks VCS (Virtualized Cloud Services)
- OpenVSwitch
- OVN (Open Virtual Networking)
- Project Calico
- Romana
- Weave Net from Weaveworks
6.5 参考
7 Question
Pod IP
在Namespace下唯一,既然可以通过Namespace
+Pod IP
准确定位一个Pod
,为什么还需要flannel
flannel
保证了在同一个集群中的Pod
的ip不重复
8 参考
- 英文文档1
- 中文文档1
- 中文文档2
- k8s-api-reference
- k8s-阿里云公开课
- nginx ingress doc
- metallb doc
- Borg、Omega 和 Kubernetes:谷歌十几年来从这三个容器管理系统中得到的经验教训
- Kubernetes核心概念总结
- Kubernetes之Service
- Kubernetes学习4–容器之间通讯方式及Flannel工作原理
- Flannel网络原理
- 解决Flannel跨主机互联网络问题【Docker】
- Kubernetes Nginx Ingress 教程
- 使用kubeadm安装Kubernetes 1.12
- forbidden: User “system:serviceaccount:kube-system:default” cannot get namespaces in the namespace "default