【學習筆記】Docker基礎實戰教程一:入門

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使用的基本過程

  1. 獲取一個鏡像;
    在安裝完Docker服務之後,本地是沒有任何鏡像的,所以首先需要獲取所需要的鏡像。

  2. 基於該鏡像創建並啓動一個容器;
    在創建容器時也可以設置容器的啓動命令,該命令會在容器啓動時執行

  3. 進入該容器,執行“程序”。

    • 在容器成功創建並啓動之後,該容器就具備了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背後的工作:

  1. 檢查本地是否存在指定的鏡像,不存在就從公有倉庫下載啓動;
  2. 利用鏡像創建並啓動一個容器;
  3. 分配一個文件系統,並在只讀的鏡像層外面掛載一層可讀寫層;
  4. 從宿主主機配置的網橋接口中橋接一個虛擬接口到容器中去;
  5. 從地址池配置一個ip地址給容器;
  6. 執行用戶指定的啓動命令;
  7. 執行完畢後容器被終止。

第二種方式:啓動一個已經終止的容器

雖然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容器的幾種方法

  1. 使用ssh登陸進容器;
  2. 使用nsenter、nsinit等第三方工具;
  3. 使用docker本身提供的工具。

Docker目前主要提供了docker attachdocker 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)來刪除所有處於終止狀態的容器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章