kubernetes/k8s CRI分析-容器執行時介面分析

良凱爾發表於2021-08-01

關聯部落格:kubernetes/k8s CSI分析-容器儲存介面分析

概述

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分析-容器儲存介面分析

相關文章