Kubernetes Pod を正常に再起動する方法

Kubernetes Pod を正常に再起動する方法

解決策1

さまざまな環境に多数の Pod があるため、手動で 1 つずつ再起動することは不可能です。これまでにも同様の操作を実行しました:

 kubectl delete --all pods --namespace=dev

この方法では、dev 名前空間内の Pod をワンクリックで削除でき、Kubernetes はこれらの Pod を自動的に再起動してアプリケーションの可用性を確保します。

しかし、大きな問題は、Kubernetes のスケジュールに大きな負担がかかることです。通常、名前空間の下には少なくとも数百の Pod が存在します。これらすべてを再スケジュールして起動する必要がある場合、 Kubernetesの負荷が非常に高くなり、注意しないと深刻な結果を招くことになります。

したがって、私の最初のバージョンのソリューションは、すべてのデプロイメントを反復処理し、ポッドを削除し、5 分間スリープしてから、次のポッドを削除するというものでした。疑似コードは次のとおりです。

 deployments, err := clientSet.AppsV1().Deployments(ns).List(ctx, metav1.ListOptions{}) if err != nil { return err } for _, deployment := range deployments.Items { podList, err := clientSet.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("app=%s", deployment.Name), }) err = clientSet.CoreV1().Pods(pod.Namespace).Delete(ctx, pod.Name, metav1.DeleteOptions{}) if err != nil { return err } log.Printf(" Pod %s rebuild success.\n", pod.Name) time.Sleep(time.Minute * 5) }

問題点

この解決策は確かに単純かつ粗雑ですが、テスト中に問題が発見されました。

一部のサービスに Pod が 1 つしかない場合、サービスを提供する追加のコピーがないため、直接削除するとサービスは失敗します。

これは絶対に受け入れられません。

削除後に再起動に失敗したものもあります。

  • 長期間再起動しないと、イメージ キャッシュが失われたり、イメージが削除されたりして、正常に起動できなくなります。
  • 一部の Pod には起動時に何かを実行する Init-Container があり、それが失敗すると正常に起動できなくなります。つまり、Pod が正常に起動できず、オンライン運用上の問題に直接つながる状況は多くあるため、ソリューション 1 は絶対に実行可能ではありません。

解決策2

このため、私はプラン2を用意しました。

画像.png

  • まずレプリカの数を 1 増やします。これにより、新しい Pod が追加され、最新のサイドカー イメージが使用されます。
  • 新しく作成された Pod が正常に再起動するまで待ちます。
  • 再起動が成功したら、元の Pod を削除します。
  • その後、コピー数を以前の数に戻します。

これにより、元の Pod をスムーズに再起動できるようになります。新しい Pod の起動に失敗した場合、他の Deployment Pod は再起動されません。古い Pod も保持されますが、サービス自体には影響はありません。

問題点

問題はなさそうですが、実装が面倒で、手順が非常に煩雑です。ここにいくつかのコアコードを貼り付けます:

 func RebuildDeploymentV2(ctx context.Context, clientSet kubernetes.Interface, ns string) error { deployments, err := clientSet.AppsV1().Deployments(ns).List(ctx, metav1.ListOptions{}) if err != nil { return err } for _, deployment := range deployments.Items { // Print each Deployment log.Printf("Ready deployment: %s\n", deployment.Name) originPodList, err := clientSet.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("app=%s", deployment.Name), }) if err != nil { return err } // Check if there are any Pods if len(originPodList.Items) == 0 { log.Printf(" No pod in %s\n", deployment.Name) continue } // Skip Pods that have already been upgraded updateSkip := false for _, container := range pod.Spec.Containers { if container.Name == "istio-proxy" && container.Image == "proxyv2:1.xx" { log.Printf(" Pod: %s Container: %s has already upgrade, skip\n", pod.Name, container.Name) updateSkip = true } } if updateSkip { continue } // Scale the Deployment, create a new pod. scale, err := clientSet.AppsV1().Deployments(ns).GetScale(ctx, deployment.Name, metav1.GetOptions{}) if err != nil { return err } scale.Spec.Replicas = scale.Spec.Replicas + 1 _, err = clientSet.AppsV1().Deployments(ns).UpdateScale(ctx, deployment.Name, scale, metav1.UpdateOptions{}) if err != nil { return err } // Wait for pods to be scaled for { podList, err := clientSet.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("app=%s", deployment.Name), }) if err != nil { log.Fatal(err) } if len(podList.Items) != int(scale.Spec.Replicas) { time.Sleep(time.Second * 10) } else { break } } // Wait for pods to be running for { podList, err := clientSet.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("app=%s", deployment.Name), }) if err != nil { log.Fatal(err) } isPending := false for _, item := range podList.Items { if item.Status.Phase != v1.PodRunning { log.Printf("Deployment: %s Pod: %s Not Running Status: %s\n", deployment.Name, item.Name, item.Status.Phase) isPending = true } } if isPending == true { time.Sleep(time.Second * 10) } else { break } } // Remove origin pod for _, pod := range originPodList.Items { err = clientSet.CoreV1().Pods(ns).Delete(context.Background(), pod.Name, metav1.DeleteOptions{}) if err != nil { return err } log.Printf(" Remove origin %s success.\n", pod.Name) } // Recover scale newScale, err := clientSet.AppsV1().Deployments(ns).GetScale(ctx, deployment.Name, metav1.GetOptions{}) if err != nil { return err } newScale.Spec.Replicas = newScale.Spec.Replicas - 1 newScale.ResourceVersion = "" newScale.UID = "" _, err = clientSet.AppsV1().Deployments(ns).UpdateScale(ctx, deployment.Name, newScale, metav1.UpdateOptions{}) if err != nil { return err } log.Printf(" Depoloyment %s rebuild success.\n", deployment.Name) log.Println() } return nil }

コードがさらに増えていることがわかります。

最終解決策

もっと簡単な方法はありますか?上記の解決策をリーダーに伝えると、彼は唖然としました。これは複雑すぎます。kubectl には直接ローリング再起動を行うコマンドがありませんか?

 ❯ k rollout -h Manage the rollout of one or many resources. Available Commands: history View rollout history pause Mark the provided resource as paused restart Restart a resource resume Resume a paused resource status Show the status of the rollout undo Undo a previous rollout

kubectl rollout restart deployment/abcこのコマンドは、デプロイメントabcに対してローリング アップデートを実行するために使用できます。この更新操作は Kubernetes サーバー上で実行されます。実行手順はソリューション 2 と似ていますが、Kubernetes の実装が私のものよりも厳密である点が異なります。

その後、Istio の公式アップグレード ガイドで次のコマンドも見かけました

したがって、公式ドキュメントを注意深く読む必要があります。

kubectlを統合する

準備が整ったので、このコマンドをスクリプトに統合し、名前空間の下のデプロイメントを走査するときにループで呼び出すことができます。

ただし、 Kubernetes client-go SDKにはこのロールアウト コマンドの API はありません

したがって、kubectl のソース コードを参照して、機能のこの部分をコピーするだけです。しかし幸いなことに、私のプロジェクトでは kubect に直接依存することができます。

 require ( k8s.io/api v0.28.2 k8s.io/apimachinery v0.28.2 k8s.io/cli-runtime v0.28.2 k8s.io/client-go v0.28.2 k8s.io/klog/v2 v2.100.1 k8s.io/kubectl v0.28.2 )

ソース コードで使用されている RestartOptions 構造は公開されているため、ソース コードに基づいて少し変更しました。

 func TestRollOutRestart(t *testing.T) { kubeConfigFlags := defaultConfigFlags() streams, _, _, _ := genericiooptions.NewTestIOStreams() ns := "dev" kubeConfigFlags.Namespace = &ns matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags) f := cmdutil.NewFactory(matchVersionKubeConfigFlags) deploymentName := "deployment/abc" r := &rollout.RestartOptions{ PrintFlags: genericclioptions.NewPrintFlags("restarted").WithTypeSetter(scheme.Scheme), Resources: []string{deploymentName}, IOStreams: streams, } err := r.Complete(f, nil, []string{deploymentName}) if err != nil { log.Fatal(err) } err = r.RunRestart() if err != nil { log.Fatal(err) } }

最後に、いくつかのデバッグ手順を経た後、実行できます。このロジックの部分をループに移動し、スリープを追加してPod を定期的に再起動します。

参考リンク:

  • https://istio.io/latest/docs/setup/upgrade/canary/#data-plane。
  • https://github.com/kubernetes/kubectl/blob/master/pkg/cmd/rollout/rollout_restart.go。

<<:  クラウド移行とは何ですか?クラウドへの移行に関する必須ガイド

>>:  IDC の評価アドバイス: ROI を最大化するには、ワークロードに最適なクラウドを選択してください

推薦する

SimpleNode-1GメモリKVM月額支払い7ドル

SimpleNode の KVM ベースの VPS の拡張を記念して、プロモーションを開始しました。...

出会い系サイト MiniDates: 出会いは宝くじのようなもの

他の出会い系アプリと比較して、MiniDatesはリアルな出会いを促進することに力を入れていますデー...

電子商取引企業が再び生鮮食品市場に群がる:見た目は良いが、試してみると失敗する

Vipshop のクリアランスセールモデルが業界から賞賛された後、大手 e コマース企業が生鮮食品ビ...

週刊ニュースレビュー: 電子商取引の価格戦争 360 検索エンジン

1. JD.comが電子商取引の価格戦争を開始今週、インターネット上で最も注目されている話題は、間違...

ウェブサイト構築における初心者ウェブマスターの「本末転倒」な考え方の簡単な分析

現在、ウェブマスターは少数の人だけが就く職業ではなくなりました。ますます多くの仲間が私たちの陣営に加...

ブランドマーケティング、岡本はどうやって消費者を捉えているのか?

消費のグレードアップという文脈において、岡本は差別化されたブランドの独自性を活用し、消費者に対する正...

ウェブサイトのログ分析では、これらの点に注目することを学ぶ必要があります。

ウェブサイトのログを分析することで、ウェブサイトの健全性を正確に判断できます。また、スパイダーのクロ...

ライカクラウド:月額9.99元から利用できる高帯域幅クラウドサーバー、鎮江/宿遷/紹興/棗荘/揚州/香港/日本/韓国/米国、100Gの高防御保護

Leica Cloudは新年のプロモーションを開始しました。クラウドサーバーは月額9.99元から、更...

K されたサイトを回復するにはどうすればいいですか?

百度がアルゴリズムを調整して以来、ウェブマスターを探している多くの草の根ウェブサイトがK-edされま...

ハッカーは、Apple iPhone 5Sが米国政府の指紋収集に役立っていると主張

有名なハッカー集団「アノニマス」は最近、Apple が米国国家安全保障局 (NSA) と協力して、最...

テンセントクラウド:「デジタルエコシステム」特別「クラウドサーバー」フラッシュセール、最低99元/年

テンセントのグローバルデジタルエコシステム特別イベントが始まりました。合計5つのプロモーションがあり...

Vultr Australiaはどうですか?メルボルンクラウドサーバーレビュー

Vultr はオセアニア、具体的にはオーストラリアのメルボルンとシドニーにもデータセンターを提供して...

モバイル検索が今後も発展を続けていくためには、どこに重点を置くべきでしょうか?

最近の関連データによると、デスクトップ検索のリーダーである百度は、モバイル検索でも依然として強い地位...

クラウドコンピューティング: 資金を浪費しながら成長する

現在、10年以上の試練と苦難を経て、中国のクラウド業界は急成長期に入り、大手インターネット企業や業界...

ご覧の通り、ダブル11のカーニバルです。真面目な話、これがクラウドコンピューティングの起源なのかもしれません。

[[249072]] 01 長期主義の勝利毎年恒例のダブル11が到来し、買い物好きの人たちはショッピ...