理解 Pod 和容器设计模式_Kubernetes开发与运维_技术课程_开发者学堂

阿里云
为了无法计算的价值
打开APP
阿里云APP内打开
开发者社区> 开发者学堂> 全部课程> 理解 Pod 和容器设计模式

理解 Pod 和容器设计模式

1课时 |
498人已学 |
免费
课程介绍

本节课程由阿里云与 CNCF 共同推出。通过课程学习,您将了解到 Kubernetes 体系中最重要的一个基础知识点,也就是 Pod 与容器设计模式。阿里巴巴高级技术专家、CNCF 官方大使张磊将通过真实案例为您阐述“为什么我们需要 Pod ”、 Pod 的实现机制,以及 Kubernetes 非常提倡的一个概念,叫做容器设计模式。

【公开课】理解 Pod 和容器设计模式

 

内容介绍:

一、为什么我们需要Pod

二、Pod的实现机制

三、详解容器设计模式

四、总结

 

一、为什么我们需要Pod

Pod是Kubernetes项目中一个重要的概念,是一个非常最要的原子

1.容器的基本概念

容器的本质:

一个视图被隔离、资源受限的进程

容器里PID =1的进程就是应用本身

管理虚拟机=管理基础设施;管理容器=直接管理应用本身

是不可变基础设施最佳的体现

那么Kubernetes呢?

Kubernetes 就是云时代的操作系统!

以此类推,容器镜像其实就是:这个操作系统的软件安装包

2.真实操作系统案例

举例: helloworld程序

helloworld程序实际上是由一组进程(Linux里的线程)组成

$ pstree -p

...

|helloworld,3062l

|  |-{api},3063

|  |-{main} ,3064l

|  |-{log} ,3065

|  、-{ compute} ,3133

进程等同于Linux 里的线程

在Linux 里查看helloworld里面的pstree

有四个线程组成,api、main、log、compute

这4个进程共享helloworld程序的资源,相互协作,完成 helloworld程序的工作

思考

Kubernetes =操作系统(比如:Linux)

容器=进程(Linux线程)

Pod = ?

进程组(Linux线程组)

在真实的操作系统中,一个程序往往是由进程组进行管理

将Kubernetes 类比为操作系统,如Linux,也可以类比为进程,容器也可以类比为进程,也就是Linux线程,而Pod即为进程组,也就是Linux线程组这样一个类比关系

3.进程组

举例:

helloworld程序由4个进程组成,这些进程之间共享某些文件

问题: helloworld程序如何用容器跑起来呢?

解法一:启动一个Docker 容器中,启动这4个进程

疑问∶容器PID= 1的进程就是应用本身比如main进程,那么“谁"来负责管理剩余的3个进程?

核心问题:容器是“单进程"模型!

由于容器等于应用等于进程,只能管理PID=1的进程

除非:

应用进程本身具备"进程管理"能力〈这意味着: helloworld程序需要具备systemd的能力)

或者,容器的PID=1进程改成systemd,或者在容器运行systemd

这会导致:管理容器=管理systemd !=直接管理应用本身

由于容器实际是一个单进程模型,如果在容器里启动多个进程,只有其中一个可以作为PID=1的进程,如果PID=1的进程失败退出了,其他三个进程无法管理和回收资源

如果在容器里运行一个systemd,用systemd来管理其他进程,会出现没办法直接管理应用,应用被systemd接管,应用状态的生命周期不等于容器的生命周期

4.Pod=“进程组”

$ pstree -p

...

|helloworld,3062l

|  |-{api},3063

|  |-{main} ,3064l

|  |-{log} ,3065

|  、-{ compute} ,3133

apiVersion: v1

kind: Pod

metadata:

name: helloworld

spec: .

containers:

- name: api

image: api

ports:

- containerPort: 80

- name: main

image: main

- name: log

image: log

volumeMounts:

- name: log-storage

name: compute

image: compute

volumeMounts:

- name: data-storage

在Kubernetes里面,Pod实际上是Kubernetes项目抽象出来的一个可以类比为进程组的概念。由四个进程共同组织的一个应用helloworld,在Kubernetes里面实际上会被定义为一个拥有四个容器的Pod。有四个进程,有四个职责不同、相互协作的进程要需要放在容器里运行。

在Kubernetes里,并不会将他们一起放入一个容器里,在Kubernetes中,把四个独立的进程分别用四个独立的容器启动起来,然后定义在一个Pod中。当Kubernetes把helloworle运行起来时,实际上会看到四个容器。他们共享某些资源,这些资源都属于Pod。Pod在Kubernetes中只是一个逻辑单位,没有一个真实的东西对应这个Pod。物理上存在的是四个容器,这四个容器的组合成为Pod,Pod是Kubernetes分配资源的一个单位。因为这些容器它要共享资源,所以Pod是多个容器的组合Kubernetes的原子调度单位

Pod:一个逻辑单位,多个容器的组合Kubernetes的原子调度单位

5.来自Google Borg的思考

Google的工程师们发现,在 Borg 项目部署的应用,往往都存在着类似于“进程和进程组”的关系。更具体地说,就是这些应用之间有着密切的协作关系,使得它们必须部署在同一台机器上并且共享某些信息

是进程组的概念,也是Pod的用法

- Large-scale cluster management at Google with Borg, EuroSys'15

6.为什么Pod必须是原子调度单位

举例:两个容器紧密协作

App:业务容器,写日志文件

LogCollector:转发日志文件到ElasticSearch 中

内存要求:

App: 1G

LogCollector: 0.5G

当前可用内存:

Node_A: 1.25G

Node_B: 2G

如果App 先被调度到了Node_A上,会怎么样?

LogCollector无法调度到了Node_A上,因为资源不够

整个应用本身出现问题

Task co-scheduling 问题

Mesos:资源囤积(resource hoarding) :

所有设置了Affinity约束的任务都达到时,才开始统一进行调度

调度效率损失和死锁

Google Omega:乐观调度处理冲突:

先不管这些冲突,而是通过精心设计回轨机制在出现了冲突之后解决问题

复杂

Kubernetes: Pod

7.再次理解Pod

亲密关系-调度解决

两个应用需要运行在同一台宿主机上

超亲密关系-Pod解决

会发生直接的文件交换

使用localhost或者Socket文件进行本地通信

会发生非常频繁的RPC调用

会共享某些Linux Namespace (比如,一个容器要加入另一个容器的Network Namespace)

 

 

二、Pod的实现机制

1.Pod 要解决的问题

如何让一个Pod里的多个容器之间最高效的共享某些资源和数据?

容器之间原本是被Linux Namespace和cgroups隔离开的

2.共享网络

容器A和B

通过Infra Container 的方式共享同一个Network Namespace:

镜像: k8s.gcr.io/pause;汇编语言编写的、永远处于“暂停";大小100~200 KB

直接使用localhost进行通信

看到的网络设备跟Infra容器看到的完全一样

一个Pod只有一个IP地址,也就是这个Pod的NetworkNamespace对应的IP地址

所有网络资源,都是一个Pod一份,并且被该Pod中的所有容器共享,整个Pod的生命周期跟Infra容器一致,而与容器A和B无关

Pod不会重建,也不会重启

3.共享存储

apiversion : v1

kind: Pod

metadata:

name : two-containers

spec :

restartPolicy : Never

volumes:

- name : shared-data

hostPath :

path: / data

containers :

- name: nginx-container

image: nginx

volumeMounts :

- name: shared-data

mountPath: /usr/ share/nginx / html

- name : debian-container

image: debian

volumeMounts:

- name : shared-data

mountPath: / pod-data

command: [ " /bin/sh" ]

args: [ "-c", "echo Hello from the debian container > / pod-datalindex.html" ]

shared-data 对应在宿主机上的目录会被同时绑定挂载进了上述两个容器当中

 

三、详解容器设计模式

举例:WAR包+ Tomcat的容器化

方法一:把WAR包和Tomcat打包进一个镜像

无论是WAR包和Tomcat更新都需要重新制作镜像

方法二︰镜像里只打包Tomcat。使用数据卷(hostPath)从宿主机上将WAR包挂载进Tomcat容器

需要维护―套分布式存储系统,容器可迁移,不固定

容器本身要依赖一套持久化的存储

有没有更通用的方法?

1.InitContainer

Init Container会比spec.containers定义的用户容器先启动,并且严格按照定义顺序依次执行

/app是一个Volume

一个Pod里的多个容器可以共享Volume

Tomcat容器,同样声明了挂载该Volume 到自己的webapps目录下

故当Tomcat容器启动时,它的webapps目录下就一定会存在sample.war文件

apiversion : v1

kind: Pod

metadata :

name: javaweb-2

spec:

initcontainers:

- image: resouer/ sample:v2

name: war

command: [ "cp" , " l sample.war" , " l app" ]

volumeMounts :

- mountPath: l app

name: app-volume

containers:

- image: resouer/tomcat : 7 .0

name: tomcat

command:[ "sh","-c","l root/apache-tomcat-7.0.42-v2/bin/ start.sh" ]

volumeMounts:

- mountPath: lroot/ apache-tomcat-7.0.42-v2/webapps

name: app-volume

ports :

- containerPort: 8080

hostPort: 8001

volumes :

- name: app-volume

emptyDir: {}

2.容器设计模式: Sidecar

通过在Pod里定义专门容器,来执行主业务容器需要的辅助工作

比如︰

原本需要SSH进去执行的脚本

日志收集(本身是一个进程,可以打包到Pod中做数据工作)

Debug应用

应用监控

优势︰

将辅助功能同主业务容器解耦.实现独立发布和能力重用

3.Sidecar:应用与日志收集

业务容器将日志写在Volume 里

Volume 在Pod中共享

日志容器共享该Volume 从而将日志转发到远程存储当中

Fluentd等,都是这样的工作方式

4.Sidecar:代理容器

代理容器:有一个Pod,需要访问外部系统或外部服务,但是这些外部系统或服务是一个集群状态

代理容器对业务容器屏蔽被代理的服务集群,简化业务代码的实现逻辑

提示:

容器之间通过localhost直接通信

代理容器的代码可以被全公司重用

不会降低性能

5.Sidecar:适配器容器

适配器容器将业务容器暴露出来的接口转换为另一种格式

举例:

业务容器暴露出来的监控接口是/metrics

Monitoring Adapter将其转换为/healthz 以适配新的监控系统

 

提示:

容器之间通过 localhost直接通信

代理容器的代码可以被全公司重用

 

四、总结

Pod是Kubernetes项目里实现“容器设计模式"的核心机制

“容器设计模式"是Google Borg的大规模容器集群管理最佳实践之一

也是Kubernetes进行复杂应用编排的基础依赖之一

所有“设计模式"的本质都是:解耦和重用