antun
antun

Reputation: 2287

How can I re-create a docker mysql image that uses a volume?

I am using docker-compose to create a mysql container, and create a database in it. If I delete the database manually, no matter what I do, I can't re-create the container and force it to re-create the database too.

Here's my docker-compose.yml:

version: '3'
services:
  mysql:
    container_name: mysqltest-db
    image: mysql:5.6.51
    volumes:
      - ./my_volume:/var/lib/mysql
      - ./data/init.sql:/docker-entrypoint-initdb.d/1.init.sql
    command: --default-authentication-plugin=mysql_native_password
    ports:
      - 3306:3306
    environment:
       MYSQL_ROOT_PASSWORD: root
       MYSQL_DATABASE: my_database
       MYSQL_USER: my_user
       MYSQL_PASSWORD: mypass

volumes:
  my_volume: {}

The init.sql script simply creates a database:

CREATE DATABASE IF NOT EXISTS my_database;
GRANT ALL PRIVILEGES ON my_database.* TO my_user@'%' IDENTIFIED BY 'mypass';
FLUSH PRIVILEGES;

The first time I run docker-compose up, everything works fine. I can log into the mysql with docker container exec -it mysqltest-db bash, then log into mysql with mysql -u root -proot, and run show databases. Sure enough, my_database is there:

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| my_database        |
| mysql              |
| performance_schema |
+--------------------+
4 rows in set (0.01 sec)

Next I manually delete the database by running drop database my_database.

Then I ctrl+c to kill the container, and run docker-compose down -v. As I understand it, the -v should destroy the volume that contained the mysql data. I check that it's really gone by running docker volume ls, and confirm that the dockertest_my_volume volume is no longer present.

Then I run docker-compose pull && docker-compose up -V --force-recreate to re-create the container. This is where I'm stuck. When I login to the container, and then to mysql, and run show databases again, the my_database is still gone:

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
+--------------------+
3 rows in set (0.01 sec)

What am I missing to force Docker to completely rebuild from scratch, so that the mysql database gets created the second time?

There is clearly a big difference in the amount of log output between the first and second runs. However in both cases, it seems to create a volume:

$-> docker-compose up -V --force-recreate
Docker Compose is now in the Docker CLI, try `docker compose up`

Creating network "dockertest_default" with the default driver
Creating volume "dockertest_my_volume" with default driver
Creating mysqltest-db ... done
Attaching to mysqltest-db

Upvotes: 3

Views: 2111

Answers (2)

David Maze
David Maze

Reputation: 158714

Docker has two kinds of built-in storage, named volumes and bind mounts. The syntax of these is very similar, but a named volume attaches Docker-managed storage to a container, and a bind mount directly attaches some host directory instead.

Your docker-compose.yml file says:

volumes:
  - ./my_volume:/var/lib/mysql

This is the bind-mount syntax; it names a path directly on your host system. (If you look you can see the my_volume directory and the MySQL data files within it.)

The MySQL image startup sequence knows to look in the /var/lib/mysql directory, and if that directory is empty it does the first-time initialization sequence. If there's a MySQL installation there, it will not re-run the initialization scripts, even if you've deleted the content there. This is the same behavior if it's a named volume or a bind mount.

In your case, even though you've deleted the database, the MySQL system files are still there. You can force it to start from scratch:

docker-compose rm --stop mysql
rm -rf my_volume
docker-compose up -d

Upvotes: 3

Software Engineer
Software Engineer

Reputation: 16100

Wow, an easy one at last. Docker volumes are names, not paths. You have:

volumes:
      - ./my_volume:/var/lib/mysql

Which mounts a path on your local filesystem relative to the context (the folder where you started the container), ./my_volume, into the container at path /var/lib/mysql. What you really want is this:

volumes:
      - my_volume:/var/lib/mysql

Which contains a volume reference (a named volume). This creates/mounts the volume my_volume into /var/lib/mysql within the container.

You can diagnose the problem because you'll have a directory called my_volume next to the docker-compose.yaml file. Another way to check this is to execute docker volume ls while the container is running and you'll notice that there is no my_volume volume (when running your code, not mine -- when you run mine it'll work ;).

Easily fixed though :)

Upvotes: 2

Related Questions