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 を最大化するには、ワークロードに最適なクラウドを選択してください

推薦する

海外ネイティブ IP VPS サーバー、ネイティブ IP サーバー、複数のストリーミング メディアのロックを解除します。

多くのビジネスでは、対外貿易促進ビジネスやTikTokでのマーケティングなど、IP、特にネイティブI...

リンク交換の見落とされがちな指標

外部リンクは、ウェブマスター業界では王様の地位を占めています。ウェブマスターは皆、その重要性を非常に...

クラウドで自動化を適用する 5 つの方法

インフラストラクチャ自動化とそのサブカテゴリであるクラウド内のアプリケーション自動化には多くの重複が...

Google PR 価値 VS Baidu 重み、ウェブマスターは何をすべきでしょうか?

グーグルが国内市場で徐々に衰退するにつれ、百度が検索エンジンの支配的地位を獲得した。多くのウェブマス...

Baidu StatisticsとGoogle Analyticsの同じ機能の比較

2011 年、Baidu Tongji はインターフェース、機能、レポートに多くの重要な改善を加えま...

教育・研修ウェブサイト運営ガイド:コンテンツ戦略

いわゆる教育・研修ウェブサイトとは、教育情報や研修コース情報の提供を主な内容とするウェブサイトを指し...

ウェブサイト運営について知っておくべきこと

インターネットの台頭と電子商取引の発展に伴い、多くの企業が電子商取引の仲間入りを果たしました。電子商...

動画サイトは利益を上げたいという隠れた願望を露呈:帯域幅とコンテンツコストが急落

動画業界全体の広告価格は、2013年上半期に30%上昇すると予想されています。今年、1~2社が利益を...

digitalocean - 4月の10ドル割引コード

Digitalocean はコンソールキャットに長い間登場していませんでした。注目していないわけでは...

クラウドで機密データを保護するための 3 つのベスト プラクティス

BetterCloud による最近の調査によると、企業は共同作業、通信、開発、契約の管理、署名の承認...

フォーブス誌が2013年トップ10ソフトウェアベンダーリストを発表

海外メディアの報道によると、オラクルは10月17日、IT業界の財務データを引用し、同社がマイクロソフ...

外国貿易ウェブサイトで競合ブランドのキーワードを追跡する方法

まず、競合他社が設定しているキーワードや、製品の説明に使用している用語など、競合他社のキーワードを理...

Openstack Neutron ネットワーク仮想化

まず、ネットワーク仮想化はなぜ必要なのでしょうか? 1. データセンターの既存のネットワークはクラウ...

B2C+BBS ShopexとDiscuzの統合に関する詳細なチュートリアル

ShopExは中国で最も市場シェアの高いオンラインストアソフトウェアであり、Discuzは世界で最も...

V.PS: 東京のソフトバンク VPS、20% 割引、月額 4.76 ユーロ、1G メモリ/2 コア/20g SSD/1T トラフィック/1Gbps 帯域幅

V.PSは現在、日本の東京データセンターのVPSで20%割引のプロモーションを実施しています。日本の...