Efficiently seeding MySQL with large amount of data before tests - mysql

I want to introduce testing into a huge legacy system with a web API. So because of the lack of features in the framework(symfony 2) I decided to write Postman tests to test the system from the outside. The problem is the huge database should be in place for the tests to work and it needs to be at a certain state for each test(it can't be reused by all tests because they might alter the data). From my tests using a sql dp to restore takes around 40 seconds which is not acceptable for each test.
Now I am in need of a solution or just give up on testing which I do not want to do.
One solution I have come up with but need verification that it works is to:
Bring up a MySQL docker container and use a sql dump to get the database to initial state.
Copy the MySQL data volume to someplace safe called initdata.
Copy the initdata to a location used as MySQL data volume.
Run the test
Delete container and modified data volume.
Reapeat from step 2 for each test.
This is the general idea but I need to know whether this works with MySQL docker and whether copying volumes is actually efficient and fast enough. Or maybe any other sane solution for this situation.

I helped a company that wanted to test a 1TB MySQL database repeatedly. The solution we ended up with was to use LVM filesystem snapshots, so one could quickly revert the whole filesystem to its saved state almost instantly.
But if using filesystem snapshots is not an option, you may still have to use some backup/restore tool.
Logical data loads (i.e. importing a mysqldump file) are known to be very time-consuming. There are some alternative tools like mysqlpump or mydumper, but they're all pretty slow.
Physical backup tools like Percona XtraBackup are much faster, especially on restore. But restoring a physical backup is a bit tricky because the MySQL Server must be shut down.
There's a good comparison of the performance of backup/restore tools for MySQL in this recent blog: https://www.percona.com/blog/backup-restore-performance-conclusion-mysqldump-vs-mysql-shell-utilities-vs-mydumper-vs-mysqlpump-vs-xtrabackup/

So this is what we did, and I'm writing it here for anyone that have faced the same problem.
We built a MySQL image with our data imported to it from a mysqldump file, and we bring up a container with that image, run our tests and then bring it down, remove it and do it all over for each test. Now this method is quite efficient and bringing up the container and stopping and removing it takes around 5 seconds for each test for a db with 500 tables and a 55mb dump (we removed all unnecessary rows).
Here is a sample of the docker file and the process that we used to build the image:
FROM docker.supply.codes/base/mysql:8.0.26
COPY ./mysql_data /var/lib/mysql
and we have a script that is run everytime our dump gets updated in git which imports the dump, builds image and pushes it to a docker registry:
# run a mysql container
docker run -d --name $MYSQL_CONTAINER_NAME -v mysql_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD -e MYSQL_DATABASE=$MYSQL_DATABASE $MYSQL_IMAGE mysqld --default-authentication-plugin=mysql_native_password
# Wait until MySQL container is completely up
sleep 10
# import mysqldump
docker exec -i $MYSQL_CONTAINER_NAME sh -c 'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" $MYSQL_DATABASE' < ./$MYSQL_DUMP_FILE_NAME
docker stop $MYSQL_CONTAINER_NAME
# keep the data directory in a new container
docker run -d --name $ALPINE_CONTAINER_NAME -v mysql_data:/mysql_data $ALPINE_IMAGE
# copy the directory to local
docker cp $ALPINE_CONTAINER_NAME:/mysql_data .
# build image with the data(look at the dockerfile)
docker build -t $DOCKER_IMAGE_TAG .
# push it to repo
docker push $DOCKER_IMAGE_TAG
Quite frankly I don't understand the need for copying data to an apline container and then back to the local machine, but the DevOps said it's required because this is being handled by the gitlab ci.
And this is a script that runs postman collections using newman cli in which I start and stop a db container with that image for each test:
for filename in ./collections/*.json; do
# run test symfony test db container
docker run --name "$dbContainerName" --network="$networkName" -d "$dbImageName" > /dev/null
# # sleep
sleep 5
# # run the collection
newman run "$filename" -d parameters.json
returnCode=$?
# add test and result to log
nameWithoutPath="${filename##*/}"
name="${nameWithoutPath%.postman_collection.json}"
tests+=("$name")
testResults+=($returnCode)
# stop and remove the symfony test db container
docker stop "$dbContainerName" > /dev/null
docker rm "$dbContainerName" > /dev/null
done

Related

Move MySQL /var/lib/mysql to shared volume

I'm running a container with MySQL 8.0.18 in Docker on a Synology nas. I'm just using it to support another container (Mediawiki) on that box. MySQL has one volume mounted at path /var/lib/mysql. I would like to move that to a shared volume so i can access it with File Station and also periodically back it up. How can i move it to the docker share shown below without breaking MySQL?
Here are available shares on the Synology nas.
Alternatively, is there a way i can simply copy that /var/lib/mysql to the shared docker folder? That should work as well for periodic backups.
thanks,
russ
EDIT: Showing result after following Zeitounator's plan. Before running the docker command (2.) i created the mediawiki_mysql_backups and 12-29-2019 folders in File Station. After running 2. and 3. all the files from mysql are here and i now have a nice backup!
Stop your current container to make sure mysql is not running.
Create a dummy temp container where you mount the 2 needed volumes. I tend to user busybox for this kind of tasks => docker run -it --rm --name temp_mysql_copy -v mediaviki-mysql:/old-mysql -v /path/to/ttpe/docker:/new-mysql busybox:latest
Copy all files => cp -a /old-mysql/* /new-mysql/
Exit the dummy container (which will cleanup by itself if you used my above command)
Create a new container with mysql:8 image mounting the new folder in /var/lib/mysql (you probably need to do this in your syno docker gui).
If everything works as expected, delete the old mediaviki-mysql volume

Can i build a already local build mysql Database with the mysql Dockerfile?

i am completely new to Docker and everything that has to do with it. In my last semester i build a mysql Database locally with mysql Workbench and connected a java project to it. This year i need to make this run in a Docker Container. I have pulled the Dockerfile from GitHub and i am using Portainer to manage Docker.
My teacher wants the following:
He wants me to put my code in a repository which he created for me
Then he wants to pull my project, which should include a Dockerfile, so that he don't needs to manually rebuild my mysql Database.
So how can i do this? Do i need to change the mysql Dockerfile? Or should i use the default one and then initialize my Database in my javacode?
This i my first post here on stackoverflow and i am not that advanced in programming (only 2 years experience with java), so if i can give you any more information please let me know. I hope this is the right way to post questions here.
I am thankful to everyone helping me out!
Greets, Luciore
You need to place your sql file also on GitHub, and it should be accessible from your docker environment.
When a container is started for the first time, a new database with
the specified name will be created and initialized with the provided
configuration variables. Furthermore, it will execute files with
extensions .sh, .sql and .sql.gz that are found in
/docker-entrypoint-initdb.d. Files will be executed in alphabetical
order.
docker-hub-mysql
And yes, Also you need to modify your Dockerfile, Here is the example.
This will build mysql8 based docker image, will download sql file from GitHub and will initiate DB name classicmodels mean anything a valid SQL file will be executed on boot.
FROM mysql:8
RUN apt update && apt install curl -y
RUN curl -0 https://raw.githubusercontent.com/Adiii717/doctor-demo-app/master/sample_database.sql > /docker-entrypoint-initdb.d/sampledb.sql
Build the docker image
docker build -t mysql8 .
Run the docker container
docker run --rm --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -it mysql8

How do I backup data from MySQL container into a shared volume?

If I'm working with a containerized MySQL database that wasn't originally run with shared volume options, what's the easiest way to sort of externalize the data? Is there still a way to modify the container so that it shares its data with the Docker host in a specified directory?
Note: if you're still having problems with this question, please comment so I can improve it further.
Official Docker documentation provides a great overview on how to backup, restore, or migrate data volumes. For my problem, in particular, I did the following:
Run a throw-away Docker container that runs Ubuntu, shares volumes with currently running MySQL container, and backs up database data in local machine (as described in the overview):
docker run --rm --volumes-from some-mysql -v /path/to/local/directory:backup ubuntu:15.10 tar cvf /backup/mysql.tar /var/lib/mysql
(The official MySQL Docker image uses /var/lib/mysql for storing data.)
The previous step will result in creation of /path/to/directory/mysql.tar in the Docker host. This can now be extracted like:
tar -xvf mysql.tar
(Assuming cd /path/to/directory). The resulting directory (/var/lib/mysql) can now be used as shared volume with same instance, or any other instance of containerized MySQL.

Installation of system tables failed! boot2docker tutum/mysql mount file volume on Mac OS

I have trouble mounting a volume on tutum/mysql container on Mac OS.
I am running boot2docker 1.5
When I run
docker run -v $HOME/mysql-data:/var/lib/mysql tutum/mysql /bin/bash -c "/usr/bin/mysql_install_db"
i get this error
Installation of system tables failed! Examine the logs in /var/lib/mysql for more information.
Running the above command also creates an empty $HOME/mysql-data/mysql folder.
The tutum/mysql container runs smoothly when no mounting occurs.
I have successfully mounted a folder on the nginx demo container, which means that the boot2docker is setup correctly for mounting volumes.
I would guess that it's just a permissions issue. Either find the uid of the mysql user inside the container and chown the mysql-data dir to that user, or use a data container to hold the volumes.
For more information on data containers see the official docs.
Also note that as the Dockerfile declares volumes, mounting is taking place whether or not you use -v argument to docker run - it just happens in a directory on the host controlled by Docker (under /var/lib/docker) instead of a directory chosen by you.
I've also had a problem starting mysql docker container with error "Installation of system tables failed". There was no changes on the docker image, and there was no recent update on my machine or docker. One thing I was doing differently was that using images that could take up or more than 5GB memory on testing.
After cleaning dangling images and volumes, I was able to start mysql image as usual.
This blog seems to have a good instructions and explains all variations of clean up with docker.

Can I use fig to initialise a persisting database in docker?

I am trying to automate the installation and running of set of linked docker containers using fig. The configuration is composed of a container running RStudio linked to a container running MySQL, such that I can query the MySQL database from RStudio.
On first run, I would like to create the MySQL container from the base MySQL image, and populate it with a user and database. From the command line, something like this:
#Get the latest database file
wget -P /tmp http://ergast.com/downloads/f1db.sql.gz && gunzip -f /tmp/f1db.sql.gz
#Create the database container with user, password and database
docker run --name ergastdb -e MYSQL_USER=ergast -e MYSQL_ROOT_PASSWORD=mrd -e MYSQL_DATABASE=f1db -d mysql
#Populate the database
docker run -it --link=ergastdb:mysql -v /tmp:/tmp/import --rm mysql sh -c 'exec mysql -h$MYSQL_PORT_3306_TCP_ADDR -P$MYSQL_PORT_3306_TCP_PORT -uergast -pmrd f1db < /tmp/import/f1db.sql'
#Fire up RStudio and link to the MySQL db
docker run --name f1djd -p 8788:8787 --link ergastdb:db -d rocker/hadleyverse
If I could get hold of a database image with the data preloaded, I guess that something like the following fig.yml script could link the elements?
gdrive:
command: echo created
image: busybox
volumes:
- "~/Google Drive/shareddata:/gdrive"
dbdata:
image: mysql_preloaded
environment:
MYSQL_USER=ergast
MYSQL_ROOT_PASSWORD=mrd
MYSQL_DATABASE=f1db
rstudio:
image: rocker/hadleyverse
links:
- dbdata:db
ports:
- "8788:8787"
volumes_from:
- gdrive
My question is, can I use a one-shot fig step to create the dbdata container, then perhaps mount a persistent volume, link to it and initialise the database, presumably as part of an initial fig up. If I then start and stop containers, I don't want to run the db initialisation step again, just link to the data volume container that contains the data I previously installed.
I also notice that the MySQL docker image looks like it will support arbitrary datadir definitions (Update entrypoints to read DATADIR from the MySQL configuration directly instead of assuming /var/lib/docker). As I understand it, the current definition of the MySQL image prevents mounting (and hence persisting) the database contents within the database container. I guess this might make it possible to create a mysql_preloaded image, but I don't think the latest version of the MySQL docker script has been pushed to dockerhub just yet and I can't quite think my way to how fig might then be able to make use of this alternative pathway?
Some options:
Edit the fig.yml to run a custom command that is different than the default image command/entrypoint.
From http://www.fig.sh/yml.html (example):
command: bundle exec thin -p 3000
Start the container locally, modify it and then commit it as a new image.
Modify the MySQL image docker-entrypoint.sh file to do your custom initialization.
https://github.com/docker-library/mysql/blob/567028d4e177238c58760bcd69a8766a8f026e2a/5.7/docker-entrypoint.sh
Couldn't you just roll your own version of the MySQL docker image? The official one from MySQL "upstream" is available at https://github.com/mysql/mysql-docker/blob/mysql-server/5.7/Dockerfile
What if you simply make your own copy of that, remove the VOLUME line (line 11) and then you can
docker build -t my_mysql .
docker run -d --name=empty_db my_mysql ...
# add data to the database running in the container
docker commit empty_db primed_db
docker rm -v empty_db
docker run -d --name=instance1 primed_db
docker run -d --name=instance2 primed_db
which should leave you with two running "identical" but fully isolated instances.