<?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[Curtis Z - Tactical Short Circuit]]></title><description><![CDATA[Come play my game.]]></description><link>https://curtisz.com/</link><image><url>https://curtisz.com/favicon.png</url><title>Curtis Z - Tactical Short Circuit</title><link>https://curtisz.com/</link></image><generator>Ghost 1.22</generator><lastBuildDate>Tue, 28 Jul 2026 01:44:03 GMT</lastBuildDate><atom:link href="https://curtisz.com/author/curtis/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Complexity is Always a Consideration]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Kubernetes is like other container orchestration platforms, except that it has many layers of abstraction built into it with the idea that these abstractions allow you more headroom to scale. The thing is that these abstractions make Kubernetes far, far more complex than the vast majority of organizations need to</p></div>]]></description><link>https://curtisz.com/when-it-strikes-recovering-a-deleted-ucp-agent-system-service-in-dockers-universal-control-plane/</link><guid isPermaLink="false">5ad0f30be5d6fb0001ba0433</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Thu, 07 Dec 2017 19:11:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Kubernetes is like other container orchestration platforms, except that it has many layers of abstraction built into it with the idea that these abstractions allow you more headroom to scale. The thing is that these abstractions make Kubernetes far, far more complex than the vast majority of organizations need to leverage. Bear with me for a minute... Kubernetes was built by Google to orchestrate its services. But your company is not Google and there are many more important problems on the road between your company and Google. You do what whatever you want, but I don't spend time solving problems I don't have. And we haven't even touched on the problems you're <em>creating</em> by introducing so many layers of abstraction into your environment. At some point, many of the folks using Kubernetes will hit its complexity like a brick wall. They may not be able to extricate themselves because of sunk cost. Fortunately for them, Docker Swarm is in a position to serve as a lifeline for these people to automate away much of the complexity around Kubernetes and bring their environment and orchestration back under their control again.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Configuring Trackpoint on the Lenovo Thinkpad]]></title><description><![CDATA[<div class="kg-card-markdown"><p>This is more or less just a post for myself. I always end up dumping a couple of hours into this problem whenever I get a new machine for work -- surprise! I work for Docker now -- and tonight especially I really could have used this post instead of</p></div>]]></description><link>https://curtisz.com/configuring-trackpoint-on-the-lenovo-thinkpad-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d84</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Wed, 16 Nov 2016 05:28:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>This is more or less just a post for myself. I always end up dumping a couple of hours into this problem whenever I get a new machine for work -- surprise! I work for Docker now -- and tonight especially I really could have used this post instead of wasting that time researching the problem all over again. I choose Thinkpads when I have a choice, because the <a href="http://www.macrumors.com/2016/10/28/new-macbook-pros-no-32gb-ram-battery-life/">popular</a> <a href="http://www.theverge.com/circuitbreaker/2016/10/25/13409258/apple-new-macbook-pro-no-escape-key">alternative</a> <a href="http://mashable.com/2016/10/28/macbook-pro-dongle-hell/">is</a> <a href="https://www.reddit.com/r/apple/comments/5cuhvo/2016_macbook_pro_non_touchbar_constantly_shocking/">stupid</a>.</p>
<p>Anyway, I use Linux Mint with Cinnamon, and LM18 is the current version. It's based on Ubuntu 16.04. I've chosen a P50 and upgraded the RAM to 64GB. Everything works out of the box, including the weird dual graphics situation going on under the hood. However, I want a super sensitive Trackpoint. The sensitivity settings are under something like <code>/sys/devices/platform/i8042/serio1/serio2/</code> in the <code>sensitivity</code>, <code>speed</code> and <code>inertia</code> files. I like to keep mine at about <code>255</code>, <code>230</code>, and <code>4</code>, respectively. The value of <code>255</code> is maximal, btw.</p>
<p>Now, simply dumping my preferences into those files works for the current session. Meaning, when I reboot the machine, they are reset to their defaults. So I'm using systemd to <a href="http://www.linuxfromscratch.org/lfs/view/7.10-systemd/chapter07/systemd-custom.html">write values into these files on boot</a>. I've got a <code>/etc/tmpfiles.d/tpoint.conf</code> file with the following contents:</p>
<pre><code>w /sys/devices/platform/i8042/serio1/serio2/speed - - - - 230
w /sys/devices/platform/i8042/serio1/serio2/sensitivity - - - - 255
w /sys/devices/platform/i8042/serio1/serio2/inertia - - - - 4

</code></pre>
<p>Now, this works great. However, after I resume (or thaw) from a suspend (or hibernate), the <em>location</em> of these config files changes. So what I've done is added a simple shell script to <code>/etc/pm/sleep.d/trackpoint-fix</code> which contains the following:</p>
<pre><code>#!/bin/bash
# set sensitivity/speed of trackpoint on resume
case &quot;${1}&quot; in
	suspend|hibernate)
		# suspending to RAM
		sleep 0
		;;
	resume|thaw)
		# resume from suspend 
		newdir=$(find /sys/devices/platform/ | grep sensitivity | sed -e &quot;s/sensitivity//&quot;)
		echo 230 | sudo tee &gt; ${newdir}speed
		echo 255 | sudo tee &gt; ${newdir}sensitivity
		echo 4 | sudo tee &gt; ${newdir}inertia
		;;       
esac   
</code></pre>
<p>This is pretty self-explanatory. I dig around for the new location of these configuration files and then dump my favorite values into them. That's all there is to it.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Dockerized IPython / Anaconda for Machine Learning]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Hey! You might have seen my <a href="https://curtisz.com/dockerized-ipython-and-graphlab-create-for-ml/">recent post</a> about having Dockerized some software called GraphLab Create (together with IPython) for a machine learning course I was taking. As it happens, I've found that image so useful for other, generic ML work that I've pared it down to its IPython/Anaconda</p></div>]]></description><link>https://curtisz.com/dockerized-ipython-anaconda-for-machine-learning-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d83</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Fri, 24 Jun 2016 08:27:46 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Hey! You might have seen my <a href="https://curtisz.com/dockerized-ipython-and-graphlab-create-for-ml/">recent post</a> about having Dockerized some software called GraphLab Create (together with IPython) for a machine learning course I was taking. As it happens, I've found that image so useful for other, generic ML work that I've pared it down to its IPython/Anaconda bundle only. So I'd like to introduce the super-simple but super-useful <a href="https://github.com/curtiszimmerman/docker-ipython">Dockerized IPython / Anaconda</a> project!</p>
<p>This repo includes a couple of useful scripts: One for building the image (<code>build.sh</code>) and one for running the resultant image as a container (<code>run.sh</code>). Just run the build script and then the run script (and optionally provide a directory to mount into the container for data files) and you're all set! <strong>Note</strong>: Either your specified directory or current working directory will be mounted to <code>/data</code> as a volume into the container! Also, your IPython Notebook may include <code>import</code> statements which reference functions inside files in your new <code>/data</code> volume directory. This means you will need to change any path references to include <code>/data</code>, and specifically add the <code>/data</code> directory to your import path by adding this to the top of your IPython Notebook:</p>
<pre><code>import sys
sys.path.insert(0, '/data')

## this relative dir won't work:
# data_dir = 'foo/dataset.1'
## so we just add /data to the front:
data_dir = '/data/foo/dataset.1' 
</code></pre>
<p>Finally, I try to be readable, but take a look at my earlier post linked above if you want a breakdown of what's going on in there. Have fun!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Let's Encrypt: Nginx-Proxy Docker Companion]]></title><description><![CDATA[<div class="kg-card-markdown"><p>I've been using the fairly popular <a href="https://github.com/jwilder/nginx-proxy">nginx-proxy</a> reverse proxy for Docker containers, created by <a href="http://jasonwilder.com/">Jason Wilder</a>. It's a slick, super-simple method of putting many containers on a single host that all need to share HTTP/HTTPS ports. I am also a huge fan of the <a href="https://letsencrypt.org/">Let's Encrypt</a> project. Free SSL</p></div>]]></description><link>https://curtisz.com/lets-encrypt-nginx-proxy-docker-companion-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d82</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Mon, 30 May 2016 05:39:10 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>I've been using the fairly popular <a href="https://github.com/jwilder/nginx-proxy">nginx-proxy</a> reverse proxy for Docker containers, created by <a href="http://jasonwilder.com/">Jason Wilder</a>. It's a slick, super-simple method of putting many containers on a single host that all need to share HTTP/HTTPS ports. I am also a huge fan of the <a href="https://letsencrypt.org/">Let's Encrypt</a> project. Free SSL certificates as long as you can prove that you are the domain's operator. This is really how it should work: In my book, forcing people to pay for SSL certificates is shitty and exploitative, especially considering how incredibly important encryption is these days. And incidentally, the behavior around self-signed certificates in browsers is stupid and broken.</p>
<p>So anyways, I played with the Let's Encrypt stuff late last year when they went into public beta. Best Christmas present ever! So the idea is pretty simple: You tell your Let's Encrypt client (probably best to use <a href="https://github.com/certbot/certbot">certbot</a>) that you want a certificate, and it talks to the Let's Encrypt server, requesting a certificate from the CA. That causes Let's Encrypt to make a curl call to your domain, requesting a specific resource that certbot creates. That resource looks something like <code>http://www.example.com/.well-known/acme-challenge/g89SrgM4UAJGHiukm3GqQ3xMjTnpN-kZDYb27u4aTRW</code>. That resource is just a regular file on disk. It has contents that look something like <code>DTe7mGGhLlML7Vlh4dyNTu97OiIrIIs7xd5O0Fpmlq8.TaRs2K47il2D0K9RjmOKOx7Neuu91FdEpLp2Wo4FcNI</code>. As long as that resource matches what Let's Encrypt is looking for, you get a free SSL certificate! And they've built some magic into certbot so that it automagically installs that cert into your webserver if it's a common one (e.g. Apache or Nginx or something).</p>
<p>I wanted to use it on on my Dockerized web frontends, which use nginx-proxy. I had spotted a couple of issues on nginx-proxy's Github page which mentioned Let's Encrypt, but I hadn't yet tried to get this working with my nginx-proxy container. Getting that to work does not seem like a trivial task. Not having the spare minutes to get Let's Encrypt working in my infrastructure, I put it on the back burner and made a mental note to check in every once in a while. Well, I completely forgot about it until I got an email recently reminding me to renew one of my SSL certs. And I'm not paying $8.99 for something that should be free, so I knew it was time to check back in with Let's Encrypt being incorporated into the nginx-proxy project.</p>
<p>Enter <a href="https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion">nginx-proxy-letsencrypt-companion</a>. This is a docker container that sits coupled to your nginx-proxy container, sharing its volumes and paying attention to containers spinning up that have <code>LETSENCRYPT_HOST</code> and <code>LETSENCRYPT_EMAIL</code> environment variables set. The idea is that you start your nginx-proxy container, then start up this nginx-proxy-letsencrypt-companion container, and then start up your other containers that need Let's Encrypt certificates. The companion will request new Let's Encrypt certificates for containers that do not have current certificates and which also have those <code>LETSENCRYPT_*</code> environment variables set.</p>
<p>So here are my notes for getting this going. I ended up adding <code>/usr/share/nginx/html</code> as a data volume in my nginx-proxy container, and making a couple of the volumes <code>rw</code> instead of <code>ro</code>. Thus, my nginx-proxy run command looks something like this:</p>
<pre><code>docker run -d \
    --name=&quot;nginx-proxy&quot; \
    --restart=&quot;always&quot; \
    -p 80:80 \
    -p 443:443 \
    -v &quot;/var/docker/nginx-proxy/htpasswd:/etc/nginx/htpasswd&quot; \
    -v &quot;/var/docker/nginx-proxy/vhost.d:/etc/nginx/vhost.d&quot; \
    -v &quot;/var/docker/nginx-proxy/certs:/etc/nginx/certs&quot; \
    -v &quot;/var/run/docker.sock:/tmp/docker.sock&quot; \
    -v &quot;/usr/share/nginx/html&quot; \
    jwilder/nginx-proxy
</code></pre>
<p>And the command for the brand new nginx-proxy-letsencrypt-companion container looks like this:</p>
<pre><code>docker run -d \
    --name=&quot;nginx-proxy-letsencrypt-companion&quot; \
    --restart=&quot;always&quot; \
    -v &quot;/var/run/docker.sock:/var/run/docker.sock:ro&quot; \
    --volumes-from &quot;nginx-proxy&quot; \
    jrcs/nginx-proxy-letsencrypt-companion
</code></pre>
<p>And finally, your individual containers will follow this pattern (note the environment variables mentioned above):</p>
<pre><code>docker run -d \
    --name=&quot;example.com&quot; \
    --restart=&quot;always&quot; \
    -e &quot;VIRTUAL_HOST=example.com,www.example.com&quot; \
    -e &quot;VIRTUAL_PORT=2368&quot; \
    -e &quot;LETSENCRYPT_HOST=example.com,www.example.com&quot; \
    -e &quot;LETSENCRYPT_EMAIL=contact@example.com&quot; \
    -v /var/docker/example.com/ghost:/var/lib/ghost \
    ghost
</code></pre>
<p>And there you have it! Once you get your containers up -- in that order: nginx-proxy, nginx-proxy-letsencrypt-companion, and your web container -- give the Let's Encrypt a minute to phone home and get a call back, and you'll have a free SSL certificate and related miscellany there in <code>/var/docker/nginx-proxy/certs</code> on your host. Also, the reason I created a data volume in the nginx-proxy container at <code>/usr/share/nginx/html</code> instead of a bind-mount volume. I do this because the Let's Encrypt client in the companion container will continually update the authorization data, and I don't want to have to worry later about cleaning up a huge, sprawling directory full of those files, some of which would contain valid information, and many others containing invalid information. Of course, you do whatever you want, as long as its right for you. Good luck!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Private Docker v2 Registry Upgrade Notes]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Recently, I was offering some help on Stack Overflow to someone asking about deleting images from a private Docker registry. Here I mean a v2 registry, which is part of the <a href="https://github.com/docker/distribution/">Docker Distribution</a> project. I should advise anyone reading this that no one ever refers to a v1 registry anymore:</p></div>]]></description><link>https://curtisz.com/private-docker-v2-registry-upgrade-notes-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d7f</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Sat, 07 May 2016 07:19:13 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Recently, I was offering some help on Stack Overflow to someone asking about deleting images from a private Docker registry. Here I mean a v2 registry, which is part of the <a href="https://github.com/docker/distribution/">Docker Distribution</a> project. I should advise anyone reading this that no one ever refers to a v1 registry anymore: That project is dead, even though it occupies the <code>registry:latest</code> image tag on Dockerhub (you want to pull <code>registry:2</code> at least). The v1 registry is an old Python project, and v2 is written in Go. Anyways, the v2 registry has not had delete capabilities (via the API) since its inception. This was my initial assumption, but I took the opportunity to research the latest information.</p>
<p>As it turns out, the latest versions of registry (later than v2.4 I think) <em>do</em> have delete functionality. While the main Docker project (&quot;docker-engine&quot;) has excellent documentation, the Distribution project has previously not been. It's not bad, but it's not great, either. The API documentation is not very clear on using the new delete API functionality. But it's there, along with an interesting <a href="https://github.com/docker/distribution/blob/master/docs/garbage-collection.md">garbage collection</a> mechanism. That's a topic for another day, but it's the reason I wanted to upgrade to version 2.4 of the registry. I was using version 2.1 or something.</p>
<p>Cue an upgrade, and a couple of problems. First, in the <code>config.yml</code> file, in the <code>cache</code> section under the <code>storage</code> section, the <code>layerinfo</code> setting has been deprecated and renamed to <code>blobdescriptor</code>. That isn't a blocking change <em>yet</em>, but it will be soon, so rename it now while you have the chance.</p>
<p>Finally, if you're <a href="https://docs.docker.com/registry/storage-drivers/s3/">backing your registry with S3</a> like a sane human being, the permissions have changed and the change is not documented anywhere. <em>Zing!</em> I couldn't push when I fired up my new registry container. I kept getting &quot;Retrying in X seconds&quot; messages when pushing individual layers. I killed and deleted the container, started up a new one with the <code>level</code> setting under the <code>log</code> section set to <code>debug</code> in my <code>config.yml</code> file. This yielded the key to the issue (notice the &quot;s3aws: AccessDenied&quot; message and <code>403</code> status code):</p>
<pre><code>&quot;err.code&quot;:&quot;unknown&quot;,&quot;err.detail&quot;:&quot;s3aws: AccessDenied: Access Denied\n\tstatus code: 403, request id: 11E0123C033B0DB5&quot;,&quot;err.message&quot;:&quot;unknown error&quot;
</code></pre>
<p>Here's what the <strong>new</strong> S3 policy needs to look like (<strong>beware</strong> copying from the documentation linked above: There is an errant comma in the documented policy):</p>
<pre><code> &quot;Statement&quot;: [
      {
        &quot;Effect&quot;: &quot;Allow&quot;,
        &quot;Action&quot;: [
          &quot;s3:ListBucket&quot;,
          &quot;s3:GetBucketLocation&quot;,
          &quot;s3:ListBucketMultipartUploads&quot;
        ],
        &quot;Resource&quot;: &quot;arn:aws:s3:::mybucket&quot;
      },
      {
        &quot;Effect&quot;: &quot;Allow&quot;,
        &quot;Action&quot;: [
          &quot;s3:PutObject&quot;,
          &quot;s3:GetObject&quot;,
          &quot;s3:DeleteObject&quot;,
          &quot;s3:ListMultipartUploadParts&quot;,
          &quot;s3:AbortMultipartUpload&quot;
        ],
        &quot;Resource&quot;: &quot;arn:aws:s3:::mybucket/*&quot;
      }
]
</code></pre>
<p>Just for the record, this new policy adds support for the <code>s3.GetBucketLocation</code>, <code>s3:ListBucketMultipartUploads</code> Actions on your particular bucket, and the <code>s3:ListMultipartUploadParts</code> and <code>s3:AbortMultipartUpload</code> Actions on your bucket contents.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Dockerized IPython and GraphLab Create for Machine Learning]]></title><description><![CDATA[<div class="kg-card-markdown"><p>So here's a fun surprise: I love machine learning just like everyone else! I have played around with a few ML concepts in a couple of weekend projects, but nothing serious. Earlier this year I came across a <a href="https://www.coursera.org/learn/ml-foundations">Coursera ML course</a> which seemed like a great place to start a</p></div>]]></description><link>https://curtisz.com/dockerized-ipython-and-graphlab-create-for-ml-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d7d</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Sun, 24 Apr 2016 08:43:12 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>So here's a fun surprise: I love machine learning just like everyone else! I have played around with a few ML concepts in a couple of weekend projects, but nothing serious. Earlier this year I came across a <a href="https://www.coursera.org/learn/ml-foundations">Coursera ML course</a> which seemed like a great place to start a more formal education about the subject. Sweet! I'm a self-taught programmer, but taking university CS courses as a non-degree-seeking student helped fill the gaps in my knowledge. I see the value of formal education.</p>
<p>To prepare for the class, they want you to set up a Python 2.7 environment (Anaconda), IPython, and GraphLab Create. <a href="https://ipython.org/">IPython</a> is an interactive environment for programming languages. Obviously, it was first targeted for Python, but I guess they do all kinds of languages now. One of the cool features is built-in support for data visualizations. We're specifically concerned with the IPython &quot;Notebook&quot; feature set. We are also using <a href="https://dato.com/products/create/">GraphLab Create</a>, which is a commercial product spawned out of a project from CMU and released by <a href="https://dato.com/">Dato</a>. The CEO of Dato is one of the primary instructors of the course.</p>
<p>Real quick, GraphLab Create is a commercial product, as I've mentioned. Dato offers a free &quot;student&quot; license for this product, which is what we will be using for the course. This blog post assumes that you have already signed up for one of these educational licenses, and you have already been given your license key (which looks, for example, like <code>ABCD-0123-EF45-6789-9876-54FE-3210-DCBA</code>). You will need both this license key and the email address you signed up with in order to continue.</p>
<p>Now, all this software is great (really, you'll see what I mean when you start using it), but it seems to be quite a lot of stuff that I'd rather not have installed on my filesystem if I can help it. If you have spoken to me at all in the last two and half years, you know that I am a big fan of Docker. Probably 80% of this blog is about Docker. And so it should come as no surprise that I've got compartmentalization on my mind. I install and use just about everything Dockerized. These tools are phenomenally useful for this illuminating ML course, but I want them containerized.</p>
<p>I've taken the installation instructions for <a href="https://www.continuum.io/downloads">Anaconda Python</a> and <a href="https://dato.com/download/install-graphlab-create.html">GraphLab Create</a> and put them into a Dockerfile, which you'll have a chance to look at a little further down. Before I get to that, I want to point out that if you look closely at the install instructions for GraphLab Create, you'll see a mention of getting your Nvidia GPU to work with the software in order to speed things along. Specifically for machine learning, having a GPU workhorse can be a computational difference of days, weeks, or even years.</p>
<p>CPUs are fine for most projects and will probably work just fine for this course, but I have seen the question of CUDA processing specifically with respect to Docker. I had heard that CUDA was not available to Docker containers because of the difficulty of making the Nvidia drivers available to the containerized process. Well, I took it as an opportunity for research, and it just so happens Nvidia has very recently released an application called <a href="https://github.com/NVIDIA/nvidia-docker">nvidia-docker</a> specifically for making the GPU available to Docker containers! You can follow that link for all kinds of interesting information, but suffice it to say nvidia-docker is a drop-in replacement for the docker executable which is used on images that you want to be CUDA-capable. They also offer similar functionality in a daemon plugin called nvidia-docker-plugin. You can read about the differences in the nvidia-docker documentation.</p>
<p>A quick note about nvidia-docker: I ran into a <a href="https://github.com/NVIDIA/nvidia-docker/issues/73">problem</a> with the <code>.deb</code> I had installed per their instructions, because I am using the latest version of Docker (1.11), and as of this writing, they haven't released an updated <code>.deb</code> with the working code. And so that meant that I had to complie my own nvidia-docker binary. It's super easy (and doubly so for anyone with the technical wherewithal to be taking a machine learning course): Just <code>git clone https://github.com/NVIDIA/nvidia-docker</code> and then <code>cd nvidia-docker &amp;&amp; make</code> and then <code>sudo make install</code> and you've got a working binary!</p>
<p>Also, I ran into import errors during the ML course (specifically with matplotlib). I ended up <a href="https://stackoverflow.com/questions/32389599/anaconda-importerror-libsm-so-6-cannot-open-shared-object-file-no-such-file-o/32389631#32389631">solving this</a> by installing <code>python-qt4</code> inside the Docker container right off the bat.</p>
<p>So finally, here's what my Dockerfile looks like:</p>
<pre><code>FROM ubuntu:14.04
MAINTAINER curtisz &lt;software@curtisz.com&gt;

# get stuff
RUN apt-get update -y &amp;&amp; \
	apt-get install -y \
		curl \
		python-qt4 &amp;&amp; \
	rm -rf /var/cache/apt/archive/*

# get more stuff in one layer so unionfs doesn't store the 400mb file in its layers
WORKDIR /tmp
RUN curl -o /tmp/Anaconda2-4.0.0-Linux-x86_64.sh http://repo.continuum.io/archive/Anaconda2-4.0.0-Linux-x86_64.sh &amp;&amp; \
	chmod +x ./Anaconda2-4.0.0-Linux-x86_64.sh &amp;&amp; \
	./Anaconda2-4.0.0-Linux-x86_64.sh -b &amp;&amp; \
	rm ./Anaconda2-4.0.0-Linux-x86_64.sh
# make the anaconda stuff available
ENV PATH=${PATH}:/root/anaconda2/bin

## anaconda
RUN conda create -n dato-env python=2.7 anaconda
# (use JSON format to force interpretation by /bin/bash)
RUN [&quot;/bin/bash&quot;, &quot;-c&quot;, &quot;. activate dato-env&quot;]
RUN conda update pip

## install graphlab create with creds provided in --build-arg in 'docker build' command:
ARG USER_EMAIL
ARG USER_KEY
RUN pip install --upgrade --no-cache-dir https://get.dato.com/GraphLab-Create/1.9/${USER_EMAIL}/${USER_KEY}/GraphLab-Create-License.tar.gz

## install ipython and ipython notebook
RUN conda install ipython-notebook

## upgrade GraphLab Create with GPU Acceleration
RUN pip install --upgrade --no-cache-dir http://static.dato.com/files/graphlab-create-gpu/graphlab-create-1.9.gpu.tar.gz

CMD jupyter notebook
</code></pre>
<p>I ended up having to update this Dockerfile when Dato released GraphLab Create version 1.9. It was as easy as changing &quot;1.8.5&quot; to &quot;1.9&quot; in the Dockerfile. Everything else was the same. Keep this in mind if you find that you need to install a newer version of GraphLab Create. Now, you'll build the image with this command, <strong>making sure to replace the email and license key with your own details</strong>:</p>
<pre><code>docker build -t=graphlab --build-arg &quot;USER_EMAIL=genius@example.edu&quot; --build-arg &quot;USER_KEY=ABCD-0123-EF45-6789-9876-54FE-3210-DCBA&quot; .
</code></pre>
<p>The build will take a few minutes. It downloads a few hundred megabytes of stuff. When you're done with that, you can launch IPython with the following command:</p>
<pre><code>nvidia-docker run -d --name=graphcreate -v &quot;`pwd`/data:/data&quot; --net=host graphlab:latest
</code></pre>
<p>Voila! You've got this whole operation running and you can access your IPython notebook by going to <a href="http://localhost:8888/">http://localhost:8888/</a> in your browser!</p>
<p><strong>Please note</strong>: When it comes time to use GraphLab Create, you will be able to browse its UI normally, because we have specified <code>--net=host</code> in the <code>docker run</code> command, which shares the host's network stack with the container. The reason we do it this way is because GraphLab Create uses tcp/0 to set its server port. If you remember, that means the system chooses a random high port number, which prevents us from targeting a specific port with an <code>EXPOSE</code> Dockerfile directive (or <code>-p</code> port assignment in the <code>docker run</code> command). Exposing the host's network stack to the container could have some security implications if you run an untrusted application in that container. The applications we're using for this course are okay, it's just something you should be aware of.</p>
<p>Finally, I've been asked to include a tiny Docker crash course in case it's new to you. So our particular <code>run</code> command also mounts the <code>./data/</code> directory into the container at <code>/data</code>! This means you can download notebooks and datasets for the course and put them in that directory, and they'll be accessible in the container under the <code>/data</code> directory. For example, you would use <code>sf = graphlab.SFrame('/data/people-example.csv')</code> to load the sample data. In your terminal, you can use <code>docker logs graphlab</code> to see the container's logs, but don't forget you can swap out <code>-d</code> with <code>-it</code> in your <code>docker run</code> command if you want to create an interactive session for the container so that you can see the output in your terminal. You can also drop into a shell on the running container with <code>docker exec -it graphlab /bin/bash</code> and poke around if you need to. Killing the container happens with <code>docker stop graphlab</code> and deleting the container happens with <code>docker rm graphlab</code>. The <a href="https://docs.docker.com/">Docker documentation</a> is generally well-written, concise, and accurate. The source is also very approachable, as is the Docker community itself! Don't be afraid to drop by #docker on Freenode IRC if you need help!</p>
<p>For your convenience, I have created a <a href="https://github.com/curtiszimmerman/docker-ipython-graphlab-create">Github repository</a> with the Dockerfile and related scripts, as well as the sample starter data provided by Coursera.</p>
<p><strong>Good luck!</strong></p>
</div>]]></content:encoded></item><item><title><![CDATA[Running Crons in Docker with Supervisord]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Recently I've had an interesting conversation in #docker on Freenode with a guy that's been trying to get crons working inside his Docker container. I hadn't yet had a chance to look at that, and so we took off on a late-night debug session exchanging Dockerfiles via Pastebin. He has</p></div>]]></description><link>https://curtisz.com/running-crons-in-docker-with-supervisord-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d7c</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Sat, 26 Mar 2016 09:17:15 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Recently I've had an interesting conversation in #docker on Freenode with a guy that's been trying to get crons working inside his Docker container. I hadn't yet had a chance to look at that, and so we took off on a late-night debug session exchanging Dockerfiles via Pastebin. He has a bunch of other stuff going on, but at the core, he's just running an Apache webserver instance and then wants to run some crons in that container as well. I took his Dockerfile and related scripts, and pared them down to the bare minimum, commenting out everything that wasn't related directly to getting Apache and cron to work. You can take a look at what I came up with:</p>
<pre><code class="language-sh">FROM ubuntu:14.04
MAINTAINER curtisz &lt;software@curtisz.com&gt;

# we install stuff this way to keep it all on one layer
# (which reduces the overall size of our image)
RUN apt-get update -y &amp;&amp; \
	apt-get install -y \
		cron \
		apache2 \
		supervisor &amp;&amp; \
	rm -rf /var/lib/apt/lists/*

# apache stuff
RUN mkdir -p /var/lock/apache2 /var/run/apache2 /etc/supervisor/conf.d/
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_LOCK_DIR /var/lock/apache2
ENV APACHE_PID_FILE /var/run/apache2.pid
# empty out the default index file
RUN echo &quot;&quot; /var/www/html/index.html

# cron job which will run hourly
# (remember that COPY is better than ADD for plain files or directories)
COPY ./crons /etc/cron.hourly/
RUN chmod +x /etc/cron.hourly/crons
# test crons added via crontab
RUN echo &quot;*/1 * * * * uptime &gt;&gt; /var/www/html/index.html&quot; | crontab -
RUN (crontab -l ; echo &quot;*/2 * * * * free &gt;&gt; /var/www/html/index.html&quot;) 2&gt;&amp;1 | crontab -

# supervisord config file
COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf
 
EXPOSE 80
WORKDIR /var/www/html/
CMD /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
</code></pre>
<p>You can tell what's going on here, it's pretty straightforward. To get this working, we need to install apache2, supervisor, and of course, cron. The next few lines are configuration options for Apache. Then finally we get to the test crons. I'm dumping a simple cron shell script into <code>/etc/cron.hourly</code> in the container and making it executable, and then creating two new crons via <code>crontab</code>. <em>Please note</em> how I have added these crons with <code>crontab -</code> by also listing the previously-added crons with <code>crontab -l</code>. If you don't do this, whatever you add via <code>crontab</code> will overwrite whatever's in there now. My crons are just stupid simple crons for dumping something easy into Apache's <code>index.html</code> file so we can prove they're running. My little <code>crons</code> file looks like this:</p>
<pre><code class="language-sh">#!/bin/sh
ps aux | grep apache &gt; /var/www/html/index.html
</code></pre>
<p>Some things to notice about this file... First, it starts with a typical shell script shebang (<code>#!/bin/sh</code>). It should also be executable. Lastly, this file can only contain alphanumeric characters in its filename (and underscores). It <em>cannot</em> contain a dot, which means naming it something like <code>crons.sh</code> won't work.</p>
<p>Next, we're adding the config file for supervisord. Then simply exposing the port, changing our working directory, and then specifying our <code>CMD</code> to start supervisord. Speaking of, this is what our <code>supervisord.conf</code> file looks like:</p>
<pre><code class="language-sh">[supervisord]
nodaemon=true
logfile = /var/log/supervisord.log
logfile_maxbytes = 50MB
logfile_backups=10
 
[program:cron]
autorestart=false
command=cron -f
 
[program:apache2]
autorestart=false
command=/usr/sbin/apache2ctl -D FOREGROUND
</code></pre>
<p>Pretty standard fare. And so let's build our container and start it:</p>
<pre><code class="language-sh">docker build -t=&quot;crontest&quot; .
docker run -it --name=&quot;crontest&quot; -p 8080:80 crontest:latest
</code></pre>
<p>And there you have it! Give your crons a few minutes to execute, then fire up a browser on your localhost and point it to <code>http://localhost:8080/index.html</code>. You should see the output of our test crons there at the tail of the file. Refresh to see more.</p>
<p>That's all there is to it! Previously, I've used the cron system available on the Docker host, which certainly has its benefits. First and foremost of which is not having to use supervisord inside a Docker container. Since Docker is just a fancy way to run a process, you want to avoid loading up your container with a bunch of cruft. It's not a VM! But when you can't avoid it and you really need to run crons alongside your containerized processes, it's no sweat to get it going.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Using a Private Docker v2 Registry with Nginx-Proxy]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Today in #docker on Freenode there was a person with a problem with their v1 Docker registry. I think I jinxed it when I said it was &quot;extremely easy&quot; to get a v2 registry running behind an Nginx proxy. It turned into a nightmare, and I'm sharing the</p></div>]]></description><link>https://curtisz.com/docker-registry-and-nginx-proxy-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d7a</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Fri, 29 Jan 2016 11:48:48 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Today in #docker on Freenode there was a person with a problem with their v1 Docker registry. I think I jinxed it when I said it was &quot;extremely easy&quot; to get a v2 registry running behind an Nginx proxy. It turned into a nightmare, and I'm sharing the design process to help anyone else that might need to debug problems with a similar setup.</p>
<p>So I had <a href="https://curtisz.com/docker-registry-v2-adventure-in-misdirection/">previously</a> spent time getting a private registry to work behind the <a href="https://github.com/jwilder/nginx-proxy">jwilder/nginx-proxy</a> image, which is a great reverse proxy for docker containers. There is a lot of movement with Docker and especially the ecosystem of orchestration applications that exist around it. These days, plenty of stuff out there does what the nginx-proxy image does. Personally I like <a href="https://www.consul.io/">Consul</a> and <a href="https://www.serfdom.io/">Serf</a> from Hashicorp. Incidentally, they also make a hell of a nice application -- <a href="https://www.vaultproject.io/">Vault</a> -- that solves most of the problem with sharing sensitive configuration information. Anyways, for single-host-multiple-container environments, I still prefer proxying with the nginx-proxy image. I use it to front for all my web applications, and our Gitlab installation, so it only made sense to front my v2 registry with the same proxy.</p>
<p>The way the nginx-proxy image works is that it is bound to listen on tcp/80 and tcp/443 and uses the host machine's <code>/var/run/docker.sock</code> to listen for docker events, and then other containers are started with a <code>VIRTUAL_HOST</code> environment variable. When that happens, the <code>docker-gen</code> utility creates an Nginx template and starts routing requests to the container. You can also mount <code>htpasswd</code> files into the proxy and SSL certs to manage authentication and HTTPS connections. It also supports custom directives and custom templates.</p>
<p>So anyways, the problem the guy was having I was pretty sure he wouldn't be able to get support for, because v1 has been officially deprecated on Docker Hub, and also is no longer the primary registry endpoint the Docker client looks for when attempting to connect to a registry. Support for v2 was introduced in 1.6, and as of version 1.9, the client now prefers v2 registry endpoints over v1. So the time has come to upgrade.</p>
<p>Let's get started. First, pull the images we need to work with:</p>
<pre><code class="language-sh">docker pull jwilder/nginx-proxy
docker pull registry:2.2
</code></pre>
<p>It's <strong>important</strong> to note that <code>registry:latest</code> <em>does not</em> point to the latest version of registry. The <code>latest</code> version points to v1! We need to make sure we're pulling v2. The docker registry is actually a part of the <a href="https://github.com/docker/distribution">&quot;distribution&quot;</a> repository. You can and should check for the latest version of both the v2 registry image and also any documentation there. It's generally true that the Docker documentation is mostly very good and correct, so make sure you read that and prefer that information above mine.</p>
<p>To configure the v2 registry, we need to create a minimal <code>config.yml</code> file. I usually keep all my stuff for docker under <code>/var/docker/&lt;container&gt;</code>, so <code>sudo sh -c &quot;mkdir -p /var/docker/registry &amp;&amp; cd /var/docker/registry/ &amp;&amp; vim config.yml&quot;</code> and put this into it:</p>
<pre><code class="language-yml">version: 0.1
log:
    level: info
    formatter: json
    fields:
        service: registry
        environment: staging
        source: registry
http:
    addr: :5000
    host: myregistry.example.com
    secret: biglongsecretwhatever
storage:
    filesystem:
        rootdirectory: /var/lib/registry
</code></pre>
<p>This is the bare minimum you'll need to get your registry going. You should change <code>http.secret</code> to something long and random. For example you can use a bash one-liner like <code>cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c36</code> to get a random string. Now save this yaml file. Finally, <code>mkdir /var/docker/registry/lib</code> to create a directory for storing our registry images on the local host. I like to use AWS S3, and if this interests you, take a look <a href="https://curtisz.com/docker-registry-v2-adventure-in-misdirection/">at my last post</a> on this subject for instructions.</p>
<p>We've got our v2 registry primed to run, but we won't run it quite yet.</p>
<p>Let's get the nginx-proxy image going. First <code>mkdir /var/docker/nginx-proxy &amp;&amp; cd /var/docker/nginx-proxy</code> to get into our base host directory. Use <code>mkdir vhost.d</code> to create a directory for storing our special Nginx directives.</p>
<p>Now, if you want to have nginx-proxy handle your SSL certificates and authentication -- and we do, since docker will complain about a registry running without HTTPS -- you'll want to <code>mdir htpasswd &amp;&amp; mkdir certs.d</code> at this time as well. For illustrative purposes, let's say our registry domain is <code>myregistry.example.com</code> and we've already pointed DNS at the host. So we'll name our SSL certificate to <code>myregistry.example.com.crt</code> and our key to <code>myregistry.example.com.key</code> and drop both of those files into the <code>/var/docker/nginx-proxy/certs.d</code> directory.</p>
<p>With HTTP authentication, we can just do some real basic stuff. We don't need anything fancy, since the client supports basic authentication. You can use <code>htpasswd</code> (from the <code>apache2-utils</code> repo on Linux Mint or Ubuntu) to generate authentication information. Save this information in <code>/var/docker/nginx-proxy/htpasswd/myregistry.example.com</code> similar to the way we named our SSL data. For future reference, you can also store a default certificate and key for HTTPS requests that arrive at your nginx-proxy which aren't routable to one of your containers.</p>
<p>We need a couple more files dropped into <code>/var/docker/nginx-proxy/vhost.d</code>. The first is <code>myregistry.example.com</code> and looks like this:</p>
<pre><code class="language-sh">client_max_body_size 0;
chunked_transfer_encoding on;
 
location /v2/ {
  # Do not allow connections from docker 1.5 and earlier
  # docker pre-1.6.0 did not properly set the user agent on ping, catch &quot;Go *&quot; user agents
  if ($http_user_agent ~ &quot;^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$&quot; ) {
    return 404;
  }
 
  add_header Docker-Distribution-Api-Version &quot;registry/2.0&quot;;
  #more_set_headers     'Content-Type: application/json; charset=utf-8';
  include               vhost.d/docker-registry.conf;
}
 
location /v1/_ping {
  auth_basic off;
  include               vhost.d/docker-registry.conf;
  add_header X-Ping     &quot;inside /v1/_ping&quot;;
  add_header X-Ping     &quot;INSIDE /v1/_ping&quot;;
}
 
location /v1/users {
  auth_basic off;
  include               vhost.d/docker-registry.conf;
  add_header X-Users    &quot;inside /v1/users&quot;;
  add_header X-Users    &quot;INSIDE /v1/users&quot;;
}
</code></pre>
<p>These directives do a couple of things. First, they lift the artificial limit Nginx places on request size. Since you're going to be uploading huge files to your v2 registry, we need to turn off that limitation. This directive also prevents access by client versions 1.5 and below (which only use v1 registry endpoints anyway). Of particular interest is that the location directives for v1 endpoints have been proven to fix docker client bugs which caused the v2 registry to throw a 404 when trying to connect.</p>
<p>The second file is <code>/var/docker/nginx-proxy/vhost.d/docker-registry.conf</code> and looks like this:</p>
<pre><code class="language-sh">proxy_pass                          http://myregistry.mexample.com;
proxy_set_header  Host              $http_host;   # required for docker client's sake
proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header  X-Forwarded-Proto $scheme;
proxy_read_timeout                  900;
</code></pre>
<p>This forwards the IP address of your clients to the registry for logging purposes, as well as providing a couple of required headers.</p>
<p>Now that we've configured our v2 registry and nginx-proxy, let's start them up! We'll begin with the nginx-proxy container (shoutout to <a href="https://twitter.com/arthbarros">Arthur</a> for noticing I'd forgotten to mount <code>vhost.d</code> in this next command):</p>
<pre><code class="language-sh">docker run -d \
  --name &quot;nginx-proxy&quot; \
  --restart &quot;always&quot; \
  -p 80:80 \
  -p 443:443 \
  -v /var/docker/nginx-proxy/certs:/etc/nginx/certs:ro \
  -v /var/docker/nginx-proxy/htpasswd:/etc/nginx/htpasswd:ro \
  -v /var/docker/nginx-proxy/vhost.d:/etc/nginx/vhost.d \
  -v /var/run/docker.sock:/tmp/docker.sock \
  jwilder/nginx-proxy
</code></pre>
<p>Next, let's start our v2 registry container:</p>
<pre><code class="language-sh">docker run -d \
  --name=&quot;registry&quot; \
  --restart=&quot;always&quot; \
  -v &quot;/var/docker/registry/config/config.yml:/etc/docker/registry/config.yml&quot; \
  -v &quot;/var/docker/registry/lib:/var/lib/registry&quot; \
  -e &quot;VIRTUAL_HOST=myregistry.example.com&quot; \
  registry:2.2
</code></pre>
<p>Notice here that we're not binding our v2 registry port to any port on the host. This is because we don't want to bind that port to the host interface. We want to &quot;bind&quot; that port to the nginx-proxy container. You may be wondering how the nginx-proxy container knows that it should route inbound HTTPS requests to port 5000 on the registry container. The answer is that it binds to whatever port your container has exposed, either via <code>EXPOSE</code> in the Dockerfile or <code>--expose</code> in the <code>docker run</code> command. In our example, the v2 registry image has <code>EXPOSE 5000</code> in its Dockerfile.</p>
<p>This brings me to one last pain point I had with the nginx-proxy image. As wonderful as it is, it's not clear that you cannot proxy anything that isn't based on the HTTP protocol. So for example you can't have the nginx-proxy image front for your SMTP server or your FTP server. Also, something else that took me a while to understand... Let's say you want to run a <a href="https://github.com/google/cadvisor">cAdvisor</a> container to expose some metrics for your <a href="http://prometheus.io">Prometheus</a> server. The documentation wants you to make that thing listen on tcp/8080. That's totally fine, but if you want to run this container behind your nginx-proxy container, you'll want to <em>not</em> bind that port to the host. And since it's exposed port 8080 in its Dockerfile, you can simply start it with <code>-e &quot;VIRTUAL_HOST=cadvisor.example.com&quot;</code> and it will then be available at <code>https://cadvisor.example.com/</code>, which will be served from behind your nginx-proxy. The nginx-proxy container gets an inbound HTTPS request on tcp/443, then routes the connection on the backend to tcp/8080 on your cAdvisor container. No <code>--link</code> necessary!</p>
<p>A piece of advice when debugging problems: Start with <code>docker logs nginx-proxy</code> and check if the problem is with your container or if it's with your nginx-proxy. Also you can use <code>docker exec -it nginx-proxy /bin/bash</code> to drop into a shell on your nginx-proxy container and poke around. You should check the <code>/etc/nginx/conf.d/default.conf</code> to make sure your container is being properly exposed to the nginx-proxy and its docker-gen utility.</p>
<p>Good luck!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Publish/Subscribe: The Five Ws (and of course, the How)]]></title><description><![CDATA[<div class="kg-card-markdown"><p>A friend of mine recently asked me about the publish/subscribe (&quot;pubsub&quot;) programming pattern. As this is something I use in almost every project, I thought I'd be able to find a decent tutorial for him online. Something that would be helpful to someone familiar with programming, but</p></div>]]></description><link>https://curtisz.com/publishsubscribe-the-five-ws-and-of-course-the-how-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d79</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Sun, 15 Nov 2015 02:11:47 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>A friend of mine recently asked me about the publish/subscribe (&quot;pubsub&quot;) programming pattern. As this is something I use in almost every project, I thought I'd be able to find a decent tutorial for him online. Something that would be helpful to someone familiar with programming, but not familiar with this pattern. As it happens, most of the pubsub documentation or tutorials out there are specific to their use in one situation: APIs. That's all fine and dandy, but the pubsub pattern is so much more powerful and applicable in so many more situations than that niche. I am pretty sure it's my favorite programming pattern of them all. Not so hard to believe once you know that I write JavaScript for a living. But, I think JavaScript programmers are more sensitive to the applicability of the pubsub pattern than programmers in other languages, because JavaScript is asynchronous.</p>
<p>So what exactly does that mean?</p>
<p>If you've ever written any JavaScript, you have probably gotten yourself in trouble with its asynchronous nature. For example, you've probably thought about doing something like this:</p>
<pre><code class="language-js">// read data from a file ...
var data = readDataFromFile('/path/to/file.txt');
// ... then do something with the data
console.log('your name is: ' + data.username);
</code></pre>
<p>But you can't do that. JavaScript's asynchronous nature means the (totally made-up) function <code>readDataFromFile('/path/to/file.txt')</code> is not executed immediately. Instead it is tossed onto the event queue and executed later, <em>after</em> the <code>console.log('your name is: ' + data.username);</code> is executed. This happens because all the code in the current scope is executed in a batch, and then JavaScript grabs the next thing on the event queue and executes everything in <em>that</em> function's scope, and so on. &quot;External&quot; function calls get put onto the event queue as they are encountered, instead of executed, as they are in synchronous languages like Python or C. You could write a lot of JavaScript before you encounter this behavior, though. JavaScript is pretty nebulous about <em>when</em> functions get put on the event queue versus executed directly. Usually you trigger the event queue when making calls out to external resources, for example via AJAX or file I/O. Well, in order to work around this limitation in JavaScript, we use callbacks. Callbacks are functions that we pass as parameters to other functions that get executed after all the processing inside the first function is done. That sounds stupid, so let me illustrate. Here's an example of proper asynchronous JavaScript using callbacks:</p>
<pre><code class="language-js">// read data from a file ...
fs.readFile('/path/to/file.txt', function( err, data ) {
    //                           ^^^^^^^^^^^^^^^^^^^^^
	// ... then do something with the data *in a callback*
	if (err) {
    	throw new Error('problem doing stuff: ' + err);
    }
    console.log('your name is: ' + data.username);
});
</code></pre>
<p>Now you're probably asking yourself why I'm even talking about asynchronous programming in JavaScript. What does that have to do with the pubsub pattern? Well, after a while, you fall into what's called &quot;callback hell&quot; when you chain callbacks with callbacks with callbacks. Take a look at what should be a simple operation: Reading from a file, making a change to the data, then writing those changes to a file:</p>
<pre><code class="language-js">var filename = '/path/to/file.txt';
var savefile = '/path/to/new/file.txt';

fs.stat(filename, function(err, stat) {
	// inside first callback
	if (err) throw new Error('could not stat file: '+err);
    if (!stat.isFile()) throw new Error('path is not a file!');
    
    fs.readFile(filename, function(err, data) {
    	// inside second callback
    	if (err) throw new Error('could not read file: '+err);
        
        modifyUsername(data.username, function(name) {
        	// inside third callback
            
        	fs.stat(savefile, function(err, stat) {
            	// inside fourth callback
            	if (err) throw new Error('could not stat file: '+err);
                if (!stat.isFile()) throw new Error('path is not a file!');
                
                fs.writeFile(savefile, name, function(err) {
                	// i want to kill myself ..............
                    // and look at all the close brackets and parentheses!
                    // how embarrassing!
                });
            });
        });
    });
});
// yikes. are you sure you closed up all your functions properly?
</code></pre>
<p>&quot;Holy shit. JavaScript <em>sucks</em>!&quot; they will say. And so. Even the most faithful will pause to think.</p>
<p>Now, if only there were a way to write a blob of callback code, and then create a &quot;trigger&quot; that we could pull when we were ready to execute the callback blob, we could clean this mess right up. ... Well, that's right! You've figured out that my beloved pubsub can swoop in and save the day.</p>
<p>Here is what super-simplified pubsub calls look like:</p>
<pre><code class="language-js">// when you have a blob of code you want to run later:
subscribe(  &quot;this-can-be-any-string&quot;,    functionToCall    );
// then when you're ready to execute the blob of code:
publish(  &quot;this-can-be-any-string&quot;,   [  array, of, parameters  ]);
</code></pre>
<p>The first parameter to the <code>subscribe()</code> function is a string that we use to &quot;index&quot; the function we want to call. We store the function using this string as a label. This is so that later, when we call the <code>publish()</code> function, we reference the function we want to execute using the same string (<code>&quot;this-can-be-any-string&quot;</code>) and then the second parameter passed to the <code>publish()</code> function is an array of arguments to pass to the function we will execute. In the above two lines, the result of the code ends up logically looking like this:</p>
<pre><code class="language-js">functionToCall(array, of, parameters);
</code></pre>
<p>Take a look at the pubsub object. It's very easy to read:</p>
<pre><code class="language-js">// here is our pubsub object
// this is how we enabled the pattern
var $pubsub = (function() {
	var cache = {};
    function _flush() { cache = {}; }
	function _pub( topic, args, scope ) {
    	if (cache[topic]) {
        	var current = cache[topic];
            for (var i=0; i&lt;current.length; i++) {
				current[i].apply(scope || this, args || []);
            }
        }
    }
    function _sub( topic, callback ) {
    	if (!cache[topic]) {
        	cache[topic] = [];
		}
        cache[topic].push(callback);
    }
	return {
    	flush: _flush,
    	pub: _pub,
    	sub: _sub
    };
})();
</code></pre>
<p>Now take a look at the refactored code, which uses pubsub to escape callback hell by &quot;subscribing&quot; some functions to events which we later &quot;publish&quot;:</p>
<pre><code class="language-js">// convenience function to DRY up our calls to fs.stat()
var fileStat = function( filename, callback ) {
	fs.stat(filename, function(err, stat) {
    	if (err) throw new Error('could not stat file: '+err);
        if (!stat.isFile()) throw new Error('path is not a file!');
        typeof(callback) === 'function' &amp;&amp; callback();
    });
};
// now begins our list of discrete functions to execute in a specific order
// (note the $pubsub.pub() calls within each function!)
var fileRead = function( filename ) {
	fileStat(filename, function() {
        fs.readFile(filename, function(err, data) {
        	if (err) throw new Error('could not read file: '+err);
            $pubsub.pub('/username/modify', [data]);
        });
    });
};
var fileWrite = function( filename, data ) {
	fileStat(filename, function() {
        fs.writeFile(filename, data, function(err) {
        	if (err) throw new Error('could not write file: '+err);
            $pubsub.pub('/continue/process');
        });
    });
};
var modifyUsername = function( username ) {
	var newUsername = username + '_modified';
	$pubsub.pub('/file/write', ['/path/to/new/file.txt', newUsername]);
};
var moreStuff = function() {
	// do more stuff after everything else
};

// set up our subscriptions
$pubsub.sub('/file/read', fileRead);
$pubsub.sub('/file/write', fileWrite);
$pubsub.sub('/username/modify', modifyUsername);
$pubsub.sub('/continue/process', moreStuff);

// kick off the whole process with this &quot;publish&quot; statement
$pubsub.pub('/file/read', ['/path/to/file.txt']);

</code></pre>
<p>This looks a lot nicer, doesn't it? It's a bit more typing, but once you grok what's happening here in this post, you will never want to go back to that awful callback hell. So I've spent the last few minutes answering the 5 Ws of the publish/subscribe pattern by showing you how to do it. If you're not totally clear on what's happening here, start reading from the top, and hand-copy the code into your IDE. Taking a closer look by hand-copying the code is something that always helps things sink in. Before you know it, you'll grok the pubsub pattern and be using it to hit all kinds of nails.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Meet DOSBox, the kickass... Debugger?]]></title><description><![CDATA[<div class="kg-card-markdown"><p>One of the members of an <a href="https://en.wikipedia.org/wiki/Alternate_reality_game">ARG</a> I play recently started talking about an old piece of equipment he'd purchased, which supposedly had been used by phone repair technicians to do their work. The equipment in question is an Itronix T5000, which has an in-built modem, speedy 486 processor, and</p></div>]]></description><link>https://curtisz.com/reversing-with-dosbox-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d77</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Tue, 07 Jul 2015 22:04:06 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>One of the members of an <a href="https://en.wikipedia.org/wiki/Alternate_reality_game">ARG</a> I play recently started talking about an old piece of equipment he'd purchased, which supposedly had been used by phone repair technicians to do their work. The equipment in question is an Itronix T5000, which has an in-built modem, speedy 486 processor, and 640KB of RAM. Kilobytes, folks. This was the 90s. You know, incidentally, I fondly remember having 640KB of RAM in my very first computer, and having to juggle peripherals.</p>
<p>Anyways, unfortunately for our friend, when he powered the device on, this is what he saw:</p>
<p><img src="http://i.imgur.com/0pQhNkI.jpg" alt=""></p>
<p>Our friend <a href="http://mrargent.tumblr.com/">Mister Argent</a> managed to offload all of the device's files to USB using an available restore feature. He just didn't know how to proceed. Fortunately for him, when it comes to binaries from the 90s, I'm your guy. Now, it's been a few years since my last encounter with something of this nature, and I hadn't realized that most of the tools we used to use for reversing no longer function on today's platforms. A simple <code>strings</code> of the files gave me nothing useful. Interesting, sure, but nothing useful to solve our primary predicament. There is a <code>PASSWORD.DAT</code> file in the collection but it's clearly not plaintext and here specifically, I am nothing more than a hobbyist, and definitely no cypherpunk. I would need to reverse this binary to get anywhere. It didn't take me long, however, to remember <a href="https://en.wikipedia.org/wiki/Master_of_Orion">the only game I spend time playing these days</a> -- which also happens to be a binary from the 90s -- and more importantly, the platform I use to play it: <a href="http://www.dosbox.com/">DOSBox</a>.</p>
<p>If you've never heard of DOSBox, it is basically what you're thinking it is after my description above: An emulator for DOS applications. The thing about DOSBox that makes it special -- besides being the key to many glorious, wonderful games from the 90s that you couldn't otherwise play -- is that the creator has built in a very useful debugger.</p>
<p>I'm a die-hard Linux Mint user, since I can't stand Ubuntu's Unity UI almost as much as I can't stand Michael Shuttleworth. One of the nice things about Linux Mint -- besides the fact that it hasn't immediately jumped into the systemd assimilation chamber -- is that it uses Ubuntu as a base, and therefore has its repositories available for consumption. DOSBox is available in Ubuntu's default repository (and probably in other default distro repos), but if you want to use the debugger, you've got to compile it with a special option, which means building from source. On LM/Ubuntu, you're going to need a few things in order to compile. If you're adventurous, you probably already have build-essential, autoconf, and automake. If not:</p>
<pre><code class="language-sh">sudo apt-get install build-essential autoconf automake
</code></pre>
<p>Either way, you're going to need to get the DOSBox dependencies:</p>
<pre><code class="language-sh">sudo apt-get build-dep dosbox
</code></pre>
<p>When you're compiling DOSBox for its debugger, you need a curses library. You'll need to install one to continue. Thanks to <a href="http://stackoverflow.com/questions/14416487/gcc-usr-bin-ld-error-cannot-find-lncurses">this answer</a> on Stack Overflow, resolving this on Linux Mint/Ubuntu is a cinch:</p>
<pre><code class="language-sh">sudo apt-get install lib32ncurses5-dev
</code></pre>
<p>Next, download the source and extract the source (sorry for the SourceForge link):</p>
<pre><code class="language-sh">wget &quot;http://downloads.sourceforge.net/project/dosbox/dosbox/0.74/dosbox-0.74.tar.gz&quot;
tar -xvf dosbox-0.74.tar.gz
</code></pre>
<p>Next we'll build our awesome DOSBox debugger, but something you should know here is that DOSBox actually comes with two &quot;levels&quot; of debugging capability. Compiling with <code>--enable-debug</code> will get you most of the debugging features, but there are a few important ones you'll want the convenience of by compiling with <code>--enable-debug=heavy</code>. Most importantly, this &quot;heavy&quot; debugger enables the <code>heavycpu</code> command, which is a hardcore CPU logger that makes following code a lot easier:</p>
<pre><code class="language-sh">cd dosbox-0.74
./autogen.sh
</code></pre>
<p>At this point, we're going to need to modify the source a little bit to prevent some errors in the actual compilation. Thanks to <a href="http://www.vogons.org/viewtopic.php?t=36074">this helpful post</a> by the DOSBox author, we know exactly what we need to change in the source to prevent the error. Let's create a little patch file to do the work for us. Create a new file in the <code>dosbox-0.74</code> directory:</p>
<pre><code class="language-sh">vim ./dosbox-0.74.patch
</code></pre>
<p>Paste the following contents into the editor:</p>
<pre><code class="language-sh">diff -rupN dosbox-0.74/include/dos_inc.h dosbox-0.74.patched/include/dos_inc.h
--- dosbox-0.74/include/dos_inc.h	2010-05-10 10:43:54.000000000 -0700
+++ dosbox-0.74.patched/include/dos_inc.h	2015-07-07 14:52:42.057078234 -0700
@@ -28,6 +28,8 @@
 #include &quot;mem.h&quot;
 #endif
 
+#include &lt;stddef.h&gt;
+
 #ifdef _MSC_VER
 #pragma pack (1)
 #endif
diff -rupN dosbox-0.74/src/cpu/cpu.cpp dosbox-0.74.patched/src/cpu/cpu.cpp
--- dosbox-0.74/src/cpu/cpu.cpp	2010-05-12 02:57:31.000000000 -0700
+++ dosbox-0.74.patched/src/cpu/cpu.cpp	2015-07-07 14:52:23.641077942 -0700
@@ -30,6 +30,7 @@
 #include &quot;paging.h&quot;
 #include &quot;lazyflags.h&quot;
 #include &quot;support.h&quot;
+#include &lt;stddef.h&gt;
 
 Bitu DEBUG_EnableDebugger(void);
 extern void GFX_SetTitle(Bit32s cycles ,Bits frameskip,bool paused);
diff -rupN dosbox-0.74/src/dos/dos.cpp dosbox-0.74.patched/src/dos/dos.cpp
--- dosbox-0.74/src/dos/dos.cpp	2010-05-10 10:43:54.000000000 -0700
+++ dosbox-0.74.patched/src/dos/dos.cpp	2015-07-07 14:52:11.929077757 -0700
@@ -31,6 +31,7 @@
 #include &quot;setup.h&quot;
 #include &quot;support.h&quot;
 #include &quot;serialport.h&quot;
+#include &lt;stddef.h&gt;
 
 DOS_Block dos;
 DOS_InfoBlock dos_infoblock;
diff -rupN dosbox-0.74/src/ints/ems.cpp dosbox-0.74.patched/src/ints/ems.cpp
--- dosbox-0.74/src/ints/ems.cpp	2010-05-10 10:43:54.000000000 -0700
+++ dosbox-0.74.patched/src/ints/ems.cpp	2015-07-07 14:51:59.081077554 -0700
@@ -32,6 +32,7 @@
 #include &quot;setup.h&quot;
 #include &quot;support.h&quot;
 #include &quot;cpu.h&quot;
+#include &lt;stddef.h&gt;
 
 #define EMM_PAGEFRAME	0xE000
 #define EMM_PAGEFRAME4K	((EMM_PAGEFRAME*16)/4096)
</code></pre>
<p>Now save the file and exit, and apply the patch you have just created:</p>
<pre><code class="language-sh">patch -p1 &lt; ./dosbox-0.74.patch
</code></pre>
<p>Finally, compile DOSBox:</p>
<pre><code class="language-sh">./configure --enable-debug=heavy
make
</code></pre>
<p>I've already got DOSBox installed, and so I chose not to install over it with my debugger-enabled version. But if you don't care, go ahead and place your newly-built debugging DOSBox version into your executables directory:</p>
<pre><code class="language-sh">sudo make install
</code></pre>
<p>Awesome! We've got a bitchin' debugger! The second part of this story will cover the password discovery process using our fresh-from-source DOSBox debugger.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Docker Registry v2: Adventures in Ambiguity]]></title><description><![CDATA[<div class="kg-card-markdown"><p>All I need is a private Docker registry that I can host myself.</p>
<p>If you're anything like me, you've been excitedly awaiting the release of the <a href="https://docs.docker.com/registry/">v2.0 Docker Registry</a>. Version 1 was not very good. The company behind Docker is in no hurry to bite the hand that feeds</p></div>]]></description><link>https://curtisz.com/docker-registry-v2-adventure-in-misdirection-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d74</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Tue, 28 Apr 2015 04:24:36 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>All I need is a private Docker registry that I can host myself.</p>
<p>If you're anything like me, you've been excitedly awaiting the release of the <a href="https://docs.docker.com/registry/">v2.0 Docker Registry</a>. Version 1 was not very good. The company behind Docker is in no hurry to bite the hand that feeds them, and so development of the registry has been spotty at best. Among other things, the documentation is not great and the registry has no built-in authentication protocol. I understand that it's much better for business to get people frustrated with setting up their own private registry and then point them at your <a href="https://hub.docker.com">hosted services</a>, where it's very easy to write a check to have someone else take care of this mess for you. But I am not in the habit of writing checks, and my check would probably bounce anyways.</p>
<p>The <a href="https://github.com/docker/distribution/blob/master/docs/deploying.md">documentation available</a> for deploying a v2 registry is specific to one situation. It is a set of instructions for using Compose (yet another Docker technology with a seemingly nebulous purpose at this time) to get both a v1 and a v2 registry working behind an Nginx proxy. But as I am using the <a href="https://github.com/jwilder/nginx-proxy">Nginx Docker reverse proxy</a> by Jason Wilder, so I don't need to bring in an external Nginx server. Nor do I need to answer requests for a v1 registry, as I am not using any Docker clients earlier than version 1.6.0.</p>
<p>All I need is a private Docker registry that I can host myself.</p>
<p>So... What we need to do is rip out all the extra stuff so that we're left with what we need. We don't need a v1 registry, so ignore all that. We're using jwilder/nginx-proxy to proxy our inbound requests, so ignore the instructions about pulling in the Nginx server. The average docker user right now doesn't really know what Compose is for or what it does -- though it will reduce complexity for most of us some day in the future -- so just ignore all the cruft about Compose. We're left with something close to what we're looking for.</p>
<p>First, clone the Distribution repository from Github and change into that directory:</p>
<p><code>git clone https://github.com/docker/distribution &amp;&amp; cd distribution</code></p>
<p>We'll build our registry server from this repository:</p>
<p><code>docker build -t=registryv2 .</code></p>
<p>Now our container will build and should be listed in the output of <code>docker images</code>. We're almost there, but first we need to configure the registry. The future may include something I've talked about before called <a href="https://github.com/docker/docker/issues/10310">the docker vault</a>, which is a cryptographically-secure, in-container, ephemeral storage mechanism which holds our sensitive configuration data. But for the moment, we don't have access to the vault because it doesn't exist. Here, we're going to have to rely on storing configuration data in a file, and then mount a volume on the host which exposes our config file to the container.</p>
<p>The v2 registry currently reads configuration data from <code>cmd/registry/config.yml</code>, so we need to map a directory on the host to this directory in the container. If you're trying to configure a v2 registry, just totally forget about everything related to configuring a v1 registry. The new configuration options are <a href="https://docs.docker.com/registry/configuration/">in the documentation</a>. I'll include my own sanitized <strong>debug mode</strong> configuration file so you've got a sanity check reference:</p>
<pre><code>version 0.1
log:
    level: debug
    fields:
        service: registry
        environment: staging
storage:
    filesystem:
        rootdirectory: /registry
    cache:
        layerinfo: inmemory
http:
    addr: :5000
    secret: somerandomstring
    debug:
        addr: localhost:5001
</code></pre>
<p>This is all pretty self-explanatory, if you've played with running any version of a docker registry. I'm running this on tcp/5000 during development for testing purposes. We are also running the server to listen on tcp/5001 (on local loopback only) if we need to connect and get some verbose debug information.</p>
<p>Now for <strong>production mode</strong> configuration, I use a different setup, which is more like something you'd expect to see out there in the real world:</p>
<pre><code>version 0.1
log:
    level: info
    fields:
        service: registry
        environment: staging
storage:
    s3:
        accesskey: AKIA0Z6307DRPWJ5VH03F
        secretkey: OgP2Yhk1ZjFFf+aYokvnqI3qTlenCxSW2nbb9zpB
        region: us-west-1
        bucket: example.com-docker-registry-v2
        encrypt: false
        secure: true
        v4auth: true
        chunksize: 5242880
        rootdirectory: /registryv2
    cache:
        layerinfo: redis
http:
    addr: :443
    secret: ZpAedwVDFHK7mkNFFKSP8OQY
    debug:
        addr: localhost:5001
redis:
    addr: localhost:6379
    db: 0
</code></pre>
<p>Here, we're setting our registry to use an AWS S3 backend (that configuration data is of course dummy data, but feel free to try it, leet). We're also using a Redis container for caching, which speeds things up considerably. Again, the jwilder/nginx-proxy container auto-detects which port is exposed on a container, and I want this registry to listen on HTTPS (tcp/443), so I've changed its listen port appropriately.</p>
<p>So, because our registry is going to use Redis for caching, we need to spin up a Redis instance and link it to our registry container. Real quick, let's pull that Redis image:</p>
<p><code>docker pull sameersbn/redis</code></p>
<p>Now run it:</p>
<p><code>docker run -d --name=&quot;registryv2-redis&quot; --restart=&quot;always&quot; sameersbn/redis</code></p>
<p>And when I run my registry container, it looks something like this:</p>
<pre><code>#!/bin/bash
docker run -d \
  --name=&quot;registryv2&quot; \
  --restart=&quot;always&quot; \
  --link registryv2-redis:redis \
  -v /var/docker/registryv2/config:/go/src/github.com/docker/distribution/cmd/registry \
  registryv2
</code></pre>
<p>Finally, it's not clear in the documentation, but to create a repository on your new v2 registry, you've got to tag your images correctly. Let's suppose for a second you have just created an image called <code>myContainer</code> and you'd like to create that on your new v2 registry:</p>
<p><code>docker tag myContainer:latest registry.example.com:443/myContainer:latest</code></p>
<p>This command tags your container not only with the latest tag, but also specifies exactly which registry you want to use for your new repository. Now you can push this image to your new v2 registry. Enjoy!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Sensitive Configuration Data: fs.readFile() or process.env()]]></title><description><![CDATA[<div class="kg-card-markdown"><p>The thoughtful engineering behind the <a href="http://12factor.net/config">Twelve-Factor App</a> design process has pushed environment variables as the place for sensitive configuration data such that it's somewhat of a standard these days. I have spent a couple of years now using environment variables to store sensitive configuration data in order to compare it</p></div>]]></description><link>https://curtisz.com/sensitive-configuration-data-fs-readfile-or-process-env-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d73</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Sat, 25 Apr 2015 10:05:47 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>The thoughtful engineering behind the <a href="http://12factor.net/config">Twelve-Factor App</a> design process has pushed environment variables as the place for sensitive configuration data such that it's somewhat of a standard these days. I have spent a couple of years now using environment variables to store sensitive configuration data in order to compare it to using time-tested configuration files. I think finally I have decided that configuration files are the way forward.</p>
<p><a href="https://gist.github.com/telent/9742059">This gist</a> summarizes fairly well the pros and cons of using environment variables. If you aren't familiar with the argument, or haven't given it much thought, I recommend you take a minute to read it over.</p>
<p>When I first heard of using environment variables for sensitive data, I was intrigued, but also initially skeptical. I've been doing this stuff for years and years. If it ain't broke, don't fix it, <em>but</em>... I'm always in the market for better solutions, so I didn't want to reject it forthright. I wanted to give it time, really roll the concepts around in my head (and my infrastructure) before making the call.</p>
<p>A lot of the opposition to config files comes from arguing that a deployment should scrub the sensitive data out of the environment once it's finished using it. However, in my experience environment variables are unintentionally &quot;leakier&quot; than files on the filesystem. Even if you scrub the environment, this data could live on in process lists and temporary files and other little nooks and crannies which are hard (or impossible) to get to, especially for the average person deploying things. With files, there's no question where your sensitive data lives, and this makes it easier to handle and to quarantine, even if only conceptually.</p>
<p>My specific situation is with loading docker containers with the sensitive data, and there's been some intriguing discussion among core devs about a kind of &quot;docker vault&quot; in #docker-dev on IRC. The mechanic they want to invent is a cryptographically-secure, ephemeral storage area where sensitive configuration data can be injected into a container, used, then destroyed. This will solve a lot of the issues associated traditionally with this specific problem. However until the time that arrives, I'm sticking with configuration files.</p>
</div>]]></content:encoded></item><item><title><![CDATA[An Excellent Talk]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Hello there! This talk was posted on HN the other day, and I think it's excellent enough to post here.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/csyL9EC0S0c" frameborder="0" allowfullscreen></iframe></div>]]></description><link>https://curtisz.com/an-excellent-talk-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d72</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Mon, 16 Mar 2015 01:37:30 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Hello there! This talk was posted on HN the other day, and I think it's excellent enough to post here.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/csyL9EC0S0c" frameborder="0" allowfullscreen></iframe></div>]]></content:encoded></item><item><title><![CDATA[Skill Progression]]></title><description><![CDATA[<div class="kg-card-markdown"><p>There's a <a href="http://www.marcdalessio.com/self-portraits-over-the-years-2/">fascinating post</a> I read recently that talks about skill progression, and how it relates to your perception of your own skill level, your frustration, your perception of the skill itself, and your actual abilities in a skill. I could talk on and on about how this relates to</p></div>]]></description><link>https://curtisz.com/skill-progression-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d70</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Tue, 17 Feb 2015 07:22:21 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>There's a <a href="http://www.marcdalessio.com/self-portraits-over-the-years-2/">fascinating post</a> I read recently that talks about skill progression, and how it relates to your perception of your own skill level, your frustration, your perception of the skill itself, and your actual abilities in a skill. I could talk on and on about how this relates to this skill, or that, and how to maximize the upward trend. But I won't do nearly as good a job explaining as this image from the original post:</p>
<p><img src="https://curtisz.com/content/images/2015/02/improvement2.jpg" alt=""></p>
<p>Isn't that amazing insight? You can abstract this graph to represent virtually any technical skill you can think of. Replace the word &quot;painting&quot; with &quot;programming&quot; or &quot;playing chess&quot;. Almost any technical skill fits this progression model.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Title II]]></title><description><![CDATA[<div class="kg-card-markdown"><p>I am still in a state of shock over <a href="http://www.wired.com/2015/02/fcc-chairman-wheeler-net-neutrality/">Tom Wheeler's comments</a> over at Wired. It looks like he's going to recommend essentially what we all thought was impossible: Title II regulations for broadband providers, <em>including</em> mobile broadband providers!</p>
<p>At first blush, the situation seems dire. Tom Wheeler was a</p></div>]]></description><link>https://curtisz.com/title-ii-2-2/</link><guid isPermaLink="false">59f6d16ce75f620001909d6f</guid><dc:creator><![CDATA[Curtis Z]]></dc:creator><pubDate>Thu, 05 Feb 2015 00:23:34 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>I am still in a state of shock over <a href="http://www.wired.com/2015/02/fcc-chairman-wheeler-net-neutrality/">Tom Wheeler's comments</a> over at Wired. It looks like he's going to recommend essentially what we all thought was impossible: Title II regulations for broadband providers, <em>including</em> mobile broadband providers!</p>
<p>At first blush, the situation seems dire. Tom Wheeler was a top cable industry lobbyist before he took the position of chairman of the FCC. It doesn't take a lot of explaining for anyone to see that this is a pretty clear conflict of interest. It's as insane as Dick Cheney having to quit his job as CEO of Halliburton in order to become the Vice President of the United States. It shouldn't surprise anyone that the next eight years were full of absurd government spending on no-bid contracts given to -- naturally -- Halliburton. So it seemed the future of the internet would be like a Comedy Central show whose humor is based solely on schadenfreude.</p>
<p>However... Something happened. I'm intensely curious about the reason Chairman Wheeler decided to bring Title II to broadband. Because whatever it is, it's miraculous. He's almost certainly slammed the revolving door shut on himself. He's single-handedly responsible for recommending Title II for broadband providers, and for him to include mobile providers (i.e. cellphone network providers) as covered under Title II regulations is a huge blow to the zero-ethics, sociopath entities like Time Warner Cable, Comcast, and Verizon, who fight so hard to screw consumers for whatever profit they can manage.</p>
<p>It almost seems too good to be true. We aren't given too many things in our lifetime that are &quot;too good to be true&quot;. I'm still cautious and thinking about what this could mean. I want to take it at face value and rejoice in what seems to be profound evidence that people in our government still care about protecting its citizen consumers. We'll just have to wait and see. We've still go to get this recommendation past the rest of the committee (which seems likely, despite there almost certainly being dissent among some members). The vote is February 26th. There are also some very smart net neutrality folks looking into the future at the possibility that Congressional Republicans will try to revoke the Telecommunications Act, thereby skirting the FCC as an entity with the power to create and enforce these kinds of regulation.</p>
<p>For the moment, however, the future looks bright.</p>
</div>]]></content:encoded></item></channel></rss>