<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: alex roman</title>
    <description>The latest articles on DEV Community by alex roman (@alexandrupero).</description>
    <link>https://dev.to/alexandrupero</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F392793%2Fe82c7b2c-0ce2-457c-80ba-7a36196a522f.jpg</url>
      <title>DEV Community: alex roman</title>
      <link>https://dev.to/alexandrupero</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexandrupero"/>
    <language>en</language>
    <item>
      <title>Continuous Delivery With Github Actions, Docker and Traefik on a Virtual Private Server (Part 1)</title>
      <dc:creator>alex roman</dc:creator>
      <pubDate>Wed, 17 Jun 2020 16:45:23 +0000</pubDate>
      <link>https://dev.to/alexandrupero/continuous-delivery-with-github-actions-docker-and-traefik-on-a-virtual-private-server-part-1-3285</link>
      <guid>https://dev.to/alexandrupero/continuous-delivery-with-github-actions-docker-and-traefik-on-a-virtual-private-server-part-1-3285</guid>
      <description>&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Why?&lt;/li&gt;
&lt;li&gt;How?&lt;/li&gt;
&lt;li&gt;Let's push that docker image to the registry&lt;/li&gt;
&lt;li&gt;That was easy, is that it?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Introduction &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In this article series, I'll show how you can use Github Actions to setup a simple workflow that after every push to your Github repository will build and publish your code as a docker image and then deploy it to your VPS running Traefik with zero downtime.&lt;/p&gt;

&lt;p&gt;If the above made your head spin, might be worth brushing up on a few topics first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://help.github.com/en/actions"&gt;Github Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/get-started/"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.traefik.io"&gt;Traefik&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why? &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Because running a fancy Kubernetes cluster might be all the rage, but it's overkill for most things. Not to mention really expensive. If all you've got is a bunch of small (static?) websites or low traffic hobby projects, you'll be better off just running them all in a cheap Virtual Private Server (even a free one - e.g. &lt;a href="https://cloud.google.com/free"&gt;GCP Free Tier&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;That's where Traefik comes in, letting you run multiple domains/subdomains on the same host, each of them running nice and isolated in a Docker container. &lt;/p&gt;

&lt;p&gt;And Github Actions takes care of deploying everything automatically, so you don't have to do anything other than push your code to the master branch (and maybe throw in a &lt;a href="https://semver.org"&gt;SemVer&lt;/a&gt; tag to keep things organised). You can even do zero downtime deployments! Neat, huh?&lt;/p&gt;

&lt;h3&gt;
  
  
  How? &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I guess the flowchart was inevitable then. It goes something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T_saOq4z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xy5hu0a6rswmbgiwlhl8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T_saOq4z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xy5hu0a6rswmbgiwlhl8.jpg" alt="Flowchart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's push that docker image to the registry &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Right, so now that the how and the why are clear as a whistle, let's push that docker image to the registry! We'll do that by creating a GitHub Actions Workflow in our GitHub repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="c1"&gt;# Controls when the action will run. Triggers the workflow on push or pull request&lt;/span&gt;
&lt;span class="c1"&gt;# events but only for the master branch&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;master&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Publish `v1.2.3` tags as releases.&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;v*&lt;/span&gt;

    &lt;span class="c1"&gt;# TODO: file paths to consider in the event. Optional; defaults to all.&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path/to/code/that/triggers/this/workflow/*'&lt;/span&gt;

  &lt;span class="c1"&gt;# Run tests for PRs into master branch&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;master&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# TODO&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path/to/code/that/triggers/this/workflow/*'&lt;/span&gt;

&lt;span class="c1"&gt;# Define enviornment variables for the workflow&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# TODO: Change variable to your image's name.&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;image-name&lt;/span&gt;

&lt;span class="c1"&gt;# A workflow run is made up of one or more jobs that can run sequentially or in parallel&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Run tests.&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

     &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;

          &lt;span class="s"&gt;# TODO: Run a Dockerized testing script, or just build the docker image if no such script exists&lt;/span&gt;
          &lt;span class="s"&gt;if [ -f docker-compose.test.yml ]; then&lt;/span&gt;
            &lt;span class="s"&gt;docker-compose --file docker-compose.test.yml build&lt;/span&gt;
            &lt;span class="s"&gt;docker-compose --file docker-compose.test.yml run sut&lt;/span&gt;
          &lt;span class="s"&gt;else&lt;/span&gt;
            &lt;span class="s"&gt;docker build . --file path/to/Dockerfile&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;

  &lt;span class="c1"&gt;# Build and push image to Docker Registry&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Ensure test job passes before pushing image.&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

    &lt;span class="c1"&gt;# The type of runner that the job will run on&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="c1"&gt;# Only run this job for push events &lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event_name == 'push'&lt;/span&gt;

    &lt;span class="c1"&gt;# Steps represent a sequence of tasks that will be executed as part of the job&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

    &lt;span class="c1"&gt;# TODO: Builds the docker image (path relative to api root)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build image&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker build . --file path/to/Dockerfile --tag $IMAGE_NAME&lt;/span&gt;

    &lt;span class="c1"&gt;# Login to the docker registry using the default GITHUB_TOKEN environment variable and github.actor (The login of the&lt;/span&gt;
    &lt;span class="c1"&gt;# user that initiated the workflow run)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Log into registry&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin&lt;/span&gt;

    &lt;span class="c1"&gt;# Push the image to the Docker Registry&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Push image&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/$IMAGE_NAME&lt;/span&gt;

        &lt;span class="s"&gt;# Strip git ref prefix from version&lt;/span&gt;
        &lt;span class="s"&gt;VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')&lt;/span&gt;

        &lt;span class="s"&gt;# Strip "v" prefix from tag name&lt;/span&gt;
        &lt;span class="s"&gt;[[ "${{ github.ref }}" == "refs/tags/"* ]] &amp;amp;&amp;amp; VERSION=$(echo $VERSION | sed -e 's/^v//')&lt;/span&gt;

        &lt;span class="s"&gt;# Use Docker `latest` tag convention&lt;/span&gt;
        &lt;span class="s"&gt;[ "$VERSION" == "master" ] &amp;amp;&amp;amp; VERSION=latest&lt;/span&gt;

        &lt;span class="s"&gt;echo IMAGE_ID=$IMAGE_ID&lt;/span&gt;
        &lt;span class="s"&gt;echo VERSION=$VERSION&lt;/span&gt;

        &lt;span class="s"&gt;docker tag $IMAGE_NAME $IMAGE_ID:$VERSION&lt;/span&gt;
        &lt;span class="s"&gt;docker push $IMAGE_ID:$VERSION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Please note a few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I've used GitHub Packages as the Docker Registry for convenience, you can use any Docker Registry you want&lt;/li&gt;
&lt;li&gt;Pay attention to the TODO comments, you'll need to change those to suit your app - like the paths and image name&lt;/li&gt;
&lt;li&gt;You can use any name and filename for the yml workflow file, but you need to place the file under .github/workflows folder in your repository&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  That was easy, is that it? &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Sure, if all you needed was a way to automatically push a docker image to the registry once you push your code and you're happy enough to manually deploy that to your server. If not, and you want to automate everything like the nice flowchart showed you, tune in for Part 2.&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

</description>
      <category>github</category>
      <category>docker</category>
      <category>traefik</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
