k8s中的Pod

一、为什么需要Pod

Pod是Kubernetes的基本操作单元,把相关的一个或多个容器构成一个Pod。
Pod包含的容器运行在同一个Host上,看作一个统一管理单元。它们共享相同的volumes和network namespace空间,通常会发生直接的文件交换、RPC调用等。
每个Pod有一个infra网络容器,需要在kubelet中通过--pod-infra-container-image参数指定。一般使用的是汇编语言写的特殊镜像k8s.gcr.io/pause,它永远处于暂停状态。网络参数(ip/port)只定义在网络容器上,其它容器借助--net=container:id的方式与它共享同一network namespace空间,使用它间接定义自身网络。因此,容器可以通过localhost或者socket通信访问同一个Pod内其它容器。
整个Pod的生命周期都和网络容器一致,与其它容器无关。
在Kubernetes里面,Pod实际上是抽象出来的一个可以类比为进程组的概念。
如图,由四个进程共同组成的一个应用Helloworld,在Kubernetes里面,实际上会被定义为一个拥有四个容器的Pod
这些容器间存在依赖关系时,就会存在Task co-scheduling(成组调度失败)问题。
在Mesos里面,会使用成组调度的解法进行资源囤积(resource hoarding):当所有设置了Affinity约束的任务都达到时,才开始统一调度。
例如有写日志的App容器和转发日志到ES的LogCollector容器。在Mesos里面不会立刻调度,而是等两个容器都提交完成,才开始统一调度。这样调度效率会损失,还可能产生死锁。
Google在Omega系统里则不管这些冲突的异常情况先乐观调度,发生冲突后,通过复杂的回滚机制来解决问题。
Kubernetes的处理方式是,调度时直接将App容器和LogCollector容器以一个Pod为单位进行调度。
 

二、使用kubectl或kubelet管理运维Pod

kubelet负责维护和管理Node上的所有容器,但是如果容器不是通过kubernetes创建的,它并不会管理。本质上,它负责使Pod的运行状态与期望的状态一致。
Pod相关命令:
  • 运行Pod
kubectl run kubernetes-bootcamp --image=xxx --port=8080 --env=“key=value"
其中--port表示暴露端口,--env可以出现多次,设置多个环境变量
最后可以加上--dry-run表示打印相关API对象而不创建它们
  • 查看Pod日志
kubectl -n {$nameSpace} logs -c {$containername} --tail=1000 {$podName} | less
  • 进入Pod
kubectl exec -it {$podName} bash
kubectl exec -it {$podName} -c {$containername} bash
 
kubelet(即使没有其它k8s组件)也可以直接创建静态Pod
kubelet会尝试通过apiserver为每个静态Pod自动创建一个镜像Pod,因此kubectl get可以查询但无法管理静态Pod
kubelet设置 staticPodPath后 ,kubelet会定期的扫描这个文件夹下的YAML/JSON文件(忽略.开头的)来创建/删除静态Pod;也可以通过--manifest-url=<URL>参数的配置定期从网络上下载指定文件
文件改变时,kubelet会字动进行Pod的更新
 

三、PodSpec数据结构

  • Containers
Pod中通常会采用sidecar设计模式,将应用程序的功能划分为单独的Container。由于Pod里面多个容器同属于一个network namespace,通信没有性能损耗;通过共享挂载卷,IO也没有损耗
sidecar设计模式的好处:辅助功能从业务容器解耦,sidecar容器可以独立发布;能力可重用,同样的一个监控或日志sidecar容器,可以被所有应用共用
使用场景:
(1)应用与日志收集
业务容器将日志写在一个Volume里面,同一Pod内的日志容器可以通过共享该Volume,直接把日志文件读出来,然后存到远程存储或进行转发。
现在业界常用的Fluentd日志进程或日志组件,一般即为此工作模式。
(2)把监控组件装到sidecar容器里查看其他容器的工作状态
(3)代理容器
 
Pod需要访问一个外部系统,或者一些外部服务,但是这些外部系统是一个集群,此时需要解耦,即通过Sidecar代理容器,单独写一个Proxy容器用来处理对接外部的服务集群,Pod内其它业务容器主要访问Proxy,就可以通过一个统一的、简单的方式把服务集群都访问到。
(4)适配器容器Adapter
业务容器暴露出来的API接口是A,但是外部系统访问业务容器时只知道API接口B 。所以要额外写一个Adapter容器,对外暴露的是接口B,它会把把所有对B的请求转发给A。
  • InitContainer
InitContainer会比普通container先启动,按定义的次序去启动执行的,执行成功后就结束退出。并且直到所有的InitContainer执行成功后,普通container才会被启动。
一般由InitContainer为普通container做启动前的初始化工作,如准备配置文件、做一些前置条件的校验(如网络是否联通)等。
例如,flannel组件的的InitContainer主要是为kube-flannel这个普通容器启动之前准备一些网络配置文件:
      initContainers:
      - name: install-cni
        image: quay.io/coreos/flannel:v0.11.0-amd64
        command:
        - cp
        args:
        - -f
        - /etc/kube-flannel/cni-conf.json
        - /etc/cni/net.d/10-flannel.conflist
        volumeMounts:
        - name: cni
          mountPath: /etc/cni/net.d
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      containers:
      - name: kube-flannel
        image: quay.io/coreos/flannel:v0.11.0-amd64
        command:
        - /opt/bin/flanneld
        args:
        - --ip-masq
        - --kube-subnet-mgr
  • EphemeralContainer
缺少对资源或执行的保证,没有端口配置(因此无法配置ports、livenessProbe、readinessProbe等字段)并且永远不会自动重启
无法直接在Pod的spec字段中创建、修改
需要编辑EphemeralContainers类型的资源对象描述文件:
{
    "apiVersion": "v1",
    "kind": "EphemeralContainers",
    "metadata": {
            "name": "example-pod"
    },
    "ephemeralContainers": [{
        "command": [
            "sh"
        ],
        "image": "busybox",
        "imagePullPolicy": "IfNotPresent",
        "name": "debugger",
        "stdin": true,
        "tty": true,
        "terminationMessagePolicy": "File"
    }]
}

通过API为Pod添加ephemeralcontainers:

kubectl replace --raw /api/v1/namespaces/default/pods/example-pod/ephemeralcontainers  -f ec.json
exec或attach到此容器后,进行Debug
如果Pod启用了进程命名空间共享,则可以查看该Pod所有容器中的进程
  • Volumes
  • restartPolicy
Always(默认)、OnFailure、Never
DaemonSet必须将其设置为Always,以保证该容器持续运行;Job需要将其设置为OnFailure或Never,确保容器执行完成后不再重启。
kubelet重启失效容器的时间间隔以sync-frequency乘以2n来计算,例如1s、2s、4s、8s倍等,最长延时5min,并且在成功重启后的10min后重置该时间。
  • terminationGracePeriodSeconds和ActiveDeadlineSeconds
terminationGracePeriodSeconds字段定义了Pod优雅下线的grace period,默认是30秒。
执行kubectl delete的时候也可以通过--grace-period参数显示指定一个优雅退出时间来覆盖Pod中的配置
ActiveDeadlineSeconds则定义了容器连续失败多久会被停止,避免容器一直启动失败
  • dnsPolicy和dnsConfig
dnsPolicy:该Pod的DNS策略
    Default:继承所在宿主机的设置,直接将宿主机的/etc/resolv.conf挂载到容器中
    ClusterFirst(默认):所有请求会优先在集群所在域查询,如果没有才会转发到上游DNS
    ClusterFirstWithHostNet:Pod运行在hostNetwork:true的情况下强制指定为此策略,效果和ClusterFirst一样
    None:忽略所有配置,以Pod的dnsConfig字段为准
dnsConfig字段包括下面几个属性:
    nameservers: DNS Server的列表,最多配置3个
    searches:search域名列表,也就是/etc/resolv.conf中的search字段的配置,最多配置6个
    options:选项列表,也就是/etc/resolv.conf中的option字段的配置
  • schedulerName
指定使用的调度器名
  • NodeSelector、 NodeName、 TopologySpreadConstraints、 Affinity、 Tolerations
调度时选择节点相关
  • PriorityClassName、 Priority、PreemptionPolicy
抢占相关
  • ServiceAccountName
指定使用的serviceAccount名
  • AutomountServiceAccountToken
是否自动挂载ServiceAccount Token
  • RuntimeClassName和Overhead
  • EnableServiceLinks
Service的信息是否需要注入Pod的环境变量。默认为true。
  • ReadinessGates
根据Pod的status.condition字段的当前状态决定Pod是否ready,必须Pod中的所有容器均已准备就绪,且ReadinessGates中指定的所有条件(若condition中缺失此字段则默认为false)均为true,Pod才能ready
它给了controller-manager之外组件(如kube-proxy、自定义控制器)控制Pod就绪的能力
例如配置:
  readinessGates:
    - conditionType: "www.example.com/feature-1"
假如状态是:
status:
  conditions:
    - type: Ready                              # a built in PodCondition
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
    - type: "www.example.com/feature-1"        # an extra PodCondition
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
由于自定义条件为false,Pod状态将为ContainersReady
  • imagePullSecrets
指定拉取镜像使用的secret
  • Hostname和Subdomain
指定subdomain后,Pod的完整访问路径将变为<hostname>.<subdomain>.<pod namespace>.svc.<cluster domain>
  • SetHostnameAsFQDN
Pod的hostname是否需要设置为其的FQDN
  • securityContext
Pod级别的SecurityContext
hostNetwork和hostPID:是否使用主机的network namespace、pid namespace,建议设置为false
shareProcessNamespace:为true时,Pod中所有容器间共享进程空间
  • HostAliases
非hostnetwork模式下,会将其指定的主机别名添加到Pod的/etc/hosts
 

四、ContainerSpac数据结构

  • Name
  • Image
  • ImagePullPolicy
Always、Never、IfNotPresent
  • Command
会覆盖镜像的ENTRYPOINT
  • Args
执行command带的参数
  • WorkingDir
  • Ports
使用主机端口时使用
  • EnvFrom
有configMapRef和secretRef两种,会将所有的kv转换成环境变量
  • Env
环境变量,除了通过value直接指定值,还可以通过valueFrom
①通过fieldRef指定ObjectFieldSelector
    - name: AI_HOST_IP
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: status.hostIP
②通过resourceFieldRef指定ResourceFieldSelector
    - name: AI_MEM_REQUEST
      valueFrom:
        resourceFieldRef:
          divisor: "0"
          resource: requests.memory
③通过configMapKeyRef指定ConfigMapKeySelector
④通过secretKeyRef指定SecretKeySelector    
  • Resource
对容器的资源限制
  • lifecycle
创建资源对象时,可以使用lifecycle来管理容器在运行前和关闭前的一些动作。
lifecycle有两种回调函数:
    PostStart:容器创建成功后,运行前的任务,用于资源部署、环境准备等。
    PreStop:在容器被终止前(发送终止信号前)的任务,用于优雅关闭应用程序、通知其他系统等等。
在Pod Hook钩子函数中有Exec和HTTP两种方式
创建容器后,Kubernetes立即发送postStart事件。不过,不能保证在调用Container的Entrypoint前先调用postStart处理程序(因为postStart处理程序相对于Container的代码异步运行)。但是Kubernetes对容器的管理会阻塞,直到postStart处理程序完成,才会把容器的状态设置为running。
停止Pod时,Pod设置为Terminating状态,并从所有Service的Endpoints列表中删除。此时,Pod停止,但是Pod中运行的容器不受影响。针对有preStop hook的容器,kubelet会调用每个容器的preStop hook,假如preStop hook运行时间超出grace period,kubelet会发送SIGTERM并再等2秒;针对没有 preStop hook的容器,kubelet直接发送SIGTERM,grace period超出之后,kubelet发送SIGKILL干掉尚未退出的容器。
  • VolumeMounts
  • VolumeDevices
指定使用的Block设备
  • TerminationMessagePath和TerminationMessagePolicy
terminationMessagePolicy默认为File,此时Kubernetes使用terminationMessagePath指定的文件(默认为/dev/termination-log)中的内容来填充容器在成功和失败时的状态消息;terminationMessagePolicy还可以设置为FallbackToLogsOnError,则将消息输出到容器日志中
  • SecurityContext
容器级别的SecurityContext
  • Stdin、StdinOnce、TTY
  • LivenessProbe、ReadinessProbe和StartupProbe
kubelet里有一个叫ProbeManager的组件,包含了各种probe。
Readiness probe(就绪探针)用来判断一个Pod是否处在就绪状态,主要应对的是启动之后无法立即对外提供服务的这些应用;
当Pod不处在就绪状态的时候,接入层会把相应的流量从这个Pod上摘除。直到下一次判断成功(Pod的状态从fail转换成success),这个Pod才会再次挂到相应的endpoint上。
此时Pod为不能进入READY状态,但实际已经running,容器内程序已经开始运行。
Liveness probe(存活探针)用来判断一个Pod是否处在存活状态,适用于支持那些可以重新拉起的应用
当一个Pod处在不存活状态的时候,会通过kubelet杀掉相应的Pod,再由上层的判断机制根据重启策略判断是否需要重新拉起。如果上层配置的重启策略是restart always,Pod会被判断为CrashLoopBackOff然后直接被重新拉起。
 
Startup probe(启动探针)是1.17新增的,用于判断应用程序是否启动
只有认为Pod启动完成后,另两个探针才会启用,它也就被Liveness probe代替(Liveness probe开始计时);如果认为Pod启动不成功,会将其杀死重启
 
指针支持三种不同的探测方式:
(1)httpGet
通过发送 http Get 请求来进行判断的,当返回码是200-399之间的状态码时,标识这个应用是健康的
livenessProbe字段里有一个httpGet,httpGet里面有字段path、port、httpHeaders(通常不配,需要通过类似像header头的一个机制做health的一个判断时才配置)。
(2)Exec
通过执行容器中的一个命令来判断当前的服务是否是正常的,当命令行的返回结果是0,则标识容器是健康的
livenessProbe字段里有一个exec。接下来又配置了一个 command 的字段。
该例子的command字段里,通过cat一个具体的文件来判断当前状态,当cat命令返回0时会认为pod处在健康状态。
(3)tcpSocket
通过探测容器的IP和Port进行TCP健康检查,如果这个TCP链接能够正常被建立,那么标识当前这个pod是健康的。livenessProbe字段里有一个tcpSocket,里面的port设置了一个检测的端口。当这个端口 tcp connect能正常建立的时候就认为pod处于健康状态。
此外还有如下的五个Global参数:
        failureThreshold,表示的健康状态下探测失败多少次认为该pod处于失败状态,默认值是 3;
        successThreshold,表示pod从探测失败到再一次判断探测成功需要的阈值次数,默认值是1次;
        timeoutSeconds,表示的是检测的超时时间,当超时时间之内没有检测成功会认为是失败状态;
        periodSeconds:表示检测的时间间隔,默认值是10秒;
        initialDelaySeconds:表示pod启动后延迟多久进行检查
探测结果来讲主要分为三种:success、Failure、Unknown(当前的执行的机制没有进行完整的一个执行,可能是因为超时或者像脚本没有及时返回。此时探测指针不做任何的一个操作,会等待下一次的机制来进行检验)
 
总结:
 
InitContainer具有Container的所有字段。 但校验时会强制执行,禁止使用readinessProbe,因为其不能定义不同于完成(completion)的就绪(readiness)。 
 

五、Pod和容器的生命周期

Pod的生命周期中包括5种状态,对应于Pod的status.Phase字段
这些都是聚合状态,根据Pod的status.conditions中的各个状态聚合而成
status.conditions有以下type,每种其实对应于Pod启动过程中的一个阶段:
    PodScheduled:当前Pod是否处在一个已经被调度(已经bound到node上)的状态
    Ready:当前Pod是否已经ready。
    Initialized:所有的init container是否都已经成功启动
    Unschedulable:当前Pod是否处在一个不可被调度的状态
    ContainerReady:所有的container是否ready。
 
Container的生命周期中包括3种状态,对应于Pod的status.containerStatuses[].state字段:Waiting、Running、Terminated三种
它们同样都是聚合状态,不仅仅由Container的LastTerminationState决定
 
kubectl describe pod xxx获得的Pod状态位的展现如下图:
第一个红框:Pod的聚合状态,对应Pod的status.Phase字段
第二个红框:Container的聚合状态
第三个红框:Pod的status.conditions,每种type一行
第四个红框:记录了Pod相关的事件(状态之间的转换),详见 Event
 

六、Pod的异常排查

Pod的异常排查:
 
状态
原因
处理方法
Pod停留在Pending
PodScheduled为False
没有创建容器
调度器没有进行介入
通过kubectl describe pod来查看相应的事件,如果由于资源不足、端口占用、node selector不满足等造成 pod无法调度,可以在事件里面看到相应的结果,结果会表示有多少个不满足的 node以及分别是因为什么原因
Pod停留在Pending
ContainerReady为False
容器停留在waiting且Reason为ImagePullBackOff
镜像没有正常拉取
镜像是私有镜像,但是没有配置 Pod secret;这个镜像地址不存在的;在内网拉取公网的镜像......
Pod不断被拉起并且Event中可以看到crashing
应用异常
关注应用自身状态,查看Pod的具体日志
处在Runing但是没有正常工作
一般是由于一些非常细碎的配置(类似字段拼写错误),造成了yaml下发后有一段没有正常生效
判断当前yaml是否是正常的。如果yaml没有问题,那么接下来可能要诊断配置的端口是否是正常的,以及Liveness或Readiness是否已经配置正确。
Pod正常但Service无法正常的工作
service异常
查看Pod的label是否有问题,造成service无法找到后面的 endpoint,从而造成service没有办法对外提供服务。
先看service后面是不是有一个真正的endpoint,其看这个endpoint是否可以对外提供正常的服务。
 
 
posted @ 2020-06-03 23:49  扬羽流风  阅读(588)  评论(0编辑  收藏  举报