Ubuntu/DebianのUSB起動のLIVE版でDockerを動作させる

前回の挫折を受けてChromeOSからUbuntuなどへの移行を考え、折角インストールしたChromeOSを消すのも、もったいないのでUSB版のLive環境をつかって継続してDockerを学ぶように方針変更をしたらまたトラブルが…という内容です。キッカケになった挫折に関しては以下参照で。

【参考】

uepon.hatenadiary.com

CentOSでも良かったのですが、USBメモリへの変更設定の保存などはできなかったので、今回はUbuntu18.02 LTSを使用することにしました。Debian系のLinuxはLive版でも設定やファイルをUSBメモリに保存できるのがいいですよね。ということでUbuntuの起動可能なメディアを作成します。ISOイメージがあれば以下のようなツールを使用することでUSBメモリ起動メディアが作成できます。

このツールを使用すると良いかなと思います。(ただし、イメージのダウンロードに関してはこのツール経由で行わない方が高速にできます)

unetbootin.github.io

作成したUSBメモリからPCを起動したら、ネットワークやキーボードマップなど設定を行いそのあとにDockerをインストールしていきます。公式ドキュメントもしっかりしているのでこちらを参考にしてもらえればと思います。

【公式ドキュメント】

docs.docker.com

コマンドだけをまとめると以下のような感じになります。

$ sudo apt-get remove docker docker-engine docker.io containerd runc
$ sudo apt-get update
$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
$ sudo docker run hello-world

これで完了するはずなのですが…また罠発生。インストール確認用の最後のテストコマンドであるsudo docker run hello-worldを実行してみると

$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete 
Digest: sha256:2557e3c07ed1e38f26e389462d03ed943586f744621577a99efb77324b0fe535
Status: Downloaded newer image for hello-world:latest
docker: Error response from daemon: error creating aufs mount to /var/lib/docker/aufs/mnt/b329231382bf8b24662da048500b454af804796ecdf11d92002f97186141984e-init: invalid argument.
See 'docker run --help'.

docker: Errorが発生します。このエラーはUbuntu 16.02 LTSDebian(最新)でも同じ状況になるようです。ググってもあまり情報がないことをみるとどうもUSB版のLive環境に固有の状況の模様。

エラーの内容からするとaufsというファイルシステムにコンテナイメージが生成できないよっていうエラーな感じです。

Wikipediaによれば

ja.wikipedia.org

aufs (AnotherUnionFS) は Linuxファイルシステムサービスであり、複数の異なるファイルシステム (ブランチと呼ばれる) のファイルやディレクトリ同士を透過的に重ねる (マージする) ことができる技術である。UnionFS を完全に書き換えるもので、信頼性とパフォーマンスの改善を狙いとしている。

とのことですが、このあとに

DebianUbuntu 系には aufs を利用し、ファイルシステムへの変更をメモリ ( tmpfs ) に保存するためのツール、fsprotec が入っている。これにより、実験的に設定ファイルを変更した状態でサーバを動作させ、再起動さえすれば元の状態に復元出来る、といった使い方も可能になる。コンテナ型仮想化技術であるDockerでは aufs をストレージドライバとして利用し、同技術の特徴である差分管理を実現している。

いろいろ調べてみるとDockerで使用するストレージドライバに問題があるような感じのようです。

さらにググってみたらこういう情報が!

stackoverflow.com

この情報を元にDockerの起動オプションでストレージドライバの変更を行います。Dockerの起動オプションを設定するファイルは/lib/systemd/system/docker.serviceとなるのでこれを編集して--storage-driver=vfsを追加することにします。

$ sudo vim /lib/systemd/system/docker.service

次の行を追加します。

…(略)...
ExecStart=/usr/bin/dockerd --storage-driver=vfs -H fd://
…(略)...

実際に追加したファイルは次の様になります。

[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
BindsTo=containerd.service
After=network-online.target firewalld.service containerd.service
Wants=network-online.target
Requires=docker.socket

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd --storage-driver=vfs -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229.
# Both the old, and new location are accepted by systemd 229 and up, so using the old location
# to make them work for either version of systemd.
StartLimitBurst=3

# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230.
# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make
# this option work for either version of systemd.
StartLimitInterval=60s

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this option.
TasksMax=infinity

# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes

# kill only the docker process, not all processes in the cgroup
KillMode=process

[Install]
WantedBy=multi-user.target

起動が終わったらDockerサービスをリスタート(PCの再起動でも可)してDockerのテストコマンドを実行すると

Dockerサービスのリスタートコマンドは以下の通りです。

$ sudo systemctl restart docker

【Dockerのテスト実行の結果】

$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete 
Digest: sha256:2557e3c07ed1e38f26e389462d03ed943586f744621577a99efb77324b0fe535
Status: Downloaded newer image for hello-world:latest

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/

見事に実行できました!ありがとう!

おわりに

Live環境でも通常OSと環境的には大差ないだろうと思っていたのですが、やっぱりそうでもなかったということでした。 しかし、はまりどころに良くあたる…これで2日つぶした。

(注意)ストレージドライバをaufsからvfsに変更するとDockerのパフォーマンスがかなり悪くなります。特にイメージをpullするときやbuildするときなどは顕著な様です。Live環境なのでそんな酷使することはないと思いますが、そのあたりを頭に入れて使用するのが良いようです。