【Day 3】Docker Container(容器)與 Volume(數據卷)


# 前言

本篇分為:「關於容器」、「容器常用操作」、「留下容器資料:數據卷(Volume)以及掛載宿主目錄/文件」三部分。

首先回憶一下前面提過的容器概念,再進入容器相關指令的解析。最後介紹 volume 以及宿主目錄的掛載,可用於留存執行過程中產生的容器資料,避免資料伴隨容器的移除一起消失。


# 關於容器

關於什麼是容器、為什麼需要容器、它跟虛擬機有什麼不同等困惑,已在【Day 1】Docker 基本概念中釐清過。

容器的本質就是程序(porcess)。
——《Docker——從入門到實踐》

簡單來說,容器的目的就是要在一個獨立的環境(沙盒),執行一項應用程式,而容器的本質就是一個程序(應用程式),當程序執行結束,容器的任務就結束,便退出執行(Exit)

容器環境的獨立性,就如同 Docker 鯨魚上頭承載的那一盒盒貨櫃箱(containers),貨櫃間彼此獨立,同時與底部乘載它的鯨魚區隔(以宿主為基礎,但與宿主環境區隔,每個容器擁有自己獨立的檔案系統、網路配置等等)。

上一篇提到 image 是容器的樣版,包含了應用程式執行所需的完整條件(即包含除了底層 kernel 以外,像是應用程式、依賴庫等資源)。當 docker run 指令把 image 實體化為容器時,會以這個靜態樣板 image(唯讀)為基礎疊加一層容器存儲層(可讀寫),用以存儲任何容器執行期間產生的資料,另外再加上一些容器相關配置(如容器與宿主機器間的接口、容器的 IP 位址等等)。

馬上來看看怎麼操作容器吧。


# 容器常用的操作:

  1. 啟動容器(把 image 實體化)----- docker run
  2. 手動終止容器的運行 --------------- docker stop
  3. 再次啟動已終止的容器 ------------- docker start
  4. 重新啟動執行中的容器 ------------- docker restart(= stop + start)
  5. 進入運行中的容器進行操作 ------------ docker exec -itdocker attach
  6. 列出(所有)本地容器 ------------- docker ps (-a)
  7. (強制)刪除容器 ------------------ docker rm (-f)
  8. 一鍵移除所有已終止的容器 -------- $ docker container prune

1. 啟動容器(把 image 實體化):$ docker run <image>

  • 當下令 docker run 時,docker 背後做了一系列操作:
    (擷取自《Docker——從入門到實踐》)

    1. 檢查本地是否有指定的<image>,若無就從 registry 下載
    2. 用<image>建立一個容器,並啟動
    3. 分配一個檔案系統,在 image(唯讀)外頭 mount 一層容器存儲層(可讀寫)
    4. 從宿主主機配置的網橋接口中橋接一個虛擬接口到容器去
    5. 從地址持配置一個 ip 位址給容器
    6. 執行指定的應用程序
    7. 執行完畢後,容器被終止
  • docker run 可分為 foreground(前台)和 detached(分離)兩種模式。

    • foreground(預設):
      • 直接在當前宿主機上執行,並印出執行過程的 logs。
    • detached(加上 -d 參數 docker run -d <image>):
      • 僅回傳所運行的 container_ID,不會印出 logs。
      • 欲查看 logs,則要用 docker container logs <container>
    • 無論是以 foreground 或 detached 方式執行,容器都會在指定的程序執行結束時,自動終止運作。

2. 手動終止容器的運行:$ docker stop <container>

  • 終止容器運行,但所做變更仍存在於容器存儲層。

3. 再次啟動已終止的容器:$ docker start <container>

  • 把終止狀態的 container 再次執行。

A stopped container can be restarted with all its previous changes intact using docker start.
—— 官網(docker run)

  • docker start 再次執行已終止的 container 時,之前對 container 的變更都還會完整存在。因預設情況下,變更會存在掛載的容器存儲層,容器終止時,容器存儲層仍存在。直到 container 被移除(docker rm <container>),容器存儲層才會跟著 container 一起被移除。

4. 重新啟動執行中的容器:$ docker restart <container>

  • 等同於 docker stop + docker start。重新啟動不會移除之前對 container 的變更(跟 docker start 一樣)。

5. 進入運行中的容器進行操作: $ docker exec -it <running_state_container>$ docker attach <running_state_container>

(1) docker exec -it

  • 指令說明(docker 的參數是可以合併的,故-it 等同於 -i -t):

    • -i(--interactive):容器的輸出會接到當前的畫面(我們才得以看到命令執行結果)。
    • -t(--tty):pseudo tty(pseudo terminal,偽終端),允許我們發送 input 給容器。
  • 指令意涵:

    • 在執行中的容器上,開新一個程序。像是 docker exec -it <container_ID> bash 就是在運行中的容器裡,執行 bash。
    • 這時若使用 exit 離開/終止,僅終止 exec 所執行的程序(像是 bash),不是終止容器。所以從 exec exit 不會中斷容器的運作
  • 範例:

$ docker run -dit ubuntu              # -d 在背後執行容器;-it 容器互動模式
$ docker exec -it <container> bash    # 進入容器
root@container_ID:/#

(2) docker attach

  • 指令意涵:

    • attach(附加)」標準輸入、標準輸出、標準錯誤到執行中的 container,而不是在上面另開新程序
    • 若使用 exit 離開/終止,則會終止 container。因為是進入到執行中的 container 用 attach 的標準輸入輸出與它互動
    • 一個運作中的 container 只能被 attach 一次(只會有一個標準輸入輸出的實體)。即使一次從多個終端 attach,也只會看到相同的輸出(其中一個終端輸入的命令卡住,所有終端都卡住)。
  • 官方範例:

$ docker run -d --name topdemo ubuntu /usr/bin/top -b
$ docker attach topdemo
root@container_ID:/#

6. 列出(所有)本地容器:$ docker ps (-a)

  • -a(--all):列出所有容器(包含終止狀態的)

7.(強制)刪除容器:$ docker rm (-f) <container>

  • -f(--force):強制刪除執行中的容器(docker 會發送 SIGKILL 信號給容器,kill process 後再刪除)
  • docker rm <container> 把容器從本地移除時,此容器的存儲層就會一起被刪掉。

8. 一鍵移除所有已終止的容器:$ docker container prune

  • 因為輕量的容器很方便創建,一不注意本地可能堆積一些已終止且用不到的容器,docker container prune可一鍵清理掉。
  • 效果等同於把它們一個個分別 docker rm

# 留下容器資料:數據卷(Volume)以及掛載宿主目錄/文件

容器創建時,在唯讀的 Image 上會疊加一層可讀寫的容器存儲層,用來記錄運行過程中讀寫的資料。然而容器存儲層會隨著 docker rm <container> 而一起被刪除。

為了保存容器執行過程中產生的資料,可以指定把資料存在 Volume 或指定的宿主機目錄。

#1 數據卷(Volume)

Volume 其實就類似 mount 宿主機的目錄/文件,一個 volume 也可以被多個容器共享。只是比起 mount 宿主目錄/文件,docker 還提供 volume 其他功能或指令,方便管理。

Volume 相關指令:

  1. 創建一個 volume -------------------------- docker volume create <volume_name>
  2. 查看所有 volumes ------------------------- docker volume ls
  3. 查看特定 volume 的詳情 ------------------ docker volume inspect <volume_name>
  4. 運行容器時,把 volume 掛載到容器上 --- docker run 時加入 --mount-v
    docker run -d -P \
     --name <container_name>
     --mount source=<volume_name>,target=<target_path_in_container> \
     # 或 -v <volume_name>:<target_path_in_container> \
     <image> \
     <command>
    
  5. 藉由容器查看掛載的 volume --------------- docker inspect <container>(裡頭的 Mounts 項目)
  6. 清除所有無主的 volumes ------------------- docker volume prune
    (※無主:建立了,但沒被掛載在任何容器上)
  7. 移除容器的同時移除掛載的 volume -------- docker rm -v <container>

#2 掛載宿主機目錄/文件

掛載的方式與 volume 差不多:

docker run -d -P \
    --name <container_name>
    --mount type=bind,source=<local_src_path>,target=<target_path_in_container>(,readonly) \
    # 或 -v <local_src_path>:<target_path_in_container> \
    <image> \
    <command>
  • 掛載的宿主目錄預設為可讀寫,若加上 readonly 則為唯讀,變更該項目會報錯。
  • 也可以掛載單一文件。即上例中的 <local_src_path> 與 <target_path_in_container> 都替換為文件 path,很適合用來記錄容器執行過程的 log 檔案。

# 以 -v 或 --mount 掛載的差別

  • 若 <local_src_path> 不存在,-v 會自動創建,--mount 會報錯
  • 作者建議對 docker 尚不熟悉者優先使用 --mount

# 結語

容器是 docker 的核心觀念,本文主要從應用層面切入了解容器。像是 docker run 背後的一系列操作;docker execdocker attach 兩者雖都可以進入容器,但因途徑不同而產生不同行為;藉由掛載 volume 或宿主目錄文件,可避免容器運行資料被移除掉等等。

另外,「容器的本質是程序(process),指定的程序一旦執行完畢,容器的生命週期就結束」也是這本書強調的觀念之一。

關於 docker 究竟如何實現容器技術,就下一篇再討論吧(docker 底層運作)。

#docker #container #volume #Docker從入門到實踐 #docker run #mount







你可能感興趣的文章

Spot the Difference - Difference in Difference

Spot the Difference - Difference in Difference

BootStrap5 第一章 : 載入

BootStrap5 第一章 : 載入

[27] 強制轉型 - 寬鬆相等 ( == ) vs. 嚴格相等 ( === )

[27] 強制轉型 - 寬鬆相等 ( == ) vs. 嚴格相等 ( === )






留言討論