[Kubernetes]深入解析Pod

  Pod是Kubernetes项目的原子调度单位

为什么需要Pod?

  容器是未来云计算系统中的进程,容器镜像就是这个系统里的".exe"安装包,那Kubernetes就是操作系统。

  在一个真正的操作系统里,进程不是独自运行的,而是以进程组的方式组织在一起。对操作系统来说,进程组更方便管理,比如Linux只要将信号SIGKILL信号发送给一个进程组,那么该进程组中的所有进程都会收到这个信号而终止运行。

  可以通过下面这个命令查看进程组,进程后面括号里的数字就是它的进程组ID(process group ID)

$ pstree -g

  这里有一个叫rsyslogd的程序,它负责的是Linux操作系统里的日志处理,由三个进程组成:一个imklog模块,一个imuxsock模块,一个rsyslogd自己的main函数主进程。这三个进程一定要运行在同一台机器上,否则它们之间基于Socket的通信和文件交换都会出现问题。

  那现在如果把rsyslogd这个应用容器化,受限于容器的“单进程模型”(不是指容器里只能运行一个进程,而是指容器没有管理多个进程的能力),这三个模块必须被分别制作成是哪个不同的容器。在这个三个容器运行的时候,它们的设置的内存配额都是1GB。

  假设现在kubernetes集群上有两个节点,node1上有3GB内存可用,node2上有2.5GB内存可用。这是假设用Docker Swarm来运行这个程序,为了让这个是三个容器都运行在同一台机器上,就必须在另外两个容器上设置一个affinity=main(与main容器有亲密性)的约束(即它们两个必须与main容器运行在同一台机器上)。然后顺序执行:“docker run main”,"docker run imkloh","docker run imuxsock",创建这三个容器,这三个容器会进入Swarm的待调度队列。然后main容器和imklog容器都出队并被调度到node2上,那当imuxsock出队时,Swarm就懵了,node2仅有0.5GB了,并不足以运行imuxsock容器。

  上面就是一个典型的成组调度(gang scheduling)没有被妥善处理的例子

  但是在Kubernetes中,Pod是原子调度单位,这就意味着Kubernetes的项目调度器是统一按照Pod而非容器的资源需求进行计算的。

  所以像imklog、imuxsock和main函数这是三个容器将组成一个Pod,这样kubernetes项目在调度时,自然就会选择内存等于3GB的node1节点进行绑定。

  像容器这样的紧密协作,可以称为“超亲密关系”,这些具有超亲密关系的容器的典型特征包括但不限于:互相之间会发生直接的文件交换,使用localhost或者Socket文件进行本地通信、会发生非常频繁的远程调用,需要共享某些Linux Namespace。

  但是也不是所有有关系的容器都属于同一个pod,例如php应用容器和MySQL虽然会发生关系,但是没有必要也不应该部署在同一台机器上,它们更适合做出两个pod。

 

Pod实现原理

  Pod只是一个逻辑概念,Kubernetes真正处理的还是宿主机操作系统上Linux容器的Namespace和Cgroups,而并不存在一个所谓的Pod边界或隔离环境

  Pod其实是一组共享了某些资源的容器,Pod里的所有容器,共享的是同一个Network Namespace,并且可以声明共享同一个Volume

  在Kubernetes项目里,Pod的实现需要使用一个中间容器,这个容器叫做Infra容器(Infra容器占用极少的资源,它的镜像时用汇编语言编写的,永远处于“暂停”状态的容器)。在Pod中,Infra容器永远都是第一个被创建的容器,而其他用户定义的容器,则通过join Network Namespace的方式,与Infra容器关联在一起,如下图所示,对于同一个Pod里面的所有用户容器,它们的进出流量都是通过Infra容器完成的。

 

  在Infra容器Hold住Network Namespace后,用户容器就可以加入到Infra容器的Network Namespace中。对于Pod里容器A和容器B来说

  • 它们可以直接使用localhost进行通信
  • 它们看到的网络设备跟infra容器看到的完全一样
  • 一个Pod只有一个IP地址(即这个Pod的Network Namespace对应的IP地址)。其他所有网络资源,都是一个Pod一份,并且被该Pod中的所有容器共享
  • Pod的生命周期只跟Infra容器一致,而与容器A和容器B无关

 

 Pod中重要字段含义和用法

  Pod是Kubernetes的最小编排单位,这个设计落实到API对象上,容器(Container)就成了Pod属性里的一个普通的字段,那么哪些属性属于Pod对象,又有哪些属性属于Container对象?

  Pod扮演的是传统部署环境里虚拟机的角色,这样的设计是为了使用户从传统环境(虚拟机环境)向Kubernetes(容器环境)的迁移更加平滑。那也可以把Pod看成传统环境里的“机器”,把容器看作是运行再这个“机器”里的“用户进程“

  凡是调度、网络、存储以及安全相关的属性,基本上都是Pod级别的, 它们描述的是“机器”这个整体,而不是里面运行的“程序”

    NodeSelector:一个供用户将Pod与Node进行绑定的字段

apiVersion: v1
kind: Pod
……
spec:
  nodeSelector:
  disktype: ssd  ##这个pod只能运行再携带了“disktype:ssd”标签的节点上,否则调度失败

 

     NodeName:

    一旦Pod这个字段被赋值,Kubernetes项目就会认为这个Pod已经经过了调度,调度的结果就是复制节点的名字。这个字段一般由调度器负责设置

 

    HostAliases:定义了Pod的hosts文件里的内容

    在Kubernetes项目中,如果要设置hosts文件里的内容,一定要通过下面这种方法,否则如果直接修改了hosts文件的话,在Pod被删除重建以后,kubelet会自动覆盖掉被修改的内容

apiVersion: v1
kind: Pod
……
spec:
    hostAliases:
    - ip: "10.1.2.3"
      hostnames:
      - "foo.remote"
      - "bar.remote"
……

  

   凡是跟容器的Linux Namespace相关的属性,也一定是Pod级别的

apiVersion: v1
kind: Pod
metadata:
    name: nginx
spec:
    shareProcessNamespace: true   #共享PID Namespace
    containers:
……

 

  凡是Pod中的容器要共享宿主机的Namespace也一定是Podc级别的定义

apiVersion: v1
kind: Pod
metadata:
    name: nginx
spec:
    hostNetwork: true  #共享宿主机Network Namespace
    hostIPC: true    #共享宿主机IPC Namespace
    hostPID: true    #共享宿主机PID Namespace
    containers:
……

 

Pod的生命周期

  • Pending:Pod的YAML文件已经提交给了Kubernetes,API对象已经被创建并保存在Etcd当中,但是这个Pod里有些容器因为某种原因而不能被顺利创建,比如调度不成功
  • Running:Pod已经调度成功,跟一个具体的节点绑定,它包含的容器都已经创建成功,并且至少有一个正在运行中
  • Succeeded: Pod里所有容器都正常运行完毕,并且已经退出了,一次性任务最常见
  • Failed:Pod里至少有一个容器以不正常的状态(非0的返回码)退出
  • Unknown:Pod状态不能持续地被kubelet汇报给kub-apiserver,很可能是Master和Kubelet之间通信出了问题

 

posted @ 2018-09-24 09:46  yuxiaoba  阅读(952)  评论(0编辑  收藏  举报