Docker內核知識


1.Docker的內核知識

Docker容器的本質是宿主機上的進程,通過namespace實現資源隔離,通過cgroups實現資源限制,通過寫時復制機制實現高效的文件操作。

1.1.namespace資源隔離

    Linux提供了6種namespace隔離的系統調用。

             

  Linux內核實現namespace的主要目的就是為了實現輕量級虛擬化(容器)服務。在同一個namespace下的進程可以感知彼此的變化,而對外界的進程一無所知。這樣就可以讓容器中的進程產生錯覺,仿佛自己置身於一個獨立的系統環境中,以此達到獨立和隔離的目的。

1.1.1.調用namespace的API

  namespace的API包括clone()setns()以及unshare(),還有/proc下的部分文件。為了確定隔離的到底是哪種namespace,在使用這些API時,通常需要指定以下六個常數的一個或多個,通過|(位或)操作來實現。你可能已經在上面的表格中注意到,這六個參數分別是CLONE_NEWIPCCLONE_NEWNSCLONE_NEWNETCLONE_NEWPID、 CLONE_NEWUSERCLONE_NEWUTS

  1.通過clone()創建新進程的同時創建namespace

  使用clone()來創建一個獨立namespace的進程是最常見做法,它的調用方式如下:

int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);

  clone()實際上是傳統UNIX系統調用fork()的一種更通用的實現方式,它可以通過flags來控制使用多少功能。一共有二十多種CLONE_*flag(標志位)參數用來控制clone進程的方方面面(如是否與父進程共享虛擬內存等等),下面外面逐一講解clone函數傳入的參數。

  • 參數child_func傳入子進程運行的程序主函數。
  • 參數child_stack傳入子進程使用的棧空間
  • 參數flags表示使用哪些CLONE_*標志位
  • 參數args則可用於傳入用戶參數

  2.查看/proc/[pid]/ns文件

  用戶就可以在/proc/[pid]/ns文件下看到指向不同namespace號的文件,效果如下所示,形如[4026531839]者即為namespace號。

  可以通過ps -ef查看容器內不同的進程,從而進入對應的ns中,會發現同一容器下,pid,mnt,net等編號相同。

  如果兩個進程指向的namespace編號相同,就說明他們在同一個namespace下,否則則在不同namespace里面。/proc/[pid]/ns的另外一個作用是,一旦文件被打開,只要打開的文件描述符(fd)存在,那么就算PID所屬的所有進程都已經結束,創建的namespace就會一直存在。

$ ls -l /proc/$$/ns         <<-- $$ 表示應用的PID
total 0
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 net -> net:[4026531956]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 pid -> pid:[4026531836]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 user->user:[4026531837]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 uts -> uts:[4026531838]

1.1.2.UTS

  UTS提供了主機名和域名的隔離,這樣每個容器就可以擁有了獨立的主機名和域名,在網絡上可以被視作一個獨立的節點而非宿主機上的一個進程。

  Docker中,每個鏡像基本都以自己所提供的服務命名了自己的hostname而沒有對宿主機產生任何影響,用的就是這個原理。

1.1.3.IPC

  IPC:容器 中進程間進行通信通常采用的消息隊列,信號量,和共享內存。

  IPC資源就申請了這樣一個全局唯一的32位ID,所以IPC namespace中實際上包含了系統IPC標識符以及實現POSIX消息隊列的文件系統。

  在同一個IPC namespace下的進程彼此可見,而與其他的IPC namespace下的進程則互相不可見=====》同一ns下進程並不一定彼此可見。

1.1.4.PID

  兩個不同的namespace可以擁有相同的PID。每個PID namespace都有 各自的計數程序。

  內核為所有的PID namespace維護了一個樹狀結構,最頂層的是系統初始時創建的,我們稱之為root namespace。他創建的新PID namespace就稱之為child namespace(樹的子節點),而原先的PID namespace就是新創建的PID namespace的parent namespace(樹的父節點)。通過這種方式,不同的PID namespaces會形成一個等級體系。所屬的父節點可以看到子節點中的進程,並可以通過信號量等方式對子節點中的進程產生影響。

  • 每個PID namespace中的第一個進程“PID 1“,都會像傳統Linux中的init進程一樣擁有特權,起特殊作用。
  • 一個namespace中的進程,不可能通過killptrace影響父節點或者兄弟節點中的進程,因為其他節點的PID在這個namespace中沒有任何意義。
  • 如果你在新的PID namespace中重新掛載/proc文件系統,會發現其下只顯示同屬一個PID namespace中的其他進程。
  • 在root namespace中可以看到所有的進程,並且遞歸包含所有子節點中的進程。

(1)PID namespace中的init進程

  當我們新建一個PID namespace時,默認啟動的進程PID為1

  PID namespace維護這樣一個樹狀結構,非常有利於系統的資源監控與回收。Docker啟動時,第一個進程也是這樣,實現了進程監控和資源回收,它就是dockerinit

(2)信號量與init進程

  PID Namespace如此特殊,自然內核也賦予它了特殊權限---信號量屏蔽

  如果init進程沒有寫處理某個代碼邏輯,那么再同一個PID namespace下的進程即使擁有超級權限,發送給他的信號量都會被屏蔽。這個功能防止了init進程被誤殺

  如果是init的父進程 ,如果不是SIGKILL(銷毀進程)SIGSTOP(暫停進程)也會被忽略。但如果發送SIGKILLSIGSTOP,子節點的init會強制執行(無法通過代碼捕捉進行特殊處理),也就是說父節點中的進程有權終止子節點中的進程。

 

1.1.5.Mount

  Mount namespace通過隔離文件掛載點來對文件進程隔離,是第一個出現的namespace。

  隔離后,不同mount namespace中的文件結構發生變化也互不影響。

  你可以通過/proc/[pid]/mounts查看到所有掛載在當前namespace中的文件系統,還可以通過/proc/[pid]/mountstats看到mount namespace中文件設備的統計信息,包括掛載文件的名字、文件系統類型、掛載位置等等。

  一個掛載狀態可能為如下的其中一種:

  • 共享掛載(shared)
  • 從屬掛載(slave)
  • 共享/從屬掛載(shared and slave)
  • 私有掛載(private)
  • 不可綁定掛載(unbindable)

1.1.6.Network

  Network namespace主要提供了網絡資源的隔離,包括網絡設備,IPv4和IPv6協議棧、IP路由表、防火牆、/proc/net目錄、/sys/class/net目錄、端口(socket)等等。

1.1.7User

  User namespace主要隔離了安全相關的標識符(identifiers)和屬性(attributes),包括用戶ID、用戶組ID、root目錄、key(指密鑰)以及特殊權限。說得通俗一點,一個普通用戶的進程通過clone()創建的新進程在新user namespace中可以擁有不同的用戶和用戶組。

 

1.2.cgroups資源限制

  cgroups是Linux內核提供的一種機制,這種機制可以根據需求把一系列系統任務及其子任務整合(或分隔)到按資源划分等級的不同組內,從而為系統資源管理提供一個統一的框架。

  cgroups可以限制、記錄任務組所使用的物理資源(包括CPU、Memory、IO等),為容器實現虛擬化提供基本保證、是構建Docker等一系列虛擬化管理工具的基石。

1.2.1.cgroups特點

  1.cgroups的api以一個偽文件系統的方式實現,用戶態的程序可以通過文件操作實現cgroups的組織管理。

        2. cgroups的組織管理操作單元可以細粒到線程級別,另外用戶可以創建和銷毀cgroup,從而實現資源再分配。

        3.所有資源管理的功能,都以子系統的方式實現,接口統一。

        4.子任務創建之初與其父任務處於同一個cgroups控制組。

1.2.2.cgroups作用

  實現cgroups的主要目的是為不同用戶層面的資源管理,提供一個統一化的接口。從單個任務的資源控制到操作系統層面的虛擬化,cgroups提供了四大功能。

  1.資源限制:cgroups可以對任務使用的資源總額進行限制,如一旦超過設定的內存限制就發出OOM

  2.優先級分配:通過分配的CPU時間片數量及磁盤IO帶寬大小。

  3.資源統計:cgroups可以統計系統的資源使用量。

  4.任務控制:cgroups可以對任務進行掛起、恢復等操作。

1.2.3.術語

  • task(任務):cgroups的術語中,task就表示系統的一個進程
  • cgroup(控制組):cgroups 中的資源控制都以cgroup為單位實現。cgroup表示按某種資源控制標准划分而成的任務組包含一個或多個子系統。一個任務可以加入某個cgroup,也可以從某個cgroup遷移到另外一個cgroup。
  • subsystem(子系統):cgroups中的subsystem就是一個資源調度控制器(Resource Controller)。比如CPU子系統可以控制CPU時間分配,內存子系統可以限制cgroup內存使用量。
  • hierarchy(層級樹):hierarchy由一系列cgroup以一個樹狀結構排列而成,每個hierarchy通過綁定對應的subsystem進行資源調度。hierarchy中的cgroup節點可以包含零或多個子節點,子節點繼承父節點的屬性。整個系統可以有多個hierarchy。

1.2.4.組織結構與基本規則

  (1)同一個hierarchy可以附加一個或者多個subsystem。

  (2)一個subsystem可以附加到多個hierarchy,當且僅當這些hierarchy只有這唯一一個subsystem。

  (3)系統每次新建一個hierarchy時,該系統上的所有task默認構成了這個新建的hierarchy的初始化cgroup,這個cgroup也稱為root cgroup。

1.2.5.subsystem

  subsystem:cgroups的資源控制系統。每種subsystem控制一種資源,目前Docker使用了如下8中subsystem:

  • blkio:為塊設備設定輸入/輸出限制,比如物理驅動設備(包括磁盤、固態硬盤、USB等)。
  • cpu: 使用調度程序控制task對CPU的使用
  • cpuacct: 自動生成cgroup中task對CPU資源使用情況的報告。
  • cpuset: 為cgroup中的task分配獨立的CPU(此處針對多處理器系統)和內存。
  • devices :可以開啟或關閉cgroup中task對設備的訪問
  • freezer :可以掛起或恢復cgroup中的task
  • memory :可以設定cgroup中task對內存使用量的限定,並且自動生成這些task對內存資源使用情況的報告。
  • perf_event :使用后使得cgroup中的task可以進行統一的性能測試。{![perf: Linux CPU性能探測器,詳見https://perf.wiki.kernel.org/index.php/Main_Page]}
  • *net_cls 這個subsystem Docker沒有直接使用,它通過使用等級識別符(classid)標記網絡數據包,從而允許 Linux 流量控制程序(TC:Traffic Controller)識別從具體cgroup中生成的數據包。

 

  查詢mount 的cgroup的文件系統

  

   以cpu子系統為例

   

   在/sys/fs/cgroup的cpu子目錄下創建控制組,控制組目錄創建成功后,多了下面類似文件

$/sys/fs/cgroup/cpu# mkdir cgtest2
$/sys/fs/cgroup/cpu# ls cgtest2/ 
 cgroup.clone_children cgroup.procs cpuacct.stat cpuacct.usage cpuacct.usage_percpu cpu.cfs_period_us cpu.cfs_quota_us cpu.shares cpu.stat notify_on_release tasks

#限制18828進程
 $echo 18828 >> /sys/fs/cgroup/cpu/cgtest2/tasks
#將cpu限制為最高使用20%
 $echo 2000 > /sys/fs/cgroup/cpu/cgtest2/cpu.cfs_quota_us

#查看docker控制組目錄
$/sys/fs/cgroup/cpu# tree docker/
docker/
├── 20fb25551e96ba42b2401ef70785da68e96ffc10525b10c2434e2b9ad4f1e477      #容器ID
│   ├── cgroup.clone_children
│   ├── cgroup.procs
│   ├── cpuacct.stat
│   ├── cpuacct.usage
│   ├── cpuacct.usage_percpu
│   ├── cpu.cfs_period_us
│   ├── cpu.cfs_quota_us
│   ├── cpu.shares
│   ├── cpu.stat
│   ├── notify_on_release
│   └── tasks

  

1.2.3.cgroups實現方式機工作原理

   1.cgroups如何判斷資源超限機超出限額后的措施

 

    cgroups提供了統一的接口對資源進行控制和統計,但限制的方式不盡相同。

   2./sys/fs/cgroup/cpu/docker/<container-ID>下文件的作用

    一個cgroup創建完成,不管綁定了何種子系統,其目錄下都會生產下面幾個文件,用來描述cgroup信息,把相應的信息寫入這些配置文件就可以生效。

    tasks:羅列了所有在該cgroup中任務的TID,即所有進程及線程。

     cgroup.procs:羅列了所有在該cgroup中的TGID(線程組ID)

     notify_on_release:表示是否在cgroup中最后一個任務推出時通知運行releaseagent,填0或者1,默認為0表示不運行

1.2.4.cgroups的使用方法簡介

1.安裝cgroup

#apt-get install cgroup-bin
mkdir /cgroup 這個目錄可以用於掛載subsystem

2.查看cgroup及子系統掛載狀態

  • 查看所有的cgroup:lscgroup
  • 查看所有支持的子系統:lssubsys -a
  • 查看所有子系統掛載的位置: lssubsys –m
  • 查看單個子系統(如memory)掛載位置:lssubsys –m memory

3.創建hierarchy並掛載子系統  

  創建hierarchy

 #mount -t tmpfs yaohongcgroups /sys/fs/cgroup

  創建對應文件夾

#mkdir /sys/fs/cgroup/yh

  創建subsystem到對應層級

# mount -t cgroup -o subsystems yhsubsystem /cgroup/yhtest

1.3.Docker 架構預覽

  Docker時采用client-server架構模式,如下圖所示,Docker client向Docker daemno發送信息進行互相交互.

       

 

   Docker 通過driver模塊來實現容器執行環境的創建和管理.

  通過鏡像管理中的distribution、registry模塊從Docker  registry中下載鏡像,

       通過鏡像管理中的image、reference和layer存儲鏡像的元數據;

       通過鏡像驅動graphdriver將鏡像文件存儲到具體的文件系統中;

       當需要為Docker容器創建網絡環境時,通過網絡管理模塊network調用libnetwork創建並配置Docker容器的網絡環境;

       當需要為容器創建數據卷volume時,通過volume調用某個具體的volumedriver創建一個數據卷,來創建一個數據卷並負責后續的掛載操作;

       當需要限制Docker容器運行資源或者執行用戶指令等操作時,咋通過execdriver來完成。

       libcontainer時對cgroups和namespace的二次封裝,

       execdriver時通過libcontainer來實現對容器的具體管理,包括利用UTS、IPC、PID、network、mount、user等namespace實現容器之間的資源隔離和利用cgroups實現資源限制

  【Docker daemon】

    后台核心進程,用戶相應client的請求,該進程會在后台啟動一個API Server,負責接收由Docker client發送的請求,請求有daemon分發調度,再由具體的函數來執行請求。

  【Docker client】

    用於想Docker daemon發起請求,執行相應的容器管理操作,它即是可以命令行工具docker,也是遵循Docker API的客戶端。

  【image mamagement】

    Docker通過distribution、registry、layer、image、reference等模塊實現Docker鏡像的管理,這些模塊統稱為鏡像管理【image mamagement】

    1.distribution:負責與Docker registry進行交換,上傳下載鏡像以及存儲與v2相關的元數據。

    2.register:負責與Docker registry有關的身份驗證,鏡像查找,驗證及管理

    3.image:負責與鏡像元數據有關的存儲,查找,鏡像層的引用

    4.reference(參考):負責存儲本地所有鏡像的repository(存儲庫),並維護與鏡像ID之間的映射關系。

    5.layer:負責與鏡像層和容器層元數據有關的增刪查改,並負責將鏡像層的增刪查改操作映射到實際存儲鏡像層文件系統的graphdriver模塊。

1.4.client和daemon

 1.4.1.client模式

  Docker命令對應源文件時docker/docker.go,它的使用方式如下:

docker [OPTIONS] COMMAND [arg ...]

  其中OPTIONS參數稱為flag,任何時候執行一個docker命令,Docker都需要先解析這些flag,然后按照用戶聲明的COMMAND向子命令執行對應的操作。

  client模式下的docker命令工作流程包含如下幾個步驟。

  1.解析flag信息

  這里列出幾個client模式比較重要的OPTIONS

    Debug,對應-D和–debug參數,這個flag用於啟動調試模式
    LogLevel,對應-l和–log-level參數,默認等級是info,可選參數有:panic、error、warn、info、debug。
    Hosts,對應-H和–host=[]參數,對於client模式,就是指本次操作需要連接的Docker daemon位置,而對於daemon模式,則提供所要監聽的地址。若Hosts變量或者系統環境變量                         DOCKER_HOST不為空,說明用戶指定了host對象;否則使用默認設定,默認情況下Linux系統設置為unix:///var/run/docker.sock.
    protoAddrParts,這個信息來自於-H參數中://前后的兩部分組合,即與Docker daemon建立通信的協議方式與socket地址。
  2.創建client實例

    client的創建就是在已有配置參數信息的基礎上,調用api/client/cli.go#NewDockerCli,需要設置好proto(傳輸協議)、addr(host的目標地址)和tlsConfig(安全傳輸層協議的配置),另外還會配置標准輸入輸出及錯誤輸出。

  3.執行具體的命令

  Docker client對象創建成功后,剩下的執行具體命令的過程就交給cli/cli.go來處理。

 1.4.2.daemon模式

  Docker運行時如果使用docker daemon 子命令,就會運行Docker daemon。一旦docker進入了daemon模式,剩下的初始化和啟動工作就都由Docker的docker/daemon.go#CmdDaemon來完成。

Docker daemon通過一個server模塊(api/server/server.go)接收來自client的請求,然后根據請求類型,交由具體的方法去執行。

  下面是Docker daemon啟動與初始化過程的詳細解析

1.API Server的配置和初始化過程

  首先,在docker/daemon.go#CmdDaemon中,Docker會繼續按照用戶的配置完成server的初始化並啟動它。這個server為API Server,就是專門負責響應用戶請求並將請求交給daemon具體方法去處理的進程。它的啟動過程如下。
  (I)整理解析用戶指定的各項參數。

  (2)創建PID文件。

  (3)加載所需的serve輔助配置,包括日志、是否允許遠程訪問、版本以及TLS認證信息等。

  (4)根據上述server配置,加上之前解析出來的用戶指定的server配置(比如Hosts ),通過goroutine的方式啟動API Server。這個server監聽的socket位置就是Hosts的值。

  (5)創建一個負責處理業務的daemon對象(對應daemon/damone.go)作為負責處理用戶請求的邏輯實體。

  (6)對APIserver中的路由表進行初始化,即將用戶的請求和對應的處理函數相對應起來。

  (7)設置一個channel,保證上述goroutine只有在server出錯的情況下才會退出。

  (8)設置信號捕獲,當Docker daemon進程收到INT, TERM, QUIT信號時,關閉API Server,調用shutdownDaemon停止這個daemon。

  (9)如果上述操作都成功,API ServergjG會與上述daemon綁定,並允許接受來自client的連接。

  (10)最后,Docker daemon進程向宿主機的init守護進程發送“READY=1”信號,表示這個Docker daemon已經開始正常工作了。
2.daemon對象的創建與初始化過程

  docker daemon是如何創建出來的?是通過daemon/daemon.go#NewDaemon方法。

  NewDaemon過程會按照Docker的功能特點,完成所需的屬性設置用戶或者系統指定的值,需要完成的配置至少包括以下特點:

  (1)Docker容器的配置信息:設置默認的網絡最大傳輸單元,檢測網橋配置信息

  (2)檢測系統支持及用戶權限

  (3)工作路徑,默認為/var/lib/docker

  (4)配置Docker容器所需的文件環境

    配置graphdriver目錄,用於完成Docker容器鏡像管理所需的底層存儲驅動層

    

 

1.5.libcontainer

  libcontainer是Docker對容器管理的包,它基於Go語言實現,通過管理namespace、cgroups、capabilities以及文件系統來進行容器控制。

  你可以使用libcontainer創建容器,並對容器進行生命周期的管理。

1.5.1libcontainer特性

  目前版本的libcontainer,功能實現上涵蓋了包括namespaces使用、cgroups管理,Rootfs的配置啟動,默認的Linux capability權限集、以及經常運行環境變量配。

  1.建立文件系統:文件系統方面,容器運行rootfs。所有容器中要執行的指令,都需要包含在rootfs所有掛載在容器銷毀時都會被卸載。

  2.資源管理:Docker使用cgroup進行資源管理和限制,包括設備、內存、CPU、輸入輸出等。

  3.安全特性:libcontainer目前可通過配置capabilitiesSELinuxapparmor 以及seccomp進行一定的安全防范。

  4.在運行着的容器中執行新進程:就是我們熟悉的docker exec功能,指令的二進制文件需要包含在容器的rootfs之內。

  5.容器熱遷移:通過libcontainer你已經可以把一個正在運行的進程狀態保存到磁盤上,然后在本地或其他機器中重新恢復當前的運行狀態。

1.6.libcontainer實現原理

  在Docker中,對容器管理的模塊為execdriver,目前Docker支持的容器管理方式有兩種,一種就是最初支持的LXC方式,另一種稱為native,即使用libcontainer進行容器管理。

  雖然在execdriver中只有LXC和native兩種選擇,但是native(即libcontainer)通過接口的方式定義了一系列容器管理的操作,包括處理容器的創建(Factory)、容器生命周期管理(Container)、進程生命周期管理(Process)等一系列接口。

 

1.6.Docker鏡像管理

1.6.1.什么是Docker鏡像

  Docker鏡像:Docker鏡像是一個只讀性的Docker容器模板,含有啟動Docker容器所需的文件系統結構及其內容是啟動一個Docker容器的基礎。

1.rootfs

  rootfs:Docker鏡像的文件內容以及一些運行Docker容器的配置文件組成了Docker容器的靜態文件系統環境。

  可以這么理解,Docker鏡像是Docker 容器的靜態視角,Docker容器時Docker鏡像的運行狀態。

  在Docker架構中,當Docker daemon為Docker容器掛載rootfs時,沿用了linux內核啟動時的方法,即將rootfs設置為只讀模式。在掛載完畢后,利用聯合掛載(union mount)技術在已有的只讀rootfs上再掛載一個讀寫層。這樣,可讀寫層處於Docker容器文件系統的最頂層,其下可能聯合掛載多個只讀層,只有再Docker容器運行過程中國文件系統發生變化,才會將變化的內容寫到可讀寫層,並且隱藏只讀層中老文件。

  容器文件系統其實是一個相對獨立的組織,分為1.可讀寫部分(read-write layer及volumes),2.init-layer,3.只讀層(read-only layer)這3個部分共同組成的一個容器所需的下層文件系統。

2.鏡像的主要特點

  (1)分層:docker commit提交這個修改過的容器文件系統為一個新的鏡像時,保存的內容僅為最上層讀寫文件系統中被更新過的文件。

  (2)寫是復制:多個容器之間共享鏡像,不需要再復制出一份鏡像,而是將所有的鏡像層以只讀的方式掛載到一個掛載點,而在上面覆蓋一個可讀寫層的容器層。

  (3)內容尋址:對鏡像層的內容計算校驗和,生成一個內容哈希值,並以此哈希值替代之前的UUID作為鏡像的唯一標志,

  (4)聯合掛載(union mount):可以在一個掛載點同時掛載多個文件系統,將掛載點的原目錄與被掛載內容進行整合,使得最終可見的文件系統將會包含整合之后的各層文件和目錄。

1.6.2.Docker鏡像關鍵概念

  (1)registry:保持Docker鏡像,其中還包括鏡像層次結構和關於鏡像的元數據。

  (2)repository(存儲庫):registry是repository的集合,repository是鏡像的集合。

  (3)manifest(描述文件):主要存在於registry中作為Docker鏡像的元數據文件,在pull、push、save和load中作為鏡像結構和基礎信息的描述文件。

  (4)image:用來存儲一組鏡像相關的元數據,主要包括鏡像的架構(amd64、arm64)、鏡像默認配置信息,構建鏡像的容器配置信息,包含所有鏡像層信息的rootfs。

  (5)layer(鏡像層):用來管理鏡像層的中間概念,主要存放鏡像層的DIFF_ID、size、cache-id和parent等內容。

  (6)dockerfile: 

 

1.8.Docker網絡管理

1.8.1.Docker網絡架構

  Docker公司再libnetwork中使用了CNM。CNM定義了構建容器虛擬化網絡的模型,同時還提供了可以用於開發多種網絡驅動的標准化接口和組件。

   libnetwork和Docker Daemon及各個網絡驅動的關系可以通過下圖表示:

   Docker daemon通過調用libnetwork對外提供的API完成網絡的創建個管理等功能。

  libnetwork中則使用了CNM來完成網絡功能的提供,CNM中主要有sandbox、endpoint、network這3種組件。

  

 

   CNM中的3個核心組件如下:

  (1)沙盒:一個沙盒包含了一個容器網絡棧的信息。沙盒可以對容器的接口、路由、DNS等設置進行管理。沙盒可以有多個端點和網絡。

  (2)端點:一個端點可以加入一個沙盒和一個網絡。一個端點只可以屬於一個網絡並且只屬於一個沙盒。

  (3)網絡:一個網絡時一組可以直接互相聯調的端點,一個網絡可以包括多個端點。

  

 

   libnetwork中有一下5個內置驅動:

  • bridge:默認驅動,網橋模式。
  • host:去掉容器和Docker主機之間的網絡隔離,直接使用主機的網絡。不會為Docker模式創建網絡協議棧,即不會創建network namespace。
  • overlay:覆蓋網絡將多個Docker daemons 連接在一起,使swarm服務能夠相互通信。
  • macvlan:macvlan網絡允許您將MAC地址分配給容器,使其顯示為網絡上的物理設備。Docker daemons 按其MAC地址將通信路由到容器。在處理希望直接連接到物理網絡而不是通過Docker主機的網絡堆棧路由的遺留應用程序時,使用macvlan驅動程序有時是最佳選擇。
  • null:Docker容器擁有自己的namepsace但不進行網絡配置。

  創建網絡:

# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
77a80a9afsdfff        bridge              bridge              local
94694ffrfrfrfrfb        host                host                local
39573frfrfrfrs4        none                null                local


# docker network create backend
ead41d30f820c2699ed532e84d0fsdffb5a1f4c37eea6c54bfa687b903649

# docker network create fronted
8d94c681869f96b668c3abb72d3cb6aa14af236e94ef4fac7e38c157260787a6

# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
ead41dsssff820        backend             bridge              local
77a80a9a5c6bc        bridge                bridge              local
8d94ccccc1869f        fronted              bridge              local
9469402ccc53b        host                  host                 local
395736cvc0e54        none                 null                  local

  指定容器網絡

# docker run -it --name container1 --net backend busybox

  

 1.8.2.bridge網絡

  此條路由表示目的IP地址的數據包時docker0發出的。

# route -n
172.25.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0

   如下圖,docke0就時一個網橋,網橋的概念就類似與一個交換機,為連在其上的設備轉發數據幀。

    網橋上的veth網卡設備相當於交換機上的端口,可以將多個容器或虛擬機連接在其上,docker 0網橋就為連在其上的容器轉發數據幀,是得同一台宿主機上的Docker容器之間可以互相通信。

   

 

   查看機器上的網橋和上面的端口:

# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.02420e64d653	no		veth7eb3e54
yhbro		8000.000000000000	no

  創建網橋:

# brctl show addbr yhbro

  網橋參數設置:

  --bip=CIDR:設置docker0的ip地址和子網范圍。

  --fixed-cidr=CIDR:限制Docker容器獲取IP范圍。

1.8.3.Docker daemon網絡配置原理

  Docker自身的網絡,主要分為兩部分,第一是Docker daemon的網絡配置,第二是libcontainer的網絡配置。Docker daemon的網絡指的daemon啟動時,在主機系統上所作的網絡設置,可以被所有的docker容器使用,libcontainer的網絡正對具體的容器是使用docker run命令啟動容器是時,根據傳入的參數為容器做的網絡配置工作。

1.8.4.libcontainer網絡配置原理

 

1.9.Docker與容器安全

1.9.1.Docker的安全機制

  1.Docker daemon安全:默認使用Unix域套接字的方式與客戶端進行通信,這種形式相對於TCP的形式比較安全。

  2.鏡像安全:registry訪問權限控制可以保證鏡像的安全。

        registry安全:添加了倉庫訪問認證。

        驗證校驗和:保證鏡像的完整性。

  3.內核安全:內核容器提供了兩種技術cgroups和namespace。

  4.容器之間網絡安全:--icc可以禁止容器之間通信,主要通過設定iptables規划和實現。

  5.容器能力限制:可以通過允許修改進程ID,用戶組ID,等能力限制

  6.限制能力:比如不需要setgid、setuid能力,可以再run容器時添加--cap-drop  SETUID  --cap-drop  SETGID。

  7.添加能力:比如啟動容器時使用--cap-add ALL --cap-add SYS_TIME來增加允許修改系統時間能力。

 

1.9.DockerFile實踐

  1. Dockerfile整體就兩類語句組成:

    • # Comment 注釋信息
    • Instruction arguments 指令 參數,一行一個指令。
  2. Dockerfile文件名首字母必須大寫。
  3. Dockerfile指令不區分大小寫,但是為方便和參數做區分,通常指令使用大寫字母。
  4. Dockerfile中指令按順序從上至下依次執行。
  5. Dockerfile中第一個非注釋行必須是FROM指令,用來指定制作當前鏡像依據的是哪個基礎鏡像。
  6. Dockerfile中需要調用的文件必須跟Dockerfile文件在同一目錄下,或者在其子目錄下,父目錄或者其它路徑無效。

  

DockerFile目前支持的參數:

1.ADD:ADD與COPY的指令功能上很相似,都支持復制本地文件到鏡像的功能,但ADD指令還支持其它的功能。

 

           ADD的時候要復制的文件可以是個網絡文件的URL

 

2.COPY:COPY <src> <dest>

     <src>:要復制的源文件或者目錄,支持通配符,COPY復制指向的文件或者目錄到鏡像中,

               <dest>:目標路徑,即正創建的鏡像的文件系統路徑,建議使用絕對路徑,否則,COPY指令會以WORKDIR為其起始路徑。如果路徑中如果包含空白字符,建議使用第二種格式用引號引起來,否則會被當成兩個文件。

 

3.ENV:指定環境變量,同docker run -e,為鏡像定義所需的環境變量,並可被ENV指令后面的其它指令所調用。

   調用格式為$variable_name或者${variable_name},使用docker run啟動容器的時候加上 -e 的參數為variable_name賦值,可以覆蓋Dockerfile中ENV指令指定的此variable_name的值。

           但是不會影響到dockerfile中已經引用過此變量的文件名。

4.FROM:FROM指令必須為Dockerfile文件開篇的第一個非注釋行,用於指定構建鏡像所使用的基礎鏡像,后續的指令運行都要依靠此基礎鏡像,所提供的的環境(簡單說就是假如Dockerfile中所引用的基礎鏡像里面沒有mkdir命令,那后續的指令是沒法使用mkdir參數的

          

5.LABEL同docker run -l,讓用戶為鏡像指定各種元數據(鍵值對的格式)

6.STOPSIGNAL:指定發送使容器退出的系統調用信號。docker stop之所以能停止容器,就是發送了15的信號給容器內PID為1的進程。此指令一般不會使用。

 

7.USER:用於指定docker build過程中任何RUN、CMD等指令的用戶名或者UID。默認情況下容器的運行用戶為root。

8.VOLUME:docker run -v簡化版,用於在鏡像中創建一個掛載點目錄。指定工作目錄,可以指多個,每個WORKDIR只影響他下面的指令,直到遇見下一個WORKDIR為止。

9.WORKDIR:同docker run -w, Docker的鏡像由只讀層組成,每個只讀層對應一個Dockerfile的一個指令,各個層堆疊在一起,每一層都是上一層的增量。WORKDIR也可以調用由ENV指令定義的變量。

 

FROM ubuntu:1804   #從ubuntu:18.04Docker映像創建一個圖層。
COPY . /app             #從Docker客戶端的當前目錄添加文件。
RUN mkdir /APP       #使用構建您的應用程序make
CMD python /app/aa.py    #指定要在容器中運行的命令

  盡可能通過字母數字排序多行參數來簡化以后的更改。這有助於避免軟件包重復,並使列表更易於更新。這也使PR易於閱讀和查看。在反斜杠(\之前添加空格也有幫助。

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion

 

2.總結

2.1.什么是Docker

  Docker本質上是一個進程,用namespace進行隔離,cgroup進行資源限制,rootfs作為文件系統。

2.2.namespace

  namespace共分為6種,UTS,IPC,PID,NETWORK,MOUNT,USER。

  UTS:隔離主機名和域名。IPC:隔離消息隊列,信號量和共享內存。PID:隔離進程。network:隔離網絡。mount:隔離掛載。user:隔離用戶。

  隔離的作用就是產生輕量級的虛擬化,相同namespace下進程可以感知彼此的變化,不同namespace的進程直接彼此無感知。

2.3.cgroup

  cgroup可以對資源進行限制,分配和統計,在/sys/fs/cgroup/中都是一個個cgroup子系統,這些子系統分別控制着輸入、輸出、cpu大小、內存大小等。

2.4Docker的架構及相關組件

  采用client-server模式:

  docker-client(可以是docker客戶端命令也可以是API的客戶端)發請求給docker-daemon,docker-daemon啟動API-server接收到消息后,根據請求調用對應組件:

       libnetwork:控制network

       execdontainer:調用libcontainer(對namespace、cgroup的二次封裝)控制namespace、cgroup

       value:通過控制ececdriver控制卷

  grapdriver:將鏡像文件存儲到具體的文件系統中

  images manager:describetion和registry控制鏡像拉取,layer、image、reference控制鏡像元數據。

2.5.Docker網絡

  分為bridge(創建namespace並且每個容器擁有自己的ip)、hosts(公用宿主機namespace,使用宿主機ip),overlay,null(擁有自己的namespace,但不進行網絡配置)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM