ECS(Fargate)でReactアプリを起動する方法メモ
はじめに
今回の記事の目的
nginxを利用した簡単なアプリをコンテナ上で起動することができたので、今回はECSのコンテナにReactアプリを載せることにする。
nginxを利用した簡単なアプリをコンテナ上で起動した方法
tomiko0404.hatenablog.com
環境
ローカル環境でdockerイメージを作成する
以前*1、dockerでReactアプリを作成した際は、
- docker-composeを利用
- vagrant上のワークスペースとdocker上のフォルダを同期する設定
volumes
を入れている - dockerコンテナ上で、React(Nodeプロジェクト)のインストールや実行(
npm install
やnpm start
を実施)
としていたが、今回はECS(Fargate)で構築するため、dockerコンテナに入れないし、ローカル環境とフォルダ同期もできない。
そこで、Dockerfile上に
- ソースコードをdocker上にコピー
- 必要なNodeモジュールのインストール
- プロジェクトの実行
を記載して指示する必要がある。
フォルダ構成
プロジェクトフォルダのフォルダ構成は以下。
動作確認用の簡易アプリで、create-react-app
で作成したもの。
App.jsx
など他のファイルがあってもOK。- ローカルで実行して動作を確認すると思うので、基本的にはapp/配下にnode_modulesフォルダが必要になる。node_modulesはappフォルダ内で
npm install
を実行すれば自動的に作成される。node_modulesを作成する場合は、後の手順(【補足】dockerコンテナにコピーしたくないファイルがある場合)を実施したほうが良い。
React-practice-6 ├── Dockerfile └── app ├── package.json ├── public │ └── index.html └── src └── index.js
Dockerfileの中身
Dockerfileの中身は以下とする。
FROM node:14.15.4-slim RUN apt-get update && apt-get install -y locales \ && locale-gen en_US.UTF-8 \ && localedef -i en_US -f UTF-8 en_US.UTF-8 \ && echo "export LC_ALL=en_US.UTF-8" >> ~/.bashrc \ && ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime COPY ./app /app WORKDIR /app RUN npm install ENV PORT 80 CMD ["npm", "start" ] RUN apt-get install -y \ wget \ curl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/*
ポイントとして、COPY
コマンドやRUN
コマンドは、dockerイメージをビルドする際に一度だけ実行される。一方で、CMD
コマンドは、コンテナを起動するたびに実行される。
そのため
# NG例 RUN npm start
と記載するとビルドが上手くいかなかった。
以下コマンドで、vagrantの./app
フォルダの中身をコンテナ上の/app
フォルダにコピーする。
COPY ./app /app
ここで、1つ目の引数(ローカルのフォルダ)には、
- 絶対パスは利用不可
- カレントフォルダより上のフォルダは設定不可
という制約がある。
次に以下コマンドで、フォルダ移動する。
WORKDIR /app
これを忘れるとコンテナ上でpackage.json
が見つからないのでエラーとなる。
npmの各種パッケージを、package.json
に従ってインストールする。
RUN npm install
これはイメージのビルド時に実行で問題ない。
次に環境変数の設定を実施。
ENV PORT 80
Nodeプロジェクトを実行したときにポート80でアクセスできるようになる。
最後にプロジェクトの実行コマンド。
CMD ["npm", "start" ]
これは起動コマンドなので、コンテナ起動時に実行してほしいものである。
【補足】dockerコンテナにコピーしたくないファイルがある場合
appフォルダ内にコピーしたくないファイルがある場合は、.dockerignore
ファイルを作成する必要がある。
node_modules
フォルダ等、ローカルでアプリを動かしてみる際に利用しているフォルダがあるのであれば、コピーしない設定を記載しておいたほうがよさそう。
dockerイメージのビルド・ECRプッシュ
あとは前回の記事と同じように実施していけばOK。
nginxを利用した簡単なアプリをコンテナ上で起動した方法
tomiko0404.hatenablog.com
毎回やることだけ簡単にメモ
イメージをビルドする。
$ sudo docker build -t react-practice-6 .
$ sudo docker images
# "react-practice-6" の箇所は、イメージ名を指定する
ECRにリポジトリを作成し、「pushコマンドの表示」に従って実施する。
ただし、イメージにタグをつける手順については、AWSコンソールのコピペではなく
$ docker tag 第1引数 第2引数
のコマンドのうち第1引数部分を自分で決めたイメージ名に変更する必要がある。
// 例 $ docker tag react-practice-6 xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-react-practice-6:latest
また、レジストリに対してローカル環境を認証する手順では、以下のようなエラーがでることがある。
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/auth": dial unix /var/run/docker.sock: connect: permission denied
その場合は以下コマンドでファイルアクセス権限をつけてあげる。
sudo chmod ///var/run/docker.sock 666
ECS立ち上げ
ECSのタスク立ち上げは前回の記事通りで問題ない。
タスクを立ち上げた後、ソースコードを変更した場合は、
- dockerイメージの再ビルド
- dockerイメージへのタグ付け
- ECRへの再プッシュ
- タスクの立ち上げ直し
が必要となる。
参考記事
ALBを利用する場合
ALBを利用してコンテナを立ち上げる場合は、前回の記事ではなくこちら。 tomiko0404.hatenablog.com
ブルーグリーンデプロイメントを行ないたい場合
CodePipelineを利用して、ブルーグリーンデプロイメントを実施できるようにするにはこちら。 tomiko0404.hatenablog.com
追記:後日イメージビルド時にエラーが出た
後日同じことをやろうとしたらビルドでエラーが発生。
E: Failed to fetch http://security.debian.org/debian-security/pool/updates/main/o/openssl1.0/libssl1.0.2_1.0.2u-1~deb9u4_amd64.deb 404 Not Found [IP: 151.101.66.132 80] E: Failed to fetch http://security.debian.org/debian-security/pool/updates/main/o/openssl/openssl_1.1.0l-1~deb9u3_amd64.deb 404 Not Found [IP: 151.101.66.132 80] E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing? The command '/bin/sh -c apt-get install -y wget curl && apt-get clean && rm -rf /var/lib/apt/lists/*' returned a non-zero code: 100
Dockerfileの最後に記載している以下部分でエラーが発生している模様。
RUN apt-get install -y \
wget \
curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
色々試した結果、既にあったnode
イメージを削除してやり直すとうまくいった。FROM node:14.15.4-slim
でベースとなっている部分が何かしらおかしくなっていた?
REPOSITORY TAG IMAGE ID CREATED SIZE node 14.15.4-slim 2f75d89d8162 7 months ago 167MB
dockerイメージを削除するコマンド(メモ)
$ docker rmi [イメージID]
これで消えない場合は、コンテナで使われている場合があるので
$ docker ps -a $ docker rm [コンテナID]
でコンテナを削除する。それでも消えなければ
$ docker rmi -f [イメージID]
おわりに
ECS Fargate上にReactアプリを立ち上げることができた。 基本を習得するためにDockerfileのみで実施したが、編集するたびにビルドをやり直すのは大変そう。
AWSでもdocker-composeを利用できる機能がリリースされているようなので、使ったほうがよいかもしれない。
Docker Compose と Amazon ECS を利用したソフトウェアデリバリの自動化 | Amazon Web Services ブログ
参考:その他のファイルの中身
あくまで動作確認用の簡易アプリ。ローカルでnpx create-react-app
すれば最低限のファイルを作ってくれる。index.js以外は、テンプレートそのままである。App.jsx
も作っていない。
Reactアプリ(または React×TypeScriptアプリ)を作る方法メモ - エンジニアを目指す日常ブログ
- index.js
import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.render( <React.StrictMode> <h1>React-Practice-6</h1> <h2>app edit</h2> </React.StrictMode>, document.getElementById('root') );
- index.html ※
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="Web site created using create-react-app" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <!-- manifest.json provides metadata used when your web app is installed on a user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ --> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <!-- Notice the use of %PUBLIC_URL% in the tags above. It will be replaced with the URL of the `public` folder during the build. Only files inside the `public` folder can be referenced from the HTML. Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> <title>React App</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <!-- This HTML file is a template. If you open it directly in the browser, you will see an empty page. You can add webfonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the <body> tag. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> </body> </html>
- package.json ※前に作ったアプリからコピーしてきたもの。必要な記載はアプリによって変わるのであくまで参考。
{ "name": "jakee-atomic-design", "version": "0.1.0", "private": true, "dependencies": { "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router-dom": "^5.3.0", "react-scripts": "4.0.3", "recoil": "^0.4.1", "styled-components": "^5.3.1", "web-vitals": "^1.0.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": [ "react-app", "react-app/jest" ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } }