経緯
Docker 上のコンテナにSamba4 をインストールし、samba-tool を実行した時に下記エラーが出力され、インストールに失敗してしまい、先に進めなくなってしまった経験があります。
......
ERROR(<class 'samba.provision.ProvisioningError'>): Provision failed - ProvisioningError: Your filesystem or build does not support posix ACLs, which s3fs requires. Try the mounting the filesystem with the 'acl' option.
File "/usr/lib/python2.7/dist-packages/samba/netcmd/domain.py", line 401, in run
use_rfc2307#use_rfc2307, skip_sysvolaclFalse)
File "/usr/lib/python2.7/dist-packages/samba/provision/__init__.py", line 2160, in provision
skip_sysvolacl=skip_sysvolacl)
File "/usr/lib/python2.7/dist-packages/samba/provision/__init__.py", line 1799, in provision_fill
names.domaindn, lp, use_ntvfs)
File "/usr/lib/python2.7/dist-packages/samba/provision/__init__.py", line 1551, in setsysvolacl
raise ProvisioningError("Your filesystem or build does not support posix ACLs, which s3fs requires. "
このエラーの原因を調べていくと、Docker デーモンで使われているストレージドライバaufs がacl に対応していないということで出るものでした。
ストレージドライバはDocker デーモンを起動するときに指定することが可能です。
今使っているDocker デーモンの設定をacl 対応のストレージドライバ(例:devicemapper) に変更するのが手っ取り早いですが、既に作成されているDocker コンテナがaufs の旨みを手放すのは嫌だ…。
うーんどうしたものか…f(´-`;) と考え、調べること数時間、以下の2 通りの方法で解決に挑戦してみることにしました。
- Docker in Docker で、子コンテナのストレージドライバをdevicemapper にする
- 現在起動しているDocker デーモンとは別のDocker デーモンを起動して、そちらのストレージドライバをdevicemapper にする
結論から先に...
個人的にはDocker in Docker で解決する方法をおすすめします。
別のDocker デーモンを起動する方法では他の人の環境へコンテナを配布するときに、もしその人が自分と同様にaufs な環境でDocker デーモンが起動していた場合、その人へ多くの構築手順を実施してしまうことになるためです。
Docker in Docker で解決する方法では、Dockerfile を配布して実行してもらうことで解決できます。
構成
今回本手順を実施した環境は下記のとおりです。
- 構成
Host OS Ubuntu 15.10 Docker version 1.9.1
Docker-in-Docker で解決する
概要
Docker in Docker を使用して子Docker デーモンのストレージドライバをdevicemapper にする方法について説明していきます。
Docker のbacking filesystem の要件として、devicemapper を利用する場合は、Docker デーモンを起動しているHost マシン上のbacking filesystem は一致している必要性がありません。
|Storage driver |Must match backing filesystem |
|---------------|------------------------------|
|overlay |No |
|aufs |No |
|btrfs |Yes |
|devicemapper |No |
|vfs* |No |
|zfs |Yes |
抜粋:
https://docs.docker.com/engine/userguide/storagedriver/selectadriver/
すなわち、devicemapper なbacking filesystem なDocker コンテナ環境がほしいのであれば、例えばaufs なDocker デーモンのコンテナ上にdevicemapper なDocker デーモンをを起動させることができるということです。
なお、この説明で使用されている最新の資材は以下のGitHub リポジトリにpush しておきました。
親コンテナの準備
Docker-in-Docker の親となるコンテナを作成して起動します。
FROM debian:jessie
RUN apt-get update
RUN apt-get install -y apt-transport-https
RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
RUN echo "deb https://apt.dockerproject.org/repo debian-jessie main" > /etc/apt/sources.list.d/docker.list
RUN apt-get update
RUN apt-get install -y docker-engine
RUN docker daemon &
COPY child /root/child
RUN cd /root/child && chmod u+x BuildChild.sh
EXPOSE 80
ENTRYPOINT ["/root/child/BuildChild.sh"]
子コンテナの準備
子コンテナを構築するためのスクリプトを作成します。
今回、子コンテナに関する資材は親コンテナのDockerfile の場所からchild
というディレクトリを作成してその中に作成していくことにします。
#!/bin/bash
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd ${SCRIPT_DIR}
CGROUP_DIR=/sys/fs/cgroup
[ -d ${CGROUP_DIR} ] || mkdir ${CGROUP_DIR}
mountpoint -q ${CGROUP_DIR} || {
mount -n -t tmpfs -o uid#0,gid=0,mode0755 cgroup ${CGROUP_DIR} || {
echo "Could not mount tmpsfs. Did you use --privileged?" >&2
exit 1
}
}
for SUBSYS in $(cut -d: -f2 /proc/1/cgroup); do
[ -d ${CGROUP}/${SUBSYS} ] || mkdir ${CGROUP}/${SUBSYS}
mountpoint -q ${CGROUP}/${SUBSYS} || {
mount -n -t cgroup -o ${SUBSYS} cgroup ${CGROUP}/${SUBSYS}
}
done
pushd /proc/self/fd
for FD in *; do
case "${FD}" in
[012])
;;
*)
eval exec "$FD>&-"
;;
esac
done
popd
ensure_loop(){
num="$1"
dev="/dev/loop$num"
if test -b "$dev"; then
echo "$dev is a usable loop device."
return 0
fi
echo "Attempting to create $dev for docker ..."
if ! mknod -m660 $dev b 7 $num; then
echo "Failed to create $dev!" 1>&2
return 3
fi
return 0
}
LOOP_A=$(losetup -f)
LOOP_A=${LOOP_A#/dev/loop}
LOOP_B=$(expr $LOOP_A + 1)
ensure_loop $LOOP_A || exit 1
ensure_loop $LOOP_B || exit 1
[ -f /var/run/docker.pid ] && {
pgrep docker > /dev/null && {
echo "Some docker daemons are already running." >&2
exit 1
} || {
rm -f /var/run/docker.pid
}
}
docker daemon --storage-driver="devicemapper" &
sleep 1
docker build -t="taro/docker-in-docker-child:latest" .
docker run --rm -p 0.0.0.0:80:80 --name docker-child -h docker-child -ti "taro/docker-in-docker-child" /bin/bash
子コンテナのDockerfile を作成します。
FROM debian:jessie
RUN apt-get update
RUN apt-get -y install nodejs
RUN ln -s /usr/bin/nodejs /usr/bin/node
COPY nodeprog /opt/nodeprog
EXPOSE 80
ENTRYPOINT ["node", "/opt/nodeprog/test.js"]
後ほど子コンテナとの疎通確認を行うときに使われるNodeJs テストプログラムを下記の用に作成しておきます。
var http = require("http");
console.log("#######################################################");
console.log("# Starting http server ... #");
console.log("#######################################################");
http.createServer(function (request, response) {
var ip = request.headers['x-forwarded-for'] || request.connection.remoteAddress;
console.log("Received a request from " + ip);
response.writeHead(200, {"Content-Type": "text/plain"});
response.end("This is a test response.\n");
}).listen(80, "0.0.0.0");
コンテナのbuild
子コンテナの準備もできたら、親コンテナのbuild を実行してみましょう。
$ sudo docker build -t="taro/docker-in-docker-parent" .
親コンテナを起動します。
親コンテナを起動するタイミングで子コンテナの構築が開始されます。
親コンテナを起動するときにHost OS のデバイスを特権で利用できるように--privilege
オプションをつけて起動するようにしてください。
このオプションがないと、Docker 内にDocker コンテナを作成することができません。
$ sudo docker run -p 127.0.0.1:80:80 --name docker-parent -h docker-parent --privileged -ti taro/docker-in-docker-parent:latest
# 初回は結構時間を要します...
...
#######################################################
# Starting http server ... #
#######################################################
docker が起動したら、curl コマンドを実行して、子コンテナ上のhttp サーバにリクエストを送信してみましょう。
以下のコマンドを実行することで、レスポンスが取得できれば成功です。
$ curl http://localhost/
This is a test response.
現在起動しているDocker デーモンとは別のDocker デーモンを起動して解決する
概要
現在起動しているDocker デーモンとは別のDocker デーモンを起動し、その別のデーモンのストレージドライバをdevicemapper なものにする方法です。
なお、この説明で使用されている最新の資材は以下のGitHub リポジトリにpush しておきました。
新しいDocker デーモン起動について
Docker デーモンを何も考えずにもうひとつ起動しようとすると、コマンドオプションの以下の物らが競合状態を起こすことになります。
- 競合を起こすDocker デーモンのオプション
-b, --bridge= Docker のネットワークbridge --exec-root= Docker デーモンの状態情報を格納するディレクトリ。デフォルトは"/var/run/docker" -g, --graph= Docker のイメージ情報が格納されるディレクトリ。デフォルトは"/var/lib/docker" -H, --host=[] Docker デーモンに接続するためのsocket。デフォルトは/var/run/docker.sock -p, --pidfile= Docker デーモンのプロセスID が格納されるファイル
Docker デーモンをもうひとつ起動する場合は、上記のオプションの値を現在起動しているDocker デーモンと重複しないようにしてください。
またこれらに加えて、今回はストレージドライバをdevicemapper にするため、-s, --storage-driver=
オプションを使用してdevicemapper を使うように指定します。
もうひとつDocker デーモンを起動する
今回起動するもうひとつのDocker デーモンの名前をaltdocker とします。
altdocker を起動する時にaltdocker 用のbridge インタフェースを作成するためにbridge-utils
パッケージをインストールしておきます。
$ sudo apt-get install bridge-utils
altdocker サービスを登録する
/lib/systemd/system
ディレクトリに下記2 つのスクリプトを作成します。
[Unit]
Description=Alt Docker Application Container Engine
Documentation=https://docs.docker.com
After=network.target altdocker.socket
Requires=altdocker.socket
[Service]
Type=simple
EnvironmentFile=-/etc/default/altdocker
ExecStart=/usr/local/bin/altdocker/altdocker-daemon
MountFlags=slave
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
[Install]
WantedBy=multi-user.target
[Unit]
Description=Alt Docker Socket for the API
PartOf=altdocker.service
[Socket]
ListenStream=/var/run/altdocker.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker
[Install]
WantedBy=sockets.target
ファイルを作成したら、systemctl daemon-reload
コマンドを実行してサービスを登録します。
$ sudo systemctl daemon-reload
altdocker-daemon コマンドを作成する
起動時にaltdocker のbridge ネットワークを作成するために、デーモンを起動するためのスクリプトを用意します。
今回はファイルを下記のように作成しました。
#!/bin/bash
if [ "$(id -u)" != "0" ]
then
echo "$0 must be run as root." >&2
exit 1
fi
if [ ! -r /etc/default/altdocker ]
then
echo "Failed to read file /etc/default/altdocker" >&2
exit 1
fi
. /etc/default/altdocker
function create_docker_bridge() {
if ! ip link show $DOCKER_BRIDGE > /dev/null 2>&1
then
brctl addbr $DOCKER_BRIDGE
ip addr add $DOCKER_BRIDGE_IP
fi
sleep 0.5
}
function inactivate_docker_bridge() {
if ip link show $DOCKER_BRIDGE > /dev/null 2>&1
then
ip link set dev $DOCKER_BRIDGE down
fi
}
trap inactivate_docker_bridge EXIT
create_docker_bridge
/usr/bin/docker daemon -H $DOCKER_SOCKET -b $DOCKER_BRIDGE -p $DOCKER_PID_FILE --exec-root $DOCKER_EXEC_ROOT --graph $DOCKER_GRAPH --storage-driver $DOCKER_STORAGE_DRIVER
EXIT_STATUS=$?
if [ $EXIT_STATUS -ne 0 ]
then
echo "Some error occured when running altdocker daemon" >&2
fi
exit $EXIT_STATUS
ファイルを作成したら権限を追加します。
$ sudo chown root:root /usr/share/bin/altdocker/altdocker-daemon
$ sudo chmod u+x /usr/share/bin/altdocker/altdocker-daemon
altdocker 管理コマンドを作成する
altdocker を管理するために/usr/local/bin/altdocker/altdocker
スクリプトを作成します。
$ sudo mkdir /usr/share/bin/altdocker/
$ sudo vim /usr/share/bin/altdocker/altdocker
今回は管理コマンドを以下のように作成しています。
#!/bin/bash
[ -r /etc/default/altdocker ] && . /etc/default/altdocker
docker -H ${DOCKER_SOCKET:-unix:///var/run/altdocker.sock} $@
権限を追加します。
$ sudo chown root:root /usr/share/bin/altdocker/altdocker
$ sudo chmod u+x /usr/share/bin/altdocker/altdocker
スクリプトに権限を追加したら、update-alternatives
でaltdocker
コマンドとして追加します。
$ sudo update-alternatives --install /usr/bin/altdocker altdocker /usr/local/bin/altdocker/altdocker 10
サービスを起動する
systemctl
コマンドを使用してaltdocker デーモンを起動します。
altdocker デーモンを起動するには下記コマンドを実行します。
$ sudo systemctl start altdocker
altdocker デーモンが起動したら、コマンドで動作確認をしてみましょう。
Storage Driver がdevicemapper
になっていることが確認できるはずです。
$ sudo altdocker info
Containers: 0
Images: 2
Server Version: 1.9.1
Storage Driver: devicemapper
Pool Name: docker-8:1-3804427-pool
......
altdocker にコンテナを作成してみます。
$ sudo altdocker run -ti debian /bin/bash
......
root@c6ed40e4c1c9:/#
問題なくコンテナが起動できれば、成功です。
参考
- Run docker inside a docker container?
- http://stackoverflow.com/questions/26239116/run-docker-inside-a-docker-container
- Running out of loopback devices
- https://github.com/jpetazzo/dind/issues/19
- Modprobe error while installing Docker on Ubuntu 14.04
- http://stackoverflow.com/questions/31904201/modprobe-error-while-installing-docker-on-ubuntu-14-04
- Select a storage driver
- https://docs.docker.com/engine/userguide/storagedriver/selectadriver/
- Is it possible to start multiple docker daemons on the same machine
- http://stackoverflow.com/questions/32334167/is-it-possible-to-start-multiple-docker-daemons-on-the-same-machine
- brctl でLinuxマシンをHUBにする
- http://www.usupi.org/sysad/162.html