blog

Kubeletポッド作成ワークフロー

Kubeletの4つのコンポーネントの1つであるKubeletは、Podのライフサイクル全体を維持し、Podを作成するチェーンの最後のリンクとなります。今回はKubeletを使ったPodの作成方法を紹...

Mar 16, 2020 · 18 min. read
シェア

この記事は、Kubernetes v1.19.0-rc.3のソースコードを読んだことに基づいています。

KubeletはKubernetesの4つのコンポーネントの1つとして、Podのライフサイクル全体を管理し、KubernetesでPodを作成する際の最後のリンクとなります。今回は、KubeletがどのようにPodを作成するかを紹介します。

Kubelet

まず、Kubeletコンポーネントのアーキテクチャ図を見てみましょう:

あなたは、Kubeletが3つの主要な層に分割されていることがわかります:API層、syncLoop層、CRI以下。API層は、レイヤーの外部部分へのインタフェースを提供することです理解しやすい、syncLoop層は、Kubeletの作業層の中核であり、Kubeletの主な仕事は、イベント駆動型ループの実行を通じて、プロデューサーとコンシューマによって、実行するsyncLoopの周り、つまり、ループを制御することです;CRIは、コンテナとImageのサービス間のインタフェースを提供します;コンテナは、CRIプラグインの形でアクセスすることができます。Kubeletの主な仕事は、このsyncLoop、つまりイベント駆動型ループの実行を通じてプロデューサーとコンシューマーが実行する制御ループを中心に展開されます。CRIは、コンテナとImageのサービス間のインタフェースを提供し、コンテナの実行時にCRIプラグインの形でアクセスできます。

syncLoopレイヤーの重要なコンポーネントを見てみましょう:

  • PLEG: コンテナランタイムインターフェースを呼び出して、このノード上のコンテナ/サンドボックスの情報を取得し、ローカルに保持されているポッドキャッシュと比較し、対応するPodLifecycleEventを生成し、それをeventChannel経由でKubelet syncLoopに送信し、次に定時タスクがを生成し、それをeventChannel経由でKubelet syncLoopに送信し、その後、定時タスクがポッドを同期してユーザーの希望する状態に到達させます。

  • CAdvisor: Kubeletに統合されたコンテナ監視ツールで、このノードとコンテナの監視情報を収集します。

  • PodWorkers:複数のPodハンドラが登録され、Podの作成、更新、削除など、さまざまなタイミングでPodを処理します。

  • oomWatcher: システム OOM のリスナーで,CAdvisor モジュールと SystemOOM を確立し,CAdvisor から受信した OOM シグナルを監視して関連イベントを生成します.

  • containerGC:ノード上の無駄なコンテナのクリーンアップを担当し、特定のゴミ収集操作はコンテナランタイムによって実装されます。

  • imageGC: ノードのイメージリサイクルを担当します。イメージを保存するためのローカルディスク容量が一定の閾値に達すると、イメージリサイクルが開始され、ポッドで使用されていないイメージは削除されます。

Kubelet 仕組み

上述したように、Kubeletの動作は主にSyncLoopを中心に展開し、goチャネルの助けを借りて、各コンポーネントはループをリッスンしてイベントを消費したり、ポッド関連のイベントをループに生成したりします。制御ループ全体がイベント駆動です:

例えば、新規ポッド作成処理中にポッドがノードにディスパッチされると、上図のHandlePodsセクションのようなループ制御でKubeletが登録したハンドラがトリガーされます。この時点でKubeletはKubeletメモリ内のポッドの状態をチェックし、作成が必要なポッドであると判断するため、ハンドラ内のADDイベントに対応するロジックがトリガーされます。

SyncLoop

このメインループ、SyncLoopを見てみましょう:

func (kl *kubelet) syncLoop(updates <-chan kubetypes.PodUpdate, handler SyncHandler) {
	klog.Info("Starting kubelet main sync loop.")
	// The syncTicker wakes up Kubelet to checks if there are any pod workers
	// that need to be sync'd. A one-second period is sufficient because the
	// sync interval is defaulted to 10s.
	syncTicker := time.NewTicker(time.Second)
	defer syncTicker.Stop()
	housekeepingTicker := time.NewTicker(housekeepingPeriod)
	defer housekeepingTicker.Stop()
	plegCh := kl.pleg.Watch()
	const (
		base = 100 * time.Millisecond
		max = 5 * time.Second
		factor = 2
	)
	duration := base
	// Responsible for checking limits in resolv.conf
	// The limits do not have anything to do with individual pods
	// Since this is called in syncLoop, we don't need to call it anywhere else
	if kl.dnsConfigurer != nil && kl.dnsConfigurer.ResolverConfig != "" {
		kl.dnsConfigurer.CheckLimitsForResolvConf()
	}
	for {
		if err := kl.runtimeState.runtimeErrors(); err != nil {
			klog.Errorf("skipping pod synchronization - %v", err)
			// exponential backoff
			time.Sleep(duration)
			duration = time.Duration(math.Min(float64(max), factor*float64(duration)))
			continue
		}
		// reset backoff if we have a success
		duration = base
		kl.syncLoopMonitor.Store(kl.clock.Now())
		if !kl.syncLoopIteration(updates, handler, syncTicker.C, housekeepingTicker.C, plegCh) {
			break
		}
		kl.syncLoopMonitor.Store(kl.clock.Now())
	}
}

SyncLoop は syncLoopIteration メソッドのみを呼び出すデッドループを開始します。syncLoopIteration はすべての受信チャンネルを繰り返し、そのどれかにメッセージを見つけるとハンドラに渡します。

これらのチャンネルには以下のようなものがあります:

  • configCh: このチャネルのプロデューサーはkubeDepsオブジェクトのPodConfigサブモジュールによって提供され、ファイル、http、apiserverからのポッド情報の変更を同時にリッスンし、特定のソースからのポッド情報が更新されると、関連するイベントをこのチャネルに生成します。
  • plegCh:このチャネルのプロデューサーは、pleg サブモジュールです。pleg サブモジュールは、すべてのコンテナの現在の状態をコンテナランタイムに定期的に照会、状態が変更された場合にこのチャネルにイベントを生成します。
  • syncCh:保存された最新のポッド状態を定期的に同期します。
  • houseKeepingCh: ポッドのクリーンアップを行うハウスキーピングイベントのパイプライン。

syncLoopIteration のコード:

func (kl *kubelet) syncLoopIteration(configCh <-chan kubetypes.PodUpdate, handler SyncHandler,
	syncCh <-chan time.Time, housekeepingCh <-chan time.Time, plegCh <-chan *pleg.PodLifecycleEvent) bool {
	select {
	case u, open := <-configCh:
		// Update from a config source; dispatch it to the right handler
		// callback.
		if !open {
			klog.Errorf("Update channel is closed. Exiting the sync loop.")
			return false
		}
		switch u.Op {
		case kubetypes.ADD:
			klog.V(2).Infof("SyncLoop (ADD, %q): %q", u.Source, format.Pods(u.Pods))
			// After restarting, Kubelet will get all existing pods through
			// ADD as if they are new pods. These pods will then go through the
			// admission process and *may* be rejected. This can be resolved
			// once we have checkpointing.
			handler.HandlePodAdditions(u.Pods)
		case kubetypes.UPDATE:
			klog.V(2).Infof("SyncLoop (UPDATE, %q): %q", u.Source, format.PodsWithDeletionTimestamps(u.Pods))
			handler.HandlePodUpdates(u.Pods)
		case kubetypes.REMOVE:
			klog.V(2).Infof("SyncLoop (REMOVE, %q): %q", u.Source, format.Pods(u.Pods))
			handler.HandlePodRemoves(u.Pods)
		case kubetypes.RECONCILE:
			klog.V(4).Infof("SyncLoop (RECONCILE, %q): %q", u.Source, format.Pods(u.Pods))
			handler.HandlePodReconcile(u.Pods)
		case kubetypes.DELETE:
			klog.V(2).Infof("SyncLoop (DELETE, %q): %q", u.Source, format.Pods(u.Pods))
			// DELETE is treated as a UPDATE because of graceful deletion.
			handler.HandlePodUpdates(u.Pods)
		case kubetypes.SET:
			// TODO: Do we want to support this?
			klog.Errorf("Kubelet does not support snapshot update")
		default:
			klog.Errorf("Invalid event type received: %d.", u.Op)
		}
		kl.sourcesReady.AddSource(u.Source)
	case e := <-plegCh:
		if e.Type == pleg.ContainerStarted {
			// record the most recent time we observed a container start for this pod.
			// this lets us selectively invalidate the runtimeCache when processing a delete for this pod
			// to make sure we don't miss handling graceful termination for containers we reported as having started.
			kl.lastContainerStartedTime.Add(e.ID, time.Now())
		}
		if isSyncPodWorthy(e) {
			// PLEG event for a pod; sync it.
			if pod, ok := kl.podManager.GetPodByUID(e.ID); ok {
				klog.V(2).Infof("SyncLoop (PLEG): %q, event: %#v", format.Pod(pod), e)
				handler.HandlePodSyncs([]*v1.Pod{pod})
			} else {
				// If the pod no longer exists, ignore the event.
				klog.V(4).Infof("SyncLoop (PLEG): ignore irrelevant event: %#v", e)
			}
		}
		if e.Type == pleg.ContainerDied {
			if containerID, ok := e.Data.(string); ok {
				kl.cleanUpContainersInPod(e.ID, containerID)
			}
		}
	case <-syncCh:
		// Sync pods waiting for sync
		podsToSync := kl.getPodsToSync()
		if len(podsToSync) == 0 {
			break
		}
		klog.V(4).Infof("SyncLoop (SYNC): %d pods; %s", len(podsToSync), format.Pods(podsToSync))
		handler.HandlePodSyncs(podsToSync)
	case update := <-kl.livenessManager.Updates():
		if update.Result == proberesults.Failure {
			// The liveness manager detected a failure; sync the pod.
			// We should not use the pod from livenessManager, because it is never updated after
			// initialization.
			pod, ok := kl.podManager.GetPodByUID(update.PodUID)
			if !ok {
				// If the pod no longer exists, ignore the update.
				klog.V(4).Infof("SyncLoop (container unhealthy): ignore irrelevant update: %#v", update)
				break
			}
			klog.V(1).Infof("SyncLoop (container unhealthy): %q", format.Pod(pod))
			handler.HandlePodSyncs([]*v1.Pod{pod})
		}
	case <-housekeepingCh:
		if !kl.sourcesReady.AllReady() {
			// If the sources aren't ready or volume manager has not yet synced the states,
			// skip housekeeping, as we may accidentally delete pods from unready sources.
			klog.V(4).Infof("SyncLoop (housekeeping, skipped): sources aren't ready yet.")
		} else {
			klog.V(4).Infof("SyncLoop (housekeeping)")
			if err := handler.HandlePodCleanups(); err != nil {
				klog.Errorf("Failed cleaning pods: %v", err)
			}
		}
	}
	return true
}

ポッドの作成プロセス

KubeletでPodを作成する処理は、configChのADDイベントをトリガーに行われるので、ここではKubeletがADDイベントを受け取った時の主な流れを見てみましょう。

Handler

まず、ハンドラはすべてのポッドインストールを作成日順にソートし、1つずつ処理します。

ミラーポッドであればミラーポッドとして扱われ、ミラーポッドでなければ通常のポッドとして扱われます:

これはKubeletではポッドアクセスコントロールと呼ばれ、以下の側面から構成されます:

  1. ノードはポッドのアフィニティ・ルールを満たしていますか?
  2. ノードがポッドに十分なリソースを割り当てているかどうか
  3. ノードがHostNetworkまたはHostIPCを使用しているかどうか、使用している場合はノードのホワイトリストにあるかどうか。
  4. /procマウント・ディレクトリは要件を満たしています。
  5. ポッドが構成され、AppArmorが正しく構成されていますか?

すべての条件が満たされると、最後にPodWorkerがトリガーされてPodが同期されます。

HandlePodAdditionsに対応するコードは以下の通りです:

func (kl *kubelet) HandlePodAdditions(pods []*v1.Pod) {
	start := kl.clock.Now()
	sort.Sort(sliceutils.PodsByCreationTime(pods))
	for _, pod := range pods {
		existingPods := kl.podManager.GetPods()
		// Always add the pod to the pod manager. Kubelet relies on the pod
		// manager as the source of truth for the desired state. If a pod does
		// not exist in the pod manager, it means that it has been deleted in
		// the apiserver and no action (other than cleanup) is required.
		kl.podManager.AddPod(pod)
		if kubetypes.IsMirrorPod(pod) {
			kl.handleMirrorPod(pod, start)
			continue
		}
		if !kl.podIsTerminated(pod) {
			// Only go through the admission process if the pod is not
			// terminated.
			// We failed pods that we rejected, so activePods include all admitted
			// pods that are alive.
			activePods := kl.filterOutTerminatedPods(existingPods)
			// Check if we can admit the pod; if not, reject it.
			if ok, reason, message := kl.canAdmitPod(activePods, pod); !ok {
				kl.rejectPod(pod, reason, message)
				continue
			}
		}
		mirrorPod, _ := kl.podManager.GetMirrorPodByPod(pod)
		kl.dispatchWork(pod, kubetypes.SyncPodCreate, mirrorPod, start)
		kl.probeManager.AddPod(pod)
	}
}

podWorkers

managePodLoopはイベントを受け取ると、まずポッドキャッシュからポッドの最新のステータスを取得し、現在処理中のポッドが最新であることを確認します。次にsyncPodメソッドを呼び出してworkQueueに同期の結果を記録し、それを処理する次の時間指定同期タスクを待ちます。

プロセス全体を以下に示します:

ポッドイベントを処理するポッドワーカーのコード:

func (p *podWorkers) UpdatePod(options *UpdatePodOptions) {
	pod := options.Pod
	uid := pod.UID
	var podUpdates chan UpdatePodOptions
	var exists bool
	p.podLock.Lock()
	defer p.podLock.Unlock()
	if podUpdates, exists = p.podUpdates[uid]; !exists {
		podUpdates = make(chan UpdatePodOptions, 1)
		p.podUpdates[uid] = podUpdates
		go func() {
			defer runtime.HandleCrash()
			p.managePodLoop(podUpdates)
		}()
	}
	if !p.isWorking[pod.UID] {
		p.isWorking[pod.UID] = true
		podUpdates <- *options
	} else {
		// if a request to kill a pod is pending, we do not let anything overwrite that request.
		update, found := p.lastUndeliveredWorkUpdate[pod.UID]
		if !found || update.UpdateType != kubetypes.SyncPodKill {
			p.lastUndeliveredWorkUpdate[pod.UID] = *options
		}
	}
}
func (p *podWorkers) managePodLoop(podUpdates <-chan UpdatePodOptions) {
	var lastSyncTime time.Time
	for update := range podUpdates {
		err := func() error {
			podUID := update.Pod.UID
			status, err := p.podCache.GetNewerThan(podUID, lastSyncTime)
			if err != nil {
				p.recorder.Eventf(update.Pod, v1.EventTypeWarning, events.FailedSync, "error determining status: %v", err)
				return err
			}
			err = p.syncPodFn(syncPodOptions{
				mirrorPod: update.MirrorPod,
				pod: update.Pod,
				podStatus: status,
				killPodOptions: update.KillPodOptions,
				updateType: update.UpdateType,
			})
			lastSyncTime = time.Now()
			return err
		}()
		// notify the call-back function if the operation succeeded or not
		if update.OnCompleteFunc != nil {
			update.OnCompleteFunc(err)
		}
		if err != nil {
			// IMPORTANT: we do not log errors here, the syncPodFn is responsible for logging errors
			klog.Errorf("Error syncing pod %s (%q), skipping: %v", update.Pod.UID, format.Pod(update.Pod), err)
		}
		p.wrapUp(update.Pod.UID, err)
	}
}

KubeletのsyncPodのコードは以下の通りです。主な処理を理解するために、最適化コードの一部を削除しています:

func (kl *kubelet) syncPod(o syncPodOptions) error {
	// pull out the required options
	pod := o.pod
	mirrorPod := o.mirrorPod
	podStatus := o.podStatus
	updateType := o.updateType
	// if we want to kill a pod, do it now!
	if updateType == kubetypes.SyncPodKill {
		...
		if err := kl.killPod(pod, nil, podStatus, killPodOptions.PodTerminationGracePeriodSecondsOverride); err != nil {
			kl.recorder.Eventf(pod, v1.EventTypeWarning, events.FailedToKillPod, "error killing pod: %v", err)
			// there was an error killing the pod, so we return that error directly
			utilruntime.HandleError(err)
			return err
		}
		return nil
	}
	...
	runnable := kl.canRunPod(pod)
	if !runnable.Admit {
		...
	}
 ...
	// If the network plugin is not ready, only start the pod if it uses the host network
	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)
	}
	...
	if !kl.podIsTerminated(pod) {
		...
		if !(podKilled && pod.Spec.RestartPolicy == v1.RestartPolicyNever) {
			if !pcm.Exists(pod) {
				if err := kl.containerManager.UpdateQOSCgroups(); err != nil {
					klog.V(2).Infof("Failed to update QoS cgroups while syncing pod: %v", err)
				}
				...
			}
		}
	}
	// Create Mirror Pod for Static Pod if it doesn't already exist
	if kubetypes.IsStaticPod(pod) {
		...
		}
		if mirrorPod == nil || deleted {
			node, err := kl.GetNode()
			if err != nil || node.DeletionTimestamp != nil {
				klog.V(4).Infof("No need to create a mirror pod, since node %q has been removed from the cluster", kl.nodeName)
			} else {
				klog.V(4).Infof("Creating a mirror pod for static pod %q", format.Pod(pod))
				if err := kl.podManager.CreateMirrorPod(pod); err != nil {
					klog.Errorf("Failed creating a mirror pod for %q: %v", format.Pod(pod), err)
				}
			}
		}
	}
	// Make data directories for the pod
	if err := kl.makePodDataDirs(pod); err != nil {
		kl.recorder.Eventf(pod, v1.EventTypeWarning, events.FailedToMakePodDataDirectories, "error making pod data directories: %v", err)
		klog.Errorf("Unable to make pod data directories for pod %q: %v", format.Pod(pod), err)
		return err
	}
	// Volume manager will not mount volumes for terminated pods
	if !kl.podIsTerminated(pod) {
		// Wait for volumes to attach/mount
		if err := kl.volumeManager.WaitForAttachAndMount(pod); err != nil {
			kl.recorder.Eventf(pod, v1.EventTypeWarning, events.FailedMountVolume, "Unable to attach or mount volumes: %v", err)
			klog.Errorf("Unable to attach or mount volumes for pod %q: %v; skipping pod", format.Pod(pod), err)
			return err
		}
	}
	// Fetch the pull secrets for the pod
	pullSecrets := kl.getPullSecretsForPod(pod)
	// Call the container runtime's SyncPod callback
	result := kl.containerRuntime.SyncPod(pod, podStatus, pullSecrets, kl.backOff)
	kl.reasonCache.Update(pod.UID, result)
	if err := result.Error(); err != nil {
		// Do not return error if the only failures were pods in backoff
		for _, r := range result.SyncResults {
			if r.Error != kubecontainer.ErrCrashLoopBackOff && r.Error != images.ErrImagePullBackOff {
				// Do not record an event here, as we keep all event logging for sync pod failures
				// local to container runtime so we get better errors
				return err
			}
		}
		return nil
	}
	return nil
}

ポッドを作成するプロセス全体は、ランタイム・レイヤーのsyncPod部分に集約されます:

プロセスは明確です。まず、ポッドのサンドボックスとコンテナの変更を計算し、サンドボックスが変更された場合は、ポッドとそれに関連するコンテナを kill します。

k8s v1.16の新機能であるエフェメラルコンテナは、トラブルシューティングなどのユーザ主導の操作を実行するために、既存のPod内で一時的に実行されます。

コード全体は以下のとおりです。ここでも、主要な処理を示すために最適化コードを一部削除しています:

func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) {
	// Step 1: Compute sandbox and container changes.
	podContainerChanges := m.computePodActions(pod, podStatus)
	klog.V(3).Infof("computePodActions got %+v for pod %q", podContainerChanges, format.Pod(pod))
	if podContainerChanges.CreateSandbox {
		ref, err := ref.GetReference(legacyscheme.Scheme, pod)
		if err != nil {
			klog.Errorf("Couldn't make a ref to pod %q: '%v'", format.Pod(pod), err)
		}
		...
	}
	// Step 2: Kill the pod if the sandbox has changed.
	if podContainerChanges.KillPod {
		killResult := m.killPodWithSyncResult(pod, kubecontainer.ConvertPodStatusToRunningPod(m.runtimeName, podStatus), nil)
		result.AddPodSyncResult(killResult)
		...
	} else {
		// Step 3: kill any running containers in this pod which are not to keep.
		for containerID, containerInfo := range podContainerChanges.ContainersToKill {
			...
			if err := m.killContainer(pod, containerID, containerInfo.name, containerInfo.message, nil); err != nil {
				killContainerResult.Fail(kubecontainer.ErrKillContainer, err.Error())
				klog.Errorf("killContainer %q(id=%q) for pod %q failed: %v", containerInfo.name, containerID, format.Pod(pod), err)
				return
			}
		}
	}
	...
	// Step 4: Create a sandbox for the pod if necessary.
	podSandboxID := podContainerChanges.SandboxID
	if podContainerChanges.CreateSandbox {
		var msg string
		var err error
		...
		podSandboxID, msg, err = m.createPodSandbox(pod, podContainerChanges.Attempt)
		if err != nil {
			...
		}
		klog.V(4).Infof("Created PodSandbox %q for pod %q", podSandboxID, format.Pod(pod))
		...
	}
	...
	// Step 5: start ephemeral containers
	if utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
		for _, idx := range podContainerChanges.EphemeralContainersToStart {
			start("ephemeral container", ephemeralContainerStartSpec(&pod.Spec.EphemeralContainers[idx]))
		}
	}
	// Step 6: start the init container.
	if container := podContainerChanges.NextInitContainerToStart; container != nil {
		// Start the next init container.
		if err := start("init container", containerStartSpec(container)); err != nil {
			return
		}
 ...
	}
	// Step 7: start containers in podContainerChanges.ContainersToStart.
	for _, idx := range podContainerChanges.ContainersToStart {
		start("container", containerStartSpec(&pod.Spec.Containers[idx]))
	}
	return
}

最後に、サンドボックスとは何かを見てみましょう。コンピュータセキュリティの分野では、サンドボックスはプログラムを隔離するためのメカニズムであり、その目的は信頼できないプロセスの特権を制限することです。dockerはコンテナでこの技法を使用し、各コンテナにサンドボックスを作成し、そのcgroupとさまざまな名前空間を定義してコンテナを隔離します。k8sでは、各ポッドがサンドボックスを共有するため、同じポッド内のすべてのコンテナは相互運用でき、外部からは隔離されます。k8sの各ポッドはサンドボックスを共有するので、同じポッド内のすべてのコンテナは相互運用でき、外部からは隔離されます。

Kubelet がポッド用のサンドボックスを作成する方法を見てみましょう。まず、ポッド用の DNS 構成、HostName、ログパス、サンドボックスポートを定義します。これらはすべてポッド内のコンテナーで共有されます。次に、father cgroup、IPC/Network/Pid 名前空間、sysctls、Linux パーミッションなど、ポッド用の Linux 構成を定義します。全体のプロセスは次のとおりです:

ソースコード

func (m *kubeGenericRuntimeManager) createPodSandbox(pod *v1.Pod, attempt uint32) (string, string, error) {
	podSandboxConfig, err := m.generatePodSandboxConfig(pod, attempt)
	...
	// Create pod logs directory
	err = m.osInterface.MkdirAll(podSandboxConfig.LogDirectory, 0755)
	...
	podSandBoxID, err := m.runtimeService.RunPodSandbox(podSandboxConfig, runtimeHandler)
	...
	return podSandBoxID, "", nil
}
func (m *kubeGenericRuntimeManager) generatePodSandboxConfig(pod *v1.Pod, attempt uint32) (*runtimeapi.PodSandboxConfig, error) {
	podUID := string(pod.UID)
	podSandboxConfig := &runtimeapi.PodSandboxConfig{
		Metadata: &runtimeapi.PodSandboxMetadata{
			Name: pod.Name,
			Namespace: pod.Namespace,
			Uid: podUID,
			Attempt: attempt,
		},
		Labels: newPodLabels(pod),
		Annotations: newPodAnnotations(pod),
	}
	dnsConfig, err := m.runtimeHelper.GetPodDNS(pod)
	...
	podSandboxConfig.DnsConfig = dnsConfig
	if !kubecontainer.IsHostNetworkPod(pod) {
		podHostname, podDomain, err := m.runtimeHelper.GeneratePodHostNameAndDomain(pod)
		podHostname, err = util.GetNodenameForKernel(podHostname, podDomain, pod.Spec.SetHostnameAsFQDN)
		podSandboxConfig.Hostname = podHostname
	}
	logDir := BuildPodLogsDirectory(pod.Namespace, pod.Name, pod.UID)
	podSandboxConfig.LogDirectory = logDir
	portMappings := []*runtimeapi.PortMapping{}
	for _, c := range pod.Spec.Containers {
		containerPortMappings := kubecontainer.MakePortMappings(&c)
		...
	}
	if len(portMappings) > 0 {
		podSandboxConfig.PortMappings = portMappings
	}
	lc, err := m.generatePodSandboxLinuxConfig(pod)
	...
	podSandboxConfig.Linux = lc
	return podSandboxConfig, nil
}
// generatePodSandboxLinuxConfig generates LinuxPodSandboxConfig from v1.Pod.
func (m *kubeGenericRuntimeManager) generatePodSandboxLinuxConfig(pod *v1.Pod) (*runtimeapi.LinuxPodSandboxConfig, error) {
	cgroupParent := m.runtimeHelper.GetPodCgroupParent(pod)
	lc := &runtimeapi.LinuxPodSandboxConfig{
		CgroupParent: cgroupParent,
		SecurityContext: &runtimeapi.LinuxSandboxSecurityContext{
			Privileged: kubecontainer.HasPrivilegedContainer(pod),
			SeccompProfilePath: v1.SeccompProfileRuntimeDefault,
		},
	}
	sysctls := make(map[string]string)
	if utilfeature.DefaultFeatureGate.Enabled(features.Sysctls) {
		if pod.Spec.SecurityContext != nil {
			for _, c := range pod.Spec.SecurityContext.Sysctls {
				sysctls[c.Name] = c.Value
			}
		}
	}
	lc.Sysctls = sysctls
	if pod.Spec.SecurityContext != nil {
		sc := pod.Spec.SecurityContext
		if sc.RunAsUser != nil {
			lc.SecurityContext.RunAsUser = &runtimeapi.Int64Value{Value: int64(*sc.RunAsUser)}
		}
		if sc.RunAsGroup != nil {
			lc.SecurityContext.RunAsGroup = &runtimeapi.Int64Value{Value: int64(*sc.RunAsGroup)}
		}
		lc.SecurityContext.NamespaceOptions = namespacesForPod(pod)
		if sc.FSGroup != nil {
			lc.SecurityContext.SupplementalGroups = append(lc.SecurityContext.SupplementalGroups, int64(*sc.FSGroup))
		}
		if groups := m.runtimeHelper.GetExtraSupplementalGroupsForPod(pod); len(groups) > 0 {
			lc.SecurityContext.SupplementalGroups = append(lc.SecurityContext.SupplementalGroups, groups...)
		}
		if sc.SupplementalGroups != nil {
			for _, sg := range sc.SupplementalGroups {
				lc.SecurityContext.SupplementalGroups = append(lc.SecurityContext.SupplementalGroups, int64(sg))
			}
		}
		if sc.SELinuxOptions != nil {
			lc.SecurityContext.SelinuxOptions = &runtimeapi.SELinuxOption{
				User: sc.SELinuxOptions.User,
				Role: sc.SELinuxOptions.Role,
				Type: sc.SELinuxOptions.Type,
				Level: sc.SELinuxOptions.Level,
			}
		}
	}
	return lc, nil
}

要約

Kubeletの中心的な仕事は、制御ループを中心に展開され、制御ループを機能させ、望ましい状態に到達させるために、プロデューサーとコンシューマーが協力するための基礎としてgoチャンネルを使用します。

Read next

Node-sassがインストールできない、エラーピット

Node-sassは、Node.jsとLibSassをバインドするライブラリです。.scssファイルを信じられないスピードでネイティブにcssにコンパイルしたり、ミドルウェアに接続してコンパイルを自動化したりすることができます。 基本的にはあまり使っていなかったのですが、今日sassのプロジェクトを引っ張り出してきてインストールしたところ、すぐに爆速になりました...。

Mar 16, 2020 · 1 min read