複数コンテナのアプリ¶
これまでは1つのコンテナでアプリを動かしていました。しかし、これからはアプリケーション スタックに MySQL を追加しようとしています。そうすると、たいてい次の疑問が沸き上がります。「どこで MySQL を実行するのですか? 同じコンテナにインストールするのかな、それとも別々に実行するの?」と。通常、1つ1つのコンテナが、1つのことをしっかりと実行すべきです 。以下はコンテナを分けて実行する理由です。
データベースとは別に、 API とフロントエンドをスケールする良い機会
コンテナを分けると、現在のバージョンと更新したバージョンを分離できる
今はローカルにあるデータベースをコンテナが使っているが、プロダクションではデータベースのマネージド サービスを利用したくなるかもしれない
複数のプロセスを実行するにはプロセスマネージャが必要であり(コンテナは1つのプロセスのみ起動するため)、コンテナの起動や停止が複雑になる
ほかにも、いくつかの理由があります。それでは、下図のように動作するようアプリケーションを更新します。
コンテナのネットワーク機能¶
コンテナについて思い出しましょう。デフォルトでは、
MySQL の起動¶
コンテナをネットワークに加えるには2つの方法があります。
コンテナの起動時にネットワークを割り当てる
既に実行しているコンテナをネットワークに接続する
以下の手順では、まずネットワークを作成し、それから MySQL コンテナの起動時に接続(アタッチ)します。
ネットワークを作成します。
$ docker network create todo-app
MySQL コンテナを起動し、先ほどのネットワークに接続します。あわせて複数の環境変数を定義します。これは、データベースの初期化に使います。MySQL の環境変数について学ぶには、 MySQL Docker Hub にある「Environment Variables」セクションをご覧ください。
Mac / Linux
$ 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:8.0
Windows
Windows では PowerShell 上でコマンドを実行します。
$ 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:8.0
先ほどのコマンドでは、
--network-alias
フラグも指定したのが見えるでしょう。こちらについては、後で触れます。Tip
ここでは
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 シェルを終了し、マシン上のシェルに戻ります。
mysql> exit
todo
データベースが手に入りましたので、いつでも使う準備が調いました。
MySQL に接続¶
MySQL の起動と実行方法が分かりましたので、次は使います。しかし、どのように使うのでしょうか? 同じネットワーク上に他のコンテナを実行したとして、どのようにして MySQL のコンテナを見つけられるのでしょうか? 各コンテナは自身の IP アドレスを持つのを思い出してください。
この疑問に答え、コンテナのネットワーク機能について良く理解するため、 nicolaka/netshoot コンテナを使います。これにはネットワーク機能の問題に対するトラブルシューティング(問題解決)やデバッグ(修正)に役立つツールがたくさん入っています。
nicolaka/netshoot イメージを使う新しいコンテナを起動します。必ず同じネットワークに接続します。
$ docker run -it --network todo-app nicolaka/netshoot
コンテナの中で、便利な DNS ツールの
dig
コマンドを使います。ホスト名mysql
の IP アドレスを調べましょう。$ dig mysql
次のような出力になります。
; <<>> DiG 9.18.8 <<>> 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 とアプリを動かす¶
MySQL への接続する設定を指定するため、todo アプリは環境変数の設定をサポートしています。
MYSQL_HOST
- MySQL サーバを実行中のホスト名MYSQL_USER
- 接続に使うユーザ名MYSQL_PASSWORD
- 接続に使うパスワードMYSQL_DB
- 接続先として使うデータベース
注釈
環境変数を使った接続設定は、開発環境であれば通常は問題ありませんが、本番環境でアプリケーションの実行時は 極めて推奨されません 。Docker の正式セキュリティ
多くのセキュリティ機構は、コンテナ オーケストレーション フレームワークによって _FILE
が末尾に付く環境変数もサポートしています。
たとえば、 MYSQL_PASSWORD_FILE
で設定した値は、アプリが接続用のパスワードとして、参照するファイルの内容を使いたいとします。ですが、Docker はこれらの環境変数を何らサポートしません。アプリ自身が変数を調べ、ファイル内容を取得する必要があります。
それでは開発に対応したコンテナを起動します。
先ほどの環境変数に加え、コンテナをアプリのネットワークに接続する指定をします。次のコマンドを実行する時は、
getting-started/app
ディレクトリにいるのを確認します。Mac / Linux
$ docker run -dp 127.0.0.1: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
Windows では PowerShell 上コマンドを実行します。
$ docker run -dp 127.0.0.1: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] 2.0.20 [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
です。$ docker exec -it <mysql-container-id> mysql -p todos
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 | +--------------------------------------+--------------------+-----------+
おそらく、アイテムが異なるため、表の見た目は違うでしょう。ですが、そこに保管されているのが見えます。
次のステップ¶
これで、別のコンテナで実行中の外部データベースに、アプリケーションは新しいデータを保管できるようになりました。コンテナのネットワーク機能と DNS を使ったサービス ディスカバリを少々学びました。
しかし、このアプリケーションを起動するための全てに対し、少々の圧倒を感じ始めているのではないでしょうか。行ったのは、ネットワークを作成し、コンテナを起動し、全ての環境変数を指定し、ポートを公開する等々です! 覚えることが多すぎますし、誰かに正確に伝えるのは大変です。
次のセクションでは、 Docker Compose について説明します。 Docker Compose があれば、より簡単な方法でアプリケーション スタックを共有でき、他の人もコマンドを1つ、かつシンプルに実行するだけで、アプリを速攻で立ち上げられます。
参考
- Part 7: Multi container app