実践Rust入門を読んだ

Rustに興味を持っていたところ、著者の一人であるκeen氏*1から献本頂いたのでゴールデンウィークに読みました。全部は読み切れていないですが、紙媒体の発売前に記事を出して応援したかったので今読んでいるところまでの感想を書きます。

Rustに興味を持ったきっかけ

κeen氏とは新卒入社時の同期で、彼はコンパイラとRustの話をよくしていたのでそれをきっかけでRustの存在は認知していましたが、そこまで強い関心は持っていませんでした。

時が経ち、世の中でもRustという言語について言及されることが増えてきた頃にこの本が出版されたので、改めて興味を持ったという感じです。

(2016年にRustのハンズオン会に参加したことがあったのですが、高熱が出てで早退してしまったのでこれは書いたウチに入らないですね。。)

普段書いている言語

普段は業務で書くScalaがメインです。Scalaで広告システムを作っています。あとはたまにスクリプト的にRubyを書くくらいです。C言語は大学の授業で使った程度です。

どんな本なのか?

Scalaを書いている人にはScalaでいうところの「コップ本」に相当すると言うとわかりやすいです。 入門する人、業務等まとまった時間を使って書きたい人が最初に読む本という感じだなと思いました。飽きずに読み進められました!

あとはRustに限らないプログラミング上必要になってくる前提知識が適度に挟まれているのがよかったです。 例えば、標準ライブラリとしてHashMapが用意されていますが、そのハッシュアルゴリズムはSipHashというものが使われています。ハッシュアルゴリズムは簡単なものだとhashdos攻撃という攻撃に対して脆弱になってしまいますが、暗号学的ハッシュ関数を使うことでこの種の攻撃に対して耐性が得られます。(初版 p183) こういった知識がしつこくない程度に入っているのが嬉しいです。

本の構成

目次の一覧は技評のサイトに載っています。

実践Rust入門[言語仕様から開発手法まで]:書籍案内|技術評論社

C言語等何かしらのプログラミング言語を触ったことがあって、とりあえずRustの言語機能的な部分が知りたい人は5章から流し読みを始めるとよさそうです。Rustの所有権の概念についてだけ知りたい人は7章だけで読むとよさそうです。ちなみに僕は1章から順番に8章までと12章を読みました。

1章はRustという言語が流行っているらしいがどんな言語なのかなんとなく知りたい、といった人にとってはありがたいと思いました。初めて聞いたプログラミング言語について調べるときまず適当にググって世の中の情報を集めるとおもうのですが、それがだいたいまとまっている感じです。どんな言語なのかなんとなく知った気になれます。

2-3章ではRustを実行するための環境を構築してVS Code上でコードを書いてみます。僕はPCを横に置いて実際に手をうごかすことはせず、流し読みしました。2章で環境構築をして、3章でバイトニックソートというソートアルゴリズムを実装します。いきなり完全なものを実装するのではなく、Pythonのコードと比較しながら最低限の実装→ジェネリクス→ソート順のカスタマイズ→並列ソートというふうに改善していきます。コードや実行結果が全て書かれているので書いて動かす様子を想像しながら進めました。とりあえず基礎編をひととおり読み進めてからまた戻ってきて見返しながら実際に実装するとよさそうな気がします。性格的な問題もあるかもしれませんが、先が気になるのでひとつひとつ丁寧に確認することはせずにどんどん読み進めめました。

4-5章でデータ型について知ることができます。5章はプログラム上で定義したデータが実行時にメモリ上でどのような扱いがされるのか等書かれているので抑えておいたほうが何か疑問に思ったときに想像がつきやすそうです。

6章が文法系です。特に違和感のあるものはなかったです。初めて見たのはシャドウイングくらいでしょうか。

7章がRustの一番の特徴でもある所有権についてです。さすがにここは初めての概念なので手元で実行しながら復習したほうがよさそうです。所有権による管理が常に強制されるのかと思っていましたが参照カウンタによるリソース管理も用意されているのは便利そうですね。弱い参照も実装されているおかげでオンメモリキャッシュ等も作りやすそうでした。

8,9-11章: トレイトとポリモーフィズム, 実践編です。まだ読んでいません。。

12章はFFI(Foreign Function Interface)についてです。RustからC言語の関数を呼び出したり、C言語からRustの関数を呼び出したりすることができますが、それの実現方法について書かれています。今までプログラミングをしていて他の言語で書かれたコードを呼び出した経験がなく興味があったのと、C言語で実装したものが呼び出せるのであればその他の言語との連携もできるのであろうという想像をしているので方法が気になったので読みました。 RustのコードからするとC言語で実装されている部分は外界なのでRustのプログラミング的な安全性は保証されませんが、unsafeという概念によってRustの言語機能によって守られる部分と守られない部分を明確に扱うことで他言語と連携できることがわかりました。

感想

2年くらいかけて書いたそうなのですが、対象は最新の2018editionについてになっていたり、昨年末に発表されたAmazonのfirecrackerについての言及があったり、常に最新の話題に追従しながら書かれていたんだろうなと思いました。

Scalaで知っていたような概念(タプルとか、トレイトとか)も多少出てきていたので、読んでいてそこまでハードルは感じませんでした。一番大きいのはきっと所有権まわりだと思いますが、まだ書いていないのでそこはこれから学びます。この本をひととおり読んでから手を動かし始めれば何もしらないよりはつまずかないはず、と予想しています。「Scalaでいうところの「コップ本」に相当する」と書きましたが、まだ自分が実際どうだったかはまだわからないものの、数日でひととおり知識としてインプットすることはできたと思うのでRustに入門したい人が周りにいればとりあえず薦めたい一冊です。

ちなみに献本してくれたκeen氏が担当した箇所は自分が読んでいない実践編なので今後ちゃんと読み進めます… m( )m

書籍版

Kindle

*1:kじゃなくてκなのがポイント

MAASを使って物理サーバとVMを簡単に構築/管理できるようにする

物理サーバ上でVMを簡単に立てたり落としたりできる環境がほしかったので、OpenStackの検証をしていました。

とりあえず最新版でVMが建てるために必要な構成を考えてKeystone, Glance, Horizon, Placement, Nova, Neutronをデプロイして、VMが経つところまでいくのに数日かかりました。(実質12時間くらい)VMが経ったのはいいのですが、VMメタデータサーバがうまく通信できていないのでcloud-initが動作せずという感じでこれ以上ゴールデンウィークの時間を使うのがしんどくなり、半ば諦める形でMAASを検証してみたのですが、MAASが自分の用途にかなり合っていたので紹介します。

結論から言うと個人で数台の物理サーバとVM管理する程度だったら構築が本当に簡単なのでおすすめです。

やりたかったこと

遠隔地にある物理サーバ数台を管理して、その上にVMを建てたり落としたりを簡単に管理できるようにする。

VMを建てるだけであれば物理サーバにOSを入れてsshをすればよいのですが、できればどのマシンでどのVMが動いているのかとか、IPアドレスとか、ついでにいろいろ管理したかったのです。 分かっていたことですが、物理サーバ複数台上にVMを建てるのとブロックストレージも使わないような環境だとOpenStackはおおげさすぎました。(単に使ってみたかった)

前提条件

MAASで管理したい各サーバの通信用ポートとIPMIポートはネットワーク的にMAASをインストールするマシンと繋がっている必要があります。サーバの通信用ポートとIPMIポートは同じネットワークでもよいですし、別れていても大丈夫です。通信用のポートに関してはPXE bootする際にDHCPを使うので同一のL2に存在している必要があります。(後述のrack controllerというのを増やすことでスケールします)IPMIがないサーバも管理できますが、MAAS経由でIPMIがないと自動で電源を入れたり、起動設定を変えたりといったことはできません。

管理対象のサーバ

RX200S5

  • CPU: 8コア16スレッド
  • Memory: 16GB
  • HDD: 1TB x 2
  • OS: Ubuntu 18.04

R610

  • CPU: 12コア24スレッド
  • Memory: 48GB
  • HDD: 320GB x2, 1TB x2
  • OS: 未インストール

R610

  • CPU: 12コア24スレッド
  • Memory: 48GB
  • HDD: 1TB x 1
  • OS: 未インストール

その他

  • 将来的には管理対象に入れたいサーバがあと数台

MAASとは

Metal as a Serviceの略でCanonicalが提供している物理サーバ等の管理ソフトウェアです。Metal(物理サーバのこと)といいつつもVMの管理もできちゃいます。

今回は詳しく紹介しませんが、MAASと連携するjujuというソフトウェアを組み合わせるとOpenStackやKubernetes等もWeb UIからぽちぽちするだけで構築できます。

MAASの構成

MAASの最小構成はサーバ1台で十分です。この次で紹介するコマンドを打つだけで以下のすべてがインストールされます。構成がシンプルなので冗長化やバックアップも簡単そうです。

PostgreSQL

MAASが使うDBです。インストール時にユーザ作成やテーブルのマイグレーション作業も自動で行われるので直接触ることはなかったです。

Region Controller

データセンターやリージョン全体を管理する概念です。1つのRegion内で管理されるAZやデータセンタのフロアといった概念も設定で表現できます。 今回は管理したい規模がとても小さいのですべてdefaultのままです。 postgresql(DB), regiond(APIとWeb UI), bind9(DNS), ntpd, squid(http cache proxy), rsyslogdが動いています。

Rack Controller

データセンタのラックを管理する概念です。ラックやラック列といった一定規模のサーバの集合ごとに設置されるイメージです。 今回は管理したい規模がとても小さいのでRegion ControllerとRack Controllerは1つのサーバに同居しています。 rackd, dhcpd, httpd, tftpdが動いています。 regiondが親だとするとrackdは子に相当します。rackdがラック等の一定規模ごとに存在することでデータセンタが大きくなってもスケールします。

MAASのインストール

今回はMAASの管理ノードとしてRX200S6を使うことにしました。ちなみにMAAS上でもVMは作れるので限られたリソースは活用できます。

$ sudo apt-add-repository -yu ppa:maas/stable
$ sudo apt update
$ sudo apt install maas
$ sudo maas init

MAAS URL [default=http://10.55.60.1:5240/MAAS]: http://192.168.0.1:5240/MAAS
Create first admin account:       
Username: admin
Password: ******
Again: ******
Email: admin@example.com
Import SSH keys [] (lp:user-id or gh:user-id): gh:kurochan

githubの公開鍵がインポートできるのでとても楽です。ここでインポートしなくてもあとで追加できますが、ここで追加しておくと作成した物理マシンやVMに自分の公開鍵でログインできるので便利です。

ハマったところ

MAASを動かすだけであれば構築作業は驚くほど簡単でした。

MAASをインストールしたサーバでだけ/var/log/syslogにログが転送されなくなるという現象に悩まされました。(Ubuntuはsyslogはログ発生(/dev/logに書き込み)→journald→rsyslog→/var/log/syslog書き込みという流れでログが転送されます)

いろいろ調査した結果、どうやらmaas-rsyslogというサービスが影響していそうなことまではわかりました。maas-rsyslogというのは、構築対象のサーバのsyslogをMAASに転送してくれるサービスです。プロセス的にはrsyslogで、

$ ps aux | grep -v grep | grep rsyslog
maas     30435  0.0  0.0 363932  4876 ?        Ssl  May05   0:00 /usr/sbin/rsyslogd -n -f /var/lib/maas/rsyslog.conf -i /var/lib/maas/rsyslog.pid
syslog   31712  0.0  0.0 269336  5344 ?        Ssl  May05   0:13 /usr/sbin/rsyslogd -n

syslogユーザで動いている通常のrsyslogに加えて、maasユーザで別のrsyslogが動いていることがわかります。maas用のrsyslogの設定を見た限り特に悪影響を与えるような設定は見当たらなかったのですが、以下の通り、通常のrsyslogの設定ファイル( /etc/rsyslog.conf )を編集し、module(load="imuxsock") の次の行に input(type="imuxsock" socket="/dev/log") の一行を加えたところなおりました。

module(load="imuxsock") # provides support for local system logging
input(type="imuxsock" socket="/dev/log") # ←この行を追加

物理マシンを認識させる

qiita.com

この記事を読みながら進めました。構築対象の物理サーバと同じセグメントに対してDHCPを有効にするとRegion ControllerのDHCPサーバが有効になり、PXE bootができるようになります。

物理マシンにOSをインストールする

物理マシンのboot優先順位をPXE bootが一番になるように設定して再起動します。するとMAASがPXE bootの要求をきっかけにサーバを認識し、IPMIのユーザを自動作成します。 対象サーバを選択し、comissionというボタンを押すとサーバの構成情報をひととおり収集してくれます。

f:id:kuro_m88:20190506183021p:plain

構築したいマシンを選んでデプロイボタンを押すだけで、rackdが対象の物理マシンのIPMIを操作して電源を入れて、PXE bootして自動でOSがインストールされます。複数台同時に構築もできて便利ですね。

f:id:kuro_m88:20190506183112p:plain

OSデプロイ時にRegister as MAAS KVM hostというチェックを入れておくとこの物理マシン上にMAASからVMが作成できるようになります。(ここでチェックを入れなくても既存のホストをKVM hostとして手動で追加する方法もあります)

VMを作成する

VMは物理マシンとちがって最初から存在しないので、作成してあげる必要があります。さきほど追加したKVM hostsはPodというメニューから見られて、ホストマシンを選択するとホストマシンのキャパシティと現在稼働しているVMの一覧が見られます。CPU, メモリのオーバーコミット率も設定できます。

f:id:kuro_m88:20190506183330p:plain

ここで好きなサイズや構成のVMを作成すると、物理マシンと同様にMachineとして認識されます。

f:id:kuro_m88:20190506183205p:plain

VMにOSをインストールする

VMと物理マシンは同様に扱われるため、物理マシンと同様な手順でOSのインストールが可能です。ただし、Comissionは終わった状態になります。

感想

OpenStackと比べなくても構築がびっくりするくらい簡単でよかった。

「転職はとりあえず3年働いてから」って本当ですか?

去年の年末に途中まで書いていた記事が下書きのまま放置されていたので、タイトルはそのままにして、加筆修正しました。半分くらい去年に書いた内容で、半分くらい今日書いた内容です。

「転職はとりあえず3年働いてから」という言葉を就活生時代によく聞いていましたが、当時は就職してすらなかったので転職なんて想像もつきませんでした。僕と同世代のエンジニアは約4年間の間で転職した人の話もそこそこ聞くようになりました。そんな中で僕は新卒で入った会社で働き続けて気がついたらもうすぐ4年になります。

入社して3年が経った

(※ この記事を書き始めた当初は3年でしたが、今はもうすぐ入社して4年になります) サイバーエージェントとという会社に2015年に新卒で入社しました。入社直後は聞きなれない言葉ばかりが飛び交っていて面白かったのでメモして記事にしてました。今みるとちょっと古いですね。

kurochan-note.hatenablog.jp

ちなみに、少し前に増田で似たような記事がバズっていましたがあれは僕は書いていません。中の人からするとちょっと内容が古かったりしたので元社員とかなのかなと想像してます。検索してみたら削除されていました。

入社してから何をやっていたのか

入社以来チームの異動をしたことはなく、ずっと同じチームに居ます。3年目くらいまではずっと同期を見て焦っていたきがします。

業務で対外的に発表した機会は少なくて、たぶん以下の5つくらいです。

Dynalystにおける動的な画像生成 - Speaker Deck

Scalaプロダクトのビルド高速化 - Speaker Deck

Dynalyst流Datadog活用法 (公開用) - Speaker Deck

Datadog Logsでアプリケーションログを管理する - Speaker Deck

月間数千億リクエストをさばく技術 (ArchitectureNight公開用) - Speaker Deck

この5つだけを見るとDevOps寄りな仕事をしているように見えますが、Scalaを書いている時間が一番多いです。インフラ、監視、ミドルウェア、アプリケーションあたりは特に担当領域を分けずにチームメンバ全員で面倒を見るようにしているので、アプリケーションの開発時間が一番長いものの、全員ひととおり触れるはずです。

最後のスライドは社内向けのエンジニアカンファレンスで発表したところ、ベストセッション賞を貰ったので社外向けに内容を加工して発表してみました。timakinというエンジニアが企画したアーキテクチャナイトという勉強会の開催を手伝ったところ、logmiさんが取材に来てくれました。

https://logmi.jp/tech/articles/306550 logmi.jp

公開する前に事前に原稿は共有してもらえて、用語とかもちゃんと表記されていてクオリティの高さにびっくりしました。ほとんど修正の依頼はしていないです。

あとはICTトラブルシューティングコンテストという学生向けのインフラ系の技術を競うコンテストの実行委員(社会人サポータ的なやつ)に会社の名義で参加しています。

developers.cyberagent.co.jp

会社からコンテスト用のサーバとかスイッチとかファイアウォールとかの機材貸出もしていて、インフラ部署の同期とその部署のボスが検証機材等を集めてくれたおかげで毎回協賛できています。そのためにデータセンタ行ってもらったこともあるっぽくてありがたい。。

最近は何をやっていたのか

ずっと広告システムの開発をやっています。バックエンド系が中心です。 3年目になってようやく普通(?)に仕事ができるようになってきた実感があります。4年目も黙々と開発をやっていました。

ようやく色々わかってきた

遅いかもしれませんが、仕事の進め方とか、巻き込み方とか、ようやくわかってきたような実感があります。

採用に関わるようになった

会社のビジョンに「採用には全力を尽くす。」「能力の高さより一緒に働きたい人を集める。」とあります。

ビジョン | 株式会社サイバーエージェント

YJC(良い人を自分たちでちゃんと採用する)プロジェクト始動! | おざまさブログ。

YJC(良い人を自分たちでちゃんと採用する)プロジェクト始動! | おざまさブログ。

YJCという取り組みがあって、要するに現場社員が採用にしっかりコミットしようということです。名前の略がしっくりときませんが(笑)採用活動に熱心な社員がたくさん集まってくるのはすごいと思います。うまくいっているところもありますが、うまくいっていないところもあって、まだまだ全社員の力は活かしきれていないのでそのあたりは模索中というところでしょうか。部署の先輩が考案した「ひとりDSP」というエンジニア研修とそれをインターンに展開したものは3年半経った今も改善を重ねて受け継がれていて、直近のインターンも後輩が記事を書いてくれました。

developers.cyberagent.co.jp

この回は僕らは運営はしなくて、運営をしてくれる社員をマネジメントする立場というのをやってみたのですが、それについては年明けに記事を出します。

新卒のトレーナーを担当した

誰が言っていたか忘れましたが、新卒のトレーナーになる人はこれを読んだ方がいいと言われました。

ameblo.jp

僕の働いている事業部の取締役の記事です。 「覚えさせる」のではなく、「成果を出させること」がトレーナーの仕事だというわけです。

ただ無理にやりすぎると「お膳立てされた成果に自分(新卒)が乗っかる」感じになって逆に嫌になる気がするので、バランスを見極めるのがトレーナーの仕事なのでしょう。放置していてもどんどん進めていくタイプの新人の場合は見守りつつ、本当にヤバい時だけストップを掛ける立ち回りがいいんじゃないでしょうか。

高校の時の数学の先生が「『なんでわからないの?』と言ったら教える人失格だ」言っていた言葉が記憶に残っています。もし自分が「なんでこんなこともわからないんだ」と思うことがあったとしてもそれだけは言ったらダメと教わりました。突き放してしまうとわかったフリをする癖がついちゃいますよね。「何がわからないのか分かっていない状態」を一緒に「何が分からないのかが分かっている状態」にするのかトレーナーの業務なのかなと思いました。何がわからないのかが分かっていればあとはその穴を自分で学んで埋めて貰うように促すことができますよね。

Slackに #furikaeri_xxx (xxxはトレーニーの名前)という自分ひとりしか居ないprivateチャネルを作って、ひとりでもくもくとメモを取っていました。基本は新卒と業務のふりかえり面談(トレーナーは月1回以上の頻度でやることになっています)をした時に新卒と話しあって出た話をひたすらメモしてるだけです。あとは業務中にふと思うことがあればふりかえり面談の時に話題に出せるようにあらかじめメモしておいたり。

f:id:kuro_m88:20181231223902p:plain

これだけ見ても他人からすれば何を言っているかわからないですし、ここに書いてある内容自体はたいして重要ではないです。読み返したときにその時の状況とか、考えてたことが想起できるのでお互いに振り返りがやりやすかったです。 毎日日記をつける習慣がある人なら自分でもやっていると思いますが、自分はしないタイプで、新卒のときに1ヶ月ごとでも振り返ったときにあまり思い出せなかったことがあったのでやってみました。 ちなみに1年のトレーナー期間が終わったあとにはこのチャネルにinviteして自分がメモしていた内容は共有しました。何かに使えれば使ってもらえばいいし、必要なければarchiveしてもらえばいいだけなので。

本を書いた

2月にこんな本が発売されました。社会人3年目のエンジニア2人と社会人2年目のエンジニア2人で書きました。

gihyo.jp

さくらインターネットの取締役の伊勢さんからお話をいただきました。雑誌の寄稿はしたことがあったのですが、本を書いたのは初めてでした。普通はこういったチャンスは社内の人間で固めたくなる気がしますが、他社の僕にも話を振ってくださって感謝しています。 ネタを探してまた本を書いてみたいと思っています。

社員総会で表彰された

今年の4月の社員総会で「最優秀ベストエンジニア賞」という賞をいただきました。”最優秀ベスト”って意味が被ってるんじゃないかと社内外からよく指摘されて若干恥ずかしい気持ちになりますが、表彰対象5人の中から1人が最優秀として選ばれるようなのでこういう名前になるようです(?)。3〜4000人くらいの社員の前でいきなり話すことになるとは思っていなかったので何を喋ったのかよく覚えていません…。たぶんたいした話はできませんでしたが、チームメンバーや同期には特に感謝しています。

給料の話

最近は新卒エンジニアの初任給のが各所で話題になっている気がします。 ぱっと思いついたところだと

エンジニアを対象に一律の初任給制度を撤廃し、個々人の能力に応じた給与体系を導入 | 株式会社サイバーエージェント

メルカリ、新卒新入社員向け人事制度『Mergrads(メルグラッズ)』導入 〜個人の能力や経験に応じたオファーを提示、内定者向けにインプットを支援し、内定期間中の昇給も〜 | 株式会社メルカリ

募集要項 - 採用情報 - ヤフー株式会社

採用について|さくらインターネット新卒採用

などでしょうか。自分の働いている会社は今年から給料が可変になっただけでなく、下限も去年度より上がっていますね。新卒エンジニアの価値がどんどん上がってきていることがわかります。

20代前半の優秀なエンジニアには転職ドラフトを見せないほうがいい - 彼女からは、おいちゃんと呼ばれています

そういう流れもあってなのかどうか知りませんが、「技術政策室」という組織が作られました。

https://7gogo.jp/-FlLmV4De4D7/12734

給料だけじゃないよっていう話は理解できるのですが、「だけ」じゃないので給料「も」大事だと思います。3年目までは何も考えていなかったので特に給料は気にしていなかったのですが、比較対象がなかったので3年目から少しずつ気にするようになりました。

転職ドラフト

転職をする意思がなくても自分がどんな評価をされるのか試せる、「転職ドラフト」というサービスがあります。学生時代を除けば1社でしか働いたことがないので、自分が世間的にどういう評価をされるのか気になり、去年の転職ドラフトに参加してみました。結果としては5社から指名を頂き、うち3社で当時より良い年収で指名を貰いました。

今の給料は当時一番よかった指名額より高くて、結果論で言えば転職しない方が給料がよかったことになります。 もしも転職ドラフトのタイミングで転職していたら自分も損していましたし、会社も(たぶん)機会損失していたでしょうから、1-3年目のうちにもう少し上がっていれば悩まずに済んだのになぁという気持ちがあります。ここの差は埋められるようにしたい。

「転職はとりあえず3年働いてから」って本当ですか?

今の職場に新卒で入ってよかったなという思いはあります。採用の中に関わっているとよく「素直でいいヤツ」という言葉を聞くのですが、僕の同期も本当にそうで、僕の代の採用に関わった人に感謝をしています。同期という繋がりが得られるのは新卒で入社した社員の特権です。非公式ながら、 “Ex CyberAgent Developers Advent Calendar 2016” という退職者アドベントカレンダー爆誕したこともありました。

Ex CyberAgent Developers Advent Calendar 2016 - Adventar

全然埋まってないんですが、実はこれ、全員同期だったりします。 僕と同じ事業部に居た同期も2人書いていました。

新卒とメンターリングについて考えてみた - Φφ's blog

サイバーエージェントを退職しました | κeenのHappy Hacκing Blog

みんな今でも仲がよくて、Slackでは毎日技術的な話からくだらない話までわいわいしています。

f:id:kuro_m88:20181231223944p:plain

同期は社内の色んな部署で働いているので、他の部署の情報といえばそこまで頻繁に流れてくるわけではないのですが、活躍している様子が耳に入ってくると「燃えている」という表現をすることがあります。4年近くも働いているとチームの重要なポジションに着いているひとも多くなってくるので、そういう人が方針転換をしたり、大きく動かすような仕事をしていると外から見たら炎上しているように見えるかもしれません。まぁみんな元気に仕事ができていればいいことなのかなと。

転職する理由はひとそれぞれですが、同期を見ていると、社外にチャンスを見つけたからというポジティブな理由の人が大半です。移り変わりが激しい業界だからこそ、チャンスだと思ったらすぐに動かないと躊躇しているとチャンスが消滅してしまう、なんてこともあるかもしれません。会社からすれば優秀な人材を失うことほど痛いことはないですが、こういった外部のチャンスをコントロールすることは無理ですし、縛り付けるのは不健全です。外部で新しいチャレンジをしたい、という人はたいてい応援して送り出して貰えるでしょう。優秀な人材を留めておくためには常にチャレンジができる環境を作らないといけないということですね。もし会社がコントロールできる事があるとしたら給料くらいでしょうか。チームや業務への満足度が高いのに給料が理由で優秀な人材が流出する場合があるとすれば悲しいですね。

で、「転職はとりあえず3年経ってから」という噂は本当だったのかという話ですが、あまり関係ないですね。ただ、 自分は結果的に同じ会社どころかずっと同じチームで働いています。自分が働いている会社だと、入社してから3年間ずっと同じチームに居るのは少数だと思います。まわりが当たり前のように異動/転職しているなか自分も動かなくていいのかと不安になったりもしました。 考え方を変えてみれば、「同じ会社で働く」という選択をしているわけです。同じ環境にずっと身を置くにしても、どこかへ行くにしても「選んでいる」という感覚は忘れないようにしないといけないんだろうなぁというのが最近思うことです。

僕は異動もしたことがないので、今のチームの事しか言えませんが、今のチームだと、なにか新しい事を始めたあと、慣れてきたので割と楽になってきたなぁと思ったり、飽きそうになるタイミングになるとたいてい新しいネタが降ってきたりしてあんまり楽はできません(いい意味で)。この会社のひとたちは成長し続けないと死ぬ病気なのかと思うくらいみんな現状維持だけすることに興味がないです。 もちろん、たまたま運良くそうなってるわけじゃなくてそういうマネジメントをしてくれているのだと思います。自分もそういうふうにやっていきたい。

次はなにするの?

ここで突然の転職予告があれば退職エントリーとして仕上がるかもしれませんが、退職しないのでこれは在職エントリーでした。

そして異動もしませんが、来年からチームの開発責任者をさせてもらうことになったのでがんばります。マネジメントっぽいことも業務に入ってくるとがっつり開発というわけにもいかなくて…という話をよく聞きますが、技術は好きなのでどこまでやれるのか試してみようとおもいます。

ただ、頑張りすぎて疲弊するという話もあるのでいい感じのバランスをとれればなと思います。

WireGuardでAllowedIPsに0.0.0.0/0を指定するとパケットが全てVPNインターフェイスに吸い込まれてしまう件

WireGuardという素晴らしいVPNソフトウェアがあります。シンプルなのでほとんどハマりどころはないのですが、自宅で試していたところ少し意図しない挙動を見つけたのでその理由と対処方法をまとめます。 WireGuard: fast, modern, secure VPN tunnel

何が素晴らしいのか、詳細が気になる方はこちらのスライドをご覧になるのがいいと思います。 [CB16] WireGuard:次世代耐乱用性カーネルネットワークトンネル by Jason Donenfeld

AllowedIPsに0.0.0.0/0を指定するとパケットが全てVPNインターフェイスに吸い込まれる

AllowedIPsに0.0.0.0/0を指定するのはどんなときかと言うと、VPNインターフェイス経由で送信するパケットの宛先をすべて許可したい時です。例えば、カフェのFree Wi-Fiが信用できない時にすべてのトラフィックVPNサーバ経由にしたい時はVPN経由のパケットの宛先IPは何が来るかわからないのですべての宛先のパケットを許可するために0.0.0.0/0を指定しますね。

もしくは、少数派な使い方かもしれませんが、L3 VPNなのでstatic routingをしたり、VPNの両端のインターフェイスでルーティングプロトコル(ospf, bgpなど)を喋るデーモンを起動して動的に経路情報を交換するようにするかもしれません。ルーティングテーブルで宛先を制御する場合も意図せずパケットがフィルタされないようにするためにAllowedIPsは0.0.0.0/0にすると思います。

このような意図で0.0.0.0/0を指定する場合、実際に0.0.0.0/0にマッチするパケット、つまり全てのパケット(後述しますが厳密には全てではありません)がWireGuardのインターフェイスを経由してほしいわけではありません。が、 wg-quick コマンドを使ってWireGuardのインターフェイスを作成すると全てのパケットがWireGuardのインターフェイスに吸い込まれてしまう現象が起きます。

現象の確認

OSはUbuntu18.04です。

ubuntu@c01:~$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
24: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:a3:9e:eb brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.59.79.145/24 brd 10.59.79.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::216:3eff:fea3:9eeb/64 scope link
       valid_lft forever preferred_lft forever

NICは1つだけあります。

WireGuardの設定ファイルを作ってみました。wg0.confの中身はこんな感じです。

ubuntu@c01:~$ cat /etc/wireguard/wg0.conf
[Interface]
Address = 10.0.0.1/24
SaveConfig = true
ListenPort = 51820
PrivateKey = GIWLRkDQXS/wym2pCWW2VDKTBpUp2a60AB+KSG18SEE=

[Peer]
PublicKey = uKjZpzBmrsp7YGqhpTLajsk2XiKj0HHYoRMkLVOaE2c=
AllowedIPs = 0.0.0.0/0
Endpoint = 192.168.100.61:51820

192.168.100.61:51820 というのがVPNの接続先だと仮定します。(適当なIPなので実際には対向は存在しませんが今回は問題ではないのでこのまま続けます)

WireGuardを起動してみる

wg-quick コマンドを使って起動してみます。

ubuntu@c01:~$ sudo wg-quick up wg0.conf
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip address add 10.0.0.1/24 dev wg0
[#] ip link set mtu 1420 dev wg0
[#] ip link set wg0 up
[#] wg set wg0 fwmark 51820
[#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
[#] ip -4 rule add not fwmark 51820 table 51820
[#] ip -4 rule add table main suppress_prefixlength 0

ubuntu@c01:~$ sudo wg
interface: wg0
  public key: M91GRcFHFdXtRL4UXh9d18sv1w9/KYbibOYcGyLzAwk=
  private key: (hidden)
  listening port: 51820
  fwmark: 0xca6c

peer: uKjZpzBmrsp7YGqhpTLajsk2XiKj0HHYoRMkLVOaE2c=
  endpoint: 192.168.100.61:51820
  allowed ips: 0.0.0.0/0

ubuntu@c01:~$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 10.0.0.1/24 scope global wg0
       valid_lft forever preferred_lft forever
24: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:a3:9e:eb brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.59.79.145/24 brd 10.59.79.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::216:3eff:fea3:9eeb/64 scope link
       valid_lft forever preferred_lft forever

wg0 インターフェイスが追加されましたね。

インターネットに向けてpingを打ってみる

8.8.8.8に向けてpingを打ってみました。

ubuntu@c01:~$ ping -c 3 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.

--- 8.8.8.8 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2055ms

…あれ?パケットが全部落ちていますね。ちなみに同時にtcpdumpをしてみたのですが、

ubuntu@c01:~$ sudo tcpdump -n icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wg0, link-type RAW (Raw IP), capture size 262144 bytes
12:52:34.626312 IP 10.0.0.1 > 8.8.8.8: ICMP echo request, id 1667, seq 3, length 64
12:52:35.650419 IP 10.0.0.1 > 8.8.8.8: ICMP echo request, id 1667, seq 4, length 64
12:52:36.674427 IP 10.0.0.1 > 8.8.8.8: ICMP echo request, id 1667, seq 5, length 64

wg0 インターフェイスのIPからインターネットに出ようとしていますね。これは変ですね。

WireGuardを落とすとどうなるでしょう?

ubuntu@c01:~$ sudo wg-quick down wg0.conf
[#] wg showconf wg0
[#] ip -4 rule delete table 51820
[#] ip -4 rule delete table main suppress_prefixlength 0
[#] ip link delete dev wg0

ubuntu@c01:~$ ping -c 3 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=3.37 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=118 time=3.35 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=118 time=3.19 ms

--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 3.196/3.309/3.378/0.093 ms

パケットは普通にインターネットに届いているようです。これが今回困った事象です。

原因

WireGuardを起動した状態でルーティングテーブルを確認してみます。一見問題がないように見えますね。

ubuntu@c01:~$ ip route
default via 10.59.79.1 dev eth0
10.0.0.0/24 dev wg0  proto kernel  scope link  src 10.0.0.1
10.59.79.0/24 dev eth0  proto kernel  scope link  src 10.59.79.145

WireGuardの起動時の出力を見ると気になるログが2箇所あります。

[#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
[#] ip -4 rule add not fwmark 51820 table 51820

なにやら怪しいですね。

ubuntu@c01:~$ ip rule
0:      from all lookup local
32764:  from all lookup main suppress_prefixlength 0
32765:  not from all fwmark 0xca6c lookup 51820
32766:  from all lookup main
32767:  from all lookup default

ubuntu@c01:~$ ip route show table 51820
default dev wg0  scope link

fwmark 51820 がパケットにセットされていない場合はルーティングテーブル 51820 を参照するルールで、そのルーティングテーブルはデフォルトルートとして wg0 インターフェイスが設定されています。つまり、何もパケットがマークされていない全てのパケットは wg0 からインターネットに出ていこうとします。これは困りますね。逆にどういう時に fwmark 51820 が付いているのでしょうか?会社の同期がそれっぽいものを見つけてきてくれました。

f:id:kuro_m88:20181104213916p:plain

Routing & Network Namespaces - WireGuard

Improved Rule-based Routing の段落をご覧ください。

we are able to set an fwmark on all packets going out of WireGuard's UDP socket

とあります。WireGuardによって暗号化されたパケットに fwmark をつけてくれるようです。全てのパケットをVPN経由にしたい場合、0.0.0.0/0の宛先をWireGuard経由にしたくなりますが、そうするとVPN自体の暗号化されたパケットもVPNインターフェイス(wg0)にルーティングされてしまいループしてしまって外に出られなくなりますが、 fwmark がついているパケットだけホストに元からあるルーティングテーブルを参照させ、それ以外のパケットは全てVPNインターフェイスにルーティングされるようにしてくれるようです。冒頭で例として挙げた、「カフェのFree Wi-Fiが信用できない時にすべてのトラフィックVPNサーバ経由にしたい時」などにはありがたい挙動ですが、後者の「static routingをしたり、VPNの両端のインターフェイスでルーティングプロトコル(ospf, bgpなど)を喋るデーモンを起動して動的に経路情報を交換する」場合には全てのパケットがVPNインターフェイスに吸い込まれてはこまりますね。

対処方法

原因はわかりました。デフォルトルートを wg0 向ける設定追加がされないようにすればいいだけですね。残念ながら、 wg-quick コマンド経由でこの設定が自動で投入されないようにする方法は見当たりませんでしたのでかっこ悪いですが以下のような設定をしました。

[Interface]
Address = 10.0.0.1/24
SaveConfig = true
PostUp = ip -4 route del 0.0.0.0/0 dev %i table $(wg show %i listen-port)
ListenPort = 51820
FwMark = 0xca6c
PrivateKey = aIG1izlztM2ISSsaLPmQpamqc0FdFnUoYTpeeYNWQm0=

[Peer]
PublicKey = uKjZpzBmrsp7YGqhpTLajsk2XiKj0HHYoRMkLVOaE2c=
AllowedIPs = 0.0.0.0/0
Endpoint = 192.168.100.61:51820

追加したのは PostUp = ip -4 route del 0.0.0.0/0 dev %i table $(wg show %i listen-port) の一行です。 %i というのは実行時にインターフェイス名に置き換えられます。追加された設定を直後に消すということですね(笑)

これで思ったような挙動を得ることができました。よかった。