云原生现在是炙手可热的技术,而这里面最核心的当属大名鼎鼎的Kubernetes;K8S号称是未来时代的操作系统,是云原生时代IT工程师必备的技能。
学习应当有输出,下面是对pod相关知识的学习
pod 的一些基础概念:
pod是K8S中最小的调度单位,那么为啥不是docker容器呢?答案可能是从最开始K8S就没有将容器作为架构的核心,正如K8S的前身--Google的Borg系统研究人员提出的:
运行在大规模集群中的各种任务之间,实际上存在各种各样的关系。这些关系的处理,才是作业编排和管理系统最困难的地方
pod扮演的是传统部署环境中“虚拟机”的角色,而容器则是这个“虚拟机”中运行的用户程序。
pod只是一个逻辑概念,K8S真正处理的是宿主机OS上容器的namespace和cgroups,并不存在所谓的pod边界或者隔离,pod内部是有一个infra容器存在的,它是pod内的第一个容器。
infra容器是一个用汇编语言编写的,永远处于“暂停”状态的容器,大小只有几百KB,举例:
pod的生命周期,只跟其内部的infra容器有关系,而与其他容器无关;infra容器永远是第一个被创建的容器
pod是一组共享了某些资源的容器,这些容器可共享的资源有: 网络namespace、pv卷、security context
用户定义的容器,则可通过join network namespace的方式与infra容器关联一起;对同一个pod里的所有用户来讲,进出的流量都可认为是通过infra容器完成的。
对于volume,K8S只需要把volume的定义设计在pod层级即可,一个volume对应的宿主机目录对于pod来说就只有一个,pod里的容器只要声明挂载这个volume,就可以共享这个volume对应的宿主机目录。
pod内部容器之间的超亲密关系,形成了sidecar模式
,即容器设计模式
,常见的场景包括日志收集、通信适配等,而这也是service mesh项目如istio微服务治理的原理。
pod相关的属性:
pod级别的属性应该是如下所描述的属性:
- 和调度、网络、存储、安全相关的属性
- 和容器linux namespace相关的属性
- 容器要共享宿主机的namespace,也一定是pod级别的定义
pod的四大类属性(应该是API对象的四大类属性),及每个大类里常见的子属性:
- typemeta: 最基本的定义
- Group
- Kind
- Version
- metadata:内有2个最重要的属性,即namespace和name,它俩可唯一确定某个对象实例;其余常见的字段还有
- Label
- Annotation
- Finalizer
- ResourceVersion
- spec : 核心属性,每个对象独有,用户的期望状态,由创建对象的用户端来定义
- status : 核心属性,每个对象独有,对象的实际状态,由对应的控制器收集状态并更新
pod的status状态:
pending :api对象已创建在etcd中,pod里的有些容器因为某种原因无法顺利创建
running :至少有一个容器已经在运行了
succeeded :pod里的所有容器都跑完且退出,在运行job任务时常见
failed :至少有一个容器是不正常的状态
unknown :异常状态,pod状态无法被kubelet汇报给kube-apiserver,比如从节点挂了
CrashLoopBackOff :比如拉镜像失败
对于pod的状态并不需要死记硬背,记住下面两个原理即可:
只要pod的restartPolicy指定的策略允许重启异常容器(eg:always),那么这个pod就会保持running状态,并进行容器重启; 否则就会进入failed状态
对于包含多个容器的pod,只有它里面所有容器都进入异常状态,pod才会进入failed状态;在此之前pod都是running状态,且此时ready字段会显示正常容器个数
一些命令与操作:
首先可以设置kubectl命令别名,因为它实在有点长:
alias k=kubectl
通过explain可以获取字段的详细解释,可以快速了解对象字段的意义:
k explain xxx
root@ubuntu:/home/kubeyaml# k explain pods
KIND: Pod
VERSION: v1
DESCRIPTION:
Pod is a collection of containers that can run on a host. This resource is
created by clients and scheduled onto hosts.
FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
...
并且可以指定下层属性:
root@ubuntu:/home/kubeyaml# k explain pods.apiVersion
KIND: Pod
VERSION: v1
FIELD: apiVersion <string>
DESCRIPTION:
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
- 获取已创建pod的完整信息,可以选择是yaml或json格式:
k get pod name -o yaml/json
- label相关
label可以附加到任何K8S对象上,不仅仅是pod
修改现有pod的label:
k label po nginx app=label1
如果修改的是已经存在的label,要加上--overwirte参数:
k label po nginx app=label1 --overwrite
删除label:
k label po nginx app-
(后边带一个 - 号)
获取pod有哪些label:
k get pod xx --show-labels
只对某些label感兴趣,可以指定-L参数指定它们,如果label不存在,这一列就会为空:
k get pod -L app,bpp
root@ubuntu:/home/kubeyaml# k get po curl-custom-ambassador -L app,bpp,cpp
NAME READY STATUS RESTARTS AGE APP BPP CPP
curl-custom-ambassador 2/2 Running 29 62d label1 label2
使用label选择器列出想要的 pod,超多写法:
k get po -l xx=yy
(找出带有xx标签,且值等于yy的pod)
k get po -l xx!=yy
(选择带有xx标签,且值不等于yy的pod)
k get po -l xx
(选择带有xx标签的pod)
k get po -l '!xx'
(注意单引号)
k get po -l env in (prod,dev)
k get po -l env not in (prod,dev)
k get po -l env=prod,app=label1
(多个标签的情况,全部匹配才算成功)
- 注解
注解也是键值对的形式,与label不同的是,注解并不是为了保存标识信息而存在,它们不能像标签一样用于对象分组;注解的目的主要是为了添加说明,比如指定创建对象的人员姓名,可以让集群中的工作人员之间协作更便利。
添加注解:
k annotate pod xx mycompany.com/somenot="foo bar"
同样的,如果想覆盖之前的注解,可添加--overwrite
参数
查看刚刚添加的注解:
k describe po curl-custom-ambassador | grep Annotation
root@ubuntu:~# k describe po curl-custom-ambassador | grep Annotation
Annotations: mycomp.com/somenot: foo bar
- namespace
创建一个namespace,可以不用yaml文件,直接用命令:
k create namespace xxxx
不同namespace中的pod对象是可以重名的。
大多数对象的名称必须符合RFC1035域名规定的命名规范,即只能包含字母、数字、横杠-,点号
,但namespace中是不能有点号的。
如何快速切换到不同的namespace:
alias kcd='kubectl config set-context $(kubectl config current-context) --namespace'
然后,就可以使用 kcd some-namespace
在namespace之间快速切换
举例,先切换到my这个namespace下,查看pod,发现是空的
root@ubuntu:~# kcd my
Context "minikube" modified.
root@ubuntu:~# k get po
No resources found in my namespace.
然后切到default namespace下,pod又出现了
root@ubuntu:~# kcd default
Context "minikube" modified.
root@ubuntu:~# k get po
NAME READY STATUS RESTARTS AGE
curl-custom-ambassador 2/2 Running 31 62d
grafana-69855b9d58-5g86k 1/1 Running 11 56d
prometheus-alertmanager-998d6d5f8-kkhz7 2/2 Running 22 56d
可以通过-A参数来快速查询所有namespace下的资源,例如:
k get po -A
- 删除操作
通过标签选择器删除:
k delete po -l xx=yy
删除namespace时,其下的所有pod也会被删除:
k delete ns xx
也可以只删除namespace下的所有pod,保留namespace:
k delete po --all
当然可以更暴力一些:
k delete all --all
此时该namespace下绝大部分的资源类型的实例,都将会删除,比如pod,rc,rs等;也有一些资源此时不会删除,比如secret
- 保持pod的健康---存活探针
存活探针 liveness probe (还有就绪探针readiness probe, 他们俩的使用场景完全不一样)
1. 当pod调度到某个node,该node上的kubelet就会运行该pod;如果容器的主进程崩溃,kubelet就会重启容器。
2. 即使进程没有崩溃,比如JVM发生了OOM,进程依然可以运行,但此时希望能有办法向K8S发送信号。
3. 当应用死循环或者死锁,就会停止响应,为了确保程序此时可以重启,应当从外部检查应用的情况,而不是依赖程序内部的检测
存活探针的3种机制:
- HTTP GET,通过get请求的响应码来判断是否要重启容器
- tcp套接字,与容器指定端口建立tcp连接,如果建立不成功则重启容器
- exec, 在容器内执行任意命令,并检查命令的退出码
存活探针中重要的附加属性:
- timeoutSeconds: 容器必须在这个时间值内做出响应,否则就被认为失败,默认为1s
- periodSeconds: 多长时间探测一次,默认10s
- failureThreshold: 连续探测 x 次失败之后,重启容器,默认为1
- successThreshold: 连续探测 x 次成功之后才算成功,默认为1
- initialDelaySeconds: 初始延迟,务必设置,如果不设置,探针会在容器启动时立即开始探测,默认为0
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 15
在生产中的pod,一定要定义存活探针;如果没有定义,k8s是不会知道应用是否还活着的。
一些推荐的做法:
- 确保探针的请求端点不需要认证,比如/health,否则探测会一直失败
- 要检查程序内部的状态(从内部对运行的所有重要组件执行状态检查),而不受到外部因素的影响。例如,当web服务器无法连接到后端数据库时,web服务器的存活探针不应该返回失败;因为如果问题是出在数据库,重启web服务器不会解决问题的。
- 保持探针轻量,因为探测器执行频率相对较高,探针的CPU时间会计入容器的CPU时间配额,过重的探针会影响容器的运行
- 不要在探针中实现重试循环,k8s本身会进行若干次的尝试
参考内容:
极客时间《深入剖析kubernetes》
《Kubernetes in action中文版》