[K8s]Kubernetes 基础(上)
备注:此文档所有操作对应 Kubernetes 版本为 v1.20
1 - Master 节点 和 Node 节点概念
Master节点
Master 节点是 Kubernetes 集群的控制节点,在生产环境中不建议部署集群核心组件外的任何Pod,公司业务的Pod更是不建议部署到Master节点上,以免升级或者维护时对业务造成影响。
Master 节点的组件包括:
APIServer:APIServer是整个集群的控制中枢,提供集群中各个模块之间的数据交换,并将集群状态和信息存储到分布式键-值(key-value)存储系统Etcd集群中。同时它也是集群管理、资源配额、提供完备的集群安全机制的入口,为集群各类资源对象提供增、删、改、查以及watch的 REST API 接口。APIServer 作为 Kubernetes 的关键组件,使用 Kubernetes API 和 JSON over HTTP 提供 Kubernetes 的内部和外部接口。
Scheduler:Scheduler 是集群Pod的调度中心,主要是通过调度算法将 Pod 分配到最佳的节点(Node),它通过 APIServer 监听所有 Pod 的状态,一旦发现新的未被调度到任何 Node 节点的 Pod(PodSpec.NodeName 为空),就会根据一系列策略选择最佳节点进行调度,对每一个 Pod 创建一个绑定(binding),然后被调度的节点上的 Kubelet 负责启动该 Pod。Scheduler 是集群可插拔式组件,它跟踪每个节点上的资源利用率以确保工作负载不会超过可用资源。因此 Scheduler 必须知道资源需求、资源可用性以及其他约束和策略,例如服务质量、亲和力/反关联性要求、数据位置等。Scheduler 将资源供应与工作负载需求相匹配以维持系统的稳定和可靠,因此 Scheduler 在调度的过程中需要考虑公平、资源高效利用、效率等方面的问题。
Controller Manager:Controller Manager 是集群状态管理器(它的英文直译名为控制器管理器),以保证 Pod 或其他资源达到期望值。当集群中某个 Pod 的副本数或其他资源因故障和错误导致无法正常运行,没有达到设定的值时,Controller Manager 会尝试自动修复并使其达到期望状态。Controller Manager 包含 NodeController、ReplicationController、EndpointController、NamespaceController、ServiceAccountController、ResourceQuotaController、ServiceController 和 TokenController,该控制器管理器可与API服务器进行通信以在需要时创建、更新或删除它所管理的资源,如 Pod、服务断点等。
Etcd:Etcd 由 CoreOS 开发,用于可靠地存储集群的配置数据,是一种持久性、轻量型、分布式的键-值(key-value)数据存储组件。Etcd 作为 Kubernetes 集群的持久化存储系统,集群的灾难恢复、状态信息存储都与其密不可分,所以在 Kubernetes 高可用集群中,Etcd 的高可用是至关重要的一部分,在生产环境中建议部署为大于3的奇数个数的 Etcd,以保证数据的安全性和可恢复性。Etcd 可与 Master 组件部署在同一个节点上,大规模集群环境下建议部署在集群外,并且使用高性能服务器来提高 Etcd 的性能和降低 Etcd 同步数据的延迟。
Node节点
Node 节点也被称为 Worker 或 Minion,是主要负责部署容器(工作负载)的单机(或虚拟机),集群中的每个节点都必须具备容器的运行环境(runtime),比如 Docker 及其他组件等。Kubelet 作为守护进程运行在 Node 节点上,负责监听该节点上所有的 Pod,同时负责上报该节点上所有 Pod的运行状态,确保节点上的所有容器都能正常运行。当 Node节点宕机(NotReady状态)时,该节点上运行的 Pod 会被自动地转移到其他节点上。
Node 节点包括:
Kubelet:负责与 Master 通信协作,管理该节点上的 Pod。
Kube-Proxy:负责各 Pod 之间的通信和负载均衡。
Docker Engine:Docker 引擎,负载对容器的管理。
2 - Pod概念
什么是Pod?
Pod 可简单地理解为是一组、一个或多个容器,具有共享存储/网络及如何运行容器的规范。Pad 包含一个或多个相对紧密耦合的应用程序容器,处于同一个 Pod 中的容器共享同样的存储空间(Volume,卷或存储卷)、IP地址和Port端口,容器之间使用 localhost:port 相互访问。根据 Docker 的构造,Pod 可被建模为一组具有共享命令空间、卷、IP地址和Port端口的 Docker 容器。 Pod 包含的容器最好是一个容器只运行一个进程。每个 Pod 包含一个pause 容器,pause 容器是 Pod 的父容器,它主要负责僵尸进程的回收管理。Kubernetes 为每个 Pod 都分配一个唯一的IP地址,这样就可以保证应用程序使用同一端口,避免了发生冲突的问题。
Pod字段解析
apiVersion: v1 # 必选,API的版本号
kind: Pod # 必选,类型Pod
metadata: # 必选,元数据
name: nginx # 必选,符合RFC 1035规范的Pod名称
namespace: web-testing # 可选,不指定默认为default,Pod所在的命名空间
labels: # 可选,标签选择器,一般用于Selector
- app: nginx
annotations: # 可选,注释列表
- app: nginx
spec: # 必选,用于定义容器的详细信息
containers: # 必选,容器列表
- name: nginx # 必选,符合RFC 1035规范的容器名称
image: nginx: v1 # 必选,容器所用的镜像的地址
imagePullPolicy: Always # 可选,镜像拉取策略
command:
- nginx # 可选,容器启动执行的命令
- -g
- "daemon off;"
workingDir: /usr/share/nginx/html # 可选,容器的工作目录
volumeMounts: # 可选,存储卷配置
- name: webroot # 存储卷名称
mountPath: /usr/share/nginx/html # 挂载目录
readOnly: true # 只读
ports: # 可选,容器需要暴露的端口号列表
- name: http # 端口名称
containerPort: 80 # 端口号
protocol: TCP # 端口协议,默认TCP
env: # 可选,环境变量配置
- name: TZ # 变量名
value: Asia/Shanghai
- name: LANG
value: en_US.utf8
resources: # 可选,资源限制和资源请求限制
limits: # 最大限制设置
cpu: 1000m
memory: 1024MiB
requests: # 启动所需的资源
cpu: 100m
memory: 512MiB
readinessProbe: # 可选,容器状态检查
httpGet: # 检测方式
path: / # 检查路径
port: 80 # 监控端口
timeoutSeconds: 2 # 超时时间
initialDelaySeconds: 60 # 初始化时间
livenessProbe: # 可选,监控状态检查
exec: # 检测方式
command:
- cat
- /health
httpGet: # 检测方式
path: /_health
port: 8080
httpHeaders:
- name: end-user
value: jason
tcpSocket: # 检测方式
port: 80
initialDelaySeconds: 60 # 初始化时间
timeoutSeconds: 2 # 超时时间
periodSeconds: 5 # 检测间隔
successThreshold: 2 # 检查成功为2次表示就绪
failureThreshold: 1 # 检测失败1次表示未就绪
securityContext: # 可选,限制容器不可信的行为
provoleged: false
restartPolicy: Always # 可选,默认为Always
nodeSelector: # 可选,指定Node节点
region: subnet7
imagePullSecrets: # 可选,拉取镜像使用的secret
- name: default-dockercfg-86258
hostNetwork: false # 可选,是否为主机模式,如是,会占用主机端口
volumes: # 共享存储卷列表
- name: webroot # 名称,与上述对应
emptyDir: {} # 共享卷类型,空
hostPath: # 共享卷类型,本机目录
path: /etc/hosts
secret: # 共享卷类型,secret模式,一般用于密码
secretName: default-token-tf2jp # 名称
defaultMode: 420 # 权限
configMap: # 一般用于配置文件
name: nginx-conf
defaultMode: 420
3 - Deployment
虽然 ReplicaSet 可以确保在任何给定时间运行的 Pod 副本达到指定的数量,但是 Deployment(部署)是一个更高级的概念,它管理 ReplicaSet 并为 Pod 和 ReplicaSet 提供声明性更新以及许多其他有用的功能,所以建议在实际使用中,使用 Deployment 代替 ReplicaSet 。
如果在 Deployment 对象中描述了所需的状态,Deployment 控制器就会以可控制的速率将实际状态更改为期望状态。也可以在 Deployment 中创建新的 ReplicaSet,或者删除现有的 Deployment 并使用新的 Deployment 部署所用的资源。
3.1 创建 Deployment
创建一个 Deployment 文件,并命名为 dc-nginx.yaml,用于部署三个 Nginx Pod:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
示例解析:
- nginx-deployment:Deployment的名称。
- replicas:创建Pod的副本数。
- selector:定义Deployment如何找到要管理的Pod,与template的label(标签)对应。
template 字段包含以下字段:
- app:nginx使用label(标签)标记Pod
- spec:表示Pod运行一个名字为nginx的容器。
- image:运行此Pod使用的镜像
- Port:容器用于发送和接收流量的端口
使用 kubectl create 创建此 Deployment:
[root@k8s-master01 2.2.8.1]# kubectl create -f dc-nginx.yaml
deployment.apps/nginx-deployment created
使用 kubectl get 或者 kubectl describe 查看此 Deployment:
[root@k8s-master01 2.2.8.1]# kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 1 60s
其中:
- NAME:集群中Deployment的名称。
- DESIRED:应用程序副本数。
- CURRENT:当前正在运行的副本数。
- UP-TO-DATE:显示已达到期望状态的被更新的副本数。
- AVAILABLE:显示用户可以使用的应用程序副本数,当前为1,因为部分Pod仍在创建过程中。
- AGE:显示应用程序运行的时间。
查看此时 Deployment rollout 的状态:
[root@k8s-master01 2.2.8.1]# kubectl rollout status deployment/nginx-deployment
deployment "nginx-deployment" successfully rolled out
再次查看此 Deployment:
[root@k8s-master01 2.2.8.1]# kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 11m
查看此 Deployment 创建的 ReplicaSet:
[root@k8s-master01 2.2.8.1]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5c689d88bb 3 3 3 12m
查看此 Deployment 创建的 Pod:
[root@k8s-master01 2.2.8.1]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-deployment-5c689d88bb-6b95k 1/1 Running 0 13m app=nginx,pod-template-hash=5c689d88bb
nginx-deployment-5c689d88bb-9z5z2 1/1 Running 0 13m app=nginx,pod-template-hash=5c689d88bb
nginx-deployment-5c689d88bb-jc8hr 1/1 Running 0 13m app=nginx,pod-template-hash=5c689d88bb
3.2 更新 Deployment
一般对应用程序升级或者版本迭代时,会通过 Deployment 对 Pod 进行滚动更新。
假如更新 Nginx Pod 的 image 使用 nginx:1.9.1:
[root@k8s-master01 2.2.8.1]# kubectl set image deployment nginx-deployment nginx=nginx:1.9.1 --record
deployment.extensions/nginx-deployment image updated
当然也可以直接编辑 Deployment,效果相同:
[root@k8s-master01 2.2.8.1]# kubectl edit deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment edited
使用 kubectl rollout status 查看更新状态:
[root@k8s-master01 2.2.8.1]# kubectl rollout status deployment.v1.apps/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out
查看 ReplicaSet:
[root@k8s-master01 2.2.8.1]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5c689d88bb 0 0 0 34m
nginx-deployment-6987cdb55b 3 3 3 5m14s
通过 describe 查看 Deployment 的详细信息:
[root@k8s-master01 2.2.8.1]# kubectl describe deploy nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Thu, 24 Jan 2019 15:15:15 +0800
Labels: app=nginx
Annotations: deployment.kubernetes.io/revision: 2
kubernetes.io/change-cause: kubectl set image deployment nginx-deployment nginx=nginx:1.9.1 --record=true
Selector: app=nginx
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.9.1
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-6987cdb55b (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 36m deployment-controller Scaled up replica set nginx-deployment-5c689d88bb to 3
Normal ScalingReplicaSet 7m16s deployment-controller Scaled up replica set nginx-deployment-6987cdb55b to 1
Normal ScalingReplicaSet 5m18s deployment-controller Scaled down replica set nginx-deployment-5c689d88bb to 2
Normal ScalingReplicaSet 5m18s deployment-controller Scaled up replica set nginx-deployment-6987cdb55b to 2
Normal ScalingReplicaSet 4m35s deployment-controller Scaled down replica set nginx-deployment-5c689d88bb to 1
Normal ScalingReplicaSet 4m34s deployment-controller Scaled up replica set nginx-deployment-6987cdb55b to 3
Normal ScalingReplicaSet 3m30s deployment-controller Scaled down replica set nginx-deployment-5c689d88bb to 0
在describe中可以看出,第一次创建时,它创建了一个名为 nginx-deployment-5c689d88bb 的 ReplicaSet,并直接将其扩展为3个副本。更新部署时,它创建了一个新的 ReplicaSet,命名为 nginx-deployment-6987cdb55b,并将其副本数扩展为1,然后将旧的 ReplicaSet 缩小为2,这样至少可以有2个 Pod 可用,最多创建了4个 Pod。以此类推,使用相同的滚动更新策略向上和向下扩展新旧 ReplicaSet,最终新的 ReplicaSet 可以拥有3个副本,并将旧的 ReplicaSet 缩小为0。
3.3 回滚 Deployment
当新版本不稳定时,可以对其进行回滚操作,默认情况下,所有 Deployment 的 rollout 历史都保留在系统中,可以随时回滚。
假设我们又进行了几次更新:
[root@k8s-master01 2.2.8.1]# kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record
[root@k8s-master01 2.2.8.1]# kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record
使用 kubectl rollout history 查看部署历史:
[root@k8s-master01 2.2.8.1]# kubectl rollout history deployment/nginx-deployment
deployment.extensions/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment nginx-deployment nginx=nginx:1.9.1 --record=true
3 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record=true
4 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
查看 Deployment 某次更新的详细信息,使用 --revision 指定版本号:
[root@k8s-master01 2.2.8.1]# kubectl rollout history deployment.v1.apps/nginx-deployment --revision=3
deployment.apps/nginx-deployment with revision #3
Pod Template:
Labels: app=nginx
pod-template-hash=645959bf6b
Annotations: kubernetes.io/change-cause: kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record=true
Containers:
nginx:
Image: dotbalo/canary:v1
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
使用 kubectl rollout undo 回滚到上一个版本:
[root@k8s-master01 2.2.8.1]# kubectl rollout undo deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment
再次查看更新历史,发现 REVISION 5 回到了 canary:v1:
[root@k8s-master01 2.2.8.1]# kubectl rollout history deployment/nginx-deployment
deployment.extensions/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment nginx-deployment nginx=nginx:1.9.1 --record=true
4 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
5 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record=true
使用 --to-revision 参数回到指定版本:
[root@k8s-master01 2.2.8.1]# kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment.extensions/nginx-deployment
3.4 扩展 Deployment
当公司访问量变大,三个 Pod 已无法支撑业务时,可以对其进行扩展。
使用 kubectl scale 动态调整 Pod 的副本数,比如增加 Pod 为5个:
[root@k8s-master01 2.2.8.1]# kubectl scale deployment.v1.apps/nginx-deployment --replicas=5
deployment.apps/nginx-deployment scaled
查看 Pod,此时 Pod 已经变成了5个:
[root@k8s-master01 2.2.8.1]# kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-deployment-5f89547d9c-5r56b 1/1 Running 0 90s
nginx-deployment-5f89547d9c-htmn7 1/1 Running 0 25s
nginx-deployment-5f89547d9c-nwxs2 1/1 Running 0 99s
nginx-deployment-5f89547d9c-rpwlg 1/1 Running 0 25s
nginx-deployment-5f89547d9c-vlr5p 1/1 Running 0 95s
3.5 暂停和恢复 Deployment 更新
Deployment 支持暂停更新,用于对 Deployment 进行多次修改操作。
使用 kubectl rollout pause 暂停 Deployment 更新:
[root@k8s-master01 2.2.8.1]# kubectl rollout pause deployment/nginx-deployment
deployment.extensions/nginx-deployment paused
然后对 Deployment 进行相关更新操作,比如更新镜像,然后对其资源进行限制:
[root@k8s-master01 2.2.8.1]# kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1
deployment.apps/nginx-deployment image updated
[root@k8s-master01 2.2.8.1]# kubectl set resources deployment.v1.apps/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
deployment.apps/nginx-deployment resource requirements updated
通过 rollout history 可以看到没有新的更新:
[root@k8s-master01 2.2.8.1]# kubectl rollout history deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
5 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record=true
7 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
8 kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
使用 kubectl rollout resume 恢复 Deployment 更新:
[root@k8s-master01 2.2.8.1]# kubectl rollout resume deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment resumed
可以查看到恢复更新的 Deployment 创建了一个新的 RS(复制集):
[root@k8s-master01 2.2.8.1]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-57895845b8 5 5 4 11s
可以查看 Deployment 的 image(镜像)已经变为 nginx:1.9.1:
[root@k8s-master01 2.2.8.1]# kubectl describe deploy nginx-deployment
Name: nginx-deployment
Namespace: default
CreationTimestamp: Thu, 24 Jan 2019 15:15:15 +0800
Labels: app=nginx
Annotations: deployment.kubernetes.io/revision: 9
kubernetes.io/change-cause: kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
Selector: app=nginx
Replicas: 5 desired | 5 updated | 5 total | 5 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.9.1
Port: 80/TCP
Host Port: 0/TCP
3.6 更新 Deployment 的注意事项
清理策略:
在默认情况下,revision 保留10个旧的 ReplicaSet,其余的将在后台进行垃圾回收,可以在 .spec.revisionHistoryLimit 设置保留 ReplicaSet 的个数。当设置为0时,不保留历史记录。
更新策略:
-
.spec.strategy.type==Recreate,表示重建,先删掉旧的 Pod 再创建新的 Pod。
-
.spec.strategy.type==RollingUpdate,表示滚动更新,可以指定 maxUnavailable 和 maxSurge 来控制滚动更新过程。
-
.spec.strategy.rollingUpdate.maxUnavailable,指定在回滚更新时最大不可用的 Pod 数量,可选字段,默认为25%,可以设置为数字或百分比,如果 maxSurge 为0,则该值不能为0。
-
.spec.strategy.rollingUpdate.maxSurge 可以超过期望值的最大 Pod 数,可选字段,默认为25%,可以设置成数字或百分比,如果 maxUnavailable 为0,则该值不能为0。
4 - StatefulSet
StatefulSet(有状态集)常用于部署有状态的且需要有序启动的应用程序。
4.1 StatefulSet 的基本概念
StatefulSet 主要用于管理有状态应用程序的工作负载API对象。比如在生产环境中,可以部署 ElasticSearch 集群、MongoDB 集群或者需要持久化的 RabbitMQ集群、Redis集群、Kafka集群和ZooKeeper集群等。
而 StatefulSet 创建的 Pod 一般使用 Headless Service(无头服务)进行通信,和普通的Service的区别在于 Headless Service 没有ClusterIP,它使用的是 Endpoint 进行互相通信,Headless 一般的格式为:
statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local.
说明:
- serviceName为 Headless Service 的名字。
- 0..N-1为Pod所在的序号,从0开始到N-1。
- statefulSetName 为 StatefulSet 的名字。
- namespace 为服务所在的命名空间。
- .cluster.local为 Cluster Domain(集群域)。
比如,一个 Redis 主从架构,Slave 连接 Master 主机配置就可以使用不会更改的 Master 的 Headless Service,例如 Redis 从节点(Slave)配置文件如下:
port 6379
slaveofredis-sentinel-master-ss-0.redis-sentinel-master-ss.public-service.svc.cluster.local 6379
tcp-backlog 511
timeout 0
tcp-keepalive 0
……
其中,redis-sentinel-master-ss-0.redis-sentinel-master-ss.public-service.svc.cluster.local 是 Redis Master 的 Headless Service。
4.2 StatefulSet 组件
定义一个简单的 StatefulSet 的示例如下:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
其中:
- kind: Service 定义了一个名字为 Nginx 的 Headless Service,创建的 Service 格式为 nginx-0.nginx.default.svc.cluster.local,其他的类似,因为没有指定 Namespace(命名空间),所以默认部署在 default 命名空间下。
- kind: StatefulSet 定义了一个名字为 web 的 StatefulSet,replicas 表示部署 Pod 的副本数,本实例为2。
4.3 创建 StatefulSet
创建 StatefulSet:
[root@k8s-master01 2.2.7]# kubectl create -f sts-web.yaml
service/nginx created
statefulset.apps/web created
[root@k8s-master01 2.2.7]# kubectl get sts
NAME DESIRED CURRENT AGE
web 2 2 12s
[root@k8s-master01 2.2.7]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d2h
nginx ClusterIP None <none> 80/TCP 16s
[root@k8s-master01 2.2.7]# kubectl get po -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2m5s
web-1 1/1 Running 0 115s
4.4 StatefulSet 扩容和缩容
和 Deployment 类似,可以通过更新 replicas 字段扩容/缩容 StatefulSet,也可以使用 kubectl scale 或者 kubectl patch 来扩容/缩容一个 StatefulSet。
4.4.1 扩容
将上述创建的 sts 副本增加到5个(扩容之前必须保证有创建完成的静态PV,动态PV和emptyDir):
[root@k8s-master01 2.2.7]# kubectl scale sts web --replicas=5
statefulset.apps/web scaled
查看 Pod 状态:
[root@k8s-master01 2.2.7]# kubectl get po
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2m58s
web-1 1/1 Running 0 2m48s
web-2 1/1 Running 0 116s
web-3 1/1 Running 0 79s
web-4 1/1 Running 0 53s
也可使用以下命令动态查看:
kubectl get pods -w -l app=nginx
4.4.2 缩容
在一个终端动态查看:
[root@k8s-master01 2.2.7]# kubectl get pods -w -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4m37s
web-1 1/1 Running 0 4m27s
web-2 1/1 Running 0 3m35s
web-3 1/1 Running 0 2m58s
web-4 1/1 Running 0 2m32s
在另一个终端将副本数改为3:
[root@k8s-master01 ~]# kubectl patch sts web -p '{"spec":{"replicas":3}}'
statefulset.apps/web patched
此时可以看到第一个终端显示 web-4 和 web-3 的 Pod 正在被删除(或终止):
[root@k8s-master01 2.2.7]# kubectl get pods -w -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 4m37s
web-1 1/1 Running 0 4m27s
web-2 1/1 Running 0 3m35s
web-3 1/1 Running 0 2m58s
web-4 1/1 Running 0 2m32s
web-0 1/1 Running 0 5m8s
web-0 1/1 Running 0 5m11s
web-4 1/1 Terminating 0 3m36s
web-4 0/1 Terminating 0 3m38s
web-4 0/1 Terminating 0 3m47s
web-4 0/1 Terminating 0 3m47s
web-3 1/1 Terminating 0 4m13s
web-3 0/1 Terminating 0 4m14s
web-3 0/1 Terminating 0 4m22s
web-3 0/1 Terminating 0 4m22s
4.5 更新策略
4.5.1 On Delete 策略
OnDelete 更新策略实现了传统(1.7版本之前)的行为,它也是默认的更新策略。当我们选择这个更新策略并修改 StatefulSet 的 .spec.template 字段时,StatefulSet 控制器不会自动更新 Pod,我们必须手动删除 Pod 才能使控制器创建新的 Pod。
4.5.2 RollingUpdate 策略
RollingUpdate(滚动更新)更新策略会更新一个 StatefulSet 中所有的 Pod,采用与序号索引相反的顺序进行滚动更新。
比如 Patch 一个名称为web的 StatefulSet 来执行 RollingUpdate 更新:
[root@k8s-master01 2.2.7]# kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}'
statefulset.apps/web patched
查看更改后的 StatefulSet:
[root@k8s-master01 2.2.7]# kubectl get sts web -o yaml | grep -A 1 "updateStrategy"
updateStrategy:
type: RollingUpdate
然后改变容器的镜像进行滚动更新:
[root@k8s-master01 2.2.7]# kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"dotbalo/canary:v1"}]'
statefulset.apps/web patched
如上所述,StatefulSet 里的 Pod 采用和序号相反的顺序更新。在更新下一个 Pod 前,StatefulSet 控制器会终止每一个 Pod 并等待它们变成 Running 和 Ready 状态。在当前顺序变成 Running 和 Ready 状态之前,StatefulSet 控制器不会更新下一个 Pod,但它仍然会重建任何在更新过程中发生故障的 Pod,使用它们当前的版本。已经接收到请求的 Pod 将会被恢复为更新的版本,没有收到请求的 Pod 则会被恢复为之前的版本。
在更新过程中可以使用 kubectl rollout status sts/<name>
来查看滚动更新的状态:
[root@k8s-master01 2.2.7]# kubectl rollout status sts/web
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 1 pods at revision web-56b5798f76...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 2 pods at revision web-56b5798f76...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
statefulset rolling update complete 3 pods at revision web-56b5798f76...
查看更新后的镜像:
[root@k8s-master01 2.2.7]# for p in 0 1 2; do kubectl get po web-$p --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
dotbalo/canary:v1
dotbalo/canary:v1
dotbalo/canary:v1
4.5.3 分段更新
StatefulSet 可以使用 RollingUpdate 更新策略的 partition 参数来分段更新一个 StatefulSet。分段更新将会使 StatefulSet 中其余的所有 Pod(序号小于分区)保持当前版本,只更新序号大于等于分区的 Pod,利用此特性可以简单实现金丝雀发布(灰度发布)或者分阶段推出新功能等。注:金丝雀发布是指在黑与白之间能够平滑过渡的一种发布方式。
比如我们定义一个分区"partition":3,可以使用 patch 直接对 StatefulSet 进行设置:
# kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
statefulset "web" patched
然后再次patch改变容器的镜像:
# kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"k8s.gcr.io/nginx-slim:0.7"}]'
statefulset "web" patched
删除Pod触发更新:
kubectl delete po web-2
pod "web-2" deleted
此时,因为 Pod web-2 的序号小于分区3,所以 Pod 不会被更新,还是会使用以前的容器恢复 Pod。
将分区改为2,此时会自动更新 web-2(因为之前更改了更新策略),但是不会更新 web-0 和 web-1:
# kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset "web" patched
按照上述方式,可以实现分阶段更新,类似于灰度/金丝雀发布。查看最终的结果如下:
[root@k8s-master01 2.2.7]# for p in 0 1 2; do kubectl get po web-$p --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
dotbalo/canary:v1
dotbalo/canary:v1
dotbalo/canary:v2
4.6 删除 StatefulSet
删除 StatefulSet 有两种方式,即级联删除和非级联删除。使用非级联方式删除 StatefulSet 时,StatefulSet 的 Pod 不会被删除;使用级联删除时,StatefulSet 和它的 Pod 都会被删除。
4.6.1 非级联删除
使用 kubectl delete sts xxx
删除 StatefulSet 时,只需提供 --cascade=false 参数,就会采用非级联删除,此时删除 StatefulSet 不会删除它的 Pod:
[root@k8s-master01 2.2.7]# kubectl get po
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 16m
web-1 1/1 Running 0 16m
web-2 1/1 Running 0 11m
[root@k8s-master01 2.2.7]# kubectl delete statefulset web --cascade=false
statefulset.apps "web" deleted
[root@k8s-master01 2.2.7]# kubectl get sts
No resources found.
[root@k8s-master01 2.2.7]# kubectl get po
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 16m
web-1 1/1 Running 0 16m
web-2 1/1 Running 0 11m
由于此时删除了 StatefulSet,因此单独删除 Pod 时,不会被重建:
[root@k8s-master01 2.2.7]# kubectl get po
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 16m
web-1 1/1 Running 0 16m
web-2 1/1 Running 0 11m
[root@k8s-master01 2.2.7]# kubectl delete po web-0
pod "web-0" deleted
[root@k8s-master01 2.2.7]# kubectl get po
NAME READY STATUS RESTARTS AGE
web-1 1/1 Running 0 18m
web-2 1/1 Running 0 12m
当再次创建此 StatefulSet 时,web-0 会被重新创建,web-1 由于已经存在而不会被再次创建,因为最初此 StatefulSet 的 replicas 是2,所以 web-2 会被删除,如下(忽略AlreadyExists错误):
[root@k8s-master01 2.2.7]# kubectl create -f sts-web.yaml
statefulset.apps/web created
Error from server (AlreadyExists): error when creating "sts-web.yaml": services "nginx" already exists
[root@k8s-master01 2.2.7]# kubectl get po
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 32s
web-1 1/1 Running 0 19m
4.6.2 级联删除
省略 --cascade=false 参数即为级联删除:
[root@k8s-master01 2.2.7]# kubectl delete statefulset web
statefulset.apps "web" deleted
[root@k8s-master01 2.2.7]# kubectl get po
No resources found.
也可以使用 -f 参数直接删除 StatefulSet 和 Service(此文件将sts和svc写在了一起):
[root@k8s-master01 2.2.7]# kubectl delete -f sts-web.yaml
service "nginx" deleted
Error from server (NotFound): error when deleting "sts-web.yaml": statefulsets.apps "web" not found
5 - DaemonSet
DaemonSet(守护进程集)和守护进程类似,它在符合匹配条件的节点上均部署一个 Pod。
5.1 什么是 DaemonSet
DaemonSet 确保全部(或者某些)节点上运行一个 Pod 副本。当有新节点加入集群时,也会为它们新增一个 Pod。当节点从集群中移除时,这些 Pod 也会被回收,删除 DaemonSet 将会删除它创建的所有 Pod。
使用 DaemonSet 的一些典型用法:
- 运行集群存储daemon(守护进程),例如在每个节点上运行Glusterd、Ceph等。
- 在每个节点运行日志收集daemon,例如Fluentd、Logstash。
- 在每个节点运行监控daemon,比如Prometheus Node Exporter、Collectd、Datadog代理、New Relic代理或 Ganglia gmond。
5.2 编写 DaemonSet 规范
创建一个 DaemonSet 的内容大致如下,比如创建一个 fluentd 的 DaemonSet:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-es-v2.0.4
namespace: logging
labels:
k8s-app: fluentd-es
version: v2.0.4
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
spec:
selector:
matchLabels:
k8s-app: fluentd-es
version: v2.0.4
template:
metadata:
labels:
k8s-app: fluentd-es
kubernetes.io/cluster-service: "true"
version: v2.0.4
# This annotation ensures that fluentd does not get evicted if the node
# supports critical pod annotation based priority scheme.
# Note that this does not guarantee admission on the nodes (#40573).
annotations:
scheduler.alpha.kubernetes.io/critical-pod: ''
seccomp.security.alpha.kubernetes.io/pod: 'docker/default'
spec:
serviceAccountName: fluentd-es
containers:
- name: fluentd-es
image: k8s.gcr.io/fluentd-elasticsearch:v2.0.4
env:
- name: FLUENTD_ARGS
value: --no-supervisor -q
resources:
limits:
memory: 500Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: config-volume
mountPath: /etc/fluent/config.d
nodeSelector:
beta.kubernetes.io/fluentd-ds-ready: "true"
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: config-volume
configMap:
name: fluentd-es-config-v0.1.4
5.2.1 必需字段
和其他所有 Kubernetes 配置一样,DaemonSet 需要 apiVersion、kind 和 metadata 字段,同时也需要一个 .spec 配置段。
5.2.2 Pod模板
.spec 唯一需要的字段是 .spec.template。.spec.template 是一个 Pod 模板,它与 Pod 具有相同的配置方式,但它不具有 apiVersion 和 kind 字段。
除了 Pod 必需的字段外,在 DaemonSet 中的 Pod 模板必须指定合理的标签。
在 DaemonSet 中的 Pod 模板必须具有一个 RestartPolicy,默认为 Always。
5.2.3 Pod Selector
.spec.selector 字段表示 Pod Selector,它与其他资源的 .spec.selector 的作用相同。
.spec.selector 表示一个对象,它由如下两个字段组成:
- matchLabels,与ReplicationController的.spec.selector的作用相同,用于匹配符合条件的Pod。
- matchExpressions,允许构建更加复杂的Selector,可以通过指定key、value列表以及与key和value列表相关的操作符。
如果上述两个字段都指定时,结果表示的是 AND关系(逻辑与的关系)。
.spec.selector 必须与 .spec.template.metadata.labels 相匹配。如果没有指定,默认是等价的,如果它们的配置不匹配,则会被API拒绝。
5.2.4 指定节点部署Pod
如果指定了 .spec.template.spec.nodeSelector,DaemonSet Controller 将在与 Node Selector(节点选择器)匹配的节点上创建 Pod,比如部署在磁盘类型为ssd的节点上(需要提前给节点定义标签Label):
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
提示:
Node Selector 同样适用于其他 Controller。
5.3 创建 DaemonSet
在生产环境中,公司业务的应用程序一般无须使用 DaemonSet 部署,一般情况下只有像 Fluentd(日志收集)、Ingress(集群服务入口)、Calico(集群网络组件)、Node-Exporter(监控数据采集)等才需要使用 DaemonSet 部署到每个节点。本节只演示 DaemonSet 的使用。
比如创建一个 nginx ingress(文件地址:https://github.com/dotbalo/k8s/blob/master/nginx-ingress/ingress.yaml):
# kubectl create -f nginx-ds.yaml
namespace/ingress-nginx created
configmap/nginx-configuration created
configmap/tcp-services created
configmap/udp-services created
serviceaccount/nginx-ingress-serviceaccount created
clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
role.rbac.authorization.k8s.io/nginx-ingress-role created
rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
daemonset.extensions/nginx-ingress-controller created
此时会在每个节点创建一个 Pod:
# kubectl get po -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-fjkf2 1/1 Running 0 44s
nginx-ingress-controller-gfmcv 1/1 Running 0 44s
nginx-ingress-controller-j89qc 1/1 Running 0 44s
nginx-ingress-controller-sqsk2 1/1 Running 0 44s
nginx-ingress-controller-tgdt6 1/1 Running 0 44s
[root@k8s-master01 2.2.8]# kubectl get po -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE
IP NODE NOMINATED NODE
nginx-ingress-controller-fjkf2 1/1 Running 0 50s 192.168.20.30 k8s-node01 <none>
nginx-ingress-controller-gfmcv 1/1 Running 0 50s 192.168.20.21 k8s-master02 <none>
nginx-ingress-controller-j89qc 1/1 Running 0 50s 192.168.20.22 k8s-master03 <none>
nginx-ingress-controller-sqsk2 1/1 Running 0 50s 192.168.20.31 k8s-node02 <none>
nginx-ingress-controller-tgdt6 1/1 Running 0 50s 192.168.20.20 k8s-master01 <none>
5.4 更新和回滚 DaemonSet
如果修改了节点标签(Label),DaemonSet 将立刻向新匹配上的节点添加 Pod,同时删除不能匹配的节点上的 Pod。
在 Kubernetes 1.6 以后的版本中,可以在 DaemonSet 上执行滚动更新,未来的 Kubernetes 版本将支持节点的可控更新。
DaemonSet 滚动更新可参考:https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/。
DaemonSet 更新策略和 StatefulSet 类似,也有 OnDelete 和 RollingUpdate 两种方式。
查看上一节创建的 DaemonSet 更新方式:
# kubectl get ds/nginx-ds -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}'
RollingUpdate
提示
如果是其他 DaemonSet,请确保更新策略是 RollingUpdate(滚动更新)。
5.4.1 命令式更新
kubectl edit ds/<daemonset-name>
kubectl patch ds/<daemonset-name> -p=<strategic-merge-patch>
5.4.2 更新镜像
kubectl set image ds/<daemonset-name><container-name>=<container-new-image>--record=true
5.4.3 查看更新状态
kubectl rollout status ds/<daemonset-name>
5.4.4 列出所有修订版本
kubectl rollout history daemonset <daemonset-name>
5.4.5 回滚到指定revision
kubectl rollout undo daemonset <daemonset-name> --to-revision=<revision>
DaemonSet 的更新和回滚与 Deployment 类似,此处不再演示。
6 - Label 和 Selector
当 Kubernetes 对系统的任何API对象如 Pod 和节点进行“分组”时,会对其添加 Label(key=value形式的“键-值对”)用以精准地选择对应的API对象。而 Selector(标签选择器)则是针对匹配对象的查询方法。注:键-值对就是key-value pair。
例如,常用的标签 tier 可用于区分容器的属性,如frontend、backend;或者一个 release_track 用于区分容器的环境,如canary、production等。
6.1 定义 Label
应用案例:
公司与xx银行有一条专属的高速光纤通道,此通道只能与 192.168.7.0 网段进行通信,因此只能将与xx银行通信的应用部署到 192.168.7.0 网段所在的节点上,此时可以对节点进行 Label(即加标签):
[root@k8s-master01 ~]# kubectl label node k8s-node02 region=subnet7
node/k8s-node02 labeled
然后,可以通过 Selector 对其筛选:
[root@k8s-master01 ~]# kubectl get no -l region=subnet7
NAME STATUS ROLES AGE VERSION
k8s-node02 Ready <none> 3d17h v1.17.3
最后,在 Deployment 或其他控制器中指定将 Pod 部署到该节点:
containers:
......
dnsPolicy: ClusterFirst
nodeSelector:
region: subnet7
restartPolicy: Always
......
也可以用同样的方式对 Service 进行 Label:
[root@k8s-master01 ~]# kubectl label svc canary-v1 -n canary-production env=canary version=v1
service/canary-v1 labeled
查看 Labels:
[root@k8s-master01 ~]# kubectl get svc -n canary-production --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
canary-v1 ClusterIP 10.110.253.62 <none> 8080/TCP 24h env=canary,version=v1
还可以查看所有 Version 为 v1 的 svc:
[root@k8s-master01 canary]# kubectl get svc --all-namespaces -l version=v1
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
canary-production canary-v1 ClusterIP 10.110.253.62 <none> 8080/TCP 25h
其他资源的 Label 方式相同。
6.2 Selector 条件匹配
Selector 主要用于资源的匹配,只有符合条件的资源才会被调用或使用,可以使用该方式对集群中的各类资源进行分配。
假如对 Selector 进行条件匹配,目前已有的 Label 如下:
[root@k8s-master01 ~]# kubectl get svc --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
details ClusterIP 10.99.9.178 <none> 9080/TCP 45h app=details
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d19h component=apiserver,provider=kubernetes
nginx ClusterIP 10.106.194.137 <none> 80/TCP 2d21h app=productpage,version=v1
nginx-v2 ClusterIP 10.108.176.132 <none> 80/TCP 2d20h <none>
productpage ClusterIP 10.105.229.52 <none> 9080/TCP 45h app=productpage,tier=frontend
ratings ClusterIP 10.96.104.95 <none> 9080/TCP 45h app=ratings
reviews ClusterIP 10.102.188.143 <none> 9080/TCP 45h app=reviews
选择app为 reviews 或者 productpage 的 svc:
[root@k8s-master01 ~]# kubectl get svc -l 'app in (details, productpage)' --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
details ClusterIP 10.99.9.178 <none> 9080/TCP 45h app=details
nginx ClusterIP 10.106.194.137 <none> 80/TCP 2d21h app=productpage,version=v1
productpage ClusterIP 10.105.229.52 <none> 9080/TCP 45h app=productpage,tier=frontend
选择app为 productpage 或 reviews 但不包括 version=v1 的 svc:
[root@k8s-master01 ~]# kubectl get svc -l version!=v1,'app in (details, productpage)' --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
details ClusterIP 10.99.9.178 <none> 9080/TCP 45h app=details
productpage ClusterIP 10.105.229.52 <none> 9080/TCP 45h app=productpage,tier=frontend
选择 label key 名为 app 的 svc:
[root@k8s-master01 ~]# kubectl get svc -l app --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
details ClusterIP 10.99.9.178 <none> 9080/TCP 45h app=details
nginx ClusterIP 10.106.194.137 <none> 80/TCP 2d21h app=productpage,version=v1
productpage ClusterIP 10.105.229.52 <none> 9080/TCP 45h app=productpage,tier=frontend
ratings ClusterIP 10.96.104.95 <none> 9080/TCP 45h app=ratings
reviews ClusterIP 10.102.188.143 <none> 9080/TCP 45h app=reviews
6.3 修改标签(Label)
在实际使用中,Label 的更改是经常发生的事情,可以使用 --overwrite 参数修改标签。
修改标签,比如将 version=v1 改为 version=v2:
[root@k8s-master01 canary]# kubectl get svc -n canary-production --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
canary-v1 ClusterIP 10.110.253.62 <none> 8080/TCP 26h env=canary,version=v1
[root@k8s-master01 canary]# kubectl label svc canary-v1 -n canary-production version=v2 --overwrite
service/canary-v1 labeled
[root@k8s-master01 canary]# kubectl get svc -n canary-production --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
canary-v1 ClusterIP 10.110.253.62 <none> 8080/TCP 26h env=canary,version=v2
6.4 删除标签(Label)
删除标签,比如删除 version:
[root@k8s-master01 canary]# kubectl label svc canary-v1 -n canary-production version-
service/canary-v1 labeled
[root@k8s-master01 canary]# kubectl get svc -n canary-production --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
canary-v1 ClusterIP 10.110.253.62 <none> 8080/TCP 26h env=canary