
# CCE容器中域名解析的最佳实践
本文档重点介绍在CCE容器中如何配置域名解析。
#### 服务
- 在创建工作负载（Deployment或ReplicaSet）之前，需要先创建与之相关联的服务。 因为Kubernetes在启动容器时，会为容器提供所有正在运行的服务作为环境变量。 例如，如果存在名为foo的服务，则所有容器将在其初始环境中获得以下变量。
  ```
  FOO_SERVICE_HOST=<the host the Service is running on>
  FOO_SERVICE_PORT=<the port the Service is running on>
  ```
  因此必须在Pod被创建之前创建它想要访问的任何Service， 否则环境变量将不会生效，而使用DNS则没有此限制。
  
- CCE集群提供了CoreDNS插件作为集群中的DNS服务器。DNS服务器为新的Services监视Kubernetes API，并为每个Services创建一组DNS记录。 如果在整个集群中启用了DNS，则所有Pods应该能够自动对Services进行名称解析。
- 除非绝对必要，否则不要为Pod指定hostPort。 将Pod绑定到hostPort时，它会限制Pod可以调度的位置数，因为每个\<hostIP, hostPort, protocol\>组合必须是唯一的。 如果您没有明确指定hostIP和protocol，Kubernetes将使用0.0.0.0作为默认hostIP和TCP作为默认protocol。
如果您只需要访问端口以进行调试，则可以使用apiserver proxy或kubectl port-forward。
如果您明确需要在节点上公开Pod的端口，请在使用hostPort之前考虑使用NodePort服务。
- 避免使用hostNetwork，原因与hostPort相同。
- 当您不需要kube-proxy负载均衡时，使用无头服务headless-services(ClusterIP被设置为None)以便于服务发现。
 
#### DNS
CCE的Kubernetes集群默认提供了一个DNS插件Service，即使用CoreDNS自动为其它Service指派DNS域名。 如果它在集群中处于运行状态，可以通过如下命令来检查：
```
kubectl get services coredns --namespace=kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.0.0.10    <none>        53/UDP,53/TCP   8m
```
如果没有在运行，可以使用kubec describe命令查看这个Pod没有启用的原因。 假设已经有一个Service，它具有一个长久存在的IP， 一个为该IP指派名称的DNS服务器（coredns集群插件），可以通过标准做法，使在集群中的任何Pod都能与该Service通信。可以运行另一个curl应用来进行测试，启用新的Pod并通过进入容器内部curl当前这个Service的域名，查看是否能正确解析域名。当然，有的场景下是无法curl通的，这与接下来的DNS的查找原理与配置有关。
使用CCE提供的托管式Kubernetes创建Pod，Pod的域名解析参数采用了一些默认值，没有开放全部的dnsConfig配置。在使用时候，您需要了解清楚提供的默认配置。典型的一个配置是ndots ，如果您在Pod内访问的域名字符串，**点** 数量在ndots阈值范围内，则被认为是Kubernetes集群内部域名，会被追加 **..svc.cluster.local**后缀。
#### DNS查找原理与规则
DNS域名解析配置文件 /etc/resolv.conf
```
nameserver 10.247.x.x
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:3
```
**参数说明：**
- nameserver：域名解析服务器。
- search：域名的查找后缀规则，查找配置越多，说明域名解析查找匹配次数越多，这里匹配有3个后缀，则查找规则至少6次，因为IPv4，IPv6都要匹配一次。
- options：域名解析选项，多个KV值；其中典型的有ndots ，访问的域名字符串内的点字符数量超过ndots值，则认为是完整域名，直接解析，如不足，则追加 **..svc.cluster.local**后缀。
 
#### Kubernetes的dnsConfig配置说明
- nameservers：将用作Pod的DNS服务器的IP地址列表。最多可以指定3个IP地址。当Pod dnsPolicy设置为" None"时，列表必须至少包含一个IP地址，否则此属性是可选的。列出的服务器将合并到从指定的DNS策略生成的基本名称服务器，并删除重复的地址。
- searches：Pod中主机名查找的DNS搜索域列表。此属性是可选的。指定后，提供的列表将合并到从所选DNS策略生成的基本搜索域名中，并删除重复的域名。Kubernetes最多允许6个搜索域。
- options：可选的对象列表，其中每个对象可以具有name属性（必需）和value属性（可选）。此属性中的内容将合并到从指定的DNS策略生成的选项中，并删除重复的条目。
详情请参考：[Kubernetes官网的dns配置说明](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/)。
#### DnsPolicy域名解析的几种场景应用
POD里的DNS策略可以对每个Pod进行设置，支持三种策略：Default、ClusterFirst、None。
- Default：表示Pod里面的DNS配置继承了宿主机上的DNS配置。简单来说，就是该Pod的DNS配置会跟宿主机完全一致，也就是和node上的dns配置是一样的。
- ClusterFirst：相对于上述的Default，ClusterFirst是完全相反的操作，它会预先把kube-dns（或CoreDNS）的信息当作预设参数写入到该Pod内的DNS配置。ClusterFirst是默认的pod设置，若没有在Pod内特别描述PodPolicy，则会将dnsPolicy预设为ClusterFirst。不过ClusterFirst还有一个冲突，如果您的Pod设置了HostNetwork=true，则ClusterFirst就会被强制转换成Default。
- None：表示会清除Pod预设的DNS配置，当dnsPolicy设置成这个值之后，Kubernetes不会为Pod预先载入任何自身逻辑判断得到的DNS配置。因此若要将dnsPolicy的值设为None，为了避免Pod里面没有配置任何DNS，建议再添加dnsConfig来描述自定义的DNS参数。
请参阅下面的DNS配置场景：
**场景一：采用自定义DNS**
采用自己建的DNS来解析Pods中的应用域名配置，可以参考以下代码配置，此配置在Pod中的DNS可以完全自定义，适用于已经有自己建的DNS，迁移后的应用也不需要去修改相关的配置。
```
apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 1.2.3.4
    searches:
      - ns1.svc.cluster.local
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0
```
**场景2：采用kubernets的DNS插件CoreDNS**
优先使用Kubernetes的DNS服务解析，失败后再使用外部级联的DNS服务解析。
```
apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: ClusterFirst
```
**场景3：采用公网域名解析**
适用于Pods中的域名配置都在公网访问，这样的话Pods中的应用都从外部的DNS中解析对应的域名。
```
apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: Default
```
**场景4：采用HostNet的DNS解析**
如果在Pod中使用hostNetwork:true来配置网络，Pod中运行的应用程序可以直接看到宿主机的网络接口，宿主机所在的局域网上所有网络接口都可以访问到该应用程序，配置如下所示：
```
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
```
如果不加上dnsPolicy: ClusterFirstWithHostNet，即便pod默认使用宿主机的DNS，也会导致容器内不能通过Service名称访问K8s集群中其他Pod。
#### CoreDNS配置
**1、CoreDNS ConfigMap选项**
查看默认的CoreDNS的配置文件：
```
Corefile: |
  .:53 {
      errors
      health
      kubernetes cluster.local in-addr.arpa ip6.arpa {
         pods insecure
         upstream
         fallthrough in-addr.arpa ip6.arpa
      }
      prometheus :9153
      forward . /etc/resolv.conf
      cache 30
      loop
      reload
      loadbalance
```
**参数说明：**
- error：错误记录到stdout。
- health：CoreDNS的运行状况报告为http://localhost:8080/health。
- kubernetes：CoreDNS将根据Kubernetes服务和Pod的IP回复DNS查询。
- prometheus：CoreDNS的度量标准可以在http://localhost:9153/Prometheus格式的指标中找到；可以通过http://localhost:9153/metrics获取prometheus格式的监控数据。
- proxy、forward：任何不在Kubernetes集群域内的查询都将转发到预定义的解析器（/etc/resolv.conf）；本地无法解析后，向上级地址进行查询，默认使用宿主机的/etc/resolv.conf配置。
- cache：启用前端缓存。
- loop：检测简单的转发循环，如果找到循环则停止CoreDNS进程。
- reload：允许自动重新加载已更改的Corefile。编辑ConfigMap配置后，请等待两分钟以使更改生效。
- loadbalance：这是一个循环DNS负载均衡器，可以在答案中随机化A，AAAA和MX记录的顺序。
**2、配置外部DNS**
有些服务不在Kubernetes内部，在内部环境内需要通过DNS去访问，名称后缀为carey.com。
```
carey.com:53 {
        errors
        cache 30
        proxy . 10.150.0.1
    }
```
完整的配置文件：
```
Corefile: |
  .:53 {
      errors
      health
      kubernetes cluster.local in-addr.arpa ip6.arpa {
         pods insecure
         upstream
         fallthrough in-addr.arpa ip6.arpa
      }
      prometheus :9153
      forward . /etc/resolv.conf
      cache 30
      loop
      reload
      loadbalance
  }
  carey.com:53 {
      errors
      cache 30
      proxy . 10.150.0.1
  }
```
当前CCE的插件管理支持配置存根域，相比较直接编辑ConfigMap更加灵活方便，无需关注Pod的域名解析配置场景。
