インフラ管理課 自動化チームの藤原です。
Wantedlyにテックブログが移行してからは、初の投稿になります。
以前、弊社コーポレートサイトにPythonの型アノテーションに関する記事を寄稿した際は、年間ビュー数がそこそこ上位だったようです。ご覧くださった皆様、ありがとうございます。
さて、私事で大変恐縮ではありますが10月末を以てlearningBOXを退職することといたしました。
退職面談の際に「ぜひ、退職前に一本記事を書いてくれ!」という謎の(?)ありがたいオファーを頂き、この記事を書いている次第です。あくまで退職エントリーじゃなくてテックブログです。
ぜひ、最後までお付き合いください。
そもそも、MySQL REST Serviceってなんだ
MySQLはOSSのRDBMSで、特にWebの開発を行っている方であれば一度は名前を耳にしていると思います。MyISAMストレージエンジンを搭載していた時代にはトランザクションをサポートしていないなどストロングスタイルな一面もありました。
そんなMySQLですが、近年のアップデートでは高機能化にも注力しています。
MySQL REST Service(MRS)はMySQL Routerの機能として現在ベータ提供されています。
MySQL RouterはinnoDBのロードバランシングなどを担当するミドルウェアで、MySQLのinnoDBクラスタを構成する際などに利用されるものです。
公式の解説記事にも簡単な構成図とセットアップ方法、MRSを利用することでできるようになることが掲載されていますが、MySQL Routerの用意するHTTPSエンドポイントへ直接RESTライクなアクセスを投げることで、データの読み書き操作を行えるというものです。データベースサーバを用意するだけで、簡単なREST APIで完結するアプリケーションについては構築できるというわけです。
個人的にはJAMStack構成で運用できるようなアプリケーションや、SPAのようにクライアントサイドで都度データfetchを行うWebアプリケーションに活用が期待できると感じています。
注意事項
ベータ機能で更新が日々行われているため、情報がすぐに古くなる可能性があります。ご了承ください。
また、きちんとしたドキュメントがおそらくVisual Studio Codeの拡張機能内にしかないため、かなり手探りで構築しています。
特にMySQL Routerに関しては公式の解説記事にあるバージョンのRouter(8.0.32)と執筆時点でのVS Code拡張機能で生成されるメタデータスキーマ (おそらく執筆時点最新版の8.0.33むけ) に互換性がなく、MySQL Routerが正常起動しませんでした。
はっきり言って現時点では試す価値はないと思いますが、面白そうなものにはチャレンジできるというチームの雰囲気をお伝えできればと思います。
実際にMySQL REST Serviceを試してみる
今回はMacBook Pro(M1 Pro)上のDockerで試しています。
この記事を参考にお手元の環境で試される場合は、アーキテクチャ差異にご注意ください。
1台構成でMySQLのサーバを用意する
まずは、MySQL Server(mysqld)を用意します。
今回は環境を簡単に再構築できるよう、Dockerで構築します。
Dockerfileでイメージを作成します。my.cnf
などで加筆が必要な場合はこのタイミングで送り込みます。
(イメージのアーキテクチャをこの時点で指定しています。必要がなければ FROMのplatformオプションを削除してください。)
FROM --platform=arm64 mysql:8.0.32-oracle
WORKDIR /root/
docker compose
で立ち上げます。後からコンテナが行方不明になるのでネットワークは明示します。
version: "3.7"
services:
sample-db:
build:
context: .
dockerfile: db.dockerfile
environment:
- MYSQL_ROOT_PASSWORD=S@mplePassword # 良い子はenvfile使ってください。
volumes:
- mrs-db:/var/lib/mysql # データを永続化します。
networks:
mrs-test:
ipv4_address: 172.31.4.2
ports:
- 3306:3306 # ホストから127.0.0.1でアクセスできるように指定します。
volumes:
mrs-db:
networks:
mrs-test:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.31.4.0/24
コンテナを起動します。
docker compose up -d --build
MySQL Shell For VS CodeでMRSをセットアップ
続いて公式の解説記事を参考にMRSをセットアップします。
公式の解説記事ではVisual Studio CodeにMySQL Shell For VS Code(ExtensionID: Oracle.mysql-shell-for-vs-code
)というOracle公式の拡張機能を利用してセットアップする方法が紹介されています。この拡張機能自体は昨年頃から存在していて業務でもちょこちょこ活用していたのですが、最近 (2023年9月19日 記事執筆は2023年9月末です)Pre-GAとなったようです。
本来MySQL Shellはコマンドラインツールなので「GUIでできることはおそらくCLIでもできるだろ」と高をくくってDocker上にMySQL RouterとinnoDB Cluster, MySQL Shellを用意しました。しかし現在ベータと言うこともあり、MRS用のプラグインの導入手順が公開されていない、MySQL Routerも独自の拡張されたバイナリが配布されている、など様々な問題があったため、まずは公式手順に沿って構築を進めます。
拡張機能を導入後、接続情報を作成します。
サイドバーのイルカからトップページを開くとConnectionを追加できます。
僕の環境ではすでに追加されていますが、New Connectionから接続を追加します。
JetBrainsのDataGripと似たような感じで設定できます。基本的にはCaptionにユーザーフレンドリーな名前をつける、HostとUser, Passwordを設定して終了です。MacのDockerではbridgeへExposeされたポートに対してlocalhostではアクセスできません。そのためループバックアドレスの127.0.0.1を明示しています。
接続できると、DB NotebookというMySQL Shellが起動します。
DBA API(Database Admin API)はこのシェルから利用できず、立ち上げたコンテナの中にある mysqlsh
を利用する必要がありそうでした。
ひとまずこのシェルは利用しません。
VS Codeのサイドバーにある「DATABASE CONNECTIONS」セクションから作成したConnectionを右クリックすることで、MRS用のスキーマが作成できるので実行します。
右下のNotice Balloonで実行するか聞かれるので、Yesを選択。
show databases;
を実行するとメタデータ用のDBが作成されていることを確認できます。
mysql> show databases;
+-------------------------------+
| Database |
+-------------------------------+
| information_schema |
| mysql |
| mysql_innodb_cluster_metadata |
| mysql_rest_service_metadata | -- ←これ!
| performance_schema |
| sys |
+-------------------------------+
6 rows in set (0.01 sec)
また、作成されたデータベースをVS Codeのサイドバーから選択し、右クリックすると詳細なドキュメントが読めるようになります。Googleで検索しても出てこないのに…。(ちなみに貼られているリンクの先へ遷移すると "Oracle Rest Data Service" と書いてあったりします。Oracle DBの機能をそのまま持ってきたようです。)
MySQL Routerをセットアップ
公式のブログに乗っている手順では、ここからホスト側(VS Codeが起動しているMac側)にRouterを入れる想定で書かれていますが、せっかくMySQL本体をDockerに分離したので、Routerはコンテナ内で作りたいですね。
Routerを手動構成する手順は上述のコンテキストメニューから読めるドキュメントに記載があります。
ということで、Dockerfileを以下のように書き換えます。
FROM --platform=arm64 mysql:8.0.32-oracle
WORKDIR /root/
RUN curl https://downloads.mysql.com/snapshots/pb/mysql-router-8.0.33-labs-mrs-preview-3/mysql-router-8.0.33-labs-mrs-3-linux-glibc2.17-aarch64.tar.gz -o mrs.tar.gz
RUN tar xzvf mrs.tar.gz
RUN mv mysql-router-8.0.33-labs-mrs-3-linux-glibc2.17-aarch64 mrs-8.0.33 && rm mrs.tar.gz
ENV PATH="{$PATH}:/root/mrs-8.0.33/bin/"
RUN microdnf -y install vim
Dockerファイル内では、ドキュメントの中(Section 6)にひっそりとあるMySQL Labのリンクからarm64版のMRS拡張済みRouterをダウンロードしてパスを通し、 /root/
配下に設置しています。
また今後の作業性も考慮し、vimをmicrodnfでインストールします。curlコマンド, sedコマンドはMySQLの-oracleコンテナ(Oracle Linux 8ベース)の初期状態で利用できました。
docker compose up -d --build
起動したら、コンテナ内に入りMySQL Routerをbootstrapで初期化します。
コンテナ内に入ります。ホストOSから下記のコマンドでコンテナにアタッチ。
docker exec -ti コンテナ名 bash
コンテナ内で
mysqlrouter_bootstrap root@127.0.0.1:3306 --mrs --directory ~/.mysqlrouter
パスワードはdocker-composeで指定したものを入力すると
# Bootstrapping MySQL Router instance at '/root/.mysqlrouter'...
- Storing account in keyring
- Adjusting permissions of generated files
- Creating configuration /root/.mysqlrouter/mysqlrouter.conf
# MySQL Router configured for the Standalone MySQL Server at 'localhost'
After this, MySQL Router can be started with the generated configuration with:
$ /root/mrs-8.0.33/bin/mysqlrouter -c /root/.mysqlrouter/mysqlrouter.conf
This Router instance can be reached by connecting to:
## MySQL Classic protocol
- Read/Write Connections: localhost:6446
- Read/Only Connections: localhost:6447
## MySQL X protocol
- Read/Write Connections: localhost:6448
- Read/Only Connections: localhost:6449
# Configuring `MRS` plugin...
- Registering metadata
- Creating account(s) (only those that are needed, if any)
- Storing account in keyring
- Adjusting configuration file /root/.mysqlrouter/mysqlrouter.conf
Once the MySQL Router is started, the MySQL REST Service can be reached at
https://localhost:8443/<service-name>
こんな感じで、セットアップが完了します。
ちなみに注意事項で書いた、Schemaのバージョンが異なる(Routerのほうが古い)場合は
# Bootstrapping MySQL Router instance at '/root/.mysqlrouter'...
- Storing account in keyring
- Adjusting permissions of generated files
- Creating configuration /root/.mysqlrouter/mysqlrouter.conf
# MySQL Router configured for the Standalone MySQL Server at 'localhost'
After this, MySQL Router can be started with the generated configuration with:
$ /root/mrs-8.0.32/bin/mysqlrouter -c /root/.mysqlrouter/mysqlrouter.conf
This Router instance can be reached by connecting to:
## MySQL Classic protocol
- Read/Write Connections: localhost:6446
- Read/Only Connections: localhost:6447
## MySQL X protocol
- Read/Write Connections: localhost:6448
- Read/Only Connections: localhost:6449
Error: Unsupported MRS metadata version (2.1.0)
このようなエラーが出て正常終了しません。
Routerを起動
エラーなく終了したら早速Routerを起動します。
この状態ではRouterは起動していないため、別のシェルからdocker内に入り、mysqlrouter
で起動しましょう。一応バックグラウンドに回さずに様子を観察します。
mysqlrouter -c /root/.mysqlrouter/mysqlrouter.conf
上記を実行するとプロセスが実行されます。
プロセスを実行していないDocker内のシェルからlocalhost:8334へcurlしてみます。
curl https://localhost:8443/ -k
<HTML><HEAD>
<TITLE>404 Not Found</TITLE>
</HEAD><BODY>
<H1>Not Found</H1>
</BODY></HTML>
どうやら動いてはいそう!
サンプルのスキーマを容易
ここまで動いたらサンプルデータ用のDBとデータを用意したいですね。MySQLに任意の方法でログインしてスキーマを流しましょう。
CREATE DATABASE sample_app COLLATE utf8mb4_general_ci;
USE sample_app;
CREATE TABLE IF NOT EXISTS prefectures (id INTEGER auto_increment primary key, name varchar(10) not null unique, capital_name varchar(10) not null unique);
INSERT INTO prefectures (name, capital_name) VALUES('北海道', '札幌市'), ('青森県', '青森市'), ('岩手県', '盛岡市');
件数は非常にしょぼいですが、整数のIDと都道府県名、都道府県庁所在地を入れたテーブルです。
これをRESTで呼び出したいですね。RESTの構文的に言えば、
GET /api/prefectures/1
としたときに、
{
"id": 1,
"name": "北海道",
"capital_name": "札幌市"
}
と、返ってくれば嬉しいわけです。
MRSを設定
VS Code側の拡張機能からMRSの初期設定を行います。ほぼドキュメント通りですが、
サイドバーの当該DBの中にある、MySQL REST Serviceを右クリックし、 Add Rest Service
… をクリック。
するとシェル上にConfig用のダイアログが現れます。
ここに設定をかいていきます。REST Service PATHはMRSでアクセスする際のパスです。 今回は /api
としました。
その他の設定は基本的に変えていません。
余談ではありますが、Authentication Appsのタブを見ると公式の解説記事でも説明されているようにOAuth2のベンダーによる認証も使えることがわかります。
このあたりよしなにできるのはかなり強みではないでしょうか。
OKをクリックすると設定が走りサイドバーに /api
の項目が追加されます。また、Routerが正常に登録されていればその下にRouterのアイコンも出現します。(画像の 534~~ がRouter。Dockerのコンテナハッシュがそのまま利用されているようです。)
さて、最後に返却対象のデータが格納されたデータベースとテーブルをMRSに登録します。
先程の追加したデータベース(sample_app)をサイドバーから選択して、右クリックします。
コンテキストメニューに Add Schema to REST Service
があるのでクリックします。
表示されるダイアログで設定を行います。Require Authenticationのチェックボックスは外して登録します。
続いてテーブルも登録します。
テーブル(prefectures)を右クリックして、Add Database Object to REST Service
を実行します。
クリックするとダイアログが出現して、CRUD操作それぞれの可否、公開しないColumnの選択やデータの構造、パスを変更できます。Flagセクションにある Requires Authが動作検証には必要ないので、チェックを外し、それ以外はデフォルトでOKします。
処理が完了すると、MySQL REST Serviceの子ノードが追加されます。
動作検証
ここまでくれば、おそらくは大丈夫でしょう。Routerが起動した状態でコンテナ内から自分に向けてcurlします。
今回の設定では
curl https://localhost:8443/api/sampleApp/prefectures/1 -k
へアクセスするとデータが取得できるはずです。実行すると…?
{"id": 1, "name": "北海道", "links": [{"rel": "self", "href": "/api/sampleApp/prefectures/1"}], "capitalName": "札幌市", "_metadata": {"etag": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855"}}
取れました!
各エンティティに付属するEtagなどのメタデータについては、別途設定で除外できるようです。
まとめ
これをアプリケーションのフロントエンドから叩けば簡単なSPAは作れそうですね。
ここまで取得するのにめちゃくちゃ設定しているわけですが...。
いかんせんドキュメントの整備状況などが芳しくない上に作業量が多いこと、ベータ版で破壊的な仕様変更が多い、など問題点が多いため現時点での利用機会は限定的だと思っています。
ただ、単純にデータを置いておきたい、正規化するレベルのデータを保存しない、APIサーバはデータのハンドリングができればOK というケースは少なからずあるため、個人的にはこの機能の進化にとても期待を寄せています。マイクロサービスの文脈でも利用価値が高そうですね。
ということで、最初で最後のテックブログはここまでです。
お付き合いくださった皆さま、ありがとうございました。
そしてlearningBOXの皆さまがた、大変お世話になりました。
今でも良い会社だったなと感じています。本当にありがとうございました!!