背景
之前基于k8s开发的AI相关平台,需要后台服务管理k8s创建的相关资源(namespace,job,service,deployment等)。
web服务使用python作为主要开发语言,选用k8s提供的python-client包来对k8s资源进行CRUD。
python-clientb包在初始化client时,需要提供k8s的configure相关信息,代码如下:
from kubernetes import client, config
config.load_kube_config('path-of-config')
configuration = client.Configuration()
其中,path-of-config是可选的,如果没有指定config的文件路径,则默认从$HOME/.kube/config文件读取。
阅读python-client包的源码发现,通过从config文件中获取对应的token和ca证书数据来完成REST Api的认证。
实践
实践一
起初,是在编写服务的镜像打包文件Dockerfile时,将config直接COPY到镜像中,在实现过程中读取对应的config文件来完成k8s配置的加载。
这种方式,缺点很明显,就是切换一个集群环境就需要重新打包一次镜像,迁移性很差,不适合多地区部署。
实践二
既然,不能固定配置文件在镜像中,那就采用挂载的方式,将宿主机上的$Home/.kube/config挂载到容器中的$HOME/.kube/config中。
这种方式,虽然能够解决实践一中不需要切换一个集群更换一次镜像的问题,但是也有明显的缺点:
- 需要确保每一个节点在
$HOME/.kube下面有config文件。
其实,在k8s集群中应该只在master节点下含有config文件,slave节点根本不应该有config文件。并且,pod只会被调度到slave节点上运行
- 多集群管理的情况下,
$HOME/.kube/config文件含有多个集群的信息,一旦通过kubectl config user-context xxx切换到其他的集群下,服务连接的k8s集群就会发现错误,导致不可预料的问题。
在超多节点的集群下,这种方法一旦发现错误,纠错很难。多个pod可能连接不同的集群,不同的请求到达不同的pod,会出现时好时坏的情况。
实践三
其实,一直想通过RBAC机制来完善这套服务,由于时间节点不允许,一直拖着,最近时间节点已经完成,再进一步优化服务,下面,将介绍本文如何利用RBAC来管理K8s集群资源。
- 创建
clusterrole
服务除了需要管理namespace下的资源之外,还需要管理cluster级别的服务,因此需要创建clusterrole。为了便于演示,本次使用k8s自带的cluster-admin。
- 创建
serviceaccout和clusterrolebinding
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa-test
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: test-crb
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: test-sa
namespace: default
- python-client配置k8s相关配置信息
import os
from kubernetes import client, config
sa_path = '/var/run/secrets/kubernetes.io/serviceaccount/'
token_path = os.path.join(sa_path, 'token')
with open(token_path, 'r') as f:
token = f.read()
configuration = client.Configuration()
configuration.api_key["authorization"] = token
configuration.api_key_prefix['authorization'] = 'Bearer'
configuration.host = 'https://{0}:{1}'.format(
os.environ['KUBERNETES_SERVICE_HOST'],
os.environ['KUBERNETES_SERVICE_PORT'])
configuration.ssl_ca_cert = os.path.join(sa_path, 'ca.crt')
- 在Deployment中,指定
spec.template.spec.serviceAccountName为test-sa