Downward API只能暴露一个pod自身的元数据,而且只可以暴露部分元数据。某些情况下,我们的应用需要知道其他pod的信息,甚至是集群中的其他资源的信息。Downard API做不到这些,我们需要直接与API Server进行交互。
image.png

8.2.1 探究Kubernetes REST API

什么是endpoint(端点)

当一个API与另一个系统交互时,这种通信的接触点被认为是endpoint。对于API,端点可以包括服务器或服务的URL。每个端点都是API可以访问执行其功能所需资源的位置。API使用“请求”和“响应”工作。当一个API从一个web应用程序或web服务器请求信息时,它会收到一个响应。API发送请求和资源所在的位置称为endpoint。API endpoint基本上是服务器或服务的URL的一个花哨的词。

We could do it simpler, by examples:

  • /this-is-an-endpoint
  • /another/endpoint
  • /some/other/endpoint
  • /login
  • /accounts
  • /cart/items

and when put under a domain, it would look like:

Can be either http or https, we use https in the example. Also endpoint can be different for different HTTP methods, for example:

  • GET /item/{id}
  • PUT /item/{id}

would be two different endpoints - one for retrieving, and the other for updating

通过Kubectl proxy访问API Server

kubectl proxy命令运行了一个proxy server来接收你本机的HTTP连接并代理到API server,同时处理身份认证,所以不需要每次请求都上传认证令牌(authentication token)。它也可以确保我们是与真实的API server交互,而不是一个中间人(man in the middle)(通过每个请求都验证服务器证书的方式)。
image.png
我们无须传递其他任何参数,因为kubectl已经知道所需的所有参数(API server的URL、认证令等)

通过Kubectl proxy 研究Kubernetes API

image.png
输出的paths对应了创建资源时资源定义中的API组和版本信息。/api/v1对应apiVersion: v1,指的是我们创建的基础资源(Pod、Service、ReplicationController等)。因为在Kubernetes初期并没有使用API组的概念,所以这些早期版本出现的基础资源不属于任何组。

注意:这些没有列入API组的初始资源类型现在一般被认为属性核心API组。

研究batch API组的REST endpoint

image.png
image.png
“name”: “jobs” 行的信息告诉我们API包含了 /apis/batch/v1/jobs endpioint。对于某些特定的资源,API server暴露了额外的API endpoint(例如 jobs/status 路径)

列举集群中所有的Job实例

image.png
该endpoint返回了跨namespace的所有Job的清单。

  1. #部署一个Job,然后再次访问REST endpoint,观察输出
  2. cd /root/k8s/
  3. cat >my-job.yaml <<'EOF'
  4. apiVersion: batch/v1
  5. kind: Job
  6. metadata:
  7. name: my-job
  8. spec:
  9. template:
  10. metadata:
  11. labels:
  12. app: batch-job
  13. spec:
  14. restartPolicy: OnFailure
  15. containers:
  16. - name: main
  17. image: luksa/batch-job
  18. EOF
  19. kubectl create -f my-job.yaml

通过名称检索一个指定的Job实例

image.png
这与 kubectl get jobs my-job -o json 的输出信息完全一致。

8.2.2 从pod内部与API服务器进行交互

想要从pod内部与API server进行交互,需要关注以下三件事情:

  • 确定API server的位置
  • 确保是与 API server进行交互,而不是一个冒名者
  • 进行(客户端)身份验证,否则将不能查看任何内容以及进行任何操作

运行一个pod来尝试与API server进行通信

  1. #实验需要一个包含curl命令的镜像
  2. docker pull tutum/curl
  3. docker tag tutum/curl 10.0.0.10:5000/tutum/curl
  4. docker push 10.0.0.10:5000/tutum/curl
  5. cd /root/k8s/
  6. cat >curl.yaml <<'EOF'
  7. apiVersion: v1
  8. kind: Pod
  9. metadata:
  10. name: curl
  11. spec:
  12. containers:
  13. - name: main
  14. image: 10.0.0.10:5000/tutum/curl
  15. command: ["sleep", "infinity"] #让容器一直处于运行状态
  16. EOF
  17. kubectl create -f curl.yaml
  18. kubectl exec -it curl -- bash

发现API server地址

一个名为kubenetes的服务在default命名空间被自动暴露,并被配置为指向API server。
image.png

我们已经知道每个service都被配置了对应的环境变量,在容器中通过查询KUBERNETES_SERVICE_HOST和KUBERNETES_SERVICE_PORT两个环境变量就可以获取API server的IP和端口。
image.png

同样,我们已经知道每个服务都可以获取得一个DNS条目,所以可以执行 curl https://kubernetes
image.png

虽然最简单的绕开这一步骤的方式是使用推荐的-k选项(这也是我们在手工操作API server时通常会使用的方式),但我们应该通过使用curl检查证书的方式验证API服务器的身份,而不是盲目地相信连接的服务器确实是真正的API server。

提示:在真实的应用中,永远不要跳过检查服务器证书的环节。这样做会导致你的应用认证令牌 ( authentication token )暴露给采用中间人攻击方式的攻击者。

验证服务器身份

让我们看一下default-token Secret自动在pod中挂载的目录下的3个文件
image.png

该Secret有三个入口(entry)(因此在这个Secret卷中有三个文件)。现在,我们关注一下ca.crt文件。该文件中包含了CA的证书(包含CA的公钥),CA用来对Kubemetes API server证书进行签名。为了验证正在交互的API server,我们需要检查API server的证书是否是由CA签发。
curl允许使用—cacert选项来指定CA 证书
image.png
Unauthorized这个响应提醒我们需要关注授权的问题。

我环境中的命令输出
image.png

  1. #通过设置CURL_CA_BUNDLE环境变量来简化curl操作:
  2. export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  3. #现在可以直接执行:
  4. curl https://kubernetes

我们的客户端 curl 现在信任API server,但API server并不确定访问者的身份,所以没有授权允许访问。

使用API server进行客户端身份验证

为了进行身份验证,我们需要身份验证令牌(authentication token),令牌通过default-token Secret来提供, 同时令牌被存放在secret卷的token文件中。

  1. #首先,将token加载到一个环境变量中:
  2. TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
  3. #现在,我们可以使用 TOKEN 向API server发送请求:
  4. curl -H "Authorization: Bearer $TOKEN" https://kubernetes

image.png
我们通过发送请求报文的HTTP头中的Authorization字段向API server传递了token,API server识别确认token并返回正确的响应。

我的环境略有不同,必须加URL路径才能访问:
image.png
#我的环境按照下面方法关闭RBAC之后,可以不加URL路径访问了:
image.png

关闭基于角色的访问控制(RBAC)

如果你的Kubernetes集群开启了RBAC (Role-Based Access Control ),service account可能没有被授权访问API server(或只有部分授权)。
为了方便,我们这里直接绕过RBAC:

  1. kubectl create clusterrolebinding permissive-binding \
  2. --clusterrole=cluster-admin \
  3. --group=system:serviceaccounts

这个命令赋予了所有service account(也可以说所有的pod)的cluser-admin权限,允许它们执行任何需要的操作。因为这很危险,不应该在生产环境中执行,仅用于测试。

获取当前运行中pod所有在namespace

#kubectl exec到容器中执行:
NS=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)

curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/$NS/pods

这样,我们通过secret卷所挂载的目录中的三个文件( ca.cert、token、namespace),列出了与当前pod运行在同一个namespace下的所有pod。使用同样的方式,我们可以使用 GET、PUT、PATCH等方法来操作其他API对象。

简要说明pod如何与Kubernetes交互

在pod中运行的应用如何正确访问Kubernetes的 API:

  • 应该验证 API server的证书是否是CA所签发, CA的证书是在ca.crt文件中。
  • 应用应该将它在token文件中持有的token,通过发送包含首部字段Authorization的 http报文,来向 API server进行身份验证。
  • 当对pod所在命名空间的 API对象进行CRUD操作时, 应该使用namespace文件来传递命名空间信息到 API server。

定义:CRUD代表Create、 Read、 Update、Delete操作, 与之对应的HTTP方法分别是POST、 GET、 PATCH/PUT、DELETE。
image.png

8.2.3 通过ambassador容器简化与 API server的交互

ambassador 容器模式介绍

在主容器运行的同时,启动一个ambassador容器,并在其中运行kubecctl proxy命令, 通过它来实现与API服务器的交互。

在这种模式下,运行在主容器中的应用不是直接与API服务器进行交互,而是通过HTTP协议(不是HTTPS协议)与ambassador连接,并且由ambassador通过HTTPS协议来连接API server,对应用透明地来处理安全问题(见图8.6)。 这种方式同样使用了default-token secret卷中的文件.
image.png
因为在同一个pod中的所有容器共享loopback网络接口,所以应用可以使用localhost的端口来访问代理(kubectl proxy命令启动的代理)。

运行带有附加ambassador容器的curl pod

image.png

#准备镜像
docker pull luksa/kubectl-proxy:1.6.2
docker tag luksa/kubectl-proxy:1.6.2 10.0.0.10:5000/luksa/kubectl-proxy:1.6.2
docker push 10.0.0.10:5000/luksa/kubectl-proxy:1.6.2

cd /root/k8s/

cat >curl-with-ambassador.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: curl-with-ambassador
spec:
  containers:
  - name: main
    image: 10.0.0.10:5000/tutum/curl
    command: ["sleep", "infinity"]
  - name: ambassador
    image: 10.0.0.10:5000/luksa/kubectl-proxy:1.6.2
EOF

kubectl create -f curl-with-ambassador.yaml

kubectl exec -it curl-with-ambassador -c main -- bash

通过ambassador来与API server进行交互

默认情况下,kubectl proxy绑定8001端口,由于pod中的两个容器共享包括loopback在内的相同的网络接口。
image.png

image.png
curl向在ambassador容器内运行的proxy发送普通的HTTP请求(不包含任何身份验证相关的首部字段),然后proxy向API server发送HTTPS请求,通过发送token来对客户端进行身份验证,同时通过验证证书来核实服务器的身份。

ambassador容器可以跨多个应用复用,而且与主应用使用的开发语言无关。负面因素是需要运行额外的进程,并且消耗额外的资源。

8.2.4 使用客户端库与API server交互

对于执行更复杂的API请求,可以使用Kubernetes API 的客户端库(library)。
image.png