前言
文章字數一多,線上編輯不方便,本文是 k8s:kube-apiserver 啟動流程的第2部分
傳送門:k8s :kube-apiserver 啟動流程 – 1
回顧
上回講到 Run 方法:
// kubernetes/cmd/kube-apiserver/app.server.go
func 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 介面示例:
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 方法也有很多重複的(模版)程式碼,比如如果我們能夠封裝以下變化點:
- 生成後端儲存需要的key
- 序列化和反序列化物件
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 的實現