├── .gitignore ├── Consul.ipynb ├── LICENSE.md ├── README.md ├── docker-compose.yml └── python_app ├── Dockerfile ├── app.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # local files 2 | *.log 3 | *.pot 4 | *.pyc 5 | local_settings.py 6 | .idea 7 | .DS_Store 8 | 9 | # Coverage 10 | .coverage 11 | htmlcov/ 12 | 13 | # VIM 14 | .*.swp 15 | 16 | # Environments 17 | env/ 18 | venv/ 19 | 20 | # IPythonNotebook 21 | .ipynb_checkpoints/ 22 | -------------------------------------------------------------------------------- /Consul.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Consul Intro\n", 8 | "\n", 9 | "Please see the [README.md](https://github.com/micahhausler/consul-demo/blob/master/README.md) and get your consul cluster up and running" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "## Setting a key/value with `requests`\n", 17 | "For full documentation, see the [Consul http-api](https://www.consul.io/docs/agent/http.html)" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 20, 23 | "metadata": { 24 | "collapsed": false 25 | }, 26 | "outputs": [ 27 | { 28 | "name": "stdout", 29 | "output_type": "stream", 30 | "text": [ 31 | "true\n" 32 | ] 33 | } 34 | ], 35 | "source": [ 36 | "import requests\n", 37 | "\n", 38 | "base_url = 'http://192.168.59.103:8500/v1/kv/'\n", 39 | "\n", 40 | "response = requests.put(base_url + 'key1', data=\"value1\")\n", 41 | "print(response.text)" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "## Getting a key/value with `python-consul`\n", 49 | "For full documentation, see python-consul [documentation](http://python-consul.readthedocs.org/en/latest/#consul-kv)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 21, 55 | "metadata": { 56 | "collapsed": false 57 | }, 58 | "outputs": [ 59 | { 60 | "name": "stdout", 61 | "output_type": "stream", 62 | "text": [ 63 | "value1\n" 64 | ] 65 | } 66 | ], 67 | "source": [ 68 | "from consul import Consul\n", 69 | "\n", 70 | "c = Consul('192.168.59.103')\n", 71 | "\n", 72 | "index, data = c.kv.get('key1')\n", 73 | "\n", 74 | "print(data['Value'].decode('utf8'))" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": { 80 | "collapsed": true 81 | }, 82 | "source": [ 83 | "## Other features:\n", 84 | "\n", 85 | "* Locks\n", 86 | "* DNS for service discovery\n", 87 | "* Limit access to key/value subsets with ACL's and tokens" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": { 94 | "collapsed": true 95 | }, 96 | "outputs": [], 97 | "source": [] 98 | } 99 | ], 100 | "metadata": { 101 | "kernelspec": { 102 | "display_name": "Python 3", 103 | "language": "python", 104 | "name": "python3" 105 | }, 106 | "language_info": { 107 | "codemirror_mode": { 108 | "name": "ipython", 109 | "version": 3 110 | }, 111 | "file_extension": ".py", 112 | "mimetype": "text/x-python", 113 | "name": "python", 114 | "nbconvert_exporter": "python", 115 | "pygments_lexer": "ipython3", 116 | "version": "3.4.2" 117 | } 118 | }, 119 | "nbformat": 4, 120 | "nbformat_minor": 0 121 | } 122 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Micah Hausler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Consul Demo 2 | This is a simple demo and introduction to consul. 3 | 4 | ## Setup 5 | Setup assumes a Mac running a modern version of OS X 6 | 7 | 1. Install [boot2docker](http://boot2docker.io) 8 | 1. Install docker-compose: 9 | ``` 10 | pip install docker-compose 11 | ``` 12 | 1. Clone this repo, run consul 13 | ``` 14 | git clone git@github.com:micahhausler/consul-demo.git 15 | cd consul-demo/ 16 | docker-compose build 17 | docker-compose up 18 | ``` 19 | 20 | ## See the UI 21 | [http://192.168.59.103:8500/ui/](http://192.168.59.103:8500/ui/) 22 | 23 | ## Interact 24 | Check out the IPython notebook Consul by running: 25 | ``` 26 | pip install -r python_app/requirements.txt 27 | ipython notebook --no-browser 28 | ``` 29 | and navigate to [/notebooks/Consul.ipnb](http://localhost:8888/notebooks/Consul.ipynb) 30 | 31 | 32 | ## Query the DNS API 33 | You can query the consul DNS API once your containers are up by running: 34 | 35 | ``` 36 | dig @$(boot2docker ip) PythonApp.service.consul +tcp 37 | ``` -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | app1: 4 | build: ./python_app/ 5 | ports: 6 | - "8080:8080" 7 | app2: 8 | build: ./python_app/ 9 | ports: 10 | - "8081:8080" 11 | consul1: 12 | image: "progrium/consul:latest" 13 | container_name: "consul1" 14 | hostname: "consul1" 15 | ports: 16 | - "8400:8400" 17 | - "8500:8500" 18 | - "8600:53" 19 | command: "-server -bootstrap-expect 3 -ui-dir /ui" 20 | consul2: 21 | image: "progrium/consul:latest" 22 | container_name: "consul2" 23 | hostname: "consul2" 24 | expose: 25 | - "8400" 26 | - "8500" 27 | - "8600" 28 | command: "-server -join consul1" 29 | depends_on: 30 | - consul1 31 | consul3: 32 | image: "progrium/consul:latest" 33 | container_name: "consul3" 34 | hostname: "consul3" 35 | expose: 36 | - "8400" 37 | - "8500" 38 | - "8600" 39 | command: "-server -join consul1" 40 | depends_on: 41 | - consul1 42 | -------------------------------------------------------------------------------- /python_app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.4.3-onbuild 2 | 3 | MAINTAINER Micah Hausler, 4 | 5 | EXPOSE 8080 6 | 7 | CMD ["python", "./app.py"] 8 | -------------------------------------------------------------------------------- /python_app/app.py: -------------------------------------------------------------------------------- 1 | import json 2 | from time import sleep 3 | 4 | import requests 5 | from flask import Flask 6 | app = Flask(__name__) 7 | 8 | BASE_CONSUL_URL = 'http://consul1:8500' 9 | 10 | PORT = 8080 11 | 12 | @app.route('/') 13 | def home(): 14 | return 'Hello World!' 15 | 16 | 17 | @app.route('/health') 18 | def hello_world(): 19 | data = { 20 | 'status': 'healthy' 21 | } 22 | return json.dumps(data) 23 | 24 | 25 | def register(): 26 | url = BASE_CONSUL_URL + '/v1/agent/service/register' 27 | data = { 28 | 'name': 'PythonApp', 29 | 'address': 'app1', 30 | 'check': { 31 | 'http': 'http://app1:{port}/health'.format(port=PORT), 32 | 'interval': '10s' 33 | } 34 | } 35 | res = requests.put( 36 | url, 37 | data=json.dumps(data) 38 | ) 39 | return res.text 40 | 41 | 42 | if __name__ == '__main__': 43 | sleep(8) 44 | try: 45 | print(register()) 46 | except: 47 | pass 48 | app.debug = True 49 | app.run(host="0.0.0.0", port=PORT) 50 | -------------------------------------------------------------------------------- /python_app/requirements.txt: -------------------------------------------------------------------------------- 1 | flask==0.10.1 2 | python-consul==0.3.12 3 | requests==2.6.0 4 | --------------------------------------------------------------------------------