Spring bootで簡単なWebアプリを作ってみる(環境構築)

こんにちは。船井総研デジタルのいっちーです。

前回の記事では、このシリーズで作っていくシステムの要件について考えてみました。

https://qiita.com/fsd_itchy/items/384f05225e83f2705cf4

早速今回から、開発に入っていきたいと思います。

# 開発環境・実行環境を整える

今回の開発で使う環境・ツールは以下のとおりです。

- WSL2/Ubuntu version 2
- openjdk version 19.0.2
- Apache Maven version 3.8.7
- Docker version 20.10.22
- VSCode version 1.76.2
- その他、DB接続ツール等

VSCodeからWSL2/Ubuntuにリモート接続して、開発を行う…という想定です。以降、本稿ではWSL2/Ubuntuのことを単にUbuntuと記載します。各ツール類のインストールや設定方法は、他の記事にお譲りすることにして、早速環境を作っていきましょう。

まずは開発用のフォルダを作成します。

![フォルダ構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280429/95c5c51e-b432-47e9-9e59-f4fd2cd39b0c.png)


Javaソースを格納するフォルダと、Dockerファイルを格納するフォルダを作りました。Ubuntuからは、下記のように見ることができます。

```
$ ls /mnt/c/work/negitter/
README.md  app  docker
```

# コンテナを設計して、設定ファイルを作る
## コンテナ設計
前回の記事で考えたコンテナ構成を設計に落としていきます。

![コンテナ設計.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280429/3de8ca70-6a94-aa5b-bfda-1eca6d198dff.png)

ホスト側から設定したいものはbind mountで、DBデータはVolumeで永続化する想定です。この設計に基づいて、docker-compose.ymlとDockerfileを作っていきます。

## docker-compose.ymlとDockerfileの作成

### docker-compose.yml

docker-compose.ymlは以下のようになりました。

```yaml:docker-compose.yml
version: "3"
services:
  web:
    container_name: web
    build:
      context: .
      dockerfile: ./web/Dockerfile
    restart: always
    volumes:
      - ./web/conf.d:/etc/nginx/conf.d
      - ./web/log:/var/log/nginx
      - ./web/src:/usr/share/nginx/html
    ports:
      - ${HTTP_PORT}:80
    networks:
      mynetwork:
        ipv4_address: ${WEB_IPADDRESS}

  ap:
    container_name: ap
    build:
      context: .
      dockerfile: ./ap/Dockerfile
    restart: always
    volumes:
      - ./ap/webapps:/usr/local/tomcat/webapps
      - ./ap/log:/usr/local/tomcat/logs
    ports:
      - ${AP_PORT}:8080
    networks:
      mynetwork:
        ipv4_address: ${AP_IPADDRESS}

  db:
    container_name: db
    build:
      context: .
      dockerfile: ./db/Dockerfile
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_ja_0900_as_cs_ks
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${ROOT_PASS}
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASS}
      TZ: ${TZ}
    ports:
        - ${DB_PORT}:3306
    volumes:
        - db-store:/var/lib/mysql
        - ./db/script:/docker-entrypoint-initdb.d
    networks:
      mynetwork:
        ipv4_address: ${DB_IPADDRESS}

volumes:
  db-store:
networks:
  mynetwork:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: ${SUBNET}
```

```env:.env
COMPOSE_PROJECT_NAME=negitter
ROOT_PASS=root
DB_NAME=negitter
DB_USER=user
DB_PASS=pass
DB_PORT=3306
AP_PORT=8080
HTTP_PORT=80
TZ=Asia/Tokyo
WEB_IPADDRESS=172.19.0.23
AP_IPADDRESS=172.19.0.24
DB_IPADDRESS=172.19.0.22
SUBNET=172.19.0.0/24
```

他の作業で作ってるコンテナとバッティングしたときに修正がしやすいように、IPアドレスやポート番号といった情報は`.env`ファイルに外出ししています。ポートはデフォルトポートで書いていますが、必要に応じて書き換えると良いでしょう。

### Webコンテナ
WebコンテナのDockerfileは以下のようになりました。設定ファイルは、とりあえずリバースプロキシの設定のみ記載しています。

```Dockerfile:Dockerfile
FROM nginx:latest
EXPOSE 80
RUN service nginx start
```

```conf:conf.d/nginx.conf
server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    location / {
        proxy_pass http://172.19.0.24:8080;
    }
}
```

### APコンテナ

APコンテナのDockerfileは以下のようになりました。今回、特に追加でインストールするもの等はありません。

```Dockerfile:Dockerfile
FROM tomcat:latest
EXPOSE 8080
```

### DBコンテナ

DBコンテナのDockerfileは以下のようになりました。文字コードを設定するための設定ファイルと、それをコンテナ内に取り込むための処理を記載しています。また、コンテナ構築と同時にテーブル作成を行うため、`docker-entrypoint-initdb.d`に紐づけたフォルダにテーブル作成のスクリプトを格納します。

```Dockerfile:Dockerfile
FROM mysql:latest
ADD ["./db/conf/my.cnf", "/etc/mysql/conf.d/"]
RUN chmod 644 /etc/mysql/conf.d/my.cnf
```

```conf:conf/my.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_ja_0900_as_cs_ks

[client]
default-character-set=utf8mb4
```

```sql:script/00_create_tables.sql
use negitter; 

DROP TABLE if exists negi_mst; 

CREATE TABLE negi_mst( 
    id int NOT NULL AUTO_INCREMENT COMMENT 'ID'
    , negi_name varchar (128) NOT NULL COMMENT 'ねぎ名称'
    , register_datetime datetime NOT NULL COMMENT '登録日時'
    , PRIMARY KEY (id)
) COMMENT = 'ねぎマスタ'; 

DROP TABLE if exists message_tbl; 

CREATE TABLE message_tbl( 
    id int NOT NULL AUTO_INCREMENT COMMENT 'ID'
    , register_name varchar (256) NOT NULL COMMENT '投稿者名'
    , negi_message varchar (256) NOT NULL COMMENT 'つぶやき'
    , register_datetime datetime NOT NULL COMMENT '投稿日時'
    , PRIMARY KEY (id)
) COMMENT = 'メッセージテーブル'; 
```
必要に応じて、初期データ投入のスクリプトを追加しても良いでしょう。今回は空のテーブルが作れれば十分なので、これでOKとします。


最終的に、Dockerの設定ファイルは以下のような構成になりました。

```
.
├── .env
├── docker-compose.yml
├── ap
│   └─── Dockerfile
├── db
│   ├── Dockerfile
│   ├── conf
│   │   └── my.cnf
│   └── script
│       └── 00_create_tables.sql
├── docker-compose.yml
└── web
    ├── Dockerfile
    └── conf.d
        └── nginx.conf
```

:::note info
**2023/03/24追記**
DBコンテナの設定でcollation-serverがutf8mb4_unicode_ciとなっていましたが、この設定だとひらがなとカタカナの区別をしなくなってしまうので、たとえば「深谷ねぎ」と「深谷ネギ」を同じ文字列だと判定してしまいます。
正しくはcollation-server=utf8mb4_ja_0900_as_cs_ksと設定するべきでした。お詫びして訂正いたします。
:::

## 起動と動作確認

### コンテナビルド～起動

`docker-compose`を使って、コンテナのビルドと起動を行います。

```
$ docker-compose build
Building web
Step 1/3 : FROM nginx:latest
 ---> a99a39d070bf
Step 2/3 : EXPOSE 80
 ---> Using cache
 ---> 9a384215c0c4
Step 3/3 : RUN service nginx start
 ---> Using cache
 ---> a2f1bb17a22b
Successfully built a2f1bb17a22b
Successfully tagged negitter_web:latest
Building ap
Step 1/2 : FROM tomcat:latest
 ---> ad4994520144
Step 2/2 : EXPOSE 8080
 ---> Using cache
 ---> 337cb58f5bfe
Successfully built 337cb58f5bfe
Successfully tagged negitter_ap:latest
Building db
Step 1/3 : FROM mysql:latest
 ---> b939d379d46e
Step 2/3 : ADD ["./db/conf/my.cnf", "/etc/mysql/conf.d/"]
 ---> Using cache
 ---> 22469fea4200
Step 3/3 : RUN chmod 644 /etc/mysql/conf.d/my.cnf
 ---> Using cache
 ---> 6602c45ce192
Successfully built 6602c45ce192
Successfully tagged negitter_db:latest
```

コンテナのビルドができたので、起動します。

```
$ docker-compose up -d
Creating network "negitter_mynetwork" with driver "bridge"
Creating volume "negitter_db-store" with default driver
Creating web ... done
Creating ap  ... done
Creating db  ... done
```

### DBコンテナの疎通確認

手近なDB接続ツールを使って、DBコンテナにアクセスします。DB`negitter`上に、`negi_mst`と`message_tbl`の2つのテーブルが見えていれば成功です。

![DB動作確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280429/6d87d730-760b-f0c9-08c2-246522a4213e.png)


### WEB・APコンテナの疎通確認

まだWebアプリの本体は作っていないので、Tomcatのサンプルアプリケーションを使って疎通確認します。

[Tomcat公式](https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/)から、サンプルアプリケーションのwarファイル`sample.war`をダウンロードします。

ダウンロードした`sample.war`を、`C:\work\negitter\docker\ap\webapps`配下にコピーします。Tomcatが立ち上がっていれば、自動的にwarが展開されてsampleアプリケーションがデプロイされます。

![Tomcat動作確認,png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280429/0823d735-8e42-ef4d-6116-e3d8d24c3151.png)


数分待って、[http://localhost/sample/](http://localhost/sample/)にアクセスし、Tomcatのサンプルページが立ち上がれば疎通確認は完了です。

![Tomcat動作確認2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280429/9a7851a5-188c-e626-63d9-8291cbc4e3cd.png)

疎通確認できたら、`sample.war`は削除しても構いません。

# まとめ

今回の記事では、環境構築と疎通確認まで行いました。次回は、Spring bootのプロジェクトを作成し、いよいよアプリ本体を作っていこうと思います。

それではまた。