sigs.k8s.io/external-dns@v0.14.1/docs/tutorials/azure.md (about) 1 2 # Setting up ExternalDNS for Services on Azure 3 4 This tutorial describes how to setup ExternalDNS for [Azure DNS](https://azure.microsoft.com/services/dns/) with [Azure Kubernetes Service](https://docs.microsoft.com/azure/aks/). 5 6 Make sure to use **>=0.11.0** version of ExternalDNS for this tutorial. 7 8 This tutorial uses [Azure CLI 2.0](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) for all 9 Azure commands and assumes that the Kubernetes cluster was created via Azure Container Services and `kubectl` commands 10 are being run on an orchestration node. 11 12 ## Creating an Azure DNS zone 13 14 The Azure provider for ExternalDNS will find suitable zones for domains it manages; it will not automatically create zones. 15 16 For this tutorial, we will create a Azure resource group named `MyDnsResourceGroup` that can easily be deleted later: 17 18 ```bash 19 $ az group create --name "MyDnsResourceGroup" --location "eastus" 20 ``` 21 22 Substitute a more suitable location for the resource group if desired. 23 24 Next, create a Azure DNS zone for `example.com`: 25 26 ```bash 27 $ az network dns zone create --resource-group "MyDnsResourceGroup" --name "example.com" 28 ``` 29 30 Substitute a domain you own for `example.com` if desired. 31 32 If using your own domain that was registered with a third-party domain registrar, you should point your domain's name servers to the values in the `nameServers` field from the JSON data returned by the `az network dns zone create` command. Please consult your registrar's documentation on how to do that. 33 34 ### Internal Load Balancer 35 36 To create internal load balancers, one can set the annotation `service.beta.kubernetes.io/azure-load-balancer-internal` to `true` on the resource. 37 **Note**: AKS cluster's control plane managed identity needs to be granted `Network Contributor` role to update the subnet. For more details refer to [Use an internal load balancer with Azure Kubernetes Service (AKS)](https://learn.microsoft.com/en-us/azure/aks/internal-lb) 38 39 ## Configuration file 40 41 The azure provider will reference a configuration file called `azure.json`. The preferred way to inject the configuration file is by using a Kubernetes secret. The secret should contain an object named `azure.json` with content similar to this: 42 43 ```json 44 { 45 "tenantId": "01234abc-de56-ff78-abc1-234567890def", 46 "subscriptionId": "01234abc-de56-ff78-abc1-234567890def", 47 "resourceGroup": "MyDnsResourceGroup", 48 "aadClientId": "01234abc-de56-ff78-abc1-234567890def", 49 "aadClientSecret": "uKiuXeiwui4jo9quae9o" 50 } 51 ``` 52 53 The following fields are used: 54 55 * `tenantId` (**required**) - run `az account show --query "tenantId"` or by selecting Azure Active Directory in the Azure Portal and checking the _Directory ID_ under Properties. 56 * `subscriptionId` (**required**) - run `az account show --query "id"` or by selecting Subscriptions in the Azure Portal. 57 * `resourceGroup` (**required**) is the Resource Group created in a previous step that contains the Azure DNS Zone. 58 * `aadClientID` and `aadClientSecret` are associated with the Service Principal. This is only used with Service Principal method documented in the next section. 59 * `useManagedIdentityExtension` - this is set to `true` if you use either AKS Kubelet Identity or AAD Pod Identities methods documented in the next section. 60 * `userAssignedIdentityID` - this contains the client id from the Managed identitty when using the AAD Pod Identities method documented in the next setion. 61 * `useWorkloadIdentityExtension` - this is set to `true` if you use Workload Identity method documented in the next section. 62 63 The Azure DNS provider expects, by default, that the configuration file is at `/etc/kubernetes/azure.json`. This can be overridden with the `--azure-config-file` option when starting ExternalDNS. 64 65 ## Permissions to modify DNS zone 66 67 ExternalDNS needs permissions to make changes to the Azure DNS zone. There are four ways configure the access needed: 68 69 - [Service Principal](#service-principal) 70 - [Managed Identity Using AKS Kubelet Identity](#managed-identity-using-aks-kubelet-identity) 71 - [Managed Identity Using AAD Pod Identities](#managed-identity-using-aad-pod-identities) 72 - [Managed Identity Using Workload Identity](#managed-identity-using-workload-identity) 73 74 ### Service Principal 75 76 These permissions are defined in a Service Principal that should be made available to ExternalDNS as a configuration file `azure.json`. 77 78 #### Creating a service principal 79 80 A Service Principal with a minimum access level of `DNS Zone Contributor` or `Contributor` to the DNS zone(s) and `Reader` to the resource group containing the Azure DNS zone(s) is necessary for ExternalDNS to be able to edit DNS records. However, other more permissive access levels will work too (e.g. `Contributor` to the resource group or the whole subscription). 81 82 This is an Azure CLI example on how to query the Azure API for the information required for the Resource Group and DNS zone you would have already created in previous steps (requires `azure-cli` and `jq`) 83 84 ```bash 85 $ EXTERNALDNS_NEW_SP_NAME="ExternalDnsServicePrincipal" # name of the service principal 86 $ AZURE_DNS_ZONE_RESOURCE_GROUP="MyDnsResourceGroup" # name of resource group where dns zone is hosted 87 $ AZURE_DNS_ZONE="example.com" # DNS zone name like example.com or sub.example.com 88 89 # Create the service principal 90 $ DNS_SP=$(az ad sp create-for-rbac --name $EXTERNALDNS_NEW_SP_NAME) 91 $ EXTERNALDNS_SP_APP_ID=$(echo $DNS_SP | jq -r '.appId') 92 $ EXTERNALDNS_SP_PASSWORD=$(echo $DNS_SP | jq -r '.password') 93 ``` 94 95 #### Assign the rights for the service principal 96 97 Grant access to Azure DNS zone for the service principal. 98 99 ```bash 100 # fetch DNS id used to grant access to the service principal 101 DNS_ID=$(az network dns zone show --name $AZURE_DNS_ZONE \ 102 --resource-group $AZURE_DNS_ZONE_RESOURCE_GROUP --query "id" --output tsv) 103 104 # 1. as a reader to the resource group 105 $ az role assignment create --role "Reader" --assignee $EXTERNALDNS_SP_APP_ID --scope $DNS_ID 106 107 # 2. as a contributor to DNS Zone itself 108 $ az role assignment create --role "Contributor" --assignee $EXTERNALDNS_SP_APP_ID --scope $DNS_ID 109 ``` 110 111 #### Creating a configuration file for the service principal 112 113 Create the file `azure.json` with values gather from previous steps. 114 115 ```bash 116 cat <<-EOF > /local/path/to/azure.json 117 { 118 "tenantId": "$(az account show --query tenantId -o tsv)", 119 "subscriptionId": "$(az account show --query id -o tsv)", 120 "resourceGroup": "$AZURE_DNS_ZONE_RESOURCE_GROUP", 121 "aadClientId": "$EXTERNALDNS_SP_APP_ID", 122 "aadClientSecret": "$EXTERNALDNS_SP_PASSWORD" 123 } 124 EOF 125 ``` 126 127 Use this file to create a Kubernetes secret: 128 129 ```bash 130 $ kubectl create secret generic azure-config-file --namespace "default" --from-file /local/path/to/azure.json 131 ``` 132 133 ### Managed identity using AKS Kubelet identity 134 135 The [managed identity](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview) that is assigned to the underlying node pool in the AKS cluster can be given permissions to access Azure DNS. Managed identities are essentially a service principal whose lifecycle is managed, such as deleting the AKS cluster will also delete the service principals associated with the AKS cluster. The managed identity assigned Kubernetes node pool, or specifically the [VMSS](https://docs.microsoft.com/azure/virtual-machine-scale-sets/overview), is called the Kubelet identity. 136 137 The managed identites were previously called MSI (Managed Service Identity) and are enabled by default when creating an AKS cluster. 138 139 Note that permissions granted to this identity will be accessible to all containers running inside the Kubernetes cluster, not just the ExternalDNS container(s). 140 141 For the managed identity, the contents of `azure.json` should be similar to this: 142 143 ```json 144 { 145 "tenantId": "01234abc-de56-ff78-abc1-234567890def", 146 "subscriptionId": "01234abc-de56-ff78-abc1-234567890def", 147 "resourceGroup": "MyDnsResourceGroup", 148 "useManagedIdentityExtension": true, 149 "userAssignedIdentityID": "01234abc-de56-ff78-abc1-234567890def" 150 } 151 ``` 152 153 #### Fetching the Kubelet identity 154 155 For this process, you will need to get the kubelet identity: 156 157 ```bash 158 $ PRINCIPAL_ID=$(az aks show --resource-group $CLUSTER_GROUP --name $CLUSTERNAME \ 159 --query "identityProfile.kubeletidentity.objectId" --output tsv) 160 $ IDENTITY_CLIENT_ID=$(az aks show --resource-group $CLUSTER_GROUP --name $CLUSTERNAME \ 161 --query "identityProfile.kubeletidentity.clientId" --output tsv) 162 ``` 163 164 #### Assign rights for the Kubelet identity 165 166 Grant access to Azure DNS zone for the kubelet identity. 167 168 ```bash 169 $ AZURE_DNS_ZONE="example.com" # DNS zone name like example.com or sub.example.com 170 $ AZURE_DNS_ZONE_RESOURCE_GROUP="MyDnsResourceGroup" # resource group where DNS zone is hosted 171 172 # fetch DNS id used to grant access to the kubelet identity 173 $ DNS_ID=$(az network dns zone show --name $AZURE_DNS_ZONE \ 174 --resource-group $AZURE_DNS_ZONE_RESOURCE_GROUP --query "id" --output tsv) 175 176 $ az role assignment create --role "DNS Zone Contributor" --assignee $PRINCIPAL_ID --scope $DNS_ID 177 ``` 178 179 #### Creating a configuration file for the kubelet identity 180 181 Create the file `azure.json` with values gather from previous steps. 182 183 ```bash 184 cat <<-EOF > /local/path/to/azure.json 185 { 186 "tenantId": "$(az account show --query tenantId -o tsv)", 187 "subscriptionId": "$(az account show --query id -o tsv)", 188 "resourceGroup": "$AZURE_DNS_ZONE_RESOURCE_GROUP", 189 "useManagedIdentityExtension": true, 190 "userAssignedIdentityID": "$IDENTITY_CLIENT_ID" 191 } 192 EOF 193 ``` 194 195 Use the `azure.json` file to create a Kubernetes secret: 196 197 ```bash 198 $ kubectl create secret generic azure-config-file --namespace "default" --from-file /local/path/to/azure.json 199 ``` 200 201 ### Managed identity using AAD Pod Identities 202 203 For this process, we will create a [managed identity](https://docs.microsoft.com//azure/active-directory/managed-identities-azure-resources/overview) that will be explicitly used by the ExternalDNS container. This process is similar to Kubelet identity except that this managed identity is not associated with the Kubernetes node pool, but rather associated with explicit ExternalDNS containers. 204 205 #### Enable the AAD Pod Identities feature 206 207 For this solution, [AAD Pod Identities](https://docs.microsoft.com/azure/aks/use-azure-ad-pod-identity) preview feature can be enabled. The commands below should do the trick to enable this feature: 208 209 ```bash 210 $ az feature register --name EnablePodIdentityPreview --namespace Microsoft.ContainerService 211 $ az feature register --name AutoUpgradePreview --namespace Microsoft.ContainerService 212 $ az extension add --name aks-preview 213 $ az extension update --name aks-preview 214 $ az provider register --namespace Microsoft.ContainerService 215 ``` 216 217 #### Deploy the AAD Pod Identities service 218 219 Once enabled, you can update your cluster and install needed services for the [AAD Pod Identities](https://docs.microsoft.com/azure/aks/use-azure-ad-pod-identity) feature. 220 221 ```bash 222 $ AZURE_AKS_RESOURCE_GROUP="my-aks-cluster-group" # name of resource group where aks cluster was created 223 $ AZURE_AKS_CLUSTER_NAME="my-aks-cluster" # name of aks cluster previously created 224 225 $ az aks update --resource-group ${AZURE_AKS_RESOURCE_GROUP} --name ${AZURE_AKS_CLUSTER_NAME} --enable-pod-identity 226 ``` 227 228 Note that, if you use the default network plugin `kubenet`, then you need to add the command line option `--enable-pod-identity-with-kubenet` to the above command. 229 230 #### Creating the managed identity 231 232 After this process is finished, create a managed identity. 233 234 ```bash 235 $ IDENTITY_RESOURCE_GROUP=$AZURE_AKS_RESOURCE_GROUP # custom group or reuse AKS group 236 $ IDENTITY_NAME="example-com-identity" 237 238 # create a managed identity 239 $ az identity create --resource-group "${IDENTITY_RESOURCE_GROUP}" --name "${IDENTITY_NAME}" 240 ``` 241 242 #### Assign rights for the managed identity 243 244 Grant access to Azure DNS zone for the managed identity. 245 246 ```bash 247 $ AZURE_DNS_ZONE_RESOURCE_GROUP="MyDnsResourceGroup" # name of resource group where dns zone is hosted 248 $ AZURE_DNS_ZONE="example.com" # DNS zone name like example.com or sub.example.com 249 250 # fetch identity client id from managed identity created earlier 251 $ IDENTITY_CLIENT_ID=$(az identity show --resource-group "${IDENTITY_RESOURCE_GROUP}" \ 252 --name "${IDENTITY_NAME}" --query "clientId" --output tsv) 253 # fetch DNS id used to grant access to the managed identity 254 $ DNS_ID=$(az network dns zone show --name "${AZURE_DNS_ZONE}" \ 255 --resource-group "${AZURE_DNS_ZONE_RESOURCE_GROUP}" --query "id" --output tsv) 256 257 $ az role assignment create --role "DNS Zone Contributor" \ 258 --assignee "${IDENTITY_CLIENT_ID}" --scope "${DNS_ID}" 259 ``` 260 261 #### Creating a configuration file for the managed identity 262 263 Create the file `azure.json` with the values from previous steps: 264 265 ```bash 266 cat <<-EOF > /local/path/to/azure.json 267 { 268 "tenantId": "$(az account show --query tenantId -o tsv)", 269 "subscriptionId": "$(az account show --query id -o tsv)", 270 "resourceGroup": "$AZURE_DNS_ZONE_RESOURCE_GROUP", 271 "useManagedIdentityExtension": true, 272 "userAssignedIdentityID": "$IDENTITY_CLIENT_ID" 273 } 274 EOF 275 ``` 276 277 Use the `azure.json` file to create a Kubernetes secret: 278 279 ```bash 280 $ kubectl create secret generic azure-config-file --namespace "default" --from-file /local/path/to/azure.json 281 ``` 282 283 #### Creating an Azure identity binding 284 285 A binding between the managed identity and the ExternalDNS pods needs to be setup by creating `AzureIdentity` and `AzureIdentityBinding` resources. This will allow appropriately labeled ExternalDNS pods to authenticate using the managed identity. When AAD Pod Identity feature is enabled from previous steps above, the `az aks pod-identity add` can be used to create these resources: 286 287 ```bash 288 $ IDENTITY_RESOURCE_ID=$(az identity show --resource-group ${IDENTITY_RESOURCE_GROUP} \ 289 --name ${IDENTITY_NAME} --query id --output tsv) 290 291 $ az aks pod-identity add --resource-group ${AZURE_AKS_RESOURCE_GROUP} \ 292 --cluster-name ${AZURE_AKS_CLUSTER_NAME} --namespace "default" \ 293 --name "external-dns" --identity-resource-id ${IDENTITY_RESOURCE_ID} 294 ``` 295 296 This will add something similar to the following resources: 297 298 ```yaml 299 apiVersion: aadpodidentity.k8s.io/v1 300 kind: AzureIdentity 301 metadata: 302 labels: 303 addonmanager.kubernetes.io/mode: Reconcile 304 kubernetes.azure.com/managedby: aks 305 name: external-dns 306 spec: 307 clientID: $IDENTITY_CLIENT_ID 308 resourceID: $IDENTITY_RESOURCE_ID 309 type: 0 310 --- 311 apiVersion: aadpodidentity.k8s.io/v1 312 kind: AzureIdentityBinding 313 metadata: 314 annotations: 315 labels: 316 addonmanager.kubernetes.io/mode: Reconcile 317 kubernetes.azure.com/managedby: aks 318 name: external-dns-binding 319 spec: 320 azureIdentity: external-dns 321 selector: external-dns 322 ``` 323 324 #### Update ExternalDNS labels 325 326 When deploying ExternalDNS, you want to make sure that deployed pod(s) will have the label `aadpodidbinding: external-dns` to enable AAD Pod Identities. You can patch an existing deployment of ExternalDNS with this command: 327 328 ```bash 329 kubectl patch deployment external-dns --namespace "default" --patch \ 330 '{"spec": {"template": {"metadata": {"labels": {"aadpodidbinding": "external-dns"}}}}}' 331 ``` 332 333 ### Managed identity using Workload Identity 334 335 For this process, we will create a [managed identity](https://docs.microsoft.com//azure/active-directory/managed-identities-azure-resources/overview) that will be explicitly used by the ExternalDNS container. This process is somewhat similar to Pod Identity except that this managed identity is associated with a kubernetes service account. 336 337 #### Deploy OIDC issuer and Workload Identity services 338 339 Update your cluster to install [OIDC Issuer](https://learn.microsoft.com/en-us/azure/aks/use-oidc-issuer) and [Workload Identity](https://learn.microsoft.com/en-us/azure/aks/workload-identity-deploy-cluster): 340 341 ```bash 342 $ AZURE_AKS_RESOURCE_GROUP="my-aks-cluster-group" # name of resource group where aks cluster was created 343 $ AZURE_AKS_CLUSTER_NAME="my-aks-cluster" # name of aks cluster previously created 344 345 $ az aks update --resource-group ${AZURE_AKS_RESOURCE_GROUP} --name ${AZURE_AKS_CLUSTER_NAME} --enable-oidc-issuer --enable-workload-identity 346 ``` 347 348 #### Create a managed identity 349 350 Create a managed identity: 351 352 ```bash 353 $ IDENTITY_RESOURCE_GROUP=$AZURE_AKS_RESOURCE_GROUP # custom group or reuse AKS group 354 $ IDENTITY_NAME="example-com-identity" 355 356 # create a managed identity 357 $ az identity create --resource-group "${IDENTITY_RESOURCE_GROUP}" --name "${IDENTITY_NAME}" 358 ``` 359 360 #### Assign a role to the managed identity 361 362 Grant access to Azure DNS zone for the managed identity: 363 364 ```bash 365 $ AZURE_DNS_ZONE_RESOURCE_GROUP="MyDnsResourceGroup" # name of resource group where dns zone is hosted 366 $ AZURE_DNS_ZONE="example.com" # DNS zone name like example.com or sub.example.com 367 368 # fetch identity client id from managed identity created earlier 369 $ IDENTITY_CLIENT_ID=$(az identity show --resource-group "${IDENTITY_RESOURCE_GROUP}" \ 370 --name "${IDENTITY_NAME}" --query "clientId" --output tsv) 371 # fetch DNS id used to grant access to the managed identity 372 $ DNS_ID=$(az network dns zone show --name "${AZURE_DNS_ZONE}" \ 373 --resource-group "${AZURE_DNS_ZONE_RESOURCE_GROUP}" --query "id" --output tsv) 374 $ RESOURCE_GROUP_ID=$(az group show --name "${AZURE_DNS_ZONE_RESOURCE_GROUP}" --query "id" --output tsv) 375 376 $ az role assignment create --role "DNS Zone Contributor" \ 377 --assignee "${IDENTITY_CLIENT_ID}" --scope "${DNS_ID}" 378 $ az role assignment create --role "Reader" \ 379 --assignee "${IDENTITY_CLIENT_ID}" --scope "${RESOURCE_GROUP_ID}" 380 ``` 381 382 #### Create a federated identity credential 383 384 A binding between the managed identity and the ExternalDNS service account needs to be setup by creating a federated identity resource: 385 386 ```bash 387 $ OIDC_ISSUER_URL="$(az aks show -n myAKSCluster -g myResourceGroup --query "oidcIssuerProfile.issuerUrl" -otsv)" 388 389 $ az identity federated-credential create --name ${IDENTITY_NAME} --identity-name ${IDENTITY_NAME} --resource-group $AZURE_AKS_RESOURCE_GROUP} --issuer "$OIDC_ISSUER_URL" --subject "system:serviceaccount:default:external-dns" 390 ``` 391 392 NOTE: make sure federated credential refers to correct namespace and service account (`system:serviceaccount:<NAMESPACE>:<SERVICE_ACCOUNT>`) 393 394 #### Helm 395 396 When deploying external-dns with Helm you need to create a secret to store the Azure config (see below) and create a workload identity (out of scope here) before you can install the chart. 397 398 ```yaml 399 apiVersion: v1 400 kind: Secret 401 metadata: 402 name: external-dns-azure 403 type: Opaque 404 data: 405 azure.json: | 406 { 407 "tenantId": "<TENANT_ID>", 408 "subscriptionId": "<SUBSCRIPTION_ID>", 409 "resourceGroup": "<AZURE_DNS_ZONE_RESOURCE_GROUP>", 410 "useWorkloadIdentityExtension": true 411 } 412 ``` 413 414 Once you have created the secret and have a workload identity you can install the chart with the following values. 415 416 ```yaml 417 fullnameOverride: external-dns 418 419 serviceAccount: 420 labels: 421 azure.workload.identity/use: "true" 422 annotations: 423 azure.workload.identity/client-id: <IDENTITY_CLIENT_ID> 424 425 podLabels: 426 azure.workload.identity/use: "true" 427 428 extraVolumes: 429 - name: azure-config-file 430 secret: 431 secretName: external-dns-azure 432 433 extraVolumeMounts: 434 - name: azure-config-file 435 mountPath: /etc/kubernetes 436 readOnly: true 437 438 provider: 439 name: azure 440 ``` 441 442 NOTE: make sure the pod is restarted whenever you make a configuration change. 443 444 #### kubectl (alternative) 445 446 ##### Create a configuration file for the managed identity 447 448 Create the file `azure.json` with the values from previous steps: 449 450 ```bash 451 cat <<-EOF > /local/path/to/azure.json 452 { 453 "subscriptionId": "$(az account show --query id -o tsv)", 454 "resourceGroup": "$AZURE_DNS_ZONE_RESOURCE_GROUP", 455 "useWorkloadIdentityExtension": true 456 } 457 EOF 458 ``` 459 460 Use the `azure.json` file to create a Kubernetes secret: 461 462 ```bash 463 $ kubectl create secret generic azure-config-file --namespace "default" --from-file /local/path/to/azure.json 464 ``` 465 466 ##### Update labels and annotations on ExternalDNS service account 467 468 To instruct Workload Identity webhook to inject a projected token into the ExternalDNS pod, the pod needs to have a label `azure.workload.identity/use: "true"` (before Workload Identity 1.0.0, this label was supposed to be set on the service account instead). Also, the service account needs to have an annotation `azure.workload.identity/client-id: <IDENTITY_CLIENT_ID>`: 469 470 To patch the existing serviceaccount and deployment, use the following command: 471 472 ```bash 473 $ kubectl patch serviceaccount external-dns --namespace "default" --patch \ 474 "{\"metadata\": {\"annotations\": {\"azure.workload.identity/client-id\": \"${IDENTITY_CLIENT_ID}\"}}}" 475 $ kubectl patch deployment external-dns --namespace "default" --patch \ 476 '{"spec": {"template": {"metadata": {"labels": {\"azure.workload.identity/use\": \"true\"}}}}}' 477 ``` 478 479 NOTE: it's also possible to specify (or override) ClientID through `userAssignedIdentityID` field in `azure.json`. 480 481 NOTE: make sure the pod is restarted whenever you make a configuration change. 482 483 ## Ingress used with ExternalDNS 484 485 This deployment assumes that you will be using nginx-ingress. When using nginx-ingress do not deploy it as a Daemon Set. This causes nginx-ingress to write the Cluster IP of the backend pods in the ingress status.loadbalancer.ip property which then has external-dns write the Cluster IP(s) in DNS vs. the nginx-ingress service external IP. 486 487 Ensure that your nginx-ingress deployment has the following arg: added to it: 488 489 ``` 490 - --publish-service=namespace/nginx-ingress-controller-svcname 491 ``` 492 493 For more details see here: [nginx-ingress external-dns](https://github.com/kubernetes-sigs/external-dns/blob/HEAD/docs/faq.md#why-is-externaldns-only-adding-a-single-ip-address-in-route-53-on-aws-when-using-the-nginx-ingress-controller-how-do-i-get-it-to-use-the-fqdn-of-the-elb-assigned-to-my-nginx-ingress-controller-service-instead) 494 495 ## Deploy ExternalDNS 496 497 Connect your `kubectl` client to the cluster you want to test ExternalDNS with. Then apply one of the following manifests file to deploy ExternalDNS. 498 499 The deployment assumes that ExternalDNS will be installed into the `default` namespace. If this namespace is different, the `ClusterRoleBinding` will need to be updated to reflect the desired alternative namespace, such as `external-dns`, `kube-addons`, etc. 500 501 ### Manifest (for clusters without RBAC enabled) 502 ```yaml 503 apiVersion: apps/v1 504 kind: Deployment 505 metadata: 506 name: external-dns 507 spec: 508 strategy: 509 type: Recreate 510 selector: 511 matchLabels: 512 app: external-dns 513 template: 514 metadata: 515 labels: 516 app: external-dns 517 spec: 518 containers: 519 - name: external-dns 520 image: registry.k8s.io/external-dns/external-dns:v0.14.0 521 args: 522 - --source=service 523 - --source=ingress 524 - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. 525 - --provider=azure 526 - --azure-resource-group=MyDnsResourceGroup # (optional) use the DNS zones from the tutorial's resource group 527 volumeMounts: 528 - name: azure-config-file 529 mountPath: /etc/kubernetes 530 readOnly: true 531 volumes: 532 - name: azure-config-file 533 secret: 534 secretName: azure-config-file 535 ``` 536 537 ### Manifest (for clusters with RBAC enabled, cluster access) 538 539 ```yaml 540 apiVersion: v1 541 kind: ServiceAccount 542 metadata: 543 name: external-dns 544 --- 545 apiVersion: rbac.authorization.k8s.io/v1 546 kind: ClusterRole 547 metadata: 548 name: external-dns 549 rules: 550 - apiGroups: [""] 551 resources: ["services","endpoints","pods", "nodes"] 552 verbs: ["get","watch","list"] 553 - apiGroups: ["extensions","networking.k8s.io"] 554 resources: ["ingresses"] 555 verbs: ["get","watch","list"] 556 --- 557 apiVersion: rbac.authorization.k8s.io/v1 558 kind: ClusterRoleBinding 559 metadata: 560 name: external-dns-viewer 561 roleRef: 562 apiGroup: rbac.authorization.k8s.io 563 kind: ClusterRole 564 name: external-dns 565 subjects: 566 - kind: ServiceAccount 567 name: external-dns 568 namespace: default 569 --- 570 apiVersion: apps/v1 571 kind: Deployment 572 metadata: 573 name: external-dns 574 spec: 575 strategy: 576 type: Recreate 577 selector: 578 matchLabels: 579 app: external-dns 580 template: 581 metadata: 582 labels: 583 app: external-dns 584 spec: 585 serviceAccountName: external-dns 586 containers: 587 - name: external-dns 588 image: registry.k8s.io/external-dns/external-dns:v0.14.0 589 args: 590 - --source=service 591 - --source=ingress 592 - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. 593 - --provider=azure 594 - --azure-resource-group=MyDnsResourceGroup # (optional) use the DNS zones from the tutorial's resource group 595 - --txt-prefix=externaldns- 596 volumeMounts: 597 - name: azure-config-file 598 mountPath: /etc/kubernetes 599 readOnly: true 600 volumes: 601 - name: azure-config-file 602 secret: 603 secretName: azure-config-file 604 ``` 605 606 ### Manifest (for clusters with RBAC enabled, namespace access) 607 This configuration is the same as above, except it only requires privileges for the current namespace, not for the whole cluster. 608 However, access to [nodes](https://kubernetes.io/docs/concepts/architecture/nodes/) requires cluster access, so when using this manifest, 609 services with type `NodePort` will be skipped! 610 611 ```yaml 612 apiVersion: v1 613 kind: ServiceAccount 614 metadata: 615 name: external-dns 616 --- 617 apiVersion: rbac.authorization.k8s.io/v1 618 kind: Role 619 metadata: 620 name: external-dns 621 rules: 622 - apiGroups: [""] 623 resources: ["services","endpoints","pods"] 624 verbs: ["get","watch","list"] 625 - apiGroups: ["extensions","networking.k8s.io"] 626 resources: ["ingresses"] 627 verbs: ["get","watch","list"] 628 --- 629 apiVersion: rbac.authorization.k8s.io/v1 630 kind: RoleBinding 631 metadata: 632 name: external-dns 633 roleRef: 634 apiGroup: rbac.authorization.k8s.io 635 kind: Role 636 name: external-dns 637 subjects: 638 - kind: ServiceAccount 639 name: external-dns 640 --- 641 apiVersion: apps/v1 642 kind: Deployment 643 metadata: 644 name: external-dns 645 spec: 646 strategy: 647 type: Recreate 648 selector: 649 matchLabels: 650 app: external-dns 651 template: 652 metadata: 653 labels: 654 app: external-dns 655 spec: 656 serviceAccountName: external-dns 657 containers: 658 - name: external-dns 659 image: registry.k8s.io/external-dns/external-dns:v0.14.0 660 args: 661 - --source=service 662 - --source=ingress 663 - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above. 664 - --provider=azure 665 - --azure-resource-group=MyDnsResourceGroup # (optional) use the DNS zones from the tutorial's resource group 666 volumeMounts: 667 - name: azure-config-file 668 mountPath: /etc/kubernetes 669 readOnly: true 670 volumes: 671 - name: azure-config-file 672 secret: 673 secretName: azure-config-file 674 ``` 675 676 Create the deployment for ExternalDNS: 677 678 ```bash 679 $ kubectl create --namespace "default" --filename externaldns.yaml 680 ``` 681 682 ## Ingress Option: Expose an nginx service with an ingress 683 684 Create a file called `nginx.yaml` with the following contents: 685 686 ```yaml 687 apiVersion: apps/v1 688 kind: Deployment 689 metadata: 690 name: nginx 691 spec: 692 selector: 693 matchLabels: 694 app: nginx 695 template: 696 metadata: 697 labels: 698 app: nginx 699 spec: 700 containers: 701 - image: nginx 702 name: nginx 703 ports: 704 - containerPort: 80 705 --- 706 apiVersion: v1 707 kind: Service 708 metadata: 709 name: nginx-svc 710 spec: 711 ports: 712 - port: 80 713 protocol: TCP 714 targetPort: 80 715 selector: 716 app: nginx 717 type: ClusterIP 718 719 --- 720 apiVersion: networking.k8s.io/v1 721 kind: Ingress 722 metadata: 723 name: nginx 724 spec: 725 ingressClassName: nginx 726 rules: 727 - host: server.example.com 728 http: 729 paths: 730 - path: / 731 pathType: Prefix 732 backend: 733 service: 734 name: nginx-svc 735 port: 736 number: 80 737 ``` 738 739 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 external-dns deployment manifest. When those host names are removed or renamed the corresponding DNS records are also altered. 740 741 Create the deployment, service and ingress object: 742 743 ```bash 744 $ kubectl create --namespace "default" --filename nginx.yaml 745 ``` 746 747 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. 748 749 ## Azure Load Balancer option: Expose an nginx service with a load balancer 750 751 Create a file called `nginx.yaml` with the following contents: 752 753 ```yaml 754 --- 755 apiVersion: apps/v1 756 kind: Deployment 757 metadata: 758 name: nginx 759 spec: 760 selector: 761 matchLabels: 762 app: nginx 763 template: 764 metadata: 765 labels: 766 app: nginx 767 spec: 768 containers: 769 - image: nginx 770 name: nginx 771 ports: 772 - containerPort: 80 773 --- 774 apiVersion: v1 775 kind: Service 776 annotations: 777 external-dns.alpha.kubernetes.io/hostname: server.example.com 778 metadata: 779 name: nginx-svc 780 spec: 781 ports: 782 - port: 80 783 protocol: TCP 784 targetPort: 80 785 selector: 786 app: nginx 787 type: LoadBalancer 788 ``` 789 790 The annotation `external-dns.alpha.kubernetes.io/hostname` is used to specify the DNS name that should be created for the service. The annotation value is a comma separated list of host names. 791 792 ## Verifying Azure DNS records 793 794 Run the following command to view the A records for your Azure DNS zone: 795 796 ```bash 797 $ az network dns record-set a list --resource-group "MyDnsResourceGroup" --zone-name example.com 798 ``` 799 800 Substitute the zone for the one created above if a different domain was used. 801 802 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). 803 804 ## Delete Azure Resource Group 805 806 Now that we have verified that ExternalDNS will automatically manage Azure DNS records, we can delete the tutorial's 807 resource group: 808 809 ```bash 810 $ az group delete --name "MyDnsResourceGroup" 811 ``` 812 813 ## More tutorials 814 815 A video explanation is available here: https://www.youtube.com/watch?v=VSn6DPKIhM8&list=PLpbcUe4chE79sB7Jg7B4z3HytqUUEwcNE 816 817 