ReplicaSet
ReplicaSet的目的是维护一组在任何时候都处于运行状态的Pod副本的稳定集合。因此,它通常用来保证给定数量的、完全相同的Pod的可用性。
ReplicaSet的工作原理
ReplicaSet是通过一组字段来定义的,包括一个用来识别可获得的pod的集合的选择符,一个用来标明应该维护的副本个数的数值,一个用来执行应该创建新Pod以满足副本个数条件时要使用的Pod模板等等。每个ReplicaSet都通过根据需要创建和删除Pod以使得副本个数达到期望值,进而实现其存在价值。当ReplicaSet需要创建新的Pod时,会使用所提供的Pod模板。
ReplicaSet通过Pod上的metadata.ownerReferences字段连接到附属Pod,该字段给出当前对象的属主资源。ReplicaSet所获得的Pod都在其ownerReferences字段中包含了属主ReplicaSet的标识信息、正是通过这一连接,ReplicaSet知道它所维护的Pod集合的状态,并据此计划其操作行为。
虽然ReplicaSet可以独立使用,但今天它主要被Deployment用作协调Pod创建、删除和更新的机制。当时用Deployment时,不必担心还要管理它们创建的ReplicaSet。Deployment会拥有并管理它们的ReplicaSet。
示例: replicaSet.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
Deployment
一个Deployment控制器为Pods和ReplicaSets提供声明式的更新能力。
用户负责描述Deployment中的目标状态,Deployment控制器可以更改实际状态,使其变为期望的状态。用户可以定义Deployment以创建新的ReplicaSet,或删除现有的Deployment,并通过新的Deployment适配其资源。
Deployment典型使用场景:
- 创建Deployment以将ReplicaSet上线。ReplicaSet在后台创建Pods。检查ReplicaSet的上线状态,查看其是否成功;
- 通过更新Deployment的PodTemplateSpec,声明Pod的新状态,新的ReplicaSet会被创建,Deployment以受控速率将Pod从旧ReplicaSet迁移到新ReplicaSet。每个新的ReplicaSet都会更新Deployment的修订版本
- 如果Deployment的当前状态不稳定,回滚到较早版本的Deployment,每次回滚都会更新Deployment的修订版本
- 扩大Deployment规模以承担更多负载
- 暂定Deployment以应用对PodTemplateSpec所做的多项修改,然后恢复其执行以启动新的上线版本
- 使用Deployment状态来判定上线过程中是否出现停滞
- 清理交旧的不再需要的ReplicaSet
创建Deployment
示例:nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
[root@master k8s]# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/nginx-67d4bdd6f5-p48dc 1/1 Running 0 11s
pod/nginx-67d4bdd6f5-rlrdq 1/1 Running 0 11s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d21h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 2/2 2 2 11s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-67d4bdd6f5 2 2 2 11s
StatefulSet
StatefulSet是用来管理有状态应用的工作负载API对象。
StatefulSet用来管理Deployment和扩展一组Pod,并且能为这些Pod提供序号和唯一性保证。
和Deployment相同的是,StatefulSet管理了基于相同容器定义的一组Pod。但和Deployment不同的是,StatefulSet为它们每个Pod维护了一个固定的ID。这些Pod是基于相同的声明来创建的,但是不能相互替换:无论怎么调度,每个Pod都有一个不变的ID。
StatefulSet和其他控制器使用相同的工作模式。在StatefulSet对象中定义了期望的状态,然后StatefulSet的控制器就会通过各种更新来达到想要的状态。
StatefulSet可以满足以下一个或多个需求的应用程序:
- 稳定的、唯一的网络标识符
- 稳定的、持久的存储
- 有序的、优雅的部署和缩放
- 有序的、自动的滚动更新
稳定意味着Pod调度或重调度的整个过程是有持久性的。如果应用程序不需要任何稳定的标识符或有序的部署、删除或伸缩,则应该使用由一组无状态的副本控制器提供的工作负载来部署应用程序,比如Deployment或者ReplicaSet可能更适合无状态应用部署需要。
限制
- 给定的Pod存储必须由PersistentVolume驱动基于所请求的storage class来提供,或者由理员预先提供。
- 删除或者收缩StatefulSet并不会删除他关联的存储卷。这样是为了保证数据安全,通常比自动清除StatefulSet所有相关的资源更有价值
- StatefulSet当前需要headless服务来负责Pod的网络标识,用户需要负责创建此服务
注意:headless使用场景:有时候创建的服务不想走负载均衡,想直接通过pod-id后链接后端,可以使用headless service。headless Service是将service的发布文件中的clusterip设置成none,不让其获取clusterip,DNS解析时直接走Pod。
- 当删除StatefulSet时,StatefulSet不提供任务终止Pod的保证。为了实现StatefulSet中Pod有序的优雅的终止,可以再删除之前将StatefulSet缩放为0。
示例: stateful.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx # has to match .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 # by default is 1
template:
metadata:
labels:
app: nginx # has to match .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: my-storage-class
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
# Supported policies: Delete, Retain
reclaimPolicy: Delete
DaemonSet
DaemonSet确保全部(或者某些)节点上运行一个Pod的副本。当有节点加入集群时,也会为它们新增一个Pod,当有节点从集群中移除时,这些Pod也会被回收。删除DaemonSet将会删除它创建的所有Pod。
DaemonSet典型用法:
- 在集群的每个节点上运行存储Daemon,比如glusterd或ceph
- 在每个节点上运行日志收集Daemon,比如logstash
- 在每个节点上运行监控Daemon,比如Prometheus Node Exporter
DaemonSet是如何调度的
通过默认调度器调度
DaemonSet确保所有符合条件的节点都运行该Pod的一个副本。通常,运行Pod的节点有Kubernetes调度器选择,不过,DaemonSet Pods由DaemonSet调度器创建和调度。这就带来一下问题:
- Pod行为的不一致性:正常Pod在被创建后等待调度时处于Pending状态,DaemonSet Pod创建后不会处于Pending状态,这就会给用户带来困惑
- Pod抢占 由默认调度器处理,启动抢占后,DaemonSet控制器将在不考虑Pod优先级和抢占的情况下执行调度策略
ScheduleDaemonSetPods允许使用默认调度器而不是DaemonSet控制器来调度DaemonSet,方法是将NodeAffinity条件而不是.spec.NodeName条件添加到DaemonSet Pods。默认调度器接下来将Pod绑定到目标主机。如果DaemonSet Pod的节点亲和性配置已存在,则被替换。DaemonSet控制器仅在创建或修改DaemonSet pod时执行这些操作,不会更改DaemonSet的spec.template
此外,系统会自动添加 node.kubernetes.io/unschedulable: NoSchedule容忍度到DaemonSet Pod。在调度DaemonSet Pod时,默认调度器会忽略unSchedule节点
与DaemonSet 通信
与DaemonSet中的Pod进行通信有几种模式:
- NodeIP和端口:DaemonSet 中的Pod可以使用hostPort,从而可以通过节点IP访问到Pod。客户端能通过某种方法获取节点IP列表,并且基于此也可以获取到相应的端口。
- DNS:创建具有相同Pod选择器的headless服务通过使用endpoints资源或从DNS中检索到多个A记录来发现DaemonSet
- Service:创建具有相同Pod选择器的服务,并使用该服务随机访问到某个节点上的守护进程(没有办法访问到特定节点)
更新DaemonSet
如果节点的标签被修改,DaemonSet将立刻像新匹配上的节点添加Pod,同时删除不匹配的节点上的Pod。
可以修改DaemonSet创建的Pod。不过并非Pod的所有字段都可以更新。下次当某节点(即使具有相同的名称)被创建时,DaemonSet控制器还会使用最初的模板。
可以删除一个DaemonSet。如果使用kubectl并指定 --cascade=false选项,则Pod将被保留在节点上。接下来如果创建使用相同选择器的新DaemonSet,新的DaemonSet会收养已有的Pod。如果有Pod需要被替换,DaemonSet会根据其updateStrategy来替换。
DaemonSet的替代方案
init脚本
直接在节点上启动守护进程(例如使用init、upstartd、systemd)的做法当然是可行的,不过,基于DaemonSet来运行这些进程有如下好处:
- 像所有运行的其他应用一样,DaemonSet具备为守护进程提供监控和日志管理的能力
- 为守护进程和应用所使用的配置语言和工具是相同的
- 在资源受限的容器中运行守护进程能够增加守护进程和应用容器的隔离性。然而,这一点也可以通过在容器中运行守护进程但却不在Pod中运行来实现。例如,基于docker启动
和Deployment区别
DaemonSet与Deployment非常类似,他们都能创建Pod,并且Pod中的进程都不希望被终止(例如web服务器、存储服务器)。建议为无状态的服务使用Deployment,比如前端服务。对这些服务而言,对副本数量进行扩缩容、平滑升级,比精确控制Pod运行在某个主机上要重要的多。当需要Pod副本总是运行在全部或特定主机上,并需要他们优先于其他Pod启动时,应该使用DaemonSet。
Job
Job会创建一个或者多个Pod,并确保指定数量的Pod成功终止,随着Pod成功结束,Job跟踪记录成功完成的Pod个数。当数量达到指定的成功个数阈值时,job结束。删除Job的操作会清楚所创建的全部pod。
一个简单的使用场景下,会创建一个Job对象以便以一种可靠的方式运行某Pod直到完成。当第一个Pod失败或者被删除(比如某些节点硬件失效或重启)时,Job对象会启动一个新的Pod。
也可以使用Job以并行方式运行多个Pod。
示例: job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
ttlSecondsAfterFinished: 100
backoffLimit: 4
template:
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
[root@master k8s]# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/myjob-hx445 0/1 Completed 0 4s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d23h
NAME COMPLETIONS DURATION AGE
job.batch/myjob 1/1 4s 4s
[root@master k8s]# kubectl logs myjob-hx445
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068
[root@master k8s]#