<?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: Chris Hunt</title>
    <description>The latest articles on DEV Community by Chris Hunt (@chrishunt).</description>
    <link>https://dev.to/chrishunt</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%2F60787%2Fd3c39303-ddde-414e-ac9c-242ac3e16738.jpg</url>
      <title>DEV Community: Chris Hunt</title>
      <link>https://dev.to/chrishunt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chrishunt"/>
    <language>en</language>
    <item>
      <title>Rancher Personal Server Setup</title>
      <dc:creator>Chris Hunt</dc:creator>
      <pubDate>Mon, 28 Oct 2019 21:58:54 +0000</pubDate>
      <link>https://dev.to/chrishunt/rancher-simple-setup-1a0k</link>
      <guid>https://dev.to/chrishunt/rancher-simple-setup-1a0k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;By profession, I'm a Software Engineer. Like many others in the same line of work, I've accumulated a few low traffic sites and apps which I've put together for friends and myself. The problem isn't specifically the sites and apps, it's the hosting. I want a simple and cheap hosting solution that don't have to worry about. Something that's easily maintainable and upgradable but also easy to use.&lt;/p&gt;

&lt;p&gt;A few years ago, I discovered Rancher which is a Docker orchestration stack. It used its own orchestration engine called Cattle upon which ran a user friendly UI that allowed me to host my sites and apps.&lt;/p&gt;

&lt;p&gt;Rancher v1 on an EC2 has served me well for nearly three years but over that time my server had a number of little hacks and quirks regarding routing and certificates which were not easily replicable. Rancher v1 is also not being maintained since v2 was released.&lt;/p&gt;

&lt;p&gt;It was time to build a new server. I'm not a sysadmin or a network guy so I really want a simple solution that just works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;My requirements haven't really changed from my previous setup with Rancher v1. Let's look at those requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A cheap, cloud based server&lt;/li&gt;
&lt;li&gt;A Docker orchestration system with project separation&lt;/li&gt;
&lt;li&gt;An administration UI&lt;/li&gt;
&lt;li&gt;Access to ECR&lt;/li&gt;
&lt;li&gt;Ability to host under 10 low traffic websites and a couple of long running Node apps&lt;/li&gt;
&lt;li&gt;A way of distributing HTTP requests to containers&lt;/li&gt;
&lt;li&gt;Certificate generation and management&lt;/li&gt;
&lt;li&gt;Ability to scale if required&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step up Rancher v2
&lt;/h2&gt;

&lt;p&gt;The documentation for Rancher v2 promised to solve the routing and certificate hacks with out of the box functionality. Rancher v2 uses Kubernetes as it's orchestration engine too which is better documented than Cattle was. This seemed a good place to start but the migration docs (&lt;a href="https://rancher.com/docs/rancher/v2.x/en/v1.6-migration/" rel="noopener noreferrer"&gt;https://rancher.com/docs/rancher/v2.x/en/v1.6-migration/&lt;/a&gt;) seemed quite a faff. I decided to start from a clean install.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spoiler alert:&lt;/em&gt; Rancher v2 lived up to the billing and gave me exactly what I required however there were a few setup hoops to jump through to get to that situation - hence writing this article hoping it may help others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server setup
&lt;/h2&gt;

&lt;p&gt;I started by setting up a &lt;code&gt;t2.medium&lt;/code&gt; EC2 instance using Amazon Linux 2 AMI with 20GB EBS storage and an Elastic IP along with my key pair.&lt;/p&gt;

&lt;p&gt;Ensure that the Security Group of the instance has inbound access to ports 80, 8080, 443 and 8443. This will allow requests to both the Rancher UI (via 8080 and 8443) and to our hosted sites (through 80 and 443).&lt;/p&gt;

&lt;p&gt;Use a suitable key pair to secure your access. This is beyond the scope of this article but full details can be found at &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next thing is to ensure our server has a fixed IP. By default, the IP will get allocated when the server boots up. This means that when we restart our server, it may fire up with a different IP. From the AWS console, in EC2 Dashboard, select Elastic IP from the left hand menu. We can allocate a new IP and then allocate it to our instance. It's important that we release IP addresses which are not in use as we will be charged for IPs that you have allocated but not associated with an instance. You do not pay for IPs which are associated with an instance.&lt;/p&gt;

&lt;p&gt;Once fired up, we can access your instance using your key through terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh -i ~/.ssh/key.pem 52.16.31.9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you're greeted with the following prompt.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbj4gcw69mkdan1volcro.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbj4gcw69mkdan1volcro.png" alt="Our server"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start by doing an update of the software on the server. &lt;code&gt;sudo yum update&lt;/code&gt;. This may take a minute or so to get everything up to date.&lt;/p&gt;

&lt;p&gt;The only software required on the server to run Rancher is Docker. This is a simple case of following the tutorial at &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/docker-basics.html#install_docker" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/AmazonECS/latest/developerguide/docker-basics.html#install_docker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One additional step is to ensure that Docker starts when the server boots using this command...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl enable docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, so good!&lt;/p&gt;

&lt;h2&gt;
  
  
  Install and access Rancher
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installing Rancher server and SSL certificate
&lt;/h3&gt;

&lt;p&gt;Next we install Rancher and access the UI. Hoorah for online docs. Rancher's single node installation guide covered everything I needed to know - &lt;a href="https://rancher.com/docs/rancher/v2.x/en/installation/single-node/" rel="noopener noreferrer"&gt;https://rancher.com/docs/rancher/v2.x/en/installation/single-node/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted the Rancher data to be persisted in case my container had issues &lt;br&gt;
I could rescue that data.&lt;/p&gt;

&lt;p&gt;I also wanted to use Let's Encrypt to provide a certificate for my Rancher UI access. &lt;/p&gt;

&lt;p&gt;In order to get the certificate, we need to start the server container bound to ports 80 and 443&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d --restart=unless-stopped \
  -p 80:80 -p 443:443 \
  -v /opt/rancher:/var/lib/rancher \
  rancher/rancher:latest --acme-domain mydomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The container will now fire up and grab an SSL certificate from Let's Encrypt. You should now be able to connect to your server at &lt;a href="https://mydomain.com" rel="noopener noreferrer"&gt;https://mydomain.com&lt;/a&gt; (or whatever you've set up to point at your IP). You'll be asked to set a password for your &lt;code&gt;admin&lt;/code&gt; account and also for the domain your Rancher UI is going to run on. We'll amend the URL to add a port &lt;code&gt;:8443&lt;/code&gt; to our URL.&lt;/p&gt;

&lt;p&gt;As I was also going to be running the agent on the same node (server) as the Rancher server, we need to bind ports 80 and 443 to different ports (8080 and 8443 respectively).&lt;/p&gt;

&lt;p&gt;This means we have to do a little juggling with our containers. We can do this as we've already got our SSL certificate. We need to remove our current container and then fire it up on the new ports.&lt;/p&gt;

&lt;p&gt;To do this, list the Docker containers with &lt;code&gt;docker ps&lt;/code&gt; and then remove the running container with &lt;code&gt;docker rm -fv 637&lt;/code&gt; where 637 is the first few characters of the container ID. See example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ec2-user@ip-172-31-12-141 /]$ docker ps
CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                                      NAMES
6372f4c2ae95        rancher/rancher:latest   "entrypoint.sh --acm…"   18 minutes ago      Up 18 minutes       0.0.0.0:80-&amp;gt;80/tcp, 0.0.0.0:443-&amp;gt;443/tcp   clever_pike
[ec2-user@ip-172-31-12-141 /]$ docker rm -fv 637
637
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fire it up again with the new port mappings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d --restart=unless-stopped \
  -p 8080:80 -p 8443:443 \
  -v /opt/rancher:/var/lib/rancher \
  rancher/rancher:latest --acme-domain mydomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we've stored our data on a host volume, all of our settings and our certificate have been persisted.&lt;/p&gt;

&lt;p&gt;Within a few seconds, I could hit &lt;code&gt;mydomain.com:8443&lt;/code&gt; in my browser and Rancher was up and running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a Cluster
&lt;/h3&gt;

&lt;p&gt;We have Rancher Server set up. We now need a cluster (which, as we've previously discussed, in this case will be a single node) to run our applications on.&lt;/p&gt;

&lt;p&gt;From the main menu, click on Clusters &amp;gt; Add Cluster.&lt;/p&gt;

&lt;p&gt;Rancher offers a lot of options to add a cluster from different providers. It will provision the resources for you. We're going to add a cluster from an existing node (server).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fd0iiojbr3u2dzz6u9yfo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fd0iiojbr3u2dzz6u9yfo.png" alt="Add Cluster options"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding a cluster has a lot of options but we'll concentrate on the basics to get up and running. The most basic is to just give the cluster a name. Click &lt;code&gt;Next&lt;/code&gt; and we're presented by Docker command.&lt;/p&gt;

&lt;p&gt;We want our agent running with all the roles &lt;code&gt;etcd&lt;/code&gt;, &lt;code&gt;Control Panel&lt;/code&gt; and &lt;code&gt;Worker&lt;/code&gt;. Check all these boxes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo docker run -d --privileged --restart=unless-stopped \
   --net=host -v /etc/kubernetes:/etc/kubernetes \
   -v /var/run:/var/run rancher/rancher-agent:v2.3.0 \
   --server https://mydomain.com:8443 \
   --token cp6jcp9lcvw8b279brstp92bvfkg8xgv8b6dkkp9xz7n6ktxqsctzq \
   --etcd --controlplane --worker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy and paste that command on to our server terminal to fire up our worker. Rancher also starts Kubernetes services behind the scenes. If you want to see what Rancher has set up for us, run &lt;code&gt;docker ps&lt;/code&gt;. This lists the running containers. At the bottom, we can see the Rancher Server with our external mapped ports and then the remaining containers are managing our agent.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F87x8sy3cewb5026cf4wp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F87x8sy3cewb5026cf4wp.png" alt="Rancher containers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back in the UI, we're informed of the status of the agent coming up. This takes a few minutes as each agent image needs to be downloaded and started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3ryv3kpgvwhgbfk9r83n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3ryv3kpgvwhgbfk9r83n.png" alt="Up and running cluster"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fukp8lpyala9cewj2vwuu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fukp8lpyala9cewj2vwuu.png" alt="Our cluster info"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take some time to have a look around the UI. Some of the features are quite self explanatory and a bit of exploring should find those.&lt;/p&gt;

&lt;p&gt;A few things I'd recommend looking at at this point (but beyond the scope of this article):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Core Rancher settings&lt;/li&gt;
&lt;li&gt;Cluster settings&lt;/li&gt;
&lt;li&gt;Change your default security provider (I went for Github) and add a user&lt;/li&gt;
&lt;li&gt;Add namespaces. Our apps will later be placed under a namespace. Namespaces help us separate our sites and apps.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Accessing ECR
&lt;/h2&gt;

&lt;p&gt;I use ECR as a registry for my Docker images so we need to allow access to pull images. This can be set up using an instance profile on EC2 with access to the ECR registry.&lt;/p&gt;

&lt;p&gt;From the EC2 console, select the instance and then Actions &amp;gt; Instance Settings &amp;gt; Attach/Replace IAM Role. From here, we can create an IAM role through the screen prompts and attach the following permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ECRGetImage",
            "Effect": "Allow",
            "Action": [
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:BatchCheckLayerAvailability"
            ]
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives our server full read permissions to all of our images on ECR. This ensures that our server has access and Kubernetes manages login to the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up SSL for sites
&lt;/h2&gt;

&lt;p&gt;The last stage before we start adding our applications is setting up SSL.&lt;/p&gt;

&lt;p&gt;This is achieved by adding the Let's Encrypt Certificate Manager application. In the context of Rancher, an application is a preconfigured image which we can launch directly from the Rancher UI.&lt;/p&gt;

&lt;p&gt;Open up our cluster and click on Apps from the menu.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgcbvtmfav613tdgozkb2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgcbvtmfav613tdgozkb2.png" alt="Add App menu"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click Launch and select the Let's Encrypt Certificate Manager. We are now presented with several options to get this started. We need to change the issuer from the stage issuer to the prod issuer using the option &lt;code&gt;Let's Encrypt Cluster Issuer clients&lt;/code&gt;. We also need to enter our email address.&lt;/p&gt;
&lt;h2&gt;
  
  
  Provisioning our first workload
&lt;/h2&gt;

&lt;p&gt;A workload is a containerised application. Both our sites and our apps are workloads.&lt;/p&gt;

&lt;p&gt;Now that we've done all of the server setup, deploying a workload is little more than an exercise of completing a UI form. From our cluster, click the Deploy button. We're presented by an intuitive form where most of the options will be familiar to Docker users.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4ku3ogwmrz3u3d5en2h8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4ku3ogwmrz3u3d5en2h8.png" alt="Deploy workload options"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give your workload a name, enter the full ECR image name, complete any other options as required and click the &lt;code&gt;Launch&lt;/code&gt; button. Our workload should fire up.&lt;/p&gt;

&lt;p&gt;If your app doesn't require connecting to the outside world, you're done. Our web app needs a couple more steps however.&lt;/p&gt;

&lt;p&gt;We want to set up a load balancer to direct the traffic coming in to our server to the correct workload determined by the host name. From the cluster menu, select the Load Balancers tab followed by the Add Ingress button. Add a name for the load balancer and then ensure that it's on the same namespace as the website workload that you set up.&lt;/p&gt;

&lt;p&gt;We then need to set up the rule which will direct traffic to our workload. The form looks as below. Enter the host name. To direct all traffic (rather than only a sub path, enter &lt;code&gt;/&lt;/code&gt; in the path input box. Select the web app workload that we've just set up and the port that that workload accepts requests on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxfa32f9mb9bs39cq7ee4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxfa32f9mb9bs39cq7ee4.png" alt="Load balancer rules"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now have requests on port 80 directed to our workload. The final step is to ensure that we can also accept secure requests on port 443. The instructions to complete this are on the introduction of the Certificate Manager app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4790u0mleizbh0ydnfan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4790u0mleizbh0ydnfan.png" alt="Cert manager instructions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back on our Load Balancers tab, we can use the menu for our load balancer and select View/Edit YAML. The first thing we need to add is in the &lt;code&gt;metadata.annotations&lt;/code&gt; section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubernetes.io/tls-acme: "true"
certmanager.k8s.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/secure-backends: "true"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a one time addition. We then need to add the following for each of the sites which we're setting up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spec:
  tls:
  - hosts:
    - www.mydomain.com
    - mydomain.com
    secretName: mydomain-crt

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we can add multiple domains to a certificate. The certificate will be saved in Kubernetes secrets and the secret name is defined here.&lt;/p&gt;

&lt;p&gt;Upon saving the YAML, &lt;code&gt;cert-manager&lt;/code&gt; should kick in and find this config and obtain a certificate from Let's Encrypt. Assuming we've pointed our host name at our server, the site should now be available on port 443 too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;I got to the situation above through a little trial and error. I have noted a few situations which popped up. Here's a few places to find information to help you debug a situation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pod logs
&lt;/h3&gt;

&lt;p&gt;Assuming you've only set up one pod for your workload, you can access the log of that pod by selecting the workload. When presented with a list of pods, you can use the dropdown menu to view the log of the specific pod.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsk0aggmfm2a5p4kc4hl8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsk0aggmfm2a5p4kc4hl8.png" alt="View logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will hopefully help us determine whether requests are reaching the pods.&lt;/p&gt;

&lt;p&gt;If you have more than one pod per workload, it may be worth reducing deployed pods to one so that you know where requests should be headed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cert Manager logs
&lt;/h3&gt;

&lt;p&gt;If you can't access your site on port 443 then it's worth checking that your certificate was created correctly. This can be done through the certificate manager logs.&lt;/p&gt;

&lt;p&gt;Click on your certificate manager workload within your cluster and then from the pod, select View Logs. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fuhc8hj86tvl12sgkcgl6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fuhc8hj86tvl12sgkcgl6.png" alt="Cert manager logs"&gt;&lt;/a&gt;&lt;br&gt;
In the logs, try searching for your host name. Hopefully there will be a message which will help you. This may be something like port 80 wasn't accessible or that your configuration wasn't correct. The couple of errors that I've had in here were well worded and finding the solution wasn't a problem.&lt;/p&gt;
&lt;h3&gt;
  
  
  ECR Permissions
&lt;/h3&gt;

&lt;p&gt;On one occasion, I had an issue where a Docker image could not be pulled from ECR. For some reason, the EC2 Profile wasn't present. Restarting the server fixed the issue.&lt;/p&gt;

&lt;p&gt;If you wanted to check the user and role within the EC2 instance, you can run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What next?
&lt;/h2&gt;

&lt;p&gt;Walking through the steps above achieves the goals I set out at the start. We have a server with user friendly administration. We can simply set up other services through the UI.&lt;/p&gt;

&lt;p&gt;Use Rancher namespaces and projects to separate and organise your workloads. While it's pretty easy to manage a couple of workloads, once you add database apps, logging apps, admin UIs connecting to your workloads, you'll certainly appreciate a way of organising them.&lt;/p&gt;

&lt;p&gt;I'd recommend getting familiar with the Secrets functionality in Rancher and how to link them in to your application. This will assist making your server more secure.&lt;/p&gt;

&lt;p&gt;Try scaling the cluster with another node by firing up another server, installing Docker and then go through the same process of adding a node as we looked at earlier (but with only a &lt;code&gt;worker&lt;/code&gt; role).&lt;/p&gt;

&lt;h2&gt;
  
  
  Final points
&lt;/h2&gt;

&lt;p&gt;I pulled all the points above from a number of tutorials, articles and Stack Overflow questions. Put that together with a decent amount of trial and error, I got my personal server set up in a user friendly and maintainable state. I'm not a "server guy" and don't have in depth knowledge of a lot of the concepts I've dabbled in so I welcome feedback and thoughts on improvement of the process and article.&lt;/p&gt;

</description>
      <category>rancher</category>
      <category>server</category>
      <category>hosting</category>
    </item>
    <item>
      <title>Code-free machine learning with Ludwig</title>
      <dc:creator>Chris Hunt</dc:creator>
      <pubDate>Sat, 09 Mar 2019 19:35:10 +0000</pubDate>
      <link>https://dev.to/chrishunt/code-free-machine-learning-with-ludwig-2gap</link>
      <guid>https://dev.to/chrishunt/code-free-machine-learning-with-ludwig-2gap</guid>
      <description>&lt;h2&gt;
  
  
  Intro and Ludwig
&lt;/h2&gt;

&lt;p&gt;At the start of February 2019, Uber made their code-free machine learning toolbox, Ludwig, open-source.&lt;/p&gt;

&lt;p&gt;Website - &lt;a href="https://uber.github.io/ludwig/" rel="noopener noreferrer"&gt;https://uber.github.io/ludwig/&lt;/a&gt;&lt;br&gt;
User guide - &lt;a href="https://uber.github.io/ludwig/user_guide/" rel="noopener noreferrer"&gt;https://uber.github.io/ludwig/user_guide/&lt;/a&gt;&lt;br&gt;
Github repo - &lt;a href="https://github.com/uber/ludwig/" rel="noopener noreferrer"&gt;https://github.com/uber/ludwig/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ludwig runs on top of the popular and powerful TensorFlow library and offers CLI access to experiment and train machine learning models and predict using TensorFlow models&lt;/p&gt;

&lt;p&gt;As an engineer, I'm absolutely not a data scientist. I know enough around TensorFlow to build the most basic of models using tutorials but really couldn't create anything from scratch. Ludwig offered that opportunity.&lt;/p&gt;
&lt;h2&gt;
  
  
  Our first experiment
&lt;/h2&gt;

&lt;p&gt;Let's dive in and run through a basic example. We're going to try to recreate the Keras tutorial at &lt;a href="https://www.tensorflow.org/tutorials/keras/basic_regression" rel="noopener noreferrer"&gt;https://www.tensorflow.org/tutorials/keras/basic_regression&lt;/a&gt; with zero lines of code.&lt;/p&gt;

&lt;p&gt;The dataset shows basic data to cars in the &lt;a href="https://archive.ics.uci.edu/ml/datasets/auto+mpg" rel="noopener noreferrer"&gt;Auto MPG dataset&lt;/a&gt;. Our task is to predict the MPG from the features provided. I've grabbed this and converted it to a CSV file for use in this example.&lt;/p&gt;

&lt;p&gt;Ludwig uses a model definition file to determine the parameters for building the model. The internals of Ludwig deals with your data. It creates train, test and validation datasets. It also modifies the data into the best format for training depending on the data type you've specified.&lt;/p&gt;

&lt;p&gt;The Keras example needs us to manipulate the data in order to train and test the model. Ludwig does all of this for us. It allows us to train the model immediately by setting up a model definition file at &lt;code&gt;modeldef.yaml&lt;/code&gt;. Here we define the input features and their data type. There are a number of other parameters against each feature which can be set for more complex models. We also define the output feature and its parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;input_features&lt;/span&gt;&lt;span class="pi"&gt;:&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;Cylinders&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;numerical&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;Displacement&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;numerical&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;Horsepower&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;numerical&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;Weight&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;numerical&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;Acceleration&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;numerical&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;ModelYear&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;numerical&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;Origin&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;category&lt;/span&gt;
&lt;span class="na"&gt;output_features&lt;/span&gt;&lt;span class="pi"&gt;:&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;MPG&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;numerical&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  First run
&lt;/h3&gt;

&lt;p&gt;Our first experiment can now be run with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ludwig experiment  --data_csv cars.csv --model_definition_file modeldef.yaml --output_directory results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the following results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;===== MPG =====
loss: 52.9658573971519
mean_absolute_error: 6.3724554520619066
mean_squared_error: 52.9658573971519
r2: 9.58827477467211e-05
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After 200 epochs complete, I have a mean absolute error (MAE) of 6.4 (yours may vary slightly depending on the random train/test split). This means that on average MPG prediction on a car is 6.4MPG from the actual value. Bearing in mind that values are generally between 10MPG and 47MPG, that 6.4MPG represents quite a large error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refinement
&lt;/h3&gt;

&lt;p&gt;If you were watching the log scrolling as Ludwig was running, you'd have seen the MAE against the validation set reducing with each epoch.&lt;/p&gt;

&lt;p&gt;The Keras example was suggesting a final MAE of ~2 so we may need a bit of tweaking to get closer. There was fair indication that the MAE was still decreasing as the run ended. We can increase the amount of epochs with a simple addition to the addition to the model definition&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;training&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;epochs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and continue from the previous training model with the command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ludwig experiment  --data_csv cars.csv --model_definition_file modeldef.yaml --output_directory results -mrp ./results/experiment_run_0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our MAE only comes down to 5.3MPG. Still not that close.&lt;/p&gt;

&lt;h3&gt;
  
  
  Further refinement
&lt;/h3&gt;

&lt;p&gt;In a real life example, we'd start amending the hyperparameters, retraining, amending and retraining again while our target MAE still reduces. &lt;/p&gt;

&lt;p&gt;We'll skip this step by replicating the hyperparameters from the Keras tutorial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;training&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;batch_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;32&lt;/span&gt;
  &lt;span class="na"&gt;epochs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;
  &lt;span class="na"&gt;early_stop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;
  &lt;span class="na"&gt;learning_rate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.001&lt;/span&gt;
  &lt;span class="na"&gt;optimizer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rmsprop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition, we set early stop at 50 epochs - this means that our model will stop training if our validation curve doesn't improve for 50 epochs. The experiment is fired off in the same way as before. It produces these results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Last improvement of loss on combined happened 50 epochs ago

EARLY STOPPING due to lack of validation improvement, it has been 50 epochs since last validation accuracy improvement

Best validation model epoch: 67

loss: 10.848812248133406
mean_absolute_error: 2.3642308198952975
mean_squared_error: 10.848812248133406
r2: 0.026479910446118703
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get a message that our model has stopped training at 132 epochs because it's hit the early stop limit. &lt;/p&gt;

&lt;p&gt;MAE is down to 2.36MPG without writing a line of code and we've got our example to similar results to the Keras tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visualising our training
&lt;/h3&gt;

&lt;p&gt;Now we'd like to validate that our test and validation loss curves are getting pretty close but not showing overfitting. Ludwig continues to deliver on its promise of a no-code solution. We can view our learning curves with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ludwig visualize -v learning_curves -ts results/experiment_run_0/training_statistics.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmfpzv0dwf0xt77pxkjns.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmfpzv0dwf0xt77pxkjns.png" alt="Learning curves"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The curves remain following a similar trajectory. Should the validation curve start heading upwards while the training curve remain on this trajectory, it would suggest that overfitting is occurring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real life validation
&lt;/h2&gt;

&lt;p&gt;Ok, this is all well and good but tutorials notoriously pick and choose data so the output "just works". Let's try our model out with some real data.&lt;/p&gt;

&lt;p&gt;With a bit of investigation, I've dug out the required stats of the DeLorean DMC-12 (&lt;a href="https://en.wikipedia.org/wiki/DMC_DeLorean):" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/DMC_DeLorean):&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cylinders:     6
Displacement:  2849cc (174 cubic inches)
Horsepower:    130hp
Weight:        1230 kg (2712 lb)
Acceleration:  10.5s
Year:          1981
Origin:        US
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and converted it to the same CSV format as the training data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cylinders,Displacement,Horsepower,Weight,Acceleration,ModelYear,Origin
6,174,130,2712,10.5,81,1

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, to predict the fuel economy of this, we run the &lt;code&gt;predict&lt;/code&gt; command through Ludwig:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ludwig predict --data_csv delorean.csv -m results/experiment_run_0/model -op
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We specify the &lt;code&gt;-op&lt;/code&gt; flag to tell Ludwig that we only want predictions. Inputting a CSV file with MPG column and not adding this flag will run the predictions but also provide us with statistics against actual values supplied in the file.&lt;/p&gt;

&lt;p&gt;The result given by my model is 23.53405mpg. How good is this? Unfortunately our Wikipedia article doesn't show the published fuel economy but I did manage to find it in this &lt;a href="https://www.telegraph.co.uk/cars/classic/back-to-the-future-day-is-the-delorean-as-bad-as-we-think/" rel="noopener noreferrer"&gt;fantastic article&lt;/a&gt; about the amazing car - 22.8mpg. A pretty decent real life test!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I appreciate that the data scientists out there are screaming that we didn't run through any analysis on the input features to create a meaningful feature set and that we didn't run specific analysis on the test data predictions. I also appreciate that MAE isn't necessarily the ultimate measure of accuracy as it may be skewed heavily by outliers which we could have validated through further analysis.&lt;/p&gt;

&lt;p&gt;What we have shown is that using Ludwig, we can experiment and train a machine learning model and then predict using the model we've trained.&lt;/p&gt;

&lt;p&gt;Machine learning is becoming more and more accessible. Ludwig seems to be big step forward in that regard.&lt;/p&gt;

</description>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Why you need a basic ML understanding</title>
      <dc:creator>Chris Hunt</dc:creator>
      <pubDate>Fri, 11 Jan 2019 17:23:52 +0000</pubDate>
      <link>https://dev.to/chrishunt/why-you-need-a-basic-ml-understanding-1ngi</link>
      <guid>https://dev.to/chrishunt/why-you-need-a-basic-ml-understanding-1ngi</guid>
      <description>&lt;p&gt;There are points in your career where you discover a piece of software or a library or a technique that you wish you'd known about years ago. You can see how it could have saved you hours and made your life so much easier in a previous job or project. &lt;/p&gt;

&lt;p&gt;For me, Docker and MongoDB both fall into this category. Although I was relatively early to the party on both of those, the impact they had on my day to day work and ability to deliver rapidly changed the way I work.&lt;/p&gt;

&lt;p&gt;Machine learning is the latest. Until relatively recently, I may have been a little naive in believing that ML was something that was exclusive to monster corporations with huge budgets and specialised ML staff who have access to mega servers. This is the situation that most tech news stories related to ML portray and, while at the top end of the spectrum this may be true, it &lt;em&gt;is&lt;/em&gt; a spectrum and the basics are not that hard to grasp and access.&lt;/p&gt;

&lt;p&gt;To make it clear, machine learning (and all it's sub categories) is a massive subject and I'm not suggesting that in a few hours you can train your car to go driverless. I am suggesting that, as a developer, you should be aware of the kind of jobs it can and can't do, how it can benefit you (and your company) and, should you wish to implement a basic ML solution, the route you'd need to take to get there. Or, to put it another way, you should have the ability to hold a conversation around ML when your manager/director/project manager inevitably drops into the conversation "I've heard so much about machine learning - I think we should be using it".&lt;/p&gt;

&lt;h2&gt;
  
  
  "We don't need Machine Learning"
&lt;/h2&gt;

&lt;p&gt;Maybe you work in a company in which you believe ML couldn't offer any value. You don't make driverless cars and you don't run petabyte scale search engines. There's a good chance, however that if your company works with any significant amount of data, ML may be able to benefit you.&lt;/p&gt;

&lt;p&gt;Let me throw an example to you and explain how ML could open up some other ideas. &lt;/p&gt;

&lt;p&gt;Let's say the sales department of your e-commerce company asks for an alert when a new customer makes their first purchase with a flag suggesting whether that customer is likely to become a long term customer so that the sales team can follow up personally. All we have to go on is the existing data of a few ten thousand existing customers.&lt;/p&gt;

&lt;p&gt;A year ago, as an experienced engineer with no ML knowledge, I'd have embarked upon looking for patterns in the existing customers, first purchases, potentially their location and their further history. I'd have looked to write a function with multiple &lt;code&gt;if&lt;/code&gt; statements which determined their status. Job done.&lt;/p&gt;

&lt;p&gt;The approach for ML also starts with looking at the data. We analyse the data and clean it into a way the at our model will be able to read ("features" is the term used in ML) and the outcome ("label") of whether they become a long term customer which we will know for existing customers. We could then train a machine learning model to associate the features to the labels. With a few tens of thousands of customers, this training wouldn't be too expensive on a decent PC (probably to the contrary of the impression we may have given ML articles in the press),  We can then test this model to find its accuracy. If sufficient, we are in a position that our model can predict whether new customers are likely to become long-term customers or not.&lt;/p&gt;

&lt;p&gt;At this point we've approached the same problem in two different ways and potentially produced two workable solutions. However six months later, the sales department inform you that the flag isn't as accurate as it used to be. Something has changed in the market and so have the habits of the customers and it needs fixing. With our engineering method, we'd potentially have to restart from the beginning and rewrite our function. However, with our ML solution, we may only need to retrain our model with the new, more recent data in order to improve its performance. In time we could train our model with new data as it arrives and your model will always be up to date.&lt;/p&gt;

&lt;p&gt;So that's easy for me to say and just give an example but I know what you're thinking....&lt;/p&gt;

&lt;h2&gt;
  
  
  "I don't have the skills"
&lt;/h2&gt;

&lt;p&gt;And neither do I. I'm not a data scientist and my experience of ML doesn't go beyond working with data scientists on a daily basis and a &lt;a href="https://udemy.com/python-for-data-science-and-machine-learning-bootcamp/learn/v4/overview"&gt;basic ML course in Python on Udemy&lt;/a&gt; which I'd highly recommended if you want a basic start. What I do have however, is knowledge of the kind of problems ML can solve. This is the tool I suggest all developers can benefit from. Having this will allow you to recognise when a ML solution may be useful and potentially more efficient for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If I've convinced you to have a look into ML, I'd suggest &lt;a href="https://www.youtube.com/watch?v=FtKUj0icUz4&amp;amp;t=179s"&gt;this video&lt;/a&gt; as a good follow on which will give you some very basic code examples. Laurence does a great job of introducing ML without going into heavy mathematical formulae. He also explains the crux of ML versus "traditional programming" really well.&lt;/p&gt;

&lt;p&gt;Ultimately, traditional programming takes in rules and data and produces answers. Machine learning takes in answers and data and produces the rules (the ML model).&lt;/p&gt;

&lt;p&gt;This approach is particularly useful as requirements become more complex or obscure (as examined in the brief e-commerce example).&lt;/p&gt;

&lt;p&gt;Putting all of this together, I truly believe that the developer who can have a solid conversation around machine learning because they have an understanding of what it can do, will have a significant advantage over those who don't both within most workplaces and then further in to the job market. It's a fast moving industry. &lt;/p&gt;

&lt;p&gt;Give it a go!&lt;/p&gt;

</description>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Elasticsearch is different</title>
      <dc:creator>Chris Hunt</dc:creator>
      <pubDate>Sun, 11 Mar 2018 23:42:38 +0000</pubDate>
      <link>https://dev.to/chrishunt/elasticsearch-is-different--1abl</link>
      <guid>https://dev.to/chrishunt/elasticsearch-is-different--1abl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I'll warn you now, I may sound like an Elasticsearch salesperson in this article but I can assure you that I'm completely impartial.&lt;/p&gt;

&lt;p&gt;Until six months ago, I'd used Elasticsearch little more basic document retrieval on simple search terms. I almost resented it as another query "language" to learn. That all changed when I moved into a position which used ES extensively and I needed to get up to speed quickly.&lt;/p&gt;

&lt;p&gt;I read the book Relevant Search (&lt;a href="https://www.manning.com/books/relevant-search"&gt;https://www.manning.com/books/relevant-search&lt;/a&gt;) in two weeks. A superb read which really highlighted the difference between basic document retrieval and real relevant search. I began to see the real power in Elasticsearch. It fired up my imagination in to ways I could have used it with great effect in previous projects.&lt;/p&gt;

&lt;p&gt;I wished I'd taken some time to understand the features it offers before now and I believe it could be very valuable to many developers to at least have an overview which is what I hope to give here. It may enable you to make a better decision on software selection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notable features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Search engine
&lt;/h3&gt;

&lt;p&gt;Ok, so it's a search engine but your company doesn't require a "search engine". You use some database queries to your NoSQL/relational database which does a decent job?&lt;/p&gt;

&lt;p&gt;That's what I thought however the key here is &lt;strong&gt;relevance&lt;/strong&gt;. It's about getting the user where they want/need to be as quickly and as simply as possible. Yes, you can write a database query which puts some data in front of a user which roughly relates to what they put in the search box. And you can tinker with query but it soon becomes very complex and hard to maintain. &lt;/p&gt;

&lt;p&gt;Consider search results specific to a user weighted by their previous searches, their favourites, their age, their gender. I don't envy you having to write that SQL query!&lt;/p&gt;

&lt;p&gt;Elasticsearch will allow you to query on any of your indexed fields. You can filter your results to create a context for your search. For example, if you the user had specifically said they are searching for a book in your multi-department store, you would filter much as you would in the &lt;code&gt;WHERE&lt;/code&gt; clause of a SQL statement. &lt;/p&gt;

&lt;p&gt;The good stuff comes when you start telling Elasticsearch that the result &lt;strong&gt;may&lt;/strong&gt; have a certain term or that term but one term should be boosted above the other by a certain factor. You may want to boost more highly rated products or maybe you have some stock that you need to move, that could be boosted in the results.&lt;/p&gt;

&lt;p&gt;If you had a library of articles, you may want the most recent ones to be boosted above older articles. If your results were location based, you could boost on distance from the user.&lt;/p&gt;

&lt;p&gt;I hope this starts giving you an idea of what &lt;strong&gt;relevance&lt;/strong&gt; is and how a search engine could be useful after all. But that's just the start...&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto-suggest
&lt;/h3&gt;

&lt;p&gt;ES has built in functionality for auto-suggest. When the user is typing in the search box, you can preempt their request and put it in front of them as they type. Once again, this may have been something you could do with your current database. &lt;/p&gt;

&lt;p&gt;ES can be "smarter" though. You can build your indexes so that it looks in any part of words or phrases in multiple fields. If indexed well with good thought, ES can return its results very quickly and minimal overhead on the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aggregation and Facets
&lt;/h3&gt;

&lt;p&gt;ES can return aggregation results in the same result set as the query results. This alone is an appealing feature but it really makes sense. When searching for something, if a user can't find what they were looking for in the results returned, they want an easy step to filter their result set further. Consider I've just searched for TVs on Amazon and it returns thousands of results, how do I get closer to what I want? Facet filters - these are the properties that Amazon display on the left hand side of their desktop page. It allows me to filter further on brand, size, price range etc. ES can easily return these facets and counts for the result set returned.&lt;/p&gt;

&lt;p&gt;Aggregations follow what you'd expect from any other database and allowing a pipeline to really dig in to your results.&lt;/p&gt;

&lt;h3&gt;
  
  
  More like this
&lt;/h3&gt;

&lt;p&gt;A very simple feature to implement but really powerful. It does exactly as it suggests. It allows you to point to a document (or more) and tell ES that you want other results similar to the document you've suggested. This can be very useful for a user to potentially continue their journey on your site or an alternative product similar to the one they're viewing in a shop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kibana and visualisations
&lt;/h3&gt;

&lt;p&gt;Kibana is a web UI allowing you to easily build visualisations for your data. It's relatively simple to use and really helps you dig deep in to your data.&lt;/p&gt;

&lt;p&gt;It offers all the standard graphing types but also some quite imaginative alternatives. All of these can be put together in dashboards which can be shared and displayed in other pages or on a big screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;I've only touched the surface on an overview of Elasticsearch but I recommend following up with this slightly more in depth overview -&lt;a href="https://www.elastic.co/blog/found-uses-of-elasticsearch"&gt;https://www.elastic.co/blog/found-uses-of-elasticsearch&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;To make it clear, while I make several comparisons to databases, I'm not suggesting that Elasticsearch should replace your database however in many cases I believe it can complement your database.&lt;/p&gt;

&lt;p&gt;It could open up functionality to your product that you maybe didn't consider possible. It's very, very fast so can really improve your user experience in places and potentially reduce the load on your database server.&lt;/p&gt;

&lt;p&gt;Even if you can't think of a use case within your products, it could potentially open up a whole now world to your logs and analysis on your product performance. With the ability to analyse your data in time series data and dig deep, it could really revolutionise the way you look at your product.&lt;/p&gt;

</description>
      <category>elasticsearch</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Developer job application assessments are a two way thing</title>
      <dc:creator>Chris Hunt</dc:creator>
      <pubDate>Sun, 11 Mar 2018 21:05:04 +0000</pubDate>
      <link>https://dev.to/chrishunt/developer-job-application-assessments-are-a-two-way-thing--4m20</link>
      <guid>https://dev.to/chrishunt/developer-job-application-assessments-are-a-two-way-thing--4m20</guid>
      <description>&lt;p&gt;When looking for a new developer job, a fair amount of positions require some kind of assessment by the company in order that developers can justify their claimed skills.&lt;/p&gt;

&lt;p&gt;In my opinion, these assessments reflect on the company too. As a developer, this is an early chance to see what a company values in their developers. Having been invited to complete a number of these tests, I've seen both ends of the spectrum which adjusted my views on the company I was applying to.&lt;/p&gt;

&lt;p&gt;These assessments can be time consuming and both sides (applicant and company) should value the time put in. The applicant should be able to get a feel for the company in what they're being asked to do. It should also be challenging so that the applicant can feel some reward in completing the assessment.&lt;/p&gt;

&lt;p&gt;Very recently, I was asked to complete a 50 minute multiple choice test. Questions were generally around syntax of code printed in a non-monospaced font without syntax highlighting. I lasted about five minutes and even less questions before I sent an email back to the company explaining that their job spec highlighted the need for imaginative and versatile developers but their test promoted some kind of robot-like code monkey and that I wanted to withdraw my application. This response may have appeared arrogant to the company in question however if taken in the spirit it was intended, may highlight the need for a review in their hiring process.&lt;/p&gt;

&lt;p&gt;I believe that in attempting to assess the ability of an applicant, a company can and should also impress the "spirit" of the job in that process.&lt;/p&gt;

&lt;p&gt;My preferred method is for a project kind of problem which allows the applicant to be imaginative and to show why they should be top candidate for a role. It also allows for discussion in any further interviews around thought processes and techniques. These can often be lengthy (3 or 4 hours) but a quality application can almost secure a the role even before further interviewing.&lt;/p&gt;

&lt;p&gt;What do you think is the ideal way for a company to assess the standard of an applicant while promoting the position and the company?&lt;/p&gt;

</description>
      <category>recruitment</category>
      <category>discuss</category>
      <category>career</category>
    </item>
    <item>
      <title>Sharpening out of date skills</title>
      <dc:creator>Chris Hunt</dc:creator>
      <pubDate>Wed, 07 Mar 2018 18:55:50 +0000</pubDate>
      <link>https://dev.to/chrishunt/sharpening-out-of-date-skills--2e99</link>
      <guid>https://dev.to/chrishunt/sharpening-out-of-date-skills--2e99</guid>
      <description>&lt;p&gt;Following a set of unfortunate circumstances I found myself out of work for the first time in nearly 20 years. In itself this isn't a large concern however what I noticed when updating my CV, was more so - my core skills had been sorely neglected.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happened?
&lt;/h3&gt;

&lt;p&gt;Working for a company generally gives you a limited set of projects to work on and if there's no business reason to upgrade/refactor code/rewrite tests to use the latest and greatest software or library versions, that learning may not be available in the work environment. &lt;/p&gt;

&lt;p&gt;How about outside work? I've always used my personal projects to teach myself new skills, technologies and techniques. Ah yes - but those funky libraries, niche techniques and third party services which I spent hours implementing in a made-up project which was never going to see the light of day aren't what gets you employed. It's the core skills that do that and they needed some polishing.&lt;/p&gt;

&lt;p&gt;I guess that fun side projects aren't considered fun if you're using the same technologies that you use in the office every day.&lt;/p&gt;

&lt;h3&gt;
  
  
  The knowledge gaps
&lt;/h3&gt;

&lt;p&gt;I identified several areas which really concerned me as they were my headlines skills on my CV. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MongoDB was a two point releases ahead of any version I've used in projects&lt;/li&gt;
&lt;li&gt;While my PHP projects were running on PHP7, I hadn't upgraded any of the code to take advantage of any PHP7 functionality&lt;/li&gt;
&lt;li&gt;Most of my front end work has been built using Bootstrap 3. Bootstrap 4 has since moved through Alpha, Beta and now in release.&lt;/li&gt;
&lt;li&gt;My projects were being run in a Docker environment on EC2 however the "deployment" was still very manual&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Time for action
&lt;/h3&gt;

&lt;p&gt;I've read plenty about PHP7, MongoDB 3.6 and Bootstrap 4 to have a good idea what was available and of the new functionality but I'd never used it. I knew for a fact that reading alone doesn't embed knowledge. I learnt that from reading a lot about Promises, await/async functions and generators in Javascript. When a came to use them for the first time, I was floundering.&lt;/p&gt;

&lt;p&gt;I considered how to approach this knowledge upgrade and decided I needed to structure my actions in order that there was value beyond just learning for the sake of it. It was also very important to try to make this process assist my job searching. &lt;/p&gt;

&lt;p&gt;I considered the following approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify areas for concern and improvement&lt;/li&gt;
&lt;li&gt;Prioritise these areas&lt;/li&gt;
&lt;li&gt;Read and understand each area&lt;/li&gt;
&lt;li&gt;Identify a use case for each area &lt;/li&gt;
&lt;li&gt;Action&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I won't go in to my findings and discoveries for each step as they're very specific to me but generally the process started to fall in to place quite well with some nice discoveries along the way. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I found a course on MongoDB University about the new features in MongoDB 3.6 (&lt;a href="https://university.mongodb.com/courses/M036/about" rel="noopener noreferrer"&gt;https://university.mongodb.com/courses/M036/about&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;A couple of the 3.6 features made complete sense to implement in a one of my personal projects&lt;/li&gt;
&lt;li&gt;I discovered Bitbucket pipelines which allowed me to build and deploy my Docker containers straight to my server on &lt;code&gt;git tag&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Within a couple of weeks, I felt my core skills were back in good shape and I could speak with confidence about new features, not just having read about them, but having implemented solutions using many of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons learnt
&lt;/h3&gt;

&lt;p&gt;What's become clear to me as a developer is that I can't neglect my core skills again. Development is a fast moving area and as a developer, I owe it to keep up to speed. &lt;/p&gt;

&lt;p&gt;Obviously, a work life balance is important so we can't get home from work each night and spend hours on training therefore I think it's important to push harder in a work environment to enable learning. This obviously needs to be "sold" to the company. As I mentioned right at the start, often if there's no business requirement or value to upgrade your internal products/software, it doesn't get done.&lt;/p&gt;

&lt;p&gt;I believe there &lt;strong&gt;is&lt;/strong&gt; value to most companies to allow developers to keep up to date though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers will generally be happier that they are using newer versions of products and that they are being allowed to learn on the job.&lt;/li&gt;
&lt;li&gt;The company is likely to be more attractive when hiring developers.&lt;/li&gt;
&lt;li&gt;New features in upgraded software/libraries may enable the possibility of increasing the feature set of the company product in which it's being used.&lt;/li&gt;
&lt;li&gt;...and obviously newer versions of products often have security and speed improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of this is "hidden" value which will rely the developer selling the idea to the company and for the company to be forward thinking enough to see the value.&lt;/p&gt;

&lt;p&gt;They are the kind of companies I'll be looking at as the job search continues.&lt;/p&gt;

</description>
      <category>training</category>
      <category>skills</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
