Ansible + Mackerel APIによる1000台規模のサーバオペレーション

Ansible と Mackerel API を組み合わせて、1000台規模のサーバ群に対して同時にパッケージの更新やその他のサーバオペレーションを行う方法を紹介します。 タイトルに Mackerel とありますが、それほど Mackerel に依存しない話です。

背景

社内では、サーバ構成管理ツールとして Chef を使用しています。 Chef Server は運用が大変なので使用しておらず、knife-solo と Mackerel APIを組み合わせてホストと Chef role とのマッピングに Mackerel のロール情報を用いています。 また、Mackerel の Ruby クライアントを利用して recipe 内で API を叩いて、Mackerel から動的にホスト情報を参照するといったこともやっています。

今も構成管理は全て Chef でやっているのですが、Chef Server を用いていないため、cookbook の変更を基本的には1台1台適用することになります。(頻繁に変更するミドルウェアのクラスタ設定などは Capistrano を用いて該当設定ファイルのみ配っています。) これでは、例えば mackerel-agent のようなパッケージを全てのホストに一斉に更新をかけるといったことができません。

そこで、エージェントレスな、並列実行に優れたサーバ構成管理ツール Ansible に注目しました。 並列実行だけでなく、後述するようにDynamic Inventoryを使ってサーバ管理ツールとの連携もしやすいことも重要です。

1000台規模で Ansible を使う

1000台規模で Ansible を使うために、いくつかのパフォーマンスチューニングを行います。 パフォーマンスチューニングについては、Ansibleの公式ブログが詳しいです。

Ansible Performance Tuning (for Fun and Profit)

まず、forks で並列度を上げます。デフォルトは 5 ぐらいなので、100 とかにしてみます。 local_action とか使ってると詰まるので、手元のファイルを送信するのではなく、どこかのファイルサーバに置いて、各ホストから落としてくるほうがよさそうです。

次に、SSH接続を高速化します。 OpenSSH の ControlPersist を使うと、SSH のコネクションを維持するようになり、再接続のオーバヘッドを軽減できます。 さらに、pipelining を有効にすると、かなりのパフォーマンスが改善されます。sudo を使う場合、/etc/sudoersで requiretty を無効にする必要があります。 以前は、Accelerated Modeを使えばよかったようですが、今では SSH pipelining を使うほうがよいようです。 ただし、RHEL 5,6環境では OpenSSH のバージョンが古くて、paramiko という pure PythonでのSSH実装にフォールバックします。paramiko は ControlPersist 機能がないため、毎回接続が発生するので、これを回避するために、Accelerated Mode を使うとよいようです。

リポジトリルートに下記のような設定を書いた .ansible.cfg を設置して、他のチームメンバーも同じ設定を使えるようにしておきます。

[defaults]
transport=ssh
pipelining=True
forks=100

[ssh_connection]
ssh_args=-o ControlMaster=auto -o ControlPersist=30m
scp_if_ssh=True 
control_path=%(directory)s/%%h-%%r

Mackerel APIと組み合わせる

通常、Ansible では静的な inventory ファイルに実行対象のホストを記述する必要があります。 特に1000台以上もサーバを持っているとファイルで管理はしていられません。 普段、Mackerel などのサーバ管理ツールを使っている場合、API経由でホスト情報がとれるので、なるべくホスト情報を別のファイルを管理したくありません。 そこで、Ansible の Dynamic Inventory を使います。 Dynamic Inventory は EC2 や Zabbix のホスト情報を inventory として使用することができる機能です。 実体は、EC2ならEC2のAPIを用いて、定められたフォーマットのJSONを出力するスクリプトです。 Dynamic Inventory スクリプトの書き方は Developing Dynamic Inventory Sources — Ansible Documentation に書かれています。

Mackerel API Dynamic Inventory

http://docs.ansible.com/developing_inventory.html#tuning-the-external-inventory-script によると、JSON出力に_meta キーを含めるフォーマットのほうが実行が高速らしいです。 つまり、下記のように、ロール名やサービス名のようなグループ名をキーとして、グループ内のホスト識別子(ホスト名に限らない)をバリューとしたJSONを出力するスクリプトを書けばよいです。 各ホストの情報は、_meta => hostvars のキーの中にいれておく。hostvars は playbook の中で参照することができる。例えば、Mackerel の status に応じた task を書くことができます。

{
  "Example-Blog_app": ["blogapp001.host.h", "blogapp002.host.h"],
  "Example-Blog_proxy": ["blogproxy001.host.h", "blogdproxy002.host.h"],
  ...
  "Example-Blog": ["blogapp001.host.h", "blogapp002.host.h", "blogproxy001.host.h", "blogdproxy002.host.h"]
  ...
  "_meta" => { 
    "hostvars" => {
      "blogapp001.host.h" => {
        "status": "working",
        "roleFullnames": ["Example-Blog::app"]
        ...
      },
      "blogapp002.host.h" => {
        ...
      },
      ...
    }
  }
}

簡単な Mackerel 用の Dynamic Inventory スクリプトを書いてみました。 Ansible は Python で書かれているので、本当は Python で書くのが筋がよさそうですが、Python クライアントがないので、とりあえず Ruby で書きました。 言語による大した違いはないと思います。

実行方法は簡単で、-i オプションに実行権限をつけてスクリプトを渡します。 パターンを all にすると、inventory 内の全ホストが対象になります。

$ ansible -i ./bin/mackerelio_inventry all --list-hosts

playbook

playbooks リポジトリのディレクトリ構成

Ansible の公式ドキュメントに構成のベストプラクティスが書かれています。 Best Practices — Ansible Documentation 今回は、そんなに複雑な構成管理をするわけではないので、シンプルなディレクトリ構成にしています。

  • 普通のフルプロビジョニング用途とは思想が異なり、単発のオペレーション用途なので、playbook ファイルはオペレーション単位で作る。 mackerel-agent.ymlmkr.ymljq.ymlなど。
  • script/ 以下に Dynamic Inventory スクリプト、bin/以下に直接実行するファイルを置く。bin/mackerelio_inventryscript/mackerelio.rb を bundle exec でラップしたもの
  • roles 以下に使用する Ansible Role を置く。これは普通。Ansible Galaxy | Find, reuse, and share the best Ansible content
.
├── Gemfile
├── Gemfile.lock
├── bin
│   ├── ansible-install-simplejson
│   ├── ansible-pssh
│   └── mackerelio_inventry
├── mackerel-agent.yml
├── mkr.yml
├── jq.yml
├── requirements.yml
├── roles
│   └── mackerel.mackerel-agent
├── script
   └── mackerelio.rb
└── vars
     └── mackerel-agent-plugin

jq のインストール

例として、実際に jq を配布してみます。jq.yml に下記のような設定を書きます。jq は apt リポジトリはありますが、yum リポジトリはない?ようなので、実行ファイルをそのまま get_url モジュールでダウンロードするだけです。サーバのディストリ情報などは使わないため、gather_facts は不要なので切っておきます。

---
-
  hosts: all
  sudo: yes
  gather_facts: no
  tasks:
  - name: install jq
    get_url: url=http://stedolan.github.io/jq/download/linux64/jq dest=/usr/local/bin/jq mode=0755

下記コマンドで実行します。

$ ansible-playbook --ask-sudo-pass -i ./bin/mackerelio_inventry ./jq.yml

だいたい20分くらいで数千台のサーバに配り終えました。それなりに時間はかかりますね。 失敗したホストに対してのみリトライしたければ上記コマンドに --limit @/Users/y_uuki/jq.retry をつけて実行してやります。

jq は all を指定して全てのホストに配りましたが、Mackerel のサービスやロール単位で task を実行することができます。 Patterns — Ansible Documentation に、対象ホストを絞り込むためのパターン指定方法があります。ワイルドカードやOR条件、AND条件、NOT条件などでそれなりに柔軟に指定できます。

補足

Capistrano などの並列sshツールとの違い

Capistrano でも複数ホストに同時にコマンド実行することは可能です。 ただし、実際に 1000 台に対して実行すると、手元のsshで詰まったり、実行に失敗したホストの情報がよくわからなかったりするので、複数回実行します。 途中で詰まったりして1回の実行に1時間以上かかるので、結構大掛かりになります。 Capistrano v2 を使用していますが、Capistrano v3 からSSHのバックエンドが sshkit になっているので、もう少しはマシかもしれません。

Ansible では、仮に失敗したホストがあっても、失敗したホストのリストをファイルに残してくれます。次回は失敗したホストのみ適用したり、失敗したホストのみ cssh などを使って、手動でオペレーションすることも可能です。 一方実行時間は Capistrano ほどではないですが、それなりに時間はかかります。この辺りは後述する Ansible v2 の free strategy を使うか、gather_facts no を指定して各ホストから情報収集ステップをスキップして、代わりに Mackerel の Inventory から取得した情報だけでホスト情報を賄うなどの高速化の可能性があります。

わざわざ Ansible や Capistrano のようなレシピ的なものに記述するタイプではなく、単純にコマンド実行するツールで十分かもしれません。 Parallel Distributed Shell(pdsh)を使って複数ホストでコマンドを同時実行する - えこ日記 に Parallel ssh や Cluster ssh など複数のリモートホストに同じコマンドを一斉実行するためのツールがまとめられています。 しかし、誰がいつどのようなオペレーションをやったのか記録が残らないかつ、適用前にPull Requestにしてレビューすることができないため、レシピとして記述するタイプのツールのほうが Infrastructure As Code の観点からみても優れていると思います。 (ワンタイムな操作の場合は日付を付けた playbook を用意するとよいかもしれません)

さらに、前述の get_url モジュールのように Ansible は標準モジュールが充実しており、ある程度冪等性を期待できるオペレーションがやりやすいのでそのあたりも加点ポイントです。

ansible-pssh

本当に単純にコマンドを実行したい場合、ansible-pssh というスクリプトを用意して、shellモジュールを使って実行させる。

#!/bin/bash

set -ex

ANSIBLE_INVENTORY_SCRIPT=./bin/mackerelio_inventry

PATTERN=$1 # Example-Bookmark
if [ -z $PATTERN ]; then
    echo 2>&1 "role required: ansible-pssh ROLE COMMAND"
    exit 1
fi

COMMAND="${@:2:($#-1)}"
if [ -z $COMMAND ]; then
    echo 2>&1 "role command: ansible -pssh ROLE COMMAND"
    exit 1
fi

exec ansible --ask-sudo-pass -i $ANSIBLE_INVENTORY_SCRIPT $PATTERN -m shell -a "$COMMAND"
$ ./bin/ansible-pssh all 'curl -sSfL https://raw.githubusercontent.com/mackerelio/mkr/master/script/install_linux_amd64 | sudo bash'

python-simplejson

CentOS 5 環境だとプリインストールされている Python のバージョンが古くて、ansible のモジュールに必要な python-simplejson がインストールされていない。 そこで、あらかじめ下記のようなスクリプトを実行しておく。raw モジュールだと python-simplejson を使わないので、実行できる。

#!/bin/bash

set -ex

ANSIBLE_INVENTORY_SCRIPT=./bin/mackerelio_inventry

PATTERN=$1 # Example-Bookmark
if [ -z $PATTERN ]; then
    echo 2>&1 "role required: ansible-install-simplejson PATTERN COMMAND"
    exit 1
fi

exec ansible --ask-sudo-pass -s -i $ANSIBLE_INVENTORY_SCRIPT $PATTERN -m raw -a "[ -e /usr/bin/yum ] && yum install -y python-simplejson || true" # https://github.com/ansible/ansible/issues/1529

Ansible v2

What's New in v2 - AnsibleFest London 2015

先日の AnsibleFest London 2015 で Ansible v2 の発表がありました。 内部実装の設計変更やエラーメッセージの改善などの変更がありますが、Execution Strategy 機能に注目しています。 Execution Strategy は task の実行方式を変更できる機能で、従来の liner 方式に加えて、他のホストの task 実行をまたずになるべく速く task を実行できる free 方式が実装されるようです。 これにより、高速実行できることを期待できます。

関連

以前にMackerel APIの利用例を書いていました。

1年以上前に Chef と Ansible について書いていました。

tagomoris さんのスライドは非常に参考になりました。かなり近い思想で運用されているようにみえます。

まとめ

若者なので大量に ssh しまくっています。

Ansible と Mackerel API を組み合わせたサーバオペレーションを紹介しました。 また、1000台規模で使えるツールであることを確認しました。

Mackerel の思想の一つとして、APIによるホスト情報の一元管理が挙げられます。Ansible の静的Inventoryファイルではなく、Dynamic Inventory により、Ansible 側でホスト情報を管理しなくてすむようになります。 さらに、Mackerel に登録したサービス、ロールやステータスなどのホスト情報を扱えるようになるのが便利なところです。

本当は1台のホストから多数のホストに接続する push 型ではなく、Gossipプロトコルなどのアドホックなネットワーク通信を用いた Serf、Consul のような pull 型のほうが圧倒的にオペレーション速度は速いはずですが、そもそも pull 型の実行ソフトウェアを各ホストにインストール/アップデートしなければならないため、このような仕組みは必要だと思っています。

積極採用!!!

1000台規模のサーバを抱えていると普通は発生しないような課題がたくさんあります。 はてなでは、そんな大規模環境特有の課題に取り組みたいWebオペレーションエンジニア(いわゆるインフラエンジニア)を積極採用中です。

Twitter

それから君のOSSすごかったよ

オンプレはみんな溶けるし、はてなの勉強してるし、Perl感だし、書いてる内容はinnodb、彼女はMySQLのことを考えてる。それから君のOSSすごかったよ。

パフォーマンスの観点からみるDockerの仕組みと性能検証 #dockerjp

Docker Meetup Tokyo #4 にて「Docker Performance on Web Application」という題で発表しました。 発表内容は、下記の2つの記事をまとめたものに加えて、最新バージョンの Docker 1.4 での ISUCON ベンチマークと、storage-driver として Device Mapper + Docker 1.4 から実装された [OverlayFS:title=https://github.com/docker/docker/pull/7619] を試しました。

この記事は、上記2記事で、いくつか難しいポイントがあったとフィードバックをいただいたので、Docker Meetup での発表内容を少し詳しめに説明したものになります。

1. Dockerのパフォーマンスについて重要な事はなにか

Docker のパフォーマンス検証に関する IBM の Research Report である An Updated Performance Comparison of Virtual Machines and Linux Containersの内容などから Linux Containers、UNION Filesystem、Volume、Portmapper、Host Networking が重要な要素であることがわかりました。

Docker Items

Linux Containers

まず、Linux Containers については、コンテナという機能があるのではなく、カーネルの各リソース(ファイルシステム、ネットワーク、ユーザ、プロセステーブルなど)について実装されている Namespace によって区切られた空間のことをコンテナと呼んでいます。つまり、Namespace で隔離された空間でプロセスを生成するというモデルになります。(普通のプロセスと扱いが変わらないので、Dockerコンテナの起動が速いのは当然)全ての Namespace を同時に使う必要はなく、一部の Namespace を使うことも当然可能です。(例えば、docker run コマンドの --net=hostオプションは、Network Namespace を使っていないだけのはず) Linux Containers は単体のカーネルで動作するので、親と子で別々のカーネルをもつ Hypervisor による仮想化と比べて、CPU命令をトラップしたり、メモリアクセスやパケットコピーの二重処理をしなくていいので、オーバヘッドがありません。(もちろん、VT-xやSR-IOVなど、ハードウェア支援による高速化手法はある)

@ten_forward さんの記事 コンテナの歴史と Linux カーネルのコンテナ関連機能についての割とどうでも良い愚痴 - TenForwardの日記 を読むとよいと思います。

Linux Containersについて実装レベルで理解されている方々にとっては、普通のプロセスと対して変わらないし、わざわざ検証するまでもないかと思いますが、production でのサービスインを考える上で一応見ておかないといけないと思いました。

UNION Filesystem

次に UNION Filesystem については、下記の公式画像をみるとだいたいわかった気になれます。

UNION Mount という手法でファイルシステムの層が実現されており、要は既にマウントされているポイントに対して重ねて別のブロックデバイス(ディレクトリ)をマウントし、最上位層のみを read-write 属性に、それ以外の層を read-only にするようなイメージです。複数のブロックデバイス(ディレクトリ)を同じマウントポイントからアクセスできます。 基本的に、任意のファイルシステムの状態から新規書き込みの分だけ上位層に書くようにすれば、最下層にベースファイルシステムがあり、その上に差分データだけを持つファイルシステム層が乗っていくようになります。

このような仕組みを実装するにあたって、ブロックデバイスレベルでの実装とファイルシステムレベルの実装があります。 Docker では storage-driver というオプションにより、UNION Filesystem の実装を切り替えることができます。 aufs,btrfs,devicemapper,vfs,overlayfs を使用可能です。 devicemapper がブロックデバイスレベルでの実装であり、aufs,btrfs,overlayfs がファイルシステムレベルでの実装となります。(vfs は docker側で無理やり層を作ってる?) Device Mapper は特定のファイルシステムに依存しないかつ、カーネル標準の機能なので気軽に使いやすいというメリットがあります。(LVM にも使われている) 一方で、Device Mapperの場合、イメージ層の作成・削除の性能は落ちるという検証結果もあります。(Comprehensive Overview of Storage Scalability in Docker | Red Hat Developer Blog) 汎用的でプリミティブな機能を持ったDevice Mapperを使って逐一、層となる仮想ブロックデバイスの作成や削除をするより、専用の機能を実装したファイルシステムレベルの実装が速そうというのはなんとなくわかる話ではあります。

発表内でデフォルトが Device Mapper とか言っていましたが、RHEL/CentOSでは事実上 Device Mapper がデフォルトであるというのが正しいです。 お詫びして訂正します。(ISUCON ベンチマークで使った Ubuntu 14.04 では、modprobe aufsした状態でデフォルトが devicemapper になっていたはずなんだけど、カーネルバージョン変えてたし、なんかミスってたのかもしれない) ちゃんとコードを読んでみると https://github.com/docker/docker/blob/5bc2ff8a36e9a768e8b479de4fe3ea9c9daf4121/daemon/graphdriver/driver.go#L79-84 となっており、aufs,btrfs,devicemapper,vfs,overlayfs の順になっているようで、デフォルトが AUFS というのが正しいです。

Volume

UNION Filesystem を使うと複数の層に対して、I/O要求もしくはその他の処理が多重に発行されるはずで(最適化はされているとは思いますが)、オーバヘッドが気になるところです。Docker には Volume という機能があり、これを使うと指定したディレクトリを UNION Mount しないようになります。したがって、そのディレクトリ以下のファイルへのI/O効率がよくなる可能性があります。

Volume 自体はパフォーマンス目的で使うものではなく、コンテナ間もしくはホスト・コンテナ間でデータを共有するためのものです。

Portmapper

コンテナ間通信やホスト・コンテナ間通信では、ホスト側の iptables によるNAPTで実現されています。(172.17.0.3がコンテナのIP)

-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 10.0.3.0/24 ! -d 10.0.3.0/24 -j MASQUERADE
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.3:8000

ただし、iptablesが利用できない環境のために、コンテナ間通信のみ docker-proxy というユーザランドのプロキシが使用されます。docker-proxy自体はiptablesを使っている使っていないに関わらず起動しているようです。

docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.3 -container-port 8000

iptables、つまりカーネルランドの netfilter で NAPT できるところをユーザランドのプロキシを経由すれば明らかにオーバヘッドが大きくなるという予想がつきます。

Host Networking

Docker では Network Namespace を使わずに、ホストと同じ Namespace を利用する Host Networking 機能があります。 Host Networking は --net=host で使えます。 ネットワークについては、ホストでプロセスを起動するのと変わらないことになります。 これならば、先ほどの Portmapper が必要なくなるため、NAPTのオーバヘッドがなくなります。

Host Networking については @deeeetさんの DockerのHost networking機能 | SOTA が詳しいです。

2. Docker化したISUCONアプリケーションのベンチマーク

ベンチマークは、Nginx と MySQL をこれまで紹介したオプションを切り替えて Docker化 して、それぞれのスコアを比較しました。 環境は前回との差分はより新しい Linux カーネル 3.8.0、Docker 1.4.1 を使っている点です。 詳しい内容は下記のスライドを参照していただくとして、結果は Nginx を Docker 化したときに Host Networking を使わずNAPTさせたときに、15%程度スコアが落ちるというものでした。それ以外の、VolumeのOn/Off や storage-driver の切り替えによるパフォーマンスの変化は ISUCON4予選の環境では起きませんでした。

Host Networking と Volume ON の状態で、性能が変わらないのは予想通りですが、storage-driver の切り替えによりパフォーマンスに変化がないのは意外でした。 これはおそらく、今回の環境では、データが全てメモリにのっているため、Read I/Oはほぼ発生していないということと、Write I/Oは UNION FS の最上層のみに適用すればよいので、複数の層があることによるオーバヘッドがあまりないのではないかと考えています。

NAPTのオーバヘッドが顕著であり、これは docker-proxy プロセスがCPU を 50% ほど使用しているためです。 iptables を有効にしているのになぜ docker-proxy が使われるのかと思いましたが、iptablesのルールに宛先がループバックアドレスの場合はコンテナへルーティングされないようです。

-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER

benchmarker はデフォルトでは 127.0.0.1:80 へ接続するため、benchmarker - Nginx 間での接続に、ホストの 0.0.0.0:80 で LISTEN してる docker-proxy が使われてしまうという事態になっています。 benchmarker のオプションで --host <host eth0 ipaddr> としてやると、iptables でルーティングされるようになるため、スコアはDocker化していない状態とほぼ同じになりました。

なぜループバックアドレスだけ外されるのか

宛先アドレスが 127.0.0.1 のままだと、コンテナがパケットを受信して返信するときに、宛先アドレスを 127.0.0.1 にしてしまい、コンテナ自身にループバックします。 ループバックを避けるため、以下の様なPOSTROUTINGルールでNAPTする設定が必要なようです。 127.0.0.1 がコンテナのIPに書き換わり、コンテナからホストへの返信時に宛先アドレスがコンテナのIPになり、結局自分に戻ってくるようにみえます。しかし、Docker は仮想ブリッジ経由でホスト側のネットワークとコンテナ側のネットワークを接続しているので、仮想ブリッジ(docker 0)のヘアピンNAT(NATループバック)を有効にすることで、ホスト側へNATしてくれるようです。(この辺りすこし怪しい)

-A POSTROUTING -p tcp -s <container ipaddr>/28 -d <container ipaddr>/28 --dport <container port> -j MASQUERADE

ただ、RHEL/CentOS 6.5環境下で /sys以下が readonly でマウントされており、 /sys/class/net/{ifname}/brport/hairpin_mode に書き込めないため、仮想ブリッジ環境でヘアピンNATモードを有効にできないようです。(RHEL/CentOS 6.5環境のみかどうかはちゃんと調べてないです) ヘアピンNAT サポートが一旦、マージされてリバートされたのもこのためです。

発表スライド

さらに詳しい情報は下記スライドを参照してください。

Keynote テーマは弊社のデザイナ @murata_s さんが作ったテーマを使わせてもらっています。

関連情報

RedHatの @enakai さんの必読のスライド。コンテナとVMM、Dockerのファイルシステム、ネットワークについて詳しく書かれていて非常に参考になりました。 26枚目の、iptables でループバックアドレス宛のパケットだけ外されている理由がわからないという点についての回答は前述の仮想ブリッジでのヘアピンNATの話かなと思います。

Linux Containers については、LWN の記事と @ten_forward さんの記事が参考になると思います。

Device mapper については、下記スライドが参考になりました。

UNION Mount については、Oreilly の Programmer's High のブログが参考になりました。

まとめ

Docker化したWebアプリケーションにおけるパフォーマンス研究の成果について書きました。 IBMのレポートの内容から、Linuxカーネルとの接点となるUNION Filesystem や、その他 Host Networking、Volume などがパフォーマンスにおける重要な要素であることがわかりました。そこから、自分で検証してみて、ISUCON4予選問題の範疇では、iptables を使わずに docker-proxy というユーザランドのプロキシの使用を回避さえすれば、いずれのパターンでも性能の変化はないことがわかってきました。

iptablesを切って、nf_conntrack を切ってチューニングするような環境ではそもそもまともにDockerは動かせないので、ギリギリまでリソースを使い切るようなホストの場合はさすがにI/Oまわりのパフォーマンスが問題となってくると思います。 Linuxカーネル、特にUNION Filesystem周りでパフォーマンスに関する知見があればぜひ教えていただけると助かります。

パフォーマンスの観点から Docker を支える技術を調査してきてましたが、だいたい満足しました。1年半Dockerを触ってきて、知見もかなりたまってきたので、Production で Docker を投入できそうな頃合いだと思っています。

会場を提供していただいた Recruit Technologies の皆様、イベントを企画運営していただいた皆様、どうもありがとうございました。 非常に有意義なイベントでした。

はてなではWebオペレーションエンジニア(いわゆるインフラエンジニア)を募集しています。

Webオペレーションエンジニア職 - 株式会社はてな

宇宙飛行士の採用についての本

「ドキュメント 宇宙飛行士選抜試験」を読んだ。

この本は、NHKスペシャルで2009年に放送された番組「宇宙飛行士はこうして生まれた ~密着・最終選抜試験~」が書籍化されたものだ。 JAXAによる宇宙飛行士の選抜試験、特に最終試験における10人の候補者それぞれの背景や試験中の様子を通して、宇宙飛行士とは何か、宇宙飛行士に求められるものは何か、また宇宙飛行士を選抜するとはどういうことかについて、ドキュメンタリー形式で描いている。

ドキュメント 宇宙飛行士選抜試験 (光文社新書)

ドキュメント 宇宙飛行士選抜試験 (光文社新書)

タイトルに選抜試験とあるので、当然宇宙飛行士の選抜試験が具体的にどのようなものかが書かれている。 しかし、一般の企業での採用や就活生の心構えを指南するといった趣旨が中心の本ではない。 むしろ試験内容そのものについては、宇宙ステーションやシャトルでの生活を模した閉鎖環境でのロボット作りやディベートなど、宇宙飛行士という特殊な職種を前提とした内容なので、これをそのまま参考にはもちろんできない。 この本が実際に伝えたかったのは、「宇宙飛行士とは何か」ということだと思う。

「宇宙飛行士とは何か」という問いに対する明確な回答は多分難しくて本文には明示的に書かれていない。 きっと人によって、答えが変わってくる類の質問だと思う。 本来の宇宙の開発・研究のための現地作業員という役割はもちろんある。しかし一方で、世間に対する広告塔であったり、人類代表のように扱われることから人々の希望になることも求められるという。 実際に、候補者の方々の宇宙飛行士になりたい理由として、「医師である自分が宇宙から元気な姿を見せれば、世界中の病に苦しむ人々に、希望を与えられるはず」や「宇宙飛行士になって、子どもたちに宇宙の不思議や魅力を紹介し、科学することの面白さを伝えたい。」という本来の役割とは異なる一種の偶像的な役割が挙げられていた。 これはスポーツ選手に近い性質ではないかと思った。

宇宙飛行士に限らず、自分の仕事が何なのかをシュッと回答することは意外と難しい。 Webオペレーションエンジニアなんてわけがわからん。 逆に言えば、自分の仕事が何なのかを自分の言葉で表現できるようになったときが一人前になったときと言えるのかもしれない。

気になる試験内容は、宇宙兄弟で描かれていた内容とほぼ同じような感じで、宇宙兄弟を読んだことがある人にとっては目新しさや驚きはあまりないと思う。 逆に、試験内容を漫画向けに誇張したりしなくても十分にエンターテイメントになることのほうがすごいかもしれない。

宇宙兄弟(1) (モーニング KC)

宇宙兄弟(1) (モーニング KC)

ところで、最近、人材の採用について少し興味がでてきた。弟がちょうど就活ということもある。 採用という観点でいくつか気になったことを書いてみる。 さきほど、書かれている試験内容はそのまま参考にはならないと書いた。 しかし、労働環境が過度のストレス環境であることや様々な能力を平均的に高いレベルで要求されるという特殊な前提を省いてみると、特にNASAの面接方法について部分的に参考になることはいくつかあった。

まず、「なぜ今、ここにいるのか」という質問を通して、候補者の人生を知ろうとしていることだ。 なるべくリラックスした状況を作り、候補者のこれまでの人生経験を詳しく聞くことで、その人間が信用に値するのか、一緒に働きたいと思えるような人間かどうかを見極めようとしている。似たような話を読んだことがあるなと思ったら、だいぶ前に tomomii さんがいい記事を書いてた今週のお題:わたしとはてなとの出会い - tomomii日記。 今思えば、自分がはてなに入社するときの jkondo さんとの最終面接はこんな感じだったように思う。

次は、全ての試験が終わった後に行われるパーティで、候補者たちの「本当の姿」をみるという点だ。 面接では必ずしもいい状況で望めなかった候補者がいた可能性を考慮して、誤った判断を防ぐためだという。 少なくとも、自分の会社のエンジニアなら面接のような人に見られる類の緊張感がある状況で、何か仕事をするということはあまりない。ブログのほうが本当のことを書けるという人もいるかもしれない。面接でうまく話せなかったといって、仕事ができないということにはならないと思う。 これは前々からそう思っていたけど、NASAでもそのようなことを考慮しているのは意外だった。

最後に、JAXAとNASAでは試験内容が異なり、NASAではほぼ面接だけで採否が決まるというのも印象に残った。 NASAでは、技術的な専門知識やメカを操作する技量などの技術的バックグランドは、候補者の履歴書と、面接でのやりとりで十分見極められていると考えているらしい。 JAXAの場合、選抜試験で採用した人間は全員、宇宙飛行しなければならず、確実に宇宙に行ける人間を採用しなけらばならないため、採用試験が極めて厳密になる。これは、日本とアメリカの宇宙開発の規模、歴史、方針の違いによるものとのことだ。 本文中には詳しく書かれていなかったが、日本のほうが宇宙開発の歴史が浅いため採用経験も浅いかつ宇宙開発費も潤沢ではないため、厳密に試験する必要があるという意味ではないかと思った。 前提が違えば、試験内容も当然異なるということがよくわかる。

宇宙飛行士になりたいと思ったことは多分一度もないし、宇宙に行きたいという願望も特に無いけど、宇宙がでてくるSFの話は大好きなので、面白く読めた。 元となったテレビ番組の方も観てみたい。