{"id":424,"date":"2025-08-19T15:33:30","date_gmt":"2025-08-19T15:33:30","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=424"},"modified":"2025-08-19T15:33:30","modified_gmt":"2025-08-19T15:33:30","slug":"install-mysql-operator-on-kubernetes","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=424","title":{"rendered":"Setup  MySQL Operator on Kubernetes (Step by Step Guide)"},"content":{"rendered":"<p>In this blog, we will learn how to setup and operate the MySQL operator on a <a href=\"https:\/\/devopscube.com\/production-ready-kubernetes-cluster\/\" rel=\"noreferrer\">Kubernetes cluster<\/a>.<\/p>\n<p>In this blog, we will primarily look at,<\/p>\n<ol>\n<li>What the MySQL Operator is and how it works<\/li>\n<li>How to install it using <a href=\"https:\/\/devopscube.com\/create-helm-chart\/\" rel=\"noreferrer\">Helm<\/a><\/li>\n<li>How to deploy a MySQL InnoDB cluster<\/li>\n<li>How to test MySQL connectivity and replication<\/li>\n<\/ol>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-text\">By the end, you will have a working MySQL cluster on Kubernetes that is highly available and production-ready.<\/div>\n<\/div>\n<h2 id=\"what-is-mysql-operator\">What is MySQL Operator?<\/h2>\n<p>Let&#8217;s say you are managing a MySQL cluster manually on <a href=\"https:\/\/devopscube.com\/kubernetes-tutorials-beginners\/\" rel=\"noreferrer\">Kubernetes<\/a>. It can quickly become complex. <\/p>\n<p>For example, you need to provision persistent volumes, configure StatefulSets, handle secrets, and set up replication and failover mechanisms.<\/p>\n<p>MySQL Operator (developed by Oracle) simplifies all these processes. <\/p>\n<p>It automates the MySQL cluster creation using <a href=\"https:\/\/devopscube.com\/kubernetes-objects-resources\/\" rel=\"noreferrer\">Kubernetes objects<\/a> (Custom Resources, Statefulset, etc)  and manages a MySQL cluster for you.<\/p>\n<p>You can perform the following using the MySQL Operator<\/p>\n<ul>\n<li>Deploy multi-instance MySQL clusters with automatic replication<\/li>\n<li>Handle failover and routing with built-in MySQL Router<\/li>\n<li>Manage backups using custom resources.<\/li>\n<\/ul>\n<h2 id=\"how-does-the-mysql-operator-work\">How Does the MySQL Operator Work?<\/h2>\n<p>Before we move to the setup, let look at how a MySQL Operator Works.<\/p>\n<p>The following image shows the high-level setup of the MySQL Operator on Kubernetes.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/08\/image-143.png\" class=\"kg-image\" alt=\"MySQL Operator Architecture on Kubernetes\" loading=\"lazy\" width=\"2000\" height=\"2146\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/08\/image-143.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/08\/image-143.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/08\/image-143.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/08\/image-143.png 2339w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Here is how it works.<\/p>\n<ol>\n<li>The application pod sends a read\/write request to the database.<\/li>\n<li>The MySQL router pods route the traffic to the correct MySQL server pods.<\/li>\n<li>The primary MySQL pod only accepts writes to avoid conflicts.<\/li>\n<li>However, the read request will be accepted by all the pods.<\/li>\n<li>Once the data is written to the primary persistent storage, MySQL replicates it across all pods to ensure they have the same data.<\/li>\n<\/ol>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">MySQL Router<\/strong><\/b> is key component for MySQL database architectures, particularly in high-availability setups. It is a transparent proxy that sits between your application and MySQL database servers that routes connections for opmtimal performance and reliability.<\/div>\n<\/div>\n<h2 id=\"setup-prerequisites\">Setup Prerequisites<\/h2>\n<p>The following are the requirements to install the MySQL operator.<\/p>\n<ol>\n<li><a href=\"https:\/\/devopscube.com\/setup-kubernetes-cluster-kubeadm\/\" rel=\"noreferrer\">Kubernetes cluster <\/a>with admin access.<\/li>\n<li><a href=\"https:\/\/devopscube.com\/kubectl-set-context\/\" rel=\"noreferrer\">Kubectl<\/a> is installed and configured on your local machine.<\/li>\n<li><a href=\"https:\/\/devopscube.com\/install-configure-helm-kubernetes\/\" rel=\"noreferrer\">Helm<\/a> is installed on your local machine.<\/li>\n<\/ol>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">Ensure your cluster Nodes have enogh CPU and Mermory resoruces to host the MySQL cluster. Or else, you will face pod errors.<\/div>\n<\/div>\n<p>Now, let&#8217;s begin the installation.<\/p>\n<h2 id=\"installing-the-mysql-operator-via-helm\">Installing the MySQL Operator via Helm<\/h2>\n<p>We will use the official <a href=\"https:\/\/devopscube.com\/create-helm-chart\/\" rel=\"noreferrer\">Helm chart <\/a>to install the MySQL operator on the cluster.<\/p>\n<p>Add and update the Helm repo on local using the following command.<\/p>\n<pre><code class=\"language-bash\">helm repo add mysql-operator https:\/\/mysql.github.io\/mysql-operator\/\n\nhelm repo update<\/code><\/pre>\n<p>Once we update the repo to ensure we have all the available versions, we can install it.<\/p>\n<pre><code class=\"language-bash\">helm install my-mysql-operator mysql-operator\/mysql-operator \\\n   --namespace mysql-operator --create-namespace<\/code><\/pre>\n<h3 id=\"customize-the-helm-chart-optional\">Customize the Helm Chart (Optional)<\/h3>\n<p>In our deployment, we haven&#8217;t made any changes in the deployment. But for production use cases, we may have to tweak the default values. <\/p>\n<p>To do that, lets pull the chart locally using the following command.<\/p>\n<pre><code class=\"language-bash\">helm pull mysql-operator\/mysql-operator --untar<\/code><\/pre>\n<p>Here is the tree structure of the MySQL Operator Helm chart<\/p>\n<pre><code class=\"language-bash\">mysql-operator\n\u251c\u2500\u2500 Chart.yaml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 crds\n\u2502   \u2514\u2500\u2500 crd.yaml\n\u251c\u2500\u2500 templates\n\u2502   \u251c\u2500\u2500 NOTES.txt\n\u2502   \u251c\u2500\u2500 _helpers.tpl\n\u2502   \u251c\u2500\u2500 cluster_kopf_keepering.yaml\n\u2502   \u251c\u2500\u2500 cluster_role_binding_operator.yaml\n\u2502   \u251c\u2500\u2500 cluster_role_operator.yaml\n\u2502   \u251c\u2500\u2500 cluster_role_sidecar.yaml\n\u2502   \u251c\u2500\u2500 deployment.yaml\n\u2502   \u251c\u2500\u2500 service.yaml\n\u2502   \u2514\u2500\u2500 service_account_operator.yaml\n\u2514\u2500\u2500 values.yaml<\/code><\/pre>\n<p>The chart uses the following container image.<\/p>\n<pre><code class=\"language-bash\">container-registry.oracle.com\/mysql\/community-operator:9.4.0-2.2.5<\/code><\/pre>\n<p> All the customizable configurations are available in the <code>values.yaml<\/code> file.<\/p>\n<p>So if you want to customize the default settings, <\/p>\n<ul>\n<li>You can either edit the same <code>values.yaml<\/code> file<\/li>\n<li>Or a recommended approach is to create a new values file (e.g., <code>dev-values.yaml<\/code>) and copy only the parameters that you want to modify from the original file<\/li>\n<\/ul>\n<h3 id=\"validate-operator-installation\">Validate Operator Installation<\/h3>\n<p>Now, we have enough information about the Helm chart, so we can list the deployed Operator and the service using the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n mysql-operator get deploy,svc\n\nNAME                             READY   UP-TO-DATE   AVAILABLE   AGE\ndeployment.apps\/mysql-operator   1\/1     1            1           18m\n\nNAME                     TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE\nservice\/mysql-operator   ClusterIP   10.1.148.89   &lt;none&gt;        9443\/TCP   18m<\/code><\/pre>\n<p>The output ensures that the MySQL operator is up and running without any issues.<\/p>\n<p>Lets validate the Custom Resource Definitions as well.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get crds \n\ninnodbclusters.mysql.oracle.com                      2025-08-08T10:26:29Z\nmysqlbackups.mysql.oracle.com                        2025-08-08T10:26:29Z\nclusterkopfpeerings.zalando.org                      2025-08-16T05:26:43Z\nkopfpeerings.zalando.org                             2025-08-16T05:26:43Z<\/code><\/pre>\n<p>In the above output, you can see two Custom Resource Definitions of the MySQL Operator.<\/p>\n<ol>\n<li><strong>InnoDB Cluster<\/strong> &#8211; To manage the InnoDB clusters<\/li>\n<li><strong>MySQL Backups<\/strong> &#8211; To manage the backups<\/li>\n<\/ol>\n<p>Till now, we have deployed only the MySQL operator. We haven&#8217;t created any MySQL clusters yet.<\/p>\n<p>Before we move to the cluster creation step, lets understand what is a InnoDB cluster.<\/p>\n<h3 id=\"innodb-cluster\">InnoDB Cluster<\/h3>\n<p>InnoDB Cluster is MySQL&#8217;s high-availability clustering solution that lets you run multiple MySQL servers together as one system. <\/p>\n<p>It supports automatic failover, keeps the data the same across all servers, and offers tools to manage the whole cluster.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">Like innoDB, there are other solutions like Galera Cluster, <a href=\"https:\/\/devopscube.com\/setup-mysql-master-slave-replication\/\" rel=\"noreferrer\">MySQL Replication<\/a>, MySQL NDB Cluster etc.<\/div>\n<\/div>\n<h2 id=\"deploying-mysql-innodb-cluster\">Deploying MySQL InnoDB Cluster<\/h2>\n<p>Let&#8217;s deploy an InnoDB cluster.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">In this setup we assume, you have dynamic volume provisioing available in your cluster.<\/div>\n<\/div>\n<p>The following Helm command installs the MySQL cluster with a MySQL router. All the required configurations are passed using the <code>--set<\/code> flag.<\/p>\n<pre><code class=\"language-bash\">helm install devcluster mysql-operator\/mysql-innodbcluster \\\n    --set credentials.root.user='root' \\\n    --set credentials.root.password='root' \\\n    --set credentials.root.host='%' \\\n    --set serverInstances=3 \\\n    --set routerInstances=1 \\\n    --set tls.useSelfSigned=true<\/code><\/pre>\n<p>The above command deploys the router <a href=\"https:\/\/devopscube.com\/kubernetes-deployment-tutorial\/\" rel=\"noreferrer\">deployment<\/a> and MySQL cluster Statefulset with PVC&#8217;s attached to it.<\/p>\n<p>Lets validate the deployments.<\/p>\n<pre><code class=\"language-bash\">$ k get deploy,statefulset,pvc\n\nNAME                                READY   UP-TO-DATE   AVAILABLE   AGE\ndeployment.apps\/devcluster-router   1\/1     1            1           10m\n\nNAME                          READY   AGE\nstatefulset.apps\/devcluster   3\/3     10m\n\nNAME                                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       VOLUMEATTRIBUTESCLASS   AGE\npersistentvolumeclaim\/datadir-devcluster-0   Bound    pvc-a47f104a-97bb-4a55-b9ef-51f110321d26   2Gi        RWO            do-block-storage   &lt;unset&gt;                 10m\npersistentvolumeclaim\/datadir-devcluster-1   Bound    pvc-4944fa31-3d88-42de-b7c0-159b85e1a240   2Gi        RWO            do-block-storage   &lt;unset&gt;                 10m\npersistentvolumeclaim\/datadir-devcluster-2   Bound    pvc-462985ea-35e4-4f51-b756-d00cef7096c4   2Gi        RWO            do-block-storage   &lt;unset&gt;                 10m<\/code><\/pre>\n<p>In the output you can see three MySQL stateful pods, its PVC&#8217;s and one Router deployment.<\/p>\n<p>Also, we deployed this as a Custom Resource, so let&#8217;s check the <code>innodbcluster<\/code> bject as well.<\/p>\n<pre><code>$ kubectl get innodbclusters\n\nNAME         STATUS   ONLINE   INSTANCES   ROUTERS   TYPE      AGE\ndevcluster   ONLINE   3        3           1         UNKNOWN   3m31s<\/code><\/pre>\n<p>Now that our MySQL cluster is ready, we can go ahead and test the connection from a client.<\/p>\n<h2 id=\"testing-the-innodb-cluster\">Testing the InnoDB Cluster<\/h2>\n<p>To test the connection, we need to create a temporary client <strong>MySQL pod<\/strong> and use the <strong>MySQL shell<\/strong> to connect to the database with the root user.<\/p>\n<pre><code class=\"language-bash\">kubectl run --rm -it myshell --image=container-registry.oracle.com\/mysql\/community-operator -- mysqlsh root@devcluster --sql\n<\/code><\/pre>\n<p>Once the shell is open, it will prompt you to <strong>provide the root password<\/strong>, in our case it is <code>root<\/code>.<\/p>\n<p>Now we can list the existing databases.<\/p>\n<pre><code>SHOW DATABASES;\n<\/code><\/pre>\n<pre><code class=\"language-bash\"> MySQL  devcluster:3306 ssl  SQL &gt; SHOW DATABASES;\n+-------------------------------+\n| Database                      |\n+-------------------------------+\n| information_schema            |\n| mysql                         |\n| mysql_innodb_cluster_metadata |\n| performance_schema            |\n| sys                           |\n+-------------------------------+\n5 rows in set (0.0049 sec)\n MySQL  devcluster:3306 ssl  SQL &gt; <\/code><\/pre>\n<p>To check the hostname, use the following command.<\/p>\n<pre><code class=\"language-bash\">SELECT @@hostname:<\/code><\/pre>\n<pre><code class=\"language-bash\"> MySQL  devcluster:3306 ssl  SQL &gt; SELECT @@hostname;\n+--------------+\n| @@hostname   |\n+--------------+\n| devcluster-0 |\n+--------------+\n1 row in set (0.0010 sec)\n MySQL  devcluster:3306 ssl  SQL &gt; <\/code><\/pre>\n<p>For testing, we are creating a custom database in it.<\/p>\n<pre><code class=\"language-bash\">CREATE DATABASE devopscube;<\/code><\/pre>\n<p>Now, we can list the databases again to see the newly created database.<\/p>\n<pre><code class=\"language-bash\"> MySQL  devcluster:3306 ssl  SQL &gt; SHOW DATABASES;\n+-------------------------------+\n| Database                      |\n+-------------------------------+\n| devopscube                    |\n| information_schema            |\n| mysql                         |\n| mysql_innodb_cluster_metadata |\n| performance_schema            |\n| sys                           |\n+-------------------------------+\n6 rows in set (0.0022 sec)\n MySQL  devcluster:3306 ssl  SQL &gt; <\/code><\/pre>\n<p>If you want to quit, press <code>control+d<\/code>,.<\/p>\n<blockquote><p>Note: If you want to check the replicated data, you can open each pod and list the databases.<\/p><\/blockquote>\n<h2 id=\"backup-using-mysql-operator\">Backup Using MySQL Operator<\/h2>\n<p>To back up the database, the MySQL operator provides a Custom Resource called <code>mysqlbackups.mysql.oracle.com<\/code><\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/08\/image-142.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"2000\" height=\"2058\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/08\/image-142.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/08\/image-142.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/08\/image-142.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/08\/image-142.png 2146w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Before creating the backup configuration, we need a <strong>Persistent Volume (PV)<\/strong> to store the backup.<\/p>\n<p>To create a PV, use the following contents.<\/p>\n<pre><code class=\"language-yaml\">cat &lt;&lt;EOF &gt; backup-pv.yaml\napiVersion: v1\nkind: PersistentVolume\nmetadata:\n  name: backup-pv\n  labels:\n    type: local\nspec:\n  storageClassName: gp2\n  capacity:\n    storage: 2Gi\n  accessModes:\n    - ReadWriteOnce\n  hostPath:\n    path: \/tmp\n---\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: backup-pvc\nspec:\n  storageClassName: gp2\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 2Gi\nEOF<\/code><\/pre>\n<p>To apply<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f backup-pv.yaml<\/code><\/pre>\n<p>Once the PV is created, we can configure the backup.<\/p>\n<p>The following manifest is to configure the backup. The <code>InnoDBCluster<\/code> object in the manifest is responsible for the backup. We have mapped the previously created <code>backup-pvc<\/code> under backupProfiles parameter.<\/p>\n<p>Create the <code>backup.yaml<\/code> manifest<\/p>\n<pre><code class=\"language-yaml\">---\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: devcluster-sa\n  namespace: default\n---\napiVersion: v1\nkind: Secret\nmetadata:\n  name: devcluster-cluster-secret\n  namespace: default\nstringData:\n  rootUser: \"root\"\n  rootHost: \"%\"\n  rootPassword: \"root\"\n---\napiVersion: mysql.oracle.com\/v2\nkind: InnoDBCluster\nmetadata:\n  name: devcluster\nspec:\n  instances: 3\n  router:\n    instances: 1\n  secretName: devcluster-cluster-secret\n  tlsUseSelfSigned: true\n  \n  backupProfiles:\n    - name: backupprofile  \n      dumpInstance:         \n        dumpOptions:\n        storage:\n          persistentVolumeClaim:\n            claimName: backup-pvc\n  backupSchedules:\n    - name: fiveminbackup\n      schedule: \"*\/5 * * * *\" \n      backupProfileName: backupprofile \n      enabled: true <\/code><\/pre>\n<p>For this demo, I have configured it to take backups <strong>every five minutes.<\/strong><\/p>\n<p><code>spec.backupProfiles<\/code> &#8211; Defines <strong>how<\/strong> the backup should be taken. Here, we specify the PVC to store the backups and <strong>dump options<\/strong> to include or exclude specific databases.<\/p>\n<p><code>spec.backupSchedules<\/code> &#8211; Defines <strong>when<\/strong> the backup should be taken. Here, we specify the <a href=\"https:\/\/devopscube.com\/create-kubernetes-jobs-cron-jobs\/\" rel=\"noreferrer\">cronjob<\/a> to periodically take the backup.<\/p>\n<p>Now, deploy the backup manifest.<\/p>\n<pre><code class=\"language-bash\">$ kubectl apply backup.yaml<\/code><\/pre>\n<p>Once the configurations are applied, we can list the <strong>backup custom resources <\/strong>created by the operator.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get mysqlbackups\n\nNAME                                   CLUSTER      STATUS      OUTPUT                                 AGE\ndevcluster-fiveminbackup250816054003   devcluster   Completed   devcluster-fiveminbackup250816054003   7h8m\ndevcluster-fiveminbackup250816054502   devcluster   Completed   devcluster-fiveminbackup250816054502   7h3m\ndevcluster-fiveminbackup250816055002   devcluster   Completed   devcluster-fiveminbackup250816055002   6h58m<\/code><\/pre>\n<p>The output shows that the backup has been taken every five minutes as we configured.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">For production use cases, instead of PV, we can use object storage like AWS S3 to store the backups. To know more about this, refer to this <a href=\"https:\/\/dev.mysql.com\/doc\/mysql-operator\/en\/mysql-operator-backups.html?ref=devopscube.com\" rel=\"noreferrer\">official documentation<\/a>.<\/div>\n<\/div>\n<p>The next section covers the things we need to consider when we setup the MySQL operator on a multi-zone Kubernetes cluster.<\/p>\n<h2 id=\"availability-zone-considerations-for-cloud-deployments\">Availability Zone Considerations for Cloud Deployments<\/h2>\n<p>The main problem most of the time with the <strong>StatefulSets<\/strong> is the <strong>persistent storage.<\/strong><\/p>\n<p>For example, when we use<strong> AWS EBS<\/strong> as a storage, it is zone-specific. So a pod from one zone cannot write to a volume in another zone.<\/p>\n<p>But in the MySQL operator, the <strong>data replication occurs on the MySQL application layer over the network<\/strong>, so the data will be shared on all the pods, even in multiple zones.<\/p>\n<p>And one common doubt is that, what if a Pod is deleted and recreated in another zone?<\/p>\n<p>When a StatefulSet pod is deleted and re-created, the Kubernetes scheduler doesnt try to place it on the same node. However, itt respects the volumes AZ via \u201cvolume node affinity.\u201d <\/p>\n<p>With storage classes using <code>volumeBindingMode: WaitForFirstConsumer<\/code>, the PV is created\/bound in the AZ of the node that first schedules the pod and future reschedules must stay in that AZ.<\/p>\n<p>This way when a pod is deleted, it is recreated in the same AZ.<\/p>\n<p>Also, what you need to consider is the <strong>data transfer costs<\/strong> because the cross-zone transfer cost is higher than the same zone.<\/p>\n<p>And the second one is <strong>latency<\/strong>, because the distance between one zone to another will slow down the transfer speed.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">The data replication works without any issues in multi-zone clusters like <a href=\"https:\/\/devopscube.com\/create-aws-eks-cluster-eksctl\/\" rel=\"noreferrer\">EKS<\/a> or AKS, because it occurs on the MySQL application layer over the network, not at the storage layer.<\/p>\n<p>The primary instance shares the data with others, and then the other nodes send back the acknowledgment to confirm that they received the data.<\/p><\/div>\n<\/div>\n<h3 id=\"split-brain-scenario\">Split Brain Scenario<\/h3>\n<p>In a MySQL cluster, the primary instance will be decided by voting, so we always need to keep the instance count in odd numbers (e.g., 3, 5, 7), which is called the <strong>Quorum<\/strong>.<\/p>\n<p>In mutti-az setup, during a AZ unavailability, split-brain scenarios can occur when Network partitions cause multiple MySQL instances to think they are the primary.<\/p>\n<h2 id=\"cleanup\">Cleanup<\/h2>\n<p>To cleanup the setup, we need to remove the InnoDB cluster before uninstalling the operator.<\/p>\n<p>To delete the InnoDB cluster, use the following command.<\/p>\n<pre><code>helm -n default uninstall devcluster<\/code><\/pre>\n<p>Now, we can uninstall the MySQL operator.<\/p>\n<pre><code>helm -n mysql-operator uninstall my-mysql-operator <\/code><\/pre>\n<p>To delete the namespace, use the following command.<\/p>\n<pre><code>kubectl delete ns mysql-operator<\/code><\/pre>\n<h2 id=\"possible-issues\">Possible Issues<\/h2>\n<p>If you are facing any issues with cluster domain error, you need to set the <code>MYSQL_OPERATOR_K8S_CLUSTER_DOMAIN<\/code> variable in the operator deployment and restart the pods using the following commands.<\/p>\n<pre><code class=\"language-bash\">kubectl -n mysql-operator set env deploy\/mysql-operator \\\n  MYSQL_OPERATOR_K8S_CLUSTER_DOMAIN=cluster.local\n\nkubectl -n mysql-operator rollout restart deploy\/mysql-operator<\/code><\/pre>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>Manually creating and managing a MySQL Cluster is quite complex, but with an operator we can easily manage it.<\/p>\n<p>We have shown you how to deploy the MySQL operator on kubernetes and mange it effectively.<\/p>\n<p>Try this setup on your Kubernetes cluster to explore more about the MySQL operators and their features so that you can modify them as per your requirements.<\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/install-mysql-operator-on-kubernetes\/\" target=\"_blank\" rel=\"noopener noreferrer\">Setup  MySQL Operator on Kubernetes (Step by Step Guide) \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/install-mysql-operator-on-kubernetes\/<\/p>\n","protected":false},"author":1,"featured_media":425,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-424","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/424","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=424"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/424\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/425"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=424"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=424"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=424"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}