Docker 入門(4)鏡像與容器

1. 鏡像與容器

1.1 鏡像

Docker鏡像類似于未運行的exe應用程序,或者停止運行的VM。當使用docker run命令基于鏡像啟動容器時,容器應用便能為外部提供服務。

鏡像實際上就是這個用來為容器進程提供隔離后執行環境的文件系統。我們也稱之為根文件系統(Rootfs)。(注意,rootfs 只是一個操作系統所包含的文件、配置和目錄,并不包括操作系統內核。同一臺機器上的所有容器,都共享宿主機操作系統的內核。)

由于 rootfs 里封裝的不僅僅是應用,還包括它運行所需要的所有依賴。這就賦予了容器的強一致性:無論在本地、云端,還是其他任何地方,用戶只需要解壓打包好的容器鏡像,這個應用運行所需要的完整的執行環境就被重現出來了。

我們可以將 Docker 鏡像理解為包含應用程序以及其相關依賴的一個基礎文件系統,在 Docker 容器啟動的過程中,它以只讀的方式被用于創建容器的運行環境。

1.1.1 鏡像分層

但還有一個不容忽視的問題,例如如果我需要一個在CentOS環境中跑的apache應用,我可以將它打包成一個apache鏡像;如果我還需要一個在CentOS環境中跑的mysql應用,我又將它打包成一個mysql鏡像……

這幾個鏡像中都有全部的CentOS的全部環境,將造成大量空間占用問題及碎片化問題。

Docker的解決方法是: 在鏡像的設計中,引入了層(layer)的概念。即: 用戶制作鏡像的每一步操作,都生成一個層,也就是一個增量 rootfs。

Docker 鏡像其實是由基于 UnionFS 文件系統的一組鏡像層依次掛載而得,而每個鏡像層包含的其實是對上一鏡像層的修改,這些修改其實是發生在容器運行的過程中的。所以,我們也可以反過來理解,鏡像是對容器運行環境進行持久化存儲的結果。

1.1.1 鏡像的實現

1.1.1.1 Docker 是如何構建并且存儲鏡像的

Docker 中的每一個鏡像都是由一系列只讀的層組成的,Dockerfile 中的每一個命令都會在已有的只讀層上創建一個新的層,容器中的每一層都只對當前容器進行了非常小的修改

當鏡像被 docker run 命令創建時就會在鏡像的最上層添加一個可寫的層,也就是容器層,所有對于運行時容器的修改其實都是對這個容器讀寫層的修改

image.png

上面的這張圖片非常好的展示了組裝的過程,每一個鏡像層都是建立在另一個鏡像層之上的,同時所有的鏡像層都是只讀的,只有每個容器最頂層的容器層才可以被用戶直接讀寫,所有的容器都建立在一些底層服務(Kernel)上,包括命名空間、控制組、rootfs 等等,這種容器的組裝方式提供了非常大的靈活性,只讀的鏡像層通過共享也能夠減少磁盤的占用。

1.1.1.2鏡像概述

所有的 Docker 鏡像都是按照 Docker 所設定的邏輯打包的,也是受到 Docker Engine 所控制的

我們常見的虛擬機鏡像,通常是由熱心的提供者以他們自己熟悉的方式打包成鏡像文件,被我們從網上下載或是其他方式獲得后,恢復到虛擬機中的文件系統里的。而 Docker 的鏡像我們必須通過 Docker 來打包,也必須通過 Docker 下載或導入后使用,不能單獨直接恢復成容器中的文件系統。

雖然這么做失去了很多靈活性,但固定的格式意味著我們可以很輕松的在不同的服務器間傳遞 Docker 鏡像,配合 Docker 自身對鏡像的管理功能,讓我們在不同的機器中傳遞和共享 Docker 變得非常方便。

對于每一個記錄文件系統修改的鏡像層來說,Docker 都會根據它們的信息生成了一個 Hash 碼,這是一個 64 長度的字符串,足以保證全球唯一性。

由于鏡像層都有唯一的編碼,我們就能夠區分不同的鏡像層并能保證它們的內容與編碼是一致的,這帶來了另一項好處,就是允許我們在鏡像之間共享鏡像層

image.png

舉一個實際的例子,由 Docker 官方提供的兩個鏡像 elasticsearch 鏡像和 jenkins 鏡像都是在 openjdk 鏡像之上修改而得,那么在我們實際使用的時候,這兩個鏡像是可以共用 openjdk 鏡像內部的鏡像層的。

1.1.2 查看鏡像

如果要查看當前連接的 docker daemon 中存放和管理了哪些鏡像,我們可以使用 docker images 這個命令 ( Linux、macOS 還是 Windows 上都是一致的 )。

在 docker images 命令的結果中,我們可以看到鏡像的 ID ( IMAGE ID)、構建時間 ( CREATED )、占用空間 ( SIZE ) 等數據。

image.png

1.1.3 鏡像命名

鏡像層的 ID 既可以識別每個鏡像層,也可以用來直接識別鏡像 ( 因為根據最上層鏡像能夠找出所有依賴的下層鏡像,所以最上層進行的鏡像層 ID 就能表示鏡像的 ID ),但是使用這種無意義的超長哈希碼顯然是違背人性的,通過鏡像名我們能夠更容易的識別鏡像。

準確的來說,鏡像的命名我們可以分成三個部分:username、repository 和 tag

  • username: 主要用于識別上傳鏡像的不同用戶,與 GitHub 中的用戶空間類似。

對于 username 來說,在上面我們展示的 docker images 結果中,有的鏡像有 username 這個部分,而有的鏡像是沒有的。沒有 username 這個部分的鏡像,表示鏡像是由 Docker 官方所維護和提供的,所以就不單獨標記用戶了。

  • repository:主要用于識別進行的內容,形成對鏡像的表意描述。
    Docker 中鏡像的 repository 部分通常采用的是軟件名。我們推崇一個容器運行一個程序的做法,那么自然容器的鏡像也會僅包含程序以及與它運行有關的一些依賴包,所以我們使用程序的名字直接套用在鏡像之上,既祛除了鏡像取名的麻煩,又能直接表達鏡像中的內容。

  • tag:主要用戶表示鏡像的版本,方便區分進行內容的不同細節
    在鏡像命名中,還有一個非常重要的部分,也就是鏡像的標簽 ( tag )。鏡像的標簽是對同一種鏡像進行更細層次區分的方法,也是最終識別鏡像的關鍵部分。

通常來說,鏡像的標簽主要是為了區分同類鏡像不同構建過程所產生的不同結果的。由于時間、空間等因素的不同,Docker 每次構建鏡像的內容也就有所不同,具體體現就是鏡像層以及它們的 ID 都會產生變化。而標簽就是在鏡像命名這個層面上區分這些鏡像的方法。

與鏡像的 repository 類似,鏡像 tag 的命名方法也通常參考鏡像所關聯的應用程序。更確切的來說,我們通常會采用鏡像內應用程序的版本號以及一些環境、構建方式等信息來作為鏡像的 tag。

1.2 容器

容器就是將軟件打包成標準化單元,以用于開發、交付和部署。

  • 容器鏡像是輕量的、可執行的獨立軟件包 ,包含軟件運行所需的所有內容:代碼、運行時環境、系統工具、系統庫和設置。
  • 容器化軟件適用于基于Linux和Windows的應用,在任何環境中都能夠始終如一地運行。
  • 容器賦予了軟件獨立性,使其免受外在環境差異(例如,開發和預演環境的差異)的影響,從而有助于減少團隊間在相同基礎設施上運行不同軟件時的沖突。

image.png

1.2.1 容器的生命周期

由于 Docker 攬下了大部分對容器管理的活,只提供給我們非常簡單的操作接口,這就意味著 Docker 里對容器的一些運行細節會被更加嚴格的定義,這其中就包括了容器的生命周期。

這里有一張容器運行的狀態流轉圖:

image.png

1.2.2 主進程

當我們啟動容器時,Docker 其實會按照鏡像中的定義,啟動對應的程序,并將這個程序的主進程作為容器的主進程 ( 也就是 PID 為 1 的進程 )。而當我們控制容器停止時,Docker 會向主進程發送結束信號,通知程序退出。

而當容器中的主進程主動關閉時 ( 正常結束或出錯停止 ),也會讓容器隨之停止。

1.2.3 寫時復制

Docker 的寫時復制與編程中的相類似,也就是在通過鏡像運行容器時,并不是馬上就把鏡像里的所有內容拷貝到容器所運行的沙盒文件系統中,而是利用 UnionFS 將鏡像以只讀的方式掛載到沙盒文件系統中。只有在容器中發生對文件的修改時,修改才會體現到沙盒環境上。

也就是說,容器在創建和啟動的過程中,不需要進行任何的文件系統復制操作,也不需要為容器單獨開辟大量的硬盤空間,與其他虛擬化方式對這個過程的操作進行對比,Docker 啟動的速度可見一斑。

Docker的容器是一個多層的結構。如果對鏡像做history操作,我們可以看見他里面每一次dockerfile的命令都會創建一個新的層次。

[root@ip-172-16-1-4 ec2-user]# docker image history nginx
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
8cf1bfb43ff5        6 days ago          /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B
<missing>           6 days ago          /bin/sh -c #(nop)  STOPSIGNAL SIGTERM           0B
<missing>           6 days ago          /bin/sh -c #(nop)  EXPOSE 80                    0B
<missing>           6 days ago          /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B
<missing>           6 days ago          /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7…   1.04kB
<missing>           6 days ago          /bin/sh -c #(nop) COPY file:1d0a4127e78a26c1…   1.96kB
<missing>           6 days ago          /bin/sh -c #(nop) COPY file:e7e183879c35719c…   1.2kB
<missing>           6 days ago          /bin/sh -c set -x     && addgroup --system -…   63.3MB
<missing>           6 days ago          /bin/sh -c #(nop)  ENV PKG_RELEASE=1~buster     0B
<missing>           6 days ago          /bin/sh -c #(nop)  ENV NJS_VERSION=0.4.2        0B
<missing>           6 days ago          /bin/sh -c #(nop)  ENV NGINX_VERSION=1.19.1     0B
<missing>           6 days ago          /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B
<missing>           6 days ago          /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           6 days ago          /bin/sh -c #(nop) ADD file:6ccb3bbcc69b0d44c…   69.2MB

Docker里面有一個重要的概念叫做 Storage driver,他可以幫助我們實現對容器的分層和讀寫。目前,docker的默認storage driver 是overlay2。所有的容器相關的文件都保存在/var/lib/docker這個目錄下。我們可以看見在overlay2里面有很多不同的文件

[root@ip-172-16-1-4 ec2-user]# ls /var/lib/docker/overlay2/
12597d435b78d470bed7cf3a4cc7d60691432e74f12c00fd44def7ecf6ab659f       6945f4530a5212bc3a8aa598dc88839d39b763799ccdfbd18a5f04180e3b676e       d1f1bcc388595272f11f4ace02f9e6976dc96fd80cce8216d3626484ba114aad
145e76991ae57691ed94de5dd2ea950ff7b50dc26729605e711cd0f35f275e84       7c4d732c22b84e0df8c0d317df825623958d4eff59055827e6b2c76cbe8b0350       d1f1bcc388595272f11f4ace02f9e6976dc96fd80cce8216d3626484ba114aad-init
16da856b3acb71eea396f529a80cf728d664a4bf3eb042f828cb9651d82e90bf       9b6749a9a76ad9f76d7795ebbf8e47595dada7a06b9126f5d9c6e1084f1d0c02       f089b3fd763c560e1c398c9b432100cea56ad4dae3a6b760de42b45e856ce693
16da856b3acb71eea396f529a80cf728d664a4bf3eb042f828cb9651d82e90bf-init  a60b187ea615a59402ad2fe6687a4dc87f1c037eafea6880167795c6e1b7f900       f089b3fd763c560e1c398c9b432100cea56ad4dae3a6b760de42b45e856ce693-init
658b1eab364b4523a8c7274e9b8a2bdc55095e9bd56dcb697f6456b4064abe66       a60b187ea615a59402ad2fe6687a4dc87f1c037eafea6880167795c6e1b7f900-init  l
658b1eab364b4523a8c7274e9b8a2bdc55095e9bd56dcb697f6456b4064abe66-init  backingFsBlockDev

一個典型的場景如下所示,一個鏡像文件里面,里面分了多層,最下面的是基礎鏡像,這個基礎鏡像不包括內核文件,執行的時候他會直接調用宿主機的內核,因此他的空間并不大。在基礎鏡像上面,又分了很多層,每一層代表在dockerfile里面執行的一行命令。這整個鏡像文件都是只讀的。每個容器通過鏡像創建自己的容器層,而容器層是可以讀寫的,修改的內容他們會保存在自己的目錄下面。因此每個容器對自己的修改 不會影響到其他容器。

在docker里面,我們通過storage driver來進行所謂的copy on write ( 寫時復制)的操作。storage driver有很多種,目前默認的是overlay2

overlay的基本工作原理如下

我們通過鏡像創建的容器包括了三層。最下面的是一個只讀的鏡像層,第二層是容器層,在他上面最上面的容器掛載層。最上層顯示的是我們在容器上直接看見的內容,他通過UnionFS,或者說類似軟連接的方式,文件的路徑指向了容器層或者是鏡像層。當我們嘗試讀取,修改或者創建一個新的文件時,我們總是從上到下進行搜索,如果在容器層找到了,那么就直接打開;如果容器層沒有,那就從鏡像層打開。如果是一個新建的文檔,那么就從鏡像層拷貝到容器層,再打開操作。

image.png

參考資料

《開發者必備的 Docker 實踐指南》

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/391279.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/391279.shtml
英文地址,請注明出處:http://en.pswp.cn/news/391279.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python:pytest中的setup和teardown

原文&#xff1a;https://www.cnblogs.com/peiminer/p/9376352.html  之前我寫的unittest的setup和teardown&#xff0c;還有setupClass和teardownClass&#xff08;需要配合classmethod裝飾器一起使用&#xff09;&#xff0c;接下來就介紹pytest的類似于這類的固件。 &#…

如何開始使用任何類型的數據? - 第1部分

從數據開始 (START WITH DATA) My data science journey began with a student job in the Advanced Analytics department of one of the biggest automotive manufacturers in Germany. I was nave and still doing my masters.我的數據科學之旅從在德國最大的汽車制造商之一…

iHealth基于Docker的DevOps CI/CD實踐

本文由1月31日晚iHealth運維技術負責人郭拓在Rancher官方技術交流群內所做分享的內容整理而成&#xff0c;分享了iHealth從最初的服務器端直接部署&#xff0c;到現在實現全自動CI/CD的實踐經驗。作者簡介郭拓&#xff0c;北京愛和健康科技有限公司&#xff08;iHealth)。負責公…

從早期的初創企業到MongoDB的經理(播客)

In this weeks podcast episode, I chat with Harry Wolff, an engineering manager at MongoDB in New York City. Harry has been in the world of tech for over a decade, holding jobs in various startups before ending up at Mongo. 在本周的播客節目中&#xff0c;我與…

leetcode 1011. 在 D 天內送達包裹的能力(二分法)

傳送帶上的包裹必須在 D 天內從一個港口運送到另一個港口。 傳送帶上的第 i 個包裹的重量為 weights[i]。每一天&#xff0c;我們都會按給出重量的順序往傳送帶上裝載包裹。我們裝載的重量不會超過船的最大運載重量。 返回能在 D 天內將傳送帶上的所有包裹送達的船的最低運載…

python:pytest優秀博客

上海悠悠&#xff1a;https://www.cnblogs.com/yoyoketang/tag/pytest/ 轉載于:https://www.cnblogs.com/gcgc/p/11514345.html

uva 11210

https://uva.onlinejudge.org/index.php?optioncom_onlinejudge&Itemid8&pageshow_problem&problem2151 題意&#xff1a;給你十三張麻將&#xff0c;問你需要哪幾張牌就可以胡牌&#xff0c;這個胡牌排除了七小對以及十三幺 胡牌必須要有一個對子加&#xff4e;個…

機器學習圖像源代碼_使用帶有代碼的機器學習進行快速房地產圖像分類

機器學習圖像源代碼RoomNet is a very lightweight (700 KB) and fast Convolutional Neural Net to classify pictures of different rooms of a house/apartment with 88.9 % validation accuracy over 1839 images. I have written this in python and TensorFlow.RoomNet是…

leetcode 938. 二叉搜索樹的范圍和

給定二叉搜索樹的根結點 root&#xff0c;返回值位于范圍 [low, high] 之間的所有結點的值的和。 示例 1&#xff1a; 輸入&#xff1a;root [10,5,15,3,7,null,18], low 7, high 15 輸出&#xff1a;32 示例 2&#xff1a; 輸入&#xff1a;root [10,5,15,3,7,13,18,1,nul…

456

456 轉載于:https://www.cnblogs.com/Forever77/p/11517711.html

課后作業-結隊編程項目進度-貪吃蛇

當前進度&#xff1a; 1.完成了窗口和蛇的繪制 2控制蛇的放向 3.繪制食物&#xff0c;隨機出現 4.設計暫停鍵和開始鍵 有遇到過問題&#xff0c;但通過上網和向同學請教解決了轉載于:https://www.cnblogs.com/qwsa/p/7605384.html

一百種簡單整人方法_一種非常簡單的用戶故事方法

一百種簡單整人方法User stories are a great way to plan development work. In theory. But how do you avoid getting burned in practice? I propose a radically simple approach.用戶故事是計劃開發工作的好方法。 理論上。 但是&#xff0c;如何避免在實踐中被燙傷&…

COVID-19和世界幸福報告數據告訴我們什么?

For many people, the idea of ??staying home actually sounded good at first. This process was really efficient for Netflix and Amazon. But then sad truths awaited us. What was boring was the number of dead and intubated patients one after the other. We al…

Python:self理解

Python類 class Student:# 類變量&#xff0c;可以通過類.類變量(Student.classroom)或者實例.類變量(a.classroom)方式調用classroom 火箭班def __init__(self, name, age):# self代表類的實例&#xff0c;self.name name表示當實例化Student時傳入的name參數賦值給類的實例…

leetcode 633. 平方數之和(雙指針)

給定一個非負整數 c &#xff0c;你要判斷是否存在兩個整數 a 和 b&#xff0c;使得 a2 b2 c 。 示例 1&#xff1a; 輸入&#xff1a;c 5 輸出&#xff1a;true 解釋&#xff1a;1 * 1 2 * 2 5 示例 2&#xff1a; 輸入&#xff1a;c 3 輸出&#xff1a;false 示例 3&…

洛谷 P2919 [USACO08NOV]守護農場Guarding the Farm

題目描述 The farm has many hills upon which Farmer John would like to place guards to ensure the safety of his valuable milk-cows. He wonders how many guards he will need if he wishes to put one on top of each hill. He has a map supplied as a matrix of int…

iOS 開發一定要嘗試的 Texture(ASDK)

原文鏈接 - iOS 開發一定要嘗試的 Texture(ASDK)(排版正常, 包含視頻) 前言 本篇所涉及的性能問題我都將根據滑動的流暢性來評判, 包括掉幀情況和一些實際體驗 ASDK 已經改名為 Texture, 我習慣稱作 ASDK 編譯環境: MacOS 10.13.3, Xcode 9.2 參與測試機型: iPhone 6 10.3.3, i…

lisp語言是最好的語言_Lisp可能不是數據科學的最佳語言,但是我們仍然可以從中學到什么呢?...

lisp語言是最好的語言This article is in response to Emmet Boudreau’s article ‘Should We be Using Lisp for Data-Science’.本文是對 Emmet Boudreau的文章“我們應該將Lisp用于數據科學”的 回應 。 Below, unless otherwise stated, lisp refers to Common Lisp; in …

鏈接訪問后刷新顏色回到初始_如何使鏈接可訪問(提示:顏色不夠)

鏈接訪問后刷新顏色回到初始Link accessibility is one of the most important aspects of usability. However, designers often dont understand what it takes to make links accessible. Most frequently, they only distinguish links by color, which makes it hard for …

567

567 轉載于:https://www.cnblogs.com/Forever77/p/11519678.html