見出し画像

Kubernetesネットワーク応用編

応用編は前回の続き、ポッド、コンテナ、サービスを中心に検証していきます。

1.Kubernetesのポッドとサービス

Kubernetesクラスタ内でポッドをスピンアップしたり、deploymentを展開したりする場合、ポッドマニフェストファイル(YAML形式)を作成する必要があります。ポッドマニフェストファイルはAKMSという4つの要素が必要となります。

画像2

ポッドをクラスタ外部に公開する場合は、Kubernetesサービスを使用します。Kubernetesサービスオブジェクトは、内部ポッドエンドポイントをサービスとして外部に公開します。

Kubernetesサービスは基本的にファイアウォールルールです。このルールは、基本的に2つの理由で作成されます。
1. ポッドを外に公開すること。
2. ポッドの集まり間の負荷分散を行い、ポッドが停止した場合にそのIPを取り除くことができます。

2.Kubernetesが使っているポートの解説

Kubernetesは、ポッドコンテナの仕様(spec)を作成するとき、またはサービスを作成するときに、さまざまなタイプのポートを使用します。これらは、nodePort、port、targetPort、containerPortです。これらの違いは何でしょうか?いつ何を使うのでしょうか?本章ではこちらについて詳しく解説していきます。

ポッドマニフェストファイルのポッドspecで、コンテナspecを定義する必要があります。このspecには、Docker Hubまたはカスタムリポジトリから取得されるコンテナイメージ名やイメージバージョンなどの情報が含まれています。コンテナspecでは、コンテナが使用するポートを定義する必要があります。これは、ポッドマニフェストのコンテナspecのcontainerPort指令で定義できます。

「containerPort」は、コンテナ内で外部アプリにアクセスできるポートを定義します。

例えばnginxのDockerイメージはポート80を公開するため、上記nginxのYAMLファイルではcontainerPort80を指定します。mysqlのDockerイメージはポート3306を公開するため、containerPortは3306を指定する必要があります。

[r-user@bastion ~]$ kubectl create -f nginx1105.yaml
pod/nginx1105 created
[r-user@bastion ~]$ kubectl get pods -l app=nginx -o yaml | grep podIP:
         f:podIP: {}
   podIP: 10.233.113.10

# クラスタ内部の任意ノードから下記コマンドを実行。
[r-user@node01 ~]$ curl 10.233.113.10:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
......

作成されたポッドのポート80にcurlするとWelcome to nginx!が表示されました。
ポッドを外部に公開するには、nodePortタイプのサービスを作成する必要があります。ただし、nodePortの前には、外部トラフィックリクエストをポッドエンドポイントに正しくルーティングするために、他の2つのポートが必要です。portとtargetPortです。

「nodePort」は、サービスオブジェクトYAMLで定義された「port」にて、外部トラフィックをクラスタに送信します。

サービスが外部ソースからトラフィックを受信すると、2つのことを行います。
1. まず、nodePortで受信したトラフィックをYAMLの「Port」ディレクティブで定義されたポートに転送します。「Port」は、サービスがリッスンするポートを定義するために使用されます。
2. 「Port」で受信したトラフィックを「targetPort」にリダイレクトします。「targetPort」は、コンテナがアプリケーションを公開したポートを定義するために使用されるディレクティブです。

targetPortとcontainerPortは、ほとんどの場合同一である必要があります。理由は、targetPortを介してサービスからコンテナにトラフィックを送信するポートは、アプリケーションに対して開いているポートが何であれ、同じにするべきだからです。

一方、nodePortは、サービスが外部に公開されるポートです。したがって、サービスにアクセスするときにブラウザに入力するポートになります。

下記デモサービスYAMLファイルを使用して検証します。

画像5

下記コマンドでサービスを作成します。

[r-user@bastion ~]$ kubectl create -f nginx-port-demo-svc.yaml
service/port-demo created
[r-user@bastion ~]$ kubectl get svc
NAME                TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
kubernetes          ClusterIP      10.233.0.1      <none>        443/TCP                      34d
port-demo           NodePort       10.233.36.193   <none>        30080:30001/TCP              21m

curlコマンドを使用して、クラスタ内部からclusterIP:port(10.233.36.193:30080)にアクセスすることが検証できます。一方クラスタ外部からnodeIP:nodePort(nodeIP:30001)を使用してアクセスできます。targetPortは必ずnginx固有の80を使用しないといけません。

ここではデモのためnodePortを指定しましたが、nodePortフィールドは省略可能です。フォルトでは利便性のため、Kubernetesコントロールプレーンはある範囲から1つポートを割り当てます(デフォルト値の範囲:30000-32767)。

まとめると、portとnodePortはどちらもサービスポートです。前者はクラスタの内部サービスアクセスに公開され、後者はクラスタ外部トラフィックアクセスに公開されます。これら2つのポートから到着するトラフィックは、リバースプロキシkube-proxyを通過し、バックエンドポッドのtargetPortに流れ込み、最後にポッド内部コンテナのcontainerPortに到達します。

3.LoadBalancerを使用してサービスを公開

上記NodePortでサービスを公開するやり方は下記制限のためサービスの可用性に影響が出ています。
1. 公開された各サービスは、すべてのノードの特定のポートを占有する必要がある
2. ポート範囲は30000-32767に限定
3. ノードIPアドレスが変更された場合に、都度対処することが必要
なので、プロダクションサービスの場合はNodePortは推奨されません。LoadBalancerあるいはIngressを使用して外部に公開することは一般的なやり方です。「楽天クラウドRed Hat® OpenStack Platform」(以下「OSP」)のLoadBalancerサービスはKubernetesクラスタのサービス公開をサポートできます。Kubernetesコマンドのみ自動的に作成されることはできないため、下記の通り手動設定を含めてLoadBalancer構築手順を説明します。

下記LoadBalancerタイプのサービスを作成してみます。

画像5

[r-user@bastion ~]$ kubectl create -f nginx1105-service.yaml
service/nginx1105-service created
[r-user@bastion ~]$ kubectl get svc nginx1105-service
NAME                TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
nginx1105-service   LoadBalancer   10.233.4.155   <pending>     80:32545/TCP   11s

作成することはできましたが、どのくらい時間が経ってもEXTERNAL-IPはpending状態で自動的にLoadBalancerは作成されません。おかしいと思うかもしれませんが、実はこの時点でNodePortサービスは作成されています。nodeIP:32545にcurlするとWelcome to nginx!が表示されます。

次はこの手順を参照しながら「楽天クラウドOSP」のコンソルを使用してLoadBalancerを作ります。
LoadBalancer名前:LB-nginx1105
サブネット:クラスタのサブネット
リスナー名前:listener1
リスナーProtocol:HTTP
リスナーポート:80
肝心なプールはクラスタのノードIP(例のクラスタは4つノード)とnodePort(32545)を設定します。

画像5

最後にFloating IPを作成してLoadBalancerに割当てます。ブラウザでFloating IPをアクセスするとNginx画面が問題なく表示されます。

LoadBalancerを作成しても、pendingのEXTERNAL-IPは自動的にFloating IPに変更することはできません。気になる場合は下記コマンドで直すことができます。

[r-user@bastion ~]$ kubectl patch svc nginx1105-service  -p '{"spec": {"type": "LoadBalancer", "externalIPs":["103.136.155.64"]}}'
service/nginx1105-service patched
[r-user@bastion ~]$ kubectl get svc nginx1105-service
NAME                TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
nginx1105-service   LoadBalancer   10.233.4.155   103.136.155.64   80:32545/TCP   63m

4. LoadBalancerの注意点

パフォーマンス
nodePortで転送する必要があります。LBは最初にトラフィックをあるノードに転送して、次にkube-proxyを介して転送します。ポッドがこのノードにない場合は、別のノードに転送する必要があります。余分なジャンプが生じます。
ある程度の手作業が必要ですが、コミュニティが提供しているexternalTrafficPolicyメカニズムやプールのウェイトの調整を活用して均等にトラフィック転送を設定する方法があります。

スケーラビリティ
同時にすべてのノードをLBに直接マウントするため、クラスタサイズが大きくなると、LBの制限に達し、マシンを追加することはできません(「OSP」の場合はインスタンス数制限が先に達します。テナントごとに最多20インスタンスとなります)。

機能
送信元IPのロス問題が発生します。転送プロセスにはSNATとNATが必要なので、特定のビジネスシナリオではユーザーのニーズを満たすことができません。
「楽天クラウドOSP」のLBサービスはSSL終端機能をサポートできます。ご興味がありましたら担当営業にお申し付けください。

楽天クラウド:https://cloud.rakuten.co.jp/

画像1

柳 松
外資系ITメーカー入社、ストレージソリューションSEから始め、アーキテクト、プレセールス、ビジネスデベロップメントを経験。インフラ系から現職のクラウド系新規サービス開発にチャレンジ中。最近の趣味は読書。特に歴史と社会に興味を持っている。理科と違って、正解のない世界を面白く感じている。
よろしければ、フォローもよろしくお願いいたします!
3
中のひとの日常や、お客様のデジタル・トランスフォーメーションのお役に立つかもしれない?情報を発信していきます。