
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Sat, 04 Apr 2026 08:45:13 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Troubleshooting network connectivity and performance with Cloudflare AI]]></title>
            <link>https://blog.cloudflare.com/AI-troubleshoot-warp-and-network-connectivity-issues/</link>
            <pubDate>Fri, 29 Aug 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Troubleshoot network connectivity issues by using Cloudflare AI-Power to quickly self diagnose and resolve WARP client and network issues. ]]></description>
            <content:encoded><![CDATA[ <p>Monitoring a corporate network and troubleshooting any performance issues across that network is a hard problem, and it has become increasingly complex over time. Imagine that you’re maintaining a corporate network, and you get the dreaded IT ticket. An executive is having a performance issue with an application, and they want you to look into it. The ticket doesn’t have a lot of details. It simply says: “Our internal documentation is taking forever to load. PLS FIX NOW”.</p><p>In the early days of IT, a corporate network was built on-premises. It provided network connectivity between employees that worked in person and a variety of corporate applications that were hosted locally.</p><p>The shift to cloud environments, the rise of SaaS applications, and a “work from anywhere” model has made IT environments significantly more complex in the past few years. Today, it’s hard to know if a performance issue is the result of:</p><ul><li><p>An employee’s device</p></li><li><p>Their home or corporate wifi</p></li><li><p>The corporate network</p></li><li><p>A cloud network hosting a SaaS app</p></li><li><p>An intermediary ISP</p></li></ul><p>A performance ticket submitted by an employee might even be a combination of multiple performance issues all wrapped together into one nasty problem.</p><p>Cloudflare built <a href="https://developers.cloudflare.com/cloudflare-one/"><u>Cloudflare One</u></a>, our <a href="https://www.cloudflare.com/learning/access-management/what-is-sase/">Secure Access Service Edge (SASE) </a>platform, to protect enterprise applications, users, devices, and networks. In particular, this platform relies on two capabilities to simplify troubleshooting performance issues:</p><ul><li><p>Cloudflare’s Zero Trust client, also known as <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/"><u>WARP</u></a>, forwards and encrypts traffic from devices to Cloudflare edge.</p></li><li><p>Digital Experience Monitoring (<a href="https://developers.cloudflare.com/cloudflare-one/insights/dex/"><u>DEX</u></a>) works alongside WARP to monitor device, network, and application performance.</p></li></ul><p>We’re excited to announce two new AI-powered tools that will make it easier to troubleshoot WARP client connectivity and performance issues.  We’re releasing a new WARP diagnostic analyzer in the <a href="https://www.cloudflare.com/learning/security/glossary/what-is-zero-trust/">Zero Trust</a> dashboard and a <a href="https://www.cloudflare.com/learning/ai/what-is-model-context-protocol-mcp/"><u>MCP (Model Context Protocol)</u></a> server for DEX. Today, every Cloudflare One customer has free access to both of these new features by default.</p>
    <div>
      <h2>WARP diagnostic analyzer</h2>
      <a href="#warp-diagnostic-analyzer">
        
      </a>
    </div>
    <p>The WARP client provides diagnostic logs that can be used to troubleshoot connectivity issues on a device. For desktop clients, the most common issues can be investigated with the information captured in logs called <a href="https://developers.cloudflare.com/learning-paths/warp-overview-course/series/warp-basics-2/"><u>WARP diagnostic</u></a>. Each WARP diagnostic log contains an extensive amount of information spanning days of captured events occurring on the client. It takes expertise to manually go through all of this information and understand the full picture of what is occurring on a client that is having issues. In the past, we’ve advised customers having issues to send their WARP diagnostic log straight to us so that our trained support experts can do a root cause analysis for them. While this is effective, we want to give our customers the tools to take control of deciphering common troubleshooting issues for even quicker resolution. </p><p>Enter the WARP diagnostic analyzer, a new AI available for free in the Cloudflare One dashboard as of today! This AI demystifies information in the WARP diagnostic log so you can better understand events impacting the performance of your clients and network connectivity. Now, when you run a <a href="https://developers.cloudflare.com/cloudflare-one/insights/dex/remote-captures/"><u>remote capture for WARP diagnostics</u></a> in the Cloudflare One dashboard, you can generate an <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/troubleshooting/warp-logs/#view-warp-diagnostics-summary-beta"><u>AI analysis of the WARP diagnostic file</u></a>. Simply go to your organization’s Zero Trust dashboard and select DEX &gt; Remote Captures from the side navigation bar. After you successfully run diagnostics and produce a WARP diagnostic file, you can open the status details and select View WARP Diag to generate your AI analysis.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/50lz9CFKKJJjL5GpppLu8V/4b404a2ec700713579b3ec9a616ee4c4/image4.png" />
          </figure><p>In the WARP Diag analysis, you will find a Cloudy summary of events that we recommend a deeper dive into.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6rV0XPL9aayuljbw9X46bQ/6fd046dfcf6d882948d1a98912cf7cab/image1.png" />
          </figure><p>Below this summary is an events section, where the analyzer highlights occurrences of events commonly occurring when there are client and connectivity issues. </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4OxLtM2CQ4SSs8NTGUdcpn/b7e4f0e3eb519838d50759e6d1decf75/image7.png" />
          </figure><p>Expanding on any of the events detected will reveal a detailed page explaining the event, recommended resources to help troubleshoot, and a list of time stamped recent occurrences of the event on the device. </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4ceezR6L1MybxhMtJGuL5U/31f24b0a057871a1f4330ea87f050873/Screenshot_2025-09-03_at_4.20.27%C3%A2__PM.png" />
          </figure><p>To further help with trouble shooting we’ve added a Device and WARP details section at the bottom of this page with a quick view of the device specifications and WARP configurations such as Operating system, WARP version, and the device profile ID.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/41N2iTeHQ9JfrOOsqG8MY5/550fa7573a6d4ed61479679cb4e954d3/image6.png" />
          </figure><p>Finally, we’ve made it easy to take all the information created in your AI summary with you by navigating to the JSON file tab and copying the contents. Your WARP Diag file is also available to download from this screen for any further analysis.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Sha8rpC7XwSkCvBWt6lv2/2702873ce14fe80904d4f0886e6f3528/image2.png" />
          </figure>
    <div>
      <h2>MCP server for DEX</h2>
      <a href="#mcp-server-for-dex">
        
      </a>
    </div>
    <p>Alongside the new WARP Diagnostic Analyzer, we’re excited to announce that all Cloudflare One customers have access to a MCP (Model Context Protocol) server for our Digital Experience Monitoring (DEX) product. Let’s dive into how this will save our customers time and money.</p><p>Cloudflare One customers use Digital Experience Monitoring (DEX) to monitor devices across their employee network and troubleshoot any connectivity or performance issues. Like many products at Cloudflare, every data point generated by DEX is available to customers via Cloudflare’s API or log ingestion. DEX API and log data is valuable because it enables Enterprises to create custom analytics for their devices’ connectivity and performance in a SIEM (<a href="https://www.cloudflare.com/learning/security/what-is-siem/"><u>Security Information and Event Management</u></a>). Building new data pipelines and various dashboards can take a lot of time and can be expensive. Some of Cloudflare One customers aren’t able to dedicate the engineering time to build custom analytics: whether it’s due to budget, resource constraints, time constraints, or other factors.</p><p>Model Context Protocol (MCP) is an AI standardization that allows AI to connect to other applications. In this instance, using MCP together with DEX gives users a custom analytics experience — without all the work of integrating log data into a SIEM. Network security practitioners can type out a question (like “Show me the performance data for alice@acme.com’s device in a few graphs”), and an MCP server will answer that question with a customized graph on Alice’s device data from the DEX API.</p>
    <div>
      <h2>Troubleshooting DNS query performance with the DEX MCP server</h2>
      <a href="#troubleshooting-dns-query-performance-with-the-dex-mcp-server">
        
      </a>
    </div>
    <p>Any IT admin, security engineer, or network engineer can ask the MCP server a question like: “I received an IT ticket from bob@acme.com who is an enterprise employee. He is complaining about the performance of his device. Can you investigate this for me?”.</p><p>After you submit this question, the DEX MCP server will start by reviewing the device’s overall health.</p>
            <pre><code>Overall Device Health:
✅ Connection Status: Consistently connected for the past 24 hours with 100% availability
✅ WARP Mode: Running in optimal "warp+doh" (WARP + DNS over HTTPS) mode
✅ Client Version: Up-to-date with version 2025.1.7.2</code></pre>
            <p>So far, the device’s health looks great. Next, the DEX MCP server will begin analyzing the results of the performance tests that are configured on Bob’s device.</p>
            <pre><code>📊 Performance Metrics Analysis

HTTP Response Times:
Google.com test: Average 247ms (Range: 81-649ms)
  • Shows significant improvement throughout the day: Started at ~317ms, dropped to ~211ms by evening
Wiki.internal.acme.com: Average 1,374ms (Range: 877-2,431ms)
  • This is notably high and could be the source of performance complaints
  • DNS resolution particularly slow: Average 616ms with spikes up to 1,600ms</code></pre>
            <p>Now we’re making progress in our troubleshooting effort. The DEX MCP server identified that the HTTP GET test for wiki.internal.acme.com has a high average resource fetch time when compared to other websites (like google.com). In particular, high resource fetch time is the result of slow DNS resolution. Now, the DEX MCP server will summarize its findings.</p>
            <pre><code>🚨 Performance Issues Identified
Extremely Slow DNS Resolution:
  • Average DNS response time of 616ms is significantly high
  • Normal DNS should be &lt;50ms for good performance
High Resource Fetch Times on wiki.internal.acme.com:
   • 1.3+ second response times indicate potential application-level issues
   • Could be related to DNS performance or server-side delays
HTTP Status Code Issues:
        • wiki.internal.acme.com is showing consistent HTTP 400 errors
        • This suggests potential configuration or routing issues</code></pre>
            <p>Now, we’ve successfully identified that Bob’s slow wiki performance is the result of a high average DNS response time. Slow DNS resolution increases the average resource fetch time, and also generates HTTP 400 errors for Enterprise employees that are attempting to access wiki.internal.acme.com. Notably, it’s likely that if the wiki is seeing slow DNS response times, other internal applications are experiencing performance issues as well.</p><p>We can ask the DEX MCP server to suggest potential solutions that would fix this wider DNS performance issue.</p>
            <pre><code>🔧 DNS Optimization Strategies

Check WARP DNS configuration and settings
  • Since the device is using WARP+DoH mode, you can optimize the DNS settings
Implement or improve local DNS caching
  • Implement or improve local DNS caching to reduce repeated lookups
Network stack tuning
  • Optimize TCP / UDP settings for DNS</code></pre>
            
    <div>
      <h2>Try out the DEX MCP server today</h2>
      <a href="#try-out-the-dex-mcp-server-today">
        
      </a>
    </div>
    
    <div>
      <h3>Fast and easy option for testing an MCP server</h3>
      <a href="#fast-and-easy-option-for-testing-an-mcp-server">
        
      </a>
    </div>
    <p>Any Cloudflare One customer with a Free, PayGo, or ENT plan can start using the DEX MCP server in less than one minute. The fastest and easiest way to try out the DEX MCP server is to visit <a href="http://playground.ai.cloudflare.com"><u>playground.ai.cloudflare.com</u></a>. There are five steps to get started:</p><ol><li><p>Copy the URL for the DEX MCP server: https://dex.mcp.cloudflare.com/sse</p></li><li><p>Open <a href="http://playground.ai.cloudflare.com"><u>playground.ai.cloudflare.com</u></a> in a browser</p></li><li><p>Find the section in the left side bar titled <b>MCP Servers</b></p></li><li><p>Paste the URL for the DEX MCP server into the URL input box and click <b>Connect</b></p></li><li><p>Authenticate your Cloudflare account, and then start asking questions to the DEX MCP server</p></li></ol><p>It’s worth noting that end users will need to ask specific and explicit questions to the DEX MCP server to get a response. For example, you may need to say, “Set my production account as the active  account”, and then give the separate command, “Fetch the DEX test results for the user bob@acme.com over the past 24 hours”.</p>
    <div>
      <h3>Better experience for MCP servers that requires additional steps</h3>
      <a href="#better-experience-for-mcp-servers-that-requires-additional-steps">
        
      </a>
    </div>
    <p>Customers will get a more flexible prompt experience by configuring the DEX MCP server with their preferred AI assistant (Claude, Gemini, ChatGPT, etc.) that has MCP server support. MCP server support may require a subscription for some AI assistants. You can read the <a href="https://developers.cloudflare.com/cloudflare-one/insights/dex/dex-mcp-server"><u>Digital Experience Monitoring - MCP server documentation</u></a> for step by step instructions on how to get set up with each of the major AI assistants that are available today.</p><p>As an example, you can configure the DEX MCP server in Claude by downloading the Claude Desktop client, then selecting Claude Code &gt; Developer &gt; Edit Config. You will be prompted to open “claude_desktop_config.json” in a code editor of your choice. Simply add the following JSON configuration, and you’re ready to use Claude to call the DEX MCP server.</p>
            <pre><code>{
  "globalShortcut": "",
  "mcpServers": {
    "cloudflare-dex-analysis": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://dex.mcp.cloudflare.com/sse"
      ]
    }
  }
}</code></pre>
            
    <div>
      <h2>Get started with Cloudflare One today</h2>
      <a href="#get-started-with-cloudflare-one-today">
        
      </a>
    </div>
    <p>Are you ready to secure your Internet traffic, employee devices, and private resources without compromising speed? You can get started with our new Cloudflare One AI powered tools today.</p><p>The WARP diagnostic analyzer and the DEX MCP server are generally available to all customers. Head to the Zero Trust dashboard to run a WARP diagnostic and learn more about your client’s connectivity with the WARP diagnostic analyzer. You can test out the new DEX MCP server (https://dex.mcp.cloudflare.com/sse) in less than one minute at <a href="http://playground.ai.cloudflare.com"><u>playground.ai.cloudflare.com</u></a>, and you can also configure an AI assistant like Claude to use the new <a href="https://developers.cloudflare.com/cloudflare-one/insights/dex/dex-mcp-server"><u>DEX MCP server</u></a>.</p><p>If you don’t have a Cloudflare account, and you want to try these new features, you can create a free account for up to 50 users. If you’re an Enterprise customer, and you’d like a demo of these new Cloudflare One AI features, you can reach out to your account team to set up a demo anytime. </p><p>You can stay up to date on latest feature releases across the Cloudflare One platform by following the <a href="https://developers.cloudflare.com/cloudflare-one/changelog/"><u>Cloudflare One changelogs</u></a> and joining the conversation in the <a href="https://community.cloudflare.com/"><u>Cloudflare community hub</u></a> or on our <a href="https://discord.cloudflare.com/"><u>Discord Server</u></a>.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/CvbpyPLYM62H7B0GhGqcZ/79317635029a9d09d31dacbec6793887/image5.png" />
          </figure><div>
  
</div><p></p> ]]></content:encoded>
            <category><![CDATA[AI Week]]></category>
            <category><![CDATA[Monitoring]]></category>
            <category><![CDATA[Analytics]]></category>
            <category><![CDATA[WARP]]></category>
            <category><![CDATA[Device Security]]></category>
            <category><![CDATA[Performance]]></category>
            <category><![CDATA[Dashboard]]></category>
            <category><![CDATA[Zero Trust]]></category>
            <category><![CDATA[Cloudflare One]]></category>
            <category><![CDATA[AI]]></category>
            <guid isPermaLink="false">7vSTlKJvMibVnsLp1YLWKe</guid>
            <dc:creator>Chris Draper</dc:creator>
            <dc:creator>Koko Uko</dc:creator>
        </item>
        <item>
            <title><![CDATA[Searching for the cause of hung tasks in the Linux kernel]]></title>
            <link>https://blog.cloudflare.com/searching-for-the-cause-of-hung-tasks-in-the-linux-kernel/</link>
            <pubDate>Fri, 14 Feb 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ The Linux kernel can produce a hung task warning. Searching the Internet and the kernel docs, you can find a brief explanation that the process is stuck in the uninterruptible state. ]]></description>
            <content:encoded><![CDATA[ <p>Depending on your configuration, the Linux kernel can produce a hung task warning message in its log. Searching the Internet and the kernel documentation, you can find a brief explanation that the kernel process is stuck in the uninterruptable state and hasn’t been scheduled on the CPU for an unexpectedly long period of time. That explains the warning’s meaning, but doesn’t provide the reason it occurred. In this blog post we’re going to explore how the hung task warning works, why it happens, whether it is a bug in the Linux kernel or application itself, and whether it is worth monitoring at all.</p>
    <div>
      <h3>INFO: task XXX:1495882 blocked for more than YYY seconds.</h3>
      <a href="#info-task-xxx-1495882-blocked-for-more-than-yyy-seconds">
        
      </a>
    </div>
    <p>The hung task message in the kernel log looks like this:</p>
            <pre><code>INFO: task XXX:1495882 blocked for more than YYY seconds.
     Tainted: G          O       6.6.39-cloudflare-2024.7.3 #1
"echo 0 &gt; /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:XXX         state:D stack:0     pid:1495882 ppid:1      flags:0x00004002
. . .</code></pre>
            <p>Processes in Linux can be in different states. Some of them are running or ready to run on the CPU — they are in the <a href="https://elixir.bootlin.com/linux/v6.12.6/source/include/linux/sched.h#L99"><code><u>TASK_RUNNING</u></code></a> state. Others are waiting for some signal or event to happen, e.g. network packets to arrive or terminal input from a user. They are in a <code>TASK_INTERRUPTIBLE</code> state and can spend an arbitrary length of time in this state until being woken up by a signal. The most important thing about these states is that they still can receive signals, and be terminated by a signal. In contrast, a process in the <code>TASK_UNINTERRUPTIBLE</code> state is waiting only for certain special classes of events to wake them up, and can’t be interrupted by a signal. The signals are not delivered until the process emerges from this state and only a system reboot can clear the process. It’s marked with the letter <code>D</code> in the log shown above.</p><p>What if this wake up event doesn’t happen or happens with a significant delay? (A “significant delay” may be on the order of seconds or minutes, depending on the system.) Then our dependent process is hung in this state. What if this dependent process holds some lock and prevents other processes from acquiring it? Or if we see many processes in the D state? Then it might tell us that some of the system resources are overwhelmed or are not working correctly. At the same time, this state is very valuable, especially if we want to preserve the process memory. It might be useful if part of the data is written to disk and another part is still in the process memory — we don’t want inconsistent data on a disk. Or maybe we want a snapshot of the process memory when the bug is hit. To preserve this behaviour, but make it more controlled, a new state was introduced in the kernel: <a href="https://lwn.net/Articles/288056/"><code><u>TASK_KILLABLE</u></code></a> — it still protects a process, but allows termination with a fatal signal. </p>
    <div>
      <h3>How Linux identifies the hung process</h3>
      <a href="#how-linux-identifies-the-hung-process">
        
      </a>
    </div>
    <p>The Linux kernel has a special thread called <code>khungtaskd</code>. It runs regularly depending on the settings, iterating over all processes in the <code>D</code> state. If a process is in this state for more than YYY seconds, we’ll see a message in the kernel log. There are settings for this daemon that can be changed according to your wishes:</p>
            <pre><code>$ sudo sysctl -a --pattern hung
kernel.hung_task_all_cpu_backtrace = 0
kernel.hung_task_check_count = 4194304
kernel.hung_task_check_interval_secs = 0
kernel.hung_task_panic = 0
kernel.hung_task_timeout_secs = 10
kernel.hung_task_warnings = 200</code></pre>
            <p>At Cloudflare, we changed the notification threshold <code>kernel.hung_task_timeout_secs</code> from the default 120 seconds to 10 seconds. You can adjust the value for your system depending on configuration and how critical this delay is for you. If the process spends more than <code>hung_task_timeout_secs</code> seconds in the D state, a log entry is written, and our internal monitoring system emits an alert based on this log. Another important setting here is <code>kernel.hung_task_warnings</code> — the total number of messages that will be sent to the log. We limit it to 200 messages and reset it every 15 minutes. It allows us not to be overwhelmed by the same issue, and at the same time doesn’t stop our monitoring for too long. You can make it unlimited by <a href="https://docs.kernel.org/admin-guide/sysctl/kernel.html#hung-task-warnings"><u>setting the value to "-1"</u></a>.</p><p>To better understand the root causes of the hung tasks and how a system can be affected, we’re going to review more detailed examples. </p>
    <div>
      <h3>Example #1 or XFS</h3>
      <a href="#example-1-or-xfs">
        
      </a>
    </div>
    <p>Typically, there is a meaningful process or application name in the log, but sometimes you might see something like this:</p>
            <pre><code>INFO: task kworker/13:0:834409 blocked for more than 11 seconds.
 	Tainted: G      	O   	6.6.39-cloudflare-2024.7.3 #1
"echo 0 &gt; /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:kworker/13:0	state:D stack:0 	pid:834409 ppid:2   flags:0x00004000
Workqueue: xfs-sync/dm-6 xfs_log_worker</code></pre>
            <p>In this log, <code>kworker</code> is the kernel thread. It’s used as a deferring mechanism, meaning a piece of work will be scheduled to be executed in the future. Under <code>kworker</code>, the work is aggregated from different tasks, which makes it difficult to tell which application is experiencing a delay. Luckily, the <code>kworker</code> is accompanied by the <a href="https://docs.kernel.org/core-api/workqueue.html"><code><u>Workqueue</u></code></a> line. <code>Workqueue</code> is a linked list, usually predefined in the kernel, where these pieces of work are added and performed by the <code>kworker</code> in the order they were added to the queue. The <code>Workqueue</code> name <code>xfs-sync</code> and <a href="https://elixir.bootlin.com/linux/v6.12.6/source/kernel/workqueue.c#L6096"><u>the function which it points to</u></a>, <code>xfs_log_worker</code>, might give a good clue where to look. Here we can make an assumption that the <a href="https://en.wikipedia.org/wiki/XFS"><u>XFS</u></a> is under pressure and check the relevant metrics. It helped us to discover that due to some configuration changes, we forgot <code>no_read_workqueue</code> / <code>no_write_workqueue</code> flags that were introduced some time ago to <a href="https://blog.cloudflare.com/speeding-up-linux-disk-encryption/"><u>speed up Linux disk encryption</u></a>.</p><p><i>Summary</i>: In this case, nothing critical happened to the system, but the hung tasks warnings gave us an alert that our file system had slowed down.</p>
    <div>
      <h3>Example #2 or Coredump</h3>
      <a href="#example-2-or-coredump">
        
      </a>
    </div>
    <p>Let’s take a look at the next hung task log and its decoded stack trace:</p>
            <pre><code>INFO: task test:964 blocked for more than 5 seconds.
      Not tainted 6.6.72-cloudflare-2025.1.7 #1
"echo 0 &gt; /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:test            state:D stack:0     pid:964   ppid:916    flags:0x00004000
Call Trace:
&lt;TASK&gt;
__schedule (linux/kernel/sched/core.c:5378 linux/kernel/sched/core.c:6697) 
schedule (linux/arch/x86/include/asm/preempt.h:85 (discriminator 13) linux/kernel/sched/core.c:6772 (discriminator 13)) 
[do_exit (linux/kernel/exit.c:433 (discriminator 4) linux/kernel/exit.c:825 (discriminator 4)) 
? finish_task_switch.isra.0 (linux/arch/x86/include/asm/irqflags.h:42 linux/arch/x86/include/asm/irqflags.h:77 linux/kernel/sched/sched.h:1385 linux/kernel/sched/core.c:5132 linux/kernel/sched/core.c:5250) 
do_group_exit (linux/kernel/exit.c:1005) 
get_signal (linux/kernel/signal.c:2869) 
? srso_return_thunk (linux/arch/x86/lib/retpoline.S:217) 
? hrtimer_try_to_cancel.part.0 (linux/kernel/time/hrtimer.c:1347) 
arch_do_signal_or_restart (linux/arch/x86/kernel/signal.c:310) 
? srso_return_thunk (linux/arch/x86/lib/retpoline.S:217) 
? hrtimer_nanosleep (linux/kernel/time/hrtimer.c:2105) 
exit_to_user_mode_prepare (linux/kernel/entry/common.c:176 linux/kernel/entry/common.c:210) 
syscall_exit_to_user_mode (linux/arch/x86/include/asm/entry-common.h:91 linux/kernel/entry/common.c:141 linux/kernel/entry/common.c:304) 
? srso_return_thunk (linux/arch/x86/lib/retpoline.S:217) 
do_syscall_64 (linux/arch/x86/entry/common.c:88) 
entry_SYSCALL_64_after_hwframe (linux/arch/x86/entry/entry_64.S:121) 
&lt;/TASK&gt;</code></pre>
            <p>The stack trace says that the process or application <code>test</code> was blocked <code>for more than 5 seconds</code>. We might recognise this user space application by the name, but why is it blocked? It’s always helpful to check the stack trace when looking for a cause. The most interesting line here is <code>do_exit (linux/kernel/exit.c:433 (discriminator 4) linux/kernel/exit.c:825 (discriminator 4))</code>. The <a href="https://elixir.bootlin.com/linux/v6.6.67/source/kernel/exit.c#L825"><u>source code</u></a> points to the <code>coredump_task_exit</code> function. Additionally, checking the process metrics revealed that the application crashed during the time when the warning message appeared in the log. When a process is terminated based on some set of signals (abnormally), <a href="https://man7.org/linux/man-pages/man5/core.5.html"><u>the Linux kernel can provide a core dump file</u></a>, if enabled. The mechanism — when a process terminates, the kernel makes a snapshot of the process memory before exiting and either writes it to a file or sends it through the socket to another handler — can be <a href="https://systemd.io/COREDUMP/"><u>systemd-coredump</u></a> or your custom one. When it happens, the kernel moves the process to the <code>D</code> state to preserve its memory and early termination. The higher the process memory usage, the longer it takes to get a core dump file, and the higher the chance of getting a hung task warning.</p><p>Let’s check our hypothesis by triggering it with a small Go program. We’ll use the default Linux coredump handler and will decrease the hung task threshold to 1 second.</p><p>Coredump settings:</p>
            <pre><code>$ sudo sysctl -a --pattern kernel.core
kernel.core_pattern = core
kernel.core_pipe_limit = 16
kernel.core_uses_pid = 1</code></pre>
            <p>You can make changes with <a href="https://man7.org/linux/man-pages/man8/sysctl.8.html"><u>sysctl</u></a>:</p>
            <pre><code>$ sudo sysctl -w kernel.core_uses_pid=1</code></pre>
            <p>Hung task settings:</p>
            <pre><code>$ sudo sysctl -a --pattern hung
kernel.hung_task_all_cpu_backtrace = 0
kernel.hung_task_check_count = 4194304
kernel.hung_task_check_interval_secs = 0
kernel.hung_task_panic = 0
kernel.hung_task_timeout_secs = 1
kernel.hung_task_warnings = -1</code></pre>
            <p>Go program:</p>
            <pre><code>$ cat main.go
package main

import (
	"os"
	"time"
)

func main() {
	_, err := os.ReadFile("test.file")
	if err != nil {
		panic(err)
	}
	time.Sleep(8 * time.Minute) 
}</code></pre>
            <p>This program reads a 10 GB file into process memory. Let’s create the file:</p>
            <pre><code>$ yes this is 10GB file | head -c 10GB &gt; test.file</code></pre>
            <p>The last step is to build the Go program, crash it, and watch our kernel log:</p>
            <pre><code>$ go mod init test
$ go build .
$ GOTRACEBACK=crash ./test
$ (Ctrl+\)</code></pre>
            <p>Hooray! We can see our hung task warning:</p>
            <pre><code>$ sudo dmesg -T | tail -n 31
INFO: task test:8734 blocked for more than 22 seconds.
      Not tainted 6.6.72-cloudflare-2025.1.7 #1
      Blocked by coredump.
"echo 0 &gt; /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:test            state:D stack:0     pid:8734  ppid:8406   task_flags:0x400448 flags:0x00004000</code></pre>
            <p>By the way, have you noticed the <code>Blocked by coredump.</code> line in the log? It was recently added to the <a href="https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git/commit/?h=mm-nonmm-stable&amp;id=23f3f7625cfb55f92e950950e70899312f54afb7"><u>upstream</u></a> code to improve visibility and remove the blame from the process itself. The patch also added the <code>task_flags</code> information, as <code>Blocked by coredump</code> is detected via the flag <a href="https://elixir.bootlin.com/linux/v6.13.1/source/include/linux/sched.h#L1675"><code><u>PF_POSTCOREDUMP</u></code></a>, and knowing all the task flags is useful for further root-cause analysis.</p><p><i>Summary</i>: This example showed that even if everything suggests that the application is the problem, the real root cause can be something else — in this case, <code>coredump</code>.</p>
    <div>
      <h3>Example #3 or rtnl_mutex</h3>
      <a href="#example-3-or-rtnl_mutex">
        
      </a>
    </div>
    <p>This one was tricky to debug. Usually, the alerts are limited by one or two different processes, meaning only a certain application or subsystem experiences an issue. In this case, we saw dozens of unrelated tasks hanging for minutes with no improvements over time. Nothing else was in the log, most of the system metrics were fine, and existing traffic was being served, but it was not possible to ssh to the server. New Kubernetes container creations were also stalling. Analyzing the stack traces of different tasks initially revealed that all the traces were limited to just three functions:</p>
            <pre><code>rtnetlink_rcv_msg+0x9/0x3c0
dev_ethtool+0xc6/0x2db0 
bonding_show_bonds+0x20/0xb0</code></pre>
            <p>Further investigation showed that all of these functions were waiting for <a href="https://elixir.bootlin.com/linux/v6.6.74/source/net/core/rtnetlink.c#L76"><code><u>rtnl_lock</u></code></a> to be acquired. It looked like some application acquired the <code>rtnl_mutex</code> and didn’t release it. All other processes were in the <code>D</code> state waiting for this lock.</p><p>The RTNL lock is primarily used by the kernel networking subsystem for any network-related config, for both writing and reading. The RTNL is a global <b>mutex</b> lock, although <a href="https://lpc.events/event/18/contributions/1959/"><u>upstream efforts</u></a> are being made for splitting up RTNL per network namespace (netns).</p><p>From the hung task reports, we can observe the “victims” that are being stalled waiting for the lock, but how do we identify the task that is holding this lock for too long? For troubleshooting this, we leveraged <code>BPF</code> via a <code>bpftrace</code> script, as this allows us to inspect the running kernel state. The <a href="https://elixir.bootlin.com/linux/v6.6.75/source/include/linux/mutex.h#L67"><u>kernel's mutex implementation</u></a> has a struct member called <code>owner</code>. It contains a pointer to the <a href="https://elixir.bootlin.com/linux/v6.6.75/source/include/linux/sched.h#L746"><code><u>task_struct</u></code></a> from the mutex-owning process, except it is encoded as type <code>atomic_long_t</code>. This is because the mutex implementation stores some state information in the lower 3-bits (mask 0x7) of this pointer. Thus, to read and dereference this <code>task_struct</code> pointer, we must first mask off the lower bits (0x7).</p><p>Our <code>bpftrace</code> script to determine who holds the mutex is as follows:</p>
            <pre><code>#!/usr/bin/env bpftrace
interval:s:10 {
  $rtnl_mutex = (struct mutex *) kaddr("rtnl_mutex");
  $owner = (struct task_struct *) ($rtnl_mutex-&gt;owner.counter &amp; ~0x07);
  if ($owner != 0) {
    printf("rtnl_mutex-&gt;owner = %u %s\n", $owner-&gt;pid, $owner-&gt;comm);
  }
}</code></pre>
            <p>In this script, the <code>rtnl_mutex</code> lock is a global lock whose address can be exposed via <code>/proc/kallsyms</code> – using <code>bpftrace</code> helper function <code>kaddr()</code>, we can access the struct mutex pointer from the <code>kallsyms</code>. Thus, we can periodically (via <code>interval:s:10</code>) check if someone is holding this lock.</p><p>In the output we had this:</p>
            <pre><code>rtnl_mutex-&gt;owner = 3895365 calico-node</code></pre>
            <p>This allowed us to quickly identify <code>calico-node</code> as the process holding the RTNL lock for too long. To quickly observe where this process itself is stalled, the call stack is available via <code>/proc/3895365/stack</code>. This showed us that the root cause was a Wireguard config change, with function <code>wg_set_device()</code> holding the RTNL lock, and <code>peer_remove_after_dead()</code> waiting too long for a <code>napi_disable()</code> call. We continued debugging via a tool called <a href="https://drgn.readthedocs.io/en/latest/user_guide.html#stack-traces"><code><u>drgn</u></code></a>, which is a programmable debugger that can debug a running kernel via a Python-like interactive shell. We still haven’t discovered the root cause for the Wireguard issue and have <a href="https://lore.kernel.org/lkml/CALrw=nGoSW=M-SApcvkP4cfYwWRj=z7WonKi6fEksWjMZTs81A@mail.gmail.com/"><u>asked the upstream</u></a> for help, but that is another story.</p><p><i>Summary</i>: The hung task messages were the only ones which we had in the kernel log. Each stack trace of these messages was unique, but by carefully analyzing them, we could spot similarities and continue debugging with other instruments.</p>
    <div>
      <h3>Epilogue</h3>
      <a href="#epilogue">
        
      </a>
    </div>
    <p>Your system might have different hung task warnings, and we have many others not mentioned here. Each case is unique, and there is no standard approach to debug them. But hopefully this blog post helps you better understand why it’s good to have these warnings enabled, how they work, and what the meaning is behind them. We tried to provide some navigation guidance for the debugging process as well:</p><ul><li><p>analyzing the stack trace might be a good starting point for debugging it, even if all the messages look unrelated, like we saw in example #3</p></li><li><p>keep in mind that the alert might be misleading, pointing to the victim and not the offender, as we saw in example #2 and example #3</p></li><li><p>if the kernel doesn’t schedule your application on the CPU, puts it in the D state, and emits the warning – the real problem might exist in the application code</p></li></ul><p>Good luck with your debugging, and hopefully this material will help you on this journey!</p> ]]></content:encoded>
            <category><![CDATA[Deep Dive]]></category>
            <category><![CDATA[Linux]]></category>
            <category><![CDATA[Kernel]]></category>
            <category><![CDATA[Monitoring]]></category>
            <guid isPermaLink="false">3UHNgpNPKn2IAwDUzD4m3a</guid>
            <dc:creator>Oxana Kharitonova</dc:creator>
            <dc:creator>Jesper Brouer</dc:creator>
        </item>
        <item>
            <title><![CDATA[Free network flow monitoring for all enterprise customers]]></title>
            <link>https://blog.cloudflare.com/free-network-monitoring-for-enterprise/</link>
            <pubDate>Thu, 07 Mar 2024 14:00:43 GMT</pubDate>
            <description><![CDATA[ Today, we’re excited to announce that a free version of Cloudflare’s network flow monitoring product, Magic Network Monitoring, is now available to all Enterprise Customers ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4JDYfP3P5eCvUA6t2OVQRW/9623c870e75e74e813cd0abc5a9b8da9/image1-24.png" />
            
            </figure><p>A key component of <a href="https://www.cloudflare.com/network-security/">effective corporate network security</a> is establishing end to end visibility across all traffic that flows through the network. Every network engineer needs a complete overview of their network traffic to confirm their security policies work, to identify new vulnerabilities, and to analyze any shifts in traffic behavior. Often, it’s difficult to build out effective network monitoring as teams struggle with problems like configuring and tuning data collection, managing storage costs, and analyzing traffic across multiple visibility tools.</p><p>Today, we’re excited to announce that a free version of Cloudflare’s <a href="https://www.cloudflare.com/network-services/solutions/network-monitoring-tools/">network flow monitoring</a> product, Magic Network Monitoring, is available to all Enterprise Customers. Every Enterprise Customer can configure Magic Network Monitoring and immediately improve their network visibility in as little as 30 minutes via our self-serve onboarding process.</p><p>Enterprise Customers can visit the <a href="https://www.cloudflare.com/network-services/products/magic-network-monitoring/">Magic Network Monitoring product page</a>, click “Talk to an expert”, and fill out the form. You’ll receive access within 24 hours of submitting the request. Over the next month, the free version of Magic Network Monitoring will be rolled out to all Enterprise Customers. The product will automatically be available by default without the need to submit a form.</p>
    <div>
      <h3>How it works</h3>
      <a href="#how-it-works">
        
      </a>
    </div>
    <p>Cloudflare customers can send their network flow data (either NetFlow or sFlow) from their routers to Cloudflare’s network edge.</p><p>Magic Network Monitoring will pick up this data, parse it, and instantly provide insights and analytics on your network traffic. These analytics include traffic volume overtime in bytes and packets, top protocols, sources, destinations, ports, and TCP flags.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6AZo6eGAZteqDAzTz8JSzc/83dd771696c34386f200144f95fb8207/image3-20.png" />
            
            </figure>
    <div>
      <h3>Dogfooding Magic Network Monitoring during the remediation of the Thanksgiving 2023 security incident</h3>
      <a href="#dogfooding-magic-network-monitoring-during-the-remediation-of-the-thanksgiving-2023-security-incident">
        
      </a>
    </div>
    <p>Let’s review a recent example of how Magic Network Monitoring improved Cloudflare’s own network security and traffic visibility during the <a href="/thanksgiving-2023-security-incident">Thanksgiving 2023 security incident</a>. Our security team needed a lightweight method to identify malicious packet characteristics in our core data center traffic. We monitored for any network traffic sourced from or destined to a list of ASNs associated with the bad actor. Our security team setup Magic Network Monitoring and established visibility into our first core data center within 24 hours of the project kick-off. Today, Cloudflare continues to use Magic Network Monitoring to monitor for traffic related to bad actors and to provide real time traffic analytics on more than 1 Tbps of core data center traffic.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/15lzmYJOViOw36MiEo7RR1/e3252df389014e6a52aad4eab6a78b7c/Screenshot-2024-03-07-at-10.55.47.png" />
            
            </figure><p><i>Magic Network Monitoring - Traffic Analytics</i></p>
    <div>
      <h3>Monitoring local network traffic from IoT devices</h3>
      <a href="#monitoring-local-network-traffic-from-iot-devices">
        
      </a>
    </div>
    <p>Magic Network Monitoring also improves visibility on any network traffic that doesn’t go through Cloudflare. Imagine that you’re a network engineer at ACME Corporation, and it’s your job to manage and troubleshoot IoT devices in a factory that are connected to the factory’s internal network. The traffic generated by these IoT devices doesn’t go through Cloudflare because it is destined to other devices and endpoints on the internal network. Nonetheless, you still need to establish network visibility into device traffic over time to monitor and troubleshoot the system.</p><p>To solve the problem, you configure a router or other network device to securely send encrypted traffic flow summaries to Cloudflare via an IPSec tunnel. Magic Network Monitoring parses the data, and instantly provides you with insights and analytics on your network traffic. Now, when an IoT device goes down, or a connection between IoT devices is unexpectedly blocked, you can analyze historical network traffic data in Magic Network Monitoring to speed up the troubleshooting process.</p>
    <div>
      <h3>Monitoring cloud network traffic</h3>
      <a href="#monitoring-cloud-network-traffic">
        
      </a>
    </div>
    <p>As <a href="https://www.cloudflare.com/learning/cloud/what-is-cloud-networking/">cloud networking</a> becomes increasingly prevalent, it is essential for enterprises to <a href="https://www.cloudflare.com/network-services/solutions/enterprise-network-security/">invest in visibility</a> across their cloud environments. Let’s say you’re responsible for monitoring and troubleshooting your corporation's cloud network operations which are spread across multiple public cloud providers. You need to improve visibility into your cloud network traffic to analyze and troubleshoot any unexpected traffic patterns like configuration drift that leads to an exposed network port.</p><p>To improve traffic visibility across different cloud environments, you can export cloud traffic flow logs from any virtual device that supports NetFlow or sFlow to Cloudflare. In the future, we are building support for native cloud VPC flow logs in conjunction with <a href="/introducing-magic-cloud-networking">Magic Cloud Networking</a>. Cloudflare will parse this traffic flow data and provide alerts plus analytics across all your cloud environments in a single pane of glass on the Cloudflare dashboard.</p>
    <div>
      <h3>Improve your security posture today in less than 30 minutes</h3>
      <a href="#improve-your-security-posture-today-in-less-than-30-minutes">
        
      </a>
    </div>
    <p>If you’re an existing Enterprise customer, and you want to improve your corporate network security, you can get started right away. Visit the <a href="https://www.cloudflare.com/network-services/products/magic-network-monitoring/">Magic Network Monitoring product page</a>, click “Talk to an expert”, and fill out the form. You’ll receive access within 24 hours of submitting the request. You can begin the self-serve onboarding tutorial, and start monitoring your first batch of network traffic in less than 30 minutes.</p><p>Over the next month, the free version of Magic Network Monitoring will be rolled out to all Enterprise Customers. The product will be automatically available by default without the need to submit a form.</p><p>If you’re interested in becoming an Enterprise Customer, and have more questions about Magic Network Monitoring, you can <a href="https://www.cloudflare.com/network-services/products/magic-network-monitoring/">talk with an expert</a>. If you’re a free customer, and you’re interested in testing a limited beta of Magic Network Monitoring, you can <a href="https://docs.google.com/forms/d/1umsmwHmXgMesP2t4wH94uVExHaT60tb5RTeawqR_9Cg/edit">fill out this form to request access</a>.</p> ]]></content:encoded>
            <category><![CDATA[Security Week]]></category>
            <category><![CDATA[Magic Network Monitoring]]></category>
            <category><![CDATA[Network]]></category>
            <category><![CDATA[Monitoring]]></category>
            <category><![CDATA[IoT]]></category>
            <category><![CDATA[Magic Transit]]></category>
            <category><![CDATA[Magic WAN]]></category>
            <category><![CDATA[Cloudflare One]]></category>
            <guid isPermaLink="false">5kxbpURa5uO3pOPgoXY9Ga</guid>
            <dc:creator>Chris Draper</dc:creator>
        </item>
        <item>
            <title><![CDATA[Keeping the Cloudflare API 'all green' using Python-based testing]]></title>
            <link>https://blog.cloudflare.com/keeping-cloudflare-api-all-green-using-python-based-testing/</link>
            <pubDate>Tue, 07 Mar 2023 19:00:00 GMT</pubDate>
            <description><![CDATA[ Scout is an automated system providing constant end to end testing and monitoring of live APIs over different environments and resources. Scout does it by periodically running self explanatory Python tests ]]></description>
            <content:encoded><![CDATA[ <p></p><p>At Cloudflare, we reuse existing core systems to power multiple products and testing of these core systems is essential. In particular, we require being able to have a wide and thorough visibility of our live APIs’ behaviors. We want to be able to detect regressions, prevent incidents and maintain healthy APIs. That is why we built Scout.</p><p>Scout is an automated system periodically running Python tests verifying the end to end behavior of our APIs. Scout allows us to evaluate APIs in production-like environments and thus ensures we can green light a production deployment while also monitoring the behavior of APIs in production.</p>
    <div>
      <h2>Why Scout?</h2>
      <a href="#why-scout">
        
      </a>
    </div>
    <p>Before Scout, we were using an automated test system leveraging the Robot Framework. This older system was limiting our testing capabilities. In fact, we could not easily match json responses against keys we were looking for. We would abandon covering different behaviors of our APIs as it was impossible to decide on which resources a given test suite would run. Two different test suites would create false negatives as they were running on the same account.</p><p>Regarding schema validation, only API responses were validated against a json schema and tests would not fail if the response did not match the schema. Moreover, It was impossible to validate API requests.</p><p>Test suites were run in a queue, making the delay to a new feature assessment dependent on the number of test suites to run. The queue would as well potentially make newer test suites run the following day. Hence we often ended up with a mismatch between tests and APIs versions. Test steps could not be run in parallel either.</p><p>We could not split test suites between different environments. If a new API feature was being developed it was impossible to write a test without first needing the actual feature to be released to production.</p><p>We built Scout to overcome all these difficulties. We wanted the developer experience to be easy and we wanted Scout to be fast and reliable while spotting any live API issue.</p>
    <div>
      <h3>A Scout test example</h3>
      <a href="#a-scout-test-example">
        
      </a>
    </div>
    <p>Scout is built in Python and leverages the functionalities of Pytest. Before diving into the exact capabilities of Scout and its architecture, let’s have a quick look at how to use it!</p><p>Following is an example of a Scout test on the Rulesets API (the docs are available <a href="https://developers.cloudflare.com/ruleset-engine/about/">here</a>):</p>
            <pre><code>from scout import requires, validate, Account, Zone

@validate(schema="rulesets", ignorePaths=["accounts/[^/]+/rules/lists"])
@requires(
    account=Account(
        entitlements={"rulesets.max_rules_per_ruleset": 2),
    zone=Zone(plan="ENT",
        entitlements={"rulesets.firewall_custom_phase_allowed": True},
        account_entitlements={"rulesets.max_rules_per_ruleset": 2 }))
class TestZone:
    def test_create_custom_ruleset(self, cfapi):
        response = cfapi.zone.request(
            "POST",
            "rulesets",
            payload=f"""{{
            "name": "My zone ruleset",
            "description": "My ruleset description",
            "phase": "http_request_firewall_custom",
            "kind": "zone",
            "rules": [
                {{
                    "description": "My rule",
                    "action": "block",
                    "expression": "http.host eq \"fake.net\""
                }}
            ]
        }}""")
        response.expect_json_success(
            200,
            result=f"""{{
            "name": "My zone ruleset",
            "version": "1",
            "source": "firewall_custom",
            "phase": "http_request_firewall_custom",
            "kind": "zone",
            "rules": [
                {{
                    "description": "My rule",
                    "action": "block",
                    "expression": "http.host eq \"fake.net\"",
                    "enabled": true,
                    ...
                }}
            ],
            ...
        }}""")</code></pre>
            <p>A Scout test is a succession of roundtrips of requests and responses against a given API. We use the functionalities of Pytest fixtures and marks to be able to target specific resources while validating the request and responses.  Pytest marks in Scout allow to provide an extra set of information to test suites. Pytest fixtures are contexts with information and methods which can be used across tests to enhance their capabilities. Hence the conjunction of marks with fixtures allow Scout to build the whole harness required to run a test suite against APIs.</p><p>Being able to exactly describe the resources against which a given test will run provides us confidence the live API behaves as expected under various conditions.</p><p>The <b><i>cfapi</i></b> fixture provides the capability to target different resources such as a Cloudflare account or a zone. In the test above, we use a Pytest mark <b><i>@requires</i></b> to describe the characteristics of the resources we want, e.g. we need here an account with a flag allowing us to have 2 rules for a ruleset. This will allow the test to only be run against accounts with such entitlements.</p><p>The <b><i>@validate</i></b> mark provides the capability to validate requests and responses to a given OpenAPI schema (here the rulesets OpenAPI schema). Any validation failure will be reported and flagged as a test failure.</p><p>Regarding the actual requests and responses, their payloads are described as f-strings, in particular the response f-string can be written as a “semi-json”:</p>
            <pre><code> response.expect_json_success(
            200,
            result=f"""{{
            "name": "My zone ruleset",
            "version": "1",
            "source": "firewall_custom",
            "phase": "phase_http_request_firewall_custom",
            "kind": "zone",
            "rules": [
                {{
                    "description": "My rule",
                    "action": "block",
                    "expression": "http.host eq \"fake.net\"",
                    "enabled": true,
                    ...
                }}
            ],
            ...
        }}""")</code></pre>
            <p>Among many test assertions possible, Scout can assert the validity of a partial json response and it will log the information. We added the handling of ellipsis <b>…</b> as an indication for Scout not to care about any further fields at a given json nesting level. Hence, we are able to do partial matching on JSON API responses, thus focusing only on what matters the most in each test.</p><p>Once a test suite run is complete, the results are pushed by the service and stored using Cloudflare Workers KV. They are displayed via a Cloudflare Worker.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2iDExs9LHFvCjTMC12zsSp/f7fcbbf636cb5a959981fbdfad3fb94f/image6-2.png" />
            
            </figure><p>Scout is run in separate environments such as production-like and production environments. It is part of our deployment process to verify Scout is <i>green</i> in our production-like environment prior to deploying to production where Scout is also used for monitoring purposes.</p>
    <div>
      <h2>How we built it</h2>
      <a href="#how-we-built-it">
        
      </a>
    </div>
    <p>The core of Scout is written in Python and it is a combination of three components interacting together:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4WgzfD8Wk6Vyd0uTE7hfNk/ccea6ac729b0e35906088af25b601cb9/image4-3.png" />
            
            </figure><ul><li><p><b>The Scout plugin</b>: a Pytest plugin to write tests easily</p></li><li><p><b>The Scout service</b>: a scheduler service to run the test suites periodically</p></li><li><p><b>The Scout Worker</b>: a collector and presenter of test reports</p></li></ul>
    <div>
      <h3>The Scout plugin</h3>
      <a href="#the-scout-plugin">
        
      </a>
    </div>
    <p>This is the core component of the Scout system. It allows us to write self explanatory tests while ensuring a high level of compliance against OpenAPI schemas and verifying the APIs’ behaviors.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3IAEu33ULvtQorn3fZWBrn/0ce45e8506e6b37293cdd90ac68da651/image1-2.png" />
            
            </figure><p>The Scout plugin architecture can be split into three components: setup, resource allocator, and runners. Setup is a conjunction of multiple sub components in charge of setting up the plugin.</p><p>The Registry contains all the information regarding a pool of accounts and zones we use for testing. As an example, entitlements are flags gating customers for using products features, the Registry provides the capability to describe entitlements per account and zone so that Scout can run a test against a specific setup.</p><p>As explained earlier, Scout can validate requests and responses against OpenAPI schemas. This is the responsibility of validators. A validator is built per OpenAPI schema and can be selected via the <b><i>@validate</i></b> mark we saw above.</p>
            <pre><code>@validate(schema="rulesets", ignorePaths=["accounts/[^/]+/rules/lists"])</code></pre>
            <p>As soon as a validator is selected, all the interaction of a given test with an API will be validated. If there is a validation failure, it will be marked as a test failure.</p><p>Last element of the setup, the config reader. It is the sub component in charge of providing all the URLs and authentication elements required for the Scout plugin to communicate with APIs.</p><p>Next in the chain, the resources allocator. This component is in charge of consuming the configuration and objects of the setup to build multiple runners. This is a factory which will make available the runners in the <b><i>cfapi</i></b> fixture.</p>
            <pre><code>response = cfapi.zone.request(method, path, payload)</code></pre>
            <p>When such a line of code is processed, it is the actual method <b><i>request</i></b> of the zone runner allocated for the test which is executed. Actually, the resources allocator is able to provide specialized runners (account, zone or default) which grant the possibility of targeting specific API endpoints for a given account or zone.</p><p>Runners are in charge of handling the execution of requests, managing the test expectations and using the validators for request/response schema validation.</p><p>Any failure on expectation or validation and any exceptions are recorded in the stash. The stash is shared across all runners. As such, when a test setup, run or cleanup is processed, the timeline of execution and potential retries are logged in the stash. The stash contents are later used for building the test suite reports.</p><p>Scout is able to run multiple test steps in parallel. Actually, each resource couple (Account Runner, Zone Runner) is associated with a Pytest-xdist worker which runs test steps independently. There can be as many workers as there are resource couples. An extra “default” runner is provided for reaching our different APIs and/or URLs with or without authentication.</p><p>Testing a test system was not the easiest part. We have been required to build a fake API and assert the Scout plugin would behave as it should in different situations. We reached and maintained a test coverage confidence which was considered good (close to 90%) for using the Scout plugin permanently.</p>
    <div>
      <h3>The Scout service</h3>
      <a href="#the-scout-service">
        
      </a>
    </div>
    <p>The Scout service is meant to schedule test suites periodically. It is a configurable scheduler providing a reporting harness for the test suites as well as multiple metrics. It was a design decision to build a scheduler instead of using cron jobs.</p><p>We wanted to be aware of any scheduling issue as well as run issues. For this we used Prometheus metrics. The problem is that the Prometheus default configuration is to scrape metrics advertised by services. This scraping happens periodically and we were concerned about the eventuality of missing metrics if a cron job was to finish prior to the next Prometheus metrics scraping. As such we decided a small scheduler was better suited for overall observability of the test runs. Among the metrics the Scout service provides are network failures, general test failures, reporting failures, tests lagging and more.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/42kpAT26a338B07zUR59ke/ff248a59d66687ac0d31ef7c4d2544a9/image3-3.png" />
            
            </figure><p>The Scout service runs threads on configured periods. Each thread is a test suite run as a separate Pytest with Scout plugin process followed by a reporting execution consuming the results and publishing them to the relevant parties.</p><p>The reporting component provided to each thread publishes the report to Workers KV and notifies us on chat in case there is a failure. Reporting takes also care of publishing the information relevant for building API testing coverage. In fact it is mandatory for us to have coverage of all the API endpoints and their possible methods so that we can achieve a wide and thorough visibility of our live APIs.</p><p>As a fallback, if there are any thread failure, test failure or reporting failure we are alerted based on the Prometheus metrics being updated across the service execution. The logs of the Scout service as well as the logs of each Pytest-Scout plugin execution provide the last resort information if no metrics are available and reporting is failing.</p><p>The service can be deployed with a minimal YAML configuration and be set up for different environments. We can for example decide to run different test suites based on the environment, publish or not to Cloudflare Workers, set different periods and retry mechanisms and so on.</p><p>We keep the tests as part of our code base alongside the configuration of the Scout service, and that’s about it, the Scout service is a separate entity.</p>
    <div>
      <h3>The Scout Worker</h3>
      <a href="#the-scout-worker">
        
      </a>
    </div>
    <p>It is a Cloudflare worker in charge of fetching the most recent Worker KVs and displaying them in an eye pleasing manner. The Scout service publishes a test report as JSON, thus the Scout worker parses the report and displays its content based on the status of the test suite run.</p><p>For example, we present below an authentication failure during a test which resulted in such a display in the worker:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/42dMShTD7SrsgASIKbpCcZ/71e84ef0e898fcad0327d495aeb8cfe5/image5.png" />
            
            </figure>
    <div>
      <h2>What does Scout let us do</h2>
      <a href="#what-does-scout-let-us-do">
        
      </a>
    </div>
    <p>Through leveraging the capabilities of Pytest and Cloudflare Workers, we have been able to build a configurable, robust and reliable system which allows us to easily write self explanatory tests for our APIs.</p><p>We can validate requests and responses against OpenAPI schemas and test behaviors over specific resources while getting alerted through multiple means if something goes wrong.</p><p>For specific use cases, we can write a test verifying the API behaves as it should, the configuration to be pushed at the edge is valid and a given zone will react as it should to security threats. Thus going beyond an end-to-end API test.</p><p>Scout quickly became our permanent live tester and monitor of APIs. We wrote tests for all endpoints to maintain a wide coverage of all our APIs. Scout has since been used for verifying an API version prior to its deployment to production. In fact, after a deployment in a production-like environment we can know in a couple of minutes if a new feature is good to go to production and assess if it is behaving correctly.</p><p>We hope you enjoyed this deep dive description into one of our systems!</p> ]]></content:encoded>
            <category><![CDATA[Monitoring]]></category>
            <category><![CDATA[API]]></category>
            <guid isPermaLink="false">2rnwmGJBmwG1wAJdzvcSG1</guid>
            <dc:creator>Elie Mitrani</dc:creator>
        </item>
        <item>
            <title><![CDATA[Monitoring our monitoring: how we validate our Prometheus alert rules]]></title>
            <link>https://blog.cloudflare.com/monitoring-our-monitoring/</link>
            <pubDate>Thu, 19 May 2022 15:39:19 GMT</pubDate>
            <description><![CDATA[ Pint is a tool we developed to validate our Prometheus alerting rules and ensure they are always working ]]></description>
            <content:encoded><![CDATA[ <p></p>
    <div>
      <h3>Background</h3>
      <a href="#background">
        
      </a>
    </div>
    <p>We use <a href="https://prometheus.io/">Prometheus</a> as our core monitoring system. We’ve been heavy Prometheus users since 2017 when we migrated off our previous monitoring system which used a customized <a href="https://www.nagios.org/">Nagios</a> setup. Despite growing our infrastructure a lot, adding tons of new products and learning some hard lessons about operating Prometheus at scale, our original architecture of Prometheus (see <a href="https://www.youtube.com/watch?v=ypWwvz5t_LE">Monitoring Cloudflare's Planet-Scale Edge Network with Prometheus</a> for an in depth walk through) remains virtually unchanged, proving that Prometheus is a solid foundation for building <a href="https://www.cloudflare.com/learning/performance/what-is-observability/">observability</a> into your services.</p><p>One of the key responsibilities of Prometheus is to alert us when something goes wrong and in this blog post we’ll talk about how we make those alerts more reliable - and we’ll introduce an open source tool we’ve developed to help us with that, and share how you can use it too. If you’re not familiar with Prometheus you might want to start by watching <a href="https://www.youtube.com/watch?v=PzFUwBflXYc">this video</a> to better understand the topic we’ll be covering here.</p><p>Prometheus works by collecting metrics from our services and storing those metrics inside its database, called TSDB. We can then <a href="https://prometheus.io/docs/prometheus/latest/querying/basics/">query these metrics</a> using Prometheus query language called PromQL using ad-hoc queries (for example to power <a href="https://grafana.com/grafana/">Grafana dashboards</a>) or via <a href="https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/">alerting</a> or <a href="https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/">recording</a> rules. A rule is basically a query that Prometheus will run for us in a loop, and when that query returns any results it will either be recorded as new metrics (with recording rules) or trigger alerts (with alerting rules).</p>
    <div>
      <h3>Prometheus alerts</h3>
      <a href="#prometheus-alerts">
        
      </a>
    </div>
    <p>Since we’re talking about improving our alerting we’ll be focusing on alerting rules.</p><p>To create alerts we first need to have some metrics collected. For the purposes of this blog post let’s assume we’re working with http_requests_total metric, which is used on the examples <a href="https://prometheus.io/docs/prometheus/latest/querying/examples/">page</a>. Here are some examples of how our metrics will look:</p>
            <pre><code>http_requests_total{job="myserver", handler="/", method=”get”, status=”200”}
http_requests_total{job="myserver", handler="/", method=”get”, status=”500”}
http_requests_total{job="myserver", handler="/posts", method=”get”, status=”200”}
http_requests_total{job="myserver", handler="/posts", method=”get”, status=”500”}
http_requests_total{job="myserver", handler="/posts/new", method=”post”, status=”201”}
http_requests_total{job="myserver", handler="/posts/new", method=”post”, status=”401”}</code></pre>
            <p>Let’s say we want to alert if our HTTP server is returning errors to customers.</p><p>Since, all we need to do is check our metric that tracks how many responses with HTTP status code 500 there were, a simple alerting rule could like this:</p>
            <pre><code>- alert: Serving HTTP 500 errors
  expr: http_requests_total{status=”500”} &gt; 0</code></pre>
            <p>This will alert us if we have any 500 errors served to our customers. Prometheus will run our query looking for a time series named http_requests_total that also has a <b>status</b> label with value <b>“500”</b>. Then it will filter all those matched time series and only return ones with value greater than zero.</p><p>If our alert rule returns any results a fire will be triggered, one for each returned result.</p><p>If our rule doesn’t return anything, meaning there are no matched time series, then alert will not trigger.</p><p>The whole flow from metric to alert is pretty simple here as we can see on the diagram below.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/57mqOPxVQ847pKAkMden9K/8ba472d0359bc3fcfd09535cc04e82b7/1-3.png" />
            
            </figure><p>If we want to provide more information in the alert we can by setting additional labels and annotations, but alert and expr fields are all we need to get a working rule.</p><p>But the problem with the above rule is that our alert starts when we have our first error, and then it will never go away.</p><p>After all, our http_requests_total is a counter, so it gets incremented every time there’s a new request, which means that it will keep growing as we receive more requests. What this means for us is that our alert is really telling us “was there ever a 500 error?” and even if we fix the problem causing 500 errors we’ll keep getting this alert.</p><p>A better alert would be one that tells us if we’re serving errors <b>right now</b>.</p><p>For that we can use the <a href="https://prometheus.io/docs/prometheus/latest/querying/functions/#rate">rate()</a> function to calculate the per second rate of errors.</p><p>Our modified alert would be:</p>
            <pre><code>- alert: Serving HTTP 500 errors
  expr: rate(http_requests_total{status=”500”}[2m]) &gt; 0</code></pre>
            <p>The query above will calculate the rate of 500 errors in the last two minutes. If we start responding with errors to customers our alert will fire, but once errors stop so will this alert.</p><p>This is great because if the underlying issue is resolved the alert will resolve too.</p><p>We can improve our alert further by, for example, alerting on the percentage of errors, rather than absolute numbers, or even calculate error budget, but let’s stop here for now.</p><p>It’s all very simple, so what do we mean when we talk about improving the reliability of alerting? What could go wrong here?</p>
    <div>
      <h3>What could go wrong?</h3>
      <a href="#what-could-go-wrong">
        
      </a>
    </div>
    <p>We can craft a valid YAML file with a rule definition that has a perfectly valid query that will simply not work how we expect it to work. Which, when it comes to alerting rules, might mean that the alert we rely upon to tell us when something is not working correctly will fail to alert us when it should. To better understand why that might happen let’s first explain how querying works in Prometheus.</p>
    <div>
      <h3>Prometheus querying basics</h3>
      <a href="#prometheus-querying-basics">
        
      </a>
    </div>
    <p>There are two basic types of queries we can run against Prometheus. The first one is an <b>instant query</b>. It allows us to ask Prometheus for a point in time value of some time series. If we write our query as http_requests_total we’ll get all time series named http_requests_total along with the most recent value for each of them. We can further customize the query and filter results by adding label matchers, like http_requests_total{status=”500”}.</p><p>Let’s consider we have two instances of our server, green and red, each one is scraped (Prometheus collects metrics from it) every one minute (independently of each other).</p><p>This is what happens when we issue an instant query:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2KyKw0PTiNywwYY6euDbuH/7910f018372888b78a4609d16395f1d4/2-fixed.png" />
            
            </figure><p>There’s obviously more to it as we can use <a href="https://prometheus.io/docs/prometheus/latest/querying/functions/">functions</a> and build complex queries that utilize multiple metrics in one expression. But for the purposes of this blog post we’ll stop here.</p><p>The important thing to know about instant queries is that they return the most recent value of a matched time series, and they will look back for up to five minutes (by default) into the past to find it. If the last value is older than five minutes then it’s considered stale and Prometheus won’t return it anymore.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4V3s3dupV4RHtWcAwbPpYd/bd14209ea0ebb73807da506422ccc5f3/3-fixed.png" />
            
            </figure><p>The second type of query is a <b>range query</b> - it works similarly to instant queries, the difference is that instead of returning us the most recent value it gives us a list of values from the selected time range. That time range is always relative so instead of providing two timestamps we provide a range, like “20 minutes”. When we ask for a range query with a 20 minutes range it will return us all values collected for matching time series from 20 minutes ago until now.</p><p>An important distinction between those two types of queries is that range queries don’t have the same “look back for up to five minutes” behavior as instant queries. If Prometheus cannot find any values collected in the provided time range then it doesn’t return anything.</p><p>If we modify our example to request [3m] range query we should expect Prometheus to return three data points for each time series:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1dBwnYThdf3ioljbPdmSKV/54b428335adffe68f87cd40ff80e3ecc/4-fixed.png" />
            
            </figure>
    <div>
      <h3>When queries don’t return anything</h3>
      <a href="#when-queries-dont-return-anything">
        
      </a>
    </div>
    <p>Knowing a bit more about how queries work in Prometheus we can go back to our alerting rules and spot a potential problem: queries that don’t return anything.</p><p>If our query doesn’t match any time series or if they’re considered stale then Prometheus will return an empty result. This might be because we’ve made a typo in the metric name or label filter, the metric we ask for is no longer being exported, or it was never there in the first place, or we’ve added some condition that wasn’t satisfied, like value of being non-zero in our http_requests_total{status=”500”} &gt; 0 example.</p><p>Prometheus will not return any error in any of the scenarios above because none of them are really problems, it’s just how querying works. If you ask for something that doesn’t match your query then you get empty results. This means that there’s no distinction between “all systems are operational” and “you’ve made a typo in your query”. So if you’re not receiving any alerts from your service it’s either a sign that everything is working fine, or that you’ve made a typo, and you have no working monitoring at all, and it’s up to you to verify which one it is.</p><p>For example, we could be trying to query for http_requests_totals instead of http_requests_total (an extra “s” at the end) and although our query will look fine it won’t ever produce any alert.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2p2hNra24jqNgnfeaaLZ6S/9a07d508dcf2c6a1417fe5b3dc4ad46b/5-1.png" />
            
            </figure><p>Range queries can add another twist - they’re mostly used in Prometheus functions like rate(),  which we used in our example. This function will only work correctly if it receives a range query expression that returns at least two data points for each time series, after all it’s impossible to calculate rate from a single number.</p><p>Since the number of data points depends on the time range we passed to the range query, which we then pass to our rate() function, if we provide a time range that only contains a single value then rate won’t be able to calculate anything and once again we’ll return empty results.</p><p>The number of values collected in a given time range depends on the interval at which Prometheus collects all metrics, so to use rate() correctly you need to know how your Prometheus server is configured. You can read more about this <a href="https://www.robustperception.io/what-range-should-i-use-with-rate">here</a> and <a href="https://promlabs.com/blog/2021/01/29/how-exactly-does-promql-calculate-rates">here</a> if you want to better understand how rate() works in Prometheus.</p><p>For example if we collect our metrics every one minute then a range query http_requests_total[1m] will be able to find only one data point. Here’s a reminder of how this looks:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3ghp7yB5qlTe23SxtTKEm3/c66a1cbe0687303ea58e672783bfdef9/6-fixed.png" />
            
            </figure><p>Since, as we mentioned before, we can only calculate rate() if we have at least two data points, calling rate(http_requests_total[1m]) will never return anything and so our alerts will never work.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1xBQkUPr4OsGRpAbeXXtmc/093488f9d4c3c129897cb0dc62190031/7.png" />
            
            </figure><p>There are more potential problems we can run into when writing Prometheus queries, for example any operations between two metrics will only work if both have the same set of labels, you can read about this <a href="https://prometheus.io/docs/prometheus/latest/querying/operators/#vector-matching">here</a>. But for now we’ll stop here, listing all the gotchas could take a while. The point to remember is simple: if your alerting query doesn’t return anything then it might be that everything is ok and there’s no need to alert, but it might also be that you’ve mistyped your metrics name, your label filter cannot match anything, your metric disappeared from Prometheus, you are using too small time range for your range queries etc.</p>
    <div>
      <h3>Renaming metrics can be dangerous</h3>
      <a href="#renaming-metrics-can-be-dangerous">
        
      </a>
    </div>
    <p>We’ve been running Prometheus for a few years now and during that time we’ve grown our collection of alerting rules a lot. Plus we keep adding new products or modifying existing ones, which often includes adding and removing metrics, or modifying existing metrics, which may include renaming them or changing what labels are present on these metrics.</p><p>A lot of metrics come from metrics exporters maintained by the Prometheus community, like <a href="https://github.com/prometheus/node_exporter">node_exporter</a>, which we use to gather some operating system metrics from all of our servers. Those exporters also undergo changes which might mean that some metrics are deprecated and removed, or simply renamed.</p><p>A problem we’ve run into a few times is that sometimes our alerting rules wouldn’t be updated after such a change, for example when we upgraded node_exporter across our fleet. Or the addition of a new label on some metrics would suddenly cause Prometheus to no longer return anything for some of the alerting queries we have, making such an alerting rule no longer useful.</p><p>It’s worth noting that Prometheus does have a way of <a href="https://prometheus.io/docs/prometheus/latest/configuration/unit_testing_rules/">unit testing rules</a>, but since it works on mocked data it’s mostly useful to validate the logic of a query. Unit testing won’t tell us if, for example, a metric we rely on suddenly disappeared from Prometheus.</p>
    <div>
      <h3>Chaining rules</h3>
      <a href="#chaining-rules">
        
      </a>
    </div>
    <p>When writing alerting rules we try to limit <a href="https://en.wikipedia.org/wiki/Alarm_fatigue">alert fatigue</a> by ensuring that, among many things, alerts are only generated when there’s an action needed, they clearly describe the problem that needs addressing, they have a link to a runbook and a dashboard, and finally that we aggregate them as much as possible. This means that a lot of the alerts we have won’t trigger for each individual instance of a service that’s affected, but rather once per data center or even globally.</p><p>For example, we might alert if the rate of HTTP errors in a datacenter is above 1% of all requests. To do that we first need to calculate the overall rate of errors across all instances of our server. For that we would use a <a href="https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/">recording rule</a>:</p>
            <pre><code>- record: job:http_requests_total:rate2m
  expr: sum(rate(http_requests_total[2m])) without(method, status, instance)

- record: job:http_requests_status500:rate2m
  expr: sum(rate(http_requests_total{status=”500”}[2m])) without(method, status, instance)</code></pre>
            <p>First rule will tell Prometheus to calculate per second rate of all requests and sum it across all instances of our server. Second rule does the same but only sums time series with status labels equal to “500”. Both rules will produce new metrics named after the value of the <b>record</b> field.</p><p>Now we can modify our alert rule to use those new metrics we’re generating with our recording rules:</p>
            <pre><code>- alert: Serving HTTP 500 errors
  expr: job:http_requests_status500:rate2m / job:http_requests_total:rate2m &gt; 0.01</code></pre>
            <p>If we have a data center wide problem then we will raise just one alert, rather than one per instance of our server, which can be a great quality of life improvement for our on-call engineers.</p><p>But at the same time we’ve added two new rules that we need to maintain and ensure they produce results. To make things more complicated we could have recording rules producing metrics based on other recording rules, and then we have even more rules that we need to ensure are working correctly.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3j3cCrMguixvR5m9ZTnhPH/b38180795e05fd9d266f3e90d0e731d7/8.png" />
            
            </figure><p>What if all those rules in our chain are maintained by different teams? What if the rule in the middle of the chain suddenly gets renamed because that’s needed by one of the teams? Problems like that can easily crop up now and then if your environment is sufficiently complex, and when they do, they’re not always obvious, after all the only sign that something stopped working is, well, silence - your alerts no longer trigger. If you’re lucky you’re plotting your metrics on a dashboard somewhere and hopefully someone will notice if they become empty, but it’s risky to rely on this.</p><p>We definitely felt that we needed something better than hope.</p>
    <div>
      <h3>Introducing pint: a Prometheus rule linter</h3>
      <a href="#introducing-pint-a-prometheus-rule-linter">
        
      </a>
    </div>
    <p>To avoid running into such problems in the future we’ve decided to write a tool that would help us do a better job of testing our alerting rules against live Prometheus servers, so we can spot missing metrics or typos easier. We also wanted to allow new engineers, who might not necessarily have all the in-depth knowledge of how Prometheus works, to be able to write rules with confidence without having to get feedback from more experienced team members.</p><p>Since we believe that such a tool will have value for the entire Prometheus community we’ve open-sourced it, and it’s available for anyone to use - say hello to pint!</p><p>You can find sources on <a href="https://github.com/cloudflare/pint">github</a>, there’s also <a href="https://cloudflare.github.io/pint/">online documentation</a> that should help you get started.</p><p>Pint works in 3 different ways:</p><ul><li><p>You can run it against a file(s) with Prometheus rules</p></li><li><p>It can run as a part of your CI pipeline</p></li><li><p>Or you can deploy it as a side-car to all your Prometheus servers</p></li></ul><p>It doesn’t require any configuration to run, but in most cases it will provide the most value if you create a configuration file for it and define some Prometheus servers it should use to validate all rules against. Running without any configured Prometheus servers will limit it to static analysis of all the rules, which can identify a range of problems, but won’t tell you if your rules are trying to query non-existent metrics.</p><p>First mode is where pint reads a file (or a directory containing multiple files), parses it, does all the basic syntax checks and then runs a series of checks for all Prometheus rules in those files.</p><p>Second mode is optimized for validating git based pull requests. Instead of testing all rules from all files pint will only test rules that were modified and report only problems affecting modified lines.</p><p>Third mode is where pint runs as a daemon and tests all rules on a regular basis. If it detects any problem it will expose those problems as metrics. You can then collect those metrics using Prometheus and alert on them as you would for any other problems. This way you can basically use Prometheus to monitor itself.</p><p>What kind of checks can it run for us and what kind of problems can it detect?</p><p>All the checks are documented <a href="https://cloudflare.github.io/pint/checks/">here</a>, along with some tips on how to deal with any detected problems. Let’s cover the most important ones briefly.</p><p>As mentioned above the main motivation was to catch rules that try to query metrics that are missing or when the query was simply mistyped. To do that pint will run each query from every alerting and recording rule to see if it returns any result, if it doesn’t then it will break down this query to identify all individual metrics and check for the existence of each of them. If any of them is missing or if the query tries to filter using labels that aren’t present on any time series for a given metric then it will report that back to us.</p><p>So if someone tries to add a new alerting rule with http_requests_totals typo in it, pint will detect that when running CI checks on the pull request and stop it from being merged. Which takes care of validating rules as they are being added to our configuration management system.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6pVy8R1PRoDvIt8jgooQJu/0bcf4d8f15e4eec4a61a854603153ffc/9.png" />
            
            </figure><p>Another useful check will try to estimate the number of times a given alerting rule would trigger an alert. Which is useful when raising a pull request that’s adding new alerting rules - nobody wants to be flooded with alerts from a rule that’s too sensitive so having this information on a pull request allows us to spot rules that could lead to alert fatigue.</p><p>Similarly, another check will provide information on how many new time series a recording rule adds to Prometheus. In our setup a single unique time series uses, on average, 4KiB of memory. So if a recording rule generates 10 thousand new time series it will increase Prometheus server memory usage by 10000*4KiB=40MiB. 40 megabytes might not sound like but our peak time series usage in the last year was around 30 million time series in a single Prometheus server, so we pay attention to anything that’s might add a substantial amount of new time series, which pint helps us to notice before such rule gets added to Prometheus.</p><p>On top of all the Prometheus query checks, pint allows us also to ensure that all the alerting rules comply with some policies we’ve set for ourselves. For example, we require everyone to write a runbook for their alerts and link to it in the alerting rule using annotations.</p><p>We also require all alerts to have priority labels, so that high priority alerts are generating pages for responsible teams, while low priority ones are only routed to <a href="https://github.com/prymitive/karma">karma dashboard</a> or create tickets using <a href="https://github.com/prometheus-community/jiralert">jiralert</a>. It’s easy to forget about one of these required fields and that’s not something which can be enforced using unit testing, but pint allows us to do that with a few configuration lines.</p><p>With pint running on all stages of our Prometheus rule life cycle, from initial pull request to monitoring rules deployed in our many data centers, we can rely on our Prometheus alerting rules to always work and notify us of any incident, large or small.</p><p>GitHub: <a href="https://github.com/cloudflare/pint">https://github.com/cloudflare/pint</a></p>
    <div>
      <h3>Putting it all together</h3>
      <a href="#putting-it-all-together">
        
      </a>
    </div>
    <p>Let’s see how we can use pint to validate our rules as we work on them.</p><p>We can begin by creating a file called “rules.yml” and adding both recording rules there.</p><p>The goal is to write new rules that we want to add to Prometheus, but before we actually add those, we want pint to validate it all for us.</p>
            <pre><code>groups:
- name: Demo recording rules
  rules:
  - record: job:http_requests_total:rate2m
    expr: sum(rate(http_requests_total[2m])) without(method, status, instance)

  - record: job:http_requests_status500:rate2m
    expr: sum(rate(http_requests_total{status="500"}[2m]) without(method, status, instance)</code></pre>
            <p>Next we’ll download the latest version of pint from <a href="https://github.com/cloudflare/pint/releases">GitHub</a> and run check our rules.</p>
            <pre><code>$ pint lint rules.yml 
level=info msg="File parsed" path=rules.yml rules=2
rules.yml:8: syntax error: unclosed left parenthesis (promql/syntax)
    expr: sum(rate(http_requests_total{status="500"}[2m]) without(method, status, instance)

level=info msg="Problems found" Fatal=1
level=fatal msg="Execution completed with error(s)" error="problems found"</code></pre>
            <p>Whoops, we have “sum(rate(...)” and so we’re missing one of the closing brackets. Let’s fix that and try again.</p>
            <pre><code>groups:
- name: Demo recording rules
  rules:
  - record: job:http_requests_total:rate2m
    expr: sum(rate(http_requests_total[2m])) without(method, status, instance)

  - record: job:http_requests_status500:rate2m
    expr: sum(rate(http_requests_total{status="500"}[2m])) without(method, status, instance)</code></pre>
            
            <pre><code>$ pint lint rules.yml 
level=info msg="File parsed" path=rules.yml rules=2</code></pre>
            <p>Our rule now passes the most basic checks, so we know it’s valid. But to know if it works with a real Prometheus server we need to tell pint how to talk to Prometheus. For that we’ll need a config file that defines a Prometheus server we test our rule against, it should be the same server we’re planning to deploy our rule to. Here we’ll be using a test instance running on localhost. Let’s create a “pint.hcl” file and define our Prometheus server there:</p>
            <pre><code>prometheus "prom1" {
  uri     = "http://localhost:9090"
  timeout = "1m"
}</code></pre>
            <p>Now we can re-run our check using this configuration file:</p>
            <pre><code>$ pint -c pint.hcl lint rules.yml 
level=info msg="Loading configuration file" path=pint.hcl
level=info msg="File parsed" path=rules.yml rules=2
rules.yml:5: prometheus "prom1" at http://localhost:9090 didn't have any series for "http_requests_total" metric in the last 1w (promql/series)
    expr: sum(rate(http_requests_total[2m])) without(method, status, instance)

rules.yml:8: prometheus "prom1" at http://localhost:9090 didn't have any series for "http_requests_total" metric in the last 1w (promql/series)
    expr: sum(rate(http_requests_total{status="500"}[2m])) without(method, status, instance)

level=info msg="Problems found" Bug=2
level=fatal msg="Execution completed with error(s)" error="problems found"</code></pre>
            <p>Yikes! It’s a test Prometheus instance, and we forgot to collect any metrics from it.</p><p>Let’s fix that by starting our server locally on port 8080 and configuring Prometheus to collect metrics from it:</p>
            <pre><code>scrape_configs:
  - job_name: webserver
    static_configs:
      - targets: ['localhost:8080’]</code></pre>
            <p>Let’ re-run our checks once more:</p>
            <pre><code>$ pint -c pint.hcl lint rules.yml 
level=info msg="Loading configuration file" path=pint.hcl
level=info msg="File parsed" path=rules.yml rules=2</code></pre>
            <p>This time everything works!</p><p>Now let’s add our alerting rule to our file, so it now looks like this:</p>
            <pre><code>groups:
- name: Demo recording rules
  rules:
  - record: job:http_requests_total:rate2m
    expr: sum(rate(http_requests_total[2m])) without(method, status, instance)

  - record: job:http_requests_status500:rate2m
    expr: sum(rate(http_requests_total{status="500"}[2m])) without(method, status, instance)

- name: Demo alerting rules
  rules:
  - alert: Serving HTTP 500 errors
    expr: job:http_requests_status500:rate2m / job:http_requests_total:rate2m &gt; 0.01</code></pre>
            <p>And let’s re-run pint once again:</p>
            <pre><code>$ pint -c pint.hcl lint rules.yml 
level=info msg="Loading configuration file" path=pint.hcl
level=info msg="File parsed" path=rules.yml rules=3
rules.yml:13: prometheus "prom1" at http://localhost:9090 didn't have any series for "job:http_requests_status500:rate2m" metric in the last 1w but found recording rule that generates it, skipping further checks (promql/series)
    expr: job:http_requests_status500:rate2m / job:http_requests_total:rate2m &gt; 0.01

rules.yml:13: prometheus "prom1" at http://localhost:9090 didn't have any series for "job:http_requests_total:rate2m" metric in the last 1w but found recording rule that generates it, skipping further checks (promql/series)
    expr: job:http_requests_status500:rate2m / job:http_requests_total:rate2m &gt; 0.01

level=info msg="Problems found" Information=2</code></pre>
            <p>It all works according to pint, and so we now can safely deploy our new rules file to Prometheus.</p><p>Notice that pint recognised that both metrics used in our alert come from recording rules, which aren’t yet added to Prometheus, so there’s no point querying Prometheus to verify if they exist there.</p><p>Now what happens if we deploy a new version of our server that renames the “status” label to something else, like “code”?</p>
            <pre><code>$ pint -c pint.hcl lint rules.yml 
level=info msg="Loading configuration file" path=pint.hcl
level=info msg="File parsed" path=rules.yml rules=3
rules.yml:8: prometheus "prom1" at http://localhost:9090 has "http_requests_total" metric but there are no series with "status" label in the last 1w (promql/series)
    expr: sum(rate(http_requests_total{status="500"}[2m])) without(method, status, instance)

rules.yml:13: prometheus "prom1" at http://localhost:9090 didn't have any series for "job:http_requests_status500:rate2m" metric in the last 1w but found recording rule that generates it, skipping further checks (promql/series)
    expr: job:http_requests_status500:rate2m / job:http_requests_total:rate2m &gt; 0.01

level=info msg="Problems found" Bug=1 Information=1
level=fatal msg="Execution completed with error(s)" error="problems found"</code></pre>
            <p>Luckily pint will notice this and report it, so we can adopt our rule to match the new name.</p><p>But what if that happens after we deploy our rule? For that we can use the “pint watch” command that runs pint as a daemon periodically checking all rules.</p><p>Please note that validating all metrics used in a query will eventually produce some false positives. In our example metrics with status=”500” label might not be exported by our server until there’s at least one request ending in HTTP 500 error.</p><p>The <a href="https://cloudflare.github.io/pint/checks/promql/series.html">promql/series</a> check responsible for validating presence of all metrics has some documentation on how to deal with this problem. In most cases you’ll want to add a comment that instructs pint to ignore some missing metrics entirely or stop checking label values (only check if there’s “status” label present, without checking if there are time series with status=”500”).</p>
    <div>
      <h3>Summary</h3>
      <a href="#summary">
        
      </a>
    </div>
    <p>Prometheus metrics don’t follow any strict schema, whatever services expose will be collected. At the same time a lot of problems with queries hide behind empty results, which makes noticing these problems non-trivial.</p><p>We use pint to find such problems and report them to engineers, so that our global network is always monitored correctly, and we have confidence that lack of alerts proves how reliable our infrastructure is.</p> ]]></content:encoded>
            <category><![CDATA[Monitoring]]></category>
            <category><![CDATA[Prometheus]]></category>
            <category><![CDATA[Observability]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <guid isPermaLink="false">YZO15EI2Aw87tTU31VtF0</guid>
            <dc:creator>Lukasz Mierzwa</dc:creator>
        </item>
        <item>
            <title><![CDATA[Automatic Remediation of Kubernetes Nodes]]></title>
            <link>https://blog.cloudflare.com/automatic-remediation-of-kubernetes-nodes/</link>
            <pubDate>Thu, 15 Jul 2021 12:59:42 GMT</pubDate>
            <description><![CDATA[ In Cloudflare’s core data centers, we are using Kubernetes to run many of the diverse services that help us control Cloudflare’s edge. We are automating some aspects of node remediation to keep the Kubernetes clusters healthy. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>We use <a href="https://kubernetes.io/">Kubernetes</a> to run many of the diverse services that help us control Cloudflare’s edge. We have five geographically diverse clusters, with hundreds of nodes in our largest cluster. These clusters are self-managed on bare-metal machines which gives us a good amount of power and flexibility in the software and integrations with Kubernetes. However, it also means we don’t have a cloud provider to rely on for virtualizing or managing the nodes. This distinction becomes even more prominent when considering all the different reasons that nodes degrade. With self-managed bare-metal machines, the list of reasons that cause a node to become unhealthy include:</p><ul><li><p>Hardware failures</p></li><li><p>Kernel-level software failures</p></li><li><p>Kubernetes cluster-level software failures</p></li><li><p>Degraded network communication</p></li><li><p>Software updates are required</p></li><li><p>Resource exhaustion<sup>1</sup></p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/59IDEspjCRavbrtH1Rtu4r/a5f057aa79b2aae10fc0073b8f3b5573/image2-5.png" />
            
            </figure>
    <div>
      <h2>Unhappy Nodes</h2>
      <a href="#unhappy-nodes">
        
      </a>
    </div>
    <p>We have plenty of examples of failures in the aforementioned categories, but one example has been particularly tedious to deal with. It starts with the following log line from the kernel:</p>
            <pre><code>unregister_netdevice: waiting for lo to become free. Usage count = 1</code></pre>
            <p>The issue is further observed with the number of network interfaces on the node owned by the Container Network Interface (CNI) plugin getting out of proportion with the number of running pods:</p>
            <pre><code>$ ip link | grep cali | wc -l
1088</code></pre>
            <p>This is unexpected as it shouldn't exceed the maximum number of pods allowed on a node (we use the default limit of 110). While this issue is interesting and perhaps worthy of a whole separate blog, the short of it is that the Linux network interfaces owned by the CNI are not getting cleaned up after a pod terminates.</p><p>Some history on this can be read in a <a href="https://github.com/moby/moby/issues/5618">Docker GitHub issue</a>. We found this seems to plague nodes with a longer uptime, and after rebooting the node it would be fine for about a month. However, with a significant number of nodes, this was happening multiple times per day. Each instance would need rebooting, which means going through our worker reboot procedure which looked like this:</p><ol><li><p>Cordon off the affected node to prevent new workloads from scheduling on it.</p></li><li><p>Collect any diagnostic information for later investigation.</p></li><li><p>Drain the node of current workloads.</p></li><li><p>Reboot and wait for the node to come back.</p></li><li><p>Verify the node is healthy.</p></li><li><p>Re-enable scheduling of new workloads to the node.</p></li></ol><p>While solving the underlying issue would be ideal, we needed a mitigation to avoid toil in the meantime — an automated node remediation process.</p>
    <div>
      <h2>Existing Detection and Remediation Solutions</h2>
      <a href="#existing-detection-and-remediation-solutions">
        
      </a>
    </div>
    <p>While not complicated, the manual remediation process outlined previously became tedious and distracting, as we had to reboot nodes multiple times a day. Some manual intervention is unavoidable, but for cases matching the following, we wanted automation:</p><ul><li><p>Generic worker nodes</p></li><li><p>Software issues confined to a given node</p></li><li><p>Already researched and diagnosed issues</p></li></ul><p>Limiting automatic remediation to generic worker nodes is important as there are other node types in our clusters where more care is required. For example, for control-plane nodes the process has to be augmented to check <a href="https://etcd.io/">etcd</a> cluster health and ensure proper redundancy for components servicing the Kubernetes API. We are also going to limit the problem space to known software issues confined to a node where we expect automatic remediation to be the right answer (as in our ballooning network interface problem). With that in mind, we took a look at existing solutions that we could use.</p>
    <div>
      <h3>Node Problem Detector</h3>
      <a href="#node-problem-detector">
        
      </a>
    </div>
    <p><a href="https://github.com/kubernetes/node-problem-detector">Node problem detector</a> is a daemon that runs on each node that detects problems and reports them to the Kubernetes API. It has a pluggable problem daemon system such that one can add their own logic for detecting issues with a node. Node problems are distinguished between temporary and permanent problems, with the latter being persisted as status conditions on the Kubernetes node resources.<sup>2</sup></p>
    <div>
      <h3>Draino and Cluster-Autoscaler</h3>
      <a href="#draino-and-cluster-autoscaler">
        
      </a>
    </div>
    <p><a href="https://github.com/planetlabs/draino">Draino</a> as its name implies, drains nodes but does so based on Kubernetes node conditions. It is meant to be used with <a href="https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler">cluster-autoscaler</a> which then can add or remove nodes via the cluster plugins to scale node groups.</p>
    <div>
      <h3>Kured</h3>
      <a href="#kured">
        
      </a>
    </div>
    <p><a href="https://github.com/weaveworks/kured">Kured</a> is a daemon that looks at the presence of a file on the node to initiate a drain, reboot and uncordon of the given node. It uses a locking mechanism via the Kubernetes API to ensure only a single node is acted upon at a time.</p>
    <div>
      <h3>Cluster-API</h3>
      <a href="#cluster-api">
        
      </a>
    </div>
    <p>The <a href="https://github.com/kubernetes/community/tree/master/sig-cluster-lifecycle">Kubernetes cluster-lifecycle SIG</a> has been working on the <a href="https://github.com/kubernetes-sigs/cluster-api">cluster-api</a> project to enable declaratively defining clusters to simplify provisioning, upgrading, and operating multiple Kubernetes clusters. It has a concept of machine resources which back Kubernetes node resources and furthermore has a concept of <a href="https://cluster-api.sigs.k8s.io/tasks/healthcheck.html">machine health checks</a>. Machine health checks use node conditions to determine unhealthy nodes and then the cluster-api provider is then delegated to replace that machine via create and delete operations.</p>
    <div>
      <h2>Proof of Concept</h2>
      <a href="#proof-of-concept">
        
      </a>
    </div>
    <p>Interestingly, with all the above except for Kured, there is a theme of pluggable components centered around Kubernetes node conditions. We wanted to see if we could build a proof of concept using the existing theme and solutions. For the existing solutions, draino with cluster-autoscaler didn’t make sense in a non-cloud environment like our bare-metal set up. The cluster-api health checks are interesting, however they require a more complete investment into the cluster-api project to really make sense. That left us with node-problem-detector and kured. Deploying node-problem-detector was simple, and we ended up testing a <a href="https://github.com/kubernetes/node-problem-detector/blob/master/docs/custom_plugin_monitor.md">custom-plugin-monitor</a> like the following:</p>
            <pre><code>apiVersion: v1
kind: ConfigMap
metadata:
  name: node-problem-detector-config
data:
  check_calico_interfaces.sh: |
    #!/bin/bash
    set -euo pipefail
    
    count=$(nsenter -n/proc/1/ns/net ip link | grep cali | wc -l)
    
    if (( $count &gt; 150 )); then
      echo "Too many calico interfaces ($count)"
      exit 1
    else
      exit 0
    fi
  cali-monitor.json: |
    {
      "plugin": "custom",
      "pluginConfig": {
        "invoke_interval": "30s",
        "timeout": "5s",
        "max_output_length": 80,
        "concurrency": 3,
        "enable_message_change_based_condition_update": false
      },
      "source": "calico-custom-plugin-monitor",
      "metricsReporting": false,
      "conditions": [
        {
          "type": "NPDCalicoUnhealthy",
          "reason": "CalicoInterfaceCountOkay",
          "message": "Normal amount of interfaces"
        }
      ],
      "rules": [
        {
          "type": "permanent",
          "condition": "NPDCalicoUnhealthy",
          "reason": "TooManyCalicoInterfaces",
          "path": "/bin/bash",
          "args": [
            "/config/check_calico_interfaces.sh"
          ],
          "timeout": "3s"
        }
      ]
    }</code></pre>
            <p>Testing showed that when the condition became true, a condition would be updated on the associated Kubernetes node like so:</p>
            <pre><code>kubectl get node -o json worker1a | jq '.status.conditions[] | select(.type | test("^NPD"))'
{
  "lastHeartbeatTime": "2020-03-20T17:05:17Z",
  "lastTransitionTime": "2020-03-20T17:05:16Z",
  "message": "Too many calico interfaces (154)",
  "reason": "TooManyCalicoInterfaces",
  "status": "True",
  "type": "NPDCalicoUnhealthy"
}</code></pre>
            <p>With that in place, the actual remediation needed to happen. Kured seemed to do most everything we needed, except that it was looking at a file instead of Kubernetes node conditions. We hacked together a patch to change that and tested it successfully end to end — we had a working proof of concept!</p>
    <div>
      <h2>Revisiting Problem Detection</h2>
      <a href="#revisiting-problem-detection">
        
      </a>
    </div>
    <p>While the above worked, we found that node-problem-detector was unwieldy because we were replicating our existing monitoring into shell scripts and node-problem-detector configuration. A <a href="https://www.infoq.com/news/2017/10/monitoring-cloudflare-prometheus/">2017 blog post</a> describes Cloudflare’s monitoring stack, although some things have changed since then. What hasn’t changed is our extensive usage of <a href="https://prometheus.io/">Prometheus</a> and <a href="https://github.com/prometheus/alertmanager">Alertmanager</a>.</p><p>For the network interface issue and other issues we wanted to address, we already had the necessary exported metrics and alerting to go with them. Here is what our already existing alert looked like<sup>3</sup>:</p>
            <pre><code>- alert: CalicoTooManyInterfaces
  expr: sum(node_network_info{device=~"cali.*"}) by (node) &gt;= 200
  for: 1h
  labels:
    priority: "5"
    notify: chat-sre-core chat-k8s</code></pre>
            <p>Note that we use a “notify” label to drive the routing logic in Alertmanager. However, that got us asking, could we just route this to a Kubernetes node condition instead?</p>
    <div>
      <h2>Introducing Sciuro</h2>
      <a href="#introducing-sciuro">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Hx2xX6FXjjTsCdZZFmn7O/1e8be40972f81d59b9be18a1e60c68c4/image1-6.png" />
            
            </figure><p>Sciuro is our open-source replacement of node-problem-detector that has one simple job: synchronize Kubernetes node conditions with currently firing alerts in Alertmanager. Node problems can be defined with a more holistic view and using already existing exporters such as <a href="https://github.com/prometheus/node_exporter">node exporter</a>, <a href="https://github.com/google/cadvisor">cadvisor</a> or <a href="https://github.com/google/mtail">mtail</a>. It also doesn’t run on affected nodes which allows us to rely on out-of-band remediation techniques. Here is a high level diagram of how Sciuro works:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2TF20JXIsXxWApKN9yyEcy/fe4d2244e860fe8e0c1477e06ae24c30/image4-3.png" />
            
            </figure><p>Starting from the top, nodes are scraped by Prometheus, which collects those metrics and fires relevant alerts to Alertmanager. Sciuro polls Alertmanager for alerts with a matching receiver, matches them with a corresponding node resource in the Kubernetes API and updates that node’s conditions accordingly.</p><p>In more detail, we can start by defining an alert in Prometheus like the following:</p>
            <pre><code>- alert: CalicoTooManyInterfacesEarly
  expr: sum(node_network_info{device=~"cali.*"}) by (node) &gt;= 150
  labels:
    priority: "6"
    notify: node-condition-k8s</code></pre>
            <p>Note the two differences from the previous alert. First, we use a new name with a more sensitive trigger. The idea is that we want automatic node remediation to try fixing the node first as soon as possible, but if the problem worsens or automatic remediation is failing, humans will still get notified to act. The second difference is that instead of notifying chat rooms, we route to a target called “node-condition-k8s”.</p><p>Sciuro then comes into play, polling the Altertmanager API for alerts matching the “node-condition-k8s” receiver. The following shows the equivalent using <a href="https://github.com/prometheus/alertmanager/tree/master/cmd/amtool">amtool</a>:</p>
            <pre><code>$ amtool alert query -r node-condition-k8s
Alertname                 	Starts At            	Summary                                                               	 
CalicoTooManyInterfacesEarly  2021-05-11 03:25:21 UTC  Kubernetes node worker1a has too many Calico interfaces  </code></pre>
            <p>We can also check the labels for this alert:</p>
            <pre><code>$ amtool alert query -r node-condition-k8s -o json | jq '.[] | .labels'
{
  "alertname": "CalicoTooManyInterfacesEarly",
  "cluster": "a.k8s",
  "instance": "worker1a",
  "node": "worker1a",
  "notify": "node-condition-k8s",
  "priority": "6",
  "prometheus": "k8s-a"
}</code></pre>
            <p>Note the node and instance labels which Sciuro will use for matching with the corresponding Kubernetes node. Sciuro uses the excellent <a href="https://github.com/kubernetes-sigs/controller-runtime">controller-runtime</a> to keep track of and update node sources in the Kubernetes API. We can observe the updated node condition on the status field via kubectl:</p>
            <pre><code>$ kubectl get node worker1a -o json | jq '.status.conditions[] | select(.type | test("^AlertManager"))'
{
  "lastHeartbeatTime": "2021-05-11T03:31:20Z",
  "lastTransitionTime": "2021-05-11T03:26:53Z",
  "message": "[P6] Kubernetes node worker1a has too many Calico interfaces",
  "reason": "AlertIsFiring",
  "status": "True",
  "type": "AlertManager_CalicoTooManyInterfacesEarly"
}</code></pre>
            <p>One important note is Sciuro added the AlertManager_ prefix to the node condition type to prevent conflicts with other node condition types. For example, DiskPressure, a kubelet managed condition, could also be an alert name. Sciuro will also properly update heartbeat and transition times to reflect when it first saw the alert and its last update. With node conditions synchronized by Sciuro, remediation can take place via one of the existing tools. As mentioned previously we are using a modified version of Kured for now.</p><p>We’re happy to announce that we’ve open sourced Sciuro, and it can be found on <a href="https://github.com/cloudflare/sciuro">GitHub</a> where you can read the code, find the deployment instructions, or open a Pull Request for changes.</p>
    <div>
      <h2>Managing Node Uptime</h2>
      <a href="#managing-node-uptime">
        
      </a>
    </div>
    <p>While we began using automatic node remediation for obvious problems, we’ve expanded its purpose to additionally keep node uptime low. Low node uptime is desirable to further reduce drift on nodes, keep the node initialization process well-oiled, and encourage the best deployment practices on the Kubernetes clusters. To expand on the last point, services that are deployed with best practices and in a high availability fashion should see negligible impact when a single node leaves the cluster. However, services that are not deployed with best practices will most likely have problems especially if they rely on singleton pods. By draining nodes more frequently, it introduces regular chaos that encourages best practices. To enable this with automatic node remediation the following alert was defined:</p>
            <pre><code>- alert: WorkerUptimeTooHigh
  expr: |
    (
      (
        (
              max by(node) (kube_node_role{role="worker"})
            - on(node) group_left()
              (max by(node) (kube_node_role{role!="worker"}))
          or on(node)
            max by(node) (kube_node_role{role="worker"})
        ) == 1
      )
    * on(node) group_left()
      (
        (time() - node_boot_time_seconds) &gt; (60 * 60 * 24 * 7)
      )
    )
  labels:
    priority: "9"
    notify: node-condition-k8s</code></pre>
            <p>There is a bit of juggling with the kube_node_roles metric in the above to isolate the alert to generic worker nodes, but at a high level it looks at node_boot_time_seconds, a metric from <a href="https://github.com/prometheus/node_exporter">prometheus node_exporter</a>. Again the notify label is configured to send to node conditions which kicks off the automatic node remediation. One further detail is the priority here is set to “9” which is of lower precedence than our other alerts. Note that the message field of the node condition is prefixed with the alert priority in brackets. This allows the remediation process to take priority into account when choosing which node to remediate first, which is important because Kured uses a lock to act on a single node at a time.</p>
    <div>
      <h2>Wrapping Up</h2>
      <a href="#wrapping-up">
        
      </a>
    </div>
    <p>In the past 30 days, we’ve used the above automatic node remediation process to action 571 nodes. That has saved our humans a considerable amount of time. We’ve also been able to reduce the time to repair for some issues as automatic remediation can act at all times of the day and with a faster response time.</p><p>As mentioned before, we’re open sourcing Sciuro and its code can be found on <a href="https://github.com/cloudflare/sciuro">GitHub</a>. We’re open to issues, suggestions, and pull requests. We do have some ideas for future improvements. For Sciuro, we may look to reduce latency which is mainly due to polling and potentially add a push model from Altermanager although this isn’t a need we’ve had yet.  For the larger node remediation story, we hope to do an overhaul of the remediating component. As mentioned previously, we are currently using a fork of kured, but a future replacement component should include the following:</p><ul><li><p>Use out-of-band management interfaces to be able to shut down and power on nodes without a functional operating system.</p></li><li><p>Move from decentralized architecture to a centralized one that can integrate more complicated logic. This might include being able to act on entire failure domains in parallel.</p></li><li><p>Handle specialized nodes such as masters or storage nodes.</p></li></ul><p>Finally, we’re looking for more people passionate about Kubernetes to <a href="https://boards.greenhouse.io/cloudflare/jobs/816059?gh_jid=816059">join our team</a>. Come help us push Kubernetes to the next level to serve Cloudflare’s many needs!</p><hr /><p><sup>1</sup>Exhaustion can be applied to hardware resources, kernel resources, or logical resources like the amount of logging being produced.</p><p><sup>2</sup>Nearly all Kubernetes objects have <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/">spec and status fields</a>. The status field is used to describe the current state of an object. For node resources, typically the kubelet manages a conditions field under the status field for reporting things like if the node is ready for servicing pods.
<sup>3</sup>The format of the following alert is documented on <a href="https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/">Prometheus Alerting Rules</a>.</p> ]]></content:encoded>
            <category><![CDATA[Kubernetes]]></category>
            <category><![CDATA[Reliability]]></category>
            <category><![CDATA[Monitoring]]></category>
            <guid isPermaLink="false">76X316XLhCYnEUO7ZQR8CO</guid>
            <dc:creator>Andrew DeMaria</dc:creator>
        </item>
        <item>
            <title><![CDATA[Improving your monitoring setup by integrating Cloudflare’s analytics data into Prometheus and Grafana]]></title>
            <link>https://blog.cloudflare.com/improving-your-monitoring-setup-by-integrating-cloudflares-analytics-data-into-prometheus-and-grafana/</link>
            <pubDate>Thu, 20 May 2021 13:00:15 GMT</pubDate>
            <description><![CDATA[ Here at Labyrinth Labs, we put great emphasis on monitoring. Having a working monitoring setup is a critical part of the work we do for our clients.
Improving your monitoring setup by integrating Cloudflare’s analytics data into Prometheus and Grafana ]]></description>
            <content:encoded><![CDATA[ <p><i>The following is a guest post by Martin Hauskrecht, DevOps Engineer at Labyrinth Labs.</i></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1No9XvnguRN5bpcRgbSZBe/5e776b72c64573b51921171da88be6c1/image1-7.png" />
            
            </figure><p>Here at Labyrinth Labs, we put great emphasis on monitoring. Having a working monitoring setup is a critical part of the work we do for our clients.</p><p>Cloudflare's Analytics dashboard provides a lot of useful information for debugging and analytics purposes for our customer Pixel Federation. However, it doesn’t automatically integrate with existing monitoring tools such as Grafana and Prometheus, which our DevOps engineers use every day to monitor our infrastructure.</p><p>Cloudflare provides a Logs API, but the amount of logs we’d need to analyze is so vast, it would be simply inefficient and too pricey to do so. Luckily, Cloudflare already does the hard work of aggregating our thousands of events per second and exposes them in an <a href="https://developers.cloudflare.com/analytics/graphql-api/">easy-to-use API</a>.</p><p>Having Cloudflare’s data from our zones integrated with other systems’ metrics would give us a better understanding of our systems and the ability to correlate metrics and create more useful alerts, making our Day-2 operations (e.g. debugging incidents or analyzing the usage of our systems) more efficient.</p><p>Since our monitoring stack is primarily based on Prometheus and Grafana, we decided to implement our own Prometheus exporter that pulls data from Cloudflare’s GraphQL Analytics API.</p>
    <div>
      <h3>Design</h3>
      <a href="#design">
        
      </a>
    </div>
    <p>Based on current cloud trends and our intention to use the exporter in Kubernetes, writing the code in Go was the obvious choice. Cloudflare provides an <a href="https://github.com/cloudflare/cloudflare-go">API SDK for Golang</a>, so the common API tasks were made easy to start with.</p><p>We take advantage of Cloudflare’s GraphQL API to obtain analytics data about each of our zones and transform them into Prometheus metrics that are then exposed on a metrics endpoint.</p><p>We are able to obtain data about the total number and rate of requests, bandwidth, cache utilization, threats, SSL usage, and HTTP response codes. In addition, we are also able to monitor what type of content is being transmitted and what countries and locations the requests originate from.</p><p>All of this information is provided through the <i>http1mGroups</i> node in Cloudflare’s GraphQL API. If you want to see what Datasets are available, you can find a brief description at <a href="https://developers.cloudflare.com/analytics/graphql-api/features/data-sets">https://developers.cloudflare.com/analytics/graphql-api/features/data-sets</a>.</p><p>On top of all of these, we can also obtain data for Cloudflare’s data centers. Our graphs can easily show the distribution of traffic among them, further helping in our evaluations. The data is obtained from the <code><i>httpRequestsAdaptiveGroups</i></code> node in GraphQL.</p><p>After running the queries against the GraphQL API, we simply format the results to follow the Prometheus metrics format and expose them on the /metrics endpoint. To make things faster, we use Goroutines and make the requests in parallel.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1d6eIT9Tv7F4p5iLP4bYQR/1fc81995d3304539456ace57170af022/image4-9.png" />
            
            </figure>
    <div>
      <h3>Deployment</h3>
      <a href="#deployment">
        
      </a>
    </div>
    <p>Our primary intention was to use the exporter in Kubernetes. Therefore, it comes with a <a href="https://hub.docker.com/repository/docker/lablabs/cloudflare_exporter">Docker image</a> and <a href="https://github.com/lablabs/cloudflare-exporter/tree/master/charts/cloudflare-exporter">Helm chart</a> to make deployments easier. You might need to adjust the Service annotations to match your Prometheus configuration.</p><p>The exporter itself exposes the gathered metrics on the /metrics endpoint. Therefore setting the Prometheus annotations either on the pod or a Kubernetes service will do the job.</p>
            <pre><code>apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/path: /metrics
    prometheus.io/scrape: "true"</code></pre>
            <p>We plan on adding a Prometheus ServiceMonitor to the Helm chart to make scraping the exporter even easier for those who use the Prometheus operator in Kubernetes.</p><p>The configuration is quite easy, you just provide your API email and key. Optionally you can limit the scraping to selected zones only. Refer to our docs in the <a href="https://github.com/lablabs/cloudflare-exporter">GitHub repo</a> or see the example below.</p>
            <pre><code> env:
   - name: CF_API_EMAIL
     value: &lt;YOUR_API_EMAIL&gt;
   - name: CF_API_KEY
     value: &lt;YOUR_API_KEY&gt;

  # Optionally, you can filter zones by adding IDs following the example below.
  # - name: ZONE_XYZ
  #   value: &lt;zone_id&gt;</code></pre>
            <p>To deploy the exporter with Helm you simply need to run:</p>
            <pre><code>helm repo add lablabs-cloudflare-exporter https://lablabs.github.io/cloudflare-exporter
helm repo update

helm install cloudflare-exporter lablabs-cloudflare-exporter/cloudflare-exporter \
--set env[0].CF_API_EMAIL=&lt;API_EMAIL&gt; \
--set env[1].CF_API_KEY=&lt;API_KEY&gt;</code></pre>
            <p>We also provide a <a href="https://github.com/lablabs/cloudflare-exporter/blob/master/examples/helmfile/cloudflare-exporter.yaml">Helmfile</a> in our repo to make deployments easier, you just need to add your credentials to make it work.</p>
    <div>
      <h3>Visualizing the data</h3>
      <a href="#visualizing-the-data">
        
      </a>
    </div>
    <p>I’ve already explained how the exporter works and how you can get it running. As I mentioned before, we use Grafana to visualize our metrics from Prometheus. We’ve created a <a href="https://grafana.com/grafana/dashboards/13133">dashboard</a> that takes the data from Prometheus and puts it into use.</p><p>The dashboard is divided into several rows, which group individual panels for easier navigation. It allows you to target individual zones for metrics visualization.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4zGurfsIFldJAl7JWn1Ugm/f7e1173d0d3ccadb91de95d49902b6fc/image2-5.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Wui3JqzgAU1GAU5J7XXj3/b5fa5aab63702997b97d7be3cc5ed7f0/image3-6.png" />
            
            </figure><p>To make things even more beneficial for the operations team, you can use the gathered metrics to create alerts. These can be created either in Grafana directly or using Prometheus alert rules.</p><p>Furthermore, if you integrate <a href="https://github.com/thanos-io/thanos">Thanos</a> or <a href="https://grafana.com/oss/cortex/">Cortex</a> into your monitoring setup, you can store these metrics indefinitely.</p>
    <div>
      <h3>Future work</h3>
      <a href="#future-work">
        
      </a>
    </div>
    <p>We’d like to integrate even more analytics data into our exporters, eventually reaching every metric that Cloudflare’s GraphQL can provide. We plan on creating new metrics for firewall analytics, DoS analytics, and Network analytics soon.</p><p>Feel free to create a GitHub issue if you have any questions, problems, or suggestions. Any pull request is greatly appreciated.</p>
    <div>
      <h3>About us</h3>
      <a href="#about-us">
        
      </a>
    </div>
    <p><a href="https://lablabs.io/">Labyrinth Labs</a> helps companies build, run, deploy and scale software and infrastructure by embracing the right technologies and principles.</p> ]]></content:encoded>
            <category><![CDATA[Analytics]]></category>
            <category><![CDATA[Customers]]></category>
            <category><![CDATA[Prometheus]]></category>
            <category><![CDATA[Grafana]]></category>
            <category><![CDATA[Monitoring]]></category>
            <guid isPermaLink="false">4IQoisV5GHmCHWVLWy6LNK</guid>
            <dc:creator>Martin Hauskrecht</dc:creator>
        </item>
        <item>
            <title><![CDATA[Helping sites get back online: the origin monitoring intern project]]></title>
            <link>https://blog.cloudflare.com/helping-sites-get-back-online-the-origin-monitoring-intern-project/</link>
            <pubDate>Mon, 13 Apr 2020 11:00:00 GMT</pubDate>
            <description><![CDATA[ Over the course of ten weeks, our team of three interns (two engineering, one product management) went from a problem statement to a new feature, which is still working in production for all Cloudflare customers. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>The most impactful internship experiences involve building something meaningful from scratch and learning along the way. Those can be tough goals to accomplish during a short summer internship, but our experience with Cloudflare’s 2019 intern program met both of them and more! Over the course of ten weeks, our team of three interns (two engineering, one product management) went from a problem statement to a new feature, which is still working in production for all Cloudflare customers.</p>
    <div>
      <h2>The project</h2>
      <a href="#the-project">
        
      </a>
    </div>
    <p>Cloudflare sits between customers’ origin servers and end users. This means that all traffic to the origin server runs through Cloudflare, so we know when something goes wrong with a server and sometimes reflect that status back to users. For example, if an origin is refusing connections and there’s no cached version of the site available, Cloudflare will display a <a href="https://support.cloudflare.com/hc/en-us/articles/115003011431-Troubleshooting-Cloudflare-5XX-errors#521error">521 error</a>. If customers don’t have monitoring systems configured to detect and notify them when failures like this occur, their websites may go down silently, and they may hear about the issue for the first time from angry users.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2kbCMvc5jyJkluEFbAIT3b/0134bb038cbb7958001da92f67435a66/image4-11.png" />
            
            </figure><p>When a customer’s origin server is unreachable, Cloudflare sends a 5xx error back to the visitor.‌‌</p><p>This problem became the starting point for our summer internship project: since Cloudflare knows when customers' origins are down, let’s send them a notification when it happens so they can take action to get their sites back online and reduce the impact to their users! This work became Cloudflare’s <a href="https://support.cloudflare.com/hc/en-us/articles/360037465932-Preventing-site-downtime#5Y1o5Sk9v44PGWnjnfvgh">passive origin monitoring</a> feature, which is currently available on all Cloudflare plans.</p><p>Over the course of our internship, we ran into lots of interesting technical and product problems, like:</p>
    <div>
      <h3>Making big data small</h3>
      <a href="#making-big-data-small">
        
      </a>
    </div>
    <p>Working with data from all requests going through Cloudflare’s 26 million+ Internet properties to look for unreachable origins is unrealistic from a data volume and performance perspective. Figuring out what datasets were available to analyze for the errors we were looking for, and how to adapt our whiteboarded algorithm ideas to use this data, was a challenge in itself.</p>
    <div>
      <h3>Ensuring high alert quality</h3>
      <a href="#ensuring-high-alert-quality">
        
      </a>
    </div>
    <p>Because only a fraction of requests show up in the sampled timing and error dataset we chose to use, false positives/negatives were disproportionately likely to occur for low-traffic sites. These are the sites that are least likely to have sophisticated monitoring systems in place (and therefore are most in need of this feature!). In order to make the notifications as accurate and actionable as possible, we analyzed patterns of failed requests throughout different types of Cloudflare Internet properties. We used this data to determine thresholds that would maximize the number of true positive notifications, while making sure they weren’t so sensitive that we end up spamming customers with emails about sporadic failures.</p>
    <div>
      <h3>Designing actionable notifications</h3>
      <a href="#designing-actionable-notifications">
        
      </a>
    </div>
    <p>Cloudflare has lots of different kinds of customers, from people running personal blogs with interest in DDoS mitigation to large enterprise companies with extremely sophisticated monitoring systems and global teams dedicated to incident response. We wanted to make sure that our notifications were understandable and actionable for people with varying technical backgrounds, so we enabled the feature for small samples of customers and tested many variations of the “origin monitoring email”. Customers responded right back to our notification emails, sent in support questions, and posted on our community forums. These were all great sources of feedback that helped us improve the message’s clarity and actionability.</p><p>We frontloaded our internship with lots of research (both digging into request data to understand patterns in origin unreachability problems and talking to customers/poring over support tickets about origin unreachability) and then spent the next few weeks iterating. We enabled passive origin monitoring for all customers with some time remaining before the end of our internships, so we could spend time improving the supportability of our product, documenting our design decisions, and working with the team that would be taking ownership of the project.</p><p>We were also able to develop some smaller internal capabilities that built on the work we’d done for the customer-facing feature, like notifications on origin outage events for larger sites to help our account teams provide proactive support to customers. It was super rewarding to see our work in production, helping Cloudflare users get their sites back online faster after receiving origin monitoring notifications.</p>
    <div>
      <h2>Our internship experience</h2>
      <a href="#our-internship-experience">
        
      </a>
    </div>
    <p>The Cloudflare internship program was a whirlwind ten weeks, with each day presenting new challenges and learnings! Some factors that led to our productive and memorable summer included:</p>
    <div>
      <h3>A well-scoped project</h3>
      <a href="#a-well-scoped-project">
        
      </a>
    </div>
    <p>It can be tough to find a project that’s meaningful enough to make an impact but still doable within the short time period available for summer internships. We’re grateful to our managers and mentors for identifying an interesting problem that was the perfect size for us to work on, and for keeping us on the rails if the technical or product scope started to creep beyond what would be realistic for the time we had left.</p>
    <div>
      <h3>Working as a team of interns</h3>
      <a href="#working-as-a-team-of-interns">
        
      </a>
    </div>
    <p>The immediate team working on the origin monitoring project consisted of three interns: Annika in product management and Ilya and Zhengyao in engineering. Having a dedicated team with similar goals and perspectives on the project helped us stay focused and work together naturally.</p>
    <div>
      <h3>Quick, agile cycles</h3>
      <a href="#quick-agile-cycles">
        
      </a>
    </div>
    <p>Since our project faced strict time constraints and our team was distributed across two offices (Champaign and San Francisco), it was critical for us to communicate frequently and work in short, iterative sprints. Daily standups, weekly planning meetings, and frequent feedback from customers and internal stakeholders helped us stay on track.</p>
    <div>
      <h3>Great mentorship &amp; lots of freedom</h3>
      <a href="#great-mentorship-lots-of-freedom">
        
      </a>
    </div>
    <p>Our managers challenged us, but also gave us room to explore our ideas and develop our own work process. Their trust encouraged us to set ambitious goals for ourselves and enabled us to accomplish way more than we may have under strict process requirements.</p>
    <div>
      <h2>After the internship</h2>
      <a href="#after-the-internship">
        
      </a>
    </div>
    <p>In the last week of our internships, the engineering interns, who were based in the Champaign, IL office, visited the San Francisco office to meet with the team that would be taking over the project when we left and present our work to the company at our all hands meeting. The most exciting aspect of the visit: our presentation was preempted by Cloudflare’s co-founders announcing public S-1 filing at the all hands! :)</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7Fo3hKDoRLIKjjEmB6HHSf/30dc74250dd5588e9fb20d528c9187db/image5-5.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7q6XuSukWKdp5CmOEieU9/3762c15a4fb0438a54f14aefbf76c34c/image1-11.png" />
            
            </figure><p>Over the next few months, Cloudflare added a notifications page for easy configurability and <a href="/new-tools-to-monitor-your-server-and-avoid-downtime/">announced</a> the availability of passive origin monitoring along with some other tools to help customers monitor their servers and avoid downtime.</p><p>Ilya is working for Cloudflare part-time during the school semester and heading back for another internship this summer, and Annika is joining the team full-time after graduation this May. We’re excited to keep working on tools that help make the Internet a better place!</p><p>Also, Cloudflare is <a href="/cloudflare-doubling-size-of-2020-summer-intern-class/">doubling the size of the 2020 intern class</a>—if you or someone you know is interested in an internship like this one, check out the <a href="https://boards.greenhouse.io/cloudflare/jobs/2156436?gh_jid=2156436&amp;gh_src=d193c1b71us">open positions</a> in software engineering, security engineering, and product management.</p> ]]></content:encoded>
            <category><![CDATA[Internship Experience]]></category>
            <category><![CDATA[Monitoring]]></category>
            <category><![CDATA[Life at Cloudflare]]></category>
            <guid isPermaLink="false">4LahDyjkddXNdKsN2e2q9r</guid>
            <dc:creator>Annika Garbers</dc:creator>
            <dc:creator>Ilya Andreev</dc:creator>
            <dc:creator>Zhengyao Lin</dc:creator>
        </item>
        <item>
            <title><![CDATA[CloudFlare’s Newest App Partner: Verelo]]></title>
            <link>https://blog.cloudflare.com/cloudflares-newest-app-partner-verelo/</link>
            <pubDate>Tue, 31 Jul 2012 13:58:00 GMT</pubDate>
            <description><![CDATA[ A group of CloudFlare customers have recently been testing Velero during a silent launch, testing our latest app integration. Deployment has been very successful and with over 100 CloudFlare customers using Verelo. ]]></description>
            <content:encoded><![CDATA[ <p><i>EDIT: Verelo was a previous app partner, but is not currently participating in the Cloudflare Apps program. The link to the app has been removed from the end of the post.</i></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/71EsY6hbQJyDgMji64lUxW/2c271d2485dc15a3540915a64eaf43ad/verelo-200.png.scaled500.png" />
            
            </figure><p><i>Verelo is a website monitoring service that provides sub-minute checks, downtime alerts by SMS, phone or email, and malware detection.</i></p><p>A group of CloudFlare customers have recently been testing Velero during a silent launch, testing our latest app integration. Deployment has been very successful and with over 100 CloudFlare customers using Verelo, we have decided it's time to publicly announce them as our newest app partner.</p><p>Verelo is a unique company. Started by two individuals (Mike and Andrew) who previously worked in the hosting space, Verelo provides webmasters constant monitoring of their sites. Because of their background, Mike and Andrew were able to identify a need that hosting providers weren't able to provide for their customers and so they launched Verelo as an answer to the site and server monitoring need.</p>
    <div>
      <h3>Features</h3>
      <a href="#features">
        
      </a>
    </div>
    <p>Velero offers prevention, detection, responses and reporting for malicious activity on any website, making running a site that much easier.</p><p>Features include:</p><ul><li><p>Downtime alerts by SMS, Phone or Email</p></li><li><p>Malware detection</p></li><li><p>Response time and uptime graphs</p></li><li><p>Sub-minute monitoring</p></li><li><p>911 conference call system</p></li></ul>
    <div>
      <h3>Plans</h3>
      <a href="#plans">
        
      </a>
    </div>
    <p>Starting at free, Velero offers a variety of plans to suit your needs:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5ATXZeGMWJyp4m9QJLdWz7/5dfeec57d87f5fd59fbfea5024c813b4/plans.png.scaled500.png" />
            
            </figure>
    <div>
      <h3>Get started!</h3>
      <a href="#get-started">
        
      </a>
    </div>
    <p>Verelo is now available to all CloudFlare customers. For more information on the app and how to get started, visit the app detail page today: [REMOVED]</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Apps]]></category>
            <category><![CDATA[Monitoring]]></category>
            <guid isPermaLink="false">58REsEmfFzQeZ1PycD8ldq</guid>
            <dc:creator>Kristin Tarr</dc:creator>
        </item>
        <item>
            <title><![CDATA[App: Panopta Provides Advanced Server Monitoring and Outage Management Services]]></title>
            <link>https://blog.cloudflare.com/app-panopta-provides-advanced-server-monitori/</link>
            <pubDate>Wed, 25 Apr 2012 22:58:00 GMT</pubDate>
            <description><![CDATA[ CloudFlare customers have websites of all sizes, so we're pleased to introduced a new advanced server monitoring and outage management service from Panopta as a CloudFlare App.  ]]></description>
            <content:encoded><![CDATA[ <p></p><p>CloudFlare customers have websites of all sizes, so we're pleased to introduced a new advanced server monitoring and outage management service from <a href="https://www.cloudflare.com/apps/panopta">Panopta</a> as a CloudFlare App. Appealing to both enterprises and SMBs, Panopta will be the first to tell you if your infrastructure is down and provide you with tools to fix it.</p><p>What makes Panopta different? They offer three simple and unique areas that separate them from the rest of the market.</p><ul><li><p>Deep and Wide Monitoring</p></li><li><p>No False Alerts</p></li><li><p>Intelligent Alerting</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/30hPk9AvARNuzoirE84QUa/fc0adf7daae45ece22a6ada8ef7ce41d/panopta.png.scaled500.png" />
            
            </figure><p>Deep and Wide Monitoring</p><hr /><p>Panopta gives you in-depth checks every 60 seconds from their global monitoring network, the Panopta Monitoring Agent and the Panopta Monitoring Appliance.</p><p>No False Alerts</p><hr /><p>Panopta guarantees no false alerts. No more chasing problems that aren't there.</p><p>Intelligent Alerting</p><hr /><p>Alerts are escalated to the right people at the right time, so you can rest assured that problems will be fixed as soon as possible.</p>
    <div>
      <h4>Getting Started</h4>
      <a href="#getting-started">
        
      </a>
    </div>
    <p>Panopta offers four different plans, including:</p><ul><li><p>$15/month for Solo</p></li><li><p>$50/month for Basic</p></li><li><p>$100/month for Intermediate</p></li><li><p>$250/month for Advanced</p></li></ul><p>See all the details and sign up for Panopta's <a href="https://www.cloudflare.com/apps/panopta">advanced server monitoring service</a> now, via CloudFlare Apps.</p><p>Immediately, the first monitor will be set up for the home page of your site, with opportunity for detailed customization and additional monitors within the Panopta control panel. Try it now!</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Apps]]></category>
            <category><![CDATA[Monitoring]]></category>
            <category><![CDATA[Analytics]]></category>
            <guid isPermaLink="false">6kVjaTWxw0oIGUqhvied75</guid>
            <dc:creator>Kristin Tarr</dc:creator>
        </item>
        <item>
            <title><![CDATA[App #16 - CodeGuard Website Backup Service]]></title>
            <link>https://blog.cloudflare.com/app-16-codeguard-website-backup-service/</link>
            <pubDate>Wed, 29 Jun 2011 03:13:00 GMT</pubDate>
            <description><![CDATA[ CodeGuard takes a compelling idea -- automatic website backup -- and makes it happen without further thought. That peace of mind keeps site owners sleeping easily, and it's just the most compelling of the service's offerings.  ]]></description>
            <content:encoded><![CDATA[ <p></p>
    <div>
      <h3>A Time Machine for Your Website</h3>
      <a href="#a-time-machine-for-your-website">
        
      </a>
    </div>
    <p><a href="https://www.cloudflare.com/apps/codeguard">CodeGuard</a> takes a compelling idea -- automatic website backup -- and makes it happen without further thought. That peace of mind keeps site owners sleeping easily, and it's just the most compelling of the service's offerings. We're thrilled to add this <a href="https://www.cloudflare.com/apps">CloudFlare App</a> to cover another angle of smooth site operation.</p><p>CodeGuard takes backups only when the code on your site changes, and gives you the chance to scroll through old copies of site code. In conventional "Backup Mode," CodeGuard can capture your existing live site on hourly, daily, or monthly basis, storing each sweep as a different version. They call this "<b>Undo Power</b>" to helping you recover from lost data or untoward changes.</p>
    <div>
      <h3>Special Offering for CloudFlare Customers</h3>
      <a href="#special-offering-for-cloudflare-customers">
        
      </a>
    </div>
    <p>CodeGuard created a special <b>free plan for CloudFlare customer: Basic</b><b>Plus</b>. Without spending a cent, you may back up a single site up to 1GB in size. For additional sites, or for larger sites, this valuable service costs only $10/month. Thank you, CodeGuard!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/GxUswRim90Tqr061Mmy4e/9143ced6cedc550f7ad328ec09365762/codeguard-image.png.scaled500.png" />
            
            </figure>
    <div>
      <h3>How to Start Using CodeGuard</h3>
      <a href="#how-to-start-using-codeguard">
        
      </a>
    </div>
    <p>In your CloudFlare dashboard, <a href="https://www.cloudflare.com/apps/codeguard">CodeGuard</a> requires just a single click to create an account. For your first site, that's it: follow the instructions in your welcome email, and have your FTP instructions handy.</p><p>Go get your <a href="https://www.cloudflare.com/apps/codeguard">free website backup</a> started now, and relax.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Apps]]></category>
            <category><![CDATA[Monitoring]]></category>
            <guid isPermaLink="false">2MQHKhUhX4TedG2tJfe8cc</guid>
            <dc:creator>John Roberts</dc:creator>
        </item>
        <item>
            <title><![CDATA[App #15 - GlobalSign HackAlert Malware Detection]]></title>
            <link>https://blog.cloudflare.com/app-15-globalsign-hackalert-malware-detection/</link>
            <pubDate>Tue, 28 Jun 2011 03:38:00 GMT</pubDate>
            <description><![CDATA[ Your website security comes in many layers, against many threats. But how do you know if your site has a problem? Once you know, how do you solve the problem, to protect your customers and your reputation?  ]]></description>
            <content:encoded><![CDATA[ <p></p>
    <div>
      <h3>Cloud-based detection</h3>
      <a href="#cloud-based-detection">
        
      </a>
    </div>
    <p>Your website security comes in many layers, against many threats. But how do you know if your site has a problem? Once you know, how do you solve the problem, to protect your customers and your reputation? Today's <a href="https://www.cloudflare.com/apps">CloudFlare App</a> answers those questions.</p><p><a href="https://www.cloudflare.com/apps/hackalert">GlobalSign HackAlert</a> is a website malware detection and monitoring solution that immediately emails you if your website is infected. The service scans your site, checking the content for signs of compromise. If found, you get instant notification with steps to resolution. This detection -- and details about how to fix the problem -- helps you avoid scary browser or search engine warnings to your site's visitors, beyond the possibility of infecting your customers with a drive-by download.</p>
    <div>
      <h3>Yes, That's All You Have To Do</h3>
      <a href="#yes-thats-all-you-have-to-do">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5exfvc6nnwjR2tEvMr6qiC/a9b97ba7788c3b28f6e853c16fdb26ed/cloud-based.gif.scaled500.gif" />
            
            </figure><p>One elegant benefit of GlobalSign HackAlert is zero configuration required. <a href="https://www.cloudflare.com/apps/hackalert">Sign up</a> for the service via CloudFlare Apps, and the monitoring kicks in. It's automatic, with no extra dashboard required. Silently scanning your site, GlobalSign HackAlert doesn't trouble you unless there's a problem.Then, the service immediately emails you with notification of the discovered malware, including clear snippets of the infected code to make identification and removal a simple matter.</p><p>With relevance and simplicity, GlobalSign HackAlert makes sure your protection isn't noisy, just effective. Try the <a href="https://www.cloudflare.com/apps/hackalert">HackAlert Basic Plan</a> today, covering up to 100 pages of your site for just $5/month/site, and rest assured.</p> ]]></content:encoded>
            <category><![CDATA[Malware]]></category>
            <category><![CDATA[Cloudflare Apps]]></category>
            <category><![CDATA[Monitoring]]></category>
            <guid isPermaLink="false">7CEmQpavmkFi9p8jsVrox0</guid>
            <dc:creator>John Roberts</dc:creator>
        </item>
        <item>
            <title><![CDATA[App a Day #9 - Monitis for Server Monitoring]]></title>
            <link>https://blog.cloudflare.com/app-a-day-9-monitis-for-server-monitoring/</link>
            <pubDate>Mon, 13 Jun 2011 04:10:00 GMT</pubDate>
            <description><![CDATA[ Our first new CloudFlare App this week is Monitis, a server monitoring solution. Monitis allows you to monitor websites, of course. But Monitis's cloud-based, all-in-one monitoring service recognizes that the internet is far more than the web alone. ]]></description>
            <content:encoded><![CDATA[ <p></p>
    <div>
      <h3>Server Monitoring</h3>
      <a href="#server-monitoring">
        
      </a>
    </div>
    <p>Our first new <a href="https://www.cloudflare.com/apps">CloudFlare App</a> this week is <a href="https://www.cloudflare.com/apps/monitis">Monitis</a>, a server monitoring solution. Monitis allows you to monitor websites, of course. But Monitis's cloud-based, all-in-one monitoring service recognizes that the internet is far more than the web alone.</p><p>With Monitis, you can use monitor server CPU, applications, databases, server CPU, memory, processes, end-user experience, uptime, page load, transactions, and lots more. All of this is available via an intuitive dashboard, but these monitors may also be extended via API and command line tools.</p>
    <div>
      <h3>Special Offering for CloudFlare Customers</h3>
      <a href="#special-offering-for-cloudflare-customers">
        
      </a>
    </div>
    <p>Monitis is offering CloudFlare customers their Basic Plan for $8.99/month, which include 8 external monitors at a 20-minute frequency. That's a <b>10% discount</b> for CloudFlare customers. Thank you, Monitis!</p>
    <div>
      <h3>How to Start Using Monitis</h3>
      <a href="#how-to-start-using-monitis">
        
      </a>
    </div>
    <p>Like all CloudFlare Apps, <a href="https://www.cloudflare.com/apps/monitis">Monitis</a> is simple to set up. Go to your CloudFlare Apps dashboard, and click the green Setup button. You'll be asked to confirm the subscription, entering billing information if your CloudFlare account doesn't already have it, and then your Monitis account will be automatically created, with a link to the Monitis dashboard.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Apps]]></category>
            <category><![CDATA[Monitoring]]></category>
            <guid isPermaLink="false">4NKrnEMjm4Zos1zQnYK0sS</guid>
            <dc:creator>John Roberts</dc:creator>
        </item>
        <item>
            <title><![CDATA[App a Day #5 - Pingdom free account]]></title>
            <link>https://blog.cloudflare.com/app-a-day-5-pingdom-free-trial/</link>
            <pubDate>Tue, 07 Jun 2011 00:36:00 GMT</pubDate>
            <description><![CDATA[ You use CloudFlare because you want high performance, reliability, and security. Website owners deserve nothing less. Because we believe in transparency, we provide insight into our system status. Still, it's important to have third-party verification, so we encourage the use of outside monitors. ]]></description>
            <content:encoded><![CDATA[ <p><i>EDIT: Pingdom was a previous app partner, but is not currently participating in the Cloudflare Apps program. The link to the app has been removed from the end of the post.</i></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7DyzWakjGWewirH6TEzBth/e5c42391d4290acbd33254345f444604/pingdom-200.png.scaled500.png" />
            
            </figure>
    <div>
      <h3>Be The First To Know If Your Website Goes Down</h3>
      <a href="#be-the-first-to-know-if-your-website-goes-down">
        
      </a>
    </div>
    <p>You use CloudFlare because you want high performance, reliability, and security. Website owners deserve nothing less. Because we believe in transparency, we provide insight into our <a href="http://www.cloudflare.com/system-status.html">system status</a>. Still, it's important to have third-party verification, so we encourage the use of outside monitors.</p><p>Today's App can help. Pingdom is a leader in uptime monitoring. From your CloudFlare dashboard, you can create a Pingdom account with free monitoring for a single domain. For those with multiple domains or more complex requirements, the upgrade is easy.</p>
    <div>
      <h3>Pingdom Features</h3>
      <a href="#pingdom-features">
        
      </a>
    </div>
    <p>There's more detail available but the core benefits of Pingdom include:</p><ul><li><p>Alerts on downtime</p></li><li><p>Performance and uptime stats</p></li><li><p>Helps you troubleshoot downtime</p></li></ul>
    <div>
      <h3>How to Create Your Free Pingdom Account</h3>
      <a href="#how-to-create-your-free-pingdom-account">
        
      </a>
    </div>
    <p>Getting started with Pingdom is simple. Visit the Pingdom [link removed] page in CloudFlare Apps, continue to the CloudFlare Apps page for your preferred domain, and toggle the switch ON... your free account will be created instantly, with a link to configure your settings.</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Apps]]></category>
            <category><![CDATA[Monitoring]]></category>
            <guid isPermaLink="false">6QeptnqmS6l5k4AGV5Eh2p</guid>
            <dc:creator>John Roberts</dc:creator>
        </item>
    </channel>
</rss>