Dev to Deploy; Cloud Native  Stack Part-1
Tags: grafana, letsencrypt, monitoring, logging
Published: 2021-01-02 02:30:45

*Originally Posted https://darnahsan.medium.com/dev-to-deploy-cloud-native-stack-part-1-8a5a7d99792d Published on Feb 13, 2020*

This post is build up on my previous post about Ultronex, a slack bot rewrite in Elixir. In this post I will walk through how a simple slack bot expanded to include an API, reverse proxy , container monitor and log management solution all on a cheap Hetzner VPS using cloud native tech Terraform, Packer, Ansible and Docker. If you have gone through the original post you would know that Ultronex was packaged as a container for easier development and it helped me to move my locally developed code to cloud and not expect any nasty surprises doing deployments manually. I will walk through the components of the app and how they were implemented and how they work together using docker-compose . In the following parts I will show how to provision cloud services using Terraform, Packer and Ansible.

## API

One of the first things I added to the bot was an API to monitor health check.

    Request: curl -v http://localhost:8080/ultronex/heartbeat
    
    Response: {"msg":"I don't have a heart but I am alive!"}
    
    status: 200

As being a slack bot what good is it if it can’t send messages to slack, hence an API endpoint support to send msgs to slack was added

    Password protected endpoint
    
    Request: curl --request POST \
      --url http://localhost:8080/ultronex/slack \
      --header 'Accept: */*' \
      --header 'Accept-Encoding: gzip, deflate' \
      --header 'Authorization: Basic username:password' \
      --header 'Cache-Control: no-cache' \
      --header 'Connection: keep-alive' \
      --header 'Content-Length: 81' \
      --header 'Content-Type: application/json' \
      --header 'Host: localhost:8080' 
      --header 'cache-control: no-cache' \
      --data '{"msg": {\n	"channel": "U08MG2700",\n	"text": "test",\n	"payload": "testing",\n "title": "Hola"\n	\n}\n	\n}'
    
    Response: {
        "id": "e21f9216-43b4-45d9-978b-de94da273654",
        "msg": {
            "channel": "U08MG27P0",
            "text": "test",
            "title": "Hola"
        },
        "status": "triggered"
    }
    
    status: 200

With time I added more endpoints to have visibility over what patterns were being tracked and some stats around how many messages were received, processed and responded to.

    Password protected endpoint
    
    Request: curl -v -H"Authorization: Basic username:password" http://localhost:8080/ultronex/stats
    
    Response: {"uptime":"2019-10-16 16:01:05.571045Z","total_msg_count":90075,"total_attachments_downloaded":64992,"replied_msg_count":32,"forwarded_msg_count":14}
    
    status: 200

    Password protected endpoint
    
    Request: curl -v -H"Authorization: Basic username:password" http://localhost:8080/ultronex/track
    
    Response: {"stark":"U08MG273O"}
    
    status: 200

Recently I added an endpoint to track the slack errors of websocket termination and rate limiting due to increasing volume of messages

    Password protected endpoint
    
    Request: curl -v -H"Authorization: Basic username:password" http://localhost:8080/ultronex/external
    
    Response: {"errors":{"Slack error : remote closed":12}}
    
    status: 200

## DNS

I took the API to cloud using a free DNS from [freedns.afraid.org](https://freedns.afraid.org). Its an amazing service where you can get a free DNS and point it to you cloud server and not bother to remember the IP address again. To keep the IP in sync with the domain name you picked just add a cron script provided by them.

    sleep 40 ; wget — no-check-certificate -O — [https://freedns.afraid.org/dynamic/update.php?](https://freedns.afraid.org/dynamic/update.php?QjI1dG5pMTR5bG9hNHJuc29BaDZLcHJkOjE4NzQwNDI3)<token> >> /tmp/freedns.log 2>&1 &

## SSL

Once you have a domain in today's age a web service that is not on HTTPs is a crime. With [LetsEncrypt](https://letsencrypt.org/) getting a free cert for your DNS is as simple as a one line command and just follow the steps and you should have a certificate on your machine.

    certbot certonly — standalone

## Container Monitoring

You will find a lot of tools out there for docker monitoring some are based on popular open source tools like grafana and prometheus but nothing comes close to open source [weavework scope](https://www.weave.works/oss/scope/). It not only shows you all your containers and how they are connected. It also gives you metrics about them and your host.

![](https://cdn-images-1.medium.com/max/2000/1*BIRowUbD8edH39W0BV6h5w.png)

The magic doesn’t end here, once you have your stack up and running it allows you to control individual container from the web console. You can enter the containers for debugging purpose, pause it or even stop it.

![](https://cdn-images-1.medium.com/max/2000/1*vQn6VhRVi7wGtkgFnihJTQ.png)

## Log Management

Once you are in cloud, log management becomes crucial and going to the server to tail a log becomes cumbersome specially when you now have so many components running. Two of the most popular log management stacks are ELK (Elasticsearch, Logstash, Kibana) and EFK (Elasticsearch,Fluentd, Kibana). Elasticsearch can be resource intensive and and I needed a solution to avoid multiple dashboards that is when I stumbled across another lesser known tool [Loki](https://grafana.com/oss/loki/). Loki is a tool from [Grafana](https://grafana.com/oss/) and uses Promtail another tool to push logs in to Grafana where you can stream the logs and query them.

![](https://cdn-images-1.medium.com/max/2000/1*f_-NDhUr-F9pGDazuCrOEA.png)

![](https://cdn-images-1.medium.com/max/2802/1*GDrsffkMIFh1dnaXDiUHBg.png)

## Reverse Proxy

Once you have all these services running from an API to log manager to container monitor. Connecting them to your DNS to route traffic requires to use a reverse proxy. For reverse proxy I chose [HAProxy](https://www.haproxy.org/) as its open source and has a ton of features others don’t have, to name a few its got built in healthchecks, DDoS protection, stats page and a promethues compliant stats exporter api. Reverse proxy allowed to redirect all incoming requests from HTTP to HTTPS and monitor all services’ uptime. It also allowed to shield all the apps from unnecessary traffic.

![](https://cdn-images-1.medium.com/max/2914/1*wz5LsiPMiJYDSQIcS-wcww.png)

With the [Prometheus](https://prometheus.io/) compliant stats api its easy to hookup tons of stats in to Grafana and have a detail look in to its performance.

![](https://cdn-images-1.medium.com/max/2800/1*gn4365D0ICDvP0zPYE2iyQ.png)

At the moment all of this is running on a [Hetzner CX11](https://www.hetzner.com/cloud) which is just 1vCPu and 2GB or ram. Serving close to 43rpm and peaking to 80rpm on the API and this excluding close to 200msgs per minute that the slack bot process in the application and all with under 18% CPU usage.

![](https://cdn-images-1.medium.com/max/2000/1*s222xWiipbRSEpha20bFNg.png)

In the following parts I will go through how to provision the instance in hetzner and how to build images programmatically to avoid manual configuration of your cloud instance. I will share terraform and packer code for doing so in Hetzner and Scaleway and why I chose to switch back to Hetzner from Scaleway.

All the code for this part is available online at
> [https://github.com/ahsandar/ultronex](https://github.com/ahsandar/ultronex)

![](https://cdn-images-1.medium.com/max/2000/1*zdxucjQYakPfXmAkzwK8oA.jpeg)