Spring + MySQL を用いたREST APIのDocker化メモ

* [SpringとMySQLを利用したREST API](https://qiita.com/KWS_0901/items/edc303066d8a00006bc1)をDockerで動作させた際のメモ
* Springで作成したAPIをDockerで動作するTomcatにデプロイし、同じくDockerで動作するMySQLに接続させる。

## プロジェクト構成

```
ルート
│  docker-compose.yml
│  
├─ap
│  └─docker
│      │  Dockerfile
│      │
│      └─webapps
│              rest.war
│
└─db
    └─docker
        └─db
            │  my.cnf

```

### docker-compose.yml

```yaml
version: "3"

services:
  ap:
    build: ./ap/docker
    image: rest_api_image
    container_name: tomcat_container
    ports:
      - 8080:8080
    links:
      - db
  db:
    image: mysql:5.7
    container_name: mysql_container
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: sample_db
      MYSQL_USER: mysqluser
      MYSQL_PASSWORD: mysqlpass
    volumes:
      - ./db/docker/db/data:/var/lib/mysql
      - ./db/docker/db/my.cnf:/etc/mysql/conf.d/my.cnf
    restart: always
    ports:
      - 3306:3306

```



### ap

* **`Dockerfile`**

  ```dockerfile
  FROM tomcat:8.5.35
  EXPOSE 8080
  RUN useradd -s /sbin/nologin tomcat
  RUN chown -R tomcat:tomcat /usr/local/tomcat
  USER tomcat
  COPY webapps/rest.war /usr/local/tomcat/webapps/
  CMD ["catalina.sh", "run"]
  ```

  

* **`rest.war`**

  * [こちら](https://qiita.com/KWS_0901/items/edc303066d8a00006bc1)で作成したAPIを以下のように改修し`.war`ファイルにビルドする。
  * 改修箇所
        * **エントリポイント(`UserApplication`)**
            * `SpringBootServletInitializer`クラスを継承させ、`configure`メソッドをoverrideさせる。

    ```java
    package com.example.restservice;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
    import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
    
    @SpringBootApplication
    @EnableJpaAuditing
    public class UserApplication extends SpringBootServletInitializer {
    
    	public static void main(String[] args) {
    		SpringApplication.run(UserApplication.class, args);
    	}
    
    	@Override
    	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    		return application.sources(UserApplication.class);
    	}
    
    }
    ```

    * **`pom.xml`**
      * 以下を追加。

    ```xml
    		<packaging>war</packaging>		
    		...
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-tomcat</artifactId>
    			<scope>provided</scope>
    		</dependency>
    ```

    * **DB接続設定(`application.properties`)**
      * localhostではなく、DBコンテナ名を指定する。

    ```properties
    spring.datasource.url=jdbc:mysql://mysql_container:3306/sample_db
    spring.datasource.username=mysqluser
    spring.datasource.password=mysqlpass
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    ```

### db

* **`my.cnf`**

  * 文字コード指定

    ```cnf
       [mysqld]
       character-set-server=utf8mb4
       collation-server=utf8mb4_unicode_ci
    
       [client]
       default-character-set=utf8mb4
    ```

    

## 動作確認

**1.コンテナを起動する。**

```shell
docker-compose up
```

**2.DBにテーブルを作成する。**

* [こちらのSQL](https://qiita.com/KWS_0901/items/edc303066d8a00006bc1#%E6%8E%A5%E7%B6%9A%E7%94%A8mysql-db%E6%A7%8B%E7%AF%89)を実行し、テーブルを作成する。

**3.APIを呼び出す。**

※呼び出しの際には、http://localhost:8080/{warファイル名}/api/users となる点に注意する。

**ユーザー登録API(`POST /rest/api/users`)**

* リクエスト

  ```http
  POST /rest/api/users HTTP/1.1
  Host: localhost:8080
  Content-Type: application/json
  Content-Length: 24
  
  {
      "name":"test4"
  }
  ```

* レスポンス

  ```json
  {
      "id": 4,
      "name": "test4"
  }
  ```

* **ユーザー取得API(`GET /rest/api/users/{user_id}`)**

  * リクエスト

    ```http
    GET /rest/api/users/4 HTTP/1.1
    Host: localhost:8080
    Content-Type: application/json
    ```

  * レスポンス

    ```json
    {
        "id": 4,
        "name": "test4"
    }
    ```

* **ユーザー一覧取得API (`GET /rest/api/users`)**

  * リクエスト

    ```http
    GET /rest/api/users HTTP/1.1
    Host: localhost:8080
    Content-Type: application/json
    ```

  * レスポンス

    ```json
    [
        {
            "id": 1,
            "name": "test"
        },
        {
            "id": 2,
            "name": "test2"
        },
        {
            "id": 3,
            "name": "test3"
        },
        {
            "id": 4,
            "name": "test4"
        }
    ]
    ```

## 詰まったところ

### 404 Not Found

* 以下の対応を行った。
  * `Dockerfile`に`tomcat`ユーザーを作成し、権限設定する処理を記述([参考](https://forums.docker.com/t/unable-to-access-the-war-on-tomcat-container-404-not-found/83231))。
  * エントリーポイントクラスに`SpringBootServletInitializer`クラスを継承させ、`configure`メソッドをoverride([参考](https://qiita.com/NagaokaKenichi/items/3f191aeb6f161101d5f6))。



## 参考情報

* [Spring BootでWARを作成して別のTomcatにデプロイする](https://qiita.com/NagaokaKenichi/items/3f191aeb6f161101d5f6)