• Kubernetes基石-pod容器


    引用三个问题来叙述Kubernetes的pod容器

      1.为什么不直接在一个Docker容器中运行所有的应用进程。

      2.为什么pod这种容器中要同时运行多个Docker容器(可以只有一个)

      3.为什么k8s使用pod这种容器而不直接使用Docker容器

      一个由多个进程进行组成的应用程序,无论是通过ipc(进程间通信)还是本地存储文件进行通信,都要求它们运行于同一台机器上。Docker容器非常像一台独立的机器,此时你可能认为在单个容器中运行多个进程是合乎逻辑的,然而在实践中这种做法并不合理。
      容器被设计为每个容器只运行一个进程(除非进程本身产生子进程)。如果在单个容器中运行多个不相关的进程,那么保持所有进程运行、管理它们的日志等将会是我们的责任。例如,我们需要包含一种在进程奔溃时能够自动重启的机制。同时这些进程都将记录到相同的标准输出中,而此时我们将很难确定每个进程分别记录了什么
    综上所述,我们需要让每个进程运行于自己的容器中,而这就是Docker和Kubernetes期望使用的方式。
      由于不能将多个进程聚集在一个单独的容器中,我们需要另一种更高级的结构来将容器绑定在一起,并将它们作为一个单元进行管理,这就是pod背后的根本原理。
      在包含容器的pod下,我们可以同时运行一些密切相关的进程,并为它们提供(几乎)相同的环境,此时这些进程就好像全部运行于单个容器中一样,同时又保持着一定的隔离。这样一来,我们便能全面地利用容器所提供的特性,同时对这些进程来说它们就像运行在一起一样,实现两全其美。
      Docker容器之间彼此是完全隔离的,但此时我们期望的是隔离容器组,而不是单个容器,并让每个容器组内的容器共享一些资源,而不是全部(换句话说,没有完全隔离)。Kubernetes通过配置Docker来让一个pod内的所有容器共享相同的Linux命名空间,而不是每个容器都有自己的一组命名空间。
      由于一个pod中的所有容器都在相同的network和UTS命名空间下运行(在这里我们讨论的是Linux命名空间),所以它们都共享相同的主机名和网络接口。同样地,这些容器也都在相同的IPC命名空间下运行,因此能够通过IPC进行通信。在最新的Kubernetes和Docker版本中,它们也能够共享相同的PID命名空间,但是该特征默认是未激活的。
      注意:当同一个pod中的容器使用单独的PID命名空间时,在容器中执行psaux就只会看到容器自己的进程。
      但当涉及文件系统时,情况就有所不同。由于大多数容器的文件系统来自容器镜像,因此默认情况下,每个容器的文件系统与其他容器完全隔离。但我们可以使用名为Volume的Kubermetes资源来共享文件目录。
      需要强调的一点是,由于一个pod中的容器运行于相同的Network命名空间中,因此它们共享相同的IP地址和端口空间。这意味着在同一pod中的容器运行的多个进程需要注意不能绑定到相同的端口号,否则会导致端口冲突,但这只涉及同一pod中的容器。由于每个pod都有独立的端口空间,对于不同pod中的容器来说则永远不会遇到端口冲突。此外,一个pod中的所有容器也都具有相同的loopback网络接口,因此容器可以通过localhost与同一pod中的其他容器进行通信。
      Kubernetes 集群中的所有pod 都在同一个共享网络地址空间中,这意味着每个pod都可以通过其他pod的IP地址来实现相互访问。换句话说,这也表示它们之间没有NAT(网络地址转换)网关。当两个pod彼此之间发送网络数据包时,它们都会将对方的实际IP地址看作数据包中的源IP。
      因此,pod之间的通信其实是非常简单的。不论是将两个pod安排在单一的还是不同的工作节点上,同时不管实际节点间的网络拓扑结构如何,这些pod内的容器都能够像在无NAT的平坦网络中一样相互通信,就像局域网(LAN)上的计算机一样。此时,每个pod都有自己的IP地址,并且可以通过这个专门的网络实现pod之间互相访问。这个专门的网络通常是由额外的软件基于真实链路实现的。
      总结本文主要内容:pod是逻辑主机,其行为与非容器世界中的物理主机或虚拟机非常相似。此外,运行在同一个pod中的进程与运行在同一物理机或虚拟机上的进程相似,只是每个进程都封装在一个容器之中。
      如何决定何时在pod中使用多个容器?当决定是将两个容器放入一个pod还是两个单独的pod时,我们需要问自己以下问题:·
        1.它们需要一起运行还是可以在不同的主机上运行?
        2.它们代表的是一个整体还是相互独立的组件?
        3.它们必须一起进行扩缩容还是可以分别进行?
      基本上,我们应该倾向于在单独的pod中运行容器,除非有特定的原因要求它们是同一pod的一部分。容器不应该包含多个进程,pod也不应该包含多个并不需要运行在同一主机上的容器。
     
    ⒉以YAML或JSON描述文件创建pod
      pod和其他Kubernetes资源通常是通过向Kubernetes RESTAPI提供JSON或YAML描述文件来创建的。此外还有其他更简单的创建资源的方法,比如使用kubectl run命令,但这些方法通常只允许你配置一组有限的属性。另外,通过YAML文件定义所有的Kubernetes对象之后,还可以将它们存储在版本控制系统中,充分利用版本控制所带来的便利性。
      pod定义由这么几个部分组成:
        首先是YAML中使用的KubernetesAPI版本和YAML描述的资源类型;
        其次是几乎在所有Kubernetes资源中都可以找到的三大重要部分:·
          1.metadata包括名称、命名空间、标签和关于该容器的其他信息。
          2.spec包含pod内容的实际说明,例如pod的容器、卷和其他数据。
          3·status包含运行中的pod的当前信息,例如pod所处的条件、每个容器的描述和状态,以及内部IP和其他基本信息。
      status部分包含只读的运行时数据,该数据展示了给定时刻的资源状态。而在创建新的pod时,永远不需要提供status部分。
      上述三部分展示了KubernetesAPI对象的典型结构。其他对象也都具有相同的结构,这使得理解新对象相对来说更加容易。
    apiVersion: v1  #指定当前描述文件遵循v1版本的KubernetesAPI
    kind: Pod #我们在描述一个pod
    metadata:
      name: kubia-manual  #指定pod的名称
      namespace: custom-namespace #指定当前描述的pod所在的命名空间
      labels: #指定pod标签
        creation_method: manual
        env: prod
    spec:
      nodeSelector: #告诉k8s我们希望把当前pod部署到拥有指定标签的节点上,k8s节点拥有一个默认的节点标签,其中键为kubernetes.io/hostname,值为该节点的实际主机名,因此我们可以将pod调度到某个确定的节点。但如果指定节点处于离线状态,通过hostname 标签将nodeSelector设置为特定节点可能会导致pod不可调度。
        labelKey: "labelValue"
      containers:
      - image: fanqisoft/coreqi #创建容器所使用的镜像
      name: coreqi  #容器的名称
      ports: 
        -containerPort: 8080  #应用监听的端口
        protocol: TCP
      livenessProbe:  
        httpGet:  #一个Http Get 存活探针
          path: / #Http请求的路径
          port: 8080  #探针连接的网络端口
        initialDelaySeconds: 15 #kubernetes会在第一次探测前等待15秒,没有没有设置该选项,探针将在启动时立即开始探测容器,如果使用探针,务必设置该选项。

    ⒊向pod发送请求

      向pod发送请求可以使用kubectl expose命令创建服务然后暴露出来,也可以通过端口转发将本地网络端口转发到pod中的端口访问pod,Kubernetes允许我们配置端口转发到pod,可以通过kubectl port-forward命令完成上述操作。例如以下命令会将机器的本地端口8888转发到我们的kubia-manual pod的8080端口
    kubectl port-forward kubia-manual 8888:8080

     ⒋pod健康

      1.存活探针(liveness probe)

      Kubernetes可以通过存活探针(liveness probe)检查容器是否还在运行。可以为pod中的每个容器单独指定存活探针。如果探测失败,Kubernetes将定期执行探针并重新启动容器。
      除了存活探针(liveness probe),Kubernetes还支持就绪探针(readiness probe),一定不要混淆两者。它们适用于两种不同的场景。
      Kubernetes有以下三种探测容器的机制:·
        HTTP GET探针对容器的IP地址(你指定的端口和路径)执行HTTPGET请求。如果探测器收到响应,并且响应状态码不代表错误(换句话说,如果HTTP响应状态码是2xx或3xx),则认为探测成功。如果服务器返回错误响应状态码或者根本没有响应,那么探测就被认为是失败的,容器将被重新启动。
        TCP套接字探针尝试与容器指定端口建立TCP连接。如果连接成功建立,则探测成功。否则,容器重新启动。
        Exec探针在容器内执行任意命令,并检查命令的退出状态码。如果状态码是0,则探测成功。所有其他状态码都被认为失败。
      k8s的退出代码有特殊含义,例如退出代码为137,这有特殊的含义——表示该进程由外部信号终止。数字137是两个数字的总和:128+x,其中x是终止进程的信号编号。在这个例子中,x等于9,这是SIGKILL的信号编号,意味着这个进程被强行终止。
      注意,当容器被强行终止时,会创建一个全新的容器—一而不是重启原来的容器。
      对于在生产中运行的pod,一定要定义一个存活探针。没有探针的话,Kubernetes无法知道你的应用是否还活着。只要进程还在运行,Kubernetes会认为容器是健康的。
      简易的存活探针仅仅检查了服务器是否响应。虽然这看起来可能过于简单,但即使是这样的存活探针也可以创造奇迹,因为如果容器内运行的web服务器停止响应HTTP请求,它将重启容器。与没有存活探针相比,这是一项重大改进,而且在大多数情况下可能已足够。
      但为了更好地进行存活检查,需要将探针配置为请求特定的URL路径(例如/health),并让应用从内部对内部运行的所有重要组件执行状态检查,以确保它们都没有终止或停止响应。
      提示:请确保/health HTTP端点不需要认证,否则探测会一直失败,导致你的容器无限重启。
      一定要检查应用程序的内部,而没有任何外部因素的影响。例如,当服务器无法连接到后端数据库时,前端Web服务器的存活探针不应该返回失败。如果问题的底层原因在数据库中,重启Web服务器容器不会解决问题。由于存活探测将再次失败,你将反复重启容器直到数据库恢复。
      存活探针不应消耗太多的计算资源,并且运行不应该花太长时间。默认情况下,探测器执行的频率相对较高,必须在一秒之内执行完毕。一个过重的探针会大大减慢你的容器运行。探针的CPU时间计入容器的CPU时间配额,因此使用重量级的存活探针将减少主应用程序进程可用的CPU时间。
      提示:如果你在容器中运行Java应用程序,请确保使用HTTP GET存活探针,而不是启动全新JVM以获取存活信息的Exec探针。任何基于JVM或类似的应用程序也是如此,它们的启动过程需要大量的计算资源。
      探针的失败阀值是可配置的,并且通常在容器被终止之前探针必须失败多次。但即使你将失败阀值设置为1,Kubernetes为了确认一次探测的失败,会尝试若干次。因此在探针中自己实现重试循环是浪费精力。
      Kubernetes会在你的容器崩溃或其存活探针失败时,通过重启容器来保持运行。这项任务由承载pod的节点上的Kubelet执行——在主服务器上运行的Kubernetes Control Plane组件不会参与此过程。
      但如果节点本身崩溃,那么Control Plane必须为所有随节点停止运行的pod创建替代品。它不会为你直接创建的pod执行此操作。这些pod只被Kubelet管理,但由于Kubelet本身运行在节点上,所以如果节点异常终止,它将无法执行任何操作。
      为了确保你的应用程序在另一个节点上重新启动,需要使用Replicationcontroller或类似机制管理pod。
      
      2.ReplicationController

      ReplicationController是一种Kubernetes资源,可确保它的pod始终保持运行状态。
      如果pod因任何原因消失(例如节点从集群中消失或由于该pod己从节点中逐出),则ReplicationController会注意到缺少了pod并创建替代pod。
      非托管的pod节点异常退出后将没有东西负责重建它,而由ReplicationController管理的pod异常退出后,ReplicationController将会创建一个新的pod来替换异常退出的pod。
      一般而言,ReplicationController旨在创建和管理一个pod的多个副本(replicas)。这就是ReplicationController名字的由来。

      当节点故障时,只有ReplicationController 管理的pod 会被重新创建。

      ReplicationController会持续监控正在运行的pod列表,并保证相应“类型”的pod的数目与期望相符。如正在运行的pod太少,它会根据pod模板创建新的副本。如正在运行的pod太多,它将删除多余的副本。

      你可能会对有多余的副本感到奇怪。这可能有几个原因:

      ·有人会手动创建相同类型的pod。
      ·有人更改现有的pod的“类型”。
      ·有人减少了所需的pod的数量,等等。

      一个ReplicationController有三个主要部分:
        label selector(标签选择器),用于确定ReplicationController作用域中有哪些
        pod·replica count(副本个数),指定应运行的pod数量
        pod template(pod模板),用于创建新的pod副本 

      ReplicationController的副本个数、标签选择器,甚至是pod模板都可以随时修改,但只有副本数目的变更会影响现有的pod。
      更改ReplicationController的标签选择器和pod模板对现有pod没有影响。更改标签选择器会使现有的pod 脱离ReplicationController的监视范围,因此ReplicationController将会停止关注它们。
      在创建pod后,ReplicationController并不关心其pod的实际“内容”(容器镜像、环境变量及其他)。因此,修改ReplicationController的pod模板仅影响由此ReplicationController创建的新pod。
      使用ReplicationController的好处像Kubernetes中的许多事物一样,ReplicationController尽管是一个令人难以置信的简单概念,却提供或启用了以下强大功能:
        ·确保一个pod(或多个pod副本)持续运行,解决方法是在监控的pod丢失时启动一个新pod。
        ·集群节点发生故障时,它将为故障节点上运行的所有pod(即受ReplicationController 控制的节点上的那些pod)创建替代副本。
        ·它能轻松实现pod的水平伸缩,手动和自动都可以
      注意:pod实例永远不会重新安置到另一个节点。ReplicationController的解决方案是根据pod模板创建一个全新的pod实例而不是去移动当前实例,它与正在替换的实例无关。

       ReplicationController的创建

        就像pod和其他Kubernetes资源,可以通过上传JSON或YAML描述文件到Kubernetes API 服务器来创建ReplicationController。

    apiVersion: v1  #指定当前描述文件遵循v1版本的KubernetesAPI
    kind: ReplicationController #我们在描述一个ReplicationController
    metadata:
      name: coreqi-manual  #指定ReplicationController的名称
    spec:
      replicas: 3 #pod实例的目标数目
      selector: #pod选择器决定了ReplicationController的操作对象
        app: coreqi #当前ReplicationController将确保符合标签选择器app=coreqi的pod实例始终是三个,当没有足够的pod时,将使用下面的pod模板创建新的pod
      template: #创建新pod所使用的pod模板
        metadata:
          labels:
            app: coreqi #模板中的pod标签显然必须和ReplicationController的标签选择器相匹配,否则控制器将无休止的创建新的pod实例。因为创建新的pod不会使实际的副本数量接近期望的副本数量。为了防止出现这种情况,Kubernetes API服务会校验ReplicationController的定义不会接收错误的配置。
                        #不指定ReplicationController的标签选择器也是一种选择,因为ReplicationController会自动从模板中提取标签,而且描述文件也将更简短
        spec:
          containers:
          - name: coreqi
            image: fanqisoft/coreqi
            ports:
            - containerPort: 8080

      创建了ReplicationController的描述文件后我们就可以通过相应的指令去创建它了。

    kubectl create -f coreqi-rc.yaml

      由ReplicationController创建的pod并不是绑定到ReplicationController。ReplicationController只会管理与当前ReplicationController标签选择器相匹配的pod。通过更改pod的标签,可以将它从ReplicationController的作用域中添加或删除。它甚至可以从一个ReplicationController移动到另一个。
      提示:尽管一个pod没有绑定到一个ReplicationController,但该pod在metadata.ownerReferences字段中引用它,可以轻松使用它来找到一个pod属于哪个ReplicationController。
      如果你更改了一个pod的标签,使它不再与ReplicationController的标签选择器相匹配,那么该pod就变得和其他手动创建的pod一样了。它不再被任何东西管理。如果运行该节点的pod异常终止,它显然不会被重新调度。但请记住,当你更改pod的标签时,ReplicationController发现一个pod丢失了,将会启动一个新的pod替换它。
      例如,一个ReplicationController管理具有app=coreqi标签的pod,当我们对一个pod删除这个标签或修改其值时,ReplicationController会将该pod移出管理范围。添加新的标签并没有用,因为ReplicationController 并不关心该pod是否有任何附加标签,它只关心该pod是否具有当前ReplicationController标签选择器中引用的所有标签。

      更改ReplicationController的pod模板只影响之后创建的pod,并且不会影响现有的pod。要修改旧的pod,需要删除它们,并让ReplicationController根据新模板将其替换为新的pod。

  • 相关阅读:
    (原).NET程序加入多语言包解决方案工具,超级棒
    c++标准库中的四个智能指针比较
    Mongodb c++ API的测试和封装
    MongoDB的c++驱动安装痛苦历程
    GLC_Player DOWNLOAD
    MongoDB的连接、创库、删库、插入文档、更新文档
    非关系型数据库--MongoDB的安装及概念
    虚拟机的安装
    OpenGL ES 学习
    QT学习
  • 原文地址:https://www.cnblogs.com/fanqisoft/p/11394748.html
Copyright © 2020-2023  润新知