kubernetes之pod超详细解读--第一篇

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kubernetes之pod超详细解读--第一篇相关的知识,希望对你有一定的参考价值。

   小编在这里向各位博友道个歉,上篇文章确实写的有些应付,但怎么说,部署确实因人而异,而且很少有刚刚进公司就让你搭建一个集群,一般公司都有自己的集群,所以小编觉得,侧重点不应该在安装,应该在维护!虽然有些牵强,但小编保证,这一篇绝对有质量!希望看了小编的博客,大家对pod有更深入的认识。
   这篇文章,小编打算介绍关于pod的11个重要的知识点,大家要有耐心的看下去哦!虽然内容比较多,有兴趣的朋友可以细细阅读,小编会尽可能的用比较容易理解的话和图,去介绍比较重要并且难以理解的地方。

1. pod的基本定义和用法

  首先我们通过yaml定义的方式看看pod中可以定义哪些内容:

apiVersion: v1  #版本号
kind: Pod       #资源对象类型
metadata:       #元数据
  name: string  #pod的名称
  namespace: string  #pod所属的命名空间
  labels:  #自定义标签列表
    - name: string
  annotations:  #自定义注解列表
    - name: string
spec:  #pod的容器的详细定义
  containers:  
  - name: string   #容器的名称
    image: string  #容器的镜像
    imagePullPolicy: [Always|Never|IfNotPresent]  #镜像获取策略
    command: [string]   #容器的启动命令列表
    args: [string]      #启动命令参数
    workingDir: string  #容器的工作目录
    volumeMounts:       #挂载到容器内部的存储卷配置
    - name: string
      mountPath: string #挂载的目录
      readOnly: boolean #是否只读挂载
    ports:  #容器暴露的端口列表
    - name: string  #端口名称
      containerPort: int  #容器监听的端口
      hostPort: int  #容器所在主机需要监听的端口
      protocol: string  #端口协议
    env: #容器中的环境变量
    - name: string 
      value: string
    resources: #资源限制设置
      limits: #最大使用资源
        cpu: string
        memory: string
      requests: #请求时资源设置
        cpu: string
        memory: string
    livenessProbe:  #对容器的健康检查
      exec:  #通过命令的返回值
        command: [string]
      httpGet: #通过访问容器的端口的返回的状态码
        path: string
        port: number
        host: string
        scheme: string
        httpHeaders:
        - name: string
          value: string
      tcpSocket:  #通过tcpSocket
        port: number
      initialDelaySeconds: 0 #首次健康检查时间
      timeoutSeconds: 0  #健康检查的超时时间
      periodSeconds: 0  #每次健康检查的时间间隔
      successThreshold: 0 
      failureThreshold: 0
    securityContext:
      privileged: false
  restartPolicy: [Always|Never|OnFailure] #pod的重启策略
  nodeSelector: object  #指定的运行pod的node标签
  imagePullSecrets:  
  - name: string
  hostNetwork: false  #是否使用主机网络模式
  volumes:  #该pod上定义的共享存储卷列表
  - name: string
    emptyDir: {}  #临时挂载
    hostPath:  #挂载宿主机目录
      path: string
    secret: #类型为secret存储卷
      secretName: string
      items:
      - key: string
        path: string
    configMap: #类型为configMap的存储卷
      name: string
      items:
      - key: string
        path: string

是不是一下子看了这么多配置,感觉有点蒙蒙的,不要着急,先苦后甜,小编接下来会一一介绍如何使用这些配置,以及这些配置有什么作用。
Pod的基本用法:
  当然这里小编要强调的是,如果自己定义的image,并且image运行的程序的脚本是后台调度运行的例:nohup ./start.sh,类似这样的,如果是在docker中可以使用docker run的方式创建并启动这个容器,当时在kubernetes中,类似这样后台的程序,kubelet创建这个容器的pod之后,运行完该命令,就认为pod执行结束,将立刻销毁这个pod,如果给这个pod绑定了RC,那么系统将会根据RC中的pod的副本数重新启动这个pod,这样下去会进入无限的死循环中,当然问题出现了肯定会有解决办法,这里小编向大家介绍一个服务supervisor,说实话,小编也不是很懂它,有时间再把它玩一玩,这里小编先介绍一下它是如何让这些后台启动的容器持续运行,并满足kubernetes对容器启动的要求:supervisor提供了一种可以同时启动多个后台进程,并保持supervisor自身在前台执行的机制。(好吧我知道是废话,但是很通俗易懂,能就这样,强行安慰自己,感觉很nice)。
  接下来创建几个pod,看看效果,这里pod可以由一个或者多个container组成,先创建一个只有一个container的:

#frontend-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: frontend
  labels:
    name: frontend
spec:
  containers:
  - name: frontend
    image: docker.io/kubeguide/guestbook-php-frontend
    env:
    - name: GET_HOSTS_FROM
      value: env
    ports: 
    - containerPort: 80
  hostNetwork: true
[[email protected] yaml_file]# kubectl create -f frontend-pod.yaml  #创建这个pod 

创建一个两个container组合成一个整体的pod对外提供服务:
技术图片

apiVersion: v1
kind: Pod
metadata:
  name: redis-php
  labels:
    name: redis-php
spec:
  containers:
  - name: frontend
    image: docker.io/kubeguide/guestbook-php-frontend:localredis
    ports: 
    - containerPort: 80
  - name: redis
    image: docker.io/kubeguide/redis-master
    ports:
    - containerPort: 6379

技术图片
  这里有一个container,死了,可能是镜像出了问题,当然重点不在这里,主要是演示如何运行2个容器在一个pod中有兴趣的可以解决一下这个问题:
技术图片

2. 静态pod

  所谓的静态的pod就是通过kubelet管理并且仅存在于特定的node上的pod,它们不能通过API server 进行管理,也无法与RC、deployment、daemonSet进行关联,并且kubelet也不能对其进行健康检查。他们总是由kubelet创建,并且总在kubelet所在node上运行。其中创建静态的pod有两种方式:配置文件方式HTTP方式
① 配置文件方式
这里我的kubelet的配置文件在/etc/kubernetes/kubelet
技术图片
然后在配置文件中加入:--config=/k8s/yaml_file/static-web.yaml

#static-web.yaml
apiVersion: v1
kind: Pod
metadata: 
  name: static-web
  labels:
    name: static-web
spec:
  containers:
  - name: static-web
    image: nginx
    ports:
    - name: web
      containerPort: 80
[[email protected] yaml_file]# systemctl restart kubelet  #重启kubelet服务
[[email protected] yaml_file]# docker ps #查看本机的容器:docker ps

技术图片
技术图片
  此时这个静态的pod就正在创建,并且使用kubectl delete pod pod_name删除不了这个pod的,只能让这个pod处于pending状态,当然只要将该node上的定义这个pod的文件删除,就能将这个pod从这个node上删除了。
② HTTP方式:因为简单这里就不在演示,只要在kubelet的配置文件中加入:--manifest-url=xxx,kubelet就会定期的从指定的URL中下载pod的定义文件(yaml|json),然后根据定义在自己的node上创建pod。

3.pod容器共享volume

  这里的配置管理,就是将程序和配置分离,使程序更加灵活,一般的我们在打包应用程序为镜像,可以通过环境变量或者外挂volume的方式在创建容器的时候进行配置注入,但是如果机器规模比较大的时候,对多容器进行不同的配置注入将十分复杂,所以这里我们介绍一种便捷方式configMap。
configMap典型使用场景:
? 生成容器内部的环境变量
? 设置容器的启动命令的参数
? 以volume的形式挂载为容器内部的文件或者目录
在同一个pod中的多个容器之间能够共享pod级别的存储卷volume,volume可以被定义为各种类型,多个容器之间进行各自挂载,将一个volume挂载为容器内部的目录:
技术图片
上图就是一个pod中有两个容器同时挂载一个volume共享,其中实现的功能为:tomcat用于向其中写日志,busybox用于向其中读取日志:

#pod-volume-applogs.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
  - name: tomcat
imagePullPolicy: IfNotPresent
    image: docker.io/tomcat
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: app-logs
      mountPath: /usr/local/tomcat/logs
  - name: logreader
image: docker.io/busybox
imagePullPolicy: IfNotPresent
    command: ["sh","-c","tail -f /logs/catalina*.log"]
    volumeMounts:
    - name: app-logs
      mountPath: /logs
  volumes:
  - name: app-logs
    emptyDir: {}

注意 :这里名称为volume-pod的pod中的两个容器,同时挂载了类型为emptyDir这种临时目录,并且在容器logreader中打印Tomcat容器生成的日志,我们通过命令查看:

[[email protected] yaml_file]# kubectl logs volume-pod -c logreader

技术图片

4. pod的配置管理

  这里的配置管理,就是将程序和配置分离,使程序更加灵活,一般的我们在打包应用程序为镜像,可以通过环境变量或者外挂volume的方式在创建容器的时候进行配置注入,但是如果机器规模比较大的时候,对多容器进行不同的配置注入将十分复杂,所以这里我们介绍一种便捷方式configMap。
configMap典型使用场景:
 ? 生成容器内部的环境变量
 ? 设置容器的启动命令的参数
 ? 以volume的形式挂载为容器内部的文件或者目录
configMap用法:configMap以key-value的形式定义,这里的value可以是string也可以是一个文件内容,通过kubectl create configmap 的方式创建configMap。
这里小编通过这个几个方面去介绍configMap的用法:
 ? configMap的两种创建方式:基于yaml文件或者基于命令
 ? configMap的两种使用方式:环境变量volume挂载
configMap的创建:
① 基于yaml文件

#cm-appvars.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-aoovars
data:
  apploglevel: info
  appdatadir: /var/data
[[email protected] yaml_file]# kubectl create  -f  cm-appvars.yaml #创建configmap
[[email protected] yaml_file]# kubectl get configmap  #查看创建的configmap

技术图片

[[email protected] yaml_file]# kubectl describe configmap cm-aoovars  #查看configmap定义的内容

技术图片

#cm-appconfigfiles.yaml(value为文件内容)
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-appconfigfiles
data:
  key-serverxml: |
    xml文件内容
  key-loggingproperties: "配置文件内容"
[[email protected] yaml_file]# kubectl get configmap cm-appconfigfiles -o yaml  #查看详细内容

技术图片
② 基于命令

[[email protected] yaml_file]# kubectl create configmap cm-figmap --from-file=server.xml

注意:这里会将文件的名称为key,内容为value,如果目录下有多个文件,则同时创建,并且文件的名称为key,内容为value。

[[email protected] yaml_file]# kubectl create configmap cm-figmap --from-literal=loglevel=info --fromliteral=appdatadir=/var/data

注意:这种方式是指定具体的key-value创建configmap
configMap的使用:
① 通过环境变量的方式使用configmap

apiVersion: v1
kind: Pod
metadata:
  name: cm-test-pod
spec:
  containers:
  - name: busybox
    image: docker.io/busybox
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","env|grep APP"]
    env:
    - name: APPLOGLEVEL  #定义环境变量
      valueFrom:
        configMapKeyRef: 
          name: configMapNAME
          key: configMap_KEY
    - name: APPLOGLEVEL  #定义环境变量
      valueFrom:
        configMapKeyRef: 
          name: configMapNAME
          key: configMap_KEY

在kubernetes1.6之后,有一种简单的定义env的方式:

envFrom:
- configMapRef:
  name: configMapNAME

② 通过volumeMount使用configmap
上面我们定义了一个基于文件的configmap,接下来看如何使用基于文件的configmap,挂载到具体的pod的容器的目录下:

#cm-test-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: cm-test-pod
spec:
  containers:
  - name: busybox
    image: kubeguide/tomcat-app:v1
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: serverxml  #引用volumes名称
      mountPath: /configfiles  #将confimap中的文件挂载到容器的目录中
  volumes:
  - name: serverxml  
    configMap:
      name: configMapName  #定义的configmap的名称
      items:  #需要挂载的文件的列表
      - key: key-serverxml  #configmap中定义的key
        path: server.xml   #挂载后的文件名
      - key: key-loggingproperties
        path: logging.properties

注意:如果在定义volume中的configmap时不指定items,那么volumeMount会在容器的指定目录下为每一个items生产一个文件名为key,内容为value的文件。
ConfigMap的使用限制说明:
 ? Configmap必须在pod创建之间创建
 ? ConfigMap受到namespace的限制,只有同一个命名空间下才能引用
 ? 静态的pod无法使用ConfigMap
 ? 在使用volumeMount挂载的时候,configMap基于items创建的文件会整体的将挂载数据卷的容器的目录下的文件全部覆盖

5.在容器中获取pod的信息

  在我们创建pod后,系统就会给其分配唯一的名字、IP、并且处于某个namspace下面,那么这些信息我们如何获取呢?通过Downward API的方式。
Downward API可以通过以下得到两种方式将pod的信息导入到container中:
 ? 环境变量,用于单个变量,可以将pod信息和container信息注入到容器中
 ? Volume挂载:将数组类信息生成文件挂载到容器内部。
实例:
① 环境变量的方式

#将pod信息注入为环境变量
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: busybox
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh","-c","env"]
      env:
        - name: MY_POD_NAME
          valueFrom:
            fieldRef: 
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef: 
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef: 
              fieldPath: status.podIP
[[email protected] yaml_file]# kubectl log dapi-test-pod  #查看

技术图片

#daip-test-pod-container-vars.yaml将容器资源信息注入为环境变量
apiVersion: v1
kind: Pod
metadata:
  name: daip-test-pod-container-vars
spec:
  containers:
    - name: test-container
      image: busybox
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh","-c"]
      args:
      - while true; do
          echo -en "\n";
          printenv MY_CPU_REQUEST MY_CPU_LIMIT;
          printenv MY_MEM_REQUEST MY_MEM_LIMIT;
          sleep 60;
        done;
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "40Mi"
          cpu: "130m"
      env:
        - name: MY_CPU_REQUEST
          valueFrom:
            resourceFieldRef: 
              containerName: test-container
              resource: requests.cpu
        - name: MY_CPU_LIMIT
          valueFrom:
            resourceFieldRef: 
              containerName: test-container
              resource: requests.cpu
        - name: MY_CPU_REQUEST
          valueFrom:
            resourceFieldRef: 
              containerName: test-container
              resource: limits.cpu
        - name: MY_MEM_REQUEST
          valueFrom:
            resourceFieldRef: 
              containerName: test-container
              resource: requests.memory
        - name: MY_MEM_LIMIT
          valueFrom:
            resourceFieldRef: 
              containerName: test-container
              resource: limits.memory
[[email protected] yaml_file]# kubectl logs daip-test-pod-container-vars

技术图片

② Volume挂载方式

![](https://s1.51cto.com/images/blog/201905/18/33b1db246d451ae70c8bc2738341fea9.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)#dapi-test-pod-volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod-volume
  labels:
    zone: us-est-coast
    cluster: test-cluster
    rack: rack-22
  annotations:
    build: two
    builder: john-doe
spec:
  containers:
    - name: test-container
      image: busybox
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh","-c"]
      args:
      - while true;do
          if [[ -e /etc/labels ]];then
            echo -en "\n\n";cat /etc/labels; fi;
          if [[ -e /etc/annotations ]];then
            echo -en "\n\n";cat /etc/annotations; fi;
          sleep 60;
        done;
      volumeMounts:
        - name: podinfo
          mountPath: /etc
          readOnly: false
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: "labels"
            fieldRef:
              fieldPath: metadata.labels
          - path: "annotations"
            fieldRef:
              fieldPath: metadata.annotations

注意:通过设置items,将会以path的名称生成文件。

[[email protected] yaml_file]# kubectl logs dapi-test-pod-volume

技术图片
Downward API的作用:在某些集群中,集群中的节点需要将自身标识以及进程绑定的IP地址等信息预习写入配置文件,进程启动时读取这些信息,然后发布到类似于服务注册中心中,做集群的节点的自动发现功能。那么Downward API就大有用处,它可以先编写一个预启动脚本或者init container,通过环境变量或者文件的方式获取pod自身的名称、IP地址等信息,然后写入主程序配置中。

6.pod的声明周期和重启策略

  pod在整个声明周期中有很多状态,了解其各种状态对于集群排错大有帮助:
技术图片
  当某个pod异常退出或者健康检查失败的时候,kubelet会根据pod定义中的spec.RestartPolicy的设置来进行相应的操作。
 Pod的重启策略包括:
  ? Always:当容器失效时,由kubelet自动重启
  ? OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启
  ? Nerver:永远不重启
 Pod的重启策略,与管理其的资源对象息息相关,RC、job、Daemonset以及通过kubelet直接管理的静态的pod,每一种控制器对pod的重启策略都有要求:
  ? RC和Daemonset:必须设置为Always。
  ? Job:OnFailure或者Nerver,确保容器执行完成不在重启
  ? 静态pod:在pod失效时自动重启。(注意静态pod不会被健康检查)

7 .Pod的健康检查

  就像HDFS中的DataNode会定时发送心跳给namenode一样,k8s集群中通过探针的方式给Pod定时做健康检查,一旦符合定义的情况,就执行相应的重启策略。
 健康检查分为以下两种探针:
  ? LivenessProbe探针: 判断容器是否存活(runnning状态),如果探测到容器不健康,则kubelet杀死该容器,并且根据重启策略做相应处理,如果容器中没有LivenessProbe探针,那么kubelet认为该容器的LivenessProbe探针一直返回“Success”
  ? ReadinessProbe探针: 用于判断容器是否启动完成(ready状态),可以接受请求。如果ReadinessProbe探针探测到失败,则pod的状态被修改。并且EndPoint Controller将从service的EndPoint中删除包含该容器所在pod的EndPoint。
 接下来我们重点介绍一下LivenessProbe探针如何判断pod是否健康,这里有三种实现方式:
  ? ExecAction:在容器中执行一个命令,如果该命令返回的状态码为0,表示该容器正常
  ? TCPSocketAction:通过容器的IP地址和端口执行TCP检查,能够建立TCP连接,表示该容器正常
  ? HTTPGetAction:通过容器的IP和端口以及路径调用HTTP get方法,如果相应的状态码大于等于200并且小于400,则认为该容器正常
实例演示:
① ExecAction

apiVersion: v1
kind: Pod
metadata:
  labels:
    test:liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    images: gcr.io/google_containers/busybox
    args:
    - /bin/sh
    - -c
    - echo ok > /tmp/health; sleep 10 ;rm -rf /tmp/health;sleep 10 ;
    livenessProbe:
      exec:
        command:
        - cat 
        - /tmp/health
      initialDelaySeconds: 15  #初次健康检查时间
      timeoutSeconds: 1        #超时时间

这里注意:我们设置了livenessProbe探针初次检查时间为15秒,但是程序中将10之后删除检查的文件,这里cat /tmp/health 这个命令执行失败,状态码返回不为0,导致kubelet杀掉该进程,并根据相应的重启策略进行操作。
② TCPSocketAction

apiVersion: v1
kind: Pod
metadata:
  labels:
    test:liveness
  name: liveness-exec
spec:
  containers:
  - name: nginx
    images: nginx
    ports:
    - containerPort: 80
    livenessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 15  #初次健康检查时间
      timeoutSeconds: 1        #超时时间

③ HTTPGetAction

apiVersion: v1
kind: Pod
metadata:
  labels:
    test:liveness
  name: liveness-exec
spec:
  containers:
  - name: nginx
    images: nginx
    ports:
    - containerPort: 80
    livenessProbe:
      httpGet:
        path: /_status/helthz
        port: 80
      initialDelaySeconds: 30  #初次健康检查时间
      timeoutSeconds: 1        #超时时间

这里小编强调一下:
initialDelaySeconds:启动容器后首次健康检查的时间
timeoutSeconds:健康检查发送请求后等待相应的时间

看到这里大家一定脑子都炸了,这里小编想说一下,后面还有很多,这里小编把pod介绍分为两篇博文向大家介绍,

后续部分URL:

文章参考至《kubernetes权威指南》

以上是关于kubernetes之pod超详细解读--第一篇的主要内容,如果未能解决你的问题,请参考以下文章

Jenkins-Kubernetes插件实现使用Pod作为 Agent-超详细

kubernetes之K8s核心原理--第一篇

等保测评2.0超详细解读,收藏这一篇就够了

Kubernetes水平自动扩缩容Hpa-超详细文档

Kubernetes二进制部署(理论部分,详细部署步骤在下一篇哦~)

第一篇:《Kubernetes 入门介绍》