-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsetup.sh
368 lines (312 loc) · 15.5 KB
/
setup.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
#!/bin/bash
readinput () {
# $1: name
# $2: default value
local VALUE
read -p "${1} (default: ${2}): " VALUE
VALUE="${VALUE:=${2}}"
echo $VALUE
}
echo ""
echo "========================================================"
echo "| SCALE DEMO SETUP |"
echo "========================================================"
echo ""
# Disable warnings
az config set core.only_show_errors=yes
# Parameters
PREFIX=kubecon
LOCATION=`readinput "Location" "eastus"`
DNSZONE=`readinput "DNS Zone" "aks.azure.sabbour.me"`
DNSZONE_RESOURCEGROUP=`readinput "DNS Zone Resource Group" "azure.sabbour.me-rg"`
PREFIX=`readinput "Prefix" "${PREFIX}"`
RANDOMSTRING=`readinput "Random string" "$(mktemp --dry-run XXX | tr '[:upper:]' '[:lower:]')"`
IDENTIFIER="${PREFIX}${RANDOMSTRING}"
CLUSTER_RG=`readinput "Resource group" "${IDENTIFIER}-rg"`
CLUSTER_NAME="${IDENTIFIER}"
DEPLOYMENT_NAME="${IDENTIFIER}-deployment"
HOSTNAME="${IDENTIFIER}.${DNSZONE}"
#LATEST_K8S_VERSION=$(az aks get-versions --location ${LOCATION} --query "values[?isPreview == null] | sort_by(reverse(@), &version)[-1:].version" -o tsv)
AVAILABLE_K8S_VERSIONS=$(az aks get-versions --location ${LOCATION} --query "sort(values[?isPreview == null][].patchVersions.keys(@)[-1])" -o tsv | tr '\n' ',' | sed 's/,$//')
LATEST_K8S_VERSION=$(az aks get-versions --location ${LOCATION} --query "sort(values[?isPreview == null][].patchVersions.keys(@)[-1])[-1]" -o tsv)
K8S_VERSION=`readinput "Kubernetes version (${AVAILABLE_K8S_VERSIONS})" "${LATEST_K8S_VERSION}"`
AZURE_TENANT_ID=$(az account show --query tenantId -o tsv)
AZURE_SUBSCRIPTION_ID=$(az account show --query id -o tsv)
CURRENT_UPN=$(az account show --query user.name -o tsv) # Get current user's UPN (for role assignments)
CURRENT_OBJECT_ID=$(az ad user show --id ${CURRENT_UPN} --query id -o tsv) # Get current user's Object ID (for role assignments)
echo ""
echo "========================================================"
echo "| ABOUT TO RUN THE SCRIPT |"
echo "========================================================"
echo ""
echo "Will execute against subscription: ${AZURE_SUBSCRIPTION_ID}"
echo "To change, terminate the script, run az account set --subscription <subscrption id> and run the script again."
echo "Continue? Type y or Y."
read REPLY
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
exit
fi
echo ""
echo "========================================================"
echo "| CONFIGURING PREREQUISITES |"
echo "========================================================"
echo ""
START="$(date +%s)"
# Make sure the preview features are registered
echo "Making sure that the features are registered"
az extension add --upgrade --name aks-preview
az feature register --namespace Microsoft.ContainerService --name AKS-KedaPreview -o none
az feature register --namespace Microsoft.ContainerService --name AKS-VPAPreview -o none
az feature register --namespace "Microsoft.ContainerService" --name CiliumDataplanePreview -o none
az feature register --namespace "Microsoft.ContainerService" --name AzureOverlayPreview -o none
az feature register --namespace "Microsoft.ContainerService" --name EnableWorkloadIdentityPreview -o none
az provider register --namespace Microsoft.ContainerService -o none
echo ""
echo "========================================================"
echo "| CREATING RESOURCE GROUP |"
echo "========================================================"
echo ""
echo "Creating resource group ${CLUSTER_RG} in ${LOCATION}"
az group create -n ${CLUSTER_RG} -l ${LOCATION}
echo ""
echo "========================================================"
echo "| CREATING AZURE MONITOR WORKSPACE |"
echo "========================================================"
echo ""
echo "Creating Azure Monitor Workspace"
AZUREMONITORWORKSPACE_RESOURCE_ID=$(az monitor account create -n ${IDENTIFIER} -g ${CLUSTER_RG} --query id -o tsv)
echo ""
echo "Retrieving the Azure Monitor managed service for Prometheus query endpoint"
AZUREMONITOR_PROM_ENDPOINT=$(az resource show --id $AZUREMONITORWORKSPACE_RESOURCE_ID --query "properties.metrics.prometheusQueryEndpoint" -o tsv)
echo "Will later update the KEDA ScaledObject with this Prometheus query endpoint ${AZUREMONITOR_PROM_ENDPOINT}"
echo ""
echo "========================================================"
echo "| CREATING AZURE MANAGED GRAFANA |"
echo "========================================================"
echo ""
echo "Creating Azure Managed Grafana"
AZUREGRAFANA_ID=$(az grafana create -n ${IDENTIFIER} -g ${CLUSTER_RG} --skip-role-assignments --query id -o tsv)
AZUREGRAFANA_PRINCIPALID=$(az resource show --id $AZUREGRAFANA_ID --query "identity.principalId" -o tsv)
echo "Granting Grafana Admin role assignment to ${CURRENT_UPN} (${CURRENT_OBJECT_ID})"
az role assignment create --assignee ${CURRENT_OBJECT_ID} --role "Grafana Admin" --scope ${AZUREGRAFANA_ID}
echo "Granting Monitoring Reader role assignment to Grafana on the Azure Monitor workspace"
az role assignment create --assignee ${AZUREGRAFANA_PRINCIPALID} --role "Monitoring Reader" --scope ${AZUREMONITORWORKSPACE_RESOURCE_ID}
echo "Granting Monitoring Reader role assignment to Grafana on the subscription"
az role assignment create --assignee ${AZUREGRAFANA_PRINCIPALID} --role "Monitoring Reader" --subscription ${AZURE_SUBSCRIPTION_ID}
echo "Sleeping to allow for identity to propagate"
sleep 60
echo ""
echo "========================================================"
echo "| CREATING AKS CLUSTER |"
echo "========================================================"
echo ""
# Create AKS cluster with the required add-ons and configuration
echo "Creating an Azure Kubernetes Service cluster ${CLUSTER_NAME} with Kubernetes version ${K8S_VERSION}"
az aks create -n ${CLUSTER_NAME} -g ${CLUSTER_RG} \
--enable-azure-monitor-metrics \
--azure-monitor-workspace-resource-id ${AZUREMONITORWORKSPACE_RESOURCE_ID} \
--grafana-resource-id ${AZUREGRAFANA_ID} \
--enable-workload-identity \
--enable-oidc-issuer \
--enable-msi-auth-for-monitoring \
--enable-keda \
--node-vm-size Standard_DS4_v2 \
--enable-addons azure-keyvault-secrets-provider,web_application_routing \
--enable-secret-rotation \
--network-dataplane cilium \
--network-plugin azure \
--network-plugin-mode overlay \
--kubernetes-version ${LATEST_K8S_VERSION} \
--enable-cluster-autoscaler \
--min-count 1 \
--max-count 5
# Wait until the provisioning state of the cluster is not updating
echo "Waiting for the cluster to be ready"
while [[ "$(az aks show -n ${CLUSTER_NAME} -g ${CLUSTER_RG} --query 'provisioningState' -o tsv)" == "Updating" ]]; do
sleep 10
done
echo ""
echo "Creating user node pools 1/4"
az aks nodepool add \
-g ${CLUSTER_RG} \
-n selftune1 \
--cluster-name ${CLUSTER_NAME} \
--enable-cluster-autoscaler \
--min-count 1 \
--max-count 30 \
--node-vm-size Standard_B4ms
echo ""
echo "Creating user node pools 2/4"
az aks nodepool add \
-g ${CLUSTER_RG} \
-n selftune2 \
--cluster-name ${CLUSTER_NAME} \
--enable-cluster-autoscaler \
--min-count 1 \
--max-count 30 \
--node-vm-size Standard_B4ms
echo ""
echo "Creating user node pools 3/4"
az aks nodepool add \
-g ${CLUSTER_RG} \
-n selftune3 \
--cluster-name ${CLUSTER_NAME} \
--enable-cluster-autoscaler \
--min-count 1 \
--max-count 30 \
--node-vm-size Standard_B4ms
echo ""
echo "Creating user node pools 4/4"
az aks nodepool add \
-g ${CLUSTER_RG} \
-n selftune4 \
--cluster-name ${CLUSTER_NAME} \
--enable-cluster-autoscaler \
--min-count 1 \
--max-count 30 \
--node-vm-size Standard_B4ms
echo ""
echo "========================================================"
echo "| CONFIGURE WORKLOAD IDENTITY FOR KEDA |"
echo "========================================================"
echo ""
# Create a managed identity for KEDA
echo "Creating a managed identity for KEDA"
az identity create -n keda-${CLUSTER_NAME} -g ${CLUSTER_RG}
echo ""
KEDA_UAMI_CLIENTID=$(az identity show -n keda-${CLUSTER_NAME} -g ${CLUSTER_RG} --query clientId -o tsv)
KEDA_UAMI_PRINCIPALID=$(az identity show -n keda-${CLUSTER_NAME} -g ${CLUSTER_RG} --query principalId -o tsv)
echo "Will later update the KEDA TriggerAuthentication to use this client identity ${KEDA_UAMI_CLIENTID}"
echo "Will later update the role assignment to use this principal identity ${KEDA_UAMI_PRINCIPALID}"
echo ""
echo "Sleeping to allow for identity to propagate"
sleep 30
# Create a federated identity credential for KEDA
echo ""
echo "Creating a federated identity credential for KEDA"
AKS_OIDC_ISSUER=$(az aks show -n ${CLUSTER_NAME} -g ${CLUSTER_RG} --query "oidcIssuerProfile.issuerUrl" -o tsv)
az identity federated-credential create --name keda-${CLUSTER_NAME} --identity-name keda-${CLUSTER_NAME} --resource-group ${CLUSTER_RG} --issuer ${AKS_OIDC_ISSUER} --subject system:serviceaccount:kube-system:keda-operator
# Assigning the Monitoring Data Reader role to KEDA's managed identity
echo ""
echo "Assigning the Monitoring Data Reader role to KEDA's managed identity"
az role assignment create --assignee ${KEDA_UAMI_PRINCIPALID} --role "Monitoring Data Reader" --scope ${AZUREMONITORWORKSPACE_RESOURCE_ID}
echo ""
echo "========================================================"
echo "| CONFIGURE APP ROUTER ADD-ON |"
echo "========================================================"
echo ""
echo "Retrieving the app router add-on managed identity"
APPROUTER_IDENTITY_OBJECTID=$(az aks show -n ${CLUSTER_NAME} -g ${CLUSTER_RG} --query ingressProfile.webAppRouting.identity.objectId -o tsv)
echo "Retrieving the Azure DNS zone ID for ${DNSZONE} in resource group ${DNSZONE_RESOURCEGROUP}"
AZUREDNS_ZONEID=$(az network dns zone show -n ${DNSZONE} -g ${DNSZONE_RESOURCEGROUP} --query "id" --output tsv)
echo "Assigning the DNS Zone Contributor role to the addon's managed identity"
az role assignment create --role "DNS Zone Contributor" --assignee $APPROUTER_IDENTITY_OBJECTID --scope $AZUREDNS_ZONEID
# Wait until the provisioning state of the cluster is not updating
echo "Waiting for the cluster to be ready"
while [[ "$(az aks show -n ${CLUSTER_NAME} -g ${CLUSTER_RG} --query 'provisioningState' -o tsv)" == "Updating" ]]; do
sleep 10
done
echo "Updating the app router add-on to use the DNS Zone ${DNSZONE}"
az aks addon update -n ${CLUSTER_NAME} -g ${CLUSTER_RG} --addon web_application_routing --dns-zone-resource-ids=$AZUREDNS_ZONEID
echo ""
echo "========================================================"
echo "| FINISHING UP |"
echo "========================================================"
echo ""
echo "Importing the nginx dashboards into Grafana"
az grafana dashboard import -n ${IDENTIFIER} -g ${CLUSTER_RG} --definition @./grafana/nginx.json
az grafana dashboard import -n ${IDENTIFIER} -g ${CLUSTER_RG} --definition @./grafana/request-handling-performance.json
az grafana dashboard create -n ${IDENTIFIER} -g ${CLUSTER_RG} --definition @./grafana/demo-dashboard.json
# Retrieve Grafana dashboard URL
echo "Retrieving the Grafana dashboard URL"
GRAFANA_URL=$(az grafana show -n ${IDENTIFIER} -g ${CLUSTER_RG} --query "properties.endpoint" -o tsv)
# Retrieve AKS cluster credentials
echo "Retrieving the Azure Kubernetes Service cluster credentials"
az aks get-credentials -n ${CLUSTER_NAME} -g ${CLUSTER_RG}
END="$(date +%s)"
DURATION=$[ ${END} - ${START} ]
echo ""
echo "========================================================"
echo "| SETUP COMPLETED |"
echo "========================================================"
echo ""
echo "Total time elapsed: $(( DURATION / 60 )) minutes"
echo ""
echo "========================================================"
echo "| UPDATING MANIFESTS |"
echo "========================================================"
echo ""
echo "Updating the manifests/scaledobject.yaml file with Prometheus query endpoint > manifests/generated/scaledobject.yaml "
yq "(.spec.triggers.[] | select(.type == \"prometheus\")).metadata.serverAddress |= \"${AZUREMONITOR_PROM_ENDPOINT}\"" manifests/scaledobject.yaml > manifests/generated/scaledobject.yaml
echo "Updating the manifests/triggerauthentication.yaml file with KEDA client id > manifests/generated/triggerauthentication.yaml"
yq "(.spec.podIdentity.identityId) |= \"${KEDA_UAMI_CLIENTID}\"" manifests/triggerauthentication.yaml > manifests/generated/triggerauthentication.yaml
echo "Updating the manifests/ingress.yaml file with hostname > manifests/generated/ingress.yaml"
yq "(.spec.rules[0]).host |= \"${HOSTNAME}\"" manifests/ingress.yaml > manifests/generated/ingress.yaml
echo ""
echo "========================================================"
echo "| DEPLOYING THE PROMETHEUS SCRAPING CONFIG |"
echo "========================================================"
echo ""
kubectl apply -f ./manifests/config/ama-metrics-settings.config.yaml
echo ""
echo "========================================================"
echo "| DEPLOYING THE APPLICATION |"
echo "========================================================"
echo ""
kubectl apply -f ./manifests/namespace.yaml
kubectl apply -f ./manifests/deployment.yaml
kubectl apply -f ./manifests/service.yaml
kubectl apply -f ./manifests/pdb.yaml
kubectl apply -f ./manifests/generated/ingress.yaml
kubectl apply -f ./manifests/generated/triggerauthentication.yaml
kubectl apply -f ./manifests/generated/scaledobject.yaml
# Force a restart for the keda operator deployment to pick up the new trigger authentication
kubectl rollout restart deployment.apps/keda-operator -n kube-system
echo ""
echo "========================================================"
echo "| GETTING THE ENDPOINTS |"
echo "========================================================"
echo ""
INGRESS_IP=""
echo "Waiting for the ingress to get an IP address"
while [ -z $INGRESS_IP ]
do
INGRESS_IP=$(kubectl get ingress serverloader --namespace=serverloader -o jsonpath="{.status.loadBalancer.ingress[0].ip}")
sleep 5
done
echo "Waiting for the A record to be created in the DNS zone"
while [[ "$(az network dns record-set a list -g ${DNSZONE_RESOURCEGROUP} -z ${DNSZONE} --query "[?name=='${IDENTIFIER}'].provisioningState" -o tsv)" != "Succeeded" ]]; do
sleep 5
done
echo ""
echo "========================================================"
echo "| TEST THE APPLICATION |"
echo "========================================================"
echo ""
echo "curl -k http://${INGRESS_IP}/workout -H \"Host: ${HOSTNAME}\""
echo "curl -k http://${INGRESS_IP}/metrics -H \"Host: ${HOSTNAME}\""
echo "curl -k http://${INGRESS_IP}/stats -H \"Host: ${HOSTNAME}\""
echo ""
echo "curl -k http://${HOSTNAME}/workout"
echo "curl -k http://${HOSTNAME}/metrics"
echo "curl -k http://${HOSTNAME}/stats"
echo ""
echo "Grafana dashboard: ${GRAFANA_URL}"
echo ""
echo ""
echo "========================================================"
echo "| CLEAN UP AFTER YOU ARE DONE |"
echo "========================================================"
echo ""
echo "Delete the ${CLUSTER_RG} resource group when you are done by running:"
echo "az group delete --name ${CLUSTER_RG} --no-wait"
echo ""
echo "Delete the ${HOSTNAME} records when you are done by running:"
echo "az network dns record-set a delete -g ${DNSZONE_RESOURCEGROUP} -z ${DNSZONE} -n ${IDENTIFIER}"
echo "az network dns record-set txt delete -g ${DNSZONE_RESOURCEGROUP} -z ${DNSZONE} -n ${IDENTIFIER}"
echo ""
echo "Have fun!"
# Enable warnings
az config set core.only_show_errors=no