内容简介:docker 资源限制以及应用的总结
一、 Docker(linux container) 所依赖的底层技术(隔离技术)
1 Namespace
用来做容器的隔离, 有了 namespace ,在 docker container 里头看来,就是一个完整的 linux 的世界。在 host 看来, container 里的进程,就是一个普通的 host 进程 , namespace 提供这种 pid 的映射和隔离效果, host 承载着 container ,就好比一个世外桃源。
namespace 包括: pid namespace 、 net namespace 、 ipc namespace 、 mnt namespace 、 uts namespace 、 user namespace
例如我们运行一个容器
查看容器的进程号
可以看到该容器的 pid 是 3894 ,在宿主的 /proc 目录下存在 3894 进程的目录
通过 kill 可以结束该容器
查看 /proc/[pid]/ns 文件
从 3.8 版本的内核开始,用户就可以在 /proc/[pid]/ns 文件下看到指向不同 namespace 号的文件,效果如下所示,形如 [4026531839] 者即为 namespace 号。
我们运行一个容器并获取容器的 pid
获取容器的 pid
# ls -l /proc/ pid /ns << pid 表示应用 容器 的 PID
如果两个进程指向的 namespace 编号相同,就说明他们在同一个 namespace 下,否则则在不同 namespace 里面。
例如我们再创建一个容器,网络模式为 container ( 使用 --net=container:NAMEorID 指定 )
从上面可以看出两个容器的 net namespace 编号相同 , 说明他们在同一个 net namespace 下 ,共用一个网络。
Docker 使用了 pid 、 network 、 mnt 、 ipc 、 uts 等命名空间来隔离进程、网络、文件系统等资源。注意,由于 Linux 并不是 namespace 了所有东西(如 cgroups 、 /sys 、 SELinux 、 /dev/sd* 、内核模块等),仅靠这几个 namespace 是无法实现像 KVM 那样的完全资源隔离的。
pid namespace : PID namespace 隔离非常实用,它对进程 PID 重新标号,即两个不同 namespace 下的进程可以有同一个 PID ,实现进程隔离,容器只能看到自己的进程,并且每个容器都有一个 pid 为 1 的父进程, kill 掉该进程容器内的所有进程都会停止;
net namespace :实现网络隔离,每个容器都可以设置自己的 interface 、 routers 、 iptables 等; docker 默认采用 veth 的方式将 container 中的虚拟网卡同 host 上的一个 docker bridge: docker0 连接在一起;
ipc namespace : container 中进程交互还是采用 linux 常见的进程间交互方法 (interprocess communication - IPC) , 容器中进程间通信采用的方法包括常见的信号量、消息队列和共享内存。然而与虚拟机不同的是,容器内部进程间通信对宿主机来说,实际上是具有相同 PID namespace 中的进程间通信,在同一个 IPC namespace 下的进程彼此可见,而与其他的 IPC namespace 下的进程则互相不可见 。
mnt namespace : 通过隔离文件系统挂载点对隔离文件系统提供支持,不同 mnt namespace 中的文件结构发生变化也互不影响。你可以通过 /proc/[pid]/mounts 查看到所有挂载在当前 namespace 中的文件系统,还可以通过 /proc/[pid]/mountstats 看到 mount namespace 中文件设备的统计信息,包括挂载文件的名字、文件系统类型、挂载位置等等
注: 43234 是容器的进程号
uts namspace : UTS namespace 提供了主机名和域名的隔离,这样每个容器就可以拥有了独立的主机名和域名,在网络上可以被视作一个独立的节点而非宿主机上的一个进程。
user namespace :每个 container 可以有不同的 user 和 group id, 也就是说可以在 container 内部用 container 内部的用户执行程序而非 Host 上的用户。
对于容器所依赖的内核文件系统(这些都是 non-namespaced ),为了保证安全性, docker 将其限制为只读的,例如进入一个容器执行 mount 命令:
#mount
2 Cgroups
在前面了 解了 Docker 背后使用的资源隔离技术 namespace ,通过系统调用构建一个相对隔离的 shell 环境,也可以称之为一个简单的 “ 容器 ” 。 下面 我们则要开始讲解另一个强大的内核工具 ――cgroups 。他不仅可以限制被 namespace 隔离起来的资源,还可以为资源设置权重、计算使用量、操控进程启停等等。 所以 cgroups ( Control groups ) 实现了对资源的配额和度量 。
cgroups 是什么 ?
cgroups ( Control Groups )最初叫 Process Container ,由 Google 工程师( Paul Menage 和 Rohit Seth )于 2006 年提出,后来因为 Container 有多重含义容易引起误解,就在 2007 年更名为 Control Groups ,并被整合进 Linux 内核。顾名思义就是把进程放到一个组里面统一加以控制
groups 的作用
通俗的来说, cgroups 可以限制、记录、隔离进程组所使用的物理资源(包括: CPU 、 memory 、 IO 等),为容器实现虚拟化提供了基本保证,是构建 Docker 等一系列虚拟化管理 工具 的基石 。 Cgroups 提供了以下四大功能。
1 ) 资源限制( Resource Limitation ): cgroups 可以对进程组使用的资源总额进行限制。如设定应用运行时使用内存的上限,一旦超过这个配额就发出 OOM ( Out of Memory )。
2 ) 优先级分配( Prioritization ):通过分配的 CPU 时间片数量及硬盘 IO 带宽大小,实际上就相当于控制了进程运行的优先级。
3 ) 资源统计( Accounting ): cgroups 可以统计系统的资源使用量,如 CPU 使用时长、内存用量等等,这个功能非常适用于计费。
4 ) 进程控制( Control ): cgroups 可以对进程组执行挂起、恢复等操作。
下面就介绍 cgroup 如何做到内存, cpu 和 io 速率的隔离
本文用脚本运行示例进程,来验证 Cgroups 关于 cpu 、内存、 io 这三部分的隔离效果。
测试机器 环境
执行 mount 命令查看 cgroup 的挂载点
从上图可以看到 cgroup 挂载在 /sys/fs/cgroup 目录
groups 可以限制 blkio 、 cpu 、 cpuacct 、 cpuset 、 devices 、 freezer 、 memory 、 net_cls 、 ns 等系统的资源,以下是主要子系统的说明:
blkio 这个子系统设置限制每个块设备的输入输出控制。例如 : 磁盘,光盘以及 usb 等等。
cpu 这个子系统使用调度程序为 cgroup 任务提供 cpu 的访问。
cpuacct 产生 cgroup 任务的 cpu 资源报告。
cpuset 如果是多核心的 cpu ,这个子系统会为 cgroup 任务分配单独的 cpu 和内存。
devices 允许或拒绝 cgroup 任务对设备的访问。
freezer 暂停和恢复 cgroup 任务。
memory 设置每个 cgroup 的内存限制以及产生内存资源报告。
net_cls 标记每个网络包以供 cgroup 方便使用, 它通过使用等级识别符 (classid) 标记网络数据包,从而允许 Linux 流量控制程序( TC : Traffic Controller )识别从具体 cgroup 中生成的数据包。
ns :命名空间子系统
cgroups 管理进程 cpu 资源
我们先看一个限制 cpu 资源的例子:
跑一个耗 cpu 的脚本
运行一个容器,在容器内创建脚本并运行脚本,脚本内容:
将容器切换到后台运行
在宿主机上 top 可以看到这个脚本基本占了 90 % 多 的 cpu 资源
下面用 cgroups 控制这个进程的 cpu 资源
对于 centos7 来说,通过 systemd-cgls 来查看系统 cgroups tree :
#systemd-cgls
注: 4813 就是我们所运行的容器 pid
将 cpu.cfs_quota_us设为50000,相对于cpu.cfs_period_us的100000是50%
进入容器,再次执行脚本,打开宿主机的另一个终端执行 top 命令
然后 top 的实时统计数据如下, cpu 占用率将近 50% ,看来 cgroups 关于 cpu 的控制起了效果
CPU 资源控制
CPU 资源的控制也有两种策略,一种是完全公平调度( CFS : Completely Fair Scheduler )策略,提供了限额和按比例分配两种方式进行资源控制;另一种是实时调度( Real-Time Scheduler )策略,针对实时进程按周期分配固定的运行时间。配置时间都以微秒( μs )为单位,文件名中用 us 表示。
CFS 调度策略下的配置
按权重比例设定 CPU 的分配
docker 提供了 �Ccpu-shares 参数,在创建容器时指定容器所使用的 CPU 份额值。 例如 :
使用命令 docker run -tid �C - cpu-shares 100 镜像 ,创建容器,则最终生成的 cgroup 的 cpu 份额配置可以下面的文件中找到:
# cat /sys/fs/cgroup/cpu/ system.slice/ docker - < 容器的完整长 ID>/cpu.shares
cpu-shares 的值不能保证可以获得 1 个 vcpu 或者多少 GHz 的 CPU 资源,仅仅只是一个加权值。
该加权值是 一个整数(必须大于等于 2 )表示相对权重,最后除以权重总和算出相对比例,按比例分配 CPU 时间。
默认情况下,每个 docker 容器的 cpu 份额都是 1024 。单独一个容器的份额是没有意义的,只有在同时运行多个容器时,容器的 cpu 加权的效果才能体现出来。例如,两个容器 A 、 B 的 cpu 份额分别为 1000 和 500 ,在 cpu 进行时间片分配的时候,容器 A 比容器 B 多一倍的机会获得 CPU 的时间片 。 如 果 容器 A 的进程一直是空闲的,那么容器 B 是可以获取比容器 A 更多的 CPU 时间片的。极端情况下,比如说主机上只运行了一个容器,即使它的 cpu 份额只有 50 ,它也可以独占整个主机的 cpu 资源。
cgroups 只在容器分配的资源紧缺时,也就是说在需要对容器使用的资源进行限制时,才会生效。因此,无法单纯根据某个容器的 cpu 份额来确定有多少 cpu 资源分配给它,资源分配结果取决于同时运行的其他容器的 cpu 分配和容器中进程运行情况。
cpu-shares 演示案例:
先删除 docker 主机上运行的容器
Docker 通过 --cpu-shares 指定 CPU 份额
运行一个容器指定 cpu 份额为 1024
注:
--cpu-shares 指定 CPU 份额 ,默认就是 1024
--cpuset-cpus 可以绑定 CPU。例如,指定容器在 --cpuset-cpus 0 ,1 或--cpuset-cpus 0-3
--cpu是stress命令的选项表示产生n个进程每个进程都反复不停的计算随机数的平方根
stress命令是 linux 下的一个压力测试工具。
在 docker 宿主机上打开一个 terminal 执行 top
然后再启动一个容器, - -c pu-shares 为 512 。
查看 top 的现实结果
可以看到 container1的CPU占比为1024/(1024+512)=2/3,container2的CPU占比为512/(1024+512)=1/ 3
将 container1 的 cpu.shares 改为 512 ,
#echo “512” > /sys/fs/cgroup/cpu/ system.slice/ docker - < 容器的完整长 ID>/cpu.shares
可以看到两个容器的 CPU占比趋于平均
设定 CPU 使用周期使用时间上限
cgroups 里,可以用 cpu.cfs_period_us 和 cpu.cfs_quota_us 来限制该组中的所有进程在单位时间里可以使用的 cpu 时间。 cpu.cfs_period_us 就是时间周期,默认为 100000 ,即百毫秒。 cpu.cfs_quota_us 就是在这期间内可使用的 cpu 时间,默认 -1 ,即无限制。
cpu.cfs_period_us :设定时间周期 ( 单位为微秒( μs ) ) ,必须与 cfs_quota_us 配合使用。
cpu.cfs_quota_us :设定周期内最多可使用的时间 ( 单位为微秒( μs ) ) 。这里的配置指 task 对单个 cpu 的使用上限。
举个例子,如果容器进程需要每 1 秒使用单个 CPU 的 0.2 秒时间,可以将 cpu-period 设置为 1000000 (即 1 秒), cpu-quota 设置为 200000 ( 0.2 秒)。
当然,在多核情况下, 若 cfs_quota_us 是 cfs_period_us 的两倍,就表示在两个核上完全使用 CPU , 例如 如果允许容器进程需要完全占用两个 CPU ,则可以将 cpu-period 设置为 100000 (即 0.1 秒), cpu-quota 设置为 200000 ( 0.2 秒)。
使用示例:
使用命令 docker run 创建容器
在宿主机上执行 top
从上图可以看到 基本占了 100 % 的 cpu 资源
则最终生成的 cgroup 的 cpu 周期配置可以下面的 目录 中找到:
/sys/fs/cgroup/cpu/ system.slice/ docker - < 容器的完整长 ID>/
修改容器的 cpu.cfs_period_us 和 cpu.cfs_quota_us 值
执行 top 查看 cpu 资源
从上图可以看到 基本占了 50 % 的 cpu 资源
RT 调度策略下的配置 实时调度策略与公平调度策略中的按周期分配时间的方法类似,也是在周期内分配一个固定的运行时间。
cpu.rt_period_us :设定周期时间。
cpu.rt_runtime_us :设定周期中的运行时间。
cpuset - CPU 绑定
对多核 CPU 的服务器, docker 还可以控制容器运行限定使用哪些 cpu 内核和内存节点,即使用 �Ccpuset-cpus 和 �Ccpuset-mems 参数。对具有 NUMA 拓扑(具有多 CPU 、多内存节点)的服务器尤其有用,可以对需要高性能计算的容器进行性能最优的配置。如果服务器只有一个内存节点,则 �Ccpuset-mems 的配置基本上不会有明显效果
注:
现在的机器上都是有多个 CPU 和多个内存块的。以前我们都是将内存块看成是一大块内存,所有 CPU 到这个共享内存的访问消息是一样的。但是随着处理器的增加,共享内存可能会导致内存访问冲突越来越厉害,且如果内存访问达到瓶颈的时候,性能就不能随之增加。 NUMA ( Non-Uniform Memory Access )就是这样的环境下引入的一个模型。比如一台机器是有 2 个处理器,有 4 个内存块。我们将 1 个处理器和两个内存块合起来,称为一个 NUMA node ,这样这个机器就会有两个 NUMA node 。在物理分布上, NUMA node 的处理器和内存块的物理距离更小,因此访问也更快。比如这台机器会分左右两个处理器( cpu1, cpu2 ),在每个处理器两边放两个内存块 (memory1.1, memory1.2, memory2.1,memory2.2) ,这样 NUMA node1 的 cpu1 访问 memory1.1 和 memory1.2 就比访问 memory2.1 和 memory2.2 更快。所以使用 NUMA 的模式如果能尽量保证本 node 内的 CPU 只访问本 node 内的内存块,那这样的效率就是最高的。
使用示例:
表示创建的容器只能用 0 、 1 、 2 这三个内核。最终生成的 cgroup 的 cpu 内核配置如下:
cpuset.cpus :在这个文件中填写 cgroup 可使用的 CPU 编号,如 0-2,16 代表 0 、 1 、 2 和 16 这 4 个 CPU 。
cpuset.mems :与 CPU 类似,表示 cgroup 可使用的 memory node ,格式同上
通过 docker exec < 容器 ID> taskset -c -p 1( 容器内部第一个进程编号一般为 1) ,可以看到容器中进程与 CPU 内核的绑定关系,可以认为达到了绑定 CPU 内核的目的。
总结:
CPU 配额控制参数的混合使用
当上面这些参数中时, cpu-shares 控制只发生在容器竞争同一个内核的时间片时,如果通过 cpuset-cpus 指定容器 A 使用内核 0 ,容器 B 只是用内核 1 ,在主机上只有这两个容器使用对应内核的情况,它们各自占用全部的内核资源, cpu-shares 没有明显效果。
cpu-period 、 cpu-quota 这两个参数一般联合使用,在单核情况或者通过 cpuset-cpus 强制容器使用一个 cpu 内核的情况下,即使 cpu-quota 超过 cpu-period ,也不会使容器使用更多的 CPU 资源。
cpuset-cpus 、 cpuset-mems 只在多核、多内存节点上的服务器上有效,并且必须与实际的物理配置匹配,否则也无法达到资源控制的目的。
在系统具有多个 CPU 内核的情况下,需要通过 cpuset-cpus 为容器 CPU 内核才能比较方便地进行测试。
内存配额控制
和 CPU 控制一样, docker 也提供了若干参数来控制容器的内存使用配额,可以控制容器的 swap 大小、可用内存大小等各种内存方面的控制。主要有以下参数:
Docker 提供参数 -m, --memory="" 限制容器的内存使用量 , 如果不设置 -m ,则默认容器内存是不设限的 ,容器可以使用主机上的所有空闲内存
内存配额控制使用示例
设置容器的内存上限,参考命令如下所示
#docker run -dit --memory128m 镜像
默认情况下,除了 �Cmemory 指定的内存大小以外, docker 还为容器分配了同样大小的 swap 分区,也就是说,上面的命令创建出的容器实际上最多可以使用 256MB 内存,而不是 128MB 内存。如果需要自定义 swap 分区大小,则可以通过联合使用 �Cmemory�Cswap 参数来实现控制。
可以发现,使用 256MB 进行压力测试时,由于超过了内存上限( 128MB 内存 +128MB swap ),进程被 OOM ( out of memory ) 杀死 。
使用 250MB 进行压力测试时,进程可以正常运行 。
通过 docker stats 可以查看到容器的内存已经满负载了。
#docker stats test2
对上面的命令创建的容器,可以查看到在 cgroups 的配置文件中,查看到容器的内存大小为 128MB (128×1024×1024=134217728B) , 内存和 swap 加起来大小为 256MB (256×1024×1024=268435456B) 。
# cat /sys/fs/cgroup/memory/ system.slice/ docker - < 容器的完整 ID>/memory.limit_in_bytes
134217728
#cat /sys/fs/cgroup/memory/system.slice/docker-< 容器的完整 ID>/memory.memsw.limit_in_bytes
268435456
磁盘 IO 配额控制
主要包括以下参数:
-- device-read-bps :限制此设备上的读速度( bytes per second ),单位可以是 kb 、 mb 或者 gb 。 -- device-read-iops :通过每秒读 IO 次数来限制指定设备的读速度。
-- device-write-bps :限制此设备上的写速度( bytes per second ),单位可以是 kb 、 mb 或者 gb 。
-- device-write-iops :通过每秒写 IO 次数来限制指定设备的写速度。
-- blkio-weight :容器默认磁盘 IO 的加权值,有效值范围为 10-100 0 。
-- blkio-weight-device :针对特定设备的 IO 加权控制。其格式为 DEVICE_NAME:WEIGHT
磁盘 IO 配额控制示例
blkio-weight
使用下面的命令创建两个 �Cblkio-weight 值不同的容器:
在容器中同时执行下面的 dd 命令,进行测试
注: oflag=direct 规避掉文件系统的 cache ,把写请求直接封装成 io 指令发到硬盘
3 Chroot
如何在 container 里头,看到的文件系统,就是一个完整的 linux 系统,有 /etc 、 /lib 等,通过 chroot 实现
4 Veth
container 里,执行 ifconfig 可以看到 eth0 的网卡,如何通信呢?其实是在 host 上虚拟了一张网卡出来( veth73f7 ),跟 container 里的网卡做了桥接,所有从 container 出来的流量都要过 host 的虚拟网卡,进 container 的流量也是如此。
5 Union FS
对于这种叠加的文件系统,有一个很好的实现是 AUFS ,这个可以做到以文件为粒度的 copy-on-write ,为海量的 container 的瞬间启动。
6 Iptables, netfilter
主要用来做 ip 数据包的过滤,比如可以做 container 之间无法通信, container 可以无法访问 host 的网络,但是可以通过 host 的网卡访问外网等这样的网络策略
二、 学习 Docker 也有一段时间了,了解了 Docker 的基本实现原理,也知道了 Docker 的使用方法,这里对 Docker 的一些典型应用场景做一个总结
1 、 配置简化
这是 Docker 的主要使用场景。将应用的所有配置工作写入 Dockerfile 中,创建好镜像,以后就可以无限次使用这个镜像进行应用部署了。这大大简化了应用的部署,不需要为每次部署都进行繁琐的配置工作,实现了一次打包,多次部署。这大大加快了应用的开发效率,使得 程序员 可以快速搭建起开发测试环境,不用关注繁琐的配置工作,而是将所有精力都尽可能用到开发工作中去。
2 、 代码流水线管理
代码从开发环境到测试环境再到生产环境,需要经过很多次中间环节, Docker 给应用提供了一个从开发到上线均一致的环境,开发测试人员均只需关注应用的代码,使得代码的流水线变得非常简单,这样应用才能持续集成和发布。
3 、 快速部署
在虚拟机之前,引入新的硬件资源需要消耗几天的时间。 Docker 的虚拟化技术将这个时间降到了几分钟, Docker 只是创建一个容器进程而无需启动操作系统,这个过程只需要秒级的时间。
4 、 应用隔离
资源隔离对于提供共享 hosting 服务的公司是个强需求。如果使用 VM ,虽然隔离性非常彻底,但部署密度相对较低,会造成成本增加。
Docker 容器充分利用 linux 内核的 namespace 提供资源隔离功能。结合 cgroups ,可以方便的设置每个容器的资源配额。既能满足资源隔离的需求,又能方便的为不同级别的用户设置不同级别的配额限制。
5 、 服务器资源整合
正如通过 VM 来整合多个应用, Docker 隔离应用的能力使得 Docker 同样可以整合服务器资源。由于没有额外的操作系统的内存占用,以及能在多个实例之间共享没有使用的内存, Docker 可以比 VM 提供更好的服务器整合解决方案。
通常数据中心的资源利用率只有 30% ,通过使用 Docker 并进行有效的资源分配可以提高资源的利用率。
6 、 多版本混合部署
随着产品的不断更新换代,一台服务器上部署多个应用或者同一个应用的多个版本在企业内部非常常见。但一台服务器上部署同一个软件的多个版本,文件路径、端口等资源往往会发生冲突,造成多个版本无法共存的问题。
如果用 docker ,这个问题将非常简单。由于每个容器都有自己独立的文件系统,所以根本不存在文件路径冲突的问题 ; 对于端口冲突问题,只需要在启动容器时指定不同的端口映射即可解决问题。
7 、 版本升级回滚
一次升级,往往不仅仅是应用软件本身的升级,通过还会包含依赖项的升级。但新旧软件的依赖项很可能是不同的,甚至是有冲突的,所以在传统的环境下做回滚一般比较困难。
如果使用 docker ,我们只需要每次应用软件升级时制作一个新的 docker 镜像,升级时先停掉旧的容器,然后把新的容器启动。需要回滚时,把新的容器停掉,旧的启动即可完成回滚,整个过程各在秒级完成,非常方便。
8 、 内部开发环境
在容器技术出现之前,公司往往是通过为每个开发人员提供一台或者多台虚拟机来充当开发测试环境。开发测试环境一般负载较低,大量的系统资源都被浪费在虚拟机本身的进程上了。
Docker 容器没有任何 CPU 和内存上的额外开销,很适合用来提供公司内部的开发测试环境。而且由于 Docker 镜像可以很方便的在公司内部共享,这对开发环境的规范性也有极大的帮助。
9 、 PaaS
使用 Docker 搭建大规模集群,提供 PaaS 。这一应用是最有前景的一个了,目前已有很多创业公司在使用 Docker 做 PaaS 了,例如云雀云平台。用户只需提交代码,所有运维工作均由服务公司来做。而且对用户来说,整个应用部署上线是一键式的,非常方便。
10 、 云桌面
在每一个容器内部运行一个图形化桌面,用户通过 RDP 或者 VNC 协议连接到容器。该方案所提供的虚拟桌面相比于传统的基于硬件虚拟化的桌面方案更轻量级,运行速率大大提升。不过该方案仍处于实验阶段,不知是否可行。可以参考一下 Docker-desktop 方案。
以上所述就是小编给大家介绍的《docker 资源限制以及应用的总结》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- GET请求Referer限制绕过总结
- 详解nginx的请求限制(连接限制和请求限制)
- PHP利用PCRE回溯次数限制绕过某些安全限制
- nginx限制客户端请求数+iptables限制TCP连接和频率来防止DDOS
- 文件上传限制绕过
- 文件上传限制绕过技巧
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
郎咸平说:新经济颠覆了什么
郎咸平 / 东方出版社 / 2016-8 / 39.00元
正所谓“上帝欲其灭亡,必先令其疯狂”,在当下中国,“互联网+资本催化”的新经济引擎高速运转,大有碾压一切、颠覆一切之势。在新经济狂热之下,每个人都在全力以赴寻找“下一个风口”,幻想成为下一只飞起来的猪。 对此,一向以“危机论”著称的郎咸平教授再次发出盛世危言:新经济光环背后,危机已悄然而至!中国式O2O还能烧多久?P2P监管黑洞有多大?互联网造车为什么不靠谱?共享经济为什么徒有虚名?BAT为......一起来看看 《郎咸平说:新经济颠覆了什么》 这本书的介绍吧!