Dockerでのデータの扱い
Dockerで永続データを扱う際にいろいろな方法があることが勉強できました。
いろいろな方法論がありなかなかに難しい分野ではありますが、勉強し甲斐のある部分でもあります。
今回はデータボリュームコンテナ(以下データコンテナ)についてを書こうと思います。
テストのホスト環境はGUN/LInux debian8
で、docker
コマンドはsudo
なしで実行できるように設定している状況です。
最小限環境コンテナ
データコンテナはなんのプロセスも動かす必要がないので、できるだけ最小限環境なコンテナであって欲しいです。
今回はBusyboxというツールのイメージを使ったコンテナをデータコンテナとして仕上げていきたいと思います。
最小限環境を作るのにうってつけのBusyBox
BusyBoxとは標準UNIXコマンドの主要コマンドをまとめて1つの実行ファイル化したツールです。 各コマンドの実行ファイルがもっているオーバヘッドを、1つのバイナリにすることで大幅に容量削減しています。
このBusyBoxは主要なコマンドとシェルを備えているので、これだけで基本的なLinux環境が整います。
組み込みLInux等のリソースが絞られた中でも環境構築に使えることから、十得ナイフとの呼び声が高いツールだそうです。
なんでもカーネルとBusyBoxとCライブラリとモジュールローダの4つがあれば最低限Linuxとして動くそうですよ。
Dockerコンテナではカーネル、モジュールローダはホストが持っているものをつかう(はず)なので、BusyBoxをベースイメージとすることで、最小限コンテナが作れるというわけです。
余談ですが最小級のディストリビューションであるTinyCoreもBusyBoxを採用していますよね。
どんなものか見てみる
Busyboxはどんなかんじなんでしょう?
以下のコマンドでBusyBoxコンテナを起動してみます。 (-i
は標準入力へのアタッチ、-t
はttyの割り当て、--rm
はコンテナ終了時にコンテナを削除するオプションです)
$ docker run -it --rm busybox
コンテナが起動すると素っ気ないプロンプトが…。
/ #
BusyBoxが持っているシェルはashというものです。高機能ではありませんが、小型で高速に動くとのことです。Almquist Shellの略だそうです。
試しにls
コマンドを実行してみます。
/ # ls
bin dev etc home proc root sys tmp usr var
/ #
ちゃんと表示されましたね。このls
は個別にインストールされているlsではなく、BusyBoxのls
です。
ちなみに以下のコマンドでBusyBoxが搭載しているコマンドの一覧を確認することができマス。通常の使用に問題ないくらいのコマンドが搭載されていますね。すげぇ。
/ # busybox
BusyBox v1.24.1 (2016-01-12 21:44:20 UTC) multi-call binary.
BusyBox is copyrighted by many authors between 1998-2015.
Licensed under GPLv2. See source distribution for detailed
copyright notices.
Usage: busybox [function [arguments]...]
or: busybox --list[-full]
or: busybox --install [-s] [DIR]
or: function [arguments]...
BusyBox is a multi-call binary that combines many common Unix
utilities into a single executable. Most people will create a
link to busybox for each function they wish to use and BusyBox
will act like whatever it was invoked as.
Currently defined functions:
[, [[, acpid, add-shell, addgroup, adduser, adjtimex, ar, arp, arping, ash, awk, base64, basename, beep, blkid, blockdev, bootchartd, brctl,
bunzip2, bzcat, bzip2, cal, cat, catv, chat, chattr, chgrp, chmod, chown, chpasswd, chpst, chroot, chrt, chvt, cksum, clear, cmp, comm,
conspy, cp, cpio, crond, crontab, cryptpw, cttyhack, cut, date, dc, dd, deallocvt, delgroup, deluser, depmod, devmem, df, dhcprelay, diff,
dirname, dmesg, dnsd, dnsdomainname, dos2unix, du, dumpkmap, dumpleases, echo, ed, egrep, eject, env, envdir, envuidgid, ether-wake, expand,
expr, fakeidentd, false, fatattr, fbset, fbsplash, fdflush, fdformat, fdisk, fgconsole, fgrep, find, findfs, flock, fold, free, freeramdisk,
fsck, fsck.minix, fstrim, fsync, ftpd, ftpget, ftpput, fuser, getopt, getty, grep, groups, gunzip, gzip, halt, hd, hdparm, head, hexdump,
hostid, hostname, httpd, hush, hwclock, i2cdetect, i2cdump, i2cget, i2cset, id, ifconfig, ifdown, ifenslave, ifplugd, ifup, inetd, init,
insmod, install, ionice, iostat, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink, iproute, iprule, iptunnel, kbd_mode, kill, killall, killall5, klogd,
last, less, linux32, linux64, linuxrc, ln, loadfont, loadkmap, logger, login, logname, logread, losetup, lpd, lpq, lpr, ls, lsattr, lsmod,
lsof, lspci, lsusb, lzcat, lzma, lzop, lzopcat, makedevs, makemime, man, md5sum, mdev, mesg, microcom, mkdir, mkdosfs, mke2fs, mkfifo,
mkfs.ext2, mkfs.minix, mkfs.vfat, mknod, mkpasswd, mkswap, mktemp, modinfo, modprobe, more, mount, mountpoint, mpstat, mt, mv, nameif,
nanddump, nandwrite, nbd-client, nc, netstat, nice, nmeter, nohup, nslookup, ntpd, od, openvt, passwd, patch, pgrep, pidof, ping, ping6,
pipe_progress, pivot_root, pkill, pmap, popmaildir, poweroff, powertop, printenv, printf, ps, pscan, pstree, pwd, pwdx, raidautorun, rdate,
rdev, readahead, readlink, readprofile, realpath, reboot, reformime, remove-shell, renice, reset, resize, rev, rm, rmdir, rmmod, route, rpm,
rpm2cpio, rtcwake, run-parts, runlevel, runsv, runsvdir, rx, script, scriptreplay, sed, sendmail, seq, setarch, setconsole, setfont,
setkeycodes, setlogcons, setserial, setsid, setuidgid, sh, sha1sum, sha256sum, sha3sum, sha512sum, showkey, shuf, slattach, sleep, smemcap,
softlimit, sort, split, start-stop-daemon, stat, strings, stty, su, sulogin, sum, sv, svlogd, swapoff, swapon, switch_root, sync, sysctl,
syslogd, tac, tail, tar, tcpsvd, tee, telnet, telnetd, test, tftp, tftpd, time, timeout, top, touch, tr, traceroute, traceroute6, true,
truncate, tty, ttysize, tunctl, ubiattach, ubidetach, ubimkvol, ubirmvol, ubirsvol, ubiupdatevol, udhcpc, udhcpd, udpsvd, uevent, umount,
uname, unexpand, uniq, unix2dos, unlink, unlzma, unlzop, unxz, unzip, uptime, users, usleep, uudecode, uuencode, vconfig, vi, vlock, volname,
wall, watch, watchdog, wc, wget, which, who, whoami, whois, xargs, xz, xzcat, yes, zcat, zcip
データコンテナを作る
データの保存
データがちゃんと保存されるかとりあえず作ってみましょう。
Busyboxは公式イメージとして存在しているので、以下のようにしてrun
できます。 -vオプションで/data
というボリュームを作っています。またコンテナの名前はdatavol
としてみました。
公式のBusyboxイメージではrun
時にsh
が起動するように設定されていますので、別途/bin/bash
などのようにシェルを指定する必要はありません。オプションのi
とt
を付与するだけでOKです。
$ docker run -it -v /data --name datavol busybox
このコマンドでコマンドラインがBusyboxコンテナに移りますので、次にファイルを作ってみましょう。echo
コマンドとリダイレクトを使ってdata!!
という内容のtest.txt
というファイルを作ってみました。
/ # echo "hello" >> /data/test.txt
/ # cat /data/test.txt
data!!
データコンテナとして使う
今度はこのコンテナを他のコンテナでデータコンテナとして利用します。
一度Busyboxコンテナから離脱します
/ # exit
そして今度はデータコンテナを使う側のコンテナをrun
してみます。今回はCentOSにしてみました。
--volumes-from
がデータコンテナをマウントするオプションになります。先ほどのBusyboxコンテナにつけた名前のdatavol
を指定しています。
$ docker run -it --volumes-from datavol --name testos centos /bin/bash
(今はデータコンテナとなった)Busyboxコンテナで先ほど作ったファイルを確認してみます。
[root@f1c629fdae43 /]# cat /data/test.txt
data!!
ちゃんとありますね。
このようにBusyboxコンテナで作ったボリュームを活用することができます。
補足・ハマりポイント
データコンテナは起動する必要はない
通常コンテナのリンクの場合はリンクするコンテナが起動している必要がありますが、データコンテナはデータの保持だけが目的なのでコンテナとして起動している必要がありません。
なのでrun
ではなくcreate
でも構いません。というかcreate
の方が楽かも。
$ docker create -it -v /data --name datavol busybox
データのホスト上の実ディレクトリ
さっき作ったtest.txt
はどこに保存されているのでしょうか?
コンテナの情報を見てみます。下記のコマンドを実行します。
$ docker inspect datavol
するとたくさん情報が出力されます。以下はマウントに関しての出力を抜粋シタものです。
"Mounts": [
{
"Name": "67fb96fd633bbf51c4daa5a37862493f565c15d7e591e2999664f67149765f79",
"Source": "/var/lib/docker/volumes/67fb96fd633bbf51c4daa5a37862493f565c15d7e591e2999664f67149765f79/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true
}
],
この中のSource
の値が先ほどBusyboxコンテナで作ったボリュームのホスト上での実ディレクトリです。
実際に確認してみるとファイルが入っています。
$ sudo cat /var/lib/docker/volumes/67fb96fd633bbf51c4daa5a37862493f565c15d7e591e2999664f67149765f79/_data/test.txt
data!
実ディレクトリを直接扱うことは無いと思いますが、このようにして管理されているんですね。覚えておこうっと。
データのバックアップ
commitとexportではできない
commit
とexport
でデータのバックアップができるという話を散見したのですが、テストしてみるどちらもボリューム上のデータは保存できません。確かにコマンド名からするとできそうな感じ。
でもできません。
docker commit
| ディレクトリ構造は保存されるが、ボリューム上につくったデータは保存されない。 |
docker export |
同じく構造はエクスポートできるがボリューム上のデータはエクスポートできない。 |
データのバックアップするにはどうもこれらの方法ではダメですね。
今のところの定石
よく言及されているのは、データコンテナをアプリケーションとは別の管理コンテナにつなげて、データボリュームの中身をさらう方法のようです。以下のページに方法が紹介されていました。
これはアプリケーションコンテナを動かしながら、バックアップ処理専用コンテナを走らせるたりすることができるということです(よね?)
アプリケーションコンテナにデータが張り付いている状態だとこういうことはなかなか面倒なので、良い方法かもしれません。
ホストのディレクトリをマウントするのと何がちがうのか?
おそらくデータディレクトリ構造の抽象化(汎化?)ができる点ではないかと思っています。
ホストのディレクトリをマウントする場合、ディレクトリ階層や名前や権限がどこのホストでも同じというわけには行きません。
コンテナ側に変化がなくてもホスト側の構成に変化があるとボリュームに不具合が出かねません。と思います。
しかしデータコンテナであればそのへんの違いを吸収できます。
どこでも同じ状態でデータディレクトリ構造を作れるというのは確かに便利な気がします。
データボリュームとデータ(ボリューム)コンテナ
データボリュームはコンテナの中に作られたボリュームであくまでそのコンテナと運命を共にします。
データ(ボリューム)コンテナは今回記事にした件で、データ保存専用のコンテナのことを指します。
複数のコンテナからデータを参照する場合、どこかのコンテナだけに所属しているボリュームを参照しあうのはとても複雑なリンクが必要になり心が折れそうな気がします。
なのでデータ(ボリューム)コンテナでアプリケーションからデータを分離することで、コンテナのリンク構造を単純化できるメリットがありそうな、そんな気がしています。
データボリュームはそのコンテナ特有のデータを扱う場合に使い、データコンテナは複数のコンテナから参照されるデータを収める場合に使うといいのかなと思っておりマス。
まとめ
Docker勉強するほど便利に思えてきて仕方がない。
データの扱いもちょっと見えてきたので楽しくなってきたデス。