docker底層技術概覽

docker解決了雲計算環境難於分發而且管理複雜,而用KVM、Xen等虛擬化又浪費系統資源的問題。Docker最初是基於lxc構建了容器引擎,爲了提供跨平臺支持,後又專門開發了libcontainer來抽象容器引擎。但不管是libcontainer仍是lxc,其底層所依賴的內核特性都是相同的。咱們來看看docker都使用了技術來實現容器引擎的。html

命名空間

Docker使用了pid、network、ipc、美mnt、uts等命名空間來隔離網絡、文件系統、進程等資源。注意,因爲Linux並非namespace了全部東西(如cgroups、/sys、SELinux、/dev/sd*、內核模塊等),僅靠這幾個namespace是沒法實現像KVM那樣的徹底資源隔離的。linux

  • 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將其限制爲只讀的:git

. /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九大子系統的資源,如下是每一個子系統的詳細說明:github

  • 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

...
  ├─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
...

特權模式下的容器:centos

└─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-dm\x2d0.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權重,能夠在容器啓動後修改,如session

[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,1app

# 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命令。

參考文檔

相關文章
相關標籤/搜索