DockerでフロントエンドとAPIを開発してみた

フロントエンドと API の両方を開発することがたまにあるのですが、悩むのは開発環境をどうするか?です。Ruby on Rails などは、バックエンドとフロントエンドの両方の面倒を見てくれるのですが、巨大すぎてメンテナンスが大変です。

バックエンドは API のみで、View はもはや JSON の提供だけです。フロントエンド開発の比重がかなり高くなりました。ですので、Ruby on Rails のようなバックエンドベースのアプリケーションでは無駄が多く、操作体系もよくないので、身軽なフロントエンド専用のツールで開発したいです。Ruby は遅いので、フロントエンドの開発は Node ベースでやりたいのです…。

そこで今回は、サーバーサイドは Node.js で、フロントエンドは create-react-app で開発するプロジェクトを紹介します。Docker を利用して、両プロジェクトをコマンド一発で起動できるようにします。

React プロジェクトを作る

まずプロジェクトのディレクトリを作ります。

$ mkdir docker-cra && cd docker-cra

フロントエンド用の React プロジェクトを create-react-app で作ります。

$ yarn create react-app frontend

Docker からローカルサーバーを起動するために Host 名を frontend/.env ファイルに指定します( .env ファイルは手動で作ります)。

HOST=0.0.0.0

これを指定して yarn start を実行すると、http://0.0.0.0:3000 でサーバーが起動します。確認したら ctrl + c でサーバーを落とします。

次に API プロジェクトを作ります。

API プロジェクトを作る

docker-cra のルートに戻り、API 用のディレクトリを作ります。

$ mkdir api && cd api

package.json を作り、yarn install を実行して必要なモジュールをインストールします。

{
  "name": "api",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "express": "^4.17.1",
    "node-dev": "^4.0.0"
  },
  "devDependencies": {},
  "scripts": {
    "start": "node-dev index.js"
  }
}

node-dev とは Node.js でファイルを更新した場合に、自動でサーバーを再起動してくれる開発用のアプリケーションです。

fgnass/node-dev: Zero-conf Node.js reloading

Express は Node.js でウェブアプリケーションを作るときによく利用されるミドルウェアです。

api/index.js に API サーバーの処理を記述します。

const express = require("express")
const app = express()

app.get("/api/users", function (req, res) {
  res.json([{
    id: 1, name: "maeda"
  }, {
    id: 2, name: "takada"
  }])
})

app.listen(9999)

api ディレクトリで yarn start を実行すると、http://localhost:9999 でローカルサーバーが起動します。http://localhost:9999/api/users にアクセスすると以下のような JSON が返ります。

[
  {
    "id": 1,
    "name": "maeda"
  },
  {
    "id": 2,
    "name": "takada"
  }
]

api サーバーの起動が確認できたので、ctrl + c で落としておきます。

フロントエンドと api サーバーの起動が確認できたので、Docker から両方を同時に起動できるようにします。

Vagrant の設定

Docker for Mac を使うと、webpack の処理が重すぎて開発できないので、Vagrant で Ubuntu を立ち上げ、その中で Docker を起動するようにします。

Vagrant の設定についての詳細はこちらの記事をご確認ください。

Github Actions を利用した WordPress 開発フロー – to-R Media

Docker や webpack の設定を色々調べてはみたのですが、Docker for Mac がやはりネックでどうしても速度を解決することができませんでした。

プロジェクトルートに Vagrantfile を作ります。(./docker-cra/Vagrantfile

Vagrant.configure('2') do |config|
  config.vm.box = 'ubuntu/xenial64'

  config.vm.hostname = 'my-app'

  config.vm.network :private_network, ip: '192.168.50.10'

  config.vm.provider :virtualbox do |vb|
    vb.gui = false
    vb.cpus = 4
    vb.memory = 4096
    vb.customize ['modifyvm', :id, '--natdnsproxy1', 'off']
    vb.customize ['modifyvm', :id, '--natdnshostresolver1', 'off']
  end

  config.disksize.size = '30GB'
  config.mutagen.orchestrate = true

  config.vm.synced_folder './', '/home/vagrant/app', type: "rsync",
		rsync_auto: true,
		rsync__exclude: ['.git/', 'node_modules/', 'log/', 'tmp/']

  config.vm.provision 'shell', inline: <<-SHELL
    curl -fsSL https://get.docker.com -o get-docker.sh
    sh get-docker.sh
    rm get-docker.sh
    usermod -aG docker vagrant
    curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    chmod +x /usr/local/bin/docker-compose
  SHELL
end

最終的には http://192.168.50.10:3000 で React が、http://192.168.50.10:9999 で API が起動するようになります。

Vagrant プラグインの mutagen の設定ファイルを、プロジェクトルートに配置します。(./docker-cra/mutagen.yml

sync:
  app:
    mode: "two-way-resolved"
    alpha: "./"
    beta: "my-app:/home/vagrant/app"
    ignore:
      vcs: true
      paths:
        - "/node_modules"
        - "/log"
        - "/tmp"

ここまでのディレクトリ構成は以下のようになります。

.
├── Vagrantfile
├── api
├── frontend
└── mutagen.yml

vagrant up コマンドで Ubuntu を起動させてみましょう。最初の起動には10分ほどかかります。処理が終了したら、vagrant status コマンドを実行してみます。以下のように表示されていれば起動が成功しています。

Current machine states:

default                   running (virtualbox)

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.

Vagrant の挙動が確認できたので、vagrant halt コマンドでシャットダウンしておきます。

次に Docker の設定をします。

Docker の設定

フロントエンド用の Dockerfile です。プロジェクトルートに Dockerfile.frontend というファイル名で保存します。

FROM node:12.10-alpine

WORKDIR /app

RUN apk update \
  && apk --no-cache add git ca-certificates wget

RUN wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub \
  && wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.30-r0/glibc-2.30-r0.apk \
  && apk --no-cache add glibc-2.30-r0.apk

COPY frontend/package.json .
COPY frontend/yarn.lock .
RUN yarn install

COPY ./frontend .

EXPOSE 3000

CMD yarn start

もうひとつ、api 用の Dockerfile を作ります。Dockerfile.api というファイル名で保存します。

FROM node:12.10-alpine

WORKDIR /app

RUN apk update \
  && apk --no-cache add git ca-certificates wget

RUN wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub \
  && wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.30-r0/glibc-2.30-r0.apk \
  && apk --no-cache add glibc-2.30-r0.apk

COPY api/package.json .
COPY api/yarn.lock .
RUN yarn install

COPY ./api .

EXPOSE 9999

CMD yarn start

2つのサーバーの準備ができたので、両方を起動するための docker-compose.yml をプロジェクトルートに作ります。

version: "3"

services:
  frontend:
    build:
      context: .
      dockerfile: Dockerfile.frontend
    command: "yarn start"
    ports:
      - "3000:3000"
    volumes:
      - ./frontend:/app
    tty: true

  api:
    build:
      context: .
      dockerfile: Dockerfile.api
    command: "yarn start"
    ports:
      - "9999:9999"
    volumes:
      - ./api:/app

create-react-app のコンテナには必ず tty: true を設定しましょう。これが無いと http://192.168.50.10:3000/ でプレビューすることができません(数時間ハマりました…)。

最終的なプロジェクト構成は以下のようになります。

.
├── Dockerfile.api
├── Dockerfile.frontend
├── Vagrantfile
├── api
├── docker-compose.yml
├── frontend
└── mutagen.yml

開発する

まずは Vagrant を起動します。

$ vagrant up

Vagrant 上に起動した Ubuntu にログインして、アプリケーションディレクトリに移動します。

$ vagrant ssh
$ cd app/

Docker を起動します(初回の起動には5分ほど時間がかかります)。

$ docker-compose up

以下の画面でターミナルが停止していれば起動しています。

Compiled successfully!
frontend_1  |
frontend_1  | You can now view frontend in the browser.
frontend_1  |
frontend_1  |   Local:            http://localhost:3000
frontend_1  |   On Your Network:  http://172.22.0.2:3000
frontend_1  |
frontend_1  | Note that the development build is not optimized.
frontend_1  | To create a production build, use yarn build.
frontend_1  |

http://192.168.50.10:3000/ をブラウザで開いてみると、create-react-app の画面が起動しているはずです。

api も起動しているので、http://192.168.50.10:3000/api/users をブラウザで開くと、以下のような JSON が確認できます。

まとめ

docker-compose up の1コマンドで、フロントエンドと api の開発の両方が同時にできるようになりました。便利。

僕は現在作っているアプリケーションで上記の組み合わせで開発していますが、create-react-app のファイル更新が Mac 上で行うのと遜色のないスピードで実行されるので、快適に開発できています。

Docker for Mac の遅さに苦しんでいる人はぜひ Vagrant を試してみてください。

今回のファイルはこちらにアップしておきました。

to-r/docker-cra

【告知】弊社のメンバーが執筆した書籍が発売されました!

弊社のメンバーが執筆した『初心者からちゃんとしたプロになる JavaScript基礎入門』が発売されました! JavaScriptの基礎からVue.jsまでをカバーしており、初学者から基本の復習をしたい方におすすめの1冊です。