コンテナ環境で「アドレスが利用できません」というメッセージが表示されたことを思い出してください

コンテナ環境で「アドレスが利用できません」というメッセージが表示されたことを思い出してください

送信元アドレスが混乱しています

ポッドが作成されると、しばらくは正常に実行されます。ある日突然、新しい接続が作成されていないことがわかります。ビジネスは、ポッド A を介して svc B の svc 名にアクセスします。ポッドに入り、手動で wget を使用して、アドレスが利用できないというエラーが報告されていることを確認します。このエラーはなぜ発生するのでしょうか?

以下に例の図を示します。

「アドレスが利用できません」と表示されるのはなぜですか?利用できないアドレスは何ですか?多くの情報を調べ、POSIX (Portable Operating System Interface for UNIX) 標準のエラー定義に関連する定義を見つけましたが、まだよくわかりません。

エラーコード参照リンク: [errno.3[1]]

 EADDRNOTAVAIL Address not available (POSIX.1-2001).

見落としがちなカーネルパラメータ

svc への接続のアドレスは、netstat -an を通じてチェックされます。確立状態の接続数が、使用可能なランダム ポート数のしきい値に達したため、新しい接続を確立できません。

最終的に、カーネル パラメータのランダム ポート net.ipv4.ip_local_port_range ポート範囲を変更することで問題は解決しました。

Linux カーネルはランダムなポート 32768 ~ 60999 を定義しますが、これはビジネス設計シナリオでは簡単に見落とされる可能性があります。各 TCP 接続は 4 つのタプル (送信元 IP、送信元ポート、宛先 IP、宛先ポート) で構成されていることは誰もが知っています。 4 つのタプルのうち 1 つが変更される限り、TCP 接続を作成できます。 POD が固定の宛先 IP + 宛先ポートにアクセスする場合、各 TCP 接続の唯一のランダム変数は送信元ポートです。したがって、長い接続を大量に作成する必要がある場合は、カーネルのランダム ポートを増やすか、ビジネスを調整する必要があります。

関連カーネル参照リンク: [ip-sysctl.txt[2]]

 ip_local_port_range - 2 INTEGERS Defines the local port range that is used by TCP and UDP to choose the local port. The first number is the first, the second the last local port number. If possible, it is better these numbers have different parity (one even and one odd value). Must be greater than or equal to ip_unprivileged_port_start. The default values are 32768 and 60999 respectively.

同じ問題で他にどのような種類のエラーが発生する可能性がありますか?

net.ipv4.ip_local_port_range を手動で減らしてから再現します。

私も同じ問題に遭遇しました。それぞれ curl、nc、wget コマンドを試しましたが、すべて異なるエラーが発生しました。これは非常に不可解でした。

統一できないのでしょうか?

  • curl: (7) サーバーに接続できませんでした
  • nc: bind: 使用中のアドレス
  • wget: リモートホスト (1.1.1.1) に接続できません: アドレスが利用できません

次に、strace コマンドを使用してプロセスを分析し、指定されたシステム コール名を追跡します。それらはすべて socket() を作成します。すると、wget/curl コマンドは connect() 関数を介して呼び出され、nc コマンドは最初に bind() 関数を介して呼び出されることがわかります。エラーが報告された場合、connect() 関数は呼び出されません。

図に示すように、B/S アーキテクチャの分析により、クライアントがソケットを作成した後に connect() が確立されます。

考えさせられる

wget/curl の両方が connect() 関数を呼び出すときに、異なるエラーを報告するのはなぜですか?

各クライアント プログラムにはカスタム エラー コードがあります。同じ connect() 関数がエラーを報告した後、wget は POSIX 標準エラー定義「アドレスが利用できません」を直接出力しますが、curl は独自に定義されたエラー コードと対応するプロンプト メッセージ「curl: (7) サーバーに接続できませんでした」を出力します。エラーコードは 7 です。curl のエラー定義は lib/strerror.c にあります。

connect() 関数と bind() 関数が異なるエラーを報告するのはなぜですか?

関数によってエラーの定義は異なりますが、POSIX 標準エラー定義で確認できます。

 EADDRINUSE Address already in use (POSIX.1-2001). EADDRNOTAVAIL Address not available (POSIX.1-2001).

これはすべての場合の出力ですか?

次に、Centos7.9 システムを直接見つけて、curl、wget、nc などのツールをインストールしました。ポート範囲を小さい範囲に変更すると、「要求されたアドレスを割り当てることができません」というエラー メッセージが表示されました。このことから、一部のイメージ (alpine、busybox) では、同じコマンド ツールが同じ状況で異なるエラーを返すことがわかります。イメージ全体のサイズを縮小するために、これらのイメージではいくつかの基本コマンドにbusyboxツールボックスが使用されることがあります(上記のwgetとncはbusyboxツールボックスからのものです。busyboxのドキュメント「Busyboxコマンドヘルプ[3]」を参照してください)。これにより、問題の特定が困難になります。

Linuxでは、エラーコードに関連する定義をインクルードするために使用されます: /usr/include/asm-generic/errno.h

 #define EADDRNOTAVAIL 99 /* Cannot assign requested address */

コンテナ環境でのポート構成のベストプラクティス

1. 変更可能なスコープ

理論上は0〜65535が使用可能ですが、0〜1023は特権ポートであり、HTTP:80、SSH:22などの標準サービス用に予約されています。特権ユーザーのみが使用できます。同時に、トラフィック特性を通じて不正なユーザーによる攻撃も防止します。したがって、ポートを増やす場合は、ランダム ポート範囲を 1024 ~ 65535 に制限することをお勧めします。

2. Podソースポートを正しく設定する方法

(1)共通ポッドのソースポートを変更する方法

Kubernetesコミュニティは、securityContext[4]をセキュリティコンテキストを通じて変更できること、またはinitContainersコンテナに特権モードmount -o remount rw /proc/sysを与えることで変更できることを知りました。この変更は、ポッドのネットワーク名前空間でのみ有効になります。

セキュリティコンテキスト:

 ... securityContext: sysctls: - name: net.ipv4.ip_local_port_range value: 1024 65535

コンテナの初期化:

 initContainers: - command: - /bin/sh - '-c' - | sysctl -w net.core.somaxconn=65535 sysctl -w net.ipv4.ip_local_port_range="1024 65535" securityContext: privileged: true ...

(2)ホストネットワークモードポッドの変更に関する注意

クラスター 1.22 以降では、ServiceNodePortRange と競合するため、net.ipv4.ip_local_port_range を変更することは推奨されません。

Kubernetes のデフォルトの ServiceNodePortRange は 30000 ~ 32767 です。 Kubernetes 1.22 以降のバージョンでは、NodePort をリッスンする kube-proxy のロジックが削除されます。リスニング ポートがある場合、アプリケーションはランダム ポートを選択するときにこれらのリスニング ポートを回避します。 net.ipv4.ip_local_port_range の範囲が ServiceNodePortRange と重複している場合、NodePort をリッスンするロジックが削除されるため、アプリケーションはランダム ポートを選択するときに、30000 ~ 32767 などの重複部分を選択する可能性があります。 NodePort がカーネルの net.ipv4.ip_local_port_range 範囲と競合すると、TCP 接続障害が時々発生し、ヘルスチェックの失敗、ビジネス アクセス例外、その他の問題が発生する可能性があります。詳細についてはKubernetesコミュニティPR[5]を参照してください。

多数の svc を作成する場合、リスナーを作成する手順を ipvs/iptables ルールの送信だけに減らすと、接続パフォーマンスを最適化できます。もう 1 つは、特定のシナリオで多数の CLOSE_WAIT が TCP 接続を占有する問題を解決することです。 PortOpener ロジックはバージョン 1.22 以降削除されました。

kubernetes/pkg/proxy/iptables/proxier.go

f98f27b[6]の1304行目

1304 proxier.openPort(lp, replacementPortsMap)

紛争は具体的にどのように発生するのでしょうか?

テスト環境は、k8s 1.22.10、kube-proxy ネットワーク モード ipvs です。 kubelet ヘルスチェックを例にとると、ノードカーネルパラメータ net.ipv4.ip_local_port_range は 1 024 ~ 65535 に調整されます。

パケットをキャプチャするために tcpdump を展開します。ヘルスチェックの失敗を示すイベントをキャプチャした後、パケットのキャプチャを停止します。

kubelet がノード IP (192.168.66.27) + ランダム ポート 32582 (ポッド IP (192.168.66.65) + 80) を使用してポッドへの TCP ハンドシェイクを開始したことがわかります。しかし、TCP ハンドシェイク中にポッドが SYN ACK を kubelet に送信したとき、ターゲット ポートは 32582 でしたが、再送信されていました。このランダム ポートは特定のサービスのノード ポートであるため、IPVS によってインターセプトされ、ルールのバックエンドでサービスに優先権が与えられます。ただし、このバックエンド サービス (192.168.66.9) は podIP (192.168.66.65) との TCP 接続を開始しないため、バックエンド サービス (192.168.66.9) は直接破棄されます。そうすると、kubelet は SYN ACK 応答を受信できず、TCP は接続を確立できず、ヘルス チェックが失敗します。

このメッセージは、kubelet が TCP ハンドシェイクを開始し、ポッドが syn ack を返すときに再送信を続けているときに表示されます。

実際には、svc 32582 のバックエンド ポッドに送信され、直接破棄されます。

3. 事前の判断を加える

したがって、ホストネットワークは判断を追加できます。 initContainers コンテナを変更するときに、podIP と hostIP が等しくない場合は、誤操作によるノードのカーネル パラメータの変更を回避するために、net.ipv4.ip_local_port_range パラメータが変更されます。

 initContainers: - command: - /bin/sh - '-c' - | if [ "$POD_IP" != "$HOST_IP" ]; then mount -o remount rw /proc/sys sysctl -w net.ipv4.ip_local_port_range="1024 65535" fi env: - name: POD_IP valueFrom: fieldRef: apiVersion: v1 fieldPath: status.podIP - name: HOST_IP valueFrom: fieldRef: apiVersion: v1 fieldPath: status.hostIP securityContext: privileged: true ...

4. NodePort範囲を正しく設定する方法

Kubernetes では、APIServer は ServiceNodePortRange パラメーター (コマンドライン パラメーター --service-node-port-range) を提供します。これは、NodePort または LoadBalancer タイプのサービスがノード上でリッスンする NodePort ポート範囲を制限するために使用されます。このパラメータのデフォルト値は 30000 ~ 32767 です。 ACK Pro クラスターでは、Pro クラスターのコントロール プレーン パラメータをカスタマイズすることでポート範囲を変更できます。詳細については、「ACK Proクラスタのコントロールプレーンパラメータのカスタマイズ」[7]を参照してください。

NodePort ポート範囲を変更するときは注意してください。 NodePort ポート範囲が、クラスター ノード上の Linux カーネルによって提供される net.ipv4.ip_local_port_range パラメータのポート範囲と競合していないことを確認します。カーネル パラメータ ip_local_port_range は、Linux システム上の任意のアプリケーションで使用できるローカル ポート番号の範囲を制御します。 ip_local_port_range のデフォルト値は 32768~60999 で、Nodeport のデフォルト値は 30000~32767 です。

ACK クラスターのデフォルト構成では、ServiceNodePortRange パラメーターと ip_local_port_range パラメーターは競合しません。以前にこれら 2 つのパラメータのいずれかを調整してポート番号の制限を増やし、2 つの範囲が重複した場合、ノード上でネットワーク異常が時々発生し、深刻な場合にはビジネス ヘルス チェックが失敗し、クラスター ノードがオフラインになる可能性があります。デフォルト値を復元するか、2 つのポート範囲が完全に重複しないように調整することをお勧めします。

ポート範囲を調整した後も、クラスター内の一部の NodePort または LoadBalancer タイプのサービスは、ip_local_port_range パラメータのポート範囲内のポートを NodePort として引き続き使用する場合があります。現時点では、競合を回避するためにサービスのこの部分を再構成する必要があります。 kubectl edit <service-name> を使用して、spec.ports.nodePort フィールドの値を空いている NodePort に直接変更できます。

関連リンク:

  • [1] エラー番号3: https://man7.org/linux/man-pages/man3/errno.3.html
  • [2] ip-sysctl.txt: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
  • [3] Busyboxコマンドヘルプ: https://www.busybox.net/downloads/BusyBox.html
  • [4] セキュリティコンテキスト: https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/
  • [5] KubernetesコミュニティPR: https://github.com/kubernetes/kubernetes/pull/108888
  • [6] f98f27b: https://github.com/kubernetes/kubernetes/blob/f98f27bc2f318add77118906f7595abab7ab5200/pkg/proxy/iptables/proxier.go#L1304
  • [7] ACK Proクラスターのコントロールプレーンパラメータをカスタマイズする: https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/customize-ack-pro-control-plane-component-parameters

<<:  Elasticsearch クエリのイノベーション: ワイルドカード型の効率的なファジー マッチング戦略の検討

>>:  クラウドは新しい領域を開き、インテリジェンスは未来に力を与えます。Tianyi Cloud Intelligence はデジタル首都の新しい姿を描きます。

推薦する

インターネットマーケティング戦略: Weibo を製品マーケティングに活用する方法

ショートビデオ、セルフメディア、インフルエンサーのためのワンストップサービス製品マーケティングにWe...

優れたウェブサイトの外部リンクからインターネットマーケティング戦略を学ぶ

インターネットの普及に伴い、大手企業ではインターネットマーケティングの重要性がますます重視されるよう...

VPS ホスティング、年間 7 ドル / 512 MB メモリ / 10 GB SSD / 無制限トラフィック / VMware

鶏はモンスターです: Dell r810、64 論理コア、512 GB RAM、SSD S「言葉では...

百度の教育・研修業界への目立たない参入の分析

百度は目立たない形で教育・研修業界に参入すると予想される最近、一部の人が「xxトレーニング」などの単...

海外の流行TシャツサイトShirtPunchが新ゲームをリリースし、7か月で3000万を稼いだ

最近、流行のTシャツを販売している「ShirtPunch」(サイトの紹介はこちら)という非常に興味深...

中国新経済企業トップ500社の発展報告書

2008 年の国際金融危機以来、世界経済は深刻な不況に見舞われ、成長は鈍化し、将来に対する不確実性が...

spinservers: 米国のハイエンドサーバー、月額 339 ドル、2*E5-2695v4/512gDDR4/4*1.92tSSD/10Gbps 帯域幅

spinservers のダラス データ センターには、最新の CPU モデル (支払い完了後 15...

中国インターネット界のナマズ、周紅一:巨人と戦ってさらに大きくなる

周宏偉はじめに:360 が決して諦めず、常に巨人に挑戦し続けることを支えているのは、周紅義の攻撃的な...

検索エンジンマーケティングの発展の歴史を振り返る

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています1997 ...

コンテナ クラウド アーキテクチャ | Kubernetes ネットワーク モデルを理解する

Kubernetes ネットワークを使用すると、k8s ネットワーク内での通信を構成できます。フラッ...

チュートリアル: BandwagonHost VPS のデータセンターを変更する

BandwagonHost の IP アドレスが「不明」になったり、BandwagonHost VP...

SaaS: 急成長を遂げる高品質なクラウドコンピューティングトラック

ガートナーによると、世界のクラウドコンピューティング市場規模は2020年までに4,114億米ドルに達...

Weibo マーケティングのやり方を聞かないでください。メディア マーケティングには 6 つのステップが必要です。

マーケティングは不思議なものだと言われています。長い髪と道教の僧侶の衣装(少なくともそのように見える...

ウェブサイトの最適化中に、ウェブサイトのどの要素に特に注意を払うべきでしょうか?

ウェブサイトの最適化中に、ウェブサイトのどの要素にもっと注意を払う必要がありますか? SEO最適化ワ...