部署 Kubernetes 集群日志插件 Fluentd、Elasticsearch、Kibana

目录

  • Kubernetes 日志架构介绍
  • 环境、软件准备
  • 启动 Fluentd
  • 启动 Elasticsearch
  • 启动 Kibana
    • 浏览器添加证书
    • RBAC 认证模式介绍

1、Kubernetes 日志架构介绍

对于任何基础架构或者服务系统,日志重要性不言而喻,当然 Kubernetes 也少不了对 Logging 的支持,集群中各个资源以及服务日志如何很好的集中查看并分析,官方给出了 Cluster-level Logging 的架构,其中就提供使用 EFK 框架作为集群日志解决方案。当然 EFK / ELK 在业内也是相对成熟作为日志集中分析平台,ELK 在 Kubernetes 集群中以 Pod 方式运行,丝毫不影响集群中其他 Pod,而且不需要修改其他 Pod 配置,非常方便。

Kubernetes 官网给出的 Cluster-level Logging 参考架构如下:

从图上可以看出,它是采用 Node Logging Agent 的方式,通过在集群每个节点上部署一个 Agent 代理 Pod 服务,收集该 Node 上的日志并 Push 到后端,当然 Logging agent 是以容器方式运行,并且有权限进入该节点上所有服务容器的日志目录。上边提到的 Logging-agent 就可以采用 Fluentd,而 Logging Backend 就可以采用 Elasticsearch,简单理解就是,通过 Fluentd 作为 Logging-agent 收集并 Push 日志到后端 Elasticsearch,最终通过 Kibana 图形化展现出来。

2、环境、软件准备

部署 Fluentd、Elasticsearch、Kibana 到 Kubernetes 集群中,前提我们需要有一个正常运行的集群服务,这里我采用 kubeadm 搭建的 Kubernetes 集群,具体搭建步骤可以参考我上一篇文章 国内使用 kubeadm 在 Centos 7 搭建 Kubernetes 集群 讲述的比较详细,这里就不做演示了。不过还是要说一下的就是国内翻墙问题,由于这三个服务所需要的 images 在国外,国内用户可以去 Docker Hub 下载指定版本的镜像替代,下载完成后,通过 docker tag ... 命令修改成指定名称的镜像即可。

本次演示所依赖的各个镜像列表如下:

Image Name

Version

Des ( * 必需)

gcr.io/google_containers/elasticsearch

v2.4.1-1

*

gcr.io/google_containers/kibana

v4.6.1-1

*

gcr.io/google_containers/fluentd-elasticsearch

1.22

*

说明一下,这里我没有使用最新版本的镜像,因为我的 Kubernetes 版本为 v1.6.2,所以我选择了Github kubernetes 下该版本对应的组件,回头再试下更新成最新版本的试试看。

可使用下边脚本,分别替换以上镜像。

#!/bin/bash

images=(
    elasticsearch:v2.4.1-1 
    kibana:v4.6.1-1
    fluentd-elasticsearch:1.22)

for imageName in ${images[@]} ; do
    docker pull docker.io/bigwhite/$imageName
    docker tag docker.io/bigwhite/$imageName gcr.io/google_containers/$imageName 
    docker rmi docker.io/bigwhite/$imageName
done   

3、启动 Fluentd

Fluentd 是以 DaemonSet 的形式运行在 Kubernetes 集群中,这样就可以保证集群中每个 Node 上都会启动一个 Fluentd,我们在 Master 节点创建 Fluented 服务,最终会在各个 Node 上运行,可以通过 Yaml 文件的方式。

$ cd /home/wanyang3/k8s/
$ git clone https://github.com/kubernetes/kubernetes.git
$ git checkout v1.6.2
$ cd cluster/addons/fluentd-elasticsearch
$ ls -l *.yaml
-rw-r--r--. 1 root root 1246 11月  1 16:55 es-controller.yaml
-rw-r--r--. 1 root root  382 11月  1 14:44 es-service.yaml
-rw-r--r--. 1 root root 1626 11月  1 14:46 fluentd-es-ds.yaml
-rw-r--r--. 1 root root  986 11月  1 14:46 kibana-controller.yaml
-rw-r--r--. 1 root root  354 11月  1 14:44 kibana-service.yaml

$ kubectl create -f fluentd-es-ds.yaml

命令创建 Fluentd 后,默认会将启动日志输出到 Node 节点的 /var/log/fluented.log 文件里面去。但是,当我们去 Node 节点上执行 tail -f /var/log/fluented.log 时,却发现提示并没有该文件,难道没启动成功?

首先查看一下 Pod 没有创建,通过 kubectl get pods -n kube-system 命令查看并没有Running 或者 Pendding 中的 fluentd,那就是没有创建成功,这是为啥呢?

接着我们查看下 fluentd-es-ds.yaml 信息

$ kubectl get -f fluentd-es-ds.yaml
NAME               DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE-SELECTOR                              AGE
fluentd-es-v1.22   0         0         0         0            0           beta.kubernetes.io/fluentd-ds-ready=true   1m

通过输出我们可以看到,确实没有启动起来,不过我们发现 NODE-SELECTOR 选项为 beta.kubernetes.io/fluentd-ds-ready=true,这下就明白为什么了,原来 fluentd 只会调度设置了标签 beta.kubernetes.io/fluentd-ds-ready=true 的 Node 节点。

不甘心的我,决定看下我集群中 Node 节点是否有这个标签,

$ kubectl describe nodes node0.localdomain
Name:           node0.localdomain
Role:
Labels:         beta.kubernetes.io/arch=amd64
                beta.kubernetes.io/os=linux
                kubernetes.io/hostname=node0.localdomain
...         

好吧,这下死心了,确实没有这个标签,那就给该 Node 补上这个标签,然后重新运行下 fluentd。

# Node 节点打标签
$ kubectl label node node0.localdomain beta.kubernetes.io/fluentd-ds-ready=true 
# 重新运行 fluentd
$ kubectl apply -f fluentd-es-ds.yaml
# 查看 Pod 是否启动成功
$ kubectl get pods -n kube-system
NAME                                         READY     STATUS    RESTARTS   AGE
fluentd-es-v1.22-4g2zl                       1/1       Running   1          5m
...

好了,这下启动成功了,这下在去看下 Node 节点 /var/log/fluented.log 文件,这下有日志输出啦。

$ tail -f /var/log/fluented.log
2017-11-01 08:37:44 +0000 [info]: reading config file path="/etc/td-agent/td-agent.conf"
2017-11-01 08:37:44 +0000 [info]: starting fluentd-0.12.31
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-mixin-config-placeholders' version '0.4.0'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-mixin-plaintextformatter' version '0.2.6'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-plugin-docker_metadata_filter' version '0.1.3'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-plugin-elasticsearch' version '1.5.0'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-plugin-kafka' version '0.4.1'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-plugin-kubernetes_metadata_filter' version '0.24.0'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-plugin-mongo' version '0.7.16'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-plugin-rewrite-tag-filter' version '1.5.5'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-plugin-s3' version '0.8.0'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-plugin-scribe' version '0.10.14'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-plugin-td' version '0.10.29'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-plugin-td-monitoring' version '0.2.2'
2017-11-01 08:37:44 +0000 [info]: gem 'fluent-plugin-webhdfs' version '0.4.2'
2017-11-01 08:37:44 +0000 [info]: gem 'fluentd' version '0.12.31'
2017-11-01 08:37:44 +0000 [info]: adding match pattern="fluent.**" type="null"
...
2017-11-01 08:37:46 +0000 [warn]: /var/log/containers/fluentd-es-v1.22-4g2zl_kube-system_fluentd-es-5632cf09917eda0637a911be6a7bb2738d490216c8f0b56a0f74b4b8c91e191c.log unreadable. It is excluded and would be examined next time.

最后的一行输出又是什么鬼?获取不到容器 fluentd 在 /var/log/containers/fluentd-es-xxxxxx 下的日志输出。果断去该目录 /var/log/containers/ 下看下确实没有任何容器日志文件,好吧,分析觉得很有可能是 Docker 输出日志的位置跟 Fluentd 监听的日志的位置不一致。

$ cat fluentd-es-ds.yaml
...
volumes:
- name: varlog
hostPath:
    path: /var/log
- name: varlibdockercontainers
hostPath:
    path: /var/lib/docker/containers

原来是去 /var/lib/docker/containers/var/log 目录去找日志。我查看那一下机器Docker 的日志驱动配置

$ docker info
...
Logging Driver: journald
Cgroup Driver: systemd
...

原来这个节点 安装的 Docker 默认日志驱动为 journald,那么所有的日志都会通过系统的 journal进行统一处理,输出到 /var/log/messages 下边了。怪不得提示 /var/log/containers/ 下没有任何容器日志文件呢。解决办法就是修改 Docker 的日志驱动为 json-file 方式。

$ vim /etc/sysconfig/docker
增加 OPTIONS='--selinux-enabled --log-driver=json-file --signature-verification=false'

$ systemctl daemon-reload
$ systemctl restart docker

好了,现在去看下 /var/log/containers/fluentd-es-xxxxxx 终于有日志输出了。

2017-11-01 08:49:25 +0000 [info]: reading config file path="/etc/td-agent/td-agent.conf"
2017-11-01 08:49:25 +0000 [info]: starting fluentd-0.12.31
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-mixin-config-placeholders' version '0.4.0'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-mixin-plaintextformatter' version '0.2.6'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-plugin-docker_metadata_filter' version '0.1.3'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-plugin-elasticsearch' version '1.5.0'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-plugin-kafka' version '0.4.1'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-plugin-kubernetes_metadata_filter' version '0.24.0'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-plugin-mongo' version '0.7.16'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-plugin-rewrite-tag-filter' version '1.5.5'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-plugin-s3' version '0.8.0'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-plugin-scribe' version '0.10.14'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-plugin-td' version '0.10.29'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-plugin-td-monitoring' version '0.2.2'
2017-11-01 08:49:25 +0000 [info]: gem 'fluent-plugin-webhdfs' version '0.4.2'
2017-11-01 08:49:25 +0000 [info]: gem 'fluentd' version '0.12.31'
2017-11-01 08:49:25 +0000 [info]: adding match pattern="fluent.**" type="null"
...
2017-11-01 08:49:39 +0000 [warn]: suppressed same stacktrace
2017-11-01 08:49:48 +0000 [warn]: temporarily failed to flush the buffer. next_retry=2017-11-01 08:50:03 +0000 error_class="Fluent::ElasticsearchOutput::ConnectionFailure" error="Can not reach Elasticsearch cluster ({:host=>\"elasticsearch-logging\", :port=>9200, :scheme=>\"http\"})!" plugin_id="object:3ff062495470"

这下输出终于正常了,不过最后输出 error="Can not reach Elasticsearch cluster 这个错误,可以先不用管,因为 Elasticsearch 服务我们还没启动呢,连接不上是正常的,下边启动完毕之后,就正常了。

4、启动 Elasticsearch

经过一番折腾,作为 Logging-agent 的 Fluentd 启动好了,现在要启动作为 Logging Backend 的 Elasticsearch 了,Elasticsearch 的主要作用是将日志信息进行分割,建立索引,配合下边 Kibana 展示数据使用。它也是通过 Yaml 文件创建。

$ kubectl create -f es-controller.yaml
$ kubectl create -f es-service.yaml

$ kubectl get pods --all-namespaces -o wide
NAMESPACE     NAME                                   READY     STATUS    RESTARTS   AGE       IP             NODE
kube-system   elasticsearch-logging-v1-1rt61         1/1       Running   0          10s        10.96.2.28     node0.localdomain
kube-system   elasticsearch-logging-v1-gbq8p         1/1       Running   0          10s        10.96.2.29     node0.localdomain
kube-system   fluentd-es-v1.22-4g2zl                 1/1       Running   1          20m        10.96.2.24     node0.localdomain
...

看样子是启动成功了,不放心还是看下日志输出吧!

$ kubectl logs -f pod/elasticsearch-logging-v1-1rt61 -n kube-system
[2017-11-01 08:58:04,774][INFO ][node                     ] [elasticsearch-logging-v1-1rt61] version[2.4.1], pid[13], build[c67dc32/2016-09-27T18:57:55Z]
[2017-11-01 08:58:04,788][INFO ][node                     ] [elasticsearch-logging-v1-1rt61] initializing ...
[2017-11-01 08:58:06,218][INFO ][plugins                  ] [elasticsearch-logging-v1-1rt61] modules [reindex, lang-expression, lang-groovy], plugins [], sites []
[2017-11-01 08:58:06,367][INFO ][env                      ] [elasticsearch-logging-v1-1rt61] using [1] data paths, mounts [[/data (/dev/mapper/cl-root)]], net usable_space [22.4gb], net total_space [32.7gb], spins? [possibly], types [xfs]
[2017-11-01 08:58:06,367][INFO ][env                      ] [elasticsearch-logging-v1-1rt61] heap size [1015.6mb], compressed ordinary object pointers [true]
[2017-11-01 08:58:12,117][INFO ][node                     ] [elasticsearch-logging-v1-1rt61] initialized
[2017-11-01 08:58:12,117][INFO ][node                     ] [elasticsearch-logging-v1-1rt61] starting ...
[2017-11-01 08:58:12,688][INFO ][transport                ] [elasticsearch-logging-v1-1rt61] publish_address {10.96.2.28:9300}, bound_addresses {[::]:9300}
[2017-11-01 08:58:12,706][INFO ][discovery                ] [elasticsearch-logging-v1-1rt61] kubernetes-logging/9bdsRkUYTCKI5DNKGZXmxw
[2017-11-01 08:58:15,828][INFO ][cluster.service          ] [elasticsearch-logging-v1-1rt61] new_master {elasticsearch-logging-v1-1rt61}{9bdsRkUYTCKI5DNKGZXmxw}{10.96.2.28}{10.96.2.28:9300}{master=true}, added {{elasticsearch-logging-v1-gbq8p}{clJyWOTlRx6qn5nHeFEinw}{10.96.2.29}{10.96.2.29:9300}{master=true},}, reason: zen-disco-join(elected_as_master, [1] joins received)
[2017-11-01 08:58:15,948][INFO ][http                     ] [elasticsearch-logging-v1-1rt61] publish_address {10.96.2.28:9200}, bound_addresses {[::]:9200}
[2017-11-01 08:58:15,948][INFO ][node                     ] [elasticsearch-logging-v1-1rt61] started
[2017-11-01 08:58:16,059][INFO ][gateway                  ] [elasticsearch-logging-v1-1rt61] recovered [0] indices into cluster_state
[2017-11-01 08:58:44,750][INFO ][cluster.metadata         ] [elasticsearch-logging-v1-1rt61] [logstash-2017.11.01] creating index, cause [auto(bulk api)], templates [], shards [5]/[1], mappings []
[2017-11-01 08:58:46,296][INFO ][cluster.routing.allocation] [elasticsearch-logging-v1-1rt61] Cluster health status changed from [RED] to [YELLOW] (reason: [shards started [[logstash-2017.11.01][0], [logstash-2017.11.01][0]] ...]).
[2017-11-01 08:58:46,434][INFO ][cluster.metadata         ] [elasticsearch-logging-v1-1rt61] [logstash-2017.11.01] create_mapping [fluentd]
[2017-11-01 08:58:46,578][INFO ][cluster.metadata         ] [elasticsearch-logging-v1-1rt61] [logstash-2017.11.01] update_mapping [fluentd]
[2017-11-01 08:58:46,697][INFO ][cluster.metadata         ] [elasticsearch-logging-v1-1rt61] [logstash-2017.11.01] update_mapping [fluentd]
[2017-11-01 08:58:47,059][INFO ][cluster.routing.allocation] [elasticsearch-logging-v1-1rt61] Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[logstash-2017.11.01][0]] ...]).

[YELLOW] to [GREEN] 了,妥妥没问题了。在去 Node 节点看下上边安装完 Fluentd 最后的 error 如何了。

$ tail -f /var/log/fluented.log
...
2017-11-01 08:58:44 +0000 [info]: Connection opened to Elasticsearch cluster => {:host=>"elasticsearch-logging", :port=>9200, :scheme=>"http"}
2017-11-01 08:58:47 +0000 [warn]: retry succeeded. plugin_id="object:3ff062495470"
2017-11-01 09:00:26 +0000 [info]: following tail of /var/log/containers/kibana-logging-3757371098-dkjsg_kube-system_kibana-logging-e60bec3a2f87ba6d85952bf4d73f301b912cf797b97ca545340a429747c4351a.log

显示连接成功,这一次还挺顺利的哈。接下来该启动 Kibana 服务来图形化查看收集的日志信息了。

5、启动 Kibana

Kibana 是一个开源的分析与可视化平台,与 Elasticsearch 一起使用的,可以用 Kibana 搜索、查看、交互存放在 Elasticsearch 索引里的数据,很直观的在浏览器页面图形化展示出来,非常方便,它也是通过 Yaml 文件创建。

$ kubectl create -f kibana-controller.yaml
$ kubectl create -f kibana-service.yaml

$ kubectl get pods --all-namespaces -o wide
NAMESPACE     NAME                                         READY     STATUS    RESTARTS   AGE       IP             NODE
kube-system   elasticsearch-logging-v1-1rt61               1/1       Running   0          2m        10.96.2.28     node0.localdomain
kube-system   elasticsearch-logging-v1-gbq8p               1/1       Running   0          2m        10.96.2.29     node0.localdomain
kube-system   fluentd-es-v1.22-4g2zl                       1/1       Running   1          22m       10.96.2.24     node0.localdomain
kube-system   kibana-logging-3757371098-dkjsg              1/1       Running   0          8s        10.96.2.30     node0.localdomain

显示启动成功,查看下日志输出,显示需要花费几分钟优化缓存 Kibana 和状态页。

$ kubectl logs -f pod/kibana-logging-3757371098-dkjsg -n kube-system
ELASTICSEARCH_URL=http://elasticsearch-logging:9200
server.basePath: /api/v1/proxy/namespaces/kube-system/services/kibana-logging
{"type":"log","@timestamp":"2017-11-01T09:00:40Z","tags":["info","optimize"],"pid":5,"message":"Optimizing and caching bundles for kibana and statusPage. This may take a few minutes"}

稍等几分钟(有的可能需要更长时间,我的 5 分钟左右),再来查看日志,就显示启动成功了。

$ kubectl logs -f pod/kibana-logging-3757371098-dkjsg -n kube-system
ELASTICSEARCH_URL=http://elasticsearch-logging:9200
server.basePath: /api/v1/proxy/namespaces/kube-system/services/kibana-logging
{"type":"log","@timestamp":"2017-11-01T09:00:40Z","tags":["info","optimize"],"pid":5,"message":"Optimizing and caching bundles for kibana and statusPage. This may take a few minutes"}
{"type":"log","@timestamp":"2017-11-01T09:06:52Z","tags":["info","optimize"],"pid":5,"message":"Optimization of bundles for kibana and statusPage complete in 371.83 seconds"}
{"type":"log","@timestamp":"2017-11-01T09:06:52Z","tags":["status","plugin:kibana@1.0.0","info"],"pid":5,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-11-01T09:06:53Z","tags":["status","plugin:elasticsearch@1.0.0","info"],"pid":5,"state":"yellow","message":"Status changed from uninitialized to yellow - Waiting for Elasticsearch","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-11-01T09:06:53Z","tags":["status","plugin:kbn_vislib_vis_types@1.0.0","info"],"pid":5,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-11-01T09:06:53Z","tags":["status","plugin:markdown_vis@1.0.0","info"],"pid":5,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-11-01T09:06:53Z","tags":["status","plugin:metric_vis@1.0.0","info"],"pid":5,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-11-01T09:06:53Z","tags":["status","plugin:spyModes@1.0.0","info"],"pid":5,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-11-01T09:06:53Z","tags":["status","plugin:statusPage@1.0.0","info"],"pid":5,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-11-01T09:06:53Z","tags":["status","plugin:table_vis@1.0.0","info"],"pid":5,"state":"green","message":"Status changed from uninitialized to green - Ready","prevState":"uninitialized","prevMsg":"uninitialized"}
{"type":"log","@timestamp":"2017-11-01T09:06:53Z","tags":["listening","info"],"pid":5,"message":"Server running at http://0.0.0.0:5601"}
{"type":"log","@timestamp":"2017-11-01T09:06:59Z","tags":["status","plugin:elasticsearch@1.0.0","info"],"pid":5,"state":"yellow","message":"Status changed from yellow to yellow - No existing Kibana index found","prevState":"yellow","prevMsg":"Waiting for Elasticsearch"}
{"type":"log","@timestamp":"2017-11-01T09:07:08Z","tags":["status","plugin:elasticsearch@1.0.0","info"],"pid":5,"state":"green","message":"Status changed from yellow to green - Kibana index ready","prevState":"yellow","prevMsg":"No existing Kibana index found"}

又看到熟悉的 yellow to green 了,妥妥没问题。不过后边同时显示 "prevMsg":"No existing Kibana index found",这个没啥影响,初次启动,Kibana 没有默认给我们创建好索引,我们可以到 Kibana Web 页面去设置索引。但是如何获取 Kibana 服务访问地址呢?可以通过获取 cluster-info 查看

$ kubectl cluster-info
Kubernetes master is running at https://10.236.65.76:6443
Elasticsearch is running at https://10.236.65.76:6443/api/v1/proxy/namespaces/kube-system/services/elasticsearch-logging
Heapster is running at https://10.236.65.76:6443/api/v1/proxy/namespaces/kube-system/services/heapster
Kibana is running at https://10.236.65.76:6443/api/v1/proxy/namespaces/kube-system/services/kibana-logging
KubeDNS is running at https://10.236.65.76:6443/api/v1/proxy/namespaces/kube-system/services/kube-dns
monitoring-grafana is running at https://10.236.65.76:6443/api/v1/proxy/namespaces/kube-system/services/monitoring-grafana
monitoring-influxdb is running at https://10.236.65.76:6443/api/v1/proxy/namespaces/kube-system/services/monitoring-influxdb

也就是 https://<api_server_ip>:<api_server_ secure_port>/api/v1/proxy/namespaces/kube-system/services/kibana-logging 这个访问地址,不过当我们满心欢喜的将这个地址在浏览器中打开时,并没有显示出来页面,而是显示:

这个主要是因为访问地址为 Https,但是没有导入 Kubernetes 证书所致。然而 Kubernetes 证书从哪里得来呢?有两种方式,一种是自己生成然后拷贝到各个机器上,配置 Kubernetes 使用,并导入到浏览器中,另一种使用现成的证书,下边说一下如何导入现成的证书到浏览器中。

使用 kubeadm 安装的 Kubernetes 集群,默认会生成一系列证书供 kubelet 使用,默认生成证书到 /etc/kubernetes/pki/ 目录。

$ /etc/kubernetes/pki/
总用量 48
-rw-r--r--. 1 root root 1237 10月 30 10:19 apiserver.crt
-rw-------. 1 root root 1675 10月 30 10:19 apiserver.key
-rw-r--r--. 1 root root 1099 10月 30 10:19 apiserver-kubelet-client.crt
-rw-------. 1 root root 1679 10月 30 10:19 apiserver-kubelet-client.key
-rw-r--r--. 1 root root 1025 10月 30 10:19 ca.crt
-rw-------. 1 root root 1675 10月 30 10:19 ca.key
-rw-r--r--. 1 root root 1025 10月 30 10:19 front-proxy-ca.crt
-rw-------. 1 root root 1679 10月 30 10:19 front-proxy-ca.key
-rw-r--r--. 1 root root 1050 10月 30 10:19 front-proxy-client.crt
-rw-------. 1 root root 1675 10月 30 10:19 front-proxy-client.key
-rw-------. 1 root root 1675 10月 30 10:19 sa.key
-rw-------. 1 root root  451 10月 30 10:19 sa.pub

这里每个证书和对应的 key 都有不同的用处,我们可以从 kubelet 进程中可以看到这些证书在启动时已经指定加载进去了。

$ ps -ef | grep kubelet
root      2045     1  1 11月02 ?      00:31:53 /usr/bin/kubelet --kubeconfig=/etc/kubernetes/kubelet.conf --require-kubeconfig=true --pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin --cluster-dns=10.96.0.10 --cluster-domain=cluster.local --authorization-mode=Webhook --client-ca-file=/etc/kubernetes/pki/ca.crt --cgroup-driver=systemd
root      5139  5126  1 08:50 ?        00:05:33 kube-controller-manager --use-service-account-credentials=true --kubeconfig=/etc/kubernetes/controller-manager.conf --root-ca-file=/etc/kubernetes/pki/ca.crt --service-account-private-key-file=/etc/kubernetes/pki/sa.key --cluster-signing-key-file=/etc/kubernetes/pki/ca.key --address=127.0.0.1 --insecure-experimental-approve-all-kubelet-csrs-for-group=system:bootstrappers --controllers=*,bootstrapsigner,tokencleaner --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt --leader-elect=true --allocate-node-cidrs=true --cluster-cidr=10.96.0.0/12
root      5704  5692  0 08:52 ?        00:03:24 kube-apiserver --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key --secure-port=6443 --storage-backend=etcd3 --requestheader-allowed-names=front-proxy-client --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --requestheader-group-headers=X-Remote-Group --requestheader-extra-headers-prefix=X-Remote-Extra- --service-cluster-ip-range=10.96.0.0/12 --service-account-key-file=/etc/kubernetes/pki/sa.pub --client-ca-file=/etc/kubernetes/pki/ca.crt --tls-cert-file=/etc/kubernetes/pki/apiserver.crt --allow-privileged=true --requestheader-username-headers=X-Remote-User --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt --tls-private-key-file=/etc/kubernetes/pki/apiserver.key --insecure-port=0 --experimental-bootstrap-token-auth=true --authorization-mode=RBAC --advertise-address=10.236.65.76 --etcd-servers=http://127.0.0.1:2379
root     29063 28044  0 14:51 pts/2    00:00:00 grep --color=auto kubelet

这里我们主要看一下 kube-apiserver,毕竟我们大部分操作都是需要请求它来完成,格式化一下:

kube-apiserver --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key 
--secure-port=6443 
--storage-backend=etcd3 
--requestheader-allowed-names=front-proxy-client 
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds 
--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname 
--requestheader-group-headers=X-Remote-Group 
--requestheader-extra-headers-prefix=X-Remote-Extra- 
--service-cluster-ip-range=10.96.0.0/12 
--service-account-key-file=/etc/kubernetes/pki/sa.pub 
--client-ca-file=/etc/kubernetes/pki/ca.crt 
--tls-cert-file=/etc/kubernetes/pki/apiserver.crt 
--allow-privileged=true 
--requestheader-username-headers=X-Remote-User 
--kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt 
--tls-private-key-file=/etc/kubernetes/pki/apiserver.key 
--insecure-port=0 
--experimental-bootstrap-token-auth=true 
--authorization-mode=RBAC 
--advertise-address=10.236.65.76 
--etcd-servers=http://127.0.0.1:2379

从上边可以看出默认开启的是 secure-port 的方式,以及一系列的证书。其中客户端证书 --client-ca-file=/etc/kubernetes/pki/ca.crt 就是我们需要的证书。下边演示下在 Mac 上如何将 ca.crt 证书导入到浏览器中。

5.1 浏览器添加证书

打开 应用程序 —> 实用工具 —> 钥匙串访问,点击 “+” 选择 ca.crt 证书。

不过添加的 Kubernetes 证书,显示此根证书不被信任,可以点击右键 “显示简介”,弹框中点击 “信任”,在使用此证书时这一栏选择 “始终信任” 即可。

此时,重新打开浏览器,或者刷新浏览器,就正常了。

好了,https 证书的问题解决了,这下应该可以了吧。在次打开 https://<api_server_ip>:<api_server_ secure_port>/api/v1/proxy/namespaces/kube-system/services/kibana-logging 这个访问地址,还是没有显示 Kibana 页面,而是显示:

User "system:anonymous" cannot proxy services in the namespace "kube-system".

OMG,这又是为啥。。。原来 Kubernetes 从 v1.6 开始,默认的认证模式变成了 RBAC 方式了,从上边 --authorization-mode=RBAC 也可以看出,那 RBAC 模式又是个啥?

5.2 RBAC 认证模式介绍

从官网文档 RBAC Support in Kubernetes 中的一张图片可以窥探 RBAC 究竟。

从图中我们可以看出 RBAC 中对各个角色进行绑定授权操作。也就是 Role 为 pod-reader 拥有对 Pod 的 get 和 list 权限,然后通过 RoleBinding 操作将该 Role 类型授权给 User、Group 和 ServiceAccount。也就是说,我们在创建 Pod 时,要通过 RoleBinding 操作将具有特定权限的 Role 授权给该 Pod 创建的 User、Group 或 ServiceAccount。我们可以在最新官网 kubernetes-dashboard 1.7 的 Yaml 文件中得到验证。

# kubernetes-dashboard.yaml
# ------------------- Dashboard Service Account ------------------- #

apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kube-system

---
# ------------------- Dashboard Role & Role Binding ------------------- #

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: kubernetes-dashboard-minimal
  namespace: kube-system
rules:
  # Allow Dashboard to create and watch for changes of 'kubernetes-dashboard-key-holder' secret.
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["create", "watch"]
- apiGroups: [""]
  resources: ["secrets"]
  # Allow Dashboard to get, update and delete 'kubernetes-dashboard-key-holder' secret.
  resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs"]
  verbs: ["get", "update", "delete"]
  # Allow Dashboard to get metrics from heapster.
- apiGroups: [""]
  resources: ["services"]
  resourceNames: ["heapster"]
  verbs: ["proxy"]

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: kubernetes-dashboard-minimal
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: kubernetes-dashboard-minimal
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard
  namespace: kube-system

---

从 Yaml 描述文件中大体可以看出,首先创建了一个 kubernetes-dashboard 的 ServiceAccount,然后创建一个 kubernetes-dashboard-minimal 的 Dashboard Role,最后通过 RoleBinding 将 Role:ubernetes-dashboard-minimal 授权给 ServiceAccount:kubernetes-dashboard,而且他们至始至终都在同一个命令空间下 namespace: kube-system。这样就可以完成 RBAC 的认证过程。

同时 Kubernetes API Server 还更新了一个 –anonymous-auth 选项,允许匿名请求访问 secure port,就像我上边访问地址报错一样,属于匿名请求。这样的匿名请求的 username 默认为 “system:anonymous”, 归属的组默认为 “system:unauthenticated”,所以当我们请求上边命名空间为 kube-system 的服务时,是肯定访问不了的。

好吧,原因找到了,还扯了这么多,到底该如何解决这个问题呢?有三种方法解决。

一、修改 Yaml 文件,为 Kibana 创建符合 RBAC 的配置。可以参考上边 kubernetes-dashboard.yaml 方式配置。

二、使用非安全端口访问 kube-apiserver,也即使用 insecure-port 方式访问,这种方式没有任何安全限制。使用该方式需要修改 kubelet apiserver 配置文件 /etc/kubernetes/manifests/kube-apiserver.yaml,指定 insecure-bind-addressinsecure-port

$ vim /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
  containers:
  - command:
    ...
    - --insecure-bind-address=0.0.0.0 
    - --insecure-port=8080
    ...

配置完成后,就可以通过 http://10.236.65.76:8080/api/v1/proxy/namespaces/kube-system/services/kibana-logging 地址访问了,当然这种方式不安全的哈。

三、使用 kubectl proxy 创建代理。这种方式是最简单的,不需要修改 kubelet 配置,只需要启动代理即可。

$ kubectl proxy --address='10.236.65.76' --port=8085 --accept-hosts='^*$'

执行完毕,就可以通过 http://10.236.65.76:8085/api/v1/proxy/namespaces/kube-system/services/kibana-logging 地址访问了。

好了,现在 Kibana 页面也成功打开了,我们需要创建一个索引,解决日志中 "prevMsg":"No existing Kibana index found" 的问题。 点击导航栏 “Setting” -> “Configure an index pattern”,选中 “Index contains time-based events” 选项,“Index name or pattern” 默认为 logstash-* 即可,“Time-field name” 默认 @timestamp,最后点击 “Create” 即可完成索引创建。

稍等一段时间,就可以看到有日志展示出来啦!如上图所示。不过默认显示的栏目只有 Time 和 _source,这看着很不直观,我们可以点开下边任何一个日志记录,将 Table 展示左侧列,有需要添加到展示栏的项,点击右侧类似打开的书的图标,就可以展示了。这里我选择了关键性栏目,如:kubernetes.container_namekubernetes.hostkubernetes.namespace_namelog,这下看起来是不是就直观了很多。

好了,服务都启动起来了,最后我们要验证一下是否集群中 Pod 发生变化时,日志能够实时发送过来。这里简单的启动一个 Pod,我们可以从 Dashboard 中创建一个部署,以示区别我们同时为这个部署指定到一个新的 Namespace 下,还以之前的 redis 为例,指定命名空间为 my-kube,看下启动完毕,在 Kibana 上能否看到对应的日志信息吧。

码字太多了。。。这个就不演示如何在 Dashboard 上创建这个 redis 实例了。看下通过日志收集并分析,最终呈现到 Kibana 上的图吧!证明是可以实时获取到的!

好了,Kibana 其他功能的用法,我还没研究完,等研究后在补充吧!

参考资料

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏blog

利用cert-manager让Ingress启用免费的HTTPS证书

cert-manager 是替代 kube-lego 的一个开源项目,用于在 Kubernetes 集群中自动提供 HTTPS 证书,支持 Let’s Encr...

1394
来自专栏xingoo, 一个梦想做发明家的程序员

Oracle 监听器无法启动(TNS-12537,TNS-12560,TNS-00507)

Oracle启动监听报错,提示 连接中断 [oracle@localhost ~]$ lsnrctl start LSNRCTL for Linux: Ver...

21710
来自专栏ITCloud的专栏

“网红架构师”解决你的Ceph 运维难题-Part1

本文为长篇连续剧,将分多个篇幅发表,主要介绍了从动手部署环境到后期运营故障处理过程中常见的问题,内容由浅入深,是居家旅行运维Ceph的必备良药。

2643
来自专栏云计算教程系列

如何在Ubuntu 14.04上使用Fluentd和ElasticSearch集中Docker日志

Fluentd是一个开源数据收集器,旨在统一日志记录基础架构。它旨在通过简化和扩展日志来收集和存储日志,从而将运营工程师,应用工程师和数据工程师聚集在一起。

900
来自专栏北京马哥教育

使用Heapster和Splunk监控Kubernetes运行性能

作者:naughty Kubernetes已经成为容器编排的事实上的王者,连Docker都已经向K8s女王大人低头。对于Kubernetes的cluster的...

3266
来自专栏Kubernetes

Kubernetes集群安全配置

这两天在梳理Kubernetes集群的安全配置,涉及到各个组件的配置,最终决定画一个图来展现,应该会更清晰。 ? 涉及以下配置: 其他各个组件作为clien...

3066
来自专栏搜云库

使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上

一个用于构建和推送Docker镜像的Maven插件。

1938

将Spring Boot应用程序部署到Bluemix

在之前的博客文章中,我介绍了如何通过Swagger在Spring Boot应用程序中记录REST API。下面我将介绍如何将这些应用程序作为Docker容器部署...

19710
来自专栏恰同学骚年

.NET Core微服务之基于Consul实现服务治理

  Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案,比如 Airbnb的SmartSt...

1444
来自专栏云计算教程系列

如何在Ubuntu上收集Docker日志

当您将Docker容器转移到生产环境中时,您会发现经常需要将日志保留在容器外的地方。Docker提供了一个本机日志驱动程序,可以很容易地收集这些日志并将它们发送...

863

扫码关注云+社区