使用Nginx InfluxDB模块监控Kubernetes Nginx Ingress

导航至

在我们将一些容器工作负载迁移到Kubernetes的过程中,我们部署了ingress-nginx项目,以拥有一个能够对暴露服务的传入流量进行仪表化的Ingress控制器。

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

然而,在使用了几个星期之后,我们发现整个系统缺少了一个最重要的可观察性功能:实时跟踪所有传入请求,无论是本地请求还是代理请求。

事实上,由于Prometheus端点(Prometheus endpoint)和Telegraf插件(Telegraf plugin)的使用混合,我们很容易拉取控制器状态的聚合指标。另一方面,在特定情况下,我们发现对我们来说非常有用,能够跟踪每个请求,并在它发生时立即将其推送到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控制器。为此,我们实际上分叉了ingress项目,在nginx中编译该模块。

为什么分叉?我们需要分叉有几个原因

  1. 制作一个深度集成的完整pull request(包括configmaps等)与控制器是一项需要分叉的努力;
  2. Nginx支持动态模块,但它有严格的运行时要求,不允许在某个环境中编译的模块共享对象直接放入;

要在Kubernetes Nginx Ingress控制器中使用该模块,您有两个选项

  1. 直接UDP连接的普通使用
  2. 使用Telegraf作为旁路代理进行连接

直接UDP连接的简单用法

直接UDP连接的简单用法

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

注意:我们的分支正在镜像官方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作为旁路代理进行连接

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

控制器

---

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仓库允许我们识别出错误和边缘行为,因此旅行之路已经变得更容易。此外,对可高度自定义并公开nginx.ingress.kubernetes.io/configuration-snippet的Ingress控制器表示敬意,这在这一阶段非常有用。