UZABASE Tech Blog

〜迷ったら挑戦する道を選ぶ〜 株式会社ユーザベースの技術チームブログです。

hbstudy#82 の「SRE大全:ユーザベース編」で話をしてきました。

こんにちは。ユーザベースのSPEEDAで、SREチーム内のソフトウェアエンジニアをしている @tkitsunai です。

f:id:kitslog:20180319164521j:plain

3月15日(木)に、株式会社ハートビーツ様が主催している「hbstudy#82」で、「SRE大全:ユーザベース編」というお題目の下に、弊社のSREチームの4人で様々なSREの取り組みについて発表してきました。

SRE大全というお題目は、日本企業でのSREチームを立ち上げたとして界隈で有名なメルカリさんも発表しており、前回はクックパッドさんの発表でした。 今回、日本企業のTech Companyとしても有名な方たちと肩を並べることができ、知名度がまだまだ低いユーザベースのSREチームにとっては身に余る光栄でした。

我々が今回SREチームとして発表してきた内容は以下となります。

  • 「UZABASEのSREについて」 羽山 雄偉
  • 「ソフトウェアエンジニアリングによるToil削減」 橘内 孝幸 ( @tkitsunai )
  • 「FullGCとの闘い」 久保 裕史 ( @hirofumikubo )
  • 「On-premise Kubernetes on Rancher」金屋 泰士

発表資料は1つだと長いので小分けにしています。

「UZABASEのSREについて」 羽山 雄偉

「ソフトウェアエンジニアリングによるToil削減」 橘内 孝幸 ( @tkitsunai )

「FullGCとの闘い」 久保 裕史 ( @hirofumikubo )

「On-premise Kubernetes on Rancher」金屋 泰士

会場の方から質問も頂きまして、

  • 「他のSRE大全では、10数名でもきついという話を聞いたが、10名で回すのはどうか。」
  • 「トイルの計測について、差し込みの割合について定義はあるか?また、計測の精度はどうか。」
  • 「K8SをPrometheusで監視しているということだが、Rancherの監視はどうしているか。」
  • 「Prometheus自体の監視は?」

などなど、10名での運用体制についてや後半ではRancher成分が多めな印象でした。

YoutubeLiveでの配信もあり、hbstudyさんのチャンネルにも公開されていますので、そちらから会場の様子などを含めた全編を閲覧することができます。

www.youtube.com

インフラエンジニア勉強会 hbstudy - YouTube

(余談)

私のパートでは、めちゃくちゃ緊張してしまって声が震えるし真っ白になるしで、軽くトラウマレベルでした。 スピーカ慣れしてる人たち、本当に尊敬します。精進あるのみ。。。

仲間募集中!

ユーザベースのSPEEDA SREチームは、No Challenge, No SRE, No SPEEDA を掲げて業務に取り組んでいます。 「挑戦しなければ、SREではないし、SREがなければ、SPEEDAもない」という意識で、日々ユーザベースのMissionである、「経済情報で、世界をかえる」の実現に向けて邁進しています。

少しでも興味を持ってくださった方はこちらまで!

SRE Loungeについて

昨日の記事のSRE Loungeについてもどんどん他社様を巻き込んでおり、コミュニティの活性化を進めています。 是非、うちのSREはこんなことやってるよーと共有して頂ける企業様が居ましたら sre@uzabase.com 宛にご一報下さい。

Chatwork、CrowdWorks、スタディスト、ユーザベースでSRE Lounge #2 を開催しました

こんにちは、ユーザベース SREチームでインターンをしております杉田です。 1/17(水)に始動したSRE Loungeの第二弾として、3/13(火)にSRE Lounge #2を開催しましたので、 今日はその模様を投稿します。

そもそも「SREとは?」といったことや、SRE Lounge開催の背景については、 SRE Lounge #1の記事に詳しく書きましたので、 ぜひご覧下さい。

今回も前回と同様に、

  • SRE取り組み事例の共有(情報交換・発信)
  • SREについて議論し、知見を深める

といったことを目的として開催しました。

開催日時

3/13(火) 19:00〜

開催場所

今回はChatWork様の東京オフィスをお借りして開催しました。 スクリーン付きのシアタールームがあったり、文字通り間近に東京タワーを眺めることが出来たりと、 とても素敵なオフィスでした。

f:id:ksksgtt:20180314193353j:plain
ChatWork様のオフィス

f:id:ksksgtt:20180314193926j:plainf:id:ksksgtt:20180314193959j:plain
会場の様子

参加企業

コンテンツ概要

  1. 各社の取り組み事例等の発表と質疑応答(各社20分程度)
  2. 発表を踏まえた座談会(30分程度)

ピザやChatWork様が提供して下さった飲み物で軽食をとりつつ行いました。

各社の取り組み事例等の発表と質疑応答

各社の発表内容を発表資料と共に以下にまとめます。

ChatWork様

マイクロサービスアーキテクチャを積極的に取り入れ、Kubernetes環境を運用しているとのことでした。 現在は新しいシステムはKubernetesで稼働させ、既存システムについてもKubernetesへ絶賛移行中とのことです。 また、サービスメッシュ(Envoy/Istio/Linkerd...)の採用検討もされており、非常に勉強になりました。 個人的には、技術負債(レガシー)をマイナスに捉えるのではなく、今までビジネスを支えてきた「レジェンド」なアプリケーションという風に敬意を持って呼ぶという点が心に刺さりました。

当日発表資料:microservicesとSRE (第2回 SRE Lounge)

www.slideshare.net



CrowdWorks様

Monitoringに対する取り組みとして、DatadogやAWS CloudWatch、その他周辺ツールの活用をしつつ、 さらにDatadogに連携するツールcyqldogを開発しているとのことでした。 また、Infrastructure as Codeの取り組みとして、ChefやTerraformを採用しつつ、 Terraformで作成したサーバーと、秘伝のタレ化したサーバーの差分を検出してくれるajimiを使って、コード化をスムーズにしているそうです。 OSSを活用するだけでなく、独自のソフトウェアを積極的に開発しOSS化している点は、弊社も見習いたいところです。

当日発表資料:SRE at CrowdWorks



スタディスト様

Monitoringでは、Fluentd・ElasticSearch・Kibanaの組み合わせやstackdriver・newrelicを、Infrastructure as CodeではAnsible・Serverspecを活用されているとのことでした。 さらに、組織的な取り組みとして、はてな様でも実施されているPerformance Working Groupという取り組みを行い、 SRE以外のチームメンバと計測数値や情報を共有し、議論する場を定期的に設けているとのことでした。 パフォーマンスを上げるためにSREだけで全ての範囲をカバーすることは難しく、SRE以外の開発メンバーの協力を必要とする機会は多々ありますので、こういった場を設けることは非常に大事なことだと思いました。

当日発表資料:デブサミ2018 で伝えきれなかった 快適なマニュアル作成共有を支えるSite Reliability Engineering



ユーザベース

ユーザベースの発表は今回で2回目なので、焦点を絞って日々発生するデータエラーについてどんな取り組みをしているか紹介しました。弊社が提供しているSPEEDAのプロダクトの要はデータです。一言にデータと言っても多種多様なデータの種類・形式を取り扱うため、データの抜け漏れやデータ同士の競合など考慮すべき点は多くあります。これをエラーが発生してから対応するのではなく、SREとしてvalidationを高度化し、先手を打つための仕組み作りについて発表しました。

当日発表資料:SRE Lounge#2 UZABASE



まとめ

形式は前回と同様ですが、発表後の懇親会の中で、各参加企業の

  • 異なる規模やSREとしての体制・内部事情
  • SREとして取り入れているノウハウ
  • 目指そうとしているSREのあり方

といったことをざっくばらんに共有し、議論する場を設けたことで、知識はもちろんお互いの交流を深めることが出来、非常に密度の濃い勉強会となりました。

SRE Loungeは、今後も継続して開催する予定ですので、もし興味を持ってくださり、参加を希望される企業の方はこちらまでご連絡ください。

f:id:ksksgtt:20180314180656j:plain
集合写真

f:id:ksksgtt:20180314180740j:plain
今回参加したユーザベース SREチームメンバー

仲間募集!!

ユーザベースのSPEEDA SREチームは、No Challenge, No SRE, No SPEEDA を掲げて業務に取り組んでいます。
「挑戦しなければ、SREではないし、SREがなければ、SPEEDAもない」という意識で、日々ユーザベースのMissionである、「経済情報で、世界をかえる」の実現に向けて邁進しています。

少しでも興味を持ってくださった方はこちらまで

【k8s合宿】 Kubernetesのメトリクスを取得する 〜PrometheusにGrafanaを添えて〜

こんにちは、SPEEDAのSREチームの阿南です。前回から少し時間が経ってしまいましたが、今回はKubernetesのメトリクス取得についてです。本番環境でkubernetesを運用する際、ポッドがどの程度リソースを消費しているのか、クラスター自体のリソースは大丈夫かなど常に把握しておく必要があります。ただ、Kubernetesってどう監視すればいいのって疑問ありますよね。PrometheusとかGrafanaとかよく出てきて概要は理解できるんだけど、実際どう構築すればいいの、とお悩みの方に役立つ記事にしたいと思います。ちなみに弊社ではRancher上にKubernetes環境を本番で利用していますが、大枠は今回紹介するような構成で運用しています。

構築する環境

f:id:tanan55:20180301095238p:plain

利用する環境はGKEです。まずKubernetesクラスターの中にPrometheusを構築し、メトリクスを取得します。さらに、クラスター外部にfederation用のPrometheusを構築し、Grafanaでメトリクスを可視化します。概要をざっくりと箇条書きすると、下記のようになります。

【クラスター内部(図の左側)】

  • KubernetesクラスターにPrometheus ポッドを稼働させる

  • Prometheus ポッドでクラスター内のメトリクスを取得する

  • NodeExporter ポッドをDaemonSetで稼働させ、Node(GCEインスタンス)のメトリクスを取得

  • Prometheus ポッドで取得したデータについては保持期間を1日とする

【 クラスター外部(図の右側)】

  • federationを使ってクラスター外部のPrometheusから値を取得

  • Grafanaでメトリクスを可視化

本記事では、まずKubernetesクラスターの中にprometheus ポッドを稼働させて値を取得するところまで紹介し、federationやGrafanaでの可視化周りは次回記事で紹介したいと思います。

構築手順

前回同様手順は別途まとめていますのでこちらをご参照ください。

Kubernetesの認証方式について

GKEのKubernetesバージョン1.8からはRBACがデフォルトで適用されているため、Prometheusで監視をする際に監視に必要な権限を与えてあげる必要があります。 さて構築する際のポイントですが、まずは自分自身のアカウント(kubectlを実行するユーザ)にcluster-adminを付与します。

$ gcloud info | grep Account
$ kubectl create clusterrolebinding anan-cluster-admin-binding --clusterrole=cluster-admin --user=sample-owner@sample-project.iam.gserviceaccount.com

cluster-adminを設定する理由は、kubectlを実行するユーザの権限より強い権限をroleとして設定することはできないためです。 ちなみにbindingには、clusterrolebindingrolebindingの2つがあり、それぞれ適用範囲をクラスター全体にするか、細かく設定するかを選択できるようになっています。この辺りの設計は利用するサービスや会社によって最適な設定が異なると思いますので、ぜひ事例があれば聞いてみたいですね。 次にPrometheus用にサービスアカウントを作成します。

$ kubectl create serviceaccount prometheus --namespace monitoring

このサービスアカウントに対して、参照権限を付与します。ここで、resourcespods, services, deploymentなどのリソースを表し、verbs がそのリソースに対する操作を表します。Prometheusは全てのリソースに対して参照権限を与えたいので、resources* とし、verbsget, watch, list の動作を許可します。nonResourceURLsはエンドポイントの権限設定なので、細かく設定する際は/api等のエンドポイントを指定します。今回はnonResourceURLs*、verbsをgetとしてエンドポイントに対してGETリクエストを許可します。これでread_onlyな形で権限を作成することができました。 Prometheusの監視について、secrets等は閲覧権限不要のため内容を修正しました。ご指摘いただいた方ありがとうございます!

gist.github.com

GKEの場合1.7まではデフォルトのサービスアカウントで全てのリソース、操作が許可されていました。Kubernetes v1.8からRBACがstableになっているのでGKE側でもこの辺りの変更が入っているようです。まだPrometheusにはたどり着いておりません。序盤からハマリどころ満載で、楽しくなってきました。

ConfigMap設定

ConfigMapにPrometheusのconfigファイルを設定します。最初のglobal設定で、メトリクスの取得間隔を指定しています。ポイントとして、 kubernetes_sd_configs を利用してメトリクスの取得先をdiscoveryできるようにしています。このservice discoveryの設定を利用することにより、サーバが追加になったりした際にも自動的にそれを検知しメトリクスを取得できるようになります。Prometheusの強力な機能ですね。

gist.github.com

kubernetes_sd_configsの中身については正直いきなり理解するのは難しいと思いますので、とりあえず細かい説明は置いて次に進みます。

Deployment設定

PrometheusのDeploymentの設定ですが、ここで最初に作成しておいたサービスアカウントが登場します。下記16行目にserviceAccountName: prometheusの記載があります。これを指定することで、全てのリソースに対する参照all-readerができるようにしています。ちなみに、サービスアカウントを作成するとsecretにca.crt、tokenというデータが作成されます。このsecretはポッドが起動した際に、自動的に/var/run/secrets/kubernetes.io/serviceaccount の配下にマウントされます。これをPrometheusのconfigに指定することでAPI Serverの認証をパスできるようにしています。先ほどのConfigMapの設定でca_filebearer_token_fileによくわからないパスが出てきましたがシークレットがマウントされていたんですね。この辺りの仕様は公式ドキュメントに記載がありますので見てみるといいと思います。Deploymentにサービスアカウントを指定しなかった場合、defaultのサービスアカウントが適用されますので、認証が通らずメトリクスの収集ができなくなります。だからと言って権限を全解放すると色々な事故が起こる可能性がありますし、後からこの認証を入れていくのはかなりしんどいと思います。最初から正しく仕事をする。頑張りましょう。

gist.github.com

Service設定とポート解放

Prometheusのサービスを公開します。今回はNodePortモードでノードの30001番ポートを解放しています。つまり、http://<Prometheus 稼働中 Node IP>:30001 にアクセスするとPrometheus ポッドの9090番ポートに接続されるので、Prometheusにアクセスするためには30001番のポートをGCPのネットワーク設定で解放しておく必要があります。GCPの管理コンソールのVPC ネットワーク > ファイアウォールルールからポートを解放してください。ちなみに、本記事では手間を省くためにNodePortモードを利用しておりますが、Production環境等ではInternalのLoadBalancerを利用した方がノードに依存することがなくなるため運用しやすいと思います。

最後に、Prometheusにアクセスしてみて下記のようにサービスディスカバリができていれば完了です! f:id:tanan55:20180305183938p:plain 結構難しいですよね。。。弊社の本番環境でもPrometheusを利用していますが、正直全てのメトリクスを把握できないほどの種類を取得しています。それだけ細かく取得できているのは素晴らしいのですが、どうやって可視化するのか迷いますよね。次回はそんな方にオススメのGrafana周りを紹介しますのでご興味のある方は楽しみにしていてください。

お知らせ

SREチームでは「No Challenge, No SRE, No SPEEDA」を掲げ、ユーザベースグループのミッションである「経済情報で、世界をかえる」の実現に向けて、日々業務に取り組んでいます。 興味を持ってくださった方はこちらをご確認ください。

また、2018/03/15(木)にハートビーツ社主催で「SRE大全:ユーザベース編」 が開催されます。Youtube Liveでも配信されますのでご興味ある方はぜひご覧ください。

ユーザベースで社内ハッカソンを初開催しました

f:id:uzabase:20180217001643p:plain

はじめに

ryoqunこと小野寺です。突然ですが、うだるような熱狂的なハック、最近してますか?僕らのそんな刺激的で情熱的な一夜限りの思い出を今日はレポートしたいと思います。

ユーザベースでは2017年12月15日に社内ハッカソンを開催しました。初開催にも関わらずとても楽しかったので、その取り組みについて紹介したいと思います。

今回の社内ハッカソンがユーザベースでの栄えある1回目の開催です。大好評に終わり、こういったエンジニアの社内イベントは継続的に開催していくことになりました。

聞くだけに終わらない「Tech Meeting」にするために

「エンジニアみんなが等しく能動的に技術に向き合う時間にしたい」

この思いを実現したいがために、板倉というエンジニアから今回のハッカソンは立案され、開催されました。その根底には、週次のエンジニア全体ミーティング(いわゆるTech Meeting)の本来の意義を、180度違う角度から解決したいという熱意が板倉にあったためでした。

これまでの弊社のTech Meetingは持ち回りで担当者が何らかのテーマで発表をするというものです。

そこには、「話す」、「聞く」という参加者の二分があり、「聞く」側の中ではさらに、「質問する」、「聞いているだけ」というさらなる二分があり、 Tech Meetingに参加しているエンジニア各人の時間の濃さにムラがあるのが課題でした。ユーザベースグループという組織において、それは望むところではなく、そこに強い問題意識を持ったのが彼でした。

弊社におけるTech Meetingの意義とは、「技術」を軸にエンジニア同士が交流すること。だからこそ、あえてTech 「Meeting」でなくてもよいのでは?その問題提起から、四半期分のTech Meetingの時間をまとめ、1日まるまる時間を確保し社内ハッカソンを開催することになりました。

ちなみにそのような大胆な改革であっても、(あるいは、ならばこそ)すんなり挑戦よいとなりました。というのも、ユーザベースの7つのルールに「自由主義で行こう」や「迷ったら挑戦」とあり、基本的に社員のWillが尊重されるからです。

そして同じような問題意識に共感し、ハッカソンを運営したいと手を上げたメンバーを加え、板倉を中心に数名の運営チームが組成されました。

自由と挑戦を念頭に、ユーザベースらしいハッカソンを。

まず、ハッカソンのお題はありません。つまり開発テーマは完全に自由です。作るものの制限はなく何でもOKでした。この背景には、できるだけレギュレーションとして制約を設けずに運営側の思いとして個々人が好きな技術に能動的に触れてほしいというのがあったためです。

結果、業務に関係あるものから、関係無いものまで、具体的にはゲームからチャットボットに至るまで、実に色々なものが作られました。

さらに、チーム分けはランダムに極力「混ざる」ようになりました。ユーザベースが大きくなるのにつれ事業部間のエンジニアの距離が遠のいてしまうのを解消したいという運営チームの思いから、有志がなんとなくいつも通りにまとまるのではなく、基本的には運営チームによって決めました。ただ、参加者の志向性をまったく考慮しないわけではなく、興味ある技術や趣味を参考とするためにアンケートを取りました。

そして、当然のごとく、最終発表後には表彰と賞品の贈呈「アリ」とのことでみんなは俄然やる気がでます。

ハッカソンの日程は、告知から打ち上げまで、大きくは次のように進んでいきました。

f:id:uzabase:20180217001028p:plain

  • 10/2: ハッカソン開催の発表
  • 10/18: ハッカソンのチーム分け発表
  • 10/30: チーム別中間発表(チームごとの取り組む開発内容の発表)
  • 12/14: ハッカソン開催宣言(18:00〜)
  • 12/15: チーム別最終発表&表彰&打ち上げ(17:00〜)

では、時系列順に足早になりますが、写真を織り交ぜつつ説明していきたいと思います!

10/2: まさかのハッカソン開催の告知

f:id:ryoqun:20180131212300p:plain

今日も代わり映えの無いTech Meetingの発表の最後に突如出たこの素っ気ないスライド一枚から全ては始まりました。

まず、突然に、4QではTech Meetingの代わりにハッカソンをするという発表がありました。当然、発表直後はざわつきました。

ともかくも、Tech Meetingの時間枠を集めてハッカソンにしてしまうという発想が非常に新鮮で、みんなの不安と期待と野望(?)の中で、ここから物事は動き出しました。

10/18: どきどきのチーム発表!

f:id:ryoqun:20180131094856j:plain

まずはともかく、ハッカソンをやるにはチーム分けから始まります。2〜6人のチームが合計が15チームができ、ユーザベースグループのエンジニアが総動員した結果、55名とかなりの大規模です!

前述の通り、チームの構成はなるべく事業をまたぐよう意識されました。この工夫にはエンジニアの交流を増やしたい狙いがありました。ただ、個人の希望も考慮するため、ハッカソン告知後に興味のある技術や趣味のアンケートは参考のために事前に実施されました。そして、運営メンバーも各チームのメンバーとして実際のハッカソンに参加しました。これもまた運営チームの思いの現れです。つまりは、Tech Meetingでの「聞く側」と「話す側」の二分構造と同じような「運営側」と「参加側」の二分構造の発生を避けるためでした。

チーム分け後、各チームのメンバーは基本的には初対面です。自己紹介したり、キックオフランチに行くチームもありつつ、各チームは早急に何を作るのかを中間発表に向けて決めなければなりません。もちろんチームの各メンバーの持ち味を活かしつつ!

10/30: 夢が膨らむチーム別中間発表!

チーム発表後、お互いのチームが何を作るかの噂が流れたりして、そわそわしつつ、ついにこの日に各チームが発表しお互いが何を作るのかが明らかになりました。

前述の通り、お題は完全自由で実に多彩な案が出ました。Slackのボットから簡単なWebサービス、はたまたゲーム、音声認識、IoTなどなど、やはり最新技術を取り入れたチームが大半です。

どのチームの企画もチームの特色があり、ひねったアイディアばかりで新規性や革新性があり、発表内容を聞いているだけでワクワクしました。

そして、この中間発表以降、本気で賞を取りにいこうとしたチームは先行して開発に着手し始めました。

12/14: 前夜祭的な開催宣言!

f:id:ryoqun:20180131134910j:plain

準備したチームがありつつも、ついにハッカソンの開催が宣言されました!

f:id:ryoqun:20180131213222j:plain

ちなみにこんな感じでお祭り感UP!ということで趣ある方法で成果発表の順番は決められました。

がっつりハック!各チームの開発風景

f:id:ryoqun:20180131213846j:plainf:id:ryoqun:20180131212926j:plainf:id:ryoqun:20180131213111j:plain

みんなが楽しそうに各チームが1つの目標の元で開発しています。最新技術や、技術を使っての誰かために問題解決が好きなんだなと思えた瞬間でした。

ぎりぎりまで粘った上での成果発表!

f:id:ryoqun:20180131213659j:plain f:id:ryoqun:20180131214332j:plain

ハッカソンなので完璧さは求められません。大事なのは、とりあえず動くものを作ること。その心意気で、みんな発表開始直前の直前まで開発していましたが、時間は無情で成果発表の時間となりました。

今回のハッカソンのテーマは「楽しく」なので、最後まで追い込んでまで必死に開発したご褒美というわけで、ビール片手の乾杯から成果発表はスタートしました。

各チームの発表持ち時間は5分で、うまく動いて歓声があがったり、ツッコミが入ったりしながらもテンポよく和気あいあいと進んでいきます。やはりデモを披露するチームが多かったです。

そして、成果発表後、交流の時間が設けられ、気になるチームのところに行って話したり、デモを試したり、逆に興味を持ってくれたエンジニアにデモを見せたりしました。

そして、各賞にふさしいと思うチームへの投票も終え、ついに、どきどきの表彰タイムです!果たして自分のチームは選ばれたのでしょうか??

表彰!

泣いても笑っても結果が全て。以下の通りで各チームがそれぞれ受賞しました!!

今回の賞は合計4つでした。エンジニアみんなが投票して選ぶ「Good Idea賞」、「Tech賞」、「最優秀賞」の3つと、サプライズでユーザベースグループのCTOの竹内さんからの「特別賞」がありました。

「Good Idea賞」

f:id:ryoqun:20180131135706j:plain

まず、「Good Idea賞」に輝いたのは、「Slaxフレンズ制作員会」というチームで、Slackの中でUnixコマンドの思想に則ったコマンドライン環境を提供するという一風変わったBotを作りました。ちょっと補足すると例えばSlack上でtail -n100 @ryoqun | grep "ハッカソン"と入力すると、ある特定の人の直近100件の発言から特定ワードで絞り込んで表示するというBotです。

「Tech賞」

f:id:ryoqun:20180131140214j:plain

次に、「Tech賞」に輝いたのは、「チームPon!」というチームで、ARと体を動かすというのを組み合わせたホッケーのようなゲームを作っていました。対戦もでき、スマホがコントローラー代わりになり、ブラウザからゲーム状況も見えたりとARKitを使った本格的なARと同時にゲームとしての完成度も十分でした。

「最優秀賞」

f:id:uzabase:20180207211449j:plain

では、栄えある「最優秀賞」に輝いたのは、「もんめ」というチームで、社内ポータルをリニューアルさせました。なんといっても開発成果のインパクトが一番でした。ユーザベースでは社内ポータルとしてCrowiを使い始めているのですが、それをフロントエンドを中心にデザイン含め、大規模にリニューアルしました。Reactを使って書き換え、オープンソースも予定しているとのことで期待も高まります。

「CTO特別賞」

最後に、サプライズだった「CTO特別賞」に輝いたのは2チームでした。

1チーム目は、「私立恵比寿中学水樹奈々がかり」というチームで、Google Homeで、アイドルのライブイベント当日の移動等の準備に便利な音声操作のツールを作りました。

2チーム目は、「UBHome」というチームで、Raspberry Piと各種音声認識や発声APIを使ったIoT的な音声受付システムを作りました。

無事に終わり、金曜夜、あとはやることといったら…

そして、成果発表開始時よりアルコール解禁になっていたのもあり、ウォーミングアップ(?)も済ませ、睡眠不足のテンションで意気揚々に、有志で打ち上げへと恵比寿の居酒屋に繰り出していくのでした〜。

「(ハッカソンを通して)仲良くなれた。楽しかった」

開催後のアンケートから抜粋すると以下のようなものがありました。

「運営お疲れさまでした!最後の発表会で予想を遥かに越えて、けっこうみんなが真面目に取り組み、良いものができたと思います!」

「普段業務で会話することがない人とも、短期間ではあったものの1つの目標に向かって協力できたことで仲良くなれたな、と思いました。」

「久しぶりにコーディングに集中しすぎて発表の時に疲れましたがめっちゃくちゃ楽しかった!!」

ありきたりかもですが、こういう反応こそを引き出せたのは、その当たり前を多くの苦労で実現した運営チームの尽力あってのことだと思います。

「みんなが楽しめたならOK!(運営は大変だった…)

開催後日、最後に板倉より以下のメッセージがありました。

「告知時に話をしましたが、今回のハッカソンはみんなが能動的に参加できるように考えたものです。撮影した写真の中では、ハッカソン当日はみなさん笑顔が多く、良い時間が過ごせたのではないかと思います。上記の目的が少しは達成できていたのであれば良かったです!」

という形で初開催のハッカソンは無事に幕を閉じました。

また、最初から完璧な運営できたわけではなく、反省として、「もっとお祭り感を運営チーム働きかけて作りだせたのではないか」、「チーム間の取り組みへの温度感のムラをもっと埋められないか」、「受賞したいと思えるような賞品にできたのでないか」などがありました。

まとめ

ユーザベースでは、「経済情報で世界をかえる」というミッションの実現のため、エンジニアが共に力を合わせ自由闊達で働ける環境を作ろうとしています。

ユーザベースはグループとして、ただそのミッションために存在し、そのミッションで束ねられた組織の団結力は非常に重要であると考えています。そのためにも、エンジニアという1つの職能という横串の切り口で、今回のようなレクレーションイベントを通し、結束力を高められたのは本当によかったです。

さらに朗報で、今回の社内エンジニアイベントに続き、次は社内ISUCONを開催しようということが決定しています。

最後になりますが、ユーザベースでは、絶賛エンジニアを大募集中なので興味ある方は是非とも応募してください!

【k8s合宿】 Kubernetesのログ分析環境を作る

こんにちは、SPEEDAのSREチームでエンジニアをしている阿南です。SPEEDAのSREチームでは、昨年末kubernetesについて理解を深めるために合宿を行いました。やり方はA〜Cの3チームに分けて、それぞれのチームでkubernetesに関することを調査、構築するという形式で、今回はAチームが実際にやってみた内容についてブログを書きたいと思います。(それぞれのチームでかなりボリュームがあるので、複数回に渡って連載的な形でお届けしたいと思います。) Aチームでは、kubernetesを本番環境に投入するにあたり、ログ収集周りをあまり調査できてないなと感じ、GCP上に環境を作ってみることにしました。

構築する環境

f:id:uzabase:20180115151549p:plain

GKEでKubernetesクラスターを構築し、その上にwordpress(Apache) + MySQLコンテナを稼働させました。ログ収集と言っても、kubernetesクラスター自体のログとその上で稼働するコンテナのログでは取得する設定も若干違ってきますので、今回はコンテナのログを収集する環境を作りました。 またポイントとして、GKEの公式DocumentではStackdriverにログを送ってBigQueryにエクスポートするという構成が紹介されているのですが、BigQueryに直接送る構成はあまり情報がなく、SPEEDAのコアな部分はオンプレ環境で運用しているため、BigQueryに直接送る構成にしました。

構築手順

ログを収集するという単純な環境ですが、意外と設定項目が多いです。手順を一つずつまとめるとかなり分量があるため、設定内容も含めてgithubに手順をまとめました。

github.com

1から構築してみたいという方はぜひご参考にしてください。 本ブログ内では、私が重要だと思った点や注意点のみ記載したいと思います。

クラスター構築

GKEは下記コマンドのみでkubernetesのクラスターを構築することができます。

$ gcloud container clusters create sample-cluster

デフォルトではGCEのインスタンスが3台起動し、そのVMを利用してkubernetesのclusterが作成されます。

wordpress + MySQL構築

続いて、wordpress + MySQL です。こちらについては、GCPの公式ページのステップ2〜5を参考にしました。wordpressのコンテンツファイルやDBのデータを永続化するために、 volumes で外部ディスクを指定し、containersvolumeMounts でマウント設定しています。

gist.github.com

Fluentdイメージの作成

利用するイメージは、GCPのリポジトリを参考にイメージを作成すればOKです。ポイントとして今回はbigqueryに直接ログをアップロードするため、gemfileで、fluentdのバージョンをv0.14に変更し、fluent-plugin-bigqueryを追記しています。このgemfileを保存して、docker buildしてください。 fluentd.conf の設定は全てConfigMapで記載します。

gist.github.com

ConfigMap設定

ConfigMapは、設定情報(環境変数やファイル)を定義できるkubernetesの機能です。これを使うと、kubernetes上でコンテナを実行した際に、ConfigMapに設定した情報を読み取ってくれます。例えば、fluentdのイメージをDocker単体で実行した場合、configをホストマウントしたりしますよね。kubernetesだとホストが頻繁に変わる可能性もあるのでホストマウントするわけにもいきません。そのような場合に、ConfigMapを利用すればconfigをimageに含めなくてもよくなり、ノードが変更した時にも追従できるということでクラスターのノードを捨てやすくなると思います。

gist.github.com

実際のfluentdの設定についてはファイルを確認して頂ければと思いますが、注意点としてfluent-plugin-bigqueryの現在のバージョンでは inject セクションが追加されているようです。過去のブログ記事で設定にinjectがないものもあり、そのまま設定するとBigQueryのtimeだけnullになってしまうことになりますのでご注意を。

<inject>
    time_key "time"
    time_format "%s"
</inject>

DaemonSet設定

DaemonSetはクラスターの各ノードにコンテナをデーモンで動作させることができるkubernetesの機能です。コンテナのログは各ノードに出力されるようになっています。このDaemonSetを利用してfluentdを各ノードに稼働させ、各ノードのログを収集します。

gist.github.com

長いので、かなり省略していますが、 volumes で先ほど設定した configMap を指定し、 containersvolumeMounts に設定しています。これで、ConfigMapで定義したファイルをfluentdが起動時に読み込み、ログ収集ができるようになります。

f:id:uzabase:20180115173310p:plain

ちなみに、下記コマンドでwordpressのpodと同じノードで稼働しているfluentdのpodを確認し、shellを起動するとfluentdのログを見ることができます。うまくfluentdのログがアップロードできない等の場合は見てみるといいかもしれません。

$ kubectl get pods -o wide
NAME                         READY     STATUS    RESTARTS   AGE       IP           NODE
fluentd-gcp-v2.0-5t96f       1/1       Running   0          5h        10.52.2.9    gke-sample-cluster-default-pool-a7431f33-rqbs
fluentd-gcp-v2.0-j45c7       1/1       Running   0          13m       10.52.0.13   gke-sample-cluster-default-pool-a7431f33-4x57
fluentd-gcp-v2.0-wmv3h       1/1       Running   0          5h        10.52.1.10   gke-sample-cluster-default-pool-a7431f33-nv5h
mysql-3368603707-ng8pr       1/1       Running   0          6h        10.52.0.7    gke-sample-cluster-default-pool-a7431f33-4x57
wordpress-3479901767-tc9p6   1/1       Running   0          6h        10.52.0.8    gke-sample-cluster-default-pool-a7431f33-4x57

$ kubectl exec -it fluentd-gcp-v2.0-j45c7 /bin/sh
# tail -f /var/log/fluentd.log

最終的に、下記のようにアップロードされました。現状だと全てのコンテナログが同じテーブルにinsertされてしまいますので、この辺りは別途テーブルやfluentdを細かく設計した方が良さそうです。

f:id:uzabase:20180118112409p:plain

まとめ

ログをアップロードするために必要な設定を見ていきました。使ってる機能が意外と多いので、少し時間がかかりましたが、本番環境で運用するにはマストの機能ばかりかと思います。また、fluentdから直接BigQueryに送ることにより、マルチクラウドでも対応できるし、OutputをBigQueryから他サービスに変更(もしくは追加)することも簡単にできますので、シーンに応じて使い分けできると思います。結構つまづいたのがfluentdで、jsonログをparseし、特定のキーに対してさらに複雑な加工をしたい場合にちょうどいいプラグインが見つからなかったのでその辺りは調査、もしくは、自分でプラグインを作るのもありかなと思います。 次回以降で、データの可視化やkubernetesのメトリクス収集等も紹介していきますのでこちらも乞うご期待。

お知らせ

SREチームでは「No Challenge, No SRE, No SPEEDA」を掲げ、ユーザベースグループのミッションである「経済情報で、世界をかえる」の実現に向けて、日々業務に取り組んでいます。 興味を持ってくださった方はこちらをご確認ください。

クローズドな勉強会 SRE Lounge始動!

はじめまして、SPEEDA SREチームの久保です。

今回当社のSREチームとハートビーツ社が共同で、1/17(水)に他社を巻き込んで、SRE LoungeというクローズドなSRE勉強会を開催したので、シェアしたいと思います。

SREとは?

Site Reliability Engineeringの略で、日本語に訳すと、「サイト信頼性エンジニアリング」です。 簡単に説明しますと、IT Proの記事にありますように、

SREとはコーディングやソフトウエアエンジニアリングによって、ハードウエアを含めたシステム全体の性能や可用性、セキュリティを高める活動全般を指す方法論

を指します。

ユーザベースのSPEEDA SREチームとしては、
従来のようなDevとOpsに役割やプロセスが分かれており、それぞれのエンジニアが各々の役割だけにコミットする状態ではなく、ソフトウェアの設計、開発・構築、デプロイ、運用、改善といったソフトウェアの一連のライフサイクルのすべて(DevとOps全体)に焦点を当て、サイトの信頼性の向上にコミットするために、ソフトウェアエンジニアリングによってプロダクトの改善を行う。

と解釈し、プロダクト全体の改善に日々努めています。

SRE Loungeの開催背景・趣旨

O'ReillyのSRE本にもありますようにSREはGoogleのDevOpsであり、必ずしも各社にとっての最適解ではあるとは限りません。

また、SREチームを持つ企業各社によって、SREとしての取り組みも様々なのが現状です。 SREチームを構成するメンバーのエンジニアとしてのバックグラウンドも会社によって異なります。 ユーザベースのSPEEDA SREチームでは、ソフトウェアエンジニアとインフラエンジニアが1:1ですが、その比率は企業によって様々です。

そのため、既に各地で開催されているような一方向の講座形式の勉強会ではなく、 双方向に取り組みのシェアや課題の共有などができる、双方向のインタラクティブな場が必要と考え、今回SRE Loungeという名前で企画しました。

SRE Loungeのコアコンピタンス

  • 1回の勉強会で複数社のSRE取り組み事例を知ることができる
  • 少人数のクローズドな場にすることで、双方向なコミュニケーションが取りやすいこと(=質問がしやすく、参加者-発表者間で議論ができる場)

勉強会ゴール

  • SRE取り組み事例の共有(情報交換, 発信)
  • SREについて議論し、知見を深める

開催日時

1/17(水)19:00〜で、 ハートビーツ様のラウンジをお借りして開催しました。

ハートビーツ様の会場はとても素敵な場所でした。代表の藤崎さんのこだわりで作られたとのことです。羨ましい! f:id:hir023:20180122104731j:plain f:id:hir023:20180121203821j:plain

参加企業

コンテンツ概要

  1. 各社SRE関連の取り組み事例紹介&質疑応答 (各社20~25分)
  2. 意見交換会、ディスカッション(with ビール・ピザ) (30~40分)

コンテンツ

各社それぞれのSREの取り組み事例を知ることができ、非常に濃い時間でした。 簡単に各社の発表内容をシェアしたいと思います。

ハートビーツ社

  • happoというツールを独自で作成し、自動でNagiosのメトリクスを収集し、かつ自動でメトリクス設定するツールなども開発して監視している。
     参考:heartbeats.jp
  • トイルの削減方法としては、nagiosのアラートをslackに通知し、スレッド単位で対応している。

当日発表資料:https://speakerdeck.com/abnoumaru/sre-lounge-20180117



dely社

  • 障害対応訓練(dely Apollo Program)という、過去に発生した障害と同じ状況を生み出し、インフラエンジニア以外のエンジニアにその復旧作業に取り組んでもらう。復旧作業での作業をすべてメモし、なぜその作業を行ったかをヒアリングし、振り返るプログラムを実施している。

当日発表資料:https://www.slideshare.net/motonobufukao/dely-sre-principles



eureka社

  • AWS環境で、Packer × Ansible × Terraformで、プロビジョニング用のAMIを作成し、そのAMIベースでインスタンスを作成。一度起動したインスタンスには変更を加えない構成にしている。

当日発表資料:https://speakerdeck.com/takuya542/ji-sok-de-nacui-ruo-xing-jian-zhi-topatutimanezimentoshou-fa-falseshao-jie-1



ユーザベース

  • プロジェクトチーム編成方法や開発手法(TDD、DDD、クリーンアーキテクチャなど)を取り入れて、手動運用のオペレーションの自動化など、 SRE内でのソフトウェア開発を行っている事例を紹介。

当日発表資料:https://speakerdeck.com/tkitsunai/software-development-in-uzabase-sre




このような形で、各社発表後のビールを片手にピザを食べながら行ったディスカッションでは、当初21:30で解散の予定が23:00まで続くという盛り上がりを見せ、参加された方の満足度が非常に高い勉強会となりました。

今後のSRE Lounge

今後のSRE Loungeとしては下記のように考えています。

  • SREをテーマで、所属関係なく、横のつながりを生み出すコミュニティを作っていく(SREコミュニティ化)
  • どこか1社が主導してSRE Loungeを企画するというよりも、SREコミュニティ内の各社が主体となって、自律的に分散的に企画されるような形を目指す
  • そして様々な会社のオフィスで開催する

もし興味持ってくださり、参加希望の企業はこちらまでご連絡ください。

最後に

第2回開催の話も挙がりましたので、ぜひ今後も継続して開催していきたいと思います。

f:id:hir023:20180121203940j:plain
集合写真
f:id:hir023:20180121203932j:plain
今回参加したユーザベース SREチームメンバー




仲間募集!!

ユーザベースのSPEEDA SREチームは、No Challenge, No SRE, No SPEEDA を掲げて業務に取り組んでいます。
「挑戦しなければ、SREではないし、SREがなければ、SPEEDAもない」という意識で、日々ユーザベースのMissionである、「経済情報で、世界をかえる」の実現に向けて邁進しています。

少しでも興味を持ってくださった方はこちらまで

SREチームの2018年度を占う

初めまして、株式会社ユーザベースのSPEEDA Japan Company、Site Reliability Engineering (SRE) Teamでエンジニアをしています、川口・阿南です。

SREチームについて

私たち、SREチームは2017年7月に始動しましたが、どのような業務をしているのかをあまり発信できていませんでしたので、少しご説明します。
端的に言うと、以下2点が主な業務になります。

  • 企業・業界情報プラットフォーム「SPEEDA」のインフラ構築、ソフトウェア改善
  • ユーザベース全体の社内インフラの構築、運用

前者については「SREといえばこれ!」という業務ですが、後者の社内インフラについては、SREと別チームになっている会社も多いのではないでしょうか。
弊社では、社内ネットワークの構築、社内メンバーのPCセットアップ、ヘルプデスク等全てSREチームが担当しており、サービスだけでなく社内インフラの信頼性も向上しようと、日々業務に取り組んでいます。

SRE合宿

2018年度(2018年1月~12月)にSREチームが向かうべき方向を合わせるための合宿を行いましたので、ブログ記事として書き残したいと思います。今回の合宿のテーマは以下の2つです。

1日目:2018年度のSREチーム施策決定
2日目:Kubernetesのスキルを上げる

1日目は、2018年度に取り組むテーマについての議論を行いました。SREチームでは四半期ごとにこのような合宿を行っていますが、前回の合宿では予定より大幅に時間がかかってしまったという反省があります。そこで、今回は以下のような取り組みを行いました。

  • 2017年度の振り返りは事前に実施
  • チーム外からファシリテータを立てて、タイムマネジメントを依頼
  • 合宿運営委員で、スケジュールを詳しく検証

合宿会場は、駒場東大前のレンタルスペースです。前回のSRE合宿でも利用させていただきましたが、古民家風の大変おしゃれなスペースで、皆とても気に入っています。

f:id:uzabase:20180115230003j:plain

チームテーマ議論

合宿当日。集合時間の午前9時半には続々メンバーが集まり、いよいよSRE合宿がスタート。まずはチームリーダーの羽山より、2017年度のSPEEDAサービス状況の報告や、来年度のSPEEDA事業の体制等について説明がありました。 ここからバトンをファシリテータの田中氏に渡し、あらためて我々のミッションや今年度のおさらいなどをしてから、いよいよ2018年度のチームテーマについての議論に入ります。各自で付箋に「これだ!」と思うテーマを記し、それをホワイトボードに貼り付けていきます。

f:id:uzabase:20180115233331j:plain

約30枚のテーマから絞り込んでいった結果、来年度のチームテーマは

「Production Ready k8s」
「Data Driven Automation」

に決まりました。 一つ目の「Production Ready k8s」は、「Kubernetes」というコンテナ管理システムを本番サービスに本格導入するという意味です。「Kubernetes」について、詳しくは合宿2日目のレポートをご参照ください。二つ目の「Data Driven Automation」はチーム内で話して作ったテーマで、システムの自動化をただやみくもに行うのではなく、時間がかかっているオペレーションや、障害になりかねないような複雑なオペレーションなど、数値化したデータを元に自動化を進めていこうという意味が込められています。

プロジェクト選別

その後は、各メンバーから事前に提出されたプロジェクト案について、重要度や上記テーマとの一致度を精査し、SREチームとして我々が取り組みたいプロジェクトに絞っていきました。ここで、その一部を紹介します。

オペレーションタスクの自動化・システム化

会社やサービスの拡大に伴いオペレーションタスクが増え続ける中、今後アップサイドの改善を行っていくためには、手動で行っているオペレーションにかかる時間を減らし、ソフトウェア開発・導入に注力する必要があります。これまでも取り組んできましたが、2018年度に自動化プロジェクトを実施し、手動オペレーションの自動化・システム化を集中して行いたいと考えています。

ログ分析 / モニタリング基盤の強化

SREチームとして改善を行うために、ログを分析してボトルネックやエラーの原因を調査したり、サービスの稼働状況をモニタリングすることは非常に重要です。しかしながら、ログを可視化してもうまく活用できていなかったり、そもそも何のためにログを収集しているのか明確でない、といったことは意外と多いのではないでしょうか。弊社でも、現状ではビジュアライズ出来ていないログがあったり、Kubernetesを本格的に利用していくにあたってますますログやメトリクスの可視化が重要になりますので、今後の重点テーマとしたいと思っています。

アラート発生時のエスカレーション自動化

サーバやネットワーク等の障害発生時、アラートを早くかつ確実に検知し、速やかに対応に入ることが重要です。その強化策として、自動エスカレーションシステムの導入を行います。

なお、プロジェクト選別の途中でランチ休憩を挟みましたが、前回はお店を探すだけで結構時間がかかってしまったので、今回は事前にお弁当を注文して会場まで配達していただきました。意外とおしゃれな弁当で、合宿会場に合った感じで良かったと思います。

f:id:uzabase:20180115231015j:plain

OKR策定

後半では、2018年度1Q(第1四半期)のOKR策定を行いました。OKRとは「Objectives and Key Results」の略で、業務の目標と成果を管理する手法です。当社のSPEEDA国内事業では2016年度からOKRについての取り組みを開始し、現在ではOKRベースでの活動が当たり前になっています。

まずは、前半で決めた2018年度テーマの中から、1Q・2Qで取り組むプロジェクトを選びます。選び方はボトムアップで、各自がコミットしたいプロジェクトを1つずつ出してもらい、その希望を元にプロジェクトごとのオーナーを決めて、メンバーアサインを調整していきました。

前述の通り、SREチームでは社内全体や「SPEEDA」のインフラ構築・運用も担当していますので、自分たちの希望するプロジェクトだけでなく、会社やSPEEDA事業全体に関わる業務も実施する必要があります。2018年には弊社オフィスの移転も計画されており、SREチームとしても様々な作業が必要になるため、オフィス移転自体も必須プロジェクトの一つとして上がりました。

f:id:uzabase:20180115232130j:plain

このようにして決定したプロジェクトごとに集まって、1QのOKRを策定しました。具体的には、プロジェクトの目標である「Objective」と計測可能な達成内容である「Key Results」を決めます。これがプロジェクトの活動のベースであり、進捗評価基準となるものですので、どのプロジェクトも真剣に議論していました。

最後に、各プロジェクトのOKRを発表して、合宿の1日目はお開きとなりました。

合宿1日目を終えて

この記事の最初の方で記述したような施策を常に意識した結果、またファシリテータの田中氏の手腕も大きかったと思われますが、非常にスムーズにスケジュールを進めることが出来ました。今回の合宿同様、2018年度の各プロジェクトも順調に進みそうです。

2日目の「Kubernetes合宿」については、各グループごとの取り組み内容をブログ記事として紹介したいと思いますので、どうぞご期待ください。

お知らせ

SREチームでは「No Challenge, No SRE, No SPEEDA」を掲げ、ユーザベースグループのミッションである「経済情報で、世界をかえる」の実現に向けて、日々業務に取り組んでいます。 興味を持ってくださった方はこちらをご確認ください。

Chrome hackingと称しブラウザのレイアウトバグをみんなで調べてみました

はじめまして。プロダクト開発チームの小野寺 (ryoqun)です。

Google Chrome (以下、Chrome)にて、HTMLのレンダリングの回帰バグが紛れ込み、その影響でSPEEDAの一部分のレイアウトが崩れてしまう問題が発生しました。そこで、「Chrome hacking」と称し、数名の希望者を社内で募り、みんなでこのバグを調査、あわよくば解決しChromeのコミットログに@uzabase.comのドメインを刻もうと奮い立ちました。

しかし結論として、別の案件が入り、作業を中断している間に先を越され、名を刻むことはできませんでした。つまりは現在このバグは別の開発者によって修正が完了しています。しかし、結果的にはOSSのソースコードレベルでの調査の実例としては非常に好例となりました。

その活動記録として、SPEEDA上での問題の発覚からChromeのバグであるという原因の特定や調査から収束に至るまでの一連の出来事を共有したいと思います。

前提として、本当にChromeのレイアウトバグでした

最初はChromeのバグだと断言できませんでした。

そのため、本当にChromeがCSS 2.1のレイアウト回帰バグを混入させてしまったことが原因だと分かった時は驚きでした。

当然として、SPEEDAでのレイアウトバグの発覚直後は、SPEEDAのCSSの問題だと考えていました。 というのもCSSは呆れるくらいに枯れたバグの入る余地のないWebの基礎技術だからです。

CSS 2.1として2011年6月にW3C Recommendationとなり、それから5年以上が経過しています。それまでの歩みは決して容易いものではなかったため、CSSは鉄壁の仕様となっています(1990年代の血みどろのブラウザ競争の中で産み落とされたCSS 1.0がのたうちまわり、当時のWebエンジニアたちをInteroperatabilityの名の下苦悩させ、戦禍の反省とでもいうかのごとく「複数レンダリングエンジン上で実装済み」という大義名分の元、W3CによってひねりだされたCSS 2.0が2000年代を通し、これでもかというくらいに精緻に策定され、晴れてCSS 2.1は生み出されました)。

培われた仕様の厳密性、テストケースの網羅性はもはや芸術レベルで、1つの仕様に対しての手厚さとしては数有る仕様の中でもトップクラスにCSSは位置すると思います(特に個人的には9 Visual formatting model10 Visual formatting model detailsあたりは傑作だと思います)。

ということで、2017年の今日において、枯れたCSSに対し、IE 6やガラケーと戯れて涙を飲みながらレイアウトバグの回避を模索していた苦悩の2000年代を彷彿とさせる事象に再び直面し、非常に印象的でした。 どんなに枯れていようが常にソフトウェアにはバグがつきものであり、バグに直面した時、時には自分たちのコードだけでなくミドルウェアも疑う必要性を痛感しました。 また、CSSを正しく実装することがいかに難しいことであるかの証左なのかもしれません。

バグの発覚と内容

今回のバグは、SPEEDAの本番環境にて、デスクトップ向けのChromeのStableチャンネルに59が出始めてからようやく気づきました。

バグの内容は、サイト検索フォームの下に表示されるサジェスト候補の一覧が異様に高くなってしまうというものでした。SPEEDAは一般公開されているサービスではないので見せられるスクリーンショットがかなり限定的でわかりにくいのですが、正しいレンダリング時の画像はこのようになります:

f:id:ryoqun:20170901011237p:plain

逆に、正しくないレンダリング時の画像は、このようにかなり縦長な感じになってしまいます:

f:id:ryoqun:20170901011251p:plain

これだけだとイメージがつきにくいのですが、サジェスト候補が画面表示領域に対してかなりの占有率になってしまい、ユーザーにも違和感を与えるレベルになってしまいました。

とりあえず応急処置

他のブラウザや以前のバージョンのChromeでは問題が起きなかったことから、どう考えてもChromeのバグらしいというのが判明してきました。そうなってくるとChromeはすぐには修正されないのでまずは応急処置です。 レンダリングエンジンがどう動いているかを想像しつつ、クロスブラウザで無害で等価なCSSを色々と試行錯誤した結果、結局は以下の変更だけで直ってしまいました。

 .g-search-suggest li .suggestItem {
-  display: block;
+  display: inline-block;
   padding-left: 90px;
   color: #555;

CSS的にはほとんど等価なはずなので、やはりどう考えてもChromeのバグのようでした(ちなみに、こういうレイアウトバグの回避策なんてものは、すっかり失われし技術となってしまいました)。

この応急処置をSPEEDAに反映し、次にChromeを直そうということになりました。

ミニマルテストケースの作成

ともかくも最初はミニマルテストケースを作りました。そうすることによって社内に公開しても大丈夫でGoogleにもバグレポートを送れるようになります。 作ったミニマルテストケースは↓の通りです。

<!DOCTYPE html>
<html>
<head>
<style>
.suggestItemOk1 {
  display: inline;
}
.suggestItemOk2 {
  display: inline-block;
}
.suggestItemNg {
  display: block;
}
.item {
  overflow: hidden;
  display: inline-block;
}
</style>
</head>
<body>
<ul>
<li><span class="suggestItemOk1"><span class="item">AAA</span></span></li>
<li><span class="suggestItemOk1"><span class="item">BBB</span></span></li>
<li><span class="suggestItemOk1"><span class="item">CCC</span></span></li>
<li><span class="suggestItemOk2"><span class="item">AAA</span></span></li>
<li><span class="suggestItemOk2"><span class="item">BBB</span></span></li>
<li><span class="suggestItemOk2"><span class="item">CCC</span></span></li>
<li><span class="suggestItemNg"><span class="item">AAA</span></span></li>
<li><span class="suggestItemNg"><span class="item">BBB</span></span></li>
<li><span class="suggestItemNg"><span class="item">CCC</span></span></li>
</ul>
</body>
</html>

ミニマルテストケースの正しいレンダリング時の画像はこうなります:

f:id:ryoqun:20170901013353p:plain

逆に、正しくないレンダリング時の画像はこうなります:

f:id:ryoqun:20170901013155p:plain

SPEEDA上でのレイアウトバグとなんとなく似ているのは想像できるかと思います。

このミニマルテストケースから分かることは、display: list-itemoverflow: hidden,display: blockが組み合わさるとどうやらまずいということです。その情報を元にChromiumのバグを検索してみましたが、同様のバグが見当たらなかったため、自分たちで直してみようということになりました(ちなみに、今現在はこのキーワードで検索すると、今回の回帰バグのレポートを見つけることができます)。

Chromiumのビルド

Chromeは、オープンソースであるChromiumから作られています。そこでオープンソースの真価を発揮ということで、手元のマシンでビルドしてみました。Chromiumは相当な数のサードパーティーライブラリに依存していますが、独自ツール(gyp)を使って比較的簡単にビルド環境を構築できます。ただストレージ容量は結構必要で、例えば私の場合は50GBは必要でした。

また、今回は回帰バグなのでChromium 58とChromium 59のどちらも並行させてビルドし、比較調査しやすいようにしました。

実際にHackして怪しい箇所を見つける

ChromeのDeveloper Toolsから得られる情報だけではレイアウトバグの状況が分からなかったので、ブラウザの真骨頂であるレンダリングの中のレイアウト(Reflow)コードを読む必要があります。大抵の大規模ソフトウェアは開発目的で色々な内部状態をダンプする機能があり、Chromiumも例外ではありません。ですが、今回の参加メンバーはChromeにはそれほど詳しくないため、当初はそのやり方が分からず、ソースコードとWebと変更履歴をつっつきまわり、最終的にはデバッグ情報を出力させることができました。

具体的には下のように、デバッグ関数をよく通るであろうコードパスから呼び出してみました。

diff --git a/third_party/WebKit/Source/core/layout/LayoutListItem.cpp b/third_party/WebKit/Source/core/layout/LayoutListItem.cpp
index 92af305..946e7c9 100644
--- a/third_party/WebKit/Source/core/layout/LayoutListItem.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutListItem.cpp
@@ -466,6 +466,10 @@ void LayoutListItem::PositionListMarker() {
 
 void LayoutListItem::Paint(const PaintInfo& paint_info,
                            const LayoutPoint& paint_offset) const {
+  this->ShowTreeForThis();
+  this->ShowLayoutTreeForThis();
+  this->ShowLineTreeForThis();
+  //this->ShowDebugData();
   ListItemPainter(*this).Paint(paint_info, paint_offset);
 }

そして、レイアウトバグの有無によって、↓のような違う2つのレイアウトツリーが生成されていることが分かりました。

正しいレイアウトツリー:

LayoutListItem 0x19a65d2243d0   LI
  LayoutListMarker (anonymous) 0x19a65d218df0
  LayoutBlockFlow 0x19a65d218f18        SPAN class="suggestItemOk2"
    LayoutBlockFlow 0x19a65d219040      SPAN class="item"
      LayoutText 0x19a65d2415f0 #text "CCC"
LayoutListItem 0x19a65d224290   LI
  LayoutBlockFlow 0x19a65d218828        SPAN class="suggestItemNg"
    LayoutListMarker (anonymous) 0x19a65d2184b0
    LayoutBlockFlow 0x19a65d218cc8      SPAN class="item"
      LayoutText 0x19a65d241528 #text "AAA"  

正しくないレイアウトツリー(suggestItemNgLayoutListMarkerが外出しされてしまっている):

LayoutListItem 0x19a65d224650   LI
  LayoutListMarker (anonymous) 0x19a65d219168
  LayoutBlockFlow 0x19a65d219290        SPAN class="suggestItemOk2"
    LayoutBlockFlow 0x19a65d2193b8      SPAN class="item"
      LayoutText 0x19a65d240ee8 #text "CCC"
LayoutListItem 0x19a65d224790   LI
  LayoutBlockFlow (anonymous) 0x19a65d218a78
    LayoutListMarker (anonymous) 0x19a65d219608
  LayoutBlockFlow 0x19a65d219e20        SPAN class="suggestItemNg"
    LayoutBlockFlow 0x19a65d219cf8      SPAN class="item"
      LayoutText 0x19a65d241910 #text "AAA"

正しくないレイアウトツリー中で、アドレスが0x19a65d218a78LayoutBlockFlowが余計に生成されています。これによって余計な論理的な行が追加され、意図せず高さがおかしくなってしまうというからくりのようでした。

ここまでくればもう峠を越していて、あとはこの差異をとことん調べ込んでいけばよくなります。

今まではまったくの五里霧中で、どこにバグがあるのか分からず怪しそうなところをとにかく広く浅く探す必要がありました。胸をなでおろせた瞬間でした。

コミットの特定

<li>関連の実装が肝になっているようだったので、LayoutListItemのソースコードを入念に見ました。調べた結果、回帰バグを混入させたコミットを特定することができ、それをgit revertしてビルドし直したらバグが発生しなくなりました! いろいろな切り口で調べたのですが、結果的にはgit annotateが決め手でした。比較的浅い回帰バグにはgit annotateは有効です。

変更内容としては非常に小さいです。ちなみにこの変更を見てみると、もともとはまた別のレイアウトのバグを直そうとしていたようです。

diff --git a/third_party/WebKit/Source/core/layout/LayoutListItem.cpp b/third_party/WebKit/Source/core/layout/LayoutListItem.cpp
index 6c98974..4dbf2a7 100644
--- a/third_party/WebKit/Source/core/layout/LayoutListItem.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutListItem.cpp
@@ -257,6 +257,11 @@ static LayoutObject* getParentOfFirstLineBox(LayoutBlockFlow* curr,
     if (currChild == marker)
       continue;

+    // Shouldn't add marker into Overflow box, instead, add marker
+    // into listitem
+    if (currChild->hasOverflowClip())
+      break;
+
     if (currChild->isInline() &&
         (!currChild->isLayoutInline() ||
          curr->generatesLineBoxesForInlineChild(currChild)))

いざ修正!(は叶わず…)

直そう!と思って一旦保留していたら、先を越され、その間にupstreamで修正されてしまいました。非常に残念です。

修正に必要なコードはたったの一行でした。もともとが2行を追加しただけで回帰バグが発生したのですから、その2行のどちらかを直せば回帰バグは直るというわけです。

diff --git a/third_party/WebKit/Source/core/layout/LayoutListItem.cpp b/third_party/WebKit/Source/core/layout/LayoutListItem.cpp
index 18e98a78..893ee6e 100644
--- a/third_party/WebKit/Source/core/layout/LayoutListItem.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutListItem.cpp
@@ -259,7 +259,7 @@ static LayoutObject* GetParentOfFirstLineBox(LayoutBlockFlow* curr,
 
     // Shouldn't add marker into Overflow box, instead, add marker
     // into listitem
-    if (curr_child->HasOverflowClip())
+    if (curr->HasOverflowClip())
       break;
 
     if (curr_child->IsInline() &&

コードリーディング

今回の一連の修正で問題なのは、ListMarkerをLayout Treeに配置する場所です。 回帰バグの発生前後に関わらず、大前提としてLayoutBlockFlow(class=suggeestItemNg)の子としてListMarkerを追加する必要があります。しかし、その前提が1つ目の修正で崩れてしまいました。回帰バグによりLayoutListItem(<li>)の子として追加するように意図せず実装が変わってしまいました。

その原因を少し説明します。

まず、1つ目の修正で本当に直したかったことは、<li>overflow: hiddenな子要素があるとき、その子としてListMarkerを追加するとclipされ、表示されないという問題でした。

その場合はListItemの子としてListMarkerを入れる必要があります。なので1つ目の修正ではそういうロジックをGetParentOfFirstLineBox()に新規に追加しました。

具体的には、特定条件時にGetParentOfFirstLineBox()からはbreak経由でnullptrを返し、呼び元であるUpdateMarkerLocation()ListItemの子としてListMarkerを追加するというものです。しかし、その判定条件が正しくなく回帰バグが発生してしまいました。

ちなみに、この処理の副作用として改行が必然的に発生します(これがCSS的に正しいかは微妙です)。これは1つ目の修正としては許容するようですが、我々のミニマルテストケースでは許容されません。ミニマルテストケースの正しいレイアウトの挙動はLayoutBlockFlow(class=suggeestItemNg)の子としてListMarkerを追加することです。

追加した判定条件中でHasOverflowClip()が判定すべき対象はcurr(つまりはLayoutBlockFlow(class=suggeestItemNg))でありcurr_childではありません。まさに2つ目の修正ではそうなっています。 というのも、curr_childをどうこうというよりもまずはcurrHasOverflowClip()でないならば、currListMarkerの親として適切なので、currListMarkerを追加すべきだからです。

上のミニマルテストケースは<span class="suggeestItemNg">の子として<span class="item">がいます。正しくない条件では、overflow: hidden<span class="item"><span class="suggeestItemNg">の子要素となっているために、判定結果が誤って真になり、ListMarkerListItemの子として追加されてしまいました。繰り返しますが、本来はoverflow: hiddenでないLayoutBlockFlow(class=suggeestItemNg)の子として追加すべきです。

結果、不要なLayoutBlockFlowができたことで論理改行が発生し、最終的には高さが意図せず高くなってしまうというレイアウト崩れが発生しました。

感想

参加したメンバーの感想です。

小野寺: 複数人でレイアウトロジックの動きを追うのは難しかったです。当社のSPEEDA開発グループではペアプロを積極採用しているのでペアプロの応用実践として何かいい解決案を考えてみたいと思いました。

北内: レンダリングエンジンのソースコードを追うのは骨の折れる作業でしたが、複数人で協力しながら作業したおかげで根気よく進めることができました。また、Appleと共同で開発していたWebKitからフォークしてBlinkに移行したことにともない、メンバ関数の名前をlower-camelcaseからupper-camelcaseに変更するといった変更履歴を見ることができたのも興味深かったです。

鈴木: Chromeがマルチプロセスで動いているからかデバッガでうまくプロセスにattachできなかったため、git grepとデバッグプリントを利用した最終的かつ原始的な手法でバグを調査しましたが、結果的に、これは思いの外有効な策となりました。また、複数人でバグ調査を行う場合、様々な視点・観点を得られ、またメンタル的にもメリットがあるので、機会があればおすすめしたいです。

久保: SREチームでインフラエンジニアとして普段業務をしているため、Chromeのバグ改修は自分には非常にハードルが高く、先輩方についていくだけで必死でしたが、Chromeのような超大なソースのバグの原因を特定する際に、どのようにあたりをつけていくのかについて少し掴めたように思います。今後のSREチームとしての業務に活かせると思いました(Uzabaseのinfraチームは今年の7月よりinfraチームからSREチームに変わり、4Q(10月)以降本格的にSREチームとしてサービス改善にコミットし、バグの改修やレスポンス改善などこれまでのインフラレイヤーにとどまらない業務範囲になります)。

まとめ

今回はソースコードレベルまでの調査を業務で行いました。当社では今後もOSSにも積極的に取り組んでいきたいと思います。Chrome内のソースコードが原因の修正までは特定できたのはよかったのですが、別件の案件が入り、Chrome hackingを一旦保留にしていたら、upstreamでその間に修正されてしまい惜しかったです(本来は自分たちでバグレポートを立てて、テストケース込みでパッチを提出しようとしていたのですが……)。

長くなりましたが、最後にまとめでこの記事を終わりたいと思います。

  • 天下のGoogleのしかもChromeでさえも回帰バグが紛れ込んでしまうことがある。
  • オープンソースだと簡単なバグは自分たちで調査&修正はやろうと思えばできて、みんなでOSSに貢献できる。
  • 弊社では、時にはミドルウェアへのソースコード調査&解決も厭わない情熱あふれる問題解決大好きエンジニアを募集しています。

www.wantedly.com

www.wantedly.com

www.wantedly.com

Gauge Test Automation Toolとアジャイル開発

こんにちはSPEEDAのQAチームの工藤です。
最近ではテスト自動化周りのツールが数多く存在していますが、英語でのみ提供されていて日本で多くの人に知られていないサービスも多いと思います。
そんな中、Gaugeという自動化のツールがイケてるという情報を発見したので実際に調べてみました。

はじめに

GaugeとはThoughtWorks社が開発しているオープンソースのテスト自動化ツールです(2017年7月現在でベータ版)。
もっと具体的に言うと様々なロールのメンバーが自動テストのスクリプトを理解できるようにするためのspecificationツール(恐らく立ち位置的にはCucumber/Gherkinの代替)になります。
ざっくりGaugeの良いところを挙げると下記になります。

  • Selenium Webdriverと一緒に使える
  • マークダウン形式で記述できる
  • ビジネス言語でテスト仕様を記述できる(実行可能な仕様書の概念をサポート)
  • 多言語、マルチプラットフォームをサポート
  • 外部データソースからテストデータを読み込める
  • 拡張可能(自分でpluginを開発できる)
  • IDEのサポートが充実している

Gaugeで使う用語

下記がGaugeで使用する基本的な用語(概念)です。
使い始めるのに最低限必要そうなものをピックアップしているので、当然他にもあります。

  • Specifications(spec)
  • Scenarios
  • Steps
  • Tags

もう少し詳しく説明していきます。

Specification(spec)

テスト対象アプリケーションの特定の機能の仕様を説明しています。

Scenarios

各Scenarioは、特定の仕様の1つのフローを表しています。仕様には少なくとも1つのScenarioが含まれている必要があります。

Steps

仕様を実行可能なコンポーネントに分けたものがStepになります。マークダウン形式のunordered list items(bulleted points)として記述されます。
(大きくContext Steps、Tear Down Steps、Scenarioやconcepts内のStepsに分けられますが、今回はそこらへんの説明は割愛します)。

Tags

TagはspecやScenarioを関連付けするために使用します。タグを用いてspecやScenarioをフィルタリングすることができ、後々便利になります。

Gaugeのインストール

下記ページからダウンロードできます。インストール方法も簡単で、下記ページに書いてある通りに進めていけば5分もかからずにGauge自体はインストールできます。
https://getgauge.io/get-started.html

サンプルコードを見てみる

下記ページから自分の好みの言語のSampleコードのGithubリンクへ飛ぶことができます。
https://docs.getgauge.io/examples.html

私はWeb app using SeleniumのJavaのSampleコードを選択しました。
https://github.com/getgauge/gauge-example-java

下記がSpecificationのファイルになります。
上記で説明した用語はこんな形で使われます。

user.spec(自然言語でテストケースを記述していくファイル)

Signup   //Specification
======

Register a customer  //Scenario
-------------------
tags: user, signup, high, final, smoke  //Tags

* Sign up a new customer                  //Step1
* On the customer page                    //Step2
* Just registered customer is listed      //Step3

上記のSpecファイルはIDEでタブ切り替えでHTML Previewを閲覧できます。Specファイルがそのまま仕様書として使えます。 (自分の場合はIntelliJを使いました。IDE側でプラグインをインストールしてやる必要があります)   f:id:kudogen:20170718122930p:plain

下記がSeleniumのテストコードになります。 各Stepに対してのテストコードが@Stepという形で実装されているのが分かります。
UserSpec.java

public class UserSpec {
    private final WebDriver driver;

    public UserSpec() {
        this.driver = DriverFactory.getDriver();
    }

    public String localPart() {
        // Creating a random local part of an email address also used as username
        return UUID.randomUUID().toString();
    }

    @Step("On signup page")    //Stepsの実装部分
    public void navigateToSignUpPage() {
        driver.get(SignUpPage.SignUpUrl);
    }

レポート機能

プラグインをインストールしてやるだけで、テスト流す度にHTMLのテストレポートを吐き出してくれます(下記画像参照)
実行したテストをSpecificationやTagで検索できて、Spec毎、Scenario毎、Step毎にどれくらい実行時間がかかったかも簡単にわかります。
f:id:kudogen:20170718123453p:plain

今回カバーしていない機能

これまでに紹介したのはあくまでも超基本的なTerminologyです。 他にも下記のようなものがあるのですが、今回は触れられていません。

  • Concepts
  • Parameters
  • Stepの種類(Contexts, Tear Down Steps)

またかなり役立ちそうな下記機能もありますが、今回はカバーしていません。

  • Data driven execution
  • Parallel Execution

まとめ

弊社ではテストケース=仕様書という考え方から、ほぼすべてのプロジェクトでUATを書いています。
元々上記のような取り組みをしていたのですが、もっといいやり方がないか探していたところに見つけたのがGaugeでした。
Gaugeは実行可能なドキュメントという概念("the concept of executable documentation")をサポートしています。
また自動テストのケースを誰でも読めるようにするという考え方も弊社の開発チームが今まで取り組んできていたことですが、Gaugeを使えばよりスマートに実現できそうです。
今後は様々なプロジェクトでGaugeを導入していこうと考えています。

次回は今回カバーできなかったGaugeのAdvancedな機能を中心に取り上げたブログ記事を執筆予定です。


株式会社ユーザベースでは、より良い開発プロセスを共に作り上げていきたいエンジニアを大募集中です!

TCP Fast Open

はじめまして。プロダクト開発チームの小野寺 (ryoqun)です。

今回は最近少しずつ浸透し始めてきた「TCP Fast Open(以下、Fast Open)」という最新技術についてTCP/IPのおさらいを踏まえながら紹介したいと思います。ちなみに、この技術はTCPを高速化するもので、Google、Facebook、Appleなどでも本番投入され初めているものの、まだ国内では浸透していなくだいぶ先取りな紹介となります。

Fast Openという技術は比較的枯れたTCPに対してプロトコルレベルで変更を加える比較的インパクトが大きいと勝手に思っている技術です。 最近は低レイヤーの技術はアプリケーション・サービス開発エンジニアだとあまり意識しないとは思いますが、基礎は大事なので 最新動向を掴むと共に、TCP/IPの良い復習ということで少しの間ですがお付き合い下さい。

Fast Openとは?

一言でいうとTCPのレイテンシーを改善する新しい拡張技術です。具体的には接続確立時(= Open)のレイテンシーを軽減(= Fast)します(よってFast Openという名前の由来となっています)。正確には、TCPのThree-way handshakeを省略することで1 RTT分のレイテンシーを削減できます。

Three-way handshakeを明示的に省略する必要性から、クライアント側とサーバー側の両方がFast Openに対応して初めて有効になります。

注意点としては、僅かに接続確立時の信頼性が犠牲となることです。これについては後述します。また、各ホストへの初回接続時には使えません。理由は、セキュリティのため認証情報(Cookie)が必要になるためです。これはTCPのSYN flooding攻撃に似た危険性を軽減する対策余地をプロトコル上に残すためです。

Three-way handshakeとFast Open

本来ならばTCPだとThree-way handshakeが必要になります。つまりは接続確立時に実際の通信ができるまでに、まずクライアント側からSYNフラグが立ったペイロードが無い小さいパケットを送り、サーバー側からSYN+ACKフラグが立ったペイロードが無い小さいパケットを受け取らなければなりません。これがまさしく1 RTT分のレイテンシーに相当するわけです。

他方でFast Openが有効な場合、接続確立時にいきなりSYNフラグを立てた上で、ペイロードを乗せることができます。例えば、HTTPリクエストがTCPのMSS以下の場合は、0 RTTでnginxなどのHTTPサーバーはリクエストの内容をアプリケーションプロセスに渡すことが実現可能です。ただ、現在だとHTTPというよりかはHTTPS(=TLS)が主流です。その場合はTLS handshakeを即座に開始できるというわけです。なお、Fast OpenとTLS 1.3を組み合わせるとTLSを含めて0-RTTを実現できるようになるようです。

デモ

百聞は一見にしかずということで、まずは実際の動きを見てみましょう。

Fast Openが有効になっているサイトから、Fast Openが有効になっているブラウザでファイルChromiumでダウンロードしてみました。その時のChromiumのDeveloper toolsのNetworkのtimingWiresharkで状態を確認してみました。ちなみにテストに使ったサービスはWikimediaですが、Fast Openが有効なサービスにはGoogleなどもありますが、日本国内からのアクセス時にレイテンシーが発生しFast Openの効果がわかりやすくなるようにWikimediaを選んでいます。

ChromiumのDeveloper tools

Fast Openが無効と有効になっている時の違いを比べてみました。

無効時:

f:id:ryoqun:20170803105742p:plain

有効時:

f:id:ryoqun:20170803105730p:plain

赤の矢印()のところに着目するとわかるように、Initial connection(= TCPのThree-way handshakeのこと)がFast Open有効時は必要なくなっていて、SSLのhandshakeが前倒しになって開始されているのがよくわかります。ただ、SSLのhandshakeにかかった時間や実際のデータの転送にかかった時間はそれほど変わっていません。結果としては、Initial connection分の時間(レイテンシー)だけが綺麗に全体のレスポンス時間から減っています。

Wireshark

Fast Openが無効と有効になっている時の違いを比べてみました。

無効時:

f:id:ryoqun:20170803111734p:plain

有効時:

f:id:ryoqun:20170803111746p:plain

赤枠()のところに着目するとわかるように、Fast Open無効時は、TCPのThree-way handshakeをちゃんとやっています。しかしFast Open有効時は、Three-way handshakeをスキップしていきなりペイロード(Client Hello; TLS handshakeの最初のパケット)があるパケットを送っているのがわかります。結果、パケットのLengthが大きくなっています。なお、このスクリーンショットからはわからないのですが、この最初のパケットにはSYNフラッグがきちんと立っています。その後のやり取りの流れは無効時と変わりありません。

長所と短所

  • ○ 接続確立時のRTTが減る!!!!(これは前述の通りですね)
  • △ 接続確立後のデータ通信のレイテンシーは向上しない(Fast Openは接続確立時だけの話です)
  • ✕ アプリケーション層での冪等性が必要(これについては後述します)

短所: アプリケーション層での冪等性が必要

これが、この記事の冒頭で書いたFast Openを有効にすることによって犠牲にされた信頼性となります。しかしながら、この懸念を考慮する必要はインターネットから到達可能なWebサービスにおいてはほとんどありません。より正確にいうと、そのような前提の場合はブラウザの多重Submit問題と同じ問題であり、ネットワーク起因やユーザー操作起因の違いはあれど、Fast Open抜きにしても必ず考慮する必要があるからです。しかし、TLSを使っていなかったり、あるいは、イントラネット内で完結するWebサービス、またはTCP本来の高信頼性を要求する通信にてFast Openを有効にする場合には懸念となります。

そのFast Openを有効にした際の信頼性の問題というのは、1つのTCP上のアプリケーションリクエストがIP通信網の品質の問題により、2つになりえるという理論的な問題となります。そのためアプリケーション層において、リクエストが2つになっても問題無いという冪等性が必要になります。

その問題の原因を説明する前にまずは、前提となる背景を説明します。TCPの役割というのは、信頼性が担保されないIP通信上に、信頼できる双方向通信路を実現するためのプロトコルです。Three-way handshakeはその信頼性の担保のために接続確立時に必要となります。当時のTCPの設計に問題があったわけでもなく、現時点に至るまでの技術的発展があったわけでもなく、昔も今も、レイテンシーと信頼性のトレードオフの結果、本来のTCPは信頼性を重視するためにはThree-way handshakeを必要としています。反面、IP通信は、設計思想として、耐障害性、冗長性を重視し、ごく稀なパケットロスやパケット重複は許容しています。

というわけで、Fast Openを有効にした上で、Three-way handshakeが省かれ、パケット重複が万が一発生した場合、サーバーにとってはあたかもTCP接続確立が2つ来たかのように見えるので、2回リクエスト処理をしてしまうということになるわけです。

普及状況

繰り返しになりますが、Fast Openはクライアント側とサーバー側がどちらも対応して初めて使えるようになります。具体的にいうと、TCP/IPスタックは多くの場合、OSが実装しているので、OSがFast Openを実装し、それが有効になっている事と、ミドルウェアやアプリケーションが対応している事が必要になります。

クライアント側とサーバー側に分けて、2017年9月時点での普及状況について説明します。

サーバー

Google, Wikipedia, CloudFront, Facebookで有効になっているようです。Fast Openはどちらかというと一般的なWebサービスでは効果が出にくく、CDNや広告配信等の1ショットのHTTPリクエストのトラフィックが大量に発生する場合に効果的です。日本サイトは筆者が簡単に確認する範囲では見つけられませんでした。

クライアント

Webブラウザはそれぞれ対応が始まっています。筆者はUbuntu 16.04/LTSChromiumで動作確認をしています。 OS的にも、iOS、Android、Windowsでそれぞれ動きがあるようです。

ツール/ミドルウェア

ちらほらとサポートが始まっています。網羅的に調べたわけではありませんが、nettyRubycurlなどで対応情報を見つけることができます。

他の技術との関連

最後に、Fast Openがそれぞれ他の技術とどのような関連を持つのかを見てみて、Fast Openの理解を多角的に深めていきたいと思います。

NAT

NATの実装によっては、Fast Openとの相性が良くない場合があります。これは当然で、NATはその原理上TCP接続の状態遷移をトラッキングしなければならないのですが、NATの実装が厳しすぎると、Fast OpenによってThree-way handshakeが省かれれば十分に正しくトラッキングできなくなる場合はあります。

HTTP keep-alive

HTTP通信におけるTCPの接続確立時のレイテンシーを軽減するという目的の上では、Fast OpenもHTTP keep-aliveも同じ立ち位置です。HTTP keep-aliveによってだいぶレイテンシーは改善されます。なので通常のブラウジングでは、keep-aliveに比べてFast Openというのはそれほど如実に効果があるわけではありません。ただ、Fast Openの策定背景としては、HTTP keep-aliveはモバイル回線網にてあまり機能していないという指摘もあります。

スマートフォン向けのHTTP APIのエンドポイント

アプリがkeep-aliveしていないならば効果はあります。ただし、まずはkeep-aliveを対応したほうがいいのは言うまでもありません。

ラストワンマイル回線網(FTTHと4G)

宅地のラストワンマイル回線網というと今で言えば日本国内で言えば、FTTHが圧倒的なシェアになっています。FTTHで通信ホストが国内の場合はそもそものレイテンシーは数msなのでFast Openの効果はほとんどありません。ADSLならば多少は効果はあります。しかし、ラストワンマイルが何にしろ、海外ホストへのアクセスの場合には効果が望めます。

モバイルのラストワンマイル回線網というと今で言えば4G回線が全盛となっています。こちらの場合は国内ホスト、海外ホストに限らず一定のレイテンシー向上が望めます。

Supercookie

Fast Openの際に使われるCookieの転用例としては、Supercookieが挙げられます。セキュリティ上、DOS攻撃対策としてCookieが必要なわけですが、別のセキュリティ観点では、匿名性が犠牲となっています。特にE-tagHSTS同様、passiveでドメインをまたいだ(=クロスドメイン)トラッキングが実現できてしまいます。今回新たにFast Openがトラッキング手段として加わったわけですが、他の既存の代替手段も存在するのも含め、残念ながらトラッキングを現在において完全に抑制することは非常に困難となっています。

HTTP/2

HTTP/2になってもTCPベースなので引き続きFast Openは意味があります。しかしQUICに対してはこちらはUDPベースなので関係なくなります。

参考情報

まとめ


株式会社ユーザベースでは、ローレベル含め技術が大好きなエンジニアを大募集中です!