巷で流行り始めてるAlpine LinuxをつかったDockerコンテナ作成を自分のアプリで試してみた時の覚書です
どうせ 16.04 LTSに切り替えるのなら、いっそのことAlpine Linuxでも使ってやろうという試みです
【結果】70%の削減
中身は RubyとUnicornベースのWebアプリ, PostgreSQL, Nginx なDockerコンテナです
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hoge alpine 29dfb2fd387f 3 hours ago 174.1 MB
hoge ubuntu14.04 7a4e83184274 4 days ago 582.2 MB
約400MB(70%)の削減となりました
やったこと
*-dev パッケージ
Gemなんかをインストールする際、Nativeなbinaryを作るためにヘッダファイルが入っている *-dev なパッケージを入れざるを得なくなります
ですが、これらを入れると+150MBくらい簡単にサイズが増えますので、それを回避します
RUN apk add --update --no-cache --virtual=.build-deps ruby-dev postgresql-dev \
make g++ musl-dev libffi-dev \
linux-headers \
&& gem install unicorn --no-rdoc --no-ri \
&& (cd APP_ROOT ; bundle install --path vendor/bundle) \
&& apk del .build-deps
こんな感じです
-
apk add --no-cache
でrm -rf /var/cache/apk/*
の手間をなくします -
&&
でつなげ、この後のapk del
まで単一のRUN(=レイヤー)まで一気に実行します -
apk --virtual=NAME
を使ってラベル付けをしておき、実行には必要のない *-dev パッケージや make などを最後にapk del NAME
で一括削除します
gosu
&&
等を駆使すればレイヤーを軽くできます
しかし、アプリを一般ユーザで実行する場合はどうでしょうか
例えばこんな場合です (ちょっと端折ってます)
RUN apk add ruby \
&& apk add --virtual=.build-deps ruby-dev postgresql-dev \
&& gem install pg
USER foo
RUN git clone https://github.com/hoge/app.git /home/foo/app \
&& cd /home/foo/app \
&& bundle install
foo
に切り替えるタイミングで一旦RUNが切れてしまいレイヤーがコミットされます
そのためpostgresql-dev等が残ったままになり、後半で apk del postgresql-dev
とかやってもサイズ縮小にはなんら寄与しません
su - USER -c ""
の出番??
あーエスケープが面倒だからお呼びしたくないです
で gosu を使う
su -c
のエスケープ問題を gosu で解決しましょう
インストールがちょっと(だいぶ)面倒ですが、こんな感じになります
RUN apk --virtual=.gosu-deps add curl gnupg openssl \
&& curl -o /usr/local/bin/gosu -sL "https://github.com/tianon/gosu/releases/download/1.8/gosu-amd64" \
&& curl -o /usr/local/bin/gosu.asc -sL "https://github.com/tianon/gosu/releases/download/1.8/gosu-amd64.asc" \
&& export GNUPGHOME="$(mktemp -d)" \
&& gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
&& gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
&& rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \
&& chmod +x /usr/local/bin/gosu \
&& gosu nobody true \
#--- ここまででgosuインストール
&& apk add ruby \
&& apk add --virtual=.build-deps ruby-dev postgresql-dev \
&& gem install pg
#--- さっきはここで USER foo としてた
&& gosu foo git clone https://github.com/hoge/app.git /home/foo/app \
&& cd /home/foo/app \
&& gosu foo bundle install \
&& apk del .gosu-deps .build-deps
これでワンレイヤーに収めることができます
Timezone
我々はJSTです
RUN apk --update --no-cache add tzdata \
&& cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
&& apk del tzdata
Edge(最新版Alpine Linux)のapkを借りてくる
Stableなバージョンのapkはちょっと古い時があります
$ docker run --rm -it alpine apk --update search -x nginx
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/community/x86_64/APKINDEX.tar.gz
nginx-1.8.1-r0
そんな時は apk --repository
オプションで edge を参照するようにしてみましょう
$ docker run --rm -it alpine apk --update --repository http://dl-cdn.alpinelinux.org/alpine/edge/main/ search -x nginx
fetch http://dl-cdn.alpinelinux.org/alpine/edge/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/community/x86_64/APKINDEX.tar.gz
nginx-1.9.14-r1
無論インストールはこんな感じです
RUN apk --update --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/main/ add nginx
参考資料
- Alpine Linux で Docker イメージを劇的に小さくする - Qiita
- Alpine Linux で軽量な Docker イメージを作る - Qiita
- Alpine Linux でタイムゾーンを変更する - Qiita
- tianon/gosu: Simple Go-based setuid+setgid+setgroups+exec
- [(より)小さいDockerイメージを作ろう - Ian Lewis](https://www.ianlewis.org/jp/small-docker-images]
あとがき
Alpine Linux、結構くる希ガス
EventMachineとかもコンパイル方法掲載してるくらいですしおすし
Ubuntu関係無かったですね
apk と gosu を駆使してワンレイヤーに押し込むというテクニックに終始してしまいました