kubernetes - day20

September 10, 2020

前面章節講過的 Service,提供了一個穩定可讓客戶端取得想要的服務接口,這之間 POD 如何知道某特定服務的 IPPort 呢?這就需要服務發現(Service Discovery) 的幫助。我們也知道在 K8s 環境中,POD 不能依靠網路,當服務重新部署時,會導致 IP 不同,因此需要借助服務發現。服務發現簡單來說就是是弄清楚如何連接到服務的實際過程。

環境變數的服務發現

Service 環境變數

只要創建 Service 都會使用以下的環境變數,當在同一 namespacePOD 會自動擁有這些資訊。可參考官網 container-environment

$ kubectl exec httpd-7765f5994-97hq5 -- printenv
PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=httpd-7765f5994-97hq5
MYAPP_SVC_PORT_80_TCP_ADDR=10.8.2.52
MYAPP_SVC_SERVICE_HOST=10.8.2.52 # this
KUBERNETES_PORT_443_TCP_ADDR=10.8.0.1
MYAPP_SVC_PORT_80_TCP_PORT=80
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
MYAPP_SVC_PORT_80_TCP_PROTO=tcp
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.8.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.8.0.1:443
MYAPP_SVC_SERVICE_PORT=80 # this
MYAPP_SVC_PORT=tcp://10.8.2.52:80
MYAPP_SVC_PORT_80_TCP=tcp://10.8.2.52:80
...

這邊我在 GCP 上的 Compute Engine 拿一個節點的容器用 docker inspect 觀察。

$ docker inspect 203e907bf5ba
"Env": [
                "MYAPP_SVC_PORT=tcp://10.8.2.52:80",
                "MYAPP_SVC_PORT_80_TCP_PROTO=tcp",
                "KUBERNETES_SERVICE_PORT=443",
                "KUBERNETES_SERVICE_PORT_HTTPS=443",
                "KUBERNETES_PORT=tcp://10.8.0.1:443",
                "KUBERNETES_PORT_443_TCP=tcp://10.8.0.1:443",
                "KUBERNETES_SERVICE_HOST=10.8.0.1",
                "KUBERNETES_PORT_443_TCP_PROTO=tcp",
                "MYAPP_SVC_SERVICE_PORT=80",
                "MYAPP_SVC_PORT_80_TCP=tcp://10.8.2.52:80",
                "MYAPP_SVC_PORT_80_TCP_PORT=80",
                "KUBERNETES_PORT_443_TCP_PORT=443",
                "KUBERNETES_PORT_443_TCP_ADDR=10.8.0.1",
                "MYAPP_SVC_SERVICE_HOST=10.8.2.52",
                "MYAPP_SVC_PORT_80_TCP_ADDR=10.8.2.52",
                "PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "HTTPD_PREFIX=/usr/local/apache2",
                "HTTPD_VERSION=2.4.46",
                "HTTPD_SHA256=740eddf6e1c641992b22359cabc66e6325868c3c5e2e3f98faf349b61ecf41ea",
                "HTTPD_PATCHES="
            ],

但是,在不同 namespace、或是 PODService 部署順序不同時,環境變數方式將是一個缺點,然而 DNS 可以解決這個問題。

DNS 服務發現

Kubernetes 上在安裝環境時,有一個 CoreDNS,是一個非常重要的元件之一,它會負責將 Service 資源等轉成資源紀錄(Resource Record)。在預設下 POD 的資源紀錄也會被自動的配置,當中資源紀錄會包含 namespace 等資訊。下面是從官方Kubernetes DNS 規格定義,所節錄。

SRV 相較於其它資源類型它記錄了 Port

只要我們創建 Service 資源,DNS 會做服務解析和服務註冊的動作,這樣可讓 POD 使用 DNS 方式去請求 Service。每個 Service 會包含以下兩個資源紀錄。在安裝 K8s 環境時可設定 DNS 相關資訊,否則默認把 cluster.local. 和主機所在的域名做為本地用。

這邊觀察 GKE 上部署的一個 POD 它的 DNS 相關設置。也就是說服務的查詢都會導向該 CoreDNSService 上。

$ kubectl exec hello-kubernetes-5b89dbbc4b-t7bvr -- cat /etc/resolv.conf
nameserver 10.8.0.10 
search default.svc.cluster.local svc.cluster.local cluster.local us-central1-c.c.sunny-catwalk-286908.internal c.sunny-catwalk-286908.internal google.internal
options ndots:5
$ kubectl get svc -n kube-system # 系統上的 DNS 系統 
NAME                   TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)         AGE
...
kube-dns               ClusterIP   10.8.0.10     <none>        53/UDP,53/TCP   18d
...

我們進到一個可交互的 POD 並使用 nslookup 查詢 myapp-svc 的域名 myapp-svc.default.svc.cluster.local,發現解析出來的就是該 ServiceIP

$ kubectl exec -it hello-kubernetes-5b89dbbc4b-t7bvr /bin/sh
~ $ nslookup myapp-svc.default.svc.cluster.local
Server:         10.8.0.10
Address:        10.8.0.10:53

Name:   myapp-svc.default.svc.cluster.local
Address: 10.8.2.52
$ kubectl get svc myapp-svc
NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
myapp-svc   ClusterIP   10.8.2.52    <none>        80/TCP    2d1h

這是基於 SRV 做查詢格式為 _PORT-NAME._PROTOCOL.SERVICE-NAME.NAMESPACE.svc.cluster.local

$ nslookup _http._tcp.myapp-svc.default.svc.cluster.local
Server:         10.8.0.10
Address:        10.8.0.10:53

_http._tcp.myapp-svc.default.svc.cluster.local  canonical name = myapp-svc.default.svc.cluster.local
Name:   myapp-svc.default.svc.cluster.local
Address: 10.8.2.52

對於 POD 解析格式為 POD-IP-ADDRESS.NAMESPACE.pod.cluster.loscal,這邊是看官網一些用法都在裡面

$ nslookup 10-4-2-53.default.pod.cluster.local
Server:         10.8.0.10
Address:        10.8.0.10:53


Name:   10-4-2-53.default.pod.cluster.local
Address: 10.4.2.53

POD DNS

因為 POD 對於 DNS 可能有不同的需求,因此也提供設定方式,這邊可直接參考官方

$ kubectl exec hello-kubernetes-5b89dbbc4b-t7bvr -- cat /etc/resolv.conf
nameserver 10.8.0.10 
search default.svc.cluster.local svc.cluster.local cluster.local us-central1-c.c.sunny-catwalk-286908.internal c.sunny-catwalk-286908.internal google.internal
options ndots:5

參考資源