运行于Kubernetes中的容器

运行于Kubernetes中的容器

介绍pod

在实际应用中我们不会单独部署容器, 更多的是针对一组pod的容器进行部署和操作.

为何需要pod

为何多个容器比单个容器包含多个进程要好

容积被设计为每个容器只运行一个进程(除非进程本身产生进程). 如果在单个容器中运行多个不相关的进程, 那么保持所有进程运行, 管理它们的日志等将会是我们的责任.

了解pod

同一pod中容器之间的部分隔离

容器之间彼此是完全隔离的, 但是此时我们期望的是隔离容器组, 而不是单个容器, 并让每个容器组内的容器共享一些资源, 而不是全部. Kubernetes通过配置Docker来让一个pod内的所有容器共享相同的Linux命名空间, 而不是每个容器都有自己的一组命名空间.

容器如何共享相同的IP和端口空间

由于一个pod中的容器运行于相同的Network命名空间中, 因此它们共享相同的IP地址和端口空间. 有可能会产生端口冲突, 但这只涉及同一个pod中的容器.

介绍平坦pod间网络

Kubernetes集群中的所有pod都在同一个共享网络地址空间中, 这意味着每个pod都可以通过其他podIP地址来实现相互访问.

通过pod合理管理容器

将多层应用分散到多个pod中, 同时基于扩缩容考虑而分割到多个pod中. 当应用可能由一个主进程和一个或多个辅助进程时, 才有可能将多个容器添加到单个pod中.

决定何时在pod中使用多个容器

当决定是将两个容器放入到一个pod还是两个独立的pod时, 我们可以问自己以下几个问题:

  1. 它们需要一起运行还是可以在不同的主机上运行?
  2. 它们代表的是一个整体还是相互独立的组件?
  3. 它们必须一起进行扩缩容还是可以分别进行?

YAMLJSON描述文件创建pod

pod和其他Kubernetes资源通常是通过向Kubernetes REST API提供JSONYAML描述文件来创建的. 此外还有其他更简单的创建资源的方法, 比如kubectl run命令, 但这些方法通常只允许你配置一组有限的属性.

检查现有podYAML描述文件

获得podyaml信息

1
kubectl get pods <pod名称> -o yaml

介绍pod定义的主要部分

pod定义由这么几个部分组成: 首先是YAML中使用的Kubernetes API版本和YAML描述的资源类型; 其次是几乎在所有Kubernetes资源中都可以找到的三大重要部分:

  1. metedata包括名称, 命名空间, 标签和关于该容器的其他信息
  2. spec包含pod内容的实际说明, 例如pod的容器, 卷和其他数据.
  3. status包含运行中的pod的当前信息, 例如pod所处的条件, 每个容器的描述和状态, 以及内部IP和其他基本信息.

pod创建一个简单的YAML描述文件

使用kubectl explain来发现可能的API对象字段

1
2
3
kubectl explain pods

kubectl explain pod.spec

使用kubectl create来创建pod

kubectl create -f命令用于从yamljson文件中创建任何资源(不只是pod).

查看应用程序日志

容器化的应用程序通常会将日志记录到标准输出和标准错误流, 而不是将其写入文件.

容器运行时将这些流重定向到文件, 并允许我们运行以下命令来获取容器的日志:

1
docker logs <container id>

使用ssh命令登录到pod正在运行的节点, 并使用docker logs命令查看其日志, 但Kubernetes提供了一种更为简单的方法.

使用kubectl logs命令获取pod日志

为了查看pod的日志(更准确地说是容器的日志), 只需要在本地机器上运行以下命令(不需要ssh到任何地方):

1
kubectl logs <pod名称>

获取多容器pod的日志时指定容器名称

如果我们的pod包含多个容器, 在运行kubectl logs命令时则必须通过包含-c <容器名称>选项来显示指定容器名称.

1
kubectl logs <pod名称> -c <容器名称>

我们只能获取仍然存在的pod的日志. 当一个pod被删除时, 它的日志也会被删除. 如果希望在pod删除之后仍然可以获取其日志, 我们需要设置中心化的, 集群范围的日志系统, 将所有日志存储到中心存储中.

pod发送请求

将本地网络端口转发到pod中的端口

如果想要在不通过service的情况下与某个特定的pod进行通信(出于调试或其他原因), Kubernetes将允许我们配置端口转发到该pod. 可以通过kubectl port-forward命令完成上述操作. 例如以下命名会将机器的本地端口8888转发到目标pod的端口8080:

1
kubectl port-forward <pod名称> 8888:8080

使用标签组织pod

我们需要一种能够基于任意标准将上述pod组织成更小群体的方式. 此外, 我们希望通过一次操作对属于某个组的所有pod进行操作, 而不必单独为每个pod执行操作.

通过标签来组织pod和所有其他Kubernetes对象.

介绍标签

标签是一种简单却功能强大的Kubernetes特性, 不仅可以组织pod, 也可以组织所有其他的Kubernetes资源. 详细来说, 标签是可以附加到任意资源的任意键值对, 用以选择具有该确切标签的资源(这是通过标签选择器完成的). 只要标签的key在资源内是唯一的, 一个资源便可以拥有多个标签. 通常我们创建资源时就会将标签附加到资源上, 但之后我们也可以在添加其他标签, 或者修改现有标签的值, 而无须重新创建资源.

金丝雀发布是指在部署新版本时, 先只让小部分用户体验新版本以观察新版本的表现, 然后再向所有用户进行推广, 这样可以防止暴露有问题的版本给过多的用户.

创建pod时指定标签

显示所有标签

1
kubectl get pods --show-labels

显示特定标签

1
kubectl get pods -L <label1的key>,<label2的key>

修改现有pod的标签

创建标签

1
kubectl label po <pod的名称> <新label的key>=<新label的value> 

修改标签

1
kubectl label po <pod的名称> <已存在label的key>=<label的值>

通过标签选择器列出pod子集

标签选择器允许我们选择标记有特定标签的pod子集, 并对这些pod执行操作. 可以说标签选择器是一种能够根据是否包含具有特定值的特定标签来过滤资源的准则.

标签选择器根据资源的以下条件来选择资源:

包含(或不包含)使用特定键的标签

包含具有特定键和值的标签

包含就有特定值的标签, 但其值与我们指定的不同

使用标签选择器列出pod

1
2
3
4
5
6
kubectl get po -l '<label的key>'
kubectl get po -l '!<label的key>'
kubectl get po -l <label的key>=<label的value>
kubectl get po -l <label的key>!=<label的value>
kubectl get po -l <label的key>in (<label的value>, <label的value>)
kubectl get po -l <label的key>notin (<label的value>, <label的value>)

在标签选择器中使用多个条件

在包含多个逗号分隔的情况下, 可以在标签选择器中同时使用多个条件, 此时资源需要全部匹配才算成功匹配了选择器.

使用标签和选择器来约束pod调度

使用标签分类工作节点

标签可以附加到任何kubernetes对象上, 包括节点.

pod调度到特定节点

为了让调度器只在合适节点上进行选择, 我们需要在podYAML文件中添加一个节点选择器. 我们只是在spec部分添加了一个nodeSelector字段.

调度到一个特定节点

同样地, 我们也可以将pod调度到某个确定的节点, 由于每个节点都有一个唯一标签, 其中键为kubernetes.io/hostname, 值为该节点的实际主机名, 因此我们也可以将pod调度到某个确定的节点. 我们绝不应该考虑单个节点, 而是应该通过标签选择器考虑符合特定标准的逻辑节点组.

注解pod

除标签外, pod和其他对象还可以包含注解. 注解也是键值对, 所以它们本质上与标签非常相似. 但与标签不同, 注解并不是为了保存标识信息而存在的, 它们不能像标签一样用于对对象进行分组.

大量使用注解可以为每个pod或其他API对象添加说明, 以便每个使用该集群的人都可以快速查找有关每个单独对象的信息.

查找对象的注解

为了查看注解, 我们需要获取pod的完整YAML文件或使用kubectl describe命令.

1
2
3
kubectl get po <pod的名称> -o yaml

kubectl describe

添加和修改注解

和标签一样, 注解可以在创建是就添加到pod中, 也可以在之后再对现有的pod进行添加或修改. 其中将注解添加到现有对象的最简单的方式是通过kubectl annotate

1
kubectl annotate pod <pod的名称> <注解的键>=<注解的值>

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

Kubernetes命名空间简单地为对象名称提供了一个作用域. 此时我们并不会将所有资源都放在同一个命名空间中, 而是将它们组织到多个命名空间中, 这样可以允许我们多次使用相同的资源名称.

发现其他命名空间及其pod

列出命名空间:

1
kubectl get ns

当使用kubectl get命令列出资源时, 我们从未明确指定命名空间, 因此kubectl总是默认为default命名空间, 只显示该命名空间下的对象.

得到指定命名空间下的对象:

1
kubectl get po -n <命名空间>

namespace使我们能够将不属于一组的资源分到不重叠的组中. 如果有多个用户或用户组正在使用同一个Kubernetes集群, 并且它们都各自管理自己独特的资源集合, 那么它们就应该使用各自的命名空间. 这样一来, 它们就不用特别担心无意中修改或删除其他用户的资源, 也无须关心名称冲突. 如前所述, 命名空间为资源名称提供了一个作用域.

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

创建一个命名空间

命名空间是一种和其他资源一样的Kubernetes资源, 因此可以通过将YAML文件提交到Kubernetes API服务器来创建该资源.

还可以使用kubectl create namespace命令创建命名空间

1
kubectl create namespace <命名空间>

命名空间只能包含字母, 数字, 横杠

管理其他命名空间中的对象

如果想在指定的命名空间中创建资源, 可以选择在metedata字段中添加一个namespace: custom-namespace属性, 也可以使用kubectl create命令创建资源时指定命名空间:

1
kubectl create -f 模版 -n <命名空间>

在列出, 描述, 修改或删除其他命名空间中的对象时, 需要给kubectl命令传递--namespace(或-n)选项. 如果不指定命名空间, kubectl将在当前上下文中配置的默认命名空间中执行操作.

命名空间提供的隔离

尽管命名空间将对象分隔到不同组, 只允许你对属于特定命名空间的对象进行操作, 但实际上命名空间之间并不提供对正在运行的对象的任何隔离.

停止和移除pod

按名称删除pod

1
kubectl delete po <po的名称> [<pod名称>...]

在删除pod的过程中, 实际上我们在指示Kubernetes终止该pod中的所有容器. Kubernetes向进程发送一个SIGTERM信号并等待一定的秒数(默认为30), 使其正常关闭. 如果它没有及时关闭, 则通过SIGKILL终止该进程. 因此, 为了确保你的进程总是正常关闭, 进程需要正确处理SIGTERM信号.

使用标签选择器删除pod

1
kubectl delete po -l <label的名称>=<label的值>

通过删除整个命名空间来删除pod

可以通过删除整个命名空间来删除这个命名空间, 以及这个命名空间下的pod.

1
kubectl delete ns <命名空间>

删除命名空间中所有pod, 当保留命名空间

删除当前命名空间中的所有pod:

1
kubectl delete po --all

删除命名空间中的(几乎)所有资源

通过使用单个命令删除当前命名空间中的所有资源:

1
kubectl delete all --all

使用all关键字删除所有内容并不是真的删除所有内容. 一些资源会被保留下来, 并且需要被明确指定删除.

-------------本文结束感谢您的阅读-------------