[NFV] LXD
近期接触到了 NFV (Network Function Virtulization),简单来说就是通过虚拟化技术,在服务器、云主机上构建虚拟的网络设备(交换机、路由器等),从灵活性、可扩展性、维护成本这些方面,是要优于专有网络硬件设备的。
NFV 主要依托KVM、LXD等虚拟化技术,在 VM 中利用 Quagga、Open vSwitch 等实现相应的功能,当然,基于容器 (Docker) 或者公有云私有云 (如 OpenStack 等) 的应用也是可以的。
本文主要内容:LXD,使用 LXD 构建虚拟路由器。
LXD
lxd 其实是 lxc 迭代的版本,我们都知道 docker 技术最初是基于 lxc 实现的,这些技术的共同点,其实都基于 Linux 内核功能实现,如 cgroups
namespaces(ipc, network, user, pid, mount)
。
Linux Cgroup
cgroups
为Linux kernel
功能,用于在系统中对进程组进行统一的资源监控和限制 (如 CPU 时间、系统内存、网络带宽等或者这些资源的组合)Linux Namespace
namespace
是Linux kernel
用来隔离内核资源的方式,同样是以组来管理进程,与上述区别cgroup
在于namespace
更多是对系统资源的一种封装隔离,使得处于不同namespace
的进程拥有独立的全局系统资源。
上述三者通过创建非特权容器,来实现像 selinux
这样的功能,即最大限度地减小进程可访问的资源 (也被称为最小权限原则) 。就我个人理解,容器也好虚拟机也好,核心的意义应该是隔离,至于镜像、接口甚至编排,都是为了让整个系统更加易用。
Privileged & Unprivileged
通常我们认为容器可以分为两种类型,对于 Linux 内核而言并不存在容器的概念,只是向用户空间提供相应的借口,所谓的特权容器 (Privileged Container)是指容器内部的 root 用户同样也是容器之外,主机之内的 root 用户 (UID 0);相反,非特权容器的
UID 0
映射到容器之外并不会是 主机的UID 0
。显而易见我们应该尽量避免使用特权容器,但是不管是 Docker 还是 Kubernetes 中都运行着大量的特权容器,为了易用性在安全性上有些妥协,不过通过鉴权、RBAC等一定程度上保证了容器不会被恶意侵入。
LSMs
Linux Security Module,顾名思义。
Linux Capabilities
早期 Unix/Linux 的权限控制比较粗糙,只有超级用户/普通用户的概念,有一个概念 SUID (Set User ID on execution) ,如将文件
/bin/passwd
设置了 SUID 的 flag,在普通用户运行passwd
命令时,将会以passwd
所属者,即 root 用户的身份运行Capabilities 机制细化了权限的粒度,在进行特权操作的时候,不再是使用 SUID 以 root 的身份执行操作,而是通过 EUID 判断是否为 root 身份,检查该特权操作所对应的 Capabilities,并以此为依据决定是否执行操作。
chroot
在 linux 系统中系统默认的目录结构都是以
/
开始的,chroot 可以将制定的位置作为系统目录结构的跟/
。通过 chroot 可以构建新根,与原系统的目录结构隔离开,限制用户访问一些特定的文件;在引导 Linux 系统启动和紧急救助上也能提供一定的便利。
LXD 与 LXC
lxc: 主要为 Linux 用户空间提供内核管理接口,包括
Kernel namespaces
,Apparmor
(访问权限控制) 和SELinux
配置, chroot 及其他内核能力。lxd: 作为一个容器的 hypervisor ,由
daemon
(lxd) ,CLI
(lxc,注意不是前面那个 lxc) 等组成,相较于 lxc 更加灵活易用,引入了包括配置文件、镜像库、多种存储后端、集群模式、丰富的设备管理功能。
Docker
lxc/lxd 提供完整的虚拟机容器,而 docker 更多是应用级别的容器,docker 引擎用一个独立的文件系统来包裹应用,而不是 userspace
,更多的还是希望把容器当作一个单独的进程来用。Docker 曾使用 lxc 用来在底层与内核通讯,现在使用的是自研的 libcontainer 。
安装 LXD
1 | $ sudo snap install lxd |
初始化:
1 | $ sudo lxd init |
由于我们需要自定义网络,所以注意初始化中的以下选项:
1 | Would you like LXD to be available over the network? (yes/no) [default=no]: yes |
用户组:
1 | $ sudo usermod -a -G lxd <username> |
创建容器
创建容器需要两样东西:镜像与模版。
镜像,顾名思义:
1 | $ sudo lxc remote add tuna-images https://mirrors.tuna.tsinghua.edu.cn/lxc-images/ --protocol=simplestreams --public |
模版中包括了对容器的一些初始化配置,如有需要,可以修改模版:
1 | $ sudo lxc profile edit default |
Optional: 比如取消容器自动创建网卡,可以将默认网卡 etho0
部分删除:
1 | config: {} |
注意:
此处移除网卡会导致基于该模版建立的容器无法联网,需要重新建立与
lxcbr0
的网络连接,通过虚拟网桥lxcbr0
以NAT
形式共享主机网络。
对模版的更多配置详见 Instance configuration .
接下来创建容器:
1 | $ sudo lxc init ubuntu/16.04 R1 -p default |
并启动容器:
1 | $ sudo lxc start R1 |
虚拟路由器
Quagga 基于 Zebra 实现,支持RIP、OSPF、BGP等路由协议的动态路由软件,现在我们要做的,就是将 Quagga 安装到刚刚运行起来的容器中。
下载 Quagga
下载 quagga-0.99.19.tar.gz 到宿主机硬盘:
1 | # 向容器发送文件到根目录 |
编译安装
在 Quagga 路径下,执行编译指令,如果有缺失依赖,逐项安装即可:
1 | $ make |
配置 Quagga
检查文件/etc/services ,是否有如下内容,没有的要添加。
1 | zebrasrv 2600/tcp # zebra service |
在quagga文件夹下 $ cp /usr/local/etc/zebra.conf.sample zebra.conf
启动 Quagga
进入 Quagga 配置文件所在路径中,(Quagga 相关执行文件在 /usr/local/sbin
中)
1 | $ cd /usr/local/etc |
注意此处可能会碰到权限问题,需要将当前用户加入到 quagga
用户组中。
通过 $ ps aux | grep zebra
确定 zebra 进程已经正常运行。
1 | # 查看启动密码 |
构建路由镜像
将容器打包成镜像:
1 | $ sudo lxc stop R1 |
通过 $ sudo lxc list
可以查看全部镜像。