NGINX+Tomcat+DockerでWebサーバを構築する。

# 目的

NGINX+Tomcat+Docker構成でサーバ環境を構築できるようになる

# 背景

過去PJにおいて、アプリケーションコードを書いたことはあるが、サーバ側の構築などのインフラ関連作業を行なったことがなかったため、実際に構築体験し身につけたかった。それと、最近勉強したDockerも活用してみたいと思った。

# 各種概要

## NGINXとは

静的コンテンツを高速に配信するように設計されているWebサーバ。リバースプロキシ機能を持つため、背後にWebアプリケーションサーバを配置して動的なコンテンツを配信したり、ソフトウェアロードバランサやHTTPキャッシュとしても機能する。
Apacheで問題だったC10K問題を克服したWebサーバ。

https://ja.wikipedia.org/wiki/Nginx

## Tomcatとは

正式名称`Apache Tomcat`（本投稿記事内ではTomcatと表記）。
JavaサーブレットやJSPを実行するためのWebコンテナ。

https://tomcat.apache.org/

## Dockerとは

アプリケーションを開発（developing）、移動（shipping）、実行（running）するためのオープンなプラットフォーム。

https://docs.docker.com/get-started/overview/

コンテナの中にアプリケーション開発環境を構築し開発を行う、サーバ環境を構築、テスト環境を構築するなど、様々な使い方がある。
ローカル環境を汚さない、インフラがコード化されているため他環境への移行、新規入場者への環境提供が楽、構築手順ミスによる環境不正を防げる等といったメリットもある。

# 全体像

こんな感じの構成です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38724/ccfb6619-2c85-c40b-9b73-a2f763a58ff3.png)

# 環境

os: macOS Monterey 12.5
machine: MacBook Air(Retina, 13-inch, 2018)
cpu: 1.6GHz デュアルコアIntel Core i5

Dockerデスクトップをインストール済みです。

```zsh
% brew list --version | grep docker
docker 4.11.1,84025
```

# 今回のファイル

全てGitHubにあります。

https://github.com/MamedenQ/docker_nginx_tomcat/tree/v1

# 内容

## Dockerfileの作成

NGINX、Tomcatサーバコンテナを作るためのDockerfileを作成します。

- NGINX

80ポートで待ち受け、初期状態で作成されている`/etc/nginx/conf.d`ディレクトリを削除します。
後に作成するdocker-compose.ymlで、設定ファイルが格納されたディレクトリが`conf.d`という名称でマウントされるように指定されます。

```Dockerfile:nginx-server/Dockerfile
FROM nginx:latest

EXPOSE 80

RUN rm -rf /etc/nginx/conf.d
```

:::note
[EXPOSE](https://docs.docker.com/engine/reference/builder/#expose)は、指定されたポートで待ち受けるよう命令するものです。
:::

- Tomcat

```Dockerfile:tomcat-server/Dockerfile
FROM tomcat:latest

EXPOSE 8080
```

## docker-compose.ymlの作成

NGINX、Tomcatコンテナをビルド、マウントするディレクトリを指定したdocker-compose.ymlを作成します。

ビルドするDockerfileの指定は各Dockerfileが配置されているディレクトリを指定します。
マウントするディレクトリは、各コンテナごとで下記となります。
- NGINX
    - NGINX設定が入ったディレクトリ
    - ログディレクトリ
- Tomcat
    - アプリケーションが入ったディレクトリ
    - ログディレクトリ

```zsh:ディレクトリ構成
% tree
.
├── docker-compose.yml
├── nginx-server
│   ├── Dockerfile
│   ├── conf.d            # NGINX設定ディレクトリ
│   │   └── nginx.conf
│   └── log               # NGINXログディレクトリ
└── tomcat-server
    ├── Dockerfile
    ├── log               # Tomcatログディレクトリ
    └── webapps           # アプリケーションディレクトリ
        └── sample
            └── sample.html
```

```yaml:docker-compose.yml
version: "3"
services:
    nginx-server:
        build:
            context: ./nginx-server
        volumes:
            - ./nginx-server/conf.d:/etc/nginx/conf.d
            - ./nginx-server/log:/var/log/nginx
        ports:
            - 80:80

    tomcat-server:
        build:
            context: ./tomcat-server
        volumes:
            - ./tomcat-server/webapps:/usr/local/tomcat/webapps
            - ./tomcat-server/log:/usr/local/tomcat/logs
        ports:
          - 8080:8080
```

## Webサーバ（NGINX）からWebコンテナ（Tomcat）への転送設定を追加

NGINX側ポート80へリクエストが来た際に、Tomcat側ポート8080へリクエストを転送するように設定をします。
`proxy_pass`のアドレスにローカルアドレスを設定してください。

```nginx:nginx-server/conf.d/nginx.conf
server {
    listen 80;
    server_name localhost;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    location / {
        proxy_pass http://192.168.x.x:8080;
        # proxy_pass http://host.docker.internal:8080;
    }
}
```

:::note warn
WinやMacでDockerデスクトップを利用している場合、`proxy_pass`のアドレスに`host.docker.internal`を指定しdocker-composeで立ち上げた他のコンテナへアクセスすることが可能ですが、Dockerデスクトップを利用していないLinuxの場合、`host.docker.internal`が使えません。DockerデスクトップforLinuxが出たようですが、同様に`host.docker.internal`が使えるかは不明、かつ、環境によっては修正しないといけないのはあまり好まれないので、IP指定が良いかと思います。
:::

## アプリケーションコンテンツを作成

実際はWebアプリを配置しますが、本投稿では静的なhtmlを配置するに留めます。

```html:tomcat-server/webapps/sample/sample.html
<h1>hello sample.html</h1>
```

## コンテナをビルド

```
% docker-compose build
```

## コンテナを起動

```
% docker-compose up -d 
```

## アクセス

ブラウザを立ち上げるのも面倒なので、curlコマンドで確認します。

```
% curl http://localhost/sample/sample.html
<h1>hellotouch sample.html</h1>
```

意図通りHTMLが返ってきてますね。

## コンテナの起動を終了

```
% docker-compose down
```

# 何かおかしいなと思ったら

コンテナ起動後に下記コマンドでコンテナ内に入り操作できます。

```
% docker exec -it コンテナ /bin/bash 
```

ログフォルダについてもマウントしてあるので、コンテナ外から確認が可能です。

# 最後に

公式が用意しているDockerイメージを再利用できたこともあり、簡単に構築することができた。様々なサイトを参考にさせてもらったが、設定周りも特に苦しむことなく進められた。

`proxy_pass`のアドレス指定で`host.docker.internal`を指定すべきか、ローカルIPを指定すべきか、お試しなので当初はどちらでも良かったが、Docker環境によっては使える使えないがあるため、実際利用した時に困らないよう、より良い指定方法を探り、知れたのは良かったと思う。

他にも、リバプロの設定やロードバランサ機能など、今後この投稿を応用し知識を蓄えていきたい。
**2022/10/01 追加投稿しました。**

https://qiita.com/shinya_sun_sun/items/ec69513f4c086a397cb8