赞
踩
Kubernetes中内建了很多controller (控制器),这些相当于一个状态机,用来控制Pod的具体状态和行为
RS、Deployment控制器用来控制pod的,还有一些其他控制器控制其他的资源
Pod 和控制器
你可以使用工作负载资源来创建和管理多个 Pod。 资源的控制器能够处理副本的管理、上线,并在 Pod 失效时提供自愈能力。 例如,如果一个节点失败,控制器注意到该节点上的 Pod 已经停止工作, 就可以创建替换性的 Pod。调度器会将替身 Pod 调度到一个健康的节点执行。
Pod 的分类
# 自主式的pod: 单独定义一个pod,这个没有副本控制器管理,也没有对应deployment
# self-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-pod
labels:
app: myapp
spec:
containers:
- name: myapp
image: hub.kaikeba.com/java12/myapp:v1 # pod内部运行的容器
注意事项/总结:
k8s资源对象(所有的k8s管理的资源,都叫做资源对象),都可以独立存在,但是需要根据相应原理,需求结合使用。
可以通过命令kubectl describe pod podName 查看创建过程详情
kubectl get pod -o wide #查看pod状态详情
自主式pod没有rs、deployment,一但删掉不会重新创建
ReplicationController (RC)用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收;
在新版本的Kubernetes中建议使用Replicaset来取代ReplicationController. ReplicaSet跟 ReplicationController没有本质的不同,只是名字不一样,并且ReplicaSet支持集合式的selector;
apiVersion: extensions/v1beta1 kind: ReplicaSet metadata: name: frontend spec: replicas: 3 #3个副本 selector: matchLabels: tier: frontend template: # 下面配置的是pod metadata: labels: tier: frontend spec: containers: - name: java-nginx #首字母不能大写 image: hub.kaikeba.com/java12/myapp:v1 env: - name: GET_HOSTS_FROM value: dns ports: - containerPort: 80
要支持滚动更新,还需要Deployment,这里主要说明K8S的资源对象都是可以单独部署的
修改其中一个pod的标签,这样该pod就脱离原来的RS管理了,而RS会再次创建一个pod,保持副本数量:
由此可见RS副本控制器是通过标签维护pod的
不受RS控制的pod就是自主式pod,删除后不再重建:
Deployment为Pod和ReplicaSet提供了一个声明式定义(declarative)方法,用来替代以前的 ReplicationController来方便的管理应用。典型的应用场景包括;
#1)、部署一简单的Nginx应用 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 #副本控制器 template: metadata: labels: app: nginx spec: containers: - name: nginx image: hub.kaikeba.com/java12/myapp:v1 ports: - containerPort: 80
创建资源,同时把之前创建的资源删掉,避免干扰
退出结束,只剩下nginx的:
根据cpu利用率自动扩容:
kubectl autoscale deployment [deployment Name] --min=[最小pod数量] --max=[最大pod数量] --cpu-percent=[根据cpu利用率]
设置了最小pod数为6,最大为15,需要等待一会儿才会变:
为什么会出现扩容效果,因为刚才的命令创建了hpa组件资源
通过hpa对象监控rs,实现pod自动扩容
演示滚动升级,为Deployment设置新版本镜像:
set image deployment [deploymentName] [containerName]=[新版本镜像路径]
我们知道pod的命名规则是 [deploymentName]-[ReplicaSetName]-[PodName]
图中可以看到升级后是新的RS,滚动更新的原理就是创建新的RS
回滚也是同理,回滚到之前的RS
演示回滚:
kubectl rollout undo deployment [deploymentName]
留意一下回滚后的RS名字,和上面的对比一下。
测试完成,清空环境:
Deployment更新策略
未来的Kuberentes版本中,将从1-1变成25%-25%
kubect1 describe deployments
#确保只运行一个副本,运行在集群中每一个节点上。(也可以部分节点上只运行一个且只有一个pod副本,如监控ssd硬盘) # kubectl explain ds # vim daemonset.yaml apiVersion: apps/v1 kind: DaemonSet metadata: #元数据信息,描述DaemonSet name: my-deamon namespace: default labels: app: daemonset spec: # RS副本控制器 selector: matchLabels: app: my-daemonset template: #描述pod metadata: labels: app: my-daemonset spec: containers: #描述容器 - name: daemon-app image: hub.kaikeba.com/java12/myapp:v1
看到DaemonSet会在每个node节点上会运行一个pod
删除其中一个pod也会自动重新创建一个:
Job
负责处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个Pod
成功结束。而CronJob
则就是在Job
上加上了时间调度。官方文档Jobs
# 我们用Job这个资源对象来创建一个任务,我们定一个Job来执行一个倒计时的任务,定义YAML文件: apiVersion: batch/v1 kind: Job metadata: name: job-demo spec: template: #描述pod metadata: name: job-demo spec: restartPolicy: Never #pod重启策略 containers: - name: counter image: busybox #指定镜像,busybox 是网络上的镜像 command: #容器启动后执行的命令 - "bin/sh" - "-c" - "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"
# 创建
kubectl apply -f job.yaml
# 查询日志
kubectl logs
注意Job
的RestartPolicy
(重启策略)仅支持Never
和OnFailure
两种,不支持Always
,我们知道Job
就相当于来执行一个批处理任务,执行完就结束了,如果支持Always
的话是不是就陷入了死循环了?
看到多了一个job的pod,状态是Completed,READY是0/1,说明已经执行结束了
PS:执行完成后是不会主动删除pod的,但是pod中的容器已经结束了
还有一个job任务处理控制器,查看该job的日志:
更多查看日志命令
kubectl logs --help
CronJob
其实就是在Job
的基础上加上了时间调度,我们可以:在给定的时间点运行一个任务,也可以周期性地在给定时间点运行。这个实际上和我们Linux
中的crontab
就非常类似了。官方文档:CronJob
一个CronJob
对象其实就对应crontab
文件中的一行,它根据配置的时间格式周期性地运行一个Job
,格式和crontab
也是一样的。
crontab
的格式如下:
分 时 日 月 星期 要运行的命令
第1列分钟 0~59
第2列小时 0~23)
第3列日 1~31
第4列月 1~12
第5列星期 0~7(0和7表示星期天)
第6列要运行的命令
# 现在,我们用CronJob来管理我们上面的Job任务 apiVersion: batch/v1beta1 kind: CronJob metadata: name: cronjob-demo spec: schedule: "*/1 * * * *" #调度规则 */1 表示每分钟调度一次 jobTemplate: #任务模板 spec: template: #描述pod spec: restartPolicy: OnFailure #pod重启策略 containers: #描述容器 - name: hello image: busybox args: - "bin/sh" - "-c" - "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"
# 创建cronjob
kubctl apply -f cron-job.yaml
# 查询cronjob
kubectl get cronjob
# 查询jon ,cronjon会循环多个job
kubectl get job
# 实时监控查询job
kubectl get job -w
我们这里的Kind
是CronJob
了,要注意的是spec.schedule
字段是必须填写的,用来指定任务运行的周期,格式就和crontab
一样,另外一个字段是spec.jobTemplate
, 用来指定需要运行的任务,格式当然和Job
是一致的。还有一些值得我们关注的字段spec.successfulJobsHistoryLimit
使用镜像仓库的镜像:
创建完cronjob后查看pod是没有的,因为策略是每分钟执行一次,还没到1分钟
等待1分钟后,查看job、pod:
到了指定时间后会创建pod,在pod内部进行调度
使用kubectl job -w(watche)进行监控,可以看到调用多次了
Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。Pods官方文档
Pod (就像在鲸鱼荚或者豌豆荚中)是一组(一个或多个) 容器; 这些容器共享存储、网络、以及怎样运行这些容器的声明。 Pod 中的内容总是并置(colocated)的并且一同调度,在共享的上下文中运行。 Pod 所建模的是特定于应用的“逻辑主机”,其中包含一个或多个应用容器, 这些容器是相对紧密的耦合在一起的。 在非云环境中,在相同的物理机或虚拟机上运行的应用类似于 在同一逻辑主机上运行的云应用。
除了应用容器,Pod 还可以包含在 Pod 启动期间运行的 Init 容器。 你也可以在集群中支持临时性容器 的情况外,为调试的目的注入临时性容器。
除了 Docker 之外,Kubernetes 支持 很多其他容器运行时, Docker 是最有名的运行时, 使用 Docker 的术语来描述 Pod 会很有帮助。
Pod 的共享上下文包括一组 Linux 名字空间、控制组(cgroup)和可能一些其他的隔离 方面,即用来隔离 Docker 容器的技术。 在 Pod 的上下文中,每个独立的应用可能会进一步实施隔离。
就 Docker 概念的术语而言,Pod 类似于共享名字空间和文件系统卷的一组 Docker 容器。
通常你不需要直接创建 Pod,甚至单实例 Pod。 相反,你会使用诸如 Deployment 或 Job 这类工作负载资源 来创建 Pod。如果 Pod 需要跟踪状态, 可以考虑 StatefulSet 资源。
这是因为 Pod 被设计成了相对临时性的、用后即抛的一次性实体。 当 Pod 由你或者间接地由 控制器 创建时,它被调度在集群中的节点上运行。 Pod 会保持在该节点上运行,直到 Pod 结束执行、Pod 对象被删除、Pod 因资源不足而被 驱逐 或者节点失效为止。
注意:重启 Pod 中的容器不应与重启 Pod 混淆。Pod 不是进程,而是容器运行的环境。Pod 天生地为其成员容器提供了两种共享资源:网络 和 存储。
在被删除之前,Pod 会一直存在。
Kubernetes 集群中的 Pod 主要有两种用法:
每个 Pod 都旨在运行给定应用程序的单个实例。如果希望横向扩展应用程序(例如,运行多个实例 以提供更多的资源),则应该使用多个 Pod,每个实例使用一个 Pod。 在 Kubernetes 中,这通常被称为 副本(Replication)。 通常使用一种工作负载资源及其控制器 来创建和管理一组 Pod 副本。
服务如何部署:
Pod 的 status
属性是一个 PodStatus 对象,拥有一个 phase
字段。它简单描述了 Pod 在其生命周期的阶段。
阶段 | 描述 |
---|---|
Pending(悬决) | Pod 已被 Kubernetes 接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。 |
Running(运行中) | Pod 已经绑定到了某个节点,Pod 中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态。 |
Succeeded(成功) | Pod 中的所有容器都已成功终止,并且不会再重启。 |
Failed(失败) | Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。 |
Unknown(未知) | 因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。 |
如果某节点死掉或者与集群中其他节点失联,Kubernetes 会实施一种策略,将失去的节点上运行的所有 Pod 的 phase 设置为 Failed。
Kubernetes 会跟踪 Pod 中每个容器的状态,就像它跟踪 Pod 总体上的阶段一样。 你可以使用容器生命周期回调 来在容器生命周期中的特定时间点触发事件。
一旦调度器将 Pod 分派给某个节点,kubelet 就通过 容器运行时 开始为 Pod 创建容器。 容器的状态有三种:Waiting(等待)、Running(运行中)和 Terminated(已终止)。
要检查 Pod 中容器的状态,可以使用如下命令,其输出中包含 Pod 中每个容器的状态:
kubectl describe pod <pod 名称>
Waiting (等待)
如果容器并不处在 Running 或 Terminated 状态之一,它就处在 Waiting 状态。 处于 Waiting 状态的容器仍在运行它完成启动所需要的操作:例如,从某个容器镜像 仓库拉取容器镜像,或者向容器应用 Secret 数据等等。 当你使用 kubectl 来查询包含 Waiting 状态的容器的 Pod 时,你也会看到一个 Reason 字段,其中给出了容器处于等待状态的原因。
Running(运行中)
Running 状态表明容器正在执行状态并且没有问题发生。 如果配置了 postStart 回调,那么该回调已经执行且已完成。
如果你使用 kubectl 来查询包含 Running 状态的容器的 Pod 时,你也会看到 关于容器进入 Running 状态的信息。
Terminated(已终止)
处于 Terminated 状态的容器已经开始执行并且或者正常结束或者因为某些原因失败。 如果你使用 kubectl 来查询包含 Terminated 状态的容器的 Pod 时,你会看到 容器进入此状态的原因、退出代码以及容器执行期间的起止时间。
如果容器配置了 preStop 回调,则该回调会在容器进入 Terminated 状态之前执行。
Pod 的 spec
中包含一个 restartPolicy
字段,其可能取值包括 Always、OnFailure 和 Never。默认值是 Always。
restartPolicy 适用于 Pod 中的所有容器。restartPolicy 仅针对同一节点上 kubelet 的容器重启动作。当 Pod 中的容器退出时,kubelet 会按指数回退 方式计算重启的延迟(10s、20s、40s、…),其最长延迟为 5 分钟。 一旦某容器执行了 10 分钟并且没有出现问题,kubelet 对该容器的重启回退计时器执行 重置操作。
pod生命周期示意图(初始化容器,post start,main container…,pre stop):
主容器,就是我们指定的镜像构建的容器
说明:
liveness probe(存活探测)
和readiness probe(就绪探测)
配置启动后钩子(post start)和终止前钩子(pre stop)
可以使用以下命令查看post start和pre stop的设置格式:
每个 Pod 中可以包含多个容器, 应用运行在这些容器里面,同时 Pod 也可以有一个或多个先于应用容器启动的 Init 容器。
Init 容器与普通的容器非常像,除了如下两点:
如果 Pod 的 Init 容器失败,kubelet 会不断地重启该 Init 容器直到该容器成功为止。 然而,如果 Pod 对应的 restartPolicy 值为 “Never”,Kubernetes 不会重新启动 Pod。
图画错了,就绪探针不会重启
为 Pod 设置 Init 容器需要在 Pod 的 spec
中添加 initContainers
字段, 该字段以 Container 类型对象数组的形式组织,和应用的 containers
数组同级相邻。 Init 容器的状态在 status.initContainerStatuse 字段中以容器状态数组的格式返回 (类似 status.containerStatuses 字段)
因为 Init 容器具有与应用容器分离的单独镜像,其启动相关代码具有如下优势:
示例,下面是一些如何使用 Init 容器的想法:
for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; exit 1
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register \
-d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
(第一次创建以后后期修改初始化容器,spec的相关配置都不起作用,只能改image字段)
(Init容器只有完成状态,所以不需要、也没有就绪检测、存活检测)
#init-pod.yaml apiVersion: v1 kind: Pod metadata: name: init-pod labels: app: myapp spec: containers: - name: myapp image: hub.kaikeba.com/java12/busybox:v1 command: ['sh', '-c', 'echo -n "running at " && date +%T && sleep 600'] initContainers: # 这里指定的就是初始化容器!!! - name: init-mydb image: hub.kaikeba.com/java12/busybox:v1 command: ['sh', '-c', 'until nslookup init-db; do echo waiting for init-db;date +%T; sleep 2;echo; done;'] # 这里执行的指令 nslookup 是查找init-db服务,until 是循环直到找到
清空cronjob的环境:
新建一个目录lifecicle:
初始化容器会先于主容器运行,如果初始化容器运行有问题主容器也不会运行成功:
可以看到状态一直是init状态,查看详情:
kubectl describe pod [podName]
看不出什么,看下容器里面日志:
kubectl logs [pod名字] -c [pod中指定容器名字]
#init-svc.yaml
kind: Service
apiVersion: v1
metadata:
name: init-db
spec:
ports:
- protocol: TCP
port: 80
targetPort: 3366
现在我们创建init-db服务:
等待一会儿后
Probe 是由 kubelet 对容器执行的定期诊断。 要执行诊断,kubelet 调用由容器实现的 Handler (处理程序)。有三种类型的处理程序:
每次探测都将获得以下三种结果之一:
针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:
livenessProbe
:指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success。
何时该使用存活态探针?
如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活态探针; kubelet 将根据Pod
的restartPolicy 自动执行修复操作。
如果你希望容器在探测失败时被杀死并重新启动,那么请指定一个存活态探针, 并指定restartPolicy 为 “Always” 或 “OnFailure”。
注意Pod和容器的关系
readinessProbe
:指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success。
何时该使用就绪态探针?
如果要仅在探测成功时才开始向 Pod 发送请求流量,请指定就绪态探针。 在这种情况下,就绪态探针可能与存活态探针相同,但是规约中的就绪态探针的存在意味着 Pod 将在启动阶段不接收任何数据,并且只有在探针探测成功后才开始接收数据。
如果你的容器需要加载大规模的数据、配置文件或者在启动期间执行迁移操作,可以添加一个 就绪态探针。
如果你希望容器能够自行进入维护状态,也可以指定一个就绪态探针,检查某个特定于 就绪态的因此不同于存活态探测的端点。
说明: 请注意,如果你只是想在 Pod 被删除时能够排空请求,则不一定需要使用就绪态探针; 在删除 Pod 时,Pod 会自动将自身置于未就绪状态,无论就绪态探针是否存在。 等待 Pod 中的容器停止期间,Pod 会一直处于未就绪状态。
startupProbe
: 指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其 重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success。
何时该使用启动探针?
对于所包含的容器需要较长时间才能启动就绪的 Pod 而言,启动探针是有用的。 你不再需要配置一个较长的存活态探测时间间隔,只需要设置另一个独立的配置选定, 对启动期间的容器执行探测,从而允许使用远远超出存活态时间间隔所允许的时长。
如果你的容器启动时间通常超出 initialDelaySeconds + failureThreshold × periodSeconds 总值,你应该设置一个启动探测,对存活态探针所使用的同一端点执行检查。 periodSeconds 的默认值是 30 秒。你应该将其 failureThreshold 设置得足够高, 以便容器有充足的时间完成启动,并且避免更改存活态探针所使用的默认值。 这一设置有助于减少死锁状况的发生。
#readinessProbe-httpget apiVersion: v1 kind: Pod metadata: name: readiness-httpget-pod namespace: default spec: containers: - name: readiness-httpget-container image: hub.kaikeba.com/java12/myapp:v1 #myapp是一个nginx镜像 imagePullPolicy: IfNotPresent readinessProbe: # 关键在这,就绪检测 httpGet: #使用http 协议get请求 port: 80 #访问端口 path: /index1.html #访问路径,如果是微服务架构就可以写 健康检查的路径/health/xx initialDelaySeconds: 1 #第一次探测前应该等待的时间 periodSeconds: 3 #每隔3秒检测一次
清除之前的测试环境:
创建
可以看到ready是0/1,只有一个容器,但是没有跑起来,这个就是僵尸程序
僵尸程序:pod状态是running,但是ready中显示0/1,总共一个容器,但是没有一个容器是ready状态,即没有跑起来,这样的情况下pod是不能对外服务的,所以就认为这个pod内部程序是僵尸程序
当然也要分情况,有的时候需求就是需要暂时不能对外服务。
kubectl describe pod [podName]
查看状态:
看到就绪检测得到的响应是404
登录当前pod的容器(里面只有一个容器,不用指定容器名):
在容器中的nginx目录下创建index1.html页面,再次查看pod状态
livenessProbe-exec方式
apiVersion: v1 kind: Pod metadata: name: liveness-exec-pod namespace: default spec: containers: - name: liveness-exec-container image: hub.kaikeba.cn/java12/myapp:v1 imagePullPolicy: IfNotPresent # 镜像拉取策略,本地有从本地拉取,没有再从镜像仓库拉取 #容器内部运行指令,创建/tmp/live文件、睡眠60毫秒、删除/tmp/live文件、睡眠3600毫秒 command: ["/bin/sh","-c","touch /tmp/live;sleep 60;rm -rf /tmp/live;sleep 3600"] livenessProbe: #存活检测! exec: # exec方式 command: ["test","-e","/tmp/live"] # 检测文件存不存在 initialDelaySeconds: 1 #1秒后再检测 periodSeconds: 3 #每3秒检测一次
删除之前的测试环境,创建新的,对pod进行监控
等待一定时间后看到pod重启了:
因为检测的时候发现文件不见了,意味着pod出问题了就会将pod重启
livenessProbe-Httpget方式
apiVersion: v1 kind: Pod metadata: name: liveness-httpget-pod namespace: default spec: containers: - name: liveness-httpget-container image: hub.kaikeba.com/java12/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 livenessProbe: #存活检测! httpGet: #http get方式 port: http path: /index.html # nginx中index.html默认是存在的 initialDelaySeconds: 1 periodSeconds: 3 timeoutSeconds: 10 # 检测超时时间
此时没问题,因为检测的时候,这个文件是在的
进入pod中的容器删除该文件:
只会重启一次,因为重启是创建新的pod,文件又存在了
livenessProbe-Tcp方式
apiVersion: v1
kind: Pod
metadata:
name: probe-tcp
spec:
containers:
- name: nginx
image: hub.kaikeba.com/java12/myapp:v1
livenessProbe: #存活检测!
initialDelaySeconds: 5 # 5秒后在开始检测
timeoutSeconds: 1 # 检测超时时间
tcpSocket:
port: 80 #通过tcp直接访问80端口检测
periodSeconds: 3 # 每3秒检测一次
如果关闭了80端口就会重启
apiVersion: v1 kind: Pod metadata: name: liveness-httpget-pod namespace: default spec: containers: - name: liveness-httpget-container image: hub.kaikeba.com/java12/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 readinessProbe: # 就绪检测 httpGet: port: 80 path: /index1.html # 就绪检测还是访问index1.html initialDelaySeconds: 1 periodSeconds: 3 livenessProbe: # 存活检测 httpGet: port: http path: /index.html # 存活检测也用http方式检测index.html initialDelaySeconds: 1 periodSeconds: 3 timeoutSeconds: 10
清空刚才的测试环境:
看到pod是running,但是里面的容器没有ready,因为就绪检测失败了:
kubectl describe pod [podName]
查看状态:
登录容器
看到就绪检测成功
再次登录容器直接执行删除index.html,校验存活检测:
等个几秒中后看到重启了,新的pod里面的文件又恢复了
容器生命周期回调 官方文档
有点类似于Spring Bean生命周期的init和destory函数
apiVersion: v1 kind: Pod metadata: name: lifecycle-startstop spec: containers: - name: lifecycle-container image: nginx lifecycle: #生命周期 postStart: #start exec: # 容器启动的时候自动执行start钩子函数,打印一段话输出到/usr/share/message command: ["/bin/sh","-c","echo Hello from the postStart handler > /usr/share/message"] preStop: #stop,退出的时候自动执行,退出看不了,因为pod没了 exec: command: ["/bin/sh","-c","echo Hello container stop"]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。