【K8s】api-server 源码分析 01-01

2022-06-16 22:17:08 浏览数 (1)

【注】源码分析均以 k8s 的第一个 commit 代码分析;

代码语言:go复制
cmd/apiserver/apiserver.go

api-server 的入口代码(main)函数的位置;

在 apiserver.go 中,定义 apiserver 启动的相关参数:

  • port/address: IP:Port, apiserver 启动监听的端口;
  • apiPrefix: 访问 api-server 的 URL 前缀
  • etcdServerList: etcd 存储节点列表
  • machineList:工作节点的列表
代码语言:go复制
	var (
		taskRegistry       registry.TaskRegistry
		controllerRegistry registry.ControllerRegistry
		serviceRegistry    registry.ServiceRegistry
	)

主要是三个接口:

  • TaskRegistry
  • ControllerRegistry
  • ServiceRegistry

对于设置 etcd 存储节点的的 api-server 的启动参数,需要实例化 etcd 的客户端,用于访问 etcd 节点;

对于有 etcd 节点列表的实例化:

代码语言:go复制
etcdClient := etcd.NewClient(etcdServerList)
taskRegistry = registry.MakeEtcdRegistry(etcdClient, machineList)
controllerRegistry = registry.MakeEtcdRegistry(etcdClient, machineList)
serviceRegistry = registry.MakeEtcdRegistry(etcdClient, machineList)

使用 etcd 存储节点的,可以进行数据持久化;

对于没有 etcd 节点列表的实例化:

代码语言:go复制
taskRegistry = registry.MakeMemoryRegistry()
controllerRegistry = registry.MakeMemoryRegistry()
serviceRegistry = registry.MakeMemoryRegistry()

没有 etcd 存储节点,使用内存保存信息,不能持久化;

taskRegistry 的实例化:

代码语言:go复制
pkg/registry/etcd_registry.go

func MakeEtcdRegistry(client EtcdClient, machines []string) *EtcdRegistry {}

对于 EtcdRegistry 结构体:

代码语言:go复制
// EtcdRegistry is an implementation of both ControllerRegistry and TaskRegistry which is backed with etcd.
type EtcdRegistry struct {
	etcdClient      EtcdClient
	machines        []string
	manifestFactory ManifestFactory
}

这个结构体中有三个成员变量:

  • etcdClient:访问 etcd 的客户端
  • machines: 工作节点的 IP 列表
  • manifestFactory:ManifestFactory 接口
代码语言:go复制
MakeManifest(machine string, task Task) (ContainerManifest, error)

这个接口只有 MakeManifest 方法,主要是用于根据机器和任务信息,生成容器信息;

对于 manifestFactory 接口的实例化:

代码语言:go复制
registry.manifestFactory = &BasicManifestFactory{
	serviceRegistry: registry,
}

通过一个基本的 BasicManifestFactory 结构体实例化:

代码语言:go复制
type BasicManifestFactory struct {
	serviceRegistry ServiceRegistry
}

在这个结构体中,是一个 ServiceRegistry 的接口类型;

在 MakeEtcdRegistry 函数中,用的是 EtcdRegistry 结构的实例进行实现;

EtcdRegistry 结构同样实现了 ServiceRegistry 的接口;


那再分析下 ServiceRegistry 接口:

代码语言:go复制
type ServiceRegistry interface {
	ListServices() (ServiceList, error)
	CreateService(svc Service) error
	GetService(name string) (*Service, error)
	DeleteService(name string) error
	UpdateService(svc Service) error
	UpdateEndpoints(e Endpoints) error
}

总共有六个方法,对于 EtcdRegistry 结构,需要实现这六个方法:

在 pkg/registry/service_registry.go 中:

代码语言:go复制
type ServiceRegistryStorage struct {
	registry ServiceRegistry
}

这个 ServiceRegistryStorage 结构体中同样有 ServiceRegistry 接口的成员变量;

这个结构体实现 apiserver.RESTStorage 接口;

代码语言:go复制
func MakeServiceRegistryStorage(registry ServiceRegistry) apiserver.RESTStorage {}

再回过头来到 main() 函数中:

  • taskRegistry
  • controllerRegistry
  • serviceRegistry 这个三个都是 EtcdRegistry 结构的实例;

container 的信息获取是通过:

代码语言:go复制
	containerInfo := &kube_client.HTTPContainerInfo{
		Client: http.DefaultClient,
		Port:   10250,
	}

这个地方只是保存了 http 的客户端和需要访问的端口信息;

代码语言:go复制
	storage := map[string]apiserver.RESTStorage{
		"tasks":                  registry.MakeTaskRegistryStorage(taskRegistry, containerInfo, registry.MakeFirstFitScheduler(machineList, taskRegistry)),
		"replicationControllers": registry.MakeControllerRegistryStorage(controllerRegistry),
		"services":               registry.MakeServiceRegistryStorage(serviceRegistry),
	}

这里 storage 是通过 map 的方式进行存储, key 是支持的三个 Registry 的名字,后续可以通过名字找到对应的 RESTStorage 接口;

  • MakeTaskRegistryStorage()
  • MakeControllerRegistryStorage()
  • MakeServiceRegistryStorage()

这个三个函数分别生成对应的 apiserver.RESTStorage 接口的实现;

代码语言:go复制
	endpoints := registry.MakeEndpointController(serviceRegistry, taskRegistry)
	go util.Forever(func() { endpoints.SyncServiceEndpoints() }, time.Second*10)

对于 EndpointController 结构:

代码语言:go复制
type EndpointController struct {
	serviceRegistry ServiceRegistry
	taskRegistry    TaskRegistry
}

这个结构体中,serviceRegistry 和 taskRegistry 接口参数;

  • SyncServiceEndpoints(): 函数主要的功能是:每 10s 同步一次 Endpoint 列表信息;
代码语言:go复制
	s := &http.Server{
		Addr:           fmt.Sprintf("%s:%d", *address, *port),
		Handler:        apiserver.New(storage, *apiPrefix),
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	log.Fatal(s.ListenAndServe())

最后启动 http 的服务;

第一个 commit 的 api-server 代码不是很复杂,跟着代码的思路,基本都可以看懂,这里主要是接口的抽象和实例化,可以通过流程图进行分析;

(后续有机会可以出视频教程,这样讲解起来比较清楚)

0 人点赞