Introdução
A implantação de aplicativos em contêineres tem se tornado uma prática cada vez mais comum no mundo da tecnologia, oferecendo flexibilidade, escalabilidade e agilidade no desenvolvimento e na operação de aplicações. O Azure Kubernetes Service (AKS) é uma das soluções mais populares para orquestração de contêineres, permitindo que as equipes de desenvolvimento gerenciem de maneira eficiente clusters de contêineres em larga escala.
Um dos principais desafios ao implantar aplicativos em contêineres é fornecer uma maneira confiável e segura para que os usuários externos acessem esses aplicativos. É aqui que o Azure Application Gateway Ingress Controller (AGIC) desempenha um papel fundamental. O AGIC atua como um controlador de ingresso, direcionando o tráfego externo para os serviços corretos dentro do cluster AKS, tornando mais fácil e seguro o acesso a aplicativos em contêineres.
Além disso, para garantir a segurança dos dados transmitidos pela aplicação, a utilização de SSL (Secure Socket Layer) ou TLS (Transport Layer Security) é essencial. Isso ajuda a proteger as informações confidenciais durante a comunicação entre o cliente e o servidor.
A combinação do AKS com o AGIC e a implementação de SSL oferecem uma solução poderosa para implantar aplicativos de forma segura e confiável. Os benefícios incluem:
-
Balanceamento de Carga Avançado: O Azure Application Gateway oferece recursos de balanceamento de carga avançados, permitindo distribuir o tráfego de entrada de maneira eficiente entre os pods do AKS.
-
Roteamento Baseado em Regras: Com o AGIC, é possível configurar regras de roteamento personalizadas para direcionar solicitações para serviços específicos com base em caminhos de URL ou cabeçalhos HTTP.
-
Gerenciamento Centralizado: O AGIC é totalmente integrado ao AKS, facilitando o gerenciamento centralizado do tráfego de entrada para todos os serviços no cluster.
-
Segurança e Criptografia: A configuração de SSL/TLS com certificados gerenciados do Azure assegura que as comunicações entre os usuários e a aplicação sejam protegidas e criptografadas.
-
Redução de Complexidade: A combinação do AKS com o AGIC simplifica a configuração de infraestrutura, permitindo que as equipes de desenvolvimento se concentrem no código da aplicação.
Em resumo, a implantação do Azure Kubernetes Service com o Azure Application Gateway Ingress Controller e a implementação de SSL oferecem uma solução robusta para a execução confiável e segura de aplicativos em contêineres. Essa abordagem garante alta disponibilidade, escalabilidade e proteção dos dados, permitindo que as empresas entreguem seus aplicativos com confiança e eficiência.
Arquitetura AKS com Ingress Controller (AGIC)
O cenário desta arquitetura envolve o uso do Azure Application Gateway Ingress Controller (AGIC) com o Azure Kubernetes Service (AKS) para gerenciar o tráfego externo e encaminhá-lo para os pods desejados no AKS. Vamos dividir o processo em etapas:
-
Criação do AKS com AGIC:
-
O AKS é um serviço gerenciado de Kubernetes fornecido pelo Azure. Você criou um cluster AKS que é a base da sua aplicação.
-
O AGIC é instalado como um addon do AKS. Ele fornece a integração do AKS com o Azure Application Gateway (App Gateway), que é um balanceador de carga avançado do Azure.
-
-
Configuração do Azure Application Gateway (App Gateway):
-
O App Gateway é criado e configurado para trabalhar como um ponto de entrada para o tráfego externo. Ele age como um balanceador de carga e um controlador de ingresso.
-
O App Gateway é configurado para aceitar tráfego HTTPS (porta 443) dos usuários externos.
-
-
Configuração do DNS:
-
Um registro DNS é configurado para apontar o domínio personalizado (exemplo: www.seusite.com) para o endereço IP público do App Gateway.
-
-
Requisições dos Usuários Externos:
-
Quando um usuário externo faz uma requisição HTTPS para o domínio personalizado (www.seusite.com), a solicitação é enviada para o endereço IP público do App Gateway.
-
-
Encaminhamento do Tráfego pelo AGIC:
-
O AGIC intercepta a requisição no App Gateway e encaminha a solicitação para o AKS.
-
O AGIC atua como um controlador de ingresso (Ingress Controller), observando os recursos Ingress definidos no AKS para determinar como encaminhar o tráfego para os serviços corretos no cluster.
-
-
Encaminhamento pelo AKS:
-
O AKS recebe a requisição e verifica o recurso Ingress definido para o domínio (www.seusite.com).
-
O Ingress mapeia o domínio para o serviço apropriado dentro do cluster AKS.
-
-
Encaminhamento para o Pod:
-
O serviço dentro do AKS encaminha a requisição para um dos pods que implementa o serviço.
-
O pod contém a aplicação web ou aplicação de back-end que responde à requisição do usuário.
-
-
Resposta para o Usuário:
-
O pod processa a requisição e retorna uma resposta ao App Gateway.
-
O App Gateway, por sua vez, retorna a resposta ao usuário externo que fez a solicitação inicial.
-
Todo esse processo acontece de forma transparente para o usuário externo. O AGIC e o AKS trabalham em conjunto para gerenciar o tráfego de entrada e encaminhá-lo para a aplicação correta. O uso do App Gateway e do AGIC oferece recursos avançados de balanceamento de carga, gerenciamento de tráfego e segurança para o seu AKS, permitindo que você gerencie eficientemente o tráfego de entrada e saída da sua aplicação.
Implantação do Laboratório
Passo 1: Declarar variaveis
Antes de começar, certifique-se de ter o Azure CLI instalado no Terminal Bash (ex: Azure Cloud Shell bash), o que permitirá que você gerencie recursos na nuvem por meio de comandos de linha de comando. Em seguida atualize e declare as variáveis que usaremos para a implementação do nosso ambiente:
# Informações da Assinatura
tenantId="<coloque o iD do seu Tenant aqui>"
subscriptionId="<coloque o id da sua subscription aqui>"
regionName="westus"
# Informações do usuário (pode ser obtido em Azure AD > Users > Seu usuario > User principal name)
userPrincipalName="<Coloque o seu UserPrincipalName aqui>"
# Nomes dos Grupos de Recursos
rgDnsName="rg-h-wus-dns01"
rgNetName="rg-h-wus-net01"
rgJmpName="rg-h-wus-jmp01"
rgAksName="rg-h-wus-aks01"
# Pares de Tags
tagPairs="foo=bar baz=qux"
# Informações do Domínio Público do Azure App Service
publicDomainName="aksagiclab.com"
# Informações da Rede Virtual do AKS
vNetName="vnet-h-wus-01"
# Informações do Cluster AKS
aksClusterName="aks-h-wus-01"
Passo 2: Autenticação usando Azure CLI
Em seguida, autentique-se com sua conta do Azure usando o seguinte comando:
az login --tenant $tenantId
az account set --subscription $subscriptionId
Passo 3: Criação dos Grupos de Recursos
Os grupos de recursos são contêineres lógicos que agrupam recursos relacionados. Execute os comandos abaixo para criar os grupos de recursos:
az group create --name $rgDnsName --location $regionName --tags $tagPairs
az group create --name $rgNetName --location $regionName --tags $tagPairs
az group create --name $rgAksName --location $regionName --tags $tagPairs
Passo 4: Implantação da Rede Virtual
Agora, criaremos a rede virtual (VNet) para o nosso ambiente AKS:
az network vnet create --name $vNetName --resource-group $rgNetName --address-prefixes "10.251.0.0/16" --location $regionName --subnet-name "snet-vnet-h-wus-aks-01" --subnet-prefixes "10.251.10.0/23" --tags $tagPairs
az network vnet subnet create --name "snet-vnet-h-wus-agic-01" --resource-group $rgNetName --vnet-name $vNetName --address-prefix "10.251.20.0/24"
Passo 5: Implantação do Azure App Service Domain
O Azure App Service Domain permite que você gerencie domínios personalizados para os serviços do Azure. Vamos criar um domínio para o Application Gateway:
# Generate tree random characters
randomCharcters=$(generate_random_char() { chars="abcdefghijklmnopqrstuvwxyz0123456789"; echo -n "${chars:$((RANDOM % ${#chars})):1}"; }; generate_random_string() { random_string=""; for ((i = 0; i < 3; i++)); do random_string+=`generate_random_char`; done; echo "$random_string"; }; random_string=$(generate_random_string); echo $random_string)
# Download the contact_info form
wget https://raw.githubusercontent.com/AzureAppServiceCLI/appservice_domains_templates/master/contact_info.json
# Edit and update the contact_info form
vi ./contact_info.json
# Deploy Azure App Service Domain
az appservice domain create --resource-group $rgDnsName --hostname "$randomCharcters$publicDomainName" --contact-info=@'./contact_info.json' --accept-terms --privacy --tags $tagPairs
Passo 6: Implantação do Azure Application Gateway
Agora, implantaremos o Application Gateway para fornecer um balanceador de carga para o AKS:
agicSubnetId=$(az network vnet subnet list --resource-group $rgNetName --vnet-name $vNetName --query "[?name=='snet-vnet-h-wus-agic-01'].id" --output tsv)
az network application-gateway create --name "appgw-$aksClusterName" --resource-group $rgAksName --capacity 1 --frontend-port 80 --http-settings-cookie-based-affinity "Disabled" --http-settings-port 80 --http-settings-protocol "Http" --location $regionName --max-capacity 2 --min-capacity 1 --priority 100 --private-ip-address "10.251.20.10" --public-ip-address "appgw-$aksClusterName-pip" --sku "Standard_v2" --subnet $agicSubnetId --tags $tagPairs
Passo 7: Registro dos Nomes DNS
Agora, registraremos os nomes DNS para que nosso ambiente AKS seja acessível:
agicPubIp=$(az network public-ip show --resource-group $rgAksName --name "appgw-$aksClusterName-pip" --query "ipAddress" --output tsv)
az network dns record-set a add-record --resource-group $rgDnsName --zone-name "$randomCharcters$publicDomainName" --record-set-name "app01" --ipv4-address $agicPubIp --ttl 300
az network dns record-set a add-record --resource-group $rgDnsName --zone-name "$randomCharcters$publicDomainName" --record-set-name "app02" --ipv4-address $agicPubIp --ttl 300
az network dns record-set a add-record --resource-group $rgDnsName --zone-name "$randomCharcters$publicDomainName" --record-set-name "app03" --ipv4-address $agicPubIp --ttl 300
Passo 8: Implantação do AKS Cluster
Agora é o momento de implantar o nosso Cluster AKS e atribuir as permissões de acesso:
aksSubnetId=$(az network vnet subnet list --resource-group $rgNetName --vnet-name $vNetName --query "[?name=='snet-vnet-h-wus-aks-01'].id" --output tsv)
az aks create --name $aksClusterName --resource-group $rgAksName --auto-upgrade-channel "node-image" --disable-local-accounts --dns-name-prefix $aksClusterName --dns-service-ip "10.252.0.10" --enable-aad --enable-addons "azure-keyvault-secrets-provider" --enable-azure-rbac --enable-blob-driver --enable-cluster-autoscaler --enable-managed-identity --generate-ssh-keys --kubernetes-version "1.25.6" --load-balancer-sku "standard" --location $regionName --max-count 2 --max-pods 50 --min-count 1 --network-dataplane "azure" --network-plugin "azure" --network-policy "azure" --node-count 1 --node-osdisk-size 63 --node-vm-size "Standard_D2as_v4" --nodepool-labels "pooltype=system" --nodepool-name "syspool1" --service-cidr "10.252.0.0/16" --tags $tagPairs --tier "free" --vnet-subnet-id $aksSubnetId --yes
az aks nodepool add --cluster-name $aksClusterName --name "usrpool1" --resource-group $rgAksName --enable-cluster-autoscaler --kubernetes-version "1.25.6" --labels "pooltype=user" --max-count 2 --max-pods 50 --min-count 1 --mode "User" --node-count 1 --node-osdisk-size 63 --node-vm-size "Standard_D2as_v4" --tags $tagPairs --vnet-subnet-id $aksSubnetId
Passo 9: Validar os recursos criados
Grupo de Recursos:
Cluster Azure Kubernetes Services (AKS):
App Domain e registros DNS:
Importante:
Perceba que temos alguns registros DNS para os seguintes FQDNs apontando para o IP Público do Application Gateway, o qual iremos integrar com nosso Cluster AKS como Ingress Controller (AGIC):
Atribuindo Permissões AKS RBAC
Para gerenciar o cluster AKS, é necessário atribuir permissões adequadas ao usuário principal. Execute os comandos abaixo para atribuir a função "Azure Kubernetes Service RBAC Cluster Admin" ao usuário:
userId=$(az ad user show --id $userPrincipalName --query "id" --output tsv)
rgId=$(az group show --name $rgAksName --query "id" --output tsv)
az role assignment create --assignee $userId --role "Azure Kubernetes Service RBAC Cluster Admin" --scope $rgId
Visualizando o resultado da atribuição ao usuário ao AKS pelo portal:
Integração AKS com Application Gateway
Agora, vamos integrar o AKS com o Application Gateway para redirecionar o tráfego externo para o cluster AKS:
agicResId=$(az network application-gateway show --name "appgw-$aksClusterName" --resource-group $rgAksName --query "id" --output tsv)
az aks enable-addons --name $aksClusterName --resource-group $rgAksName --addon "ingress-appgw" --appgw-id $agicResId
# Get application gateway id from AKS addon profile
appGatewayId=$(az aks show --name $aksClusterName --resource-group $rgAksName -o tsv --query "addonProfiles.ingressApplicationGateway.config.effectiveApplicationGatewayId")
# Get Application Gateway subnet id
appGatewaySubnetId=$(az network application-gateway show --ids $appGatewayId -o tsv --query "gatewayIPConfigurations[0].subnet.id")
# Get AGIC addon identity
agicAddonIdentity=$(az aks show --name $aksClusterName --resource-group $rgAksName -o tsv --query "addonProfiles.ingressApplicationGateway.identity.clientId")
# Assign network contributor role to AGIC addon identity to subnet that contains the Application Gateway
az role assignment create --assignee $agicAddonIdentity --scope $appGatewaySubnetId --role "Network Contributor"
Revisão das Permissões do AKS Managed Identity (ingressapplicationgateway-cluster-name) para o Application Gateway
Certifique-se de que a Identidade Gerenciada do AKS tenha as permissões adequadas para o Application Gateway:
- Grupo de Recursos do AKS - Função de Leitura (Reader)
- Grupo de Recursos do AKS/Nodes MC (AKS Managed Clusters) - Função de Contribuinte (Contributor)
- Recurso/Grupo de Recursos do Application Gateway - Função de Contribuinte (Contributor)
- Sub-rede do Application Gateway - Função de Contribuinte de Rede (Network Contributor)
A identidade gerenciada “ingressapplicationgateway-cluster-name” foi criada automaticamente durante a integração entre o Application Gateway e o cluster AKS:
Implantação e Exposição de Aplicativos
Antes de prosseguir com a implantação dos aplicativos, certifique-se de ter o utilitário "kubectl" instalado. Se ainda não o tiver, você pode instalá-lo executando o seguinte comando:
az aks install-cli
Em seguida, faça o download das credenciais do cluster AKS para autenticação e a conversão para kubelogin:
az aks get-credentials --resource-group $rgAksName --name $aksClusterName
kubelogin convert-kubeconfig -l azurecli
O AKS está configurado com uma integração completa com o Application Gateway, permitindo o roteamento de tráfego externo para aplicativos implantados no cluster. Você pode usar o utilitário "kubectl" para implantar os aplicativos no cluster e expô-los para acesso externo.
Vamos supor que você tenha um aplicativo ASP.NET Core para implantar. Crie um arquivo de manifesto para o aplicativo e, em seguida, faça a implantação:
vi AgicSampleApp-PUB.yml
apiVersion: v1
kind: Namespace
metadata:
name: aspnetapp01
labels:
name: aspnetapp01
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: aspnetapp01
namespace: aspnetapp01
labels:
app: aspnetapp01
spec:
replicas: 3
selector:
matchLabels:
app: aspnetapp01
template:
metadata:
labels:
app: aspnetapp01
spec:
nodeSelector:
kubernetes.azure.com/mode: user
containers:
- name: aspnetapp-image
image: "mcr.microsoft.com/dotnet/samples:aspnetapp"
ports:
- containerPort: 8080
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: aspnetapp01
namespace: aspnetapp01
spec:
selector:
app: aspnetapp01
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: aspnetapp01
namespace: aspnetapp01
annotations:
kubernetes.io/ingress.class: azure/application-gateway
appgw.ingress.kubernetes.io/backend-path-prefix: "/"
appgw.ingress.kubernetes.io/ssl-redirect: "false"
appgw.ingress.kubernetes.io/connection-draining: "true"
appgw.ingress.kubernetes.io/connection-draining-timeout: "60"
appgw.ingress.kubernetes.io/cookie-based-affinity: "false"
appgw.ingress.kubernetes.io/request-timeout: "30"
appgw.ingress.kubernetes.io/use-private-ip: "false"
appgw.ingress.kubernetes.io/backend-protocol: "http"
spec:
rules:
- http:
paths:
- path: /
backend:
service:
name: aspnetapp01
port:
number: 80
pathType: Prefix
Salve o conteúdo acima em um arquivo chamado AgicSampleApp-PUB.yml. Em seguida, implante o aplicativo com o seguinte comando:
kubectl apply -f AgicSampleApp-PUB.yml
Verifique o ingress criado através do comando abaixo:
kubectl get ingress aspnetapp01 -n aspnetapp01
NAME CLASS HOSTS ADDRESS PORTS AGE
aspnetapp01 <none> * 13.83.132.111 80 101s
Perceba que o ingress recebeu o IP “13.83.132.111” que é o IP Público do nosso Application Gateway como Ingress Controller do nosso cluster. Ao acessar no browser esse endereço IP batemos no Application Gateway/AGIC e ele o redirecionará para a aplicação no AKS.
Agora para a próxima implementação iremos realizar a implantação de uma aplicação com nome de host e certificado SSL com Ingress.
AGIC Sample App with Public IP + Host + Cert (issued by Cert-Manager with Let's Encrypt)
Agora vamos prosseguir com a implantação de um aplicativo no AKS usando o IP público no Application Gateway e um certificado gerenciado pelo Cert Manager.
Cert Manager
Primeiro, vamos implantar o Cert Manager para gerenciar os certificados TLS automaticamente. Cert Manager é uma ferramenta para automatizar a emissão e renovação de certificados TLS. Ele usa a Autoridade de Certificação ACME (Automatic Certificate Management Environment) para emissão e renovação de certificados Let's Encrypt:
# Install the CustomResourceDefinition resources separately
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.3/cert-manager.crds.yaml
# Create the namespace for cert-manager
kubectl create namespace cert-manager
# Label the cert-manager namespace to disable resource validation
kubectl label namespace cert-manager cert-manager.io/disable-validation=true
# Add the Jetstack Helm repository
helm repo add --force-update jetstack https://charts.jetstack.io
# Update your local Helm chart repository cache
helm repo update
# Install the cert-manager Helm chart
# Helm v3+
helm --install cert-manager jetstack/cert-manager --namespace cert-manager --version v1.12.3 --set extraArgs='{--logging-format=json}' --set webhook.extraArgs='{--logging-format=json}' --set cainjector.extraArgs='{--logging-format=json}'
Deploy Cluster Issuer Resource
Agora, vamos criar um objeto Cluster Issuer que nos permitirá emitir certificados automaticamente para o Application Gateway:
vi ClusterIssuer.yml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# You must replace this email address with your own.
# Let's Encrypt uses this to contact you about expiring
# certificates, and issues related to your account.
email: your.email@address.com
# ACME server URL for Let’s Encrypt’s Prod environment.
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource used to store the account's private key.
name: letsencrypt-secret
# Enable the HTTP-01 challenge provider
# you prove ownership of a domain by ensuring that a particular
# file is present at the domain
solvers:
- http01:
ingress:
class: azure/application-gateway
kubectl apply -f ClusterIssuer.yml
Crie o arquivo de manifesto para implantação da aplicação com SSL
Agora, vamos criar um arquivo de manifesto para implantar nosso aplicativo no AKS usando um IP privado no Application Gateway e um certificado gerenciado pelo Cert Manager.
Crie o arquivo AgicSampleApp-PUB-Host-Cert-LE.yml e adicione o seguinte conteúdo:
apiVersion: v1
kind: Namespace
metadata:
name: aspnetapp03
labels:
name: aspnetapp03
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: aspnetapp03
namespace: aspnetapp03
labels:
app: aspnetapp03
spec:
replicas: 3
selector:
matchLabels:
app: aspnetapp03
template:
metadata:
labels:
app: aspnetapp03
spec:
nodeSelector:
kubernetes.azure.com/mode: user
containers:
- name: aspnetapp-image
image: "mcr.microsoft.com/dotnet/samples:aspnetapp"
ports:
- containerPort: 8080
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: aspnetapp03
namespace: aspnetapp03
spec:
selector:
app: aspnetapp03
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: aspnetapp03
namespace: aspnetapp03
annotations:
kubernetes.io/ingress.class: azure/application-gateway
cert-manager.io/cluster-issuer: letsencrypt-prod
cert-manager.io/acme-challenge-type: http01
appgw.ingress.kubernetes.io/use-private-ip: "false"
appgw.ingress.kubernetes.io/ssl-redirect: "true"
appgw.ingress.kubernetes.io/connection-draining: "true"
appgw.ingress.kubernetes.io/connection-draining-timeout: "60"
appgw.ingress.kubernetes.io/cookie-based-affinity: "false"
appgw.ingress.kubernetes.io/request-timeout: "30"
spec:
tls:
- hosts:
- app03.aksagiclab.com # update domain same as "$randomCharcters$publicDomainName"
secretName: app03.aksagiclab.com-secret # update domain same as "$randomCharcters$publicDomainName"
rules:
- host: app03.aksagiclab.com # update domain same as "$randomCharcters$publicDomainName"
http:
paths:
- path: /
backend:
service:
name: aspnetapp03
port:
number: 80
pathType: Prefix
Realize a implantação do manifesto através do comando abaixo:
kubectl apply -f AgicSampleApp-PUB-Host-Cert-LE.yml
namespace/aspnetapp03 created
deployment.apps/aspnetapp03 created
service/aspnetapp03 created
ingress.networking.k8s.io/aspnetapp03 created
Visualize o ingress no namespace aspnetapp03 através do comando abaixo:
kubectl get ingress -n aspnetapp03
NAME CLASS HOSTS ADDRESS PORTS AGE
aspnetapp03 <none> app03.aksagiclab.com 13.83.132.111 80, 443 76
Acessando a aplicação pelo host "app03.aksagiclab.com" usando SSL:
Conclusão
Neste guia, você aprendeu como implantar um cluster Azure Kubernetes Service (AKS) com integração Azure Active Directory (AAD) e Application Gateway. Com esses recursos habilitados, você pode fornecer uma experiência segura e confiável para seus aplicativos em contêineres, permitindo um gerenciamento mais eficiente, escalabilidade elástica e uma resposta rápida a eventos críticos.
Agora que seu ambiente AKS está configurado e funcionando corretamente, você pode continuar explorando e implantando seus próprios aplicativos em contêineres para aproveitar ao máximo a infraestrutura de nuvem altamente disponível e resiliente que o Microsoft Azure oferece.
Mais informações
- https://learn.microsoft.com/en-us/azure/application-gateway/tutorial-ingress-controller-add-on-new
- https://learn.microsoft.com/en-us/azure/application-gateway/tutorial-ingress-controller-add-on-existing?source=recommendations
- https://learn.microsoft.com/en-us/azure/application-gateway/ingress-controller-annotations
- https://learn.microsoft.com/en-us/azure/application-gateway/ingress-controller-migration
- https://learn.microsoft.com/en-us/azure/application-gateway/ingress-controller-expose-service-over-http-https
- https://learn.microsoft.com/en-us/azure/application-gateway/ingress-controller-letsencrypt-certificate-application-gateway
- https://github.com/Azure/application-gateway-kubernetes-ingress/blob/master/docs/tutorials/tutorial.general.md#expose-services-over-https
- https://github.com/Azure/application-gateway-kubernetes-ingress/blob/master/docs/tutorials/tutorial.e2e-ssl.md
- https://learn.microsoft.com/en-us/azure/application-gateway/ingress-controller-disable-addo
- https://learn.microsoft.com/en-us/azure/aks/internal-lb