すごく分かるwarden

35
NTT Software Innovation Center すごく分かるWarden 岩嵜 雄大 NTT Software Innovation Center 2012-07-26

description

Cloud Foundryで用いられるコンテナシステム「Warden」の動作について、ソースコードを交えた紹介を行います。第8回CloudFoundry輪読会で発表を行いました:http://atnd.org/events/30614

Transcript of すごく分かるwarden

Page 1: すごく分かるwarden

NTT Software Innovation Center

すごく分かるWarden

岩嵜 雄大

NTT Software Innovation Center

2012-07-26

Page 2: すごく分かるwarden

NTT Software Innovation Center

Warden(ウォードン)とは

DEA上でアプリケーションを隔離する仕組み

2012-07-26 2

https://github.com/cloudfoundry/warden

Page 3: すごく分かるwarden

NTT Software Innovation Center

Outline

なぜWardenが必要なのか

Wardenの動作原理

2012-07-26 3

Page 4: すごく分かるwarden

NTT Software Innovation Center

なぜWardenが必要なのか

2012-07-26 4

Page 5: すごく分かるwarden

NTT Software Innovation Center

なぜWardenが必要なのか

Appはユーザが自由に開発する

– 悪意のあるAppの可能性もある

現在はUNIX UserによりAppを隔離

2012-07-26 5

DEAホスト(OS)

App App App

ユーザ権限で動作

Page 6: すごく分かるwarden

NTT Software Innovation Center

Unix Userの問題点

脆弱性に弱い

– 特権昇格

他のAppの影響を受ける

– CPU、メモリ、IO、etc.

情報が他Appに伝わる

– PID、CPU、メモリ、etc.

– 気持ち悪い

2012-07-26 6

DEAホスト(OS)

App App App

DEAホスト(OS)

App App

App

DEAホスト(OS)

App App App

Page 7: すごく分かるwarden

NTT Software Innovation Center

求められるもの

App環境の隔離

リソース制限の導入

操作用API

2012-07-26 7

DEAホスト

App App App

一方通行

制限

Page 8: すごく分かるwarden

NTT Software Innovation Center

FAQ

AppごとにVM建てればいいのでは?

– パフォーマンス的に難しい

chroot jailではダメ?

– マウントポイントしか隠ぺいできない

LXCではダメ?

– 初期のWardenはLXCを利用していたが…

– Linuxでしか動かない

– 機能過多

2012-07-26 8

Page 9: すごく分かるwarden

NTT Software Innovation Center

Wardenの動作原理

2012-07-26 9

Page 10: すごく分かるwarden

NTT Software Innovation Center

基本方針

コンテナ

– 環境の隔離:Namespaces

– リソース制限:Cgroups

– /sbin/init起動によるシステムコンテナ

操作用API

– Wardenサーバがコンテナを管理

– Warden Protocl経由でサーバにアクセス

2012-07-26 10

Page 11: すごく分かるwarden

NTT Software Innovation Center

Cgroups

Linux Kernel 2.6.24から

プロセスをグループに分離

– メモリ使用量、CPU・IOの優先度

2012-07-26 11

書いてあるよ!

Page 12: すごく分かるwarden

NTT Software Innovation Center

Namepaces

プロセスの名前空間を分離

– PID、Network, Mount、UTS、IPC、User

– unshare(1), clone(2)

マウント状況も分離される

– 高級版chroot jail

ネットワークも分離される

– 仮想NIC(veth)を使用

2012-07-26 12

Page 13: すごく分かるwarden

NTT Software Innovation Center

Wardenの概要

2012-07-26 13

EMサーバ (Ruby)

Warden クライアント

Linux クラス

シェル スクリプト群

clone.c DEA

②コンテナの起動

sshd

OS

ジョブ

コンテナ

①コンテナの生成

③ジョブの起動

Page 14: すごく分かるwarden

NTT Software Innovation Center

サーバ本体

2012-07-26 14

EMサーバ (Ruby)

Warden クライアント

Linux クラス

シェル スクリプト群

clone.c DEA

②コンテナの起動

sshd

OS

ジョブ

コンテナ

①コンテナの生成

③ジョブの起動

Page 15: すごく分かるwarden

NTT Software Innovation Center

サーバ本体

/lib/warden

命令を受け取ってコンテナの操作を行う

– EMがListen

– OS・環境ごとのクラスに委譲

Linuxクラス

– シェルスクリプトに処理を委託

2012-07-26 15

Page 16: すごく分かるwarden

NTT Software Innovation Center

サーバ本体

/lib/warden/container/linux.rb

2012-07-26 16

def do_create sh "#{env_command} #{root_path}/create.sh #{handle}", :timeout => nil debug "container created" write_bind_mount_commands debug "wrote bind mount commands" sh "#{container_path}/start.sh", :timeout => nil debug "container started" end

コンテナの生成

コンテナの起動 (コンテナは常時起動)

Page 17: すごく分かるwarden

NTT Software Innovation Center

シェルスクリプト部分

2012-07-26 17

EMサーバ (Ruby)

Warden クライアント

Linux クラス

シェル スクリプト群

clone.c DEA

②コンテナの起動

sshd

OS

ジョブ

コンテナ

①コンテナの生成

③ジョブの起動

Page 18: すごく分かるwarden

NTT Software Innovation Center

シェルスクリプト部分

/root/linux

コンテナの生成・削除

Skeleton

– ファイルがコンテナごとにコピーされる

2012-07-26 18

Page 19: すごく分かるwarden

NTT Software Innovation Center

シェルスクリプト部分(コンテナ生成)

/root/linux/create.sh

– スケルトンをコピー

– マウント名前空間をunshareしてsetup.shを実行

/root/linux/skeleton/setup.sh

– 設定を/etc内に書きだす

– /etc/ssh, /etc/init.d, /etc/hosts, etc.

2012-07-26 19

# インスタンスごとのディレクトリ

target="instances/${1}" mkdir -p instances … cp -r skeleton "${target}“ unshare -m "${target}"/setup.sh

Page 20: すごく分かるwarden

NTT Software Innovation Center

シェルスクリプト部分(コンテナ起動)

/root/linux/skeleton/start.sh

– network名前空間をunshareしてclone.cを実行

2012-07-26 20

env -i unshare -n ../../../../src/clone/clone

Page 21: すごく分かるwarden

NTT Software Innovation Center

シェルスクリプト部分

2012-07-26 21

EMサーバ (Ruby)

Warden クライアント

Linux クラス

シェル スクリプト群

clone.c DEA

②コンテナの起動

sshd

OS

ジョブ

コンテナ

①コンテナの生成

③ジョブの起動

Page 22: すごく分かるwarden

NTT Software Innovation Center

clone.c

/src/clone/clone.c

システムコールによるコンテナ生成

– clone(2)

2012-07-26 22

Page 23: すごく分かるwarden

NTT Software Innovation Center

clone.c

/src/clone/clone.c

2012-07-26 23

rv = unshare(CLONE_NEWNS); … # clone前のフック # コンテナ用のファイルシステムをマウントする

rv = run("./hook-parent-before-clone.sh"); … # コンテナ内のinitプロセスを起動する

rv = parent_clone_child(h); … # clone後のフック

rv = run("./hook-parent-after-clone.sh");

Page 24: すごく分かるwarden

NTT Software Innovation Center

clone.c

/src/clone/clone.c

2012-07-26 24

rv = unshare(CLONE_NEWNS); … # clone前のフック # コンテナ用のファイルシステムをマウントする

rv = run("./hook-parent-before-clone.sh"); … # コンテナ内のinitプロセスを起動する

rv = parent_clone_child(h); … # clone後のフック

rv = run("./hook-parent-after-clone.sh");

Page 25: すごく分かるwarden

NTT Software Innovation Center

skeletonに寄り道

/root/linux/skeleton/hook-parent-before-clone.sh

/root/linux/skeleton/common.sh:setup_fs()

– コンテナごとのファイルをループバックマウント

– OSが入っているベースにオーバーレイ

•ベースは書き込み禁止状態で共有

2012-07-26 25

setup_fs

if [ ! -f fs ]; then dd if=/dev/null of=fs bs=1M seek=${disk_size_mb} … fi mkdir -p rootfs ${target} mount -n -o loop fs rootfs … mount -n -t overlayfs -o rw,upperdir=rootfs,lowerdir=../../base/rootfs none ${target}

Page 26: すごく分かるwarden

NTT Software Innovation Center

clone.c

/src/clone/clone.c

2012-07-26 26

rv = unshare(CLONE_NEWNS); … # clone前のフック # コンテナ用のファイルシステムをマウントする

rv = run("./hook-parent-before-clone.sh"); … # コンテナ内のinitプロセスを起動する

rv = parent_clone_child(h); … # clone後のフック

rv = run("./hook-parent-after-clone.sh");

Page 27: すごく分かるwarden

NTT Software Innovation Center

clone.c

2012-07-26 27

int parent_clone_child(clone_helper_t *h) { … # 名前空間分離の設定

/* Setup namespaces */ flags |= CLONE_NEWIPC; flags |= CLONE_NEWNET; flags |= CLONE_NEWNS; flags |= CLONE_NEWPID; flags |= CLONE_NEWUTS; # start()から子プロセス起動

pid = clone(start, stack, flags, h); … }

Page 28: すごく分かるwarden

NTT Software Innovation Center

clone.c

2012-07-26 28

int start(void *data) { … rv = run(hook_before_pivot … rv = run(hook_after_pivot); … # /sbin/initをexecvpして起動完了

char * const argv[] = { "/sbin/init", "--debug", NULL }; execvp(argv[0], argv); … }

Page 29: すごく分かるwarden

NTT Software Innovation Center

clone.c

/src/clone/clone.c

2012-07-26 29

rv = unshare(CLONE_NEWNS); … # clone前のフック # コンテナ用のファイルシステムをマウントする

rv = run("./hook-parent-before-clone.sh"); … # コンテナ内のinitプロセスを起動する

rv = parent_clone_child(h); … # clone後のフック

rv = run("./hook-parent-after-clone.sh");

Page 30: すごく分かるwarden

NTT Software Innovation Center

skeletonに寄り道

/root/linux/skeleton/hook-parent-after-clone.sh

2012-07-26 30

# 新しいcgroupを作って

mkdir -p /dev/cgroup/instance-${id} pushd /dev/cgroup/instance-${id} > /dev/null # 上限などを決め

cat ../cpuset.cpus > cpuset.cpus cat ../cpuset.mems > cpuset.mems # 子プロセスに値をコピーするコールバックを呼ぶ設定

echo 1 > cgroup.clone_children # cgroupsに現在のプロセスを登録

echo ${PID} > tasks

Page 31: すごく分かるwarden

NTT Software Innovation Center

ジョブの起動

2012-07-26 31

EMサーバ (Ruby)

Warden クライアント

Linux クラス

シェル スクリプト群

clone.c DEA

②コンテナの起動

sshd

OS

ジョブ

コンテナ

①コンテナの生成

③ジョブの起動

Page 32: すごく分かるwarden

NTT Software Innovation Center

ファイルの送受信とコマンドの実行

DEA用の特別な仕掛けは見当たらない

– ファイルの送受信(rsync)

– コマンドの実行(ssh)

•ジョブとして入出力を管理

2012-07-26 32

Page 33: すごく分かるwarden

NTT Software Innovation Center

ジョブの起動

/lib/warden/container/linux.rb

2012-07-26 33

def create_job(request) user = request.privileged ? "root" : "vcap" # -T: Never request a TTY # -F: Use configuration from <container_path>/ssh/ssh_config args = ["-T", "-F", File.join(container_path, "ssh", "ssh_config"), "#{user}@container"] args << { :input => request.script } child = DeferredChild.new("ssh", *args) …

Page 34: すごく分かるwarden

NTT Software Innovation Center

ファイルの送受信

/lib/warden/container/linux.rb

2012-07-26 34

def do_copy_in(request, response) src_path = request.src_path dst_path = request.dst_path perform_rsync(src_path, "vcap@container:#{dst_path}") nil end def do_copy_out(request, response) src_path = request.src_path dst_path = request.dst_path perform_rsync("vcap@container:#{src_path}", dst_path) if request.owner sh "chown", "-R", request.owner, dst_path end nil end private def perform_rsync(src_path, dst_path) ssh_config_path = File.join(container_path, "ssh", "ssh_config") # Build arguments args = ["rsync"] args += ["-e", "ssh -T -F #{ssh_config_path}"] args += ["-r"] # Recursive copy args += ["-p"] # Preserve permissions args += ["--links"] # Preserve symlinks args += [src_path, dst_path] # Add option hash args << { :timeout => nil } sh *args end

Page 35: すごく分かるwarden

NTT Software Innovation Center

まとめ

WardenはApp隔離用のコンテナ

LinuxではCgroupsとNamespacesを使用

サーバがコンテナの操作を行う

ファイルを転送してコマンドを実行する

2012-07-26 35