<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>InfluxData Blog - Lorenzo Fontana</title>
    <description>Posts by Lorenzo Fontana on the InfluxData Blog</description>
    <link>https://www.influxdata.com/blog/author/lorenzo/</link>
    <language>en-us</language>
    <lastBuildDate>Mon, 26 Feb 2018 12:47:32 -0700</lastBuildDate>
    <pubDate>Mon, 26 Feb 2018 12:47:32 -0700</pubDate>
    <ttl>1800</ttl>
    <item>
      <title>Monitoring the Kubernetes Nginx Ingress with the Nginx InfluxDB Module</title>
      <description>&lt;p&gt;In the process of moving some of our container workloads to Kubernetes we deployed the &lt;a href="https://github.com/kubernetes/ingress-nginx"&gt;ingress-nginx project &lt;/a&gt;to have an Ingress controller that can instrument Nginx for incoming traffic to exposed services.&lt;/p&gt;

&lt;p&gt;The project itself is pretty well crafted, and it met all the expectations we had for a project under the Kubernetes dome. Overall we like the idea that it has a controlling Daemon (the controller) controlling nginx by managing, configuring and scaling it.&lt;/p&gt;

&lt;p&gt;However, after a few weeks of usage we noted that the whole thing was lacking one of the most important features in terms of observability: the ability to track down in real time all of the incoming requests in terms of local requests and proxied ones.&lt;/p&gt;

&lt;p&gt;In fact, it was very easy for us to pull aggregated metrics on the status of the controller thanks to a mix of usage between the &lt;a href="https://github.com/kubernetes/ingress-nginx/tree/39c30853ae1f0e61e2ce94cb5c16bb79be3b905a/internal/ingress/controller/metric/collector"&gt;Prometheus endpoint &lt;/a&gt;and the &lt;a href="https://github.com/influxdata/telegraf/tree/master/plugins/inputs/nginx"&gt;Telegraf plugin&lt;/a&gt;. On the other hand, for certain situations we noted that it was very useful for us to keep track of every single request, pushing it as soon as it happens directly to InfluxDB.&lt;/p&gt;

&lt;p&gt;We want to be able to do that for three main reasons:&lt;/p&gt;
&lt;ul&gt;
 	&lt;li&gt;Spot as soon as possible any proxy backend error or unexpected status code;&lt;/li&gt;
 	&lt;li&gt;Understand how clients are connecting to our services, http methods, type of connection, requested endpoints;&lt;/li&gt;
 	&lt;li&gt;Taking action (with Kapacitor) on consistent streams of raw data. In this case, it is more effective because of the nature of the data itself. For example, I want an alert if the requests are not processed and not going back to the client that made them, and then, after the alert, I want to take action on that specific request.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When a bad request happens, doing a query like this is the ideal situation:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-markup"&gt;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&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After some searching in the interwebs, we wrote a module (&lt;a href="https://github.com/influxdata/nginx-influxdb-module"&gt;nginx-influxdb-module&lt;/a&gt;) that acts as a filter on each request in a non-blocking fashion and sends out the processed data to an InfluxDB backend using UDP and &lt;a href="https://docs.influxdata.com/influxdb/v1.4/write_protocols/line_protocol_tutorial/"&gt;line protocol&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Kubernetes Ingress and Telegraf as Sidecar&lt;/h2&gt;
&lt;p&gt;After writing the Nginx module to serve our purpose, we needed a way to connect it to the Kubernetes Ingress Controller. To do so, we actually &lt;a href="https://github.com/fntlnz/ingress-nginx/commit/8b30ff67ce58f551bf09cffc9473d88303be2d21"&gt;forked&lt;/a&gt; the ingress project to compile the module inside its nginx.&lt;/p&gt;

&lt;p&gt;Why forking? We needed to fork for a few reasons:&lt;/p&gt;
&lt;ol&gt;
 	&lt;li&gt;Crafting a full pull request to deeply integrate the module (&lt;a href="https://github.com/kubernetes/ingress-nginx/tree/master/docs/user-guide" target="_blank" rel="noopener noreferrer"&gt;with the configmaps&lt;/a&gt; and everything) with the controller is an effort that requires a fork;&lt;/li&gt;
 	&lt;li&gt;Nginx supports dynamic modules, but has strict runtime requirements that do not allow just dropping in the module shared objects compiled in some other environment;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To use the module in the Kubernetes Nginx ingress controller, you have two options:&lt;/p&gt;
&lt;ol&gt;
 	&lt;li&gt;Plain usage with direct UDP connection&lt;/li&gt;
 	&lt;li&gt;Connection using Telegraf as sidecar proxy&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Plain Usage with Direct UDP Connection&lt;/h2&gt;
&lt;p&gt;Plain Usage with Direct UDP Connection&lt;/p&gt;

&lt;p&gt;You can follow &lt;a href="https://github.com/kubernetes/ingress-nginx/blob/master/deploy/README.md#installation-guide"&gt;the official steps &lt;/a&gt;by just replacing the controller image in &lt;code class="language-markup"&gt;with-rbac.yml&lt;/code&gt; or &lt;code class="language-markup"&gt;without-rbac.yml&lt;/code&gt; with our &lt;a href="http://quay.io/fntlnz/nginx-ingress-controller:kubernetes-controller-8b30ff6"&gt;fork’s controller&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nota bene:&lt;/strong&gt; Our fork is mirroring the actual tags of the official ingress controller. For each tag starting from &lt;code class="language-markup"&gt;nginx-0.10.2&lt;/code&gt; you will find the equivalent ones &lt;a href="https://github.com/fntlnz/ingress-nginx/releases"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We don’t have a deeply integrated set of specific parameters for now, but we rely on the nginx.ingress.kubernetes.io/configuration-snippet annotation:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-markup"&gt;kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/configuration-snippet: |
  influxdb server_name=yourappname host=your-influxdb port=8089 measurement=nginx enabled=true;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A full example using the annotation to configure the InfluxDB module would look like this:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-markup"&gt;---
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&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;h2&gt;Connection Using Telegraf as Sidecar Proxy&lt;/h2&gt;
&lt;p&gt;This configuration is a bit different than the official one because it involves the deployment of a Telegraf container as a sidecar proxy in every Nginx controller pod. To do this, we need a different controller definition, a configmap to configure Telegraf’s environment variables and a secret for InfluxDB urls, username, password and database.&lt;/p&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;h2&gt;The Controller&lt;/h2&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-markup"&gt;---

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&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;h2&gt;The Telegraf's ConfigMap&lt;/h2&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-markup"&gt;---

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"&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The configmap needs to be configured in order to allow Telegraf to connect to the InfluxDB backend. To do so:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-markup"&gt;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&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above example, we used “threedays” as the retention policy. You can leave the retention policy empty, but if you want to keep three days as in the example you can simply do this query:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-markup"&gt;CREATE RETENTION POLICY threedays ON nginx_ingress DURATION 3d REPLICATION 1&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;The general plan is to stabilize the module’s API and tag a 1.0 release. While doing this, we will complete the integration with the Kubernetes Ingress and send a PR to the upstream project to allow people to optionally enable the module in their Ingress controllers.&lt;/p&gt;
&lt;h2&gt;Is This Ready for Production?&lt;/h2&gt;
&lt;p&gt;We’re using all of this in production; however, we don’t recommend you to do that at this stage. If you have a staging/acceptance environment, you can test it and contribute to the module to allow us to move forward with a 1.0 release and help us contribute to the &lt;a href="https://github.com/kubernetes/ingress-nginx"&gt;kubernetes/ingress-nginx&lt;/a&gt; project.&lt;/p&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;Even if writing this kind of module is an easy pick, it still requires effort. Fortunately, the journey has been made easier thanks to the &lt;a href="https://github.com/nginx/nginx-tests"&gt;nginx/nginx-tests&lt;/a&gt; repo that allowed us to spot bad and edge behaviors. Also, kudos to the Ingress controller that is highly customizable and exposes the &lt;code class="language-markup"&gt;nginx.ingress.kubernetes.io/configuration-snippet&lt;/code&gt;, which turned to be really useful at this stage.&lt;/p&gt;
</description>
      <pubDate>Mon, 26 Feb 2018 12:47:32 -0700</pubDate>
      <link>https://www.influxdata.com/blog/monitoring-kubernetes-nginx-ingress-nginx-influxdb-module/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/monitoring-kubernetes-nginx-ingress-nginx-influxdb-module/</guid>
      <category>Product</category>
      <category>Use Cases</category>
      <category>Developer</category>
      <author>Lorenzo Fontana (InfluxData)</author>
    </item>
  </channel>
</rss>
