Note K8 S API Call

How to get Kubernetes API host and port #

[root@master ~]# kubectl cluster-info
Kubernetes control plane is running at https://192.168.199.100:6443
KubeDNS is running at https://192.168.199.100:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://192.168.199.100:6443/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy
[root@master ~]# kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://192.168.199.100:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED

既然kubectl在$HOME/.kube下找到的config文件,为什么不直接从这里读取API地址?

因为有可能会有配置合并,因为可能有很多个配置文件会merge以后得到一个配置。

我们可以这样设置KUBE_API地址方便下面使用

$ KUBE_API=$(kubectl config view -o jsonpath='{.clusters[0].cluster.server}')

How to call Kubernetes API using curl #

任何HTTP客户端都可以,(curl,httpie,wget,postman)

Authenticating API server to client #

:one:首先尝试查询API's /version

[root@master ~]# curl $KUBE_API/version
curl: (60) Peer's Certificate issuer is not recognized.
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

:sob:没有办法访问呢

[root@master crt]# cat /root/.kube/config 
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJek1ESXhPREUyTVRRek9Wb1hEVE16TURJeE5URTJNVFF6T1Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTHlJCjFXTUVSMVlqNFpWYUJOS1ZyNXZJSzF5WVRmQTBSZ2VrMlFTdi9mNTlHVlB2TVppYmVKSkVFc3g1SStEdFBDME8KWjZKMU5FYjZYM0RFc1UzWW4rbkwydk1hS0JjWWN4aXhLUHBoRnVoRm9uWG1QM0ZNc2JrZkVYOXVuRmpGb1Z2NwoyRFdTaGxVZmVZR3ZjdkxldURuZ1ZacllzUUhRSUxVZFNwbE1oMVRYOEF2UWtER0U2SWZoQVhFWjZGdDVxSkw0CklHWlc5d0VheTdJdDIvN0c1eFlXK3J1N3duSTFBT3o4bVNEbDZrWVRLN092OWRLdmxNcTVuSUJrRWNJRmZOS1UKU2R2K0o4Uk9JK1lwdUgxeEliVmNDM3JvWDFFRjg2MERPVnY3MUpkNUNKY25na25ydkhJeGM3b2R2ZEpsajZhVwpOalFJckkzdC96UHU3VnpOY25jQ0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZIRlNsSEQvNDc4c2hPY1Zycmo5ZHFUOThnL3pNQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFDaDFSdUVtbzBCRjNuWVB3Zi80QUxCSjZZQ3JlWC84VW4vYnFTQmMza3E2SFV1T1JLTwp3bVY0SmNDSy9FTnR2REdhZGZmRVZpYW9zWUgybkdWc2NXY1JId1BwU0VSMk1JU01VZmlNQU84Vng1RjZaciszClB2bXhhT0RuSVMvT1pRb003amJ5RGZRRTdhZk1Qa09EWk5KWHl3OExxWllIRWJQV1cwMUthOWZCbEt0enVPV3AKbjRJYVJMbFlpTTFHdmdscnNETEhSd0REc1czbENpZzhZRm9qaWlnUy9COUxQUjJDSHBQSDVlTE84SlBUNy8zKwo2eStDYnMyNEtOZnAxQmxPdUZRTEVscnovVjJRK3pFLzR4UERmam44SldRN3RjMDIwL3ZUaUI5MGZ5TEIvRkxnCjExRkxIR1hRUy9MNG9IbUJJWWRRKzJ2M3Ruc1hjeTkvcUJKawotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
    server: https://192.168.199.100:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURFekNDQWZ1Z0F3SUJBZ0lJQmxaMDVPaDM5MEF3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TXpBeU1UZ3hOakUwTXpsYUZ3MHlOREF5TVRneE5qRTBOREZhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXF2YlN5TnNyYTlpdHdBd08KSkh1SmRFb2lObFd6Ylk4Rkp4Z2xmajFKb0IwcFh4U1R1cTNjcW12bmkrM3A1TE4rT0wwMzFUbU1SNDVoL20xKwpFUysxWE5xZTdpTU9UdmZ0V1hLdDRMcTFLbjlhS1o5bmU0TUdTQ0FqZ29FN1I4ZjNzRU9Pb3UwZkpQVVFtUElNCjlPMm1WQytUSzZ3MGlTWGZyZ1dvRGNzV0Q5RnAvRHpmNkY3TVZmMTliNjJlL1U0WnF4d2dLc2gxL2pjUVhDRXUKMFBqaXdSWG5SZmUxUzJxQXBmVW1GdU5aZGhuUHUrQlpCUXpzTlBhV0o5SjloVTMxWmxObVF3OGIySGNoUkt2UwpqN3JNTWZrbnBDOFV0RW5KaTNXbXBNMzJldzNVZTdFR1NoeWVRVTNQWFJOSEZvSGlnM3Q5a3hzSG1VVTBhcEg0CldCY3JZUUlEQVFBQm8wZ3dSakFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0h3WURWUjBqQkJnd0ZvQVVjVktVY1AvanZ5eUU1eFd1dVAxMnBQM3lEL013RFFZSktvWklodmNOQVFFTApCUUFEZ2dFQkFMd2l4aDFOL2NqVWdGSGJhLzhPWVJlbUxreFVXTHZqRThvZGFPSUJHRitRd0Uwb3JwcnlHeXoyCi8ybDFMdGg2cnhVSE1XVnI1VXFPWlV1WVV3SnBJTWVLVElYRnAydnoyaHdKcVNvSTQ1NUJJS2lVZUx4YVhLdGQKWjZSQUR0YWJyT25rR2c0dUZ5R09NMVNid0VzVnJ6cVF5RDI0SDFmUDUxNmJIb29kSERNNTRhdkYxNjUya25MVApjTXNGY045aFdLU0ZBT0hDdGJYUGJPTUNOL2t3T2VROWVFUUNNN2VjbzBHU3pkdEQ2OS80SFJsSnBlSnYxN0NuCnZORS9RWFAzdThsdXlQMUQzQUIzUEh3ZGUrM2kzMndYYU0rRHpoZDlQc0RPdzllN0VZYW9BRDd0a0VxWjFVa28KeEJkUnljSzZobHRsMUlkRTRUSzZQaTRZK1gweFN3TT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
    client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBcXZiU3lOc3JhOWl0d0F3T0pIdUpkRW9pTmxXemJZOEZKeGdsZmoxSm9CMHBYeFNUCnVxM2NxbXZuaSszcDVMTitPTDAzMVRtTVI0NWgvbTErRVMrMVhOcWU3aU1PVHZmdFdYS3Q0THExS245YUtaOW4KZTRNR1NDQWpnb0U3UjhmM3NFT09vdTBmSlBVUW1QSU05TzJtVkMrVEs2dzBpU1hmcmdXb0Rjc1dEOUZwL0R6Zgo2RjdNVmYxOWI2MmUvVTRacXh3Z0tzaDEvamNRWENFdTBQaml3UlhuUmZlMVMycUFwZlVtRnVOWmRoblB1K0JaCkJRenNOUGFXSjlKOWhVMzFabE5tUXc4YjJIY2hSS3ZTajdyTU1ma25wQzhVdEVuSmkzV21wTTMyZXczVWU3RUcKU2h5ZVFVM1BYUk5IRm9IaWczdDlreHNIbVVVMGFwSDRXQmNyWVFJREFRQUJBb0lCQUFWaVlLRVN4ZnRQaDZsVQp0OTFPUnJYeTM4RDJVZ0JSVU1nNmFuUGZXa0pBcU56bHVRRllHR3NGbXZVOU9QQ0s5cDZ5MXQ5UVFLckFRVFhTCkhQWk5tbGlpU2Y4Vis0MWhJWWgvcEJvL3h4VGZqZWRocmRDbC83eWx4bmlGdVdnNVZBT3BIUVRra3VhSEVVNi8KME1pbDgyY1RXSDgzblMvMGtXYlpwc0ZJZEJsclljbkQ2NlJIWlN1aTgyY2hFckdNSHFOUEtUOVBLbWwyMEdvbgoxcEFFNDFqQkR3Z1g2ZmNOTzY3Y0ZMb01wVlZXYTVDMzYyTVQza2Y5L21oNjFjaEg1SE53M0VhWjg1c3JmZkJVCktXbUpHTC9UbDJ5UTduai8xN2huWkZVLy9vMTJZMW5DNDNtMUlGQVJIdldqMEJmMnN1OTFFZG1UY280eVkxVmoKcmEvekFBRUNnWUVBMjRzajFUdWQ1T29kTHU1NUV6MXVwcjdaRHE1bXBuZXhudysrU2k1SmY0aHVKQmlRMUtreQoyUmRWRHR1M0ZGRmFiVDU4SjVsOVlaQ3l2dVhGSUVQMThjTldjRTVma29ld1pqNldxMnc3TG9rRkJCOGhyZkU1ClpaM0RxRVU0bXhzTzJlV1psWHRVdG92UVZkbXk4K0pzdlBIKzlsR1ZhTnc0RGlYMW5mMGpmV0VDZ1lFQXgxcVAKY05vRk1IakNxSXFTbFZqVGhOcXNySGRlbnRQRHVKNjJKeUpCMHFWQmxtSWZMaWhGTlBEakhtaWxacUpNTEFYagpyeEJkd3JpU2JVYmI5NmpUZFZUdkdyeFk3OS9GbWM1QmxvNVpvSVd2eEQ2UEFrNHdEWktFd1VVL2pVMkYwdUM2Cmd4Y2o3d0M1SVZ0WHJRZVV5WVNXS2ljUkE3NXNOeW54VGYrVWJnRUNnWUJSK3FydXZNeEE1b3J2TTIxU21lWHYKcmVRdmIwQTFlUXlDY01hRnZMTUZSRlNjZGUvZStTOWJrVExaMFlHVHZLMGZqZTJlZTlvdHpISnloaW9OMmxMRQpiRVNpdXlGRS9oWUlsK1o3TEhjTThXMUdGTG5tMGVTMDVTeGljVGFwOUhpZk5QVWN0R2oxb1UreVB4QnJzV2taClJPUUg1bjc4SVA5dGlROG1aNWdSQVFLQmdIY3g1WXdUUDRFUTQwckV1QXBGOXdwN2VUMFJqbWltczJLaXVzVEIKVGR2MTVUWldhdEE5VWN2cXI5R1J2anVVbExqSnVLNEd1aGpnSk9UanRrZnBFSzRaMzNENzVxMWQvWmNONU5keApPNU9uKzBUNkpxVzVQREFSU0FFTE40bDBMYXk5bzZjWDRldFlZbGpZZFo3R1pxYnErS0l4ZzVIYWZIZXJRMVZnCm1FNEJBb0dBR0UzcVNhL3poWnVWTjR4bEFxN2R6VmFmYTNsb1pSbTlPQjFXQlFDaE5ZZW5XSnQ3NDN5Y1JELzYKRlg1V3J2ZitlNzluZjhrckRudXNPSWtGL0pweE5HdlJuL1JNbGdNQldHU0lvNHExRjhQVlJ4bSt4Tk9GVHQ5dApDMWw4dTI4QjBHMW1PSW1rUXpPVjdzRnhOUzVOZ29haW1GTVFFOFk2Nm9CdXA0YU8zdlk9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==

:two:接着把证书设为环境变量

export clientcert=$(grep client-cert ~/.kube/config |cut -d" " -f 6)
echo $clientcert
export clientkey=$(grep client-key-data ~/.kube/config |cut -d" " -f 6)
echo $clientkey
export certauth=$(grep certificate-authority-data ~/.kube/config |cut -d" " -f 6)
echo $certauth

加密这些变量

echo $clientcert | base64 -d > ./client.pem
echo $clientkey | base64 -d > ./client-key.pem
echo $certauth | base64 -d > ./ca.pem

:three:通过这些变量访问

[root@master crt]# curl --cert ./client.pem --key ./client-key.pem --cacert ./ca.pem $KUBE_API/version
{
  "major": "1",
  "minor": "20",
  "gitVersion": "v1.20.6",
  "gitCommit": "8a62859e515889f07e3e3be6a1080413f17cf2c3",
  "gitTreeState": "clean",
  "buildDate": "2021-04-15T03:19:55Z",
  "goVersion": "go1.15.10",
  "compiler": "gc",
  "platform": "linux/amd64"
}

Yay! 🎉

Authenticating Client to API Server using Certificates #

像上面那步一样设置就可以了

curl --cert ./client.pem --key ./client-key.pem --cacert ./ca.pem $KUBE_API/apis/apps/v1/deployments
{
  "kind": "DeploymentList",
  "apiVersion": "apps/v1",
  "metadata": {
    "resourceVersion": "194785"
  },
  "items": [...]
}

😍

Authenticating Client to API Server using Service Account Tokens #

:one:创建一个Token

# Kubernetes <1.24
$ JWT_TOKEN_DEFAULT_DEFAULT=$(kubectl get secrets \
    $(kubectl get serviceaccounts/default -o jsonpath='{.secrets[0].name}') \
    -o jsonpath='{.data.token}' | base64 --decode)

# Kubernetes 1.24+
$ JWT_TOKEN_DEFAULT_DEFAULT=$(kubectl create token default)

:two:接着直接用这个头来访问

[root@master crt]# curl --cacert ./ca.pem $KUBE_API/apis/apps/v1 --header "Authorization: Bearer $JWT_TOKEN_DEFAULT_DEFAULT"
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "apps/v1",
  "resources": [...]
}

:+1:很Nice,但是就好奇了,他有多大的权限呢?

小试一下,又不行了:cry:

[root@master crt]# curl --cacert ./ca.pem $KUBE_API/apis/apps/v1/namespaces/default/deployments --header "Authorization: Bearer $JWT_TOKEN_DEFAULT_DEFAULT"
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "deployments.apps is forbidden: User \"system:serviceaccount:default:default\" cannot list resource \"deployments\" in API group \"apps\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "group": "apps",
    "kind": "deployments"
  },
  "code": 403
}

:three:那我们使用更加强大的kube-system来试试呗

# Kubernetes <1.24
$ JWT_TOKEN_KUBESYSTEM_DEFAULT=$(kubectl -n kube-system get secrets \
    $(kubectl -n kube-system get serviceaccounts/default -o jsonpath='{.secrets[0].name}') \
    -o jsonpath='{.data.token}' | base64 --decode)

# Kubernetes 1.24+
$ JWT_TOKEN_KUBESYSTEM_DEFAULT=$(kubectl -n kube-system create token default)
[root@master crt]# curl --cacert ./ca.pem $KUBE_API/apis/apps/v1/namespaces/default/deployments --header "Authorization: Bearer $JWT_TOKEN_KUBESYSTEM_DEFAULT"
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "deployments.apps is forbidden: User \"system:serviceaccount:kube-system:default\" cannot list resource \"deployments\" in API group \"apps\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "group": "apps",
    "kind": "deployments"
  },
  "code": 403
}

我这边还是不行,但是博客上的那个是可以的,但我觉得问题不大,其实就是一个权限的问题,其实可以调整权限的吧。

Bonus: How to call Kubernetes API from inside a Pod #

这个就不照着做了,其实是一样的。

$ kubectl run -it --image curlimages/curl --restart=Never mypod -- sh
$ env | grep KUBERNETES
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_HOST=10.96.0.1
$ curl https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}/apis/apps/v1 \
  --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  --header "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"

Creating, Reading, Watching, Updating, Patching, and Deleting objects #

嗯。 :yum:很重要的一个知识点咧。

GET    /<resourcePlural>            -  Retrieve a list of type <resourceName>.


POST   /<resourcePlural>            -  Create a new resource from the JSON
                                       object provided by the client.

GET    /<resourcePlural>/<name>     -  Retrieves a single resource with the
                                       given name.

DELETE /<resourcePlural>/<name>     -  Delete the single resource with the
                                       given name.

DELETE /<resourcePlural>            -  Deletes a list of type <resourceName>.


PUT    /<resourcePlural>/<name>     -  Update or create the resource with the given
                                       name with the JSON object provided by client.

PATCH  /<resourcePlural>/<name>     -  Selectively modify the specified fields of
                                       the resource.

GET    /<resourcePlural>?watch=true -  Receive a stream of JSON objects 
                                       corresponding to changes made to any
                                       resource of the given kind over time.

Create #

[root@master crt]# curl --cert ./client.pem --key ./client-key.pem --cacert ./ca.pem $KUBE_API/apis/apps/v1/namespaces/default/deployments \
  -X POST \
  -H 'Content-Type: application/yaml' \
  -d '---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      containers:
      - name: sleep
        image: curlimages/curl
        command: ["/bin/sleep", "365d"]
'

Get all objects in the default namesapce #

[root@master crt]# curl --cert ./client.pem --key ./client-key.pem --cacert ./ca.pem $KUBE_API/apis/apps/v1/namespaces/default/deployments

Get an object by a name and a namespace #

[root@master crt]# curl --cert ./client.pem --key ./client-key.pem --cacert ./ca.pem  $KUBE_API/apis/apps/v1/namespaces/default/deployments/sleep

Watch #

[root@master crt]# curl --cert ./client.pem --key ./client-key.pem --cacert ./ca.pem  $KUBE_API/apis/apps/v1/namespaces/default/deployments?watch=true

Update #

[root@master crt]# curl --cert ./client.pem --key ./client-key.pem --cacert ./ca.pem  $KUBE_API/apis/apps/v1/namespaces/default/deployments/sleep \
  -X PUT \
  -H 'Content-Type: application/yaml' \
  -d '---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      containers:
      - name: sleep
        image: curlimages/curl
        command: ["/bin/sleep", "730d"]  # <-- Making it sleep twice longer
'

Delete #

[root@master crt]# curl --cert ./client.pem --key ./client-key.pem --cacert ./ca.pem  $KUBE_API/apis/apps/v1/namespaces/default/deployments/sleep \
- X DELETE

How to call Kubernetes API using kubectl #

$ kubectl config current-context
cluster1

$ kubectl proxy --port=8080 &
$ curl localhost:8080/apis/apps/v1/deployments
{
  "kind": "DeploymentList",
  "apiVersion": "apps/v1",
  "metadata": {
    "resourceVersion": "660883"
  },
  "items": [...]
}
# Sends HTTP GET request
$ kubectl get --raw /api/v1/namespaces/default/pods

# Sends HTTP POST request
$ kubectl create --raw /api/v1/namespaces/default/pods -f file.yaml

# Sends HTTP PUT request
$ kubectl replace --raw /api/v1/namespaces/default/pods/mypod -f file.json

# Sends HTTP DELETE request
$ kubectl delete --raw /api/v1/namespaces/default/pods

Bonus: Kubernetes API calls equivalent to a kubectl command #

$ kubectl scale deployment sleep --replicas=2 -v 6
I0116 ... loader.go:372] Config loaded from file:  /home/vagrant/.kube/config
I0116 ... cert_rotation.go:137] Starting client certificate rotation controller
I0116 ... round_trippers.go:454] GET https://192.168.58.2:8443/apis/apps/v1/namespaces/default/deployments/sleep 200 OK in 14 milliseconds
I0116 ... round_trippers.go:454] PATCH https://192.168.58.2:8443/apis/apps/v1/namespaces/default/deployments/sleep/scale 200 OK in 12 milliseconds
deployment.apps/sleep scaled

Wanna see the actual request and response bodies? Increase the log verbosity up to 8.