d99744c3df
The XML root element of Docbook XML files should match the following format: <ELEMENT xmlns="http://docbook.org/ns/docbook" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="THE_XML_ID_OF_THE_ELEMENT"> Change-Id: I95a6f36d6887af952257a800e3359d53ac1f895a
304 lines
16 KiB
XML
304 lines
16 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<section xmlns="http://docbook.org/ns/docbook"
|
||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||
version="5.0"
|
||
xml:id="ch_introduction-to-openstack-object-storage-monitoring">
|
||
<!-- ... Based on a blog, should be replaced with original material... -->
|
||
<title>Object Storage monitoring</title>
|
||
<?dbhtml stop-chunking?>
|
||
<para>Excerpted from a blog post by <link
|
||
xlink:href="http://swiftstack.com/blog/2012/04/11/swift-monitoring-with-statsd"
|
||
>Darrell Bishop</link></para>
|
||
<para>An OpenStack Object Storage cluster is a collection of many
|
||
daemons that work together across many nodes. With so many
|
||
different components, you must be able to tell what is going
|
||
on inside the cluster. Tracking server-level metrics like CPU
|
||
utilization, load, memory consumption, disk usage and
|
||
utilization, and so on is necessary, but not
|
||
sufficient.</para>
|
||
<para>What are different daemons are doing on each server? What is
|
||
the volume of object replication on node8? How long is it
|
||
taking? Are there errors? If so, when did they happen?</para>
|
||
<para>In such a complex ecosystem, you can use multiple approaches
|
||
to get the answers to these questions. This section describes
|
||
several approaches.</para>
|
||
<section xml:id="monitoring-swiftrecon">
|
||
<title>Swift Recon</title>
|
||
<para>The Swift Recon middleware (see <link
|
||
xlink:href="http://swift.openstack.org/admin_guide.html#cluster-telemetry-and-monitoring"
|
||
>http://swift.openstack.org/admin_guide.html#cluster-telemetry-and-monitoring</link>)
|
||
provides general machine statistics, such as load average,
|
||
socket statistics, <code>/proc/meminfo</code> contents,
|
||
and so on, as well as Swift-specific metrics:</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>The MD5 sum of each ring file.</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>The most recent object replication time.</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>Count of each type of quarantined file: Account,
|
||
container, or object.</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>Count of “async_pendings” (deferred container
|
||
updates) on disk.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<para>Swift Recon is middleware that is installed in the
|
||
object servers pipeline and takes one required option: A
|
||
local cache directory. To track
|
||
<literal>async_pendings</literal>, you must set up an
|
||
additional cron job for each object server. You access
|
||
data by either sending HTTP requests directly to the
|
||
object server or using the <command>swift-recon</command>
|
||
command-line client.</para>
|
||
<para>There are some good Object Storage cluster statistics
|
||
but the general server metrics overlap with existing
|
||
server monitoring systems. To get the Swift-specific
|
||
metrics into a monitoring system, they must be polled.
|
||
Swift Recon essentially acts as a middleware metrics
|
||
collector. The process that feeds metrics to your
|
||
statistics system, such as <literal>collectd</literal> and
|
||
<literal>gmond</literal>, probably already runs on the
|
||
storage node. So, you can choose to either talk to Swift
|
||
Recon or collect the metrics directly.</para>
|
||
</section>
|
||
<section xml:id="monitoring-swift-informant">
|
||
<title>Swift-Informant</title>
|
||
<para>Florian Hines developed the Swift-Informant middleware
|
||
(see <link
|
||
xlink:href="http://pandemicsyn.posterous.com/swift-informant-statsd-getting-realtime-telem"
|
||
>http://pandemicsyn.posterous.com/swift-informant-statsd-getting-realtime-telem</link>)
|
||
to get real-time visibility into Object Storage client
|
||
requests. It sits in the pipeline for the proxy server,
|
||
and after each request to the proxy server, sends three
|
||
metrics to a StatsD server (see <link
|
||
xlink:href="http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/"
|
||
>http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/</link>):</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>A counter increment for a metric like
|
||
<code>obj.GET.200</code> or
|
||
<code>cont.PUT.404</code>.</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>Timing data for a metric like
|
||
<code>acct.GET.200</code> or
|
||
<code>obj.GET.200</code>. [The README says the
|
||
metrics look like
|
||
<code>duration.acct.GET.200</code>, but I do
|
||
not see the <literal>duration</literal> in the
|
||
code. I am not sure what the Etsy server does but
|
||
our StatsD server turns timing metrics into five
|
||
derivative metrics with new segments appended, so
|
||
it probably works as coded. The first metric turns
|
||
into <code>acct.GET.200.lower</code>,
|
||
<code>acct.GET.200.upper</code>,
|
||
<code>acct.GET.200.mean</code>,
|
||
<code>acct.GET.200.upper_90</code>, and
|
||
<code>acct.GET.200.count</code>].</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>A counter increase by the bytes transferred for
|
||
a metric like
|
||
<code>tfer.obj.PUT.201</code>.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<para>This is good for getting a feel for the quality of
|
||
service clients are experiencing with the timing metrics,
|
||
as well as getting a feel for the volume of the various
|
||
permutations of request server type, command, and response
|
||
code. Swift-Informant also requires no change to core
|
||
Object Storage code because it is implemented as
|
||
middleware. However, it gives you no insight into the
|
||
workings of the cluster past the proxy server. If the
|
||
responsiveness of one storage node degrades, you can only
|
||
see that some of your requests are bad, either as high
|
||
latency or error status codes. You do not know exactly why
|
||
or where that request tried to go. Maybe the container
|
||
server in question was on a good node but the object
|
||
server was on a different, poorly-performing node.</para>
|
||
</section>
|
||
<section xml:id="monitoring-statsdlog">
|
||
<title>Statsdlog</title>
|
||
<para>Florian’s <link
|
||
xlink:href="https://github.com/pandemicsyn/statsdlog"
|
||
>Statsdlog</link> project increments StatsD counters
|
||
based on logged events. Like Swift-Informant, it is also
|
||
non-intrusive, but statsdlog can track events from all
|
||
Object Storage daemons, not just proxy-server. The daemon
|
||
listens to a UDP stream of syslog messages and StatsD
|
||
counters are incremented when a log line matches a regular
|
||
expression. Metric names are mapped to regex match
|
||
patterns in a JSON file, allowing flexible configuration
|
||
of what metrics are extracted from the log stream.</para>
|
||
<para>Currently, only the first matching regex triggers a
|
||
StatsD counter increment, and the counter is always
|
||
incremented by one. There is no way to increment a counter
|
||
by more than one or send timing data to StatsD based on
|
||
the log line content. The tool could be extended to handle
|
||
more metrics for each line and data extraction, including
|
||
timing data. But a coupling would still exist between the
|
||
log textual format and the log parsing regexes, which
|
||
would themselves be more complex to support multiple
|
||
matches for each line and data extraction. Also, log
|
||
processing introduces a delay between the triggering event
|
||
and sending the data to StatsD. It would be preferable to
|
||
increment error counters where they occur and send timing
|
||
data as soon as it is known to avoid coupling between a
|
||
log string and a parsing regex and prevent a time delay
|
||
between events and sending data to StatsD.</para>
|
||
<para>The next section describes another method for gathering
|
||
Object Storage operational metrics.</para>
|
||
</section>
|
||
<section xml:id="monitoring-statsD">
|
||
<title>Swift StatsD logging</title>
|
||
<para>StatsD (see <link
|
||
xlink:href="http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/"
|
||
>http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/</link>)
|
||
was designed for application code to be deeply
|
||
instrumented; metrics are sent in real-time by the code
|
||
that just noticed or did something. The overhead of
|
||
sending a metric is extremely low: a <code>sendto</code>
|
||
of one UDP packet. If that overhead is still too high, the
|
||
StatsD client library can send only a random portion of
|
||
samples and StatsD approximates the actual number when
|
||
flushing metrics upstream.</para>
|
||
<para>To avoid the problems inherent with middleware-based
|
||
monitoring and after-the-fact log processing, the sending
|
||
of StatsD metrics is integrated into Object Storage
|
||
itself. The submitted change set (see <link
|
||
xlink:href="https://review.openstack.org/#change,6058"
|
||
>https://review.openstack.org/#change,6058</link>)
|
||
currently reports 124 metrics across 15 Object Storage
|
||
daemons and the tempauth middleware. Details of the
|
||
metrics tracked are in the <link
|
||
xlink:href="http://docs.openstack.org/developer/swift/admin_guide.html"
|
||
>Administrator's Guide</link>.</para>
|
||
<para>The sending of metrics is integrated with the logging
|
||
framework. To enable, configure
|
||
<code>log_statsd_host</code> in the relevant config
|
||
file. You can also specify the port and a default sample
|
||
rate. The specified default sample rate is used unless a
|
||
specific call to a statsd logging method (see the list
|
||
below) overrides it. Currently, no logging calls override
|
||
the sample rate, but it is conceivable that some metrics
|
||
may require accuracy (sample_rate == 1) while others may
|
||
not.</para>
|
||
<literallayout class="monospaced">[DEFAULT]
|
||
...
|
||
log_statsd_host = 127.0.0.1
|
||
log_statsd_port = 8125
|
||
log_statsd_default_sample_rate = 1</literallayout>
|
||
<para>Then the LogAdapter object returned by
|
||
<code>get_logger()</code>, usually stored in
|
||
<code>self.logger</code>, has these new
|
||
methods:</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para><code>set_statsd_prefix(self, prefix)</code>
|
||
Sets the client library stat prefix value which
|
||
gets prefixed to every metric. The default prefix
|
||
is the “name” of the logger such as “object-server”,
|
||
“container-auditor”, and so on. This is currently used to
|
||
turn “proxy-server” into one of “proxy-server.Account”,
|
||
“proxy-server.Container”, or “proxy-server.Object”
|
||
as soon as the Controller object is determined and
|
||
instantiated for the request.</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><code>update_stats(self, metric, amount,
|
||
sample_rate=1)</code> Increments the supplied
|
||
metric by the given amount. This is used when you
|
||
need to add or subtract more that one from a
|
||
counter, like incrementing “suffix.hashes” by the
|
||
number of computed hashes in the object
|
||
replicator.</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><code>increment(self, metric,
|
||
sample_rate=1)</code> Increments the given
|
||
counter metric by one.</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><code>decrement(self, metric,
|
||
sample_rate=1)</code> Lowers the given counter
|
||
metric by one.</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><code>timing(self, metric, timing_ms,
|
||
sample_rate=1)</code> Record that the given
|
||
metric took the supplied number of
|
||
milliseconds.</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><code>timing_since(self, metric, orig_time,
|
||
sample_rate=1)</code> Convenience method to
|
||
record a timing metric whose value is “now” minus
|
||
an existing timestamp.</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<para>Note that these logging methods may safely be called
|
||
anywhere you have a logger object. If StatsD logging has
|
||
not been configured, the methods are no-ops. This avoids
|
||
messy conditional logic each place a metric is recorded.
|
||
These example usages show the new logging methods:</para>
|
||
<programlisting language="bash"># swift/obj/replicator.py
|
||
def update(self, job):
|
||
# ...
|
||
begin = time.time()
|
||
try:
|
||
hashed, local_hash = tpool.execute(tpooled_get_hashes, job['path'],
|
||
do_listdir=(self.replication_count % 10) == 0,
|
||
reclaim_age=self.reclaim_age)
|
||
# See tpooled_get_hashes "Hack".
|
||
if isinstance(hashed, BaseException):
|
||
raise hashed
|
||
self.suffix_hash += hashed
|
||
self.logger.update_stats('suffix.hashes', hashed)
|
||
# ...
|
||
finally:
|
||
self.partition_times.append(time.time() - begin)
|
||
self.logger.timing_since('partition.update.timing', begin)</programlisting>
|
||
<programlisting language="bash"># swift/container/updater.py
|
||
def process_container(self, dbfile):
|
||
# ...
|
||
start_time = time.time()
|
||
# ...
|
||
for event in events:
|
||
if 200 <= event.wait() < 300:
|
||
successes += 1
|
||
else:
|
||
failures += 1
|
||
if successes > failures:
|
||
self.logger.increment('successes')
|
||
# ...
|
||
else:
|
||
self.logger.increment('failures')
|
||
# ...
|
||
# Only track timing data for attempted updates:
|
||
self.logger.timing_since('timing', start_time)
|
||
else:
|
||
self.logger.increment('no_changes')
|
||
self.no_changes += 1</programlisting>
|
||
<para>The development team of StatsD wanted to use the <link
|
||
xlink:href="https://github.com/sivy/py-statsd"
|
||
>pystatsd</link> client library (not to be confused
|
||
with a <link
|
||
xlink:href="https://github.com/sivy/py-statsd"
|
||
>similar-looking project</link> also hosted on
|
||
GitHub), but the released version on PyPi was missing two
|
||
desired features the latest version in GitHub had: the
|
||
ability to configure a metrics prefix in the client object
|
||
and a convenience method for sending timing data between
|
||
“now” and a “start” timestamp you already have. So they
|
||
just implemented a simple StatsD client library from
|
||
scratch with the same interface. This has the nice fringe
|
||
benefit of not introducing another external library
|
||
dependency into Object Storage.</para>
|
||
</section>
|
||
</section>
|