WWDC 2018に参加しました

こんにちは。freeeでモバイルエンジニアをしている高野です。
6/4(月)から6/8(金)にかけてサンノゼで開催された、AppleのWWDCに参加してきました。

このブログはiOSエンジニア以外の読者もいらっしゃるので、WWDCについて簡単に説明します。WWDCはAppleが毎年開催している大規模な開発者向けのカンファレンスです。国籍・性別・年齢問わず大勢の参加者が訪れます。5日間かけて、最新OSの発表やそれに含まれる新しい機能などについて説明する多くのセッションが開かれます。デザインに関するセッションも多く、エンジニアだけでなくデザイナーの参加者も居ます。

目次

今回の旅程

私は今回がWWDC初参加のため、張り切りから前のめり気味の6/1(金)に仕事を早めに切り上げ、夜の便で羽田〜サンフランシスコへ向かいました。

サンフランシスコを経由せず直接サンノゼに行っても良かったのですが、羽田からのサンノゼに行く便がなく、成田まで1時間以上かけて移動するよりは、サンフランシスコ〜サンノゼと移動した方が景色も楽しめるし良いかな、ということでサンフランシスコ行きの便にしました。

WWDC会期前後の数日でGoogleplex、Computer History Museum、Apple Park Visitor Centerなどを回ったり、サンフランシスコ市内の観光をしました。
観光に関しては後回しにするとして、まずはWWDCについてのレポートです。
少し長くなってしまいましたが、初めて行く方向けの有益そうな情報も散りばめているので、よければお読みください。

WWDC前日チェックインがオススメ

6/1(金)の昼過ぎにサンフランシスコに着き、2日かけて何箇所か観光した後、6/3(日)に会場であるSan Jose McEnery Convention Centerに行き、事前チェックインしました。チェックインは会期中もできるのですが、Keynoteの列に並んでなるべくいい席で見るためには、前日にチェックインしておく必要があります。

チェックイン時に入場カードや参加者へのお土産を受け取ります。

参加者へのお土産のLevi'sのジャケット

今年のお土産は去年と同じくLevi'sのジャケットとピンバッジでした。

WWDC会場入り口。大きくWWDC18の看板が掲げられている 会場正面の入り口です。着いた時はテンション上がりました。

事前チェックインの時、Tim Cookが会場に訪れていて写真を撮ったりできたようですが、残念ながら私は会えませんでした。

チェックインの後、Yahooのエンジニアの方が開いてくださった日本人グループの懇親会に参加しました(事前に参加していたWWDC2018日本人参加者のFacebookグループで知りました)。日本からのWWDC参加者と食事しつつ色々と交流することができ、同じホテルに滞在する方とも知り合えて良かったです(有意義な場をありがとうございました 🙏)。

WWDC初日・Keynote待ちの朝は早くて寒い

初日はKeynote、Platforms State of the Union、Apple Design Awardsがあります。
KeynoteはTim Cookも登壇し、新型のハードが発表されることもあるためメディアから最も注目される発表です。

ライブ配信もあるのでリアルタイムで見たり、翌朝メディアでキャッチアップされる方も多いのではないでしょうか。残念ながら今年はハードの発表はありませんでしたね。

まだ暗い時間帯のWWDC会場の屋外

初日ですが、私は午前3時半くらいにホテルから会場に移動し、並び始めました。
この時点ですでに100人位並んでいました。ちなみに先頭の人は夜の9時台から並んでいたらしいです。早くから並ぶ場合、特に朝方はダウンジャケットを着てもいいくらい寒くなるので注意が必要です。

ここから何時間も並びます。イベントへの期待感もあり、近くにいる他の参加者と話したりして時間を過ごしていると、そこまで辛い時間ではありませんでした。私は今回1人で参加したのですが、列の前後の人と話して仲良くなっておいたりすると、お手洗いなどでその場を離れたい時などに場所をキープしてもらいやすくなったりもします。

入場直後の様子。前に多くの参加者が溜まっている

朝の7時頃に会場の中に入れましたが、入場時に一時的に列が崩れるところがあり、油断していたら若干後ろに下がってしまいましたがまだまだ前寄りです。

ホール開場前、通路で待っている様子。多くの人で混みあっている

今度はKeynoteが行われるホールの開場を待ちます(写真左手奥が入り口)。ここで2~3時間待つのですが、整理された列という形の物がないので、結構人口密度が高くて、外にいる間よりここがしんどかったです。

ホール前で提供された朝食のビュッフェ

朝食も提供されるのですが、隙間の少ない列の中にいると取りに行くのもなかなか苦労します。長時間並ぶなら自分でパンや飲み物を持って行っておくのもアリですね。

そして・・・

ホールの中に入れた様子。奥に見えるスクリーンには数多くのアプリアイコンが表示されている

9時過ぎになり遂にホールに入りました。早くから並んだ甲斐あって、Tim Cookも立つ中央スクリーン近くの前寄りの席に座れました。ステージ正面・中央あたりの一番良い列はメディアなど向けて確保されているようで、一般参加者はどうしてもちょっと斜めの角度からになってしまうみたいですね。

ようやく落ち着いて座れたので、スクリーンに映るアイコンの中から日本のアプリを探してみると、UNIQLO、Suica、NAVITIMEを見つけました。

壇上に上がったTim Cook

そしてTim Cookの登場。近くてテンション上がります。 iOS 12、macOS Mojave、watchOS 5など新しいOSバージョンや、それらの主要な変更点が発表されます。発表内容について詳しくはGigazineさんなどを参照ください。

WWDC会場内の高速有線LANが使えるスペース。大きな机にたくさんの人が向かい、それぞれラップトップ(もちろんほとんどがMacBook)を開いている

会場にはWi-Fiも飛んでいますが、開発者がすぐに新しい物を試せるよう高速の有線LANを使えるスペースがあり、ご覧の通りかなり混雑していました。すぐ試せるようにメインのものとは別に検証用のiPhoneなどを持っていくといいですね。

2日目以降・エンジニア的にはここからが本番

屋外のビュッフェで朝食が提供され、参加者で賑わっている

朝9時から始まるセッションの開始前から、毎日朝食が提供されます。パンやフルーツ、コーヒーやオレンジジュースなど。

他にも、早朝からワークアウト系の催しがあり、参加すると特典(Beatsイヤフォンなど結構豪華なものも)がもらえたりします。会期中、こういう催しに参加すると何かにつけてピンバッジ等の特典が貰えたりするようですね。

9:00からはセッションが始まります。初日に発表された新しいOSの新機能について扱う具体的なセッションや、技術やデザインについてのセッションが4トラック程並行して行われるので、参加者は各々興味のあるセッションを選んで参加します。どのホールも大きなスクリーンや音響設備が整っており、どの席に座っても満足度は高そうでした。

前列の椅子の間から電源タップがぶら下がっている plain
席によっては、椅子の足に電源タップがついていて便利です。

セッションは夕方6時頃には終わって会場も閉館しますが、日没が夜8時半と遅いので、その後でも近場なら観光できますね。

会場までの移動方法・VTAが安い

会場までの移動手段として

  • Lyft/Uber
  • VTA(バス・ライトレール)
  • シェアサイクル
  • 近ければ徒歩

等があります。
勤務先の経費でLyftやUberを利用できる場合はそれがベストですが、個人の費用で行く場合や観光のため等、私用のため自費で移動する場合等の交通費はなるべく安く済ませたいところです。
Lyft等だと近距離移動でもチップを入れると$10前後はしてしまうので、1日に複数回利用しているとそこそこの額になりますが、VTAだと一定時間内の乗り継ぎ等含めてどこまで行っても約$2(後述するClipper Cardを使うとさらに少し安くなる)で済むので無駄な出費をかなり抑えられます。

VTAはバス以外にもライトレールという路面電車を運行しており、会場までの移動手段は充実しています。現地の人が利用するバスに乗るのも楽しいものですし、利用してみても良いかと思います。

他、Limebike等のシェアサイクルを利用するのも手ですが、自転車で歩道は走れなかったり車線の向きが日本と逆だったり少し勝手が違うのでやや注意が必要です。

道端に停められた4台のLime-s電動スクーター

免許を持っている方はこのような電動スクーター(写真はLime-s)という選択肢もありますが、最高時速20km近く出るので、特にヘルメットなしで使うのは少し危険な感じがしました。

なお、シェアサイクルもスクーターも値段的にはVTAとあまり変わらないです。

ランチは海外のエンジニアと話すチャンス

会期中は毎日ランチが配られます。日本人の知り合いを探して一緒に食べたり、セッションの内容を振り返りながら食べるのも良いですが、せっかく「Appleに関連する何らかのクリエイターである」こと位しか共通点のない人が大勢集まる場でもあるので、同じくボッチの外国人の隣に座ってカジュアルに話しかけたりしました。

某ドイツ自動車メーカーでiOSアプリを作っているエンジニアや、韓国の某盛れるカメラアプリを作っているエンジニアとも知り合い、中の話を聞くことができて面白かったです。

WWDCでは会場待ちで並んでいる時間・セッションの開始を待っている時間・ランチの時間など、別の国のエンジニアと話すチャンスが沢山あるのも魅力の一つだと思いました。

Company Storeは初日がベスト

2日目以降、Company Storeと呼ばれる、いわゆる物販がありました。私は初日最初のセッションが始まる前に行ったので十分在庫がある状態でしたが、2日目の夕方頃に再度行ったところ、売り切れている物もかなりありました。

Company Storeの様子。バックパックなどが販売されている

Bashも有り

木曜日には会場付近の広場でBashがあり、音楽ライブなども催されていました。しかも単体の野外ライブと同じくらい本格的。

Bashの様子

食事やドリンクも提供されます。ランチよりBashの食事の方が美味しかったです。

セッションについて

ARやML、Siri等目立つ発表以外にも、ユーザー目線でも嬉しいNotification周り・パスワード管理関連の変更など、セッションを聞いていくごとにとてもいい変更だなと感じることが多く、新しいハードこそ発表されませんでしたが、エンジニア的には充実した内容を聞くことができたかなと思います。

セッションの動画は翌日にはWWDCのサイトにアップロードされます。以下のページにほとんどのセッションが上がっているので、気になるものは見てみると良いと思います。

WWDC 2018 - Videos - Apple Developer

Appleはこの辺り年々オープンになっていて、今ではDevelper Accountを持っていなくてもセッション動画が視聴できますし、今年に至ってはSafari以外のブラウザでも動画を視聴することができるようになっていて素晴らしいですね。

最も印象に残ったセッション

印象に残ったものは沢山あるのですが、ここでは紹介しきれないので、見た中で最も印象に残った、Designing Fluid Interfacesのセッションについて紹介します。

developer.apple.com

iOS・iPhoneを、思考を拡張するデバイスにすべくAppleのデザイナーが考え、実践する手法やその背景にある論理を、Fluid Interfacesという言葉を用いて解きほどいていく感じの内容です。

スピーカーのプレゼンスキルの高さも相まって、私たちがなぜ普段気持ちよくAppleのデバイス(特にiPhone)を使うことができるのか、その根底にあるAppleのデザイナーの情熱のようなものに触れてグッと来るものがありました。私も自分の関わるプロダクトにあれだけの情熱を注いでいるだろうかと振り返るきっかけになるような、繰り返し見たくなるセッションでした。
自分のプロダクトをより良くするのに繋がりそうな具体的なテクニックも含まれるので、まだ観ていない方は是非観ることをオススメします。

セッション飛ばしてもLabには行くべき

Labの様子

WWDC会期中はAppleのエンジニアやデザイナーに直接質問することができるLabが多く開かれます。技術に関すること、デザインに関すること、マーケティングに関すること大体なんでも聞けます。セッションは後からでも見られますが、Labはこの場限りなので積極的に利用しました。
私は参加できなかったのですが、Appleのデザイナーに、自分のアプリのUIデザインに対してアドバイスをもらえるLabなんかもありました。

Labで貰えたXcodeのアイコンのステッカー plain
Labではこんなステッカーを貰えたりします。

最終日はあっさり

最終日は少し早く、夕方の4時くらいにはセッションが終わります。 最後は特別な催しなどはなく、スタッフに見送られながら案外素っ気なく解散となる感じでした。
私は最終日もサンノゼのホテルに宿泊しましたが、すぐにサンフランシスコに向かって観光しても良かったかなと思いました。

WWDC前後で観光

WWDCの話はここまでで、ここから少しWWDC会期前後の観光についての内容になります。会期前の3日間と、会期後の2日間で

などに行きました。 大体の位置関係はこんな感じです

WWDC2018関連観光マップ - Google マイマップ

サンフランシスコ周辺に本社があるIT企業などにも行ってみたかったですが、土日だと休み&平日でも基本的に知り合い社員のツテが無いと社屋には入れないみたいなので諦めました。定期的にオフィスツアーを開いている企業もあるそうなので、タイミングがよければ行けるかもしれませんね。

Googleplex

Googleplexの社員用自転車。各部のパーツがGoogleのロゴの配色になっている Google本社ですね。金曜日、サンフランシスコに着いてすぐに訪れました。広大な敷地の中にいくつも建物があります。建物の中はツテが無いと入れませんが、敷地内は入っても大丈夫そうでした。敷地が広いので、社員用の自転車がいたるところにあります。敷地内のコートで社員の方がビーチバレーをしていたり。

Google Mechandise Store内部の様子 次にGoogle Merchandise Storeです。Googleの敷地内にあります。Googleのデバイスやプリントボトル・Tシャツ、他にボールペンやステッカーなどお土産にしやすいものも売っています。平日は開いていないので注意が必要です。実はここでお土産を買っておきたくて金曜日に来たのでした。

Computer History Museum

Googleのすぐ近くにあるComputer History Museumでは、100年以上前の計算機から始まり、現代のコンピューターに繋がるまでの歴史を、当時の機材そのものの展示含めて見学でき、かなり感動します。

Computer History Museumに展示されるApple II Apple Ⅱや

Computer History Museumに展示されるENIGMA ENIGMAも

チケットを購入して中に入り、気づいたら4時間経っていました。まだの方は是非。

Apple Park Visitor Center

Apple Park Visitor Centerの外観。ガラスの外壁の上に屋根が載った構造 こちらはApple Park Visitor Center。

Apple本社の模型の説明をiPadで見ている様子 中にはApple本社の大きな模型が置いてあり、貸し出されるiPadをかざすとARで社屋の設計や機能が見られて楽しいです。

販売されているTシャツが展示された棚 限定Tシャツなども売られています。

残念ながら一般客は普段Apple本社の社屋には入れませんが、WWDCのスカラーシップ参加者は入る機会があったらしく、羨ましいです。

Apple本社を囲む歩道の写真。両側には木が植えられている

Googleのあるマウンテンビューもそうでしたが、Appleのあるクパチーノは高い建物が少なく、緑も多くて観光していて気持ちよかったです。(上の写真はApple本社を囲む歩道)治安もかなり良いようです。
サンノゼまでは車で20分(バスだと40分)位でしょうか。WWDCの会期中は会場付近にあるホテルの宿泊料金は高騰するので、この辺りのホテルから通っても良かったかもしれません。

サンフランシスコ周辺

WWDC会期後の土日はCaltrainでサンフランシスコに戻り、ケーブルカーに乗ってフィッシャーマンズワーフに行ってクラムチャウダーを食べたり、ゴールデンゲートブリッジに行ったりと普通の観光を楽しみました。

b8taというショップで、面白いガジェットが色々と売っているので、ここにも行きました。
サンフランシスコ以外にもいくつか店舗があるようなので、近くにあったら寄ってみると楽しいと思います。

b8ta内部の写真

現地での必携アイテムClipper Card

Clipper Card実物の写真 plain

Clipper Cardという交通系のICカードで、Caltrain・BART・VTA・Muniなどサンフランシスコやサンノゼなどで移動に使える複数の交通機関に乗ることができます。さらに、都度チケットを買うより少し安くなりますので、着いたら初日に入手しておくと便利です。大きめの駅や、Walgreenというドラッグストアチェーンで入手・チャージできます。

バスや電車の時刻表は一応事前に公式サイトなどから入手していましたが、実際現地ではほとんどiOSのMapアプリの経路案内に従って行動しましたが何も困りませんでした。

まとめ

というわけで、非常に充実した10日間でした。チケット代・渡航費・宿泊費等合わせるとかなりの額になりますが、ありがたいことに前後の観光日程以外は全て経費で行かせてもらうことができました。来年も自分や他のメンバーが参加できるように頑張るぞという気持ちです。

おなじみの採用情報

freee ではモバイルエンジニアを募集中です。 ご興味がある方は是非一度弊社に遊びにいらしてください!

スモールビジネスの未来をアプリで切り拓くモバイルエンジニア募集! - freee 株式会社のモバイルエンジニア中途の求人 - Wantedly

jobs.freee.co.jp

KubernetesでのService公開方法に関する検証 - Ingress Controllerの活用

freeeでSREをしている河村(at-k)です。

freeeでは、既存・新規サービスのマイクロサービス化を推進しており、効率的なマイクロサービスの運用を実現するためにKubernetesを積極活用しています。Kubernetesはコンテナのオーケストレーションツールであり、コンテナ化されたマイクロサービスを管理・運用していく上で大きな効果が期待されます。

Kubernetesでは、複数のノード(例えばAWS EC2 Instance)を組み合わせてクラスタを構成し、そのクラスタ上にコンテナが指定された構成(manifest)で配置されます。Kubernetesはコンテナ構成を自律的に維持する機能を持ち、運用コストや耐障害率を改善します。また、クラスタに配置されたサービスに対しては、具体的などのノードにコンテナをスケールするか、といった詳細にとらわれることなく、宣言的にサービスを定義し、細かな運用はKubernetesにまかせるといったことができます。本稿では後者の、Kubernetes内サービスを外部に公開する手法について行った検証を解説します。

Kubernetesクラスタ内のサービスを公開する方法はいくつかありますが、最近社内で検証を行ったIngress Controller、特にNGINX Ingress Controllerについて紹介し、導入方法及び、カスタマイズ方法としてgRPC通信を有効にする手順を解説します。他の方法としては、例えばIstioや、envoyを利用する、ということも考えられましたが、freeeではNGINXの本番運用経験が豊富なことと、Ingress以外でもKubernetes上でNGINXを利用するあてがあったこと、envoyでないとできないというクリティカルな要件が「まだ」なかったこと、Ingressの実装としてはNGINX Ingress Controllerが一番こなれている(機能の豊富さとメンテナのアクティブさ、本番事例)といった点を考慮しました。

Kubernetesについてある程度の基本的知識を想定していますが、公式が提供しているTutorials - Kubernetesがよくまとまっており、1時間程度でキャッチアップ出来ますので、興味があればこの機会にいかがでしょうか。

Minikubeを使ったサービスの公開

まず例として、Minikubeを使った公式チュートリアル(Hello Minikube - Kubernetes)をベースに、サービスの外部公開まで実施します。MinikubeのインストールとImageの作成までは省略しますが、以下のコマンドで指定しているhello-nodeは、上記の公式チュートリアルで作成した、”Hello World!” を返すWeb ServerのImageです。

> kubectl run hello-node --image=hello-node:v1 --port=8080

> kubectl expose deployment hello-node --type=LoadBalancer

> kubectl get svc
NAME           TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello-node     LoadBalancer       10.96.19.86     <none>        8080:32623/TCP   1m

> minikube service hello-node --url
http://192.168.99.100:32623

> curl http://192.168.99.100:32623
Hello World!

LoadBalancer Type(NodePort Typeでも可)のServiceを作成し、Service毎に割り当てられたポート(例では32623)を通してアクセスします。この様な方法は、サービスがどこで動いているのか一見してわかりやすく感じます。しかし、サービスを追加するたびに、未使用portを払い出したり、portへのネットワーク疎通を確保するなどのKubernetesの外側のインフラを別途整備する必要があります。公開するサービスが少なければ良いですが、数が増えてくると煩雑になります。

参考

Kubernetes NodePort vs LoadBalancer vs Ingress? When should I use what?

Services - Kubernetes Ingress - Kubernetes

Nginx Ingress Controllerを用いたサービス公開

そこで、次に紹介するIngressとIngress Controllerを用いて、それらの課題の解決を図ります。Serviceの前段にIngressを配置する構成で、それらのIngressに対し、ルーティングを行うIngress Controllerがクラスタ内に最低1つ置かれます。Ingress ControllerはInboundトラフィックに対し、要求ホスト名とIngressで公開されるホスト名を突き合わせてルーティングを行います。先程の例と比較すると以下の図の様になります。

Ingress Controllerを使った構成とそうでない構成との違いの図。Ingressを使わない場合はサービスごとにポートを別々にしているが、Ingressを使用した場合は同じポートで2つのサービスを公開している

詳しくは実際の動作を見ていただくほうがわかりやすいかと思うので、ここからIngress Controllerの導入と動作例を見ていきます。Ingress Controllerとして利用できるものはいくつかあるようですが、ここではNGINX Ingress Controllerを試しました。Kubernetesが公式に開発していること、NGINXベースで高いスケーラビリティが期待できること、また、gRPCに対応していること、が採用理由です。

標準設定でのインストール

helm chartが用意されているので、標準的設定であればクラスタへのインストールは比較的容易です。helmはKubernetesにおけるパッケージ管理ツールで、パッケージの検索、インストール、標準設定からのカスタマイズ、設定のrevision管理、などの便利な機能が提供されています。ここでは詳細は省略します。

まずはinstall。

> helm search nginx-ingress
NAME                    CHART VERSION   APP VERSION     DESCRIPTION
stable/nginx-ingress    0.20.1          0.14.0          An nginx Ingress controller that uses ConfigMap...

> helm upgrade --install nginx-ingress stable/nginx-ingress

> helm list
NAME            REVISION        UPDATED                         STATUS          CHART                   NAMESPACE
nginx-ingress   1               Sun Jun 17 22:44:48 2018        DEPLOYED        nginx-ingress-0.20.1    default

helm installで何が行われるかは、以下のコマンドを実行することで、具体的にどういったmanifestが生成・適用されるかを確認することが出来ます。

> helm upgrade --install nginx-ingress stable/nginx-ingress --dry-run --debug | grep Source
...
# Source: nginx-ingress/templates/controller-service.yaml
# Source: nginx-ingress/templates/default-backend-service.yaml
# Source: nginx-ingress/templates/controller-deployment.yaml
# Source: nginx-ingress/templates/default-backend-deployment.yaml
...

Service/Deploymentがそれぞれ2つ宣言されていることがわかります。1つはIngress Controller本体、もう一つのdefault backendは、定義されていないhost nameでアクセスされた場合に用いられるServiceです。kubectlで配置されたServiceを見てみます。

> kubectl get svc
NAME                            TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
nginx-ingress-controller        LoadBalancer   10.103.136.223   <pending>     80:30774/TCP,443:31153/TCP   35m
nginx-ingress-default-backend   ClusterIP      10.106.62.223    <none>        80/TCP                       35m

動作検証

続いて、ingressの設定を行います。

> kubectl expose deployment hello-node --type=ClusterIP --name hello-node-svc --port 8080
service "hello-node-svc" exposed

> cat << EOF | kubectl create -f -
pipe heredoc> apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-world
spec:
  rules:
  - host: hello-world
    http:
      paths:
      - path: /
        backend:
          serviceName: hello-node-svc
          servicePort: 8080
EOF

> kubectl get ingress
NAME            HOSTS              ADDRESS   PORTS     AGE
hello-world   hello-world                80        1m

このとき、ingress controllerのlogを見ていると以下のようにingressの追加が自動で検出され、設定が反映されたことがわかります。

> kubectl logs nginx-ingress-controller-786dc4f648-zlcqz -f

I0617 14:14:29.430140       6 event.go:218] Event(v1.ObjectReference{Kind:"Ingress", Namespace:"default", Name:"hello-world", UID:"bfa92545-7238-11e8-b043-080027992c84", APIVersion:"extensions", ResourceVersion:"995305", FieldPath:""}): type: 'Normal' reason: 'CREATE' Ingress default/hello-world
I0617 14:14:29.546514       6 controller.go:177] ingress backend successfully reloaded...

それではpodにアクセスします。

> minikube service nginx-ingress-controller --url
http://192.168.99.100:30774
http://192.168.99.100:31153

> sudo sh -c 'echo 192.168.99.100 hello-world >> /etc/hosts'

> curl http://hello-world:30774
Hello World!

Ingressに設定したhost nameを使うことで、hello world podにアクセス出来ました。

同じ要領で、"Hello World!"の代わりに"Hello Ingress!"と返すServiceを作ります。ImageとDeploymentの作成は省略、hello-ingress-nodeというDeploymentを作成したとします。

> kubectl expose deployment hello-ingress-node --type=ClusterIP --name hello-ing-node-svc --port 8080
service "hello-ing-node-svc" exposed

> cat << EOF | kubectl create -f -
pipe heredoc> apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-ingress
spec:
  rules:
  - host: hello-ingress
    http:
      paths:
      - path: /
        backend:
          serviceName: hello-ing-node-svc
          servicePort: 8080
EOF

> sudo sh -c 'echo 192.168.99.100 hello-ingress >> /etc/hosts'

> curl http://hello-ingress:30774
Hello Ingress!

hello-worldとhello-ingressは、host nameは異なりますが、どちらも同じIPを指しており、portも同じなので、ネットワーク的には同一のものを参照しています。host nameを元にIngress Controllerが振り分けを行っています。

この様に、Ingress Controllerを配置しておけば、新しいサービスが追加される際に、公開ホスト名を定義したIngressを設定することで自動でサービスまでの動線を整備してくれます。これらの作業はKubernetes内で完結しているため、クラスタ内・外での責任範囲が明確化され、運用の簡易化・権限委譲を効率的に進めることが出来ます。 なお、ホスト名をDNSに登録する必要がありますが(例では/etc/hostsに登録)、こちらについてもexternal-dnsを使うことで、例えばAWSではIngressの追加時にRoute 53に自動設定することが出来ます(今回はMinikubeでの検証なので省略します)。

参考

Kubernetesのexternal-dnsでRoute53 RecordSetを自動作成する - Qiita

external-dns/aws.md at master · kubernetes-incubator/external-dns

カスタマイズ - gRPC用追加設定

デフォルトの設定でもおおよその目的は達せられましたが、実際の運用では設定をカスタマイズしたくなることもあると思います。ここでは、NGINX Ingress ControllerのBackendにgRPCアプリケーションを配置するための手順を説明します。 先にも書いたとおり、NGINX Ingress ControllerはgRPCに対応していますが、いくつか追加設定が必要になります。対象は、Ingress、Ingress Controller、場合によってはapplicationにも手を入れる必要があります。

検証環境として、grpc/grpc-goで配布されているgreeter_serverを配置した以下のDockerfileのコンテナを用いました。同様にgreeter_clientを使って疎通検証を行います。

FROM grpc/go:1.0
EXPOSE 50051
RUN go get -u google.golang.org/grpc/examples/helloworld/greeter_server
RUN go get -u google.golang.org/grpc/examples/helloworld/greeter_client
CMD greeter_server

Ingressの設定

gRPCサービスであることをAnnotationに記載する必要があります。metadataにannotationsとして、grpc-backendであることを明示します。

cat << EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/grpc-backend: "true"
  name: hello-grpc
spec:
  rules:
  - host: hello-grpc
    http:
      paths:
      - path: /
        backend:
          serviceName: hello-grpc-svc
          servicePort: 8080
EOF

Ingress Controllerの設定

NGINX Ingress ControllerのHTTPポートのデフォルトの設定では、HTTP2が有効になっていません。gRPCをHTTPで公開する場合は、追加の設定が必要です(HTTPSの場合は不要)。デフォルト設定で、HTTP2でIngress ControllerのHTTPポートにアクセスすると、次のようなログが出力されます。

172.17.0.1 - [172.17.0.1] - - [17/Jun/2018:16:26:34 +0000] "PRI * HTTP/2.0" 400 174 "-" "-" 0 0.001 [] - - - -

修正が必要な設定箇所は以下のあたりです。listen 443はhttp2が設定されていますが、listen 80にはされていません。

minikube > k exec nginx-ingress-controller-786dc4f648-2k4hh cat /etc/nginx/nginx.conf | grep listen
                listen 80 default_server  backlog=511;
                listen [::]:80 default_server  backlog=511;
                listen 443  default_server  backlog=511 ssl http2;
                listen [::]:443  default_server  backlog=511 ssl http2;

そこで、修正を入れた設定ファイルをロードするように、helm chartsを補正します。NGINX Ingress Controllerは、カスタムテンプレートからnginx.confを生成する仕組みになっているため、このテンプレートを自前のものに差し替えます。

まず、オリジナルのテンプレートを取ってきます。

> kubectl exec nginx-ingress-controller-786dc4f648-2k4hh cat /etc/nginx/template/nginx.tmpl > nginx.tmpl.org

これに対し、以下のように変更を加えます。わかりにくいですが、listen 80 にhttp2を追加しています。

> diff nginx.tmpl.org nginx.tmpl
702c696
<         listen {{ $address }}:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}{{end}};
---
>         listen {{ $address }}:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }} http2{{end}};
704c698
<         listen {{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}{{end}};
---
>         listen {{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }} http2{{end}};
708c702
<         listen {{ $address }}:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}{{ end }};
---
>         listen {{ $address }}:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }} http2{{ end }};
710c704
<         listen [::]:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}{{ end }};
---
>         listen [::]:{{ $all.ListenPorts.HTTP }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }} http2{{ end }};

これを元にconfigmapを作成します。

> kubectl create configmap nginx-custom-tmpl --from-file=nginx.tmpl
configmap "nginx-custom-tmpl" created

configmapをマウントするようにvalues.yamlを書き換え、helm upgradeします。

> cat <<EOF > values.yaml
controller:
  name: controller
  ## Override NGINX template
  customTemplate:
    configMapName: nginx-custom-tmpl
    configMapKey: nginx.tmpl
EOF

> helm upgrade --install nginx-ingress stable/nginx-ingress -f values.yaml
参考

ingress-nginx version 0.13 for grpc not work · Issue #2444 · Kubernetes/ingress-nginx

動作確認

アクセス先を、Nginx Ingress Controllerに変更したgreeter_clientを実行します。

> go run greeter_client/main.go
2018/06/18 02:01:40 Greeting: Hello world

うまく疎通したようです。

アプリケーション側対応(必要であれば)

ここまででIngress ControllerのgRPC対応は完了ですが、アプリケーション側に修正が必要なケースもあります。gRPC通信が時々うまくいかず(全部失敗するわけではない)、NGINX Ingress Controllerのログに

upstream sent invalid http2 table index: 64 while reading response header from upstream

といったエラーが出ている場合は、go-grpcの既知のバグが原因である可能性があり、その場合は、修正PRが取り込まれたバージョンが必要です。詳しくは下記のIssueを参考にしてください。

gRPC servers cannot use dynamic HPACK · Issue #2405 · kubernetes/ingress-nginx

終わりに

Kubernetesのサービス公開方法として、Nginx Ingress Controllerを使った方法を紹介しました。個別にNodePort設定をするよりも、シンプルかつ容易にサービスの外部公開が可能になります。

Kubernetesはコンテナ管理のための新しい基盤であり、日々多くの機能が提案・追加されています。そのため、情報が不足していたり、まだデファクトが定まっていなかったりする部分が多く、学習・導入コストが高くつく可能性があります。一方で、Kubernetesの提供するフレームワークは非常に強力であり、複雑化しがちなマイクロサービス構成を、集約・統合的にまとめることで、サービスの運用を大幅に効率化できる可能性を持っています。本稿が、読者の皆様の何かしらのご助力になれば幸いです。

freeeの"マジ価値"なプロダクト開発の"現場"を体感する | Summer Internship 2018募集中!

freeeで新卒採用を担当している高森(@takamo15g)です。

freeeには2016年4月に新卒入社し、現在社会人3年目です。
今年の4月から主にエンジニア職の新卒採用を担当しています。

freeeでは今年もエンジニア職志望の学生向けにサマーインターンを開催します!

freeeとしてのサマーインターン開催は今年が3度目。

昨年は1週間だったインターンの期間も今年は2週間に伸ばし、より深くfreeeの開発現場を知ってもらえるよう設計しました。

freeeの"マジ価値"な開発の"現場"を体感するインターン

freeeでは freee が重要と考える価値基準の1つとして「ユーザーにとって本質的な価値があると信じることをする(通称、マジ価値)」というものを持っています。

freeeらしい行動や意思決定をするための5つの価値基準

この"マジ価値"を体現すべく、freeeには独自の組織体制や開発文化があります。

今回は昨年のサマーインターン参加者で来年4月に入社予定の内定者の声も交えて、「サマーインターンって何するの?」についてご紹介したいと思います。

サマーインターンってなにやるの?

サマーインターンでは上記のようなfreeeの開発文化を体感していただくことはもちろん、ひとりひとりにメンターがついて密なフィードバックやレビューを行うので、ご自身のエンジニアリング力を高める機会としても活用いただけます。

2週間のうち、初日はPC等の各種セットアップや事業・開発組織についての講義となりますが、2日目からは各自それぞれのチームに配属となり、実際のプロダクト開発に携わっていただきます。

配属チームについては、ご本人との面談のうえ、専攻されている内容や業務内容などを考慮しつつ、個別にアサインメントを考えていきます。

昨年は、会計freee・人事労務freeeのWebアプリケーションやモバイルアプリへの機能開発・機能改善のほか、インフラやセキュリティ関係のチームへも配属を行いました。

昨年のサマーインターン参加者の声

金子誠也くん(北陸先端科学技術大学院大学 / 来年4月入社予定の内定者)

——そもそもfreeeのインターンに応募したきっかけは?

サマーインターンを探しているときに、就活サイトで募集ページを見つけたことがきっかけです。

1dayインターンとして自作のモバイルアプリへのレビュー・フィードバック会が開催されると聞いて、興味を持ちました。*1現役で活躍しているエンジニアに自分のつくったアプリを見てもらう機会なんてなかなかないじゃないですか。

——1dayインターンに参加してみてどうだった?

ギークで技術的な知見を持っている社員から、鋭いフィードバックをもらって、圧倒されましたね。

この人たちと一緒に働いて、もっと吸収して力をつけたいと思い、1weekのインターン*2への参加も希望しました。

——1weekインターンでは具体的にどんなことをしたの?

freeeの事業や開発体制に関するレクチャーは1日目の前半で終わってしまって、後半は開発環境のセットアップをして、2日目からは早速チームに配属されて業務に当たりました。

僕が配属されたのはモバイルチームで、実際にfreeeが提供しているモバイルアプリを触って自ら課題点を洗い出して、その改善まで取り組む、ということを行いました。

2日目に課題点洗い出しを行い、残りの3日間で改善に取り組むという流れでした。

インターン中は同じモバイルチームの方にメンターとしてついてもらっていたので、適宜フィードバックやレビューをもらいながら進めていきました。

正直、5日間はけっこう短かったですね。

最後まで課題を潰しきれず、本当にあっという間に終わってしまったという感じでした。

——インターンを通じて学びになったことはある?

実際に動いているコードを触ることの大事さを実感したことですね。

freeeが提供しているモバイルアプリに実際に使われているコードに触ることで、複雑に絡み合ってアプリが設計・運用されていることを初めて具体的にイメージできました。

それから、やっぱり社員から直接フィードバックを得られる環境だったことも大きかったですね。

印象的だったのは、僕がアプリを触ってUI面での改善案をメンターに提案したら、すぐに社内のデザイナーを呼んできてくれて、話す機会を作ってくれたことです。

インターン生の意見に対しても、こうやってしっかり向き合って対応してくれるんだと感じたことを覚えてます。

f:id:takamo15g:20180615192504j:plain

成果報告をする参加メンバー。メンター陣からも鋭い質問やフィードバックが飛んできます。

——インターンをやってみてfreeeに対する印象とか理解に変化はあった?

そもそも最初はベンチャーで働くってイメージが全く無かったんですよね。

キャリアとしてもベンチャーから大手メーカーまで幅広く見てましたから。

でも、インターンとしてfreeeで働く中で、本当にメンバーがのびのび働いているんだなということがわかりました。

フリードリンク制で社内にドリンクバーがあったり、リラックスできる畳スペースがあったり。

f:id:takamo15g:20180615194523j:plain
ドリンクやスナック類は完全無料。ちょっとリラックスしながら談笑できるカウンターもあります。

インターン初日に社内を見て回ったときに、地下に寝転べるスペースがあったのを見たんですけど、業務を始めてそのスペースで本当に寝そべりながらコードを書いてる人を見かけてびっくりしました。

「あ、これ飾りじゃなくて、本当に寝そべっていいやつなんだ」と驚きましたねw

f:id:takamo15g:20180615194025j:plain
地下のyogiboがあるスペース。地べたに座ってミーティングしたり、寝転がりながらコード書いたり、ちょっと仮眠をとったり、みな思い思いに過ごしてます。

——インターンを経て、freeeへの入社を決断してくれたわけだけど、何が一番の決め手だった?

インターンとして、1週間業務を体験してこれなら続けられそうだと思ったことですね。

一緒に働いたメンターとの性格が似てたことも大きかったです。

気を張らなくていいというか、自然体でいられる環境だなって思ったんです。

マネージャーや先輩に意見しても怒られないのが良いなと思いましたw

自分の意見をしっかり伝えて反映させていけそうだと思えましたね。

——やっぱりインターンを踏まえて実際に自分が働くことを具体的にイメージできたのは大きかったのかな。お話ありがとうございました!

サマーインターンにぜひご応募ください!

ここまでのインタビューを読んで、freeeのサマーインターンに興味を持ったみなさん!

今年はより深く実際の開発現場に入り込んでもらえるよう、期間を2週間に伸ばしてパワーアップしたサマーインターンにぜひご応募ください!

jobs.freee.co.jp

2週間も時間が取れない!という方へ

2週間のインターンプログラムに加えて、1dayインターンのプログラムもご用意しています。

今年は、昨年同様「モバイルコース」に加えて、「Kubernetesコース」の開催も決定しています。

www.wantedly.com

www.wantedly.com

研究などで2週間も時間が取れない!という方はぜひ1dayコースへの参加をご検討ください。

今後1dayコースは追加開催する可能性があります。追加開催を決定し次第、随時お知らせしていきますので、以下をご確認ください。

それでは、皆様のご応募お待ちしています!

*1:今年の1dayインターンはモバイルコースに加えてKubernetesコースも開催します

*2:今年は2weekです

RubyKaigi 2018に参加しました

こんにちは。freeeでアプリケーションエンジニアをしているimamuraです。

去年に続き、今年もRubyKaigiに弊社エンジニア数名で参加しました。 RubyKaigiはプログラミング言語Rubyの技術カンファレンスで、5/30~6/1の三日間でRubyに関する知見や最新の情報が共有されます。そして、今回もfreeeはスポンサーとして協賛させていただきました!

rubykaigi.org

RubyKaigi会場に置かれたfreeeのパンフレット

RUbyKaigi会場のスポンサーロゴが印刷された板

会場の雰囲気

1000人以上の色々な国からRubyエンジニアが集まっていました。 スポンサーブースでは、企業が展示をしてライブコーディングやノベルティの配布、書籍の販売などがあり、大変賑わっていました!

RubyKaigi会場の書籍販売ブース 自社パンフレットが置かれているのを撮影するfreee社員 *1

セッション

そして、RubyKaigiはRubyの生みの親こと、Matzによるkeynoteから始まりました。 MatzのCRubyへのバージョンを上げる以外のコミットがRuby 2.6.0 preview2には反映され、それが多分5年ぶりぐらいなので拍手が起こっていました。 そしてそのコミットは、プログラミングで名前の付け方が重要という前置きで、本人が名付けたyield_selfメソッドが分かりづらかったため、thenという名前への変更だったので会場では笑いが起こっていました。終始、和気藹々とした雰囲気でした。*2

Matzによるキーノートセッションの様子

そのあとは、三会場に分かれてそれぞれ多種多様なセッションがありました。普段Rubyを書いている際に意識していなかった仕組みについて考える機会になったり、RubyGems3の変更点についての話型システムやRuby 2.6.0-preview2からendless rangeという書き方ができるようになった話など最新のRubyに関する情報にキャッチアップすることもできました。

これらのセッションの中でも自分が面白かったセッションを紹介したいと思います。

Analyzing and Reducing Ruby Memory Usage

Analyzing and Reducing Ruby Memory Usage - RubyKaigi 2018

Ruby 2.6系のパフォーマンス改善についての内容でした。大きく分けて二つトピックがありました。*3

一つ目が共有文字列についてです。Rubyの文字列を表す構造体は、メモリが割り当てられた文字の配列の先頭の要素へのポインタと、そこからの長さの情報を持っており、部分一致する文字列は、作られるたびにメモリ割り当てをするのではなく、構造体のポインタと長さの情報だけ変えて同じメモリの文字列を参照するようにしたそうです。Rubyは、ファイルの位置が格納されている配列と、requireメソッドの引数のファイル名をキー、その配列のインデックスを値としたハッシュを持っており、先ほどの共有文字列の仕組みを使って、requireメソッドのパフォーマンスが改善した話でした。*4

二つ目に、Rubyのコードは、命令列(数字)の配列(ISeq Object)にされてからバイトコードになり実行されます。このISeqObjectが参照するオブジェクトはmark_arrayという配列にアドレスを入れて、GCされないように監視されるようになっていましたが、配列を使わずに、直接ISeqObjectからそのオブジェクトをマークするようになりました。これによってmark_arrayに必要なメモリが削減されたようです。

そして、改善を紹介するたびに、「あなたならこの機能にいくら払いますか?5000円、10000円、5000兆円?アップグレードすればタダです!」というセリフで会場が沸いていました。

セッション中の風景

Exploring Internal Ruby Through C Extensions

Exploring Internal Ruby Through C Extensions - RubyKaigi 2018

RubyのHashのパフォーマンスをCで拡張したものやC++で実装したものと比較する内容でした。RubyのオブジェクトがどのようにCの構造体と連携されているか、C側でRubyのデータがどのような構造体で表現されているかなどが説明されました。また標準のRubyのHashはC++で実装したものと条件によってはあまり差がなく、高速だったことが印象に残っています。

RNode with code positions

RNode with code positions - RubyKaigi 2018

speakerdeck.com

Rubyの2.5系から行番号だけでなく、カラムの情報を持つようになった話でした。 カラムとはその行におけるコードの位置のことで、これによってNoMethodErrorなどがどの行のどの位置で起こったかが分かるようになりました。「Rubyのしくみ」などで知っていたASTにどうやって複雑な位置情報を持たせるのかが興味深かったです。

Extend your own programming language

extend your own programming language - RubyKaigi 2018

speakerdeck.com

MinRubyというrubyインタプリタを使ってrubyのインタプリタを拡張する話でした。このインタプリタ自体もrubyで書かれています。rubyのASTを評価するevaluate関数を基本的な四則演算などを使って実装したりしていました。例えば、論理和・論理積の演算子を追加したり、末尾再帰で計算する関数を作ったり、Lindaという並列処理言語のevalを使って拡張したりがありました。具体的な実装でイメージしやすかったので、自分でもMinRubyを拡張したいなと思いました。

DRINK UPを開催しました!

freee.connpass.com

1日目の晩にRubyKaigiに参加された方の懇親の場としてDRINK UPを開催しました! 東京、福島、沖縄、海外など世界中のRubyistたちにお越しいただき、素敵な出会いがたくさんありました。その中にはRubyコミッターでpatch monsterことnobuさんや去年に引き続きご参加された方もいらっしゃいました。

仙台の美味しい料理と日本酒を飲みながらRubyの話、なぜかGoの話、エンジニアのキャリアの話などしながら、普段なかなか交流できない方々と盛り上がれたのは貴重な体験でした。

参加してくださった方々ありがとうございます! また来年もDRINK UPを開催しますので、次回の会場である福岡で皆様のご参加をお待ちしております!

DRINK UP恒例の集合写真

最後に

ここまで読んでくださりありがとうございます!

私自身は去年、新卒でfreeeに入社し2年目になりましたが、初めてのRubyKaigiへの参加でした。登壇者の話す内容のレベルが高くて正直、圧倒されました。自分はまだまだだなーと感じつつ、このままやっていけば自分もその高みに行けるのではないかといった不思議な気分になりました。

そして、何よりもっとRubyについて詳しくなりたい、Rubyでもっとコードを書きたい気持ちが高まった機会でもありました!

お約束の紹介になりますが、freeeではRubyを使ってプロダクトを一緒に成長させてくれる仲間を募集しています!興味を持ってくださった方はぜひfreeeに遊びに来てください!

jobs.freee.co.jp

www.wantedly.com

*1:これは、ブログ用の写真を撮る本人です。

*2:RSpecの英語のような書き方に馴染めないMatzがそれと同じような「then」メソッドを実装したことも笑いを誘ったポイントでした。

*3:Aaronの記事が参考になります。

*4:しかし、Aaronの考案の前にすでに修正されており、コードを書く前に検索しようという言葉でこの話は締めくくられました。これは数年前にあったRubyKaigi 2011の発表で、出てくる「書く前にグーグルしたほうがいい」と同じオチでした。