Dockerを触ってみる

## Dockerを触ってみようと思う
自分はほとんど触ったことない。友人が使ってるのを隣で指くわえてみてました。

しかしある程度どんなものかは知っているので早速触ってみる

## Dockerfileってなんだ
DockerfileはImageの設計図だ。
Imageはコンテナの設計図だ
つまり

Dockerfile
↓
Image
↓
コンテナ

って感じで作られるぽい。

じゃあDockerfileから直接コンテナつくれよって思った。
Dockerfile
↓
コンテナ

Imageがなぜ生成されるのかというと、
Imageを作成する際に前回のImageを作った際の結果をキャッシュして保存しておいて、
二回目以降のImage作成を高速にしてくれる。

あとはDocker hubにあげて共有できる

## React + Laravel + Nginx
この構成でDockerの環境を構築する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3765500/bf7f2668-80e3-4a2b-bd60-e9bcf3eb11fd.png)

コンテナのどう分けるのか、ホストOSにフレームワークは入れて開発するのかとか、
わからないことだらけでしたが、とりあえずこの画像みたいに組んでいきたいです。

LaravelはAPIを返すだけに注力してもらいます。

フォルダ構成
|---backend
|　　|---laravel
|　　|---Dockerfile-dev
|　　|---Dockerfile-prod
|
|---web
|　　|---react
|　　|---Dockerfile-dev
|　　|---Dockerfile-prod
|　　|---nginx.conf
|
|---docker-compose-dev.yml
|---docker-compose-prod.yml

ymlファイルとDockerfileは開発環境用と本番環境用を作りました。
これが一般的なんでしょうか？？？わかりません


:::note alert
**追記** 2025/04/16
開発環境と本番環境でDockerfileを変えることは無いそうです。
:::



## Dockerfile
```dockerfile:web
FROM node:20-alpine AS build
COPY ./react-introduce/package.json ./react-introduce/yarn.lock /app/
WORKDIR /app
RUN yarn install
COPY ./react-introduce/ /app/
RUN yarn build

FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY --from=build /app/build /usr/share/nginx/html
COPY ./react-introduce/.env /app/.env
COPY ./nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx","-g", "daemon off;"]
```

この書き方はマルチステージビルドを用いています。
```dockerfile:web
FROM node:20-alpine AS build
...
RUN yarn build
```
ここで1つのコンテナでビルドとインストールを行います。
```dockerfile:web
FROM nginx:alpine
...
COPY --from=build /app/build /usr/share/nginx/html
```
ココから別のコンテナで前のコンテナはすでに止まっています
前のコンテナからビルドしたフォルダだけをコピーしているので効率的で、軽い

```dockerfile:web
CMD ["nginx","-g", "daemon off;"]
```
これはNginxをフォアグラウンドで起動するためのコマンドです。
バックグランドで起動するプロセスだとコンテナは止まってしまいます。
```dockerfile:web
CMD ["nginx"]
```
これだとバックグランドで起動するのでダメです。

```CMD```と```RUN```の違い
Imageからコンテナが作られたときに動くの```RUN```
コンテナが起動して最初に動くのが```CMD```

```CMD```はコンテナの核となるプロセスを動かすために使うような感じかなと思います。

Dockerfile内では```CMD```は一回しか使えません


```dockerfile:backend
FROM node:20-alpine AS install
WORKDIR /laravel
COPY ./laravel-introduce/package.json /laravel/
RUN npm install

FROM php:8.2-fpm

RUN apt-get update \
    && apt-get install -y zlib1g-dev mariadb-client vim libzip-dev \
    && docker-php-ext-install zip pdo_mysql

WORKDIR /laravel
COPY ./laravel-introduce/ /laravel
COPY ./laravel-introduce/.env /laravel/.env
COPY --from=install /laravel /laravel
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
RUN composer install
CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"]
```

## ymlファイル

ymlファイルは複数のコンテナを管理するためのファイル
```yml:docker-compose-prod
services:
  backend:
    build:
      context: ./backend
      dockerfile: ./Dockerfile-prod
    ports:
      - "8000:8000"
    networks:
      - app-network

  web:
    build:
      context: ./web
      dockerfile: Dockerfile-prod
    ports:
      - "80:80"
      - "5173:5173"
    environment:
      - HOST=0.0.0.0
    networks:
      - app-network

networks:
  app-network:
    driver: bridge
```

```backend```や```web```はコンテナの名前でありコンテナ間でアクセスするためのホスト名になる


```yml:docker-compose-dev
volumes:
      - ./backend/laravel-introduce:/laravel
....

volumes:
      - ./web/react-introduce/:/app
```
開発用ではそれぞれのフレームワークのプロジェクトファイルを丸ごとマウントしています。

マウントするとホストOSのファイルを変更した場合、コンテナ内のファイルもリアルタイムで変わります。


## 嵌ったところ
ボリュームマウントで```dockerfile```で```COPY```した内容が消えてる問題
```dockerfile:Dockerfile-dev
FROM node:20-alpine AS build
COPY ./react-introduce/package.json ./react-introduce/yarn.lock /app/
WORKDIR /app
RUN yarn install
COPY ./react-introduce/ /app/
RUN yarn build

FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY --from=build /app/ /app/
COPY ./react-introduce/.env /app/.env
COPY ./nginx.conf /etc/nginx/nginx.conf
EXPOSE 5173
CMD ["yarn", "dev"]
```
こんな感じで開発用のDockerfileでも```yarn install```してそのディレクトリを
```COPY --from=build /app/ /app/```で持ってきてる。
```yml
volumes:
      - ./web/react-introduce/:/app
```
それでボリュームをマウントしている。

**何がしたかったか**

ホストOSでは依存関係をインストールしていません。つまり```node_modules```などの
```yarn install```して作成されるディレクトリはホストOSにはありません

すべてコンテナ内で完結したかったのでホストOSにあるソースコードをマウントして
**ホストOSにあるソースコード**　＋　**コンテナでインストールした依存関係**
こんな感じしたかったのです。
ですがマウントする際に、既にあるファイルは隠されるそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3765500/ae0330ae-527b-4908-8a99-ffd27cd5907a.png)

```yml
volumes:
      - ./web/react-introduce/:/app
```
これはホストosの```react-introduceディレクトリ```を```appディレクトリ```にマウントします。
そしてマウントされる順序はdockerfileの```COPY```よりも後です。

ホストOSの依存関係がない状態のディレクトリで上書きしているので、
**ホストOSにあるソースコード**　＋　**コンテナでインストールした依存関係**


**ホストOSにあるソースコード**で**コンテナでインストールした依存関係**を上書きしていたんですね

これに気づかずにかなり時間を使ってしまいました。
**ホストOSにあるソースコード**　＋　**コンテナでインストールした依存関係**
これの実現方法はわかりません。有識者の方教えてください。

## 疑問
```Dockerfile
CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"]
```
本番用のDockerfileでも```artisan serve```しています。ここに違和感を感じます。
```artisan serve```というのは本番で使うものでないと思っています。


```Dockerfile
COPY --from=build /app/build /usr/share/nginx/html
```
ですがnginxのドキュメントルートにはReactのビルドしたフォルダを置いています。
ドキュメントルートに```Laravel```の```publicディレクトリ```を置いてもいいものか
ドキュメントルートを共有していいのかわからずにしぶしぶ```artisan serve```するという選択をしました。

後から聞いたのですがDockerfileは本番も開発も一緒なのが一般的だそうです。