Skip to content

PMM-7: New MySQL setup for version 8.4 #136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: v3
Choose a base branch
from
Empty file added pmm_qa/mysql/__init__.py
Empty file.
14 changes: 14 additions & 0 deletions pmm_qa/mysql/data/group_replication/init.sql.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- Create replication user and grant necessary privileges
SET SQL_LOG_BIN=0;
CREATE USER '{{ replication_user }}'@'%' IDENTIFIED BY '{{ replication_password }}';
GRANT REPLICATION SLAVE ON *.* TO '{{ replication_user }}'@'%';
GRANT CONNECTION_ADMIN ON *.* TO '{{ replication_user }}'@'%';
GRANT BACKUP_ADMIN ON *.* TO '{{ replication_user }}'@'%';
GRANT GROUP_REPLICATION_STREAM ON *.* TO '{{ replication_user }}'@'%';
-- GRANT SERVICE_CONNECTION_ADMIN ON *.* TO '{{ replication_user }}'@'%';
-- GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO '{{ replication_user }}'@'%';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;

-- Configure group replication recovery credentials
CHANGE REPLICATION SOURCE TO SOURCE_USER='{{ replication_user }}', SOURCE_PASSWORD='{{ replication_password }}' FOR CHANNEL 'group_replication_recovery';
44 changes: 44 additions & 0 deletions pmm_qa/mysql/data/group_replication/my.cnf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[mysqld]
# General server configuration
server_id={{ server_id_start + item - 1 }}
bind-address=0.0.0.0
port={{ mysql_listen_port }}

# General replication settings
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE
log_bin=binlog
log_replica_updates=ON
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
lower_case_table_names=2 # MacOS-specific, but also good generally

# MySQL 8.4 compatibility settings
report_host=mysql_pmm_{{ms_version}}_{{ item }}

# Group Replication Settings
plugin_load_add='group_replication.so'
loose-group_replication_group_name='aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
loose-group_replication_local_address='mysql_pmm_{{ms_version}}_{{ item }}:{{ group_seeds_port }}'
loose-group_replication_group_seeds='{% for i in range(1, nodes_count | int + 1) %}mysql_pmm_{{ms_version}}_{{ i }}:{{ group_seeds_port }}{% if not loop.last %},{% endif %}{% endfor %}'
loose-group_replication_communication_stack=XCOM

# Group replication behavior
loose-group_replication_start_on_boot=OFF
loose-group_replication_bootstrap_group=OFF
loose-group_replication_single_primary_mode=ON
loose-group_replication_enforce_update_everywhere_checks=OFF

# Recovery settings
loose-group_replication_recovery_get_public_key=ON
loose-group_replication_recovery_retry_count=10
loose-group_replication_recovery_reconnect_interval=60

# Crash-safe replication settings
relay-log=mysql_pmm_{{ms_version}}_{{ item }}-relay-bin
relay_log_recovery=ON
relay_log_purge=ON

# Performance and connection settings
max_connections=1000
innodb_buffer_pool_size=256M
13 changes: 13 additions & 0 deletions pmm_qa/mysql/data/init-async-replication.sql.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- Create replication user and grant necessary privileges
SET SQL_LOG_BIN=0;
CREATE USER '{{ replication_user }}'@'%' IDENTIFIED WITH 'caching_sha2_password' BY '{{ replication_password }}' REQUIRE NONE;
GRANT REPLICATION SLAVE ON *.* TO '{{ replication_user }}'@'%';
GRANT CONNECTION_ADMIN ON *.* TO '{{ replication_user }}'@'%';
GRANT BACKUP_ADMIN ON *.* TO '{{ replication_user }}'@'%';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;

{% if item == 1 %}
-- Primary server: enable binary logging for replication
FLUSH BINARY LOGS;
{% endif %}
42 changes: 42 additions & 0 deletions pmm_qa/mysql/data/my-async-replication.cnf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[mysqld]
# General server configuration
server_id={{ item }}
bind-address=0.0.0.0
port={{ mysql_listen_port }}

# Authentication settings for caching_sha2_password
caching_sha2_password_auto_generate_rsa_keys=ON
# The following two parameters tell MySQL where to store the RSA key pair
caching_sha2_password_private_key_path=private_key.pem
caching_sha2_password_public_key_path=public_key.pem

# Replication settings
gtid_mode=ON
enforce_gtid_consistency=ON
log_bin=binlog
log_replica_updates=ON
sync_binlog=1
binlog_checksum=NONE
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
# MacOS-specific, where table names are case-sensitive
lower_case_table_names=2

# MySQL 8.4 compatibility settings
report_host=mysql_pmm_{{ ms_version }}_{{ item }}

# Replica configuration - applies to all nodes except primary (they'll be able to become replicas)
{% if item != 1 %}
# Replica specific settings
replica_parallel_workers=4
replica_parallel_type=LOGICAL_CLOCK
replica_preserve_commit_order=1
{% endif %}

# Crash-safe replication settings
relay-log=mysql_pmm_{{ ms_version }}_{{ item }}-relay-bin
relay_log_recovery=ON
relay_log_purge=ON

# Performance and connection settings
max_connections=1000
innodb_buffer_pool_size=256M
Empty file added pmm_qa/mysql/data/my.cnf.j2
Empty file.
236 changes: 236 additions & 0 deletions pmm_qa/mysql/mysql_setup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
---
# MySQL 8.4 and higher single instance and also Cluster with Group Replication
- name: Setup MySQL 8.4 and higher. Cluster with Group Replication in Docker
hosts: localhost
connection: local
gather_facts: yes
vars:
ms_version: "{{ lookup('env', 'MS_VERSION') | default('8.4', true) }}"
cluster_name: "mysql_cluster"
replication_user: "repl_user"
replication_password: "GRgrO9301RuF"
root_password: "GRgrO9301RuF"
mysql_port: 4306
mysql_listen_port: 3306
group_seeds_port: 44061
nodes_count: "{{ (lookup('env', 'NODES_COUNT') | default('1', true)) | int }}"
network_name: "pmm-qa"
data_dir: "{{ lookup('env', 'HOME') }}/mysql_cluster_data"
server_id_start: 1
pmm_server_ip: "{{ lookup('vars', 'extra_pmm_server_ip', default=lookup('env','PMM_SERVER_IP') | default('127.0.0.1', true) ) }}"
client_version: "{{ lookup('vars', 'extra_client_version', default=lookup('env','CLIENT_VERSION') | default('3-dev-latest', true) ) }}"
admin_password: "{{ lookup('vars', 'extra_admin_password', default=lookup('env','ADMIN_PASSWORD') | default('admin', true) ) }}"
query_source: "{{ lookup('env', 'QUERY_SOURCE') | default('perfschema', true) }}"
metrics_mode: "{{ lookup('env', 'metrics_mode') }}"
setup_type: "{{ lookup('env', 'SETUP_TYPE') }}"
random_service_name_value: ""

tasks:
- name: Mofidy the node count for group replication
set_fact:
nodes_count: 3
when: nodes_count | int < 3 and setup_type == "gr"

- name: Chance to correct nodes count for async replication
set_fact:
nodes_count: 2
when: nodes_count | int < 2 and setup_type == "replication"

- name: Create Docker network
community.docker.docker_network:
name: "{{ network_name }}"
state: present

- name: Remove old data folders
shell: 'rm -fr {{ data_dir }}'
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Create data directories
file:
path: "{{ data_dir }}/node{{ item }}/data"
state: directory
mode: '0755'
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Remove old MySQL containers
community.docker.docker_container:
name: "mysql_pmm_{{ ms_version }}_{{ item }}"
image: "mysql:{{ ms_version }}"
restart_policy: always
state: absent
loop: "{{ range(1, nodes_count | int + 1) | list }}"
ignore_errors: yes

- name: Recursively change ownership of a directory
shell: "sudo chown -R 1001:1001 {{ data_dir }}/node{{ item }}/data"
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Setup MySQL group replication
include_tasks: ./tasks/mysql-group-replication-setup.yml
when: setup_type == "gr"

- name: Setup MySQL with async replication
include_tasks: ./tasks/mysql-async-replication-setup.yml
when: setup_type == "replication"

- name: Setup MySQL
include_tasks: tasks/mysql-setup-single.yml
when: setup_type != "gr" and setup_type != "replication"

- name: Create slowlog configuration for mysql nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: >
mysql -uroot -p{{ root_password }} -e "
SET GLOBAL slow_query_log='ON';
SET GLOBAL long_query_time=0;
SET GLOBAL log_slow_admin_statements=ON;
SET GLOBAL log_slow_slave_statements=ON;
"
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: query_source == "slowlog"

- name: Install and add pmm client.
include_tasks: ../tasks/install_pmm_client_docker_container.yml
vars:
container_name: "mysql_pmm_{{ ms_version }}_{{ item }}"
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Get already connected services to pmm server
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_1"
command: >
sh -c 'curl --location --insecure -u"admin:{{ admin_password }}" -s --request GET "http://{{ pmm_server_ip }}:{{ '80' if pmm_server_ip is ansible.utils.ipv4 else '8080' }}/v1/management/services" | jq -r ".services[].service_name"'
register: pmm_server_services

- name: Display already connected services to pmm server
debug:
msg: "{{ pmm_server_services.stdout | split('\n') }}"

- name: Find out if service is already connected to pmm server
block:
- name: Loop through MySQL databases
set_fact:
random_service_name_value: "_{{ 9999 | random + 1 }}"
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: "('mysql_pmm_' ~ ms_version ~ '_' ~ item) in pmm_server_services.stdout"

- name: Add service to pmm server
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: pmm-admin add mysql --query-source={{ query_source }} --username=root --password={{ root_password }} --environment=ms-gr-dev --cluster=ms-gr-dev-cluster --replication-set=ms-gr-replication mysql_pmm_{{ ms_version }}_{{ item }}{{ random_service_name_value }} --debug 127.0.0.1:3306
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: setup_type == "gr"

- name: Add service to pmm server
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: pmm-admin add mysql --query-source={{ query_source }} --username=root --password={{ root_password }} --environment=ms-replication-dev --cluster=ms-replication-dev-cluster --replication-set=ms-async-replication mysql_pmm_{{ ms_version }}_{{ item }}{{ random_service_name_value }} --debug 127.0.0.1:3306
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: setup_type == "replication"

- name: Add service to pmm server
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: pmm-admin add mysql --query-source={{ query_source }} --username=root --password={{ root_password }} --environment=ms-dev mysql_pmm_{{ ms_version }}_{{ item }}{{ random_service_name_value }} --debug 127.0.0.1:3306
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: setup_type != "gr" and setup_type != "replication"

- name: Install sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
user: "root"
command: >
/bin/sh -c "
wget -O epel-release.rpm --progress=dot:giga https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm &&
rpm -i epel-release.rpm &&
microdnf install -y sysbench
"
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Prepare sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: >
mysql -uroot -p{{ root_password }} -e "
SET GLOBAL super_read_only = OFF;
SET GLOBAL read_only = OFF;
"
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Prepare sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: >
mysql -uroot -p{{ root_password }} -e "
CREATE DATABASE sbtest;
CREATE USER 'sbtest'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'sbtest'@'localhost';
CREATE USER 'sbtest'@'127.0.0.1' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'sbtest'@'127.0.0.1';
FLUSH PRIVILEGES;
"
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: setup_type != "gr" and setup_type != "replication"

- name: Prepare sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_1"
command: >
mysql -uroot -p{{ root_password }} -e "
CREATE DATABASE sbtest;
CREATE USER 'sbtest'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'sbtest'@'localhost';
CREATE USER 'sbtest'@'127.0.0.1' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'sbtest'@'127.0.0.1';
FLUSH PRIVILEGES;
"
when: setup_type == "gr" or setup_type == "replication"

- name: Prepare data for sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: >
sysbench /usr/share/sysbench/oltp_read_write.lua
--mysql-host=127.0.0.1
--mysql-port=3306
--mysql-user=sbtest
--mysql-password=password
--mysql-db=sbtest
--tables=10
--table-size=100000
prepare
when: setup_type != "gr" and setup_type != "replication"
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Prepare data for sysbench inside of first MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_1"
command: >
sysbench /usr/share/sysbench/oltp_read_write.lua
--mysql-host=127.0.0.1
--mysql-port=3306
--mysql-user=sbtest
--mysql-password=password
--mysql-db=sbtest
--tables=10
--table-size=100000
prepare
when: setup_type == "gr" or setup_type == "replication"

- name: Run load for sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: >
sysbench /usr/share/sysbench/oltp_read_write.lua
--mysql-host=127.0.0.1
--mysql-port=3306
--mysql-user=sbtest
--mysql-password=password
--mysql-db=sbtest
--tables=10
--table-size=100000
--threads=16
--time=60
run
loop: "{{ range(1, nodes_count | int + 1) | list }}"
16 changes: 16 additions & 0 deletions pmm_qa/mysql/setup_mysql.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os
from scripts.get_env_value import get_value
from scripts.run_ansible_playbook import run_ansible_playbook

def setup_mysql_docker(db_type, container_name, db_config=None, args=None):
env_vars = {
'SETUP_TYPE': get_value('SETUP_TYPE', db_type, args, db_config).lower(),
'MS_VERSION': get_value('MS_VERSION', db_type, args, db_config),
'PMM_SERVER_IP': args.pmm_server_ip or container_name or '127.0.0.1',
'CLIENT_VERSION': get_value('CLIENT_VERSION', db_type, args, db_config),
'QUERY_SOURCE': get_value('QUERY_SOURCE', db_type, args, db_config),
'ADMIN_PASSWORD': os.getenv('ADMIN_PASSWORD') or args.pmm_server_password or 'admin',
'NODES_COUNT': get_value('NODES_COUNT', db_type, args, db_config),
}

run_ansible_playbook('mysql/mysql_setup.yml', env_vars, args)
Loading
Loading