有没有办法向 kube-dns 添加任意记录?

Is there a way to add arbitrary records to kube-dns?

我会用一种非常具体的方式来解释我的问题,但我认为这比用抽象的方式解释更好...

比如说,在 Kubernetes 集群之外但在网络中有一个 MongoDB 副本集。副本集所有成员的 ip 地址由应用服务器和数据库服务器中的 /etc/hosts 解析。

在 experiment/transition 阶段,我需要从 kubernetes pods 访问那些 mongo 数据库服务器。 但是,kubernetes 似乎不允许在 pods/containers.

中向 /etc/hosts 添加自定义条目

MongoDB 副本集已经在处理大型数据集,因此无法在集群中创建新的副本集。

因为我使用 GKE,所以我想应该避免更改 kube-dns 命名空间中的任何资源。配置或替换 kube-dns 以适合我的需要是最后一件事。

有没有办法在 Kubernetes 集群中解析自定义主机名的 IP 地址?

这只是一个想法,但如果kube2sky可以读取configmap的一些条目并将它们用作dns记录,那就太好了。 例如repl1.mongo.local: 192.168.10.100.

编辑:我从 https://github.com/kubernetes/kubernetes/issues/12337

中引用了这个问题

更新:2017-07-03 Kunbernetes 1.7 现在支持 Adding entries to Pod /etc/hosts with HostAliases


解决方案不是关于 kube-dns,而是 /etc/hosts。 无论如何,到目前为止,以下技巧似乎有效...

编辑:更改 /etc/hosts 可能与 kubernetes 系统存在竞争条件。让它重试。

1) 创建一个configMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: db-hosts
data:
  hosts: |
    10.0.0.1  db1
    10.0.0.2  db2

2) 添加名为 ensure_hosts.sh.

的脚本
#!/bin/sh                                                                                                           
while true
do
    grep db1 /etc/hosts > /dev/null || cat /mnt/hosts.append/hosts >> /etc/hosts
    sleep 5
done

别忘了chmod a+x ensure_hosts.sh

3) 添加包装器脚本start.sh 你的图片

#!/bin/sh
$(dirname "$(realpath "[=12=]")")/ensure_hosts.sh &
exec your-app args...

别忘了chmod a+x start.sh

4) 将 configmap 用作卷并且 运行 start.sh

apiVersion: extensions/v1beta1
kind: Deployment
...
spec:
  template:
    ...
    spec:
      volumes:
      - name: hosts-volume
        configMap:
          name: db-hosts
      ...
      containers:
        command:
        - ./start.sh
        ...
        volumeMounts:
        - name: hosts-volume
          mountPath: /mnt/hosts.append
        ...

使用 configMap 似乎是设置 DNS 的更好方法,但是当只添加几条记录时(在我看来)它有点重。所以我通过 docker CMD.

执行的 shell 脚本将记录添加到 /etc/hosts

例如:

Docker 文件

...(ignore)
COPY run.sh /tmp/run.sh
CMD bash /tmp/run.sh

run.sh

#!/bin/bash
echo repl1.mongo.local 192.168.10.100 >> /etc/hosts
# some else command...

注意,如果你的运行一个pod中有多个容器,你必须在每个容器中添加脚本,因为kubernetes随机启动容器,/etc/hosts 可能会被另一个容器覆盖(稍后启动)。

作为记录,对于那些不检查引用 github issue 的人的替代解决方案。

您可以在 Kubernetes 中定义一个 "external" 服务,无需指定任何选择器或 ClusterIP。您还必须定义一个指向您的外部 IP 的相应端点。

来自Kubernetes documentation

{
    "kind": "Service",
    "apiVersion": "v1",
    "metadata": {
        "name": "my-service"
    },
    "spec": {
        "ports": [
            {
                "protocol": "TCP",
                "port": 80,
                "targetPort": 9376
            }
        ]
    }
}
{
    "kind": "Endpoints",
    "apiVersion": "v1",
    "metadata": {
        "name": "my-service"
    },
    "subsets": [
        {
            "addresses": [
                { "ip": "1.2.3.4" }
            ],
            "ports": [
                { "port": 9376 }
            ]
        }
    ]
}

有了这个,您可以将容器内的应用程序指向 my-service:9376,流量应该被转发到 1.2.3.4:9376

限制:

  • 使用的 DNS 名称必须只能是字母、数字或破折号。您不能使用 multi-level 个名称 (something.like.this)。这意味着您可能必须修改您的应用程序以仅指向 your-service,而不是 yourservice.domain.tld.
  • 您只能指向特定的 IP,不能指向 DNS 名称。为此,您可以定义一种具有 ExternalName 类型服务的 DNS 别名。

访问 kubernetes 之外的主机或 ips 需要一种外部名称。

以下对我有用。

{
    "kind": "Service",
    "apiVersion": "v1",
    "metadata": {
        "name": "tiny-server-5",
        "namespace": "default"
    },
    "spec": {
        "type": "ExternalName",
        "externalName": "192.168.1.15",
        "ports": [{ "port": 80 }]
    }
}

这个问题现在有 2 种可能的解决方案:

  1. Pod-wise(将更改添加到解析这些域所需的每个 pod)
  2. 集群方式(将更改添加到所有 pods 都可以访问的中心位置,在我们的例子中是 DNS

让我们从 pod-wise 解决方案开始:

从 Kunbernetes 1.7 开始,现在可以直接使用 .spec.hostAliases

将条目添加到 Pod 的 /etc/hosts

例如:将foo.localbar.local解析为127.0.0.1foo.remotebar.remote10.1.2.3,您可以为 Pod 配置 HostAliases .spec.hostAliases:

apiVersion: v1
kind: Pod
metadata:
  name: hostaliases-pod
spec:
  restartPolicy: Never
  hostAliases:
  - ip: "127.0.0.1"
    hostnames:
    - "foo.local"
    - "bar.local"
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
  containers:
  - name: cat-hosts
    image: busybox
    command:
    - cat
    args:
    - "/etc/hosts"

集群解决方案:

从 Kubernetes v1.12 开始,CoreDNS 是推荐的 DNS 服务器,替换 kube-dns. 如果您的集群最初使用 kube-dns,您可能仍然部署了 kube-dns而不是 CoreDNS。我假设您使用 CoreDNS 作为您的 K8S DNS。

在 CoreDNS 中,可以在集群域内添加任意条目,这样所有 pods 将直接从 DNS 解析此条目,而无需更改每个 /etc/hosts 文件每个豆荚。

第一个:

让我们更改 coreos ConfigMap 并添加所需的更改:

kubectl edit cm coredns -n kube-system 

apiVersion: v1
kind: ConfigMap
data:
  Corefile: |
    .:53 {
        errors
        health {
          lameduck 5s
        }
        hosts /etc/coredns/customdomains.db example.org {
          fallthrough
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
          pods insecure
          fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . "/etc/resolv.conf"
        cache 30
        loop
        reload
        loadbalance
    }
  customdomains.db: |
    10.10.1.1 mongo-en-1.example.org
    10.10.1.2 mongo-en-2.example.org
    10.10.1.3 mongo-en-3.example.org
    10.10.1.4 mongo-en-4.example.org

基本上我们添加了两件事:

  1. kubernetes插件之前的hosts插件和使用hosts插件的fallthrough选项来满足我们的情况。

    进一步阐明 fallthrough 选项。任何给定的后端通常是其区域的最终词 - 它要么是 returns 结果,要么是 returns NXDOMAIN 询问。然而,有时这不是我们想要的行为,因此一些插件支持 fallthrough 选项。 当启用 fallthrough 时,当没有找到记录时,插件不会返回 NXDOMAIN,而是通过 请求链下。链下游的后端有机会处理请求,在我们的例子中,后端是 kubernetes.

  2. 我们向 ConfigMap (customdomains.db) 添加了一个新文件,并在其中添加了我们的自定义域 (mongo-en-*.example.org)。

最后一件事是记住将 customdomains.db 文件添加到 CoreDNS pod 模板的 config-volume 中:

kubectl edit -n kube-system deployment coredns
volumes:
        - name: config-volume
          configMap:
            name: coredns
            items:
            - key: Corefile
              path: Corefile
            - key: customdomains.db
              path: customdomains.db

最后让 kubernetes 重新加载 CoreDNS(每个 pod 运行):

$ kubectl rollout restart -n kube-system deployment/coredns

@OxMH 的回答很棒,可以简化为简洁。 CoreDNS 允许您直接在主机插件中指定主机 (https://coredns.io/plugins/hosts/#examples)。

因此可以像这样编辑 ConfigMap:

$ kubectl edit cm coredns -n kube-system 


apiVersion: v1
kind: ConfigMap
data:
  Corefile: |
    .:53 {
        errors
        health {
          lameduck 5s
        }
        hosts {
          10.10.1.1 mongo-en-1.example.org
          10.10.1.2 mongo-en-2.example.org
          10.10.1.3 mongo-en-3.example.org
          10.10.1.4 mongo-en-4.example.org
          fallthrough
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
          pods insecure
          fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . "/etc/resolv.conf"
        cache 30
        loop
        reload
        loadbalance
    }

您仍然需要重新启动 coredns,以便它重新读取配置:

$ kubectl rollout restart -n kube-system deployment/coredns

内联主机文件的内容消除了从 configmap 映射主机文件的需要。两种方法都达到相同的结果,至于要定义主机的位置取决于个人喜好。