Docker是一套以容器技術爲核心的思想和一套標準化的體系,是IT領域的“集裝箱”
Hello Docker!
鏡像
-
鏡像可以理解爲一個打包了運行環境的特殊文件系統,它包含了容器啓動運行所需的所有信息,包括運行程序和配置數據等。
-
鏡像不包含任何動態數據,其內容在構建之後也不會改變。
-
例如,一個官方的Ubuntu14.04鏡像,就包含了一套完整的Ubuntu14.04最小系統的root文件系統。
容器
-
鏡像和容器的關係,類似於面向對象程序設計中的類和實例一樣,鏡像是靜態的定義,而容器是鏡像運行時的實體,可以看成是一個具備某個運行環境的非常輕量的虛擬機。
-
容器可以被創建、啓動、停止和刪除等。
-
在創建容器時,需要顯示地爲容器指定鏡像。指定鏡像之後,容器就具備了鏡像中保存的運行環境了。
-
例如,可以爲容器指定Ubuntu14.04的鏡像,然後該容器就具備Ubuntu14.04的運行環境了。
Docker Registry
倉庫(Repository)是集中存放鏡像的地方。另外一個非常相似的單詞Registry是註冊服務器(例如Docker Hub就是一個官方的Registry)。註冊服務器是管理倉庫的具體服務器,每個服務器上可以有多個倉庫,而每個倉庫下面有多個鏡像。
從這方面來說,倉庫可以被認爲是一個具體的項目或目錄。例如對於倉庫地址dl.dockerpool.com/ubuntu來說,dl.dockerpool.com 是註冊服務器地址,ubuntu 是倉庫名。(一般而言,一個倉庫會存放同一種類型的鏡像,例如ubuntu的倉庫。)
一個 Docker Registry 中可以包含多個倉庫(Repository);每個倉庫可以包含多個標籤(Tag);每個標籤對應一個鏡像。通常,一個倉庫會包含同一個軟件不同版本的鏡像會,而標籤就常用於對應該軟件的各個版本。我們可以通過 <倉庫名>:<標籤> 的格式來指定具體是這個軟件哪個版本的鏡像。如果不給出標籤,將以 latest 作爲默認標籤。
以 Ubuntu 鏡像爲例, ubuntu是倉庫的名字,其內包含有不同的版本標籤,比如,14.04 , 16.04 。我們可以通過 ubuntu:14.04 ,或者 ubuntu:16.04來具體指定所需哪個版本的鏡像。如果忽略了標籤,比如ubuntu ,那將視爲ubuntu:latest 。
倉庫名經常以兩段式路徑形式出現,比如training/webapp,前者往往意味着 Docker Registry 多用戶環境下的用戶名,後者則往往是對應的軟件名。
Docker使用的基本過程
-
獲取一個鏡像;
在安裝完Docker服務之後,本地是沒有任何鏡像的,所以首先需要獲取所需要的鏡像。 -
基於該鏡像創建並啓動一個容器;
在創建容器時也可以設置容器的啓動命令,該命令會在容器啓動時執行 -
進入該容器,執行“程序”。
- 在容器成功創建並啓動之後,該容器就具備了ubuntu的運行環境。我們可以進入該容器內部,並在其內部執行任何在ubuntu系統上的程序了。
- 這裏的“程序”可以是“Linux命令”、“shell腳本”、“C++程序”等。
示例
docker pull busybox:latest
docker run --name zgh busybox:latest echo "Hello Docker"
-
第一條命令:獲取一個名爲busybox:latest的鏡像。這條命令會從Docker Hub官方鏡像倉庫獲取一個名爲busybox:latest的鏡像(busybox的最新版),並把它下載到宿主機。其中busybox是最小的Linux系統。
-
第二條命令: 創建並啓動一個容器,並執行相應命令。首先,–name設置容器的名字爲zgh,然後爲容器指定了busybox:latest作爲啓動鏡像,最後設置了該容器的啓動命令爲echo “Hello Docker”。容器啓動並輸出 “Hello Docker”後,將其停止。
-
其實我們也可以去掉第一條命令,直接使用第二條命令即可完成同樣的功能。後臺在執行命令時,發現本地沒有busybox:latest鏡像,會首先自動執行docker pull busybox:latest,將busybox:latest鏡像下載到宿主機,然後再以busybox鏡像作爲基礎,創建一個名爲first_docker_container的鏡像,並執行echo “Hello Docker”命令。
拉取鏡像
在Docker的官方鏡像倉庫Docker Hub中保存了各種各樣的鏡像,這些鏡像中保存了各種各樣的運行環境。
例如包含Linux運行環境的“ubuntu”鏡像、“centos”鏡像、“busybox”鏡像等,提供數據庫服務的“mysql”鏡像、“o\fracle”鏡像、“redis”鏡像等。提供程序運行環境的“java”鏡像、“python”鏡像、“c++”鏡像等等。
基本上我們日常工作所需要的運行環境在Docker Hub中都會有對應的鏡像(Docker Hub官網:https://hub.docker.com/ )
(這些鏡像不是憑空出現的,這是鏡像構建者們辛勤的勞動成果。每一個Docker的使用者都應該感謝這些鏡像構建者們!!)
但是在安裝完Docker之後,本地是沒有任何鏡像的。下面介紹如何從Docker Hub中拉取鏡像(或者說下載鏡像)。
命令
docker pull [OPTIONS] <倉庫名>:<標籤>
- 默認情況下,使用
docker pull
命令,會從官方的Docker Hub庫中將鏡像拉取到本地。 docker pull
:Docker拉取鏡像的命令關鍵詞;[OPTIONS]
:命令選項;倉庫名
:倉庫名的格式一般爲<用戶名>/<軟件名>。對於Docker Hub,如果不指定用戶名,則默認爲library,即官方鏡像;標籤
:標籤是區分鏡像不同版本的一個重要參數,<倉庫名>:<標籤>會唯一確定一個鏡像。默認爲latest
(在Docker Hub中有很多個鏡像倉庫,一般情況下會將同一類型的鏡像放在同一個倉庫中,例如在一個ubuntu倉庫中由很多個ubuntu鏡像組成,包括ubuntu:14.04、ubuntu:16.04、ubuntu:latest等等鏡像)。
設置鏡像加速器
#以root用戶執行以下操作
mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{#下面的URL可以替換爲你自己的阿里雲加速地址
"registry-mirrors": ["https://jxus37ad.mirror.aliyuncs.com "]
}
EOF
systemctl daemon-reload
systemctl restart docker
由於“偉大的牆”的原因,在國內從Docker Hub中拉取鏡像的速度可能會比較慢,國內很多雲服務商都提供了鏡像加速器服務,例如阿里、網易等等。
以Linux系統配置阿里雲加速器爲例,只需要將下面的命令複製到Linux的終端,以root用戶的身份執行之後,就成功的配置了阿里雲加速器了!
啓動一個容器
啓動容器有兩種方式,
- 一種是基於鏡像新建一個容器並啓動,
- 另外一個是將在終止狀態(stopped)的容器重新啓動。
第一種方式:新建並啓動
docker run [OPTIONS] 鏡像名 [COMMAND] [ARG]
docker run
命令會基於指定的鏡像創建一個容器並且啓動它OPTIIONS
: 命令選項,最常用的包括-d
後臺運行容器並返回容器ID,-i
以交互模式運行容器,-t
爲容器分配一個僞輸入終端,--name
指定啓動容器的名稱。更多選項請參考Docker幫助文檔;鏡像名
: 以<倉庫名>:<標籤>
的方式來指定;COMMAND
: 設置啓動命令,該命令在容器啓動後執行;ARG
: 其他一些參數。
docker run背後的工作:
- 檢查本地是否存在指定的鏡像,不存在就從公有倉庫下載啓動;
- 利用鏡像創建並啓動一個容器;
- 分配一個文件系統,並在只讀的鏡像層外面掛載一層可讀寫層;
- 從宿主主機配置的網橋接口中橋接一個虛擬接口到容器中去;
- 從地址池配置一個ip地址給容器;
- 執行用戶指定的啓動命令;
- 執行完畢後容器被終止。
第二種方式:啓動一個已經終止的容器
雖然Docker容器是非常輕量的,這意味着一般情況下,我們在啓動完容器並完成操作之後都會將容器刪除掉。但是有些時候我們會進入之前創建的容器,而docker run每次都會創建一個新容器,顯然不符合我們的需求。這種時候,可以使用docker start命令,使用容器名或者容器id啓動一個已經終止的容器。
docker start [OPTIONS] 容器 [容器2...]
docker start
: Docker啓動容器的命令關鍵詞;OPTIIONS
: 命令選項;容器
: 需要啓動的容器,該容器用“容器ID”或“容器名”表示,如果指定了多個容器,那麼就將這些容器都啓動。
查看容器信息
docker ps
- 可以查看容器的信息,包括容器ID,基礎鏡像,啓動命令,創建時間,當前狀態,端口號,容器名字。
- 如果不加任何參數,只執行
docker ps
,將會顯示所有運行中的容器。
docker ps –a
- 可以查看Docker環境中所有的容器,包括已經停止的容器。
停止一個容器
docker stop [OPTIONS] Container [Container ...]
docker stop
: Docker停止容器的命令關鍵詞;OPTIONS
:命令選項,其中-t
指定等待多少秒後如果容器還沒終止,就強行停止,默認等待10秒;Container
:需要啓動的容器,該容器用“容器ID”或“容器名”表示,如果指定了多個容器,那麼就將這些容器都啓動。
實際工作中,執行docker stop可能並不會立即終止容器,而是需要等待一段時間。
前面我們說過,容器實際上是一個進程。而執行docker stop之後,首先會向容器的主進程發送一個SIGTERM信號,讓主進程釋放資源保存狀態,嘗試自己終止。
但當等待時間超過了-t設置的時間後,會向容器的主進程發送一個SIGKILL信號,使容器立即終止。
在什麼情況下容器啓動後會立即終止?
實際情況中,除了使用docker stop命令來強制地終止一個容器以外,當容器的啓動命令終結時,容器也自動會終止。
之前我們介紹過Docker容器是一個進程,實際上它以sh作爲主進程。如果主進程停止了,那麼容器也就停止了。
而如果容器的“啓動命令”執行完之後,由於主進程沒有命令繼續執行,所以主進程會停止,容器也就因此而停止了
如何才能使容器啓動後不立即終止?
如果容器的sh主進程不停止,是不是以爲這容器就不會停止?答案是肯定的。因此,如果使啓動命令不能執行完畢,或者在執行完啓動命令後,容器的sh主進程不停止,那麼容器在啓動後就不會立即終止了!
將啓動命令設置爲“啓動一直運行的子進程
docker run --name zgh -it ubuntu /bin/bash
執行完這條命令後,創建並啓動容器之後,執行/bin/bash,會啓動一個子進程,此時父進程(也就是容器的主進程sh)會進入sleep狀態,由於sleep狀態不是終止狀態,所以容器會繼續運行。
爲什麼在容器中輸入exit或者執行ctrl D後,容器將會終止呢,這是因爲exit會退出(結束)當前進程,也就是/bin/bash,由於子進程結束,sh主進程恢復到運行態,然而由於沒有命令需要繼續執行,所以sh主進程結,因此容器終止。
進入一個容器
有些時候,需要讓容器在後臺運行而不是直接把“啓動命令”的結果輸出在當前宿主機下。此時,可以通過添加-d參數來實現。
舉個例子,假如不使用-d參數執行下面這條命令:
docker run ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
那麼會一直在控制檯輸出hello world,如下圖所示:
docker run ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
hello world
hello world
hello world
...
但是如果使用了-d參數,此時容器會在後臺運行並且不會將輸出結果輸出到控制檯。如下圖所示:
docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
ccd644424bffed71747e2a36977d70745cc211e7dac71006437ca52914c1b743
返回了容器ID,
一般情況下,前4位就能唯一標識一個容器了
如上,ccd6
就是代表容器ID以ccd6
開頭的容器
進入一個docker容器的幾種方法
- 使用ssh登陸進容器;
- 使用nsenter、nsinit等第三方工具;
- 使用docker本身提供的工具。
Docker目前主要提供了docker attach
和docker exec
兩個命令。
docker attach containerId|containerName
- 一般情況下,輸入容器ID的前4位即可
- 使用
docker attach
進入了該容器內部,實際上就是進入容器“啓動命令”的終端
docker exec [options] containerName|containerId command [arg]
- docker exec命令可以在一個運行的容器內部執行一條命令,
- 例如執行
docker exec aec0 mkdir dir1
後,就在容器中創建了一個dir1的文件夾。 - 補充:
aeco
代表容器ID - 除此以外,還可以在容器中啓動一個新的bash,例如執行了
docker exec -it aec0 /bin/bash
,在容器內部啓動了一個新的bash終端,並使用-it爲其分配一個僞終端綁定到標準輸出上。
attach與exec的主要區別
- attach直接進入容器“啓動命令”的終端,不會啓動新的進程;
- exec則是在容器中打開新的終端,並且可以啓動新的進程;
- 如果想直接在終端中查看容器“啓動命令”的輸出,用attach;其他情況使用exec。
刪除容器
隨着我們創建的容器越來越多,容器的管理越來越難。而且一般情況下,容器都是在使用完成後立即就刪除掉,這時候就需要用到“刪除容器”了。
刪除一個處於終止狀態的容器
docker rm containName|containId
- 請留意,在不加任何參數的情況下,docker rm只能刪除處於終止狀態的容器。
刪除一個正在運行的容器
有兩種方式:
- 第一種,先執行
docker stop
停止該容器,然後使用docker rm
刪除掉; - 第二種,執行
docker rm –f
命令,強制刪除。
刪除所有處於終止狀態的容器
docker rm $(docker ps -a -q)
我們知道docker ps –a
命令可以查看所有容器的信息。而docker ps –a –q
只查看所有容器的containerId。
在Linux中,將命令放在$()
中,會執行命令並返回命令的執行結果。因此$( docker ps -a -q)
會返回所有容器的container id,
而docker rm只能幹掉終止的容器,而如果用docker rm刪除正在運行的容器時,將不能刪除掉。
所以可以使用docker rm $(docker ps -a -q)
來刪除所有處於終止狀態的容器。