はじめに
前回ClusterIPの動作を確認しました。
その際、動作確認用のPodにはClusterIPのIPアドレスを設定していました。しかし、Kubernetesで実現するマイクロサービスでは、頻繁にリソースを追加削除します。各リソースにはKubernetesが自動でIPアドレスを割り当てますので、IPアドレスはデプロイの度に変わります。そのため、アプリケーション内ではIPアドレスではなく、DNS名を使用するのが望ましいでしょう。
そこで今回は、クラスタ内でのDNSの動作を確認したいと思います。
クラスタ内のDNS
マニュアルには以下のような記載があります。クラスタ内では、kubernetesがDNSのサービスを提供しています。
KubernetesのDNSはクラスター上でDNS PodとServiceをスケジュールし、DNSの名前解決をするために各コンテナに対してDNS ServiceのIPを使うようにKubeletを設定します。
kubernetes.io
提供されるサービスは以下の2つがあります。
- Aレコード
 - SRVレコード
 
Aレコード
Serviceには、「[サービス名].[ネームスペース名].svc.cluster.local」という形式のDNS Aレコードが割り当てられています。
前回作成したClusterIPを例にすると、「cluster-ip.default.svc.cluster.local」になります。
動作確認
前回の環境で、動作確認用のPodに指定したClusterIPのIPアドレスをFQDNに書き換えました。
これで同様に動作確認してみたいと思います。
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
    - name: centos
      image: centos:latest
      command:
      - sh
      - -c
      args:
      - for i in 1 2 3 4 5 ; do curl -s http://cluster-ip.default.svc.cluster.local:8080 ; sleep 5; done ;exit 0
このマニフェストをapplyして、30秒ほど待ってlogを確認します。
$ kubectl apply -f test_pod.yaml
pod/test-pod created
$ kubectl logs test-pod | grep pod
pod1
pod2
pod2
pod1
pod2
IPアドレスを指定した時と同様にClusterIPに対してリクエストが送られていますね。
なお、今回は正式なFQDNを指定しましたが、Podとサービスが同じネームスペースの場合には、[サービス名]だけでも名前解決ができます。
これは、デプロイされるコンテナの/etc/resolve.confにsearchから始まる一行があるため、[サービス名]だけ、ネームスペースが異なる場合には、「[サービス名].[ネームスペース名]」だけでも名前解決ができます。
$ kubectl exec -it test-pod /bin/bash
[root@test-pod /]# cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
SRVレコード
SRVレコードは、一言で言うとポート番号を含めたDNSです(と私は理解しました)。SRVレコードは以下の形式になります。
[_Serviceのポート名].[_ポートのプロトコル].[サービス名].[ネームスペース名].svc.cluster.local
前回作成したClusterIPの詳細を確認します。
$ kubectl describe svc cluster-ip
Name:              cluster-ip
Namespace:         default
Labels:            <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"cluster-ip","namespace":"default"},"spec":{"ports":[{"name":"clus...
Selector:          app=nginx-dep
Type:              ClusterIP
IP:                10.101.47.213
Port:              cluster-port  8080/TCP
TargetPort:        80/TCP
Endpoints:         192.168.69.234:80,192.168.79.98:80
Session Affinity:  None
Events:            <none>
上記の結果から、このClusterIPのSVRレコードは、以下になります。
_cluster-port._TCP.cluster-ip.default.svc.cluster.local
動作確認
動作確認用のPodとして、以下のマニフェストのPodをデプロイします。
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
    - name: centos
      image: centos:latest
      command:
      - sh
      - -c
      args:
      - tail -f /dev/null
$ kubectl apply -f test_pod2.yaml
pod/test-pod created
Podにログインします。
$ kubectl exec -it test-pod /bin/bash
[root@test-pod /]#
デフォルトでは、nslookupやdigコマンドがないため、インストールします。
[root@test-pod /]# yum install -y bind-utils
Failed to set locale, defaulting to C.UTF-8
CentOS-8 - AppStream                                                                                                                                                           1.7 MB/s | 6.6 MB     00:03
CentOS-8 - Base                                                                                                                                                                1.8 MB/s | 5.0 MB     00:02
CentOS-8 - Extras                                                                                                                                                              6.1 kB/s | 4.9 kB     00:00
Dependencies resolved.
・・・
Installed:
  bind-utils-32:9.11.4-26.P2.el8.x86_64  bind-libs-32:9.11.4-26.P2.el8.x86_64  bind-libs-lite-32:9.11.4-26.P2.el8.x86_64  bind-license-32:9.11.4-26.P2.el8.noarch  python3-bind-32:9.11.4-26.P2.el8.noarch
  python3-ply-3.9-7.el8.noarch
Complete!
nslookupとdigで確認します。
[root@test-pod /]]# nslookup -type=srv _cluster-port._TCP.cluster-ip.default.svc.cluster.local
Server:         10.96.0.10
Address:        10.96.0.10#53
_cluster-port._TCP.cluster-ip.default.svc.cluster.local service = 0 100 8080 cluster-ip.default.svc.cluster.local.
[root@test-pod /]# dig _cluster-port._TCP.cluster-ip.default.svc.cluster.local SRV
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> _cluster-port._TCP.cluster-ip.default.svc.cluster.local SRV
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8930
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 2
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 3127dac7c4b7e34a (echoed)
;; QUESTION SECTION:
;_cluster-port._TCP.cluster-ip.default.svc.cluster.local. IN SRV
;; ANSWER SECTION:
_cluster-port._TCP.cluster-ip.default.svc.cluster.local. 30 IN SRV 0 100 8080 cluster-ip.default.svc.cluster.local.
;; ADDITIONAL SECTION:
cluster-ip.default.svc.cluster.local. 30 IN A   10.101.47.213
;; Query time: 1 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Sat Apr 04 12:37:15 UTC 2020
;; MSG SIZE  rcvd: 259
どちらの結果でも、cluster-ipサービスのポート「8080」とホスト名「cluster-ip.default.svc.cluster.local」が解決できていますね。
まとめ
KubernetesではIPアドレスは自動で割り当てられるため、IPアドレスではなくDNS名を指定して各リソース間で通信をするのが望ましいです。マイクロサービスを開発するには、今回確認したDNSの設定と動作を理解しておく必要がありますね。
補足
ClusterIPに割り当てられるIPアドレスをユーザーが指定することができます。指定する場合は、「spec.clusterIP」フィールドに指定するIPアドレスを記載します。
指定できるIPアドレスは、APIサーバに設定されている「service-cluster-ip-range」内のアドレスである必要があります。
$ kubectl -n kube-system describe pod/kube-apiserver-k8s-master | grep range
      --service-cluster-ip-range=10.96.0.0/12
ユーザー所有のIPアドレスを選択する
kubernetes.io