深入淺出Docker 讀書筆記(三)

   第7章:Docker容器

容器是鏡像運行的實例。單個鏡像可以啓動一個或多個容器,容器共享其所在主機的操作系統/內核。啓動容器的簡便方式是使用docker container run命令。該命令可以攜帶很多參數,在其基礎的格式docker container run <image> <app>中,指定了啓動所需的鏡像以及要運行的應用。docker container run -it ubuntu /bin/bash則會啓動某個 Ubuntu Linux容器,並運行 Bash Shell作爲其應用。-it 參數可以將當前終端連接到容器的 Shell 終端之上。容器隨着其中運行應用的退出而終止。可以使用 docker container stop 命令手動停止容器運行,並且使用 docker container start 再次啓動該容器。如果再也不需要該容器,則使用 docker container rm 命令來刪除容器。

虛擬機與容器的對比:兩者都需要依賴於宿主機才能運行,以虛擬機與容器運行四個服務做對比來比較差異,在虛擬機模型中,首先要開啓物理機並啓動 Hypervisor 引導程序。一旦 Hypervisor 啓動,就會佔有機器上的全部物理資源,如 CPU、RAM、存儲和 NICHypervisor 接下來就會將這些物理資源劃分爲虛擬資源,並且看起來與真實物理資源完全一致。然後 Hypervisor 會將這些資源打包進一個叫作虛擬機(VM)的軟件結構當中。這樣用戶就可以使用這些虛擬機,並在其中安裝操作系統和應用。
運行 4 個應用,要在 Hypervisor 之上需要創建 4 個虛擬機並安裝 4 個操作系統,然後安裝 4 個應用。當操作完成後,結構如下圖所示。

運行4個業務應用的物理服務器

而容器模型則略有不同。服務器啓動之後,所選擇的操作系統會啓動。在 Docker 世界中可以選擇 Linux,或者內核支持內核中的容器原語的新版本 Windows。與虛擬機模型相同,OS 也佔用了全部硬件資源。在 OS 層之上,需要安裝容器引擎(如 Docker)。容器引擎可以獲取系統資源,比如進程樹、文件系統以及網絡棧,接着將資源分割爲安全的互相隔離的資源結構,稱之爲容器。每個容器看起來就像一個真實的操作系統,在其內部可以運行應用。按照前面的假設,需要在物理機上運行 4 個應用。因此,需要劃分出 4 個容器並在每個容器中運行一個應用,如下圖所示。

劃分4個容器

從更高層面上來講,Hypervisor 是硬件虛擬化(Hardware Virtualization)——Hypervisor 將硬件物理資源劃分爲虛擬資源。容器是操作系統虛擬化(OS Virtualization)——容器將系統資源劃分爲虛擬資源。從中就可以看出容器比虛擬機所帶來的優勢,1)消耗低安全性高:因爲虛擬機模型將底層硬件資源劃分到虛擬機當中。每個虛擬機都是包含了虛擬 CPU、虛擬 RAM、虛擬磁盤等資源的一種軟件結構。操作系統本身是有其額外開銷的。操作作系統都需要獨立的許可證,並且都需要打補丁升級,每個操作系統也都面臨被攻擊的風險。通常將這種現象稱作 OS Tax 或者 VM Tax,每個操作系統都佔用一定的資源。容器模型具有在宿主機操作系統中運行的單個內核。這意味着只有一個操作系統消耗 CPU、RAM 和存儲資源,授權、升級和打補丁,只有一個操作系統面臨被攻擊的風險。簡言之,就是隻有一份 OS 損耗。2)啓動快:因爲容器並不是完整的操作系統,所以其啓動要遠比虛擬機快。在容器內部並不需要內核,也就沒有定位、解壓以及初始化的過程——更不用提在內核啓動過程中對硬件的遍歷和初始化了。這些在容器啓動的過程中統統都不需要!唯一需要的是位於下層操作系統的共享內核是啓動了的,唯一對容器啓動時間有影響的就是容器內應用啓動所花費的時間。

通常登錄 Docker 主機後的第一件事情是檢查 Docker 是否正在運行。$ docker version 當命令輸出中包含 Client 和 Server 的內容時,可以繼續下面的操作。如果在 Server 部分中包含了錯誤碼,這表示 Docker daemon 很可能沒有運行,或者當前用戶沒有權限訪問。如果在 Linux 中遇到無權限訪問的問題,需要確認當前用戶是否屬於本地 Docker UNIX 組。如果不是,可以通過usermod -aG docker <user>來添加,然後退出並重新登錄 Shell,改動即可生效。可以通過一下命令檢查 Docker daemon 的狀態。//使用 Systemd 在 Linux 系統中執行該命令 $ service docker status    /或 $ systemctl is-active docker     //在Windows Server 2016的PowerShell窗口中運行該命令 > Get-Service docker

啓動容器的一個簡單的方式是通過 docker container run 命令。如果通過輸入 exit 退出 Bash Shell,那麼容器也會退出(終止)。原因是容器如果不運行任何進程則無法存在,殺死 Bash Shell 即殺死了容器唯一運行的進程,導致這個容器也被殺死。這對於 Windows 容器來說也是一樣的,殺死容器中的主進程,則容器也會被殺死。按下 Ctrl-PQ 組合鍵則會退出容器但並不終止容器運行。這樣做會切回到 Docker 主機的 Shell,並保持容器在後臺運行。可以使用 docker container ls 命令來觀察當前系統正在運行的容器列表。當前容器仍然在運行,並且可以通過 docker container exec 命令將終端重新連接到 Docker,理解這一點很重要。$ docker container exec -it 3027eb644874 bash 。   用於重連 Windows Nano Server PowerShell 容器的命令是 docker container exec -it <container-name-or-ID> pwsh.exe
       容器的生命週期:創建、運行、休眠,銷燬,在這四個過程中會介紹容器數據的持久化,書中是先啓動一個容器並將一部分數據寫入其中。按 Ctrl-PQ 組合鍵退出當前容器。現在使用 docker container stop 命令來停止容器運行,切換到暫停(vacation)狀態。$ docker container stop percy  可以在 docker container stop 命令中指定容器的名稱或者 ID。具體格式爲: docker container stop <container-id or container-name>   停止容器就像停止虛擬機一樣。儘管已經停止運行,容器的全部配置和內容仍然保存在 Docker 主機的文件系統之中,並且隨時可以重新啓動。使用 docker container start 命令可以將容器重新啓動。使用 docker container exec 命令連接到重啓後的容器。確認之前創建的文件依然存在,並且文件中仍包含之前寫入的數據。儘管上面的示例闡明瞭容器的持久化特性,還是需要指出卷(volume)纔是在容器中存儲持久化數據的首選方式。現在停止該容器並從系統中刪除它。通過在 docker container rm 命令後面添加 -f 參數來一次性刪除運行中的容器是可行的。但是,刪除容器的最佳方式還是分兩步,先停止容器然後刪除。這樣可以給容器中運行的應用/進程一個停止運行並清理殘留數據的機會。因爲docker container stop 命令給容器內進程發送將要停止的警告信息,給進程機會來有序處理停止前要做的事情。一旦 docker stop 命令返回後,就可以使用 docker container rm 命令刪除容器了。這背後的原理可以通過 Linux/POSIX 信號來解釋。docker container stop 命令向容器內的 PID 1 進程發送了 SIGTERM 這樣的信號。如果 10s 內進程沒有終止,那麼就會收到 SIGKILL 信號。這是致命一擊。但是,進程起碼有 10s 的時間來“解決”自己。docker container rm <container> -f 命令不會先友好地發送 SIGTERM,這條命令會直接發出 SIGKILL。就像剛剛所打的比方一樣,該命令悄悄接近並對容器發起致命一擊。所以通常建議在運行容器時配置好重啓策略。這是容器的一種自我修復能力,可以在指定事件或者錯誤後重啓來完成自我修復。重啓策略應用於每個容器,可以作爲參數被強制傳入 docker-container run 命令中,或者在 Compose 文件中聲明(在使用 Docker Compose 以及 Docker Stacks 的情況下)。容器支持的重啓策略包括 always、unless-stopped 和 on-failed。

快速清理:簡單快速並且強制刪除所用容器的清理 Docker 主機上全部運行容器的方法。這種操作一定不能在生產環境系統或者運行着重要容器的系統上執行。在 Docker 主機的 Shell 中運行下面的命令,可以刪除全部容器。$docker container rm $(docker container ls -aq) -f     果將 $(docker container ls -aq) 作爲參數傳遞給 docker container rm 命令,等價於將系統中每個容器的 ID 傳給該命令。-f 標識表示強制執行,所以即使是處於運行狀態的容器也會被刪除。接下來,無論是運行中還是停止的容器,都會被刪除並從系統中移除。

 第8章:應用容器化

將應用整合到容器中並且運行起來的這個過程,稱爲“容器化”(Containerizing),有時也叫作“Docker化”(Dockerizing)。完整的應用容器化過程主要分爲以下幾個步驟。

  • 編寫應用代碼。
  • 創建一個 Dockerfile,其中包括當前應用的描述、依賴以及該如何運行這個應用。
  • 對該 Dockerfile 執行 docker image build 命令。
  • 等待 Docker 將應用程序構建到 Docker 鏡像中。

下圖展示了上述步驟。
 

容器化的基本過程

Dockerfile 的文件。這個文件包含了對當前應用的描述,並且能指導 Docker 完成鏡像的構建。在 Docker 當中,包含應用文件的目錄通常被稱爲構建上下文(Build Context)。通常將 Dockerfile 放到構建上下文的根目錄下。另外很重要的一點是,文件開頭字母是大寫 D,這裏是一個單詞。像“dockerfile”或者“Docker file”這種寫法都是不允許的。


下面是這個文件中的一些關鍵步驟概述:以 alpine 鏡像作爲當前鏡像基礎,指定維護者(maintainer)爲“[email protected]”,安裝 Node.js 和 NPM,將應用的代碼複製到鏡像當中,設置新的工作目錄,安裝依賴包,記錄應用的網絡端口,最後將 app.js 設置爲默認運行的應用。

$ cat Dockerfile
FROM alpine
LABEL maintainer="[email protected]"
RUN apk add --update nodejs nodejs-npm
COPY . /src
WORKDIR /src
RUN npm install
EXPOSE 8080
ENTRYPOINT ["node", "./app.js"]
FROM 指令指定的鏡像,會作爲當前鏡像的一個基礎鏡像層,當前應用的剩餘內容會作爲新增鏡像層添加到基礎鏡像層之上

接下來,Dockerfile 中通過標籤(LABLE)方式指定了當前鏡像的維護者爲“nigelpoulton@hotmail. com”。每個標籤其實是一個鍵值對(Key-Value),在一個鏡像當中可以通過增加標籤的方式來爲鏡像添加自定義元數據。
RUN apk add --update nodejs nodejs-npm 指令使用 alpine 的 apk 包管理器將 nodejs 和 nodejs-npm 安裝到當前鏡像之中。RUN 指令會在 FROM 指定的 alpine 基礎鏡像之上,新建一個鏡像層來存儲這些安裝內容。COPY. / src 指令將應用相關文件從構建上下文複製到了當前鏡像中,並且新建一個鏡像層來存儲。COPY 執行結束之後,當前鏡像共包含 3 層,下一步,Dockerfile 通過 WORKDIR 指令,爲 Dockerfile 中尚未執行的指令設置工作目錄。該目錄與鏡像相關,並且會作爲元數據記錄到鏡像配置中,但不會創建新的鏡像層。然後,RUN npm install 指令會根據 package.json 中的配置信息,使用 npm 來安裝當前應用的相關依賴包。npm 命令會在前文設置的工作目錄中執行,並且在鏡像中新建鏡像層來保存相應的依賴文件。目前鏡像一共包含 4 層,如下圖所示。

當前的4層鏡像

因爲當前應用需要通過 TCP 端口 8080 對外提供一個 Web 服務,所以在 Dockerfile 中通過 EXPOSE 8080 指令來完成相應端口的設置。這個配置信息會作爲鏡像的元數據被保存下來,並不會產生新的鏡像層。最終,通過 ENTRYPOINT 指令來指定當前鏡像的入口程序。ENTRYPOINT 指定的配置信息也是通過鏡像元數據的形式保存下來,而不是新增鏡像層。打出來的鏡像最好推送到倉庫,推送 Docker 鏡像之前,還需要爲鏡像打標籤。這是因爲 Docker 在鏡像推送的過程中需要如下信息。1)Registry(鏡像倉庫服務)。2)Repository(鏡像倉庫)。3)Tag(鏡像標籤)。無須爲 Registry 和 Tag 指定值。當沒有爲上述信息指定具體值的時候,Docker 會默認Registry=docker.io、Tag=latest。但是 Docker 並沒有給 Repository 提供默認值,而是從被推送鏡像中的 REPOSITORY 屬性值獲取。

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