标签 nginx 下的文章

Kubernetes集群中的Nginx配置热更新方案

Nginx已经是互联网IT业界一个无敌的存在,作为反向代理、负载均衡、Web服务器等多种角色的扮演者,Nginx在全球各个互联网公司落地、开花和结果,Ngnix已经成为了支撑全球互联网应用的一个不可获取的组成部分。

在我们的平台中,Nginx同样被拿来作为服务接入的最前端的反向代理,并且我们的Nginx也是作为一个Service跑在我们的Kubernetes集群中的。Ngnix背后的服务众多,服务的生生死死都要在Nginx上这些服务路由的配置中有所体现,这就要求部署在Kubernetes集群中的Nginx需要有一个合理的配置热更新方案。

Nginx自身是支持配置热更新的,通过nginx -s reload命令可以实现这一点:

# sudo nginx -s reload

# sudo tail -100f /var/log/nginx/error.log
2016/11/18 08:21:03 [notice] 31516#31516: signal process started

这也是诸多nginx热更新方案的基础。

随着Docker容器以及容器集群/云的出现,Nginx也被Dockerize了,Docker中Nginx的配置热更新方案在Jason Wilder这篇文章中有体现,在该方案中,你可以直接使用Jason Wilder开源的Nginx-proxy实现容器中Nginx的配置的热更新。但这个方案并不能直接适用于Kubernetes,而且作者也并没有Plan support k8s

在Kubernetes集群中部署的Nginx,我其实也找到了一个配置热更新的方案,这是普元的一份技术资料《微服务动态路由实现:OpenResty与kubernetes》中提供的,这个方案通过OpenResty与K8s的结合实现了配置热更新。由于我对OpenResty并不熟悉,并且我个人更希望通过Kubernetes自身的一些Feature来实现这个方案,于是我开始了我自己的探索。

一、需求场景和方案原理

我们要实现的就是:当Kubernetes集群中的Service发生变化时,比如新创建一个Service或删除了一个Service,这些Service在Nginx反向代理中的路由配置需要同步更新并生效。因此,这个过程的场景大致如下:

  • 管理员通过命令或程序通过API操作K8s集群创建或删除Service;
  • 监听API Server Event的某个程序获取该Event,并从API Server读取最新Service数据,重新生成/etc/nginx/conf.d/default.conf;
  • /etc/nginx/conf.d/default.conf文件的变动触发文件变更事件,监听该事件的脚本调用“nginx -s reload”命令实现Nginx的配置热更新。

针对这一需求场景,我这里给出一个实现方案,先上图:

img{512x368}

简答说明一下:

  • Nginx作为一个Service部署在Kubernetes集群中,可以有多个Pod副本;
  • 以一个nginx pod为例,该Pod中包含三个Container,分别是init container、nginx container和config-nginx-generator container;
  • 三个Container共同挂载且共享一个Pod volume,emptyDir类型即可,无需持久化的存储卷,三个Container的挂载路径均为/etc/nginx/conf.d;
  • Pod启动时,init container首先启动并访问API Server,获取Service列表,按照一定条件过滤后(比如通过label的key和Value值),初始创建/etc/nginx/conf.d/default.conf。创建成功后,Container退出;
  • nginx container启动,加载配置,开始提供反向代理服务,并通过inotify工具监视/etc/nginx/conf.d/default.conf文件状态变化,一般变化,就执行nginx -s reload热加载最新配置。
  • config-nginx-generator container同时也启动起来,监听API Server的service变更Event,一旦有Event出现,就重新读取API Server中的Service list,并重新生成一份新的default.conf,覆盖old版本 default.conf。

二、环境

由于KubernetesDocker都在Active Develop的过程中,两个项目的变动都很快,因此,特定的Feature(比如k8s的init container)、操作和说明在某些版本是好用的,但对另外一些版本却是不灵光的。这里先把环境确定清楚,避免误导。

OS:
Ubuntu 14.04.4 LTS Kernel:3.19.0-70-generic #78~14.04.1-Ubuntu SMP Fri Sep 23 17:39:18 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

Docker:
# docker version
Client:
 Version:      1.12.2
 API version:  1.24
 Go version:   go1.6.3
 Git commit:   bb80604
 Built:        Tue Oct 11 17:00:50 2016
 OS/Arch:      linux/amd64

Server:
 Version:      1.12.2
 API version:  1.24
 Go version:   go1.6.3
 Git commit:   bb80604
 Built:        Tue Oct 11 17:00:50 2016
 OS/Arch:      linux/amd64

Kubernetes集群:1.3.7

私有镜像仓库:阿里云镜像仓库

三、实现

1、nginx image的创建

nginx image实现了两个功能,一个自然是nginx自身了,另外一个就是监听/etc/nginx/conf.d/default.conf文件的变化,并适时调用nginx -s reload更新nginx配置。在kubernetes的源码目录kubernetes/examples下有一个例子:https-nginx,这里面已经为我们实现了一个基于auto-reload-nginx.sh的Nginx image Dockerfile,我们稍作改造就可以直接使用了:

//Dockerfile

FROM nginx
MAINTAINER Tony Bai <bigwhite.cn@aliyun.com>

COPY auto-reload-nginx.sh /home/auto-reload-nginx.sh
RUN chmod +x /home/auto-reload-nginx.sh

# install inotify
RUN apt-get update && apt-get install -y inotify-tools

基于该Dockefile构建image:

# docker build -t xxxx/nginx

# docker images
REPOSITORY                                             TAG                 IMAGE ID            CREATED             SIZE
xxxx/nginx                                            latest              a1503b1c2b70        42 seconds ago      191.9 MB

官方nginx image基于debian jessie版本构建,apt-get update & install时需要耐心等待一下。

打标签并推送到我们的阿里云私有镜像库

# docker tag a1503b1c2b70 registry.cn-hangzhou.aliyuncs.com/xxxx/nginx

# docker images
REPOSITORY                                             TAG                 IMAGE ID            CREATED             SIZE
xxxx/nginx                                            latest              a1503b1c2b70        12 minutes ago      191.9 MB
registry.cn-hangzhou.aliyuncs.com/xxxx/nginx          latest              a1503b1c2b70        12 minutes ago      191.9 MB

# docker push registry.cn-hangzhou.aliyuncs.com/xxxx/nginx
2、编写Pod yaml

由于init container和config-nginx-generator container在真实场景中都是要与Kubernetes的API Server交互,并生成/etc/nginx/conf.d/default.conf,这需要一个实现过程,在这里我们暂不给出两个Container的具体Dockerfile以及实现功能的实际程序,而是用两个通用docker image,并通过“手动”方式实现它们各自的功能。因此,我们在这一节中就可以给出Nginx Pod的yaml描述文件了:

//nginx-reload-on-k8s.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx-reload-on-k8s
  annotations:
    pod.beta.kubernetes.io/init-containers: '[
      {
           "name": "nginx-reload-on-k8s-init-1",
           "image": "busybox",
           "command": ["wget", "-O", "/etc/nginx/conf.d/index1.html", "http://www.baidu.com"],
           "volumeMounts": [
               {
                  "name": "conf-volume",
                  "mountPath": "/etc/nginx/conf.d"
               }
           ]
      },
      {
           "name": "nginx-reload-on-k8s-init-2",
           "image": "busybox",
           "command": ["wget", "-O", "/etc/nginx/conf.d/index2.html", "http://dict.cn"],
           "volumeMounts": [
               {
                  "name": "conf-volume",
                  "mountPath": "/etc/nginx/conf.d"
               }
           ]
      }
    ]'
spec:
  containers:
  - name: nginx-config-generator
    volumeMounts:
    - mountPath: /etc/nginx/conf.d
      name: conf-volume
    image: registry.cn-hangzhou.aliyuncs.com/xxxx/test:latest
    imagePullPolicy: IfNotPresent
    command:
       - "tail"
       - "-f"
       - "/var/log/bootstrap.log"
  - name: nginx-origin
    volumeMounts:
    - mountPath: /etc/nginx/conf.d
      name: conf-volume
    image: registry.cn-hangzhou.aliyuncs.com/xxxx/nginx:latest
    imagePullPolicy: IfNotPresent
    command: ["/home/auto-reload-nginx.sh"]
    ports:
    - containerPort: 80
  volumes:
  - name: conf-volume
    emptyDir: {}

Yaml中,我们创建了两个init container,分别用于从baidu.com和dict.cn抓取主页,并存储于/etc/nginx/conf.d的下面备用。nginx-config-generator我们使用image xxxx/test,这就是一个基于ubuntu且安装了诸多网络工具的镜像,用于做目标镜像调试的;nginx container用的就是上面push到私有镜像仓库的那个镜像,command则是执行/home/auto-reload-nginx.sh这个脚本,从而启动nginx和通过inotify监控/etc/nginx/conf.d/default.conf文件。

我们来创建这个Pod(注意:只有用kubectl apply命令时,init container才会被创建和执行,如果用kubectl create -f ,那么将忽略init container):

# kubectl apply -f nginx-reload-on-k8s.yaml
pod "nginx-reload-on-k8s" created

# kubectl get pod
NAME                           READY     STATUS             RESTARTS   AGE
nginx-reload-on-k8s            2/2       Running            0          41s

通过describe pod/nginx-reload-on-k8s,我们能看到一些Container创建的详细信息:

# kubectl describe pod/nginx-reload-on-k8s
Name:        nginx-reload-on-k8s
Namespace:    default
Node:        10.46.181.146/10.46.181.146
Start Time:    Thu, 17 Nov 2016 21:39:55 +0800
Labels:        <none>
Status:        Running
IP:        172.16.57.9
... ...

Events:
  FirstSeen    LastSeen    Count    From            SubobjectPath                    Type        Reason        Message
  ---------    --------    -----    ----            -------------                    --------    ------        -------
  57s        57s        1    {default-scheduler }                            Normal        Scheduled    Successfully assigned nginx-reload-on-k8s to 10.46.181.146
  39s        39s        1    {kubelet 10.46.181.146}    spec.initContainers{nginx-reload-on-k8s-init-1}    Normal        Created        Created container with docker id 0e21afb58eee
  39s        39s        1    {kubelet 10.46.181.146}    spec.initContainers{nginx-reload-on-k8s-init-1}    Normal        Started        Started container with docker id 0e21afb58eee
  56s        38s        2    {kubelet 10.46.181.146}    spec.initContainers{nginx-reload-on-k8s-init-1}    Normal        Pulling        pulling image "busybox"
  39s        26s        2    {kubelet 10.46.181.146}    spec.initContainers{nginx-reload-on-k8s-init-1}    Normal        Pulled        Successfully pulled image "busybox"
  26s        26s        1    {kubelet 10.46.181.146}    spec.initContainers{nginx-reload-on-k8s-init-2}    Normal        Created        Created container with docker id 85632ff73ea8
  26s        26s        1    {kubelet 10.46.181.146}    spec.initContainers{nginx-reload-on-k8s-init-2}    Normal        Started        Started container with docker id 85632ff73ea8
  25s        25s        1    {kubelet 10.46.181.146}    spec.containers{nginx-config-generator}        Normal        Pulled        Container image "registry.cn-hangzhou.aliyuncs.com/xxxx/test:latest" already present on machine
  25s        25s        1    {kubelet 10.46.181.146}    spec.containers{nginx-config-generator}        Normal        Created        Created container with docker id 1ce8c6d8a8af
  25s        25s        1    {kubelet 10.46.181.146}    spec.containers{nginx-config-generator}        Normal        Started        Started container with docker id 1ce8c6d8a8af
  25s        25s        1    {kubelet 10.46.181.146}    spec.containers{nginx-origin}            Normal        Pulled        Container image "registry.cn-hangzhou.aliyuncs.com/xxxx/nginx:latest" already present on machine
  25s        25s        1    {kubelet 10.46.181.146}    spec.containers{nginx-origin}            Normal        Created        Created container with docker id 0c692ec28acd
  25s        25s        1    {kubelet 10.46.181.146}    spec.containers{nginx-origin}            Normal        Started        Started container with docker id 0c692ec28acd

... ...

可以看到四个container依次被pull and create。

四、测试

现在我们就来测试一下nginx的reload。

之前的两个init container分别在/etc/nginx/conf.d下创建了index1.html和index2.html,我们就用这两个文件分别作为配置变更前和变更后的首页。

注意:这时我们还没有/etc/nginx/conf.d/default.conf文件,我们在Pod内访问localhost:80将会得到失败结果:

# curl localhost:80
curl: (7) Failed to connect to localhost port 80: Connection refused

我们进入nginx-config-generator,创建/etc/nginx/conf.d/default.conf文件,与此同时,通过docker logs -f 监控nginx-origin容器的日志:

//default.conf

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {
        root   /etc/nginx/conf.d;
        index  index1.html index1.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

我们把/etc/nginx/conf.d/index1.html作为服务站点的首页了。文件创建完毕后,我们同时就可以从nginx-origin容器的日志能看到如下内容:

At 14:07 on 17/11/16, config file update detected.
2016/11/17 14:07:25 [notice] 20#20: signal process started

我们再从Pod中访问localhost:80(注意:Pod中的多个container共享network namespace,通过localhost就可以进行互访):

root@nginx-reload-on-k8s:/etc/nginx# curl localhost:80
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> .... </html>

我们顺利得到index1.html的内容,这说明配置实时生效了。

我们再来“触发”一次配置变更。我们将default.conf中的:

location / {
        root   /etc/nginx/conf.d;
        index  index1.html index1.htm;
    }

改为:

location / {
        root   /etc/nginx/conf.d;
        index  index2.html index2.htm;
    }

保存!

从nginx-origin容器日志可以看到如下输出:

At 14:17 on 17/11/16, config file update detected.
2016/11/17 14:17:46 [notice] 32#32: signal process started

在Pod中再次访问站点首页:

# curl localhost:80
<!DOCTYPE HTML>
<html>
    <head>
        <meta name="renderer" content="webkit"/>
                <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>海词词典_在线词典_在线翻译_海量正版权威词典官方网站</title>
... ...

可以看到配置更新成功,首页换成了dict.cn的首页。

五、测试

通过上述这些“手动”的触发和测试,可以看出这个方案是可行的。并且我们可以看出,这个方案是有一些好处的:

  • 不需要依赖外部持久化存储卷;
  • 通过k8s api server获取当前所有 service列表,通过service label来过滤,无需依赖额外的redis server或etcd服务;

剩下的就是具体init container以及config-generator的实现了。这个留给我以及大家后续去完成^_^。

Kubernetes集群DNS插件安装

上一篇关于Kubernetes集群安装的文章中,我们建立一个最小可用的k8s集群,不过k8s与1.12版本后的内置了集群管理的Docker不同,k8s是一组松耦合的组件组合而成对外提供服务的。除了核心组件,其他组件是以Add-on形式提供的,比如集群内kube-DNSK8s Dashboard等。kube-dns是k8s的重要插件,用于完成集群内部service的注册和发现。随着k8s安装和管理体验的进一步完善,DNS插件势必将成为k8s默认安装的一部分。本篇将在《一篇文章带你了解Kubernetes安装》一文的基础上,进一步探讨DNS组件的安装”套路”^_^以及问题的troubleshooting。

一、安装前提和原理

上文说过,K8s的安装根据Provider的不同而不同,我们这里是基于provider=ubuntu为前提的,使用的安装脚本是浙大团队维护的那套。因此如果你的provider是其他选项,那么这篇文章里所讲述的内容可能不适用。但了解provider=ubuntu下的DNS组件的安装原理,总体上对其他安装方式也是有一定帮助的。

在部署机k8s安装工作目录的cluster/ubuntu下面,除了安装核心组件所用的download-release.sh、util.sh外,我们看到了另外一个脚本deployAddons.sh,这个脚本内容不多,结构也很清晰,大致的执行步骤就是:

init
deploy_dns
deploy_dashboard

可以看出,这个脚本就是用来部署k8s的两个常用插件:dns和dashboard的。进一步分析,发现deployAddons.sh的执行也是基于./cluster/ubuntu/config-default.sh中的配置,相关的几个配置包括:

# Optional: Install cluster DNS.
ENABLE_CLUSTER_DNS="${KUBE_ENABLE_CLUSTER_DNS:-true}"
# DNS_SERVER_IP must be a IP in SERVICE_CLUSTER_IP_RANGE
DNS_SERVER_IP=${DNS_SERVER_IP:-"192.168.3.10"}
DNS_DOMAIN=${DNS_DOMAIN:-"cluster.local"}
DNS_REPLICAS=${DNS_REPLICAS:-1}

deployAddons.sh首先会根据上述配置生成skydns-rc.yaml和skydns-svc.yaml两个k8s描述文件,再通过kubectl create创建dns service。

二、安装k8s DNS

1、试装

为了让deployAddons.sh脚本执行时只进行DNS组件安装,需要先设置一下环境变量:

export KUBE_ENABLE_CLUSTER_UI=false

执行安装脚本:

# KUBERNETES_PROVIDER=ubuntu ./deployAddons.sh
Creating kube-system namespace...
The namespace 'kube-system' is successfully created.

Deploying DNS on Kubernetes
replicationcontroller "kube-dns-v17.1" created
service "kube-dns" created
Kube-dns rc and service is successfully deployed.

似乎很顺利。我们通过kubectl来查看一下(注意:由于DNS服务被创建在了一个名为kube-system的namespace中,kubectl执行时要指定namespace名字,否则将无法查到dns service):

# kubectl --namespace=kube-system get services
NAME                   CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE
kube-dns               192.168.3.10    <none>        53/UDP,53/TCP   1m

root@iZ25cn4xxnvZ:~/k8stest/1.3.7/kubernetes/cluster/ubuntu# kubectl --namespace=kube-system get pods
NAME                                    READY     STATUS              RESTARTS   AGE
kube-dns-v17.1-n4tnj                    0/3       ErrImagePull        0          4m

在查看DNS组件对应的Pod时,发现Ready为0/3,STATUS为”ErrImagePull”,DNS服务并没有真正起来。

2、修改skydns-rc.yaml

我们来修正上面的问题。在cluster/ubuntu下,我们发现多了两个文件:skydns-rc.yaml和skydns-svc.yaml,这两个文件就是deployAddons.sh执行时根据config-default.sh中的配置生成的两个k8s service描述文件,问题就出在skydns-rc.yaml中。在该文件中,我们看到了dns service启动的pod所含的三个容器对应的镜像名字:

gcr.io/google_containers/kubedns-amd64:1.5
gcr.io/google_containers/kube-dnsmasq-amd64:1.3
gcr.io/google_containers/exechealthz-amd64:1.1

在这次安装时,我并没有配置加速器(vpn)。因此在pull gcr.io上的镜像文件时出错了。在没有加速器的情况,我们在docker hub上可以很容易寻找到替代品(由于国内网络连接docker hub慢且经常无法连接,建议先手动pull出这三个替代镜像):

gcr.io/google_containers/kubedns-amd64:1.5
=> chasontang/kubedns-amd64:1.5

gcr.io/google_containers/kube-dnsmasq-amd64:1.3
=> chasontang/kube-dnsmasq-amd64:1.3

gcr.io/google_containers/exechealthz-amd64:1.1
=> chasontang/exechealthz-amd64:1.1

我们需要手工将skydns-rc.yaml中的三个镜像名进行替换。并且为了防止deployAddons.sh重新生成skydns-rc.yaml,我们需要注释掉deployAddons.sh中的下面两行:

#sed -e "s/\\\$DNS_REPLICAS/${DNS_REPLICAS}/g;s/\\\$DNS_DOMAIN/${DNS_DOMAIN}/g;" "${KUBE_ROOT}/cluster/saltbase/salt/kube-dns/skydns-rc.yaml.sed" > skydns-rc.yaml
#sed -e "s/\\\$DNS_SERVER_IP/${DNS_SERVER_IP}/g" "${KUBE_ROOT}/cluster/saltbase/salt/kube-dns/skydns-svc.yaml.sed" > skydns-svc.yaml

删除dns服务:

# kubectl --namespace=kube-system delete rc/kube-dns-v17.1 svc/kube-dns
replicationcontroller "kube-dns-v17.1" deleted
service "kube-dns" deleted

再次执行deployAddons.sh重新部署DNS组件(不赘述)。安装后,我们还是来查看一下是否安装ok,这次我们直接用docker ps查看pod内那三个容器是否都起来了:

# docker ps
CONTAINER ID        IMAGE                                      COMMAND                  CREATED             STATUS              PORTS               NAMES
e8dc52cba2c7        chasontang/exechealthz-amd64:1.1           "/exechealthz '-cmd=n"   7 minutes ago       Up 7 minutes                            k8s_healthz.1a0d495a_kube-dns-v17.1-0zhfp_kube-system_78728001-974c-11e6-ba01-00163e1625a9_b42e68fc
f1b83b442b15        chasontang/kube-dnsmasq-amd64:1.3          "/usr/sbin/dnsmasq --"   7 minutes ago       Up 7 minutes                            k8s_dnsmasq.f16970b7_kube-dns-v17.1-0zhfp_kube-system_78728001-974c-11e6-ba01-00163e1625a9_da111cd4
d9f09b440c6e        gcr.io/google_containers/pause-amd64:3.0   "/pause"                 7 minutes ago       Up 7 minutes                            k8s_POD.a6b39ba7_kube-dns-v17.1-0zhfp_kube-system_78728001-974c-11e6-ba01-00163e1625a9_b198b4a8

似乎kube-dns这个镜像的容器并没有启动成功。docker ps -a印证了这一点:

# docker ps -a
CONTAINER ID        IMAGE                                      COMMAND                  CREATED             STATUS                       PORTS               NAMES
24387772a2a9        chasontang/kubedns-amd64:1.5               "/kube-dns --domain=c"   3 minutes ago       Exited (255) 2 minutes ago                       k8s_kubedns.cdbc8a07_kube-dns-v17.1-0zhfp_kube-system_78728001-974c-11e6-ba01-00163e1625a9_473144a6
3b8bb401ac6f        chasontang/kubedns-amd64:1.5               "/kube-dns --domain=c"   5 minutes ago       Exited (255) 4 minutes ago                       k8s_kubedns.cdbc8a07_kube-dns-v17.1-0zhfp_kube-system_78728001-974c-11e6-ba01-00163e1625a9_cdd57b87

查看一下stop状态下的kube-dns container的容器日志:

# docker logs 24387772a2a9
I1021 05:18:00.982731       1 server.go:91] Using https://192.168.3.1:443 for kubernetes master
I1021 05:18:00.982898       1 server.go:92] Using kubernetes API <nil>
I1021 05:18:00.983810       1 server.go:132] Starting SkyDNS server. Listening on port:10053
I1021 05:18:00.984030       1 server.go:139] skydns: metrics enabled on :/metrics
I1021 05:18:00.984152       1 dns.go:166] Waiting for service: default/kubernetes
I1021 05:18:00.984672       1 logs.go:41] skydns: ready for queries on cluster.local. for tcp://0.0.0.0:10053 [rcache 0]
I1021 05:18:00.984697       1 logs.go:41] skydns: ready for queries on cluster.local. for udp://0.0.0.0:10053 [rcache 0]
I1021 05:18:01.292557       1 dns.go:172] Ignoring error while waiting for service default/kubernetes: the server has asked for the client to provide credentials (get services kubernetes). Sleeping 1s before retrying.
E1021 05:18:01.293232       1 reflector.go:216] pkg/dns/dns.go:155: Failed to list *api.Service: the server has asked for the client to provide credentials (get services)
E1021 05:18:01.293361       1 reflector.go:216] pkg/dns/dns.go:154: Failed to list *api.Endpoints: the server has asked for the client to provide credentials (get endpoints)
I1021 05:18:01.483325       1 dns.go:439] Received DNS Request:kubernetes.default.svc.cluster.local., exact:false
I1021 05:18:01.483390       1 dns.go:539] records:[], retval:[], path:[local cluster svc default kubernetes]
I1021 05:18:01.582598       1 dns.go:439] Received DNS Request:kubernetes.default.svc.cluster.local., exact:false
... ...

I1021 05:19:07.458786       1 dns.go:172] Ignoring error while waiting for service default/kubernetes: the server has asked for the client to provide credentials (get services kubernetes). Sleeping 1s before retrying.
E1021 05:19:07.460465       1 reflector.go:216] pkg/dns/dns.go:154: Failed to list *api.Endpoints: the server has asked for the client to provide credentials (get endpoints)
E1021 05:19:07.462793       1 reflector.go:216] pkg/dns/dns.go:155: Failed to list *api.Service: the server has asked for the client to provide credentials (get services)
F1021 05:19:07.867746       1 server.go:127] Received signal: terminated

从日志上去看,应该是kube-dns去连接apiserver失败,重试一定次数后,退出了。从日志上看,kube-dns视角中的kubernetes api server的地址是:

I1021 05:18:00.982731       1 server.go:91] Using https://192.168.3.1:443 for kubernetes master

而实际上我们的k8s apiserver监听的insecure port是8080,secure port是6443(由于没有显式配置,6443是源码中的默认端口),通过https+443端口访问apiserver毫无疑问将以失败告终。问题找到了,接下来就是如何解决了。

3、指定–kube-master-url

我们看一下kube-dns命令都有哪些可以传入的命令行参数:

# docker run -it chasontang/kubedns-amd64:1.5 kube-dns --help
Usage of /kube-dns:
      --alsologtostderr[=false]: log to standard error as well as files
      --dns-port=53: port on which to serve DNS requests.
      --domain="cluster.local.": domain under which to create names
      --federations=: a comma separated list of the federation names and their corresponding domain names to which this cluster belongs. Example: "myfederation1=example.com,myfederation2=example2.com,myfederation3=example.com"
      --healthz-port=8081: port on which to serve a kube-dns HTTP readiness probe.
      --kube-master-url="": URL to reach kubernetes master. Env variables in this flag will be expanded.
      --kubecfg-file="": Location of kubecfg file for access to kubernetes master service; --kube-master-url overrides the URL part of this; if neither this nor --kube-master-url are provided, defaults to service account tokens
      --log-backtrace-at=:0: when logging hits line file:N, emit a stack trace
      --log-dir="": If non-empty, write log files in this directory
      --log-flush-frequency=5s: Maximum number of seconds between log flushes
      --logtostderr[=true]: log to standard error instead of files
      --stderrthreshold=2: logs at or above this threshold go to stderr
      --v=0: log level for V logs
      --version[=false]: Print version information and quit
      --vmodule=: comma-separated list of pattern=N settings for file-filtered logging

可以看出:–kube-master-url这个命令行选项可以实现我们的诉求。我们需要再次修改一下skydns-rc.yaml:

        args:
        # command = "/kube-dns"
        - --domain=cluster.local.
        - --dns-port=10053
        - --kube-master-url=http://10.47.136.60:8080   # 新增一行

再次重新部署DNS Addon,不赘述。部署后查看kube-dns服务信息:

# kubectl --namespace=kube-system  describe service/kube-dns
Name:            kube-dns
Namespace:        kube-system
Labels:            k8s-app=kube-dns
            kubernetes.io/cluster-service=true
            kubernetes.io/name=KubeDNS
Selector:        k8s-app=kube-dns
Type:            ClusterIP
IP:            192.168.3.10
Port:            dns    53/UDP
Endpoints:        172.16.99.3:53
Port:            dns-tcp    53/TCP
Endpoints:        172.16.99.3:53
Session Affinity:    None
No events

在通过docker logs直接查看kube-dns容器的日志:

docker logs 2f4905510cd2
I1023 11:44:12.997606       1 server.go:91] Using http://10.47.136.60:8080 for kubernetes master
I1023 11:44:13.090820       1 server.go:92] Using kubernetes API v1
I1023 11:44:13.091707       1 server.go:132] Starting SkyDNS server. Listening on port:10053
I1023 11:44:13.091828       1 server.go:139] skydns: metrics enabled on :/metrics
I1023 11:44:13.091952       1 dns.go:166] Waiting for service: default/kubernetes
I1023 11:44:13.094592       1 logs.go:41] skydns: ready for queries on cluster.local. for tcp://0.0.0.0:10053 [rcache 0]
I1023 11:44:13.094606       1 logs.go:41] skydns: ready for queries on cluster.local. for udp://0.0.0.0:10053 [rcache 0]
I1023 11:44:13.104789       1 server.go:101] Setting up Healthz Handler(/readiness, /cache) on port :8081
I1023 11:44:13.105912       1 dns.go:660] DNS Record:&{192.168.3.182 0 10 10  false 30 0  }, hash:6a8187e0
I1023 11:44:13.106033       1 dns.go:660] DNS Record:&{kubernetes-dashboard.kube-system.svc.cluster.local. 0 10 10  false 30 0  }, hash:529066a8
I1023 11:44:13.106120       1 dns.go:660] DNS Record:&{192.168.3.10 0 10 10  false 30 0  }, hash:bdfe50f8
I1023 11:44:13.106193       1 dns.go:660] DNS Record:&{kube-dns.kube-system.svc.cluster.local. 53 10 10  false 30 0  }, hash:fdbb4e78
I1023 11:44:13.106268       1 dns.go:660] DNS Record:&{kube-dns.kube-system.svc.cluster.local. 53 10 10  false 30 0  }, hash:fdbb4e78
I1023 11:44:13.106306       1 dns.go:660] DNS Record:&{kube-dns.kube-system.svc.cluster.local. 0 10 10  false 30 0  }, hash:d1247c4e
I1023 11:44:13.106329       1 dns.go:660] DNS Record:&{192.168.3.1 0 10 10  false 30 0  }, hash:2b11f462
I1023 11:44:13.106350       1 dns.go:660] DNS Record:&{kubernetes.default.svc.cluster.local. 443 10 10  false 30 0  }, hash:c3f6ae26
I1023 11:44:13.106377       1 dns.go:660] DNS Record:&{kubernetes.default.svc.cluster.local. 0 10 10  false 30 0  }, hash:b9b7d845
I1023 11:44:13.106398       1 dns.go:660] DNS Record:&{192.168.3.179 0 10 10  false 30 0  }, hash:d7e0b1e
I1023 11:44:13.106422       1 dns.go:660] DNS Record:&{my-nginx.default.svc.cluster.local. 0 10 10  false 30 0  }, hash:b0f41a92
I1023 11:44:16.083653       1 dns.go:439] Received DNS Request:kubernetes.default.svc.cluster.local., exact:false
I1023 11:44:16.083950       1 dns.go:539] records:[0xc8202c39d0], retval:[{192.168.3.1 0 10 10  false 30 0  /skydns/local/cluster/svc/default/kubernetes/3262313166343632}], path:[local cluster svc default kubernetes]
I1023 11:44:16.084474       1 dns.go:439] Received DNS Request:kubernetes.default.svc.cluster.local., exact:false
I1023 11:44:16.084517       1 dns.go:539] records:[0xc8202c39d0], retval:[{192.168.3.1 0 10 10  false 30 0  /skydns/local/cluster/svc/default/kubernetes/3262313166343632}], path:[local cluster svc default kubernetes]
I1023 11:44:16.085024       1 dns.go:583] Received ReverseRecord Request:1.3.168.192.in-addr.arpa.

通过日志可以看到,apiserver的url是正确的,kube-dns组件没有再输出错误,安装似乎成功了,还需要测试验证一下。

三、测试验证k8s DNS

按照预期,k8s dns组件可以为k8s集群内的service做dns解析。当前k8s集群默认namespace已经部署的服务如下:

# kubectl get services
NAME         CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes   192.168.3.1     <none>        443/TCP   10d
my-nginx     192.168.3.179   <nodes>       80/TCP    6d

我们在k8s集群中的一个myclient容器中尝试去ping和curl my-nginx服务:

ping my-nginx解析成功(找到my-nginx的clusterip: 192.168.3.179):

root@my-nginx-2395715568-gpljv:/# ping my-nginx
PING my-nginx.default.svc.cluster.local (192.168.3.179): 56 data bytes

curl my-nginx服务也得到如下成功结果:

# curl -v my-nginx
* Rebuilt URL to: my-nginx/
* Hostname was NOT found in DNS cache
*   Trying 192.168.3.179...
* Connected to my-nginx (192.168.3.179) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: my-nginx
> Accept: */*
>
< HTTP/1.1 200 OK
* Server nginx/1.10.1 is not blacklisted
< Server: nginx/1.10.1
< Date: Sun, 23 Oct 2016 12:14:01 GMT
< Content-Type: text/html
< Content-Length: 612
< Last-Modified: Tue, 31 May 2016 14:17:02 GMT
< Connection: keep-alive
< ETag: "574d9cde-264"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host my-nginx left intact

客户端容器的dns配置,这应该是k8s安装时采用的默认配置(与config-default.sh有关):

# cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 192.168.3.10
options timeout:1 attempts:1 rotate
options ndots:5

到此,k8s dns组件就安装ok了。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats