LoginSignup
12
4

More than 3 years have passed since last update.

Traefik 2とExternalDNSでDNSレコードを自動作成する方法

Last updated at Posted at 2019-12-24

こんにちは、(株)日立製作所 研究開発グループ サービスコンピューティング研究部の中村です。

kubernetesでWebサービスを公開する場合、L7 Load Balancerとして動作するIngressを利用している方も多いと思います。そして、Webサービスを外部に公開するためにDNSレコードの自動作成をExternalDNSで管理している方も多いと思います。今回は、IngressコントローラとしてTraefik 2とExternalDNSを連携させて、DNSレコードを自動作成する方法について紹介したいと思います。

そもそも、Ingressコントローラは、kubernetesプロジェクトとして標準サポートしているnginx以外にも、以下のようなものがIngressコントローラがあります。

Traefikは、それ単体でLet's encrypt(ACME)をサポートし証明書更新が自動化される点と、ユーザ認証によるアクセス制限されていないWebアプリをOAuth2.0認証できるようするForward Auth機能を備えている点が他よりも使いやすいと感じており、個人的にはよく利用しています。

また、Traefikは、2019年9月にv2.0が正式にリリースされ、v1系と比較すると、かなり機能が変わりました。
その中でも大きな改善項目は、v1系ではL7 Load Balancerとしてしか動作しなかったのが、v2.0からL4 Load Balancerとして動作するようになった点です。これにより、HTTP(80)/HTTPS(443)のポートだけではなく、それ以外のポートであるSSHやDatabaseをTraefik経由で公開できたり、HTTPS Terminateさせずにbackendにforwardさせるようにできるようになっています。

動作環境

以下の環境を前提に説明を進めます。

種別 コンポーネント Version
OS MacOS Catalina(10.15.1)
OSS Kubernetes 1.16.3
OSS kubectl 1.16.3
OSS ExternalDNS 0.8.3
OSS Traefik 2.1.1

KubernetesクラスタとExternalDNSのセットアップ

こちら([Mac] ExternalDNS+ローカルCoreDNS/etcdでkind上のServiceを名前解決してみる - Qiita)を参考にKubernetessクラスタとExternalDNSをセットアップしてください。

Traefikのセットアップ

Namespace "kube-system"にTraefikをIngress Controllerとしてデプロイします。以下のようなmanifestファイルを作成し、kubectlでデプロイします。

traefik.yaml
    ---
    kind: ServiceAccount
    apiVersion: v1
    metadata:
      name: traefik-ingress-controller
      namespace: kube-system
    ---
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: traefik
    rules:
      - apiGroups: [""]
        resources: ["pods", "services", "endpoints", "secrets"]
        verbs: ["get", "list", "watch"]
      - apiGroups: ["extensions"]
        resources: ["ingresses"]
        verbs: ["get", "list", "watch"]
      - apiGroups: ["extensions"]
        resources: ["ingresses/status"]
        verbs: ["update"]
      - apiGroups: ["traefik.containo.us"]
        resources:
          - ingressroutes
          - ingressroutetcps
          - middlewares
          - tlsoptions
          - traefikservices
        verbs: ["get", "list", "watch"]
    ---
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: traefik
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: traefik
    subjects:
      - kind: ServiceAccount
        name: traefik-ingress-controller
        namespace: kube-system
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: traefik-ingress-controller
      namespace: kube-system
      labels:
        app: traefik
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: traefik
      strategy:
        type: RollingUpdate
        rollingUpdate:
          maxUnavailable: 1
          maxSurge: 1
      template:
        metadata:
          labels:
            app: traefik
        spec:
          serviceAccountName: traefik-ingress-controller
          terminationGracePeriodSeconds: 60
          containers:
            - image: traefik:2.1.1
              name: traefik-ingress-controller
              ports:
                - name: traefik
                  containerPort: 8000
                  protocol: TCP
                - name: web
                  containerPort: 80
                  protocol: TCP
                - name: websecure
                  containerPort: 443
                  protocol: TCP
              args:
                - --entryPoints.traefik.address=:8000
                - --entryPoints.web.address=:80
                - --entryPoints.websecure.address=:443
                - --api.dashboard=true
                - --ping=true
                - --providers.kubernetescrd
                - --providers.kubernetesingress
                - --providers.kubernetesingress.ingressendpoint.publishedservice=kube-system/traefik-ingeress-svc
                - --log.level=debug
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: traefik-ingeress-svc
      namespace: kube-system
      labels:
        app: traefik
    spec:
      type: LoadBalancer
      selector:
        app: traefik
      ports:
        - name: traefik
          port: 8000
          targetPort: traefik
        - name: web
          port: 80
          targetPort: web
        - name: websecure
          port: 443
          targetPort: websecure

TraefikのPodが起動したか確認してみましょう。

$ kubectl -n kube-system get pods -l app=traefik
NAME                                          READY   STATUS    RESTARTS   AGE
traefik-ingress-controller-64cc9449cb-kgwjl   1/1     Running   0          2m23s

もし、STATUSがRunningとなっていなければ、Runningになるまで待ってください。
また、TraefikのServiceがLoadBalancerとしてデプロイされ、External IPが割り当てられているか確認してみましょう。

$ kubectl -n kube-system get services
NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)                                     AGE
kube-dns               ClusterIP      10.96.0.10       <none>         53/UDP,53/TCP,9153/TCP                      2d5h
traefik-ingeress-svc   LoadBalancer   10.107.102.194   172.17.255.1   8000:30403/TCP,80:32367/TCP,443:30214/TCP   8s

次に、Custom Resource Definition(CRD)を利用するので、下記のようなCRDを定義したmanifestファイルを作成して、こちらも忘れずにデプロイします。

traefik-crd.yaml
    ---
    apiVersion: apiextensions.k8s.io/v1beta1
    kind: CustomResourceDefinition
    metadata:
      name: ingressroutes.traefik.containo.us
    spec:
      group: traefik.containo.us
      version: v1alpha1
      names:
        kind: IngressRoute
        plural: ingressroutes
        singular: ingressroute
      scope: Namespaced
    ---
    apiVersion: apiextensions.k8s.io/v1beta1
    kind: CustomResourceDefinition
    metadata:
      name: ingressroutetcps.traefik.containo.us
    spec:
      group: traefik.containo.us
      version: v1alpha1
      names:
        kind: IngressRouteTCP
        plural: ingressroutetcps
        singular: ingressroutetcp
      scope: Namespaced
    ---
    apiVersion: apiextensions.k8s.io/v1beta1
    kind: CustomResourceDefinition
    metadata:
      name: middlewares.traefik.containo.us
    spec:
      group: traefik.containo.us
      version: v1alpha1
      names:
        kind: Middleware
        plural: middlewares
        singular: middleware
      scope: Namespaced
    ---
    apiVersion: apiextensions.k8s.io/v1beta1
    kind: CustomResourceDefinition
    metadata:
      name: tlsoptions.traefik.containo.us
    spec:
      group: traefik.containo.us
      version: v1alpha1
      names:
        kind: TLSOption
        plural: tlsoptions
        singular: tlsoption
      scope: Namespaced
    ---
    apiVersion: apiextensions.k8s.io/v1beta1
    kind: CustomResourceDefinition
    metadata:
      name: traefikservices.traefik.containo.us
    spec:
      group: traefik.containo.us
    version: v1alpha1
      names:
        kind: TraefikService
        plural: traefikservices
        singular: traefikservice
      scope: Namespaced

TraefikでのEndpoint公開方法

サービスを外部に公開する際、Traefik 2では以下2通りの方法を提供しています。

  1. Kubernetes標準のIngressリソースで公開する
  2. Traefik独自CRD(IngressRoute/IngressTCPRoute)リソースで公開する

以下、具体例を元に詳細について紹介したいと思います。

1. Ingressリソースで公開する

単純なWebサービスをDeploymentとしてデプロイして、Ingressリソースで外部からアクセスできるようにしてみたいと思います。
今回は、Webサービスとして、paulbouwer/hello-kubernetesというDockerイメージを利用しました。
以下のようなmanifestファイルを作成し、kubectlでデプロイします。

helloworld-ingress.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: helloworld-ingress
    spec:
      selector:
        matchLabels:
          app: helloworld-ingress
      template:
        metadata:
          labels:
            app: helloworld-ingress
        spec:
          containers:
            - name: helloworld-ingress
              image: paulbouwer/hello-kubernetes:1.5
              ports:
                - name: http
                  containerPort: 8080
              env:
                - name: MESSAGE
                  value: This is defined by an Ingress resource!
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: helloworld-ingress-svc
    spec:
      ports:
        - name: http
          port: 80
          protocol: TCP
          targetPort: http
      selector:
        app: helloworld-ingress
    ---
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    metadata:
      name: helloworld-ingress
      annotations:
        kubernetes.io/ingress.class: "traefik"
    spec:
      rules:
        - host: helloworld-ingress.example.com
          http:
            paths:
              - path: /
                backend:
                  serviceName: helloworld-ingress-svc
                  servicePort: 80

TraefikをIngress Controllerとして、Kubernetes標準のIngressリソースを定義するには、annotationにkubernetes.io/ingress.class: "traefik"を忘れずに追加してください。

動作確認

Ingressリソースが作成で来ているか確認してみます。

$ kubectl get ingress helloworld-ingress
NAME                 HOSTS                            ADDRESS        PORTS   AGE
helloworld-ingress   helloworld-ingress.example.com   172.17.255.1   80      28s

Ingressリソースが作成され、TraefikのServiceにアサインされたExternal IPと同じアドレスがADDRESSに割り当てられています。
次に、ExternalDNSのログを確認してみます。

$ kubectl -n kube-system logs external-dns-56c79c6b69-ptzsr
〜〜〜
time="2019-12-20T00:17:20Z" level=debug msg="No endpoints could be generated from service default/kubernetes"
time="2019-12-20T00:17:20Z" level=debug msg="No endpoints could be generated from service kube-system/kube-dns"
time="2019-12-20T00:17:20Z" level=debug msg="No endpoints could be generated from service kube-system/traefik-ingeress-svc"
time="2019-12-20T00:17:20Z" level=debug msg="No endpoints could be generated from service default/helloworld-ingress-svc"
time="2019-12-20T00:17:20Z" level=debug msg="Endpoints generated from ingress: default/helloworld-ingress: [helloworld-ingress.example.com 0 IN A 172.17.255.1 []]"
time="2019-12-20T00:17:20Z" level=info msg="Add/set key /skydns/com/example/helloworld-ingress/30e3a08a to Host=172.17.255.1, Text=\"heritage=external-dns,external-dns/owner=default,external-dns/resource=ingress/default/helloworld-ingress\", TTL=0"
〜〜〜

Ingressリソースを検出し、DNSレコードが登録されたログが確認できました。
それでは、実際にアクセスしてみます。ブラウザを開き、アドレスバーに"http://helloworld-ingress.example.com"と入力してみます。

helloworld-ingress.png

無事にアクセスできました。

2. IngressRouteで公開する

次に、Traefik 2.0より追加されたCustom Resource Definition(CRD)であるIngressRouteを使って公開してみたいと思います。
先程のIngressで公開した時と同じWebサービスをDeploymentでデプロイし、TraefikのCRDであるIngressRouteを使って定義してみます。
具体的には、以下のようなmanifestファイルを作成し、デプロイします。

helloworld-ingressroute.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: helloworld-ingressroute-crd
    spec:
    selector:
      matchLabels:
        app: helloworld-ingressroute-crd
    template:
      metadata:
        labels:
            app: helloworld-ingressroute-crd
      spec:
        containers:
          - name: helloworld-ingressroute-crd
            image: paulbouwer/hello-kubernetes:1.5
            ports:
              - name: http
                containerPort: 8080
            env:
              - name: MESSAGE
                value: This is defined by an IngressRoute resource!
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: helloworld-ingressroute-crd-svc
    spec:
      ports:
        - name: http
          port: 80
          protocol: TCP
          targetPort: http
      selector:
        app: helloworld-ingressroute-crd
    ---
    apiVersion: traefik.containo.us/v1alpha1
    kind: IngressRoute
    metadata:
      name: helloworld-ingressroute-crd
    spec:
      entryPoints:
        - web
      routes:
        - match: Host(`helloworld-ingressroute.example.com`)
          kind: Rule
          services:
            - name: helloworld-ingressroute-crd-svc
              port: 80

実際にIngressRouteリソースが作成されたか確認してみます。

$ kubectl describe ingressroute helloworld-ingressroute-crd
Name:         helloworld-ingressroute-crd
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"traefik.containo.us/v1alpha1","kind":"IngressRoute","metadata":{"annotations":{},"name":"helloworld-ingressroute-crd","name...
API Version:  traefik.containo.us/v1alpha1
Kind:         IngressRoute
Metadata:
Creation Timestamp:  2019-12-20T00:43:22Z
Generation:          1
Resource Version:    108873
Self Link:           /apis/traefik.containo.us/v1alpha1/namespaces/default/ingressroutes/helloworld-ingressroute-crd
UID:                 ad90f795-0dc5-453d-af6c-27340737cbb1
Spec:
Entry Points:
    web
Routes:
    Kind:   Rule
    Match:  Host(`helloworld-ingressroute.example.com`)
    Services:
    Name:  helloworld-ingressroute-crd-svc
    Port:  80
Events:      <none>

正常に作成できているようです。
それでは、ExternalDNSのログをみてみましょう。

$ kubectl -n kube-system logs external-dns-56c79c6b69-ptzsr
〜〜〜
time="2019-12-20T00:39:19Z" level=debug msg="No endpoints could be generated from service default/kubernetes"
time="2019-12-20T00:39:19Z" level=debug msg="No endpoints could be generated from service kube-system/kube-dns"
time="2019-12-20T00:39:19Z" level=debug msg="No endpoints could be generated from service kube-system/traefik-ingeress-svc"
time="2019-12-20T00:39:19Z" level=debug msg="No endpoints could be generated from service default/helloworld-ingress-svc"
time="2019-12-20T00:39:19Z" level=debug msg="Endpoints generated from ingress: default/helloworld-ingress: [helloworld-ingress.example.com 0 IN A 172.17.255.1 []]"
time="2019-12-20T00:39:32Z" level=debug msg="pod added"
time="2019-12-20T00:39:32Z" level=debug msg="service added"
time="2019-12-20T00:40:19Z" level=debug msg="No endpoints could be generated from service default/kubernetes"
time="2019-12-20T00:40:19Z" level=debug msg="No endpoints could be generated from service kube-system/kube-dns"
time="2019-12-20T00:40:19Z" level=debug msg="No endpoints could be generated from service kube-system/traefik-ingeress-svc"
time="2019-12-20T00:40:19Z" level=debug msg="No endpoints could be generated from service default/helloworld-ingress-svc"
time="2019-12-20T00:40:19Z" level=debug msg="No endpoints could be generated from service default/helloworld-ingressroute-crd-svc"
time="2019-12-20T00:40:19Z" level=debug msg="Endpoints generated from ingress: default/helloworld-ingress: [helloworld-ingress.example.com 0 IN A 172.17.255.1 []]"
〜〜〜

Serviceリソースが追加されていることは検知できているようですが、IngressRouteリソースの追加が検知できていないので、DNSレコードの作成がされていないようです。
実際に、curlでアクセスして確認してみましょう。

$ curl http://helloworld-ingressroute.example.com
curl: (6) Could not resolve host: helloworld-ingressroute.example.com

DNSで名前解決できていないようですね。

ExternalDNSがサポートしているリソース

なぜExternalDNSで自動でDNSレコードができなかったのか考えてみたいと思います。
ExternalDNSのSourceに関するドキュメントを確認してみましょう。ExternalDNSでサポートしているのは、以下の通りです。

  • ServiceSource
  • IngressSource
  • IstioGatewaySource
  • ContourIngressRouteSource
  • FakeSource
  • ConnectorSource
  • CRDSource
  • EmptySource

これを見ると、CRDもサポートしているように見えます。どのようなものか詳細をこちらで確認してみましょう。

どうも、CRDとしてサポートしているデータ構造は、特定のもののようです。また、ExternalDNSのIssueで調べてみても、こちらのように、TraefikのIngressRouteはサポートされていないようです。これはTraefikに限らず、その他のCRDを使ってサービス公開を行うIngress Controller(例えば、AmbassadorやVoyager)も同様の問題にぶつかってしまうと思われます。

解決策

現状、どのような解決策があるか整理してみたいと思います。

  1. ExternalDNSのCRDSourceを使ってDNSを定義する
  2. Serviceリソース(ExternalName)を使ってDNSを定義する

1つ目は、ExternalDNSのIssueでも推奨されていましたが、ExternalDNSのDNS定義専用のCRD("DNSEndpoint")を用いる方法です。
2つ目は、ExternalDNSがサポートしているServiceリソースのtypeをExternalNameを用いて、DNSを定義する方法です。
以下、実際に、その手順を紹介したいと思います。

(解決策1) ExternalDNSのCRDSourceを使ってDNSを定義する

ExternalDNSのドキュメントを参考に、進めていきたいと思います。
まずは、CRDを定義した以下の様なmanifestファイルを作成します。

dnsendpoint-crd.yaml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  creationTimestamp: null
  labels:
    api: externaldns
    kubebuilder.k8s.io: 1.0.0
  name: dnsendpoints.externaldns.k8s.io
spec:
  group: externaldns.k8s.io
  names:
    kind: DNSEndpoint
    plural: dnsendpoints
  scope: Namespaced
  subresources:
    status: {}
  validation:
    openAPIV3Schema:
      properties:
        apiVersion:
          type: string
        kind:
          type: string
        metadata:
          type: object
        spec:
          properties:
            endpoints:
              items:
                properties:
                  dnsName:
                    type: string
                  labels:
                    type: object
                  providerSpecific:
                    items:
                      properties:
                        name: 
                          type: string
                        value:
                          type: string
                      type: object
                    type: array
                  recordTTL:
                    format: int64
                    type: integer
                  recordType:
                    type: string
                  targets:
                    items:
                      type: string
                    type: array
                type: object
              type: array
          type: object
        status:
          properties:
            observedGeneration:
              format: int64
              type: integer
          type: object
  version: v1alpha1
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: null

これをkubectlでデプロイします。デプロイする際には、validateが無効になるオプションをつけて実行します。

$ kubectl apply --validate=false -f dnsendpoint-crd.yaml
customresourcedefinition.apiextensions.k8s.io/dnsendpoints.externaldns.k8s.io created

次に、ExternalDNSにRBACを設定している場合は、先程追加したCRDに対するアクセス権を付与する以下のmanifestファイルを作成し、適用します。

update-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: external-dns-crd
rules:
- apiGroups: ["externaldns.k8s.io"]
  resources: ["dnsendpoints"]
  verbs: ["get","watch","list","update"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: external-dns-crd-manager
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns-crd
subjects:
- kind: ServiceAccount
  name: external-dns
  namespace: kube-system

そして、追加したCRDをExternalDNSが検出できるように、ExternalDNSの起動時のオプションを追加します。以下のようなmanifestファイルを作成し、ExternalDNSのDeploymentをUpdateします。

update-extenaldns.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  namespace: kube-system
spec:
  strategy:
    type: Recreate
  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:latest
        args:
        - --source=service
        - --source=ingress
        - --provider=coredns
        - --log-level=debug
        - --policy=sync
        - --domain-filter=example.com
        - --source=crd                                        # <--- 追加
        - --crd-source-apiversion=externaldns.k8s.io/v1alpha1 # <--- 追加
        - --crd-source-kind=DNSEndpoint                       # <--- 追加
        env:
        - name: ETCD_URLS
          value: http://host.docker.internal:2379

最後に、追加したCRD"DNSEndpoint"を使って、DNSを以下のように定義して、kubectlでデプロイします。なお、最後のtargetsには、Ingress ControllerであるTraefikのServiceに割り当てられたExternal IPを指定します。

helloworld-dns.yaml
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
  name: helloworld-ingressroute-dnsrecord
spec:
  endpoints:
  - dnsName: helloworld-ingressroute.example.com
    recordTTL: 180
    recordType: A
    targets:
    - 172.17.255.1

それでは、確認してみましょう。まずは、DNSEndpointリソースが作成されているか確認します。

$ kubectl get dnsendpoint
NAME                                AGE
helloworld-ingressroute-dnsrecord   21s

次に、ExternalDNSのログを確認してみます。

$ kubectl -n kube-system logs external-dns-6f59d484c-nh6zv
〜〜〜
time="2019-12-20T01:54:14Z" level=debug msg="No endpoints could be generated from service default/kubernetes"
time="2019-12-20T01:54:14Z" level=debug msg="No endpoints could be generated from service kube-system/kube-dns"
time="2019-12-20T01:54:14Z" level=debug msg="No endpoints could be generated from service kube-system/traefik-ingeress-svc"
time="2019-12-20T01:54:14Z" level=debug msg="No endpoints could be generated from service default/helloworld-ingress-svc"
time="2019-12-20T01:54:14Z" level=debug msg="No endpoints could be generated from service default/helloworld-ingressroute-crd-svc"
time="2019-12-20T01:54:14Z" level=debug msg="Endpoints generated from ingress: default/helloworld-ingress: [helloworld-ingress.example.com 0 IN A 172.17.255.1 []]"
time="2019-12-20T01:54:14Z" level=warning msg="Could not update ObservedGeneration of the CRD: dnsendpoints.externaldns.k8s.io \"helloworld-ingressroute-dnsrecord\" is forbidden: User \"system:serviceaccount:kube-system:external-dns\" cannot update resource \"dnsendpoints/status\" in API group \"externaldns.k8s.io\" in the namespace \"default\""
time="2019-12-20T01:54:14Z" level=info msg="Add/set key /skydns/com/example/helloworld-ingressroute/089779ea to Host=172.17.255.1, Text=\"heritage=external-dns,external-dns/owner=default,external-dns/resource=crd/default/helloworld-ingressroute-dnsrecord\", TTL=180"
〜〜〜

CRDの情報を元に、DNSレコードが追加されたようです。
早速、ブラウザからアクセスできるか確認してみましょう。

helloworld-ingressroute-crd.png

無事にアクセスできました。
別の解決策を試すために、一旦、追加したDNSEndpointリソースを削除します。

$ kubectl delete dnsendpoint helloworld-ingressroute-dnsrecord
dnsendpoint.externaldns.k8s.io "helloworld-ingressroute-dnsrecord" deleted

1分程度待ってからcurlでアクセスすると、以下のようにDNSの名前解決に失敗すると思います。

$ curl http://helloworld-ingressroute.example.com
curl: (6) Could not resolve host: helloworld-ingressroute.example.com

(解決策2) Serviceリソース(ExternalName)を使ってDNSを定義する

今度は、Kubernetes標準のServiceリソースだけでDNSを定義したいと思います。
まずは、以下のようなServiceリソースを定義します。これは、サービスを公開するときに作成しているServiceとは別に、DNSレコード登録用のServiceリソースになるので、名前が重複しないように気をつけて作成してください。

helloworld-external-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: hellowrold-ingressroute-svc-external
  annotations:
    external-dns.alpha.kubernetes.io/hostname: helloworld-ingressroute.example.com  <-- 3
spec:
  type: ExternalName                     <-- 1
  externalName: 172.17.255.1             <-- 2

ポイントは以下の3点です。

  1. typeをExternalNameと指定
  2. externalNameに、Ingress ControllerであるTraefikのServiceに割り当てられたExternal IPを指定
  3. annotationにDNSとして登録したいhostnameを記載

この作成したmanifestファイルをデプロイすればOKです。

早速、確認してみましょう。
まずは、Serviceリソースが作成されているかを確認します。

$ kubectl get service
NAME                                   TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)   AGE
helloworld-ingress-svc                 ClusterIP      10.98.240.223   <none>         80/TCP    124m
helloworld-ingressroute-crd-svc        ClusterIP      10.103.54.127   <none>         80/TCP    101m
hellowrold-ingressroute-svc-external   ExternalName   <none>          172.17.255.1   <none>    7s
kubernetes                             ClusterIP      10.96.0.1       <none>         443/TCP   2d7h

typeがExternalNameのServiceリソースが作成されているようです。
次に、ExternalDNSのログを確認します。

$ kubectl -n kube-system logs external-dns-6f59d484c-nh6zv
〜〜〜
time="2019-12-20T02:20:13Z" level=debug msg="No endpoints could be generated from service default/kubernetes"
time="2019-12-20T02:20:13Z" level=debug msg="No endpoints could be generated from service kube-system/kube-dns"
time="2019-12-20T02:20:13Z" level=debug msg="No endpoints could be generated from service kube-system/traefik-ingeress-svc"
time="2019-12-20T02:20:13Z" level=debug msg="No endpoints could be generated from service default/helloworld-ingress-svc"
time="2019-12-20T02:20:13Z" level=debug msg="No endpoints could be generated from service default/helloworld-ingressroute-crd-svc"
time="2019-12-20T02:20:13Z" level=debug msg="Endpoints generated from ingress: default/helloworld-ingress: [helloworld-ingress.example.com 0 IN A 172.17.255.1 []]"
time="2019-12-20T02:20:41Z" level=debug msg="service added"
time="2019-12-20T02:21:12Z" level=debug msg="No endpoints could be generated from service default/helloworld-ingressroute-crd-svc"
time="2019-12-20T02:21:12Z" level=debug msg="Endpoints generated from service: default/hellowrold-ingressroute-svc-external: [helloworld-ingressroute.example.com 0 IN A 172.17.255.1 []]"
time="2019-12-20T02:21:12Z" level=debug msg="No endpoints could be generated from service default/kubernetes"
time="2019-12-20T02:21:12Z" level=debug msg="No endpoints could be generated from service kube-system/kube-dns"
time="2019-12-20T02:21:12Z" level=debug msg="No endpoints could be generated from service kube-system/traefik-ingeress-svc"
time="2019-12-20T02:21:12Z" level=debug msg="No endpoints could be generated from service default/helloworld-ingress-svc"
time="2019-12-20T02:21:12Z" level=debug msg="Endpoints generated from ingress: default/helloworld-ingress: [helloworld-ingress.example.com 0 IN A 172.17.255.1 []]"
time="2019-12-20T02:21:12Z" level=info msg="Add/set key /skydns/com/example/helloworld-ingressroute/671dbad1 to Host=172.17.255.1, Text=\"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/hellowrold-ingressroute-svc-external\", TTL=0"
〜〜〜

Serviceの追加が検出され、DNSレコードが登録されたようです。
最後に、ブラウザからアクセスできるか確認してみます。

helloworld-ingressroute-crd2.png

先程と全く同じ画面が表示されることが確認できました。

まとめ

IngressコントローラとしてTraefik 2とExternalDNSを連携させて、DNSレコードを自動作成する方法について紹介しました。Kubernetes標準のIngressリソースの場合は特に問題はなかったのですが、Traefik独自のCRDを用いた場合は、そのままではExternalDNSが追加されたCRDを検出できずDNSレコードの作成がされないという問題があり、その解決方法を2つ紹介しました。

ご紹介した2つの解決方法はどちらも、Ingress ControllerのServiceにアサインされているExternal IPを手作業で埋める必要があるので、運用は煩雑になってしまいます。
この問題は、Traefikのコミュニティでも認識されており、これに関するIssueが登録されています。早く改善されることを望みながら、この動向をWatchしていきたいと思います。

12
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
4