国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

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

共 54592字,需瀏覽 110分鐘

 ·

2021-08-16 02:29


概述

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

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

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

CRI是什么

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

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

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

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

CRI shim 通信圖

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

dockershim 通信圖

為什么要有CRI

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

Kubernetes中的容器運行時組成

按照不同的功能可以分為四個部分:

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

CRI架構(gòu)圖

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

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

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

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

  1. kubelet CRI相關(guān)啟動參數(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分析。

因篇幅原因,本篇博文先對前三部分做分析,下一篇博文再對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)啟動參數(shù)分析

kubelet組件CRI相關(guān)啟動參數(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組件啟動參數(shù)的默認值在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)啟動參數(shù)的默認值在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
  }
 }
 ...
}

下面來簡單分析幾個比較重要的CRI相關(guān)啟動參數(shù):

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

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

CRI相關(guān)接口

  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服務(wù)端-容器運行時接口;代碼位置: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ù)端啟動方法,所以其包括了一個CRI shim服務(wù)端需要實現(xiàn)的所有接口方法;代碼位置:pkg/kubelet/dockershim/docker_service.go

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

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

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

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

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

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

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

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

CRI shim server接口圖示

RuntimeServiceServer

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

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

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

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

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

ImageServiceServer

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

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

2.1 RuntimeService interface

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

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

容器運行時會實現(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負責(zé)返回容器運行時的名稱、版本以及 API 版本信息,只有一個接口函數(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包含了對container(業(yè)務(wù)容器)進行操作的一些方法,如CreateContainer(創(chuàng)建容器)、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(創(chuà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包含了對容器統(tǒng)計數(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負責(zé)管理鏡像的生命周期,是CRI shim客戶端需要實現(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ù)端必須實現(xiàn)的一些方法,其中包括了RuntimeServiceServer interface(容器運行時操作相關(guān)方法)、ImageServiceServer interface(鏡像操作相關(guān)方法)以及CRI shim服務(wù)端啟動方法。

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

實現(xiàn)了CRI shim客戶端-容器運行時接口RuntimeService interface,持有與CRI shim容器運行時服務(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

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

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. 初始化并啟動內(nèi)置容器運行時服務(wù)端dockershim:根據(jù)containerRuntime的值(kubelet啟動參數(shù)--container-runtime),如果是docker,則初始化并啟動docker CRI shim即kubelet內(nèi)置容器運行時dockershim,暴露grpc socket,如果是remote,則不做初始化啟動操作。
  2. 調(diào)用getRuntimeAndImageServices:初始化容器運行時CRI shim客戶端,包括容器運行時客戶端runtimeClient以及容器鏡像客戶端imageClient
  3. 調(diào)用kuberuntime.NewKubeGenericRuntimeManager,以及klet賦值:初始化kubeGenericRuntimeManager struct,用于容器運行時的管理。初始化完成后,后續(xù)kubelet對容器以及鏡像的相關(guān)操作都會通過該結(jié)構(gòu)體持有的CRI shim客戶端,與CRI shim服務(wù)端進行通信來完成。
// pkg/kubelet/kubelet.go
func NewMainKubelet(...) {
    ...
    switch containerRuntime {
    //1. 初始化并啟動內(nèi)置容器運行時服務(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. 初始化容器運行時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 初始化并啟動內(nèi)置容器運行時服務(wù)端dockershim

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

初始化并啟動dockershim主要邏輯如下:

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

3.1.1 dockershim.NewDockerService

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

  1. 調(diào)用NewDockerClientFromConfig:創(chuàng)建docker的客戶端-client對象,包含了我們常用的docker run,docker images等所有操作調(diào)用;
  2. 構(gòu)建dockerService struct;
  3. 初始化CNI網(wǎng)絡(luò)配置(CNI網(wǎng)絡(luò)配置初始化在專門進行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. 創(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的值來自于kubelet啟動參數(shù)--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,暴露服務(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 初始化容器運行時CRI shim客戶端

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

  1. 調(diào)用remote.NewRemoteRuntimeService函數(shù):實例化容器相關(guān)操作的CRI shim客戶端-容器運行時客戶端runtimeClient,實現(xiàn)了上述CRI相關(guān)interface/struct分析中的RuntimeService接口(CRI shim客戶端接口);
  2. 調(diào)用remote.NewRemoteImageService函數(shù):實例化鏡像相關(guān)操作的CRI shim客戶端-容器鏡像客戶端imageClient,實現(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ù)作用:實例化容器相關(guān)操作的CRI shim客戶端-容器運行時客戶端runtimeClient,實現(xiàn)了上述CRI相關(guān)interface/struct分析中的RuntimeService接口(CRI shim客戶端接口)。

主要邏輯:根據(jù)kubelet啟動參數(shù)--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函數(shù)作用:實例化鏡像相關(guān)操作的CRI shim客戶端-容器鏡像客戶端imageClient,實現(xiàn)了上述CRI相關(guān)interface/struct分析中的ImageManagerService接口(CRI shim客戶端接口)。

主要邏輯:根據(jù)kubelet啟動參數(shù)--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函數(shù)主要是初始化kubeGenericRuntimeManager struct,而kubeGenericRuntimeManager struct是對KubeGenericRuntime interface的實現(xiàn)。kubeGenericRuntimeManager是kubelet中容器運行時的管理者,管理著CRI shim客戶端,負責(zé)與CRI shim服務(wù)端 交互,完成容器和鏡像的管理。

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

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

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

CRI介紹

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

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

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

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

Kubernetes中的容器運行時組成

按照不同的功能可以分為四個部分:

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

CRI架構(gòu)圖

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

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

CRI shim server接口圖示

CRI相關(guān)初始化

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

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

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


你可能還喜歡

點擊下方圖片即可閱讀

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

云原生是一種信仰 ??

關(guān)注公眾號

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



點擊 "閱讀原文" 獲取更好的閱讀體驗!


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

瀏覽 71
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報
評論
圖片
表情
推薦
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 久久不雅视频| 欧美嗯啊| 国产精品视频色| 午夜午夜福利理论片在线播放| 久草福利在线观看| 午夜av福利| 天天日夜夜拍| 中文免费高清在线| 国产乱码| 中文字幕在线观看第一页| 欧美AAA视频| 欧美后门菊门交3p| 青青草原在线视频免费观看| 亚洲素人无码| 亚洲无码日| 欧美精产国品一二三| 久草香蕉视频| 国产成人无码AⅤ片免费播放| 免费三级片网址| 特级西西444www高清大胆免费看| 欧美综合亚洲图片综合区| 玉米地一级婬片A片| 无码一卡| 色综合天天综合成人网| 影音先锋av在线资源| 啪啪成人视频| 欧美理伦| AV天堂免费播放| 国产第二页| 五月婷婷一区| 麻豆激情| 在线国产中文字幕| 在线三级片视频| 亚洲婷婷精品国产成人| 久久久免费黄色视频| 国产无码一区二区| 熟女人妻人妻の视频| 91精品国产麻豆国产自产在线| 五月天成人小说| 欧美日韩中文字幕视频| 国产精品自产拍| 国产美女18毛片水真多| 久草黄色电影在线观看| 亚洲AV免费在线观看| 人妻FrXXeeXXee护士| 一级一级一级做a免费一级做a| 久久国产毛片| 少妇中文字幕| 精品久久免费一区二区三区 | 超碰黄片| 人人摸人人干人人操| 久久狠狠干| 激情五月婷婷五月| 德美日三级片在线观看| 婷婷伊人中文字幕| 国产精品一二三区| 国产一级A片视频| 伊人网av| 五月婷婷五月丁香| 思思热思思操免费视频| 免费观看色情视频| 亚洲乱码日产精品BD在线观看| 99热中文字幕在线观看| 日韩人妻中文| 久久视频一二| 午夜激情乱伦| 天堂在线社区| 中文字幕丰满熟妇人妻| 国产操逼免费| 亚洲激情内射| 国产特級黃色大片| 亚洲系列中文字幕| 欧美一级黄色电影| 色噜噜狠狠一区二区三区| 伊人影院在线看| 成人av影院| 久久嫩草精品久久久久精| 国产又爽又黄免费| 国产福利视频| 丁香五月婷婷啪啪| www,操逼| 美女久久| AV中文字幕电影| 欧美级毛片一夜| 日韩欧美国产一区二区| 91最新网址| 国产精品毛片VA一区二区三区| 殴美A片| 日本高清版色视频| 色婷婷18禁| 国产在线观看无码免费视频| 国产无码AV在线| 大香蕉免费网站| 大草AV| 国产不卡在线观看| 综合五月婷婷| 热久久中文字幕| 色就色欧美| 国产精品你懂的| 国产a精品| 一区二区精品| 欧美一区二区三区不卡| 国产乱码精品一区二区三区的特点| 无码人妻中文字幕| 污视频在线观看免费| 免费91视频| 国产精品视频免费观看| 精品福利视频导航| 精品中文字幕在线观看| 亚洲人成电影网| 性感成人在线| 久久熟女| 无码人妻AⅤ一区二区三区| 欧美精品在线观看| 亚洲AV无码成人精品国产五月天| 亚洲国产三级片| 亚洲av| 国产熟女露脸普通话对白| 欧美激情无码炮击| 欧美色大香蕉| 永久免费黄色视频网站| 婷婷五月中文字幕| 午夜av免费在线| 久久日av| 杨门女将婬乱史1—6| 黄色网址av| 翔田千里無碼破解| 欧美一级夜夜爽| 成人网站免费在线观看| 北京熟妇搡BBBB搡BBBB| 有免费的欧美操逼视频吗| 日韩中文在线播放| 欧美黄色性爱| 国产成人免费视频在线| 欧美性性生交XXXXX无码| 日韩毛片在线播放| 久久久成人免费电影| 久久综合热| 大香蕉三级片| 天天爽日日澡AAAA片| 日韩成人免费视频| 亚洲天堂女| 先锋影音资源站av每日资源在线| 日韩和的一区二区| 欧美日韩免费观看视频| 操逼视频在线免费看| 高清无码免费观看视频| 中文字幕高清无码在线| 欧美一区二区三区成人片在线| 精品视频91| 丰满少妇一区二区三区| 九九九亚洲| 人妻在线观看| 色秘乱码一区二区三区| 91精品酒店视频| 国产精品免费人成网站酒店| 午夜老司机福利一二三区| 亚洲欧洲无码视频| 欧美成年人网站| 北条麻妃被躁57分钟视频在线 | 成人AV无码| 黄色成人网站在线观看| 超碰小说| 熟女3p| 北条麻妃青青久久| 2014av天堂网| 熟女视频91| 97视频在线免费观看| 人人干人人干| 中文字幕亚洲在线| 在线中文字幕在线观看| 操逼操逼操| 成人精品福利| 中文在线一区| 国产一卡二卡在线| 国精品91无码一区二区三区在线 | 日本老妇操屄视频| 停停六综合| 91丨牛牛丨国产| 大香蕉在线看| 成人无码免费看| 国产精品日韩无码| 日韩欧美亚洲| 免费毛片视频| 无码一区二区av| 国产一区二区三区四区五区六区七区 | 淫香淫色天天影视| 国产口爆在线观看| 人人肏人人摸| 亚洲中文在线播放| 老鸭窝av免费入口在线观看| AV大全在线免费观看| 日本欧美一区二区三区| 国产三级小视频| 亚洲无码黄色电影| 国产精品无码一区二区三区免费 | a片在线免费| 亲子伦一区二区三区观看方式| 国产做受91电影| 国产色五月| 国产又爽又黄免费网站校园里| 欧美人操逼一二区| 熟女视频一区二区| 日韩在线你懂的| 韩日中文字幕| 国产a片| 木下凛凛子AV888AV在线观看 | 波多野59部无码喷潮| 五月天成人网址| 91精品视频在线播放| 91AV视频在线观看| 成人久操| 亚洲精品影视| 久久99人妻无码精品一区| 激情AV在线| 黄片免费视频| 国产高清激情| 特级西西人体444www高清大胆| 尤物精品| 亚洲vs无码秘蜜桃少妇| 91在线无码精品国产三年| 亚洲乱码在线| 天天搞天天搞| 97超碰在| 一本道不卡色色| 91av在线免费播放| 男女无套在线观看免费| 日本熟妇一区二区三区| 人人做人人爱人人做人人乐的意思| 一本色道精品久久一区二区三区| 污视频网站免费在线观看| 亚洲熟妇视频| 五月激情丁香| 一区二区三区四区不卡| 婷婷五月综合中文字幕| 日韩精品人妻中文字幕第4区| 日本成人中文字幕| 成人网一区二区| 蜜桃91精品| 京东一热本色道久久爱| 日韩视频免费观看| 黄色A片网| 日韩A∨| A在线免费观看| 精品无码一区二区三区四区| 无码任你操| 久热免费| 啪啪网站免费| 婷婷综合缴情亚洲另类在线| 综合伊人大香蕉| 亚洲涩情91日韩一区二区| 黄色片在线看| 日韩一区二区三区在线视频| 日韩天堂在线观看| 亚洲骚逼| 一级黄色录相片| 亚洲免费黄色片| JiZZjiZZ亚洲成熟熟妇| 国产一级婬女AAAA片季秀英| 国产精品嫩草久久久久yw193| 欧美操BB| 亚洲高清免费视频| 未满十八18禁止免费无码网站 | 乱伦乱码| 亚洲黄片免费| 欧洲成人在线播放| 精品丰满人妻一区二区三区免费观| 国产精品无码av| 人妻一区二区三区| 亚洲永久天堂| 日韩啪啪网站| 亚洲群交| 国产操逼网址| 久久丝袜视频| 91视频在线观看| 欧美成人A片AAA片在线播放| 精品无码人妻一区二区| 亚洲AV无码乱码| 欧美亚洲日韩一区| 亚洲国产成人91PORN| 日本Sm/调教/捆绑/紧缚| 91色色影院| 在线观看av网站| 爱爱动态图| 亚洲网站免费在线观看| 91无码AⅤ在线| 黄色视频网站在线| 日韩中字无码| 成人小说一区二区三区| 日本在线播放| 天堂视频在线观看亚洲美女| 日本综合久久| 精品蜜桃一区内容| 色黄视频在线观看| 奇米av| 日韩操逼图| 无码av一区二区| 亚洲人妻AV| 好吊妞操| 大香蕉精品在线视频| 日本精品视频| 一级二级三级毛片| 日本精品国产| 欧美啪啪视频| 亚洲中文欧美| 人妻日韩精品中文字幕| 大香蕉伊人色| 自拍av在线| 久久久国产一区二区三区| 毛片网站免费| 午夜天堂精品久久久久| av老鸭窝| 亚洲AV无码免费| 欧美性爱天天| 老司机视频在线视频18| 国产1区| 无码人妻AV一区| 狠狠撸狠狠操| 日本一区二区三区四区| 国产成人黄色| 亚洲成人毛片| 黄片高清无码在线观看| 久久久久成人片免费观看蜜芽| 欧美成人乱码一区二区三区| 北条麻妃被躁57分钟视频在线 | 日韩精品一区二区三区黄冈站长| 色色97| 成人一区视频| 欧美日韩精品在线观看| 曰韩精品| 欧美综合在线观看| 黄色一级片免费在线观看| 一区二区三区免费在线| 色人阁人妻中文字幕| 日韩精品视频一区二区三区| 在线成人网站| 蜜臀av网| 久久99精品视频| 日韩69视频| 神马午夜精品| 一本免费视频| 久久伊人草| 91久久久久| 能看的操逼视频| 99免费精品视频| 三级无码视频在线观看| 人妻熟女一区二区| 又粗又硬又爽18级A片| 亚洲色偷精品一区二区三区| 久久亚洲中文字幕乱码| 久久久高清无码| 欧美性爱视频网站| 亚洲无码婷婷| 国产无码免费视频| 成人毛片av| 亚洲www啪成人一区二区麻豆| 91亚洲国产成人精品一区二区三| H片在线免费观看| 性爱av在线观看| 白嫩外女BBWBBWBBW| 欧美特黄AAAAAA| 东京热av在线| 热久久在线| 少妇黄色视频| 中文字幕成人网站中文字幕| 狠狠躁夜夜躁人爽| 久久久无码电影| 99热这里只有精品1| AA免费视频| 欧美一级在线视频| 波多野结衣被操| av资源免费观看| 色大香蕉伊人| 中文字幕人成人乱码亚洲电影| 伊人久久大香线蕉| av资源站| 无码草| 久色国产| 欧美一级三级| 色九九九| 一级A片黄色| www.色老板| 四川美人搡BBw搡BBw| 日逼网址| 成人爱爱免费视频| 精品國產一區二區三區久久蜜月 | 欧美三级一级| 自慰喷水流白浆中文字幕| 免费成人黄色| 九九操比| 在线毛片网站| 五月天亚洲无码| 一本色道久久88加勒比| 蜜臀久久99精品久久久久久宅男 | 免费中文视频| 成年人视频免费看| 成人性爱毛片| 免费视频一区二区三区四区 | 天干天干天夜夜| 日韩aaa| 一级a一级a爰片免费| 午夜亚洲AV永久无码精品蜜芽 | 中文一级片| a级片在线观看| 日韩无码毛片| 亚洲v在线观看| 色老板在线视频| 精品人妻一区二区蜜桃视频| 91操操操| 亚洲高清在线观看视频| 日韩免费视频一区| 中文字幕永久在线| 亚洲精品久久久久avwww潮水| 女同一区二区三区| 色婷婷Av| 婷婷网五月天| 伊人免费| 成人性生活片| 免费在线国产| 特级毛片www| 五月婷婷导航| 天天肏| 国产精品成人无码专区| 亚洲色婷婷久久精品AV蜜桃| 欧美黄色片在线观看| 国产激情无码免费| 欧美性爱视频网站| 国产一级婬乱片AV片AAA毛片| 亚洲国产成人无码| 成人午夜无码福利视频| 成人午夜天堂| 日本高清色清di免费观看| 天天操天天操天天| 97av在线| 午夜探花| 91黄色视频网站| 日本色情视频网站| 久久久久三级片| 国产精品扒开腿做爽爽爽视频 | 91亚洲精华国产精华精华液| 欧美精品99| 日韩欧美国产精品综合嫩V| 成人AV在线一区二区| 欧美老熟妇乱大交XXXXX| 91无码一区二区三区| 亚洲日韩视频| 日韩日日日| 少妇搡BBBB搡BBB搡18禁| 日韩一区二区视频| 日本久久成人| 日韩精品三区| 九色PORN视频成人蝌蚪自拍| 五月天四房播播| 国产亚洲色婷婷| 日韩AV成人电影| 日韩成人AV在线播放| 九九色影院| 黄色三级av| 中文字幕五月久久婷婷| 九九这里有精品| 九九九九九九精品| 日韩一级无码毛片| 青青草大香蕉| 乱伦激情| 国产婷婷五月天| 日韩电影免费在线观看中文字幕| 啪一啪操一操| 91精品国产一区二区三区四区大 | 久草免费福利| 欧美色欲| 欧美成人大香蕉| 特级西西人体444www高清大胆 | 一区二区三级片| 精品欧美激情精品一区| 日本久久久| 国产成人精品777777| 亚洲人操逼| 操b在线观看| 九九热视频在线观看| 作爱免费视频| 亚洲色诱| 午夜精品久久久| www.色色网| 欧美日韩国产尤物主播精品| 俺去| 91碰| 女公务员人妻呻吟求饶| 五月天中文字幕| JlZZJLZZ亚洲美女18| 亚洲综合片| 久久午夜无码鲁丝片午夜精品| 亚洲成av人无码| 亚洲中文字幕日韩| 国产三级片网| 亚洲中文字幕网| 欧美性BBB槡BBB槡BBB| 自慰喷水在线观看| 久久电影五月天| A黄色片| 天天射天天操天天干| 色吊妞| 青春草在线观看| 日韩黄色在线| 首屈一指视频在线观看| 麻豆三级精品| 国产精品毛片久久久久久久| 久久99久久99久久99| 69av网站| 欧美操逼免费视频| 毛片一区二区三区| 91成人无码看片在线观看网址| 2021天天夜日| 成人黄色网址| 国产熟妇婬乱一区二区| 熟妇人妻丰满久久久久久久无码 | 黄色A毛片| 中字一区人妻水多多| AV天堂亚洲| 九九九在线视频| 中国美女一级黄片| 人妻少妇一区二区| 老熟女视频| 2025av天堂网| 男人视频网| 一级日韩一级欧美| 欧美日韩三级| 亚洲国产成人综合| a√天堂中文在线8| 精品国产香蕉| 国色天香一区二区| 日韩天堂在线| 精品麻豆| 亚洲成人精品一区二区| 久久久在线视频| 香蕉国产AV| 在线无码一区| 黄片天堂| 日本电影一区二区三区| 日韩国产成人在线| 亚洲国产精品成人综合色五月| 色伊人| 欧美精品一级片| 国产精品扒开腿做爽爽爽视频 | chinese搡老熟老妇人| 一本大道久久久久| 黄色特级aaa片| 国产福利91精品一区二区三区| 日本精品中文字幕| 亚洲成人免费在线| 久久久精品淫秽色情| 成人123区| 日韩va| 97香蕉久久国产超碰青草专区| 婷婷五月开心五月| 狼色视频| 国产精品扒开腿| 午夜福利干B在线免费小视频| a视频在线免费观看| 91超碰免费| 色色影音先锋| 人人操人人人| 最新日韩中文字幕| 丰臀肥逼高清视频电影播放| 免费一级a片| 免费无码国产| 在线观看黄色网| 亚洲高清电影| 久久久在线视频| 狠狠大香蕉| 亚洲无码久久久| 日韩一级免费视频| 性爱福利视频| 一级黄色在线| 五月天福利影院| 日本黄色视频。| 欧美精品一区二区少妇免费A片 | 黄色无码av| a√天堂资源中文8| 日韩免费性爱视频| 青青草免费福利视频| 日韩无码第一页| 亚洲一级黄| 亚洲AV永久无码精品| 亚洲欧洲av| 久久学生妹| 激情另类视频| 亚洲av大片| 五月天婷婷色播| 男人天堂手机视频| www.婷婷| 国产精品AV在线| 亚洲一级AV| 青娱乐老视频| 午夜天堂精品久久久| 五月天天| 青青久久91| 91久久久久久久91| 亚欧av无码| 山东熟妇搡BBBB搡BBBB| 五月婷婷激情网| 人妻久久久| 久久精品www人人爽人人| 亚洲无吗在线观看| 99re在线观看观看这里只有精品| 午夜私人福利| 人妖和人妖互交性XXXX视频| 五月播播| 成人电影亚洲天堂| 黄色免费网站| 五月天操逼| 三级小说| 九九性爱视频| 91人人妻人人澡| 中文字幕在线视频免费观看| 麻豆影音先锋| 在线99热| 大鸡巴久久久久久| 超碰97人妻| 2024男人天堂| www.无码视频| 一道本无码免费视频| 玖玖爱资源站| 欧美夜夜草视频| 伊人成人在线| 亚洲精品区| 国产高清在线观看| 国产天堂在线| 黄色片A片| 老熟女-ThePorn| 无码av免费精品一区二区三区| 桃色五月天| 激情综合久久| 日韩综合在线视频| 五月婷婷激情五月| 黄色三级av| 在线观看视频你懂的| 国产91白丝在线播放| 国产精品人妻无码久久久郑州天气网 | 国产成人大香蕉| 天堂久久久久| 婷婷婷色| 亚洲AV无码久久寂寞少妇多毛| 另类欧美| 无码一区二区黑人猛烈视频网站 | 天天澡日日久| 干妞网免费视频| 天天干天天在线观看| 俺去吔| 韩国精品一区| 欧美在线观看视频一区| 在线看操逼| 五月天亚洲激情| 日韩性爱一区二区| 91大神在线资源观看无广告| 国产嫩草久久久一二三久久免费观看 | 中日韩在线| 91中文视频| 欧美一区二区三区不卡| 一区二区三区四区免费看| 国产精品污www在线观看| 大香蕉a片| 12一15女人A片毛| www.五月丁香| 五月婷婷国产| 粉嫩av懂色av蜜臀av熟妇| 亚洲三级在线视频| 亚洲40p| 黄片无码免费观看| 亚洲高清无码免费观看| 毛片h| 日韩欧美123| 成人动漫一区二区| 91丨国产丨熟女熟女| 无码AV一区| 久久噜噜噜精品国产亚洲综合| 久久免费黄色| 国精品伦一区一区三区有限公司 | 国产不卡一区| 波多野成人无码精品视频| 操逼视频无码| 91精品国产综合久久久蜜臀九色 | 亚洲第一黄色视频| 青青草性爱| 国产精品每日更新| 草逼动态图| 美女福利视频| 国产TS变态重口人妖| 超碰在线视| 蜜臀精品一区二区三区| 成人肏屄视频| 91人人妻人人澡| 97国产精品视频| 高清无码视频在线播放| 中文字幕无码在线播放| 日韩高清无码一区| 国产三级网站| 欧美撒色逼撒| 性欧美成人播放77777| 日韩欧美国产黄色电影| 色五月天激情| 色婷婷狠狠操| 中文在线A∨在线| 国产A级视频| 国产精品不卡一区二区三区| 黄片无码免费观看| 国产精品成人99一区无码| 亚洲AV永久无码精品| 大香蕉网视频| 国产超碰在线| 成人毛片网| 成人国产精品免费观看| 亚洲激情精品| 成人才看的在线视频| 福利视频在线| 晚上碰视频| 国产精品内射视频| 亚洲调教| 免费无码进口视频| 中文字幕国产视频| 免费成人黄色| 中文字幕激情精品| 激情亚洲五月天| 亚洲色综合网| 天天爱天天插| 国产又粗又猛又爽又黄91精品| 国产成人免费视频在线| 日韩一级无码特黄AAA片| 国产成人精品电影| 97久久精品国产熟妇高清网| 中文字幕免费高清| 亚洲人内射片又| 1024手机在线观看| 激情五月天在线视频| 色视频免费观看| 亚洲AV成人无码AV小说| 91精品久久久久久久久| 成片免费观看视频大全| 久色伊人| 国产高清视频| 三级国产网站| 大陆搡BBBBB搡BBBBBB | 中文免费高清在线| 女人18片毛片90分钟免费明星| 国产乱伦内射| 国产真实乱婬A片三区高清蜜臀| 日皮视频免费| 亚洲三级网站| 北条麻妃一区二区三区在线观看 | 久久久无码精品亚洲日韩男男| 成人做爰黄级A片免费看土方| 激情白浆| A片网站在线观看| 久久成人导航| 青草视频在线播放| 三级网站在线| 国产精品无码久久久久成人app| 先锋影音男人资源站| 亚洲AV无码久久寂寞少妇多毛| 日韩无码性爱视频| 99久久精| 91黄色视频在线观看| 无码任你躁久久久久| 最新毛片网站〖网:.〗| 亚洲AV毛片成人精品网站| 国产av资源网| 巜痴漢電車~凌脔版2| 激情乱伦五月天| 婷婷五月天在线观看| 欧美操B在线| 中文无码AV在线| 无码免费毛片一区二区三区古代| 欧美国产日本| 无码人妻一区二区三区| 九九99电影| 东京热网站在线观看| 亚洲国产成人无码a在线播放| 91在线一区二区三区| 国内老熟妇对白HDXXXX| 天天做天天爱天天高潮| 精品一区二区三区毛片| 一起操在线视频| 91大香蕉伊人| 国产aⅴ激情无码久久久无码| 免费黄色A片| 亚洲欧美美国产| 国产av影音| 成人免费在线网站| 精品国产污污免费网站入口| 69视频网| 欧美精品A级片| 操逼视频试看| 在线视频99| 女人AV天堂| 国产精品揄拍一区二区| 久久er视频| 三级片无码在线| 俺去啦俺来也| 性久久久久久| 日鸡吧链接| 嫩BBB槡BBBB槡BBBB| 午夜av在线免费观看| 呦小BBBB小小BBBB| 韩日一级片| 一级片黄色免费| 成人777777免费视频色| 国产8区| 国产成人一区二区三区| 免费成人AV| 日韩人妻久久| 水蜜桃视频在线| 日韩一级无码视频| 老司机永久免费91| 日韩av综合| 欧美人妻中文字幕| 亲子乱AⅤ一区二区三区| 自慰喷水流白浆中文字幕| 免费无码婬片AAAAA片| 无码A级片| 成人视频无码| 亚洲精品字幕| 天天射中文| 色天使视频| 天堂中文字幕在线| footjobvk| av亚洲波多野结衣白嫩水多波| 粉嫩av懂色av蜜臀av熟妇| 日本亚洲欧美| 蜜臀av一区二区三区| 999热视频| 3p绿帽黑人看自己老婆| AA片在线观看视频在线播放| 欧美乱欲视频| 91丨九色丨国产在线| 国产精品91视频| 亚洲在线高清视频| 翔田千里无码免费播放| 中文字幕你懂的| 国产精品久久久久永久免费看| 国产美女网站| 国产激情视频网站| 99九九久久| 国产黄色视频在线观看| 黄色视频大全在线观看| 久久艹免费视频| 欧美A片视频| 久久精品国产AV一区二区三区 | 好吊妞在线观看| 三级无码在线| 91精品又粗又猛又爽| 久久影院av| 自拍成人视频| 成人一级A片| 东北女人毛多又黑A片| 久久私拍| 中文字幕在线资源| 中国最大成人网站| 黄色免费在线网站| 久久国产精品网站| 99久久99久久99久久久99国产| 免费日韩黄色电影| 中文字幕国产视频| 六月丁香网| 91欧美精品| 成人h视频| 成年人免费黄色视频| 12——13女人毛片毛片| 天天视频色| 日韩一a| 欧美成人看片黄a免费看| 国产视频99| 色五月国产| 日本成人A片| 欧美日韩免费在线观看| 91亚色视频| 抽插免费视频| 2017人人操| 中文字幕免费一区| 色色色色五月| 91免费网站| 成人做爰黄级A片免费看土方| 欧美伦妇AAAAAA片| 伊人网综合| 久久青青草在线视频| 小泬BBBBBB免费看| 亚洲三级视频| 无码专区亚洲| 97在线国产| 欧美熟女性爱| 日韩AV乱伦| 熟妇人妻中文字幕无码老熟妇| 操15p| 亚洲a√| 熟女无码| 97人妻人人澡人人| www.久久久久| 青青草资源站| 无码视频在线免费播放| 尤物AV| 国产欧美精品AAAAAA片| 高清无码黄|