新聞中心
今天就跟大家聊聊有關(guān)Kubernetes上如何控制容器的啟動(dòng)順序,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
專(zhuān)注于為中小企業(yè)提供成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)道縣免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了千余家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
控制 Pod 內(nèi)容器的啟動(dòng)順序,分析了 TektonCD 的容器啟動(dòng)控制的原理。
為什么要做容器啟動(dòng)順序控制?我們都知道 Pod 中除了 init-container 之外,是允許添加多個(gè)容器的。類(lèi)似 TektonCD 中 task 和 step 的概念就分別與 pod 和 container 對(duì)應(yīng),而 step 是按照順序執(zhí)行的。此外還有服務(wù)網(wǎng)格的場(chǎng)景,sidecar 容器需要在服務(wù)容器啟動(dòng)之前完成配置的加載,也需要對(duì)容器的啟動(dòng)順序加以控制。否則,服務(wù)容器先啟動(dòng),而 sidecar 還無(wú)法提供網(wǎng)絡(luò)上的支持。
現(xiàn)實(shí)

期望

到了這里肯定有同學(xué)會(huì)問(wèn),spec.containers[] 是一個(gè)數(shù)組,數(shù)組是有順序的。Kubernetes 也確實(shí)是按照順序來(lái)創(chuàng)建和啟動(dòng)容器,但是 容器啟動(dòng)成功,并不表示容器可以對(duì)外提供服務(wù)。
在 Kubernetes 1.18 非正式版中曾在 Lifecycle 層面提供了對(duì) sidecar 類(lèi)型容器的 支持,但是最終該功能并沒(méi)有落地。
那到底該怎么做?
TL;DR
筆者準(zhǔn)備了一個(gè)簡(jiǎn)單的 go 項(xiàng)目,用于模擬 sidecar 的啟動(dòng)及配置加載。
克隆代碼后可以通過(guò) make build 構(gòu)建出鏡像,假如你是用的 minikube 進(jìn)行的實(shí)驗(yàn),可以通過(guò)命令 make load-2-minikube 將鏡像加載到 minikube 節(jié)點(diǎn)中。
使用 Deployment 的方式進(jìn)行部署,直接用 Pod 也可以。
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: sample
name: sample
spec:
replicas: 1
selector:
matchLabels:
app: sample
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: sample
spec:
containers:
- image: addozhang/k8s-container-sequence-sidecar:latest
name: sidecar
imagePullPolicy: IfNotPresent
lifecycle:
postStart:
exec:
command:
- /entrypoint
- wait
- image: busybox:latest
name: app
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c"]
args: ["date; echo 'app container started'; tail -f /dev/null"]下面的截圖中,演示了在 sample 命名空間中,pod 內(nèi)兩個(gè)容器的執(zhí)行順序。

Kubernetes 源碼
在 kubelet 的源碼 pkg/kubelet/kuberuntime/kuberuntime_manager.go 中,#SyncPod 方法用于創(chuàng)建 Pod,步驟比較繁瑣,直接看第 7 步:創(chuàng)建普通容器。
// SyncPod syncs the running pod into the desired pod by executing following steps:
//
// 1. Compute sandbox and container changes.
// 2. Kill pod sandbox if necessary.
// 3. Kill any containers that should not be running.
// 4. Create sandbox if necessary.
// 5. Create ephemeral containers.
// 6. Create init containers.
// 7. Create normal containers.
func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) {
...
// Step 7: start containers in podContainerChanges.ContainersToStart.
for _, idx := range podContainerChanges.ContainersToStart {
start("container", containerStartSpec(&pod.Spec.Containers[idx]))
}
return
}在 #start 方法中調(diào)用了 #startContainer 方法,該方法會(huì)啟動(dòng)容器,并返回容器啟動(dòng)的結(jié)果。注意,這里的結(jié)果還 包含了容器的 Lifecycle hooks 調(diào)用。
也就是說(shuō),假如容器的 PostStart hook 沒(méi)有正確的返回,kubelet 便不會(huì)去創(chuàng)建下一個(gè)容器。
// startContainer starts a container and returns a message indicates why it is failed on error.
// It starts the container through the following steps:
// * pull the image
// * create the container
// * start the container
// * run the post start lifecycle hooks (if applicable)
func (m *kubeGenericRuntimeManager) startContainer(podSandboxID string, podSandboxConfig *runtimeapi.PodSandboxConfig, spec *startSpec, pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, podIP string, podIPs []string) (string, error) {
...
// Step 4: execute the post start hook.
if container.Lifecycle != nil && container.Lifecycle.PostStart != nil {
kubeContainerID := kubecontainer.ContainerID{
Type: m.runtimeName,
ID: containerID,
}
msg, handlerErr := m.runner.Run(kubeContainerID, pod, container, container.Lifecycle.PostStart)
if handlerErr != nil {
m.recordContainerEvent(pod, container, kubeContainerID.ID, v1.EventTypeWarning, events.FailedPostStartHook, msg)
if err := m.killContainer(pod, kubeContainerID, container.Name, "FailedPostStartHook", reasonFailedPostStartHook, nil); err != nil {
klog.ErrorS(fmt.Errorf("%s: %v", ErrPostStartHook, handlerErr), "Failed to kill container", "pod", klog.KObj(pod),
"podUID", pod.UID, "containerName", container.Name, "containerID", kubeContainerID.String())
}
return msg, fmt.Errorf("%s: %v", ErrPostStartHook, handlerErr)
}
}
return "", nil
}實(shí)現(xiàn)方案

cmd/entrypoint/wait.go#L26 (這里參考了 Istio 的 pilot-agent 實(shí)現(xiàn) )
在 PostStart 中持續(xù)的去檢查 /ready 斷點(diǎn),可以 hold 住當(dāng)前容器的創(chuàng)建流程。保證 /ready 返回 200 后,kubelet 才會(huì)去創(chuàng)建下一個(gè)容器。
這樣就達(dá)到了前面截圖中演示的效果。
for time.Now().Before(timeoutAt) {
err = checkIfReady(client, url)
if err == nil {
log.Println("sidecar is ready")
return nil
}
log.Println("sidecar is not ready")
time.Sleep(time.Duration(periodMillis) * time.Millisecond)
}
return fmt.Errorf("sidecar is not ready in %d second(s)", timeoutSeconds)看完上述內(nèi)容,你們對(duì)Kubernetes上如何控制容器的啟動(dòng)順序有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。
當(dāng)前標(biāo)題:Kubernetes上如何控制容器的啟動(dòng)順序
鏈接URL:http://www.dlmjj.cn/article/iegcod.html


咨詢(xún)
建站咨詢(xún)
