zoukankan      html  css  js  c++  java
  • kubelet分析

    kubelet是k8s中节点上运行的管理工具,它负责接受api-server发送的调度请求,在Node上创建管理pod,并且向api-server同步节点的状态.这篇文章主要讲讲kubelet组件如何使用docker的.

    一.kubecontainer.Runtime

    type Kubelet struct {
    //....
    containerRuntime kubecontainer.Runtime
    //...
    }
    type Runtime interface {
    //...
        // Type returns the type of the container runtime.
        Type() string
        // Version returns the version information of the container runtime.
        Version() (Version, error)
    // Syncs the running pod into the desired pod.
        SyncPod(pod *v1.Pod, apiPodStatus v1.PodStatus, podStatus *PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) PodSyncResult
        KillPod(pod *v1.Pod, runningPod Pod, gracePeriodOverride *int64) error
        GetPodStatus(uid types.UID, name, namespace string) (*PodStatus, error)
    //...
    }

    在类Kubelet中定义了很多的组件和对象,其中containerRuntime就是负责和容器Runtime进行交互的,这里的容器Runtime可以理解为就是docker.我们可以看到, 这个containerRuntime是一个接口类型,这个接口中主要定义了一系列直接操作Pod的函数和获取Runtime信息的函数.其中最关键的函数是SyncPod, 它接受一个Pod的配置信息,启动一次同步,决定启动/更新/删除这个Pod. 目前Runtime接口只有一个实现类,kubeGenericRuntimeManager.

    二.kubeGenericRuntimeManager

    这个类是kubecontainer.Runtime的实现类,如上述所说,这个类对外暴露的接口是直接对Pod进行管理的.也就是说,这个类对使用者屏蔽了container的概念.对于一个创建Pod的请求,SyncPod函数负责进行处理.这个函数会首先创建一个sandbox,然后依次创建init containers和normal containers. 这里sandbox是用来提供pod层级的namespace隔离,具体细节不在本文的讨论范围之内.kubeGenericRuntimeManager中负责创建container的成员变量是runtimeService.

    type kubeGenericRuntimeManager struct {
    //...
        // gRPC service clients
        runtimeService internalapi.RuntimeService
    //...
    }

    三.internalapi.RuntimeService

    首先,我们观察一下这个接口的定义,这个接口聚合了好几个其它接口,每一个接口定义了一组操作.runtimeService的实例化函数是getRuntimeAndImageServices().这个函数做的事情稍有点复杂.在解析这个函数之前,首先解析下面这个代码片段,为了便于阅读,只列出了核心代码.

        switch containerRuntime {
        case kubetypes.DockerContainerRuntime:
    //sentence 1
            ds, err := dockershim.NewDockerService(kubeDeps.DockerClientConfig, crOptions.PodSandboxImage, streamingConfig,
                &pluginSettings, runtimeCgroups, kubeCfg.CgroupDriver, crOptions.DockershimRootDirectory, !crOptions.RedirectContainerStreaming)
    //sentence 2
            server := dockerremote.NewDockerServer(remoteRuntimeEndpoint, ds)
            server.Start()
        }
    // sentence 3 runtimeService, imageService, err :
    = getRuntimeAndImageServices(remoteRuntimeEndpoint, remoteImageEndpoint, kubeCfg.RuntimeRequestTimeout)

    A. sentence 1

    这个函数调用的核心功能就是一句话:连接docker.

    docker的守护进程会创建一个gPRC服务接口,本质上它是一个默认定义在/var/run/docker.sock的套接字.NewDockerService()函数首先会连接这个套接字,经过包装后,返回一个dockerService类的实例,该实例的client掌握了和docker守护进程的连接.

    B. sentence 2

    NewDockerServer函数有两个参数,一个是string类型的remoteRuntimeEndpoint,其默认值是/var/run/dockershim.sock.另一个就是NewDockerService函数构建的dockerService实例.在Start()函数中,首先启动dockerService,然后在endpoint(也就是/var/run/dockershim.sock)上启动一个gRPC服务.函数RegisterRuntimeServiceServer()将s.server上的请求映射到s.service上.

    // NewDockerServer creates the dockershim grpc server.
    func NewDockerServer(endpoint string, s dockershim.CRIService) *DockerServer {
        return &DockerServer{
            endpoint: endpoint,
            service:  s,
        }
    }
    
    // Start starts the dockershim grpc server.
    func (s *DockerServer) Start() error {
        // Start the internal service.
      s.service.Start()
        l, err := util.CreateListener(s.endpoint)// Create the grpc server and register runtime and image services.
        s.server = grpc.NewServer(
            grpc.MaxRecvMsgSize(maxMsgSize),
            grpc.MaxSendMsgSize(maxMsgSize),
        )
        runtimeapi.RegisterRuntimeServiceServer(s.server, s.service)
        runtimeapi.RegisterImageServiceServer(s.server, s.service)
        go func() {
            if err := s.server.Serve(l); err != nil {
                glog.Fatalf("Failed to serve connections: %v", err)
            }
        }()
        return nil
    }

    C. sentence 3

    sentence 1建立了和docker的连接,sentence 2在/var/run/dockershim.sock上监听gRPC服务并且将收到的请求转发到sentence 1所构建的和docker的连接上.那么sentence 3所完成的事就很简单了,那就是建立到/var/run/dockershim.sock的连接.

    四.小结

    kubelet和docker的交互简单来说是通过两层gPRC服务来实现的,kubelet自己在/var/run/dockershim.sock启动一层gPRC服务,然后这个gPRC服务将请求转发给docker守护进程所创建的/var/run/docker.sock上.从而完成一次交互.那么kubelet为什么要再创建一层gPRC服务呢?原因是k8s为了兼容其他的container runtime,提出了CRI(container runtime interface)的概念,其理想的模型是kubelet->[x-runtime].sock.也就是说,所有runtime统一实现一个gPRC接口,kubelet直接调用这个接口,这样屏蔽了底层rutime的差异.而dockershim.sock只是这个CRI概念的docker化实现.

    五. 其它container runtime

    根据上文我们可以知道,CRI的存在是为了屏蔽container runtime的存在,从而使k8s可以更好的支持多种container runtime,例如rkt.那么其它的kubelet是如何管理其它的runtime呢.原来docker的CRI实现是以dockershim的形式存在于k8s的主线代码中了,其它的runtime需要自己实现类似于dockershim的一套gPRC服务给kubelet访问.

     kubelet ----container-runtime-endpoint选项就是定义这个gPRC服务的地址的.

  • 相关阅读:
    继续OI
    [WARNING]考前必读?!
    近些日的总结吧
    续上文
    又是一年NOIP然鹅我考的是高数(虽然我没打并且内容与NOIP无关)(手动滑稽)
    轮船问题(DP基础)
    NOIP2016报零记
    字符数组
    HA-0302 退役
    各种模板(part 2)
  • 原文地址:https://www.cnblogs.com/elnino/p/9626837.html
Copyright © 2011-2022 走看看