點擊關注公衆號,Java乾貨及時送達
1. Docker出現的背景
在平常的研發和項目場景中,以下情況普遍存在:
-
個人開發環境 爲了做大數據相關項目,需要安裝一套CDH集羣,常見的做法是在自己電腦裏搭建3臺與CDH版本對應的虛擬機,把CDH集羣裝起來後,考慮到以後很有可能還要使用一個乾淨的CDH集羣,爲了避免以後重複安裝環境,通常會對整套CDH集羣做一個備份,這樣電腦裏就有6個虛擬機鏡像了。另外,後面在學習其他技術時,比如學習Ambari大數據集羣,那麼爲了不破壞已有的虛擬機環境,又要重新搭建3臺虛擬機,本機磁盤很快被一大堆的虛擬機鏡像佔滿。 -
公司內部開發環境 公司裏往往會以小團隊的方式來做項目,一般由運維部門從他們管理的服務器資源中分配出虛擬機供團隊內部開發測試使用。 比如做一個與機器學習相關的項目: 1)小明在運維部門分配的虛擬機上搭建了一套Ambari集羣,拿來跑大數據相關業務
-
開發/測試/現場環境 研發人員在開發環境裏寫好了代碼做好測試後,提交給測試部門,測試人員在測試環境跑起來發現有BUG,研發人員說在開發環境沒這個BUG,和測試人員多次扯皮解決BUG後發佈版本,發到現場在生產環境部署後,又發現有BUG,這下輪到工程人員和測試人員扯皮。有時候爲了兼容特殊的現場環境,還需要對代碼進行定製化修改,拉出分支,這樣導致了每次到現場升級都是一場噩夢 -
升級或遷移項目 在每次發版本要升級到現場時,如果現場起了多個tomcat應用,那麼需要對每個tomcat都先停掉,替換war包,然後再起起來,輪流着做,不僅繁瑣而且很容易出錯,如果遇到升級後出現嚴重BUG,還要手工做回退。另外,如果項目想上雲,那麼在雲上部署後要重新進行一輪測試,如果後面考慮還雲廠商,可能相同的測試還要再進行一次(比如更換了數據存儲組件),費時費力。
總結以上列舉的所有場景,他們存在的一個共同的問題是:沒有一種既能夠屏蔽操作系統差異,又能夠以不降低性能的方式來運行應用的技術,來解決環境依賴的問題。Docker應運而生。
2. Docker是什麼
Linux Container
是Linux系統提供的容器化技術,簡稱LXC
,它結合Namespace和CGroup技術爲用戶提供了更易用的接口來實現容器化。LXC僅爲一種輕量級的容器化技術,它僅能對部分資源進行限制,無法做到諸如網絡限制、磁盤空間佔用限制等。dotCloud公司結合LXC和以下列出的技術
實現了Docker容器引擎,相比於LXC,Docker具備更加全面的資源控制能力,是一種應用級別的容器引擎。
-
Chroot:該技術能在container裏構造完整的Linux文件系統; -
Veth:該技術能夠在主機上虛擬出一張網卡與container裏的eth0網卡進行橋接,實現容器與主機、容器之間的網絡通信; -
UnionFS:聯合文件系統,Docker利用該技術“Copy on Write”的特點實現容器的快速啓動和極少的資源佔用,後面會專門介紹該文件系統; -
Iptables/netfilter:通過這兩個技術實現控制container網絡訪問策略; -
TC:該技術主要用來做流量隔離,限制帶寬; -
Quota:該技術用來限制磁盤讀寫空間的大小; -
Setrlimit:該技術用來限制container中打開的進程數,限制打開的文件個數等
也正是因爲Docker依賴Linux內核的這些技術,至少使用3.8或更高版本的內核才能運行Docker容器,官方建議使用3.10以上的內核版本。
3. 與傳統虛擬化技術的區別
4. Docker基本概念
Docker主要有如下幾個概念:
-
引擎:創建和管理容器的工具,通過讀取鏡像來生成容器,並負責從倉庫拉取鏡像或提交鏡像到倉庫中; -
鏡像:類似於虛擬機鏡像,一般由一個基本操作系統環境和多個應用程序打包而成,是創建容器的模板; -
容器:可看作一個簡易版的Linxu系統環境(包括root用戶權限、進程空間、用戶空間和網絡空間等)以及運行在其中的應用程序打包而成的盒子; -
倉庫:集中存放鏡像文件的場所,分爲公共倉庫和私有倉庫,目前最大的公共倉庫是官方提供的Docker Hub,此外國內的阿里雲、騰訊雲等也提供了公共倉庫; -
宿主機:運行引擎的操作系統所在服務器。
5. Docker與虛擬機、Git、JVM的類比
爲了讓大家對Docker有更直觀的認識,下面分別進行三組類比:
Docker的倉庫思想與Git是相同的。
Java是基於JVM適配操作系統的特點來屏蔽系統的差異,Docker則是利用內核版本兼容性的特點來實現一次構建導出運行,只要Linux系統的內核是3.8或更高的版本,就都能把容器跑起來。
當然,正如Java中如果應用代碼使用了JDK10的新特性,基於JDK8就無法運行一樣,如果容器內的應用使用了4.18版本的內核特性,那麼在CentOS7(內核版本爲3.10)啓動容器時,雖然容器能夠啓動,但裏面應用的功能是無法正常運行的,除非把宿主機的操作系統內核升級到4.18版本。
6. Docker鏡像文件系統
UnionFS可以把多個物理位置獨立的目錄(也叫分支)內容聯合掛載到同一個目錄下,UnionFS允許控制這些目錄的讀寫權限,此外對於只讀的文件和目錄,它具有“Copy on Write(寫實複製)”的特點,即如果對一個只讀的文件進行修改,在修改前會先把文件複製一份到可寫層(可能是磁盤裏的一個目錄),所有的修改操作其實都是對這個文件副本進行修改,原來的只讀文件並不會變化。其中一個使用UnionFS的例子是:Knoppix,一個用於Linux演示、光盤教學和商業產品演示的Linux發行版,它就是把一個CD/DVD和一個存在在可讀寫設備(例如U盤)聯合掛載,這樣在演示過程中任何對CD/DVD上文件的改動都會在被應用在U盤上,不改變原來的CD/DVD上的內容。
UnionFS有很多種,其中Docker中常用的是AUFS,這是UnionFS的升級版,除此之外還有DeviceMapper、Overlay2、ZFS和 VFS等。Docker鏡像的每一層默認存放在/var/lib/docker/aufs/diff
目錄中,當用戶啓動一個容器時,Docker引擎首先在/var/lib/docker/aufs/diff
中新建一個可讀寫層目錄,然後使用UnionFS把該可讀寫層目錄和指定鏡像的各層目錄聯合掛載到/var/lib/docker/aufs/mnt
裏的一個目錄中(其中指定鏡像的各層目錄都以只讀方式掛載),通過LXC等技術進行環境隔離和資源控制,使容器裏的應用僅依賴mnt目錄中對應的掛載目錄和文件運行起來。
利用UnionFS寫實複製的特點,在啓動一個容器時, Docker引擎實際上只是增加了一個可寫層和構造了一個Linux容器,這兩者都幾乎不消耗系統資源,因此Docker容器能夠做到秒級啓動,一臺服務器上能夠啓動上千個Docker容器,而傳統虛擬機在一臺服務器上啓動幾十個就已經非常喫力了,而且虛擬機啓動很慢,這是Docker相比於傳統虛擬機的兩個巨大的優勢。
當應用只是直接調用了內核功能來運作的情況下,應用本身就能直接作爲最底層的層來構建鏡像,但因爲容器本身會隔絕環境,因此容器內部是無法訪問宿主機裏文件的(除非指定了某些目錄或文件映射到容器內),這種情況下應用代碼就只能使用內核的功能。但是Linux內核僅提供了進程管理、內存管理、文件系統管理等一些基礎且底層的管理功能,在實際的場景中,幾乎所有軟件都是基於操作系統來開發的,因此往往都需要依賴操作系統的軟件和運行庫等,如果這些應用的下一層直接是內核,那麼應用將無法運行。所以實際上應用鏡像往往底層都是基於一個操作系統鏡像來補足運行依賴的。
Docker中的操作系統鏡像,與平常安裝系統時用的ISO鏡像不同。ISO鏡像裏包含了操作系統內核及該發行版系統包含的所有目錄和軟件,而Docker中的操作系統鏡像,不包含系統內核,僅包含系統必備的一些目錄(如/etc /proc等)和常用的軟件和運行庫等,可把操作系統鏡像看作內核之上的一個應用,一個封裝了內核功能,併爲用戶編寫的應用提供運行環境的工具。應用基於這樣的鏡像構建,就能夠利用上相應操作系統的各種軟件的功能和運行庫,此外,由於應用是基於操作系統鏡像來構建的,就算換到另外的服務器,只要操作系統鏡像中被應用使用到的功能能適配宿主機的內核,應用就能正常運行,這就是一次構建到處運行的原因。
下圖形象的表現出了鏡像和容器的關係:
7. Docker基礎操作系統
以上系統鏡像分別適用於不同的場景:
-
BusyBox:一個極簡版的Linux系統,集成了100多種常用Linux命令,大小不到2MB,被稱爲“Linux系統的瑞士軍刀”,適用於簡單測試場景; -
Alpine:一個面向安全的輕型Linux發行版系統,比BusyBox功能更完善,大小不到5MB,是官網推薦的基礎鏡像,由於其包含了足夠的基礎功能和體積較小,在生產環境中最常用; -
Debian/Ubuntu:Debian系列操作系統,功能完善,大小約170MB,適合研發環境; -
CentOS/Fedora:都是基於Redhat的Linux發行版,企業級服務器常用操作系統,穩定性高,大小約200MB,適合生產環境使用。
8. Docker持久化存儲
根據前面介紹的容器UnionFS寫實複製的特點,可知在容器裏增加、刪除或修改文件,其實都是對可寫層裏的文件副本進行了操作。在容器關閉後,該可寫層也會被刪除,對容器的所有修改都會失效,因此需要解決容器內文件持久化的問題。Docker提供了兩種方案來實現:
-
把宿主機文件系統裏的目錄映射到容器內的目錄, 如下圖所示
。如此一來,容器內在該目錄裏創建的所有文件,都存儲到宿主機的對應目錄中,在關閉容器後,宿主機的目錄依然存在,再次啓動容器時還能讀取到之前創建的文件,因此實現了容器的文件持久化。當然同時要明白,如果是對鏡像自帶文件進行了修改,由於鏡像是隻讀的,該修改操作無法在關閉容器時保存下來,除非在修改了文件後構建一個新的鏡像。
-
把多臺宿主機的磁盤目錄通過網絡聯合爲共享存儲,然後把共享存儲中的特定目錄映射給特定的容器, 如下圖所示
。這樣容器在重啓時,還是能讀取到關閉前創建的文件。生產環境中常用NFS作爲共享存儲方案。
9. Docker鏡像製作方法
鏡像製作方法有兩種:
-
通過正在運行的容器生成新鏡像
這種方式比較簡單,但無法直觀的設置環境變量、監聽端口等內容,適合在簡單使用的場景運用。
-
通過Dockerfile文件來生成新鏡像
FROM ubuntu/14.04 # 基礎鏡像
MAINTAINER guest # 製作者簽名
RUN apt-get install openssh-server -y # 安裝ssh服務
RUN mkdir /var/run/sshd # 創建目錄
RUN useradd -s /bin/bash -m -d /home/guest guest # 創建用戶
RUN echo ‘guest:123456’| chpasswd # 修改用戶密碼
ENV RUNNABLE_USER_DIR /home/guest # 設置環境變量
EXPOSE 22 # 容器內默認開啓的端口
CMD ["/usr/sbin/sshd -D"] # 啓動容器時自動啓動ssh服務
Docker引擎可以根據以上Dockerfile定義的步驟,構造出一個帶有ssh服務的Ubuntu鏡像。
10. Docker的使用場景
Docker作爲一種輕量級的虛擬化方案,應用場景十分豐富,下面收集了一些常見的場景:
-
作爲輕量級虛擬機使用 可以使用Ubuntu等系統鏡像創建容器,當作虛擬機來使用,相比於傳統虛擬機,啓動速度更快,資源佔用更少,單機可以啓動大量的操作系統容器,方便進行各種測試; -
作爲雲主機使用 結合Kubernetes這樣的容器管理系統,可以在大量服務器上動態分配和管理容器,在公司內部,甚至可以取代VMWare這樣的虛擬機管理平臺,使用Docker容器作爲雲主機使用; -
應用服務打包 在Web應用服務開發場景,可以把Java運行環境、Tomcat服務器打包爲一個基礎鏡像,在修改了代碼包後加入到基礎鏡像來構建一個新的鏡像,能很方便的升級服務和控制版本; -
容器雲平臺CaaS Docker的出現,使得很多雲平臺供應商開始提供容器雲的服務,簡稱容器即服務CaaS,以下對比一下IaaS、PaaS和SaaS: IaaS(基礎設施即服務):提供虛擬機或者其他基礎資源作爲服務提供給用戶。用戶可以從供應商那裏獲得虛擬機或者存儲等資源來裝載相關的應用,同時這些基礎設施的繁瑣的管理工作將由IaaS供應商來處理。其主要的用戶是企業的系統管理員和運維人員;
-
持續集成和持續部署 互聯網行業提倡敏捷開發,持續集成部署CI/CD便是最典型的開發模式。使用Docker容器雲平臺,就能實現從代碼編寫完成推送到Git/SVN後,自動觸發後端CaaS平臺將代碼下載、編譯並構建成測試Docker鏡像,再替換測試環境容器服務,自動在Jenkins或者Hudson中運行單元/集成測試,測試通過後,馬上就能自動將新版本鏡像更新到線上,完成服務升級。整個過程全自動化,一氣呵成,最大程度地簡化了運維,而且保證線上、線下環境完全一致,而且線上服務版本與Git/SVN發佈分支也實現統一。 -
解決微服務架構的實施難題 基於Spring Cloud這樣的微服務框架,能夠實現微服務的管理,但微服務本身還是需要運行在操作系統上。一個採用微服務架構開發的應用中,微服務的個數往往很多,這就導致了一臺服務器上往往需要啓動多個微服務來提高資源的利用率,而微服務本身可能就只能兼容部分操作系統,這就導致了就算有大量的服務器資源(操作系統可能不一樣),但由於微服務本身與操作系統可能相關,就不能做到讓微服務在任意服務器上運行,這就帶來了資源的浪費和運維的困難。利用Docker容器的環境隔離能力,讓微服務運行在容器內,就能夠解決以上所說的問題。 -
執行臨時任務 有時候用戶只是想執行一次性的任務,但如果用傳統虛擬機的方式就要搭建環境,執行完任務後還要釋放資源,比較麻煩。使用Docker容器就可以構建臨時的運行環境,執行完任務後關閉容器即可,方便快捷。 -
多租戶環境 利用Docker的環境隔離能力,可以爲不同的租戶提供獨佔的容器,實現簡單而且成本較低。
11. 總結
Docker的技術並不神祕,只是整合了前人積累的各種成果實現的應用級的容器化技術,它利用各種Linux發行版中使用了版本兼容的內核容器化技術,來實現鏡像一次構建到處運行的效果,並且利用了容器內的基礎操作系統鏡像層,屏蔽了實際運行環境的操作系統差異,使用戶在開發應用程序時,只需確保在選定的操作系統和內核版本上能正確運行即可,幾乎不需要關心實際的運行環境的系統差異,大大提高效率和兼容性。
但隨着容器運行得越來越多,容器管理將會稱爲另一個運維的難題,這時候就需要引入Kubernetes、Mesos或Swarm這些容器管理系統,後面有機會再介紹這些技術。
最後,關注公衆號Java技術棧,在後臺回覆:面試,可以獲取我整理的 Java 系列面試題和答案,非常齊全。
關注Java技術棧看更多幹貨
本文分享自微信公衆號 - Java技術棧(javastack)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。