<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Drew Cain @groksrc on Medium]]></title>
        <description><![CDATA[Stories by Drew Cain @groksrc on Medium]]></description>
        <link>https://medium.com/@groksrc?source=rss-d059ff1b12a3------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*oZfUA_JyNDG2x0FFMZftgA.jpeg</url>
            <title>Stories by Drew Cain @groksrc on Medium</title>
            <link>https://medium.com/@groksrc?source=rss-d059ff1b12a3------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 04:03:52 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@groksrc/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Elixir + Kubernetes =  (Part 3)]]></title>
            <link>https://medium.com/@groksrc/elixir-kubernetes-part-3-9bbd71c9c370?source=rss-d059ff1b12a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/9bbd71c9c370</guid>
            <category><![CDATA[elixir]]></category>
            <category><![CDATA[k8s]]></category>
            <category><![CDATA[k8s-series]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Drew Cain @groksrc]]></dc:creator>
            <pubDate>Fri, 26 Jul 2019 01:00:08 GMT</pubDate>
            <atom:updated>2020-01-08T20:33:30.075Z</atom:updated>
            <content:encoded><![CDATA[<p>How To Set Up and Auto-Scaling Elixir Cluster with Elixir 1.9</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*2KYLHRnV85jhOuYg" /><figcaption>Photo by <a href="https://unsplash.com/@kent_pilcher?utm_source=medium&amp;utm_medium=referral">Kent Pilcher</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><blockquote>This is Part 3 of a three part series on how to create and deploy an Elixir application that will automatically scale on Kubernetes. Be sure to visit <a href="https://medium.com/@groksrc/elixir-plus-kubernetes-part-1-80129eab14f0">Part 1</a> and <a href="https://medium.com/@groksrc/https-medium-com-groksrc-elixir-plus-kubernetes-part-2-a0579473d7f9">Part 2</a> if you need to catch up. If you just want to see the source code it’s available here: <a href="https://github.com/groksrc/el_kube">https://github.com/groksrc/el_kube</a> and if you just want a summary of the commands the slide deck from the original talk is available <a href="https://docs.google.com/presentation/d/1xN2Mi_Q-TfwGHNnJ3OczKTvhq_bFyOcMNBa1H4NPMak/edit?usp=sharing">here</a>.</blockquote><p>In <a href="https://medium.com/@groksrc/elixir-plus-kubernetes-part-1-80129eab14f0">Part 1</a> of this series we created an Elixir app named el_kube that is configured to automatically join an erlang cluster and in <a href="https://medium.com/@groksrc/https-medium-com-groksrc-elixir-plus-kubernetes-part-2-a0579473d7f9">Part 2</a> we created a Docker container for it so that we can run it on Kubernetes (k8s) and tested it, ending on Step 15. Here in Part 3 we’re going resume our previous work and run the container in k8s by way of minikube. Again, all of the commands I show below are issued from the root of the project directory. Let’s get started!</p><h4>16: Create a directory for your k8s files</h4><p>Kubernetes is configurable in a number of different ways, but generally, I recommend using a file-based approach over the CLI. The primary reason is that often you’ll want a history of the commands that have been run on your cluster that the CLI can’t provide you as well as the ability to more easily reason about how you’re configuring the cluster. The CLI command and arguments can become exceedingly verbose when doing anything more than just trivial examples, so I’ll rely on the file-based approach going forward.</p><pre>$ mkdir k8s</pre><h4>17: Create a Persistent Volume Claim</h4><p>If you think about what a k8s cluster is, it’s essentially a set of physical machines, all with some of their own internal storage, as well as a network connection to shared storage, usually like a SAN or something. A database needs a permanent spot to put files that will continue to exist no matter what happens to the running containers or the nodes of the k8s cluster, and that’s what a Persistent Volume Claim (PVC) gives you. Let’s create a new file called k8s/pvc.yaml and then apply it to our minikube cluster.</p><pre>kind: PersistentVolumeClaim<br>apiVersion: v1<br>metadata:<br>  name: postgres-pvc<br>  labels:<br>    app: postgres<br>spec:<br>  accessModes:<br>    - ReadWriteOnce<br>  resources:<br>    requests:<br>      storage: 1Gi</pre><p>This file defines a PVC, sets its name to postgres-pvc and applies the app: postgres label. The labels are arbitrary key/value pairs (which is true on all k8s configs) and the spec that follows sets the accessModes for the container and defines the amount of space. In our case, we’re defining a ReadWriteOnce mode which means that only one connection at a time is allowed, which is fine because we’re only going to have one container (the Postgres DB container) accessing the PVC and we ask for 1 GB of storage.</p><p>Once you have created and saved the file above, issue the following command to add the PVC to the cluster:</p><pre>$ kubectl create -f k8s/pvc.yaml<br>persistentvolumeclaim/postgres-pvc created</pre><h4>18: Create the DB using the PVC</h4><p>Next, let’s define a Deployment for the Postgres database and call it k8s/db.yaml. This config will start a Postgres container and mount the PVC to the mountPath defined under volumeMounts. This way, any DB container that boots will find the files in the same location, which is the location the container expects, and it will pick up where it left off. Here is the content for the db.yaml:</p><pre>apiVersion: apps/v1<br>kind: Deployment<br>metadata:<br>  name: db<br>spec:<br>  replicas: 1<br>  strategy:<br>    type: Recreate<br>  selector:<br>    matchLabels:<br>      app: postgresql<br>  template:<br>    metadata:<br>      creationTimestamp: null<br>      labels:<br>        app: postgresql<br>    spec:<br>      containers:<br>      - env:<br>        - name: POSTGRES_DB<br>          value: el_kube_prod<br>        image: postgres:9.6<br>        name: db<br>        ports:<br>        - containerPort: 5432<br>        resources: {}<br>        volumeMounts:<br>        - mountPath: /var/lib/postgresql/data<br>          name: data<br>      hostname: db<br>      restartPolicy: Always<br>      volumes:<br>        - name: data<br>          persistentVolumeClaim:<br>            claimName: postgres-pvc</pre><p>Notice again we’re passing the POSTGRES_DB environment variable to the container and we’re also declaring a volume named data that uses the PVC we defined above. We’re also applying the app: postgresql label to this container. We’ll see how this comes in to play in the next config file.</p><p>Notice again we’re passing the POSTGRES_DB environment variable to the container and we’re also declaring a volume named data that uses the PVC we defined above. We’re also applying the app: postgresql label to this container. We’ll see how this comes in to play in the next config file.</p><p>Now that the file is saved, let’s create the deployment on the cluster:</p><pre>$ kubectl create -f k8s/db.yaml<br>deployment.extensions/db created</pre><p>At this point you should be able to see that the pod is up and running:</p><pre>$ kubectl get pods<br>NAME                  READY   STATUS    RESTARTS   AGE<br>db-84c67d88cf-d2xss   1/1     Running   0          13s</pre><p>Great! But there’s a problem. This container has a dynamic name and IP address that’s assigned to it and we need a static IP address or DNS name to give to our Phoenix nodes so they know where to connect. What to do? A k8s service will do the trick!</p><h4>19: Create the DB service</h4><p>Back in your editor, let’s now create a file named k8s/db-svc.yaml and add the following content to it:</p><pre>apiVersion: v1<br>kind: Service<br>metadata:<br>  labels:<br>    app: postgresql<br>  name: db<br>spec:<br>  ports:<br>  - name: postgres<br>    port: 5432<br>    targetPort: 5432<br>  selector:<br>    app: postgresql</pre><p>This file defines a k8s Service and gives it the name db . The key point here is that inside a container on the cluster the DNS name of db can be resolved once this service is in place. The spec says that the service will have a port named postgres and containers outside the service will connect on port 5432 while containers under the service will use the targetPort, also 5432. How are containers included as a part of the service? With the selector. Like I mentioned previously, the label app: postgresql is applied to our database container. When the k8s management engine sees that container (pod really) labeled with that key/value pair it will automatically start sending traffic directed at that service to those containers.</p><p>Now that we understand what this does, let’s fire it up on the minikube cluster:</p><pre>$ kubectl create -f k8s/db-svc.yaml<br>service/db created</pre><h4>20: Create the el_kube private and public services</h4><p>Next, let’s create a private Service for the app. This one is going to be a little different than the service for the database though. Remember, the last service assigned a static IP address to the database server. In this case, when we query DNS we don’t want back a static IP. Instead what we’d like to get is a list of IP addresses to give to peerage. That’s how peerage will know what containers in the cluster are running the beam and it will attempt to join them together.</p><p>Create a file name k8s/el-kube-private-svc.yaml and add the following content:</p><pre>apiVersion: v1<br>kind: Service<br>metadata:<br>  name: el-kube-private<br>spec:<br>  clusterIP: None<br>  ports:<br>  - name: epmd<br>    port: 4369<br>  selector:<br>    app: el-kube</pre><p>Here the main difference is that we’re setting the clusterIP to none. This way a single static address is not assigned to the service, but instead, any pods with the label app: el-kube will be returned in the DNS query.</p><p>With this file in place let’s create the service on the minikube cluster:</p><pre>$ kubectl create -f k8s/el-kube-private-svc.yaml</pre><p>Now let’s create a public service so that we can reach the Phoenix app from our host. This service will expose the containers running the elixir application on port 4000 to our host.</p><pre>apiVersion: v1<br>kind: Service<br>metadata:<br>  name: el-kube-public<br>spec:<br>  ports:<br>  - name: http<br>    port: 4000<br>  selector:<br>    app: el-kube<br>  type: LoadBalancer</pre><p>And now create the service in minikube.</p><pre>$ kubectl create -f k8s/el-kube-public-svc.yaml</pre><h4>21: Push the container to the minikube cache</h4><p>At this point we’re almost ready to deploy the app container. But there’s another problem. We haven’t pushed it to a cloud repo anywhere that minikube can pull it from. That’s ok, let’s use a shortcut instead. We can actually just push the image straight into the minikube image cache. Execute the following command to do this:</p><pre>$ minikube cache add el_kube:latest</pre><h4>22: Create the el_kube Deployment</h4><p>Now that the container is in place on the k8s cluster, we’re ready to create the deployment. Switch back to your editor and create a file named k8s/el-kube.yaml. Add the following content to the k8s/el-kube.yaml file:</p><pre>apiVersion: apps/v1<br>kind: Deployment<br>metadata:<br>  name: el-kube-deployment<br>  labels:<br>    app: el-kube<br>spec:<br>  replicas: 3<br>  selector:<br>    matchLabels:<br>      app: el-kube<br>  strategy:<br>    type: RollingUpdate<br>    rollingUpdate:<br>      maxSurge: 34%<br>      maxUnavailable: 34%<br>  template:<br>    metadata:<br>      name: el-kube<br>      labels:<br>        app: el-kube<br>    spec:<br>      containers:<br>      - name: el-kube<br>        image: el_kube:latest<br>        imagePullPolicy: Never<br>        env:<br>          - name: APP_HOST<br>            value: el-kube.com<br>          - name: DB_URL<br>            value: ecto://postgres:postgres@db/el_kube_prod<br>          - name: HOSTNAME<br>            valueFrom:<br>              fieldRef:<br>                fieldPath: status.podIP<br>          - name: PORT<br>            value: &quot;4000&quot;<br>          - name: RELEASE_COOKIE<br>            value: el-kube-secret-cookie<br>          - name: SECRET_KEY_BASE<br>            value: super-secret-key-base<br>          - name: SERVICE_NAME<br>            value: el-kube.default.svc.cluster.local<br>        resources: {}<br>        securityContext:<br>          privileged: false<br>          procMount: Default<br>        terminationMessagePath: /dev/termination-log<br>        terminationMessagePolicy: File<br>      dnsPolicy: ClusterFirst<br>      restartPolicy: Always<br>      schedulerName: default-scheduler<br>      securityContext: {}<br>      terminationGracePeriodSeconds: 30</pre><p>There’s a lot going on here, probably too much to explain in a blog post. Much of this I picked up from other configs, but you should recognize the important bits. We’re creating a Deployment named el-kube-deployment and we’re matching containers with the label app: el-kube We’re also configuring 3 replicas, this is our 3 node cluster, and we’re setting our update strategy. This will tell k8s how many containers to create/terminate at a time so you get a smooth rollout. The spec uses our container label and note we set the imagePullPolicy: Never so that it will use the image we built locally.</p><p>With this file in place let’s fire up the app. Run the following command from your terminal:</p><pre>$ kubectl create -f k8s/el-kube.yaml<br>deployment.apps/el-kube-deployment created</pre><p>Let’s see if it worked!</p><h4>23: Check your work</h4><p>First thing first, make sure your containers (pods) are running:</p><pre>$ kubectl get pods<br>NAME                                 READY   STATUS    RESTARTS   AGE<br>db-84c67d88cf-d2xss                  1/1     Running   0          9m56s<br>el-kube-deployment-ffc6db99c-9p7n5   1/1     Running   0          12s<br>el-kube-deployment-ffc6db99c-dt4f4   1/1     Running   0          12s<br>el-kube-deployment-ffc6db99c-qqd22   1/1     Running   0          12s</pre><p>You should have 4 pods, one DB and 3 el-kube. You should issue this command a couple of times to make sure that they aren’t crashing. If they stay up for 30s or so you should be good.</p><p>Next, let’s see if we can reach the phoenix web application. Minikube has a built-in command that will expose LoadBalancer services. This should open the browser on your machine and show you the running application. To view the phoenix app:</p><pre>$ minikube service el-kube-public</pre><p>Now let’s connect to one and see if the peerage configuration worked. Issue the following command to connect to a running container. Make sure to use the pod&#39;s names your system created, they will be different than mine:</p><pre>$ kubectl exec -it el-kube-deployment-ffc6db99c-9p7n5 sh</pre><p>The sh at the end of the command overrides the default CMD command from the container, so you should be dropped into the shell on that running container in the WORKDIR directory.</p><p>Now, let’s see if we can connect to the beam as we did before in Part 1.</p><pre>/usr/local/el_kube # bin/el_kube remote</pre><pre>Erlang/OTP 22 [erts-10.4.4] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [hipe]</pre><pre>Interactive Elixir (1.9.0) - press Ctrl+C to exit (type h() ENTER for help)</pre><pre>iex(el_kube@172.17.0.9)1&gt;</pre><p>If you see this that is a good sign. You’re now connected remotely to the beam cluster. Let’s list the Nodes and see if everything is hooked up.</p><pre>iex(el_kube@172.17.0.9)1&gt; Node.list<br>[:&quot;el_kube@172.17.0.7&quot;, :&quot;el_kube@172.17.0.8&quot;]</pre><p>If you get two nodes back, stop and breathe in that sweet, sweet success. You’ve done it! Don’t be surprised that there are only two nodes in the list. Node.list/0 returns remote nodes, not the node you’re currently connected to, so everything is working.</p><p>Next, let’s make sure the database is connected:</p><pre>iex(el_kube@172.17.0.9)2&gt; ElKube.Repo.query(&quot;select 1 as testing&quot;)<br>{:ok,<br> %Postgrex.Result{<br>   columns: [&quot;test&quot;],<br>   command: :select,<br>   connection_id: 95,<br>   messages: [],<br>   num_rows: 1,<br>   rows: [[1]]<br> }}</pre><p>If you get back the ok tuple then you’re golden. Everything is connected and beers are to be had all around. 🍻</p><p>But wait! There’s more! Wouldn’t it be incredible if we could just change the el-kube-deployment config and automatically add new nodes to the erlang cluster? Well, we can!</p><h4>24: Grow your cluster’s cluster</h4><p>Let’s change the number of deployment replicas to 5 and make sure that the erlang cluster connects:</p><pre>$ kubectl scale deployment el-kube-deployment --replicas 5<br>deployment.apps/el-kube-deployment scaled</pre><p>Let’s see if the new pods came up:</p><pre>$ kubectl get pods<br>NAME                                 READY   STATUS    RESTARTS   AGE<br>db-84c67d88cf-d2xss                  1/1     Running   0          12m<br>el-kube-deployment-ffc6db99c-4nw5k   1/1     Running   0          6s<br>el-kube-deployment-ffc6db99c-9p7n5   1/1     Running   0          2m49s<br>el-kube-deployment-ffc6db99c-dt4f4   1/1     Running   0          2m49s<br>el-kube-deployment-ffc6db99c-gzwqf   1/1     Running   0          6s<br>el-kube-deployment-ffc6db99c-qqd22   1/1     Running   0          2m49s</pre><p>They did! Did they connect??</p><pre>$ kubectl exec -it el-kube-deployment-ffc6db99c-9p7n5 sh<br>/usr/local/el_kube # bin/el_kube remote<br>Erlang/OTP 22 [erts-10.4.4] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [hipe]</pre><pre>Interactive Elixir (1.9.0) - press Ctrl+C to exit (type h() ENTER for help)<br>iex(el_kube@172.17.0.8)1&gt; Node.list<br>[:&quot;el_kube@172.17.0.6&quot;, :&quot;el_kube@172.17.0.7&quot;, :&quot;el_kube@172.17.0.9&quot;,<br> :&quot;el_kube@172.17.0.10&quot;]</pre><p>They did!!! 🎉 🎉 🎉</p><p>Ahh, that’s the stuff. That’s why I do this job. So I hope you’ve enjoyed this series. Here are a few resources to follow up with if you’d like to dive a little deeper.</p><h4>Resources</h4><p>The <em>In Action</em> series by Manning was incredibly helpful in getting me up to speed on all of the technologies I’ve covered in this series. I highly recommend them:</p><ul><li><a href="https://www.manning.com/books/elixir-in-action-second-edition">https://www.manning.com/books/elixir-in-action-second-edition</a></li><li><a href="https://www.manning.com/books/kubernetes-in-action">https://www.manning.com/books/kubernetes-in-action</a></li><li><a href="https://www.manning.com/books/docker-in-action-second-edition">https://www.manning.com/books/docker-in-action-second-edition</a></li></ul><p>If you’re stuck on versions of Elixir before 1.9, don’t worry, there’s hope. I actually originally implemented this in 1.8.1 with Distillery right before 1.9 dropped. It’s a little harder to work with but this is still definitely doable using the concepts applied here. Do check out Distillery if that’s the way you need to go: <a href="https://github.com/bitwalker/distillery">https://github.com/bitwalker/distillery</a></p><p>I also found these articles helpful in my quest, I stand upon the shoulder of giants:</p><ul><li><a href="https://medium.com/polyscribe/a-complete-guide-to-deploying-elixir-phoenix-applications-on-kubernetes-part-1-setting-up-d88b35b64dcd">https://medium.com/polyscribe/a-complete-guide-to-deploying-elixir-phoenix-applications-on-kubernetes-part-1-setting-up-d88b35b64dcd</a></li><li><a href="https://stackoverflow.com/questions/42564058/how-to-use-local-docker-images-with-minikube">https://stackoverflow.com/questions/42564058/how-to-use-local-docker-images-with-minikube</a></li><li><a href="https://stackoverflow.com/questions/52890718/kubectl-run-is-deprecated-looking-for-alternative">https://stackoverflow.com/questions/52890718/kubectl-run-is-deprecated-looking-for-alternative</a></li><li><a href="https://elixir-lang.org/blog/2019/06/24/elixir-v1-9-0-released/">https://elixir-lang.org/blog/2019/06/24/elixir-v1-9-0-released/</a></li></ul><p>And that’s about it. I hope you’ve enjoyed reading this series and it’s helpful in your day-to-day. It took me quite a while to piece all of this together, so hopefully, you don’t have to. Enjoy!</p><p>-g</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9bbd71c9c370" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elixir + Kubernetes =  (Part 2)]]></title>
            <link>https://medium.com/@groksrc/https-medium-com-groksrc-elixir-plus-kubernetes-part-2-a0579473d7f9?source=rss-d059ff1b12a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/a0579473d7f9</guid>
            <category><![CDATA[elixir]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[k8s-series]]></category>
            <category><![CDATA[kubernetes]]></category>
            <dc:creator><![CDATA[Drew Cain @groksrc]]></dc:creator>
            <pubDate>Fri, 26 Jul 2019 00:59:24 GMT</pubDate>
            <atom:updated>2020-01-08T20:42:43.151Z</atom:updated>
            <content:encoded><![CDATA[<p>How To Set Up and Auto-Scaling Elixir Cluster with Elixir 1.9</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*FsyBcF9m7hAWvZ5y" /><figcaption>Photo by <a href="https://unsplash.com/@jlcruz_photography?utm_source=medium&amp;utm_medium=referral">Jordan Lomibao</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><blockquote>This is Part 2 of a three part series on how to create and deploy an Elixir application that will automatically scale on Kubernetes. <a href="https://medium.com/@groksrc/elixir-plus-kubernetes-part-1-80129eab14f0">Part 1</a> and <a href="https://medium.com/@groksrc/elixir-kubernetes-part-3-9bbd71c9c370">Part 3</a> are also available. If you just want to see the source code it’s available here: <a href="https://github.com/groksrc/el_kube">https://github.com/groksrc/el_kube</a> and if you just want a summary of the commands the slide deck from the original talk is available <a href="https://docs.google.com/presentation/d/1xN2Mi_Q-TfwGHNnJ3OczKTvhq_bFyOcMNBa1H4NPMak/edit?usp=sharing">here</a>.</blockquote><p>In <a href="https://medium.com/@groksrc/elixir-plus-kubernetes-part-1-80129eab14f0">Part 1</a> of this series, we created an Elixir application that is ready to be deployed in an erlang cluster configuration. The next step (Step 12) is to create the Docker container for the app and make sure everything spins up. In this post I’m using a default install of the latest Docker for Mac, but this should work on Linux and Windows as well.</p><h4>12: Create the Dockerfile and .dockerignore</h4><p>Let’s start by creating a Dockerfile and .dockerignore in the root of the folder:</p><pre>$ touch Dockerfile &amp;&amp; touch .dockerignore</pre><h4>13: Edit the Dockerfile and .dockerignore</h4><p>Now paste in the content for the Dockerfile and we’ll look at what it does:</p><pre>FROM elixir:1.9 AS builder</pre><pre>ENV MIX_ENV=prod</pre><pre>WORKDIR /usr/local/el_kube</pre><pre># This step installs all the build tools we&#39;ll need<br>RUN curl -sL <a href="https://deb.nodesource.com/setup_13.x">https://deb.nodesource.com/setup_13.x</a> | bash - &amp;&amp; \<br>    apt-get install -y nodejs &amp;&amp; \<br>    mix local.rebar --force &amp;&amp; \<br>    mix local.hex --force</pre><pre># Copies our app source code into the build container<br>COPY . .</pre><pre># Compile Elixir<br>RUN mix do deps.get, deps.compile, compile</pre><pre># Compile Javascript<br>RUN cd assets \<br>    &amp;&amp; npm install \<br>    &amp;&amp; ./node_modules/webpack/bin/webpack.js --mode production \<br>    &amp;&amp; cd .. \<br>    &amp;&amp; mix phx.digest</pre><pre># Build Release<br>RUN mkdir -p /opt/release \<br>    &amp;&amp; mix release \<br>    &amp;&amp; mv _build/${MIX_ENV}/rel/el_kube /opt/release</pre><pre># Create the runtime container<br>FROM erlang:22 as runtime</pre><pre>WORKDIR /usr/local/el_kube</pre><pre>COPY --from=builder /opt/release/el_kube .</pre><pre>CMD [ &quot;bin/el_kube&quot;, &quot;start&quot; ]</pre><pre>HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=2 \<br> CMD nc -vz -w 2 localhost 4000 || exit 1</pre><p>At the top of the file we start with an Elixir base container image and set our MIX_ENV environment variable to prod and configure our working directory to /usr/local/el_kube</p><p>For those not familiar with Docker, you can think of it like this. The docker runtime reads in the Dockerfile and executes each command from top to bottom. So first it starts the base container image, then it sets the environment variable. The WORKDIR command creates a directory at that path and changes into it.</p><p>We then execute the bash script that follows the RUN command to add our dependencies necessary to compile the app.</p><p>Next we COPY the code that’s in our working directory (where we ran the command) into the container and compile it. Then we compile the javascript and call the mix release command to generate the release.</p><blockquote>ProTip: Don’t forget the .dockerignore file. You don’t want the _build directory from your development host winding up in the container. We’ll add a .dockeringore next.</blockquote><p>Next, you find another FROM command. What is this you ask? Great question! It’s a two-phase container build. Huh? Right. So each line of the Dockerfile creates a “layer” in Docker parlance that is used to compose the container. In our release container we don’t actually want or need all of the dependencies necessary to compile the app. We just want the output of all of that.</p><p>So in the first phase of the build we compile, and in the second phase we copy the results from the first phase and include only the dependencies we need to actually execute the application. The second phase of the container build process starts with the second FROM command and pulls the base erlang:22-alpine image (which is the base of the first container) and copies in the one dependency on gcc along with the output from the first phase of the build.</p><p>Then we set the CMD to execute at startup and end with a HEALTHCHECK. The HEALTHCHECK here is didactic, you don’t really need it but it’s nice to show. Kubernetes will run the command in the health check at the configured interval, and if it fails the specified number of times it will kill the container. In practical terms what it means for this project is that the container would fail and a new one would be brought up and automatically rejoin the cluster. Pretty cool!</p><p>Now that we’ve looked at the Dockerfile let’s throw in a few lines to the .dockerignore:</p><pre>_build/<br>.elixir_ls/<br>.git/<br>.vscode/<br>deps/<br>priv/static/<br>k8s/<br>test/<br>.dockerignore<br>.env<br>.formatter.exs<br>.gitignore<br>.travis.yml<br>Dockerfile<br>README.md</pre><p>This is my .dockerignore so customize as you see fit. Just don’t remove that top line! Now we’re ready to build the container.</p><h4>14: Build the container</h4><p>Issue the following command to build the container. You should see the success messages that follow.</p><pre>$ docker build -t el_kube:latest .<br>...<br>Successfully built 1a5ed90cc8c0<br>Successfully tagged el_kube:latest</pre><h4>15: Smoke test the container</h4><p>Now let’s see if it works. We’re going to use Docker to create a private network, a Postgres container and our app container to make sure we’re all communicating. The benefit of creating the private docker network is that all containers on the network will be able to access any port, and you’re not going to conflict with your host. You could, of course, use the default bridge network, but then you’d have to use the deprecated --link argument to get the containers talking. More details about this are available in the <a href="https://docs.docker.com/network/bridge/">docker network docs</a>.</p><pre>$ docker network create el-kube-net<br>80a3e59c2cc82c7d2e4aff2d2a8a2daf14f3731f0e7604777be5f925b99b0006</pre><p>Now that the network is available, let’s start a Postgres container and join it to the el-kube-net network.</p><pre>$ docker run --rm -d \<br>  -h db \<br>  -e POSTGRES_DB=el_kube_prod \<br>  -p 5432 \<br>  --name db \<br>  --network el-kube-net \<br>  postgres:9.6<br>116714d3b9b03f8730bd2c942d092ea3c2ad0d97fc955e656e8e73c96ca40ca0</pre><p>Here we’re telling docker to run the postgres:9.6 image. The --rm flag will remove the container files when the container stops. The -d flag sets it to run as a daemon. The -h flag is for the DNS name of the container inside the el-kube-net network and setting this allows other containers to refer to it as db. The -e flag is nice because here we’re passing the POSTGRES_DB environment variable. The Postgres container will look for this variable to be set and if it finds it then it will automatically create a database with the supplied name after the database engine starts. No need to issue a CREATE DATABASE command.</p><p>The --name flag tells Docker what this container will be named. That’s different than the -h flag which is how it’s referred to inside the network. This is from outside the network at the terminal on your Docker host. And finally, the --network flag joins the container to the user-defined bridge network we created above.</p><p>Finally! Let’s start the app. We pass all of the environment variables describe in Part 1 of the series, in addition to the --network and --publish flags. The --publish flag I didn’t cover previously, it exposes port 4000 on the host (outside the docker network) to whatever is bound to port 4000 inside the network, allowing traffic to pass through.</p><pre>$ docker run -it -d --rm \<br>-e DB_URL=ecto://postgres:postgres@db/el_kube_prod \<br>-e RELEASE_COOKIE=secret-cookie \<br>-e SECRET_KEY_BASE=your-secret-key \<br>-e SERVICE_NAME=el-kube \<br>-e APP_HOST=localhost \<br>-e PORT=4000 \<br>--network el-kube-net --publish 4000:4000 el_kube:latest<br>f6cba46a29c079284b7625554105640e4a5beaff1bdf417d6fa623c26948d26b</pre><p>If you got that final hash back it’s a good sign. Let’s make sure our containers are up:</p><pre>$ docker container ls <br>CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS                            PORTS                     NAMES<br>f6cba46a29c0        el_kube:latest      &quot;bin/el_kube start&quot;      9 seconds ago        Up 8 seconds (health: starting)   0.0.0.0:4000-&gt;4000/tcp    reverent_hugle<br>116714d3b9b0        postgres:9.6        &quot;docker-entrypoint.s…&quot;   About a minute ago   Up About a minute                 0.0.0.0:32768-&gt;5432/tcp   db</pre><p>This is a bit ugly, but if you see two containers you’re in pretty good shape. Make sure you can again browse to <a href="http://localhost:4000/">http://localhost:4000</a>. If so, you’re in business. The container is working and now we’re ready to spin up on Kubernetes.</p><blockquote>EDIT 01/2020: I recently updated the source code to make it easier to smoke test the application. Now all you need to do is:</blockquote><blockquote>$ docker-compose up</blockquote><blockquote>See the source code repository for the docker-compose.yaml and docker.env files that make this work</blockquote><p>In <a href="https://medium.com/@groksrc/elixir-kubernetes-part-3-9bbd71c9c370">Part 3</a> of the series, we’ll put these first two pieces together and get our erlang cluster running on our minikube cluster. It’s a cluster of clusters. You might even say, “it’s a real cluster.”</p><p>-g</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a0579473d7f9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elixir + Kubernetes =  (Part 1)]]></title>
            <link>https://itnext.io/elixir-plus-kubernetes-part-1-80129eab14f0?source=rss-d059ff1b12a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/80129eab14f0</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[k8s-series]]></category>
            <category><![CDATA[elixir]]></category>
            <dc:creator><![CDATA[Drew Cain @groksrc]]></dc:creator>
            <pubDate>Fri, 26 Jul 2019 00:58:25 GMT</pubDate>
            <atom:updated>2020-01-08T20:48:49.954Z</atom:updated>
            <content:encoded><![CDATA[<p>How To Set Up and Auto-Scaling Elixir Cluster with Elixir 1.9</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/919/1*pyeXtobbr8z8mAQNIi2xvA.png" /></figure><blockquote>This is Part 1 of a three part series on how to create and deploy an Elixir application that will automatically scale on Kubernetes. <a href="https://medium.com/@groksrc/https-medium-com-groksrc-elixir-plus-kubernetes-part-2-a0579473d7f9">Part 2</a> and <a href="https://medium.com/@groksrc/elixir-kubernetes-part-3-9bbd71c9c370">Part 3</a> are also available. If you just want to see the source code it’s available here: <a href="https://github.com/groksrc/el_kube">https://github.com/groksrc/el_kube</a> and if you just want a summary of the commands the slide deck from the original talk is available <a href="https://docs.google.com/presentation/d/1xN2Mi_Q-TfwGHNnJ3OczKTvhq_bFyOcMNBa1H4NPMak/edit?usp=sharing">here</a>.</blockquote><p>This series is designed to show you how to create an auto-scaling Elixir cluster using Elixir 1.9 and Kubernetes. What does that mean? Well, it means that the application will start, automatically connect in an erlang cluster configuration, and then automatically add and remove erlang nodes as the Kubernetes (k8s) configuration changes.</p><p>In Part 1 we’ll look at creating the application itself. Part 2 will show you how to Dockerize the application and confirm that the container works. And finally in Part 3 we will launch the application on Kubernetes using minikube and demonstrate how it automatically scales as the deployment changes. Let’s get started!</p><h3>Part 1 — Creating the App</h3><p>Before you begin you’ll want to make sure that you have <a href="https://elixir-lang.org/install.html">Elixir 1.9</a> installed, along with OTP 22. You’ll also want to make sure that you’re using the current version of <a href="https://hexdocs.pm/phoenix/installation.html#content">Phoenix</a> which is v1.4.9 at the time of this writing. Also, make sure that <a href="https://github.com/kubernetes/minikube">minikube</a> is installed and <a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/">kubectl</a> is configured to use the minikube context.</p><p>Our application is going to be named el_kube and as an aside, all of this code is available in my repo at <a href="https://github.com/groksrc/el_kube">https://github.com/groksrc/el_kube</a>. I start with an empty project at master, then at each branch I change only the files necessary to proceed to the next step. I originally presented this work at a Simpli.fi tech talk so you should be able to issue the commands and make the file changes straight from the <a href="https://docs.google.com/presentation/d/1xN2Mi_Q-TfwGHNnJ3OczKTvhq_bFyOcMNBa1H4NPMak/edit?usp=sharing">slide deck</a> as well. If you run into any trouble, send me a reply and I’ll try to help out.</p><h4>00: New Project</h4><p>From the working directory on your development machine let’s start a new Phoenix project. I called mine el_kube because elixir and Kubernetes mashed up you know.</p><pre>$ mix phx.new el_kube</pre><p>Be sure to say Yes when asked to “Fetch and install dependencies? [Yn]”</p><h4>01: Open the project</h4><p>Now let’s switch into the project folder and open our editor. All of the following commands are issued from the project folder’s root directory. Also, I’m using <a href="https://code.visualstudio.com/">vscode</a> with the ElixirLS plugin, so you’re working with git you will probably want to add the .elixir_ls/ folder to the .gitignore.</p><pre>$ cd el_kube &amp;&amp; code .</pre><h4>02: Edit mix.exs</h4><p>Once the project is open in your editor pop over to the mix.exs file that’s at the project root. We’re going to make three changes here.</p><p>First, update the project key from 1.5 to 1.9. Using Elixir 1.9 is required for this project.</p><pre>elixir: &quot;~&gt; 1.9&quot;,</pre><p>Next, we’re going to add a dependency on peerage and set <a href="https://github.com/mrluc/peerage">peerage</a> to boot as an extra_application on startup. Like it says in its readme, Peerage helps your erlang nodes find each other. We’ll be using it to provide the secret sauce for getting this to work.</p><pre>def application do<br>  [<br>    mod: {ElKube.Application, []},<br>    extra_applications: [:logger, :runtime_tools, :peerage]<br>  ]<br>end</pre><p>And...</p><pre>defp deps do<br>    [<br>      {:phoenix, &quot;~&gt; 1.4.9&quot;},<br>      {:phoenix_pubsub, &quot;~&gt; 1.1&quot;},<br>      {:phoenix_ecto, &quot;~&gt; 4.0&quot;},<br>      {:ecto_sql, &quot;~&gt; 3.1&quot;},<br>      {:postgrex, &quot;&gt;= 0.0.0&quot;},<br>      {:phoenix_html, &quot;~&gt; 2.11&quot;},<br>      {:phoenix_live_reload, &quot;~&gt; 1.2&quot;, only: :dev},<br>      {:gettext, &quot;~&gt; 0.11&quot;},<br>      {:jason, &quot;~&gt; 1.0&quot;},<br>      {:plug_cowboy, &quot;~&gt; 2.0&quot;},<br>      {:peerage, &quot;~&gt; 1.0&quot;}<br>    ]<br>  end</pre><p>Once the three changes are mix.exs you can save it and close it up. Don’t forget to run $ mix deps.get if your editor doesn’t do that for you.</p><h4>02: Initialize the release</h4><p>Next, we’re going to use the new mix command to generate some template files for us. These template files are used to help generate the scripts which are executed when the application starts up.</p><pre>$ mix release.init<br>* creating rel/vm.args.eex<br>* creating rel/env.sh.eex<br>* creating rel/env.bat.eex</pre><p>This command generates three different Elixir templates for you in the rel directory. We’ll ignore the env.bat.eex. I’m not on windows, but if I were I would need to apply the relevant changes there. Instead, I’m going to update env.sh.eex to set some environment variables for me at startup.</p><h4>03: Update rel/env.sh.eex</h4><p>Inside rel/env.sh.eex we’re going to uncomment the lines that export RELEASE_DISTRIBUTION and RELEASE_NODE. Then change the localhost IP to an environment variable that will be passed dynamically. Make the following changes to the file:</p><pre>export RELEASE_DISTRIBUTION=name<br>export RELEASE_NODE=&lt;%= @release.name %&gt;@${HOSTNAME}</pre><p>Configuring the RELEASE_DISTRIBUTION environment variable to name will configure the erlang beam to be able to connect to other nodes in the cluster using the long name format. The RELEASE_NODE is the name of the erlang node using the long name format. See the mix <a href="https://hexdocs.pm/mix/Mix.Tasks.Release.html#module-environment-variables">task docs</a> for more details on configuring these environment variables.</p><p>For our purposes, we’re going to dynamically pass in the IP address that k8s assigns to the container as the HOSTNAME but you can use a valid cluster DNS name here as well. Save that file up and we’re on to the next.</p><h4>04: Remove config/prod.secret.exs</h4><p>This file won’t be used, so we’re going to drop it.</p><pre>$ rm config/prod.secret.exs</pre><h4>05: Create config/releases.exs</h4><p>Instead, we’re going to use the new releases.exs file to help us dynamically configure our application at startup. Let’s create the file and open it in our editor.</p><pre>$ touch config/releases.exs</pre><h4>06: Edit config/releases.exs</h4><p>Now we’re ready to set up our dynamic configuration. Paste this into the file:</p><pre>import Config<br><br>service_name = System.fetch_env!(&quot;SERVICE_NAME&quot;)<br>db_url = System.fetch_env!(&quot;DB_URL&quot;)<br>secret_key_base = System.fetch_env!(&quot;SECRET_KEY_BASE&quot;)<br>port = System.fetch_env!(&quot;PORT&quot;)<br><br>config :el_kube, ElKube.Repo, url: db_url<br><br>config :el_kube, ElKubeWeb.Endpoint,<br>  http: [port: port],<br>  secret_key_base: secret_key_base,<br>  url: [host: {:system, &quot;APP_HOST&quot;}, port: {:system, &quot;PORT&quot;}]<br><br>config :peerage, via: Peerage.Via.Dns,<br>  dns_name: service_name,<br>  app_name: &quot;el_kube&quot;</pre><p>The service_name is going to be the internal DNS name of the application. In other words, a node on the cluster will be able to query DNS for the service_name and get a response with a list of IP addresses.</p><p>The db_url is the Postgres connection string. The secret_key_base is used for Phoenix to sign the cookies and the port is the port the web endpoint will listen on.</p><p>You’ll also notice there’s an APP_HOST environment variable sprinkled in to the ElKubeWeb.Endpoint as well. Phoenix is a bit of an odd-ball because it did allow some dynamic configuration on its own prior to Elixir 1.9. Here we’re just using that built-in construct to tell Phoenix to pull APP_HOST and PORT from the environment. These two values are used for its internal URL helpers. Save this file up, on to the next.</p><h4>07: Edit config/prod.exs</h4><p>Now let’s look at the production config file. As in prior Elixir releases, the environment specific configs are layered on top of the base config/config.exs, so here we can set some production specific stuff that can’t be changed. First, we need to remove the url key that’s been moved into config/releases.exs and next we need to add the endpoint config key/value pair server: true</p><p>This tells Phoenix to start the webserver endpoint when the server starts up. Why that’s not the default in this file I’m not sure, but let’s make those changes and move on. Your config/prod.exs file should now look like this.</p><pre>use Mix.Config</pre><pre># Lots of comments ...<br>config :el_kube, ElKubeWeb.Endpoint,<br>  cache_static_manifest: &quot;priv/static/cache_manifest.json&quot;,<br>  server: true</pre><pre># Do not print debug messages in production<br>config :logger, level: :info</pre><pre># Lots more comments ...</pre><p>Oh! And don’t forget to scroll down to the very bottom of the file and delete the import_config &quot;config/prod.secrets.exs&quot; I missed that on several run-throughs, so that’s why we deleted it earlier. It will now complain at us if we forget this step.</p><h4>08: Edit config/config.exs</h4><p>This step isn’t completely necessary. Of course you can configure your application however you want in real life. But for demonstration purposes we’re going to drop in some base Ecto config just so we can show that it does indeed get picked up. Open up the config/config.exs file and add the following:</p><pre>config :el_kube, ElKube.Repo,<br>  adapter: Ecto.Adapters.Postgres,<br>  pool_size: 10</pre><p>Again, all or none of this could be in here. In real life you’d probably want your pool_size to be configured from config/releases.exs so this is purely didactic.</p><h4>09: Edit config/dev.exs</h4><p>One last file change and we’ll be ready to start compiling. Let’s make peerage happy in the development environment, and also because we typically don’t run a cluster in development mode (which peerage complains vociferously about) let’s tell it to be quiet. Add the following to your config/dev.exs file:</p><pre>config :peerage,<br>  via: Peerage.Via.List,<br>  node_list: [:&quot;el_kube@127.0.0.1&quot;],<br>  log_results: false</pre><h4>10: Compile</h4><p>Now let’s switch to our terminal and execute the compilation and smoke tests. First we need to generate the digest files for the javascript side of Phoenix to be happy.</p><pre>$ mix phx.digest<br>...<br>==&gt; el_kube<br>Check your digested files at &quot;priv/static&quot;</pre><p>Next let’s generate the release:</p><pre>$ MIX_ENV=prod mix release<br>...<br>...<br>To list all commands:</pre><pre>  _build/prod/rel/el_kube/bin/el_kube</pre><p>If you see the above, you’re making great progress! The last step is to create a database for our application to connect when it starts up.</p><pre>$ mix ecto.create<br>The database for ElKube.Repo has been created</pre><p>Now we’re ready to run the release and make sure it fires up. This command has to pass all of the environment variables we considered earlier in addition to one I didn’t talk about, the RELEASE_COOKIE. This one is also covered in the mix release task docs, but I wanted to touch on it here. The RELEASE_COOKIE is a sort of pre-shared key that the erlang beam uses to authenticate when nodes connect. It’s not strictly necessary here, but I wanted to introduce it here because you’ll be seeing it again in Part 3.</p><h4>11: Smoke Test</h4><p>Here is the command to start the app:</p><pre>DB_URL=ecto://postgres:postgres@localhost/el_kube_dev \<br>RELEASE_COOKIE=foo \<br>SECRET_KEY_BASE=foo \<br>HOSTNAME=127.0.0.1 \<br>SERVICE_NAME=localhost.svc \<br>APP_HOST=localhost \<br>PORT=4000 \<br>_build/prod/rel/el_kube/bin/el_kube start</pre><p>We covered what all of these environment variables do above so I won’t go over that again. What’s new here is that we’re actually executing our application and passing the start command. When you do this you should see the following:</p><pre>15:20:47.765 [info] Running ElKubeWeb.Endpoint with cowboy 2.6.3 at 0.0.0.0:4000 (http)<br>15:20:47.765 [info] Access ElKubeWeb.Endpoint at <a href="http://localhost:4000/">http://localhost:4000</a></pre><p>Now you can open your browser to that address or $ curl <a href="http://localhost:4000/">http://localhost:4000</a> and you should see a webpage come back. If it does then you’re in business!</p><p>Next, let’s confirm our database connectivity is working by launching another terminal and connecting to this running instance. Everything is the same as the previous command except this time instead of passing start at the end we’ll pass theremote argument:</p><pre>DB_URL=ecto://postgres:postgres@localhost/el_kube_dev \<br>RELEASE_COOKIE=foo \<br>SECRET_KEY_BASE=foo \<br>HOSTNAME=127.0.0.1 \<br>SERVICE_NAME=localhost.svc \<br>APP_HOST=localhost \<br>PORT=4000 \<br>_build/prod/rel/el_kube/bin/el_kube remote</pre><p>Here you should be dropped into an elixir prompt:</p><pre>Erlang/OTP 22 [erts-10.4.4] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe] [dtrace]</pre><pre>Interactive Elixir (1.9.1) - press Ctrl+C to exit (type h() ENTER for help)<br>iex(el_kube@127.0.0.1)1&gt;</pre><p>At this point you can issue an Ecto command and you should get back an :ok tuple.</p><pre>iex(el_kube@127.0.0.1)1&gt; ElKube.Repo.query(&quot;select 1 as test&quot;)<br>{:ok,<br> %Postgrex.Result{<br>   columns: [&quot;test&quot;],<br>   command: :select,<br>   connection_id: 41993,<br>   messages: [],<br>   num_rows: 1,<br>   rows: [[1]]<br> }}</pre><p>If you made it this far, congratulations! You’re now ready to move on to <a href="https://medium.com/@groksrc/https-medium-com-groksrc-elixir-plus-kubernetes-part-2-a0579473d7f9">Part 2</a> and Dockerize your application.</p><p>-g</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=80129eab14f0" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/elixir-plus-kubernetes-part-1-80129eab14f0">Elixir + Kubernetes = 💜 (Part 1)</a> was originally published in <a href="https://itnext.io/">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Create an OpenSSL self-signed SAN cert in a single command]]></title>
            <link>https://medium.com/@groksrc/create-an-openssl-self-signed-san-cert-in-a-single-command-627fd771f25?source=rss-d059ff1b12a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/627fd771f25</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[ssl]]></category>
            <dc:creator><![CDATA[Drew Cain @groksrc]]></dc:creator>
            <pubDate>Tue, 05 Mar 2019 16:56:23 GMT</pubDate>
            <atom:updated>2019-03-05T16:57:04.703Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Note: This is mainly for my future self. Hopefully, you’ll find it useful too.</em></p><p>I’m currently working on a project that requires SSL on my development web server. Setting up a self-signed certificate with OpenSSL is reasonably straightforward and that had been working for a while. But then the requirement was added that the hostname could not be ‘localhost’. 🙄 Blame the vendor. Hello SAN (Subject Alternative Name) cert.</p><p>I found many examples online about how to do this with a config file, but I needed this to work in a simple one-liner. So here it is:</p><pre>openssl req \<br>-x509 \<br>-newkey rsa:4096 \<br>-sha256 \<br>-days 3560 \<br>-nodes \<br>-keyout example.key \<br>-out example.crt \<br>-subj &#39;/CN=&lt;Your Cert Name&gt;&#39; \<br>-extensions san \<br>-config &lt;( \<br>  echo &#39;[req]&#39;; \<br>  echo &#39;distinguished_name=req&#39;; \<br>  echo &#39;[san]&#39;; \<br>  echo &#39;subjectAltName=DNS:localhost,&lt;yourdomain&gt;&#39;)</pre><p>Just replace &lt;Your Cert Name&gt; with what you want to appear as the title, and &lt;yourdomain&gt; with the domain you want to impersonate. You can, of course, add additional domain names to the list. Just separate them with a comma (no space). You’ll then need to import the certificate to your certificate store and set the trust options according to your environment. That’s it!</p><p>-g</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=627fd771f25" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ProTip: How To Split Large Branches Into Small Pull Requests]]></title>
            <link>https://medium.com/@groksrc/protip-how-to-split-large-branches-into-small-pull-requests-81d607660c05?source=rss-d059ff1b12a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/81d607660c05</guid>
            <category><![CDATA[git]]></category>
            <dc:creator><![CDATA[Drew Cain @groksrc]]></dc:creator>
            <pubDate>Fri, 19 Oct 2018 19:33:44 GMT</pubDate>
            <atom:updated>2018-10-19T19:33:44.675Z</atom:updated>
            <content:encoded><![CDATA[<p>Large PRs are hard to review for a number of reasons I won’t go into, but let’s assume that the axiom is true that smaller PRs are better. One strategy I use to split a large feature branch into small PRs is detailed here. To keep things simple for the example I’m assuming that we’re branching from master to create feature branches.</p><h3>Step 1: Create a patch against your base branch</h3><pre>$ git diff master my_large_feature_branch &gt; ../my_pr_patch</pre><h3>Step 2: Checkout a new branch</h3><p>Create a new branch that will contain a subset of the changes in my_large_feature_branch</p><pre>$ git checkout master<br>$ git checkout -b small_pr_one</pre><h3>Step 3: Apply Changes Selectively</h3><p>Now apply the patch to your new small_pr_one branch. This will apply all of the changes from my_large_feature_branch to disk and allow you to stage things that can go individually.</p><pre>$ git apply ../my_pr_patch</pre><p>Once you’ve staged and committed whatever you want for small_pr_one it’s time to clean up.</p><h3>Step 4: Stash the Remaining Changes</h3><p>To stash everything that isn’t staged, including untracked files, run the following command.</p><pre>git stash --include-untracked --keep-index</pre><p>Now small_pr_one is clean and you can open a PR against it. Next move on to small_pr_two through small_pr_n until complete.</p><h3>Step 5: Repeat 3 &amp; 4 Until Done</h3><p>If the next set of changes can be made independently then open small_pr_two against master. If you need what was in small_pr_one to keep going just branch from there.</p><pre>git checkout master<br>git checkout -b small_pr_two</pre><p>You can use $ git stash show to list the remaining files in your stash. If you want to apply changes from the stash selectively you can use git checkout stash@{0} -- &lt;your filename&gt; to checkout an individual file from the stash. If you need to see a diff between what’s in the stash and your current branch run: git diff stash@{0} -- &lt;your filename&gt;</p><h3>Additional Resources</h3><p>Another approach you might want to try is Stacked PRs by Grayson Koonce: <a href="https://graysonkoonce.com/stacked-pull-requests-keeping-github-diffs-small/">https://graysonkoonce.com/stacked-pull-requests-keeping-github-diffs-small/</a></p><p>I’ve used Stacked PRs a few times but tend to prefer keeping my branching structure shallow if possible so I normally use the approach shown above.</p><p>Happy Coding!</p><p>-g</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=81d607660c05" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How To: Set up NativeScript, Vue.js, and Firebase]]></title>
            <link>https://medium.com/@groksrc/how-to-set-up-nativescript-vue-js-and-firebase-465354d4296b?source=rss-d059ff1b12a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/465354d4296b</guid>
            <category><![CDATA[firebase]]></category>
            <category><![CDATA[vuejs]]></category>
            <category><![CDATA[nativescript]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Drew Cain @groksrc]]></dc:creator>
            <pubDate>Mon, 13 Aug 2018 04:45:06 GMT</pubDate>
            <atom:updated>2018-08-13T15:22:25.427Z</atom:updated>
            <content:encoded><![CDATA[<p>If you’re trying to set up a new project with NativeScript, Vue.js, and Firebase you may run into a few issues along the way. This quickstart guide should help you avoid a few of the pitfalls I ran into whenever I went down the path.</p><p>We’ll start a sample project named tnsvuefirebasestarter. If you’d like to see the source for the finished project it’s available here:</p><p><a href="https://github.com/groksrc/tnsvuefirebasestarter">groksrc/tnsvuefirebasestarter</a></p><p>First start by creating your new project. I’ve just installed the new Vue CLI 3.0 release and haven’t had any trouble:</p><p>$ vue init nativescript-vue/vue-cli-template tnsvuefirebasestarter</p><p>Once the template initializes your project, change to the directory</p><p>$ cd tnsvuefirebasestarter</p><p>Now install dependencies with npm. NOTE: do not use yarn. I found that for some reason the hooks directories are not created when using yarn and this prevents the project from compiling correctly.</p><p>$ npm i</p><p>Next, you’ll need to open the firebase console and download the project files that allow the apps to connect to firebase. If you’re using Android the file named google-services.json and put it in the template/app/App_Resources/Android directory. For iOS download the GoogleService-Info.plist file and place it in template/app/App_Resources/iOS.</p><p>Now we’ll install the firebase plugin. Running this command will execute a template that will ask you which firebase services you’d like to enable. NOTE: I had trouble with the Messaging component and was not able to get it to compile or run on Android or iOS. I don’t know if this is an existing issue, but to work around it I chose not to enable it. Also, messaging on iOS in the emulator is known to have issues. You may need to test on a real device.</p><p>$ npm i --save nativescript-plugin-firebase</p><p>Next we need to switch into the template directory and run npm install there as well.</p><pre>$ cd template<br>$ npm i</pre><p>At this point we’re almost finished. Let’s hook up our src/main.js to firebase. Right beneath the line to import &#39;./styles.css&#39; add the following code:</p><pre>import firebase from ‘nativescript-plugin-firebase’;<br>firebase.init({<br>  // Optionally pass in properties for database, authentication and cloud messaging,<br>  // see their respective docs.<br>}).then(<br> instance =&gt; {<br>   console.log(“firebase.init done”);<br> },<br> error =&gt; {<br>   console.log(`firebase.init error: ${error}`);<br> }<br>);<br>Vue.prototype.$firebase = firebase;</pre><p>Now make sure you run a clean before launch:</p><p>$ npm run clean</p><p>At this point your project should be ready to go. For android run:</p><p>$ npm run watch:android</p><p>For iOS run:</p><p>$ npm run watch:ios</p><p>If everything goes as expected the app should start up and you should see the following in your console:</p><pre>JS: &#39;firebase.init done&#39;</pre><p>I’d like to give special thanks to <a href="https://medium.com/u/1a6318bb9a19">Jen Looper</a> who worked consecutive weekends with me helping me to figure this out. If you need additional help there’s a #firebase channel in the <a href="https://nativescriptcommunity.slack.com/">NativeScript Community Slack</a> where you should be able to reach either one of us.</p><p>Also, be sure to check out her two sample projects in case you need other examples:</p><ul><li><a href="https://github.com/jlooper/elocute/tree/master/mobile-v2">jlooper/elocute</a></li><li><a href="https://github.com/jlooper/picme">jlooper/picme</a></li></ul><p>P.S. I’m a relative newcomer to mobile development and ran into trouble compiling on iOS. If you run in to trouble compiling your iOS make sure your developer account is hooked up in xcode and check the project for any errors. You can open the xcode output project after the NativeScript build runs at: dist/platforms/ios/dist.xcworkspace</p><p>Happy Coding!</p><p>-g</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=465354d4296b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How To: Vue SSR with Nuxt.js on Firebase]]></title>
            <link>https://medium.com/@groksrc/how-to-vue-ssr-with-nuxt-js-on-firebase-259b3cc1ba92?source=rss-d059ff1b12a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/259b3cc1ba92</guid>
            <category><![CDATA[firebase]]></category>
            <category><![CDATA[nuxtjs]]></category>
            <category><![CDATA[vuejs]]></category>
            <dc:creator><![CDATA[Drew Cain @groksrc]]></dc:creator>
            <pubDate>Fri, 04 May 2018 18:23:42 GMT</pubDate>
            <atom:updated>2018-05-04T18:23:42.204Z</atom:updated>
            <content:encoded><![CDATA[<p>The VueConf sessions from this year’s conference are being released as editing finishes up on them. Yesterday saw the release of Sebastien Chopin’s talk on Vue &amp; SSR. Here’s a <a href="https://www.vuemastery.com/conferences/vueconf-2018/vue-and-ssr-the-best-practices-sebastien-chopin/">link</a>, it’s a good talk. One of the nice things about Nuxt.js is that it allows you to leverage the same code for client and server side renderings. SSR is important for public facing apps, especially where SEO is involved.</p><p>I’ve also been getting into Firebase a bit. It’s a great platform for building all kinds of applications, especially if you want to go serverless. I did some digging for examples on how to set up Nuxt.js on Firebase and there were a few things out there, but nothing that I could just clone and go with. Google’s David East has a <a href="https://www.youtube.com/watch?v=ZYUWsjUxxUQ">video</a> that walks through the process of getting started, but I had a little trouble following it. There’s also <a href="https://github.com/nuxt/nuxt.js/issues/1634">this issue</a> on the NUXT GitHub issue tracker that was helpful, but didn’t have an example solution.</p><p>So I made one! My sample project is a pseudo search engine that demonstrates how you can get started with Nuxt.js on Firebase today. It leverages three features of Firebase: Hosting, Functions and Storage. And it also illustrates how to use the asyncData feature described in Sebastien’s talk.</p><p>If you haven’t worked with Nuxt.js or Firebase before this is an easy way to get started with both. Here are the high-level steps you need to take:</p><ul><li>Set up a Firebase account and create a project</li><li>Set up your environment</li><li>Clone the project and initialize Firebase</li><li>Run locally</li><li>Publish &amp; Voilà</li></ul><h3>Get a Firebase Account and Create a Project</h3><p>You can do this at <a href="https://firebase.google.com./">https://firebase.google.com.</a> After you’ve signed in with your Google account hit the giant Add Project button and start a new project. Give it a name and hit the Create Project button. Once you‘re on the project dashboard click Storage from the left nav, then click Get Started. This will enable Storage for you and give you a place to upload files. After we clone the project we’re going to drop a results.json file here so that it’s available to query. This will drive the search results screen. Now you’re ready to head over to your terminal.</p><h3>Environment Setup</h3><p>First you’ll want to be sure that you’re running Node 8.x or later. Firebase only supports Node 6 right now (es5), but that’s ok. This project uses webpack/babel to transpile for you, so you can ignore the warnings you may see about this in your console. You’ll also want to be sure that you have the Firebase CLI tools installed:</p><pre>$ npm install -g firebase-tools</pre><h3>Clone and Init Firebase</h3><p>Now you can clone the <a href="https://github.com/groksrc/groksrc-search">project repo</a>. Once you’ve done that you need to hook the code up to your Firebase project. Do that with the following:</p><pre>$ git clone <a href="/service/mailto:git@github.com">git@github.com</a>:groksrc/groksrc-search.git<br>$ firebase init</pre><p>This will walk you through a series of questions and at the end a .firebaserc file is created for you. This is how the Firebase CLI knows where your backend is.</p><p>⚠️ Caution: when you run the init command it will overwrite the firebase.json file. Be sure to discard this change and just keep the .firebaserc file.</p><p>As you walk through the firebase init command options select the following:</p><h4>Step 1:</h4><ul><li>Choose only the Hosting option. You can skip the others because this configures the firebase.json file and we already have one in the project.</li></ul><h4>Step 2:</h4><ul><li>Select the project you created above from the list of choices. If you don’t see it, make sure you’re logged in to the correct account. This will be saved in the .firebaserc file.</li></ul><h4>Step 3:</h4><ul><li>Enter dist for the public directory. This will prevent junk files from being created.</li></ul><h4>Remaining Steps:</h4><ul><li>Choose the defaults</li></ul><p>Now be sure to reset the firebase.json file. Once that’s complete you need to also need to be sure and install the npm dependencies:</p><pre>$ yarn install # or npm install</pre><h3>Running Locally</h3><p>Now you should be able to start the project locally. Use the following command to make sure that everything is running:</p><pre>$ yarn dev</pre><p>It should start and you should be able to browse <a href="http://localhost:3000/">http://localhost:3000</a>. If everything is working as expected you’ll see a search page that looks similar to Google’s homepage. Once it’s up you can start making changes, Hot Module Reload should work too.</p><p>Note at this point we’re just running with nuxt via node.js. Firebase hasn’t come into play yet. If we want to test our Firebase functions locally we have to transpile the project. Then we can run it:</p><pre>$ yarn package-functions<br>$ firebase serve --only hosting,functions</pre><p>This will launch the app via a Firebase process, similar to what it would do once you publish. Note that HMR is not available while running via the Firebase CLI.</p><h3>Publishing</h3><p>To push your project and see it live you can run the following:</p><pre>$ yarn push</pre><p>This script will compile the Google Functions code into a /functions directory and publish it for you. The code in this directory is what Functions will use to perform the server side rendering.</p><p>Once the push command is complete the project will be up and you can test it on your live URL that is display in the console. Note: when I was testing these steps I noticed that the push command would fail on occasion for some unspecified reason. Just running the command again seemed to take care of the problem.</p><h3>Summary</h3><p>The sample app is set to pull the results.json from the original Firebase project I set up for the demo. In order to make your own results just pop open the file in the root directory, tweak &amp; save, then upload it to the Storage project in your app. Once you’ve done that, click on the uploaded file to expand the details and expand the File Location section. Copy the Download URL from this section. Then in the source code locate results.vue and update the URL in the asyncData method to point to your file. Now save and yarn publish again.</p><p>That’s it! At this point you’re off and running with Nuxt.js and Firebase. I hope you’re able to spend a few minutes with these two awesome technologies, it’s definitely a smooth way to build an app and I’ve really enjoyed getting acquainted with them. Until next time!</p><p>g</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=259b3cc1ba92" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elixir is fun!]]></title>
            <link>https://medium.com/@groksrc/elixir-is-fun-be51851d0c1f?source=rss-d059ff1b12a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/be51851d0c1f</guid>
            <category><![CDATA[elixir]]></category>
            <dc:creator><![CDATA[Drew Cain @groksrc]]></dc:creator>
            <pubDate>Thu, 26 Apr 2018 03:56:48 GMT</pubDate>
            <atom:updated>2018-04-26T03:56:48.262Z</atom:updated>
            <content:encoded><![CDATA[<h3>Drink the Potion</h3><p>Here’s a new language I’m really excited about: <a href="https://elixir-lang.org/">Elixir</a>. Elixir is a higher order functional language with the aesthetics of Ruby. It is derived from the old telecom language, Erlang, and in fact it transpiles down to vanilla Erlang source code which then targets Erlang’s BEAM runtime.</p><p>Elixir is a functional language, and one of the easiest and best to get started with if you’re new to functional languages. The idea with functional languages is that functions live inside of modules and modules process data. That’s it. There are no objects to instantiate. You simply transform data from one shape to another shape, always returning something new from your function.</p><p>It sounds strange if you’ve never worked without objects before, but I find this approach incredibly refreshing. And if you think about it, it makes total sense for the web because you’re just transforming web requests into web responses. The web’s stateless nature naturally fits a data in and data out request/response paradigm.</p><h3>Phoenix Framework</h3><p>The Phoenix Framework is commonly compared to Rails, it’s counterpart in OO land, but Phoenix is actually quite different from Rails in many respects. New developers to both Elixir and Phoenix should proceed with humility and an open mind as problem/solution pairings can vary a good deal from their OO counterparts.</p><p>But once you’re up to speed, you should find Phoenix to be a capable, fun development experience with a more advanced toolchain and solution space than you might expect from such a young framework.</p><p>I’ve used it now on a few small projects and find it to be approachable, familiar and well documented. In fact, <a href="https://hexdocs.pm/">hexdocs</a> and the built in documentation solution Elixir provides is yet another reason I see big things on the horizon for this up-and-comer.</p><p>Recommended Reading</p><p>I’ve read through the following titles personally and recommend them for getting up to speed with Elixir and Phoenix. The community forums are also active and there’s a positive vibe around the core contributors and the direction the language is taking, so things are looking up.</p><ul><li><a href="https://pragprog.com/book/elixir16/programming-elixir-1-6">Programming Elixir &gt; 1.6</a></li><li><a href="https://pragprog.com/book/phoenix/programming-phoenix">Programming Phoenix</a></li><li><a href="https://pragprog.com/book/wwgraphql/craft-graphql-apis-in-elixir-with-absinthe">Craft GraphQL APIs in Elixir with Absinthe</a></li></ul><h3>Demo</h3><p>Finally, when you’re just getting started with a new language it helps to look at some code. Here’s a quick project I did to demonstrate how you can hook Phoenix to a Postgresql database and render something.</p><p><a href="https://github.com/groksrc/tablex">groksrc/tablex</a></p><p>In this case it just renders the names of the tables that are in the database by creating some schema files to get started with. NOTE: It is not recommended to use these schema files to build an app, you’d really do better with focused Contexts instead of fat schemas, but the point is that it gives you something to look at and play with.</p><h3>Summary</h3><p>I hope you have a few minutes to sit down and experiment with Elixir. It’s a viable option today and for a certain class of problems it is a fantastic fit. We’ve been using it in production at my current role for some time now and it runs like a champ. Enjoy!</p><p>g</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=be51851d0c1f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Reasons I like Vue.js]]></title>
            <link>https://medium.com/@groksrc/reasons-i-like-vue-js-b8fac6434e4d?source=rss-d059ff1b12a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/b8fac6434e4d</guid>
            <category><![CDATA[vuejs]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Drew Cain @groksrc]]></dc:creator>
            <pubDate>Thu, 26 Apr 2018 03:30:41 GMT</pubDate>
            <atom:updated>2018-04-26T03:30:41.608Z</atom:updated>
            <content:encoded><![CDATA[<p><a href="https://vuejs.org/">Vue.js</a> has recently become my go-to front end framework. I like it because it’s easy to get started with and you can use it for anything from simple tasks to complex enterprise applications. Here are the reasons why I like Vue.js:</p><h3>The API is Stable</h3><p>I really like how <a href="https://github.com/yyx990803">Evan</a> and the core contributors have defined a nice API and they don’t feel or see the need to overhaul it often. Sure it is changing and improving over time, but it’s not a constant revolutionary change.</p><blockquote>me: gives side-eye to webpack<em> 👀</em></blockquote><h3>The API is easy to understand</h3><p>You have in the API exactly what you would expect: A well defined, easy to follow event lifecycle for components that define a series of steps to go through to render some html.</p><p>The real beauty of the vue.js approach is the ability to define html and javascript in the same file, but separate the markup from the javascript. It makes the code familiar, easier to read, and to reason about.</p><p>Contrast with React the blurring of html, xml and javascript into jsx. The entire learning curve is avoided in vue.js, and we follow the same paradigms that we have traditionally followed. Your view code and model/controller code remain separate and distinct. Simple.</p><h3>The Style Guide</h3><p>The <a href="https://vuejs.org/v2/style-guide/">Vue.js Style Guide</a> is a fantastic way to define cohesion in the community around best practices and what a cannonical code base should look like.</p><p>You may or may not like every rule and suggestion that they define, but they at least contribute to what you should be thinking about and you should have just cause to deviate from them.</p><p>By following the conventions, the broader community benefits as we collaborate with a shared understanding of how things should be put together. It increases productivity and reduces the number of things to actually think about as you implement your project requirements.</p><h3>vue-router and vuex</h3><p>It’s unfortunate (imho) that React doesn’t take a more pronounced approach on what is recommended when dealing with routing or storage. Though clearly react-redux and react-router are some common go-tos, there are many, many other options. And while React works well together with these libraries, we’re told up front that the relationship is not explicit. In fact, the first line of the <a href="https://redux.js.org/basics/usage-with-react">Redux docs</a> state that “Redux has no relation to React”.</p><p>With vue-router and vuex we are given the recommended path, which in cases of frameworks like this, happens to be exceedingly helpful. It reduces the amount of discovery necessary and leads you to more consistent implementations across projects. In a large environment I find this point to be expecially compelling.</p><h3>Summary</h3><p>I’m sorry for any React fans I may have rubbed the wrong way. This wasn’t to bash React as much as it was to talk up the benefits of Vue.js and React is obviously a primary basis for comparison. React is clearly a popular choice, and valuable technology. But I hope you have the chance to spend some time with Vue.js. It’s a great framework for small and large projects. It has my full throated endorsement.</p><p>g</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b8fac6434e4d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Software Engineers Are Authors]]></title>
            <link>https://medium.com/@groksrc/software-engineers-are-authors-8dab225ee4d6?source=rss-d059ff1b12a3------2</link>
            <guid isPermaLink="false">https://medium.com/p/8dab225ee4d6</guid>
            <category><![CDATA[technical-writing]]></category>
            <dc:creator><![CDATA[Drew Cain @groksrc]]></dc:creator>
            <pubDate>Thu, 26 Apr 2018 03:26:31 GMT</pubDate>
            <atom:updated>2018-04-26T03:26:31.301Z</atom:updated>
            <content:encoded><![CDATA[<h3>Background</h3><p>I’ve worked in a lot of different engineering environments over the course of my career and one thing that always comes up as the team grows is how to handle documentation. At a certain point you just can’t scale effectively without it.</p><p>So then the question automatically becomes, “Who is going to write it?” In some places I’ve worked this question goes unanswered. That’s unfortunate because now the ticketing system winds up as the default documentation center. This is problematic for a number of reasons, but more importantly the team suffers as a result.</p><h3>Technical Writers</h3><p>Often the suggestion is made to hire a technical writer. “Engineering time is valuable so why waste it on mundane tasks” is a common refrain I’ve heard. Someone with experience writing good documentation will surely be able to produce high quality documentation that normal human beings can read. Right? Not so fast..</p><p>Technical writers can most certainly produce quality content. That’s not an issue. But experience tells us that the content they produce can vary significantly based on how they understand the systems they are documenting and who is available to provide color commentary about what is going on.</p><p>In many cases having good documentation is not simply about users being able to access a set of instructions about how to use the software. The best documentation covers things like special conditions, potential problem areas and remediations, and background information on why the system is designed in the way that it is.</p><p>And how could a Technical Writer ever get any of that information by only looking at the screens of the software? More often than not, the software engineers are the only people in the room who know what is really going on under the hood.</p><p>Aside from that, if you think about the responsibilities of a Technical Writer we realize that they cover a lot more than just producing standard documentation for the system. They also need to format, publish, and version the documentation which can be a daunting task in and of itself.</p><h3>Why Software Engineers Should Write Documentation</h3><p>In an ideal world the Technical Writer on your team is not the author. They are the editor and the engineers are authors. Technical Writers can help guide us to publication, much like would happen when writing a book. But as software engineers we have the most in-depth knowledge about the systems we develop and often times can do the best job of explaining the details about how things actually work.</p><p>But wait! I can already hear some of you grumbling about how documentation is boring or that nobody reads it and that it’s a waste of time. Well, if you’ve made it this far then why not become part of the solution?</p><p>I posit that software engineers are authors irrespective of <em>what</em> we write, but instead of limiting ourselves to writing code we should open ourselves to authoring all types of documents. The best engineers I know are also very keen on this point. If you’ve mastered writing code and tests, documentation is your next challenge.</p><h3>How To Write Good Documentation In 5 Minutes Or Less</h3><p>It’s more easy than you realize to write good documentation. You do it every day, you just don’t recognize it as such. And the documentation you do write remains “stuck” where you wrote it: in the code. Here are 3 easy steps to break that documentation log jam.</p><h3>1. Start a docs folder</h3><p>The docs folder can live in your project repo or somewhere else. Just make sure it’s easy for everyone else to get to it. Inside the folder I recommend vanilla markdown unless it’s hosted and provides its own formatting (e.g. <a href="https://www.atlassian.com/software/confluence">Confluence</a>). You could even use <a href="http://jekyllrb.com/">Jekyll</a> if you wanted. The key is that there’s low friction to getting into the documentation and working with it.</p><h3>2. Start An Outline</h3><p>When you’re ready to start a new page of documentation create an empty file and start with a title and some headings. Here’s an example of how I did this very thing when writing this article:</p><pre>Software Engineers Are Authors</pre><pre>## Background<br>## Technical Writers<br>## How To Write Good Documentation</pre><p>If you’ve ever used GitHub’s hub <a href="https://github.com/github/hub">utility</a>, this should be familiar. Like when you use $ hub pull-request, the first line of the document is the title, followed by an empty line. Anything after that is the body content. In the body use markdown headings an write an outline that covers just the main topics of what this feature does. The names of your public methods are good starting points.</p><h3>3. Start A Loop</h3><p>Any time you’re stuck waiting on a task (installers, downloads, jobs, builds) is a good time to loop into the docs and add flesh to your outline. When your task is finished, context switch out. Even if you just note the thoughts you’re having as you’re working, that’s good to get down. It’s this simple process where you consistently loop through the docs that really makes the difference because you’re now slowly adding to a critical component of the software that is often overlooked. If you’re stuck waiting on an operation, go async yourself. It will make you more efficient and your Technical Writer will thank you.</p><h3>You + Your Technical Writer = Jimmy &amp; Justin #winning</h3><p>If you’ve done all of this, now your Technical Writer has awesome source material for coloring the documentation they create. If you don’t have a Technical Writer yet you’ll be prepared with some basic content for when they arrive. Either way it’s a win/win.</p><h3>ProTip: For When You Don’t Like Long-form Entry</h3><p>Speach to text is actually pretty good now. Using a dictation app can save loads of typing and speaking is typically faster than typing anyway. If you have a lot of content but don’t want to bother typing it all out give this a try. You might be surprised at how well it works.</p><h3>Summary</h3><p>You just read a lot about documentation. Congratulations! Here’s your <a href="https://www.youtube.com/watch?v=57dzaMaouXA">prize</a>.</p><p>g</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8dab225ee4d6" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>