服务网格中的DNS代理具备缓存DNS代理的功能。当服务网格收到来自应用程序的DNS查询时,Sidecar代理将进行透明地拦截并提供解析能力。本文介绍如何在ASM中启用和使用DNS代理功能。

前提条件

背景信息

ACK集群默认部署了一套DNS服务,为工作负载提供域名解析功能,使得在Kubernetes集群中运行的应用程序可以使用DNS解析来发现集群中的其他服务。

DNS服务器运行在每个Kubernetes集群中,每个Pod都会使用内部DNS服务器进行域名解析。默认情况下DNS请求不会被Sidecar代理拦截,并且每个应用程序都会在打开与其他服务的连接之前尝试解析DNS名称。在ASM中启用DNS代理功能后,收到来自应用程序的DNS查询时,Sidecar代理将进行透明地拦截并提供解析能力,加快域名解析的速度。

启用DNS代理功能

场景一:全局启用DNS代理功能

  1. 登录ASM控制台
  2. 在左侧导航栏,选择服务网格 > 网格管理
  3. 网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理
  4. 在网格详情页面左侧导航栏选择Sidecar管理(数据面) > Sidecar代理配置
  5. 全局页签下单击DNS代理功能,选中启用DNS代理功能,单击更新设置
  6. 重启Pod,使DNS配置在应用中生效。
    1. 登录容器服务管理控制台
    2. 在控制台左侧导航栏中,单击集群
    3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
    4. 在集群管理页左侧导航栏中,选择工作负载 > 容器组
    5. 容器组页面单击目标Pod右侧操作列下的删除
    6. 删除容器组对话框中单击确定
      稍等一段时间,容器重启后配置生效。

场景二:针对特定命名空间启用DNS代理功能

  1. 登录ASM控制台
  2. 在左侧导航栏,选择服务网格 > 网格管理
  3. 网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理
  4. 在网格详情页面左侧导航栏选择Sidecar管理(数据面) > Sidecar代理配置
  5. Sidecar代理配置页面单击命名空间页签。
  6. 选择命名空间,单击DNS代理功能,选中启用DNS代理功能,单击更新设置
  7. 重启Pod,使DNS配置在应用中生效。
    1. 登录容器服务管理控制台
    2. 在控制台左侧导航栏中,单击集群
    3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
    4. 在集群管理页左侧导航栏中,选择工作负载 > 容器组
    5. 容器组页面单击目标Pod右侧操作列下的删除
    6. 删除容器组对话框中单击确定
      稍等一段时间,容器重启后配置生效。

场景三:针对特定Pod启用DNS代理功能

您需要在Pod的YAML文件中添加注释,才能为特定的Pod启用DNS代理功能。

  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,单击集群
  3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  4. 在集群管理页左侧导航栏中,选择工作负载 > 无状态
  5. 无状态页面目标应用右侧操作列下选择更多 > 查看YAML
  6. 编辑YAML对话框中spec参数下添加以下注释,然后单击更新
    annotations:
      proxy.istio.io/config: |
        proxyMetadata:
          ISTIO_META_DNS_CAPTURE: "true"
          ISTIO_META_DNS_AUTO_ALLOCATE: "true"
    注释
  7. 重启Pod,使DNS配置在应用中生效。
    1. 登录容器服务管理控制台
    2. 在控制台左侧导航栏中,单击集群
    3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
    4. 在集群管理页左侧导航栏中,选择工作负载 > 容器组
    5. 容器组页面单击目标Pod右侧操作列下的删除
    6. 删除容器组对话框中单击确定
      稍等一段时间,容器重启后配置生效。

使用DNS代理功能

步骤一:创建服务条目

使用服务条目将aliyun.com添加到服务网格内部维护的服务注册表中。

  1. 登录ASM控制台
  2. 在左侧导航栏,选择服务网格 > 网格管理
  3. 网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理
  4. 在网格详情页面左侧导航栏选择集群与工作负载管理 > 服务条目,然后在右侧页面单击使用YAML创建
  5. 创建页面选择命名空间,选择任意场景模板,将以下内容服务复制到文本框中,然后单击创建
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
     name: test1-mydnsproxying
    spec:
     hosts:
     - aliyun.com
     location: MESH_EXTERNAL
     ports:
     - number: 443
       name: https
       protocol: TLS
     resolution: DNS

步骤二:部署示例应用

  1. 通过kubectl工具连接集群
  2. 使用以下内容,创建名为sleep.yaml的文件。
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: sleep
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: sleep
      labels:
        app: sleep
        service: sleep
    spec:
      ports:
      - port: 80
        name: http
      selector:
        app: sleep
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sleep
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: sleep
      template:
        metadata:
          labels:
            app: sleep
        spec:
          terminationGracePeriodSeconds: 0
          serviceAccountName: sleep
          containers:
          - name: sleep
            image: curlimages/curl
            command: ["/bin/sleep", "3650d"]
            imagePullPolicy: IfNotPresent
            volumeMounts:
            - mountPath: /etc/sleep/tls
              name: secret-volume
          volumes:
          - name: secret-volume
            secret:
              secretName: sleep-secret
              optional: true
  3. 执行以下命令,部署Sleep应用。
    kubectl apply -f sleep.yaml
  4. 执行以下命令,查看Sleep Pod是否成功启动。
    kubectl get pod |grep sleep

    预期输出:

    NAME                       READY   STATUS    RESTARTS   AGE
    sleep-66cd8f684f-nxw8v     2/2     Running   0          16m

步骤三:为Sleep容器启用DNS代理功能

  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,单击集群
  3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  4. 在集群管理页左侧导航栏中,选择工作负载 > 无状态
  5. 无状态页面目标应用右侧操作列下选择更多 > 查看YAML
  6. 编辑YAML对话框中spec参数下添加以下注释,然后单击更新
    annotations:
      proxy.istio.io/config: |
        proxyMetadata:
          ISTIO_META_DNS_CAPTURE: "true"
          ISTIO_META_DNS_AUTO_ALLOCATE: "true"
    注释
  7. 重启Pod,使DNS配置在Sleep应用中生效。
    1. 登录容器服务管理控制台
    2. 在控制台左侧导航栏中,单击集群
    3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
    4. 在集群管理页左侧导航栏中,选择工作负载 > 容器组
    5. 容器组页面单击Sleep容器右侧操作列下的删除
    6. 删除容器组对话框中单击确定
      稍等一段时间,容器重启后配置生效。

步骤四:验证使用DNS代理功能是否成功

  1. 查看istio-init容器的日志。
    1. 登录容器服务管理控制台
    2. 在控制台左侧导航栏中,单击集群
    3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
    4. 在集群管理页左侧导航栏中,选择工作负载 > 容器组
    5. 容器组页面单击Sleep容器的名称。
    6. 在容器详情页面单击日志页签,设置容器istio-init
      可以看到以下日志。
      -A OUTPUT -p udp --dport 53 -d 192.168.0.10/32 -j REDIRECT --to-port 15053
      -A ISTIO_OUTPUT -p tcp --dport 53 -d 192.168.0.10/32 -j REDIRECT --to-ports 15053

      Sidecar代理会拦截来自应用程序容器的所有DNS查询,拦截的方式与通过iptables规则拦截其他类型的流量相同。Istio添加了额外的iptables规则来将所有发送到Kubernetes DNS服务(例如CoreDNS服务)的端口53(TCP和UDP)上的DNS数据包重定向到端口15053。

  2. 查看pilot-agent进程监听的端口。
    1. 在集群管理页左侧导航栏中,选择工作负载 > 容器组
    2. 容器组页面单击Sleep容器右侧操作列下的终端,单击容器:istio-proxy
    3. 在istio-proxy容器中执行以下命令,查看pilot-agent进程监听的端口。
      netstat -anp |grep 15053

      预期输出:

      tcp        0      0 127.0.0.1:15053         0.0.0.0:*               LISTEN      1/pilot-agent
      udp        0      0 127.0.0.1:15053         0.0.0.0:*                           1/pilot-agent

      DNS查询被重定向到在Sidecar代理容器中运行的pilot-agent进程中,可以看到该进程正在监听端口15053。

  3. 访问aliyun.com。
    1. 在集群管理页左侧导航栏中,选择工作负载 > 容器组
    2. 容器组页面单击Sleep容器右侧操作列下的终端,单击容器:sleep
    3. 在Sleep容器中执行以下命令,访问aliyun.com。
      curl -v https://aliyun.com

      预期输出:

      *   Trying 240.240.**.**:443...
      * Connected to aliyun.com (240.240.**.**) port 443 (#0)

      可以看到返回的地址是240.240.**.**,该地址是服务网格自动分配的虚拟IP,而不是真实的公开的IP地址。服务网格使用iptables劫持了对kube-dns的请求,并将请求路由到了Pod中运行的Sidecar Proxy。当应用程序Pod将aliyun.com解析为虚拟IP并发出请求时,虚拟IP将被替换为Sidecar Proxy中解析的实际公共IP地址。

为DNS代理启用调试日志

为DNS代理和pilot-agent启用调试日志后,pilot-agent将记录来自应用程序容器的每个DNS查询。

  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,单击集群
  3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  4. 在集群管理页左侧导航栏中,选择工作负载 > 无状态
  5. 无状态页面目标应用右侧操作列下选择更多 > 查看YAML
  6. 编辑YAML对话框中spec参数下添加以下注释,然后单击更新
    annotations:
      sidecar.istio.io/agentLogLevel: "dns:debug"
    日志注释
  7. 在集群中执行以下命令,查看调试日志。
    kubectl logs -n default sleep-85fdfd8896-2ctq4 -c istio-proxy | grep debug

    预期输出:

    2022-03-28T12:15:05.064562Z    debug    dns    request ;; opcode: QUERY, status: NOERROR, id: 16390
    ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;alibabacloud.com.default.svc.cluster.local.    IN     AAAA
        protocol=udp edns=false id=1c71c9f1-e051-49e9-8d04-d7c82ee****
    2022-03-28T12:15:05.064572Z    debug    dns    request ;; opcode: QUERY, status: NOERROR, id: 16016
    ;; flags: rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;alibabacloud.com.default.svc.cluster.local.    IN     A
        protocol=udp edns=false id=4cec8078-5355-4d1b-b496-ab57367****
    2022-03-28T12:15:05.064593Z    debug    dns    response for hostname "alibabacloud.com.default.svc.cluster.local." (found=true): ;; opcode: QUERY, status: NOERROR, id: 16390
    ;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;alibabacloud.com.default.svc.cluster.local.    IN     AAAA
        protocol=udp edns=false id=1c71c9f1-e051-49e9-8d04-d7c82ee****
    2022-03-28T12:15:05.064614Z    debug    dns    response for hostname "alibabacloud.com.default.svc.cluster.local." (found=true): ;; opcode: QUERY, status: NOERROR, id: 16016
    ;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;alibabacloud.com.default.svc.cluster.local.    IN     A
    
    ;; ANSWER SECTION:
    alibabacloud.com.default.svc.cluster.local.    30    IN    CNAME    alibabacloud.com.
    alibabacloud.com.    30    IN    A    240.240.**.**
        protocol=udp edns=false id=4cec8078-5355-4d1b-b496-ab573670****
                            

    可以在日志中看到found=true,说明DNS查询在本地注册表中找到了域名,并将对域名进行解析。