与KubernetesAPI服务器交互
在介绍过的Downward API提供了一种简单的方式,将pod和容器的元数据传递给在它们内部运行的进程。但这种方式其实仅仅可以暴露一个pod自身的元数据,而且只可以暴露部分元数据。某些情况下,应用需要知道其他pod的信息,甚至是集群中其他资源的信息。这种情况下DownwardAPI方式将无能为力。
可以通过服务相关的环境变量或者DNS来获取服务和pod的信息,但如果应用需要获取其他资源的信息或者获取最新的信息,就需要直接与API服务器进行交互。
在了解pod中的应用如何与Kubernetes API服务器交互之前,先在自己的本机上研宄一下服务器的REST endpoit,这样可以大致了解什么是API服务器。
1.探究Kubernetes REST API
己经了解了Kubernetes不同的资源类型。但如果打算开发一个可以与 Kubernetes API交互的应用,要首先了解API。
先尝试直接访问API服务器,可以通过运行kubectl cluster-info命令来得到服务器的URL。
$ kubectl cluster-info
Kubernetes master is running at https://192.168.99.100:8443
因为服务器使用HTTPS协议并且需要授权,所以与服务器交互并不是一件简单的事情,可以尝试通过curl来访问它,使用curl的--insecure(或-k)选 项来跳过服务器证书检查环节,但这也不能让我们走得更远。
$ curl https://192.168.99.100:8443 -k
Unauthorized
幸运的是,我们可以执行kubectl proxy命令,通过代理与服务器交互,而不是自行来处理验证过程。
通过Kubectl proxy访问API服务器
kubectl proxy命令启动了一个代理服务来接收来自你本机的HTTP连接并转发至API服务器,同时处理身份认证,所以不需要每次请求都上传认证凭证。它也可以确保我们直接与真实的API服务器交互,而不是一个中间人(通过每次验证服务器证书的方式)。
运行代理很简单,所要做的就是运行以下命令:
$ kubectl proxy
Starting to serve on 127.0.0.1:8001
也无须传递其他任何参数,因为kubectl己经知晓所需的所有参数(API服务器URL、认证凭证等)。一旦启动,代理服务器就将在本地端口8001接收连接请求。
$ curl localhost:8001
{
"paths":[
"/api",
"/api/v1",
可以看到发送请求给代理,代理接着发送请求给API服务器,然后代理将返回从服务器返回的所有信息。
通过Kubectl proxy研究Kubernetes API
可以继续使用curl,或者打幵浏览器并且指向http://localhost:8001,看一下当访问这个基础的URL时,API服务器会返回什么。服务器的应答是一组路径的清单,如下所示。
#代码 8.7 API 服务器的 REST endpoint 清单:http://localhost:8001
$ curl http://localhost:8001
{
"paths":[
"/api",
"/api/v1", #这里可以看到大部分的资源类型
"/apis",
"/apis/apps",
"/apis/apps/v1beta1",
"/apis/batch",
"/apis/batch/v1", #batch API组以及它的两个版本
"/apis/batch/v2alpha1",
....
这些路径对应了创建Pod、Service这些资源时定义的API组和版本信息。
在/api/V1对应apiVersion:这里所说的V1指的是创建的基础资源(Pod、Service、ReplicalionController等)。在Kubernetes最早期版本中提到的最基础的资源并不属于任何指定的组,原因是Kubernetes初期并没有使用API组的概念,这个概念是后期引入的。
注意:这些没有列入API组的初始资源类型现在一般被认为归属于核心的API组。
研究批量API组的REST endpoint
看看Job资源API,从路径/apis/batch下的内容开始(暂时忽略版本),如下面的代码清单所示。
#代码8.8 在/apis/batch/目录下的endpoint清单http://localhost:8001/apis/batch
curl http://localhost:8001/apis/batch
{
"kind": "APIGroup",
"apiVersion": "v1",
"name": "batch",
"versions": [
{
"groupVersion": "batch/v1", #批量API组包含两个版本
"version": "v1"
},
{
"groupVersion": "batch/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": { #客户应该使用V1版本而不是V2alpha1版本
"groupVersion": "batch/v1",
"version": "v1"
}
}
这个响应消息展示了包括可用版本、客户推荐使用版本在内的批量API组信息。 接着看一下/apis/batch/V1路径下的内容,如下面的代码清单所示。
#代码8.9 在/batch/V1中的资源类型:http://localhost:8001/apis/batch/v1
curl http://localhost:8001/apis/batch/v1
{
"kind": "APIResourceList", #这里实在batch/V1API组中的API资源清单
"apiVersion": "v1",
"groupVersion": "batch/v1",
"resources": [ #这个数据包含了这个组中所有的资源类型
{
"name": "jobs", #这里描述了已经被指定了命名空间的JOB资源
"singularName": "",
"namespaced": true,
"kind": "Job",
"verbs": [ #这里给出了资源对应可以使用的动词
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
],
"categories": [
"all"
]
},
{
"name": "jobs/status", #资源也有一个专门的REST endpoint来修改他们的状态
"singularName": "",
"namespaced": true,
"kind": "Job",
"verbs": [ #状态可以被恢复、打补丁或者修改
"get",
"patch",
"update"
]
}
]
}
API服务器返回了在batch/V1目录下API组中的资源类型以及REST endpoint清单。除了资源的名称和相关的类型,API服务器也包含了一些其他信息,比如资源是否被指定了命名空间、名称简写(如果有的话,对于Job来说没有)、资源对应可以使用的动词列表等。
返回的列表描述了在API服务器中暴露的REST资源。"name":"jobs"行的信息告诉我们API包含了/apis/batch/V1/jobs的endpoint,"verbs"数组告诉我们可以通过endpoint恢复、修改以及删除Job资源。对于某些特定的资源,API服务器暴露了额外的API endpoint(例如,通过jobs/status路径可以修改Job的状态)。
列举集群中所有的Job实例
通过在/apis/batch/v1/jobs路径运行一个GET请求,可以获取集群中所有Job的清单,如下面的代码清单所示。
#代码8.10 JOB清单: http://localhost:8001/apis/batch/v1/jobs
curl http://localhost:8001/apis/batch/v1/jobs
{
"kind": "JobList",
"apiVersion": "batch/v1",
"metadata": {
"selfLink": "/apis/batch/v1/jobs",
"resourceVersion": "81048521"
},
"items": []
}
如果在集群中没有部署Job资源,那么items数组将是空的。
通过名称恢复一个指定的Job实例
前面的endpoint返回了跨命名空间的所有Job的清单,如果想要返回指定的一个Job,需要在URL中指定它的名称和所在的命名空间。为了恢复在之前清单中的一个Job (name:my-job;namespace:dfault),需要访问路径:/apis/batch/v1/namespaces/default/jobs/my-job,如下面的代码清单所示。
#代码8.11 通过名称恢复一个指定命名空间下的资源
curl http://localhost:8001/apis/batch/v1/namespaces/default/jobs/my-job
{
"kind": "Job",
"apiVersion": "batch/v1",
"metadata": {
"name": "my-job",
"namespace": "default",
"selfLink": "/apis/batch/v1/namespaces/default/jobs/my-job",
虽然不使用任何特定的工具,也可以访问Kubernetes REST API服务器, 但如果要全面地研究REST API并与之交互,在最后会介绍更好的方式。暂时来看,像这样使用curl进行研究,对理解一个应用如何在pod中运行并与Kubernetes交互己经足够。
2.从pod内部与API服务器进行交互
现在来研究从一个pod内部访问它,这种情况下通常没有kubectl可用。因此,想要从pod内部与API服务器进行交互,需要关注以下三件事情:
- 确定API服务器的位置
- 确保是与API服务器进行交互,而不是一个冒名者
- 通过服务器的认证,否则将不能查看任何内容以及进行任何操作
接下来看一下交互如何实现。
运行一个pod来尝试与API服务器进行通信
首先需要一个pod以便从它内部发起与API服务器的交互。运行一个什么也不做的pod(在它仅有的容器内部运行一个sleep命令),然后通过kubectl exec 在容器内部运行一个脚本,接下来在脚本中使用curl尝试访问API服务器。
因此,需要使用一个包含curl二进制的容器镜像。如果在Docker Hub中搜索, 就会发现tutum/curl镜像,可以使用这个镜像(也可以使用任何包含curl二进制的已有镜像或者自己打包的镜像hpod的定义如下面的代码清单所示。
#代码8.12 用来尝试与API服务器通信的pod:curl.yaml
apiVersion: v1
kind: Pod
metadata:
name: curl
spec:
containers:
- name: main
image: tutum/curl
command: ["sleep", "9999999"]
在完成pod的创建后,在容器中运行kubectl exec来启动一个bashshell: kubectl exec -it curl bash
发现API服务器地址
首先,需要找到Kubernetes API服务器的IP地址和端口。这一点比较容易做到, 因为一个名为kubernetes的服务在默认的命名空间被自动暴露,并被配置为指向API服务器。每次使用kubectl get svc命令显示所有服务清单时,都会看到这个服务。
$ kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.0.0.1 <none> 443/TCP 46d
每个服务都被配置了对应的环境变量,在容器内通过查询KUBERNETES_SERVICE_HOST和KUBERNETES_SERVICE_PORT这两个环境变量,可以获取API服务器的IP地址和端口。
root@curl:/# env | grep KUBERNETES_SERVICE
KUBERNETES_SERVICE PORT=443
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT_HTTPS=443
同样,每个服务都可以获得一个DNS入口,所以甚至没有必要 去查询环境变量,而只是简单地将curl指向https://Kubernetes。公平地讲,如果不知道服务在哪个端口是可用的,既可以查询环境变量,也可以查看DNS SRV记录来得到实际的端口号。
之前展示的环境变量说明API服务器监听HTTPS协议默认的443端口,所以尝试通过HTTPS协议来访问服务器。
root@curl:/# curl https://kubernetes
curl: (60) SSL certificate problem: unable to get local issuer certificate
If you'd like to turn off curl's verification of the certificate, use the -k (or --insecure) option.
虽然最简单的绕开这一步骤的方式是使用推荐的-k选项(这也是在手工操作API服务器时通常会使用的方式),但还是来看一下更长(也是正确)的途径。 应该通过使用curl检查证书的方式验证API服务器的身份,而不是盲目地相信连接的服务是可信的。
验证服务器身份
在Secret章节中,有一个名为defalut-token-xyz的Secret被自动创建,并挂载到每个容器的/var/run/secrets/kubernetes.io/serviceaccount目录下。查看目录下的文件,再次看一下Secret的内容。
root@curl:/#ls /var/run/secrets/kubernetes.io/serviceaccount/
ca.crt namespace token
Secret有三个入口(因此在Secret卷中有三个文件)。ca.crt文件文件中包含了CA的证书,用来对Kubernetes API服务器证书进行签名。为了验证正在交互的API服务器,需要检查服务器的证书是否是由CA签发。curl允许使用-cacert选项来指定CA证书,尝试重新访问API服务器:
root@curl:/# curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt https://kubernetes
Unauthorized
到目前为止,服务使用了信任的CA签名的证书,所以curl验证通过了服务器的身份,但Unauthorized这个响应提醒我们需要关注授权的问题。同时,看一下如何通过设置CURL_CA_BUNDLE环境变量来简化操作,从而不必在每次运行curl时都指定--cacert选项:
root@curl:/# export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
现在,可以不使用--cacert来访问API服务器:
root@curl:/# curl https://kubernetes
Unauthorized
这样操作相对便捷,客户端(curl)现在信任API服务器,但API服务器并不确认访问者的身份,所以没有授权允许访问。
获得API服务器授权
需要获得API服务器的授权,以便可以读取并进一步修改或删除部署在集群中的API对象。为了获得授权,需要认证的凭证,幸运的是,凭证可以使用之前提到的default-token Secret来产生,同时凭证可以被存放在secret卷的token文件中。Secret这个名字就说明了它主要的作用。
可以使用凭证来访问API服务器,第一步,将凭证挂载到环境变量中:
root@curl:/# TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
此时,凭证己经被存放在TOKEN环境变量中,如下面的代码清单所示,可以在向API服务器发送请求时使用它。
#代码8.13 获得API服务器的正确响应
root@curl:/# curl -H "Authorization: Bearer $TOKEN" https://kubernetes
{
"paths":[
"/api",
"/api/v1",
"/apis",
"/apis/apps",
"/apis/apps/v1beta1",
"/apis/authorization.k8s.io",
"/ui/",
"/version"
]
]
通过发送请求的HTTP头中的Authorization字段向API服务器传递了凭证,API服务器识别确认凭证并返回正确的响应,现在可以探索集群中所有的资源。
例如,可以列出集群中所有的pod,但前提是我们知道运行curl的pod属于哪个命名空间。
获取当前运行pod所在的命名空间
之前了解了如何使用Downward API的方式将命名空间的属性传递到pod。如果你注意观察的话,secret卷中也包含了一个叫作命名空间的文件。这个文件包含了当前运行pod所在的命名空间,所以可以读取这个文件来获得命名空间信息,而不是通过环境变量明确地传递信息到pod。文件内容挂载到NS环境变量中,然后列出所有的pod,如下面的代码清单所示。
#代码8.14 获取当前pod所在命名空间中所有pod清单
root@curl:/# NS=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
root@curl:/# curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/$NS/pods
{
"kind": "PodList”,
"apiVersion":"v1",
...
通过使用挂载在secret卷目录下的三个文件,可以罗列出与当前pod运行在同一个命名空间下的所有pod的清单。使用同样的方式不仅可以使用GET请求,还可以使用PUT或者PATCH来检索和修改其他API对象。
简要说明pod如何与Kubernetes交互
简单说明一下在pod中运行的应用如何正确访问Kubernetes的API:
- 应用应该验证API服务器的证书是否是证书机构所签发,这个证书是在cacrt文件中。
- 应用应该将它在token文件中持有的凭证通过Authorization标头来获得API服务器的授权。
- 当对pod所在命名空间的API对象进行CRUD操作时,应该使用namespace文件来传递命名空间信息到API服务器。
定义:CRUD代表创建、读取、修改和删除操作,与之对应的HTTP方法分别是POST、GET、PATCH/PUT 以及DELETE。
与API服务器通信相关的pod的三个方面如图8.5所示。
关闭基于角色的访问控制(RBAC)
如果正在使用一个带有RBAC机制的Kubernetes集群,服务账户可能不会被授权访问API服务器(或只有部分授权)。目前最简单的方式就是运行下面的命令查询API服务器,从而绕过RBAC方式。
$ kubectl create clusterrolebinding permissive-binding \
--clusterrole=cluster-admin \
--group=system:serviceaccounts
这个命令赋予了所有服务账户(也可以说所有的pod)的集群管理员权限,允许它们执行任何需要的操作,很明显这是一个危险的操作,永远都不应该在生产的集群中执行,对于测试来说是没有问题的。
3.通过ambassador容器简化与API服务器的交互
使用HTTPS、证书和授权凭证,对于开发者来说看上去有点复杂。很多开发 者在许多场景下关闭了对服务器证书验证的功能(很多人就一样)。幸运的是,在保证安全性的前提下有办法简化通信的方式。
之前提到过的kubectl proxy命令。在本机上运行这个命令, 从而可以更加方便地访问API服务器。向代理而不是直接向API服务器发送请求,通过代理来处理授权、加密和服务器验证。同样,也可以在pod中这么操作。
ambassador容器模式介绍
想象一下,如果一个应用需要查询API服务器(此外还有其他原因)。除了像之前章节讲到的直接与API服务器交互,可以在主容器运行的同时,启动一个ambassador容器,并在其中运行kubecctl proxy命令,通过它来实现与API服务器的交互。
在这种模式下,运行在主容器中的应用不是直接与API服务器进行交互,而是通过HTTP协议(不是HTTPS协议)与ambassador连接,并且由ambassador通过HTTPS协议来连接API服务器,对应用透明地来处理安全问题(见图8.6)。这种方式同样使用了默认凭证Secret卷中的文件。
因为在同一个pod中的所有连接共享同样的回送网络接口,所以应用可以使用本地的端口来访问代理。
运行带有附加ambassador容器的CURL pod
为了通过操作来理解ambassador容器模式,我们像之前创建curl pod—样创建一个新的pod,但这次不是仅仅在pod中运行单个容器,而是基于一个多用途的kubectl容器镜像来运行一个额外的ambassador容器,这个镜像是之前创建的并己提交到DockerHub。
也有Dockerfile
FROM alpine
RUN apk update && apk add curl && curl -L -O https://dl.k8s.io/v1.8.0/kubernetes-client-linux-amd64.tar.gz && tar zvxf kubernetes-client-linux-amd64.tar.gz kubernetes/client/bin/kubectl && mv kubernetes/client/bin/kubectl / && rm -rf kubernetes && rm -f kubernetes-client-linux-amd64.tar.gz
ADD kubectl-proxy.sh /kubectl-proxy.sh
ENTRYPOINT /kubectl-proxy.sh
pod的manifest文件如以下代码清单所示。
#代码8.15 带有ambassador容器的pod:curl-with-ambassador.yaml
apiVersion: v1
kind: Pod
metadata:
name: curl-with-ambassador
spec:
containers:
- name: main
image: tutum/curl
command: ["sleep", "9999999"]
- name: ambassador
image: luksa/kubectl-proxy:1.6.2 #ambassador容器,运行kubectl-proxy镜像
pod的spec与之前非常类似,但pod名称是不同的,同时增加了一个额外的容器。 运行这个pod,并且通过以下命令进入主容器:
$ kubectl exec -it curl-with-ambassador -c main bash
root@curl-with-ambassador:/#
现在pod包含两个容器,希望在main容器中运行bash,所以使用-c main选项。如果想在pod的第一个容器中运行该命令,也无须明确地指定容器。但如果想在任何其他的容器中运行这个命令,就需要使用-c选项来说明容器的名称。
通过ambassador来与API服务器进行交互
接下来尝试通过ambassador容器来连接API服务器。默认情况下,kubectl proxy绑定8001端口,由于pod中的两个容器共享包括回送地址在内的相同的网络接口,可以如下面的代码清单所示,将curl指向localhost:8001。
#代码8.16 通过ambassador容器访问API服务器
root@curl-with-ambassador:/# curl localhost:8001
{
"paths":[
"/api",
]
}
成功了! curl的输出打印结果与我们之前看到的响应相同,但这次,并不需要处理授权的凭证和服务器证书。
想要清楚地了解处理的细节,请参考图8.7。curl向在ambassador容器内运行的代理发送普通的HTTP请求(不包含任何授权相关的标头),然后代理向API服务器发送HTTPS请求,通过发送凭证来对客户端授权,同时通过验证证书来识别服务器的身份。
这是一个很好的例子,它说明了如何使用一个ambassador容器来屏蔽连接外部服务的复杂性,从而简化在主容器中运行的应用。ambassador容器可以跨多个应用复用,而且与主应用使用的开发语言无关。负面因素是需要运行额外的进程,并且消耗资源。
4.使用客户端库与API服务器交互
每个语言都有自己的客户端库,这个可以去官方网站查找,不做过多解释。
与KubernetesAPI服务器交互的更多相关文章
- 从高处理解android与服务器交互(看懂了做开发就会非常的容易)
今天帮一个朋友改一个bug 他可以算是初学者吧 .我给他看了看代码,从代码和跟他聊天能明显的发现他对客户端与服务器交互 基本 不是很了解.所以我花了更多时间去给他讲客户端与服务器的关系.我觉得从这个高 ...
- 跟服务器交互的Web表单(form)
使用HTML来构建可以跟服务器交互的Web表单(form),通过给你的form元素添加一个action属性来达到此目的. action属性的值指定了表单提交到服务器的地址. 例如: <form ...
- Android和FTP服务器交互,上传下载文件(实例demo)
今天同学说他备份了联系人的数据放在一个文件里,想把它存到服务器上,以便之后可以进行下载恢复..于是帮他写了个上传,下载文件的demo 主要是 跟FTP服务器打交道-因为这个东东有免费的可以身亲哈 1. ...
- html name id, 与服务器交互必须有name
html name id, 与服务器交互必须有name 在HTML中元素的ID和Name的区别和联系. 今天写了个测试,在php脚本里怎么也获取不到$_POST['userName']的值,经检查在h ...
- 20171018 微信小程序客户端数据和服务器交互
-- 时常在想,怎么样才能把知识写的清晰,其实是我理解的不够清晰 微信小程序其实是一个客户端页面,也是需要和服务器交互才能体现数据. 1 --服务器搭建Web API :MVC4 中的一个模板, 如下 ...
- 浏览器与服务器交互原理以及用java模拟浏览器操作v
浏览器应用服务器JavaPHPApache * 1,在HTTP的WEB应用中, 应用客户端和服务器之间的状态是通过Session来维持的, 而Session的本质就是Cookie, * 简单的讲,当浏 ...
- ASP.NET 表单验证方法与客户端(浏览器)服务器交互机制的故事
想到这个问题完全是一个意外吧,是在寻找另外一个问题答案的过程中,才对验证方法与浏览器服务器交互机制的关系有了清晰的认识. 先说下验证方法,验证方法分为前台验证和后台验证. 前台验证就是类似jQuery ...
- 利用ajax短轮询+php与服务器交互制作简易即时聊天网站
主流的Web端即时通讯方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Server-sent Events). 本文主要介绍ajax短轮询的简易实现方式. 看懂此文 ...
- AngularJs与Java Web服务器交互
AngularJs是Google工程师研发的产品,它的强大之处不是几句话就能描述的,只有真正使用过的人才能体会到,笔者准备在这篇文章中,以一个简单的登录校验的例子说明如何使用AngularJs和Web ...
随机推荐
- css背景|列表样式
背景样式 背景区包含内容.padding 和 boder 不包含外边距 background-color 设置元素的背景颜色 background-image 把图像设置为背景,包含内边距和边框,不包 ...
- [转载]centos 7(1611)安装笔记
centos 7(1611)安装笔记 麻烦 前天我把双系统笔记本里的 deepin 的磁盘分区直接从 Windows 7 磁盘管理里格式化了,结果悲催了,开不了机了,显示: 我以为是 Window ...
- Fedora镜像下载地址
Fedora镜像下载地址 Fedora 7核心源码包在: http://archives.fedoraproject.org/pub/archive/fedora/linux/releases/7/F ...
- linux中级之lvs配置(命令)
一.nat模式配置 环境说明: DS:nat网卡(自动获取也可以,充当vip): 192.168.254.13 255.255.255.0 vmnet3网卡(仅主机): 172.16.100.1 25 ...
- 【转载】java与xml
原文地址:http://www.lai18.com/content/1198237.html java项目中,xml文件一般都是用来存储一些配置信息一般的编程, 多数用来存储配置信息 . 拿JDBC来 ...
- 安卓开发(2)—— Kotlin语言概述
安卓开发(2)-- Kotlin语言概述 Android的官方文档都优先采用Kotlin语言了,学它来进行Android开发已经是一种大势所趋了. 这里只讲解部分的语法. 如何运行Kotlin代码 这 ...
- 『动善时』JMeter基础 — 28、JMeter函数助手详解
目录 1.函数助手介绍 2.函数助手中的函数分类 3.常用函数介绍 (1)__Random函数 (2)__counter函数 (3)__time函数 (4)__property函数 (5)__setP ...
- salesforce零基础学习(一百零三)项目中的零碎知识点小总结(五)
本篇参考:Salesforce Admin篇(四) Security 之Two-Factor Authentication & Single Sign On https://developer ...
- 性能调优命令之jstack
jstack是java虚拟机自带的一种线程堆栈跟踪工具. /opt/java8/bin/jstack Usage: jstack [-l] <pid> (to connect to run ...
- grasshopper之python电池执行逻辑
在grasshopper中,需要导入的包虽然不多,但是相当绕人,所要实现的操作往往找不到,暂时做个分类. 双击输入 python 电池: # 导入rhino 包 import Rhino #Rhino ...