Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
130
Help us understand the problem. What is going on with this article?
@KeitaMoromizato

Dockerでnginx+node.jsのSPA構成を試す

More than 3 years have passed since last update.

なにをしたのか

Docker Composeという、複数のDockerコンテナを管理する機能を使ったサンプルです。目指しているのは、「実用的かつ最小のサンプル(と思っているもの)」です。

nginxコンテナでstaticなファイル(html/js)を配信し、APIのアクセスは後ろにいるnode.jsのアプリケーションコンテナにフォワードして、SPAのWebサービスを構築しています。

docker.png

基本的に、以下のページを参考に書いています。
DockerでのNodeアプリ構築で学んだこと | POSTD

リポジトリはこちら。
docker-compose-test

環境

  • Docker 1.12.1
  • docker-compose 1.8.0
  • node.js 4.6.0
  • nginx 1.11.4

動かす

Docker for macがインストールされていることを前提として。

$ git clone https://github.com/KeitaMoromizato/docker-compose-test
$ cd docker-compose-test
$ sudo docker-compose build
$ sudo docker-compose up

内容

Dockerとは

Dockerは、コンテナ型の仮想環境です。

簡単に言うと、Infrastructure as Code(インフラのコード化)の一種です。DockerFileという設定ファイルに、どういうサーバーを構築したいかを書いておき、docker runコマンドを実行するだけで、その環境のコンテナが即起動できます。

また、そのDockerFileから構成されるコンテナが、Docker Imageという形で世の中(DockerHubというサイト)に公開されています。なので、例えば公式のWordpressイメージをダウンロードして、docker runするだけで、簡単にWordpressの環境が入ったコンテナが作れるようになっています。

Docker Composeとは

Dockerは、その特徴から1コンテナ1アプリケーションという思想が基本です。1つのコンテナにすべての機能を詰め込まず、複数のコンテナを起動して、協調してシステムを構成しましょうという考えです。

例えば、リバースプロキシのnginx、バックエンドのアプリケーション、データベースを、それぞれ別のコンテナで起動します。それを簡単に実現するのが、Docker Composeです。

docker-compose.ymlという設定ファイルに、どのコンテナを立ち上げるか、どのコンテナ同士が通信するのかといったことを記述し、これまたdocker-compose upというコマンド1つで、必要なサービスがすべて立ち上がります。

目的

プロダクションで使うメリットは、まだ使っていないのでパス。

今回使った目的は、手元の開発環境の管理のため。(プライベートも含め)いろいろな開発をしていると、やれnode.jsのバージョンが違うだとか、やれGo langのパスが通ってないとか、なんだか色々と面倒なことが多い。

Dockerを使えば、バックエンドアプリケーションを動かす環境をDockerFileという形で明示でき、かつnginxやDBなどをローカルで個別に立ち上げるのではなく、Docker Composeを使うことでより本番環境に近いものが、簡単に再現できます。

という訳で、普段ちょっとしたものを作るときによく使うスタック、node.js+SPA(Webpackビルド)の環境を構築してみました。

ディレクトリ構成

主要なものだけで、こんな感じ。

|-- Dockerfile
|-- docker-compose.yml
|-- client
|  |-- index.html
|  |-- index.js
|
|-- server
|  |-- index.js
|
|-- nginx
|  |-- default.conf 
名前 役割
Dockerfile node.jsアプリのコンテナの設定。コンテナを最初に構築するときに、何をするのかなど。
docker-compose.yml 各コンテナの詳細設定。今回は、nodeコンテナとnginxコンテナの設定が書いてある
client 静的配信するファイルのコンパイル前データ。これをWebpackでビルドして、outフォルダに格納される
server nodeアプリ側のコード
nginx nginxの設定ファイル。静的配信と、nodeアプリへフォワードする設定が書いてある

詳細

Dockerfile

Docke Composeを使う場合でも、Singleコンテナの場合でも、基本となるのはDockerfileです。ベースとなるコンテナ(今回で言うと、nodeアプリケーション)の設定を書いていきます。

ここでは、主にnpm installとフロントのビルド(Webpack)のための設定を記述しています。

まずは必要なファイルをコピーして

Dockerfile
COPY package.json webpack.config.js $HOME/nodeapp/
COPY client $HOME/nodeapp/client

npm installnpm run buildを実行します。

Dockerfile
USER app
WORKDIR $HOME/nodeapp
RUN npm install
RUN npm run build

package.jsonwebpack.config.jsを見るとわかるように、ビルドしたファイルはnodeコンテナ上outディレクトリに出力されます。

package.json
{
  "scripts": {
    "build": "webpack --config webpack.config.js"
  }
}
webpack.config.js
{
  output: {
    path: __dirname + "/out",
    filename: 'bundle.js',
  }
}

docker-compose.yml

docker-compose.ymlには、起動するすべてのコンテナの設定を書きます。

docker-compose.yml
nodeapp:
  build: .
  container_name: "nodeapp"
  command: node index.js
  ports:
    - '3000:3000'
  environment:
    - PORT=3000
  volumes:
    - ./server:/home/app/nodeapp
    - /home/app/nodeapp/node_modules
    - www:/home/app/nodeapp/out
nginx-proxy:
  image: nginx
  container_name: "nginx"
  ports:
    - '8080:8080'
  volumes:
    - ./nginx:/etc/nginx/conf.d:ro
    - www:/www/app:ro
  links:
    - 'nodeapp'

image

DockerHubにおいてあるイメージを選択します。nodeのイメージはDockerfileの方に書いてあるので、ここではnginxのイメージのみ。

DockerHubからイメージを探すときは、できるだけOFFICIALなものを選びましょう。

environment

コンテナに設定する環境変数を指定します。

volumes

volumesの記述には、いくつか目的があります。例えば、以下の記述は「ホスト上の.serverディレクトリを、コンテナ上の/home/app/nodeappにマウントする`設定です。これにより、コンテナ上で任意のアプリケーションを動かせるようになります。

  volumes:
    - ./server:/home/app/nodeapp

また、以下の記述は「node_modulesディレクトリをボリュームとして作成する」設定です。ボリュームは、コンテナとは別のライフサイクルを持っています。ここでは、コンテナを落とす度にnpm installしたモジュールが消えてしまうと困るので、別途ボリュームとして定義しています。

  volumes:
    - /home/app/nodeapp/node_modules

また、ボリュームはコンテナ間で共有することもできます。以下の記述は「コンテナ上のディレクトリを、名前付きボリューム(www)として他のコンテナと共有できるようにする」設定です。ここでは、nodeコンテナでビルドしたファイルの出力ディレクトリ(out)にwwwwという名前を付けています。

  volumes:
    - www:/home/app/nodeapp/out

そして、そのボリュームを、nginxコンテナから参照し、静的配信しています。後ろにroを付けると、そのコンテナからは読み取り専用(ReadOnly)になります。

  volumes:
    - www:/www/app:ro

links

コンテナが通信できる、別のコンテナの名前を記述します。ここでは、nginx-proxyがリクエストをフォワードするため、nodeappコンテナを見えるようにしています。

nginxの設定

nginxのコンテナでは、静的ファイルの配信と、APIへのリクエストをnodeコンテナにフォワードする設定をします。

静的ファイルは、先のdocker-compose.ymlの設定で、outディレクトリを/www/appにマウントしているため、そこをrootとして設定します。

APIへのリクエストは、proxy_passでnodeコンテナにフォワードします。ここでは、linksで設定した名前(nodeapp)で転送できるようになります。

nginx/default.conf

server {
  listen 8080;
  server_name localhost;

  location /api/ {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://nodeapp:3000/;
  }

  location / {
    root /www/app;
  }
}

nodeアプリ

普通のexpressアプリ。上記のnginxの設定だと、URLの/api部分が省かれて転送されるので、こちらでは/itemsでLISTENする。

server/index.js
const app = require('express')();

const PORT = 3000;

app.use((req, res, next) => {
  console.log(req.url);
  next();
});

app.get('/items', (req, res) => {
  res.send([
    'hoge', 'huga'
  ]);
});

app.listen(PORT, () => console.log(`LISTEN:${PORT} on docker`));

フロント

実装はReactだけど何でも良いので省略。Webpackでビルドしたものと、HTMLファイルをoutディレクトリに出力するように設定する。

webpack.config.js
module.exports = {
  entry: {
    js: "./client/index.js",
    html: "./client/index.html",
  },  
  output: {
    path: __dirname + "/out",
    filename: 'bundle.js',
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        query: {
          presets: ['es2015', 'react'],
        },
      },
      {
        test: /\.html$/,
        loader: 'file?name=[name].[ext]'
      },
    ],
  },
}

まとめ

一度慣れてしまうと、開発環境は基本的にこれで構築したほうが良いと思うくらい、楽。

あとは、Mongo DBMySQLElasticsearchなども公式のイメージがあるので、自分のサービスに合わせてdocker-compose.ymlに追加してあげれば良い。公式のREADMEは結構充実しているので、それを見ればなんとかなる。

次やること

プロダクションと開発用の設定をわけたい
* フロントをプロダクションビルドしたい
* 開発中はwebpack --watchしたい

130
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
130
Help us understand the problem. What is going on with this article?