<?xml version="1.0" encoding="UTF-8"?><rss 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" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Matthias Lee - Musings on Software and Performance Engineering]]></title><description><![CDATA[Matthias Lee is a Software Performance Engineer, Technical Lead and Computer Science PhD. Currently a Principal Performance Engineer at Appian.]]></description><link>https://matthiaslee.com/</link><image><url>https://matthiaslee.com/favicon.png</url><title>Matthias Lee - Musings on Software and Performance Engineering</title><link>https://matthiaslee.com/</link></image><generator>Ghost 2.14</generator><lastBuildDate>Mon, 17 Feb 2020 04:50:25 GMT</lastBuildDate><atom:link href="https://matthiaslee.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Duplicati: Self-hosted CrashPlan Alternative]]></title><description><![CDATA[<p>For the past few years I have been using CrashPlan to do incremental encrypted backups for all of my family's various computers. CrashPlan offered <em>free</em> peer-to-peer backups, which allowed me to just specify one of my ZFS disk boxes as the target, then <em>set it and forget it</em>. This has</p>]]></description><link>https://matthiaslee.com/self-hosted-crash-plan-alternative/</link><guid isPermaLink="false">5b4a06a626f9be0001ff6a5d</guid><category><![CDATA[duplicati]]></category><category><![CDATA[backup]]></category><category><![CDATA[linux]]></category><category><![CDATA[crashplan]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Thu, 26 Jul 2018 01:27:55 GMT</pubDate><content:encoded><![CDATA[<p>For the past few years I have been using CrashPlan to do incremental encrypted backups for all of my family's various computers. CrashPlan offered <em>free</em> peer-to-peer backups, which allowed me to just specify one of my ZFS disk boxes as the target, then <em>set it and forget it</em>. This has been working spectacularly well, especially considering the wide variety of operating systems I needed to &quot;support&quot;: Linux (Desktop/Server), Windows and Mac.</p>
<p><img src="https://matthiaslee.com/content/images/2018/07/crashplan.jpg" alt="CrashPlan Client/Server User Interface"></p>
<p>Unfortunately in August 2017, CrashPlan announced they will discontinue their consumer offering in order to refocus on small-businesses, which I'm sure is a much more profitable market. The announcement email has been sitting <em>starred</em> in my inbox since that day and I've been dreading to find the next solutions. I remembered, last time I looked around, CrashPlan was more or less the only self-hosted choice that worked across all OSs and offered incremental encrypted backups. More recently I came across the new <a href="https://www.duplicati.com/">Duplicati</a>, which has seen a major revamp since the last time I had looked at it, now supporting all sorts of storage backends, encryption, incremental backups and a variety of retention policies  as well as a slick new web-based UI.</p>
<p><img src="https://matthiaslee.com/content/images/2018/07/duplicati.jpg" alt="Duplicati Web Interface"></p>
<p>Below I will describe how to setup duplicati to use a central linux server as the backend to store all of your files.</p>
<h2 id="setupandinstallduplicati">Setup and Install Duplicati</h2>
<p>On the client machine, which will be backed up, go to <a href="https://www.duplicati.com/download">duplicati.com/download</a> and grab the latest installer for your operating system and follow the default installation. Below are the directions I used on my Ubuntu Laptop</p>
<pre><code>user@box:~$ wget https://updates.duplicati.com/beta/duplicati_2.0.3.3-1_all.deb
user@box:~$ sudo dpkg -i duplicati_2.0.3.3-1_all.deb
</code></pre>
<p>then start <code>duplicati</code> and continue in the web interface at <a href="http://localhost:8200/ngax/index.html#/">localhost:8200/ngax/index.html</a>.</p>
<h2 id="addingabackuplocation">Adding a Backup location</h2>
<p>I usually like to keep all of my data on my own servers so here we will configure backing up via SSH, making sure to setup ssh-keys and a separate backup user on my disk box.</p>
<ol>
<li>
<p><strong>Getting Started</strong> Select &quot;+ Add Backup&quot;, then &quot;Configure New Backup&quot;, then &quot;next&quot;<br>
<img src="https://matthiaslee.com/content/images/2018/07/duplicati_start_backup.png" alt="Duplicati Getting Started"></p>
</li>
<li>
<p><strong>Encryption</strong> A great feature of duplicati is the encryption of backups, to make good use of it, generate a password and store it in your <em>password manager</em>, I prefer KeePass and LastPass.<br>
<img src="https://matthiaslee.com/content/images/2018/07/homedir.png" alt="Setup Encryption"></p>
</li>
<li>
<p><strong>Destination</strong> Next we select where the backups will be stored, this could be an external drive attached to your computer or a remote machine via SSH, FTP, WebDav or even S3-like storage. For the purpose of this tutorial we will go with SSH.</p>
</li>
</ol>
<p><img src="https://matthiaslee.com/content/images/2018/07/backup-settings.png" alt="Set your Backup Destination"></p>
<ul>
<li><strong>Add SSH Key</strong> When using SSH, I recommend using an SSH key for security, especially if you use the default SSH port. Under Advanced Settings select <strong>ssh-keyfile</strong>, if your key has a password, enter this into the <strong>Password</strong> field above.</li>
</ul>
<p><img src="https://matthiaslee.com/content/images/2018/07/test-connection.png" alt="Specify SSH Key"></p>
<ul>
<li><strong>Test your connection</strong> Now hit <strong>Test Connection</strong> to make sure everything works properly. You'll be asked whether you accept the Host-key, select <strong>Yes</strong>, then go <strong>Next</strong></li>
</ul>
<p><img src="https://matthiaslee.com/content/images/2018/07/add-host-key.png" alt="Add SSH Host-key"></p>
<ol start="4">
<li><strong>Configure Source</strong> Here we select the files we wish yo backup. Generally on a linux machine I backup <code>~/</code> and exclude some of the <em>dot-files</em>.</li>
</ol>
<p><img src="https://matthiaslee.com/content/images/2018/07/source-files.png" alt="Selecting Files to be backed up"></p>
<ol start="5">
<li><strong>Backup Schedule</strong> Generally I don't need backup every day, so go with Weekly backup.</li>
</ol>
<p><img src="https://matthiaslee.com/content/images/2018/07/schedule-7day.png" alt="Duplicati Backup Schedule"></p>
<ol start="6">
<li><strong>General Settings</strong> These final settings are important.</li>
</ol>
<ul>
<li><strong>Volume Size</strong> controls the size of chunks that will be pushed to your backup system and will therefore control how many file will land on your backup server. I've found 50MB to work well for me.</li>
<li><strong>blocksize</strong> controls the size of the blocks which will be hashed, i set this to 1MB, cutting down on the size of the database duplicati has to maintain.</li>
<li><strong>Backup Retention</strong> Generally it is most useful to thin-out older backups in order to save space. I set this to <strong>Smart Backup Retention</strong>.</li>
</ul>
<p>Play with these settings to find what is most optimal for your setup, it will depend on your upload speed, the backend server as well as the compute capability of your client device.</p>
<p><img src="https://matthiaslee.com/content/images/2018/07/size-options.png" alt="Genral settings controling Volume Size, Blocksize and Retention."></p>
<p><strong>All Done!</strong><br>
Now, a word of wisdom, after you get this setup, make sure you can successfully go through a full backup-restore cycle to work any kinks you may encounter.</p>
<p><img src="https://matthiaslee.com/content/images/2018/07/action-shot.png" alt="Duplicati in Action!"></p>
]]></content:encoded></item><item><title><![CDATA[No network access after Ubuntu 14.04->16.04->18.04 upgrade]]></title><description><![CDATA[<p>Recently I was tending to my fleet of personal servers and I ran into a problem which essentially took my system offline and with it a number of websites I host.</p>
<p>After the upgrade my system did not automatically start eth0 with its assigned static IP address, only the loopback</p>]]></description><link>https://matthiaslee.com/no-network-access-after-ubuntu-14-04-16-04-18-04-upgrade/</link><guid isPermaLink="false">5b3e5ba8ede43600016b7830</guid><category><![CDATA[linux]]></category><category><![CDATA[networking]]></category><category><![CDATA[netplan]]></category><category><![CDATA[networkd]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Thu, 05 Jul 2018 19:29:42 GMT</pubDate><content:encoded><![CDATA[<p>Recently I was tending to my fleet of personal servers and I ran into a problem which essentially took my system offline and with it a number of websites I host.</p>
<p>After the upgrade my system did not automatically start eth0 with its assigned static IP address, only the loopback device was configured. Thankfully DigitalOcean provides easy console access. So i started some debugging:</p>
<pre><code>m@box:~$ ifconfig
lo: flags=73&lt;UP,LOOPBACK,RUNNING&gt;  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10&lt;host&gt;
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

m@box:~$ cat ifconfig-a.log 
eth0: flags=4098&lt;BROADCAST,MULTICAST&gt;  mtu 1500
        ether 04:01:01:8a:1b:01  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73&lt;UP,LOOPBACK,RUNNING&gt;  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10&lt;host&gt;
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
</code></pre>
<p>Turns out the root cause was no <code>ifup</code> or <code>ifdown</code>, which were both scripts required by <code>/etc/init.d/networking</code> to bring up networking</p>
<pre><code>m@box:~$ ifup eth0
-bash: ifup: command not found
m@box:~$ ifdown eth0
-bash: ifdown: command not found
</code></pre>
<p>Excerpt from <code>/etc/init.d/networking</code></p>
<pre><code>m@box:~$ grep -in &quot;ifup\|ifdown&quot; /etc/init.d/networking 
3:# Provides:          networking ifupdown
17:[ -x /sbin/ifup ] || exit 0
18:[ -x /sbin/ifdown ] || exit 0
101:ifup_hotplug () {
115:		ifup $ifaces &quot;$@&quot; || true
141:	if ifup -a $exclusions $verbose &amp;&amp; ifup_hotplug $exclusions $verbose
157:	if ifdown -a --exclude=lo $verbose; then
172:	ifdown -a --exclude=lo $verbose || true
173:	if ifup --exclude=lo $state $verbose ; then
188:	ifdown -a --exclude=lo $verbose || true
191:	if ifup -a --exclude=lo $exclusions $verbose &amp;&amp; ifup_hotplug $exclusions $verbose
</code></pre>
<h3 id="tempfixtogetbackontotheinternettoactuallyinstallnetworkdnetplan">Temp fix to get back onto the internet to actually install networkd/netplan:</h3>
<pre><code>m@box:~$ sudo ifconfig eth0 up
# xx.xx.xx.xx/24 is your static IP
m@box:~$ sudo ip addr add xx.xx.xx.xx/24 dev eth0
# xx.xx.xx.1 is your gateway
m@box:~$ sudo ip route add default via xx.xx.xx.1 dev eth0
</code></pre>
<h3 id="longtermfixinstallnetplanconfigureittomanagenetworkd">long-term fix, install netplan, configure it to manage networkd:</h3>
<pre><code>m@box:~$ sudo apt-get install netplan.io
</code></pre>
<p>We configure netplan(<code>/etc/netplan/01-netcfg.yaml</code>):</p>
<pre><code>network:
  version: 2
  renderer: networkd
  ethernets:
   eth0:
    dhcp4: no
    dhcp6: no
    addresses: [xx.xx.xx.xx/24]
    gateway4: xx.xx.xx.1
    nameservers:
     addresses: [8.8.8.8, 8.8.4.4]
</code></pre>
<p>next we apply the new changes and make sure it gives no errors:</p>
<pre><code>m@box:~$ sudo netplan --debug apply
** (generate:1883): DEBUG: 19:19:36.239: Processing input file //etc/netplan/01-netcfg.yaml..
** (generate:1883): DEBUG: 19:19:36.240: starting new processing pass
** (generate:1883): DEBUG: 19:19:36.240: eth0: setting default backend to 1
** (generate:1883): DEBUG: 19:19:36.244: Generating output files..
** (generate:1883): DEBUG: 19:19:36.244: NetworkManager: definition eth0 is not for us (backend 1)
DEBUG:netplan generated networkd configuration exists, restarting networkd
DEBUG:no netplan generated NM configuration exists
DEBUG:device eth0 operstate is up, not replugging
DEBUG:netplan triggering .link rules for eth0
DEBUG:device lo operstate is unknown, not replugging
DEBUG:netplan triggering .link rules for lo
</code></pre>
<p>now the last step is to reboot and make sure the new configuration persists.</p>
<p>And viola, after a reboot everything stayed in place:</p>
<pre><code>m@box:~$ ifconfig
eth0: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500
        inet xx.xx.xx.xx  netmask 255.255.255.0  broadcast xx.xx.xx.255
        inet6 xx::xx:xx:xx:xx  prefixlen 64  scopeid 0x20&lt;link&gt;
        ether 04:01:xx:xx:xx:xx  txqueuelen 1000  (Ethernet)
        RX packets 654  bytes 69420 (69.4 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 517  bytes 108478 (108.4 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73&lt;UP,LOOPBACK,RUNNING&gt;  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10&lt;host&gt;
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Thesis Defense: Data Fusion at Scale in Astronomy]]></title><description><![CDATA[We are facing a deluge of data streaming in from countless sources and across virtually all disciplines. Data intensive sciences such as astronomy expect to collect 100 PB in 10 years from a one survey. The challenge is keeping up with these data rates and extracting meaningful information.]]></description><link>https://matthiaslee.com/data-fusion-at-scale-in-astronomy/</link><guid isPermaLink="false">5b79995bb969240001d44e72</guid><category><![CDATA[Computational Optics]]></category><category><![CDATA[performance]]></category><category><![CDATA[Thesis Defense]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Wed, 16 Aug 2017 16:22:00 GMT</pubDate><media:content url="https://matthiaslee.com/content/images/2018/08/matthias-lee_thesis-defense.png" medium="image"/><content:encoded><![CDATA[<h2 id="timeandlocation">Time and Location</h2>
<img src="https://matthiaslee.com/content/images/2018/08/matthias-lee_thesis-defense.png" alt="Thesis Defense: Data Fusion at Scale in Astronomy"><p>August 29, 2017 @ 2:00 pm – 4:00 pm<br>
<a href="https://goo.gl/maps/8e5jpsKMRQk">Malone Hall 228</a></p>
<h2 id="abstract">Abstract</h2>
<p>We have arrived in an era where we face a deluge of data streaming in from countless sources and across virtually all disciplines; This holds especially true for data intensive sciences such as astronomy where upcoming surveys such as the LSST are expected to collect tens of terabytes per night, upwards of 100 Petabytes in 10 years. The challenge is keeping up with these data rates and extracting meaningful information from them. We present a number of methods for combining and distilling vast astronomy datasets using GPUs. In particular we focus on cross-matching catalogs containing close to 0.5 Billion sources, optimally combining multi-epoch imagery and computationally extracting color from monochrome telescope images.</p>
<p><strong><a href="https://www.cs.jhu.edu/events/student-matthias-a-lee-johns-hopkins-university-high-performance-cross-matching-and-computational-optics-for-telescopes/">JHU Announcement</a></strong></p>
<p><strong><a href="https://matthiaslee.com/pub/Matthias-Lee-2017-Thesis-defense.pdf">Final Slides</a></strong></p>
]]></content:encoded></item><item><title><![CDATA[Don't blindly trust your summary statistics.]]></title><description><![CDATA[<p><strong>Summary statistics</strong> are a common way to evaluate and compare performance data. They are simple, easy to compute and most people have an intuitive understanding of them, therefore mean, median, standard deviation and percentiles tend to be the default metrics used to report, monitor and compare performance.<br>
Many of the</p>]]></description><link>https://matthiaslee.com/dont-blindly-trust-your-summary-statistics/</link><guid isPermaLink="false">5a0a60d882e47c00018dabf2</guid><category><![CDATA[performance testing]]></category><category><![CDATA[tends]]></category><category><![CDATA[distribution]]></category><category><![CDATA[summary statistics]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Mon, 15 May 2017 01:26:53 GMT</pubDate><content:encoded><![CDATA[<p><strong>Summary statistics</strong> are a common way to evaluate and compare performance data. They are simple, easy to compute and most people have an intuitive understanding of them, therefore mean, median, standard deviation and percentiles tend to be the default metrics used to report, monitor and compare performance.<br>
Many of the common Load and Performance testing tools (<a href="https://httpd.apache.org/docs/2.4/programs/ab.html">ApacheBench</a>, <a href="https://github.com/httperf/httperf">Httperf</a> and <a href="http://locust.io">Locust.IO</a>) produce reports using these metrics to summarize their results. While easy to understand, they rely on the assumption that what you are measuring stays constant during your test and even more importantly that the set of samples follow a normal distribution, often this is <em>not</em> the case.<br>
In this post we will evaluate two tricky scenarios which I have seen come up in real world testing. First a simple example to show how two very different distributions can have the same summary statistics and second an example of how summary statistics and distributions can conceal underlying problems.</p>
<p><strong>First</strong>, let's start with a simple set of performance test results, featuring 1000 samples of a web service endpoint.</p>
<p><img src="https://matthiaslee.com/content/images/2017/05/bimodal_latencies-1.png" alt="figure 1: 1000 latency samples of a web service endpoint"></p>
<p>Our favorite summary statistics have been overlaid, showing us the mean, median and +/- standard deviation. At first glance there is nothing interesting about these results. We see some variability, perhaps due to network jitter or load on the system, but otherwise a pretty consistent result. <em>Can you identify any interesting features? Given these summary statistics, could you determine a change in behaviour?</em><br>
In the above example, the mean sits at 26.6, the median at 26.01 and we have a standard deviation of around 3. Given that the median is slightly lower than the mean suggests we may have a positively <a href="https://en.wikipedia.org/wiki/Skewness">skewed distribution</a>. Which is a common feature of latency distributions, since there are at least a few packets that always hit snags such as errors or taking the scenic network path.<br>
If we just look at the summary statistics, we do not get the full picture. <em>Figure 2</em> shows two distributions with an <em>identical</em> mean, median and standard deviation, but as you can see these are very different in shape.<br>
<em>Could you identify which distribution corresponds to the samples from figure 1?</em></p>
<p><img src="https://matthiaslee.com/content/images/2017/05/skewed_normal_dist-2.png#centered" alt="figure 2(a): Skewed Normal Distribution"><br>
<img src="https://matthiaslee.com/content/images/2017/05/bimodal_dist.png" alt="figure 2(b): Bimodal Normal Distribution"></p>
<p>Intuitively and most commonly with latencies, the distribution tends to look more like <em>figure 2(a)</em>, but in our case the actual distribution is as in <em>figure 2(b)</em>. Multi-modal distributions often indicate some sort of caching at work, the lower mode representing a cache hit and the higher mode a cache miss. Understanding changes in the relationship between cache hits and misses is very important, as a rise in cache misses could indicate a serious problem.<br>
Given only the medians, means and standard deviations, it would be impossible to determine any difference, therefore performance changes such as these would never surface. There is no easy solution here besides adding more advanced metrics. One such metric to consider is the <a href="https://en.wikipedia.org/wiki/Kolmogorov%E2%80%93Smirnov_test">Kolmogorov–Smirnov test</a>, which computes the difference between two <a href="https://en.wikipedia.org/wiki/Cumulative_distribution_function">Cumulative Density Functions</a>.</p>
<p><strong>Another</strong> gotcha are results, which when evaluated based on their summary statistics <em>and</em> their distribution (see <em>figure 3</em>), look to be completely normal, no bimodal tendencies, a slight right skew, but nothing that stands out.</p>
<p><img src="https://matthiaslee.com/content/images/2017/05/latency_dist2-2.png" alt="figure 3: Deceptive latency distribution"></p>
<p>These are the trickiest, the ones that don't ring any alarm bells, are the ones that will bite you once you go into production. The critical missing piece is the <a href="https://en.wikipedia.org/wiki/Time_domain">time-domain</a> information of the original results, which by definition cannot be captured by summary statistics or distributions.<br>
Collecting time-domain information usually is not a problem, but on high throughput tests, it may become prohibitively expensive, both memory- and storage-wise. Instead you may be tempted to purely rely on streaming statistics, perhaps using a snazzy sliding-window histogram to do <a href="https://en.wikipedia.org/wiki/Reservoir_sampling">reservoir sampling</a> or something like the <a href="https://github.com/tdunning/t-digest">t-digest</a>. These are fantastic approaches and I am absolutely in favor of using these, but if you do not keep at least some interval-based snapshots of the streaming statistics, you may end up discarding valuable information.<br>
Let's return to our example from <em>figure 3</em>, when viewed as a time-series, see <em>figure 4</em>, it is clear that we have significant trend!</p>
<p><img src="https://matthiaslee.com/content/images/2017/05/trending_latencies.png" alt="figure 4: Trending latencies are invisible when looking at the distribution"></p>
<p>Trends cannot be characterized using summary statistics and add extra complexity to performance comparisons, therefore should be avoided whenever possible.<br>
To ensure that that you trends do not secretly distort your statistics, compute a <a href="https://en.wikipedia.org/wiki/Robust_regression">robust linear regression</a> metric (I've had good luck with the <a href="https://en.wikipedia.org/wiki/Random_sample_consensus">RANSAC</a> algorithm) to quantify the trend in terms of slope and y-intercept. Given these metrics it becomes easy to develop a sanity check to determine whether any drastic trend changes have occurred.</p>
<p><strong>Summary statistics</strong> can be valuable first indicators about performance, but can easily lead to false conclusions if not combined with other metrics. It is especially important to retain time-domain information to be able to detect trends which might otherwise be hidden. Stay tuned for future posts which will deep dive on how to accurately detect the slightest performance changes.</p>
]]></content:encoded></item><item><title><![CDATA[Caching Ghost with Apache for Maximum Performance, 100x faster]]></title><description><![CDATA[<p>Ghost can be a bit CPU hungry, especially for a lightweight (single core) VPS, but all of that can me negated with a little bit of caching. Luckily Apache's <code>mod_disk_cache</code> makes easy work of this.</p>
<h2 id="configuringthecache">Configuring the cache:</h2>
<p>First we need to enable <code>mod_cache</code>, <code>mod_cache_disk</code></p>]]></description><link>https://matthiaslee.com/caching-ghost-with-apache-for-maximum-performance-100x-faster/</link><guid isPermaLink="false">5a0a60d882e47c00018dabf0</guid><category><![CDATA[apache]]></category><category><![CDATA[cache]]></category><category><![CDATA[ghost]]></category><category><![CDATA[performance testing]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Sun, 23 Apr 2017 05:35:44 GMT</pubDate><content:encoded><![CDATA[<p>Ghost can be a bit CPU hungry, especially for a lightweight (single core) VPS, but all of that can me negated with a little bit of caching. Luckily Apache's <code>mod_disk_cache</code> makes easy work of this.</p>
<h2 id="configuringthecache">Configuring the cache:</h2>
<p>First we need to enable <code>mod_cache</code>, <code>mod_cache_disk</code> and <code>mod_expires</code>:</p>
<pre><code>sudo a2enmod cache
sudo a2enmod cache_disk
sudo a2enmod expires
</code></pre>
<p>Then edit your virtual host file, usually <code>/etc/apache2/sites-enabled/default.conf</code> (may differ based on setup)</p>
<pre><code>&lt;VirtualHost *:80&gt;
     # Domain name and Alias
     ServerName example.com
     ServerAlias www.example.com

     # Configure Reverse proxy for Ghost
     ProxyPreserveHost on
     ProxyPass / http://localhost:1234/
     ProxyPassReverse / http://localhost:1234/

     CacheQuickHandler off
     CacheLock on
     CacheLockPath /tmp/mod_cache-lock
     CacheLockMaxAge 5
     CacheIgnoreHeaders Set-Cookie

     &lt;Location /&gt;
        # Enable disk cache, set defaults
        CacheEnable disk
        CacheHeader on
        CacheDefaultExpire 600
        CacheMaxExpire 86400
        FileETag All

        # Set cache-control headers for all request
        # which do not have them by default
        # must enable: mod_expires
        ExpiresActive on
        ExpiresDefault &quot;access plus 15 minutes&quot;
    &lt;/Location&gt;

    # While this is not needed since Ghost automatically
    # passes back 'Cache-Control: no-cache, private'
    # It makes me feel better to explicitly state it again.
    &lt;Location /ghost&gt;
        # Don't cache the ghost admin interface
        SetEnv no-cache
    &lt;/Location&gt;

&lt;/VirtualHost&gt;
</code></pre>
<p>Finally we need to restart apache and then we are done!</p>
<pre><code>sudo service apache2 restart
</code></pre>
<p>Now it's time to check whether your cache is working by inspecting the headers.</p>
<pre><code>:~$ curl -i -X GET http://example.com | less
HTTP/1.1 200 OK
Date: Sun, 21 Apr 2017 05:15:13 GMT
Server: Apache
Cache-Control: public, max-age=0, max-age=900
Expires: Sun, 21 Apr 2017 05:30:12 GMT
Age: 832
X-Cache: HIT from example.com
...
</code></pre>
<p>As long as you see an <code>X-Cache</code> and a <code>Cache-Control</code> header it is all working. Now lets see what kind of performance improvement we have achieved.</p>
<h1 id="performancetesting">Performance Testing:</h1>
<p>To quantify the improvement, I broke out <code>ApacheBench</code> and did a couple of quick tests from a neighboring machine.<br>
First test without caching enabled, yielding approximately <strong>25 Requests per second</strong> with a median response time of <strong>~4 seconds!</strong>:</p>
<pre><code>Concurrency Level:      100
Time taken for tests:   40.943 seconds
Complete requests:      1000
Requests per second:    24.42 [#/sec] (mean)
Time per request:       4094.286 [ms] (mean)
Time per request:       40.943 [ms] (mean, across all concurrent requests)
Transfer rate:          282.64 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   1.7      1      14
Processing:  1052 3936 670.4   3933    5252
Waiting:     1052 3936 670.4   3933    5252
Total:       1056 3938 669.7   3934    5252

Percentage of the requests served within a certain time (ms)
  50%   3934
  66%   4119
  75%   4261
  80%   4406
  90%   4554
  95%   5067
  98%   5173
  99%   5211
 100%   5252 (longest request)

</code></pre>
<p>After enabling the caching, we get <strong>~2700 Requests per second</strong> with a median response time of <strong>31 milliseconds!</strong>. That is 100x more requests served per second!</p>
<pre><code>Concurrency Level:      100
Time taken for tests:   18.487 seconds
Complete requests:      50000
Failed requests:        0
Total transferred:      597400000 bytes
HTML transferred:       578850000 bytes
Requests per second:    2704.57 [#/sec] (mean)
Time per request:       36.974 [ms] (mean)
Time per request:       0.370 [ms] (mean, across all concurrent requests)
Transfer rate:          31556.82 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    8  52.1      4    1019
Processing:     1   29  19.1     26     693
Waiting:        1   28  16.9     26     659
Total:          2   37  55.1     31    1158

Percentage of the requests served within a certain time (ms)
  50%     31
  66%     34
  75%     37
  80%     40
  90%     46
  95%     51
  98%     67
  99%     91
 100%   1158 (longest request)
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Performance Testing 101 - 5 min intro & example]]></title><description><![CDATA[Introduction to performance testing, using ApacheBench to load test a simple Flask server and optimizing it's performance.]]></description><link>https://matthiaslee.com/performance-testing-101-5-minute-intro/</link><guid isPermaLink="false">5a0a60d882e47c00018dabee</guid><category><![CDATA[performance testing]]></category><category><![CDATA[apache bench]]></category><category><![CDATA[Flask]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Fri, 21 Apr 2017 02:56:00 GMT</pubDate><content:encoded><![CDATA[<p>When developing and deploying web services, apps or sites the following questions come up: <em>&quot;How will it perform?&quot;, &quot;How many concurrent users will it support?&quot;, &quot;If I tweak this setting, will it be faster?&quot;, &quot;Do these new features effect performance?&quot;</em>. The list could go on and on and on. Performance questions are common, solid answers are not.</p>
<p>Performance testing can take many different shapes, from dead-simple one-liners to complex setups, tests, tear-downs and analysis. While this article focuses on quick, easy and straightforward testing, future articles will address more advanced topics.</p>
<p>There are some great easy tools to get first ball-park answers to performance questions getting at the number of concurrent users as well as how the response time changes as load increases. Here I'll give a short intro to <code>ApacheBench</code>.</p>
<p>Let us begin with setting up some basic terminology, first let's refer to our machine under test as the <code>host</code>, this can be any kind of http-accessible server you have. Second we will want an <code>agent</code> machine to drive our tests from.<br>
When performance testing, it is key to limit the number of possible variables which could distort our results. Ideally your <code>agent</code> is a separate dedicated machine and as close as possible (network distance wise) to you <code>host</code> system in order to minimize the amount of networking you test. This is especially relevant when you are testing applications hosted in a shared environment (ie cloud). The performance impact of <em>noisy neighbors</em> can be surprising, but that is a topic we will explore in detail in the future.</p>
<h1 id="apachebench">ApacheBench</h1>
<p><strong>ApacheBench</strong> is a command line tool (<code>ab</code>) which allows for simple load driving against HTTP hosts. It's great at producing large numbers of <code>REST</code> requests, capable of producing thousands of requests per second. Generally I find ApacheBench most useful for getting a rough idea of how many requests an application can handle. It's extremely simple to use and therefore a great tool while debugging configurations.</p>
<p>To install on Debian/Ubuntu:</p>
<pre><code>sudo apt-get install apache2-utils
</code></pre>
<p>To Install on RHEL/Centos/Fedora</p>
<pre><code>sudo yum install httpd-tools
</code></pre>
<h3 id="usage">Usage:</h3>
<pre><code>Usage: ab [options] [http[s]://]hostname[:port]/path
Options are:
    -n requests     Number of requests to perform
    -c concurrency  Number of multiple requests to make at a time
    -t timelimit    Seconds to max. to spend on benchmarking
                    This implies -n 50000
    -s timeout      Seconds to max. wait for each response
                    Default is 30 seconds
    -b windowsize   Size of TCP send/receive buffer, in bytes
    -B address      Address to bind to when making outgoing connections
    -p postfile     File containing data to POST. Remember also to set -T
    -u putfile      File containing data to PUT. Remember also to set -T
    -T content-type Content-type header to use for POST/PUT data, eg.
                    'application/x-www-form-urlencoded'
                    Default is 'text/plain'
    -v verbosity    How much troubleshooting info to print
    -w              Print out results in HTML tables
    -i              Use HEAD instead of GET
    -x attributes   String to insert as table attributes
    -y attributes   String to insert as tr attributes
    -z attributes   String to insert as td or th attributes
    -C attribute    Add cookie, eg. 'Apache=1234'. (repeatable)
    -H attribute    Add Arbitrary header line, eg. 'Accept-Encoding: gzip'
                    Inserted after all normal header lines. (repeatable)
    -A attribute    Add Basic WWW Authentication, the attributes
                    are a colon separated username and password.
    -P attribute    Add Basic Proxy Authentication, the attributes
                    are a colon separated username and password.
    -X proxy:port   Proxyserver and port number to use
    -V              Print version number and exit
    -k              Use HTTP KeepAlive feature
    -d              Do not show percentiles served table.
    -S              Do not show confidence estimators and warnings.
    -q              Do not show progress when doing more than 150 requests
    -l              Accept variable document length (use this for dynamic pages)
    -g filename     Output collected data to gnuplot format file.
    -e filename     Output CSV file with percentages served
    -r              Don't exit on socket receive errors.
    -m method       Method name
    -h              Display usage information (this message)
    -Z ciphersuite  Specify SSL/TLS cipher suite (See openssl ciphers)
    -f protocol     Specify SSL/TLS protocol
                    (TLS1, TLS1.1, TLS1.2 or ALL)
</code></pre>
<h3 id="exampleusage">Example usage:</h3>
<pre><code> ab -c 1 -n 1000 http://example.com/
</code></pre>
<h1 id="puttingapachebenchtouse">Putting ApacheBench to use:</h1>
<p>The above section should be plenty to get you started, but lets look at a quick example of testing caching. Below I've setup a simple <code>flask</code> server example, which on request calculated a random Fibonacci number between <code>1</code> and <code>30</code>.</p>
<pre><code>#!/usr/bin/env python
#
# To start this server, you must have python and flask installed
# Start server: python testserver-fib.py
#
# To install flask use the pip line below:
# pip install Flask
# or visit: http://flask.pocoo.org/docs/0.12/installation/

from flask import Flask
import random
app = Flask(__name__)

# snagged from: http://stackoverflow.com/a/499245
def F(n):
    if n == 0: return 0
    elif n == 1: return 1
    else: return F(n-1)+F(n-2)

@app.route('/')
def hello_world():
    r = random.randint(1,30)
    fib = F(r)
    # ApacheBench expects constant output
    return 'fib({0:02}):{0:06}'.format(r,fib)

if __name__ == &quot;__main__&quot;:
    app.run(debug=True)
</code></pre>
<p>Now let's see how we do performance wise. We set the concurrency to 1 using <code>-c 1</code> and specify the number of requests to 500 by setting <code>-n 500</code>. Note that we are using the simple flask dev-server, which is single threaded.</p>
<pre><code>m@test:~$ ab -c 1 -n 500 http://127.0.0.1:5000/
-- snip --
Concurrency Level:      1
Time taken for tests:   17.821 seconds
Complete requests:      500
Failed requests:        0
Total transferred:      85000 bytes
HTML transferred:       7000 bytes
Requests per second:    28.06 [#/sec] (mean)
Time per request:       35.642 [ms] (mean)
Time per request:       35.642 [ms] (mean, across all concurrent requests)
Transfer rate:          4.66 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:     1   35  88.2      1     471
Waiting:        0   35  88.2      1     471
Total:          1   36  88.2      1     471

Percentage of the requests served within a certain time (ms)
  50%      1
  66%      5
  75%     16
  80%     27
  90%    107
  95%    277
  98%    447
  99%    461
 100%    471 (longest request)
</code></pre>
<p>In the above example, we see the average request time was <code>34ms</code>, the median was <code>1ms</code> and we had <code>28rps</code> (requests per second). What happens if instead of a single connection we have 10 concurrent connections (setting <code>-c 10</code>)?</p>
<pre><code>m@test:~$ ab -c 1 -n 500 http://127.0.0.1:5000/
-- snip --
Concurrency Level:      10
Time taken for tests:   18.579 seconds
Complete requests:      500
Failed requests:        0
Total transferred:      85000 bytes
HTML transferred:       7000 bytes
Requests per second:    26.91 [#/sec] (mean)
Time per request:       371.583 [ms] (mean)
Time per request:       37.158 [ms] (mean, across all concurrent requests)
Transfer rate:          4.47 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:     3  360 294.4    294    1470
Waiting:        2  360 294.4    294    1470
Total:          3  360 294.4    294    1470

Percentage of the requests served within a certain time (ms)
  50%    294
  66%    443
  75%    529
  80%    579
  90%    746
  95%   1018
  98%   1136
  99%   1160
 100%   1470 (longest request)
</code></pre>
<p>While the RPS remains very similar to before at <code>~27rps</code>, our response times have gone through the roof (mean of <code>371ms</code> and median of <code>294ms</code>)! Here we have a situation, where multiple parallel connections get serialized and processed one at a time, while the overall rate remains unchanged, the quality of service delivered to each client degrades by a factor roughly similar to the number of concurrent connections.</p>
<p>Let's see if we can do better. Since we repeatedly calculate the same 30 Fibonacci numbers, let's add some caching into the mix. Generally, if you have long-running requests that will always return an unchanging value, it is a good idea to cache these. With the caching in place, the first few requests will still have the same <em>slow</em> response time, but all of the following requests will benefit from the cache and therefore be as fast as our cache lookup. See the modified code below:</p>
<pre><code>#!/usr/bin/env python
#
# * To start this server, you must have python and flask installed
# * Copy this into a file named testserver-fib-cached.py
# * Start server: python testserver-fib-cached.py
#
# * To install flask use the pip line below:
#      `pip install Flask`
#   or visit: http://flask.pocoo.org/docs/0.12/installation/
from flask import Flask
import random
app = Flask(__name__)

cache = {}

# snagged from: http://stackoverflow.com/a/499245
def F(n):
    if n == 0: return 0
    elif n == 1: return 1
    else: return F(n-1)+F(n-2)

@app.route('/')
def hello_world():
    r = random.randint(1,30)
    if r in cache:
        print('hit')
        # ApacheBench expects constant output
        return 'Cache Hit!  fib({0:02}):{0:06}'.format(r,cache[r])
    else:
        fib = F(r)
        cache[r] = fib
        print('miss')
        # ApacheBench expects constant output
        return 'Cache Miss! fib({0:02}):{0:06}'.format(r,fib)

if __name__ == &quot;__main__&quot;:
    app.run(debug=True)
</code></pre>
<p>Now let's run our single connection, 500 request benchmark again:</p>
<pre><code>-- snip --
Concurrency Level:      1
Time taken for tests:   1.680 seconds
Complete requests:      500
Failed requests:        0
Total transferred:      91000 bytes
HTML transferred:       13000 bytes
Requests per second:    297.55 [#/sec] (mean)
Time per request:       3.361 [ms] (mean)
Time per request:       3.361 [ms] (mean, across all concurrent requests)
Transfer rate:          52.89 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:     1    3  27.7      1     497
Waiting:        0    3  27.7      1     497
Total:          1    3  27.7      1     497

Percentage of the requests served within a certain time (ms)
  50%      1
  66%      1
  75%      1
  80%      1
  90%      1
  95%      1
  98%      7
  99%     85
 100%    497 (longest request)
</code></pre>
<p>The results are quite impressive: the mean is down to <code>3.3ms</code>, the median down to <code>1ms</code> and the request rate is at <code>297rps</code>! That is 10x faster. Once the cache is initialized and our benchmark no longer includes the cache seeding time, we get even higher performance, which at this point is likely limited only by the cache-lookups. My local testing gets me up to <code>1100rps</code> with median and mean both less than<code>1ms</code>. While this is a simple example for demonstration, it is important to note that part of what we are seeing is a misleading flaw in how most load driving tools generate requests and record latencies, this is known as the <strong>coordinated-omission problem</strong>, but that is a topic for another day.</p>
<p>This concludes our short introduction into performance testing, but soon to follow will be more articles addressing more complex setups, benchmarking methods and types, metrics to be evaluating and considerations for repeatability.</p>
]]></content:encoded></item><item><title><![CDATA[Migration finally done.]]></title><description><![CDATA[<p>As of late last week I have finally completed the migration away from the previous incarnation of my Drupal-based blog. Even since its initial deployment I found it to be overly complicated for me to get simple things accomplished. I would often think about writing a post but usually got</p>]]></description><link>https://matthiaslee.com/migration-finally-done/</link><guid isPermaLink="false">5a0a60d882e47c00018dabef</guid><category><![CDATA[ghost]]></category><category><![CDATA[drupal]]></category><category><![CDATA[personal]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Thu, 20 Apr 2017 03:41:00 GMT</pubDate><content:encoded><![CDATA[<p>As of late last week I have finally completed the migration away from the previous incarnation of my Drupal-based blog. Even since its initial deployment I found it to be overly complicated for me to get simple things accomplished. I would often think about writing a post but usually got hung up on actually properly formatting and writing the post. In the meantime I started messing around with Ghost, first as an quick and easy-to-use blog separate from my main website, then even ended up using it for our wedding website last year.</p>
<p>After sifting through many different themes I think I've arrived at one which simple, clean and un-obtrusive. I have completed the content migration and ultimately ended up merging my main website and my blog together into one easily manageable platform. Along the way I did some performance tuning using <code>ApacheBench</code> to see how Ghost does under load and ultimately enabled Apache's <code>mod_cache_disk</code> to prevent node.js from pegging the CPU on my humble VPS.</p>
<p>I look forward to having a clean and easy to use platform at my disposal to document some of my thoughts, tips and tricks in a way that it may be useful to others.</p>
]]></content:encoded></item><item><title><![CDATA[Debugging mysql crash on a low-memory VPS]]></title><description><![CDATA[<p>Recently I had a run-in with a seemingly random, occasional crash of mysql on a system with only 512MB of memory. My suspicion was that sometimes mysql runs some cleanup tasks or something along those lines, causing the memory usage to spike and ultimately cause a crash. So I wrote</p>]]></description><link>https://matthiaslee.com/debugging-mysql-crash-on-a-low-memory-vps/</link><guid isPermaLink="false">5a0a60d882e47c00018dabe0</guid><category><![CDATA[mysql]]></category><category><![CDATA[low memory]]></category><category><![CDATA[crash]]></category><category><![CDATA[debug]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Sat, 28 Mar 2015 02:16:00 GMT</pubDate><content:encoded><![CDATA[<p>Recently I had a run-in with a seemingly random, occasional crash of mysql on a system with only 512MB of memory. My suspicion was that sometimes mysql runs some cleanup tasks or something along those lines, causing the memory usage to spike and ultimately cause a crash. So I wrote this quick and dirty script to log 48 hours of memory:</p>
<pre><code>#!/usr/bin/env python
import os
import datetime
import psutil
 
# This script will dump the memory state and write it to mem.log
# if more than 2880 entries are in the log file, it will start removing 
# old ones in order to keep the log size down.
#
# To use this script first install psutil: sudo pip install psutil
# then run: crontab -e
# add this line to the bottom(adjust the path to where the script is): 
# * * * * * cd /home/madmaze/trash; /usr/bin/python /home/madmaze/trash/memoryLogger.py
#
# This will write add a new entry to mem.log every minute and keep 48hrs of records
 
def getUsage():
    m=psutil.virtual_memory()
    s=psutil.swap_memory()
    memkeys=[&quot;total&quot;, &quot;available&quot;, &quot;percent&quot;, &quot;used&quot;, &quot;free&quot;, &quot;active&quot;, &quot;inactive&quot;, &quot;buffers&quot;, &quot;cached&quot;]
    swapkeys=[&quot;total&quot;, &quot;used&quot;, &quot;free&quot;, &quot;perc&quot;,&quot;sin&quot;,&quot;sout&quot;]
    info={}
    # MEM
    for n,k in enumerate(m):
        if memkeys[n] not in [&quot;percent&quot;,&quot;active&quot;,&quot;inactive&quot;]:
            info[&quot;mem_&quot;+memkeys[n]]=str(k/1024/1024)+&quot; MB&quot;
 
    # SWAP
    for n,k in enumerate(s):
        if n &lt; 3:
            info[&quot;swap_&quot;+memkeys[n]]=str(k/1024/1024)+&quot; MB&quot;
    
    return info
 
 
logfile=&quot;mem.log&quot;
f_in=open(logfile,&quot;r&quot;)
lines=f_in.readlines()
f_in.close()
 
totalLen=len(lines)
maxlen=2*24*60	# 2days * 24hr * 60m
 
if totalLen &gt; maxlen+100:
    # Crop file to length
    f_out=open(logfile,&quot;w&quot;)
    for n,l in enumerate(lines):
        if (totalLen-n)&lt;maxlen:
            f_out.write(l)
else:
    # just append
    f_out=open(logfile,&quot;a&quot;)
 
# Actually append the next line of data
f_out.write(str(datetime.datetime.now())+&quot; &quot;+str(getUsage())+&quot;\n&quot;)
f_out.close()
</code></pre>
<p>Github Gist: <a href="https://gist.github.com/madmaze/560cbcf0392aab824820">https://gist.github.com/madmaze/560cbcf0392aab824820</a></p>
<p>Judging by the recorded memory usage pattern, it seems that at 3AM EST a series of database intensive cron jobs kicked off at the same time causing mysql's memory foot print to grow until it crashed. Long story short, I spaced the cron jobs out such that each has plenty of time to complete before the next one begins.</p>
]]></content:encoded></item><item><title><![CDATA[Dynamically changing conky network interface]]></title><description><![CDATA[<p>I use conky on many of my machines, most desktop machines will have one or two network interfaces which I want to always monitor, but my laptop I often switch between many different network interfaces, (eth0, wlan0, usb0 and tun0), and I really like Conky's downspeedgraph and upspeedgraph. The issue</p>]]></description><link>https://matthiaslee.com/dynamically-changing-conky-network-interface/</link><guid isPermaLink="false">5a0a60d882e47c00018dabe3</guid><category><![CDATA[conky]]></category><category><![CDATA[network interface]]></category><category><![CDATA[dynamic]]></category><category><![CDATA[linux]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Sun, 03 Aug 2014 17:28:42 GMT</pubDate><content:encoded><![CDATA[<p>I use conky on many of my machines, most desktop machines will have one or two network interfaces which I want to always monitor, but my laptop I often switch between many different network interfaces, (eth0, wlan0, usb0 and tun0), and I really like Conky's downspeedgraph and upspeedgraph. The issue is I dont want to be displaying all interfaces at all times. I only want to display the interfaces I am currently using.</p>
<p>I went to consult the Conky Doc (<a href="http://conky.sourceforge.net/variables.html">http://conky.sourceforge.net/variables.html</a>) at first I came across:</p>
<pre><code>${if_up &lt;iface&gt;} ${endif}
</code></pre>
<p>Which switches conky config blocks based on which interfaces are up.<br>
This is great, but still always displays eth0 and wlan0 as long as the interfaces are enabled, even if they are not connected to anything.<br>
Next I found:</p>
<pre><code>${if_existing /sys/class/net/&lt;iface&gt;/operstate up} ${endif}
</code></pre>
<p>This will automatically switch blocks of your config only when an interface's &quot;operstate&quot; is &quot;up&quot;. Surprisingly this is different from the <code>${if_up &lt;iface&gt;}</code> statement and at least for me this produces the expected behavior.</p>
<p>I ended up using a combination of the <code>${if_up}</code> and <code>${if_existing}</code> mainly because for temporary interfaces such as usb0 and tun0 the  <code>${if_up}</code> statement works fine and looks cleaner.</p>
<h4 id="hereismycurrentconfig">Here is my current config:</h4>
<pre><code>Network ${hr}
${if_existing /sys/class/net/eth0/operstate up}
eth0
Down ${downspeed eth0} k/s ${alignr}Up ${upspeed eth0} k/s
${downspeedgraph eth0 25,100 dddddd ffffff 150} ${alignr}${upspeedgraph eth0 25,100 dddddd ffffff 18}
Total ${totaldown eth0} ${alignr}Total ${totalup eth0}
${endif}${if_existing /sys/class/net/wlan0/operstate up}
wlan0
Down ${downspeed wlan0} k/s ${alignr}Up ${upspeed wlan0} k/s
${downspeedgraph wlan0 25,100 dddddd ffffff 150} ${alignr}${upspeedgraph wlan0 25,100 dddddd ffffff 18}
Total ${totaldown wlan0} ${alignr}Total ${totalup wlan0}
${endif}${if_up usb0}
usb0
Down ${downspeed usb0} k/s ${alignr}Up ${upspeed usb0} k/s
${downspeedgraph usb0 25,100 dddddd ffffff 150} ${alignr}${upspeedgraph usb0 25,100 dddddd ffffff 18}
Total ${totaldown usb0} ${alignr}Total ${totalup usb0}
${endif}${if_up tun0}
tun0
Down ${downspeed tun0} k/s ${alignr}Up ${upspeed tun0} k/s
${downspeedgraph tun0 25,100 dddddd ffffff 150} ${alignr}${upspeedgraph tun0 25,100 dddddd ffffff 18}
Total ${totaldown tun0} ${alignr}Total ${totalup tun0}
${endif}
</code></pre>
<p>enjoy.</p>
]]></content:encoded></item><item><title><![CDATA[Backing up your VPS]]></title><description><![CDATA[<p><em>NOTE: Work in progress.. currently just lecture notes</em></p>
<p><strong>Outline:</strong></p>
<ul>
<li>Setting up a backup server (perhaps)</li>
<li>Advanced authentication with SSH keys</li>
<li>Task scheduling with Cron</li>
<li>Various backup methods (scp, rsync and rdiff-backup)</li>
</ul>
<hr>
<ul>
<li>
<p><strong>Setting up a backup server (perhaps)</strong></p>
<ul>
<li>Ubuntu LTS (12.04/14.04)
<ul>
<li>preferably not VPS, especially not with who</li></ul></li></ul></li></ul>]]></description><link>https://matthiaslee.com/backing-up-your-vps/</link><guid isPermaLink="false">5a0a60d882e47c00018dabe1</guid><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Mon, 07 Apr 2014 18:00:41 GMT</pubDate><content:encoded><![CDATA[<p><em>NOTE: Work in progress.. currently just lecture notes</em></p>
<p><strong>Outline:</strong></p>
<ul>
<li>Setting up a backup server (perhaps)</li>
<li>Advanced authentication with SSH keys</li>
<li>Task scheduling with Cron</li>
<li>Various backup methods (scp, rsync and rdiff-backup)</li>
</ul>
<hr>
<ul>
<li>
<p><strong>Setting up a backup server (perhaps)</strong></p>
<ul>
<li>Ubuntu LTS (12.04/14.04)
<ul>
<li>preferably not VPS, especially not with who you host your other stuff.</li>
<li>doesn't need to be powerful, just have a spinning disk or two.</li>
</ul>
</li>
<li>Perhaps set up RAID? mdadm is great
<ul>
<li>What is RAID?</li>
</ul>
</li>
<li>could be headless</li>
</ul>
</li>
<li>
<p><strong>Advanced authentication with SSH keys</strong></p>
<ul>
<li>What are SSH keys?
<ul>
<li>bit size, Algorithm RSA/DSM</li>
<li>Password vs Password-less keys</li>
</ul>
</li>
<li>Setting up Keys:
<ul>
<li>{lost? there is a great reference in these <a href="https://help.github.com/articles/generating-ssh-keys">github docs</a>}</li>
<li>Basics, generate a key: <code>$&gt; ssh-keygen</code>
<ul>
<li>this will use the default options and generate a key</li>
</ul>
</li>
<li>More advanced: <code>$&gt; ssh-keygen -t rsa -b 2048 -f ./test -C &quot;this is a test&quot;</code>
<ul>
<li><code>-t &lt;type&gt;</code> specify the algorithm: rsa1, dsa, ecdsa, rsa</li>
<li><code>-b &lt;bits&gt;</code> this specifies the complexity of key, or the &quot;key size&quot;, bigger is better usually.</li>
<li><code>-f &lt;filename&gt;</code> name of the key</li>
<li><code>-C &quot;&lt;comment here&gt;&quot;</code> give it a comment to recognize it.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>Task scheduling with Cron</strong></p>
<ul>
<li>What is cron?
<ul>
<li>cron is a &quot;task scheduler&quot;</li>
<li>need to do something every day at 3pm??, this is your tool</li>
</ul>
</li>
<li><code>crontab -e</code>
<ul>
<li><code>&lt;some activation pattern&gt; &lt;some command here&gt;</code></li>
<li><code>* * * * * /bin/bash /root/some.script</code> - this will run every minute of every hour of every day of every month</li>
<li><code>@reboot /bin/bash /root/some.script</code> - this will run after every reboot</li>
<li><code>@yearly /bin/bash /root/some.script</code> - this will run every year</li>
<li>{ need more reference, <a href="http://team.macnn.com/drafts/crontab_defs.html">this page is great</a> }</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>Various backup methods (scp, rsync and rdiff-backup)</strong></p>
<ul>
<li><strong>scp</strong> - &quot;secure&quot; copy, copies over an ssh connection
<ul>
<li><code>scp -r &lt;from&gt; &lt;to&gt;</code></li>
</ul>
</li>
<li><strong>rsync</strong> - copies only the differences between local and remote copy
<ul>
<li><code>rsync -av &lt;from&gt; &lt;to&gt;</code></li>
</ul>
</li>
<li><strong>rdiff-backup</strong> - only copies the diffs between local and remote copy, stores all versions of diffs.
<ul>
<li><code>rdiff-backup &lt;from&gt; &lt;to&gt;</code></li>
</ul>
</li>
</ul>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Screen: Allowing you to "save" your SSH session if your connection drops.]]></title><description><![CDATA[<p>GNU screen, most commonly just called screen, is one of couple of tools which allows you to multiplex multiple &quot;virtual consoles&quot;. In layman's terms, it allows you to start into a virtual terminal session which you can detach from and then reattach without loosing what's going on inside</p>]]></description><link>https://matthiaslee.com/screen-allowing-you-to-save-your-ssh-session-if-your-connection-drops/</link><guid isPermaLink="false">5a0a60d882e47c00018dabde</guid><category><![CDATA[ssh]]></category><category><![CDATA[screen]]></category><category><![CDATA[reconnect ssh session]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Sun, 23 Feb 2014 03:48:25 GMT</pubDate><content:encoded><![CDATA[<p>GNU screen, most commonly just called screen, is one of couple of tools which allows you to multiplex multiple &quot;virtual consoles&quot;. In layman's terms, it allows you to start into a virtual terminal session which you can detach from and then reattach without loosing what's going on inside of that terminal session. This is ideal for when you are connecting to a machine over SSH and you are at risk of loosing your connection. That way after you have been disconnected you can just attach to exactly the same state you has disconnected from. No matter if you are in the middle of running a copy, top, irssi or sitting at a blank terminal. Everything is just like it was before you disconnected.</p>
<p>Screen has some basic, yet important to remember keyboard shortcuts. The most important one is CTRL+a then tap d. This will disconnect your current session.</p>
<p>To start a new screen, just type screen.</p>
<pre><code>madmaze@spork:~&amp; screen
</code></pre>
<p>Your terminal will go blank signaling that you have entered a new terminal session inside of a new screen. Lets start a command like top.</p>
<pre><code>madmaze@spork:~$ 			&lt; you are now inside of a &quot;screen&quot; &gt;
madmaze@spork:~$ top
</code></pre>
<p>Then to detach from this screen hit CTRL &amp; a, then tap d.</p>
<pre><code>&lt; hit CTRL+a then d to detach &gt;

[detached from 20041.pts-4.sp0rk]
madmaze@spork:~$
</code></pre>
<p>Now to see which screens are running, run screen -ls</p>
<pre><code>madmaze@spork:~$ screen -ls
There is a screen on:
	20041.pts-4.sp0rk	(02/22/2014 09:44:35 PM)	(Detached)
1 Socket in /var/run/screen/S-madmaze.
</code></pre>
<p>That means you currently have one screen open and you are detached from this screen. To reconnect, run screen -dr 20041</p>
<pre><code>madmaze@spork:~$ screen -dr 20041
</code></pre>
<p>.. and now you should be back to top which had been running in the background while you were detached.</p>
<pre><code>s):  9.8 us,  5.3 sy,  0.0 ni, 84.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   3105556 total,  2364832 used,   740724 free,    36460 buffers
KiB Swap:  2572284 total,   362828 used,  2209456 free,   532236 cached

  PID USER      PR  NI  VIRT  RES  SHR S  %CPU %MEM    TIME+  COMMAND
 2189 root      20   0 98.6m  23m  11m S   8.0  0.8 492:18.82 Xorg
27255 madmaze   20   0  538m 246m  27m S   6.3  8.1 117:39.77 chrome
 2824 madmaze   20   0  265m  12m 5820 S   3.3  0.4   6:31.73 guake
 8479 madmaze   20   0  518m 103m  20m S   3.0  3.4  63:45.22 chrome
 2893 madmaze   20   0 30724 3236  548 S   1.0  0.1  47:08.06 compton
 9733 madmaze   20   0  318m 115m  26m S   1.0  3.8  12:56.18 chrome  
</code></pre>
<p>Easy as that. Also when reconnecting, you don't have to type out the full name of the screen, only enough to differentiate it from other ones. In the case that you just have one screen running, <code>screen -dr</code> without a name will do the trick.</p>
]]></content:encoded></item><item><title><![CDATA[Building the Open-Electronics.org GSM/GPRS Shield V2]]></title><description><![CDATA[<p><img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1442.JPG" alt="GSM Shield overview"><br>
I got one of these <a href="http://store.open-electronics.org/GSM_GPRS_GPS_SHIELD">SIM900 based GSM Arduino shields</a> and decided to write a little step-by-step guide of building this shield. For schematics and the official documentation have a look <a href="http://www.open-electronics.org/gsm-gps-shield-for-arduino/">here</a>.<br>
Happy building!</p>
<ul>
<li>
<p>Lets start by soldering on microphone and speaker jacks:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1457.JPG" alt="GSM Shield step 1"></p>
</li>
<li>
<p>Clip the two separate jacks into place,</p></li></ul>]]></description><link>https://matthiaslee.com/building-open-electornics-org-gsmgprs-shield-v2/</link><guid isPermaLink="false">5a0a60d882e47c00018dabdf</guid><category><![CDATA[GSM shield]]></category><category><![CDATA[GPS Shield]]></category><category><![CDATA[Arduino]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Sun, 16 Feb 2014 03:12:15 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1442.JPG" alt="GSM Shield overview"><br>
I got one of these <a href="http://store.open-electronics.org/GSM_GPRS_GPS_SHIELD">SIM900 based GSM Arduino shields</a> and decided to write a little step-by-step guide of building this shield. For schematics and the official documentation have a look <a href="http://www.open-electronics.org/gsm-gps-shield-for-arduino/">here</a>.<br>
Happy building!</p>
<ul>
<li>
<p>Lets start by soldering on microphone and speaker jacks:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1457.JPG" alt="GSM Shield step 1"></p>
</li>
<li>
<p>Clip the two separate jacks into place, you might need to adjust the pins a bit to make them fit in the holes. Then grab your trusty soldering iron and solder up those connections<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1461.JPG" alt="GSM Shield step 2"></p>
</li>
<li>
<p>Next lets solder on the reset button:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1464.JPG" alt="GSM Shield step 3"></p>
</li>
<li>
<p>Solder on the 2 jumpers next to the mic jack:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1469.JPG" alt="GSM Shield step 4"></p>
</li>
<li>
<p>Now lets solder 6 Ceramic 47 pf Capacitors marked C5-C10:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1470.JPG" alt="GSM Shield step 5"><br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1471.JPG" alt="GSM Shield step 5.5"></p>
</li>
<li>
<p>Locate R1-R4(mine were labeled R3-R6), these should be 10K Ohm Resistors(Brown-Black-Orange):<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1473.JPG" alt="GSM Shield step 6"><br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1475.JPG" alt="GSM Shield step 6.5"></p>
</li>
<li>
<p>Next find the BS170 MOSFET and thread the leads into the holes associated with T1:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1515.JPG" alt="GSM Shield step 7"><br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1516.JPG" alt="GSM Shield step 7.5"></p>
</li>
<li>
<p>Attaching the Arduino headers, for this you need to be careful to attach them straight and square, otherwise you'll have trouble attaching it to your arduino.</p>
</li>
<li>
<p>I started with the pins on the side of the reset button, I used some extra header to join the two header sections together and put it into my panavise. First I soldered in only one pin of each header:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1520.JPG" alt="GSM Shield step 8"></p>
</li>
<li>
<p>Then, since on the other side it is non standard spacing and therefore the trick of joining the two heads together with a third doesn't work. Instead I used that third header as a crossbrace from the one side to the other. Yet again I only soldered in one pin on each header:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1523.JPG" alt="GSM Shield step 9"><br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1524.JPG" alt="GSM Shield step 9.33"><br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1525.JPG" alt="GSM Shield step 9.66"></p>
</li>
<li>
<p>Now before we solder in the rest of the pins, lets double check that everything is square by plugging it onto an actual arduino:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1527.JPG" alt="GSM Shield step 10"></p>
</li>
<li>
<p>If everything checks out, go and solder in all the pins. If not, now you have a chance of reheating and reseating the headers:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1531.JPG" alt="GSM Shield step 11"></p>
</li>
<li>
<p>Before we continue on to the other easy parts, lets solder on the surface mount headers for the sim900 and sim908 modules.</p>
</li>
<li>
<p>For this I applied some flux paste onto the pads, then aligned one of the headers and soldered on one end pin, making sure it was straight:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1533.JPG" alt="GSM Shield step 12"></p>
</li>
<li>
<p>Then follow by carefully soldering all the other pads. Make sure its clean and none of the solder bridges two pads.</p>
</li>
<li>
<p>Next lets solder on the rest of the through-hole headers, again make sure that these are straight as this is where the regular size SIM900 breakout will mate:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1541.JPG" alt="GSM Shield step 13"></p>
</li>
<li>
<p>Now lets tackle the unlabled capacitor just above the SIM908, according to the data sheet it should be marked CRTC. Clip it in place, making sure the negative lead, has a tiny minus sign stamped on it, faces towards the center of the board:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1542.JPG" alt="GSM Shield step 14"></p>
</li>
<li>
<p>Then continue by adding more capacitors, this time C1, C3 and C12. These should all be 100nF, probably labled something like &quot;104&quot;:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1544.JPG" alt="GSM Shield step 15"></p>
</li>
<li>
<p>Next locate and solder on C2, C4 and C11. C2 is a 470uF Capacitor. C4 and C11 are both 220uF capacitors. Make sure to get the polarity right, the longer lead has to go through the positive marked hole:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1546.JPG" alt="GSM Shield step 16"></p>
</li>
<li>
<p>Add the 1N4007 diode to D1:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1551.JPG" alt="GSM Shield step 17"></p>
</li>
<li>
<p>Finally lets put on the big MOSFET and heatsink, first approximate the bends the need to be done to the MOSFET and then thread the screw through the MOSFET and the heatsink and screw it down. Then solder the connections:<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1555.JPG" alt="GSM Shield step 18"></p>
</li>
<li>
<p>Almost done, to finish everything off, lets add headers for the Battery plug(its a surface mount, pain in the ass. Also optional depending on whether you're plan on using a battery or not), the Vext and the CHRG headers. I failed at attaching the Battery plug, so I guess I'll be running on external power =)<br>
<img src="https://matthiaslee.com/content/images/2014/Feb/IMG_1557.JPG" alt="GSM Shield step 19"></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Using pyGASP; Python Signal Processing(FFT,DWT,DCT) library with GPU-acceleration via pyCUDA]]></title><description><![CDATA[<p>I came across pyGASP while I was working on my Image Deconvolution research. It seems to be one of the only python tools which provides &quot;GPU-accellerated&quot; Discrete Wavelet Transforms. It features a barebones API similar to pywt. Sadly the docs and &quot;performance&quot; are a bit lacking,</p>]]></description><link>https://matthiaslee.com/using-pygasp-python-fast-fourier-wavelet-and-cosine-transform-library-with-a-gpu-acceleration-via-pycuda/</link><guid isPermaLink="false">5a0a60d882e47c00018dabdd</guid><category><![CDATA[pygasp]]></category><category><![CDATA[pycuda]]></category><category><![CDATA[gpu]]></category><category><![CDATA[python]]></category><category><![CDATA[dwt]]></category><category><![CDATA[wavelet]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Wed, 15 Jan 2014 16:27:51 GMT</pubDate><content:encoded><![CDATA[<p>I came across pyGASP while I was working on my Image Deconvolution research. It seems to be one of the only python tools which provides &quot;GPU-accellerated&quot; Discrete Wavelet Transforms. It features a barebones API similar to pywt. Sadly the docs and &quot;performance&quot; are a bit lacking, so here are some of my notes on getting it working and benchmarking it a bit.. <em><strong>Turns out that the pyGASP GPU code is about 5x slower than the CPU-based pywt (At least in my test case)</strong></em></p>
<h3 id="gettingstarted">Getting Started...</h3>
<h4 id="installingpygasp">Installing pyGASP:</h4>
<p>Easiest way to install pyGASP is using pip or a similar tool.</p>
<pre><code>$&gt; sudo pip install pygasp
</code></pre>
<h4 id="documentation">Documentation:</h4>
<p>The official documentation can be found here: <a href="http://pythonhosted.org/PyGASP/">http://pythonhosted.org/PyGASP/</a><br>
and here: <a href="https://pypi.python.org/pypi/PyGASP">https://pypi.python.org/pypi/PyGASP</a></p>
<p>The docstring generated documentation is not too bad and certainly gives you the basics. The README on the other hand is a out of date. Following are my notes on evaluating it.</p>
<h3 id="pywtvspygaspbenchmarkingthe2dwavelettransform">pywt vs pyGASP; Benchmarking the 2D wavelet transform:</h3>
<p>I am only going to compare dwt2 between these two packages. I, perhaps wrongly, assume other comparisons would yield similar results.</p>
<pre><code>import numpy as np
import scipy.misc
import pylab
from datetime import datetime as dt

import pywt
import pygasp.dwt.dwt as pygaspDWT
import pygasp.dwt.dwtCuda as pygaspDWTgpu

def show(data):
    pylab.jet()
    pylab.imshow(data)
    pylab.colorbar()
    pylab.show()
	pylab.clf()

# Lets get an image to play with.
img = scipy.misc.lena().astype(np.float32)

# pywt
s = dt.now()
res_pywt = pywt.dwt2(img, &quot;haar&quot;, &quot;zpd&quot;)
print &quot;pywt took:&quot;, dt.now()-s

# pygasp CPU version
s = dt.now()
res_gasp = pygaspDWT.dwt2(img, &quot;haar&quot;, &quot;zpd&quot;)
print &quot;pygaspCPU took:&quot;, dt.now()-s

# pygasp GPU version
s = dt.now()
res_gaspGPU = pygaspDWTgpu.dwt2(img, &quot;haar&quot;, &quot;zpd&quot;)
print &quot;pygaspGPU took:&quot;, dt.now()-s

# if you want to view the results
#show(res_pywt[0])
#show(res_gasp[0])
#show(res_gaspGPU[0])
</code></pre>
<p>Now that we have a basic comparison, lets grab a larger image and try it again:</p>
<pre><code>$&gt; wget http://i.imgur.com/CjJL2wG.jpg -O largeTest.jpg
</code></pre>
<p>And add the following:</p>
<pre><code># add this at the top
from PIL import Image
.
.
.
# replace the lena image with this:
imgObj = Image.open(&quot;largeTest.jpg&quot;)
img = np.array(imgObj)
</code></pre>
<p>Sadly the results with the larger image are quite disappointing. I had hoped that the pyGASP GPU code would be at least as fast as the CPU-based pywt.</p>
<pre><code>$&gt; python pygaspTest.py
pywt took: 0:00:01.412802
pygaspCPU took: 0:01:34.589889
pygaspGPU took: 0:00:06.963826
</code></pre>
<p>Even though the pyGASP GPU version is 13.5 times faster than its own CPU equivalent, the pywt CPU version is another ~5 times faster!! Perhaps it is not yet prime time for this library, but it might be a starting point to get a truely GPU accelerated version going. These tests were performed on a Nvidia Tesla K20c. For now I will have to venture on to find another faster solution, but I might come back to this and work on optimizing the it to suit my needs. Sadly there is no public code repo available.</p>
<p>Edit: Looks like pyGASP is related to <a href="http://ieeexplore.ieee.org/xpl/articleDetails.jsp?arnumber=6632683">this paper</a></p>
]]></content:encoded></item><item><title><![CDATA[Ubuntu Server & HP Proliant Support Pack ML350 G5, hpacucli & hp-health]]></title><description><![CDATA[<p>In order to fully take advantage of the built in HP ProLiant features, we need to install HP's <a href="http://h18004.www1.hp.com/products/servers/management/psp/index.html">ProLiant Support Pack</a>. Sadly HP no longer supports this for Debian/Ubuntu, but with a little bit of tweaking we can still make it work.</p>
<h3 id="installinghparrayadmintools">Installing HP Array admin tools</h3>
<p>Download bootstrap</p>]]></description><link>https://matthiaslee.com/ubuntu-server-hp-proliant-ml350-g5/</link><guid isPermaLink="false">5a0a60d782e47c00018dabdb</guid><category><![CDATA[HP]]></category><category><![CDATA[ml350]]></category><category><![CDATA[RAID]]></category><category><![CDATA[hpacucli]]></category><category><![CDATA[hp-health]]></category><category><![CDATA[server]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Sat, 11 Jan 2014 05:49:08 GMT</pubDate><content:encoded><![CDATA[<p>In order to fully take advantage of the built in HP ProLiant features, we need to install HP's <a href="http://h18004.www1.hp.com/products/servers/management/psp/index.html">ProLiant Support Pack</a>. Sadly HP no longer supports this for Debian/Ubuntu, but with a little bit of tweaking we can still make it work.</p>
<h3 id="installinghparrayadmintools">Installing HP Array admin tools</h3>
<p>Download bootstrap from HP</p>
<pre><code>test@ml350:~$ wget http://downloads.linux.hp.com/SDR/downloads/bootstrap.sh
</code></pre>
<p>We need to fix HP's bootstrap script to point to the current HP download url. Then we add HP's repo. Since HP no longer supports debian/ubuntu, we must override the distro version detection, therefore we specify <code>-r oneiric</code>,  11.10 being the latest supported version. Then we add the repo's GPG key.</p>
<pre><code>test@ml350:~$ sed -i 's/blofly.usa/downloads.linux/g' ./bootstrap.sh
test@ml350:~$ chmod +x bootstrap.sh
test@ml350:~$ sudo ./bootstrap.sh -r oneiric ProLiantSupportPack
test@ml350:~$ wget -qO- http://downloads.linux.hp.com/SDR/downloads/ProLiantSupportPack/GPG-KEY-ProLiantSupportPack |  sudo apt-key add -
</code></pre>
<p>Finally we can update and install hpacu-cli and hp-health</p>
<pre><code>test@ml350:~$ sudo apt-get update
test@ml350:~$ sudo apt-get install hpacucli hp-health
</code></pre>
<h3 id="fixingerrornocontrollersdetected">Fixing &quot;Error: No controllers detected.&quot;</h3>
<p>Now if you are like me and running a semi modern system(Kernel 3.0+), you will get the following error message:</p>
<pre><code>test@ml350:~$ sudo hpacucli ctrl all show config
Error: No controllers detected.
</code></pre>
<p>To fix this you can either compile and run uname26 or backport a newer version of hpacucli from RHEL/SUSE to Debian/Ubuntu. For further reference, check <a href="http://blog.wpkg.org/2012/03/15/hpacucli-error-no-controllers-detected-with-hpsa-module-in-use/">here</a>.</p>
<h4 id="option1option2isrecommended">Option #1 (Option 2 is recommended):</h4>
<p>Compile uname26 to pretend we are in an older kernel</p>
<pre><code>test@ml350:~$ mkdir uname26
test@ml350:~$ cd uname26
test@ml350:~/uname26$ wget http://mirror.linux.org.au/linux/kernel/people/ak/uname26/Makefile
test@ml350:~/uname26$ wget http://mirror.linux.org.au/linux/kernel/people/ak/uname26/uname26.c
test@ml350:~/uname26$ make
test@ml350:~/uname26$ ./uname26
</code></pre>
<p>Now you can run it with uname26:</p>
<pre><code>test@ml350:~/uname26$ sudo ./uname26 hpacucli ctrl all show config

Smart Array E200i in Slot 0 (Embedded)    (sn: QXXXXXXXX)

 array A (SAS, Unused Space: 0 MB)

  logicaldrive 1 (341.7 GB, RAID 5, OK)

  physicaldrive 1I:1:1 (port 1I:box 1:bay 1, SAS, 72 GB, OK)
  physicaldrive 1I:1:2 (port 1I:box 1:bay 2, SAS, 72 GB, OK)
  physicaldrive 1I:1:3 (port 1I:box 1:bay 3, SAS, 72 GB, OK)
  physicaldrive 1I:1:4 (port 1I:box 1:bay 4, SAS, 72 GB, OK)
  physicaldrive 2I:1:5 (port 2I:box 1:bay 5, SAS, 72 GB, OK)
  physicaldrive 2I:1:6 (port 2I:box 1:bay 6, SAS, 72 GB, OK)
</code></pre>
<h4 id="option2">Option #2:</h4>
<p>Backport the newer version of hpacucli from SUSE, since debian and ubuntu are no longer supported.</p>
<p>We use alien to convert the SUSE RPM into a DEB and then install it.</p>
<pre><code>test@ml350:~$ wget http://downloads.linux.hp.com/SDR/downloads/proliantsupportpack/SuSE/11.2/x86_64/9.10/hpacucli-9.10-22.0.x86_64.rpm
test@ml350:~$ sudo apt-get install alien
test@ml350:~$ sudo alien hpacucli-9.10-22.0.x86_64.rpm
test@ml350:~$ sudo dpkg -i hpacucli_9.10-23_amd64.deb
</code></pre>
<p>And now it works without using uname26:</p>
<pre><code>test@ml350:~$ sudo hpacucli ctrl all show config

Smart Array E200i in Slot 0 (Embedded)    (sn: QXXXXXXXX)

 array A (SAS, Unused Space: 0 MB)

  logicaldrive 1 (341.7 GB, RAID 5, OK)

  physicaldrive 1I:1:1 (port 1I:box 1:bay 1, SAS, 72 GB, OK)
  physicaldrive 1I:1:2 (port 1I:box 1:bay 2, SAS, 72 GB, OK)
  physicaldrive 1I:1:3 (port 1I:box 1:bay 3, SAS, 72 GB, OK)
  physicaldrive 1I:1:4 (port 1I:box 1:bay 4, SAS, 72 GB, OK)
  physicaldrive 2I:1:5 (port 2I:box 1:bay 5, SAS, 72 GB, OK)
  physicaldrive 2I:1:6 (port 2I:box 1:bay 6, SAS, 72 GB, OK)
</code></pre>
<p>Cheers &amp; enjoy =).</p>
]]></content:encoded></item><item><title><![CDATA[Use the "SWAP" (on your VPS)]]></title><description><![CDATA[<h3 id="swapspacecanbealifesaver">SWAP space can be a life saver.</h3>
<p>If your are like me, you may often find yourself messing with VPSes and other servers. All too often these are provisioned with very little RAM, 512MB or less.<br>
When your applications/processes run out of RAM, most of them become relatively unhappy</p>]]></description><link>https://matthiaslee.com/use-the-swap-on-your-vps/</link><guid isPermaLink="false">5a0a60d782e47c00018dabda</guid><category><![CDATA[VPS]]></category><category><![CDATA[SWAP]]></category><category><![CDATA[Linux]]></category><category><![CDATA[fstab]]></category><category><![CDATA[digital ocean]]></category><dc:creator><![CDATA[Matthias A. Lee]]></dc:creator><pubDate>Mon, 09 Dec 2013 01:16:32 GMT</pubDate><content:encoded><![CDATA[<h3 id="swapspacecanbealifesaver">SWAP space can be a life saver.</h3>
<p>If your are like me, you may often find yourself messing with VPSes and other servers. All too often these are provisioned with very little RAM, 512MB or less.<br>
When your applications/processes run out of RAM, most of them become relatively unhappy and begin to behave erradically and finally crash.<br>
In many cases you only need more RAM temporarily while you run some memory intensive command or perhaps you just want a safty net. SWAP space offers you this flexibillity by tapping into your hard drive's space. Imagine it as emergency RAM which lives on your hard drive in the form of a file or partition. In the event that more RAM is need than available, the OS will automatically tap that file instead of crashing your programs. The only down side is that SWAP is many times slower than your physical RAM and will therefore affect your overall performance.</p>
<h2 id="creatingaswapfile">Creating a SWAP file</h2>
<p>Log into your machine over ssh or locally, then we will create a 512mb file full of zeros using dd and finally we will make it only read-writable by root.</p>
<pre><code>sudo dd if=/dev/zero of=/swapfile bs=1024 count=524288
sudo chown root:root /swapfile
sudo chmod 600 /swapfile
</code></pre>
<p>now we will turn this file into a swapfile format using mkswap:</p>
<pre><code>sudo mkswap /swapfile
</code></pre>
<p>Swapping the swapfile on:</p>
<pre><code>sudo swapon /swapfile
</code></pre>
<p>To check whether it worked or not check the output of free -m for the line specifying swap:</p>
<pre><code>derp@box:~$ free -m
         total       used       free     shared    buffers     cached
Mem:           491        484          6          0         21        169
-/+ buffers/cache:        293        198
Swap:          511          0        511
</code></pre>
<p>In the above example it looks like everything went well. Now we have to make it permanent, otherwise at every reboot you would need to run the swapon command again. This is where our friend fstab comes in.</p>
<pre><code>sudo nano /etc/fstab
</code></pre>
<p>Add the following line at the bottom:</p>
<pre><code>/swapfile       swap    swap    defaults        0       0
</code></pre>
<p>This will mount the swap file at boot with default options.</p>
<p>Cheers and enjoy your swapping.</p>
<p>Also sidenote: VPSes and other servers with fast disks will do much better when swapping. I can recommend <a href="https://www.digitalocean.com/?refcode=618964996923">Digital Ocean</a>, all their VPSes are backed by SSD, which makes running in swap very quick.</p>
]]></content:encoded></item></channel></rss>