morishitaです。
Dockerコンテナは1プロセスだけ動作させるのが基本なので、複数の言語実行環境をインストールする必要はほとんどないです。
ただ唯一、例外かなと思うのがNode.js。
Webアプリケーションを開発する場合、それほどリッチなUIでなくても多かれ少なかれJavaScriptのコードを書くと思います。
そんな開発の現場では、JavaScriptを書いてそれをそのままブラウザで動かす牧歌的な時代は今は昔。
ES6やTypescriptなど、より高機能かつ実装しやすい仕様でコードを書いてしトランスパイルすることが多いのではないでしょうか。CSSだってSCSSやSASSで書いてビルドしますよね1?
プロダクション環境ではトランスパイル専用のコンテナやマルチステージでビルドしたものをコピーする方法があるので、必ずしもメインの言語を動作させるコンテナでNode.jsも動く必要はないです。
でも開発用のDockerイメージ/コンテナにおいては使えたほうが便利なのでインストールすることが多いのではないでしょうか。
apt-get install
でインストールする方法もありますが、マルチステージビルドでコピーしてインストールする方法もあるので試してみました。
シンプルに Ruby と Node.js(Yarn含む)が使えるだけのイメージの作成で両者を比較してみます。
apt-get install
でインストールする方法
まずはマルチステージビルを使わない従来の方法。 Dockerfileは次の様になります。
FROM ruby:2.6.3 # Install Node.js and Yarn RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - \ && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list RUN apt-get update -qq \ && apt-get install -y --no-install-recommends \ yarn \ nodejs \ && rm -rf /var/lib/apt/lists/*
次のコマンドでイメージをビルドできます。
$ docker build -t ruby-node:normal .
マルチステージビルドを使う場合
続いて、マルチステージビルドを利用したNode.jsのインストール。 Dockerfileは次の様になります。
FROM node:12.4-stretch as node FROM ruby:2.6.3 # Install Node.js and Yarn ENV YARN_VERSION 1.16.0 RUN mkdir -p /opt COPY --from=node /opt/yarn-v$YARN_VERSION /opt/yarn COPY --from=node /usr/local/bin/node /usr/local/bin/ COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/ RUN ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \ && ln -s /opt/yarn/bin/yarn /usr/local/bin/yarnpkg \ && ln -s /usr/local/bin/node /usr/local/bin/nodejs \ && ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \ && ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx
特徴はFROM
が2つあることです。
FROM node:12.4-stretch as node
はNodeの公式イメージにnode
というエイリアス名をつけています。
このエイリアス名はCOPY --from=node ...
のようにコピー元として指定できます。上記のDockerfileでは公式イメージに含まれるNodeとYarnをコピーするのに使っています。
コピー後はシンボリックリンクをパスの通ったところに置いて使えるようにしています。
先程と同様に次のコマンドでイメージをビルドできます。
$ docker build -t ruby-node:multi .
ビルドの結果
上記の2通りの方法で実際にビルドしてみました。
ビルド時間
条件はできるだけ合わせて計測しましたが、
ビルドの時間はマシンの性能やネットワーク速度に依存するため、時間については参考値だと思ってください。
相対的な違いを見ていただければと思います。
Rubyのイメージについては予めPullした状態からです。
両者のビルドについて時間を計測しました。
まずは、apt-get install
でインストールする方。
$ time docker build -t ruby-node:normal . 〜 略 〜 real 0m18.781s user 0m0.082s sys 0m0.071s
私のマシンでは19秒弱。
続いてマルチステージビルドの方。
これにはNodeのイメージのPullの時間が含まれています。
$ time docker build -t ruby-node:multi . 〜 略 〜 real 0m13.219s user 0m0.087s sys 0m0.070s
同じマシンで約13秒2。 こちらのほうが少しビルド時間は少ないです。
イメージのサイズ
イメージのサイズとはどうなったかというと次のとおりです。
ruby-node:normal
:通常のビルドで作ったイメージruby-node:multi
:マルチステージビルドで作ったイメージ
マルチステージビルドしたほうが僅かですが40MBほど小さくなりました。
まとめ
比較するとマルチステージビルドを使ったほうがビルド時間は短く、出来上がるイメージも小さいです。
ただ、圧倒的な効率化になるかというとそうでもなく、 もうちょっと差が出るかと思ったのですが…。
最後に
アクトインディではエンジニアを募集しています。