PostDock icon indicating copy to clipboard operation
PostDock copied to clipboard

Service which will always point to master

Open paunin opened this issue 6 years ago • 2 comments

There are cases when it's required to have connection to master in any point of time. Need to introduce service which always would connect client to the master regardless of the current state of the cluster.

Service should proxy all of the used ports from postgres service including one for replication and pcp.

Some examples with HA proxy

  • https://severalnines.com/blog/postgresql-load-balancing-using-haproxy-keepalived
  • https://sushilmohanty.wordpress.com/2013/08/20/haproxy-scaling-postgres-read-only-database-replicas/

paunin avatar Mar 11 '18 08:03 paunin

Just in case it helps, we do that with pgpool.

palmerabollo avatar Jul 21 '18 10:07 palmerabollo

I've solved this for barman by adding script that adds label to pod with current role (primary or standby) and service that has a selector with role=primary. Here's the script:

#!/usr/bin/env python3

import json
import logging
import requests
import requests.exceptions
import os
import socket
import sys
import time

KUBE_SERVICE_DIR = '/var/run/secrets/kubernetes.io/serviceaccount/'
KUBE_NAMESPACE_FILENAME = KUBE_SERVICE_DIR + 'namespace'
KUBE_TOKEN_FILENAME = KUBE_SERVICE_DIR + 'token'
KUBE_CA_CERT = KUBE_SERVICE_DIR + 'ca.crt'

KUBE_API_URL = 'https://kubernetes.default.svc.cluster.local/api/v1/namespaces'

logger = logging.getLogger(__name__)

LABEL = os.environ.get("KUBERNETES_ROLE_LABEL", 'role')

def read_first_line(filename):
    try:
        with open(filename) as f:
            return f.readline().rstrip()
    except IOError:
        return None


def read_token():
    return read_first_line(KUBE_TOKEN_FILENAME)

def api_patch(namespace, kind, name, entity_name, body):
    api_url = '/'.join([KUBE_API_URL, namespace, kind, name])
    while True:
        try:
            token = read_token()
            if token:
                r = requests.patch(api_url, data=body, verify=KUBE_CA_CERT,
                                   headers={'Content-Type': 'application/strategic-merge-patch+json',
                                            'Authorization': 'Bearer {0}'.format(token)})
                if r.status_code >= 300:
                    logger.warning('Unable to change %s: %s', entity_name, r.text)
                else:
                    break
            else:
                logger.warning('Unable to read Kubernetes authorization token')
        except requests.exceptions.RequestException as e:
            logger.warning('Exception when executing PATCH on %s: %s', api_url, e)
        time.sleep(1)

def change_pod_role_label(namespace, new_role):
    body = json.dumps({'metadata': {'labels': {LABEL: new_role}}})
    api_patch(namespace, 'pods', os.environ['HOSTNAME'], '{} label'.format(LABEL), body)

def record_role_change(new_role):
    logger.debug("Changing the pod's role to %s", new_role)
    pod_namespace = os.environ.get('POD_NAMESPACE', read_first_line(KUBE_NAMESPACE_FILENAME)) or 'default'
    change_pod_role_label(pod_namespace, new_role)

def main():
    logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=logging.INFO)
    if len(sys.argv) == 2 and sys.argv[1] in ('primary', 'standby'):
        record_role_change(new_role=sys.argv[1])
    else:
        sys.exit('Usage: %s <primary/standby>', sys.argv[0])
    return 0

if __name__ == '__main__':
    main()

This script is then called by /usr/local/bin/cluster/repmgr/events/execs/includes/unlock_standby.sh like python3 /usr/local/bin/label.py primary. And same for unlock_master.sh with parameter standby. The only extra package needed is python3-requests.

alivespirit avatar Feb 19 '19 17:02 alivespirit