Docker从精通到入门(三)----libcontainer

### libcontainer版本1.2.0
### libcontainer源码可以在vendor中找到

一、libcontainer介绍
    1. Docker的本质离不开Linux内核的很多高级特性;

    2. Docker Daemon作为一个常驻进程,管理Client请求的同时,还管理所有的Docker容器;
       Linux操作系统内核态对容器的管理,需要为用户态的Daemon服务,
       如果在Daemon与Linux内核之间有一套完善的API接口,那么两者的衔接将变得尤为顺畅;
       libcontainer的出现标志着内核API的出现。
    
    3. libcontainer是一套实现容器的Go语言解决方案;
       libcontainer实现过程中使用了Linux内核特性 namespace与 cgroup;
       同时还采用了capability与文件权限控制等一系列技术;
       基于这些特性,除了创建容器之外,libcontainer还可以完成容器生命周期的任务。

    4. libcontainer为Docker容器提供了隔离、独立的环境;
        隔离的效果是容器内部和外部的隔离;
        libcontainer需要完成的工作正像容器内外一样,
        既需要在容器外做容器的配置与管理,同时还需要再容器内部完成相应的初始化工作。
    

二、libcontianer模块分析
    · Docker容器范畴内一切与Linux内核相关的技术实现,都可以认为是通过libcontainer来完成的;
      
    · libcontainer指定了所有与容器相关的配置信息:
        1. namespace;
        2. cgroup;
        3. 容器的网络栈配置信息;
        4. 容器的挂载点配置、设备信息;
        5. netlink (用于完成 内核态进程与 用户态进程的通信)
    

三、namespace
    · namespace是libcontainer中最重要的一个模块;
      在linux中创建进程一般会使用fork等系统调用;  (fork 复制一个子进程)
        /*  创建进程时,fork等系统调用完成子进程对父进程task_struct的复制,fork还可以传入和namespace的相关参数;
            而Linux操作系统通过exec来启动子进程的运行。 */
      回到容器的创建,由于容器的本质也是进程,所以在派生进程时,Docker Daemon传入了与namespace相关的flag参数,实现namespace的创建;
      确保容器被执行之后,也就是在进程在运行时达到namespae隔离的效果。
        以下是libcontainer需要的clone标志内核支持:
            func init() {
            	namespaceList = Namespaces{
	            	{Key: "NEWNS", Value: syscall.CLONE_NEWNS, File: "mnt"},
	            	{Key: "NEWUTS", Value: syscall.CLONE_NEWUTS, File: "uts"},
		            {Key: "NEWIPC", Value: syscall.CLONE_NEWIPC, File: "ipc"},
		            {Key: "NEWUSER", Value: syscall.CLONE_NEWUSER, File: "user"},
	            	{Key: "NEWPID", Value: syscall.CLONE_NEWPID, File: "pid"},
	            	{Key: "NEWNET", Value: syscall.CLONE_NEWNET, File: "net"},
	            }
            }
      虽然namespace中加入了用户命名空间( CLONE_NEWUSER),但是libcontainer由于一些特殊原因却并未将其完全实现;
      且网络命名空间( CLONE_NEWNET)的支持也要视用户对容器的需求而定,若用户指定容器的网络模式为host,则libcontainer不为容器创建网络命名空间。

      namespace的完全生效,并不仅仅依靠namespace的创建,同时还需要对每一个namespace进行初始化;
      如:  CLONE_NEWNS     (设置clone自己的命名空间)
            CLONE_NEWNET    (设置网络命名空间)
            CLONE_NEWUTS    (设置uts命名空间)
      需要初始化信息参数,其他命名空间采用默认值。

    · namespace的创建
        Docker Daemon一方面从自身所在的namespace创建新的namespace供给容器使用;
        另一方面在容器外部的namespace中,为容器分配所需的命名空间资源;
            func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Writer, console string, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error)
        
        1. 创建管道用于将外面的进程( Docker Daemon)跨namespace传递给容器;
            //  syncPipe, err := syncpipe.NewSyncPipe()
        2. 创建一个容器内部的可执行命令
            //  command := createCommand(container, console, rootfs, dataPath, os.Args[0], syncPipe.Child(), args)
        3. 启动该命令实现namespace的初始化
            //  if err := command.Start(); err != nil {...}
        4. 初始化设置Cgroup限制
            // cgroupRef, err := SetupCgroups(container, command.Process.Pid)
        5. 初始化net信息,再将net信息通过管道传递给容器

    · namespace初始化
        Docker Daemon有两种方法将资源配置信息传递到容器:
            1. 创建时的管道;2. docker daemon持久化到宿主机的container.json文件;
        容器中的第一个进程为dockerinit,dockerinit完成了容器自身namespace内部挂载资源、用户资源、网络资源的初始化工作;
            func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syncPipe *syncpipe.SyncPipe, args []string) (err error)
        
    · 总结:
        容器的整个生命周期中,namespace是最基础的一块;
        Docker容器的很多功能均在namespace基础上完成;
        隔离的内部环境创建完毕之后,内部环境的初始化才能进行。


四、cgroup
    · cgroup是Linux内核的一个特性,可以帮助用户对一组进程进行资源使用的控制、统计以及隔离;
      Docker利用对cgroup的支持,完成对Docker容器进程组的资源限制、统计以及隔离。
    
    · cgroup结构体:
        type Cgroup struct {
        	Name   string `json:"name,omitempty"`
        	Parent string `json:"parent,omitempty"` // name of parent cgroup or slice
            // 设备device
	        AllowAllDevices   bool              `json:"allow_all_devices,omitempty"` // If this is true allow access to any kind of device within the container.  If false, allow access only to devices explicitly listed in the allowed_devices list.
	        AllowedDevices    []*devices.Device `json:"allowed_devices,omitempty"`
            // 内存
            Memory            int64             `json:"memory,omitempty"`             // Memory limit (in bytes)
	        MemoryReservation int64             `json:"memory_reservation,omitempty"` // Memory reservation or soft_limit (in bytes)
        	MemorySwap        int64             `json:"memory_swap,omitempty"`        // Total memory usage (memory + swap); set `-1' to disable swap
	        // CPU
            CpuShares         int64             `json:"cpu_shares,omitempty"`         // CPU shares (relative weight vs. other containers)
	        CpuQuota          int64             `json:"cpu_quota,omitempty"`          // CPU hardcap limit (in usecs). Allowed cpu time in a given period.
        	CpuPeriod         int64             `json:"cpu_period,omitempty"`         // CPU period to be used for hardcapping (in usecs). 0 to use system default.
	        CpusetCpus        string            `json:"cpuset_cpus,omitempty"`        // CPU to use
        	// Freezer
            Freezer           FreezerState      `json:"freezer,omitempty"`            // set the freeze value for the process
	        // systemd
            Slice             string            `json:"slice,omitempty"`              // Parent slice to use for systemd
        }
    
    · cgroup分析:
        Docker对cgroup的支持主要有五点:
            1. 设备(device): 通过 AllowAllDevices和 AllowedDevices字段来控制容器可以访问的设备;
                              AllowAllDevices == true表示可以访问所有设备,否则只能访问 AllowedDevices中的。
            2. 内存(memory): 限制容器运行时的内存上限
            3. CPU:          限制容器对CPU的使用时间、频率和占比
            4. Freezer:      可以使容器挂起,节省CPU资源;
                              docker pause/unpause就是采用了cgroup的子系统;
                              容器进程组挂起,不意味着进程已经终止,
                              linux内核的角度来看,被挂起的进程仍然拥有完整的任务结构(task_struct),
                              但是Freezer保证了容器进程不会被CPU调度到。
            5. Slice:        属于有systemd方面的配置


五、网络
    · Docker容器的网络管理在底层都是借助libcontainer的network包完成的;
      network包定义了Docker容器的网络栈类型,Docker容器的网络模式有:
        1. bridge模式:     为容器配置虚拟网卡( veth)网络接口对,并配置loopback接口;
        2. host模式:        不为容器创建网络命名空间;
        3. other container模式:    不为容器创建网络命名空间,让容器与容器之间共享网络命名空间;
        4. none模式:       为容器创建网络命名空间,配置loopback接口。
    
    · libcontainer为了表示网络接口的数据接口,抽象出了network结构体:
        type Network struct {
        	// 设置网络类型
        	Type string `json:"type,omitempty"`
            // 代表容器网络命名空间
	        NsPath string `json:"ns_path,omitempty"`
            // 代表容器网络使用的网桥设备名
        	Bridge string `json:"bridge,omitempty"`
	        // veth(虚拟网卡)的前缀
	        VethPrefix string `json:"veth_prefix,omitempty"`
	        // 容器网络接口的IP地址和子网掩码
	        Address string `json:"address,omitempty"`
            // 用于设置网关地址,作为接口的默认地址
	        Gateway string `json:"gateway,omitempty"`
            // 如果创建了接口对,代表容器网络接口的MTU值
            // 特别是type == veth时
	        Mtu int `json:"mtu,omitempty"`
        }


六、挂载
    · Docker容器除了镜像提供的rootf,还会使用valume使得容器的文件系统可以共享主机的资源;
      除了volume之外,容器的配置文件也是通过挂载的方法,使容器可以访问mount之外的空间;(hostname、hosts、resolv.conf)

    · Mount的对象:
        type Mount struct {
	        Type        string `json:"type,omitempty"`
	        Source      string `json:"source,omitempty"`      // 源地址
	        Destination string `json:"destination,omitempty"` // 目的挂载地址
	        Writable    bool   `json:"writable,omitempty"`
	        Relabel     string `json:"relabel,omitempty"` // z表示可共享,Z表示不可共享
	        Private     bool   `json:"private,omitempty"`
        }
      对于Docker Daemon而言,记录Mount的源地址以及目的地址是很重要的;
      在此基础上还配置了一些属性配置,比如是否开启容器对Mount的写权限。


七、设备
    · 对于容器来说,使用Linux内核管辖范围内的设备,是一个非常基本的需求;
      libcontainer使用devices包来为Docker提供设备。   (特殊设备,存在于shell中)

    · libcontainer中设备的定义:
        type Device struct {
        	Type              rune        `json:"type,omitempty"`
        	Path              string      `json:"path,omitempty"`               // It is fine if this is an empty string in the case that you are using Wildcards
	        MajorNumber       int64       `json:"major_number,omitempty"`       // Use the wildcard constant for wildcards.
	        MinorNumber       int64       `json:"minor_number,omitempty"`       // Use the wildcard constant for wildcards.
        	CgroupPermissions string      `json:"cgroup_permissions,omitempty"` // Typically just "rwm"
	        FileMode          os.FileMode `json:"file_mode,omitempty"`          // The permission bits of the file's mode
        }
    
    · 默认情况下,会创建一些必备的设备:
        1. /dev/null:  黑洞文件,用于重定向丢弃数据或者获取null值
        2. /dev/zero:  用于覆盖信息或者填充空白文件
        3. /dev/tty:   用于
        4. /dev/urandom:随机数发生器,来源于设备噪声
        5. /dev/console
        6. /dev/full:  表示满文件,向里面写入时会报错
        ...


八、nsinit
    · nsinit只需要一个roofs以及一个容器的配置文件container.conf就能创建一个容器;

    · JSON文件container.json需要处于rootfs的根目录下,并且文件中含有的配置信息需要包括:
        容器的环境变量、网络、容器Capability等内容


九、其他模块
    · Netlink作为Linux的一套接口,提供进程间用户态与内核态之间的通信方式;

    · security模块负责容器的安全方面,可以配置容器的Capability;

    · Capability可以极大的限制用户进程的权限;
      但是仍然有很多不能覆盖的方面,Docker的namespace没有完全实现用户命名空间,
      因此Docker中的root和Linux的root时同一个用户;
      Docker在尽量让root权限降到很小的情况下,还支持SELinux和Apparmor为容器提供安全
    

十、总结
    · Docker的运行不能没有libcontainer,在libcontainer的基础之上,任何开发者都能再造类似的容器引擎;

    · libcontainer中核心模块就是namespace和cgroup;
        namespace主要提供了隔离性;
        cgroup提供了性能限制


十一、附录(docker容器进程的层级关系)
    1. 原始状态:
        systemd(1)─┬─NetworkManager(990)─┬─{NetworkManager}(995)
           │                     └─{NetworkManager}(1001)
           ├─VGAuthService(917)
           ├─agetty(1028)
           ├─auditd(881)───{auditd}(882)
           ├─chronyd(915)
           ├─containerd(1025)─┬─{containerd}(1263)
           │                  ├─{containerd}(1264)
           │                  ├─{containerd}(1265)
           │                  ├─{containerd}(1267)
           │                  ├─{containerd}(1268)
           │                  ├─{containerd}(1365)
           │                  ├─{containerd}(1366)
           │                  ├─{containerd}(1367)
           │                  └─{containerd}(1373)
           ├─crond(1020)
           ├─dbus-daemon(907)───{dbus-daemon}(938)
           ├─dockerd(3691)─┬─{dockerd}(3692)
           │               ├─{dockerd}(3693)
           │               ├─{dockerd}(3694)
           │               ├─{dockerd}(3695)
           │               ├─{dockerd}(3696)
           │               ├─{dockerd}(3697)
           │               ├─{dockerd}(3698)
           │               ├─{dockerd}(3699)
           │               ├─{dockerd}(3700)
           │               ├─{dockerd}(3701)
           │               └─{dockerd}(3702)
           ... 

    2. run一个tomcat容器
        systemd(1)─┬─NetworkManager(990)─┬─{NetworkManager}(995)
           │                     └─{NetworkManager}(1001)
           ├─VGAuthService(917)
           ├─agetty(1028)
           ├─auditd(881)───{auditd}(882)
           ├─chronyd(915,chrony)
           ├─containerd(1025)─┬─{containerd}(1263)
           │                  ├─{containerd}(1264)
           │                  ├─{containerd}(1265)
           │                  ├─{containerd}(1267)
           │                  ├─{containerd}(1268)
           │                  ├─{containerd}(1365)
           │                  ├─{containerd}(1366)
           │                  ├─{containerd}(1367)
           │                  └─{containerd}(1373)
           ├─containerd-shim(2775)─┬─java(2794)─┬─{java}(2828)
           │                       │            ├─{java}(2829)
           │                       │            ├─{java}(2830)
           │                       │            ├─{java}(2831)
           │                       │            ├─{java}(2832)
           │                       │            ├─{java}(2833)
           │                       │            ├─{java}(2834)
           │                       │            ├─{java}(2835)
           │                       │            ├─{java}(2836)
           │                       │            ├─{java}(2837)
           │                       │            ├─{java}(2838)
           │                       │            ├─{java}(2848)
           │                       │            ├─{java}(2849)
           │                       │            ├─{java}(2850)
           │                       │            ├─{java}(2851)
           │                       │            ├─{java}(2852)
           │                       │            ├─{java}(2853)
           │                       │            ├─{java}(2854)
           │                       │            ├─{java}(2855)
           │                       │            ├─{java}(2856)
           │                       │            ├─{java}(2857)
           │                       │            ├─{java}(2858)
           │                       │            ├─{java}(2859)
           │                       │            ├─{java}(2860)
           │                       │            ├─{java}(2861)
           │                       │            ├─{java}(2862)
           │                       │            ├─{java}(2863)
           │                       │            ├─{java}(2864)
           │                       │            ├─{java}(2865)
           │                       │            ├─{java}(2866)
           │                       │            ├─{java}(2867)
           │                       │            ├─{java}(2868)
           │                       │            ├─{java}(2869)
           │                       │            ├─{java}(2870)
           │                       │            ├─{java}(2871)
           │                       │            ├─{java}(2872)
           │                       │            ├─{java}(2873)
           │                       │            ├─{java}(2874)
           │                       │            └─{java}(2875)
           │                       ├─{containerd-shim}(2776)
           │                       ├─{containerd-shim}(2777)
           │                       ├─{containerd-shim}(2778)
           │                       ├─{containerd-shim}(2779)
           │                       ├─{containerd-shim}(2780)
           │                       ├─{containerd-shim}(2781)
           │                       ├─{containerd-shim}(2782)
           │                       ├─{containerd-shim}(2783)
           │                       ├─{containerd-shim}(2817)
           │                       └─{containerd-shim}(2818)
           ├─crond(1020)
           ├─dbus-daemon(907,dbus)───{dbus-daemon}(938)
           ├─dockerd(1374)─┬─docker-proxy(2754)─┬─{docker-proxy}(2755)
           │               │                    ├─{docker-proxy}(2756)
           │               │                    ├─{docker-proxy}(2757)
           │               │                    ├─{docker-proxy}(2758)
           │               │                    ├─{docker-proxy}(2759)
           │               │                    └─{docker-proxy}(2760)
           │               ├─docker-proxy(2761)─┬─{docker-proxy}(2762)
           │               │                    ├─{docker-proxy}(2763)
           │               │                    ├─{docker-proxy}(2764)
           │               │                    ├─{docker-proxy}(2765)
           │               │                    └─{docker-proxy}(2766)
           │               ├─{dockerd}(1439)
           │               ├─{dockerd}(1440)
           │               ├─{dockerd}(1441)
           │               ├─{dockerd}(1442)
           │               ├─{dockerd}(1443)
           │               ├─{dockerd}(1475)
           │               ├─{dockerd}(1497)
           │               └─{dockerd}(1498)
           ...

    3. 再run一个tomcat容器  
        systemd(1)─┬─NetworkManager(990)─┬─{NetworkManager}(995)
           │                     └─{NetworkManager}(1001)
           ├─VGAuthService(917)
           ├─agetty(1028)
           ├─auditd(881)───{auditd}(882)
           ├─chronyd(915,chrony)
           ├─containerd(1025)─┬─{containerd}(1263)
           │                  ├─{containerd}(1264)
           │                  ├─{containerd}(1265)
           │                  ├─{containerd}(1267)
           │                  ├─{containerd}(1268)
           │                  ├─{containerd}(1365)
           │                  ├─{containerd}(1366)
           │                  ├─{containerd}(1367)
           │                  └─{containerd}(1373)
           ├─containerd-shim(2775)─┬─java(2794)─┬─{java}(2828)
           │                       │            ├─{java}(2829)
           │                       │            ├─{java}(2830)
           │                       │            ├─{java}(2831)
           │                       │            ├─{java}(2832)
           │                       │            ├─{java}(2833)
           │                       │            ├─{java}(2834)
           │                       │            ├─{java}(2835)
           │                       │            ├─{java}(2836)
           │                       │            ├─{java}(2837)
           │                       │            ├─{java}(2838)
           │                       │            ├─{java}(2848)
           │                       │            ├─{java}(2849)
           │                       │            ├─{java}(2850)
           │                       │            ├─{java}(2851)
           │                       │            ├─{java}(2852)
           │                       │            ├─{java}(2853)
           │                       │            ├─{java}(2854)
           │                       │            ├─{java}(2855)
           │                       │            ├─{java}(2856)
           │                       │            ├─{java}(2857)
           │                       │            ├─{java}(2858)
           │                       │            ├─{java}(2859)
           │                       │            ├─{java}(2860)
           │                       │            ├─{java}(2861)
           │                       │            ├─{java}(2862)
           │                       │            ├─{java}(2863)
           │                       │            ├─{java}(2864)
           │                       │            ├─{java}(2865)
           │                       │            ├─{java}(2866)
           │                       │            ├─{java}(2867)
           │                       │            ├─{java}(2868)
           │                       │            ├─{java}(2869)
           │                       │            ├─{java}(2870)
           │                       │            ├─{java}(2871)
           │                       │            ├─{java}(2872)
           │                       │            ├─{java}(2873)
           │                       │            ├─{java}(2874)
           │                       │            └─{java}(2875)
           │                       ├─{containerd-shim}(2776)
           │                       ├─{containerd-shim}(2777)
           │                       ├─{containerd-shim}(2778)
           │                       ├─{containerd-shim}(2779)
           │                       ├─{containerd-shim}(2780)
           │                       ├─{containerd-shim}(2781)
           │                       ├─{containerd-shim}(2782)
           │                       ├─{containerd-shim}(2783)
           │                       ├─{containerd-shim}(2817)
           │                       └─{containerd-shim}(2818)
           ├─containerd-shim(3131)─┬─java(3150)─┬─{java}(3182)
           │                       │            ├─{java}(3183)
           │                       │            ├─{java}(3184)
           │                       │            ├─{java}(3185)
           │                       │            ├─{java}(3186)
           │                       │            ├─{java}(3187)
           │                       │            ├─{java}(3188)
           │                       │            ├─{java}(3189)
           │                       │            ├─{java}(3190)
           │                       │            ├─{java}(3191)
           │                       │            ├─{java}(3192)
           │                       │            ├─{java}(3193)
           │                       │            ├─{java}(3194)
           │                       │            ├─{java}(3195)
           │                       │            ├─{java}(3196)
           │                       │            ├─{java}(3197)
           │                       │            ├─{java}(3198)
           │                       │            ├─{java}(3199)
           │                       │            ├─{java}(3200)
           │                       │            ├─{java}(3201)
           │                       │            ├─{java}(3202)
           │                       │            ├─{java}(3203)
           │                       │            ├─{java}(3204)
           │                       │            ├─{java}(3205)
           │                       │            ├─{java}(3206)
           │                       │            ├─{java}(3207)
           │                       │            ├─{java}(3208)
           │                       │            ├─{java}(3209)
           │                       │            ├─{java}(3210)
           │                       │            ├─{java}(3211)
           │                       │            ├─{java}(3212)
           │                       │            ├─{java}(3213)
           │                       │            ├─{java}(3214)
           │                       │            ├─{java}(3215)
           │                       │            ├─{java}(3216)
           │                       │            ├─{java}(3217)
           │                       │            ├─{java}(3218)
           │                       │            ├─{java}(3219)
           │                       │            ├─{java}(3220)
           │                       │            ├─{java}(3221)
           │                       │            └─{java}(3222)
           │                       ├─{containerd-shim}(3132)
           │                       ├─{containerd-shim}(3133)
           │                       ├─{containerd-shim}(3134)
           │                       ├─{containerd-shim}(3135)
           │                       ├─{containerd-shim}(3136)
           │                       ├─{containerd-shim}(3137)
           │                       ├─{containerd-shim}(3138)
           │                       ├─{containerd-shim}(3139)
           │                       └─{containerd-shim}(3172)
           ├─crond(1020)
           ├─dbus-daemon(907,dbus)───{dbus-daemon}(938)
           ├─dockerd(1374)─┬─docker-proxy(2754)─┬─{docker-proxy}(2755)
           │               │                    ├─{docker-proxy}(2756)
           │               │                    ├─{docker-proxy}(2757)
           │               │                    ├─{docker-proxy}(2758)
           │               │                    ├─{docker-proxy}(2759)
           │               │                    └─{docker-proxy}(2760)
           │               ├─docker-proxy(2761)─┬─{docker-proxy}(2762)
           │               │                    ├─{docker-proxy}(2763)
           │               │                    ├─{docker-proxy}(2764)
           │               │                    ├─{docker-proxy}(2765)
           │               │                    └─{docker-proxy}(2766)
           │               ├─docker-proxy(3110)─┬─{docker-proxy}(3111)
           │               │                    ├─{docker-proxy}(3112)
           │               │                    ├─{docker-proxy}(3113)
           │               │                    ├─{docker-proxy}(3114)
           │               │                    ├─{docker-proxy}(3115)
           │               │                    ├─{docker-proxy}(3117)
           │               │                    └─{docker-proxy}(3118)
           │               ├─docker-proxy(3116)─┬─{docker-proxy}(3119)
           │               │                    ├─{docker-proxy}(3120)
           │               │                    ├─{docker-proxy}(3121)
           │               │                    ├─{docker-proxy}(3122)
           │               │                    └─{docker-proxy}(3123)
           │               ├─{dockerd}(1439)
           │               ├─{dockerd}(1440)
           │               ├─{dockerd}(1441)
           │               ├─{dockerd}(1442)
           │               ├─{dockerd}(1443)
           │               ├─{dockerd}(1475)
           │               ├─{dockerd}(1497)
           │               └─{dockerd}(1498)
           ... 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值