入門 Kubernetes

先週『入門 Kubernetes』という本が出版されました。わたしもレビュアーの一人として、この本の製作に携わらせていただきました。

入門 Kubernetes (オライリー・ジャパン)

翻訳者の松浦さんのブログにもある通り、この本の価値のひとつは原著で古くなってしまった部分が訳注として補われているところにあると思います。

原著はKubernetes 1.5あるいは1.6をベースに書かれています。日本語版には、翻訳完了時の最新版であるKubernetes 1.9までに加えられた変更などを、注釈などの形でできるだけ反映しました。

英語に堪能な方であっても、これから読むなら翻訳版の方を読む方がよいでしょう。

また、原著は内容こそ素晴らしいものの、ディテールに難がありました。率直に言うと「サンプルをコピペしても動かない」ことや「説明とサンプルが微妙に一致しない」ことなどがありました。ここに関しても、松浦さんやわたしなどが原著に Errata を提出しつつ、丁寧に内容を修正して日本語版に反映させました。

Kubernetes は今でも勢いがあり、この本も数年後には「古い」本になるだろうなーと思います。買おうか迷っているのであれば、なるべく早く買ってしまうことをオススメします 😉

広告

輪読会:Site Reliability Engineering – 8, 9, 10章

職場で SRE 本の輪読会をしている。職場の Qiita::Team でサマリをまとめて、輪読会仲間にシェアしたのだけれど、別にこの内容は一般に公開してもかまわないものなので、ブログ記事にしてみた。

以下で括弧書きされているものは、注釈と僕の心の声とが半々である。また、本記事の画像は上記の SRE 本からの引用である。

Chapter 8. Release Engineering

Google ではリリースに対して責任を持つ Release Engineering という仕事がある。そういう職種もある。

The Role of a Release Engineer

  • コードが変更されてからリリースされるまでの時間を測っていたりする。
    • この時間を release velocity と呼ぶ。
  • リリースエンジニアはリリースに必要なツールを作る。
  • リリースエンジニアはリリースのベストプラクティスを作る。
    • 一貫性のあるプラクティス
    • 繰り返しデプロイできるプラクティス。

(リリースエンジニアと SRE が協調して働く… みたいな記述があるので、これは SRE とは別な Role なのかな 🤔)

Philosophy

リリースエンジニアの哲学には4つの原則がある。

Self-Service Model

各々のチームがどのくらいの頻度でいつリリースするか自分で決められる。リリース/デプロイツールで、人手を極力排してリリースできるようにする。

High Velocity

少ない変更をどんどんリリースする。テスト結果が “Green” なら常にリリースするようにしているチームもある。

Hermetic Builds (日本語でどう訳されてるんだ… 外部環境に依存しないビルド、みたいな感じかな?)

ビルド/リリースツールは一貫性があり、かつ何度でも実行できなければならない。複数人が同じコードを同時にビルドしたら、成果物は同じでなければならない。

ビルド結果はビルドマシン内のライブラリやソフトウェアには依存しない。ビルドは 特定のバージョンの コンパイラやライブラリに依存する。ビルドは self-contained であり、外部環境には左右されない。(要ディスカッション: たとえば Docker みたいなビルド環境を準備しておいて、そこの中でビルドが閉じている…みたいなことを言いたいのでは)

ビルドツール自身も閉じた環境でビルドしている。

Enforcement of Policies and Procedures

誰が何をできるかコントロールする。誰がコードを変更できるか、新しいリリースを作れるか… etc.

Continuous Build and Deployment

Google には Rapid という自動リリースシステムがある (ぐぐっても詳細が出てこない)。なんかすごいらしい。前述の4つの哲学 (Self-Service Model, High Velocity, etc.) を満足するためのツールらしい。

以下、ソフトウェア開発のライフサイクル @ Google。

Building

Blaze (OSS 版は Bazel) で Google のソフトウェアはビルドされる。Make, Gradle, Maven に類するもの。色んなプログラミング言語をビルド/テストするのに使える。Rapid の一部として組み込まれている。

Branching

コードは全て mainline (Git で言うところの master) にコミットされる。mainline からリリースブランチを切ってリリースする。リリースブランチへの変更を mainline に戻すことはない。バグ修正は mainline にコミットされ、リリースブランチに cherry-pick される。

(そもそもの開発時にどこにコミットする、みたいな話はなし)

Testing

mainline への変更にはテストが走る。

全てのテストが通ったビルドをリリースするのがオススメ (当然では…)。

リリースブランチをリリースするときにもテストを走らせる。全てのテストが通ったことを示すログを保存する。

テストには独立した環境を準備して、ビルド成果物に対してシステムレベルのチェックをする (これは具体的には何を言っているんだ…??)。テストは手動でも起動できるし、Rapid からも起動できる。

Packaging

Google には MPM (Midas Package Manager) という仕組みがあり、これを介してリリースが行われる。MPM は Blaze の成果物をパッケージにする。

# (英辞郎より)
Midas  {名-1} : 《ギリシャ神話》ミダス◆欲張りな Phrygia の王。Dionysus によって、手に触れる物すべてが金になる褒美(苦悩?)を与えられる。

MPM パッケージにはラベルがつけられる。例えば devcanary, production など。新しいパッケージを既存のラベルに適用すると、既存のラベルが示すパッケージが上書きされる。

(割と普通の仕組みだ)

Rapid

  • Rapid は Blueprints というファイルで設定される。
    • DSL で記述される。テスト対象やデプロイ方法、プロジェクトオーナーなどが書かれる。
    • Rapid で誰が何をできるかはロールベースの ACL で管理される。

  • Workflow には、リリース時に何をするかが記述される。
  • Workflow は他の Workflow を起動できる。

Jenkins で例えると (だいたい):

  • Rapid Client: GitHub Webhook / ユーザーのブラウザ
  • Rapid Service: Jenkins サーバ
  • Rapid Build Job: Jenkins ジョブ
  • Tasks (実際には Borg のジョブ): Jenkins のタスク

典型的な Rapid のリリースプロセスは次のような感じ:

  1. 所定のリビジョンナンバーからリリースブランチを作成する。
  2. Blaze でテストとビルドが並列で走る。これらは独立の環境で実行される。
  3. ビルドの成果物が canary deployment 用に準備される。
  4. ここまでの処理内容がログとして記録される。

Deployment

RapidBorg のジョブに MPM の新しいパッケージ (Blueprint ファイルに記述された) を使うように指示してデプロイできる。

さらに込み入ったデプロイには Sisyphus を使う。 Sisyphus は SRE チームが開発した汎用的リリース自動化フレームワーク (Sisyphus もぐぐっても情報が出てこない)。 Python でデプロイメント手順を記述する (??)。

Sisyphus なら簡単なことから難しいことまで何でもできる。たとえば、クラスタ内の全てのジョブを一気に更新したり、数時間かけてゆっくり更新したりできる。なんなら数日間かけることもできる (それうれしいの?)。

Configuration Management

設定管理はリリースエンジニアと SRE が協調してがんばる領域だ。設定管理は簡単っぽくみえるけど、下手にやると不安定さの元凶になる。設定は SCM で管理し、変更にはコードレビューが課されるべきだ。

設定は mainline のモノを使おう…というのが初期の発想だった。これで、バイナリのリリースと設定変更を分離できた。ただし、これだとジョブを明示的に更新しないと設定変更が反映されないので、実行中のシステムと mainline の設定情報とが乖離することがあった。

MPM パッケージにバイナリと設定ファイルを詰め込もう…というのが次の発想だった。設定変更はデプロイ時にしかできないけど、デプロイそのものは単にパッケージを配布するだけに簡素化された。

設定ファイルも MPM パッケージにしよう…というのがさらに次の発想だった。たとえば、ある機能をフラグで on/off できるようにしておいて、実際の on/off は設定パッケージの種類で決まる、など。この場合、「ある機能を含むバイナリ」そのものは再起動する必要がない。

外部のデータストアから設定ファイルを読み込む方法もある。Chubby とか Bigtable に設定を置くこともある。

Conclusions

It’s Not Just for Googlers

(言っていることは分かるが、 Rapid の辺りは完全に Google べったりだろうよ)

Start Release Engineering at the Beginning

リリースエンジニアは後付で作られることが多いけど、それだと大変なので、早い内にそういうロールを考えましょう。

Chapter 9. Simplicity

(短いし思想的なことが主なので、さらっと)

ユーザーは増えるし、機能は追加されるし、ハードウェアは変わるし、安定して稼働するシステムを作るのは難しい。究極的には “agility” と “stability” のバランスを取ることが SRE の仕事だ。

System Stability Versus Agility

“agility” のために “stability” を犠牲にすることはままある。未知の領域を手探りでコーディングすることもある。そういうときには、挑戦して失敗することが問題を真に理解するために必要なステップだったりする。

プロダクションでは “agility” と “stability” のバランスが肝要である。SRE はシステムを安定させることに腐心しつつ、それが agility の低下につながらないようにする。実際には reliable さと agile さには相関がある。reliable なシステム (例えばビルド) がないと、agile に開発できないし。

The Virtue of Boring

退屈なシステムは素晴らしい。予期せぬタイミングで不思議なトラブルが起こるようなシステムは最悪だ。

essential complexity と accidental complexity は区別しましょう。essential complexity は本質的な複雑さ。accidental なのはエンジニアリングで排除できる複雑さ。たとえば、速く動作するウェブサーバーを書くのは essentially complex だ。Java で GC のインパクトを軽減させるのは accidental complexity を減らす仕事だ。 (わかりにくくないですか、この例…)

  • SRE は accidental complexity が導入される変更は差し戻すべきだ。
  • SRE は定期的に複雑さを削減していくべきだ。

I Won’t Give Up My Code!

エンジニアは人間なので、自分が書いたコードに対して執着がある (そうなの?)。

次のような判断は基本的に悪手。

  • 後で必要になるかもしれないので、消すのではなくコメントアウトする。
  • 今はいらないかもしれないけど、消すほどではないので feature flag でロジックが通らないようにする。

書かれたコードは全て負債だと思え。

The “Negative Lines of Code” Metric

新しい行や変更された行はバグをもたらす可能性がある。短いコードは理解しやすく、テストもしやすい。つまり、コードを消すのは素晴らしいことだ。

Minimal APIs

“perfection is finally attained not when there is no longer more to add, but when there is no longer anything to take away”

「足すものが何もない状態ではなく、削るものが何もない状態が最も美しい」的な。

Unix 哲学的な、ひとつのことをうまくやる API を作りましょう、というお話。

Modularity

これも Unix 哲学にしたがう感じの話。ひとつのことをうまくやる API は Modular であるべきで、他の API と組み合わせて使えるようにしましょう、というお話。

Modular なシステムであれば、バグ修正も局所的なリリースで直せる。

API の提供側は API をバージョニングするべき。そうでないと、API のクライアントにコードの変更を強いることになる。

データフォーマットも Modularity を意識すべき。Google の protobuf はシステム間のコミュニケーションを backward and forward compatible に行うためのフォーマット。

Release Simplicity

小さいリリースの方がインパクトも計測しやすい。小さい変更をどんどんリリースしよう。

A Simple Conclusion

Simple なモノは Reliable なモノである。

(SRE が) 機能追加に対して “No” と言うとき、それはイノベーションに反対しているわけではない。環境を Simple に保つことで、イノベーションを加速しているのだ。

ʅ(◞‿◟)ʃ

Chapter 10. Practical Alerting from Time-Series Data

(今回の輪読会の肝。さらっと嘘を書いている可能性あり。ご指摘お願いしたく。ただ、 Borgmon rules は真面目に読めたとしても、どうせ僕らが使えるものではないので、そこはゆるふわで)

Monitoring は Hierarchy of Production Needs の最も基礎的なレイヤーである。

大規模システムのモニタが難しい理由はいくつかある。

  • 単にモニタするコンポーネントが多すぎる。
  • 適切にモニタリングすることで、システムを担当するエンジニアの負荷を下げる必要がある。

Google 規模だと、ひとつのマシンが不具合を起こしているくらいでアラートが鳴ると noisy 過ぎる。

大規模システムは個々のコンポーネントを管理するより、データをまとめあげて異常値 (outlier) を刈りとるべきである。

The Rise of Borgmon

2003 年に Borg ができて、割とすぐに Borgmon というモニタリングシステムが作られた。似たようなソフトウェアには Prometheus などがある。

(ほとんど Borg と Prometheus は同じもののようなので、Prometheus の典型的なシステムを掲載する)

/varz エンドポイントを叩くとメトリクスが取れる。

% curl http://webserver:80/varz
http_requests 37
errors_total 12

Borgmon は他の Borgmon からデータを取得できる。普通はクラスタごとに Borgmon を立てて、グローバルに Borgmon のペアを置く。

(クラスタと一言で表現しても、そのサイズはさまざまなのでは…)

Instrumentation of Applications / アプリケーションの計測一巡り

/varz が plain text でスペース区切りのメトリクスを出力する (前述のやつ)。後の拡張で、ひとつの変数に対して複数のラベルをつけて出力できるようになった。例えば、 HTTP 200 が 25 件で、500 が 12 件の map-valued な値は次のような感じになる。

http_responses map:code 200:25 404:0 500:12

スキーマレスなので、新しいメトリクスを追加するのも簡単で便利 (それだけだと JSON とかでも同じでは?)。

Collection of Exported Data

Borgmon は監視対象の発見に Service Discovery のツールを使う (DNS [BNS] や Consul など)。

Borgmon は一定間隔ごとに /varz を叩いて、結果をメモリに貯める。結果はピアにも共有される。

Borgmon は監視対象ごとに “synthetic (作り物)” な情報も記録する。例えば名前解決が成功したか、いつデータを取得したか、などの情報。

/varz のアプローチは SNMP のような「極力ネットワークを使わないようにする」考え方と違う。HTTP のオーバーヘッドが問題になるケースはほとんどないし、そもそも Borgmon はメトリクスが取れないことそのものを signal として使うことができる。

Storage in the Time-Series Arena

Borgmon は (timestamp, value) の組みでデータを保存する。この形式を time-series と言う。各々の time-series はユニークにラベル (name=value の形式) 付される。

メモリの内容は定期的に TSDB (Time-Series Database) 形式でディスクに Sync される。少し遅くなるけど、Borgmon は TSDB に対してクエリを投げることができる。

Labels and Vectors

time-series の名前は labelset である。なぜなら、これは key=value のラベルのセットで表現されるから。TSDB でひとつの time-series をユニークに探すためには、次のラベルは必須である。

  • var: 変数名
  • job: モニタ対象のサーバー (アプリ) の種類
  • service: 雑に言うとジョブの集合を表すもの
  • zone: Borgmon が所属するデータセンタの名前

これらをまとめて、variable expression として表現できる。

{var=http_requests,job=webserver,instance=host0:80,service=web,zone=us-west}

こういう variable expressionlabelset がマッチする全ての time-series をベクタ形式で取得できる。たとえば、先ほどの expression から instance ラベルを外すと (クラスタ内に複数のインスタンスがあれば) 各々のインスタンスでの最新の http_requests の数がベクタで取得できる。

{var=http_requests,job=webserver,service=web,zone=us-west}

の結果:

{var=http_requests,job=webserver,instance=host0:80,service=web,zone=us-west} 10
{var=http_requests,job=webserver,instance=host1:80,service=web,zone=us-west} 9
{var=http_requests,job=webserver,instance=host2:80,service=web,zone=us-west} 11
{var=http_requests,job=webserver,instance=host3:80,service=web,zone=us-west} 0
{var=http_requests,job=webserver,instance=host4:80,service=web,zone=us-west} 10

期間を指定した time-series のクエリも可能である (variable expression と言ったり query と言ったりしているけど、これらは同じものを指しているのか?)。

{var=http_requests,job=webserver,service=web,zone=us-west}[10m]

ここの [10m] は “直近10分間” を表現する。もしデータを毎分で収集しているのであれば、次のような出力が得られるはず。

{var=http_requests,job=webserver,instance=host0:80, ...} 0 1 2 3 4 5 6 7 8 9 10
{var=http_requests,job=webserver,instance=host1:80, ...} 0 1 2 3 4 4 5 6 7 8 9
{var=http_requests,job=webserver,instance=host2:80, ...} 0 1 2 3 5 6 7 8 9 9 11
{var=http_requests,job=webserver,instance=host3:80, ...} 0 0 0 0 0 0 0 0 0 0 0
{var=http_requests,job=webserver,instance=host4:80, ...} 0 1 2 3 4 5 6 7 8 9 10

Rule Evaluation

Borgmon は単なる programable calculator である。Borgmon プログラミングは Borgmon rules で行う。これを使い代数表現で、ある time-series から別な time-series を作る。

Borgmon rules は可能な限り並列に評価される。後ろの評価が前の評価結果に依存する場合なんかは無理。評価結果として返るベクタのサイズなども実行時間を決める要素のひとつ。実行時間がかかるときは、いい CPU で Borgmon を動かせばいい。

(rateratio が英語版だと可換ではなくて、この書籍の文脈の rate は “時間” を表現しているようだ。でも Borgmon rulesrate() は比率を出してくれているような?)

(Borgmon にはかぎらないけど) モニタにはカウンターとゲージがある。カウンターは増加だけする。たとえば通算アクセス数など。計測インターバル中のデータがロスしないように、カウンターを使うのがオススメ。

次の Borgmon rules をどう評価するか…。

rules <<<
  # Compute the rate of requests for each task from the count of requests
  {var=task:http_requests:rate10m,job=webserver} =
    rate({var=http_requests,job=webserver}[10m]);

  # (前の評価結果に依存している)
  # Sum the rates to get the aggregate rate of queries for the cluster;
  # ‘without instance’ instructs Borgmon to remove the instance label
  # from the right hand side.
  {var=dc:http_requests:rate10m,job=webserver} =
    sum without instance({var=task:http_requests:rate10m,job=webserver})
>>>

このとき、 task:http_requests:rate10m は次のような感じ:

{var=task:http_requests:rate10m,job=webserver,instance=host0:80, ...} 1
{var=task:http_requests:rate10m,job=webserver,instance=host1:80, ...} 0.9
{var=task:http_requests:rate10m,job=webserver,instance=host2:80, ...} 1.1
{var=task:http_requests:rate10m,job=webserver,instance=host3:80, ...} 0
{var=task:http_requests:rate10m,job=webserver,instance=host4:80, ...} 1

dc:http_requests:rate10m はこんな感じ:

{var=dc:http_requests:rate10m,job=webserver,service=web,zone=us-west} 4

dc:http_requests:rate10m みたいな名前づけは Google の convention で、それぞれ “aggregation level”, “the variable name” そして “the operation that created that name” を表現する。

じゃあ、これはどう読む?

{var=task:http_responses:rate10m,job=webserver} はこんな感じの出力になる:

{var=task:http_responses:rate10m,job=webserver,code=200,instance=host0:80, ...} 1
{var=task:http_responses:rate10m,job=webserver,code=500,instance=host0:80, ...} 0
{var=task:http_responses:rate10m,job=webserver,code=200,instance=host1:80, ...} 0.5
{var=task:http_responses:rate10m,job=webserver,code=500,instance=host1:80, ...} 0.4
{var=task:http_responses:rate10m,job=webserver,code=200,instance=host2:80, ...} 1
{var=task:http_responses:rate10m,job=webserver,code=500,instance=host2:80, ...} 0.1
{var=task:http_responses:rate10m,job=webserver,code=200,instance=host3:80, ...} 0
{var=task:http_responses:rate10m,job=webserver,code=500,instance=host3:80, ...} 0
{var=task:http_responses:rate10m,job=webserver,code=200,instance=host4:80, ...} 0.9
{var=task:http_responses:rate10m,job=webserver,code=500,instance=host4:80, ...} 0.1

{var=dc:http_responses:rate10m,job=webserver} はこんな感じ:

{var=dc:http_responses:rate10m,job=webserver,code=200, ...} 3.4
{var=dc:http_responses:rate10m,job=webserver,code=500, ...} 0.6

そんで {var=dc:http_responses:rate10m,job=webserver,code=!/200/} はこんな感じ:

{var=dc:http_responses:rate10m,job=webserver,code=500, ...} 0.6

{var=dc:http_errors:rate10m,job=webserver} は…:

{var=dc:http_errors:rate10m,job=webserver, ...} 0.6

{var=dc:http_errors:ratio_rate10m,job=webserver} は:

{var=dc:http_errors:ratio_rate10m,job=webserver} 0.15

になる (そうなの?)。

Borgmon rules は新しい time-series を作るので on-call のときに読めるし、便利そうなら permanent にコンソールで表示することもできる。

Alerting

10分間のエラー率が1%以上で、1秒に1回以上のエラーが (2分以上) 出るときにアラートをあげる rule:

rules <<<
  {var=dc:http_errors:ratio_rate10m,job=webserver} > 0.01
    and by job, error
  {var=dc:http_errors:rate10m,job=webserver} > 1
    for 2m
    => ErrorRatioTooHigh
      details "webserver error ratio at %trigger_value%"
      labels { severity=page };
>>>

Borgmon は Alertmanager と接続している。これは Alert RPC を受け取ると alert notification を送信する。 Alertmanager で設定できる機能はこんな感じ:

  • 他のアラートが active なときには別なアラートを active にしない。
  • 同じラベルセットを持つ複数のアラートはひとつにまとめあげる。
  • 似たようなラベルセットのアラートが発火したときに fan-out したり fan-in したり。

Sharding the Monitoring Topology

Borgmon は別な Borgmon から time-series をインポートできる。Region ごとに Borgmon で監視対象の time-series を取得して、最終的に global な Borgmon が各 region の Borgmon から time-series を取得する。それが Google のジャスティス。

Black-Box Monitoring

Prometheus は White-Box Monitoring だけど、これだけだとユーザーにインパクトがあるメトリクスはとれない。Black-Box Monitoring も組み合わせましょう。

Prober が Black-Box のための機能で、監視対象に特定のプロトコル (たとえば HTTP) でリクエストを投げて結果が success かどうかを調べる。失敗したら Alertmanager に通知をしてアラートを投げる。(つまりは Runscope みたいな機能かな)

なお prober は Prometheus にもある概念。

Maintaining the Configuration

Borgmon では複数のモニタ対象に同じ Borgmon rules を適用できる。同じような設定をモニタ対象ごとに何度も書き直す必要はない。

Borgmon には language templates というマクロ的な機能もある (しかし具体的にどう機能するか分からず)。これを使い、似通った rule はライブラリ化できる。

人工的な time-series をつかい、rule をテストすることもできる。(これが必要になるということは、rule は複雑になりかねない…ということですよね)

Ten Years On…

Borgmon のおかげで、スケールする管理システムが実現できましたよ、的な話。 check-and-alert な仕組みだとスケールしないですよ、と。

サービス規模のスケールに対してモニタリングのコストのスケールは sublinear であるべきだよ、と。

Borgmon の話ばっかりしていたけど、Prometheus や Riemann、Heka や Bosun 辺りはコンセプトの似た OSS なので、そういうのを調べてみるのもいいかもね、と。

書評 – 理科系の作文技術 「第四章〜第十一章」

理科系の作文技術』の後半を読んだ。前半部分の感想もエントリにしている

後半を読みながら、「これは果たして自分が読むべき本なのだろうか?」と自問するようになった。例えば第五章には次のようなくだりがある。

論文は読者に向けて書くべきもので、著者の思いをみたすために書くものではない。序論は、読者を最短経路で本論に導き入れるようにスーッと書かなければならないのである。モヤモヤや逆茂木は禁物で、著者が迷い歩いた跡などは露いささかもおもてに出すべきでない。

たしかに論文とは、このように書かれるものだと思う。

しかし、自分が書きたい文章が「論文的」かというと、そうではない。僕が文章を書く場所はこのブログであったり、職場の技術共有の資料だったりだ。こういう文章では「自分がどう試行錯誤しつつ、現在の結論にいたったか」という情報も重要だ。

この辺から、自分が対象読者から外れているような気持ちになってしまい、残りの箇所は流し読む感じになってしまった。

それでも、いくつか学べる部分はあったので、メモを残しておく。

段落の作り方

適当に段落を作るのはよくない。一段落は一つのトピックを扱うべきだ。

段落は「トピックセンテンス」とそれを補強する文で構成するべきだと、本書では主張している。トピックセンテンスとは、その段落で主張したい内容を端的に表現する一文だ。多くの場合は段落の最初の一文がトピックセンテンスになる。もし『理科系の作文技術』に忠実なテキストを流し読みするのであれば、各段落の最初の文だけを読んでいけばよい。その行為は「トピックセンテンスを拾い読む」ことと近似だからだ。

書き手の立場としては、トピックセンテンスを中心に段落を組み立てていくことを意識することで、ぐっと読みやすい文章を作れるはずだ。

むずかしい言葉の回避

前半部分の感想に書いたことを再掲する。

できるだけ普通の用語、日常用語を使い、またなるべく短い文で文章を構成する

後半部分でも、表現を変えて同じことが書かれている。

面白いのは「テキストの白さを意識する」というくだりだ。「小難しい漢字ばかりで書かれたテキストでは、印字したときに黒々しくて読みづらい。ひらがなを交えた、適度な白さのテキストの方が読みやすい」というのだ。そういう視点で文章について考えたことはなかった。

まとめ

『理科系の作文技術』は1981年に出版された本にも関わらず、今でも人気の高い本だ。Amazon では今年も続々と新しくレビューが書かれており、今でも読者を獲得し続けていることがわかる。

しかし、理科系の「全ての文章」がこの本の主張の通りに書かれるべきかと聞かれると、首をすぐに縦に振ることは難しい。論文や技術マニュアルを書く立場にないのであれば、エッセンスを汲み取った上で内容を取捨選択をする必要があると思う。

書評 – 理科系の作文技術 「第一章〜第三章」

往年の名作である『理科系の作文技術』を読んでいる。文庫本サイズではあるものの、内容が濃いので少しずつまとめてブログにまとめていく。

本を手に取ったきっかけ

最近はまめに技術系のブログ記事を書くように心がけている。対象について分かりやすく書くことを通して自分の理解を深めることをモチベーションにしている。ここしばらくブログを書いてみて「そもそも、分かりやすい文章とは何なのだろう?」と考えるようになった。

『理科系の作文技術』は僕が大学生の頃に、教授から「論文を書く前に一読しておくように」と勧められた本だ。ずいぶん昔のことだ。しかし最近でも、ブログ界隈で『理科系の作文技術』を推す声がちらほら聞こえてくる。一過性の流行本ではなく、確たる地位を築いた本なのだろう。

僕も分かりやすい文章を追求するため、あらためてこの本を手に取った次第だ。

明快・簡潔な文章

明快な文章を書くための心得として次の三つが挙げられている。

  1. 一文を書くたびに、その表現が一義的に読めるかどうかを吟味する
  2. はっきりと言えることはスパリと言い切り、ぼかした表現を避ける
  3. できるだけ普通の用語、日常用語を使い、またなるべく短い文で文章を構成する

例として「〜ではないいかと思われる」という表現は避けるように書かれている。僕も気をつけなくては…。

なまの情報

本書の第二章から一部分を引用する。

ひろい題目のなかで自分で主題をえらべる場合には、できるかぎり、自分自身が直接にことに当たりものに当たって得た情報 – なまの情報 -、またそれについての自分自身の考えに重点をおくべきである。これらは、たとえ不備であり未熟・浅薄であったとしても、オリジナリティーという無比の強みをもっている。

「なまの情報」は確かに強い。僕は今は『世界で闘うプログラミング力を鍛える150問』を解きながら解答例をブログ記事にまとめているけれど、解答は自分自身が解いたリアルなものだ。僕が作るものより、いい解答もあるかもしれない。しかし、自身で考えて書いたものでない限り、深い考察は難しいだろう。少なくとも思考の過程を文章に落とし込むのは無理だ。

何かを書いて公開するときは、内容に「なまの情報」が含まれているかを意識的に確認するようにしよう。

根性 (のようなもの)

凡人としての心得も書かれている。

天才は別として常人にとっては、スッキリと筋の通ったものを書けるかどうかは、自分の書いたものをきびしく見直す能力と、何度でも書き直す根気とにかかっているのではあるまいか。

精進します…。

書評 – アライアンス – 人と企業が信頼で結ばれる新しい雇用

アライアンス – 人と企業が信頼で結ばれる新しい雇用』を読んだ。

雇用者と被雇用者の新しい関係性について書かれた本だ。雇用の流動性が高まっている現代においては、旧来とは異なるマインドセットで「雇用」というものを考える必要があるだろう。著者陣の一人は LinkedIn の創業者だ。さすがに雇用についての洞察には深いものがある。

本書では、人材は流動するというドライな価値観のもとで、個人と企業がどう付き合うべきかについて述べられている。退職を「卒業」と言い換え、卒業生のネットワークを作ることの有用性について語られている。企業は卒業生を通してブランドを作れるし、個人は卒業生のコネクションで次の雇用主を探せる、というわけだ。

僕の友人には、勤務先に対して愚痴っぽくなっている人が少なくない。僕は「そんなに勤務先に文句があるなら、さっと転職すればいいのに」と思ってしまう。モチベーションの上がらない状態で働いても幸せではないし、上司の受けもよくないだろう。これでは企業との間にアライアンスを作れない。

カジュアルに雇用主が変わる環境で、末永い「縁」を築いていくためのヒントを得られる本であった。

書評 – グーグル、アップル、マイクロソフトに就職する方法

グーグル、アップル、マイクロソフトに就職する方法』を読んだ。

この本の著者が『世界で闘うプログラミング力を鍛える150問』と同じであると知り、興味が湧いた次第だ。

いくつか学びがあったので、ここでメモを残す。

よい履歴書の書き方

履歴書にかっちりしたフォーマットは存在しない。「x年y月にz大学を卒業しました…」のような退屈な記述ではなく、もっと自分をアピールできる内容を書く方がいい (「z大学」がよほどネームバリューの高いものでない限り)。

独特な履歴書を会社説明会に持ち込んだ、アレックスという大学生のエピソードが面白かった。

説明会で何百人もの応募者と話した後は、リクルーターの側は誰が誰だか混乱してしまう。だがこれまでに一人だけ、際立っていた応募者がいた。アレックスという大学二年生である。彼はこれまでに書いたプログラムのポートフォリオを持参し、その中からとくに重要な四つについてスクリーンショットを用意していた。おかげで会話は大いに盛り上がったものである。

確かにビジュアライズされた形で仕事が見える方が、ぐっと理解が早くなるだろう。

また、数字を用いて実績をアピールすることの重要さにも触れられている。本書で取り上げられている、履歴書の悪い記述とよい記述の例を引用しよう。

(悪い例)
クラッシュレポート機能を実行し、クラッシュの主な原因三つを解決した。

(改善例)
クラッシュレポート機能を実行し、クラッシュの主な原因三つを解決して、顧客からの苦情件数を45%減らすことに成功した。

改善例の方であれば、仕事のインパクトを客観的に伝えることができる。

想定質問表

面接で過去のプロジェクトについて聞かれたときのため、想定質問とプロジェクトをマトリックスにした「想定質問表」があるといい。具体的な例を引用しよう。

広告エンジン 暗号化
最も困難だったのは? 時間とコストのバランスをうまくとること システムの最下層を入れ替えること
学んだことは? あまり欲張りすぎた設計は非現実的である エンジニアの目標はマーケティングの目標と衝突しがちである
プロジェクトの波及効果は? 経営陣にプロジェクトの重要性を再認識させた 予算配分の決定方式を変えさせた
意見対立はあったか? 現状維持派と対立した 自称専門家を説得するのが面倒だった
失敗はあったか? 事前に十分な根回しをしておかなかった 従属要因の分析が不十分だった

こういう表を作ることで、他人に対して仕事の説明をしやすくなるだけでなく、自分自身の仕事について振り替えることもできるだろう。

まとめ

いくつか学びもあったものの、自信を持って他人に勧められる本ではないと感じた。情報が北米向けなところが難点だ。「電話面接」や「州ごとの所得税率の差」などは、日本人には縁遠いだろう。面接の質問内容のサンプルが載っているのは魅力だが、これに関しては同じ著者の『世界で闘うプログラミング力を鍛える150問』の方が詳しく書かれている。

もし北米で働いてみたいのであれば、この本の内容は参考になるだろう。そうでない場合は、エッセンスを抽出して噛み砕き、ディテールは取捨選択する必要がある。

書評 – Soft Skills

前の記事でも少し触れた『Soft Skills』という本を紹介しよう。

この本はソフトウェア開発者向けの本としてはかなり異色で、ソフトウェア開発そのものについてのトピックはほとんど存在しない。代わりに、ソフトウェア開発者が人生をよりよく過ごすためにする べき ことについて書かれている。

例えば、次のような章がある。

  • 第11章: 自由を得る: 仕事の辞め方
  • 第17章: ダメな履歴書をよくする方法
  • 第21章: 大成功するブログの作り方
  • 第27章: 学び方を学ぶ: 独学の方法
  • 第34章: 学位は必要か、なしで済ませられるか
  • 第38章: ポモドーロテクニック
  • 第50章: 給与交渉の方法
  • 第52章: 不動産投資の基礎
  • 第60章: 筋肉のつけ方: オタクでも膨らむ上腕二頭筋
  • 第64章: フィットネスのためのオタク向けテクニカル製品
  • 第68章: 恋愛と人間関係: コンピューターはあなたの手を握れない

実に多岐にわたる内容だ。

読書メモ

読みながら、いくつか付箋を貼った。このブログにもメモを残しつつ転記しよう。

「第4章 社交術: 考えている以上のものが必要だ」

  • 決して批判しない
  • なんとしても議論を避ける

他人の行動を改めたいのであれば、間違った行動を批判するのではなく、その人が正しく行動したときに褒めるべきだ。なぜなら『人を動かす)』にもある通り「どんな人間も、自分が尊重されていると感じたい」からだ。自分を尊重してくれる人間に耳を傾けないでいることは難しい。まずは相手を尊重することからはじめよう。

また、正しい論理展開で議論を進めれば、かならず合意にたどりつけるという幻想も捨てるべきだ。人間は感情を持つ生き物だ。例えば、自分の今までの努力を誰かに理路整然と否定されたとしよう。それを「ありがたい」と思える人は少ない。僕自身も、たぶん「ありがたい」より先に「むかつく」という気持ちを先に持ちそうだ。

避けても大事にいたらない議論であれば、避ける方が賢明だ。

「第24章 講演、プレゼンテーション、講師: しゃべるギーク」

この章には背中を押された気分だ。少し引用する。

(人前で話すのが怖い?) 全然問題ない。多くの人がそうだ。…。私たち人間は非常に適応性が高いということを覚えておこう。…。人前で話し続けていれば、時間とともに適応して恐怖は消える。

今までのキャリアでは、社内でプレゼンテーションをすることはあれど、社外で話すことは多くはなかった。

せっかくカナダから日本に戻り、日本語でプレゼンテーションできる機会が増えたのだから、積極的に話していこうと思う。少し前にも Kubernetes Meetup で LT をさせてもらったけど、この手のチャンスには手を上げて行こうと思う。経験を重ねて恐怖をつぶしていきたい。

「第26章 バカにされるのを恐れるな」

タイトルが全てかもしれない。

最初は誰もが素人だ。素人は玄人から見てもらうことで成長する。あなたが素人であるならば、素人として見られることは得なことじゃないか。

バカにされることを恐れてチャンスを逃すなんて、それこそバカみたいだ。

「第50章 給与交渉の方法」

僕がカナダで働いていた時代に知りたかった話だ。どちらかというと北米文化を前提としている。

『Soft Skills』の主張は次の通りだ。

まず、面接官に「希望給与は?」と聞かれても答えない方がよい。募集ポジションに割り当てられている予算より下を言ってしまう可能性があるからだ。先に会社の予算を話してもらうようにするべきだ。

また、現在の給与を聞かれても返答するべきではない。理由は…自明だろう。現在の給与を聞かれたときのテンプレートも紹介されている。

「申し訳ありませんが、今いただいている額は申し上げたくありません。御社がこのポストに考えられている額よりも高い場合、私としてはこのポストなら給与が下がってもいいと思っていますので、選考対象から外されてしまうのは不本意なことです。そして、御社が考えられている額よりも低い場合、自分を安く売りたくはありません」

この回答は十分に受け入れられると思う。自身の面接官としての経験を踏まえても、こう言われて気分を害することはないだろう。

ここから先は日本では難しいかもしれないが、給与を提示された後での話だ。カナダでは (そしてアメリカでは)、提示額をそのまま受け入れることは稀らしい。「らしい」というのは僕自身がカナダで仕事を手に入れたときには知らなかったからだ。プロトコルとしては、「1. 企業から給与を提示される」「2. 自分の希望給与はさらに高いという」「3. 企業から調整された条件を再提示される」というのが一般的のようだ。場合によっては「2. ~ 3.」を繰り返すこともあるようだ。

一般に、日本よりも北米の方が採用にかけるコストが高い。誰もが知っている技術系企業の北米ブランチに転職した友人は、採用試験のために数日間ずーっと缶詰にされたと言っていた。そのくらい厳しくチェックして採用のオファーを出しているのだから、多少きびしく条件を詰められたくらいで採用通知を撤回するわけには行かないのだろう。

良し悪しはあるだろうけど、僕は北米の採用カルチャーの方に好感を持つ。

「第70章 失敗に正面からぶつかれ」

これもタイトルが全てかもしれない。

失敗を喜び、期待し、受け入れ、正面からぶつかることを学ぼう。…。単に失敗に対する恐怖を取り除いただけでは不十分であり、さらに失敗を求めるようにすべきだ。成長したければ、確実に失敗することが保証されているような状況に身を置く必要がある。

よいアドバイスだと思う。

僕にとっては4年間のカナダでの生活が「失敗が保証された状況」だったように思う。見知らぬ土地で不慣れな言語で仕事をするのだから、当然だろう。カルチャーも違う。ただ、楽観的な気持ちで「死にはしないだろう」というつもりで海を渡った。結果として、失敗もやはり多かったが、得られた経験は大きい。今の職場で働けているのも、ここでの経験があるからこそだ。

総括

『Soft Skills』は「ごった煮」である。僕が好きだと思う章と、他の人が気に入る章は違うだろう。そして、僕自身も全ての章の内容に同意しているわけではない。それでも、何かソフトウェア技術者としての生活に不満があり、改善したいと思っているのであれば、何かのヒントは見つかる本だと思う。