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_NEWIPC
、CLONE_NEWNS
、CLONE_NEWNET
、CLONE_NEWPID
、 CLONE_NEWUSER
和CLONE_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中的進程,不可能通過
kill
或ptrace
影響父節點或者兄弟節點中的進程,因為其他節點的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(暫停進程)
也會被忽略。但如果發送SIGKILL
或SIGSTOP
,子節點的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目前可通過配置capabilities
、SELinux
、apparmor
以及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實踐
-
Dockerfile整體就兩類語句組成:
- # Comment 注釋信息
- Instruction arguments 指令 參數,一行一個指令。
- Dockerfile文件名首字母必須大寫。
- Dockerfile指令不區分大小寫,但是為方便和參數做區分,通常指令使用大寫字母。
- Dockerfile中指令按順序從上至下依次執行。
- Dockerfile中第一個非注釋行必須是FROM指令,用來指定制作當前鏡像依據的是哪個基礎鏡像。
-
Dockerfile中需要調用的文件必須跟Dockerfile文件在同一目錄下,或者在其子目錄下,父目錄或者其它路徑無效。
DockerFile目前支持的參數:
ADD與COPY的指令功能上很相似,都支持復制本地文件到鏡像的功能,但ADD指令還支持其它的功能。1.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,但不進行網絡配置)。