微服务核心技术总结
虚拟化技术将一台服务器虚拟出多个虚拟机来提供服务。
虚拟化技术包括计算虚拟化(服务器虚拟化)、存储虚拟化、网络虚拟化等。
在实际讲解虚拟化之前,我们需要先解释一下隔离技术,在隔离的基础上我们才能任意粒度、自由地分配资源。
隔离的作用
- 实现更细粒度的资源管理,比如将一个物理 CPU 的计算能力分给多台云主机,再将这台云主机卖给多个人。
- 控制故障的影响范围,一个 Tomcat 中可以运行多个 Web 服务器,一台 Web 服务器崩溃并不会令其他服务器一并崩溃。
隔离的分类
广义的隔离技术包括如下几类
- 硬件:虚拟机,如 KVM、Xen
- 操作系统:容器,如 LXC、Docker
- Web 服务器:如 Servlet 容器
- 依赖版本:虚拟环境
- 运行环境:语言虚拟机,如JVM
- 语言:DSL
前两种隔离是虚拟化技术的基础,主要用于物理资源的池化,进而弹性地分配给用户。
作为一个例子,下面是一个运行时服务器中各抽象层次所采取的隔离技术的示意图:
上述隔离技术探讨的是单机环境下的资源分配,在微服务中聚焦的是如何提供更好的服务,因此可以在以下几个方向实施数据隔离:
- 应用数据隔离
多个业务服务器使用不同的数据库服务器,虽然有隔离故障、提高数据安全等好处,但是也引入了分布式事务的问题。 - 租户隔离
提供统一的云服务,但是对不同的用户分组单独使用一些服务实例来提供服务,这样这些服务实例挂掉了也只会影响对应分组的用户,这样的用户分组称为租户。
对于单独的一个租户,有独立服务独立数据库、共享服务独立数据库和共享服务共享数据库三种方式,可以根据成本和安全性来考虑选择哪种方案。
对于业务服务器来说,更重要的是业务层面上的隔离:
- 业务层
平台与业务,业务与业务。 - Provider,即服务提供者
如订单服务和库存服务,使用上面提到的虚拟化来实现隔离,如果; - Consumer,即服务消费者
下游服务调用多个上游服务(upstream)的时候,如果不对上游服务做服务隔离,一个服务出现问题,就会导致下游服务不可用,这种情况下,可选的隔离方案可以是给每个上游服务都准备一个线程池,称为线程池隔离:
这种情况下,就算其中一个服务不可用导致线程池被迅速占满,下游服务仍可以根据预定义的降级方案来忽略这个服务。
另一种可行的隔离方式是信号量隔离,常见的框架包括 Hytrix、Sentinel、以及 Resilience4j。
网络虚拟化
我们暴露服务时使用的 IP 一般不是业务服务器网卡的真实 IP,而是另外配置的一个虚拟 IP,请求先被打到该 IP,然后由 LVS 等负载均衡技术来找到一个真实 IP。
虚拟网卡通过实现一个字符设备来支持物理层,从而使应用层和物理层就通过这个字符设备联系起来,从这个字符设备读出来的就是虚拟网卡发往物理层的字节流,写入字符设备的数据作为字节流被虚拟网卡接收。
虚拟网卡可以像网卡一样进行配置,常见的虚拟网卡有TUN/TAP和VEth。
虚拟网桥(Bridge)也是一种虚拟设备,用于将多块网卡(包括虚拟网卡)连接起来。
值得注意的是, Linux 中虚拟网桥是通用网络设备抽象的一种,能够绑定 IP 地址。因此在把网卡接入到网桥上后,网卡原来绑定的 IP 会失效,如果还要像原来那样收发数据,需要把该 IP 绑定到网桥上。
1 | # 添加网桥br0 |
1 | docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 |
由 Docker Daemon 创建的虚拟网卡 docker0 其实就是一个网桥,可以使用 ethtool 查看设备类型。
1 | $ ethtool -i docker0 |
根据官方文档,Docker 通过 docker0 网桥在内核层连通了其他的物理或虚拟网卡,从而将所有容器和宿主都放到同一个物理网络下。
veth780318a 是一个 VEth 设备,在容器启动后动态创建。每次启动一个容器的时候,Docker 会新建一对 VETH 设备,其中一个插在 docker0 上,另一个插在该容器里,然后从可用的地址段中选择一个空闲的 IP 地址分配给容器的 VEth,使用 docker0 的 IP 作为默认网关,从而实现宿主机和容器的双向数据通讯。
1 | $ ethtool -S veth780318a |
上边的 veth780318a@if8 的 peer_ifindex 为 9,和其对应的 VEth 设备的 peer_ifindex 为 8。
接下来可以进入容器查看是否存在对应的 peer_ifindex 为 9 的虚拟网卡(下面的是教程中的执行效果,我并没有执行成功,因为普通的容器镜像里面一般没有安装常用的网络工具)。
1 | root@4c04df175784:/# ip route show |
TUN/TAP(Tunnel)
TUN 工作在 OSI 第三层(network),实现了 IP 包的转发,相当于路由。
TAP 工作在 OSI 第二层(data link),实现了 Ethernet 帧的转发,相当于网桥。
操作系统通过 TUN/TAP 设备向绑定该设备的用户空间的程序发送数据,反之,用户空间的程序也可以像操作硬件网络设备那样通过 TUN/TAP 设备发送数据,然后 TUN/TAP 设备会向操作系统的网络栈 push(或 inject)数据包,从而模拟从外部接受数据的过程。
TUN 常用于 VPN,通过使用 TUN,VPN 能够在 IP 包被发出去之前将其进行加密。
TAP 常用于虚拟机,为虚拟机提供网卡。
1 | ip tuntap add mode tap # 创建tap |
VEth(Virtual Ethernet)
VEth 是成对出现的,它的作用是反转通讯数据的方向,当数据从网络栈发送到 VEth 的一端时,数据被传送到 VEth 的另外一端流出,然后放回网络栈,相当于把需要接受的数据转换成需要发送的数据,
常用于虚拟化中穿透 network namespace,把从一个 network namespace 发出的数据包转发到另一个 namespace。
1 | ip link add veth1 type veth peer name veth2 # 创建一对VEth,名为veth1和veth2 |
服务器虚拟化
插槽、内核和内核线程
虚拟机如何分配 CPU 数:vSphere 中 CPU 资源如何分配
Xen、KVM 与 LXC
TODO
服务器虚拟化的三种比较常见的技术。
Xen:
KVM:
LXC:
LXC 的使用方法
- 安装
1
2
3
4apt-get install lxc
lxc-checkconfig # 安装完成后, 用这个命令检查系统是否可以使用 lxc
# 我执行后没有发现missing的情况,如果没有挂载cgroup可能会出现“Cgroup namespace: CONFIG_CGROUP_NSmissing”的错误,可以挂载一个cgroup
mount -t cgroup cgroup /mnt/cgroup - 创建容器这样创建的容器默认在 /var/lib/lxc/test 中, 为了将容器创建在我们指定的位置, 可以写个简单的配置文件 lxc.conf, 里面只需要一句:
1
sudo lxc-create -n test -t debian # # 创建一个 debian 系统
然后重新创建容器:1
lxc.rootfs = /home/lxc/test
这样, 就把容器创建在了 /home/lxc/test 中了, /var/lib/lxc/test 中只有一个 config 文件(这个 config 文件可以作为 lxc-create 命令 -f 参数对应配置文件的参考)1
sudo lxc-create -n test -t debian -f /path/to/lxc.conf
- 启动容器
启动后就进行入了虚拟机的控制台了. (果然像传说一样, 几秒就启动完成了 ^_^)1
lxc-start -n test
- 停止容器
在主机中输入停止的命令.1
lxc-stop -n test
- 销毁容器
销毁之前, 可以通过 lxc-ls 来查看有几个容器1
2
3
4lxc-ls
test
lxc-destroy -n test
lxc-ls
使用示例(配置 python uliweb 开发环境)
尝试在容器配置一次开发环境, 然后通过复制容器, 形成多个虚拟机.
1 | # 主机中 |
主机中设置网桥, 虚拟机用桥接方式上网, 确保每个虚拟机有独立的 IP
1 | # 主机中 |
配置容器的网络(也是在主机中修改容器的配置文件)
1 | root@debian-113:/var/lib/lxc/test# cat /var/lib/lxc/test/config |
启动 Linux 容器, 进入虚拟机
1 | root@debian-113:/var/lib/lxc/test# lxc-start -n test |
启动 Web 服务后, 就可以在主机的浏览器中 通过 http://192.168.1.167:8000/ 来访问虚拟机中的 web 服务了.
最后, 复制一个新的容器, 也就是再重新生成一个上面的 python uliweb 开发环境
1 | # 在主机中 |
LXC
原理
通过 namespace 进行资源的隔离,Gust1 下的进程与 Guset2 下的进程是独立的,可以看作运行在两台物理机上一样。Contaniner 管理工具就是对 Guest 进行管理的(创建、销毁)。
下图是对 LXC 架构的介绍。
虚拟化管理平台
虚拟化虽然可以用于自由分配资源,但是在生产环境内如果要一台一台机器安装及配置并不现实,一般来说都会尝试使用一个管理平台来自动化、批量化虚拟机的管理,或者从零开始使用 Libvirt 这样的库来实现自己需要的功能。
如 Libvirt 库提供了一套用于管理虚拟机和其他虚拟化功能的 Linux API,它支持各种虚拟机监控程序,包括 Xen 和 KVM,以及 QEMU 和用于其他操作系统的一些虚拟产品。
容器管理平台
容器管理平台与其他虚拟化平台有所不同,因为和微服务关系密切,所以更关注高可用、弹性扩展等特性,如Docker Swam、Kubernetes。
参考
- 隔离
猿学~程序员必知的六种隔离技术
谈谈怎么做【服务隔离】 - 网络设备虚拟化
网络设备设备虚拟化
一文搞懂网络虚拟化
从 Bridge 到 OVS,探索虚拟交换机
虚拟网络设备
Linux 上的基础网络设备详解
Linux-虚拟网络设备-LinuxBridge
Linux-虚拟网络设备-tun/tap
Linux-虚拟网络设备-veth pair
Linux-虚拟网络设备-OpenvSwitch(持续更新) - 硬件虚拟化
硬件虚拟化技术浅析
Compare of Xen, KVM, LXC and Traditional VM - Libvirt
Libvirt - Xen
Xen - KVM
Kernel Virtual Machine(KVM)
KVM 源代码分析 1:基本工作原理
在 Centos6.5 上部署 kvm 虚拟化技术 - LXC
LXC 的介绍
LinuX Container(LXC)
Linux 容器的使用 - Docker
使用 NGINX 和 NGINX Plus 进行 Docker Swarm 负载均衡
DockerSwarm 提供的负载均衡运行于每个节点上(应该是 DockerService 中的某个 Job),提供有限的负载均衡服务(TCP 层负载均衡),因此引入 Nginx 是有道理的。
据说 Nginx Plus 可以实现服务弹性伸缩的功能,但还没试过。