{% set publish_dict = {} -%} {% set part_list = [] -%} {% set monitor_base_url_dict = {} -%} {% set mariadb_dict = {} -%} {% set mariadb_server_list = [] -%} {% set receiver_port_list = [] -%} {% set mariadb_path_list = [] -%} {% set ip = (ipv6_set | list)[0] -%} {% set ipv4 = (ipv4_set | list)[0] -%} {% set cluster_list = [] -%} {% set tag_list = ["gtidstrict", "bind", "pkg", "innodb", "noquerycache", "slow", "pfs", "linux", "readonly", "diskmonitor", "sqlerror", "compressbinlog", "bm4ci", "mroonga", "utctime", "readcommitted", "nohandshake"] -%} {% set frontend_parameter_dict = slapparameter_dict.get('slave-frontend', {}) -%} [directory] recipe = slapos.cookbook:mkdirectory home = ${buildout:directory} etc = ${:home}/etc var = ${:home}/var run = ${:var}/run scripts = ${:etc}/run service = ${:etc}/service controller = ${:etc}/controller promise = ${:etc}/promise log = ${:var}/log data = ${:var}/lib nginx-prefix = ${:var}/nginx tmp = ${:home}/tmp {% import "supervisord_lib" as supervisord_lib with context %} {% set proxysql_controller = "proxysql-ctl" -%} {{ supervisord_lib.supervisord(proxysql_controller, buildout_bin_directory, supervisord_conf, use_service_hash=False) }} {% do part_list.append("supervisord-proxysql-ctl") -%} [request-common] recipe = slapos.cookbook:request.serialised software-url = ${slap-connection:software-release-url} server-url = ${slap-connection:server-url} key-file = ${slap-connection:key-file} cert-file = ${slap-connection:cert-file} computer-id = ${slap-connection:computer-id} partition-id = ${slap-connection:partition-id} [download-proxy-config] recipe = slapos.recipe.template:jinja2 template = inline:#!{{ bash_bin }} NAME=$1 HOST=$2 PORT=$3 CONFIG=$4 if [ -z "$CONFIG" ]; then CONFIG="${repman:proxies}/proxysql-$NAME.cnf" fi mkdir -p ${repman:config-tmp}/proxies cd ${repman:config-tmp}/proxies {{ curl_bin }} -o proxies-$NAME.tar.gz ${nginx-parameter:repman-url}/api/clusters/$NAME/servers/$HOST/$PORT/config tar -xzf proxies-$NAME.tar.gz cp etc/proxysql.cnf $CONFIG rendered = ${directory:bin}/update-proxysql-config mode = 755 {% do mariadb_dict.__setitem__('computer-memory-percent-threshold', 80) -%} {% set default_parameter_dict = {"cluster1": {"name": "cluster1", "db-prefered-master": "", "database-amount": 2, "proxysql-user": "external", "logical-backup-cron": "0 21 * * *", "physical-backup-cron": "0 1 * * *"}} -%} {% for name, parameter_dict in slapparameter_dict.get('repman-cluster-dict', default_parameter_dict).items() -%} {% do mariadb_dict.__setitem__('innodb-file-per-table', parameter_dict.get('innodb-file-per-table', 1)) -%} {% do mariadb_dict.__setitem__('use-ipv6', parameter_dict.get('use-ipv6', True)) -%} {% set database_list = parameter_dict.get('database-list', [{'name': 'repdb', 'user': 'user', 'password': 'insecure'}]) -%} # Request mariadb instances {% set db_amount = parameter_dict.get('database-amount', 2) -%} {% if db_amount < 2 -%} {% set db_amount = 2 -%} {% endif -%} {% for i in range(0, db_amount) -%} {% do mariadb_dict.__setitem__('tcp-port', 2099 + (i * 100)) -%} {% set section = 'request-mariadb-' ~ i -%} {% set dbname = 'Mariadb-' ~ i -%} [{{ section }}] <= request-common software-type = mariadb name = {{ dbname }} sla-computer_guid = {{ dumps(parameter_dict.get('-sla-' ~ i ~'-computer_guid', '')) }} {% for key, value in mariadb_dict.items() -%} config-{{ key }} = {{ dumps(value) }} {% endfor -%} config-monitor-passwd = ${publish-early:monitor-password} config-root-password = ${publish-early:db-root-password} config-repman-user = ${repman-parameter:username} config-heartbeat-user = ${repman-parameter:heartbeat-user} #config-repman-passwd = ${repman-parameter:password} config-repman-url = ${nginx-parameter:backend-url} config-repman-secure-url = ${nginx-parameter:backend-ssl-url} config-cluster = {{ name }} config-name = {{ dbname }} config-database-list = {{ dumps(database_list) }} return = database-host receiver-port monitor-base-url partition-path {% do part_list.append(section) -%} {% do mariadb_server_list.append('${' ~ section ~ ':connection-database-host}') -%} {% do receiver_port_list.append('${' ~ section ~ ':connection-receiver-port}') -%} {% do mariadb_path_list.append('${' ~ section ~ ':connection-partition-path}') -%} {% do monitor_base_url_dict.__setitem__('mariadb' ~ i, '${' ~ section ~ ':connection-monitor-base-url}') -%} {% endfor -%} # Manage Replication Manager clusters [{{name}}-admin-port] recipe = slapos.cookbook:free_port ip = {{ ipv4 }} minimum = 6032 maximum = 6132 [{{name}}-port] recipe = slapos.cookbook:free_port ip = {{ ipv4 }} minimum = 7032 maximum = 7132 {% set prefered_master = parameter_dict.get("db-prefered-master") -%} [{{ name ~ '-cluster-parameter' }}] {% for key, value in parameter_dict.items() -%} {{ key }} = {{ value }} {% endfor -%} proxysql-user = {{ parameter_dict.get("proxysql-user", "external") }} proxy-port = {{ '${' ~ name ~ '-port:port}' }} proxy-admin-port = {{ '${' ~ name ~ '-admin-port:port}' }} db-user = repman db-password = ${publish-early:db-root-password} db-list = {{ mariadb_server_list | join(',') }} heartbeat-user = ${repman-parameter:heartbeat-user} heartbeat-password = ${publish-early:db-root-password} partition-list = {{ mariadb_path_list | join(',') }} {% if prefered_master -%} db-prefered-master = {{ prefered_master }} {% else -%} # First database is the prefered master db-prefered-master = {{ mariadb_server_list[0] }} {% endif -%} proxysql-servers = {{ ipv4 }} proxysql-servers-ipv6 = [{{ ip }}] password = ${repman-parameter:password} proxysql-partition = ${buildout:directory} receiver-port-list = {{ receiver_port_list | join(',') }} enabled-tags = {{ slapparameter_dict.get("tag-list", tag_list) | join(',') }} proxy-tags = {{ parameter_dict.get("proxy-tags", ["pkg", "masterslave", "linux", "noreadwritesplit"]) | join(',') }} logical-backup-cron = {{ parameter_dict.get("logical-backup-cron", "0 22 * * *") }} physical-backup-cron = {{ parameter_dict.get("physical-backup-cron", "0 0 * * *") }} proxy-cpu-cores = {{ parameter_dict.get("proxy-cpu-cores", 2) }} proxy-memory = {{ parameter_dict.get("proxy-memory", 1) }} db-cpu-cores = {{ parameter_dict.get("db-cpu-cores", 2) }} db-disk-iops = {{ parameter_dict.get("db-disk-iops", 300) }} db-memory = {{ parameter_dict.get("db-memory", 256) }} db-memory-shared-pct = {{ parameter_dict.get("db-memory-shared-pct", ["threads:16", "innodb:60", "myisam:10", "aria:10", "rocksdb:1", "tokudb:1", "s3:1", "archive:1", "querycache:0"]) | join(',') }} db-memory-threaded-pct = {{ parameter_dict.get("db-memory-threaded-pct", ["tmp:70", "join:20", "sort:10"]) | join(',') }} # failover failover-mode = {{ parameter_dict.get('failover-mode', 'manual') }} failover-limit = {{ parameter_dict.get('failover-limit', 5) }} failover-falsepositive-heartbeat = {{ parameter_dict.get('failover-falsepositive-heartbeat', True) }} failover-falsepositive-heartbeat-timeout = {{ parameter_dict.get('failover-falsepositive-heartbeat-timeout', 3) }} failover-falsepositive-ping-counter = {{ parameter_dict.get('failover-falsepositive-ping-counter', 5) }} failover-max-slave-delay = {{ parameter_dict.get('failover-max-slave-delay', 30) }} failover-readonly-state = {{ parameter_dict.get('failover-readonly-state', True) }} failover-restart-unsafe = {{ parameter_dict.get('failover-restart-unsafe', False) }} failover-time-limit = {{ parameter_dict.get('failover-time-limit', 0) }} #switchover switchover-at-equal-gtid = {{ parameter_dict.get('switchover-at-equal-gtid', False) }} switchover-slave-wait-catch = {{ parameter_dict.get('switchover-slave-wait-catch', True) }} switchover-wait-kill = {{ parameter_dict.get('switchover-wait-kill', 5000) }} switchover-wait-trx = {{ parameter_dict.get('switchover-wait-trx', 10) }} switchover-wait-write-query = {{ parameter_dict.get('switchover-wait-write-query', 10) }} [{{ 'config-' ~ name }}] recipe = slapos.recipe.template:jinja2 template = {{ config_cluster_toml_in }} rendered = ${repman:clusters}/config-{{ name }}.toml extra-context = context = section parameter_dict {{ name ~ '-cluster-parameter' }} # Donwnload mariadb configuration from repman [config-proxysql-{{ name }}] recipe = plone.recipe.command # if Repman is not started, cannot download config from server stop-on-error = false config = ${repman:proxies}/proxysql-{{ name }}.cnf data = ${repman:proxy-data}/{{ name }} command = mkdir -p ${:data} && ${download-proxy-config:rendered} {{ name }} {{ ipv4 }} {{ '${' ~ name ~ '-cluster-parameter:proxy-admin-port}' }} ${:config} update-command = ${:command} [proxysql-{{ name }}-wrapper] recipe = slapos.cookbook:wrapper command-line = {{ proxysql_location }}/bin/proxysql -f -c ${config-proxysql-{{ name }}:config} -D ${config-proxysql-{{ name }}:data} --reload # -S /tmp/proxysql_admin.sock wrapper-path = ${directory:controller}/proxysql-{{ name }} wait-for-files = ${repman:bootstrap}/{{ name }}_bootstrapped ${config-proxysql-{{ name }}:config} depends = {{ '${proxysql-' ~ name ~ '-admin-promise:recipe}' }} {{ '${proxysql-' ~ name ~ '-promise:recipe}' }} {{ '${proxysql-' ~ name ~ '-ipv6-promise:recipe}' }} [proxysql-{{ name }}-admin-promise] <= monitor-promise-base promise = check_socket_listening name = proxysql-{{ name }}-admin-port-listening.py config-host = {{ ipv4 }} config-port = {{ '${' ~ name ~ '-cluster-parameter:proxy-admin-port}' }} [proxysql-{{ name }}-promise] <= monitor-promise-base promise = check_socket_listening name = proxysql-{{ name }}-port-listening.py config-host = {{ ipv4 }} config-port = {{ '${' ~ name ~ '-cluster-parameter:proxy-port}' }} [proxysql-{{ name }}-ipv6-promise] <= monitor-promise-base promise = check_socket_listening name = proxysql-{{ name }}-ipv6-port-listening.py config-host = {{ ip }} config-port = {{ '${' ~ name ~ '-cluster-parameter:proxy-port}' }} {% set service_name = "proxysql-" ~ name -%} {% set proxysql_dict = {"name": service_name, "command": "${" ~ service_name ~ "-wrapper:wrapper-path}", "stopwaitsecs": 60, "environment": [], "stdout_logfile": "${repman:proxies-log}/" ~ service_name ~ ".log", "stderr_logfile": "${repman:proxies-log}/" ~ service_name ~ ".log" } %} {{ supervisord_lib.supervisord_program(service_name, proxysql_dict) }} {% do part_list.append("supervisord-" ~ service_name) %} {% do part_list.append('config-' ~ name) -%} {% do cluster_list.append("{'name': '" ~ name ~ "', 'host': '" ~ ipv4 ~ "', 'port': '${" ~ name ~ "-cluster-parameter:proxy-admin-port}'}") -%} {% set publish_database_list = [] -%} {% set publish_database_v6_list = [] -%} {% for database in database_list -%} {% if database.get('user') -%} {% do publish_database_list.append("mysql://" ~ database['user'] ~ ":" ~ database['password'] ~ "@" ~ ipv4 ~ ":${" ~ name ~ "-cluster-parameter:proxy-port}/" ~ database['name']) -%} {% do publish_database_v6_list.append("mysql://" ~ database['user'] ~ ":" ~ database['password'] ~ "@[" ~ ip ~ "]:${" ~ name ~ "-cluster-parameter:proxy-port}/" ~ database['name']) -%} {% else -%} {% do publish_database_list.append("mysql://" ~ ipv4 ~ ":${" ~ name ~ "-cluster-parameter:proxy-port}/" ~ database['name']) -%} {% do publish_database_v6_list.append("mysql://[" ~ ip ~ "]:${" ~ name ~ "-cluster-parameter:proxy-port}/" ~ database['name']) -%} {% endif -%} {% endfor -%} {% do publish_dict.__setitem__(name ~ '-database-list', "!py!['" ~ publish_database_list | join("', '") ~ "']") -%} {% do publish_dict.__setitem__(name ~ '-database-list-v6', "!py!['" ~ publish_database_v6_list | join("', '") ~ "']") -%} {% endfor -%} [slap-configuration] recipe = slapos.cookbook:slapconfiguration computer = ${slap-connection:computer-id} partition = ${slap-connection:partition-id} url = ${slap-connection:server-url} key = ${slap-connection:key-file} cert = ${slap-connection:cert-file} [instance-parameter] <= slap-configuration # repman monitor seems to use a fixed port repman-secure-port = 10005 repman-port = 10001 nginx-ssl-port = 10006 nginx-port = 10007 [repman] recipe = slapos.cookbook:mkdirectory etc = ${directory:etc}/repman data-dir = ${directory:var}/lib root-dir = ${directory:srv}/repman clusters = ${:etc}/cluster.d proxies = ${:etc}/proxy proxy-data = ${:data-dir}/proxy config-tmp = ${directory:tmp}/config bootstrap = ${:etc}/bootstrap proxies-log = ${directory:log}/proxy [nginx-parameter] ipv6 = ${instance-parameter:ipv6-random} port = ${instance-parameter:nginx-port} ssl-port = ${instance-parameter:nginx-ssl-port} ssl-certificate = ${ca-nginx:cert-file} ssl-key = ${ca-nginx:key-file} pid-file = ${directory:run}/nginx.pid access-log = ${directory:log}/nginx_access.log error-log = ${directory:log}/nginx_error.log repman-secure-url = https://${repman-parameter:ipv4}:${repman-parameter:secure-port} repman-url = http://${repman-parameter:ipv4}:${repman-parameter:port} config-file = ${directory:etc}/nginx.conf backend-ssl-url = https://[${:ipv6}]:${:ssl-port} backend-url = http://[${:ipv6}]:${:port} [repman-password] recipe = slapos.cookbook:generate.password bytes = 12 [gen-root-password] recipe = slapos.cookbook:generate.password bytes = 12 [repman-parameter] log = ${directory:log}/repman.log http-root = ${repman:root-dir}/dashboard share-dir = ${repman:root-dir}/share secure-port = ${instance-parameter:repman-secure-port} port = ${instance-parameter:repman-port} ipv4 = ${instance-parameter:ipv4-random} username = admin heartbeat-user = heartbeat password = ${publish-early:repman-password} cluster-d = ${repman:clusters} autorejoin = {{ slapparameter_dict.get("autorejoin", True) }} autoseed = {{ slapparameter_dict.get("autoseed", True) }} mysql-bin-dir = {{ mariadb_location }}/bin mysqlbinlog-path = {{ mariadb_location }}/bin/mysqlbinlog mysqlclient-path = {{ mariadb_location }}/bin/mysql mysqldump-path = {{ mariadb_location }}/bin/mysqldump haproxy-bin = {{ haproxy_location }}/sbin/haproxy sysbench-bin = {{ sysbench_location }}/bin/sysbench restic-bin = {{ restic_bin_location }} mail-from = {{ slapparameter_dict.get("mail-from", "mrm@localhost") }} mail-smtp-addr = {{ slapparameter_dict.get("mail-smtp-addr", "localhost") }} mail-smtp-port = {{ slapparameter_dict.get("mail-smtp-port", "25") }} mail-smtp-password = {{ slapparameter_dict.get("mail-smtp-password", "") }} mail-smtp-user = {{ slapparameter_dict.get("mail-smtp-user", "") }} mail-to = {{ slapparameter_dict.get("mail-to", "") }} http-session-lifetime = {{ slapparameter_dict.get("http-session-lifetime", 86400) }} http-refresh-interval = {{ slapparameter_dict.get("http-refresh-interval", 4) }} [repman-config-folder] recipe = plone.recipe.command repman-location = {{ repman_src_location }} command = cd ${:repman-location} {{ rsync_location }}/bin/rsync -av share ${repman:root-dir}/ {{ rsync_location }}/bin/rsync -av dashboard ${repman:root-dir}/ update-command = ${:command} [replication-manager-reload] recipe = slapos.recipe.template:jinja2 template = {{ template_repman_manager_sh }} cluster-list = {{ dumps( list(slapparameter_dict.get('repman-cluster-dict', default_parameter_dict)) ) }} context = section parameter_dict repman key username repman-parameter:username key password repman-parameter:password key secure_url nginx-parameter:backend-ssl-url key cluster_name_list :cluster-list raw jq_bin {{ jq_bin }} raw curl_bin {{ curl_bin }} raw bash_bin {{ bash_bin }} rendered = ${directory:scripts}/repman-reload mode = 755 [replication-manager] recipe = slapos.cookbook:wrapper command-line = {{ gowork_bin }}/replication-manager --monitoring-basedir=${repman:root-dir} --monitoring-sharedir=${repman-parameter:share-dir} --http-root=${repman-parameter:http-root} --monitoring-datadir=${repman:data-dir} --config=${repman-config.toml:rendered} --log-file=${repman-parameter:log} --memprofile=${directory:tmp}/repmgr.mprof monitor wrapper-path = ${directory:service}/replication-manager # setup repman instance folder depends = ${repman-config-folder:recipe} ${replication-manager-reload:recipe} ${repman-listen-promise:recipe} ${repman-listen-ssl-promise:recipe} [repman-config.toml] recipe = slapos.recipe.template:jinja2 template = {{ config_toml_in }} rendered = ${repman:etc}/config.toml extra-context = context = section parameter_dict repman-parameter [repman-listen-promise] <= monitor-promise-base promise = check_socket_listening name = repman_service_listen.py config-host = ${repman-parameter:ipv4} config-port = ${repman-parameter:port} [repman-listen-ssl-promise] <= monitor-promise-base promise = check_socket_listening name = repman_service_ssl_listen.py config-host = ${repman-parameter:ipv4} config-port = ${repman-parameter:secure-port} [nginx-conf] recipe = slapos.recipe.template:jinja2 template = {{ nginx_conf_in }} rendered = ${nginx-parameter:config-file} context = section parameter_dict nginx-parameter [nginx-launcher] recipe = slapos.cookbook:wrapper command-line = {{ nginx_bin }} -p ${directory:nginx-prefix} -c ${nginx-conf:rendered} wrapper-path = ${directory:bin}/nginx-start wait-for-files = ${ca-directory:certs}/nginx.key ${ca-directory:certs}/nginx.crt ${nginx-graceful-wrapper:rendered} [nginx-graceful-wrapper] recipe = slapos.recipe.template:jinja2 template = inline:#!{{ bash_bin }} kill -USR1 "$(cat ${nginx-parameter:pid-file})" rendered = ${directory:scripts}/nginx-graceful context = mode = 755 [ca-nginx] <= certificate-authority recipe = slapos.cookbook:certificate_authority.request cert-file = ${ca-directory:certs}/nginx.crt key-file = ${ca-directory:certs}/nginx.key executable = ${nginx-launcher:wrapper-path} wrapper = ${directory:bin}/ca-nginx [ca-nginx-service] recipe = slapos.cookbook:wrapper command-line = ${ca-nginx:wrapper} wrapper-path = ${directory:services}/nginx hash-existing-files = ${buildout:directory}/software_release/buildout.cfg [logrotate-entry-nginx] <= logrotate-entry-base name = nginx log = ${nginx-parameter:access-log} ${nginx-parameter:error-log} post = kill -USR1 $(cat ${nginx-parameter:pid-file}) [publish-early] recipe = slapos.cookbook:publish-early -init = monitor-password monitor-htpasswd:passwd db-root-password gen-root-password:passwd repman-password repman-password:passwd [publish-connection-parameter] <= monitor-publish -extends = publish-early recipe = slapos.cookbook:publish backend-url = ${nginx-parameter:backend-ssl-url} url = ${repman-frontend:connection-secure_access} username = ${repman-parameter:username} {% for name, value in publish_dict.items() -%} {{ name }} = {{ value }} {% endfor %} [monitor-instance-parameter] monitor-httpd-port = 8060 cors-domains = {{ slapparameter_dict.get('monitor-cors-domains', 'monitor.app.officejs.com') }} username = admin password = ${publish-early:monitor-password} [monitor-base-url-dict] {% for key, value in monitor_base_url_dict.items() -%} {{ key }} = {{ value }} {% endfor %} [repman-frontend] <= slap-connection recipe = slapos.cookbook:requestoptional name = Replication Manager Frontend # XXX We have hardcoded SR URL here. software-url = {{ frontend_parameter_dict.get('frontend-software-url', 'http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg') }} {% if frontend_parameter_dict.get('frontend-software-type', '') -%} software-type ={{ frontend_parameter_dict['frontend-software-type'] }} {% endif -%} slave = true config-url = ${nginx-parameter:backend-ssl-url} config-domain = {{ frontend_parameter_dict.get('slave-domain', '') }} return = domain secure_access [repman-frontend-promise] <= monitor-promise-base promise = check_url_available name = check_repman_frontend.py config-url = https://${repman-frontend:connection-domain} [repman-backend-promise] <= monitor-promise-base promise = check_url_available name = check_repman_backend.py config-url = ${nginx-parameter:backend-ssl-url} [template-proxysql-need-stop-start] recipe = slapos.recipe.template:jinja2 rendered = ${directory:bin}/proxysql_check_stop_start template = {{ template_proxy_need_stopstart }} mode = 755 cluster-list = !py![{{ cluster_list | join(', ') }}] context = key proxysql_controller {{proxysql_controller}}-bin:wrapper-path key repman_url nginx-parameter:backend-url key get_proxy_config download-proxy-config:rendered key cluster_list :cluster-list raw jq_bin {{ jq_bin }} raw bash_bin {{ bash_bin }} raw curl_bin {{ curl_bin }} [proxy-need-stop-start] recipe = slapos.cookbook:cron.d cron-entries = ${cron:cron-entries} name = proxysql-need-stop-start frequency = * * * * * command = ${template-proxysql-need-stop-start:rendered} ############################# # # Deploy replication-manager instance # ############################# [buildout] extends = {{ template_monitor }} parts = replication-manager monitor-base logrotate-entry-nginx ca-nginx-service publish-connection-parameter repman-frontend-promise repman-backend-promise proxy-need-stop-start # Complete parts with sections {{ part_list | join('\n ') }} eggs-directory = {{ eggs_directory }} develop-eggs-directory = {{ develop_eggs_directory }} offline = true