运行于Kubernetes中的容器
运行于Kubernetes
中的容器
介绍pod
在实际应用中我们不会单独部署容器, 更多的是针对一组pod
的容器进行部署和操作.
为何需要pod
为何多个容器比单个容器包含多个进程要好
容积被设计为每个容器只运行一个进程(除非进程本身产生进程). 如果在单个容器中运行多个不相关的进程, 那么保持所有进程运行, 管理它们的日志等将会是我们的责任.
了解pod
同一pod
中容器之间的部分隔离
容器之间彼此是完全隔离的, 但是此时我们期望的是隔离容器组, 而不是单个容器, 并让每个容器组内的容器共享一些资源, 而不是全部. Kubernetes
通过配置Docker
来让一个pod
内的所有容器共享相同的Linux
命名空间, 而不是每个容器都有自己的一组命名空间.
容器如何共享相同的IP和端口空间
由于一个pod
中的容器运行于相同的Network
命名空间中, 因此它们共享相同的IP
地址和端口空间. 有可能会产生端口冲突, 但这只涉及同一个pod
中的容器.
介绍平坦pod
间网络
Kubernetes
集群中的所有pod
都在同一个共享网络地址空间中, 这意味着每个pod
都可以通过其他pod
的IP
地址来实现相互访问.
通过pod
合理管理容器
将多层应用分散到多个pod
中, 同时基于扩缩容考虑而分割到多个pod
中. 当应用可能由一个主进程和一个或多个辅助进程时, 才有可能将多个容器添加到单个pod
中.
决定何时在pod
中使用多个容器
当决定是将两个容器放入到一个pod
还是两个独立的pod
时, 我们可以问自己以下几个问题:
- 它们需要一起运行还是可以在不同的主机上运行?
- 它们代表的是一个整体还是相互独立的组件?
- 它们必须一起进行扩缩容还是可以分别进行?
以YAML
或JSON
描述文件创建pod
pod
和其他Kubernetes
资源通常是通过向Kubernetes REST API
提供JSON
或YAML
描述文件来创建的. 此外还有其他更简单的创建资源的方法, 比如kubectl run
命令, 但这些方法通常只允许你配置一组有限的属性.
检查现有pod
的YAML
描述文件
获得pod
的yaml
信息
1 | kubectl get pods <pod名称> -o yaml |
介绍pod
定义的主要部分
pod
定义由这么几个部分组成: 首先是YAML
中使用的Kubernetes API
版本和YAML
描述的资源类型; 其次是几乎在所有Kubernetes
资源中都可以找到的三大重要部分:
metedata
包括名称, 命名空间, 标签和关于该容器的其他信息spec
包含pod
内容的实际说明, 例如pod
的容器, 卷和其他数据.status
包含运行中的pod
的当前信息, 例如pod
所处的条件, 每个容器的描述和状态, 以及内部IP
和其他基本信息.
为pod
创建一个简单的YAML
描述文件
使用kubectl explain
来发现可能的API
对象字段
1 | kubectl explain pods |
使用kubectl create
来创建pod
kubectl create -f
命令用于从yaml
或json
文件中创建任何资源(不只是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 | kubectl get po -l '<label的key>' |
在标签选择器中使用多个条件
在包含多个逗号分隔的情况下, 可以在标签选择器中同时使用多个条件, 此时资源需要全部匹配才算成功匹配了选择器.
使用标签和选择器来约束pod
调度
使用标签分类工作节点
标签可以附加到任何kubernetes
对象上, 包括节点.
将pod
调度到特定节点
为了让调度器只在合适节点上进行选择, 我们需要在pod
的YAML
文件中添加一个节点选择器. 我们只是在spec
部分添加了一个nodeSelector
字段.
调度到一个特定节点
同样地, 我们也可以将pod
调度到某个确定的节点, 由于每个节点都有一个唯一标签, 其中键为kubernetes.io/hostname
, 值为该节点的实际主机名, 因此我们也可以将pod
调度到某个确定的节点. 我们绝不应该考虑单个节点, 而是应该通过标签选择器考虑符合特定标准的逻辑节点组.
注解pod
除标签外, pod
和其他对象还可以包含注解. 注解也是键值对, 所以它们本质上与标签非常相似. 但与标签不同, 注解并不是为了保存标识信息而存在的, 它们不能像标签一样用于对对象进行分组.
大量使用注解可以为每个pod
或其他API
对象添加说明, 以便每个使用该集群的人都可以快速查找有关每个单独对象的信息.
查找对象的注解
为了查看注解, 我们需要获取pod
的完整YAML
文件或使用kubectl describe
命令.
1 | kubectl get po <pod的名称> -o yaml |
添加和修改注解
和标签一样, 注解可以在创建是就添加到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
关键字删除所有内容并不是真的删除所有内容. 一些资源会被保留下来, 并且需要被明确指定删除.