侧边栏壁纸
博主头像
张种恩的技术小栈博主等级

绿泡泡:___zze,添加备注来意

  • 累计撰写 748 篇文章
  • 累计创建 65 个标签
  • 累计收到 39 条评论

目 录CONTENT

文章目录

在 Pod 内部访问 Kubernetes API

zze
zze
2020-11-27 / 0 评论 / 0 点赞 / 470 阅读 / 13975 字

本篇文章来介绍一些在 Kubernetes 集群的 Pod 中如何访问 Kubernetes API。

知识储备

首先,在一个新创建的 Kubernetes 集群中,我们会发现在 default 命名空间下默认就有一个 ServiceAccount 资源和 Secret 资源,如下:

$ kubectl get serviceaccounts 
NAME      SECRETS   AGE
default   1         16d

$ kubectl get secrets 
NAME                  TYPE                                  DATA   AGE
default-token-pvpbd   kubernetes.io/service-account-token   3      16d

如果你有心的话,你会发现每个命名空间下其实都有一个这样的名称包含 default 的 ServiceAccount 和 Secret 资源。

比如我这里新建一个 test 命名空间,然后查看该命名空间下就会自动创建一个 ServiceAccount 资源和 Secret 资源,如下:

$ kubectl create ns test
namespace/test created

$ kubectl get serviceaccounts -n test
NAME      SECRETS   AGE
default   1         5s

$ kubectl get secrets -n test
NAME                  TYPE                                  DATA   AGE
default-token-62dhm   kubernetes.io/service-account-token   3      9s

从名称就可以看出来,上述两个 ServiceAccount 其实是默认的 ServiceAccount,而每个 ServiceAccount 资源都有一个与之对应的 Secret 资源。

那么这个 Secret 资源到底保存了啥玩意儿呢?其实按其名称以及资源类型我们就可以稍加推断了,首先 Secret 资源肯定是用来存放相对私密的数据,而这个 Secret 的名称命名中包含 default-token 就有点像访问令牌啥的了。。推测就看到这里,反正集群是咱们的咱们就直接剥开它来看,还猜个啥哈哈。。

先获取一下 default 命名空间下的 default-token 的详细信息:

$ kubectl get secrets default-token-pvpbd -o yaml
apiVersion: v1
data:
  ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR3akNDQXFxZ0F3SUJBZ0lVTFh1bzZZay9lVFREbitwRFc4L1lHZ1ZmMTJNd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1p6RUxNQWtHQTFVRUJoTUNRMDR4RVRBUEJnTlZCQWdUQ0ZOb1pXNTZhR1Z1TVJFd0R3WURWUVFIRXdoVAphR1Z1ZW1obGJqRU1NQW9HQTFVRUNoTURhemh6TVE4d0RRWURWUVFMRXdaVGVYTjBaVzB4RXpBUkJnTlZCQU1UCkNtdDFZbVZ5Ym1WMFpYTXdIaGNOTWpBeE1URXhNREV5TmpBd1doY05NalV4TVRFd01ERXlOakF3V2pCbk1Rc3cKQ1FZRFZRUUdFd0pEVGpFUk1BOEdBMVVFQ0JNSVUyaGxibnBvWlc0eEVUQVBCZ05WQkFjVENGTm9aVzU2YUdWdQpNUXd3Q2dZRFZRUUtFd05yT0hNeER6QU5CZ05WQkFzVEJsTjVjM1JsYlRFVE1CRUdBMVVFQXhNS2EzVmlaWEp1ClpYUmxjekNDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFOSWF1K2lQVVVmMnB3aWsKWFJIY2RyUWhkR29TaGxBSnlSYVh4dm8xSzdISlphQU5GMTYycVZIS3p6aXorSUVjK0RsK2dqL05NSCs1SVBHbApMMFI5bDNKU1hNeWdsbVh3UnZ4MlFHeEplTVZ4R0lCZ0VDOFh6NG9UM2F6ZkNGZUpDRnl1L2tXQjNpVkpjRzVqCkpSd3krdjh1OUdGTlFETEtQZStQclZBcGI2VFZBZDhncUY0Vm1KbTYvUDFmcFFBcCtpVVhxRlBTNm9Bc0hxVzgKOVpOZE52ODVFTG8wN0x2SDJQZkxsVklsT2RoZ0dDbFZ1YjY4VDU5bDd4eVV6R1JDV3JIclRBTUM3aENmOGZaQwpXbE96V3hrMW9vemVqcnBJWDhMM1g1YzJ4dEp6RVQ5L2s0aVJGeHNJNDRiVFpTejZGR2ttKzhoYjBaQ3lxdUxBCkVEKzZtaU1DQXdFQUFhTm1NR1F3RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJTUFZQkFmOEMKQVFJd0hRWURWUjBPQkJZRUZEVzl6VFg3Ujl1c2xGMGM5bHBORVVhRnRtdnVNQjhHQTFVZEl3UVlNQmFBRkRXOQp6VFg3Ujl1c2xGMGM5bHBORVVhRnRtdnVNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUM4VlBNbGJIcStpMktjCnRldVdvMmdRSFp3ekVITTgrOFdUUzZ0ZGNIb3BJNnh4anUwc29BQWg0ZG1heVNndTdoMndNY3c3bFQvOGNYeTEKTC9kb2RObVJkZUUzQmh2eE5OdUF6VitTR2pCbEs0TEFQblBPZngwa3l3cVNFVURMOXZkSWE3MXhzUnVGQS9WaApVWmZPV2xsbXREOEhYSFZ1Ny9jSVNYUEt5QWFzQ2lDbks2bStmNE91aVdxZng3Ty8ydkgvODJVYlIyczZTdVFNCjNIK2JoaWhnWWozcHRyNVRvTXhJRkJtaXh5RFBDWVFiOHpzaWJPUUhGaWppVlNQR2c0YWJpWFZ6NzYydmVPOGIKNUtYc3lrK0dCNTl0UmtpWEVaVkNDY2tXREhyMUlyM21ncHVUZUhBSUlWb0thazNsdWUyV0xxMjJZcDVSOXkxQQoxQVJyZ0VxQQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
  namespace: ZGVmYXVsdA==
  token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNkluUkpSRFYzVEdsV1JHNVBXVlEwTFVGek4zZHFTeTA1Ymw5RFpsRkxNRU00YVhGaWRIb3pNemN3V1ZraWZRLmV5SnBjM01pT2lKcmRXSmxjbTVsZEdWekwzTmxjblpwWTJWaFkyTnZkVzUwSWl3aWEzVmlaWEp1WlhSbGN5NXBieTl6WlhKMmFXTmxZV05qYjNWdWRDOXVZVzFsYzNCaFkyVWlPaUprWldaaGRXeDBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5elpXTnlaWFF1Ym1GdFpTSTZJbVJsWm1GMWJIUXRkRzlyWlc0dGNIWndZbVFpTENKcmRXSmxjbTVsZEdWekxtbHZMM05sY25acFkyVmhZMk52ZFc1MEwzTmxjblpwWTJVdFlXTmpiM1Z1ZEM1dVlXMWxJam9pWkdWbVlYVnNkQ0lzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVnlkbWxqWlMxaFkyTnZkVzUwTG5WcFpDSTZJalptWlRZeE0yTTFMV1UzT1dRdE5HRXhOUzFpWmpFeUxUUm1aamsxTmpJNE5ESmlNQ0lzSW5OMVlpSTZJbk41YzNSbGJUcHpaWEoyYVdObFlXTmpiM1Z1ZERwa1pXWmhkV3gwT21SbFptRjFiSFFpZlEueGYzdnNIS2FGUkEydVpLNTZua1VfZkhEanRaS2hHRXFyY2F2c3c4YjFESkVLVlQ3Y05LWWVuV0NRY2sxeXVpdEZIYVZXZkNSUTZoWnlvdm0waHFMQ1JiaWhmRjFtQ0NEYm40ZUVpZE1JeEZLdUFBWTR0QWNuNGVoZ3pGbHJ2Tk1oUnQ4V2xJM29YNnBMNXFhdlFlQ3IwSC1NT3ZBVnRYaEE3MnZWUUNtSDBINDRYdmdaTWpORm5UV0Z0ekhFbFRsaHdWejBaQ1hOdHI3R05wbG1sR19Eb3RQckhyWXFHY05DLVhmS1liQTRBSXBremZoTUdTQ3pNWEtwZmYwWW92TGpWNWdwazZiQVM4S0RNb2Y4Zi1rNll6WXRGR0RGSkFOVXNSZGc2TXgyeC0wU25KZmN2dndMY1dGR0ZFTEpUNzd2bGpiRTdkZkQ0SXJfVXdhV0ptaGlR
kind: Secret
metadata:
  annotations:
    kubernetes.io/service-account.name: default
    kubernetes.io/service-account.uid: 6fe613c5-e79d-4a15-bf12-4ff9562842b0
  creationTimestamp: "2020-11-11T01:33:06Z"
  name: default-token-pvpbd
  namespace: default
  resourceVersion: "202"
  selfLink: /api/v1/namespaces/default/secrets/default-token-pvpbd
  uid: 97b44ff4-4014-4d6c-ba27-e607e6b8ba01

很明显,我们可以看到其中保存了三个键值对,这三个键分别为 ca.crtnamespacetoken,见名知意,它们各自的含义如下:

  • ca.crt:为 API Server 签发证书的 CA 的证书;
  • namespace:命名空间;
  • token:认证令牌;

我们知道,Secret 存放的资源会使用 Base64 进行编码,这里为验证我们的猜想可以对这几个值进行解码,如下:

$ echo LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR3akNDQXFxZ0F3SUJBZ0lVTFh1bzZZay9lVFREbitwRFc4L1lHZ1ZmMTJNd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1p6RUxNQWtHQTFVRUJoTUNRMDR4RVRBUEJnTlZCQWdUQ0ZOb1pXNTZhR1Z1TVJFd0R3WURWUVFIRXdoVAphR1Z1ZW1obGJqRU1NQW9HQTFVRUNoTURhemh6TVE4d0RRWURWUVFMRXdaVGVYTjBaVzB4RXpBUkJnTlZCQU1UCkNtdDFZbVZ5Ym1WMFpYTXdIaGNOTWpBeE1URXhNREV5TmpBd1doY05NalV4TVRFd01ERXlOakF3V2pCbk1Rc3cKQ1FZRFZRUUdFd0pEVGpFUk1BOEdBMVVFQ0JNSVUyaGxibnBvWlc0eEVUQVBCZ05WQkFjVENGTm9aVzU2YUdWdQpNUXd3Q2dZRFZRUUtFd05yT0hNeER6QU5CZ05WQkFzVEJsTjVjM1JsYlRFVE1CRUdBMVVFQXhNS2EzVmlaWEp1ClpYUmxjekNDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFOSWF1K2lQVVVmMnB3aWsKWFJIY2RyUWhkR29TaGxBSnlSYVh4dm8xSzdISlphQU5GMTYycVZIS3p6aXorSUVjK0RsK2dqL05NSCs1SVBHbApMMFI5bDNKU1hNeWdsbVh3UnZ4MlFHeEplTVZ4R0lCZ0VDOFh6NG9UM2F6ZkNGZUpDRnl1L2tXQjNpVkpjRzVqCkpSd3krdjh1OUdGTlFETEtQZStQclZBcGI2VFZBZDhncUY0Vm1KbTYvUDFmcFFBcCtpVVhxRlBTNm9Bc0hxVzgKOVpOZE52ODVFTG8wN0x2SDJQZkxsVklsT2RoZ0dDbFZ1YjY4VDU5bDd4eVV6R1JDV3JIclRBTUM3aENmOGZaQwpXbE96V3hrMW9vemVqcnBJWDhMM1g1YzJ4dEp6RVQ5L2s0aVJGeHNJNDRiVFpTejZGR2ttKzhoYjBaQ3lxdUxBCkVEKzZtaU1DQXdFQUFhTm1NR1F3RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJTUFZQkFmOEMKQVFJd0hRWURWUjBPQkJZRUZEVzl6VFg3Ujl1c2xGMGM5bHBORVVhRnRtdnVNQjhHQTFVZEl3UVlNQmFBRkRXOQp6VFg3Ujl1c2xGMGM5bHBORVVhRnRtdnVNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUM4VlBNbGJIcStpMktjCnRldVdvMmdRSFp3ekVITTgrOFdUUzZ0ZGNIb3BJNnh4anUwc29BQWg0ZG1heVNndTdoMndNY3c3bFQvOGNYeTEKTC9kb2RObVJkZUUzQmh2eE5OdUF6VitTR2pCbEs0TEFQblBPZngwa3l3cVNFVURMOXZkSWE3MXhzUnVGQS9WaApVWmZPV2xsbXREOEhYSFZ1Ny9jSVNYUEt5QWFzQ2lDbks2bStmNE91aVdxZng3Ty8ydkgvODJVYlIyczZTdVFNCjNIK2JoaWhnWWozcHRyNVRvTXhJRkJtaXh5RFBDWVFiOHpzaWJPUUhGaWppVlNQR2c0YWJpWFZ6NzYydmVPOGIKNUtYc3lrK0dCNTl0UmtpWEVaVkNDY2tXREhyMUlyM21ncHVUZUhBSUlWb0thazNsdWUyV0xxMjJZcDVSOXkxQQoxQVJyZ0VxQQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== | base64 -d
-----BEGIN CERTIFICATE-----
MIIDwjCCAqqgAwIBAgIULXuo6Yk/eTTDn+pDW8/YGgVf12MwDQYJKoZIhvcNAQEL
BQAwZzELMAkGA1UEBhMCQ04xETAPBgNVBAgTCFNoZW56aGVuMREwDwYDVQQHEwhT
aGVuemhlbjEMMAoGA1UEChMDazhzMQ8wDQYDVQQLEwZTeXN0ZW0xEzARBgNVBAMT
Cmt1YmVybmV0ZXMwHhcNMjAxMTExMDEyNjAwWhcNMjUxMTEwMDEyNjAwWjBnMQsw
CQYDVQQGEwJDTjERMA8GA1UECBMIU2hlbnpoZW4xETAPBgNVBAcTCFNoZW56aGVu
MQwwCgYDVQQKEwNrOHMxDzANBgNVBAsTBlN5c3RlbTETMBEGA1UEAxMKa3ViZXJu
ZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANIau+iPUUf2pwik
XRHcdrQhdGoShlAJyRaXxvo1K7HJZaANF162qVHKzziz+IEc+Dl+gj/NMH+5IPGl
L0R9l3JSXMyglmXwRvx2QGxJeMVxGIBgEC8Xz4oT3azfCFeJCFyu/kWB3iVJcG5j
JRwy+v8u9GFNQDLKPe+PrVApb6TVAd8gqF4VmJm6/P1fpQAp+iUXqFPS6oAsHqW8
9ZNdNv85ELo07LvH2PfLlVIlOdhgGClVub68T59l7xyUzGRCWrHrTAMC7hCf8fZC
WlOzWxk1oozejrpIX8L3X5c2xtJzET9/k4iRFxsI44bTZSz6FGkm+8hb0ZCyquLA
ED+6miMCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C
AQIwHQYDVR0OBBYEFDW9zTX7R9uslF0c9lpNEUaFtmvuMB8GA1UdIwQYMBaAFDW9
zTX7R9uslF0c9lpNEUaFtmvuMA0GCSqGSIb3DQEBCwUAA4IBAQC8VPMlbHq+i2Kc
teuWo2gQHZwzEHM8+8WTS6tdcHopI6xxju0soAAh4dmaySgu7h2wMcw7lT/8cXy1
L/dodNmRdeE3BhvxNNuAzV+SGjBlK4LAPnPOfx0kywqSEUDL9vdIa71xsRuFA/Vh
UZfOWllmtD8HXHVu7/cISXPKyAasCiCnK6m+f4OuiWqfx7O/2vH/82UbR2s6SuQM
3H+bhihgYj3ptr5ToMxIFBmixyDPCYQb8zsibOQHFijiVSPGg4abiXVz762veO8b
5KXsyk+GB59tRkiXEZVCCckWDHr1Ir3mgpuTeHAIIVoKak3lue2WLq22Yp5R9y1A
1ARrgEqA
-----END CERTIFICATE-----

$ echo ZGVmYXVsdA== | base64 -d 
default

可以看到和猜想的一样,而 namespace 正是当前命名空间的名称,可以通过 test 命名空间下的 default-token 验证一下:

$ kubectl get secrets default-token-62dhm -n test -o yaml | grep 'namespace:'
  namespace: dGVzdA==
  namespace: test
$ echo dGVzdA== | base64 -d
test

那么还有一个 token 是干啥用的呢?上面其实已经说明了它主要是做认证令牌用,即服务端(API Service)能够通过该 Token 获取到对应的 ServiceAccount 信息,从而针对对应的 ServiceAccount 完成认证、鉴权操作。

测试

上面花了如此大篇幅来聊 ServiceAccount 和 Secret,当然是因为此篇文章的主题【在 Kubernetes 集群的 Pod 中如何访问 Kubernetes API】与之是密切相关的。

这里我们可以先创建一个测试用的 Deployment 资源,为了能够直观的请求的 API,所以使用的镜像必须包含 curl 命令,我这里就使用 tutum/curl 了,资源 YAML 如下:

$ cat test-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test-app
  template:
    metadata:
      labels:
        app: test-app
    spec:
      containers:
      - name: curl
        image: tutum/curl
        command: ['sleep', '3000']

$ kubectl apply -f test-deploy.yaml
deployment.apps/test-deployment created
# 检查 Pod 成功运行了
$ kubectl get pod 
NAME                               READY   STATUS    RESTARTS   AGE
test-deployment-5cf6d49cb4-lzsbb   1/1     Running   0          46s

接下来查看该 Pod 的详细 YAML:

$ kubectl get pod test-deployment-5cf6d49cb4-lzsbb -o yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: test-app
    pod-template-hash: 5cf6d49cb4
  name: test-deployment-5cf6d49cb4-lzsbb
  namespace: default
...
spec:
  containers:
  - command:
    - sleep
    - "3000"
    image: tutum/curl
    name: curl
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-pvpbd
      readOnly: true
...
  volumes:
  - name: default-token-pvpbd
    secret:
      defaultMode: 420
      secretName: default-token-pvpbd
...

上述输出中我删除了一些不重要的内容,最终我们会发现我们新创建的 Pod 默认将上面提到的当前命名空间下的 default-token-pvpbd 注册为卷(Volume),并将其挂载到了容器的 /var/run/secrets/kubernetes.io/serviceaccount 目录,我们可以进入容器中查看:

$ kubectl exec -it test-deployment-5cf6d49cb4-lzsbb -- bash
root@test-deployment-5cf6d49cb4-lzsbb:/# ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt	namespace  token

此时在 Pod 内部我们就可以借助这三个文件与 API Server 进行通信了,由于 API Server 是开启 HTTPS 的,所以我们这里会使用 ca.crt 来对 API Server 证书进行校验,使用 token 作为 API Server 的访问令牌,如下:

# 让 curl 默认使用该 CA 证书校验
root@test-deployment-5cf6d49cb4-lzsbb:/$ export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# 将 Token 设置为变量
root@test-deployment-5cf6d49cb4-lzsbb:/$ TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
# 访问 API Server
root@test-deployment-5cf6d49cb4-lzsbb:/$ curl -H "Authorization: Bearer $TOKEN" https://kubernetes
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "forbidden: User \"system:serviceaccount:default:default\" cannot get path \"/\"",
  "reason": "Forbidden",
  "details": {
    
  },
  "code": 403
}

从上述 API Server 响应的内容可以看出 API Server 根据 Token 识别出了 ServiceAccount 为 default 命名空间下的 default ServiceAccount,但此时该 ServiceAccount 显然是不具有访问 API 的权限的,所以被 Forbidden 了。

那么现在就是要为 default:default ServiceAccount 授权了,反正是测试我这里就直接给该 ServiceAccount 绑定集群管理员角色:

$ kubectl create clusterrolebinding permissive-binding \
	--clusterrole=cluster-admin \
	--serviceaccount=default:default
clusterrolebinding.rbac.authorization.k8s.io/permissive-binding created

# 或为所有 ServiceAccount 绑定集群管理员角色
# $ kubectl create clusterrolebinding permissive-binding \
# 	 --clusterrole=cluster-admin \
# 	 --group=system:serviceaccounts

在实际使用中应该为对应的 Pod 规划一个专门用于访问 API Server 的 ServiceAccount,并为其绑定合适的角色。

再次在 Pod 内对 API Server 进行访问:

root@test-deployment-5cf6d49cb4-lzsbb:/# curl -H "Authorization: Bearer $TOKEN" https://kubernetes
{
  "paths": [
    "/api",
    "/api/v1",
    "/apis",
    "/apis/",
  ...

可以看到 API Server 正常响应 API 内容了,下面我们就可以正常通过 API Server API 获取集群信息啦~~

以获取当前 Pod 所在命名空间的所有 Pod 信息为例:

# 获取 Pod 所在的命名空间设置为变量
root@test-deployment-5cf6d49cb4-lzsbb:/$ NS=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
# 获取 Pod 所在命名空间的所有 Pod
root@test-deployment-5cf6d49cb4-lzsbb:/$ curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/$NS/pods
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "selfLink": "/api/v1/namespaces/default/pods",
    "resourceVersion": "691463"
  },
  "items": [
    {
      "metadata": {
...

此时 API Server 就以 Json 形式响应了 Pod 列表了,这样我们就成功在 Pod 内部通过 API 获取到了集群信息啦~~如果你需要获取其它信息无非就是访问的路径不同而已了~

0

评论区