Docker の image を軽くする
Docker に無事既存の web アプリを載せることはできたものの、めちゃ重い。web アプリのイメージだけで 5GB もあった。
イメージを軽くするには、ベースのイメージを alpine Linux なるものに変えると軽くなるということだったので、変えてみた。
# Node.js & Yarn FROM node:10.15.1-alpine as node RUN apk add --no-cache --virtual .ruby-builddeps bash curl \ && curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.22.4 # Ruby & Bundler & postgresql-client FROM ruby:2.6.5-alpine COPY --from=node /usr/local/bin/node /usr/local/bin/node COPY --from=node /opt/yarn-* /opt/yarn RUN ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn ENV RUN_PACKAGES="nodejs postgresql postgresql-dev tzdata" \ DEV_PACKAGES="build-base curl-dev gcc libc-dev libxml2-dev linux-headers make" \ CHROME_PACKAGES="chromium chromium-chromedriver dbus mesa-dri-swrast ttf-freefont udev wait4ports xorg-server xvfb zlib-dev" RUN apk --update --no-cache add ${RUN_PACKAGES} \ && apk --update --no-cache add ${DEV_PACKAGES} \ && cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \ && apk del --purge tzdata ENV ENTRYKIT_VERSION 0.4.0 # Entrykit & ChromeDriver RUN wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \ && tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \ && rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \ && mv entrykit /bin/entrykit \ && chmod +x /bin/entrykit \ && entrykit --symlink \ && apk --update --no-cache add ${CHROME_PACKAGES} # 作業ディレクトリの作成、設定 RUN mkdir /ketsuatsu_app WORKDIR /ketsuatsu_app # bundle install & Delete Unnecessary Packages_and_Cache & Copy File COPY Gemfile /ketsuatsu_app/Gemfile COPY Gemfile.lock /ketsuatsu_app/Gemfile.lock RUN gem install bundler --version 2.1.2 \ && bundle install --path vendor/bundle \ && find vendor/bundle/ruby -path '*/gems/*/ext/*/Makefile' -exec dirname {} \; | xargs -n1 -P$(nproc) -I{} make -C {} clean \ && apk del ${DEV_PACKAGES} \ && apk del .ruby-builddeps COPY . /ketsuatsu_app ENTRYPOINT [ \ "prehook", "bundle install -j3 --path vendor/bundle", "--", \ "prehook", "ruby -v", "--", \ "prehook", "node -v", "--" \ ]
# Node.js & Yarn FROM node:10.15.1-alpine as node RUN apk add --no-cache --virtual .ruby-builddeps bash curl \ && curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.22.4
最初に Node.js 10.15.1 のベースイメージを読み込み、nodeという名前をイメージにつけている。
次に alpine では使えない bash と curl コマンドをイメージに加えてコマンドを使えるようにして、
Yarn 1.22.4 をインストールしている。
このイメージには、下記のフォルダに Node.js と Yarn がインストールされている。
/usr/local/bin/node /opt/yarn-v1.21.1
# Ruby & Bundler & postgresql-client FROM ruby:2.6.5-alpine COPY --from=node /usr/local/bin/node /usr/local/bin/node COPY --from=node /opt/yarn-* /opt/yarn RUN ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn
次に、ruby 2.6.5 のベースイメージを読み込み、先ほど作ったnodeイメージの Node.js と Yarn を ruby のイメージにコピーしている。
そして、ln -s コマンドでシンボリックリンクを作成している。
Node.js は /usr/local/bin/node に、 Yarn は /usr/local/bin/yarn にコピーされたことになる。
ENV RUN_PACKAGES="nodejs postgresql postgresql-dev tzdata" \ DEV_PACKAGES="build-base curl-dev gcc libc-dev libxml2-dev linux-headers make" \ CHROME_PACKAGES="chromium chromium-chromedriver dbus mesa-dri-swrast ttf-freefont udev wait4ports xorg-server xvfb zlib-dev"
複数のパッケージを変数に格納してあとで使いやすくしている。
RUN apk --update --no-cache add ${RUN_PACKAGES} \ && apk --update --no-cache add ${DEV_PACKAGES} \ && cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \ && apk del --purge tzdata
先ほど変数に格納した必要なパッケージをインストール。
RUN と DEV に分けているのは、 RUN の方はそのまま Docker 上で使用するが、 DEV の方は環境構築が終わったら必要なくなるパッケージなので、あとで削除するためにこの分け方をしている。
apk del --purge パッケージ名 でそのパッケージに関連するファイルもまとめて削除できる。
# Entrykit & ChromeDriver RUN wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \ && tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \ && rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \ && mv entrykit /bin/entrykit \ && chmod +x /bin/entrykit \ && entrykit --symlink \ && apk --update --no-cache add ${CHROME_PACKAGES}
Entrykit と ChromeDriver のインストール。
前回は ChromeDriver のインストールのために直接 URL を叩いていたが、 apt-get から apk に変わったとき、ChromeDriver インストール時に必要になる apt-key というコマンドが使用できないことが判明。これを何に置き換えれば良いかわからず苦戦したが、別の記事で必要なパッケージを読み込む方法があったので、そちらを採用した。
# bundle install & Delete Unnecessary Packages_and_Cache & Copy File COPY Gemfile /ketsuatsu_app/Gemfile COPY Gemfile.lock /ketsuatsu_app/Gemfile.lock RUN gem install bundler --version 2.1.2 \ && bundle install --path vendor/bundle \ && find vendor/bundle/ruby -path '*/gems/*/ext/*/Makefile' -exec dirname {} \; | xargs -n1 -P$(nproc) -I{} make -C {} clean \ && apk del ${DEV_PACKAGES} \ COPY . /ketsuatsu_app
この箇所はあまり変わりないが、最後にファイルコピーの前に必要ないパッケージとキャッシュを削除している。
また、ここでは割愛するが、docker-compose.yml の方でもdbのイメージに alpine を採用している。
以上でビルドした結果、5GBあったのが2.5GBくらいまで削減できた。
しかし、エラーが多数発生。今回はローカル環境で作った既存アプリを dockerに乗せて軽量化しようとしたが、Docker のイメージのほとんどは Ubuntu 環境であり、ローカル(Mac OS / つまり unix)環境のものを乗せようとするとうまく表示されないとのこと。
やるなら、最初から Docker 上で作るときのみ alpine は使った方が良さそう。
参考記事
Dockerのマルチステージビルドを使う - Qiita
Docker イメージサイズを抑えながら Ruby on Rails + PostgreSQL の開発環境を作成する - bitA Tech Blog
Rails 6.0 × MySQL8でDocker環境構築(Alpineベース) - Qiita
RailsのDockerイメージを一番小さくする方法 - Qiita