k8s有很多的插件是必须的,我们下面来看看一些重要的组件。

DNS

DNS 有两种配置方式,在 1.3 之前使用 etcd + kube2sky + skydns 的方式,在 1.3 之后可以使用 kubedns + dnsmasq 的方式。从 Kubernetes v1.12 开始,CoreDNS 是推荐的 DNS 服务器,取代了kube-dns。

coredns

CoreDNS 是一个灵活可扩展的 DNS 服务器,可以作为 Kubernetes 集群 DNS。

部署CoreDNS

部署 CoreDNS 需要使用到官方提供的部署文件coredns.yaml.sed。需要修改部署文件service的clusterIP指定coredns的ip,也是我们在kubelet的时候指定的dns的ip:–cluster-dns=10.254.0.2当然对应的配置文件也需要修改为自己需要的配置

Corefile: |
    .:53 {
        errors
        health {
          lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
          pods insecure
          fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . /etc/resolv.conf {
          max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }

配置文件会在下面详细说明,先部署

[root@dev1 bin]# kubectl apply -f coredns.yaml
serviceaccount/coredns created
clusterrole.rbac.authorization.k8s.io/system:coredns created
clusterrolebinding.rbac.authorization.k8s.io/system:coredns created
configmap/coredns created
deployment.apps/coredns created
service/kube-dns created
[root@dev1 bin]# kubectl get pod -n kube-system
NAME                      READY   STATUS    RESTARTS   AGE
coredns-cfc5dfc45-n4c7z   1/1     Running   0          40m

基于kube-dns

已经安装kube-dns,官方提供了一套安装升级的方法, 需要使用到官方提供的两个文件 deploy.shcoredns.yaml.sed(这两个文件已经放入manifest的coredns目录中)。

deploy.sh 是一个用于在已经运行kube-dns的集群中生成运行CoreDNS部署文件(manifest)的工具脚本。它使用 coredns.yaml.sed文件作为模板,创建一个ConfigMap和CoreDNS的deployment,然后更新集群中已有的kube-dns 服务的selector使用CoreDNS的deployment。重用已有的服务并不会在服务的请求中发生冲突。

deploy.sh文件并不会删除kube-dns的deployment或者replication controller。如果要删除kube-dns,你必须在部署CoreDNS后手动的删除kube-dns。

总结为下面的两个命令:

$ ./deploy.sh | kubectl apply -f -
$ kubectl delete --namespace=kube-system deployment kube-dns

建议在部署CoreDNS后删除kube-dns。否则如果CoreDNS和kube-dns同时运行,服务查询可能会随机的在CoreDNS和kube-dns之间产生。这是一段来自官方的部署方案,

实战部署

# kubectl get deployments.apps coredns -n kube-system -oyaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
    k8s-app-vivodns: coredns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: CoreDNS
  name: coredns
  namespace: kube-system
spec:
  progressDeadlineSeconds: 600
  replicas: 10
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      k8s-app: coredns
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        k8s-app: coredns
        k8s-app-vivodns: coredns
    spec:
      containers:
      - args:
        - -conf
        - /etc/coredns/Corefile
        image: coredns/coredns:1.2.2
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 5
          httpGet:
            path: /health
            port: 48080
            scheme: HTTP
          initialDelaySeconds: 60
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 5
        name: coredns
        ports:
        - containerPort: 53
          name: dns
          protocol: UDP
        - containerPort: 53
          name: dns-tcp
          protocol: TCP
        resources:
          limits:
            cpu: "8"
            memory: 4Gi
          requests:
            cpu: "8"
            memory: 4Gi
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /etc/coredns
          name: config-volume
      dnsPolicy: Default
      nodeSelector:
        k8s-app-vivodns: coredns
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      serviceAccount: coredns
      serviceAccountName: coredns
      terminationGracePeriodSeconds: 30
      tolerations:
      - effect: NoSchedule
        key: node-role.kubernetes.io/master
      - key: CriticalAddonsOnly
        operator: Exists
      volumes:
      - configMap:
          defaultMode: 420
          items:
          - key: Corefile
            path: Corefile
          name: coredns-v2
        name: config-volume
status:
  availableReplicas: 10
  conditions:
  - lastTransitionTime: 2020-09-23T08:36:24Z
    lastUpdateTime: 2020-09-23T08:38:59Z
    message: ReplicaSet "coredns-86dc8bf5c7" has successfully progressed.
    reason: NewReplicaSetAvailable
    status: "True"
    type: Progressing
  - lastTransitionTime: 2021-03-16T12:02:31Z
    lastUpdateTime: 2021-03-16T12:02:31Z
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  observedGeneration: 8
  readyReplicas: 10
  replicas: 10
  updatedReplicas: 10


# kubectl get horizontalpodautoscalers.autoscaling --all-namespaces -o wide| grep coredns
kube-system     coredns-hpa                    Deployment/coredns                        1%/25%                         10        20        10         249d


# kubectl get horizontalpodautoscalers.autoscaling coredns-hpa -n kube-system -oyaml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  labels:
    k8s-app: kube-dns
  name: coredns-hpa
  namespace: kube-system
spec:
  maxReplicas: 20
  minReplicas: 10
  scaleTargetRef:
    apiVersion: extensions/v1beta1
    kind: Deployment
    name: coredns
  targetCPUUtilizationPercentage: 25
status:
  currentCPUUtilizationPercentage: 1
  currentReplicas: 10
  desiredReplicas: 10
  lastScaleTime: 2021-01-08T01:39:38Z



# kubectl get pod -n kube-system -o wide | grep coredns
coredns-86dc8bf5c7-4zb9f          1/1     Running             0          100d   10.225.18.19    10.193.28.16
coredns-86dc8bf5c7-695nb          1/1     Running             0          100d   10.225.17.2      10.193.28.15
coredns-86dc8bf5c7-79pf2          1/1     Running             0          100d   10.225.12.29     10.193.28.22
coredns-86dc8bf5c7-87tsb          1/1     Running             0          100d   10.225.18.22    10.193.28.14
coredns-86dc8bf5c7-9pb87          1/1     Running             0          100d   10.225.18.29    10.193.28.14
coredns-86dc8bf5c7-bhhjz          1/1     Running             0          100d   10.225.18.17    10.193.28.16
coredns-86dc8bf5c7-lsztk          1/1     Running             0          100d   10.225.13.11    10.193.28.35
coredns-86dc8bf5c7-vdn82          1/1     Running             0          100d   10.225.62.31     10.194.36.14
coredns-86dc8bf5c7-wn94q          1/1     Running             0          100d   10.225.18.21    10.193.28.16
coredns-86dc8bf5c7-xgswf          1/1     Running             0          100d   10.225.18.20    10.193.28.16
coredns-hostnetwork-7f7fdd47cb-8z6nn             1/1     Running             2          100d   10.193.28.35     10.193.28.35
coredns-hostnetwork-7f7fdd47cb-gp5zh             1/1     Running             0          100d   10.193.28.22     10.193.28.22
coredns-hostnetwork-7f7fdd47cb-sjvxf             1/1     Running             0          100d   10.193.28.19    10.193.28.169


正在使用的就是hpa的coredns

配置文件

CoreDNS 的配置文件是 Corefile 形式的,我们来看一下

[root@localhost ~]# kubectl -n kube-system get cm coredns -oyaml
apiVersion: v1
data:
  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
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2019-06-10T03:19:01Z"
  name: coredns
  namespace: kube-system

1、第一部分

kubernetes cluster.local in-addr.arpa ip6.arpa {
   pods insecure
   upstream
   fallthrough in-addr.arpa ip6.arpa
}

指明 cluster.local 后缀的域名,都是 kubernetes 内部域名,coredns 会监听 service 的变化来维护域名关系,所以cluster.local 相关域名都在这里解析。在这里也实现了启动服务后进行的DNS注册

2、第二部分

proxy . /etc/resolv.conf

proxy 指 coredns 中没有找到记录,则去 /etc/resolv.conf 中的 nameserver 请求解析,而 coredns 容器中的 /etc/resolv.conf 是继承自宿主机的。实际效果是如果不是 k8s 内部域名,就会去默认的 dns 服务器请求解析,并返回给 coredns 的请求者。

3、其他

  • prometheus:CoreDNS 的监控地址为: http://localhost:9153/metrics ,满足 Prometheus 的格式。
  • cache:允许缓存
  • loop:如果找到循环,则检测简单的转发循环并停止 CoreDNS 进程。
  • reload:允许 Corefile 的配置自动更新。在更改 ConfigMap 后两分钟,修改生效
  • loadbalance:这是一个循环 DNS 负载均衡器,可以在答案中随机化 A,AAAA 和 MX 记录的顺序。

基本原理

k8s服务注册

Kubernetes 使用 DNS 作为服务注册表。为了满足这一需要,每个 Kubernetes 集群都会在 kube-system 命名空间中用 Pod 的形式运行一个 DNS 服务,通常称之为集群 DNS。DNS会监听service的变化,每个 Kubernetes 服务都会自动注册到集群 DNS 之中。注册过程大致如下:

  • 向 API Server 用 POST 方式提交一个新的 Service 定义;
  • 这个请求需要经过认证、鉴权以及其它的准入策略检查过程之后才会放行;
  • Service 得到一个 ClusterIP(虚拟 IP 地址),并保存到集群数据仓库;
  • 在集群范围内传播 Service 配置;
  • 集群 DNS 服务得知该 Service 的创建,据此创建必要的 DNS A 记录。DNS 中注册的名称就是 metadata.name,而 ClusterIP 则由 Kubernetes 自行分配。

CoreDNS 实现了一个控制器,会对 API Server 进行监听,一旦发现有新建的 Service 对象,就创建一个从 Service 名称映射到 ClusterIP 的域名记录。这样 Service 就不必自行向 DNS 进行注册,CoreDNS 控制器会关注新创建的 Service 对象,并实现后续的 DNS 过程。

域名解析

在部署 pod 的时候,如果用的是 K8s 集群的 DNS(修改 kubelet 的启动配置项,告诉 kubelet,给每个启动的 pod 设置对应的 DNS 信息,一共有两个参数:–cluster_dns=10.10.10.10 –cluster_domain=cluster.local,分别是 DNS 在集群中的 vip 和域名后缀,要和 DNS rc 中保持一致),那么 kubelet 在起 pause 容器的时候,会将其 DNS 解析配置初始化成集群内的配置resolv.conf。

比如我创建了一个叫 my-nginx 的 deployment,其 pod 中的 resolv.conf 文件如下:

[root@localhost ~]# kubectl exec -it my-nginx-b67c7f44-hsnpv cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

在集群中 pod 之间互相用 svc name 访问的时候,会根据 resolv.conf 文件的 DNS 配置来解析域名,resolv.conf 文件可以由 K8s 指定,也可以通过 pod.spec.dnsConfig 字段自定义,下面来分析具体的过程。

1、nameserver

resolv.conf 文件的第一行 nameserver 指定的是 DNS 服务的 IP,这里就是 coreDNS 的 clusterIP:

[root@localhost ~]# kubectl -n kube-system get svc |grep dns
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   32d

也就是说所有域名的解析,都要经过 coreDNS 的虚拟 IP 10.96.0.10 进行解析,不论是 Kubernetes 内部域名还是外部的域名。

2、search 域

resolv.conf 文件的第二行指定的是 DNS search 域。解析域名的时候,将要访问的域名依次带入 search 域,进行 DNS 查询(不同namespace下的pod,第一个域会有所不同)。比如我要在刚才那个 pod 中访问一个域名为 your-nginx 的服务,其进行的 DNS 域名查询的顺序是:

your-nginx.default.svc.cluster.local. -> your-nginx.svc.cluster.local. -> your-nginx.cluster.local.

3、options

resolv.conf 文件的第三行指定的是其他项,最常见的是 dnots。dnots 指的是如果查询的域名包含的点 “.” 小于 5,则先走 search 域,再用绝对域名;如果查询的域名包含点数大于或等于 5,则先用绝对域名,再走 search 域。K8s 中默认的配置是 5。

也就是说,如果我访问的是 a.b.c.e.f.g ,那么域名查找的顺序如下:

a.b.c.e.f.g. -> a.b.c.e.f.g.default.svc.cluster.local. -> a.b.c.e.f.g.svc.cluster.local. -> a.b.c.e.f.g.cluster.local.

k8s域名的策略

Kubernetes 中,域名的全称,必须是 service-name.namespace.svc.cluster.local 这种模式,服务名,就是Kubernetes中 Service 的名称,namespace就是namespace的名称。

在 Kubernetes 中,Service 和 Pod 都会被分配对应的 DNS A 记录(从域名解析 IP 的记录)。对于 ClusterIP 模式的 Service 来说(比如我们上面的例子),它的 A 记录的格式是:..svc.cluster.local。当你访问这条 A 记录的时候,它解析到的就是该 Service 的 VIP 地址。而对于指定了 clusterIP=None 的 Headless Service 来说,它的 A 记录的格式也是:..svc.cluster.local。但是,当你访问这条 A 记录的时候,它返回的是所有被代理的 Pod 的 IP 地址的集合。当然,如果你的客户端没办法解析这个集合的话,它可能会只会拿到第一个 Pod 的 IP 地址。此外,对于 ClusterIP 模式的 Service 来说,它代理的 Pod 被自动分配的 A 记录的格式是:..pod.cluster.local。这条记录指向 Pod 的 IP 地址。而对 Headless Service 来说,它代理的 Pod 被自动分配的 A 记录的格式是:…svc.cluster.local。这条记录也指向 Pod 的 IP 地址。但如果你为 Pod 指定了 Headless Service,并且 Pod 本身声明了 hostname 和 subdomain 字段,那么这时候 Pod 的 A 记录就会变成:…svc.cluster.local

我们可以使用nslookup命令来解析域名

kubectl exec -i -t dnsutils -- nslookup kubernetes.default
Server:    10.0.0.10
Address 1: 10.0.0.10

Name:      kubernetes.default
Address 1: 10.0.0.1

K8s创建pod的 DNS 策略

Kubernetes 中 Pod 的 DNS 策略有四种类型,这些策略可以在 Pod 规约中的 dnsPolicy 字段设置。

  • Default:Pod 继承所在主机上的 DNS 配置;
  • ClusterFirst:K8s 的默认设置;先在 K8s 集群配置的 coreDNS 中查询,查不到的再去继承自主机的上游 nameserver 中查询;
  • ClusterFirstWithHostNet:对于网络配置为 hostNetwork 的 Pod 而言,其 DNS 配置规则与 ClusterFirst 一致;
  • None:忽略 K8s 环境的 DNS 配置,只认 Pod 的 dnsConfig 设置。

比如下面的示例显示了一个 Pod,其 DNS 策略设置为 “ClusterFirstWithHostNet“, 因为它已将 hostNetwork 设置为 true

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet