Flywayを触ってみる(コマンドライン)

はじめに

データベースマイグレーションツールの一つであるFlywayを触ってみます。
業務で使用しているものの、私がやっていることはDBに変更があった際のマイグレーションファイルを作成する程度なので、改めて使用方法などを整理できればと思います。

なお、私の仕事上ではSpring bootと併用していますが、今回はSpringプロジェクトを作成するほどのことでもないので、コマンドライン上でのお試しとします。
Springとの併用はそのうちやります・・。

マイグレーションとは

データベースの変更を記録することで、データベースの変更を容易にするためのもの・・と理解しています。

やってみる

Flywayも、他のツールに漏れず公式ドキュメントが充実しているので、そちらを参考に実施していきます。

インストール

先述したように、今回はコマンドツールを用いてFlywayを利用していきます。
また、今回は学習用ですので、Community Edition(無料)をインストールします。 こちらから、適宜環境にあったものをダウンロードしましょう。

FlywayをPATHに通す

FlywayをPATHに通します。私はfishというシェルを使用しているため、~/.config/fish/config.fishに以下の記述を追記しました。

set PATH /Users/ochiaisho/devTools/flyway-7.8.2 $PATH

zshや、bashなど、各自の環境に合わせてPATHを通すようにしましょう。

flyway.confの修正

データベース接続情報をconf/flyway.confに記述します。
私の場合は以下のようになります。

flyway.url=jdbc:mysql://localhost:3306/flyway
flyway.user=root
flyway.password=password

マイグレーションファイルの作成

実際にデータベースへ反映するためのマイグレーションファイルを作成します。
今回は、新しくUserというテーブルを作成するDDLを追加します。

create table USER (
    ID int not null,
    NAME varchar(100) not null
);

ここまでで、準備はあらかた終了です。

flyway migrate

それでは実際にflyway migrateを実行してみましょう。

$ flyway migrate

成功すると、以下のようなレスポンスとなります。
(バージョンやデータベース名などは適宜読み替えてください。)

Flyway Community Edition 7.8.2 by Redgate
Database: jdbc:mysql://localhost:3306/flyway (MySQL 8.0)
Successfully validated 1 migration (execution time 00:00.017s)
Creating Schema History table `flyway`.`flyway_schema_history` ...
Current version of schema `flyway`: << Empty Schema >>
Migrating schema `flyway` to version "1 - create user"
Successfully applied 1 migration to schema `flyway`, now at version v1 (execution time 00:00.040s)

成功すると、Userテーブルと、flyway_schema_historyというテーブルが作成されます。

mysql> show tables;
+-----------------------+
| Tables_in_flyway      |
+-----------------------+
| flyway_schema_history |
| USER                  |
+-----------------------+
2 rows in set (0.00 sec)

Userテーブルは自分で作成したテーブルなのでいいとして、flyway_schema_historyというテーブルはなんなのでしょうか?
ということで、もう少し続けます。

flyway_schema_historyとは

平たく言うと、flyway_schema_historyはテーブルのマイグレーション(変更)の記録を保持するテーブルとなります。 見てみる方が早いと思いますので、とりあえず作成されたテーブルの中身を調べてみることにしましょう。

mysql> select * from flyway_schema_history;
+----------------+---------+-------------+------+---------------------+------------+--------------+---------------------+----------------+---------+
| installed_rank | version | description | type | script              | checksum   | installed_by | installed_on        | execution_time | success |
+----------------+---------+-------------+------+---------------------+------------+--------------+---------------------+----------------+---------+
|              1 | 1       | create user | SQL  | V1__create_user.sql | 1761476051 | root         | 2021-05-09 14:48:27 |             10 |       1 |
+----------------+---------+-------------+------+---------------------+------------+--------------+---------------------+----------------+---------+
1 row in set (0.00 sec)

今回、Userというテーブルを作成した履歴が残っているのがわかるかと思います。
履歴が残るということを確認したいので、試しに別のテーブルを作成してみましょう。

マイグレーションファイルを追加する

今回も、テーブル追加のためのマイグレーションファイルを作成しようと思います。
sqlディレクトリ配下に、V2__create_second_table.sqlというファイル名で、適当なテーブルを作成しようと思います。

create table SECOND_TABLE(
    ID int not null,
);            

ファイルが作成できたら、再度flyway migrateを実行してみます。

$ flyway migrate

成功すると以下のようなメッセージが返ってきます。

Flyway Community Edition 7.8.2 by Redgate
Database: jdbc:mysql://localhost:3306/flyway (MySQL 8.0)
Successfully validated 2 migrations (execution time 00:00.018s)
Current version of schema `flyway`: 1
Migrating schema `flyway` to version "2 - create second table"
Successfully applied 1 migration to schema `flyway`, now at version v2 (execution time 00:00.050s)

この状態で再度データベースの状態を確認してみましょう。

mysql> show tables;
+-----------------------+
| Tables_in_flyway      |
+-----------------------+
| flyway_schema_history |
| SECOND_TABLE          |
| USER                  |
+-----------------------+
3 rows in set (0.00 sec)

show tablesで確認すると、SECOND_TABLEが作成されていることがわかりますね。
次に、flyway_schema_historyの中身も確認してみましょう。

mysql> select * from flyway_schema_history;
+----------------+---------+---------------------+------+-----------------------------+------------+--------------+---------------------+----------------+---------+
| installed_rank | version | description         | type | script                      | checksum   | installed_by | installed_on        | execution_time | success |
+----------------+---------+---------------------+------+-----------------------------+------------+--------------+---------------------+----------------+---------+
|              1 | 1       | create user         | SQL  | V1__create_user.sql         | 1761476051 | root         | 2021-05-09 14:48:27 |             10 |       1 |
|              2 | 2       | create second table | SQL  | V2__create_second_table.sql |  942681622 | root         | 2021-05-09 15:17:43 |             15 |       1 |
+----------------+---------+---------------------+------+-----------------------------+------------+--------------+---------------------+----------------+---------+
2 rows in set (0.00 sec)

レコードが追加されていますね。

このような形で、Flywayが実行したマイグレーションの履歴がどんどん積み重なっていくことになります。

おまけ

実はSECOND_TABLEを追加しようとしたところ、sql syntax errorが発生したため、マイグレーションに失敗した、という事象が発生していました。
そこで、SQLを修正し、再度flyway migrateを実行したところ、さらに別のエラーが発生・・ということも発生したんですね。つまり、

  1. flyway migrateで、sql の文法エラーが発生
  2. sqlファイルの修正
  3. flyway migrateでさらにエラーが発生

という流れです。この時のエラー解消方法を念のため記録しておこうと思います。

エラー内容

まずは実際のエラー内容から。
(なお、実際はsql syntax errorも出ていましたが、直接Flywayとは関係ないので割愛しています。)

ERROR: Validate failed: Migrations have failed validation
Detected failed migration to version 2 (create second table). Please remove any half-completed changes then run repair to fix the schema history.
Need more flexibility with validation rules? Learn more: https://flywaydb.org/custom-validate-rules

Please remove any half-completed changes then run repair to fix the schema history.ということなので、不完全な状態の変更を削除してから再実行するのが正しいようです。 ここで、上記の1.でエラーが発生した直後のflyway_schema_historyを確認してみます。

+----------------+---------+---------------------+------+-----------------------------+------------+--------------+---------------------+----------------+---------+
| installed_rank | version | description         | type | script                      | checksum   | installed_by | installed_on        | execution_time | success |
+----------------+---------+---------------------+------+-----------------------------+------------+--------------+---------------------+----------------+---------+
|              1 | 1       | create user         | SQL  | V1__create_user.sql         | 1761476051 | root         | 2021-05-09 14:48:27 |             10 |       1 |
|              2 | 2       | create second table | SQL  | V2__create_second_table.sql | -371576338 | root         | 2021-05-09 15:15:48 |             17 |       0 |
+----------------+---------+---------------------+------+-----------------------------+------------+--------------+---------------------+----------------+---------+

実は、マイグレーションに失敗しても、flyway_schema_historyにレコードは追加されます。
ここでさらにレコードの内容をみてみると、successというカラムがマイグレーションの成否を表すカラムであることに気づくと思います。
1が成功、0が失敗ですね。 そこで、マイグレーションに失敗しているレコードであるsuccess=0のレコードを削除してから再度flyway migrateを実行することでエラーが解消され、マイグレーションが成功しました。

終わりに

マイグレーションツールを使用すれば、データベースに対する変更が正しい順序で反映されるので、ちゃんと使用できれば便利ですね。今回はやりませんでしたが、例えばカラムの削除・カラム定義の変更など、テーブルの初期状態からの変化を正しい順序で実行していくというのは手作業ではどうしても難しいシーンもあると思いますので、そういったことへの対策にもなりそうです。

Dockerを学ぶ(第3回)

はじめに

今回は、アプリケーションを修正してイメージの再作成を行います。

アプリケーションの修正

前回cloneしたアプリについて、TODOが0件の場合のメッセージを修正します。

修正後、最後イメージの作成とアプリケーションの開始を行います。 前回と同様のコマンドを実行しましょう。

  • ビルド
$ docker build -t getting-started .
  • コンテナスタート
$ docker run -dp 3000:3000 getting-started

前回の続きを実施している場合、この時点でエラーが発生します。
前回docker runしたコンテナが現在も動いているためです。これは、前回docker runしたコンテナと今回docuer runするコンテナとでリッスンするPORTが重複しているためです。
今回は、前回スタートしたコンテナを削除することでこの問題を解決します。

コンテナの削除

コンテナを削除するためには、以下の手順を踏む必要があります。

  1. コンテナIDの取得
  2. コンテナの停止
  3. コンテナの削除

それでは、順に実施していきましょう。

コンテナIDの取得

まずはじめに、docker psを使用して、コンテナのIDを取得します。

$ docker ps

すると結果が以下のようになると思います。

CONTAINER ID   IMAGE                    COMMAND                  CREATED          STATUS          PORTS                    NAMES
88c0598817c1   5b0fd87f541d             "docker-entrypoint.s…"   32 minutes ago   Up 32 minutes   0.0.0.0:3000->3000/tcp   jovial_merkle
5261b924c97e   docker/getting-started   "/docker-entrypoint.…"   2 hours ago      Up 2 hours      0.0.0.0:80->80/tcp       unruffled_hopper

1番左端のCONTAINER IDがコンテナのIDとなりますので、このIDをメモなりしておきましょう。

コンテナの停止

先ほどの手順で取得したIDを使用して、コンテナを停止します。

$ docker stop <docker-id>

コンテナの削除

$ docker rm <docker-id>

ここまで出来たら、再度docker psを実施してみましょう。コンテナが削除されていることがわかると思います。

コンテナの起動

古いコンテナが削除できましたので、最初に再作成したイメージを使用してコンテナを開始しましょう。

$ docker run -dp 3000:3000 getting-started

もう一度http://localhost:3000にアクセスしてみましょう。
タスクが0件のときのメッセージが変更されているはずです!!

Dockerを学ぶ(第2回)

はじめに

今回もDocker公式ドキュメントを実施してみます。
お次はどうやらNode.jsを使用したサンプルアプリケーションを使ったチュートリアルになるようです。
とはいえ、JavaScriptの経験がなくとも問題ないようですよね、安心して実施していきます。(?)

サンプルアプリケーションの取得

以下のコマンドを実行します。
なお、こちらのリポジトリはgetting-startedで使用する全てのコードが含まれていますが、今回使用するのは/appディレクトリとなります。

$ git clone https://github.com/docker/getting-started.git

コンテナイメージのビルド

ここから、/appをコンテナ化するためのイメージを作成していきます。

Dockerfileの作成

アプリケーションをビルドするために、まずはDockerfileを作成します。
Dockerfileには、コンテナイメージで使用される手順(指示)スクリプトをテキストベースで記述します。
それでは実際に作成していきましょう。
Dockerfilepackage.jsonがあるディレクトリと同じディレクトリに作成します。(/appディレクトリ内に作成する)

# syntax=docker/dockerfile:1
 FROM node:12-alpine
 RUN apk add --no-cache python g++ make
 WORKDIR /app
 COPY . .
 RUN yarn install --production
 CMD ["node", "src/index.js"]

この時、.txtなどの拡張子がつかないように注意します。ファイル名は単にDockerfileとしてください。

docker build

Dockerfileが作成できたら、ターミナルからdocker buildを実行します。 ターミナルで、app ディレクトリへ移動するのを忘れずに。 完全なコマンドは以下となります。

$ docker build -t getting-started . 

(最後のピリオドを忘れずに!!)

今回もコマンドについて解説します。

フラグ 解説
-t getting-started -t をつけると、イメージにタグ名をつけることができます。
. (最後のピリオド) Dockerfileがカレントディレクトリにあることを表します。

Dockerfileに記述されている内容についてはいずれまとめます・・・。

コンテナの開始(docker run)

イメージが完成したので、アプリケーションを実行してみます。

$ docker run -dp 3000:3000 getting-started

(コマンド中の-dp-d -pと同じ意味です) コマンド実行が成功しましたら、ブラウザでhttp://localhost:3000 を見てみましょう。 以下のようになれば成功です!!

f:id:sho03:20210501173605p:plain

終わりに

今回はここまでとなります。次回はアプリケーションの更新を行います。

Dockerを学ぶ

はじめに

正直何もわかっていないので、Dockerを学ぼうと思います。 とりあえず何はともあれ公式ドキュメントを見てみるのが1番だろう、ということで公式ドキュメントのget-startedを参考にしています。

なお、実際に使用してみることを第一目標にしているので、Dockerが解決する問題や、Dockerを使用することのメリットは本稿では扱いません。
(とはいえこの辺りを見れば大体書いています。)

Dockerのダウンロード

こちらを参考に。

初めてのdocker run

以下のコマンドを実行します。

$ docker run -d -p 80:80 docker/getting-started

一応、フラグについて解説します。

フラグ 解説
-d バックグラウンドでの実行
-p 80:80 ポート80をコンテナーの80ポートにマッピング
docker/getting-started 使用するイメージの指定

Docker Dashboard

Docker Dashboardを使用すると、先ほどdocker runしたコンテナーが起動しているのが分かります。(下記参照)

f:id:sho03:20210501162436p:plain

コンテナとは?

ローカルマシンの持つ他のプロセスから独立しているプロセスです。

コンテナイメージとは?

先ほどdocker run -d -p 80:80 docker/getting-startedで指定したdocker/getting-startedがそれにあたります。
コンテナは、独立したファイルシステムを持ちます。そのファイルシステム自体はdocker imageから提供されるものであり、docker imageはコンテナのファイルシステムおよびアプリケーション(コンテナ)の実行に必要な依存性・スクリプト・バイナリなどの含んでいます。
詰まるところ、コンテナを起動するために必要なもの全てが揃っている、という解釈をしています。

Cloud run へデプロイする

Google Cloud Platformの1サービスであるCloud Runを使用してJavaアプリケーションをデプロイします。
手順についてはこちらを参考にしていますので、興味のある方は参照してください。

0. 事前準備

こちらを参考に、Cloud SDKをインストールします。

1. プロジェクトの作成

GCPのコンソールに移動してプロジェクトを作成します。
プロジェクト名は任意ですが、とりあえず「example java project deploy」とでもしておきましょう。

f:id:sho03:20210429130527p:plain

2. サンプルアプリケーションの作成

実際にデプロイするサンプルアプリケーションを作成します。
こちらも手順通りに、適当なディレクトリにSpringアプリケーションを構築しましょう。

curl https://start.spring.io/starter.zip \
    -d dependencies=web \
    -d javaVersion=1.8 \
    -d bootVersion=2.3.3.RELEASE \
    -d name=helloworld \
    -d artifactId=helloworld \
    -d baseDir=helloworld \
    -o helloworld.zip
unzip helloworld.zip
cd helloworld

次に、作成されたアプリケーションがリクエストを処理することができるようにします。HellloworldApplication.javaを修正します。

3. アプリのコンテナ化

Dockerfile.dockerignoreファイルを作成します。 ファイルの内容についてはこちらをご確認ください。

4. Container Registryへのアップロード

Container RegistryはGCPのサービスの1つで、コンテナを登録することができます。
のちに実施するCloud RunへのデプロイはContainer Registryに登録されているコンテナをデプロイする、ということになります。

それでは、以下のコマンドを実行します。この時、Dockerfileが存在しているディレクトリでコマンド実行することに注意してください。

gcloud builds submit --tag gcr.io/{PROJECT-ID}/helloworld

{PROJECT_ID}には手順で作成したプロジェクトのIDを入れてください。 完全なコマンドは以下のようになります。

gcloud builds submit --tag gcr.io/example-java-project-deploy/helloworld

さて、ここまでできるとコンソール上から実際にContainer Registryへコンテナが登録されているはずですので確認してみます。

f:id:sho03:20210429133509p:plain

できました!!

5. Cloud Runへのデプロイ

さて、ここまでできればあとはデプロイするだけですね。
以下のコマンドを実行します。

gcloud run deploy --image gcr.io/example-java-project-deploy/helloworld --platform managed

コマンドを実行すると、実行中に

  1. サービス名
  2. リージョン
  3. 未認証の呼び出し許可

について入力を求められますので適宜入力してください。

実行が完了するとアクセスするためのURLがターミナルに表示されますので、ブラウザで開くと画面にHelloWorld!が表示されます。

6. 補足

デプロイしたアプリケーションにアクセスがある場合など、課金が発生しますので、不要であればプロジェクトごと削除してしまってください。

終わりに

手順通りに実施すればなんてことはなく簡単にできました。
コンテナ技術もクラウドサービス技術も知識がほぼないのでこれから学習していければと思います。

とりあえずDockerあたり勉強するかなー。

react-collapsibleを使ってアコーディオンを作成する

アコーディオン、Webでよく見かけますよね。
今回はreact-collapsibleを使用してFAQのようなアコーディオンを作成してみようと思います。

完成形

最初に完成形だけ置いときます。
f:id:sho03:20210220172807g:plain
とりあえずソースを見せろという方はこちらをご覧ください。

準備

まずは必要なパッケージのインストール

yarn add react-collapsible

簡単な例

1番単純な例を作成します。

import Collapsible from "react-collapsible";

export function AccordionSample() {

  return <Collapsible trigger={"click me!!"}>
      This is easiest Accordion!!
    </Collapsible>
}

f:id:sho03:20210220161322g:plain
上記のようなコンポーネントを作成することで、click me!!!の文字をクリックするとその下部にhello, collapsible!が出力されるのがわかると思います。

Collapsibleのオプション

Collapsibleアコーディオンの開閉を制御するtriggerオプションが必須になりますが、それ以外にもいくつかのオプションがあります。
すでに使用したtriggerオプションも含めていくつか紹介しようと思います。

アコーディオンの開閉動作に関わるオプション

trigger

アコーディオンの開閉のトリガー(契機)となる文字列またはReactNodeを指定します。前述した例では文字列を指定しましたが、ReactNodeを渡せるので、任意のコンポーネントも使用できます。

<Collapsible trigger={<AnyComponent/>}>
</Collapsible>

transitionTime

アコーディオンの開閉にかかる時間を指定します。単位はミリ秒です。
transitionTime={1000}

transitionCloseTime

アコーディオンが閉じる時の時間を指定します。transitionTimeと同様に単位はミリ秒です。
transitionCloseTime={1000}

アコーディオンのスタイルに関わるオプション

className

アコーディオンが閉じている時に適用されるCSSスタイルを指定します。なお、

openedClassName

アコーディオンが開いている時に適用されるCSSスタイルを指定します。

感想

かなり簡単にアコーディオンが実現できるので、今回作成したようなアコーディオンを作るには便利に感じました。

参考

公式ドキュメント