前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kubernetes Pod 全面知识

Kubernetes Pod 全面知识

作者头像
痴者工良
发布2021-11-30 21:41:15
7170
发布2021-11-30 21:41:15
举报
文章被收录于专栏:痴者工良痴者工良

Pod 是在 Kubernetes 中创建和管理的、最小的可部署的计算单元,是最重要的对象之一。一个 Pod 中包含一个或多个容器,这些容器在 Pod 中能够共享网络、存储等环境。

学习 Kubernetes,Pod 是最重要最基本的知识,本章将介绍什么是 Pod、Pod 的结构等,并练习创建 Pod。

Pod 基础知识

创建 Pod

创建一个 Pod 很简单,示例命令如下:

代码语言:javascript
复制
kubectl run nginxtest --image=nginx:latest --port=80

nginxtest 为 Pod 名称,并且为其映射端口为 80。

但是一般不会直接创建 Pod,因为手动创建的 Pod 不被 “托管” 起来,如果 Pod 故障或者其他原因消失了,不会自动恢复,而且 kubectl run 提供的参数有限。

Kubernetes Pod 中的所有容器共享一个相同的 Linux 命名空间(network、UTS、IPC),而不是每个容器一个命名空间,因此 Pod 中的容器,网络、主机名称相同、IP 地址相同。据说在新版本的 Kubernetes 和 Docker 中, PID 命名空间也可以设置为相同的。由于 Mount、User 命名空间不共享,因此在容器中,文件系统和用户是隔离的。

另外我们也可以使用 yaml 方式定义 Pod,然后创建它。使用 YAML 文件定义需要的 Pod 格式示例如下:

代码语言:javascript
复制
apiVersion: v1
kind: Pod
metadata:
  name: nginxtest
spec:
  containers:   
  - image: nginx:latest
    name: nginxtest
    ports:
    - containerPort: 80
      protocol: TCP

但是描述 Pod 的 YAML 文件,包含哪些内容?格式如何?每个字段是什么意思?我们不可能记住所有 Kubernetes 对象的 YAML 文件的每个字段吧?

还好,Kuberentes 提供了 kubectl explain 命令,可以帮助我们快速了解创建一个对象的 YAML 需要哪几部分内容。例如创建 Pod 的 YAML 定义如下:

代码语言:javascript
复制
oot@master:~# kubectl explain pod
KIND:     Pod
VERSION:  v1

DESCRIPTION:
     Pod is a collection of containers that can run on a host. This resource is
     created by clients and scheduled onto hosts.

FIELDS:
   apiVersion    <string>
     APIVersion defines the versioned schema of this representation of an
     object. Servers should convert recognized schemas to the latest internal
     value, and may reject unrecognized values. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

   kind    <string>
     Kind is a string value representing the REST resource this object
     represents. Servers may infer this from the endpoint the client submits
     requests to. Cannot be updated. In CamelCase. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

   metadata    <Object>
     Standard object's metadata. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

   spec    <Object>
     Specification of the desired behavior of the pod. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

   status    <Object>
     Most recently observed status of the pod. This data may not be up to date.
     Populated by the system. Read-only. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

如果要查更深一层的字段,则可以:

代码语言:javascript
复制
root@master:~# kubectl explain pod.kind
KIND:     Pod
VERSION:  v1

FIELD:    kind <string>

DESCRIPTION:
     Kind is a string value representing the REST resource this object
     represents. Servers may infer this from the endpoint the client submits
     requests to. Cannot be updated. In CamelCase. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

[Info] 提示 此方式查询到的文档,其列举的字段不一定都是 Pod YAML 所需要的,还需要读者多练习多学习,从文档中补充需要的信息。

了解 Pod

Pod 是 Kubernetes 中调度资源的最小单位,一个 Pod 中可以包含多个容器,Pod 中的容器被打包在一起作为一个整体, Pod 中的容器不会被分配到不同节点中,它们一定被部署到同一个节点中。

每个 Pod 有且只有一个唯一的 IP 地址,通过 kubectl get pod {pod名称} -o wide 可以查询到。

代码语言:javascript
复制
root@master:~# kubectl get pods -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP          NODE     NOMINATED NODE   READINESS GATES
nginxtest   1/1     Running   0          12m   10.32.0.2   slave1   <none>           <none>

[Info] 提示 -o wide 可以查看更多对象的信息,不只是 Pod,其他对象也可以加上去。

Pod 之间可以通过 IP 访问,这个 IP 可以 Ping 通。

pod1
pod1

Pod 共享网络和存储

我们可以把一个 Pod 形容为一个虚拟主机。在 Pod 中,所有容器(进程)都在一个主机上,我们知道,同一个主机上的所有进程共享着主机网络,多个进程自然不能同时占用一个端口,否则会冲突。容器共享 Pod 中的网络,所以 Pod 中的容器也能够通过 localhost 相互访问,因此 Pod 中的 容器(进程)不能暴露/使用相同的端口。

pod共享网络
pod共享网络

通过 Pod 和 locahost ,解决了 高度耦合的容器间通信问题。

Docker 有一个 Contaner 模式,能够让多个容器共享一个 Network Namespace,可以让一个容器和另一个容器共享 IP、端口范围。

假如容器 A 已经创建起来,那么容器 A 会创建一个虚拟网卡;然后指定 容器 B 与容器 A 共享网络,那么两者就可以直接通过 localhost 进行通讯。

虚拟网卡1
虚拟网卡1

在 Kubernetes 中,当创建 Pod 时,会先启动一个 pause 容器,然后 Pod 中我们定义的容器会以 Container 模式共享 pause 中的网络。

代码语言:javascript
复制
docker ps | grep pause
pause
pause

当然,pause 不只是实现容器的网络互通,还有其它功能。

在第一章的 Docker 介绍中,谈到过此类容器,其目的是让其他容器联通起来, pause 在 Pod 的网络中相当于交换机。

Pod 中的容器是部分隔离的,每个容器都有自己的文件系统,各自的文件被隔离,容器不能访问或修改其它容器的文件。

为了让多个 容器之间能够共享文件,可以使用卷,把同一个卷映射到容器中。

pod共享存储
pod共享存储

划分 Pod 和容器

容器中应只包含一个进程,或进程和创建的子进程。如果在同一个容器中包含多个进程,那么需要同时管理进程的启动、日志等,一个进程崩溃时,容易影响到另一个进程。由于多个进程都会记录信息到标准输出中(如控制台输出),容器日志会合在一起,可能会导致出现问题难以排查。

一个容器只应该运行一个进程,但是他们放到一个 Pod 中就行了吗?例如程序和数据库,在设计时应该放到同一个 Pod,还是单独不同的 Pod?接下来我们简单讨论一下这个问题,限于经验和技术水平,笔者的论点可能不到位,读者可以多参考一下别的文章,了解如何设计 这些架构。

下面以 Web 程序和数据库举例。

耦合

使用 Pod/容器 的原因,是为了让不同服务能够降低耦合,能够隔离环境,如果程序跟数据库放在一起,是否能够有足够的隔离程度?如果 Web 跟 数据库放在同一个 Pod,此时 web 跟数据库的实例(容器)数量是 1:1。对于 Kubernests 来说,Pod 是最小单位,Kbernetes 不能横向扩容单个容器,因此扩容的最小单位是 Pod,多个容器必须捆绑在一起。同时 Pod 中的所有容器都使用同一机器的资源。在同一个 Pod 中的容器,在生命周期、计算机资源(内存、CPU)、实例数量、网络等都会耦合在一起。

请参考 https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/

访问压力

一般来说,Web 是要被外界访问的,但是数据库为了安全,应当避免能够公网访问,只有处于集群中的程序或客户端才能访问数据库。同时Web的访问是直接面向用户的,访问量肯定比数据库的访问量大得多,而且数据库需要的存储空间比web大得多,那么两者使用的计算资源并不相近。

Pod 可以使用服务器资源,当服务器压力过大时,当太多用户访问 Web 时,Web就要考虑扩容实例,可以在其它节点上部署相同的 Pod(扩容),降低单节点访问压力。而一个数据库实例能够支持多个 Web 程序同时访问,那么数据库实例有必要跟 Web 放在同一个 Pod 中,保持 1:1的实例数量?

故障恢复

在 Kubernetes 中,容器应当是无状态的,也就是说容器或容器中的进程挂了,Kubernetes 可以快速在其它地方再创建一个 Pod ,启动容器,维持一定数量的 Pod 实例。对于 Web 来说,只要配置文件和数据库数据在,再启动一个 Web 容器,结果是一样的,流水的程序铁打的数据,只要数据在,可以随时启动 Web 程序,很容易恢复服务。但是数据库却不一定,数据库的运维比 Web 程序复杂得多,我们要考虑数据的安全性和可用性,当容器甚至节点服务器挂了后、磁盘损坏等,如何恢复数据库。数据库的维护不觉得。

两者的维护难度不在同一水平上,此时我们要考虑两者放在不同的 Pod 中。(实际上很少将数据库放在容器中,一般都是裸机部署)。

应用分层
应用分层

其中负载均衡是通过 Ingress 和 Service 实现的,后面的章节会学习到。

何时使用多个容器

前面提到 Web 跟数据库,应当划分在不同的 Pod 中,类似地,对于微服务中的不同服务或模块,也应当放在不同的 Pod 中。微服务架构、容器化,并不是那么容易,例如,对于前后端分离的项目,前后端文件放在同一个 容器中还是同一个 Pod 中还是不同 Pod 中?在设计中我们要考虑很多问题。

对于单体 Web 来说,一个程序中包含了所有服务,那么 Web 完全可以托管前端静态文件,前端文件跟后端程序打包在一起即可。例如 PHP、ASP.NET Core 等使用 wwwroot、www 等 目录存储静态文件。

如果是一个较大的网站,网站使用了多个微服务,则前端更可能放到一个 Pod 中,用户访问前端页面,然后前端根据访问的模块,自动访问不同的服务。

如果前端和后端文件需要频繁发布,两者的发布版本分开工作,则为了避免一方等待另一方发布,或者从 Devops 角度,前端和和后端文件可以放在不同容器中,然后通过存储卷,两个容器共享文件。

如果一个 Pod 中,包含一个主进程和多个辅助进程,则可以使用一个 Pod 部署多个 容器,多个容器之间紧密联系。

具体怎么设计,需要根据实际情况考虑。

Pod 生命周期

当 Pod 被分配到某个节点时, Pod 会一直在该节点运行,直到停止或被终止,Pod 在整个生命周期中只会被调度一次。

Pod 的整个生命周期可能有四种状态:

  • Pending,尝试启动容器,如果容器正常启动,则进入下一个阶段;
  • Running,处于运行状态;
  • Succeeded、Failed,正常结束或故障等导致容器结束;
  • Unknown,因为某些原因无法取得 Pod 的状态。

我们可以创建一个 Deployment,查看当前正在发生的事件:

代码语言:javascript
复制
kubectl create deployment nginx --image=nginx:latest --replicas=3
代码语言:javascript
复制
kubectl get events
events
events

通过 kubectl describe pod {pod名称} 查看一个 Pod 的事件:

代码语言:javascript
复制
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  3m22s  default-scheduler  Successfully assigned default/nginx-* to instance-2
  Normal  Pulling    3m19s  kubelet            Pulling image "nginx:latest"
  Normal  Pulled     3m17s  kubelet            Successfully pulled image "nginx:latest" in 2.570763819s
  Normal  Created    3m17s  kubelet            Created container nginx
  Normal  Started    3m16s  kubelet            Started container nginx

上面查询到的事件均发生在 Pod 的 Pending 状态,我们可以看到在这个阶段中,Pod 被调度,然后拉取镜像、启动容器,如果容器启动成功,Pod 便会进入 Running 状态。

事件记录保存在 etcd 中。

在 Kubernetes 中,Pod 被认为是相对的临时性实体,而不是长期存在的。由于 Pod 本身不具有治愈能力,如果 节点故障或者节点资源耗尽、节点被维护、Pod 被驱逐等,那么 Pod 无法在节点上继续存活。无论 Pod 因为何种原因被删除,在 Pod 中的网络、存储卷等,也会被销毁,新的 Pod 被创建时,相关的网络、存储卷也会被重建。

pod结构
pod结构

【图来源:https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/

[Info] 提示 由于 Pod 是临时性的,为了保障服务能够在 Pod 挂了后自动重建,可以使用 Deployement、Daemon 等对象管理 Pod,这些对象被称为 控制器。

在删除 Pod 时,Kubernetes 会终止 Pod 中的所有容器,会向容器中的进程发生 SIGTERM 信号,等待进程的正常关闭,所以 Pod 可能不会被马上删除,当然如果进程不能正常关闭,Kubernetes 最多等待 30s,不然会使用 SIGKILL 杀死进程。

容器重启策略

这是一个简单的 Pod 的 YAML 定义:

代码语言:javascript
复制
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
spec:
  containers:
  - image: nginx:latest
    imagePullPolicy: Always
    name: nginx
    ... ...
  restartPolicy: Always

在这个 YAML 中,有两个*Policy,取值有 Always、OnFailure 和 Never 三种,它们代表了某种生命周期策略。

对于每个容器,都可以设置 imagePullPolicy ,指示在拉取镜像时如果失败,是否进行重试。

spec 中,有个 restartPolicy 字段,其值默认为 Always,指示 Pod 中所有容器的重启动作,但是在 DeploymentStatefulSetDaemonSet 等控制器中,restartPolicy 只支持 Always ,不支持 OnFailure 和 Never 。

[Info] 提示 如果容器启动失败,重试间隔会越来越长。 kubelet 会在 10s 后重试第一次,如果还是失败,第二次 20s 后再重试;按照 10s、20s、40s、80s ... 的间隔重试,但最长不超过 5 分钟。如果容器被成功运行且运行了 10 分钟以上,那么计时器会被重置,下次出现故障时,按照 10s、20s 的间隔时间重试。

如果单独创建 Pod,并且设置了 restartPolicy: Always,那么 Pod 会一直停留在此节点上,如果 容器故障,Pod 可能会无限重试。

如果我们使用 DeploymentStatefulSetDaemonSet 等控制器管理 Pod,这些控制器能够处理 Pod/副本 的管理、上线,并且在 Pod 失效时提供治愈能力,当,当然,这些东西后面再提。

节点上的 Pod 停止工作时,可以创建替代性的 Pod, Pod 被调度到一个健康的节点执行。

[Info] 提示 为什么要使用控制器管理 Pod 呢? 举个例子,笔者有个朋友,写了个 A 程序,这个程序能够在执行后,关闭计算机(关机)。本来是需要的时候执行一次,但是后面配置了一个守护进程,这个守护进程会自动启动 A 程序。这样出现了无限循环,开机 -> 启动守护进程 -> A 出现 -> 关机,结果这个朋友的电脑无法正常开机,一开机就被关机。 这种情况跟单独的 Pod 部署类似,很容易因为某些原因无限重试,并且一直驻留在节点上。因为重试是在原来的基础上进行重试,使用原来的文件、数据、网络等。控制器则可以将其重置,恢复 出厂设置。 如果一个节点上的 CPU、内存不够用了,那么容器有可能因为资源不足,无法启动,导致无限重试;如果使用控制器,控制器会在有充足资源的、健康的节点上重建 Pod。

Pod 的部署和管理

但是一般很少直接创建或管理 Pod,一般使用控制器来管理 Pod。下面列出一些控制器,在后面的学习中我们会一步步深入学习。

  • Deployment
  • StatefulSet
  • DaemonSet

单独创建 Pod ,一般用于临时调试的等。

创建 Pod

在 Kubernetes 中,所有对象都可以使用 YAML 表示。我们可以使用 YAML 来定义 Pod 对象,一个 Pod 的基本模板:

代码语言:javascript
复制
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest

如果要映射网络端口,则 YAML 文件为:

代码语言:javascript
复制
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports: 
    - containerPort: 80
      protocol: TCP

由于 Pod 中的容器共享网络,技术不加上 ports,照样能够访问。在 YAML 中多写一些,有助于其他人快速了解此 Pod 的定义信息。

将上面的 YAML 内容复制到 pod.yaml 中,然后执行命令应用 YAML :

代码语言:javascript
复制
kubectl apply -f pod.yaml
# 或
kubectl create -f pod.yaml

使用 kubectl run 命令也可以创建 pod,命令示例: kubectl run nginx-pod --image=nginx:latest

覆盖容器命令

在 Pod 中可以配置容器的一些信息,也可以替换容器的启动命令,其配置格式如下:

代码语言:javascript
复制
spec:
  containers:
  - name: nginx
    image: nginx:latest
    command: ["/bin/command"]
    args: ["arg1","arg2","arg3"]

Pod 管理

输入 kubectl get pods 可以查看名为 default 命名空间的 Pod。输入 kubectl get pods -o wide 可以查看多几个字段的 Pod 信息,输入 kubectl describe pods 可以查看每个 Pod 的所有详细信息。

代码语言:javascript
复制
root@instance-2:~# kubectl get pods -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          11s   192.168.56.3   instance-2   <none>           <none>

可以看到 Pod 在第二个节点上部署,其 IP 为 192.168.56.3。Pod 的 IP 只能在被部署服务的节点上访问,不同节点不能访问其的 Pod。

nginx 默认使用了 80 端口,因此通过 192.168.56.3:80 ,可以访问到容器中的 nginx 服务。

pod访问nginx
pod访问nginx

[Error] 注意 只能在 Pod 部署的节点上连接到此 IP。如果要在不同的节点访问 Pod,需要安装 CNI 网络插件,如 flannel、calico、weave 等,在 2.2、2.3 中有介绍。

查看日志

在 Docker 中,我们可以通过 docker logs {容器id} 来查看容器中的日志,这些日志是进程打印到控制器的标准输出,例如 C# 的 Console.Write、C 语言的 printf、Go 语言的 fmt.Print,Docker 的 本地日志驱动会捕获容器的 stdout/stderr 输出记录驱动器。

Docker 日志默认限制日志大小为 10 MB ,每天会轮替一个日志文件,并使用自动压缩来减少磁盘文件大小。

Docker 日志驱动程序使用基于文件的存储。文件格式和存储机制被设计为由 Docker 守护进程独占访问,不应被外部工具使用,因为在未来的版本中实现可能会发生变化。 笔者没有明确查找到 Docker 的日志具体限制情况,以上内容来自 https://docs.docker.com/config/containers/logging/local/ 的参考资料。

在 Kubernetes 中,也可以通过命令快速查看 Pod 中的容器的日志。

如果 Pod 中只有一个容器,则直接使用类似命令即可:

代码语言:javascript
复制
kubectl logs {pod名称}

如果 Pod 中有多个容器,则需要指定容器名称:

代码语言:javascript
复制
kubectl logs {pod名称} -c {容器名称}

kubectl logs 只能获取当前正在运行的 Pod 的日志,如果 Pod 被删除,所有日志记录都会被删除。

查看、维护 Pod 状态,比较常用的命令有:

  • kubectl get - 列出对象资源,如 kubectl get pods
  • kubectl describe - 显示有关资源的详细信息,如 kubectl describe pod nginxtest
  • kubectl logs - 打印 pod 和其中容器的日志;
  • kubectl exec - 在 pod 中的容器上执行命令,格式为 kubectl exec {pod名称} -c {容器名称} -- {要执行的命令}-- 用于分隔命令,其后面的参数均表示要传递进容器的命令。
本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-11-29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Pod 基础知识
    • 创建 Pod
      • 了解 Pod
        • Pod 共享网络和存储
          • 划分 Pod 和容器
            • 何时使用多个容器
              • Pod 生命周期
                • 容器重启策略
                • Pod 的部署和管理
                  • 创建 Pod
                    • 覆盖容器命令
                      • Pod 管理
                        • 查看日志
                        相关产品与服务
                        容器服务
                        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
                        http://www.vxiaotou.com