Pod:运行于k8s中的容器

2021-11-29
8 min read

pod是一组并置的容器,代表了k8s中的基本构建模块。实际应用中一般不单独部署容器而是对一组pod的容器进行部署和操作。

基本介绍

一个pod绝不会跨越多个工作节点,在一个pod中的容器总是在一个工作节点上运行

为什么需要pod

因为我们希望一个容器只运行一个进程。如果需要保持多个不相关进程,那么我们需要保持所有进程运行并管理他们的日志,需要包含一种进程崩溃的时候自动重启的机制,需要记录到相同标准输出中等等。。所以我们希望每个进程运行于自己的容器之中。 基于这样的思考,我们希望能够同时运行一些密切相关的进程,并为他们提供(几乎)相同的环境,此时这些进程就像同时运行在一个容器中一样,但是又保持着一定的隔离。所以我们需要一种高级的结构将容器绑定,并将他们作为一个单元进行管理。

简单介绍

在pod中,不是完全隔离,而是会让容器组中的容器共享一些资源,k8s通过配置Docker让一个pod内的所有容器共享完全相同的Linux命名空间。由于其所有容器在相同network和UTS命名空间下运行,所以共享相同主机名和网络接口,也能通过IPC(进程间通信)来进行通信。(在最新的k8s和docker中,还可以选择共享相同pid命名空间,但是默认是未激活的)。但是对于文件系统,每个容器文件系统都是完全隔离的,但可以使用名为Volume的k8s资源来共享文件目录。

同一个pod内的容器可能遇到端口冲突,不同容器间不会遇到。除此之外,一个pod中所有容器有相同的loopback接口,因此可以通过localhost与同一个pod中的其他容器进行通信。

k8s集群中所有pod在同一个共享网络地址空间中,即每个pod可以通过其他pod的IP地址实现互相访问,他们之间没有NAT(网络地址转换)网关。

总的而言,pod就是一个逻辑主机,行为与非容器世界中的物理主机类似,只是每一个进程被封装在了一个容器中。

由于pod比较轻量,所以我们几乎可以在不导致任何额外开销的情况下拥有尽可能多的pod,我们应该将程序组织到多个pod中,每个pod只包含紧密相关的组件或者进程

如何确定该配置为单个pod还是多个pod呢?

  • 将多层应用分散到多个pod中,比如可以将前端后端分成两个pod
  • 基于扩缩容考虑而分割到多个pod中。pod是扩缩容的基本单位。
  • 将多个容器添加到单个pod的主要原因可能是应用由一个主进程和若干个辅助进程组成。比如一个pod的主容器时仅仅服务于某个目录中的文件的web服务器,而另一个容器(所谓的sidecar容器)可以定期从外部源下载内容并存储,还需要使用k8s volumn,并挂载到两个容器中,常见的sidecar容器例子还有日志轮转器和收集器、数据处理器、通信适配器等。
  • 决定在pod中使用多个容器时,需要问自己以下问题:
    • 是否需要一起运行,还是可以在不同主机上运行
    • 代表的是一个整体还是相互独立的组件
    • 必须一起扩缩容还是可以分别进行

通过YAML或JSON描述文件创建pod

创建对象时可以参考kubernetes API文档:http: //kubemetes.io/ docs/reference/

  • 主要部分基本介绍:
    • apiVersion:YAML描述文件使用的k8s API版本
    • kind:k8s对象或资源类型
    • metadata:元数据,名称,命名空间,标签和注解等
    • spec:pod规格或内容(pod的容器列表、volume等)
    • status:运行中的pod及其内部容器的详细状态(每个容器的描述和状态,内部IP和其他基本信息),status部分包含的是只读的运行时数据,创建新pod时,不需要提供status部分
  • 在实际写时,可以用explain语句来查看api对象支持的属性(也就是看参考文档)
    • kubectl explain pods:看pod的基本介绍
    • kubectl explain pod.spec:看pod中spec部分中的属性具体信息
  • 创建语句:kubectl create -f xxx.yaml(值得注意的时create -f可以从YAML或JSON文件创建任何的资源,包括但不限于pod)
  • 查看pod对应的yaml或者json信息:kubectl get po xxx -o yaml (/ json):此处的xx就是上面创建语句里面那个
  • 查看pods状态:kubectl get pods
  • 容器在运行时会见标准输出和标准错误流重定向到文件,如docker可以通过docker logs <container id> 查看日志
  • 可以直接在本地上运行kubectl logs xxx 查看pod的日志,值得注意的是每天或者日志达到10mb之后,容器日志会自动的轮替,logs只能看到最后一次轮替后的日志
  • 如果pod内由多个容器,可以加上-c 参数查看指定容器的日志,如kubectl logs xxx -c yyy 。目前的设置中pod被删除日志就被删除,如果希望不删除则要设置一个中心化、集群范围的日志系统,将所有的日志存储到中心存储中去。
  • 如果想要不通过service的情况和某个pod进行通信(比如调试或者测试的情况下),k8s允许将配置端口转发到该pod,可以使用kubectl port-forward 命令。比如kubectl port-forward xxx 8888:8080 就会把机器的8888端口转发到xxx pod的8080端口。这样可以通过 localhost:8888访问到pod

使用标签组织pod

由于微服务可能有很多个pod,我们需要一种能够基于任意标准将上述pod组织成更小群体的方式,这样处理系统的人员可以看到pod是什么,并且可以通过一次操作给属于某个组的所有pod进行操作,而不用对每个pod执行操作。故而可以使用标签进行组织。

标签是可以附加到资源的任意键值对,用来确认具有该确切标签的资源(通过标签选择器完成)。只要标签的key在资源内唯一,一个资源就可以有多个标签。

  • 比如可以添加标签:
    • app:指定pod属于哪个应用、组件或者微服务
    • rel:显示pod中运行的应用程序的版本是stable,beta还是canary(金丝雀版本就是只让一小部分用户体验新版本)

在添加标签时,可以在YAML中的metadata部分用labels指示,如

...
metadata:
	name: xxx
	labels:
		creation_method: xx
		env: xx
spec:
...
  • get pod时展示标签:kubectl get po --show-labels
  • 只列出部分标签,并希望分别显示:kubectl get po -L xx(希望展示的标签,可以用 , 分隔)
  • 给已有pod添加标签:kubectl label po xx yy=zz (xx为pod名字,yy为标签名,zz为标签值)
  • 修改已有标签:kubectl label po xx yy=zz --overwrite
  • 显示没有这个标签的pod:kubectl get po -l '!xx'
  • 标签选择器还支持以下操作
    • 带有xx标签,值不为yy的xx!=yy
    • 带有xx标签,值为yy或zz的:xx in (yy,zz)
    • 带有xx标签,值不为yy或zz的:xx notin (yy,zz)
    • 可以使用多条件,中间用,分隔即可

还可以给Node标签,然后在pod的YAML文件中,spec部分使用nodeSelector来让pod被调度到有指定特征的节点上去。(比如希望gpu运算密集的pod被调度到支持gpu加速的节点上)

注解pod

pod和其他对象也可以包含注解,注解也是键值对,可以容纳更多的信息,主要用于工具使用。

在向k8s引入新特性的时候,常常会使用注解。新功能的alpha和beta版本不会向API对象引入任何新字段,因此使用注解而不是字段,一旦API更改通过并得到认可,就引入字段并废弃注解。

注解可以在YAML文件的metadata的annotations部分加入,也可以向标签一样使用kubectl annotate pod xx yy=zz 添加。一般使用类似mycompany.com/someannotation 这样的形式作为注解名可以避免一些冲突。

使用命名空间对资源进行分组

命名空间可以隔离资源,还可以用于仅允许某些用户访问某些特定资源,甚至限制单个用户可用的计算资源数量

可以使用YAML文件或者kubectl create namespace 命令创建命名空间

如果有两个同名的pod在不同的命名空间中,在修改其中对象时,需要给kubectl命令传递一个--namespace 或者 -n 的选项,否则将会在当前上下文配置的默认命名空间中执行操作,当前上下文命名空间等可以通过kubectl config set-context 命令进行更改。

值得注意的是,命名空间在实质上没有提供对正在运行的对象的任何隔离。命名空间之间是否提供网络隔离取决于k8s所使用的网络解决方案。

停止和移除pod

  • 按名称删除pod: kubectl delete po xx 删除过程中,是指示k8s终止pod中所有容器,k8s向进程发送一个SIGTERM信号并等待一段时间(默认30s),使其正常关闭,如果没关,就发送SIGKILL终止进程。所以为了保证进程总能正常关闭,进程需要能够正确处理SIGTERM信号。

  • 按名称删除多个时,使用空格分隔即可

  • 按照标签选择器删除pod:kubectl delete po -l xx=yy

  • 也可以直接删除整个命名空间来删除pod:kubectl delete ns xx

  • 删除当前命名空间中所有pod,但是保留命名空间:kubectl delete pod --all

  • 删除命名空间中所有资源(包括了service):kubectl delete all --all