さて、前回、前々回は Vagrant を通じて VirtualBox 上に仮想マシンを作る話でしたが、VirtualBox のように、実機の機能を全てエミュレーションする技術は、「完全仮想化」と呼ばれます。
対して、「準仮想化」という技術があり、最近流行(?)の「コンテナ」というものがあります。有名なのは Doker ですね。
「完全仮想化」と「準仮想化」の違いについては、Google ッても死ぬほど出て来ますので、ここでは踏み込みません。
今回は、「準仮想化」の「コンテナ」の一種、「LXD」をお手軽に試す手段について書いてみます。
その方法は……、「完全仮想化」の VirtualBox 上で動かすホスト上に、「準仮想化」LXC コンテナを動かす、という変態的なものです(笑)。
※)本当は、前回の Vagrant 話は、LXC コンテナを VirtualBox 上の仮想ホスト上にバンバン自動で立てる……話の伏線になる予定でしたが、ディストリビューション謹製の公式 BOX ファイルを使うとしても、マシン構成とか思い通りの要件から外れていることが判明したので、今回は見送ります。またいずれかの機会にチャレンジということで。
- 目次 -
使用ソフト
- VirtualBox 5.1.26
- Ubuntu Server 16.04.3 LTS(LVM 仮想ディスク2台構成)
- LXD 2.10
ハードウェア事前設定
- ストレージで、SATA コントローラーに、LXD 用 HDD を追加する。
- ネットワークアダプター1 の割り当てを「ブリッジアダプター」に変更する。
- 続いて、ネットワークアダプター1 で、「高度」→「プロミスキャスモード」を「全て許可」に変更する。
ちなみに、「プロミスキャスモード」の設定を忘れると、ネットワーク的に同一サブネット上にある実機とコンテナゲスト間でネットワークが疎通しなくなります。これで半日+半日のほぼ丸一日を無駄にしました……。
Ubuntu 16 インストール手順
Language | English |
Install Ubuntu Server | |
Language | English |
Country | UnitedStates |
Detect Keyboard Layout? | No |
configure the keyboard | Japanese |
Keyboard layout: | Japanese |
HostNage | ubuntu16 |
Full name for the new user | ubuntu |
Username for your acount | ubuntu |
Password | ************ |
Encrypt your home directory? | No |
time zone | Asia/Tokyo |
Partition disks | Yes |
Partitioning method | Guided use entire disk and set up LVM |
HTTP Proxy | continue |
How do you want to manage upgrades on this system? | No automatic updates |
software to install | standard system, OpenSSH server |
Install the GRUB master boot record? | yes |
「software to install」の項は、一見「Virtual Machine host」にもチェックを入れた方が良いように見えますが、無駄なソフトが入って、変なネットワークブリッジが出来ますので、ここではチェックしませんでした。
インストール後確認・初期作業
ubuntu@ubuntu16:~$ ifconfig enp0s3 Link encap:Ethernet HWaddr 08:00:27:79:46:aa inet addr:192.168.0.119 Bcast:192.168.0.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fe79:46aa/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1858 errors:0 dropped:0 overruns:0 frame:0 TX packets:112 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:122378 (122.3 KB) TX bytes:13607 (13.6 KB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:160 errors:0 dropped:0 overruns:0 frame:0 TX packets:160 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1 RX bytes:11840 (11.8 KB) TX bytes:11840 (11.8 KB)
インストール時に「Virtual Machine host」を選んでしまうと、ここで「virbr0」という見慣れぬネットワークブリッジが出来ます。
その場合は、下記で消せます。
root@ubuntu16:~# virsh net-list Name State Autostart Persistent ---------------------------------------------------------- default active yes yes root@ubuntu16:~# virsh net-destroy default Network default destroyed root@ubuntu16:~# virsh net-autostart default --disable Network default unmarked as autostarted root@ubuntu16:~# virsh net-list --all Name State Autostart Persistent ---------------------------------------------------------- default inactive no yes root@ubuntu16:~# reboot
続いて、もしかして既に LXD が入っちゃってるかも知れなかったりするので、念のため確認。。。
ubuntu@ubuntu16:~$ dpkg -l | grep lx ii liblxc1 2.0.8-0ubuntu1~16.04.2 amd64 Linux Containers userspace tools (library) ii lxc-common 2.0.8-0ubuntu1~16.04.2 amd64 Linux Containers userspace tools (common tools) ii lxcfs 2.0.7-0ubuntu1~16.04.2 amd64 FUSE based filesystem for LXC ii lxd 2.0.10-0ubuntu1~16.04.1 amd64 Container hypervisor based on LXC - daemon ii lxd-client 2.0.10-0ubuntu1~16.04.1 amd64 Container hypervisor based on LXC - client
入っちゃってるじゃないですか! 「Virtual Machine host」を選択しなかったのに、入っている……謎です。ッてか、意図的に入れてるとすれば、攻めすぎ ubuntu(笑)。
次に、root パスワードの設定とソフトウェアアップデートの適用、必要なパッケージの追加。
ubuntu@ubuntu16:~$ sudo passwd [sudo] password for ************: Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully ubuntu@ubuntu16:~$ su - Password: root@ubuntu16:~# apt-get update root@ubuntu16:~# apt-get upgrade root@ubuntu16:~# reboot root@ubuntu16:~# apt-get install zfsutils-linux bridge-utils
ブリッジ整理・追加
LXC で使用するネットワークブリッジの追加、LXC に追加したブリッジを使用するように設定を行います。
root@ubuntu16:~# vi /etc/network/interfaces auto enp0s3 iface enp0s3 inet manual auto br0 iface br0 inet static address 192.168.0.50 netmask 255.255.255.0 network 192.168.0.0 broadcast 192.168.0.255 gateway 192.168.0.254 dns-nameservers 192.168.0.254 bridge_ports enp0s3 bridge_stp off bridge_fd 0 bridge_maxwait 0 root@ubuntu16:~# lxc profile edit default Generating a client certificate. This may take a minute... If this is your first time using LXD, you should also run: sudo lxd init To start your first container, try: lxc launch ubuntu:16.04 name: default config: {} description: Default LXD profile devices: eth0: name: eth0 nictype: bridged parent: br0 type: nic root@ubuntu16:~# reboot root@ubuntu16:~# ifconfig br0 Link encap:Ethernet HWaddr 08:00:27:79:46:aa inet addr:192.168.0.50 Bcast:192.168.0.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fe79:46aa/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:620 errors:0 dropped:0 overruns:0 frame:0 TX packets:76 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:58067 (58.0 KB) TX bytes:9757 (9.7 KB) enp0s3 Link encap:Ethernet HWaddr 08:00:27:79:46:aa inet6 addr: fe80::a00:27ff:fe79:46aa/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1034 errors:0 dropped:0 overruns:0 frame:0 TX packets:84 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:106756 (106.7 KB) TX bytes:10621 (10.6 KB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:80 errors:0 dropped:0 overruns:0 frame:0 TX packets:80 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1 RX bytes:5920 (5.9 KB) TX bytes:5920 (5.9 KB)
LXD 初期化
さて、ホスト側の準備が整いました。
続いて LXD を初期化して使用出来る状態にします。
root@ubuntu16:~# lxc list +------+-------+------+------+------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +------+-------+------+------+------+-----------+ root@ubuntu16:~# lxd init Name of the storage backend to use (dir or zfs) [default=zfs]: zfs Create a new ZFS pool (yes/no) [default=yes]? Name of the new ZFS pool [default=lxd]: Would you like to use an existing block device (yes/no) [default=no]? yes Path to the existing block device: /dev/sdb Would you like LXD to be available over the network (yes/no) [default=no]? no Do you want to configure the LXD bridge (yes/no) [default=yes]? yes LXD bridge: Would you like to setup a network bridge for LXD containers now? No Do you want to use an existing bridge? Yes Bridge interface name: br0 Warning: Stopping lxd.service, but it can still be activated by: lxd.socket LXD has been successfully configured. root@ubuntu16:~# lxc profile edit default Generating a client certificate. This may take a minute... If this is your first time using LXD, you should also run: sudo lxd init To start your first container, try: lxc launch ubuntu:16.04 name: default config: {} description: Default LXD profile devices: eth0: name: eth0 nictype: bridged parent: br0 type: nic
最後に再び「lxc profile edit default」しているのは、念のため default プロファイルが初期化によって書き換わっていないか、確認しています。
CentOS7 コンテナ起動
root@ubuntu16:~# lxc remote list +-----------------+------------------------------------------+---------------+--------+--------+ | NAME | URL | PROTOCOL | PUBLIC | STATIC | +-----------------+------------------------------------------+---------------+--------+--------+ | images | https://images.linuxcontainers.org | simplestreams | YES | NO | +-----------------+------------------------------------------+---------------+--------+--------+ | local (default) | unix:// | lxd | NO | YES | +-----------------+------------------------------------------+---------------+--------+--------+ | ubuntu | https://cloud-images.ubuntu.com/releases | simplestreams | YES | YES | +-----------------+------------------------------------------+---------------+--------+--------+ | ubuntu-daily | https://cloud-images.ubuntu.com/daily | simplestreams | YES | YES | +-----------------+------------------------------------------+---------------+--------+--------+ root@ubuntu16:~# lxc image list images: centos +------------------------+--------------+--------+---------------------------------+--------+---------+------------------------------+ | ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE | +------------------------+--------------+--------+---------------------------------+--------+---------+------------------------------+ | centos/6 (3 more) | 6c7e736e0738 | yes | Centos 6 amd64 (20160901_02:16) | x86_64 | 65.78MB | Sep 1, 2016 at 12:00am (UTC) | +------------------------+--------------+--------+---------------------------------+--------+---------+------------------------------+ | centos/6/i386 (1 more) | 434ea83beee5 | yes | Centos 6 i386 (20160901_02:16) | i686 | 65.72MB | Sep 1, 2016 at 12:00am (UTC) | +------------------------+--------------+--------+---------------------------------+--------+---------+------------------------------+ | centos/7 (3 more) | 0384825c50a4 | yes | Centos 7 amd64 (20160901_02:16) | x86_64 | 62.91MB | Sep 1, 2016 at 12:00am (UTC) | +------------------------+--------------+--------+---------------------------------+--------+---------+------------------------------+ root@ubuntu16:~# lxc launch images:centos/7/amd64 cnt01 Creating cnt01 Retrieving image: 100% Starting cnt01 root@ubuntu16:~# lxc list +-------+---------+----------------------+------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +-------+---------+----------------------+------+------------+-----------+ | cnt01 | RUNNING | 192.168.0.116 (eth0) | | PERSISTENT | 0 | +-------+---------+----------------------+------+------------+-----------+ root@ubuntu16:~# ifconfig br0 Link encap:Ethernet HWaddr 08:00:27:79:46:aa inet addr:192.168.0.50 Bcast:192.168.0.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fe79:46aa/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:22507 errors:0 dropped:0 overruns:0 frame:0 TX packets:13202 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:67822192 (67.8 MB) TX bytes:922691 (922.6 KB) enp0s3 Link encap:Ethernet HWaddr 08:00:27:79:46:aa inet6 addr: fe80::a00:27ff:fe79:46aa/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:53189 errors:0 dropped:0 overruns:0 frame:0 TX packets:13224 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:70689770 (70.6 MB) TX bytes:925165 (925.1 KB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:80 errors:0 dropped:0 overruns:0 frame:0 TX packets:80 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1 RX bytes:5920 (5.9 KB) TX bytes:5920 (5.9 KB) veth8D5VKR Link encap:Ethernet HWaddr fe:5a:2b:70:27:00 inet6 addr: fe80::fc5a:2bff:fe70:2700/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:14 errors:0 dropped:0 overruns:0 frame:0 TX packets:334 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:1544 (1.5 KB) TX bytes:30773 (30.7 KB)
同一サブネット内に DHCP サーバーがあれば、このように自動的に IP アドレスが割り当てられて、コンテナが立ち上がります。
最後にある「veth8D5VKR」は、コンテナが使用するブリッジアダプタで、コンテナ内からは、後述のように「eth0」として見えます。
ではさっそく、コンテナ内に入って、ネットワーク状態の確認、及び固定 IP の設定を行います。
root@ubuntu16:~# lxc exec cnt01 bash [root@cnt01 ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000 link/ether 00:16:3e:bf:f4:59 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 192.168.0.116/24 brd 192.168.0.255 scope global dynamic eth0 valid_lft 10759sec preferred_lft 10759sec inet6 fe80::216:3eff:febf:f459/64 scope link valid_lft forever preferred_lft forever [root@cnt01 ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0 # DEVICE=eth0 # BOOTPROTO=dhcp # ONBOOT=yes # HOSTNAME=LXC_NAME # NM_CONTROLLED=no # TYPE=Ethernet # MTU= # DHCP_HOSTNAME=`hostname` DEVICE=eth0 ONBOOT=yes BOOTPROTO=static TYPE=Ethernet IPADDR=192.168.0.51 NETMASK=255.255.255.0 NETADDR=192.168.0.0 BROADCAST=192.168.0.255 GATEWAY=192.168.0.254 IPV6INIT=no [root@cnt01 ~]# /etc/init.d/network restart Restarting network (via systemctl): [ OK ]
コンテナのホストで実行する
# lxc exec cnt01 bash
についてはちょっと説明が必要でしょうか。
これは、LXC に対して
- 以下のコマンドを実行しなさい(exec)
- 対象のコンテナ名は「cnt01」
- 実行コマンドの内容は「bash」
という意味になっています。
正式には「bash」ではなくて「/bin/bash」になるかと思います。
と、いうことは、別に「bash」でなくてもよくて、例えば「ip a」コマンドであれば、
root@ubuntu16:~# lxc exec cnt01 ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000 link/ether 00:16:3e:45:4e:28 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 192.168.8.51/24 brd 192.168.8.255 scope global eth0 valid_lft forever preferred_lft forever inet6 2001:ce8:94:1023:216:3eff:fe45:4e28/64 scope global mngtmpaddr dynamic valid_lft 201sec preferred_lft 201sec inet6 fe80::216:3eff:fe45:4e28/64 scope link valid_lft forever preferred_lft forever
と、わざわざコンテナに入らなくても、ホストからコンテナ側で好きなコマンドが実行出来ます。
というわけで、ホスト側にもし侵入でもされたら、怖いですね。怖いですね。とても怖いですね。VirtualBox 上のお試しで良かったですね(笑)。
とはいえ、一番最初の「ハードウェア事前設定」で、ネットワークアダプタタイプを「ブリッジ」にしていました。
同じ LAN 内の同じサブネット内の他実機からは、コンテナのホスト(つまり VirtualBox 内の仮想マシン)も、コンテナのゲストも、ネットワーク的に見えている状態となりますので、間違っても公衆無線 LAN に繋がったまま実験したりしないようにしましょう。
では、どのように「見える」状態かの確認……別名「疎通確認」をしておきましょう。
[root@cnt01 ~]# ping 192.168.0.50 PING 192.168.0.50 (192.168.0.50) 56(84) bytes of data. 64 bytes from 192.168.0.50: icmp_seq=1 ttl=64 time=0.136 ms 64 bytes from 192.168.0.50: icmp_seq=2 ttl=64 time=0.079 ms 64 bytes from 192.168.0.50: icmp_seq=3 ttl=64 time=0.083 ms --- 192.168.0.50 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 1999ms rtt min/avg/max/mdev = 0.079/0.099/0.136/0.027 ms
[root@cnt01 ~]# ping www.yahoo.co.jp PING edge.g.yimg.jp (182.22.25.124) 56(84) bytes of data. 64 bytes from 182.22.25.124 (182.22.25.124): icmp_seq=1 ttl=56 time=9.84 ms 64 bytes from 182.22.25.124 (182.22.25.124): icmp_seq=2 ttl=56 time=10.1 ms 64 bytes from 182.22.25.124 (182.22.25.124): icmp_seq=3 ttl=56 time=10.2 ms ^C --- edge.g.yimg.jp ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2004ms rtt min/avg/max/mdev = 9.840/10.071/10.240/0.169 ms
[root@cnt01 ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000 link/ether 00:16:3e:45:4e:28 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 192.168.0.51/24 brd 192.168.8.255 scope global eth0 valid_lft forever preferred_lft forever inet6 2001:ce8:94:1023:216:3eff:fe45:4e28/64 scope global mngtmpaddr dynamic valid_lft 228sec preferred_lft 228sec inet6 fe80::216:3eff:fe45:4e28/64 scope link valid_lft forever preferred_lft forever [root@cnt01 ~]# exit exit root@ubuntu16:~# ping 192.168.0.51 PING 192.168.8.51 (192.168.0.51) 56(84) bytes of data. 64 bytes from 192.168.0.51: icmp_seq=1 ttl=64 time=0.060 ms 64 bytes from 192.168.0.51: icmp_seq=2 ttl=64 time=0.050 ms 64 bytes from 192.168.0.51: icmp_seq=3 ttl=64 time=0.053 ms 64 bytes from 192.168.0.51: icmp_seq=4 ttl=64 time=0.052 ms ^C --- 192.168.0.51 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.050/0.053/0.060/0.009 ms
C:\>ping 192.168.0.50 192.168.0.50 に ping を送信しています 32 バイトのデータ: 192.168.0.50 からの応答: バイト数 =32 時間 <1ms TTL=64 192.168.0.50 からの応答: バイト数 =32 時間 <1ms TTL=64 192.168.0.50 からの応答: バイト数 =32 時間 <1ms TTL=64 192.168.0.50 からの応答: バイト数 =32 時間 <1ms TTL=64 192.168.0.50 の ping 統計: パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、 ラウンド トリップの概算時間 (ミリ秒): 最小 = 0ms、最大 = 0ms、平均 = 0ms C:\>ping 192.168.0.51 192.168.0.51 に ping を送信しています 32 バイトのデータ: 192.168.0.51 からの応答: バイト数 =32 時間 <1ms TTL=64 192.168.0.51 からの応答: バイト数 =32 時間 <1ms TTL=64 192.168.0.51 からの応答: バイト数 =32 時間 <1ms TTL=64 192.168.0.51 からの応答: バイト数 =32 時間 <1ms TTL=64 192.168.0.51 の ping 統計: パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、 ラウンド トリップの概算時間 (ミリ秒): 最小 = 0ms、最大 = 0ms、平均 = 0ms
Linux ~ # ping 192.168.0.51 PING 192.168.8.51 (192.168.0.51) 56(84) bytes of data. 64 bytes from 192.168.0.51: icmp_seq=1 ttl=64 time=0.472 ms 64 bytes from 192.168.0.51: icmp_seq=2 ttl=64 time=0.505 ms 64 bytes from 192.168.0.51: icmp_seq=3 ttl=64 time=0.604 ms ^C --- 192.168.0.51 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2045ms rtt min/avg/max/mdev = 0.472/0.527/0.604/0.056 ms
最後の「Linux」と言う名前のホストは、同一サブネットにある Linux 機(例の Gentoo 機)です。
考察
今回は特に考察することも無さそうですが、手順さえ踏み外さなければ、LXD コンテナ、意外と簡単に作成出来ます。
もちろん、今回は「LXD をお気軽に試す」とタイトル通りなので、「LXD で作成したコンテナを何に使うか」という点については、意図的に触れずにおきます。
同じコンテナの一種、Docker がそうであるように、もはやコンテナは「好事家のおもちゃ」を脱して、「いかに実利用されるか」の時代にさしかかっているのかも知れませんね。
参考 URL
- http://gihyo.jp/admin/serial/01/linux_containers?start=20
- http://hnakamur.github.io/blog/2016/05/07/start-using-lxd-2.0-on-ubuntu-16.04/
- http://qiita.com/white_aspara25/items/723ae4ebf0bfefe2115c
- http://hnakamur.github.io/blog/2016/05/07/how-to-use-fixed-ip-address-for-a-lxd-container/
- http://www.linux-beginner.com/linux_setei9.html
- http://blog.goo.ne.jp/takuminews/e/888ac886a472e666aac7854ec0755a6b
- http://qiita.com/tabimoba/items/13411acd3d2e03651913
- http://www.postcard.st/nosuz/tech/2011/09/17-23
- http://kurochan-note.hatenablog.jp/entry/2017/01/31/190200