複数コンテナのアプリ¶
これまでは1つのコンテナでアプリを動かしていました。しかし、これからはアプリケーション群に MySQL を追加しようとしています。そうすると、たいてい次の疑問が沸き上がります。「どこで MySQL を実行するの? 同じコンテナにインストールするのかな、それとも別々に実行するの?」 通常、 1つ1つのコンテナが、1つのことをしっかりと実行すべきです 。いくつかの理由があります。
データベースとは別に、 API とフロントエンドをスケールする良い機会
コンテナを分けると、現在のバージョンと更新したバージョンを分離できる
今はローカルにあるデータベースをコンテナが使っているが、プロダクションではデータベースのマネージド サービスを利用したくなるかもしれない
複数プロセスの実行にはプロセスマネージャが必要であり(コンテナは1つのプロセスのみ起動するため)、コンテナの起動や停止が複雑になる
ほかにも、いくつかの理由があります。それでは、下図のように動作するようアプリケーションを更新します。
コンテナのネットワーク機能¶
コンテナについて思い出しましょう。デフォルトでは、
注釈
2つのコンテナが同じネットワーク上にあれば、お互いに通信できます。そうでなければ、どちらも通信できません。
MySQL の起動¶
コンテナをネットワークに加えるには、2つの方法があります。1つは、起動する前に割り当てるか、もう1つは、既存のコンテナに接続します。さしあたり、まずはネットワークを作成し、それから MySQL コンテナの起動時に接続します。
ネットワークを作成します。
$ docker network create todo-app
MySQL コンテナを起動し、先ほどのネットワークに接続します。あわせて複数の環境変数を定義します。これは、データベースの初期化に使います( MySQL Docker Hub にある「Environment Variables」セクションをご覧ください)。
$ docker run -d \ --network todo-app --network-alias mysql \ -v todo-mysql-data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=secret \ -e MYSQL_DATABASE=todos \ mysql:5.7
Macbook M1 チップ / Apple Silicon のような ARM ベースのチップを使う場合は、こちらのコマンドを使います。
$ docker run -d \ --network todo-app --network-alias mysql \ --platform "linux/amd64" \ -v todo-mysql-data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=secret \ -e MYSQL_DATABASE=todos \ mysql:5.7
Windows を使う場合は、 PowerShell でこちらのコマンドを使います。
PS> docker run -d ` --network todo-app --network-alias mysql ` -v todo-mysql-data:/var/lib/mysql ` -e MYSQL_ROOT_PASSWORD=secret ` -e MYSQL_DATABASE=todos ` mysql:5.7
また、
--network-alias
フラグも指定したのが見えるでしょう。こちらについては、後で触れます。ちなみに
ここでは
todo-mysql-data
という名前のボリュームを使い、 MySQL が自身のデータを保管する/var/lib/mysql
をマウントしているのに気づくでしょう。しかしまだ、docker volume create
コマンドを実行していません。名前付きボリュームを使いたい時は、 Docker が認識し、自動的にボリュームを作成します。
データベースが起動して実行中なのを確認するには、データベースに接続し、つながっているかを確認します。
$ docker exec -it <mysql-container-id> mysql -u root -p
パスワードのプロンプトが表示されたら、 secret と入力します。 MySQL のシェル内では、データベース一覧を表示すると、
todo
データベースの存在が確認できます。mysql> SHOW DATABASES;
このような出力が見えるでしょう。
+--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | todos | +--------------------+ 5 rows in set (0.00 sec)
MySQL シェルを終了し、マシン上のシェルに戻ります。
$ exit
やった!
todo
データベースが手に入りましたので、使う準備が調いました!
MySQL に接続¶
答えを探すために、 nicolaka/netshoot コンテナを使います。これには、ネットワーク機能の問題に対するトラブルシューティング(問題解決)やデバッグ(修正)に便利なツールがたくさん入っています。
nicolaka/netshoot イメージを使う新しいコンテナを起動します。必ず同じネットワークに接続します。
$ docker run -it --network todo-app nicolaka/netshoot
コンテナの中で、便利な DNS ツールの
dig
コマンドを使います。ホスト名mysql
の IP アドレスを調べましょう。$ dig mysql
そうすると、次のような出力があります。
; <<>> DiG 9.14.1 <<>> mysql ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;mysql. IN A ;; ANSWER SECTION: mysql. 600 IN A 172.23.0.2 ;; Query time: 0 msec ;; SERVER: 127.0.0.11#53(127.0.0.11) ;; WHEN: Tue Oct 01 23:47:24 UTC 2019 ;; MSG SIZE rcvd: 44
「ANSWER SECTION」に、
mysql
のA
レコードがあり、172.23.0.2
(おそらく似たような値が表示されているでしょう)に解決されているのが分かります。mysql
は通常のホスト名としては有効ではありませんが、 Docker はコンテナの IP アドレスをネットワーク エイリアス(先ほど--network-alias
フラグを使ったのを覚えていますか?)で調べられます。これが意味するのは……アプリはシンプルにホスト名
mysql
へ接続できればよいので、これでデータベースと通信できます! これ以上にシンプルなことはありません!
MySQL とアプリを動かす¶
todo アプリでは、 MySQL へ接続する設定を指定するため、いくつかの環境変数の設定をサポートしています。
MYSQL_HOST
- MySQL サーバを実行中のホスト名MYSQL_USER
- 接続に使うユーザ名MYSQL_PASSWORD
- 接続に使うパスワードMYSQL_DB
- 接続先として使うデータベース
注釈
環境変数を通した接続設定
環境変数を使った接続設定は、開発環境であれば通常は問題ありませんが、本番環境でアプリケーションの実行時は 極めて推奨されません 。Docker の正式セキュリティ
多くのセキュリティ機構は、コンテナ オーケストレーション フレームワークによって _FILE
が末尾に付く環境変数もサポートしています。
たとえば、 MYSQL_PASSWORD_FILE
で設定した値は、アプリが接続用のパスワードとして、参照するファイルの内容を使いたいとします。ですが、Docker はこれらの環境変数を何らサポートしません。アプリ自身が変数を調べ、ファイル内容を取得する必要があります。
説明を全て終えたところで、開発に対応したコンテナを起動しましょう!
注意 : MySQL 8.0 以上では、
mysql
の中で以下のコマンドを実行する必要があります。mysql> ALTER USER 'root' IDENTIFIED WITH mysql_native_password BY 'secret'; mysql> flush privileges;
先ほど環境変数をそれぞれ指定したのと同様に、コンテナをアプリのネットワークに接続します。
$ docker run -dp 3000:3000 \ -w /app -v "$(pwd):/app" \ --network todo-app \ -e MYSQL_HOST=mysql \ -e MYSQL_USER=root \ -e MYSQL_PASSWORD=secret \ -e MYSQL_DB=todos \ node:18-alpine \ sh -c "yarn install && yarn run dev"
Windows を使っている場合は、PowerShell でこちらのコマンドを使います。
PS> docker run -dp 3000:3000 ` -w /app -v "$(pwd):/app" ` --network todo-app ` -e MYSQL_HOST=mysql ` -e MYSQL_USER=root ` -e MYSQL_PASSWORD=secret ` -e MYSQL_DB=todos ` node:18-alpine ` sh -c "yarn install && yarn run dev"
コンテナのログを確認すると(
docker logs <container-id>
)、mysql データベースの使用を示すメッセージが表示されるでしょう。$ nodemon src/index.js [nodemon] 1.19.2 [nodemon] to restart at any time, enter `rs` [nodemon] watching dir(s): *.* [nodemon] starting `node src/index.js` Connected to mysql db at host mysql Listening on port 3000
ブラウザでアプリを開き、todo リストにいくつかのアイテムを追加します。
mysql データベースに接続し、アイテムがデータベースに書き込まれているのを確認します。思い出してください、パスワードは secret です。
mysql シェルから、以下のように実行します。
mysql> select * from todo_items; +--------------------------------------+--------------------+-----------+ | id | name | completed | +--------------------------------------+--------------------+-----------+ | c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! | 0 | | 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome! | 0 | +--------------------------------------+--------------------+-----------+
おそらく、アイテムが異なるため、表の見た目は違うでしょう。ですが、そこに保管されているのが見えます!
Docker ダッシュボードをさっと見ると、2つのアプリ用コンテナが動いているのが見えます。ですが、1つのアプリとして一緒のグループだとは分かりません。近いうちに改善する方法を見ていきます!
まとめ¶
これで、別のコンテナで実行中の外部データベースに、アプリケーションは新しいデータを保管できるようになりました。コンテナのネットワーク機能を少々学び、それから、DNS を使った処理で、サービス ディスカバリをどのようにして行うのかを見てきました。
しかし、このアプリケーションを起動するための全てに対し、少々の圧倒を感じ始めているのではないでしょうか。行ったのは、ネットワークを作成し、コンテナを起動し、全ての環境変数を指定し、ポートを公開する等々です! 覚えることが多すぎますし、誰かに正確に伝えるのは大変です。
次のセクションでは、 Docker Compose について説明します。 Docker Compose があれば、より簡単な方法でアプリケーション スタックを共有でき、他の人もコマンドを1つ(かつシンプルに)実行するだけで、アプリを速攻で立ち上げられます!
参考
- Part 7: Multi container app