SIer だけど技術やりたいブログ

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

コンテナイメージを作成する方法といえば 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 リポジトリもホストと同一の URL を指していたので、つまりコンテナは(ユーザ空間は)ホストと同様の仕組みで動作している。

がしかしコンテナイメージの作成で distroless などは bazel を使ってビルドするとか見た気がするので、例外はある気がする。また今度調べる。