On August 18 2016, Puppet held one of our Puppet Camp events in Denver. During such events, I have the opportunity to showcase what Puppet does in new, usually fun, ways. This time I went for deploying a micro-service that served virtual beer. This post describes how I did such a fun little demo.

The demo itself builds upon several things I have done during the last months. It expands from this post. Once you complete that exercise, you can use this post to serve the beer.

Demo Architecture

Overall, the architecture of the demo looks like this:

Preso.006

The barge(n) VMs are the same OpenStack builds that get created following the previous post referenced in this writing. However, I take that build and take it a step further by deploying a Docker engine on each “barge” and deploying 2 containers: a Flask RESTful service that has our beer gifs and a registrator container that notifies Consul that there is a new service out there. All these instances will reside behind a load balancer.

Procedure and simple code

First, if you are using Consul, you need at least a manager server. Use this manifest and dependent module to build it:

Module

Consul module

Code

class servebeer::consul_mgr {

class { 'consul':
    config_hash => {
    'datacenter' => hiera('consul.datacenter'),
    'data_dir' => '/opt/consul',
    'ui_dir' => '/opt/consul/ui',
    'bind_addr' => $::ipaddress,
    'client_addr' => '0.0.0.0',
    'node_name' => $::hostname,
    'advertise_addr' => $::ipaddress,
    'bootstrap_expect' => '1',
    'server' => true
    }
  }
}

I am leveraging the use of Hiera to get my datacenter value. You can just give a name here as a string.

Once Consul is built we can create the basic Docker + Flask service deployment.

Flask beer service container

The Flask service needs to be turned into a Docker image. For this to happen, you need to code it on a system that already has Docker installed. Go ahead and create a folder to do the work:

mkdir ~/brewery && cd ~/brewery

Now create a file called app.py and populate with this content:

from flask import Flask,jsonify,send_file
app = Flask(__name__)

@app.route('/drink')
def gimmebeer():
 filename = 'beer_pour.gif'
 return send_file(filename, mimetype='image/gif')

@app.route('/dry')

def nobeer():
 filename = 'vanderbeer.gif'
 return send_file(filename, mimetype='image/gif')


if __name__ == '__main__':
 app.run(debug=True,host='0.0.0.0')

Essentially this is serving two gif files on different routes. The gif files are on the github repo I will link at the end of this write up. Notice how there is no port 5000 reference in the code but it is on the architecture. That’s when things get fun!

Next, we create a file that allows us to include Flask itself. Name the file requirements.txt:

Flask==0.10.1

Finally, we make our Dockerfile (<- same name) and populate:

FROM ubuntu:latest
MAINTAINER Xuxo Garcia "jesus@puppet.com"
RUN apt-get update -y
RUN apt-get install -y python-pip python-dev build-essential
COPY . /brewery
WORKDIR /brewery
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["app.py"]

I have my own Docker hub registry so I will use it to push my container and make it available to my deployment. Please use your public or private registry. Run these commands to build, tag, and publish the container:

docker login
docker build -t puppetcamp-pong:latest .
docker images
docker tag <image_id> xuxog/puppetcamp-pong:latest
docker push xuxog/puppetcamp-pong

Our beer micro-service is now a container out there. For this exercise we will not attach it to another service but you get the idea with this simple deployment. Let’s deploy it!

Puppet deployment procedure and code

Module

Docker module

Code

The code needs a bit of a walk-through. I will leverage Docker Compose to perform the actual deployment on the service. At the very basics, compose let’s you define containers via a YAML file and deploy them as prescribed in that file. Furthermore, you can scale and orchestrate dependencies using it.

Create a folder to store the work in Puppet where your modules reside:

mkdir -p <modulepath>/servebeer/{manifests,files,templates}

On the files directory, create beer.yaml:

beer:
 container_name: puppetcamp2016
 image: xuxog/puppetcamp-pong
 ports:
 - "5000:5000"

Notice how it is here that I assign the service port and define the container run.

On the manifests directory, create our puppet manifest, compose_beer.pp:

class servebeer::compose_beer{

class {'docker':

    tcp_bind => ['tcp://0.0.0.0:2375'],
    socket_bind => 'unix:///var/run/docker.sock',
    ip_forward => false,
    ip_masq => false,
    iptables => false,
    dns => '8.8.8.8',

  }->

class {'docker::compose':
    ensure => present,
  }->

file {'/tmp/beer.yaml':
    ensure => file,
    source => 'puppet:///modules/servebeer/beer.yaml',
  }->

docker_compose { '/tmp/beer.yaml':
    ensure => present,
    require => File['/tmp/beer.yaml'],
  }
}

And that’s it! Now when you run this manifest via Puppet on any barge, or in my case all classified barges, the micro-service will be deployed.

I also did a registrator container deployment. This is optional if you are running Consul:

class servebeer::registrator{

exec {'deploy_registrator':
   command => "/usr/bin/docker run -d --name=registrator --net=host --volume=/var/run/docker.sock:/tmp/docker.sock gliderlabs/registrator:latest consul://192.168.0.20:8500",
   unless => "/usr/bin/test -f /root/.registrator-deployed",
 }->
 file {'/root/.registrator-deployed':
   ensure => file,
   content => "deployed registrator container",
}

}

Since I have deployed everything behind a load balancer, now we can access http://www.puppet.xuxo, my private site, and see these two services:

Beer pour!

beer

Sadness when dry 😦

vander

This fun demo is an example of how you can use Puppet to deploy micro-services on a container-based infrastructure.

If you wish to spin this demo up, grab this repo.

Thanks for reading.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s