Skip to content

Commit e150b17

Browse files
committed
Add localdns config
1 parent be56dbf commit e150b17

File tree

10 files changed

+566
-0
lines changed

10 files changed

+566
-0
lines changed

examples/localdns_config/README.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# LocalDNS Configuration Example
2+
3+
This example demonstrates how to use the `localdns_config` feature in the terraform-azurerm-aks module to configure LocalDNS settings for your AKS cluster.
4+
5+
## Overview
6+
7+
LocalDNS in AKS allows you to customize DNS resolution behavior for pods with different DNS policies:
8+
9+
- **VNet DNS Overrides**: Configuration for pods using `dnsPolicy: default` (uses VNet DNS)
10+
- **Kube DNS Overrides**: Configuration for pods using `dnsPolicy: ClusterFirst` (uses Kubernetes CoreDNS)
11+
12+
## Configuration Features
13+
14+
This example configures:
15+
16+
1. **Mode**: Set to `Required` to enforce LocalDNS usage
17+
2. **VNet DNS Overrides**:
18+
- Root zone (`.`) configured to use VNet DNS with caching and stale serving
19+
- Custom zone (`example.local`) with round-robin policy
20+
3. **Kube DNS Overrides**:
21+
- Cluster-local zone (`cluster.local`) using CoreDNS with enhanced caching
22+
- Service discovery zone (`svc.cluster.local`) for Kubernetes services
23+
24+
## Key Configuration Options
25+
26+
### DNS Zone Configuration
27+
28+
Each zone can be configured with:
29+
30+
- **Query Logging**: Set to `Error` or `Log` for different verbosity levels
31+
- **Protocol**: `PreferUDP` (default) or `ForceTCP` for transport protocol
32+
- **Forward Destination**: `VnetDNS` or `ClusterCoreDNS` based on the zone type
33+
- **Forward Policy**: `Random`, `RoundRobin`, or `Sequential` for upstream server selection
34+
- **Caching**: Configure cache duration and stale serving policies
35+
- **Concurrency**: Control maximum concurrent queries
36+
37+
### Important Constraints
38+
39+
- Root zone (`.`) under `vnet_dns_overrides` cannot use `ClusterCoreDNS`
40+
- Zone `cluster.local` cannot use `VnetDNS` as forward destination
41+
- When protocol is `ForceTCP`, serve_stale cannot be `Verify`
42+
43+
## Usage
44+
45+
1. Set your Azure credentials:
46+
```bash
47+
export ARM_CLIENT_ID="your-client-id"
48+
export ARM_CLIENT_SECRET="your-client-secret"
49+
export ARM_SUBSCRIPTION_ID="your-subscription-id"
50+
export ARM_TENANT_ID="your-tenant-id"
51+
```
52+
53+
2. Initialize and apply Terraform:
54+
```bash
55+
terraform init
56+
terraform plan
57+
terraform apply
58+
```
59+
60+
3. Connect to your AKS cluster:
61+
```bash
62+
az aks get-credentials --resource-group $(terraform output -raw resource_group_name) --name $(terraform output -raw aks_cluster_name)
63+
```
64+
65+
## Testing LocalDNS Configuration
66+
67+
After deployment, you can test the LocalDNS configuration:
68+
69+
```bash
70+
# Test DNS resolution from a pod
71+
kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup kubernetes.default.svc.cluster.local
72+
73+
# Check LocalDNS pods
74+
kubectl get pods -n kube-system -l k8s-app=node-local-dns
75+
76+
# View LocalDNS configuration
77+
kubectl get configmap node-local-dns -n kube-system -o yaml
78+
```
79+
80+
## Cleanup
81+
82+
```bash
83+
terraform destroy
84+
```
85+
86+
## More Information
87+
88+
- [Azure LocalDNS Documentation](https://learn.microsoft.com/en-us/azure/aks/localdns-custom)
89+
- [Kubernetes DNS Policies](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy)

examples/localdns_config/main.tf

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
resource "random_id" "prefix" {
2+
byte_length = 8
3+
}
4+
5+
resource "random_id" "name" {
6+
byte_length = 8
7+
}
8+
9+
resource "azurerm_resource_group" "main" {
10+
count = var.create_resource_group ? 1 : 0
11+
12+
location = var.location
13+
name = coalesce(var.resource_group_name, "${random_id.prefix.hex}-rg")
14+
}
15+
16+
locals {
17+
resource_group = {
18+
name = var.create_resource_group ? azurerm_resource_group.main[0].name : var.resource_group_name
19+
location = var.location
20+
}
21+
}
22+
23+
resource "azurerm_virtual_network" "test" {
24+
address_space = ["10.52.0.0/16"]
25+
location = local.resource_group.location
26+
name = "${random_id.prefix.hex}-vn"
27+
resource_group_name = local.resource_group.name
28+
}
29+
30+
resource "azurerm_subnet" "test" {
31+
address_prefixes = ["10.52.0.0/24"]
32+
name = "${random_id.prefix.hex}-sn"
33+
resource_group_name = local.resource_group.name
34+
virtual_network_name = azurerm_virtual_network.test.name
35+
}
36+
37+
module "aks" {
38+
source = "../.."
39+
40+
location = local.resource_group.location
41+
prefix = random_id.name.hex
42+
rbac_aad_tenant_id = data.azurerm_client_config.current.tenant_id
43+
resource_group_name = local.resource_group.name
44+
kubernetes_version = "1.30" # don't specify the patch version!
45+
automatic_channel_upgrade = "patch"
46+
agents_availability_zones = ["1", "2"]
47+
agents_count = null
48+
agents_max_count = 2
49+
agents_max_pods = 100
50+
agents_min_count = 1
51+
agents_pool_name = "testnodepool"
52+
agents_size = "Standard_DS2_v2"
53+
auto_scaling_enabled = true
54+
client_id = var.client_id
55+
client_secret = var.client_secret
56+
enable_auto_scaling = true
57+
log_analytics_workspace_enabled = true
58+
net_profile_dns_service_ip = "10.0.0.10"
59+
net_profile_service_cidr = "10.0.0.0/16"
60+
network_plugin = "azure"
61+
orchestrator_version = "1.30"
62+
os_disk_size_gb = 60
63+
private_cluster_enabled = false
64+
rbac_aad = true
65+
rbac_aad_managed = true
66+
role_based_access_control_enabled = true
67+
sku_tier = "Standard"
68+
vnet_subnet_id = azurerm_subnet.test.id
69+
70+
# LocalDNS configuration example
71+
localdns_config = {
72+
mode = "Required"
73+
74+
# Configuration for pods using dnsPolicy:default (VNet DNS)
75+
vnet_dns_overrides = {
76+
zones = {
77+
# Root zone configuration - uses VNet DNS
78+
"." = {
79+
query_logging = "Error"
80+
protocol = "PreferUDP"
81+
forward_destination = "VnetDNS"
82+
forward_policy = "Random"
83+
max_concurrent = 150
84+
cache_duration_in_seconds = 300
85+
serve_stale_duration_in_seconds = 86400
86+
serve_stale = "Immediate"
87+
}
88+
# Custom zone configuration
89+
"example.local" = {
90+
query_logging = "Log"
91+
protocol = "PreferUDP"
92+
forward_destination = "VnetDNS"
93+
forward_policy = "RoundRobin"
94+
max_concurrent = 100
95+
}
96+
}
97+
}
98+
99+
# Configuration for pods using dnsPolicy:ClusterFirst (Kubernetes DNS)
100+
kube_dns_overrides = {
101+
zones = {
102+
# Cluster-local zone - uses Kubernetes CoreDNS
103+
"cluster.local" = {
104+
query_logging = "Error"
105+
protocol = "PreferUDP"
106+
forward_destination = "ClusterCoreDNS"
107+
forward_policy = "Sequential"
108+
max_concurrent = 200
109+
cache_duration_in_seconds = 600
110+
serve_stale_duration_in_seconds = 3600
111+
serve_stale = "Verify"
112+
}
113+
# Service discovery zone
114+
"svc.cluster.local" = {
115+
query_logging = "Log"
116+
protocol = "PreferUDP"
117+
forward_destination = "ClusterCoreDNS"
118+
forward_policy = "Random"
119+
}
120+
}
121+
}
122+
}
123+
124+
depends_on = [
125+
azurerm_subnet.test,
126+
]
127+
}
128+
129+
data "azurerm_client_config" "current" {}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
output "aks_cluster_name" {
2+
description = "Name of the AKS cluster"
3+
value = module.aks.cluster_name
4+
}
5+
6+
output "aks_cluster_id" {
7+
description = "ID of the AKS cluster"
8+
value = module.aks.cluster_id
9+
}
10+
11+
output "cluster_ca_certificate" {
12+
description = "Base64 encoded certificate data required to communicate with the cluster"
13+
value = module.aks.cluster_ca_certificate
14+
sensitive = true
15+
}
16+
17+
output "client_certificate" {
18+
description = "Base64 encoded public certificate used by clients to authenticate to the cluster"
19+
value = module.aks.client_certificate
20+
sensitive = true
21+
}
22+
23+
output "client_key" {
24+
description = "Base64 encoded private key used by clients to authenticate to the cluster"
25+
value = module.aks.client_key
26+
sensitive = true
27+
}
28+
29+
output "host" {
30+
description = "The Kubernetes cluster server host"
31+
value = module.aks.host
32+
sensitive = true
33+
}
34+
35+
output "kube_config" {
36+
description = "Raw Kubernetes config to be used by kubectl and other compatible tools"
37+
value = module.aks.kube_config_raw
38+
sensitive = true
39+
}
40+
41+
output "localdns_config" {
42+
description = "LocalDNS configuration applied to the cluster"
43+
value = module.aks.localdns_config
44+
}
45+
46+
output "resource_group_name" {
47+
description = "Name of the resource group"
48+
value = local.resource_group.name
49+
}
50+
51+
output "subnet_id" {
52+
description = "ID of the subnet used by the AKS cluster"
53+
value = azurerm_subnet.test.id
54+
}
55+
56+
output "vnet_id" {
57+
description = "ID of the virtual network used by the AKS cluster"
58+
value = azurerm_virtual_network.test.id
59+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
terraform {
2+
required_providers {
3+
azapi = {
4+
source = "Azure/azapi"
5+
version = ">=2.0, < 3.0"
6+
}
7+
azurerm = {
8+
source = "hashicorp/azurerm"
9+
version = ">= 4.16.0, < 5.0"
10+
}
11+
random = {
12+
source = "hashicorp/random"
13+
version = "~>3.0"
14+
}
15+
}
16+
required_version = ">= 1.3"
17+
}
18+
19+
provider "azurerm" {
20+
features {}
21+
}
22+
23+
provider "azapi" {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
variable "client_id" {
2+
description = "The Client ID which should be used."
3+
type = string
4+
default = ""
5+
}
6+
7+
variable "client_secret" {
8+
description = "The Client Secret which should be used."
9+
type = string
10+
default = ""
11+
}
12+
13+
variable "create_resource_group" {
14+
description = "Whether to create resource group and use it for all networking resources"
15+
default = true
16+
}
17+
18+
variable "location" {
19+
default = "East US"
20+
description = "The location where the Managed Kubernetes Cluster should be created."
21+
}
22+
23+
variable "resource_group_name" {
24+
description = "The resource group name to be imported"
25+
type = string
26+
default = null
27+
}

main.tf

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,3 +745,57 @@ resource "azapi_update_resource" "aks_cluster_http_proxy_config_no_proxy" {
745745
replace_triggered_by = [null_resource.http_proxy_config_no_proxy_keeper[0].id]
746746
}
747747
}
748+
749+
# LocalDNS configuration trigger resource
750+
resource "null_resource" "localdns_config_keeper" {
751+
count = var.localdns_config != null ? 1 : 0
752+
triggers = {
753+
localdns_config = jsonencode(var.localdns_config)
754+
}
755+
}
756+
757+
# Apply LocalDNS configuration using azapi
758+
resource "azapi_update_resource" "aks_cluster_localdns_config" {
759+
count = var.localdns_config != null ? 1 : 0
760+
761+
resource_id = azurerm_kubernetes_cluster.main.id
762+
type = "Microsoft.ContainerService/managedClusters@2024-02-01"
763+
body = {
764+
properties = {
765+
localDNSConfig = {
766+
mode = var.localdns_config.mode
767+
vnetDNSOverrides = var.localdns_config.vnet_dns_overrides != null ? {
768+
for zone_name, zone_config in var.localdns_config.vnet_dns_overrides.zones : zone_name => {
769+
queryLogging = zone_config.query_logging
770+
protocol = zone_config.protocol
771+
forwardDestination = zone_config.forward_destination
772+
forwardPolicy = zone_config.forward_policy
773+
maxConcurrent = zone_config.max_concurrent
774+
cacheDurationInSeconds = zone_config.cache_duration_in_seconds
775+
serveStaleDurationInSeconds = zone_config.serve_stale_duration_in_seconds
776+
serveStale = zone_config.serve_stale
777+
}
778+
} : null
779+
kubeDNSOverrides = var.localdns_config.kube_dns_overrides != null ? {
780+
for zone_name, zone_config in var.localdns_config.kube_dns_overrides.zones : zone_name => {
781+
queryLogging = zone_config.query_logging
782+
protocol = zone_config.protocol
783+
forwardDestination = zone_config.forward_destination
784+
forwardPolicy = zone_config.forward_policy
785+
maxConcurrent = zone_config.max_concurrent
786+
cacheDurationInSeconds = zone_config.cache_duration_in_seconds
787+
serveStaleDurationInSeconds = zone_config.serve_stale_duration_in_seconds
788+
serveStale = zone_config.serve_stale
789+
}
790+
} : null
791+
}
792+
}
793+
}
794+
795+
depends_on = [azapi_update_resource.aks_cluster_post_create]
796+
797+
lifecycle {
798+
ignore_changes = all
799+
replace_triggered_by = [null_resource.localdns_config_keeper[0].id]
800+
}
801+
}

0 commit comments

Comments
 (0)