Vite+FastAPI+NGINX+Dockerの環境構築

## 概要
- GitHubにテンプレートとして公開しています。
    - https://github.com/gaitou2048/fastAPI-veutify-template


## 前提
- Docker, Docker Composeインストール済み
実行環境はMac + Docker for Desktop(Mac) ですが、Windows(WSL)やLinuxでもできるはず。

## FastAPIコンテナ(バックエンド)


以下のDockerfileを使います。
pythonのパッケージ管理はpoetryを使っています。
以下のパッケージをpyproject.tomlでインストールしています。
- fastapi
- uvicorn

(pyproject.toml) 
https://github.com/gaitou2048/fastAPI-veutify-template/blob/main/back/app/pyproject.toml


```dockerfile

FROM python:3.11-slim-bullseye

ENV TZ=Asia/Tokyo

# Debian日本語化
RUN apt-get update && apt-get install -y locales \
    && apt-get clean -y && rm -rf /var/lib/apt/lists/* \
    && sed -i -e 's/# \(ja_JP.UTF-8\)/\1/' /etc/locale.gen \
    && locale-gen \
    && update-locale LANG=ja_JP.UTF-8

# 必要なパッケージのインストール
RUN apt-get update && apt-get install -y \
    curl \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Poetryインストール
ENV POETRY_HOME=/opt/poetry
RUN curl -sSL https://install.python-poetry.org | python3 - \
    && cd /usr/local/bin \
    && ln -s /opt/poetry/bin/poetry \
    && poetry config virtualenvs.create false

# Pythonパッケージインストール
WORKDIR /app
COPY ./app/pyproject.toml* ./app/poetry.lock* ./
RUN poetry install
```

## Vueコンテナ(フロント)

Dockerファイルは以下になります。
あらかじめ[package.json](https://github.com/gaitou2048/fastAPI-veutify-template/blob/main/front/app/package.json)を導入済みのため、
COPY でpackage.json等をコピーし、`yarn install`しています。

以下のモジュールを入れています。
- vuetify: UIとかの機能や見た目がたくさんある便利なフレームワーク
- axios: APIを叩くときに使う


※ 完全に空の状態から作る場合は、COPYと`yarn install`をせずにコンテナを作成します。
その後、コンテナ内で[viteのプロジェクト作成](https://ja.vitejs.dev/guide/)を実行していきます。
するとpackage.json等ができるので、もう一度コンテナをビルドする流れですね。


```Dockerfile
FROM node:19.6-slim

WORKDIR /app
ENV LANG=C.UTF-8 \
    TZ=Asia/Tokyo
COPY app/package.json app/yarn.lock ./
RUN yarn install

```

## NGIXNコンテナ(サーバ)
フロントエンドコンテナで、`yarn build`をすると、htmlとスクリプトがビルドされます。
(多分Webpackでソースコード等をバンドルしているだけ)


そのビルドされたhtmlを表示させるためのサーバとなるコンテナです。
NGINXを使うと負荷が少なかったり、高速だったりするらしい。
使った方が良さそうです。


Dockerfileは以下ですね。
NGINXイメージ使っているだけ。

```Dockerfile
FROM nginx:latest
ENV TZ=Asia/Tokyo
```

設定ファイルが重要です。
`http://localhost:8000/` でフロントエンドコンテナでビルドされたものを表示してます。
/api でバックエンド側に繋がるようにしてます。


```nginx.conf
worker_processes auto;

events {
    worker_connections 512;
    multi_accept on;
}

http {
    include /etc/nginx/conf.d/server.conf;
    server_tokens off;
    send_timeout 60; # タイムアウト60秒

    # mimetype
    include         /etc/nginx/mime.types;
    default_type    application/octet-stream;
}
```

```confd/server.conf
server{
    listen 8000;

    location / {    
        root /www/dist;
        index index.html;
    }

    location /api {
        proxy_pass http://back:5001/;
    }
}
```

## docker-compose.yml

3つのコンテナを取り仕切るdocker-compose.ymlがこちら。
フロントエンドコンテナでビルドすると、`/front/app/dist`に出力されるので、
それをサーバコンテナでマウントしています。

```docker-compose.yml
version: '3'
services:
  # バックエンド(docker FastAPI)
  back:
    build:
      context: ./back
      dockerfile: Dockerfile
    volumes:
      - ./back/app:/app/
    tty: true
    restart: on-failure
    working_dir: /app
    command: uvicorn src.main:app --reload --host 0.0.0.0 --port 5001

  front:
    build:
      context: ./front
      dockerfile: Dockerfile
    ports:
      - 3000:3000 # 本番で動かす際はコメントアウト
    volumes:
      - ./front/app:/app
      - front_node_modules:/app/node_modules
    tty: true
    restart: on-failure
    working_dir: /app

  server:
    build:
      context: ./server
      dockerfile: Dockerfile
    ports:
      - 8000:8000
    volumes:
      - ./server/nginx.conf:/etc/nginx/nginx.conf
      - ./server/conf.d:/etc/nginx/conf.d
      - ./server/log:/var/log/nginx
      - ./front/app/dist:/www/dist
    tty: true
    restart: on-failure

  
volumes:
  front_node_modules:
```

## 開発時
開発時は`docker compose exec front yarn dev`を実行して、
開発サーバを起動します。
`http://localhost:3000`で繋がります。

開発サーバを使うと、フロントの修正がすぐに反映されるのでお手軽です。楽々です。
使わないとこんな感じの流れに。ビルドして、コンテナを再起動するはめになります。
1. `docker compose exec front yarn build` でビルド
1. `docker compose restart server`

あと、VSCodeなどで開く場合は、補完が効きません。
何故ならパッケージやモジュールはコンテナ内にあるから。
RemoteContainerを使うと幸せになりそうです。
バックエンドとフロントエンドで、ウィンドウが二つ必要ですが。

ローカル(ホスト)環境が汚れても良い場合は、
nodeやらpythonのパッケージをインストールしてしまってもよいかも。
コンテナに追加した場合は、ローカルにも入れないといけないですけどね。

## テンプレートの見た目
せっかく(?)なので、vuetifyのワイヤーフレームにあるページを表示しています。
https://vuetifyjs.com/ja/examples/wireframes/system-bar/

Hello Worldとなっている部分はAPIで取得しています。
Axiosとバックエンドと、サーバ(NGINX)が作動しているかのテストですね。
上手くいかないと「文字列」と表示されます。
デフォルトが `const message: Ref<string> = ref("文字列");` なのです。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3113048/8f151cfc-7223-743b-ae4c-b2e7e1cbb6a0.png)