はじめに
Linux From Scratch に基づいて構築した手作りの Linux OS 内に Docker Engine を導入します。Linux From Scratch というものが、そもそもソースコードを使って一から作り上げていくシステムであるため、Docker も一から作り上げていくしかありません。この目的は何よりも Docker を理解することです。Docker の実用を目指すものではありません。
なお本文は Linux From Scratch に基づいた Linux OS を用いるため、カーネルソースの再ビルドという大きな作業が含まれます。Linux From Scratch を使うつもりのない方には、無縁の話に見えるかもしれません。ただしカーネルソースのビルド以降では、Docker バイナリを導入して環境設定を行う手順を示しています。主要 Linux ディストリビューションでは Docker がパッケージ提供されていると思いますが、そうでないディストリビューションをお使いの方は、Docker バイナリを用いることになるため、本文に示す手順が参考になるのではないかと思っています。
前提
Docker Engine をソースコードからビルドするためには、Docker そのものが必要になります。したがってまずは、公式に提供されている Docker Engine のバイナリをインストールすることから始めます。
ソースコードからのビルドにこだわる Linux From Scratch の流儀に従うのであれば、次に Docker ソースコードを使ってビルドすることになります。ただし Docker Engine バイナリの動作環境を作り出すだけでも、ひと苦労があります。さらにその環境が出来てしまえば Docker が動作するわけですから、そこで目的は一応達成していると見ることもできます。本文では、Docker バイナリを使った環境を構築するところまでの説明とします。
また本質的ではありませんが個人環境の都合により、すべての手順を VMware Workstation 上に構築した Linux From Scratch システム上で行うものとします。この理由は、ただ単に自由になる Linux マシンがなかったからです。VMware 仮想イメージに関する固有な情報は含まれませんので、純粋な Linux OS 上での処理操作と思って頂いて構いません。
PCおよび各ソフトウェアは、以下を利用します:
項目 | 内容 |
---|---|
ノートPC | Windows 7、64bit、amd、メモリ4GB (わりと貧弱) |
VMware Workstation | VMware Workstaion 10.0.7 build-2844087 (結構古い) |
Linux From Scratch | 最新 SVN systemd 版 SVN-20200528 (linux-5.6.15、gcc-10.1.0、systemd-245)、VMware 上の仮想イメージとして構築。64bit、メモリ1.5GB |
Beyond Linux From Scratch | 上記 SVN-20200528 に、種々ツール類をインストール (主要ツールについて、以降の手順にて説明) |
Docker | 最新 Docker Engine バイナリ: docker-19.03.10 |
手順
1. Linux カーネルの再ビルド
1.1. カーネル再ビルドの概略
Linux From Scratch を特に何も考えずに構築した場合、Docker 環境向けの Linux カーネル設定が十分になっていないはずです。カーネルソースのデフォルト設定では、ビルトインまたはモジュールビルド設定になっていないものがあり、しかも Docker 環境向けに必要となるものが多々あるからです。
Linux カーネルを再ビルドするには、Linux From Scratch を構築した際の「ホストシステム」を再度用いることになります。つまりもう一度「ホストシステム」からログインして、chroot
コマンドによって Linux From Scratch 環境内の Linux カーネルを再ビルドするものです。
Linux カーネルを再ビルドする方法は、確かに当初の「ホストシステム」を使うというものなのですが、はたして実際にその「ホストシステム」を、簡単に用意できるかどうか、再び必要になるとは思いもよらず、もう捨ててしまっているかもしれません。
このテーマはわりと現実的ではありながら、結構面倒なものです。ひょっとすると初めから Linux From Scratch を作り直すことの方が、一番の近道になるかもしれません。ここでは Docker 環境を構築することに比重を置くため、カーネル再ビルドの細かな情報は示しません。以下では再ビルド最中の、カーネルオプション設定メニューに焦点を当てて説明します。
1.2. 必要なカーネルオプションの確認
Docker 公式の github に、必要となるカーネル設定を確認するスクリプトがあります。check-config.sh というものです。これをダウンロードして実行します。
# wget https://raw.githubusercontent.com/moby/moby/master/contrib/check-config.sh
# sh ./check-config.sh
この出力はおよそ以下のようになります。
warning: /proc/config.gz does not exist, searching other paths for kernel config ...
info: reading kernel config from /boot/config-5.6.15 ...
Generally Necessary:
- cgroup hierarchy: nonexistent??
(see https://github.com/tianon/cgroupfs-mount)
- CONFIG_NAMESPACES: enabled
- CONFIG_NET_NS: enabled
- CONFIG_PID_NS: enabled
- CONFIG_IPC_NS: enabled
- CONFIG_UTS_NS: enabled
- CONFIG_CGROUPS: enabled
- CONFIG_CGROUP_CPUACCT: enabled
- CONFIG_CGROUP_DEVICE: missing
- CONFIG_CGROUP_FREEZER: enabled
- CONFIG_CGROUP_SCHED: enabled
- CONFIG_CPUSETS: enabled
- CONFIG_MEMCG: missing
- CONFIG_KEYS: enabled
- CONFIG_VETH: missing
- CONFIG_BRIDGE: missing
- CONFIG_BRIDGE_NETFILTER: missing
- CONFIG_NF_NAT_IPV4: missing
- CONFIG_IP_NF_FILTER: enabled
- CONFIG_IP_NF_TARGET_MASQUERADE: missing
- CONFIG_NETFILTER_XT_MATCH_ADDRTYPE: missing
- CONFIG_NETFILTER_XT_MATCH_CONNTRACK: enabled
- CONFIG_NETFILTER_XT_MATCH_IPVS: missing
- CONFIG_IP_NF_NAT: missing
- CONFIG_NF_NAT: enabled
- CONFIG_NF_NAT_NEEDED: missing
- CONFIG_POSIX_MQUEUE: enabled
Optional Features:
- CONFIG_USER_NS: missing
- CONFIG_SECCOMP: enabled
(以下略)
ここに示される Generally Necessary
(全般的に必須)の配下に示されるカーネルオプションが、Docker 環境において必要になります。enabled
であれば現状有効であり、missing
であれば、該当するカーネルモジュールが存在しないことを意味します。ちなみに Linux From Scratch 構築での Linux カーネルを、デフォルト設定にまかせてビルドしていた場合、この missing
がいくつも並ぶことになるはずです。missing
が存在するということは、一部の例外を除き、Docker を動作させることができません。
Optional Features
の配下に示されるカーネルオプションは、文字通りオプション的なものであり、必要に応じてビルド設定するものです。今後、必要に応じてビルドすることにしましょう。
なお Generally Necessary
の次行に、上では - cgroup hierarchy: nonexistent??
と示されています。これは cgroup マウントが存在しないか、あるいは適切なマウントになっていないことを意味します。この後に cgroup マウント設定をおこなうと、この表示は以下のように変わります。このような出力結果となるように、これより順に作業を進めていきます。
Generally Necessary:
- cgroup hierarchy: properly mounted [/sys/fs/cgroup]
1.3. カーネルオプションの再設定
Linux カーネルを再ビルドする処理を行います。Linux From Scratch ブックの Linux カーネルのビルド をもう一度実施するということです。そしてその手順の最中、make menuconfig
を実行した際のオプション設定メニューから、以降に示す必要オプションを有効にして Linux カーネルインストールまで行います。
以下は筆者が行ったカーネル設定です。最低でもこれらを設定しないと Docker 環境は動作しません。試行錯誤しながら進めてきた経緯があるため、誤りや不足があるかもしれません。気づき次第、加筆修正していこうと思います。
<カーネル設定項目> <オプション名>
General setup --->
[*] Control Group support ---> CONFIG_CGROUPS
[*] Memory controller (NEW) CONFIG_MEMCG
[*] Swap controller (NEW) CONFIG_MEMCG_SWAP
[*] Swap controller enabled by default (NEW) CONFIG_MEMCG_SWAP_ENABLED
[*] CPU controller --->
[*] Group scheduling for SCHED_OTHER CONFIG_CGROUP_SCHED
[*] CPU bandwidth provisioning for FAIR_GROUP_SCHED CONFIG_CFS_BANDWIDTH
[*] IO controller (NEW) CONFIG_BLK_CGROUP
[*] PIDs controller (NEW) CONFIG_CGROUP_PIDS
[*] Device controller (NEW) CONFIG_CGROUP_DEVICE
[*] Networking support ---> CONFIG_NET
Networking options --->
[*] Network packet filtering framework (Netfilter) ---> CONFIG_NETFILTER
[*] Advanced netfilter configuration CONFIG_NETFILTER_ADVANCED
<M> Bridged IP/ARP packets filtering (NEW) CONFIG_BRIDGE_NETFILTER
Core Netfilter Configuration --->
<M> LOG target support CONFIG_NETFILTER_XT_TARGET_LOG
*** Xtables matches ***
<M> "addrtype" address type match support CONFIG_NETFILTER_XT_MATCH_ADDRTYPE
IP: Netfilter Configuration --->
<*> Packet filtering CONFIG_IP_NF_FILTER
<M> iptables NAT support CONFIG_IP_NF_NAT
<M> MASQUERADE target support (NEW) CONFIG_IP_NF_TARGET_MASQUERADE
<M> 802.1d Ethernet Bridging CONFIG_BRIDGE
Device Drivers --->
[*] Network device support --->
[*] Network core driver support CONFIG_NET_CORE
<*> Virtual ethernet pair device CONFIG_VETH
File systems --->
<*> Overlay filesystem support CONFIG_OVERLAY_FS
(Linux-5.6.15 カーネル設定メニュー)
なおビルトインする設定「*
」と、モジュールビルドする設定「M
」は、あまり気にしていません。ビルドインできるものはできるだけ「*
」を選び、それができないものは「M
」にしています。
1.4. カーネルオプションの再確認
Linux カーネルビルドを終えたら、この新しい Linux カーネルに基づくシステムを起動し直します。そしてもう一度、前述の check-config.sh
を実行してみます。出力結果の多くが missing
でなく enabled
になっているはずです。
2. ツール類導入
そもそも Docker バイナリを導入するためのシステム要件は、公式ドキュメント Install Docker Engine from binaries の Prerequisites の項に説明されています。以下、それを簡易に訳して示します。
- 64ビット
- Linux カーネル 3.10 以降
- iptables 1.4 以降
- git 1.7 以降
- procps パッケージの ps
- xzutils 4.9 以降
- 適正にマウントされた cgroup 階層
Linux From Scratch により構築したばかりのシステムでも、すでに procps、xz は導入されています。64ビットやカーネル 3.10以降といったものは、それを選ぶしかありません。ここで必要となるのは git と iptables です。cgroup階層は大きなテーマですので後述します。
2.1. make-ca など
git
と iptables
のインストールに取り掛かる前に、おそらく必須であろう make-ca
を導入します。上に示したシステム要件では、あまりにも当たり前すぎて明記されていないのかもしれません。すべてを一から作り出す Linux From Scratch にとって、自分でビルドしてインストールしない限り、勝手に入ってくれるソフトウェアは一つもないということです。
make-ca
を入れるためには p11-kit
が必須、また make-ca
インストール作業時に wget
が必要となります。wget
は何かにつけ利用していくものですから、ここで導入します。make-ca
、p11-kit
、wget
はいずれも Beyond Linux From Scratch の各項に示されているインストール手順に従って導入します。
2.2. git
Beyond Linux From Scratch の git のページに従ってインストールします。依存パッケージは cURL くらいで十分でしょう。バージョンは本文執筆時点最新の git-2.27.0
です。
2.3. iptables
Beyond Linux From Scratch の iptables のページに従ってインストールします。依存パッケージは(面倒なので)特に入れません。バージョンは本文執筆時点最新の iptables-1.8.4
です。
iptables の設定ファイル /etc/systemd/scripts/iptables
は、上記ページ内の Personal Firewall の項に示される設定をそのまま使います。必要なときに設定を加えていきます。
3. Docker バイナリ類の入手、インストール
3.1. Docker バイナリの入手、インストール
Docker ドキュメントの Install static binaries ページに、バイナリのダウンロード URL https://download.docker.com/linux/static/stable/ が示されています。アーキテクチャー別のサブディレクトリが用意されているので、ここでは x86_64 配下の docker-19.03.10.tgz をダウンロードします。以降の操作はすべて root
ユーザーになって行います。(参考: 公式ドキュメント Install static binaries)
# wget https://download.docker.com/linux/static/stable/x86_64/docker-19.03.10.tgz
なお、この tarball は docker
というディレクトリ内にバイナリモジュールを含めてアーカイブされています。
# tar tf docker-19.03.10.tgz
docker/
docker/ctr
docker/docker
docker/containerd
docker/docker-proxy
docker/runc
docker/docker-init
docker/containerd-shim
docker/dockerd
そこで tarball を伸張(解凍)した上で、以下のように /usr/bin
にインストールすることにします。(参考: 公式ドキュメント Install static binaries)
# tar xf docker-19.03.10.tgz
# cp -p docker/* /usr/bin
3.2. systemd 用 Docker サービスの入手とインストール
上記のバイナリ tarball には、systemd 用のサービス設定ファイル docker.service
、docker.socket
が含まれていません。そこで公式 github からそれらを入手し、systemd サービスを収容するディレクトリ /lib/systemd/system
に移動させます。(参考: 公式ドキュメント Control Docker with systemd > Manually create the systemd unit files)
# wget https://raw.githubusercontent.com/moby/moby/master/contrib/init/systemd/docker.service
# wget https://raw.githubusercontent.com/moby/moby/master/contrib/init/systemd/docker.socket
# cp docker.{service,socket} /lib/systemd/system
4. もろもろの設定
4.1. グループ docker
の生成
グループ docker
を生成します。(参考:公式ドキュメント Post-installation steps for Linux)
# groupadd docker
4.2. systemd に関する設定
systemd 243 以降?に、cgroup は v2 なるものがデフォルト採用されることになったそうで、v2 のままにしていると Docker のさまざまな機能が動作しないようです。(入手情報元を失念につき、後日加筆予定)
そこでこれを解消するために、/boot/grub/grub.cfg
のブートオプションとして systemd.unified_cgroup_hierarchy=0
を加えます。
systemd.unified_cgroup_hierarchy=0
他サイトでは随所に GRUB_CMDLINE_LINUX_DEFAULT
にこの記述を加えて updated-grub
を実行する解消法が示されていますが、同じことです。Linux From Scratch では GRUB_CMDLINE_LINUX_DEFAULT
を利用していないので、grub.cfg
への直接指定とします。
なお具体例として筆者の /boot/grub/grub.cfg
は以下となっています。
# cat /boot/grub/grub.cfg
# Begin /boot/grub/grub.cfg
set default=0
set timeout=5
insmod ext2
set root=(hd0,1)
menuentry "GNU/Linux, Linux 5.6.15-lfs-20200528-systemd" {
linux /vmlinuz-5.6.15-lfs-20200528-systemd root=/dev/sda3 ro net.ifnames=0 biosdevname=0 systemd.unified_cgroup_hierarchy=0
}
linux
行の最後に systemd.unified_cgroup_hierarchy=0
を加えています。ちなみに筆者は /boot
を専用パーティションにマウントして利用しているため、上の linux
行は linux /boot/vmlinux...
ではなく linux /vmlinux...
という記述にしています。
修正後はシステムを再起動します。
4.3. cgroup
マウント
cgroup
(control group)のマウント設定を行います。/etc/fstab
ファイルに以下の記述を加えてマウントします。(参考: Docker の Github issue #2683)
# echo "cgroup /sys/fs/cgroup cgroup defaults 0 0" >> /etc/fstab
# mount -a
確認として以下を実行してみます。これはまず、カーネルモジュールが生成できているかどうかの確認です。
# less /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 4 1 1
cpu 3 1 1
cpuacct 3 1 1
blkio 5 1 1
memory 2 42 1
devices 6 29 1
freezer 7 1 1
だいたい上のような出力となるはずです。ここで重要なのは memory
や devices
の行が存在していることです。もしこれが出力されていないなら、前述の Linux カーネルオプションが適切にビルドされていないことを意味します。
次に以下を実行します。これは cgroup が適切にマウントされているかどうかの確認です。
# mount | grep ^cgroup
cgroup2 on /sys/fs/cgroup/unified type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
/sys/fs/cgroup
配下に各グループ(cpu
や devices
など)が存在しマウントされていることが確認できます。
5. 動作確認
5.1. docker デーモンの起動確認
まずは Docker デーモンの起動確認を行います。
systemd サービスである docker.service
を動作させるのでも良いですが、それよりも dockerd
を直接実行する方が、エラーメッセージを直接示してくれるので便利です。以下のようにして dockerd
コマンドを実行します。(参考: 公式ドキュメント Install static binaries)
# dockerd &
エラーが発生した場合は、出力の最終行に以下のようなメッセージが表示されます。
failed to start daemon: Error XXXXXXXXXX
こういったエラーメッセージがなければ、とりあえず正常起動したものと思われます。
5.2. docker info
による確認
docker info
を実行してみます。
# docker info
以下の出力が得られました。
Client:
Debug Mode: false
Server:
Containers: 5
Running: 0
Paused: 0
Stopped: 5
Images: 0
Server Version: 19.03.10
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc version: dc9208a3303feef5b3839f4323d9beb36df0a9dd
init version: fec3683
Security Options:
seccomp
Profile: default
Kernel Version: 5.6.15
Operating System: Linux From Scratch 20200528-systemd
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 1.432GiB
Name: DockerOnLFS
ID: PMIG:ONBA:YGH6:XMWF:GFGH:EW66:2GBR:A2E4:4D5X:TBWT:M2BL:KRVE
Docker Root Dir: /var/lib/docker
Debug Mode: false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine
WARNING: No cpu cfs quota support
WARNING: No cpu cfs period support
5.3. docker run hello-world
による確認
docker run hello-world
を実行してみます。(参考: 公式ドキュメント Install static binaries)
# docker run hello-world
以下の出力が得られました。
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:6a65f928fb91fcfbc963f7aa6d57c8eeb426ad9a20c7ee045538ef34847f44f1
Status: Downloaded newer image for hello-world:latest
INFO[2020-06-08T10:56:23.077433898+09:00] shim containerd-shim started
address="/containerd-shim/moby/fa9173d0b26b33c1075296abd578df5c0c088a61f5683214d60bbf5d1f12fa47/shim.sock" debug=false pid=504
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
INFO[2020-06-08T10:56:24.864625786+09:00] shim reaped
id=fa9173d0b26b33c1075296abd578df5c0c088a61f5683214d60bbf5d1f12fa47
INFO[2020-06-08T10:56:24.874963601+09:00] ignoring event
module=libcontainerd namespace=moby topic=/tasks/delete type="*events.TaskDelete"
この後も、公式ドキュメントの チュートリアル part2 を実行してみました。問題なく動作しています。
6. systemd の設定
6.1. dockerd
プロセスの kill
上記手順で実行した dockerd
プロセスを kill します。
# kill -SIGKILL $(pidof dockerd)
6.2. docker.service
の起動
docker.service
を起動します。(参考: 公式ドキュメント Control Docker with systemd > Start the Docker daemon)
# systemctl start docker.service
誤りがなければエラーなく、出力もないはずです。
必要に応じて systemctl enable docker.service
を実行しておき、システム起動時にこのサービスが自動起動するようにします。(参考: 公式ドキュメント Post-installation steps for Linux > Configure Docker to start on boot)
# systemctl enable docker.service
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /lib/systemd/system/docker.service.
念のためサービスのステータスを確認します。
# systemctl status docker.service
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset:
enabled)
Active: active (running) since Mon 2020-06-08 11:27:52 JST; 9min ago
TriggeredBy: ● docker.socket
Docs: https://docs.docker.com
Main PID: 269 (dockerd)
Tasks: 27 (limit: 1755)
Memory: 98.5M
CGroup: /system.slice/docker.service
tq269 /usr/bin/dockerd -H fd://
mq284 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
6月 08 11:27:50 DockerOnLFS dockerd[269]: time="2020-06-08T11:27:50.415279942+09:00" level=warning msg="Your kernel does not support cgroup blkio throttle.write_bps_device"
6月 08 11:27:50 DockerOnLFS dockerd[269]: time="2020-06-08T11:27:50.415333378+09:00" level=warning msg="Your kernel does not support cgroup blkio throttle.read_iops_device"
6月 08 11:27:50 DockerOnLFS dockerd[269]: time="2020-06-08T11:27:50.415385780+09:00" level=warning msg="Your kernel does not support cgroup blkio throttle.write_iops_device"
6月 08 11:27:50 DockerOnLFS dockerd[269]: time="2020-06-08T11:27:50.438407022+09:00" level=info msg="Loading containers: start."
6月 08 11:27:51 DockerOnLFS dockerd[269]: time="2020-06-08T11:27:51.864165833+09:00" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0/16. Daemon option --bip can be used to set a preferred IP address"
6月 08 11:27:51 DockerOnLFS dockerd[269]: time="2020-06-08T11:27:51.957543814+09:00" level=info msg="Loading containers: done."
6月 08 11:27:52 DockerOnLFS dockerd[269]: time="2020-06-08T11:27:52.591932252+09:00" level=info msg="Docker daemon" commit=9424aea graphdriver(s)=overlay2 version=19.03.10
6月 08 11:27:52 DockerOnLFS dockerd[269]: time="2020-06-08T11:27:52.663437436+09:00" level=info msg="Daemon has completed initialization"
6月 08 11:27:52 DockerOnLFS systemd[1]: Started Docker Application Container Engine.
6月 08 11:27:52 DockerOnLFS dockerd[269]: time="2020-06-08T11:27:52.950638271+09:00" level=info msg="API listen on /run/docker.sock"
ちなみに DockerOnLFS
はホスト名です。
また warning メッセージとして Your kernel does not support cgroup blkio throttle.write_bps_device
などが出ていますが、これは対応するカーネルモジュールがビルドされていないためです。今のところ Docker は動作していることと、それらのモジュールがどういったものか理解できていないため、今後理解していくことにして、今はそのまま無視しておくことにします。
本文は以上です。