通过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 记录,从而自动化该过程并确保通过用户友好的域名无缝访问您的服务。

相关推荐
AnnyYoung3 小时前
从Dockerfile 构建docker镜像——保姆级教程
docker·容器·eureka
慧一居士3 小时前
Docker Compose 的详细使用总结、常用命令及配置示例
容器·架构
何怀逸3 小时前
安装 Docker
运维·docker·容器
小马爱打代码3 小时前
K8S - Harbor 镜像仓库部署与 GitLab CI 集成实战
ci/cd·kubernetes·gitlab
裁二尺秋风3 小时前
k8s(11) — 探针和钩子
java·容器·kubernetes
格桑阿sir6 小时前
Kubernetes控制平面组件:Controller Manager 之 内置Controller详解
kubernetes·controller·endpoint·hpa·job·garbege·lease
may_一一6 小时前
Docker中mysql镜像保存与导入
mysql·docker·容器
liuze4086 小时前
使用 docker 安装 nacos3.x
运维·docker·容器
家庭云计算专家7 小时前
一键设置动态域名+ipv6内网直通访问ssh服务-家庭云计算专家
运维·docker·容器·云计算·ssh·onlyoffice