Dockerを触ってみようと思う
自分はほとんど触ったことない。友人が使ってるのを隣で指くわえてみてました。
しかしある程度どんなものかは知っているので早速触ってみる
Dockerfileってなんだ
DockerfileはImageの設計図だ。
Imageはコンテナの設計図だ
つまり
Dockerfile
↓
Image
↓
コンテナ
って感じで作られるぽい。
じゃあDockerfileから直接コンテナつくれよって思った。
Dockerfile
↓
コンテナ
Imageがなぜ生成されるのかというと、
Imageを作成する際に前回のImageを作った際の結果をキャッシュして保存しておいて、
二回目以降のImage作成を高速にしてくれる。
あとはDocker hubにあげて共有できる
React + Laravel + Nginx
この構成でDockerの環境を構築する
コンテナのどう分けるのか、ホスト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は開発環境用と本番環境用を作りました。
これが一般的なんでしょうか???わかりません
追記 2025/04/16
開発環境と本番環境でDockerfileを変えることは無いそうです。
Dockerfile
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;"]
この書き方はマルチステージビルドを用いています。
FROM node:20-alpine AS build
...
RUN yarn build
ここで1つのコンテナでビルドとインストールを行います。
FROM nginx:alpine
...
COPY --from=build /app/build /usr/share/nginx/html
ココから別のコンテナで前のコンテナはすでに止まっています
前のコンテナからビルドしたフォルダだけをコピーしているので効率的で、軽い
CMD ["nginx","-g", "daemon off;"]
これはNginxをフォアグラウンドで起動するためのコマンドです。
バックグランドで起動するプロセスだとコンテナは止まってしまいます。
CMD ["nginx"]
これだとバックグランドで起動するのでダメです。
CMD
とRUN
の違い
Imageからコンテナが作られたときに動くのRUN
コンテナが起動して最初に動くのがCMD
CMD
はコンテナの核となるプロセスを動かすために使うような感じかなと思います。
Dockerfile内ではCMD
は一回しか使えません
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ファイルは複数のコンテナを管理するためのファイル
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
はコンテナの名前でありコンテナ間でアクセスするためのホスト名になる
volumes:
- ./backend/laravel-introduce:/laravel
....
volumes:
- ./web/react-introduce/:/app
開発用ではそれぞれのフレームワークのプロジェクトファイルを丸ごとマウントしています。
マウントするとホストOSのファイルを変更した場合、コンテナ内のファイルもリアルタイムで変わります。
嵌ったところ
ボリュームマウントでdockerfile
でCOPY
した内容が消えてる問題
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/
で持ってきてる。
volumes:
- ./web/react-introduce/:/app
それでボリュームをマウントしている。
何がしたかったか
ホストOSでは依存関係をインストールしていません。つまりnode_modules
などの
yarn install
して作成されるディレクトリはホストOSにはありません
すべてコンテナ内で完結したかったのでホストOSにあるソースコードをマウントして
ホストOSにあるソースコード + コンテナでインストールした依存関係
こんな感じしたかったのです。
ですがマウントする際に、既にあるファイルは隠されるそうです。
volumes:
- ./web/react-introduce/:/app
これはホストosのreact-introduceディレクトリ
をappディレクトリ
にマウントします。
そしてマウントされる順序はdockerfileのCOPY
よりも後です。
ホストOSの依存関係がない状態のディレクトリで上書きしているので、
ホストOSにあるソースコード + コンテナでインストールした依存関係
ホストOSにあるソースコードでコンテナでインストールした依存関係を上書きしていたんですね
これに気づかずにかなり時間を使ってしまいました。
ホストOSにあるソースコード + コンテナでインストールした依存関係
これの実現方法はわかりません。有識者の方教えてください。
疑問
CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"]
本番用のDockerfileでもartisan serve
しています。ここに違和感を感じます。
artisan serve
というのは本番で使うものでないと思っています。
COPY --from=build /app/build /usr/share/nginx/html
ですがnginxのドキュメントルートにはReactのビルドしたフォルダを置いています。
ドキュメントルートにLaravel
のpublicディレクトリ
を置いてもいいものか
ドキュメントルートを共有していいのかわからずにしぶしぶartisan serve
するという選択をしました。
後から聞いたのですがDockerfileは本番も開発も一緒なのが一般的だそうです。