在k8s的面试中Pod的建立流程是一个常问的问题,而kubelet则无疑重中之重,以前也写过一篇Pod的运行,不过没有涉及到具体的代码,本文尝试用代码的方式,来复数整个核心的流程,同时为了方便记忆,又将整个过程分为:准备、配置、清理、构建运行四个阶段,让咱们一块儿来看下吧, 文末有大图总结node
当获取到Pod添加的事件的时候,首先会进行一些基础的工做,我吧这个过程称为准备阶段,准备阶段主要作的事情有以下:1)加入PodManager 2)准入控制检查 3)分发事件 4)根据Pod添加对应的探针, 让咱们一块儿来看下关键实现git
PodManager中的功能除了存储Pod的信息,还会进行对应Pod的configMap和secret的管理,小心加入Pod的时候,会检查对应的Pod是否有对应的configMap和secret配置,若是有则就会建立对应的监听器,监听资源的变化,进行本地缓存github
除此以外,若是对应的Pod的BootstrapCheckpointAnnotationKey有设定,则还会建立对应的checkpoint,即将pod的配置数据写入到本地磁盘面试
kl.podManager.AddPod(pod)
准入控制检查主要是在运行Pod以前在kubelet上进行Pod运行条件的检查,检查当前节点在scheduler决策完成后到感知到Pod运行这段时间资源是否依旧知足,而且检查Pod的一些特殊资源好比好比sysctl、security等检查,这里我感受比较重要的两个分别是eviction和predicate, 若是不知足准入检查,则会直接拒绝算法
若是当前节点只存在内存压力,则会根据对应的Pod的QOS等级来判断,若是说不是BestEffort或者容忍内存压力的污点,则会容许,不然则会拒绝运行docker
nodeOnlyHasMemoryPressureCondition := hasNodeCondition(m.nodeConditions, v1.NodeMemoryPressure) && len(m.nodeConditions) == 1 if nodeOnlyHasMemoryPressureCondition { // 若是不是PodQOSBestEffort, 则都会尝试运行 notBestEffort := v1.PodQOSBestEffort != v1qos.GetPodQOS(attrs.Pod) if notBestEffort { return lifecycle.PodAdmitResult{Admit: true} } // 若是对应的Pod容忍内存压力的污点,则就能够继续进行其余准入控制器的检查 if v1helper.TolerationsTolerateTaint(attrs.Pod.Spec.Tolerations, &v1.Taint{ Key: v1.TaintNodeMemoryPressure, Effect: v1.TaintEffectNoSchedule, }) { return lifecycle.PodAdmitResult{Admit: true} } }
predicate准入控制器中的逻辑主要是分为两个部分:
1)检查对应的资源是否知足分配请求,同时会记录缺乏的资源
2)若是是Critical类型的Pod则会按照QOS等级来进行资源的抢占,知足这些高优先的Pod
这里的Critical类型的Pod主要包含以下三类:静态Pod、镜像Pod、高优先Pod(优先级高于2000000000)json
func (w *predicateAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult { node, err := w.getNodeAnyWayFunc() // 踢出扩展资源,只进行内存和CPU资源的检查 podWithoutMissingExtendedResources := removeMissingExtendedResources(admitPod, nodeInfo) // 进行预选算法筛选, 筛选出那些资源不足的资源 fit, reasons, err := predicates.GeneralPredicates(podWithoutMissingExtendedResources, nil, nodeInfo) if !fit { // 若是预选失败,则尝试进行抢占 fit, reasons, err = w.admissionFailureHandler.HandleAdmissionFailure(admitPod, reasons) } }
k8s里面的探针主要分为三类:startup、readiness、liveness,在Pod经过准入控制检查后,会根据Pod的探针配置建立对应的探针,可是这里的探针并不会真正的进行探测,由于当前还没法感知到对应的pod的状态api
kl.probeManager.AddPod(pod)
在kubelet中会为每一个Pod都建立一个对应的goroutine和事件管道,后续新的事件也都经过管道发送给对应的goroutine缓存
func (p *podWorkers) UpdatePod(options *UpdatePodOptions) { // 获取pod信息 pod := options.Pod uid := pod.UID var podUpdates chan UpdatePodOptions var exists bool p.podLock.Lock() defer p.podLock.Unlock() // kubelet会为每一个pod建立一个goroutine, 而且经过管道来进行通讯 if podUpdates, exists = p.podUpdates[uid]; !exists { podUpdates = make(chan UpdatePodOptions, 1) p.podUpdates[uid] = podUpdates // 为当前pod启动一个goroutine go func() { defer runtime.HandleCrash() p.managePodLoop(podUpdates) }() } if !p.isWorking[pod.UID] { p.isWorking[pod.UID] = true // 更新Pod的事件发送到管道 podUpdates <- *options } }
至此一个Pod的启动的准备阶段就基本完成了,检查运行环境、拉取对应的cofnigMap和secret资源、建立探针、启动负责Pod状态维护的线程,至此准备阶段完成
在kubelet最终的状态同步都是由syncPod来完成,该函数会根据传递进来的目标状态和Pod的当前状态来进行决策,从而知足目标状态,由于内部逻辑的复杂,会分为:配置阶段、清理阶段、构建运行阶段,这里先看下配置阶段微信
配置阶段主要是获取当前的Pod状态、应用CGOUP配置、Pod数据目录构建、等待VOlume挂载、获取镜像拉取的secret等
Pod的状态数据主要包含当前阶段、Conditions(容器Condition、初始化容器Condition、PodReadyCondition),而这些状态则须要根据当前的PodStatus里面的状态计算,还有probeManager里面探测的数据两部分共同完成
func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.PodStatus) v1.PodStatus { allStatus := append(append([]v1.ContainerStatus{}, s.ContainerStatuses...), s.InitContainerStatuses...) // 根据Pod的容器状态,设定当前的的阶段 s.Phase = getPhase(spec, allStatus) kl.probeManager.UpdatePodStatus(pod.UID, s) s.Conditions = append(s.Conditions, status.GeneratePodInitializedCondition(spec, s.InitContainerStatuses, s.Phase)) s.Conditions = append(s.Conditions, status.GeneratePodReadyCondition(spec, s.Conditions, s.ContainerStatuses, s.Phase)) s.Conditions = append(s.Conditions, status.GenerateContainersReadyCondition(spec, s.ContainerStatuses, s.Phase)) return *s }
该运行环境是指的一些软件状态的,这里主要涉及到Appmor、特权模式、proc挂载,实现机制就是检测对应的Pod是否须要对应的操做,而且SecurityContext中是否容许对应的操做,从而肯定Pod是否可以进行运行
func (kl *Kubelet) canRunPod(pod *v1.Pod) lifecycle.PodAdmitResult { // 准入控制插件 for _, handler := range kl.softAdmitHandlers { if result := handler.Admit(attrs); !result.Admit { return result } } return lifecycle.PodAdmitResult{Admit: true} }
更新状态主要是为了probeManager来进行状态检查的,若是probeManager没法获取到对应的状态,就不会执行对应的健康探针的检查,这里的状态就是根据以前的各类计算在kubelet上对应Pod的当前状态
kl.statusManager.SetPodStatus(pod, apiPodStatus)
if err := kl.runtimeState.networkErrors(); err != nil && !kubecontainer.IsHostNetworkPod(pod) { kl.recorder.Eventf(pod, v1.EventTypeWarning, events.NetworkNotReady, "%s: %v", NetworkNotReadyErrorMsg, err) return fmt.Errorf("%s: %v", NetworkNotReadyErrorMsg, err) }
Cgroup的配置主要是按照QOS等级来进行cgroup目录的构建,而且更新当前Pod的配置
pcm := kl.containerManager.NewPodContainerManager() // cgroup应用cgroup if !kl.podIsTerminated(pod) { podKilled := false if !pcm.Exists(pod) && !firstSync { // 若是对于的cgroup不存在,而且也不是第一次运行,就先将以前的pod沙雕 if err := kl.killPod(pod, nil, podStatus, nil); err == nil { podKilled = true } } if !(podKilled && pod.Spec.RestartPolicy == v1.RestartPolicyNever) { if !pcm.Exists(pod) { // 更新qoscgroup设置 if err := kl.containerManager.UpdateQOSCgroups(); err != nil { } // 更新podde的cgroup配置 if err := pcm.EnsureExists(pod); err != nil { } } } }
由于要经过镜像Pod来向apiserver传递静态Pod的状态,因此该阶段主要是为静态Pod建立对应的镜像Pod
if kubetypes.IsStaticPod(pod) { // 静态pod podFullName := kubecontainer.GetPodFullName(pod) deleted := false if mirrorPod != nil { if mirrorPod.DeletionTimestamp != nil || !kl.podManager.IsMirrorPodOf(mirrorPod, pod) { deleted, err = kl.podManager.DeleteMirrorPod(podFullName, &mirrorPod.ObjectMeta.UID) } } if mirrorPod == nil || deleted { if err := kl.podManager.CreateMirrorPod(pod); err != nil { } } } }
Pod的数据目录主要是包含三个部分:Pod目录、Volume目录、Plugin目录三个目录
if err := kl.makePodDataDirs(pod); err != nil { return err }
if !kl.podIsTerminated(pod) { if err := kl.volumeManager.WaitForAttachAndMount(pod); err != nil { } }
pullSecrets := kl.getPullSecretsForPod(pod)
着多是最复杂的一部分了,接下来就进入到下一个阶段:清理阶段
result := kl.containerRuntime.SyncPod(pod, podStatus, pullSecrets, kl.backOff) kl.reasonCache.Update(pod.UID, result)
在Pod运行前可能已经有部分容器已经在运行,则此时就须要根据当前的状态,来进行一些容器的清理工做,为接下来的构建运行阶段提供一个相对干净的环境
在k8s中Pod的状态主要包含sandbox容器状态、初始化容器状态、临时容器状态、业务容器状态等几部分,咱们依次来看下关键的实现
podContainerChanges := m.computePodActions(pod, podStatus)
沙箱状态计算:当且仅有一个Ready的沙箱而且沙箱的IP不为空的状况,沙箱的状态才不须要更改,其余状况下,都须要从新进行沙箱的构建,而且须要kill掉Pod关联的全部容器
func (m *kubeGenericRuntimeManager) podSandboxChanged(pod *v1.Pod, podStatus *kubecontainer.PodStatus) (bool, uint32, string) { if len(podStatus.SandboxStatuses) == 0 { return true, 0, "" } readySandboxCount := 0 for _, s := range podStatus.SandboxStatuses { if s.State == runtimeapi.PodSandboxState_SANDBOX_READY { readySandboxCount++ } } sandboxStatus := podStatus.SandboxStatuses[0] if readySandboxCount > 1 { return true, sandboxStatus.Metadata.Attempt + 1, sandboxStatus.Id } if sandboxStatus.State != runtimeapi.PodSandboxState_SANDBOX_READY { return true, sandboxStatus.Metadata.Attempt + 1, sandboxStatus.Id } if sandboxStatus.GetLinux().GetNamespaces().GetOptions().GetNetwork() != networkNamespaceForPod(pod) { return true, sandboxStatus.Metadata.Attempt + 1, "" } if !kubecontainer.IsHostNetworkPod(pod) && sandboxStatus.Network.Ip == "" { return true, sandboxStatus.Metadata.Attempt + 1, sandboxStatus.Id } return false, sandboxStatus.Metadata.Attempt, sandboxStatus.Id }
计算Pod的容器状态计算逻辑相对长一些,这里我就不贴代码了,其如要流程分为两个部分:
1.须要建立sandbox:
在该状态下,若是存在初始化容器,则会先进行初始化容器的初始化,即当前步骤只建立第一个初始化容器,若是没有初始化容器,则就将全部的业务容器加入到启动的列表里面
2.不须要建立sandbox:
该状态下会检查遍历全部的临时容器,初始化容器(若是存在失败的初始化容器,则就先启动初始化容器,不会进行业务容器的启动),业务容器,最终会构建一个须要kill掉的容器列表,还有两个启动的容器列表
须要进行KillPod的状态有两种:
sanbbox状态变动
即当sandbox状态不知足要求,则此时须要将Pod的全部容器都杀掉,而后进行重建
无需进行保留的容器
若是Pod对应的容器的hash值变动、状态为失败,则就须要重建
if podContainerChanges.KillPod { // 杀死当前全部的pod killResult := m.killPodWithSyncResult(pod, kubecontainer.ConvertPodStatusToRunningPod(m.runtimeName, podStatus), nil) if podContainerChanges.CreateSandbox { // 终止初始化运行 m.purgeInitContainers(pod, podStatus) } }
若是容器当前的状态是正常的,而且hash没有发生变化,则就不须要进行变动,此时就只须要将当前状态不正常的容器进行清理重建便可
for containerID, containerInfo := range podContainerChanges.ContainersToKill { if err := m.killContainer(pod, containerID, containerInfo.name, containerInfo.message, nil); err != nil { return } }
清理初始化容器
在正式启动容器以前,除了上面两部分,还会进行初始化容器的清理工做
m.pruneInitContainersBeforeStart(pod, podStatus)
构建运行阶段,主要分为两个大的部分:建立并运行sandbox容器、运行用户容器
检查须要建立sandbox,则会首先建立sandbox容器,并获取状态,而后填充当前的Pod的IP信息
// Step 4: Create a sandbox for the pod if necessary. // 建立沙箱环境 podSandboxID := podContainerChanges.SandboxID if podContainerChanges.CreateSandbox { podSandboxID, msg, err = m.createPodSandbox(pod, podContainerChanges.Attempt) podSandboxStatus, err := m.runtimeService.PodSandboxStatus(podSandboxID) if !kubecontainer.IsHostNetworkPod(pod) { podIPs = m.determinePodSandboxIPs(pod.Namespace, pod.Name, podSandboxStatus) } }
建立sandbox的主流程主要就三个步骤:建立配置信息、建立日志目录、调用cri运行sandbox
生成配置阶段主要包含端口映射、主机名、DNS、Linux中的SecurityContext灯的配置
func (m *kubeGenericRuntimeManager) createPodSandbox(pod *v1.Pod, attempt uint32) (string, string, error) { // 获取沙箱配置 podSandboxConfig, err := m.generatePodSandboxConfig(pod, attempt) // 建立目录 err = m.osInterface.MkdirAll(podSandboxConfig.LogDirectory, 0755) runtimeHandler := "" if utilfeature.DefaultFeatureGate.Enabled(features.RuntimeClass) && m.runtimeClassManager != nil { // 获取当前的runtimeHandler runtimeHandler, err = m.runtimeClassManager.LookupRuntimeHandler(pod.Spec.RuntimeClassName) } // 运行Sandbox podSandBoxID, err := m.runtimeService.RunPodSandbox(podSandboxConfig, runtimeHandler) return podSandBoxID, "", nil }
sandbox的启动主要包含下面几部分:1) 拉取sanbox容器镜像 2)建立sandbox容器 3)建立sandbox的checkpoint 4)启动sandbox容器 5)为sandbox启动网络(若是不是主机网络)
func (ds *dockerService) RunPodSandbox(ctx context.Context, r *runtimeapi.RunPodSandboxRequest) (*runtimeapi.RunPodSandboxResponse, error) { config := r.GetConfig() // Step 1: Pull the image for the sandbox. // 拉取sandbox沙箱 // defaultPodSandboxImageName = "k8s.gcr.io/pause" // defaultPodSandboxImageVersion = "3.1" image := defaultSandboxImage podSandboxImage := ds.podSandboxImage if len(podSandboxImage) != 0 { image = podSandboxImage } // 拉取镜像 if err := ensureSandboxImageExists(ds.client, image); err != nil { return nil, err } // 2.建立sandbox容器 if r.GetRuntimeHandler() != "" && r.GetRuntimeHandler() != runtimeName { return nil, fmt.Errorf("RuntimeHandler %q not supported", r.GetRuntimeHandler()) } // 建立沙箱配置 createConfig, err := ds.makeSandboxDockerConfig(config, image) // 建立容器 createResp, err := ds.client.CreateContainer(*createConfig) resp := &runtimeapi.RunPodSandboxResponse{PodSandboxId: createResp.ID} ds.setNetworkReady(createResp.ID, false) defer func(e *error) { // Set networking ready depending on the error return of // the parent function if *e == nil { ds.setNetworkReady(createResp.ID, true) } }(&err) // Step 3: 建立sandbox checkpoint if err = ds.checkpointManager.CreateCheckpoint(createResp.ID, constructPodSandboxCheckpoint(config)); err != nil { return nil, err } // Step 4: Start the sandbox container. // // 4.启动sandbox容器 err = ds.client.StartContainer(createResp.ID) if err != nil { return nil, fmt.Errorf("failed to start sandbox container for pod %q: %v", config.Metadata.Name, err) } //重写docker生成的resolv.conf文件。 if dnsConfig := config.GetDnsConfig(); dnsConfig != nil { containerInfo, err := ds.client.InspectContainer(createResp.ID) if err != nil { return nil, fmt.Errorf("failed to inspect sandbox container for pod %q: %v", config.Metadata.Name, err) } // DNS写配置文件 if err := rewriteResolvFile(containerInfo.ResolvConfPath, dnsConfig.Servers, dnsConfig.Searches, dnsConfig.Options); err != nil { return nil, fmt.Errorf("rewrite resolv.conf failed for pod %q: %v", config.Metadata.Name, err) } } // 若是处于主机网络模式,请不要调用网络插件。 if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtimeapi.NamespaceMode_NODE { return resp, nil } // Step 5: 设置sandbox容器的网络 //全部的pod网络都是由启动时发现的CNI插件设置的。 //这个插件分配pod ip,在沙盒内设置路由,建立接口等。理论上,它的管辖权以pod沙盒网络结束, // 但它也可能在主机上插入iptables规则或打开端口,以知足CNI标准还没有识别的pod规范的部分要求。 cID := kubecontainer.BuildContainerID(runtimeName, createResp.ID) networkOptions := make(map[string]string) if dnsConfig := config.GetDnsConfig(); dnsConfig != nil { // Build DNS options. dnsOption, err := json.Marshal(dnsConfig) if err != nil { return nil, fmt.Errorf("failed to marshal dns config for pod %q: %v", config.Metadata.Name, err) } // 设置网络dns networkOptions["dns"] = string(dnsOption) } // 网络信息 err = ds.network.SetUpPod(config.GetMetadata().Namespace, config.GetMetadata().Name, cID, config.Annotations, networkOptions) return resp, nil }
容器启动函数中会经过闭包来保存上面建立的sandbox的信息,同时根据当前容器的配置,建立新的业务容器
start := func(typeName string, container *v1.Container) error { klog.V(4).Infof("Creating %v %+v in pod %v", typeName, container, format.Pod(pod)) if msg, err := m.startContainer(podSandboxID, podSandboxConfig, container, pod, podStatus, pullSecrets, podIP, podIPs); err != nil { startContainerResult.Fail(err, msg) } return nil }
容器的启动,主要包含四个流程:1.拉取镜像 2.建立容器&PreStart钩子回调 3) 启动容器 4)postStart启动容器
func (m *kubeGenericRuntimeManager) startContainer(podSandboxID string, podSandboxConfig *runtimeapi.PodSandboxConfig, container *v1.Container, pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, podIP string, podIPs []string) (string, error) { // 启动容器 // Step 1: pull the image. imageRef, msg, err := m.imagePuller.EnsureImageExists(pod, container, pullSecrets, podSandboxConfig) // Step 2: create the container. ref, err := kubecontainer.GenerateContainerRef(pod, container) // 获取容器配置, 里面会进行各类文件目录的挂载 containerConfig, cleanupAction, err := m.generateContainerConfig(container, pod, restartCount, podIP, imageRef, podIPs) if cleanupAction != nil { defer cleanupAction() } if err != nil { s, _ := grpcstatus.FromError(err) m.recordContainerEvent(pod, container, "", v1.EventTypeWarning, events.FailedToCreateContainer, "Error: %v", s.Message()) return s.Message(), ErrCreateContainerConfig } // 建立容器 containerID, err := m.runtimeService.CreateContainer(podSandboxID, containerConfig, podSandboxConfig) // 启动容器钩子 err = m.internalLifecycle.PreStartContainer(pod, container, containerID) m.recordContainerEvent(pod, container, containerID, v1.EventTypeNormal, events.CreatedContainer, fmt.Sprintf("Created container %s", container.Name)) if ref != nil { m.containerRefManager.SetRef(kubecontainer.ContainerID{ Type: m.runtimeName, ID: containerID, }, ref) } // Step 3: 启动容器 err = m.runtimeService.StartContainer(containerID) if err != nil { s, _ := grpcstatus.FromError(err) m.recordContainerEvent(pod, container, containerID, v1.EventTypeWarning, events.FailedToStartContainer, "Error: %v", s.Message()) return s.Message(), kubecontainer.ErrRunContainer } containerMeta := containerConfig.GetMetadata() sandboxMeta := podSandboxConfig.GetMetadata() legacySymlink := legacyLogSymlink(containerID, containerMeta.Name, sandboxMeta.Name, sandboxMeta.Namespace) // 容器日志 containerLog := filepath.Join(podSandboxConfig.LogDirectory, containerConfig.LogPath) if _, err := m.osInterface.Stat(containerLog); !os.IsNotExist(err) { if err := m.osInterface.Symlink(containerLog, legacySymlink); err != nil { } } // Step 4: 执行postStart钩子 if container.Lifecycle != nil && container.Lifecycle.PostStart != nil { msg, handlerErr := m.runner.Run(kubeContainerID, pod, container, container.Lifecycle.PostStart) if handlerErr != nil { if err := m.killContainer(pod, kubeContainerID, container.Name, "FailedPostStartHook", nil); err != nil { } } } return "", nil }
CreateContainer中会首先根据k8s里面传递的配置信息,根据当前平台和对应的参数来进行docker容器运行的配置,而后调用docker接口进行容器的配置
func (ds *dockerService) CreateContainer(_ context.Context, r *runtimeapi.CreateContainerRequest) (*runtimeapi.CreateContainerResponse, error) { podSandboxID := r.PodSandboxId config := r.GetConfig() sandboxConfig := r.GetSandboxConfig() containerName := makeContainerName(sandboxConfig, config) // 建立容器配置 createConfig := dockertypes.ContainerCreateConfig{ Name: containerName, Config: &dockercontainer.Config{ // TODO: set User. Entrypoint: dockerstrslice.StrSlice(config.Command), Cmd: dockerstrslice.StrSlice(config.Args), Env: generateEnvList(config.GetEnvs()), Image: image, WorkingDir: config.WorkingDir, Labels: labels, // Interactive containers: OpenStdin: config.Stdin, StdinOnce: config.StdinOnce, Tty: config.Tty, // Disable Docker's health check until we officially support it // (https://github.com/kubernetes/kubernetes/issues/25829). Healthcheck: &dockercontainer.HealthConfig{ Test: []string{"NONE"}, }, }, HostConfig: &dockercontainer.HostConfig{ Binds: generateMountBindings(config.GetMounts()), RestartPolicy: dockercontainer.RestartPolicy{ Name: "no", }, }, } hc := createConfig.HostConfig err = ds.updateCreateConfig(&createConfig, config, sandboxConfig, podSandboxID, securityOptSeparator, apiVersion) if err != nil { return nil, fmt.Errorf("failed to update container create config: %v", err) } // 设置容器devices devices := make([]dockercontainer.DeviceMapping, len(config.Devices)) for i, device := range config.Devices { devices[i] = dockercontainer.DeviceMapping{ PathOnHost: device.HostPath, PathInContainer: device.ContainerPath, CgroupPermissions: device.Permissions, } } hc.Resources.Devices = devices securityOpts, err := ds.getSecurityOpts(config.GetLinux().GetSecurityContext().GetSeccompProfilePath(), securityOptSeparator) if err != nil { return nil, fmt.Errorf("failed to generate security options for container %q: %v", config.Metadata.Name, err) } hc.SecurityOpt = append(hc.SecurityOpt, securityOpts...) cleanupInfo, err := ds.applyPlatformSpecificDockerConfig(r, &createConfig) if err != nil { return nil, err } createResp, createErr := ds.client.CreateContainer(createConfig) if createErr != nil { createResp, createErr = recoverFromCreationConflictIfNeeded(ds.client, createConfig, createErr) } if createResp != nil { containerID := createResp.ID if cleanupInfo != nil { // we don't perform the clean up just yet at that could destroy information // needed for the container to start (e.g. Windows credentials stored in // registry keys); instead, we'll clean up when the container gets removed ds.containerCleanupInfos[containerID] = cleanupInfo } return &runtimeapi.CreateContainerResponse{ContainerId: containerID}, nil } return nil, createErr }
更新容器配置
func (ds *dockerService) updateCreateConfig( createConfig *dockertypes.ContainerCreateConfig, config *runtimeapi.ContainerConfig, sandboxConfig *runtimeapi.PodSandboxConfig, podSandboxID string, securityOptSep rune, apiVersion *semver.Version) error { if lc := config.GetLinux(); lc != nil { rOpts := lc.GetResources() if rOpts != nil { // 更新资源配置信息 createConfig.HostConfig.Resources = dockercontainer.Resources{ Memory: rOpts.MemoryLimitInBytes, MemorySwap: rOpts.MemoryLimitInBytes, CPUShares: rOpts.CpuShares, CPUQuota: rOpts.CpuQuota, CPUPeriod: rOpts.CpuPeriod, } createConfig.HostConfig.OomScoreAdj = int(rOpts.OomScoreAdj) } // 应用SecurityContext if err := applyContainerSecurityContext(lc, podSandboxID, createConfig.Config, createConfig.HostConfig, securityOptSep); err != nil { return fmt.Errorf("failed to apply container security context for container %q: %v", config.Metadata.Name, err) } } // 应用cgroup配置 if lc := sandboxConfig.GetLinux(); lc != nil { // Apply Cgroup options. cgroupParent, err := ds.GenerateExpectedCgroupParent(lc.CgroupParent) createConfig.HostConfig.CgroupParent = cgroupParent } return nil }
其实就直接掉Docker的接口启动容器便可
func (ds *dockerService) StartContainer(_ context.Context, r *runtimeapi.StartContainerRequest) (*runtimeapi.StartContainerResponse, error) { err := ds.client.StartContainer(r.ContainerId) return &runtimeapi.StartContainerResponse{}, nil }
Pod启动的核心流程大概就这些,里面会有一些笔认购具体参数数据的构建,没有写明,可是若是对代码感兴趣的,能够顺着这个核心流程基本能够读下来,若是对代码不感兴趣,则后面这张图能够算做一个精简版的,面试可用的Pod建立流程图
本文地址: https://www.yuque.com/baxiaoshi/tyado3/kw00f2
kubernetes学习笔记地址: https://www.yuque.com/baxiaos...
微信号:baxiaoshi2020![]()
关注公告号阅读更多源码分析文章![]()
本文由博客一文多发平台 OpenWrite 发布!