7 - Service
Service 主要用于 Pod 之间的通信,对于 Pod 的IP地址而言,Service 是提前定义好并且是不变的资源类型。
7.1 基本概念
Kubernetes Pod 具有生命周期的概念,它可以被创建、删除、销毁,一旦被销毁就意味着生命周期的结束。通过 ReplicaSet 能够动态地创建和销毁 Pod,例如进行扩缩容和执行滚动升级。每个 Pod 都会获取到它自己的IP地址,但是这些IP地址不总是稳定和可依赖的,这样就会导致一个问题:在 Kubernetes 集群中,如果一组 Pod(比如后端的 Pod)为其他 Pod(比如前端的 Pod)提供服务,那么如果它们之间使用 Pod 的IP地址进行通信,在 Pod 重建后,将无法再进行连接。
为了解决上述问题,Kubernetes 引用了 Service 这样一种抽象概念:逻辑上的一组 Pod,即一种可以访问 Pod 的策略——通常称为微服务。这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector(标签选择器)实现的。
举个例子,有一个用作图片处理的 backend(后端),运行了3个副本,这些副本是可互换的,所以 frontend(前端)不需要关心它们调用了哪个 backend 副本,然而组成这一组 backend 程序的 Pod 实际上可能会发生变化,即便这样 frontend 也没有必要知道,而且也不需要跟踪这一组 backend 的状态,因为 Service 能够解耦这种关联。
对于 Kubernetes 集群中的应用,Kubernetes 提供了简单的 Endpoints API,只要 Service 中的一组 Pod 发生变更,应用程序就会被更新。对非 Kubernetes 集群中的应用,Kubernetes 提供了基于VIP的网桥的方式访问 Service,再由 Service 重定向到 backend Pod。
7.2 定义 Service
一个 Service 在 Kubernetes 中是一个 REST 对象,和 Pod类似。像所有 REST 对象一样,Service 的定义可以基于POST方式,请求 APIServer 创建新的实例。例如,假定有一组 Pod,它们暴露了9376端口,同时具有 app=MyApp 标签。此时可以定义 Service 如下:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
上述配置创建一个名为my-service的 Service 对象,它会将请求代理到TCP端口为9376并且具有标签 app=MyApp 的Pod上。这个 Service 会被分配一个IP地址,通常称为 ClusterIP,它会被服务的代理使用。
需要注意的是,Service 能够将一个接收端口映射到任意的 targetPort。默认情况下,targetPort 将被设置为与Port字段相同的值。targetPort 可以设置为一个字符串,引用 backend Pod 的一个端口的名称。
7.3 定义没有 Selector 的 Service
Service 抽象了该如何访问 Kubernetes Pod,但也能够抽象其他类型的 backend,例如:
- 希望在生产环境中访问外部的数据库集群。
- 希望Service指向另一个NameSpace中或其他集群中的服务。
- 正在将工作负载转移到Kubernetes集群,和运行在Kubernetes集群之外的backend。
在任何这些场景中,都能定义没有 Selector 的 Service:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
由于这个 Service 没有 Selector,就不会创建相关的 Endpoints 对象,可以手动将 Service 映射到指定的 Endpoints:
kind: Endpoints
apiVersion: v1
metadata:
name: my-service
subsets:
- addresses:
- ip: 1.2.3.4
ports:
- port: 9376
注意
Endpoint IP 地址不能是 loopback(127.0.0.0/8)、link-local(169.254.0.0/16)或者link-local 多播地址(224.0.0.0/24)。
访问没有Selector的Service与有Selector的Service的原理相同。请求将被路由到用户定义的Endpoint,该示例为1.2.3.4:9376。
ExternalName Service是Service的特例,它没有Selector,也没有定义任何端口和Endpoint,它通过返回该外部服务的别名来提供服务。
比如当查询主机 my-service.prod.svc 时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME记录:
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
7.4 多端口 Service
在许多情况下,Service 可能需要暴露多个端口,对于这种情况 Kubernetes 支持 Service 定义多个端口,但使用多个端口时,必须提供所有端口的名称,例如:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
7.5 发布服务/服务类型
对于应用程序的某些部分(例如前端),一般要将服务公开到集群外部供用户访问。这种情况下都是用 Ingress 通过域名进行访问。
Kubernetes ServiceType(服务类型)主要包括以下几种:
- ClusterIP:在集群内部使用,默认值,只能从集群中访问。
- NodePort:在所有节点上打开一个端口,此端口可以代理至后端Pod,可以通过NodePort从集群外部访问集群内的服务,格式为NodeIP:NodePort。
- LoadBalancer:使用云提供商的负载均衡器公开服务,成本较高。
- ExternalName:通过返回定义的CNAME别名,没有设置任何类型的代理,需要1.7或更高版本kube-dns支持。
以 NodePort 为例。如果将type字段设置为 NodePort,则 Kubernetes 将从 --service-node-port-range 参数指定的范围(默认为30000-32767)中自动分配端口,也可以手动指定 NodePort,并且每个节点将代理该端口到 Service。
一般格式如下:
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kube-system
spec:
type: NodePort
ports:
- port: 443
targetPort: 8443
nodePort: 30000
selector:
k8s-app: kubernetes-dashboard
常用的服务访问是 NodePort 和 Ingress,其他服务访问方式详情参看以下网址:
https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types
8 - Ingress
Ingress 为 Kubernetes 集群中的服务提供了入口,可以提供负载均衡、SSL终止和基于名称的虚拟主机,在生产环境中常用的 Ingress 有Treafik、Nginx、HAProxy、Istio等。
8.1 基本概念
在 Kubernetesv 1.1 版中添加的 Ingress 用于从集群外部到集群内部 Service 的HTTP和HTTPS路由,流量从Internet到 Ingress 再到 Services 最后到 Pod 上,通常情况下,Ingress 部署在所有的 Node 节点上。
Ingress 可以配置提供服务外部访问的URL、负载均衡、终止SSL,并提供基于域名的虚拟主机。但 Ingress 不会暴露任意端口或协议。
8.2 创建一个 Ingress
创建一个简单的 Ingress 如下:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: simple-fanout-example
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
backend:
serviceName: service1
servicePort: 4200
- path: /bar
backend:
serviceName: service2
servicePort: 8080
上述host定义该 Ingress 的域名,将其解析至任意 Node 上即可访问。
- 如果访问的是foo.bar.com/foo,则被转发到service1的4200端口。
- 如果访问的是foo.bar.com/bar,则被转发到service2的8080端口。
8.2.1 Ingress Rules
- host:可选,一般都会配置对应的域名。
- path:每个路径都有一个对应的serviceName和servicePort,在流量到达服务之前,主机和路径都会与传入请求的内容匹配。
- backend:描述Service和Port的组合。对Ingress匹配主机和路径的HTTP与HTTPS请求将被发送到对应的后端。
8.2.2 默认后端
没有匹配到任何规则的流量将被发送到默认后端。默认后端通常是 Ingress Controller 的配置选项,并未在 Ingress 资源中指定。
8.3 Ingress 类型
8.3.1 单域名
单个域名匹配多个path到不同的 service:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: simple-fanout-example
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
backend:
serviceName: service1
servicePort: 4200
- path: /bar
backend:
serviceName: service2
servicePort: 8080
此时,访问foo.bar.com/foo到 service1 的4200。访问foo.bar.com/bar到 service2 的8080。
8.3.2 多域名
基于域名的虚拟主机支持将HTTP流量路由到同一IP地址的多个主机名:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
serviceName: service1
servicePort: 80
- host: bar.foo.com
http:
paths:
- backend:
serviceName: service2
servicePort: 80
此时,访问foo.bar.com到 service1,访问bar.foo.com到 service2。
8.3.3 基于TLS的 Ingress
首先创建证书,生产环境的证书为公司购买的证书:
kubectl -n default create secret tls nginx-test-tls --key=tls.key --cert=tls.crt
定义 Ingress(此示例为Traefik,nginx-ingress将traefik改为nginx即可):
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginx-https-test
namespace: default
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: traefix-test.com
http:
paths:
- backend:
serviceName: nginx-svc
servicePort: 80
tls:
- secretName: nginx-test-tls
9 - HPA
9.1 什么是 HPA
HPA(Horizontal Pod Autoscaler,水平 Pod 自动伸缩器)可根据观察到的CPU、内存使用率或自定义度量标准来自动扩展或缩容 Pod 的数量。HPA 不适用于无法缩放的对象,比如 DaemonSet。
HPA 控制器会定期调整 RC 或 Deployment 的副本数,以使观察到的平均CPU利用率与用户指定的目标相匹配。
HPA 需要 metrics-server(项目地址:https://github.com/kubernetes-incubator/metrics-server)获取度量指标,由于在高可用集群安装中已经安装了 metrics-server,所以本节的实践部分无须再次安装。
9.2 HPA 实践
在生产环境中,总会有一些意想不到的事情发生,比如公司网站流量突然升高,此时之前创建的 Pod 已不足以撑住所有的访问,而运维人员也不可能24小时守着业务服务,这时就可以通过配置 HPA,实现负载过高的情况下自动扩容 Pod 副本数以分摊高并发的流量,当流量恢复正常后,HPA 会自动缩减 Pod 的数量。
本节将测试实现一个Web服务器的自动伸缩特性,具体步骤如下:
首先启动一个Nginx服务:
[root@k8s-master01 ~]#kubectl run nginx-server --requests=cpu=10m --image=nginx --port=80
service/php-apache created
deployment.apps/php-apache created
临时开启nginx-server的端口,实际使用时需要定义 service:
kubectl expose deployment nginx-server --port=80
使用 kubectl autoscale 创建 HPA:
[root@k8s-master01 ~]# kubectl autoscale deployment nginx-server --cpu-percent=10 --min=1 --max=10
此 HPA 将根据CPU的使用率自动增加和减少副本数量,上述设置的是CPU使用率超过10%(--cpu-percent参数指定)即会增加 Pod 的数量,以保持所有 Pod 的平均CPU利用率为10%,允许最大的 Pod 数量为10(--max),最少的 Pod 数为1(--min)。
查看当前 HPA 状态,因为未对其发送任何请求,所以当前CPU使用率为0%:
[root@k8s-master01 metric-server]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-server Deployment/nginx-server 0%/10% 1 10 1 5m
查看当前Nginx的 Service 地址:
[root@k8s-master01 ~]# kubectl get service -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d
nginx-server ClusterIP 10.108.160.23 <none> 80/TCP 5m
增加负载:
[root@k8s-master01 ~]# while true; do wget -q -O- http://10.108.160.23 > /dev/null; done
1分钟左右再次查看 HPA:
[root@k8s-master01 metric-server]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-server Deployment/nginx-server 540%/10% 1 10 1 15m
再次查看 Pod,可以看到nginx-server的 Pod 已经在扩容阶段:
[root@k8s-master01 metric-server]# kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-server-589c8db585-5cbxl 0/1 ContainerCreating 0 <invalid>
nginx-server-589c8db585-7whl8 1/1 Running 0 <invalid>
nginx-server-589c8db585-cv4hs 1/1 Running 0 <invalid>
nginx-server-589c8db585-m5dn6 0/1 ContainerCreating 0 <invalid>
nginx-server-589c8db585-sxbfm 1/1 Running 0 19m
nginx-server-589c8db585-xbctd 0/1 ContainerCreating 0 <invalid>
nginx-server-589c8db585-xffs9 1/1 Running 0 <invalid>
nginx-server-589c8db585-xlb8s 0/1 ContainerCreating 0 <invalid>
在增加负荷的终端,按Ctrl+C键终止访问。
停止1分钟后再次查看 HPA 和 deployment,此时副本已经恢复为1:
[root@k8s-master01 metric-server]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-server Deployment/nginx-server 0%/10% 1 10 10 20m
10 - Volumes
Container(容器)中的磁盘文件是短暂的,当容器崩溃时,kubelet 会重新启动容器,但最初的文件将丢失,Container 会以最干净的状态启动。另外,当一个 Pod 运行多个Container时,各个容器可能需要共享一些文件。Kubernetes Volume 可以解决这两个问题。
10.1 卷的类型
Kubernetes 支持的卷的类型有很多,以下为常用的卷。
ConfigMap
ConfigMap 卷也可以作为 volume 使用,存储在 ConfigMap 中的数据可以通过 ConfigMap 类型的卷挂载到 Pod 中,然后使用该 ConfigMap 中的数据。引用 ConfigMap 对象时,只需要在 volume 中引用 ConfigMap 的名称即可,同时也可以自定义 ConfigMap 的挂载路径。
例如,将名称为log-config的 ConfigMap 挂载到 Pod 的 /etc/config 目录下,挂载的文件名称为path指定的值,当前为 log_level:
apiVersion: v1
kind: Pod
metadata:
name: configmap-pod
spec:
containers:
- name: test
image: busybox
volumeMounts:
- name: config-vol
mountPath: /etc/config
volumes:
- name: config-vol
configMap:
name: log-config
items:
- key: log_level
path: log_level
注意
ConfigMap 需要提前创建。
10.2 emptyDir
和上述 volume 不同的是,如果删除 Pod,emptyDir 卷中的数据也将被删除,一般 emptyDir 卷用于 Pod 中的不同 Container 共享数据。它可以被挂载到相同或不同的路径上。
默认情况下,emptyDir 卷支持节点上的任何介质,可能是SSD、磁盘或网络存储,具体取决于自身的环境。可以将 emptyDir.medium 字段设置为Memory,让 Kubernetes 使用 tmpfs(内存支持的文件系统),虽然 tmpfs 非常快,但是 tmpfs 在节点重启时,数据同样会被清除,并且设置的大小会被计入到 Container 的内存限制当中。
使用 emptyDir 卷的示例,直接指定 emptyDir 为 {} 即可:
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
10.3 hostPath
hostPath 卷可将节点上的文件或目录挂载到 Pod 上,用于 Pod 自定义日志输出或访问 Docker 内部的容器等。
使用 hostPath 卷的示例。将主机的/data目录挂载到 Pod 的/test-pd目录:
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /data
# this field is optional
type: Directory
hostPath 卷常用的 type(类型)如下:
- type为空字符串:默认选项,意味着挂载hostPath卷之前不会执行任何检查。
- DirectoryOrCreate:如果给定的path不存在任何东西,那么将根据需要创建一个权限为0755的空目录,和Kubelet具有相同的组和权限。
- Directory:目录必须存在于给定的路径下。
- FileOrCreate:如果给定的路径不存储任何内容,则会根据需要创建一个空文件,权限设置为0644,和Kubelet具有相同的组和所有权。
- File:文件,必须存在于给定路径中。
- Socket:UNIX套接字,必须存在于给定路径中。
- CharDevice:字符设备,必须存在于给定路径中。
- BlockDevice:块设备,必须存在于给定路径中。
10.4 NFS
NFS 卷也是一种网络文件系统,同时也可以作为动态存储,和 GFS 类似,删除 Pod 时,NFS 中的数据不会被删除。NFS 可以被多个写入同时挂载。
10.5 persistentVolumeClaim
persistentVolumeClaim 卷用于将 PersistentVolume(持久化卷)挂载到容器中,PersistentVolume 分为动态存储和静态存储,静态存储的 PersistentVolume 需要手动提前创建 PV,动态存储无需手动创建 PV。
10.6 Secret
Secret 卷和 ConfigMap 卷类似,见上述。
10.7 SubPath
有时可能需要将一个卷挂载到不同的子目录,此时使用 volumeMounts.subPath 可以实现不同子目录的挂载。
本示例为一个 LAMP 共享一个卷,使用 subPath 卷挂载不同的目录:
apiVersion: v1
kind: Pod
metadata:
name: my-lamp-site
spec:
containers:
- name: mysql
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "rootpasswd"
volumeMounts:
- mountPath: /var/lib/mysql
name: site-data
subPath: mysql
- name: php
image: php:7.0-apache
volumeMounts:
- mountPath: /var/www/html
name: site-data
subPath: html
volumes:
- name: site-data
persistentVolumeClaim:
claimName: my-lamp-site-data
更多 volume 可参考:
https://kubernetes.io/docs/concepts/storage/volumes/
10.8 PV & PVC
PersistentVolume(简称PV)是由管理员设置的存储,它同样是集群中的一类资源,PV 是容量插件,如 Volumes(卷),但其生命周期独立使用 PV 的任何 Pod,PV 的创建可使用NFS、iSCSI、GFS、CEPH等。
PersistentVolumeClaim(简称PVC)是用户对存储的请求,类似于 Pod,Pod 消耗节点资源,PVC 消耗 PV 资源,Pod 可以请求特定级别的资源(CPU和内存),PVC 可以请求特定的大小和访问模式。例如,可以以一次读/写或只读多次的模式挂载。
虽然 PVC 允许用户使用抽象存储资源,但是用户可能需要具有不同性质的 PV 来解决不同的问题,比如使用SSD硬盘来提高性能。所以集群管理员需要能够提供各种 PV,而不仅是大小和访问模式,并且无须让用户了解这些卷的实现方式,对于这些需求可以使用 StorageClass 资源实现。
目前PV的提供方式有两种:静态或动态。
静态 PV 由管理员提前创建,动态 PV 无需提前创建,只需指定 PVC 的 StorageClass 即可。
(1)回收策略
当用户使用完卷时,可以从API中删除 PVC 对象,从而允许回收资源。回收策略会告诉 PV 如何处理该卷,目前卷可以保留、回收或删除。
Retain:保留,该策略允许手动回收资源,当删除PVC时,PV仍然存在,volume被视为已释放,管理员可以手动回收卷。
Recycle:回收,如果volume插件支持,Recycle策略会对卷执行rm -rf
清理该PV,并使其可用于下一个新的PVC,但是本策略已弃用,建议使用动态配置。
Delete:删除,如果volume插件支持,删除PVC时会同时删除PV,动态卷默认为Delete。
(2)创建PV
在使用持久化时,需要先创建 PV,然后再创建 PVC,PVC 会和匹配的 PV 进行绑定,然后 Pod 即可使用该存储。
创建一个基于NFS的 PV:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /tmp
server: 172.17.0.2
说明:
- capacity:容量。
- accessModes:访问模式。包括以下3种:
- ReadWriteOnce:可以被单节点以读写模式挂载,命令行中可以被缩写为RWO。
- ReadOnlyMany:可以被多个节点以只读模式挂载,命令行中可以被缩写为ROX。
- ReadWriteMany:可以被多个节点以读写模式挂载,命令行中可以被缩写为RWX。
- storageClassName:PV的类,一个特定类型的PV只能绑定到特定类别的PVC。
- persistentVolumeReclaimPolicy:回收策略。
- mountOptions:非必须,新版本中已弃用。
- nfs:NFS服务配置。包括以下两个选项:
- path:NFS上的目录
- server:NFS的IP地址
创建的PV会有以下几种状态:
- Available(可用),没有被PVC绑定的空间资源。
- Bound(已绑定),已经被PVC绑定。
- Released(已释放),PVC被删除,但是资源还未被重新使用。
- Failed(失败),自动回收失败。
可以创建一个基于 hostPath 的 PV:
kind: PersistentVolume
apiVersion: v1
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
(3)创建PVC
创建 PVC 需要注意的是,各个方面都符合要求 PVC 才能和 PV 进行绑定,比如accessModes、storageClassName、volumeMode都需要相同才能进行绑定。
创建 PVC 的示例如下:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: slow
selector:
matchLabels:
release: "stable"
matchExpressions:
- {key: environment, operator: In, values: [dev]}
比如上述基于 hostPath 的 PV 可以使用以下 PVC 进行绑定,storage 可以比 PV 小:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
然后创建一个 Pod 指定 volumes 即可使用这个 PV:
kind: Pod
apiVersion: v1
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
注意
claimName 需要和上述定义的 PVC 名称 task-pv-claim 一致。
11 - CronJob
CronJob 用于以时间为基准周期性地执行任务,这些自动化任务和运行在Linux或UNIX系统上的 CronJob 一样。CronJob 对于创建定期和重复任务非常有用,例如执行备份任务、周期性调度程序接口、发送电子邮件等。
对于 Kubernetes 1.8 以前的版本,需要添加 --runtime-config=batch/v2alpha1=true 参数至 APIServer 中,然后重启 APIServer 和 Controller Manager 用于启用API,对于1.8以后的版本无须修改任何参数,可以直接使用,本节的示例基于1.8以上的版本。
11.1 创建 CronJob
创建 CronJob 有两种方式,一种是直接使用 kubectl 创建,一种是使用yaml文件创建。
使用 kubectl 创建 CronJob 的命令如下:
kubectl run hello --schedule="*/1 * * * *" --restart=OnFailure --image=busybox -- /bin/sh -c "date; echo Hello from the Kubernetes cluster"
对应的yaml文件如下:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
说明
本例创建一个每分钟执行一次、打印当前时间和 Hello from the Kubernetes cluster 的计划任务。
查看创建的 CronJob:
$ kubectl get cj
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello */1 * * * * False 0 <none> 5s
等待1分钟可以查看执行的任务(Jobs):
$ kubectl get jobs
NAME COMPLETIONS DURATION AGE
hello-1558779360 1/1 23s 32s
CronJob 每次调用任务的时候会创建一个 Pod 执行命令,执行完任务后,Pod 状态就会变成Completed,如下所示:
$ kubectl get po
NAME READY STATUS RESTARTS AGE
hello-1558779360-jcp4r 0/1 Completed 0 37s
可以通过logs查看 Pod 的执行日志:
$ kubectl logs -f hello-1558779360-jcp4r
Sat May 25 10:16:23 UTC 2019
Hello from the Kubernetes cluster
如果要删除 CronJob,直接使用delete即可:
kubectl delete cronjob hello
11.2 可用参数的配置
定义一个 CronJob 的yaml文件如下:
apiVersion: v1
items:
- apiVersion: batch/v1beta1
kind: CronJob
metadata:
labels:
run: hello
name: hello
namespace: default
spec:
concurrencyPolicy: Allow
failedJobsHistoryLimit: 1
jobTemplate:
metadata:
creationTimestamp: null
spec:
template:
metadata:
creationTimestamp: null
labels:
run: hello
spec:
containers:
- args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
image: busybox
imagePullPolicy: Always
name: hello
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: OnFailure
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
schedule: '*/1 * * * *'
successfulJobsHistoryLimit: 3
suspend: false
其中各参数的说明如下,可以按需修改:
- schedule:调度周期,和Linux一致,分别是分时日月周。
- restartPolicy:重启策略,和Pod一致。
- concurrencyPolicy:并发调度策略。可选参数如下:
- Allow:允许同时运行多个任务。
- Forbid:不允许并发运行,如果之前的任务尚未完成,新的任务不会被创建。
- Replace:如果之前的任务尚未完成,新的任务会替换的之前的任务。
- suspend:如果设置为true,则暂停后续的任务,默认为false。
- successfulJobsHistoryLimit:保留多少已完成的任务,按需配置。
- failedJobsHistoryLimit:保留多少失败的任务。
相对于Linux上的计划任务,Kubernetes 的 CronJob 更具有可配置性,并且对于执行计划任务的环境只需启动相对应的镜像即可。比如,如果需要Go或者PHP环境执行任务,就只需要更改任务的镜像为Go或者PHP即可,而对于Linux上的计划任务,则需要安装相对应的执行环境。此外,Kubernetes 的 CronJob 是创建 Pod 来执行,更加清晰明了,查看日志也比较方便。可见,Kubernetes 的 CronJob 更加方便和简单。
12 - Taint 和 Toleration
Taint 能够使节点排斥一类特定的 Pod,Taint 和 Toleration 相互配合可以用来避免 Pod 被分配到不合适的节点,比如 Master 节点不允许部署系统组件之外的其他 Pod。每个节点上都可以应用一个或多个 Taint,这表示对于那些不能容忍这些 Taint 的 Pod 是不会被该节点接受的。如果将 Toleration 应用于 Pod 上,则表示这些 Pod 可以(但不要求)被调度到具有匹配 Taint 的节点上。
12.1 概念
给节点增加一个 Taint:
[root@k8s-master01 2.2.8]# kubectl taint nodes k8s-node01 key=value:NoSchedule
node/k8s-node01 tainted
上述命令给 k8s-node01 增加一个 Taint,它的key对应的就是键,value对应就是值,effect 对应的就是 NoSchedule。这表明只有和这个 Taint 相匹配的 Toleration 的 Pod 才能够被分配到 k8s-node01 节点上。按如下方式在 PodSpec 中定义 Pod 的 Toleration,就可以将 Pod 部署到该节点上。
方式一:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
方式二:
tolerations:
- key: "key"
operator: "Exists"
effect: "NoSchedule"
一个 Toleration 和一个 Taint 相匹配是指它们有一样的key和effect,并且如果 operator 是 Exists(此时toleration不指定value)或者 operator 是 Equal,则它们的value应该相等。
注意两种情况:
如果一个 Toleration 的key为空且 operator 为 Exists,表示这个 Toleration 与任意的key、value和effect都匹配,即这个 Toleration 能容忍任意的 Taint:
tolerations:
- operator: "Exists"
如果一个 Toleration 的effect为空,则key与之相同的相匹配的 Taint 的effect可以是任意值:
tolerations:
- key: "key"
operator: "Exists"
上述例子使用到effect的一个值 NoSchedule,也可以使用 PreferNoSchedule,该值定义尽量避免将 Pod 调度到存在其不能容忍的 Taint 的节点上,但并不是强制的。effect 的值还可以设置为 NoExecute。
一个节点可以设置多个 Taint,也可以给一个 Pod 添加多个 Toleration。Kubernetes 处理多个 Taint 和 Toleration 的过程就像一个过滤器:从一个节点的所有 Taint 开始遍历,过滤掉那些 Pod 中存在与之相匹配的 Toleration 的 Taint。余下未被过滤的 Taint 的effect值决定了 Pod 是否会被分配到该节点,特别是以下情况:
- 如果未被过滤的Taint中存在一个以上effect值为NoSchedule的Taint,则Kubernetes不会将Pod分配到该节点。
- 如果未被过滤的Taint中不存在effect值为NoExecute的Taint,但是存在effect值为PreferNoSchedule的Taint,则Kubernetes会尝试将Pod分配到该节点。
- 如果未被过滤的Taint中存在一个以上effect值为NoExecute的Taint,则Kubernetes不会将Pod分配到该节点(如果Pod还未在节点上运行),或者将Pod从该节点驱逐(如果Pod已经在节点上运行)。
例如,假设给一个节点添加了以下的 Taint:
kubectl taint nodes k8s-node01 key1=value1:NoSchedule
kubectl taint nodes k8s-node01 key1=value1:NoExecute
kubectl taint nodes k8s-node01 key2=value2:NoSchedule
然后存在一个 Pod,它有两个 Toleration:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
在上述例子中,该 Pod 不会被分配到上述节点,因为没有匹配第三个 Taint。但是如果给节点添加上述3个 Taint 之前,该 Pod 已经在上述节点中运行,那么它不会被驱逐,还会继续运行在这个节点上,因为第3个 Taint 是唯一不能被这个 Pod 容忍的。
通常情况下,如果给一个节点添加了一个effect值为 NoExecute 的 Taint,则任何不能容忍这个 Taint 的 Pod 都会马上被驱逐,任何可以容忍这个 Taint 的 Pod 都不会被驱逐。但是,如果 Pod 存在一个effect值为 NoExecute 的 Toleration 指定了可选属性 tolerationSeconds 的值,则该值表示是在给节点添加了上述 Taint 之后 Pod 还能继续在该节点上运行的时间,例如:
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
tolerationSeconds: 3600
表示如果这个 Pod 正在运行,然后一个匹配的 Taint 被添加到其所在的节点,那么 Pod 还将继续在节点上运行3600秒,然后被驱逐。如果在此之前上述 Taint 被删除了,则 Pod 不会被驱逐。
删除一个 Taint:
kubectl taint nodes k8s-node01 key1:NoExecute-
查看 Taint:
[root@k8s-master01 2.2.8]# kubectl describe node k8s-node01 | grep Taint
Taints: key=value:NoSchedule
12.2 用例
通过 Taint 和 Toleration 可以灵活地让 Pod 避开某些节点或者将 Pod 从某些节点被驱逐。下面是几种情况。
(1)专用节点
如果想将某些节点专门分配给特定的一组用户使用,可以给这些节点添加一个 Taint(kubectl taint nodes nodename dedicated=groupName:NoSchedule
),然后给这组用户的 Pod 添加一个相对应的 Toleration。拥有上述 Toleration 的 Pod 就能够被分配到上述专用节点,同时也能够被分配到集群中的其他节点。如果只希望这些 Pod 只能分配到上述专用节点中,那么还需要给这些专用节点另外添加一个和上述 Taint 类似的 Label(例如:dedicated=groupName),然后给 Pod 增加节点亲和性要求或者使用 NodeSelector,就能将 Pod 只分配到添加了 dedicated=groupName 标签的节点上。
(2)特殊硬件的节点
在部分节点上配备了特殊硬件(比如GPU)的集群中,我们只允许特定的 Pod 才能部署在这些节点上。这时可以使用 Taint 进行控制,添加 Taint 如 kubectl taint nodes nodename special=true:NoSchedule
或者kubectl taint nodes nodename special=true:PreferNoSchedule
,然后给需要部署在这些节点上的 Pod 添加相匹配的 Toleration 即可。
(3)基于Taint的驱逐
属于 alpha 特性,在每个 Pod 中配置在节点出现问题时的驱逐行为。
12.3 基于 Taint 的驱逐
之前提到过 Taint 的effect值 NoExecute,它会影响已经在节点上运行的 Pod。如果 Pod 不能忍受effect值为 NoExecute 的 Taint,那么 Pod 将会被马上驱逐。如果能够忍受effect值为 NoExecute 的 Taint,但是在 Toleration 定义中没有指定 tolerationSeconds,则 Pod 还会一直在这个节点上运行。
在 Kubernetes 1.6 版以后已经支持(alpha)当某种条件为真时,Node Controller 会自动给节点添加一个 Taint,用以表示节点的问题。当前内置的 Taint 包括:
- node.kubernetes.io/not-ready:节点未准备好,相当于节点状态Ready的值为False。
- node.kubernetes.io/unreachable:Node Controller访问不到节点,相当于节点状态Ready的值为Unknown。
- node.kubernetes.io/out-of-disk:节点磁盘耗尽。
- node.kubernetes.io/memory-pressure:节点存在内存压力。
- node.kubernetes.io/disk-pressure:节点存在磁盘压力。
- node.kubernetes.io/network-unavailable:节点网络不可达。
- node.kubernetes.io/unschedulable:节点不可调度。
- node.cloudprovider.kubernetes.io/uninitialized:如果Kubelet启动时指定了一个外部的cloudprovider,它将给当前节点添加一个Taint将其标记为不可用。在cloud-controller-manager的一个controller初始化这个节点后,Kubelet将删除这个Taint。
使用这个 alpha 功能特性,结合 tolerationSeconds,Pod 就可以指定当节点出现一个或全部上述问题时,Pod 还能在这个节点上运行多长时间。
比如,一个使用了很多本地状态的应用程序在网络断开时,仍然希望停留在当前节点上运行一段时间,愿意等待网络恢复以避免被驱逐。在这种情况下,Pod 的 Toleration 可以这样配置:
tolerations:
- key: "node.alpha.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 6000
注意
Kubernetes会自动给Pod添加一个key为node.kubernetes.io/not-ready的Toleration并配置tolerationSeconds=300,同样也会给Pod添加一个key为node.kubernetes.io/unreachable的Toleration并配置tolerationSeconds=300,除非用户自定义了上述key,否则会采用这个默认设置。
这种自动添加 Toleration 的机制保证了在其中一种问题被检测到时,Pod 默认能够继续停留在当前节点运行5分钟。这两个默认 Toleration 是由 DefaultTolerationSeconds admission controller 添加的。
DaemonSet 中的 Pod 被创建时,针对以下 Taint 自动添加的 NoExecute 的 Toleration 将不会指定 tolerationSeconds:
- node.alpha.kubernetes.io/unreachable
- node.kubernetes.io/not-ready
这保证了出现上述问题时 DaemonSet 中的 Pod 永远不会被驱逐。
13 - RBAC
13.1 RBAC 基本概念
RBAC(Role-Based Access Control,基于角色的访问控制)是一种基于企业内个人用户的角色来管理对计算机或网络资源的访问方法,其在 Kubernetes 1.5 版本中引入,在1.6时升级为Beta版本,并成为 Kubeadm 安装方式下的默认选项。启用 RBAC 需要在启动APIServer时指定 --authorization-mode=RBAC。
RBAC 使用 rbac.authorization.k8s.io API组来推动授权决策,允许管理员通过 Kubernetes API 动态配置策略。
RBAC API 声明了4种*资源对象,即Role、ClusterRole、RoleBinding、ClusterRoleBinding,管理员可以像使用其他API资源一样使用 kubectl API 调用这些资源对象。例如:kubectl create -f (resource).yml。
13.2 Role 和 ClusterRole
Role 和 ClusterRole 的关键区别是,Role 是作用于命名空间内的角色,ClusterRole 作用于整个集群的角色。
在 RBAC API 中,Role 包含表示一组权限的规则。权限纯粹是附加允许的,没有拒绝规则。
Role 只能授权对单个命名空间内的资源的访问权限,比如授权对default命名空间的读取权限:
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
ClusterRole 也可将上述权限授予作用于整个集群的 Role,主要区别是,ClusterRole 是集群范围的,因此它们还可以授予对以下内容的访问权限:
- 集群范围的资源(如Node)。
- 非资源端点(如/healthz)。
- 跨所有命名空间的命名空间资源(如Pod)。
比如,授予对任何特定命名空间或所有命名空间中的 secret 的读权限(取决于它的绑定方式):
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
# "namespace" omitted since ClusterRoles are not namespaced
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]
13.3 RoleBinding 和 ClusterRoleBinding
RoleBinding 将 Role 中定义的权限授予User、Group或Service Account。RoleBinding 和 ClusterRoleBinding 最大的区别与 Role 和 ClusterRole 的区别类似,即 RoleBinding 作用于命名空间,ClusterRoleBinding 作用于集群。
RoleBinding 可以引用同一命名空间的 Role 进行授权,比如将上述创建的pod-reader的 Role 授予default命名空间的用户jane,这将允许jane读取default命名空间中的 Pod:
# This role binding allows "jane" to read pods in the "default" namespace.
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: jane # Name is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role #this must be Role or ClusterRole
name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
apiGroup: rbac.authorization.k8s.io
说明:
roleRef:绑定的类别,可以是Role或ClusterRole。
RoleBinding也可以引用ClusterRole来授予对命名空间资源的某些权限。管理员可以为整个集群定义一组公用的ClusterRole,然后在多个命名空间中重复使用。
比如,创建一个 RoleBinding 引用 ClusterRole,授予dave用户读取development命名空间的Secret:
# This role binding allows "dave" to read secrets in the "development" namespace.
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-secrets
namespace: development # This only grants permissions within the "development" namespace.
subjects:
- kind: User
name: dave # Name is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
ClusterRoleBinding 可用于在集群级别和所有命名空间中授予权限,比如允许组manager中的所有用户都能读取任何命名空间的 Secret:
# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager # Name is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
13.4 对集群资源的权限控制
在 Kubernetes 中,大多数资源都由其名称的字符串表示,例如 pods。但是一些 Kubernetes API 涉及的子资源(下级资源),例如 Pod 的日志,对应的 Endpoint 的URL是:
GET /api/v1/namespaces/{namespace}/pods/{name}/log
在这种情况下,pods 是命名空间资源,log是 Pod 的下级资源,如果对其进行访问控制,要使用斜杠来分隔资源和子资源,比如定义一个 Role 允许读取 Pod 和 Pod 日志:
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list"]
针对具体资源(使用resourceNames指定单个具体资源)的某些请求,也可以通过使用get、delete、update、patch等进行授权,比如,只能对一个叫my-configmap的 configmap 进行get和update操作:
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: configmap-updater
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["my-configmap"]
verbs: ["update", "get"]
注意
如果使用了resourceNames,则verbs不能是list、watch、create、deletecollection等。
13.5 聚合 ClusterRole
从 Kubernetes 1.9 版本开始,Kubernetes 可以通过一组 ClusterRole 创建聚合 ClusterRoles,聚合 ClusterRoles 的权限由控制器管理,并通过匹配 ClusterRole 的标签自动填充相对应的权限。
比如,匹配 rbac.example.com/aggregate-to-monitoring: "true"标签来创建聚合 ClusterRole:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: monitoring
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # Rules are automatically filled in by the controller manager.
然后创建与标签选择器匹配的 ClusterRole 向聚合 ClusterRole 添加规则:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: monitoring-endpoints
labels:
rbac.example.com/aggregate-to-monitoring: "true"
# These rules will be added to the "monitoring" role.
rules:
- apiGroups: [""]
resources: ["services", "endpoints", "pods"]
verbs: ["get", "list", "watch"]
13.6 Role 示例
以下示例允许读取核心API组中的资源 Pods(只写了规则rules部分):
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
允许在 extensions 和 apps API 组中读写 deployments:
rules:
- apiGroups: ["extensions", "apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
允许对 Pods 的读和 Job 的读写:
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: ["batch", "extensions"]
resources: ["jobs"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
允许读取一个名为my-config的 ConfigMap(必须绑定到一个 RoleBinding 来限制到一个命名空间下的 ConfigMap):
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["my-config"]
verbs: ["get"]
允许读取核心组 Node资源(Node属于集群级别的资源,必须放在ClusterRole中,并使用ClusterRoleBinding进行绑定):
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
允许对非资源端点/healthz和所有其子资源路径的Get和Post请求(必须放在ClusterRole并与ClusterRoleBinding进行绑定):
rules:
- nonResourceURLs: ["/healthz", "/healthz/*"] # '*' in a nonResourceURL is a suffix glob match
verbs: ["get", "post"]
13.7 RoleBinding 示例
以下示例绑定为名为“alice@example.com”的用户(只显示subjects部分):
subjects:
- kind: User
name: "alice@example.com"
apiGroup: rbac.authorization.k8s.io
绑定为名为“frontend-admins”的组:
subjects:
- kind: Group
name: "frontend-admins"
apiGroup: rbac.authorization.k8s.io
绑定为 kube-system 命名空间中的默认 Service Account:
subjects:
- kind: ServiceAccount
name: default
namespace: kube-system
绑定为qa命名空间中的所有 Service Account:
subjects:
- kind: Group
name: system:serviceaccounts:qa
apiGroup: rbac.authorization.k8s.io
绑定所有 Service Account:
subjects:
- kind: Group
name: system:serviceaccounts
apiGroup: rbac.authorization.k8s.io
绑定所有经过身份验证的用户(v1.5+):
subjects:
- kind: Group
name: system:authenticated
apiGroup: rbac.authorization.k8s.io
绑定所有未经过身份验证的用户(v1.5+):
subjects:
- kind: Group
name: system:unauthenticated
apiGroup: rbac.authorization.k8s.io
对于所有用户:
subjects:
- kind: Group
name: system:authenticated
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: system:unauthenticated
apiGroup: rbac.authorization.k8s.io
13.8 命令行的使用
权限的创建可以使用命令行直接创建,较上述方式更加简单、快捷,下面我们逐一介绍常用命令的使用。
(1)kubectl create role
创建一个 Role,命名为pod-reader,允许用户在 Pod 上执行get、watch和list:
kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
创建一个指定了resourceNames的 Role,命名为pod-reader:
kubectl create role pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
创建一个命名为foo,并指定APIGroups的 Role:
kubectl create role foo --verb=get,list,watch --resource=replicasets.apps
针对子资源创建一个名为foo的 Role:
kubectl create role foo --verb=get,list,watch --resource=pods,pods/status
针对特定/具体资源创建一个名为my-component-lease-holder的 Role:
kubectl create role my-component-lease-holder --verb=get,list,watch,update --resource=lease --resource-name=my-component
(2)kubectl create clusterrole
创建一个名为pod-reader的 ClusterRole,允许用户在 Pod 上执行get、watch和list:
kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
创建一个名为pod-reader的 ClusterRole,并指定resourceName:
kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
使用指定的apiGroup创建一个名为foo的 ClusterRole:
kubectl create clusterrole foo --verb=get,list,watch --resource=replicasets.apps
使用子资源创建一个名为foo的 ClusterRole:
kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
使用non-ResourceURL创建一个名为foo的 ClusterRole:
kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*
使用指定标签创建名为monitoring的聚合 ClusterRole:
kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true"
(3)kubectl create rolebinding
创建一个名为bob-admin-binding的 RoleBinding,将名为admin的 ClusterRole 绑定到名为acme的命名空间中一个名为bob的user:
kubectl create rolebinding bob-admin-binding --clusterrole=admin --user=bob --namespace=acme
创建一个名为myapp-view-binding的 RoleBinding,将名为view的 ClusterRole,绑定到acme命名空间中名为myapp的 ServiceAccount:
kubectl create rolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp --namespace=acme
(4)kubectl create clusterrolebinding
创建一个名为root-cluster-admin-binding 的 clusterrolebinding,将名为cluster-admin的 ClusterRole 绑定到名为root的user:
kubectl create clusterrolebinding root-cluster-admin-binding --clusterrole=cluster-admin --user=root
创建一个名为myapp-view-binding的 clusterrolebinding,将名为view的 ClusterRole 绑定到acme命名空间中名为myapp的 ServiceAccount:
kubectl create clusterrolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp