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).