React+Express+Node.js+MongoDBでブログっぽい何かを作る (1)環境構築編

今までWebアプリケーションを作る時には、バックエンドであればDjango+Postgres+nginx(もしくはApache)、フロントエンドであればjQueryやその周辺を主に用いてきた。
しかし、そろそろ何か別のものを学習したくなってきたので、React + Express + Node.js + MongoDBでやっていく。
どれも触ったことがないので新鮮。

とにかくまずは環境構築をする必要がある。
今回作る環境はこんな感じ。

f:id:itto-ki:20180701054452p:plain

フロントエンド(React)を提供するためフロントエンドサーバ、APIを提供するバックエンドサーバ、データベースの3つをDockerコンテナとして用意する。

ディレクトリ構成

ディレクトリの構成はこんな感じ

blog
├── backend
│   ├── dockerfile
│   └── project
├── data
│   └── .
├── docker-compose.yml
└── frontend
    ├── dockerfile
    └── project

frontend/projectをフロントエンドコンテナに、backend/projectをバックエンドコンテナに、dataをデータベースコンテナにそれぞれマウントする。

docker-compose.ymlは以下の通り。

docker-compose.yml


gistf1da8a2e4bc697870988241df76035b1
フロントエンドコンテナのポート3000をホストの8888に、バックエンドコンテナのポート3000をホストの8080に、
データベースコンテナのポート27017をホストの27017にそれぞれおポートフォワードしている。
バックエンドとデータベースに関してはサービスを運用する上ではポートフォワードをする必要はないのだが、
このようにしておいたほうが開発時にホスト側から動作確認ができて便利である。


また、backendとfrontendのdockerfileは以下の通り。
ディレクトリの作成とグローバルに必要なモジュールのインストールのみを行っている。

dockerfile(backend)

dockerfile(frontend)

以上の3つのファイルを作成したら、docker-compose.ymlのあるディレクトリで以下のコマンドを叩くとそれぞれのイメージがビルドされる。

docker-compose build

以降でも特別な記述がない限り、docker-compose.ymlのあるディレクトリでコマンドを叩いてるものとする。

モジュールのインストール

続いて書くイメージに必要なモジュールをインストールしていく。
まずはバックエンドサーバから。
以下のコマンドでコンテナを起動する。

docker run -it -v $(pwd)/backend/express:/home/node/backend blog_backend /bin/bash

これでバックエンドコンテナに入ることができる。
続いてプロジェクトに必要な設定ファイルを作り、さらにモジュールをインストールするために以下のコマンドを順に実行。

express --view=pug --git
yarn add http-errors mongoose nodemon

これでバックエンドコンテナの環境が出来上がる。
現在のディレクトリはホストの$(pwd)/backend/expressディレクトリにマウントされているため、
上記のコマンドで生成された設定ファイル、モジュール等はホストからも操作することができる。

続いてフロントエンドコンテナにも必要な環境を作っていく。
先程とほぼ同じように、以下のコマンドでコンテナを起動する。

docker run -it -v $(pwd)/frontend/react:/home/node/frontend blog_frontend /bin/bash

これで先程と同じくフロントエンドコンテナに入ることができた。
さらに同様に環境・モジュールのセットアップを以下のコマンドで行う。

npx create-react-app .
yarn add redux react-redux react-router react-router-dom history @material-ui/core

以上でフロントエンド、バックエンド共に起動させるのに必要な設定、モジュールの準備が出来た。
モジュールはとりあえずこんなもんかなという感じなので必要に応じて適宜追加していく予定。

あとは追加でバックエンドコンテナにインストールしたnodemonモジュールを用いるための設定を行う。
/frontend/project/package.jsonのを以下の様に書き換える。

重要なのはstartコマンドの内容で、このように書き換えることでnodemonを用いてサーバを起動させることになる。
nodemonはコードに変更がある度にサーバをリスタートしてくれるモジュールで、用いることで開発が楽になる。

サービスの開始

以上が全て完了したら以下のコマンドを実行する。

docker-compose up -d

docker-compose.ymlで定義した3つのコンテナが立ち上がりサービスが開始される。
ホストのブラウザからlocalhostの8888, 8080, 27017ポートに接続してみることによって、
それぞれのサービスが稼働していることが確認できるだろう。

環境構築は以上で終了。
次回からはサービスを作り始めていく。

セグメンテーションでのメモリ管理について

IA-32 プロセッサには以下に挙げる2つの動作モードがある。

リアルモード
  • 8086用に書かれたプログラムを実行するモード
  • 16bitで動作する
プロテクトモード
  • メモリ管理やタスク管理、保護機能等の80386で拡張された機能を利用するためのモード
  • 32bitで動作する

今回はプロテクトモードについて特にセグメンテーションでのメモリ管理に注目して取り上げる。

プロテクトモードのメモリ管理

GDTとセグメントディスクリプタ

プロテクトモードではセグメントを用いてメモリ管理を行う。これをセグメンテーションと呼ぶ。
セグメンテーションのためにはメモリ上にGDT(Global Discriptor Table)と呼ばれるセグメントディスクリプタのテーブルを作成する必要がある。
GDTを作るタイミングはリアルモードからプロテクトモードに移行する時。
セグメントディスクリプタはメモリ上に存在するセグメントの位置を指定する。
この様子を以下の図に示す。
f:id:itto-ki:20180413011326p:plain

セグメントレジスタ

また、プロセッサにはセグメントレジスタと呼ばれるセグメントを管理するためのCS,DS,ES,FS,SSの5つのレジスタがある。
これらのレジスタを用いてGDTの中に複数個あるセグメントディスクリプタのうち、どれを使用するのかを指定する。
この様子を以下に示す。
f:id:itto-ki:20180413012838p:plain

この時、セグメントレジスタにあるデータ構造をセグメントセレクタと呼び、以下の様な構造になっている。
f:id:itto-ki:20180413015224p:plain

セグメントセレクタのindex値によって使用するセグメントディスクリプタを指定している。

アドレス指定

さて先程、GDT内のセグメントディスクリプタを用いてメモリ上に存在するセグメントの位置を指定すると書いたが、
これではセグメントの先頭アドレスしか分からない。
一方で、プログラムからは◯◯セグメントの△△番地のアドレスを指定される場合がある。
この様な場合にどうするのか。
ここでセグメントディスクリプタの構造を見てみると以下の様になっている。
f:id:itto-ki:20180413193804p:plain

注目するべきなのは以下の3つのフィールドである。

フィールド 説明
Base Address Low ベースアドレスの下位16bit
Base Address Mid ベースアドレスの中位8bit
Base Address Hi ベースアドレスの上位8bit

これら3つのフィールドを合わせて(32bitsで)ベースアドレスを表す。
プログラムからのメモリアクセスの時は、このベースアドレスとプログラムによって指定されたアドレス(これを論理アドレスと呼ぶ)から、
実際のメモリ上のアドレス(これを物理アドレスと呼ぶ)への変換作業がCPUにより行われる。
また、これによりメモリがどのようにセグメンテーションされていようと、
プログラムに対してはメモリを先頭から使用できるように見せかけることができる。

感想

前に一度理解していたつもりだったけど、ほとんど覚えてなかったので復習した。
セグメンテーションは複雑すぎる機能でLinuxでは使われていないため触れる機会もないので仕方ない。