zoukankan      html  css  js  c++  java
  • docker底层技术概览

    docker解决了云计算环境难于分发并且管理复杂,而用KVM、Xen等虚拟化又浪费系统资源的问题。Docker最初是基于lxc构建了容器引擎,为了提供跨平台支持,后又专门开发了libcontainer来抽象容器引擎。但无论是libcontainer还是lxc,其底层所依赖的内核特性都是相同的。我们来看看docker都使用了技术来实现容器引擎的。

    命名空间

    Docker使用了pid、network、ipc、美mnt、uts等命名空间来隔离网络、文件系统、进程等资源。注意,由于Linux并不是namespace了所有东西(如cgroups、/sys、SELinux、/dev/sd*、内核模块等),仅靠这几个namespace是无法实现像KVM那样的完全资源隔离的。

    • pid namespace:实现进程隔离,容器只能看到自己的进程,并且每个容器都有一个pid为1的父进程,kill掉该进程容器内的所有进程都会停止;
    • net namespace:实现网络隔离,每个容器都可以设置自己的interface、routers、iptables等;docker默认采用veth的方式将container中的虚拟网卡同host上的一个docker bridge: docker0连接在一起;
    • ipc namespace:container中进程交互还是采用linux常见的进程间交互方法(interprocess communication - IPC), 包括常见的信号量、消息队列和共享内存。然而同 VM 不同的是,container 的进程间交互实际上还是host上具有相同pid namespace中的进程间交互,因此需要在IPC资源申请时加入namespace信息 - 每个IPC资源有一个唯一的 32 位 ID;
    • mnt namespace:类似chroot,将一个进程放到一个特定的目录执行。mnt namespace允许不同namespace的进程看到的文件结构不同,这样每个 namespace 中的进程所看到的文件目录就被隔离开了。同chroot不同,每个namespace中的container在/proc/mounts的信息只包含所在namespace的mount point;
    • uts namspace:允许每个container拥有独立的hostname和domain name, 使其在网络上可以被视作一个独立的节点而非Host上的一个进程;
    • user namespace:每个container可以有不同的 user 和 group id, 也就是说可以在container内部用container内部的用户执行程序而非Host上的用户。

    对于容器所依赖的内核文件系统(这些都是non-namespaced),为了保证安全性,docker将其限制为只读的:

    . /sys
    . /proc/sys
    . /proc/sysrq-trigger
    . /proc/irq
    . /proc/bus
    

    cgroups机制

    cgroups 实现了对资源的配额和度量。 cgroups 的使用非常简单,提供类似文件的接口,在 /cgroup目录下新建一个文件夹即可新建一个group,在此文件夹中新建task文件,并将pid写入该文件,即可实现对该进程的资源控制。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方便使用。
    • ns 名称空间子系统。

    对于centos7来说,通过systemd-cgls来查看系统cgroups tree:

    ...
      ├─docker-b1f965f8e682e9d2ff9ed3039fca63c008810efd9c5e6d796344b0270d329a98.scope
      │ ├─18853 /usr/lib/systemd/systemd
      │ └─system.slice
      │   ├─keepalived.service
      │   │ ├─19307 /usr/sbin/keepalived -D -d -S 7
      │   │ └─19309 /usr/sbin/keepalived -D -d -S 7
      │   ├─haproxy.service
      │   │ ├─25195 /usr/sbin/haproxy-systemd-wrapper -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
      │   │ ├─25210 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
      │   │ └─25211 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
      │   ├─rsyslog.service
      │   │ └─23648 /usr/sbin/rsyslogd -n
      │   └─systemd-journald.service
      │     └─18990 /usr/lib/systemd/systemd-journald
    ...
    
    

    特权模式下的容器:

    └─system.slice
      ├─NetworkManager-dispatcher.service
      │ └─2580 /usr/libexec/nm-dispatcher.action
      ├─var-lib-docker-devicemapper-mnt-a80a5f851d00842414d5fb03866c19424df1f2afb47a04f41dd056b54c4df7ac.mount
      ├─docker-a80a5f851d00842414d5fb03866c19424df1f2afb47a04f41dd056b54c4df7ac.scope
      │ ├─2558 /usr/sbin/init
      │ └─system.slice
      │   ├─systemd-journald.service
      │   │ └─2605 /usr/lib/systemd/systemd-journald
      │   ├─dbus.socket
      │   ├─dbus.service
      │   ├─system-getty.slice
      │   ├─rc-local.service
      │   ├─systemd-user-sessions.service
      │   ├─dev-dmx2d0.swap
      │   ├─etc-yum.repos.d.mount
      │   ├─etc-hosts.mount
      │   ├─etc-hostname.mount
      │   ├─etc-resolv.conf.mount
      │   └─-.mount
    

    cgroups配置方法:

    (1) cpu相对权重:docker run -it --rm -c 512,如果未设置,默认为1024

    # cat /sys/fs/cgroup/cpu/system.slice/docker-a80a5f851d00842414d5fb03866c19424df1f2afb47a04f41dd056b54c4df7ac.scope/cpu.shares
    1024
    

    如果在容器开启的时候没有设置cpu权重,可以在容器启动后修改,如

    [root@fei ~]# systemctl set-property --runtime docker-a80a5f851d00842414d5fb03866c19424df1f2afb47a04f41dd056b54c4df7ac.scope CPUShares=128
    [root@fei ~]# cat /sys/fs/cgroup/cpu/system.slice/docker-a80a5f851d00842414d5fb03866c19424df1f2afb47a04f41dd056b54c4df7ac.scope/cpu.shares
    128
    

    (2) 设置cpu pin:docker run -it --rm --cpuset=0,1

    # cat /sys/fs/cgroup/cpuset/system.slice/docker-b0848aa49a03b8541bb698c1544b6c411c584dce5e86831f84803228e93e61d4.scope/cpuset.cpus
    0-1
    

    (3) 内存限制: docker run -it --rm -m 128m,默认swap为mem的两倍

    [root@fei ~]# cat /sys/fs/cgroup/memory/system.slice/docker-c9fed54afc8986be888b231b984be9c1a2a533c739f7a5458a56882fb13b4b93.scope/memory.limit_in_bytes
    134217728
    [root@fei ~]# cat /sys/fs/cgroup/memory/system.slice/docker-c9fed54afc8986be888b231b984be9c1a2a533c739f7a5458a56882fb13b4b93.scope/memory.memsw.limit_in_bytes
    268435456
    

    如果不设置-m 128m,则默认容器内存是不设限的

    [root@fei ~]# cat /sys/fs/cgroup/memory/system.slice/docker-641cdebd22b55f2656a560cd250e661ab181dcf2f5c5b78dc306df7ce62231f2.scope/memory.limit_in_bytes
    9223372036854775807
    

    (4) 磁盘IO限制,docker本身默认没有做磁盘io的限制,不过我们可以通过直接操作cgroups来实现

    # 磁盘写
    [root@fei ~]# cid=641cdebd22b5
    [root@fei ~]# nsenter --target $(docker inspect -f '{{ .State.Pid }}' $cid) --mount --uts --ipc --net --pid mount | head -1
    /dev/mapper/docker-253:1-138011042-641cdebd22b55f2656a560cd250e661ab181dcf2f5c5b78dc306df7ce62231f2 on / type ext4 (rw,relatime,discard,stripe=16,data=ordered)
    [root@fei ~]# systemctl set-property --runtime docker-641cdebd22b55f2656a560cd250e661ab181dcf2f5c5b78dc306df7ce62231f2.scope "BlockIOWriteBandwidth=/dev/mapper/docker-253:1-138011042-641cdebd22b55f2656a560cd250e661ab181dcf2f5c5b78dc306df7ce62231f2 1M"
    # 磁盘读
    [root@fei ~]# systemctl set-property --runtime docker-641cdebd22b55f2656a560cd250e661ab181dcf2f5c5b78dc306df7ce62231f2.scope "BlockIOReadBandwidth =/dev/mapper/docker-253:1-138011042-641cdebd22b55f2656a560cd250e661ab181dcf2f5c5b78dc306df7ce62231f2 1M"
    
    

    (5) 磁盘大小,docker容器默认都会分配10GB的空间,如果想改变这个值,需要修改docker服务启动参数,并重启docker服务:docker -d --storage-opt dm.basesize=5G。其他磁盘相关的配置可以参考https://github.com/docker/docker/tree/master/daemon/graphdriver/devmapper

    Capability机制

    Linux把原来和超级用户相关的高级权限划分成为不同的单元,称为Capability,这样就可以独立对特定的Capability进行使能或禁止。通常来讲,不合理的禁止Capability,会导致应用崩溃。

    Docker默认为容器删除了以下capability:

    CAP_SETPCAP Modify process capabilities
    CAP_SYS_MODULE  Insert/Remove kernel modules
    CAP_SYS_RAWIO   Modify Kernel Memory
    CAP_SYS_PACCT   Configure process accounting
    CAP_SYS_NICE    Modify Priority of processes
    CAP_SYS_RESOURCE    Override Resource Limits
    CAP_SYS_TIME    Modify the system clock
    CAP_SYS_TTY_CONFIG  Configure tty devices
    CAP_AUDIT_WRITE Write the audit log
    CAP_AUDIT_CONTROL   Configure Audit Subsystem
    CAP_MAC_OVERRIDE    Ignore Kernel MAC Policy
    CAP_MAC_ADMIN   Configure MAC Configuration
    CAP_SYSLOG  Modify Kernel printk behavior
    CAP_NET_ADMIN   Configure the network
    CAP_SYS_ADMIN   Catch all
    

    如果确实需要这些capability,可以通过--cap-add or --cap-drop添加或删除,如docker run --cap-add all --cap-drop sys-admin -ti rhel7 /bin/sh

    SELinux

    SELinux是一个标签系统,进程有标签,每个文件、目录、系统对象都有标签。SELinux通过撰写标签进程和标签对象之间访问规则来进行安全保护。

    Union FS

    对于这种叠加的文件系统,有一个很好的实现是AUFS,在Ubuntu比较新的发行版里都是自带的,这个可以做到以文件为粒度的copy-on-write,为海量的container的瞬间启动,提供了技术支持,也会持续部署提供了帮助(注意,centos7系统是基于devicemapper来实现类似的功能的)。

    AUFS支持为每一个成员目录(类似Git Branch)设定readonly、readwrite 和 whiteout-able 权限, 同时 AUFS 里有一个类似分层的概念, 对 readonly 权限的 branch 可以逻辑上进行修改(增量地, 不影响 readonly 部分的)。通常 Union FS 有两个用途, 一方面可以实现不借助 LVM、RAID 将多个disk挂到同一个目录下, 另一个更常用的就是将一个 readonly 的 branch 和一个 writeable 的 branch 联合在一起,Live CD正是基于此方法可以允许在 OS image 不变的基础上允许用户在其上进行一些写操作。Docker 在 AUFS 上构建的 container image 也正是如此。

    IPTABLES

    主要用来做端口映射以及NAT转换,端口映射已经集成到Docker命令中,但NAT转换需要手动执行iptables命令。

    参考文档

     
  • 相关阅读:
    JavaScript中的闭包
    SQL 备忘
    SqlServer 2005 升级至SP2过程中出现"身份验证"无法通过的问题
    unable to start debugging on the web server iis does not list an application that matches the launched url
    Freebsd 编译内核
    Freebsd 6.2中关于无线网络的设定
    【Oracle】ORA01219
    【Linux】Windows到Linux的文件复制
    【Web】jar命令行生成jar包
    【Linux】CIFS挂载Windows共享
  • 原文地址:https://www.cnblogs.com/feisky/p/4105739.html
Copyright © 2011-2022 走看看