LXD分析
1. 介绍
lxd是基于lxc构筑的容器管理进程,提供镜像,网络,存储,以及容器的能力,对外暴漏restfull API。其与docker的区别是docker更切近与app container,以应用为中心构筑,lxd属于system container,以资源为中心构筑,其使用方式也是切近与vm,而与vm的区别,请自行搜索容器与vm差异。lxd由ubuntu开源,意在提供资源视角的容器,不颠覆传统的资源的运维管理方式。其配套由openstack的nova-lxd driver 可与openstack轻松集成,管理vm方式管理容器。

Lxd项目home:https://linuxcontainers.org/lxd/,目标是融入到openstack体系被管理,像虚拟机一样被管理使用。github上1749颗星,本人研究分析的时候版本为2.0.9(一年前).如下分析的也是2.0.9版本的时期。当前已经是3.1版本了。
题外话:容器标准OCI已出,当前业界有新出虚拟化容器,Hyper container。其路线大有可能是基于OCI标准的容器既可以面向CloudNative应用的K8S路线,同时也可以面向虚拟化容器的OpenStack路线,而OCI标准下已经存在RunC(Docker重构后的容器引擎部分,基于原有的libcontainer),也存在HyperContainer的容器引擎RunV。而lxd的存在个人觉得似乎没太大价值了(从目前看),无非就是No vendor lock in下的容器引擎的利益格局之争。lxd主导方是Ubuntu。
2. 架构
Lxd由两部分组成:lxd damon守护进程与lxd client。lxd client提供linux下的cli操作指令,其内部调用lxd daemon提供的restfull接口实现。关于lxd的代码架构基本如下:

基本包含四部分:进程启动初始化init,容器适配,存储适配,内存数据库,api。Container-adapter默认是适配lxc实现的,从接口定义看可能会扩展适配其它容器接口(libcontainer),当前并未解耦,总体代码量大约3.5W左右,实现语言go。
3. 使用
lxd目标是融入到openstack体系被管理,像虚拟机一样被管理使用。从如下图可知,并非走的是libvirt-lxc路线,而是nova-compute这一层直接走lxd。其提供了nova-lxd的plugin。

pylxd是封装的lxd restfull api的三方python库。nova-lxd是基于nova-compute定义的标准南向接口的具体插件实现。对于lxd容器的创建流程如下:实际上因为lxd的restfull接口和nova的compute插件接口未必完全能完整映射(也就是lxd的能力并未完整暴漏到nova这一层),所以会涉及部分能力不能再openstack测体现(就如同hypervisor与libvirt,通用性的折中)。譬如虚拟机下vcpu和容器化中cpu不等价,容器环境变量设置,linux kernel module加载控制,这些都未在nova-lxd中体现(虽然lxd侧开放了配置能力)
也有另一种简单场景下的使用模式:提供独立的console,并基于console手动发放和管理lxd容器。
4. 原理
以创建并启动lxd容器的过程进行分析,其运行流程如下:

主要的逻辑都在启动阶段,启动阶段的流程如下:

由上面可知,我们可以通过启动参数配置 对lxd的运行阶段进行全方位的跟踪,有cpu,内存,以及运行堆栈的信息,这对lxd的问题定位很有帮助。lxd daemon守护进程的启动重点在daemon初始化过程中:

下面对红色部分详细展开解释:
1. 检测usernamespace
l 读取/proc/self/uid_map,如果该文件内容为:0 0 4294967295 表示将namespace内部从0开始的uid映射到外部从0开始的uid。如果符合此规则,返回false,否则返回true,false表示运行在特权模式。此变量为daemon的全局变量,在AppArmor support检测的时候会使用,关于Linux namespace介绍见后面namespace介绍篇
2. 检测Apparmor support。需呀满足如下的4点条件
l 环境变量LXD_SECURITY_APPARMOR不为false
l /sys/kernel/security/apparmor存在且是文件非目录
l 可执行程序:apparmor_parser存在
3. 检测AppArmoradmin support
l Apparmor support满足
l capability具有创建id=0的进程能力,且具有CAP_MAC_ADMIN的特权能力
l user namespace运行在特权模式(检测user namespace步骤)
4. 检测Apparmordisable
l 读取/proc/self/attr/current,内容如果不为空或者且不等于unconfined,说明lxd已经被apparmor进行了保护
l 如果是lxd已经被apparmor保护,则读取/sys/kernel/security/apparmor/features/domain/stack,/sys/kernel/security/apparmor/features/domain/version文件内容进行解析判断。
5. 检测CGroupsupport。检测如下CGroup子系统,如果某个不存在则不能对其进行限额类控制
l 检测/sys/fs/cgroup/blkio/目录存在
l 检测/sys/fs/cgroup/cpu/
l 检测/sys/fs/cgroup/cpuacct/
l 检测/sys/fs/cgroup/cpuset/
l 检测/sys/fs/cgroup/devices/
l 检测/sys/fs/cgroup/memory/
l 检测/sys/fs/cgroup/net_prio/
l 检测/sys/fs/cgroup/pids/
l 检测/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes
6. 设置容器运行目录:
l /var/lib/lxd/containers-------/var/lib/lxd是环境变量设置可配置
l /var/lib/lxd/下检测创建device,devlxd,images等目录
7. 读取uid,gid并校验
l {Isuid: true, Isgid: true, Nsid: 0, Hostid: 1000000, Maprange:1000000000}可知容器内映射是1000000+宿主机的userId,groupId方式。
8. 初始化storagedriver。默认是dir
l 查询storage_pool数据表
l 根据pool_namespace查询storage_pools_config获取config
l 判断pool_name 匹配zfs,lvm,dir,mock,brtfs,并调用各个storage进行初始化
9. 设置网络
l 查询network表,以及根据network查询networks_config表 生成network对象实例
l 每个network实例start操作。关于start如下:
l 如果/var/lib/lxd/networks/network名称/不存在则创建目录
l 如果network.config.bridge.driver==openvswitch,则执行ovs-vsctl add-br network名称命令,否则执行ip link addnetwork名称 type bridge命令
l 进行IPv6 bridge 配置
l 通过ip link del 进行cleaninterface工作
l 通过ip link add 设置MTU
l 通过 ip link set启动bridge
l 删除ipV4 IPTABLES规则
l flush ipv4 address:shared.RunCommand("ip","-4", "addr", "flush", "dev", n.name,"scope", "global")和shared.RunCommand("ip","-4", "route", "flush", "dev", n.name,"proto", "static")
l 配置ipV4防火墙规则
l ......
10. 设置/dev/lxd
l /var/lib/lxd/devlxd/sock.使用了unix domainsocket相关知识,详见:http://www.th7.cn/Program/go/201508/539156.shtml解释
11. 启动scheduler
l go协程执行,进行Linux内核与用户态空间通信,发现cpu,net,usb的热插拔发现,内核态通知到用户态,用户态lxd则进行对应的负载调整,
l 对于cpu Task进行重新平衡设置
l 对于net设置到lxc参数net_prio.ifpriomap,并通过ip link或者ovs-vsctl命令进行绑定,
l 对于usb,给每个容器创建或者删除
12. 初始化web server
l 启动web服务器,发布restfull api。api分为内部和外部两种
13. ready就绪
l 每间隔24小时清理过期镜像
l 根据配置参数启动周期性更新镜像任务
l 检查哪些属于autostart的当前为stop的容器,自动拉起来
l CPU Task进行重新平衡
从启动篇看具备如下能力:
1:支持apparmor
2:支持storage driver:为容器设置不同的文件系统
3:支持bridge和ovs两种网络方式
4:支持cpu,net,usb热插拔
5::支持GPU设备发现加载
6:支持代理服务器访问
从前面分析,lxd主要是通过lxc来完成容器的创建和运行的,而lxc的接口就是需要接收lxc.conf,因此简要概括下上述过程则为lxd接收业务传递的参数后一部分出于管理需要进行持久化以及控制逻辑外,其它就是适配lxc接口生成lxc.conf,并喂给lxc。关于lxc.conf详细属性参考:
http://manpages.ubuntu.com/manpages/yakkety/en/man5/lxc.container.conf.5.htm
本文对常用的部分属性做解释说明:

通过lxc.conf的了解可得如下能力:
l NUMA CPU亲和性
l NUMA MEM亲和性
l 容器对宿主机设备的访问黑白名单控制
l 容器的可使用的capabilities控制
l 设备的挂载控制
l 容器rootfs的自定义支持:可为块设备
l APPARMOR的支持
l SELINUX的支持
l 容器自定义Hooks的支持
l 容器自定义环境变量支持
l 容器cpu限额
l 容器内存限额
l 容器网卡限速
l 容器磁盘IO限速(取决于容器磁盘是否块设备)
l 由上述的能力其可知:GPU等设备实际也均可支持(lxc外层发现pci设备并通过lxc.mount挂载给容器,以及设置访问的黑白名单即可)
lxc.conf的配置示例:





