From 91b71f857910887528b9531a1b50545cd5c5979b Mon Sep 17 00:00:00 2001 From: Feike Steenbergen Date: Mon, 11 May 2015 11:46:01 +0200 Subject: [PATCH 1/3] Created tatuspage, a health check for a PostgreSQL cluster served using http. The StatusPage uses a BaseHTTPServer to server 3 simple http endpoints: - pg_master, returns 200 if PostgreSQL is running as master - pg_slave, returns 200 if PostgreSQL is running as slave - pg_status, returns some information about the cluster It does this by relying on the PostgreSQL connection of the Governor. --- Dockerfile | 2 +- helpers/statuspage.py | 56 +++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 ++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 helpers/statuspage.py create mode 100644 requirements.txt diff --git a/Dockerfile b/Dockerfile index faaa21f6..53d56965 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update -y RUN apt-get upgrade -y ENV PGVERSION 9.4 -RUN apt-get install curl python python-pip python-psycopg2 python-yaml python-cherrypy postgresql-${PGVERSION} -y +RUN apt-get install curl python python-pip python-psycopg2 python-yaml postgresql-${PGVERSION} -y RUN ln -s /usr/lib/postgresql/* /usr/lib/postgresql/current ENV PATH /usr/lib/postgresql/current/bin:$PATH diff --git a/helpers/statuspage.py b/helpers/statuspage.py new file mode 100644 index 00000000..a27947b6 --- /dev/null +++ b/helpers/statuspage.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from BaseHTTPServer import BaseHTTPRequestHandler + + +class StatusPage(BaseHTTPRequestHandler): + + def do_GET(self): + try: + if self.path == '/pg_master': + response = (200 if postgresql.is_leader else 503) + self.send_response(response) + elif self.path == '/pg_slave': + response = (503 if postgresql.is_leader else 200) + self.send_response(response) + elif self.path == '/pg_status': + self.send_response(200) + self.end_headers() + self.wfile.write(postgresql.status()) + else: + self.send_response(404) + except: + self.send_response(500) + + +def getHTTPServer(postgresql, http_port=8081, listen_address='0.0.0.0'): + server = HTTPServer((listen_address, http_port), StatusPage) + server.postgresql = postgresql + + return server + + +if __name__ == '__main__': + import sys + import logging + from BaseHTTPServer import HTTPServer + + logging.basicConfig(format='%(levelname)-6s %(asctime)s - %(message)s', level=logging.DEBUG) + logging.debug('Starting as a standalone application') + + # # Create a dummy configuration to be able to use the Postgresql class + from postgresql import Postgresql + postgres_config = { + 'name': 'dummy', + 'listen': 'localhost:5432', + 'data_dir': None, + 'replication': {'username': None, 'password': None}, + } + aws_host_address = None + if len(sys.argv) > 1: + postgres_config['listen'] = sys.argv[1] + postgresql = Postgresql(postgres_config, aws_host_address) + + getHTTPServer(postgresql, 8081, '0.0.0.0').serve_forever() + logging.debug('Abc') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..377437fe --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +PyYAML +psycopg2 From e7adb695034a25a927293888f8c788a3f48efd39 Mon Sep 17 00:00:00 2001 From: Feike Steenbergen Date: Mon, 11 May 2015 13:11:09 +0200 Subject: [PATCH 2/3] Start the http request handler for load balancing as a thread in Governor. When not running standalone, some better referencing to the Postgresql instance was needed. --- governor.py | 8 ++++++++ helpers/statuspage.py | 12 +++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/governor.py b/governor.py index 7492b0e5..680bbb71 100755 --- a/governor.py +++ b/governor.py @@ -2,10 +2,12 @@ import sys, os, yaml, time, urllib2, atexit import logging +import threading from helpers.etcd import Etcd from helpers.postgresql import Postgresql from helpers.ha import Ha +from helpers.statuspage import StatusPage, getHTTPServer INSTANCE_METADATA_URL = "http://169.254.169.254/latest/meta-data/" @@ -30,6 +32,12 @@ etcd = Etcd(config["etcd"]) postgresql = Postgresql(config["postgresql"], aws_host_address) ha = Ha(postgresql, etcd) +## Start the http_server to serve a simple healthcheck +http_server = getHTTPServer(postgresql, http_port=8080, listen_address='0.0.0.0') +http_thread = threading.Thread(target=http_server.serve_forever, args=()) +http_thread.daemon = True +http_thread.start() + # stop postgresql on script exit def stop_postgresql(): postgresql.stop() diff --git a/helpers/statuspage.py b/helpers/statuspage.py index a27947b6..67fdb4ab 100644 --- a/helpers/statuspage.py +++ b/helpers/statuspage.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from BaseHTTPServer import BaseHTTPRequestHandler +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer class StatusPage(BaseHTTPRequestHandler): @@ -9,19 +9,21 @@ class StatusPage(BaseHTTPRequestHandler): def do_GET(self): try: if self.path == '/pg_master': - response = (200 if postgresql.is_leader else 503) + response = (200 if self.server.postgresql.is_leader else 503) self.send_response(response) elif self.path == '/pg_slave': - response = (503 if postgresql.is_leader else 200) + response = (503 if self.server.postgresql.is_leader else 200) self.send_response(response) elif self.path == '/pg_status': self.send_response(200) self.end_headers() - self.wfile.write(postgresql.status()) + self.wfile.write(self.server.postgresql.status()) else: self.send_response(404) - except: + except Exception, e: self.send_response(500) + self.end_headers() + self.wfile.write(repr(e)) def getHTTPServer(postgresql, http_port=8081, listen_address='0.0.0.0'): From 35b111083c9ec8d1b7536d26691d6d8d4eabe9b8 Mon Sep 17 00:00:00 2001 From: Oleksii Kliukin Date: Mon, 11 May 2015 17:55:00 +0200 Subject: [PATCH 3/3] Change the governor health check default port to 8008 in order to avoid a conflict with etcd proxy. --- governor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/governor.py b/governor.py index 680bbb71..f992babd 100755 --- a/governor.py +++ b/governor.py @@ -33,7 +33,7 @@ postgresql = Postgresql(config["postgresql"], aws_host_address) ha = Ha(postgresql, etcd) ## Start the http_server to serve a simple healthcheck -http_server = getHTTPServer(postgresql, http_port=8080, listen_address='0.0.0.0') +http_server = getHTTPServer(postgresql, http_port=8008, listen_address='0.0.0.0') http_thread = threading.Thread(target=http_server.serve_forever, args=()) http_thread.daemon = True http_thread.start()