Dockerによる開発から本番デプロイまでの完全ガイド（EC2）

## Dockerとは

Dockerは軽量なコンテナ型仮想化技術で、アプリケーションとその実行環境をパッケージ化し、どこでも同じ環境で動作させることのできる開発・運用支援ツールである。
ホストOS上に仮想のコンテナ環境を構築する。

なぜ使われるのか
1. 環境構築が容易
2. 軽量で高速
3. ポータビリティ(一度構築したイメージはどこでも同様に動く)
4. CI/CDとの親和性

# Dockerの構成
1. イメージ
アプリ実行環境の設計図
2. コンテナ
イメージを実行したアプリそのもの
3. ボリューム
データの保存場所
4. ネットワーク
コンテナ同士をつなぐ通信経路

# イメージ
イメージとは、アプリケーションの実行に必要な環境一式をまとめたテンプレートで、アプリの設計図のようなもの。
開発の際にはDockerfileをもとに```docker build```でビルドする。
dockefileは以下のようなものである。(開発用)
```
FROM node:20
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm","run","dev"]
```
上からそれぞれ実行環境、コンテナ内の作業ディレクトリ、そこへコピーするディレクトリ、ビルド時に実行するコマンド、コンテナ起動時に実行するコマンドを表している。

:::note
イメージのビルドは何度も行うものではない。
変更を永続化し、配布・本番用にするためのものである
:::

# コンテナ
コンテナとは、dockerイメージをもとに作られた実行中のアプリケーション環境。つまりアプリケーション(実行中のプロセス)そのもの。

:::note
イメージとコンテナの分離
> イメージは再現性のある設計図、コンテナはその実行体。
開発中に汚れた環境も、コンテナを削除して再実行すれば一瞬で初期化できる。
この柔軟で軽量な仕組みが、Dockerを強力な開発ツールたらしめている。
:::

# ボリューム
ボリュームとは、dockerコンテナのデータを永続化(保存)するための仕組みである。
通常、コンテナ内で生成されたファイルやデータは、コンテナを削除するとともに消えてしまう。
しかしボリュームを使えば、コンテナの外にある領域にデータを保持できるため、コンテナを削除・再作成してもデータが残る。

ボリュームはコンテナの外(ホスト側)に存在し、開発や本番でデータを使う際にコンテナへマウントする。

# yamlファイル
YAMLは人間にとって読みやすく、機械にとって解析しやすいデータ記述フォーマットである。
dockerでは複数のコンテナをまとめて定義・管理する際などに```docker-compose.yml```を使用する。
これにより
- 複数コンテナの起動・停止のコマンド一つでの操作
- ネットワークやボリューム設定の一括管理
- 本番・開発環境の構成をコード化
することができる。

**(ex)webアプリ+ db構成のyamlファイル**
```
version: "3.9"

services:
    web:
        build: .
        ports:
            - "3000:3000"
        volumes: .:/app
        depends_on:
            - db

    db:
        image: mysql:8
        ports:
            - "3000:3000"
        environment:
            MYSQL_ROOT_PASSWORD: password
        volumes:
            - db-data:/var/lib/mysql
volumes:
    db-data:
```
各要素の意味は
- ```services```: 起動するコンテナ群
-  ```volumes```: ボリューム定義(データ永続化)
-  ```depends_on```: 起動順序の指定
-  ```build```: イメージのビルド
-  ```environment```: 環境変数の設定

となる。

:::note
contextはビルド時に渡す作業ディレクトリのことで、contexのディレクトリ以下のファイルやフォルダがDockerfileからアクセスできる。
```
services:
  web:
    build:
      context: ./frontend
      dockerfile: Dockerfile.dev
```
この場合、frontend/ディレクトリをビルド対象(COPY . .などはこの中のファイル)とし、dockerfileで指定されたdockerfileを用いる。
以下のような時に記述する。
- 複数サービスがある
- Dockerfileがルート以外にある
- ビルド用途本番ようでファイルを分けたい
- セキュリティ上アクセス範囲を制限したい
:::
コマンドは以下のようである。
- ```docker compose up```: コンテナを起動(ビルドも行う)
- ```docker compose up -d```: バックグラウンドで起動(detached)
- ```docker compose down```: 起動中の全コンテナ・ネッタワークなどを停止、削除
- ```docker compose build```: Dockerfileに基づいてイメージをビルド
- ```docker compose restart```: コンテナ再起動
- ```docker compose logs```: ログ出力を確認

# マウント
マウントとはコンテナとホストの間でファイルやディレクトリを共有する仕組みである。
特によく使うのがbindマウントとボリュームマウントである。

**Bindマウント**
Bindマウントとはホストマシン上の任意のディレクトリやファイルを、コンテナ内の特定のパスに直接マウントする方法で、主に開発時にコードや設定ファイルをリアルタイムで反映させるために使用される。
開発向きのマウントである。
```docker run -v $(pwd):/app -w /app myapp```でbindマウントをすることができ、このコマンドは、myappというイメージから作られたコンテナを走らせ、そこへ現在のディレクトリをマウントするという意味である。
yamlファイルのvolumesで指定することもできる。

**Volumeマウント**
Dockerが自動的に管理する専用のストレージ領域をコンテナにマウントする方法で、データベースなどの永続データの保存や本番環境での運用に適している。
本番・永続化向きのマウントであるが、ボリュームはDockerが自動管理するため、他のチームメンバーとの共有やバックアップにも向いている。
yamlファイルのvolumesで指定することができる。

# 開発時と本番時のDBの扱いの違い
開発環境では、アプリケーションと同時にローカルでmysqlコンテナを立ち上げ、アプリ側からは```DATABASE_URL```環境変数を通じてそのmysqlに接続する。Docker compose上ではサービス目(ここでは```db```)が内部DNSとして解決されるため、```.env.development```に以下のように記述するだけで接続が成立する:
```
DATABASE_URL=mysql://root:password:33006/{{データベース名}}
```
この構成により、開発者はDocker composeを起動するだけでアプリとDBの環境を簡単に再現することができる。
一方、本番間k表ではdocker compose上でmysqlコンテナを立てるのではなく、クラウド上に用意されたDBサービスを使用するアプリケーションは、```.env.production```に記載された接続文字列を参照して、外部のDBに接続する:
```
DATABASE_URL=mysql://admin:securepw@prod-db.xxxxxx.rds.amazonaws.com:3306/prod_db
```
このように開発ではローカルのDBコンテナに接続し、本番ではクラウドDBに接続することで、環境に応じた安全かつ柔軟な構成を実現できる。
Docker Composeファイルも開発用と本番ようで分離して管理するのがベストプラクティス。

# 開発の流れ
1. DockerfileとComposeファイルを用意
2. git clone
3. docker composeup
4. 開発開始

# 本番環境へのデプロイ(ex: EC2)

CI/CDの話までしてしまうと長くなってしまうので今回は簡潔に。
AWS EC2を用いた本番デプロイの流れをもとに、実践的な手順をまとめる。

**ローカルでの手順**
開発環境ではDockerfileや```docker-compose.yml```を使ってイメージをビルドする。本番用```dicker-compose.yml```に```build:```と```image:```の両方を記述しておくことで、開発時はローカルで```build```、本番では```pull```による再利用が可能である。
```
# ローカルでイメージをビルド
docker compose build

# Docker Hubにpush
docker push yourname/yourapp:latest

```

**EC2での手順**
EC2上ではソースコードをcloneする必要はない。すでにアプリケーションの中身はイメージに含まれているからである。
1. EC2にSSH接続
2. Docker Hubからイメージをpull(イメージがdockerエンジンにダウンロードされる)
3. docker-compose.ymlを設置して起動（または直接runでも可）
```
# 1.
ssh ec2-user@your-ec2-ip

# 2.
docker pull yourname/yourapp:latest

# 3.
docker compose up -d

```
これにより、ローカルで動いていたアプリケーションと全く同じものをEC2上に再現できる。
ソースコードやnpm/pip環境をEC2に持ち込むことなく完成品をデプロイすることができること、これがDockerの強みである。

:::note
```docker compose build``` vs ```docker build```
```docker compose build``` は ```docker build``` をラップしたものに過ぎず、複数サービスの定義や環境変数、ボリューム設定などもまとめて管理できるのが特徴。
そのため、本番用構成でも ```docker-compose.yml``` を利用するのが一般的。
:::