|
|
|
@@ -15,8 +15,9 @@ weight: 30
|
|
|
|
|
|
|
|
|
|
This page shows how to run a replicated stateful application using a
|
|
|
|
|
[StatefulSet](/docs/concepts/workloads/controllers/statefulset/) controller.
|
|
|
|
|
The example is a MySQL single-master topology with multiple slaves running
|
|
|
|
|
asynchronous replication.
|
|
|
|
|
This application is a replicated MySQL database. The example topology has a
|
|
|
|
|
single primary server and multiple replicas, using asynchronous row-based
|
|
|
|
|
replication.
|
|
|
|
|
|
|
|
|
|
{{< note >}}
|
|
|
|
|
**This is not a production configuration**. MySQL settings remain on insecure defaults to keep the focus
|
|
|
|
@@ -69,9 +70,9 @@ kubectl apply -f https://k8s.io/examples/application/mysql/mysql-configmap.yaml
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This ConfigMap provides `my.cnf` overrides that let you independently control
|
|
|
|
|
configuration on the MySQL master and slaves.
|
|
|
|
|
In this case, you want the master to be able to serve replication logs to slaves
|
|
|
|
|
and you want slaves to reject any writes that don't come via replication.
|
|
|
|
|
configuration on the primary MySQL server and replicas.
|
|
|
|
|
In this case, you want the primary server to be able to serve replication logs to replicas
|
|
|
|
|
and you want replicas to reject any writes that don't come via replication.
|
|
|
|
|
|
|
|
|
|
There's nothing special about the ConfigMap itself that causes different
|
|
|
|
|
portions to apply to different Pods.
|
|
|
|
@@ -96,12 +97,12 @@ cluster and namespace.
|
|
|
|
|
|
|
|
|
|
The Client Service, called `mysql-read`, is a normal Service with its own
|
|
|
|
|
cluster IP that distributes connections across all MySQL Pods that report
|
|
|
|
|
being Ready. The set of potential endpoints includes the MySQL master and all
|
|
|
|
|
slaves.
|
|
|
|
|
being Ready. The set of potential endpoints includes the primary MySQL server and all
|
|
|
|
|
replicas.
|
|
|
|
|
|
|
|
|
|
Note that only read queries can use the load-balanced Client Service.
|
|
|
|
|
Because there is only one MySQL master, clients should connect directly to the
|
|
|
|
|
MySQL master Pod (through its DNS entry within the Headless Service) to execute
|
|
|
|
|
Because there is only one primary MySQL server, clients should connect directly to the
|
|
|
|
|
primary MySQL Pod (through its DNS entry within the Headless Service) to execute
|
|
|
|
|
writes.
|
|
|
|
|
|
|
|
|
|
### StatefulSet
|
|
|
|
@@ -167,33 +168,33 @@ This translates the unique, stable identity provided by the StatefulSet
|
|
|
|
|
controller into the domain of MySQL server IDs, which require the same
|
|
|
|
|
properties.
|
|
|
|
|
|
|
|
|
|
The script in the `init-mysql` container also applies either `master.cnf` or
|
|
|
|
|
`slave.cnf` from the ConfigMap by copying the contents into `conf.d`.
|
|
|
|
|
Because the example topology consists of a single MySQL master and any number of
|
|
|
|
|
slaves, the script simply assigns ordinal `0` to be the master, and everyone
|
|
|
|
|
else to be slaves.
|
|
|
|
|
The script in the `init-mysql` container also applies either `primary.cnf` or
|
|
|
|
|
`replica.cnf` from the ConfigMap by copying the contents into `conf.d`.
|
|
|
|
|
Because the example topology consists of a single primary MySQL server and any number of
|
|
|
|
|
replicas, the script simply assigns ordinal `0` to be the primary server, and everyone
|
|
|
|
|
else to be replicas.
|
|
|
|
|
Combined with the StatefulSet controller's
|
|
|
|
|
[deployment order guarantee](/docs/concepts/workloads/controllers/statefulset/#deployment-and-scaling-guarantees/),
|
|
|
|
|
this ensures the MySQL master is Ready before creating slaves, so they can begin
|
|
|
|
|
this ensures the primary MySQL server is Ready before creating replicas, so they can begin
|
|
|
|
|
replicating.
|
|
|
|
|
|
|
|
|
|
### Cloning existing data
|
|
|
|
|
|
|
|
|
|
In general, when a new Pod joins the set as a slave, it must assume the MySQL
|
|
|
|
|
master might already have data on it. It also must assume that the replication
|
|
|
|
|
In general, when a new Pod joins the set as a replica, it must assume the primary MySQL
|
|
|
|
|
server might already have data on it. It also must assume that the replication
|
|
|
|
|
logs might not go all the way back to the beginning of time.
|
|
|
|
|
These conservative assumptions are the key to allow a running StatefulSet
|
|
|
|
|
to scale up and down over time, rather than being fixed at its initial size.
|
|
|
|
|
|
|
|
|
|
The second Init Container, named `clone-mysql`, performs a clone operation on
|
|
|
|
|
a slave Pod the first time it starts up on an empty PersistentVolume.
|
|
|
|
|
a replica Pod the first time it starts up on an empty PersistentVolume.
|
|
|
|
|
That means it copies all existing data from another running Pod,
|
|
|
|
|
so its local state is consistent enough to begin replicating from the master.
|
|
|
|
|
so its local state is consistent enough to begin replicating from the primary server.
|
|
|
|
|
|
|
|
|
|
MySQL itself does not provide a mechanism to do this, so the example uses a
|
|
|
|
|
popular open-source tool called Percona XtraBackup.
|
|
|
|
|
During the clone, the source MySQL server might suffer reduced performance.
|
|
|
|
|
To minimize impact on the MySQL master, the script instructs each Pod to clone
|
|
|
|
|
To minimize impact on the primary MySQL server, the script instructs each Pod to clone
|
|
|
|
|
from the Pod whose ordinal index is one lower.
|
|
|
|
|
This works because the StatefulSet controller always ensures Pod `N` is
|
|
|
|
|
Ready before starting Pod `N+1`.
|
|
|
|
@@ -206,15 +207,15 @@ server, and an `xtrabackup` container that acts as a
|
|
|
|
|
[sidecar](https://kubernetes.io/blog/2015/06/the-distributed-system-toolkit-patterns).
|
|
|
|
|
|
|
|
|
|
The `xtrabackup` sidecar looks at the cloned data files and determines if
|
|
|
|
|
it's necessary to initialize MySQL replication on the slave.
|
|
|
|
|
it's necessary to initialize MySQL replication on the replica.
|
|
|
|
|
If so, it waits for `mysqld` to be ready and then executes the
|
|
|
|
|
`CHANGE MASTER TO` and `START SLAVE` commands with replication parameters
|
|
|
|
|
extracted from the XtraBackup clone files.
|
|
|
|
|
|
|
|
|
|
Once a slave begins replication, it remembers its MySQL master and
|
|
|
|
|
Once a replica begins replication, it remembers its primary MySQL server and
|
|
|
|
|
reconnects automatically if the server restarts or the connection dies.
|
|
|
|
|
Also, because slaves look for the master at its stable DNS name
|
|
|
|
|
(`mysql-0.mysql`), they automatically find the master even if it gets a new
|
|
|
|
|
Also, because replicas look for the primary server at its stable DNS name
|
|
|
|
|
(`mysql-0.mysql`), they automatically find the primary server even if it gets a new
|
|
|
|
|
Pod IP due to being rescheduled.
|
|
|
|
|
|
|
|
|
|
Lastly, after starting replication, the `xtrabackup` container listens for
|
|
|
|
@@ -224,7 +225,7 @@ case the next Pod loses its PersistentVolumeClaim and needs to redo the clone.
|
|
|
|
|
|
|
|
|
|
## Sending client traffic
|
|
|
|
|
|
|
|
|
|
You can send test queries to the MySQL master (hostname `mysql-0.mysql`)
|
|
|
|
|
You can send test queries to the primary MySQL server (hostname `mysql-0.mysql`)
|
|
|
|
|
by running a temporary container with the `mysql:5.7` image and running the
|
|
|
|
|
`mysql` client binary.
|
|
|
|
|
|
|
|
|
@@ -291,7 +292,7 @@ it running in another window so you can see the effects of the following steps.
|
|
|
|
|
|
|
|
|
|
## Simulating Pod and Node downtime
|
|
|
|
|
|
|
|
|
|
To demonstrate the increased availability of reading from the pool of slaves
|
|
|
|
|
To demonstrate the increased availability of reading from the pool of replicas
|
|
|
|
|
instead of a single server, keep the `SELECT @@server_id` loop from above
|
|
|
|
|
running while you force a Pod out of the Ready state.
|
|
|
|
|
|
|
|
|
@@ -409,9 +410,9 @@ Now uncordon the Node to return it to a normal state:
|
|
|
|
|
kubectl uncordon <node-name>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Scaling the number of slaves
|
|
|
|
|
## Scaling the number of replicas
|
|
|
|
|
|
|
|
|
|
With MySQL replication, you can scale your read query capacity by adding slaves.
|
|
|
|
|
With MySQL replication, you can scale your read query capacity by adding replicas.
|
|
|
|
|
With StatefulSet, you can do this with a single command:
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|