Pod 是Kubernetes 集群中的基本单元,是最小并且最简单的 Kubernetes 对象,是 Kubernetes 调度的基本单位。
Pod 的设计理念是,支持多个容器在一个 Pod 中共享网络、存储以及 CPU、内存等资源,其具体特性如下:
- 多个容器共享 IPC、Network 和 UTC namespace
- 多个容器可直接通过 localhost 通信
- 多个容器共享Volume,可以访问共享数据
- 优雅终止,Pod 删除的时候先给其内的进程发送 SIGTERM,等待一段时间后才强制停止
- 特权容器具有改变系统配置的权限
生命周期
pod的生命周期包括了pod的状态、init container、hooks、重启策略、健康检查
pod状态
Kubernetes 以 PodStatus.Phase
抽象 Pod 的状态,包括:
- Pending 等待中
- Running 运行中
- Succeeded 正常终止
- Failed 异常终止
- Unknonwn 未知状态
Kubernetes以PodStatus.Conditions.type
表示Pod更详细的condition状态,包括:
- PodScheduled 调度中
- Initialized 容器初始化完成
- Ready 准备就绪,可以正常服务
- Unschedulable 不能被调度
Kubernetes以PodStatus.containerStatuses.state
表示容器的状态,包括:
- waiting
- created
- running
- exited
- unknown
Kubernetes以PodStatus.containerStatuses.state.reason
表示容器状态出现的可能原因,包括:
-
CrashLoopBackOff 容器退出,kubelet正在将它重启
-
InvalidImageName 无法解析镜像名称
-
ImageInspectError 无法校验镜像
-
ErrImageNeverPull 策略禁止拉取镜像
-
ImagePullBackOff 正在重试拉取
-
RegistryUnavailable 连接不到镜像中心
-
ErrImagePull 通用的拉取镜像出错
-
CreateContainerConfigError 不能创建kubelet使用的容器配置
-
CreateContainerError 创建容器失败
-
RunContainerError 启动容器失败
-
PostStartHookError 执行hook报错
-
ContainersNotInitialized 容器没有初始化完
-
ContainersNotReady 容器没有准备就绪
-
ContainerCreating 容器创建中
-
PodInitializing pod 初始化中
-
DockerDaemonNotReady Docker没有启动
-
NetworkPluginNotReady 网络插件没有启动
在Pod的生命周期里通常会产生各种事件Events,kubernetes事件的种类总共有4种,包括:
- ADDED
- MODIFIED
- DELETED
- ERROR
生命周期钩子
容器生命周期钩子(Container Lifecycle Hooks),监听容器生命周期的特定事件,并在事件发生时执行已注册的回调函数,支持两种钩子:
- postStart 容器创建后立即执行,如果执行失败,容器会被杀死
- preStop 容器终止前执行,如果执行失败,容器会被杀死
钩子的回调函数支持两种方式:
- exec 在容器中执行命令,返回
0
则表示成功,否则表示失败 - httpGet 对指定的容器 IP、端口和路径执行 HTTP Get 请求,返回状态码在 [200, 400] 之间则表示成功,否则表示失败
apiVersion: v1
kind: Pod
metadata:
name: lifecycle
spec:
containers:
- name: lifecycle
image: nginx
lifecycle:
postStart:
httpGet:
path: /
port: 80
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
Init Container
Init 容器在所有容器运行之前执行,常用来初始化配置。
Init 容器会按顺序一次运行一个,每个 Init 容器必须运行成功,下一个才能够运行。
apiVersion: v1
kind: Pod
metadata:
name: init
spec:
initContainers:
- name: install
image: busybox
command:
- wget
- "-O"
- "/work-dir/index.html"
- http://kubernetes.io
containers:
- name: nginx
image: nginx
重启策略
Kubernetes 以PodSpec.restartPolicy
设置是否对退出的 Pod 重启,包括:
- Always
- OnFailture
- Never
注意,这里的重启是指在 Pod 所在 Node 上面本地重启,并不会调度到其他 Node 上去。
健康检查
Kubernetes 提供了两种探针(Probe)方式来探测容器的健康状态:
- LivenessProbe 应用是否处于健康状态,如果不健康则重启容器
- ReadinessProbe 应用是否处于正常服务状态,如果不正常则不接收来自Service 的流量
Kubernetes 提供了三种方式来执行探针:
- exec 在容器中执行命令,返回
0
则表示探测成功,否则表示失败 - tcpSocket 对指定的IP 和端口执行 TCP 检查,端口是开放的则表示探测成功,否则表示失败
- httpGet 对指定的容器 IP、端口和路径执行 HTTP Get 请求,返回状态码在 [200, 400] 之间则表示探测成功,否则表示失败
apiVersion: v1
kind: Pod
metadata:
labels:
app: nginx
name: nginx
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: http
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 15
periodSeconds: 20
timeoutSeconds: 5
readinessProbe:
exec:
command:
- cat
- /usr/share/nginx/html/index.html
initialDelaySeconds: 5
periodSeconds: 20
timeoutSeconds: 5
私有镜像
在使用私有镜像时,需要创建一个 docker registry secret,并在容器中引用。
kubectl create secret docker-registry <your-registry-secret-name> --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>
在引用 docker registry secret 时,有两种方法:
- 直接在 Pod 描述文件中引用
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullSecrets:
- name: <your-registry-secret-name>
- 把 secret 添加到 service account 中,再通过 service account 引用
kubectl patch serviceaccount <your-serviceaccount> -p '{"imagePullSecrets": [{"name": "<your-registry-secret-name>"}]}'
拉取镜像时,支持三种拉取策略ImagePullPolicy,包括:
- Always
- IfNotPresent (默认)
- Never
资源限制
Kubernetes 通过 cgroups 限制容器的 CPU 和内存等计算资源,包括 requests和limits。
requests.cpu
和requests.memory
是调度cpu和内存的依据。
apiVersion: v1
kind: Pod
metadata:
labels:
app: nginx
name: nginx
spec:
containers:
- image: nginx
name: nginx
resources:
requests:
cpu: "300m"
memory: "56Mi"
limits:
cpu: "1"
memory: "128Mi"
带宽限制
Kubernetes通过注解(annotation )来限制 Pod 的网络带宽,包括:
- kubernetes.io/ingress-bandwidth
- kubernetes.io/egress-bandwidth
apiVersion: v1
kind: Pod
metadata:
name: nginx
annotations:
kubernetes.io/ingress-bandwidth: 3M
kubernetes.io/egress-bandwidth: 4M
spec:
调度限制
Kubernetes通过nodeSelector、nodeAffinity、podAffinity 、Taints 和 tolerations 等来将 Pod 调度到需要的 Node 上。
限制Pod 调度到指定的 Node 节点上,有三种方式:
- nodeSelector 选择指定标签的节点
- nodeAffinity 选择指定亲和性的节点
- podAffinity 指pod 间亲和与反亲和
限制Pod不允许调度到不合适的Node节点上,有两种方式:
- Taints
- Tolerations
nodeSelector
首先给节点打标签
kubectl label nodes node-1 app=nginx
然后在 nodeSelector中指定标签
spec:
nodeSelector:
app: nginx
除了自己添加的标签外,Kubernetes还预置了一组标准标签:
- kubernetes.io/hostname
- failure-domain.beta.kubernetes.io/zone
- failure-domain.beta.kubernetes.io/region
- beta.kubernetes.io/instance-type
- kubernetes.io/os
- kubernetes.io/arch
nodeAffinity
Affinity
,即亲和性。亲和功能包含两种类型:
- 节点亲和。约束节点标签,提供了比
nodeSelector
更为灵活的调度控制能力。 - pod 间亲和/反亲和。约束 pod 标签。
调度策略分为软策略和硬策略:
- 软策略指,如果节点没有满足调度条件,pod 会忽略这条规则,继续完成调度,即满足条件最好,没有满足也无所谓;
- 硬策略指,如果没有满足条件的节点,就不断重试直到满足条件为止,即必须满足条件,不满足不行;
nodeAffinity目前支持两种条件选择:
- requiredDuringSchedulingIgnoredDuringExecution 表示 pod 部署时节点必须满足的条件,如果没有满足条件的节点,就不断重试,即硬策略,仅将 pod 运行在满足条件的节点上
- preferredDuringSchedulingIgnoredDuringExecution 表示优先选择满足条件的节点进行部署,如果没有满足条件的节点,就忽略这些条件,按照正常逻辑部署,即软策略,尝试将 pod 运行在满足条件的节点上
IgnoredDuringExecution,表示pod 部署之后,进入运行态时,如果节点标签发生了变化,不再满足 pod 指定的条件,pod 也还会继续运行,即亲和选择只在 pod 调度期间有效
节点亲和通过 PodSpec 的 affinity
字段下的 nodeAffinity
字段进行指定
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
如果你指定了多个与 nodeAffinity
类型关联的 nodeSelectorTerms
,则如果其中一个 nodeSelectorTerms
满足的话,pod将可以调度到节点上;
如果你指定了多个与 nodeSelectorTerms
关联的 matchExpressions
,则只有当所有 matchExpressions
满足的话,pod 才会可以调度到节点上;
preferredDuringSchedulingIgnoredDuringExecution
中的 weight
字段值的范围是 1-100
节点亲和语法支持下面的操作符: In
,NotIn
,Exists
,DoesNotExist
,Gt
,Lt
,可以使用 NotIn
和 DoesNotExist
来实现节点反亲和行为
podAffinity
pod 间亲和与反亲和,podAffinity
和podAntiAffinity
,指可以基于pod的标签来约束 pod 可以调度到的节点,而不是基于节点的标签。
pod 间亲和与反亲和的规则是,基于已经在节点上运行的 pod 的标签来约束 pod 可以调度到的节点,可以这样理解,pod调度的时候选择(或者不选择)节点 N,取决于已经在这些节点上运行的pod的标签,和这些pod的标签是否满足条件 X,条件 X 是一组标签选择器,它必须指明作用的 namespace。
pod 间亲和与反亲和目前支持两种条件选择:
- requiredDuringSchedulingIgnoredDuringExecution
- preferredDuringSchedulingIgnoredDuringExecution
pod 间亲和与反亲和的设计需求来自哪里,我们可以看看它的一些实际使用场景:
- 我们希望一些服务的pod可以调度到同一个节点,因为这些服务有比较强的关联性,比如webserver和redis;
- 我们希望一些服务的pod副本可以分布到不同节点上,因为我们想要保证这些副本的可靠性,比如redis;
pod 间亲和与反亲和通过 PodSpec 的 affinity
字段下的podAffinity
或podAntiAffinity
字段进行指定
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: web-store
replicas: 3
template:
metadata:
labels:
app: web-store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-store
topologyKey: "kubernetes.io/hostname"
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
Pod 亲和与反亲和的合法操作符有 In
,NotIn
,Exists
,DoesNotExist
原则上,topologyKey
可以是任何合法的标签键。然而,出于性能和安全原因,topologyKey 受到一些限制:
- 对于亲和与
requiredDuringSchedulingIgnoredDuringExecution
要求的 pod 反亲和,topologyKey
不允许为空; - 对于
requiredDuringSchedulingIgnoredDuringExecution
要求的 pod 反亲和,准入控制器LimitPodHardAntiAffinityTopology
被引入来限制topologyKey
不为kubernetes.io/hostname
。如果你想使它可用于自定义拓扑结构,你必须修改准入控制器或者禁用它; - 对于
preferredDuringSchedulingIgnoredDuringExecution
要求的 pod 反亲和,空的topologyKey
被解释为“所有拓扑结构”(这里的“所有拓扑结构”限制为kubernetes.io/hostname
,failure-domain.beta.kubernetes.io/zone
和failure-domain.beta.kubernetes.io/region
的组合); - 除上述情况外,
topologyKey
可以是任何合法的标签键;
Taints 和 tolerations
Taints 和 tolerations 用于保证 Pod 不被调度到不合适的 Node 上,其中 Taint 应用于 Node 上,而 toleration 则应用于 Pod 上。
目前支持的 taint 类型:
- NoSchedule:新的 Pod 不允许调度到该 Node 上,不影响正在运行的 Pod
- PreferNoSchedule:新的 Pod尽量不调度到该 Node 上
- NoExecute:新的 Pod 不调度到该 Node 上,并且删除(evict)已在运行的 Pod,Pod 可以增加一个时间(tolerationSeconds)
首先,给节点打标签
kubectl taint nodes node1 key1=value1:NoSchedule
然后,在PodSpec 中中指定tolerations
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
优先级调度
kube-scheduler 支持定义 Pod 的优先级,从而保证高优先级的 Pod 优先调度。
首先,定义一个 PriorityClass
apiVersion: v1
kind: PriorityClass
metadata:
name: high-priority
value: 100
globalDefault: false
description: ""
value
为 32 位整数的优先级,该值越大,优先级越高
然后,在 PodSpec 中通过 PriorityClassName 设置 Pod 的优先级
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority
增强功能
设置DNS访问策略
通过设置 dnsPolicy 参数可以设置pod访问DNS的策略,支持三种模式:
- ClusterFirst 优先从coreDNS 查询 (默认策略)
- Default 优先从Node 中配置的 DNS查询
- None
设置pod子域名
通过subdomain 参数设置 Pod 的子域名,默认为空
设置pod的DNS选项
通过dnsConfig设置pod的DNS选项
apiVersion: v1
kind: Pod
metadata:
namespace: default
name: dns-example
spec:
containers:
- name: test
image: nginx
dnsPolicy: "None"
dnsConfig:
nameservers:
- 1.2.3.4
searches:
- ns1.svc.cluster.local
- my.dns.search.suffix
设置主机名
通过hostname参数设置主机名,如果未设置默认使用 metadata.name
参数的值作为 Pod 的 hostname
设置hosts文件
通过hostAliases增加/etc/hosts下的内容
apiVersion: v1
kind: Pod
metadata:
name: hostaliases
spec:
hostAliases:
- ip: "10.0.0.1"
hostnames:
- "foo.local"
- "bar.local"
设置使用Capabilities
默认情况下,容器都是以非特权容器的方式运行。比如,不能在容器中创建虚拟网卡、配置虚拟网络。
Kubernetes 提供了修改 Capabilities的机制,可以按需要给容器增加或删除。
apiVersion: v1
kind: Pod
metadata:
name: cap-pod
spec:
containers:
- name: friendly-container
image: "alpine:3.4"
command: ["/bin/sleep", "3600"]
securityContext:
capabilities:
add:
- NET_ADMIN
drop:
- KILL
设置内核参数
通过securityContext.sysctls设置容器内核参数
apiVersion: v1
kind: Pod
metadata:
name: sysctl-example
spec:
securityContext:
sysctls:
- name: kernel.shm_rmid_forced
value: "0"
- name: net.ipv4.route.min_pmtu
value: "552"
设置使用主机的IPC命名空间
通过设置 hostIPC 参数为 true,使用主机的 IPC 命名空间,默认为 false
设置使用主机的PID命名空间
通过设置 hostPID 参数为 true,使用主机的 PID 命名空间,默认为 false
设置使用主机的Network命名空间
通过设置 hostNetwork参数为 true,使用主机的Network命名空间,默认为 false
网友评论