Dockerfileのapt-getでよく見るあの呪文について

DebianやUbuntuでよく使うapt-getですが、Dockerのイメージをビルドするときに一連のコマンドよく見ると思います。 今回はそんなDockerfileにおけるapt-getの呪文を紐解いていきます。
2022.12.01

apt-getの呪文

Dockerfile内でapt-getを使ってアプリケーションをインストーするするとき以下のようなものをよく見ると思います。

apt-getの呪文

RUN apt-get update && apt-get install -y \
    xxx \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

公式ドキュメントによればこれらはDockerのイメージサイズを小さくする効果があるようです。 今回はこれを紐解いて行こうと思います。

比較

疑問が多いのは以下の部分だと思います。

  • apt-get clean
  • rm -rf /var/lib/apt/lists/*

これらをそれぞれない状態にしてイメージサイズを比較してみます。 今回はUbuntu:22.04をもとにNginxをインストールします。

イメージサイズ
全部あり 136.72MB
apt-get cleanだけ実行 176.54MB
rm -rf /var/lib/apt/lists/*だけ実行 136.72MB
両方なし 176.54MB

確かに、全部ありはイメージサイズが小さいです。 rm -rf /var/lib/apt/lists/*も同じ値になっています。

apt-get cleanだけを行ったものは両方ない場合と同じになっています。

それぞれのコマンドの意味を見ていきます。

apt-get clean

apt-get cleanはaptがダウンロードしてきたDebianパッケージファイルを削除するためのコマンドです。 ダウンロードされてきたファイルは/var/cache/apt以下に一時的に保存され、再度インストールする際はダウンロードせずこちらが使用されます。

ではなぜこのコマンドはイメージサイズに影響を与えなかったのでしょうか?

この秘密は/etc/apt/apt.conf.d/docker-cleanにあります。 ここにはaptの設定が書いてあり、内容は以下のとおりです。

docker-clean

DPkg::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };
APT::Update::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };
Dir::Cache::pkgcache "";
Dir::Cache::srcpkgcache "";

ここでaptの設定を行っており、インストール後に自動的にapt-get clean相当の処理が走るようになっています。 つまりは、RUN命令に含めなくとも自動でダウンロードしたパッケージを削除してくれるということです。 UbuntuやDebianの公式イメージではこの設定がすでに行われています。

Dockerfile

FROM ubuntu:22.04

RUN rm /etc/apt/apt.conf.d/docker-clean
RUN apt-get update && apt-get install -y \
    nginx

実際上記のようなDockerfileを作成しイメージをビルドするとイメージサイズは275.04MBととなります。

キャッシュの確認

/# ls -la  /var/cache/apt/archives | head
total 17224
drwxr-xr-x. 1 root root     4096 Dec  1 07:59 .
drwxr-xr-x. 1 root root       65 Dec  1 07:59 ..
-rw-r--r--. 1 root root    29096 Mar 24  2022 fontconfig-config_2.13.1-4.2ubuntu5_all.deb
-rw-r--r--. 1 root root  1041300 Feb 22  2021 fonts-dejavu-core_2.37-2build1_all.deb
-rw-r--r--. 1 root root  1070038 Mar 24  2022 iproute2_5.15.0-1ubuntu2_amd64.deb
-rw-r--r--. 1 root root    22768 Mar 25  2022 libatm1_1%3a2.5.1-4build2_amd64.deb
-rw-r--r--. 1 root root   139806 Jan 25  2022 libbpf0_1%3a0.5.0-1_amd64.deb
-rw-r--r--. 1 root root   315164 Mar 24  2022 libbrotli1_1.0.9-2build6_amd64.deb
-rw-r--r--. 1 root root    44760 Jan 25  2022 libbsd0_0.11.5-1_amd64.deb

実際/var/cache/apt/以下に.debファイルが残されています。

では、このapt-get cleanは不要でしょうか? それは使用するイメージによります。 ダウンロードしたパッケージの自動クリーンが入っていないイメージを使用する場合はあったほうが良いでしょう。

rm -rf /var/lib/apt/lists/*

/var/lib/apt/lists以下を削除していますがここには何が入っているのでしょうか?

ここにはapt-get updateで取得したリポジトリのパッケージのリストが保存されています。

パッケージのリスト

/# du -sh /var/lib/apt/lists 
39M     /var/lib/apt/lists
# ls -al /var/lib/apt/lists/ | head
total 39024
drwxr-xr-x. 1 root root     4096 Dec  1 07:58 .
drwxr-xr-x. 1 root root       42 Dec  1 07:59 ..
-rw-r--r--. 1 root root    99847 Dec  1 07:02 archive.ubuntu.com_ubuntu_dists_jammy-backports_InRelease
-rw-r--r--. 1 root root     4893 Aug 24 19:11 archive.ubuntu.com_ubuntu_dists_jammy-backports_main_binary-amd64_Packages.lz4
-rw-r--r--. 1 root root    11115 Nov 30 16:18 archive.ubuntu.com_ubuntu_dists_jammy-backports_universe_binary-amd64_Packages.lz4
-rw-r--r--. 1 root root   114016 Dec  1 07:02 archive.ubuntu.com_ubuntu_dists_jammy-updates_InRelease
-rw-r--r--. 1 root root  1566039 Dec  1 02:50 archive.ubuntu.com_ubuntu_dists_jammy-updates_main_binary-amd64_Packages.lz4
-rw-r--r--. 1 root root    12559 Nov 18 01:25 archive.ubuntu.com_ubuntu_dists_jammy-updates_multiverse_binary-amd64_Packages.lz4
-rw-r--r--. 1 root root  1024871 Dec  1 01:17 archive.ubuntu.com_ubuntu_dists_jammy-updates_restricted_binary-amd64_Packages.lz4

このディレクトリだけで39MBもあるので削除しておいたほうが無難でしょう。 問題としてはDockerfilen内で複数回apt-updateを走らせるたびに毎回取得が入ってしまいます。 ですが、よほど高速にビルドしたいという局面以外は毎回削除することが安全だと思っています。

最後に

Dockerにおけるapt-getの利用方法としてよく練られたコマンドだと思いました。 DebianやUbuntuの公式イメージは自動でapt-get cleanしてくれるのでありがたいですね。

付録

以下に今回使用したDockerファイルを載せておきます。

両方あり

FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    nginx \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

apt-get clean だけ

FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    nginx \
    && apt-get clean

rm -rf /var/lib/apt/lists/*だけ

FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    nginx \
    && rm -rf /var/lib/apt/lists/*

両方なし

FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    nginx