1. 程式人生 > >k8s中hostname, hosts檔案, DNS和代理問題, service和pod的訪問問題

k8s中hostname, hosts檔案, DNS和代理問題, service和pod的訪問問題

驗證一個域名的ip地址可以使用

nslookup  xx.xx..xx

在kubernets中不同名稱空間的服務相互訪問

涉及到的是Pod和Service之間的相互訪問,主要格式如下:

    訪問Pod:(注意下面不是直接pod的ip,而是由pod的ip組成的字串)
    {pod-ip}.{namespace}.pod.cluster.local
  例如某pod的ip為  1.2.3.4,在名稱空間default與DNS名稱cluster.local將有一個域名:1-2-3-4.default.pod.cluster.local。

    {hostname}.{subdomain}.{namespace}.svc.cluster.local
    subdomain是在建立pod設定的屬性,和hostname可以一起設定
    
    訪問StatefulSet:
    {pod-name}.{service-name}.{namespace}.svc.cluster.local
    可以進入到pod中檢視/etc/hosts
    
    訪問Service:
    {service-name}.{namespace}.svc.cluster.local

當你建立一個Service時,Kubernetes會自動建立一個形如<service-name>.<namespace-name>.svc.cluster.local的DNS項。如果叢集中另一個服務呼叫這個服務時,僅僅指定了<service-name>,那麼Kubernetes會使用呼叫方所在的Namespace將<service-name>補全。因此如果呼叫方和被呼叫方不處於同一個Namespace,你必須使用包含Namespace的service name

修改pod的hostname和subdomain

可以在 Pod /Deployment中指定pod的 hostname 和 subdomain:,例如:

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: busybox-subdomain
  containers:
  name: busybox
  - image: busybox
    command:
    - sleep
    - "3600"

因為pod的訪問域名是hostname.custom-subdomain.default.svc.cluster.local

所以該 Pod 的域名是 busybox-1.busybox-subdomain.default.svc.cluster.local。

使用 HostAliases 向 Pod /etc/hosts 檔案新增條目

當 DNS 配置以及其它選項不合理的時候,通過向 Pod 的 /etc/hosts 檔案中新增條目,可以在 Pod 級別覆蓋對主機名的解析。在 1.7 版本,使用者可以通過 PodSpec 的 HostAliases 欄位來新增這些自定義的條目。

建議通過使用 HostAliases 來進行修改,因為該檔案由 Kubelet 管理,並且可以在 Pod 建立/重啟過程中被重寫。

預設 hosts 檔案內容

讓我們從一個 Nginx Pod 開始,給該 Pod 分配一個 IP:

$ kubectl get pods --output=wide
NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
nginx    1/1       Running   0          13s    10.200.0.4   worker0

預設,hosts 檔案只包含 ipv4 和 ipv6 的樣板內容,像 localhost 和主機名稱。

通過 HostAliases 增加額外的條目

除了預設的樣板內容,我們可以向 hosts 檔案新增額外的條目,將 foo.local、 bar.local 解析為127.0.0.1,將 foo.remote、 bar.remote 解析為 10.1.2.3,我們可以在 .spec.hostAliases 下為 Pod 新增 HostAliases。

hostaliases-pod.yaml docs/concepts/services-networking  Copy hostaliases-pod.yaml to clipboard
apiVersion: v1
kind: Pod
metadata:
  name: hostaliases-pod
spec:
  hostAliases:
  - ip: "127.0.0.1"
    hostnames:
    - "foo.local"
    - "bar.local"
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
  containers:
  - name: cat-hosts
    image: busybox
    command:
    - cat
    args:
    - "/etc/hosts"

hosts 檔案的內容看起來類似如下這樣:

$ kubectl logs hostaliases-pod
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.4	hostaliases-pod
127.0.0.1	foo.local
127.0.0.1	bar.local
10.1.2.3	foo.remote
10.1.2.3	bar.remote

在最下面額外添加了一些條目。

限制

在 1.7 版本,如果 Pod 啟用 hostNetwork,那麼將不能使用這個特性,因為 kubelet 只管理非 hostNetwork 型別 Pod 的 hosts 檔案。目前正在討論要改變這個情況。

為什麼 Kubelet 管理 hosts檔案?

kubelet 管理 Pod 中每個容器的 hosts 檔案,避免 Docker 在容器已經啟動之後去 修改 該檔案。

因為該檔案是託管性質的檔案,無論容器重啟或 Pod 重新排程,使用者修改該 hosts 檔案的任何內容,都會在 Kubelet 重新安裝後被覆蓋。因此,不建議修改該檔案的內容。

參考:https://kubernetes.io/zh/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/

Pod 之間是如何找到彼此呢 - DNS Service Discovery

介紹 DNS 是什麼 ?

DNS,全名為 Domain Name System。DNS 會有一張表格,記錄每個 domain name 相對應的 IP 位址。如此,我們不再需要去記錄該服務的 IP address,而是可以透過該服務的域名來連線到該服務。以 www.google.com.tw 為例,當我們在瀏覽器輸入 www.google.com.tw 時,DNS會幫我們找到該域名相對應的 IP,並連線到該服務.

如果想知道某個特定的域名相對應的 IP 位址,可以在終端機輸入 host 指令,

在這裡插入圖片描述

介紹 Kubernetes 內部外掛 kube-dns

而 Kubernetes 本身提供了 DNS 的套件,kube-dns。 kube-dns 幫助在同一個 Kubernetes Cluster 中的所有 Pods ,都能透過 Service 的名稱找到彼此。透過 kubectl get 指令,會發現 kube-dns 也是一個在 Cluster 中執行的服務,一旦 Kubernetes Cluster 被建立後,便會自動執行。
在這裡插入圖片描述

而 kube-dns 的相關設定檔則放是放在 master node 的 /etc/kubernetes/addons 資料夾底下,以 minikube 為例,minikube 執行的 VM 本身就是 master node,所以我們先透過 minikube ssh 指令登入,
在這裡插入圖片描述

在 /etc/kubernetes/addons 資料夾底下,除了可以看到 kube-dns 的相關設定檔外,也可以看到 Kubernetes 其他套件的設定檔。

kube-dns 如何運作

如上圖我們可以看到 kube-dns 是執行在 Kubernetes Cluster 的一個 Pod,而這個 kube-dns-86f6f55dd5-cht2h Pod,也有一個相對應的 Service 物件。
在這裡插入圖片描述

Kubernetes 在每一個 Pod 建立時,都會在該 Pod 的 /etc/resolve.conf 檔案中,自動加入 kube-dns service 的 domain name 與相對應的 IP 位址。因此 其他 Pods 可以透過名稱為 kube-dns 的 Service 物件,找到正在執行的 kube-dns,以下圖為例:
在這裡插入圖片描述
我們在 Kubernetes Cluster 跑起 alpine Pod,並 ssh 進到 alpine 的 shell,用 cat 指令檢視 /etc/resolve.conf 內容,

若是與上上圖比對,會發現 /etc/resolve.conf 中的 nameserver 指向的 IP 位置,就是 kube-dns 的 Service。或者是dnsmasq的service

配置Kubernetes DNS服務kube-dns

DNS服務有很多中方式, 官方推薦安裝的是kube-dns, 我們這裡先介紹這個. 如果你的k8s上dns服務不是使用這個外掛進行管理的,可以跳過.

kube-dns是Kubernetes中的一個內建外掛,目前作為一個獨立的開源專案維護,見https://github.com/kubernetes/dns。

前提要求

Kubernetes 1.6 及以上版本。
叢集必須使用 kube-dns 外掛進行配置。

kube-dns 介紹

從 Kubernetes v1.3 版本開始,使用 [cluster add-on 外掛管理器回自動啟動內建的 DNS。

Kubernetes DNS pod 中包括 3 個容器:

  • kubedns:kubedns 程序監視 Kubernetes master 中的 Service 和 Endpoint 的變化,並維護記憶體查詢結構來服務DNS請求。
  • dnsmasq:dnsmasq 容器新增 DNS 快取以提高效能。
  • sidecar:sidecar 容器在執行雙重健康檢查(針對 dnsmasq 和 kubedns)時提供單個健康檢查端點(監聽在10054埠)。

在這裡插入圖片描述

Kube-DNS以Pod的形式部署到kubernetes集群系統;
Kube-DNS對SkyDNS進行封裝優化,由4個容器變成3個;
kubedns容器:基於skydns實現;監視k8s Service資源並更新DNS記錄;替換etcd,使用TreeCache資料結構儲存DNS記錄並實現SkyDNS的Backend介面;接入SkyDNS,對dnsmasq提供DNS查詢服務;
dnsmasq容器:為叢集提供DNS查詢服務,即簡易的dns server;設定kubedns為upstream;提供DNS快取,降低kubedns負載,提高效能;
sidecar容器:監控健康模組,同時向外暴露metrics記錄;定期檢查kubedns和dnsmasq的健康狀態;為k8s活性檢測提供HTTP API。

上面的DNS pod 具有靜態 IP 並作為 Kubernetes 服務暴露出來。該靜態 IP 分配後,kubelet 會將使用 --cluster-dns = <dns-service-ip> 標誌配置的 DNS 傳遞給3個容器的每個容器。

DNS 名稱也需要域名。本地域可以使用標誌 --cluster-domain = <default-local-domain> 在 kubelet 中配置。

Kubernetes叢集DNS伺服器基於 SkyDNS 庫。它支援正向查詢(A 記錄),服務查詢(SRV 記錄)和反向 IP 地址查詢(PTR 記錄)

kube-dns 支援的 DNS 格式

kube-dns 將分別為 service 和 pod 生成不同格式的 DNS 記錄。

Service

  • A記錄:生成my-svc.my-namespace.svc.cluster.local域名,解析成 IP 地址,分為兩種情況:
    • 普通 Service:解析成 ClusterIP
    • Headless Service:解析為指定 Pod 的 IP 列表
  • SRV記錄:為命名的埠(普通 Service 或 Headless Service)生成 _my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster.local 的域名

Pod

  • A記錄:生成域名 pod-ip.my-namespace.pod.cluster.local

繼承節點的 DNS

執行 Pod 時,kubelet 將預先配置叢集 DNS 伺服器到 Pod 中,並搜尋節點自己的 DNS 設定路徑。如果節點能夠解析特定於較大環境的 DNS 名稱,那麼 Pod 應該也能夠解析。請參閱下面的已知問題以瞭解警告。

自定義pod中的dns伺服器

如果您不想要這個,或者您想要為 Pod 設定不同的 DNS 配置,您可以給 kubelet 指定 --resolv-conf 標誌。將該值設定為 “” 意味著 Pod 不繼承 DNS。將其設定為有效的檔案路徑意味著 kubelet 將使用此檔案而不是 /etc/resolv.conf 用於 DNS 繼承。

配置存根域和上游 DNS 伺服器

通過為 kube-dns (kube-system:kube-dns)提供一個 ConfigMap,叢集管理員能夠指定自定義存根域和上游 nameserver。

例如,下面的 ConfigMap 建立了一個 DNS 配置,它具有一個單獨的存根域和兩個上游 nameserver:

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 請求被轉發到 1.2.3.4 處監聽的 DNS。 “8.8.8.8”, “8.8.4.4”(這兩個ip為Google Public DNS) 為上游查詢提供服務。

ConfigMap 選項

kube-dns kube-system:kube-dns ConfigMap 的選項如下所示:

欄位 格式 描述
stubDomains(可選) 使用 DNS 字尾 key 的 JSON map(例如 “acme.local”),以及 DNS IP 的 JSON 陣列作為 value。 目標 nameserver 可能是一個 Kubernetes Service。例如,可以執行自己的 dnsmasq 副本,將 DNS 名字暴露到 ClusterDNS namespace 中。
upstreamNameservers(可選) DNS IP 的 JSON 陣列。 注意:如果指定,則指定的值會替換掉被預設從節點的 /etc/resolv.conf 中獲取到的 nameserver。限制:最多可以指定三個上游 nameserver。

注意,叢集管理員不希望覆蓋節點的上游 nameserver,所以他們不會指定可選的 upstreamNameservers 欄位。

下表描述瞭如何將具有特定域名的查詢對映到其目標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 的影響

Kubernetes 目前在 Pod 定義中支援兩個 DNS 策略:Default 和 ClusterFirst,dnsPolicy 預設為 ClusterFirst。如果將 dnsPolicy 設定為 Default,域名解析配置則完全從 Pod 所在的節點(/etc/resolv.conf)繼承而來。

在這裡插入圖片描述

如果包含自定義DNS,則邏輯圖為

在這裡插入圖片描述

自定義的上游名稱伺服器和存根域不會影響那些將自己的 dnsPolicy 設定為 Default 或者 None 的 Pod。

如果 Pod 的 dnsPolicy 設定為 “ClusterFirst”,則其名稱解析將按其他方式處理,具體取決於存根域和上游 DNS 伺服器的配置。

未進行自定義配置:沒有匹配上配置的叢集域名字尾的任何請求,例如 “www.kubernetes.io”,將會被轉發到繼承自節點的上游 nameserver。

進行自定義配置:如果配置了存根域和上游 DNS 伺服器(和在 前面例子 配置的一樣),DNS 查詢將根據下面的流程進行路由:

  • 查詢首先被髮送到 kube-dns 中的 DNS 快取層。

  • 從快取層,檢查請求的字尾,並轉發到合適的 DNS 上,基於如下的示例:

    • 具有集群后綴的名字(例如 “.cluster.local”):請求被髮送到 kube-dns。
    • 具有存根域字尾的名字(例如 “.acme.local”):請求被髮送到配置的自定義 DNS 解析器(例如:監聽在 1.2.3.4)。
    • 不具有能匹配上字尾的名字(例如 “widget.com”):請求被轉發到上游 DNS(例如:Google 公共 DNS 伺服器,8.8.8.8 和 8.8.4.4)。

在這裡插入圖片描述

使用dnsmasq自定義DNS伺服器,並新增自己的記錄

上面的教程都是配置dns伺服器的地址, 我們也可以自己來建一個dns伺服器,並新增記錄,然後將自定義的dns伺服器新增為k8s的上游dns伺服器.

dnsmasq的部署參考:https://blog.csdn.net/luanpeng825485697/article/details/84139310

dnsmasq的解析流程

dnsmasq先去解析hosts檔案, 再去解析/etc/dnsmasq.d/下的*.conf檔案,並且這些檔案的優先順序要高於dnsmasq.conf,我們自定義的resolv.dnsmasq.conf中的DNS也被稱為上游DNS,這是最後去查詢解析的;

如果不想用hosts檔案做解析,我們可以在/etc/dnsmasq.conf中加入no-hosts這條語句,這樣的話就直接查詢上游DNS了,如果我們不想做上游查詢,就是不想做正常的解析,我們可以加入no-reslov這條語句。

dnsmasq.conf檔案中的配置含義參考: http://blog.51cto.com/longlei/2065967

dnsmasq的引數及常用設定說明

編輯 dnsmasq 的配置檔案 /etc/dnsmasq.conf 。這個檔案包含大量的選項註釋。

(1)dnsmasq經常修改的比較重要引數說明

具體引數 引數說明
resolv-file 定義dnsmasq從哪裡獲取上游DNS伺服器的地址, 預設從/etc/resolv.conf獲取。
strict-order 表示嚴格按照resolv-file檔案中的順序從上到下進行DNS解析,直到第一個解析成功為止。
listen-address 定義dnsmasq監聽的地址,預設是監控本機的所有網絡卡上。
address 啟用泛域名解析,即自定義解析a記錄,例如:address=/long.com/192.168.115.10 訪問long.com時的所有域名都會被解析成192.168.115.10
bogus-nxdomain 對於任何被解析到此 IP 的域名,將響應 NXDOMAIN 使其解析失效,可以多次指定. 通常用於對於訪問不存在的域名,禁止其跳轉到運營商的廣告站點
server 指定使用哪個DNS伺服器進行解析,對於不同的網站可以使用不同的域名對應解析。例如:server=/google.com/8.8.8.8 #表示對於google的服務,使用谷歌的DNS解析。

(2)檢視配置檔案語法是否正確,可執行下列命令

[[email protected] ~]# dnsmasq --test
dnsmasq: syntax check OK.

(3)DNS 快取設定

要在單臺電腦上以守護程序方式啟動dnsmasq做DNS快取伺服器,編輯/etc/dnsmasq.conf,新增監聽地址:

listen-address=127.0.0.1

如果用此主機為區域網提供預設 DNS,請用為該主機繫結固定 IP 地址,設定:

listen-address=192.168.x.x

這種情況建議配置靜態IP

多個ip地址設定:

listen-address=127.0.0.1,192.168.x.x

(4)三個以上域名伺服器
Linux 處理 DNS 請求時有個限制,在 resolv.conf 中最多隻能配置三個域名伺服器(nameserver)。作為一種變通方法,可以在 resolv.conf 檔案中只保留 localhost 作為域名伺服器,然後為外部域名伺服器另外建立 resolv-file 檔案。

也就是/etc/resolv.conf內容為

nameserver 127.0.0.1

首先,為 dnsmasq 新建一個域名解析檔案:

[[email protected] ~]# vim /etc/resolv.dnsmasq.conf
# Google's nameservers, for example
nameserver 8.8.8.8
nameserver 8.8.4.4

然後編輯 /etc/dnsmasq.conf 讓 dnsmasq 使用新建立的域名解析檔案:

[[email protected] ~]# vim  /etc/dnsmasq.conf
...
resolv-file=/etc/resolv.dnsmasq.conf

增加自己的dns解析記錄

通過上面的理論基礎我們知道了dns解析的流程.現在我們增加一條自己的記錄.

dnsmasq支援讀取/etc/hosts檔案作為自己的DNS記錄,如果/etc/hosts解析不方便的話,還可以指定–addn-hosts,指定某一檔案作為hosts新增到dnsmasq快取中。所以現在方案就簡單了

建立一個configmap,其內容為待解析的域名記錄;將configmap作為volume掛載到dnsmasq容器的/dns目錄下,然後為dnsmasq增加引數–addn-hosts=/dns/key,宿主機主機名記錄就可以加到dnsmasq中去了。

如果叢集擴容,configmap更新了呢?我們知道configmap更新了以後,會更新掛載到容器中的檔案;但遺憾的是,dnsmasq並不會管addn-hosts的檔案的變化,新機器的主機名並不能加到kube-dns中。

辦法也有,因為kube-dns是一個deployment,所以可以將其scaleout 1 -> 0 -> 1:

kubectl scale kube-dns --replicas=0 -n kube-system
kubectl scale kube-dns --replicas=1 -n kube-system

缺點就是叢集內會暫時關閉DNS解析。

或者 我們也可以在/etc/dnsmasq.d/下的*.conf檔案中增加一行自己想要的解析記錄

address=/long.com/192.168.115.10

增加以後重啟pod就可以了. 跟前面的方法其實是一樣的.

使用CoreDNS進行服務發現

上面我們使用的是kube-dns進行的dns管理,當然還有很多其他的dns管理工具, 用的多的就是CoreDNS.

要使用CoreDN您的Kubernetes伺服器必須是v1.9或更高版本。要檢查版本,請輸入kubectl version。

注意:在Kubernetes 1.11中,CoreDNS已經而配置為通用可用性(GA)並預設安裝。

CoreDNS是一個通用的權威DNS伺服器,可以作為叢集DNS,符合dns規範。並且在高版本的k8s中CoreDNS作為預設的dns伺服器

使用kubeadm升級現有叢集

在Kubernetes 1.10及更高版本中,您還可以在使用kubeadm升級正在使用的群集時移至CoreDNS 。在這種情況下,kubeadm將基於kube-dnsConfigMap 生成CoreDNS配置(“Corefile”),保留聯合,存根域和上游名稱伺服器的配置。

如果要從kube-dns轉移到CoreDNS,請確保 在升級期間將CoreDNS功能門設定為true。例如,以下是v1.11.0升級的樣子:

kubeadm upgrade apply v1.11.0 --feature-gates=CoreDNS=true

在1.11之前的版本中,Corefile將被升級期間建立的檔案覆蓋。 如果已對其進行自定義,則應儲存現有的ConfigMap。在新的ConfigMap啟動並執行後,您可以重新應用自定義。

如果您在Kubernetes 1.11及更高版本中執行CoreDNS,則在升級期間,將保留現有的Corefile。

CoreDNS ConfigMap選項

CoreDNS是一個模組化和可插拔的DNS伺服器,每個外掛都為CoreDNS添加了新功能。這可以通過維護Corefile來配置,Corefile是CoreDNS配置檔案。叢集管理員可以修改CoreDNS Corefile的ConfigMap以更改服務發現的工作方式。

在Kubernetes中,CoreDNS安裝了以下預設的Corefile配置。

在這裡插入圖片描述

官網參考:https://github.com/coredns/coredns/tree/master/plugin/kubernetes

Corefile配置包括以下CoreDNS 外掛:

  • error:錯誤記錄到stdout。
  • health:CoreDNS的執行狀況報告為http:// localhost:8080 / health。
  • kubernetes:CoreDNS將根據Kubernetes服務和pod的IP回覆DNS查詢。
  • prometheus:CoreDNS的度量標準可以在http:// localhost:9153 / Prometheus格式的指標中找到。
  • proxy:任何不在Kubernetes叢集域內的查詢都將轉發到預定義的解析器(/etc/resolv.conf)。
  • cache:這將啟用前端快取。
  • loop:檢測簡單的轉發迴圈,如果找到迴圈則停止CoreDNS程序。
  • reload:允許自動重新載入已更改的Corefile。
  • loadbalance:這是一個迴圈DNS負載均衡器,通過在答案中隨機化A,AAAA和MX記錄的順序。

kubernetes下包含更多的配置項

 kubernetes [ZONES...] {
        resyncperiod DURATION
        endpoint URL [URL...]
        tls CERT KEY CACERT
        kubeconfig KUBECONFIG CONTEXT
        namespaces NAMESPACE...
        labels EXPRESSION
        pods POD-MODE
        endpoint_pod_names
        upstream [ADDRESS...]
        ttl TTL
        noendpoints
        transfer to ADDRESS...
        fallthrough [ZONES...]
        ignore empty_service
    }

其中

  • resyncperiod指定Kubernetes資料API DURATION週期。

    • pods insecure返回一個A記錄對應的ip,但並不會檢查這個ip對應的Pod當前是否存在。這個選項主要用於相容kube-dns。
    • verified:推薦的方式,返回A記錄的同時會確保對應ip的pod存在。比insecure會消耗更多的記憶體。
    • disabled:預設, 如果您不使用pod記錄,則可以使用該選項。
  • Upstream 用於解析指向外部主機的服務(外部服務)。

參考官網:https://github.com/coredns/coredns/tree/master/plugin/kubernetes

我們可以通過修改此configmap來修改預設行為。

使用CoreDNS配置Stub域和上游名稱伺服器

如果叢集運營商的Consul域伺服器位於10.150.0.1,並且所有Consul名稱都具有後綴.consul.local。要在CoreDNS中配置它,叢集管理員在CoreDNS ConfigMap中建立。

另外如果明確強制所有非群集DNS的查詢在172.16.0.1要經過特定的名稱伺服器,proxy和upstream對域名伺服器,而不是/etc/resolv.conf(好吧,我翻譯錯了)

To explicitly force all non-cluster DNS lookups to go through a specific nameserver at 172.16.0.1, point the proxy and upstream to the nameserver instead of /etc/resolv.conf

則設定成下面的配置

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           upstream 172.16.0.1
           fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        proxy . 172.16.0.1
        cache 30
        loop
        reload
        loadbalance
    }
    consul.local:53 {
        errors
        cache 30
        proxy . 10.150.0.1
    }

在Kubernetes 1.10及更高版本中,kubeadm支援從kube-dns ConfigMap自動轉換CoreDNS ConfigMap。

又例如下面coredns配置檔案:

1.訪問cluster.local字尾的,去查10.254.0.2
2.訪問out-of.kubernetes的如server.out-of.kubernetes去查192.168.x.x
3.訪問網際網路的,走resolve.conf的地址

    .:53 {
        errors      # show errors
        log stdout  # show query logs
        health
        kubernetes cluster.local 10.254.0.0/16
        proxy out-of.kubernetes 192.168.x.x
        proxy . /etc/resolv.conf

再例如

. {
    debug
    errors
    whoami
    log
    proxy . /etc/resolv.conf {
            except mydomain.com
        }
    file /file/local mydomain.com
}

debug errors whoami log file proxy都是coredns支援的外掛,這裡就直接使用。上面最重要的是proxy和file兩個外掛。

proxy外掛中 . /etc/resolv.conf表示所有的查詢請求都由/etc/resolv.conf中的nameserver來負責解析,但除了mydomain.com的請求。

mydomain.com的請求怎麼處理呢?就靠下面的file /file/local。coredns會載入/file/local中的資料,並且使用這個檔案的資料來解析所有和mydomain.com有關的請求。這裡需要注意,為什麼coredns知道mydomain.com有關的資料都在local檔案中,是因為file /file/local mydomain.com最後的mydomain.com表示local檔案繫結的就是mydomain.com這個域名。

在pod內部自定義 DNS延伸

為了讓使用者更容易控制 Pod 中的 DNS 設定,Kubernetes v1.9 引入了一項新的 Alpha 特性(在 v1.10 中處於 Beta 階段)。該特性在 v1.10 中被預設啟用,在 v1.9 中如果想要啟用此功能,叢集管理員需要在 apiserver 和 kubelet 上啟用 CustomPodDNS 特性,例如:“–feature-gates=CustomPodDNS=true,…”。啟用了該特性之後,使用者可以將 Pod 的 dnsPolicy 欄位設定為 “None”,並且可以在 Pod.Spec 中新增新的欄位 dnsConfig。

其中 dnsConfig 用來自定義 DNS 引數,而 dnsPolicy 用來給 Pod 選取預設的 DNS。接下來就看看可以通過哪些手段自定義 DNS。

dnsConfig

dnsConfig 可以讓操作者延伸到 Pod 內部關於 DNS 的配置,這邊需要特別注意的是,我使用的字眼是 延伸 而不是 配置,這是因為通過下一節的 dnsPolicy,每個 Pod 都會有一組預設的 DNS 配置。通過 dnsConfig 我們可以繼續往上疊加相關的 DNS 引數到 Pod 之中。

目前總共支援三個引數,分別是:

  • nameservers
  • searches
  • options

這三個引數對應的就是大家熟悉的 /etc/resolv.conf 裡面的三個引數,下面解釋一下

最主要是nameserver關鍵字,如果沒指定nameserver就找不到DNS伺服器,其它關鍵字是可選的。

  • nameserver表示解析域名時使用該地址指定的主機為域名伺服器。其中域名伺服器是按照檔案中出現的順序來查詢的,且只有當第一個nameserver沒有反應時才查詢下面的nameserver。
  • domain   宣告主機的域名。很多程式用到它,如郵件系統;當為沒有域名的主機進行DNS查詢時,也要用到。如果沒有域名,主機名將被使用,刪除所有在第一個點( .)前面的內容。
  • search   它的多個引數指明域名查詢順序。當要查詢沒有域名的主機,主機將在由search宣告的域中分別查詢。
  • domain和search不能共存;如果同時存在,後面出現的將會被使用。
  • sortlist  允許將得到域名結果進行特定的排序。它的引數為網路/掩碼對,允許任意的排列順序。
  • options 設定為 timeout:n attempts:n 其中timeout是指連線某個dnsserver有問題造成堵塞的超時值,單位是秒;attempts是指解析域名嘗試的次數。(好像不是這個含義)

在 Kubernetes 裡面,這三個引數都包含在 dnsConfig 配置項中,而 dnsConfig 包含在 PodSpec 配置項中,因為 Pod 內所有的容器都共享相同的 Network Namespace,所以網路方面的配置都會共享。

這邊提供一個簡單的 yaml 示例:

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-setting
  namespace: default
spec:
  containers:
  - image: hwchiu/netutils
    command:
      - sleep
      - "360000"
    imagePullPolicy: IfNotPresent
    name: ubuntu
  restartPolicy: Always
  dnsConfig:
    nameservers:
      - 1.2.3.4
    searches:
      - ns1.svc.cluster.local
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

通過上述 yaml 建立 Pod 之後,通過下面的命令可以觀察到容器中 DNS 配置檔案中會出現額外的配置。

$ kubectl exec ubuntu-setting cat /etc/resolv.conf

nameserver 10.254.0.2
nameserver 1.2.3.4
search default.svc.cluster.local svc.cluster.local cluster.local ns1.svc.cluster.local my.dns.search.suffix
options ndots:2 edns0

可以看到 nameserver 多了一個 1.2.3.4,而 search 則多了 ns1.svc.cluster.local my.dns.search.suffix 這兩個自定義的值,最後 options 則增加了我們示例中指定的 ndots:2 edns0。

dnsConfig 非常簡單直觀,如果你需要自定義 DNS 引數,就可以通過這個欄位來指定。

dnsPolicy

前面提過,dnsConfig 提供的是延伸 Pod 內預設的 DNS 配置,而 dnsPolicy 就是決定 Pod 內預設的 DNS 配置有哪些。

目前總共有四個型別可以選擇:

  • None
  • Default
  • ClusterFirst
  • ClusterFirstHostNet

接下來針對這四個型別分別介紹。

None

None 表示會清除 Pod 預設的 DNS 配置,當 dnsPolicy 設定成這個值之後,Kubernetes 不會為 Pod 預先載入任何自身邏輯判斷得到的 DNS 配置。因此若要將 dnsPolicy 的值設為 None,為了避免 Pod 裡面沒有配置任何 DNS,最好再新增 dnsConfig 來描述自定義的 DNS 引數。

Default

Default 表示 Pod 裡面的 DNS 配置繼承了宿主機上的 DNS 配置。簡單來說,就是該 Pod 的 DNS 配置會跟宿主機完全一致。也就是和node上的dns配置是一樣的

ClusterFirst

相對於上述的 Default,ClusterFirst 是完全相反的操作,它會預先把 kube-dns(或 CoreDNS)的資訊當作預設引數寫入到該 Pod 內的 DNS 配置。

ClusterFirst 是預設的行為,若沒有在 Pod 內特別描述 PodPolicy, 則會將 dnsPolicy 預設為 ClusterFirst。

不過ClusterFirst 還有一個衝突,如果你的 Pod 設定了 HostNetwork=true,則 ClusterFirst 就會被強制轉換成 Default。

ClusterFirstWithHostNet

ClusterFirstWithHostNet 用途非常簡單,就是為了滿足使用 HostNetwork 同時使用 k8s DNS 作為我 Pod 預設 DNS 的配置。

只要將 dnsPolicy 設定為 ClusterFirstWithHostNet, 就會一律返回 k8s DNS 的 clusterIP 這種形式。

hostNetwork就可以並行存在了

apiVersion: v1
kind: Pod
metadata:
  name: ubuntu-hostnetwork-policy
  namespace: default
spec:
  containers:
  - image: hwchiu/netutils
    command:
      - sleep
      - "360000"
    imagePullPolicy: IfNotPresent
    name: ubuntu
  hostNetwork: true
  restartPolicy: Always
  dnsPolicy: ClusterFirstWithHostNet

關於如果使用

  hostNetwork: true
  restartPolicy: Always
  dnsPolicy: ClusterFirst

的錯誤這裡解釋一下設計上的原理以及流程:

  • 因為設定了 HostNetwork=true, 會讓該 Pod 與該節點共用相同的網路空間(網絡卡/路由等功能)。
  • 而預設的 k8s DNS 是使用 ClusterIP 的 kubernetes serivce. 這種情況下,只有屬於 Cluster 內的 Pod 可以獲取該 ClusterIP。
  • 所以設定了 HostNetwork=true 的 Pod 就沒有辦法獲取該 ClusterIP。
  • 於是預設就會將對應的 DNS 配置改回 Default 的形式,從節點繼承其 DNS 配置資訊。

coredns自定義dns記錄

根據上面的知識,我們知道我們可以通過配置Corefile檔案來實現

重新對映解析域名

例如重寫規則對映新增foo.example.com到foo.default.svc.cluster.local:

.:53 {
    errors
    log
    health
    rewrite name foo.example.com foo.default.svc.cluster.local
    kubernetes cluster.local 10.0.0.0/24
    proxy . /etc/resolv.conf
    cache 30
}

通過rewrite實現, 這樣在訪問域名foo.example.com就是在訪問域名foo.default.svc.cluster.local

一旦我們將它新增到ConfigMapvia kubectl edit或者kubectl apply,我們必須讓CoreDNS知道它Corefile 已經改變了。您可以傳送它SIGUSR1以告訴它重新載入優雅 - 也就是說,不會丟失服務:

$ kubectl exec -n kube-system coredns-980047985-g2748 -- kill -SIGUSR1 1

增加解析檔案

我們可以增加自定義的域名解析檔案
我們需要修改coredns.yaml我們用於在pod中建立其他檔案的方法。要做到這一點,我們必須ConfigMap通過為區域檔案新增file一行Corefile,並example.db為區域檔案新增另一個鍵來編輯:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        log
        health
        rewrite name foo.example.com foo.default.svc.cluster.local
        kubernetes cluster.local 10.0.0.0/24
        file /etc/coredns/example.db example.org
        proxy . /etc/resolv.conf
        cache 30
    }
  example.db: |
    ; example.org test file
    example.org.            IN      SOA     sns.dns.icann.org. noc.dns.icann.org. 2015082541 7200 3600 1209600 3600
    example.org.            IN      NS      b.iana-servers.net.
    example.org.            IN      NS      a.iana-servers.net.
    example.org.            IN      A       127.0.0.1
    a.b.c.w.example.org.    IN      TXT     "Not a wildcard"
    cname.example.org.      IN      CNAME   www.example.net.

    service.example.org.    IN      SRV     8080 10 10 example.org.

我們還需要編輯模板規範的volumes部分Pod:

      volumes:
        - name: config-volume
          configMap:
            name: coredns
            items:
            - key: Corefile
              path: Corefile
            - key: example.db
              path: example.db

一旦我們使用了這個kubectl apply -f,就會構建一個新的CoreDNS pod,因為卷中有新檔案。以後對檔案的更改不需要新的pod,只需像我們之前那樣優雅地重啟。

讓我們來看看:

$ kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools
If you don't see a command prompt, try pressing enter.
/ # host foo
foo.default.svc.cluster.local has address 10.0.0.72
/ # host foo.example.com
foo.example.com has address 10.0.0.72
/ # host example.org
example.org has address 127.0.0.1
/ #

解析pod的ip

ingress代理