前面咱們給你們講解了 Service 的用法,咱們能夠經過 Service 生成的 ClusterIP(VIP)來訪問 Pod 提供的服務,可是在使用的時候還有一個問題:咱們怎麼知道某個應用的 VIP 呢?好比咱們有兩個應用,一個是 api 應用,一個是 db 應用,兩個應用都是經過 Deployment 進行管理的,而且都經過 Service 暴露出了端口提供服務。api 須要鏈接到 db 這個應用,咱們只知道 db 應用的名稱和 db 對應的 Service 的名稱,可是並不知道它的 VIP 地址,咱們前面的 Service 課程中是否是學習到咱們經過 ClusterIP 就能夠訪問到後面的 Pod 服務,若是咱們知道了 VIP 的地址是否是就好了?html
咱們知道能夠從 apiserver 中直接查詢獲取到對應 service 的後端 Endpoints信息,因此最簡單的辦法是從 apiserver 中直接查詢,若是偶爾一個特殊的應用,咱們經過 apiserver 去查詢到 Service 後面的 Endpoints 直接使用是沒問題的,可是若是每一個應用都在啓動的時候去查詢依賴的服務,這不但增長了應用的複雜度,這也致使了咱們的應用須要依賴 Kubernetes 了,耦合度過高了,不具備通用性。nginx
爲了解決上面的問題,在以前的版本中,Kubernetes 採用了環境變量的方法,每一個 Pod 啓動的時候,會經過環境變量設置全部服務的 IP 和 port 信息,這樣 Pod 中的應用能夠經過讀取環境變量來獲取依賴服務的地址信息,這種方法使用起來相對簡單,可是有一個很大的問題就是依賴的服務必須在 Pod 啓動以前就存在,否則是不會被注入到環境變量中的。好比咱們首先建立一個 Nginx 服務:(test-nginx.yaml)web
apiVersion: apps/v1beta1 kind: Deployment metadata: name: nginx-deploy labels: k8s-app: nginx-demo spec: replicas: 2 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-service labels: name: nginx-service spec: ports: - port: 5000 targetPort: 80 selector: app: nginx
建立上面的服務:shell
$ kubectl create -f test-nginx.yaml
deployment.apps "nginx-deploy" created service "nginx-service" created $ kubectl get pods NAME READY STATUS RESTARTS AGE ... nginx-deploy-75675f5897-47h4t 1/1 Running 0 53s nginx-deploy-75675f5897-mmm8w 1/1 Running 0 53s ... $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ... nginx-service ClusterIP 10.107.225.42 <none> 5000/TCP 1m ...
咱們能夠看到兩個 Pod 和一個名爲 nginx-service 的服務建立成功了,該 Service 監聽的端口是 5000,同時它會把流量轉發給它代理的全部 Pod(咱們這裏就是擁有 app: nginx 標籤的兩個 Pod)。後端
如今咱們再來建立一個普通的 Pod,觀察下該 Pod 中的環境變量是否包含上面的 nginx-service 的服務信息:(test-pod.yaml)api
apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-service-pod image: busybox command: ["/bin/sh", "-c", "env"]
而後建立該測試的 Pod:緩存
$ kubectl create -f test-pod.yaml pod "test-pod" created
等 Pod 建立完成後,咱們查看日誌信息:服務器
$ kubectl logs test-pod
... KUBERNETES_PORT=tcp://10.96.0.1:443 KUBERNETES_SERVICE_PORT=443 HOSTNAME=test-pod HOME=/root NGINX_SERVICE_PORT_5000_TCP_ADDR=10.107.225.42 NGINX_SERVICE_PORT_5000_TCP_PORT=5000 NGINX_SERVICE_PORT_5000_TCP_PROTO=tcp KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin NGINX_SERVICE_SERVICE_HOST=10.107.225.42 NGINX_SERVICE_PORT_5000_TCP=tcp://10.107.225.42:5000 KUBERNETES_PORT_443_TCP_PORT=443 KUBERNETES_PORT_443_TCP_PROTO=tcp NGINX_SERVICE_SERVICE_PORT=5000 NGINX_SERVICE_PORT=tcp://10.107.225.42:5000 KUBERNETES_SERVICE_PORT_HTTPS=443 KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443 KUBERNETES_SERVICE_HOST=10.96.0.1 PWD=/ ...
咱們能夠看到打印了不少環境變量處理,其中就包括咱們剛剛建立的 nginx-service 這個服務,有 HOST、PORT、PROTO、ADDR 等,也包括其餘已經存在的 Service 的環境變量,如今若是咱們須要在這個 Pod 裏面訪問 nginx-service 的服務,咱們是否是能夠直接經過 NGINX_SERVICE_SERVICE_HOST 和 NGINX_SERVICE_SERVICE_PORT 就能夠了,可是咱們也知道若是這個 Pod 啓動起來的時候若是 nginx-service 服務還沒啓動起來,在環境變量中咱們是沒法獲取到這些信息的,固然咱們能夠經過 initContainer 之類的方法來確保 nginx-service 啓動後再啓動 Pod,可是這種方法畢竟增長了 Pod 啓動的複雜性,因此這不是最優的方法。app
因爲上面環境變量這種方式的侷限性,咱們須要一種更加智能的方案,其實咱們能夠本身想學一種比較理想的方案:那就是能夠直接使用 Service 的名稱,由於 Service 的名稱不會變化,咱們不須要去關心分配的 ClusterIP 的地址,由於這個地址並非固定不變的,因此若是咱們直接使用 Service 的名字,而後對應的 ClusterIP 地址的轉換可以自動完成就很好了。咱們知道名字和 IP 直接的轉換是否是和咱們平時訪問的網站很是相似啊?他們之間的轉換功能經過 DNS 就能夠解決了,一樣的,Kubernetes 也提供了 DNS 的方案來解決上面的服務發現的問題。less
DNS 服務不是一個獨立的系統服務,而是做爲一種 addon 插件而存在,也就是說不是 Kubernetes 集羣必須安裝的,固然咱們強烈推薦安裝,能夠將這個插件當作是一種運行在 Kubernetes 集羣上的一直比較特殊的應用,如今比較推薦的兩個插件:kube-dns 和 CoreDNS。咱們在前面使用 kubeadm 搭建集羣的時候直接安裝的 kube-dns 插件,若是不記得了能夠回頭去看一看。固然若是咱們想使用 CoreDNS 的話也很方便,只須要執行下面的命令便可:
$ kubeadm init --feature-gates=CoreDNS=true
Kubernetes DNS pod 中包括 3 個容器,能夠經過 kubectl 工具查看:
$ kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
... kube-dns-5868f69869-zp5kz 3/3 Running 0 19d ...
READY 一欄能夠看到是 3/3,用以下命令能夠很清楚的看到 kube-dns 包含的3個容器:
$ kubectl describe pod kube-dns-5868f69869-zp5kz -n kube-system
kube-dns、dnsmasq-nanny、sidecar 這3個容器分別實現了什麼功能?
kube dns
DNS Pod 具備靜態 IP 並做爲 Kubernetes 服務暴露出來。該靜態 IP 被分配後,kubelet 會將使用 --cluster-dns = <dns-service-ip>
參數配置的 DNS 傳遞給每一個容器。DNS 名稱也須要域名,本地域可使用參數--cluster-domain = <default-local-domain>
在 kubelet 中配置。
咱們說 dnsmasq 容器經過監聽 ConfigMap 來動態生成配置,能夠自定義存根域和上下游域名服務器。
例如,下面的 ConfigMap 創建了一個 DNS 配置,它具備一個單獨的存根域和兩個上游域名服務器:
apiVersion: v1 kind: ConfigMap metadata: name: kube-dns namespace: kube-system data: stubDomains: | {"acme.local": ["1.2.3.4"]} upstreamNameservers: | ["8.8.8.8", "8.8.4.4"]
按如上說明,具備.acme.local後綴的 DNS 請求被轉發到 DNS 1.2.3.4。Google 公共 DNS 服務器 爲上游查詢提供服務。下表描述了具備特定域名的查詢如何映射到它們的目標 DNS 服務器:
域名 | 響應查詢的服務器 |
---|---|
kubernetes.default.svc.cluster.local | kube-dns |
foo.acme.local | 自定義 DNS (1.2.3.4) |
widget.com | 上游 DNS (8.8.8.8, 8.8.4.4,其中之一) |
另外咱們還能夠爲每一個 Pod 設置 DNS 策略。 當前 Kubernetes 支持兩種 Pod 特定的 DNS 策略:「Default」 和 「ClusterFirst」。 能夠經過 dnsPolicy 標誌來指定這些策略。
注意:Default 不是默認的 DNS 策略。若是沒有顯式地指定dnsPolicy,將會使用 ClusterFirst
咱們前面說了若是咱們創建的 Service 若是支持域名形式進行解析,就能夠解決咱們的服務發現的功能,那麼利用 kubedns 能夠將 Service 生成怎樣的 DNS 記錄呢?
CoreDNS 實現的功能和 KubeDNS 是一致的,不過 CoreDNS 的全部功能都集成在了同一個容器中,在最新版的1.11.0版本中官方已經推薦使用 CoreDNS了,你們也能夠安裝 CoreDNS 來代替 KubeDNS,其餘使用方法都是一致的:https://coredns.io/
如今咱們來使用一個簡單 Pod 來測試下 Service 的域名訪問:
$ kubectl run --rm -i --tty test-dns --image=busybox /bin/sh If you don't see a command prompt, try pressing enter. / # cat /etc/resolv.conf nameserver 10.96.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5 / #
咱們進入到 Pod 中,查看/etc/resolv.conf中的內容,能夠看到 nameserver 的地址10.96.0.10,該 IP 地址便是在安裝 kubedns 插件的時候集羣分配的一個固定的靜態 IP 地址,咱們能夠經過下面的命令進行查看:
$ kubectl get svc kube-dns -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 62d
也就是說咱們這個 Pod 如今默認的 nameserver 就是 kubedns 的地址,如今咱們來訪問下前面咱們建立的 nginx-service 服務:
/ # wget -q -O- nginx-service.default.svc.cluster.local
能夠看到上面咱們使用 wget 命令去訪問 nginx-service 服務的域名的時候被 hang 住了,沒有獲得指望的結果,這是由於上面咱們創建 Service 的時候暴露的端口是 5000:
/ # wget -q -O- nginx-service.default.svc.cluster.local:5000 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> 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>
加上 5000 端口,就正常訪問到服務,再試一試訪問:nginx-service.default.svc、nginx-service.default、nginx-service,不出意外這些域名均可以正常訪問到指望的結果。
到這裏咱們是否是就實現了在集羣內部經過 Service 的域名形式進行互相通訊了,你們下去試着看看訪問不一樣 namespace 下面的服務呢?下節課咱們來給你們講解使用 ingress 來實現集羣外部的服務發現功能。