kubernetes client for java使用踩坑记录(io.kubernetes.client.ApiException: Forbidden)

卜鹏
2023-12-01

0,前言

之前说的,使用k8s client 在pod内部操作外部k8s集群,curd configmap,但是在这里遇到一个问题,就是k8s client需要提供/root/.kube/config文件作为参数,如何提供这个配置文件呢?

1,想到的解决方案

1,代码写死。

缺点:(1)当node为master节点时,master:127.0.0.1:6443,需要把这个本地地址改为ip v4地址.
(2)如果node挂掉,pod需要调度到别的node上时,提供的master地址需要随之变化。

2,docker打包镜像时,执行一个脚本,将/root/.kube/config文件复制到容器内。

分析:如果在node上打包,是没有问题,(测试版本就是这样干的)但是如果需要把这个打包好的镜像丢到别的机器上运行,master节点就无法修改了。
缺点:必须要在node节点上打包。并且master地址不能变化。

3,使用存储etcd。

分析:每个node可以把自己的信息丢高etcd去,然后当pod被调度或者启动的时候,从etcd中拿取node节点地址,但是这个功能原生的k8s好像没有提供。
缺点:要自己去实现,可能需要通过crontab做,很麻烦。

2,最终解决方案

后边经过同事的指导,才发现,其实pod和k8s集群交互的方式有两种,一种是处于集群内部(也就是容器内pod操作容器外集群),还有一种就是集群外部操作集群(比如使用一个集群内的pod,操作另一个集群),链接分别如下:集群内集群外上边是k8s client for go,java版本在这里:集群内集群外
而一开始的使用/root/.kube/config这种方式属于集群外部操作,比较麻烦。而如果时在集群内部操作,则直接可以通过
// loading the in-cluster config, including:
//   1. service-account CA
//   2. service-account bearer-token
//   3. service-account namespace
//   4. master endpoints(ip, port) from pre-set environment variables
ApiClient client = ClientBuilder.cluster().build();
Configuration.setDefaultApiClient(client);
CoreV1Api api = new CoreV1Api();
V1PodList list =
        api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null);

这也太直接,太暴力了,希特。

从代码注释可以看出,是通过pod内部的token来访问的,并且环境变量已经设置了ip和port信息,自己可以去pod内部echo一下

3,说干就干,动手实践。

(1),遇到的问题

使用上边的代码,删除configmap的时候,报错:
exception :forbidden

(2)分析

通过万能的Google,了解到是pod的权限太低。也就是这个pod不允许删除和创建configmap,(应该可以查看)

(3)solution

既然权限不够,那就使用rbac给它一点儿权限咯,稍微改一下,使用给ServiceAccount加权限,大概步骤:

1,创建一个ServiceAccount。伪代码:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: curd-serviceaccount
automountServiceAccountToken: true #一定要注意这里,如果这个值是false,是不会将serivceAccount的token挂在到pod里去的,其实不用手动声明为true,因为默认值是true。手动改为false导致k8s config初始化失败,因为token没挂进去,并且go代码报错一点儿也不友好,排查起来很费力,花了好长时间才定位是这里的问题。可恶:(

2,创建一个ClusterRole,以及ClusterRoleBinding。(和Role以及RoleBinding的区别是,集群级别和namespace级别,并且可以混合搭配使用,不过一般建议配套使用)。伪代码:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: curd-clusterRole
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
# 这些规则将被添加到 "monitoring" 角色中。
rules:
- apiGroups: [""]
  resources: ["services", "endpoints", "pods", "configmaps"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: ServiceAccount
  name: curd-serviceaccount# 名称区分大小写
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: curd-clusterRole

具体说明参见,上面的操作做完了后,就可以使用这里ServiceAccount创建pod资源了,

3,修改pod容器定义,引用ServiceAccount。伪代码:

spec:
  serviceAccount: curd-serviceaccount
  containers:
    - image: prom/prometheus

这样创建出来的pod就有了对外部k8s集群各种资源的操作权限了。

4,经过验证,此方法可行。

心得:

遇到问题,可以多看看官方文档,一般来说,你遇到的问题,官方早就已经想到了。所以一定要多看官方文档,或者多看书。

 类似资料: