Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
コマンドを叩いて遊ぶ
コンテナ仮想、その裏側
Preferred Infrastructure, Inc.
製品開発事業部 武井裕也
自己紹介
 武井 裕也
 github: takei-yuya
 元インターン組 (筑波勢)
 日本酒と鶏肉とbashとvimと猫
と高輝度LEDを愛してやまな
いエンジニア
2
はじめに
 コンテナ型仮想化
 物理マシンを仮想化(エミュレーション)
する完全仮想化に対し、OSのレイヤー以
下で異なる環境を動かす仮想化
 ✔ 軽量 (省メモリ・省リソース)
 ✔ 高速 (オーバーヘッドほぼ無し)
 ✘ 異なるOS/カーネルを動かせない
 e.g. Windows on Linux
3
仮想マシンA
仮想マシン
カーネル
プロセス
仮想マシンB
仮想マシン
カーネル
プロセス
ハイパーバイザ
(ホストOS)
ハードウエア
コンテナA
プロセス
ホストカーネル
ハードウエア
コンテナB
プロセス
名前空間 名前空間
VM仮想化
コンテナ仮想化
コンテナー仮想化あれこれ(Linux)
 LXC (Linux Containers)
 Docker以前から名を馳せていたコンテナ仮想化
 cgroupsの産みの親
 Docker
 コンテナ仮想化ブームの立役者 Go言語製
 1コンテナ1プロセスを想定している
 LXCベースだったが途中からlibcontainerを使う用に
 最近色々物議を醸している
 root権限中央集権デーモン、docker pull のセキュリティ等4
コンテナー仮想化あれこれ(Linux)
 CentOS/Rocket
 Dockerの諸問題を解決すべく作られたコンテナ仮想化
 コンテナーのオープン仕様
 systemd-nspawn
 systemdの一機能として提供される仮想化機能
 Rocketの裏で使われてたりする
 MINCS
 shell scriptで書かれたコンテナ仮想化
 何をやっているのか勉強するのにとても良い5
で、コンテナ仮想化ってなにやってるの?
 Ans. 大ざっぱには、単にプロセスを起動するだけ
 ただし、そのプロセスの
 1. ルート(/)の差し替え (chroot / pivot_root)
 2. 名前空間の分離 (unshare / clone)
 3. リソースの制御 (cgroups)
 をする
 今回は「名前空間の分離」と「ルートの差し替え」を話します
 cgroupsはスライド作成が間に合わずorz
6
Linux名前空間
 名前空間の種類
 ipc: 共有メモリやメッセージキューなど
 mnt: マウントポイント
 net: ネットワーク全般
 pid: プロセスID
 uts: ホスト/ドメイン名
 user: uid / gid (>=3.8)
 RHEL7では未サポート(Kernel自体は3.10)
 今回はスキップ (面白い機能なのですが……)7
プロセスの名前空間
 各プロセスはなにかしらの名前空間に属している
 forkした場合には基本的には親の名前空間を引き継ぐ
 clone(2) / unshare(2) など新しい名前空間を作ることが出来る
 コマンドラインで遊ぶ場合にはunshare(1)
 /proc/${PID}/ns 以下のシンボリックリンクのリンク先で区別できる
8
$ ll /proc/$$/ns
合計 0
lrwxrwxrwx. 1 takei takei 0 10月 17 20:57 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 takei takei 0 10月 17 20:57 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 takei takei 0 10月 17 20:57 net -> net:[4026531992]
lrwxrwxrwx. 1 takei takei 0 10月 17 20:57 pid -> pid:[4026531836]
lrwxrwxrwx. 1 takei takei 0 10月 17 20:57 uts -> uts:[4026531838]
mnt名前空間
 unshareコマンドに --mount オプションを付けると、新しいmount名
前空間に属するプロセスを作ることが出来る。
 mount名前空間を分離させると、プロセスごとに異なるマウント
ポイントを持てる (e.g. PrivateTemp)
9
$ readlink /proc/$$/ns/mnt # 元のmount名前空間の確認
mnt:[4026531840]
$ sudo unshare --mount /bin/bash # 新しいmount名前空間の作成
# readlink /proc/$$/ns/mnt # 新しいmount名前空間の確認
mnt:[4026532249]
# mkdir mnt; mount -t tmpfs tmpfs mnt
# mount # マウントポイントの確認
# exit
$ mount # 元のマウントポイントの確認
mnt名前空間 - 余談: マウントプロパゲーション
 マウントイベントの伝播を制御する仕組み
 あるマウントポイント以下へのmount/unmountのイベントを、
関係するマウントポイントに伝播するかどうか。
 shared(双方向) slave(一方向) private(伝播しない)
10
$ mkdir src dest src/{master,slave}
# mount --bind src dest # srcをdestにbindする
# mount --make-slave dest # masterからslaveへは伝播する
# mount -t tmpfs tmpfs src/master # マウント元(src)へのマウント
# mount -t tmpfs tmpfs dest/slave # マウント先(dest)へのマウント
$ mount
tmpfs on /home/alice/src/master type tmpfs (rw,relatime,seclabel)
tmpfs on /home/alice/dest/master type tmpfs (rw,relatime,seclabel)
tmpfs on /home/alice/dest/slave type tmpfs (rw,relatime,seclabel)
mnt名前空間 - 余談: マウントプロパゲーション
 マウントプロパゲーションはmnt名前空間を貫通する
 もし名前空間で完全に区切られると……。
 → 例えばプロセス独自/tmpをマウントするために名前空間を分
離してデーモンを起動
 → ユーザCD-ROMをパソコンにいれる
 → CD-ROMが/mediaとかに自動マウントされる
 → デーモンは別mnt名前空間にいるのでマウントされない
 → デーモンさん(´・ω・) CD読めない……。
 → ディレクトリごとに、共有するしないが選べる!11
mnt名前空間 - 余談: マウントプロパゲーション
 RHEL7ではデフォルトで / がshared (というかsystemdのせい)
 mnt名前空間で遊ぶ場合、そもそも root(/) を切り替えるか、 一
旦マウントプロパゲーションをprivateにする必要あり
12
$ sudo unshare --mount /bin/bash # 新しいmount名前空間の作成
# mkdir mnt
# mount --make-private / # プロパゲーションを切る
# mount -t tmpfs tmpfs mnt
# mount --make-shared / # 元に戻す
# mount # マウントポイントの確認
# exit
$ mount # 元のマウントポイントの確認
pid名前空間
 unshare --pid --fork で新しいpid名前空間でプロセスを起動
 新しいプロセスを作るために --fork が必須
 新しいプロセスが pid=1となり、他のプロセスは見られなくなる
 元のpid名前空間(親)からは新しいpid名前空間の中身は見える
 ※ ps(1)コマンドなどはprocファイルシステムを参照する
 mount -t proc proc /proc でマウントしなおせばok
 ただしmnt名前空間を分離していないと他のプロセスの/proc
まで上書きマウントされるので注意。
 --mount-proc オプションでこの辺を全部やってくれる13
uts名前空間
 unshare --uts ホスト名やドメイン名の新しい名前空間を作成
 /etc/hostnameなどファイルを書き換えるようなホスト名の指定は当
然ファイルを書き換えるので注意。(chrootやmntnsなど必要)
14
$ hostname
ip-172-31-13-102.ap-northeast-1.compute.internal
$ sudo unshare --uts
# hostname wonderland
# hostname
wonderland
# logout
$ hostname
ip-172-31-13-102.ap-northeast-1.compute.internal
net名前空間
 unshare --net で新しいnet名前空間でプロセスを起動
 ただし ip(1) を使った方が名前付きで作れるのでおすすめ
 /var/run/netns 以下に指定された名前でprocfsがマウントされる
15
$ sudo ip netns add test # testという名前でnetnsを作成
$ sudo ip netns list # 名前空間一覧
test
$ sudo ip netns exec test /bin/bash # test名前空間でプロセスを起動
# readlink /proc/$$/ns/net # netns確認
net:[4026532219]
# ls -li /var/run/netns/test # /var/run/netns以下にファイルができる
4026532219 -r--r--r--. 1 root root 0 Oct 18 03:02 /run/netns/test
# ip addr # loインターフェースしか見えない
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
net名前空間 - 余談: veth
 通信するために仮想ethインタフェース(veth)を作って遊んでみる
 1. vethペア(master/slave)の作成とnetnsの設定
16
$ sudo ip link add name master type veth peer name slave # vethペア作成
$ sudo ip addr # 確認
6: slave: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 3a:64:e8:80:03:5f brd ff:ff:ff:ff:ff:ff
7: master: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 86:cf:cc:26:74:e4 brd ff:ff:ff:ff:ff:ff
$ sudo ip link set slave netns test # 片方をnetns testに移動
$ sudo ip addr # インターフェースの確認
7: master: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 86:cf:cc:26:74:e4 brd ff:ff:ff:ff:ff:ff
$ sudo ip netns exec test ip addr
6: slave: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 3a:64:e8:80:03:5f brd ff:ff:ff:ff:ff:ff
net名前空間 - 余談: veth
 通信するために仮想ethインタフェース(veth)を作って遊んでみる
 2. IPアドレス割り振り & 疎通確認
17
$ sudo ip addr add 192.168.50.101/24 dev master # masterにIPアドレス設定
$ sudo ip link set dev master up # リンクアップ
$ sudo ip netns exec test /bin/bash # 名前空間内にbash起動
# ip addr add 192.168.50.102/24 dev slave # slaveにIPアドレス設定
# ip link set dev slave up # リンクアップ
# ping 192.168.50.101 -c1 # 疎通確認
PING 192.168.50.101 (192.168.50.101) 56(84) bytes of data.
64 bytes from 192.168.50.101: icmp_seq=1 ttl=64 time=0.047 ms
# exit
$ ping 192.168.50.102 -c1
net名前空間 - 余談: veth
 通信するために仮想ethインタフェース(veth)を作って遊んでみる
 3. IPマスカレードの設定 & 外部ネットーワークへの疎通確認
18
$ sudo ip netns exec test /bin/bash
# ip route add default via 192.168.50.101 dev slave # default gw の設定
# ip route
default via 192.168.50.101 dev slave
192.168.50.0/24 dev slave proto kernel scope link src 192.168.50.102
# exit
$ # IPマスカレードの設定
$ sudo iptables -t nat -A POSTROUTING -s 192.168.50.0/24 -o eth0 -j MASQUERADE
$ sudo ip netns exec test /bin/bash
# ping 8.8.8.8 -c1 # 外部への疎通確認
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=55 time=2.18 ms
Linux名前空間
 名前空間の種類
 ipc: 共有メモリやメッセージキューなど
 mnt: マウントポイント
 net: ネットワーク全般
 pid: プロセスID
 uts: ホスト/ドメイン名
 user: uid / gid
 ユーザごとにユーザ一覧を持てる
 一般ユーザなのにrootっぽいユーザも作れる!19
chroot
 古の昔からある仮想化(?)
 root(/)を張り替えた状態でプロセスを起動する
 元々はビルドシステムをクリーンな / で実行する目的で作られた
 システムファイルを隠せるのでセキュリティ目的に使われることも
 新しいrootにもシステム(binやlib等)が必要なのでchrootするためには
OSのイメージなど(Gentooのstage3とか)が必要
 RHEL系の場合、installrootを指定してCore等を入れ直せばok
20
mkdir new-root
sudo yum -y --releasever=7Server --installroot=${PWD}/new-root install 
@Core @Base redhat-release-server vim-enhanced
pivot_root
 chrootに似た機能
 ディレクトリをルートにしてプロセスを起動するchrootに対して
 ルートファイルシステムを入れ替えることでルートを変える
 マウントポイントを変更するので要 mnt名前空間
 旧ルートは新ルートのサブディレクトリにマウントされる
21
$ sudo unshare -m -p -f /bin/bash # pid/mnt名前空間切り離し
# mount --make-rprivate / # プロパゲーションoff
# mount -o loop /root.img /mnt/new-root/ # 新しいrootをマウント
# cd /mnt/new-root/
# mkdir .old # 旧rootのマウントポイント
# pivot_root . .old # pivot!
# ls /.old # 旧rootの中身をみてみる
pivot_rootとchroot
 pivot_rootはマウントポイントを差し替えるだけなので、新しいルー
トのshellをexecし直したり、chrootするなりする必要アリ
 古いrootをunmountするためには pts やらバイナリやらの張り直
し、exec し直しが必要になる
 MINCSの場合、pivot_rootを二回(ルートの変更、旧ルートの移
動)したあとでchrootしている
 ディレクトリやデバイスひとつが、1つの環境になる
 → コンテナを複数作りたい場合には、複数デバイスやOSイメー
ジが必要……?
22
chroot - 余談: overlayfs
 ディレクトリを「重ねて」マウントできるfs
 変更は「上」にだけ保存され「下」には影響を与えない
 基準となるイメージを共有しつつchrootごとに差分を適応できる
23
$ mkdir upper work # 差分ディレクトリと作業用ディレクトリ
$ sudo mount -t overlay 
-o lowerdir=/,upperdir=upper,workdir=work overlayfs new-root
$ touch /home/alice/file1 new-root/home/alice/file2
$ ls -l new-root/home/alice/file* # 両方とも見える
-rw-rw-r--. 1 alice alice 0 Oct 18 12:30 new-root/home/alice/file1
-rw-rw-r--. 1 alice alice 0 Oct 18 12:30 new-root/home/alice/file2
$ rm new-root/home/alice/file1 # ファイルを消してみる
$ ll upper/home/alice/file* # 差分がupperにだけ残る
c---------. 1 alice alice 0, 0 Oct 18 12:32 upper/home/alice/file1
chroot - 余談: thin provisioning(device mapper)
 device mapperの一機能: thin porvisioning (dm-thin)
 ディスク領域のプーリングとスナップショットの機能を持つ
 プーリング: 必要に応じてストレージプールから一部分を取
り出し、一意なidを振って管理する
 スナップショット: 切り出してきたストレージパッチを使い
CoW(Copy On Write)差分スナップショットを提供する
 LVMの皮をかぶせて、容量変更やスナップショットを、ヴォリ
ュームレベルで提供できる
 RHELのDockerのデフォルトでは生dm-thinを使っている
24
chroot - 余談: thin provisioning(device mapper)
 手作業でいじるのは面倒なのでDockerの挙動を追ってみる
 1. loop backにdm-thinのpoolを割り当てる
 実体は /var/lib/docker/devicemapper/devicemapper/{,meta}data
25
$ sudo systemctl start docker # dockerデーモンの起動
$ losetup # loopにpoolが割り当てられる
NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE
/dev/loop0 0 0 1 0 /var/lib/docker/devicemapper/devicemapper/data
/dev/loop1 0 0 1 0 /var/lib/docker/devicemapper/devicemapper/metadata
$ sudo ls -hl /var/lib/docker/devicemapper/devicemapper/ # 100Gと2G (デフォルト値)
total 4.5G
-rw-------. 1 root root 100G Oct 19 04:54 data
-rw-------. 1 root root 2.0G Oct 19 04:56 metadata
chroot - 余談: thin provisioning(device mapper)
 (続き) デーモン起動時: スパースファイル
 poolはRHEL7デフォルトだとスパースファイルで定義される
 ただし、スパースファイルは断片化しやすく、loopbackマウン
トも性能もよろしくないのでRHEL7的には本番環境では非推奨
 → dm-thin用のLVを割り当てよう。
26
$ sudo du -h /var/lib/docker/devicemapper/devicemapper/data
4.4G/var/lib/docker/devicemapper/devicemapper/data
$ sudo ls -lh /var/lib/docker/devicemapper/devicemapper/data
-rw-------. 1 root root 100G Oct 19 04:54 /var/lib/docker/devicemapper/devicemapper/data
$ fallocate -o 9223372036854775807 -l 1 huge # スパースファイルはfallocateで作れる
$ ls -lh huge; du -h huge # 8EB(エクサバイト)!!
-rw-r--r--. 1 alice alice 8.0E Oct 19 05:10 huge
chroot - 余談: thin provisioning(device mapper)
 2.コンテナの起動
 10Gずつ切り出されてコンテナ用に使われる(RHEL7 default)
27
$ docker run -d centos:centos7 /sbin/init # 適当なコンテナを起動
$ docker ps # コンテナーIDの確認
CONTAINER ID IMAGE COMMAND ...
b90ed5b981ae centos:centos7 "/sbin/init" ...
$ lsblk # ブロックデバイス一覧を確認
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 30G 0 disk
├─xvda1 202:1 0 1M 0 part
└─xvda2 202:2 0 30G 0 part /
loop0 7:0 0 100G 0 loop
└─docker-202:2-62765-pool 253:0 0 100G 0 dm
└─docker-202:2-62765-b90ed5b981ae9d06...ee67 253:1 0 10G 0 dm
loop1 7:1 0 2G 0 loop
└─docker-202:2-62765-pool 253:0 0 100G 0 dm
chroot - 余談: thin provisioning(device mapper)
 3. runされていないイメージの情報
28
$ # タグ付きイメージのidの確認
$ sudo jq . /var/lib/docker/repositories-devicemapper
{
"Repositories": {
"test": { "latest": "a02698bf3...e5c42b" }
},
"ConfirmDefPush": true
}
$ # イメージのdm-thinデバイス情報
$ sudo jq . /var/lib/docker/devicemapper/metadata/a02698bf3...e5c42b
{
"device_id": 352,
"size": 10737418240,
"transaction_id": 582,
chroot - 余談: thin provisioning(device mapper)
 (おまけ) dockerのイメージをマウントしてみる
29
$ # device_idとsizeを確認
$ sudo jq . /var/lib/docker/devicemapper/metadata/a02698bf...5c42b
... "device_id": 352, "size": 10737418240, ...
$ # プール名を確認
$ lsblk
loop0
└─docker-202:2-62765-pool
$ # dmデバイスの準備
$ sudo dmsetup create dockervol 
--table "0 $((10737418240 / 512)) thin /dev/mapper/docker-202:2-62765-pool 352"
$ # dmデバイスの確認とマウント
$ ll /dev/mapper/dockervol
lrwxrwxrwx. 1 root root 7 Oct 19 06:10 /dev/mapper/dockervol -> ../dm-3
$ mkdir mnt
chroot - 余談: thin provisioning(device mapper)
 (続き) dockerのイメージをマウントしてみる
30
$ ll mnt/ # 早速中を見てみる
total 24
-rw-------. 1 root root 64 Aug 26 23:08 id
drwx------. 2 root root 16384 Aug 26 22:58 lost+found
$ ll mnt/rootfs/ # dockerのディスクイメージの中身(OS)
total 64
lrwxrwxrwx. 1 root root 7 Jun 18 08:34 bin -> usr/bin
drwxr-xr-x. 3 root root 4096 Oct 18 12:56 boot
:
$ sudo cat mnt/id # idはオリジナルのディスクイメージのidらしい
f1b10cd842498c23d206ee0cbeaa9de8d2ae09ff3c7af2723a9e337a6965d639
$ docker history test:latest
IMAGE CREATED CREATED BY ...
a02698bf3120 17 hours ago /bin/sh -c yum install -y httpd
a6673f7926d7 7 weeks ago /bin/sh -c #(nop) MAINTAINER TAKEI Yuya <take
7322fbe74aa5 4 months ago /bin/sh -c #(nop) CMD ["/bin/bash"]
まとめ
 コンテナ仮想化?
 → ただの名前空間・ルート(・リソース)の切り離しだ!
 名前空間は、OSにつき一つしかないものを分離する機構
 unshareで分離できる!
 ルートはchrootやpivot_rootで切れる
 複数の環境を管理するためにoverlayfsやdm-thinが便利
 リソースはcgroups(コントロールグループ)で管理できる
 今回は資料が間に合いませんでしたorz
31
参考URL
 Red Hat Enterprise Linux 7 - 製品マニュアル
 https://access.redhat.com/site/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/index.html
 IBM developerWorks - マウント名前空間を適用する
 http://www.ibm.com/developerworks/jp/linux/library/l-mount-namespaces.html
 github - MINCS
 https://github.com/mhiramat/mincs
 TenForwardの日記 - シェルスクリプトで書かれた軽量コンテナ MINCS がすばらしい (1)
 http://d.hatena.ne.jp/defiant/20150701/1435749116
 めもめも - Dockerのネットワーク管理とnetnsの関係
 http://enakai00.hatenablog.com/entry/20140424/1398321672
 めもめも - RHEL7におけるDockerのディスクイメージ管理方式
 http://enakai00.hatenablog.com/entry/20140420/1397981156
 paiza開発日誌 - 知らぬはエンジニアの恥。今さら聞けない【コンテナ/仮想化技術】11選
 http://paiza.hatenablog.com/entry/2014/10/21/知らぬはエンジニアの恥%E3%80%82今さら聞けない【コン
32

More Related Content

PFIセミナーH271022 ~コマンドを叩いて遊ぶ コンテナ仮想、その裏側~

  • 2. 自己紹介  武井 裕也  github: takei-yuya  元インターン組 (筑波勢)  日本酒と鶏肉とbashとvimと猫 と高輝度LEDを愛してやまな いエンジニア 2
  • 3. はじめに  コンテナ型仮想化  物理マシンを仮想化(エミュレーション) する完全仮想化に対し、OSのレイヤー以 下で異なる環境を動かす仮想化  ✔ 軽量 (省メモリ・省リソース)  ✔ 高速 (オーバーヘッドほぼ無し)  ✘ 異なるOS/カーネルを動かせない  e.g. Windows on Linux 3 仮想マシンA 仮想マシン カーネル プロセス 仮想マシンB 仮想マシン カーネル プロセス ハイパーバイザ (ホストOS) ハードウエア コンテナA プロセス ホストカーネル ハードウエア コンテナB プロセス 名前空間 名前空間 VM仮想化 コンテナ仮想化
  • 4. コンテナー仮想化あれこれ(Linux)  LXC (Linux Containers)  Docker以前から名を馳せていたコンテナ仮想化  cgroupsの産みの親  Docker  コンテナ仮想化ブームの立役者 Go言語製  1コンテナ1プロセスを想定している  LXCベースだったが途中からlibcontainerを使う用に  最近色々物議を醸している  root権限中央集権デーモン、docker pull のセキュリティ等4
  • 5. コンテナー仮想化あれこれ(Linux)  CentOS/Rocket  Dockerの諸問題を解決すべく作られたコンテナ仮想化  コンテナーのオープン仕様  systemd-nspawn  systemdの一機能として提供される仮想化機能  Rocketの裏で使われてたりする  MINCS  shell scriptで書かれたコンテナ仮想化  何をやっているのか勉強するのにとても良い5
  • 6. で、コンテナ仮想化ってなにやってるの?  Ans. 大ざっぱには、単にプロセスを起動するだけ  ただし、そのプロセスの  1. ルート(/)の差し替え (chroot / pivot_root)  2. 名前空間の分離 (unshare / clone)  3. リソースの制御 (cgroups)  をする  今回は「名前空間の分離」と「ルートの差し替え」を話します  cgroupsはスライド作成が間に合わずorz 6
  • 7. Linux名前空間  名前空間の種類  ipc: 共有メモリやメッセージキューなど  mnt: マウントポイント  net: ネットワーク全般  pid: プロセスID  uts: ホスト/ドメイン名  user: uid / gid (>=3.8)  RHEL7では未サポート(Kernel自体は3.10)  今回はスキップ (面白い機能なのですが……)7
  • 8. プロセスの名前空間  各プロセスはなにかしらの名前空間に属している  forkした場合には基本的には親の名前空間を引き継ぐ  clone(2) / unshare(2) など新しい名前空間を作ることが出来る  コマンドラインで遊ぶ場合にはunshare(1)  /proc/${PID}/ns 以下のシンボリックリンクのリンク先で区別できる 8 $ ll /proc/$$/ns 合計 0 lrwxrwxrwx. 1 takei takei 0 10月 17 20:57 ipc -> ipc:[4026531839] lrwxrwxrwx. 1 takei takei 0 10月 17 20:57 mnt -> mnt:[4026531840] lrwxrwxrwx. 1 takei takei 0 10月 17 20:57 net -> net:[4026531992] lrwxrwxrwx. 1 takei takei 0 10月 17 20:57 pid -> pid:[4026531836] lrwxrwxrwx. 1 takei takei 0 10月 17 20:57 uts -> uts:[4026531838]
  • 9. mnt名前空間  unshareコマンドに --mount オプションを付けると、新しいmount名 前空間に属するプロセスを作ることが出来る。  mount名前空間を分離させると、プロセスごとに異なるマウント ポイントを持てる (e.g. PrivateTemp) 9 $ readlink /proc/$$/ns/mnt # 元のmount名前空間の確認 mnt:[4026531840] $ sudo unshare --mount /bin/bash # 新しいmount名前空間の作成 # readlink /proc/$$/ns/mnt # 新しいmount名前空間の確認 mnt:[4026532249] # mkdir mnt; mount -t tmpfs tmpfs mnt # mount # マウントポイントの確認 # exit $ mount # 元のマウントポイントの確認
  • 10. mnt名前空間 - 余談: マウントプロパゲーション  マウントイベントの伝播を制御する仕組み  あるマウントポイント以下へのmount/unmountのイベントを、 関係するマウントポイントに伝播するかどうか。  shared(双方向) slave(一方向) private(伝播しない) 10 $ mkdir src dest src/{master,slave} # mount --bind src dest # srcをdestにbindする # mount --make-slave dest # masterからslaveへは伝播する # mount -t tmpfs tmpfs src/master # マウント元(src)へのマウント # mount -t tmpfs tmpfs dest/slave # マウント先(dest)へのマウント $ mount tmpfs on /home/alice/src/master type tmpfs (rw,relatime,seclabel) tmpfs on /home/alice/dest/master type tmpfs (rw,relatime,seclabel) tmpfs on /home/alice/dest/slave type tmpfs (rw,relatime,seclabel)
  • 11. mnt名前空間 - 余談: マウントプロパゲーション  マウントプロパゲーションはmnt名前空間を貫通する  もし名前空間で完全に区切られると……。  → 例えばプロセス独自/tmpをマウントするために名前空間を分 離してデーモンを起動  → ユーザCD-ROMをパソコンにいれる  → CD-ROMが/mediaとかに自動マウントされる  → デーモンは別mnt名前空間にいるのでマウントされない  → デーモンさん(´・ω・) CD読めない……。  → ディレクトリごとに、共有するしないが選べる!11
  • 12. mnt名前空間 - 余談: マウントプロパゲーション  RHEL7ではデフォルトで / がshared (というかsystemdのせい)  mnt名前空間で遊ぶ場合、そもそも root(/) を切り替えるか、 一 旦マウントプロパゲーションをprivateにする必要あり 12 $ sudo unshare --mount /bin/bash # 新しいmount名前空間の作成 # mkdir mnt # mount --make-private / # プロパゲーションを切る # mount -t tmpfs tmpfs mnt # mount --make-shared / # 元に戻す # mount # マウントポイントの確認 # exit $ mount # 元のマウントポイントの確認
  • 13. pid名前空間  unshare --pid --fork で新しいpid名前空間でプロセスを起動  新しいプロセスを作るために --fork が必須  新しいプロセスが pid=1となり、他のプロセスは見られなくなる  元のpid名前空間(親)からは新しいpid名前空間の中身は見える  ※ ps(1)コマンドなどはprocファイルシステムを参照する  mount -t proc proc /proc でマウントしなおせばok  ただしmnt名前空間を分離していないと他のプロセスの/proc まで上書きマウントされるので注意。  --mount-proc オプションでこの辺を全部やってくれる13
  • 14. uts名前空間  unshare --uts ホスト名やドメイン名の新しい名前空間を作成  /etc/hostnameなどファイルを書き換えるようなホスト名の指定は当 然ファイルを書き換えるので注意。(chrootやmntnsなど必要) 14 $ hostname ip-172-31-13-102.ap-northeast-1.compute.internal $ sudo unshare --uts # hostname wonderland # hostname wonderland # logout $ hostname ip-172-31-13-102.ap-northeast-1.compute.internal
  • 15. net名前空間  unshare --net で新しいnet名前空間でプロセスを起動  ただし ip(1) を使った方が名前付きで作れるのでおすすめ  /var/run/netns 以下に指定された名前でprocfsがマウントされる 15 $ sudo ip netns add test # testという名前でnetnsを作成 $ sudo ip netns list # 名前空間一覧 test $ sudo ip netns exec test /bin/bash # test名前空間でプロセスを起動 # readlink /proc/$$/ns/net # netns確認 net:[4026532219] # ls -li /var/run/netns/test # /var/run/netns以下にファイルができる 4026532219 -r--r--r--. 1 root root 0 Oct 18 03:02 /run/netns/test # ip addr # loインターフェースしか見えない 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  • 16. net名前空間 - 余談: veth  通信するために仮想ethインタフェース(veth)を作って遊んでみる  1. vethペア(master/slave)の作成とnetnsの設定 16 $ sudo ip link add name master type veth peer name slave # vethペア作成 $ sudo ip addr # 確認 6: slave: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000 link/ether 3a:64:e8:80:03:5f brd ff:ff:ff:ff:ff:ff 7: master: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000 link/ether 86:cf:cc:26:74:e4 brd ff:ff:ff:ff:ff:ff $ sudo ip link set slave netns test # 片方をnetns testに移動 $ sudo ip addr # インターフェースの確認 7: master: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000 link/ether 86:cf:cc:26:74:e4 brd ff:ff:ff:ff:ff:ff $ sudo ip netns exec test ip addr 6: slave: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000 link/ether 3a:64:e8:80:03:5f brd ff:ff:ff:ff:ff:ff
  • 17. net名前空間 - 余談: veth  通信するために仮想ethインタフェース(veth)を作って遊んでみる  2. IPアドレス割り振り & 疎通確認 17 $ sudo ip addr add 192.168.50.101/24 dev master # masterにIPアドレス設定 $ sudo ip link set dev master up # リンクアップ $ sudo ip netns exec test /bin/bash # 名前空間内にbash起動 # ip addr add 192.168.50.102/24 dev slave # slaveにIPアドレス設定 # ip link set dev slave up # リンクアップ # ping 192.168.50.101 -c1 # 疎通確認 PING 192.168.50.101 (192.168.50.101) 56(84) bytes of data. 64 bytes from 192.168.50.101: icmp_seq=1 ttl=64 time=0.047 ms # exit $ ping 192.168.50.102 -c1
  • 18. net名前空間 - 余談: veth  通信するために仮想ethインタフェース(veth)を作って遊んでみる  3. IPマスカレードの設定 & 外部ネットーワークへの疎通確認 18 $ sudo ip netns exec test /bin/bash # ip route add default via 192.168.50.101 dev slave # default gw の設定 # ip route default via 192.168.50.101 dev slave 192.168.50.0/24 dev slave proto kernel scope link src 192.168.50.102 # exit $ # IPマスカレードの設定 $ sudo iptables -t nat -A POSTROUTING -s 192.168.50.0/24 -o eth0 -j MASQUERADE $ sudo ip netns exec test /bin/bash # ping 8.8.8.8 -c1 # 外部への疎通確認 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=55 time=2.18 ms
  • 19. Linux名前空間  名前空間の種類  ipc: 共有メモリやメッセージキューなど  mnt: マウントポイント  net: ネットワーク全般  pid: プロセスID  uts: ホスト/ドメイン名  user: uid / gid  ユーザごとにユーザ一覧を持てる  一般ユーザなのにrootっぽいユーザも作れる!19
  • 20. chroot  古の昔からある仮想化(?)  root(/)を張り替えた状態でプロセスを起動する  元々はビルドシステムをクリーンな / で実行する目的で作られた  システムファイルを隠せるのでセキュリティ目的に使われることも  新しいrootにもシステム(binやlib等)が必要なのでchrootするためには OSのイメージなど(Gentooのstage3とか)が必要  RHEL系の場合、installrootを指定してCore等を入れ直せばok 20 mkdir new-root sudo yum -y --releasever=7Server --installroot=${PWD}/new-root install @Core @Base redhat-release-server vim-enhanced
  • 21. pivot_root  chrootに似た機能  ディレクトリをルートにしてプロセスを起動するchrootに対して  ルートファイルシステムを入れ替えることでルートを変える  マウントポイントを変更するので要 mnt名前空間  旧ルートは新ルートのサブディレクトリにマウントされる 21 $ sudo unshare -m -p -f /bin/bash # pid/mnt名前空間切り離し # mount --make-rprivate / # プロパゲーションoff # mount -o loop /root.img /mnt/new-root/ # 新しいrootをマウント # cd /mnt/new-root/ # mkdir .old # 旧rootのマウントポイント # pivot_root . .old # pivot! # ls /.old # 旧rootの中身をみてみる
  • 22. pivot_rootとchroot  pivot_rootはマウントポイントを差し替えるだけなので、新しいルー トのshellをexecし直したり、chrootするなりする必要アリ  古いrootをunmountするためには pts やらバイナリやらの張り直 し、exec し直しが必要になる  MINCSの場合、pivot_rootを二回(ルートの変更、旧ルートの移 動)したあとでchrootしている  ディレクトリやデバイスひとつが、1つの環境になる  → コンテナを複数作りたい場合には、複数デバイスやOSイメー ジが必要……? 22
  • 23. chroot - 余談: overlayfs  ディレクトリを「重ねて」マウントできるfs  変更は「上」にだけ保存され「下」には影響を与えない  基準となるイメージを共有しつつchrootごとに差分を適応できる 23 $ mkdir upper work # 差分ディレクトリと作業用ディレクトリ $ sudo mount -t overlay -o lowerdir=/,upperdir=upper,workdir=work overlayfs new-root $ touch /home/alice/file1 new-root/home/alice/file2 $ ls -l new-root/home/alice/file* # 両方とも見える -rw-rw-r--. 1 alice alice 0 Oct 18 12:30 new-root/home/alice/file1 -rw-rw-r--. 1 alice alice 0 Oct 18 12:30 new-root/home/alice/file2 $ rm new-root/home/alice/file1 # ファイルを消してみる $ ll upper/home/alice/file* # 差分がupperにだけ残る c---------. 1 alice alice 0, 0 Oct 18 12:32 upper/home/alice/file1
  • 24. chroot - 余談: thin provisioning(device mapper)  device mapperの一機能: thin porvisioning (dm-thin)  ディスク領域のプーリングとスナップショットの機能を持つ  プーリング: 必要に応じてストレージプールから一部分を取 り出し、一意なidを振って管理する  スナップショット: 切り出してきたストレージパッチを使い CoW(Copy On Write)差分スナップショットを提供する  LVMの皮をかぶせて、容量変更やスナップショットを、ヴォリ ュームレベルで提供できる  RHELのDockerのデフォルトでは生dm-thinを使っている 24
  • 25. chroot - 余談: thin provisioning(device mapper)  手作業でいじるのは面倒なのでDockerの挙動を追ってみる  1. loop backにdm-thinのpoolを割り当てる  実体は /var/lib/docker/devicemapper/devicemapper/{,meta}data 25 $ sudo systemctl start docker # dockerデーモンの起動 $ losetup # loopにpoolが割り当てられる NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE /dev/loop0 0 0 1 0 /var/lib/docker/devicemapper/devicemapper/data /dev/loop1 0 0 1 0 /var/lib/docker/devicemapper/devicemapper/metadata $ sudo ls -hl /var/lib/docker/devicemapper/devicemapper/ # 100Gと2G (デフォルト値) total 4.5G -rw-------. 1 root root 100G Oct 19 04:54 data -rw-------. 1 root root 2.0G Oct 19 04:56 metadata
  • 26. chroot - 余談: thin provisioning(device mapper)  (続き) デーモン起動時: スパースファイル  poolはRHEL7デフォルトだとスパースファイルで定義される  ただし、スパースファイルは断片化しやすく、loopbackマウン トも性能もよろしくないのでRHEL7的には本番環境では非推奨  → dm-thin用のLVを割り当てよう。 26 $ sudo du -h /var/lib/docker/devicemapper/devicemapper/data 4.4G/var/lib/docker/devicemapper/devicemapper/data $ sudo ls -lh /var/lib/docker/devicemapper/devicemapper/data -rw-------. 1 root root 100G Oct 19 04:54 /var/lib/docker/devicemapper/devicemapper/data $ fallocate -o 9223372036854775807 -l 1 huge # スパースファイルはfallocateで作れる $ ls -lh huge; du -h huge # 8EB(エクサバイト)!! -rw-r--r--. 1 alice alice 8.0E Oct 19 05:10 huge
  • 27. chroot - 余談: thin provisioning(device mapper)  2.コンテナの起動  10Gずつ切り出されてコンテナ用に使われる(RHEL7 default) 27 $ docker run -d centos:centos7 /sbin/init # 適当なコンテナを起動 $ docker ps # コンテナーIDの確認 CONTAINER ID IMAGE COMMAND ... b90ed5b981ae centos:centos7 "/sbin/init" ... $ lsblk # ブロックデバイス一覧を確認 NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 30G 0 disk ├─xvda1 202:1 0 1M 0 part └─xvda2 202:2 0 30G 0 part / loop0 7:0 0 100G 0 loop └─docker-202:2-62765-pool 253:0 0 100G 0 dm └─docker-202:2-62765-b90ed5b981ae9d06...ee67 253:1 0 10G 0 dm loop1 7:1 0 2G 0 loop └─docker-202:2-62765-pool 253:0 0 100G 0 dm
  • 28. chroot - 余談: thin provisioning(device mapper)  3. runされていないイメージの情報 28 $ # タグ付きイメージのidの確認 $ sudo jq . /var/lib/docker/repositories-devicemapper { "Repositories": { "test": { "latest": "a02698bf3...e5c42b" } }, "ConfirmDefPush": true } $ # イメージのdm-thinデバイス情報 $ sudo jq . /var/lib/docker/devicemapper/metadata/a02698bf3...e5c42b { "device_id": 352, "size": 10737418240, "transaction_id": 582,
  • 29. chroot - 余談: thin provisioning(device mapper)  (おまけ) dockerのイメージをマウントしてみる 29 $ # device_idとsizeを確認 $ sudo jq . /var/lib/docker/devicemapper/metadata/a02698bf...5c42b ... "device_id": 352, "size": 10737418240, ... $ # プール名を確認 $ lsblk loop0 └─docker-202:2-62765-pool $ # dmデバイスの準備 $ sudo dmsetup create dockervol --table "0 $((10737418240 / 512)) thin /dev/mapper/docker-202:2-62765-pool 352" $ # dmデバイスの確認とマウント $ ll /dev/mapper/dockervol lrwxrwxrwx. 1 root root 7 Oct 19 06:10 /dev/mapper/dockervol -> ../dm-3 $ mkdir mnt
  • 30. chroot - 余談: thin provisioning(device mapper)  (続き) dockerのイメージをマウントしてみる 30 $ ll mnt/ # 早速中を見てみる total 24 -rw-------. 1 root root 64 Aug 26 23:08 id drwx------. 2 root root 16384 Aug 26 22:58 lost+found $ ll mnt/rootfs/ # dockerのディスクイメージの中身(OS) total 64 lrwxrwxrwx. 1 root root 7 Jun 18 08:34 bin -> usr/bin drwxr-xr-x. 3 root root 4096 Oct 18 12:56 boot : $ sudo cat mnt/id # idはオリジナルのディスクイメージのidらしい f1b10cd842498c23d206ee0cbeaa9de8d2ae09ff3c7af2723a9e337a6965d639 $ docker history test:latest IMAGE CREATED CREATED BY ... a02698bf3120 17 hours ago /bin/sh -c yum install -y httpd a6673f7926d7 7 weeks ago /bin/sh -c #(nop) MAINTAINER TAKEI Yuya <take 7322fbe74aa5 4 months ago /bin/sh -c #(nop) CMD ["/bin/bash"]
  • 31. まとめ  コンテナ仮想化?  → ただの名前空間・ルート(・リソース)の切り離しだ!  名前空間は、OSにつき一つしかないものを分離する機構  unshareで分離できる!  ルートはchrootやpivot_rootで切れる  複数の環境を管理するためにoverlayfsやdm-thinが便利  リソースはcgroups(コントロールグループ)で管理できる  今回は資料が間に合いませんでしたorz 31
  • 32. 参考URL  Red Hat Enterprise Linux 7 - 製品マニュアル  https://access.redhat.com/site/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/index.html  IBM developerWorks - マウント名前空間を適用する  http://www.ibm.com/developerworks/jp/linux/library/l-mount-namespaces.html  github - MINCS  https://github.com/mhiramat/mincs  TenForwardの日記 - シェルスクリプトで書かれた軽量コンテナ MINCS がすばらしい (1)  http://d.hatena.ne.jp/defiant/20150701/1435749116  めもめも - Dockerのネットワーク管理とnetnsの関係  http://enakai00.hatenablog.com/entry/20140424/1398321672  めもめも - RHEL7におけるDockerのディスクイメージ管理方式  http://enakai00.hatenablog.com/entry/20140420/1397981156  paiza開発日誌 - 知らぬはエンジニアの恥。今さら聞けない【コンテナ/仮想化技術】11選  http://paiza.hatenablog.com/entry/2014/10/21/知らぬはエンジニアの恥%E3%80%82今さら聞けない【コン 32

Editor's Notes

  1. FreeBSD: Jail Solaris : Solaris Containers (2005) Docker(2013)
  2. http://rhelblog.redhat.com/2015/07/07/whats-next-for-containers-user-namespaces/ That said, Red Hat disabled them, because we think that user namespaces need to incubate in the upstream community longer to fully understand the security implications and mitigate/remediate any exploits/attack vectors that could expose our customers to malicious activity. Put differently, as with all of Red Hat’s enterprise products, including our solutions that focus on Linux containers (like Red Hat Enterprise Linux Atomic Host and OpenShift Enterprise 3), we don’t enable features until we are sure that they are ready for enterprise use.
  3. docker case: br0 を作る、けど物理NICにmasterを繋がない NATでごり押しる
  4. http://rhelblog.redhat.com/2015/07/07/whats-next-for-containers-user-namespaces/ That said, Red Hat disabled them, because we think that user namespaces need to incubate in the upstream community longer to fully understand the security implications and mitigate/remediate any exploits/attack vectors that could expose our customers to malicious activity. Put differently, as with all of Red Hat’s enterprise products, including our solutions that focus on Linux containers (like Red Hat Enterprise Linux Atomic Host and OpenShift Enterprise 3), we don’t enable features until we are sure that they are ready for enterprise use.
  5. 1982年3月18日のFree BSD実装が初出
  6. TECH PREVIEW: Overlay filesystem may not be fully supported Red Hat Enterprise Linux 7.1 では OverlayFS はテクノロジープレビューとしての対応になります。現在、以下の 2 つの制限があります。 下部のファイルシステムには ext4 の使用を推奨しています。 xfs および gfs2 ファイルシステムには対応していません。 - SELinux には対応していないため、OverlayFS を使用する場合は enforcing モードを無効にしてください。
  7. LVMから使う場合は--thinオプションつけるだけ