{"id":1310,"date":"2024-02-24T23:38:20","date_gmt":"2024-02-24T23:38:20","guid":{"rendered":"https:\/\/haxed.me.uk\/?p=1310"},"modified":"2024-02-24T23:41:07","modified_gmt":"2024-02-24T23:41:07","slug":"creating-a-master-slave-mysql-replication-virtual-machines","status":"publish","type":"post","link":"https:\/\/haxed.me.uk\/index.php\/2024\/02\/24\/creating-a-master-slave-mysql-replication-virtual-machines\/","title":{"rendered":"Creating a Master-Slave MySQL Replication Virtual Machines"},"content":{"rendered":"\n<p>I have previously described how to create a galera cluster using a created libvirt lab. I now describe how to create a master-slave mysql replication lab of the same without utilising Galera and instead using the inbuilt mysql binary-log master and a slave replica synchronising with it&#8217;s own user to the master. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><br>Creating the Master and Slave VM&#8217;s<\/h2>\n\n\n\n<p>As before we create a pair of oneliners, and user-data and meta-data for cloud init to create the two nodes with our provided .ssh\/id_rsa.pub key. In this case we only need to recreate the meta-data for the new hostnames and VM instance name. The cloud-init.yaml we created in the previous article is still relevant since that only contains our SSH public key, so we will reuse it.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;root@localhost cloudinit]# cat mysql-master.yaml\ninstance-id: mysql-master\nlocal-hostname: mysql-master\n\n&#x5B;root@localhost cloudinit]# cat mysql-slave.yaml\ninstance-id: mysql-slave\nlocal-hostname: mysql-slave\n\n&#x5B;root@localhost cloudinit]# cat cloud-init.yaml\n#cloud-config\n\nusers:\n  - name: adam\n    ssh_authorized_keys:\n      - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDB1D9CP4jqWxKw4ug+lR2zol7W6oJZ7wuliMt8aqlYekUBk7Pi7apQakc7rqjYD+b4iUDig0\/4Zk4u6DC8WPgVr6o60fV7sdpoj0GBuxL+voGE0YV84zmorHoM8TCfLeMN3AdM0EMcT2NI8V\/dmZ7uILYLYaXB+RRLv1QoMiL6zLGhLOfhdVKdvmbNqNcrvAEonnzQCVhFjRied2CfhnuH9tNXzGT5Y8wz0E9I8gQQp6GCyU7HnCHW8CLWpymZIrt2y7\/Bi4XlKAbvaUFZJ9XLNsAK3gBC\/VygIVQkWp9o3Y+KOmOsmsS51xJsigfDI0UMRdehdNEN+6vm7Eft9QZYHOg1xoTyJkgiFs9yCRFSRuXvFSsFLXUq5TFLv73qquKE6e\/STORKobF2V7LaOuvbw1BIt2zo4v4c4toyaB5hshojO7bpORzhH8K43vEs0VW2ou9Zo8L3DwmZv6qFAy88BDCAIHoElgc3fmddlZJfCvcN4ZWDuISEP\/j2oVuDT40= adam@localhost.localdomain\n    sudo: &#x5B;&quot;ALL=(ALL) NOPASSWD:ALL&quot;]\n    groups: sudo\n    shell: \/bin\/bash\n\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Creating a small script to build the Mariadb(mysql) Master-Slave Replica cluster<\/h2>\n\n\n\n<p>We create the script as before to build the cluster virtual machines with cloud-init.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;root@localhost cloudinit]# cat create-mysql-master-slave.sh\nvirt-install --name mysql-master --memory 2048 --vcpus 4 --disk=size=10,backing_store=\/var\/lib\/libvirt\/images\/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2 --cloud-init user-data=.\/cloud-init.yaml,meta-data=.\/mysql-master.yaml,disable=on --network bridge=virbr0 --osinfo=centos-stream9 --noautoconsole\n\n\nvirt-install --name mysql-slave --memory 2048 --vcpus 4 --disk=size=10,backing_store=\/var\/lib\/libvirt\/images\/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2 --cloud-init user-data=.\/cloud-init.yaml,meta-data=.\/mysql-slave.yaml,disable=on --network bridge=virbr0 --osinfo=centos-stream9 --noautoconsole\n\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Preparing the Master and Slave VM<\/h2>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;root@localhost cloudinit]# virsh net-dhcp-leases default | grep mysql\n 2024-02-24 23:54:39   52:54:00:8c:2c:94   ipv4       192.168.122.132\/24   mysql-master   01:52:54:00:8c:2c:94\n 2024-02-24 23:52:54   52:54:00:c8:e7:9f   ipv4       192.168.122.117\/24   mysql-slave    01:52:54:00:c8:e7:9f\n\n<\/pre><\/div>\n\n\n<p>Above we obtain the ip address of the the two nodes with virsh net-dhcp-leases, and a grep for our name tag used in the creation of the VM&#8217;s for easy to view output. <br><br>We then login and install the mariadb-server package, since we&#8217;re at it lets do it properly and install firewalld too and add a firewall rule for mysql port [3306]. We&#8217;ll also install vim, because its awesome fast for editing config files and isn&#8217;t present in our minimal base image. We also make sure firewalld and mariadb start on boot.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n# Login to Master to install packages\nssh adam@192.168.122.132 -i ~adam\/.ssh\/id_rsa \n\nyum install firewalld mariadb-server vim\n&#x5B;adam@mysql-master ~]$ sudo systemctl start firewalld\n&#x5B;adam@mysql-master ~]$ sudo systemctl enable firewalld\n&#x5B;adam@mysql-master ~]$ sudo systemctl enable mariadb\n&#x5B;adam@mysql-master ~]$ sudo mysql_secure_installation\n&#x5B;adam@mysql-master ~]$ sudo firewall-cmd --add-service=mysql --permanent\n&#x5B;adam@mysql-master ~]$ sudo systemctl restart firewalld\n\n# Login to Slave and perform the same\n\n&#x5B;adam@mysql-slave ~]$ sudo yum install firewalld mariadb-server vim\n&#x5B;adam@mysql-slave ~]$ sudo systemctl start firewalld\n&#x5B;adam@mysql-slave ~]$ sudo systemctl enable firewalld\n&#x5B;adam@mysql-slave ~]$ sudo systemctl enable mariadb\n&#x5B;adam@mysql-slave ~]$ sudo mysql_secure_installation\n&#x5B;adam@mysql-slave ~]$ sudo firewall-cmd --add-service=mysql --permanent\n&#x5B;adam@mysql-slave ~]$ sudo systemctl restart firewalld\n\n# For good measure we check firewall-cmd open ports were added as the services we need\n\n&#x5B;adam@mysql-slave ~]$ sudo firewall-cmd --list-all\npublic (active)\n  target: default\n  icmp-block-inversion: no\n  interfaces: eth0\n  sources:\n  services: cockpit dhcpv6-client mysql ssh\n -- truncated output\n\n&#x5B;adam@mysql-master~]$ sudo firewall-cmd --list-all\npublic (active)\n  target: default\n  icmp-block-inversion: no\n  interfaces: eth0\n  sources:\n  services: cockpit dhcpv6-client mysql ssh\n -- truncated output\n\n<\/pre><\/div>\n\n\n<p>We proceed with the masters configuration by giving it id=1 and enabling bin logs for replication.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;adam@mysql-master ~]$ cat \/etc\/my.cnf.d\/mariadb-server.cnf\n&#x5B;mysqld]\nserver-id=1\nlog-bin=mysql-bin\n\n<\/pre><\/div>\n\n\n<p>Make sure you add these in the correct section. Note some distributions still use \/etc\/my.cnf as opposed to \/etc\/my.cnf.d\/ , such as Ubuntu. It&#8217;s clear which one you have since there will be a [mysqld] section. Include the two lines we need to add in there. Once done restart mariadb on the master.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;adam@mysql-master ~]$ sudo systemctl restart mariadb\n<\/pre><\/div>\n\n\n<p>So far so good. Let&#8217;s create a user on the master for the slave mysql VM to use for replicating (copying) the data from master node. After creating the new account we flush privileges table for good measure and put the master in a read-only mode to prevent changes for the next step &#8216;bin file location&#8217; and &#8216;bin file position&#8217;.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;adam@mysql-master ~]$ sudo mysql -u root\n\nMariaDB &#x5B;(none)]&gt; grant replication slave on *.* to 'replication'@'192.168.122.117' IDENTIFIED BY 'makemereallysecureplease'\n\nMariaDB &#x5B;(none)]&gt; flush privileges;\nMariaDB &#x5B;(none)]&gt; flush tables with read lock;\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">A word on Secure Passwords if binding in a publicnet<\/h2>\n\n\n\n<p>This is only a test lab so it is safe for us to use a low security password. I recommend you use pwgen or similar to create a secure password in a production environment though, especially if you are binding to 0.0.0.0 and are in an open public net. <br><br>If you install pwgen install it, by first installing epel-release repo and then pwgen.<\/p>\n\n\n\n<p>Most people will have a private network backend that sits behind a load balancer or master sql server that is only accessible to the service network. Still it&#8217;s better in a non lab environment to always get into the habit of using a secure password; notice in my use case I only give access via explicit static ipv4 of the VM in question to prevent unauthorised access to the db. [this is really much more important in a publicnet setting where there is no option for rich rules with firewalld etc]\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nsudo yum install epel-release\nsudo yum install pwgen\n\n$ pwgen --secure 12 -n 1\n3L6km4alWQLb\n\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Preparing the Slave VM <\/h2>\n\n\n\n<p>Pretty simple, the only important thing is to set the id.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;mysqld]\nserver-id = 2\n\n<\/pre><\/div>\n\n\n<p>Now we&#8217;re ready to tell the Slave where the master is. First we need to login to the master sql server to get some important information for the slave we don&#8217;t know yet know.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;adam@mysql-master ~]$ sudo mysql -u root\n\nMariaDB &#x5B;(none)]&gt; show master status;\n+------------------+----------+--------------+------------------+\n| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |\n+------------------+----------+--------------+------------------+\n| mysql-bin.000001 |      328 |              |                  |\n+------------------+----------+--------------+------------------+\n1 row in set (0.001 sec)\n\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;adam@mysql-slave ~]$ sudo mysql -u root\nMariaDB &#x5B;(none)]&gt; change master to master_host='192.168.122.132', master_user='replication', master_password='test', master_log_file='mysql-bin.000001', master_log_pos=328;\n\n<\/pre><\/div>\n\n\n<p>In the legacy mysql master-slave replica&#8217;s we need to know 3 things. The <strong>credentials <\/strong>for the slave login set on the master (in this case you can see this is set above as the user replication and the password, and also the <strong>name <\/strong>and position of the <strong>master log file<\/strong>,  this is critical for the slave node to know &#8216;where to sync from&#8217;, especially if there is major differences in the two databases. Also, its important to understand later on if you add additional slaves or one of the slaves gets corrupted and needs to be manually resynced with the master.<\/p>\n\n\n\n<p>Checking that the Slave is synching with the replica user using &#8216;<strong>show slave status<\/strong>&#8216;. We use \\G for clearer output.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nMariaDB &#x5B;(none)]&gt; show slave status\\G;\n*************************** 1. row ***************************\n                Slave_IO_State: Waiting for master to send event\n                   Master_Host: 192.168.122.132\n                   Master_User: replication\n                   Master_Port: 3306\n                 Connect_Retry: 60\n               Master_Log_File: mysql-bin.000003\n           Read_Master_Log_Pos: 342\n                Relay_Log_File: mariadb-relay-bin.000006\n                 Relay_Log_Pos: 641\n         Relay_Master_Log_File: mysql-bin.000003\n              Slave_IO_Running: Yes\n             Slave_SQL_Running: Yes\n          Replicate_Rewrite_DB:\n               Replicate_Do_DB:\n           Replicate_Ignore_DB:\n            Replicate_Do_Table:\n        Replicate_Ignore_Table:\n       Replicate_Wild_Do_Table:\n   Replicate_Wild_Ignore_Table:\n                    Last_Errno: 0\n                    Last_Error:\n                  Skip_Counter: 0\n           Exec_Master_Log_Pos: 342\n               Relay_Log_Space: 1251\n               Until_Condition: None\n                Until_Log_File:\n                 Until_Log_Pos: 0\n            Master_SSL_Allowed: No\n            Master_SSL_CA_File:\n            Master_SSL_CA_Path:\n               Master_SSL_Cert:\n             Master_SSL_Cipher:\n                Master_SSL_Key:\n         Seconds_Behind_Master: 0\n Master_SSL_Verify_Server_Cert: No\n                 Last_IO_Errno: 0\n                 Last_IO_Error:\n                Last_SQL_Errno: 0\n                Last_SQL_Error:\n   Replicate_Ignore_Server_Ids:\n              Master_Server_Id: 1\n                Master_SSL_Crl:\n            Master_SSL_Crlpath:\n                    Using_Gtid: No\n                   Gtid_IO_Pos:\n       Replicate_Do_Domain_Ids:\n   Replicate_Ignore_Domain_Ids:\n                 Parallel_Mode: optimistic\n                     SQL_Delay: 0\n           SQL_Remaining_Delay: NULL\n       Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates\n              Slave_DDL_Groups: 1\nSlave_Non_Transactional_Groups: 0\n    Slave_Transactional_Groups: 0\n1 row in set (0.000 sec)\n\nERROR: No query specified\n\n<\/pre><\/div>\n\n\n<p>As we can see the mysql-slave is correctly configured. Once this is done we can instruct the master to continue processing queries as normal, removing the read lock on the tables;<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n&#x5B;adam@mysql-master ~]$ sudo mysql -u root\nMariaDB &#x5B;(none)]&gt; unlock tables;\nQuery OK, 0 rows affected (0.000 sec)\n\nMariaDB &#x5B;(none)]&gt; create database testsync;\nQuery OK, 1 row affected (0.001 sec)\n\n<\/pre><\/div>\n\n\n<p>You can see how important it is to properly label servers with cloud-init and virsh, vmware, openstack etc. Since performing maintenance and upgrades of any kind it can be easy to mistake the server otherwise, and can result in some pretty unpleasant confusion for yourself and your clients. We can quickly test that the slave is syncing with the master by creating a new database, too.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nMariaDB &#x5B;(none)]&gt; show databases;\n+--------------------+\n| Database           |\n+--------------------+\n| information_schema |\n| mysql              |\n| performance_schema |\n| sys                |\n| test               |\n| testsync           |\n+--------------------+\n6 rows in set (0.001 sec)\n\n<\/pre><\/div>\n\n\n<p>Sure enough our replica is taking output correctly from the master and replicating data. <br><br>You may, if the dataset on the master is pre-existing and very large, want to backup the mysql database on the master for good measure with mysqldump<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nmysqldump -u root \u2013all-databases &gt; all_databases_dump.sql\n<\/pre><\/div>\n\n\n<p>In our case it was two new servers so this step wasn&#8217;t necessary for the lab demonstration.<\/p>\n\n\n\n<p>Another useful command you might want to use is show processlist; which is handy for debugging issues that may arise;<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nMariaDB &#x5B;(none)]&gt; show processlist;\n+----+-------------+-----------+------+-----------+-------+--------------------------------------------------------+------------------+----------+\n| Id | User        | Host      | db   | Command   | Time  | State                                                  | Info             | Progress |\n+----+-------------+-----------+------+-----------+-------+--------------------------------------------------------+------------------+----------+\n|  8 | system user |           | NULL | Slave_IO  | 12266 | Waiting for master to send event                       | NULL             |    0.000 |\n|  9 | system user |           | NULL | Slave_SQL |   274 | Slave has read all relay log; waiting for more updates | NULL             |    0.000 |\n| 10 | root        | localhost | NULL | Query     |     0 | starting                                               | show processlist |    0.000 |\n+----+-------------+-----------+------+-----------+-------+--------------------------------------------------------+------------------+----------+\n3 rows in set (0.000 sec)\n\n<\/pre><\/div>\n\n\n<p> I hope that this is useful to someone, as there are not a lot of really good and simple documentation showing this process in a clear to understand way online. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have previously described how to create a galera cluster using a created libvirt lab. I now describe how to create a master-slave mysql replication lab of the same without utilising Galera and instead using the inbuilt mysql binary-log master &hellip; <a href=\"https:\/\/haxed.me.uk\/index.php\/2024\/02\/24\/creating-a-master-slave-mysql-replication-virtual-machines\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1310","post","type-post","status-publish","format-standard","hentry","category-uncategorised"],"_links":{"self":[{"href":"https:\/\/haxed.me.uk\/index.php\/wp-json\/wp\/v2\/posts\/1310","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/haxed.me.uk\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/haxed.me.uk\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/haxed.me.uk\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/haxed.me.uk\/index.php\/wp-json\/wp\/v2\/comments?post=1310"}],"version-history":[{"count":4,"href":"https:\/\/haxed.me.uk\/index.php\/wp-json\/wp\/v2\/posts\/1310\/revisions"}],"predecessor-version":[{"id":1317,"href":"https:\/\/haxed.me.uk\/index.php\/wp-json\/wp\/v2\/posts\/1310\/revisions\/1317"}],"wp:attachment":[{"href":"https:\/\/haxed.me.uk\/index.php\/wp-json\/wp\/v2\/media?parent=1310"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/haxed.me.uk\/index.php\/wp-json\/wp\/v2\/categories?post=1310"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/haxed.me.uk\/index.php\/wp-json\/wp\/v2\/tags?post=1310"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}