本文来自正在规划的Go语言&云原生自我提升系列,欢迎关注后续文章。
Kubernetes可随着应用大小和复杂度的扩展而扩展。标签和注解是Kubernetes中的基本概念,可以让人类的想法对应用进行分组。我们可以组织、标记以及交叉索引所有表示应用中分组的资源。
标签是打在Kubernetes对象比如Pod和副本集上的键值对。可以是任意名称,对于关联识别Kubernetes对象信息非常有用。标签为对象分组提供了基础。
而注解则提供了类似标签的存储机制:用于存储工具和库可使用的非标识信息的键值对。不同于标签的是,注解不是用来查询、过滤或是区分Pod的。
标签
标签用于标识对象的元数据。用于对对象分组、浏览和操作。使用标签源自Google运行大型复杂应用的经验。来自这些经验的一些总结:
- 生产环境无单体。在部署软件时,通常会启动单实例。但随着应用逐渐成熟,这些单体通常会变多,成本一组对象。因此Kubernetes处理一组对象而不是单个实例。
- 系统的等级对很多用户总会捉襟见肘。此外,用户分组和等级会随时间发生变化。比如,用户一开始认为所有应用由多个服务组成。但一段时间后发现同一个服务可能由多应用共享。Kubernetes的标签足够灵活,远不止可适用这些场景。
有关站点可靠性请参见Betsy Beyer等人所写的站点可靠性工程一书(也就是大名鼎鼎的Google SRE)。
标签语法简单。就是键值对,键和值都是字符串。标签的键可分为两部分:可选前缀和名称,由斜杠分开。如定义了前缀,必须是不超过253个字符的DNS子域名。必须要有键名称,最大长度为63个字符。名称必须以字母数字字符开头和结束,允许在中间使用横杠(-
)、下划线(_
)和点(.
) 。
标签的值是最长63个字符的字符串。标签的值与键规则相同。表6-1展示了一些有限的标签键和值。
表6-1 标签示例
键 | 值 |
---|---|
acme.com/app-version | 1.0.0 |
appVersion | 1.0.0 |
app.version | 1.0.0 |
kubernetes.io/cluster-service | true |
在标签和注解中使用域名时,应该以某种方式结合具体的实体。例如,项目中可能会定义一组标签来标识应用部署的不用阶段,如待发布、金丝雀测试和生产等。或是云服务商可能会定义厂商相关的注解来扩展Kubernetes对象开启相应服务特性。
应用标签
这里我们使用一些标签创建一些部署(一种创建一组Pod的方式)。共两个应用(alpaca
和bandicoot
),环境和版本各异。
首先,创建一个部署alpaca-prod
,设置ver
、app
和env
标签:
1 2 |
$ kubectl create deployment alpaca-prod --image=gcr.io/kuar-demo/kuard-amd64:blue --replicas=2 $ kubectl label pods -l 'app=alpaca-prod' --overwrite ver=1 app=alpaca env=prod |
注:kubectl run
已弃用了--replicas
参数,所以没有法直接使用kubectl run
接着,创建 一个部署alpaca-test
,并将ver
、app
和env
标签设置为相应值:
1 2 |
$ kubectl create deployment alpaca-test --image=gcr.io/kuar-demo/kuard-amd64:green --replicas=1 $ kubectl label deployment pods -l 'app=alpaca-test' --overwrite ver=2 app=alpaca env=test |
最后为bandicoot
创建两个部署。将环境分别命名为prod
和staging
:
1 2 3 4 |
$ kubectl create deployment bandicoot-prod --image=gcr.io/kuar-demo/kuard-amd64:green --replicas=2 $ kubectl label pods -l 'app=bandicoot-prod' --overwrite ver=2 app=bandicoot env=prod $ kubectl create deployment bandicoot-staging --image=gcr.io/kuar-demo/kuard-amd64:green --replicas=1 $ kubectl label pods -l 'app=bandicoot-staging' --overwrite ver=2 app=bandicoot env=staging |
此时,应该有4个部署,alpaca-prod
、alpaca-test
、bandicoot-prod
和bandicoot-staging
:
$ kubectl get deployments –show-labels
1 2 3 4 5 |
NAME ... LABELS alpaca-prod ... app=alpaca,env=prod,ver=1 alpaca-test ... app=alpaca,env=test,ver=2 bandicoot-prod ... app=bandicoot,env=prod,ver=2 bandicoot-staging ... app=bandicoot,env=staging,ver=2 |
可根据标签可视化为如下的文氏图:
图6-1 应用于部署的标签可视化显示
修改标签
创建好对象后还可以应用或更新标签:
1 |
$ kubectl label deployments alpaca-test "canary=true" |
警告:以上示例中,kubectl labe
命令只会修改部署自身的标签,而不会影响其它由部署创建的对象,比如副本集和Pod。如需修改它们,需要更改部署中内嵌的模板(参见第10章)。
还可以对kubectl get
使用-L
参数按列显示标签值:
1 2 3 4 5 6 7 |
$ kubectl get deployments -L canary NAME DESIRED CURRENT ... CANARY alpaca-prod 2 2 ... alpaca-test 1 1 ... true bandicoot-prod 2 2 ... bandicoot-staging 1 1 ... |
可以通过减号后缀删除某个标签:
1 |
$ kubectl label deployments alpaca-test "canary-" |
标签选择器
标签选择器用于通过一组标签过滤Kubernetes对象。选择器使用一种简单的布尔表达式。可供终端用户(通kubectl
这样的工具)或是各类对象(比如副本集如何与Pod关联)使用。
每个(通过副本集的)部署使用部署中内嵌模板指定的标签创建一组Pod。由kubectl run
命令配置。
运行kubectl get pods
命令会返回集群中当前运行的所有Pod。我们一共有位于三种环境的6个kuard
Pods:
1 2 3 4 5 6 7 8 9 |
$ kubectl get pods --show-labels NAME ... LABELS alpaca-prod-3408831585-4nzfb ... app=alpaca,env=prod,ver=1,... alpaca-prod-3408831585-kga0a ... app=alpaca,env=prod,ver=1,... alpaca-test-1004512375-3r1m5 ... app=alpaca,env=test,ver=2,... bandicoot-prod-373860099-0t1gp ... app=bandicoot,env=prod,ver=2,... bandicoot-prod-373860099-k2wcf ... app=bandicoot,env=prod,ver=2,... bandicoot-staging-1839769971-3ndv ... app=bandicoot,env=staging,ver=2,... |
注:读者可能会发现之前没见过的标签:pod-template-hash
。这个标签由部署添加,用于跟踪哪个Pod是由哪个模板版本所生成。这样可以由部署清晰地管理更新,这部分内容会在第10章中进行讲解。
如果希望只列出标签ver
为2
的Pod,可以使用--selector
参数:
1 2 3 4 5 6 7 |
$ kubectl get pods --selector="ver=2" NAME READY STATUS RESTARTS AGE alpaca-test-1004512375-3r1m5 1/1 Running 0 3m bandicoot-prod-373860099-0t1gp 1/1 Running 0 3m bandicoot-prod-373860099-k2wcf 1/1 Running 0 3m bandicoot-staging-1839769971-3ndv5 1/1 Running 0 3m |
如果指定了由逗号分隔的两个选择器,只有同时满足两个条件的对象会返回。这是一个逻辑与运算:
1 2 3 4 5 6 |
$ kubectl get pods --selector="app=bandicoot,ver=2" NAME READY STATUS RESTARTS AGE bandicoot-prod-373860099-0t1gp 1/1 Running 0 4m bandicoot-prod-373860099-k2wcf 1/1 Running 0 4m bandicoot-staging-1839769971-3ndv5 1/1 Running 0 4m |
也可查询标签是否在一组值之中。下面查看所有app
标签为alpaca
或bandicoot
的Pod(返回所有这6个Pod):
1 2 3 4 5 6 7 8 9 |
$ kubectl get pods --selector="app in (alpaca,bandicoot)" NAME READY STATUS RESTARTS AGE alpaca-prod-3408831585-4nzfb 1/1 Running 0 6m alpaca-prod-3408831585-kga0a 1/1 Running 0 6m alpaca-test-1004512375-3r1m5 1/1 Running 0 6m bandicoot-prod-373860099-0t1gp 1/1 Running 0 6m bandicoot-prod-373860099-k2wcf 1/1 Running 0 6m bandicoot-staging-1839769971-3ndv5 1/1 Running 0 6m |
最后,我们可以查询是否设置了某个标签。下面我们查找设置过canary
标签值的部署:
1 2 3 4 |
$ kubectl get deployments --selector="canary" NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE alpaca-test 1 1 1 1 7m |
以上这些还有取反的版本,参见表6-2。
表6-2 选择器运算符
运算符 | 描述 |
---|---|
key=value | key设置为value |
key!=value | key未设置为value |
key in (value1, value2) | key为value1或value2中之一 |
key notin (value1, value2) | key不为value1或value2之一 |
key | 设置了key |
!key | 没设置key |
例如,查询某个键未设置的命令如下,此处为canary
:
1 |
$ kubectl get deployments --selector='!canary' |
可以合并使用正向和取反运算符:
1 |
$ kubectl get pods -l 'ver=2,!canary' |
API对象中的标签选择器
Kubernetes对象使用标签选择器来指代一组其它Kubernetes对象。不同于前面小节中的普通字符串,我们使用一套解析结构。
出于历史原因(Kubernetes保持API的兼容),存在两种形式。大部分对象支持更新、更强大的一组选择器运算符。app=alpaca,ver in (1, 2)
选择器可转化成:
1 2 3 4 5 |
selector: matchLabels: app: alpaca matchExpressions: - {key: ver, operator: In, values: [1, 2]} |
上例使用紧凑的YAML语法。它是matchExpressions
列表中的一个包含三项的字典。最后一项 (values
)的值为包含两项的列表。所有这些内容都通过逻辑与进行运算。表示!=
运算符的唯一方式是将其转换为带单个值的NotIn
表达式。
选择器更老一些的形式(用于ReplicationController
和服务中)仅支持=
运算符。=
运算选择所有配置键值对组的对象。app=alpaca,ver=1
选择器可表示为:
1 2 3 |
selector: app: alpaca ver: 1 |
Kubernetes架构中的标签
标签除了可供用户组织自己的基础设置,还对关联各个Kubernetes对象起着重要的作用。Kubernetes是刻意保持解耦的系统。不存在等级,所有组件都独立运行。但在很多情况下,对象需要相互关联,这些关联通过标签和标签选择器定义。
例如,副本集,用于创建和维护Pod的多个副本,查找其通过选择器管理的Pod。类似地,服务负载均衡通过选择器查询查找发送流量的Pod。在创建Pod时,可使用节点选择器来标识出一组可供其调度的节点。在希望在集群中限制网络流量时,可使用网络策略配合具体的标签来分别出允许和不允许通讯的Pod。
标签是将Kubernetes应用组合在一起的强大的万能胶水。虽然应用可能一开始只有一组简单的标签和查询,随着业务增长它会变得更多更复杂。
注解
注解用于存储Kubernetes更多的元信息,元信息的唯一目的是辅助工具和库。它是其它程序通过API存储一些复杂对象数据驱动Kubernetes 的一种方式。注解可用于工具本身或是在外部系统之间传递配置信息。
标签的用途是标识和分组对象,而注解用于提供对象来源、如何使用及其策略的额外信息。有重叠的部分,何时使用注解、何时使用标签是个人习惯问题。在不确定时,以注解向对象添加信息,在发现需要在选择器中使用它时再将其修改为标签。
注解用于:
- 记录对象最近更新的原因
- 一个专项调度策略和另一个专项调度的通讯
- 上一个工具更新资源的扩展数据以及是如何更新的(用于监测其它工具的变化及进行智能合并)
- 添加不适宜标签的构建、发布或镜像信息(包括Git哈希、时间戳、pull请求单号等)
- 部署对象记录其管理的滚动发布的副本集
- 提供增强视觉品质或UI可用性的附加信息。登录,对象可包含图标的链接(或base64编码的图标)
- Kubernetes原型alpha版本功能(不创建一级API字体,而是将功能的参数编码放到注解中)
注解用在Kubernetes中的各处,主要用途是滚动部署。在滚动部署期间,注释用于追踪滚动发布状态并在需要回滚到上一个状态的部署时提供回滚所需信息。
避免把 Kubernetes API服务端用作通用数据库。注解对存储与特定资源调度关联的多个小块数据很有益处。如果希望在Kubernetes中存储的数据 并没有明确的关联对象,考虑将数据存储到更合适的数据库中。
注解的键与标签的键格式相同。但因其更常用于在工具间传输信息,键中的命名空间就显得更为重要。举几个键的例子:deployment.kubernetes.io/revision
或kubernetes.io/change-cause
。
注解中的值为任意形式的字符串。虽然有很强的灵活性,允许用户存储任意数据,但因为是任意文本,并没有做格式校验。例如,经常会将JSON文档编码为字符串存到注解中。需要注意Kubernetes 服务端并不会知道注解的值需要用什么格式。如果使用注解传递或存储数据,并不能保证数据的有效性。这会让错误的追踪变得更困难。
注解在Kubernetes对象的通用metadata
版块中定义:
1 2 3 4 5 |
... metadata: annotations: example.com/icon-url: "https://example.com/icon.png" ... |
警告:注解用起来很方便,具有强大的松耦合性,但谨慎使用避免出现混乱的数据。
清理
清理本文一开始创建的部署非常简单:
1 |
$ kubectl delete deployments --all |
如果希望进行限定,可使用–selector参数选择要删除的部署。
小结
标签可用于标识或是分组Kubernetes集群中的对象。也可用于选择器查询实现对象,如Pod的灵活运行时分组。
注解提供了自动化工具和客户端库使用的具有对象作用域的键值元数据。也可用于存储外部工具如第三方调度工具或监控工具使用的配置数据。
标签和注解对于理解Kubernetes集群中的重要组成部分如何协作保障处于所需的集群状态非常关键。恰当的使用可以解锁Kubernetes的强大能力,也为自动化工具和部署工作流提供了起点。