<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>blag</title><link href="https://www.roylarsen.xyz/" rel="alternate"/><link href="https://www.roylarsen.xyz/feeds/all.atom.xml" rel="self"/><id>https://www.roylarsen.xyz/</id><updated>2021-07-06T00:01:00-04:00</updated><entry><title>See you in hell, Nginx</title><link href="https://www.roylarsen.xyz/see-you-in-hell-nginx.html" rel="alternate"/><published>2021-07-06T00:01:00-04:00</published><updated>2021-07-06T00:01:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2021-07-06:/see-you-in-hell-nginx.html</id><summary type="html">&lt;p&gt;Details about my migratioon from Nginx to Traefik&lt;/p&gt;</summary><content type="html">&lt;p&gt;I'm mostly kidding, I'll always love me some Nginx.&lt;/p&gt;
&lt;p&gt;But I did decide to do some needless engineering and migrate to a new Reverse Proxy on my public webserver.&lt;/p&gt;
&lt;p&gt;I had a few needs that I wanted to address and I felt it was time. I wanted to take advantage of more automatic configuration through Docker that some modern reverse proxies use (it's always good to get rid of extra config files), and I also wanted to take advantage of automatic Let's Encrypt renewals (because I've been doing it mostly by hand for too long). I'd also like to do all of this in Docker Compose as well (my set-up is pretty basic and I don't need to deal with the eight million ton gorilla on my off-time).&lt;/p&gt;
&lt;p&gt;I decided to move to Traefik since it seems to handle my (limited) requirements the best.&lt;/p&gt;
&lt;p&gt;Traefik is a Cloud Native Ingress/Reverse Proxy that has built-in support for ACME/Let's Encrypt and is pretty much completely configurable through Labels (for Docker compose) or Annotations (for Kubernetes). The extent of my configuration outside of my compose file is mounting a volume to store the certificates that are requested.&lt;/p&gt;
&lt;p&gt;Is what I'm doing correct? Probably not. Does it work? As far as I can tell. I don't have any issues with things.&lt;/p&gt;
&lt;h3&gt;Core Config&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;traefik&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik:v2.4&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;container_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--log.level=DEBUG&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--api.insecure=false&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--api.dashboard=false&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--providers.docker=true&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--providers.docker.exposedbydefault=false&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--entrypoints.web.address=:80&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--entrypoints.websecure.address=:443&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.xyz.acme.httpchallenge=true&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.xyz.acme.httpchallenge.entrypoint=web&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.xyz.acme.email=my&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;addy&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.xyz.acme.storage=/letsencrypt/xyz.json&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.tde.acme.httpchallenge=true&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.tde.acme.httpchallenge.entrypoint=web&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.tde.acme.email=my&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;again&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.tde.acme.storage=/letsencrypt/tde.json&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I left out a bunch of this configuration since it wasn't important to this post.&lt;/p&gt;
&lt;p&gt;Here are some of the more important directives:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--providers.docker.exposedbydefault=false&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I don't want every service exposed, I want to control things on a service-by-service basis. I'll cover that in the next section.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.xyz.acme.httpchallenge=true&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.xyz.acme.httpchallenge.entrypoint=web&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.xyz.acme.email=my&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;addy&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--certificatesresolvers.tde.acme.storage=/letsencrypt/xyz.json&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These are for ACME/Let's Encrypt. I'm a fan of the HTTP challenge, it seems like the easiest thing to set up and maintain in my opinion (it's literally just these commands for each domain I want to secure).&lt;/p&gt;
&lt;p&gt;Since the HTTP challenge is over HTTP, we want the entrypoint to be my configured web entrypoint (as opposed to websecure, which is HTTPS).&lt;/p&gt;
&lt;p&gt;The email command is just my email address so when the renewal comes up, I get the notification.&lt;/p&gt;
&lt;p&gt;The storage command is just a mounted folder from the host that Docker is running on in the event something bad happens to the host, like I delete it or something - I can quickly get up and running again. /letsencrypt is a mounted NFS share frrom Digital Ocean that's backed up.&lt;/p&gt;
&lt;h3&gt;Service Config&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ghost:3.27-alpine&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;always&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/docker/ghost/:/var/lib/ghost/content&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;url=http://www.roylarsen.xyz&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;2368:2368&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.enable=true&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog-http.entrypoints=web&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog-http.rule=Host(`roylarsen.xyz`)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Host(`www.roylarsen.xyz`)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog-http.middlewares=blog-https&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.middlewares.blog-https.redirectscheme.scheme=https&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog.entrypoints=websecure&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog.rule=Host(`roylarsen.xyz`)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Host(`www.roylarsen.xyz`)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.services.blog.loadbalancer.server.port=2368&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog.tls.certresolver=xyz&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Above is how I'm exposing the container hosting my Blog.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;  &amp;quot;traefik.enable=true&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is how to enable/expose each service as mentioned above.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog-http.entrypoints=web&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog-http.rule=Host(`roylarsen.xyz`)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Host(`www.roylarsen.xyz`)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog-http.middlewares=blog-https&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.middlewares.blog-https.redirectscheme.scheme=https&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These lines are how to enable the HTTP listener for the domains I want my blog to answer on, and then redirect to HTTPS (since things should only be served over HTTPS, but I don't want errors over HTTP).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog.entrypoints=websecure&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog.rule=Host(`roylarsen.xyz`)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Host(`www.roylarsen.xyz`)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.services.blog.loadbalancer.server.port=2368&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;traefik.http.routers.blog.tls.certresolver=xyz&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After we accept the request from the HTTP listener, we need a place to redirect to (also just straight just to HTTPS).&lt;/p&gt;
&lt;p&gt;The biggest differences between the two are the loadbalancer forward and the certresolver.&lt;/p&gt;
&lt;p&gt;The loadbalancer is just where requests are sent on the backend, and then the certresolver is just what resolver to pull the certificate from in the Traefik config from the previous section.&lt;/p&gt;
&lt;h3 id="that-s-it"&gt;That's It&lt;/h3&gt;
&lt;p&gt;Seriously. It was pretty easy overall to be able to swap from Nginx to Traefik (except when I forgot the url for the admin panel to write this, heh), and now I don't have to think about my cert renewals for the rest of the life of this server. It's all very exciting.&lt;/p&gt;
&lt;/p&gt;</content><category term="misc"/><category term="docker"/><category term="cloud"/><category term="traefik"/></entry><entry><title>Jenkins Build Agents as Docker Swarm Services</title><link href="https://www.roylarsen.xyz/jenkins-build-agents-as-docker-swarm-services.html" rel="alternate"/><published>2019-09-25T20:07:00-04:00</published><updated>2019-09-25T20:07:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2019-09-25:/jenkins-build-agents-as-docker-swarm-services.html</id><summary type="html">&lt;p&gt;Lately I've been working on (re-)learning Jenkins and learning how to use their Pipelines feature.&lt;/p&gt;
&lt;p&gt;We had set up a Jenkins server at MHB to off-load some of the potential work from Bamboo, and also just because we're trying as a DevOps team to move away from Bamboo since …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Lately I've been working on (re-)learning Jenkins and learning how to use their Pipelines feature.&lt;/p&gt;
&lt;p&gt;We had set up a Jenkins server at MHB to off-load some of the potential work from Bamboo, and also just because we're trying as a DevOps team to move away from Bamboo since we're not too happy with it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Docker Swarm&lt;/h3&gt;
&lt;p&gt;First, let's address the ten thousand pound elephant in the room: Docker Swarm is less cool than Kubernetes.&lt;/p&gt;
&lt;p&gt;For small, easy to set up/manage Docker clusters on-prem, Docker Swarm is fine and perfectly serviceable. The automatic load balancing of published ports to all nodes/manager by default is really slick.&lt;/p&gt;
&lt;p&gt;That being said, not everything is great. For this to work, you need to expose the Docker Swarm API and to do that is fairly ugly. I'm not going to get into securing it, since this is a lab set-up.&lt;/p&gt;
&lt;p&gt;Exposing the swarm in an insecure manner is fairly straight-forward. Find the systemd unit file and add this to the end of the ExecStart line and then restart the service: -H tcp://0.0.0.0:2375&lt;/p&gt;
&lt;h3&gt;Jenkins Service&lt;/h3&gt;
&lt;p&gt;Here's my docker-compose.yml I used to deploy this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;3&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;services:  jenkins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;jenkins/jenkins&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;8080:8080&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;50000:50000&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;always&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/usr/bin/docker:/usr/bin/docker&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;JENKINS_SLAVE_AGENT_PORT=50000&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Beyond the Docker/Swarm stuff, I'm not getting into Jenkins configuration. There's plenty of documentation that exists for general Jenkins configuration and usage.&lt;/p&gt;
&lt;p&gt;I can't stress this isn't a production service. Nothing is persistant.&lt;/p&gt;
&lt;h3&gt;Jenkins Docker Swarm Plug-In&lt;/h3&gt;
&lt;p&gt;The next two sections are the most annoying. The documentation is definitely lacking when it exists.&lt;/p&gt;
&lt;p&gt;To enable Docker Swarm access in Jenkins, you need to install 'Docker Swarm Plugin' and then reload Jenkins.&lt;/p&gt;
&lt;p&gt;That adds a couple things to Jenkins. First and foremost, the Docker Swarm Dashboard:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Jenkins console showing Docker Swarm Dashboard" class="kg-image" src="http://www.roylarsen.xyz/content/images/2019/09/Screen-Shot-2019-09-25-at-3.31.31-PM.png"&gt;&lt;/p&gt;
&lt;p&gt;Then the more important thing, in Manage Jenkins -&amp;gt; Configure System, at the bottom there's an option for Adding a Cloud:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Jenkins console showing configuration for adding Docker Swarm Cloud" class="kg-image" src="http://www.roylarsen.xyz/content/images/2019/09/Screen-Shot-2019-09-25-at-3.34.16-PM.png"&gt;&lt;/p&gt;
&lt;p&gt;The configuration for that is pretty straight forward:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Ignore the 403's, I obviously changed the FQDNs" class="kg-image" src="http://www.roylarsen.xyz/content/images/2019/09/Screen-Shot-2019-09-25-at-12.15.36-PM.png"&gt;&lt;/p&gt;
&lt;p&gt;The 'Test' button is a little flaky from what I've seen, you might need to save your configuration before it will succeed.&lt;/p&gt;
&lt;h3&gt;Jenkins JNLP Agents&lt;/h3&gt;
&lt;p&gt;This was the most interesting part. And by interesting, I mean frustrating. This is where the documetation was really lacking and I happened to find a blog post with a quick explanation of how this all works.&lt;/p&gt;
&lt;p&gt;When you configure your cluster above, you get a button to add templates:&lt;/p&gt;
&lt;p&gt;&lt;img alt="" class="kg-image" src="http://www.roylarsen.xyz/content/images/2019/09/Screen-Shot-2019-09-25-at-3.53.10-PM.png"&gt;&lt;/p&gt;
&lt;p&gt;Clicking that button gives you this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Overall, very cool, with caveats" class="kg-image" src="http://www.roylarsen.xyz/content/images/2019/09/Screen-Shot-2019-09-25-at-12.13.04-PM.png"&gt;&lt;/p&gt;
&lt;p&gt;The label is what you can use for targetting, the image is the Docker Container to pull from. They actually provide a bunch of containers to use: &lt;a href="https://github.com/jenkinsci/jnlp-agents"&gt;https://github.com/jenkinsci/jnlp-agents&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The docker image is different from the other containers and works as documented/is fine with the example configuration. The general idea on how it's supposed to work is that when the container starts, it does a curl to the Jenkins master and downloads the JNLP agent to ensure there's no version mis-matches.&lt;/p&gt;
&lt;p&gt;As far as I can tell, none of the other agent images work that way. They're all based on their languages/toolsets alpine image and don't have tools like curl or git installed, but in the Dockerfile, Jenkins copies the agent .jar from the base JNLP agent image.&lt;/p&gt;
&lt;p&gt;jnlp-docker command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    sh -cx curl --connect-timeout 20 --max-time 60 -o agent.jar $DOCKER_SWARM_PLUGIN_JENKINS_AGENT_JAR_URL &amp;amp;&amp;amp; java -jar agent.jar -jnlpUrl $DOCKER_SWARM_PLUGIN_JENKINS_AGENT_JNLP_URL -secret $DOCKER_SWARM_PLUGIN_JENKINS_AGENT_SECRET -noReconnect -workDir /tmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;jnlp-python command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    sh -cx apk -u add git &amp;amp;&amp;amp; java -jar /usr/share/jenkins/agent.jar -jnlpUrl $DOCKER_SWARM_PLUGIN_JENKINS_AGENT_JNLP_URL -secret $DOCKER_SWARM_PLUGIN_JENKINS_AGENT_SECRET -noReconnect -workDir /tmp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It took a bit of debugging and digging to figure out what was different between the two container images and figure out why my Docker container was working but my Python container wasn't.&lt;/p&gt;
&lt;p&gt;The other big 'gotchas' I ran into is that the container needs to be run as root, or it will fail to launch. For Docker, you also need to make sure you mount /var/run/docker.sock from the host into the container so the Docker client in the container has a Docker daemon to work against.&lt;/p&gt;
&lt;h3&gt;Jenkinsfile&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    pipeline{
      agent {
        label &amp;#39;jenkins-docker&amp;#39;
      }
      stages {
        stage(&amp;#39;Print Test&amp;#39;){
          agent {
            label &amp;#39;jenkins-docker&amp;#39;
          }
          steps {
            sh &amp;quot;uname -a&amp;quot;
            sh &amp;quot;docker run hello-world&amp;quot;
          }
        }
        stage(&amp;#39;Schedule Tests&amp;#39;){
          agent {
            label &amp;#39;jenkins-python&amp;#39;
          }
          steps{
            sh &amp;#39;python --version&amp;#39;
            sh &amp;#39;ls .&amp;#39;
            sh &amp;#39;printenv&amp;#39;
          }
        }
      }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above Jenkinsfile is a working example where I have the agents for the Pipeline level, and each stage (the job spawns as the first agent, then that spawns the agents for each of the stages).&lt;/p&gt;
&lt;p&gt;&lt;img alt="Jobs running, agents in various stages" class="kg-image" src="http://www.roylarsen.xyz/content/images/2019/09/Screen-Shot-2019-09-25-at-2.03.09-PM.png"&gt;&lt;/p&gt;
&lt;p&gt;I'm not going to get into setting up builds on API calls, again, that gets into Jenkins documentation that is good and there's plenty of.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Hopefully this helps. It'll be nice to have this reference for myself in the future.&lt;/p&gt;
&lt;/p&gt;</content><category term="misc"/><category term="docker"/><category term="jenkins"/><category term="mobileheartbeat"/><category term="devops"/><category term="swarm"/></entry><entry><title>So I decided to containerize our application stack... Pt 2</title><link href="https://www.roylarsen.xyz/so-i-decided-to-containerize-our-application-stack-pt-2.html" rel="alternate"/><published>2019-06-12T04:01:00-04:00</published><updated>2019-06-12T04:01:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2019-06-12:/so-i-decided-to-containerize-our-application-stack-pt-2.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;Here's the story so far:&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.roylarsen.xyz/so-i-decided-to-containerize-our-application-stack/"&gt;Part 1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;h2 id="beginningtoworkthroughmylist"&gt;Beginning to work through my list&lt;/h2&gt;
&lt;/p&gt;
&lt;p&gt;In the last post I came up with a list of tasks that I need to work through so that we can do things in Docker "correctly".&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;("Correctly" meaning, as close to "dev clones the repo, fills …&lt;/p&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;Here's the story so far:&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.roylarsen.xyz/so-i-decided-to-containerize-our-application-stack/"&gt;Part 1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;h2 id="beginningtoworkthroughmylist"&gt;Beginning to work through my list&lt;/h2&gt;
&lt;/p&gt;
&lt;p&gt;In the last post I came up with a list of tasks that I need to work through so that we can do things in Docker "correctly".&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;("Correctly" meaning, as close to "dev clones the repo, fills in their .env, then runs docker-compose")&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="gettingsqlservertoworkindockeronlinux"&gt;Getting SQL Server to work in Docker on Linux&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;It's funny, this was actually pretty straight-forward. Faults aside over the years, Microsoft has been putting a lot of effort into building compatibility from their flagship products and also transitioning from a traditional software company to a platform company, and transitioning a lot of their major software to either being a SaaS offering (Outlook/O365) or making it run on Linux (SQL Server, Powershell) as well.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Running SQL Server is pretty easy, MS has good guides on SQL Server running on Linux on technet: &lt;a href="https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-2017&amp;amp;pivots=cs1-bash"&gt;https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-2017&amp;amp;pivots=cs1-bash&lt;/a&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;It's super easy and all you really need to do is provide the password for the SA account as an environment variable.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="runningflywaymigrations"&gt;Running Flyway migrations&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;Everything's easier when the vendor makes docker images easily available and usable.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Now, this is still a little tricky, because while the repo that has all our application code also has all our migrations, it's more than one migration and the flyway container comes expecting to be run via &lt;code&gt;docker run&lt;/code&gt; and just providing arguments to the migrate command.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;That doesn't really work for me.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;There's a couple of issues that I needed to overcome to get this service in my compose file working:&lt;/p&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I need the two databases the application expects&lt;/li&gt;
&lt;li&gt;I need to run the two migrations for our databases&lt;/li&gt;
&lt;/ol&gt;
&lt;/p&gt;
&lt;p&gt;By the time this service starts/runs, all I have is a bare SQL Server instance with no databases or accounts other than the default ones.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Normally, to create the two databases that our application needs, I just use sqlcmd or Invoke-SQLCmd, but this container doesn't have the first command and these are Linux containers and not Windows - so no Invoke-SQLCmd.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I started thinking about how (since I'm already using it in this container) I could abuse Flyway - since all it does is run specially named .sql files.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;So, the first thing I did was create a new migration to do the initial Database creation. It was pretty straightforward since in the repo, there was already a script to create the base databases, I just needed to name it correctly (eg, VO__createDB.sql, and write the config file for this migration)&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;However, since this adds to the complexity of the problem, I need to update the above list:&lt;/p&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I need the two databases the application expects&lt;/li&gt;
&lt;li&gt;I need to run the ~~two~~ three migrations for our databases&lt;/li&gt;
&lt;/ol&gt;
&lt;/p&gt;
&lt;p&gt;Luckily, point two are why shell scripts exist.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I threw together a &lt;a href="https://gist.github.com/roylarsen/80eb8c536b1d391c0a18992a4b669263"&gt;really basic shell script&lt;/a&gt; to run three flyway migrations. It's nothing fancy, but at the end of the day, it didn't need to be.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Now, the fun thing was trying to figure out how to overwrite the container's command and inject my shell script.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The nice thing is that Compose gives you exactly that.&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;entrypoint: /flyway/mh-scripts/mh-run-migrations.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;The 'entrypoint' option just overrides the CMD of the container. In my case, giving me a one off container that runs the migrations.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="buildingandrunningthetomcatapplication"&gt;Building and running the Tomcat Application&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;First and foremost, shoutout to &lt;a href="https://twitter.com/samstelfox"&gt;Sammy&lt;/a&gt; for telling me about this.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;My goal was to be able to have one Dockerfile to handle doing both the build, and also running the application.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Luckily, these days Dockerfiles have multi-stage builds (thanks for pointing that out, Sammy).&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="https://gist.github.com/roylarsen/55e47e2e3b9e9fab2cf8be4f71955751"&gt;Dockerfile&lt;/a&gt;!&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The important parts of this are the following:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;FROM gradle:5.4-jdk8 as build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;The first FROM line sets up the stage to copy artifacts from. In my instance, I can refer to it as 'build'.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The next important thing is the change to the COPY command.&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;COPY --from=build /home/gradle/project/heartbeatWeb/build/libs/heartbeat.war /usr/local/tomcat/webapps
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;The important part of this is the --from= argument. Normally, you use COPY to copy from the host to the container. When using the from argument, you can specify the first step and copy artifacts from that container instead.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="wiringeverythingupindocker"&gt;Wiring everything up in Docker&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;I'm not up to doing anything particularly fancy yet (separate netoworks for separation of concerns or anything, so a basic network configuration in compose is more than enough.&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;networks&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;That's more than enough to get all the containers into a state where they can talk.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;80:8080&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;$JAVA_OPTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Ddbserver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;flyway_migrate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;This is the last container. From here, we see the links to the DB service, the the dependency on running the database migrations.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The default network is enough that you can address containers by their service name (see the -Ddbserver arg for the environment above).&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Now, I can't stress this enough, this set up &lt;strong&gt;isn't&lt;/strong&gt; for production. Everything is accessible. In the future, one of my items will be to separate internal services and external services, and to put things behind a proper proxy service (probably something like HAProxy, maybe Traefik if that can handle non-http traffic).&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="tasklistupdate"&gt;Task List Update&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;I did everything in my old task list, now I need a new one!&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our Event Management Service&lt;/li&gt;
&lt;li&gt;Ejabberd&lt;/li&gt;
&lt;li&gt;Maybe clustered Ejabberd&lt;/li&gt;
&lt;li&gt;ActiveMQ&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/><category term="mobileheartbeat"/><category term="docker"/><category term="devops"/><category term="sqlserver"/><category term="series"/></entry><entry><title>So I decided to containerize our application stack...</title><link href="https://www.roylarsen.xyz/so-i-decided-to-containerize-our-application-stack.html" rel="alternate"/><published>2019-05-12T17:28:00-04:00</published><updated>2019-05-12T17:28:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2019-05-12:/so-i-decided-to-containerize-our-application-stack.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;This is going to be the first part of a series of posts where I'm going to document my journey containerizing our Platform.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Here's the story so far:&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.roylarsen.xyz/so-i-decided-to-containerize-our-application-stack-pt-2/"&gt;Part 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;h2 id="firstforays"&gt;First Forays&lt;/h2&gt;
&lt;/p&gt;
&lt;p&gt;The beginning of this journey was extremely haphazard (aka, I was bored one night and wanted something to …&lt;/p&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;This is going to be the first part of a series of posts where I'm going to document my journey containerizing our Platform.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Here's the story so far:&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.roylarsen.xyz/so-i-decided-to-containerize-our-application-stack-pt-2/"&gt;Part 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;h2 id="firstforays"&gt;First Forays&lt;/h2&gt;
&lt;/p&gt;
&lt;p&gt;The beginning of this journey was extremely haphazard (aka, I was bored one night and wanted something to work on while watching TV). For fun I spun up a Server 2016 box with our software installed on it and set up &lt;a href="https://www.roylarsen.xyz/docker-on-windows/"&gt;Docker on Windows&lt;/a&gt; since (due to our Customers) our Platform is Windows first.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Once that was all done, I copied the .wars that make up our admin application and the distribution of tomcat that we use and threw all of that into a Dockerfile and spun it up to talk to the SQL Server instance installed on that box.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;It was pretty cool to type &lt;code&gt;docker run&lt;/code&gt; and connect to that tomcat instance and see it work. &lt;a href="https://gist.github.com/roylarsen/14f0f87775e4d039e47d2c42002d0d87"&gt;Server 2016 Dockerfile&lt;/a&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;It's all fun to get this to work on Windows, but lets face it, Windows is a limiting factor when all the new developments in DevOps treats Windows as a Second Class Citizen at best.&lt;/p&gt;
&lt;/p&gt;
&lt;h2 id="movingforward"&gt;Moving Forward&lt;/h2&gt;
&lt;/p&gt;
&lt;p&gt;Those first steps were done back in November of 2018 and I eventually picked this back up in May of 2019.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;This is when I started to want to get more serious about my personal projects and wanting to learn more about containers and take learning Docker more seriously.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="deployingourwarstolinux"&gt;Deploying our wars to linux&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;This is where things get really fun.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;With the help of my awesome co-worker Valerie answering my really basic questions, I got things working.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The nice thing about Java is the WORA (write once, run anywhere) principal it follows. For the most part the initial getting things working was a case of pulling the image for Tomcat 8.5.40 and building my container by writing another pretty basic Dockerfile. &lt;a href="https://gist.github.com/roylarsen/6c9bb232120e04ef913d20cae01e52a4"&gt;Linux Dockerfile&lt;/a&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;After some trial and error and tracking down dependencies (jar with the SQL Server driver, a mail library) to get things working, I got a connection to an existing database on another server, and authentication via LDAP that was already pre-configured in that database.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Getting our webapps to work on Linux has been fun, because I'm getting to learn more about Tomcat. For the most part, the server.xml is a static entity but there are items that I want to be able to pass to the container as it starts up. Mainly the DB connection string and information.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;That's where I learned how to use the \$JAVA_OPTS to populate variables in the server.xml.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker run -it -p 80:8080 -e "JAVA_OPTS=$JAVA_OPTS -Ddbserver=sql1" --net test mhb/heartbeat:1.0&lt;/code&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Is what I've been using to launch the tomcat container with the environment variable to set the JAVA_OPTS environment variable in the container.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;-e "JAVA_OPTS=$JAVA_OPTS -Ddbserver=sql1"&lt;/code&gt; option, that sets the \$JAVA_OPTS environment variable in the container. Within the server.xml I have access to the dbserver variable that I can set with \${dbserver}.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The next iteration of this is going to involve learning the setenv.sh script so I can use environment files in my compose file so I can more securly set the variables.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="comingupwithatasklist"&gt;Coming up with a task list&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;Once I saw that working, I came up with a list of tasks to go from the most basic of POC to an actual MVP that I can show off and see what people think.&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get &lt;a href="https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-2017&amp;amp;pivots=cs1-bash"&gt;SQL Server on Linux working in Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Figure out how to run our &lt;a href="https://flywaydb.org/"&gt;Flyway&lt;/a&gt; migrations in Docker&lt;/li&gt;
&lt;li&gt;Wire everything up in docker&lt;/li&gt;
&lt;li&gt;Perform builds and deploy to Docker&lt;/li&gt;
&lt;li&gt;Docker Compose&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;Now I have a task list to work through&lt;/p&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/><category term="docker"/><category term="mobileheartbeat"/><category term="tomcat"/><category term="linux"/><category term="devops"/><category term="work"/><category term="series"/></entry><entry><title>Microsoft OpenHack - Day 2</title><link href="https://www.roylarsen.xyz/microsoft-openhack-day-2.html" rel="alternate"/><published>2019-05-09T04:11:00-04:00</published><updated>2019-05-09T04:11:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2019-05-09:/microsoft-openhack-day-2.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;Where did we leave off yesterday? Oh yeah, Challenge 3.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Challenge 3 is where we start really seeing the pipelines in Azure DevOps (ADO) come together.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Side note: Days 2 and 3 I wasn't feeling super great, so starting this day, we have less screenshots.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Since we got one of …&lt;/p&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;Where did we leave off yesterday? Oh yeah, Challenge 3.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Challenge 3 is where we start really seeing the pipelines in Azure DevOps (ADO) come together.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Side note: Days 2 and 3 I wasn't feeling super great, so starting this day, we have less screenshots.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Since we got one of the test pipelines working the end of the day before, that gave us a good starting point for getting all our other testing working.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen-Shot-2018-11-28-at-12.18.17-PM" src="http://www.roylarsen.xyz/content/images/2018/12/Screen-Shot-2018-11-28-at-12.18.17-PM.png"&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The above is where I ended up for Challenge 3 with the node.js API.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The pipeline started with getting the sources, building the container, then pushing it to ACR.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;We needed to add running tests, verifying the tests, and then acting based on the outcome of the tests.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The nice thing about modern languages, is that they provide all the tools you need to do things like running tests in the same binary as what you use to run your scripts.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Now that's out of the way, lets start pulling that screen shot apart, job by job.&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Get Sources
    &lt;/p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is pretty straightforward. This preps the build environment for the current code base (git pull)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
-   npm install
&lt;/p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is also pretty straightforward. This will install the npm tooling and project dependencies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
-   npm custom
&lt;/p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is where the testing happens. ADO only gives two two jobs options (this is what I'm rembering seeing a few days later) and those are the npm install command and a 'custom' command to let you use the other npm tools&lt;/li&gt;
&lt;li&gt;The custom command in this intance is just for running npm test in the root of the node.js API&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
-   Publish Test Results
&lt;/p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This was me going a little above and beyond&lt;/li&gt;
&lt;li&gt;I was having some issues running my npm test command (because I don't know npm or node.js) and wasn't getting the debugging output I needed to figure out the problem&lt;/li&gt;
&lt;li&gt;This just takes the .xml output of the test results and puts it in the pipeline output so you can see the results with more output&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
-   Build an Image
&lt;/p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is the same as it was before, it just builds the docker image from the accompanying Dockerfile&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
-   Push an Image
&lt;/p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Again, unchanged from before. This just pushes the container to the ACR on a successful build&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
-   Publish Artifact helm
&lt;/p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This is new and unneeded (for this challenge). All this does is take the /helm/ folder and publush it as a artifact to be shared in downstream pipelines&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;Now, the next question should be: What about the second part of the challenge posted in the previous article? Creating a work ticket based on problems in this pipeline?&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;It was literally a toggle in the options for build pipelines. Between that and making sure that jobs are set to "run only if the previous job succeeded", that will keep the pipelines from moving bad code down the line.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Challenge 4)&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Create a release pipeline!&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;(this was after lunch where I started feeling way worse than before lunch and I started forgetting to take screenshots)&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;So, a lot of this is a lot like setting up the build pipelines. When you have your pipelines defaulted to the drag and drop it's pretty straight-forward.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;When you're building pipelines in ADO, it's mostly dragging and dropping the tools you need to where you need them. For Release Pipelines, the main difference is that they go horizontal as opposed to vertical.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Our release pipeline was roughly:  &lt;/p&gt;
&lt;p&gt;|Artifact| -&amp;gt; | Stage 1 |&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;(I know, my ASCII drawing is &lt;em&gt;too&lt;/em&gt; good)&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;-Long time ago post-&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;So, during this day I started feeling ill and I kinda dropped this (oops). There's not much for Day 3 since it was mostly trying to get these tasks done.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I was also still not feeling well, so I actually don't remember much beyond closing things out/&lt;/p&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/><category term="ci"/><category term="k8s"/><category term="microsoft"/><category term="docker"/><category term="nodejs"/><category term="devops"/></entry><entry><title>Microsoft OpenHack - Day 1</title><link href="https://www.roylarsen.xyz/microsoft-openhack-day-1.html" rel="alternate"/><published>2018-11-28T06:02:00-05:00</published><updated>2018-11-28T06:02:00-05:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2018-11-28:/microsoft-openhack-day-1.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;Lions, tigers, and Azure DevOps. Oh my!&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Where to begin?&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Recently my boss emailed myself and a couple other engineers at Mobile Heartbeat about an event Microsoft was running about learning Azure, Azure Kubernetes (k8s) Service, and Azure DevOps (silly name for an actually pretty cool project). Naturally, I took …&lt;/p&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;Lions, tigers, and Azure DevOps. Oh my!&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Where to begin?&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Recently my boss emailed myself and a couple other engineers at Mobile Heartbeat about an event Microsoft was running about learning Azure, Azure Kubernetes (k8s) Service, and Azure DevOps (silly name for an actually pretty cool project). Naturally, I took him up on the offer because it was free and was a training on stuff I'm interested in/not well versed in.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Alright, now the fun bit.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The task:&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen-Shot-2018-11-27-at-9.28.57-AM" src="http://www.roylarsen.xyz/content/images/2018/11/Screen-Shot-2018-11-27-at-9.28.57-AM.png"&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;That Architecture, but allowing for automatic rolling/zero downtime deployments of the 4 API microservices within the k8s cluster.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The event is setup in a pretty interesting way. The way that Microsoft set it up is as a series of challenges that build on the successes of the previous challenges.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;When we got there this morning, they assigned everyone into different teams and after some moving me around, I settled into Team 11 with my first cup of coffee.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Challenge 1)&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;"In order to have a successful DevOps strategy, your team needs to have a plan."&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;The first challenge of the day, we needed to come up with a plan of how we want to run our team. Pretty basic, but it gave us a chance to work out how we like to work.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;For the most part we decided that between tickets and the Wiki within Azure DevOps and the fact that we're sitting face to face, we don't need to worry about also adding some chat software into the mix.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Challenge 2)&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Show to your coach that you can link a work item with the associated code changes and the artifacts that have been generated.&lt;/li&gt;
&lt;li&gt;Demonstrate to your coach that you have implemented a policy to prevent any unreviewed code changes from being committed to master.&lt;/li&gt;
&lt;li&gt;On a successful build, demonstrate that the updated version of the container is pushed to a container registry.&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;From here we created stories for each of these tasks, and then sub-divided them into actionable tasks.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The first thing we did was import the git repository from Github (&lt;a href="https://github.com/Azure-Samples/openhack-devops-team"&gt;https://github.com/Azure-Samples/openhack-devops-team&lt;/a&gt;), into Azure DevOps.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;That allowed our team member working on task 2 to create the appropriate policies to institute mandatory code reviews where we need two reviewers that aren't the person who created the PR.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The rest of us tackled task 3. We broke that into feature tickets for each of the API services.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;We were able to create build pipelines for each of the APIs.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Here's the Java Pipeline I worked on for example (there's an extra task, but I'll cover that in 3)&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen-Shot-2018-11-28-at-12.56.03-AM" src="http://www.roylarsen.xyz/content/images/2018/11/Screen-Shot-2018-11-28-at-12.56.03-AM.png"&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;On approved PR, the pipeline is kicked off for API folders that there's changes.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screen-Shot-2018-11-28-at-1.01.53-AM" src="http://www.roylarsen.xyz/content/images/2018/11/Screen-Shot-2018-11-28-at-1.01.53-AM.png"&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Once these items were completed and we had containers building and being pushed to the correct repositories (there was a little confusion that was quickly cleared up), we then quickly took care of task 1 by putting everything together.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;We created a couple tasks to run end to end tests of our new pipelines.&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a ticket and associated branch from master&lt;/li&gt;
&lt;li&gt;Make a change in our branch and create a PR to master&lt;/li&gt;
&lt;li&gt;Get the required amount of approvals (2 minimum, can't be the person who created the PR) which then kicked off the pipeline&lt;/li&gt;
&lt;li&gt;As soon as Azure DevOps saw the PR had the correct approvals, the build was automatically started&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;Challenge 3)&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;/p&gt;
&lt;p&gt;You are expected to improve the pipeline that you have started to build in the previous challenge by validating the functionality of at least one part of the code for the APIs of your application that you have selected.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
-   &lt;/p&gt;&lt;/p&gt;
&lt;p&gt;In the event that a test fail, a bug shall be created automatically and added in the backlog and the submitted changes are not integrated in your code.&lt;/p&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;When the second challenge was accepted by our coach, we got to work on 3. Overall, it's not that complicated of a challenge - but when you're sitting at a table of non-developers, unit tests become tricky.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;So, first thing I did was create stories for the two main tasks and start splitting those into features and issues.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Luckily, the APIs all already had their test suites written and had jobs in their various build systems ready to go, so all we had to do was add a task before the docker image build.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The hardest one to get going was the C# project, because each of the APIs had documentation in the README.md about how tests are run - that project didn't.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;After some trial and error, we had that pipeline ready to go and it was 4:30 and time to go.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Tomorrow is Day 2 and we're starting off strong and getting task 3 finished.&lt;/p&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/><category term="azure"/><category term="docker"/><category term="k8s"/><category term="ci"/><category term="devops"/><category term="microsoft"/></entry><entry><title>DevOpsDays Boston 2018: Day 2</title><link href="https://www.roylarsen.xyz/devopsdays-boston-2018-day-2.html" rel="alternate"/><published>2018-09-25T23:41:00-04:00</published><updated>2018-09-25T23:41:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2018-09-25:/devopsdays-boston-2018-day-2.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;Today, a lot like yesterday. I got to sit in talks and listen to smart people talk about things they care a lot about. Back to regular life tomorrow, but I'll at least be going back with some new ideas.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="jamesmeicklekeynotebeyondburnout"&gt;James Meickle- KEYNOTE: Beyond Burnout&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;This was awesome. It was …&lt;/p&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;Today, a lot like yesterday. I got to sit in talks and listen to smart people talk about things they care a lot about. Back to regular life tomorrow, but I'll at least be going back with some new ideas.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="jamesmeicklekeynotebeyondburnout"&gt;James Meickle- KEYNOTE: Beyond Burnout&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;This was awesome. It was about making your engineering teams more inclusive for our friends/collegues dealing with mental illness as a way to better serve your team(s) as a whole.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="tiffanylongworthchangemanagementforhumans"&gt;Tiffany Longworth - Change Management for Humans&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;We need to build for specific people. Our change is going to fail if we dont' have a specific target goal. Stakeholders are paramount.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Create your own evangelists!&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="flynntheriseoflayer7microservicesandtheproxywarwithenvoynginxandhaproxy"&gt;Flynn - The rise of Layer 7, microservices, and the proxy war with Envoy, NGINX, and HAProxy&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;This was one of the more technical talks I got to go to. This talk was about how we moved from Monoliths to Decentralized Monoliths to Monoliths (again) and now to Decentralized microservices.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;We've been able to vetter decentralize our services by using things like HAPRoxy and Zookeeper, but then all-in-one tools like Envoy came out to simplify comms and increase observability.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="mattystrattoneverythingisaproducthowtoapplyproductmanagementpracticestotechnologyservices"&gt;Matty Stratton - Everything Is A Product - How To Apply Product Management Practices to Technology Services&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;It was fun seeing a host of a &lt;a href="https://www.arresteddevops.com/"&gt;podcast I actively like and enjoy listening to&lt;/a&gt; talk at the conference.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;There was a lot of good information in this about how everything we do should be treated as a product with owners and stakeholders. Just because an environment is labelled as Dev or QA or that a tool we've written for internal use, people still rely on it and it needs to be up as much as we can garantee it.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="yegorbugayenkoexpertisevsexperts"&gt;Yegor Bugayenko - Expertise vs Experts&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;There were things in this talk I didn't agree with, but I think he made a lot of points that were good.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Software is better as manufacturing than art. Art makes things magic and special. Magic and special is unmaintainable. A good engineer is one that can easily be replaced.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="paulbruceprogressivetestingtomeettheperformanceimperative"&gt;Paul Bruce - Progressive Testing to Meet the Performance Imperative&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;This talk was the biggest bummer. Paul was having A/V issues throughout his entire talk.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="mikerothmannquickanddirtydevsecops"&gt;Mike Rothmann - Quick and Dirty DevSecOps&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;Long story short: There is no such thing as Quick and Dirty DevSecOps. We're too often sacrificing security in the name of speed and agility. Mike argued that if we build our software and pipelines with security at the forefront, we don't have to sacrifice anything.&lt;/p&gt;
&lt;/p&gt;
&lt;h2 id="overallthoughts"&gt;Overall thoughts&lt;/h2&gt;
&lt;/p&gt;
&lt;p&gt;Overall, one of my favorite things about this conference was how much emphasis on inclusivity and diversity there was. Whether it was the breakfast they ran for underrepresented groups, or the Keynote this morning where James framed accomodating for people with mental illnesses as a way to make things better for you organization as a whole.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Catering to underrepresented groups is a strength, not a weakness. Homogeneous teams and companies are missing out on valuable input from individuals that you wouldn't normally get.&lt;/p&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/></entry><entry><title>DevOpsDays Boston 2018: Day 1</title><link href="https://www.roylarsen.xyz/devopsdays-boston-2018-day-1.html" rel="alternate"/><published>2018-09-24T20:53:00-04:00</published><updated>2018-09-24T20:53:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2018-09-24:/devopsdays-boston-2018-day-1.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;h2 id="soworkpaidformeandmybosstogotoaconference"&gt;So, work paid for me and my boss to go to a conference&lt;/h2&gt;
&lt;/p&gt;
&lt;p&gt;Yes, it's only in Boston, but it's still not work, so that rules. Here are my big takeaways from the first day:&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="katyfarmerkeynotehowtobehappybeingaverage"&gt;Katy Farmer - KEYNOTE: How to be Happy Being Average&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;It's ok to not be a …&lt;/p&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;h2 id="soworkpaidformeandmybosstogotoaconference"&gt;So, work paid for me and my boss to go to a conference&lt;/h2&gt;
&lt;/p&gt;
&lt;p&gt;Yes, it's only in Boston, but it's still not work, so that rules. Here are my big takeaways from the first day:&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="katyfarmerkeynotehowtobehappybeingaverage"&gt;Katy Farmer - KEYNOTE: How to be Happy Being Average&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;It's ok to not be a rockstar! We're all getting shit done and able to stand on the shoulders of giants to do cool things.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="antongurovanatomyofanawsaccountcryptojack"&gt;Anton Gurov - Anatomy of an AWS Account Cryptojack&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;hahaha, holy shit we're bad at security. Don't ever post AWS/GCE/Azure/DO/etc... creds to a public git repository. Not only will you get owned in a hilariously short amount of time, but you'll probably be on the hook for thousands to your IaaS provider.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="quintessenceanxsensoryfriendlymonitoringkeepingthenoisedown"&gt;Quintessence Anx - Sensory Friendly Monitoring: Keeping the Noise Down&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;Notifications are insanely distracting. Even if it's a welcomed notification, it's going to take time to deal with it and get back to work. Take a good long look at your notifications and figure out how to make them more manageable and how to make them better for you.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="janepruskakovacanyoukeepasecretprivacyinthedigitalage"&gt;Jane Pruskakova - Can You Keep a Secret? Privacy in the Digital Age&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;Long story short: No. No we can't. Also, everyone wants every bit of data out of us that they can get.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="mattsurabianvaultatscale"&gt;Matt Surabian - Vault at Scale&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;Vault is actually pretty easy to run at scale, it's the backend and dataload that's hard to scale (insert fun anectdote of Zipcar's 6k container fleet clogging up token lease aggregation)&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="petecheslockpickanythreegoodfastandsafedevopsfromscratch"&gt;Pete Cheslock - Pick any three: Good, Fast, and Safe. DevOps from Scratch&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;We're still bad at DevOps, but we're getting better. Now that Dev and Ops are a happy family, we need to start being better at talking to Security.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="aliciadaviswhyweprovisioned37jenkinsservershowtoarchitectyourcicdpipelinetoaccountfororganizationalmaturity"&gt;Alicia Davis - Why we provisioned 37 Jenkins Servers: How to architect your CICD pipeline to account for organizational maturity&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;Apparently a company with a couple hundred dev teams requires a lot of space to work effectively.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="angelriveracicd101withcircleci"&gt;Angel Rivera - CICD 101 with CircleCI&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;Modern CICD pipelines are fun! git commit -a to publish a new container to the Docker Hub.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Ok, that's day 1. All that's left is closing and drinking at Banyon!&lt;/p&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/></entry><entry><title>Docker on Windows</title><link href="https://www.roylarsen.xyz/docker-on-windows.html" rel="alternate"/><published>2018-09-12T17:31:00-04:00</published><updated>2018-09-12T17:31:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2018-09-12:/docker-on-windows.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;This is a thing I've beeen doing at work. Partly because I've been learning docker on my own time, and partly because it'll make deploying our chatbot code easier.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;A lot of this is going to be copy and pasted from the wiki article I wrote about Docker on Windows …&lt;/p&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;This is a thing I've beeen doing at work. Partly because I've been learning docker on my own time, and partly because it'll make deploying our chatbot code easier.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;A lot of this is going to be copy and pasted from the wiki article I wrote about Docker on Windows.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I'm not going to get into what are containers and what Docker is, so, a certain level of knowledge is assumed. Smarter people who are better writers have already done that and those articles are easy to find.&lt;/p&gt;
&lt;/p&gt;
&lt;h3&gt;Background&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;So, there's a few things that we need to address first.&lt;/p&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Docker on Server 2016 is &lt;em&gt;very&lt;/em&gt; different than Docker on Windows 10&lt;/li&gt;
&lt;li&gt;Everything in this doc is assumed Server 2016&lt;/li&gt;
&lt;li&gt;When did MS perform the dark ritual to get Docker working on Windows?&lt;/li&gt;
&lt;/ol&gt;
&lt;/p&gt;
&lt;p&gt;Docker on Server 2016 is a native feature that doesn't rely on running Hyper-V or anything, u like Windows 10. You install it just like you install any other Windows Feature.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Once the feature is installed, you're pretty much off to the races. There's no configuration or anything to do.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The only thing that you'll probably want to do once the features are installed is installing compose. Don't worry, it's not hard and I have the instructions for that.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="thesetup"&gt;The Set-Up&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;The version of Server 2016 doesn't matter that much. I've done this with both core installs, and full desktop environments.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;To prep the server for Docker for Windows:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Install-Module -Name DockerMsftProvider -Repository PSGallery -ForceInstall-Package -Name docker -ProviderName DockerMsftProviderRestart-Computer -force
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;Once those lines are run, you can test your installation now:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;docker run microsoft/dotnet-samples:dotnetapp-nanoserver
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;At this point, you should have a working Docker install.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Docker is great, but you also probably want docker-compose so that you can have something useful to run things with:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServicePointManager&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;SecurityProtocol&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SecurityProtocolType&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Tls12Invoke&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;WebRequest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;https://github.com/docker/compose/releases/download/1.22.0/docker-compose-Windows-x86_64.exe&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;UseBasicParsing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;OutFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ProgramFiles&lt;/span&gt;\&lt;span class="n"&gt;docker&lt;/span&gt;\&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;compose&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;Powershell is kinda annoying, and you have to force it to use TLS 1.2 in order to interact with sites that have a proper HTTPS set-up.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The the download is pretty straight forward. Substitute the version of compose for the version that you want. The -usebasicparsing flag just makes it go a bit quicker.&lt;/p&gt;
&lt;/p&gt;
&lt;h2&gt;Usage&lt;/h2&gt;
&lt;/p&gt;
&lt;p&gt;Now that docker and compose are installed on your system, you can pretty much use them as you would on Linux systems.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The big things to remember when using Docker is that you can only run containers of the same OS on the host. For Windows, there are two base containers that you can use:&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="https://hub.docker.com/r/microsoft/windowsservercore/"&gt;https://hub.docker.com/r/microsoft/windowsservercore/&lt;/a&gt; - This is about \~4GB for a container  &lt;/p&gt;
&lt;p&gt;&lt;a href="https://hub.docker.com/r/microsoft/nanoserver/"&gt;https://hub.docker.com/r/microsoft/nanoserver/&lt;/a&gt; - This is about \~2GB for a container, but you can't use insallers&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Docker on Windows is cool and makes life easier, but for all of the sacrifices to the Dark Lord, the reality of life in Windows is that the network stack is garbage.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;The workflow that I've been working with for a bit is having the Dockerfile and docker-compose.yml in my git repo for our project, then run the compose for deployments.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I haven't gotten around to using container registries yet, but that's the next improvement I plan on making. Have the container build process be part of a CI pipeline and automatically build and push the container when done. Currently I'm just cloning the git repo and building and running the container from our host.&lt;/p&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/><category term="docker"/><category term="windows"/><category term="devops"/></entry><entry><title>Ghost + NGINX + Docker, OH MY</title><link href="https://www.roylarsen.xyz/ghost-nginx-docker-oh-my.html" rel="alternate"/><published>2018-07-10T04:47:00-04:00</published><updated>2018-07-10T04:47:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2018-07-10:/ghost-nginx-docker-oh-my.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;
&lt;p&gt;&lt;img alt="Ghost + NGINX + Docker, OH MY" src="http://www.roylarsen.xyz/content/images/2018/07/It-s_a_mystery.png"&gt;&lt;/p&gt;
&lt;p&gt;So, I recently decided to redo my website and this is a active work in progress.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Enter:&lt;/p&gt;
&lt;/p&gt;
&lt;h3&gt;DOCKER&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;~WHAAAAAA~&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I've been using this as an excuse to finally get my shit together and actually learn Docker.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Up until now I've done basic docker work (docker run hello-world, etc...), but I …&lt;/p&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;
&lt;p&gt;&lt;img alt="Ghost + NGINX + Docker, OH MY" src="http://www.roylarsen.xyz/content/images/2018/07/It-s_a_mystery.png"&gt;&lt;/p&gt;
&lt;p&gt;So, I recently decided to redo my website and this is a active work in progress.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Enter:&lt;/p&gt;
&lt;/p&gt;
&lt;h3&gt;DOCKER&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;~WHAAAAAA~&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I've been using this as an excuse to finally get my shit together and actually learn Docker.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Up until now I've done basic docker work (docker run hello-world, etc...), but I haven't done anything particularly serious with it.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I'm not going to get into details and explain the internals and how docker works. I'm not super great at that, plus, there ware way smarter people who've beaten that horse to death.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="puttingittogether"&gt;Putting it together&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;So, after some furious research (aka, typing the words "docker", "ghost", and "blog" into google) I found their official docker image.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I then went and set up a new VM in DigitalOcean to act as my docker host with a external volume to hold all the persistent data (blog DB, config info for nginx, etc...) so that all can be easily backed up and I don't have to worry about it.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;At first, my set-up was kinda clunky. I was building containers and running those with all the static content (tastefuldinosaurerotica.com, keybase challenge) pre-baked into the container. For simple set-ups that don't change - this is fine for the most part.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Here was the initial Dockerfile I was using to run nginx:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;FROM nginxMAINTAINER Roy LarsenCOPY nginx.conf /etc/nginx/nginx.confCOPY keybase.txt /usr/share/nginx/htmlCOPY tde/* /usr/share/nginx/html/tde/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;See? Not very exciting, not very portable/maintainable since now I need to maintain my own container that only exists on this one server.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Part of the portability, maintainability problem was was made this blog possible: a container running ghost.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;So, now I have two containers that are essentially running the default software and I'm re-building at least one of them.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="entercompose"&gt;Enter Compose:&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;So, the first big change that I made was create a symlink on the host to be what the containers expect for their persistent storage instead of building the containers with the static content inside of them.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Here's how I'm now defining my containers:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2.2&amp;quot;&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;nginx:latest&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;80:80&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;443:443&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;share&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="n"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;blog&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ghost:1-alpine&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ghost&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ghost&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;roylarsen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xyz&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;defaultnetworks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;external&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;When that file is in place, all you need to do is sudo docker-compose up -d from that directory and you're good to go.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;So, the nice thing about this is that when I want to tackle that first bullet point in Future, I can just have the container hosting certbot place the certs into the volume mapped to /docker/nginx/conf/certs and that will be that.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;For portability, as long as the appropriate drive is mapped and symlink is created - I can always spin up these containers the same way every time. It's pretty sweet.&lt;/p&gt;
&lt;/p&gt;
&lt;h3&gt;Future:&lt;/h3&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;~~Let's Encrypt for doing SSL.~~ Ha, notice how this is served over HTTPS? Need to write that article&lt;/li&gt;
&lt;li&gt;~~&lt;a href="https://mastodon.social/"&gt;https://mastodon.social/&lt;/a&gt;~~ Ha, &lt;a href="https://social.tastefuldinosaurerotica.com"&gt;https://social.tastefuldinosaurerotica.com&lt;/a&gt;. Need to write that up&lt;/li&gt;
&lt;li&gt;Separating internal and external container networks.&lt;/li&gt;
&lt;li&gt;Ansible to prep the host server&lt;/li&gt;
&lt;li&gt;~~Get more serious about Docker on Windows (I know: boo, hiss, etc...)~~ Ha, I wrote this&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/><category term="docker"/><category term="devops"/><category term="cloud"/></entry><entry><title>first</title><link href="https://www.roylarsen.xyz/first.html" rel="alternate"/><published>2018-04-02T03:38:00-04:00</published><updated>2018-04-02T03:38:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2018-04-02:/first.html</id><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;Ayy, lets try blogging on Ghost again.&lt;/p&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/></entry><entry><title>saltstack gitfs</title><link href="https://www.roylarsen.xyz/saltstack-gitfs.html" rel="alternate"/><published>2018-01-09T04:04:00-05:00</published><updated>2018-01-09T04:04:00-05:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2018-01-09:/saltstack-gitfs.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;This is mostly notes for myself, but I wanted to write up an easy reference.&lt;/p&gt;
&lt;/p&gt;
&lt;h3&gt;basics&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;This is to get a basic setup with a map from saltenv to git branch.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;This requires some basic knowlege of saltstack concepts like pillar, salt environments.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;My setup is pretty simplistic, I have …&lt;/p&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;This is mostly notes for myself, but I wanted to write up an easy reference.&lt;/p&gt;
&lt;/p&gt;
&lt;h3&gt;basics&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;This is to get a basic setup with a map from saltenv to git branch.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;This requires some basic knowlege of saltstack concepts like pillar, salt environments.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;My setup is pretty simplistic, I have two main branches: master and dev. The branches are  &lt;/p&gt;
&lt;p&gt;present in the state and pillar repos. Top files are managed per-repo, per-branch (there are  &lt;/p&gt;
&lt;p&gt;probably better ways of handling it, but this is fine for right now).&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Here's the link to the saltstack docs on gitfs: &lt;a href="https://docs.saltstack.com/en/latest/topics/tutorials/gitfs.html"&gt;https://docs.saltstack.com/en/latest/topics/tutorials/gitfs.html&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;For my notes/usage, I use pygit2 and ssh key auth.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;First things first, you need to add your git host key to your master's known hosts:  &lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.saltstack.com/en/latest/topics/tutorials/gitfs.html#adding-the-ssh-host-key-to-the-known-hosts-file"&gt;https://docs.saltstack.com/en/latest/topics/tutorials/gitfs.html#adding-the-ssh-host-key-to-the-known-hosts-file&lt;/a&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;At this point you've created your repos and the branches that you want ot work with. My mapping is pretty simplistic since  &lt;/p&gt;
&lt;p&gt;I just use two branches:  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;branch -&amp;gt; saltenv&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;master -&amp;gt; base  &lt;/p&gt;
&lt;p&gt;dev -&amp;gt; dev  &lt;/p&gt;
&lt;/p&gt;
&lt;h2 id="actualconfigurationstuff"&gt;actual configuration stuff&lt;/h2&gt;
&lt;/p&gt;
&lt;p&gt;So, there are three things that you really need to configure before we're off to the races.  &lt;/p&gt;
&lt;p&gt;These are all in your /etc/salt/master&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Configuring your backend:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;fileserver_backend:  - git  - roots
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;Configuring your state repo:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;gitfs_remotes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//&amp;lt;state git repo address&amp;gt;.git:    - pubkey: /root/.ssh/id_rsa.pub    - privkey: /root/.ssh/id_rsa    - saltenv:      - dev:        - ref: dev      - base:        - ref: master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;In the above config, you configure your saltenv's per remote. I'm assuming you you do this over multiple  &lt;/p&gt;
&lt;p&gt;backend definitions and use saltstack's merging function. I haven't played around with it, so, don't  &lt;/p&gt;
&lt;p&gt;quote me on any of this.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Pillar repo config:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;ext_pillar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;git&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;master&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//&amp;lt;pillar git repo address&amp;gt;.git:      - env: base      - privkey: /root/.ssh/id_rsa      - pubkey: /root/.ssh/id_rsa.pub    - dev ssh://&amp;lt;pillar repo address&amp;gt;.git:      - privkey: /root/.ssh/id_rsa      - pubkey: /root/.ssh/id_rsa.pub&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;If you've noticed, the pillar definition is a tad different from the state definition. Where in the state, you could define the remote once and then map the  &lt;/p&gt;
&lt;p&gt;env to the branch/ref.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;For the pillar definition, you need to define the branch/ref and the repo in the same lone, then map the saltenv under it (if you want to map  &lt;/p&gt;
&lt;p&gt;saltenvs to branches that are named differently - master/base for me)&lt;/p&gt;
&lt;/p&gt;
&lt;h2&gt;conclusion&lt;/h2&gt;
&lt;/p&gt;
&lt;p&gt;That's pretty much it. Restart the salt-master and you should have it all set up:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo salt &amp;#39;*&amp;#39; state.show_top&amp;lt;id&amp;gt;:    ----------    base:       - state       - state&amp;lt;id2&amp;gt;:    ----------    dev:       - state       - state
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;(I removed my company specific info from that output)&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I think my next post will be about my mhqa tool and the Saltstack API&lt;/p&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/><category term="saltstack"/><category term="gitfs"/><category term="git"/></entry><entry><title>devops</title><link href="https://www.roylarsen.xyz/devops.html" rel="alternate"/><published>2017-12-17T03:59:00-05:00</published><updated>2017-12-17T03:59:00-05:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2017-12-17:/devops.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;&lt;img alt="no_idea" src="http://www.roylarsen.xyz/content/images/2018/04/no_idea.jpg"&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Well, not really. I have a pretty decent idea of what's going on.. Trust me, I've got this (mostly)  &lt;/p&gt;
&lt;p&gt;I've also figured out how to do images in Hugo, #dealwtithit&lt;/p&gt;
&lt;/p&gt;
&lt;h3&gt;mhqa&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;I'm currently working on authoring a tool in python, and that's been fun to work  &lt;/p&gt;
&lt;p&gt;on. It's currently pretty …&lt;/p&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;&lt;img alt="no_idea" src="http://www.roylarsen.xyz/content/images/2018/04/no_idea.jpg"&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Well, not really. I have a pretty decent idea of what's going on.. Trust me, I've got this (mostly)  &lt;/p&gt;
&lt;p&gt;I've also figured out how to do images in Hugo, #dealwtithit&lt;/p&gt;
&lt;/p&gt;
&lt;h3&gt;mhqa&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;I'm currently working on authoring a tool in python, and that's been fun to work  &lt;/p&gt;
&lt;p&gt;on. It's currently pretty limited in scope, but soon I should be able to start  &lt;/p&gt;
&lt;p&gt;building things out and really start doing some fun Continuous Deployment/Delivery  &lt;/p&gt;
&lt;p&gt;stuff for our QA people.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Long story short, this tool uses the saltstack API (probably something I'll write a post about in the future)  &lt;/p&gt;
&lt;p&gt;to interact with the vSphere API to revert back to "clean" snapshots and reinstall our backend  &lt;/p&gt;
&lt;p&gt;software with a named version on the CLI. In the future, I'll probably kill off the snapshot part of  &lt;/p&gt;
&lt;p&gt;the tool (because you're not really supposed to use snapshots like that) and just do fresh VMs for installs  &lt;/p&gt;
&lt;p&gt;(and upgrade installs).&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;It'll be exciting to see this tool when I finish it. It's the first real bit of programming I've ever  &lt;/p&gt;
&lt;p&gt;done at work (I think I'm up to \~200 lines of code, probably closer to 300 with the salt runner I wrote  &lt;/p&gt;
&lt;p&gt;to revert snapshots).&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="moarsaltstack"&gt;moar saltstack&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;A big reason I was hired into this role was because I had experience doing using Saltstack. Today (20171215)  &lt;/p&gt;
&lt;p&gt;I spent a bunch of time setting up saltenvs with gitfs branches. Now I can safely/easily work on things in my  &lt;/p&gt;
&lt;p&gt;dev branch and not have to worry about messing things up for people doing real work (not that there are other  &lt;/p&gt;
&lt;p&gt;people doing things yet, but now when people do start working - I can experiment in peace).&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Soon I'll be doing work with &lt;a href="http://www.roylarsen.xyz/posts/proxmox-cloud/"&gt;salt-cloud&lt;/a&gt;, just 100% less Proxmox and 100% more vSphere.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Once I start getting the keys to that kingdom, I'll be writing overwrought autounattend.xml files (I don't think  &lt;/p&gt;
&lt;p&gt;I'll ever be able to adequately express how much I hate XML - it's the worst). But the nice thing about all of that,  &lt;/p&gt;
&lt;p&gt;I'll be able to spin up Windows Server 2012r2 boxes easily.&lt;/p&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/><category term="python"/><category term="mhqa"/><category term="saltstack"/><category term="salt-cloud"/></entry><entry><title>I've got a new job!</title><link href="https://www.roylarsen.xyz/ive-got-a-new-job.html" rel="alternate"/><published>2017-10-09T03:57:00-04:00</published><updated>2017-10-09T03:57:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2017-10-09:/ive-got-a-new-job.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;So, as of 20171009, I'm in my last week of work at Hachette Book Group. Next week I start my new role as DevOps Engineer at Mobile Heartbeat!&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I'm an equal mix of excited, scared, and a little bummed.&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Excited because this is something I've been woring towards for a …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;So, as of 20171009, I'm in my last week of work at Hachette Book Group. Next week I start my new role as DevOps Engineer at Mobile Heartbeat!&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I'm an equal mix of excited, scared, and a little bummed.&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Excited because this is something I've been woring towards for a couple years now (the huge pay bump doesn't hurt)&lt;/li&gt;
&lt;li&gt;Scared because it'll be one of my first more &lt;em&gt;senior&lt;/em&gt; roles I've had&lt;/li&gt;
&lt;li&gt;Bummed because I'm leaving Downtown Boston and I'll be leaving my friends at HBG&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;This job will be a great platform to really start developing my skills in the DevOps space. I'm stoked  &lt;/p&gt;
&lt;p&gt;to work with some amazing people to build and deliver a (tbh) awesome tool in the healthcare space.&lt;/p&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/><category term="mobileheartbeat"/><category term="work"/><category term="devops"/><category term="duckets"/></entry><entry><title>Proxmox + salt-cloud = &lt;kinda3</title><link href="https://www.roylarsen.xyz/proxmox-salt-cloud-kinda3.html" rel="alternate"/><published>2017-06-17T03:53:00-04:00</published><updated>2017-06-17T03:53:00-04:00</updated><author><name>roy.</name></author><id>tag:www.roylarsen.xyz,2017-06-17:/proxmox-salt-cloud-kinda3.html</id><summary type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;So, I've been working on getting salt-cloud playing nicely with Proxmox. The biggest issue I've seen so far is within salt-cloud's proxmox driver is more of a limitation within proxmox itself.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Unless you statically assign you IP address, you're not going to see your IP address in the GUI or …&lt;/p&gt;</summary><content type="html">&lt;!--kg-card-begin: markdown--&gt;

&lt;p&gt;So, I've been working on getting salt-cloud playing nicely with Proxmox. The biggest issue I've seen so far is within salt-cloud's proxmox driver is more of a limitation within proxmox itself.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Unless you statically assign you IP address, you're not going to see your IP address in the GUI or in the standard salt-cloud query to your hypervisor. The proxmox driver also allows your to query DNS for  &lt;/p&gt;
&lt;p&gt;the IP address of your machine.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Neither of those really work for me at home, since I like to have my VMs get an address from DHCP, then convert that to a static reservation when I'm sure that VM will stick around.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;My prefered solution would be to pull the IP address straight from the hypervisor, which would make my life so much easier. That's doable when you're using the VMWare/ESXi driver when the agent is  &lt;/p&gt;
&lt;p&gt;installed, I figure that there should be a way of handling that within Proxmox.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Enter the &lt;a href="https://pve.proxmox.com/wiki/Qemu-guest-agent"&gt;QEMU Guest Agent&lt;/a&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;As it turns out, installing this is pretty easy. You install the service within your target VM, set the Agent option under hardware in the PVE web GUI to 'On', and then you reboot the VM.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Boom. Easy.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;At this point you have better access to the guest through the API (either through the web or pvesh command on the host).&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Now I'm not going to do into details about how long it took me to dig through the PVE API, but I found what I want.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://pve.proxmox.com/pve-docs/api-viewer/index.html"&gt;PVE APIv2 Docs!&lt;/a&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Dig down to nodes/{node}/qemu/{vmid}/agent - That's the API commands for querying the guest agent directly. Not a lot is implemented, however, network-get-interfaces looks promising.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;(spoiler: that's what we want)&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;So playing around with pvesh on my VM host gets me to this command:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pvesh create nodes/technodrome/qemu/101/agent -command network-get-interfaces(yes, my main node name is a reference to TMNT)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;That returns a JSON response with all the network information on the interfaces on your guest:&lt;/p&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;pvesh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;technodrome&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;qemu&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;interfaces200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;result&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hardware-address&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;00:00:00:00:00:00&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ip-addresses&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ip-address&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;127.0.0.1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ip-address-type&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ipv4&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;prefix&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ip-address&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;::1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ip-address-type&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ipv6&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;prefix&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;lo&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hardware-address&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;MAC address&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ip-addresses&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ip-address&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;ipv4 address&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ip-address-type&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ipv4&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;prefix&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ip-address&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;ipv6 address&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ip-address-type&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ipv6&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;prefix&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;eth0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="nx"&gt;sanitised&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;so&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;can&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;see&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;internal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;numbering&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/p&gt;
&lt;h3 id="ohshit"&gt;OH SHIT.&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;So, now the biggest obstacles I have are as follow:&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Getting what I found into: &lt;a href="https://github.com/saltstack/salt/blob/develop/salt/cloud/clouds/proxmox.py"&gt;https://github.com/saltstack/salt/blob/develop/salt/cloud/clouds/proxmox.py&lt;/a&gt;
    &lt;/p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Need to create a function &lt;code&gt;wait_for_ip_address()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Need to add some options to the Proxmox profile to handle being able to use DHCP&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
-   Support having multiple interfaces that aren't lo
-   Get patch accepted into salt-cloud
&lt;/p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(Or I just keep it for myself)&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;h3 id="updatenerds"&gt;UPDATE, NERDS&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;So, as I've been working on the code for my patch to the proxmox provider, I was running some other solutions to my problem in my head.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Enter: &lt;a href="https://cloudinit.readthedocs.io/en/latest/"&gt;cloud-init&lt;/a&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;What I've been realizing that I can do is use cloud-init to run a python scipt to set the hostname of the box.&lt;/p&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Get the VMID of the VM
    &lt;/p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;possibly use the MAC address as my unique identifier?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
-   Use VMID to call /qemu/{VMID}/status/current
-   Potentially use a shell script instead of a Python script to lower the amount of dependencies used&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;h3 id="update2nerds"&gt;Update 2, nerds&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;I think at this point I am going to eventually end up with the cloud-init solution. I think it's the easiest to support and it's &lt;a href="http://cloudinit.readthedocs.io/en/latest/topics/datasources/digitalocean.html"&gt;really&lt;/a&gt; &lt;a href="http://cloudinit.readthedocs.io/en/latest/topics/datasources/ec2.html"&gt;interesting&lt;/a&gt; &lt;a href="http://cloudinit.readthedocs.io/en/latest/topics/datasources/configdrive.html"&gt;what&lt;/a&gt; &lt;a href="http://cloudinit.readthedocs.io/en/latest/topics/datasources/azure.html"&gt;other&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;clouds do.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I'm going to write my patch for the Proxmox provider, but probably just keep that fork as an example in my github account as something to show off. It'll be a fun exercise, but I don't see it as supportable  &lt;/p&gt;
&lt;p&gt;beyond a stopgap until I get cloud-init working.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;Plus, I'm pretty sure cloud-init will be a better solution for when I want to experiment with building LXC containers.&lt;/p&gt;
&lt;/p&gt;
&lt;h3 id="update3nerds"&gt;Update 3, nerds&lt;/h3&gt;
&lt;/p&gt;
&lt;p&gt;So, I've been neglecting my blog because I'm a dummy.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;I've actually written my patch for the proxmox provider and have been using it in my homelab for a while at this point.&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;It was pretty cool, I got to learn how to do list comprehensions and now I can easily spin up new VMs in my lab!&lt;/p&gt;
&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;</content><category term="misc"/><category term="salt-cloud"/><category term="proxmox"/><category term="cloud"/><category term="virtualization"/><category term="saltstack"/></entry></feed>