监控Kubernetes架构
作者:Gianluca Arbezzano254 / 产品,用例,开发者
2018年3月19日
导航至
在监控Kubernetes架构时,有两个重要点需要您思考。一是关于底层资源,即Kubernetes运行的裸金属。二是与您部署的每个服务、入口和pod相关。为了更好地了解您的集群,您需要从这两个方面获取指标,以便进行比较和参考。我写这篇文章的原因是,在InfluxData,我们正在与Kubernetes打交道,我认为是时候分享一些我们应用到我们集群中的实践了(也因为它们效果很好)。您应该有一个完全专用的命名空间用于监控。我们称之为监控
kubectl create namespace monitoring
不要将其部署在默认命名空间中。一般来说,默认命名空间始终应该是空的。我假设您能够在这里部署InfluxDB和Chronograf在Kubernetes上,否则这篇文章将变成一个不可读的糟糕的YAML文件。
关于持久卷的简单说明。InfluxDB、Kapacitor 和 Chronograf 都将数据存储在磁盘上。这意味着我们需要小心地管理它们。否则,我们的数据会随着容器一起消失。Kubernetes 有一种名为持久卷的资源,可以帮助您根据集群的运行位置挂载卷。我们使用 AWS,并声明 EBS 卷来管理 /var/lib/influxdb
和其他目录。
现在您的系统已经运行,我们可以使用 DeamonSet 在每个节点上部署 Telegraf。这个 Telegraf 代理将负责从主机监控资源,例如 iops、网络、cpu、内存、磁盘和其他服务。为了做到这一点,我们需要从主机共享一些目录,否则 Telegraf 将会监控容器而不是主机。
DaemonSet 是 Kubernetes 的一个资源,它可以在所有节点上自动分配容器。如果您需要部署像我们现在正在做的指标或日志收集器,它会非常有用。
apiVersion: v1
kind: ConfigMap
metadata:
name: telegraf
namespace: monitoring
labels:
k8s-app: telegraf
data:
telegraf.conf: |+
[global_tags]
env = "$ENV"
[agent]
hostname = "$HOSTNAME"
[[outputs.influxdb]]
urls = ["$MONITOR_HOST"] # required
database = "$MONITOR_DATABASE" # required
timeout = "5s"
username = "$MONITOR_USERNAME"
password = "$MONITOR_PASSWORD"
[[inputs.cpu]]
percpu = true
totalcpu = true
collect_cpu_time = false
report_active = false
[[inputs.disk]]
ignore_fs = ["tmpfs", "devtmpfs", "devfs"]
[[inputs.diskio]]
[[inputs.kernel]]
[[inputs.mem]]
[[inputs.processes]]
[[inputs.swap]]
[[inputs.system]]
[[inputs.docker]]
endpoint = "unix:///var/run/docker.sock"
[[inputs.kubernetes]]
url = "http://1.1.1.1:10255"
---
# Section: Daemonset
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: telegraf
namespace: monitoring
labels:
k8s-app: telegraf
spec:
selector:
matchLabels:
name: telegraf
template:
metadata:
labels:
name: telegraf
spec:
containers:
- name: telegraf
image: docker.io/telegraf:1.5.2
resources:
limits:
memory: 500Mi
requests:
cpu: 500m
memory: 500Mi
env:
- name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: "HOST_PROC"
value: "/rootfs/proc"
- name: "HOST_SYS"
value: "/rootfs/sys"
- name: ENV
valueFrom:
secretKeyRef:
name: telegraf
key: env
- name: MONITOR_USERNAME
valueFrom:
secretKeyRef:
name: telegraf
key: monitor_username
- name: MONITOR_PASSWORD
valueFrom:
secretKeyRef:
name: telegraf
key: monitor_password
- name: MONITOR_HOST
valueFrom:
secretKeyRef:
name: telegraf
key: monitor_host
- name: MONITOR_DATABASE
valueFrom:
secretKeyRef:
name: telegraf
key: monitor_database
volumeMounts:
- name: sys
mountPath: /rootfs/sys
readOnly: true
- name: docker
mountPath: /var/run/docker.sock
readOnly: true
- name: proc
mountPath: /rootfs/proc
readOnly: true
- name: docker-socket
mountPath: /var/run/docker.sock
- name: utmp
mountPath: /var/run/utmp
readOnly: true
- name: config
mountPath: /etc/telegraf
terminationGracePeriodSeconds: 30
volumes:
- name: sys
hostPath:
path: /sys
- name: docker-socket
hostPath:
path: /var/run/docker.sock
- name: proc
hostPath:
path: /proc
- name: utmp
hostPath:
path: /var/run/utmp
- name: config
configMap:
name: telegraf
如您所见,配置映射和 Telegraf 需要一些环境变量,因此我使用 secret
来注入它们。要创建它,请运行此命令,将选项替换为您的需求
kubectl create secret -n monitoring generic telegraf --from-literal=env=prod --from-literal=monitor_username=youruser --from-literal=monitor_password=yourpassword --from-literal=monitor_host=https://your.influxdb.local --from-literal=monitor_database=yourdb
这里有一个名为 env 的参数,在这个示例的 prod 上设置了。我们在 Telegraf 的每个实例上设置这个变量,它标识了集群。如果您像我们一样复制环境,您可以在 Chronograf 上创建相同的仪表板,并使用模板变量在集群之间切换。
现在,如果您一切设置正确,您将能够看到存储在 InfluxDB 和 Chronograf 上的主机和点。这只是第一阶段:我们现在可以看到主机,但我们对正在运行的服务一无所知。
Telegraf Sidecar
解决这个问题有不同方法,但我们使用的是称为 sidecar
的方法。这个术语最近在网络安全和路由 mesh 中变得流行,但它与我们所做的是相似的。
让我们假设您需要 etcd
,因为您的应用程序之一将其用作存储。在 k8s 上,它将是类似于这样的 StatefulSet
apiVersion: v1
data:
telegraf.conf: |+
[global_tags]
env = "$ENV"
[[inputs.prometheus]]
urls = ["https://127.0.0.1:2379/metrics"]
[agent]
hostname = "$HOSTNAME"
[[outputs.influxdb]]
urls = ["$MONITOR_HOST"]
database = "mydb"
write_consistency = "any"
timeout = "5s"
username = "$MONITOR_USERNAME"
password = "$MONITOR_PASSWORD"
kind: ConfigMap
metadata:
name: telegraf-etcd-config
namespace: myapp
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
namespace: "myapp"
name: "etcd"
labels:
component: "etcd"
spec:
serviceName: "etcd"
# changing replicas value will require a manual etcdctl member remove/add
# command (remove before decreasing and add after increasing)
replicas: 3
template:
metadata:
name: "etcd"
labels:
component: "etcd"
spec:
volumes:
- name: telegraf-etcd-config
configMap:
name: telegraf-etcd-config
containers:
- name: "telegraf"
image: "docker.io/library/telegraf:1.4"
volumeMounts:
- name: telegraf-etcd-config
mountPath: /etc/telegraf
env:
- name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: MONITOR_HOST
valueFrom:
secretKeyRef:
name: monitor
key: monitor_host
- name: MONITOR_USERNAME
valueFrom:
secretKeyRef:
name: monitor
key: monitor_username
- name: MONITOR_PASSWORD
valueFrom:
secretKeyRef:
name: monitor
key: monitor_password
- name: ENV
valueFrom:
secretKeyRef:
name: monitor
key: env
- name: "etcd"
image: "quay.io/coreos/etcd:v3.2.9"
ports:
- containerPort: 2379
name: client
- containerPort: 2380
name: peer
env:
- name: CLUSTER_SIZE
value: "3"
- name: SET_NAME
value: "etcd"
command:
- "/bin/sh"
- "-ecx"
- |
IP=$(hostname -i)
for i in $(seq 0 $((${CLUSTER_SIZE} - 1))); do
while true; do
echo "Waiting for ${SET_NAME}-${i}.${SET_NAME} to come up"
ping -W 1 -c 1 ${SET_NAME}-${i}.${SET_NAME} > /dev/null && break
sleep 1s
done
done
PEERS=""
for i in $(seq 0 $((${CLUSTER_SIZE} - 1))); do
PEERS="${PEERS}${PEERS:+,}${SET_NAME}-${i}=http://${SET_NAME}-${i}.${SET_NAME}:2380"
done
# start etcd. If cluster is already initialized the `--initial-*` options will be ignored.
exec etcd --name ${HOSTNAME} \
--listen-peer-urls http://${IP}:2380 \
--listen-client-urls http://${IP}:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://${HOSTNAME}.${SET_NAME}:2379 \
--initial-advertise-peer-urls http://${HOSTNAME}.${SET_NAME}:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster ${PEERS} \
--initial-cluster-state new \
--data-dir /var/run/etcd/default.etcd
如您所见,还有很多内容。有一个配置映射,并在同一个 pod 下部署了两个容器:etcd 和 Telegraf。同一 pod 下的容器共享网络命名空间,因此从 Telegraf 解析 etcd 就像调用 https://127.0.0.1:2379/metrics 一样简单。etcd 提供类似于 Prometheus 的指标,您可以使用 Telegraf 输入插件来抓取它们。
apiVersion: v1
data:
telegraf.conf: |+
[global_tags]
env = "$ENV"
[[inputs.prometheus]]
urls = ["https://127.0.0.1:2379/metrics"]
[agent]
hostname = "$HOSTNAME"
[[outputs.influxdb]]
urls = ["$MONITOR_HOST"]
database = "mydb"
write_consistency = "any"
timeout = "5s"
username = "$MONITOR_USERNAME"
password = "$MONITOR_PASSWORD"
kind: ConfigMap
metadata:
name: telegraf-etcd-config
namespace: myapp
假设您的 Go 应用程序使用我们的 sdk 将指标推送到 InfluxDB。您可以做的事情是在同一个 pod 上部署 Telegraf,就像我们为 etcd
做的那样,一个使用 http listener 输入插件 的 Telegraf。这个插件非常强大,因为它暴露了一个兼容的 InfluxDB http 层,当您将应用程序指向 localhost:8086
时,您不需要做任何更改——您最终将与 Telegraf 通信而不需要修改代码。
Telegraf 作为您应用程序和 InfluxDB 之间的中介是一个优点,因为它将批量请求,优化网络流量和在 InfluxDB 上的负载。另一个优化,虽然需要一点代码,是将您的应用程序从 tcp 移动到 udp。sdk 支持这两种方法,您可以使用 Telegraf 的 socket_listener_plugin。
这意味着您的应用程序将通过upd与Telegraf进行通信,它们共享网络命名空间,因此数据包丢失将最小化,您的应用程序将运行得更快。Telegraf将通过tcp将点与InfluxDB通信,您可以确信一切都将记录在InfluxDB中。额外福利:如果Telegraf由于某些原因而关闭,您的应用程序不会崩溃,因为udp不在乎!您的应用程序将按正常方式运行,但不会存储任何点。如果这个场景适合您,那就太好了!
将Telegraf用作Kubernetes上分布式应用程序的sidecar
进行监控的好处是,您的服务的监控配置将接近应用程序规范,因此部署简单,共享相同的pod服务发现也很容易,就像调用localhost
一样。
通常,这种情况会有问题,因为如果您只有一个收集器,容器会改变,您将不知道它们在哪里。配置可能很复杂,但使用这种架构,Telegraf将永远跟随您的应用程序。