Kubernetes 集群性能监控

在 Kubernetes 系统中,使用 cAdvisor 对 Node 所在主机资源和在该 Node 上运行的容器进行监控和性能数据采样。由于 cAdvisor 集成在 Kubelet 中,即运行在每个 Node 上,所以一个 cAdvisor 仅能对一台 Node 进行监控。在大规模容器集群中,我们需要对所有 Node 和全部容器进行性能监控,Kubernetes 使用一套工具来实现集群性能数据的采集、存储和展示:Heapster、InfluxDB 和 Grafana。

Heapster:是对集群中各 Node、Pod 的资源使用数据进行采集的系统,通过访问每个 Node 上 Kubelet 的 API,再通过 Kubelet 调用 cAdvisor 的 API 来采集该节点上所有容器的性能数据。之后 Heapster 进行数据聚合,并将结果保存到后端存储系统中。Heapster 支持多种后端存储系统,包括 memory、InfluxDB、BigQuery、谷歌云平台提供的 Google Cloud Monitoring 和 Google Cloud Logging等。

InfluxDB:是分布式时序数据库(每条记录都带有时间戳属性),主要用于实时数据采集、事件跟踪记录、存储时间图标、原始数据等。InfluxDB 提供 REST API 用于数据的存储和查询。

Grafana:通过 Dashboard 将 InfluxDB 中的时序数据展现成图标或曲线等形式,便于运维人员查看集群的运行状态。

配置 Kubernetes 集群的 ServiceAccount 和 Secret

Heapster 当前版本需要使用 HTTPS 的安全方式与 Kubernetes Master 进行连接,所以需要先进行 ServiceAccount 和 Secret 的创建。如果不使用 Secret,则 Heapster 启动时将会报错,然后 Heapster 容器会被 ReplicationController 反复销毁、创建,无法正常工作。

关于 ServiceAccount 和 Secret 的原理详见http://blog.dingmingk.com/blog/kube_security.html

在进行一下操作时,我们假设在 Kubernetes 集群中没有创建过 Secret(如果之前创建过,则可以先删除 etcd 中与 Secret 相关的键值)。

首先,使用 OpenSSL 工具在 Master 服务器上创建一些证书和私钥相关的文件:

1
2
3
4
5
# openssl genrsa -out ca.key 2048
# openssl req -x509 -new -nodes -key ca.key -subj "/CN=yourcompany.com" -days 5000 -out ca.crt
# openssl genrsa -out server.key 2048
# openssl req -new -key server.key -subj "/CN=kubernetes-master" -out server.csr
# openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 5000

注意,在生成 server.csr 时 -subj 参数中 /CN 指定的名字需为 Master 的主机名。另外,在生成 ca.crt 时 -subj 参数中 /CN 的名字最好与主机名不同,设为相同可能导致对普通 Master 的 HTTPS 访问认证失败。

执行完成后会生成 6 个文件:ca.crt、ca.key、ca.srl、server.crt、server.csr、server.key。将这些文件复制到 /var/run/kubernetes/ 目录中,然后设置kube-apiserver 的启动参数:

1
2
3
--client_ca_file=/var/run/kubernetes/ca.crt
--tls-private-key-file=/var/run/kubernetes/server.key
--tls-cert-file=/var/run/kubernetes/server.crt

之后重启 kube-apiserver 服务。

接下来,给 kube-controller-manager 服务添加以下启动参数:

1
2
--service_account_private_key_file=/var/run/kubernetes/server.key
--root-ca-file=/var/run/kubernetes/ca.crt

然后重启 kube-controller-manager 服务。

在 kube-apiserver 服务成功启动后,系统会自动为每个命名空间创建一个 ServiceAccount 和一个 Secret(包含一个 ca.crt 和一个 token):

1
2
3
$ kubectl get serviceaccounts --all-namespaces
$ kubectl get secrets --all-namespaces
$ kubectl describe secret xxx

之后 ReplicationController 在创建 Pod 时,会生成类型为 Secret 的 Volume 存储卷,并将该 Volume 挂载到 Pod 内的如下目录中:/var/run/secrets/kubernetes.io/serviceaccount。然后,容器内的应用程序就可以使用该 Secret 与 Master 建立 HTTPS 连接了。Pod 的 Volumes 设置和挂载操作由 ReplicationController 和 Kubelet 自动完成,可以通过查看 Pod 的详细信息了解到。

1
$ kubectl get pods kube-dns-v8-xxxxx --namespace=kube-system -o yaml

进入容器,查看/var/run/secrets/kubernetes.io/serviceaccount 目录,可以看到两个文件 ca.crt 和 token,这两个文件就是与 Master 通信时所需的证书和秘钥信息。

部署 Heapster、InfluxDB、Grafana

在 ServiceAccount 和 Secrets 创建完成后,我们就可以创建 Heapster、InfluxDB 和 Grafana 等 ReplicationController 和 Service 了。

heapsster-service.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
labels:
kubernetes.io/cluster-service: "true"
kubernetes.io/name: Heapster
name: heapster
namespace: kube-system
spec:
ports:
- port: 80
targetPort: 8082
selector:
k8s-app: heapster

InfluxDB-service.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Service
metadata:
labels: null
name: monitoring-InfluxDB
namespace: kube-system
spec:
type: NodePort
ports:
- name: http
port: 8083
targetPort: 8083
nodePort: 30083
- name: api
port: 8086
targetPort: 8086
nodePort: 30086
selector:
name: influxGrafana

Grafana-service.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Service
metadata:
labels:
kubernetes.io/name: monitoring-Grafana
kubernetes.io/cluster-service: "true"
name: monitoring-Grafana
namespace: kube-system
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30080
selector:
name: influxGrafana

heapster-controller.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: v1
kind: ReplicationController
metadata:
labels:
k8s-app: heapster
name: heapster
version: v6
name: heapster
namespace: kube-system
spec:
replicas: 1
selector:
name: heapster
k8s-app: heapster
version: v6
template:
metadata:
labels:
k8s-app: heapster
version: v6
spec:
containers:
- name: heapster
image: gcr.io/google_containers/heapster:v0.17.0
command:
- /heapster
- --source=kubernetes:http://192.168.1.128:8080?inClusterConfig=false&kubeletHttps=true&useServiceAccount=true&auth=
- --sink=InfluxDB:http://monitoring-InfluxDB:8086

Heapster 需要设置的参数如下:

  • –source:为配置监控来源。在本例中使用 kubernetes:表示从 Kubernetes Master 获取各 Node 的信息。在 URL 后面的参数部分,修改 kubeletHttps、inClusterConfig、useServiceAccount 的值,并设置 auth 的值为空。URL中可配置的参数如下:
    • IP 地址和端口号:为 Kubernetes Master 的地址。
    • kubeletPort:默认为 10255(Kubelet 服务的只读端口号)。
    • kubeletHttps:是否通过 HTTPS 方式连接 Kubelet,默认为 false。
    • apiVersion: API 版本号,默认为 Kubernetes 系统的版本号,当前为 v1.
    • inClusterConfig:是否使用 Heapster 命名空间中的 ServiceAccount,默认为 true。
    • insecure:是否信任 Kubernetes 证书,默认为 false。
    • auth:客户端认证授权文件,当 ServiceAccount 不可用时对其进行设置。
    • useServiceAccount:是否使用 ServiceAccount,默认为 false。
  • –sink:为配置后端的存储系统,在本例中使用 InfluxDB 系统。

InfluxDB-Grafana-controller.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: v1
kind: ReplicationController
metadata:
labels:
name: influxGrafana
name: infludb-Grafana
namespace: kube-system
spec:
replicas: 1
selector:
name: influxGrafana
template:
metadata:
labels:
name: influxGrafana
spec:
containers:
- name: InfluxDB
image: gcr.io/google_containers/heapster_InfluxDB:v0.3
ports:
- containerPort: 8083
hostPort: 8083
- containerPort: 8086
hostPort: 8086
- name: Grafana
image: gcr.io/google_containers/heapster_Grafana:v0.7
ports:
- containerPort: 8080
hostPort: 8080
env:
- name: INFLUXDB_HOST
value: monitoring-InfluxDB

最后,创建所有 Service 和 RC:

1
2
3
4
5
$ kubectl create -f heapster-service.yaml
$ kubectl create -f InfluxDB-server.yaml
$ kubectl create -f Grafana-service.yaml
$ kubectl create -f InfluxDB-Grafana-controller.yaml
$ kubectl create -f heapster-controller.yaml

通过 kubectl get pods –namespace=kube-system 确认各 Pod 都成功启动了。