Jekyll2023-02-10T00:29:34+00:00https://shovik.com/feed.xmlAlex KovshovikSoftware developer, creator, father of 4.Git Rebase: how to use interactive rebase2023-02-06T04:00:00+00:002023-02-06T04:00:00+00:00https://shovik.com/2023/02/06/git-interactive-rebase-turotial<p>Hi All!</p>
<p>This is another video on how to use interactive rebase in git, to edit commit history</p>
<p><strong>Please watch:</strong></p>
<iframe width="680" height="415" src="https://www.youtube.com/embed/rt9ZOVciJm8" frameborder="0" allowfullscreen=""></iframe>Hi All!Git Rebase: how to resolve conflicts2021-04-24T04:00:00+00:002021-04-24T04:00:00+00:00https://shovik.com/2021/04/24/git-rebase-resolve-conflicts<p>Hi All!</p>
<p>This is a short humorous video about the git rebase: a practical example of how to resolve conflicts during the automatic rebase:</p>
<p><strong>Please watch:</strong></p>
<iframe width="680" height="415" src="https://www.youtube.com/embed/OXtdxHTh2oY" frameborder="0" allowfullscreen=""></iframe>Hi All!Git rebase basics: first YouTube video2021-03-30T04:00:00+00:002021-03-30T04:00:00+00:00https://shovik.com/2021/03/30/git-rebase-basics<p>Hi All!</p>
<p>This is a short humorous video about the git rebase: a simple explanation of git’s distributed nature and a humorous sketch :)</p>
<p><strong>Please watch:</strong></p>
<iframe width="680" height="415" src="https://www.youtube.com/embed/gkGZzd9c4ow" frameborder="0" allowfullscreen=""></iframe>Hi All!Amazon’s EKS vs Digital Ocean’s Kubernetes2020-01-22T04:00:00+00:002020-01-22T04:00:00+00:00https://shovik.com/2020/01/22/amazon-eks-vs-digital-ocean-kubernetes<p><strong>Update 2022-11-24:</strong>
This website used to run on a Kubernetes cluster managed by Digital Ocean. Now it is a simple static site generated by Jekyll and hosted by GitHub for free. Thank you GitHub</p>
<p><a href="https://imgflip.com/i/3muvfv" title="k8s is NOT an overkill for this website, change my mind"><img src="https://i.imgflip.com/3muvfv.jpg" title="made at imgflip.com" /></a></p>
<h2 id="digital-oceans-managed-kubernetes">Digital Ocean’s Managed Kubernetes</h2>
<p>Until Digital Ocean’s managed Kubernetes, having your own Kubernetes cluster was an overkill for small apps: it was hard to configure it right, it was too expensive. Some would argue that it is still the case.</p>
<p>There are other popular container orchestrators (Rancher), but Kubernetes eventually won the popular vote and became the de-facto container orchestrator. Rancher replaced it’s home-grown Cattle with Kubernetes in it’s version 2.x. They made the decision quite early and I believe this was the right choice.</p>
<p>To create and configure a Kubernetes cluster, all you need are a few clicks on Digital Ocean’s online console.</p>
<p><strong>Steps:</strong></p>
<ol>
<li>Select “Create Cluster”.</li>
<li>Choose version of Kubernetes.</li>
<li>Choose datacenter region.</li>
<li>Choose node type and number of nodes you need.</li>
<li>Give the cluster a name.</li>
</ol>
<p>A few minutes later the cluster is up and running. Just download the config file and you’re ready to run <code class="language-plaintext highlighter-rouge">kubectl</code> commands.</p>
<p>This is just too easy!</p>
<h2 id="amazon-eks">Amazon EKS</h2>
<p>Amazon EKS is entirely a different beast and I believe Amazon made it so complex for a good reason: their solution needs to work for “enterprise customers”, with specific network, security and compliance requirements. Amazon’s solution also works great with its other services: VPC, ELB, Security Groups.</p>
<p>On December 31 of 2019 I completed the migration of <a href="https://decisely.com/">my employer’s</a> production workload from Rancher 1.6.x to Amazon EKS, <strong>with about 2 seconds of downtime.</strong> It took way longer than I anticipated, but along the way I learned a lot about Amazon VPCs, availability zones, subnets, routing tables, VPC peering, NAT gateways, Internet gateways, CIDR IP address blocks, Elastic IPs, network ACLs and IAM policies. This gave me better understanding and appreciation of AWS and its design philosophy.</p>
<p><strong>Steps:</strong></p>
<ol>
<li>Download and install command line tools:
<ul>
<li>AWS CLI</li>
<li>AWS IAM Authenticator</li>
<li><a href="https://eksctl.io/">eksctl</a></li>
</ul>
</li>
<li>Create new VPC with 2 availability zones, 2 public subnets (one per zone) and 2 private subnets.
<ul>
<li>Define IP address ranges than you like or use defaults.</li>
</ul>
</li>
<li>Create new cluster using <code class="language-plaintext highlighter-rouge">eksctl</code> - this is by far the easiest way I found. See command example below.
<ul>
<li>The first nodegroup is created together with the cluster.</li>
<li>The nodegroup uses AWS CloudFormation to provision an auto-scaling group.</li>
</ul>
</li>
<li><a href="https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html">Install ALB Ingress Controller in your cluster</a> to be able to create load balancers to expose your web app to outside world.</li>
</ol>
<p><strong>eksctl command:</strong></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>eksctl create cluster <span class="se">\</span>
<span class="nt">--name</span> my-cluster <span class="se">\</span>
<span class="nt">--region</span> us-west-1 <span class="se">\</span>
<span class="nt">--nodegroup-name</span> my-nodes <span class="se">\</span>
<span class="nt">--node-type</span> t3.medium <span class="se">\</span>
<span class="nt">--nodes</span> 3 <span class="se">\</span>
<span class="nt">--nodes-min</span> 1 <span class="se">\</span>
<span class="nt">--nodes-max</span> 4 <span class="se">\</span>
<span class="nt">--node-ami</span> auto <span class="se">\</span>
<span class="nt">--node-private-networking</span> <span class="se">\</span>
<span class="nt">--node-volume-size</span> 24 <span class="se">\</span>
<span class="nt">--node-security-groups</span> security-group-id <span class="se">\</span>
<span class="nt">--ssh-public-key</span> my-ssh-private-key-name <span class="se">\</span>
<span class="nt">--vpc-private-subnets</span> subnet-id-1,subnet-id-2 <span class="se">\</span>
<span class="nt">--vpc-public-subnets</span> subnet-id-3,subnet-id-4
</code></pre></div></div>
<p>There are fewer steps to configure a Kubernetes cluster on Amazon EKS, however it was a long journey for me to figure them out, because there are MANY ways to configure a Kubernetes cluster on AWS. There are a ton of tutorials and best practices articles, so many decisions that I had to make along the way.</p>
<h2 id="conclusion">Conclusion</h2>
<p>DigitalOcean has great defaults. It is cheaper and much easier :)</p>
<p>The complexity of deploying workloads is the same on both: Amazon EKS and Digital Ocean Kubernetes.</p>Update 2022-11-24: This website used to run on a Kubernetes cluster managed by Digital Ocean. Now it is a simple static site generated by Jekyll and hosted by GitHub for free. Thank you GitHubDeploying Phoenix Apps with Docker2017-02-05T04:00:00+00:002017-02-05T04:00:00+00:00https://shovik.com/2017/02/05/deploying-phoenix-apps-with-docker<p>My earlier attempts at setting up reliable and repeatable Elixir application deployments worked, but I didn’t feel completely safe. I really tried to setup the deployment of my Phoenix app “correctly”, sticking closely to “the standard” - Erlang/OTP, hot code reloading, upgrade releases and automatic versions. About 1/4 of the time upgrades simply didn’t work. The process being so reliable failed like a professional soldier: no complaining, no screaming and no emotions. <a href="https://github.com/boldpoker/edeliver">edeliver</a> would tell me that everything worked well, but the new code “wasn’t taking”: my changes were not visible on the website until I manually restarted the app on the server. BTW, this is why (as of this writing) this website displays app version in the footer of each page. After hours of troubleshooting I couldn’t figure out a pattern for these seemingly random failures.</p>
<p>Check out my last 2 blog posts for full <code class="language-plaintext highlighter-rouge">distillery</code> + <code class="language-plaintext highlighter-rouge">edeliver</code> deployment configuration walkthrough:</p>
<ul>
<li><a href="/2017/01/11/deploying-phoenix-apps-for-rails-developers.html">Deploying Phoenix Apps for Rails developers: Part 1</a></li>
<li><a href="/2017/01/20/deploying-phoenix-apps-for-rails-developers-part2.html">Deploying Phoenix Apps for Rails developers: Part 2</a></li>
</ul>
<p>Shortly after I finished my last post about Elixir upgrade releases, I came across this <a href="http://teamon.eu/2017/deploying-phoenix-to-production-using-docker/">wonderfully useful article</a> by <a href="http://teamon.eu/">Tymon Tobolski</a> about a tool he created - <a href="https://github.com/Recruitee/mix_docker">mix_docker</a>.</p>
<p>This post is the complete walkthrough of the Phoenix app deployment using <code class="language-plaintext highlighter-rouge">mix_docker</code>. Some of the material here reiterates Tymon’s post while adding much more detail specific to packaging a Phoenix app in a Docker image.</p>
<p><img src="https://s3.amazonaws.com/shovik-com/uploads/post_images/8/small_v.png?v=63658354361" alt="Docker logo" /></p>
<p><strong>We are going to:</strong></p>
<ol>
<li>Create brand new Phoenix app.</li>
<li>Add <code class="language-plaintext highlighter-rouge">mix_docker</code>.</li>
<li>Customize docker images.</li>
<li>Configure your app with ENV variables.</li>
<li>Run your app.</li>
<li>Draw conclusions. :)</li>
</ol>
<p>This guide assumes you already have <a href="https://www.docker.com/">Docker</a>, <a href="http://elixir-lang.org/">Elixir</a> and <a href="http://www.phoenixframework.org/">Phoenix Framework</a> installed on your machine.</p>
<p><code class="language-plaintext highlighter-rouge">mix_docker</code> is a hex package that drastically simplifies the packaging of Elixir releases into a minimal Docker container. The key trick here is to split the construction of your production image into 2 steps:</p>
<ol>
<li>Use a “build image” to compile everything (Elixir code + assets) and build an Erlang release.</li>
<li>Create a “release image” and put Erlang release in it.</li>
</ol>
<p>Quick refresher from <a href="https://hexdocs.pm/distillery/terminology.html#content">distillery documentation</a> on what Erlang release is:</p>
<blockquote>
<p>A release is a package of your application’s .beam files, including it’s dependencies .beam files, a sys.config, a vm.args, a boot script, and various utilities and metadata files for managing the release once it is installed. A release may also contain a copy of ERTS (the Erlang Runtime System).</p>
</blockquote>
<p><code class="language-plaintext highlighter-rouge">distillery</code> is the most popular hex package that <code class="language-plaintext highlighter-rouge">mix_docker</code> uses (depends on) to build Erlang releases.</p>
<p>Build image must have a lot of software installed on it in order to build the release: Erlang, Elixir, NodeJs, etc., hence the image size = large. Release image only needs a matching version of Erlang installed. This is how a release image can be very small. In fact, a release image doesn’t even have to have Erlang installed if you choose to include the Erlang runtime in your application’s release (<em>default <code class="language-plaintext highlighter-rouge">distillery</code> setting for production environment</em>).</p>
<h2 id="1-create-brand-new-phoenix-app">1. Create brand new Phoenix app</h2>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix phoenix.new hi_docker
</code></pre></div></div>
<p>It is ok to use an existing app too.</p>
<h2 id="2-add-mix_docker">2. Add mix_docker</h2>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="n">deps</span> <span class="k">do</span>
<span class="p">[{</span><span class="ss">:mix_docker</span><span class="p">,</span> <span class="s2">"~> 0.3.0"</span><span class="p">}]</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Set the name for the Docker image in <code class="language-plaintext highlighter-rouge">config/config.exs</code>:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># config/config.exs</span>
<span class="n">config</span> <span class="ss">:mix_docker</span><span class="p">,</span> <span class="ss">image:</span> <span class="s2">"hi_docker"</span>
</code></pre></div></div>
<p>Run <code class="language-plaintext highlighter-rouge">mix deps.get</code> to install the added hex package.</p>
<p>Run <code class="language-plaintext highlighter-rouge">mix docker.init</code> to create default distillery release configuration in <code class="language-plaintext highlighter-rouge">rel/config.exs</code>.</p>
<p>Edit <code class="language-plaintext highlighter-rouge">rel/config.exs</code>:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Don't bundle Erlang runtime,</span>
<span class="c1"># because it would already be installed in the release image</span>
<span class="n">environment</span> <span class="ss">:prod</span> <span class="k">do</span>
<span class="n">set</span> <span class="ss">include_erts:</span> <span class="no">false</span> <span class="c1"># set to false.</span>
<span class="c1"># ...</span>
</code></pre></div></div>
<p>Edit <code class="language-plaintext highlighter-rouge">.dockerignore</code>, add the following lines:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>node_modules
priv/static
hi_docker.tar.gz
</code></pre></div></div>
<p>Add <code class="language-plaintext highlighter-rouge">hi_docker.tar.gz</code> to your <code class="language-plaintext highlighter-rouge">.gitignore</code> as well.</p>
<p>We’re going to compile and digest static assets inside of our build image, that way both build image and release image could be built on CI server - as recommended by Tymon himself.</p>
<h2 id="3-customize-docker-images">3. Customize docker images</h2>
<p>Run <code class="language-plaintext highlighter-rouge">mix docker.customize</code>. This will copy the default <code class="language-plaintext highlighter-rouge">Dockerfile.build</code> and <code class="language-plaintext highlighter-rouge">Dockerfile.release</code> into your app’s root directory.</p>
<p>Add the following packages to <code class="language-plaintext highlighter-rouge">Dockerfile.build</code> using standard Dockerfile commands:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">nodejs</code></li>
<li><code class="language-plaintext highlighter-rouge">python</code></li>
</ul>
<p>Install nodejs dependencies and cache them by adding the following lines before the <code class="language-plaintext highlighter-rouge">COPY</code> command:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Cache node deps</span>
COPY package.json ./
RUN npm <span class="nb">install</span>
</code></pre></div></div>
<p>Build and digest static assets by adding the following lines after the <code class="language-plaintext highlighter-rouge">COPY</code> command:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>RUN ./node_modules/brunch/bin/brunch b <span class="nt">-p</span> <span class="o">&&</span> <span class="se">\</span>
mix phoenix.digest
</code></pre></div></div>
<p>Test the <code class="language-plaintext highlighter-rouge">Dockerfile.build</code>:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix docker.build
</code></pre></div></div>
<p>Complete listing of <code class="language-plaintext highlighter-rouge">Dockerfile.build</code>:</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> bitwalker/alpine-erlang:6.1</span>
<span class="k">ENV</span><span class="s"> HOME=/opt/app/ TERM=xterm</span>
<span class="c"># Install Elixir and basic build dependencies</span>
<span class="k">RUN </span><span class="se">\
</span> <span class="nb">echo</span> <span class="s2">"@edge http://nl.alpinelinux.org/alpine/edge/community"</span> <span class="o">>></span> /etc/apk/repositories <span class="o">&&</span> <span class="se">\
</span> apk update <span class="o">&&</span> <span class="se">\
</span> apk <span class="nt">--no-cache</span> <span class="nt">--update</span> add <span class="se">\
</span> git make g++ nodejs python <span class="se">\
</span> elixir@edge <span class="o">&&</span> <span class="se">\
</span> <span class="nb">rm</span> <span class="nt">-rf</span> /var/cache/apk/<span class="k">*</span>
<span class="c"># Install Hex+Rebar</span>
<span class="k">RUN </span>mix local.hex <span class="nt">--force</span> <span class="o">&&</span> <span class="se">\
</span> mix local.rebar <span class="nt">--force</span>
<span class="k">WORKDIR</span><span class="s"> /opt/app</span>
<span class="k">ENV</span><span class="s"> MIX_ENV=prod</span>
<span class="c"># Cache elixir deps</span>
<span class="k">COPY</span><span class="s"> mix.exs mix.lock ./</span>
<span class="k">RUN </span>mix <span class="k">do </span>deps.get, deps.compile
<span class="c"># Cache node deps</span>
<span class="k">COPY</span><span class="s"> package.json ./</span>
<span class="k">RUN </span>npm <span class="nb">install</span>
<span class="k">COPY</span><span class="s"> . .</span>
<span class="k">RUN </span>./node_modules/brunch/bin/brunch b <span class="nt">-p</span> <span class="o">&&</span> <span class="se">\
</span> mix phoenix.digest
<span class="k">RUN </span>mix release <span class="nt">--env</span><span class="o">=</span>prod <span class="nt">--verbose</span>
</code></pre></div></div>
<h2 id="4-configure-your-app-with-env-variables">4. Configure your app with ENV variables</h2>
<p>The best way to provide runtime configuration is via environment variables. Remember <a href="https://12factor.net/">The Twelve-Factor App</a>? These principles still apply here.</p>
<p>Remove <code class="language-plaintext highlighter-rouge">config/prod.secret.exs</code> file and remove a reference to it from <code class="language-plaintext highlighter-rouge">config/prod.exs</code>. Configure your app’s secrets directly in <code class="language-plaintext highlighter-rouge">config/prod.exs</code> using environment variables:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># config/prod.exs</span>
<span class="c1">#</span>
<span class="c1"># Configure your app's endpoint.</span>
<span class="n">config</span> <span class="ss">:hi_docker</span><span class="p">,</span> <span class="no">HiDocker</span><span class="o">.</span><span class="no">Endpoint</span><span class="p">,</span>
<span class="ss">http:</span> <span class="p">[</span><span class="ss">port:</span> <span class="p">{</span><span class="ss">:system</span><span class="p">,</span> <span class="s2">"PORT"</span><span class="p">}],</span>
<span class="ss">url:</span> <span class="p">[</span><span class="ss">host:</span> <span class="s2">"${HOST}"</span><span class="p">,</span> <span class="ss">port:</span> <span class="p">{</span><span class="ss">:system</span><span class="p">,</span> <span class="s2">"PORT"</span><span class="p">}],</span>
<span class="ss">secret_key_base:</span> <span class="s2">"${SECRET_KEY_BASE}"</span><span class="p">,</span>
<span class="ss">cache_static_manifest:</span> <span class="s2">"priv/static/manifest.json"</span><span class="p">,</span>
<span class="ss">server:</span> <span class="no">true</span><span class="p">,</span>
<span class="ss">root:</span> <span class="s2">"."</span><span class="p">,</span>
<span class="ss">version:</span> <span class="no">Mix</span><span class="o">.</span><span class="no">Project</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="ss">:version</span><span class="p">]</span>
<span class="c1"># Configure your database</span>
<span class="n">config</span> <span class="ss">:hi_docker</span><span class="p">,</span> <span class="no">HiDocker</span><span class="o">.</span><span class="no">Repo</span><span class="p">,</span>
<span class="ss">adapter:</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Adapters</span><span class="o">.</span><span class="no">Postgres</span><span class="p">,</span>
<span class="ss">hostname:</span> <span class="s2">"${DB_HOST}"</span><span class="p">,</span>
<span class="ss">database:</span> <span class="s2">"${DB_NAME}"</span><span class="p">,</span>
<span class="ss">username:</span> <span class="s2">"${DB_USER}"</span><span class="p">,</span>
<span class="ss">password:</span> <span class="s2">"${DB_PASSWORD}"</span><span class="p">,</span>
<span class="ss">pool_size:</span> <span class="mi">20</span>
</code></pre></div></div>
<h2 id="5-run-your-app">5. Run your app</h2>
<p>No changes are needed for the default <code class="language-plaintext highlighter-rouge">Dockerfile.release</code> - it works as is!</p>
<p>Build production image:</p>
<ol>
<li><code class="language-plaintext highlighter-rouge">mix docker.build</code></li>
<li><code class="language-plaintext highlighter-rouge">mix docker.release</code></li>
</ol>
<p><strong>Run your production image!</strong></p>
<p><code class="language-plaintext highlighter-rouge">docker run -it --rm -p 8080:8080 -e PORT=8080 -e HOST=<domain-name> -e DB_HOST=<postgresql-domain> -e DB_NAME=hi_docker -e DB_USER=<postgresql-user> -e DB_PASSWORD=<postgresql-password> -e SECRET_KEY_BASE=<top-secret> hi_docker:release foreground</code></p>
<p>The above command starts a Docker container using your release image - <code class="language-plaintext highlighter-rouge">hi_docker:release</code>. It simply runs the Erlang release with your app in the “foreground” mode.</p>
<p>Breakdown of the switches:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">-it</code> a combination of 2 docker run switches: “-i” and “-t” to run your container in the “interactive” mode with TTY allocated, so that you could stop your container by pressing <code class="language-plaintext highlighter-rouge">Ctrl + C</code>.</li>
<li><code class="language-plaintext highlighter-rouge">--rm</code> tells docker to delete the container automatically after it is stopped. By default docker does not delete stopped containers, you could either delete them manually (<code class="language-plaintext highlighter-rouge">docker rm CID</code>) or use <code class="language-plaintext highlighter-rouge">--rm</code> to prevent “container pollution”.</li>
<li><code class="language-plaintext highlighter-rouge">-p 8080:8080</code> maps port 8080 on your machine to port 8080 inside of the docker container.</li>
<li><code class="language-plaintext highlighter-rouge">-e PORT=8080</code> sets environment variable <code class="language-plaintext highlighter-rouge">PORT</code> to 8080 inside of the container.</li>
<li><code class="language-plaintext highlighter-rouge">-e HOST=<domain-name></code> sets the ENV variable to be used by your app to generate URLs, this is your website’s domain name!</li>
<li><code class="language-plaintext highlighter-rouge">-e DB_HOST</code> and related <code class="language-plaintext highlighter-rouge">DB_</code> variables - no explanation needed.</li>
<li><code class="language-plaintext highlighter-rouge">-e SECRET_KEY_BASE=<top-secret></code> another ENV variable used by Phoenix to verify integrity of cookies.</li>
<li><code class="language-plaintext highlighter-rouge">hi_docker:release</code> name of your release image and a tag.</li>
<li><code class="language-plaintext highlighter-rouge">foreground</code> the argument for your app’s release executable, to tell Erlang to run your app in “foreground” mode: it logs everything into STDOUT and lets you stop the app by pressing <code class="language-plaintext highlighter-rouge">Ctrl + C</code>.</li>
</ul>
<p><strong>Postgresql settings</strong></p>
<p>I assume you’re running your production release image on you personal computer (for now) and <code class="language-plaintext highlighter-rouge">Postgresql</code> database server runs locally. By default Posgresql only allows connections from <code class="language-plaintext highlighter-rouge">localhost</code>. Your app is running inside of a container where the <code class="language-plaintext highlighter-rouge">localhost</code> means a different thing: you need to <a href="http://www.thegeekstuff.com/2014/02/enable-remote-postgresql-connection/?utm_source=tuicool">configure your Postgresql to allow remote connections</a>. If you’re running the macOS and Postgresql is installed via <a href="http://brew.sh/">Homebrew</a>, then your postgres config is likely located in <code class="language-plaintext highlighter-rouge">/usr/local/var/postgres</code>: both <code class="language-plaintext highlighter-rouge">pg_hba.conf</code> and <code class="language-plaintext highlighter-rouge">postgresql.conf</code>.</p>
<p><strong>Ready for production?</strong></p>
<p>Head over to <a href="http://teamon.eu/2017/setting-up-elixir-cluster-using-docker-and-rancher/">Tymon’s post on setting up Elixir Cluster Using Docker and Rancher</a>.</p>
<h2 id="6-conclusions">6. Conclusions</h2>
<p>As of this writing, this website is not running on Docker - it is still deployed using “classic” Erlang upgrade releases. My plan is to migrate to Docker, which would hopefully let me switch the focus from deployment to development :)</p>
<p>I feel a lot better about deploying my Phoenix apps with Docker: it is <strong>reliable</strong> and <strong>repeatable</strong>, exactly what deployment must be. I can build my release image on my personal computer and on a CI server, as part of CI build. I can run my release image anywhere Docker runs.</p>
<p>There ARE legitimate uses for hot code upgrades. <a href="https://blog.codeship.com/author/barryjones/">Barry Jones</a> from Codeship pointed out <a href="https://blog.codeship.com/comparing-elixir-go/">in his blog post</a> that upgrade releases are “a little bit more complex” and <code class="language-plaintext highlighter-rouge">distillery</code> <em>“goes out of its way to make this easy… but that still doesn’t mean you should always use it.”</em></p>
<p>How do you deploy your Phoenix apps?</p>My earlier attempts at setting up reliable and repeatable Elixir application deployments worked, but I didn’t feel completely safe. I really tried to setup the deployment of my Phoenix app “correctly”, sticking closely to “the standard” - Erlang/OTP, hot code reloading, upgrade releases and automatic versions. About 1/4 of the time upgrades simply didn’t work. The process being so reliable failed like a professional soldier: no complaining, no screaming and no emotions. edeliver would tell me that everything worked well, but the new code “wasn’t taking”: my changes were not visible on the website until I manually restarted the app on the server. BTW, this is why (as of this writing) this website displays app version in the footer of each page. After hours of troubleshooting I couldn’t figure out a pattern for these seemingly random failures.Deploying Phoenix Apps for Rails developers: Part 22017-01-21T04:00:00+00:002017-01-21T04:00:00+00:00https://shovik.com/2017/01/21/deploying-phoenix-apps-for-rails-developers-part2<p>This is part 2 of the Deploying Phoenix Apps for Rails developers series. In part 1 we created a bare bones Phoenix application and configured it to build and deploy <code class="language-plaintext highlighter-rouge">distillery</code> releases using <code class="language-plaintext highlighter-rouge">edeliver</code>. In this post we’re going to deploy an upgrade release and use Erlang’s hot code upgrades.</p>
<blockquote>
<p>The deployed upgrade will be <strong>available immediately, without restarting</strong> your application.</p>
</blockquote>
<p>— from <a href="https://github.com/boldpoker/edeliver#examples">edeliver documentation</a>.</p>
<ul>
<li><a href="/2017/01/11/deploying-phoenix-apps-for-rails-developers.html">Deploying Phoenix Apps for Rails developers: Part 1</a></li>
<li><a href="https://github.com/alex-kovshovik/edelivered_app/tree/0.0.1">Edelivered_app version 0.0.1</a> - this is the final state of the test app that we built in part 1, feel free to use it as a starting point. You’re going to need to change configuration in <code class="language-plaintext highlighter-rouge">.deliver/config</code> to point it to your server.</li>
</ul>
<p>This is how our app looks now:</p>
<p><img src="https://s3.amazonaws.com/shovik-com/uploads/post_images/7/01-deployed-app-before.png?v=63658318751" alt="Deployed Phoenix App" /></p>
<h2 id="upgrade-releases">Upgrade releases</h2>
<p>Before you continue I strongly encourage you to read <a href="https://hexdocs.pm/distillery/upgrades-and-downgrades.html">distillery documentation page on upgrades and downgrades</a> and brush up on <a href="https://github.com/boldpoker/edeliver#quick-start">edeliver quick start documentation</a>.</p>
<p>In order to deploy an upgrade, you need to build an “upgrade release”. Unlike regular releases, upgrade releases include <a href="http://erlang.org/doc/man/appup.html">appups</a> - instructions on how to upgrade a running application from one version to another. When building an upgrade release you must specify the “from” version and the “to” version, so that distillery can generate correct <code class="language-plaintext highlighter-rouge">appups</code>.</p>
<blockquote>
<p>Release archives in your release store that were created by the build release command <strong>cannot be used to deploy an upgrade</strong>.</p>
</blockquote>
<p>Upgrade releases let you deploy changes to your app <strong>without downtime</strong>, which is fantastic, however automatic upgrades won’t work every time because distillery won’t generate correct <code class="language-plaintext highlighter-rouge">Appup</code> for every possible upgrade scenario, although it usually does an admirable job.</p>
<p><strong>The plan:</strong></p>
<ol>
<li>Apply edeliver config workaround.</li>
<li>Make a change to our app.</li>
<li>Deploy the change.</li>
<li>Troubleshooting.</li>
<li>Conclusion.</li>
</ol>
<h2 id="1-apply-edeliver-config-workaround">1. Apply edeliver config workaround.</h2>
<p>As of this writing, <a href="https://github.com/boldpoker/edeliver/issues/182">edeliver support of distillery is broken</a>, but to fix it, all you need to do is to add one line to <code class="language-plaintext highlighter-rouge">:prod</code> environment configuration in <code class="language-plaintext highlighter-rouge">rel/config.exs</code>:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">environment</span> <span class="ss">:prod</span> <span class="k">do</span>
<span class="n">set</span> <span class="ss">include_erts:</span> <span class="no">true</span>
<span class="n">set</span> <span class="ss">include_src:</span> <span class="no">false</span>
<span class="n">set</span> <span class="ss">cookie:</span> <span class="ss">:"..."</span>
<span class="n">set</span> <span class="ss">output_dir:</span> <span class="s2">"rel/edelivered_app"</span> <span class="c1"># ADD THIS LINE!!!</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Let’s deploy the change the “old fashioned way”: by building a regular release, deploying it and then running it.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix edeliver stop production <span class="c"># Run this if production node is currently running</span>
<span class="nb">env </span><span class="nv">MIX_ENV</span><span class="o">=</span>prod mix edeliver build release production
mix edeliver deploy release to production
mix edeliver start production
</code></pre></div></div>
<h2 id="2-make-a-change-to-our-app">2. Make a change to our app.</h2>
<p>First make a few changes to your app and then bump the version in <code class="language-plaintext highlighter-rouge">config/mix.exs</code> to <code class="language-plaintext highlighter-rouge">0.0.2</code>. This is important, because we’re going to prepare and deploy an <strong>upgrade release</strong> from version <code class="language-plaintext highlighter-rouge">0.0.1</code> to version <code class="language-plaintext highlighter-rouge">0.0.2</code>:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="n">project</span> <span class="k">do</span>
<span class="p">[</span><span class="ss">app:</span> <span class="ss">:edelivered_app</span><span class="p">,</span>
<span class="ss">version:</span> <span class="s2">"0.0.2"</span><span class="p">,</span> <span class="c1"># Bump to 0.0.2!</span>
<span class="ss">elixir:</span> <span class="s2">"~> 1.4"</span><span class="p">,</span>
<span class="ss">elixirc_paths:</span> <span class="n">elixirc_paths</span><span class="p">(</span><span class="no">Mix</span><span class="o">.</span><span class="n">env</span><span class="p">),</span>
<span class="ss">compilers:</span> <span class="p">[</span><span class="ss">:phoenix</span><span class="p">,</span> <span class="ss">:gettext</span><span class="p">]</span> <span class="o">++</span> <span class="no">Mix</span><span class="o">.</span><span class="n">compilers</span><span class="p">,</span>
<span class="ss">build_embedded:</span> <span class="no">Mix</span><span class="o">.</span><span class="n">env</span> <span class="o">==</span> <span class="ss">:prod</span><span class="p">,</span>
<span class="ss">start_permanent:</span> <span class="no">Mix</span><span class="o">.</span><span class="n">env</span> <span class="o">==</span> <span class="ss">:prod</span><span class="p">,</span>
<span class="ss">aliases:</span> <span class="n">aliases</span><span class="p">(),</span>
<span class="ss">deps:</span> <span class="n">deps</span><span class="p">()]</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Commit your changes and tag them:</p>
<ol>
<li><code class="language-plaintext highlighter-rouge">git commit -am "Steal default Phoenix look to make Edeliver look"</code></li>
<li><code class="language-plaintext highlighter-rouge">git tag 0.0.2</code></li>
</ol>
<p>Again, no need to push your commits to a remote git repository (GitHub). Edeliver pushes commits and tags from your <code class="language-plaintext highlighter-rouge">local</code> git repository to the configured server(s).</p>
<h2 id="3-deploy-the-change">3. Deploy the change.</h2>
<p>Before we deploy the change, let’s make sure <code class="language-plaintext highlighter-rouge">edeliver</code> can connect to our running node and confirm its version:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix edeliver version production
</code></pre></div></div>
<p>You should see this:</p>
<p><img src="https://s3.amazonaws.com/shovik-com/uploads/post_images/7/02-mix-edeliver-version-production.png?v=63658353860" alt="Edeliver output" /></p>
<p>If you receive an error or <code class="language-plaintext highlighter-rouge">edeliver</code> tells you that the node is not running - head over to the troubleshooting section.</p>
<p><strong>Ready to build and deploy the upgrade release:</strong></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix edeliver upgrade production <span class="nt">--from</span><span class="o">=</span>0.0.1 <span class="nt">--to</span><span class="o">=</span>0.0.2
</code></pre></div></div>
<p>Command output:</p>
<p><img src="https://s3.amazonaws.com/shovik-com/uploads/post_images/7/03-mix-edeliver-upgrade-production.png?v=63658353963" alt="Command output" /></p>
<p>Refresh the page and voilà:</p>
<p><img src="https://s3.amazonaws.com/shovik-com/uploads/post_images/7/04-deployed-app-after.png?v=63658353968" alt="Updated Phoenix Application" /></p>
<h2 id="4-troubleshooting">4. Troubleshooting.</h2>
<p>Most of the points from the <a href="https://shovik.com/blog/6-deploying-phoenix-apps-for-rails-developers#troubleshooting">troubleshooting section of last post</a> still apply here. Here are a few specific ones that only apply to upgrades:</p>
<ul>
<li>If you’re upgrading your dependencies, hot upgrades might not work. Just do a clean release and restart the node.</li>
<li>Sometimes edeliver can’t see your node running on the server, even though it’s really running. In that case I had to SSH to each server and kill the Erlang process manually, then start the node: either using edeliver or manually.</li>
<li>When using <code class="language-plaintext highlighter-rouge">mix edeliver start production</code> sometimes I got “START SUCCESSFUL” message, but in reality nothing was started. I couldn’t find an explanation for it yet… In this case I just SSH to the server and do it manually.</li>
</ul>
<h2 id="5-conclusion">5. Conclusion</h2>
<p>The promise of Erlang runtime sounds great: it is a highly reliable system that can run forever and that you could deploy changes to with hot code reloading, without any downtime. This all sounds great, but in my limited experience so far the <em>go to proper way of deploying Elixir apps</em> is highly unreliable - complete opposite of what I expected. As of this writing I deployed about 15 upgrade releases to this website and had problems 1/4 of the time: sometimes even basic text changes <em>just didn’t take</em> LOL.</p>
<p>I’m sure my limited knowledge of Appups, Relups and releases in general is why I’m having all these problems. No worries, I’m going to try to deploy another way.</p>
<p>Tymon Tobolski make some great points in his <a href="http://teamon.eu/2017/deploying-phoenix-to-production-using-docker/">blog post about Depoying Phoenix to Production using Docker</a>:</p>
<ul>
<li>Unification: the typical production stack runs several types of applications, not just Elixir. If all of those apps can be deployed the same way - it’s a great win for DevOps.</li>
<li>Normally production environment doesn’t run on just one server and I like to think of those servers as disposable: if one server goes down - no big deal. Load balancer would quickly stop routing requests to it, some people would lose their websockets connections. That’s not a problem at all - their clients would just re-connect. As long as it doesn’t happen every minute - we’re fine.</li>
<li>People are excited about Docker and that excitement doesn’t seem to wane, so I’d like to see how docker deployment compares with edeliver deployment: reliability, repeatability.</li>
</ul>This is part 2 of the Deploying Phoenix Apps for Rails developers series. In part 1 we created a bare bones Phoenix application and configured it to build and deploy distillery releases using edeliver. In this post we’re going to deploy an upgrade release and use Erlang’s hot code upgrades.Deploying Phoenix Apps for Rails developers: Part 12017-01-12T04:00:00+00:002017-01-12T04:00:00+00:00https://shovik.com/2017/01/12/deploying-phoenix-apps-for-rails-developers<p>In Rails world <a href="https://github.com/capistrano/capistrano">capistrano gem</a> is pretty much a go to solution, it is proven over and over again in production, it has great documentation and everyone seem to agree that this is the way deploy a Rails application. In Elixir/Phoenix there is no such “go to” solution, not yet. In the <a href="https://www.dailydrip.com/blog/elixir-users-survey-2016-results">Elixir Users Survey 2016</a> done by <a href="https://github.com/knewter">Josh Adams</a> 33% of respondents use some ad-hoc way, another 30% use Docker, another 30% deploy to Heroku, finally only 14% use <a href="https://github.com/boldpoker/edeliver">edeliver</a> and the rest use some other technique. In this post we’re going to learn how to deploy Phoenix apps by doing, just follow along and if something doesn’t work - feel free to leave a comment and I’ll try to help you the best I could.</p>
<p><img src="https://s3.amazonaws.com/shovik-com/uploads/post_images/6/edeliver.jpg?v=63658147257" alt="Edeliver logo" /></p>
<p>At this point in time it not considered easy to deploy Phoenix. In his awesome talk about <a href="https://www.youtube.com/watch?v=H686MDn4Lo8&t=1317s&index=1&list=WL">Real World Elixir Deployment</a>, <a href="https://twitter.com/gamache">Pete Gamache</a> jokingly says: “You’re going to hurt… a lot” (or something to that effect). Big part of the problem, I think, is lack of the understanding of how deployments <u>should</u> be done in Elixir/Erlang. A lot of Elixir developers come from the world of Rails and so their minds are “infected” by knowledge of Capistrano. This is similar to the problem Subversion users have while trying to learn Git.</p>
<blockquote>
<p>git doesn’t have a high learning curve. svn has a high unlearning curve. observe the difference in those familiar with neither. From https://twitter.com/dlsspy/status/8914952195</p>
</blockquote>
<p>Erlang/OTP platform’s proper way of deploying apps is fundamentally different from most other platforms. Erlang VM is meant to run forever. The code changes are hot-loaded into the Erlang VM without a downtime. In Rails world apps are deployed as worker processes. When new code is deployed, new workers are started and old workers are killed. In order to reduce or remove downtime, old requests are kept running on old workers until they’re finished, new requests get routed to new workers. See <a href="https://www.phusionpassenger.com/">Phusion Passenger</a>, <a href="http://puma.io/">Puma</a> and <a href="https://bogomips.org/unicorn/">Unicorn</a>. Lately Rails apps are deployed as Docker containers, which in essence are processes that are completely self-contained and isolated from the host OS.</p>
<p>You <strong>can</strong> deploy Phoenix in a Docker container, but if you do that, you’ll loose the advantages of Erlang VM’s code-loading and built-in no downtime deployments.</p>
<h2 id="deployment-plan">Deployment plan</h2>
<p>It is important to learn the deployment flow in “correct”: order to troubleshoot deployments effectively. I made my way into deployments in the wrong order. I wanted to get something to work quickly: I started with edeliver, then after a few failed attempts I learned more about releases, then I learned about distillery - the latest release build tool. This process ended up taking me longer, because I didn’t understand the foundation of edeliver first: releases.</p>
<p>I assume you already have Erlang, Elixir, NodeJS and NPM installed locally on your local machine. If not, follow <a href="http://www.phoenixframework.org/docs/installation">this installation guide</a>.</p>
<p><strong>The plan:</strong></p>
<ol>
<li>Create brand new Phoenix application.</li>
<li>Build <a href="https://github.com/bitwalker/distillery">distillery</a> release.</li>
<li>Test distillery release locally.</li>
<li>Prepare ONE server in the cloud. We’re not going to create Erlang clusters today :)</li>
<li>Deploy distillery release to production using <a href="https://github.com/boldpoker/edeliver">edeliver</a>.</li>
<li>Troubleshooting.</li>
<li>What’s next?</li>
</ol>
<h2 id="1-create-brand-new-phoenix-application">1. Create brand new Phoenix application.</h2>
<p><a href="http://www.phoenixframework.org/docs/up-and-running">Follow this guide</a> or simply run the following command in your terminal to create an empty Phoenix application:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix phoenix.new edelivered_app
</code></pre></div></div>
<p>After the above your Phoenix application would be created in <code class="language-plaintext highlighter-rouge">edelivered_app</code> directory. Follow the instructions printed out by <code class="language-plaintext highlighter-rouge">mix phoenix.new</code> command and make sure the application runs on your local system.</p>
<p>Configure <code class="language-plaintext highlighter-rouge">config/prod.secret.exs</code> with correct database credentials. It is OK to use the same development database here - this file is not committed into a version control and you would have a different copy on your server(s). Your application must run OK in “prod” mode locally, so that you could build a production release.</p>
<p>Make sure this works:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">MIX_ENV</span><span class="o">=</span>prod mix ecto.create <span class="c"># create prod database if not the same as dev database.</span>
<span class="nv">MIX_ENV</span><span class="o">=</span>prod mix phoenix.server <span class="c"># run phoenix server in prod mode.</span>
</code></pre></div></div>
<p>Initialize an empty git repo and commit the initial version of your app. You won’t need to push your commits to a source code hosting sites, all you need is a local Git repo.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git init
git commit <span class="nt">-am</span> <span class="s2">"Initial commit. First!"</span>
</code></pre></div></div>
<h2 id="2-configure-distillery-to-build-releases">2. Configure distillery to build releases.</h2>
<p>From <a href="https://hexdocs.pm/distillery/terminology.html#content">distillery’s documentation</a>:</p>
<blockquote>
<p>A release is a package of your application’s .beam files, including it’s dependencies .beam files, a sys.config, a vm.args, a boot script, and various utilities and metadata files for managing the release once it is installed. A release may also contain a copy of ERTS (the Erlang Runtime System).</p>
</blockquote>
<p>Basically a <em>release</em> is the archive (<code class="language-plaintext highlighter-rouge">*.tar.gz</code>) with your compiled app and all its dependencies, including even Erlang Runtime itself.</p>
<p>Once built a release is distributed to your servers and deployed. Edeliver does that - see <a href="/2017/01/11/deploying-phoenix-apps-for-rails-developers.html#distillery">Configure distillery</a>.</p>
<p>Add the following to your <code class="language-plaintext highlighter-rouge">mix.exs</code>:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">defp</span> <span class="n">deps</span> <span class="k">do</span>
<span class="p">[</span>
<span class="c1">#...,</span>
<span class="p">{</span><span class="ss">:distillery</span><span class="p">,</span> <span class="s2">"~> 1.0"</span><span class="p">}</span>
<span class="p">]</span>
</code></pre></div></div>
<p>and run</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix deps.get
</code></pre></div></div>
<p>Create a release config file:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix release.init
</code></pre></div></div>
<p>Review the created <code class="language-plaintext highlighter-rouge">rel/config.exs</code> file. It would have you default release configured for 2 environments: <code class="language-plaintext highlighter-rouge">dev</code> and <code class="language-plaintext highlighter-rouge">prod</code>. You can add more releases and environments to the configuration, but we can stick with the default for now, with one exception.</p>
<p>There is no point in building and running a distillery release in dev mode, it didn’t work by default as of this writing: https://github.com/bitwalker/distillery/issues/25</p>
<p>Let’s configure our release to build <code class="language-plaintext highlighter-rouge">prod</code> environment by default. Change <code class="language-plaintext highlighter-rouge">default_environment</code> in <code class="language-plaintext highlighter-rouge">rel/config.exs</code> to “prod”:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">use</span> <span class="no">Mix</span><span class="o">.</span><span class="no">Releases</span><span class="o">.</span><span class="no">Config</span><span class="p">,</span>
<span class="c1"># This sets the default release built by `mix release`</span>
<span class="ss">default_release:</span> <span class="ss">:default</span><span class="p">,</span>
<span class="c1"># This sets the default environment used by `mix release`</span>
<span class="ss">default_environment:</span> <span class="ss">:prod</span> <span class="c1"># <------ SET THIS TO :prod</span>
</code></pre></div></div>
<p>Make sure to review the <a href="https://hexdocs.pm/distillery/terminology.html#content">distillery terminology page</a> it will save you a lot of frustration in reading error messages and googling for solutions later on.</p>
<p>Change the Enpoint in your <code class="language-plaintext highlighter-rouge">config/prod.exs</code> to look like this:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">config</span> <span class="ss">:edelivered_app</span><span class="p">,</span> <span class="no">EdeliveredApp</span><span class="o">.</span><span class="no">Endpoint</span><span class="p">,</span>
<span class="ss">http:</span> <span class="p">[</span><span class="ss">port:</span> <span class="p">{</span><span class="ss">:system</span><span class="p">,</span> <span class="s2">"PORT"</span><span class="p">}],</span>
<span class="ss">url:</span> <span class="p">[</span><span class="ss">host:</span> <span class="s2">"localhost"</span><span class="p">,</span> <span class="ss">port:</span> <span class="p">{</span><span class="ss">:system</span><span class="p">,</span> <span class="s2">"PORT"</span><span class="p">}],</span>
<span class="ss">cache_static_manifest:</span> <span class="s2">"priv/static/manifest.json"</span><span class="p">,</span>
<span class="ss">server:</span> <span class="no">true</span><span class="p">,</span>
<span class="ss">root:</span> <span class="s2">"."</span><span class="p">,</span>
<span class="ss">version:</span> <span class="no">Mix</span><span class="o">.</span><span class="no">Project</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="ss">:version</span><span class="p">]</span>
</code></pre></div></div>
<p>It is important to have matching port for both <code class="language-plaintext highlighter-rouge">http</code> and <code class="language-plaintext highlighter-rouge">url</code> options. See <a href="https://hexdocs.pm/distillery/use-with-phoenix.html#configuring-your-release">Using Distillery With Phoenix</a> for explanations of these options.</p>
<p>Now you’re ready to build your first release.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">MIX_ENV</span><span class="o">=</span>prod mix release
</code></pre></div></div>
<p>We run <code class="language-plaintext highlighter-rouge">mix release</code> command with <code class="language-plaintext highlighter-rouge">MIX_ENV</code> set to “prod”, because even if you set your default environment to :prod - distillery still builds your distillery “prod” environment using Mix “dev” environment configuration. Those 2 are not the same thing.</p>
<p>Now you have a production release built!</p>
<h2 id="3-test-distillery-release-locally">3. Test distillery release locally.</h2>
<p>Releases are placed under <code class="language-plaintext highlighter-rouge">_build/<env>/rel/<app-name></code> directory by default and you can run a release from within that directory, however to demonstrate the portability of the elixir releases, let’s extract our release archive into a separate folder and run it from there.</p>
<ol>
<li>Create a directory for your app: <code class="language-plaintext highlighter-rouge">mkdir ~/Downloads/edelivered_app</code></li>
<li>Copy your release into that directory:<code class="language-plaintext highlighter-rouge">cp _build/prod/rel/edelivered_app/releases/0.0.1/edelivered_app.tar.gz ~/Downloads/edelivered_app/</code></li>
<li>Switch to destination directory: <code class="language-plaintext highlighter-rouge">cd ~/Downloads/edelivered_app/</code></li>
<li>Extract files from the archive: <code class="language-plaintext highlighter-rouge">tar -zxvf edelivered_app.tar.gz</code>. I can never remember those tar switches - I just don’t extract <code class="language-plaintext highlighter-rouge">*.tar.gz</code> files often enough, and so I have a dedicated Evernote document - just for how to extract. <strong>Idea:</strong> create a simple script and name it <code class="language-plaintext highlighter-rouge">untar</code>.</li>
<li>Run the app: <code class="language-plaintext highlighter-rouge">PORT=8080 bin/edelivered_app foreground</code>. Make sure to set the <strong>required</strong> environment variable PORT to a desired port.</li>
</ol>
<p>We’re using the “foreground” command to start your app in foreground to begin with - just to see any errors right away.</p>
<p>If the app runs OK in foreground you can start it as a daemon:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">PORT</span><span class="o">=</span>8080 bin/edelivered_app start
</code></pre></div></div>
<p>Use the same script to stop, restart and even attach to your app. The following are the most useful:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">bin/edelivered_app</code> outputs the list of available commands.</li>
<li><code class="language-plaintext highlighter-rouge">bin/edelivered_app stop</code> stops the app.</li>
<li><code class="language-plaintext highlighter-rouge">bin/edelivered_app ping</code> responds with “pong” if everything is OK with your app.</li>
<li><code class="language-plaintext highlighter-rouge">bin/edelivered_app remote_console</code> connects to the running release with IEx console. Unlike “console” or “attach”, “remote_console” would not terminate the running release if you quit the console.</li>
</ul>
<p>Once deployed you can use the same commands on your production server(s).</p>
<p>Commit your changes: <code class="language-plaintext highlighter-rouge">git commit -am "Add distillery dependency"</code></p>
<p>For additional details about building releases follow installation instructions from <a href="https://github.com/bitwalker/distillery">distillery’s GitHub page</a> or read this awesome guide for <a href="https://hexdocs.pm/distillery/use-with-phoenix.html">Using Distillery With Phoenix</a>. I recommend reading the entire distillery documentation actually - it explains a lot about releases and provides tips on troubleshooting, which I’m going to go over at the end of this post.</p>
<h2 id="4-prepare-one-server-in-the-cloud">4. Prepare ONE server in the cloud.</h2>
<p>You’ll need an Ubuntu 16.04 Server with the IP address and a SSH port 22 open, with root access.</p>
<p>Use any of the cloud providers to create a new server. I use <a href="https://www.digitalocean.com/">Digital Ocean</a> for my sites and I had great success with <a href="https://www.linode.com/">Linode</a>, both options have very competitive pricing. All you need is a server with at least 1Gb of RAM. You CAN go with a cheaper 512Mb option, but you’ll need to increase the swap space in order to even build releases on that server, which would be VERY slow. This is <a href="https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-16-04">how to increase swap space on Ubuntu 16.04</a>, for example. The red warning box at the top of the post should hopefully dissuade you from doing that.</p>
<p>Another option is to install Ubuntu 16.04 LTS in a virtual machine. <a href="https://www.virtualbox.org/">VirtualBox</a> would work. Configure your VM’s network with NAT (Network Address Translation) to access it via SSH and HTTP/HTTPS.</p>
<p>For the sake of simplicity we’re not going to use Chef, Ansible or any other DevOps’y tools.</p>
<p>Configure the following:</p>
<ol>
<li>Create new user named “app” with home directory: <code class="language-plaintext highlighter-rouge">adduser app</code></li>
<li>Put your public RSA key in <code class="language-plaintext highlighter-rouge">/home/app/.ssh/authorized_keys</code> to SSH to the server without having to type in password. Create <code class="language-plaintext highlighter-rouge">.ssh</code> directory if doesn’t exist.</li>
<li>Set the following permissions:
<ul>
<li><code class="language-plaintext highlighter-rouge">chmod 700 /home/app/.ssh</code></li>
<li><code class="language-plaintext highlighter-rouge">chmod 644 /home/app/.ssh/authorized_keys</code></li>
<li><code class="language-plaintext highlighter-rouge">chown -R app:app /home/app/.ssh</code></li>
</ul>
</li>
<li>Add user “app” to group <code class="language-plaintext highlighter-rouge">sudo</code> for convenience: edit /etc/group file and tack <code class="language-plaintext highlighter-rouge">app</code> to the end of the “sudo” line.</li>
<li>SSH to the server as user <code class="language-plaintext highlighter-rouge">app</code>, this shouldn’t ask you for a password.</li>
<li>Follow <a href="http://elixir-lang.org/install.html#unix-and-unix-like">Ubuntu installation instructions for Elixir</a>. Install Erlang and Elixir.</li>
<li>Follow <a href="http://tecadmin.net/install-latest-nodejs-npm-on-ubuntu/">How to install NodeJs and NPM on Ubuntu</a>.</li>
<li>Install git: <code class="language-plaintext highlighter-rouge">sudo apt-get install git</code></li>
<li>Install Postgres database server: <code class="language-plaintext highlighter-rouge">sudo apt-get install postgresql postgresql-contrib</code></li>
<li>Configure Postgres role for our app:
<ul>
<li>Switch to linux user “postgres”: <code class="language-plaintext highlighter-rouge">sudo su - postgres</code></li>
<li>Launch Postgres console client: <code class="language-plaintext highlighter-rouge">psql</code></li>
<li><code class="language-plaintext highlighter-rouge">CREATE ROLE app WITH superuser;</code></li>
<li><code class="language-plaintext highlighter-rouge">ALTER ROLE app WITH login;</code></li>
<li><code class="language-plaintext highlighter-rouge">ALTER ROLE app WITH createdb;</code></li>
<li><code class="language-plaintext highlighter-rouge">ALTER USER app WITH PASSWORD 'coolpass';</code> Please don’t use this password on real servers. :)</li>
<li>Exit Postres console client: Ctrd+D.</li>
<li>Exit postgres user: exit or Ctrl+D.</li>
</ul>
</li>
<li>Create Postgres database for your app: <code class="language-plaintext highlighter-rouge">createdb edelivered_app_prod</code></li>
<li>Create a directory for your application and settings: <code class="language-plaintext highlighter-rouge">mkdir /home/app/mysite.com</code></li>
<li>Create a directory for the release store: <code class="language-plaintext highlighter-rouge">mkdir /home/app/mysite.com/edeliver_release_store</code></li>
<li>Add a global environment variable PORT: <code class="language-plaintext highlighter-rouge">echo "PORT=8080" | sudo tee -a /etc/environment</code></li>
<li>Add another global environment variable MIX_ENV: <code class="language-plaintext highlighter-rouge">echo "MIX_ENV=prod" | sudo tee -a /etc/environment</code></li>
</ol>
<h2 id="5-deploy-distillery-release-to-production-using-edeliver">5. Deploy distillery release to production using Edeliver.</h2>
<p>Edeliver is set of smart scripts that would build the release using <code class="language-plaintext highlighter-rouge">distillery</code> and deploy it to a number of servers. Unlike Rails and Capistrano, Edeliver uploads your code (using Git) to only ONE of your servers (a build server), compiles it there, compiles and digests assets there and then deploys the prepared package on all servers. Capistrano deploys code to ALL servers and compiles assets on ALL servers (by default).</p>
<p>There is one limitation though: a release must be built on the similar Erlang VM, same architecture. To make matters easier, our “build host” and “production host” are the same host.</p>
<p>Add <code class="language-plaintext highlighter-rouge">edeliver</code> to your project dependencies and applications:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="n">application</span><span class="p">,</span> <span class="k">do</span><span class="p">:</span> <span class="p">[</span>
<span class="ss">applications:</span> <span class="p">[</span>
<span class="o">...</span>
<span class="c1"># Add edeliver to the END of the list</span>
<span class="ss">:edeliver</span>
<span class="p">]</span>
<span class="p">]</span>
<span class="k">defp</span> <span class="n">deps</span> <span class="k">do</span>
<span class="p">[</span>
<span class="o">...</span>
<span class="p">{</span><span class="ss">:edeliver</span><span class="p">,</span> <span class="s2">"~> 1.4.0"</span><span class="p">}</span>
<span class="p">]</span>
<span class="k">end</span>
</code></pre></div></div>
<p>and run</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mix deps.get
</code></pre></div></div>
<p>In your project directory, create <code class="language-plaintext highlighter-rouge">.deliver/config</code> file:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/usr/bin/env bash</span>
<span class="nv">APP</span><span class="o">=</span><span class="s2">"edelivered_app"</span> <span class="c"># <--- THIS MUST MATCH THE NAME OF THE RELEASE IN rel/config.exs</span>
<span class="c"># AND THE NAME OF THE APP IN config/mix.exs!!!!!!!!!!</span>
<span class="c"># Configuration of where the releases would be built.</span>
<span class="nv">BUILD_HOST</span><span class="o">=</span><span class="s2">"138.197.37.15"</span> <span class="c"># change to your server's IP address</span>
<span class="nv">BUILD_USER</span><span class="o">=</span><span class="s2">"app"</span>
<span class="nv">BUILD_AT</span><span class="o">=</span><span class="s2">"/home/app/mysite.com/edeliver_builds"</span>
<span class="c"># The location where built releases are going to be stored.</span>
<span class="nv">RELEASE_STORE</span><span class="o">=</span>app@138.197.37.15:/home/app/mysite.com/edeliver_release_store/
<span class="c"># Host and use of where the app would run.</span>
<span class="nv">PRODUCTION_HOSTS</span><span class="o">=</span><span class="s2">"138.197.37.15"</span> <span class="c"># same host in our case.</span>
<span class="nv">PRODUCTION_USER</span><span class="o">=</span><span class="s2">"app"</span>
<span class="nv">DELIVER_TO</span><span class="o">=</span><span class="s2">"/home/app/mysite.com"</span>
pre_erlang_get_and_update_deps<span class="o">()</span> <span class="o">{</span>
<span class="c"># copy it on the build host to the build directory when building</span>
<span class="nb">local </span><span class="nv">_secret_config_file_on_build_host</span><span class="o">=</span><span class="s2">"/home/app/mysite.com/prod.secret.exs"</span>
status <span class="s2">"Linking '</span><span class="nv">$_secret_config_file_on_build_host</span><span class="s2">' to build config dir"</span>
__sync_remote <span class="s2">"
ln -sfn '</span><span class="nv">$_secret_config_file_on_build_host</span><span class="s2">' '</span><span class="nv">$BUILD_AT</span><span class="s2">/config/prod.secret.exs'
"</span>
<span class="o">}</span>
pre_erlang_clean_compile<span class="o">()</span> <span class="o">{</span>
status <span class="s2">"Installing nodejs dependencies"</span>
__sync_remote <span class="s2">"
[ -f ~/.profile ] && source ~/.profile
set -e
cd '</span><span class="nv">$BUILD_AT</span><span class="s2">'
APP='</span><span class="nv">$APP</span><span class="s2">' MIX_ENV='</span><span class="nv">$TARGET_MIX_ENV</span><span class="s2">' npm install
"</span>
status <span class="s2">"Building brunch assets"</span>
__sync_remote <span class="s2">"
[ -f ~/.profile ] && source ~/.profile
set -e
cd '</span><span class="nv">$BUILD_AT</span><span class="s2">'
mkdir -p priv/static
APP='</span><span class="nv">$APP</span><span class="s2">' MIX_ENV='</span><span class="nv">$TARGET_MIX_ENV</span><span class="s2">' npm run deploy
"</span>
status <span class="s2">"Compiling code"</span>
__sync_remote <span class="s2">"
[ -f ~/.profile ] && source ~/.profile
set -e #
cd '</span><span class="nv">$BUILD_AT</span><span class="s2">'
APP='</span><span class="nv">$APP</span><span class="s2">' MIX_ENV='</span><span class="nv">$TARGET_MIX_ENV</span><span class="s2">' </span><span class="nv">$MIX_CMD</span><span class="s2"> do deps.get, compile
"</span>
status <span class="s2">"Running phoenix.digest"</span>
__sync_remote <span class="s2">"
[ -f ~/.profile ] && source ~/.profile
set -e #
cd '</span><span class="nv">$BUILD_AT</span><span class="s2">'
APP='</span><span class="nv">$APP</span><span class="s2">' MIX_ENV='</span><span class="nv">$TARGET_MIX_ENV</span><span class="s2">' </span><span class="nv">$MIX_CMD</span><span class="s2"> phoenix.digest </span><span class="nv">$SILENCE</span><span class="s2">
"</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The above configuration would generate the following directory structure on the server:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/home/app/mysite.com # Your entire app is in one place: configuration, builds and releases.
├── edeliver_builds # This is where edeliver builds all releases: your local repo is pushed here.
│ ├── brunch-config.js
│ ├── _build
│ ├── config
│ ├── deps
│ ├── lib
│ ├── mix.exs
│ ├── mix.lock
│ ├── node_modules
│ ├── package.json
│ ├── priv
│ ├── README.md
│ ├── rel
│ ├── test
│ └── web
├── edelivered_app # Your app is deployed here: this is where it runs, from "bin" directory.
│ ├── bin
│ ├── erl_crash.dump
│ ├── erts-8.2
│ ├── lib
│ ├── releases
│ └── var
├── edeliver_release_store # Edeliver stores all built releases here to then distribute them to servers.
│ └── edelivered_app_0.0.1.release.tar.gz
└── prod.secret.exs # Your app's secrets: production database connection parameters.
</code></pre></div></div>
<p>Commit the changes and tag the commit with “0.0.1” - a version that matches your project’s version in <code class="language-plaintext highlighter-rouge">mix.exs</code>.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git commit <span class="nt">-am</span> <span class="s2">"Add edeliver configuration"</span>
git tag 0.0.1
</code></pre></div></div>
<p>Tags will be needed for edeliver upgrade releases in the next post.</p>
<p>SSH to your server and create file <code class="language-plaintext highlighter-rouge">/home/app/mysite.com/prod.secret.exs</code> with the following contents:</p>
<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">use</span> <span class="no">Mix</span><span class="o">.</span><span class="no">Config</span>
<span class="n">config</span> <span class="ss">:edelivered_app</span><span class="p">,</span> <span class="no">EdeliveredApp</span><span class="o">.</span><span class="no">Endpoint</span><span class="p">,</span>
<span class="ss">secret_key_base:</span> <span class="s2">"Xt317VM159wCrVgKhatAAbJcz3/yYewpbuXEpBeUpiIEOBVrTWEW878d6vADJU2u"</span>
<span class="n">config</span> <span class="ss">:edelivered_app</span><span class="p">,</span> <span class="no">EdeliveredApp</span><span class="o">.</span><span class="no">Repo</span><span class="p">,</span>
<span class="ss">adapter:</span> <span class="no">Ecto</span><span class="o">.</span><span class="no">Adapters</span><span class="o">.</span><span class="no">Postgres</span><span class="p">,</span>
<span class="ss">username:</span> <span class="s2">"app"</span><span class="p">,</span>
<span class="ss">password:</span> <span class="s2">"coolpass"</span><span class="p">,</span>
<span class="ss">database:</span> <span class="s2">"edelivered_app_prod"</span><span class="p">,</span>
<span class="ss">pool_size:</span> <span class="mi">20</span>
</code></pre></div></div>
<p>Build release, deploy it and run it:</p>
<ol>
<li><code class="language-plaintext highlighter-rouge">env MIX_ENV=prod mix edeliver build release</code> builds the release and puts it into the release store on the server.</li>
<li><code class="language-plaintext highlighter-rouge">mix edeliver deploy release to production --version=0.0.1</code> deploys the release to all servers, but doesn’t run it!</li>
<li>Try running your release in foreground mode after the first deploy, just in case:
<ul>
<li>SSH to the server as user <code class="language-plaintext highlighter-rouge">app</code>.</li>
</ul>
</li>
<li><code class="language-plaintext highlighter-rouge">cd ~/mysite.com/edelivered_app</code></li>
<li><code class="language-plaintext highlighter-rouge">bin/edelivered_app foreground</code></li>
<li>Make sure it started and try to open it in your browser: <a href="http://138.197.37.15:8080">http://138.197.37.15:8080</a> (your IP address will be different, of course)</li>
<li>Exit out of it: Ctrl+C</li>
<li><code class="language-plaintext highlighter-rouge">mix edeliver start production</code> runs your app as a daemon on your server!</li>
</ol>
<p>Sometimes start command doesn’t start the release for some reason and I didn’t yet get to the core of the problem. If your site is not available after the <code class="language-plaintext highlighter-rouge">mix edeliver start production</code>, SSH to the server and see if Erlang process is running: <code class="language-plaintext highlighter-rouge">ps -ef | grep erl</code>. If not - see the troubleshooting section below.</p>
<h2 id="6-troubleshooting">6. Troubleshooting</h2>
<p>The latest version of the sample Phoenix app with distillery and edeliver configuration is available here: https://github.com/alex-kovshovik/edelivered_app.</p>
<ul>
<li>Always include <code class="language-plaintext highlighter-rouge">MIX_ENV=prod</code> in all deployment commands.</li>
<li>If something doesn’t work: remove the <code class="language-plaintext highlighter-rouge">_build</code> directory and start over.</li>
<li>Don’t use AUTO_VERSION right from the start: get the manual versions to work first. I had all kinds of trouble trying to make deployment as easy as the Capistrano deployment. Always bump the version of your app and create tag with the same name.</li>
<li>Variable APP must match the release name in rel/config.exs. If doesn’t match, you get: “Failed to build release: :no_release.”</li>
<li>Variable APP must match the actual name of the app, otherwise it’ll fail to start! I tried to change it to “current_release” to optimize the server directory structure and was getting unexpected errors.</li>
<li>Useful mix edeliver switches:</li>
<li><code class="language-plaintext highlighter-rouge">--verbose</code> displays the output of all commands;</li>
<li><code class="language-plaintext highlighter-rouge">--debug</code> displays all commands and their output.</li>
<li>Fix <code class="language-plaintext highlighter-rouge">rel/config.exs</code> as explained in <a href="https://github.com/boldpoker/edeliver/issues/182">distillery support broke with changed output_dir in distillery 1.0.0 - Issue #182</a> - first comment.</li>
</ul>
<h2 id="7-whats-next">7. What’s next?</h2>
<p>In the next post(s) we’re going to explore:</p>
<ol>
<li>Configuration of <a href="https://nginx.org/en/">nginx</a> as a reverse proxy for our Erlang VM.</li>
<li>Create and configure SSL certificate using <a href="https://letsencrypt.org/">letsencrypt</a>.</li>
<li>Build and deploy upgrade releases.</li>
<li>Building releases in a docker container or on local VM - to speed up the builds.</li>
<li>Build releases on CI server.</li>
</ol>
<p><a href="/2017/01/20/deploying-phoenix-apps-for-rails-developers-part2.html">Deploying Phoenix Apps for Rails Developers - Part 2</a></p>In Rails world capistrano gem is pretty much a go to solution, it is proven over and over again in production, it has great documentation and everyone seem to agree that this is the way deploy a Rails application. In Elixir/Phoenix there is no such “go to” solution, not yet. In the Elixir Users Survey 2016 done by Josh Adams 33% of respondents use some ad-hoc way, another 30% use Docker, another 30% deploy to Heroku, finally only 14% use edeliver and the rest use some other technique. In this post we’re going to learn how to deploy Phoenix apps by doing, just follow along and if something doesn’t work - feel free to leave a comment and I’ll try to help you the best I could.Migrating a website to new server with minimal downtime2014-07-23T04:00:00+00:002014-07-23T04:00:00+00:00https://shovik.com/2014/07/23/migrating-a-website-to-new-server-with-minimal-downtime<p>Once a customer asked me to move their smallish website to a new server: to deploy enhancements and upgrade OS with latest security patches. They were too afraid to upgrade Linux OS on the old one for it was too far behind with upgrades. There was one requirement: “please keep the downtime to a minimum”.</p>
<h2 id="warnings-and-limitations">Warnings and Limitations</h2>
<p>This post is about relatively small websites with tiny databases:</p>
<ul>
<li>One server with everything on it: web server, database, files.</li>
<li>Up to ~5 Gb database.</li>
<li>Up to ~5 Gb of files.</li>
</ul>
<p>It wouldn’t work for larger sites because it takes a lot more time to back them up and restore, so for these sites some DB replication should probably be setup, etc.</p>
<p>My website is a Rails application, but this same technique should apply for anything else out there.</p>
<h2 id="introduction">Introduction</h2>
<p>Sometimes it is necessary to move a website or a couple from one VPS host to another. In my case I was moving one website from one Digital Ocean droplet to another, just because I decided to upgrade to the latest LTS version of Ubuntu Server - 14.04. I do not have any load balancing setup with multiple hosts involved, nor do I have any HA solutions in place. Everything is on one host: web server, Rails application, database, file storage, etc. If I had a dedicated load balancer host I could’ve switched backends with ease.</p>
<p>I didn’t really have to, but I decided I’d try to migrate with very little or no downtime - just to learn how it’s done. Turns out it is not as hard as I thought.</p>
<p>The biggest issue is DNS propagation: it is not fast, sometimes it takes a couple of hours to fully propagate, especially if you’re changing your name servers at the same time. During this time it is possible that some requests would go to a new server, some would still hit the old one. This means that your users could create content on both sites, which is a problem.</p>
<h2 id="high-level-steps">High level steps</h2>
<ol>
<li>Configure new host with the website.</li>
<li>Verify website on the new host using hosts file.</li>
<li>Configure nginx on the old host to proxy all requests to new one.</li>
<li>Figure out SSL thing.</li>
</ol>
<h2 id="details">Details</h2>
<h3 id="1-configure-new-host-with-the-website">1. Configure new host with the website</h3>
<p>There is a ton of information on how to configure new Linux server with webserver and a website, so I wouldn’t go into a lot of detail here.</p>
<p>For example: <a href="https://www.digitalocean.com/community/tutorials/how-to-install-ruby-on-rails-on-ubuntu-14-04-using-rvm">How To Install Ruby on Rails on Ubuntu 14.04 using RVM</a></p>
<h3 id="2-verify-website-on-the-new-host-using-hosts-file">2. Verify website on the new host using hosts file</h3>
<p>At this point DNS is still configured to send all requests to the old host we’re trying to migrate off of. There is a very easy way to configure your own computer to send all requests for a specific URL to the new host.</p>
<p>Edit your “hosts” file:</p>
<ul>
<li>Linux or Mac: edit /etc/hosts</li>
<li>Windows: http://www.rackspace.com/knowledge_center/article/how-do-i-modify-my-hosts-file#Windows_Vista</li>
</ul>
<p>Add a line like this to the bottom of it:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>192.168.253.17 yourcoolwebsite.com
</code></pre></div></div>
<p>This will point all requests for yourcoolwebsite.com to 192.168.253.17.</p>
<p><strong>Please note:</strong> use your domain name without http or https.</p>
<p>After the above you could just open your website in any browser and you would be testing it on the new host while everybody else on the Internet would still be using old host.</p>
<h3 id="3-configure-nginx-on-the-old-host-to-proxy-all-requests-to-a-new-one">3. Configure nginx on the old host to proxy all requests to a new one</h3>
<p>I started from <a href="http://nginx.org/en/docs/beginners_guide.html#proxy">this article from nginx documentation</a>. It shows how to configure a pretty basic proxy, which is more than enough for our cause.</p>
<p>Also since I need to proxy ALL requests to the new server, the nginx “server” config is very basic:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server <span class="o">{</span>
listen 80<span class="p">;</span>
server_name yourdomain.com<span class="p">;</span>
location / <span class="o">{</span>
proxy_pass http://192.168.1.1:8080<span class="p">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>This tells nginx to proxy all requests to IP 192.168.1.1, port 8080.</p>
<p>At this point we’re going to need to move quickly, so prepare to perform the following steps:</p>
<ol>
<li>Stop web server on the old server.</li>
<li>Make a backup of the database and any user files. This ensures that all the latest data created by your users isn’t lost and that the users can’t create any new data on the old server until all requests are proxies to the new one.</li>
<li>Move the backups to the new server.</li>
<li>Restore backups on the new server.</li>
<li>Start the proxy on the old server. In my case I had Apache running as a web server on the old host, so I only had to shutdown Apache and start nginx. If you had nginx as a web server already, then you’ll only have to comment out the old “server” section of your website and uncomment the new “server” section with the proxy configuration.</li>
<li>Change DNS to point your domain to the new server.</li>
</ol>
<h3 id="4-figure-out-ssl-thing">4. Figure out SSL thing</h3>
<p>If you have SSL/HTTPS configured on your site, then you’ll need to do a bit more work, because you don’t want to configure a double SSL encryption:</p>
<ol>
<li>Between your user and Nginx proxy.</li>
<li>Between Nginx proxy and a new server.</li>
</ol>
<p>To solve this I came up with a very simple idea: configure new server to run a website on port 80 (redirects to https 443) and port 8080, which is only accessible for connections from the old server’s IP address. With that my system only has SSL between the end user and nginx proxy. All the requests between the proxy and a new server are transmitted unencrypted.</p>
<p>I guess, this is a security concern, but this it is within the same datacenter (which I trust), this wasn’t a big deal for me.</p>
<p>For the proxy I ended up with the following configuration:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server <span class="o">{</span>
listen 80<span class="p">;</span>
listen 443 ssl<span class="p">;</span>
ssl_certificate cert_file_name_and_path<span class="p">;</span>
ssl_certificate_key key_file_name_and_path<span class="p">;</span>
server_name yourdomain.com<span class="p">;</span>
location / <span class="o">{</span>
proxy_pass http://192.168.1.1:8080<span class="p">;</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="nv">$ssl_protocol</span> <span class="o">=</span> <span class="s2">""</span><span class="o">)</span> <span class="o">{</span>
rewrite ^ https://<span class="nv">$server_name$request_uri</span>? permanent<span class="p">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>This setup has to only be in place for a couple of hours - while DNS changes propagate. After that the old server could just be shut down and port 8080 on the new server could be closed.</p>
<p>I’m going to move another website and I’ll follow this blog post and would update stuff as I go, but the general idea should be clear.</p>
<p><em>Originally published at alex.shovik.com on July 22, 2014.</em></p>Once a customer asked me to move their smallish website to a new server: to deploy enhancements and upgrade OS with latest security patches. They were too afraid to upgrade Linux OS on the old one for it was too far behind with upgrades. There was one requirement: “please keep the downtime to a minimum”.Thoughts on GTD and how to stay productive2014-05-13T04:00:00+00:002014-05-13T04:00:00+00:00https://shovik.com/2014/05/13/thoughts-on-gtd-and-how-to-stay-productive<p>Any job requires a worker to perform various tasks daily. In some cases there is just one task that never changes. For example, a manufacturing line where workers are forced to do the same operation over and over again.</p>
<p>Most IT jobs however are not that simple, there is always several tasks in line, always the need to prioritize, always distractions of the office environment: multiple email accounts, several IM clients, colleagues arguing loudly in the hallway and THE WORST OF ALL: Internet at your fingertips!</p>
<p>When I started my programmer career my job resembled a manufacturing job closer than an office job: I had just one task, there were almost no deadlines, since I worked for a government organization, no pressure, NO INTERNET CONNECTION - Internet was blocked and I had to use books and MSDN documentation from a set of CDs.</p>
<p>Now things changed. I’m working for a successful private company on a “Senior Developer” position, where 30% or so of my time (according to <a href="https://www.rescuetime.com/">Rescue Time</a>) or less is actual programming (duh) and the rest is project management: scheduling, feedback, meetings, phone conversations, emails, IMs, requirements, etc. There were days when I work with no breaks and still don’t get anything done, which is odd - how can that be?</p>
<h2 id="procrastination">Procrastination!</h2>
<p>Procrastination is exacly what the corresponding Wiki article says: putting off important stuff and doing small quick things because important stuff is HARD! Really?! Why? Because programmer folk are lazy turds!</p>
<p>This blog post I’m writing right now, for example, is one of such “hard” tasks, because it requires both imagination and deep concentration - a “dangerous” mix. Majority of “hard” tasks that I perform daily only require one of these things, mostly deep concentration. And this is a problem, because in 2014 deep concentration is a rare resource. Internet taught me how to quickly skim though text, jumping between pages by clicking links - searching for interesting stuff: political commentary, disaster coverage, funny pictures and videos, celebrity scandals, new tech gadgets, IT rumors, etc.</p>
<p>Ask yourself:</p>
<ul>
<li>Are these things making you better?</li>
<li>Do you achieve something worthy?</li>
<li>Do you move forward?</li>
</ul>
<p>The answer is NO!</p>
<p>On top of that there is always a slew of emails from colleagues that take less than a minute each to reply, why not do that right now? I can work on that beast of a task later… Then comes lunch time and nothing important was done.</p>
<p><img src="https://s3.amazonaws.com/shovik-com/uploads/post_images/4/procrastination-cycle.jpg?v=63658145837" alt="Procrastination cycle" /></p>
<h2 id="gtd-getting-things-done">GTD: Getting Things Done</h2>
<p>When I was just a programmer I didn’t bother about GTD, now I have to.</p>
<p>There are many ways to improve your productivity, but I found the following working for me the best:</p>
<ol>
<li><strong>No multi-tasking.</strong> I try to focus on one thing at a time whenever possible. To make it easier I disable notifications, move my email client and IM clients windows out of my sight. Recently I’ve installed a pesky Focusbar app on my Mac - I love it so far!</li>
<li><strong>Complete something important first thing in the morning.</strong> This sets my mood for the first part of the day: even if I couldn’t complete anything else before lunch — I already did something worthy, so I don’t feel bad while having lunch.</li>
<li><strong>Answer emails twice a day.</strong> Email is a single most distracting thing I fight every day: I get notified whenever somebody updates anything in the projects I’m involved in, I receive automated email notifications about problems with various deployed applications, etc. For that reason I keep my email client closed at all times, except for at 11 AM and at 3 PM - sorry guys :) My IM clients are always open, so in case of emergency somebody would tell me: “Alex, did you notice that our production is down since morning?” What would happen if you don’t reply your boss’s email within a minute? Would you get fired or punished? Think about it. At the end of the day you’re being productive, which is what matters.</li>
<li><strong>Work from a coffee shop.</strong> Removing myself from the office environment to where I’m “alone” helps me a lot. And I use “alone” in a sense that no one I know is there with me, so the only thing I can do is work. Ambient noise doesn’t matter as long as it doesn’t register in my consciousness. Working from home doesn’t help me much, because home has a whole set of other distractions: my wife, fridge, TV, powerful gaming computer. Even if I lock myself in a dedicated office at home - this doesn’t help: the chair is too comfortable, the view out of the window is divine…</li>
<li><strong>Don’t use 3 displays.</strong> I can fit ALL of my windows on 3 displays: IM clients, email client, browser, documentation, programming IDE, terminal, etc. With this setup my focus becomes blurry, I can’t help myself but notice the incoming emails, etc. See rule #1: “No multi-tasking”! Why do I need 3 displays if I don’t multi-task?</li>
<li><strong>Work on a daunting task every day.</strong> The longer the periods are between working on parts of a daunting task the harder it is get back on track and be productive. The rule of thumb is to work on it EVERY DAY, no matter how small the accomplishment is each given day. Renaming a few variables or methods is worth it, because it keeps my memory about the problem fresh. If an urgent issue comes around it is tempting and justifiable to switch to it and work on this issue full time for a few days, completely forgetting about that complex problem I was trying to solve earlier. Urgent issues are never a good excuse - I can always dedicate 15 minutes to that daunting task, no matter the workload.</li>
<li><strong>Decide today about the shortlist of tasks for tomorrow.</strong> It is awesome to start a work day with the exact prioritized list of things to do. I don’t have to force myself to think too hard about priorities in the morning, I know that I can take the first item from the list and start working on it with my hands while my brain wakes up. It probably sounds silly, but I’m dead serious: I start my mornings with less creative tasks to get into a working mood.</li>
<li><strong>Use RescueTime application.</strong> I learned a lot about my productivity patterns. RescueTime is an asshole-program: it watches me 24/7, records the stats about what I’m doing every second and it tells me what I shouldn’t be doing. I love it!</li>
</ol>
<p>These are just a few things I use to fight my weakness and to be more productive. I have to be productive in order to be satisfied as a part of the society.</p>
<p>I hope this blog post explains some of my weirdness and I hope it would help a few people identify their own weak spots.</p>
<p>What tricks do YOU use to organize yourself?</p>Any job requires a worker to perform various tasks daily. In some cases there is just one task that never changes. For example, a manufacturing line where workers are forced to do the same operation over and over again.Improve exception handling in Ruby2014-04-04T04:00:00+00:002014-04-04T04:00:00+00:00https://shovik.com/2014/04/04/improve-exceptions-in-ruby<p>Exception handling is important and requires design - just like any other part of your program. Bad exception handling design often results in extended troubleshooting time in production, especially for larger applications with a lot of data. This is especially useful for long running background code that runs through a lot of data.</p>
<p><img src="https://s3.amazonaws.com/shovik-com/uploads/post_images/3/exceptions.jpg?v=63658145185" alt="Exceptions meme" /></p>
<p>I use this one little trick often: whenever I think a method is risky, I always add the exception catch/re-raise clause at the end, with details about method execution context: parameters, other things - depending on the context.</p>
<p>For example, the following method is called 100K times within one long-running program:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">do_cool_calc</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
<span class="n">calc_result</span> <span class="o">=</span> <span class="n">calc</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
<span class="n">export</span><span class="p">(</span><span class="n">calc_result</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Obviously since I’m a noob this method is going to fail <strong>1 hour into process</strong> with something like that:</p>
<p><code class="language-plaintext highlighter-rouge">awesomeness.rb:39:in 'export_bills': undefined method 'name' for nil:NilClass (NoMethodError)</code></p>
<p>Wouldn’t it be nice to know which record this code failed on? How about the type of the record? Yeah! Here’s the better version of this method:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">do_cool_calc</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
<span class="n">calc_result</span> <span class="o">=</span> <span class="n">calc_stuff</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
<span class="n">export</span><span class="p">(</span><span class="n">calc_result</span><span class="p">)</span>
<span class="k">rescue</span> <span class="no">Exception</span> <span class="o">=></span> <span class="n">ex</span>
<span class="k">raise</span> <span class="no">AwesomeError</span><span class="p">,</span> <span class="s2">"</span><span class="si">#{</span><span class="n">ex</span><span class="p">.</span><span class="nf">message</span><span class="si">}</span><span class="s2">, on </span><span class="si">#{</span><span class="n">record</span><span class="p">.</span><span class="nf">type</span><span class="si">}</span><span class="s2"> ID </span><span class="si">#{</span><span class="n">record</span><span class="p">.</span><span class="nf">id</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="n">ex</span><span class="p">.</span><span class="nf">backtrace</span>
<span class="k">end</span>
</code></pre></div></div>
<p>This gives you a lot better troubleshooting information:</p>
<p><code class="language-plaintext highlighter-rouge">awesomeness.rb:39:in 'calc_stuff': undefined method 'name' for nil:NilClass (NoMethodError), on RosterLineItem ID 523967</code></p>
<p>Note that in the exception handler above I’m re-raising the exception to still let the program fail. We don’t want the program to just keep going and then fail unexpectedly further along. Also I’m changing the exception backtrace to the original exception’s backtrace - so that I know the exact point of failure. Otherwise - I’ll only see the line number of where I re-raised the exception, not very useful information.</p>
<p>Happy software construction!</p>Exception handling is important and requires design - just like any other part of your program. Bad exception handling design often results in extended troubleshooting time in production, especially for larger applications with a lot of data. This is especially useful for long running background code that runs through a lot of data.