[关闭]
@dujun 2015-03-31T15:39:10.000000Z 字数 15547 阅读 9613

APIServer深入解读

Kubernetes写书


Kubernetes APIServer在Kubernetes的管理节点——master节点启动运行,对外提供Kubernetes API服务。在我们撰写这本书的时候,master节点只支持单个(原因见下文)。Kubernetes APIServer总体上由两个部分组成:http/https server和一些功能性插件。其中,这些插件又可以分成两类:一部分与底层IaaS平台(Cloud Provider)相关,代码详见:pkg/cloudprovider/{Cloud Providers};另一部分与资源的管理控制(Admission Control)相关,代码见:plugin/pkg/admission/{plugins}。与Cloud Provider相关的插件无非是调用IaaS的API完成对Kubernetes minion节点的操作(如果minion节点是这些IaaS提供的虚拟机的话)。而对于Admission Control插件,会有专门的篇幅来介绍,详见xxx。接下来我们将重点介绍Kubernetes APIServer的http/https server部分(为方便起见,下文凡是出现apiserver的地方都可以认为是APIServer的http/https server缩写)。

1. APIServer职能

APIserver作为Kubernetes集群的全局掌控者,主要职能如下所示。

(1)对外提供基于RESTful的管理接口,支持对Kubernetes的资源对象譬如:pod,service,replication controller,minion等进行增、删、改、查和监测(watch)操作。例如:

GET localhost:8080/api/v1beta1/pods 

表示查询默认namespace中所有pod的信息。

WATCH localhost:8080/api/v1beta1/watch/pods

表示监测默认namespace中所有pod的状态变化信息,返回pod的创建、更新和删除事件。

(2)配置和确保Kubernetes的资源对象一直处于用户期望的正确状态,并将这些资源对象的期望状态和当前实际存储在etcd中供Kubernetes其他组件读取和分析。

(3)与service配置数据同步pod信息(包括:所处位置、暴露的端口等),向Kubernetes service对象实例提供后端pod集的完整信息。

(4)提供丰富的功能性插件(支持用户自定义),完善对集群的管理。例如:调用内部/外部的用户认证与授权机制保证集群安全性,调用Admission Control插件对集群资源的使用进行管理控制,调用底层IaaS接口创建和管理Kubernetes工作节点等。

(5)UI(swagger)和Log支持。

2. APIServer启动过程

apiserver的启动程序见cmd/kube-apiserver/apiserver.gomain()函数,如下所示。

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    s := app.NewAPIServer()
    s.AddFlags(pflag.CommandLine)

    util.InitFlags()
    util.InitLogs()
    defer util.FlushLogs()

    verflag.PrintAndExitIfRequested()

    if err := s.Run(pflag.CommandLine.Args()); err != nil {
        fmt.Fprint(os.Stderr, err.Error)
        os.Exit(1)
    }
}

现逐行分析如下。

runtime.GOMAXPROCS(runtime.NumCPU())

熟悉Go语言的读者应该不会感到陌生,这是使用Go语言的特性来充分利用master节点的多核CPU,以达到增加apiserver的系统吞吐量的目的。

s := app.NewAPIServer()

新建一个apiserver对象。apiserver的数据结构定义如下所示。

type APIServer struct {
    WideOpenPort               int
    Address                    util.IP
    PublicAddressOverride      util.IP
    ReadOnlyPort               int
    APIRate                    float32
    APIBurst                   int
    SecurePort                 int
    TLSCertFile                string
    TLSPrivateKeyFile          string
    APIPrefix                  string
    StorageVersion             string
    CloudProvider              string
    CloudConfigFile            string
    EventTTL                   time.Duration
    TokenAuthFile              string
    AuthorizationMode          string
    AuthorizationPolicyFile    string
    AdmissionControl           string
    AdmissionControlConfigFile string
    EtcdServerList             util.StringList
    EtcdConfigFile             string
    CorsAllowedOriginList      util.StringList
    AllowPrivileged            bool
    PortalNet                  util.IPNet // TODO: make this a list
    EnableLogsSupport          bool
    MasterServiceNamespace     string
    RuntimeConfig              util.ConfigurationMap
    KubeletConfig              client.KubeletConfig
    ClusterName                string
}

各属性的含义如下表所示。

参数 含义 默认值 备注
WideOpenPort apiserver监听的端口 8080 该端口具有读写权限
Address apiserver绑定的网卡地址 127.0.0.1 0.0.0.0代表所有网卡地址
PublicAddressOverride 公有IP地址,也是只读端口和安全端口绑定的网卡地址 如果默认值为空,则选择第一个非回环网卡地址
ReadOnlyPort 只读端口 7080 该端口只有读权限
APIRate 只读端口的API请求速率限制 10.0 QPS
APIBurst 只读端口能够应对的最大流量(使用token bucket算法) 200 相当于token bucket的容量
SecurePort 安全端口 6443 0代表不启用https
TLSCertFile TLS证书文件 与https配合使用
TLSPrivateKeyFile TLS私钥文件 与https配合使用
APIPrefix API请求前缀 /api /
StorageVersion etcd版本 与EtcdServerList和EtcdConfigFile一起用于初始化etcd client
CloudProvider 底层IaaS提供商 如果为空,表示不需要IaaS支持
CloudConfigFile 底层IaaS配置文件路径 该字段非空的前提是CloudProvider字段非空
EventTTL 事件的存储保留时间 48小时 /
TokenAuthFile 存储token的文件 用于apiserver的token认证,与https配合使用
AuthorizationMode 授权模式 AlwaysAllow 用户可以自定义授权模式,默认是永远允许
AuthorizationPolicyFile csv格式的访问控制规则文件 仅当authorization_mode=ABAC时生效
AdmissionControl 以逗号作为分割符的Admission Control插件的排序列表 AlwaysAdmit 用户可以自定义授权模式,默认是永远允许
AdmissionControlConfigFile 启动admission control插件的配置文件 /
EtcdServerList etcd server列表,http://ip:port的形式,以逗号分隔 与EtcdConfigFile互斥,且两者至少有一个不为空
EtcdConfigFile etcd配置文件 与EtcdServerList互斥,且两者至少有一个不为空
CorsAllowedOriginList 允许进行跨域资源共享的源服务器列表 /
AllowPrivileged 是否特权优先级容器 false 布尔值
PortalNet 一个CIDR的IP段,用于分配service portal IP 如果默认值为空,则设置为10.0.0.0/24
MasterServiceNamespace 默认namespace default /
RuntimeConfig 运行时环境配置 一个map对象,譬如:{api/v1beta3 = true}
KubeletConfig Kubelet客户端配置信息 {Port:10250,EnableHttps: false} 一个map对象,无法自定义
ClusterName 集群实例前缀 kubernetes /

表x apiserver数据结构各属性含义

s.AddFlags(pflag.CommandLine)

接受用户命令行输入,自定义上表各参数值。

util.InitFlags()

解析并格式化用户传入的参数,最后填充APIServer结构体的各熟悉。

util.InitLogs()

初始化log配置,包括:log输出位置、log等级等。Kubernetes使用glog完成对系统运行过程中产生的各类事件和运行状态进行记录与输出。这里简单介绍下Kubernetes的log等级,每个log等级有各自约定俗成的含义和习惯用法,为方便用户/开发者阅读系统的log输出以及在Kubernetes代码开发时能够熟练运用glog,现列举如下。

log等级 习惯用法
glog.Errorf() 永远输出一个错误信息
glog.Warningf() 发生了意料之外的事情,但可能不是一个错误
glog.V(0).Infof() 输出一个对运维人员非常重要的错误信息,目前Kubernetes并未使用该等级的log输出
glog.V(1).Infof() 输出能够被纠正的错误信息,譬如检测到pod处于unhealthy状态
glog.V(2).Infof() 输重要的系统状态变化信息,譬如http request和他的返回码
glog.V(3).Infof() 额外的系统状态变化信息
glog.V(4).Infof() 详尽的debug信息

表x glog在Kubernetes的习惯用法

Kubernetes默认log等级是V(2),开发者可能需要得到更详尽的log输出,譬如V(3)或V(4),如果想要改变log等级,只需在APIServer(或其他组件)启动时传入-v=X参数,其中X是期望的最高log等级,1代表最简洁的log输出,4代表最详细的log输出。

defer util.FlushLogs()

上面这条代码保证了即使apiserver异常崩溃也能够将内存中的log信息保存到磁盘文件中。

verflag.PrintAndExitIfRequested()

如果用户只是想查看apiserver的版本号而不是启动apiserver,则打印apiserver的版本号并退出,即:

$ ./kube-apiserver --version
Kubernetes v0.12.0

最后的s.Run(pflag.CommandLine.Args())表示启动运行一个全新的apiserver。apiserver会启动一个nginx作为http/https服务器并监听以下三个端口对外提供Kubernetes API服务。

使用http访问,默认为8080端口,有读/写权限。该端口是一个非安全端口,没有用户认证和授权检查机制。如果需要自定义该端口,在启动时传入--WideOpenPort参数。该端口默认绑定到localhost(127.0.0.1),如果需要自定义绑定的网络接口地址,在启动时传入--address参数或者将其写入配置文件/etc/default/kube-apiserver(譬如:0.0.0.0表示绑定所有的网络接口地址)。但一般不推荐这样做,因为设计该端口的初衷是为Kubernetes的其他组件(例如:Scheduler和Controller-manager)的后台程序提供读/写API操作和用于测试,这也是目前Scheduler和Controller-manager需要和apiserver装在同一台机器上的原因之一。Kubernetes认为在生产环境下,防火墙是屏蔽外网直接访问该端口的,而且集群公有IP地址(PublicAddressOverride)的安全端口的流量将转发至该端口。

使用http访问,默认为7080端口,只有读权限,即只接受http GET请求。该端口是一个非安全端口,没有用户认证和授权检查机制。如果需要自定义该端口,在启动时传入--read_only_port参数。该端口绑定到集群公有IP地址上,集群公有IP地址(PublicAddressOverride)可以在apiserver启动时通过--public_address_override参数传入,但如果没有传入该参数,apiserver会使用其所在机器上的第一个非回环网卡地址作为该共有IP地址以保证集群的可用性。另外,该端口的API请求速率受到一定限制,默认为10/s。

使用https访问,默认为6443端口(如果是0表示不启用https),有读/写权限,同时支持x509安全证书和x509私钥认证。在apiserver启动时分别通过--tls_cert_file参数传入证书文件和--tls_private_key_file参数传入私有密钥文件。如果启用了https且apiserver在启动时未被提供以上参数,则apiserver会自动为该端口绑定的公有IP地址分别生成一个自注册的证书文件和密钥并将它们存储在/var/run/kubernetes目录下,分别为:/var/run/kubernetes/apiserver.crt/var/run/kubernetes/apiserver.key。如果需要自定义该端口,在启动时传入--secure_port参数。与只读端口类似,该端口绑定到集群公有IP地址(PublicAddressOverride)上,如果需要自定义绑定的网络接口地址,在apiserver启动时传入--public_address_override参数,否则apiserver会默认使用其所在机器上的第一个非回环网卡地址。未来,如果Scheduler和Controller-manager都使用安全端口,则它们就不必与apiserver运行在同一台机器上。APIServer使用基于token文件的认证机制和基于访问规则的授权机制来保障该端口的安全。

在前面对Kubernetes service概念的讨论中,我们知道Kubernetes使用用户的service概念对Kubernetes API服务本身进行了抽象和封装。这两个API服务即kuberneteskubernetes-ro,在apiserver的启动过程中,会创建并运行这两个service,代码如下所示。

package master

m.masterServices = util.NewRunner(m.serviceWriterLoop, m.roServiceWriterLoop)
m.masterServices.Start()

service kubernetes-roPortalNet的第一个IP和系统定义的只读端口(80端口,目前不支持通过传入参数来自定义)绑定,它接收来自集群只读端口的流量导入,提供Kubernetes只读API(GET操作)。用户访问service kubernetes-ro不需要进行认证与授权操作。而service kubernetes则与PortalNet的第二个IP和系统定义的安全端口(443端口,目前不支持通过传入参数来自定义)绑定,它接收来自集群安全端口的流量导入,提供Kubernetes完整API。用户访问service kubernetes需要通过认证与授权。

3. 资源对象的RESTful接口

Kubernetes使用etcd作为其后台存储解决方案,而apiserver则基于etcd实现了一套RESTful API用于操作存储在etcd中的Kubernetes对象实例。所有针对Kubernetes资源对象的操作都可以使用传统的REST模式,如下所示。

除了上面提到的通用增、删、改、查操作以外,apiserver还提供其他一些URL以支持额外的操作,譬如:

registry是apiserver内部访问存储层(etcd)的API client,提供对存储在etcd内部的Kubernetes对象的增、删、改、查(CRUD)和监测(watch)等操作。Kubernetes资源对象在etcd中的存储路径形如下面所示。

$ etcdctl ls /registry --recursive
...
/registry/pods
/registry/pods/default
/registry/pods/default/f54759d0-a51f-11e4-91b1-005056b43972

这些存储路径均在pkg/registry/etcd/etcd.go中被定义,如下所示。

package etcd

const (
    PodPath string = "/registry/pods"
    ControllerPath string = "/registry/controllers"
    ServicePath string = "/registry/services/specs"
    ServiceEndpointPath string = "/registry/services/endpoints"
    NodePath string = "/registry/minions"
)

以上{ResourceName}Path{Resource}在etcd中的存储路径。

registry中真正负责etcd存储的逻辑部分——即对Kubernetes资源对象的CRUD和watch操作的具体实现函数可以在pkg/registry/etcd/etcd.go中找到。例如:

package etcd

// GetPod根据podID返回一个指定的pod对象
func (r *Registry) GetPod(ctx api.Context, id string) (*api.Pod, error){
...
}

// CreatePod根据制定的资源文件创建一个pod实例
func (r *Registry) CreatePod(ctx api.Context, pod *api.Pod){
...
}
...

那以上功能函数由谁调用呢?答案是Registry对象。Registry的数据结构如下所示,其中包含了etcd的客户端。

package etcd

type Registry struct {
    tools.EtcdHelper
    pods pod.Registry
}

Registry作为一个统一的存储入口,实现了BindingRegistryControllerRegistryEndpointRegistryMinionRegistryPodRegistryServiceRegistry等接口。{Resource}Registry接口知道如何在etcd中存储和操作特定的{Resource}对象。以PodRegistry为例,该Registry定义的接口函数声明如下所示。

package pod

type Registry interface {
    // ListPods返回匹配标签选择器的pod列表
    ListPods(ctx api.Context, selector labels.Selector) (*api.PodList, error)
    // WatchPods监测pod实时的创建、更新、删除等信息
    WatchPods(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error)
    // GetPod根据podID返回一个指定的pod对象
    GetPod(ctx api.Context, podID string) (*api.Pod, error)
    // CreatePod根据制定的资源文件创建一个pod实例
    CreatePod(ctx api.Context, pod *api.Pod) error
    ...
}

我们知道,registry负责与etcd打交道,那从etcd中拿到的数据又给谁呢?答案是RESTStorage对象。RESTStorage是一个用于RESTful存储操作的通用接口,任意一种对apiserver暴露RESTful存储 API的Kubernetes资源对象都要实现该接口,该接口的定义如下所示。

package apiserver

type RESTStorage interface {
    New() runtime.Object
}

该接口声明的New()函数返回一个空的运行时的资源对象,该资源对象可以在请求数据被放入后用于资源的创建和更新操作。与Registry类似,不同的Kubernetes资源类型都要实现各自的RESTStorage接口,具体实现代码见pkg/registry/{ResourceName}/rest.go。现以pod为例,分析如下。

package pod

type REST struct {
    registry Registry
    podCache PodStatusGetter
}

pod.REST实现了RESTStorage接口。可以发现,pod.REST包含Registry对象,用于操作存储层的pod对象,而podCache对象则存储pod的当前状态。再看用于创建一个pod对象的Create函数,

package pod

func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
    pod := obj.(*api.Pod)
    if !api.ValidNamespace(ctx, &pod.ObjectMeta) {
        return nil, errors.NewConflict("pod", pod.Namespace, fmt.Errorf("Pod.Namespace does not match the provided context"))
    }
    api.FillObjectMetaSystemFields(ctx, &pod.ObjectMeta)
    if len(pod.Name) == 0 {
        pod.Name = string(pod.UID)
    }
    if errs := validation.ValidatePod(pod); len(errs) > 0 {
        return nil, errors.NewInvalid("pod", pod.Name, errs)
    }
    return apiserver.MakeAsync(func() (runtime.Object, error) {
        if err := rs.registry.CreatePod(ctx, pod); err != nil {
            return nil, err
        }
        return rs.registry.GetPod(ctx, pod.Name)
    }), nil
}

该函数接受两个参数:api.Context类型的ctx代表当前API请求的上下文,runtime.Object类型的obj代表运行时的Kubernetes对象。该函数返回一个apiserver.RESTResult类型的go channel,apiserver.RESTResult代表一个REST操作的结果,其数据结构定义如下所示。

package apiserver

type RESTResult struct {
    runtime.Object
    Created bool
}

如上所示,apiserver.RESTResult结构体包含两个字段:runtime.ObjectCreatedruntime.Object是一个运行时的Kubernetes对象,代表该操作的结果,如果该操作是Delete,则置为nil。布尔值Created表明该操作的结果是否已成功地填入runtime.Object对象,若是,则置为true。

我们可以通过研究Create函数一窥创建pod的主要过程。首先从obj中解析出相应的信息来创建一个运行时的pod对象,并根据API请求的上下文和该pod对象的元数据来验证两者的namespace是否匹配,如不匹配则创建pod失败。namespace验证匹配后,apiserver会向pod对象注入一些系统元数据,包括:创建时间和uuid等。如果定义pod时未提供pod的名字,则apiserver会将pod的uudid作为pod的名字。然后,apiserver会检查pod对象中一些必需字段是否为空,譬如:namenamespacespeclabels等,只要有一个字段为空,则抛出异常并终止创建过程。最后调用registry.CreatePod函数在etcd中持久化该pod对象。apiserver.MakeAsync函数负责将操作结果封装成RESTResult并填入作为返回值的go channel。

实现了所有对象的RESTStorage接口后,接下来就要向apiserver注册ResourceHandlerapiserver.InstallREST负责向一个restful容器(注意:这里的容器与docker容器是完全不同的概念,是与器皿相似的概念)注册一个REST handlers,包括:ResourceHandlerwatchHandlerproxyHandlerredirectHandleropHandler等,形如:

mux.Handle(prefix+"/watch/", http.StripPrefix(prefix+"/watch/", watchHandler))

即根据RESFful API的URL路径匹配相应的REST handler。

最后,根据系统支持的API版本,分别建立不同的API,以v1beta1为例,代码如下所示。

if err := apiserver.NewAPIGroupVersion(m.api_v1beta1()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta1"); err != nil {
    glog.Fatalf("Unable to setup API v1beta1: %v", err)
}

这样,当用户发起一个GET localhost:8080/api/v1beta1/pods的API请求时,就能访问本地的读写端口,按照v1beta1的API标准调用etcd客户端的ListPods接口查询默认namespace(default)中所有pod的信息。

4. API操作的原子性

所有Kubernetes资源对象都有一个resourceVersion作为其元数据(详见pkg/api/v1beta1/types.goTypeMeta结构体)的一部分,apiserver借此保证对资源对象操作的原子性。resourceVersion是用于标识一个资源对象的内部版本的一个字符串,客户端可以通过它来判断该对象是否被更新过。resourceVersion仅对当前资源对象和namespace有效,每次Kubernetes资源对象的更新都会导致apiserver修改其值。如果一个PUT操作包含了resourceVersion的值,那么apiserver就会通过验证当前resourceVersion的值与指定的值是否相匹配来确保在此次PUT操作的读/修改/写周期内没有任何其他对该资源的修改操作。可以将resourceVersion看成是apiserver用来排序用户请求的逻辑时钟。

客户端能够获取resourceVersion的值的唯一途径是向apiserver发起GET请求,apiserver查询检索到的资源对象的resourceVersion字段的值,最后通过http response返回。apiserver还支持HTTP PUT方法的幂等性,即如果一个HTTP请求头部包含If-Match: resourceVersion=或一个HTTP请求URL包含?resourceVersion=参数且当前对象存储的resourceVersion值与http请求包含的值不匹配,则apiserver会返回一个StatusConflict(409)的http状态码。这时,正确的做法是客户端重新发起GET请求并对该资源对象应用更新操作,最后再次提交。

resourceVersion机制能够防止以下代码块的竞争。

Client #1                                  
GET Foo                                    
Set Foo.Bar = "one"                        
PUT Foo          

Client #2
GET Foo
Set Foo.Baz = "two"
PUT Foo

当以上两个代码块并发执行且没有适当的锁机制保护时,Foo.BarFoo.Baz的修改都可能丢失。但是当为Foo对象指定resourceVersion时,其中一个PUT操作必定会失败,因为任意一个PUT操作的成功执行都会修改resourceVersion的值。

5. APIServer总体架构小结

最后,用下面这张APIServer的总体架构图作为上面讨论的一个小结。

图3
图1 APIServer总体架构

如上所示,APIServer运行在总控节点(master node)上,总控节点上的一个公有IP(public address,一般是总控节上的第一个非回环IP地址,姑且认为是集群的总入口)一般对外暴露三个端口提供API服务。在上图的例子中,从左到右分别是443端口、7080端口和6443端口。443端口是一个可选端口,用于保证APIServer非安全端口(本地端口)的安全性,在上面这个例子中,使用了nginx监听该443端口,并将客户端的API请求转发给APIServer的本地端口上。事实上,使用saltstack部署的基于GCE的Kubernetes集群默认安装了nginx作为客户端与APIServer非安全server(本地server)的流量代理。nginx开启https模式,客户端与nginx之间的通信使用相互认证的方式进行加密且nginx对客户端使用http basic auth的认证模式。如果不使用例如nginx这样的代理服务器,则客户端/Kubernetes其他组件只能登录到主控节点上使用入口地址localhost:8080访问本地端口,进而访问Kubernetes的读/写API。7080端口是个只读端口,APIServer的只读server侦听该端口,接收客户端的http请求。该请求会根据iptables规则被转发给kubernetes系统自定义的一个用于提供Kubernetes只读API的service,即:ro-service。kubernetes系统自定义的service与用户定义的service均处于Kubernetes自己管理的虚拟网络中,在上面这个例子中service所处的网段(portal net)即:10.0.0.0/24ro-service一般绑定在portal net中第一个可用的IP地址(在上面这个例子中,即:10.0.0.1)上,对外暴露80端口。7080端口是个只读端口,APIServer的只读server侦听该端口,接收客户端的http请求。该请求会根据iptables规则被转发给kubernetes系统自定义的一个用于提供Kubernetes只读API的service,即:ro-service。kubernetes系统自定义的service与用户定义的service均处于Kubernetes自己管理的虚拟网络中,在上面这个例子中service所处的网段(portal net)为10.0.0.0/24ro-service一般绑定在portal net中第一个可用的IP地址(在上面这个例子中,即:10.0.0.1)上,对外暴露80端口。6443端口是个Kubernetes自己维护的安全端口,APIServer的读/写server侦听该端口,接收客户端的https
请求。该请求会根据iptables规则被转发给kubernetes系统自定义的另一个用于提供Kubernetes读/写API的service,即:rw-service。与ro-service一样,rw-service处于Kubernetes自己管理的虚拟网络中且一般绑定在portal net中第二个可用的IP地址(在上面这个例子中,即:10.0.0.2)上,对外暴露443端口。6443端口和443端口虽然都支持https,但不同的是Kubernetes使用自定义的用户认证/授权插件(详情请参见后面章节)而非http basic auth来确保6443端口的访问安全。

用户的API请求通过用户授权/认证后,Kubernetes的资源管理插件Admission Control(详情请参见后面章节)会根据用户的API请求类型、用户请求上下文所处namespace和申请的资源数量等信息决定到底是通过还是驳回该API请求。如果用户API请求通过了Admission Control插件的验证后,将调用etcd的存储接口Registry对存储在etcd中的REST对象执行增、删、改、查(CRUD)和监测(watch)操作。

下面,以创建一个pod的工作流作为本文的结束。假设创建pod的这个API请求访问Kubernetes安全端口且对应的HTTP请求如下所示。

HTTP方法:POST
URL:{public_address}:6443/api/v1beta1/pods

那么,APIServer的处理工作流可以分为以下几个步骤:
1)Kubernetes调用用户认证模块对用户的身份进行认证,再根据用户HTTP请求的操作类型和发起请求所处上下文的namespace等信息结合Kubernetes预先定义的授权规则判断该用户是否有权限执行创建该pod的操作。
2)在6443端口监听连接信息的APIServer的安全server将用户的HTTP请求转发给rw-service
3)rw-service将该HTTP请求的请求方法和URL映射成对应的RESTful API,即:create pod,并调用Admission Control插件对该API请求所需资源的合法性进行检验。如果Admission Control通过该API请求对资源的需求,则执行步骤4,否则返回调用者一个错误信息并结束处理流程。
4)根据API请求中pod的配置信息,调用pod的Registry存储接口在etcd中创建一个pod实例。

调度pod全过程(与对象标识符相关,只是其中一部分)

有很多种途径将Pod调度到一个特定的工作节点上,下面举两个例子。

更多见这里

[注]

HealthCheck要写一写。文档见这里

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注