Kubernetes の Headless Service / ヘッドレスサービスとは

以下の記事で k8s の headless service と pod の名前解決を学んだので記録します。間違ってたらツッコミください。

> Keycloak + Infinispanで分散キャッシュをしてセッション共有・冗長化する方法
> https://qiita.com/uturned0/items/37d2ce64d04a3f3a5dd2

# kubernetes pods の 名前解決

dockerから乗り換えてずっとよくわからなかったのが名前解決。一つのnamespaceにpod-A, Bがある時、お互いの名前解決はどうやってするのでしょうか？ docker networkなら同じnetworkにあれば簡単に行けるのが、k8sだとできません。k8sではpod間の通信は serviceを通じて行う仕組みになっているそうです。

ただ、ちなみに k8s では同じnamespaceにあるpod同士は通信が可能です。podのIP addressを指定すれば ping も返ってきます。

```rb
pod-A $ ping -c1 172.18.5.221   # pod-B 
PING 172.18.5.221 (172.18.5.221) 56(84) bytes of data.
64 bytes from 172.18.5.221: icmp_seq=1 ttl=62 time=1.25 ms
```

しかし、わかりやすいpod名でアクセスする方法は分かりませんでした。chatGPTはserviceを作ればできるというけれども、serviceに名前解決をして返ってくるのはserviceのIP addressであって pod の IP Addressではありません。


# headless-service を作って擬似的にpodの名前解決をする

pod 間で名前解決をしたい場合は headless-service を使うことができます。これは（私からすると）不思議なserviceです。podがserviceのFQDNに名前解決をすると pod の ip address が返ってきます。serviceのselectorで特定のpodを指定していれば、特定のpodの名前解決をすることができます。ここでは、複数のpodを指定したselectorで試してみます。

> official doc

https://kubernetes.io/ja/docs/concepts/services-networking/service/#headless-service


まず headless service を作ってみましょう。残念ながら `headless: true` のような分かりやすい設定は存在せず、 `clusterIP: None` がそのスイッチとなっています。（分かりづらい...)

```yml:headless-service.yml
---
apiVersion: v1
kind: Service
metadata:
  name: headless-service
  namespace: myapp
spec:
  clusterIP: None      # <------ これが headless service に必須の設定です
  selector:
    app: myapp
```  

`kubectl apply -f headless-service.yml` でこのリソースを作成しましょう。

このサービスを使うと、podは以下のフォーマットに即したFQDNで名前解決を行うことができます。

```<service-name>.<namespace-name>.svc.cluster.local```

この場合は、以下の FQDN になります。

```headless-service.myapp.svc.cluster.local```

podの中から実際にリクエストをしてみます。 `.53` と `.221` の pod の名前解決がされます。２つの順番は必ず `.53` が上で、一貫性があります。

```rb
$ dig a +short headless-service.myapp.svc.cluster.local
172.18.3.53
172.18.5.221

$ dig a +short headless-service.myapp.svc.cluster.local
172.18.3.53
172.18.5.221

$ dig a +short headless-service.myapp.svc.cluster.local
172.18.3.53
172.18.5.221

$ dig a +short headless-service.myapp.svc.cluster.local
172.18.3.53
172.18.5.221
```

ここで pod の数を増やすと、当然返却されるIP addressも増えます。つまり headless serviceは、一つの FQDN で selector に対応する全ての pod の ip addressをまとめて返す DNS service だと考えることができます。infinispan ではこの FQDN を使って、namespace 内の inifinispan nodes を discover(発見)します。


headless service の名前解決は一貫しているため、このままでは DNS round robinはされません。それはclientの責務であり、clientの実装次第になるそうです。例えば、 `host` コマンドは内部でランダムな並び替えを実行するため、DNS round robin されたような出力をします。以下の例では `.221` と `.53` いずれかが先頭に表示されます。

```rb
$ host headless-service.myapp.svc.cluster.local
headless-service.myapp.svc.cluster.local has address 172.18.5.221
headless-service.myapp.svc.cluster.local has address 172.18.3.53

$ host headless-service.myapp.svc.cluster.local
headless-service.myapp.svc.cluster.local has address 172.18.3.53
headless-service.myapp.svc.cluster.local has address 172.18.5.221

$ host headless-service.myapp.svc.cluster.local
headless-service.myapp.svc.cluster.local has address 172.18.3.53
headless-service.myapp.svc.cluster.local has address 172.18.5.221

$ host headless-service.myapp.svc.cluster.local
headless-service.myapp.svc.cluster.local has address 172.18.5.221
headless-service.myapp.svc.cluster.local has address 172.18.3.53
```

以上、k8sのserviceは難しいという学びの記録でした。