On-prem MySQL to GCP Cloud SQL MySQL - Database Migration using External Replica
Jisto Jose
Data Architect | DE | DBE | DB Consultant | DB Migration Expert | 5x Multi-cloud Certificate
This article describes how to migrate on-premises/self-managed MySQL to GCP Cloud MySQL with minimal downtime and effort.
Source and Destination:
Source - On Premises / Self-Managed MySQL
Destination - GCP CloudSQL MySQL
For ease of testing, We are assuming GCP Compute Engine VM “n1-standard-4” Machine as the On-premise.
Source MySQL Version - 5.7
Destination MySQL Version - 5.7
Cloud SQL for MySQL is a fully-managed database service that helps you set up, maintain, manage, and administer your MySQL relational databases on Google Cloud Platform. With Cloud SQL for MySQL, you can spend less time on your database operations and more time on your applications.
GCP Database Migration Service makes it easy to migrate databases from on-premises, Compute Engine, and other clouds to Cloud SQL with minimal downtime.
Alternatively, if you need more control while migrating to CloudSQL you can create CloudSQL MySQL as a native replica from an external server and replicate data. Replication between your self-managed/managed mysql server and the Cloud SQL replica continues indefinitely.
Use cases for external server configuration:
External server configuration helps you achieve the following goals:
External replication configuration includes the following instances:
Overview:
Migration Steps:
1. Create a GCE instance , install and configure MySQL 5.7(with GTID)
Below are the configuration used for setting up the GCE Instance with MySQL 5.7. You can choose MySQL5.7 with Ubuntu from GCP MarketPlace.
New MySQL 5.7 on Ubuntu 14.04 LTS deployment - Marketplace
Compute Engine VM MySQL,
purpllemysqlcompute
asia-south1-a
N1 Series
n1-standard-4 - 4 CPU, 15GB Memory.
200GB Standard Persistent Disk
default - Network & Subnetwork.
Allow port 22 from internet.
Source IP Ranges for TCP Port 22 - 0.0.0.0/0
IP Forwarding - Off
ajkumar_cldcvr_com@purpllemysqlcomputevm-vm:~$ cat /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket? ? ? ? ? = /var/run/mysqld/mysqld.sock
datadir? ? ? ? ?= /var/lib/mysql
log-error? ? ? ?= /var/log/mysql/error.log
tls_version=TLSv1.2
#bind-address=...
bind-address=0.0.0.0
log_bin=/var/log/mysql/mysql-bin.log
server_id=11111
gtid_mode=ON
enforce_gtid_consistency=ON
symbolic-links=0?
rajkumar_cldcvr_com@purpllemysqlcomputevm-vm:~$ sudo su
root@purpllemysqlcomputevm-vm:~# service mysql stop
...
MySQL Community Server 5.7.23 is stopped
root@purpllemysqlcomputevm-vm:~# service mysql start
..
MySQL Community Server 5.7.23 is started
root@purpllemysqlcomputevm-vm:~# service mysql status
MySQL Community Server 5.7.23 is running
root@purpllemysqlcomputevm-vm:~#-
Please make sure the mysql user has R/W permissions on /var/run/mysqld , /var/lib/mysql , /var/log/mysql. You may also use root user. For any errors , please check and fix /var/log/mysql/error.log.
Validate GTID config on source MySQL :
2. Create a dummy database & table and insert few records
Create database sample
Use sample;
CREATE TABLE employees (?
emp_no INT NOT NULL, -- UNSIGNED AUTO_INCREMENT??
birth_date DATE NOT NULL,
first_name VARCHAR(14) NOT NULL,
last_name VARCHAR(16) NOT NULL,
gender ENUM ('M','F') NOT NULL, -- Enumeration of either 'M' or 'F'?
hire_date DATE NOT NULL,
PRIMARY KEY (emp_no) -- Index built automatically on primary-key column
);
insert into employees values (1,now(),'aaaa','bbbb','M',now());
insert into employees values (2,now(),'aaaa','bbbb','M',now());
insert into employees values (3,now(),'aaaa','bbbb','M',now());
insert into employees values (4,now(),'aaaa','bbbb','M',now());
insert into employees values (5,now(),'aaaa','bbbb','M',now());
insert into employees values (6,now(),'aaaa','bbbb','M',now());
3. Prerequisites on source DB — Enable binary logging and log retention , create replication user & grant privileges
Create replication user & grant privileges on the source DB:
CREATE USER 'replicationUser'@'%' IDENTIFIED BY 'Rep@123'
GRANT SELECT, SHOW VIEW ON . TO 'replicationUser'@'%';
FLUSH PRIVILEGES;
GRANT REPLICATION SLAVE, EXECUTE ON .
TO 'replicationUser'@'%';
grant process ON . TO 'replicationUser'@'%';
grant FLUSH_TABLES ON . TO 'replicationUser'@'%';
4. Set up a source representation instance
(base) rajkumarnarendiran@RajKumar-Narendiran migrationpoc % cat Source.jso
{
? ? ?"name": "clousql-custom-purplle",
? ? ?"region": "asia-south1",
? ? ?"databaseVersion": "MYSQL_5_7",
? ? ?"onPremisesConfiguration": {
? ? ? ?"hostPort": "35.200.254.189:3306",
? ? ? ?"username": "replicationUser",
? ? ? ?"password": "****"
? ? ? }
}
(base) rajkumarnarendiran@RajKumar-Narendiran migrationpoc %n
gcloud auth login
? ?ACCESS_TOKEN="$(gcloud auth print-access-token)"
? ?curl --header "Authorization: Bearer ${ACCESS_TOKEN}" \
? ? ? ? --header 'Content-Type: application/json' \
? ? ? ? --data @/Users/rajkumarnarendiran/Documents/Purplle/migrationpoc/Source.json \
? ? ? ? -X POST \
? ? ? ?https://sqladmin.googleapis.com/sql/v1beta4/projects/rajkumar-1682338385/instances
{
?"kind": "sql#operation",
?"targetLink": "https://sqladmin.googleapis.com/sql/v1beta4/projects/rajkumar-1682338385/instances/clousql-custom-purplle",
?"status": "DONE",
?"user": "[email protected]",
?"insertTime": "2023-04-24T16:58:46.384Z",
?"endTime": "2023-04-24T16:58:46.387Z",
?"operationType": "CREATE",
?"name": "3a51c391-5836-441f-ac89-cc0a0000002f",
?"targetId": "clousql-custom-purplle",
?"selfLink": "https://sqladmin.googleapis.com/sql/v1beta4/projects/rajkumar-1682338385/operations/3a51c391-5836-441f-ac89-cc0a0000002f",
?"targetProject": "rajkumar-1682338385"
}
(base) rajkumarnarendiran@RajKumar-Narendiran migrationpoc %
领英推荐
5. Set up a Cloud SQL replica & add users to the Cloud SQL replica
(base) rajkumarnarendiran@RajKumar-Narendiran migrationpoc % cat replica.json
{
? ? ? ?"settings": {
? ? ? ? ? ?"tier": "db-custom-4-15360",
? ? ? ? ? ?"dataDiskSizeGb": "200"
? ? ? ?},
? ? ? ?"masterInstanceName": "clousql-custom-purplle",
? ? ? ?"region": "asia-south1",
? ? ? ?"databaseVersion": "MYSQL_5_7",
? ? ? ?"name": "clousql-custom-purplle-replica"
}
(base) rajkumarnarendiran@RajKumar-Narendiran migrationpoc %
Project ID - rajkumar-1682338385
(base) rajkumarnarendiran@RajKumar-Narendiran migrationpoc % gcloud config set project rajkumar-1682338385
Updated property [core/project].
(base) rajkumarnarendiran@RajKumar-Narendiran migrationpoc %
--
gcloud auth login
? ACCESS_TOKEN="$(gcloud auth print-access-token)"
? curl --header "Authorization: Bearer ${ACCESS_TOKEN}" \
? ? ?--header 'Content-Type: application/json' \
? ? ?--data @/Users/rajkumarnarendiran/Documents/Purplle/migrationpoc/replica.json \
? ? ?-X POST \
? ? ?https://sqladmin.googleapis.com/sql/v1beta4/projects/rajkumar-1682338385/instances
{
?"kind": "sql#operation",
?"targetLink": "https://sqladmin.googleapis.com/sql/v1beta4/projects/rajkumar-1682338385/instances/clousql-custom-purplle-replica",
?"status": "PENDING",
?"user": "[email protected]",
?"insertTime": "2023-04-24T17:24:02.187Z",
?"operationType": "CREATE_REPLICA",
?"name": "63cddf43-d53f-4ac3-b48a-1a210000002f",
?"targetId": "clousql-custom-purplle-replica",
?"selfLink": "https://sqladmin.googleapis.com/sql/v1beta4/projects/rajkumar-1682338385/operations/63cddf43-d53f-4ac3-b48a-1a210000002f",
?"targetProject": "rajkumar-1682338385"
}
(base) rajkumarnarendiran@RajKumar-Narendiran migrationpoc %
Add the users with their passwords in the CloudSQL Replica Instance.
Change the authorized connection to allow the external Network IP Ranges.
6. Get the Cloud SQL replica’s outgoing IP address and Allow incoming connections on the external server
Outgoing IP address to be whitelisted = 34.93.207.168
The Cloud SQL replica needs to connect to the external server for replication to succeed. You must configure the network firewall for your external server to accept connections from the Cloud SQL replica’s outgoing IP address if the following conditions apply:
7. Promote CloudSQL Replica
Because Cloud SQL replica instances are read-only, in order to perform a custom import, you need to promote the Cloud SQL replica to a standalone instance. Once the initial data import is complete, you demote the instance back to a replica
8. Perform a custom dump and import
(base) rajkumarnarendiran@RajKumar-Narendiran dumps % mysqldump --host=35.200.254.189 --port=3306 --user=replicationUser --password=Rep@123 --databases sample --ssl-mode=DISABLED --hex-blob --no-autocommit --default-character-set=utf8mb4 --single-transaction --set-gtid-purged=on --add-drop-table > sample_dump.sql
In case the database size is big , then you can use mydumper and myloader to tune the export and import process and documented here.
Write down the GTID or binlog information of the data dump. You need this information to configure the replication with the Cloud SQL stored procedures.
(base) rajkumarnarendiran@RajKumar-Narendiran dumps % cat sample_dump.sql|grep GTID
-- GTID state at the beginning of the backup
#SET @@GLOBAL.GTID_PURGED=/!80000 '+'/ 'dc40d33b-6787-11e8-9f70-42010a800003:1-19';
(base) rajkumarnarendiran@RajKumar-Narendiran dumps %
Remove the following lines in the dump file that require super privileges. Since Cloud SQL users don’t have super privileges, these lines cause the import to fail.
For GTID-based replication: Remove the SET GTID_PURGED statement along with the session variable setting statement in the dump. For example:
ET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;
#SET @@SESSION.SQL_LOG_BIN= 0;
#SET @@GLOBAL.GTID_PURGED=/!80000 '+'/ 'dc40d33b-6787-11e8-9f70-42010a800003:1-13';
Import in the promoted CloudSQL Replica,
Import complete,
(base) rajkumarnarendiran@RajKumar-Narendiran dumps % mysql -h 34.100.152.81 -u root -p < sample_dump.sql
Enter password:
(base) rajkumarnarendiran@RajKumar-Narendiran dumps %
9. Demote the Cloud SQL instance
(base) rajkumarnarendiran@RajKumar-Narendiran migrationpoc % cat demotemaster.json
{
? ? ? ?"demoteMasterContext": {
? ?"masterInstanceName": "clousql-custom-purplle",
? ? ? ? ? ?"skipReplicationSetup": true
? ? ? ?}
}
(base) rajkumarnarendiran@RajKumar-Narendiran migrationpoc %
gcloud auth login
? ? ACCESS_TOKEN="$(gcloud auth print-access-token)"
? ? curl --header "Authorization: Bearer ${ACCESS_TOKEN}" \
? ? ? --header 'Content-Type: application/json' \
? ? ? --data @/Users/rajkumarnarendiran/Documents/Purplle/migrationpoc/demotemaster.json \
? ? ? -X POST \? ? https://sqladmin.googleapis.com/sql/v1beta4/projects/rajkumar-1682338385/instances/clousql-custom-purplle-replica/demoteMaster
curl: (3) URL using bad/illegal format or missing URL
{
?"kind": "sql#operation",
?"targetLink": "https://sqladmin.googleapis.com/sql/v1beta4/projects/rajkumar-1682338385/instances/clousql-custom-purplle-replica",
?"status": "PENDING",
?"user": "[email protected]",
?"insertTime": "2023-04-25T06:33:39.668Z",
?"operationType": "DEMOTE_MASTER",
?"name": "a45d4d03-0211-45f8-a558-7ae80000002f",
?"targetId": "clousql-custom-purplle-replica",
?"selfLink": "https://sqladmin.googleapis.com/sql/v1beta4/projects/rajkumar-1682338385/operations/a45d4d03-0211-45f8-a558-7ae80000002f",
?"targetProject": "rajkumar-1682338385"
}
(base) rajkumarnarendiran@RajKumar-Narendiran migrationpoc %
10. Start replication on the Cloud SQL instance
Log on to the replica instance. Use the mysql.resetMaster stored procedure to reset replication settings.
mysql> call mysql.resetMaster()
Query OK, 0 rows affected (0.04 sec)
mysql>;
Configure the replication,
mysql> call mysql.skipTransactionWithGtid('dc40d33b-6787-11e8-9f70-42010a800003:1-19')
Query OK, 0 rows affected (0.04 sec)
mysql>
mysql> call mysql.setupExternalSourceAutoPosition('35.200.254.189', 3306, \
? ?->? ? 'replicationUser', '***', \
? ?->? ? /* master_auto_position= */ 1,false, false); \
Query OK, 0 rows affected (0.06 sec)
mysql>
mysql> call mysql.startReplication();
Query OK, 0 rows affected (0.04 sec)
mysql>
mysql> show slave status\G
*************************** 1. row ***************************
? ? ? ? ? ? ? Slave_IO_State: Connecting to master
? ? ? ? ? ? ? ? ?Master_Host: 1.1.1.1
? ? ? ? ? ? ? ? ?Master_User: replicationUser
? ? ? ? ? ? ? ? ?Master_Port: 3306
? ? ? ? ? ? ? ?Connect_Retry: 60
? ? ? ? ? ? ?Master_Log_File:
? ? ? ? ?Read_Master_Log_Pos: 4
? ? ? ? ? ? ? Relay_Log_File: relay-log.000001
? ? ? ? ? ? ? ?Relay_Log_Pos: 4
? ? ? ?Relay_Master_Log_File:
? ? ? ? ? ? Slave_IO_Running: Connecting
? ? ? ? ? ?Slave_SQL_Running: Yes
? ? ? ? ? ? ?Replicate_Do_DB:
? ? ? ? ?Replicate_Ignore_DB:
? ? ? ? ? Replicate_Do_Table:
? ? ? Replicate_Ignore_Table:
? ? ?Replicate_Wild_Do_Table:
?Replicate_Wild_Ignore_Table: mysql.%
? ? ? ? ? ? ? ? ? Last_Errno: 0
? ? ? ? ? ? ? ? ? Last_Error:
? ? ? ? ? ? ? ? Skip_Counter: 0
? ? ? ? ?Exec_Master_Log_Pos: 0
? ? ? ? ? ? ?Relay_Log_Space: 154
? ? ? ? ? ? ?Until_Condition: None
? ? ? ? ? ? ? Until_Log_File:
? ? ? ? ? ? ? ?Until_Log_Pos: 0
? ? ? ? ? Master_SSL_Allowed: No
? ? ? ? ? Master_SSL_CA_File:
? ? ? ? ? Master_SSL_CA_Path:
? ? ? ? ? ? ?Master_SSL_Cert:
? ? ? ? ? ?Master_SSL_Cipher:
? ? ? ? ? ? ? Master_SSL_Key:
? ? ? ?Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
? ? ? ? ? ? ? ?Last_IO_Errno: 0
? ? ? ? ? ? ? ?Last_IO_Error:
? ? ? ? ? ? ? Last_SQL_Errno: 0
? ? ? ? ? ? ? Last_SQL_Error:
?Replicate_Ignore_Server_Ids:
? ? ? ? ? ? Master_Server_Id: 0
? ? ? ? ? ? ? ? ?Master_UUID:
? ? ? ? ? ? Master_Info_File: mysql.slave_master_info
? ? ? ? ? ? ? ? ? ?SQL_Delay: 0
? ? ? ? ?SQL_Remaining_Delay: NULL
? ? ?Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
? ? ? ? ? Master_Retry_Count: 86400
? ? ? ? ? ? ? ? ?Master_Bind:
? ? ?Last_IO_Error_Timestamp:
? ? Last_SQL_Error_Timestamp:
? ? ? ? ? ? ? Master_SSL_Crl:
? ? ? ? ? Master_SSL_Crlpath:
? ? ? ? ? Retrieved_Gtid_Set:
? ? ? ? ? ?Executed_Gtid_Set: dc40d33b-6787-11e8-9f70-42010a800003:1-19
? ? ? ? ? ? ? ?Auto_Position: 1
? ? ? ? Replicate_Rewrite_DB:
? ? ? ? ? ? ? ? Channel_Name:
? ? ? ? ? Master_TLS_Version:
1 row in set (0.03 sec)
mysql>;
11. Replication validation
Insert records on the source DB and Validate on CloudSQL Replica.
Data Validation at source,
mysql> insert into employees values (7,now(),'aaaa','bbbb','M',now())
Query OK, 1 row affected, 2 warnings (0.13 sec)
mysql>;
Data Validation at destination,
mysql> select * from employees
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+-----------+--------+------------+
|? ? ?1 | 2023-04-24 | aaaa? ? ? | bbbb? ? ?| M? ? ?| 2023-04-24 |
|? ? ?2 | 2023-04-24 | aaaa? ? ? | bbbb? ? ?| M? ? ?| 2023-04-24 |
|? ? ?3 | 2023-04-24 | aaaa? ? ? | bbbb? ? ?| M? ? ?| 2023-04-24 |
|? ? ?4 | 2023-04-24 | aaaa? ? ? | bbbb? ? ?| M? ? ?| 2023-04-24 |
|? ? ?5 | 2023-04-24 | aaaa? ? ? | bbbb? ? ?| M? ? ?| 2023-04-24 |
|? ? ?6 | 2023-04-24 | aaaa? ? ? | bbbb? ? ?| M? ? ?| 2023-04-24 |
|? ? ?7 | 2023-04-25 | aaaa? ? ? | bbbb? ? ?| M? ? ?| 2023-04-25 |
|? ? ?8 | 2023-04-25 | aaaa? ? ? | bbbb? ? ?| M? ? ?| 2023-04-25 |
+--------+------------+------------+-----------+--------+------------+
8 rows in set (0.04 sec)
mysql>;
mysql> show slave status\
*************************** 1. row ***************************
? ? ? ? ? ? ? Slave_IO_State: Waiting for master to send event
? ? ? ? ? ? ? ? ?Master_Host: 35.200.254.189
? ? ? ? ? ? ? ? ?Master_User: replicationUser
? ? ? ? ? ? ? ? ?Master_Port: 3306
? ? ? ? ? ? ? ?Connect_Retry: 60
? ? ? ? ? ? ?Master_Log_File: mysql-bin.000004
? ? ? ? ?Read_Master_Log_Pos: 1401
? ? ? ? ? ? ? Relay_Log_File: relay-log.000002
? ? ? ? ? ? ? ?Relay_Log_Pos: 1014
? ? ? ?Relay_Master_Log_File: mysql-bin.000004
? ? ? ? ? ? Slave_IO_Running: Yes
? ? ? ? ? ?Slave_SQL_Running: Yes
? ? ? ? ? ? ?Replicate_Do_DB:
? ? ? ? ?Replicate_Ignore_DB:
? ? ? ? ? Replicate_Do_Table:
? ? ? Replicate_Ignore_Table:
? ? ?Replicate_Wild_Do_Table:
?Replicate_Wild_Ignore_Table: mysql.%
? ? ? ? ? ? ? ? ? Last_Errno: 0
? ? ? ? ? ? ? ? ? Last_Error:
? ? ? ? ? ? ? ? Skip_Counter: 0
? ? ? ? ?Exec_Master_Log_Pos: 1401
? ? ? ? ? ? ?Relay_Log_Space: 1215
? ? ? ? ? ? ?Until_Condition: None
? ? ? ? ? ? ? Until_Log_File:
? ? ? ? ? ? ? ?Until_Log_Pos: 0
? ? ? ? ? Master_SSL_Allowed: No
? ? ? ? ? Master_SSL_CA_File:
? ? ? ? ? Master_SSL_CA_Path:
? ? ? ? ? ? ?Master_SSL_Cert:
? ? ? ? ? ?Master_SSL_Cipher:
? ? ? ? ? ? ? Master_SSL_Key:
? ? ? ?Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
? ? ? ? ? ? ? ?Last_IO_Errno: 0
? ? ? ? ? ? ? ?Last_IO_Error:
? ? ? ? ? ? ? Last_SQL_Errno: 0
? ? ? ? ? ? ? Last_SQL_Error:
?Replicate_Ignore_Server_Ids:
? ? ? ? ? ? Master_Server_Id: 11111
? ? ? ? ? ? ? ? ?Master_UUID: dc40d33b-6787-11e8-9f70-42010a800003
? ? ? ? ? ? Master_Info_File: mysql.slave_master_info
? ? ? ? ? ? ? ? ? ?SQL_Delay: 0
? ? ? ? ?SQL_Remaining_Delay: NULL
? ? ?Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
? ? ? ? ? Master_Retry_Count: 86400
? ? ? ? ? ? ? ? ?Master_Bind:
? ? ?Last_IO_Error_Timestamp:
? ? Last_SQL_Error_Timestamp:
? ? ? ? ? ? ? Master_SSL_Crl:
? ? ? ? ? Master_SSL_Crlpath:
? ? ? ? ? Retrieved_Gtid_Set: dc40d33b-6787-11e8-9f70-42010a800003:20-21
? ? ? ? ? ?Executed_Gtid_Set: dc40d33b-6787-11e8-9f70-42010a800003:1-21
? ? ? ? ? ? ? ?Auto_Position: 1
? ? ? ? Replicate_Rewrite_DB:
? ? ? ? ? ? ? ? Channel_Name:
? ? ? ? ? Master_TLS_Version:
1 row in set (0.03 sec)
mysql>
mysql> show master status\G
*************************** 1. row ***************************
? ? ? ? ? ? File: mysql-bin.000004
? ? ? ? Position: 1401
? ? Binlog_Do_DB:
?Binlog_Ignore_DB:
Executed_Gtid_Set: dc40d33b-6787-11e8-9f70-42010a800003:1-21
1 row in set (0.00 sec)
mysql>G
Thus, we have created the Migration Template for On-prem MySQL to CloudSQL MySQL
Thanks @Rajkumar for the content