試行錯誤のおと

日々の試行錯誤した結果です。失敗することが多い記録、それだけでっす!

Hatena Engineer Seminar #6 〜インフラ編〜 @ Tokyo に参加してきました

(最初文で書いてたけど、読みづらかったのでメモに置き換えた〜)

個人的には勉強会に参加したらやっぱり、記事を書くべきだと思ってる。 参加したくても様々な事情で参加できなかった人のためになるし、感想を書くと発表された側、発表を聞いた側のモチベーションにもなると考えてる!

内容と感想

hatena.connpass.com

LT も面白かったけど、メモなしで聞く方を重視したので記事で触れるのは通常のトーク 3 つ。

id: wtatsuru さん: はてなのログ運用のこれまでとこれから

  • 前提となるシステム構成
    Reverse Proxy -> Application Server -> DB
  • ログの種類
    アクセスログ、エラーログ: Reverse Proxy が書き出し
    アプリケーションログ: Application Server が書き出し
    ほとんどのサービスで同じような構成をとっているのでログの処理も同じ
    • アクセスログの運用・分析
      フォーマットは LTSV
      ログの保存は rsyslog でリモートに転送
      ローカルから s3 にも保存して二重化していて、しばらく日が経過したら glacier に保存している
      SSD 上のサーバにも転送して、 SSD 上のサーバで分析、レスポンスタイム、レイテンシを計算
      発展して EMR 上で分析、 EMR はクラスタの初期化が遅いので今は google big query を検証中(embulk を挟む)
    • アプリケーションログの運用
      構造化ログ(json) の出力、すべてのサービスで特定の場所に出力
      fluentd で中央の aggregator に出力、集約の精度はベストエフォート、完全に保証しない
      他に s3 に保存、mackerel に転送、 elasticache に転送
  • 今後
    syslog からの脱却、開発者以外も活用できる仕組み、行動ログ基盤

rsyslog を廃止する課題を挙げられていたのだけれど、弊社では journald を使ったログ転送の仕組みを試行錯誤していて、この課題の解決には journald のログ転送あるいは自作のログ転送ツールを利用されたりするのかなあと思った。 また、行動ログを取りたいという話をされていて、懇親会で話していると行動ログを取るんだったら時系列 DB を利用しないといけないねという話になった。 時系列 DB は http://druid.io/ というものがでてきていて、いい感じ(?)らしい。

id: hagihala さん: はてなのサーバプロビジョニングの話(仮)

  • プロビジョニング
    chef, AMI の運用について
    今回の話は Configuration, Bootstrapping、 Orchestration の話はない
    サーバ環境は最近専用サーバ(さくらさん)、他に DC, Amazon VPC
    物理サーバのプロビジョニングは物理層はさくらさん、PXEブート、 Xen domU 上の仮想サーバは EC2 と同じ扱い
    ツールは chef、 chef 昔、本よんだくらいで全然わからないのでなかなかついていけない
    はてな内部でも動きが抽象的だったりして chef の評判はよくないらしい
    問題はたくさんある、テストが回せていない、コードを外部に出せていない
    AMI は debian のものを元にすべての元になる AMI を作成している、使っている技術は open-vz, packer

あと Chef を使っていて、問題はたくさんあってテストが回せていない、コードを外部に使えないところなど、プロビジョニングツールあるあるみたいな知見が共有されていてよかった。 懇親会で若手インフラのメンバと話していたのだけれど、プロビジョニングツールを使ってセットアップの設定を書いていると、テストがそれと被るようになってテストを書くモチベーションが下がるというものあるあるらしい。

プロビジョニングするには何か理由があってされているはずなので、プロビジョニングを意識するに至った理由やツールを選択された理由があれば聞きたかった。 最近、実戦的に Ansible を使って一部プロビジョニングをしているのだけれど、仕事で Ansible を導入したのには下記に書くような理由があって、 Chef を導入された理由があるようなら知りたかったなあと思うなど。

  • Ansible を採用した理由
    • 動作に必要な Python はどのディストリにも大体システム標準で入っている
    • Python で実装されているのでいざとなれば読めばいいし読みやすい
    • べき等性が保証されたコードを書くことができる

id: hagihala さん: MySQL運用とらぶるすとーり〜3

  • テーブル行数制限のはなし
    The table 'relword' is full のエラーログ
    不要なデータを削除して延命
    DB は Mysql-4.0.25(?) (Senna, MyISAM)
    --with-big-tables が configure に見当たらない
    テーブルの最大行数制限に引っかかる
    MySQL 4.0 -> 5.1 へのアップデート
    はまりどころ共有(Timestamp とか)
    4.0 -> 5.0 -> 5.1 のレプリケーション
    参照系のみ 5.1 をみるようにして互換を参照
  • binlog 破損から救いを求めた話
    Master の Disk full
    古い binlog を削除するとレプリケーションエラーが発生
    mysqlbinlog コマンドで binlog を確認しようとすると binlog が開けない
    各 slave の binlog のポジションが同じ
    master の binlog が壊れて、壊れた binlog を読もうとして失敗していた
    binlog のバイナリを hexdump で読む..バイナリを見ても壊れている
    backup のバイナリログポジションを壊れているポジションを飛ばして修正
    バイナリログは追記形式なので途中で壊れたとしても後続は壊れない

普段、 RDS しか運用していなくてレプリケーション遅延やログバッファサイズやその他パラメタのパフォーマンスチューニングしか MySQL を調査したことがなかったのでこの手の話は新鮮でとても面白く聞けた。

イベントを開催してくださった皆様、発表者の皆様、ありがとうございました!!

Kyoto.なんか #2 で Rust の実践的な話について発表してきました

タイムラインをみていると流れてきたので、なんか会社休みだし、なんか実家にいるので Kyoto.なんか #2 で インフラエンジニアのための Rust というタイトルで発表してきた。

atnd.org

今回、発表で話しきれなかったことやうまくいかなかったことを、ブログ記事に発表内容をまとめた。

※ 発表資料はあとでリンク貼ります。

モチベーション

趣味で Rust を書いていて、 Rust に対するモチベーションをあげたかった話。

Rust というと一般にシステムプログラミング言語で、変数や式の所有権を付与することで安全性や高速性が謳われている。 Hacker News や Reddit を眺めているとほぼ毎日のように Rust の話があって、みんな OS や DNS サーバを実装したり、知見を共有し合っていて楽しそうだなあと思っていた。 趣味のプロダクトだけではなくて、エンタープライズでも一部のコンポーネントとして使っている事例が CloudFlare や Dropbox からもあがっていて、すごくホットな雰囲気も伝わってくる。 一方で国内の Rust 事情はというと、あまり情報が出回っていなくて、そもそも Rust の実践的な話を聞かないなあと感じていたのが今回の話につながっている。

最近、業務で苦労していることを絡めて Rust で netmap を使ったネットワークパケットの作成、解析として将来を見据えた話題になればなあと思って話をしてきた。

実践的な Rust の使い方を目指して

最近、 AWS を使って運用している案件で高トラフィックな要件の案件があって、負荷試験なんかやるとボトルネックが掴みにくくなっていて困っている。 大抵は EC2 インスタンスのリソースが限界になることが多くて、インスタンスタイプごとの性能がこういった形で共有されているのだけど、帯域はともかく m3.large なんかだと PPS は限界に到達してしまう事案が発生している。

qiita.com

問題に対して、今のところは数の暴力で殴って解決していて落ち着いている。 けれども、それではコストがかさむし、AWS だからといって無限にスケールアウトできるわけでもない。 さらに、パケットの処理能力がボトルネックとなっているほどネットワークが高負荷な場合、パケットキャプチャしようとすればパケットを取りこぼす可能性がある。 パケットキャプチャで取りこぼすようだと、何かネットワークまわりで問題が発生したときに調査がしづらくなるため見過ごせない。

そうした課題は以前から社内でも懸念に上がっていて、 netmap や Intel DPDK といった技術は注目していて情報は追っていた。 Rust で netmap や Intel DPDK を利用して何かできれば実践的な話になるんじゃないかなあということで、Rust から netmap を使ってみた。

Rust で packet を作成 & 解析する

Rust には libpnet という Crate (Rust のライブラリ) があって、汎用的なプロトコルスタックを持っていてパケットを作成 & 解析するのに使える。

github.com

標準では扱うレイヤによって OS のネットワークスタックを使うようなんだけど、その気になれば自前でネットワークスタックを libpnet 上で作ることもできる。 リポジトリexamples にはパケットキャプチャのサンプルプログラムが含まれていて、 lo を指定してパケットキャプチャしてみると以下のような結果を得ることができる。

./target/debug/pnet-study-general lo
[lo]: TCP Packet: 127.0.0.1:52472 > 127.0.0.1:6600; length: 39
[lo]: TCP Packet: 127.0.0.1:6600 > 127.0.0.1:52472; length: 269
[lo]: TCP Packet: 127.0.0.1:52472 > 127.0.0.1:6600; length: 32
[lo]: TCP Packet: 127.0.0.1:52472 > 127.0.0.1:6600; length: 44

コードも 200 行程度しかなくて、簡潔な記述でかけることがわかると思う。 パケットキャプチャと逆の要領でパケットを作成することもできて、各レイヤの処理に精通していなくても必要なヘッダやパラメタが分かるところが嬉しい。

さらに libpnet では様々なデータリンク層インターフェイスがサポートされていて、 以下のリンクから見れる。

https://github.com/libpnet/libpnet/tree/master/src/datalink

libpnet がサポートするデータリンク層インターフェイスには中には netmap も含まれていて、今回はこれを利用した。

試してみたのは ICMP echo-request と ICMP echo-reply の簡単な例。 netmap の環境を用意するには netmap をサポートしたデバイスを持っていなかったため、docker を利用して veth インターフェイスを作成し環境を切り分けて、 veth 上で netmap を試してみた*1。 ICMP echo-request を送信して、受け取ったパケットを解析して標準出力に出力した後に ICMP echo-reply を返し、返したパケットも解析して標準出力に出力している。

root@6485c836a3ff:/# /netmap-ping eth0 172.18.0.1
64 bytes from 172.18.0.1: icmp_seq=0 ttl=64 time=
64 bytes from 172.18.0.1: icmp_seq=1 ttl=64 time=
64 bytes from 172.18.0.1: icmp_seq=2 ttl=64 time=
debug # ./pnet-study-pong vethf90b4d8
[vethf90b4d8]: ICMP echo request 172.17.0.2 -> 172.18.0.1 (seq=0, id=70)
[vethf90b4d8]: ICMP echo reply 172.18.0.1 -> 172.17.0.2 (seq=0, id=70)
[vethf90b4d8]: ICMP echo request 172.17.0.2 -> 172.18.0.1 (seq=1, id=70)
[vethf90b4d8]: ICMP echo reply 172.18.0.1 -> 172.17.0.2 (seq=1, id=70)
[vethf90b4d8]: ICMP echo request 172.17.0.2 -> 172.18.0.1 (seq=2, id=70)

OS のネットワークスタックを使うと、 ICMP のヘッダとデータ部分を作成するだけでいいんだろうけれど、 netmap を使うと当然ながら OS のネットワークスタックを使えない。 そのため、今のところパケットを全部自前で作成しないといけないところが少ししんどいところ。

ちなみに、今回利用した、コードは以下のリンクから

netmap-ping の方は需要ありそうだと思っていて、これからも機能を加えていきたいなぁと思っているので、アップデートしていきたい。

まとめ

発表初めに会場で Rust を書いたことがある人を聞いたら結構、多くて驚いた。 # たぶん、若手エンジニアが多かったからだろうな〜。若手エンジニアが多いからこそ、 Rust について話そうと思ったんだけども。

Rust を使ったモチベーションとして、インフラエンジニア向けな話だったと思うのでそこは刺さりにくいのよなあと思ったので少し反省。 けれども id:y_uuki さんや id:moznion くんには刺さったようで、発表後や懇親会ではずっと喋っていてて、一定手応えはあったと感じることができたので良かった。

コードを書いていて、思ったのが libpnet のプロトコルスタックを使うと簡単にパケットを作成できるので libpnet は優秀だなあと感じた。 使っていて思ったのはパケットを作成するというよりはどちらかというと、受け取ったパケットを一部書換えてキャプチャしたり、転送するのに向いているなと思ったので、ロードバランサやホワイトボックススイッチとかの実装に向いているんじゃないかなあと思った。 そういったこれからも発展していくものに関わっているところで Rust は将来明るい技術だと思うし、学ぶモチベーションが上がるし、プロダクション投入に向けて高めていきたい。

最後になりましたが、こうした発表の機会を設けてくださり、ありがとうございました!! > id:hakobe932, id:hitode909

*1:発表した時点ではうまく疎通できなかったんだけど、 veth だから実際は一つのインタフェイスしかないわけで..普通に考えてコンテナの内側も外側も netmap じゃないとダメだよねといオチだった

Cargo.toml の編集に cargo-edit を使う

この記事は以下の記事の本記事です。 qiita.com

cargo-edit について cargo-edit の README.md をベースに簡単に日本語でまとめています。

※ 執筆時点の cargo-edit のバージョンは cargo-edit-0.1.3 です。

cargo は Rust の強力なツールの一つで、パッケージの取得、ビルドといった機能を持っています。 パッケージの追加や削除といった作業は Cargo.toml を編集して行えます。 管理するパッケージが増えるにつれ、依存解決やバージョンの変更などを行いたくなる場合、ファイルの編集というインターフェースはエディタで開くという手間もあり面倒に感じることが多くなるでしょう。 cargo-edit はそれを解決してくれるツールで、パッケージの追加・削除・確認を行うことができます。

cargo-edit: https://github.com/killercup/cargo-edit

インストール

cargo install でインストールできます。 cargo はなるべく最新版を利用することが推奨されています。

cargo install cargo-edit

使い方

cargo に以下のサブコマンドをサポートする形でインストールされます。

  • cargo add
  • cargo list
  • cargo rm

各サブコマンドは cargo のサブコマンドと同様に cargo add --help のようにサブコマンドの後に --help を指定するとヘルプが出力されます。

cargo add

cargo add は名前の通り、パッケージを追加します。

# パッケージ名@バージョン でバージョンを指定する
$ cargo add regex@0.1.41
# バージョンが指定されない場合は最新のバージョン番号を crates.io から取得する
$ cargo add rand --build
# crates.io に存在しないパッケージを追加することもできる
$ mkdir -pv ./lib/trial-and-error
$ cd ./lib/trial-and-error
$ cargo init --name local_experiment
$ cd ../../
$ cargo add local_experiment --path=lib/trial-and-error/

Cargo.toml はこうなります。

[package]
authors = ["kizkoh"]
name = "study"
version = "0.1.0"

[build-dependencies]
rand = "0.3.14"

[dependencies]
regex = "0.1.41"

[dependencies.local_experiment]
optional = false
path = "lib/trial-and-error/"

cargo list

cargo list は依存パッケージの一覧を出力します。

# cargo build しないと `Your Cargo.toml is missing.` が出力される
$ cargo build
# --tree オプションで依存木を出力できる
$ cargo list --tree
├── rand (0.3.14)
│   └── libc (0.2.14)
└── regex (0.1.73)
    ├── aho-corasick (0.5.2)
    │   └── memchr (0.1.11)
    │       └── libc (0.2.14)
    ├── memchr (0.1.11)
    │   └── libc (0.2.14)
    ├── regex-syntax (0.3.4)
    ├── thread_local (0.2.6)
    │   └── thread-id (2.0.0)
    │       ├── kernel32-sys (0.2.2)
    │       │   ├── winapi (0.2.8)
    │       │   └── winapi-build (0.1.1)
    │       └── libc (0.2.14)
    └── utf8-ranges (0.1.3)

cargo rm

cargo rmcargo add の逆の操作です。

# regex の依存を削除する
$ cargo rm regex
# rand の依存を削除する
$ cargo rm rand --build

Cargo.toml はこうなります。

[package]
authors = ["kizkoh"]
name = "study"
version = "0.1.0"

[dependencies]

[dependencies.local_experiment]
optional = false
path = "lib/trial-and-error/"

まとめ

ターミナルは常にビルドやテストのため開いていると思います。 cargo-edit を使えばエディタで Cargo.toml を開くことなくコマンド一つでパッケージの追加・削除・確認を行うことができます。

また、記事中では例に取り上げませんでしたが cargo add で追加したパッケージはアルファベット順にソートされます。 rustfmt を使ってコードはフォーマッティングできても Cargo.toml はフォーマットできません。見栄えを気にする際も便利なツールです。使わない理由はないですね!