Dockerでコンテナ間通信するアプリケーションをデプロイ【Tomcat + MySQL】

[Ubuntu上でビルド・デプロイしたwebapp](https://qiita.com/zaki-lknr/items/dc5578d0a8d78edad992)を、今度はDockerコンテナ上でデプロイしてみる。

「Dockerってなにそれおいしいの？」とかの、コンテナを使うメリットとかの話はここにはないですごめんなさい。

アプリケーションのビルドについてもマルチステージビルドを使ってコンテナ上でビルドして、作成したwarをデプロイ用のイメージに組み込む手順もあるけど、とりあえずビルドについてはコンテナの外(VM上)でやります。
※ マルチステージビルドについてはこちら: [Dockerのマルチステージビルド機能でコンテナ上のMavenビルド - Qiita](https://qiita.com/zaki-lknr/items/1d7ab5d92359d30dfac0)

全体の構成図はこんな感じ

![docker.png](https://qiita-image-store.s3.amazonaws.com/0/256859/1e6f0c85-9099-658b-f161-992c752b7b38.png)

Windows 10 homeを使っているからというのも大きいけど、VirtualBox上のVMの中でDockerを動かしているので、Windows上からVMで動いているコンテナへの__直接の__通信(図中windowsからブラウザでwebアクセスとか)はできない。
(接続方法は後述)

ちなみに、[Minishift(OpenShift)を使ったオーケストレーション環境でのデプロイはこちら](https://qiita.com/zaki-lknr/items/867d858ccb7430521a8a)

---

更新履歴

- 2019.02.11: マルチステージビルドについて記事へのリンク追加
- 2019.02.07: ポートフォワーディング(`-p`)について説明追加
- 2019.02.06: 構成図追加

# Dockerのインストール

まずは実行環境としてのDockerをインストール。といっても`apt`や`yum`で入る。

Ubuntuであれば[docker.ioパッケージ](https://packages.ubuntu.com/cosmic/docker.io)。(dockerパッケージもあるけどそっちは違うｗ)
また、universeリポジトリにあるので、[aptlineに追加されていなければ修正する](https://qiita.com/zaki-lknr/items/dc5578d0a8d78edad992#maven%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)。

```
root@chaource:~# apt install docker.io
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  bridge-utils cgroupfs-mount libltdl7 pigz ubuntu-fan
Suggested packages:
  ifupdown aufs-tools debootstrap docker-doc rinse zfs-fuse | zfsutils
The following NEW packages will be installed:
  bridge-utils cgroupfs-mount docker.io libltdl7 pigz ubuntu-fan
0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
Need to get 40.3 MB of archives.
After this operation, 198 MB of additional disk space will be used.
Do you want to continue? [Y/n] y

...snip...

Adding group `docker' (GID 113) ...
Done.
Created symlink /etc/systemd/system/sockets.target.wants/docker.socket → /lib/systemd/system/docker.socket.
Processing triggers for ureadahead (0.100.0-20) ...
Processing triggers for libc-bin (2.27-3ubuntu1) ...
Processing triggers for systemd (237-3ubuntu10.11) ...
root@chaource:~# 
root@chaource:~# 
root@chaource:~# 
root@chaource:~# docker version
Client:
 Version:           18.06.1-ce
 API version:       1.38
 Go version:        go1.10.4
 Git commit:        e68fc7a
 Built:             Fri Oct 19 19:43:14 2018
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.06.1-ce
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.10.4
  Git commit:       e68fc7a
  Built:            Thu Sep 27 02:39:50 2018
  OS/Arch:          linux/amd64
  Experimental:     false
root@chaource:~# 
```

# アプリのビルド

`git clone`して`mvn package`するだけなので省略…と言いたいところだけど、接続先のMySQLサーバが`localhost`ではなくなるので、そこだけ確認・修正する。

## git clone

[VMのTomcatで動かしていたバージョン](https://qiita.com/zaki-lknr/items/dc5578d0a8d78edad992)は`v1.0.0`のタグをつけてるのでそれを`clone`する

```
root@chaource:~/src# git clone https://github.com/zaki-lknr/javaee-memoapp2.git -b v1.0.0
Cloning into 'javaee-memoapp2'...
remote: Enumerating objects: 384, done.
remote: Counting objects: 100% (384/384), done.
remote: Compressing objects: 100% (158/158), done.
remote: Total 384 (delta 144), reused 355 (delta 115), pack-reused 0
Receiving objects: 100% (384/384), 39.46 KiB | 2.08 MiB/s, done.
Resolving deltas: 100% (144/144), done.
Note: checking out '3656f7114c526027c96c0a585b6a5152ce43947c'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

root@chaource:~/src# 
```

## 接続先MySQLの設定

```
root@chaource:~/src# cd javaee-memoapp2/
root@chaource:~/src/javaee-memoapp2# ls
LICENSE.txt  pom.xml  README.md  src
root@chaource:~/src/javaee-memoapp2# vi src/main/webapp/META-INF/context.xml 
```

変更点はこんな感じ。

```diff
root@chaource:~/src/javaee-memoapp2# git diff
diff --git a/src/main/webapp/META-INF/context.xml b/src/main/webapp/META-INF/context.xml
index 2a0018b..7da0d93 100644
--- a/src/main/webapp/META-INF/context.xml
+++ b/src/main/webapp/META-INF/context.xml
@@ -31,6 +31,6 @@
   username="memoapp"
   password="memoapp"
   driverClassName="com.mysql.jdbc.Driver"
-  url="jdbc:mysql://localhost:3306/memoapp_db" />
+  url="jdbc:mysql://memoapp-db:3306/memoapp_db" />
 
 </Context>
```

`memoapp-db`というホストへDB接続する設定にしているけど、これはこの名前でDBのコンテナを起動し、この名前でAPのコンテナから名前解決するよう構築する予定。

## ビルド

これは[VM上でビルド](https://qiita.com/zaki-lknr/items/dc5578d0a8d78edad992#maven%E3%83%93%E3%83%AB%E3%83%89)と一緒。
Mavenがインストールされてなければ[aptで入れる](https://qiita.com/zaki-lknr/items/dc5578d0a8d78edad992#maven%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)。

```
root@chaource:~/src/javaee-memoapp2# 
root@chaource:~/src/javaee-memoapp2# 
root@chaource:~/src/javaee-memoapp2# mvn package
:
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building memoapp2 Maven Webapp 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
:
:
[INFO] Packaging webapp
[INFO] Assembling webapp [memoapp2] in [/root/src/javaee-memoapp2/target/memoapp2]
[INFO] Processing war project
[INFO] Copying webapp resources [/root/src/javaee-memoapp2/src/main/webapp]
[INFO] Webapp assembled in [65 msecs]
[INFO] Building war: /root/src/javaee-memoapp2/target/memoapp2.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:00 min
[INFO] Finished at: 2019-01-27T08:26:21+09:00
[INFO] Final Memory: 19M/46M
[INFO] ------------------------------------------------------------------------
root@chaource:~/src/javaee-memoapp2# ls target/
classes            maven-archiver  memoapp2
generated-sources  maven-status    memoapp2.war
root@chaource:~/src/javaee-memoapp2# 
```

ビルドされたwarファイル

```
root@chaource:~/src/javaee-memoapp2# ll target/memoapp2.war 
-rw-r--r-- 1 root root 3905453 Feb  4 21:43 target/memoapp2.war
```

# Dockerネットワークの作成

以前はDockerでのコンテナ間通信は`--link`オプションを使用することでコンテナ名で名前解決したり通信したりできたが、現在このオプションは非推奨(廃止予定)となっている。

現在どうするかというと、「Dockerネットワーク」を一つ作り、相互に通信が必要なコンテナをそのネットワークに所属するように設定してあげれば、そのDockerネットワーク内でコンテナ間通信ができるようになる。

- [Docker コンテナ・ネットワークの理解 — Docker-docs-ja 17.06.Beta ドキュメント](http://docs.docker.jp/engine/userguide/networking/dockernetworks.html)
- [Docker入門（第五回）〜コンテナ間通信〜 | さくらのナレッジ](https://knowledge.sakura.ad.jp/16082/)

(これはさくらインターネットさんの入門記事見るのが早いです笑)

```
root@chaource:~# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
3a1b834bcf14        bridge              bridge              local
312a61b7e4d9        host                host                local
031ce5d26836        none                null                local
root@chaource:~# 
root@chaource:~# docker network create memoapp-network
308817de4a7fd68fdf811af09ac00eceb20a20ac7b522d1b89bc16db8130d288
root@chaource:~# 
root@chaource:~# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
3a1b834bcf14        bridge              bridge              local
312a61b7e4d9        host                host                local
308817de4a7f        memoapp-network     bridge              local
031ce5d26836        none                null                local
```

`memoapp-network`が作成されました

# MySQLのデプロイ

[DockerHubにMySQLのOfficialイメージが公開されていて、これは現在8.0が最新になっている](https://hub.docker.com/_/mysql)

が、今まで5.7を使ってきたので、一応5.7でやってみましょう。
起動パラメタはDockerHubを見れば載っている。
ただ、コンテナオーケストレーションだとなかった`--character-set-server=utf8`という神対応のオプションががが。
それから、コンテナ間通信用に作成したネットワークの指定も忘れずに。

```
root@chaource:~/src/javaee-memoapp2# docker run --network memoapp-network --name memoapp-db -e MYSQL_DATABASE=memoapp_db -e MYSQL_USER=memoapp -e MYSQL_PASSWORD=memoapp -d mysql:5.7 --character-set-server=utf8
Unable to find image 'mysql:5.7' locally
5.7: Pulling from library/mysql
5e6ec7f28fb7: Pull complete 
4140e62498e1: Pull complete 
e7bc612618a0: Pull complete 
1af808cf1124: Pull complete 
ff72a74ebb66: Pull complete 
3a28cb03e3dc: Pull complete 
2b52dda3bd7d: Pull complete 
dd1e5bc08c44: Pull complete 
2cbf322d346d: Pull complete 
7193a395fe03: Pull complete 
d177f9940737: Pull complete 
Digest: sha256:1590f2540fd87e39605686873fb10206da4cbd7e83df2bc4110abe9fb740699e
Status: Downloaded newer image for mysql:5.7
36717545d79a19ede42b9f088c6f2420c863aab6ac448cb11b7f2fe3f1c32398
root@chaource:~/src/javaee-memoapp2#
root@chaource:~/src/javaee-memoapp2# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
root@chaource:~/src/javaee-memoapp2# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
151d1c502fc3        mysql:5.7           "docker-entrypoint.s…"   4 seconds ago       Exited (1) 3 seconds ago                       memoapp-db
```

あれ？
エラーで止まってる。

ログを見てみる。

```
root@chaource:~/src/javaee-memoapp2# docker logs memoapp-db
error: database is uninitialized and password option is not specified 
  You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD
root@chaource:~/src/javaee-memoapp2# 
```

rootパスワードをなんかセットしないとダメらしい。
お試し動作なので`MYSQL_RANDOM_ROOT_PASSWORD`でランダム設定にしてみる。


```
root@chaource:~/src/javaee-memoapp2# docker rm memoapp-db
memoapp-db
root@chaource:~/src/javaee-memoapp2# docker run --network memoapp-network --name memoapp-db -e MYSQL_DATABASE=memoapp_db -e MYSQL_USER=memoapp -e MYSQL_PASSWORD=memoapp -e MYSQL_RANDOM_ROOT_PASSWORD=yes -d mysql:5.7 --character-set-server=utf8
root@chaource:~/src/javaee-memoapp2# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                 NAMES
2b7c5b914055        mysql:5.7           "docker-entrypoint.s…"   2 seconds ago       Up 2 seconds        3306/tcp, 33060/tcp   memoapp-db
```

うごいた。

ちなみにエンコード設定は…

```
root@chaource:~/src/javaee-memoapp2# docker exec -it memoapp-db bash
root@2b7c5b914055:/# mysql -u memoapp -p -D memoapp_db
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.25 MySQL Community Server (GPL)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show variables like '%char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | latin1                     |
| character_set_connection | latin1                     |
| character_set_database   | utf8                       |
| character_set_filesystem | binary                     |
| character_set_results    | latin1                     |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.00 sec)

mysql> 
```

おぉ、utf8になってる。Dockerすばらしい。

ちなみに、パラメタの指定順序が適当すぎたりすると動かなかったりする。

```
root@chaource:~# docker run --network memoapp-network --name memoapp-db -d mysql:5.7 -e MYSQL_DATABASE=memoapp_db -e MYSQL_USER=memoapp -e MYSQL_PASSWORD=memoapp -e MYSQL_RANDOM_ROOT_PASSWORD=yes --character-set-server=utf8
07425255c74377d2cec7b30ec1d16d58b39acf791605bdf6cfa779defd8de47f
root@chaource:~# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
root@chaource:~# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
07425255c743        mysql:5.7           "docker-entrypoint.s…"   13 seconds ago      Exited (1) 12 seconds ago                       memoapp-db
root@chaource:~# docker logs memoapp-db

ERROR: mysqld failed while attempting to check config
command was: "mysqld -e MYSQL_DATABASE=memoapp_db -e MYSQL_USER=memoapp -e MYSQL_PASSWORD=memoapp -e MYSQL_RANDOM_ROOT_PASSWORD=yes --character-set-server=utf8 --verbose --help"

2019-02-04T22:19:33.614318Z 0 [ERROR] mysqld: unknown option '-e'
2019-02-04T22:19:33.619595Z 0 [ERROR] Aborting
root@chaource:~#
```

基本的に以下の順序でオプションを並べておけばOK

0. コマンド名(`docker`)
1. Dockerのサブコマンド(`run`とか`exec`とか)
2. Docker(のサブコマンド)に対するオプション(`-d`とか`--name`とか`-e`とか)
3. 起動するイメージ:タグ名(`mysql:5.7`)
4. 起動するコンテナ(今回はMySQL)に対する引数(`--character-set-server=utf8`とか)
    - 引数が有効かどうかは、コンテナの定義次第

# Tomcat/webappのデプロイ

[TomcatもOfficialイメージが公開されている](https://hub.docker.com/_/tomcat)ので、これを使いましょう。

## パターンA: 手動でwar配置

コンテナの構造がわからないときはまずここから。
(といってもDockerHubのDescriptionとDockerfile見ればわかるんだけど)

基本的に[効率的に安全な Dockerfile を作るには - Qiita](https://qiita.com/pottava/items/452bf80e334bc1fee69a)の要領で作業すればいい感じにできます。

まずはわかりやすさ重視で1ステップずつやります。

### Tomcatコンテナのpullと起動とポートフォワード設定

まずはDockerHubのレジストリからTomcatのイメージを取得。
ただしこの手順は省略可能(未取得の状態で`run`すると、勝手に`pull`してくれる)

```
root@chaource:~# docker pull tomcat:8.5
8.5: Pulling from library/tomcat
ab1fc7e4bf91: Pull complete 
35fba333ff52: Pull complete 
f0cb1fa13079: Pull complete 
3d79c18d1bc0: Pull complete 
ff1d0ae4641b: Pull complete 
8883e662573f: Pull complete 
adab760d76bd: Pull complete 
86323b680e93: Pull complete 
14a2c1cdce1c: Pull complete 
ee59bf8c5470: Pull complete 
067f988306af: Pull complete 
Digest: sha256:296b26baeee450a9814b2733e9d085f3d26af1c48e5fdc2000496ff7e12bc897
Status: Downloaded newer image for tomcat:8.5
root@chaource:~# 
```

そして実行

```
root@chaource:~/src/javaee-memoapp2# docker run --network memoapp-network -p 18080:8080 -d tomcat:8.5
d9e3953b0b7e38a0c3d1b4868b26654291045cae968c306f2e23dba4b36ebd2f
```

```
root@chaource:~/src/javaee-memoapp2# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                     NAMES
d9e3953b0b7e        tomcat:8.5          "catalina.sh run"        11 seconds ago      Up 10 seconds       0.0.0.0:18080->8080/tcp   zealous_banach
```

起動時に、MySQLのデプロイでは指定してなかった`-p 18080:8080`を指定している。
これは「ホストOSへの18080/TCPアクセスをコンテナの8080/TCPへbindする」というオプション。

![docker-port-forward.png](https://qiita-image-store.s3.amazonaws.com/0/256859/ec6def1f-e1a3-9675-981d-05af1c7b30df.png)

これを指定することで、(図のWindowsのように)Tomcatコンテナへ直接アクセスできなくてもホストOS(図のUbuntu 18.04)の18080/TCPへアクセスすれば、Tomcat(の8080ポート)へアクセスできるようになる。

コンテナと直接アクセスできる場合(図のUbuntu 18.04からのブラウザアクセス)であれば、このオプションは必要ない。
その場合は、実行しているコンテナのIPアドレスを確認し、そのIPアドレスの8080ポートにアクセスすればよい。

コンテナのIPアドレスの確認方法は`docker inspect`で`IPAddress`の行を探す。
(`docker exec`でシェルログインし、`ip a`とか実行してももちろんOK)

```
root@chaource:~# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                     NAMES
ee586da83b13        mysql:5.7           "docker-entrypoint.s…"   36 hours ago        Up 36 hours         3306/tcp, 33060/tcp       docker_memoapp-db_1
4d114d7a8376        memoapp:latest      "catalina.sh run"        36 hours ago        Up 36 hours         0.0.0.0:18082->8080/tcp   docker_memoapp_1
root@chaource:~# docker inspect 4d11 | grep -i ipaddress
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "172.23.0.2",
root@chaource:~# 
```

この出力であれば、http://172.23.0.2:8080/ にアクセスすれば良い。

### コンテナにシェルログインする

コンテナの指定は、`docker ps`で表示される`CONTAINER ID`か`NAMES`を指定する。
コンテナID(ハッシュ値)は実は全桁入力せずに先頭3-4桁で特定できるなら一部の入力でも動く。

```
root@chaource:~/src/javaee-memoapp2# docker exec -it d9e bash
root@d9e3953b0b7e:/usr/local/tomcat# 
```

内部の状態

```
root@a15a72232cfe:/usr/local/tomcat# env | sort
CATALINA_HOME=/usr/local/tomcat
GPG_KEYS=05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 713DA88BE50911535FE716F5208B0AB1D63011C7 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23
HOME=/root
HOSTNAME=d9e3953b0b7e
JAVA_DEBIAN_VERSION=8u181-b13-2~deb9u1
JAVA_HOME=/docker-java-home/jre
JAVA_VERSION=8u181
LANG=C.UTF-8
LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib
OPENSSL_VERSION=1.1.0j-1~deb9u1
PATH=/usr/local/tomcat/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/usr/local/tomcat
SHLVL=1
TERM=xterm
TOMCAT_ASC_URLS=https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-8/v8.5.37/bin/apache-tomcat-8.5.37.tar.gz.asc      https://www-us.apache.org/dist/tomcat/tomcat-8/v8.5.37/bin/apache-tomcat-8.5.37.tar.gz.asc        https://www.apache.org/dist/tomcat/tomcat-8/v8.5.37/bin/apache-tomcat-8.5.37.tar.gz.asc         https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.37/bin/apache-tomcat-8.5.37.tar.gz.asc
TOMCAT_MAJOR=8
TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib
TOMCAT_SHA512=be6d6df8b49a760b2e181d4a45d8e6dc7bba5ef2ec6a000f8562cf5f34db5b7fac300cba65bca782bfd25a9f9d8d4a48625f1ad046115c1d6629ea5f210a2718
TOMCAT_TGZ_URLS=https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-8/v8.5.37/bin/apache-tomcat-8.5.37.tar.gz  https://www-us.apache.org/dist/tomcat/tomcat-8/v8.5.37/bin/apache-tomcat-8.5.37.tar.gz    https://www.apache.org/dist/tomcat/tomcat-8/v8.5.37/bin/apache-tomcat-8.5.37.tar.gz     https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.37/bin/apache-tomcat-8.5.37.tar.gz
TOMCAT_VERSION=8.5.37
_=/usr/bin/env
root@d9e3953b0b7e:/usr/local/tomcat# 
```

Tomcatにwarファイルをデプロイするうえでチェックするのは、環境変数`CATALINA_HOME`

```
root@d9e3953b0b7e:/usr/local/tomcat# echo $CATALINA_HOME/
/usr/local/tomcat/
root@d9e3953b0b7e:/usr/local/tomcat# ls $CATALINA_HOME/webapps/
ROOT  docs  examples  host-manager  manager
```

このパス(`$CATALINA_HOME/webapps/`)にアプリケーションのwarを置けばOK (これはコンテナ云々じゃなくてTomcatの話)

ちなみに、肝心のコンテナ間通信については

```
root@d9e3953b0b7e:/usr/local/tomcat# ping -c 4 memoapp-db
PING memoapp-db (172.18.0.2) 56(84) bytes of data.
64 bytes from memoapp-db.memoapp-network (172.18.0.2): icmp_seq=1 ttl=64 time=0.043 ms
64 bytes from memoapp-db.memoapp-network (172.18.0.2): icmp_seq=2 ttl=64 time=0.077 ms
64 bytes from memoapp-db.memoapp-network (172.18.0.2): icmp_seq=3 ttl=64 time=0.102 ms
64 bytes from memoapp-db.memoapp-network (172.18.0.2): icmp_seq=4 ttl=64 time=0.369 ms

--- memoapp-db ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3061ms
rtt min/avg/max/mdev = 0.043/0.147/0.369/0.130 ms
root@d9e3953b0b7e:/usr/local/tomcat#
```

名前解決もできてるし疎通も確認できる。
(本当はDB接続できているかは`mysql`コマンドとかないと厳しけど、Tomcatコンテナからは無理なので省略)

### ホストOSからコンテナへファイルをコピーする

このファイル

```
root@chaource:~/src/javaee-memoapp2# ls target/memoapp2.war 
target/memoapp2.war
```

を

```
root@chaource:~/src/javaee-memoapp2# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                     NAMES
d9e3953b0b7e        tomcat:8.5          "catalina.sh run"        About a minute ago   Up About a minute   0.0.0.0:18080->8080/tcp   zealous_banach
```

このコンテナにコピーする。
ホストOSからコンテナへファイル転送するには`docker cp`で。

```
root@chaource:~/src/javaee-memoapp2# docker cp target/memoapp2.war d9e3953b0b7e:/usr/local/tomcat/webapps/
root@chaource:~/src/javaee-memoapp2# 
```

これでwarファイルの転送ができ、アプリケーションのデプロイが完了

```
root@d9e3953b0b7e:/usr/local/tomcat# ls -l $CATALINA_HOME/webapps/
total 3840
drwxr-xr-x  3 root root    4096 Jan 23 07:56 ROOT
drwxr-xr-x 14 root root    4096 Jan 23 07:56 docs
drwxr-xr-x  6 root root    4096 Jan 23 07:56 examples
drwxr-xr-x  5 root root    4096 Jan 23 07:56 host-manager
drwxr-xr-x  5 root root    4096 Jan 23 07:56 manager
drwxr-x---  4 root root    4096 Feb  4 23:21 memoapp2
-rw-r--r--  1 root root 3905453 Feb  4 12:43 memoapp2.war
root@d9e3953b0b7e:/usr/local/tomcat# 
```

Tomcatのログ

```
root@chaource:~/src/javaee-memoapp2# docker logs d9e
:
:
04-Feb-2019 23:21:56.508 INFO [localhost-startStop-2] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/usr/local/tomcat/webapps/memoapp2.war]
04-Feb-2019 23:21:57.746 INFO [localhost-startStop-2] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
04-Feb-2019 23:21:57.804 INFO [localhost-startStop-2] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/usr/local/tomcat/webapps/memoapp2.war] has finished in [1,295] ms
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/12218a37-88fb-23d4-3e98-efffe07b6cb9.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/52f43e75-481c-1a4d-a5d5-f8642ca4a9b9.png)

さすが`--character-set-server=utf8`の威力！

---

コンテナの環境変数は使えないのか…な？

```
root@chaource:~/src/javaee-memoapp2# docker cp target/memoapp2.war d9e3953b0b7e:'$CATALINA_HOME/webapps/'
no such directory
```

## パターンB: war組み込みのカスタムイメージ作成

ここまでだと、実行環境をVMからDockerに置き換えただけで、手順的にも`apt`や`yum`のインストール・`systemctl`でのサービス起動/停止操作が、`docker pull`や`docker run`に変わったのと、さほど違いがない。

そこで『「Tomcatコンテナを起動して手動でwarをデプロイ」したあとの状態のイメージ』を作成して、いつでもアプリケーションの初期状態を`run`できるようにする。
これを作ることで再作成(再起動ではない)が容易になったり、アプリケーションをほかの環境へ配布したりすることができる。

### Dockerfileの作成

カスタムイメージを作るには`Dockerfile`を作り、`docker build`する。
今回は[OfficialのTomcatイメージ](https://hub.docker.com/_/tomcat)にアプリケーションwarをcopyするだけのカスタムイメージを作る。

といってもこれだけ

```Dockerfile:Dockerfile
FROM tomcat:8.5

COPY memoapp2.war /usr/local/tomcat/webapps/
```

`FROM`がベースイメージの指定で、(デフォルト設定ではDockerHubのレジストリが指定されているので)Tomcatのイメージ名とタグ名を指定する。

`COPY`でイメージに入れ込むファイルをコピーする。
同様のコマンドに`ADD`があり、こちらは単純なファイルコピーだけでなく、web上のファイルを指定してイメージに組み込むこともできる。

本来はこれ以外にも`CMD`や`ENTRYPOINT`でコンテナ起動後に実行するコマンドを指定したり、アプリケーションが使用するポート番号指定の`EXPOSE`などあるが、この辺りはベースイメージ(今回`FROM`で指定した`tomcat:8.5`)のものが継承されるため、指定していない。
(※ 素のdebianやcentosをベースに必要なパッケージを`apt`や`yum`で`RUN`コマンドでインストールして、、、みたいに作る場合は、実行するコマンドの指定をする)

- [Dockerfile リファレンス — Docker-docs-ja 17.06.Beta ドキュメント](http://docs.docker.jp/engine/reference/builder.html)
- [Dockerfile のベストプラクティス — Docker-docs-ja 1.9.0b ドキュメント](http://docs.docker.jp/engine/articles/dockerfile_best-practice.html)

### イメージのビルド

```
root@chaource:~/docker# docker build -t memoapp2 .
Sending build context to Docker daemon  3.908MB
Step 1/2 : FROM tomcat:8.5
 ---> 7ee26c09afb3
Step 2/2 : COPY memoapp2.war /usr/local/tomcat/webapps/
 ---> 598d605d24ce
Successfully built 598d605d24ce
Successfully tagged memoapp2:latest
root@chaource:~/docker# 
```

warを入れるだけだと秒で完了する。
できたイメージがこちら。(タグ名を指定しなかったので`latest`になっている)

```
root@chaource:~/docker# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
memoapp2            latest              598d605d24ce        10 seconds ago      466MB
```

### カスタムイメージからコンテナ起動

カスタムイメージはあくまでコンテナの中身のカスタムを行っているので、コンテナの起動/実行に関するオプション(ネットワークの指定など)は指定が必要。

素のTomcatコンテナの起動に`18080`ポートを使っているので、今度は`18081`で起動

```
root@chaource:~/docker# docker run --network memoapp-network -p 18081:8080 -d memoapp2
4255587eb565b20e040c50ea3516900a5bb96d99b23cc8549868d7e9d0c76c99
root@chaource:~/docker# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                     NAMES
4255587eb565        memoapp2            "catalina.sh run"        4 seconds ago       Up 3 seconds        0.0.0.0:18081->8080/tcp   reverent_mahavira
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/1b4975d1-b0c5-6542-dfe4-5eec6f1c387f.png)

アクセス先DBが同じなので、先に起動して実行したアプリケーションで登録したデータが見えている。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/1e19d5bd-23c5-e232-f3ee-ec8480ff4bcb.png)

追加もOK

# Docker Compose

ここまでで「DBコンテナ」「APサーバコンテナ」の二つを連携させたが、大きなシステムほど構成要素が増え、また依存関係(APの前にDBが起動してないといけない、とか)もでてくる。
これを手動で管理するのは大変だし、シェルスクリプト作ってでできなくもないけど、Docker ComposeというYAMLファイルで構成を定義して起動や停止などを管理できる機能があるので、それについて。

- [Docker Compose — Docker-docs-ja 17.06.Beta ドキュメント](http://docs.docker.jp/compose/toc.html)

## インストール

Docker本体に入ってないのでインストールする。
Ubuntuだと`apt`で入る

```
root@chaource:~# apt install docker-compose
```

```
root@chaource:~# docker-compose version
docker-compose version 1.17.1, build unknown
docker-py version: 2.5.1
CPython version: 2.7.15rc1
OpenSSL version: OpenSSL 1.1.0g  2 Nov 2017
```

## APとDBのコンテナをデプロイするYAMLファイル

書式はだいたいここ

- [Compose file version 3 reference | Docker Documentation](https://docs.docker.com/compose/compose-file/#command)

すでに`docker run`で起動確認できている`mysql:5.7`と作成済みカスタムイメージ`memoapp2:latest`に対して、次のように`docker-compose.yml`ファイルを作成する

```yaml:docker-compose.yml
version: '3'
services:
  memoapp:
    image: memoapp2:latest
    ports:
      - 18082:8080
  memoapp-db:
    image: mysql:5.7
    environment: 
      - MYSQL_DATABASE=memoapp_db
      - MYSQL_USER=memoapp
      - MYSQL_PASSWORD=memoapp
      - MYSQL_RANDOM_ROOT_PASSWORD=yes
    command: mysqld --character-set-server=utf8
```

実行

```
root@chaource:~/docker# docker-compose up -d
Creating network "docker_default" with the default driver
Creating docker_memoapp-db_1 ... 
Creating docker_memoapp_1 ... 
Creating docker_memoapp-db_1
Creating docker_memoapp-db_1 ... done
root@chaource:~/docker# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                     NAMES
2c8be6cb4308        memoapp2:latest     "catalina.sh run"        24 seconds ago      Up 23 seconds       0.0.0.0:18082->8080/tcp   docker_memoapp_1
5e18c0aca11d        mysql:5.7           "docker-entrypoint.s…"   24 seconds ago      Up 22 seconds       3306/tcp, 33060/tcp       docker_memoapp-db_1
root@chaource:~/docker# docker exec -it docker_memoapp_1 bash
root@2c8be6cb4308:/usr/local/tomcat# ping memoapp-db
PING memoapp-db (172.20.0.3) 56(84) bytes of data.
64 bytes from docker_memoapp-db_1.docker_default (172.20.0.3): icmp_seq=1 ttl=64 time=0.181 ms
64 bytes from docker_memoapp-db_1.docker_default (172.20.0.3): icmp_seq=2 ttl=64 time=0.087 ms
64 bytes from docker_memoapp-db_1.docker_default (172.20.0.3): icmp_seq=3 ttl=64 time=0.080 ms
64 bytes from docker_memoapp-db_1.docker_default (172.20.0.3): icmp_seq=4 ttl=64 time=0.091 ms
^C
--- memoapp-db ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3052ms
rtt min/avg/max/mdev = 0.080/0.109/0.181/0.043 ms
root@2c8be6cb4308:/usr/local/tomcat# exit
root@chaource:~/docker# 
```

docker-compose.ymlでは明示的に記述しなかったが、何も書かなかったら`docker_default`というネットワークが作成され、そのネットワーク上でコンテナ間通信ができるようになる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/256859/446ba241-4727-7c2e-d847-ea0cba47d5de.png)


停止(破棄)するには

```
root@chaource:~/docker# docker-compose down
Stopping docker_memoapp_1    ... done
Stopping docker_memoapp-db_1 ... done
Removing docker_memoapp_1    ... done
Removing docker_memoapp-db_1 ... done
Removing network docker_default
root@chaource:~/docker# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
root@chaource:~/docker# 
```

綺麗になりました。

## APイメージのビルドも含めてYAMLで定義

前段では作成済みカスタムイメージができている状態で、それをデプロイするだけの定義だったが、カスタムビルドをビルドする処理も含めてDocker Composeで定義することもできる。

### ディレクトリ構成

イメージのビルドに関連するファイルのディレクトリ構成はこの通り

```
.
├── docker
│   ├── docker-compose.yml
│   └── Dockerfile
└── target
    └── memoapp2.war
```

Dockerfileの仕様で、`COPY`はカレントディレクトリ以下のファイルしか扱えない。
そのため、dockerディレクトリから`../target/memoapp2.war`というパスのCOPYはできない。

じゃあどうするかというと、`docker-compose.yml`の定義で、「どのディレクトリを基準とするか」を定義する`context`という設定項目があるので、これを`..`つまり`docker`や`target`ディレクトリのあるディレクトリが基準となるよう設定する。
そして`dockerfile`のDockerfileのある場所の指定で、`docker/Dockerfile`と指定する。
Dockerfileの中のwarファイルの`COPY`処理は、基準が`target`の上位ディレクトリなので、`target/memoapp2.war`を`COPY`できるようになる。
(うまく説明できない)

### docker-compose.yml

ビルドを定義するには`build`で定義を記述する。

```yaml:docker-compose.yml
version: '3'
services:
  memoapp:
    build:
      context: ..
      dockerfile: docker/Dockerfile
    image: memoapp:latest
    ports:
      - 18082:8080
  memoapp-db:
    image: mysql:5.7
    environment: 
      - MYSQL_DATABASE=memoapp_db
      - MYSQL_USER=memoapp
      - MYSQL_PASSWORD=memoapp
      - MYSQL_RANDOM_ROOT_PASSWORD=yes
    command: mysqld --character-set-server=utf8
```

### Dockerfile

```Dockerfile:Dockerfile
FROM tomcat:8.5

COPY target/memoapp2.war /usr/local/tomcat/webapps/
```

### ビルドとデプロイ

```
root@chaource:~/src/javaee-memoapp2/docker# docker-compose up -d
Creating network "docker_default" with the default driver
Building memoapp
Step 1/2 : FROM tomcat:8.5
 ---> 7ee26c09afb3
Step 2/2 : COPY target/memoapp2.war /usr/local/tomcat/webapps/
 ---> 2cd39d9db4c2
Successfully built 2cd39d9db4c2
Successfully tagged memoapp:latest
WARNING: Image for service memoapp was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating docker_memoapp_1 ... 
Creating docker_memoapp-db_1 ... 
Creating docker_memoapp_1
Creating docker_memoapp_1 ... done
root@chaource:~/src/javaee-memoapp2/docker# 
```

```
root@chaource:~/src/javaee-memoapp2/docker# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                     NAMES
ee586da83b13        mysql:5.7           "docker-entrypoint.s…"   17 seconds ago      Up 15 seconds       3306/tcp, 33060/tcp       docker_memoapp-db_1
4d114d7a8376        memoapp:latest      "catalina.sh run"        17 seconds ago      Up 15 seconds       0.0.0.0:18082->8080/tcp   docker_memoapp_1
```

(画面は同じなのでもう省略するね。。)