皆さん、Angular使ってますか?
私は仕事でも趣味でもAngularを使うAngular大好き人間ですが、共同開発なんかで動作確認してもらうとき、node
入れて@angular/cli
入れてng serve
して…となってしまい、結構環境が汚れてしまうのが難点だなーと思っていました。
あと、本番環境のときng build --prod
して出来たビルドファイルをWinSCPで直上げするのも何とかしたい。いちいちコマンド叩いてWinSCP開くのめんどくせぇ…
でも@angular/cli
の便利さは捨てがたい…特にオートリロードが…
というわけで、「AngularアプリをDockerの上で動かす」ことに挑戦してみましたので、知見を残しておきます。
仕様
- 本番環境としても開発環境としても使えること
- 本番環境ではnginxを用いること
- 開発環境ではオートリロードしたい
- 一つのDockerfileにまとめる
方法
Dockerのマルチステージビルドを使います。
### ベースステージ ###
FROM node:lts-alpine as base
# @angular/cliをグローバルインストール
RUN npm install -g @angular/cli
# ワーキングディレクトリの設定
WORKDIR /some-angular-app
# package.jsonをコピー
COPY ./package*.json /some-angular-app/
# 一度node_modulesを削除してからnpm install
RUN rm -rf node_modules && npm install
### ビルドステージ ###
FROM base as build
# 全てのソースファイルをコピー
COPY ./ /some-angular-app/
# 本番用ビルド
RUN ng build --prod --output-path=./dist/build-by-docker
### プロダクションステージ ###
FROM nginx:alpine as prod
# ビルドステージで生成されたファイルをnginxの公開用ディレクトリにコピー
COPY --from=build /some-angular-app/dist/build-by-docker /usr/share/nginx/html
# nginx.confをコピー
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
Dockerfileではソースのコピーとビルド処理、ビルド結果のコピーのみを行い、
主たるコマンドはdocker-compose.ymlに書きます。
version: "3.4"
services:
node:
build:
context: ./
dockerfile: "Dockerfile"
target: base
ports:
- "4200:4200"
command: sh -c "ng serve --host 0.0.0.0 --poll=1000"
volumes:
- .:/some-angular-app
- node_modules:/some-angular-app/node_modules
tty: true
volumes:
node_modules:
driver: "local"
こちらは開発環境用のdocker-compose.ymlです。Dockerfileにおけるbase
ステージを継承して動きます。
volumes
でソースコードをマウントしているため、Dockerfile内で特にCOPYをしなくてもこれで動きます。
node_modulesをマウント対象から除外するのを忘れないようにして下さい。
commandですが、ng serve --host 0.0.0.0 --poll=1000
がミソです。
Dockerコンテナは、ホストPCとlocalhostを共有していますが、コンテナ内で動いている@angular/cli
にはそんなことは知ったこっちゃないため、@angular/cli
が言うところの「localhost」は、Dockerコンテナの「localhost」だけなのです。
つまり、--host
オプションで「同ネットワークに接続しているすべてのIPアドレス」からのアクセスを許可してあげなければなりません。
また、オートリロードを正しく作動させるために、--poll
オプションでファイル変更状況をポーリングするように設定しています。単位はmsです。
version: "3.4"
services:
node:
build:
context: ./
dockerfile: "Dockerfile"
target: prod
ports:
- "8080:80"
tty: true
こちらは本番用のdocker-compose.prod.ymlです。prod
ステージを継承して動きますが、特筆して何かをしているわけではありません。
.dockerignore
.git
dist
*Dockerfile*
*docker-compose*
node_modules
COPY時にnode_modulesがコピーされないよう、.dockerignoreに書いておきます。
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
include /etc/nginx/mime.types;
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
location / {
try_files $uri $uri/ /index.html;
}
}
最後に、nginxのconfフォルダにコピーしているdefault.conf
です。
Angularをnginxで動かすために必要な「すべてのURIに対するアクセスをindex.htmlに流す」処理を行っているだけです。適宜変更してください。
立ち上げ方
# 開発
$ docker-compose up --build
# 本番
$ docker-compose -f docker-compose.prod.yml up --build
まとめ
マルチステージビルドを使って、docker-composeコマンド一発でAngularアプリを立ち上げる環境を構築することができましたが、予想通りというかなんというか、直接ホストPCでng serve
したときよりはかなりビルドが重いです。
他人の環境を汚さないようにするだけでもかなりメリットはありますが、自分がゴリゴリ開発する立場だったら素直にホストPCに@angular/cli
入れた方がいいと思いました。