service account,顾名思义,主要是给service使用的一个账号。

具体一点,就是为了让Pod中的进程、服务能访问k8s集群而提出的一个概念,基于service account,pod中的进程、服务能获取到一个username和令牌Token,从而调用kubernetes集群的api server。

kubernetes中,每个命名空间默认会有一个名为“default”的service accout,如:

#其中k8s-example1是之前手工创建的一个service account
$kubectl get serviceAccount --namespace=default
NAME           SECRETS   AGE
default        1         23h
k8s-example1   1         31m

service account中最主要的内容是一个secret资源,其中包含了认证所需的token、根CA等。

$kubectl get serviceAccount default --output=yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2019-02-14T10:03:21Z"
  name: default
  namespace: default
  resourceVersion: "325"
  selfLink: /api/v1/namespaces/default/serviceaccounts/default
  uid: c2b7b49b-303f-11e9-91a6-0800274138bb
secrets:
- name: default-token-bwq8p


#default-token-bwq8p 中的内容如下
$kubectl get secret default-token-bwq8p --output=yaml
apiVersion: v1
data:
  ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwdGFXNXAKYTNWaVpVTkJNQjRYRFRFNU1ESXhNekE0TlRNd01sb1hEVEk1TURJeE1UQTROVE13TWxvd0ZURVRNQkVHQTFVRQpBeE1LYldsdWFXdDFZbVZEUVRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTlRnCnNhTzk3bVk0ekpmU3dZcSs0eG94L0pRZ3dyMm9hSkNiMkg5dG9EcVRLcXNLb1FJMWpnQ3IwU0NvYlZXN2VHR2cKUTVRU2w5MFBNbmxiUGFldjUvZkM5b0xHcVFZTlgzSTJjUGxINFo2ODVCZ1hDMGdseDFqRVgyNlBINEFtQjk4WQpTZXpSWitoc2pPclNJQnFvSHRLZDJPdzdzcTFCYjBBZlBLZVJPSFZRK2UyUDZIK0VJVkZXSmxXbUhPeGIxbFJ0CmVrV3pPYlVCVlVQNlltYTZqWEw1VmNndlUwSWRGQkNPRFJ2K0N3TWhaOFRoemxwVWhaTW1nZlNKbUJyT3VJcTIKOGw0RFV1TE5ZU0diTkhnTG9GTTA2c1AybVNrN01NdmN4c1hRVzBENmJXcU9UdlZhNGhodXJrbExjOXpRa1dNcgpra1I0WFBEeVpIZmlZanJoWi9jQ0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUIwR0ExVWRKUVFXCk1CUUdDQ3NHQVFVRkJ3TUNCZ2dyQmdFRkJRY0RBVEFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFEUEtZMkxDSzR6S0loRmtLWkpwY1UrZ0o3NzVSYWxDSzFnSWN1NnZBdWpmV3V0RWRTeApJZ2I3VmgyWEVYR0JOQ2VuRGpTUVNvRVREVG5tNS9sVWV6WFZERzlPVlo1Tng5RGllbG9XQmE3M1FtTldPTFdtCktsY083YWRkMDNocFFNY0dnamNoTjJxSEhkaVdxOEZPVUErK1ZGWG9VN2JpbXV0aFhsRGRTSW9uMStjS0V3b1oKanFIb1UxL1I4dkdWVjRvYWRIbDZudkt1Tk4xNHNlUmdLeS9QVlg4disyZXlEdjJNZXJ5SVdrQ3J4UFhqenBCUwpwSkhIZitlUU9VZXIwYmxwL2dhT1VRdGhqalVMQnk1U3lQT1lsa29ybG9rcE1zN2JUeFJkM2tEcXVWMWxnL2FzCk9XU2VhcnNPNkI3ejlZT1lWeVgzVGtrbHBwQlpoVXhSeTFEVAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
  namespace: ZGVmYXVsdA==
  token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklpSjkuZXlKcGMzTWlPaUpyZFdKbGNtNWxkR1Z6TDNObGNuWnBZMlZoWTJOdmRXNTBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5dVlXMWxjM0JoWTJVaU9pSmtaV1poZFd4MElpd2lhM1ZpWlhKdVpYUmxjeTVwYnk5elpYSjJhV05sWVdOamIzVnVkQzl6WldOeVpYUXVibUZ0WlNJNkltUmxabUYxYkhRdGRHOXJaVzR0WW5keE9IQWlMQ0pyZFdKbGNtNWxkR1Z6TG1sdkwzTmxjblpwWTJWaFkyTnZkVzUwTDNObGNuWnBZMlV0WVdOamIzVnVkQzV1WVcxbElqb2laR1ZtWVhWc2RDSXNJbXQxWW1WeWJtVjBaWE11YVc4dmMyVnlkbWxqWldGalkyOTFiblF2YzJWeWRtbGpaUzFoWTJOdmRXNTBMblZwWkNJNkltTXlZamRpTkRsaUxUTXdNMll0TVRGbE9TMDVNV0UyTFRBNE1EQXlOelF4TXpoaVlpSXNJbk4xWWlJNkluTjVjM1JsYlRwelpYSjJhV05sWVdOamIzVnVkRHBrWldaaGRXeDBPbVJsWm1GMWJIUWlmUS5ua0tMTGItemhlYldibjcxVWJ3WkhjWnJTRHN4eFc2UjRVUTJCMHZCQTZrSWJWeVJWaHpOSkZNaFc0UGxNZkdPUTJJSEswR3Z3RjFfb1RkRFJFX2d4aW93d0VUQUtwZXZZcWFKcGtpV2tzanJVMjdOejNTOW56LVBUS2RlbHI3UFpJVF9rS2dJVGwwbmlQT1pJYXV4eWlidTlneFBkRlBYbGdXN3hTQW9KWURsTnVZdXNGcmo5d0lMaDQxYjVTbFl5VjNlQlhudGJfTVdVeFFXcVdzbmxiOHpVWmZvaER0dGRnNTN3SjFFZGUyaEZDZGxETmtfZGlGcW5jZVpNa1FmNHhBUEhFc3VKVjVzU3dRTGE5MEhwY0lXQXpJcDJmcmx3SnRVWTF6V25wUnc5VXRxLXI1RC10SVZlQ2pMNUFCYlFybTFlcU9ZVzB2V2JjOG8tVklRSVE=
kind: Secret
metadata:
  annotations:
    kubernetes.io/service-account.name: default
    kubernetes.io/service-account.uid: c2b7b49b-303f-11e9-91a6-0800274138bb
  creationTimestamp: "2019-02-14T10:03:21Z"
  name: default-token-bwq8p
  namespace: default
  resourceVersion: "322"
  selfLink: /api/v1/namespaces/default/secrets/default-token-bwq8p
  uid: c2be7873-303f-11e9-91a6-0800274138bb
type: kubernetes.io/service-account-token

其中data字段下包含三部分内容,都是base64编码后的内容。

ca.crt: 集群api server使用的根CA,基于该CA会签发一系列的服务端证书、客户端证书等。

namespace: service account的名称,base64 解码之后,可以看到就是 “default”

token: 认证用的令牌Token

认证用的用户名默认为:

system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT)

 

大致机制介绍完了,很自然会有一个问题:

Pod中的进程、服务从什么地方去获取service account中包含的secret数据????

 

这个是kubernetes在启动一个Pod的过程中,“自动”注入到Pod下的所有容器中的。

pod的manifest文件和service account的manifest文件中都有一个配置字段:

automountServiceAccountToken: false/true

来控制是否自动注入。

注入的具体位置可以查看一个pod的manifest文件,比如:

$kubectl get pod security-context-demo --output=yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"security-context-demo","namespace":"default"},"spec":{"containers":[{"image":"gcr.io/google-samples/node-hello:1.0","name":"sec-ctx-demo","securityContext":{"allowPrivilegeEscalation":false},"volumeMounts":[{"mountPath":"/data/demo","name":"sec-ctx-vol"}]}],"securityContext":{"fsGroup":20000,"runAsUser":8000},"volumes":[{"emptyDir":{},"name":"sec-ctx-vol"}]}}
  creationTimestamp: "2019-02-15T08:37:10Z"
  name: security-context-demo
  namespace: default
  resourceVersion: "23058"
  selfLink: /api/v1/namespaces/default/pods/security-context-demo
  uid: e28423a0-30fc-11e9-91a6-0800274138bb
spec:
  containers:
  - image: gcr.io/google-samples/node-hello:1.0
    imagePullPolicy: IfNotPresent
    name: sec-ctx-demo
    resources: {}
    securityContext:
      allowPrivilegeEscalation: false
      procMount: Default
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /data/demo
      name: sec-ctx-vol
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-bwq8p
      readOnly: true

最后的mountPath中:mountPath: /var/run/secrets/kubernetes.io/serviceaccount

可以登陆到该container中验证具体注入的文件。

 

这块儿没理解的一点是:为什么会有这种需求??为什么pod中的进程、服务要访问kubernetes集群??

一般理解,pod中承载的都是一些业务需求,不应该理会平台层的东西。

 

走到这一步,可能都想试一下,怎么在pod中访问kubernetes集群的服务,这个可以参考 client-go,

mac上安装golang语言后,拉一下代码

go get k8s.io/client-go

 

具体的代码位置:go/src/k8s.io/client-go/examples/in-cluster-client-configuration,同目录下自带了一个Dockerfile文件,方便打包image

 

// Note: the example only works with the code within the same release/branch.
package main

import (
	"fmt"
	"time"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	//
	// Uncomment to load all auth plugins
	// _ "k8s.io/client-go/plugin/pkg/client/auth
	//
	// Or uncomment to load specific auth plugins
	// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
	// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
	// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)

func main() {
	// creates the in-cluster config
	config, err := rest.InClusterConfig()
	if err != nil {
		panic(err.Error())
	}
	// creates the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}
	for {
		pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
		if err != nil {
			panic(err.Error())
		}
		fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))

		// Examples for error handling:
		// - Use helper functions like e.g. errors.IsNotFound()
		// - And/or cast to StatusError and use its properties like e.g. ErrStatus.Message
		_, err = clientset.CoreV1().Pods("default").Get("example-xxxxx", metav1.GetOptions{})
		if errors.IsNotFound(err) {
			fmt.Printf("Pod not found\n")
		} else if statusError, isStatus := err.(*errors.StatusError); isStatus {
			fmt.Printf("Error getting pod %v\n", statusError.ErrStatus.Message)
		} else if err != nil {
			panic(err.Error())
		} else {
			fmt.Printf("Found pod\n")
		}

		time.Sleep(10 * time.Second)
	}
}

 

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐