MySQL道普請便り

第106回 Docker Composeを使って便利にMySQLを利用してみる

この記事を読むのに必要な時間:およそ 3 分

DockerでMySQLを利用する方法に関しては,以前第5回 Dockerで複数バージョンのMySQLを開発環境に用意するで紹介させていただきました。大分間が空いてしまいましたが,今回はDocker Composeというオーケストレーションツールを作って実際にアプリケーションを開発する際に便利にMySQLを活用していきたいと思います。

検証環境

今回はmacOS Mojave(10.14.6)上で,Docker Desktop for mac(2.1.0.3)を利用して検証を行っています。WindowsでもDocker Desktopがインストールできる環境であれば同様に実行することができると思います。Linuxを利用している場合には,dockerパッケージのインストールとpip install docker-composeなどを利用してdocker-composeを追加でインストールする必要があります。

Docker Composeとは?

Dockerでよく使われるコンテナオーケストレーションツールの一種で,ローカル環境やCI環境などで環境構築を一気に行うことができるツールです。ローカルにアプリ環境を構築したほうが簡単に思えるかもしれませんが,たとえば以下のようなケースの際には切り替えて利用ができる方が便利です。

  • 複数のMySQLのバージョンを使うアプリケーションを切り替えて利用する場合
  • MySQLのバージョンアップでどんな問題が発生するか調査したい場合

たとえば,MySQL 5.7から8.0に移行している最中などで5.7と8.0のサービスが混在している場合,サービスごとにMySQLを切り替えたりポートを書き換えたりして動作を確認するのは難しいですし面倒です。また,MySQLのバージョンアップを雑に行ってどんな問題が発生するのか調査したい場合にも,いちいち立ち上げたり落としたりするのは大変です。そんなときにDocker Composeを使って便利に環境を揃えてみましょう。

アプリケーション側の準備

今回は,Rubyで書いた簡単なsinatraのアプリケーションを利用してDocker Composeを試してみましょう。とりあえず,今回はmysqlのデータをjsonで返すシンプルなアプリケーションを構築します。Gemfileは以下のとおりです。

Gemfile

source "https://rubygems.org"

gem 'sinatra'
gem 'mysql2'

Rubyアプリケーションの内容は以下になります。

main.rb

require 'sinatra'
require 'mysql2'
require 'json'

get '/' do
  get_json
end

def get_json
  client = Mysql2::Client.new(username: 'root', host: 'db', database: 'data_store', password: 'password')
  client.query('SELECT * FROM data').to_a.to_json
end

dataテーブル内にある情報を1件取得してjson形式で返すアプリケーションです。

続いてDockerfileとdocker-compose.ymlを準備しましょう。

Dockerfile

FROM ruby:2.6-alpine3.9
ENV APP_ROOT /usr/src/app
WORKDIR $APP_ROOT
RUN apk add --no-cache     mysql-client \
                           mysql-dev\
                           alpine-sdk

alpineのRubyのイメージをベースに必要なソフトウェアをインストールしています。そしてdocker-compose.ymlです。

docker-compose.yml

version: "3.7"
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: data_store
  sinatra:
    build: .
    command: ./start.sh
    volumes:
      - ./:/usr/src/app
    depends_on:
      - db
    ports:
      - "4567:4567"

見てみるとそれほど難しくないように見えると思います。versionはdocker-composeのバージョンを示していて,servicesで建てるコンテナを指定していきます。dbでは公式のイメージを利用しています。enviroment内にdockerに指定する環境変数を記載しています。他のコンテナからはホスト名がdbとして見えるため,前述のmain.rb内のhostの指定がdbとなっています。

sinatraでは今回作成したDockerイメージをビルドして利用します。commandではshellスクリプトを実行できるのですが,今回はdbの起動待ちを入れるためstart.shを用意して実行しています。また今回のアプリでは前述のMySQLのイメージが必須なので,sinatraが起動するときに同時に起動するように設定します。sinatraのポートはデフォルトでは4567となっているので,母艦からアクセスができるように母艦のポートとコンテナのポートを揃えてあげます。

途中で出てきたstart.shですが,こちらはbundle installとdbの初期化をしています。ファイルの実行権限が必要になると思いますので,chmod 755 start.shと忘れずに実行してください。

start.sh

#!/bin/sh

bundle install
until mysqladmin ping -h db -P 3306 --silent; do
  >&2 echo "MySQL is unavailable - sleeping"
  sleep 2
done
mysql -uroot -h db -ppassword data_store < ./initial.sql
bundle exec ruby main.rb -o 0.0.0.0

until mysqladmin ping -h db -P 3306 --silentの部分で何をしているかというと,Docker Composeでコンテナを立ち上げた際に,depends_onに書かれているdbのコンテナを立ち上げてはくれるのですが,立ち上がりきったかは確認ができません。そのためmysqladminを利用してMySQLが立ち上がるのを待っています。

完了したあとにDBの初期化を行って,最後にsinatraを立ち上げる処理を入れています。

initial.sql

drop table if exists data_store.data;
CREATE TABLE data_store.data(name varchar(255));
INSERT INTO data_store.data VALUE ("test");

最後に設置したときのイメージを載せておきます。

$ ls
Dockerfile              Gemfile                 Gemfile.lock
docker-compose.yml      initial.sql             main.rb
sql                     start.sh

実際に動作を確認してみる

それでは,Docker Composeを使ってアプリケーションを立ち上げてみましょう。まずはbuildしてみましょう。

$ docker-compose build
kimurakouichirounoMacBook-Pro:demo koichiro.kimura$ docker-compose build
db uses an image, skipping
Building sinatra
Step 1/4 : FROM ruby:2.6-alpine3.9
 ---> 1fbb4d7710eb
Step 2/4 : ENV APP_ROOT /usr/src/app
 ---> Using cache
 ---> ddeffb842a7e
Step 3/4 : WORKDIR $APP_ROOT
 ---> Using cache
 ---> 52655a3119f8
Step 4/4 : RUN apk add --no-cache     mysql-client                            mysql-dev                           alpine-sdk
 ---> Using cache
 ---> fff7623a1636

Successfully built fff7623a1636
Successfully tagged demo_sinatra:latest

今回dbで指定したimageは公式のものなので,ビルドがスキップされます。各ステージで成功してSuccessfully builtが成功していたら,ビルドは完了しています。続いて実際に立ち上げてみましょう。

$ docker-compose run sinatra -d

-dを入れることで,デーモンとして起動することができます。この状態でcurlなどを使ってアクセスしてみましょう。

$ curl http://localhost:4567
[{"name":"test"}]

initial.sqlで挿入した値が取得できていることがわかります。ということで,無事実行することができました。

止める時はdocker-compose downとすることで,削除まで行うことができます。永続化しておきたいものがある場合は,適切に設定を入れてあげましょう。

$ docker-compose down
Stopping demo_sinatra_1 ... done
Stopping demo_db_1      ... done
Removing demo_sinatra_1 ... done
Removing demo_db_1      ... done
Removing network demo_default

動作がうまく行かなかった場合

MySQLが起動しなくなった場合などに起動時のログを確認したい場合があると思います。その場合にはdocker-compose logsコマンドを実行することで,ログを確認することができます。

$  docker-compose logs

ちなみにですが,Dockerのビルドを何度も行っていると,ディスク容量を食い尽くしてしまうことがよくあります。そういった場合にはディスクの容量を空けて,docker-compose downをしてコンテナを一度落としてから起動しましょう。

またここで発見したエラーは,perrorというコマンドを利用して検索することもできます。

$ perror 28
OS error code  28:  No space left on device

こちらも合わせて覚えておくと便利でしょう。

まとめ

今回は,Docker Composeを利用して更に便利にMySQLを使ってアプリケーションを開発する方法について紹介しました。Docker Composeが使える環境であれば,即座に開発用のコンテナを立ち上げて動作を確認することができ,便利ということが伝わりましたでしょうか。

このような環境を整備しておくと,開発環境が壊れてしまった場合や,さまざまなアプリケーション開発を行っている人だけでなく,インフラエンジニアの方が,試しにMySQL等のミドルウェアの構成を変更する際にも楽に行えるようになります。いろいろな人にとって楽になると思われるので,興味のある方はぜひ一度構築してみてはいかがでしょうか。

著者プロフィール

木村浩一郎(きむらこういちろう)

株式会社オプティム 技術統括本部のエンジニア。最近はミドルウェア・インフラ周りのことも少しずつ学習しています。趣味は将棋。好きな戦法は四間飛車。

Twitter:@kk2170