通过Kubernetes 外部 DNS控制器来自动管理Azure DNS 和 AKS

前言:

将应用程序及其服务部署到 Kubernetes 集群后,一个问题浮现:如何使用自定义域名访问它?一个简单的解决方案是创建一条 A 记录,将域名指向服务 IP 地址。这可以手动完成,但随着服务数量的增加,扩展起来会非常困难。而使用外部 DNS 可以完全自动化。

什么是Kubernetes 外部 DNS?

外部 DNS 是一个 Kubernetes 控制器,它会监视带有特定注释的新 Ingress 和服务,然后在 Azure DNS 中创建相应的 DNS 记录。它在 Github 上是一个开源项目:https://github.com/kubernetes-sigs/external-dns。它支持 30 多个 DNS 提供商,包括 Azure DNS 和私有 DNS 区域。

外部 DNS Pod 使用以下三种方法之一向 Azure DNS 进行身份验证:

  • 服务主体。
  • Kubelet 托管身份。
  • 由 AAD Pod 身份控制的用户分配的托管身份。

在本教程中,您将使用服务主体 (Service Principal)。

创建带有 Ingress Controller 的 AKS 集群

创建 AKS 集群。

复制代码
$AKS_RG="rg-aks-cluster"
$AKS_NAME="aks-cluster"

az group create -n $AKS_RG -l eastasia

az aks create -g $AKS_RG -n $AKS_NAME `
--kubernetes-version "1.31.1" `
--node-count 3 `
--network-plugin azure

az aks get-credentials -n $AKS_NAME -g $AKS_RG --overwrite-existing

安装 nginx Ingress Controller 以便稍后使用。

复制代码
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

helm repo update

helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx `
--create-namespace `
--namespace ingress-nginx `
--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz

创建 Azure DNS 区域,或使用现有区域

您可以创建一个新的 Azure DNS 区域,可以使用或不使用委托域名。不使用委托域名意味着它将无法公开解析域名。但您仍然可以看到已创建的 DNS 记录。

在本实验中,我使用委托域名:rockwang.cloud。请将其替换为您自己的域名。

复制代码
$DNS_ZONE_NAME="rockwang.cloud"
$DNS_ZONE_RG="rg-azure-dns"

az group create -n $DNS_ZONE_RG -l eastasia

az network dns zone create -g $DNS_ZONE_RG -n $DNS_ZONE_NAME

为 ExternalDNS 创建服务主体

ExternalDNS 将连接到 Azure DNS 以更改其配置。因此,它需要进行身份验证。如前所述,您将使用服务主体。

复制代码
$EXTERNALDNS_SPN_NAME="spn-external-dns-aks"


$DNS_SPN=$(az ad sp create-for-rbac --name $EXTERNALDNS_SPN_NAME)
$EXTERNALDNS_SPN_APP_ID=$(echo $DNS_SPN | jq -r '.appId')
$EXTERNALDNS_SPN_PASSWORD=$(echo $DNS_SPN | jq -r '.password')

为服务主体分配 RBAC

授予服务主体对 Azure DNS 区域的访问权限。

复制代码
# fetch DNS id and RG used to grant access to the service principal
$DNS_ZONE_ID=$(az network dns zone show -n $DNS_ZONE_NAME -g $DNS_ZONE_RG --query "id" -o tsv)
$DNS_ZONE_RG_ID=$(az group show -g $DNS_ZONE_RG --query "id" -o tsv)

# assign reader to the resource group
az role assignment create --role "Reader" --assignee $EXTERNALDNS_SPN_APP_ID --scope $DNS_ZONE_RG_ID

# assign contributor to DNS Zone itself
az role assignment create --role "DNS Zone Contributor" --assignee $EXTERNALDNS_SPN_APP_ID --scope $DNS_ZONE_ID

验证role 的指派

复制代码
az role assignment list --all --assignee $EXTERNALDNS_SPN_APP_ID -o table
# Principal                                         Role                        Scope
# ------------------------------------  --------------------  ----------------------------------------------------------------------------------------------------------------------------------
# 9cc6c0d1-99a3-4d86-9df4-a84df55b8232  Reader                /subscriptions/82f6d75e-85f4-434a-ab74-5dddd9fa8910/resourceGroups/rg-azure-dns
# 9cc6c0d1-99a3-4d86-9df4-a84df55b8232  DNS Zone Contributor  /subscriptions/82f6d75e-85f4-434a-ab74-5dddd9fa8910/resourceGroups/rg-azure-dns/providers/Microsoft.Network/dnszones/rockwang.cloud

为服务主体创建 Kubernetes 密钥

ExternalDNS 需要在名为 azure.json 的 JSON 文件中找到服务主体凭据,该文件保存为 Kubernetes 密钥。

复制代码
cat azure.json
# {
#   "tenantId": "16b3c013-d300-468d-ac64-7eda0820b6d3",
#   "subscriptionId": "82f6d75e-85f4-434a-ab74-5dddd9fa8910",
#   "resourceGroup": "rg-dns-zone-rockwang-cloud",
#   "aadClientId": "9cc6c0d1-99a3-4d86-9df4-a84df55b8232",
#   "aadClientSecret": "LJS8Q~ZeuAPJfE7Hjzy6bYZ8NQ4O5YrlJfATxbL6"
# }

将为密钥文件创建secret

复制代码
kubectl create namespace external-dns
# namespace/external-dns created

kubectl create secret generic azure-config-file -n external-dns --from-file azure.json
# secret/azure-config-file created

验证secret

复制代码
kubectl describe secret azure-config-file -n external-dns
# Name:         azure-config-file
# Namespace:    external-dns
# Labels:       <none>
# Annotations:  <none>
# 
# Type:  Opaque
# 
# Data
# ====
# azure.json:  552 bytes

部署外部 DNS

ExternalDNS 可以通过原始 YAML 清单、Helm Chart 或 Operator 部署。为方便起见,我们将使用官方 YAML 部署,该部署文件位于:https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/azure.md#manifest-for-clusters-with-rbac-enabled-cluster-access。

在部署 YAML 之前,请更改 external-dns.yaml 文件中 ClusterRoleBinding 中的命名空间名称。

复制代码
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: external-dns
rules:
  - apiGroups: [""]
    resources: ["services","endpoints","pods", "nodes"]
    verbs: ["get","watch","list"]
  - apiGroups: ["extensions","networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["get","watch","list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: external-dns-viewer
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
  - kind: ServiceAccount
    name: external-dns
    namespace: external-dns # default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
        - name: external-dns
          image: registry.k8s.io/external-dns/external-dns:v0.13.2
          args:
            - --source=service
            - --source=ingress
            - --provider=azure
            - --txt-prefix=externaldns-
          volumeMounts:
            - name: azure-config-file
              mountPath: /etc/kubernetes
              readOnly: true
      volumes:
        - name: azure-config-file
          secret:
            secretName: azure-config-file

kubectl apply -f external-dns.yaml -n external-dns
# serviceaccount/external-dns created
# clusterrole.rbac.authorization.k8s.io/external-dns created
# clusterrolebinding.rbac.authorization.k8s.io/external-dns-vieYour created
# deployment.apps/external-dns created

将外部 DNS 与 Kubernetes 服务结合使用

您将创建一个类型为 LoadBalancer 的公共服务。这将创建一个新的公共 IP 地址来访问该服务。然后添加一个注释 external-dns.alpha.kubernetes.io/hostname,其值为自定义域名。外部 DNS 将引用此注释,以将 IP 地址添加到 DNS 区域(在本例中为 app01.rockwang.cloud)。

复制代码
# app-lb.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app01
spec:
  selector:
    matchLabels:
      app: app01
  template:
    metadata:
      labels:
        app: app01
    spec:
      containers:
        - image: mcr.microsoft.com/dotnet/samples:aspnetapp
          name: aspnetapp
          ports:
          - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: app01-svc
  annotations:
    external-dns.alpha.kubernetes.io/hostname: app01.rockwang.cloud # external-dns configuration
spec:
  ports:
    - port: 80
      protocol: TCP
      targetPort: 80
  selector:
    app: app01
  type: LoadBalancer

kubectl apply -f app-lb.yaml 
# deployment.apps/nginx created
# service/nginx-svc created

kubectl get pods,svc
# NAME                                              READY   STATUS    RESTARTS   AGE
# pod/app01-67745dc95d-bwzgf      1/1        Running   0                 100s

# NAME                       TYPE                 CLUSTER-IP    EXTERNAL-IP    PORT(S)            AGE
# service/app01-svc    LoadBalancer   10.0.95.113     20.86.202.21     80:31067/TCP   100s
# service/kubernetes   ClusterIP          10.0.0.1           <none>            443/TCP            2m30s

创建通过 Ingress 公开的示例应用

您将通过 Ingress 控制器公开应用。在 Ingress 资源中,您将添加一个配置,供外部 DNS 用来管理 Azure DNS 中的域名。该配置是 Ingress 资源(即主机)的原生配置。

复制代码
# app-ingress.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app02
spec:
  selector:
    matchLabels:
      app: app02
  template:
    metadata:
      labels:
        app: app02
    spec:
      containers:
        - image: mcr.microsoft.com/dotnet/samples:aspnetapp
          name: aspnetapp
          ports:
          - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: app02-svc
spec:
  ports:
    - port: 80
      protocol: TCP
      targetPort: 80
  selector:
    app: app02
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app02-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: app02.rockwang.cloud
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app02-svc
                port:
                  number: 80

kubectl apply -f app-ingress.yaml
# deployment.apps/app02 created
# service/app02-svc created
# ingress.networking.k8s.io/app02-ingress created

kubectl get pods,svc,ingress
# NAME                                             READY   STATUS     RESTARTS   AGE
# pod/app02-9bdd6845f-vh422       1/1          Running   0                 92s

# NAME                       TYPE            CLUSTER-IP    EXTERNAL-IP    PORT(S)        AGE
# service/app02-svc    ClusterIP      10.0.74.196     <none>            80/TCP          92s
# service/kubernetes   ClusterIP      10.0.0.1           <none>            443/TCP        2m30s

# NAME                                                         CLASS   HOSTS                          ADDRESS        PORTS   AGE
# ingress.networking.k8s.io/app02-ingress   nginx    app02.rockwang.cloud   20.73.123.67   80         92s

让我们检查一下 Azure DNS 区域配置。注意,已添加 A 记录,其中包含服务和入口控制器的公共 IP 地址。

通过遵循这些步骤,您可以使用 Azure DNS 和外部 DNS 有效地管理在 AKS 中运行的应用程序的 DNS 记录,从而自动化该过程并确保通过用户友好的域名无缝访问您的服务。

相关推荐
虚伪的空想家33 分钟前
K8S的dashboard部署与访问
云原生·容器·kubernetes·k8s·web·dashboard
BBluster1 小时前
Kubernetes(K8S)入门以及命令指南
云原生·容器·kubernetes
韩zj1 小时前
docker部署Gitlab社区版,步骤以及外网访问出现502的解决方式
docker·容器·gitlab
走上未曾设想的道路1 小时前
gitlab流水线与k8s集群的联通
kubernetes·gitlab
一个向上的运维者6 小时前
详细解读k8s的kind中service与pod的区别
容器·k8s
橙*^O^*安9 小时前
Go 语言基础:变量与常量
运维·开发语言·后端·golang·kubernetes
Miya_Ye10 小时前
Azure AI-102 自学记录
microsoft·flask·azure
Linux运维技术栈11 小时前
【实战+原理】微软云 Azure Database 私有网络接入模式全解析:从子网委派到Private Endpoint
数据库·microsoft·azure
_Walli_11 小时前
k8s集群搭建(三)-------- Dashboard UI
云原生·容器·kubernetes
橙*^O^*安17 小时前
Kubernetes集群部署Jenkins指南
云原生·容器·kubernetes·jenkins·devops