设为首页 收藏本站
查看: 521|回复: 0

[经验分享] OpenStack入门 之 架构分析

[复制链接]

尚未签到

发表于 2018-5-31 06:28:14 | 显示全部楼层 |阅读模式
  学习目标:

  •   了解 OpenStack 各组件的逻辑关系;
  •   了解 OpenStack 的各组件的通信和部署关系;
  •   了解 OpenStack 的工作流程;
  接下来我会掌握:

  •   OpenStack 组件间的逻辑关系;
  •   OpenStack 的API;
  •   OpenStack 组件间的通信关系;
  •   OpenStack 中几种不同的存储;
  •   OpenStack 工作流程;
  •   OpenStack 的部署架构;
  OpenStack 各组件之间的关系有:逻辑关系,通信关系,部署关系…
1. OpenStack组件之间的逻辑关系
  openstack 是一个不断发展的系统,所以 OpenStack 的架构是演进的,举个例子:

  •   E 版本有5个组件

  Compute 是 Nova;Image 是 Glance,为 Nova 提供镜像存储服务;Object 是提供 Object 存储服务的 Swift;Dashboard 是我们平时说的 Horizon;Identity 是 Keystone;

  •   F版本有7各组件,核心组件:

  有这七个组件可以搭出一个相对完整的云计算环境,Heat、Sahala 是可选的;相对 E 版本,新增加的两个组件分别是 Block Storage Cinder 和 Network Neutron,这两个组件和 Glance,swift之间没有直接的联系,实际上是从 Compute Network 和 Compute Volume 发展出来的,Neutron
组件并没有直接的去替换 Compute Network,它是一个相对独立的,也是非常著名的 SDN 的一个项目,它为 Compute
提供网络连接,提供网络的资源管理这样一些服务,Block Storage(也就是 Cinder)为 Compute 提供块存储服务,替换了
Compute Volume
2. OpenStack的API
  OpenStack 的逻辑关系是要各个组件之间的信息传输来实现的,而组件之间的信息传输主要是通过OpenStack 之间相互调用 API 来实现的,作为一个操作系统,作为一个框架,它的 API 有着重要的意义。
  基于 HTTP 协议,RESTful Web API;
  什么是 REST?
  全称是:Representational State Transfer,表现状态传输。由 Fielding 博士(HTTP 协议的1.0 和 1.1 版本的主要设计者,Apache 服务器软件的作者之一,Apache 基金会的第一任主席)提出。REST 是通过操作资源的表现来操作资源的状态
  另外一种 Web 服务接口协议是 SOAP。
  两者的区别,RESTful Web API 的调用非常简单,但是我们平时编程的时候用 SOAP 可能是基于一些框架在去做,.Net,Java 的这些都已经很成熟了,我们感受不到底层机制的这种复杂性,而 REST 其实和 SOAP 比起来非常之简洁的,另外一方面,REST 描述的是一种风格一种架构一种原则,所以它并没有规定具体的实践方式或者说协议。
目前最常见的实现方式就是基于 HTTP 协议实现的 RESTful Web API,我们的 OpenStack 里面用的就是这种方式。REST
架构里面对资源的操作,包括:获取、创建、修改和删除,正好对应着 HTTP 协议提供的 GET、POST、PUT 和 DELETE 方法,所以用
HTTP 来实现 REST 是比较方便的。
  RESTful Web API 主要有以下三个要点

  •   资源地址与资源的 URI,比如:http://example.com/resources/
  •   传输资源的表现形式,指的是 Web 服务接受与返回的互联网媒体类型,比如:JSON,XML 等,其中 JSON 具有轻量级的特点,移动互联网的飞速发展轻量级的协议非常受欢迎,JSON 得到了广泛的应用
  •   对资源的操作,Web 服务在该资源上所支持的一系列请求方法,比如:POST,GET,PUT,DELETE
  下面以 OpenStack Swift 的接口作为一个例子来说明:
首先,用 curl 命令访问一个 http 服务
crul -i -X GET http://storage.clouddrive.com/v1/my_account?format=json\ -H
"X-Auth-User:jdoe" -H "X-Auth-Key:jdoepassword"  返回结果:

  它是一个 HTTP 响应的头部,有 Account 的细节信息,包括这个 Account 有多少个  
Container 有多少个 Object,占用了多少字节的存储空间等等
然后是 JOSN 格式的内容,列出了这个 Account 下面的 Container

  调用及调试 API 的几种方式

  •   第一种方式:curl 命令(linux 下发送 HTTP 请求并接受响应的命令行工具),这种方式其实用的比较少,比较麻烦
  •   第二种方式:比较常用的 OpenStack 命令行客户端,每一个 OpenStack 项目都有一个 Python 写的命令行客户端
  •   第三种方式:用 Firefox 或 Chrome 浏览器的 REST 的客户端(图形界面的,浏览器插件)
  •   第四种方式:用 OpenStack 的 SDK,可以不用手动写代码发送 HTTP 请求调用 REST 接口,还省去了一些管理诸如
    Token 等数据的工作,能够很方便地基于 OpenStack 做开发,那么 OpenStack 官方提供的是 Python 的
    SDK,当然还有第三方提供的 SDK 比如说支持 Java 的著名的 Jclouds,还有支持 Node.js、Ruby、.Net 等等
  OpenStack 还提供了另外一套 API 兼容亚马逊的 EC2,能够方便应用在两套系统之间做迁移。
3. OpenStack组件间的通信关系
  OpenStack 组件之间的通信分为四类:

  •   基于 HTTP 协议
  •   基于 AMQP 协议(基于消息队列协议)
  •   基于数据库连接(主要是 SQL 的通信)
  •   Native API(基于第三方的 API)
  有一张图需要了解一下:


  •   Compute Node 是实际运行虚拟机的节点
  •   Block Storage Node 主要是 Cinder 连接的存储后端(存储设备)
  •   Network Node 通常是具有路由等一些网关功能的节点(网络设备)
3-1. 基于HTTP协议进行通信
  通过各项目的 API 建立的通信关系,基本上都属于这一类,这些 API 都是 RESTful Web API,最常见的就是通过
Horizon 或者说命令行接口对各组件进行操作的时候产生的这种通信,然后就是各组件通过 Keystone
对用户身份进行校验,进行验证的时候使用这种通信,还有比如说 Nova Compute 在获取镜像的时候和 Glance 之间,对 Glance
API 的调用,还有比方说 Swift 数据的读写,也是通过这个 HTTP 协议的 RESTful Web API 来进行的。
3-2. 基于高级消息队列协议
  基于 AMQP 协议进行的通信,主要是每个项目内部各个组件之间的通信,比方说 Nova 的 Nova Compute 和 Scheduler 之间,然后 Cinder 的 Scheduler 和 Cinder Volume之间。
  需要说明的是,Cinder 是从 Nova Volume 演化出来的,所以 Cinder 和 Nova 之间也有通过 AMQP
协议的通信关系,由于 AMQP 协议进行通信也属于面向服务的架构,虽然大部分通过 AMQP
协议进行通信的组件属于同一个项目,但是并不要求它们安装在同一个节点上,给系统的横向扩展带来了很大的好处,可以对其中的各个组件分别按照他们负载的情
况进行横向扩展,因为他们不在一个节点上,分别用不同数量的节点去承载它们的这些服务。
  ( AMQP 是一种协议,OpenStack 没有规定它是用什么实现,我们经常使用的是 Private MQ,实际上用户也可以根据自身的情况选择其它的消息中间件。)
3-3. 基于SQL的通信
  通过数据库连
接实现通信,这些通信大多也属于各个项目内部,也不要求数据库和项目其它组件安装在同一个节点上,它也可以分开安装,还可以专门部署数据库服务器,把数据
库服务放到上面,之间通过基于 SQL 的这些连接来进行通信。OpenStack 没有规定必须使用哪种数据库,虽然通常用的是 MySQL
3-4. 通过Native API实现通信
  出现在 OpenStack 各组件和第三方的软硬件之间,比如说,Cinder 和存储后端之间的通信,Neutron 的 agent
或者说插件和网络设备之间的通信,这些通信都需要调用第三方的设备或第三方软件的 API,我们称为它们为 Native
API,那么这个就是我们前面说的基于第三方 API 的通信。
4. OpenStack几种不同的存储
  OpenStack的存储服务分为三种:GlanceSwiftCinder

  •   Glance(镜像存储)是一个镜像存储管理服务,本身不具备存储的功能;
  •   Cinder (块存储)提供块存储的接口;本身也不提供数据的存储,后面也需要接一个存储的后端,像
    EMC 的散设备,华为的存储设备,NetApp 的存储设备可以做它的后端。还有一个比较火的开源分布式存储叫 Ceph,Ceph
    也提供块存储服务,也可以用来作为 Cinder 的后端。Cinder 的作用就是为 OpenStack 提供块存储的接口,有个很重要的功能叫卷管理功能
    虚拟机并不直接去使用存储设备(并不直接去使用后端的存储系统),使用的是虚拟机上的块设备(卷 Volume),实际上 Cinder
    就是创建和管理这些 Volume 并且把它挂载到虚拟机上。Cinder 是从 Nova Volume
    里面独立出来的,独立出来之后很受各种存储厂商的欢迎,可以通过写 Cinder Driver 的形式把自己的存储设备纳入到 OpenStack
    的生态圈里面去。
  •   Swift (对象存储)提供的是对象存储服务,同样的具有像亚马逊 IWSS3
    的特点,提供通过RESTful API 的方式去访问数据,这样做是为了解决两个问题:第一个,我们可以直接去访问一个存储,而不需要在通过自己开发的Web
    服务器去做一次数据的转发,否则对服务器的负载来说是一种压力。第二个,在我们的大数据时代,当数据量特别大的时候,如果我们用文件系统就会出现问题:文
    件的数量激增以后,存储的性能会急剧下降,而对象存储实际上则是解决这个问题的,对象存储抛弃了那种目录树的结构,用一种扁平化的结构去管理数据。
    Swift 实际上只有三层结构,即 Account、Container、Object。Object
    就是最终的那个数据了,就是文件,前面有两级管理,一级是 Container 容器,它把 Object 放到容器里面,然后再上面一级是
    Account,是和账户去关联的,Container 相当于是把这些 Object 做了分类,用 Account 去跟账户关联起来。
  三种存储的概念文件存储块存储对象存储

  •   有 POSIX 接口或者 POSIX 兼容的接口,就可认为它是一个文件系统,比较典型的分布式文件系统有像 Glance 的 FS,Hadoop 里的 HDFS
  •   电脑上的一个盘格式化之后是一个文件系统,那么在格式化之前是一个块设备,也就是块存储,实际上我们在数据中心里面,像 EMC 的很多设备,像华为的一些叫作 SAN 的设备,像 NetApp 的一些设备,如果是散存储一般来说就是提供块存储的;
  •   对象存储的典型代表是亚马逊的 AWS S3,它的接口不是 POSIX,也不是像一块硬盘那样作为一个块存储的接口,是通过 RESTful
    Web API 去访问的,对于应用开发者来说优势在于可以很方便的去访问存储里面存的数据,对象存储里存的数据通常叫做 Object,实际上它就是
    File,但是对象存储里面为了和文件系统做一个区别,便被叫作对象 Object
5. OpenStack工作流程
  这里以创建一个虚拟机为例来了解 OpenStack 是如何工作的,下面的图是 OpenStack 创建虚拟机整个工作过程:

  下面进行简要的文字说明:
  总共有 28 个步骤,主要是和 Nova 相关的,实际上在 Keystone Glance Neutron Cinder Swift
等组件内部还有很多小步骤,加起来恐怕有50多个步骤。现在主要看这28个基本步骤,这里的大部分信息发表在来自 ilearnstack
网站上的一篇文章,在 Dashboard 上创建虚拟机的过程.

  •   第 1 步,首先,Horizon 或者是命令行工具向 keystone 发起了 REST 调用,并且把用户名和密码发给 Keystone;
  •   第 2 步,Keystone 对接收到的用户名及其密码进行验证,并生成 Token,这个 Token 在后面为其他组件发送 REST 调用时使用;
  •   第 3 步,Horizon 或者命令行客户端把启动虚拟机操作或者在命令行上敲的 nova boot 命令,包括上一步说的 Token 转化成 RESTful Web API 发送给 Nova-API;
  •   第 4 步,Nova-API 收到请求之后向Keystone 验证客户端发来的 Token 的合法性,这就是说 Nova 和 Keystone 之间有一个交互了;
  •   第 5 步,Keystone 验证完 Token 之后,将用户的角色和权限返回给 Nova,也是返回到 Nova-API;
  •   第 6 步,是 Nova-API与 Nova Database 之间有一个交互的过程;
  •   第 7 步,是 Nova Database 为新的虚拟机实例创建一条记录,并且将结果返回给 Nova-API。
  •   第 8 步,Nova-API 通过 AMQP 向 Nova-Scheduler 发送一个同步远程调用请求,这里的同步远程调用请求实际上是AMQP 协议里的叫作 rpc.call
    request,这地方是同步调用,同步调用的意思就是,它发送完请求以后就一直在等待,一直等待到这个队列(消息队列)里面给它返回来结果,所以就是在
    等待获得新的虚拟机实例的条目和 Host ID,Host ID 是虚拟机将来要启动的寄主机的 ID;
  •   第 9 步,Nova-Scheduler 从消息队列里面取出请求,因为中间有 AMQP 组件在这,所以 Nova 的其他各个组件之间的交互基本上都是通过 AMQP 来做的,实际上 AMQP 的实现用的是 RabbitMQ;
  •   第 10 步,是 Nova-Scheduler 与 Nova Database 进行交互并且挑选出一台适合的宿主机来启动这个虚拟机,(挑选的过程很复杂);
  •   第 11 步,是 Nova-Scheduler 通过 AMQP 返回给前面的 Nova-API 的调用,并且将宿主机的 ID 发送给它;
  •   第 12 步,是 Nova-Scheduler 通过消息队列,向 Nova-Compute
    发出一个在上述宿主机上启动虚拟机的异步调用请求,这里是异步调用请求(Nova-Schduler
    把请求发送到消息队列里面以后它就返回了,就不用再等待这个请求给它返回结果,在 AMQP 里面是 rpc-cast 这个请求);
  •   第 13 步,是 Nova-Compute 从队列里面取该请求;
  •   第 14 步,是 Nova-Compute 通过消息队列向 Nova-Conducter 发送一个 rpc.call 同步调用请求获取虚拟机的信息(包括宿主机的 ID,虚拟机配置信息有内存大小、CPU 配置、硬盘大小等等);
  •   第 15 步,Nova-Conducter 从队列里取出上述请求;
  •   第 16 步,Nova-Conducter 与数据库进行交互;
  •   第 17 步,Nova-Conducter 返回前面 Nova-Compute 请求的这些信息;
  •   第 18 步,Nova-Compute 从消息队列里面取出 Nova-Conducter 返回的信息,也就是同步调用到这里就结束了;
  •   第 19 步,是 Nova-Compute 向 Glance-API 发出带有 Token 的 REST 请求,去请求这个镜像数据;
  •   第 20 步,是 Glance-API 向 Keystone 去验证这个 Token 的合法性,在前面有类似的步骤(Nova 去验证
    Token 的合法性,其实在 19 步和 20 步两步里面还有很多细节比如说,如果是 Swift 来存储 Glance 的镜像,中间还有
    Swift 和 Glance 之间的交互,Glance 内部也有一些交互);
  •   第 21 步,是 Nova-Compute 获取镜像的元数据,前面几步相当于把镜像的元数据给获取了,知道了镜像是从哪里来的长什么样的。后面的 22 到 24 步这几步是为虚拟机去准备网络,还是以 Nova-Compute 为中心的;
  •   第 22 步,是 Nova-Compute 向 Neutron API 发送带有 Token 的 REST 请求进行网络配置,并获得分配给待创建的这个虚拟机的 IP 地址,这中间其实 Neutron 做了一些工作,也有很多细节;
  •   第 23 步,Neutron Server 向 Keystone 去验证 Token 的合法性;
  •   第 24 步,Nova-Compute 就获得了网络的配置信息,后面这几步是给虚拟机去准备虚拟机的硬盘,也就是前面提到过的卷存储或块存储;
  •   第 25 步,是 Nova-Compute 调用 Cinder-API 的 RESTful 接口给虚拟机挂载卷,也就是块设备或者是虚拟硬盘;
  •   第 26 步,跟上面的 Glance 和 Neutron 类似,Cinder-API 也要向 Keystone 去验证,这个Token 的合法性;
  •   第 27 步,Nova-Compute 就会得到这个虚拟机的块存储的信息;经过 27 个步骤,这样创建一个虚拟机所需要的各种信息和条件才正式地准备完毕;
  •   第 28 步,Nova-Compute 去调用 Hypervisor 或者 Libvirt 的接口创建虚拟机,当然,在 Libvirt 或者说是 Hypervisor 去创建虚拟机的过程中,内部也是一个非常复杂的过程;
  虚拟机创建的四个阶段

  •   scheduling
  •   networking
  •   block_ device_mapping
  •   spawing
  几种通信关系的体现

  •   各个组件 API 之间的调用,这就属于 HTTP 通信;
  •   Nova 和 Neutron 内部各个组件的通信属于通过 AMQP 协议的通信;
  •   中间频繁的读写数据库的操作属于数据库连接进行通信的;
  •   Nova 与 Hypervisor 或者说 Libvirt 交互属于通过 Native API
    即第三方接口去进行通信的,还有一个就是在给虚拟机准备 Volume 的过程中 Cinder 还需要和存储设备进行交互,这中间也需要用到
    Native API 或者是第三方接口;
6. OpenStack的部署架构
  前面已经从逻辑关系、通信关系分析了OpenStack 各组件之间的关系,并且也介绍了 OpenStack 的 API 和存储。
  前面谈到的各种架构基本上都是属于软件上的逻辑架构,但是 OpenStack
是个分布式的系统,就得解决从逻辑架构到物理架构的映射的问题,也就是 OpenStack
的各个项目、组件按什么方式安装到实际的服务器节点上去,实际的存储设备上,如何通过把它们通过网络连接起来,这就是 OpenStack 的部署架构。
  OpenStack的部署分为:

  •   单节点部署,通常是用于学习或者是开发
  •   多节点部署(集群部署)
  OpenStack 的部署架构不是一成不变的,而是根据实际的需求设计不同的实施方案。
  在实际生产过程中,我们首先要对计算、网络、存储所需要的资源进行规划,虽然说我们现在用的云计算技术,它比传统的 IT 架构在资源规划方面的困难和工作量要小一些,但是还是需要有一个规划,这里学习了解一下基本的和复杂的两种集群部署架构。
6-1. 简单部署架构
  是一个最基本的生产环境所需要的 OpenStack 的部署情况。
  根据实际需要设计不同的实施方案
  下面解释一下 “三种网络和四种节点”
  (1)绿色的管理网络 + 蓝色的存储网络 + 黄色的服务网络


  •   管理网络 是 OpenStack 的管理节点或者说是它的管理服务对其它的节点去进行管理的这样一个网络,他们之间有 “不同组件之间的 API 调用,虚拟机之间的迁移” 等等;
  •   存储网络 是计算节点访问存储服务的网络,包括向存储设备里面读写数据的流量基本上都需要从存储网络走,还有另外一种是服务网络;
  •   服务网络 是由 OpenStack
    去管理的虚拟机对外提供服务的网络,服务器上通常都是一台服务器上带好几块网卡,好几块网口,我们可以给各个网络做隔离。隔离的好处是,它们的流量不会交
    叉,比方说我们在读写存储设备的时候,可能在存储网络上的流量特别大,但是它不会影响到我们这些节点对外提供服务,同样,在我们做虚拟机迁移的时候可能在
    管理网络上它的数据流量会非常大,但是它同样不会影响到我们这些计算节点对存储设备的读写性能。
  (2)四种节点:

  •   控制节点(OpenStack 的管理节点,OpenStack 的大部分服务都是运行在控制节点上的,比如说 Keystone 的认证服务,虚拟机镜像管理服务 Glance 等等)
  •   计算节点(计算节点指的是实际运行虚拟机的节点)
  •   存储节点(提供对象存储服务,提供对象存储的 Swift 的节点或者是 Swift 集群的 Proxy 节点,也可以是其它服务的存储后端)
  •   网络节点(实现网关和路由的功能
  有些服务可以直接部署在 Controller 节点上或者说是直接部署在控制节点上,但是特别需要说明的一点是: Nova 和 Neutron这两个组件必须采用分布式部署。说一下 Nova:Nova-Compute 是控制虚拟机的,是控制和管理虚拟机的,所以必须部署在计算节点上,而
Nova 的其它几个服务则应该部署在控制节点上,特别需要强调一点,Nova-Compute 和 Nova-Conducter
一定不能部署在同一个节点上,把这两个分开就是为了解耦。
说一下 Neutron:Neutron 的一些插件或 Agent 需要部署在网络节点和计算节点上,而其他的部分,比如说 Neutron Server 可以部署在控制节点上
6-2. 复杂部署架构
  需要掌握三个要点:

  •   规模较大的情况下,把各种管理服务部署到不同的服务器上。把这些 服务拆开部署 到不同的节点上,甚至要把同 一个服务的不同组件也拆开部署
    比如说可以把 Nova 的数据库给独立拧出来部署成一个 MySQL 数据库集群,还有 Cinder 里面的 Scheduler 和 Volume可以部署到不同的节点上,实际上因为 Swift 项目具有一定的独立性,所以 Swift
    本身就有跨地域部署的生产环境,规模非常之大,跨地域部署,所以它的服务的可用性极高,自然有这种栽培的特性,可以提供 极高可用性和数据持久性的对象存储服务。于是乎,很容易的对 OpenStack 的这些服务进行横向扩展,对 OpenStack 整个系统做横向扩展,这些让
    OpenStack 具有比较高的负载能力,可以达到一个比较大的规模。所有的这些都得益于 OpenStack 设计的时候采用了 SO
    吻合的面向服务的架构,就是 SOA 架构,具体到每个组件如何进行分布式的部署,如何进行横向扩展。
  •   出于高可用的考虑,生产环境中我们会把 OpenStack 的同一个服务部署到不同的节点上,形成双机热备或者多机热备的高可用集群。(或者用负载均衡集群)。
  •   在复杂的数据中心环境中,还有很多第三方服务,比方说 LDAP 服务、DNS 服务等等,考虑如何与第三方服务进行对接和集成。比如说,我们可能需要使用 OPEN LDAP 服务器来作为 Keystone 的认证和健全的后端,这些一般是在管理网络中去完成的。
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-483203-1-1.html 上篇帖子: OpenStack部署运维实战 下篇帖子: OpenStack入门 之 初步认识
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表