前端未来的主流技术方向有哪些?腾讯、京东、同城旅行等大厂都是怎么布局的?戳此了解 了解详情
写点什么

OpenStack 容器服务 Zun 初探与原理分析

2019 年 7 月 19 日

OpenStack容器服务Zun初探与原理分析

01 Zun 服务简介

Zun 是 OpenStack 的容器服务(Containers as Service),类似于 AWS 的 ECS 服务,但实现原理不太一样,ECS 是把容器启动在 EC2 虚拟机实例上,而 Zun 会把容器直接运行在 compute 节点上。


和 OpenStack 另一个容器相关的 Magnum 项目不一样的是:Magnum 提供的是容器编排服务,能够提供弹性 Kubernetes、Swarm、Mesos 等容器基础设施服务,管理的单元是 Kubernetes、Swarm、Mesos 集群,而 Zun 提供的是原生容器服务,支持不同的 runtime 如 Docker、Clear Container 等,管理的单元是 container。


Zun 服务的架构如图:



Zun 服务和 Nova 服务的功能和结构非常相似,只是前者提供容器服务,后者提供虚拟机服务,二者都是主流的计算服务交付模式。功能类似体现在如下几点:


  • 通过 Neutron 提供网络服务。

  • 通过 Cinder 实现数据的持久化存储。

  • 都支持使用 Glance 存储镜像。

  • 其他如 quota、安全组等功能。


组件结构结构相似则表现在:


  • 二者都是由 API、调度、计算三大组件模块构成,Nova 由 nova-api、nova-scheduler、nova-compute 三大核心组件构成,而 Zun 由 zun-api、zun-compute 两大核心组件构成,之所以没有 zun-scheduler 是因为 scheduler 集成到 zun-api 中了。

  • nova-compute 调用 compute driver 创建虚拟机,如 Libvirt。zun-compute 调用 container driver 创建容器,如 Docker。

  • Nova 通过一系列的 proxy 代理实现 VNC(nova-novncproxy)、Splice(nova-spiceproxy)等虚拟终端访问,Zun 也是通过 proxy 代理容器的 websocket 实现远程 attach 容器功能。


02 Zun 服务部署

Zun 服务部署和 Nova、Cinder 部署模式类似,控制节点创建数据库、Keystone 创建 service 以及注册 endpoints 等,最后安装相关包以及初始化配置。计算节点除了安装 zun-compute 服务,还需要安装要使用的容器,比如 Docker。详细的安装过程可以参考官方文档,如果仅仅是想进行 POC 测试,可以通过 DevStack 自动化快速部署一个 AllInOne 环境,供参考的 local.conf 配置文件如下:



如上配置会自动通过 DevStack 安装 Zun 相关组件、Kuryr 组件以及 Docker。


03 Zun 服务入门

3.1 Dashboard

安装 Zun 服务之后,可以通过 zun 命令行以及 Dashboard 创建和管理容器。


有一个非常赞的功能是如果安装了 Zun,Dashboard 能够支持 Cloud Shell,用户能够在 DashBoard 中进行交互式输入 OpenStack 命令行。



原理的话就是通过 Zun 启动了一个 gbraad/openstack-client:alpine 容器。


通过 Dashboard 创建容器和创建虚拟机的过程非常相似,都是通过 panel 依次选择镜像(image)、选择规格(Spec)、选择或者创建卷(volume)、选择网络(network/port)、选择安全组(SecuiryGroup)以及 scheduler hint,如图:



其中 Miscellaneous 杂项中则为针对容器的特殊配置,比如设置环境变量(Environment)、工作目录(Working Directory)等。


3.2 命令行操作

通过命令行创建容器也非常类似,使用过 nova 以及 docker 命令行的基本不会有困难,下面以创建一个 mysql 容器为例:



  • 如上通过–mount 参数指定了 volume 大小,由于没有指定 volume_id,因此 Zun 会新创建一个 volume。需要注意的是,Zun 创建的 volume 在容器删除后,volume 也会自动删除(auto remove),如果需要持久化 volume 卷,则应该先通过 Cinder 创建一个 volume,然后通过 source 选项指定 volume_id,此时当容器删除时不会删除已有的 volume 卷。

  • 和虚拟机不一样,虚拟机通过 flavor 配置规格,容器则直接指定 cpu、memory、disk。

  • 如上没有指定–image-driver 参数,则默认从 dockerhub 下载镜像,如果指定 glance,则会往 glance 下载镜像。


另外 mysql 容器初始化时数据卷必须为空目录,挂载的 volume 新卷格式化时会自动创建 lost+found 目录,因此需要手动删除,否则 mysql 容器会初始化失败:



创建完成后可以通过 zun list 命令查看容器列表:



可以看到 mysql 的容器 fixed IP 为 192.168.233.80,和虚拟机一样,租户 IP 默认与外面不通,需要绑定一个浮动 IP(floating ip),



zun 命令行目前还无法查看 floating ip,只能通过 neutron 命令查看,获取到 floatingip 并且安全组入访允许 3306 端口后就可以远程连接 mysql 服务了:



当然在同一租户的虚拟机也可以直接通过 fixed ip 访问 mysql 服务:



可见,通过容器启动 mysql 服务和在虚拟机里面部署 mysql 服务,用户访问上没有什么区别,在同一个环境中,虚拟机和容器可共存,彼此可相互通信,在应用层上可以完全把虚拟机和容器透明化使用,底层通过应用场景选择虚拟机或者容器。


3.3 关于 capsule

Zun 除了管理容器 container 外,还引入了 capsule 的概念,capsule 类似 Kubernetes 的 pod,一个 capsule 可包含多个 container,这些 container 共享 network、ipc、pid namespace 等。


通过 capsule 启动一个 mysql 服务,声明 yaml 文件如下:



创建 mysql capsule:



可见 capsule 的 init container 用的就是 kubernetes 的 pause 镜像。


3.4 总结

OpenStack 的容器服务本来是在 Nova 中实现的,实现了 Nova ComputeDriver,因此 Zun 的其他的功能如容器生命周期管理、image 管理、service 管理、action 管理等和 Nova 虚拟机非常类似,可以查看官方文档,这里不再赘述。


04 Zun 实现原理

4.1 调用容器接口实现容器生命周期管理

前面提到过 Zun 主要由 zun-api 和 zun-compute 服务组成,zun-api 主要负责接收用户请求、参数校验、资源准备等工作,而 zun-compute 则真正负责容器的管理,Nova 的后端通过 compute_driver 配置,而 Zun 的后端则通过 container_driver 配置,目前只实现了 DockerDriver。因此调用 Zun 创建容器,最终就是 zun-compute 调用 docker 创建容器。


下面以创建一个 container 为例,简述其过程。


4.1.1 zun-api

首先入口为 zun-api,主要代码实现在 zun/api/controllers/v1/containers.py 以及 zun/compute/api.py,创建容器的方法入口为 post()方法,其调用过程如下:


zun/api/controllers/v1/containers.py


  1. policy enforce: 检查 policy,验证用户是否具有创建 container 权限的 API 调用。

  2. check security group: 检查安全组是否存在,根据传递的名称返回安全组的 ID。

  3. check container quotas: 检查 quota 配额。

  4. build requested network: 检查网络配置,比如 port 是否存在、network id 是否合法,最后构建内部的 network 对象模型字典。注意,这一步只检查并没有创建 port。

  5. create container object:根据传递的参数,构造 container 对象模型。

  6. build requeted volumes: 检查 volume 配置,如果传递的是 volume id,则检查该 volume 是否存在,如果没有传递 volume id 只指定了 size,则调用 Cinder API 创建新的 volume。


zun/compute/api.py


  1. schedule container: 使用 FilterScheduler 调度 container,返回宿主机的 host 对象。这个和 nova-scheduler 非常类似,只是 Zun 集成到 zun-api 中了。目前支持的 filters 如 CPUFilter、RamFilter、LabelFilter、ComputeFilter、RuntimeFilter 等。

  2. image validation: 检查镜像是否存在,这里会远程调用 zun-compute 的 image_search 方法,其实就是调用 docker search。这里主要为了实现快速失败,避免到了 compute 节点才发现 image 不合法。

  3. record action: 和 Nova 的 record action 一样,记录 container 的操作日志。

  4. rpc cast container_create: 远程异步调用 zun-compute 的 container_create()方法,zun-api 任务结束。


4.1.2 zun-compute

zun-compute 负责 container 创建,代码位于 zun/compute/manager.py,过程如下:


  1. wait for volumes avaiable: 等待 volume 创建完成,状态变为 avaiable。

  2. attach volumes:挂载 volumes,挂载过程后面再介绍。

  3. checksupportdisk_quota: 如果使用本地盘,检查本地的 quota 配额。

  4. pull or load image: 调用 Docker 拉取或者加载镜像。

  5. 创建 docker network、创建 neutron port,这个步骤下面详细介绍。

  6. create container: 调用 Docker 创建容器。

  7. container start: 调用 Docker 启动容器。


以上调用 Dokcer 拉取镜像、创建容器、启动容器的代码位于 zun/container/docker/driver.py,该模块基本就是对社区 Docker SDK for Python 的封装。



Zun 的其他操作比如 start、stop、kill 等实现原理也类似,这里不再赘述。


4.2 通过 websocket 实现远程容器访问

我们知道虚拟机可以通过 VNC 远程登录,物理服务器可以通过 SOL(IPMI Serial Over LAN)实现远程访问,容器则可以通过 websocket 接口实现远程交互访问。


Docker 原生支持 websocket 连接,参考 APIAttach to a container via a websocket,websocket 地址为/containers/{id}/attach/ws,不过只能在计算节点访问,那如何通过 API 访问呢?


和 Nova、Ironic 实现完全一样,也是通过 proxy 代理转发实现的,负责 container 的 websocket 转发的进程为 zun-wsproxy。


当调用 zun-compute 的 container_attach()方法时,zun-compute 会把 container 的 websocket_url 以及 websocket_token 保存到数据库中.



zun-wsproxy 则可读取 container 的 websocket_url 作为目标端进行转发:



通过 Dashboard 可以远程访问 container 的 shell:



当然通过命令行 zun attach 也可以 attach container。


4.3 使用 Cinder 实现容器持久化存储

前面介绍过 Zun 通过 Cinder 实现 container 的持久化存储,之前我的另一篇文章介绍了 Docker 使用 OpenStack Cinder 持久化 volume 原理分析及实践,介绍了 john griffith 开发的 docker-cinder-driver 以及 OpenStack Fuxi 项目,这两个项目都实现了 Cinder volume 挂载到 Docker 容器中。另外 cinderclient 的扩展模块 python-brick-cinderclient-ext 实现了 Cinder volume 的 local attach,即把 Cinder volume 挂载到物理机中。


Zun 没有复用以上的代码模块,而是重新实现了 volume attach 的功能,不过实现原理和上面的方法完全一样,主要包含如下过程:


  1. connect volume: connect volume 就是把 volume attach(映射)到 container 所在的宿主机上,建立连接的的协议通过 initialize_connection 信息获取,如果是 LVM 类型则一般通过 iscsi,如果是 Ceph rbd 则直接使用 rbd map。

  2. ensure mountpoit tree: 检查挂载点路径是否存在,如果不存在则调用 mkdir 创建目录。

  3. make filesystem:如果是新的 volume,挂载时由于没有文件系统因此会失败,此时会创建文件系统。

  4. do mount: 一切准备就绪,调用 OS 的 mount 接口挂载 volume 到指定的目录点上。


Cinder Driver 的代码位于`zun/volume/driver.py 的 Cinder 类中,方法如下:



其中 cinder.attach_volume()实现如上的第 1 步,而_mount_device()实现了如上的 2-4 步。


4.4 集成 Neutron 网络实现容器网络多租户

4.4.1 关于容器网络

前面我们通过 Zun 创建容器,使用的就是 Neutron 网络,意味着容器和虚拟机完全等同的共享 Neutron 网络服务,虚拟机网络具有的功能,容器也能实现,比如多租户隔离、floating ip、安全组、防火墙等。


Docker 如何与 Neutron 网络集成呢?根据官方 Docker network plugin API 介绍,插件位于如下目录:


  • /run/docker/plugins

  • /etc/docker/plugins

  • /usr/lib/docker/plugins



由此可见 Docker 使用的是 kuryr 网络插件。


Kuryr 也是 OpenStack 中一个较新的项目,其目标是“Bridge between container framework networking and storage models to OpenStack networking and storage abstractions.”,即实现容器与 OpenStack 的网络与存储集成,当然目前只实现了网络部分的集成。


而我们知道目前容器网络主要有两个主流实现模型:


  • CNM:Docker 公司提出,Docker 原生使用的该方案,通过 HTTP 请求调用,模型设计可参考 The Container Network Model Design,network 插件可实现两个 Driver,其中一个为 IPAM Driver,用于实现 IP 地址管理,另一个为 Docker Remote Drivers,实现网络相关的配置。

  • CNI:CoreOS 公司提出,Kubernetes 选择了该方案,通过本地方法或者命令行调用。


因此 Kuryr 也分成两个子项目,kuryr-network 实现 CNM 接口,主要为支持原生的 Docker,而 kury-kubernetes 则实现的是 CNI 接口,主要为支持 Kubernetes,Kubernetes service 还集成了 Neutron LBaaS,下次再单独介绍这个项目。


由于 Zun 使用的是原生的 Docker,因此使用的是 kuryr-network 项目,实现的是 CNM 接口,通过 remote driver 的形式注册到 Docker libnetwork 中,Docker 会自动向插件指定的 socket 地址发送 HTTP 请求进行网络操作,我们的环境是http://127.0.0.1:23750,即 kuryr-libnetwork.service 监听的地址,Remote API 接口可以参考 Docker Remote Drivers。


4.4.2 kuryr 实现原理

前面 4.1 节介绍到 zun-compute 会调用 docker driver 的 create()方法创建容器,其实这个方法不仅仅是调用 python docker sdk 的 create_container()方法,还做了很多工作,其中就包括网络相关的配置。


首先检查 Docker 的 network 是否存在,不存在就创建,network name 为 Neutron network 的 UUID,



然后会调用 Neutron 创建 port,从这里可以得出结论,容器的 port 不是 Docker libnetwork 也不是 Kuryr 创建的,而是 Zun 创建的。


回到前面的 Remote Driver,Docker libnetwork 会首先 POST 调用 kuryr 的/IpamDriver.RequestAddressAPI 请求分配 IP,但显然前面 Zun 已经创建好了 port,port 已经分配好了 IP,因此这个方法其实就是走走过场。如果直接调用 docker 命令指定 kuryr 网络创建容器,则会调用该方法从 Neutron 中创建一个 port。


接下来会 POST 调用 kuryr 的/NetworkDriver.CreateEndpoint 方法,这个方法最重要的步骤就是 binding,即把 port attach 到宿主机中,binding 操作单独分离出来为 kuryr.lib 库,这里我们使用的是 veth driver,因此由 kuryr/lib/binding/drivers/veth.py 模块的 port_bind()方法实现,该方法创建一个 veth 对,其中一个为 tap-xxxx,xxxx 为 port ID 前缀,放在宿主机的 namespace,另一个为 t_cxxxx 放到容器的 namespace,t_cxxxx 会配置上 IP,而 tap-xxxx 则调用 shell 脚本(脚本位于/usr/local/libexec/kuryr/)把 tap 设备添加到 ovs br-int 桥上,如果使用 HYBRID_PLUG,即安全组通过 Linux Bridge 实现而不是 OVS,则会创建 qbr-xxx,并创建一个 veth 对关联到 ovs br-int 上。


从这里可以看出,Neutron port 绑定到虚拟机和容器基本没有什么区别,如下所示:



唯一不同的就是虚拟机是把 tap 设备直接映射到虚拟机的虚拟设备中,而容器则通过 veth 对,把另一个 tap 放到容器的 namespace 中。


有人会说,br-int 的流表在哪里更新了?这其实是和虚拟机是完全一样的,当调用 port update 操作时,neutron server 会发送 RPC 到 L2 agent 中(如 neutron-openvswitch-agent),agent 会根据 port 的状态更新对应的 tap 设备以及流表。


因此其实 kuryr 只干了一件事,那就是把 Zun 申请的 port 绑定到容器中。


05 总结

OpenStack Zun 项目非常完美地实现了容器与 Neutron、Cinder 的集成,加上 Ironic 裸机服务,OpenStack 实现了容器、虚拟机、裸机共享网络与存储。未来我觉得很长一段时间内裸机、虚拟机和容器将在数据中心混合存在,OpenStack 实现了容器和虚拟机、裸机的完全平等、资源共享以及功能对齐,应用可以根据自己的需求选择容器、虚拟机或者裸机,使用上没有什么区别,用户只需要关心业务针对性能的需求以及对硬件的特殊访问,对负载(workload)是完全透明的。


参考文献



本文转载自公众号 int32bit(ID:int32bit)


原文链接


https://mp.weixin.qq.com/s/aJsTSaCjoO2Fq_nqCkz0ZQ


2019 年 7 月 19 日 08:008850

评论

发布
暂无评论
发现更多内容

如何保护您的SaaS应用程序?

龙归科技

网络安全 SaaS 远程工作 单点登录

我叫小M,立志建立MySQL帝国。

yes

MySQL

Airtest入门及多设备管理总结

行者AI

自动化测试

学习笔记

山@支

Wireshark数据包分析学习笔记Day28

穿过生命散发芬芳

Wireshark 数据包分析 4月日更

webrtc 开启新特性

越努力,越幸运

众盟科技:直播浪潮下,医美行业的私域营销之变

脑极体

如何避免成为一个油腻的中年猥琐男?

石云升

读书笔记 中年 28天写作 4月日更

真香!凭借这份Github上60K+star面试笔记,帮我搞定了字节跳动、阿里、华为、小米95%以上的Java面试题

Java成神之路

Java 程序员 架构 面试 编程语言

飞桨与宸曜科技完成兼容性认证

百度大脑

认证 飞桨

聪明人的训练(八)

Changing Lin

4月日更

揭开MySQL索引神秘面纱

咔咔

MySQL 索引

你对JVM垃圾收集器了解多少?面试官夺命13问谁碰谁不迷糊啊!

北游学Java

Java JVM 垃圾回收

蚂蚁金服二面被血虐,鬼知道面试的我经历了什么?

Java成神之路

Java 程序员 架构 面试 编程语言

百度联合研究成果登上《自然》子刊 推动人才管理大数据智能化转型

百度大脑

百度 AI

stream,source,track关系

越努力,越幸运

MVCC:听说有人好奇我的底层实现

咔咔

MySQL MVCC

Linux下使用简单的一条命令实现控制用户的目录访问权限

代码熬夜敲

Linux 网络安全 信息安全 linux 文件权限控制

Python OpenCV 泛洪填充,取经之旅第 21 天

梦想橡皮擦

Python OpenCV 4月日更

IPFS怎么挖矿?怎么参与IPFS挖矿?

投资矿机v:IPFS1234

IPFS怎么挖矿 怎么参与IPFS挖矿

MySQL查询优化必备

咔咔

MySQL 查询优化

好家伙!Github又现神作,阿里大牛亲码24W字面试总结也太香了!(涵盖P5到P8所有技术点)

程序员小毕

Java 程序员 架构 面试 并发编程

Edge 修改使用的默认搜索引擎

HoneyMoose

2021 优质前端资源精选 —— 持续更新,欢迎共建

清秋

前端 教程 资源 社区 4月日更

面试磕磕碰碰,辛得蚂蚁高级工程师的技术笔记指导,终获P7岗offer

Java成神之路

Java 程序员 架构 面试 编程语言

解Bug之路-主从切换”未成功”?

无毁的湖光

数据库 主从环境

从零开始写游戏服务器①:前期了解

Integer

c

c 语言思维地基搭建(vis2013编译+第一个c语言程序)

-jf.

四月日更

上来就问MySQL事务,瑟瑟发抖...

咔咔

MySQL 事务

在华为云专属月中,寻觅互联网更需要的云味道

脑极体

霸榜GitHub!银四匠心之作:拼多多/蚂蚁/百度面经分享

云流

Java 编程 程序员 架构 面试

技术为帆,纵横四海- Lazada技术东南亚探索和成长之旅

技术为帆,纵横四海- Lazada技术东南亚探索和成长之旅

OpenStack容器服务Zun初探与原理分析-InfoQ