Skip to content

Commit

Permalink
Create test setup for Azure DNS integration
Browse files Browse the repository at this point in the history
As stated in k8gb-io#1772, we would like to have test setup with the main DNS providers.
This PR creates a complete guide documenting how to test the Azure integration, including terraform and helm configuration.

According to the [docs](https://azure.microsoft.com/en-us/pricing/details/dns/) the infrastructure should cost $0.90 per month, but it can also be destroyed and re-provisioned anytime:
```
First 25 hosted DNS zones	$0.50 per zone per month
First billion DNS queries/month	$0.40 per million
```

Signed-off-by: Andre Aguas <[email protected]>
  • Loading branch information
abaguas committed Nov 2, 2024
1 parent fae2e41 commit 968aec6
Show file tree
Hide file tree
Showing 14 changed files with 331 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,8 @@ changes
cosign.key
*.sig
*.att

# Terraform
terraform.tfstate*
.terraform.lock.hcl
.terraform/
3 changes: 3 additions & 0 deletions dns-providers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# DNS provider tests

In this space we document setups where we connect a local cluster to upstream DNS providers with the goal of testing the integrations.
108 changes: 108 additions & 0 deletions dns-providers/azure/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Test Azure DNS integration from a local cluster

This is a guide how to test the Azure DNS integration of K8GB

## Azure infrastructure

### Azure subscription

First you will need an Azure subscription, if you don't have one already you can get started with a [free account](https://azure.microsoft.com/en-us/pricing/purchase-options/azure-account).
Afterwards, login to your subscription in your terminal using `az login`.

Afterwards store your Azure subcription ID in an environment variable. We will use it as a terraform variable.
```
export ARM_SUBSCRIPTION_ID="$(az account show --query id -o tsv)"
```

### DNS Zone and service principal

The next step is to create a DNS zone and a service principal that allows K8GB to modify records in the zone.
You can use the terraform code provided in the `terraform` folder to get started. You will be prompted with the name of the DNS zone. The name needs to be unique in Azure, but you don't need to own the zone for the purpose of this guide:
```
$ cd terraform
$ terraform init
$ terraform apply
var.dns_zone_name
Name of the DNS zone
Enter a value: k8gb.io
```

### Create local clusters

We have everything we need from Azure, we can now create a local cluster.
Navigate to the home of the k8gb repo run the following command. It will create the clusters `k3d-test-gslb1` and `k3d-test-gslb2`, and install k8gb from the branch you are on:
```
K8GB_LOCAL_VERSION=test FULL_LOCAL_SETUP_WITH_APPS=false make deploy-full-local-setup
```

### Connect K8GB to Azure

At this moment K8GB is using the upstream DNS server running on the local cluster `k3d-edgedns`. We want to point it to the DNS infrastructure we created in Azure.

To do that we will need to create a secret on both clusters, on the `k8gb` namespace with the name `external-dns-secret-azure`. The format of the secret is documented in the [external dns docs](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/azure.md#creating-a-configuration-file-for-the-service-principal). If you are in your `terraform` folder you can create it using:
```
cat <<-EOF > azure.json
{
"tenantId": "$(az account show --query tenantId -o tsv)",
"subscriptionId": "$(az account show --query id -o tsv)",
"resourceGroup": "rg-k8gb",
"aadClientId": "$(terraform output --raw service_principal_client_id)",
"aadClientSecret": "$(terraform output --raw service_principal_client_secret)"
}
EOF
```
Now apply the secret to both of the clusters:
```
kubectl create secret generic external-dns-secret-azure -n k8gb --from-file azure.json --context k3d-test-gslb1
kubectl create secret generic external-dns-secret-azure -n k8gb --from-file azure.json --context k3d-test-gslb2
```

### Create application

Finally, we can create a GSLB resouce that will trigger a reconciliation loop of the controller and configure DNS name delegation on Azure.
To do that we will need to configure the DNS zone we create on K8GB:
```
# replace with your zone
EDGE_DNS_ZONE="k8gb.io"
```
```
DNS_ZONE="cloud.${EDGE_DNS_ZONE}"
EDGE_DNS_SERVER=$(az network dns record-set ns list --resource-group rg-k8gb --zone-name "$EDGE_DNS_ZONE" --query "[?name=='@'].NSRecords[0].nsdname" --output tsv | sed 's/\.$//')
```

```
cd helm
helm package -u . > /dev/null && helm template k8gb k8gb-v0.1.0.tgz -n k8gb -f values.yaml -f values-eu.yaml --set "k8gb.k8gb.dnsZone=$DNS_ZONE" --set "k8gb.k8gb.edgeDNSZone=$EDGE_DNS_ZONE" --set "k8gb.k8gb.edgeDNSServers[0]=$EDGE_DNS_SERVER" > manifests-eu.yaml
helm package -u . > /dev/null && helm template k8gb k8gb-v0.1.0.tgz -n k8gb -f values.yaml -f values-us.yaml --set "k8gb.k8gb.dnsZone=$DNS_ZONE" --set "k8gb.k8gb.edgeDNSZone=$EDGE_DNS_ZONE" --set "k8gb.k8gb.edgeDNSServers[0]=$EDGE_DNS_SERVER" > manifests-us.yaml
kubectl apply -f manifests-eu.yaml --context k3d-test-gslb1
kubectl apply -f manifests-us.yaml --context k3d-test-gslb2
```

### Verify zone delegation in Azure

And voila, our local clusters are now integrated with Azure. We can quickly verify everything is working.

In Azure we should find the following records (the IP addresses may be different depending on the allocation by docker):
| Name | Type | Value |
| -------- | ------- | ------- |
| cloud | NS | gslb-ns-eu-cloud.k8gb.io gslb-ns-us-cloud.k8gb.io
| gslb-ns-eu-cloud | A | 172.18.0.6 172.18.0.7
| gslb-ns-us-cloud | A | 172.18.0.10 172.18.0.11
```
az network dns record-set a list --resource-group rg-k8gb --zone-name "$EDGE_DNS_ZONE" --output json
az network dns record-set ns list --resource-group rg-k8gb --zone-name "$EDGE_DNS_ZONE" --output json
```

You can also fetch the records using the following DNS query:
```
dig @${EDGE_DNS_SERVER} cloud.k8gb.io
...
;; AUTHORITY SECTION:
cloud.k8gb.io. 5 IN NS gslb-ns-eu-cloud.k8gb.io.
cloud.k8gb.io. 5 IN NS gslb-ns-us-cloud.k8gb.io.
...
```

Unfortunately the A records cannot be queried because they are private IP addresses and Azure does not return them in a public DNS zone, but this is enough for testing.
14 changes: 14 additions & 0 deletions dns-providers/azure/helm/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: v2
name: k8gb
description: A Helm chart for Kubernetes Global Balancer
icon: https://www.k8gb.io/assets/images/icon-192x192.png
type: application
version: v0.1.0
dependencies:
- name: k8gb
repository: file://../../../chart/k8gb
version: v0.14.0

home: https://www.k8gb.io/
sources:
- https://github.com/k8gb-io/k8gb
51 changes: 51 additions & 0 deletions dns-providers/azure/helm/templates/gslb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
apiVersion: k8gb.absa.oss/v1beta1
kind: Gslb
metadata:
name: failover-playground-istio
namespace: test-azure
spec:
resourceRef:
apiVersion: networking.istio.io/v1
kind: VirtualService
matchLabels:
app: failover-playground-istio
strategy:
type: failover
dnsTtlSeconds: 5
primaryGeoTag: "eu"
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: failover-playground-istio
namespace: test-azure
labels:
app: failover-playground-istio
spec:
gateways:
- istio-ingress/failover-playground-istio
hosts:
- failover-playground-istio.{{ .Values.k8gb.k8gb.dnsZone }}
http:
- route:
- destination:
host: frontend-podinfo
port:
number: 9898
---
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: failover-playground-istio
namespace: istio-ingress
spec:
selector:
app: istio-ingressgateway
servers:
- hosts:
- failover-playground-istio.{{ .Values.k8gb.k8gb.dnsZone }}
port:
name: http
number: 8080
protocol: http
6 changes: 6 additions & 0 deletions dns-providers/azure/helm/templates/ns.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: test-azure
labels:
istio-injection: enabled
43 changes: 43 additions & 0 deletions dns-providers/azure/helm/templates/podinfo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
apiVersion: v1
kind: Service
metadata:
name: frontend-podinfo
namespace: test-azure
labels:
app.kubernetes.io/name: frontend-podinfo
spec:
type: ClusterIP
ports:
- port: 9898
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: frontend-podinfo
---
apiVersion: v1
kind: Pod
metadata:
name: frontend-podinfo
namespace: test-azure
labels:
app.kubernetes.io/name: frontend-podinfo
spec:
containers:
- name: podinfo
image: "ghcr.io/stefanprodan/podinfo:5.1.1"
command:
- ./podinfo
- --port=9898
ports:
- name: http
containerPort: 9898
protocol: TCP
resources:
requests:
memory: 64Mi
cpu: 250m
limits:
memory: 128Mi
cpu: 500m
4 changes: 4 additions & 0 deletions dns-providers/azure/helm/values-eu.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
k8gb:
k8gb:
clusterGeoTag: "eu"
extGslbClustersGeoTags: "us"
4 changes: 4 additions & 0 deletions dns-providers/azure/helm/values-us.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
k8gb:
k8gb:
clusterGeoTag: "us"
extGslbClustersGeoTags: "eu"
12 changes: 12 additions & 0 deletions dns-providers/azure/helm/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
k8gb:
k8gb:
dnsZone: "<helm set>"
edgeDNSZone: "<helm set>"
edgeDNSServers:
- "<helm set"

azuredns:
enabled: true
authSecretName: external-dns-secret-azure
createAuthSecret:
enabled: false
38 changes: 38 additions & 0 deletions dns-providers/azure/terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# dns zone

resource "azurerm_resource_group" "this" {
name = var.resource_group_name
location = var.resource_group_location
}

resource "azurerm_dns_zone" "this" {
name = var.dns_zone_name
resource_group_name = azurerm_resource_group.this.name
}

# service principal

resource "azuread_application" "k8gb" {
display_name = "k8gb"
}

resource "azuread_service_principal" "k8gb" {
client_id = azuread_application.k8gb.client_id
}

resource "azuread_service_principal_password" "k8gb" {
service_principal_id = azuread_service_principal.k8gb.id
end_date = "2099-01-01T00:00:00Z"
}

resource "azurerm_role_assignment" "dns_zone_contributor" {
principal_id = azuread_service_principal.k8gb.object_id
role_definition_name = "DNS Zone Contributor"
scope = azurerm_dns_zone.this.id
}

resource "azurerm_role_assignment" "resource_group_reader" {
principal_id = azuread_service_principal.k8gb.object_id
role_definition_name = "Reader"
scope = azurerm_resource_group.this.id
}
10 changes: 10 additions & 0 deletions dns-providers/azure/terraform/output.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
output "service_principal_client_id" {
value = azuread_service_principal.k8gb.client_id
description = "client id of the service principal"
}

output "service_principal_client_secret" {
value = azuread_service_principal_password.k8gb.value
description = "client secret of the service principal"
sensitive = true
}
17 changes: 17 additions & 0 deletions dns-providers/azure/terraform/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
terraform {
required_version = ">=1.9"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "4.8.0"
}
azuread = {
source = "hashicorp/azuread"
version = "3.0.2"
}
}
}
provider "azurerm" {
features {}
}
provider "azuread" {}
16 changes: 16 additions & 0 deletions dns-providers/azure/terraform/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
variable "resource_group_location" {
type = string
default = "germanywestcentral"
description = "Location for all resources"
}

variable "resource_group_name" {
type = string
default = "rg-k8gb"
description = "Resource group name to be created"
}

variable "dns_zone_name" {
type = string
description = "Name of the DNS zone"
}

0 comments on commit 968aec6

Please sign in to comment.