從應用開發角度認識K8S

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"雲原生應用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們正經歷從單體應用轉向分佈式微服務架構應用的技術趨勢。分佈式微服務架構作爲越來越多的軟件開發設計模式,以領域設計模型來指導業務需求的抽象與封裝。對業務的實體抽象還是邊界劃分,會以微服務架構作爲落地點,形成微服務集羣。並實施運行在雲原生編排平臺。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/01/013b22edc66739cb3f42827eb6c55f6c.png","alt":null,"title":"雲原生應用結構 from Kubernetes-Patterns","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雲原生應用的基石是乾淨整潔,業務邏輯相對單一,並與其他領域對象獨立的代碼實現。這一階段保證業務質量的主要是編程基本功,以及高覆蓋率的自動化測試能力。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"領域設計驅動是近年微服務技術熱潮下的主流設計模式,主要解決的問題是如何拆解一個複雜的業務場景需求到多個微服務單元。領域設計驅動是微服務架構的設計模式,微服務架構是基於領域設計模式的實現方式。一個微服務可以對應一個領域對象,也可以是一個領域服務。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分佈式微服務架構實現的雲原生應用具有高可用,彈性伸縮,容忍失敗以及健康自省等特點。它使得我們處理日益增長的業務需求的能力從開發編程的複雜性逐漸轉移到了資源整合,操作與管理的複雜性上。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務是單一的,運行在一個單進程中的簡單應用。容器技術恰好能夠提供這種隔離封裝,將一個簡單的微服務以Dockfile模版方式標準化,可無差別得運行在分佈式集羣的任意資源節點。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"K8S作爲目前最流行的雲原生平臺架構,對於一組微服務的交互,持續化數據的存儲,或者實施多個具有依賴關係的微服務運行,以及容量規劃等問題,能夠提供一套自動化的系統性解決方案。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"用OOP方式解讀K8S"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於應用開發者,面向對象模式想必瞭然於胸。OOP設計了一套對一個邏輯對象的生命週期管理的方法論,類比OOP思路,筆者接下來詳細介紹一些K8S核心資源對象以及應用方式。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a1/a1495432cb615caf9d3a76fb8448aac9.png","alt":null,"title":"OOP vs K8S from Kubernetes-Patterns","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"構建/部署保持隔離性Pod/Deployment"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Image"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"容器鏡像類比OOP的類,定義了一個模塊的全部屬性與功能,提供了唯一暴露在外的API調用方式以及參數集合,對應着一個獨立完整的發佈週期,就像容器的設計藍圖。這種靜態定義方式,可以定義並初始化容器進行,使其在任意環境任意時刻行爲完全一致。一個容器鏡像對應着一個微服務,屬於開發團隊的產物。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Container"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"容器類比OOP的對象,是容器鏡像的運行態。一個容器是一個容器鏡像的運行進程,而一個容器鏡像則可以在任意時刻任意環境下創建任何數量容器。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Pod"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java應用開發者都知道基於Springboot-MVC框架的Java應用部署時只需提供一個Jar包,Jar包內部源碼被編譯後不可再改變。Pod是雲原生編排平臺的資源調度部署的最小單元。Pod和容器的關係類似Java的Jar包和對象,應用開發者交付的容器鏡像通過Pod在K8S集羣上部署並調度,容器則是Pod的內部資源對象,K8S無法感知也無法干預。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"K8S設計Pod爲部署調度的最小單元,是因爲Pod實現了內部一組容器在存儲空間,網絡空間及進程空間可共享該Pod資源,類似一個虛擬機上同時運行多個進程。容器間通信類似單節點進程間通信。Pod 的設計,就是要讓它裏面的容器儘可能多地共享 Linux Namespace,僅保留必要的隔離和限制能力。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Pod類似OOP的Module,即邏輯緊密的對象集合通常屬於一個獨立模塊。Pod的定義示例如下:"}]}]}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"apiVersion: v1\nkind: Pod\nmetadata:\n name: index-helm-57677c549-lgww5\n namespace: bss-dev\nspec:\n containers:\n - command:\n - java\n - '-jar'\n - /home/demo/app.jar\n env:\n - name: aliyun_logs_release_tags\n value: revision=3f44253.20201030-1039\n image: 'registry-vpc.cn-shanghai-finance-1.aliyuncs.com/XXX/XXX.XXX:latest'\n imagePullPolicy: Always"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Pod->spec下包含了一個或多個容器模版定義。PodYAML提供了很多屬性,有興趣深入的讀者可以參考學習"},{"type":"link","attrs":{"href":"https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/api/core/v1/types.go","title":""},"content":[{"type":"text","text":"https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/api/core/v1/types.go"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Pod->spec有一個很特殊的容器模版定義,關鍵詞爲initContainer,顧名思義是做初始化的容器。初始化容器必須先於應用容器啓動執行完畢,並且只能執行成功。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Pod->spec除了定義了容器模版,還定義容器間共享的存儲資源Volume掛載方式,和影響Pod資源調度的節點選擇器標籤,親和性以及容忍性等。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"NameSpace"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"命名空間是一個組的概念,爲集羣資源提供了邏輯劃分的能力。這種使用方式類似OOP的Package。當項目變大,時長有同名的類或者對象,此時爲了區分,我們會並以packge爲路徑前綴,定義和引用相應對象。K8S集羣中常常運行着數百個應用服務,也會出現同名資源的情況。命名空間可以在K8S上實現對一組資源對象隔離與權限管理。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"命名空間最常用的場景是在一個K8S集羣區分開發環境和測試環境。命名空間也可以提供多租戶運行環境,或者爲了應用運行具有隔離性,爲某個應用部署命名一個單獨的命名空間。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"命名空間雖然提供了資源範圍邏輯劃分的能力,但是並沒有真正隔離一個集羣內部Pod之間的通信,即屬於同一集羣內的多個命名空間下的Pod仍能在集羣內互相通信。如果需要做到命名空間之間的完全隔離,可以採用NetworkPolicy實現。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Deployment"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在K8S平臺運行的應用服務版本升級就是創建一個新版本Pod,並銷燬舊版本Pod的過程,由Deployment資源對象定義。微服務架構應用的數量成百上千,如果手動部署,則會引入人爲錯誤並且部署操作很容易成爲整個系統的瓶頸。K8S將部署Pod全部操作定義在Deployment資源對象,由平臺自動化地部署Pod。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Deployment資源對象除了要定義部署Pod是什麼樣的以外,還會定義預期部署狀態。比如,預期部署Pod數量,或在哪個節點上部署。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"部署一個新版本要麼就生成額外的帶有新版本Pod,待健康檢查等確認新Pod可以提供服務,再將原有的帶有舊版本Pod銷燬,最終達到預期部署狀態;要麼就先銷燬原有舊版本Pod,再生成新版本Pod,直到達到預期部署狀態。第一種方式爲Rolling Update,好處是部署時沒有任何downtime,壞處是會存在多個版本同時提供服務,導致服務狀態不一致。第二種方式爲Recreate,好處是在任意時刻都不會存在多個版本的應用服務,壞處則是部署時存在downtime。K8S默認的部署策略爲Rolling Update。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Deployment通過ReplicaSet控制器創建和管理Pod,確保Pod能如預期運行成功,並且滿足Deployment的部署定義,比如開啓幾個副本,滿足部署策略等。ReplicaSet解耦了部署與Pod運行,Pod處於Running狀態並不能代表應用部署成功。Deployment對Pod副本數量以及應用容器的Health Probe做了設置,以確保進程啓動以及應用運行成功。只有當新建ReplicaSet所屬的Pod運行成功,並且副本數量達到Deployment設置ReplicaSet的數量,舊的ReplicaSet管理的Pod不再承接任何負載或被銷燬時,Deployment達到預期部署狀態,才能代表部署成功。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"構造器InitContainer"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"初始化在OOP中,比如Java類定義,是構造器。封裝了對象使用之前必要的初始化操作。初始化容器InitContainer,InitContainer是Pod級別的初始化,同理,是Pod->spec的一類特殊的容器模版定義,隔離了主應用容器進程與初始化操作,確保初始化操作能夠在應用容器啓動之前完成。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在容器級別也可以完成初始化,利用容器鏡像模版Dockerfile->ENTERPOINT定義。容器級別的初始化影響範圍是該容器鏡像定義內部,而Pod級別初始化操作InitContainer是對Pod內的全部容器組定義。一般情況下,容器初始化更多是Devops關心,與應用開發者工作相關性不大。InitContainer可以將容器模版定義從開發運行週期上完全隔離。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Pod級別初始化操作InitContainer的好處是可以統一設置訪問共享Volume的訪問權限;在應用服務啓動之前準備好依賴的組件或者數據;驗證應用服務運行的依賴運行健康等。在應用服務主容器進程啓動之前,確保前置條件準備完備,進而確保應用服務能夠運行成功。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"處於訪問控制等安全性考慮,一般不建議在應用容器鏡像定義裏開放Pod共享資源的訪問權限。我們儘量將Pod的共享資源管理操作留給編排平臺設置與控制,與應用服務本身隔離。最大化的確保應用服務本身不帶有平臺依賴屬性。所以,InitContainer也提升了微服務應用開發的安全性。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個Pod模版可定義多個InitContainer以及多個應用容器。K8S確保InitContainer按定義順序依次執行初始化操作,在應用容器啓動前執行完畢,而應用容器啓動是並行的。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"InitContainer與一般的應用容器基本相同,但是,InitContainer一般爲Completed,不會存在Failed終結狀態,因爲當InitContainer執行失敗會直接導致Pod重啓,重新開始執行InitContainer。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"組合模式Sidecar"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在前文我們把容器鏡像和容器類比OOP的類和對象,因爲容器鏡像定義了一個職責單一的應爲微服務。那如果在應服務運行時,我們需要擴展或添加一些旁路操作,此時,類似OOP的組合設計模式,我們其實可以直接整合另一個容器鏡像定義到同一個Pod,這就是Sidecar。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Sidecar保證了應用容器的職責單一,同時,也能在Pod級別爲其添加更新數據,配置文件,靜態資源或者採集日誌數據這種能夠獨立複用的旁路操作集合。Sidecar能夠通過組合多個職責單一的容器,提供一個功能完備,具備上線能力的應用微服務,同時,確保開發團隊只用考慮業務應用功能本身。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Sidecar與InitContainer是兩種不同的容器定義。首先InitContainer定義的是Pod級別的初始化操作集合,必須在所有應用容器啓動之前執行完畢,具有嚴格的執行順序。Sidecar與應用容器執行順序沒有嚴格控制,兩者通常是同時運行在一個Pod內,共享Pod資源,共同完成Pod暴露的服務能力。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"配置管理ConfigMap/Secret"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"應用開發的12原則中有一條是在環境中存儲配置。配置信息與應用隔離,可以通過環境變量來存儲應用的配置信息。環境變量具有全局性,可將其在應用進程運行時加載。當配置信息較大時,利用環境變量傳遞配置信息就不是什麼好辦法了。Java-Springboot應用提供了Profile文件,記錄和保存應用相關的配置信息,開發者可以依據環境區分不同的Profile文件。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在K8S裏配置管理ConfigMap和Secret同時支持環境變量Key/Value形式和應用配置Profile文件形式。環境變量的Key一般是全大寫字母字母表示;應用配置Profile文件是小寫字母表示額文件名。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"K8S提供Secret資源對象來配置敏感數據。比如,數據庫鏈接的用戶名,密碼等。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ConfigMap與Secret對象通過Volume掛載到Pod裏,所以該配置信息被Pod內的容器組共享。ConfigMap與Secret的數據存儲上限爲1MB,故當應用配置文件過大,可考慮使用InitContainer初始化一個配置管理容器在同一個Pod下。在應用容器啓動前,傳遞應用配置到應用指定掛載目錄,從而更新應用配置信息。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"異步/併發執行Job/Cronjob"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"應用開發過程中,常會面對批處理任務/定時任務需求。目前流行的應用框架,比如Java的Spring-Batch或者Python的Celery都可以實現異步任務/定時任務。但是這種應用級別的實現方法,在雲原生中,會使應用服務實現的很重,比如異步任務通常要求所屬應用滿足高可用,資源彈性伸縮以及故障自愈。這些特性都是K8S平臺天然自帶的,可以考慮將應用的異步任務實現委託給K8S的Job/Cronjob控制器對象。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"K8S的Job控制器對象,類似Deployment,是創建與管理Pod生命週期的一種實現方式。與Deployment不同的是,Job控制的Pod是運行結束就終止,即Pod的終態爲Completed。Job的Pod默認不會直接銷燬,主要目的是提供查看任務運行日誌結果。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"K8S的Cronjob控制器對象,顧名思義,在Job對象之上組合了定時觸發事件邏輯。主要使用的場景包括但不限於文件傳輸,發送郵件或者短信通知,以及備份與定時清理過期備份等。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"結語"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"筆者整理了一部分K8S基礎知識點的初衷是爲了審視一下K8S這個龐大的技術棧裏開發者掌握和使用K8S所要了解的最小知識點集合。筆者相信未來的應用都是建立在雲之上,所以不論是哪個角色,都得掌握必要的K8S知識點才能流暢地開啓雲原生開發之旅。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上很多內容都是筆者在學習"},{"type":"link","attrs":{"href":"https://time.geekbang.org/column/intro/116","title":""},"content":[{"type":"text","text":"https://time.geekbang.org/column/intro/116"}]},{"type":"text","text":"以及Kubernetes-patterns"},{"type":"link","attrs":{"href":"https://developers.redhat.com/blog/2020/05/11/top-10-must-know-kubernetes-design-patterns/","title":""},"content":[{"type":"text","text":"https://developers.redhat.com/blog/2020/05/11/top-10-must-know-kubernetes-design-patterns/"}]},{"type":"text","text":"時的學習心得和讀書體會,收益於大師們對K8S技術棧多種角度的解讀與梳理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後這次比較系統的學習梳理的契機也源於團隊對於K8S技術的重視,特別感謝團隊領導的重視與支持。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1}},{"type":"heading","attrs":{"align":null,"level":2}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章