使用 Nginx InfluxDB 模块监控 Kubernetes Nginx Ingress

导航至

在将我们的一些容器工作负载迁移到 Kubernetes 的过程中,我们部署了 ingress-nginx 项目,以便拥有一个 Ingress 控制器,该控制器可以为传入暴露服务的流量检测 Nginx。

该项目本身制作精良,满足了我们对 Kubernetes 领域下项目的所有期望。总的来说,我们喜欢它拥有一个控制守护进程(控制器)通过管理、配置和扩展来控制 nginx 的想法。

然而,经过几周的使用,我们注意到整个系统缺少最重要的可观察性功能之一:实时跟踪所有传入请求(本地请求和代理请求)的能力。

事实上,借助 Prometheus 端点Telegraf 插件的混合使用,我们可以非常轻松地提取控制器状态的聚合指标。另一方面,对于某些情况,我们注意到跟踪每个请求并将请求直接推送到 InfluxDB 非常有用。

我们希望能够出于三个主要原因做到这一点

  • 尽快发现任何代理后端错误或意外状态代码;
  • 了解客户端如何连接到我们的服务,HTTP 方法、连接类型、请求的端点;
  • 对一致的原始数据流采取行动(使用 Kapacitor)。在这种情况下,由于数据本身的性质,它更有效。例如,如果请求未被处理且未返回给发出请求的客户端,我希望收到警报,然后在警报之后,我想对该特定请求采取行动。

当发生错误请求时,执行如下查询是理想的情况

SELECT * FROM FROM nginx_requests.threedays WHERE "status" = '502'

1518524349994255769 173             0                     text/html                                                          152         GET    35             myserver    502    /bad
1518524349994714916 173             0                     text/html                                                          152               GET    35             myserver    502    /bad

在互联网上搜索一番后,我们编写了一个模块 (nginx-influxdb-module),该模块充当每个请求上的非阻塞过滤器,并使用 UDP 和 行协议将处理后的数据发送到 InfluxDB 后端。

Kubernetes Ingress 和 Telegraf 作为 Sidecar

在编写了 Nginx 模块以服务于我们的目的之后,我们需要一种将其连接到 Kubernetes Ingress 控制器的方法。为此,我们实际上 fork 了 ingress 项目,以在其 nginx 内部编译该模块。

为什么要 fork?我们需要 fork 的原因有几个

  1. 制作完整的拉取请求以将模块与控制器深度集成(使用 configmap 和所有内容)需要 fork;
  2. Nginx 支持动态模块,但具有严格的运行时要求,不允许直接放入在其他环境中编译的模块共享对象;

要在 Kubernetes Nginx ingress 控制器中使用该模块,您有两种选择

  1. 使用直接 UDP 连接的简单用法
  2. 使用 Telegraf 作为 sidecar 代理进行连接

使用直接 UDP 连接的简单用法

使用直接 UDP 连接的简单用法

您可以通过替换 with-rbac.ymlwithout-rbac.yml 中控制器镜像为我们的 fork 的控制器,来遵循 官方步骤

请注意: 我们的 fork 镜像了官方 ingress 控制器的实际标签。对于每个从 nginx-0.10.2 开始的标签,您都可以在 此处找到等效的标签。

我们目前没有一组深度集成的特定参数,但我们依赖于 nginx.ingress.kubernetes.io/configuration-snippet 注解

kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/configuration-snippet: |
  influxdb server_name=yourappname host=your-influxdb port=8089 measurement=nginx enabled=true;

使用注解配置 InfluxDB 模块的完整示例如下所示

---
apiVersion: v1
kind: Namespace
metadata:
  name: caturday
---
apiVersion: v1
kind: Service
metadata:
  name: caturday
  namespace: caturday
  labels:
    app: caturday
spec:
  selector:
    app: caturday
  ports:
    - name: caturday
      port: 8080
      protocol: TCP
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/configuration-snippet: |
      influxdb server_name=acceptance-ingress host=127.0.0.1 port=8094 measurement=nginx enabled=true;
  name: caturday
  namespace: caturday
spec:
  rules:
    - host: kittens.local
      http:
        paths:
          - backend:
              serviceName: caturday
              servicePort: 8080
            path: /
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: caturday
  namespace: caturday
  labels:
    app: catrday
spec:
  replicas: 3
  selector:
    matchLabels:
      app: caturday
  template:
    metadata:
      labels:
        app: caturday
    spec:
      containers:
      - name: caturday
        image: docker.io/fntlnz/caturday:latest
        resources:
          limits:
            cpu: 0.1
            memory: 100M

使用 Telegraf 作为 Sidecar 代理进行连接

此配置与官方配置略有不同,因为它涉及在每个 Nginx 控制器 pod 中部署 Telegraf 容器作为 sidecar 代理。为此,我们需要不同的控制器定义、用于配置 Telegraf 环境变量的 configmap 以及用于 InfluxDB url、用户名、密码和数据库的 secret。

控制器

---

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ingress-nginx
  template:
    metadata:
      labels:
        app: ingress-nginx
      annotations:
        prometheus.io/port: '10254'
        prometheus.io/scrape: 'true'
    spec:
      serviceAccountName: nginx-ingress-serviceaccount
      initContainers:
      - command:
        - sh
        - -c
        - sysctl -w net.core.somaxconn=32768; sysctl -w net.ipv4.ip_local_port_range="1024 65535"
        image: docker.io/alpine:3.6
        imagePullPolicy: IfNotPresent
        name: sysctl
        securityContext:
          privileged: true
      containers:
      - name: nginx-ingress-controller
        image: quay.io/fntlnz/nginx-ingress-controller:kubernetes-controller-8b30ff6
        args:
          - /nginx-ingress-controller
          - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
          - --configmap=$(POD_NAMESPACE)/nginx-configuration
          - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
          - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
          - --annotations-prefix=nginx.ingress.kubernetes.io
        env:
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
        ports:
        - name: http
          containerPort: 80
        - name: https
          containerPort: 443
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
      - name: nginx-telegraf-collector
        image: docker.io/telegraf:1.5.2
        ports:
        - name: udp
          containerPort: 8094
        env:
        - name: HOSTNAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: ENV
          valueFrom:
            secretKeyRef:
              name: telegraf
              key: env
        - name: MONITOR_RETENTION_POLICY
          valueFrom:
            secretKeyRef:
              name: telegraf
              key: monitor_retention_policy
        - 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: config
          mountPath: /etc/telegraf
      volumes:
      - name: config
        configMap:
          name: telegraf

Telegraf 的 ConfigMap

---

apiVersion: v1
kind: ConfigMap
metadata:
  name: telegraf
  namespace: ingress-nginx
  labels:
    k8s-app: telegraf
data:
  telegraf.conf: |+
    [global_tags]
      env = "$ENV"
    [agent]
      interval = "10s"
      round_interval = true
      metric_batch_size = 1000
      metric_buffer_limit = 10000
      collection_jitter = "0s"
      flush_interval = "10s"
      flush_jitter = "0s"
      precision = ""
      debug = false
      quiet = false
      logfile = ""
      hostname = "$HOSTNAME"
      omit_hostname = false
    [[outputs.influxdb]]
      urls = ["$MONITOR_HOST"]
      database = "$MONITOR_DATABASE"
      retention_policy = "$MONITOR_RETENTION_POLICY"
      write_consistency = "any"
      timeout = "5s"
      username = "$MONITOR_USERNAME"
      password = "$MONITOR_PASSWORD"
     [[inputs.socket_listener]]
      service_address = "udp://:8094"

需要配置 configmap 以允许 Telegraf 连接到 InfluxDB 后端。为此

kubectl create secret -n ingress-nginx generic telegraf \
  --from-literal=env=acc \
  --from-literal=monitor_retention_policy="threedays" \
  --from-literal=monitor_username="" \
  --from-literal=monitor_password="" \
  --from-literal=monitor_host=http://your-influxdb:8086 \
  --from-literal=monitor_database=nginx_ingress

在上面的示例中,我们使用了 “threedays” 作为保留策略。您可以将保留策略留空,但如果您想像示例中一样保留三天,您可以简单地执行此查询

CREATE RETENTION POLICY threedays ON nginx_ingress DURATION 3d REPLICATION 1

下一步

总体计划是稳定模块的 API 并标记 1.0 版本。在执行此操作的同时,我们将完成与 Kubernetes Ingress 的集成,并向上游项目发送 PR,以允许人们在其 Ingress 控制器中选择性地启用该模块。

这是否已准备好用于生产环境?

我们正在生产环境中使用所有这些;但是,我们不建议您在此阶段这样做。如果您有暂存/验收环境,您可以对其进行测试并为模块做出贡献,以使我们能够推进 1.0 版本的发布,并帮助我们为 kubernetes/ingress-nginx 项目做出贡献。

结论

即使编写这种模块很容易上手,但仍然需要付出努力。幸运的是,由于 nginx/nginx-tests 仓库,旅程变得更加轻松,该仓库允许我们发现不良和边缘行为。此外,感谢 Ingress 控制器的高度可定制性以及公开了 nginx.ingress.kubernetes.io/configuration-snippet,这在现阶段非常有用。