ベースのコンテナイメージはどうやって作成されているか?

コンテナイメージを作成する方法といえば Dockerfile。 いつも FROM に何かしらのディストリビューションを指定してるけど、

FROM ubuntu
RUN ...

そもそも FROM に指定してるベースイメージはどうやって作成されてるのか?を調べた。

CentOS

scratch イメージ(空のイメージ)を FROM にして、centos-7-x86_64-docker.tar.xz という "/" 以降全部含むファイルをルートにぶち込む。これでディストリビューションのファイル全部格納できるなガハハ。

FROM scratch
ADD centos-7-x86_64-docker.tar.xz /

LABEL \
    org.label-schema.schema-version="1.0" \
    org.label-schema.name="CentOS Base Image" \
    org.label-schema.vendor="CentOS" \
    org.label-schema.license="GPLv2" \
    org.label-schema.build-date="20201113" \
    org.opencontainers.image.title="CentOS Base Image" \
    org.opencontainers.image.vendor="CentOS" \
    org.opencontainers.image.licenses="GPL-2.0-only" \
    org.opencontainers.image.created="2020-11-13 00:00:00+00:00"

CMD ["/bin/bash"]

centos:7 Dockerfile

いやいやちょっと待てと。じゃあその tar.xz はどうやって作ったのと。コンテナイメージのビルド元(sig-cloud-instance-build)を辿って README やスクリプトを読むと、livemedia-creator コマンドという既存のツールを使っているらしい。
参考 sig-cloud-instance-build/docker/containerbuild.sh
参考 RHEL7 インストールガイド 28.2. 自動でのディスクイメージへのインストール

なお、イメージに関する設定(例えばユーザが何で~とか)は kickstart ファイルでやるらしい。ここまでくると聞いたことのある仕組みで馴染みがあって安心感がある。
参考 sig-cloud-instance-build centos-7-x86_64.ks

alpine

scratch イメージ(空のイメージ)を FROM にして、alpine-minirootfs-3.13.5-x86_64.tar.gz という "/" 以降全部含むファイルをルートにぶち込む。これでディストリビューションのファイル全部格納できるなガハハ。

FROM scratch
ADD alpine-minirootfs-3.13.5-x86_64.tar.gz /
CMD ["/bin/sh"]

alpine:3 Dockerfile

じゃあその tar.gz はどうやって作ったのと。docker-alpine/docs/build.adoc を読む限り、alpine では lua スクリプトで alpine のリポジトリから該当の(今回だと alpine-minirootfs-3.13.5-x86_64.tar.gz)アーカイブをダウンロードしてるっぽい。じゃあそのアーカイブはどうやって…と思ったけど、これ以上調べても alpine ディストリビューションのリリースフローの調査になるのでやめた。

Ubuntu

sctratch イメージ(空のイメージ)を FROM にして、ubuntu-focal-core-cloudimg-amd64-root.tar.gz(これ自体は cloudimg というOpenStack とかでも使うファイル) をルートにぶち込む。このノリ何度目だよ!と思ったけど、Ubuntu は docker 固有の調整をしてる様子。

FROM scratch
ADD ubuntu-focal-core-cloudimg-amd64-root.tar.gz /

# a few minor docker-specific tweaks
# see https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap
RUN set -xe \
    \
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L40-L48
    && echo '#!/bin/sh' > /usr/sbin/policy-rc.d \
    && echo 'exit 101' >> /usr/sbin/policy-rc.d \
    && chmod +x /usr/sbin/policy-rc.d \
    \
# https://github.com/docker/docker/blob/9a9fc01af8fb5d98b8eec0740716226fadb3735c/contrib/mkimage/debootstrap#L54-L56
    && dpkg-divert --local --rename --add /sbin/initctl \
    && cp -a /usr/sbin/policy-rc.d /sbin/initctl \
    && sed -i 's/^exit.*/exit 0/' /sbin/initctl \
    \
...

focal Dockerfile

まあでも他のディストリビューションとも大差ないので省略。

終わりに

どのディストリビューションも、シェルスクリプトみたいなもんで 1. Dockerfile を作る 2. ルートに突っ込むアーカイブを(取ってくる or 作る) を自動化してコンテナイメージを作ってた。 コンテナイメージ作るなら Dockerfile …という仕組みは、調査した範囲では、どのディストリビューションでも共通で安心した。調べた限りでは yum リポジトリなどもホストと共通なので、つまりコンテナも(ユーザ空間は)ホストと同様の仕組みで動作している。

がしかし distroless などは bazel を使ってビルドするとか見た気がするので、例外はある気がする。また今度調べる。