DockerでDjango+React+PostgreSQL環境を作ってみたので備忘録

# 前書き
- Windows11+WSL2環境での記事となります。

今回の構築理由は下記になります。
- 自分でDockerfileやdocker-compose.ymlを作ったことがなかったのでDockerへの理解を深めるため
- Django+Reactのモダンな環境を触ってみたかった

※筆者のDockerに対する理解度は下記の記事を一通りを読んだ程度なので改善点などがあるかもしれません。

https://zenn.dev/suzuki_hoge/books/2022-03-docker-practice-8ae36c33424b59

# ディレクトリ構造
これから作成するファイルのディレクトリです。
次のように配置してください。
```
root/
├── backend/
│   └── dockerfile(django用)
└── frontend/
│   └── dockerfile(react用)
└── docker-compose.yml
```

# Dockerfileの作成(Django)

DockerfileはDockerHubに上がっているimageを元に自分の好きな環境を作るための設定ファイルです。

今回はDockerHubにあるPythonのImageを元にしてDjangoとpsycopg2-binary(PythonからPostgreSQLに接続するためのライブラリ)をインストールしています。

:::note warn
DjangoのImageもありますが、非推奨となり2016年12月31日以降更新されていないで利用しません。
:::

``` Dockerfile:Dockerfile
FROM python:3.8
RUN pip install --upgrade pip && pip install Django==4.2.2 psycopg2-binary

WORKDIR /usr/backend

CMD ["bash"]
```

各コマンドの説明は下記の通りです。

|コマンド|意味|
|:--:|:--:|
|FROM python:3.8|元となるイメージをPython 3.8と設定|
|RUN pip install --upgrade pip|pip(pythonのパッケージ管理システム)をアップデート|
|~ pip install Django==4.2.2|Djangoをインストール|
|~ psycopg2-binary|psycopg2をインストール|
|WORKDIR /usr/backend|ワークディレクトリを設定|
|CMD ["bash"]|コンテナ実行時のデフォルト命令をbashにする|


# Dockerfileの作成(React)

``` Dockerfile:Dockerfile
FROM node:16.20.0-alpine3.18
WORKDIR /usr/frontend
RUN npm install -g create-react-app@5.0.1
CMD ["sh"]
```
|コマンド|意味|
|:--:|:--:|
|FROM node:16.20.0-alpine3.18|元となるイメージを設定(node)|
|WORKDIR /usr/frontend|ワークディレクトリを設定|
|RUN npm install -g create-react-app@5.0.1|reactのアプリを作成するパッケージをインストール|
|CMD ["sh"]|コンテナ実行時のデフォルト命令をshにする|


# docker-compose.ymlの作成

``` docker-compose.yml:docker-compose.yml
version: '3.8'

services:
  backend:
    build: ./backend
    command: python /usr/backend/manage.py runserver 0.0.0.0:8000
    volumes:
      - ./backend:/usr/backend
    ports:
      - 8000:8000
    depends_on:
      - db
  db:
    image: postgres
    ports:
      - 5432:5432
    environment:
      - POSTGRES_DB=dbname
      - POSTGRES_USER=root
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data/
  frontend:
    build: ./frontend
    working_dir: /usr/frontend/appname
    tty: true
    environment:
      - NODE_ENV=development
    volumes:
      - ./frontend:/usr/frontend
    ports:
      - "3000:3000"

volumes:
  postgres_data:
```
docker-compose.ymlではコンテナを立ち上げる際の設定(コマンドで実行する場合でいうところのオプション)を行います。
長いので各項目の内容は割愛しますが、アプリケーションのポートやホストPC側とファイルを同期するディレクトリなどを指定しています。

下記のappname部分は、このあとで実行するReactのプロジェクト作成時に指定したプロジェクト名に合わせてください。
```
working_dir: /usr/frontend/appname
```
# Docker Imageの準備

docker-composeではサーバー立ち上げのコマンドを設定していますが、
まだプロジェクトを作成していないので各イメージでコンテナを一つずつ立ち上げてreactやdjangoのプロジェクトを先に作ります。

docker-compose.ymlがあるディレクトリで次のコマンドを叩きまます。
```
docker-compose build
```
処理が完了したら
```
docker image ls
```
を実行してImageが作成できている事を確認します。
![3455b5a9f57fda090730d37ac639fb41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264526/d50e635c-3f36-a876-aa80-9802698e5f90.png)

## djangoのプロジェクト作成

バックエンドのImageを立ち上げます。
```
docker container run -it --rm [backendのIMAGE ID] bash
```
[/usr/backend]ディレクトリにいる事を確認し、次のコマンドを入力(appnameの部分は好きに設定してください。)
```
django-admin startproject appname .
```
lsコマンドでプロジェクトが作成されている事を確認してください。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264526/2a369237-f587-8450-d2a0-646a7a52d445.png)
作成されている事を確認できたら、コンソールをもう一つ開いて次のコマンドを実行します。
```
docker ps
```

次のように起動しているコンテナの一覧が表示されるのでContainer IDをコピーします。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264526/6bb1ee39-b55f-fd81-c765-3491effe7ba6.png)
その後、作成したプロジェクトのファイルをホストPC側にコピーするため、次のコマンドを実行します。
※rootはプロジェクトのルートディレクトリ(docker-compose.ymlがあるディレクトリ)を指定してください。
```
docker cp [コンテナID]:usr/backend [root]
```
コピーが完了したらdocker container runを行ったコンソールは閉じてください。

:::note warn
下記のようなエラーが出る場合は、コンソールを管理者権限で実行してcpコマンドを実行してください。
symlink ..\ディレクトリ\ quired privilege is not held by the client.
:::

## Reactのプロジェクト作成
backend側と同様に単体でコンテナを立ち上げます。
コマンドの末尾がbashではなくshになっていることに注意してください。
```
docker run -it --rm [frontendのimage id] sh
```
usr/frontendディレクトリにいるこを確認して下記を実行
```
npx create-react-app appname --template typescript
```

実行が完了したら、また新しいコンソールを開いて下記のコマンドを実行し、ファイルをホストPCにコピーしてください。
※コンテナIDは今実行しているfrontendのコンテナID、rootはプロジェクトのルートディレクトリ(docker-compose.ymlがあるディレクトリ)を指定してください。
```
docker cp [コンテナID]:usr/frontend [root]
```
コピーが完了したらコンソールを閉じるか、[exit]コマンドでコンテナから抜けてください。(--rmオプションを設定しているのでコンテナが終了します。)


# docker-compose upを実行
プロジェクトのルートディレクトリ(docker-compose.ymlが存在するディレクトリ)上で次のコマンドを実行します。
```
docker-compose up -d
```

DockerDesktopのContainerタブでコンテナが緑色になっていれば完了です！
お疲れ様でした。
![9759b7cd56ea2c5aa8ae47ea5add9ec3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264526/90b4b236-da46-e1b5-62a9-62057545352d.png)

# あとがき
Laravel-sailや人からもらったdockerfileなどからしかdocker環境を利用をしたことがなかったのでいい勉強になりました。
本当はDockerfile上でプロジェクト生成も行ってしまいたかったのですが、docker-compose up時にホストPC側のディレクトリ構造が優先して同期されるからなのかうまく行かなかった事が心残りです。
ReactとDjangoの連携方法などについても気が向いたら書こうかと思います。