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

推薦する

vps4cheap-3.5/kvm/512m メモリ/5gSSD/500g トラフィック/Win/Phoenix

vps4cheap.com のドメイン名は に登録されたため、新しい販売業者とみなされます。現在の ...

ビリビリがWeChatの役目を担う

「この調子が続けば、ビリビリは金融部門を開設できるだろう」ビリビリのユーザーは、最近多くの一般アカウ...

アリババが土地を奪い、グーグルが資金を投じる:クラウドコンピューティング大手、春の軍拡競争開始

近年、クラウド コンピューティングは、俊敏性、拡張性、コストなどの利点により、企業が IT 変革を実...

タオバオストアの購買転換率に影響を与える致命的なポイント

少し前、多くの淘宝網の販売業者が張立氏を見つけ、自分の店舗の状況を調べるのを手伝ってほしいと頼んだ。...

Orange VPS: 50% オフのプロモーション、3 つのネットワークへの直接接続 (CN2 を返す) + DDOS 保護、ネイティブ米国 IP で「Ne​​tflix」のロックを解除

中国の新商人Juzi VPSは、米国ロサンゼルスのVPSに注力しており、発信ルートは3つのネットワー...

ベンチャーキャピタルは国内のテクノロジーブログに熱心:戦略的意義として収益性を重視していない

「私は36Kr、Huxiu、Leifengをよく読んでいます。初期段階の投資家として、国内のテクノロ...

ramnode3か月間、openvz/ssd キャッシュ/G ポートが 35% オフ

ramnode は 2009 年に設立された VPS プロバイダーですが、すでによく知られています。...

アリババとテンセントがミニプログラムをめぐって争う!

WeChatミニプログラムの発売から3年が経過した現在も、Alipayミニプログラムは勢いを増し続け...

QQ Space は Baidu のキーワードをランク​​付けするために使用できますか?

私たちのグループの友人が、QQ Spaceがキーワードランキングに参加できるかどうか尋ねてきましたが...

新しい百度アルゴリズムによる外部リンク構築アイデアの分析

Baidu アルゴリズムは頻繁に更新されますが、これらの変更は基本的に、不正な SEO 最適化方法を...

百度アライアンスが監査に失敗した理由の分析例

今日、グループで誰かが質問しているのを見ました。なぜ 2 つの Baidu Alliance に申し...

日常の話題:Taobaoオンラインストアは相続や離婚による譲渡で他者に譲渡できます。

ウェブマスターネットワーク(www.admin5.com)は7月25日、タオバオの「オンラインストア...

分散SQLビッグデータクエリエンジンの開発

導入大まかに見ると、多くのデータおよび分析ソリューションは長年にわたって同じ方法で構築されてきました...

Yahoo 検索向けにウェブサイトを最適化する方法

HTMLコードの最適化Google や MSN と比較すると、Yahoo! は HTML コードに重...

NetApp、ハイブリッドクラウドデータ管理ソリューションを発表

NetApp は本日、ハイブリッド クラウド全体で顧客がデータを最大限に制御し、データ移動のセキュリ...