【Docker】構築したrailsイメージの軽量化

## 概要
先日Railsアプリの開発環境にDockerを導入しました。
しかし[入門Dokcer](https://y-ohgi.com/introduction-docker/2_component/image/)を見てみると、どうやら記述方法次第や指定するイメージによって軽量化できるとのこと

## Dockerの設定記事
先日僕が書いて投稿した記事の中で出てくるDockerfileを参考に書いていくので、一度目を通しておいてください。
[作成したRailsアプリケーションでDocker環境構築](https://qiita.com/AK4747471/items/39ddacfde6a235103277)

## Dockerのイメージを軽量化するメリット
僕は業務未経験なので[参考書](https://www.amazon.co.jp/Docker-Kubernetes-%E5%AE%9F%E8%B7%B5%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-%E5%B1%B1%E7%94%B0-%E6%98%8E%E6%86%B2/dp/4297100339)からの受け売りですが・・・

Dockerイメージが大きくなってくるとこんな弊害が出てきます。
・イメージのビルド時間の増大(1.8GBアプリで60秒くらいはかかります。なかなかイライラです。)
・イメージをDocker hubやECRといったレジストリにpushするときにかかる時間の増大
・コンテナを実行したいホスト・ノードへのイメージダウンロード時間の増大

これにより発生する課題としては
・Kubernatesなどのコンテナクラスタを構成するNodeのディスク消費
・CI時間の増大
・トライアンドエラーのしにくさ、生産性の低下
・オートスケールでコンテナがサービスインされるまでの時間が長くなる(Nodeにイメージが存在しない場合は新たにダウンロードするため)

と行ったところです。勉強不足でちょいちょい意味わかっていないです・・。
要は、開発環境の差異によるトラブル(時間がとられる)っていうのを防ぐためにDockerを入れたのに、その都度環境構築するという特性により容量がでかいと時間がかかるよってことかと思います。


## 軽量化の手法
前置きはこの辺りにして、軽量化していこうかと思います。
軽量化するにはイメージ名を変えるだけでよかったりします。
slimとかstretchとか色々あるんですが、今回はalpineを使います。

## Alpine Linuxとは
BusyBoxをベースに作られた「セキュリティ、シンプルさ、リソース効率を重視するパワーユーザー」向けに設計されたもの・・らしいです。
最大の特徴は**apk**というパッケージマネージャが使えるというところ。

Dockerfileでは1行1行開発環境の挙動を記述していくわけですが、冗長化しやすいです。
そこでapkという軽量で手軽なパッケージマネージャがあると、開発環境は構築しやすいし、後から見たときにどんな環境でアプリケーションが動いているのか確認しやすい、というところみたいですね。


このalpineを使うことにより、前回まで使っていたapt-getといった記述は使えなくなります。
このalpineとかいうやつかなり強力で、もともと構築していたrubyのイメージが**460MB**くらいだったのですが、ruby:alpineにしただけで**45MB**まで減りました。
1/10って・・・・


## 最終コード
最終的にこんな感じになりました。
詳細はコメントアウトを見てください

```Dockerfile:Dockerfile
# ruby2.5.1のalpineを指定
FROM ruby:2.5.1-alpine

# apk --update --no-cache add でインストールするパッケージを指定します。最低限の構成を目指しました。
RUN apk --update --no-cache add tzdata libxml2-dev curl-dev make gcc libc-dev g++ mariadb-dev linux-headers nodejs && \
    mkdir /workdir

WORKDIR /workdir

ADD Gemfile /workdir/Gemfile
ADD Gemfile.lock /workdir/Gemfile.lock

ENV BUNDLER_VERSION 2.0.2
# && \で改行していくことにより、より軽量になるそうです
RUN gem install bundler && \
    bundle install && \
# Warningがでたのでここでupdateを一度入れています。
    apk update && \
# 上で指定したパッケージの中、開発環境を構築したら不要になるファイルたちです。apk delで消しちゃいます。
    apk del libxml2-dev curl-dev make gcc libc-dev g++ linux-headers

ADD . /workdir
# 構築したDockerの中に入って、大きく容量をとっているディレクトリを調べました。容量が大きく不要であるcacheファイルは構築後削除しています。
RUN rm -rf /usr/local/bundle/cache/* /workdir/vendor/bundle/cache/*
```

## apk --update --no-cache add
従来は --update add でインストールした後に rm -rf /var/cache/apk/* で
不要なゴミファイルを削除していましたが、--no-cacheでOKです。

## apk del
全てのインストールが終わったら、不要なファイルは削除するように指示しています。
しかしtzdata、mariadb-dev、nodejsは削除していません。
tzdata：参考にした記事にこれがないとサーバー構築でエラーが出ると書いてあったので残しています。
mariadb-dev：mysqlをDBに使いたいので残しています。
nodejs：railsはnodejsがないと動きません。

## rm -rf /usr/local/bundle/cache/* ~~~
ここまでやるかは個人次第ですかね・・・
費用対効果も薄いです。確か50MBくらい削除できたかな？
やり方は下記記事を見てください。
[さらにダイエット](https://qiita.com/baban/items/99877f9b3065c4cf3d50#%E3%81%95%E3%82%89%E3%81%AB%E3%83%80%E3%82%A4%E3%82%A8%E3%83%83%E3%83%88)

## 確認してみよう！
```
# イメージを構築
docker-compose build

# イメージ一覧を表示。ここのSIZEカラムが容量です。
docker images
```

僕の場合は1.9GBから1.2GBくらいまでは落ちました。
ほんとはもっと落としたかったんですが・・・・。
ここから先は**マルチステージビルド**なる技も使わないといけないとか・・・

## まとめ
alpineを使えばごく簡単にイメージの軽量化を測れるのではないかなと思います。
しかし少なからずデメリットもあります。
冒頭で、環境の構築状況を読み取りやすくしたいがためにalpineのapkパッケージが人気だよと書いたんですが、軽量化に走ると可読性が下がります。
そんなトレードオフなところもあるので、ざくっとやってざくっとイメージを減らすくらいは常に意識しといてそんはないかなと思いますぞ:alien:

## 参考記事

[Alpine Linux で軽量な Docker イメージを作る](https://qiita.com/pottava/items/970d7b5cda565b995fe7)
[Alpine Linux使ってみた](https://qiita.com/tukiyo3/items/247f853c81bf00e82c11)
[apkコマンドのhelpを日本語訳した](http://mokicks.hatenablog.com/entry/2018/12/24/002815)

## 補足
この方の記事では、2.19GBGBのイメージファイルを464MBまで落としてます。
参考までに。

[RailsのDockerイメージを一番小さくする方法](https://qiita.com/baban/items/99877f9b3065c4cf3d50)

ただ、軽量化により大幅に削減した後は時間当たりの軽量化量が悪くなるといった側面もあるみたいです。
ざくっと減らしたらその後の費用対効果はあんまりよくないってことみたいですね。