Help us understand the problem. What is going on with this article?

複数の言語の runtime を持つ Docker image を作る

More than 1 year has passed since last update.

TL;DR

FROM a as a
FROM b

COPY --from=a /path/to/binary /path/to/binary

# snip.

やりたいこと

よくあるのが 👇 のような話

  • Web サービス開発
    • サーバ実装に Ruby on Rails
    • フロントエンドのビルドに Webpack (Webpacker)
  • Docker コンテナ上で開発をしたい
    • 全開発者が同じ環境で開発ができる → セットアップで消耗しにくい
  • Ruby をベースイメージに利用した
    • Node.js はどうやって入れる?

この問題を解決したい.

よくあるパターン

apk add nodejs する

FROM ruby:2.4.2-alpine

RUN apk add --update \
  nodejs

# snip.
  • pros
    • 1行書くだけ
  • cons
    • Node.js のバージョン固定ができない
      • .node-version があったら終わり

node:x.y.z-alpineDockerfile からコピペ

FROM ruby:2.4.2-alpine

# Copy from https://github.com/nodejs/docker-node/blob/bf84a38aeacb4f6aad34e07c79fd3a0084da5cd2/8/alpine/Dockerfile#L3-L66
ENV NODE_VERSION 8.9.1

RUN addgroup -g 1000 node \
    && adduser -u 1000 -G node -s /bin/sh -D node \
    && apk add --no-cache \
        libstdc++ \
    && apk add --no-cache --virtual .build-deps \
        binutils-gold \
        curl \
        g++ \
        gcc \
        gnupg \
        libgcc \
        linux-headers \
        make \
        python \
  # gpg keys listed at https://github.com/nodejs/node#release-team
  && for key in \
    94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
    FD3A5288F042B6850C66B31F09FE44734EB7990E \
    71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
    DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
    C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
    B9AE9905FFD7803F25714661B63B535A4C206CA9 \
    56730D5401028683275BD23C23EFEFE93C4CFFFE \
    77984A986EBC2AA786BC0F66B01FBB92821C587A \
  ; do \
    gpg --keyserver pgp.mit.edu --recv-keys "$key" || \
    gpg --keyserver keyserver.pgp.com --recv-keys "$key" || \
    gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" ; \
  done \
    && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz" \
    && curl -SLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
    && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
    && grep " node-v$NODE_VERSION.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
    && tar -xf "node-v$NODE_VERSION.tar.xz" \
    && cd "node-v$NODE_VERSION" \
    && ./configure \
    && make -j$(getconf _NPROCESSORS_ONLN) \
    && make install \
    && apk del .build-deps \
    && cd .. \
    && rm -Rf "node-v$NODE_VERSION" \
    && rm "node-v$NODE_VERSION.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt

ENV YARN_VERSION 1.3.2

RUN apk add --no-cache --virtual .build-deps-yarn curl gnupg tar \
  && for key in \
    6A010C5166006599AA17F08146C2130DFD2497F5 \
  ; do \
    gpg --keyserver pgp.mit.edu --recv-keys "$key" || \
    gpg --keyserver keyserver.pgp.com --recv-keys "$key" || \
    gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" ; \
  done \
  && curl -fSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
  && curl -fSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz.asc" \
  && gpg --batch --verify yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \
  && mkdir -p /opt/yarn \
  && tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/yarn --strip-components=1 \
  && ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
  && ln -s /opt/yarn/bin/yarn /usr/local/bin/yarnpkg \
  && rm yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \
  && apk del .build-deps-yarn

# snip.
  • pros
    • Node.js や yarn のバージョンは自由にできる
  • cons
    • Dockerfile が地獄になる
    • docker build にめっちゃ時間かかる
      • Ruby はビルド済みだが Node.js は自分でビルドしているので

そもそも image を分ける

# frontend.dockerfile
FROM node:8.9.1-alpine

# snip.
FROM ruby:2.4.2-alpine

# snip.
  • pros
    • Node.js や yarn のバージョンは自由にできる
    • 個々の Dockerfile はシンプルになる
  • cons
    • ファイルが増える
    • docker-compose.yml が複雑になる
    • 開発環境の VOLUME マウントのやりかたに工夫が必要

アイディア: multi-stage build を利用する

multi-stage build については以下の記事が詳しい

multi-stage build のよくある利用法としては「 Go のバイナリを生成するイメージ」「生成済みバイナリを実行するだけのイメージ」に分ける というもの.

前段に node:x.y.z-alpine を置いて node のバイナリを COPY する

FROM node:8.9.1-alpine as node
FROM ruby:2.4.2-alpine

RUN mkdir /opt
COPY --from=node /opt/yarn /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/
RUN ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
  && ln -s /opt/yarn/bin/yarn /usr/local/bin/yarnpkg

# snip.
  • pros
    • Node.js や yarn のバージョンは自由にできる
    • ビルドがはやい
    • FROM が2つ連続で書いてあると初心者かな?と見せかけてちゃんと動くのでかっこいい
  • cons
    • コンテナあたりの責務がすこし複雑になる
      • とはいえ開発環境では無理に単一責務化するより1つのコンテナで完結できたほうが便利 かも
      • Docker 流行る前は1つのマシンで開発しとったやん?

まとめ

  • Docker で複数言語のランタイム・コンパイラ等を利用したい場合,multi-stage build を利用すればいい
    • 擬似的に複数のベースイメージを利用できる
    • Dockerfile もシンプル
  • 開発環境では無理に Docker イメージのダイエットや単一責務化に拘る必要はなく,1つのコンテナで解決できると解決したい問題もシンプルになって便利
izumin5210
ʕ ◔ϖ◔ʔ < I'm a Rubyist.
https://blog.izum.in
wantedly
「シゴトでココロオドル」ためのビジネスSNS「Wantedly」の開発・運営をしています。
https://wantedlyinc.com/ja/presentations
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.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした