Docker是一個(gè)開源的引擎,可以輕松的為任何應(yīng)用創(chuàng)建一個(gè)輕量級(jí)的、可移植的、自給自足的容器。
在本文中,將分享容器的整個(gè)生命周期,從創(chuàng)建、管理到停止,直到最終刪除。
0x01 Docker安裝
在Centos7中可以直接使用yum命令安裝docker
$ yum install -y docker #安裝docker
$ systemctl start docker # 啟動(dòng) docker
$ systemctl enable docker # 加入開機(jī)自啟動(dòng)
$ docker info # 檢查docker程序是否正常工作
Docker是基于客戶端-服務(wù)器構(gòu)架的。它有一個(gè)docker程序,既能作為客戶端,也可以作為服務(wù)器端。作為客戶端時(shí),docker程序向Docker守護(hù)進(jìn)程發(fā)送請(qǐng)求(如請(qǐng)求返回守護(hù)進(jìn)程自身的信息),然后再對(duì)返回來(lái)的請(qǐng)求結(jié)果進(jìn)行處理。
0x02 運(yùn)行第一個(gè)容器
- 查詢鏡像
$ docker search centos # 搜索所有centos的docker 鏡像
- 獲取鏡像
$ docker pull centos # 獲取 centos 鏡像
- 創(chuàng)建容器
$ docker run -it centos /bin/bash
-i 標(biāo)志保證容器中STDIN是開啟的;
-t 標(biāo)志則是告訴Docker為要?jiǎng)?chuàng)建的容器分配一個(gè)偽tty終端。這樣,新創(chuàng)建的容器才能提供一個(gè)交互式shell;
接下來(lái),我們告訴Docker基于什么鏡像來(lái)創(chuàng)建容器,示例中使用的是centos鏡像;
最后,我們告訴Docker在新容器中要運(yùn)行什么命令,在本例中我們?cè)谌萜髦羞\(yùn)行/bin/bash命令啟動(dòng)了一個(gè)Bash shell
0x03 使用第一個(gè)容器
現(xiàn)在,我們已經(jīng)以root用戶登錄到了新容器中,容器的ID 0a280c092d66,看起來(lái)有些令人迷惑的字符串。這是一個(gè)完整的centos系統(tǒng),你可以用它來(lái)做任何事情。
- 我們可以獲取該容器的主機(jī)名
[root@0a280c092d66 /]# hostname
0a280c092d66
可以看到,容器的主機(jī)名就是該容器的ID。
- 檢查容器的/etc/hosts文件
[root@0a280c092d66 /]# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 0a280c092d66
Docker在hosts文件中為該容器的IP地址添加了一條主機(jī)配置項(xiàng)。
- 查看容器IP
[root@0a280c092d66 /]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
24: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:3/64 scope link
valid_lft forever preferred_lft forever
我們可以看到,這里有l(wèi)o的環(huán)回接口,還有IP為172.17.0.3的標(biāo)準(zhǔn)eth0網(wǎng)絡(luò)接口,和普通宿主機(jī)是完全一樣的
- 檢查容器的進(jìn)程
[root@0a280c092d66 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 15:36 ? 00:00:00 /bin/bash
root 131 1 0 16:33 ? 00:00:00 ps -ef
你可以繼續(xù)在容器中做任何自己想做的事情。當(dāng)所有工作都結(jié)束時(shí),輸入exit,就可以返回到centos宿主機(jī)了,但此時(shí)容器的停止?fàn)顟B(tài);如果想退出后容器繼續(xù)運(yùn)行,則可以先按ctrl+p再按ctrl+q。
0x04 容器命名
Docker會(huì)為我們創(chuàng)建的每一個(gè)容器自動(dòng)生成一個(gè)隨機(jī)的名稱。如果想為容器指定一個(gè)名稱,而不是使用自動(dòng)生成的名稱,則可以用--name標(biāo)志來(lái)實(shí)現(xiàn)
$ docker run --name yangfannie -it centos /bin/bash # 運(yùn)行容器并指定名稱為yangfannie
一個(gè)合法的容器名稱只能包含以下字符:小寫字母az、大寫字母AZ、數(shù)字0~9、下劃線、圓點(diǎn)、橫線(如果用正則表達(dá)式來(lái)表示這些符號(hào),就是[a-zA-Z0-9_.-])
在很多Docker命令中,我們都可以用容器的名稱來(lái)替代容器ID,后面我們將會(huì)看到。容器名稱有助于分辨容器,當(dāng)構(gòu)建容器和應(yīng)用程序之間的邏輯連接時(shí),容器的名稱也有助于從邏輯上理解連接關(guān)系。具體的名稱(如web、db)比容器ID和隨機(jī)容器名好記多了。我推薦大家都使用容器名稱,以更加方便地管理容器。
容器的命名必須是唯一的。如果我們?cè)噲D創(chuàng)建兩個(gè)名稱相同的容器,則命令將會(huì)失敗。如果要使用的容器名稱已經(jīng)存在,可以先用docker rm命令刪除已有的同名容器后,再來(lái)創(chuàng)建新的容器。
0x05 重新啟動(dòng)停止的容器
$ docker start yangfannie # 通過(guò)容器名稱來(lái)啟動(dòng)
$ docker start 0a280c092d66 # 通過(guò)容器ID來(lái)啟動(dòng)
$ docker ps -a #查看所有的容器
0x06 附著到容器上
Docker容器重新啟動(dòng)的時(shí)候,會(huì)沿用docker run命令時(shí)指定的參數(shù)來(lái)運(yùn)行,因此我們?nèi)萜髦匦聠?dòng)后會(huì)運(yùn)行一個(gè)交互式會(huì)話shell。此外,我們也可以用docker attach命令,重新附著到該容器的會(huì)話上
$ docker attach yangfannie #通過(guò)容器名稱附著
$ docker attach 0a280c092d66 # 通過(guò)容器ID附著
0x07 創(chuàng)建守護(hù)式容器
除了這些交互式運(yùn)行的容器(interactive container),我們也可以創(chuàng)建長(zhǎng)期運(yùn)行的容器。守護(hù)式容器(daemonized container)沒(méi)有交互式會(huì)話,非常適合運(yùn)行應(yīng)用程序和服務(wù)。大多數(shù)時(shí)候我們都需要以守護(hù)式來(lái)運(yùn)行我們的容器
$ docker run --name yangfannie -d centos /bin/bash -c "while true; do echo hello world; sleep 1; done"
在上面的docker run命令使用了-d參數(shù),因此Docker會(huì)將容器放到后臺(tái)運(yùn)行。另外還在容器要運(yùn)行的命令里使用了一個(gè)while循環(huán),該循環(huán)會(huì)一直打印hello world,直到容器或其進(jìn)程停止運(yùn)行。
通過(guò)組合使用上面的這些參數(shù),你可以發(fā)現(xiàn)docker run命令并沒(méi)有像上一個(gè)容器一樣將主機(jī)的控制臺(tái)附著到新的shell會(huì)話上,而是僅僅返回了一個(gè)容器ID而已,我們還是在主機(jī)的命令行之中。如果我們執(zhí)行docker ps命令,可以看到一個(gè)正在運(yùn)行的容器。
0x08 容器內(nèi)部都在干些什么
現(xiàn)在我們已經(jīng)有了一個(gè)在后臺(tái)運(yùn)行while循環(huán)的守護(hù)型容器。為了探究該容器內(nèi)部都在干些什么,我們可以用docker logs命令來(lái)獲取容器的日志
$ docker logs yangfannie
hello world
hello world
hello world
hello world
hello world
hello world
hello world
. . .
我們可以看到while循環(huán)正在向日志里打印hello world。Docker會(huì)輸出最后幾條日志項(xiàng)并返回。我們也可以在命令后使用-f參數(shù)來(lái)監(jiān)控Docker的日志,這與tail -f命令非常相似,通過(guò)Ctrl+C退出日志跟蹤。
我們也可以跟蹤容器日志的某一片段,和之前類似,只需要在tail命令后加入-f --lines標(biāo)志即可。
$ docker logs --tail 10 yangfannie # 獲取日志的最后10行內(nèi)容
$ docker logs --tail 0 -f yangfannie # 跟蹤某個(gè)容器的最新日志而不必讀取整個(gè)日志文件
$ docker logs -ft yangfannie # -t為每條日志項(xiàng)加上時(shí)間戳
0x09 查看容器內(nèi)的進(jìn)程
除了容器的日志,我們也可以查看容器內(nèi)部運(yùn)行的進(jìn)程
$ docker top yangfannie
PID USER COMMAND
897 root /bin/sh -c while true; do echo hello world; sleep 1; done
1323 root sleep 1
0x10 在容器內(nèi)部運(yùn)行進(jìn)程
在容器內(nèi)運(yùn)行的進(jìn)程有兩種類型:后臺(tái)任務(wù)和交互式任務(wù)。后臺(tái)任務(wù)在容器內(nèi)運(yùn)行且沒(méi)有交互需求,而交互式任務(wù)則保持在前臺(tái)運(yùn)行。對(duì)于需要在容器內(nèi)部打開shell的任務(wù),交互式任務(wù)是很實(shí)用的。
- 在容器中運(yùn)行后臺(tái)任務(wù)
$ docker exec -d yangfannie touch /tmp/new_file
這里的-d標(biāo)志表明需要運(yùn)行一個(gè)后臺(tái)進(jìn)程,-d標(biāo)志之后,指定的是要在內(nèi)部執(zhí)行這個(gè)命令的容器的名字以及要執(zhí)行的命令。上面例子中的命令會(huì)在yangfannie容器內(nèi)創(chuàng)建了一個(gè)空文件,文件名為/tmp/new_file。通過(guò)docker exec后臺(tái)命令,我們可以在正在運(yùn)行的容器中進(jìn)行維護(hù)、監(jiān)控及管理任務(wù)。
- 在容器內(nèi)運(yùn)行交互命令
$ docker exec -t -i yangfannie /bin/bash
和運(yùn)行交互容器時(shí)一樣,這里的-t和-i標(biāo)志為我們執(zhí)行的進(jìn)程創(chuàng)建了TTY并捕捉STDIN。接著我們指定了要在內(nèi)部執(zhí)行這個(gè)命令的容器的名字以及要執(zhí)行的命令。在上面的例子中,這條命令會(huì)在yangfannie容器內(nèi)創(chuàng)建一個(gè)新的bash會(huì)話,有了這個(gè)會(huì)話,我們就可以在該容器中運(yùn)行其他命令了。
0x11 自動(dòng)重啟容器
如果由于某種錯(cuò)誤而導(dǎo)致容器停止運(yùn)行,我們還可以通過(guò)--restart標(biāo)志,讓Docker自動(dòng)重新啟動(dòng)該容器。--restart標(biāo)志會(huì)檢查容器的退出代碼,并據(jù)此來(lái)決定是否要重啟容器。默認(rèn)的行為是Docker不會(huì)重啟容器。
$ docker run --restart=always --name yangfannie -d centos /
bin/sh -c "while true; do echo hello world; sleep 1; done"
--restart標(biāo)志被設(shè)置為always。無(wú)論容器的退出代碼是什么,Docker都會(huì)自動(dòng)重啟該容器。除了always,我們還可以將這個(gè)標(biāo)志設(shè)為on-failure,這樣,只有當(dāng)容器的退出代碼為非0值的時(shí)候,才會(huì)自動(dòng)重啟。
--restart=on-failure:5 # 當(dāng)容器退出代碼為非0時(shí),Docker會(huì)嘗試自動(dòng)重啟該容器,最多重啟5次。
0x12 深入容器
除了通過(guò)docker ps命令獲取容器的信息,我們還可以使用docker inspect``來(lái)獲得更多的容器信息
$ docker inspect yangfannie
[{
"ID": "0a280c092d669c5d2c8cc60c0e43086933862578f244e352b704c850f66b0c44",
"Created": "2017-02-14T07:36:37.708334146Z",
"Path": "/bin/sh",
"Args": [
"-c",
"while true; do echo hello world; sleep 1; done"
],
"Config": {
"Hostname": "0a280c092d66",
. . .
docker inspect命令會(huì)對(duì)容器進(jìn)行詳細(xì)的檢查,然后返回其配置信息,包括名稱、命令、網(wǎng)絡(luò)配置以及很多有用的數(shù)據(jù)。
我們也可以用-f或者--format標(biāo)志來(lái)選定查看結(jié)果
- 返回容器的運(yùn)行狀態(tài)
$ docker inspect --format='{{ .State.Running }}' yangfannie
false
- 查看容器的IP地址
$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' yangfannie
172.17.0.3
- 查看多個(gè)容器
$ docker inspect --format '{{.Name}} {{.State.Running}}' yangfannie bob_container
/yangfannie false
/bob_container false
0x13 刪除容器
如果容器已經(jīng)不再使用,可以使用docker rm命令來(lái)刪除它們
$ docker rm yangfannie
需要注意的是,運(yùn)行中的Docker容器是無(wú)法刪除的。你必須先通過(guò)docker stop或docker kill命令停止容器,才能將其刪除。
如何一次性刪除所有容器?
$ docker rm `docker ps -a -q`
上面的docker ps命令會(huì)列出現(xiàn)有的全部容器,-a標(biāo)志代表列出所有容器,而-q標(biāo)志則表示只需要返回容器的ID而不會(huì)返回容器的其他信息。這樣我們就得到了容器ID的列表,并傳給了docker rm命令,從而達(dá)到刪除所有容器的目的。不過(guò)前提是這些容器都要是停止?fàn)顟B(tài)。