再战 k8s:Pod 定义及基本用法

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了再战 k8s:Pod 定义及基本用法相关的知识,希望对你有一定的参考价值。

文章目录

1. 什么是pod

一些关于pod的表述很专业,例如

Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元

pod是kubernetes项目中的最小API对象

pod是kubernetes项目的原子调度单位

官方描述:https://kubernetes.io/zh/docs/concepts/workloads/pods/


2. 为什么需要pod

对于这些对pod概念的总结大多殊路同归。

在理解概念前不妨先想一个问题:

明明有了容器,为什么还需要pod?

因为:

为了解决容器不足,或者说容器无法满足需求。

确实是这样,先分析一下容器的本质:

namespace做隔离

Cgroup做限制

rootfs做文件系统

三者相辅相成组成容器基本模型

但是,宏观的来说,**容器的本质是系统中的一个进程。**只不过这个进程启动时被附加了以上等等一下特殊的属性。

所谓容器只是一个恰当的说法。

就如系统中的大多数进程来说,不是单个进程独自工作,而是以“进程组”的方式“原则性”的组织在一起,互相协作,完成复杂任务。当然,它们之间也会共享一些资源,例如pid,namespace,存储等等。

在实际开发和运维中也是随处可见的这种问题,应用之间有深切的联系和依赖。

比如说,我要将一个应用容器化,这个应用由负责各个功能的5个进程组成,这时候,问题来了。

正是容器的局限性:单进程模型

单进程不是指容器中只能运行一个进程,而是容器无法去管理多个进程

例如,容器中有pid=1的进程,还有一个pid=5的进程,当这个pid=5的进程异常退出时,后续的垃圾回收等处理工作又由谁去做呢?

所以,这个应用的5个模块就必须分别制成5个容器,而且必须在同一个机器上运行。

随之而来的又是一个问题:

也就是容器调度问题,例如有两个容器调度节点node1和node2,可用内存分别为5G和4.5G,

每个容器分1G内存。由于5个容器必须在一台机器,正常全部调度到node1刚刚好,没有任何问题。

但是,因为是以容器为单位调度,有这样一些特殊情况。当前4个容器被调度到node2上时,空间只有0.5G,不足以运行最后一个容器,它有只能在node1运行,这就是以容器为调度单位的缺点。

当然也有解决方案:如Mesos中的资源囤积(resource hoarding),也就是所有调度任务都到达了才进行调度,也有谷歌Omega论文提出乐观调度,就是先不管冲突,而是在冲突之后通过一系列回滚机制解决冲突。

但这些都没有很完善的解决容器调度问题,但是在kubernetes中,这些问题都迎刃而解。

因为不在按照传统思维的将容器作为调度单位

kubernetes中的项目调度器是统一按照pod的资源需求做调度计算

也就是开始总结的那句话: *pod*是kubernetes项目的原子调度单位


3. pod结构

如图所示,一个pod包含了两类容器:

  • 用户容器
  • Pause容器,也常常被称为“根容器”(貌似老版本叫做infra容器)

用户容器好理解,但是Pause容器,也就是根容器,他是做什么用的呢?

pause容器主要为每个用户容器提供以下功能:

① PID命名空间:Pod中的不同应用程序可以看到其他应用程序的进程ID。

② 网络命名空间:Pod中的多个容器能够访问同一个IP和端口范围。

③ IPC命名空间:Pod中的多个容器能够使用SystemV IPC或POSIX消息队列进行通信。

④ UTS命名空间:Pod中的多个容器共享一个主机名;Volumes(共享存储卷):

⑤ Pod中的各个容器可以访问在Pod级别定义的Volumes。


4. 容器设计模式

考虑这样一个问题,容器间的关系是一成不变的吗?

举一些例子:

  1. 在pod中的一些容器的启动,必须依赖某一个正在运行的容器,也就是说这个容器必须比其他容器先启动
  2. 在pod中的容器必须同时协作,也就是说所有容器必须并行执行
  3. 一个pod需要给外部的其他pod提供接口
  4. 外部访问pod时,又怎样确保响应报文的一致性

常见容器设计模式:

Init(初始化)容器

Sidecar(边车)容器

Adapter(适配器)容器

Ambassador(外交官)容器

关于容器设计模式可以参考论文:https://www.usenix.org/conference/hotcloud16/workshop-program/presentation/burns


5. pod实现原理

一定要明白一点:pod只是一个逻辑上的概念

因为kubernetes真正处理的还是宿主机操作系统上容器的Namespace和Cgroup,也就是说没有所谓的pod边界或隔离环境。

所以说,pod就是一组共享了某些资源的容器

在一个pod中所有容器是共享一个Network Namespace的,根据声明的不同来实现不同的资源共享

但是容器间的复杂关系在容器上难以解决,所以kubernetes项目里,pod的实现借用了一个中间容器,也就是常常说的根容器(也叫pause容器或infra容器,现在好像都统称pause容器,infra容器已经不再使用)。

在pod中,根容器永远是第一个创建的容器,用户后面定义的容器会加入进pod的Network Namespace,从而在视图上容器都在pod里。

不妨在k8s中看看这个容器镜像:

[root@master ~]# docker images | grep pause
registry.aliyuncs.com/google_containers/pause                     3.2        80d28bedfe5d   15 months ago   683kB

#当然,主机上有多少个pod就能docker ps 过滤看到多少pause容器

pause镜像大小只有683k,它是由汇编语言写的镜像

在一个pod中的容器,他们的Namespace文件,是一样的

也就意味着:

  • pod内的容器可以使用localhost进行通信
  • 根容器能看到的网络资源,其他容器都能看到,也就是pod的网络资源和pod内的容器共享
  • 一个pod有一个ip地址,和pod对应的Network namespace的ip一致
  • pod的生命周期只与根容器有关,与pod内的容器无关
  • 从pod里的容器的视角来说,它们的流量进出可以看做是通过根容器完成的

Pod 定义

属性名称取值类型是否必选取值说明
apiVersion: v1StringRequried版本号,例如v1,版本号必须可以用 kubectl api-versions 查询到
kind: PodStringRequriedPod
metadata:ObjectRequried元数据
name: stringStringRequriedPod名称,需符合RFC 1035规范
namespace: stringStringRequriedPod所属的命名空间,默认为"default"
labels:List自定义标签
- name: stringString自定义标签名字
annotations:List自定义注释列表
- name: string
spec:ObjectRequriedPod中容器的详细定义
containers:ListRequriedPod中容器列表
- name: stringStringRequried容器名称,需符合RFC 1035规范
image: stringStringRequried容器的镜像名称
imagePullPolicy: [ Always|Never|IfNotPresent ]String获取镜像的策略,默认值为Alawys,Alawys表示每次都尝试下载镜像,IfnotPresent表示优先使用本地镜像,否则下载镜像,Nerver表示仅使用本地镜像
command: [string]List容器的启动命令列表,如不指定,使用打包时使用的启动命令
args: [string]List容器的启动命令参数列表
workingDir: stringString容器的工作目录
volumeMounts:List挂载到容器内部的存储卷配置
- name: stringString引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
mountPath: stringString存储卷在容器内mount的绝对路径,应少于512字符
readOnly: booleanBoolean是否为只读模式,默认为读写模式
ports:List需要暴露的端口库号列表
- name: stringString端口的名称
containerPort: intInt容器需要监听的端口号
hostPort: intInt容器所在主机需要监听的端口号,默认与Container相同
protocol: stringString端口协议,支持TCP和UDP,默认TCP
env:List容器运行前需设置的环境变量列表
- name: stringString环境变量名称
value: stringString环境变量的值
resources:Object资源限制和请求的设置
limits:Object资源限制的设置
cpu: stringStringCPU的限制,单位为core数,将用于docker run --cpu-shares参数
memory: stringString内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
requests:Object资源限制的设置
cpu: stringStringCPU请求,容器启动的初始可用数量
memory: stringString内存请求,容器启动的初始可用数量
livenessProbe:Object对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器,可设置的方法包括exec、httpGet和tcpSocket,对一个容器仅需设置其中一种健康检查方法
exec:Object对Pod容器内检查方式设置为exec方式
command: [string]Stringexec方式需要制定的命令或脚本
httpGet:Object对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
path: string
port: number
host: string
scheme: string
HttpHeaders:
- name: string
value: string
tcpSocket:Object对Pod内个容器健康检查方式设置为tcpSocket方式
port: number
initialDelaySeconds: 0Number容器启动完成后首次探测的时间,单位为秒
timeoutSeconds: 0Number对容器健康检查探测等待响应的超时时间,单位秒,默认1秒,若超过该超时时间设置,则认为该容器不健康,会重启该容器
periodSeconds: 0Number对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged: false
restartPolicy: [Always | Never | OnFailure]StringPod的重启策略,(1)Always表示一旦不管以何种方式终止运行,kubelet都将重启它,(2)OnFailure表示只有Pod以非0退出码退出才重启,如果容器正常结束(退出码为0)kubelet将不会重启它,(3)Nerver:Pod终止后,kubelet将退出码报告给Master,不会再重启该Pod。
nodeSelector: obejectObject设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定
imagePullSecrets:ObjectPull镜像时使用的secret名称,以name:secretkey格式指定
- name: string
hostNetwork: falseBoolean是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络,不再使用Docker网桥,该Pod将无法在同一宿主机上启动第2哥副本
volumes:List在该pod上定义共享存储卷列表
- name: stringString共享存储卷名称 ,在一个Pod中每个存储卷定义一个名称,应符合RFC 1035规范。容器定义部分的containers[].volumeMounts[].name将引用该共享存储卷的名称。Volume的类型包括:emptyDir、hostPath、gcePersistentDisk、awsElasticBlockStore、gitRepo、secret、nfs、iscsi、glusterfs、persistentVolumeClaim、rbd、flexVolume、cinder、cephfs、flocker、downwardAPI、fc、azureFile、configMap、vsphereVolume,可以定义多个Volume,每个Volume的name保持唯一。
emptyDir: Object类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
hostPath: stringObject类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
path: stringStringPod所在宿主机的目录,将被用于同期中mount的目录
secret:Object类型为secret的存储卷,挂载集群与定义的secre对象到容器内部
scretname: string
items:
- key: string
path: string
configMap:Object类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
name: string
items:
- key: string
path: string

容器基本用法

1.定义创建pod

创建一个Hello World Pod,运行一个输出“Hello World的容器”

  • 定义hello-world-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello-world
spec:
  restartPolicy: OnFailure
  containers:
  - name: hello
    image: "ubuntu"
    command: ["/bin/echo","hello”,”world"]
  • 创建
kubectl create -f hello-world-pod.yaml

2.查询Pod

# 简要查询
kubectl get pod hello-world


#JSON格式显示Pod的完整信息
kubectl get pod hello-world --output json

    
#YAML方式显示Pod的完整信息
kubectl get pod hello-world --output yaml
  • 状态和生命周期查询
kubectl describe pod hello-world

3.更新Pod

  • 更新
kubectl replace -f hello-world-pod.yaml

但是由于Pod的很多属性没办法修改,比如容器镜像,这时候可以采用–force参数

  • 重建Pod
kubectl replace --force -f hello-world-pod.yaml

4.删除Pod

  • 通过kubectl delete删除指定Pod
kubectl delete pod hello-world
  • 通过kubectl delete批量删除全部Pod
kubectl delete pod --all


静态 Pod

什么是 Static Pod

静态 Pod 在指定的节点上由 kubelet 守护进程直接管理,不需要 API 服务器监管。 与由控制面管理的 Pod(例如,[Deployment]) 不同;kubelet 监视每个静态 Pod(在它崩溃之后重新启动)。

静态 Pod 永远都会绑定到一个指定节点上的 [Kubelet]。

kubelet 会尝试通过 Kubernetes API 服务器为每个静态 Pod 自动创建一个 [镜像 Pod]。 这意味着节点上运行的静态 Pod 对 API 服务来说是可见的,但是不能通过 API 服务器来控制。 Pod 名称将把以连字符开头的节点主机名作为后缀。

最常见的 Static Pod

  • etcd
  • kube-apiserver
  • kube-controller-manager
  • kube-scheduler

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

配置文件方式

可以通过kubelet的启动参数查看kubelet扫描静态Pod配置文件的路径,如下:

可以到kubelet是由这个配置文件进行启动的,在通过查看此配置文件,内容如下:

apiVersion: kubelet.config.k8s.io/v1beta1
.....
.....
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s

其中staticPodPath: /etc/kubernetes/manifests就是正在运行的kubelet需要监控的配置文件所在的目录,kubelet会定期扫描该目录,并根据该目录下的.yaml或.json文件进行创建操作。

在目录/etc/kubernetes/manifests中放入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

等待一会儿,查看本机中已经启动的容器:

[root@k8s-node0 ~]# docker ps|grep static-web
85fd075aaddd        nginx                                               "/docker-entrypoint.…"   About an hour ago   Up About an hour                        k8s_static-web_static-web-k8s-node0_default_d9890f08374e33f99c31a2a034276178_0

可以看到一个Nginx容器已经被kubelet成功创建了出来。

到Master上查看Pod列表,可以看到这个static pod:

[root@k8s-master ~]# kubectl get pod
NAME                    READY   STATUS    RESTARTS   AGE
nginx-f89759699-gn2wn   1/1     Running   1          17h
static-web-k8s-node0    1/1     Running   0          12s

由于静态Pod无法通过API Server直接管理,所以在Master上尝试删除这个Pod时,会使其变成Pending状态,且不会被删除。

[root@k8s-master ~]# kubectl delete pod static-web-k8s-node0
pod "static-web-k8s-node0" deleted
[root@k8s-master ~]# kubectl get pod
NAME                    READY   STATUS    RESTARTS   AGE
nginx-f89759699-gn2wn   1/1     Running   1          18h
static-web-k8s-node0    0/1     Pending   0          2s

删除该Pod的操作只能是到其所在Node上将其定义文件static-web.yaml从/etc/kubernetes/manifests目录下删除。

[root@k8s-node0 ~]# rm /etc/kubernetes/manifests/static_pod_web_nginx.yaml
rm: remove regular file ‘/etc/kubernetes/manifests/static_pod_web_nginx.yaml’? y
[root@k8s-node0 ~]# docker ps|grep static-web
[root@k8s-node0 ~]# docker ps|grep static-web

在Master上查看这个Pod:

[root@k8s-master ~]# kubectl get pod
NAME                    READY   STATUS    RESTARTS   AGE
nginx-f89759699-gn2wn   1/1     Running   1          18h

HTTP方式

通过设置kubelet的启动参数–manifest-url或kubelet的配置文件加上此配置项,kubelet将会定期从该URL地址下载Pod的定义文件,并以.yaml或.json文件的格式进行解析,然后创建Pod。其实现方式与配置文件方式是一致的。

belet的配置文件加上此配置项,kubelet将会定期从该URL地址下载Pod的定义文件,并以.yaml或.json文件的格式进行解析,然后创建Pod。其实现方式与配置文件方式是一致的。

以上是关于再战 k8s:Pod 定义及基本用法的主要内容,如果未能解决你的问题,请参考以下文章

再战 k8s(13):Pod 的扩缩容

再战 k8s(14):service 简介 及 无头 service

再战 k8s(11):污点容忍,亲和性

再战 k8s(10):job

再战 k8s:Pod Volume存储卷健康检查

再战 k8s(12):Deployment 指导下 Pod 的升级和回滚