sigs.k8s.io/external-dns@v0.14.1/docs/tutorials/azure-private-dns.md (about)

     1  # Set up ExternalDNS for Azure Private DNS
     2  
     3  This tutorial describes how to set up ExternalDNS for managing records in Azure Private DNS.
     4  
     5  It comprises of the following steps:
     6  1) Provision Azure Private DNS
     7  2) Configure service principal for managing the zone
     8  3) Deploy ExternalDNS
     9  4) Expose an NGINX service with a LoadBalancer and annotate it with the desired DNS name
    10  5) Install NGINX Ingress Controller (Optional)
    11  6) Expose an nginx service with an ingress (Optional)
    12  7) Verify the DNS records
    13  
    14  Everything will be deployed on Kubernetes.
    15  Therefore, please see the subsequent prerequisites.
    16  
    17  ## Prerequisites
    18  - Azure Kubernetes Service is deployed and ready
    19  - [Azure CLI 2.0](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) and `kubectl` installed on the box to execute the subsequent steps
    20  
    21  ## Provision Azure Private DNS
    22  
    23  The provider will find suitable zones for domains it manages. It will
    24  not automatically create zones.
    25  
    26  For this tutorial, we will create a Azure resource group named 'externaldns' that can easily be deleted later.
    27  
    28  ```
    29  $ az group create -n externaldns -l westeurope
    30  ```
    31  
    32  Substitute a more suitable location for the resource group if desired.
    33  
    34  As a prerequisite for Azure Private DNS to resolve records is to define links with VNETs.
    35  Thus, first create a VNET.
    36  
    37  ```
    38  $ az network vnet create \
    39    --name myvnet \
    40    --resource-group externaldns \
    41    --location westeurope \
    42    --address-prefix 10.2.0.0/16 \
    43    --subnet-name mysubnet \
    44    --subnet-prefixes 10.2.0.0/24
    45  ```
    46  
    47  Next, create a Azure Private DNS zone for "example.com":
    48  
    49  ```
    50  $ az network private-dns zone create -g externaldns -n example.com
    51  ```
    52  
    53  Substitute a domain you own for "example.com" if desired.
    54  
    55  Finally, create the mentioned link with the VNET.
    56  
    57  ```
    58  $ az network private-dns link vnet create -g externaldns -n mylink \
    59     -z example.com -v myvnet --registration-enabled false
    60  ```
    61  
    62  ## Configure service principal for managing the zone
    63  ExternalDNS needs permissions to make changes in Azure Private DNS.
    64  These permissions are roles assigned to the service principal used by ExternalDNS.
    65  
    66  A service principal with a minimum access level of `Private DNS Zone Contributor` to the Private DNS zone(s) and `Reader` to the resource group containing the Azure Private DNS zone(s) is necessary.
    67  More powerful role-assignments like `Owner` or assignments on subscription-level work too.
    68  
    69  Start off by **creating the service principal** without role-assignments.
    70  ```
    71  $ az ad sp create-for-rbac --skip-assignment -n http://externaldns-sp
    72  {
    73    "appId": "appId GUID",  <-- aadClientId value
    74    ...
    75    "password": "password",  <-- aadClientSecret value
    76    "tenant": "AzureAD Tenant Id"  <-- tenantId value
    77  }
    78  ```
    79  > Note: Alternatively, you can issue `az account show --query "tenantId"` to retrieve the id of your AAD Tenant too.
    80  
    81  Next, assign the roles to the service principal.
    82  But first **retrieve the ID's** of the objects to assign roles on.
    83  
    84  ```
    85  # find out the resource ids of the resource group where the dns zone is deployed, and the dns zone itself
    86  $ az group show --name externaldns --query id -o tsv
    87  /subscriptions/id/resourceGroups/externaldns
    88  
    89  $ az network private-dns zone show --name example.com -g externaldns --query id -o tsv
    90  /subscriptions/.../resourceGroups/externaldns/providers/Microsoft.Network/privateDnsZones/example.com
    91  ```
    92  Now, **create role assignments**.
    93  ```
    94  # 1. as a reader to the resource group
    95  $ az role assignment create --role "Reader" --assignee <appId GUID> --scope <resource group resource id>
    96  
    97  # 2. as a contributor to DNS Zone itself
    98  $ az role assignment create --role "Private DNS Zone Contributor" --assignee <appId GUID> --scope <dns zone resource id>
    99  ```
   100  
   101  ## Deploy ExternalDNS
   102  Configure `kubectl` to be able to communicate and authenticate with your cluster.
   103  This is per default done through the file `~/.kube/config`.
   104  
   105  For general background information on this see [kubernetes-docs](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/).
   106  Azure-CLI features functionality for automatically maintaining this file for AKS-Clusters. See [Azure-Docs](https://docs.microsoft.com/de-de/cli/azure/aks?view=azure-cli-latest#az-aks-get-credentials).
   107  
   108  Follow the steps for [azure-dns provider](./azure.md#creating-configuration-file) to create a configuration file.
   109  
   110  Then apply one of the following manifests depending on whether you use RBAC or not.
   111  
   112  The credentials of the service principal are provided to ExternalDNS as environment-variables.
   113  
   114  ### Manifest (for clusters without RBAC enabled)
   115  ```yaml
   116  apiVersion: apps/v1
   117  kind: Deployment
   118  metadata:
   119    name: externaldns
   120  spec:
   121    selector:
   122      matchLabels:
   123        app: externaldns
   124    strategy:
   125      type: Recreate
   126    template:
   127      metadata:
   128        labels:
   129          app: externaldns
   130      spec:
   131        containers:
   132        - name: externaldns
   133          image: registry.k8s.io/external-dns/external-dns:v0.14.0
   134          args:
   135          - --source=service
   136          - --source=ingress
   137          - --domain-filter=example.com
   138          - --provider=azure-private-dns
   139          - --azure-resource-group=externaldns
   140          - --azure-subscription-id=<use the id of your subscription>
   141          volumeMounts:
   142          - name: azure-config-file
   143            mountPath: /etc/kubernetes
   144            readOnly: true
   145        volumes:
   146        - name: azure-config-file
   147          secret:
   148            secretName: azure-config-file
   149  ```
   150  
   151  ### Manifest (for clusters with RBAC enabled, cluster access)
   152  ```yaml
   153  apiVersion: v1
   154  kind: ServiceAccount
   155  metadata:
   156    name: externaldns
   157  ---
   158  apiVersion: rbac.authorization.k8s.io/v1
   159  kind: ClusterRole
   160  metadata:
   161    name: externaldns
   162  rules:
   163  - apiGroups: [""]
   164    resources: ["services","endpoints","pods"]
   165    verbs: ["get","watch","list"]
   166  - apiGroups: ["extensions","networking.k8s.io"]
   167    resources: ["ingresses"]
   168    verbs: ["get","watch","list"]
   169  - apiGroups: [""]
   170    resources: ["nodes"]
   171    verbs: ["get", "watch", "list"]
   172  ---
   173  apiVersion: rbac.authorization.k8s.io/v1
   174  kind: ClusterRoleBinding
   175  metadata:
   176    name: externaldns-viewer
   177  roleRef:
   178    apiGroup: rbac.authorization.k8s.io
   179    kind: ClusterRole
   180    name: externaldns
   181  subjects:
   182  - kind: ServiceAccount
   183    name: externaldns
   184    namespace: default
   185  ---
   186  apiVersion: apps/v1
   187  kind: Deployment
   188  metadata:
   189    name: externaldns
   190  spec:
   191    selector:
   192      matchLabels:
   193        app: externaldns
   194    strategy:
   195      type: Recreate
   196    template:
   197      metadata:
   198        labels:
   199          app: externaldns
   200      spec:
   201        serviceAccountName: externaldns
   202        containers:
   203        - name: externaldns
   204          image: registry.k8s.io/external-dns/external-dns:v0.14.0
   205          args:
   206          - --source=service
   207          - --source=ingress
   208          - --domain-filter=example.com
   209          - --provider=azure-private-dns
   210          - --azure-resource-group=externaldns
   211          - --azure-subscription-id=<use the id of your subscription>
   212          volumeMounts:
   213          - name: azure-config-file
   214            mountPath: /etc/kubernetes
   215            readOnly: true
   216        volumes:
   217        - name: azure-config-file
   218          secret:
   219            secretName: azure-config-file
   220  ```
   221  
   222  ### Manifest (for clusters with RBAC enabled, namespace access)
   223  This configuration is the same as above, except it only requires privileges for the current namespace, not for the whole cluster.
   224  However, access to [nodes](https://kubernetes.io/docs/concepts/architecture/nodes/) requires cluster access, so when using this manifest,
   225  services with type `NodePort` will be skipped!
   226  
   227  ```yaml
   228  apiVersion: v1
   229  kind: ServiceAccount
   230  metadata:
   231    name: externaldns
   232  ---
   233  apiVersion: rbac.authorization.k8s.io/v1
   234  kind: Role
   235  metadata:
   236    name: externaldns
   237  rules:
   238  - apiGroups: [""]
   239    resources: ["services","endpoints","pods"]
   240    verbs: ["get","watch","list"]
   241  - apiGroups: ["extensions","networking.k8s.io"]
   242    resources: ["ingresses"]
   243    verbs: ["get","watch","list"]
   244  ---
   245  apiVersion: rbac.authorization.k8s.io/v1
   246  kind: RoleBinding
   247  metadata:
   248    name: externaldns
   249  roleRef:
   250    apiGroup: rbac.authorization.k8s.io
   251    kind: Role
   252    name: externaldns
   253  subjects:
   254  - kind: ServiceAccount
   255    name: externaldns
   256  ---
   257  apiVersion: apps/v1
   258  kind: Deployment
   259  metadata:
   260    name: externaldns
   261  spec:
   262    selector:
   263      matchLabels:
   264        app: externaldns
   265    strategy:
   266      type: Recreate
   267    template:
   268      metadata:
   269        labels:
   270          app: externaldns
   271      spec:
   272        serviceAccountName: externaldns
   273        containers:
   274        - name: externaldns
   275          image: registry.k8s.io/external-dns/external-dns:v0.14.0
   276          args:
   277          - --source=service
   278          - --source=ingress
   279          - --domain-filter=example.com
   280          - --provider=azure-private-dns
   281          - --azure-resource-group=externaldns
   282          - --azure-subscription-id=<use the id of your subscription>
   283          volumeMounts:
   284          - name: azure-config-file
   285            mountPath: /etc/kubernetes
   286            readOnly: true
   287        volumes:
   288        - name: azure-config-file
   289          secret:
   290            secretName: azure-config-file
   291  ```
   292  
   293  Create the deployment for ExternalDNS:
   294  
   295  ```
   296  $ kubectl create -f externaldns.yaml
   297  ```
   298  
   299  ## Create an nginx deployment
   300  
   301  This step creates a demo workload in your cluster. Apply the following manifest to create a deployment that we are going to expose later in this tutorial in multiple ways:
   302  
   303  ```yaml
   304  ---
   305  apiVersion: apps/v1
   306  kind: Deployment
   307  metadata:
   308    name: nginx
   309  spec:
   310    selector:
   311      matchLabels:
   312        app: nginx
   313    template:
   314      metadata:
   315        labels:
   316          app: nginx
   317      spec:
   318        containers:
   319          - image: nginx
   320            name: nginx
   321            ports:
   322            - containerPort: 80
   323  ```
   324  
   325  ## Expose the nginx deployment with a load balancer
   326  
   327  Apply the following manifest to create a service of type `LoadBalancer`. This will create a public load balancer in Azure that will forward traffic to the nginx pods.
   328  
   329  ```yaml
   330  ---
   331  apiVersion: v1
   332  kind: Service
   333  annotations:
   334    service.beta.kubernetes.io/azure-load-balancer-internal: "true"
   335    external-dns.alpha.kubernetes.io/hostname: server.example.com
   336    external-dns.alpha.kubernetes.io/internal-hostname: server-clusterip.example.com
   337  metadata:
   338    name: nginx-svc
   339  spec:
   340    ports:
   341      - port: 80
   342        protocol: TCP
   343        targetPort: 80
   344    selector:
   345      app: nginx
   346    type: LoadBalancer
   347  ```
   348  
   349  In the service we used multiple annptations. The annotation `service.beta.kubernetes.io/azure-load-balancer-internal` is used to create an internal load balancer. The annotation `external-dns.alpha.kubernetes.io/hostname` is used to create a DNS record for the load balancer that will point to the internal IP address in the VNET allocated by the internal load balancer. The annotation `external-dns.alpha.kubernetes.io/internal-hostname` is used to create a private DNS record for the load balancer that will point to the cluster IP.
   350  
   351  ## Install NGINX Ingress Controller (Optional)
   352  
   353  Helm is used to deploy the ingress controller.
   354  
   355  We employ the popular chart [ingress-nginx](https://github.com/kubernetes/ingress-nginx/tree/main/charts/ingress-nginx).
   356  
   357  ```
   358  $ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
   359  $ helm repo update
   360  $ helm install [RELEASE_NAME] ingress-nginx/ingress-nginx
   361       --set controller.publishService.enabled=true
   362  ```
   363  
   364  The parameter `controller.publishService.enabled` needs to be set to `true.`
   365  
   366  It will make the ingress controller update the endpoint records of ingress-resources to contain the external-ip of the loadbalancer serving the ingress-controller.
   367  This is crucial as ExternalDNS reads those endpoints records when creating DNS-Records from ingress-resources.
   368  In the subsequent parameter we will make use of this. If you don't want to work with ingress-resources in your later use, you can leave the parameter out.
   369  
   370  Verify the correct propagation of the loadbalancer's ip by listing the ingresses.
   371  
   372  ```
   373  $ kubectl get ingress
   374  ```
   375  
   376  The address column should contain the ip for each ingress. ExternalDNS will pick up exactly this piece of information.
   377  
   378  ```
   379  NAME     HOSTS             ADDRESS          PORTS   AGE
   380  nginx1   sample1.aks.com   52.167.195.110   80      6d22h
   381  nginx2   sample2.aks.com   52.167.195.110   80      6d21h
   382  ```
   383  
   384  If you do not want to deploy the ingress controller with Helm, ensure to pass the following cmdline-flags to it through the mechanism of your choice:
   385  
   386  ```
   387  flags:
   388  --publish-service=<namespace of ingress-controller >/<svcname of ingress-controller>
   389  --update-status=true (default-value)
   390  
   391  example:
   392  ./nginx-ingress-controller --publish-service=default/nginx-ingress-controller
   393  ```
   394  
   395  ## Expose the nginx deployment with the ingress (Optional)
   396  
   397  Apply the following manifest to create an ingress resource that will expose the nginx deployment. The ingress resource backend points to a `ClusterIP` service that is needed to select the pods that will receive the traffic.
   398  
   399  ```yaml
   400  ---
   401  apiVersion: v1
   402  kind: Service
   403  metadata:
   404    name: nginx-svc-clusterip
   405  spec:
   406    ports:
   407    - port: 80
   408      protocol: TCP
   409      targetPort: 80
   410    selector:
   411      app: nginx
   412    type: ClusterIP
   413  
   414  ---
   415  apiVersion: networking.k8s.io/v1
   416  kind: Ingress
   417  metadata:
   418    name: nginx
   419  spec:
   420    ingressClassName: nginx
   421    rules:
   422    - host: server.example.com
   423      http:
   424        paths:
   425        - backend:
   426            service:
   427              name: nginx-svc-clusterip
   428              port:
   429                number: 80
   430          pathType: Prefix
   431  ```
   432  
   433  When using ExternalDNS with ingress objects it will automatically create DNS records based on host names specified in ingress objects that match the domain-filter argument in the externaldns deployment manifest. When those host names are removed or renamed the corresponding DNS records are also altered.
   434  
   435  Create the deployment, service and ingress object:
   436  
   437  ```
   438  $ kubectl create -f nginx.yaml
   439  ```
   440  
   441  Since your external IP would have already been assigned to the nginx-ingress service, the DNS records pointing to the IP of the nginx-ingress service should be created within a minute.
   442  
   443  ## Verify created records
   444  
   445  Run the following command to view the A records for your Azure Private DNS zone:
   446  
   447  ```
   448  $ az network private-dns record-set a list -g externaldns -z example.com
   449  ```
   450  
   451  Substitute the zone for the one created above if a different domain was used.
   452  
   453  This should show the external IP address of the service as the A record for your domain ('@' indicates the record is for the zone itself).