1. TOP
  2. BLOG
  3. TECH ARTICLE:連載 第4回
    Kubernetesネットワークの理解
TECH ARTICLE

2024年04月01日

連載 第4回
Kubernetesネットワークの理解

今回の実習では、クバネティスネットワークの基本概念を確認します。

1. 既存VM環境とクバネティスのネットワークの違い

クバネティス環境のネットワークは既存のVM環境のネットワークと比較して幾つか異なる点があります。
クバネティスはコンテナオーケストレーションプラットフォームで、ネットワークの抽象化レベルが高いです。クバネティスを使用するとサービス、Ingress、ネットワークポリシーなどのネットワークに関連するリソースを使用しますが、クバネティスは複雑な作業、詳細を単純化してユーザに見えないようにします。ユーザはネットワーク関連の作業をするとき、その作業がどのように行われるのか分からなくても、簡単なコマンドまたはインターフェースで求める結果を得られます。ハイレベルなオブジェクトを使用して複雑なネットワーク構成を単純化します。
一方、VM環境は一般的に物理的ハードウェアと密接な関係があり、手動による設定と管理が必要です。クバネティスのネットワークは動的で自動化された環境を提供しますが、VM環境のネットワークはより細かい制御と最適化された性能を提供します。

例えば、既存VM環境でWebサーバとDBサーバとのコネクション時、NGINX Webサーバの設定にDBサーバのMySQLのIPを直接入力します。IPは固定された(Static)情報で、もしDBサーバのIPが変更か追加されてIPの変化がある場合はNGINXサーバでその都度、設定を変更します。それに比べてクバネティスは基本設計で常に高可用性を考慮するため、ポッドが再開されてIPが変更かリソースが増加してIPが増加しても自動的に認識します。クバネティスはこのような機能をサービス(Service)というリソースを利用し、ドメイン基盤で処理して自動設定が可能です。

クバネティスは動的環境をサポートし、サービスディスカバリー、ロードバランシング、ネットワークの隔離などを自動化します。VM環境ではこのような機能を手動で構成するか追加ツールでできますが、変更事項を適用するには更に多くの作業が必要です。

また、クバネティスはフラットネットワークモデルを使用し、全てのポッドが相互に直接通信できるようにします。 このために多様なCNI(Container Network Interface※1)プラグインを使用できます。VM環境では別途のVLAN、サブネットなどを構成する必要があり、ルーティングとファイアウォールのルールを手動で管理します。勿論、クバネティスは抽象化と自動化のレベルが高いため、管理が容易ですが、これによりVM環境より少しオーバヘッドが発生することがあります。

個人的には、最初はクバネティスの新しいネットワークリソースのサービス、Ingressなどの理解が難しかったですが、実際に使用してみるとVM環境より抽象化と自動化ができているため、運用が簡単でした。大半の環境でネットワーク担当者がいなくてもクバネティスサービスを運用することができました。

2. CNI(Container Network Interface)の理解とEKS VPC_CNIの特徴

CNIはコンテナオーケストレーションシステムで、ネットワーク通信を担当する標準インターフェースです。クラスタで実行中のポッドのネットワーク構成や内部と外部の通信を管理する重要な役割をします。CNIは特定のネットワーキング技術と関係がない標準化されたインターフェースを提供します。このインターフェースによって多様なネットワークプラグインをクバネティスに簡単に統合できます。CNI標準だけ守れば多様なソリューションを選択できます。

CNIはクバネティスがネットワークを効率的に管理し、拡張性を提供して、特定のネットワークの構築に従属しないようにします。これで、開発担当者とシステム管理者はクバネティスクラスタのネットワーク要求事項に最も適合したソリューションを自由に選択できます。例えば、オーバレイネットワーク、SDNソリューションのように特定要求事項による多様なネットワークの構築を選択できます。

CNIがコンテナを生成するか削除するとき、CNIプラグインはコンテナに必要なネットワークリソースを自動で割当てるか解除します。特定のCNIプラグインはネットワークポリシーに従って、個別のポッド単位でファイアウォールを作動してコンテナ間のトラフィックの流れを制御することでセキュリティーを強化します。

ネイティブクバネティス環境ではCalico、Flannel、CilliumなどのCNIがあります。主にCalicoが使用されていますが、最近はeBPF環境で性能と拡張性の高いCilliumも多く使用されています。EKSは基本CNIのAWS VPC-CNIを大多数の環境で使用します。AWS VPC-CNIの主な特徴はポッドとノードのIPが同一のVPC 帯域のIPを使用することです。そのため、互いに異なるノードのポッド間の通信時に、ポッドとノードのIPが異なる場合に発生する追加的なIPの変更(NAT、Network Address Translation)の作業が必要なく直接の通信が可能であるため、性能が向上します。勿論、同一の帯域を使用してポッドのIPが多数必要であるため、VPC IP帯域を事前に十分に割当(20以上推奨、4096個)することをお勧めします。

<AWSからの出典>
<continoからの出典>

ここで、EKS環境で直接ポッドのIPを確認します。

1.	k get pod -o wide
2.	NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
3.	nginx-hello-7d95c6dbcb-8dnbs 1/1 Running 0 4h50m 10.110.23.249 ip-10-110-24-222.ap-southeast-1.compute.internal <none> <none>

ポッドの状況を確認する‘k get pod’コマンドに‘-o wide’を追加すると、IP情報も同時に確認できます。上記のコマンドの出力結果で確認できるようにポッドのIPは‘10.110.23.249’です。そのポッドが実行されるノードのホスト名は‘ip-10-110-24-222.ap-southeast-1.compute.internal’で、ホスト名から分かるようにノードのIPが‘10.110.24.222’であることが確認できます。また、ポッドとノードのIP帯域が同じ10.110.x.xを使用していることも確認できます。一方、ネイティブクバネティスの環境ではポッドとノードが使用するIP帯域は異なります。

1.	k get pod -o wide
2.	NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
3.	netshoot-796845c847-t888l 1/1 Running 0 76s 10.233.104.127 master1 <none> <none>
4.	
5.	k get nodes -o wide master1
6.	NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
7.	master1 Ready control-plane 107d v1.26.2 192.168.1.101 <none> Ubuntu 18.04.6 LTS 4.15.0-206-generic containerd://1.7.0

ネイティブクバネティス環境で実行した netshoot-xxxx ポッドのIPは 10.233.104.127で、該当ポッドが実行中のmaster1ノードのIPは192.168.1.101です。10.233.x.xと192.168.x.xで互いに異なるIP帯域を使用していることが確認できます。つまり、互いに異なるノードで実行中のポッドが通信するには、中間段階で現在のノードのIPでIPを変更する作業が必要です。

<continoからの出典>

3. クバネティスリソースServiceの主要機能の実習 : サービスディスカバリーとロードバランシング

クバネティスはサービス(Service)リソースを利用してクラスタ内部と外部のトラフィックをポッドとコネクションします。一般的にITで「サービス」とはWebサービス、DBサービスなどのアプリケーションですが、クバネティスではポッドとポッドが相互にコネクションするサービスを提供する意味で使用します。初めての方には紛らわしい内容です。クバネティスでサービスはアプリケーションの内部と外部の間のネットワーク通信を管理する抽象化された概念です。

クバネティスサービスの主な特徴はサービスディスカバリーとロードバランシングです。サービスディスカバリー(discovery)とは名前から分かるように変更事項を自動で発見する機能です。クラスタで動的にポッドを生成や削除するなどの変更事項をサービスが検知し、内部で自動的にアップデートして、既存ポッドのコネクションを維持します。既存のVM環境ではVMのIPが変更されると、手動で設定ファイルに変更されたIPを登録する必要がありますが、クバネティス環境では自動でアップデートするので追加の設定が不要です。

クバネティスはDNS機能を利用してサービスディスカバリーを構築します。クバネティスサービスはサービス毎にユニークなDNS名を登録し、ポッド間の通信時にポッドはサービス名を利用してコネクションします。サービス名に登録されたポッドのIPアドレスとポート番号は、クバネティス内部のサービスディスカバリーのメカニズムを利用して、リアルタイムにポッドの変更有無をチェックして変更事項はアップデートされます。一般的にポッドはコネクションするポッド(WebサーバのDBサーバ情報など)のサービス名を環境変数(ConfigMapの利用を推奨)に登録して使用します。

<サービスディスカバリー>

また、クバネティスサービスはロードバランシング機能をサポートし、サービス名に登録された複数のポッドにトラフィックを分散します。ポッドが実行するノードのiptables(またはIPVS)のロードバランシング機能を使用します。ポッド数が増加して複数ある場合、サービスはトラフィックをポッド間で均等に分配してアプリケーションの可用性と拡張性を向上させます。

実習

ポッド(Deployment)とサービスを作成してみます。

nginx-deploy

1.	apiVersion: apps/v1
2.	kind: Deployment
3.	metadata:
4.	    name: nginx-hello
5.	    namespace: default
6.	    labels:
7.	        app: nginx
8.	spec:
9.	    replicas: 1
10.	    selector:
11.	        matchLabels:
12.	            app: nginx
13.	    template:
14.	        metadata:
15.	            labels:
16.	                app: nginx
17.	        spec:
18.	            containers:
19.	            - name: nginx
20.	                image: nginxdemos/hello

Nginxを実行する簡単なDeploymentファイルです。

spec.template.metadata.labels.app: nginx

ラベルがapp: nginxに指定されました。今後このラベル名を基準にサービスリソースがNginxポッドを選択します。

clusterIP-svc

1.	apiVersion: v1
2.	kind: Service
3.	metadata:
4.	    name: nginx-svc
5.	spec:
6.	    selector: # 選択するポッドを指定
7.	        app: nginx # ポッドLabelと同じ
8.	    type: ClusterIP
9.	    ports:
10.	    - name: tcp
11.	        port: 80
12.	        targetPort: 80

上記で宣言したNginx Deploymentとコネクションするサービスmanifestのファイルです。※2

spec.selector.app: nginx

サービスはselector属性を利用してサービスリソースがコネクションするポッドを選択(select)します。複数のポッドの中で自分が選択するポッドを、ラベル(app: nginx)を利用して指定します。

spec.ports.port: 80

サービスが提供するポート番号を指定します。クライアントに使用する異なるポッドでコネクションするポート番号です。

spec.ports.targetPort: 80

サービスが管理しているターゲットポッドのポート番号です。ここでは、80ですのでターゲットになるnginxポッドの80ポートにコネクションします。

<サービスとtarget port>

下記のYAMLファイルを基準にDeployment、Serviceリソースを生成します。

1.	$ (⎈ |switch-singapore-test:default) k apply -f nginx-deploy.yaml -f clusterIP-svc.yml 
2.	deployment.apps/nginx-hello created
3.	service/nginx-svc created
1.	$ (⎈ |switch-singapore-test:default) k get deploy,svc 
2.	NAME READY UP-TO-DATE AVAILABLE AGE
3.	deployment.apps/nginx-hello 1/1 1 1 75s
4.	
5.	NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
6.	service/nginx-svc ClusterIP 172.20.107.173 <none> 80/TCP 74s

上記のようにnginx-svcという名前でサービスリソースが生成されました。異なるポッドでnginx-helloポッドをコネクションするためにサービス名のnginx-svcを使用します。

このようにサービスが生成されると、クバネティスはエンドポイント(endpoint、終点)コントローラーを利用してサービスを管理します。エンドポイントはサービスとポッド間のコネクション(endpoint)を管理して、ポッドの状態が変更され、IP情報が変更されると自動で該当情報をアップデートします。

1.	[(jerry-test:default) service]$ k get endpoints nginx-svc 
2.	NAME ENDPOINTS AGE
3.	nginx-svc 10.110.12.31:80 7m34s

endpointリソースを確認するnginx-svcサービス名とnginxポッドのIP:ポート(10.110.12.31:80)が確認できます。nginx-svc名とnginxポッドがお互いに正常にコネクションされたことを意味します。

新しいポッドを実行してサービス名で通信します。次にネットワークのトラブルシューティング用に多く使用されるnetshootイメージを持つデプロイを実行します。

netshoot-deploy

1.	apiVersion: apps/v1
2.	kind: Deployment
3.	metadata:
4.	    name: netshoot
5.	    labels:
6.	        app: netshoot
7.	spec:
8.	    replicas: 1
9.	    selector:
10.	        matchLabels:
11.	            app: netshoot
12.	    template: 
13.	        metadata:
14.	            labels:
15.	                app: netshoot
16.	        spec:
17.	            containers:
18.	            - name: netshoot
19.	                image: nicolaka/netshoot
20.	                args:
21.	                - sleep
22.	                - infinity

netshoot-xxxx名のポッドが実行されます。

1.	[(jerry-test:default) ~]$ k get pod netshoot-7844866874-xjhc9
2.	NAME READY STATUS RESTARTS AGE
3.	netshoot-7844866874-xjhc9 1/1 Running 0 6m2s

ポッドに接続してnginx-svcサービス名でポッド間のコネクションが可能です。VMのSSHでコネクションするようなポッドに接続するためにexec(実行) + bash コマンドを使用します。

1.	[(jerry-test:default) ~]$ k exec -it netshoot-7844866874-xjhc9 -- bash
2.	netshoot-7844866874-xjhc9:~# curl -I nginx-svc
3.	HTTP/1.1 200 OK
4.	Server: nginx/1.25.2
5.	Date: Tue, 29 Aug 2023 18:34:36 GMT
6.	Content-Type: text/html
7.	Connection: keep-alive
8.	Expires: Tue, 29 Aug 2023 18:34:35 GMT
9.	Cache-Control: no-cache

nginx-svcサービス名でcurlコマンドが正常に実行されました。このようにクバネティス環境でポッド間のコネクションにサービス名を使用します。

次に異なるネームスペース間のポッドの通信を確認します。ネームスペース(Namespace)はクバネティスクラスタ内でリソースを区分し、隔離する論理的な空間です。ネームスペースを使用し、クラスタ内のリソースをグループ化してリソース間の衝突や混乱を防止できます。同じネームスペース内ではポッド、サービス名を重複して使用できませんが、ネームスペースが異なるときは同じ名前を使用できます。クバネティスクラスタは複数のネームスペースを持つことができます。各ネームスペースはユニークな名前で識別されます。

1.	[(jerry-test:default) argo-cd-app-of-apps-qa]$ k ns
2.	argocd
3.	default
4.	kube-node-lease
5.	kube-public
6.	kube-system
7.	nginx

筆者のテストクラスタで使用中のネームスペースのリストです。ネームスペースは任意に生成できますが、上記のようにアプリケーション名で区別することが多いです。

同じネームスペース内でポッド間のコネクションは前の例のようにサービス名(nginx-svc)でできます。しかし、ネームスペースが異なるときは、コネクションのためのサービス名にネームスペース名も追加します。例えば、nginx-svcがnginxネームスペースで実行中であれば、{サービス名}.{ネームスペース名}タイプとなりnginx-svc.nginxでコネクションします。

下記は、筆者が任意にnginxネームスペースにnginx名のサービスを生成した例です。

1.	[(jerry-test:nginx) service]$ k get svc -n nginx 
2.	NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
3.	nginx ClusterIP 172.20.202.76 <none> 80/TCP,9113/TCP 6d23h

デフォルトのネームスペースで実行中のポッドでnginxネームスペースのサービスとコネクションするためには下記のようにnginxネームスペース名を追加します。

netshoot-7844866874-xjhc9:~# curl -I nginx.nginx
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 29 Aug 2023 18:37:22 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 13 Jun 2023 18:21:37 GMT
Connection: keep-alive
ETag: “6488b3b1-267”
X-Frame-Options: SAMEORIGIN
Accept-Ranges: bytes

ネームスペース名が無くサービス名だけで呼び出しすると下記のようにコネクションできません。

1.	netshoot-7844866874-xjhc9:~# curl -I nginx
2.	curl: (6) Could not resolve host: nginx

実際の事例ですが、次に確認するモニタリングダッシュボードGrafanaはログソリューションのLokiコネクションを下記のように {サービス名}.{ネームスペース名}を利用してloki-gateway.loki-s3でコネクションします。

次はロードバランシング機能です。nginx ポッドを再開してポッドのIPが変更するかポッドの数が増加すると、サービスが提供対象としているターゲットIP情報が変更されます。このような変更事項をサービスは自動で検知して情報をアップデートします。変更された内容はendpoint リソースで確認できます。

増加したポッドの数を確認します。確認にはscaleコマンドを使用します。

1.	$ (⎈ |switch-singapore-test:default) k scale deployment nginx-hello --replicas 5
2.	deployment.apps/nginx-hello scaled
3.	
4.	$ (⎈ |switch-singapore-test:default) k describe ep nginx-svc 
5.	Name: nginx-svc
6.	Namespace: default
7.	Labels: <none>
8.	Annotations: endpoints.kubernetes.io/last-change-trigger-time: 2023-07-03T23:51:41Z
9.	Subsets:
10.	    Addresses: 10.110.11.214,10.110.15.167,10.110.19.90,10.110.20.44,10.110.23.249
11.	    NotReadyAddresses: <none>
12.	    Ports:
13.	        Name Port Protocol
14.	        ---- ---- --------
15.	        tcp 80 TCP
16.	
17.	Events: <none>

describeコマンドを利用してnginx endpointの詳細情報を確認すると、ターゲットアドレスが1個から5個に増えています。endpointオブジェクトが自動で5個に増えたポッド IPを反映しました。次はnginx-svcという名前で接続すると自動で5個のポッドに均等に接続されることを確認できます。

1.	netshoot-7844866874-xjhc9:~# curl -S nginx-svc |grep address
2.	% Total % Received % Xferd Average Speed Time Time Time Current
3.	Dload Upload Total Spent Left Speed
4.	0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0<p><span>Server address:</span> <span>10.110.0.151:80</span></p>
5.	100 7236 0 7236 0 0 756k 0 --:--:-- --:--:-- --:--:-- 785k
6.	netshoot-7844866874-xjhc9:~# curl -S nginx-svc |grep address
7.	% Total % Received % Xferd Average Speed Time Time Time Current
8.	Dload Upload Total Spent Left Speed
9.	<p><span>Server address:</span> <span>10.110.2.237:80</span></p>:-- 0
10.	100 7236 0 7236 0 0 3381k 0 --:--:-- --:--:-- --:--:-- 7066k
11.	netshoot-7844866874-xjhc9:~# curl -S nginx-svc |grep address
12.	% Total % Received % Xferd Average Speed Time Time Time Current
13.	Dload Upload Total Spent Left Speed
14.	1<p><span>Server address:</span> <span>10.110.4.201:80</span></p>-- 0
15.	00 7236 0 7236 0 0 3695k 0 --:--:-- --:--:-- --:--:-- 7066k

上記のようにnginx-svc接続時、応答するサーバのIPが 10.110.0.151、10.110.2.237、10.110.4.201で異なります。サービスがロードバランシングして二つ以上のポッドにトラフィックを分散させるためです。

4. クラスタ内部と外部接続によるクバネティスサービスの分類

クバネティスサービスはクラスタ内部の通信を担当するCluster IPと、外部からクラスタ内部のポッドとコネクションを担当するノードポート(Node Port)、ロードバランサー(Load Balancer)タイプに区分できます。 現場では、外部とのコネクション時には、主にIngressを使用します。

Cluster IPタイプはクライアントポッドからサービス名で呼び出すと、サービス名はDNSクエリを利用してCluster IPで応答して、そのIPはIP Tablesのルールで各ポッドに負荷分散されて接続します。

1.	$ (⎈ |switch-singapore-test:default) k get svc nginx-svc
2.	NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
3.	nginx-svc ClusterIP 172.20.107.173 <none> 80/TCP 3d1h

上記の例でCLUSTER-IPは 172.20.107.173 です。実際に異なるポッドからサービス名でDNSクエリをすると下記のように該当CLUSTER-IPが応答します。curl などを利用して接続テストをすると正常に接続されます。

1.	netshoot:~# nslookup nginx-svc
2.	Server: 172.20.0.10
3.	Address: 172.20.0.10#53
4.	
5.	Name: nginx-svc.default.svc.cluster.local
6.	Address: 172.20.107.173
1.	netshoot:~# curl -I 172.20.107.173
2.	HTTP/1.1 200 OK
3.	Server: nginx/1.25.1
4.	Date: Thu, 06 Jul 2023 19:54:01 GMT
5.	Content-Type: text/html
6.	Connection: keep-alive
7.	Expires: Thu, 06 Jul 2023 19:54:00 GMT
8.	Cache-Control: no-cache

クラスタ外部から接続する方法ではノードポートかロードバランサーのタイプを使用します。

ノードポートタイプは名前から推測できるようにノードのIPとポートで外部から接続できます。ノードポートのサービスタイプを使用するとクラスタの全てのノードが同一ポート番号でサービスを外部に提供します。外部トラフィックがノードポートに入ると、クバネティスはこのトラフィックを適切なポッドに自動的にルーティングします。ポート番号は 30000-32767の中の一つのポートを使用します。

ノードポートはクラスタ内の全てのノードで同一ポートへアクセスできるため、一つのノードが失敗してもサービスアクセスは維持されます。トラフィックはクバネティスにより自動で適切なポッドへルーティングされます。

1.	[service]$ k get svc ingress-nginx-controller
2.	NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
3.	ingress-nginx-controller NodePort 10.233.59.140 <none> 80:30080/TCP,443:30443/TCP 6d7h

現場ではオンプレミス環境のingress-nginxサービスを上記のようにノードポートタイプで使用します。ポートは 30080、30443 を使用しました。

ロードバランサーのサービスタイプはクラスタ外部でクバネティスサービスにアクセスするためのロードバランサーを自動でプロビジョニングします。このサービスタイプはクラウドのプロバイダーが提供するロードバランサーあるいはオンプレミス環境で構築したロードバランサー(Metal LBなど)を使用して、外部トラフィックをクラスタ内の特定サービスでルーティングします。現場ではロードバランサーより、主にIngressを使用します。 ロードバランサータイプはL4 基盤のため、URLやPATHを区分できないデメリットがあります。

以上、今回はクバネティスネットワークについて確認しました。次回はIngressについて確認します。


※1 類似したものでストレージを担当するCSI(Container Storage Interface)もあります。

※2 一般的にクバネティスではこのようなファイルをmanifest(明細書)といいます。

お気軽にお問い合わせください

製品に関する事やご不明な点など、お気軽にご相談ください。
また、ジェニファーソフトでは2週間の無償トライアルを提供しています。

詳しく知りたい方は
導入や費用のご相談は