從零開始入門 K8s| 詳解 Pod 及容器設計模式
一、為什麼需要 Pod
容器的基本概念
我們知道 Pod 是 Kubernetes 專案裡面一個非常重要的概念,也是非常重要的一個原子排程單位,但是為什麼我們會需要這樣一個概念呢?在使用容器 Docker 的時候,也沒有這個說法。其實,如果想要理解 Pod,首先要理解容器,所以來回顧一下容器的概念:
容器的本質實際上是一個程式,是一個檢視被隔離,資源受限的程式。容器裡面 PID=1 的程式就是應用本身,這意味著管理虛擬機器等於管理基礎設施,因為我們是在管理機器,但管理容器卻等於直接管理應用本身。這也是之前說過的不可變基礎設施的一個最佳體現,這個時候,你的應用就等於你的基礎設施,它一定是不可變的。
在以上面的例子為前提的情況下,Kubernetes 又是什麼呢?很多人都說 Kubernetes 是雲時代的作業系統,這個非常有意思,因為如果以此類推,容器映象就是這個作業系統的軟體安裝包,它們之間是這樣的一個類比關係。
真實作業系統裡的例子
如果說 Kubernetes 就是作業系統的話,那麼不妨看一下真實的作業系統的例子。
例子裡面有一個程式叫做 Helloworld,這個 Helloworld 程式實際上是由一組程式組成的,需要注意一下,這裡說的程式實際上等同於 Linux 中的執行緒。
因為 Linux 中的執行緒是輕量級程式,所以如果從 Linux 系統中去檢視 Helloworld 中的 pstree,將會看到這個 Helloworld 實際上是由四個執行緒組成的,分別是
{api、main、log、compute}。也就是說,四個這樣的執行緒共同協作,共享 Helloworld 程式的資源,組成了 Helloworld 程式的真實工作情況。
這是作業系統裡面程式組或者執行緒組中一個非常真實的例子,以上就是程式組的一個概念。
那麼大家不妨思考一下,在真實的作業系統裡面,一個程式往往是根據程式組來進行管理的。Kubernetes 把它類比為一個作業系統,比如說 Linux。針對於容器我們前面提到可以類比為程式,就是前面的 Linux 執行緒。那麼 Pod 又是什麼呢?實際上 Pod 就是我們剛剛提到的程式組,也就是 Linux 裡的執行緒組。
程式組概念
說到程式組,首先建議大家至少有個概念上的理解,然後我們再詳細的解釋一下。
還是前面那個例子:Helloworld 程式由四個程式組成,這些程式之間會共享一些資源和檔案。那麼現在有一個問題:假如說現在把 Helloworld 程式用容器跑起來,你會怎麼去做?
當然,最自然的一個解法就是,我現在就啟動一個 Docker 容器,裡面執行四個程式。可是這樣會有一個問題,這種情況下容器裡面 PID=1 的程式該是誰? 比如說,它應該是我的 main 程式,那麼問題來了,“誰”又負責去管理剩餘的 3 個程式呢?
這個核心問題在於,容器的設計本身是一種“單程式”模型,不是說容器裡只能起一個程式,由於容器的應用等於程式,所以只能去管理 PID=1 的這個程式,其他再起來的程式其實是一個託管狀態。 所以說服務應用程式本身就具有“程式管理”的能力。
比如說 Helloworld 的程式有 system 的能力,或者直接把容器裡 PID=1 的程式直接改成 systemd,否則這個應用,或者是容器是沒有辦法去管理很多個程式的。因為 PID=1 程式是應用本身,如果現在把這個 PID=1 的程式給 kill 了,或者它自己執行過程中死掉了,那麼剩下三個程式的資源就沒有人回收了,這個是非常嚴重的一個問題。
反過來,如果真的把這個應用本身改成了 systemd,或者在容器裡面執行了一個 systemd,將會導致另外一個問題:使得管理容器不再是管理應用本身了,而等於是管理 systemd,這裡的問題就非常明顯了。比如說我這個容器裡面 run 的程式或者程式是 systemd,那麼接下來,這個應用是不是退出了?是不是 fail 了?是不是出現異常失敗了?實際上是沒辦法直接知道的,因為容器管理的是 systemd。這就是為什麼在容器裡面執行一個複雜程式往往比較困難的一個原因。
這裡再幫大家梳理一下:
由於容器實際上是一個“單程式”模型,所以如果你在容器裡啟動多個程式,只有一個可以作為 PID=1 的程式,而這時候,如果這個 PID=1 的程式掛了,或者說失敗退出了,那麼其他三個程式就會自然而然的成為孤兒,沒有人能夠管理它們,沒有人能夠回收它們的資源,這是一個非常不好的情況。
注意:Linux 容器的“單程式”模型,指的是容器的生命週期等同於 PID=1 的程式(容器應用程式)的生命週期,而不是說容器裡不能建立多程式。當然,一般情況下,容器應用程式並不具備程式管理能力,所以你透過 exec 或者 ssh 在容器裡建立的其他程式,一旦異常退出(比如 ssh 終止)是很容易變成孤兒程式的。
反過來,其實可以在容器裡面 run 一個 systemd,用它來管理其他所有的程式。這樣會產生第二個問題:實際上沒辦法直接管理我的應用了,因為我的應用被 systemd 給接管了,那麼這個時候應用狀態的生命週期就不等於容器生命週期。這個管理模型實際上是非常非常複雜的。
Pod = “程式組”
在 Kubernetes 裡面,Pod 實際上正是 Kubernetes 專案為你抽象出來的一個可以類比為程式組的概念。
前面提到的,由四個程式共同組成的一個應用 Helloworld,在 Kubernetes 裡面,實際上會被定義為一個擁有四個容器的 Pod,這個概念大家一定要非常仔細的理解。
就是說現在有四個職責不同、相互協作的程式,需要放在容器裡去執行,在 Kubernetes 裡面並不會把它們放到一個容器裡,因為這裡會遇到兩個問題。那麼在 Kubernetes 裡會怎麼去做呢?它會把四個獨立的程式分別用四個獨立的容器啟動起來,然後把它們定義在一個 Pod 裡面。
所以當 Kubernetes 把 Helloworld 給拉起來的時候,你實際上會看到四個容器,它們共享了某些資源,這些資源都屬於 Pod,所以我們說 Pod 在 Kubernetes 裡面只有一個邏輯單位,沒有一個真實的東西對應說這個就是 Pod,不會有的。真正起來在物理上存在的東西,就是四個容器,這四個容器,或者說是多個容器的組合就叫做 Pod。並且還有一個概念一定要非常明確,Pod 是 Kubernetes 分配資源的一個單位,因為裡面的容器要共享某些資源,所以 Pod 也是 Kubernetes 的原子排程單位。
上面提到的 Pod 設計,也不是 Kubernetes 專案自己想出來的, 而是早在 Google 研發 Borg 的時候,就已經發現了這樣一個問題。這個在 Borg paper 裡面有非常非常明確的描述。簡單來說 Google 工程師發現在 Borg 下面部署應用時,很多場景下都存在著類似於“程式與程式組”的關係。更具體的是,這些應用之前往往有著密切的協作關係,使得它們必須部署在同一臺機器上並且共享某些資訊。
以上就是程式組的概念,也是 Pod 的用法。
為什麼 Pod 必須是原子排程單位?
可能到這裡大家會有一些問題:雖然瞭解這個東西是一個程式組,但是為什麼要把 Pod 本身作為一個概念抽象出來呢?或者說能不能透過排程把 Pod 這個事情給解決掉呢?為什麼 Pod 必須是 Kubernetes 裡面的原子排程單位?
下面我們透過一個例子來解釋。
假如現在有兩個容器,它們是緊密協作的,所以它們應該被部署在一個 Pod 裡面。具體來說,第一個容器叫做 App,就是業務容器,它會寫日誌檔案;第二個容器叫做 LogCollector,它會把剛剛 App 容器寫的日誌檔案轉發到後端的 ElasticSearch 中。
兩個容器的資源需求是這樣的:App 容器需要 1G 記憶體,LogCollector 需要 0.5G 記憶體,而當前叢集環境的可用記憶體是這樣一個情況:Node_A:1.25G 記憶體,Node_B:2G 記憶體。
假如說現在沒有 Pod 概念,就只有兩個容器,這兩個容器要緊密協作、執行在一臺機器上。可是,如果排程器先把 App 排程到了 Node_A 上面,接下來會怎麼樣呢?這時你會發現:LogCollector 實際上是沒辦法排程到 Node_A 上的,因為資源不夠。其實此時整個應用本身就已經出問題了,排程已經失敗了,必須去重新排程。
以上就是一個非常典型的成組排程失敗的例子。英文叫做:Task co-scheduling 問題,這個問題不是說不能解,在很多專案裡面,這樣的問題都有解法。
比如說在 Mesos 裡面,它會做一個事情,叫做資源囤積(resource hoarding):即當所有設定了 Affinity 約束的任務都達到時,才開始統一排程,這是一個非常典型的成組排程的解法。
所以上面提到的“App”和“LogCollector”這兩個容器,在 Mesos 裡面,他們不會說立刻排程,而是等兩個容器都提交完成,才開始統一排程。這樣也會帶來新的問題,首先排程效率會損失,因為需要等待。由於需要等,還會有外一個情況會出現,就是產生死鎖,即互相等待的一個情況。這些機制在 Mesos 裡都是需要解決的,也帶來了額外的複雜度。
另一種解法是 Google 的解法。它在 Omega 系統(就是 Borg 下一代)裡面,做了一個非常複雜且非常厲害的解法,叫做樂觀排程。比如說:不管這些衝突的異常情況,先排程,同時設定一個非常精妙的回滾機制,這樣經過沖突後,透過回滾來解決問題。這個方式相對來說要更加優雅,也更加高效,但是它的實現機制是非常複雜的。這個有很多人也能理解,就是悲觀鎖的設定一定比樂觀鎖要簡單。
而像這樣的一個 Task co-scheduling 問題,在 Kubernetes 裡,就直接透過 Pod 這樣一個概念去解決了。因為在 Kubernetes 裡,這樣的一個 App 容器和 LogCollector 容器一定是屬於一個 Pod 的,它們在排程時必然是以一個 Pod 為單位進行排程,所以這個問題是根本不存在的。
再次理解 Pod
在講了前面這些知識點之後,我們來再次理解一下 Pod,首先 Pod 裡面的容器是“超親密關係”。
這裡有個“超”字需要大家理解,正常來說,有一種關係叫做親密關係,這個親密關係是一定可以透過排程來解決的。
比如說現在有兩個 Pod,它們需要執行在同一臺宿主機上,那這樣就屬於親密關係,排程器一定是可以幫助去做的。但是對於超親密關係來說,有一個問題,即它必須透過 Pod 來解決。因為如果超親密關係賦予不了,那麼整個 Pod 或者說是整個應用都無法啟動。
什麼叫做超親密關係呢?大概分為以下幾類:
-
比如說兩個程式之間會發生檔案交換,前面提到的例子就是這樣,一個寫日誌,一個讀日誌;
-
兩個程式之間需要透過 localhost 或者說是本地的 Socket 去進行通訊,這種本地通訊也是超親密關係;
-
這兩個容器或者是微服務之間,需要發生非常頻繁的 RPC 呼叫,出於效能的考慮,也希望它們是超親密關係;
-
兩個容器或者是應用,它們需要共享某些 Linux Namespace。最簡單常見的一個例子,就是我有一個容器需要加入另一個容器的 Network Namespace。這樣我就能看到另一個容器的網路裝置,和它的網路資訊。
像以上幾種關係都屬於超親密關係,它們都是在 Kubernetes 中會透過 Pod 的概念去解決的。
現在我們理解了 Pod 這樣的概念設計,理解了為什麼需要 Pod。它解決了兩個問題:
-
我們怎麼去描述超親密關係;
-
我們怎麼去對超親密關係的容器或者說是業務去做統一排程,這是 Pod 最主要的一個訴求。
二、Pod 的實現機制
Pod 要解決的問題
像 Pod 這樣一個東西,本身是一個邏輯概念。那在機器上,它究竟是怎麼實現的呢?這就是我們要解釋的第二個問題。
既然說 Pod 要解決這個問題,核心就在於如何讓一個 Pod 裡的多個容器之間最高效的共享某些資源和資料。
因為容器之間原本是被 Linux Namespace 和 cgroups 隔開的,所以現在實際要解決的是怎麼去打破這個隔離,然後共享某些事情和某些資訊。這就是 Pod 的設計要解決的核心問題所在。
所以說具體的解法分為兩個部分:網路和儲存。
1.共享網路
第一個問題是 Pod 裡的多個容器怎麼去共享網路?下面是個例子:
比如說現在有一個 Pod,其中包含了一個容器 A 和一個容器 B,它們兩個就要共享 Network Namespace。在 Kubernetes 裡的解法是這樣的:它會在每個 Pod 裡,額外起一個 Infra container 小容器來共享整個 Pod 的 Network Namespace。
Infra container 是一個非常小的映象,大概 100~200KB 左右,是一個組合語言寫的、永遠處於“暫停”狀態的容器。由於有了這樣一個 Infra container 之後,其他所有容器都會透過 Join Namespace 的方式加入到 Infra container 的 Network Namespace 中。
所以說一個 Pod 裡面的所有容器,它們看到的網路檢視是完全一樣的。即:它們看到的網路裝置、IP地址、Mac地址等等,跟網路相關的資訊,其實全是一份,這一份都來自於 Pod 第一次建立的這個 Infra container。這就是 Pod 解決網路共享的一個解法。
在 Pod 裡面,一定有一個 IP 地址,是這個 Pod 的 Network Namespace 對應的地址,也是這個 Infra container 的 IP 地址。所以大家看到的都是一份,而其他所有網路資源,都是一個 Pod 一份,並且被 Pod 中的所有容器共享。這就是 Pod 的網路實現方式。
由於需要有一個相當於說中間的容器存在,所以整個 Pod 裡面,必然是 Infra container 第一個啟動。並且整個 Pod 的生命週期是等同於 Infra container 的生命週期的,與容器 A 和 B 是無關的。這也是為什麼在 Kubernetes 裡面,它是允許去單獨更新 Pod 裡的某一個映象的,即:做這個操作,整個 Pod 不會重建,也不會重啟,這是非常重要的一個設計。
2.共享儲存
第二問題:Pod 怎麼去共享儲存?Pod 共享儲存就相對比較簡單。
比如說現在有兩個容器,一個是 Nginx,另外一個是非常普通的容器,在 Nginx 裡放一些檔案,讓我能透過 Nginx 訪問到。所以它需要去 share 這個目錄。我 share 檔案或者是 share 目錄在 Pod 裡面是非常簡單的,實際上就是把 volume 變成了 Pod level。然後所有容器,就是所有同屬於一個 Pod 的容器,他們共享所有的 volume。
比如說上圖的例子,這個 volume 叫做 shared-data,它是屬於 Pod level 的,所以在每一個容器裡可以直接宣告:要掛載 shared-data 這個 volume,只要你宣告瞭你掛載這個 volume,你在容器裡去看這個目錄,實際上大家看到的就是同一份。這個就是 Kubernetes 透過 Pod 來給容器共享儲存的一個做法。
所以在之前的例子中,應用容器 App 寫了日誌,只要這個日誌是寫在一個 volume 中,只要宣告掛載了同樣的 volume,這個 volume 就可以立刻被另外一個 LogCollector 容器給看到。以上就是 Pod 實現儲存的方式。
三、詳解容器設計模式
現在我們知道了為什麼需要 Pod,也瞭解了 Pod 這個東西到底是怎麼實現的。最後,以此為基礎,詳細介紹一下 Kubernetes 非常提倡的一個概念,叫做容器設計模式。
舉例
接下來將會用一個例子來給大家進行講解。
比如我現在有一個非常常見的一個訴求:我現在要釋出一個應用,這個應用是 JAVA 寫的,有一個 WAR 包需要把它放到 Tomcat 的 web APP 目錄下面,這樣就可以把它啟動起來了。可是像這樣一個 WAR 包或 Tomcat 這樣一個容器的話,怎麼去做,怎麼去釋出?這裡面有幾種做法。
-
第一種方式:可以把 WAR 包和 Tomcat 打包放進一個映象裡面。但是這樣帶來一個問題,就是現在這個映象實際上揉進了兩個東西。那麼接下來,無論是我要更新 WAR 包還是說我要更新 Tomcat,都要重新做一個新的映象,這是比較麻煩的;
-
第二種方式:就是映象裡面只打包 Tomcat。它就是一個 Tomcat,但是需要使用資料卷的方式,比如說 hostPath,從宿主機上把 WAR 包掛載進我們 Tomcat 容器中,掛到我的 web APP 目錄下面,這樣把這個容器啟用起來之後,裡面就能用了。
但是這時會發現一個問題:這種做法一定需要維護一套分散式儲存系統。因為這個容器可能第一次啟動是在宿主機 A 上面,第二次重新啟動就可能跑到 B 上去了,容器它是一個可遷移的東西,它的狀態是不保持的。所以必須維護一套分散式儲存系統,使容器不管是在 A 還是在 B 上,都可以找到這個 WAR 包,找到這個資料。
注意,即使有了分散式儲存系統做 Volume,你還需要負責維護 Volume 裡的 WAR 包。比如:你需要單獨寫一套 Kubernetes Volume 外掛,用來在每次 Pod 啟動之前,把應用啟動所需的 WAR 包下載到這個 Volume 裡,然後才能被應用掛載使用到。
這樣操作帶來的複雜程度還是比較高的,且這個容器本身必須依賴於一套持久化的儲存外掛(用來管理 Volume 裡的 WAR 包內容)。
InitContainer
所以大家有沒有考慮過,像這樣的組合方式,有沒有更加通用的方法?哪怕在本地 Kubernetes 上,沒有分散式儲存的情況下也能用、能玩、能釋出。
實際上方法是有的,在 Kubernetes 裡面,像這樣的組合方式,叫做 Init Container。
還是同樣一個例子:在上圖的 yaml 裡,首先定義一個 Init Container,它只做一件事情,就是把 WAR 包從映象裡複製到一個 Volume 裡面,它做完這個操作就退出了,所以 Init Container 會比使用者容器先啟動,並且嚴格按照定義順序來依次執行。
然後,這個關鍵在於剛剛複製到的這樣一個目的目錄:APP 目錄,實際上是一個 Volume。而我們前面提到,一個 Pod 裡面的多個容器,它們是可以共享 Volume 的,所以現在這個 Tomcat 容器,只是打包了一個 Tomcat 映象。但在啟動的時候,要宣告使用 APP 目錄作為我的 Volume,並且要把它們掛載在 Web APP 目錄下面。
而這個時候,由於前面執行過了一個 Init Container,已經執行完複製操作了,所以這個 Volume 裡面已經存在了應用的 WAR 包:就是 sample.war,絕對已經存在這個 Volume 裡面了。等到第二步執行啟動這個 Tomcat 容器的時候,去掛這個 Volume,一定能在裡面找到前面複製來的 sample.war。
所以可以這樣去描述:這個 Pod 就是一個自包含的,可以把這一個 Pod 在全世界任何一個 Kubernetes 上面都順利啟用起來。不用擔心沒有分散式儲存、Volume 不是持久化的,它一定是可以公佈的。
所以這是一個透過組合兩個不同角色的容器,並且按照一些像 Init Container 的編排方式,統一去打包這樣一個應用,把它用 Pod 來去做的非常典型的一個例子。像這樣的一個概念,在 Kubernetes 裡面就是一個非常經典的容器設計模式,叫做:“Sidecar”。
容器設計模式:Sidecar
什麼是 Sidecar?就是說其實在 Pod 裡面,可以定義一些專門的容器,來執行主業務容器所需要的一些輔助工作,比如我們前面舉的例子,其實就幹了一個事兒,這個 Init Container,它就是一個 Sidecar,它只負責把映象裡的 WAR 包複製到共享目錄裡面,以便被 Tomcat 能夠用起來。
其它有哪些操作呢?比如說:
-
原本需要在容器裡面執行 SSH 需要乾的一些事情,可以寫指令碼、一些前置的條件,其實都可以透過像 Init Container 或者另外像 Sidecar 的方式去解決;
-
當然還有一個典型例子就是我的日誌收集,日誌收集本身是一個程式,是一個小容器,那麼就可以把它打包進 Pod 裡面去做這個收集工作;
-
還有一個非常重要的東西就是 Debug 應用,實際上現在 Debug 整個應用都可以在應用 Pod 裡面再次定義一個額外的小的 Container,它可以去 exec 應用 pod 的 namespace;
-
檢視其他容器的工作狀態,這也是它可以做的事情。不再需要去 SSH 登陸到容器裡去看,只要把監控元件裝到額外的小容器裡面就可以了,然後把它作為一個 Sidecar 啟動起來,跟主業務容器進行協作,所以同樣業務監控也都可以透過 Sidecar 方式來去做。
這種做法一個非常明顯的優勢就是在於其實將輔助功能從我的業務容器解耦了,所以我就能夠獨立釋出 Sidecar 容器,並且更重要的是這個能力是可以重用的,即同樣的一個監控 Sidecar 或者日誌 Sidecar,可以被全公司的人共用的。這就是設計模式的一個威力。
Sidecar:應用與日誌收集
接下來,我們再詳細細化一下 Sidecar 這樣一個模式,它還有一些其他的場景。
比如說前面提到的應用日誌收集,業務容器將日誌寫在一個 Volume 裡面,而由於 Volume 在 Pod 裡面是被共享的,所以日誌容器 —— 即 Sidecar 容器一定可以透過共享該 Volume,直接把日誌檔案讀出來,然後存到遠端儲存裡面,或者轉發到另外一個例子。現在業界常用的 Fluentd 日誌程式或日誌元件,基本上都是這樣的工作方式。
Sidecar:代理容器
Sidecar 的第二個用法,可以稱作為代理容器 Proxy。什麼叫做代理容器呢?
假如現在有個 Pod 需要訪問一個外部系統,或者一些外部服務,但是這些外部系統是一個叢集,那麼這個時候如何透過一個統一的、簡單的方式,用一個 IP 地址,就把這些叢集都訪問到?有一種方法就是:修改程式碼。因為程式碼裡記錄了這些叢集的地址;另外還有一種解耦的方法,即透過 Sidecar 代理容器。
簡單說,單獨寫一個這麼小的 Proxy,用來處理對接外部的服務叢集,它對外暴露出來只有一個 IP 地址就可以了。所以接下來,業務容器主要訪問 Proxy,然後由 Proxy 去連線這些服務叢集,這裡的關鍵在於 Pod 裡面多個容器是透過 localhost 直接通訊的,因為它們同屬於一個 network Namespace,網路檢視都一樣,所以它們倆通訊 localhost,並沒有效能損耗。
所以說代理容器除了做了解耦之外,並不會降低效能,更重要的是,像這樣一個代理容器的程式碼就又可以被全公司重用了。
Sidecar:介面卡容器
Sidecar 的第三個設計模式 —— 介面卡容器 Adapter,什麼叫 Adapter 呢?
現在業務暴露出來的 API,比如說有個 API 的一個格式是 A,但是現在有一個外部系統要去訪問我的業務容器,它只知道的一種格式是 API B ,所以要做一個工作,就是把業務容器怎麼想辦法改掉,要去改業務程式碼。但實際上,你可以透過一個 Adapter 幫你來做這層轉換。
有個例子:現在業務容器暴露出來的監控介面是 /metrics,訪問這個容器的 metrics 的 URL 就可以拿到了。可是現在,這個監控系統升級了,它訪問的 URL 是 /health,我只認得暴露出 health 健康檢查的 URL,才能去做監控,metrics 不認識。那這個怎麼辦?那就需要改程式碼了,但可以不去改程式碼,額外寫一個 Adapter,用來把所有對 health 的這個請求轉發給 metrics 就可以了,所以這個 Adapter 對外暴露的是 health 這樣一個監控的 URL,這就可以了,你的業務就又可以工作了。
這樣的關鍵,還在於 Pod 之中的容器是透過 localhost 直接通訊的,所以沒有效能損耗,並且這樣一個 Adapter 容器可以被全公司重用起來,這些都是設計模式給我們帶來的好處。
本文總結
-
Pod 是 Kubernetes 專案裡實現“容器設計模式”的核心機制;
-
“容器設計模式”是 Google Borg 的大規模容器叢集管理最佳實踐之一,也是 Kubernetes 進行復雜應用編排的基礎依賴之一;
-
所有“設計模式”的本質都是:解耦和重用。
本文作者:張磊 阿里雲容器平臺高階技術專家,CNCF 官方大使
本文為雲棲社群原創內容,未經允許不得轉載。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69947441/viewspace-2657701/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 從零開始入門 K8s | 深入剖析 Linux 容器K8SLinux
- 從零開始入門 K8s | 理解容器執行時介面 CRIK8S
- 從零開始入門 K8s | 理解 RuntimeClass 與使用多容器K8S
- 從零開始入門 K8s | Kata Containers 創始人帶你入門安全容器技術K8SAI
- 從零開始入門 K8s | 理解 RuntimeClass 與使用多容器執行時K8S
- 從零開始入門 K8s | Kubernetes API 程式設計正規化K8SAPI程式設計
- 從零開始學設計模式(七)—橋接模式設計模式橋接
- 從零開始入門 K8s | Kubernetes API 程式設計利器:Operator 和 Operator FrameworkK8SAPI程式設計Framework
- 從零開始入門 K8s | K8s 安全之訪問控制K8S
- 從零開始入門 K8s | 理解 CNI 和 CNI 外掛K8S
- 從零開始入門 K8s | Kubernetes 儲存架構及外掛使用K8S架構
- 從零開始單排學設計模式「策略模式」黑鐵 II設計模式
- 從零開始單排學設計模式「裝飾模式」黑鐵 I設計模式
- 從零入門 Serverless | 一文詳解 Serverless 架構模式Server架構模式
- 從零開始入門 K8s | Kubernetes 網路模型進階K8S模型
- 從零開始入門 K8s | 有狀態應用編排 - StatefulSetK8S
- 從零開始入門 K8s | etcd 效能最佳化實踐K8S
- 從零開始入門 K8s | 手把手帶你理解 etcdK8S
- 從零開始單排學設計模式「簡單工廠設計模式」黑鐵 III設計模式
- 從零開始入門 K8s | GPU 管理和 Device Plugin 工作機制K8SGPUdevPlugin
- 從零開始入門 K8s | 可觀測性:監控與日誌K8S
- 從零開始入門 K8s | Kubernetes 網路概念及策略控制K8S
- 從零開始學機器學習——分類器詳解機器學習
- 詳解Java 容器(第②篇)——容器中的設計模式Java設計模式
- k8s入門之pod(四)K8S
- 詳解Java 容器(完結篇)——詳解容器的設計模式、List、Map、併發容器Java設計模式
- 從零開始單排學設計模式「UML類圖」定級賽設計模式
- PYTHON系列-從零開始的爬蟲入門指南Python爬蟲
- 從抽象類開始,詳解責任鏈模式抽象模式
- 《從零開始學Swift》學習筆記(Day 63)——Cocoa Touch設計模式及應用之單例模式Swift筆記設計模式單例
- Maven例項講解教程,從零開始學Maven,帶你快速入門!Maven
- 設計模式從放棄到入門設計模式
- 從零開始理解 Laravel 的設計哲學Laravel
- Photon物聯網程式設計從零開始程式設計
- 從零開始寫 Docker(十一)---實現 mydocker exec 進入容器內部Docker
- 黑客入門,從HTB開始黑客
- 瞭解新版Win10,從“入門”開始Win10
- 從零開始入門 K8s | 排程器的排程流程和演算法介紹K8S演算法