簡單回答是:容器技術很是熱門,但門檻高。java
容器技術是繼大數據和雲計算以後又一煊赫一時的技術,並且將來至關一段時間內都會很是流行。docker
對 IT 行業來講,這是一項很是有價值的技術。而對 IT 從業者來講,掌握容器技術是市場的須要,也是提高自我價值的重要途徑。數據庫
拿我本身的工做經從來說,畢業後的頭幾年是作 J2EE 應用開發。後來到一家大型IT公司,公司的產品從中間件到操做系統、從服務器到存儲、從虛擬化到雲計算都有涉及。apache
我所在的部門是專門作 IT 基礎設施實施服務的,最開始是作傳統的 IT 項目,包括服務器配置,雙機 HA 等。隨着虛擬化技術成熟,工做上也開始涉及各類虛擬化技術的規劃和實施,包括 VMWare、KVM、PowerVM等。後來雲計算興起,在公司業務和我的興趣的驅動下,開始學習和實踐 OpenStack,在這個過程當中寫了《天天5分鐘玩轉OpenStack》教程並獲得你們的承認。django
如今以 Docker 爲表明的容器技術來了,並且關注度愈來愈高,這一點能夠從 Google Trend 中 Docker 的搜索上升趨勢(藍色曲線)中清楚看到。ubuntu
每一輪新技術的興起,不管對公司仍是我的既是機會也是挑戰。vim
我我的的見解是:若是某項新技術將來將成爲主流,就應該及早儘快掌握。
由於:centos
機會講過了,我們再來看看挑戰。瀏覽器
新技術每每意味着技術上的突破和創新,會有很多新的概念和方法。
並且從大數據,雲計算和容器技術來看,這些新技術都是平臺級別,覆蓋的技術範圍很是廣,包括了計算、網絡、存儲、高可用、監控、安全等多個方面,要掌握這些新技術對 IT 老兵尚有不小難度,更別說新人了。安全
因爲對技術一直保持着很高的熱誠和執着,在掌握了 OpenStack 相關 IaaS 技術後,我便開始調研 PaaS 技術棧。正好這時 Docker 也愈來愈流行,天然而然便開始了容器相關技術的學習研究和實踐。
學習容器技術的過程能夠說是驚喜不斷,常常驚歎於容器理念的先進和容器生態環境的完整和強大。不少傳統軟件開發和運維中的難題在容器世界裏都能輕鬆解決,也漸漸理解了容器爲什麼如此受到青睞。
不誇張地說,容器爲我打開了一扇通往另外一個軟件世界的大門,讓我沉浸其中,激動不已。高興之餘,我也火燒眉毛地想把我所看到所學到和所想到的有關容器的知識介紹給更多的人,讓更多的IT工程師可以從容器技術中受益。
我但願這個教程也能爲你們打開這扇門,下降學習的曲線,系統地學習和掌握容器技術。
這套教程的目標讀者包括:
軟件開發人員
相信微服務架構(Microservice Architectur)會逐漸成爲開發應用系統的主流。而容器則是這種架構的基石。市場將須要更多可以開發出基於容器的應用程序的軟件開發人員。
IT 實施和運維工程師
容器爲應用提供了更好的打包和部署方式。愈來愈多的應用將以容器的方式在開發、測試和生產環境中運行。掌握容器相關技術將成爲實施和運維工程師的核心競爭力。
我本身
我堅信最好的學習方法是分享。編寫這個教程同時也是對本身學習和實踐容器技術的總結。對於知識,只有把它寫出來並可以讓其餘人理解,才能說明真正掌握了這項知識。
本系列教程包括如下三大塊內容:
下面分別介紹各部分包含的內容。
啓程
「啓程」會介紹容器的生態系統,讓你們先從總體上了解容器都包含哪些技術,各類技術之間的相互關係是什麼,而後再來看咱們的教程都會涉及生態中的哪些部分。
爲了讓你們儘快對容器有個感性認識,咱們會搭建實驗環境並運行第一個容器,爲以後的學習熱身。
容器技術
這是教程的主要內容,包含「容器核心知識」和「容器進階知識」兩部分。
核心知識主要回答有關容器 what, why 和 how 三方面的問題。 其中以 how 爲重,將展開討論架構、鏡像、容器、網絡和存儲。
進階知識包括將容器真正用於生產所必需的技術,包括多主機管理、跨主機網絡、監控、數據管理、日誌管理和安全管理。
容器平臺技術
以下圖所示,「容器平臺技術」包括容器編排引擎、容器管理平臺和基於容器的 PaaS。容器平臺技術在生態環境中佔據着舉足輕重的位置,對於容器是否可以落地,是否能應用於生產相當重要。
我會繼續採用《天天5分鐘玩轉OpenStack》的方式,經過大量的實驗由淺入深地探討和實踐容器技術,力求達到以下目標:
在內容的發佈上仍是經過微信公衆號(cloudman6)每週 一、三、5 按期分享。歡迎你們經過公衆號提出問題和建議,進行技術交流。
爲了下降學習的難度而且考慮到移動端碎片化閱讀的特色,每次推送的內容你們只須要花5分鐘就能看完(注意這裏說的是看完,有時候要徹底理解可能須要更多時間哈),每篇內容包含1-3個知識點,這就是我把教程命名爲《天天5分鐘玩轉Docker容器技術》的緣由。雖然是碎片化推送,但整個教程是系統、連貫和完整的,只是化整爲零了。
好了,今天這5分鐘算是開了個頭,下次咱們正式開始玩轉容器技術。
對於像容器這類平臺級別的技術,一般涉及的知識範圍會很廣,相關的軟件,解決方案也會不少,初學者每每容易迷失。
那怎麼辦呢?
咱們能夠從生活經驗中尋找答案。
當咱們去陌生城市旅遊想了解一下這個城市通常咱們會怎麼作?
我想大部分人應該會打開手機看一下這個城市的地圖:
一樣的道理,學習容器技術咱們能夠先從天上鳥瞰一下:
首先得對容器技術有個總體認識,以後咱們的學習纔可以有的放矢,纔可以分清輕重緩急,作到心中有數,這樣就不容易迷失了。
接下來我會根據本身的經驗幫你們規劃一條學習路線,一塊兒探索容器生態系統。
學習新技術獲得及時反饋是很是重要的,因此咱們立刻會搭建實驗環境,並運行第一個容器,感覺什麼是容器。
千里之行始於足下,讓咱們從瞭解生態系統開始吧。
容器生態系統
一談到容器,你們都會想到 Docker。
Docker 如今幾乎是容器的代名詞。確實,是 Docker 將容器技術發揚光大。同時,你們也須要知道圍繞 Docker 還有一個生態系統。Docker 是這個生態系統的基石,但完善的生態系統纔是保障 Docker 以及容器技術可以真正健康發展的決定因素。
大體來看,容器生態系統包含核心技術、平臺技術和支持技術。
下面分別介紹。
容器核心技術
容器核心技術是指可以讓 container 在 host 上運行起來的那些技術。
這些技術包括容器規範、容器 runtime、容器管理工具、容器定義工具、Registry 以及 容器 OS,下面分別介紹。
容器規範
容器不光是 Docker,還有其餘容器,好比 CoreOS 的 rkt。爲了保證容器生態的健康發展,保證不一樣容器之間可以兼容,包含 Docker、CoreOS、Google在內的若干公司共同成立了一個叫 Open Container Initiative(OCI) 的組織,其目是制定開放的容器規範。
目前 OCI 發佈了兩個規範:runtime spec 和 image format spec。
有了這兩個規範,不一樣組織和廠商開發的容器可以在不一樣的 runtime 上運行。這樣就保證了容器的可移植性和互操做性。
容器 runtime
runtime 是容器真正運行的地方。runtime 須要跟操做系統 kernel 緊密協做,爲容器提供運行環境。
若是你們用過 Java,能夠這樣來理解 runtime 與容器的關係:
Java 程序就比如是容器,JVM 則比如是 runtime。JVM 爲 Java 程序提供運行環境。一樣的道理,容器只有在 runtime 中才能運行。
lxc、runc 和 rkt 是目前主流的三種容器 runtime。
lxc 是 Linux 上老牌的容器 runtime。Docker 最初也是用 lxc 做爲 runtime。
runc 是 Docker 本身開發的容器 runtime,符合 oci 規範,也是如今 Docker 的默認 runtime。
rkt 是 CoreOS 開發的容器 runtime,符合 oci 規範,於是可以運行 Docker 的容器。
容器管理工具
光有 runtime 還不夠,用戶得有工具來管理容器啊。容器管理工具對內與 runtime 交互,對外爲用戶提供 interface,好比 CLI。這就比如除了 JVM,還得提供 java
命令讓用戶可以啓停應用不是。
lxd 是 lxc 對應的管理工具。
runc 的管理工具是 docker engine。docker engine 包含後臺 deamon 和 cli 兩個部分。咱們一般提到 Docker,通常就是指的 docker engine。
rkt 的管理工具是 rkt cli。
容器定義工具
容器定義工具容許用戶定義容器的內容和屬性,這樣容器就可以被保存,共享和重建。
docker image 是 docker 容器的模板,runtime 依據 docker image 建立容器。
dockerfile 是包含若干命令的文本文件,能夠經過這些命令建立出 docker image。
ACI (App Container Image) 與 docker image 相似,只不過它是由 CoreOS 開發的 rkt 容器的 image 格式。
Registry
容器是經過 image 建立的,須要有一個倉庫來統一存放 image,這個倉庫就叫作 Registry。
企業能夠用 Docker Registry 構建私有的 Registry。
Docker Hub(https://hub.docker.com) 是 Docker 爲公衆提供的託管 Registry,上面有不少現成的 image,爲 Docker 用戶提供了極大的便利。
http://Quay.io(https://quay.io/)是另外一個公共託管 Registry,提供與 Docker Hub 相似的服務。
容器 OS
因爲有容器 runtime,幾乎全部的 Linux、MAC OS 和 Windows 均可以運行容器。但這不併無妨礙容器 OS 的問世。
容器 OS 是專門運行容器的操做系統。與常規 OS 相比,容器 OS 一般體積更小,啓動更快。由於是爲容器定製的 OS,一般它們運行容器的效率會更高。
目前已經存在很多容器 OS,CoreOS、atomic 和 ubuntu core 是其中的傑出表明。
容器核心技術使得容器可以在單個 host 上運行。而容器平臺技術可以讓容器做爲集羣在分佈式環境中運行。
容器平臺技術包括容器編排引擎、容器管理平臺和基於容器的 PaaS。
容器編排引擎
基於容器的應用通常會採用微服務架構。在這種架構下,應用被劃分爲不一樣的組件,並以服務的形式運行在各自的容器中,經過 API 對外提供服務。爲了保證應用的高可用,每一個組件均可能會運行多個相同的容器。這些容器會組成集羣,集羣中的容器會根據業務須要被動態地建立、遷移和銷燬。
你們能夠看到,這樣一個基於微服務架構的應用系統其實是一個動態的可伸縮的系統。這對咱們的部署環境提出了新的要求,咱們須要有一種高效的方法來管理容器集羣。而這,就是容器編排引擎要乾的工做。
所謂編排(orchestration),一般包括容器管理、調度、集羣定義和服務發現等。經過容器編排引擎,容器被有機的組合成微服務應用,實現業務需求。
docker swarm 是 Docker 開發的容器編排引擎。
kubernetes 是 Google 領導開發的開源容器編排引擎,同時支持 Docker 和 CoreOS 容器。
mesos 是一個通用的集羣資源調度平臺,mesos 與 marathon 一塊兒提供容器編排引擎功能。
以上三者是當前主流的容器編排引擎。
容器管理平臺
容器管理平臺是架構在容器編排引擎之上的一個更爲通用的平臺。一般容器管理平臺可以支持多種編排引擎,抽象了編排引擎的底層實現細節,爲用戶提供更方便的功能,好比 application catalog 和一鍵應用部署等。
Rancher 和 ContainerShip 是容器管理平臺的典型表明。
基於容器的 PaaS
基於容器的 PaaS 爲微服務應用開發人員和公司提供了開發、部署和管理應用的平臺,使用戶沒必要關心底層基礎設施而專一於應用的開發。
Deis、Flynn 和 Dokku 都是開源容器 PaaS 的表明。
下面這些技術被用於支持基於容器的基礎設施。
容器網絡
容器的出現使網絡拓撲變得更加動態和複雜。用戶須要專門的解決方案來管理容器與容器,容器與其餘實體之間的連通性和隔離性。
docker network 是 Docker 原生的網絡解決方案。除此以外,咱們還能夠採用第三方開源解決方案,例如 flannel、weave 和 calico。不一樣方案的設計和實現方式不一樣,各有優點和特色,須要根據實際須要來選型。
服務發現
動態變化是微服務應用的一大特色。當負載增長時,集羣會自動建立新的容器;負載減少,多餘的容器會被銷燬。容器也會根據 host 的資源使用狀況在不一樣 host 中遷移,容器的 IP 和端口也會隨之發生變化。
在這種動態的環境下,必需要有一種機制讓 client 可以知道如何訪問容器提供的服務。這就是服務發現技術要完成的工做。
服務發現會保存容器集羣中全部微服務最新的信息,好比 IP 和端口,並對外提供 API,提供服務查詢功能。
etcd、consul 和 zookeeper 是服務發現的典型解決方案。
監控
監控對於基礎架構很是重要,而容器的動態特徵對監控提出更多挑戰。針對容器環境,已經涌現出不少監控工具和方案。
docker ps/top/stats 是 Docker 原生的命令行監控工具。除了命令行,Docker 也提供了 stats API,用戶能夠經過 HTTP 請求獲取容器的狀態信息。
sysdig、cAdvisor/Heapster 和 Weave Scope 是其餘開源的容器監控方案。
數據管理
容器常常會在不一樣的 host 之間遷移,如何保證持久化數據也可以動態遷移,是 Rex-Ray 這類數據管理工具提供的能力。
日誌管理
日誌爲問題排查和事件管理提供了重要依據。
docker logs 是 Docker 原生的日誌工具。而 logspout 對日誌提供了路由功能,它能夠收集不一樣容器的日誌並轉發給其餘工具進行後處理。
安全性
對於年輕的容器,安全性一直是業界爭論的焦點。
OpenSCAP 可以對容器鏡像進行掃描,發現潛在的漏洞。
本教程覆蓋的知識範圍
前面咱們已經鳥瞰了整個容器生態系統,對容器所涉及的技術體系有了全面的認識。那咱們的系列教程會討論其中的哪些內容呢?
會覆蓋容器生態系統 91.6% 的技術!
爲了讓你們對容器有個感性認識,咱們將盡快讓一個容器運行起來。首先咱們須要搭建實驗環境。
容器須要管理工具、runtime 和操做系統,咱們的選擇以下:
本節咱們將在 ubuntu 16.04 虛擬機中安裝 Docker。由於安裝過程須要訪問 internet, 因此虛擬機必須可以上網。
Docker 支持幾乎全部的 Linux 發行版,也支持 Mac 和 Windows。各操做系統的安裝方法能夠訪問:https://docs.docker.com/engine/installation/
Docker 分爲開源免費的 CE(Community Edition)版本和收費的 EE(Enterprise Edition)版本。下面咱們將按照文檔,經過如下步驟在 Ubuntu 16.04 上安裝 Docker CE 版本。
配置 Docker 的 apt 源
1.安裝包,容許 apt
命令 HTTPS 訪問 Docker 源。
2.添加 Docker 官方的 GPG key
3.將 Docker 的源添加到 /etc/apt/sources.list
安裝 Docker
環境就緒,立刻運行第一個容器,執行命令:
其過程能夠簡單的描述爲:
下面咱們能夠經過瀏覽器驗證容器是否正常工做。在瀏覽器中輸入 http://[your ubuntu host IP]
能夠訪問容器的 http 服務了,第一個容器運行成功!咱們輕輕鬆鬆就擁有了一個 WEB 服務器。隨着學習的深刻,會看到容器技術帶給咱們更多的價值。
咱們已經完成了教程的第一部分。
咱們認識了容器生態系統,後面會陸續學習生態系統中的大部分技術。咱們在 Ubuntu 16.04 上配置好了實驗環境,併成功運行了第一個容器 httpd。
容器大門已經打開,讓咱們去探祕吧。
容器是一種輕量級、可移植、自包含的軟件打包技術,使應用程序能夠在幾乎任何地方以相同的方式運行。開發人員在本身筆記本上建立並測試好的容器,無需任何修改就可以在生產系統的虛擬機、物理服務器或公有云主機上運行。
容器與虛擬機
談到容器,就不得不將它與虛擬機進行對比,由於二者都是爲應用提供封裝和隔離。
容器由兩部分組成:
容器在 Host 操做系統的用戶空間中運行,與操做系統的其餘進程隔離。這一點顯著區別於的虛擬機。
傳統的虛擬化技術,好比 VMWare, KVM, Xen,目標是建立完整的虛擬機。爲了運行應用,除了部署應用自己及其依賴(一般幾十 MB),還得安裝整個操做系統(幾十 GB)。
下圖展現了兩者的區別。
如圖所示,因爲全部的容器共享同一個 Host OS,這使得容器在體積上要比虛擬機小不少。另外,啓動容器不須要啓動整個操做系統,因此容器部署和啓動速度更快,開銷更小,也更容易遷移。
爲何須要容器?容器到底解決的是什麼問題?
簡要的答案是:容器使軟件具有了超強的可移植能力。
容器解決的問題
咱們來看看今天的軟件開發面臨着怎樣的挑戰?
現在的系統在架構上較十年前已經變得很是複雜了。之前幾乎全部的應用都採用三層架構(Presentation/Application/Data),系統部署到有限的幾臺物理服務器上(Web Server/Application Server/Database Server)。
而今天,開發人員一般使用多種服務(好比 MQ,Cache,DB)構建和組裝應用,並且應用極可能會部署到不一樣的環境,好比虛擬服務器,私有云和公有云。
一方面應用包含多種服務,這些服務有本身所依賴的庫和軟件包;另外一方面存在多種部署環境,服務在運行時可能須要動態遷移到不一樣的環境中。這就產生了一個問題:
如何讓每種服務可以在全部的部署環境中順利運行?
因而咱們獲得了下面這個矩陣:
各類服務和環境經過排列組合產生了一個大矩陣。開發人員在編寫代碼時須要考慮不一樣的運行環境,運維人員則須要爲不一樣的服務和平臺配置環境。對他們雙方來講,這都是一項困難而艱鉅的任務。
如何解決這個問題呢?
聰明的技術人員從傳統的運輸行業找到了答案。
幾十年前,運輸業面臨着相似的問題。
每一次運輸,貨主與承運方都會擔憂因貨物類型的不一樣而致使損失,好比幾個鐵桶錯誤地壓在了一堆香蕉上。另外一方面,運輸過程當中須要使用不一樣的交通工具也讓整個過程痛苦不堪:貨物先裝上車運到碼頭,卸貨,而後裝上船,到岸後又卸下船,再裝上火車,到達目的地,最後卸貨。一半以上的時間花費在裝、卸貨上,並且搬上搬下還容易損壞貨物。
這一樣也是一個 NxM 的矩陣。
幸運的是,集裝箱的發明解決這個難題。
任何貨物,不管鋼琴仍是保時捷,都被放到各自的集裝箱中。集裝箱在整個運輸過程當中都是密封的,只有到達最終目的地才被打開。標準集裝箱能夠被高效地裝卸、重疊和長途運輸。現代化的起重機能夠自動在卡車、輪船和火車之間移動集裝箱。集裝箱被譽爲運輸業與世界貿易最重要的發明。
Docker 將集裝箱思想運用到軟件打包上,爲代碼提供了一個基於容器的標準化運輸系統。Docker 能夠將任何應用及其依賴打包成一個輕量級、可移植、自包含的容器。容器能夠運行在幾乎全部的操做系統上。
其實,「集裝箱」 和 「容器」 對應的英文單詞都是 「Container」。
「容器」 是國內約定俗成的叫法,多是由於容器比集裝箱更抽象,更適合軟件領域的原故吧。
我我的認爲:在老外的思惟中,「Container」 只用到了集裝箱這一個意思,Docker 的 Logo 不就是一堆集裝箱嗎?
Docker 的特性
咱們能夠看看集裝箱思想是如何與 Docker 各類特性相對應的。
容器的優點
對於開發人員 - Build Once, Run Anywhere
容器意味着環境隔離和可重複性。開發人員只需爲應用建立一次運行環境,而後打包成容器即可在其餘機器上運行。另外,容器環境與所在的 Host 環境是隔離的,就像虛擬機同樣,但更快更簡單。
對於運維人員 - Configure Once, Run Anything
只須要配置好標準的 runtime 環境,服務器就能夠運行任何容器。這使得運維人員的工做變得更高效,一致和可重複。容器消除了開發、測試、生產環境的不一致性。
接下來學習容器核心知識的最主要部分。
咱們首先會介紹 Docker 的架構,而後分章節詳細討論 Docker 的鏡像、容器、網絡和存儲。
Docker 的核心組件包括:
Docker 架構以下圖所示:
Docker 採用的是 Client/Server 架構。客戶端向服務器發送請求,服務器負責構建、運行和分發容器。客戶端和服務器能夠運行在同一個 Host 上,客戶端也能夠經過 socket 或 REST API 與遠程的服務器通訊。
最經常使用的 Docker 客戶端是 docker
命令。經過 docker
咱們能夠方便地在 Host 上構建和運行容器。
docker
支持不少操做(子命令),後面會逐步用到。
除了 docker
命令行工具,用戶也能夠經過 REST API 與服務器通訊。
Docker daemon 是服務器組件,以 Linux 後臺服務的方式運行。
Docker daemon 運行在 Docker host 上,負責建立、運行、監控容器,構建、存儲鏡像。
默認配置下,Docker daemon 只能響應來自本地 Host 的客戶端請求。若是要容許遠程客戶端請求,須要在配置文件中打開 TCP 監聽,步驟以下:
1.編輯配置文件 /etc/systemd/system/multi-user.target.wants/docker.service,在環境變量 ExecStart
後面添加 -H tcp://0.0.0.0
,容許來自任意 IP 的客戶端鏈接。
若是使用的是其餘操做系統,配置文件的位置可能會不同。
2.重啓 Docker daemon。
3.服務器 IP 爲 192.168.56.102,客戶端在命令行里加上 -H 參數,便可與遠程服務器通訊。
info
子命令用於查看 Docker 服務器的信息。
可將 Docker 鏡像當作只讀模板,經過它能夠建立 Docker 容器。
例如某個鏡像可能包含一個 Ubuntu 操做系統、一個 Apache HTTP Server 以及用戶開發的 Web 應用。
鏡像有多種生成方法:
咱們能夠將鏡像的內容和建立步驟描述在一個文本文件中,這個文件被稱做 Dockerfile,經過執行 docker build <docker-file>
命令能夠構建出 Docker 鏡像,後面咱們會討論。
Docker 容器就是 Docker 鏡像的運行實例。
用戶能夠經過 CLI(docker)或是 API 啓動、中止、移動或刪除容器。能夠這麼認爲,對於應用軟件,鏡像是軟件生命週期的構建和打包階段,而容器則是啓動和運行階段。
Registry 是存放 Docker 鏡像的倉庫,Registry 分私有和公有兩種。
Docker Hub(https://hub.docker.com/) 是默認的 Registry,由 Docker 公司維護,上面有數以萬計的鏡像,用戶能夠自由下載和使用。
出於對速度或安全的考慮,用戶也能夠建立本身的私有 Registry。後面咱們會學習如何搭建私有 Registry。
docker pull
命令能夠從 Registry 下載鏡像。 docker run
命令則是先下載鏡像(若是本地沒有),而後再啓動容器。
還記得咱們運行的第一個容器嗎?如今經過它來體會一下 Docker 各個組件是如何協做的。
容器啓動過程以下:
docker run
命令。docker images
能夠查看到 httpd 已經下載到本地。
docker ps
或者 docker container ls
顯示容器正在運行。
Docker 借鑑了集裝箱的概念。標準集裝箱將貨物運往世界各地,Docker 將這個模型運用到本身的設計哲學中,惟一不一樣的是:集裝箱運輸貨物,而 Docker 運輸軟件。
每一個容器都有一個軟件鏡像,至關於集裝箱中的貨物。容器能夠被建立、啓動、關閉和銷燬。和集裝箱同樣,Docker 在執行這些操做時,並不關心容器裏到底裝的什麼,它無論裏面是 Web Server,仍是 Database。
用戶不須要關心容器最終會在哪裏運行,由於哪裏均可以運行。
開發人員能夠在筆記本上構建鏡像並上傳到 Registry,而後 QA 人員將鏡像下載到物理或虛擬機作測試,最終容器會部署到生產環境。
使用 Docker 以及容器技術,咱們能夠快速構建一個應用服務器、一個消息中間件、一個數據庫、一個持續集成環境。由於 Docker Hub 上有咱們能想到的幾乎全部的鏡像。
不知你們是否意識到,潘多拉盒子已經被打開。容器不但下降了咱們學習新技術的門檻,更提升了效率。
若是你是一個運維人員,想研究負載均衡軟件 HAProxy,只須要執行 docker run haproxy
,無需繁瑣的手工安裝和配置既能夠直接進入實戰。
若是你是一個開發人員,想學習怎麼用 django 開發 Python Web 應用,執行 docker run django
,在容器裏隨便折騰吧,不用擔憂會搞亂 Host 的環境。
不誇張的說:容器大大提高了 IT 人員的幸福指數。
鏡像是 Docker 容器的基石,容器是鏡像的運行實例,有了鏡像才能啓動容器。
本章內容安排以下:
爲何咱們要討論鏡像的內部結構?
若是隻是使用鏡像,固然不須要了解,直接經過 docker
命令下載和運行就能夠了。
但若是咱們想建立本身的鏡像,或者想理解 Docker 爲何是輕量級的,就很是有必要學習這部分知識了。
咱們從一個最小的鏡像開始吧。
hello-world - 最小的鏡像
hello-world 是 Docker 官方提供的一個鏡像,一般用來驗證 Docker 是否安裝成功。
咱們先經過 docker pull
從 Docker Hub 下載它。
用 docker images
命令查看鏡像的信息。
hello-world 鏡像居然還不到 2KB!
經過 docker run
運行。
其實咱們更關心 hello-world 鏡像包含哪些內容。
Dockerfile 是鏡像的描述文件,定義瞭如何構建 Docker 鏡像。Dockerfile 的語法簡潔且可讀性強,後面咱們會專門討論如何編寫 Dockerfile。
hello-world 的 Dockerfile 內容以下:
只有短短三條指令。
鏡像 hello-world 中就只有一個可執行文件 「hello」,其功能就是打印出 「Hello from Docker ......」 等信息。
/hello 就是文件系統的所有內容,連最基本的 /bin,/usr, /lib, /dev 都沒有。
hello-world 雖然是一個完整的鏡像,但它並無什麼實際用途。一般來講,咱們但願鏡像能提供一個基本的操做系統環境,用戶能夠根據須要安裝和配置軟件。
這樣的鏡像咱們稱做 base 鏡像。
上一節咱們介紹了最小的 Docker 鏡像,本節討論 base 鏡像。
base 鏡像有兩層含義:
因此,能稱做 base 鏡像的一般都是各類 Linux 發行版的 Docker 鏡像,好比 Ubuntu, Debian, CentOS 等。
咱們以 CentOS 爲例考察 base 鏡像包含哪些內容。
下載鏡像:
docker pull centos
查看鏡像信息:
鏡像大小不到 200MB。
等一下!
一個 CentOS 才 200MB ?
平時咱們安裝一個 CentOS 至少都有幾個 GB,怎麼可能才 200MB !
相信這是幾乎全部 Docker 初學者都會有的疑問,包括我本身。下面咱們來解釋這個問題。
Linux 操做系統由內核空間和用戶空間組成。以下圖所示:
rootfs
內核空間是 kernel,Linux 剛啓動時會加載 bootfs 文件系統,以後 bootfs 會被卸載掉。
用戶空間的文件系統是 rootfs,包含咱們熟悉的 /dev, /proc, /bin 等目錄。
對於 base 鏡像來講,底層直接用 Host 的 kernel,本身只須要提供 rootfs 就好了。
而對於一個精簡的 OS,rootfs 能夠很小,只須要包括最基本的命令、工具和程序庫就能夠了。相比其餘 Linux 發行版,CentOS 的 rootfs 已經算臃腫的了,alpine 還不到 10MB。
咱們平時安裝的 CentOS 除了 rootfs 還會選裝不少軟件、服務、圖形桌面等,須要好幾個 GB 就不足爲奇了。
base 鏡像提供的是最小安裝的 Linux 發行版。
下面是 CentOS 鏡像的 Dockerfile 的內容:
第二行 ADD 指令添加到鏡像的 tar 包就是 CentOS 7 的 rootfs。在製做鏡像時,這個 tar 包會自動解壓到 / 目錄下,生成 /dev, /proc, /bin 等目錄。
注:可在 Docker Hub 的鏡像描述頁面中查看 Dockerfile 。
支持運行多種 Linux OS
不一樣 Linux 發行版的區別主要就是 rootfs。
好比 Ubuntu 14.04 使用 upstart 管理服務,apt 管理軟件包;而 CentOS 7 使用 systemd 和 yum。這些都是用戶空間上的區別,Linux kernel 差異不大。
因此 Docker 能夠同時支持多種 Linux 鏡像,模擬出多種操做系統環境。
上圖 Debian 和 BusyBox(一種嵌入式 Linux)上層提供各自的 rootfs,底層共用 Docker Host 的 kernel。
這裏須要說明的是:
1. base 鏡像只是在用戶空間與發行版一致,kernel 版本與發行版是不一樣的。
例如 CentOS 7 使用 3.x.x 的 kernel,若是 Docker Host 是 Ubuntu 16.04(好比咱們的實驗環境),那麼在 CentOS 容器中使用的實際是是 Host 4.x.x 的 kernel。
① Host kernel 爲 4.4.0-31
② 啓動並進入 CentOS 容器
③ 驗證容器是 CentOS 7
④ 容器的 kernel 版本與 Host 一致
2. 容器只能使用 Host 的 kernel,而且不能修改。
全部容器都共用 host 的 kernel,在容器中沒辦法對 kernel 升級。若是容器對 kernel 版本有要求(好比應用只能在某個 kernel 版本下運行),則不建議用容器,這種場景虛擬機可能更合適。
Docker 支持經過擴展示有鏡像,建立新的鏡像。
實際上,Docker Hub 中 99% 的鏡像都是經過在 base 鏡像中安裝和配置須要的軟件構建出來的。好比咱們如今構建一個新的鏡像,Dockerfile 以下:
① 新鏡像再也不是從 scratch 開始,而是直接在 Debian base 鏡像上構建。
② 安裝 emacs 編輯器。
③ 安裝 apache2。
④ 容器啓動時運行 bash。
構建過程以下圖所示:
能夠看到,新鏡像是從 base 鏡像一層一層疊加生成的。每安裝一個軟件,就在現有鏡像的基礎上增長一層。
問什麼 Docker 鏡像要採用這種分層結構呢?
最大的一個好處就是 - 共享資源。
好比:有多個鏡像都從相同的 base 鏡像構建而來,那麼 Docker Host 只需在磁盤上保存一份 base 鏡像;同時內存中也只需加載一份 base 鏡像,就能夠爲全部容器服務了。並且鏡像的每一層均可以被共享,咱們將在後面更深刻地討論這個特性。
這時可能就有人會問了:若是多個容器共享一份基礎鏡像,當某個容器修改了基礎鏡像的內容,好比 /etc 下的文件,這時其餘容器的 /etc 是否也會被修改?
答案是不會!
修改會被限制在單個容器內。
這就是咱們接下來要學習的容器 Copy-on-Write 特性。
可寫的容器層
當容器啓動時,一個新的可寫層被加載到鏡像的頂部。
這一層一般被稱做「容器層」,「容器層」之下的都叫「鏡像層」。
全部對容器的改動 - 不管添加、刪除、仍是修改文件都只會發生在容器層中。
只有容器層是可寫的,容器層下面的全部鏡像層都是隻讀的。
下面咱們深刻討論容器層的細節。
鏡像層數量可能會不少,全部鏡像層會聯合在一塊兒組成一個統一的文件系統。若是不一樣層中有一個相同路徑的文件,好比 /a,上層的 /a 會覆蓋下層的 /a,也就是說用戶只能訪問到上層中的文件 /a。在容器層中,用戶看到的是一個疊加以後的文件系統。
只有當須要修改時才複製一份數據,這種特性被稱做 Copy-on-Write。可見,容器層保存的是鏡像變化的部分,不會對鏡像自己進行任何修改。
這樣就解釋了咱們前面提出的問題:容器層記錄對鏡像的修改,全部鏡像層都是隻讀的,不會被容器修改,因此鏡像能夠被多個容器共享。
對於 Docker 用戶來講,最好的狀況是不須要本身建立鏡像。幾乎全部經常使用的數據庫、中間件、應用軟件等都有現成的 Docker 官方鏡像或其餘人和組織建立的鏡像,咱們只須要稍做配置就能夠直接使用。
使用現成鏡像的好處除了省去本身作鏡像的工做量外,更重要的是能夠利用前人的經驗。特別是使用那些官方鏡像,由於 Docker 的工程師知道如何更好的在容器中運行軟件。
固然,某些狀況下咱們也不得不本身構建鏡像,好比:
因此本節咱們將介紹構建鏡像的方法。同時分析構建的過程也可以加深咱們對前面鏡像分層結構的理解。
Docker 提供了兩種構建鏡像的方法:
docker commit
docker commit 命令是建立新鏡像最直觀的方法,其過程包含三個步驟:
舉個例子:在 ubuntu base 鏡像中安裝 vi 並保存爲新鏡像。
-it
參數的做用是以交互模式進入容器,並打開終端。412b30588f4a
是容器的內部 ID。
確認 vi 沒有安裝。
安裝 vi。
silly_goldberg
是 Docker 爲咱們的容器隨機分配的名字。
執行 docker commit 命令將容器保存爲鏡像。
新鏡像命名爲 ubuntu-with-vi
。
查看新鏡像的屬性。
從 size 上看到鏡像由於安裝了軟件而變大了。
重新鏡像啓動容器,驗證 vi 已經可使用。
以上演示瞭如何用 docker commit 建立新鏡像。然而,Docker 並不建議用戶經過這種方式構建鏡像。緣由以下:
既然 docker commit 不是推薦的方法,咱們幹嗎還要花時間學習呢?
緣由是:即使是用 Dockerfile(推薦方法)構建鏡像,底層也 docker commit 一層一層構建新鏡像的。學習 docker commit 可以幫助咱們更加深刻地理解構建過程和鏡像的分層結構。
Dockerfile 是一個文本文件,記錄了鏡像構建的全部步驟。
第一個 Dockerfile
用 Dockerfile 建立上節的 ubuntu-with-vi,其內容則爲:
下面咱們運行 docker build 命令構建鏡像並詳細分析每一個細節。
① 當前目錄爲 /root。
② Dockerfile 準備就緒。
③ 運行 docker build 命令,-t
將新鏡像命名爲 ubuntu-with-vi-dockerfile
,命令末尾的 .
指明 build context 爲當前目錄。Docker 默認會從 build context 中查找 Dockerfile 文件,咱們也能夠經過 -f
參數指定 Dockerfile 的位置。
④ 從這步開始就是鏡像真正的構建過程。 首先 Docker 將 build context 中的全部文件發送給 Docker daemon。build context 爲鏡像構建提供所須要的文件或目錄。
Dockerfile 中的 ADD、COPY 等命令能夠將 build context 中的文件添加到鏡像。此例中,build context 爲當前目錄 /root
,該目錄下的全部文件和子目錄都會被髮送給 Docker daemon。
因此,使用 build context 就得當心了,不要將多餘文件放到 build context,特別不要把 /
、/usr
做爲 build context,不然構建過程會至關緩慢甚至失敗。
⑤ Step 1:執行 FROM
,將 ubuntu 做爲 base 鏡像。
ubuntu 鏡像 ID 爲 f753707788c5。
⑥ Step 2:執行 RUN
,安裝 vim,具體步驟爲 ⑦、⑧、⑨。
⑦ 啓動 ID 爲 9f4d4166f7e3 的臨時容器,在容器中經過 apt-get 安裝 vim。
⑧ 安裝成功後,將容器保存爲鏡像,其 ID 爲 35ca89798937。
這一步底層使用的是相似 docker commit 的命令。
⑨ 刪除臨時容器 9f4d4166f7e3。
⑩ 鏡像構建成功。
經過 docker images 查看鏡像信息。
鏡像 ID 爲 35ca89798937,與構建時的輸出一致。
在上面的構建過程當中,咱們要特別注意指令 RUN 的執行過程 ⑦、⑧、⑨。Docker 會在啓動的臨時容器中執行操做,並經過 commit 保存爲新的鏡像。
查看鏡像分層結構
ubuntu-with-vi-dockerfile 是經過在 base 鏡像的頂部添加一個新的鏡像層而獲得的。
這個新鏡像層的內容由 RUN apt-get update && apt-get install -y vim
生成。這一點咱們能夠經過 docker history
命令驗證。
docker history
會顯示鏡像的構建歷史,也就是 Dockerfile 的執行過程。
ubuntu-with-vi-dockerfile 與 ubuntu 鏡像相比,確實只是多了頂部的一層 35ca89798937,由 apt-get 命令建立,大小爲 97.07MB。docker history 也向咱們展現了鏡像的分層結構,每一層由上至下排列。
注: 表示沒法獲取 IMAGE ID,一般從 Docker Hub 下載的鏡像會有這個問題。