勉強会 | AI tech studio https://cyberagent.ai AI事業本部は、サイバーエージェントグループのデジタルマーケティング分野におけるサービス開発を行う組織です。 Fri, 03 Jul 2020 01:06:00 +0000 ja hourly 1 https://wordpress.org/?v=5.3.26 AWS SageMakerのハンズオンを実施していただきました! https://cyberagent.ai/blog/tech/seminar/13460/ Fri, 03 Jul 2020 01:02:29 +0000 https://ai-blog-admin.cyberagent.ai/?post_type=blog&p=13460 こんにちは!
AI事業本部 小売DXセンター AirTrackの宗政 一舟(ムネマサ イッシュウ)です。

先月6月18日(木)にSagemakerハンズオンをAWS様にオンラインで開催していただきました。
AirTrack用にカスタマイズされたハンズオンで、チームのデータサイエンティスト・サーバーサイドエンジニア皆で参加しました!

当日の様子と内容をブログにさせていただきます☆

はじめに

機械学習は実世界のサービスで広く活用されるようになってきました。
しかし、データサイエンティストやMLエンジニアの開発した学習モデルをいざサービスに導入しようとした時に
機械学習特有の課題に直面することがあると思います。

学習に用いたデータによって出力される値が変化するため、学習モデルのテストをすることが難しい点や
出力値に異常が存在したことが分かってもデータ、学習アルゴリズム、前処理等のプログラムの
どこに問題があったのかを突き止めることはとても大変です。

リリース時だけでなく、サービスで正常に動作させる運用面の課題もあります。

例えば
時間の経過とともに性能劣化する可能性のある学習モデルを定期的に更新できる状態にしておくこと
オフラインでの評価が良くても本番環境で暴走してしまう恐れがあるため、学習モデルの出力値をモニタリングできるようにしておくこと
などなど。。。

ここ数年で「MLOps」という言葉がでてきたように
機械学習の開発/運用は多くの企業で取り組まれるようになっています。

今回のハンズオンでは、そんな機械学習システムの開発/運用をしていく上で大事な要素の一つである
学習モデルの性能劣化への対応を題材に行っていただきました!

テーマ

AirTrackは位置情報を用いた来訪計測や広告配信を行うことのできるプロダクトです。
来訪見込みの高さを考慮して広告を配信するサービスがAirTrackにはあり、来訪を予測する機械学習モデルを採用しています。
このモデルの性能の劣化が確認されたときに、更新を行える機械学習パイプラインの作成を目指しました。

このハンズオンで想定している状況としては
「old期間中のデータ」で作成した来訪予測モデルが現在本番環境で動いているものとして
「new期間中のデータ」が揃っタイミングで、new期間のモデルとold期間のモデルとの性能を比較し
劣化が確認されたとき本番で動作しているモデルを入れ替えるものになります。

AirTrackサービスに基づいたハンズオンを用意していただいたので、実務を意識して取り組むことができました。

ハンズオンのダイジェスト

作成した機械学習パイプラインのアーキテクチャになります。

「モデルの学習」や「推論の部分」は Amazon SageMakerで作成し
old期間中で作成したモデルとnew期間中で作成したモデルの性能比較をAWS Lambdaで行うような構成で
ワークフローの作成にはStep Functionsを使っています。

 

開発環境の準備

Amazon SageMakerのコンソールからノートブックインスタンスを作成しました。
起動したいインスタンスタイプを設定すると、JupyterまたはJupyterLabで開けるようになります。

 

ノートブックの環境構築が簡単なことに加えて、必要なリソースを選択して開発者が各々で起動できるのは便利ですね☆

 

学習モデルの開発

来訪予測モデル作成にXGBoostを用いました。
AWS Sagemakerでは組み込みのアルゴリズムや独自のアルゴリズムを使うものなど、モデルのトレーニングを定義するのにいくつか方法があるみたいですが
今回はAWS Sagemakerが提供しているXGBoostのフレームワークを利用しました。

以下の xgb_script_mode_estimator の部分で

  •  学習用のpythonのスクリプト
  • 学習が動作するインスタンスサイズ、インスタンスの数
  • ハイパーパラメータ
  • 作成された学習モデルの保存先(S3のパス)

を渡してあげるとXGBoostが使用できるコンテナが呼び出されます。

続いてfit関数で学習の実行します。
この時、train用のデータやvalidation用のデータが置いてあるS3のパスを引数で渡すと
置いてあるデータを読み込みモデルのトレーニングをしてくれます。

学習の状況については
ノートブックインスタンスのセルや

CloudWatch Logsから確認できました。

モデルの開発者は「学習用のデータ」と「学習用のコード」を準備すれば
ノートブックからインスンタンスサイズと数を指定してモデルのトレーニングを実行できるようになるので、本番同様の環境での動作確認がかなりラクになると思いました。

また立ち上がったインスタンスはジョブが終了したら勝手に落ちてくれるので、お金まわりでも嬉しさがあるかもしれません☆

 

実行フローの作成

StepFunctionsに各処理を実行するためのワークフローを定義していきます。
以下のようなワークフローを目指しました。

StepFunctionsでの実行の流れとしては
まず新しいデータに対してモデルのトレーニングジョブ行います。
続いてnew期間中で作成された学習モデルでバッチ推論ジョブold期間中で作成された学習モデルでバッチ推論ジョブの実行。
最後にnew学習モデルとold学習モデルをAUCで評価して、良かった方を採用といったものになります。

 

StepFunctionsの各プロセス(step)の作成

StepFunctionsのビジュアルワークフローのModel Trainingの部分の例です。

trainining_stepにプロセスの名前やestimator、学習に用いるデータのS3パスを渡してTrainingStepを定義します。
ステートマシン図を書かなくてもpythonのSDKからプロセスを定義できるみたいです。
またノートブック上で動作の確認ができていればStepFunctionsのワークフローに乗せられるので、モデル開発者が本番環境のリソースやパッケージのバージョンを気にする負担は減らせるのかなと思いました。

 

StepFunctionsの各プロセス(step)を連結

training_stepのように、その他のプロセスについても定義した後
独立して定義されたプロセスを連結させてあげます。

作成したワークフローはノートブック上で可視化して確認することができます。

またノートブック上からStepFuncsionsをキックし、その実行状況もセルに出力することができました。

ノートブックインスタンス環境から色々なAWSのサービスを使えるようにするためのIAMの設定が必要になりますが
今回のハンズオンで作成した「SageMakerとStepFunctionsを用いた更新フロー」の開発はノートブック上で完結するものになっていました☆
3時間という短い時間で開発環境構築から機械学習パイプライン作成までできてしまうのは衝撃的でした。

最後に

実際のAirTrackサービスを意識した機械学習のパイプラインを作成するハンズオンをAWS様に開催していただきました。
機械学習サービスに携わったことのあるメンバーだけでなく初めて触れるメンバーも参加し、とても良い機会だったと思います。
また早速今回の知見を業務内で活かせる場面もあったりと、収穫の多いハンズオンでした。
オフライン開催の厳しい時世の中、急遽オンライン開催で対応していただき本当にありがとうございました!

ハンズオンの様子です☆

会社に来ていたメンバーで自席以外で作業したいメンバーは少し広めの部屋から参加しました☆

最後まで読んでいただきありがとうございます!

]]>
スッキリわかる渋谷スクランブルスクエアへの行き方! https://cyberagent.ai/blog/tech/seminar/4674/ Wed, 16 Oct 2019 05:23:02 +0000 https://cyberagent.ai/techblog/?p=4674 AI事業本部の黒崎です。

サイバーエージェントの本社はAbemaTowersですが、インターネット広告事業部は渋谷スクランブルスクエアに移転しました。

インターネット広告事業部 移転のお知らせ

勉強会等で渋谷スクランブルスクエアにお越しになる方も増えてきたので、弊社渋谷スクランブルスクエアオフィスへの行き方を紹介します!

場所

サイバーエージェント渋谷スクランブルスクエアオフィスの住所は
〒150-6121 東京都渋谷区渋谷2丁目24番12号
です。

渋谷駅の各路線から通路や地下よりアクセスが可能です。

JR各線, 東京メトロ銀座線, 京王線よりお越しの方

2Fのご利用が便利です。

東京メトロ各線, 東急線よりお越しの方

B2Fの利用が便利です。

フロアマップ

サイバーエージェント渋谷スクランブルスクエアオフィスへお越しの方は、17Fがオフィスエントランスですので、17Fへお越しください。

17Fオフィスロビー

17Fのオフィスロビーはこのようになっています。
ゲートを通過するためには入館証が必要ですので、勉強会等大人数の場合と少人数でのご来社の場合とで入館方法がことなります。
基本的にはイベントの規模によって集合方法は社員から告知しますので、そちらの指示に従ってください。

ゲストカードを使って、一番左側の「19-27」と書かれたゲートをお進みください。
弊社セミナールーム, 会議室は21Fにありますので21Fへエレベーターでお上がりください。

勉強会等大人数の場合

オフィスロビーに机を出して受付を設置しますので、そちらで受付をし、社員の指示に従ってください。

少人数の場合

17F奥にゲストカードの発行機があり、事前にお送りしたQRコードをかざしてください。

退館方法

ご利用になったゲストカードは17Fのセキュリティーゲートにありますゲストカード返却機へ投入してください。
カードが回収されるとともにゲートが開き退館ができます。

以上、スッキリわかる渋谷スクランブルスクエアへの行き方でした!

都会 イズ ジャンゴー…

この記事は自分が新卒で入社する前に スッキリわかる渋谷マークシティ入門! のおかげで当時のオフィスにたどり着けた事に影響を受けて書きました。

]]>
F8 2018 developer conferenceから見る企業とコミュニティの関係 https://cyberagent.ai/blog/tech/seminar/4140/ Fri, 18 May 2018 01:31:17 +0000 https://cyberagent.ai/techblog/?p=4140 はじめに

アドテクスタジオの@waysakuです。
先日行われたF8 2018 Facebook developer conferenceに運良く参加できたので、そこから感じたことを書いてみたいと思います。

発表内容は多くのニュースサイトなどで紹介されているのでそちらを見ていただくとして、今回はニュースサイトではあまり触れられていない(気がする)、且つ日本ではあまり情報が少ないデベロッパーコミュニティにフォーカスしたいと思います。

私も個人としてエンジニアの勉強会に参加したり、会社としてカンファレンスのスポンサーになったりしていることはあるものの、企業とコミュニティ(主催者、参加者)がコラボレーションできることはもっとあるのではないかと感じていたためです。

Facebookのような巨大企業が自社APIを利用したアプリに留まらず、世界各国のデベロッパーの支援を行う理由はなんだろうかと思いを馳せてみました。

※以下、Facebookの表記に沿ってエンジニア、デベロッパー、クリエイター、プログラマーをデベロッパーという表記で統一させていただきます。

F8で語られたデベロッパー支援

現在、Facebookで取り組んでいるデベロッパー支援は以下の3つのようです。

  • FbStart
  • Developer Circle
  • Tech hubs

いずれもFacebookがプラットフォーム戦略をとる上で重要な位置づけと捉えているように感じました。
1日目のKeynoteの後半でIme Archibong氏 (Facebook VP Product Partnerships )によるfacebookのコミュニティへの投資についてのセッションでは多くの成果が出ていることを強調しています。

それぞれどんな支援内容なのか簡単に見ていきましょう。

FbStart

https://developers.facebook.com/fbstart

5年前に開始された中小のモバイルスタートアップや個人デベロッパー向けの支援プログラムです。
今どきのアプリやWEBサービスを持っているスタートアップや個人が申し込むことによって、AWSやDropboxなどの事業に関係するサービスやツールの無償提供が受けられるようになっています。

Developer Circle

https://developers.facebook.com/developercircles

2018年から始まり、当初1万人ほどでしたが、今では10万人が世界で参加している開発者コミュニティになっているようです。
facebookデベロッパーとの交流や情報交換、それ以外にも地域のデベロッパー同士の交流のためのイベントの開催を後押しする取り組みです。
日本では大阪でコミュニティがあるみたいですね。

Tech hubs

※オフィシャルのページが見つけられませんでした

こちらも2018年から開始した地域の技術育成プログラムとスペースを提供する取り組みです。
既に15のTech Hubsに投資しているとのこと。
ナイジェリアで最初に実施されたとのことで、facebookとしてもビジネス的に重要な投資だと位置づけているようです。

 

フェーズごとに支援する

Building Together: Platform Strategy Overviewではフェーズごとの包括的な支援を行っていると説明されていました。

  • Skills
  • Products
  • Business
  • Scale
  • Community

Skills

Developer Circleによる支援がこのフェーズをカバーしているようです。

交流や情報交換の場を提供することによりスキルの補完を促すことが目的なんでしょうか。

Products

様々なfacebook APIを通じて開発をサポートしているとのことです。
facebook認証から始まりAIツールなど潤沢なリソースが開放されていますね。
https://developers.facebook.com/docs/

Business

Tech hubsがここをカバーするとのことでした。
トレーニング支援だけでなく、ビジネス的な支援もしていくんでしょうか。

Scale

FbStartがここに該当するんですね。
中小スタートアップが対象だったみたいですが、現在は拡大を狙うスタートアップも参加しているとのことです。

Community

ユーザーを増やすためにもコミュニティは重要でサポートの提供や情報交換をするためにも是非facebookのプログラムに参加してほしいとのことです。

 

企業がデベロッパーコミュニティを支援するということ

今回改めてFacebookがどんな形でデベロッパーコミュニティを支援しているかを体系的に知ることができました。

わかったことは非常に戦略的にコミュニティ支援を行っていることです。
それだけデベロッパーコミュニティがfacebookビジネスにとって重要だと位置づけているということなんでしょう。

私の印象では日本では企業の宣伝や採用目的でスポンサーなどを行っていることが多いと思いますが(もちろんそれでも大きな意味はあります)、ビジネスの拡大戦略として位置づけて考えてみるとまた違ったコミュニティが生まれて企業とデベロッパーの双方にとっていい取り組みが作れるのではないかと感じました。

企業の成長戦略に合わせた社内コミュニティの形成の参考になりそうですね。

]]>
KubeCon + CloudNativeCon NorthAmerica 2017 @Austin recap https://cyberagent.ai/blog/pr/kubernetes/3925/ Wed, 10 Jan 2018 03:30:53 +0000 https://cyberagent.ai/techblog/?p=3925 こんにちは。

アドテクスタジオ CIA の 青山真也 です。

本日は KubeCon + CloudNativeCon 2017 の話をしたいと思います。
資料はちょこちょこ公開していましたが、ブログを書くのが遅れてしまいました。
にしても、Kubernetes 楽しいですね。

 

KubeCon 2017 の概要

KubeCon + CNCon は半年に1回開催されており、今回は大幅に人数が増え 4000 人満員御礼でした。
Kubernetes が事実上のデファクトスタンダードとなったことにより、注目度も非常に高くなったのだと思います。

CNCF が提供する CloudNative Landscape も 1.0 となり、Fluentd や CoreDNS、その他多くのプロジェクトが 1.0 リリースを発表しました。
これは事実上、CNCF からの CloudNative はもう準備完了というメッセージのようですね。
他にも ServiceMesh を実現する Istio や Conduit、Serverless を実現する Fission、kubeless、OpenFaaS などの注目度が高かった印象です。

今回の会場はテキサス州オースティンだったため、時差的には +0900 から -0600 の差(15 時間の時差)があるため 12/4 は 39 時間ありました。
2018 年は下記の日程で開催予定です。
皆様コペンハーゲンでお会いしましょう。

  • KubeCon + CloudNativeCon Europe 2018
    • コペンハーゲン デンマーク
    • 2018/5/2-4
  • KubeCon + CloudNativeCon China 2018
    • 上海 中国
    • 2018/11/14-15
  • KubeCon + CloudNativeCon North America 2018
    • シアトル アメリカワシントン州
    • 2018/12/11-13

 

Keynote

Keynote では様々な企業での Kubernetes や CNCF がホストしている Cloud Native を促進するプロジェクトの事例が多々紹介されていました。
また、CNCF が保有する各プロジェクトのアップデートやコミュニティの活発さも紹介されており、非常に盛り上がりを見せていました。

社内向けに作成した Keynote 1 日目 + αの資料を挙げてありますので、詳しくはこちらを御覧ください。

 

 

CI/CD 周りのセッション

今回の KubeCon では、「CI/CD 関連」「ServiceMesh(Istio / Conduit)」「Network や OpenStack 周りの SIG」「Serverless(Fission / Kubeless / OpenFaaS)」辺りを中心に聞いてきました。

社内的には CI/CD 周りの改善が望まれていたため、こちらも上記の社内向けに作成した資料に挙げてあります。以下簡単に概要だけ記載しておきます。

 

Using Containers for Continuous Integration and Continuous Delivery

Jenkins と Kubernetes の Integration の話。Groovy で PodTemplate のような記述を行って、build task をコンテナとして起動することができるようです。

 

Deploying to Kubernetes Thousands of Times Per/Day

Kubernetes に対して高速な開発を行なう際に注意すべき4つのポイントの話。具体的かつ体系だって説明がされており、Kubernetes を開発に取り入れる場合には是非見てほしい内容です。

 

Continuous Delivery with Kubernetes at Box

Kubernetes クラスタに Agent を仕込んでおき、Agent が Github 上の Manifest を監視して自身の Kubernetes クラスタを更新する方式で CD を行なう話。新規クラスタに Agent を入れるだけで復旧できるなど、Github をベースとした CD 環境を構築できるため、後術する GitOps などとも近い印象。

 

Microservices, Service Mesh, and CI/CD Pipelines: Making It All Work Together

Istio と BRIGATE (+ kashti)を使った、高度な CI/CD パイプラインを構成する方法。Istio を使うと Canary ・Chaos テスト・Fault Injection・レスポンスタイムなどの集計などができ、BRIGADE を組み合わせると Istio と連携しつつコードによるPipeline 定義をはじめとして、様々なことができるようです。使いこなせたらかなり良い Pipeline 環境にできそうです。

 

Building Better Containers: A Survey of Container Build Tools

Dockerfile では様々な問題があるため、多々ある別のイメージビルドツールの調査結果の話。packer のような方式のイメージビルドツールもあれば、ソースコードから直接イメージを作成するようなツールなど多岐に渡って紹介されていました。

 

GitOps – Operations by Pull Request

GitHub (Pull Request)を利用した Kubernetes 環境への CI/CD パイプラインの作り方の話。非常に分かりやすく、CI/CD パイプラインを考えるなら、このセッションか 2 日目の Kelsey Hightower さんの Keystone 辺りは非常に参考になります。

 

Developer Tooling for Kubernetes Configuration

Kubernetes の Manifest ファイルの Validation と Unit Test を kubeval と kubetest を使って行なう話。CI 時にある程度テストをしておくのは有用かもしれません。

 

Webhooks for Automated Updates

Rancher の Webhook Framework を使って DockerHub の更新時に Kubernetes クラスタの更新を自動化する話。

 

また、先日「@市ヶ谷Geek★Night #16 Kubernetes Christmas!」 でお話させていただいた CI/CD の話では、これらのセッションと CyberAgent での話を混ぜ合わせて CI/CD の話をさせていただきました。

 

 

日本交流会@Austin

今回 KubeCon に参加するにあたり現地で日本交流会を主催させていただきました。著名な方々を含む 33 名もの方々にお集まりいただき、貴重な意見交換ができて良かったです。@tnir さんによると KubeCon 2016 の日本人参加者は 3 名とのことなので、最低でも 11 倍になったようです。非常に盛り上がりを見せていますね。

KubeCon + CloudNativeCon Europe 2018 に行かれる方がいらっしゃいましたら、ぜひコペンハーゲンでお会いしましょう。

 

 

その他

Certified Kubernetes Administrator に合格していると RX-M 様のスポンサーブースで CKA の Tシャツをいただく事ができました。

 

また、KubeCon + CloudNativeCon の多くのセッションは動画が公開されています。興味のあるセッションがあれば Youtube の字幕機能を使いつつ見てみるといいかもしれません。

 

そして KubeCon + CNCon EU 2018 へ

弊社ではオンプレ環境に GKE ライクな Kubernetes 基盤を構築しています。Kubernetes にパッチを宛てて Hardware LB との連携を行ったり Ingress Controller の実装などを行い nginx-ingress / nghttpx-ingress ベースの GKE ライクな Ingress Controller を作成しました。今後はこういった事例で登壇も目指していきたいと思います。

日本からも Kubernetes や Cloud Native を盛り上げていきましょう!

 

 

]]>
[イベントレポート] サイバーエージェントとさくらインターネットのインフラ談義 https://cyberagent.ai/blog/pr/kubernetes/3673/ Tue, 21 Nov 2017 03:00:16 +0000 https://cyberagent.ai/techblog/?p=3673 みなさんこんにちは。

アドテク本部の makocchi です。

 

先日さくらインターネット様と合同で勉強会を開催させて頂きました。

今回の勉強会のテーマを決めるにあたり、双方で検討した結果、最近のトレンドでもあるコンテナについてが良いだろうということで

コンテナまわりの話題についての勉強会となりました。

メインの発表の後に懇親会兼 LT 大会も開催され、夜遅い時間になったにもかかわらず最後までお客様が多くて大変盛況でした!

 

それではさっそく発表の振り返りを(makocchi が勝手に)していきましょう。

 

「Tectonic関連の発表(仮)」

最初に登壇して頂いたのはさくらインターネットの前佛様です。

CoreOS が開発している Tectonic について発表して頂きました。

実際に構築する際のハマリポイントや内部の構成について網羅されていたので、とても興味深い内容でした。

あまり Tectonic についての情報って世の中に無い(特に日本語)ので貴重な話だと思いました。

Tectonic 自体は Enterprise なものなので、基本的には有料なのですが 10 node までは無料でできるので、是非自分で構築してみたいと思います。

今まで名前は知っていたんですけども、あんまり詳しくなかったので参考になりました!

 

 

「On-premise コンテナ基盤とHardware LB を使った “type LoadBalancer”」

続いて弊社の青山より Kubernetes の type LoadBalancer をカスタマイズした話をさせて頂きました。

アドテク本部では OpenStack の上に GKE のように Kubernetes の cluster を展開できるサービスを提供しています。

この仕組みのことを我々は AKE (Adtech Container Engine) と呼んでいます。

この AKE の Kubernetes は OpenStack と連携しており、persistence volume を cinder から切り出したり認証を keystone に担当させたりしています。

しかしながら弊社の OpenStack の環境では Kubernetes 上で type LoadBalancer を作った場合に、そのままでは cluster 外から繋ぐことが出来なくて少々工夫が必要になります。

その少々工夫した部分について紹介させて頂きました。

 

「1年ほどDC/OSを運用してみた話」

引き続き弊社の和田より DC/OS の運用について話をさせて頂きました。

先程の Kubernetes と同じようにコンテナをオーケストレーションしてくれる DC/OS ですが Mesosphere が開発しています。

アドテク本部では分析基盤の構築に DC/OS が使われており、その運用まわりで苦労した点を中心にお話させて頂きました。

Kubernetes に比べれば圧倒的に情報量が少ない DC/OS ですが、今のところ DC/OS 自体には大きなトラブルはなく運用できています。

構築も難しくなく、これからコンテナ基盤を構築される方は DC/OS も検討してみては如何でしょうか。

余談ですが、こちらの blog にもある通り、DC/OS もついに Kubernetes を support し始めるそうです。

Kubernetes の勢いは止まら無さそうですね。

 

「LT大会」

最後にさくらインターネット様と弊社で 2 名ずつ LT を発表させて頂きました。

  • 「10分でわかる Cilium」
  • 「Dockerを始めて1週間のセキュリティエンジニア」
  • 「KubernetesでCLIを快適を使いたい」
  • 「高火力インスタンスのGPUについて」

中でも面白かったのは Cilium についての LT ですね。Cilium は Kubernetes の CNI の plugin として使うことができると初めて知りました。

Linux の kernel の BPF を使って実現していますが、新しめの kernel じゃないと対応してないです。

LT 内では実際のデモもして頂き、もりだくさんの内容で 正直 LT レベルでは無かったので今後機会があればじっくりお話を聞いてみたいところです。

また、さくらのクラウドの高火力インスタンスで使われている GPU の話もとても面白かったです。

 

今回の勉強会はさくらインターネット様と一緒に企画を考えながら運営させて頂きました。

申し込み人数が少なかったらどうしようと心配していましたが、たくさんの方に来て頂いてよかったです。

また機会がありましたら一緒に勉強会を開催していきたいと思います。ご期待ください。

 

最後に公開されている登壇資料を貼っておきます。みなさん是非御覧ください。

 

 

 

 

 

]]>
Kubernetes をいじって Hardware LoadBalancer で “type LoadBalancer” を実現してみた @Kubernetes meetup #7 https://cyberagent.ai/blog/pr/kubernetes/3127/ Fri, 13 Oct 2017 00:00:49 +0000 https://cyberagent.ai/techblog/?p=3127 こんにちは。
Central Infrastructure Agency の青山 真也です。

今回は Kubernetes Meetup #7 で発表してきた話をまとめようと思います。
(CIA にみなさんが予想以上に食いついてきてくださり、緊張がほぐれました。)

 

アドテクスタジオのコンテナ基盤

アドテクスタジオでは、数年前からプライベートクラウドとして OpenStack を導入しています。
また、アドテクシステムではネットワークのレイテンシが許容されないシビアなシステムのため、SNAT や多段ロードバランシングが苦手な背景があります。

また、GKE ライクなコンテナ基盤として AKE (Adtech Container Engine)を社内で正式にリリースし、既にプロダクションでも利用されています。
AKE では OpenStack 連携を行った Kubernetes クラスタを自動構築していますが、ネットワークの制約から Hardware LoadBalancer は L2 DSR を使った構成となっています。

今回はその Hardware LoadBalancer をいかにして AKE と統合し、オンプレミス環境でクラウドネイティブな Kubernetes クラスタを構築したかのお話をさせていただきました。

 

OpenStack の LBaaS で実現できないこと

まず、前提として OpenStack にも L3 機能によるものや Octavia など LBaaS 相当のサービスがあります。
Kubernetes の OpenStack CloudProvider もそれを使えば、何もせずとも type LoadBalancer を利用することが出来ます。
しかし、OpenStack の LBaaS では L3 エージェントを使い、VXLAN や OVS などの技術を利用するため、レイテンシが大きくなる傾向にあります。
また、やはりレイテンシのことなどを考えると Hardware LoadBalancer を利用したいという背景があります。

 

type NodePort + Hardware LoadBalancer + (HAProxy) [AKE 1.0]

まず、真っ先に思いつくのが type NodePort の Service を作って、外部のロードバランサーから NodePort 宛に転送する方式です。
オンプレミス環境で Kubernetes を提供しているところは、この方式を使っているところは結構多いんじゃないかと思います。

弊社の環境では、L2 DSR 構成での利用を想定しており、将来的なことを考えると現状の Hardware LoadBalancer で全てのService の SNAT/NAPT  をさせることに不安が残ったため、一旦は HAProxy に SNAT/NAPT をさせている構成となっています。

 

 

HAProxy を挟んで SNAT/NAPT をしているため、経路としては

  1. Hardware LoadBalancer
  2. HAProxy
  3. Kubernetes Node
  4. Pod (場合によっては Kubernetes Node 間転送)

のように 3 段階も LoadBalancing される構成となってしまいます。
また、Hardware LoadBalancer に SNAT/NAPT させる場合でも、LB の CPU 負荷が高くなってしまうと言った問題もあります。

 

DSR 構成で複数 VIP を利用することを考えると、ポート番号を変えられず、LoadBalancer には下記のようなエントリーが埋め込まれます。
また、VM α、VM βには loopback に VIP がつけられていると想定します。

 

NodePort を利用する場合、特定の Interface に対してバインドするのではなく *:80 のようにに Interface 全てでバインドを行われてしまうため、同じ Port 番号で待ち受けることができなくなります。

 

 

externalIP を使えば、サービス毎に VIP を分散することは可能なのですが、これだとユーザの利便性が低下してしまうという問題が有ります。

 

 

type ClusterIP + iptables + Hardware LoadBalancer [AKE 2.0]

そこで、次に考えられるのが iptables を上手く使うことで SNAT/NAPT の層を排除する方法です。

Kubernetes では ClusterIP が作成された際に、”[ClusterIP] > [対象となるコンテナのIP ]” 宛となるように、iptables のチェイン(KUBE-SVC-*)が自動生成されます。

 

下記の例は 12 Replica の Pod に対して ClusterIP: 10.105.154.99 の Service 例です。
KUBE-SVC-6JWLWQN75X2GPEMN が Service 1 つにあたり、KUBE-SEP-LE6FDRM3QUFC336B がコンテナ 1つにあたるようなイメージです。

...
-A KUBE-SEP-LE6FDRM3QUFC336B -s 10.100.132.3/32 -m comment --comment "default/np-aoyama:http-port" -j KUBE-MARK-MASQ
-A KUBE-SEP-LE6FDRM3QUFC336B -p tcp -m comment --comment "default/np-aoyama:http-port" -m tcp -j DNAT --to-destination 10.100.132.3:80
(これが 12個分)
...
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -m statistic --mode random --probability 0.08332999982 -j KUBE-SEP-LE6FDRM3QUFC336B
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -m statistic --mode random --probability 0.09090999980 -j KUBE-SEP-RHORBY4H7HZZNAON
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -m statistic --mode random --probability 0.10000000009 -j KUBE-SEP-AX3TGI6NI2ZD5QBB
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -m statistic --mode random --probability 0.11110999994 -j KUBE-SEP-Z5P4JUSPI6ND3PJL
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -m statistic --mode random --probability 0.12500000000 -j KUBE-SEP-OS3MZ2BKIDDD5M3X
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -m statistic --mode random --probability 0.14286000002 -j KUBE-SEP-VPU4KVJXFHUVOZEF
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -m statistic --mode random --probability 0.16667000018 -j KUBE-SEP-TLXZNL5OPMTTTR5Z
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -m statistic --mode random --probability 0.20000000019 -j KUBE-SEP-RK7SFLE3SAGU3WIL
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -m statistic --mode random --probability 0.25000000000 -j KUBE-SEP-FQZWZY5ZPC5TWYYN
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-UVJQITOO335GOHM3
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-HZ2Y3NUA5FLEK4DX
-A KUBE-SVC-6JWLWQN75X2GPEMN -m comment --comment "default/svc1:http-port" -j KUBE-SEP-TOUD5TCUHJOWUU5L
...
-A KUBE-SERVICES ! -s 10.96.0.0/12 -d 10.105.154.99/32 -p tcp -m comment --comment "default/svc1:http-port cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.105.154.99/32 -p tcp -m comment --comment "default/svc1:http-port cluster IP" -m tcp --dport 80 -j KUBE-SVC-6JWLWQN75X2GPEMN
...

そこで、LoadBalancer から転送されてくるパケットをそのチェインに流し込むことで、SNAT/NAPT の層を排除することが可能です。
ちなみに、type LoadBalancer もこの方式を利用しています。

 

 

type LoadBalancer + Hardware LoadBalancer [AKE 3.0]

AKE 2.0 では ClusterIP を作り、別の CLI から LoadBalancer と Service の紐付け(52.0.0.1 > svc1)を行なうことで、擬似的に type LoadBalancer 相当のことが出来るようになりました。
しかし、GKE を使っているユーザが AKE に移行する際や、AKE を使っているユーザが GKE に移行する際にはそのまま移行できないという不便な点が残ってしまっていました。

 

やはり type LoadBalancer 作るしかない。。

 

“type LoadBalancer” や外部永続ディスクの動的生成(Persistent Volume Claim からの Persistent Volume 作成)などは、CloudProvider 連携している Kubernetes が利用できる機能となっています。
そのため、type LoadBalancer を実装する場合には、この CloudProvider の部分を改変することで、実現することが可能です。

他にも CloudProvider で制御できるものには色々なものがあります。

  • LoadBalancer (今回はここの話)
  • Routing
  • Host
  • Zone
  • BlockDevice


CloudProvider の Interface は pkg/cloudprovider/cloud.go に定義されており、OpenStack の場合の処理はpkg/cloudprovider/providers/openstack/* に定義されています。

LoadBalancer の CloudProvider を実装する場合には、下記の 4 つの関数を実装します。

GetLoadBalancer(clusterName string, service *v1.Service)
 ・あまり変える部分はない

EnsureLoadBalancer(clusterName string, service *v1.Service, nodes []*v1.Node)
 ・LoadBalancer を作成する、IP の指定がない場合は自動アサイン

UpdateLoadBalancer(clusterName string, service *v1.Service, nodes []*v1.Node)
 ・LoadBalancer を更新する

EnsureLoadBalancerDeleted(clusterName string, service *v1.Service)
 ・LoadBalancer を削除する

 

実際には上記 3 種類の Interface を実装してあげる形です。
service 構造体には Service の定義が、nodes には K8s Node の情報が配列で渡ってきます。

例えば、下記のような形でそれぞれの情報を取得することが可能です。

// Kubernetes Node の情報
nodes[].Name

// Kubernetes Service の情報
service.Name
service.Spec.LoadBalancerIP
service.Spec.Ports[].Port
service.Spec.Ports[].TargetPort

 

この 4 つの関数の中で外部 LoadBalancer の制御、自動での IP 払い出しの機能を実装することで、CloudProvider を独自実装することが可能です。
外部 LoadBalancer の制御に関しては、AXC を利用しています。

 

 

こうして無事に GKE 互換な AKE 環境を Hardware LoadBalancer を使って実現することが可能になるのです。

沢山オンプレのコンテナ環境の話をしましたが、もちろん adtech studio でも GKE も積極的に利用しています。
パブリッククラウドでは、World Wide な環境に強かったり立ち上げ期のスピード感に対応できるなど、多くのメリットがあります。
オンプレ環境にも同等の環境を用意できたことによって、相互の利点を生かしつつ、GKE > AKE, AKE > GKE のシームレスな移行が可能となり、コンテナ特有のポータピリティを活かすことができると期待しています。

 

 

参考スライド

発表内容

https://youtu.be/kjK-LpvfZFU?t=6834

]]>
Scala将軍達の後の祭り 2017 レポート https://cyberagent.ai/blog/no_use/no_genre/10195/ Wed, 17 May 2017 01:06:52 +0000 https://cyberagent.ai/scalablog/?p=2353 こんにちは、アドテクスタジオ 業務推進室の伊藤(@wildtiger0713)です。

 

2月に開催されたScalaMatsuri2017の将軍スポンサー3社(マーベリック/セプテーニ・オリジナル/サイバーエージェント)と2016の将軍スポンサー、チャットワークの4社が集ったScalaMatsuriのアフターイベント的な位置づけになりつつある「Scala将軍達の後の祭り」。今年も西新宿にあるセプテーニ社オフィス内カフェスペースをお借りして、4社合同にて開催させていただきました!!

 

今年もScala界隈の多くのエンジニアが集まる中でサイバーエージェント アドテクスタジオからも2名が登壇させていただきましたので、その様子をレポートさせて頂きます。

 

 

 

■Akka StreamsでS3とKafkaを繋いでみた(水野聖也)

[slideshare id=75944549&doc=akkastreams-170513130830]

 

Alpakkaを使ってS3とKafkaを簡単に接続し、S3上のオブジェクトを負荷分散しながらDownloadしKafkaに投入するStream処理の実装を紹介されていました。トップバッターということで緊張していたようですが、無事に発表を終えて安堵していたようです(笑) 前日の土曜日にも出社して入念に検証をしているところを見かけました。準備から登壇までお疲れ様でした!

 

 

 

 

■Akka PersistenceのJournalのライフサイクル管理(松村郁生)

[slideshare id=75953463&doc=presentation-170514012355]

 

ScalaMatsuri当日の「Akka ClusterとAuto-scaling (https://cyberagent.ai/scalablog/2017/02/27/akka-cluster-and-auto-scaling/)」に続いての登壇です。Akka PersistenceのJournalで古いイベントをあえて捨てたいケースにおけるイベントの回収方法と、Redis上のPersistence Queryで終了処理を実装した場合のメモリ消費と列挙時間の検証結果を紹介されていました。

 

 

 

 

弊社以外からもチャットワーク社 かとじゅん氏「DDD with RDRA, ICONIX」、マーベリック社  福島秀貴氏「ScalaとGolangの多相性(多態性)いろいろ」、大谷俊介氏「ギョームで使用しているScala OSSに投げたPRの話」、 セプテーニ・オリジナル社 @kimutyam氏「Reactive Messaging Patternsを使った境界づけられたコンテキストの統合」が登壇され、またLT枠ではScalaMatsuri座長を勤める 麻植泰輔氏による「AUXパターンをDOTTYで解決する」等、本家ScalaMatsuriさながらの深い話が聞ける意義のある会になったかと思います。

※「Scala将軍達の後の祭り」connpassのイベントページはコチラ

 

 

今後も4社協力してScala界隈を盛り上げていければと思います。

最後になりましたが、会場提供、準備、運営等を主動して頂いたセプテーニ・オリジナルの皆様、本当にありがとうございました!

 

来年またScalaMatsuri 2018でお会いできることを楽しみにしています!

]]>
アドテクスタジオ × Scala 勉強会 レポート https://cyberagent.ai/blog/tech/scala/10202/ Fri, 31 Mar 2017 13:56:44 +0000 https://cyberagent.ai/scalablog/?p=2335 こんにちは、アドテクスタジオ 業務推進室の伊藤(@wildtiger0713)です。

 

3/21(火)、弊社サイバーエージェントオフィスのセミナールームにて、「アドテクスタジオ × Scala勉強会」を開催させて頂きました。

このイベントは毎年ScalaMatsuriの開催後に、弊社主催で行わせて頂いているイベントで、ビッグデータ、高スループット低レイテンシーの性能要件などアドテク領域のサービスに求められる厳しい要求の中でのScalaの導入事例や、ディープラーニング等の最新技術への取り組み等をアドテク初心者にも理解できるように分かりやすく語る会です。今回はこちらのイベントの様子をレポートしたいと思います!

 

 

当日は多くのScalaエンジニアの皆さまに足を運んで頂く中で、アドテクスタジオから3名のエンジニアが登壇させて頂きました。

 

 

 

 

■Dynalystにおける動的な広告の画像生成 (Dynalyst 黒崎 優太)

 

ユーザ毎のパーソナライズされた動的な広告バナーをScalaで生成するために使用しているライブラリや処理上の工夫点、Scalaの強みが活かせそうなポイントなどが紹介されていました。

 

 

 

 

■明日から業務で使うScala (LODEO 陶山 育男)

 

チュートリアルでは出てこないけど実際の開発では役に立つ。開発環境、ビルドツール、頻出するイディオム、デプロイ、運用などにおけるScalaのTipsが紹介されていました。

 

 

 

■RTBにおける機械学習システムの実装と運用 (Smalgo 成田 敦博)

 

RTB(Real-Time Bidding)システムの概要と機械学習による予測モデルをどう実装しているかのSmalgoにおける事例、また運用フェーズにおける予測モデルの精度とその整合性のテストについての話がありました。Scalaと直接の関連は薄いものの、技術的な関心は高かったようで質疑も盛り上がっていました。

 

 

セッション後の懇親会も和気あいあいの雰囲気の中でLTも盛り上がりました。LTに登壇頂いた皆さま、ありがとうございました。

また、懇親会後に募ったオフィスの見学ツアーにも20名余りの方に手を上げて頂き、実際に弊社のエンジニアが働いている環境を見て頂くことができました。

 

Scala関連に留まらず、アドテクスタジオでは定期的にこういった勉強会など、社外のエンジニアの皆さまとの交流機会となるイベントを開催していきたいと思いますので、引き続きよろしくお願いいたします。

 

]]>
[AdTech Scala Meetup] おてがる単位型パターン https://cyberagent.ai/blog/tech/scala/3342/ Fri, 20 Jan 2017 10:24:52 +0000 https://cyberagent.ai/scalablog/?p=2225 こんにちは。AMoAdの鳥越(@piyo7)ですっ。
以前、社内のAdTech Scala Meetupにて、用途に適した単位型を自力で小さく実装してみよう、という発表をしました。そのスライドはQiitaで公開済みなのですが、ブログ用にリライトしてこちらでもご紹介します。コードはScala 2.12.1で動作確認しています。

 

できる!単位演算

アドテクで、ありそうなシチュエーションを考えてみましょう。
広告が400回閲覧されたので、30円支払いました。単価のCPMを求めるため、新人のあもえは次のようなコードを書いてみました。

val imp: Int    = 400
val yen: Int    = 30
val cpm: Double = yen / imp

その様子を横からのぞきこんでいた道玄坂先輩は、ニヤニヤしながらバグの指摘を始めます。
しかしバグと言われても、電卓代わりのシンプルなコードです。Scala覚えたてのあもえも、さすがに間違えようがないでしょう。新手の新人いびりでしょうか……?

「Intの割り算は切り捨てだから、割る前にDoubleに変換しないとダメ。それだとCPMは0になっちゃう」
「それから、CPMは1000掲載(impression)単位の価格だから、1000掛けないと」
「あ、400っていうのは、閲覧(viewable impression)数ね。掲載(impression)数は500でしょ」

残念ながら、先輩の指摘は全て的を得ていたため、あもえはコードを修正しました。たった3行に3つもバグを仕込んでしまってションボリです。

val imp: Int    = 500
val yen: Int    = 30
val cpm: Double = yen.toDouble / imp * 1000
assert((cpm - 60.0).abs < 1e-4) // テストOK

さて、このような単位演算のバグを防ぐためにはどうすればいいでしょうか?

レビューしてもらえば大丈夫でしょうか。
でも、道玄坂先輩だって冬の寒気に震えて、うっかりバグを見逃してしまうこともあるのです。

単体テストを走らせれば大丈夫でしょうか。
でも、道玄坂先輩だって春の陽気に浮かれ、うっかりテスト対象を覗いてしまい、テストも同じようにバグらせてしてしまうこともあるのです。

下記のように関数化すれば大丈夫でしょうか。
でも、道玄坂先輩だって夏の熱気に参って、うっかり引数impとyenの順序をひっくり返して呼んでしまい、にも関わらずコンパイルは通ってしまって、新たなバグを作りこんでしまうこともあるのです。

def calcCpm(imp: Int, yen: Int): Double =
  yen.toDouble / imp * 1000

もちろん、これは品質のために開発コストをどれだけ掛けるかという話ですから、きちんとしたテストとレビューがあれば大抵の場合は大丈夫でしょう。
でも、できることならば、コーディング時に不正な単位演算に気づける仕組みがあると良いですね? Scalaには豊かな型システムがありますね? それを使えばコンパイルエラーにできそうですね!

 

ILLな幽霊型とDOPEな型クラス

型テクニックの一つに、幽霊型(phantom type)があります。実際には使用しない型パラメータを宣言することで、同じ振る舞いをする別の型を作りだす、というものです。これはScalaに限らず、C++のテンプレートなどでも、よく見かけるテクニックですね。
まず準備として、次のような型AのラッパークラスUnitBaseを定義します。AnyValを継承して値クラスにすることで、実行時のオーバーヘッドを回避しています。

case class UnitBase[A, Tag](value: A) extends AnyVal

これを元に幽霊型ImpTagを使って、新たな型Impを定義してみましょう。

trait ImpTag
type Imp = UnitBase[Int, ImpTag]

同様にYenやCpmも定義します。すると、先程の関数は次のように書きなおせます。

def calcCpm(imp: Imp, yen: Yen): Cpm =
  new Cpm(yen.value.toDouble / imp.value * 1000)

やりました! これで、引数の順序を間違えたら、コンパイルエラーになりますね。めでたしめでたし。

……でもないですね。数値を取りだすためにフィールドvalueを呼ぶ必要がある分、単位演算はちょっと面倒になってしまいました。
改善しましょう。単位演算は、UnitBaseに実装した方が便利そうです。今回、ラップする型にはIntやDoubleを想定しているので、たとえばUnitBaseの足し算は、IntやDoubleの足し算をそのままラップすれば良さそうです。

♪ Yo、Yo、IntやDouble、存在しないから問題視する親クラス
♪ でも、ヒントは厚く、開いた拡張暗黙に磨いた型クラス

型クラスとは、継承を使わずに共通のインターフェースを外部から与える仕組みです。型クラスのインスタンスは、暗黙に受け渡すと便利です。
ややこしそうに聞こえるかもしれませんが、具体例を見ると分かりやすいです。Numeric型クラスを使って、3つの数値を足し算する関数を定義してみましょう。

def plus3[A](x: A, y: A, z: A)(implicit op: Numeric[A]): A = {  
  op.plus(op.plus(x, y), z)
}

このplus3は型パラメータAがIntでもDoubleでも使うことができ、Numeric型クラスのインスタンスがないStringなどに使おうとするとコンパイルエラーになります。気が利いてますね。

 

単位型を作るならこんなふうに

さて、本記事の目的は、用途に適した単位型をユーティリティとして気軽に用意することです。
アドテクだと、たとえば次のような要望を満たしたくなるでしょう。

  • 同じ型の足し算&引き算&大小比較は、元の数値型のものをそのままラップする。
  • 掛け算&割り算は、単位の組み合わせを制限したい。
    • クリック率にコンバージョン数を掛けているコードは、おそらくバグ。
  • 掛け算&割り算は、固定の係数を自動で適用したい。
    • CPMの1000とか。

ということで、実装したものがこちらになります。
50行程度の小さなコードで、その大部分が型の制御です。

case class UnitNum[A, Tag](value: A) extends AnyVal {
  def +(x: UnitNum[A, Tag])(implicit op: Numeric[A]) =
    UnitNum[A, Tag](op.plus(value, x.value))

  def -(x: UnitNum[A, Tag])(implicit op: Numeric[A]) =
    UnitNum[A, Tag](op.minus(value, x.value))

  def *[Tag1, Tag2](x: UnitNum[A, Tag1])(implicit op: Numeric[A], mul: UnitNum.Mul[A, Tag, Tag1, Tag2]) =
    UnitNum[A, Tag2](op.times(op.times(value, x.value), mul.factor))

  def /[Tag1, Tag2](x: UnitNum[A, Tag1])(implicit op: Fractional[A], mul: UnitNum.Mul[A, Tag1, Tag2, Tag]) =
    UnitNum[A, Tag2](op.div(op.div(value, x.value), mul.factor))

  def map[B](f: A => B) =
    UnitNum[B, Tag](f(value))

  def unary_-(implicit op: Numeric[A]) =
    map(op.negate)

  def abs(implicit op: Numeric[A]) =
    map(op.abs)

  def mapInt(implicit op: Numeric[A]) =
    map(op.toInt)

  def mapLong(implicit op: Numeric[A]) =
    map(op.toLong)

  def mapFloat(implicit op: Numeric[A]) =
    map(op.toFloat)

  def mapDouble(implicit op: Numeric[A]) =
    map(op.toDouble)
}

object UnitNum {
  import scala.language.implicitConversions

  implicit def ordering[A: Ordering, Tag]: Ordering[UnitNum[A, Tag]] =
    Ordering.by(_.value)

  implicit def ordered[A: Ordering, Tag](x: UnitNum[A, Tag]): Ordered[UnitNum[A, Tag]] =
    Ordered.orderingToOrdered(x)(ordering[A, Tag])

  case class Mul[A, Tag1, Tag2, Tag3](factor: A) extends AnyVal

  object Mul {
    implicit def commutative[A, Tag1, Tag2, Tag3](implicit mul: Mul[A, Tag1, Tag2, Tag3]) =
      Mul[A, Tag2, Tag1, Tag3](mul.factor)
  }
}

それでは時を巻き戻して、このUnitNumによる単位型を定義した世界で、新人のあもえにコーディングしてもらいましょう。

val imp: Vimp = Vimp(400)
val yen: Yen  = Yen(30)
val cpm: Cpm  = yen / imp // コンパイルエラー!!!

おおっと、コンパイルエラーが出てしまいました。Yen型の値をVimp型の値で割っても、Cpm型は付かないというわけです。
エラーメッセージを見て、おやおやと首を傾げたあもえは、そもそも変数impにVimp型を付けているのが変だということに気づきます。そしてImp型に修正してもまだ消えてくれないコンパイルエラーに頭を抱えて、くるくるっとアーロンチェアを回したあもえは、ラップする型をDoubleに変換しないといけないことに気づきます。
そのようにして、コンパイルを通すためにコードを修正したところ、次のようになりました。

val imp: Imp = Imp(500)
val yen: Yen = Yen(30)
val cpm: Cpm = yen.mapDouble / imp
assert((cpm - Cpm(60.0)).abs < Cpm(1e-4)) // テストOK

自然にバグが解消されていますね。新人が同じ轍を踏む姿を見れなくて、道玄坂先輩はちょっと寂しそうです。

なお、実際にどう単位型を定義したかというと、次のような形で、

trait ImpTag
type Imp = UnitNum[Int, ImpTag]
object Imp {
  def apply(value: Int) = new Imp(value)
}

掛け算&割り算の制限については、

implicit val mulImpCpmYen = UnitNum.Mul[Double, ImpTag, CpmTag, YenTag](0.001)

この定義一つで、次の四つの演算ができるようにしています。

  • Imp * Cpm / 1000 = Yen
  • Cpm * Imp / 1000 = Yen
  • Yen / Imp * 1000 = Cpm
  • Yen / Cpm * 1000 = Imp

まとめ

いくつかの型テクニックを使って、良い感じの単位型を実装してみました。
この実装は、単位演算の要件に応じて細かくカスタマイズすることができるので、ぜひ自分色に染めてみてください!

なお物理計算の場合、たとえばメートル毎秒(m/s)を秒(s)で割るとメートル毎秒毎秒(m/s^2)になるといった次元計算を型で扱おうとすると、また別のアプローチが必要になります。そのあたりの話は、また別の機会にっ。

]]>
[AdTech Scala Meetup] Log & Scala2.12リリース速報 https://cyberagent.ai/blog/tech/seminar/3337/ Fri, 18 Nov 2016 05:50:13 +0000 https://cyberagent.ai/scalablog/?p=2125 こんにちは。Dynalyst でエンジニアをしております瓜生です。

11月に入ってめっきり寒くなってきましたね。
そんな寒空の中、今回も Hot な話題が発表された AdTech Scala Meetup の内容を紹介して行きたいと思います!

 
今回は「Log」というテーマで2つの発表がありました。

 

Logback stackdriver-loggingを作った

まず一人目は黒崎さんの発表です。

Google Cloud Platform のサービスの Stackdriver Logging が一般公開されたことを元に、
Logback&fluentd でアプリケーションのログをオンプレで動作している Stackdriver Logging に送るという内容でした。

初めはELK的な感じなのかなーと思っていたのですが、Stackdriver ErrorReporting がかなり便利そうで、
アラートが来るのはもちろん、エラーが起きた際のスタックトレースが表示されたり、
さらには Github と連携しておけば、スタックトレースでの該当の箇所が Github のソースコードにリンクされ、
すぐに確認できるらしいです!

 

ログのメトリックを取ってみる話

そして、二人目は阿川さんの発表です。

ログのメトリックを取って監視の運用をしているというお話でした。
datadog 等のモニタリングサービスで、
エラー数のしきい値によってアラート出すようなところでも同じような仕組みになっていそうですね。
個人的には、発表の中にあった、ログレベルの定義の部分は肝に銘じておかねばと思いました。

 
また、今回は飛び込みで志村さんから、つい最近リリースされた Scala2.12 の変更点についての発表もありました。

Scala 2.12.0 変更点

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-11-11-11-35-54
スライドはこちら

発表では、コンパイラの最適化部分の話が面白かったです!

 

まとめ

今回はテーマ外の内容でも飛び込みで発表があったりと、かなりフラットな感じで活動しています。
次回の AdTeck Scala Meetup が開催され次第、引き続き活動内容を公開していきます!
To be continued…

]]>