作者 主題: 使用k8s externel-dns自動更新BIND DNS  (閱讀 2524 次)

0 會員 與 1 訪客 正在閱讀本文。

netman

  • 管理員
  • 俺是博士!
  • *****
  • 文章數: 17484
    • 檢視個人資料
    • http://www.study-area.org
使用k8s externel-dns自動更新BIND DNS
« 於: 2020-02-24 21:06 »
使用k8s externel-dns自動更新BIND DNS

2020.02

一,需求背景

在 Kubernetes 的管理中,服務名稱的解釋在 cluster 內幾乎不需要操心就能直接使用。然而,很多時候,我們建置好的服務是提供給 cluster 之外的用戶端作存取的,一般來說用戶端都以 DNS 名稱來存取服務。倘若每一筆服務都要人工方式更新 DNS 紀錄的話,這顯然不能滿足當今的營運需求。

為此,我們可以從 https://github.com/kubernetes-sigs/external-dns 取得 External DNS for Kubernetes 這一工具幫我自動完成 DNS 的更新。

二,External DSN 簡介

   External DNS 可以將 K8S 中的 Service 與 Ingress 這兩種資源的名稱解釋同步到外部的 DNS 伺服器。也就是說,當資源產生的時候自動新增 hostname 與 IP 的記錄,並在資源刪除的時候一併移除 DNS 記錄。

External DNS 支援的外部 DNS 非常多,幾乎網際網路上各家提供動態 DNS 更新服務的服務商都在其支援之列,例如:
  • AWS Route 53
  • Google Cloud DNS
  • AzureDNS
  • CloudFlare
  • Dyn
  • ….

早期的 external-dns 版本並不支援 BIND,好消息是最新的版本提供了一個 RFC2136 的 provider 讓我們用 dns-key 來更新 BIND DNS 伺服器了!(Microsoft DNS也是支援的,但目前沒有提供連線加密的方式)

三,實作環境

本文章將示範如何利用 External DNS 將 K8S 的服務資源名稱同步到不在 cluster 中的 Linux BIND 伺服器:
  • Linux: CentOS Linux release 7.6.1810 (Core)
  • BIND: bind-9.9.4-74.el7_6.1.x86_64
  • IP: 192.168.100.1

K8S Cluster則是使用 kubeadm建置完成:
  • Kubernetes: v1.16.2

四,設定 BIND 的 dns update 功能

4.1 產生 dns key:
代碼: [選擇]
cd /var/named/dynamic
dnssec-keygen -a hmac-sha256 -b 128 -n HOST externaldns-key
chown named.named Kexternaldns-key.*
cat Kexternaldns-key.*.key | awk '{print $NF}'
確定輸出類似 y+gUcHxLWqzg3JcBU2bbgw== 的結果,並複製結果。

4.2 修改 named.conf,末尾處增加如下內容:
代碼: [選擇]
key "externaldns-key" {
    algorithm hmac-sha256;
    secret "y+gUcHxLWqzg3JcBU2bbgw==";
};
zone "k8s.example.org" {
    type master;
    file "/var/named/dynamic/named.k8s.example.org";
    allow-transfer {
        key "externaldns-key";
    };
    update-policy {
        grant externaldns-key zonesub ANY;
    };
};
注意:secret 內容請用複製的key貼上。

4.3 建立 zone file (/var/named/dynamic/named.k8s.example.org)內容:
代碼: [選擇]
$TTL 60 ; 1 minute
@         IN SOA  k8s.example.org. root.k8s.example.org. (
                                16         ; serial
                                60         ; refresh (1 minute)
                                60         ; retry (1 minute)
                                60         ; expire (1 minute)
                                60         ; minimum (1 minute)
                                )
                        NS      ns.k8s.example.org.
ns                      A       192.168.100.1
注意:BIND server 的 IP 請修改實際 IP。

4.4 重新啟動 named 服務:
代碼: [選擇]
systemctl restart named
systemctl status -l named
確定沒有 error,並且 k8s.example.org 的 serial 是正確的。

五,建置 metallb Load Balancer (Optional)
# 如果 K8S Cluster 已經有可用的 Load Balancer 可略過此一步驟。

5.1 下載 metallb yaml 並套用:
代碼: [選擇]
wget https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/metallb.yaml
sed -i '/^apiVersion: apps/s/beta2//' metallb.yaml
kubectl apply -f metallb.yaml
因為我們這裡的 k8s 版本已經升級到 v1.16, 因此需要調整 api 的版本。若您的環境是 v1.15 或之前的版本, 請略過 sed 那行指令不要執行。

5.2 建立 metallb configmap (metallb_configmap.yaml),其內容如下:
代碼: [選擇]
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: my-ip-space
      protocol: layer2
      addresses:
      - 192.168.100.240-192.168.100.249
請將 ip range 修改爲實際的網段,這是分配給 k8s service 資源用的 IP。完成後套用即可:
kubectl apply -f metallb_configmap.yaml
如果沒有 error 就表示就緒了。

六,佈署 external-dns

6.1 撰寫yaml (external-dns.yaml),其內容如下:
代碼: [選擇]
apiVersion: v1
kind: Namespace
metadata:
  name: external-dns
  labels:
    name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: external-dns
  namespace: external-dns
rules:
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - get
  - watch
  - list
- apiGroups:
  - extensions
  resources:
  - ingresses
  verbs:
  - get
  - list
  - watch
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
  namespace: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: external-dns-viewer
  namespace: external-dns
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
- kind: ServiceAccount
  name: external-dns
  namespace: external-dns
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  namespace: external-dns
spec:
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: registry.opensource.zalan.do/teapot/external-dns:v0.5.17
        args:
        - --provider=rfc2136
        - --registry=txt
        - --txt-owner-id=k8s
        - --source=service
        - --source=ingress
        - --domain-filter=k8s.example.org
        - --rfc2136-host=192.168.100.1
        - --rfc2136-port=53
        - --rfc2136-zone=k8s.example.org
        - --rfc2136-tsig-secret=y+gUcHxLWqzg3JcBU2bbgw==
        - --rfc2136-tsig-secret-alg=hmac-sha256
        - --rfc2136-tsig-keyname=externaldns-key
        - --rfc2136-tsig-axfr
        #- --interval=10s
        #- --log-level=debug
最後兩行是方便 debug 時用的,需要的時候才移除掉 # 註解符號。設定中比較關鍵的是 dns server 與 key 的正確性,請特別留意。

6.2 佈署服務:
代碼: [選擇]
kubectl apply -f external-dns.yaml如果沒有 error 就表示就緒了。

七,建置測試服務 (nginx)

7.1 撰寫服務 yaml (nginx.yaml),其內容如下:
代碼: [選擇]
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  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-svc
  annotations:
    external-dns.alpha.kubernetes.io/hostname: nginx.k8s.example.org
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  sessionAffinity: None
  type: LoadBalancer
External-dns 最關鍵的部分就是要從 service 的 annotations 取得 hostname,然後再抓取 Load Balancer 分配的 IP 進行 dns update。

7.2 佈署服務:
代碼: [選擇]
kubectl apply -f nginx.yaml如果沒有 error 就表示就緒了。

7.3 觀察結果:
代碼: [選擇]
kubectl -n external-dns logs external-dns-5d986694c9-5n9wm請注意實際運行的 pod 名稱或許不太一樣,請調整。如果一切順利,從輸出結果中可以看到類似如下的內容:
代碼: [選擇]
time="2019-12-04T16:26:44Z" level=info msg="Created Kubernetes client https://10.96.0.1:443"
time="2019-12-04T16:26:49Z" level=info msg="Configured RFC2136 with zone 'k8s.example.org.' and nameserver '192.168.100.1:53'"
time="2019-12-04T16:26:49Z" level=info msg="Adding RR: nginx.k8s.example.org 0 A 192.168.100.245"
time="2019-12-04T16:26:49Z" level=info msg="Adding RR: nginx.k8s.example.org 0 TXT \"heritage=external-dns,external-dns/owner=k8s,external-dns/resource=service/default/nginx-svc\""

另外, 從 BIND 伺服器那邊, 也可以用 systemctl status -l named 觀察更新的動作,當然也可以用 dig 或 host 命令來實際檢查 dns 的查詢結果。假如名稱解釋一如預期,那就用瀏覽器連線服務的 hostname 來作最後的確認。

八,其他

前面我們是用 service 資源來作示範。假如 k8s 環境中已經建置好 ingress-controller 的話,那我們也可以透過建置 ingress 來同步 dns 的更新。我們只需要設定 ingress 資源並進行套用即可:
代碼: [選擇]
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
    name: nginx-ingress
spec:
    rules:
    - host: ingress.k8s.example.org
      http:
          paths:
          - path: /
            backend:
                serviceName: nginx-svc
                servicePort: 80
請留意使用 ingress 比 service 會稍有不同:
  • 不需要設定 annotations 來指定 hostname
  • IP 一律共用 ingress 服務的位址

假如我們要將服務資源停掉的話,也可以透過前述的檢查動作觀察到 dns 名稱也會自動地被刪除:
代碼: [選擇]
12月 05 00:43:49 srv1.localdomain named[6533]: client 192.168.100.56#58519/key externaldns-key: updating zone 'k8s.example.org/IN': deleting rrset at 'nginx.k8s.example.org' A
12月 05 00:43:49 srv1.localdomain named[6533]: zone k8s.example.org/IN: sending notifies (serial 21)
12月 05 00:43:49 srv1.localdomain named[6533]: client 192.168.100.56#45520/key externaldns-key: updating zone 'k8s.example.org/IN': deleting rrset at 'ingress.k8s.example.org' A
12月 05 00:43:49 srv1.localdomain named[6533]: client 192.168.100.56#49731/key externaldns-key: updating zone 'k8s.example.org/IN': deleting rrset at 'nginx.k8s.example.org' TXT
12月 05 00:43:49 srv1.localdomain named[6533]: client 192.168.100.56#56605/key externaldns-key: updating zone 'k8s.example.org/IN': deleting rrset at 'ingress.k8s.example.org' TXT

九,結語

好了,到這裡我們已經體驗到 external-dns 帶來的便利性。希望能夠對大家在 k8s 應用上有所幫助。

--- End ---