首页 >> 大全

openstack zun源码分析

2023-12-14 大全 23 作者:考证青年

容器服务启动过程

项目包括三个服务,分别是zun-api,zun-,zun-,均使用来管理启动停止,相关的服务文件如 zun-api. 在/etc// 或 /usr/lib//(nova 等在这里)中。nova 等是自动创建的,而zun的是手动创建的,指定了创建位置。

zun-api启动过程

该文件内的指定了启动脚本所在位置,如 /usr/bin/zun-api,该脚本是个代码,启动就是调用了zun.cmd.api.main进行启动

启动之后解析命令行参数,如果设置的有配置文件,则从配置文件中读取数据

重点在.(),构建该对象时,在方法中,app.()加载了api-paste.ini文件,该文件中构建的app只有一个,在zun.api.app.(),这里调用zun.api.app.构建了wsgi应用(即pecan对象,该对象中有的实现,对应方法是),然后又使用wsgi.构建了wsgi服务,传入参数有self.app,也就是该wsgi 和构建的wsgi app关联上了

而该wsgi服务的创建是由提供的。

paste提供的是如何调用配置文件来启动wsgi应用,以及定义执行流程,还有一些路由的功能,通过部分实现,但是zun的配置文件中没有这块,路由不是通过paste实现的,而是pecan实现的

oslo. 提供了一个框架,用于使用其他 应用程序建立的模式定义新的长期运行服务。它还包括长时间运行的应用程序可能需要使用 SSL 或 WSGI、执行定期操作、与 交互等的实用程序。

zun-启动过程

zun-的启动,本质上是rpc 的启动

zun-安装之后,创建启动文件,放置在/etc///zun-.中,其中有/usr/local/bin/zun-这个命令,查看可以看到其中执行了·pute.main()·,该函数内进行·rpc ·的创建,即构建了pute..对象。也就是zun-启动的时候,就启动了rpc

zun创建容器全流程分析

1、 start zun-api启动WSGI ,对接应用主要为zun/api//v1/.py(容器相关代码)

2、以创建容器流程做全过程分析

zun 的其他操作比如 start、stop、kill 容器等实现原理也类似

zun-api详细流程分析

容器服务入口为 zun-api,主要代码实现在 zun/api//v1/.py 以及 zun//api.py

创建容器代码分析

请求体样例

{"name": "aaa","image": "cirros","image_driver": "docker","run": true,"auto_heal": false,"mounts": [],"security_groups": ["default"],"interactive": true,"hints": {},"nets": []
}

路由走向:

zun/api/root.py() –> zun/api//v1/.py() – > zun/api//v1/.py(.post(1.)–>

()方法是创建容器的方法,核心处理代码如下:

...
# 检查 policy,验证用户是否具有创建 container 权限的 API 调用
policy.enforce(context, "container:create", action="container:create")
...
# 检查安全组是否存在,根据传递的名称返回安全组的 ID。
security_group_id = self._check_security_group(context, {'name': sg})
...
# 检查 quota 配额。
self._check_container_quotas(context, container_dict)
...
# 检查网络配置,比如 port 是否存在、network id 是否合法,最后构建内部的 network 对象模型字典。注意,这一步只检查并没有创建 port。
requested_networks = utils.build_requested_networks(context, nets)
...
# 根据传递的参数,构造 container 对象模型。
new_container = objects.Container(context, **container_dict)
...
# 检查 volume 配置,如果传递的是 volume id,则检查该 volume 是否存在,如果没有传递 volume id 只指定了 size,则调用 Cinder API 创建新的 volume。
self._build_requested_volumes(context, new_container, mounts)
...
# 调用zun/compute/api.py中对应的方法创建容器
compute_api.container_create(context, new_container, **kwargs)

经过以上处理之后流程进入zun//api.py内的API.,在这里执行以下步骤:

# 使用 FilterScheduler 调度 container,返回宿主机的 host 对象。这个和 nova-scheduler 非常类似,只是 Zun 集成到 zun-api 中了。目前支持的 filters 如 CPUFilter、RamFilter、LabelFilter、ComputeFilter、RuntimeFilter 等。
host_state = self._schedule_container(context, new_container, extra_spec)
...
# image validation: 检查镜像是否存在,这里会远程调用 zun-compute 的 image_search 方法,其实就是调用 docker search。这里主要为了实现快速校验,避免到了 compute 节点才发现 image 不合法。
if CONF.api.enable_image_validation:try:images = self.rpcapi.image_search(context, new_container.image,new_container.image_driver, True, new_container.registry,host_state['host'])
...
# record action: 和 Nova 的 record action 一样,记录 container 的操作日志。	
self._record_action_start(context, new_container, container_actions.CREATE)
...
# 调用zun/compute/rpcapi.py的API.container_create
self.rpcapi.container_create

注意:方法只支持从搜索,搜不到不要紧,后续会继续执行。但是在zun-中会看到错误如下:ERROR pute. [None req--a6ef-4d51-b2ea- admin admin] while image: Image is not in : : d: Image is not in :

接下来进入 zun//.py(API.)。这个API中的函数即为zun服务提供给RPC调用的接口,其他服务在调用前需要先这个模块。

def container_create(self, context, host, container, limits,requested_networks, requested_volumes, run,pci_requests):self._cast(host, 'container_create', limits=limits,requested_networks=requested_networks,requested_volumes=requested_volumes,container=container,run=run,pci_requests=pci_requests)

self._cast: 通过rpc远程异步调用 zun- 的 ()方法,zun-api 任务结束。

pute..API只是暴露给其他服务的RPC调用接口,服务的RPC 在接收到RPC请求后,真正完成任务的是pute.模块。从pute..API到pute..的过程就是RPC调用的过程。

进入 zun//.py(API.) --> zun//.py ,这个文件内构建的rpc的客户端class API和服务端class

从zun//rpc.py(获取rpc的,构建过程由模块提供),其中的参数由本文件内的init函数执行初始化,而init函数的执行通过mon..()调用,被mon..调用,又被zun.cmd.api.main调用,即在启动zun-api构建WSGI服务时,就已经进行了配置项的初始化。因此在rpc.py文件内找不到init的调用之处。

zun-详细流程分析

在zun//.py中走到self._cast(host, ''时,注意这里的,会到zun//.py中寻找对应的方法。因该文件中只有类,后续会直接说该文件中的方法名,不再说类名。

    def container_create(self, context, limits, requested_networks,requested_volumes, container, run, pci_requests=None):@utils.synchronized(container.uuid)def do_container_create():# 等待 volume 创建完成,状态变为 avaiable。# -->zun.container.docker.driver.DockerDriver.is_volume_available()# -->zun.volume.driver.Cinder.is_volume_avalable() self._wait_for_volumes_available(context, requested_volumes, container)# 这一步的目的是将cinder api创建的卷映射到宿主机上,并挂载到对应目录上self._attach_volumes(context, container, requested_volumes)# 如果使用本地盘,检查本地的 quota 配额。self._check_support_disk_quota(context, container)# 创建容器created_container = self._do_container_create( context, container, requested_networks, requested_volumes, pci_requests, limits)if run:# 创建容器后如设置启动,调用 Docker客户端 启动容器self._do_container_start(context, created_container)utils.spawn_n(do_container_create)

重点过程分析 self.

首先说明一下,容器挂载卷的全流程如下所示:

以上1-2-3就在self.(, , ![在这里插入图片描述]() mes)这段逻辑中完成,因为可能是多卷挂载,因此还会进入 进行处理,这里是针对一个卷的处理过程,细节如下:

注:在上指定的挂载点()是容器内的路径,宿主机的挂载点在 tree会自动创建(路径在zun.conf中的参数配置的目录下mnt内),本例中在`/var/lib/zun/mnt/zun..uuid目录下,这里的uuid对应的是zun库表的uuid字段,不是中的id,中的id对应的是zun库表的。

简单说,1-2的过程与容器无关,因此使用的是 id,与容器映射的目录肯定就是容器相关,使用的就是容器的 uuid。

这两个字段都在zun的表中

以上的挂载实现的是卷挂载到容器宿主机的过程

self. --> _base,到这里开始执行具体操作,本质上就是组织创建容器需要的一系列参数,最后向提供的接口发起创建容器的请求,完成容器的创建。简要内容如下:

# 调用 Docker 拉取或者加载镜像。
self.driver.pull_image,pull or load image
# 具体执行流程:从zun/compute/manager.py(_do_container_create_base)-->zun/container/docker/driver.py(DockerDriver,配置文件配置了container_driver指向这里) -->zun/image/docker/driver.py(DockerDriver.pull_image)# 镜像拉取完成,开始创建容器,调用 Docker 创建容器。
create container: 从zun/compute/manager.py(_do_container_create_base)-->zun/container/docker/driver.py(DockerDriver.create)其中涉及到创建 docker network、创建 neutron port,最后再创建容器,向docker api发起请求创建

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

创建容器过程中的网络相关流程

1、zun.api..v1... 创建容器接口

2、mon.utils.orks 通过调用 客户端构建请求的网络。请求参数中如果有网络相关的参数(其实就是网络的uuid),就使用。如果没有调用可用,如果还没有就抛出异常。返回可用的 数据供后续使用

3、pute...() --> () --> _base(拉取镜像开始调用驱动构建容器)

4、zun.....(),这个方法在调用 sdk的()方法之前,还做了很多工作,其中包括网络相关配置,具体如下:

其实 kuryr 只干了一件事,那就是把 Zun 申请的 port(中的port) 绑定到容器中。即 –kuryr–,kuryr是桥梁

查询容器 查询容器列表

访问url:

zun-api:zun/api//v1/.py

路由走向:

api/root.py()

–> api//v1/.py()

– > api//v1/.py(.)

– >)

做了一些验证,还有请求参数验证是否符合要求,默认情况下是{},最终进入..list(在模型中定义方法),指向到zun.db.api 的数据库操作中,在这里首先获取DB api对象,配置从配置项中获取,同时后端映射采用的zun.db..api,最终是从数据库获取数据返回给

注:此过程不经过zun-

查询单个容器

访问url:

zun-api: zun/api//v1/.py

路由走向:

api/root.py()

–>api//v1/.py()

–>api//v1/.py(.)

核心逻辑:

# 获取该容器的基本信息,读取zun数据库得到
container = utils.get_container(container_ident)utils.get_container--> zun.common.utils.get_container-->zun.api.utils.get_resource-->zun.objects.container.get_by_uuid or get_by_name如果有container.host,最终会调用rpc,运行到zun.compute.manager.Manager.container_show--> zun.container.docker.driver.DockerDriver.show -->实际执行docker inspect命令得到数据 -->数据填充到container中(self._populate_container)返回

删除容器

删除一个容器

:

body [“-3e7b-428d-94f1-”]

源码分析怎么写__源码分析器

zun-api: zun/api//v1/.py

路由走向:

api/root.py()

–> api//v1/.py()

–> api//v1/.py(.)

如果.host存在,即主机节点可以连上,调用pute.api.,通过rpc异步调用pute... --> 。否则调用zun....。仅仅干了一件事,就是从数据库中删除这个容器。

...
# 调用 zun/container/driver/docker.py 中的delete方法,清理容器的网络,然后向docker服务发起实质性删除容器的请求
self.driver.delete(context, container, force)
...
# 卸载卷,详见下方分析
self._detach_volumes(context, container, reraise=reraise)
...
# 摧毁容器在数据库中的内容
container.destroy(context)

卸载卷( )

卸载卷( ):pute...

zun源码中调用的只有在删除和 时才会调用。

核心处理逻辑如下:

zun....

def detach(self, context, volmap):self._unmount_device(volmap)  # 这一步执行是linux命令 umount,卸载卷cinder = cinder_workflow.CinderWorkflow(context)cinder.detach_volume(context, volmap)

1、begin_detaching: 
--> zun.volume.cinder_api.py(begin_detaching 136行)
--> cinderclient.v3.volumes.py(VolumeManager) 
--> cinderclient.v2.volumes.py(VolumeManager.begin_detaching,  os-begin_detaching 具体发起post请求,url是 /volumes/volume-id/action)  
这一步目的是将volume的状态改为detaching  即分离中  Update volume status to detaching。 2、如果volume mapping表中还有该volume的信息,要执行断开连接的操作,通过os_brick包完成该操作3、执行分离卷
zun.volume.cinder_api.py(detach) 
--> cinderclient.v2.volumes.py(VolumeManager.detach)执行 os-detach,cinderclient发送请求。/volumes/volume-id/action  这一步目的从服务器分离卷。卷状态必须为in-use.。4、如果要删除volume,还要执行删除操作并等待删除完成,delete_volume
compute.manager._detach_volumes 
--> container.docker.driver.delete_volume 
--> volume.driver.delete 
--> volume.cinder_workerflow.delete_volume 
--> cinder_api.delete_volume 
--> cinderclient.v2.volumes.delete 
--> cinderclient.base._delete     至此发起删除请求/volumes/volume-id?cascade=True给cinder服务

删除卷的前提条件:

must be , in-use, error, , , , and must not be , , -, to a group, have or be from after .

从上可以看出, 与毫无关系,整个过程就是zun源码执行,而后通过对卷操作(类似卸载u盘的操作,先执行卸载,后拔出,与u盘同理,如果卷被使用着就卸载不掉)

从原生使用的地方,即删除时调用,在之前,先进行了的删除操作。但是只要卷挂载的目录没有被正在使用,就能够执行,可以手动执行 /dev/sdc 进行测试。

通过 实现远程容器访问

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

原生支持 连接,参考 to a via a , 地址为//{id}//ws,不过只能在计算节点访问,那如何通过 API 访问呢,和 Nova、 实现完全一样,也是通过 proxy 代理转发实现的,负责 的 转发的进程为 zun-。

当调用 zun- 的 () (代码在zun//.py中)方法时,zun- 会把 的 以及 保存到数据库中.。zun- 则可读取 的 作为目标端进行转发。

相应代码在 zun\\.py中的t()

的连接是通过包-处理的。

整个流程:浏览器(ws://192.168.221.129:6784/?token=-f887-4ac8-ba11-&uuid=-6bf6-42ab-9811-),访问该地址,到达zun-服务,该服务为每一个新的客户端连接创建 对象,最后通过进行代理转发到对象中设置的目标地址,即数据库中的字段对应的地址

更新容器

目前只支持更新cpu、name、(看页面似乎disk也行,但是代码的patch方法中没有disk)

路由走向:

api/root.py()

–>api//v1/.py()

– >api//v1/.py(.patch)

源码中可看到,如果更新了cpu或者内存,会先检查配额,而后向·zun-·发起请求,进行相应的更新

对存在的容器进行重命名方法是(POST)api//v1/.py(.) ,在zun-api完成操作

原生的按钮不知为何无法点击?存疑

容器配额quota 创建容器时的配额检查

创建容器时,会设置cpu、、如果用户没有输入,默认值在 zun/conf/.py中。即容器驱动中设置容器的一些默认参数。disk并不在其中

另外,创建容器时会检查quota,具体代码逻辑在 zun/api//v1/.py中的tas方法,检查项如下所示:

deltas = {'containers': 0 if update_container else 1,'cpu': container_delta_dict.get('cpu', 0),'memory': container_delta_dict.get('memory', 0),'disk': container_delta_dict.get('disk', 0)
}
# container_delta_dict 是创建容器时提交的容器参数

项目配额

zun-ui没有提供容器配额相关页面,因此后续操作通过命令行进行

初始配额设置位置zun/conf/quota.py

查询默认配额

查询默认配额GET://v1//{}/

这个接口获取的是配置文件(zun/conf/quota.py)中设置的数据

获取项目配额

命令:zun quota-get

接口:GET //v1//{}

路由落脚点在:zun/api//v1/.py(.get)

代码执行过程:

.get

–>.,通过对象

–zun//quota.py(,.()) 。创建时

–>(继承自)

–>(方法)

–> zun/conf/quota.py(该代码内全是设置的默认值,上边执行的命令在数据库没有数据时,就从此处获取默认配置)

具体执行到conf/quota.py中获取参数的代码在zun//quota.py(.

for resource in resources.values():quotas[resource.name] = default_quotas.get(resource.name, resource.default)

这里的.就是获取默认值,是对象,继承自,有方法,但是该方法有@装饰器,因此可以用属性的调用方法调用

zun.conf也能配置这个参数

quota-defaults      Print a default quotas for a project
quota-delete        Delete quotas for a project
quota-get           Print a quotas for a project with usages (optional)
quota-update        Print an updated quotas for a project
quota-class-get     Print a quotas for a quota class
quota-class-update  Print an updated quotas for a quota class

更多信息参考 -.html

增加和修改项目配额

增加和修改项目配额接口PUT://v1//{}

请求体:

{"disk": 200,  "cpu": 30,"containers": 80,"memory": 102400
}

这里的一个参数对应表中的一个字段,即字段内记录的是以上四个选项。另外这里使用发起请求时,不是json格式,-type需要设置成/x-www-form-

代码执行完之后数据会写入表中,表中原本不需要有该条记录。每次执行put请求,都不是更新,而是新增记录,取数据取的最新的记录进行返回。数据库的字段发现未生效。

删除配额

接口:://v1//{}

返回null,会将数据库中这个相关的所有记录全部删除

镜像的增删改查

官方没有image api只有 api,以下内容从源码中分析得到。如有错漏,请告知

zun-ui提供的镜像功能(非镜像仓库)

选择拉取镜像

提供的功能点:

1、pull image,从配置的镜像仓库拉取指定镜像(镜像名)到指定主机上

源码分析怎么写__源码分析器

2、删除镜像(注意不要手动在服务器删除zun拉取的镜像,否则zun-ui会删不掉该镜像在数据库的数据,执行不到这一步还看不到错误输出。实际应该在zun///.py -->第一行就有问题了,因为查不到这个镜像了)

3、过滤搜索镜像,即页面上的搜索框(注意这里走的接口仍然是获取所有镜像,前端完成的过滤,与通常理解的搜索逻辑不同)

4、镜像列表

本质上这里执行的是 pull ,从镜像仓库拉取镜像到本地

拉取完成后,在对应的host上通过 命令可以看到拉取成功的镜像。

API(无官方文档):

通过命令行创建的镜像zun-ui看不到,或通过zun-ui pull的镜像,命令行也看不到,但是数据库是有数据的,造成该现象的原因可能是命令和页面看的不是一个项目所导致

容器

通过原生zun-ui,是没有提供类似 功能的入口的,但是源码中提供有接口完成此功能。

zun-api中接口

def commit(self, container_ident, **kwargs):"""Create a new image from a container's changes.:param container_ident: UUID or Name of a container."""container = utils.get_container(container_ident)check_policy_on_container(container.as_dict(), "container:commit")utils.validate_container_state(container, 'commit')LOG.debug('Calling compute.container_commit %s ', container.uuid)context = pecan.request.contextcompute_api = pecan.request.compute_apipecan.response.status = 202return compute_api.container_commit(context, container,kwargs.get('repository', None),kwargs.get('tag', None))

核心只在最后调用完成此功能

zun-中

def container_commit(self, context, container, repository, tag=None):# NOTE(miaohb): Glance is the only driver that support image# uploading in the current version, so we have hard-coded here.# https://bugs.launchpad.net/zun/+bug/1697342# 从上提到的glance是支持镜像上传的唯一驱动。docker是不支持的。创建glance镜像,此处并未上传镜像,尚未使用docker commit从容器创建一个新的镜像snapshot_image = self.driver.create_image(context, repository,glance.GlanceDriver())...self._do_container_commit(context, snapshot_image, container,repository, tag)

核心内容如下:

...
# ensure the container is paused before doing commit
# commit之前要确保容器状态是暂停
container = self.driver.pause(context, container)
...
# 执行docker commit操作
# 流程经过 zun/container/docker/driver.py(commit)  --> docker/api/container.py(commit) 实质上向docker服务提供的api发起commit操作
container_image_id = self.driver.commit(context, container, repository, tag)# 获取image,核心执行的是docker/api/image.py(get_image)
# Get a tarball of an image. Similar to the ``docker save`` command. 可以看到执行的是docker save操作,返回镜像数据
container_image = self.driver.get_image(repository + ':' + tag)
...
#  如果之前操作了暂停容器,此时要将容器恢复
container = self.driver.unpause(context, container)
...
# 将镜像上传到glance,当前版本不支持上传到dockerhub等仓库
self._do_container_image_upload(context, snapshot_image,container_image_id,container_image, tag)

api见 a new image from a

an image 接口

浏览器通过非方式访问容器的可行性研究 exec

exec命令的流程(该命令在 api中相当于执行俩api,分别是 an exec 和 start an exec )

该命令的实质是让容器能像宿主机中一样执行命令。只是可以通过指定i和t参数,来得到一个tty,方便操作。最终实质是要执行的命令。

因此要执行的命令是不可缺少的。这也决定了通过该命令开一个终端是不现实的,因为没有命令不会给你显示伪终端。而要执行命令,如bash,sh等,又不确定每个容器内都存在,就导致不能使用该方案。

在对应接口中,api文档中给了四个参数分别是 ,,run,

run在代码中默认值是true,即exec 之后还要执行exec run,实际就是 api中的start,(代码中默认值false)代表了 api中的两个参数和Tty(zun.....中进行了赋值,二者值与保持一致)

执行流程:

zun.api..v1...

–>pute...

–>zun.....

–>.api...,在这里拼装了请求体,拼装了url,最后发起post请求,self._url和self.在–>.api..中实现,post请求调用的是发起的

如果run是true就要start

–>zun.....

–>.api... 这一步对接的就是 api的start exec接口(这里将、tty、参数全部写死为false了)

但是start传递的参数比 api要多,而且 api没有返回值,这里却有返回值可以接收

start之后,还要执行 获取容器的简单信息,对应的也是 api中的 an exec

ssh方式

在使用 平台时,发现该平台提供给用户的就是容器,可以在浏览器访问,执行exit也不会导致容器停止。经过调研发现该平台是通过ssh方式访问容器的。

但是ssh方式也不可行,ssh连接需要服务器内安装有sshd服务并开启。每个容器是否存在该服务是不确定的,因此该方案也不行

关于ssh方式实现访问可参考xterm.js

研究该问题的初衷是因为浏览器上进入容器后,在容器内执行exit命令,会使容器直接退出,状态变为,这是因为本质上是通过 的方式进入容器的,而该方式在容器内执行exit,就是会导致容器停止。希望通过其他方案不让容器退出,但是目前看没有可行性

paste配置文件详解

# 这是一个composite段,表示这将会根据一些条件将web请求调度到不同的应用
[composite:main]
use = egg:Paste#urlmap  # 表示我们将使用Paste egg包中urlmap来实现composite
/ = home
/blog = blog  # 根据web请求的path的前缀进行一个到应用的映射(map)
/wiki = wiki
/cms = config:cms.ini  # 映射到了另外一个配置文件,Paste Deploy再根据这个文件进行载入
# app是一个callable object,接受的参数(environ,start_response)app需要完成的任务是响应envrion中的请求,准备好响应头和消息体,然后交给start_response处理,并返回响应消息体
[app:home]  
use = egg:Paste#static  # Paste包中的一个简单程序,它只处理静态文件
# 它需要一个配置文件document_root,后面的值可以是一个变量,形式为%(var)s相应的值应该在[DEFAULT]字段指明以便Paste读取。
document_root = %(here)s/htdocs  [filter-app:blog]  # filter是一个callable object,其唯一参数是(app),这是WSGI的application对象,filter需要完成的工作是将application包装成另一个application(“过滤”),并返回这个包装后的application。
use = egg:Authentication#auth
next = blogapp  # 在正式调用blogapp之前,我会调用egg:Authentication#auth进行一个用户的验证,随后才会调用blogapp进行处理
roles = admin
htpasswd = /home/me/users.htpasswd[app:blogapp]  # 定义了blogapp,并指明了需要的database参数。
use = egg:BlogApp
database = sqlite:/home/me/blog.db[app:wiki]
#  call(表示使用call方法):模块的完成路径名字:应用变量的完整名字
use = call:mywiki.main:application  # 使用call的话,相应的函数,类,实例中必须实现call()方法。
database = sqlite:/home/me/wiki.db

# pipeline就是简化了filter-app,不然你想,如果我有十个filter,那不是要写十
# 个filter-app(有next),通过pipeline,我就可以把这些filter都连起来写在一行,很方便。但要注意的是这些filter需要有一个app作为结尾。
[pipeline:main]  
pipeline = cors request_id osprofiler authtoken api_v1
# 定义WSGI应用,main表示只有一个应用,有多个应用的话main改为应用名字
#  定义application需要运行的Python code
# 这种方式必须明确指定使用的protocol(此例中是paste.app_factory),value值表
# 示需要import的内容。此例中是import zun.api.app,然后检测app_factory执行。
[app:api_v1]
paste.app_factory = zun.api.app:app_factory
# 就是一个过滤
[filter:authtoken]
acl_public_routes = /, /v1
paste.filter_factory = zun.api.middleware.auth_token:AuthTokenMiddleware.factory[filter:osprofiler]
paste.filter_factory = zun.common.profiler:WsgiMiddleware.factory[filter:request_id]
paste.filter_factory = oslo_middleware:RequestId.factory[filter:cors]
paste.filter_factory =  oslo_middleware.cors:filter_factory
oslo_config_project = zun

uwsgi部署实现

参考官方文档

创建文件 /etc/zun/zun-uwsgi.ini ,放在其他位置也是可以的

[uwsgi]
http = 0.0.0.0:9517
wsgi-file = /zun/api/app.wsgi
plugins = python
# This is running standalone
master = true
# Set die-on-term & exit-on-reload so that uwsgi shuts down
exit-on-reload = true
die-on-term = true
# uwsgi recommends this to prevent thundering herd on accept.
thunder-lock = true
# Override the default size for headers from the 4k default. (mainly for keystone token)
buffer-size = 65535
enable-threads = true
# Set the number of threads usually with the returns of command nproc
threads = 8
# Make sure the client doesn't try to re-use the connection.
add-header = Connection: close
# Set uid and gip to a appropriate user on your server. In many
# installations ``zun`` will be correct.
uid = zun
gid = zun

启动:uwsgi ./zun-uwsgi.ini

后台启动:uwsgi -d ./zun-uwsgi.ini

依旧是使用paste启动wsgi 应用,对比二者启动方式发现,这里仅是用uwsgi代替了 zun.cmd.api.main中的wsgi 功能,不再走zun提供的wsgi服务,而是走wsgi-file指定的文件,如zun/api/app.wsgi,其余不变,仅此而已

如果要默认使用uwsgi启动zun-api,还得修改/etc///zun-api.

测试接口说明

当无法通过完成请求时,需要使用或其他工具直接访问zun-api提供的接口

获取服务接口

通过 list 命令可以看到各个组件的接口,如下所示:

如zun-api对应::9517/v1 ,然后再到各组件的api文档中查看各个请求的接口url,拼接后就是完整的请求路径。

使用组成的url直接通过浏览器访问是会报401错的。因为没有携带认证信息

通过以下命令获取token

/etc// 这个文件由自己决定放在哪里,内容大概如下:

export OS_PROJECT_DOMAIN_NAME=Default 
export OS_USER_DOMAIN_NAME=Default 
export OS_PROJECT_NAME=admin 
export OS_USERNAME=admin 
export OS_PASSWORD=hy@123 
export OS_AUTH_URL=http://controller:5000/v3 
export OS_IDENTITY_API_VERSION=3 
export OS_IMAGE_API_VERSION=2

通过 token issue 可以拿到当前用户的token,下图中的id就是

然后请求上边的url时将token加入中。参数名是 X-Auth-Token 值就是token。这样就可以测试所有api了。

发现的一些问题 1、服务器重启

创建有容器的服务器如果发生重启,此时挂载的硬盘会全部丢失。发生这种问题是致命的。容器挂载硬盘这块的实现关系如下:

卷 --> map到宿主机/dev/sdb --> mount /var/lib/zun/mnt/zun..uuid 目录 --> /var/lib/zun/mnt/zun..uuid:/data

服务器重启后卷还在,容器也在,但是中间两步丢失了,因此在服务器重启后再次重启容器时需要完成中间两步即可。这部分处理逻辑是源码中的,因此可以复用该代码进行适当调整实现服务器重启后挂载卷功能,映射关系保持不变。

2、容器启动失败

此处的启动失败特指之前创建好的可以正常运行的容器,因为某些原因状态变为,当再次启动该容器时由于外在原因(如服务未启动、kuryr-未启动)导致容器启动失败,源码中的处理逻辑是直接走的逻辑,在该部分代码中会执行,这里会将卷从容器卸载,并且如果卷设置的是卸载后自动删除,还会将卷直接删掉。这种bug是不可承受的。

当前思考的处理方式是在容器启动失败时,判断容器状态,如果状态是,并且当前正在执行的状态是,则只卸载卷,而不删除卷,再新建容器将这个卷挂到新容器上实现数据的保留。

参考链接:

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

api

zun

the api via wsgi

Pecan web 框架简介

pecan框架的使用

about

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了