Code Monkey home page Code Monkey logo

docker-percona_galera's Introduction

Auto Clustering/Replicating Percona Database

This is a tech demo of a combination of the factorish toolset to create and run a Percona (mysql) Database image that when combined with etcd will automatically cluster and replicate with itself.

How does it work ?

There are two images in this project, the first contains Percona and the Galera replication tools, the second contains Maxscale (a MySQL load balancer).

When run with access to a service discovery tool (etcd by default) it is able discover other running databases and set up a replication relationship.

By default it uses Glider Labs' registrator to perform the service registry, but can access etcd directly if that is your preference.

Inside the container runit manages three processes:

confd

Used to watch the etcd endpoint and rewrite config files with any changes.

healthcheck

Watches availability of the application ( percona or maxscale ) and kills runit (thus the container) when it fails.

percona / maxscale

Runs the main process for the container, either Percona or Maxscale depending on which image is running.

See factorish for a detailed description of the factorish toolset.

Tech Demo

In order to demonstrate the clustering capabilties there is an included Vagrantfile which when used will spin up a 3 node coreos cluster running a local Docker Registry and Registrator images.

If you want to run this outside of the tech demo see the contrib/ directory and/or start the tech demo first and view the /etc/profiles.d/functions.sh file in any of the coreos nodes.

The registry is hosted in a path mapped in from the host computer and therefore is shared amongst the coreos nodes. This means that any images pushed to it from one host are immediately avaliable to all the other hosts.

This allows for some intelligent image pulling/building to ensure that only a single node has to do the heavy lifting. See the user-data.erb file for the scripts that allow this sharing of work.

Both the database and maxscale images are built from scratch automatically and started as the coreos nodes come online. Thanks to the registry they will survive a vagrant destroy which means subsequent vagrant up will be substantially faster.

Running in CoreOS with etcd/registrator discovery

In order to use the tech demo simply run the following:

$ git clone https://github.com/paulczar/docker-percona_galera.git
$ cd docker-percona_galera
$ vagrant up

Once Vagrant has brought up your three nodes you want to log in and watch the progress of the build using one of the provided helper functions:

$ vagrant ssh core-01
$ journal_database

This make take a few minutes if its the first time you've run this and the images aren't cached in the registry. If you get bored you can also check out journal_registry and journal_registrator and watch them get pulled down and run. It is also possible a different host will be elected to do the build, in which case you'll see it show as waiting for that host before it proceeds.

Once the database is online ( you'll see percona start and replication collect in the journal_database output ) you can connect to Maxscale via the helper function mysql:

$ mysql
mysql> select @@hostname;
+--------------+
| @@hostname   |
+--------------+
| a7575fd684eb |
+--------------+

the maxscale LB can take a while to find the service to loadbalance, and can also sometimes just fail. I haven't worked out why yet.

or by connecting to the shell of the database container on the current host:

$ database
root@ecfd272af45e:/app# mysql
mysql> select @@hostname;
+--------------+
| @@hostname   |
+--------------+
| ecfd272af45e |
+--------------+

notice the returned hostname is not the same in both queries, this is because the first was loadbalanced to the database on a different container

Helper functions

Each container started at boot has the following helper functions created created in /etc/profile.d/functions.sh and autoloaded by the shell. (examples shown below for database container)

  • database - get shell in container.
  • kill_database - kills the container, equivalent to docker rm -f database
  • build_database - rebuilds the image
  • push_database - pushs the image to registry
  • log_database - connect to the docker log stream for that container
  • journal_database - connect to the systemd journal for that container

They become very useful when combined:

$ build_database && push_database
$ kill_database && run_database && log_database

There is also the mysql function which will connect you via the local proxy to a percona server and a cleanup function which deletes the /services namespace in etcd

Finally in the git repo is a clean_registry script which when run on the host will remove all images from the registry filesystem which is useful if you want to do a full rebuild from scratch.

Running without service discovery:

Server 1

change HOST to be the IP address of the server.

$ export HOST=172.17.8.101
$ docker run --detach \
  --name database01 \
  -e BOOTSTRAP=1 -e DEBUG=1 \
  -e MYSQL_PASS=password -e REP_PASS=replicate \
  -e HOST=$HOST -e SERVICE_DISCOVERY=env \
  -p $HOST:3306:3306 \
  -p $HOST:4444:4444 \
  -p $HOST:4567:4567 \
  -p $HOST:4568:4568 \
  paulczar/percona-galera

Servers 2,3,etc

change HOST to be the IP address of the server, change CLUSTER_MEMBERS to be the IP of the first server.

$ export HOST=172.17.8.102
docker run -ti --rm \
  --name database02 \
  -e DEBUG=1 \
  -e MYSQL_PASS=password -e REP_PASS=replicate \
  -e CLUSTER_MEMBERS=172.17.8.101 \
  -e HOST=$HOST -e SERVICE_DISCOVERY=env \
  -p $HOST:3306:3306 \
  -p $HOST:4444:4444 \
  -p $HOST:4567:4567 \
  -p $HOST:4568:4568 \
  paulczar/percona-galera  bash

Run in Rackspace's Carina Service:

Signup for carina and create a 3 node cluster. Download and source the carina config files and get the carina binary as well.

note this will leave the mysql and galera ports open to the whole of the service-net network

we need to get the servicenet IP address of each node:

$ docker info
Containers: 7
Images: 6
Engine Version: 
Role: primary
Strategy: spread
Filters: health, port, dependency, affinity, constraint
Nodes: 3
 bf76bea4-47ef-43ac-a7ae-67a6e6db15bf-n1: 172.99.65.11:42376
  └ Containers: 3
  └ Reserved CPUs: 0 / 12
  └ Reserved Memory: 0 B / 4.2 GiB
  └ Labels: executiondriver=native-0.2, kernelversion=3.18.21-1-rackos, operatingsystem=Debian GNU/Linux 7 (wheezy) (containerized), storagedriver=aufs
 bf76bea4-47ef-43ac-a7ae-67a6e6db15bf-n2: 172.99.65.12:42376
  └ Containers: 2
  └ Reserved CPUs: 0 / 12
  └ Reserved Memory: 0 B / 4.2 GiB
  └ Labels: executiondriver=native-0.2, kernelversion=3.18.21-1-rackos, operatingsystem=Debian GNU/Linux 7 (wheezy) (containerized), storagedriver=aufs
 bf76bea4-47ef-43ac-a7ae-67a6e6db15bf-n3: 172.99.65.13:42376
  └ Containers: 2
  └ Reserved CPUs: 0 / 12
  └ Reserved Memory: 0 B / 4.2 GiB
  └ Labels: executiondriver=native-0.2, kernelversion=3.18.21-1-rackos, operatingsystem=Debian GNU/Linux 7 (wheezy) (containerized), storagedriver=aufs
CPUs: 36
Total Memory: 12.6 GiB
Name: a892be77e40c

for each node we need to get the servicenet ip:

$ docker run --net=host \
  --env constraint:node==bf76bea4-47ef-43ac-a7ae-67a6e6db15bf-n1 \
  racknet/ip service ipv4
10.176.230.11
  --env constraint:node==bf76bea4-47ef-43ac-a7ae-67a6e6db15bf-n2 \
  racknet/ip service ipv4
10.176.230.12
$ docker run --net=host \
  --env constraint:node==bf76bea4-47ef-43ac-a7ae-67a6e6db15bf-n3 \
  racknet/ip service ipv4
10.176.230.13

Start your first MySQL server:

$ docker run --detach \
  --name database01 \
  --env constraint:node==bf76bea4-47ef-43ac-a7ae-67a6e6db15bf-n1 \
  -e DEBUG=1 -e MYSQL_USER=admin \
  -e MYSQL_PASS=notthispassword -e REP_PASS=woopdedoo \
  -e HOST=10.176.230.11 -e SERVICE_DISCOVERY=env \
  -p 10.176.230.11:3306:3306 \
  -p 10.176.230.11:4444:4444 \
  -p 10.176.230.11:4567:4567 \
  -p 10.176.230.11:4568:4568 \
  paulczar/percona-galera

Second and third:

$ docker run -d \
  --name database02 \
  --env constraint:node==bf76bea4-47ef-43ac-a7ae-67a6e6db15bf-n2 \
  -e MYSQL_USER=admin \
  -e CLUSTER_MEMBERS=10.176.230.11 \
  -e MYSQL_PASS=notthispassword -e REP_PASS=woopdedoo \
  -e HOST=10.176.230.12 -e SERVICE_DISCOVERY=env \
  -p 10.176.230.12:3306:3306 \
  -p 10.176.230.12:4444:4444 \
  -p 10.176.230.12:4567:4567 \
  -p 10.176.230.12:4568:4568 \
  paulczar/percona-galera

$ docker run -d \
  --name database03 \
  --env constraint:node==bf76bea4-47ef-43ac-a7ae-67a6e6db15bf-n3 \
  -e MYSQL_USER=admin \
  -e CLUSTER_MEMBERS=10.176.230.11 \
  -e MYSQL_PASS=notthispassword -e REP_PASS=woopdedoo \
  -e HOST=10.176.230.13 -e SERVICE_DISCOVERY=env \
  -p 10.176.230.13:3306:3306 \
  -p 10.176.230.13:4444:4444 \
  -p 10.176.230.13:4567:4567 \
  -p 10.176.230.13:4568:4568 \
  paulczar/percona-galera  

wait a minute or so then check status:

docker exec -ti database01 mysql -e "SHOW STATUS LIKE 'wsrep_cluster%'"
+--------------------------+--------------------------------------+
| Variable_name            | Value                                |
+--------------------------+--------------------------------------+
| wsrep_cluster_conf_id    | 3                                    |
| wsrep_cluster_size       | 3                                    |
| wsrep_cluster_state_uuid | 2882bcb7-ab3b-11e5-ab75-2b510ef0ec6f |
| wsrep_cluster_status     | Primary                              |
+--------------------------+--------------------------------------+

Author(s)

Paul Czarkowski ([email protected])

License

Copyright 2014 Paul Czarkowski Copyright 2015 Paul Czarkowski

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

docker-percona_galera's People

Contributors

paulczar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

docker-percona_galera's Issues

/app/bin/reload missing

The file /app/bin/reload is missing - it's referenced in database_creds.toml - any ideas?

Failed node cannot join back

Hi,
I have just setup a simple 5 node cluster and testing how recovery could work... Whenever a node fails (kill -9, docker restart, etc) that node will not join back with this error:

2016-06-28 08:14:16 1899 [ERROR] WSREP: gcs/src/gcs_group.cpp:group_post_state_exchange():321: Reversing history: 935 -> 934, this member has applied 1 more events than the primary component.Data loss is possible. Aborting.

It would seem something makes an extra transaction while the environment is setting up. Using default current docker hub build, ran at least a dozen scenarios and with each I get the same result.
After this only recover is possible by deleting grastate.dat, but that defeats the purpose if I want to make a HA cluster.
Any guess what could be the issue?

Does this work with the latest CoreOS?

Hello,

Does this environment still work with the latest CoreOS? I'm testing the alpha release which is currently at 459.0.0.

Upon a vagrant up I'll SSH into the core-01 VM and run watch docker ps and after 10 minutes nothing has happened. It's almost like the /var/lib/coreos-vagrant/vagrantfile-user-data file isn't being loaded.

I can manually run the commands in the generated user-data and I can get the DB to come up as a single instance (because the $COREOS_PRIVATE_IPV4 obviously isn't set when I do this).

I'm looking around to see if something changed with the latest CoreOS and loading of the vagrantfile-user-data but am having no luck.

Cheers,
-Andrew

support encrypted replication

I'd like to consider using this image to replicate across data centers, or in other scenarios where the security of the network is not entirely guaranteed. (For example, across availability zones or regions of EC2 classic.) To do this, I think this image would have to support:

Is anyone else interested in this?

/bin/boot doesn't work if CLUSTER is empty...

confd hangs with:

2015-02-13T20:39:29Z mysql confd[3247]: ERROR 100: Key not found (/database/cluster) [323]
2015-02-13T20:39:29Z mysql confd[3247]: ERROR 100: Key not found (/database/cluster) [323]
...

Checking endpoints

Hi,

I think this line (in several /bin scripts)

while [[ -z $(etcdctl --no-sync -C $ETCD ls $ETCD_PATH/cluster/galeraEndpoints | awk -F/ '{print $6}' | xargs | sed 's/ /,/') ]]

assumes a specific $ETCD_PATH.

Maybe this is more robust:

while [[ -z $(etcdctl --no-sync -C $ETCD ls $ETCD_PATH/cluster/galeraEndpoints | awk -F/ '{print $NF}' | xargs | sed 's/ /,/') ]]

?

Thanks for all the work
Marco

fleet - database container

they dont remove the name on unexpect reboot

-- Logs begin at Sun 2014-09-21 18:29:34 UTC. --
Sep 24 01:32:12 core1.c.graphite-sphere-686.internal docker[1126]: Pulling repository paulczar/percona-galera
Sep 24 01:32:19 core1.c.graphite-sphere-686.internal systemd[1]: Started database-1.
Sep 24 01:32:19 core1.c.graphite-sphere-686.internal sh[1174]: 2014/09/24 01:32:19 Error response from daemon: Conflict, The name database-1 is already assigned to 632648f90cbb. You have to delete (or rename) that container to be able to assign database-1 to a container again.
Sep 24 01:32:19 core1.c.graphite-sphere-686.internal systemd[1]: database-1.service: main process exited, code=exited, status=1/FAILURE
Sep 24 01:32:19 core1.c.graphite-sphere-686.internal docker[1193]: database-1
Sep 24 01:32:19 core1.c.graphite-sphere-686.internal systemd[1]: Unit database-1.service entered failed state.
Sep 24 01:35:40 core1.c.graphite-sphere-686.internal systemd[1]: Stopped database-1.

  • unitfiles need some refactoring .. ill PR as soon as i have changes in place ..

Thanks for all the hard work !

Stuck at elections

Hi,

First-off: the dockerfile looks very promising, thank you for your effort.

Now, when using this command (below) to start the image at three servers (5-10 seconds apart), The elections don't go as planned.

docker run -d --name mysql -e HOST=$COREOS_PRIVATE_IPV4 -e MYSQL_USER=admin -e MYSQL_PASS=admin -e CLUSTER=galera -P paulczar/percona-galera

The first server:

2015-06-24T17:31:44Z 85c0976bdbfc confd[72]: ERROR exit status 1
echo ==> database: waiting for confd to write initial templates...
==> An empty or uninitialized database is detected in /var/lib/mysql
-----> Creating database...
-----> Done!
==> starting mysql in order to set up passwords
-----> sleeping for 20 seconds, then testing if DB is up
150624 17:31:58 mysqld_safe Logging to '/var/log/mysql/error.log'.
150624 17:31:58 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
150624 17:31:58 mysqld_safe Skipping wsrep-recover for empty datadir: /var/lib/mysql
150624 17:31:58 mysqld_safe Assigning 00000000-0000-0000-0000-000000000000:-1 to wsrep_start_position
==> stopping mysql after setting up passwords
150624 17:32:21 mysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
Starting MySQL for reals
==> Performing Election...
-----> Hurruh I win!
==> sleeping for 20 seconds, then testing if DB is up.
150624 17:32:23 mysqld_safe Logging to '/var/log/mysql/error.log'.
150624 17:32:23 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
150624 17:32:23 mysqld_safe Skipping wsrep-recover for ea8c9e41-1a96-11e5-9660-4283726c59af:0 pair
150624 17:32:23 mysqld_safe Assigning ea8c9e41-1a96-11e5-9660-4283726c59af:0 to wsrep_start_position
==> database running...
2015-06-24 17:32:23 2061 [Note] /usr/sbin/mysqld: ready for connections.
Version: '5.6.20-68.0-56-log'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  Percona XtraDB Cluster (GPL), Release 25.7, wsrep_25.7.r4126
2015-06-24 17:32:23 2061 [Note] WSREP: inited wsrep sidno 2
2015-06-24 17:32:23 2061 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
2015-06-24 17:32:23 2061 [Note] WSREP: REPL Protocols: 6 (3, 2)
2015-06-24 17:32:23 2061 [Note] WSREP: Service thread queue flushed.
2015-06-24 17:32:23 2061 [Note] WSREP: Assign initial position for certification: 0, protocol version: 3
2015-06-24 17:32:23 2061 [Note] WSREP: Service thread queue flushed.
2015-06-24 17:32:23 2061 [Note] WSREP: Synchronized with group, ready for connections
2015-06-24 17:32:23 2061 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.

Nothing out of the ordinary.

But the second and third server:

2015-06-24T17:32:01Z ee8e3ca31f90 confd[73]: ERROR exit status 1
echo ==> database: waiting for confd to write initial templates...
==> An empty or uninitialized database is detected in /var/lib/mysql
-----> Creating database...
-----> Done!
==> starting mysql in order to set up passwords
-----> sleeping for 20 seconds, then testing if DB is up
150624 17:32:15 mysqld_safe Logging to '/var/log/mysql/error.log'.
150624 17:32:15 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
150624 17:32:15 mysqld_safe Skipping wsrep-recover for empty datadir: /var/lib/mysql
150624 17:32:15 mysqld_safe Assigning 00000000-0000-0000-0000-000000000000:-1 to wsrep_start_position
==> stopping mysql after setting up passwords
150624 17:32:38 mysqld_safe mysqld from pid file /var/run/mysqld/mysqld.pid ended
Starting MySQL for reals
==> Performing Election...

Shouldn't they join the already existing cluster? I can also wait a few minutes before starting the second/third server, but the effect is the same.

SST issue

hi all, when rejoining a node a complete sync works fine by SST fails, here's the output:

Apr 27 09:51:38 mysql-1.local docker[22198]: 2016-04-27 07:51:38 4405 [Note] WSREP: (db0ec150, 'tcp://0.0.0.0:4567') turning message relay requesting on, nonlive peers:
Apr 27 09:51:38 mysql-1.local docker[22198]: 2016-04-27 07:51:38 4405 [Note] WSREP: gcomm: connected
Apr 27 09:51:38 mysql-1.local docker[22198]: 2016-04-27 07:51:38 4405 [Note] WSREP: Changing maximum packet size to 64500, resulting msg size: 32636
Apr 27 09:51:38 mysql-1.local docker[22198]: 2016-04-27 07:51:38 4405 [Note] WSREP: Shifting CLOSED -> OPEN (TO: 0)
Apr 27 09:51:38 mysql-1.local docker[22198]: 2016-04-27 07:51:38 4405 [Note] WSREP: Opened channel 'database'
Apr 27 09:51:38 mysql-1.local docker[22198]: 2016-04-27 07:51:38 4405 [Note] WSREP: Waiting for SST to complete.
Apr 27 09:51:39 mysql-1.local docker[22198]: + ss -l -n -t
Apr 27 09:51:39 mysql-1.local docker[22198]: + grep :3306
Apr 27 09:51:39 mysql-1.local docker[22198]: + kill 1

maybe it's related to small size of galera.cache which defaults to 128M.
everything else is fine, including service discovery with etcd. Thanks for feedback.

my.cnf

Great code, testing and running it on CoreOS, works. Just one question: how to get MySQL preferences via config file into the container, like changing max connection or similar? Thanks.

Unable to start using Vagrant

vagrant up doesn't work. I am on Vagrant 1.7.4.
This is the error I get :-

rewriting userdata
Bringing machine 'core-01' up with 'virtualbox' provider...
Bringing machine 'core-02' up with 'virtualbox' provider...
Bringing machine 'core-03' up with 'virtualbox' provider...
==> core-01: Box 'coreos-alpha' could not be found. Attempting to find and install...
    core-01: Box Provider: virtualbox
    core-01: Box Version: >= 554
==> core-01: Loading metadata for box 'http://storage.core-os.net/coreos/amd64-usr/alpha/coreos_prod
uction_vagrant.json'
    core-01: URL: http://storage.core-os.net/coreos/amd64-usr/alpha/coreos_production_vagrant.json
==> core-01: Adding box 'coreos-alpha' (v1153.4.0) for provider: virtualbox
    core-01: Downloading: https://alpha.release.core-os.net/amd64-usr/1153.4.0/coreos_production_vag
rant.box
    core-01: Progress: 0% (Rate: 0/s, Estimated time remaining: --:--:--)
An error occurred while downloading the remote file. The error
message, if any, is reproduced below. Please fix this error and try
again.

The requested URL returned error: 404 Not Found

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.