Friday, 9 October 2020

Persisting data in a MySQL Docker container

For my first steps into the wonderful world of Docker I created an image for a MySQL database that should start MySQL and create a database called 'entity' along with a user than can access this database.

The Dockerfile is simple enough (and can be found in the GitHub repo). The python packages are installed to enable a local health check. This check will simply try to connect to the database server. If it fails this will signal Docker that the container is not healthy.

We also set an environment variable for the default database but this will be overridden in the docker-compose.yml file. Setting the workdir is done to store the small python app.

Any sql files we copy into docker-entrypoint-initdb.d/ will be executed when MySQL starts. In our case that will be a single script that sets grants for a user.


FROM mysql

RUN apt-get update && apt-get install -y --no-install-recommends python3 python3-pip && rm -rf /var/lib/apt/lists/*
RUN python3 -m pip install PyMySQL
RUN python3 -m pip install PyMySQL[rsa]

# default database, can be replaced in docker-compose
ENV MYSQL_DATABASE default

WORKDIR /usr/src/app

COPY dbserver_entity/sql-scripts/ /docker-entrypoint-initdb.d/

COPY common/dbping.py .

HEALTHCHECK CMD ["python3", "./dbping.py"] 

In the docker-compose.yml file we define the service that uses the image we created. The root password is kept in a local file that is converted to a secret. This way we can later change thus secret in a secure manner when we deploy to a swarm.

In the secrets section of the service definition we make this secret available to the service. It will be made available in the /run/secrets directory inside the container. In the environment section we set the appropriate environment variable to point to the file.


# define secrets
secrets:
    mysql-root:
        file: mysqlrootpassword.txt

All other environment variables that contain information that is not so secret, are sourced from a file called .env . Because we use this specific filename those same variables will be available inside the docker-compose.yml file itself, which might be handy.


# define services (we pass .env to have the same environment vars inside the compose file as in the containers
services:

  dbserver_entity:
    image: dbserver_entity
    # no exposed ports
    networks:
      - appnetwork
    # use a secret (and the database name is just passed on from the .env file)
    environment:
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql-root
    env_file: .env
    # give access to a secret
    secrets:
      - mysql-root
    volumes:
      - "data:/var/lib/mysql"
    restart: on-failure
    build:
      context: .
      dockerfile: dbserver_entity/Dockerfile

The final bits relevant for the dbserver_entity service are the network (so that the other services can access this network specifically) and a named volume. Naming a volume will create a block of storage that is managed separately by Docker from the containers. In the service definition we encountered before, the volumes section made sure we mount this volume on the directory where MySQL stores its databases, i.e. /var/lib/mysql, and it is this combination of a named volume and a mount that makes our database persistent. Now we can safely bring our services down with docker-compose down or even lose power and on restart we would still have our databases.


networks:
  appnetwork:

volumes:
    data:

No comments:

Post a comment