容器是鏡像的一個運行實例。所不同的是,鏡像是靜態的只讀文件,而容器帶有運行時需要的可寫文件層。如果認為虛擬機是模擬運行的一整套操作系統(包括內核、應用運行態環境和其他系統環境)和跑在上面的應用,那么Docker容器就是獨立運行的一個(或一組)應用,以及它們必需的運行環境。
創建容器
對容器進行操作就跟直接操作應用一樣簡單、快速。Docker容器實在太輕量級了,用戶可以隨時創建或刪除容器。
1.新建容器
可以使用docker create命令新建一個容器,例如:
$ docker create -it ubuntu:latest
$ docker ps -a
使用docker create命令新建的容器處於停止狀態,可以使用docker start命令來啟動它。
create命令和后續的run命令支持的選項都十分復雜,主要包括如下幾大類:與容器運行模式相關、與容器和環境配置相關、與容器資源限制和安全保護相關。
create命令與容器運行模式相關的選項:
create命令與容器環境和配置相關的選項:
create命令與容器資源限制和安全保護相關的選項:
其他比較重要的選項還包括:
·-l,--label=[]:以鍵值對方式指定容器的標簽信息;
·--label-file=[]:從文件中讀取標簽信息。
2.啟動容器
使用docker start命令來啟動一個已經創建的容器,例如啟動剛創建的ubuntu容器:
$ docker start containerId
$ docker stop containerId
3.新建並啟動容器
除了創建容器后通過start命令來啟動,也可以直接新建並啟動容器。所需要的命令主要為docker run,等價於先執行docker create命令,再執行docker start命令。
例如,下面的命令輸出一個“Hello World”,之后容器自動終止:$ docker run ubuntu /bin/echo 'Hello world'
這跟在本地直接執行/bin/echo'hello world'幾乎感覺不出任何區別。
當利用docker run來創建並啟動容器時,Docker在后台運行的標准操作包括:
- 檢查本地是否存在指定的鏡像,不存在就從公有倉庫下載;
- 利用鏡像創建一個容器,並啟動該容器;
- 分配一個文件系統給容器,並在只讀的鏡像層外面掛載一層可讀寫層;
- 從宿主主機配置的網橋接口中橋接一個虛擬接口到容器中;
- 從網橋的地址池配置一個IP地址給容器;
- 執行用戶指定的應用程序;
- 執行完畢后容器被自動終止。
下面的命令啟動一個bash終端,允許用戶進行交互:
$ docker run -it ubuntu:14.04 /bin/bash
root@af8bae53bdd3:/#
其中,-t選項讓Docker分配一個偽終端(pseudo-tty)並綁定到容器的標准輸入上,-i則讓容器的標准輸入保持打開。
更多的命令選項可以通過man docker-run命令來查看。在交互模式下,用戶可以通過所創建的終端來輸入命令。
在容器內用ps命令查看進程,可以看到,只運行了bash應用,並沒有運行其他無關的進程。
對於所創建的bash容器,當使用exit命令退出之后,容器就自動處於退出(Exited)狀態了。這是因為對Docker容器來說,當運行的應用退出后,容器也就沒有繼續運行的必要了。
某些時候,執行docker run會出錯,因為命令無法正常執行容器會直接退出,此時可以查看退出的錯誤代碼。
默認情況下,常見錯誤代碼包括:
·125:Docker daemon執行出錯,例如指定了不支持的Docker命令參數;
·126:所指定命令無法執行,例如權限出錯;
·127:容器內命令無法找到。
命令執行后出錯,會默認返回錯誤碼。
4.守護態運行
更多的時候,需要讓Docker容器在后台以守護態(Daemonized)形式運行。此時,可以通過添加-d參數來實現。
例如下面的命令會在后台運行容器:
$ docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
ce554267d7a4c34eefc92c5517051dc37b918b588736d0823e4c846596b04d83
容器啟動后會返回一個唯一的id,也可以通過docker ps命令來查看容器信息
此時,要獲取容器的輸出信息,可以如下使用docker logs命令:$ docker logs containerId
終止容器
可以使用docker stop來終止一個運行中的容器。該命令的格式為docker stop [-t|--time[=10]] [CONTAINER...]。
首先向容器發送SIGTERM信號,等待一段超時時間(默認為10秒)后,再發送SIGKILL信號來終止容器:$ docker stop containerId
docker kill命令會直接發送SIGKILL信號來強行終止容器。
此外,當Docker容器中指定的應用終結時,容器也會自動終止。例如對於上一節中只啟動了一個終端的容器,用戶通過exit命令或Ctrl+d來退出終端時,所創建的容器立刻終止,處於stopped狀態。
可以用docker ps -qa命令看到所有容器的ID。例如:$ docker ps -qa
處於終止狀態的容器,可以通過docker start命令來重新啟動:$ docker start containerId
docker restart命令會將一個運行態的容器先終止,然后再重新啟動它:$ docker restart containerId
進入容器
在使用-d參數時,容器啟動后會進入后台,用戶無法看到容器中的信息,也無法進行操作。
這個時候如果需要進入容器進行操作,有多種方法,包括使用官方的attach或exec命令,以及第三方的nsenter工具等。
1.attach命令
attach是Docker自帶的命令,命令格式為:
docker attach [--detach-keys[=[]]] [--no-stdin] [--sig-proxy[=true]] CONTAINER
支持三個主要選項:
·--detach-keys[=[]]:指定退出attach模式的快捷鍵序列,默認是CTRL-p CTRL-q;
·--no-stdin=true|false:是否關閉標准輸入,默認是保持打開;
·--sig-proxy=true|false:是否代理收到的系統信號給應用進程,默認為true。
下面示例如何使用該命令:$ docker run -itd ubuntu
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia
$ docker attach nostalgic_hypatia
root@243c32535da7:/#
但是使用attach命令有時候並不方便。當多個窗口同時用attach命令連到同一個容器的時候,所有窗口都會同步顯示。當某個窗口因命令阻塞時,其他窗口也無法執行操作了。
2.exec命令
Docker從1.3.0版本起提供了一個更加方便的exec命令,可以在容器內直接執行任意命令。該命令的基本格式為:
docker exec [-d|--detach] [--detach-keys[=[]]] [-i|--interactive] [--privileged] [-t|--tty] [-u|--user[=USER]] CONTAINER COMMAND [ARG...]。
比較重要的參數有:
·-i,--interactive=true|false:打開標准輸入接受用戶輸入命令,默認為false;
·--privileged=true|false:是否給執行命令以高權限,默認為false;
·-t,--tty=true|false:分配偽終端,默認為false;
·-u,--user="":執行命令的用戶名或ID。
例如進入到剛創建的容器中,並啟動一個bash:
$ docker exec -it 243c32535da7 /bin/bash
root@243c32535da7:/#
可以看到,一個bash終端打開了,在不影響容器內其他應用的前提下,用戶可以很容易與容器進行交互。
通過指定-it參數來保持標准輸入打開,並且分配一個偽終端。通過exec命令對容器執行操作是最為推薦的方式。
3.nsenter工具
在util-linux軟件包版本2.23+中包含nsenter工具。如果系統中的util-linux包沒有該命令,可以按照下面的方法從源碼安裝:
$ cd /tmp; curl https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz | tar -zxf-; cd util-linux-2.24;
$ ./configure --without-ncurses
$ make nsenter && cp nsenter /usr/local/bin
為了使用nsenter連接到容器,還需要找到容器進程的PID,可以通過下面的命令獲取:PID=$(docker inspect --format "{{ .State.Pid }}" )
通過這個PID,就可以連接到這個容器:$ nsenter --target $PID --mount --uts --ipc --net --pid
下面給出一個完整的例子,通過nsenter命令進入容器:
$ docker run -idt ubuntu 243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia $ PID=$(docker-pid 243c32535da7) 10981 $ nsenter --target 10981 --mount --uts --ipc --net --pid root@243c32535da7:/#
刪除容器
可以使用docker rm命令來刪除處於終止或退出狀態的容器,命令格式為docker rm[-f|--force][-l|--link][-v|--volumes]CONTAINER[CONTAINER...]。
主要支持的選項包括:
·-f,--force=false:是否強行終止並刪除一個運行中的容器;
·-l,--link=false:刪除容器的連接,但保留容器;
·-v,--volumes=false:刪除容器掛載的數據卷。
例如,查看處於終止狀態的容器,並刪除:
$ docker ps -a
$ docker rm ce554267d7a4
默認情況下,docker rm命令只能刪除處於終止或退出狀態的容器,並不能刪除還處於運行狀態的容器。
如果要直接刪除一個運行中的容器,可以添加-f參數。Docker會先發送SIGKILL信號給容器,終止其中的應用,之后強行刪除,如下所示:
$ docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world;sleep 1; done" 2aed76caf8292c7da6d24c3c7f3a81a135af942ed1707a79f85955217d4dd594 $ docker rm 2ae Error response from daemon: You cannot remove a running container. Stop the container before attempting removal or use -f 2016/07/03 09:02:24 Error: failed to remove one or more containers $ docker rm -f 2ae 2ae
導入和導出容器
某些時候,需要將容器從一個系統遷移到另外一個系統,此時可以使用Docker的導入和導出功能。這也是Docker自身提供的一個重要特性。
1.導出容器
導出容器是指導出一個已經創建的容器到一個文件,不管此時這個容器是否處於運行狀態,可以使用docker export命令。
該命令的格式為docker export [-o|--output[=""]] CONTAINER。其中,可以通過-o選項來指定導出的tar文件名,也可以直接通過重定向來實現。
首先查看所有的容器,如下所示:
$ docker ps -a
分別導出ce554267d7a4容器和e812617b41f6容器到文件test_for_run.tar文件和test_for_stop.tar文件:
$ docker export -o test_for_run.tar ce5
$ docker export e81 >test_for_stop.tar
之后,可將導出的tar文件傳輸到其他機器上,然后再通過導入命令導入到系統中,從而實現容器的遷移。
2.導入容器
導出的文件又可以使用docker import命令導入變成鏡像,該命令格式為:
docker import [-c|--change[=[]]] [-m|--message[=MESSAGE]] file|URL|-[REPOSITORY[:TAG]]
用戶可以通過-c,--change=[]選項在導入的同時執行對容器進行修改的Dockerfile指令。
下面將導出的test_for_run.tar文件導入到系統中:
$ docker import test_for_run.tar - test/ubuntu:v1.0
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB
實際上,既可以使用docker load命令來導入鏡像存儲文件到本地鏡像庫,也可以使用docker import命令來導入一個容器快照到本地鏡像庫。
這兩者的區別在於容器快照文件將丟棄所有的歷史記錄和元數據信息(即僅保存容器當時的快照狀態),而鏡像存儲文件將保存完整記錄,體積也更大。此外,從容器快照文件導入時可以重新指定標簽等元數據信息。