Golang で DB が簡単に扱える自作コマンドを作ってみた

この記事は書かれてから1年以上が経過しており、最新の情報とは異なる可能性があります

※どうでもいいけど、9月のブログ記事数が急に増えだしてます。 Hugo 化がここにきてすごく効いてきてますね。

この前 “db-cli” という自作コマンドをリリースしてみました。 コマンド名自体は db です。超短いので何かにかぶってたらゴメンナサイ。

https://github.com/girigiribauer/db-cli

経緯

何かプログラムなどを書いていて、 試しに MySQL(MariaDB) でもちょこっと立てて検証したいなあ、 みたいなことはたまにあると思います。

最近になって、ローカルにそういったデータベースをインストールせずに Docker を利用するという選択肢も出てきたのですが、 まだちょっととっつきにくいというか、 長ったらしいコマンドをずらずらと並べて コンテナを立てることになる ので、 結構面倒臭かったりします。

まだコマンドラインのヒストリーから Ctrl+R とかで探していればまだマシなのですが、 とても毎回ゼロから入力する気にはなれません。

「僕は DB をちょこっと使いたいだけなんだよ!」 というところから、 じゃあ試しに db って打ったら DB コンテナが立ち上がるようなコマンドでも作ってみるかー と思ったのがきっかけです。

コンセプト

  • by Golang
  • ストローク数は超短く。 D! B! Enter! で終わりにしたい。
  • 僕はほとんど使わないが、必要に応じて オプションで上書き 可能。
  • Docker で名前つけるのすらだるい、 勝手につけてほしい。
  • dump, restore も可能な限り楽にやりたい。

こんな感じで、後はオプション指定で別のデータベースとか指定できたらいいなあとか、 配布もさくっとできたらいいなあとか、 (僕は使わないけど)Windows とかでも使えたらいいなあとか、 他にも様々ありますが、必要な項目としては上の4つです。

するともう自然とコマンド名は db に決まり、 コマンドラインを作るのに今適してそうな Golang で書くのに決めました。 (というか先に Golang で書くことだけは決まっていた気がする)

インストール

先にDocker あるいは Docker for Mac/Windows を入れましょう。

その後 MacOS であれば

$ brew tap girigiribauer/db-cli

$ brew install db-cli

でインストールできます。 (Homebrew 対応の話はまた別にメモっておきます)

バイナリファイルが直接欲しい人は https://github.com/girigiribauer/db-cli/releases にあります。

使い方

基本的にヘルプに全部書いてありますのでお読みください。

以下は https://github.com/girigiribauer/db-cli と同じ内容です。

データベース作る

$ db

これだけで DB が立ち上がった状態になります。 MariaDB をイメージとした “db0” という名前のコンテナが起動状態になっていて、 通常利用するであろう 3306 ポートが空いていれば、その番号がそのままポートフォワーディングされます。

試しにもう1回 db と打てばわかりますが、名前が空いていなければ “db1”, “db2” と繰り上がり、 3306 ポートが空いていなければ自動で割り当てられます。

なので、 1つだけ使って消す、使って消す、みたいな使い方をしていれば、 名前は必ず “db0” になり、ポートは 3306 になります。 (実際僕の利用用途の9割くらいはこれで満たされてます)

データベース名は何もつけなければ “db” です。 ユーザー名もパスワードも初期値 “db” です。

データベース消す

$ db -d

消すのも合計6ストロークです。 けっこう短くないですか?

これも上と同様で、“db0” があればそれが消えます。なければ “db1”, “db2” を探しにいきます。

その他

オプション以外の設定項目も、一通り 環境変数で上書きできるようになっています。

例えば .bashrc などに

DBCLI_CONTAINER_PREFIX=go

などと書いておけば、勝手に go0, go1 … という名前のコンテナが立ち上がります。

dump, restore もできるので、 https://github.com/girigiribauer/db-cli をお読みください。

まだ微妙なところ

TODO というわけではないのですが、うーん・・・と思ってるところが若干あります。 ただ自分用のコマンドを単に見えるようにしただけなので、 正直対応するかどうかは微妙です。

  • Docker コンテナのヘルスチェック
  • test
  • PostgreSQL 対応

Docker コンテナのヘルスチェック

正直なところ、Docker 1.12 あたりで導入された、 コンテナのヘルスチェック周りの知見がちょっと足らないかもです・・・。

この辺いじりだしたのは restore オプションの導入あたりからなのですが、 db, db -d コマンドだけ使っていれば、 ヘルスチェックなどはそもそも不要でした。(create -> start するだけで問題ない)

restore オプションを実装するには、 コンテナを作るときにボリュームをマウントするなどの必要があったのですが、 あまり複雑なことをやりたくなかったので、 データベースが完全に用意できてから dump と同じ要領で 外部からのコマンドを流し込む方法 をとりました。

なお、今回は ボリュームのマウント機能は一切利用せずにコマンド作ってます。 ボリュームのマウントまで考慮に入れてしまうと、 じゃあローカルのどのディレクトリをマウント設定するんだとか、 そのオプションがなかった場合にどのような挙動になるんだとか、 色々(オペレーション的に)複雑になりすぎる気がしてたので ボリュームを一切使わない方法はないか検討しました。

で、この データベースが完全に用意できてから というのが曲者で、 MariaDB が使えるようになるまで待機するのがどこまでなのかが未だによくわかっておりません・・・。

mysqladmin の ping が通り始めるタイミング なのか、 あるいは "/var/run/mysqld/mysqld.sock” のファイルができるタイミング なのか、 いずれにしてもタイミングによって restore ができるときとできないときが出てきてしまうので、 5秒程度余分に待つといったダサい策をとってます。ぐぬぬ。

なのでもうちょっと Docker のヘルスチェックというか、 Docker を利用した MariaDB の起動の仕組みについてもう一段掘り下げてきちんと調べる必要がありそうですが、 正直そこまでコストかけるかどうか微妙なところです。

test

今回は API を叩くのがほとんどで、大きく DockerClient の状態に依存するので、 正直なところあまり頑張らずに DockerClient のエラーなどをそのまま垂れ流してもいいかなと思い、 あまり力を入れてません。

最初はイメージダウンロードしてコンテナ作った状態を再現してから、ってやってたんですが、 僕のストレスが増加しそうだったのでやめました。

PostgreSQL 対応

めんどかったので入れませんでした。 気が向いたら入れるかも。

一応ある程度楽には入れられるような作りにはなってます。

ただこれもヘルスチェックどうするんだ的な話が絡んでくるので、 気が向いたとしてもすぐ飽きるかもしれません。

逆に思ったよりもよくできたところ、割り切ったところ

  • マルチプラットフォーム対応
  • そもそもの利用用途

マルチプラットフォーム対応

Golang のマルチプラットフォーム対応、なめてました。

GOOS=windows GOARCH=amd64 go build -o build/windows-amd64/db.exe cmd/db/*.go

go build コマンドの前に GOOSGOARCH 環境変数をセットしてやるだけで 本当に Windows 用のバイナリが出来てしまいました。

僕が使うわけではないので、あわよくば Windows 対応もできればいいなあくらいに思っていたのですが、 正直そんな手間もかからず(ゼロではないですが)対応できた のはありがたかったです。Golang マジすげえな。

ちょっと違うところといえば、 裏側で Docker Remote API を用いているのですが、 DockerClient のエンドポイントが Linux, MacOS は UNIX ドメインソケットなのに対して、 Windows では名前付きパイプ(npipe?)なんですよね。

ただこれも、大元のライブラリが普通に対応していて パスの違いを吸収する程度の手間だったので 「まあこれくらいなら・・・」と思わせてくれるのは良いですね。

ちなみにこの DockerClient ライブラリに大きくお世話になりました。

https://github.com/fsouza/go-dockerclient

docker コマンドで一通りできることは問題無くできると思います。

そもそもの利用用途

あまり大事にしたくなかったというか、試しに構築してみたいくらいの温度感だったので、 僕が使うプラスアルファの対応に留めたい と最初から思っていました。

なので、そもそも開発用としてローカルで使うもの前提だし、 レプリケーションやら dump 時のテーブルロックの話などはばっさり切り捨てています。

まとめ

自分でコマンドを作ることは無くは無かったのですが、 表に出すところまではやったことがなかったので良い経験になりました。 Homebrew 対応についてはまた改めてメモっておきます。

Golang はクロスプラットフォームな CLI を作るのに適していると思います。

最後の方はみんなのGo言語も読みつつ進めていましたが、 ちょうど今回に合った話もちらほらあって読んでてとてもためになりました。

参考URL

この記事は書かれてから1年以上が経過しており、最新の情報とは異なる可能性があります

もし記事内に誤りなどございましたら、 @girigiribauer @girigiribauer.com までご一報いただけると助かります。

Related articles