Linuxカーネルもくもく会#21に参加してきました
Linuxカーネルもくもく会 #21に参加してきました。iij/ipgenというパケットジェネレータで使用されている、高速パケットI/Oフレームワークの"netmap"に興味が湧きつつあり、少しずつソースコードを読み進めています。 今回と前回のもくもく会でnetmapのLinux実装部分のコードを読んでみたので、把握できた内容を簡単にまとめてみました。
netmapのコードはGitHubから取得できます。現時点ではLinux,FreeBSD,Windows環境向けにnetmapが実装されています。ちなみにnetmapは当初FreeBSDで開発されていたこともあり、netmapのコードはFreeBSDカーネルにマージされています。
Linuxにおけるopen("/dev/netmap",...)処理
さっそくLinuxにおけるnetmapの実装を見てみます。netmapのソースコードには内部構造の詳しい解説コメントが記載されており、実装の理解の助けになります。例えばnetmap.cには以下のコメントがあり、Linuxではlinux_netmap_open()から見て行くと良さそうです。
sys/dev/netmap/netmap.c /* --- internals ---- * * Roadmap to the code that implements the above. * * > 1. a process/thread issues one or more open() on /dev/netmap, to create * > select()able file descriptor on which events are reported. * * Internally, we allocate a netmap_priv_d structure, that will be * initialized on ioctl(NIOCREGIF). There is one netmap_priv_d * structure for each open(). * * os-specific: * FreeBSD: see netmap_open() (netmap_freebsd.c) * linux: see linux_netmap_open() (netmap_linux.c)
処理の流れを把握するため、linux_netmap_open()の関数呼び出し階層を示します。
LINUX/netmap_linux.c: 1057 static struct file_operations netmap_fops = { 1058 .owner = THIS_MODULE, 1059 .open = linux_netmap_open, 1060 .mmap = linux_netmap_mmap, 1061 LIN_IOCTL_NAME = linux_netmap_ioctl, 1062 .poll = linux_netmap_poll, 1063 .release = linux_netmap_release, 1064 };
関数の呼び出し階層は以下のようになっています。
-> LINUX/netmap_linux.c:linux_netmap_open() -> sys/dev/netmap/netmap.c:netmap_priv_new() -> LINUX/netmap_linux.c:nm_os_get_module()
linux_netmap_open()は帰り値として常に0を返すようです。変数errorは握りつぶされている気がする...。
1036 static int 1037 linux_netmap_open(struct inode *inode, struct file *file) 1038 { 1039 struct netmap_priv_d *priv; 1040 int error; 1041 (void)inode; /* UNUSED */ 1042 1043 NMG_LOCK(); 1044 priv = netmap_priv_new(); 1045 if (priv == NULL) { 1046 error = -ENOMEM; 1047 goto out; 1048 } 1049 file->private_data = priv; 1050 out: 1051 NMG_UNLOCK(); 1052 1053 return (0); 1054 }
netmap_priv_new()はnetmap用の構造体(struct netmap_priv_d)用のカーネルメモリを確保し、linux_netmap_open()に返します。 先ほどの変数errorの値はlinux_netmap_open()から参照できなさそうですが、file->private_data == NULLの判定をエラーが発生しているかどうかは判定できそうです。
959 struct netmap_priv_d* 960 netmap_priv_new(void) 961 { 962 struct netmap_priv_d *priv; 963 964 priv = malloc(sizeof(struct netmap_priv_d), M_DEVBUF, 965 M_NOWAIT | M_ZERO); 966 if (priv == NULL) 967 return NULL; 968 priv->np_refs = 1; 969 nm_os_get_module(); 970 return priv; 971 }
nm_os_get_module()は自身のモジュールを取得するだけの処理です。
LINUX/netmap_linux.c: 63 void 64 nm_os_get_module(void) 65 { 66 __module_get(THIS_MODULE); 67 }
処理がlinux_netmap_open()に戻ってきたのち、確保したstruct netmap_priv_dへの参照をfile->private_dataに設定します。
1036 static int 1037 linux_netmap_open(struct inode *inode, struct file *file) 1038 { 1039 struct netmap_priv_d *priv; ... 1049 file->private_data = priv; 1050 out: 1051 NMG_UNLOCK(); 1052 1053 return (0); 1054 }
linux_netmap_open()の変数errorについて
linux_netmap_open()内の変数errorは握りつぶされている(呼び出し元に変数errorの値が渡らない)ように見える件、例えばLinuxのwrite()に対応する関数ではエラー値を負値にしてreturnしています。これに倣うとlinux_netmap_open()でもreturn (-ENOMEM);とするのが良いのかもしれません。
linux-4.5/fs/read_write.c: 617 SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, 618 size_t, count) 619 { 620 struct fd f = fdget_pos(fd); 621 ssize_t ret = -EBADF; 622 623 if (f.file) { 624 loff_t pos = file_pos_read(f.file); 625 ret = vfs_write(f.file, buf, count, &pos); 626 if (ret >= 0) 627 file_pos_write(f.file, pos); 628 fdput_pos(f); 629 } 630 631 return ret; 632 }
Linuxカーネルのmalloc()に対するグルーコード
netmap_priv_new()でmalloc()を呼んでいます。このmalloc()はLinuxでもBSD系と同じ引数で呼び出せるよう、ラッパーコードを挟んでいます。netmapはFreeBSDで実装されたこともあり、グルーコードの形で既存の処理を変更することなく別プラットフォームへの移植を行っている、ということですね。
sys/dev/netmap/netmap.c: 959 struct netmap_priv_d* 960 netmap_priv_new(void) 961 { ... 964 priv = malloc(sizeof(struct netmap_priv_d), M_DEVBUF, 965 M_NOWAIT | M_ZERO); 966 if (priv == NULL) 967 return NULL; ... 971 }
*_glue.hで各プラットフォーム向けのmalloc()ラッパーコードが実装されています。
$ global malloc LINUX/bsd_glue.h WINDOWS/win_glue.h
Linux/bsd_glue.hではkmalloc()へのラッパーになっています。
LINUX/bsd_glue.h: 304 /* 305 * in the malloc/free code we ignore the type 306 */ 307 /* use volatile to fix a probable compiler error on 2.6.25 */ 308 #define malloc(_size, type, flags) \ 309 ({ volatile int _v = _size; kmalloc(_v, GFP_ATOMIC | __GFP_ZERO); })
WINDOWS/win_glue.hではwin_kernel_malloc()を挟んでExAllocatePoolWithTag APIを読んでいます。
WINDOWS/win_glue.h: 315 /*------------------------------------------- 316 * KERNEL MEMORY ALLOCATION and management 317 */ ... 321 #define malloc(size, _ty, flags) win_kernel_malloc(size, _ty, flags) ... 325 /* 326 * we default to always allocating and zeroing 327 */ 328 static void * 329 win_kernel_malloc(size_t size, int32_t ty, int flags) 330 { 331 void* mem = ExAllocatePoolWithTag(NonPagedPool, size, ty); 332 333 (void)flags; 334 335 if (mem != NULL) { // && flags & M_ZERO XXX todo 336 RtlZeroMemory(mem, size); 337 } 338 return mem; 339 }
まとめ
Linuxにおけるnetmapのopen()に関する処理を追いかけてみました。linux_netmap_open()は比較的シンプルな実装になっています。netmapのソースコードには処理の流れやデータ構造の詳しい説明がコメントの形で記載されており理解の助けになります。
Linuxカーネルもくもく会#6に参加してきました
Linuxカーネルもくもく会#6に参加してきました。この会は各自Linuxソースコードの気になる箇所を読み進めたり、Linuxに関する何かをもくもくと進める会です。
cgroup周りのソースコードを読み進めてみているので、読んでみた内容のメモをまとめてみます。linux-3.17-rc3のソースコードを対象にしています。
start_kernel()からcgroup_init_early()を読んでみる
まずはLinuxカーネルのエントリポイントであるinit/main.c:start_kernel()から読み進めてみます。
init/main.c: 500 asmlinkage __visible void __init start_kernel(void) 501 { ... 519 cgroup_init_early(); ...
cgroup_*()な関数群で最初に呼ばれているのはkernel/cgroup.c:cgroup_init_early()です。関数コメントによると、OS起動時にcgroupの初期化とearly initが必要なサブシステムの初期化を行うようです。
kernel/cgroup.c: 4895 /** 4896 * cgroup_init_early - cgroup initialization at system boot 4897 * 4898 * Initialize cgroups at system boot, and initialize any 4899 * subsystems that request early init. 4900 */ 4901 int __init cgroup_init_early(void) 4902 { 4903 static struct cgroup_sb_opts __initdata opts; 4904 struct cgroup_subsys *ss; 4905 int i; 4906 4907 init_cgroup_root(&cgrp_dfl_root, &opts); 4908 cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF; 4909 4910 RCU_INIT_POINTER(init_task.cgroups, &init_css_set); 4911 4912 for_each_subsys(ss, i) { 4913 WARN(!ss->css_alloc || !ss->css_free || ss->name || ss->id, 4914 "invalid cgroup_subsys %d:%s css_alloc=%p css_free=%p name:id=%d:%s\n", 4915 i, cgroup_subsys_name[i], ss->css_alloc, ss->css_free, 4916 ss->id, ss->name); 4917 WARN(strlen(cgroup_subsys_name[i]) > MAX_CGROUP_TYPE_NAMELEN, 4918 "cgroup_subsys_name %s too long\n", cgroup_subsys_name[i]); 4919 4920 ss->id = i; 4921 ss->name = cgroup_subsys_name[i]; 4922 4923 if (ss->early_init) 4924 cgroup_init_subsys(ss, true); 4925 } 4926 return 0; 4927 }
引数cgrp_dfl_rootはグローバル変数で同じソースファイル内で宣言されています。
kernel/cgroup.c: 139 /* 140 * The default hierarchy, reserved for the subsystems that are otherwise 141 * unattached - it never has more than a single cgroup, and all tasks are 142 * part of that cgroup. 143 */ 144 struct cgroup_root cgrp_dfl_root;
変数cgrp_dfl_rootのデータ型である、struct cgroup_rootを見てみます。
include/linux/cgroup.h: 264 /* 265 * A cgroup_root represents the root of a cgroup hierarchy, and may be 266 * associated with a kernfs_root to form an active hierarchy. This is 267 * internal to cgroup core. Don't access directly from controllers. 268 */ 269 struct cgroup_root { 270 struct kernfs_root *kf_root; 271 272 /* The bitmask of subsystems attached to this hierarchy */ 273 unsigned int subsys_mask; 274 275 /* Unique id for this hierarchy. */ 276 int hierarchy_id; 277 278 /* The root cgroup. Root is destroyed on its release. */ 279 struct cgroup cgrp; 280 281 /* Number of cgroups in the hierarchy, used only for /proc/cgroups */ 282 atomic_t nr_cgrps; 283 284 /* A list running through the active hierarchies */ 285 struct list_head root_list; 286 287 /* Hierarchy-specific flags */ 288 unsigned int flags; 289 290 /* IDs for cgroups in this hierarchy */ 291 struct idr cgroup_idr; 292 293 /* The path to use for release notifications. */ 294 char release_agent_path[PATH_MAX]; 295 296 /* The name for this hierarchy - may be empty */ 297 char name[MAX_CGROUP_ROOT_NAMELEN]; 298 };
kernel/cgroup.c:init_cgroup_root()は必要なデータ構造の初期化を行っているだけのようなので、ざっくりと眺める感じで次の処理を読み進めてみます。
kernel/cgroup.c: 1596 static void init_cgroup_root(struct cgroup_root *root, 1597 struct cgroup_sb_opts *opts) 1598 { 1599 struct cgroup *cgrp = &root->cgrp; 1600 1601 INIT_LIST_HEAD(&root->root_list); 1602 atomic_set(&root->nr_cgrps, 1); 1603 cgrp->root = root; 1604 init_cgroup_housekeeping(cgrp); 1605 idr_init(&root->cgroup_idr); 1606 1607 root->flags = opts->flags; 1608 if (opts->release_agent) 1609 strcpy(root->release_agent_path, opts->release_agent); 1610 if (opts->name) 1611 strcpy(root->name, opts->name); 1612 if (opts->cpuset_clone_children) 1613 set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags); 1614 }
init_cgroup_root()の直後で何やらフラグを設定しています。
kernel/cgroup.c: 4901 int __init cgroup_init_early(void) 4902 { ... 4907 init_cgroup_root(&cgrp_dfl_root, &opts); 4908 cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF;
CSS_NO_REFはinclude/linux/cgroup.hでenum定義されており、このCSS(Cgroup Subsys State)には参照カウンタが無いことを示しているみたいです(「参照カウンタを持たない」という意味かも)。
include/linux/cgroup.h: 96 /* bits in struct cgroup_subsys_state flags field */ 97 enum { 98 CSS_NO_REF = (1 << 0), /* no reference counting for this css */ 99 CSS_ONLINE = (1 << 1), /* between ->css_online() and ->css_offline() */ 100 CSS_RELEASED = (1 << 2), /* refcnt reached zero, released */ 101 };
cgroup_init_early()を読み進めて行きます。関数の後半にはfor_each_subsys()のループがあり、何やらsubsys的なものを順次処理しているようです。
kernel/cgroup.c: 4901 int __init cgroup_init_early(void) 4902 { ... 4912 for_each_subsys(ss, i) { ... 4920 ss->id = i; 4921 ss->name = cgroup_subsys_name[i]; 4922 4923 if (ss->early_init) 4924 cgroup_init_subsys(ss, true); 4925 } ...
for_each_subsus()はfor文に展開されるマクロだと予想できるのですが、実際どうなっているか見てみます。
kernel/cgroup.c: 376 /** 377 * for_each_subsys - iterate all enabled cgroup subsystems 378 * @ss: the iteration cursor 379 * @ssid: the index of @ss, CGROUP_SUBSYS_COUNT after reaching the end 380 */ 381 #define for_each_subsys(ss, ssid) \ 382 for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT && \ 383 (((ss) = cgroup_subsys[ssid]) || true); (ssid)++)
上記で参照しているCGROUP_SUBSYS_COUNT、cgroup_subsys_idはenum値なので、最後にCGROUP_SUBSYS_COUNTが付いています。
43 /* define the enumeration of all cgroup subsystems */ 44 #define SUBSYS(_x) _x ## _cgrp_id, 45 enum cgroup_subsys_id { 46 #include <linux/cgroup_subsys.h> 47 CGROUP_SUBSYS_COUNT, 48 }; 49 #undef SUBSYS
cgroup_init_early()の4925行目のcgroup_init_subsys()、こちらはcgroup_subsys_name[]で配列になっています。
kernel/cgroup.c: 125 /* generate an array of cgroup subsystem pointers */ 126 #define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys, 127 static struct cgroup_subsys *cgroup_subsys[] = { 128 #include <linux/cgroup_subsys.h> 129 }; 130 #undef SUBSYS 131 132 /* array of cgroup subsystem names */ 133 #define SUBSYS(_x) [_x ## _cgrp_id] = #_x, 134 static const char *cgroup_subsys_name[] = { 135 #include <linux/cgroup_subsys.h> 136 }; 137 #undef SUBSYS
linux/cgroup_subsys.hの中身を見てみます。
linux/cgroup_subsys.h: 1 /* 2 * List of cgroup subsystems. 3 * 4 * DO NOT ADD ANY SUBSYSTEM WITHOUT EXPLICIT ACKS FROM CGROUP MAINTAINERS. 5 */ 6 #if IS_ENABLED(CONFIG_CPUSETS) 7 SUBSYS(cpuset) 8 #endif 9 10 #if IS_ENABLED(CONFIG_CGROUP_SCHED) 11 SUBSYS(cpu) 12 #endif ...
kernel/cgroup.c:SUBSYS()マクロで値が列挙されています。簡単なサンプルコードで展開結果を確認してみます。
$ cat tmp.c #define SUBSYS(_x) [_x ## _cgrp_id] = #_x, SUBSYS(cpu) $ gcc -E tmp.c ... [cpu_cgrp_id] = "cpu",
微妙にややこしいのですが、上記のcpu_cgrp_idはlinux/cgroup_sybsys.h(こっちはヘッダファイル)でenum宣言された値です。
linux/cgroup_subsys.hをテンプレートのように利用し、*cgroup_sybsys[]とenum cgroup_syubsys_idを再定義したSUBSYS()マクロで作るという方法になっています。cgroupサブシステムの列挙を一つのファイルで完結させたいという気持ちは分かるのですが、知らずに読むと混乱してしまいます...。
気を取り直してcgroup_init_early()の続きを読み進めてみます。
kernel/cgroup.c: 4901 int __init cgroup_init_early(void) 4902 { ... 4912 for_each_subsys(ss, i) { ... 4920 ss->id = i; 4921 ss->name = cgroup_subsys_name[i]; 4922 4923 if (ss->early_init) 4924 cgroup_init_subsys(ss, true); 4925 } 4926 return 0; 4927 }
再度for_each_subsys()、変数ssに入るのはcgroup_sybsys[]ですね。
381 #define for_each_subsys(ss, ssid) \ 382 for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT && \ 383 (((ss) = cgroup_subsys[ssid]) || true); (ssid)++)
cgroup_subsys[]の中身を見てみます。サブシステムごとに関数ポインタを渡せるみたいですが、この段階ではidとnameの設定とearly_initが真の場合にcgroup_init_subsys()を呼ぶだけですね。
include/cgroup/cgroup.h: 623 struct cgroup_subsys { 624 struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *parent_css); 625 int (*css_online)(struct cgroup_subsys_state *css); 626 void (*css_offline)(struct cgroup_subsys_state *css); 627 void (*css_free)(struct cgroup_subsys_state *css); 628 void (*css_reset)(struct cgroup_subsys_state *css); 629 630 int (*can_attach)(struct cgroup_subsys_state *css, 631 struct cgroup_taskset *tset); 632 void (*cancel_attach)(struct cgroup_subsys_state *css, 633 struct cgroup_taskset *tset); 634 void (*attach)(struct cgroup_subsys_state *css, 635 struct cgroup_taskset *tset); 636 void (*fork)(struct task_struct *task); 637 void (*exit)(struct cgroup_subsys_state *css, 638 struct cgroup_subsys_state *old_css, 639 struct task_struct *task); 640 void (*bind)(struct cgroup_subsys_state *root_css); 641 642 int disabled; 643 int early_init; ... 660 /* the following two fields are initialized automtically during boot */ 661 int id; 662 #define MAX_CGROUP_TYPE_NAMELEN 32 663 const char *name;
続けてcgroup_init_subsys()も読み進めようと思ったのですが、このあたりでLinuxもくもく会の終了時刻に。続きは次回ですね。
kernel/cgroup.c: 4845 static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early) 4846 { 4847 struct cgroup_subsys_state *css; 4848 4849 printk(KERN_INFO "Initializing cgroup subsys %s\n", ss->name); ...
まとめ
Linuxカーネルもくもく会に参加し、cgroupソースコードを読み進めてみました。次回のもくもく会でもcgroup周りを読んでみようと思います。
「第4回 コンテナ型仮想化の情報交換会@東京」に参加してきました
「第4回 コンテナ型仮想化の情報交換会@東京」に参加してきました
第4回 コンテナ型仮想化の情報交換会@東京に参加してきました。
以下は勉強会のメモです。私の理解に間違い勘違いがあるかもしれませんのでご注意ください。
最新cgroup事情
- 発表者は@hiro_kamezawaさん
- 発表資料は以下のURLにて公開されています
私が遅れて到着してしまったため、最初の部分は聞き逃してしまいました……。
cgroup概要
(プロセス等の?)コントロールはタスク単位だが、リソースはタスク単位ではない。そのため、タスク単位でリソースを扱うのは正しいのか、という議論がある。
cgroupの自由度を高めた結果、カオスな状況になっている。そこでsane_behaviorオプションにより「制限を加えること」で正しい使い方を強制する。これはKernel3.19-3.20あたりでデフォルトで有効なオプションになるかも。
systemdの話
systemdは全てのcgroupを牛耳るというスタンス。systemdが有効なシステムでは、systemdでcgroupを作る。Unit file, DBUS APIで作る。libvirtのcgroupもsystemdを使うよう修正された。
Systemdに新たに加わったユニットタイプの一つにsliceがある。systemdの下には3つのsliceがあり、注意が必要なのはuser.sliceとsystem.slice。user.sliceの下にあるやつはsystemdが完全に掴んでいる。
systemd/src/shared/unit-name.h: 33 enum UnitType { 34 UNIT_SERVICE = 0, 35 UNIT_SOCKET, 36 UNIT_BUSNAME, 37 UNIT_TARGET, 38 UNIT_SNAPSHOT, 39 UNIT_DEVICE, 40 UNIT_MOUNT, 41 UNIT_AUTOMOUNT, 42 UNIT_SWAP, 43 UNIT_TIMER, 44 UNIT_PATH, 45 UNIT_SLICE, 46 UNIT_SCOPE, 47 _UNIT_TYPE_MAX, 48 _UNIT_TYPE_INVALID = -1 49 };
systemd/src/shared/path-lookup.h: 34 typedef enum SystemdRunningAs { 35 SYSTEMD_SYSTEM, 36 SYSTEMD_USER, 37 _SYSTEMD_RUNNING_AS_MAX, 38 _SYSTEMD_RUNNING_AS_INVALID = -1 39 } SystemdRunningAs;
systemdから設定できるパラメータは限定的。CPUパラメータに"Share"は設定可能だが"Limit"は設定不可。また、Memoryの上限を設定できてもSwapの設定はできない。現状では設定可能なパラメータを増やす予定は無い。これはLinux Kernel側のcgroupが再設計中であるため、それが一段落して安定してからAPIの追加が行われるためとのこと。
Memory cgroupの話
TCPバッファ量の設定機能は他のメモリ上限設定とは趣きが異なるが、これはNECのHPCのグループの要望によるものとのこと。メモリとスワップの上限設定があるのは、これらを制限すれば、グローバルなリソースを都度触らなくても管理できるよね、という考え方。また、swap上限の設定は、fork bomb等でswapを使いきってしまうのを防ぐ側面もある。
最近のcgroupの実装では、最初にメモリページを読んだ人に課金されている。が、Dockerの流行など考えると、ページキャッシュを占有しているとよかったね……という話も(最初にページを触った人に課金されてしまう)。
kernelメモリのアカウンティング
kernelメモリのアカウンティングは、以下の2つのケースで発生する。カーネルメモリ課金はfree()された時に減算。とはいえ、特定のmemory cgroup狙い撃ちでカーネルメモリを開放するルーチンは今のところ無い。
- SLAB/SLUBアロケータからページを割り当てた場合
- alloc_kmem_pages()を呼ぶ場合
TCP bufferのアカウンティング
TCP bufferのアカウンティング。元々システム全体でtcp bufferを制限するための仕組みがあり、これを流用している。Socketのdata用のメモリ領域をアロケートする所で判定する。
memory cgroupの面倒な所
タスクとメモリのライフサイクルが異なる場合がある。莫大な性能オーバーヘッドがあると信じられている。タスクに課金ではなくページメモリ課金なのでレースコンディションが多い。課金に関する点で見ると、ページメモリに大して課金する。race conditionを回避するためロックを使っていると性能に影響する
性能オーバーヘッドの改善方法のひとつとして、各CPU毎に課金の前借り情報を付与している。前借りなので、メモリのusageのカウント誤差を許容(memory cgroupは性能のためにカウント誤差を許容)する。cgroupの利用、BMと比較すると場合によっては3,4%性能が落ちる。メモリ解放処理はput_page()のバッチ処理の中で複数のページ文をまとめて開放する。LRUはmemory cgroup毎に持つ。システム全体のLRUは「存在しない」
今後の強化ポイント(予想)
- kernel memory cgroyupのメモり回収処理を追加
- Blkio cgroupと連動してのbufferd I/Oの制御
- Page付帯情報を16byteから8byteにする
- soft limitの再実装
- kswapd per memory cgroup
- 不揮発メモリの扱い?
- 今後の議論になってゆくと思う
質疑応答
- 質問. ドキュメントでcgroup,cgroupsの表記が揺らいでいる、理由は?
回答. OSSの悪い所。特に理由は無い。cgroupsと表記する人が多い感じ。
質問. cgroupのカーネルメモリも課金対象との話だが、これはプロセスに紐づいているもの?
- 回答. プロセスには紐づいていない。i-node等も課金される。プロセスがいなくなっても課金は継続される。基本的にはkmalloc()を(最初に)使っている人に課金される。例えば、ファイルを複数のグループで使っている場合、最初に使った人に課金される。
Using LXC on Production
- 発表者は@isaoshimizuさん
mixiのモンストスタジオに所属されている方で、「OpenStackとLXCを導入した話」を元にした発表でした。
mixiにおける仮想化環境
当初はKVM(Kernel-based Virtual Machine)だった。用途は開発・ステージング環境。構築は自作のシェルスクリプトでbridge I/F、Cobblerとの連系でホスト名の連番化やIPの重複防止、virt-install,Kickstartをやっていた。しかし、基本手作業で面倒であったとのこと。
次にOpenStackを使ってみた。用途は社内プロダクト向けのPaaS("Gizumo"という名称でたぶん水をかけると増える)。アプリサーバは独自のデプロイツールを利用。ミドルウェアの構成はChefでMySQL, Redis, Jenkins等を展開。後に個人の開発環境にも展開していった。シェルスクリプトでKVMを使っていた時期よりも楽になった。
現在の運用はLXCを利用しているとのこと。
LXC概要
KVMのようなハードウェアエミュレーション上で仮想マシンを動作させるのはなく、Kernelの機能を利用してプロセスやネットワーク、ユーザ空間を分離し、仮想的な環境を提供する。KVMのケースのようなCPU、ディスクI/O等のパフォーマンス劣化がほとんど発生しない。KVMと比べて起動が速い。
同時期にDockerの人気が出始めたようで、AUFSが気にななったり、Docker Registryが便利そうだったり、Goのポータビリティは素晴らしかったり。IPマスカレードはちょっと面倒くさい(DockerはIPマスカレード)。バージョンアップが激しい。コンテナにIPを個別に振って、仮想マシンのように扱いたい(macvlan使いたい)。taggedVLANの環境でも問題なく使いたい。ネットワーク周りの要件の兼ね合いでDockerは見送ったらしい。
独自ツールtrailerの開発
LXCにかぶせる形での独自ツール"trailer"を開発。Rubyで記述されている。現段階ではオープンソースではない。LXCのラッパーとして動作。mixi内での運用に必要な機能に絞って実装。IP,MACアドレスの採番。コンテナイメージのダウンロードと展開。起動中のコンテナからイメージを作成する。Trailerfileと呼ばれるコンテナ定義。 リポジトリサーバへのイメージアップロード。
LXC向けに用意してあるイメージ
- ベースイメージ
- Reverse Proxy(mod_proxy)
- Varnish
- Q4M(Job Queue)
- Application Server(mod_perl)
- Tokyo Tyrant
- memcached
これらを起動するとIPとMACが振られた状態で起動する。アプリケーションサーバはさらにアプリのデプロイが必要。
コンテナを作るときの注意点
スレッド、PID数の上限に注意。ファイルディスクリプタ数やTCP/IP周りのKernelパラメータも用途に応じて調整する。インスタンス側では設定できないKernelパラメータがあったりするので注意。
その他に気をつける点として、利用リソースの予測と見積りを行い、他のコンテナに悪影響を及ぼさないようにする。ディスク容量については(LXCでは)容量制限ができない。モニタリングデータのグラフ化は重要(=監視は重要)。trailerではコンテナ単体とCPUとメモリの利用量、ホスト側でのメモリ量を取得できるようにしている。
発表者によるtrailerのデモ。以下のような感じでtrailerコマンドを使用していました。
$ trailer image-list $ sudo trailer image-destroy fedora19 $ sudo trailer start --image fedora19-x86_64 --hostname container-test --dhcp --briged-interface eth0 Copying rootfs from fedora19-x86_64 ---done Starting container container-test
trailerコマンドからLXCでコンテナが起動します。アタッチしたコンテナからはCtrl-q Ctrl-aで抜けられるようです。
$ ps ax | grep container-test ...中略... lxc-start --name container-test --daemon --rcfile /data/lxc/container-test/config --lxcpath /data/lxc -o /data/lxc/container-test/log/lxc.log -l debug -p /data/lxc/container-test/container-test.pid -- /.trailerinit
trailerを利用して一つの物理マシン上にコンテナとしてサービスを集約。考え方として、複数のサービスを一つのマシンに集約するのではなく、mod_perl,memcached等のサービス毎に集約したコンテナを利用する。
質疑応答
- 質問. trailerからLXCへのアドレス付与はどう行っている?
回答. init起動した後にifconfig/ipコマンドを発行している。内部からIPを設定している。基本的には固定IPを(起動時に)動的に付与し、DHCPは使っていない。root fsの中にIPを記述した設定ファイルを置いておき、それを用いてIPを設定している。(補足:LXCの設定項目にIPアドレスの項目がある、とのこと)
質問. ディスクイメージ内のFSとして、ZFSとか使ってみたりしてますか?
回答. ディスクは気をつけていれば特に問題なく、こういったサービスでは不要かも。
質問. LXCシステムコンテナの元になるイメージ、trailerではどうやっていいますか?
回答. LXCに付属しているtemplatesを元にしてイメージ作成していますが、だいぶ変更しています。(補足:LXC 1.0でのtemplatesでだいぶ改善された)
質問. リソース監視は外から行うのですか?
回答. 外から取得している。snmpdを動かしている。コンテナ側の情報はextendと独自のスクリプトで取得している。
質問. アプリケーションコンテナの場合、ログインする(sshが必ず動いている?)
回答. sshdは全てのコンテナで立ち上げています。普通のマシンとして利用できることを想定したコンテナにしています。
質問. trailerの1ホスト上にコンテナはいくつくらい立ち上げている?
回答. 多くて6個か8個、少なくて2個。プロセスをたくさん立ち上げるやつはコンテナ数を少なくしている。
質問. LXC 0.9から1.0への以降は考えている?
回答. 考えている。lxc start以外のコマンドではうまく動かないものもあるので移行したい。が、今のところ移行の予定は無いです。
質問. プロダクション運用する際、コンテナ周りで困ったことは?
回答. スライドにもあったがPIDの問題(これは気づきやすい類の問題)。メモリ使用量は取れてもロードバランスは取りにくい等があり、監視・リソースのモニタリングは難しい。意外と大問題にはぶち当たっていない気はする。
質問. KVMからLXCに移行する際、セキュリティはどう考えた?
- 回答. 子の親殺しといった、(LXCの機能に起因する)セキュリティは意識していない。ホスティングサービスとは異なり、社内で利用するのであれば、あまり神経質になることは無いかもしれない。
LT資料 (第4回 コンテナ型仮想化の情報交換会@東京)
- 発表者は@tukiyo3さん
vagrantやLXC,Docker、OpenVZのproxmoxに関するTips集とDocker上でCentOS7のsystemctlを動作させる内容の発表でした。
個人的にはDockerでUbuntuを起動してみたことがあるだけで、Docker+CentOS 7でハマり所があるのは知りませんでした。
- 発表内容は以下で公開されています
vagrant 1.6.5(2014/9/6)でCentOS 7ゲストに対応された。/etc/yum.confのautoupdateを無効化しておくとよい。vagrantcloud(vagrant init chef/centos-7.0)を用いたvagrant shareは便利とのことです。
- boot2dockerでvagrant share使ってみた
Docker Hubはdockerhubにアカウントを作成後、"docker login", "docker push'するだけでお手軽に使い始められます、とのことです。ただ、ちょっと帯域が細く、apt-getとかが遅いようです。"docker search tukiyo3"で@tukiyo3さんのイメージが検索できます。
OpenVZのproxmoxについてはバックアップの方法が解説されており、proxmoxを使うとそのままホストOS、ゲストOS間で通信ができるため、定期的にバックアップが取れるとのことです。バックアップは一時的にOSをスリープさせているようだが、たまに復帰できない時があるという話が……。
他にもDockerのデータ永続化の方法として、cronで定期的にdocker commitを実行する方法が紹介されていました。
CoreOSによるDockerコンテナのクラスタリング
- 発表者は@yujiodさん
- PukiWikiの開発者さん(!)とのことです
- 発表スライドは以下で公開されています
- CoreOSによるDockerコンテナのクラスタリング
- http://www.slideshare.net/yujiod/coreosdocker
CoreOSはChorome OSベースのLinuxディストリビューション。単体で開発機として利用可能だが、クラスタリング構成で最も威力を発揮する。Google,Twitter,Mozilla,Suse,Cisco,Rackspace等のメンバーが開発に参加している。基本的に64bit CPUであれば動作する。IaaSではAmazon EC2,Google Compute Engine,Rackspace Cloudで動作、他にもさくらVPS,mac miniで動作する。
CoreOSの特徴
- 省メモリ
- 起動時で114MB(Dockerの動作のみに注力)
- 自動OSアップデート
- Docker専用OS
- ファイルシステムはread only
- アプリケーションはDockerで起動する必要がある
- クラスタリング機能が標準搭載
- ノードの自動構成
- コンテナのプロセス情報
- key-vlaueストア
- 分散デプロイ、フェイルオーバーの管理
- すべてGo言語で開発されている
- 自動フェイルオーバー
- 事前に「このロールのノードはn台」と定義しておく
CoreOSの構成要素
- locksmith
- OSアップデートのためのノードとupdate_engintプロセスの監視
- systemd
- コンテナを一つのUnitとして動作させられる
- 発表スライドの15枚目にsystemd unitの記述例があります
- X-Fleetというセクションが特別
- etcd
- fleet
- cloud-config
- クラスタ全体の構成管理を行う。YAMLで記述し、OSアップデートポリシーやプロセス、コンテナ管理(systemd)、他ファイルの書き込みやCoreOSのログイン(ssh)設定、hostsファイルの設定を行う
- (cloud-configの例→写真参照)
- 発表スライドの25,26枚目にデモ用のcloud-configの完全な例が提示されています。
CoreOS上のコンテナでの分散デプロイ、フェイルオーバーのデモ
以下のコマンドを投入し、CoreOS上での分散デプロイとフェイルオーバのデモがありました。
CoreOS$ # CoreOSにログインして以下を実行 CoreOS$ fleectl list-machine CoreOS$ fleectl list-unit CoreOS$ fleectl submit busybox\@{1,2}.service CoreOS$ fleectl start busybox@*.service
デモの補足として、クラスタを組む際にはフェイルオーバーを前提にすること。RDB/NoSQLのデータは外部ディスクに保存。ロードバランサーへの自動組込が必要との説明がありました。
- Dynamic Docker links with an ambassador powered by etcd
最近のCoreOS(2014/09-)に関する情報
CoreOS Managed Linuxという有償サポートも開始された。CoreUpdateというノード管理GUIが提供される。Premium Managed Linuxという上位プランにはプライベートDocker Hub Registryも提供される。
デバッグを目的としてFedoraの環境が利用できる。実態はCoreOSと同じ名前空間で起動するコンテナ。コンテナはDockerではなくsystemd-nspawnを利用しており、ファイルシステムは/media/rootにマウントされる。CoreOS自体の管理等にも利用できる。
質疑応答
- 質問. EFIな環境にインストールする方法が分からないです。ベアメタル環境にCoreOSをインストールされたとの話ですが、EFI環境へのインストールは試したことがありますか?
質問. Linuxコンテナ内のログをFluentd等で管理したい場合、どうしたら良いですか?
- 回答. Fluentdでの設定方法は未調査。Fluentdにこだわってログ管理しなくても良いかも。
コンテナ仮想化とはなんだったのか?
- 発表者は@m_birdさん
- 発表資料は以下のURLで公開されています
仮想化の概念を振り返りつつ、完全仮想化とコンテナ型仮想化(OSレベル仮想化)についての比較と共にFreeBSDのjail機能を説明するという内容での発表でした。
仮想化の概念の話として、PopekとGoldbergの仮想化要件の説明がありました。まず、仮想マシンモニタ(VMM)の要件として等価性(Equivalence)、資源の管理(Resource Control)、効率性(Efficiency)があり、仮想マシンモニタを構築に際し、CPU命令セットを以下の3つに分類します。
- 特権命令
- 特権センシティブ命令
- 動作センシティブ命令
ただし、これらは完全仮想化に関するものであり、コンテナ型仮想化の観点からこの要件と分類を比較する形で、仮想化「っぽい」要件とは何か、という説明がありました。いずれも「コンテナ型仮想化から見た場合」の話です。
- 等価性→コンテナに分けた環境は同じ振る舞いをするので、「コンテナ環境」が等価性を満たす要件といえる。
- 効率性→(後述)
- 資源管理→コンテナはホストOSが完全に掌握しているので、「ホストOS」が資源管理を満たす要件といえる。
効率性については、完全仮想化の場合「大部分の機械の命令をVMMの介在無く実行できると」(Wikipediaから引用)とありますが、コンテナ型仮想化の場合はユーザ空間のレベルでコンテナが作られるので、機械の命令云々の話はそもそも出てこない、というワケです。
それでも完全仮想化とコンテナ型仮想化のI/O性能の比較について言及があり、完全仮想化ではI/O性能低下の要因としてホストOSとゲストOS間でのコンテキストスイッチの増加、その改善方法としてvirtio等の準仮想化ドライバでコンテキストスイッチを減らす方法とPlan9由来のプロトコルを使用し、ホストOSのファイルシステムを直接読み書きするvirtfsの仕組みが紹介されていました。
完全仮想化におけるI/O性能低下のデータも提示されており、実機上のI/O性能を100%とした場合に、virtfsで99%、準仮想化ドライバで81%、IDEブロックデバイスエミュレーションで41%の性能になるという結果になっていました。
FreeBSD jail
FreeBSD jailについては、jail(2)システムコールから見た説明となっていました。コンテナ型仮想化ではホストOS(コンテナホスト)でリソースを制限をしており、FreeBSDでは4.2BSD以降に追加されたgetrlimit/setrlimitや/etc/login.confでリソース制限を行います(Linuxでは/etc/security/limits.confとのことです)。
jailの設定は/etc/jail.confで行います。jailはOS標準の仕組みで構成されている。jailシステムコール自体にはリソース制限は存在しないため、FreeBSD 9よりRACCTL(カーネル内の資源量把握), RCTL(資源の制限を行う)が利用できますが、GENERICカーネルでは提供されていない機能なのでカーネルの再構築が必要とのことです。
その他、FreeBSD jailの面白い機能として、jail環境でCentOSを動作させる方法が紹介されていました。
- Running CentOS 5.5 in a Jail
質疑応答
- 質問. 完成版の資料はいつかどこかで見られる(笑)?
回答. 予定は未定です(笑)
回答. 別のカーネルを動かして別のコンテナを動かすことは技術的には可能、Dragonfly BSDでそうったことをやっているはず。ただ、それはコンテナ型仮想化とは異なる別の概念の仮想化になるかと思う。(参加者からの補足→)カーネルレベルでは無理だが、エミュレーションレベルだとOKかも。例えば、FreeBSD 9の上でFreeBSD 8バイナリを動かすことは可能。
質問. jailってOS Xでも使える?
- 回答. カーネルが別物なので利用できないと思う。
Oracle Solaris Zones -Oracle Solarisのコンテナ技術-
- 発表者は@satokazさん
Oracle Solaris Zoneに関する発表でした。Oracle Solarisは「研究及び開発目的であれば無償利用可能」とのことです。
Solaris Zone
2003年代のOracle Solaris Zonesの開発目標としては以下のが挙げられていた(当時からリソースの制御も要件に含めていたようです)。「粒度」はリソースを分配可能にするという話で、「透過性」はコンテナに分離する際にアプリケーションの移植を必要としない(させない)というものです。
- セキュリティ(Security)
- 隔離(Isolation)
- 仮想化(Virtualization)
- 粒度(Granularity)
- 透過性(Transparency)
隔離(Isolation)のアプローチとしてchroot,FreeBSDのjail等があり、基本的にはjailの考え方に基づいて実装されたようです。
Oracle Solaris Zonesは単一のシステム上に複数の隔離されたSolarisインスタンスを提供する機能で、イメージとしてはjailやLXC,Docker等と同じです。ただし、Zonesは以下の2種類に分類されます。
- 大域ゾーン(global zone)
- → オペレーティングシステムの実体(ホストOS)
- 非大域ゾーン(non-global zone)
ネットワークについても以下の2種類に分類されます。
- 共有IPゾーン(shared IP zone)
- →デフォルトのネットワーク
- 排他的IPゾーン(shared IP zone)
- →非大域ゾーン専用の物理ネットワーク
これらのゾーン間でのアクセス可否は以下のようになっています。
アクセス元ゾーン | アクセス先ゾーン | アクセス可否 |
---|---|---|
大域ゾーン | 非大域ゾーン | 可能 |
非大域ゾーン | 大域ゾーン | 可能 |
非大域ゾーン | 非大域ゾーン | 不可 |
Oracle Solaris Zonesでのリソースは「資源プール」として管理されており、以下のリソースがあります。
- プロセッサセット(CPU)
- スケジューリングクラス
Solaris Kernel Zones
Oracle Solaris 11.2から提供されるゾーンとして、"Solaris Kernel Zones"があります。物理ホストには以下の高いスペックが要求されます。
- CPU
- メモリは最小8GB必要
- ZFS
- ZFS ARC(Adaptive Replacement Cache)の上限値を搭載物理メモリの半分程度におさえる
- これは重要なポイントで、これを忘れるとある日突然Kernel Zonesが起動しなくなる現象に見舞われるとのこと
Kernel Zonesの内部についても解説があり、kzhostプロセスとzvmmカーネルモジュールの説明がありました。kzhostプロセスはゲストOSに仮想CPUを利用させるためのもので、Kernel Zones毎に生成され、I/Oスレッドや各種管理、Zonesに割り当てられるメモリ管理を行います。zvmm(zone virtual machine monitor)カーネルモジュールは擬似ドライバでゲストOSに対して仮想ハードウェアとして振る舞います。
Oracle Solaris Zoneの参考情報として、以下が紹介されていました。
- Solaris Zones: Operating System Support for Consolidating Commercial Workloads
- Oracle Solaris 11.2 Information Library
- Introduction to Oracle Solaris Zones
- http://docs.oracle.com/cd/E36784_01/html/E36848/index.html
- 日本語版はもう少しで公開とのことです
質疑応答
- 質問. Zoneをいくら作ってもお値段同じですか?
回答. 残念ながら……「無料」です。(というワケで、リソースの許す限りZoneを作れます)
質問. トワイライトゾーンについて聞きたいです。
- 回答. ゾーン毎に作成される(ゾーンに紐づいている)。ネイティブゾーンにはトワイライトゾーンが存在しない。kzprocessが裏でちょっとしたゾーンを作っている。
ニフティのクラウドへの取り組み
- 発表者は@ysaotomeさん
- 発表資料は以下のURLにて公開されています
ニフティクラウド→エンタープライズ向けで時間貸しのクラウド、が、法人向けのクラウド。そこで、個人でも使えるサービスを始めた。「ニフティクラウド C4SA」。15日間は無料で利用できる。
コンテナを「キャンバス」という概念で示し、Webブラウザから操作する形。コントロールパネルにsshっぽい画面がある。リソースは内部からデータを取ってお客さんに見せている。cgroupsの機能はバリバリ利用している。
管理、権限、課金ノードについてはセキュリティの観点から互いを信用せず、相互に監視しつづけるアーキテクチャになっている(例えば課金ノードの情報が不正に書き換えられても他のノードはそれを検知できる、ということ?)。
LXC最新情報
- 発表者は@ten_forwardさん
- 発表資料は以下のURLで公開されています。
LXCの最新状況に関する発表でした。現在LXC 1.0.5がリリース(2014/07/14)がされており、LXC 1.0の新機能として、公式APIバインディング(Python2.7とPython3、lua,Go,ruby)、stableなliblxc1によるAPIの提供、非特権コンテナのサポートがあり、セキュリティに関しては、SELinuxとAppArmorをサポートし、seccompによるコンテナ毎のケーパビリティ指定が可能とのことです。
cgmanager(1)というコマンドが追加されており、DBusメッセージを送ることでcgroupを管理できるようです。
そしてこの度LXC日本語サイト作りました、とのこと。URLは以下です。
- LXC Linux Containers
まとめ
第4回 コンテナ型仮想化の情報交換会@東京に参加し、勉強会メモをまとめてみました。6時間近く開催された内容をまとめるのは大変でしたが、放っておくと頭から内容が揮発して行くので忘れない内に文章にしておくのが良さそうです。
CentOS 7.0でDockerを動かしてみた
CentOS 7.0でDockerを動かしてみた
先日開催された第2回東京SoftLayer勉強会で『SoftLayerでDockerを使ってみた』という発表があり、Dockerのインストールから使い方までの手順が解説されていました。
個人的にDockerを試してみたいということもあり、さっそく解説されていた手順を参考にDockerを動かしてみました。勉強会ではCentOS 6系向けの手順となっていましたが、私の方ではCentOS 7系で試してみました。
CentOS 7.0のセットアップ
GUI等は使用しないので、minimalインストールの状態からDocker環境を構築していきます。以下はインストール後にsshログインが行えるようになるまでの手順です。
SELinuxの無効化
/etc/selinux/configを以下のように修正し、SELinuxを無効化します。
--- config.orig 2014-07-29 11:24:02.949653548 +0900 +++ config 2014-07-29 11:24:09.957653412 +0900 @@ -4,7 +4,7 @@ # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - No SELinux policy is loaded. -SELINUX=enforcing +SELINUX=disabled # SELINUXTYPE= can take one of these two values: # targeted - Targeted processes are protected, # minimum - Modification of targeted policy. Only selected processes are protected.
grub.cfgの設定
起動時のコンソールログを見えるようにしておきたいので、/boot/grub2/grub.cfgを以下のように修正します。ついでにLANGもen_US.UTF-8に変更しておきます。
--- grub.cfg.orig 2014-07-29 11:24:18.721653243 +0900 +++ grub.cfg 2014-07-29 11:24:38.572652858 +0900 @@ -81,7 +81,7 @@ else search --no-floppy --fs-uuid --set=root dd4521d9-ca1c-462b-b353-2ba57bf4fc61 fi - linux16 /boot/vmlinuz-3.10.0-123.el7.x86_64 root=UUID=dd4521d9-ca1c-462b-b353-2ba57bf4fc61 ro vconsole.keymap=us crashkernel=auto vconsole.font=latarcyrheb-sun16 rhgb quiet LANG=ja_JP.UTF-8 + linux16 /boot/vmlinuz-3.10.0-123.el7.x86_64 root=UUID=dd4521d9-ca1c-462b-b353-2ba57bf4fc61 ro vconsole.keymap=us crashkernel=auto vconsole.font=latarcyrheb-sun16 LANG=en_US.UTF-8 initrd16 /boot/initramfs-3.10.0-123.el7.x86_64.img } menuentry 'CentOS Linux, with Linux 0-rescue-6764668114ff4741a8994286ae140fdd' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-0-rescue-6764668114ff4741a8994286ae140fdd-advanced-dd4521d9-ca1c-462b-b353-2ba57bf4fc61' { @@ -95,7 +95,7 @@ else search --no-floppy --fs-uuid --set=root dd4521d9-ca1c-462b-b353-2ba57bf4fc61 fi - linux16 /boot/vmlinuz-0-rescue-6764668114ff4741a8994286ae140fdd root=UUID=dd4521d9-ca1c-462b-b353-2ba57bf4fc61 ro vconsole.keymap=us crashkernel=auto vconsole.font=latarcyrheb-sun16 rhgb quiet + linux16 /boot/vmlinuz-0-rescue-6764668114ff4741a8994286ae140fdd root=UUID=dd4521d9-ca1c-462b-b353-2ba57bf4fc61 ro vconsole.keymap=us crashkernel=auto vconsole.font=latarcyrheb-sun16 initrd16 /boot/initramfs-0-rescue-6764668114ff4741a8994286ae140fdd.img } if [ "x$default" = 'CentOS Linux, with Linux 3.10.0-123.el7.x86_64' ]; then default='Advanced options for CentOS Linux>CentOS Linux, with Linux 3.10.0-123.el7.x86_64'; fi;
ホスト名の設定
以下の手順でホスト名を設定します。
# echo dockertest.furandon.net > /etc/hostname
固定IPアドレスの設定
DHCPよりも固定IPアドレスの方が作業しやすいため、Arch Wikiの『固定IPアドレス』を参考にしながら設定します。
/etc/conf.d/network@enp0s3にIPアドレス等を記述します。/etc/conf.dディレクトリは存在しないので予め作成しておきます。
# mkdir /etc/conf.d # touch /etc/conf.d/enp0s3
/etc/conf.d/network@enp0s3の中身は以下の通りです。
address=192.168.0.232 netmask=24 broadcast=192.168.0.255 gateway=192.168.0.1
/etc/systemd/system/network@.serviceにsystemdのユニットファイルを作成します。
(Arch Wikiの内容を参考に作成しています)
Arch Wikiの/etc/systemd/system/network@.service中でipコマンドが/usr/bin/ipでフルパス指定されていますが、CentOS-7では/sbin/ipとパスが異なるので注意してください。
[Unit] Description=Network connectivity (%i) Wants=network.target Before=network.target BindsTo=sys-subsystem-net-devices-%i.device After=sys-subsystem-net-devices-%i.device [Service] Type=oneshot RemainAfterExit=yes EnvironmentFile=/etc/conf.d/network@%i ExecStart=/sbin/ip link set dev %i up ExecStart=/sbin/ip addr add ${address}/${netmask} broadcast ${broadcast} dev %i ExecStart=/bin/sh -c 'test -n ${gateway} && /sbin/ip route add default via ${gateway}' ExecStop=/sbin/ip addr flush dev %i ExecStop=/sbin/ip link set dev %i down [Install] WantedBy=multi-user.target
サービスを有効化します。
# systemctl enable network@enp0s3.service
/etc/resolv.confの設定
インストール時にrpmパッケージのインストール等でネットワーク設定が行われており、/etc/resolv.confには(DHCPでのIPアドレス取得による)nameserver設定が入っています。固定IPアドレスでも同じnameserver設定を利用するため、この段階での設定変更は不要です。
LANGの設定
/etc/locale.confでLANGをen_US.UTF-8に設定します。
(日本語のままでいいよ、という場合はこの手順をスキップしてください)
--- locale.conf.orig 2014-07-29 11:24:55.014652540 +0900 +++ locale.conf 2014-07-29 11:25:01.258652419 +0900 @@ -1 +1 @@ -LANG="ja_JP.UTF-8" +LANG="en_US.UTF-8"
不要なサービスの停止
Dockerを試す用途では不要と思われるサービスを停止します。
$ sudo systemctl disable avahi-daemon.service $ sudo systemctl disable dbus-org.fedoraproject.FirewallD1.service $ sudo systemctl disable dbus-org.freedesktop.Avahi.service $ sudo systemctl disable dbus-org.freedesktop.NetworkManager.service $ sudo systemctl disable dbus-org.freedesktop.nm-dispatcher.service $ sudo systemctl disable NetworkManager-dispatcher.service $ sudo systemctl disable NetworkManager.service $ sudo systemctl disable postfix.service $ sudo systemctl disable tuned.service $ sudo systemctl disable avahi-daemon.socket
ユーザの追加
作業用のユーザを追加します。
$ sudo useradd -g users -G wheel -m -s /bin/bash fpig $ sudo passwd fpig
再起動
ここで一旦再起動します。再起動後、固定IPアドレスが付与されているか等の確認をおこないます。
$ sudo shutdown -r now
ここまでで最低限の設定は完了し、sshでログインできる状態になっています。
以降はsshログインした状態での作業となります。
Dockerを試してみる
dockerのインストール
docker.x86_64というパッケージが提供されているので、これをインストールします。
$ yum search docker ... docker-registry.noarch : Registry server for Docker docker.x86_64 : Automates deployment of containerized applications $ sudo yum install docker
勉強会で例示されていたCentOS 6系の環境では、EPELからDockerをインストールしていましたが、CentOS 7系の環境ではベースリポジトリにDockerパッケージが含まれていました。
$ rpm -qa | grep docker docker-0.11.1-22.el7.centos.x86_64 $ yum provides docker ... docker-0.11.1-19.el7.centos.x86_64 : Automates deployment of containerized : applications Repo : extras $ grep -A5 '^\[extras' /etc/yum.repos.d/CentOS-Base.repo [extras] name=CentOS-$releasever - Extras mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras #baseurl=http://mirror.centos.org/centos/$releasever/extras/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Dockerサービスを有効化します。DockerパッケージにSystemdのユニットファイルが同梱されているので、これを利用します。
$ rpm -ql docker-0.11.1-22.el7.centos.x86_64 | grep systemd /usr/lib/systemd/system/docker.service $ sudo systemctl enable docker.service ln -s '/usr/lib/systemd/system/docker.service' '/etc/systemd/system/multi-user.target.wants/docker.service' $ systemctl list-unit-files | grep docker docker.service enabled
上記設定は再起動してから有効になるので、ここでは手動でdocker.serviceを起動します。
$ sudo systemctl start docker.service
さっそくdockerコマンドを使ってみます。
$ sudo docker version Client version: 0.11.1-dev Client API version: 1.12 Go version (client): go1.2 Git commit (client): 02d20af/0.11.1 Server version: 0.11.1-dev Server API version: 1.12 Go version (server): go1.2 Git commit (server): 02d20af/0.11.1
Dockerイメージのダウンロード
手元でDockerイメージを作るところまで試してみたかったのですが、まだ使い始めたばかりということもあり、既存のDockerイメージファイルを使ってみることにしました。
$ sudo docker pull ubuntu
しばらく待つとUbuntuのイメージファイルが手元にダウンロードされます。使用可能なイメージはdocker imageで確認できます。
$ sudo docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE ubuntu 14.04 ba5877dc9bec 7 days ago 192.7 MB ubuntu trusty ba5877dc9bec 7 days ago 192.7 MB ubuntu latest ba5877dc9bec 7 days ago 192.7 MB ubuntu precise b9e56c8f2cf5 7 days ago 103.8 MB ubuntu 12.04 b9e56c8f2cf5 7 days ago 103.8 MB ubuntu 14.10 6ef6f1a66de1 7 days ago 194.1 MB ubuntu utopic 6ef6f1a66de1 7 days ago 194.1 MB ubuntu 12.10 c5881f11ded9 5 weeks ago 172.1 MB ubuntu quantal c5881f11ded9 5 weeks ago 172.1 MB ubuntu 13.04 463ff6be4238 5 weeks ago 169.4 MB ubuntu raring 463ff6be4238 5 weeks ago 169.4 MB ubuntu 13.10 195eb90b5349 5 weeks ago 184.6 MB ubuntu saucy 195eb90b5349 5 weeks ago 184.6 MB ubuntu 10.04 3db9c44f4520 3 months ago 183 MB ubuntu lucid 3db9c44f4520 3 months ago 183 MB
Dockerコンテナの起動
さっそくDockerでUbuntuを起動してみます。
$ sudo docker run -i -t ubuntu:14.10 /bin/bash
サクッとUbuntuが起動しました!
/etc/lsb-releaseを見ると、確かにUbuntuです。
root@9cf71dd83894:/# cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=14.10 DISTRIB_CODENAME=utopic DISTRIB_DESCRIPTION="Ubuntu Utopic Unicorn (development branch)"
Dockerコンテナのデタッチ・アタッチ
起動したDockerコンテナ上でexitするとコンテナは停止してしまいます。練習もかねてDockerコンテナのデタッチ→アタッチ→exitによるコンテナ停止という手順を試してみます。
Dockerコンテナを終了せずに抜ける(デタッチする)には、Ctrl+p Ctrl+qを入力します。
root@9cf71dd83894:/# (ここでCtrl+p Ctrl+qを入力)$ $
docker psコマンドで稼働しているコンテナを確認できます。
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9cf71dd83894 ubuntu:14.10 /bin/bash 9 minutes ago Up 9 minutes backstabbing_rosalind0
コンテナにアタッチしてみます。
$ sudo docker attach 9cf71dd83894 root@9cf71dd83894:/#
無事にアタッチでできました。後はexitでコンテナを終了します。
root@9cf71dd83894:/# exit exit $ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES $
export/import
私の手元ではまだ試していませんが、イメージファイルのexport/importも可能です。
以下の手順でexport/importを行います。
$ docker export <コンテナID> $ docker export 14773ff9e93c > myweb.tar # コマンド例
$ docker import [リポジトリTAG] $ docker import < - myweb:rel1 < myweb.tar # コマンド例
ローカルストレージのアタッチ
こちらも私の手元では試していませんが、Runコマンドの"-v"オプションでホストのディレクトリをアタッチ可能です。
$ docker run -t -i -v <local_dir>:<container dir>:<rw|ro> centos /bin/bash $ docker run -d -p 5000:5000 -v /home/hoge/data:/tmp/data centos # コマンド例
/home/data/dataは、共有ストレージ(NFS)やディスクミラー(DRBD)、分散ストレージ(Ceph-fs)等を利用して自分でミラーリングします。
まとめ
参加した勉強会でDockerの一連の使用手順が解説されていました。Dockerを覚えてみたいと思っていたので、さっそく覚えた手順を自分の環境で試してみました。
思っていたよりも簡単にコンテナの実行が行え、ついに我が家にもDockerの技術が伝来したという感じです。
(種子島に火縄銃が伝来した……という気分です)
まだ試してはいないものの、イメージのexport/importやプライベートリポジトリといった機能もあるようなので、勉強会用のイメージ提供に活用できればと思います。
Fedora20をインストールしてみた
Fedora20をインストールしてみた
Fedora20の環境を用意する必要があったので、インストール後の設定手順をまとめてみました。
sshログインのみで使用する形態を想定しているため、GUIの設定は行わず、インストール後に最低限の設定をするだけの手順になっています。
インストールCD-ROMの入手
今回は以下のURLからネットワークインストール用のCD-ROMをダウンロードしました。インストール作業中に出てくる「ベース環境」の画面で「最小限のインストール」を選択すると、(CentOSでいうところの)minimalインストールになるようです。
- http://fedoraproject.org/ja/get-fedora-all
- http://download.fedoraproject.org/pub/fedora/linux/releases/20/Fedora/x86_64/iso/Fedora-20-x86_64-netinst.iso
インストール後の作業
不要なサービスの停止
必要に応じて不要なサービスを停止します。CentOS環境のようなchkconfigかと思っていたのですが、initデーモンがsystemdに置き換えられたため、コマンド自体が変わっていました。
systemdではsystemctlコマンドを使用しており、以下はchkconfigとのコマンド対応表です。
サービスの 一覧表示 | CentOSの場合 | chkconfig --list |
Fedora20の場合 | systemctl list-unit-files | |
現在有効に なっている サービスの 一覧表示 | CentOSの場合 | chkconfig --list | grep 3:on |
Fedora20の場合 | systemctl list-unit-files | grep enabled | |
サービスの 有効化 | CentOSの場合 | chkconfig sshd on |
Fedora20の場合 | systemctl enable sshd.service | |
サービスの 無効化 | CentOSの場合 | chkconfig sshd off |
Fedora20の場合 | systemctl disable sshd.service |
ネットワークの設定
ネットワークまわりの設定はこれまでの方法(CentOS等の設定方法)と変わりないようです。
# diff -u /etc/sysconfig/network.orig /etc/sysconfig/network --- /etc/sysconfig/network.orig 2014-06-26 16:56:00.223810936 +0900 +++ /etc/sysconfig/network 2014-06-26 17:02:09.848893449 +0900 @@ -1 +1,4 @@ # Created by anaconda + +NETWORKING="yes" +GATEWAY="192.168.0.1"
ただし、NICのデバイス名がeth0等ではなく、p2p1といった名前になっています。
# diff -u /etc/sysconfig/network-scripts/ifcfg-p2p1.orig /etc/sysconfig/network-scripts/ifcfg-p2p1 --- /etc/sysconfig/network-scripts/ifcfg-p2p1.orig 2014-06-26 16:38:20.626798779 +0900 +++ /etc/sysconfig/network-scripts/ifcfg-p2p1 2014-06-26 16:54:57.349810215 +0900 @@ -1,5 +1,7 @@ TYPE="Ethernet" -BOOTPROTO="dhcp" +BOOTPROTO="none" +IPADDR0="192.168.0.225" +PREFIX0="24" DEFROUTE="yes" IPV4_FAILURE_FATAL="no" IPV6INIT="yes"
ホスト名の設定
ホスト名は/etc/hostnameに記載します。CentOS等では/etc/sysconfig/networkにHOSTNAME="..."な形式で設定していたのですが、これもFedora20では設定方法がかわったのでしょうか?(まだ調査しきれていないです...)
# cat vmfedora20 > /etc/hostname
SELinuxの無効化
SELinuxは無効化しておきます(ごめんなさい...)。
# diff -u /etc/selinux/config.orig /etc/selinux/config --- /etc/selinux/config.orig 2014-06-26 17:04:15.353965308 +0900 +++ /etc/selinux/config 2014-06-26 17:04:42.989963918 +0900 @@ -4,7 +4,7 @@ # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - No SELinux policy is loaded. -SELINUX=enforcing +SELINUX=disabled # SELINUXTYPE= can take one of these three values: # targeted - Targeted processes are protected, # minimum - Modification of targeted policy. Only selected processes are protected.
起動時のログをコンソールに出力させる(grub.cfgの設定)
OS起動時のグラフィカルな起動画面もよいのですが、起動時のログがコンソールに出力されないと不安になるので、ログが出力されるよう設定します。
以下のdiffではちょっと分かりにくいですが、"rhgb quiet"というオプションを除去しているだけです。
# diff -u /boot/grub2/grub.cfg.orig /boot/grub2/grub.cfg --- /boot/grub2/grub.cfg.orig 2014-06-26 16:59:24.535293206 +0900 +++ /boot/grub2/grub.cfg 2014-06-26 17:00:01.614295112 +0900 @@ -72,7 +72,7 @@ else search --no-floppy --fs-uuid --set=root 6b176d94-7c9a-4ff7-a007-51d0cd40ccaa fi - linux /boot/vmlinuz-3.14.8-200.fc20.x86_64 root=UUID=6b176d94-7c9a-4ff7-a007-51d0cd40ccaa ro vconsole.font=latarcyrheb-sun16 rhgb quiet LANG=ja_JP.UTF-8 + linux /boot/vmlinuz-3.14.8-200.fc20.x86_64 root=UUID=6b176d94-7c9a-4ff7-a007-51d0cd40ccaa ro vconsole.font=latarcyrheb-sun16 LANG=ja_JP.UTF-8 initrd /boot/initramfs-3.14.8-200.fc20.x86_64.img } menuentry 'Fedora, with Linux 0-rescue-78fd1bcdf2504b058f547faf585019df' --class fedora --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-0-rescue-78fd1bcdf2504b058f547faf585019df-advanced-6b176d94-7c9a-4ff7-a007-51d0cd40ccaa' {
yum update
ここまでで一旦OSを再起動し、IPアドレスが付与されているか等の確認を行ってからyum updateを実行します。
# yum -y update
sudo設定について
Fedora20ではminimalインストールでもsudoがインストールされています。また、wheelグループがsudoできるよう設定されており、visudoで改めて設定する必要はありませんでした。
ユーザの追加
一般ユーザを追加します。sudoできるようにしたいので、セカンダリグループとしてwheelを指定します。
# useradd -g users -G wheel -m -s /bin/bash fpig # passwd fpig
これでインストール後の設定は一通り完了です。後はsshでログインし、パッケージの追加等で生活環境を整えていきます。
まとめ
Fedora20のインストール後の設定手順をまとめてみました。initデーモンがsystemdに置き換えられた点がCentOSとの大きな違いでした。サービスの有効・無効を切り替えるコマンドもchkconfigからsystemctlになっており、当面はコマンドを調べながらの利用になりそうです。