這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
前言
文章字數一多,線上編輯不方便,本文是 k8s:kube-apiserver 啟動流程的第2部分
傳送門:k8s :kube-apiserver 啟動流程 - 1
回顧
上回講到 Run 方法:
// kubernetes/cmd/kube-apiserver/app.server.gofunc Run(runOptions *options.ServerRunOptions, stopCh <-chan struct{}) error { ... server, err := CreateServerChain(runOptions, stopCh) if err != nil { return err } return server.PrepareRun().Run(stopCh)}
目前系統中有以下 api server:
- CustomResourceDefinitions
- Master
- APIAggregator
每個 api server 都對應一個 Config(配置)
- apiextensionsapiserver.Config
- master.Config
- aggregatorapiserver.Config
CreateServerChain 的任務就是根據 ServerRunOptions 建立 XXXConfig,然後再用 XXXConfig 建立 api server,各個 api server 通過 GenericAPIServer 的 delegationTarget 欄位組成《責任鏈》
以 Master api server 建立為例:
func CreateServerChain(runOptions *options.ServerRunOptions, stopCh <-chan struct{})(*genericapiserver.GenericAPIServer, error) { ... kubeAPIServerConfig, ... := CreateKubeAPIServerConfig(...) ... kubeAPIServer, err := CreateKubeAPIServer(kubeAPIServerConfig, apiExtensionsServer.GenericAPIServer, sharedInformers, versionedInformers) ...}
下面將簡要介紹 Master api server 的建立過程,主要分析 kube-apiserver 是如何將 資來源物件(Node,Pod,Service 等)綁定到具體的 RESTful API,使得用戶端可以通過 RESTful API 操作資來源物件
如果是你會怎麼做?
在大概看了一些原始碼之後,我不禁問自己:如果是你來設計代碼架構,你會怎麼做?
例如給定一個實體 Student(Java 虛擬碼,下同),持久化在 etcd 裡
public class Student { public int id; public String name; public String phone;}
如何提供 RESTful api 介面提供對 Student 的 CRUD 操作? 設計代碼架構使之適應所有的實體
api 介面樣本:
增加(假定參數都通過 url 傳遞)
PUT /user?id=xxx&name=yyy&phone=zzz
刪除
DELETE /user?id=xxx
修改
POST /user?id=xxx&name=yyy
查詢
GET /user?id=xxx
我們分幾步來考慮,首先考慮持久化,為了支援不同的持久化架構,或者即時我們就使用一種持久化架構也需要考慮架構版本匹配問題,這就需要將對持久化架構的基本操作進行抽象,抽取出介面 Backend
public interface Backend { String get(String key); void set(String key, String value);}
然後我們有具體的實作類別 EtcdBackend, ConsulBackend 以及 工廠類 BackendFactory
public class EtcdBackend implements Backend { public String get(String key) { ... } public void set(String key, String value) { ... }}public class ConsulBackend implements Backend { public String get(String key) { ... } public void set(String key, String value) { ... }}public class BackendFactory { Backend get(String name) { ... }}
Backend 搞定了,現在我們需要一個 DAO(Data access object)來訪問它
public class UserDao { private Backend backend; // CRUD 方法 ...}
我們注意到會有很多實體,他們都需要使用 Backend 介面訪問後端儲存,所以可以搞個基類 AbstractDao,將 backedn 欄位移到基類裡頭
pubic class AbstractDao { private Backend backend;}public class User extends AbstractDao { // CRUD 方法 ...}
進一步觀察,其實各個 DAO 的 CRUD 方法也有很多重複的(模版)代碼,比如如果我們能夠封裝以下變化點:
DAO 中的 CRUD 方法可以進一步抽取到 AbstractDao 中,那些實在需要子類特例化的方法,可以通過《模版方法》模式來實現
public class AbstractDao { private Backend backend; // CRUD 方法 ...}public class UserDao extends AbstractDao { // Template 方法 ...}
我們現在離最後的完工又近了一步,還剩一個問題,就是如何將 url 和 DAO 對應起來,這是一個映射問題,可以使用 map 來保持 url 對應的 DAO
map.put("/user", userDao)
以上只是一個簡單的推導,k8s 的時候比這個 demo 複雜的多,考慮到各種解耦和擴充性,下回將正式介紹 k8s 的實現