5-K8S核心技术pod
概述
pod是k8s中最小的单元,是一个抽象的概念,是一组容器的集合,是资源对象模型中用户创建或部署的最小的资源对象模型,也是k8s上运行容器化应用的资源对象,其他对象都是用来支撑或扩展pod对象功能的。每一个pod都有一个特殊的被称为“根容器”的pause容器,pause容器对应的镜像属于k8s平台的一部分。
pause容器又叫infra容器,后面将介绍改容器的作用。
pod基本概念
- 最小部署单元
- pod是由一个或多个容器组成
- 一个pod内的容器共享网络命名空间
- pod是短暂的
- 每个pod包含一个或多个紧密相关的用户业务容器
pod存在的意义
- 容器是但进程设计,一个容器运行一个应用进程
- pod是多进程设计,一个pod中可以有多个容器,可以运行多个应用进程
- pod的存在是为了亲密作用
- 多应用之间进行交互
- 网络之间的调用
- 多个应用之间需要频繁调用
pod的设计理念是支持多个容器在pod中共享网络地址和文件系统,可以通过进程通信和文件共享这种简单高效的方式完成组合。可以将pod看做是运行在k8s上的小机器人,k8s的业务可以分为以下几种:
- 长期伺服型:long-running
- 批处理:batch
- 节点后台支撑型:node-deamon
- 有状态应用型:stateful application
对应的控制器分别为:deployment、job、deamonSet、statefulSet
pod实现机制
容器本身之间是隔离的,一般通过namespace和group进行隔离,而pod中共享网络、共享存储是通过pause容器来实现的
pause容器
pause容器又叫infra容器,每个pod中都会有一个pause容器,其他容器称为业务容器,这些业务容器共享pause容器的网络和volume挂载卷;因此容器之间的通信和数据交换更为高效,可以充分利用这一点将一组密切关系的服务放在一个pod中;同一个pod中的容器之间使用localhost就能互相通信。这样pause容器就实现了pod内的共享网络与共享存储。
pause提供的功能
- PID命名空间:作为Pod的“init进程”(pid=1,也即所有其它容器的父容器,所有其它进程的父进程),负责僵尸进程的回收,1.8之后的版本默认不共享PID
- 网络命名空间:pod中的多个通容器可以访问同一个IP和端口范围
- IPC命名空间:pod中的多个容器能够使用systemV IPC或POSIX消息队列进行通信
- UTS命名空间:pod中的多个容器共享一个主机名、volume
pause代码
1 | /* |
共享namespace
在Linux系统中,创建一个新的子进程的时候,该进程就会集成父进程的namespace,k8s的pod就是模拟这种方式。创建pod时,里面第一个被创建的容器永远都是pause容器(系统自动创建),pause容器创建完成后才会创建用户业务容器,系统会为pause创建namespace,同一个pod内创建的业务容器都加入到pause容器的namespace,这样就处于同一个namespace中。从pause代码中可以发现最后是一个死循环+pause函数调用,pause函数的作用是让当前进程暂停,因为要维持namespace的存在就必须有一个属于这个namespace的进程或者文件存在,所以为了维护Pod里面pause创建的Namespace,pause就必须一直存在。
充当系统init进程
在Linux系统中,pid=1的进程我们称之为“init”进程,是内核启动的第一个用户级进程,现在比较新的Linux发行版的init进程就是systemd进程。这个进程有许多工作,其中一个重要工作就是负责“收养孤儿进程”,防止产生太多僵尸进程。简单介绍一下相关的基本概念:Linux系统维护了一个进程表,记录每个进程的状态信息和退出码(exit code),当一个进程退出时,它在表中的信息会一直保留,直到其父进程调用wait(包括waitpid)获取其退出码。所谓僵尸进程就是进程已经退出了,但它的信息还在进程表里面。正常情况下,进程退出时父进程会马上查询该表,并回收子进程的相关资源,所以僵尸进程的持续状态一般都很短。但如果(1)父进程启动子进程之后没有调用wait或者(2)父进程先于子进程挂掉了,那子进程就会变成僵尸进程。如果是第2种情况,即父进程先于子进程死掉了,那操作系统就会把init进程设置为该父进程所有子进程的父进程,即init进程收养了该父进程的所有子进程。当这些子进程退出时,init进程就会充当父进程的角色,从而避免长时间的僵尸进程。但对于第1种情况,一般认为是代码有缺陷,这种情况因为子进程的父进程存在(只是没有调用wait而已),init进程是不会做处理的。此时子进程会成为僵尸进程长期存在,如果要消除这种僵尸进程,只能kill掉父进程。
而pause容器的第二个功能就是充当这个init进程,负责回收僵尸进程。从上面的代码可以看到,pause启动的时候会判断自己的pid是否为1。不过如果要实现该功能,则Pod内的所有容器必须和pause共享PID Namespace。在K8s 1.8版本之前默认是开启PID Namespace共享的,之后版本默认关闭了,用户可以通过–docker-disable-shared-pid=true/false自行设置。开启PID Namespace的好处就是可以享受pause回收僵尸进程的功能,并且因为容器同处于一个PID Namespace,进程间通信也会变得非常方便。但也有一些弊端,比如有些容器进程的PID也必须为1(比如systemd进程),这就会和pause容器产生冲突,另外也会涉及一些安全问题。
影响pod调度的属性
根据request进行调度
1 | resources: #资源管理 |
根据nodeSelector调度
1 | nodeSelector: |
Label 是 Kubernetes 系统中另一个核心概念。一个 Label 是一个 key=value 的键值对,其中 key 与 value 由用户自己指 定。Label 可以附加到各种资源对象上,如 Node、Pod、Service、RC,一个资源对象可以定义任意数量的 Label, 同一个 Label 也可以被添加到任意数量的资源对象上,Label 通常在资源对象定义时确定,也可以在对象创建后动态 添加或删除。
1 | 为node节点设定标签 |
节点亲和性 nodeAffinity
硬亲和
1 | affinity: |
通过requiredDuringSchedulingIgnoredDuringExecution硬亲和,约束条件必须满足。
上面的例子则表示为pod将会调度到标签KEY为VALUE1或VALUE2的节点上。
软亲和
1 | affinity: |
preferredDuringSchedulingIgnoredDuringExecution软亲和为尝试满足约束条件
operator参数 | 说明 |
---|---|
In | label 的值在某个列表中 |
NotIn | label 的值不在某个列表中 |
Gt | label 的值大于某个值 |
Lt | label 的值小于某个值 |
Exists | 某个 label 存在 |
DoesNotExist | 某个 label 不存在 |
污点与污点容忍
污点
使用kubectl taint可以为node设置污点,node被设置污点后将会与pod存在一种互斥关系,可以让node拒绝pod的调度,甚至将已经存在的pod驱逐出去
污点的组成 KEY=VALUE:EFFECT 其中value可以为空,EFFECT描述污点的作用,有以下三个选项:
- NoSchedule : 不会将pod调度到具有该污点的node上
- PreferNoSchedule:尽量避免将pod调度到有该污点的node上
- NoExecute:不会将pod调度到有该污点的node上,同时会将node上已有的pod驱逐
1 | 设置污点 |
污点容忍
设置了污点的node和pod之间产生互斥关系,pod将一定程度上不会调度到这个node上,但可以为pod设置容忍度,设置了容忍度的pod将会容忍指定的污点,从而可以调度到该node上,污点与污点容忍组合使用,避免pod调度到不适合的node上。每个node可以有一个或多个污点,不能忍受这些污点的pod是不会被节点接受的
1 | tolerations: |
当有多个master时,防止资源浪费,可以进行设置,让master也启动pod
1 | kubectl taint nodes NODENAME node-role.kubernetes.io/master=:PreferNoSchedule |
当node有多个污点时,pod必须忍受所有的污点才可以调度到该node