<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Service on Jason Wilder&#39;s Blog</title>
        <generator uri="https://gohugo.io">Hugo</generator>
        <link>http://jasonwilder.com/categories/service/</link>
        
        <language>en-us</language>  
        <updated>Tue, 25 Mar 2014 00:00:00 UTC</updated>
        
        <item>
            <title>Automated Nginx Reverse Proxy for Docker</title>
            <link>http://jasonwilder.com/blog/2014/03/25/automated-nginx-reverse-proxy-for-docker/</link>
            <pubDate>Tue, 25 Mar 2014 00:00:00 UTC</pubDate>
            
            <guid>http://jasonwilder.com/blog/2014/03/25/automated-nginx-reverse-proxy-for-docker/</guid>
            <description>

&lt;p&gt;A reverse proxy server is a server that typically sits in front of other web servers in order to provide additional functionality that the web servers may not provide themselves.&lt;/p&gt;

&lt;p&gt;For example, a reverse proxy can provide SSL termination, load balancing, request routing, caching, compression or even A/B testing.&lt;/p&gt;

&lt;p&gt;When running web services in docker containers, it can be useful to run a reverse proxy in front of the containers to simplify depoyment.&lt;/p&gt;

&lt;h2 id=&#34;why-use-a-reverse-proxy-with-docker:068079669506d19d9ac0100aa8bfeb4c&#34;&gt;Why Use A Reverse Proxy With Docker&lt;/h2&gt;

&lt;p&gt;Docker containers are assigned random IPs and ports which makes
addressing them much more complicated from a client perspsective. By default, the IPs and ports are private to the host and cannot be accessed externally unless they are bound to the host.&lt;/p&gt;

&lt;p&gt;Binding the container to the hosts port can prevent multiple containers from running on the same host.  For example, only one container can bind to port 80 at a time.  This also complicates rolling out new versions of the container without downtime since the old container must be stopped before the new one is started.&lt;/p&gt;

&lt;p&gt;A reverse proxy can help with these issues as well as improve availabilty by facilitating zero-downtime deployments.&lt;/p&gt;

&lt;h2 id=&#34;generating-reverse-proxy-configs:068079669506d19d9ac0100aa8bfeb4c&#34;&gt;Generating Reverse Proxy Configs&lt;/h2&gt;

&lt;p&gt;Setting up a reverse proxy configuration can be complicated when containers are started and stopped.  Typically the configuration needs to be updated manually which is error prone and time consuming.&lt;/p&gt;

&lt;p&gt;Fortunately, Docker provides a remote API to &lt;a href=&#34;http://docs.docker.io/en/latest/reference/api/docker_remote_api_v1.10/#inspect-a-container&#34;&gt;inspect containers&lt;/a&gt; and access their IP, Ports and other configuration meta-data.  In addition, it also provides a &lt;a href=&#34;http://docs.docker.io/en/latest/reference/api/docker_remote_api_v1.10/#monitor-docker-s-events&#34;&gt;real-time events API&lt;/a&gt; that can be used for notifications when containers are started and stopped.  These APIs can be used to generate a reverse proxy config automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/jwilder/docker-gen&#34;&gt;docker-gen&lt;/a&gt; is a small utility that uses these APIs and exposes container meta-data to templates.  Templates are rendered and an optional notification command can be run to restart the service.&lt;/p&gt;

&lt;p&gt;Using &lt;a href=&#34;https://github.com/jwilder/docker-gen&#34;&gt;docker-gen&lt;/a&gt;, we can generate Nginx config files automatically and reload nginx when they change.  The same approach can also be used for &lt;a href=&#34;http://jasonwilder.com/blog/2014/03/17/docker-log-management-using-fluentd/&#34;&gt;docker log management&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&#34;nginx-reverse-proxy-for-docker:068079669506d19d9ac0100aa8bfeb4c&#34;&gt;Nginx Reverse Proxy for Docker&lt;/h2&gt;

&lt;p&gt;This example nginx template can be used to generate a reverse proxy configuration for docker containers using virtual hosts for
routing.  The template is implemented using the &lt;a href=&#34;http://golang.org/pkg/text/template/&#34;&gt;golang text/template package&lt;/a&gt;. It uses a custom &lt;code&gt;groupBy&lt;/code&gt; template function to group the running containers by their &lt;code&gt;VIRTUAL_HOST&lt;/code&gt; environment variable.  This simplifies iterating over the containers to generate a load-balanced backend and also enables zero-downtime deployments.&lt;/p&gt;

&lt;p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;k&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$host,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$containers&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;groupBy&lt;/span&gt; $ &lt;span class=&#34;s&#34;&gt;&amp;quot;Env.VIRTUAL_HOST&amp;quot;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt;
&lt;span class=&#34;s&#34;&gt;upstream&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$host&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

&lt;span class=&#34;kn&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$index,&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$value&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$containers&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$address&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$value.Addresses&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt;
    &lt;span class=&#34;s&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$address.IP&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:{&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$address.Port&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;kn&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt;

&lt;span class=&#34;err&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;s&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;#ssl_certificate /etc/nginx/certs/demo.pem;&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;#ssl_certificate_key /etc/nginx/certs/demo.key;&lt;/span&gt;

    &lt;span class=&#34;kn&#34;&gt;gzip_types&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/plain&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/css&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/json&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/x-javascript&lt;/span&gt;
               &lt;span class=&#34;s&#34;&gt;text/xml&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/xml&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/xml+rss&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/javascript&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$host&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;kn&#34;&gt;proxy_pass&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;http://&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;kn&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$host&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
        &lt;span class=&#34;kn&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/etc/nginx/proxy_params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;kn&#34;&gt;{{&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;}}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;The template can be run with &lt;a href=&#34;https://github.com/jwilder/docker-gen&#34;&gt;docker-gen&lt;/a&gt; using:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker-gen -only-exposed -watch -notify &amp;quot;/etc/init.d/nginx reload&amp;quot; templates/nginx.tmpl /etc/nginx/sites-enabled/default&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-only-exposed&lt;/code&gt; - Only use containers that have exposed ports.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-watch&lt;/code&gt; - After starting up, watch for docker container events and regenerate the template.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-notify &amp;quot;/etc/init.d/nginx reload&amp;quot;&lt;/code&gt; - Reload the nginx config after the template is generated.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;templates/nginx.tmpl&lt;/code&gt; - The nginx template.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/sites-enabled/default&lt;/code&gt; - Destination file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the rendered template with two containers configured with &lt;code&gt;VIRTUAL_HOST=demo1.localhost&lt;/code&gt; and one with &lt;code&gt;VIRTUAL_HOST=demo2.localhost&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre&gt;&lt;span class=&#34;k&#34;&gt;upstream&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;demo1.localhost&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;kn&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;172.17.0.4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;kn&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;172.17.0.3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;#ssl_certificate /etc/nginx/certs/demo.pem;&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;#ssl_certificate_key /etc/nginx/certs/demo.key;&lt;/span&gt;

    &lt;span class=&#34;kn&#34;&gt;gzip_types&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/plain&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/css&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/json&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/x-javascript&lt;/span&gt;
               &lt;span class=&#34;s&#34;&gt;text/xml&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/xml&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/xml+rss&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/javascript&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;demo1.localhost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;kn&#34;&gt;proxy_pass&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;http://demo.localhost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
        &lt;span class=&#34;kn&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/etc/nginx/proxy_params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;upstream&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;demo2.localhost&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;kn&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;172.17.0.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;k&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;#ssl_certificate /etc/nginx/certs/demo.pem;&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;#ssl_certificate_key /etc/nginx/certs/demo.key;&lt;/span&gt;

    &lt;span class=&#34;kn&#34;&gt;gzip_types&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/plain&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/css&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/json&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/x-javascript&lt;/span&gt;
               &lt;span class=&#34;s&#34;&gt;text/xml&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/xml&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;application/xml+rss&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;text/javascript&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;kn&#34;&gt;server_name&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;demo2.localhost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;kn&#34;&gt;location&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;kn&#34;&gt;proxy_pass&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;http://demo2.localhost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
        &lt;span class=&#34;kn&#34;&gt;include&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;/etc/nginx/proxy_params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;h2 id=&#34;try-it-out:068079669506d19d9ac0100aa8bfeb4c&#34;&gt;Try It Out&lt;/h2&gt;

&lt;p&gt;I created a &lt;a href=&#34;https://index.docker.io/u/jwilder/nginx-proxy/&#34;&gt;trusted build&lt;/a&gt; with this setup to make
it easier to try it out:&lt;/p&gt;

&lt;p&gt;Run nginx-proxy container:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock -t jwilder/nginx-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Start your containers with a &lt;code&gt;VIRTUAL_HOST&lt;/code&gt; environment variables:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ docker run -e VIRTUAL_HOST=foo.bar.com -t ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you need HTTPS, would like to run &lt;code&gt;docker-gen&lt;/code&gt; in a separate container from nginx,
Websocket support or other features, take a look at the
&lt;a href=&#34;https://github.com/jwilder/nginx-proxy&#34;&gt;github&lt;/a&gt; project for more information.&lt;/p&gt;

&lt;h2 id=&#34;conclusion:068079669506d19d9ac0100aa8bfeb4c&#34;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Generating nginx reverse proxy configs for docker containers can be automated using the Docker APIs and some basic templating. This can simplify deployments as well as improve availability.&lt;/p&gt;

&lt;p&gt;While this works well for containers running on a single host, generating configs for remote hosts requires &lt;a href=&#34;http://jasonwilder.com/blog/2014/02/04/service-discovery-in-the-cloud/&#34;&gt;service discovery&lt;/a&gt;.  Take a look at &lt;a href=&#34;http://jasonwilder.com/blog/2014/07/15/docker-service-discovery&#34;&gt;docker service discovery&lt;/a&gt; for a solution to that problem.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update: There&amp;rsquo;s a few other posts with similar ideas and variations that are worth checking out:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;http://brianketelsen.com/2014/02/25/using-nginx-confd-and-docker-for-zero-downtime-web-updates/&#34;&gt;Using Nginx, Confd, and Docker for Zero-Downtime Web Update&lt;/a&gt; - Brian Ketelsen&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://crosbymichael.com/docker-events.html&#34;&gt;Docker Events&lt;/a&gt; - Michael Crosby&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://oskarhane.com/haproxy-as-a-static-reverse-proxy-for-docker-containers/&#34;&gt;Haproxy As A Static Reverse Proxy for Docker Containers&lt;/a&gt; - Oskar Hane&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
