<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>

          kubernetes CRI 解析-容器運(yùn)行時(shí)接口分析

          共 54592字,需瀏覽 110分鐘

           ·

          2021-08-16 02:29


          概述

          kubernetes的設(shè)計(jì)初衷是支持可插拔架構(gòu),從而利于擴(kuò)展kubernetes的功能。在此架構(gòu)思想下,kubernetes提供了3個(gè)特定功能的接口,分別是容器網(wǎng)絡(luò)接口CNI、容器運(yùn)行時(shí)接口CRI和容器存儲(chǔ)接口CSI。kubernetes通過調(diào)用這幾個(gè)接口,來(lái)完成相應(yīng)的功能。

          下面我們來(lái)對(duì)容器運(yùn)行時(shí)接口CRI來(lái)做一下介紹與分析。

          在本文中,會(huì)對(duì)CRI是什么、為什么要有CRI、CRI系統(tǒng)架構(gòu)做一下介紹,以及k8s對(duì)CRI進(jìn)行相關(guān)操作的流程分析,包括了pod創(chuàng)建、刪除等操作。

          CRI是什么

          CRI是Container Runtime Interface(容器運(yùn)行時(shí)接口)的簡(jiǎn)寫。

          CRI解耦了kubelet與容器運(yùn)行時(shí),讓kubelet無(wú)需重新編譯就可以支持多種容器運(yùn)行時(shí)。

          kubelet將通過CRI接口來(lái)跟第三方容器運(yùn)行時(shí)進(jìn)行通信,來(lái)操作容器與鏡像。

          實(shí)現(xiàn)了 CRI 接口的容器運(yùn)行時(shí)通常稱為 CRI shim, 這是一個(gè) gRPC Server,監(jiān)聽在本地的 unix socket  上;而 kubelet 作為 gRPC 的客戶端來(lái)調(diào)用 CRI 接口,來(lái)進(jìn)行Pod  和容器、鏡像的生命周期管理。另外,容器運(yùn)行時(shí)需要自己負(fù)責(zé)管理容器的網(wǎng)絡(luò),推薦使用 CNI。

          CRI shim 通信圖

          提出了CRI標(biāo)準(zhǔn)以后,意味著在新的版本里需要使用新的連接方式與docker通信,為了兼容以前的版本,k8s提供了針對(duì)docker的CRI實(shí)現(xiàn),也就是kubelet包下的dockershim包,dockershim是一個(gè)grpc服務(wù),監(jiān)聽一個(gè)端口供kubelet連接,dockershim收到kubelet的請(qǐng)求后,將其轉(zhuǎn)化為REST API請(qǐng)求,再發(fā)送給docker daemon。

          dockershim 通信圖

          為什么要有CRI

          在1.5以前的版本中,k8s依賴于docker,為了支持不同的容器運(yùn)行時(shí),如rkt、containerd等,kubelet從1.5開始加入了CRI標(biāo)準(zhǔn),它將 Kubelet 與容器運(yùn)行時(shí)解耦,將原來(lái)完全面向 Pod 級(jí)別的內(nèi)部接口拆分成面向 SandboxContainer 的 gRPC 接口,并將鏡像管理和容器管理分離到不同的服務(wù),方便后續(xù)其他容器運(yùn)行時(shí)與k8s對(duì)接。

          Kubernetes中的容器運(yùn)行時(shí)組成

          按照不同的功能可以分為四個(gè)部分:

          1. kubelet 中容器運(yùn)行時(shí)的管理,kubeGenericRuntimeManager,它管理與CRI shim通信的客戶端,完成容器和鏡像的管理(代碼位置:pkg/kubelet/kuberuntime/kuberuntime_manager.go);
          2. 容器運(yùn)行時(shí)接口CRI,包括了容器運(yùn)行時(shí)客戶端接口與容器運(yùn)行時(shí)服務(wù)端接口;
          3. CRI shim客戶端,kubelet持有,用于與CRI shim服務(wù)端進(jìn)行通信;
          4. CRI shim服務(wù)端,即具體的容器運(yùn)行時(shí)實(shí)現(xiàn),包括 kubelet 內(nèi)置的 dockershim (代碼位置:pkg/kubelet/dockershim)以及外部的容器運(yùn)行時(shí)如 cri-containerd(用于支持容器引擎containerd)、rktlet(用于支持容器引擎rkt)等。

          CRI架構(gòu)圖

          在 CRI 之下,包括兩種類型的容器運(yùn)行時(shí)的實(shí)現(xiàn):

          1. kubelet內(nèi)置的 dockershim,實(shí)現(xiàn)了 Docker 容器引擎的支持以及 CNI 網(wǎng)絡(luò)插件(包括 kubenet)的支持。dockershim代碼內(nèi)置于kubelet,被kubelet調(diào)用,讓dockershim起獨(dú)立的server來(lái)建立CRI shim,向kubelet暴露grpc server;
          2. 外部的容器運(yùn)行時(shí),用來(lái)支持 rkt、containerd 等容器引擎的外部容器運(yùn)行時(shí)。

          kubelet中CRI相關(guān)的源碼分析

          kubelet的CRI源碼分析包括如下幾部分:

          1. kubelet CRI相關(guān)啟動(dòng)參數(shù)分析;
          2. kubelet CRI相關(guān)interface/struct分析;
          3. kubelet CRI初始化分析;
          4. kubelet調(diào)用CRI創(chuàng)建pod分析;
          5. kubelet調(diào)用CRI刪除pod分析。

          因篇幅原因,本篇博文先對(duì)前三部分做分析,下一篇博文再對(duì)CRI創(chuàng)建pod以及CRI刪除pod做分析。

          基于tag v1.17.4

          https://github.com/kubernetes/kubernetes/releases/tag/v1.17.4

          1.kubelet組件CRI相關(guān)啟動(dòng)參數(shù)分析

          kubelet組件CRI相關(guān)啟動(dòng)參數(shù)相關(guān)代碼如下:

          // pkg/kubelet/config/flags.go
          // AddFlags adds flags to the container runtime, according to ContainerRuntimeOptions.
          func (s *ContainerRuntimeOptions) AddFlags(fs *pflag.FlagSet) {
           dockerOnlyWarning := "This docker-specific flag only works when container-runtime is set to docker."

           // General settings.
           fs.StringVar(&s.ContainerRuntime, "container-runtime", s.ContainerRuntime, "The container runtime to use. Possible values: 'docker', 'remote', 'rkt (deprecated)'.")
           fs.StringVar(&s.RuntimeCgroups, "runtime-cgroups", s.RuntimeCgroups, "Optional absolute name of cgroups to create and run the runtime in.")
           fs.BoolVar(&s.RedirectContainerStreaming, "redirect-container-streaming", s.RedirectContainerStreaming, "Enables container streaming redirect. If false, kubelet will proxy container streaming data between apiserver and container runtime; if true, kubelet will return an http redirect to apiserver, and apiserver will access container runtime directly. The proxy approach is more secure, but introduces some overhead. The redirect approach is more performant, but less secure because the connection between apiserver and container runtime may not be authenticated.")

           // Docker-specific settings.
           fs.BoolVar(&s.ExperimentalDockershim, "experimental-dockershim", s.ExperimentalDockershim, "Enable dockershim only mode. In this mode, kubelet will only start dockershim without any other functionalities. This flag only serves test purpose, please do not use it unless you are conscious of what you are doing. [default=false]")
           fs.MarkHidden("experimental-dockershim")
           fs.StringVar(&s.DockershimRootDirectory, "experimental-dockershim-root-directory", s.DockershimRootDirectory, "Path to the dockershim root directory.")
           fs.MarkHidden("experimental-dockershim-root-directory")
           fs.StringVar(&s.PodSandboxImage, "pod-infra-container-image", s.PodSandboxImage, fmt.Sprintf("The image whose network/ipc namespaces containers in each pod will use. %s", dockerOnlyWarning))
           fs.StringVar(&s.DockerEndpoint, "docker-endpoint", s.DockerEndpoint, fmt.Sprintf("Use this for the docker endpoint to communicate with. %s", dockerOnlyWarning))
           fs.DurationVar(&s.ImagePullProgressDeadline.Duration, "image-pull-progress-deadline", s.ImagePullProgressDeadline.Duration, fmt.Sprintf("If no pulling progress is made before this deadline, the image pulling will be cancelled. %s", dockerOnlyWarning))
           ...
          }
          // cmd/kubelet/app/options/options.go
          // AddFlags adds flags for a specific KubeletFlags to the specified FlagSet
          func (f *KubeletFlags) AddFlags(mainfs *pflag.FlagSet) {
              ...
              fs.StringVar(&f.RemoteRuntimeEndpoint, "container-runtime-endpoint", f.RemoteRuntimeEndpoint, "[Experimental] The endpoint of remote runtime service. Currently unix socket endpoint is supported on Linux, while npipe and tcp endpoints are supported on windows.  Examples:'unix:///var/run/dockershim.sock', 'npipe:////./pipe/dockershim'")
           fs.StringVar(&f.RemoteImageEndpoint, "image-service-endpoint", f.RemoteImageEndpoint, "[Experimental] The endpoint of remote image service. If not specified, it will be the same with container-runtime-endpoint by default. Currently unix socket endpoint is supported on Linux, while npipe and tcp endpoints are supported on windows.  Examples:'unix:///var/run/dockershim.sock', 'npipe:////./pipe/dockershim'")
           ...
          }

          kubelet組件啟動(dòng)參數(shù)的默認(rèn)值在NewKubeletFlags函數(shù)中設(shè)置。

          // cmd/kubelet/app/options/options.go
          // NewKubeletFlags will create a new KubeletFlags with default values
          func NewKubeletFlags() *KubeletFlags {
           remoteRuntimeEndpoint := ""
           if runtime.GOOS == "linux" {
            remoteRuntimeEndpoint = "unix:///var/run/dockershim.sock"
           } else if runtime.GOOS == "windows" {
            remoteRuntimeEndpoint = "npipe:////./pipe/dockershim"
           }

           return &KubeletFlags{
            EnableServer:                        true,
            ContainerRuntimeOptions:             *NewContainerRuntimeOptions(),
            CertDirectory:                       "/var/lib/kubelet/pki",
            RootDirectory:                       defaultRootDir,
            MasterServiceNamespace:              metav1.NamespaceDefault,
            MaxContainerCount:                   -1,
            MaxPerPodContainerCount:             1,
            MinimumGCAge:                        metav1.Duration{Duration: 0},
            NonMasqueradeCIDR:                   "10.0.0.0/8",
            RegisterSchedulable:                 true,
            ExperimentalKernelMemcgNotification: false,
            RemoteRuntimeEndpoint:               remoteRuntimeEndpoint,
            NodeLabels:                          make(map[string]string),
            VolumePluginDir:                     "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/",
            RegisterNode:                        true,
            SeccompProfileRoot:                  filepath.Join(defaultRootDir, "seccomp"),
            // prior to the introduction of this flag, there was a hardcoded cap of 50 images
            NodeStatusMaxImages:         50,
            EnableCAdvisorJSONEndpoints: true,
           }
          }

          CRI相關(guān)啟動(dòng)參數(shù)的默認(rèn)值在NewContainerRuntimeOptionsNewMainKubelet函數(shù)中設(shè)置。

          // cmd/kubelet/app/options/container_runtime.go
          // NewContainerRuntimeOptions will create a new ContainerRuntimeOptions with
          // default values.
          func NewContainerRuntimeOptions() *config.ContainerRuntimeOptions {
           dockerEndpoint := ""
           if runtime.GOOS != "windows" {
            dockerEndpoint = "unix:///var/run/docker.sock"
           }

           return &config.ContainerRuntimeOptions{
            ContainerRuntime:           kubetypes.DockerContainerRuntime,
            RedirectContainerStreaming: false,
            DockerEndpoint:             dockerEndpoint,
            DockershimRootDirectory:    "/var/lib/dockershim",
            PodSandboxImage:            defaultPodSandboxImage,
            ImagePullProgressDeadline:  metav1.Duration{Duration: 1 * time.Minute},
            ExperimentalDockershim:     false,

            //Alpha feature
            CNIBinDir:   "/opt/cni/bin",
            CNIConfDir:  "/etc/cni/net.d",
            CNICacheDir: "/var/lib/cni/cache",
           }
          }
          // pkg/kubelet/kubelet.go
          func NewMainKubelet(...) {
              ...
              if remoteRuntimeEndpoint != "" {
            // remoteImageEndpoint is same as remoteRuntimeEndpoint if not explicitly specified
            if remoteImageEndpoint == "" {
             remoteImageEndpoint = remoteRuntimeEndpoint
            }
           }
           ...
          }

          下面來(lái)簡(jiǎn)單分析幾個(gè)比較重要的CRI相關(guān)啟動(dòng)參數(shù):

          1. --container-runtime:指定kubelet要使用的容器運(yùn)行時(shí),可選值docker、remote、rkt (deprecated),默認(rèn)值為docker,即使用kubelet內(nèi)置的容器運(yùn)行時(shí)dockershim。當(dāng)需要使用外部容器運(yùn)行時(shí),該參數(shù)配置為remote,并設(shè)置--container-runtime-endpoint參數(shù)值為監(jiān)聽的 unix socket位置。
          2. --runtime-cgroups:容器運(yùn)行時(shí)使用的cgroups,可選值。
          3. --docker-endpoint:docker暴露服務(wù)的socket地址,默認(rèn)值為unix:///var/run/docker.sock,該參數(shù)配置當(dāng)且僅當(dāng)--container-runtime參數(shù)值為docker時(shí)有效。
          4. --pod-infra-container-image:pod sandbox的鏡像地址,默認(rèn)值為k8s.gcr.io/pause:3.1,該參數(shù)配置當(dāng)且僅當(dāng)--container-runtime參數(shù)值為docker時(shí)有效。
          5. --image-pull-progress-deadline:容器鏡像拉取超時(shí)時(shí)間,默認(rèn)值為1分鐘,該參數(shù)配置當(dāng)且僅當(dāng)--container-runtime參數(shù)值為docker時(shí)有效。
          6. --experimental-dockershim:設(shè)置為true時(shí),啟用dockershim模式,只啟動(dòng)dockershim,默認(rèn)值為false,該參數(shù)配置當(dāng)且僅當(dāng)--container-runtime參數(shù)值為docker時(shí)有效。
          7. --experimental-dockershim-root-directorydockershim根目錄,默認(rèn)值為/var/lib/dockershim,該參數(shù)配置當(dāng)且僅當(dāng)--container-runtime參數(shù)值為docker時(shí)有效。
          8. --container-runtime-endpoint:容器運(yùn)行時(shí)的endpoint,linux中默認(rèn)值為unix:///var/run/dockershim.sock,注意與上面的--docker-endpoint區(qū)分開來(lái)。
          9. --image-service-endpoint:鏡像服務(wù)的endpointlinux中默認(rèn)值為unix:///var/run/dockershim.sock

          2.kubelet CRI相關(guān)interface/struct分析

          CRI相關(guān)接口

          1. RuntimeService interface:CRI shim客戶端-容器運(yùn)行時(shí)接口;代碼位置:staging/src/k8s.io/cri-api/pkg/apis/services.go

          2. ImageManagerService interface:CRI shim客戶端-容器鏡像接口;代碼位置:staging/src/k8s.io/cri-api/pkg/apis/services.go

          3. RuntimeServiceServer interface:CRI shim服務(wù)端-容器運(yùn)行時(shí)接口;代碼位置:staging/src/k8s.io/cri-api/pkg/apis/runtime/v1alpha2/api.pb.go

          4. ImageServiceServer interface:CRI shim服務(wù)端-容器鏡像接口;代碼位置:staging/src/k8s.io/cri-api/pkg/apis/runtime/v1alpha2/api.pb.go

          5. CRIService interface:包括了RuntimeServiceServer interface、ImageServiceServer interface與CRI shim服務(wù)端啟動(dòng)方法,所以其包括了一個(gè)CRI shim服務(wù)端需要實(shí)現(xiàn)的所有接口方法;代碼位置:pkg/kubelet/dockershim/docker_service.go

          6. DockerService interface:包括了CRIService interface。代碼位置:pkg/kubelet/dockershim/docker_service.go

          說(shuō)明:RuntimeService interfaceRuntimeServiceServer interface、ImageManagerService interfaceImageServiceServer interface中的接口方法是相同的,它們之間的區(qū)別只是一個(gè)用于CRI shim客戶端,一個(gè)用于CRI shim服務(wù)端。容器運(yùn)行時(shí)接口負(fù)責(zé)管理 Pod 和容器的生命周期,容器鏡像接口負(fù)責(zé)管理容器鏡像的生命周期。

          CRI相關(guān)結(jié)構(gòu)體

          1. RemoteRuntimeService struct:實(shí)現(xiàn)了CRI shim客戶端-容器運(yùn)行時(shí)接口RuntimeService interface,持有與CRI shim容器運(yùn)行時(shí)服務(wù)端通信的客戶端;代碼位置:pkg/kubelet/remote/remote_runtime.go

          2. RemoteImageService struct:實(shí)現(xiàn)了CRI shim客戶端-容器鏡像接口ImageManagerService interface,持有與CRI shim容器鏡像服務(wù)端通信的客戶端;代碼位置:pkg/kubelet/remote/remote_image.go

          3. dockerService struct:實(shí)現(xiàn)了CRI shim服務(wù)端-容器運(yùn)行時(shí)接口RuntimeServiceServer interface;代碼位置:pkg/kubelet/dockershim/docker_service.gopkg/kubelet/dockershim/docker_container.go

          4. dockerService struct:實(shí)現(xiàn)了CRI shim服務(wù)端-容器鏡像接口ImageServiceServer interface;代碼位置:pkg/kubelet/dockershim/docker_service.gopkg/kubelet/dockershim/docker_image.go

          5. DockerServer struct:代表了dockershim(kubelet內(nèi)置的CRI shim)的服務(wù)端,其實(shí)現(xiàn)了CRIService interface。代碼位置:pkg/kubelet/dockershim/remote/docker_server.go

          CRI shim server接口圖示

          RuntimeServiceServer

          RuntimeServiceServer 提供了的接口,按照功能可以劃分為四組:

          1. PodSandbox 的管理接口:PodSandbox 是對(duì) Kubernete Pod 的抽象,用來(lái)給容器提供一個(gè)隔離的環(huán)境,并提供網(wǎng)絡(luò)等共享的命名空間;

          2. Container 的管理接口:在指定的 PodSandbox 中創(chuàng)建、啟動(dòng)、停止和刪除容器;

          3. Streaming API 接口:包括 Exec、Attach 和 PortForward 等和容器進(jìn)行數(shù)據(jù)交互的接口,這三個(gè)接口返回的是運(yùn)行時(shí) Streaming Server 的 URL,而不是直接跟容器交互;

          4. runtime狀態(tài)接口:包括查詢 runtime名稱、版本、API 版本和狀態(tài)等。

          ImageServiceServer

          ImageServiceServer提供了 5 個(gè)接口,用于管理容器鏡像。

          下面會(huì)對(duì)上面提到的接口/結(jié)構(gòu)體做分析。

          2.1 RuntimeService interface

          RuntimeService 負(fù)責(zé)管理 Pod 和容器的生命周期,是CRI shim客戶端需要實(shí)現(xiàn)的容器運(yùn)行時(shí)接口。

          RuntimeService interface包含了RuntimeVersioner、ContainerManager、PodSandboxManagerContainerStatsManager接口,下面對(duì)對(duì)這些接口一一做介紹。

          容器運(yùn)行時(shí)會(huì)實(shí)現(xiàn)RuntimeService interface

          // staging/src/k8s.io/cri-api/pkg/apis/services.go
          // RuntimeService interface should be implemented by a container runtime.
          // The methods should be thread-safe.
          type RuntimeService interface {
           RuntimeVersioner
           ContainerManager
           PodSandboxManager
           ContainerStatsManager

           // UpdateRuntimeConfig updates runtime configuration if specified
           UpdateRuntimeConfig(runtimeConfig *runtimeapi.RuntimeConfig) error
           // Status returns the status of the runtime.
           Status() (*runtimeapi.RuntimeStatus, error)
          }
          RuntimeVersioner interface

          RuntimeVersioner interface負(fù)責(zé)返回容器運(yùn)行時(shí)的名稱、版本以及 API 版本信息,只有一個(gè)接口函數(shù) Version

          // staging/src/k8s.io/cri-api/pkg/apis/services.go
          // RuntimeVersioner contains methods for runtime name, version and API version.
          type RuntimeVersioner interface {
           // Version returns the runtime name, runtime version and runtime API version
           Version(apiVersion string) (*runtimeapi.VersionResponse, error)
          }
          ContainerManager interface

          ContainerManager interface包含了對(duì)container(業(yè)務(wù)容器)進(jìn)行操作的一些方法,如CreateContainer(創(chuàng)建容器)、StartContainer(啟動(dòng)容器)、StopContainer(停止容器)、RemoveContainer(刪除容器)等。

          // staging/src/k8s.io/cri-api/pkg/apis/services.go
          // ContainerManager contains methods to manipulate containers managed by a
          // container runtime. The methods are thread-safe.
          type ContainerManager interface {
           // CreateContainer creates a new container in specified PodSandbox.
           CreateContainer(podSandboxID string, config *runtimeapi.ContainerConfig, sandboxConfig *runtimeapi.PodSandboxConfig) (string, error)
           // StartContainer starts the container.
           StartContainer(containerID string) error
           // StopContainer stops a running container with a grace period (i.e., timeout).
           StopContainer(containerID string, timeout int64) error
           // RemoveContainer removes the container.
           RemoveContainer(containerID string) error
           // ListContainers lists all containers by filters.
           ListContainers(filter *runtimeapi.ContainerFilter) ([]*runtimeapi.Container, error)
           // ContainerStatus returns the status of the container.
           ContainerStatus(containerID string) (*runtimeapi.ContainerStatus, error)
           // UpdateContainerResources updates the cgroup resources for the container.
           UpdateContainerResources(containerID string, resources *runtimeapi.LinuxContainerResources) error
           // ExecSync executes a command in the container, and returns the stdout output.
           // If command exits with a non-zero exit code, an error is returned.
           ExecSync(containerID string, cmd []string, timeout time.Duration) (stdout []byte, stderr []byte, err error)
           // Exec prepares a streaming endpoint to execute a command in the container, and returns the address.
           Exec(*runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error)
           // Attach prepares a streaming endpoint to attach to a running container, and returns the address.
           Attach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error)
           // ReopenContainerLog asks runtime to reopen the stdout/stderr log file
           // for the container. If it returns error, new container log file MUST NOT
           // be created.
           ReopenContainerLog(ContainerID string) error
          }
          PodSandboxManager interface

          PodSandboxManager interface包含了對(duì)pod sandboxpause container)進(jìn)行操作的一些方法,如RunPodSandbox(創(chuàng)建并啟動(dòng)pause container)、StopPodSandbox(停止pause container)、RemovePodSandbox(刪除pause container)等。

          // staging/src/k8s.io/cri-api/pkg/apis/services.go
          // PodSandboxManager contains methods for operating on PodSandboxes. The methods
          // are thread-safe.
          type PodSandboxManager interface {
           // RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure
           // the sandbox is in ready state.
           RunPodSandbox(config *runtimeapi.PodSandboxConfig, runtimeHandler string) (string, error)
           // StopPodSandbox stops the sandbox. If there are any running containers in the
           // sandbox, they should be force terminated.
           StopPodSandbox(podSandboxID string) error
           // RemovePodSandbox removes the sandbox. If there are running containers in the
           // sandbox, they should be forcibly removed.
           RemovePodSandbox(podSandboxID string) error
           // PodSandboxStatus returns the Status of the PodSandbox.
           PodSandboxStatus(podSandboxID string) (*runtimeapi.PodSandboxStatus, error)
           // ListPodSandbox returns a list of Sandbox.
           ListPodSandbox(filter *runtimeapi.PodSandboxFilter) ([]*runtimeapi.PodSandbox, error)
           // PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address.
           PortForward(*runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error)
          }
          ContainerStatsManager interface

          ContainerStatsManager interface包含了對(duì)容器統(tǒng)計(jì)數(shù)據(jù)的查詢接口,如ContainerStats、ListContainerStats

          // staging/src/k8s.io/cri-api/pkg/apis/services.go
          // ContainerStatsManager contains methods for retrieving the container
          // statistics.
          type ContainerStatsManager interface {
           // ContainerStats returns stats of the container. If the container does not
           // exist, the call returns an error.
           ContainerStats(containerID string) (*runtimeapi.ContainerStats, error)
           // ListContainerStats returns stats of all running containers.
           ListContainerStats(filter *runtimeapi.ContainerStatsFilter) ([]*runtimeapi.ContainerStats, error)
          }

          2.2 ImageManagerService interface

          ImageManagerService負(fù)責(zé)管理鏡像的生命周期,是CRI shim客戶端需要實(shí)現(xiàn)的鏡像接口。

          ImageManagerService interface包含了容器鏡像的相關(guān)操作接口,如PullImage(拉取鏡像)、ListImages(列出現(xiàn)存鏡像列表)等。

          // staging/src/k8s.io/cri-api/pkg/apis/services.go
          // ImageManagerService interface should be implemented by a container image
          // manager.
          // The methods should be thread-safe.
          type ImageManagerService interface {
           // ListImages lists the existing images.
           ListImages(filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error)
           // ImageStatus returns the status of the image.
           ImageStatus(image *runtimeapi.ImageSpec) (*runtimeapi.Image, error)
           // PullImage pulls an image with the authentication config.
           PullImage(image *runtimeapi.ImageSpec, auth *runtimeapi.AuthConfig, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, error)
           // RemoveImage removes the image.
           RemoveImage(image *runtimeapi.ImageSpec) error
           // ImageFsInfo returns information of the filesystem that is used to store images.
           ImageFsInfo() ([]*runtimeapi.FilesystemUsage, error)
          }

          2.3 CRIService interface / DockerService interface

          CRIService interface中定義了CRI shim服務(wù)端必須實(shí)現(xiàn)的一些方法,其中包括了RuntimeServiceServer interface(容器運(yùn)行時(shí)操作相關(guān)方法)、ImageServiceServer interface(鏡像操作相關(guān)方法)以及CRI shim服務(wù)端啟動(dòng)方法。

          // pkg/kubelet/dockershim/docker_service.go
          // CRIService includes all methods necessary for a CRI server.
          type CRIService interface {
           runtimeapi.RuntimeServiceServer
           runtimeapi.ImageServiceServer
           Start() error
          }

          // DockerService is an interface that embeds the new RuntimeService and
          // ImageService interfaces.
          type DockerService interface {
           CRIService

           // For serving streaming calls.
           http.Handler

           // For supporting legacy features.
           DockerLegacyService
          }

          2.4 RemoteRuntimeService struct

          實(shí)現(xiàn)了CRI shim客戶端-容器運(yùn)行時(shí)接口RuntimeService interface,持有與CRI shim容器運(yùn)行時(shí)服務(wù)端通信的客戶端runtimeClient。

          // pkg/kubelet/remote/remote_runtime.go
          // RemoteRuntimeService is a gRPC implementation of internalapi.RuntimeService.
          type RemoteRuntimeService struct {
           timeout       time.Duration
           runtimeClient runtimeapi.RuntimeServiceClient
           // Cache last per-container error message to reduce log spam
           logReduction *logreduction.LogReduction
          }

          2.5 RemoteImageService struct

          實(shí)現(xiàn)了CRI shim客戶端-容器鏡像接口ImageManagerService interface,持有與CRI shim容器鏡像服務(wù)端通信的客戶端imageClient。

          // pkg/kubelet/remote/remote_image.go
          // RemoteImageService is a gRPC implementation of internalapi.ImageManagerService.
          type RemoteImageService struct {
           timeout     time.Duration
           imageClient runtimeapi.ImageServiceClient
          }

          2.5 DockerServer struct

          DockerServer struct代表了dockershim(kubelet內(nèi)置的CRI shim)的服務(wù)端,其實(shí)現(xiàn)了CRIService interface。

          // pkg/kubelet/dockershim/remote/docker_server.go
          // DockerServer is the grpc server of dockershim.
          type DockerServer struct {
           // endpoint is the endpoint to serve on.
           endpoint string
           // service is the docker service which implements runtime and image services.
           service dockershim.CRIService
           // server is the grpc server.
           server *grpc.Server
          }

          3.kubelet CRI相關(guān)初始化

          kubelet中CRI相關(guān)初始化邏輯如下:

          1. 當(dāng)kubelet選用dockershim作為容器運(yùn)行時(shí),則初始化并啟動(dòng)容器運(yùn)行時(shí)服務(wù)端dockershim(初始化dockershim過程中也會(huì)初始化網(wǎng)絡(luò)插件CNI);
          2. 初始化容器運(yùn)行時(shí)CRI shim客戶端(用于調(diào)用CRI shim服務(wù)端:內(nèi)置的容器運(yùn)行時(shí)dockershim或remote容器運(yùn)行時(shí));
          3. 初始化kubeGenericRuntimeManager,用于容器運(yùn)行時(shí)的管理。初始化完成后,后續(xù)kubelet對(duì)容器以及鏡像的相關(guān)操作都會(huì)通過該結(jié)構(gòu)體持有的CRI shim客戶端,與CRI shim服務(wù)端進(jìn)行通信來(lái)完成。

          CRI初始化的調(diào)用鏈

          main (cmd/kubelet/kubelet.go)
           -> NewKubeletCommand (cmd/kubelet/app/server.go)
           -> Run (cmd/kubelet/app/server.go)
           -> run (cmd/kubelet/app/server.go)
           -> RunKubelet (cmd/kubelet/app/server.go)
           -> CreateAndInitKubelet(cmd/kubelet/app/server.go)
           -> kubelet.NewMainKubelet(pkg/kubelet/kubelet.go)
           -> getRuntimeAndImageServices(pkg/kubelet/kubelet.go) &&  kuberuntime.NewKubeGenericRuntimeManager(pkg/kubelet/kuberuntime/kuberuntime_manager.go)

          NewMainKubelet函數(shù)中CRI相關(guān)邏輯:

          1. 初始化并啟動(dòng)內(nèi)置容器運(yùn)行時(shí)服務(wù)端dockershim:根據(jù)containerRuntime的值(kubelet啟動(dòng)參數(shù)--container-runtime),如果是docker,則初始化并啟動(dòng)docker CRI shim即kubelet內(nèi)置容器運(yùn)行時(shí)dockershim,暴露grpc socket,如果是remote,則不做初始化啟動(dòng)操作。
          2. 調(diào)用getRuntimeAndImageServices:初始化容器運(yùn)行時(shí)CRI shim客戶端,包括容器運(yùn)行時(shí)客戶端runtimeClient以及容器鏡像客戶端imageClient。
          3. 調(diào)用kuberuntime.NewKubeGenericRuntimeManager,以及klet賦值:初始化kubeGenericRuntimeManager struct,用于容器運(yùn)行時(shí)的管理。初始化完成后,后續(xù)kubelet對(duì)容器以及鏡像的相關(guān)操作都會(huì)通過該結(jié)構(gòu)體持有的CRI shim客戶端,與CRI shim服務(wù)端進(jìn)行通信來(lái)完成。
          // pkg/kubelet/kubelet.go
          func NewMainKubelet(...) {
              ...
              switch containerRuntime {
              //1. 初始化并啟動(dòng)內(nèi)置容器運(yùn)行時(shí)服務(wù)端dockershim
           case kubetypes.DockerContainerRuntime:
            // Create and start the CRI shim running as a grpc server.
            streamingConfig := getStreamingConfig(kubeCfg, kubeDeps, crOptions)
            ds, err := dockershim.NewDockerService(kubeDeps.DockerClientConfig, crOptions.PodSandboxImage, streamingConfig,
             &pluginSettings, runtimeCgroups, kubeCfg.CgroupDriver, crOptions.DockershimRootDirectory, !crOptions.RedirectContainerStreaming)
            if err != nil {
             return nil, err
            }
            if crOptions.RedirectContainerStreaming {
             klet.criHandler = ds
            }

            // The unix socket for kubelet <-> dockershim communication.
            klog.V(5).Infof("RemoteRuntimeEndpoint: %q, RemoteImageEndpoint: %q",
             remoteRuntimeEndpoint,
             remoteImageEndpoint)
            klog.V(2).Infof("Starting the GRPC server for the docker CRI shim.")
            server := dockerremote.NewDockerServer(remoteRuntimeEndpoint, ds)
            if err := server.Start(); err != nil {
             return nil, err
            }

            // Create dockerLegacyService when the logging driver is not supported.
            supported, err := ds.IsCRISupportedLogDriver()
            if err != nil {
             return nil, err
            }
            if !supported {
             klet.dockerLegacyService = ds
             legacyLogProvider = ds
            }
           case kubetypes.RemoteContainerRuntime:
            // No-op.
            break
           default:
            return nil, fmt.Errorf("unsupported CRI runtime: %q", containerRuntime)
           }
           //2. 初始化容器運(yùn)行時(shí)CRI shim客戶端
           runtimeService, imageService, err := getRuntimeAndImageServices(remoteRuntimeEndpoint, remoteImageEndpoint, kubeCfg.RuntimeRequestTimeout)
           if err != nil {
            return nil, err
           }
           klet.runtimeService = runtimeService

           if utilfeature.DefaultFeatureGate.Enabled(features.RuntimeClass) && kubeDeps.KubeClient != nil {
            klet.runtimeClassManager = runtimeclass.NewManager(kubeDeps.KubeClient)
           }
              //3. 初始化```GenericRuntimeManager```,用于容器運(yùn)行時(shí)的管理
           runtime, err := kuberuntime.NewKubeGenericRuntimeManager(
            kubecontainer.FilterEventRecorder(kubeDeps.Recorder),
            klet.livenessManager,
            klet.startupManager,
            seccompProfileRoot,
            containerRefManager,
            machineInfo,
            klet,
            kubeDeps.OSInterface,
            klet,
            httpClient,
            imageBackOff,
            kubeCfg.SerializeImagePulls,
            float32(kubeCfg.RegistryPullQPS),
            int(kubeCfg.RegistryBurst),
            kubeCfg.CPUCFSQuota,
            kubeCfg.CPUCFSQuotaPeriod,
            runtimeService,
            imageService,
            kubeDeps.ContainerManager.InternalContainerLifecycle(),
            legacyLogProvider,
            klet.runtimeClassManager,
           )
           if err != nil {
            return nil, err
           }
           klet.containerRuntime = runtime
           klet.streamingRuntime = runtime
           klet.runner = runtime
           ...
          }

          3.1 初始化并啟動(dòng)內(nèi)置容器運(yùn)行時(shí)服務(wù)端dockershim

          這里對(duì)變量containerRuntime值等于docker時(shí)做分析,即kubelet啟動(dòng)參數(shù)--container-runtime值為docker,這時(shí)kubelet會(huì)使用內(nèi)置的CRI shim即dockershim作為容器運(yùn)行時(shí),dockershim調(diào)用docker進(jìn)行容器以及鏡像的相關(guān)操作。

          初始化并啟動(dòng)dockershim主要邏輯如下:

          1. 調(diào)用dockershim.NewDockerService:新建并初始化dockershim服務(wù)端,包括初始化docker client、初始化cni網(wǎng)絡(luò)配置等操作;
          2. 調(diào)用dockerremote.NewDockerServerserver.Start:?jiǎn)?dòng)dockershim,暴露服務(wù)socket。

          3.1.1 dockershim.NewDockerService

          新建并初始化dockershim服務(wù)端,主要邏輯如下:

          1. 調(diào)用NewDockerClientFromConfig:創(chuàng)建docker的客戶端-client對(duì)象,包含了我們常用的docker run,docker images等所有操作調(diào)用;
          2. 構(gòu)建dockerService struct;
          3. 初始化CNI網(wǎng)絡(luò)配置(CNI網(wǎng)絡(luò)配置初始化在專門進(jìn)行CNI分析的博文再詳細(xì)講解)。
          // pkg/kubelet/dockershim/docker_service.go
          // NewDockerService creates a new `DockerService` struct.
          // NOTE: Anything passed to DockerService should be eventually handled in another way when we switch to running the shim as a different process.
          func NewDockerService(config *ClientConfig, podSandboxImage string, streamingConfig *streaming.Config, pluginSettings *NetworkPluginSettings,
           cgroupsName string, kubeCgroupDriver string, dockershimRootDir string, startLocalStreamingServer bool)
           (DockerService, error)
           {
              //1. 創(chuàng)建docker的客戶端
           client := NewDockerClientFromConfig(config)

           c := libdocker.NewInstrumentedInterface(client)

           checkpointManager, err := checkpointmanager.NewCheckpointManager(filepath.Join(dockershimRootDir, sandboxCheckpointDir))
           if err != nil {
            return nil, err
           }
              //2. 構(gòu)建```dockerService struct```
           ds := &dockerService{
            client:          c,
            os:              kubecontainer.RealOS{},
            podSandboxImage: podSandboxImage,
            streamingRuntime: &streamingRuntime{
             client:      client,
             execHandler: &NativeExecHandler{},
            },
            containerManager:          cm.NewContainerManager(cgroupsName, client),
            checkpointManager:         checkpointManager,
            startLocalStreamingServer: startLocalStreamingServer,
            networkReady:              make(map[string]bool),
            containerCleanupInfos:     make(map[string]*containerCleanupInfo),
           }

           // check docker version compatibility.
           if err = ds.checkVersionCompatibility(); err != nil {
            return nil, err
           }

           // create streaming server if configured.
           if streamingConfig != nil {
            var err error
            ds.streamingServer, err = streaming.NewServer(*streamingConfig, ds.streamingRuntime)
            if err != nil {
             return nil, err
            }
           }

           // Determine the hairpin mode.
           if err := effectiveHairpinMode(pluginSettings); err != nil {
            // This is a non-recoverable error. Returning it up the callstack will just
            // lead to retries of the same failure, so just fail hard.
            return nil, err
           }
           klog.Infof("Hairpin mode set to %q", pluginSettings.HairpinMode)
              //3. 初始化CNI網(wǎng)絡(luò)配置
           // dockershim currently only supports CNI plugins.
           pluginSettings.PluginBinDirs = cni.SplitDirs(pluginSettings.PluginBinDirString)
           cniPlugins := cni.ProbeNetworkPlugins(pluginSettings.PluginConfDir, pluginSettings.PluginCacheDir, pluginSettings.PluginBinDirs)
           cniPlugins = append(cniPlugins, kubenet.NewPlugin(pluginSettings.PluginBinDirs, pluginSettings.PluginCacheDir))
           netHost := &dockerNetworkHost{
            &namespaceGetter{ds},
            &portMappingGetter{ds},
           }
           plug, err := network.InitNetworkPlugin(cniPlugins, pluginSettings.PluginName, netHost, pluginSettings.HairpinMode, pluginSettings.NonMasqueradeCIDR, pluginSettings.MTU)
           if err != nil {
            return nil, fmt.Errorf("didn't find compatible CNI plugin with given settings %+v: %v", pluginSettings, err)
           }
           ds.network = network.NewPluginManager(plug)
           klog.Infof("Docker cri networking managed by %v", plug.Name())

           // NOTE: cgroup driver is only detectable in docker 1.11+
           cgroupDriver := defaultCgroupDriver
           dockerInfo, err := ds.client.Info()
           klog.Infof("Docker Info: %+v", dockerInfo)
           if err != nil {
            klog.Errorf("Failed to execute Info() call to the Docker client: %v", err)
            klog.Warningf("Falling back to use the default driver: %q", cgroupDriver)
           } else if len(dockerInfo.CgroupDriver) == 0 {
            klog.Warningf("No cgroup driver is set in Docker")
            klog.Warningf("Falling back to use the default driver: %q", cgroupDriver)
           } else {
            cgroupDriver = dockerInfo.CgroupDriver
           }
           if len(kubeCgroupDriver) != 0 && kubeCgroupDriver != cgroupDriver {
            return nil, fmt.Errorf("misconfiguration: kubelet cgroup driver: %q is different from docker cgroup driver: %q", kubeCgroupDriver, cgroupDriver)
           }
           klog.Infof("Setting cgroupDriver to %s", cgroupDriver)
           ds.cgroupDriver = cgroupDriver
           ds.versionCache = cache.NewObjectCache(
            func() (interface{}, error) {
             return ds.getDockerVersion()
            },
            versionCacheTTL,
           )

           // Register prometheus metrics.
           metrics.Register()

           return ds, nil
          }
          NewDockerClientFromConfig

          NewDockerClientFromConfig函數(shù)主要是建立與docker通信的客戶端。其中config結(jié)構(gòu)體里,dockerEndpoint的值來(lái)自于kubelet啟動(dòng)參數(shù)--container-runtime-endpoint的配置,默認(rèn)是unix:///var/run/docker.sock。

          // pkg/kubelet/dockershim/docker_service.go
          // NewDockerClientFromConfig create a docker client from given configure
          // return nil if nil configure is given.
          func NewDockerClientFromConfig(config *ClientConfig) libdocker.Interface {
           if config != nil {
            // Create docker client.
            client := libdocker.ConnectToDockerOrDie(
             config.DockerEndpoint,
             config.RuntimeRequestTimeout,
             config.ImagePullProgressDeadline,
             config.WithTraceDisabled,
             config.EnableSleep,
            )
            return client
           }

           return nil
          }
          // pkg/kubelet/dockershim/libdocker/client.go
          // ConnectToDockerOrDie creates docker client connecting to docker daemon.
          // If the endpoint passed in is "fake://", a fake docker client
          // will be returned. The program exits if error occurs. The requestTimeout
          // is the timeout for docker requests. If timeout is exceeded, the request
          // will be cancelled and throw out an error. If requestTimeout is 0, a default
          // value will be applied.
          func ConnectToDockerOrDie(dockerEndpoint string, requestTimeout, imagePullProgressDeadline time.Duration,
           withTraceDisabled bool, enableSleep bool)
           Interface
           {
           if dockerEndpoint == FakeDockerEndpoint {
            fakeClient := NewFakeDockerClient()
            if withTraceDisabled {
             fakeClient = fakeClient.WithTraceDisabled()
            }

            if enableSleep {
             fakeClient.EnableSleep = true
            }
            return fakeClient
           }
           client, err := getDockerClient(dockerEndpoint)
           if err != nil {
            klog.Fatalf("Couldn't connect to docker: %v", err)
           }
           klog.Infof("Start docker client with request timeout=%v", requestTimeout)
           return newKubeDockerClient(client, requestTimeout, imagePullProgressDeadline)
          }

          3.1.2 啟動(dòng)dockershim,暴露服務(wù)socket。

          dockerremote.NewDockerServer()

          // pkg/kubelet/dockershim/remote/docker_server.go
          // NewDockerServer creates the dockershim grpc server.
          func NewDockerServer(endpoint string, s dockershim.CRIService) *DockerServer {
           return &DockerServer{
            endpoint: endpoint,
            service:  s,
           }
          }

          3.2 初始化容器運(yùn)行時(shí)CRI shim客戶端

          getRuntimeAndImageServices函數(shù)主要邏輯:

          1. 調(diào)用remote.NewRemoteRuntimeService函數(shù):實(shí)例化容器相關(guān)操作的CRI shim客戶端-容器運(yùn)行時(shí)客戶端runtimeClient,實(shí)現(xiàn)了上述CRI相關(guān)interface/struct分析中的RuntimeService接口(CRI shim客戶端接口);
          2. 調(diào)用remote.NewRemoteImageService函數(shù):實(shí)例化鏡像相關(guān)操作的CRI shim客戶端-容器鏡像客戶端imageClient,實(shí)現(xiàn)了上述CRI相關(guān)interface/struct分析中的ImageManagerService接口(CRI shim客戶端接口)。
          // pkg/kubelet/kubelet.go
          func getRuntimeAndImageServices(remoteRuntimeEndpoint string, remoteImageEndpoint string, runtimeRequestTimeout metav1.Duration) (internalapi.RuntimeService, internalapi.ImageManagerService, error) {
           rs, err := remote.NewRemoteRuntimeService(remoteRuntimeEndpoint, runtimeRequestTimeout.Duration)
           if err != nil {
            return nilnil, err
           }
           is, err := remote.NewRemoteImageService(remoteImageEndpoint, runtimeRequestTimeout.Duration)
           if err != nil {
            return nilnil, err
           }
           return rs, is, err
          }
          3.2.1 remote.NewRemoteRuntimeService

          remote.NewRemoteRuntimeService函數(shù)作用:實(shí)例化容器相關(guān)操作的CRI shim客戶端-容器運(yùn)行時(shí)客戶端runtimeClient,實(shí)現(xiàn)了上述CRI相關(guān)interface/struct分析中的RuntimeService接口(CRI shim客戶端接口)。

          主要邏輯:根據(jù)kubelet啟動(dòng)參數(shù)--container-runtime-endpoint或使用默認(rèn)值unix:///var/run/dockershim.sock,嘗試連接該socket,建立client。

          // pkg/kubelet/remote/remote_runtime.go
          // NewRemoteRuntimeService creates a new internalapi.RuntimeService.
          func NewRemoteRuntimeService(endpoint string, connectionTimeout time.Duration) (internalapi.RuntimeService, error) {
           klog.V(3).Infof("Connecting to runtime service %s", endpoint)
           addr, dailer, err := util.GetAddressAndDialer(endpoint)
           if err != nil {
            return nil, err
           }
           ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout)
           defer cancel()

           conn, err := grpc.DialContext(ctx, addr, grpc.WithInsecure(), grpc.WithDialer(dailer), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)))
           if err != nil {
            klog.Errorf("Connect remote runtime %s failed: %v", addr, err)
            return nil, err
           }

           return &RemoteRuntimeService{
            timeout:       connectionTimeout,
            runtimeClient: runtimeapi.NewRuntimeServiceClient(conn),
            logReduction:  logreduction.NewLogReduction(identicalErrorDelay),
           }, nil
          }
          3.2.2 remote.NewRemoteImageService

          remote.NewRemoteImageService函數(shù)作用:實(shí)例化鏡像相關(guān)操作的CRI shim客戶端-容器鏡像客戶端imageClient,實(shí)現(xiàn)了上述CRI相關(guān)interface/struct分析中的ImageManagerService接口(CRI shim客戶端接口)。

          主要邏輯:根據(jù)kubelet啟動(dòng)參數(shù)--image-service-endpoint或使用默認(rèn)值unix:///var/run/dockershim.sock,嘗試連接該socket,建立client。

          // pkg/kubelet/remote/remote_runtime.go
          // NewRemoteImageService creates a new internalapi.ImageManagerService.
          func NewRemoteImageService(endpoint string, connectionTimeout time.Duration) (internalapi.ImageManagerService, error) {
           klog.V(3).Infof("Connecting to image service %s", endpoint)
           addr, dailer, err := util.GetAddressAndDialer(endpoint)
           if err != nil {
            return nil, err
           }

           ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout)
           defer cancel()

           conn, err := grpc.DialContext(ctx, addr, grpc.WithInsecure(), grpc.WithDialer(dailer), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)))
           if err != nil {
            klog.Errorf("Connect remote image service %s failed: %v", addr, err)
            return nil, err
           }

           return &RemoteImageService{
            timeout:     connectionTimeout,
            imageClient: runtimeapi.NewImageServiceClient(conn),
           }, nil
          }

          3.3 初始化kubeGenericRuntimeManager,用于容器運(yùn)行時(shí)的管理

          kuberuntime.NewKubeGenericRuntimeManager函數(shù)主要是初始化kubeGenericRuntimeManager struct,而kubeGenericRuntimeManager struct是對(duì)KubeGenericRuntime interface的實(shí)現(xiàn)。kubeGenericRuntimeManager是kubelet中容器運(yùn)行時(shí)的管理者,管理著CRI shim客戶端,負(fù)責(zé)與CRI shim服務(wù)端 交互,完成容器和鏡像的管理。

          初始化完成后,后續(xù)kubelet對(duì)容器以及鏡像的相關(guān)操作都會(huì)通過該結(jié)構(gòu)體持有的CRI shim客戶端,與CRI shim服務(wù)端進(jìn)行通信來(lái)完成。

          // pkg/kubelet/kuberuntime/kuberuntime_manager.go
          // NewKubeGenericRuntimeManager creates a new kubeGenericRuntimeManager
          func NewKubeGenericRuntimeManager(
           recorder record.EventRecorder,
           livenessManager proberesults.Manager,
           startupManager proberesults.Manager,
           seccompProfileRoot string,
           containerRefManager *kubecontainer.RefManager,
           machineInfo *cadvisorapi.MachineInfo,
           podStateProvider podStateProvider,
           osInterface kubecontainer.OSInterface,
           runtimeHelper kubecontainer.RuntimeHelper,
           httpClient types.HttpGetter,
           imageBackOff *flowcontrol.Backoff,
           serializeImagePulls bool,
           imagePullQPS float32,
           imagePullBurst int,
           cpuCFSQuota bool,
           cpuCFSQuotaPeriod metav1.Duration,
           runtimeService internalapi.RuntimeService,
           imageService internalapi.ImageManagerService,
           internalLifecycle cm.InternalContainerLifecycle,
           legacyLogProvider LegacyLogProvider,
           runtimeClassManager *runtimeclass.Manager,
          )
           (KubeGenericRuntime, error)
           {
           kubeRuntimeManager := &kubeGenericRuntimeManager{
            recorder:            recorder,
            cpuCFSQuota:         cpuCFSQuota,
            cpuCFSQuotaPeriod:   cpuCFSQuotaPeriod,
            seccompProfileRoot:  seccompProfileRoot,
            livenessManager:     livenessManager,
            startupManager:      startupManager,
            containerRefManager: containerRefManager,
            machineInfo:         machineInfo,
            osInterface:         osInterface,
            runtimeHelper:       runtimeHelper,
            runtimeService:      newInstrumentedRuntimeService(runtimeService),
            imageService:        newInstrumentedImageManagerService(imageService),
            keyring:             credentialprovider.NewDockerKeyring(),
            internalLifecycle:   internalLifecycle,
            legacyLogProvider:   legacyLogProvider,
            runtimeClassManager: runtimeClassManager,
            logReduction:        logreduction.NewLogReduction(identicalErrorDelay),
           }

           typedVersion, err := kubeRuntimeManager.runtimeService.Version(kubeRuntimeAPIVersion)
           if err != nil {
            klog.Errorf("Get runtime version failed: %v", err)
            return nil, err
           }

           // Only matching kubeRuntimeAPIVersion is supported now
           // TODO: Runtime API machinery is under discussion at https://github.com/kubernetes/kubernetes/issues/28642
           if typedVersion.Version != kubeRuntimeAPIVersion {
            klog.Errorf("Runtime api version %s is not supported, only %s is supported now",
             typedVersion.Version,
             kubeRuntimeAPIVersion)
            return nil, ErrVersionNotSupported
           }

           kubeRuntimeManager.runtimeName = typedVersion.RuntimeName
           klog.Infof("Container runtime %s initialized, version: %s, apiVersion: %s",
            typedVersion.RuntimeName,
            typedVersion.RuntimeVersion,
            typedVersion.RuntimeApiVersion)

           // If the container logs directory does not exist, create it.
           // TODO: create podLogsRootDirectory at kubelet.go when kubelet is refactored to
           // new runtime interface
           if _, err := osInterface.Stat(podLogsRootDirectory); os.IsNotExist(err) {
            if err := osInterface.MkdirAll(podLogsRootDirectory, 0755); err != nil {
             klog.Errorf("Failed to create directory %q: %v", podLogsRootDirectory, err)
            }
           }

           kubeRuntimeManager.imagePuller = images.NewImageManager(
            kubecontainer.FilterEventRecorder(recorder),
            kubeRuntimeManager,
            imageBackOff,
            serializeImagePulls,
            imagePullQPS,
            imagePullBurst)
           kubeRuntimeManager.runner = lifecycle.NewHandlerRunner(httpClient, kubeRuntimeManager, kubeRuntimeManager)
           kubeRuntimeManager.containerGC = newContainerGC(runtimeService, podStateProvider, kubeRuntimeManager)

           kubeRuntimeManager.versionCache = cache.NewObjectCache(
            func() (interface{}, error) {
             return kubeRuntimeManager.getTypedVersion()
            },
            versionCacheTTL,
           )

           return kubeRuntimeManager, nil
          }

          總結(jié)

          該博文先對(duì)CRI做了介紹,然后對(duì)kubelet CRI相關(guān)源碼進(jìn)行分析,包括kubelet組件CRI相關(guān)啟動(dòng)參數(shù)分析、CRI相關(guān)interface/struct分析、CRI相關(guān)初始化分析3個(gè)部分,剩下的其他部分分析,將在下一篇CRI博文里做分析。

          CRI介紹

          CRI,全稱Container Runtime Interface,容器運(yùn)行時(shí)接口。

          在1.5以前的版本中,k8s依賴于docker,為了支持不同的容器運(yùn)行時(shí),如rktcontainerd等,kubelet從1.5開始加入了CRI標(biāo)準(zhǔn),它將 Kubelet 與容器運(yùn)行時(shí)解耦,將原來(lái)完全面向 Pod 級(jí)別的內(nèi)部接口拆分成面向 SandboxContainer 的 gRPC 接口,并將鏡像管理和容器管理分離到不同的服務(wù)。

          實(shí)現(xiàn)了 CRI 接口的容器運(yùn)行時(shí)通常稱為 CRI shim, 這是一個(gè) gRPC Server,監(jiān)聽在本地的 unix socket  上;而 kubelet 作為 gRPC 的客戶端來(lái)調(diào)用 CRI 接口,來(lái)進(jìn)行Pod  和容器、鏡像的生命周期管理。另外,容器運(yùn)行時(shí)需要自己負(fù)責(zé)管理容器的網(wǎng)絡(luò),推薦使用 CNI。

          提出了CRI標(biāo)準(zhǔn)以后,意味著在新的版本里需要使用新的連接方式與docker通信,為了兼容以前的版本,k8s提供了針對(duì)docker的CRI實(shí)現(xiàn),也就是kubelet包下的dockershim包,dockershim是一個(gè)grpc服務(wù),監(jiān)聽一個(gè)端口供kubelet連接,dockershim收到kubelet的請(qǐng)求后,將其轉(zhuǎn)化為REST API請(qǐng)求,再發(fā)送給docker daemon。

          Kubernetes中的容器運(yùn)行時(shí)組成

          按照不同的功能可以分為四個(gè)部分:

          1. kubelet 中容器運(yùn)行時(shí)的管理,kubeGenericRuntimeManager,它管理與CRI shim通信的客戶端,完成容器和鏡像的管理(代碼位置:pkg/kubelet/kuberuntime/kuberuntime_manager.go);
          2. 容器運(yùn)行時(shí)接口CRI,包括了容器運(yùn)行時(shí)客戶端接口與容器運(yùn)行時(shí)服務(wù)端接口;
          3. CRI shim客戶端,kubelet持有,用于與CRI shim服務(wù)端進(jìn)行通信;
          4. CRI shim服務(wù)端,即具體的容器運(yùn)行時(shí)實(shí)現(xiàn),包括 kubelet 內(nèi)置的 dockershim (代碼位置:pkg/kubelet/dockershim)以及外部的容器運(yùn)行時(shí)如 cri-containerd(用于支持容器引擎containerd)、rktlet(用于支持容器引擎rkt)等。

          CRI架構(gòu)圖

          在 CRI 之下,包括兩種類型的容器運(yùn)行時(shí)的實(shí)現(xiàn):

          1. kubelet內(nèi)置的 dockershim,實(shí)現(xiàn)了 Docker 容器引擎的支持以及 CNI 網(wǎng)絡(luò)插件(包括 kubenet)的支持。dockershim代碼內(nèi)置于kubelet,被kubelet調(diào)用,讓dockershim起獨(dú)立的server來(lái)建立CRI shim,向kubelet暴露grpc server;
          2. 外部的容器運(yùn)行時(shí),用來(lái)支持 rkt、containerd 等容器引擎的外部容器運(yùn)行時(shí)。

          CRI shim server接口圖示

          CRI相關(guān)初始化

          kubelet中CRI相關(guān)初始化邏輯如下:

          1. 當(dāng)kubelet選用dockershim作為容器運(yùn)行時(shí),則初始化并啟動(dòng)容器運(yùn)行時(shí)服務(wù)端dockershim(初始化dockershim過程中也會(huì)初始化網(wǎng)絡(luò)插件CNI);
          2. 初始化容器運(yùn)行時(shí)CRI shim客戶端(用于調(diào)用CRI shim服務(wù)端:內(nèi)置的容器運(yùn)行時(shí)dockershim或remote容器運(yùn)行時(shí));
          3. 初始化kubeGenericRuntimeManager,用于容器運(yùn)行時(shí)的管理。初始化完成后,后續(xù)kubelet對(duì)容器以及鏡像的相關(guān)操作都會(huì)通過該結(jié)構(gòu)體持有的CRI shim客戶端,與CRI shim服務(wù)端進(jìn)行通信來(lái)完成。

          原文鏈接:https://www.cnblogs.com/lianngkyle/p/15086099.html?utm_source=pocket_mylist


          你可能還喜歡

          點(diǎn)擊下方圖片即可閱讀

          騷操作,這款工具可以把Kubernetes集群打包成一個(gè)鏡像

          云原生是一種信仰 ??

          關(guān)注公眾號(hào)

          后臺(tái)回復(fù)?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具,只需一條命令,連 ssh 都不需要!



          點(diǎn)擊 "閱讀原文" 獲取更好的閱讀體驗(yàn)!


          發(fā)現(xiàn)朋友圈變“安靜”了嗎?

          瀏覽 71
          點(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>
                  亚洲AV毛片在线观看。。 | 一道本无吗一区 | 日韩无码人妻一区二区 | 亚洲中文字幕第一 | 日韩视频一区二区三区在线播放免费观看 |