1. 深挖Kubernetes存儲為何如此難及其解決方案
以Kubernetes為代表的容器編排工具在應用開發部署領域起正發揮著顛覆性的變革作用。隨著微服務架構的發展,從開發人員的角度來看,應用邏輯架構與基礎設施架構之間開始解耦,這意味著開發者能夠將精力更多集中在軟體構建以及價值交付身上。
當管理Docker鏡像的時候,Kubernetes也讓實際應用變的十分便捷靈活。在利用Kubernetes進行容器架構的應用部署時,管理員們將在無需修改底層代碼的前提下將其部署在任何位置——包括公有雲、混合雲乃至私有雲。
雖然Kubernetes在擴展性、便攜性與管理性等方面的表現都相當給力,但截至目前,它仍然不支持存儲狀態。與之對應的是,如今的大多數應用都是有狀態的——換言之,要求在一定程度上配合外部存儲資源。
Kubernetes架構本身非常靈活的,能夠根據開發者的需求、規范以及實際負載情況,對容器進行隨意創建與撤銷。此外,Pod和容器還具有自我修復與復制能力。因此從本質上講,它們的生命周期普遍非常短暫。
但是,現有持久存儲解決方法無法支持動態的應用場景,而持久化存儲也無法滿足動態創建與撤銷的需求。
當我們需要將有狀態應用部署到其它基礎架構平台,或者另一家內部或混合雲供應商的環境中時,可移植性低下無疑將成為我們面臨的巨大挑戰。更具體地講,持久化存儲解決方案往往會鎖定於特定雲服務供應商,而無法靈活完成轉移。
另外,雲原生應用中的存儲機制也相當復雜、難於理解。Kubernetes中的不少存儲術語極易混淆,其中包含著復雜的含義與微妙的變化。再有,在原生Kubernetes、開源框架以及託管與付費服務之間還存在著諸多選項,這極大增加了開發人員在做出決定之前的考量與試驗成本。
以下是CNCF列出的雲原生存儲可選方案:
我們首先從最簡單的場景出發,即在Kubernetes當中部署一套資料庫。具體流程包括:選擇一套符合需求的資料庫,讓它在本地磁碟上運行,然後將其作為新的工作負載部署到集群當中。但是,由於資料庫中存在的一些固有屬性,這種方式往往無法帶來符合預期的效果。
容器本身是基於無狀態原則進行構建的,憑借這一天然屬性,我們才能如此輕松地啟動或撤銷容器環境。由於不存在需要保存及遷移的數據,集群也就不需要同磁碟讀寫這類密集型操作綁定在一起了。
但對於資料庫,其狀態必須隨時保存。如果以容器方式部署在集群當中的資料庫不需要進行遷移,或者不需要頻繁開關,那麼其基本屬性就相當於一種物理存儲設備。在理想情況下,使用數據的容器應該與該資料庫處於同一Pod當中。
當然,這並不是說將資料庫部署在容器中的作法不可取。在某些應用場景下,這樣的設計完全能夠滿足需求。舉例來說,在測試環境或者處理非生產級數據時,由於總體數據量很小,將資料庫納入集群完全沒有問題。但在實際生產中,開發人員往往需要仰仗於外部存儲機制。
Kubernetes到底是如何與存儲資源彼此通信的?其利用的是控制層介面。這些介面負責將Kubernetes與外部存儲相對接。接入Kubernetes的外部存儲解決方案被稱為「卷插件(Volume Plugins)」。正是有了卷插件的存在,存儲資源才得以抽象化並實現可移植性。
以前,卷插件一般由核心Kubernetes代碼庫進行構建、鏈接、編譯以及裝載。這樣就極大的限制了開發人員的發揮空間,同時也帶來了額外的維護開銷。因此,項目維護人員們決定在Kubernete的代碼庫上增加一些新的存儲功能。
隨著CSI以及Flexvolume的引入,卷插件如今可以在集群中直接部署,而完全無需更改代碼庫。
原生Kubernetes與存儲
持久卷是由管理員負責配置的存儲單元,它們獨立於任何單一Pod之外,因此不受Pod生命周期的影響。
存儲資源有兩種使用方式:靜態存儲與動態存儲。
實際上,靜態定義的持久卷並不能適應Kubernetes的可移植特性,因為存儲資源具有對環境的依賴性——例如AWS EBS或者GCE Persistent Disk。另外,手動綁定還需要根據不同供應商的存儲方案修改YAML文件。
在資源分配方面,靜態配置實際上也違背了Kubernetes的設計原則。後者的CPU與內存並非事先被分配好綁定在Pod或者容器上,而是以被動態形式進行分配。
通過簡單的說明,相信大家已經了解了原生Kubernetes對外部存儲資源的使用方式。當然,這里僅僅做出概括,實際使用場景中還有更多其它因素需要考量。
CSI——容器存儲介面
下面來看容器存儲介面(簡稱CSI)。CSI是由CNCF存儲工作組創建的統一標准,旨在定義一個標準的容器存儲介面,從而使存儲驅動程序能夠在任意容器架構下正常起效。
CSI規范目前已經在Kubernetes中得到普及,大量驅動插件被預先部署在Kubernetes集群內供開發人員使用。如此一來,我們就可以利用Kubernetes上的CSI卷來訪問與CSI兼容的開放存儲卷。
CSI的引入,意味著存儲資源能夠作為Kubernetes集群上的另一種工作負載實現容器化以及部署。
相關開源項目
目前,圍繞雲原生技術涌現出大量工具與項目。但作為生產場景中的一大突出問題,我們往往很難在雲原生架構中選擇最合適的開源項目。換言之,解決方案選項太多,反而令存儲需求變得更難解決。
我們再看一次CNCF列出的雲原生存儲的可選方案:
下面我會分享一下當下流行的存儲方案Ceph與Rook,還有Rancher開源的容器化分布式存儲Longhorn。
Ceph
Ceph是一種動態託管、橫向擴展的分布式存儲集群。Ceph面向存儲資源提供一種邏輯抽象機制,其設計理念包括無單點故障、自管理以及軟體定義等特性。Ceph可以面向同一套存儲集群分別提供塊存儲、對象存儲以及文件存儲的對應介面。
Ceph架構相當復雜的,其中使用到大量的底層技術,例如RADOS、librados、RADOSGW、RDB、CRUSH演算法,外加monitor、OSD以及MDS等功能性組件。這里我們先不談它的底層架構,關鍵在於Ceph屬於一種分布式存儲集群,這使得擴展更便利、能夠在不犧牲性能的前提下消除單點故障,且提供涵蓋對象存儲、塊存儲以及文件存儲的統一存儲體系。
Ceph架構圖
Rook
另一個有趣且頗具人氣的項目是Rook,這是一項旨在將Kubernetes與Ceph融合起來的技術方案。從本質上講,它將計算節點和存儲節點放進了同一個集群當中。
Rook是一種雲原生編排器,並對Kubernetes做出擴展。Rook允許用戶將Ceph放置在容器內,同時提供卷管理邏輯以立足Kubernetes之上實現Ceph的可靠運行。Rook還使本應由集群管理員操作的多種任務完成了自動化實現,其中包括部署、引導、配置、擴展以及負載均衡等等。
Rook自身不具備持久狀態,也不需要單獨管理。這,才是真正與Kubernetes設計原則相符的存儲資源管理方案。
Rook憑借著將Ceph與Kubernetes協同起來的強大能力而頗受歡迎,在GitHub上獲得近4000顆星,1600多萬次的下載,並吸引到100多名貢獻者,現已進入CNCF孵化階段。
Longhorn
Longhorn項目是Rancher Labs推出的開源的基於雲和容器部署的分布式塊存儲新方式。Longhorn遵循微服務的原則,利用容器將小型獨立組件構建為分布式塊存儲,並使用容器編排來協調這些組件,形成彈性分布式系統。
如今,基於雲和容器的部署規模日益擴大,分布式塊存儲系統也正變得越來越復雜,單個存儲控制器上的volume數量在不斷增加。2000年代初,存儲控制器上的volume數量只有幾十個,但現代雲環境卻需要數萬到數百萬的分布式塊存儲卷。存儲控制器變成了高度復雜的分布式系統。
Longhorn充分利用了近年來關於 如何編排大量的容器和虛擬機的核心技術 。例如,Longhorn並沒有構建一個可以擴展到100,000個volume的高度復雜的控制器,而是出於讓存儲控制器簡單輕便的考慮,創建了100,000個單獨的控制器。然後,我們可以利用像Kubernetes這樣的最先進的編排系統來調度這些獨立的控制器,共享一組磁碟中的資源,協同工作,形成一個彈性的分布式塊存儲系統。
Longhorn基於微服務的設計還有很多其他優勢。因為每個volume都有自己的控制器,在升級每個volume的控制器和replica容器時,是不會導致IO操作明顯的中斷的。Longhorn可以創建一個長期運行的工作來編排所有live volume的升級,同時確保不會中斷系統正在進行的操作。為確保升級不會導致意外的問題,Longhorn可以選擇升級一小部分volume,並在升級過程中出現問題時回滾到舊版本。這些做法在現代微服務應用中已得到廣泛應用,但在存儲系統中並不常見。希望Longhorn可以 助力於微服務在存儲領域的更多應用。
結 語
對於實際應用層面出現的任何問題,最重要的自然是判斷需求、設計系統或者選擇適當的工具。同樣的道理也適用於雲原生環境。雖然具體問題非常復雜,但也必然會出現大量工具方案嘗試解決。隨著雲原生世界的持續發展,我們可以肯定,新的解決方案將不斷涌現。未來,一切都會更加美好!
2. 持久化存儲之 PV、PVC、StorageClass
容器化一個應用比較麻煩的地方,就是對於有狀態的服務的管理,最常見的狀態就是 存儲狀態 。
創建的PVC只有和對應的PV綁定才可以使用
綁定條件:
成功綁定之後,Pod 就是聲明PVC綁定的持久化存儲了,使用方法如下:
Pod 只需要在 volumes 欄位里聲明要使用的 PVC 的name,等Pod創建後,Kubelet會將 PVC 綁定的 PV, 例如上面的 NFS 類型的 volume 掛載到容器內目錄。
PVC 和 PV 的設計,其實跟「面向對象」的思想完全一致
當每次創建 PVC 聲明使用存儲時,都需要去手動的創建 PV,來滿足 PVC 的使用。
可以用一種機制來根據用戶聲明的存儲使用量(PVC)來動態的創建對應的持久化存儲卷(PV)。k8s 用 StorageClass 來實現動態創建 持久化存儲。
存儲控制器 Volume Controller,是用來專門處理持久化存儲的控制器,其一個子控制循環 PersistentVolumeController 負責實現 PV 和 PVC 的綁定。
PersistentVolumeController 會 watch kube-apiserver 的 PVC 對象。如果發現有 PVC對象創建,則會查看所有可用的 PV, 如果有則綁定,若沒有,則會使用 StorageClass 的配置和 PVC 的描述創建 PV 進行綁定。
所謂將一個 PV 與 PVC 進行「綁定」,其實就是將這個PV對象的名字,填在了 PVC 對象的 spec.volumeName 欄位上
3. Docker+ Kubernetes已成為雲計算的主流(二十六)
最近正在抽時間編寫k8s的相關教程,很是費時,等相關內容初步完成後,再和大家分享。對於k8s,還是上雲更為簡單、穩定並且節省成本,因此我們需要對主流雲服務的容器服務進行了解,以便更好地應用於生產。
主流雲服務容器服務介紹
Docker+ Kubernetes已成為雲計算的主流
亞馬遜AWS
Amazon Web Services (AWS) 是亞馬遜公司旗下雲計算服務平台,為全世界范圍內的客戶提供雲解決方案。AWS面向用戶提供包括彈性計算、存儲、資料庫、應用程序在內的一整套雲計算服務,幫助企業降低IT投入成本和維護成本。
那麼如何在AWS上運行Docker呢?AWS 同時為 Docker 開源解決方案和商業解決方案提供支持,並且可通過多種方式在 AWS 上運行容器:
微軟Azure
Microsoft Azure 是一個開放而靈活的企業級雲計算平台。通過 IaaS + PaaS 幫助用戶加快發展步伐,提高工作效率並節省運營成本。
Azure是一種靈活和支持互操作的平台,它可以被用來創建雲中運行的應用或者通過基於雲的特性來加強現有應用。它開放式的架構給開發者提供了Web應用、互聯設備的應用、個人電腦、伺服器、或者提供最優在線復雜解決方案的選擇。
在容器這塊,Azure同樣的提供了眾多解決方案:
下面我們側重介紹下以下服務:
阿里雲
阿里雲(www.aliyun.com)創立於2009年,是全球領先的雲計算及人工智慧 科技 公司,為200多個國家和地區的企業、開發者和政府機構提供服務。2017年1月阿里雲成為奧運會全球指定雲服務商。2017年8月阿里巴巴財報數據顯示,阿里雲付費雲計算用戶超過100萬。阿里雲致力於以在線公共服務的方式,提供安全、可靠的計算和數據處理能力,讓計算和人工智慧成為普惠 科技 。阿里雲在全球18個地域開放了49個可用區,為全球數十億用戶提供可靠的計算支持。此外,阿里雲為全球客戶部署200多個飛天數據中心,通過底層統一的飛天操作系統,為客戶提供全球獨有的混合雲體驗。
飛天(Apsara)是由阿里雲自主研發、服務全球的超大規模通用計算操作系統。 它可以將遍布全球的百萬級伺服器連成一台超級計算機,以在線公共服務的方式為 社會 提供計算能力。 從PC互聯網到移動互聯網到萬物互聯網,互聯網成為世界新的基礎設施。飛天希望解決人類計算的規模、效率和安全問題。飛天的革命性在於將雲計算的三個方向整合起來:提供足夠強大的計算能力,提供通用的計算能力,提供普惠的計算能力。飛天誕生於2009年2月,目前為全球200多個國家和地區的創新創業企業、政府、機構等提供服務。
同樣,阿里雲對容器也提供了友好的支持:
容器服務提供高性能可伸縮的容器應用管理服務,支持用Docker和Kubernetes進行容器化應用的生命周期管理,提供多種應用發布方式和持續交付能力並支持微服務架構。容器服務簡化了容器管理集群的搭建工作,整合了阿里雲虛擬化、存儲、網路和安全能力,打造雲端最佳容器運行環境。
容器服務 Kubernetes 版(簡稱 ACK)提供高性能可伸縮的容器應用管理能力,支持企業級 Kubernetes 容器化應用的全生命周期管理。容器服務 Kubernetes 版簡化集群的搭建和擴容等工作,整合阿里雲虛擬化、存儲、網路和安全能力,打造雲端最佳的 Kubernetes 容器化應用運行環境。
阿里雲彈性容器實例(Elastic Container Instance)是 Serverless 和容器化的彈性計算服務。用戶無需管理底層 ECS 伺服器,只需要提供打包好的鏡像,即可運行容器,並僅為容器實際運行消耗的資源付費。
容器鏡像服務(Container Registry)提供安全的鏡像託管能力,穩定的國內外鏡像構建服務,便捷的鏡像授權功能,方便用戶進行鏡像全生命周期管理。容器鏡像服務簡化了Registry的搭建運維工作,支持多地域的鏡像託管,並聯合容器服務等雲產品,為用戶打造雲上使用Docker的一體化體驗。
騰訊雲
騰訊雲為騰訊傾力打造的雲計算品牌,以卓越 科技 能力助力各行各業數字化轉型,為全球客戶提供領先的雲計算、大數據、人工智慧服務,以及定製化行業解決方案。其基於QQ、微信、騰訊 游戲 等海量業務的技術錘煉,從基礎架構到精細化運營,從平台實力到生態能力建設,騰訊雲將之整合並面向市場,使之能夠為企業和創業者提供集雲計算、雲數據、雲運營於一體的雲端服務體驗。
在容器這塊,騰訊雲提供了如下解決方案:
騰訊雲容器服務(Tencent Kubernetes Engine ,TKE)基於原生 kubernetes 提供以容器為核心的、高度可擴展的高性能容器管理服務。騰訊雲容器服務完全兼容原生 kubernetes API ,擴展了騰訊雲的 CBS、CLB 等 kubernetes 插件,為容器化的應用提供高效部署、資源調度、服務發現和動態伸縮等一系列完整功能,解決用戶開發、測試及運維過程的環境一致性問題,提高了大規模容器集群管理的便捷性,幫助用戶降低成本,提高效率。容器服務提供免費使用,涉及的其他雲產品另外單獨計費。
容器實例服務(Container Instance Service , CIS)可以幫用戶在雲上快捷、靈活的部署容器,讓用戶專注於構建程序和使用容器而非管理設備上。無需預購 CVM(雲伺服器),就可以在幾秒內啟動一批容器來執行任務。同時,開發者也可以通過 kubernetes API 把已有kubernetes 集群的 pod 調度到 CIS 上以處理突增業務。CIS 根據實際使用的資源計費,可以幫用戶節約計算成本。使用 CIS 可以極大降低用戶部署容器的門檻,降低用戶執行 batch 型任務或處理業務突增的成本。
從上面主流的雲服務中我們可以看到,沒有哪家雲廠商不支持Docker,同樣的,也沒有哪家雲廠商不支持Kubernetes!也就是說,Docker+ Kubernetes已經成為雲計算的主流!
什麼是Kubernetes(k8s)
Kubernetes(簡稱k8s)誕生於谷歌,是一個開源的,用於管理雲平台中多個主機上的容器化的應用,k8s的目標是讓部署容器化的應用簡單並且高效,其提供了應用部署、規劃、更新、維護的機制。
k8s主要有以下特點:
支持公有雲,私有雲,混合雲,多重雲(multi-cloud) 。可以將容器化的工作負載從本地開發計算機無縫移動到生產環境。在本地基礎結構以及公共雲和混合雲中,在不同環境中協調容器,保持一致性。
支持模塊化,插件化,可掛載,可組合。並且k8s的擴展和插件在社區開發者和各大公司的支持下高速增長,用戶可以充分利用這些社區產品/服務以添加各種功能。
支持自動部署,自動重啟,自動復制,自動伸縮/擴展,並且可以定義復雜的容器化應用程序並將其部署在伺服器群集甚至多個群集上——因為k8s會根據所需狀態優化資源。通過內置的自動縮放器,k8s可輕松地水平縮放應用程序,同時自動監視和維護容器的正常運行。
Kubernetes正在塑造應用程序開發和管理的未來
k8s構建於 Google 數十年經驗,一大半來源於 Google 生產環境規模的經驗。結合了社區最佳的想法和實踐,而且還在不斷地高速迭代和更新之中。
她銜著金鑰匙出生,一誕生就廣受歡迎,更是在2017,其打敗了所有的競爭對手,贏得了雲計算的戰爭——主流的雲廠商基本上都紛紛放棄了自己造「輪子」的舉動,終止了各自的容器編排工具,加盟了k8s陣營,其中包括Red Hat、微軟、IBM、阿里、騰訊、華為和甲骨文等。
k8s像風暴一樣席捲了應用開發領域,並且已成為雲原生應用程序(架構、組件、部署和管理方式)的事實標准,大量的開發者和企業正在使用k8s創建由微服務和無伺服器功能組成的現代架構。
Docker+ Kubernetes已成為雲計算的主流
容器是現代軟體交付的未來,而Kubernetes是編排容器的最佳方案(事實上的標准)。
Docker 和Kubernetes相輔相成,聯手打下了雲計算的「萬里江山」。Docker 為打包和分發容器化應用程序提供了一個開放的標准,而 Kubernetes 則協調和管理通過 Docker 創建的分布式容器化應用程序。換句話說,Kubernetes 提供了部署和運行通過Docker生成的應用程序所需的基礎結構。
在主流的雲服務,基於Docker+k8s的新型PaaS平台具有敏捷部署、彈性伸縮、靈活調度、故障自動恢復等優勢,充分滿足業務擴展中的資源支持,因此在短短兩年之內,便從Docker Swarm、Cloud Foundry Diego、Kontena、Apache Mesos、Amazon ECS…等大量對手中脫穎而出,拿下了皇冠。
k8s和Docker的勝利意味著這是有史以來第一次,無論使用哪一種雲平台,研發人員都可以擁有完全相同的計算環境。
4. Kubernetes存儲
在 Docker的設計 實現中, 容器中的數據是臨時性的,當容器銷毀或重新啟動時存儲在容器內部的數據將會全部丟失 ,但實際上很多容器化應用是需要持久化保存的數據,這就需要使用Docker數據卷掛載宿主機上的文件或者目錄到容器中以保證數據的持久化存儲。在 Kubernetes中Pod重建如同Docker銷毀一樣,數據就會丟失 ,Kubernetes也通過掛載數據卷方式為Pod數據提供持久化能力,這些數據卷以Pod為最小單位進行存儲,通過共享存儲或分布式存儲在各個Pod之間實現共享。
Kubernetes是由Master節點及Node節點組成的,在Master節點中通過etcd存儲了Kubernetes集群的節點信息、Pod信息、容器信息、配置信息。Node節點主要對外提供容器服務,著重描述Node節點與存儲相關的內容。
Kubernetes以Pod為單位對外提供容器服務,真正的服務是通過Service進行訪問的。 Kubernetes中的服務按類型分成三種:無狀態服務(stateless)、普通有狀態服務、有狀態集群服務 。
無狀態服務:不需要持久化存儲的,即使Pod重建也不會受影響,只要確保服務的可靠性便可,Kubernetes通過ReplicationSet來保證某個服務的實例數量。
普通有狀態服務:這類服務需要保留服務的狀態,通常通過Kubernetes提供的Volume及Persistent Volume、Persistent Volume Claim來保存狀態。
有狀態的集群服務:這類服務除了保存服務狀態的同時還需要提供集群管理的功能,集群管理過程中也涉及臨時數據的保存、集群內數據共享等。
Kubernetes中涉及存儲的主要使用場景 :
1) 容器集群相關配置信息及運行時信息保存,這類信息存儲在etcd中。
2) 服務的基本配置文件及證書文件。
3) 服務的狀態存儲、數據存儲信息。
4) 集群內不同服務交換共享的數據信息。
1) 臨時文件形式 :同一個Pod內不同容器間通過共享內存方式訪問,會創建一個空目錄,交換完信息後會刪除這個空目錄。
2) HostPath方式 :同一個Node內不同的Pod間進行信息共享使用HostPath方式(比如時區timezone)。如果Pod配置了EmptyDir數據卷,則它在Pod的生命周期內都會存在。當Pod被分配到Node上的時候,會在Node上創建EmptyDir數據卷,並掛載到Pod的容器中。
3) PV及PVC: Kubernetes的持久化存儲機制的核心是PV(Persistent Volume)、PVC (Persistent Volume Claim)。PV是Volume插件,關聯到真正的後端存儲系統,PVC是從PV中申請資源,而不需要關心存儲的提供方。PVC和PV的關系就如同Pod和Node一樣,Pod是消費Node提供的資源,PVC是消費PV提供的存儲資源。PVC和PV通過匹配完成綁定關系,PVC可以被Pod里的容器掛載。
4) 網路方式 :不同Node節點之間的數據共享通過網路方式使用,通常採用分布式存儲方式。開源的分布式文件系統比較流行的選擇有GlusterFS和Ceph,還有一些其他的分布式文件系統(NFS)選擇。
5) 自定義插件方式 :Kubernetes提供了豐富的Volume的插件來進行存儲管理。如果存儲管理介面不夠用,用戶可以通過CSI或Flex Volume進行擴展。
存儲管理組件(存儲組件)主要是接收北向API收到的Rest請求,維護持久卷的生命周期管理。如創建、刪除卷, 存儲組件負責與後端存儲軟體交互完成實際的創建、刪除卷等操作;並負責調用Kubernetes原生介面創建對應的PVC和PV 。
存儲後端系統提供數據文件的實際持久化能力,不僅需要實現數據文件的讀寫、副本復制等存儲操作,通常還需具備多節點高可用的能力。
當需要為自己的微服務應用掛載持久卷時,只需要通過存儲組件創建持久卷,存儲組件會在Kubernetes業務集群創建PVC/PV,並到後端存儲系統(如GlusterFS)上創建真正的物理Volume,同時維護好PVC/PV/Volume之間的一一映射對應關系。這樣,用戶部署容器時就可以選擇掛載相應的持久卷,部署後相應的卷就可以掛載到對應的容器。應用掛載持久卷後,進行讀寫時就類似於本地目錄的讀寫操作。
在Pod進行重建或者遷移到其他節點時,Pod可以自動掛回原來對應的持久卷,繼續使用原先的數據。多個Pod可以共享一個持久卷,從而達到容器間文件共享的目的。
摘抄自陸平的《基於Kubernetes的容器雲平台實戰》一書的第11章Kubernetes存儲
5. 超值一篇分享,Docker:從入門到實戰過程全記錄
作者 | 天元浪子
來源 | CSDN博客
想要真正理解Docker,就不得不從虛擬化技術的發展歷程說起。普遍認為虛擬化技術經歷了物理機時代、虛擬機時代,目前已經進入到了容器化時代。可以說,Docker是虛擬化技術不斷發展的必然結果。
那麼,什麼是容器呢?容器和虛擬機有什麼不同?Docker和容器又是什麼關系呢?搞明白這幾個問題,Docker的概念就清晰了。
1.1 虛擬機和容器
藉助於VMWare等軟體,可以在一台計算機上創建多個虛擬機,每個虛擬機都擁有獨立的操作系統,可以各自獨立的運行程序。這種分身術雖然隔離度高(操作系統級),使用方便(類似物理機),但佔用存儲資源多(GB級)、啟動速度慢(分鍾級)的缺點也是顯而易見的。
相較於虛擬機,容器(Container)是一種輕量型的虛擬化技術,它虛擬的是最簡運行環境(類似於沙盒)而非操作系統,啟動速度快(秒級)、佔用存儲資源少(KB級或MB級),容器間隔離度為進程級。在一台計算機上可以運行上千個容器,這是容器技術對虛擬機的碾壓式優勢。
1.2 容器、鏡像和Docker
Docker是一個開源的應用容器引擎,可以創建容器以及基於容器運行的程序。Docker可以讓開發者打包他們的應用和依賴包到一個輕量級、可移植的容器中,然後發布到任何流行的Linux機器上,也可以實現虛擬化。
聽起來很簡單,但是在Docker和容器之間,還隱藏著一個鏡像的概念,令初學者頗感困惑。本質上,Docker鏡像是一個特殊的文件系統,它提供容器運行時所需的程序、庫、資源、配置等文件。Docker鏡像類似於一個py文件,它需要Docker的運行時(類似於Python解釋器)運行。鏡像被運行時,即創建了一個鏡像的實例,一個實例就是一個容器。
1.3 Docker 和 k8s
作為容器引擎,Docker為容器化的應用程序提供了開放的標准,使得開發者可以用管理應用程序的方式來管理基礎架構,實現快速交付、測試和部署代碼。隨著容器的大量使用,又產生了如何協調、調度和管理容器的問題,Docker的容器編排應運而生。
k8s是Google開源的一個容器編排引擎,它支持自動化部署、大規模可伸縮、應用容器化管理,是一個開源的,用於管理雲平台中多個主機上的容器化的應用,k8s的目標是讓部署容器化的應用簡單並且高效,k8s提供了應用部署、規劃、更新、維護的一種機制。
Docker和k8sr都是以containerd(容器化標准)作為運行時,因此使用Docker創建的鏡像完全可以在k8s中無障礙的使用。
2.1 在ubuntu中安裝
在linux系統中安裝Docker非常簡單,官方為我們提供了一鍵安裝腳本。這個方法也適用於Debian或CentOS等發行版。
安裝過程如果出現超時,不要灰心,多試幾次,總會成功的。安裝完成後,Docker只能被root用戶使用,可以使用下面的命令取消許可權限制:
然後,重啟docker服務:
最後,關閉當前的命令行,重新打開新的命令行就可以了。
順便提一下,如果在CentOS下安裝,可能會出現一堆類似於下面的錯誤:
這是由於docker和Podman沖突造成的,需要先卸載Podman:
2.2 在Win10中安裝
Docker的運行,依賴linux的環境,官方提供了Docker Desktop for Windows,但是它需要安裝Hyper-V,Hyper-V是微軟開發的虛擬機,類似於 VMWare 或 VirtualBox,僅適用於 Windows 10。這個虛擬機一旦啟用,QEMU、VirtualBox 或 VMWare Workstation 15 及以下版本將無法使用!如果你必須在電腦上使用其他虛擬機(例如開發 Android 應用必須使用的模擬器),請不要使用 Hyper-V!
我的電腦是win10家庭版,不能直接安裝hyper-v,需要將下面的命令保存到cmd文件中:
然後在cmd文件上點擊右鍵,選擇使用管理員運行。執行完畢後會重啟,在重啟的過程中進行安裝。
2.3 Hello world
docker服務啟動的情況下,運行下面的命令:
此命令的含義是:
第一次運行時,因為本地沒有ubuntu:20.04鏡像,docker會自動從鏡像伺服器下載。下載過程可能需要多試幾次,只要成功一次,以後執行就不再需要下載了。
docker官方還提供了一個hello-world鏡像,可以直接運行:
此命令省略了鏡像版本和運行參數,docker使用latest作為版本,即最新版本。
從hello world的例子中,也可以體驗到,docker實例的運行是非常快的。
docker官方的鏡像庫比較慢,在進行鏡像操作之前,需要將鏡像源設置為國內的站點。
新建文件/etc/docker/daemon.json,輸入如下內容:
然後重啟docker的服務:
3.1 列出本地所有鏡像
執行命令 docker images 可以查看
當前我本地只有剛才安裝的兩個鏡像。
3.2 從鏡像庫中查找鏡像
執行命令 docker search 鏡像名稱可以從docker鏡像庫中查找鏡像。
最好選擇官方(OFFICIAL)的鏡像,這樣的鏡像最穩定一些。
3.3 下載新的鏡像
執行命令docker pull 鏡像名稱:版本號即可下載新的鏡像。
鏡像下載後,就可以使用鏡像來創建容器了。
4.1 啟動容器
執行命令docker run即可啟動容器,也就是創建某個鏡像的實例。docker run命令非常復雜,可以先執行一個docker run --help來查看幫助:
比如我們要執行python的shell,需要添加-it參數,即:docker run -it python:3.8
4.2 將宿主機的文件掛載到容器
docker容器與宿主機是隔離的,要想讓容器內的程序能訪問宿主機上的文件,需要通過-v參數將宿主機的文件掛載到容器中。
比如我們在宿主機上有一個hello.py,可以列印hello,想要在python容器中執行,就需要進行掛載。-v後還需要接兩個參數,分別是宿主機的目錄和容器內的目錄,兩者使用:分隔,路徑必須都是絕對路徑。
我的hello.py保存在主目錄的/docker_test目錄中,將這個目錄掛載到容器的/docker_test目錄,然後在容器內執行python /docker_test/hello.py:
4.3 容器的埠映射
我們修改一下hello.py,創建一個socket服務端,並監聽5000埠,當有客戶端連接時,列印客戶端的地址,先客戶端發送hello,然後關閉連接:
在容器內執行:
接下來,嘗試用telnet命令連接,結果卻是失敗的。原因是,127.0.0.1是宿主機的ip地址,5000是容器的埠,這與我們的習慣稍微有些不同。事實上,docker的容器是非常輕量的,它並沒有自己的網路,要想訪問容器的埠,需要進行埠映射,將容器的某埠映射到宿主機的埠,客戶端連接時,只要與宿主機的埠進行連接就可以了。
需要注意的是,上面的代碼創建的伺服器,無論如何也不可能被客戶端連接,因為代碼中綁定了127.0.0.1的ip,在容器中運行時,需要綁定所有ip,即0.0.0.0。
然後,再使用-p參數,-p還需要三個參數,即宿主機的ip地址、宿主機的埠、容器的埠,三者之間使用:分隔。一般的,可以將宿主機的ip地址省略,只寫宿主機的埠:容器的埠即可。
這樣,就將容器的5000埠映射到了宿主機的5001埠,使用:
即可與容器中的伺服器進行連接。
4.4 容器管理
上面的服務運行之後,可以使用docker ps命令,查看運行中的容器:
顯示的內容有下面幾列:
要想結束容器,可以使用docker kill 容器ID命令。
一般而言,當我們的程序開發完成後,會連同程序文件與運行環境一起製作成一個新的鏡像。
要製作鏡像,需要編寫Dockerfile。DockeFile由多個命令組成,常用的命令有:
注意,Docker鏡像中有一個層的概念,每執行一個RUN命令,就會創建一個層,層過多會導致鏡像文件體積增大。盡量在RUN命令中使用&&連接多條shell命令,減少RUN命令的個數,可以有效減小鏡像文件的體積。
5.1 自製顯示文本文件內容鏡像
編寫cat.py,接收一個文件名,由python讀取文件並顯示文件的內容:
這個例子比較簡單,縮寫Dockerfile如下:
這個Dockerfile的含義是:
需要說明的是,ENTRYPOINT有兩種寫法:
這里採用第二種寫法,是因為我們要在外部給容器傳遞參數。執行命令編譯Docker鏡像:
這個命令中,-t的含義是目標,即生成的鏡像名為hello,版本號為1.0,別忘了最後那個.,這叫到上下文路徑,是指 docker 在構建鏡像,有時候想要使用到本機的文件(比如復制),docker build 命令得知這個路徑後,會將路徑下的所有內容打包。
這樣,我們的第一個鏡像就製作完成了,使用下面的命令執行它:
即可看到~/docker_test/cat/files/test.txt的內容。
5.2 自製web伺服器鏡像
我們使用tornado開發一個網站,而python的官方鏡像是沒有tornado庫的,這就需要在製作鏡像時進行安裝。
測試的ws.py如下:
編寫Dockerfile文件如下:
在此我們驗證一下CMD與ENTRYPOINT的區別。在Dockerfile所在有目錄下執行如下命令:
執行完成後,再使用docker images使用就可以看到生成的鏡像了,然後使用下面的命令運行:
在瀏覽器中輸入宿主機的ip和8000埠,就可以看到頁面了。
在這個例子中,我使用的運行命令是CMD,如果在docker run中指定的其他的命令,此命令就不會被執行,如:
此時,容器中被執行的是python命令,而不是我們的服務。在更多情況下,我們希望在docker run命令中為我們的服務傳參,而不是覆蓋執行命令,那麼,我們應該使用ENTRYPOINT而不是CMD:
上面這種寫法,是不支持傳遞參數的,ENTRYPOINT和CMD還支持另一種寫法:
使用這種寫法,docker run命令中的參數才可以傳遞給hello.py:
這個命令中,--port=9000被作為參數傳遞到hello.py中,因此容器內的埠就成了9000。
在生產環境中運行時,不會使用-it選項,而是使用-d選項,讓容器在後台運行:
這種方式下,即使當前的控制台被關閉,該容器也不會停止。
5.3 自製apscheler服務鏡像
接下來,製作一個使用apscheler編寫的服務鏡像,代碼如下:
Dockerfile也是信手拈來:
生成鏡像:
應該可以運行了,文件復制需要兩個目錄,在運行時,可以使用兩次-v來掛載不同的目錄:
前面用到的官方python鏡像大小足足882MB,在這個基礎上,再安裝用到的第三方庫,添加項目需要的圖片等資源,大小很容易就超過1個G,這么大的鏡像,網路傳給客戶非常的不方便,因此,減小鏡像的體積是非常必要的工作。
docker hub上有個一python:3.8-alpine鏡像,大小隻有44.5MB。之所以小,是因為alpine是一個採用了busybox架構的操作系統,一般用於嵌入式應用。我嘗試使用這個鏡像,發現安裝一般的庫還好,但如果想安裝numpy等就會困難重重,甚至網上都找不到解決方案。
還是很回到基本的路線上來,主流的操作系統鏡像,ubuntu的大小為72.9MB,centos的大小為209MB——這也算是我更喜歡使用ubuntu的一個重要原因吧!使用ubuntu作為基礎鏡像,安裝python後的大小為139MB,再安裝pip後的大小一下子上升到了407MB,要是再安裝點其他東西,很容易就趕上或超過python官方鏡像的大小了。
看來,尋常路線是很難壓縮鏡像文件體積了。幸好,還有一條曲線救國的路可走,這就是多階段構建法。
多階段構建的思想其實很簡單,先構建一個大而全的鏡像,然後只把鏡像中有用的部分拿出來,放在一個新的鏡像里。在我們的場景下,pip只在構建鏡像的過程中需要,而對運行我們的程序卻一點用處也沒有。我們只需要安裝pip,再用pip安裝第三方庫,然後將第三方庫從這個鏡像中復制到一個只有python,沒有pip的鏡像中,這樣,pip佔用的268MB空間就可以被節省出來了。
1、在ubuntu鏡像的基礎上安裝python:
然後運行:
這樣,就生成了python:3.8-ubuntu鏡像。
2、在python:3.8-ubuntu的基礎上安裝pip:
然後運行:
這樣,就生成了python:3.8-ubuntu-pip鏡像。
3、多階段構建目標鏡像:
這個dockerfile需要解釋一下了,因為它有兩個FROM命令。
第一個是以python:3.8-ubuntu-pip鏡像為基礎,安裝numpy,當然,在實際應用中,把所有用到的第三方庫出寫在這里。
第二個FROM是以FROM python:3.8-ubuntu鏡像為基礎,將第三方庫統統復制過來,COPY命令後的–from=0的意思是從第0階段進行復制。實際應用中再從上下文中復製程序代碼,添加需要的ENTRYPOINT等。
最後,再運行:
這然,用於我們項目的鏡像就做好了。比使用官方python鏡像構建的版本,小了大約750MB。
到此,我們的鏡像已經製作好了,可是,鏡像文件在哪,如何在生產環境下運行呢?
剛才使用docker images命令時,已經看到了生成的鏡像:
我們可以使用docker save命令將鏡像保存到指定的文件中,保存的文件是一個.tar格式的壓縮文件:
將hello.tar復制到生產環境的機器上,然後執行導入命令:
就可以使用了。
6. 金融行業中容器技術被泛接受和使用,容器安全將要面臨哪些問題
容器有多安全?
很多人認為,容器比虛擬機安全性更低,因為如果容器主機內核存在漏洞,那麼它可以提供一種進入共享它的容器的方法。管理程序也是如此,但由於管理程序提供遠遠少於Linux內核(通常實現文件系統,網路,應用程序進程式控制制等)的功能,因此它的攻擊面更小。
但是在過去的幾年裡,為了增強容器的安全性開發了大量的軟體。
例如,Docker(和其它容器系統)現在包括一個簽名的基礎架構,允許管理員簽署容器鏡像,以防止不可信的容器被部署。
然而,可信任的簽名容器不一定可以安全運行,因為在簽名後容器中的一些軟體可能會被發現漏洞。因此,Docker和其它容器提供容器安全掃描方案,可以就容器鏡像是否有任何可被利用的漏洞而通知管理員。
更專業的容器安全軟體也被開發出來了。比如Twistlock,它提供的軟體可以配置容器的預期行為和「白名單」進程,網路活動(如源和目標IP地址和埠),甚至是某些存儲實踐,以便可以標記任何惡意的或意外的行為。
另一家專業的容器安全公司Polyverse採用了不同的方法。它利用了這樣一個事實,容器可以在幾分之一秒內啟動,以便每隔幾秒在已知的良好狀態中重新啟動容器化應用程序,將黑客必須利用在容器中運行的應用程序的時間最小化。
哪一個Linux發行版適合用作容器主機?
如果Linux發行版的預期用途只是充當容器主機來運行容器,那麼它們大多數都是功能上臃腫的。因此,很多Linux發行版本被設計為專門用於運行容器。
一些例子包括:
·Container Linux(以前的CoreOS Linux)—為容器而構建的第一個輕量級容器操作系統之一。
·RancherOS –由容器構建的簡化的Linux發行版,專門用於運行容器。
·Photon OS - 最小的Linux容器主機,被優化在VMware平台上運行。
·Project Atomic Host - Red Hat的輕量級容器操作系統擁有基於CentOS和Fedora的版本,Red Hat Enterprise Linux中還有一個下游企業版本。
·Ubuntu Core - 最小的Ubuntu版本,Ubuntu Core被設計為用於物聯網設備和大規模雲端容器部署的主機操作系統
如果是Windows環境會怎麼樣?
除了在任何運行3.10(或更高版本)的Linux內核的Linux發行版上運行,Docker還可以在Windows上運行。
這是因為在2016年,微軟在Windows Server 2016和Windows 10中引入了運行Windows容器的能力。這些是為Windows設計的Docker容器,並且它們可以在任何Docker客戶端或微軟的PowerShell中進行管理。
(微軟還引入了Hyper-V容器,這些容器是運行在Hyper-V虛擬機中的Windows容器,用於增加隔離度。)
Windows容器可以部署在Windows Server 2016的標准安裝中,精簡的Server Core安裝或Nano Server安裝選項,專門用於在容器或虛擬機中運行應用程序。
除了Linux和Windows之外,Docker還在流行的雲平台上運行,包括亞馬遜的EC2,谷歌的 Compute Engine,微軟的Azure和Rackspace。
容器最終會取代全面的伺服器虛擬化嗎?
由於一些重要的原因,這在可預見的未來不太可能。
首先,仍然有廣泛的意見認為虛擬機比容器提供了更高的安全性,因為它們提供了增強的隔離級別。
其次,可用於編排大量容器的管理工具還不如管理虛擬化基礎架構的軟體(如VMware的 vCenter或微軟的System Center)全面。對這類軟體進行了大量投資的公司在沒有充分理由的情況下不太可能放棄他們的虛擬化基礎架構。
也許更重要的是,虛擬化和容器也開始被視為互補技術而不是敵對技術。這是因為容器可以在輕量級虛擬機中運行,以增加隔離度,進而提高安全性,並且因為硬體虛擬化可以更輕松地管理支持容器所需的硬體基礎架構(網路、伺服器和存儲)。
VMware鼓勵投資虛擬機管理基礎架構的客戶在其輕量級虛擬機上的Photon OS容器Linux發行版上運行容器,而這些輕量級的虛擬機可以在vCenter進行管理。這是VMware的「VM中的容器」策略。
但是,VMware還引入了所謂的vSphere集成容器(vSphere Integrated Containers ,VIC)。這些容器可以被直接部署到獨立的ESXi主機,也可以像虛擬機一樣被部署到vCenter Server。這是VMware的「容器作為虛擬機」策略。
這兩種方法都有其優點,但重要的是,能夠在虛擬化基礎架構中使用容器而不是替換虛擬機,這往往是很有用的。