# 前言
Docker是基礎設施的新成員。很少有新興技術能像它這樣,在DevOps和基礎設施領域中快速風靡起來。在不到兩年的時間內,Google、亞馬遜、微軟、IBM以及幾乎所有云供應商都宣布支持運行Docker容器。大量與Docker相關的創業公司在2014年和2015年年初都獲得了風險資本的投資。Docker開源技術背后的同名公司——Docker公司,在2015年第一季度的D輪融資中估值為10億美元左右。
大大小小的公司都在轉換其應用,使之運行于容器內,以此實現面向服務架構(SOA)和微服務。不論是參加從舊金山到柏林的任何DevOps聚會,還是閱讀最熱門的公司工程博客,都可以看出全世界的運維領導者們如今都在云上運行Docker。
毫無疑問,容器已經成為應用程序打包和基礎設施自動化的重要組成部分。但有一個棘手的問題,促使本書作者和同僚們創作了另一本Docker圖書。
具有中高級DevOps和運維背景的讀者將從本書獲益最多。因而,強烈建議讀者應具備在生產環境中運行服務器以及創建和管理容器這兩方面的基本經驗。
很多圖書和博客文章已經涵蓋了與Docker安裝及運行相關的話題,但能把在生產環境中運行Docker時產生的大量甚至是令人撓頭的關注點結合在一起的材料則少之又少。不用擔心,如果你很喜歡《盜夢空間》(Inception)這部電影,在云服務器的虛擬機中運行容器會讓你感覺很自然。
本書將帶讀者深入理解生產環境中架構的組成部分、關注點,以及如何運行基于Docker的基礎設施。
換個更深刻的說法,對于在真實生產環境中使用Docker遇到的問題,如何找到解決之道?本書綜合了訪談、真實公司端到端的生產環境實例,以及來自DevOps杰出專家的參考文獻,以此來解答這些問題。雖然本書包含了一些有用的示例,但它并不是一本復制粘貼的“教程式”參考書。相反,本書側重于生產環境中對前沿技術進行評估、風險抵御及運維所需的實踐理論和經驗。
作為作者,我們希望這本書所包含的內容能夠為那些正在評估如何及何時將Docker相關技術引入其DevOps棧的團隊提供一個可靠的決策指南,這遠比代碼片段要來得長久。
生產環境中運行的Docker為企業提供了多個新的運行和管理服務器端軟件的方式。很多現成的用例講解了如何使用Docker,但很少有公司公開分享過他們的全棧生產環境經驗。本書匯集了作者在生產環境中運行Docker的多個實例和一組選定的友好公司分享的使用經驗。
Docker所使用的底層容器技術已經存在了很多年,甚至早于[dotCloud](https://www.dotcloud.com/)這家平臺即服務(PaaS)創業公司,即后來我們所熟知的Docker。在dotCloud之前,許多知名的公司(如[Heroku](https://www.heroku.com/)和[Iron.io](http://www.iron.io/))已經在生產環境中運行大型容器集群,以獲取額外的超越虛擬機的性能優勢。與虛擬機相比,在容器中運行軟件賦予了這些公司秒級而非分鐘級的實例啟動與停止的能力,同時能使用更少的機器運行更多實例。
既然這項技術并不新鮮,為什么Docker能獲得如此巨大的成功呢?主要是因為它的易用性。Docker創造了一種統一的方式,通過簡便的命令行及HTTP API工具來打包、運行和維護容器。這種簡化降低了將應用程序及其運行時環境打包成一個自包含鏡像的入門門檻,使之變得可行且有趣,而不需要類似Chef、Puppet及Capistrano之類的配置管理和發布系統。
Docker提供了一種統一手段,將應用程序及其運行時環境打包到一個簡單的Dockerfile里,這從根本上改變了開發人員與DevOps團隊之間的交互界面。從而極大簡化了開發團隊與DevOps之間的溝通需求與責任邊界。
在Docker出現之前,各個公司的開發與運維團隊之間經常會爆發史詩般的戰爭。開發團隊想要快速前進,整合最新版的軟件及依賴,以及持續部署。運維團隊則以保證穩定為己任,他們負責把關可以運行于生產環境中的內容。如果運維團隊對新的依賴或需求感到不適,他們通常會站在保守的立場上,要求開發人員使用舊版軟件以確保糟糕的代碼不會搞垮整臺服務器。
Docker一下子改變了DevOps的決策思維,從“基本上說不”變成了“好的,只要運行在Docker中就可以”,因為糟糕的代碼只會讓容器崩潰,而不會影響到同一服務器上的其他服務。在這種泛型中,DevOps有效地負責為開發人員提供PaaS,而開發人員負責保證其代碼能正常運行。如今,很多團隊將開發人員加入到PagerDuty中,以監控他們在生產環境中的代碼,讓DevOps和運維人員專注于平臺的穩定運行及安全。
對大多數團隊而言,采用Docker是受開發人員更快的迭代和發布周期需求推動的。這對于開發環境是非常有益的,但對于生產環境,在單臺宿主機上運行多個Docker容器可能會導致安全漏洞,這一點我們將在第6章“安全”中講述。事實上,幾乎所有關于在生產環境中運行Docker的話題都是圍繞著將開發環境與生產環境區分開的兩個關注點進行的:一是編排,二是安全。
有些團隊試圖讓開發環境和生產環境盡可能保持一致。這種方法看起來很好,但是限于開發環境這樣做所需定制工具的數量又或者說模擬云服務(如AWS)的復雜度,這種方法并不實際。
為了簡化這本書的范疇,我們將介紹一些部署代碼的用例,但判定最佳開發環境設置的實踐機會將留給讀者。作為基本原則之一,盡量保持生產環境和開發環境的相似性,并使用一個持續集成/持續交付(CI/CD)系統以獲取最佳結果。
對于不同的團隊,生產環境意味著不同的東西。在本書中,我們所說的生產環境是指真實客戶用于運行代碼的環境。這是相對于開發環境、預演環境及測試環境而言的,后者的停機時間不會被客戶感知到。
在生產環境中,Docker有時是用于接收公共網絡流量的容器,有時則是用于處理來自隊列負荷的異步的后臺作業。不管哪種用途,在生產環境中運行Docker與在其他環境中運行相比,最主要的差異都是需要在其安全性與穩定性上投入較多的注意力。
編寫本書的動力之一是,與Docker相關的文檔和博客文章中缺乏對實際生產環境與其他環境的明確區分。我們認為,80%的Docker博客文章中的建議在嘗試在生產環境中運行6個月之后會被放棄(或至少修改)。為什么?因為大多數博客文章中舉的都是理想化的例子,使用了最新、最好用的工具,一旦某個極端的情況變成了致命缺陷,這些工具將被遺棄(或延期),被更簡單的方法所取代。這是Docker技術生態系統現狀的一個反映,而非技術博客的缺陷。
總的來說,生產環境很難管理。Docker簡化了從開發到生產的工作流程,但同時增加了安全和編排的復雜度(更多關于編排的內容參見第4章)。
為了節省時間,下面給出本書的重點綜述。
所有在生產環境中運行Docker的團隊,都會在傳統的安全最佳實踐上做出一項或多項妥協。如果無法完全信任容器內運行的代碼,那么就只得選用容器與虛擬機一對一的拓撲方式。對于很多團隊而言,在生產環境中運行Docker的優勢遠遠大于其帶來的安全與編排問題。如果遇到工具方面的問題,請等待一到兩個月,以便Docker社區對其進行修復,不要浪費時間去修補其他人的工具。保持Docker設置最小化。讓一切自動化。最后,對成熟的編排工具(如Mesos、Kubernates等)的需求遠比想象的要少得多。
Docker社區一個常見的口頭禪是“電池內置但可移除”,指的是將很多功能捆綁在一起的單體二進制文件,這有別于傳統Unix哲學下相對較小、功能單一、管道化的二進制文件。
這種單體式的做法是由兩個主要因素決定的:(1)使Docker易于開箱即用;(2)Golang缺少動態鏈接。Docker及多數相關工具都是用Google的[Go編程語言](https://golang.org/)編寫的,該語言可以簡化高并發代碼的編寫與部署。雖然Go是一門出色的編程語言,但用它來構建的Docker生態系統中也因此遲遲無法實現一個可插拔的架構,在這種架構中可以很容易用替代品對工具進行更換。
如果讀者有Unix系統背景,最好是編譯自己的精簡版Docker守護進程,以符合生產環境的需求。如果讀者有開發背景,預計到2015年下半年,Docker插件將成為現實[\[1\]](part0006.xhtml#anchor001)。在此期間,估計Docker生態系統中的工具將會出現明顯的重疊現象,某些情況下甚至是相互排斥的。
換句話說,要讓Docker運行于生產環境中,用戶的一半工作將是決定哪些工具對自己的技術棧最有意義。與DevOps所有事情一樣,先從最簡單的解決方案入手,然后在必要時增加其復雜性。
2015年5月,Docker公司發布了[Compose](https://docs.docker.com/compose/)、[Machine](https://docs.docker.com/machine/)及[Swarm](https://docs.docker.com/swarm/),與Docker生態系統內的同類工具進行競爭。所有這些工具都是可選的,請根據實際情況對其進行評估,而不要認為Docker公司提供的工具就一定是最佳解決方案。
探索Docker生態系統時的另一項關鍵建議是:評估每個開源工具的資金來源及其商業目標。目前,Docker公司和CoreOS經常發布工具,以爭奪關注度和市場份額。一個新工具發布后,最好等上幾個月,看看社區的反應,不要因為它看起來很酷就切換到最新、最好用的工具上。
最后一個關鍵點是,不要期望能在Docker容器中運行所有東西。Heroku風格的“十二要素”([12 factor](http://12factor.net/))應用是最容易Docker化的,因為它們不維護狀態。在理想的微服務環境中,容器能在幾毫秒內啟動、停止而不影響集群的健康或應用程序的狀態。
類似[ClusterHQ](https://clusterhq.com/)這樣的創業公司正著手實現Docker化數據庫和有狀態的應用程序,但眼下,由于編排和性能方面的原因,可能需要繼續直接在虛擬機或裸機上運行數據庫。
Docker還不適用于任何需要動態調整CPU和內存要求的應用[\[2\]](part0006.xhtml#anchor002)。允許動態調整的代碼已經完成,但尚不清楚何時才能在一般的生產環境中投入使用。目前,若對容器的CPU和內存的限制進行調整,需要停止并重新啟動容器。
另外,對網絡吞吐量有高要求的應用進行最佳優化時不要使用Docker,因為Docker使用iptables來完成宿主機IP到容器IP的NAT轉換。通過禁用Docker的NAT來提升網絡性能是可行的,但這是一個高級的使用場景,很少有團隊會在生產環境中這么做。
衷心感謝以下技術審稿人提供的早期反饋及細致的評論:Mika Turunen、Xavier Bruhiere和Felix Rabe。
- - - - - -
[\[1\]](part0006.xhtml#ac001) Docker 1.7版中正式引入了插件系統。——譯者注
[\[2\]](part0006.xhtml#ac002) Docker 1.10版中新增的docker update命令可實現CPU和內存的動態調整。——譯者注
- 版權信息
- 版權聲明
- 內容提要
- 對本書的贊譽
- 譯者介紹
- 前言
- 本書面向的讀者
- 誰真的在生產環境中使用Docker
- 為什么使用Docker
- 開發環境與生產環境
- 我們所說的“生產環境”
- 功能內置與組合工具
- 哪些東西不要Docker化
- 技術審稿人
- 第1章 入門
- 1.1 術語
- 1.1.1 鏡像與容器
- 1.1.2 容器與虛擬機
- 1.1.3 持續集成/持續交付
- 1.1.4 宿主機管理
- 1.1.5 編排
- 1.1.6 調度
- 1.1.7 發現
- 1.1.8 配置管理
- 1.2 從開發環境到生產環境
- 1.3 使用Docker的多種方式
- 1.4 可預期的情況
- 為什么Docker在生產環境如此困難
- 第2章 技術棧
- 2.1 構建系統
- 2.2 鏡像倉庫
- 2.3 宿主機管理
- 2.4 配置管理
- 2.5 部署
- 2.6 編排
- 第3章 示例:極簡環境
- 3.1 保持各部分的簡單
- 3.2 保持流程的簡單
- 3.3 系統細節
- 利用systemd
- 3.4 集群范圍的配置、通用配置及本地配置
- 3.5 部署服務
- 3.6 支撐服務
- 3.7 討論
- 3.8 未來
- 3.9 小結
- 第4章 示例:Web環境
- 4.1 編排
- 4.1.1 讓服務器上的Docker進入準備運行容器的狀態
- 4.1.2 讓容器運行
- 4.2 連網
- 4.3 數據存儲
- 4.4 日志
- 4.5 監控
- 4.6 無須擔心新依賴
- 4.7 零停機時間
- 4.8 服務回滾
- 4.9 小結
- 第5章 示例:Beanstalk環境
- 5.1 構建容器的過程
- 部署/更新容器的過程
- 5.2 日志
- 5.3 監控
- 5.4 安全
- 5.5 小結
- 第6章 安全
- 6.1 威脅模型
- 6.2 容器與安全性
- 6.3 內核更新
- 6.4 容器更新
- 6.5 suid及guid二進制文件
- 6.6 容器內的root
- 6.7 權能
- 6.8 seccomp
- 6.9 內核安全框架
- 6.10 資源限制及cgroup
- 6.11 ulimit
- 6.12 用戶命名空間
- 6.13 鏡像驗證
- 6.14 安全地運行Docker守護進程
- 6.15 監控
- 6.16 設備
- 6.17 掛載點
- 6.18 ssh
- 6.19 私鑰分發
- 6.20 位置
- 第7章 構建鏡像
- 7.1 此鏡像非彼鏡像
- 7.1.1 寫時復制與高效的鏡像存儲與分發
- 7.1.2 Docker對寫時復制的使用
- 7.2 鏡像構建基本原理
- 7.2.1 分層的文件系統和空間控管
- 7.2.2 保持鏡像小巧
- 7.2.3 讓鏡像可重用
- 7.2.4 在進程無法被配置時,通過環境變量讓鏡像可配置
- 7.2.5 讓鏡像在Docker變化時對自身進行重新配置
- 7.2.6 信任與鏡像
- 7.2.7 讓鏡像不可變
- 7.3 小結
- 第8章 存儲Docker鏡像
- 8.1 啟動并運行存儲的Docker鏡像
- 8.2 自動化構建
- 8.3 私有倉庫
- 8.4 私有registry的擴展
- 8.4.1 S3
- 8.4.2 本地存儲
- 8.4.3 對registry進行負載均衡
- 8.5 維護
- 8.6 對私有倉庫進行加固
- 8.6.1 SSL
- 8.6.2 認證
- 8.7 保存/載入
- 8.8 最大限度地減小鏡像體積
- 8.9 其他鏡像倉庫方案
- 第9章 CI/CD
- 9.1 讓所有人都進行鏡像構建與推送
- 9.2 在一個構建系統中構建所有鏡像
- 9.3 不要使用或禁止使用非標準做法
- 9.4 使用標準基礎鏡像
- 9.5 使用Docker進行集成測試
- 9.6 小結
- 第10章 配置管理
- 10.1 配置管理與容器
- 10.2 面向容器的配置管理
- 10.2.1 Chef
- 10.2.2 Ansible
- 10.2.3 Salt Stack
- 10.2.4 Puppet
- 10.3 小結
- 第11章 Docker存儲引擎
- 11.1 AUFS
- 11.2 DeviceMapper
- 11.3 BTRFS
- 11.4 OverlayFS
- 11.5 VFS
- 11.6 小結
- 第12章 Docker 網絡實現
- 12.1 網絡基礎知識
- 12.2 IP地址的分配
- 端口的分配
- 12.3 域名解析
- 12.4 服務發現
- 12.5 Docker高級網絡
- 12.6 IPv6
- 12.7 小結
- 第13章 調度
- 13.1 什么是調度
- 13.2 調度策略
- 13.3 Mesos
- 13.4 Kubernetes
- 13.5 OpenShift
- Red Hat公司首席工程師Clayton Coleman的想法
- 第14章 服務發現
- 14.1 DNS服務發現
- DNS服務器的重新發明
- 14.2 Zookeeper
- 14.3 基于Zookeeper的服務發現
- 14.4 etcd
- 基于etcd的服務發現
- 14.5 consul
- 14.5.1 基于consul的服務發現
- 14.5.2 registrator
- 14.6 Eureka
- 基于Eureka的服務發現
- 14.7 Smartstack
- 14.7.1 基于Smartstack的服務發現
- 14.7.2 Nerve
- 14.7.3 Synapse
- 14.8 nsqlookupd
- 14.9 小結
- 第15章 日志和監控
- 15.1 日志
- 15.1.1 Docker原生的日志支持
- 15.1.2 連接到Docker容器
- 15.1.3 將日志導出到宿主機
- 15.1.4 發送日志到集中式的日志平臺
- 15.1.5 在其他容器一側收集日志
- 15.2 監控
- 15.2.1 基于宿主機的監控
- 15.2.2 基于Docker守護進程的監控
- 15.2.3 基于容器的監控
- 15.3 小結
- DockOne社區簡介
- 看完了