<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          kube-apiserver 初始命名空間實(shí)現(xiàn)方式

          共 12078字,需瀏覽 25分鐘

           ·

          2023-08-19 09:29

          在我們第 1 回僅啟動(dòng)一個(gè) kube-apiserver 組件的情況下,Kubernetes 就創(chuàng)建了四個(gè)初始命名空間:

          • default:默認(rèn)命名空間
          • kube-node-lease:用于保存與各個(gè)節(jié)點(diǎn)關(guān)聯(lián)的 Lease(租約)對(duì)象
          • kube-public:所有(包括未經(jīng)身份驗(yàn)證)的客戶端都可以讀取,主要用于集群內(nèi)部共享資源
          • kube-system:用于 Kubernetes 系統(tǒng)創(chuàng)建資源,包括 Kubernetes 運(yùn)行所需的核心組件
          $ kubectl get ns
          NAME                 STATUS   AGE
          default              Active   2d22h
          kube-node-lease      Active   2d22h
          kube-public          Active   2d22h
          kube-system          Active   2d22h

          如果刪除這四個(gè)初始命名空間:

          $ kubectl delete ns default kube-node-lease kube-public kube-system
          namespace "kube-node-lease" deleted
          Error from server (Forbidden): namespaces "default" is forbidden: this namespace may not be deleted
          Error from server (Forbidden): namespaces "kube-public" is forbidden: this namespace may not be deleted
          Error from server (Forbidden): namespaces "kube-system" is forbidden: this namespace may not be deleted

          會(huì)發(fā)現(xiàn)只有 kube-node-lease 允許刪除,而且經(jīng)過(guò)觀察,刪除后 1 分鐘內(nèi),該命名空間也會(huì)自動(dòng)重新創(chuàng)建:

          $ kubectl get ns
          NAME                 STATUS   AGE
          default              Active   2d22h
          kube-node-lease      Active   10s
          kube-public          Active   2d22h
          kube-system          Active   2d22h

          這四個(gè)初始命名空間的維護(hù)工作,實(shí)際是由 GenericAPIServer 的 Hook 機(jī)制完成的。

          在第 5 回啟動(dòng) HTTP Server 的 NonBlockingRun 方法中:

          // k8s.io/apiserver/pkg/server/genericapiserver.go
          func (s preparedGenericAPIServer) NonBlockingRun(stopCh <-chan struct{}, shutdownTimeout time.Duration) (<-chan struct{}, <-chan struct{}, error) {
           // ...
           if s.SecureServingInfo != nil && s.Handler != nil {
            // 啟動(dòng) HTTP Server
            stoppedCh, listenerStoppedCh, err = s.SecureServingInfo.Serve(s.Handler, shutdownTimeout, internalStopCh)
            // ...
           }

            // HTTP Server 退出通知
           go func() {
            <-stopCh
            close(internalStopCh)
           }()

           // 運(yùn)行 Hook
           s.RunPostStartHooks(stopCh)
           // ...
          }

          當(dāng) HTTP Server 啟動(dòng)成功后,就會(huì)開(kāi)始運(yùn)行所有的 Hook :

          // k8s.io/apiserver/pkg/server/hooks.go

          type PostStartHookFunc func(context PostStartHookContext) error

          // Hook 實(shí)體
          type postStartHookEntry struct {
           // hook 方法
           hook PostStartHookFunc
            // hook 方法的調(diào)用棧信息
           originatingStack string
           done chan struct{}
          }

          func (s *GenericAPIServer) RunPostStartHooks(stopCh <-chan struct{}) {
           s.postStartHookLock.Lock()
           defer s.postStartHookLock.Unlock()
           s.postStartHooksCalled = true

           context := PostStartHookContext{
            LoopbackClientConfig: s.LoopbackClientConfig,
            StopCh:               stopCh,
           }

           // postStartHooks 是一個(gè)保存所有 Hook 的 map[string]postStartHookEntry
            // 遍歷所有 Hook 并運(yùn)行其 hook 方法
           for hookName, hookEntry := range s.postStartHooks {
            go runPostStartHook(hookName, hookEntry, context)
           }
          }

          func runPostStartHook(name string, entry postStartHookEntry, context PostStartHookContext) {
           var err error
           func() {
            // don't let the hook *accidentally* panic and kill the server
            defer utilruntime.HandleCrash()
            // 運(yùn)行其 hook 方法
            err = entry.hook(context)
           }()
           // if the hook intentionally wants to kill server, let it.
           if err != nil {
            klog.Fatalf("PostStartHook %q failed: %v", name, err)
           }
           close(entry.done)
          }

          s.postStartHooks 位置斷點(diǎn)調(diào)試,可以看到當(dāng)前版本一共有 24 種不同功能的 Hook :

          其中 start-system-namespaces-controller 便是本文的主角。

          如果從 debug 的角度去找,可以看其對(duì)應(yīng)的 hook 的調(diào)用棧信息定位到該 Hook 的創(chuàng)建位置:

          或者繼續(xù)跟隨我的思路,來(lái)到第 3 回注冊(cè) /api/* 核心路由的 InstallLegacyAPI 方法:

          // pkg/controlplane/instance.go
          func (m *Instance) InstallLegacyAPI(c *completedConfig, restOptionsGetter generic.RESTOptionsGetter) error {
           // 創(chuàng)建 RESTStorage
           legacyRESTStorage, apiGroupInfo, err := legacyRESTStorageProvider.NewLegacyRESTStorage(c.ExtraConfig.APIResourceConfigSource, restOptionsGetter)


           controllerName := "bootstrap-controller"
           client := kubernetes.NewForConfigOrDie(c.GenericConfig.LoopbackClientConfig)
           // Kubernetes clusters contains the following system namespaces:
           // kube-system, kube-node-lease, kube-public, default
           // 注冊(cè) start-system-namespaces-controller Hook
           m.GenericAPIServer.AddPostStartHookOrDie("start-system-namespaces-controller"func(hookContext genericapiserver.PostStartHookContext) error {
            go systemnamespaces.NewController(client, c.ExtraConfig.VersionedInformers.Core().V1().Namespaces()).Run(hookContext.StopCh)
            return nil
           })

           bootstrapController, err := c.NewBootstrapController(legacyRESTStorage, client)
           if err != nil {
            return fmt.Errorf("error creating bootstrap controller: %v", err)
           }
           m.GenericAPIServer.AddPostStartHookOrDie(controllerName, bootstrapController.PostStartHook)
           m.GenericAPIServer.AddPreShutdownHookOrDie(controllerName, bootstrapController.PreShutdownHook)

           // 核心路由注冊(cè)
           if err := m.GenericAPIServer.InstallLegacyAPIGroup(genericapiserver.DefaultLegacyAPIPrefix, &apiGroupInfo); err != nil {
            return fmt.Errorf("error in registering group versions: %v", err)
           }
           return nil
          }

          在 RESTStorage 創(chuàng)建之后,核心路由注冊(cè)之前,注冊(cè)了兩個(gè) Hook :bootstrap-controllerstart-system-namespaces-controller 。

          Hook 注冊(cè)的 AddPostStartHookOrDie 方法實(shí)際就是將 Hook 添加到 postStartHooks 中:

          // k8s.io/apiserver/pkg/server/hooks.go
          func (s *GenericAPIServer) AddPostStartHookOrDie(name string, hook PostStartHookFunc) {
           if err := s.AddPostStartHook(name, hook); err != nil {
            klog.Fatalf("Error registering PostStartHook %q: %v", name, err)
           }
          }

          func (s *GenericAPIServer) AddPostStartHook(name string, hook PostStartHookFunc) error {
           // ...
            // 添加到 postStartHooks map 中
           s.postStartHooks[name] = postStartHookEntry{hook: hook, originatingStack: string(debug.Stack()), done: done}

           return nil
          }

          因?yàn)?Hook 運(yùn)行時(shí)實(shí)際是直接調(diào)用 hook 方法,所以重點(diǎn)還是各個(gè) Hook 注冊(cè)時(shí)自身所傳入的 hook PostStartHookFunc 參數(shù)。

          對(duì)于 start-system-namespaces-controller Hook 注冊(cè)的 PostStartHookFunc 方法:

          func(hookContext genericapiserver.PostStartHookContext) error {
            // 創(chuàng)建 start-system-namespaces-controller 并調(diào)用 Run 運(yùn)行
            go systemnamespaces.NewController(client, c.ExtraConfig.VersionedInformers.Core().V1().Namespaces()).Run(hookContext.StopCh)
            return nil
          }

          該 controller 的實(shí)現(xiàn)很簡(jiǎn)單,我直接全部貼出來(lái):

          // pkg/controlplane/controller/systemnamespaces/system_namespaces_controller.go

          // 創(chuàng)建 start-system-namespaces-controller
          func NewController(clientset kubernetes.Interface, namespaceInformer coreinformers.NamespaceInformer) *Controller {
           // 需要維護(hù)的四個(gè)初始命名空間
           systemNamespaces := []string{metav1.NamespaceSystem, metav1.NamespacePublic, v1.NamespaceNodeLease, metav1.NamespaceDefault}
           // 定時(shí)周期為 1 分鐘
           interval := 1 * time.Minute

           return &Controller{
            client:           clientset,
            namespaceLister:  namespaceInformer.Lister(),
            namespaceSynced:  namespaceInformer.Informer().HasSynced,
            systemNamespaces: systemNamespaces,
            interval:         interval,
           }
          }

          // 運(yùn)行 start-system-namespaces-controller
          func (c *Controller) Run(stopCh <-chan struct{}) {
           defer utilruntime.HandleCrash()
           defer klog.Infof("Shutting down system namespaces controller")

           klog.Infof("Starting system namespaces controller")

           if !cache.WaitForCacheSync(stopCh, c.namespaceSynced) {
            utilruntime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
            return
           }

           // 定時(shí)執(zhí)行 c.sync 方法,定時(shí)周期為 1 分鐘
           go wait.Until(c.sync, c.interval, stopCh)

           <-stopCh
          }

          func (c *Controller) sync() {
           // Loop the system namespace list, and create them if they do not exist
            // 遍歷四個(gè)初始命名空間
           for _, ns := range c.systemNamespaces {
            // 如果需要?jiǎng)t創(chuàng)建
            if err := c.createNamespaceIfNeeded(ns); err != nil {
             utilruntime.HandleError(fmt.Errorf("unable to create required kubernetes system Namespace %s: %v", ns, err))
            }
           }
          }

          func (c *Controller) createNamespaceIfNeeded(ns string) error {
           if _, err := c.namespaceLister.Get(ns); err == nil {
            // the namespace already exists
              // 命名空間已存在
            return nil
           }
           // 命名空間不存在,就去創(chuàng)建命名空間
           newNs := &v1.Namespace{
            ObjectMeta: metav1.ObjectMeta{
             Name:      ns,
             Namespace: "",
            },
           }
           _, err := c.client.CoreV1().Namespaces().Create(context.TODO(), newNs, metav1.CreateOptions{})
           if err != nil && errors.IsAlreadyExists(err) {
            err = nil
           }
           return err
          }

          這就是為什么啟動(dòng)一個(gè) kube-apiserver 組件就創(chuàng)建了四個(gè)初始命名空間,并且 kube-node-lease 命名空間刪除后 1 分鐘內(nèi)會(huì)自動(dòng)重新創(chuàng)建的原因。

          對(duì)于其它 23 種 Hook ,感興趣也可以直接全局搜索 Hook name 快速定位到其注冊(cè)位置去看。


          瀏覽 1392
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  三级在线观看网站 | 国产成人黄色在线观看 | 水蜜桃网站在线观看 | 黄色片免费视频网站 | 中国a一片一级一片 |