その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

tcpdumpで今すぐキャプチャ内容を全部ファイルにflushしてくれ!! というとき

tcpdumpはキャプチャした内容をバッファに蓄積してバッチ的にflushするので,うっかりプロセスを即断するとキャプチャしていた内容をdrainできないことがままあります.困りますね.

www.tcpdump.org

Using the SIGUSR2 signal along with the -w flag will forcibly flush the packet buffer into the output file.

というわけでここに書いてあるとおり,SIGUSR2tcpdumpのプロセスに対して発行してあげるとその時点のバッファの内容をファイルに吐き出します (ただし -w フラグによって出力先ファイルを指定している,すなわちファイル書き出しモードにしている必要がある).

If the -w option is specified, make the saved raw packet output ``packet-buffered''; i.e., as each packet is saved, it will be written to the output file, rather than being written only when the output buffer fills.

あるいは -U (--packet-buffered) フラグを -w フラグとともに利用するというのも良いでしょう.ただ,この際はバッファをバイパスして直接ファイルに書き込むような挙動となるので,tcpdumpdaemonとして常に動かしているような環境ではパフォーマンスの問題が発生する可能性があるので注意が必要そうです.


なお,-B オプションを使ってバッファサイズを0にするとどうなのか……というとこれは

tcpdump: invalid packet buffer size 0

と出て動きません.なるほど〜,そりゃそうですね.

TypeScriptを使ってアプリケーションの設定ファイルを書く

アプリケーションの設定ファイルと言えばjsonyamlなんかで書くことが多いと思うんですが,最近はTypeScriptで設定ファイルを書いております.このような感じ:

export interface ApplicationConfig {
  readonly listenPort: number;
  readonly timeout: number;
  readonly apiBaseUrl: string;
  readonly loggerOptions: LoggerOptions;
}

こういう感じで設定のinterfaceを定義しておいて

export class ProductionConfig implements ApplicationConfig {
  readonly listenPort = 5963;
  readonly timeout = 5000;
  readonly apiBaseUrl = "http://internal-api.example.com";
  readonly loggerOptions = {
    level: 'info',
  };
}

// TypeScript (JavaScript) 内で使うなら以下のようにしておくと便利そう (例A)
const productionConfig = new ProductionConfig();
export { productionConfig };

というふうに実際の設定を記述するという感じです.


メリットとしては,

  • 「何が設定項目なのか」が定義により明らかになる
  • 設定漏れが基本的に無い
    • これはinterfaceのサポートによるもの
  • 型のサポートがある
    • 設定の構造化ができる
  • 継承が使える
    • というかTypeScriptの言語機能を使ったコードが書ける

といったあたりが挙げられると思います.

interfaceの定義によって,「どういった項目が設定されるべきなのか」が「型情報も含めて」明示できるというのは利点の一つと言って良いと思います.

また,ウェブアプリケーションだと例えば本番環境用の設定,ステージング環境用の設定,開発環境用の設定……というふうに複数の設定が存在していることがままあると思うのですが,それぞれの環境のためにinterfaceを満足した設定を記述 (実装) してあげるだけで,漏れが無い (つまりそれぞれが等価な振る舞いをする) 設定を安全に用意することができるようになります.
当然のことながら,設定の実装クラスがinterfaceを実装しきらなかった場合はコンパイルエラーが出るので堅牢です.

型のサポートが得られるというのは言うまでもないですね.安全! 型定義の無い設定を取り扱う際に苦労することがままありましたからね……
あと自分で定義したclassについてもpropertyの型にすることができるので便利です.

個人的には継承が使えるというのは便利ポイントの一つだと思っていまして,例えば「基本的な動作はまったく同じなんだけど一部の機能だけ有効 (あるいは無効) にしたい」みたいなことがあったときに継承は便利に使えるかな〜と思っています.例えばこのような感じ:

export interface ApplicationConfig {
  readonly port: number;
  readonly tcpdumpEnabled: boolean;
}

export class ProductionConfig implements ApplicationConfig {
  readonly port = 5963;
  constructor(readonly tcpdumpEnabled: boolean = false) {
  }
}

export class ProductionInspectorConfig extends ProductionConfig{
  constructor() {
    super(true);
  }
}

このようにしておくと,基本的には ProductionConfig と基本的には同じ設定でありながらも, tcpdumpEnabledtrue にした ProductionInspectorConfig をお手軽に作れることとなります.
もちろん通常のソフトウェアにおける継承と同じで,is-aとして取り扱える (に等しい) 状況以外で使うと良くないことが起きると思うしそもそも継承を使うと複雑さが容易に増すので,そこは用法用量を守って……という感じでしょう.というかこれはTypeScriptの言語機能を設定記述に使えるという話に過ぎませんね.


あとはTypeScript (JavaScript) 内で使うのならば個別の設定のインスタンスをexportしてしまうか (上記の例A),あるいはinterface側に例えば以下のようなコードを書いて,それによって得られたインスタンスをexportしてしまえば便利そうです:

export interface ApplicationConfig {
  ...
}

const appConfig: ApplicationConfig = (() => {
  const nodeEnv = process.env.NODE_ENV;
  switch (nodeEnv) {
    case 'local':
      return new LocalConfig();
    case 'staging':
      return new StagingConfig();
    case 'production':
      return new ProductionConfig();
    default:
      throw new Error('invalid NODE_ENV: ' + nodeEnv);
  }
})();
export { appConfig };

また,TypeScript以外でこの設定を利用したい,つまり単なる設定記述言語としてこれを利用したい場合は生成した設定インスタンスJSON.stringify()に食わせてしまえば他の処理系でも取り扱えるJSON形式の設定が得られるのでそのようなスクリプトを書くとポータブル.良いですね.良かった良かった.


と,このような感じでした.TypeScriptは書き味が比較的ライトなので,設定ファイル用途にも書きやすくて良いですね.

systemdで管理しているserviceがopenできるfile descriptorの上限数を増やす

systemdのバージョン

# systemd --version
systemd 237

TL;DR

ServiceLimitNOFILEを設定すると良い.他の特定のリソースについての制限もどうようにかけることが可能です.

ドキュメントは man systemd.exec で参照できます (ウェブ上はこれが正しいリソース? http://man7.org/linux/man-pages/man5/systemd.exec.5.html).

挙動の検証

現状確認

テストのためにfileを開きまくるperlのscript.ついでにprocのlimit (/proc/{PID}/limitsの中身) も出力する:

#!/usr/bin/env perl

use strict;
use warnings;
use feature qw/say/;

open my $fh, '<', "/proc/$$/limits";
say do { local $/; <$fh> };

my @fhs;
for (my $i = 0; $i <= 2000; $i++) {
    my $filename = "/tmp/test.${i}";
    open my $fh, ">", $filename or die "$filename: $!";
    push @fhs, $fh;
}

特に何も考えずにsystemdのservice fileを書いてみます.

[Unit]
Description=test

[Service]
Type=oneshot
WorkingDirectory=/tmp/test

ExecStart=/usr/bin/perl run.pl

[Install]
WantedBy=multi-user.target

で,実行してみます

Starting test...
Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max data size             unlimited            unlimited            bytes
Max stack size            8388608              unlimited            bytes
Max core file size        0                    unlimited            bytes
Max resident set          unlimited            unlimited            bytes
Max processes             3841                 3841                 processes
Max open files            1024                 1048576              files
Max locked memory         16777216             16777216             bytes
Max address space         unlimited            unlimited            bytes
Max file locks            unlimited            unlimited            locks
Max pending signals       3841                 3841                 signals
Max msgqueue size         819200               819200               bytes
Max nice priority         0                    0
Max realtime priority     0                    0
Max realtime timeout      unlimited            unlimited            us

/tmp/test.1020: Too many open files at run.pl line 13, <$fh> line 1.
test.service: Main process exited, code=exited, status=24/n/a
test.service: Failed with result 'exit-code'.
Failed to start test.

Too many open filesで死にます.Max open filesは1024のようですね.ulimit -nで表示される現状のデフォの値っぽい.

LimitNOFILEを設定してみる
[Unit]
Description=test

[Service]
Type=oneshot
WorkingDirectory=/tmp/test

ExecStart=/usr/bin/perl run.pl

LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

LimitNOFILE=65536をserviceの設定に追加してみます.それでscriptを実行:

Starting test...
Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max data size             unlimited            unlimited            bytes
Max stack size            8388608              unlimited            bytes
Max core file size        0                    unlimited            bytes
Max resident set          unlimited            unlimited            bytes
Max processes             3841                 3841                 processes
Max open files            65536                65536                files
Max locked memory         16777216             16777216             bytes
Max address space         unlimited            unlimited            bytes
Max file locks            unlimited            unlimited            locks
Max pending signals       3841                 3841                 signals
Max msgqueue size         819200               819200               bytes
Max nice priority         0                    0
Max realtime priority     0                    0
Max realtime timeout      unlimited            unlimited            us

Max open filesが65536になってますね.おかげで実行にも失敗しない! 良かった良かった.

Starting test...
Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max data size             unlimited            unlimited            bytes
Max stack size            8388608              unlimited            bytes
Max core file size        0                    unlimited            bytes
Max resident set          unlimited            unlimited            bytes
Max processes             3841                 3841                 processes
Max open files            10                   10                   files
Max locked memory         16777216             16777216             bytes
Max address space         unlimited            unlimited            bytes
Max file locks            unlimited            unlimited            locks
Max pending signals       3841                 3841                 signals
Max msgqueue size         819200               819200               bytes
Max nice priority         0                    0
Max realtime priority     0                    0
Max realtime timeout      unlimited            unlimited            us
/tmp/test.6: Too many open files at run.pl line 13, <$fh> line 1.
test.service: Main process exited, code=exited, status=24/n/a
test.service: Failed with result 'exit-code'.
Failed to start test.

試しにLimitNOFILE=10にしてみた様子.逆方向にもちゃんと絞れていることがわかります.

proxy-protocol-jsリリースした & Kyoto.js #16で喋ってきました

表題の通りproxy-protocol-jsをリリースしました.

github.com

www.npmjs.com

Kyoto.js #16の発表資料は以下です.

スライドでも簡単に説明しましたが,PROXY protocolというのはHAProxyが提唱しているTransport層 (L4) においてoriginalの送信元を維持したままproxyするためのprotocolになります.HTTPにおけるX-Forwarded-ForのL4版という感じですね.詳しくはこちら: http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt


で,このPROXY protocolをnode.jsで触りたくなったのでこの度ライブラリをこさえた次第です.主なフィーチャーとしては

という感じになっています.nodeでPROXY protocolを取り扱う人はそう多くはない気はしているのですが,もし来るべき時が来た際にはどうぞご利用ください.

「テクノロジーのイノベーションと民主化、そのはじめの一歩」というタイトルで首都大学東京で話してきました

首都大学東京は私の母校でして,当時は本当に色々なことがあったのですが,今回たまたまお話しする機会を頂いたので表題のような話をしてきました.

www.slideshare.net

このタイトルを思いついた時は酔っ払っていたのでしょうね.かなり大上段に構えたタイトルになってしまい恐縮しているのですが,

・情報通信コース・学域を卒業して,会社に入ったけど,会社ってこんなところですよ,
・会社でこんなことやってますよ.
・うちの会社にはこんな特徴がありますよ.
・会社に入るとこんなことしますよ.
・入社して,困ったことはこんなことです.
・入社して良かったことはこんなことです.
・今思うと,学生時代にこんなことやっていたらよかったかな,って思います.
・会社に入ってからの私の年表
・会社はICTイノベーションとしてこんな取り組みをしていますよ,してきましたよ.私はそれにこんな形で携わりましたよ.

というような内容をオーダーされたので,ここは一丁エモい内容でいこうと思い,打ったのがこの発表となります.趣旨としては,テクノロジーイノベーションを起こしたい,それを起こすためにはーーというのを自分なりにテクニカルな観点から発表したものになります.
大学に所属する学部生の方々のために作ったスライドなので,もしかしたらそれ以外の人にはもしかしたら刺さらないかもわかりません *1.なお中盤のスライドにある英語に対する思いは心から思っていることです.本当に苦戦を強いられている……
普段はエモい発表あんましないので (というか苦手),発表のフィードバックがどうだったのかは気になるところですが…… (結構心配)


このスライドに書かれている内容については,LINEで働いていたときに僕に本当によくしてくれた@yappoさんと@tokuhirom さんのお陰で学ばせてもらったことが本当に,本当に多くあります.なかなかこれを改まって言う機会はないのですが,平成も終わることですし,この場を借りて感謝の意を表したいと思います.ありがとうございます!!


僕は「とにかくコードを書き続けることは良いことだと思っていて,コードを書かなければ終わる,どんどん書く,書いた量が質に転換するタイミングは必ずあるのだ」という思いを持っているのでこのような内容となっています.
オリジナルのスライドの最後には "Shut the fxxk out and write some code" という一言が書かれたスライドがあり,つまりこれは「グダグタ言ってねーでコードを書けよハゲ」という意味合いでして,僕を動かす原動力の一つであった言葉なのであります.これはものすごく好きな言葉なのですが,しかし自らの良識が働いた結果,公開版のスライドからは削除しました.ということを記します.


結びとして,人の感想を記したいと思います.

f:id:moznion:20190426235321p:plain

ハハハ,ありがとうございました.

*1:学生の人に必ずしも刺さると言っているわけではない

恐怖! パスワード無しでログインを受け付けるsshd Dockerコンテナを作る

表題の通りです.
ほら,ローカルでのテスト用途でそういうコンテナが欲しくなることが月2くらいであるじゃないですか……あるんですよ……本チャンの環境で使うともちろん即死なので使ってはいけません.

gist.github.com

このようにすると test ユーザーでパスワード無しでsshログインできるコンテナを作ることができます.便利ですね.
sedで気合を入れまくっている部分を,あらかじめ書き換えておいた sshd_config との COPY に置き替えても良いでしょう.

現場からは以上です.

outage reportを書くときに気をつけていること

そうは言っても障害は起きるものです.で,障害が起きて,終息したあとの振り返りとして社内向けにoutage report (障害報告書的な?) のようなものを書くと思うのですが,本記事ではそのときに気をつけていることについて書きたいと思います。

outage reportの目的

そもそもですが,outage reportを書く目的としては以下のような物があるのかなと思っています。

  • A: 障害が起きたという事実に関する周知
    • 障害についてお客様からお問い合わせが来たりした時に正しい情報を届けられるようにするため
  • B: 根本原因の洗い出し
    • 再発防止のため
  • C: 障害検知フローの確認
    • 障害に対する初動までにかかる時間を短くするため
  • D: トポロジの形成
    • 知見の醸成
    • 似たような問題が起きたときに,outage reportに書いてある対処法を逆引き的に利用できるようにするため

outage reportに書いている内容

以下は個人的に (というか所属している組織的に) 書いている内容なので千差万別だとは思いますが,このような感じのことを書いていますというご紹介です.カッコ内のアルファベットは目的の部分に書いたアルファベットと対応しています (Dについては横断的に有効だと思うので省略しています).

障害のサマリ (A)
  • なるべく簡潔に書くように心がけている.多くても3行以内で済むように.
  • 外部向けに簡潔に障害内容を伝えられるような内容を心がける
  • このサマリだけを読む非技術系のスタッフもいる
障害で発生した症状・お客様から見た問題の詳細 (A)
  • 外から見た時の症状について書くようにしている
    • 内部のシステム構成などは関係なく,お客さんが実際に対面した (であろう) 問題について書く
  • 詳細に書くようにする
機能単位の影響範囲 (A)
  • 実際にどの機能が利用不可能になったか,について列挙するようにしている
コンポーネント単位の影響範囲 (B)
  • これは内部のシステム構成について書く
  • 実際にどのコンポーネントが利用不能状態になったか,について列挙するようにしている
    • microservicesのようになっているとここらへんは割と書きやすい気がする
障害期間 (A, C)
  • 以下の4つについて明記する
    • 発生日時
      • 問題自体が発生した日時
    • 検知日時
      • 問題を検知した (初動可能になった) 日時
    • お客様影響があった期間
      • お客様が当該機能を利用できなかった期間 (from/to)
    • 終息日時
      • 問題の終息が正しく確認された日時
  • 文字だけだとわかりにくいので,「時系列で追いやすい図」や「グラフ」があると良いと思っている (余裕があれば)
検知フロー (C)
  • 以下の3つについて書いている
    • いつ検知したのか
    • 何によって検知したのか
    • 誰が (人・物にかかわらず) 検知したのか
  • これによって,検知して初動対応可能になるまでにかかった時間がわかる
  • 検知までの時間が長くかかってしまっている場合は,アラートが見落とされているとか,あるいはアラートの仕組みが不足しているとか,なんらか非効率な方法になっているとか,そういった問題を浮き彫りにすることができる
対応タイムライン (B)
  • 対応した内容を時系列順に記す
  • 「いつ・誰が・何を」という情報が重要
  • 「効いた・効かなかった」という情報についても記すべき
原因と解決 (B)
  • 以下の3つについて書いている
    • 根本的な原因
      • 例えば「特定のコンポーネントhogeがxxxという状況下でyyyするとエラーを返してしまうため」とか
      • ここがoutage reportのコアだと思うのでガチっと書くように心がけている
    • 有効だった対応・復旧作業
    • 再発防止策
      • 根本的な原因と有効だった作業を振り返って,再発させないための今後のアクションについて書く
      • あるいはもう適用済みのアクションについて書く
その他反省
  • 気づいたことをつらつらと書いてサービス改善につなげる
  • 例えば「初動までの時間が長く,alertを上手く検知できていないので改善すべき」みたいな (これは例なのでざっくりしてますが……)

こんなところでしょうか.気づいたら追記するかもわかりません.

outage reportを書くという気持ちを高める

outage reportを書く時ってあまり気持ちがアガるものではないし,障害明けで疲れてたりピリピリしていたり,えてして「なんで俺が書かなきゃならんのだ……」みたいな罰当番的な感じにもなりがちだと思うんですが,実際はそうじゃないですよという雰囲気と仕組みを作ることが重要な気がしています.

例えばoutage reportは関連する人が複数人で書くようにするだとか,あるいはoutage reportを書いた人が評価されるというようにしていくのが良いのではないかと思っている次第です.非常に重要なドキュメントだと思うので.
所属している組織ではscrapboxを使っているので自然と複数人でoutage reportを書く感じになっており,ここらへんは上手くワークしているという感じがしているところです.

今後の課題

これは気をつけている事というよりも抱えている課題なんですが,

  • 対応タイムラインを自動的に収集して,あとはコピペするだけにしたい
  • outage reportを効果的に社内に周知したい

というのがあって,前者はchat opsに全振りしているような組織だと簡単にできそうで良いな〜と思っているところです (ただそれだけの理由でchat opsに全振りする理由にはならないな……という感じでもある).なんらかのソリューションが欲しい……
またoutage reportを効果的に社内に通知する方法についてもいい方法を探していて,今の所朝会的なところでoutage reportへのポインタを示すくらいしかできていない状況で,もうちょっとなんかいい方法はないかと探っているという段階です.

なんらか良い方法をご存知の方がいたら教えてもらえると嬉しいです.あと他のみなさんがどういう感じでoutage reportを書いているのかも気になる.