监控Kubernetes架构

导航至

在监控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将永远跟随您的应用程序。