なになれ

IT系のことを記録していきます。

MEAN StackをDocker Composeで動かす

前回はMEAN StackをHeroku上で動かしてみました。
hi1280.hatenablog.com

今回はMEAN Stackアプリに手を加えて、Dockerに対応させます。
Dockerにすれば、他の環境への移行も楽になるのではと思います。
Docker Composeを使って、nginx+Node.js+MongoDBの環境でMEAN Stackアプリを動かします。

プログラム一式はこちら
github.com

Docker Composeを使って、以下の3つのコンテナで構成します。

  • nginxでAngularを動かす(フロントエンドアプリ)
  • Node.jsでExpressを動かす(バックエンドアプリ)
  • MongoDBを動かす(DB)

Angularビルド環境用のDockerfile

AngularビルドのためにAngular CLI環境のDockerfileを作ります。

Dockerfile

FROM node:8.11.3-alpine
LABEL authors="hi1280"
RUN npm install -g @angular/cli@6.0.8
RUN npm cache clean --force

Node.jsの環境にAngular CLIをインストールしているだけというシンプルなモノです。

Angularアプリ実行用のDockerfile

Angularをビルドして、それをそのままDockerコンテナに含めると容量がかなり大きくなってしまいます。
そのため、Angularビルドとnginx上でのAngularアプリの実行を分けます。
これはDockerのマルチステージビルドの機能を使えば、実現できます。

Use multi-stage builds | Docker Documentation

マルチステージビルドを用いたDockerfileを作ります。

Dockerfile

# Angular Build
FROM hi1280/angular-cli:latest as build-stage
WORKDIR /usr/src/app
COPY ["package.json", "package-lock.json*", "./"]
RUN npm install --production --silent
COPY . .
RUN npm run build
# nginx
FROM nginx:alpine
RUN rm -rf /usr/share/nginx/html/*
COPY --from=build-stage /usr/src/app/dist/mean-example  /usr/share/nginx/html
COPY ./nginx /etc/nginx/conf.d
CMD ["nginx", "-g", "daemon off;"]

COPY --from=build-stageでビルド後の成果物のみをコピーしています。
これにより、最小限のファイルサイズになります。
AngularでビルドしたHTMLリソースをnginxで動かすDockerコンテナになります。

Express実行用のDockerfile

Expressを動かすDockerfileを作ります。

Dockerfile

FROM node:8.11.3-alpine
WORKDIR /usr/src/app
COPY ["server.js", "/src/server/package.json", "/src/server/package-lock.json", "./"]
COPY ["/src/server", "./src/server"]
RUN npm install --production --silent
EXPOSE 3000
ENV NODE_ENV production
CMD [ "node", "server.js" ]

Node.jsの環境を公式のイメージから取得して、Expressを動かすだけのDockerコンテナになります。

Docker Composeで各コンテナを連携

これまでに用意した各Dockerコンテナを連携します。

docker-compose.yml

version: '2.1'

services:
  frontend:
    build:
      context: .
      dockerfile: Dockerfile-frontend
    ports:
      - 80:80
    environment:
      - APP_HOST=backend
      - APP_PORT=3000
    command: /bin/sh -c "envsubst '$$APP_HOST$$APP_PORT' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"
    depends_on:
      - backend
  backend:
    build:
      context: .
      dockerfile: Dockerfile-backend
    ports:
      - 3000:3000
    depends_on:
      - db
    environment: 
      - MONGODB_URI=mongodb://db:27017/my-heroes
  db:
    image: mongo:3.4.16-jessie
    restart: always
    volumes: 
      - /data/db:/data/db

URLにサービス名を指定することで、各サービスにアクセスすることができます。
mongodb://db:27017/my-heroesといった形です。
depends_onを指定することで、サービスを立ち上げるために事前に起動が必要なサービスを指定できます。
これにより、サービスを起動する順番を制御することができます。

Docker Composeにおけるnginxの設定変更

環境変数を用いて動的にnginxの設定を変更しています。
nginxの公式Dockerイメージにそのやり方が記載されています。(Using environment variables in nginx configurationという箇所です)
https://hub.docker.com/_/nginx/
こちらも参考にしました。
Docker上のNginxのconfに環境変数(env)を渡すたったひとつの全く優れてない方法(修正:+優れている方法)

default.conf.template

server {
    listen       80;
    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://${APP_HOST}:${APP_PORT}/api/;
    }

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

}

${APP_HOST}と${APP_PORT}が環境変数で定義した値に変わります。
Expressのサービス名にアクセスする設定になります。

まとめ

公式のDockerイメージを活用すれば、プログラムをコピーして、起動コマンドを実行するといった形で、Dockerコンテナ化できるので難しいことはあまりないかなと思います。
ハマったら、コンテナのシェルに入ってデバッグするといったことができると良いです。