Kubernetes Pods (三)

Kubernetes Pods

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

Pod (就像在鲸鱼荚或者豌豆荚中)是一组(一个或多个) 容器; 这些容器共享存储、网络、以及怎样运行这些容器的声明。 Pod 中的内容总是并置(colocated)的并且一同调度,在共享的上下文中运行。 Pod 所建模的是特定于应用的“逻辑主机”,其中包含一个或多个应用容器, 这些容器是相对紧密的耦合在一起的。 在非云环境中,在相同的物理机或虚拟机上运行的应用类似于 在同一逻辑主机上运行的云应用。

除了应用容器,Pod 还可以包含在 Pod 启动期间运行的 Init 容器。 你也可以在集群中支持临时性容器 的情况下,为调试的目的注入临时性容器。

什么是 Pod?

说明: 除了 Docker 之外,Kubernetes 支持 很多其他容器运行时Docker 是最有名的运行时, 使用 Docker 的术语来描述 Pod 会很有帮助。

Pod 的共享上下文包括一组 Linux 名字空间、控制组(cgroup)和可能一些其他的隔离 方面,即用来隔离 Docker 容器的技术。 在 Pod 的上下文中,每个独立的应用可能会进一步实施隔离。

就 Docker 概念的术语而言,Pod 类似于共享名字空间和文件系统卷的一组 Docker 容器。

使用 Pod

通常你不需要直接创建 Pod,甚至单实例 Pod。 相反,你会使用诸如 DeploymentJob 这类工作负载资源 来创建 Pod。如果 Pod 需要跟踪状态, 可以考虑 StatefulSet 资源。

Kubernetes 集群中的 Pod 主要有两种用法:

  • 运行单个容器的 Pod。"每个 Pod 一个容器"模型是最常见的 Kubernetes 用例; 在这种情况下,可以将 Pod 看作单个容器的包装器,并且 Kubernetes 直接管理 Pod,而不是容器。

  • 运行多个协同工作的容器的 Pod。 Pod 可能封装由多个紧密耦合且需要共享资源的共处容器组成的应用程序。 这些位于同一位置的容器可能形成单个内聚的服务单元 —— 一个容器将文件从共享卷提供给公众, 而另一个单独的“边车”(sidecar)容器则刷新或更新这些文件。 Pod 将这些容器和存储资源打包为一个可管理的实体。

    说明: 将多个并置、同管的容器组织到一个 Pod 中是一种相对高级的使用场景。 只有在一些场景中,容器之间紧密关联时你才应该使用这种模式。

每个 Pod 都旨在运行给定应用程序的单个实例。如果希望横向扩展应用程序(例如,运行多个实例 以提供更多的资源),则应该使用多个 Pod,每个实例使用一个 Pod。 在 Kubernetes 中,这通常被称为 副本(Replication)。 通常使用一种工作负载资源及其控制器 来创建和管理一组 Pod 副本。

参见 Pod 和控制器以了解 Kubernetes 如何使用工作负载资源及其控制器以实现应用的扩缩和自动修复。

Pod 怎样管理多个容器

Pod 被设计成支持形成内聚服务单元的多个协作过程(形式为容器)。 Pod 中的容器被自动安排到集群中的同一物理机或虚拟机上,并可以一起进行调度。 容器之间可以共享资源和依赖、彼此通信、协调何时以及何种方式终止自身。

例如,你可能有一个容器,为共享卷中的文件提供 Web 服务器支持,以及一个单独的 “sidecar(挂斗)”容器负责从远端更新这些文件,如下图所示:

Kubernetes Pods (三)
有些 Pod 具有 Init 容器应用容器。 Init 容器会在启动应用容器之前运行并完成。

Pod 天生地为其成员容器提供了两种共享资源:网络存储

共享⽹络空间

Kubernetes Pods (三)

POD内的容器使⽤container模式共享根容器的⽹络
容器看到的⽹络设备信息和根容器完全相同
POD内的多个容器可以直接使⽤localhost通信
POD内的多个容器不能绑定相同的端⼝
POD的⽣命周期和根容器⼀样,如果根容器推出, POD就退出了

可以在node节点使⽤docker inspect查看容器的详细信息,会发现有⽹络模式为container

[root@node2 ~]# docker inspect 6d636151f567|grep "NetworkMode"
"NetworkMode":
"container:1613f2dee9e410805679c6073ee7c1a9160c3fd24b7135f7e5ed17ffc6502ee2",

共享文件系统

Kubernetes Pods (三)

默认情况下⼀个POD内的容器⽂件系统是互相隔离的
如果想让⼀个POD的容器共享⽂件,那么只需要定义⼀个Volume,然后两个容器分别挂在这个Volume就可以共享了

举个例子:

cat nginx_v.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  spec:
	nodeName: node2
	volumes:
	- name: nginxlog    #自定义volumes
	  hostPath:
		path: /var/log/nginx/
		
	containers:
	- name: nginx-1
	  image: nginx
	  imagePullPolicy: IfNotPresent
	  
	  volumeMounts:
	  - name: nginxlog 			#引用自定义volumes
		mountPath: /var/log/nginx/

	- name: tail-log
	  image: busybox
	  imagePullPolicy: IfNotPresent
	  args: [/bin/sh, -c, 'tail -f /var/log/nginx/access.log']
	  volumeMounts:
	  - name: nginxlog 			#引用自定义volumes
        mountPath: /var/log/nginx/
  
kubectl get pod -w 查看现在运行pod的状态

pod的生命周期

Kubernetes Pods (三)

init container:
初始化容器是指在主容器启动之前,先启动⼀个容器来做⼀些准备⼯作,⽐如两个容器做了共享volum,然后可
以先启动⼀个容器来对⽬录进⾏更改⽤户授权。
⽐如主容器需要连接数据库,可以先使⽤初始化容器测试可否正常连接数据库,如果可以正常连接再启动主容
器。
hook:
PostStart:在容器启动创建后⽴刻执⾏,但是时间不能太⻓,否则容器将不会是running状态
PreStop:在容器停⽌被删除前执⾏,主要⽤于优雅的关闭应⽤程序。
liveness probe:
存活性探针,⽤于确定容器内的应⽤是否还活着
readiness probe:
就绪性探针,⽤于确定容器是否已经准备就绪可以⼲活了,⽐如扩容⼀个Pod,只有等这个Pod⾥⾯的应⽤完全启
动了,才会将流量进⼊

pod运行的状态

Pending(挂起) :
Pod 已被Kubernetes 系统接受,但有⼀个或者多个容器尚未创建亦未运⾏。此阶段包括等待 Pod 被调度的
时间和通过⽹络下载镜像的时间
Running(运⾏中) :
Pod 已经绑定到了某个节点, Pod 中所有的容器都已被创建。⾄少有⼀个容器仍在运⾏,或者正处于启动或重启
状态。
Succeeded(成功) :
Pod 中的所有容器都已成功终⽌,并且不会再重启。
Failed(失败) :
Pod 中的所有容器都已终⽌,并且⾄少有⼀个容器是因为失败终⽌。也就是说,容器以⾮ 0 状态退出或者被系
统终⽌。
Unknown(未知) :
因为某些原因⽆法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。

Kubernetes Pods (三)

Pod对容器的封装和应⽤

那么在⼯作中我们究竟如何决策⼀个POD是跑⼀个容器还是多个容器呢?
在实际⼯作中我们除了完成任务以外还需要考虑以后扩容的问题,就拿wordpress举例,有以下两种⽅案:
第⼀种:全部放⼀个pod⾥

Kubernetes Pods (三)

第⼆种: Wordpress和MySQL分开

Kubernetes Pods (三)

那么如何扩容呢?如果第⼀种⽅案⼤家会发现没有办法很好的扩容,因为数据库和wordpress已经绑定成⼀个整体
了,扩容wordpress就得扩容mysql。⽽第⼆种⽅案就要灵活的多。

Kubernetes Pods (三)

多个容器之间是否⾮常紧密的关系,⽐如nginx和php就⾮常紧密,但是php和mysql不紧密
这些容器是否必须是⼀个整体
这些容器放在⼀起是否影响扩容

init容器

apiVersion: v1kind: Podmetadata:  name: myapp-pod  labels:    app: myappspec:  containers:  - name: myapp-container    image: busybox:1.28    command: ['sh', '-c', 'echo The app is running! && sleep 3600']  initContainers:  - name: init-myservice    image: busybox:1.28    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]  - name: init-mydb    image: busybox:1.28    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]

你通过运行下面的命令启动 Pod:

kubectl apply -f myapp.yaml

输出类似于

pod/myapp-pod created

使用下面的命令检查其状态:

kubectl get -f myapp.yamlNAME        READY     STATUS     RESTARTS   AGEmyapp-pod   0/1       Init:0/2   0          6m或者查看更多详细信息kubectl describe -f myapp.yaml如需查看 Pod 内 Init 容器的日志,请执行:kubectl logs myapp-pod -c init-myservice # 查看第一个 Init 容器kubectl logs myapp-pod -c init-mydb      # 查看第二个 Init 容器

在这一刻,Init 容器将会等待至发现名称为 mydbmyservice 的 Service。

如下为创建这些 Service 的配置文件:

---apiVersion: v1kind: Servicemetadata:  name: myservicespec:  ports:  - protocol: TCP    port: 80    targetPort: 9376---apiVersion: v1kind: Servicemetadata:  name: mydbspec:  ports:  - protocol: TCP    port: 80    targetPort: 9377

Pod 重启的原因

Pod 重启会导致 Init 容器重新执行,主要有如下几个原因:Pod 的基础设施容器 (译者注:如 pause 容器) 被重启。这种情况不多见, 必须由具备 root 权限访问节点的人员来完成。当 restartPolicy 设置为 "Always",Pod 中所有容器会终止而强制重启。 由于垃圾收集机制的原因,Init 容器的完成记录将会丢失。当 Init 容器的镜像发生改变或者 Init 容器的完成记录因为垃圾收集等原因被丢失时, Pod 不会被重启。

Pod hook 钩子 容器生命周期回调

PostStart

这个回调在容器被创建之后立即被执行。 但是,不能保证回调会在容器入口点(ENTRYPOINT)之前执行。 没有参数传递给处理程序

PreStop

在容器因 API 请求或者管理事件(诸如存活态探针、启动探针失败、资源抢占、资源竞争等) 而被终止之前,此回调会被调用。 如果容器已经处于已终止或者已完成状态,则对 preStop 回调的调用将失败。 在用来停止容器的 TERM 信号被发出之前,回调必须执行结束。 Pod 的终止宽限周期在 PreStop 回调被执行之前即开始计数,所以无论 回调函数的执行结果如何,容器最终都会在 Pod 的终止宽限期内被终止。 没有参数会被传递给处理程序。
containers:- name: nginx  image: nginx:alpine  lifecycle:	postStart:	  exec:		command: [/bin/sh, -c, 'echo k8s > /usr/share/nginx/html/index.html']	preStop:	  exec:		command: [/bin/sh, -c, 'echo bye > /var/log/nginx/stop.log']	ports:	- containerPort: 80

为容器的生命周期事件设置处理函数

pod健康检查

存活指针

存活探针简单来说就是⽤来检测容器的应⽤程序是否还正常⼯作,如果应⽤程序不正常,即使容器还活着也没有意义了,所以这时候就可以使⽤存活探针来探测,如果应⽤程序不正常,就重启POD。

存活指针语法:

apiVersion: v1kind: Podmetadata:  name: liveness-podspec:  nodeName: node2  volumes:  - name: nginx-html  	hostPath:	  path: /usr/share/nginx/html/  containers:  - name: liveness    image: nginx    imagePullPolicy: IfNotPresent    lifecycle:      postStart:    	exec:    	  command: [/bin/sh, -c, 'echo k8s > /usr/share/nginx/html/index.html']    livenessProbe:      exec:        command:        - cat		- /usr/share/nginx/html/index.html	  initialDelaySeconds: 3      periodSeconds: 1    volumeMounts:	- name: nginx-html	  mountPath: /usr/share/nginx/html/  

配置解释:

livenessProbe: #存活探针  exec: #执⾏命令    command: #具体的命令结果0的状态被视为存活,⾮零是不健康的。    - cat    - /tmp/healthy  initialDelaySeconds: 3 #第⼀次执⾏探针的时候等待5秒  periodSeconds: 1 #每隔5秒执⾏⼀次存活探针,默认为10秒,最⼩值为1秒

通过命令可以查看Pod的详细状态

kubectl describe pod liveness-podkubectl get pod -w

除了我们⾃⼰写的命令以外,也⽀持基于http请求:

apiVersion: v1kind: Podmetadata:  name: liveness-podspec:  nodeName: node2  volumes:   - name: nginx-html    hostPath:       path: /usr/share/nginx/html/  containers:  - name: liveness    image: nginx     imagePullPolicy: IfNotPresent    lifecycle:      postStart:        exec:          command: [/bin/sh, -c, 'echo k8s > /usr/share/nginx/html/index.html']    livenessProbe:      #exec:      #  command:      #  - cat      #  - /usr/share/nginx/html/index.html      httpGet:        path: /index.html        port: 80      initialDelaySeconds: 3      periodSeconds: 1    volumeMounts:    - name: nginx-html      mountPath: /usr/share/nginx/html/

参数解释

livenessProbe:  httpGet: #基于http请求探测	path: /health.html #请求地址,如果这个地址返回的状态码在200-400之间正常	port: 80 #请求的端⼝  initialDelaySeconds: 3 #第⼀次启动探测在容器启动后3秒开始  periodSeconds: 3 #容器启动后每隔3秒检查⼀次

就绪探针

有时候我们Pod本身已经起来了,但是pod的容器还没有完全准备好对外提供服务,那么这时候流量进来就会造成请求失败的情况出现,针对这种情况k8s有⼀种探针叫就绪探针,他的作⽤就是让k8s知道你的Pod内应⽤是否准备好为请求提供服务。只有就绪探针ok了才会把流量转发到pod上。

配置语法:就绪探针的配置语法和存活探针基本⼀样

apiVersion: v1kind: Podmetadata:  name: liveness-podspec:  containers:  - name: liveness-pod    image: nginx    lifecycle:	  postStart:        exec:          command: ["/bin/sh", "-c", "echo ok > /usr/share/nginx/html/health.html"]    readinessProbe:	  httpGet:		path: /actuator/health		port: 8080	  initialDelaySeconds: 5	  timeoutSeconds: 3      periodSeconds: 3      successThreshold: 3      failureThreshold: 3	livenessProbe:	  httpGet:		path: /health.html		port: 80	  initialDelaySeconds: 3	  periodSeconds: 3

参数解释

initialDelaySeconds: 第⼀次执⾏探针需要在容器启动后等待的时候时间periodSeconds: 容器启动后每隔多少秒执⾏⼀次存活探针timeoutSeconds: 探针超时时间,默认1秒,最⼩1秒successThreshold: 探针失败后最少连续探测成功多少次才被认定成功,默认1次,如果是liveness必须为1failureThreshold: 探针成功后被视为失败的探测的最⼩连续失败次数。默认3次。最⼩值为1

pod资源限制

容器中的程序要运行,肯定是要占用一定资源的,比如cpu和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量资源,导致其它容器无法运行。针对这种情况,kubernetes提供了对内存和cpu的资源进行配额的机制,这种机制主要通过resources选项实现,他有两个子选项:

  • limits:用于限制运行时容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启
  • requests :用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动

可以通过上面两个选项设置资源的上下限。

接下来,编写一个测试案例,创建pod-resources.yaml

apiVersion: v1kind: Podmetadata:  name: pod-resources  namespace: devspec:  containers:  - name: nginx    image: nginx:1.17.1    resources: # 资源配额      limits:  # 限制资源(上限)        cpu: "2" # CPU限制,单位是core数        memory: "10Gi" # 内存限制      requests: # 请求资源(下限)        cpu: "1"  # CPU限制,单位是core数        memory: "10Mi"  # 内存限制

在这对cpu和memory的单位做一个说明:

  • cpu:core数,可以为整数或小数
  • memory: 内存大小,可以使用Gi、Mi、G、M等形式
# 运行Pod[root@k8s-master01 ~]# kubectl create  -f pod-resources.yamlpod/pod-resources created# 查看发现pod运行正常[root@k8s-master01 ~]# kubectl get pod pod-resources -n devNAME            READY   STATUS    RESTARTS   AGE  pod-resources   1/1     Running   0          39s   # 接下来,停止Pod[root@k8s-master01 ~]# kubectl delete  -f pod-resources.yamlpod "pod-resources" deleted# 编辑pod,修改resources.requests.memory的值为10Gi[root@k8s-master01 ~]# vim pod-resources.yaml# 再次启动pod[root@k8s-master01 ~]# kubectl create  -f pod-resources.yamlpod/pod-resources created# 查看Pod状态,发现Pod启动失败[root@k8s-master01 ~]# kubectl get pod pod-resources -n dev -o wideNAME            READY   STATUS    RESTARTS   AGE          pod-resources   0/1     Pending   0          20s    # 查看pod详情会发现,如下提示[root@k8s-master01 ~]# kubectl describe pod pod-resources -n dev......Warning  FailedScheduling  35s   default-scheduler  0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 Insufficient memory.(内存不足)
上一篇:kubernetes Istio是什么


下一篇:kubernetes 查看所有namespace、默认的namespace