1歩ずつカスタマイズしながら作るDocker + Nginx + Flask リモートデバッグ環境(前半)

# 目標

- 簡単なウェブサービスの構成を仮想環境で作成する
- ウェブサービスの構成は
    - フロント: Nginx
    - バックエンド: WSGI(mod_wsgi) + Flask
- 仮想環境はDockerを利用してgitを通じて他の開発者と共有できるようにする
- ホストPCのOSに依存しないPythonのデバッグ環境を実現する
    - デバッグにはVSCodeのリモートデバッグを利用

# このドキュメントを読んで出来る事

目標を前半と後半にわけます。このページでは前半部分を書いています。

- 前半: ← ここ
    - 仮想環境のセットアップ(Hyper-V, Docker)
    - Nginxコンテナ作成
    - Apacheコンテナ作成
    - ウェブサービスの骨組みを作成
- 後半:
    - ウェブアプリ作成(Flask)
    - リモートデバッグ設定

# 必要な環境
- Windows10 Pro
- Chocolatey (なくても良いがあると便利)

# 新規にインストールされるパッケージ等
- Hyper-V(有効化)
- Docker for Windows

# 仮想環境のセットアップ

下記サイトが詳しく書かれていて参考になりました。
参考サイト: [[Win] Docker を Windows で使えるようにする](https://www.koreyome.com/web/docker-install-for-windows/)

以下簡単な流れだけ書きます
## Hyber-Vを有効化
1. `Win`キー + `S` → Windowsの機能の有効化または無効化
2. Hyper-V の項目にチェック
3. 再起動(必要があれば)

## Docker のインストール

1. powershellアイコンを右クリックして「管理者として実行する」を選択
2. 下記コマンドを入力してdockerをインストール  
※Chocolateyがない場合は[公式サイト](https://www.docker.com/products/docker-desktop)から[パッケージ](https://www.docker.com/products/docker-desktop)をダウンロードしてインストール

```powershell:管理者
PS>  choco install docker-desktop -y
```
## Docker の設定

1. インストール終了後、Widowsに再ログイン
2. [docker hub](https://hub.docker.com/)にアカウントを作成
3. Docker Desktopを起動
4. タスクバーのdockerアイコンからをShared Drivesの設定
    1. dockerアイコンを右クリック
    2. Setting
    3. Shared Drives
    4. 作業ドライブにチェック

# Nginxコンテナ作成

以下作業用フォルダ: `webapp-docker`

```powershell:作業用フォルダ作成と移動
PS> mkdir webapp-docker
PS> cd webapp-docker
```

```powershell:nginx用フォルダを作成
PS> mkdir nginx
```

`Dockerfile`ファイル作成。ファイルの中身は:point_down:

```docker:nginx/Dockerfile
FROM nginx
```

```powershell:イメージ作成とコンテナの起動
PS> docker build -t test_nginx nginx
PS> docker container run -p 80:80 --name test_nginx --rm test_nginx
```

## 動作確認

ブラウザで動作確認。http://localhost/
![first-nginx-screenshot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494172/6dd28603-f3b0-d054-7d81-f126e3018d1a.jpeg)

正常に動作しています:slight_smile:。 後始末して次にすすみます。

```powershell:nginxコンテナの終了
PS> docker container stop test_nginx
```

# Apacheコンテナ作成

Nginxの動作確認が出来たので、つぎはウェブサーバーのコンテナを作ります。後でウェブアプリも追加する予定なのでUbuntuのイメージから作っていくことにします。すでに他の人が目的に近いDockerfileを作ってくれているので、それをベースにカスタマイズしていきます。いまはapacheの動作が確認できればよいので、カスタマイズはvimとcurlのパッケージの追加のみしています。
手順をまとめると以下のようになります。

## Apacheコンテナ作成手順
1. ベースのDockerfileを作成
2. 必要なツール群(vim, curl)をDockerfileに追記
3. イメージの作成
4. コンテナの起動と動作確認
5. Dockerfileの微調整

```powershell:apache用フォルダ作成
PS> mkdir flask
```
`Dockerfile`ファイルを作成。ファイルの中身は:point_down:

```docker:flask/Dockerfile
FROM ubuntu
# Avoid warnings by switching to noninteractive
ENV DEBIAN_FRONTEND=noninteractive
# Apache
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_PID_FILE /var/run/apache2.pid
ENV APACHE_RUN_DIR /var/run/apache2
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_LOCK_DIR /var/lock/apache2
RUN apt-get update \
    && apt-get install -y --no-install-recommends apt-utils dialog 2>&1 \
    && apt-get install -y git iproute2 procps lsb-release \
    && apt-get install -y apache2
# others tools
RUN apt-get install -y vim curl
# Clean up
RUN apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*
EXPOSE 80
CMD ["apachectl", "-D", "FOREGROUND"]
```

```powershell:イメージ作成とコンテナの起動
PS> docker image build -t test_apache flask
PS> docker container run -p 80:80 --name test_apache --rm test_apache
```

## 動作確認

ブラウザで動作確認 http://localhost/
![first-apache-screenshot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494172/5ba1ae7d-ca13-a89b-02eb-88ff0050a9ac.jpeg)

コンソールに以下のようなワーニングが出ているのでつぎのステップで微調整します。

```powershell:ワーニング
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
```

## コンテナ内で試行錯誤

ここで下記のようにDockerfileを修正して再ビルドしても良いのですが、
1. Dockerfile 修正 ← 試行錯誤ポイント
2. ビルド
3. コンテナ作成
4. 動作確認 ← ここまで結果がわからない
Dockerfileの修正内容が間違っていた場合、再度、1.からやり直しになり時間をロスしてしまいます。(経験済:cry:) なので下記のほうが結果的に早く終わります。(Dockerfileにvimとcurlを記述したのはこのためです:v:)
1. コンテナ内で設定ファイル等を修正 ← 試行錯誤ポイント
2. 動作確認 ← 結果がすぐにわかる
3. Dockerfile 修正
4. ビルド
5. コンテナ作成
6. 動作確認

```powershell:別ターミナルで実行
PS> docker container exec -it test_apache bash
```

このエラーは、/etc/hosts にlocalhost以外に自分で設定したホスト名がApacheの設定ファイルに定義されていないために発生するらしいので、apacheの設定にSeverNameの定義を追加します。
参考: [ubuntuにおけるapache2のAH00558エラーを解決する](https://mk-55.hatenablog.com/entry/2014/07/07/004510)

```bash:コンテナ内
root# echo ServerName $HOSTNAME > /etc/apache2/conf-available/fqdn.conf
root# a2enconf fqdn
root# apachectl graceful
```

## Dockerfile に反映
エラーが出力されなくなったのでこの内容をDockerfileに書きます。

```dockerfile:Dockerfile修正
# 略
# others tools
RUN apt-get install -y vim curl
# ---追加---
RUN  echo ServerName $HOSTNAME > /etc/apache2/conf-available/fqdn.conf \
    && a2enconf fqdn
# ---ここまで---
# Clean up
```
powershellで再ビルト～コンテナ起動 → 動作確認します。

```powershell:動作確認
PS> docker container stop test_apache
PS> docker build -t test_apache flask
```
エラーが出力されなくなりました :smile: 

# ウェブサービスの骨組みを作成

各々のコンテナで動作確認出来たので、2つのコンテナが連帯して動作するように修正していきます。

## 複数コンテナの起動設定

作業用フォルダ`webapp-docker`に`docker-compose.yml`を作成します。ファイルの中身は:point_down:

```dockerfile:docker-compose.yml
version: '3'

services:
  nginx:
    build:
      context: ./nginx
      dockerfile: Dockerfile
    container_name: wbx
    ports:
      - 80:80
  flask:
    build:
      context: ./flask
      dockerfile: Dockerfile
    container_name: wbb3
    ports:
      - 5000:80
```

## 動作確認

```powershell:docker-compose
PS> docker-compose up --build
```

ブラウザで確認　→ :ok_hand: :ok:
- http://localhost/
- http://localhost:5000/

## リバースプロキシの設定(Nginx → Apache)

いまは別々に起動しているだけなので、特定のURLのみApacheにリダイレクトするようにNginxの設定を変更します。

```powershell:別ターミナルで実行
PS> docker container exec -it wbx bash
```

```bash:nginxの修正
root# vi /etc/nginx/conf.d/default.conf
bash: vi: command not found
```
Nginxのコンテナには何も入れていませんでした :sweat:。シェル上でパッケージをインストールして、設定ファイルの調整をしていきます。 

```bash:nginxの修正
root# apt update
root# apt install vim curl -y
root# vi /etc/nginx/conf.d/default.conf
```
ファイルの中身は :point_down:

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

    location /app/ {
        proxy_pass   http://wbb3/;
    }

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}
```

```bash:設定ファイルのテストと再読み込み
root# nginx -t
root# nginx -s reload
```
ブラウザで動作確認します。
- http://localhost/ ← nginx
- http://localhost/app/ ← apache
でそれぞれのDefault Pageが表示されるまで設定ファイルを調整します。

## Nginx の設定ファイルのコピーと Dockerfile の修正

```powershell:設定ファイルをホストPCにコピー
PS> mkdir nginx/etc/nginx
PS> docker container cp wbx:/etc/nginx/conf.d ./nginx/etc/nginx/
```

```dockerfile:nginx/Dockerfile
FROM nginx
COPY ./etc/nginx /etc/nginx
```
`Ctrl`+`C`で起動しているコンテナを停止し、Dockerfile を再ビルドします。

```powershell:nginx/Dockerfileの再ビルド
PS> docker-compose up --build
```
ブラウザで動作確認 → :ok_hand: :ok: 

# まとめ

最終的なフォルダ構成とファイル内容は以下になります。

```powershell:フォルダ構成
PS> tree /F
C:.
│  docker-compose.yml
│
├─flask
│      Dockerfile
│
└─nginx
    │  Dockerfile
    │
    └─etc
        └─nginx
            └─conf.d
                    default.conf
```

```dockerfile:docker-compose.yml
version: '3'

services:
  nginx:
    build:
      context: ./nginx
      dockerfile: Dockerfile
    container_name: wbx
    ports:
      - 80:80
  flask:
    build:
      context: ./flask
      dockerfile: Dockerfile
    container_name: wbb3
    ports:
      - 5000:80
```

```dockerfile:flask/Dockerfile
FROM ubuntu
# Avoid warnings by switching to noninteractive
ENV DEBIAN_FRONTEND=noninteractive
# Apache
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_PID_FILE /var/run/apache2.pid
ENV APACHE_RUN_DIR /var/run/apache2
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_LOCK_DIR /var/lock/apache2
RUN apt-get update \
    && apt-get install -y --no-install-recommends apt-utils dialog 2>&1 \
    && apt-get install -y git iproute2 procps lsb-release \
    && apt-get install -y apache2
# others tools
RUN apt-get install -y vim curl
# Apache
RUN  echo ServerName $HOSTNAME > /etc/apache2/conf-available/fqdn.conf \
    && a2enconf fqdn
# Clean up
RUN apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*
EXPOSE 80
CMD ["apachectl", "-D", "FOREGROUND"]
```

```dockerfile:nginx/Dockerfile
FROM nginx
COPY ./etc/nginx /etc/nginx
```

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

    location /app/ {
        proxy_pass   http://wbb3/;
    }

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}
```

[後半へ続く](https://qiita.com/glarpycat/items/3edccd75cb748ae5b896)