12

k8s实践(9)--深入了解Pod

 3 years ago
source link: https://blog.csdn.net/hguisu/article/details/92798387
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、Pod简介

Pod是k8s系统中可以创建和管理的最小单元,是资源对象模型中由用户创建或部署的最小资源对象模型,也是在k8s上运行容器化应用的资源对象,其他的资源对象都是用来支撑或者扩展Pod对象功能的,比如控制器对象是用来管控Pod对象的,Service或者Ingress资源对象是用来暴露Pod引用对象的,PersistentVolume资源对象是用来为Pod提供存储等等, k8s不会直接处理容器,而是Pod ,Pod是由一个或者多个container组成的。

每个Pod都是运行应用的单个实例,如果需要水平扩展应用(例如,运行多个实例),则应该使用多个Pods,每个实例一个Pod。在Kubernetes中,这样通常称为Replication。Replication的Pod通常由Controller创建和管理。

mq6fAfZ.png!web

IFfqiqB.png!web

1.1、为什么需要pod

我们先谈谈为什么k8s会使用pod这个最小单元,而不是使用docker的容器,k8s既然使用了pod,当然有它的理由。

1、更利于扩展

k8s不仅仅支持Docker容器,也支持rkt甚至用户自定义容器,为什么会有这么多不同的容器呢,因为容器并不是真正的虚拟机,docker的一些概念和误区总结,此外,Kubernetes不依赖于底层某一种具体的规则去实现容器技术,

而是通过CRI这个抽象层操作容器

,这样就会需要pod这样一个东西,pod内部再管理多个业务上紧密相关的用户业务容器,就会更有利用业务扩展pod而不是扩展容器。

2、更容易定义一组容器的状态

如果我们没有使用pod,而是直接使用一组容器去跑一个业务呢,那么当其中一个或者若干个容器出现问题呢,我们如何去定义这一组容器的状态呢,通过pod这个概念,这个问题就可以很好的解决,一组业务容器跑在一个k8s的pod中,这个pod中会有一个pause容器,这个容器与其他的业务容器都没有关系,以这个pause容器的状态来代表这个pod的状态.

3、利于容器间文件共享,以及通信。

pod里的多个业务容器共享pause容器的ip和存储卷Volume,pod中的其他容器共享pause容器的ip地址和存储,这样就做到了文件共享和互信。

1.2 Pod 特性:

1  资源共享:IP和Volume

一个Pod里的多个容器可以共享存储和网络IP,可以看作一个逻辑的主机。共享的如 namespace,cgroups或者其他的隔离资源。

多个容器共享同一个network namespace,由此在一个Pod里的多个容器共享Pod的IP和端口namespace,所以一个Pod内的多个容器之间可以通过localhost来进行通信,所需要注意的是不同容器要注意不要有端口冲突即可。不同的Pod有不同的IP,不同Pod内的多个容器之前通信,不可以使用IPC(如果没有特殊指定的话)通信,通常情况下使用Pod的IP进行通信。

k8s要求底层网络支持集群内任意两个pod直接的TCP/IP直接通信,这通常才有虚拟二层网络技术来实现,例如Flannel,Openswitch等。

一个Pod里的多个容器可以共享存储卷,这个存储卷会被定义为Pod的一部分,并且可以挂载到该Pod里的所有容器的文件系统上。

2 生命周期短暂

Pod属于生命周期比较短暂的组件,比如,当Pod所在节点发生故障,那么该节点上的Pod会被调度到其他节点,但需要注意的是,被重新调度的Pod是一个全新的Pod,跟之前的Pod没有半毛钱关系。

3  平坦的网络

K8s集群中的所有Pod都在同一个共享网络地址空间中,也就是说每个Pod都可以通过其他Pod的IP地址来实现访问。

1.3 Pod使用和管理

1、核心原则是:将多个应用分散到多个Pod中

原因:基于资源的合理应用;扩缩容,不同应用应该有不同的扩缩容策略等。

如果容器之间不是必须运行在一起的话,那么就放到不同的Pod里

如果容器之前是相互独立的组件,那么就放到不同的Pod里

如果容器之前扩缩容策略不一样,那么就放到不同的Pod里

结论:单Pod单容器应用,除非特殊原因。

你很少会直接在kubernetes中创建单个Pod。因为Pod的生命周期是短暂的,用后即焚的实体。当Pod被创建后(不论是由你直接创建还是被其他Controller),都会被Kubernetes调度到集群的Node上。直到Pod的进程终止、被删掉、因为缺少资源而被驱逐、或者Node故障之前这个Pod都会一直保持在那个Node上。

注意:重启Pod中的容器跟重启Pod不是一回事。Pod只提供容器的运行环境并保持容器的运行状态,重启容器不会造成Pod重启。

Pod不会自愈。如果Pod运行的Node故障,或者是调度器本身故障,这个Pod就会被删除。同样的,如果Pod所在Node缺少资源或者Pod处于维护状态,Pod也会被驱逐。Kubernetes使用更高级的称为Controller的抽象层,来管理Pod实例。虽然可以直接使用Pod,但是在Kubernetes中通常是使用Controller来管理Pod的。

1.4、Pod和Controller

Controller可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力。例如,如果一个Node故障,Controller就能自动将该节点上的Pod调度到其他健康的Node上。

包含一个或者多个Pod的Controller示例:

通常,Controller会用你提供的Pod Template来创建相应的Pod。

在用户定义范围内,如果pod增多,则ReplicationController会终止额外的pod,如果减少,RC会创建新的pod,始终保持在定义范围。例如,RC会在Pod维护(例如内核升级)后在节点上重新创建新Pod。

二、Pod定义

对Pod的定义可以通过Yaml或Json格式的配置文件来完成。关于Yaml或Json中都能写哪些参数,参考官网 http://kubernetes.io/docs/user-guide/pods/multi-container/

Pod的yaml整体文件内容及功能注解如下:

# yaml格式的pod定义文件完整内容:

apiVersion: v1 #必选 ,版本号,例如v1

kind: Pod       #必选 ,Pod

metadata:       #必选 ,元数据

name: string       #必选 ,Pod名称

namespace: string    #必选 ,Pod所属的命名空间

labels:      #自定义标签

- name: string     #自定义标签名字

annotations:       #自定义注释列表

- name: string

spec:         #必选 ,Pod中容器的详细定义

containers:      #必选 ,Pod中容器列表

- name: string     #必选 ,容器名称

image: string    #必选 ,容器的镜像名称

imagePullPolicy: [Always | Never | IfNotPresent] #获取镜像的策略 Alawys表示下载镜像 IfnotPresent表示优先使用本地镜像,否则下载镜像,Nerver表示仅使用本地镜像

command: [string]    #容器的启动命令列表,如不指定,使用打包时使用的启动命令

args: [string]     #容器的启动命令参数列表

workingDir: string     #容器的工作目录

volumeMounts:    #挂载到容器内部的存储卷配置

- name: string     #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名

mountPath: string    #存储卷在容器内mount的绝对路径,应少于512字符

readOnly: boolean    #是否为只读模式

ports:       #需要暴露的端口库号列表

- name: string     #端口号名称

containerPort: int   #容器需要监听的端口号

hostPort: int    #容器所在主机需要监听的端口号,默认与Container相同

protocol: string     #端口协议,支持TCP和UDP,默认TCP

env:       #容器运行前需设置的环境变量列表

- name: string     #环境变量名称

value: string    #环境变量的值

resources:       #资源限制和请求的设置

limits:      #资源限制的设置

cpu: string    #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数

memory: string     #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数

requests:      #资源请求的设置

cpu: string    #Cpu请求,容器启动的初始可用数量

memory: string     #内存清楚,容器启动的初始可用数量

livenessProbe:     #对Pod内个容器健康检查的设置,当探测无响应几次后将自动重启该容器,检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可

exec:      #对Pod容器内检查方式设置为exec方式

command: [string]  #exec方式需要制定的命令或脚本

httpGet:       #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port

path: string

port: number

host: string

scheme: string

HttpHeaders:

- name: string

value: string

tcpSocket:     #对Pod内个容器健康检查方式设置为tcpSocket方式

port: number

initialDelaySeconds: 0  #容器启动完成后首次探测的时间,单位为秒

timeoutSeconds: 0   #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒

periodSeconds: 0    #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次

successThreshold: 0

failureThreshold: 0

securityContext:

privileged:false

restartPolicy: [Always | Never | OnFailure]#Pod的重启策略,Always表示一旦不管以何种方式终止运行,kubelet都将重启,OnFailure表示只有Pod以非0退出码退出才重启,Nerver表示不再重启该Pod

nodeSelector: obeject  #设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定

imagePullSecrets:    #Pull镜像时使用的secret名称,以key:secretkey格式指定

- name: string

hostNetwork:false      #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络

volumes:       #在该pod上定义共享存储卷列表

- name: string     #共享存储卷名称 (volumes类型有很多种)

emptyDir: {}     #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值

hostPath: string     #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录

path: string     #Pod所在宿主机的目录,将被用于同期中mount的目录

secret:      #类型为secret的存储卷,挂载集群与定义的secre对象到容器内部

scretname: string  

items:     

- key: string

path: string

configMap:     #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部

name: string

items:

- key: string

path: string   

三、Pod使用

在使用docker时,我们可以使用docker run命令创建并启动一个容器,而在Kubernetes系统中对长时间运行的容器要求是:其主程序需要一直在前台运行。如果我们创建的docker镜像的启动命令是后台执行程序,例如Linux脚本:

nohup ./startup.sh &

则kubelet创建包含这个容器的pod后运行完该命令,即认为Pod执行结束,之后根据RC中定义的pod的replicas副本数量生产一个新的pod,而一旦创建出新的pod,将在执行完命令后陷入无限循环的过程中,这就是Kubernetes需要我们创建的docker镜像以一个前台命令作为启动命令的原因。

对于无法改造为前台执行的应用,也可以使用开源工具supervisor辅助进行前台运行的功能。

Pod可以由一个或多个容器组合而成

场景1:单个应用多个容器

spring boot web:

apiVersion:v1  kind: Pod  metadata:    name:  springbootweb    label:      name:  test  spec:    containers:    - name: springbootweb      image: registry.tuling123.com/springboot:latest      ports:      - containerPort: 9081
kubectl create -f springboot-deployment.yml
 

[root@k8s-master pod]# kubectl get pods

NAME            READY     STATUS    RESTARTS   AGE

springbootweb   0/1       Pending   0          1m

kubectl get pods -o wide

# 加入 –o wide参数 查看额外信息:包括node和ip

pod处于pending的原因:通过kubectl describe pods springbootweb进一步查找问题。

可以看到pod的镜像信息写错了:

EnUFRnZ.png!web

先删除pod,然后再创建:kubectl delete pod  springbootweb

Nbymeyv.png!web

由于创建的端口号是9081,可以直接访问:curl 10.0.86.2:9081

# curl 10.0.86.2:9081

Hello world

bUbQVbZ.png!web

场景2:Pod不同应用多个容器组合而成

例如:两个容器应用的前端frontend和redis为紧耦合的关系,应该组合成一个整体对外提供服务,则应该将这两个打包为一个pod.

Ubye2q6.png!web

配置文件frontend-localredis-pod.yaml如下:

apiVersion:v1  kind: Pod  metadata:    name: redis-php    label:      name: redis-php  spec:    containers:    - name: frontend      image: kubeguide/guestbook-php-frontend:localredis      ports:      - containersPort: 80    - name: redis-php      image:kubeguide/redis-master      ports:      - containersPort: 6379

属于一个Pod的多个容器应用之间相互访问只需要通过localhost就可以通信,这一组容器被绑定在一个环境中。

使用kubectl create创建该Pod后,get Pod信息可以看到如下图:

#kubectl get gods

NAME READY STATUS RESTATS AGE

redis-php 2 /2 Running 0 10m

可以看到READY信息为2/2,表示Pod中的两个容器都成功运行了.

2.3 集群外部访问Pod

上面的例子,在k8s集群的安装有kube-proxy的node节点上,可以直接通过curl 10.0.86.2:9081 访问集群的pod。但在集群外的客户端系统无法通过Pod的IP地址或者Service的虚拟IP地址和虚拟端口号访问到它们。为了让外部客户端可以访问这些服务,可以将Pod或Service的端口号映射到宿主机,以使得客户端应用能够通过物理机访问容器应用。

1、将容器应用的端口号映射到物理机

apiVersion:v1  kind: Pod  metadata:    name:  springbootweb    label:      name:  test  spec:    containers:    - name: springbootweb      image: registry.tuling123.com/springboot:latest      ports:      - containerPort:9081        hostPort: 9082

(2)通过设置Pod级别的hostNetwork-true,该Pod中所有容器的端口号都将被直接映射到物理机上。设置hostNetwork-true时需要注意,在容器的ports定义部分如果不指定hostPort,则默认hostPort等于containerPort,如果指定了hostPort,则hostPort必须等于containerPort的值。

apiVersion:v1  kind: Pod  metadata:    name:  springbootweb    label:      name:  test  spec:    hostNetwork: true    containers:    - name: springbootweb      image: registry.tuling123.com/springboot:latest      ports:      - containerPort:9081

四、静态Pod

静态pod是由kubelet进行管理的仅存在于特定Node的Pod上,他们不能通过API Server进行管理,无法与ReplicationController、Deployment或者DaemonSet进行关联,并且kubelet无法对他们进行健康检查。静态Pod总是由kubelet进行创建,并且总是在kubelet所在的Node上运行。

创建静态Pod有两种方式:配置文件或者HTTP方式

1)配置文件方式

首先,需要设置kubelet的启动参数"--config",指定kubelet需要监控的配置文件所在的目录,kubelet会定期扫描该目录,冰根据目录中的 .yaml或 .json文件进行创建操作

假设配置目录为/etc/kubelet.d/配置启动参数:--config=/etc/kubelet.d/,然后重启kubelet服务后,再宿主机受用docker ps或者在Kubernetes Master上都可以看到指定的容器在列表中

由于静态pod无法通过API Server直接管理,所以在master节点尝试删除该pod,会将其变为pending状态,也不会被删除

#kubetctl delete pod static-web-node1

pod "static-web-node1" deleted

#kubectl get pods

NAME READY STATUS RESTARTS AGE

static-web-node1 0 /1 Pending 0 1s

要删除该pod的操作只能在其所在的Node上操作,将其定义的.yaml文件从/etc/kubelet.d/目录下删除

#rm -f /etc/kubelet.d/static-web.yaml

#docker ps

五、Pod容器共享Volume

Volume类型包括:emtyDir、hostPath、gcePersistentDisk、awsElasticBlockStore、gitRepo、secret、nfs、scsi、glusterfs、persistentVolumeClaim、rbd、flexVolume、cinder、cephfs、flocker、downwardAPI、fc、azureFile、configMap、vsphereVolume等等,可以定义多个Volume,每个Volume的name保持唯一。在同一个pod中的多个容器能够共享pod级别的存储卷Volume。Volume可以定义为各种类型,多个容器各自进行挂载操作,讲一个Volume挂载为容器内需要的目录。

如下图:

yArieyJ.png!web

如上图中的Pod中包含两个容器:tomcat和busybox,在pod级别设置Volume “app-logs”,用于tomcat想其中写日志文件,busybox读日志文件。

配置文件如下:

apiVersion:v1  kind: Pod  metadata:    name: redis-php    label:      name: volume-pod  spec:    containers:    - name: tomcat      image: tomcat      ports:      - containersPort: 8080      volumeMounts:      - name: app-logs        mountPath:/usr/local/tomcat/logs    - name: busybox      image:busybox      command: ["sh","-C","tail -f /logs/catalina*.log"]    volumes:    - name: app-logs      emptyDir:{}

busybox容器可以通过kubectl logs查看输出内容

#kubectl logs volume-pod -c busybox

tomcat容器生成的日志文件可以登录容器查看

#kubectl exec -ti volume-pod -c tomcat -- ls /usr/local/tomcat/logs

六.Pod的配置管理

应用部署的一个最佳实践是将应用所需的配置信息于程序进行分离,这样可以使得应用程序被更好的复用,通过不用配置文件也能实现更灵活的功能。将应用打包为容器镜像后,可以通过环境变量或外挂文件的方式在创建容器时进行配置注入。ConfigMap是Kubernetes v1.2版本开始提供的一种统一集群配置管理方案。

6.1 ConfigMap:容器应用的配置管理

容器使用ConfigMap的典型用法如下:

(1)生产为容器的环境变量。

(2)设置容器启动命令的启动参数(需设置为环境变量)。

(3)以Volume的形式挂载为容器内部的文件或目录。

ConfigMap以一个或多个key:value的形式保存在Kubernetes系统中共应用使用,既可以用于表示一个变量的值,也可以表示一个完整的配置文件内容。

通过yuaml配置文件或者直接使用kubelet create configmap 命令的方式来创建ConfigMap

6.2 ConfigMap的创建

举个小例子cm-appvars.yaml来描述将几个应用所需的变量定义为ConfigMap的用法:

# vim cm-appvars.yaml

apiVersion: v1

kind: ConfigMap

metadata:

name: cm-appvars

data:

apploglevel: info

appdatadir:/var/data

执行kubectl create命令创建该ConfigMap

#kubectl create -f cm-appvars.yaml

configmap "cm-appvars.yaml" created

查看建立好的ConfigMap:

#kubectl get configmap

kubectl describe configmap cm-appvars

kubectl get configmap cm-appvars -o yaml

另:创建一个cm-appconfigfile.yaml描述将两个配置文件server.xml和logging.properties定义为configmap的用法,设置key为配置文件的别名,value则是配置文件的文本内容:

    apiVersion: v1      kind: ConfigMap      metadata:        name: cm-appvars      data:        key-serverxml:          <?xml Version='1.0'encoding='utf-8'?>          <Server port="8005"shutdown="SHUTDOWN">          .....            </service>          </Server>        key-loggingproperties:          "handlers=lcatalina.org.apache.juli.FileHandler,          ...."

在pod "cm-test-app"定义中,将configmap "cm-appconfigfile"中的内容以文件形式mount到容器内部configfiles目录中。

Pod配置文件cm-test-app.yaml内容如下:

#vim cm-test-app.yaml

apiVersion: v1

kind: Pod

metadata:

name: cm-test-app

spec:

containers:

- name: cm-test-app

image: tomcat-app:v1

ports:

- containerPort: 8080

volumeMounts:

- name: serverxml                          #引用volume名

mountPath:/configfiles                       #挂载到容器内部目录

configMap:

name: cm-test-appconfigfile                  #使用configmap定义的的cm-appconfigfile

items:

- key: key-serverxml                     #将key=key-serverxml

path: server.xml                           #value将server.xml文件名进行挂载

- key: key-loggingproperties                 #将key=key-loggingproperties    

path: logging.properties                   #value将logging.properties文件名进行挂载

创建该Pod:

#kubectl create -f cm-test-app.yaml

Pod "cm-test-app"created

登录容器查看configfiles目录下的server.xml和logging.properties文件,他们的内容就是configmap “cm-appconfigfile”中定义的两个key的内容

#kubectl exec -ti cm-test-app -- bash

root@cm-rest-app:/# cat /configfiles/server.xml

root@cm-rest-app:/# cat /configfiles/logging.properties

6.3使用ConfigMap的条件限制

使用configmap的限制条件如下:

  • configmap必须在pod之间创建
  • configmap也可以定义为属于某个Namespace,只有处于相同namespaces中的pod可以引用
  • configmap中配额管理还未能实现
  • kubelet只支持被api server管理的pod使用configmap,静态pod无法引用
  • 在pod对configmap进行挂载操作时,容器内部职能挂载为目录,无法挂载文件。

七、Pod生命周期和重启策略

Pod在整个生命周期过程中被定义为各种状态,熟悉Pod的各种状态有助于理解如何设置Pod的调度策略、重启策略

Pod的状态包含以下几种,如图:

3eQfyuf.png!web

Pod的重启策略(RestartPolicy)应用于Pod内所有的容器,并且仅在Pod所处的Node上由kubelet进行判断和重启操作。当某哥容器异常退出或者健康检查石柏师,kubelet将根据RestartPolicy的设置进行相应的操作

Pod的重启策略包括Always、OnFailure及Nerver,默认值为Always。

kubelet重启失效容器的时间间隔以sync-frequency乘以2n来计算,例如1、2、4、8倍等,最长延时5分钟,并且成功重启后的10分钟后重置该事件。

Pod的重启策略和控制方式息息相关,当前可用于管理Pod的控制器宝库ReplicationController、Job、DaemonSet及直接通过kubelet管理(静态Pod),每种控制器对Pod的重启策略要求如下:

  • RC和DaemonSet:必须设置为Always,需要保证该容器持续运行
  • Job:OnFailure或Nerver,确保容器执行完成后不再重启
  • kubelet:在Pod失效时重启他,不论RestartPolicy设置什么值,并且也不会对Pod进行健康检查

八、Pod健康检查

对Pod的健康检查可以通过两类探针来检查:LivenessProbe和ReadinessProbe

  • LivenessProbe探针:用于判断容器是否存活(running状态),如果LivenessProbe探针探测到容器不健康,则kubelet杀掉该容器,并根据容器的重启策略做响应处理
  • ReadinessProbe探针:用于判断容器是否启动完成(ready状态),可以接受请求。如果ReadinessProbe探针探测失败,则Pod的状态被修改。Endpoint Controller将从service的Endpoint中删除包含该容器所在的Pod的Endpoint。

kubelet定制执行LivenessProbe探针来诊断容器的健康状况。LivenessProbe有三种事项方式。

1)ExecAction:在容器内部执行一个命令,如果该命令的返回值为0,则表示容器健康。例:

apiVersion:v1  kind: Pod  metadata:    name: liveness-exec    label:      name: liveness  spec:    containers:    - name: tomcat      image: grc.io/google_containers/tomcat      args:      -/bin/sh      - -c      -echo ok >/tmp.health;sleep10; rm -fr /tmp/health;sleep600      livenessProbe:        exec:          command:          -cat          -/tmp/health        initianDelaySeconds:15        timeoutSeconds:1

(2)TCPSocketAction:通过容器ip地址和端口号执行TCP检查,如果能够建立tcp连接表明容器健康。例:

kind: Pod  metadata:    name: pod-with-healthcheck  spec:    containers:    - name: nginx      image: nginx      livenessProbe:        tcpSocket:          port: 80        initianDelaySeconds:30        timeoutSeconds:1

3)HTTPGetAction:通过容器Ip地址、端口号及路径调用http get方法,如果响应的状态吗大于200且小于400,则认为容器健康。例:

apiVersion:v1  kind: Pod  metadata:    name: pod-with-healthcheck  spec:    containers:    - name: nginx      image: nginx      livenessProbe:        httpGet:          path:/_status/healthz          port: 80        initianDelaySeconds:30        timeoutSeconds:1

对于每种探针方式,都需要设置initialDelaySeconds和timeoutSeconds两个参数,它们含义如下:

  • initialDelaySeconds:启动容器后首次监控检查的等待时间,单位秒
  • timeouSeconds:健康检查发送请求后等待响应的超时时间,单位秒。当发生超时就被认为容器无法提供服务无,该容器将被重启

九.玩转Pod调度

在Kubernetes系统中,Pod在大部分场景下都只是容器的载体而已,通常需要通过RC、Deployment、DaemonSet、Job等对象来完成Pod的调度和自动控制功能。

9.1 RC、Deployment:全自动调度

RC的主要功能之一就是自动部署容器应用的多份副本,以及持续监控副本的数量,在集群内始终维护用户指定的副本数量。

在调度策略上,除了使用系统内置的调度算法选择合适的Node进行调度,也可以在Pod的定义中使用NodeSelector或NodeAffinity来指定满足条件的Node进行调度。

1)NodeSelector:定向调度

Kubernetes Master上的scheduler服务(kube-Scheduler进程)负责实现Pod的调度,整个过程通过一系列复杂的算法,最终为每个Pod计算出一个最佳的目标节点,通常我们无法知道Pod最终会被调度到哪个节点上。实际情况中,我们需要将Pod调度到我们指定的节点上,可以通过Node的标签和pod的nodeSelector属性相匹配来达到目的。

(1)首先通过kubectl label命令给目标Node打上标签

kubectl label nodes <node-name> <label-key>=<label-value>

例: #kubectllabel nodes k8s-node-1 zonenorth

(2)然后在Pod定义中加上nodeSelector的设置,例:

apiVersion:v1  kind: Pod  metadata:    name: redis-master    label:      name: redis-master  spec:    replicas: 1    selector:      name: redis-master      template:        metadata:          labels:            name: redis-master        spec:          containers:          - name: redis-master            images: kubeguide/redis-master            ports:            - containerPort: 6379          nodeSelector:            zone: north

运行kubectl create -f命令创建Pod,scheduler就会将该Pod调度到拥有zone=north标签的Node上。

如果多个Node拥有该标签,则会根据调度算法在该组Node上选一个可用的进行Pod调度。

需要注意的是:如果集群中没有拥有该标签的Node,则这个Pod也无法被成功调度。

2)NodeAffinity:亲和性调度

该调度策略是将来替换NodeSelector的新一代调度策略。由于NodeSelector通过Node的Label进行精确匹配,所有NodeAffinity增加了In、NotIn、Exists、DoesNotexist、Gt、Lt等操作符来选择Node。调度侧露更加灵活。

9.2 DaemonSet:特定场景调度

DaemonSet用于管理集群中每个Node上仅运行一份Pod的副本实例,如图:

IfqqIrz.png!web

这种用法适合一些有下列需求的应用:

  • 在每个Node上运行个以GlusterFS存储或者ceph存储的daemon进程
  • 在每个Node上运行一个日志采集程序,例如fluentd或者logstach
  • 在每个Node上运行一个健康程序,采集Node的性能数据。

DaemonSet的Pod调度策略类似于RC,除了使用系统内置的算法在每台Node上进行调度,也可以在Pod的定义中使用NodeSelector或NodeAffinity来指定满足条件的Node范围来进行调度。

十.Pod的扩容和缩容

在实际生产环境中,我们经常遇到某个服务需要扩容的场景,也有可能因为资源精确需要缩减资源而需要减少服务实例数量,此时我们可以Kubernetes中RC提供scale机制来完成这些工作。

以redis-slave RC为例,已定义的最初副本数量为2,通过kubectl scale命令可以将Pod副本数量重新调整

#kubectl scale rc redis-slave --replicas=3  ReplicationController"redis-slave" scaled  #kubectl get pods  NAME READY STATUS RESTARTS AGE  redis-slave-1sf23 1/1Running 0 1h  redis-slave-54wfk 1/1Running 0 1h  redis-slave-3da5y 1/1Running 0 1h

除了可以手工通过kubectl scale命令完成Pod的扩容和缩容操作以外,新版本新增加了Horizontal Podautoscaler(HPA)的控制器,用于实现基于CPU使用路进行启动Pod扩容缩容的功能。该控制器基于Mastger的kube-controller-manager服务启动参数 --horizontal-pod-autoscler-sync-period定义的时长(默认30秒),周期性监控目标Pod的Cpu使用率并在满足条件时对ReplicationController或Deployment中的Pod副本数量进行调整,以符合用户定义的平均Pod Cpu使用率,Pod Cpu使用率来源于heapster组件,所以需预先安装好heapster。

十一.Pod的滚动升级

当集群中的某个服务需要升级时,我们需要停止目前与该服务相关的所有Pod,然后重新拉取镜像并启动。如果集群规模较大,因服务全部停止后升级的方式将导致长时间的服务不可用。由此,Kubernetes提供了rolling-update(滚动升级)功能来解决该问题。

滚动升级通过执行kubectl rolling-update命令一键完成,该命令创建一个新的RC,然后自动控制旧版本的Pod数量逐渐减少到0,同时新的RC中的Pod副本数量从0逐步增加到目标值,最终实现Pod的升级。需要注意的是,系统要求新的RC需要与旧的RC在相同的Namespace内,即不能把别人的资产转到到自家名下。

例:将redis-master从1.0版本升级到2.0:

apiVersion: v1  kind: replicationController  metadata:    name: redis-master-v2    labels:      name: redis-master      Version: v2  spec:    replicas: 1    selector:      name: redis-master      Version: v2    template:      labels:        name: redis-master        Version: v2      spec:        containers:        - name: master          images: kubeguide/redis-master:2.0          ports:          - containerPort: 6379

需要注意的点:

(1)RC的name不能与旧的RC名字相同

(2)在sele中应至少有一个label与旧的RC的label不同,以标识为新的RC。本例中新增了一个名为version的label与旧的RC区分

运行kubectl rolling-update来完成Pod的滚动升级:

#kubectl rolling-update redis-master -f redis-master-controller-v2.yaml

另一种方法就是不使用配置文件,直接用kubectl rolling-update加上--image参数指定新版镜像名来完成Pod的滚动升级

#kubectl rolling-update redis-master --image=redis-master:2.0

与使用配置文件的方式不同的是,执行的结果是旧的RC被删除,新的RC仍然使用就的RC的名字。

如果在更新过程总发现配置有误,则用户可以中断更新操作,并通过执行kubectl rolling-update-rollback完成Pod版本的回滚。

问题

1、在创建pod的时候发现报了这个错误:

Error from server (ServerTimeout): error when creating "busybox.yaml": No API token found for

service account "default", retry after the token is automatically created and added to the service

分析

根据报错信息可以初步看出是service account没有设置API token引起的。

解决

解决方式有两种:

方式一:禁用ServiceAccount

编辑/etc/kubenetes/apiserver:

将以下这行中的ServiceAccount删除即可

KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota"

改为:

KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ResourceQuota"

这种方式比较粗暴,可能会遇到必须要用ServiceAccount的情况。

方式二:配置ServiceAccount

1、首先生成密钥:

openssl genrsa -out /etc/kubernetes/serviceaccount.key 2048

2、编辑/etc/kubenetes/apiserver

添加以下内容:

KUBE_API_ARGS="--service_account_key_file=/etc/kubernetes/serviceaccount.key"

3、再编辑/etc/kubernetes/controller-manager

添加以下内容:

KUBE_CONTROLLER_MANAGER_ARGS="--service_account_private_key_file=/etc/kubernetes/serviceaccount.key"

最后无论是哪种解决方式都需要再重启kubernetes服务:

systemctl restart etcd kube-apiserver kube-controller-manager kube-scheduler


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK