4.2.1 配置Pod的liveness和readiness探针

Kubelet使用liveness probe(存活探针)来确定何时重启容器。

Kubelet使用readiness probe(就绪探针)来确定容器是否已经就绪可以接受流量。
只有当Pod中的容器都处于就绪状态时Kubelet才会认定该Pod处于就绪状态。
该信号的作用是控制哪些Pod应该作为service的后端。如果Pod处于非就绪状态,那么它们将会被从service的load balancer中移除。

定义liveness命令
长时间运行的应用程序最终会转换到broken状态,除非重启,否则无法恢复。Kubernetes提供了liveness probe来检测和补救这种情况。

基于gcr.io/google_containers/busybox镜像创建运行一个容器的Pod。
Pod配置文件exec-liveness.yaml

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. labels:
  5. test: liveness
  6. name: liveness-exec
  7. spec:
  8. containers:
  9. - name: liveness
  10. args:
  11. - /bin/sh
  12. - -c
  13. - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
  14. image: gcr.io/google_containers/busybox
  15. livenessProbe:
  16. exec:
  17. command:
  18. - cat
  19. - /tmp/healthy
  20. initialDelaySeconds: 5
  21. periodSeconds: 5

periodSeconds:规定kubelet要每隔5秒执行一次liveness probe。
initialDelaySeconds:告诉kubelet在第一次执行probe之前要等待5秒钟。
探针检测命令是在容器中执行cat /tmp/healthy命令。
如果命令执行成功,将返回0,kubelet就会认为该容器是活着的并且很健康。
如果返回非0值,kubelet就会杀掉这个容器并重启它。

定义一个liveness HTTP请求
可以使用HTTP GET请求作为liveness probe。

基于gcr.io/google_containes/liveness镜像运行一个容器的Pod:
配置文件名称:http-liveness.yaml

apiVersion: v1
kind: Pod
metadata:
   labels:
     test: liveness
   name: liveness-http
spec:
   containers:
   - name: liveness
     args:
     - /server
     image: gcr.io/google_containers/liveness
     livenessProbe:
       httpGet:
         path: /healthz
         port: 8080
         httpHeaders:
            - name: X-Custom-Header
              value: Awesome
       initalDelaySeconds: 3
       periodSeconds: 3

livenessProbe:指定Kubelet需要每隔3秒执行一次liveness probe。
initalDelaySeconds:指定kubelet在该执行第一次探测之前需要等待3秒钟。
该探针将向容器中的Server的8080端口发送一个HTTP GET请求。
如果Server的/healthz路径的handler返回一个成功的返回码,kubelet就会认定该容器是活着的并且很健康。
如果返回失败的返回码,kubelet将杀掉该容器并重启。

任何大于200小于400的返回码都会认定是成功的返回码。其他返回码被认定为失败的返回码。

定义TCP liveness探针
第三种liveness probe使用TCP Socket。使用此配置,kubelet将尝试在指定端口上打开容器的套接字。
如果可以建立连接,容器被认为是健康的。如果不能则认为是失败的。

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
     app: goproxy
spec:
  containers:
  - name: goproxy
    image: gcr.io/google_containers/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
         port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    livessProbe:
      tcpSocket:
         port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20

此示例同时使用了livenessProbe和readinessProbe。
容器启动后5秒钟,kubelet将发送第一个readiness probe。这将尝试连接到端口8080上的goproxy容器。
如果探测成功,则该Pod将被标记为就绪。kubelet每隔10秒钟执行一次检查。

容器还配置了livenessProbe。
容器启动15秒后,kubelet将运行第一个liveness probe。并尝试连接到goproxy容器的8080端口上。
如果livenessProbe探测失败,容器将重新启动。

使用命名的端口
使用命名的ContainerPort作为HTTP或TCP liveness检查:

ports:
- name: liveness-port
  containersPort: 8080
  hostPort: 8080

livenessProbe:
  httpGet:
  path: /healthz
  port: liveness-port

定义Readiness Probe探针
有时,应用程序暂时无法对外部流量提供服务。
例如:应用程序可能需要在启动期间加载大量数据或配置文件,在这种情况下,不想杀死应用程序,也不想发送请求。Kubernetes中可以采用readinessProbe。
Pod中的容器可以报告自己还没有准备,不能处理Kubernetes服务发送过来的流量。

readinessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5

Readiness和liveness Probe可以并行用于同一容器。

配置Probe
Probe详细的配置如下:

  • initialDelaySeconds:容器启动后第一次执行探测是需要等待多少秒
  • periodSeconds:执行探测的频率。默认是10秒,最小1秒
  • timeoutSeconds:探测超时时间。默认1秒,最小1秒。
  • successThreshold:探测失败后,最少连接探测成功多少次才被认定为成功。默认是1。对于liveness必须是1。最小值是1.
  • failureThreshold:探测成功后,最少连接探测失败多少次被认定为失败。默认为3。最小值是1

HTTP probe中可以给httpGet设置其他配置项:

  • host:连接的主机名,默认连接到Pod的IP。你可能想在http header中设置“Host”而不是使用IP。
  • schema:连接使用的schema,默认HTTP。
  • path:访问的HTTP server的path。
  • httpHeaders:自定义请求的header。HTTP运行重复的header。
  • port:访问的容器的端口名字或者端口号。端口号介于1和65535之间。

对于HTTP探测器,kubelet向指定的路径和端口发送HTTP请求以执行检查。
Kubelet将probe发送到容器的IP地址。除非地址被httpGet中的可选host字段覆盖。

4.2.2 配置Pod的Service Account

Service Account为Pod中的进程提供身份信息。
当您访问集群时,apiserver会将您认证为一个特定的User Account(通常是admin)。

使用默认的Service Account访问API Server
当创建Pod时,如果没有指定一个Service Account,系统会自动在与该Pod相同的namespace下为其指派一个default service account。
如果您获取刚创建的pod的原始json或者yaml信息,您将看到spec.serviceAccountName字段已经被设置为automatically。

可以在Pod中使用自动挂载的service account凭证来访问API。
Service Account是否能够取得访问API的许可取决于您使用的授权插件和策略。

可选择取消为Service Account自动挂载API凭证,只需要在Service Account中设置automountServiceAccountToken: false:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
automountServiceAccountToken: false
...

只取消单个Pod的API凭证自动挂载:

apiVersion: v1
kind: Pod
metadata:
   name: my-pod
spec:
   serviceAccountName: build-robot
   automountServiceAccountToken: false
...

如果在Pod和Service Account中同时设置了automountServiceAccountToken,Pod设置中的优先级更高。

使用Service Account作为用户权限管理配置kubeconfig

创建服务账号**

kubectl create serviceaccount sample-sc

创建角色
创建一个只可以查看集群deployments、services、pods相关的角色。采用如下配置:

apiVersion: rbac.authorization.k8s.io/v1
#这里也可以用Role
kind: ClusterRole
metadata:
   name: mofang-viewer-role
   labels:
     from: mofang
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - pods/status
  - pods/log
  - services
  - services/status
  - endpoints
  - endpoints/status
  - deployments
  verbs:
  - get
  - list
  - watch

创建角色绑定

apiVersion: rbac.authorization.k8s.io/v1
#这里也可以使用RoleBinding
kind: ClusterRoleBinding
metadata:
   name: sample-role-binding
   labels:
      from: mofang
roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: ClusterRole
   name: mofang-viewer-role
subjects:
- kind: ServiceAccount
  name: sample-sc
  namespace: default

配置kubeconfig
可以动态更改ClusterRole的授权来及时控制某个账号的权限。配置如下:

apiVersion: v1
clusters:
- cluster:
    ##这个是集群的TLS证书,与授权无关。
    certificate-authorization-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvekNDQW1pZ0F3SUJBZ0lEQnN0WU1BMEdDU3FHU0liM0RRRUJDd1VBTUdJeEN6QUpCZ05WQkFZVEFrTk8KTVJFd0R3WURWUVFJREFoYWFHVkthV0Z1WnpFUk1BOEdBMVVFQnd3SVNHRnVaMXBvYjNVeEVEQU9CZ05WQkFvTQpCMEZzYVdKaFltRXhEREFLQmdOVkJBc01BMEZEVXpFTk1Bc0dBMVVFQXd3RWNtOXZkREFlRncweE9EQTFNamt3Ck16UXdNREJhRncwek9EQTFNalF3TXpRMU5UbGFNR294S2pBb0JnTlZCQW9USVdNMlpUbGpObUpqWVRjellqRTAKWTJNMFlXRTNPVE13WWpNNE5ERXhORFpqWVRFUU1BNEdBMVVFQ3hNSFpHVm1ZWFZzZERFcU1DZ0dBMVVFQXhNaApZelpsT1dNMlltTmhOek5pTVRSall6UmhZVGM1TXpCaU16ZzBNVEUwTm1OaE1JR2ZNQTBHQ1NxR1NJYjNEUUVCCkFRVUFBNEdOQURDQmlRS0JnUURMY0VaYnJwK0FrS1o0dThOSVUzbmNoVThiQTEyeEpHSkkzOXF1N3hoUWxHeUcKZmpBMWp1dXhxUzJoTi9ManAzbVc2R0hpbTN3aUl3Y3VZS1Q3dEY5b1R6OCtPOEFDNkdiemRYTEgvVFBNa0JnZgo5U1hoR2h3WGd2SUxvdjNudmVLUzNESXFTdSt5L04rWG4zOE45bndIcXpLSnZYTVE5a0lpQm5NeDBWeXNIUUlECkFRQUJvNEc2TUlHM01BNEdBMVVkRHdFQi93UUVBd0lDckRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUI4R0ExVWQKSXdRWU1CYUFGSVZhLzkwanpTVnZXRUZ2bm0xRk9adFlmWFgvTUR3R0NDc0dBUVVGQndFQkJEQXdMakFzQmdncgpCZ0VGQlFjd0FZWWdhSFIwY0RvdkwyTmxjblJ6TG1GamN5NWhiR2w1ZFc0dVkyOXRMMjlqYzNBd05RWURWUjBmCkJDNHdMREFxb0NpZ0pvWWthSFIwY0RvdkwyTmxjblJ6TG1GamN5NWhiR2w1ZFc0dVkyOXRMM0p2YjNRdVkzSnMKTUEwR0NTcUdTSWIzRFFFQkN3VUFBNEdCQUpYVGlYSW9DVVg4MUVJTmJ1VFNrL09qdEl4MzRyRXR5UG5PK0FTagpqSzNpNHd3UFEwS3kwOGZOU25TZnhDUTJ4RjU1MjE1U29TMzFTd0x6WUlUSnVYWkE3bFdPb1FTVFkvaUFMdVBCCi9rM0JsOE5QY2Z6OEY1eVk3L25jU3pYNHBNeDE4cjBjb09MTWJmWlRRcm1IcGZDTndtZGNCZUIrQm5EUkxQSkYKaDNJRAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg
    server: https://47.95.24.167:6443
  name: beta
contexts:
- context:
    cluster: beta
    user: beta-viewer
  name: beta-viewer
current-context: beta-viewer
kind: Config
preferences: {}
users:
- name: beta-viewer
  user:
    ##这个使我们在创建serviceaccount生成相关secret之后的data.token的base64编码字段,它本质是一个jwt token
    token: eyJhbGciOiJSUzI1NiIsInR5dffg6IkpXVCJ9

使用多个Service Account
每个namespace中都有一个默认的叫做default的service account资源。
在Pod创建之初service account就必须已经存在,否则创建将被拒绝。

4.2.3 Secret配置

Secret用来保存敏感信息。例如密码、OAuth令牌和ssh key。

Secret概览
Secret是一种包含少量敏感信息例如密码、token或key的对象。这样的信息可能被放在Pod Spec中或者镜像中;

要使用Secret,Pod需要引用secret。
Pod可以用两种方式使用secret:作为Volume中的文件被挂载到Pod中的一个或者多个容器里,或者当kubelet为Pod拉取镜像时使用。

内置secret
Service Account使用API凭证自动创建和附加secret
**
Kubernetes自动创建包含访问API凭据的secret,并自动修改您的Pod以使用此类型的secret。

创建您自己的Secret

使用kubectl创建Secret
有些Pod需要访问数据库。这些Pod需要使用的用户名和密码在本机的./username.txt和./password.txt文件里。

$ echo -n "admin" > ./username.txt
$ echo -n "1f2d1e2e67df" > ./password.txt

kubectl create secret命令将这些文件打包到一个Secret中并在API Server中创建了一个对象。

kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt

手动创建Secret
可以以json或yaml格式在文件中创建一个Secret对象,然后创建该对象。
每一项必须是base64编码:

$ echo -n "admin" | base64
YWRtaW4=
$ echo -n "1f2d1e2e67df" | base64
MWYyZDFlMmU2N2Rm

写一个Secret对象:

apiVersion: v1
kind: Secret
metadata:
   name: mysecret
type: Opaque
data: 
   username: YWRtaW4=
   password: MWYyZDFlMmU2N2Rm

解码Secret
使用kubectl get secret命令获取Secret。
解码密码字段:

$ echo "MWYyZDFlMmU2N2Rm"| base64 --decode

使用Secret
Secret可以作为数据卷被挂载,或作为环境变量暴露出来以供Pod中的容器使用。它们也可以被系统的其他部分使用,而不直接暴露在Pod内。

在Pod中使用Secret文件
在Pod中的Volume里使用Secret:

  1. 创建一个secret或者使用已有的secret。多个Pod可以引用同一个secret。
  2. 修改您的Pod的定义在spec.volumes[]下增加一个volume。可以给这个volume随意命名,它的spec.volumes[].secret.secretName必须等于secret对象的名字。
  3. 将spec.containers[].volumeMounts[]加到需要用到该secret的容器中。指定spec.containers[].volumeMounts[].readOnly=true和spec.containers[].volumeMounts[].mountPath为您想要该secret出现的尚未使用的目录。
  4. 修改您的镜像并且/或者命令行让程序从该目录下寻找文件。Secret的data映射中的每一个键都成为了mountPath下的一个文件名。

在一个Pod中使用volume挂载secret的例子:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret

想要用的每个secret都需要在spec.volumes中指明。

如果pod中有多个容器,每个容器都需要自己的volumeMounts配置块,但是每个secret只需要一个spec.volumes。

向特性路径映射secret密钥
可以控制Secret key映射在volumes中的路径。可以使用spec.volumes[].secret.items字段修改每个key的目标路径:

apiVersion: v1
kind: Pod
metadata:
   name: mypod
spec:
   containers:
   - name: mypod
     image: redis
     volumeMounts: 
     - name: foo
       mountPath: "/etc/foo"
       readOnly: true
   volumes:
   - name: foo
     secret:
       secretName: mysecret
       items:
       - key: username
         path: my-group/my-username
  • username secret 存储在/etc/foo/my-group/my-username文件中而不是/etc/foo/username中。
  • password secret没有被映射

如果使用了spec.volumes[].secret.items,只有在items中指定的key被映射。
要使用的Secret中所有的key,所有这些都必须列在items字段中。
所有列出的密钥必须存在于相应的Secret中。否则,不会创建卷。

Secret文件权限
可以指定Secret将拥有的权限模式位文件。如果不指定,默认为0644。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts: 
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret: 
      secretName: mysecret
      defaultMode: 256

然后,Secret将被挂载到/etc/foo目录,所有通过该secret volume挂载创建的文件的权限都是0400。

Secret作为环境变量
将Secret作为pod中的环境变量使用:

  1. 创建一个Secret或者使用一个已存在的Secret。多个Pod可以引用同一个Secret。
  2. 在每个容器中修改你想要使用secret key的Pod定义,为要使用的每个Secret key添加一个环境变量。消费Secret key的环境变量填充Secret的名称,并键入env[x].valueFrom.secretKeyRef。
  3. 修改镜像并/或者命令行,以便程序在指定的环境变量中查找值。
    apiVersion: v1
    kind: Pod
    metadata:
    name: secret-env-pod
    spec:
    containers:
    - name: mycontainer
     image: redis
     env:
       - name: SECRET_USERNAME
         valueFrom:
            secretKeyRef:
              name: mysecret
              key: username
       - name: SECRET_PASSWORD
         valueFrom:
            secretKeyRef:
              name: mysecret
              key: password
    restartPolicy: Never
    

详细
限制
Secret API对象驻留在命名空间中。它们只能由同一命名空间中的pod引用。
每个Secret的大小限制为1MB。这是为了防止创建非常大的Secret会耗尽apiserver和kubelet的内存。

Kubelet仅支持从API Server获取的Pod使用Secret。包括使用kubectl创建的任何Pod,或间接通过replication controller创建的Pod。它不包括通过kubelet —manifest-url标志,其 —config标志或其REST API创建的Pod。

必须先创建Secret,除非将它们标记为可选项,否则必须在将其作为环境变量在Pod中使用之前创建Secret。对不存在的Secret的引用将阻止其启动。

通过secretKeyRef对不存在于命名的key中的key进行引用将阻止其启动。

使用案例
使用案例:包含ssh密钥的pod
创建一个包含ssh key的secret:

$ kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub

我们创建一个使用ssh密钥引用secret的Pod,并在一个卷中使用它:

kind: Pod
apiVersion: v1
metadata:
  name: secret-test-pod
  labels:
    name: secret-test
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: ssh-key-secret
  containers:
  - name: ssh-test-container
    image: mySshImage
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

当容器中的命令运行时,密钥的片段将可在以下目录:

/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey

然后容器可以自由使用密钥数据建立一个ssh连接。

使用案例:包含prod/test凭据的Pod

创建Secret:

$ kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
$ kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests

创建Pod:

apiVersion: v1
kind: List
items:
- kind: Pod
  apiVersion: v1
  metadata:
    name: prod-db-client-pod
    labels:
      name: prod-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: prod-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
- kind: Pod
  apiVersion: v1
  metadata:
    name: test-db-client-pod
    labels:
      name: test-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: test-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"

这2个容器将在其文件系统上显示如下文件,其中包含每个容器环境的值:

/etc/secret-volume/username
/etc/secret-volume/password

使用案例:Secret卷中以点号开头的文件
为了将数据隐藏起来,让该键以一个点开始。例如:当如下Secret被挂载到卷中:

kind: Secret
apiVersion: v1
metadata:
  name: dotfile-secret
data:
  .secret-file: dmFsdWUtMg0KDQo=
---
kind: Pod
apiVersion: v1
metadata:
  name: secret-dotfiles-pod
spec:
  volumes:
  - name: secret-volume
    secret:
       secretName: dotfile-secret
  containers:
  - name: dotfile-test-container
    image: gcr.io/google_containers/busybox
    command:
    - ls
    - "-l"
    - "/etc/secret-volume"
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

Secret-volume将包含一个单独的文件,叫做.secret-file,dotfile-test-container的/etc/secret-volume/.secret-file路径下将有该文件。

以点号开头的文件在ls -l的输出中被隐藏起来了;列出目录内容时,必须使用ls -la才能查看它们。

4.2.4 管理namespace中的资源配额

开启资源配额限制功能
目前有两种资源分配管理相关的控制策略插件ResourceQuota和LimitRange。

两种控制策略的作用范围都是对于某一namespace。
ResourceQuota用来限制namespace中所有的Pod占用的总的资源request和limit;
LimitRange用来设置namespace中Pod的默认的资源request和limit值;

资源配额分为三种类型:

  • 计算资源配额
  • 存储资源配额
  • 对象数量配额

示例
为spark-cluster这个namespace设置ResourceQuota和LimitRange。

配置计算资源配额
配置文件:spark-compute-resource.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
   name: compute-resources
   namespace: spark-cluster
spec:
   hard:
     pods: "20"
     requests.cpu: "20"
     requests.memory: 100Gi
     limits.cpu: "40"
     limits.memory: 200Gi

查看该配置:

kubectl -n spark-cluster describe resourcequota compute-resources

配置对象数量限制
配置文件:spark-object-counts.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
   name: object-counts
   namespace: spark-cluster
spec:
   hard:
     configmaps: "10"
     persistentvolumeclaims: "4"
     replicationcontrollers: "20"
     secrets: "10"
     services: "10"
     services.loadbalancers: "2"

配置CPU和内存LimitRange
配置文件:spark-limit-range.yaml

apiVersion: v1
kind: LimitRange
metadata:
   name: mem-limit-range
spec:
   limits:
   - default:
       memory: 50Gi
       cpu: 5
     defaultRequest:
       memory: 1Gi
       cpu: 1
     type: Container
  • default即limit值
  • defaultRequest即request值