Serviceとは
Serviceは、クラスター内で1つ以上のPodとして実行されているネットワークアプリケーションを公開する方法です。
なぜServiceが必要なのか
KubernetesのPodは、クラスターの状態に合わせて作成されたり削除されたりします。例えば、あるノードがダウンしてしまったときに、そのノードで動いていたPodを他のノードで再作成する場合などが考えられます。そのようなシナリオの場合、Pod自体に外部向けのIPアドレスを割り当てていると、ユーザーは新しく再作成されたPodのIPアドレスをいちいち取得して修正する必要があります。
そこでワンクッション挟んで、Serviceというリソースに外部向けIPアドレスを割り当て、このIPアドレスを固定しておくことでユーザーはPodの状態にかかわらず同一のIPアドレスでPodにアクセスすることができるようになります。
ServiceとPodの結び付け
ServiceとPodの結び付けは、基本的にはセレクターを用いて行われます。Podに付与したラベルを見て、適切なPodにトラフィックをルーティングします。
Serviceの作成方法
Podの時と同様、Serviceを作成する方法は二つあります。
- ワンライナーで作成する
- マニフェストファイルから作成する
ワンライナーで作成する
まずは、以下のコマンドでnginxのPodを作成します。
kubectl run nginx-pod --image=nginx --port=80
次に、Serviceを作成します。
kubectl expose pod nginx-pod --type=NodePort --name=nginx-service --port=80 --target-port=80
解放されたポートを確認します。
$ kubectl describe svc nginx-service
Name: nginx-service
Namespace: default
Labels: run=nginx-pod
Annotations: <none>
Selector: run=nginx-pod
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.150.153
IPs: 10.96.150.153
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30127/TCP <= この値
Endpoints: 10.0.10.79:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
Podが配置されたNodeのIPを確認します。
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-pod 1/1 Running 0 23m 10.0.10.79 10.0.10.71 <none> <none>
上記の出力から、10.0.10.71
のノードに配置されていることが分かります。
以上から、以下のコマンドでアクセスできるかを確認します。
$ curl 10.0.10.71:30127
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
接続できていることが分かります。
ServiceとPodの結びつきはセレクターとラベルで行うというお話をしましたが、ワンライナーで作成する場合、それらを指定していません。では、セレクターとラベルはどのように定義されているのでしょうか?
まず、Serviceのセレクターを確認します。
$ kubectl describe svc nginx-service
Name: nginx-service
Namespace: default
Labels: run=nginx-pod
Annotations: <none>
Selector: run=nginx-pod <= セレクター
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.150.153
IPs: 10.96.150.153
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30127/TCP
Endpoints: 10.0.10.79:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
セレクターは、run=nginx-pod
になっていることが分かります。
次に、nginx-podのラベルを確認してみます。
$ kubectl describe po nginx-pod
Name: nginx-pod
Namespace: default
Priority: 0
Service Account: default
Node: 10.0.10.71/10.0.10.71
Start Time: Tue, 25 Jun 2024 02:38:24 +0000
Labels: run=nginx-pod <= ラベル
Annotations: <none>
Status: Running
IP: 10.0.10.79
IPs:
IP: 10.0.10.79
Containers:
nginx-pod:
Container ID: cri-o://c444353ec04d9a00a4ddcdede7932930799b31c9cd1ac84e20a56d6d6c65672c
Image: nginx
Image ID: e0c9858e10ed8be697dc2809db78c57357ffc82de88c69a3dee5d148354679ef
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Tue, 25 Jun 2024 02:38:25 +0000
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-62dd9 (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-62dd9:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events: <none>
Podには、run=nginx-pod
のラベルが付与されていることが分かります。kubectl run
コマンドでPodを作成したとき、Pod名を指定したと思いますが、それがそのままrun=nginx-pod
に来ています。Serviceを作成する際は、kubectl expose pod nginx-pod
でこのPodを指定しているので、セレクターにもrun=nginx-pod
が設定できています。
マニフェストファイルから作成する
kubectl run nginx-pod --image=nginx --port=80
で作成した先ほどのPodに接続するためのServiceを、次はマニフェストファイルから作成してみます。
まずは、先ほど作成したServiceを削除します。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP,12250/TCP 47d
nginx-service NodePort 10.96.150.153 <none> 80:30127/TCP 79m
$ kubectl delete svc nginx-service
service "nginx-service" deleted
次に、以下のマニフェストファイルを作成します。
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport-service
spec:
type: NodePort
selector:
run: nginx-pod
ports:
- port: 80
targetPort: 80
nodePort: 30007
spec.selector
には、Podのラベルであるrun=nginx-pod
を指定します。spec.ports.nodePort
には、適当に30007を指定しました(基本的には30000-32767を割り当てます)。
このマニフェストファイルをクラスターに適用します。
$ kubectl apply -f nginx-service.yaml
service/nginx-nodeport-service created
接続を確認します。
$ kubectl get svc # 作成したServiceを確認
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP,12250/TCP 47d
nginx-nodeport-service NodePort 10.96.227.37 <none> 80:30007/TCP 6s
$ kubectl get po -o wide # Podが配置されたノードのIPアドレスを確認
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-pod 1/1 Running 0 101m 10.0.10.79 10.0.10.71 <none> <none>
$ curl 10.0.10.71:30007 # 接続を確認
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
kubectl expose
コマンドの時と同様、正しく接続できていることが分かります。
その他のServiceタイプ
今回は例としてNodePort
を使いましたが、Serviceには以下のタイプがあります。
- ClusterIP
- NodePort
- LoadBalancer
- ExternalName
ClusterIP
クラスター内部のIPでServiceを公開します。このタイプでは、クラスター内部からのみ接続できます。
NodePort
今回作成したServiceのタイプです。各ノードのIPにて、静的なポート上でServiceを公開します。
ServiceにClusterIPが作成されるので、クラスター内部からの通信の場合はこのServiceのClusterIPを使って通信します。
クラスター外部からの通信の場合は、PodがデプロイされたノードのIPとNodePortで公開しているポートを調べて、通信します。
LoadBalancer
Service用にロードバランサ―をプロビジョニングして、ロードバランサ―経由でアクセスするためのタイプです。クラウドプロバイダー上で使用すると、自動でそのクラウドプロバイダーが提供するロードバランサ―が作成され、Serviceに紐づきます。
マネージドのKubernetesを使う場合は特に何の事前準備もなく利用できますが、そうでない場合はクラウドプロバイダー毎に必要なクラウドコントローラーマネージャをインストールする必要があります。
このタイプでPodを公開した場合、Podがデプロイされたノードに関わらず、ロードバランサ―のパブリックIPアドレスでPodにアクセスすることができます。
ExternalName
クラスタ内の名前解決を、クラスタ外部のDNS名にマッピングするために使用されます。これによって、クラスタ内のアプリケーションが内部的に定義された名前を使用して、クラスタ外部のリソースにアクセスできるようになります。
まとめ
今回はServiceについて概要を説明しました。次回はPodのレプリカを維持するための、Replicasetについて説明したいと思います。