Entry Point & Startup
main() → NewAPIServerCommand() → Run()
The binary entry point wires a Cobra command. When the
command executes, it completes options, builds a config,
constructs the server chain, and starts serving.
-
main()Creates and runs the Cobra command. Signal context is wired at this level.
// cmd/kube-apiserver/apiserver.go L32-36 func main() { command := app.NewAPIServerCommand() code := cli.Run(command) os.Exit(code) }cmd/kube-apiserver/apiserver.go L32–36 -
NewAPIServerCommand() — parse flags, call Run()Registers all flags from
ServerRunOptions. TheRunEcallback validates options then callsRun(ctx, completedOptions).// cmd/kube-apiserver/app/server.go L70-145 func NewAPIServerCommand() *cobra.Command { s := options.NewServerRunOptions() // ... RunE: func(cmd *cobra.Command, args []string) error { completedOptions, err := s.Complete(ctx) // validate options... return Run(ctx, completedOptions) // L117 }, }server.go L70–145 -
Run() — build config → server chain → serveThe core startup sequence:
NewConfig→Complete→CreateServerChain→PrepareRun→Run.// cmd/kube-apiserver/app/server.go L148-173 func Run(ctx context.Context, opts options.CompletedOptions) error { config, err := NewConfig(opts) // builds all sub-configs completed, err := config.Complete() server, err := CreateServerChain(completed) // L162 prepared, err := server.PrepareRun() return prepared.Run(ctx) // starts HTTP(S) listeners }server.go L148–173
Server Chain
CreateServerChain() — three-layer delegation
The API server is actually
three nested servers that delegate
unknown requests down the chain. This allows CRDs
(apiextensions), built-in APIs (kube), and aggregated
APIs to coexist.
Aggregator (kube-aggregator) — handles
aggregated APIs & discovery
↓ delegates
KubeAPIServer — built-in resources (Pod,
Service, Node, …)
↓ delegates
APIExtensionsServer — CustomResourceDefinitions
// cmd/kube-apiserver/app/server.go L176-197
func CreateServerChain(config CompletedConfig) (*aggregatorapiserver.APIAggregator, error) {
apiExtensionsServer, err := config.ApiExtensions.New(
genericapiserver.NewEmptyDelegateWithCustomHandler(notFoundHandler))
kubeAPIServer, err := config.KubeAPIs.New(
apiExtensionsServer.GenericAPIServer) // delegates to CRD server
aggregatorServer, err := controlplaneapiserver.CreateAggregatorServer(
config.Aggregator,
kubeAPIServer.ControlPlane.GenericAPIServer, // delegates to kube server
...)
return aggregatorServer, nil
}
server.go L176–197
InstallAPIs() — registering REST storage per resource group
Each API group (core, apps, batch, …) exposes a
RESTStorageProvider.
InstallAPIs calls
NewRESTStorage() on each provider to wire
the HTTP routes to etcd-backed storage.
// pkg/controlplane/apiserver/apis.go L88-130 (approx)
func (c *Config) InstallAPIs(s *genericapiserver.GenericAPIServer) error {
restStorageProviders := []RESTStorageProvider{
appsrest.StorageProvider{},
batchrest.StorageProvider{},
corerest.LegacyRESTStorageProvider{...},
// ... many more groups
}
for _, provider := range restStorageProviders {
groupInfo, err := provider.NewRESTStorage(apiResourceConfigSource, restOptionsGetter)
s.InstallAPIGroups(&groupInfo)
}
}
pkg/controlplane/apiserver/apis.go
pkg/registry/core/rest/storage_core.go
Request Handling Pipeline
Request filter chain — authn → authz → admission → storage
Every incoming HTTP request passes through a layered
middleware chain before reaching the REST handler.
Filters are composed in
k8s.io/apiserver/pkg/server.
-
Authn AuthenticationAuthenticator chain tries: client certificates, bearer tokens (ServiceAccount JWTs), OIDC tokens, webhook token review. First success wins. On failure → 401.
// pkg/kubeapiserver/authenticator/config.go // Builds an authenticator.Request that chains: // - x509.Authenticator (client cert) // - bearertoken.Authenticator (SA tokens, OIDC) // - webhook.Authenticator (TokenReview webhook) func (config Config) New() (authenticator.Request, ...) { ... }pkg/kubeapiserver/authenticator/config.go -
Authz AuthorizationAuthorization chain (RBAC, Node, webhook) checks whether the identified user may perform the requested verb on the resource. On failure → 403.
// pkg/kubeapiserver/authorizer/config.go // Chains: NodeAuthorizer, RBACAuthorizer, WebhookAuthorizer func (config Config) New(clusterRoleRegistry ...) (authorizer.Authorizer, ...) { ... }pkg/kubeapiserver/authorizer/config.go -
Admission Mutating & Validating webhooks + built-in pluginsMutating plugins run first (may change the object), then validating plugins. Built-in plugins include LimitRanger, ResourceQuota, ServiceAccount, PodSecurity, NamespaceLifecycle.
// Built-in admission plugins are registered in: // plugin/pkg/admission/ // pkg/admission/plugin/ // Example: namespace lifecycle // pkg/admission/plugin/namespace/lifecycle/admission.go func (l *Lifecycle) Admit(ctx context.Context, a admission.Attributes, ...) error { // reject requests to non-existent or terminating namespaces }pkg/admission/plugin/namespace/lifecycle/admission.go plugin/pkg/admission/ -
Store REST storage → etcdThe validated object is encoded (JSON/protobuf) and written to etcd via the generic registry. On success the API server returns 201/200 to the client.
// pkg/registry/core/pod/storage/storage.go // REST embeds genericregistry.Store which uses // etcd3.store as the backend. type REST struct { *genericregistry.Store proxyTransport http.RoundTripper } func NewStorage(optsGetter generic.RESTOptionsGetter, ...) (*REST, ...) { store := &genericregistry.Store{ NewFunc: func() runtime.Object { return &api.Pod{} }, // ... } options := &generic.StoreOptions{RESTOptions: optsGetter} store.CompleteWithOptions(options) // wires etcd backend }pkg/registry/core/pod/storage/storage.go pkg/registry/core/pod/strategy.go
Watch & Notification
Watch — how controllers and schedulers get notified
The API server maintains an in-memory
watch cache. On
GET /...?watch=true it streams
ADDED / MODIFIED / DELETED events to
watchers. Controllers use
client-go informers which multiplex a
single watch stream.
// Every resource type supports Watch via genericregistry.Store:
//
// GET /api/v1/pods?watch=true&resourceVersion=12345
//
// The API server sends chunked HTTP/2 frames:
//
// {"type":"ADDED","object":{"kind":"Pod","metadata":{...}}}
// {"type":"MODIFIED","object":{"kind":"Pod","metadata":{...}}}
// {"type":"DELETED","object":{"kind":"Pod","metadata":{...}}}
//
// client-go Informer (SharedIndexInformer) multiplexes this single
// watch stream to all controllers that registered event handlers.
staging/src/k8s.io/client-go/tools/cache/shared_informer.go