{"id":587,"date":"2018-10-17T19:39:17","date_gmt":"2018-10-17T19:39:17","guid":{"rendered":"https:\/\/www.appservgrid.com\/paw92\/?p=587"},"modified":"2018-10-18T14:13:11","modified_gmt":"2018-10-18T14:13:11","slug":"configure-mysql-replication-with-puppet-lisenet-com-linux-security","status":"publish","type":"post","link":"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/17\/configure-mysql-replication-with-puppet-lisenet-com-linux-security\/","title":{"rendered":"Configure MySQL Replication with Puppet | Lisenet.com :: Linux | Security"},"content":{"rendered":"<p>We\u2019re going to use Puppet to install MySQL and configure Master\/Master replication.<\/p>\n<p>This article is part of the <a href=\"https:\/\/www.lisenet.com\/2018\/homelab-project-with-kvm-katello-and-puppet\/\" target=\"_blank\" rel=\"noopener\">Homelab Project with KVM, Katello and Puppet<\/a> series.<\/p>\n<h2>Homelab<\/h2>\n<p>We have two CentOS 7 servers installed which we want to configure as follows:<\/p>\n<p>db1.hl.local (10.11.1.17) \u2013 will be configured as a MySQL master<br \/>\ndb2.hl.local (10.11.1.18) \u2013 will be configured as a MySQL master<\/p>\n<p>SELinux set to enforcing mode.<\/p>\n<p>See the image below to identify the homelab part this article applies to.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.lisenet.com\/wp-content\/uploads\/2018\/04\/lisenet-homelab-diagram_mysql.png\" alt=\"\" width=\"1200\" height=\"793\" \/><\/p>\n<h2>Configuration with Puppet<\/h2>\n<p>Puppet master runs on the <a href=\"https:\/\/www.lisenet.com\/2016\/install-katello-on-centos-7\/\" target=\"_blank\" rel=\"noopener\">Katello<\/a> server.<\/p>\n<h3>Puppet Modules<\/h3>\n<p>We use <a href=\"https:\/\/forge.puppet.com\/puppetlabs\/mysql\" target=\"_blank\" rel=\"noopener\">puppetlabs-mysql<\/a> Puppet module to configure the server.<\/p>\n<p>Please see the module documentation for features supported and configuration options available.<\/p>\n<h3>Katello Repositories<\/h3>\n<p>MySQL repository is provided by Katello (we configured them <a href=\"https:\/\/www.lisenet.com\/2018\/katello-create-products-repositories-content-views-lifecycle-environments-activation-keys\/\" target=\"_blank\" rel=\"noopener\">here<\/a>).<\/p>\n<h3>Configure Firewall<\/h3>\n<p>It is essential to ensure that MySQL servers can talk to each other. The following needs applying to both MySQL masters:<\/p>\n<p>firewall { &#8216;007 allow MySQL&#8217;:<br \/>\ndport =&gt; [3306],<br \/>\nsource =&gt; &#8216;10.11.1.0\/24&#8217;,<br \/>\nproto =&gt; tcp,<br \/>\naction =&gt; accept,<br \/>\n}<\/p>\n<p>This will also allow Apache connections to the database.<\/p>\n<h3>Configure MySQL Master on db1.hl.local<\/h3>\n<p>Nothing groundbreaking here really, but note the auto-increment-offset. This is to help prevent the situation where two queries insert data at the same time in the same database and the same table on both servers db1 and db2, and different entries end up with the same id.<\/p>\n<p>class { &#8216;mysql::server&#8217;:<br \/>\npackage_name =&gt; &#8216;mysql-community-server&#8217;,<br \/>\nservice_name =&gt; &#8216;mysqld&#8217;,<br \/>\nroot_password =&gt; &#8216;PleaseChangeMe&#8217;,<br \/>\ncreate_root_my_cnf =&gt; true,<br \/>\nmanage_config_file =&gt; true,<br \/>\nconfig_file =&gt; &#8216;\/etc\/my.cnf&#8217;,<br \/>\npurge_conf_dir =&gt; true,<br \/>\nrestart =&gt; true,<br \/>\noverride_options =&gt; {<br \/>\nmysqld =&gt; {<br \/>\nbind-address =&gt; &#8216;0.0.0.0&#8217;,<br \/>\ndatadir =&gt; &#8216;\/var\/lib\/mysql&#8217;,<br \/>\nlog-error =&gt; &#8216;\/var\/log\/mysqld.log&#8217;,<br \/>\npid-file =&gt; &#8216;\/var\/run\/mysqld\/mysqld.pid&#8217;,<br \/>\nwait_timeout =&gt; &#8216;600&#8217;,<br \/>\ninteractive_timeout =&gt; &#8216;600&#8217;,<br \/>\nserver-id =&gt; &#8216;1&#8217;,<br \/>\nlog-bin =&gt; &#8216;mysql-bin&#8217;,<br \/>\nrelay-log =&gt; &#8216;mysql-relay-log&#8217;,<br \/>\nauto-increment-offset =&gt; &#8216;1&#8217;,<br \/>\nauto-increment-increment =&gt; &#8216;2&#8217;,<br \/>\n},<br \/>\nmysqld_safe =&gt; {<br \/>\nlog-error =&gt; &#8216;\/var\/log\/mysqld.log&#8217;,<br \/>\n},<br \/>\n},<br \/>\nremove_default_accounts =&gt; true,<br \/>\n}-&gt;<br \/>\n<em>## MySQL admin user who can connect remotely<\/em><br \/>\nmysql_user { &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%&#8217;:<br \/>\nensure =&gt; &#8216;present&#8217;,<br \/>\npassword_hash =&gt; mysql_password(&#8216;PleaseChangeMe&#8217;),<br \/>\n}-&gt;<br \/>\nmysql_grant { &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%\/*.*&#8217;:<br \/>\nensure =&gt; &#8216;present&#8217;,<br \/>\noptions =&gt; [&#8216;GRANT&#8217;],<br \/>\nprivileges =&gt; [&#8216;ALL&#8217;],<br \/>\ntable =&gt; &#8216;*.*&#8217;,<br \/>\nuser =&gt; &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%&#8217;,<br \/>\n}-&gt;<br \/>\n<em>## MySQL user for replication<\/em><br \/>\nmysql_user { &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%&#8217;:<br \/>\nensure =&gt; &#8216;present&#8217;,<br \/>\npassword_hash =&gt; mysql_password(&#8216;PleaseChangeMe&#8217;),<br \/>\n}-&gt;<br \/>\nmysql_grant { &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%\/*.*&#8217;:<br \/>\nensure =&gt; &#8216;present&#8217;,<br \/>\nprivileges =&gt; [&#8216;REPLICATION SLAVE&#8217;],<br \/>\ntable =&gt; &#8216;*.*&#8217;,<br \/>\nuser =&gt; &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%&#8217;,<br \/>\n}<\/p>\n<h3>Configure MySQL Master on db2.hl.local<\/h3>\n<p>Configuration of the second server is almost identical to the first one with two exceptions: server-id and auto-increment-offset.<\/p>\n<p>class { &#8216;mysql::server&#8217;:<br \/>\npackage_name =&gt; &#8216;mysql-community-server&#8217;,<br \/>\nservice_name =&gt; &#8216;mysqld&#8217;,<br \/>\nroot_password =&gt; &#8216;PleaseChangeMe&#8217;,<br \/>\ncreate_root_my_cnf =&gt; true,<br \/>\nmanage_config_file =&gt; true,<br \/>\nconfig_file =&gt; &#8216;\/etc\/my.cnf&#8217;,<br \/>\npurge_conf_dir =&gt; true,<br \/>\nrestart =&gt; true,<br \/>\noverride_options =&gt; {<br \/>\nmysqld =&gt; {<br \/>\nbind-address =&gt; &#8216;0.0.0.0&#8217;,<br \/>\ndatadir =&gt; &#8216;\/var\/lib\/mysql&#8217;,<br \/>\nlog-error =&gt; &#8216;\/var\/log\/mysqld.log&#8217;,<br \/>\npid-file =&gt; &#8216;\/var\/run\/mysqld\/mysqld.pid&#8217;,<br \/>\nwait_timeout =&gt; &#8216;600&#8217;,<br \/>\ninteractive_timeout =&gt; &#8216;600&#8217;,<br \/>\nserver-id =&gt; &#8216;2&#8217;,<br \/>\nlog-bin =&gt; &#8216;mysql-bin&#8217;,<br \/>\nrelay-log =&gt; &#8216;mysql-relay-log&#8217;,<br \/>\nauto-increment-offset =&gt; &#8216;2&#8217;,<br \/>\nauto-increment-increment =&gt; &#8216;2&#8217;,<br \/>\n},<br \/>\nmysqld_safe =&gt; {<br \/>\nlog-error =&gt; &#8216;\/var\/log\/mysqld.log&#8217;,<br \/>\n},<br \/>\n},<br \/>\nremove_default_accounts =&gt; true,<br \/>\n}-&gt;<br \/>\n<em>## MySQL admin user who can connect remotely<\/em><br \/>\nmysql_user { &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%&#8217;:<br \/>\nensure =&gt; &#8216;present&#8217;,<br \/>\npassword_hash =&gt; mysql_password(&#8216;PleaseChangeMe&#8217;),<br \/>\n}-&gt;<br \/>\nmysql_grant { &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%\/*.*&#8217;:<br \/>\nensure =&gt; &#8216;present&#8217;,<br \/>\noptions =&gt; [&#8216;GRANT&#8217;],<br \/>\nprivileges =&gt; [&#8216;ALL&#8217;],<br \/>\ntable =&gt; &#8216;*.*&#8217;,<br \/>\nuser =&gt; &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%&#8217;,<br \/>\n}-&gt;<br \/>\n<em>## MySQL user for replication<\/em><br \/>\nmysql_user { &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%&#8217;:<br \/>\nensure =&gt; &#8216;present&#8217;,<br \/>\npassword_hash =&gt; mysql_password(&#8216;PleaseChangeMe&#8217;),<br \/>\n}-&gt;<br \/>\nmysql_grant { &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%\/*.*&#8217;:<br \/>\nensure =&gt; &#8216;present&#8217;,<br \/>\nprivileges =&gt; [&#8216;REPLICATION SLAVE&#8217;],<br \/>\ntable =&gt; &#8216;*.*&#8217;,<br \/>\nuser =&gt; &#8216;<a href=\"\/cdn-cgi\/l\/email-protection\">[email protected]<\/a>%&#8217;,<br \/>\n}<\/p>\n<h3>Configure Master\/Master Replication<\/h3>\n<p>The easy part is complete, and we should have our MySQL nodes provisioned at this stage.<\/p>\n<p>We don\u2019t have any databases created yet, therefore at this point there isn\u2019t much we want to sync between the two servers.<\/p>\n<p>Let us go ahead and put the steps required to configure MySQL replication manually into a Bash script start_mysql_repl.sh. Note that the script is a quick and dirty way of getting MySQL replication working, but it\u2019s not the right approach.<\/p>\n<p>Ideally we should use a Puppet template with parameters, so that we can provide values for them by passing a parameter hash to a function and wouldn\u2019t have to hardcode hostnames, usernames etc.<\/p>\n<p>#!\/bin\/bash<br \/>\n#<br \/>\n# Author: Tomas at www.lisenet.com<br \/>\n# Configure MySQL Replication with Puppet<br \/>\n#<br \/>\n# Variables below must match with the ones<br \/>\n# defined in the Puppet manifest<br \/>\n#<br \/>\nmaster1_host=&#8221;db1.hl.local&#8221;;<br \/>\nmaster2_host=&#8221;db2.hl.local&#8221;;<br \/>\nrepl_user=&#8221;dbrepl&#8221;;<br \/>\nrepl_pass=&#8221;PleaseChangeMe&#8221;;<br \/>\ndb_user=&#8221;dbadmin&#8221;;<br \/>\ndb_pass=&#8221;PleaseChangeMe&#8221;;<br \/>\nmaster1_status=&#8221;\/tmp\/master1.status&#8221;;<br \/>\nmaster2_status=&#8221;\/tmp\/master2.status&#8221;;<\/p>\n<p>if ! [ -f &#8220;\/root\/.replication1.done&#8221; ];then<br \/>\nmysql -h&#8221;$master1_host&#8221; -u&#8221;$db_user&#8221; -p&#8221;$db_pass&#8221; -ANe &#8220;SHOW MASTER STATUS;&#8221;|awk &#8221; &gt;&#8221;$master1_status&#8221; &amp;&amp;<br \/>\nlog_file=$(cut -d&#8221; &#8221; -f1 &#8220;$master1_status&#8221;) &amp;&amp;<br \/>\nlog_pos=$(cut -d&#8221; &#8221; -f2 &#8220;$master1_status&#8221;) &amp;&amp;<br \/>\nmysql -h&#8221;$master2_host&#8221; -u&#8221;$db_user&#8221; -p&#8221;$db_pass&#8221; &#8220;$master2_status&#8221; &amp;&amp;<br \/>\nlog_file=$(cut -d&#8221; &#8221; -f1 &#8220;$master2_status&#8221;) &amp;&amp;<br \/>\nlog_pos=$(cut -d&#8221; &#8221; -f2 &#8220;$master2_status&#8221;) &amp;&amp;<br \/>\nmysql -h&#8221;$master1_host&#8221; -u&#8221;$db_user&#8221; -p&#8221;$db_pass&#8221;<\/p>\n<p>Note: there are no spaces between in front of EOSQL. WordPress does funny things with formatting sometimes.<\/p>\n<p>The script configures master host db1.hl.local as a slave for master host db2.hl.local.<\/p>\n<p>The script also configures master host db2.hl.local as a slave for master host db1.hl.local.<\/p>\n<p>Apply the following Puppet configuration to the server db1.hl.local (it must not be applied to the second server).<\/p>\n<p>Note how we deploy the script, configure replication and then create a database. The name of the database is &#8220;blog&#8221;, mostly because of the fact that we&#8217;ll be using it for WordPress.<\/p>\n<p>file { &#8216;\/root\/start_mysql_repl.sh&#8217;:<br \/>\nensure =&gt; &#8216;file&#8217;,<br \/>\nsource =&gt; &#8216;puppet:\/\/\/homelab_files\/start_mysql_repl.sh&#8217;,<br \/>\nowner =&gt; &#8216;0&#8217;,<br \/>\ngroup =&gt; &#8216;0&#8217;,<br \/>\nmode =&gt; &#8216;0700&#8217;,<br \/>\nnotify =&gt; Exec[&#8216;configure_replication&#8217;],<br \/>\n}<br \/>\nexec { &#8216;configure_replication&#8217;:<br \/>\ncommand =&gt; &#8216;\/root\/start_mysql_repl.sh&#8217;,<br \/>\npath =&gt; &#8216;\/usr\/bin:\/usr\/sbin:\/bin:\/sbin&#8217;,<br \/>\nprovider =&gt; shell,<br \/>\nunless =&gt; [&#8216;test -f \/root\/.replication1.done&#8217;, &#8216;test -f \/root\/.replication2.done&#8217;],<br \/>\nnotify =&gt; Exec[&#8216;create_database&#8217;],<br \/>\n}<br \/>\n<em>## We want to create the database after<br \/>\n## the replication has been established<\/em><br \/>\nexec { &#8216;create_database&#8217;:<br \/>\ncommand =&gt; &#8216;mysql &#8211;defaults-file=\/root\/.my.cnf -e &#8220;DROP DATABASE IF EXISTS blog; CREATE DATABASE blog; GRANT ALL PRIVILEGES ON blog.* TO &#8216;dbuser1&#8217;@&#8217;10.11.1.%&#8217; IDENTIFIED BY &#8216;PleaseChangeMe&#8217;; FLUSH PRIVILEGES;&#8221;&#8216;,<br \/>\npath =&gt; &#8216;\/usr\/bin:\/usr\/sbin:\/bin:\/sbin&#8217;,<br \/>\nprovider =&gt; shell,<br \/>\nrefreshonly =&gt; true,<br \/>\nnotify =&gt; Exec[&#8216;import_database&#8217;],<br \/>\n}<br \/>\n<em>## We want to import the database from a dump file<\/em><br \/>\nexec { &#8216;import_database&#8217;:<br \/>\ncommand =&gt; &#8216;mysql &#8211;defaults-file=\/root\/.my.cnf blog &#8216;\/usr\/bin:\/usr\/sbin:\/bin:\/sbin&#8217;,<br \/>\nprovider =&gt; shell,<br \/>\nonlyif =&gt; [&#8216;test -f \/root\/blog.sql&#8217;],<br \/>\nrefreshonly =&gt; true,<br \/>\n}<br \/>\nfile { &#8216;\/root\/blog.sql&#8217;:<br \/>\nensure =&gt; file,<br \/>\nsource =&gt; &#8216;puppet:\/\/\/homelab_files\/blog.sql&#8217;,<br \/>\nowner =&gt; &#8216;0&#8217;,<br \/>\ngroup =&gt; &#8216;0&#8217;,<br \/>\nmode =&gt; &#8216;0600&#8217;,<br \/>\n}<\/p>\n<p>The database import part restores the content of our WordPress database. Because the import is performed after the replication has been established, the database is available on both MySQL masters.<\/p>\n<p><a href=\"https:\/\/www.lisenet.com\/2018\/configure-mysql-replication-with-puppet\/\" target=\"_blank\" rel=\"noopener\">Source<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We\u2019re going to use Puppet to install MySQL and configure Master\/Master replication. This article is part of the Homelab Project with KVM, Katello and Puppet series. Homelab We have two CentOS 7 servers installed which we want to configure as follows: db1.hl.local (10.11.1.17) \u2013 will be configured as a MySQL master db2.hl.local (10.11.1.18) \u2013 will &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/17\/configure-mysql-replication-with-puppet-lisenet-com-linux-security\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Configure MySQL Replication with Puppet | Lisenet.com :: Linux | Security&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-587","post","type-post","status-publish","format-standard","hentry","category-linux"],"_links":{"self":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/587","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/comments?post=587"}],"version-history":[{"count":1,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/587\/revisions"}],"predecessor-version":[{"id":712,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/587\/revisions\/712"}],"wp:attachment":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/media?parent=587"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/categories?post=587"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/tags?post=587"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}