HK15
HK15

Reputation: 807

The most correct way to run MyBatis migration from a Docker container

I'm trying to figure out how and when to run the mybatis schema migrations from a Docker container deployed inside a Docker Swarm. I mean: I need the most correct way to do that.

At the moment We build a Docker container from a Dockerfile

FROM ubuntu:18.04
RUN apt-get update && apt-get install -y \
        openjdk-11-jre \
        openjdk-11-jdk \
        maven
    
    ARG JAR_FILE=target/*.jar
    COPY ${JAR_FILE} app.jar
    COPY start.sh start.sh
    
    RUN chmod +x start.sh
    ENTRYPOINT ["/bin/sh","start.sh"]

then the start.sh script contains

mvn resources:resources migration:up -Dmigration.path="target/classes/migrations" -Dmigration.env=development -Papply_migrations
java -jar /app.jar

But in this way we have to build an image from Ubuntu, install Maven and lunch the migrations with the environment "hardcoded" into the start.sh file, so We need different files from different envs.

What do you think is the most correct method to run these scheme migrations during the build/deployment process?

Thanks in advance.

EDIT: I've found useful the solution to use the mybatis migration docker image found on DockerHub and posted by @h3adache but still to have an Issue trying to execute it on a DockerSwarm: the issue is related to the volume mounted between the host folder with mybatis migrations files and the container folder "/migration"

-v $PWD:/migration

My docker-compose.yml is

  mybatis-migration:
    image: mybatis/migrations
    volumes:
      - ./mybatis-migrations:/migration
    command:
      - up

It works fine locally against a dockerized MySQL but fails during the deploy with a GitLab pipeline.

The ./mybatis-migrations folder is, obviously, on my localhost when I checkout the code and It is in the build path of the GitLab repository when the GitLab runner builds everything but is not on the DockerSwarm host so it's unable to find that directory.

This is the error message:

invalid mount config for type "bind": bind source path does not exist

How can I fix this?

Upvotes: 1

Views: 859

Answers (2)

h3adache
h3adache

Reputation: 1386

I suggest you follow the guide I posted on medium which uses the official Mybatis Migrations docker hub image

It gives you an 'out of the box' docker experience and allows you to target different environments (as mentioned in my post).

tl;dr

  1. Use https://hub.docker.com/r/mybatis/migrations for your base image.
    • This gives you migration commands out of the box
  2. Instead of using the .gitlab-ci.yml from the post, you can add the action (eg. migrate up) as your docker image entrypoint or command.
  3. You can control env or directly affect parameters used by using docker --env

eg.

docker run \
--rm \
--env "MIGRATIONS_URL=jdbc:mysql://$(hostname):3306/mb_migration" \
-v $PWD:/migration \
-it mybatis/migrations status

Upvotes: 1

Let's look to the problem with maven first. I understand that you (quite rightfully) don't want to install maven (and probably JDK).

There are two ways to achieve what you need.

Runtime Schema Upgrade

You can run migration right from your application when it starts. It may be run from the main method or from the custom javax.servlet.ServletContextListener if you deploy a web application.

Here's how it may look like:

new UpOperation().operate(
  new DataSourceConnectionProvider(dataSource),
    new JavaMigrationLoader("mycompany.migration.script"), null, null);

Check the documentation with details how to configure this.

This would require to only include mybatis migration to the dependencies of your project (which you might already have).

Using mybatis migrations library directly

The other way is to run mybaits migration directly that is without maven. This can be done by installing the library inside docker as described in the documentation. Note that you only need the libraryr itself and JRE, so no JDK and maven is required.

Then you can run migration using migrate script that is part of the distribution archive.

Environment

In order to fix this you can pass that as a parameter to the docker container that runs start.sh. One option is to use environment variable via --env option for docker service create or docker run. The variable passed this way can be accessed as a regular environment variable in linux in your start.sh.

Upvotes: 1

Related Questions