systemdとcgroup

systemdではcgroupを活用しているようだ。サービスごとにcgroupでアイソレーションされているので、関係するプロセスを一網打尽で殺せるし、サービスごとの資源利用量を観測できる。確かにpidをファイルに記録しておいて、殺すってのはダサいよね。流れとしてはユニットファイルでExecStopで指定されているコマンドを実行し、それでも当該グループのプロセスが残っている場合は、SIGTERM、SIGKILLの順でシグナルを投げるそうな。詳細はレッドハット中井さんの「Linux女子部 systemd徹底入門」とか。

次はsystemd-cgtopの実行例。

Path                                                         Tasks   %CPU   Memory  Input/s Output/s

/                                                               86    0.3   197.3M        -        -
/system.slice/NetworkManager.service                             2      -        -        -        -
/system.slice/auditd.service                                     1      -        -        -        -
/system.slice/avahi-daemon.service                               2      -        -        -        -
/system.slice/crond.service                                      1      -        -        -        -
/system.slice/dbus.service                                       1      -        -        -        -
/system.slice/firewalld.service                                  1      -        -        -        -
/system.slice/iprdump.service                                    1      -        -        -        -
/system.slice/iprinit.service                                    1      -        -        -        -
/system.slice/iprupdate.service                                  1      -        -        -        -
/system.slice/lvm2-lvmetad.service                               1      -        -        -        -
/system.slice/polkit.service                                     1      -        -        -        -
/system.slice/postfix.service                                    3      -        -        -        -
/system.slice/rsyslog.service                                    1      -        -        -        -
/system.slice/sshd.service                                       1      -        -        -        -
/system.slice/system-getty.slice/getty@tty1.service              1      -        -        -        -
/system.slice/systemd-journald.service                           1      -        -        -        -
/system.slice/systemd-logind.service                             1      -        -        -        -
/system.slice/systemd-udevd.service                              1      -        -        -        -
/system.slice/tuned.service                                      1      -        -        -        -
/system.slice/vboxadd-service.service                            1      -        -        -        -
/user.slice/user-1000.slice/session-4.scope                      4      -        -        -        -

大きく分けるとsystem.sliceとuser.sliceってのがある。/sys/fs/cgroup/systemdがルートね。
systemd-cglsでcgroupの使われ方を見ることができる。

[vagrant@localhost ~]$ systemd-cgls
├─1 /usr/lib/systemd/systemd --switched-root --system --deserialize 23
├─user.slice
│ └─user-1000.slice
│   └─session-4.scope
│     ├─2407 sshd: vagrant [priv]
│     ├─2410 sshd: vagrant@pts/0 
│     ├─2411 -bash
│     ├─2471 systemd-cgls
│     └─2472 systemd-cgls
└─system.slice
  ├─polkit.service
  │ └─747 /usr/lib/polkit-1/polkitd --no-debug
  ├─auditd.service
  │ └─501 /sbin/auditd -n
  ├─systemd-udevd.service
  │ └─455 /usr/lib/systemd/systemd-udevd
  ├─lvm2-lvmetad.service
  │ └─440 /usr/sbin/lvmetad -f
  ├─systemd-journald.service
  │ └─426 /usr/lib/systemd/systemd-journald
  ├─dbus.service
  │ └─543 /bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
  ├─systemd-logind.service
  │ └─542 /usr/lib/systemd/systemd-logind
  ├─sshd.service
  │ └─1109 /usr/sbin/sshd -D
  :

冒頭で触れた挙動を確認するため、ちょっとソースコードを眺める。systemd本体はsrc/core以下なのかな。サービスユニットの上げ下げはsrc/core/service.cとかsrc/core/unit.cあたりかな。確かにSysVinitでシェルスクリプトで実装されていた分が全部Cで書かれているわけね。たしかにこれはビッグチェンジだなぁ。

service_stop()がsystemctl stopで呼ばれる関数。ユニットごとに状態遷移マシンになっていて、service_enter_stop()でExecStop、service_enter_stop_post()でExecStopPostに登録されたコマンドを順に実行する。コマンドの実行に失敗した場合は、service_enter_signal()を経由してunit_kill_context()でプロセスにSIGTERMを投げる。それでも死なないプロセスはwatchdogで回収され、同様にunit_kill_context()でSIGKILLを投げる。

unit_kill_context()が参照するKillContextにはkill_modeというメンバ変数があり、ユニットはKILL_CONTROL_GROUPに初期化されている(SysVinit互換のケースはKILL_PROCESSになっている)。unit_kill_context()ではcg_kill_recursive()を呼んで、cgroupのプロセスにシグナルを投げる。
cg_*関数の定義はsrc/shared/cgroup-util.c。cgroupの実装がファイルシステムというのがよくわかる。

systemdについて

前回の続きで、systemdを少し調べてみる。systemdはsysvinitに代わってRedhatDebianで採用されているが、「Linus様がSystemdにぶちきれる」とかboycott systemdとか、物議を醸しているようだ。

CentOS 6.5だと、PID 1は当然initだけど、CentOS 7はsystemdになっているのがわかる。

$ pstree -h
init─┬─acpid
     ├─agetty
     ├─crond
     ├─6*[mingetty]
     ├─ntpd
     ├─rpc.statd
     ├─rpcbind
     ├─rsyslogd───3*[{rsyslogd}]
     ├─sshd───sshd───sshd───bash───pstree
     └─udevd───2*[udevd]
[vagrant@localhost ~]$ pstree
systemd─┬─NetworkManager─┬─dhclient
        │                └─3*[{NetworkManager}]
        ├─VBoxService───7*[{VBoxService}]
        ├─agetty
        ├─auditd───{auditd}
        ├─avahi-daemon───avahi-daemon
        ├─crond
        ├─dbus-daemon───{dbus-daemon}
        ├─firewalld───{firewalld}
        ├─iprdump
        ├─iprinit
        ├─iprupdate
        ├─lvmetad
        ├─master─┬─pickup
        │        └─qmgr
        ├─polkitd───5*[{polkitd}]
        ├─rsyslogd───2*[{rsyslogd}]
        ├─sshd───sshd───sshd───bash───pstree
        ├─systemd-journal
        ├─systemd-logind
        ├─systemd-udevd
        └─tuned───4*[{tuned}]

/sbin/initがsystemdへのシンボリックリンクになっているのね。

[vagrant@localhost ~]$ ls -l /sbin/init 
lrwxrwxrwx. 1 root root 22 Aug  1 08:41 /sbin/init -> ../lib/systemd/systemd

systemdは高速起動が売りの一つとのことだけど、systemd-analyzeってコマンドで起動時間や何処で時間を食っているかなどを調べることができる。

[vagrant@localhost ~]$ systemd-analyze time
Startup finished in 431ms (kernel) + 1.326s (initrd) + 7.844s (userspace) = 9.603s

[vagrant@localhost ~]$ systemd-analyze blame
          2.167s firewalld.service
          1.825s kdump.service
          1.584s tuned.service
          1.442s vboxadd.service
          1.166s network.service
          1.102s lvm2-monitor.service
          :

また、ログ周りもsyslogdからjournaldという独自の実装に変わっている。

[vagrant@localhost ~]$ sudo journalctl
-- Logs begin at Fri 2014-09-05 01:45:16 EDT, end at Sat 2014-09-06 00:32:19 EDT. --
Sep 05 01:45:16 localhost.localdomain systemd-journal[80]: Runtime journal is using 4.0M (max 24.5M, leaving 36.7M of free 241.1M, current limit 24.5M).
Sep 05 01:45:16 localhost.localdomain systemd-journal[80]: Runtime journal is using 4.0M (max 24.5M, leaving 36.7M of free 241.1M, current limit 24.5M).
Sep 05 01:45:16 localhost.localdomain kernel: Initializing cgroup subsys cpuset
Sep 05 01:45:16 localhost.localdomain kernel: Initializing cgroup subsys cpu
Sep 05 01:45:16 localhost.localdomain kernel: Initializing cgroup subsys cpuacct
Sep 05 01:45:16 localhost.localdomain kernel: Linux version 3.10.0-123.6.3.el7.x86_64 (builder@kbuilder.dev.centos.org) (gcc version 4.8.2 20140120 (Red Hat 4.8.2-16) (GCC) ) #1 SMP Wed Aug 6 21:12:36 UTC 2014
Sep 05 01:45:16 localhost.localdomain kernel: Command line: BOOT_IMAGE=/vmlinuz-3.10.0-123.6.3.el7.x86_64 root=/dev/mapper/centos-root ro rd.lvm.lv=centos/swap vconsole.font=latarcyrheb-sun16 vconsole.keymap=jp106 rd.lvm.lv=centos/root crashkernel=auto rhgb quiet LANG=en_US.UTF-8
:

オプションに"-b"を指定すればブート時のもの、_SYSTEMD_UNIT=sshd.serviceまたは'which sshd'(デーモンのパス)を指定すればそのログのみが取り出せる。

[vagrant@localhost ~]$ sudo journalctl _SYSTEMD_UNIT=sshd.service
-- Logs begin at Fri 2014-09-05 01:45:16 EDT, end at Sat 2014-09-06 00:34:34 EDT. --
Sep 05 01:45:24 localhost.localdomain sshd[1099]: Server listening on 0.0.0.0 port 22.
Sep 05 01:45:24 localhost.localdomain sshd[1099]: Server listening on :: port 22.
Sep 05 01:45:30 localhost.localdomain sshd[2202]: Accepted publickey for vagrant from 10.0.2.2 port 49275 ssh2: RSA dd:3b:b8:2e:85:04:06:e9:ab:ff:a8:0a:c0:0
Sep 05 01:48:34 localhost.localdomain sshd[3895]: Accepted publickey for vagrant from 10.0.2.2 port 49326 ssh2: RSA dd:3b:b8:2e:85:04:06:e9:ab:ff:a8:0a:c0:0

systemdのメインコマンドはsystemctl。これでサービス(systemd用語ではユニットというのかな)の有効・無効などを制御できる。serviceコマンドのレベルでは互換性を保っているようなので、取りあえずは戸惑いなく使えるのかなぁ。それに加えて、systemctl rebootとかpoweroffってのもできる。ふむ、/usr/sbin/rebootなどがsystemctlへのシンボリックリンクになっている。結構システム全体に影響あるんだなぁ。

あと話は脱線するけど、systemd-detect-virtというハイパーバイザを認識するコマンドがある。

[vagrant@localhost ~]$ systemd-detect-virt 
oracle

どんな実装になっているのかな。gitリポジトリを眺めてみたところ、このへんだね。CPUIDやDMIを使って検出するようね。VirtualBoxの場合は、/sys/class/dmi/id/sys_vendorが"innotek GmbH"であれば、VirtualBoxと判定されるようだ。

関連して、hostnamectlってコマンドもあるようだ。Chassisは"vm"になりそうなものの、"n/a"になっているね。

[vagrant@localhost ~]$ sudo hostnamectl 
   Static hostname: localhost.localdomain
         Icon name: computer
           Chassis: n/a
        Machine ID: 42223b0ecc4b406f8c15a34a4ad16be9
           Boot ID: c140fb95493041e7ab58a6ddbe4b74f6
    Virtualization: oracle
  Operating System: CentOS Linux 7 (Core)
       CPE OS Name: cpe:/o:centos:centos:7
            Kernel: Linux 3.10.0-123.6.3.el7.x86_64
      Architecture: x86_64

Vagrant事始め

Vagrantのインストール

夏休みにCentOS 7の勉強をしてみようと、ついでにVagrant + VirtualBoxで環境を構築してみた。以下、試行錯誤のメモ。
VagrantVirtualBoxHomebrew caskでインストールした。

$ brew cask install virtualbox
$ brew cask install vagrant

この辺で公開されているBoxファイルを眺めると、CentOS 7のVirtualBox用Boxがあるので、これを使う。minimal installされたものなので、CentOS 7の勉強用にはちょうどいい。Vagrantではcentos7という名前で登録。

$ vagrant init centos7 https://f0fff3908f081cb6461b407be80daf97f07ac418.googledrive.com/host/0BwtuV7VyVTSkUG1PM3pCeDJ4dVE/centos7.box

なんか以下のようなエラーメッセージが出ているけど、とりあえず問題なく動いている。

Failed to mount folders in Linux guest. This is usually because
the "vboxsf" file system is not available. Please verify that
the guest additions are properly installed in the guest and
can work properly. The command attempted was:

mount -t vboxsf -o uid=`id -u vagrant`,gid=`getent group vagrant | cut -d: -f3` vagrant /vagrant
mount -t vboxsf -o uid=`id -u vagrant`,gid=`id -g vagrant` vagrant /vagrant

(追記:2015-04-09)これはゲストOSにVirtualBox guest additionsが入っていないので、共有フォルダがmountできないから。共有フォルダを無効にするには、Vagrantfileに次の一行を追加する。

  config.vm.synced_folder(".", nil, :disabled => true, :id => "vagrant-root")

"vagrant init"でカレントディレクトリにVagrantfileが生成される。VagrantfileはRuby DSLのフォーマットで記述されている。
あとは、"vagrant up"でVMが起動するので(初回はダウンロードに結構時間がかかるけど、2回目以降はさっくり起動する)、"vagrant ssh"でVMにログインする。使い終わったら"vagrant destroy"でVMを破棄。なお、vagrantコマンド実行時のカレントディレクトリは、ゲストOSの/vagrantにマウントされる。

インストールされているBoxは次のコマンドで調べることができる。

$ vagrant box list
centos7 (virtualbox, 0)

現在実行しているVMをBoxとしてエクスポートするときは"vagrant package"コマンドを使う。VMが起動中の場合は、シャットダウンされる。カレントディレクトリにpackage.boxファイルが生成される。

$ vagrant package
$ vagrant box add centos7_custom package.box
$ vagrant box list
centos7        (virtualbox, 0)
centos7_custom (virtualbox, 0)

Vagrant + Chef solo

Vagrantfileをながめると、Chefの設定とかがコメントアウトされている。C/S構成のChefは敷居が高そうなので、まずはChef Soloを試す。

動作の流れは次のようになる。

  • VagrantVMを起動
  • CookbookをVMに転送
  • VM上でChef Soloを実行

また、便利なVagrantプラグインvagrant-omnibusをインストールする。vagrant-omnibusはBoxにChefがインストールされているか確認して、入ってない場合にインストールする。(これは便利だけど、Vagrantに依存してしまうところがちょっと難ありか)

まずは、インストールから。

$ sudo gem install chef
$ sudo gem install knife-solo

$ vagrant plugin install vagrant-omnibus
$ vagrant plugin list
vagrant-login (1.0.1, system)
vagrant-omnibus (1.4.1)
vagrant-share (1.1.0, system)
vagrant-vbguest (0.10.0)

vagrant-omnibusを有効にするには、Vagrantfileに次の一行を追加すればOK。

  config.omnibus.chef_version = :latest

適当なChefレシピを書いてみる。

$ knife solo init chef
$ cd chef
$ knife cookbook create hello -o site-cookbooks
$ echo log "hello, world!" >> site-cookbooks/hello/recipes/default.rb

このレシピを実行するには、Vagrantfileに次の行を追加する。

  config.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = ["chef/site-cookbooks"]
    chef.add_recipe "hello"
  end

問題なければ、起動時のログに"hello, world!"と表示されるはず。

もう少し実用的なものということで、ここのDevelopment toolsをインストールするレシピを追加してみるとか。

$ vagrant reload --provision

しかし、正直Chefは取っつきにくいなぁ。Ansibleってのはもう少しお気楽に始められそう。

CentOS 7

ネットワークインタフェース名はenp0s3になるのねとか(この場合PCIバス0番の3スロットに繋がっているという意味)、ifconfigはないのでip addrを使うのねとか、お決まりの点を確認する。

変更点は「RHEL7/CentOS7でipコマンドをマスター」がまとまっている。

net-tools iproute2
ifconfig ip a(addr), ip l(link)
route ip r(route)
netstat ss
netstat -i ip -s l(link)
arp ip n(neighbor)

あと、デフォルトのファイルシステムがXFSになっている。後発のext4がなぜデフォルトファイルシステムの座を譲ってしまったのか理由を調べたいな。

園芸家とプログラマ

Paul Grahamの「画家とハッカー」というエッセイがあったけど、園芸家とプログラマも意外と似たところがあるかもね。という話をサイボウズ式に書かせて頂いた(「「園芸家12カ月」──techな人にお勧めする「意外」な一冊(7)」)。興味があれば読んでみて下さいな。

結局、マニアと言われる人種は分野を超えて通じる何かがあるという話かもしれないけど。

ハッカーと画家 コンピュータ時代の創造者たち

ハッカーと画家 コンピュータ時代の創造者たち

園芸家12カ月 (中公文庫)

園芸家12カ月 (中公文庫)

Plan9portがRetina対応に

「lsを読まずにプログラマを名乗るな!」という書籍が話題になっていて、lsで1冊の本になるなんてGNU/Linuxだせぇと脊髄反射的に思ったので、Plan 9のls.cを読んで心を落ち着ける。

そんなこんなでPlan9portを触っていたら、Retinaディスプレイ対応の文字が。おぉ!

起動時に環境変数devdrawretina=1を設定すればよい。DPIがデフォルトで110のところが220になるらしい。このままだとフォントが細かすぎて可読性が低いので、fontsrvを立ち上げてOS Xのフォントを指定する。ただし、OS Xではデフォルトでfontsrvがコンパイルされないので、野良ビルドする。

$ cd src/cmd/fontsrv
$ mk
$ cp o.fontsrv ../../../bin/

あらかじめfontsrvを起動しておく。

$ fontsrv &

"9p ls font"で利用可能なフォントがわかる。「/mnt/font/フォト名/フォントサイズ/font」がパス名。

で、フォントを指定してacmeを起動する。やっぱりPlan 9のデフォルトフォントよりもMenloの方が読みやすいよね。

$ devdrawretina=1 acme -f /mnt/font/LucidaGrande/24a/font -F /mnt/font/Menlo-Regular/24a/font &
$ devdrawretina=1 9term -f /mnt/font/Menlo-Regular/24a/font rc &

関連する話題

lsを読まずにプログラマを名乗るな!

lsを読まずにプログラマを名乗るな!

Spotlight検索が意外と便利

最近、ぱらぱらとカンフーマックを読みながら、Macの使い方を見直している。特に影響を受けたのがアプリケーションランチャーとしてのSpotlightの使い方と、Dockの簡素化という部分。これまでは、アプリケーションランチャーとしてDockを使ってきたけど、Spotlight検索を使うようになった。補完が効くので、数文字入力すれば候補が現れるので、リターンで起動。キーボード操作だけでアプリを起動できるので、慣れるとこれがものすごく便利だ。Spotlight検索のキーバインドはずっと殺していたけど、今はCommand+SpaceをIMの切替、Option+SpaceをSpotlight検索に割り当てている。

また、ちょっと変わったところでは、Spotlightは電卓代わりにもなって、検索フィールドに数式を入力すると計算してくれる。計算結果はCommand+cでコピーできる。シェルでbcコマンドを使うよりスマートだ。

Dockをランチャーとして使わなくなったので、余計なアイコンは消したくなってきた。ということで起動中のアプリだけをDockに表示するシンプルモードに設定する。これはコマンドラインからの設定が必要。あぁ、すっきり。

defaults write com.apple.dock static-only -bool TRUE; killall Dock

カンフーマック ―猛獣を飼いならす310の技

カンフーマック ―猛獣を飼いならす310の技

Cloudmonkey on DevCloud2

前回作ったDevCloud環境で、Cloudmonkeyを動かしてみる。CloudmonkeyはWeb portalから操作するかわりに、REST API経由で操作するCLIツールで、Pythonで実装されている。バージョン4.0.2にはまだ入ってないようだけど、4.2.0-SNAPSHOPではすでにマージされているので、すぐに試すことができる。

devcloud# cd tools/cli
devcloud# python setup.py build
devcloud# python setup.py install

これでインストールが終わったので、API keyとSecret keyを登録する。これらの鍵はWeb portalの「Accounts->admin->Users->admin」などからわかる。

devcloud# cloudmonkey
> set apikey XXXXX
> set secretkey XXXXX