程式扎記: Docker Practice - Container

標籤

2015年5月18日 星期一

Docker Practice - Container

(Source)

Docker 容器#

(P27) 簡單的說,容器是獨立執行的一個或一組應用,以及它們的執行態環境。換句話說,虛擬機可以理解為模擬執行的一整套作業系統(提供了執行態環境和其他系統環境)和跑在上面的應用。本章將具體介紹如何來管理一個容器,包括建立、啟動和停止等。

啟動容器#

啟動容器有兩種方式,一種是將映像檔新建一個容器並啟動,另外一個是將終止狀態(stopped)的容器重新啟動。因為 Docker 的容器實在太輕量級了,使用者可以隨時刪除和新建立容器。

新建並啟動#

所需要的命令主要為 docker run :
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Run a command in a new container...
-d, --detach=false         Run container in background and print container ID-i, --interactive=false    Keep STDIN open even if not attached...
-t, --tty=false            Allocate a pseudo-TTY

例如下面的命令輸出一個 “Hello World”,之後終止容器:
# docker run ubuntu:14.04 /bin/echo 'Hello World' 
Hello World

這跟在本地直接執行 /bin/echo 'hello world' 相同, 幾乎感覺不出任何區別。

下面的命令則啟動一個 bash 終端,允許使用者進行互動。
// -t 選項讓Docker分配一個虛擬終端(pseudo-tty)並綁定到容器的標準輸入上, 
// -i 則讓容器的標準輸入保持打開。 

# docker run -t -i ubuntu:14.04 /bin/bash 
root@11ddb79d2533:/# // "11ddb79d2533" 為 Container ID

在互動模式下,使用者可以透過所建立的終端來輸入命令:
# uname -a 
#40~14.04.1-Ubuntu SMP Thu Jan 15 17:43:14 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux 
# cat /proc/meminfo | grep MemTotal 
MemTotal: 2042644 kB

當利用 docker run 來建立容器時,Docker 在後臺執行的標準操作包括:
  1. 檢查本地是否存在指定的映像檔,不存在就從公有倉庫下載
  2. 利用映像檔建立並啟動一個容器
  3. 分配一個檔案系統,並在唯讀的映像檔層外面掛載一層可讀寫層
  4. 從宿主主機設定的網路橋界面中橋接一個虛擬埠到容器中去
  5. 從位址池中設定一個 ip 位址給容器
  6. 執行使用者指定的應用程式
  7. 執行完畢後容器被終止

啟動已終止容器#

可以利用 docker start 命令,直接將一個已經終止的容器啟動執行。
Usage: docker start [OPTIONS] CONTAINER [CONTAINER...]
Start one or more stopped containers

  -a, --attach=false         Attach STDOUT/STDERR and forward signals
  -i, --interactive=false    Attach container's STDIN

一個使用範例如下:
# docker ps -a // 檢視目前的 Container 清單 
... 
1ecea0251a6c ubuntu:14.04 bash 3 minutes ago Exited (127) 34 seconds ago agitated_pasteur 
... 

# docker start 1ecea0251a6c // 重新啟動 Container ID='1ecea0251a6c' 
1ecea0251a6c 
# docker ps -a | grep 1ecea0251a6c // 確認 Container 已經啟動 
1ecea0251a6c ubuntu:14.04 bash 7 minutes ago Up 7 seconds agitated_pasteur

容器的核心為所執行的應用程式,所需要的資源都是應用程式執行所必需的。除此之外,並沒有其它的資源。可以在虛擬終端中利用 ps 或 top 來查看程式訊息。


可見,容器中僅執行了指定的 bash 應用。這種特點使得 Docker 對資源的使用率極高,是貨真價實的輕量級虛擬化。

守護態執行#

更多的時候,需要讓 Docker 容器在後臺以守護態(Daemonized)形式執行。此時,可以透過新增 -d 參數來實作。

例以下面的命令會在後臺執行容器:
# docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done" 
ca58ebb0ce8bc6f7ecb03b6026fa1599e50bdb0793ec730ce9ce750fdf0d0f30 
# docker ps 
... 
ca58ebb0ce8b ubuntu:14.04 /bin/sh -c 'while tr 15 seconds ago Up 13 seconds kickass_pike
... 

要取得容器的輸出訊息,可以透過 docker logs 命令:
Usage: docker logs [OPTIONS] CONTAINER
Fetch the logs of a container

  -f, --follow=false        Follow log output
  -t, --timestamps=false    Show timestamps
  --tail="all"              Number of lines to show from the end of the logs

延續前面的執行結果, 列出 Container 名稱為 "kickass_pike" 的輸出:
# docker logs kickass_pike 
... 
hello world 
hello world 

終止容器#

可以使用 docker stop 來終止一個執行中的容器:
Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
Stop a running container by sending SIGTERM and then SIGKILL after a
grace period

  -t, --time=10      Seconds to wait for stop before killing it

此外,當 Docker 容器中指定的應用終結時,容器也自動終止。 例如對於上一章節中只啟動了一個終端機的容器,使用者透過 exit 命令或 Ctrl+d 來退出終端時,所建立的容器立刻終止。終止狀態的容器可以用 docker ps -a 命令看到。

處於終止狀態的容器,可以透過 docker start 命令來重新啟動; 此外, docker restart 命令會將一個執行中的容器終止,然後再重新啟動它。

進入容器#

(P32) 在使用 -d 參數時,容器啟動後會進入後臺。 某些時候需要進入容器進行操作,有很多種方法,包括使用 docker attach 命令或 nsenter 工具等。

exec 命令#

你可以使用 docker exec 在已經運行的容器中執行命令:
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Run a command in a running container

  -d, --detach=false         Detached mode: run command in the background
  -i, --interactive=false    Keep STDIN open even if not attached
  -t, --tty=false            Allocate a pseudo-TTY

下面示範如何使用該命令:
# docker run -idt ubuntu 
243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550 
# docker ps 
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS 
243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds 

# docker exec -ti nostalgic_hypatia bash 
root@243c32535da7:/#

attach 命令#

你可以使用 docker attach 命令連接到一個已經運行的容器, 並取得 Interactive Shell:
Usage: docker attach [OPTIONS] CONTAINER
Attach to a running container

  --no-stdin=false    Do not attach STDIN
  --sig-proxy=true    Proxy all received signals to the process

一個使用範例如下:
# docker ps // 檢視目前運行的 Container 
... 
30f2728741da ubuntu:14.04 /bin/sh -c 'while tr 3 hours ago Up 3 hours jolly_yonath 
... 

# docker attach jolly_yonath 
hello world 
hello world 
...
 
但是使用 attach 命令有時候並不方便。當多個窗口同時 attach 到同一個容器的時候,所有窗口都會同步顯示。當某個窗口因命令阻塞時,其他窗口也無法執行操作了。

nsenter 命令#

nsenter 工具已含括在 util-linux 2.23 後的版本內。 如果系統中 util-linux 包沒有該命令,可以按照下面的方法從原始碼安裝:
# cd /tmp 
# wget https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz 
# tar -xvf util-linux-2.24.tar.gz 
# cd util-linux-2.24 
# ./configure --without-ncurses 
# make nsenter && sudo cp nsenter /usr/local/bin

nsenter 可以存取另一個程式的命名空間, 但需要有 root 權限。為了連接到容器,你還需要找到容器的第一個程式的 PID,可以透過下面的命令取得:
# docker ps -q // 檢視目前運行的 Container ID 
360d8fdc88ba 
1ecea0251a6c 

# PID=$(docker inspect --format " .State.Pid " 360d8fdc88ba) 
# ps aux | grep $PID 
root 38733 0.0 0.1 18156 3048 pts/24 Ss+ 05:35 0:00 /bin/bash

透過這個 PID,就可以連接到這個容器:
// -t, --target  target process to get namespaces from 
// -m, --mount [=] enter mount namespace 
// -u, --uts [=] enter UTS namespace (hostname etc) 
// -n, --net [=] enter network namespace 
// -p, --pid [=] enter pid namespace 

# nsenter --target $PID --mount --uts --ipc --net --pid 
root@360d8fdc88ba:/#

建議大家下載 .bashrc_docker,並將內容放到 .bashrc 中。
# wget -P https://github.com/yeasy/docker_practice/raw/master/_local/.bashrc_docker 
# echo "[ -f ~/.bashrc_docker ] && . ~/.bashrc_docker" >> ~/.bashrc; source ~/.bashrc

這個檔案中定義了很多方便使用 Docker 的命令,例如 docker-pid 可以取得某個容器的 PID;而 docker-enter 可以進入容器或直接在容器內執行命令。
# docker ps -q 
360d8fdc88ba 
1ecea0251a6c 

# docker-pid 360d8fdc88ba 
38733 
# docker-enter 360d8fdc88ba ls 
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

匯出和匯入容器#

匯出容器#

如果要匯出本地某個容器,可以使用 docker export 命令:
Usage: docker export [OPTIONS] CONTAINER
Export the contents of a filesystem to a tar archive (streamed to STDOUT by default)

  -o, --output=""    Write to a file, instead of STDOUT

  Produces a tarred repository to the standard output stream.

下面範例將匯出容器快照到本地檔案:
# docker ps -a // 檢視現有容器 
# docker export ca58ebb0ce8b > ubuntu.tar // 倒出容器 ID='ca58ebb0ce8b' 到本地檔案 'ubuntu.tar' 

匯入容器快照#

可以使用 docker import 從容器快照檔案中再匯入為映像檔:
Usage: docker import URL|- [REPOSITORY[:TAG]]
Create an empty filesystem image and import the contents of the
tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then
optionally tag it.

  -c, --change=[]     Apply specified Dockerfile instructions while importing the image

一個使用範例如下:
# cat ubuntu.tar | docker import - test/ubuntu:v1.0 
858be488b0528ffd149581b4765c7d68f6e0246e6938b12b182e23e7485d1063 
# docker images | grep test/ubuntu 
test/ubuntu v1.0 858be488b052 35 seconds ago 192.5 MB 

此外,也可以透過指定 URL 或者某個目錄來匯入,例如:
# docker import http://example.com/exampleimage.tgz example/imagerepo

*註:使用者既可以使用 docker load 來匯入映像檔儲存檔案到本地映像檔庫,也可以使用 docker import 來匯入一個容器快照到本地映像檔庫。這兩者的區別在於容器快照檔案將丟棄所有的歷史記錄和原始資料訊息(即僅保存容器當時的快照狀態),而映像檔儲存檔案將保存完整記錄,檔案體積也跟著變大。此外,從容器快照檔案匯入時可以重新指定標籤等原始資料訊息。

刪除容器#

可以使用 docker rm 來刪除一個處於終止狀態的容器:
Usage: docker rm [OPTIONS] CONTAINER [CONTAINER...]
Remove one or more containers

  -f, --force=false      Force the removal of a running container (uses SIGKILL)
  -l, --link=false       Remove the specified link
  -v, --volumes=false    Remove the volumes associated with the container

一個使用範例如下:
# docker ps -a | grep johnklee/tutorial:v2 // 檢視要被刪除的 Container 
5af0078144aa johnklee/tutorial:v2 /bin/bash 5 days ago Exited (0) 5 days ago loving_lumiere 
# docker rm loving_lumiere 
loving_lumiere

如果要刪除一個執行中的容器,可以新增 -f 參數。Docker 會發送 SIGKILL 信號給容器。

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!