kubernetes/k8s CRI分析-容器運行時接口分析


關聯博客:kubernetes/k8s CSI分析-容器存儲接口分析
kubernetes/k8s CNI分析-容器網絡接口分析

概述

kubernetes的設計初衷是支持可插拔架構,從而利於擴展kubernetes的功能。在此架構思想下,kubernetes提供了3個特定功能的接口,分別是容器網絡接口CNI、容器運行時接口CRI和容器存儲接口CSIkubernetes通過調用這幾個接口,來完成相應的功能。

下面我們來對容器運行時接口CRI來做一下介紹與分析。

在本文中,會對CRI是什么、為什么要有CRICRI系統架構做一下介紹,以及k8sCRI進行相關操作的流程分析,包括了pod創建、刪除等操作。

CRI是什么

CRI是Container Runtime Interface(容器運行時接口)的簡寫。

CRI解耦了kubelet與容器運行時,讓kubelet無需重新編譯就可以支持多種容器運行時。

kubelet將通過CRI接口來跟第三方容器運行時進行通信,來操作容器與鏡像。

實現了 CRI 接口的容器運行時通常稱為 CRI shim, 這是一個 gRPC Server,監聽在本地的 unix socket 上;而 kubelet 作為 gRPC 的客戶端來調用 CRI 接口,來進行Pod 和容器、鏡像的生命周期管理。另外,容器運行時需要自己負責管理容器的網絡,推薦使用 CNI。

圖1:CRI shim通信圖

提出了CRI標准以后,意味着在新的版本里需要使用新的連接方式與docker通信,為了兼容以前的版本,k8s提供了針對docker的CRI實現,也就是kubelet包下的dockershim包,dockershim是一個grpc服務,監聽一個端口供kubelet連接,dockershim收到kubelet的請求后,將其轉化為REST API請求,再發送給docker daemon

圖2:dockershim通信圖

為什么要有CRI

在1.5以前的版本中,k8s依賴於docker,為了支持不同的容器運行時,如rktcontainerd等,kubelet從1.5開始加入了CRI標准,它將 Kubelet 與容器運行時解耦,將原來完全面向 Pod 級別的內部接口拆分成面向 SandboxContainer 的 gRPC 接口,並將鏡像管理和容器管理分離到不同的服務,方便后續其他容器運行時與k8s對接。

Kubernetes中的容器運行時組成

按照不同的功能可以分為四個部分:
(1)kubelet 中容器運行時的管理,kubeGenericRuntimeManager,它管理與CRI shim通信的客戶端,完成容器和鏡像的管理(代碼位置:pkg/kubelet/kuberuntime/kuberuntime_manager.go);
(2)容器運行時接口CRI,包括了容器運行時客戶端接口與容器運行時服務端接口;
(3)CRI shim客戶端,kubelet持有,用於與CRI shim服務端進行通信;
(4)CRI shim服務端,即具體的容器運行時實現,包括 kubelet 內置的 dockershim (代碼位置:pkg/kubelet/dockershim)以及外部的容器運行時如 cri-containerd(用於支持容器引擎containerd)、rktlet(用於支持容器引擎rkt)等。

CRI架構圖

在 CRI 之下,包括兩種類型的容器運行時的實現:
(1)kubelet內置的 dockershim,實現了 Docker 容器引擎的支持以及 CNI 網絡插件(包括 kubenet)的支持。dockershim代碼內置於kubelet,被kubelet調用,讓dockershim起獨立的server來建立CRI shim,向kubelet暴露grpc server;
(2)外部的容器運行時,用來支持 rktcontainerd 等容器引擎的外部容器運行時。

kubelet中CRI相關的源碼分析

kubelet的CRI源碼分析包括如下幾部分:
(1)kubelet CRI相關啟動參數分析;
(2)kubelet CRI相關interface/struct分析;
(3)kubelet CRI初始化分析;
(4)kubelet調用CRI創建pod分析;
(5)kubelet調用CRI刪除pod分析。

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

基於tag v1.17.4

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

1.kubelet組件CRI相關啟動參數分析

kubelet組件CRI相關啟動參數相關代碼如下:

// 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組件啟動參數的默認值在NewKubeletFlags函數中設置。

// 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相關啟動參數的默認值在NewContainerRuntimeOptionsNewMainKubelet函數中設置。

// 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
		}
	}
	...
}

下面來簡單分析幾個比較重要的CRI相關啟動參數:
(1)--container-runtime:指定kubelet要使用的容器運行時,可選值dockerremoterkt (deprecated),默認值為docker,即使用kubelet內置的容器運行時dockershim。當需要使用外部容器運行時,該參數配置為remote,並設置--container-runtime-endpoint參數值為監聽的 unix socket位置。
(2)--runtime-cgroups:容器運行時使用的cgroups,可選值。
(3)--docker-endpoint:docker暴露服務的socket地址,默認值為unix:///var/run/docker.sock,該參數配置當且僅當--container-runtime參數值為docker時有效。
(4)--pod-infra-container-image:pod sandbox的鏡像地址,默認值為k8s.gcr.io/pause:3.1,該參數配置當且僅當--container-runtime參數值為docker時有效。
(5)--image-pull-progress-deadline:容器鏡像拉取超時時間,默認值為1分鍾,該參數配置當且僅當--container-runtime參數值為docker時有效。
(6)--experimental-dockershim:設置為true時,啟用dockershim模式,只啟動dockershim,默認值為false,該參數配置當且僅當--container-runtime參數值為docker時有效。
(7)--experimental-dockershim-root-directorydockershim根目錄,默認值為/var/lib/dockershim,該參數配置當且僅當--container-runtime參數值為docker時有效。
(8)--container-runtime-endpoint:容器運行時的endpoint,linux中默認值為unix:///var/run/dockershim.sock,注意與上面的--docker-endpoint區分開來。
(9)--image-service-endpoint:鏡像服務的endpointlinux中默認值為unix:///var/run/dockershim.sock

2.kubelet CRI相關interface/struct分析

CRI相關接口

(1)RuntimeService interface:CRI shim客戶端-容器運行時接口;
代碼位置: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服務端-容器運行時接口;
代碼位置:staging/src/k8s.io/cri-api/pkg/apis/runtime/v1alpha2/api.pb.go

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

(5)CRIService interface:包括了RuntimeServiceServer interfaceImageServiceServer interface與CRI shim服務端啟動方法,所以其包括了一個CRI shim服務端需要實現的所有接口方法;
代碼位置:pkg/kubelet/dockershim/docker_service.go

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

說明:RuntimeService interfaceRuntimeServiceServer interfaceImageManagerService interfaceImageServiceServer interface中的接口方法是相同的,它們之間的區別只是一個用於CRI shim客戶端,一個用於CRI shim服務端。容器運行時接口負責管理 Pod 和容器的生命周期,容器鏡像接口負責管理容器鏡像的生命周期。

CRI相關結構體

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

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

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

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

(5)DockerServer struct:代表了dockershim(kubelet內置的CRI shim)的服務端,其實現了CRIService interface
代碼位置:pkg/kubelet/dockershim/remote/docker_server.go

CRI shim server接口圖示

RuntimeServiceServer

RuntimeServiceServer 提供了的接口,按照功能可以划分為四組:
(1)PodSandbox 的管理接口:PodSandbox 是對 Kubernete Pod 的抽象,用來給容器提供一個隔離的環境,並提供網絡等共享的命名空間;

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

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

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

ImageServiceServer

ImageServiceServer提供了 5 個接口,用於管理容器鏡像。

下面會對上面提到的接口/結構體做分析。

2.1 RuntimeService interface

RuntimeService 負責管理 Pod 和容器的生命周期,是CRI shim客戶端需要實現的容器運行時接口。

RuntimeService interface包含了RuntimeVersionerContainerManagerPodSandboxManagerContainerStatsManager接口,下面對對這些接口一一做介紹。

容器運行時會實現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負責返回容器運行時的名稱、版本以及 API 版本信息,只有一個接口函數 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包含了對container(業務容器)進行操作的一些方法,如CreateContainer(創建容器)、StartContainer(啟動容器)、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包含了對pod sandboxpause container)進行操作的一些方法,如RunPodSandbox(創建並啟動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包含了對容器統計數據的查詢接口,如ContainerStatsListContainerStats

// 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負責管理鏡像的生命周期,是CRI shim客戶端需要實現的鏡像接口。

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

// 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服務端必須實現的一些方法,其中包括了RuntimeServiceServer interface(容器運行時操作相關方法)、ImageServiceServer interface(鏡像操作相關方法)以及CRI shim服務端啟動方法。

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

實現了CRI shim客戶端-容器運行時接口RuntimeService interface,持有與CRI shim容器運行時服務端通信的客戶端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

實現了CRI shim客戶端-容器鏡像接口ImageManagerService interface,持有與CRI shim容器鏡像服務端通信的客戶端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內置的CRI shim)的服務端,其實現了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相關初始化

kubelet中CRI相關初始化邏輯如下:
(1)當kubelet選用dockershim作為容器運行時,則初始化並啟動容器運行時服務端dockershim(初始化dockershim過程中也會初始化網絡插件CNI);
(2)初始化容器運行時CRI shim客戶端(用於調用CRI shim服務端:內置的容器運行時dockershim或remote容器運行時);
(3)初始化kubeGenericRuntimeManager,用於容器運行時的管理。初始化完成后,后續kubelet對容器以及鏡像的相關操作都會通過該結構體持有的CRI shim客戶端,與CRI shim服務端進行通信來完成。

CRI初始化的調用鏈

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函數中CRI相關邏輯:
(1)初始化並啟動內置容器運行時服務端dockershim:根據containerRuntime的值(kubelet啟動參數--container-runtime),如果是docker,則初始化並啟動docker CRI shim即kubelet內置容器運行時dockershim,暴露grpc socket,如果是remote,則不做初始化啟動操作。
(2)調用getRuntimeAndImageServices:初始化容器運行時CRI shim客戶端,包括容器運行時客戶端runtimeClient以及容器鏡像客戶端imageClient
(3)調用kuberuntime.NewKubeGenericRuntimeManager,以及klet賦值:初始化kubeGenericRuntimeManager struct,用於容器運行時的管理。初始化完成后,后續kubelet對容器以及鏡像的相關操作都會通過該結構體持有的CRI shim客戶端,與CRI shim服務端進行通信來完成。

// pkg/kubelet/kubelet.go
func NewMainKubelet(...) {
    ...
    switch containerRuntime {
    // (1)初始化並啟動內置容器運行時服務端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)初始化容器運行時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```,用於容器運行時的管理
	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 初始化並啟動內置容器運行時服務端dockershim

這里對變量containerRuntime值等於docker時做分析,即kubelet啟動參數--container-runtime值為docker,這時kubelet會使用內置的CRI shim即dockershim作為容器運行時,dockershim調用docker進行容器以及鏡像的相關操作。

初始化並啟動dockershim主要邏輯如下:
(1)調用dockershim.NewDockerService:新建並初始化dockershim服務端,包括初始化docker client、初始化cni網絡配置等操作;
(2)調用dockerremote.NewDockerServerserver.Start:啟動dockershim,暴露服務socket。

3.1.1 dockershim.NewDockerService

新建並初始化dockershim服務端,主要邏輯如下:
(1)調用NewDockerClientFromConfig:創建docker的客戶端-client對象,包含了我們常用的docker run,docker images等所有操作調用;
(2)構建dockerService struct
(2)初始化CNI網絡配置(CNI網絡配置初始化在專門進行CNI分析的博文再詳細講解)。

// 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)創建docker的客戶端
	client := NewDockerClientFromConfig(config)

	c := libdocker.NewInstrumentedInterface(client)

	checkpointManager, err := checkpointmanager.NewCheckpointManager(filepath.Join(dockershimRootDir, sandboxCheckpointDir))
	if err != nil {
		return nil, err
	}
    // (2)構建```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網絡配置
	// 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函數主要是建立與docker通信的客戶端。其中config結構體里,dockerEndpoint的值來自於kubelet啟動參數--container-runtime-endpoint的配置,默認是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 啟動dockershim,暴露服務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 初始化容器運行時CRI shim客戶端

getRuntimeAndImageServices函數主要邏輯:
(1)調用remote.NewRemoteRuntimeService函數:實例化容器相關操作的CRI shim客戶端-容器運行時客戶端runtimeClient,實現了上述CRI相關interface/struct分析中的RuntimeService接口(CRI shim客戶端接口);
(2)調用remote.NewRemoteImageService函數:實例化鏡像相關操作的CRI shim客戶端-容器鏡像客戶端imageClient,實現了上述CRI相關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 nil, nil, err
	}
	is, err := remote.NewRemoteImageService(remoteImageEndpoint, runtimeRequestTimeout.Duration)
	if err != nil {
		return nil, nil, err
	}
	return rs, is, err
}
3.2.1 remote.NewRemoteRuntimeService

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

主要邏輯:根據kubelet啟動參數--container-runtime-endpoint或使用默認值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函數作用:實例化鏡像相關操作的CRI shim客戶端-容器鏡像客戶端imageClient,實現了上述CRI相關interface/struct分析中的ImageManagerService接口(CRI shim客戶端接口)。

主要邏輯:根據kubelet啟動參數--image-service-endpoint或使用默認值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,用於容器運行時的管理

kuberuntime.NewKubeGenericRuntimeManager函數主要是初始化kubeGenericRuntimeManager struct,而kubeGenericRuntimeManager struct是對KubeGenericRuntime interface的實現。kubeGenericRuntimeManager是kubelet中容器運行時的管理者,管理着CRI shim客戶端,負責與CRI shim服務端 交互,完成容器和鏡像的管理。

初始化完成后,后續kubelet對容器以及鏡像的相關操作都會通過該結構體持有的CRI shim客戶端,與CRI shim服務端進行通信來完成。

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

總結

該博文先對CRI做了介紹,然后對kubelet CRI相關源碼進行分析,包括kubelet組件CRI相關啟動參數分析、CRI相關interface/struct分析、CRI相關初始化分析3個部分,剩下的其他部分分析,將在下一篇CRI博文里做分析。

CRI介紹

CRI,全稱Container Runtime Interface,容器運行時接口。

在1.5以前的版本中,k8s依賴於docker,為了支持不同的容器運行時,如rktcontainerd等,kubelet從1.5開始加入了CRI標准,它將 Kubelet 與容器運行時解耦,將原來完全面向 Pod 級別的內部接口拆分成面向 SandboxContainer 的 gRPC 接口,並將鏡像管理和容器管理分離到不同的服務。

實現了 CRI 接口的容器運行時通常稱為 CRI shim, 這是一個 gRPC Server,監聽在本地的 unix socket 上;而 kubelet 作為 gRPC 的客戶端來調用 CRI 接口,來進行Pod 和容器、鏡像的生命周期管理。另外,容器運行時需要自己負責管理容器的網絡,推薦使用 CNI。

提出了CRI標准以后,意味着在新的版本里需要使用新的連接方式與docker通信,為了兼容以前的版本,k8s提供了針對docker的CRI實現,也就是kubelet包下的dockershim包,dockershim是一個grpc服務,監聽一個端口供kubelet連接,dockershim收到kubelet的請求后,將其轉化為REST API請求,再發送給docker daemon

Kubernetes中的容器運行時組成

按照不同的功能可以分為四個部分:
(1)kubelet 中容器運行時的管理,kubeGenericRuntimeManager,它管理與CRI shim通信的客戶端,完成容器和鏡像的管理(代碼位置:pkg/kubelet/kuberuntime/kuberuntime_manager.go);
(2)容器運行時接口CRI,包括了容器運行時客戶端接口與容器運行時服務端接口;
(3)CRI shim客戶端,kubelet持有,用於與CRI shim服務端進行通信;
(4)CRI shim服務端,即具體的容器運行時實現,包括 kubelet 內置的 dockershim (代碼位置:pkg/kubelet/dockershim)以及外部的容器運行時如 cri-containerd(用於支持容器引擎containerd)、rktlet(用於支持容器引擎rkt)等。

CRI架構圖

在 CRI 之下,包括兩種類型的容器運行時的實現:
(1)kubelet內置的 dockershim,實現了 Docker 容器引擎的支持以及 CNI 網絡插件(包括 kubenet)的支持。dockershim代碼內置於kubelet,被kubelet調用,讓dockershim起獨立的server來建立CRI shim,向kubelet暴露grpc server;
(2)外部的容器運行時,用來支持 rktcontainerd 等容器引擎的外部容器運行時。

CRI shim server接口圖示

CRI相關初始化

kubelet中CRI相關初始化邏輯如下:
(1)當kubelet選用dockershim作為容器運行時,則初始化並啟動容器運行時服務端dockershim(初始化dockershim過程中也會初始化網絡插件CNI);
(2)初始化容器運行時CRI shim客戶端(用於調用CRI shim服務端:內置的容器運行時dockershim或remote容器運行時);
(3)初始化kubeGenericRuntimeManager,用於容器運行時的管理。初始化完成后,后續kubelet對容器以及鏡像的相關操作都會通過該結構體持有的CRI shim客戶端,與CRI shim服務端進行通信來完成。

關聯博客:kubernetes/k8s CSI分析-容器存儲接口分析


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM