はじめに
私は IT未経験の新卒として入社し、アプリケーションをコンテナでデプロイする機会がありました。先輩方が用意した手順書を参考にしながら、Dockerの設定などを進めることが多いのですが、その際に「なぜこの手順が必要なのか」「各設定項目が何を意味するのか」といった疑問を感じることがよくあります。
そこで今回は、Reactアプリケーションの作成から、ローカルでコンテナを立ち上げてデプロイするまでの手順について、まとめてみました。
初学者の皆さんにとって、この内容がお役に立てば幸いです。
環境について
- React: 18.3.1
- Node.js: v20.16.0
- Podman: 3.4.4
- Ubuntu 22.04.4 LTS (WindowsのWSL2)
- WindowsOS:11
目次
手順
1.Reactプロジェクトの作成について
1.1. Reactプロジェクトの作成
- 作業用のディレクトリを作成します。
$mkdir mySPAapp
- Reactプロジェクトを作成します。
$npx create-react-app my-spa-react-app
#Version指定することもできます
$npx create-react-app@(version number) (project name)
npx
は npm
の一部として Node.js をインストールする際に自動的にインストールされます。したがって、Node.js をインストールしていない環境では、まず Node.js をインストールする必要があります。
1.2. Reactプロジェクトの確認
my-spa-react-appというプロジェクト名のディレクトリも作成されます。
現在、フォルダの構成は以下のようになります:
.
└── mySPAapp
└── my-spa-react-app
- カレントディレクトリを
my-spa-react-app
に変更します。 -
npm start
で「http://localhost:3000/」 を起動してアプリケーションが動作できるかどうかをチェックします。
2. 作成したReactプロジェクトのコンテナ化
2.1. Dockerfile と docker-compose.ymlの作成
2.1.1. Dockerfileの作成
Dockerfileとは、新規Dockerイメージを作成するための一連の命令を記述したテキストファイルです。システムは、これらの命令とプロセスに従ってイメージをビルドします。
プロジェクトフォルダ(my-spa-react-app)内で、ReactアプリケーションのDockerfileを作成します。
$touch Dockerfile
現在、フォルダの構成は以下のようになります:
.
├── Dockerfile
├── README.md
├── package-lock.json
├── package.json
├── public
└── src
Dockerfileに以下の内容を記述します。
FROM node:20
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm","start"]
説明:
-
FROM node:20
今回はNode.js v20でプロジェクトを作成しているため、node:20を指定します。
これにより、コンテナ内でNode.js v20の環境が準備されます。 -
WORKDIR /app
システムはコンテナ中で/appというディレクトリを作ってくれます。
また、このディレクトリがコンテナ内での作業領域となります。 -
COPY package.json package-lock.json ./
ローカルのNode.jsのコンフィグファイル(package.json package-lock.json)をWORKDIR(/app)中にコピーします。 -
RUN npm install
package.json package-lock.json二つのコンフィグファイルに従ってパッケージのインストール
順番は重要です これをCOPY package.json package-lock.json ./
の前に移すとエラーになる。 -
COPY . .
残りのファイルをコンテナ内の現在の作業ディレクトリ(/app)にコピーします。 -
EXPOSE 3000
EXPOSE
は開発者や他の人に「コンテナ内でこのポートが使われている」ことを示すだけで、ホストとのポートマッピングは行いません。
ポートマッピングを行うには、docker-compose.yml で設定するか、コンテナ起動時に -p オプションを使用します。 -
CMD ["npm", "start"]
コンテナを起動する際のデフォルトコマンドを指定します。
ここでは、npm startを実行するように設定しています。これがないとコンテナを起動しても何も実行されず、すぐに終了してしまいます。
Docker はレイヤーベースのアーキテクチャを使用しており、各コマンドがレイヤーとしてキャッシュされます。
COPY を2回に分ける理由は、package.json のような依存関係のファイルが変わらない限り、キャッシュを再利用できるためです。これにより、再ビルド時に全体を再実行する必要がなくなり、効率が向上します。全てのファイルを一度にコピーすると、わずかな変更でもすべてのステップが再実行されてしまいます。
.dockerignore ファイルの作成について
※本手順は必須でない為、スキップしても問題無い
.dockerignore
ファイルは、Docker イメージをビルドする際に特定のファイルやディレクトリを除外するためにつくられるファイルです。
一般的に、次のような内容を記述します。
node_modules #インストールされた依存パッケージのフォルダ
.git #Gitのバージョン管理情報を含むフォルダ
Dockerfile #Dockerイメージ作成の指示を記載したファイル
.dockerignore #本ファイル
.env #環境変数を定義するためのファイル
コンテナを起動する際に、Dockerfile中の命令 RUN npm install
を通して自動的に必要なパッケージをコンテナ中でインストールされます。
Reactアプリケーションのnode_modulesフォルダのサイズは大きいため、node_modulesを除外することでビルド時間を高速、イメージサイズの縮小を行います。
2.1.2. docker-compose.ymlの作成
docker-compose.ymlは、複数のコンテナを管理するための設定ファイルです。Docker Composeを使うことで、複数コンテナのビルドや起動・停止が簡単に行えます。
Docker Compose は通常、複数のコンテナを管理するために使用されますが、1つのコンテナの場合でも、環境設定を簡単にしたり、設定ファイルをバージョン管理できるメリットがあります。さらに、将来的にコンテナが増える可能性がある場合にも、柔軟に対応できます。
現在、Reactアプリケーションのフロントエンド部分のみを作成していますが、今後データベースサービスを追加するため、Docker Composeを使用させていただきます。
プロジェクトフォルダ(my-spa-react-app)内で、docker-compose.ymlを作成します。
$touch docker-compose.yml
現在、フォルダの構成は以下のようになります:
.
├── Dockerfile
├── README.md
├── docker-compose.yml
├── package-lock.json
├── package.json
├── public
└── src
docker-compose.yml に以下記の内容を記述します。
version: '3'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- ./:/app
説明:
-
version
: '3': Docker Composeのバージョン指定。通常、最新バージョンを使用します。 -
services
: サービス名は自由に設定できます。今回はappというサービスを定義します。
Webアプリケーションのサービスをappとして定義します。
app:
build: .
ports:
- "3000:3000"
volumes:
- ./:/app
-
build: .
現在のディレクトリ(Dockerfileが存在するディレクトリ)からイメージをビルドします。 -
ports: "3000:3000"
Dockerfile中のEXPOSEで指定したポートはコンテナ内のポートです。
ホストとのポートマッピングを行うには Docker Compose の設定か、docker run の -p オプションで指定する必要があります。 -
volumes: ./:/app
ホストの現在のディレクトリ(./)をコンテナ内の/appディレクトリにマウントします。これにより、ホスト側のファイルを変更するとコンテナ内のファイルもリアルタイムで反映されます。
補足:
データベースサービスの追加の場合は、次のようになります
version: '3'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- ./:/app
db:
image: mongodb
environment:
MONGO_INITDB_ROOT_USERNAME: example
MONGO_INITDB_ROOT_PASSWORD: example
ports:
- "27017:27017"
2.2. イメージのビルド
以上のことを完了した上で、 イメージをビルドします。
$podman build -t my-spa-react-app:01 .
- -t : タグ
- . : 現在のディレクトリ
完了後、以下のコマンドで、自分が作成したイメージがリストに表示されていることが確認します。
$podman images
localhost/my-spa-react-app 01 5c0c7096dbb1 6 seconds ago 1.47 GB
2.3. コンテナ化 および起動
ビルドしたイメージでコンテナを作成して、起動します。
$podman run -d -p 3000:3000 --name my-spa-app-container my-spa-react-app:01
-
-d
: コンテナをバックグラウンド(デタッチドモード)で実行します。 -
--name my-spa-app-container
: コンテナの名前を指定します。 -
my-spa-react-app:01
: 先ほど作ったイメージ名とタグ。
※ Docker Composeでホストポート番号とコンテナポート番号のマッピングを設定していない場合、-p
オプションを使用して、ホストのポート3000をコンテナのポート3000にマッピングする必要があります。
完了後、以下のコマンドでチェックします。
status が Up になっていれば、コンテナは正常に起動して稼働中であることを意味します。
$podman ps -a
43e15c99bb94 localhost/my-spa-react-app:01 npm start 3 seconds ago Up 3 seconds ago 0.0.0.0:3000->3000/tcp my-spa-app-container
通常、podman ps コマンドだけでは、現在実行中のコンテナのみが表示されます。しかし、停止中のコンテナやエラーで起動できなかったコンテナ、過去に実行されたコンテナも確認したい場合は、-a オプションの使用が推奨されます。
最後は 「http://localhost:3000/」 を起動してアプリケーションをチェックします。画面が表示されれば、環境構築は完了になります。
2.3.1. 補足 発生し得るエラーおよびその解決策
今回はwindowsで作ったReactアプリケーションをLinuxでpodmanを用いてデプロイしました。
Windows と Linux ではファイルパスや環境設定が異なるため、このようなエラーが発生する可能性があります。
Error: spawn /mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe ENOENT
このエラーは、ReactアプリケーションがホストOSであるWindowsの環境で動作していると認識し、Powershellを使ってブラウザを開こうとしているために発生しています。
これを解決するために、プロジェクトフォルダ内のpackage.jsonファイルを以下のように修正します。
"scripts": {
"start": "cross-env BROWSER=none react-scripts start",
...
}
説明:
-
BROWSER=none
:アプリケーションの環境変数です。通常create-react-app
で作られたアプリをnpm start
で起動するとブラウザでアプリが自動的に開かれます。しかし、サーバーやコンテナなど、ブラウザやGUIがない環境では、自動的にブラウザを開くことができず、エラーが発生します。BROWSER=none
に設定することで、自動的に開かなくなります。 -
cross-env
: 異なるOS間で環境変数を一貫して設定できるようにするための npm パッケージです。これを通して、どのOSでもおなじような環境変数を設定できます。
まとめ
今回はReactアプリケーションの作成からコンテナの立ち上げまでの環境構築をしました。
最後までお読みただき、ありがとうございました!