kube-apiserver — Data Flow

Central gateway: validates, stores, and serves all Kubernetes objects

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.
  1. 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
  2. NewAPIServerCommand() — parse flags, call Run()
    Registers all flags from ServerRunOptions. The RunE callback validates options then calls Run(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
  3. Run() — build config → server chain → serve
    The core startup sequence: NewConfigCompleteCreateServerChainPrepareRunRun.
    // 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.
  1. Authn Authentication
    Authenticator 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
  2. Authz Authorization
    Authorization 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
  3. Admission Mutating & Validating webhooks + built-in plugins
    Mutating 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/
  4. Store REST storage → etcd
    The 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