k8s :kube-apiserver 啟動流程 – 2

xingpingz發表於2019-02-16

前言

文章字數一多,線上編輯不方便,本文是 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 的實現

相關文章