Kubernetes中的service account
service account,顾名思义,主要是给service使用的一个账号。具体一点,就是为了让Pod中的进程、服务能访问k8s集群而提出的一个概念,基于service account,pod中的进程、服务能获取到一个username和令牌Token,从而调用kubernetes集群的api server。kubernetes中,每个命名空间默认会有一个名为“default”的serv...
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)
}
}
更多推荐
所有评论(0)