文章目录

  • 概述
  • OpenStack
    • 虚拟化
      • kvm
        • 网络虚拟化
    • 基本技术
      • Memcached
      • Etcd
      • 消息队列
        • 概念
        • 交换机类型
        • 缺点
        • 重复投递问题
        • 顺序投递问题
      • restful api
    • Horizon
    • Nova
      • nova-api
      • nova-scheduler
        • 过滤器类型
      • nova-compute
      • 基本操作
    • Keystone
    • Glance
    • Neutron
      • 一些虚拟的网络设备
      • 架构
        • Neutron Server
        • ML2 Plugin
      • Linux Bridge实现
        • local network
        • flat network
        • vlan network
        • vxlan
      • DHCP服务
      • NameSpace
      • Routing
        • 底层实现
        • router连接外网
        • floating IP
      • Fwaas
      • OpenvSwitch
        • local network
        • flat network
        • VLAN 网络
        • Roting
        • VxLAN
    • Cinder
    • Swift
    • Zun
    • Kuryr-libnetwork
  • SDN
    • OpenFlow
      • OF 1.0
      • OF 1.3
  • Django
    • 概述
      • MVC和MTV
    • 和Flask Tornado的区别
    • Django请求生命周期
    • 常见的web应用程序
      • 框架组件
      • RestFramework
      • 缓存机制
      • WSGI,uwsgi,uWSGI
      • 请求的生命周期
      • 中间件的作用和应用场景
      • 默认的中间件
      • django中的csrf实现机制
        • 基于django的ajax发送请求给后端如何携带token
        • ajax请求的csrf解决方法
      • 为什么不使用django的runserver部署(runserver和uWSGI的区别)
  • Kubernetes
    • Pod
      • Pod控制器
  • Docker
    • NameSpace
    • Cgroups
    • 驱动
      • 启动一个容器的步骤
    • 基础
      • 什么是Docker
      • Docker与虚拟机的不同
      • Docker和普通进程的区别
    • 镜像
      • 什么是Docker镜像和Docker容器
      • Docker镜像分层
      • bootfs
      • 容器分层、写时复制(copy-on-write)
      • DockerFile常见指令
      • build
      • Dockerfile的COPY和ADD
      • Docker容器的状态
    • 网络
      • Docker的网络类型
      • docker常用命令
  • 面经
    • OpenStack
        • 一些命令行
        • OpenStack中计算节点上虚拟机默认保存路径在哪?
        • OpenStack中Glance镜像的默认保存路径在哪?
        • OpenStack中计算节点的集成桥(br-int)的作用是什么?
        • OpenStack中计算节点的隧道桥(br-tun)的作用是什么?
        • OpenStack中外部OVS桥(br-ex)的作用是什么?
        • OpenStack和Docker区别
        • OpenStack和kvm的区别
        • kvm和xen区别
      • Neutron
        • vlan和vxlan的区别
    • Docker
      • 命名空间

概述

云计算是一种采用按量付费的模式,基于虚拟化技术,将相应计算资源(如网络、存储等)池化后,提供便捷的、高可用的、高扩展性的、按需的服务(如计算、存储、应用程序和其他 IT 资源)。
云计算基本特征:

  • 自主服务:可按需的获取云端的相应资源(主要指公有云);
  • 网路访问:可随时随地使用任何联网终端设备接入云端从而使用相应资源。
  • 资源池化:
  • 快速弹性:可方便、快捷地按需获取和释放计算资源。
  • 按量计费:

常见的部署模式

  • 公有云
  • 私有云
  • 社区云
  • 混合云

三种服务模式

  • IaaS:云服务商将IT系统的基础设施(如计算资源、存储资源、网络资源)池化后作为服务进行售卖;
  • PaaS:云服务商将IT系统的平台软件层(数据库、OS、中间件、运行库)作为服务进行售卖;
  • SaaS:云服务商将IT系统的应用软件层作为服务进行售卖。

云计算和虚拟化
云计算:IT能力服务化,按需使用,按量计费,多租户隔离,是一个系统的轻量级管理控制面。
虚拟化:环境隔离,资源复用,降低隔离损耗,提升运行性能,提供高级虚拟化特性。
虚拟化是实现云计算的技术支撑之一,但并非云计算的核心关注点。

IT架构的三个发展阶段

  1. 物理机架构:如果需要部署一个系统,需要使用多个物理机组成一个集群,所有节点都使用物理机,资源利用率都非常低,可能就20%
  2. 虚拟化架构:在物理机上运行多个虚拟机,将系统的各个服务部署在虚拟机上,可以极大的提高物理机资源的利用效率,降低成本
  3. 云计算架构:虚拟化提高了单机的资源使用率,如何对环境中的所有虚拟机进行统一管理,就要使用到云计算了。云计算将计算、存储和网络等资源虚拟化为资源池,当需要创建虚拟机的时候,平台只需要按照规格需求自动分配相应的资源创建虚拟机,完成网络IP的配置等功能。用户不需要关心虚拟机是在哪里运行的、存储空间在哪里、IP地址怎么分配的,只需要关心如何部署具体的业务就好了。

OpenStack

一个开源云操作系统内核,用于构建云平台,主要实现以下五个主要特点:

  • 资源抽象:OpenStack将各类硬件资源,通过虚拟化与软件定义的形式,抽象成虚拟的资源池;
  • 资源调度:OpenStack根据管理员/用户的需求,将资源池中的资源分配给不同的用户,承载不同的应用;
  • 应用生命周期管理:OpenStack可以提供初步的应用部署/撤销、自动规模调整等功能;
  • 系统运维:OpenStack可以提供一定的系统监控能力;
  • 人机交互:OpenStack提供人机接口,外界可通过API、CLI或图形界面的方式与OpenStack进行交互。

组件间交互是消息队列,组件内部交互是restful api。

虚拟化

Ⅰ型虚拟化是:hypervisor直接安装在物理机,就像是vmware的EXSi,底下是基于硬件层的
Ⅱ型虚拟化是:在物理机的正常操作系统,hypersivor作为一个程序模块运行管理虚拟机,如kvm,virtual box,vmware woerkstation。对硬件虚拟化特别优化,性能好,灵活性高。

kvm

基于linux内核实现的。有一个kvm.so,只需好管理虚拟cpu和内存等;IO外设交给linux内核和Qemu。
Libvirt是一个kvm管理工具,除了能管理kvm还可以xen,virtualBox,openstack底层也是libvirt。
libvirt:

  • libvirtd:守护进程:接受处理API请求
  • API库:提供API调用,然后开发一些高级工具,比如virt-manager,可视化管理工具
  • virsh:命令行工具

kvm虚拟机是需要CPU硬件支持的,一个kvm虚拟机实质上就是一个qemu-kvm进程,一个vcpu对应一个进程里的一个线程。一个cpu可以调度进程里的多个线程,也就是cvpu数量实际上可以超过cpu,叫CPU超配。可以充分利用宿主机的资源。
通过内存虚拟化共享物理系统内存,动态分配给虚拟机。实现虚拟内存->物理内存->机器内存的映射。虚拟机系统只能实现虚拟内存到物理内存,最后一步无法真正访问机器内存,因此需要kvm进行一个映射。
存储虚拟化通过存储池和卷实现的,卷在虚拟机里就是一块硬盘。

网络虚拟化

Linex Bridge是Linux上的TCP/IP二层协议交换设备,二层交换机,多个网络设备连接到同一个bridge时,有数据包传来beidge会转发给其他设备。
br-ctl show查看当前网桥配置。
比如在eth0网卡上配置一个网桥br0,然后虚拟机的网卡可以选择br0,启动以后,br0底下会挂载一个inet0的设备,这就是虚拟机的虚拟网卡,但是在虚拟机内部来说虚拟网卡时eth0,inet0是在宿主机的时候标识的名称。

virbr0是kvm默认创建的一个Bridge,作用是给连接的虚拟网卡提供NAT访问外网的功能,默认192.168.122.1,如果网络选择默认,就会挂载在这个上面。
virbr和br的区别:

  • virbr会NAT,发出去的数据包的源IP会被替换为宿主机的IP,只能发不能收
  • br直接使用自己的IP通信,可收可发,不用NAT

VLAN
LAN是本地局域网,通产使用hub或者switch连接其中的主机。一个LAN是一个广播域,所有成员都能收到其他成员的广播包。
VLAN是Virtual LAN,一个带有VLAN功能的switch可以将端口划分出很多个LAN。可以将一个交换机划分为多个交换机,在二层进行隔离,隔离到不同VLAN中。
一般交换机端口有两种配置方式

  • Access:端口被打上VLAN标签,表明该端口是属于哪个VLAN的,不同VLAN通过VLANID区分,范围是1~4096.Access口是直接和网卡相连接的。Access口只能属于一个VLAN,网卡流出的数据包经过这个端口后直接打上一个VLAN标签
  • Trunk:一个端口可以同时传输转发多个VLAN标签的数据包,在传输的过程中数据包始终携带VLAN ID

总结:

  1. 物理交换机有多个VLAN,每个VLAN可以有多个端口。有交换和隔离两个二层功能,同一VLAN相互交换转发,不同VLAN相互隔离
  2. Linux的VLAN只能实现隔离,不能实现交换,因为比如一个VLAN母设备,比如物理机的网卡eth0,他不允许连接两个相同的ID的VLAN子设备,不存在同意VLAN交换转发的情况
  3. LinuxBridge,将同一VLAN的子设备挂载到一个Bridge上,就可以实现交换转发了。LinuxBridge+VLAN可以模拟现实世界的二层交换机

基本技术

Memcached

是一个高性能分布式内存对象缓存系统,在OpenStack中用于缓存认证系统的令牌,减少高并发下对于数据库的访问压力,提高访问速度。
将需要存取的数据或对象缓存在内存中,内存中缓存数据可以通过API进行操作,数据经过Hash操作以后存在一个Hash表中,是K-V形式存储。
memcached没有访问控制和安全管理,因此需要使用防火墙等安全功能进行相应防护
使用最近最少使用算法LRU对最近不活跃的数据进行清理,从而得到新的内存空间。
对于大规模数据缓存有着较为明显的优势而且通过开放API多种语言可以直接操作memcached。OpenStack的keystone就是用memcached缓存租户的身份等信息、从而在租户登录验证的时候无需重复访问数据库即可查询得到相应数据。Horizon和Swift也用到了这个来进行数据的缓存以提高客户端的访问请求速率。
操作的过程

  • 检查数据是否存在memcached,如果有直接返回
  • 如果数据不存在memcached,去数据库查询,同时将数据库缓存到memcached
  • 每次更新数据库的时候同步更新memcached的数据保证一致性
  • 如果分配的内存空间满了,使用LRU算法对失效的数据进行处理

功能特点

  • 协议简单:基于文本行进行操作
  • 基于libevent事件处理:将epoll等事件处理封装成一个接口,保证服务器端的连接数,使用这个库进行异步事件处理
  • 内置的内存管理方式:有自己特有的一套内存管理方式,很高效;但是不考虑容灾,重启数据丢失
  • 节点之间相互独立:各个服务器之间互不通信,独立存取数据不共享,通过客户端实现其分布式,支持海量缓存和大规模应用

缺点

  • 单点故障:因为每个节点是独立存取数据的,服务器之间没有通信,即不会进行数据的同步和备份,如果一个节点down了,节点的数据全部丢失且无法恢复
  • 存储空间限制:会受到寻址空间大小限制,32位系统可缓存2G,64位可缓存无限,只要物理内存足够大
  • 存储单元限制:K-V的key最大250字节,value最大1MB
  • 数据碎片:内存存储单元按照Chunk分配的,会造成内存碎片,因为不可能所有存储的value大小都是一个Chunk大小
  • 不安全

Etcd

是一个go语言开发的开源的高可用的K-V存储系统,用于配置共享和服务的注册和发现。
集群部署一般使用奇数个服务器配置,因为Raft决策时候需要多节点投票。
有几个特点:

  • 高可用:避免因为单点故障或者网络故障造成服务down
  • 一致性:每次读取都会返回跨多个主机的最新写入
  • 完全复制:每个节点都可以使用完整数据归档
  • 安全:提供一个带有可选的客户端证书身份验证的自动化TLS
  • 快速:每秒一万次写入的基准速度
  • 可靠:使用Raft算法实现强一致、高可用的服务存储目录

应用场景

  • 服务发现:同一个分布式集群中找到是否有进程在监听端口
  • 配置中心:讲一些数据放在Etcd集中管理,比如在启动的时候主动从etcd获取配置信息,并且设置一个Watcher,有更新的时候etcd会主动通知订阅者获取最新配置信息
  • 分布式锁:使用Raft实现数据的强一致性,某次操作存储必须是全局一致性的。有两种:保持独占(始终只有一个用户可以获得,实现了一套分布式原子操作CAS的API)、控制时序(所有想获得锁的用户都会被安排执行,获得锁的顺序也是全局唯一,决定了执行的顺序)

相比荣誉zookeeper和doozer,有如下特点:

  • 简单:基于HTTP+JSON,提供API便于调用
  • 安全:使用SSL客户认证机制
  • 快速:实例每秒支持一万次写操作
  • 可信:Raft算法实现分部署

消息队列

rabbitmq属于AMQP(高级消息队列协议)的一种实现,应用层的一个开放标准。
特点:

  • 较高的灵活性
  • 高可扩展性,多个rabbitmq可以组成集群
  • 支持常见的编程语言
  • 提供可视化界面便于管理
  • 支持多种协议,不只是AMQP
  • 提供了多种插件

AMQP的三大组件:

  • Exchange交换机:把消息路由到队列
  • Queue队列:存储消息等待消费,多个消费者可以订阅同一个队列
  • Binding绑定:将交换机和队列进行绑定,告知交换机应该投递到哪个队列

优点:

  • 异步:如果阻塞式的话,A给B发请求,B如果处理需要很长时间,那么A也需要等待很久,显然是不合理的,还会资源浪费,可以通过消息队列先发送一个请求命令给B ,然后A直接进行下一步,当B有结果时会通知A
  • 解耦:如果每一个组件之间通过直接通信的话,都需要维护一套比如接口代码,会有很多重复冗余的代码,,而且还需要考虑比如对方是否接到消息,消息是否丢失之类的。使用消息队列就不需要考虑这些了,把消息投递进入就好了,剩下的消息队列做
  • 削锋:如果短时间内有大量请求直接到服务器,可能会造成拥塞甚至崩溃。放入消息队列里,消费者可以依次取出消息进行消费,相当于一个缓冲,虽然会慢一点,但是会很高效稳定
概念

有几个概念关键词

  • Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列
  • Queue:消息队列载体,每个消息都会被投入到一个或多个队列,多个消费者可以订阅同一个队列
  • Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来
  • Routing Key:路由关键字, exchange根据这个关键字进行消息投递
  • producer:消息生产者,就是投递消息的程序
  • consumer:消息消费者,就是接受消息的程序
  • channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
交换机类型
  • Direct Exchange(直连交换机):交换机和队列一对一匹配
  • Fanout Exchange(扇型交换机):消息会被发送至所有与该交换机绑定的队列,不会按照指定的路由键进行投递,类似广播
  • Topic Exchange(主题交换机):交换机会将消息的路由键和绑定到自身的队列进行匹配,将消息投递到匹配上的队列中去。比如如果写成这样log.#,那么可以匹配log.开头的routingkey
  • Headers Exchange:类似主题交换机,但是是使用消息头部的属性进行分发,而不是routingkey
缺点

系统可用性降低,rabbitmq挂了以后系统就崩了。
如何解决消息重复投递或者丢失的问题
如何解决消息按顺序发送的问题

重复投递问题

给每一个消息设置一个类似唯一标识的一个ID,接收者去除消息以后先在数据库中对比一下是否存在,如果不存在,就正常消费。

顺序投递问题

还是使用一个ID,一般这个会出现在比如说订单的场景,生成订单,制作订单,订单号都是一样的,把这些消息投递到一个订单队列里面,取出的时候就是顺序的了。

restful api

restful api是一种软件架构设计的风格,不是标准,提供了一组设计的原则和约束条件,是一套编写接口的协议,协议规定如何编写,返回值和状态码等,主要用于客户端和服务器端的交互。
最显著的特点就是一个url可以通过不同的HTTP方法实现不同的功能,而no rest则需要使用多个url分别实现多个功能。

  • 还可以使用https
  • 不同版本可以有不同接口
  • 可以根据不同http方法执行不同操作
  • 返回格式应该统一为json,还有状态码

django中的erest框架叫django rest framework
能够生成符合restful规范的api

  • 在开发restful api的时候,虽然具体业务流程不一样,但是基本增删改查操作差不多,这部分代码可以简写复用
  • 序列化和反序列化的时候代码流程也差不多,,也可以简写

传统的URL需要在链接里加上操作的动作等;
而restful api则用URI统一资源标识符唯一标识服务器的一个资源,并且使用HTTP方法对其进行操作

Horizon

提供一个Web前端控制台,从而实现通过web管理云平台,建云主机,分配网络,配安全组等。
Region(区域):地理上的概念可以理解为是两个不同区域的数据中心,是完全隔离的,但是可以共享同一套keystone和horizon了用户可以选择距离自己更近的域使用服务

Nova

端口

  • nova-api:通过接口对外提供服务,接受请求处理操作调度资源等。接收传来的HTTP请求校验参数,然后调用其他子服务实现请求操作,之后处理结果返回
  • nova-compute:Nova的核心服务,,负责管理虚拟机的整个生命周期如创建相应销毁等,通过调用Hypervisor API
  • nova-scheduler:虚拟机调度服务,决定虚拟机应该创建在哪个节点。通过过滤器选择合适的计算节点,然后计算节点权重,选择最优的节点(默认权值是计算空闲内存量,内存越大权值越大)
  • nova-conductor:负责数据库的访问控制,避免compute直接访问数据库。避免计算节点出现故障导致数据库出现问题,可以扩展配置多个conductor更好的应对更多计算节点对于数据库的访问

创建一个虚拟机大概的流程

  1. 发送http请求,nova-api接受请求后,进行参数和身份校验
  2. 发送消息给nova-schedule调度计算节点
  3. 发送消息给nova-compute创建实例
  4. 在整个过程中通过nova-conductor与数据库进行访问交互

这里的消息发送全部都是通过消息队列实现的,进行异步调用,不会阻塞服务;解耦各个子模块功能;提高可扩展性;提高性能,可以处理多个请求,提高吞吐量
使用API的好处:

  • 对外提供了一个统一的调用方法,隐藏实现细节
  • 提供了restful风格的服务,便于和第三方集成
  • 通过运行多个api进程实现高可用

OpenStack的开放性,一个重要方面就是基于Driver的框架。比如说nov-acompute中为hypervisor定义了统一的接口,支持多种Hypervisor,如Xen、VirtualBox、Hyper-V、Docker等

nova-api

对外暴露若干个Restful API,用户可以发送请求到指定Endpoint

  • 收到HTTP请求后,会先进行参数校验
  • 调用nova其他子服务开始执行相应操作
  • 再返回执行的结果

和虚拟机生命周期的指定请求nova-api都可以收到并处理。

nova-scheduler

在创建虚拟机的时候需要指定一个flavor也就是规格,其中规定了vcpu、ram、disk等参数,随后将falvor信息传入到noca-scheduler中,会根据flavor选择一个合适的节点创建虚拟机。
Filer scheduler是默认调度器,过程分为两步,第一是通过过滤器计算出满足条件的计算节点,通过权重计算,在权重最大(最优)的节点创建虚拟机。
同样可以使用第三方的scheduler,只需要配置一个scheduler_driver即可,再次体现出OpenStack开放性。

过滤器类型

nova可以配置默认使用全部过滤器acheduler_availiable_filters,但是真正使用到的是这个参数scheduler_default_filters

  • RetryFilter:如果AB节点通过了第一步过滤,然后A权重最大,但是在A创建失败了,那么下一次调度的时候,就会在第一步将A刷掉,避免再次失败
  • AvailabilityZoneFilter:为了提高容灾和隔离,将计算节点可以划分到不同的AvailablittZone中,每个AZ都可能有着各自独立的电力系统、独立的网络和机柜等,为了应对故障的发生,一个Region可以有多个AZ,默认是nova。这里会判断将不属于指定AZ的计算节点删除
  • RamFilter:将不满足flavor中内存规格的计算节点除去。OpenStack允许overcommit,可以在nova里面改这个参数ram_allocation_ratio=1.5,如果某个计算节点有10G内存,OpenStack会认为其有15G内存
  • DiskFilter:同上,同样允许overcommit,默认参数值为1
  • CPUFilter:同上,允许overcommit,默认参数值为16.也就是如果一个计算节点有8vcpu,那么OpenStack会默认其有128vcpu。默认过滤器是不包含这个过滤器,需要手动添加
  • ComputeFilter:保证工作正常的计算节点才会被筛选出来,是必须的一个过滤器
  • ComputeCapabilitiesFilter:根据计算节点的特性筛选,如计算节点有x86_64和ARM架构,如果指定ARM架构,就需要这个过滤器,这个需要在flavor的Metadata中指定
  • ImagePropertiesFilter:image同样具有metadata,根据这个元数据筛选出匹配的计算节点。比如可以选择hypervisor_type=kvm,那么使用这个镜像过滤就只能筛选出kvm的节点
  • ServerGroupAntiAffinityFilter:尽可能将实例分散到不同的节点上。需要创建一份server group,把实例添加进来才有用
  • ServerGroupAffinityFilter:尽可能创建实例在一个计算节点上。

nova-compute

compute和hypervisor一起共同管理虚拟机生命周期。
定义了很多统一的接口,hypervisor实现这些接口就可以在openstack里面了。
一个计算节点只能指定一种虚拟类型。
主要有两种功能:

  • 报告计算节点状态
  • 实现虚拟机生命周期管理

需要实时报告计算节点可用内存、vcpu等数量,scheduler才可有进行过滤调度的依据,通过hypervisor api获取虚虚拟机的资源信息。
管理生命周期包括如下:

  • 为实例准备资源
  • 创建实例镜像文件
  • 创建实例的.xml定定义文件
  • 创建网络并启动实例

在下载镜像的时候,如果存在就直接使用,会很快,不存在会先下载。比如是qcow2格式的镜像,由qemu-img转为raw格式然后进行backing file,这个不能是qcow2格式。

基本操作

  • 软硬重启其实就分别是reboot重启和关机再开机
  • Pause/Resume:可以暂停虚拟机,并将状态保存到内存中,需要的时候再resume读取内存恢复虚拟机
  • Suspend和Pause:前者可以暂停虚拟机并将状态保存在磁盘上,后者保存到内存中,相对较快;前者状态会变成shutdown,后者是paused;前者对应的恢复操作叫resume,后者其实是unpaused
  • rescure/Unrecure:救援。当因为断电或者误操作导致操作系统出现故障不能启动实例了,一般会使用一张引导盘进入系统,然后试图恢复系统,比如忘记密码或者删除了某个文件。rescure可以将某个image作为启动盘,然就将原系统盘挂载到这个操作系统上,fdisk可以看到,新的image是vda,原系统盘就是vdb,修好以后unrescure恢复引导盘
  • snapshot:快照的原理就是对实例的镜像文件进行全量备份,保存到glance。
  • rebuild:当损坏严重的时候,可以使用快照恢复。rebuild会使用快照替换当前实例的系统盘,并且保持实例原有的网络、资源等信息

Keystone

常用的5000端口,以及没怎么用过的35357端口
为OpenStack其他服务提供身份验证、服务规则和服务令牌的功能,管理Domains、Projects、Users、Groups、Roles。

  • User:代指任何使用gopenstack的实体,可以是真正的用户,也可以是一个系统服务,在访问openstack的时候,keystone会对User进行身份认证
  • Credentials:User用来表明自己身份的凭证,可以是用户名密码、token或者API Key等
  • Authentication:是keystone验证User身份的一个过程,User访问openstack的时候向keystone发送用户名密码等信息,keystone验证成功后返回一个token,作为用户的Credentials
  • Token:是一串数字和字母组成的字符串,用户在keystone验证身份后会得到,在访问其他服务的时候会被用来验证身份有效性,一般默认24小时。
  • Project:用于将OpenStack的资源进行分组和隔离,可以是一个客户或者部门。一个用户需要挂载到Project后才能访问其中的资源
  • Service:Nova、Glance等就是OpenStack的服务,每个Service会提供若干个Endpoints供用户去访问资源和执行操作
  • Endpoint:是一个可以访问的地址,通过Endpoint暴露自己的API,keystone维护
  • Role:对用户进行鉴权,每个User会被分配一个或多个Role,该用户拥有Role指定的权限。如果修改权限需要修改/etc{service_name}/policy.json

Glance

监听端口:

  • glance-api:9292
  • glance-registry:9191

传统的安装一个系统可能需要从CD或者ghost工具进行安装,非常繁琐。二运计算环境中有一个更高效地解决方案,就是Image。
每个Image中装有操作系统以及相应的配套软件环境,用户可以直接使用安装即可快速创建一个虚拟机。
比如云计算中可以有这么一个场景:当我们需要有一个新的系统需求的时候,我们可以先手动创建一个虚拟机然后安装好相应的操作系统和需要的软件环境,然后创建一个snapshot保存在云中,后续如果有用户需要使用可以直接使用这个snapshot快速创建虚拟机,这都是可以自动完成了。

为云主机提供不同系统镜像,支持多种虚拟机镜像格式(AKI、AMI、ARI、ISO、QCOW2、Raw、VDI、VHD、VMDK),有创建上传镜像、删除镜像、编辑镜像基本信息的功能。

glance-api对外提供restful api接口调用,当有请求来以后,api不会自己主动处理,而是会调用glance-registry处理有关元数据的操作,调用store backend处理有关镜像的操作。
Image默认存储在/var/lib/glance/images
glance-registry负责处理有关metadata的操作,如存取。比如image的大小或者类型,支持多种格式:QCOW2、RAW、vmdk、ISO等,元数据默认保存到mysql
store-backend:glance自己不保存镜像,而是使用backend存储,glance支持多种backend,如默认的local filesystem本地文件系统、ceph、swift、cinder、EXSI等

Neutron

端口9292
提供云计算的网络虚拟化技术,为OpenStack其他服务提供网络连接服务。为用户提供接口,可以定义Network、Subnet、Router,配置DHCP、DNS、负载均衡、L3服务,网络支持GRE、VLAN。插件架构支持许多主流的网络厂家和技术,如OpenvSwitch。
根据网络类型的不同有以下几种网络:

  • Flat network:基于不使用 VLAN 的物理网络实现的虚拟网络。每个物理网络最多只能实现一个虚拟网络。
  • local network(本地网络):一个只允许在本服务器内通信的虚拟网络,不进行跨服务器的通信。主要用于单节点上测试。
  • VLAN network(虚拟局域网) :基于物理 VLAN 网络实现的虚拟网络。共享同一个物理网络的多个 VLAN 网络是相互隔离的,甚至可以使用重叠的 IP 地址空间。每个支持 VLAN network 的物理网络可以被视为一个分离的 VLAN trunk,它使用一组独占的 VLAN ID。有效的 VLAN ID 范围是 1 到 4094。
  • GRE network (通用路由封装网络):一个使用 GRE 封装网络包的虚拟网络。GRE 封装的数据包基于 IP 路由表来进行路由,因此 GRE network 不和具体的物理网络绑定。
  • VXLAN network(虚拟可扩展网络):基于 VXLAN 实现的虚拟网络。同 GRE network 一样, VXLAN network 中 IP 包的路由也基于 IP 路由表,也不和具体的物理网络绑定。

网络转发
一般架构是控制节点、计算节点和网络节点。
网络节点就dhcp服务之类的,计算节点是各种agent,控制节点是neutron-server一个组件。
然后如果跨子网通信的话,流量需要从计算节点到网络节点,通过router路由一下,转发到对应的计算节点的实例上。而如果需要配置NAT的话是需要在router上面配置的,也就是网路节点,如果有需要流量转发出去的话,都需要通过网络节点。
但是这样的话网络节点的压力就比较大,一旦网络节点崩溃了,整个网络就崩了;还有一个就是即便两个实例在同一个计算节点上,如果不在一个网络中,还需要经过网络节点路由一下才能回来,很麻烦。
那么就有一个东西DVR(Distributed Virtual Routing),部署在计算节点,可以直接使用浮动IP访问外网,不需要经过网络节点了。是通过openflow规则进行判断的。
Neutron功能

  • 二层交换:实例通过虚拟交换机连接到虚拟二层网络,Nova支持多种交换机,Linux Bridge和OpenvSwitch(开源虚拟交换机)
  • 三层路由:给实例配置不同网段的IP,router(虚拟路由器)可以实现跨网段通信,使用IP forwarding、iptables等实现路由和NAT
  • 负载均衡
  • 防火墙:一个是security group使用的iptables、另一个是firewall-as-a-service,也是基于iptables的

一些概念

  • network:二层广播域,支持多种类型网络:local、flat、vlan、vxlan、gre
    • local:这个网络与其他网络节点隔离,只能和同一节点的同一网络实例通信
    • flat:没有vlan tag的网络,网络中的实例可以和其他实例通信
    • vlan:802.1q标签的网络,一个二层广播域,同一个vlan中的实例可以相互通信,不同vlan只能通过router通信。vlan可以跨界点,标签数为1-4096
    • vxlan:基于隧道技术的overlay网络,VxLAN网络使用唯一的段ID与其他VxLAN进行隔离。会通过VNI封装为UDP数据包传输,二层包通过封装可以在三层传输,因此克服了物理网络基础设施的限制
    • gre:是和VxLAN类似的overlay网络,区别在于使用IP包封装而不是UDP

一些虚拟的网络设备

Veth pair
Veth是linux系统虚拟出来的一个网络设备,总是成对出现的,功能类似虚拟网线的功能,在创建两个Veth设备,假设放在两个namespace里面,那么通过这个Veth设备可以实现相互通信,默认情况下是不通的。
通常链接网络的namespace,也链接linuxbridge,一般用于连接两个linuxbridge或者一个linuxbridge和ovs,两个ovs一般不太用这个,一般用patch连接两个ovs bridge,效率更好。
实际操作
在这里创建一个新的namespace,然后创建一对Veth pair试一下,单纯记录,我没有操作过。

#创建namespace
ip netns add tns(tempnamespace)
#创建Veth设备
ip link add tns-0 type veth peer name tns-1
#生效网卡
ipconfig tns-0 up或者ip link setet tns-0 up
ipconfig tns-1 up
#将一段veth pair 1放入namespace,并重命名为eth0,添加新的网络网卡接口
ip linhk set tns-1 netns tns
ip netns exec tns-1 name eth0
#生效一下网卡ip netns exec tns ip link set eth0 upip netns exec tns ip link set lo up#设置一下新的Veth网卡设备的IP地址,随便写吧ip netns tns ip addt add 10.254.1.1/24 dev eth0ip addr add  10.254.1.2/24 dev tns-0#现在可以验证连通性了验证ip netns exec tns ping 10.254.1.1

TUN、TAP
Veth是两端都一样的虚拟网线,而这个就是两端都不一样的虚拟网先,相当于一边是水晶头,另一端是USB接口,是用户空间和内核空间传输报文使用到的“网线”,一边是普通的网卡比如eth0,另一端是文件描述符,用户空间使用的。
实例的本质是qemu进程,因此TUN、TAP网线一般是给VM用的,所以这个文件描述符一般是VM,另一端则是虚拟出来的TAP设备,这也就是为什么Neutron几种网络模型里面的VM或者linuxbridge连接router的时候都是通过TAP设备的
Bridge
是一种虚拟集线器的实现方式,多个网卡连接到这个上面,一个发送报文其他都能接收到。
把TUN/TAP或者Veth pair放在Bridge上面,就可以实现相互通信。Docker就是用linuxbridge将所有的容器连接在一起,bridge模式,就是docekr0网卡

架构

Neutron主要有以下几个部分:

  • Neutron Server:对外提供API,接收请求,调用Plugins处理请求
  • Plugin:处理来自Server的请求,并且转发调用Agent处理请求
  • Agent:处理来自Plugin的请求,并且在network provider上具体实现每个网络功能
  • network provider:提供网络服务的虚拟网络设备或物理网络设备,比如Linux Bridge或者OpenvSwitch或者其他支持Neutron的物理交换机
  • Queue:组件之间交互的消息队列
  • Database:存放网络状态信息

架构这么多层次有两个原因

  • 为了支持现有或者未来出现的网络技术
  • 为了支持分布式的灵活扩展性

plugin有一个功能时需要维护数据库中的网络状态信息,如果有多个插件的话,每一个provider的plugin都要写一套类似的数据库操作代码,会很繁琐,因此现有版本使用了ML2 plugin,对plugin进行了抽象。只需要实现响应driver就行了,不需要具体实现plugin了。
plugin主要分为两种(均有对应的agent):

  • core plugin:比如linux bridge或者openvswitch
  • service plugin:比如routing、firewall、load balance等

物理架构
有两种。
第一种:控制节点+计算节点
控制节点配置neutron server、core plugin、service plugin和两个对应的agent
计算节点配置core plugin的agrnt,负责二层网络功能。
通过agent实现控制节点和计算节点之间的二层网络通信,可以部署多个控制节点和计算节点
第二种:控制节点+网络节点+计算节点
控制节点配置neutron server
网络节点配置core plugin、service plugin和对应的agent
计算节点配置core plugin,负责二层网络功能

  • 将agent从控制节点分离出来,控制节点只通过neutron server处理分发api请求
  • 可以通过增加网络节点的个数提高负载
  • 使用独立的节点实现数据交换,并且可以配置路由、负载均衡等高级功能
  • 适合大规模环境
Neutron Server

由上而下分别是

  • Core API:对内提供restful api,如管理子网网络端口等
  • Extension API:对外提供restful api,如管理router、firewall-as-a-service、负载均衡等
  • Common Service:认证校验API
  • Core Plugin API:定义个Core Plugin抽象功能,通过API调用对应plugin
  • Neutron Core:Neutron的核心处理程序,调用相应Plugin处理
  • Extension Plugin API:定义。。。
  • Service Plugin:维护管理router、负载均衡等资源状态信息,调用agent在network privider上实现相应功能

但是提出了两个问题

  1. 只能使用一种core plugin,多个无法共存
  2. 不同plugin之间复用的代码太多了,维护工作量大
ML2 Plugin

主要解决上面说的两个问题
允许网络中使用多种二层网络技术,不同节点直接按可以使用不同的网络实现机制。
可以在不同节点上使用不同的agent了,而且不用重新开发core plugin,实现mechanism driver就可以了。
ML2对二层网络进行了抽象解耦,type driver和mechanism driver使得其具有非常好的弹性,能够灵活支持多种agent(type或者mechanism)
每一种网络都有这么两种driver
Type Driver
负责管理网络状态、创建网络等。
有这么集中网络类型:local、flat、vlan、vxlan、gre
mechanism driver
获取由type维护的网络状态,并且确保其在物理或者虚拟设备上的实现
比如创建一个VLAN100,VLAN的type driver负责将数据保存在数据库里,然后linux bridge的mechanism driver负责在各个节点上调用agent创建相应的vlan设备和bridge。
Mechanism Driver有三种:

  • Agent-based:就是基于agent的,比如linux bridge和openvswitch
  • Controller-based:基于控制器的,如opendaylight,就是sdn
  • 基于物理交换机:如思科的设备

Linux Bridge实现

local network

local网络不会和任何物理网卡连接,也不会有VLAN ID。
对于每一个local网络都会创建一个网桥,将实例的tap设备绑定到网桥上。
同一网桥上的实例可以相互通信。每个local网络都有自己的网桥,互不影响,因此同一个节点上不同往前的实例不能通信。
其实只有admin创建网络可以选择type driver,普通用户在自己租户里创建网络的时候默认使用/etc/neutron/plugins/mk2/ml2_conf.ini里面的一个参数tenant_networks_type=local,默认是local,需要修改一下,可以指定多个,如vxlan,local,会从左到右按照顺序依次创建,如果vxlan的id用完了,就会创建local。
命名
brqxxxxxx对应的是网络,表明这是网络id为xxxxx创建的一个网桥
tapyyyyy,其实就是虚拟机的一个虚拟网卡,对应的是port,说明这是id为yyyy的port的tap接口设备,tap将连接到brq上

在创建实例的时候,neutron-linubgridge-agent根据port信息创建一个tap设备,连接到local网络所在的bridge网桥上。这个tap设备就会映射为实例的虚拟网卡。

flat network

不带tag的,要求和物理网卡连接,所以需要在ml2的配置文件里写physical_interface_mappings=default:thh0,前面式一个label,用来标识flat,可以是任意字符粗汉,就是表明和物理网卡的对应关系,因为可能每个节点使用的物理网卡不一样,因此这里的default就映射为配置好的物理网卡。
如果不同节点的实例连接到同一个网络,他们所处的网桥名称一致,通过provider network通信。

vlan network

vlan是带tag的网络。
如下所示,多个tap连接到brq网桥上。eth1网卡上创建了一个eth1.100的vlan interface接口,可以连接到这个brq上,然后通过eth1.100到eth1的数据包就会被标记一个100的vlan tag。
如果有多个eth1.xxx接口连载eth1网卡上,就可以通过这个tag标签相互隔离不同的vlan。

如果需要这么设置的话,物理机连接的交换机端口需要设置为trunk,不能是access,因为需要流过多个tag数据包

vxlan

OpenStack支持VxLAN和GRE这两种overlay网络,overlay网络指建立在其他网络之上的网络。
linuxbridge只支持vxlan,ovs两个都支持。
优点

  • VLAN使用12bit标记tag,最多4096个,而VxLAN支持24bit,最多16777216个二层网段
  • 能够很好利用已有路径,封装UDP通过三层传输和转发,可以使用所有路径。(VLAN用了Spanning Tree Protocol避免环路,有一半路径用不成)
  • 避免物理交换机MAC表耗尽,因为用了隧道技术,不需要再MAC表里记录虚拟机信息

缺点
其实也是有一些缺点的

  • 比如强制在三层传输数据包可能路径不是最优传输速度慢
  • 虽然避免了物理交换机记录大量的虚拟机MAC,但是可能会造成路由器的ARP表有较多的记录,影响正常的性能
  • 而且标准规定VxLAN不能分片,要求物理链路层的MTU足够大才行

VxLAN是将二层建立在三层上的网络,把二层数据封装在UDP数据包里扩展二层网络。IP+UDP

DHCP服务

通过DHCP agent实现DHCP服务。运行在网络节点,dnsmasq
当 创建一个网络的子网开启dhcp功能后,agent会启动一个dnsmasq进程提供服务,其实dnsmasq和network是对应的,一个进程可以给所有的子网提供dhcp服务。

通过dnsmasq获取IP信息。
在创建实例的时候,实例会绑定一个port,有MAC和IP信息,dnsmasq会把这个记录在一个host文件里。
实例启动以后,会发送DHCPDISCOVER广播,然后广播消息会在flat网络里传播,通过veth paur到达另一个namesapce里面,dhsmasq监听到了,然后返回对应的IP信息,DHCPOFFER,包含IP地址、租期时间等给实例
最后实例返回一个DHCPREQUEST消息接收DNSOFFER

NameSpace

二层网络上可以通过VLAN将物理交换机分割为多个虚拟交换机。
而namespace可以将物理的三层网络划分为多个隔离的虚拟三层网络,有这几个字的网络栈、防火墙规则、路由表等。
Neutron就是通过namespace为每个network提供DHCP服务和路由,让租户之间的网络可以重叠不冲突,提高了灵活性。
每个namesapce里有各自的dnsmasq,ip nets list可以查看。
管理员可以将brq或者tap添加到某个namesapce里面。

如何让两个namespace相互通信呢
默认是不能通信的,Neutron里面使用了 veth pair,相当于虚拟网线,连接两个namespace,这边输入另一边接收。

Routing

路由提供了跨子网的通信。这里有两个实例
vm1 - 172.16.100.3 - vlan100
vm3 - 172.16.101.3 - vlan101
这两个实例是两个不同vlan的,他们之间不能通过二层交换进行通信,必须借助router。
虚拟router由L3 Agent在控制节点或者网络节点运行。

底层实现

通过配置一个router,vm1和vm3就可以进行通信了。
首先vlan101的bridge网桥上多了一个tap设备,这个设备就是路由器的interface接口
同样vlan100所在的网桥也有这么个接口设备。
L3 Agent会给每个router配置一个namespace,使用veth pair和tap设备连起来。对应的gateway ip在namesapce内的veth interface上。叫qr-…
其中namespace里的qr-xxxx和tap-xxxx组成veth pair进行通信。

如下图所示

引入namespace而不直接使用网关的作用
为了使得租户之间的网络可以重叠提高灵活性。
不然如果A和B都有相同子网,只用网关的话需要在控制节点的路由表上加两项,而且目的IP可能一致,无法区分了。

router连接外网

给router配置好一个外部网络(一般是flat或者vlan类型)的网关以后,router多一个接口,通过这个接口可以连接到外网,ip比如是10.10.10.2。
然后连接外部网络的bridge的tap-xxxx设备,通过veth pair和路由器namesapce内的qg-xxxx接口连接
冷知识,router的内部网络的接口叫qr-xxxxx,外部网络叫qg-yyyyy
此时看ip nets exec qrouter-xxxx route,会看到10.10.10.1有一个qg的接口。说明如果是外网流量的话,router会通过这个接口将流量转发到这个网关,然后到linuxbrige的brq网桥上转发到网卡上。。
从qg-xxxxx出去的话会进行一个SNAT的转换,将源IP修改为router的源IP 10.10.10.2,以便回来的时候可以找到路由器

floating IP

SNAT可以让实例访问外网,但是还不能让外网访问实例,使用浮动IP可以解决这个。
floating ip可以提供一个一对一映射的静态NAT。
创建好一个floating ip以后,会配置到router的外网的qg接口,然后iptables会添加两个规则

  • 如果是内网IP到达路由器,转换为对应的floating ip发送出去
  • 如果是floating ip对应的数据包到达路由器,会修改为对应的内网IP

Fwaas

让用户可以创建和管理防火墙,在子网的边界进行流量过滤。传统的防火墙是在网关上的,隔离子网,而这个是在router上配置的,控制租户网络进出的流量。
分为:

  • Firewall:必须关联某个Policy
  • Firewall Policy:是Rule的集合,会按照Policy中的顺序依次过滤
  • Firewall Rule:访问控制规则,包括源IP和端口、目的IP和端口,协议类型以及操作

安全组的对象是虚拟网卡,L2 Agent实现,对通过iptables实现对实例虚拟网卡流量的过滤。而fwaas是配置在虚拟路由器上的,在到达安全组之前可以先过滤一下,但是同一个subnet内部的虚拟网卡间不会过滤,因为不通过router。

fwaas没有单独的agent,是在L3 Agent中配置的,driver为IP tables,要在service plugins启用fwaas。
FWaaS v2
上面说的是v1,Stein版本以后,v1废弃了,是v2了。
Firewall概念变为Firewall Group了,而且不像v1是唯一绑定一个路由器,v2需要指定路由器的某个接口。
可以同时管理ingress和egress进口和出口两种流量。v1则不区分,对双向流量进行过滤,安全组区分流量。

OpenvSwitch

ovs是除了linuxbridge外的另一种虚拟化交换机技术。
安装ovs agent,修改ml2的mechanism_drivers
初始状态有三个网桥:

  • br-int:连接所有虚拟机的虚拟网卡或和其他虚拟网络设备
  • br-ex:连接外部网络的网桥
  • br-tun:隧道技术用这个,比如VxLAN或者GRE

计算节点没有br-ex,因为计算节点的外网流量是通过网络节点的虚拟路由器转发的,所以br-ex在网络节点上。

local network

同样的local网络不会和网卡连接,流量被限制在宿主机内,只有同一个网桥上的才可以通信。
创建了一个local网络以后,br-int网桥上有一个tap设备,是dhcp的接口,tap设备是在这个命名空间里的。

创建一个实例以后,Neutron在子网subnet中创建一个port,分配IP和MAC,绑定在实例上,会创建一个tap-xxxx设备作为实例的虚拟网卡。
然后在linuxbridge上创建一个qbr-xxxx网桥,和tap设备相连。
然后通过veth pair连接到br-int,即qvb-xxxx与qvo-xxxx
为什么需要使用一个linuxbridge中转不能直接使用tap设备连接到br-int呢
因为ovs不支持将iptables规则放在与其相连的tap设备上,因此为了实现security group安全组的功能,需要引入linuxbridge作为一个中转
如下图所示
一个实例的网络设备连接情况如下(以次连接):

  • tap-xxxx:虚拟网卡
  • qbr-xxxx:tap连接到linuxbridge的qbr,为了使用security group功能
  • qbr通过一对veth pair(qvb-xxxx和qvo-xxxx)连接到br-int网桥

    <font color="red>如果此时创建第二个local 网络,然后创建一个虚拟机,其虚拟网卡同样会连接到br-int上,那么可以通信吗。
    不可以。
    ovs会将每个网桥看成一个虚拟机,然后可以支持vlan,每个local网络的djcp和虚拟网卡都有一个tag,这个tag就是VLAN ID,相互之间隔离。仅用于隔离网桥中的port。
flat network

flat网络的话是不带tag的,而且一个flat网路需要和一个物理网卡相连。
这里在修改配置文件的bridge_mappings的时候,不是标签:物理网卡了,而是标签:连接网卡的网桥名。
因此需要先创建一个br-eth1网桥,连接到物理网卡eth1。
此时在br-int和br-eth1会分别多出一个接口int-br-eth1和phy-br-eth1,是patch类型的,用来连接br-int和br-eth1,可以访问外网。
如下所示

在linuxbridge里面用veth pair连接br-int和linuxbridge,而这里使用patch连接br-int和br-eth1。
veth pair 和 patch
两者都可以作为一个类似虚拟网线的功能连接网桥。

  • patch port是连接两个ovs bridge的专属,而且性能更好
  • veth pair只能用于连接两个linuxbridge或者linuxbridge与ovs连接

底层实现
创建一个flat网络与后,会创建一个dhcp的接口,tap-xxxx连接到br-int网桥。
创建一个虚拟机后,同样的,先创建一个tap设备作为虚拟机的虚拟网卡,然后连接到linuxbridge的qbr网桥上,然后qbr通过一对veth pair连接到br-int网桥。

VLAN 网络

ovs中是所有虚拟网卡都连接到br-int网桥,而linuxbridge里面则是不同VLAN连接到不同网卡的VLAN网桥。因此物理交换机连接eth1的口也要设置为trunk。
ovs通过flow rule对流过br-int的数据包进行转发处理,比如打上tag或者去掉tag等。
ovs-ofctl dump-flow查看流规则。
比如br-eth1网桥的流规则如下,这里的in_port编号可以在ovs-ofctl show 查看到。
这里就是说从br-eth1网桥的phy-br-eth1(port=2)端口进来的vlan为1的数据包,修改vlan id为100然后发送出去

br-int网桥的规则同理
这里的tag和VLAN其实是不一样的,tag是ovs内部进行网络隔离使用的,在接收到数据包的时候,需要进行vlan的一个映射,Neutron维护VLAN ID的映射关系。

Roting

两个子网之间的实例通信需要使用路由器进行通信。
创建一个路由器以后,将 子网可以添加到路由器的接口上,接口的IP分别为子网的网关。
br-int网桥上多了两个接口port,叫qr-xxxx。router也是运行在自己的namesapce里面。
如果路由器需要连接外网,需要绑定一个外部网关,然后router就多了一个接口10.10.10.2,用于连接外网。也就是路由器的qg-xxxx接口。

VxLAN

可以设置VxLAN 的vni也就是tag,需要在配置文件的[ovs]配置tunnel_bridge
br-int和br-tun是通过patch port连接的,其中patch-tun在br-int上,patch-int在br-tun上。
底层实现
创建网络以后,dhcp也会通过tap设备连接到br-int上。
实例的虚拟网卡tap设备还是连接到linuxbridge的网桥qbr上,通过veth pair(qvb和qvo)连接到br-int上。
而br-tun上则多了一个vxlan-xxxx的东西,比如用于连接控制节点和计算节点的,制定了VTEP的IP。
此时br-int充当了一个二层交换机,查看flow rule以后可以看到是通过vlan和mac转发数据包的。
而br-tun的flow-rule才是进行真正转发数据包的。

Cinder

为运行实例提供稳定持久化的数据块存储服务,如创建卷、删除卷,在实例上挂载和卸载卷。
cinder-api:接收http请求调用cinder-volume
cinder-volume:管理volume服务,管理卷的生命周期
cinder-schedule:通过算法为卷调度一个合适的节点

Swift

Zun

openstack租户管理_OpenStack容器服务Zun初探与原理分析
Keystone、Neutron以及Kuryr-libnetwork是运行Zun必备的服务。分别提供了身份认证以及网络的功能支持。
zun-api负责接收api请求进行处理。
zun-compute服务调用container driver进行docker的创建,目前只实现了Docker Driver
流程步骤
zun/api/controllers/v1/containers.py
进行参数校验,如用户是否具有policy权限,网络、安全组、配额等是否符合。
zun/compute/api.py
先schedule调度一个合适的计算节点,返回host对象,这个功能集成在zun-api里面了。
检查镜像是否存在,远程调用zun-compute的image search方法,其实就是调用了docker search,为了实现快速失败,避免到计算节点才发现错误。
然后远程调用zun-compute进行后续工作
zun/compute/manager.py
创建卷或者挂载硬盘,然后下载镜像,创建端口,调用docker启动容器。
其中调用docker启动容器的操作的代码位于zun/container/docker/driver.py,这个部分其实就是对于Docker SDK for python的一个封装。
如import docker

Kuryr-libnetwork

这个项目的目的是将容器网络与neutron进行融合,提供接口南向连接neutron,北向连接容器网络。
开始目的是为了提供Docker与Neutron的连接。将Neutron的网络服务带给Docker。随着容器的发展,容器网络的发展也出现了分歧。主要分为两派,一个是Docker原生的CNM(Container Network Model),另一个是兼容性更好的CNI(Container Network Interface)。Kuryr相应的也出现了两个分支,一个是kuryr-libnetwork(CNM),另一个是kuryr-kubernetes(CNI)。

kuryr-libnetwork是运行在Libnetwork框架下的一个plugin,替换了原有的docker engine,成为一个kuryr就是libnetwork的一个remote-driver,现在是docker的推荐remote-driver。
其实实际上kuryr起了一个http服务,23750端口,提供了libnetwork的所有接口,docker找到以后通过这个与kuryr通信。
kuryr借用了neutron的subnetpool,保证了子网间ip的不重复
实现原理
在上面zun调用docker模块创建容器的时候,也会进行网络的配置,就在这里。
先检查docker网络是否存在,不存在就创建,创建的docker网络name就是neutron的uuid,会调用neutron创建一个port,即容器的port是zun创建的,不是kuryr创建的。
然后对于虚拟网卡,会虚拟出一个tap设备,其中容器内部命名空间有一个t_cxxxx的设备,两个通过veth pair连接起来。然后tap设备会连接到qbr的linuxbridge网桥,随后网桥通过qvb和qvo的veth连接到br-int集成网桥。
其实哦在那个接一下kuryr的作用就是把neutron的port绑定在容器上。

SDN

软件定义网络,是网络虚拟化的一种实现方式。
主要体现在如下三个方面:

  • 转发与控制分离:SDN具有转发与控制分离的特点,采用SDN控制器实现网络拓扑的收集、路由的计算、流表的生成及下发、网络的管理与控制等功能;而网络层设备仅负责流量的转发及策略的执行。通过这种方式可使得网络系统的转发面和控制面独立发展,转发面向通用化、简单化发展,成本可逐步降低;控制面可向集中化、统一化发展,具有更强的性能和容量。
  • 控制逻辑集中:转发与控制分离之后,使得控制面向集中化发展。控制面的集中化,使得SDN控制器拥有网络的全局静态拓扑,全网的动态转发表信息,全网络的资源利用率,故障状态等。因此,SDN控制器可实现基于网络级别的统一管理、控制和优化,更可依托全局的拓扑的动态转发信息帮助实现快速的故障定位和排除,提高运营效率。
  • 网络能力开放化:SDN还有一个重要特征是支持网络能力开放化。通过集中的SDN控制器实现网络资源的统一管理、整合以及虚拟化后,采用规范化的北向接口为上层应用提供按需分配的网络资源及服务,进而实现网络能力开放。这样的方式打破了现有网络对业务封闭的问题,是一种突破性的创新。

OpenFlow

OpenFlow是一种网上通信协议,属于数据链路层,允许控制器直接访问和操作网络设备的转发平面,借此改变数据包的走向。这些设备可以是物理设备也可以是虚拟交换机。
转发平面基于流的方式转发。
网络设备会维护若干个流表,数据流只按照流表进行转发,而流表的生成与维护都是控制器做的事。
这里的流表不仅仅是IP五元组,而是还有这一些关键词和执行动作等。在实际使用中可以根据需要的粒度进行一个限制,比如如果想要粗粒度,只需要设置IP就好了,不需要设置其他的参数比如端口之类的。

OF 1.0

一个流表包含一个流表项的集合(包头域)、活动计数器以及一个操作集。
所有流过交换机的数据包都需要进行流表的匹配,如果匹配成功,执行相应的操作,如果没有匹配上,将其转发到控制器,由控制器决定如何处理这个数据包。
这个包头域会根据数据包的信息进行相应的匹配,使用的是12元组

  • 入口:交换机端口
  • 以太网源端口
  • 以太网源IP
  • 以太网类型:VLAN是0x8100,ARP是0x0806、IP是0x0800
  • VLAN ID
  • VLAN优先级
  • 源IP
  • 目的IP
  • IP协议:TCP6、UDP7、ICMP1
  • IP ToS位
  • 源端口
  • 目的端口

活动计数器包含有多个计数器,会根据匹配的数据包进行一个更新。

  • 流表:流表的匹配数据包数
  • 流表项flow:每一项接收的数据包字节数、数据包个数、延迟等
  • 端口:每个端口接收到的数据包数、字节数、转发的数据包数、字节数等
  • 队列:转发的数据包数

操作集规定了对匹配成功数据包进行什么操作,可以有0到多个操作,如果没有相应操作,丢弃数据包。
一些基本的操作有:

  • ALL:给除了入口的所有接口发出数据包
  • Controller:发给控制器
  • INN PORT:往入口发送数据包
  • DROP:丢弃

还有一些可选的

  • NORMAL:传统交换机的正常转发
  • FLOOD:泛洪至除了入口以外的所有出口
  • 修改域:这个功能比较厉害,它可以对数据包里面的信息进行修改随后转发,比如可以修改VLAN ID、VLAN优先级、源IP和目的IP、源端口和目的端口等信息

OF 1.3

主要添加了多级流表(流水线),增加了一些多控制器的支持,增加了对数据包处理的动作
一个流表项的组成

  • 匹配域
  • 优先级
  • 计数器
  • 超时时间
  • 指令
  • cookie

将原来的动作变更为指令,然后允许数据包在流表之间跳转。
不同于1.0的十二元组,这里其实有四十多个字段,但是一般大多数用不到,主要也就是那么些。
有一个指令是go-to-table:转向另一个流表
如果没有这个,会有以下几个动作

  • output:指定端口转发出去
  • drop:丢弃
  • push-tag或者pop-tag:对VLAN等ID进行处理
  • setField:识别匹配的字段,并且可以修改
  • change-TTL:修改数据包的TTL值

同时 还需要支持table-miss,就是说如果没有匹配成功,进行什么样的动作,转给控制器还是丢弃。

Django

概述

Django是一个开放源代码的web应用框架,使用python写成。本身是基于MVC模型的,实际采用了MTV的框架模式,即模型(Model)、模(Templates)板和视图(Views)。其中:

  • 模型:数据存取曾,处理所有与数据有关的事务,比如如何存取数据、验证有效性等
  • 模板:表现层,比如如何在页面或者其他类型文档中显示
  • 视图:业务逻辑层,处理数据存储以及调用模板的逻辑

简单流程:
用户通过浏览器向我们的服务器发起一个请求(request),这个请求会去访问视图函数:
a.如果不涉及到数据调用,那么这个时候视图函数直接返回一个模板也就是一个网页给用户。
b.如果涉及到数据调用,那么视图函数调用模型,模型去数据库查找数据,然后逐级返回。
视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。

Django的主要目的是简单快捷的开发数据驱动的网站,强调代码复用,多个组件可以很方便的以插件形式服务与整个框架。
有以下几个特点:

  1. 对象关系映射(ORM):通过定义映射类构建数据结构,并且将模型与数据库连接起来,使用ORM框架内置的接口即可方便快捷操作数据库
  2. URL设计:可以设计匹配任意地URL.(支持正则表达式)
  3. 模板系统:提供可扩展的模板,模板还可以继承
  4. Auth认真那个系统,提供有一个后台管理界面,可以进行用户认证以权限认证
  5. 国际化:内置国际化,开发出多语言的网站
  6. Cache系统:完善的缓存系统:支持多种缓存方式
  7. 表单处理:生成各种表单,还可以对数据进行验证

MVC和MTV

MVC:
核心思想是解耦

  • model:对数据库的底层封装
  • view:向用户展示结果
  • controller:核心,处理请求、获取数据、返回结果


MVT:

  • model:与数据库进行交互
  • views:核心,接受请求,获取处理数据,返回结果
  • template:呈现内容到浏览器上

views里处理业务逻辑比如登录验证、数据处理等
model里面不需要编写一句sql,ORM会自动转为sql去执行。
数据库里每一行数据就是一个对象,每一个表是一个集合,python用列表表示这个集合
template呈现页面,用css和js渲染html等
还需要一个 URL 分发器,它的作用是将一个个 URL 的页面请求分发给不同的 View 处理,View 再调用相应的 Model 和 Template,MTV 的响应模式如下所示

和Flask Tornado的区别

Django提供了更多的组件支持,让开发变得更加方便。
比如

  • ORM对象关系映射,对数据库进行操作,不需要编写sql语句
  • Admin管理后台
  • 模板
  • 表单
  • 认证权限
  • 缓存
  • session机制

Flask轻量级轻在它本身是一个内核,其他功能都需要扩展实现。比如可以引入其他模块实现ORM,也没有默认数据库,但是可以自行配置使用mysq或者nosql,性能比Django好。依赖于Werkzurg WSGI路由模块和jinja2模板。
对于路由的匹配

  • Django使用过urls.py种对路由进行字符串的匹配,选择匹配的views.py函数进行业务处理
  • Flask则是使用装饰器给一个函数配置一个路由,然后进行处理

Tornado
功能少而精,非阻塞式异步设计方式。

  • iostream:对非阻塞式socket进行封装
  • ioloop:对IO多路复用进行封装,实现单例

Tornado是一个使用python开发的全栈式的web框架和一部网络库,使用非阻塞式IO,可以处理数以万计的开放链接,常用于long polling、web sockets和其他需要维护长连接应用的选择。
主要分为四个部分

  • Web框架(RequestHandler,用于创建Web的基类支持各种类)
  • 实现HTTP的客户端和服务器端(HTTPServer、AsyncHTTPClient)
  • 异步网络库(IOLoop、IOStream)
  • 协程库(tornado.gen),使得异步调用代码可以更好编写

tornado的性能比django和flask好就是因为其底层io处理机制是根本不同。

  • tornado、gevent、syncio、aiohttp:事件循环+协程
  • django、flask:

Django请求生命周期

浏览器输入网址,发送请求到服务器
匹配路由
url经过uWSGI->WSGI,然后经过中间件的处理,在urls.py路由映射中找到一个匹配的路由,从上到下依次匹配,匹配一个就可以停了
视图处理
匹配到url以后,跳转到对应的视图函数进行相应的业务处理,比如操作数据库等
返回响应
先经过中间件对返回的数据再进行处理,然后返回相应的页面文件,由浏览器渲染以后显示给用户,同时将模板内容填充到html空白处。

一些额外的
在匹配路由的时候,有两种方式,是CBV、FBV

  • FBV(function base views):一个url对应一个视图函数
  • CBV(class base views):一个url对应一个类,会根据请求的方法调用对应的函数

CBV,url匹配成功后会自动去找dispatch方法,然后Django会通过dispatch反射的方式找到类中对应的方法并执行,类方法执行完成以后,返回的结果回传给dispatch()
对应类的话如下所示

#urls.py
urlpatterns = [url(r'^fbv/',views.fbv),url(r'^cbv/',views.CBV.as_view()),
]#views.py
form django.views import Views
class CBV(Views):def get(self,request):return render(request,'cbv/xx/html',context)def post(self,request):return HttpResponse('...')

常见的web应用程序

框架组件

  1. 序列化组件:进行序列化并且对数据格式进行验证
  2. 路由组件:对请求进行路由分发
  3. 视图组件:对请求进行业务处理
  4. 认证组件:写一个类并注册到认证类(authentication_classes),在类的的authticate方法中编写认证逻
  5. 权限组件:写一个类并注册到权限类(permission_classes),在类的的has_permission方法中编写认证逻辑。
  6. 频率:写一个类并注册到频率类(throttle_classes),在类的的allow_request/wait 方法中编写认证逻辑
  7. 解释器:选择对数据解析的类,在解析器类中注册(parser_classes)
  8. 渲染器:定义数据如何渲染到到页面上,在渲染器类中注册(renderer_classes)
  9. 分页:对获取到的数据进行分页处理, pagination_class
  10. 版本:用来控制不同版本客户端的不同行为

RestFramework

面向资源是REST最明显的特征,资源是一种看待服务器的方式,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。
可以使用URI统一资源标识符标记一个服务器的资源,通过不同的http方法对资源进行相应的操作。这里的资源可以是一个文本文件、视频、图片等。
符合REST架构设计的API就是restful api。

继承APIView
@api_view([‘POST’],[‘GET’])
在这个类中,传入的参数是rest framework的request实例,不是django的httprequest
可以返回rest framework的resonse,而不是HttpResponse
传入的请求可以进行一些认证
可以指定解析器Parser对传入的数据进行解析,如JSON、Form等解析。

一些功能模块
认证、权限和频率
dispatch()方法中进行一个判定。
认证和权限需要检查request.user和request.auth是否合法,是否允许请求。

缓存机制

网站访问量过大的时候,响应速度可能会大大降低,出现卡死的状况,可以使用缓存解决这类问题。
缓存是将一个请求的响应内容保存到内存、数据库、文件或者高速缓存系统(memcached),如果接下来一段时间再次来同一个请求,就不需要执行相应过程,只需要读取内存或者高速缓存系统就可以了。
Django提供5种缓存方式:

  • Memcached:一个高性能的分布式内存对象缓存系统,用于动态网站,减轻数据库负载。适合在内存中缓存数据和对象减少读取数据库的次数,适合超大型网站
  • 数据库缓存:将缓存的信息存储在网站数据库的缓存表中,适合中型网站
  • 文件系统缓存:缓存的信息以文本文件的格式保存下来,适合中小型网站
  • 本地内存缓存:默认保存缓存的方式,缓存存放于内存中,仅用于测试
  • 虚拟缓存:Django内置的虚拟缓存,实际上只是提供一个接口,不实际缓存数据,用于测试

WSGI,uwsgi,uWSGI

WSGI
是python定义实现的一个web服务器和web应用程序之间交互的一个接口,是一种规范,描述定义了服务器和应用程序之间通信的规范。
WSGI包括server和applicatioon两部分。
其中server负责接收客户端请求,把request转发给application,接收application的response返回给客户端。
WSGI其实是一种server和application解耦的规范,有多个实现server的服务器,也有多个可以实现WSGI application的框架,可以自由组合。比如uWSGI、Gunicorn就是实现了WSGI server,而Django和Flask则实现了WSGI application的框架,可以自由组合,比如uWSGI+Django。

WSGI除了监听端口进行解析http,还有流量转发和管理application进程。一般WSGI内置的WSGI server都是单进程,一次只能处理一个请求,而通用的比如gunicorn或者uwsgi都是pre fork模型,会有一个master进行监听,启动多个slave(一个slave就是一个WSGI application)处理请求。

uwsgi
也是一种协议,用于定义传输信息的类型,可以和nginx等代理服务器通信
uWSGI
是一个web服务器,实现了WSGI协议、uwsgi和HTTP协议,把接收到的HTTP协议转为支持的网络协议,比如可以转为WSGI协议,然后python可以直接使用。

比如可以使用nginx处理静态内容,使用uWSGI处理动态内容。
对于Django和Flask其实自己本身带有一个简单的WSGI server,一般用于服务器调试,生产环境下建议使用其他的,比如manage.py runserver就是启动一个WSGI,只用于本地开发,生产环境建议nginx+uwsgi+django

请求的生命周期

  1. wsgi接收请求:发给后端框架(flask或者django等)
  2. 中间件处理:对请求进行校验或者进一步处理(如csrf、request.session)
  3. 路由匹配:根据用户请求的url匹配不同的视图函数
  4. 视图函数:在views.py里面处理业务逻辑
  5. 中间件:对响应的数据进行处理
  6. wsgi:返回响应给浏览器

中间件的作用和应用场景

介于request和response中间对数据处理的一个流程,用于全局范围改变django的输入和输出,中间件就是在视图函数执行之前或者之后可以执行额外的操作。
比如

  1. csrf保护,默认开启,会检查请求的csrf token是否匹配
  2. 通过中间件可以判断用户请求是否登陆等,进行身份验证功能
  3. 可以检查用户是否处于请求白名单或者黑名单等

中间件的五个方法:

  • process_request:请求进来,进行认证等
  • process_view:路由匹配后得到视图函数
  • process_exception:异常的时候执行
  • process_template_responseprocess:渲染模板的时候执行
  • process_response:请求有响应的时候执行

这五个方法分别大概以下功能
中间件

  • process_request(request):收到request以后,按照settings.py文件中的中间件顺序依次执行,如果返回None,则继续,如果返回HttpResponse,就返回不继续了,也就是报错了
  • process_view(request, view_func, view_args, view_kwargs):执行完上一步的方法以后,在url里面找到对应的视图函数,然后获取到相应参数,在执行视图函数之前执行这一步。如果返回为None,则继续执行这个方法剩下的操作,如果返回HttpResponse,不执行这个方法和视图函数,继续后面的函数
  • process_exception(request, exception):如果执行视图函数出错,按照settings.py中的中间件顺序,倒序执行这个方法,如果返回None,就继续上一个方法的process_exception方法,如果返回HttpResponse,则不会被调用。也就是说,如果没有响应,就说明报错,倒序一级一级报错,如果有返回值说明还是正常的,就继续往下执行后面两个函数
  • process_template_response(request, response):response是视图或者某一中间件的返回,只有response实现了render才能执行,所有中间件的这个函数执行完了,调用render()方法
  • process_response(request, response):在视图函数执行完以后执行,必须有响应HttpResponse

默认的中间件

有几个默认配置的中间件,主要功能如下

  • django.middleware.security.SecurityMiddleware:防止xss脚本过滤的安全改进
  • django.contrib.sessions.middleware.SessionMiddleware:开始session会话支持,可以使用session,不开启就不能session保存数据
  • django.contrib.messages.middleware.MessageMiddleware:提供cookie的功能支持
  • django.middleware.common.CommonMiddleware:用来重写URL。如果APPEND_SLASH为True,那么URL末尾没有斜杠或者没有找到对应匹配的时候,会自动添加末尾斜杠;PREPEND_WWW为True则会将没有www.开头的URL重定向到同样的www.开头的URL路由
  • django.middleware.csrf.CsrfViewMiddleware:跨站请求伪造就不说了
  • django.contrib.auth.middleware.AuthenticationMiddleware:收到request后,可以对user对象添加相应的HttpRequest属性,表示当前用户身份呢

django中的csrf实现机制

  1. django第一次接收到客户端的请求时,随机生成一个随机数,保存在session中,同时放在cookie里面返回给客户端
  2. 在前端需要发送请求到后端时,需要将token加入到请求数据或者头部数据中
  3. 后端会校验传来的token和session里面的token是否一致
基于django的ajax发送请求给后端如何携带token

CSRF跨站请求伪造,是一种对网站的恶意利用、窃取网站用户信息制造恶意请求。
为了防护这类攻击,在用户提交表单的时候,表单中会自动加入一个叫csrfmiddlewaretoken的隐藏控件,后台也保存有这个控件的值,当请求到达后端时,服务器会检查这个值,如果匹配则进行后续处理,否则不处理。
原理:

  1. 用户访问网页的时候,django会在表单中添加一个叫csrfmiddlewaretoken的隐藏控件,并设置一个值,然后同时保存在后台,这是一个随机生成的
  2. 当用户提交表单的时候,服务器会对比用户表单里的这个token和自己的token,判断是否合法
  3. 如果请求是从其他地方发过来的,那他不会知道这个token,因此后台会校验失败,请求就不会被处理

但是XSRF防护一般只适用于POST请求,不能防护GET,因为GET一般是制度性是访问网页资源,不会涉及资源的更新和修改等操作。
在前端的form表单中添加{% csrf_token %}即可

  1. 使用模板填充
data:{csrfmiddlewaretoken:'{{csrf_token}}'
}
  1. 在form中设置一个隐藏控件,如标签,填充内容为token,然后获取控件的值
  2. 从cookie中获取token,放入头部。headers:{ “X-CSRFtoken”

    面经 - OpenStack(Docker、Django、K8S、SDN)知识点相关推荐

    1. 云计算:OpenStack、Docker、K8S(Kubernetes容器编排工具)的演进史 | 附推荐阅读

      目录 引子 OpenStack 的诞生 OpenStack 是什么 Docker 的出现 K8S(Kubernetes) - 为 Docker 而生 推荐阅读 引子 作为一名程序员,设计程序架构.优化 ...

    2. 云计算openstack、kvm以及docker和k8s

      云计算openstack.kvm以及docker和k8s 云计算 概念 为什么需要云计算 云计算服务模式 云计算应用 OpenStack 简介 组件介绍 DNS解析过程 Docker 为什么有dock ...

    3. docker、k8s 简介

      2010年,几个搞IT的年轻人,在美国旧金山成立了一家名叫"dotCloud"的公司. 这家公司主要提供基于PaaS的云计算技术服务.具体来说,是和LXC有关的容器技术. LXC, ...

    4. 干货满满!10分钟看懂Docker和K8S(转)

      转载地址:https://my.oschina.net/jamesview/blog/2994112 2010年,几个搞IT的年轻人,在美国旧金山成立了一家名叫"dotCloud" ...

    5. 10分钟看懂Docker和K8S,docker k8s 区别(生动形象,清晰易懂)

      本文来源:鲜枣课堂 原创时间:2018年12月25日 查看docker和k8s的资料看到这篇文章,感觉讲的很好容易理解,整理到自己这里,当作记录,方便查阅 2010年,几个搞IT的年轻人,在美国旧金山 ...

    6. Linux操作系统学习笔记(三十)docker和k8s的恩怨情仇

      一. 简介   之前聊天发现很多小伙伴对docker和k8s了解甚少,所以决定分享一下在docker和k8s背后这些年容器发展的故事,谈不上以史为鉴,但是至少可以从中汲取经验教训,同时也能了解容器及容 ...

    7. 10分钟看懂Docker和K8S

      戳蓝字"CSDN云计算"关注我们哦! 2010年,几个搞IT的年轻人,在美国旧金山成立了一家名叫"dotCloud"的公司. 这家公司主要提供基于PaaS的云计 ...

    8. Docker和K8S

      干货满满!10分钟看懂Docker和K8S [摘自:https://my.oschina.net/jamesview/blog/2994112] 本文来源微信号:鲜枣课堂 2010年,几个搞IT的年轻 ...

    9. [转载]Docker和k8s的区别与介绍

      转载至:https://www.cnblogs.com/misswangxing/p/10669444.html 2010年,几个搞IT的年轻人,在美国旧金山成立了一家名叫"dotCloud ...

    10. 已解决:centos 7.x系统自带的3.10.x内核存在一些bugs,导致运行docker、k8s不稳定,需要升级内核解决此问题。

      1.问题描述 Docker 要求 CentOS 系统的内核版本高于 3.10,因为centos 7.x系统自带的3.10.x内核存在一些bugs,导致运行docker.k8s不稳定. 2.问题分析 升 ...

    最新文章

    1. mysql主从复制的简单配置
    2. oracle数据库日期时间参数大全(一)
    3. 网站推广——网站推广专员浅析企业网站验收和交付要注意哪些问题
    4. 时间序列模型(ARIMA模型)
    5. Go2Shell 已无法使用
    6. matlab2c使用c++实现matlab函数系列教程-conj函数
    7. runC爆严重漏洞影响Kubernetes、Docker,阿里云修复runC漏洞的公告
    8. glibc中malloc源码分析
    9. 马拉车java_算法-Manacher算法 / 马拉车算法(Java实现)
    10. 阿里云盘 Mac客户端(附福利兑换码)
    11. [存档]使用CxServer的7个战略原因
    12. 常用Alink协议总结
    13. 推荐的四款产品原型设计工具
    14. 搭建在线视频网站,怎么弄?
    15. win10如何新增ip地址
    16. 双线机房托管双线双IP20M独享带宽4500/年 欢迎咨询Q1763371
    17. BSN-DDC 基础网络关键知识点(三)接入DDC网络
    18. 度数换算_度数的换算
    19. 常见颜色RGB值,有图。
    20. h5 html页面百度定位当前位置不准

    热门文章

    1. sketch up在线查看_使用Sketch Viewer在线查看和共享您的草图样机
    2. win10键盘win键失效了
    3. nginx反向代理和正向代理的区别
    4. 土石坝渗流分析的目的
    5. 谷歌向公众开放Fuchsia操作系统,华为鸿蒙与之对标
    6. 制造业ERP系统如何解决每个企业都存在的管理困境
    7. leaflet-离线地图
    8. Jsp+Servlet基于B2C的网上拍卖系统_秒杀与竞价
    9. 手机闪存速度排行_手机闪存读写速度对比?一加不如iQOO,华为最强,默秒全...
    10. 力科示波器上位机软件NS-Scope功能介绍