diff --git a/kubernetes/Dockerfile b/kubernetes/Dockerfile index a339038b..c6a97e0e 100644 --- a/kubernetes/Dockerfile +++ b/kubernetes/Dockerfile @@ -1,11 +1,11 @@ -FROM postgres:9.6 +FROM postgres:10 MAINTAINER Alexander Kukushkin RUN export DEBIAN_FRONTEND=noninteractive \ && echo 'APT::Install-Recommends "0";\nAPT::Install-Suggests "0";' > /etc/apt/apt.conf.d/01norecommend \ && apt-get update -y \ && apt-get upgrade -y \ - && apt-get install -y git curl jq python-psycopg2 python-yaml python-requests python-six python-pysocks \ + && apt-get install -y git curl jq python-psycopg2 python-yaml python-requests python-six \ python-dateutil python-pip python-prettytable python-wheel python-psutil python locales \ ## Make sure we have a en_US.UTF-8 locale available @@ -23,7 +23,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* /root/.cache -ADD entrypoint.sh callback.py / +ADD entrypoint.sh / EXPOSE 5432 8008 ENV LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 diff --git a/kubernetes/callback.py b/kubernetes/callback.py deleted file mode 100755 index c3293149..00000000 --- a/kubernetes/callback.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python - -import logging -import os -import socket -import sys -import time - -from kubernetes import client as k8s_client, config as k8s_config -from urllib3.exceptions import HTTPError -from six.moves.http_client import HTTPException - -logger = logging.getLogger(__name__) - - -class CoreV1Api(k8s_client.CoreV1Api): - - def retry(func): - def wrapped(*args, **kwargs): - count = 0 - while True: - try: - return func(*args, **kwargs) - except (HTTPException, HTTPError, socket.error, socket.timeout): - if count >= 10: - raise - logger.info('Throttling API requests...') - time.sleep(2 ** count * 0.5) - count += 1 - return wrapped - - @retry - def patch_namespaced_endpoints(self, *args, **kwargs): - return super(CoreV1Api, self).patch_namespaced_endpoints(*args, **kwargs) - - -def patch_master_endpoint(api, namespace, cluster): - addresses = [k8s_client.V1EndpointAddress(ip=os.environ['POD_IP'])] - ports = [k8s_client.V1EndpointPort(port=5432)] - subsets = [k8s_client.V1EndpointSubset(addresses=addresses, ports=ports)] - body = k8s_client.V1Endpoints(subsets=subsets) - return api.patch_namespaced_endpoints(cluster, namespace, body) - - -def main(): - logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=logging.INFO) - if len(sys.argv) != 4 or sys.argv[1] not in ('on_start', 'on_stop', 'on_role_change'): - sys.exit('Usage: %s ', sys.argv[0]) - - action, role, cluster = sys.argv[1:4] - - k8s_config.load_incluster_config() - k8s_api = CoreV1Api() - - namespace = os.environ['KUBERNETES_NAMESPACE'] - - if role == 'master' and action in ('on_start', 'on_role_change'): - patch_master_endpoint(k8s_api, namespace, cluster) - - -if __name__ == '__main__': - main() diff --git a/kubernetes/entrypoint.sh b/kubernetes/entrypoint.sh index c665726c..f018ad90 100755 --- a/kubernetes/entrypoint.sh +++ b/kubernetes/entrypoint.sh @@ -13,24 +13,11 @@ bootstrap: - data-checksums pg_hba: - host all all 0.0.0.0/0 md5 - - host replication ${PATRONI_REPLICATION_USERNAME} ${POD_IP}/16 md5 + - host replication ${PATRONI_REPLICATION_USERNAME} ${PATRONI_KUBERNETES_POD_IP}/16 md5 restapi: - connect_address: '${POD_IP}:8008' + connect_address: '${PATRONI_KUBERNETES_POD_IP}:8008' postgresql: - connect_address: '${POD_IP}:5432' - authentication: - superuser: - password: '${PATRONI_SUPERUSER_PASSWORD}' - replication: - password: '${PATRONI_REPLICATION_PASSWORD}' - callbacks: - on_start: /callback.py - on_stop: /callback.py - on_role_change: /callback.py + connect_address: '${PATRONI_KUBERNETES_POD_IP}:5432' __EOF__ -unset PATRONI_SUPERUSER_PASSWORD PATRONI_REPLICATION_PASSWORD -export KUBERNETES_NAMESPACE=$PATRONI_KUBERNETES_NAMESPACE -export POD_NAME=$PATRONI_NAME - exec /usr/bin/python /usr/local/bin/patroni /home/postgres/patroni.yml diff --git a/kubernetes/patroni_k8s.yaml b/kubernetes/patroni_k8s.yaml index 71e82350..5f929478 100644 --- a/kubernetes/patroni_k8s.yaml +++ b/kubernetes/patroni_k8s.yaml @@ -1,3 +1,5 @@ +# This manifest (and Docker image) works only with emptyDir volume and are designed for demo purpose. +# For production usage please take a look at https://github.com/zalando/spilo/tree/master/postgres-appliance apiVersion: apps/v1beta1 kind: StatefulSet metadata: @@ -19,6 +21,9 @@ spec: - name: *cluster_name image: patroni # docker build -t patroni . imagePullPolicy: IfNotPresent +# resources: +# limits: +# cpu: 100m ports: - containerPort: 8008 protocol: TCP @@ -28,7 +33,7 @@ spec: - mountPath: /home/postgres/pgdata name: pgdata env: - - name: POD_IP + - name: PATRONI_KUBERNETES_POD_IP valueFrom: fieldRef: fieldPath: status.podIP @@ -36,6 +41,8 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + - name: PATRONI_KUBERNETES_USE_ENDPOINTS + value: 'true' - name: PATRONI_KUBERNETES_LABELS value: '{application: patroni, cluster-name: patronidemo}' - name: PATRONI_SUPERUSER_USERNAME @@ -86,6 +93,7 @@ spec: # storage: 5Gi --- +# Endpoint for leader elections apiVersion: v1 kind: Endpoints metadata: @@ -96,6 +104,7 @@ metadata: subsets: [] --- +# master Service apiVersion: v1 kind: Service metadata: @@ -110,6 +119,26 @@ spec: targetPort: 5432 --- +# replica Service +apiVersion: v1 +kind: Service +metadata: + name: patronidemo-repl + labels: + application: patroni + cluster-name: &cluster_name patronidemo +spec: + type: ClusterIP + selector: + application: patroni + cluster-name: *cluster_name + role: replica + ports: + - port: 5432 + targetPort: 5432 + +--- +# Secrets (superuser password and replication passwor) apiVersion: v1 kind: Secret metadata: