k8s管理pod资源对象(上)
一、容器于pod资源对象
现代的容器技术被设计用来运行单个进程时,该进程在容器中pid名称空间中的进程号为1,可直接接收并处理信号,于是,在此进程终止时,容器即终止退出。若要在一个容器中运行多个进程,则需要为这些进程提供一个类似于linux操作系统init进程的管控类进程,以树状结构完成多进程的生命周期管理。绝大多数场景中都应该于一个容器中仅运行一个进程,它将日志信息直接输出至容器的标准输出。不过,分别运行于各自容器的进程之间无法实现基于ipc的通信机制,此时,容器间的隔离机制对于依赖于此类通信方式的进程来说却又成了阻碍。pod资源抽象正是用来解决此类问题的组件。pod对象是一组容器的集合,这些容器共享network、uts及ipc名称空间,因此具有相同的域名、主机名和网络接口,并可通过ipc直接通信。为一个pod对象中的各容器提供网络名称空间等共享机制的是底层基础容器pause。
尽管可以将pod类比为物理机或vm,但一个pod内通常仅应该运行一个应用,除非多个进程间有密切关系。不过,有些场景要求必须于同一pod中同时运行多个容器,此时,这些分布式应用必须遵循某些最佳实践机制或基本准则。事实上,k8s并非期望成为一个管理系统,而是一个支持这些最佳实践的向开发人员或管理人员提供更高级别服务的系统。分布式系统设计通常包含以下几种模型:
1、sidecar pattern(边车模型或挎斗模型):即为pod的主应用容器提供协同的辅助应用容器,每个应用独立运行,最为典型的代表是将主应用容器中的日志使用agent收集至日志服务器中时,可以将agent运行为辅助容器。
2、ambassador pattern(大使模型):即为远程服务创建一个本地代理,代理应用运行于容器中,著容器中的应用通过代理容器访问远程服务。一个典型的场景是主应用容器中的进程访问一主多从模型的redis应用时,可在当前pod容器中为redis服务创建一个ambassador container,主应用容器中的进程直接通过localhost接口访问ambassador container即可。即便是redis主从集群架构发生变动时,也仅需要将ambassador conftainer加以修改即可,主应用容器无须对此做出任何反应。
3、adapter pattern(适配器模型):此种模型一般用于将主应用容器中的内容进行标准化输出,例如,日志数据或指标数据的输出,这有助于调用者统一接收数据的接口,另外,某应用滚动升级后的版本不兼容旧的版本时,其报告信息的格式也存在不兼容的可能性,使用adapter container有助于避免那些调用此报告的应用发生错误。
k8s系统的pod资源对象用于运行单个容器化应用,此应用称为pod对象的主容器,同时pod也能容纳多个容器,不过额外的容器一般工作为sidecar模型,用于辅助著容器完成工作职能。
二、管理pod对象的容器
一个pod对象中至少要存在一个容器,因此,containers字段是定义pod时其嵌套字段spec中的必选项,用于为pod指定要创建的容器列表。进行容器配置时,name为必选字段,用于指定容器名称,image字段是可选,以为方便更高级别的管理类资源(如deployment)等能覆盖此字段,于是自助式的pod并不可省略此字段。
1、镜像及其获取策略
各工作节点负责运行pod对象,而pod的核心功用在于运行容器,因此工作节点上必须配置容器运行引擎。k8s系统支持用于自定义镜像文件的获取策略,例如在网络资源较为紧张的时候可以禁止从仓库中获取镜像文件等。容器的“imagesPullPolicy”字段用于为其指定镜像获取策略,它的可用值包括:
Always:镜像标签为latest或镜像不存在时总是从镜像仓库中获取镜像
IfNotPresent:仅当本地镜像缺失时方才从目标仓库下载镜像
Never:禁止从仓库下载镜像,即仅使用本地镜像。
对于标签为latest的镜像文件,其默认的镜像获取策略为Always,而对于其他标签的镜像,其默认策略为IfNotPresent。当使用私有镜像仓库中的镜像时,通常需要由仓库服务器完成认证后才能进行,认证过程要么需要在相关节点上交互式执行docker login命令来进行,要么就是将认证信息定义为专有的secret资源,并配置pod通过imagePullSecrets字段调用此认证信息完成。
2、暴露端口
k8s系统的网络模型中,各pod的IP地址处于同一网络平面,无论是否为容器指定了暴露的端口,都不会影响集群中其他节点之上的pod客户端对其进行访问,这就意味着,任何监听在非lo接口上的端口都可以通过pod网络直接被请求,从这个角度来说,容器端口只是信息性数据,它只是为集群用户提供一个快速了解相关pod对象的可访问端口的途径,而且显式指定容器端口,还能为其赋予一个名称以方便调用。
容器的ports字段的值是一个列表,由一个到多个端口对象组成,它的常用嵌套字段包括:
containerPort:必选字段,指定在pod对象的IP地址上暴露的容器端口。
name:当前端口的名称,在当前pod内必须是唯一;此端口名可被service资源调用。
protocol:端口协议,默认tcp
然而,pod对象的IP地址仅在当前集群内可达,它们无法直接接收来自集群外部的客户端请求流量。可通过其所在的工作节点的IP和端口将其暴露到集群外部:
hostPort:主机端口,它将接收到的请求通过nat机制转发至由containerPort字段指定的容器端口
hostIP:主机端口要绑定的主机IP,默认0.0.0.0,即主机之上所有可用的IP地址;考虑到pod对象是由调度器调度运行的,工作节点的IP地址难以明确指定,因此此字段通常使用默认值。
hostPort和NodePort类型的Service对象暴露端口的方式不同,NodePort是通过所有节点暴露容器服务,而hostPort则是经由pod对象所在的节点IP地址来进行。
3、自定义运行的容器化应用
容器的command字段能够指定不同于镜像默认运行的应用程序,并且可以同时使用args字段进行参数传递,它们将覆盖镜像中的默认定义。不过,如果仅为容器定义了args字段,那么它将作为参数传递给镜像中默认指定运行的应用程序;如果仅为容器定义了command字段,那么它将覆盖镜像中定义的程序及参数,并以无参数方式运行应用程序。自定义args,也是向容器中的应用程序传递配置信息的常用方式之一,对于非云原生的应用程序,这几乎也是最简单的配置方式。另一个常用的方式是使用环境变量。
4、环境变量
向pod对象中的容器环境变量传递数据的方法有两种:env和envFrom,通过环境变量配置容器化应用时,需要在容器配置段中嵌套使用env字段,它的值是一个由环境变量构成的列表。环境变量通常由name和value字段构成。
name:环境变量的名称,必选字段
value:传递给环境变量的值,通过$(VAAR_NAME)引用,逃逸格式为$$(VAR_NAME),默认为空。
5、共享节点的网络名称空间
同一个pod对象的各容器均运行于一个独立的、隔离的network名称空间中,共享同一个网络协议栈及相关的网络设备。也有一些特殊的pod对象需要运行于所在节点的名称空间中,执行系统级的管理任务,例如查看和操作节点的网络资源甚至是网络设备等。
通常,以k8s部署的k8s集群中的kube-apiserver、kube-controller-manager、kube-scheduler,以及kube-proxy和kube-flannel等通常都是第二种类型的pod对象。事实上,仅需要设置spec.hostNetwork的属性为true即可创建共享节点网络名称空间的pod对象。另外,在创建pod对象时,还可以使用spec.hostPid和spec.hostIPC来共享工作节点的pid和ipc名称空间。
6、设置pod对象的安全上下文
pod对象的安全上下文用于设定pod容器的权限和访问控制功能,其支持设置的常用属性包括:
1、基于用户id和组id控制访问对象时的权限。
2、以特权或非特权的方式运行。
3、通过Linux Capabilities为其提供部分特权。
4、基于Seccomp过滤进行的系统调用。
5、基于SELinux的安全标签
6、是否能够进行权限升级。
pod对象的安全上下文定义在spec.securityContext字段中,而容器的安全上下文则定义在spec.containers[].securityContext字段中,且二者可嵌套使用的字段还有所不同。
另外,可设置的安全上下文属性还有fsGroup、seLinuxOptions、supplementalGroups、sysctls、capabilities和privileged等。