编排
声明式 API
比如:控制器中的 Deployment,服务中的 NodePort,等等,都是 Kubernetes 提供的 API 对象,我们常用的使用方式就是通过 YAML 文件来声明要使用的这些 API 对象。
基于YAML文件的操作方式,就是“声明式 API”吗?
不是。
比如:在本地编写一个 Deployment 的 YAML 文件:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
然后,使用 kubectl create 命令在 Kubernetes 里创建 nginx 这个 Deployment 对象:
$ kubectl create -f nginx.yaml
这样,两个 Nginx 的 Pod 就会运行起来了。
接下来修改这个 YAML 文件里的 Pod 模板部分,把 Nginx 容器的镜像改成 1.7.9,如下所示:
...
spec:
containers:
- name: nginx
image: nginx:1.7.9
再接下来,就可以执行一句 kubectl replace 操作,来完成这个 Deployment 的更新:
$ kubectl replace -f nginx.yaml
但是,对于上面这种先 kubectl create,再 replace 的操作,我们称为命令式配置文件操作。
什么才是“声明式 API”呢?
使用 kubectl apply 命令来创建这个 Deployment:
$ kubectl apply -f nginx.yaml
这样,Nginx 的 Deployment 就被创建了出来,这看起来跟 kubectl create 的效果一样。
然后,我再修改一下 nginx.yaml 里定义的镜像:
...
spec:
containers:
- name: nginx
image: nginx:1.7.9
这时候,关键来了。
在修改完这个 YAML 文件之后,我不再使用 kubectl replace 命令进行更新,而是继续执行一条 kubectl apply 命令,即:
$ kubectl apply -f nginx.yaml
这时,Kubernetes 就会立即触发这个 Deployment 的“滚动更新”。
Q:它跟 kubectl replace 命令有什么本质区别吗?
A:kubectl replace 的执行过程,是使用新的 YAML 文件中的 API 对象,替换原有的 API 对象;而 kubectl apply,则是执行了一个对原有 API 对象做局部更新的 PATCH 操作。 类似地,kubectl set image 和 kubectl edit 也是对已有 API 对象的修改。对于声明式请求(比如,kubectl apply),一次能处理多个写操作,并且具备 Merge 能力。
总结
首先,所谓“声明式”,指的就是我只需要提交一个定义好的 API 对象来“声明”,我所期望的状态是什么样子。
其次,“声明式 API”允许有多个 API 写端,以 PATCH 的方式对 API 对象进行修改,而无需关心本地原始 YAML 文件的内容。
最后,也是最重要的,有了上述两个能力,Kubernetes 项目才可以基于对 API 对象的增、删、改、查,在完全无需外界干预的情况下,完成对“实际状态”和“期望状态”的调谐(Reconcile)过程。
Reconcile:Kubernetes 的控制器,实际上就是一个“死循环”,它不断地获取“实际状态”,然后与“期望状态”作对比,并以此为依据决定下一步的操作。
** 声明式 API,是 Kubernetes 项目编排能力“赖以生存”的核心所在。**
权限控制之RBAC
Kubernetes 中所有的 API 对象,都保存在 Etcd 里。而对这些 API 对象的操作,都是通过访问 kube-apiserver 实现的。其中一个非常重要的原因,就是需要 APIServer 来做授权工作。而在 Kubernetes 项目中,负责完成授权(Authorization)工作的机制,就是 RBAC:基于角色的访问控制(Role-Based Access Control)。Kubernetes 的 RBAC 与基于数据库来实现 RBAC 的方式不同,但核心思想是相同的。
Role:角色,它其实是一组规则,定义了一组对 Kubernetes API 对象的操作权限。
Subject:被作用者,既可以是“人”,也可以是“机器”,也可以是你在 Kubernetes 里定义的“用户”。
RoleBinding:定义了“被作用者”和“角色”的绑定关系。
Role
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: mynamespace # 指定了它能产生作用的 Namepace 是:mynamespace
name: example-role
rules: # 定义的权限规则
- apiGroups: [""]
resources: ["pods"] # 限制的资源是 Pod
verbs: ["get", "watch", "list"] # 定义了允许的操作
以上的含义是:允许“被作用者”,对 mynamespace 下面的 Pod 对象,进行 GET、WATCH 和 LIST 操作。
RoleBinding
以上的 Role 还没有指定"被作用者",需要通过 RoleBinding 来指定,它的作用相当于通过数据库来实现 RBAC 方式中的关联表。
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: example-rolebinding
namespace: mynamespace # 仅在 Namepace 是:mynamespace 下生效
subjects: # "被作用者"
- kind: ServiceAccount # 类型是 ServiceAccount,名字是 example-user
name: example-sa
namespace: mynamespace
roleRef: # 与上一步定义的 Role 绑定
kind: Role
name: example-role
apiGroup: rbac.authorization.k8s.io
还缺一个 ServiceAccount 的定义:
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: mynamespace
name: example-sa
Subject
apiVersion: v1
kind: Pod
metadata:
namespace: mynamespace
name: sa-token-test
spec:
containers:
- name: nginx
image: nginx:1.7.9
serviceAccountName: example-sa # 指定要使用的 ServiceAccount 的名字是:example-sa
操作验证
创建如上3个对象:
$ kubectl create -f svc-account.yaml
$ kubectl create -f role-binding.yaml
$ kubectl create -f role.yaml
$ kubectl get sa -n mynamespace -o yaml
- apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: 2018-09-08T12:59:17Z
name: example-sa
namespace: mynamespace
resourceVersion: "409327"
...
secrets:
- name: example-sa-token-vmfg6
可以看到,Kubernetes 会为一个 ServiceAccount 自动创建并分配一个 Secret 对象,即:上述 ServiceAcount 定义里最下面的 secrets 字段。
这个 Secret,就是这个 ServiceAccount 对应的、用来跟 APIServer 进行交互的授权文件,一般称它为:Token。Token 文件的内容一般是证书或者密码,它以一个 Secret 对象的方式保存在 Etcd 当中。
等这个 Pod 运行起来之后,我们就可以看到,该 ServiceAccount 的 token,也就是一个 Secret 对象,被 Kubernetes 自动挂载到了容器的 /var/run/secrets/kubernetes.io/serviceaccount 目录下,如下所示:
$ kubectl describe pod sa-token-test -n mynamespace
Name: sa-token-test
Namespace: mynamespace
...
Containers:
nginx:
...
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from example-sa-token-vmfg6 (ro)
$ kubectl exec -it sa-token-test -n mynamespace -- /bin/bash
root@sa-token-test:/# ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt namespace token
这样,容器里的应用,就可以使用这个 ca.crt 来访问 APIServer 了。更重要的是,此时它只能够做 GET、WATCH 和 LIST 操作。因为 example-sa 这个 ServiceAccount 的权限,已经被绑定了 Role 做了权限限制。
用户组
一个 ServiceAccount,在 Kubernetes 里对应的“用户”的名字是:
system:serviceaccount:<ServiceAccount 名字 >
而它对应的内置“用户组”的名字,就是:
system:serviceaccounts:<Namespace 名字 >
比如,可以在 RoleBinding 里定义如下的 subjects:
subjects:
- kind: Group
name: system:serviceaccounts:mynamespace
apiGroup: rbac.authorization.k8s.io
这就意味着这个 Role 的权限规则,作用于 mynamespace 里的所有 ServiceAccount。这就用到了“用户组”的概念。也就相当于指定 Role 与指定的 mynamespace 下所有的 ServiceAccount 都在关联表中建立了关联记录。
而下面这个例子:
subjects:
- kind: Group
name: system:serviceaccounts
apiGroup: rbac.authorization.k8s.io
就意味着这个 Role 的权限规则,作用于整个系统里的所有 ServiceAccount。
ClusterRole 与 ClusterRoleBinding
Role 和 RoleBinding 对象都是 Namespaced 对象(Namespaced Object),它们对权限的限制规则仅在它们自己的 Namespace 内有效,roleRef 也只能引用当前 Namespace 里的 Role 对象。那么,对于非 Namespaced(Non-namespaced)对象(比如:Node),或者,某一个 Role 想要作用于所有的 Namespace 的时候,该如何去做授权呢?
这时候,就必须要使用 ClusterRole 和 ClusterRoleBinding 这两个组合了。这两个 API 对象的用法跟 Role 和 RoleBinding 完全一样。只不过,它们的定义里,没有了 Namespace 字段:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: example-clusterrole
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: example-clusterrolebinding
subjects:
- kind: User
name: example-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: example-clusterrole
apiGroup: rbac.authorization.k8s.io
上面的例子里的 ClusterRole 和 ClusterRoleBinding 的组合,意味着名叫 example-user 的用户,拥有对所有 Namespace 里的 Pod 进行所有操作的权限。
Kubernetes 提供了四个预先定义好的 ClusterRole 来供用户直接使用:
cluster-admin:最高权限
admin:
edit:
view:规定了被作用者只有 Kubernetes API 的只读权限。
Last updated
Was this helpful?