通過容器化 Python Web 掌握 Docker 容器核心技能下


測試必會 | 通過容器化 Python Web 掌握 Docker 容器核心技能下


本篇文章將通過一個非常典型的 Python Web 應用作為案例,講解 Docker 容器使用的主要場景。包括構建鏡像、啟動鏡像、分享鏡像、在鏡像中操作、在鏡像中掛載宿主機目錄、對容器使用的資源進行限制、管理容器的狀態和如何保持容器始終運行。熟悉了這些操作,你也就基本上摸清了 Docker 容器的核心功能。

前期準備:需要一臺已經安裝了 Docker 的 Linux 虛擬機。


容器與宿主機之間如何共享文件

容器技術使用了 rootfs 機制和 Mount Namespace,構建出了一個同宿主機完全隔離開的文件系統環境。但是我們使用過程中經常會遇到這樣兩個問題:

  • 容器裡進程新建的文件,怎麼才能讓宿主機獲取到?
  • 宿主機上的文件和目錄,怎麼才能讓容器裡的進程訪問到?

這正是 Docker Volume 要解決的問題:Volume 機制,允許你將宿主機上指定的目錄或者文件,掛載到容器裡面進行讀取和修改。

在 Docker 項目裡,它支持兩種 Volume 聲明方式,可以把宿主機目錄掛載進容器的 /test 目錄當中:

<code>docker run -v /test ...  
docker run -v /home:/test ...  /<code>

而這兩種聲明方式的本質,實際上是相同的:都是把一個宿主機的目錄掛載進了容器的 /test 目錄。

只不過,在第一種情況下,由於你並沒有顯示聲明宿主機目錄,那麼 Docker 就會默認在宿主機上創建一個臨時目錄 /var/lib/docker/volumes/[VOLUME_ID]/_data,然後把它掛載到容器的 /test 目錄上。而在第二種情況下,Docker 就直接把宿主機的 /home 目錄掛載到容器的 /test 目錄上。

啟動容器時,給他聲明一個 volume

<code>docker run -d -v /test helloworld  /<code>

容器啟動之後,我們來查看一下這個容器 的 Volume 在宿主機上的對應的目錄:

<code>docker volume ls  
DRIVER              VOLUME NAME  
local               dc195c8ad14ad505832461d9f37da889c54ef284ebaf777a100e10a932217ad3  /<code>

或者執行docker inspect CONTAINER_ID命令查看,命令輸出的 Mounts 字段中 Source 的值就是宿主機上的目錄:

<code>"Mounts": \\[  
 {  
 "Type": "volume",  
 "Name": "dc195c8ad14ad505832461d9f37da889c54ef284ebaf777a100e10a932217ad3",  
 "Source": "/var/lib/docker/volumes/dc195c8ad14ad505832461d9f37da889c54ef284ebaf777a100e10a932217ad3/_data",  

 "Destination": "/test",  
 "Driver": "local",  
 "Mode": "",  
 "RW": true,  
 "Propagation": ""  
 }  
 \\],  /<code>

然後,查看宿主機上的路徑:

<code>ls /var/lib/docker/volumes/dc195c8ad14ad505832461d9f37da889c54ef284ebaf777a100e10a932217ad3/_data  /<code>

這個 _data 文件夾,就是這個容器的 Volume 在宿主機上對應的臨時目錄了。接下來,我們在容器的 Volume 裡,添加一個文件 text.txt:

<code>docker exec -it cf53b766fa6f /bin/sh  
cd test/  
touch text.txt  /<code>

這時,我們再回到宿主機,就會發現 text.txt 已經出現在了宿主機上對應的臨時目錄裡了:

<code>ls /var/lib/docker/volumes/dc195c8ad14ad505832461d9f37da889c54ef284ebaf777a100e10a932217ad3/_data  
text.txt  /<code>

因為容器運行時產生的文件,在容器停止後將會消失。因此,將容器的目錄映射到宿主機的某個目錄,一個重要使用場景是持久化容器中產生的文件,比如應用的日誌。

給容器加上資源限制

其實容器是運行在宿主機上的特殊進程,多個容器之間是共享宿主機的操作系統內核的。默認情況下,容器並沒有被設定使用操作系統資源的上限。

有些情況下,我們需要限制容器啟動後佔用的宿主機操作系統的資源。Docker 可以利用 Linux Cgroups 機制可以給容器設置資源使用限制。

Linux Cgroups 的全稱是 Linux Control Group。它最主要的作用,就是限制一個進程組能夠使用的資源上限,包括 CPU、內存、磁盤、網絡帶寬等等。Docker 正是利用這個特性限制容器使用宿主上的 CPU、內存。

下面啟動容器的方式,給這個 Python 應用加上 CPU 和 Memory 限制:

<code>docker run -it --cpu-period=100000 --cpu-quota=20000 -m 300M helloworld  /<code>

–cpu-period 和–cpu-quota 組合使用來限制容器使用的 CPU 時間。表示在–cpu-period 的一段時間內,容器只能被分配到總量為 --cpu-quota 的 CPU 時間。-m 選項則限制了容器使用宿主機內存的上限。

上面啟動容器的命令,將容器使用的 CPU 限制設定在最高 20%,內存使用最多是 300MB。

容器的啟動、重啟、停止與刪除

前面我們使用過docker ps 查看當前運行中的容器,如果加上 -a 選項,則可以查看運行中和已經停止的所有容器。現在,看一下我的系統中目前的所有容器:

<code>$ docker ps -a  
CONTAINER ID   IMAGE        COMMAND          CREATED      STATUS                 PORTS                  NAMES  

525a8c3fc769   helloworld   "python app.py"  4 hours ago  Up 3 minutes           80/tcp                 hardcore_feistel  
1695ed10e2cb   helloworld   "python app.py"  4 hours ago  Up 3 minutes           0.0.0.0:5000->80/tcp   focused_margulis7  
a242ecaf6cf6   helloworld   "python app.py"  5 hours ago  Exited (0) 4 hours ago                        dazzling_khayyam  
be0439b30b2a   helloworld   "python app.py"  5 hours ago  Created                                       vigilant_lalande  /<code>

從輸出中可以看到目前有四個容器,有兩個容器處於 Up 狀態,也就是處於運行中的狀態,一個容器處於 Exited(0) 狀態,也就是退出狀態,一個處於 Created 狀態。

這裡補充說明一下 docker ps -a 的輸出結果,一共包含 7 列數據,分別是 CONTAINER ID、IMAGE、COMMAND、CREATED、STATUS、PORTS 和 NAMES。這些列的含義分別如下所示:

  • CONTAINER ID:容器 ID,唯一標識容器
  • IMAGE:創建容器時所用的鏡像
  • COMMAND:在容器最後運行的命令
  • CREATED:容器創建的時間
  • STATUS:容器的狀態
  • PORTS:對外開放的端口號
  • NAMES:容器名(具有唯一性,docker 負責命名)
    獲取到容器的 ID 之後,可以對容器的狀態進行修改,比如容器 1695ed10e2cb 進行停止、啟動、重啟:
<code>docker stop 1695ed10e2cb  
docker start 1695ed10e2cb  
docker restart 1695ed10e2cb  /<code>

刪除容器,有兩種操作:

<code>docker rm 1695ed10e2cb  
docker rm -f 1695ed10e2cb  /<code>

不帶-f 選項,只能刪除處於非 Up 狀態的容器,帶上-f 則可以刪除處於任何狀態下的容器。

Docker 容器狀態的流轉關係,可以下面這張圖:

測試必會 | 通過容器化 Python Web 掌握 Docker 容器核心技能下

這張圖對容器的生命週期有了清晰的描述,總結了容器各種狀態之間是如何轉換的。

需要注意一點是容器可以先創建容器,稍後再啟動。也就是可以先執行docker create 創建容器(處於 Created 狀態),再通過docker start 以後臺方式啟動容器。docker run 命令實際上是 docker create 和 docker start 的組合。

維持容器始終保持運行狀態

docker run 指令有一個參數--restart,在容器中啟動的進程正常退出或發生 OOM 時, docker 會根據 --restart 的策略判斷是否需要重啟容器。但如果容器是因為執行 docker stop 或 docker kill 退出,則不會自動重啟。

docker 支持如下 restart 策略:

  • no – 容器退出時不要自動重啟。這個是默認值。
  • on-failure[:max-retries] – 只在容器以非 0 狀態碼退出時重啟。可選的,可以退出 docker daemon 嘗試重啟容器的次數。
  • always – 不管退出狀態碼是什麼始終重啟容器。當指定 always 時,docker daemon 將無限次數地重啟容器。容器也會在 daemon 啟動時嘗試重啟容器,不管容器當時的狀態如何。
  • unless-stopped – 不管退出狀態碼是什麼始終重啟容器。不過當 daemon 啟動時,如果容器之前已經為停止狀態,不啟動它。

在每次重啟容器之前,不斷地增加重啟延遲(上一次重啟的雙倍延遲,從 100 毫秒開始),來防止影響服務器。這意味著 daemon 將等待 100ms, 然後 200 ms, 400 ms, 800 ms, 1600 ms 等等,直到超過 on-failure 限制,或執行 docker stop 或 docker rm -f。

如果容器重啟成功(容器啟動後並運行至少 10 秒),然後 delay 重置為默認的 100ms。

重啟策略示例:

<code>docker run --restart=always 1695ed10e2cb # restart 策略為 always,使得容器退出時 ,docker 將重啟它。並且是無限制次數重啟。  
docker run --restart=on-failure:10 redis #restart 策略為 on-failure, 最大重啟次數為 10 的次。容器以非 0 狀態連續退出超過 10 次,docker 將中斷嘗試重啟這個容器。  /<code>

可以通過 docker inspect 來查看已經嘗試重啟容器了多少次。例如,獲取容器 1695ed10e2cb 的重啟次數:

<code>docker inspect -f "{{ .RestartCount }}" 1695ed10e2cb  /<code>

或者獲取上一次容器重啟時間:

<code>docker inspect -f "{{ .State.StartedAt }}" 1695ed10e2cb  /<code>

以上,歡迎大家留言探討。

本文首發於 liuchunming.net ,作者授權發佈。



分享到:


相關文章: