Kubernetes Serviceを利用したクラスタ外部公開

ここではServiceを利用して、クラスタ内部および外部へ公開する方法について説明する。

クラスタ内部から接続する

まずクラスタ内で接続を確認する。クラスタにcurlコンテナが入ったPodを作成し、Shellへ接続したあと、helloworld Podへのcurlアクセスをテストする。

まず、テスト用Podを作成して実行する。

% kubectl run --port 8080 --image gcr.io/google-samples/hello-app:1.0 --restart Never helloworld
pod/helloworld created

Pod一覧をIP付きで表示する(-o wideを付ける)。

% kubectl get pods -o wide
NAME         READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
helloworld   1/1     Running   0          3m44s   172.17.0.3   minikube   <none>           <none>

クラスタ内部に入り、curlを実行する。

% kubectl run --image curlimages/curl:latest -it --restart Never --rm curl sh
If you don't see a command prompt, try pressing enter.
/ $ curl 172.17.0.3:8080
Hello, world!
Version: 1.0.0
Hostname: helloworld
/ $

この結果から、curlからhelloworldのIPである172.17.0.3を指定し、ポート8080で開かれているため、curlコンテナからhelloworldコンテナへ接続できたことが分かる。

内部にあることを確認するため、curlコンテナを動作させたまま、別のターミナルを開いてPod一覧をIP付きで表示してみよう。

% kubectl get pod -o wide
NAME         READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
curl         1/1     Running   0          23s   172.17.0.4   minikube   <none>           <none>
helloworld   1/1     Running   0          12m   172.17.0.3   minikube   <none>           <none>

コンテナが同じネットワーク内に存在していることを確認できる。

クラスタ外部から接続する

次にクラスタ外部から接続しようとしても、そのままでは接続できない。これはDockerの場合と同じで、ホストIPとPodのIP範囲が異なるためである。したがってアクセスできるようにするには、Serviceを作成してPodを外部に公開する必要がある。

Serviceは、Podをクラスタ内外へ公開する静的IPアドレスを持つL4ロードバランサを意味する。Pod自体はいつ停止するか分からないため、分配先をServiceとして公開する。

Serviceには3種類あり、それぞれ次のような特徴を持つ。

  • ClusterIP Service(クラスタ内接続)
    いつ消滅するか分からないPodのIPを抽象化し、静的IPを持つプロキシを置くことで、次の利点がある。
    1. PodへアクセスするときにPodのIPを知る必要がない。
    2. Podへアクセスするときにロードバランスしてくれる。
  • NodePort Service(クラスタ内外接続)
    ClusterIPではできなかったクラスタ外部へのPod公開を、Node IPとNodePort経由で可能にできる点が利点である。
    ただし、次のような欠点もある。
    1. Node IPを知っている必要がある。
    2. Node Portを知っている必要がある。
  • LoadBalancer Service(クラスタ内外接続)
    プロバイダのL4ロードバランサのDNSから、各ノードの特定ポートへRoutingしてPodへアクセスする。
    したがって、Node IPおよびNodeポートを知る必要がない。ただし、次のような欠点もある。
    1. 1つのServiceごとに1つのロードバランサが作成されるため高コストになる。
    2. L4ロードバランサなので、L7のHTTPホストやパスによるLB振り分けはできない。

上記の各Serviceについて内容を確認していこう。

ClusterIP Service

HelloWorld PodをClusterIP Serviceとしてクラスタ内部へ公開し、同じクラスタの別Podからアクセスできることを確認する。
次のように起動中のPodに--type ClusterIPオプションを指定することで、ClusterIPのServiceとして公開できる。

kubectl expose pod [Pod名] --type ClusterIP [Service名]

まず、テスト用Podを作成して実行する。*(既に同じ名前のPodがある場合は、kubectl delete pod helloworldコマンドで削除する。)

% kubectl run --image gcr.io/google-samples/hello-app:1.0 --restart Never helloworld
pod/helloworld created

helloworld Podをhelloworld-clusteripというService名で公開する。

% kubectl expose pod helloworld --type ClusterIP --port 8080 --name helloworld-clusterip
service/helloworld-clusterip exposed

次のコマンドを使用してService一覧を取得する。

% kubectl get service
NAME                   TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
helloworld-clusterip   ClusterIP   10.98.60.0   <none>        8080/TCP   25s
kubernetes             ClusterIP   10.96.0.1    <none>        443/TCP    16h

結果の1行目にTypeがClusterIPのServiceが作成されたことを確認できる。

次にcurlコンテナを再度起動し、そのコンテナ内からhelloworld-clusteripを通じてhelloworldへアクセスしてみる。

% kubectl run --image curlimages/curl:7.68.0 -it --restart Never --rm curl sh
If you don't see a command prompt, try pressing enter.
/ $

開いたShellでcurlコマンドを使って接続する。

/ $ curl 10.98.60.0:8080
Hello, world!
Version: 1.0.0
Hostname: helloworld

接続結果から、helloworldへアクセスできることを確認できる。この結果は、上で説明した「クラスタ内部から接続する」の結果と同じである。

ただし、上で行ったのはcurlコンテナから直接helloworldコンテナへ接続したものであり、今回の実行結果はcurlコンテナからServiceであるhelloworld-clusteripへアクセスし、ロードバランス機能(Podは1つだけ作成しているため特定のPodにはなる)によってServiceがhelloworldへ接続するという流れである。したがって意味が異なるため注意が必要である。

NodePort Service

HelloWorld PodをNodePort Serviceとしてクラスタ内部と外部へ公開し、同じクラスタの別Podからアクセスできることを確認する。

次のように起動中のPodに対して--type NodePortオプションを指定するだけで、NodePortのServiceとして公開できる。

kubectl expose pod [Pod名] --type NodePort [Service名]

helloworld Podをhelloworld-nodeportというService名で公開する。

% kubectl expose pod helloworld --type NodePort --port 8080 --name helloworld-nodeport
service/helloworld-nodeport exposed

次のコマンドを使用してService一覧を取得する。

% kubectl get service
NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
helloworld-nodeport    NodePort    10.110.238.186   <none>        8080:31942/TCP   38s
kubernetes             ClusterIP   10.96.0.1        <none>        443/TCP          16h

その結果、1行目にTypeNodePortのServiceが作成されたことを確認できる。

次に、同じクラスタ内から接続できるか確認する。手順はClusterIPの場合と同じである。 まず既存のcurlコンテナを削除し、再作成する。

% kubectl delete pod curl
% kubectl run --image curlimages/curl:7.68.0 -it --restart Never --rm curl sh
If you don't see a command prompt, try pressing enter.
/ $

そして、そのコンテナ内からhelloworld-nodeportを通じてhelloworldへアクセスしてみる。

/ $ curl 10.110.238.186:8080
Hello, world!
Version: 1.0.0
Hostname: helloworld
/ $

その結果、helloworld-nodeportを通じてhelloworldへ接続できることを確認できる。

では次に、クラスタ外部(ローカル環境)から接続できるか確認する。接続にはminikubeのIPが必要なので、次のコマンドを実行してIPを取得する。

% minikube ip
192.168.49.2

このIPに対して、ローカル環境からcurlコマンドを実行して接続してみる。このときポートはPodの31942を指定する。

/ $ curl 192.168.49.2:31942
Hello, world!
Version: 1.0.0
Hostname: helloworld

接続結果から、helloworldへ接続できることを確認できる。
(minikube起動時の設定によってはアクセスできない場合がある。この場合はminikube service --url helloworld-nodeportコマンドを実行し、出力されたURLに対してcurlコマンドを実行する。)

ロードバランサService

HelloWorld PodをLoadBalancer Serviceとしてクラスタ内部と外部へ公開し、同じクラスタの別Podからアクセスできることを確認する。

次のように起動中のPodに--type LoadBalancerオプションを指定することで、LoadBalancerのServiceとして公開できる。

kubectl expose pod [Pod名] --type LoadBalancer [Service名]

helloworld Podをhelloworld-loadbalancerというService名で公開する。

% kubectl expose pod helloworld --type LoadBalancer --port 8080 --name helloworld-loadbalancer
service/helloworld-loadbalancer exposed

次のコマンドを使用してService一覧を取得する。

% kubectl get service
NAME                      TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
helloworld-loadbalancer   LoadBalancer   10.104.176.59    <pending>     8080:30100/TCP   27s
kubernetes                ClusterIP      10.96.0.1        <none>        443/TCP          16h

その結果、1行目にTypeLoadBalancerのServiceが作成されたことを確認できる。

まず同じクラスタ内からアクセスできるか確認する。手順はClusterIPの場合と同じである。

% kubectl run --image curlimages/curl:7.68.0 -it --restart Never --rm curl sh
If you don't see a command prompt, try pressing enter.
/ $

開いたShellでcurlコマンドを使って接続する。

/ $ curl 10.104.176.59:8080
Hello, world!
Version: 1.0.0
Hostname: helloworld
/ $

接続結果から、helloworld-LoadBalancerを通じてhelloworldへアクセスできることを確認できる。

次にクラスタ外部(ローカル環境)からアクセスできるか確認する。

% minikube service helloworld-loadbalancer --url
http://127.0.0.1:53240
❗  Because you are using a Docker driver on darwin, the terminal needs to be open to run it.

コンソールをそのまま開いた状態にし、別のコンソールを開いて次のコマンドで接続を確認する。

% curl http://127.0.0.1:53240
Hello, world!
Version: 1.0.0
Hostname: helloworld

実行すると、helloworldへアクセスできることを確認できる。