從Docker 1.11開始暗挑,Docker容器運(yùn)行已經(jīng)不是簡單的通過Docker daemon來啟動(dòng)笋除,而是集成了containerd、runC等多個(gè)組件炸裆。Docker服務(wù)啟動(dòng)之后垃它,我們也可以看見系統(tǒng)上啟動(dòng)了dockerd、docker-containerd等進(jìn)程烹看,本文主要介紹新版Docker(1.11以后)每個(gè)部分的功能和作用国拇。
Docker Daemon
作為Docker容器管理的守護(hù)進(jìn)程,Docker Daemon從最初集成在docker
命令中(1.11版本前)惯殊,到后來的獨(dú)立成單獨(dú)二進(jìn)制程序(1.11版本開始)酱吝,其功能正在逐漸拆分細(xì)化,被分配到各個(gè)單獨(dú)的模塊中去土思。從Docker服務(wù)的啟動(dòng)腳本务热,也能看見守護(hù)進(jìn)程的逐漸剝離:
在Docker 1.8之前,Docker守護(hù)進(jìn)程啟動(dòng)的命令為:
docker -d
這個(gè)階段己儒,守護(hù)進(jìn)程看上去只是Docker client的一個(gè)選項(xiàng)崎岂。
Docker 1.8開始,啟動(dòng)命令變成了:
docker daemon
這個(gè)階段闪湾,守護(hù)進(jìn)程看上去是docker
命令的一個(gè)模塊冲甘。
Docker 1.11開始,守護(hù)進(jìn)程啟動(dòng)命令變成了:
dockerd
此時(shí)已經(jīng)和Docker client分離,獨(dú)立成一個(gè)二進(jìn)制程序了江醇。
當(dāng)然省艳,守護(hù)進(jìn)程模塊不停的在重構(gòu),其基本功能和定位沒有變化嫁审。和一般的CS架構(gòu)系統(tǒng)一樣跋炕,守護(hù)進(jìn)程負(fù)責(zé)和Docker client交互,并管理Docker鏡像律适、容器辐烂。
下面就來介紹下獨(dú)立分拆出來的其他幾個(gè)模塊。
Containerd
containerd是容器技術(shù)標(biāo)準(zhǔn)化之后的產(chǎn)物捂贿,為了能夠兼容OCI標(biāo)準(zhǔn)纠修,將容器運(yùn)行時(shí)及其管理功能從Docker Daemon剝離。理論上厂僧,即使不運(yùn)行dockerd扣草,也能夠直接通過containerd來管理容器。(當(dāng)然颜屠,containerd本身也只是一個(gè)守護(hù)進(jìn)程辰妙,容器的實(shí)際運(yùn)行時(shí)由后面介紹的runC控制。)
最近甫窟,Docker剛剛宣布開源containerd密浑。從其項(xiàng)目介紹頁面可以看出,containerd主要職責(zé)是鏡像管理(鏡像粗井、元信息等)尔破、容器執(zhí)行(調(diào)用最終運(yùn)行時(shí)組件執(zhí)行)。
containerd向上為Docker Daemon提供了gRPC接口浇衬,使得Docker Daemon屏蔽下面的結(jié)構(gòu)變化懒构,確保原有接口向下兼容。向下通過containerd-shim結(jié)合runC耘擂,使得引擎可以獨(dú)立升級胆剧,避免之前Docker Daemon升級會(huì)導(dǎo)致所有容器不可用的問題。
Docker梳星、containerd和containerd-shim之間的關(guān)系赞赖,可以通過啟動(dòng)一個(gè)Docker容器,觀察進(jìn)程之間的關(guān)聯(lián)冤灾。首先啟動(dòng)一個(gè)容器前域,
docker run -d busybox sleep 1000
然后通過pstree
命令查看進(jìn)程之間的父子關(guān)系(其中20708是dockerd
的PID):
pstree -l -a -A 20708
輸出結(jié)果如下:
dockerd -H fd:// --storage-driver=overlay2
|-docker-containe -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --shim docker-containerd-shim --runtime docker-runc
| |-docker-containe b9a04a582b66206492d29444b5b7bc6ec9cf1eb83eff580fe43a039ad556e223 /var/run/docker/libcontainerd/b9a04a582b66206492d29444b5b7bc6ec9cf1eb83eff580fe43a039ad556e223 docker-runc
| | |-sleep 1000
雖然pstree
命令截?cái)嗔嗣睿覀冞€是能夠看出韵吨,當(dāng)Docker daemon啟動(dòng)之后匿垄,dockerd和docker-containerd進(jìn)程一直存在。當(dāng)啟動(dòng)容器之后,docker-containerd進(jìn)程(也是這里介紹的containerd組件)會(huì)創(chuàng)建docker-containerd-shim進(jìn)程椿疗,其中的參數(shù)b9a04a582b66206492d29444b5b7bc6ec9cf1eb83eff580fe43a039ad556e223就是要啟動(dòng)容器的id漏峰。最后docker-containerd-shim子進(jìn)程,已經(jīng)是實(shí)際在容器中運(yùn)行的進(jìn)程(既sleep 1000)届榄。
docker-containerd-shim另一個(gè)參數(shù)浅乔,是一個(gè)和容器相關(guān)的目錄/var/run/docker/libcontainerd/b9a04a582b66206492d29444b5b7bc6ec9cf1eb83eff580fe43a039ad556e223,里面的內(nèi)容有:
.
├── config.json
├── init-stderr
├── init-stdin
└── init-stdout
其中包括了容器配置和標(biāo)準(zhǔn)輸入铝条、標(biāo)準(zhǔn)輸出靖苇、標(biāo)準(zhǔn)錯(cuò)誤三個(gè)管道文件。
docker-shim
docker-shim是一個(gè)真實(shí)運(yùn)行的容器的真實(shí)墊片載體班缰,每啟動(dòng)一個(gè)容器都會(huì)起一個(gè)新的docker-shim的一個(gè)進(jìn)程贤壁,
他直接通過指定的三個(gè)參數(shù):容器id,boundle目錄(containerd的對應(yīng)某個(gè)容器生成的目錄埠忘,一般位于:/var/run/docker/libcontainerd/containerID)脾拆,
運(yùn)行是二進(jìn)制(默認(rèn)為runc)來調(diào)用runc的api創(chuàng)建一個(gè)容器(比如創(chuàng)建容器:最后拼裝的命令如下:runc create 。莹妒。名船。。动羽。)
RunC
OCI定義了容器運(yùn)行時(shí)標(biāo)準(zhǔn)包帚,runC是Docker按照開放容器格式標(biāo)準(zhǔn)(OCF, Open Container Format)制定的一種具體實(shí)現(xiàn)。
runC是從Docker的libcontainer中遷移而來的运吓,實(shí)現(xiàn)了容器啟停、資源隔離等功能疯趟。Docker默認(rèn)提供了docker-runc實(shí)現(xiàn)拘哨,事實(shí)上,通過containerd的封裝信峻,可以在Docker Daemon啟動(dòng)的時(shí)候指定runc的實(shí)現(xiàn)倦青。
我們可以通過啟動(dòng)Docker Daemon時(shí)增加--add-runtime
參數(shù)來選擇其他的runC現(xiàn)。例如:
docker daemon --add-runtime "custom=/usr/local/bin/my-runc-replacement"
下面就讓我們看下這幾個(gè)模塊如何工作盹舞。
舉個(gè)例子
這里通過Docker一些命令产镐,實(shí)現(xiàn)不使用Docker Daemon直接啟動(dòng)一個(gè)鏡像,以便了解Docker Daemon每個(gè)模塊的作用踢步。
首先癣亚,需要?jiǎng)?chuàng)建容器標(biāo)準(zhǔn)包,這部分實(shí)際上由containerd的bundle模塊實(shí)現(xiàn)获印,將Docker鏡像轉(zhuǎn)換成容器標(biāo)準(zhǔn)包述雾。
mkdir my_container
cd my_container
mkdir rootfs
docker export $(docker create busybox) | tar -C rootfs -xvf -
上述命令將busybox鏡像解壓縮到指定的rootfs目錄中。如果本地不存在busybox鏡像,containerd還會(huì)通過distribution模塊去遠(yuǎn)程倉庫拉取玻孟。
現(xiàn)在整個(gè)my_container目錄結(jié)構(gòu)如下:
$ tree -d my_container/
my_container/
└── rootfs
├── bin
├── dev
│ ├── pts
│ └── shm
├── etc
├── home
├── proc
├── root
├── sys
├── tmp
├── usr
│ └── sbin
└── var
├── spool
│ └── mail
└── www
17 directories
此時(shí)唆缴,標(biāo)準(zhǔn)包所需的容器數(shù)據(jù)已經(jīng)準(zhǔn)備完畢,接下來我們需要?jiǎng)?chuàng)建配置文件:
docker-runc spec
此時(shí)會(huì)生成一個(gè)名為config.json
的配置文件黍翎,該文件和Docker容器的配置文件類似面徽,主要包含容器掛載信息、平臺信息匣掸、進(jìn)程信息等容器啟動(dòng)依賴的所有數(shù)據(jù)趟紊。
最后,可以通過runc
命令來啟動(dòng)容器:
runc run busybox
注意旺聚,runc必須使用root權(quán)限啟動(dòng)织阳。
執(zhí)行之后,我們可以看見容器已經(jīng)啟動(dòng):
localhost my_container # runc run busybox
/ # ps aux
PID USER TIME COMMAND
1 root 0:00 sh
9 root 0:00 ps aux
此時(shí)砰粹,事實(shí)上已經(jīng)可以不依賴Docker本身唧躲,如果系統(tǒng)上安裝了runc
包,即可運(yùn)行容器碱璃。對于Gentoo系統(tǒng)來說弄痹,安裝app-emulation/runc
包即可。
當(dāng)然嵌器,也可以使用docker-runc命令來啟動(dòng)容器:
localhost my_container # docker-runc run busybox
/ # ps aux
PID USER TIME COMMAND
1 root 0:00 sh
7 root 0:00 ps aux
從這里可以看到標(biāo)準(zhǔn)化的重要性肛真。
總結(jié)
從Docker 1.11之后,Docker Daemon被分成了多個(gè)模塊以適應(yīng)OCI標(biāo)準(zhǔn)爽航。拆分之后蚓让,結(jié)構(gòu)分成了以下幾個(gè)部分。
其中讥珍,containerd獨(dú)立負(fù)責(zé)容器運(yùn)行時(shí)和生命周期(如創(chuàng)建历极、啟動(dòng)、停止衷佃、中止趟卸、信號處理、刪除等)氏义,其他一些如鏡像構(gòu)建锄列、卷管理、日志等由Docker Daemon的其他模塊處理惯悠。
Docker的模塊塊擁抱了開放標(biāo)準(zhǔn)邻邮,希望通過OCI的標(biāo)準(zhǔn)化,容器技術(shù)能夠有很快的發(fā)展吮螺。