谷本 心 in せろ部屋

はてなダイアリーから引っ越してきました

陸マイラーは何で税金を払うべきか。au Payの1択か?

友人から「自動車税や固定資産税などの税金は、何で払うのがお得なの?」と聞かれました。僕はau PAYで払っているのですが、他の支払いだとどうなるのかをきちんと調べたことがなかったので、調べてみました。これは調べてみましたブログです。

なお、前提として貯めるのはJALANAのマイルであり、JALカードANAカードを使っている想定です。

au PAYの場合 : クレカのマイル(0.5〜1.5%) + 0.5%のPonta(≒0.3%のJALマイル)

  • 使えるクレジットカードが多い
  • 残高チャージでクレジットカードのポイントが貯まる(一部カードを除く)
  • 請求書払いで0.5%のPontaがもらえる
  • PayPayより使える店や自治体が少ない

au PAYの特徴は チャージできるクレジットカードの種類が多い ことです。MastercardとAMEXのすべてのカードと、VISAとJCBは一部のカードが使えます。JCBビューカードが使えるため、僕はJALカードSuicaを登録しています。

またau PAYへのクレジットカードのチャージは 通常のショッピングと同じポイント還元 があります。これも他のPAYではなかなかない特徴です。ただし、JALカードの一部ブランドのようにポイント還元されないカードもあるため、事前にきちんと確認してください。僕が使っているJALカードSuicaは問題なく1%のマイルが還元されることを確認しています。

www.jal.co.jp

さらに、au PAYでは 請求書払いで0.5%のPontaが還元 されます。Pontaは50%のレートでJALマイルに交換できます。時々キャンペーンで60%のレートになるので、このキャンペーンを待って交換するのが良いと思います。つまりチャージ時の還元とは別に、0.25〜0.3%のマイル還元がある計算になります。

ANAのマイルを貯めている人はPontaが貯まってもあまり嬉しくないかも知れないので、Pontaが使える店で消化してしまうなり、レートが悪くともJALマイルから別のポイントを経由してANAマイルに交換することになりますね。

そんな還元率の良いau PAYですが、まだPayPayほどは使える店や自治体が多くありません。僕が住んでいる自治体は、昨年(2021年)の12月からやっとau PAYでの納税が可能になりました。そこだけがネックですね。

PayPayの場合 : ソフトバンクグループの回線を持っていればクレカのマイル(0.5〜1.5%) 持っていなければ現金同様

  • 使える店や自治体が多い
  • YahooカードかPayPayカードでしかチャージできない
  • 残高チャージでクレジットカードのポイントが貯まらない
  • 請求書払いの還元もない
  • ソフトバンクグループの回線ならまとめて支払いでポイントが貯まる

PayPayの特徴は 使える店や自治体が多い ことです。クレジットカードは使えないけどPayPayだけは使えるという店だってよくありました。自治体でもPayPayとLINE Payのみ対応しているところがまだあると思います。

使える場所が多いPayPayですが、クレジットカードからチャージしようと思うとYahooカードかPayPayカードしか使えないうえ PayPay残高にチャージしてもクレジットカード側でのポイントは貯まらない のです。PayPayの請求書払いはPayPay残高でしか払えないため、実質的に現金でチャージするのと変わりません。

またPayPay請求書払いで納税すると、かつては0.5%のPayPayボーナスが還元されていたのですが、それも2022年3月いっぱいでなくなり、いまは完全に 現金納付と同じ になってしまいました。

paypay.ne.jp

ただ一つだけPayPayのチャージで還元を受ける方法があり、それがソフトバンクグループの回線(ソフトバンク、ワイモバイル、LINEMO)の まとめて支払いからのチャージ です。まとめて支払いを使ってチャージすると回線料金の支払いに使っているクレジットカードに請求されるため、そこでクレジットカードのポイント還元が受けられます。

ただし、まとめて支払いは上限額が契約状況によって変わり、回線開通直後などは月に1万円や3万円くらいまでしか使えないこともあります。回線を維持していると毎月上限額が増えていき、上限の最大は10万円になります。ただし税金額が高い場合には月10万円でも足りない場合があるため、この方法でチャージする場合は 毎月、計画的にチャージする必要がある でしょうね。

僕は納税ではPayPayを使っていませんが、PayPayしか使えない店での支払いや、PayPay祭りのような還元が大きいイベントに備えて、ソフトバンクやワイモバイルの回線から定期的にチャージするようにしています。

LINE Payの場合 : 0.5%のVポイント(≒0.25%のマイル)

LINE Payについては実際に使っていないので、調べた限りの情報を書きます。間違っているところがあれば教えてもらえると嬉しいです。

まず大前提として LINE Payで貯まるLINEポイントはマイルと交換できません陸マイラーとしてはこの時点でやる気がゼロになります。

気を取り直してもう少し調べると LINE Payの残高にはクレジットカードからのチャージもできません。なかなか手強いですね。

ただLINE Payの請求書払いは、残高からの支払いだけでなく、クレジットカードから直接払う「チャージ&ペイ」という方法でも支払うことができます。光明が差してきました。

このチャージ&ペイは、Visa LINE Payクレジットカードか三井住友カードを使うことができます。三井住友カードであればマイルが貯まるのではと思ったのですが、なんと ANAカードは対象外 ですし、そもそもJALには三井住友カードがありません。終わりましたね。

www.smbc-card.com

ANAカード以外の三井住友カードであれば チャージ&ペイによる請求書払いで200円につき1ポイントのVポイント が貯まり、50%のレートでANAマイルに交換できるので、少しANAマイルを貯めることができますね。JALマイルに持っていくのは大変なので、JALマイルしか貯めていない場合はVポイントとして消化するのが良いでしょう。

クレジットカードの場合 : クレカのマイル(0.5〜1.5%) ただし手数料が1%程度

  • メジャーなクレジットカードはだいたい使える
  • 手数料が1%掛かる

ここまで三大Payを見てきましたが、条件が厳しかったり、ルールが複雑だったりして難しい面がありましたが、そもそもPayではなくクレジットカードで直接払ってしまえばどうでしょうか。

クレジットカード納付は、VISA、mastercard、JCB、AMEX、ダイナースなどのメジャーなクレジットカードを使うことができ、一部のカードを除いて 通常のショッピングと同様のポイント還元 が受けられます。

ただしクレジットカードで納付する場合、自治体にもよりますが、おおむね 1%前後のシステム利用料 が掛かります。一部のプラチナカードでない限り、クレジットカードの還元は0.5%〜1%ですから、なかなか判断が難しいところです。

1マイルの価値が最低2円以上あると考えれば、1%〜2%の還元になるわけですから損はしないと考えることもできますね。この辺りはマイルの使い方、捉え方次第ということになります。

まとめ

まとめ代わりに、1%還元されるJAL/ANAのクレジットカードを持っていて、10万円の納税をする場合の還元マイルを計算してみます。

  1. au PAY : 1000マイル + JAL 300マイル
  2. PayPay : 1000マイル(ソフトバンクグループの回線が必要)
  3. LINE Pay : ANA 250マイル(ANAカード以外の三井住友カードが必要)
  4. クレジットカード : 1000マイル(手数料1000円)

こうなりました。

見るからにau PAYの1強という感じではありますが、自治体ごとに使える支払い方法や、持っているカードや回線、また貯めているマイルの種類などで最適なものを選んでもらえればと思います。

いかがでしたか?

Intel Mac miniを10GbE対応のNAS代わりにしてみた。

Mac Studioが発表されてすぐに発注し、発売から1週間ほどで手元に届きました。Mac Studioは有線LANがデフォルトで10GbEになっているという思い切りの良さでして、せっかくなのでこの10GbEを活かすべく、家のネットワークを見直すことにしました。

目的

10GbE化して何をするかって言うと、ファイル共有です。

元々はIntel Mac miniで外付けのSSDを3台(Thunderbolt 3、USB-C 3.1 Gen2、USB 2.0)使っていましたが、足りなくなるたびに買い足してポートが埋まっていってしまうため、いずれNASを使いたいなと思っていました。ただ1Gbpsの環境では100MB/sec程度しか出ないため、性能的には微妙です。

外付けSSDはThunderbolt 3のもので2000MB/sec(理論値 4000MB/sec程度)、USB 3.1のもので700MB/sec(同 1000MB/sec程度)が出るため、それらに比べても100MB/secというのはあまりに遅すぎます。

ただ10GbEならUSB 3.1に相当するスピードですから、この環境でならNASも快適に使えそうです。ただ10GbE対応のNASは高いですし、既存のNASSATASSD(HDD)のものがほとんどで、M.2 NVMeのSSDが使えるものがあまりありません。

そんなこんなで迷ってるときに「Mac miniNAS代わりにしちゃえば、十分に性能が出るし便利だよ」と教わったので、今回、Mac Studioを導入する機会にMac miniNAS代わりにすることにしました。

買ったもの

  • 10GbE対応のハブ
  • Thunderbolt 3 → 10GbEアダプタ

10GbE対応のハブ

10GbE対応のハブはまだまだ製品も少なく値段も高めです。今回はこんな条件で選びました。

ただ、すべての条件を満たそうとすると厳しくなるため、今回はポートの数だけ我慢して、比較的コスパの高いTP-Linkの5ポートの製品を選びました。

www.amazon.co.jp

他にもQNAPがいくつか10GbE対応のハブを出しています。

www.amazon.co.jp

ただ、こちらの製品だとSFP+ポートを10GbEのRJ-45ポートに変換するためのアダプタも必要になると少し出費がかさむため、まずは安いTP-LinkのTL-SX105で試してみることにしました。

Thunderbolt 3 → 10GbEアダプタ

ハブと同じくらい高いのが、MacのThunderbolt 3ポートで10GbE対応の有線LANに変換できるアダプタです。今回はOWCの製品を選びました。

www.amazon.co.jp

通常は2〜3万円ぐらいするのですが、公式サイトで箱破損のものが1万円くらいで破格のセールになっていたため、飛びつきました。セール開始から3時間後には品切れになっていましたね。

構成

接続はこんな構成になっています(10Gbpsの部分のみ)

他にもメッシュルーターやら家電やらゲーム機がつながっていますが、今回の話題の対象外なので割愛します。

ベンチマーク

構成を作ったらまずベンチマーク

SSDベンチマーク

まずはMac miniのThunderbolt 3ポートに繋いだSSDベンチマークです。

f:id:cero-t:20220326175311p:plain
Mac miniのThunderbolt 3ポートに接続したSSD

外付けSSDでも2000MB/secを超えてくるあたり、USBやeSATAの時代からすると隔世の感があります。

Mac miniの共有設定でこのSSDに外部からアクセスできるようにして、Mac Studioからアクセスしてみました。そのベンチマークがこちらです。

f:id:cero-t:20220326175243p:plain
10GbE経由でMac miniSSDにアクセス

10GbE = 10000Mbps = 1250MB/sec と考えると、シーケンシャルリードは上限に近い数字が出ています。それに比べてライトは劣りますし、ランダムリードライトはかなりイマイチな感じです。ランダムアクセスはネットワークがオーバーヘッドになりやすいので仕方ないのでしょうけど、もう少しランダムの性能が出てくれれば、直接ThunderboltやUSBで繋いだ場合と遜色ない感じで使えるのでしょうね。

使用感

テキストファイルや画像ファイル、小さな動画ファイルなどは、ローカルファイルと全く変わらない体感で操作できます。この辺は別に1Gbpsのネットワークでもさして変わらないでしょう。

問題は、大きな動画ファイルです。

10GBくらいの動画ファイルをVLCで再生したところ、ローカルで開くのと変わらないスピードですぐに再生が始まりました。ただ、20GBを超えるファイルになると、開くまで数秒くらい掛かることがありました(パッと開くこともあります。謎)

また、無線LAN環境では再生中に止まってしまうようなビットレートの高い動画でも、10GbE環境ではまったく止まることがありませんでした。シーク(見たいところにジャンプ)しても待たされることはありません。このあたりはまさに期待通りの挙動ですね。

そんなわけで、ネットワークを10GbEにすることでファイル共有の使い方が一段階変わったという感想になりました。無線LANWi-Fi 6/6Eにすれば、使い勝手が変わるんですかね? またいずれ試してみたいと思います。

おまけ: 固定回線のベンチマーク

ついでに固定回線をauひかりの10Gbps契約をしたので、そのベンチマークも載せておきますね。

f:id:cero-t:20220326180816p:plain
auひかり 10G契約の回線テスト

めっちゃはやい。

Intel Mac miniをmacOS 12.3にアップデート後、映像が出なくなる現象が起きている。

※本事象はmacOS 12.3.1にアップデートすることで改善されました。記録のために残しておきます。

事の起こり

待望のユニバーサルコントロールが提供されたmacOS 12.3、ついにリリースされたので僕も喜び勇んで即アップデートをしました。

ただ、アップデート後、Mac miniが起動しない。正確にはジャーーンという音がしてリンゴマークが出た後に、画面が真っ暗なまま。何度か電源長押しで強制シャットダウン→再起動を繰り返すも症状は変わらず。

画面は真っ暗でもキーボードを叩いているとビープ音がするので、画面が映っていないだけで、起動はしているようです。

映像出力がおかしいのだと思って、ディスプレイと接続しているUSB-CやHDMIを接続し直して何度か起動を繰り返した結果、何とか起動できる状態まで復旧したのですが(方法は後述)こんな不安定な状態になってしまうのは、さすがに困ります。

他にも同症状の人が

他にも同じ症状が出ている人はいないかと思ってツイッターで検索してみたところ、同症状の人が何人かいました。

Redditにもスレッドがありました。

www.reddit.com

共通点はIntel Mac mini、LGのディスプレイでの報告が多い

全員に確認したわけではないのですが、この症状が起きている人に聞いてみたところ、だいたい共通点がありました。

  • Intel Mac mini(2018だけ?)
  • USB-Cの映像出力がされなくなる
  • LGのディスプレイが多い。BenQDellPhilipsなどでも報告あり(4Kディスプレイだけ?)
  • デュアルディスプレイの場合、症状の出方が人によって少しずつ違う

ちなみに僕の場合は、LG 27UL850 + Intel Mac mini (2018) でした。

同じ機種を使う人がみな発現しているのか、個体差があるのかはまだ分かっていません。

回避方法

  1. ディスプレイ側の設定でDisplayPortのバージョンを1.4 → 1.2に下げる
    • これが最も確度の高い回避策です
  2. Mac miniのThunderbolt3ポートで、右側2つのいずれかをディスプレイと繋いでいる場合、左側2つのどちらかに変える
    • これだけで回避できた人もいました
  3. HDMI接続に変える
    • HDMIが使える場合は、HDMI接続に変えれば問題を回避できます。なお、僕の場合はHDMI接続でもブラックアウトしたままでした。
  4. Thunderbolt3(左側2つのいずれか)と、HDMIの両方を接続して起動する
    • これでThuderbolt3(USB-C)がメインディスプレイとして表示され、HDMIがサブディスプレイとしてブラックアウトしたままになることがあります。
    • ディスプレイをミラーリングにすれば、少なくとUSB-Cをメインディスプレイとして使う分には困らなくなります。

まとめ

DisplayPortのバージョンを1.4 → 1.2に下げるのが最も効果的なようです。

※冒頭にも書きましたが、本事象はmacOS 12.3.1にアップデートすることで改善されました。

Dapr Advent Calendar 25日目 - Daprを使うべきかどうか

こんにちは Dapr Advent Calendar 25日目です。ついにファイナルを迎えました。私たちの挑戦を応援し続けてくれた世界中のファンの皆さん、そして、Dapr Advent Calendarに集まってくださった皆さん、本当にありがとうございます!

Daprを使うべきかどうか

一部のオタクにしか分からない挨拶から始めて申し訳ありません、いよいよDapr Advent Calendarも最終回です。最後は「Daprを使うべきかどうか」について論じたいと思います。

なぜDaprを使うのか

まずはDaprのメリットや、Daprが解決する課題について説明します。

この3つを順に説明します。

1. アプリとインフラを疎結合にする

Daprの主目的であり、このAdvent Calendarでも何度となく触れてきたことですが、Daprを使うことでアプリケーションとインフラ層を分離することができます。そのおかげで開発時にインフラ関連のツールは必要ありませんし、インフラのことを気にせずビジネスロジック(とWeb API)の開発に注力することができます。

その辺りはこの記事に書いていました。

cero-t.hatenadiary.jp

また、Spring Cloudのようにアプリケーションとインフラ層の結合がより密接しているフレームワークでは、新旧バージョンの混在が難しいことがありましたが、アプリケーションとインフラ層が分離されているDaprでは、アプリケーションだけのバージョンアップ、Daprやk8sだけのバージョンアップが行いやすいというメリットがあります。

その辺りについては、この記事にも書いた通りです。

cero-t.hatenadiary.jp

このアプリケーションとインフラ層を分離できるというのが、Daprの1つ目のメリットとなります。

2. 言語やフレームワークを選ばない

2つ目のメリットが、Daprがサイドカーという方式を採っているため、言語やフレームワークを選ばないことです。

僕はJava + Spring Bootで開発していますが、僕が大好きだったSpring CloudはJVM言語でしか開発ができません。Daprであればその縛りはなく、HTTP通信さえできれば好きな言語や好きなフレームワークでの開発ができます。

仮に組織の中で様々な言語やフレームワークを使う人たちがいたとしても、組織の共通技術基盤としてDaprを採用することができ、皆のノウハウを集めて育てることができます。

その気になれば、一つのシステムを別々の言語やフレームワークを混在させて構築することもできるでしょう。マイクロサービスの一要素としてポリグロット(多言語)が挙げられることがありますが、まさにそれを実現するための基盤としてDaprを利用することができます。

そんな好き放題に言語やフレームワークが使われることはあまりないとは思いますが、システムが非常に大きくてサブシステムごとに開発する組織が分かれる場合や、たとえばフロントエンドに近い部分はNode.jsで作り、コアに近い部分はJVM言語で作る、という程度のポリグロットなら現実にもあり得るでしょう。Daprはそうした状況でも共通的に使えるのです。

3. マルチクラウド

そしてメリットの最後が、マルチクラウドです。

たとえばAWS向けに開発していたシステムを、AWSの障害に備えてAzureやGCPでも運用したい時には、それぞれのマネージドサービスやミドルウェアを抽象化するDaprを利用できます。

そんな案件ほとんどないでしょ、せいぜいマルチリージョンまででしょ、と思っていたのですが、意外とそういうリクエストが "なくはない" という感じで、実際にマルチクラウドを目指している案件で、Daprを採用するかべきかどうかを一緒に検討して欲しいと言われています。

もちろんDaprなど使わなくても、各クラウド固有のマネージドサービスではなくPostgreSQLMySQL、Redis、またたKafkaやRabbitMQなどを使うようにすれば十分だという面もあるでしょう。しかしDaprを使えばマネージドサービスのメリットを引き出しながら、共通のAPIで利用できるというメリットがあるというのも確かです。

ネットワーク関連の設定などはDaprの範疇外ですし、マルチクラウドは簡単でもありませんが、せめてアプリケーションとその周辺のインフラ層においてはDaprを使うことで複雑さを排除できる可能性があります。

この辺りは実際に取り組む機会などあれば、また改めてお伝えしたいと思います。

Daprを使う上での課題

もちろん、Daprを使えば順風満帆というわけでも、全幅の信頼が置けるというわけでもありません。Daprのデメリットや課題についても目を向けてみましょう。

  • 導入事例が極めて少ない
  • 機能が少ない
  • メンテナンス期間の短さ

この3つです。ほぼ説明不要な気もしますが、順に説明します。

1. 導入事例が極めて少ない

まず一つ目、Daprは導入事例がほとんどありません。人によっては「cero-tさんがやってるやつ」みたいな印象があると耳にしたことすらあります。それくらい日本でも、おそらく世界でも、導入事例は多くありません。

全く枯れていない技術ですから、大きな問題があったり、情報が少なかったりするかも知れません。あるいは全く流行らなくてメンテナンスされなくなってフェードアウトしてしまうリスクだってあります。

新しいプロダクトを使うというのは、そういうリスクと戦うことになります。

2. 機能が少ない

Daprは世にアナウンスされから約2年、バージョン1.0がリリースされてから約1年くらいの新しいプロダクトですから、そこまで機能が多くありません。このDapr Advent Caledndarだけであらかたの機能を網羅できてしまう程度です。

Spring Cloudなどと比較すると、提供する機能も、対応するコンポーネント(マネージドサービス、ミドルウェア)も少ないですし、いくつかのコンポーネントはまだAlpha版やBeta版だったりします。Alpha版やBeta版の機能でも十分に使えるので僕は使っていますが、いつかAPIの仕様が変わるかも知れないというリスクを受け入れざるを得ません。

機能が少ない部分は自分でカバーしたり、クラウドの機能を直接使ったりする必要があるため、すべてがDaprだけで完結するわけでもありません。足りないところは自分で補う必要があるのです。

3. メンテナンス期間の短さ

DaprはMicrosoft社が中心に開発しているプロダクトですが、まだビジネス的に十分回っているわけではありませんから、サポートなどのエコシステムもそんなに整ってはいません。バージョン1.0からは「Production Ready」な品質として、少し古いバージョンでもパッチが提供されるようになりました。ただ、そのパッチの提供期間も長くはありません。

Daprはおおむね2ヶ月に1度程度、マイナーバージョンアップ(1.4→1.5、1.5→1.6など)が行われます。またバグフィックスや軽微な機能追加のためのパッチ(1.4.1→1.4.2、1.5.0→1.5.1など)が提供されます。このパッチの提供は「現行バージョンと、その一つ前のバージョン」のみが対象となっています。

docs.dapr.io

2021-12-25現在の最新バージョンは 1.5.1 で、その前バージョンである 1.4 はパッチ提供の範囲内なので 1.4.4 までリリースされていますが、バージョン 1.3 以前にはパッチは提供されません。

おおむね2ヶ月に一度バージョンが上がることを踏まえると、特定バージョンのメンテナンス期間は4ヶ月程度しかありません。もちろん最新バージョン(1.x.0)がリリースされてすぐには使わず、パッチが1つ2つリリースされてから使うことも多いでしょうから、実質的には長くとも2ヶ月に一度くらいにはDaprのバージョンを上げる必要があります。

次のドキュメントにあるように1コマンドでバージョンアップできるとは言え、その頻度には抵抗がある組織だってあるかも知れません。

docs.dapr.io

このサポートの短さは課題の一つと言えます。

Daprの課題を覆す

僕は上に書いたようなリスクを受け入れてDaprを使っているのですが、もちろん僕の「Daprを使いましょう」という提案を受け入れてくれたお客様も相当な勇気(あるいは無謀さ)があったように思います。*1

ただ、Daprに問題があった時に心中するつもりで使っているわけでもありません。Daprにどうしても受け入れられない問題があると分かったときには「Daprを捨てて自分で作る」つもりでいます。

DaprはHTTP通信で使うサイドカーですから、同様の機能を自分で作り込み、URLの向き先を自分で作った機能にすれば、Daprを使わずに同様の機能を提供できます。Daprを使っている以上はアプリケーションとインフラ層が疎結合になっているので、別のものに差し替えることだって不可能ではないのです。

もちろん口で言うほど容易なことではないですし、そんな状況が訪れることは願ってないですが、「Daprを使って開発すれば、最悪、自分で作り直すことだってできる」という感覚ではいるのです。そのため、リスクを受け入れることができました。

サイドカーコンテナ

ところで、Daprのようなサイドカーを、単独プロセスではなくコンテナとして提供するパターンもあります。

qiita.com

ここで記載されているサイドカーパターンやアンバサダーパターンを使えば、アプリケーション開発はビジネスロジックに注力し、サイドカーやアンバサダーがインフラとの連携に注力するという責任分解もやりやすくなるでしょう。Daprで提供されているような主要機能を、自分で好きなように実装して提供することもできるはずです。

ではなぜサイドカーコンテナパターンにしなかったのかと言うと、単純に、自分で実装するのが面倒だったためです。そこにDaprがあるんだから、Daprを使おう、ダメだった時には自分で作ろう、というくらいの感覚です。

そうやってダメな場合の撤退パターンも考えたうえで、僕はDaprを使うことに決めたのです。

まとめ

  • なぜDaprを使うのか
  • Daprを使う上での課題
    • 導入事例が極めて少ない
    • 機能が少ない
    • メンテナンス期間の短さ
  • 最悪、自分で作り直すという覚悟を持って使い始めた

最後になりますが、僕はDaprというプロダクトに魅力を感じているのもそうですが、それ以上にDaprという「アプリケーション寄りのサイドカーサービス」というアーキテクチャそのものに魅力を感じて使っているとも言えます。そのアーキテクチャに利があるという確信があるため、最悪は自分で作れば良いという覚悟ができましたし、他にもっと良いものが出てきたら乗り換えれば良い、きっと乗り換えやすいはずだと考えています。

それがいまの僕の考え方なんだ、ということをお伝えして、Dapr Advent Calendarを締めくくりたいと思います。

それでは、またの機会に!

See you!

*1:ちなみにこのお客様は5〜6年くらい前に「マイクロサービスで作ってみましょう」という提案も受け入れてくれました

Dapr Advent Calendar 23日目 - Daprのロードマップ

こんにちは Dapr Advent Calendar 23日目です。これまでのAdvent CalendarではDaprの機能や使い方を紹介してきましたが、今後、Daprはどういう方向が強化されるのでしょうか。そのロードマップを少し眺めてみたいと思います。

Daprのロードマップを見てみよう

Daprのロードマップは、Github Projectsで管理されています。

github.com

ロードマップにはいくつかのレーンが設けられています。

Backlog は対応予定だけど優先順位が確定していないもの。👍をつけていけば優先度を上げられるようです。

Planned (Committed) はだいたいマイルストーンが決まって設計を検討しているもの。

In Progress (Development) は開発中のもの。

DoneReleased はそれぞれ開発完了のものとリリース済みのものです。

もちろんすべてのIssueは紹介できませんので、In Progress、Planned、Backlogの中から、主要なものや面白そうなものをピックアップして紹介します。

In Progress

現在開発中の In Progress には、2021-12-23時点で8個のIssueがあります。4つが現在AlphaやBetaのコンポーネントをGAにするとか、作り直そうというものです。

残りの4つを簡単に紹介します。

Planned

今後対応予定の Planned には、2021-12-23時点で26個のIssueがあります。

品質周りのIssueが多く、現在AlphaやBetaのコンポーネントをGAにするとか、DaprコンポーネントのE2Eテストや結合テストを強化するなど、内部の品質を高めるissueが全体の半数を超えています。

残りの半数のうちから、興味深いものをいくつか紹介します。

Backlog

まだマイルストーンなど決まっていない Backlog には、2021-12-23時点で25個のIssueがあります。いつ出てくるかは分かりませんが、逆に言えば少し規模や影響の大きなIssueも多いです。

Pub/sub関連

分散トレーシング関連

ネットワーク関連

その他

また、ロードマップにはないのですが components-contribのissue で、次のIssueが重要なものとしてピン止めされています。

ECS上でDaprを動かせるようにしようという提案。以前にもこのAdvent Calendarの中で触れたことがありましたが、ECSで使えるようになればユーザーも増えるんじゃないかと期待しています。

まとめに代えて

全体を眺めた感じ、品質向上のテーマや既存機能の改善がほとんどで、とんでもない大物が待っているような気配はありません。まずはアーリーアダプターが使って満足できるように足場を固めているところ、という感じでしょうかね。利用者が増えてきたり、Azureと絡めて予算がつくようになってくれば、また傾向が変わるかも知れません。

ところで、「とんでもない大物」と言えば、ロードマップにはありませんが、こんなIssueがありました。

github.com

SQLを発行するためのAPIを作ろう、トランザクションも有効にしよう、というものです。さすがにそれは考えないようにしてるのかな、と思ってたのですが、少なくとも提案はされているようですね。ちょっと注目しておきたいIssueです。

そんなわけで、今回はここまでにしたいと思います。

明日のAdvent Calendarは、@backpaper0さんがDaprのActorについて書いてくれる予定です。Actorはよく分からなくて飛ばしたところなので、楽しみですね!

それでは!

Dapr Advent Calendar 22日目 - Dapr vs Spring Cloud

こんにちは Dapr Advent Calendar 22日目です。おとといから合宿をしていたため今日は更新が遅くなってしまいました。今回はDaprをSpring Cloudと比べてみたいと思います。

Spring Cloud vs Dapr

僕はもともとSpring BootとSpring Cloudを使って、いわゆるマイクロサービスを開発してました。

Spring CloudはいわゆるCloud Nativeな分散アプリケーションに必要となる要素をフルスタックで提供するプロダクトで、サービスディスカバリー、メッセージング/ストリーミング処理、分散トレーシング、サーキットブレイカーなどの機能をSpring Bootから扱いやすい形で提供しています。こんな機能をSpring Cloud以上に使いやすく提供しているプロダクトは、おそらく今のところ他にはないでしょう*1

一時期、僕はJava以外の言語、たとえばGoなどでマイクロサービスの開発をしようかと考えたこともあったのですが、結局はSpring Cloudを使いたいからSpring Boot、そのためにJavaJVM言語)を使うという選択をしていました。そうやって言語選択に影響するくらい、Spring Cloudは分散アプリケーション開発における重要なプロダクトでした。

そんなSpring Cloudと、Daprを、次の3つの機能で比べたいと思います。

  • サービス呼び出し
  • 非同期メッセージング
  • 分散トレーシング

この3つが、分散アプリケーションの柱ですよね。

1. サービス呼び出し

まずはアプリケーションから別アプリケーションを呼び出す機能を比較します。

DaprのInvoke API

Daprのサービス呼び出しはInvoke APIを利用します。このAdvent Calendarでも何度となく触れてきました。

cero-t.hatenadiary.jp

ソースコードは次のようになります。

@Value("${baseUrl}")
private String baseUrl;

@GetMapping("/invokeHello")
public Map<String, ?> invokeHello() {
    Map<?, ?> result = restTemplate.getForObject(baseUrl + "/hello", Map.class);
    return Map.of("baseUrl", baseUrl, "remoteMessage", result);
}

baseUrl の値に http://localhost:${DAPR_HTTP_PORT}/v1.0/invoke/hello-app/method というDaprのInvoke APIを指定することで、Dapr経由で対象のアプリケーションを呼ぶことができます。

Daprはアプリケーション名から対象サービスが動いているホストを発見するために、ローカル環境ではmDNS(マルチキャストDNS)を利用し、k8sではk8s自身が持つ名前解決機能を利用します。いずれも利用できない環境では、現時点ではConsulを利用する必要があります。

たとえば、Amazon EC2上でもConsulを利用すればDaprを動かすことができます。

cero-t.hatenadiary.jp

そのような場合を除けば、Daprは基本的にはサービスレジストリなしで運用できるところが強みですね。

Spring Cloud Service Discovery

Spring Cloudのサービス呼び出しは、名前解決のためにNetflix Eurekaを利用します。

spring.pleiades.io

名前解決のためのサーバとなるEureka Server(上で言うConsulに相当します)を立て、それぞれのアプリケーションではEureka Discovery Clientを利用して自分自身をEureka Serverに登録したり、他のアプリケーションの情報をEureka Serverから取得してアクセスするという仕組みになっています。

サーバ側であるEureka Serverのコードは次のようになります。

@EnableEurekaServer
@SpringBootApplication
public class ServiceRegistrationAndDiscoveryServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceRegistrationAndDiscoveryServiceApplication.class, args);
    }
}

@EnableEurekaServer アノテーションをつけたアプリケーションを起動するだけで、難しいことはありません。もちろん設定ファイルを追加して細かな設定をしたり、クラスタを組んだりすることができます。

クライアント側のソースコードは次のようになります。

@Value("${baseUrl}")
private String baseUrl;

@GetMapping("/invokeHello")
public Map<String, ?> invokeHello() {
    Map<?, ?> result = restTemplate.getForObject(baseUrl + "/hello", Map.class);
    return Map.of("baseUrl", baseUrl, "remoteMessage", result);
}

Dapr側のソースコードと同じです。

baseUrlhttp://hello-app というアプリケーション名さえ指定すれば、RestTemplateが自動的にEureka Discovery Clientを利用して対象のアプリケーションにアクセスします。URLのシンプルさで言えば、Daprを使うよりも分かりやすいですね。

どちらが良いか?

サービス呼び出しについて、DaprとSpring Cloud、どちらが良いでしょうか。

DNSを別に立てる必要がないDaprのほうが使いやすいですが、Spring Cloudはローカル環境でもEureka Serverを立てる必要があります。ただ、立てることは大して難しくないので、そこまでのデメリットとも言えません。

また、呼び出す際のURLはSpring Cloudのほうが分かりやすく、DaprのInvoke APIはどうしても長くなってしまいます。しかし、これもそこまで大きなデメリットと言えません。

判定としては「引き分け」というか、大差ないというのが正味の所ですね。

2. 非同期メッセージング

続いて、非同期メッセージングについて比較します。

DaprのPus/sub APIとSubscription

DaprではメッセージングのPub/sub APIを用いてメッセージを送信し、Subscriptionの設定ファイルとWeb APIを作るだけでメッセージを受信できます。

cero-t.hatenadiary.jp

メッセージを送る側のソースコードは次のようになります。

@Value("${pubsubUrl}")
private String pubsubUrl;

@PostMapping("/publish")
public void publish(@RequestBody MyMessage message) {
    restTemplate.postForObject(pubsubUrl, message, Void.class);
}

pubsubUrlhttp://localhost:${DAPR_HTTP_PORT}/v1.0/publish/rabbitmq-pubsub/my-message というDaprのPub/sub APIを指定して、メッセージブローカーにメッセージを送ることができます。

続いて、メッセージを受け取る側のソースコードです。次のようになります。

@PostMapping("/subscribe")
public void subscribe(@RequestBody CloudEvent<MyMessage> cloudEventMessage) {
    System.out.println("subscriber is called");
    System.out.println(message);
}

メッセージを受け取るWeb APIを作成するだけです。

このWeb APIを利用するために、次のような設定ファイルを作成します。

apiVersion: dapr.io/v1alpha1
kind: Subscription
metadata:
  name: subscription
spec:
  pubsubname: rabbitmq-pubsub
  topic: my-message
  route: /subscribe
scopes:
  - subscribe-app

詳細な説明は割愛しますが、メッセージブローカーからメッセージを受け取ると /subscribe という上で作ったWeb APIにアクセスするという設定を書いています。

シンプルに、やりたいことを実現できる感じです。

Spring Cloud Stream

Spring Cloudでメッセージングを行うためには、Spring Cloud Streamを利用します。

spring.pleiades.io

Spring Cloud Stream 2.xから3.xの間でAPIに大きな変化があり、よりストリーム処理に特化したような形になりました。ここでは3.xの記法で紹介します。

メッセージを送る側は次のようなソースコードになります。

@PostMapping("/publish")
public void publish(@RequestBody MyMessage message) {
    streamBridge.send("my-message-0", message);
}

StreamBridge クラスを使って、メッセージを送ります。

また、ソースコード内で指定したメッセージのキーに対して、どのメッセージブローカーに送るかを設定ファイルに書きます。

spring.cloud.stream.bindings.my-message-0.destination=my-message

ここで指定した値がRabbitMQのExchangeの名前として使われます。

続いて、メッセージを受け取る側のソースコードです。

@Bean
public Consumer<MyMessage> subscribe() {
    return (map) -> {
        System.out.println("subscriber is called");
        System.out.println(map);
    };
}

DaprのようなWeb APIではなく、java.util.funciton パッケージの ConsumerFunction などを使って実装します。

そして、メッセージを受け取った際にこのメソッド(Bean)を呼び出すよう設定ファイルを作成します。

spring.cloud.stream.bindings.subscribe-in-0.destination=my-message
spring.cloud.stream.bindings.subscribe-in-0.group=my-message-subscribe

上の値がExchangeの名前、下の値がQueueの名前として使われます。設定に若干クセがあって微妙に分かりづらいですね。

ただ、subscribe側を「API」などではなく「Function」と捉えて実装させるというのは納得感もあります。このバージョンのSpring Cloud Streamのソースコードを読んだことはないので推測になりますが、おそらくこの前段の処理は、メッセージブローカーからメッセージを1つずつ受け取って処理するのではなく、WebFluxを使ってノンブロッキングに複数の呼び出しをまとめているのでしょう。その方が性能的なメリットがありますからね。

どちらが良いか?

サービス呼び出しについては、DaprとSpring Cloud、どちらが良いでしょうか。

設定ファイルも含めた構成としては、Daprの方が分かりやすいです。しかし性能やメモリ使用量の少なさに関しては(計測したことはないですが)Spring Cloud Streamの方が上になると推測できます。特に大量のメッセージを同時に捌くような必要がある際には差が出そうです。

またDaprのほうはHTTPを使ってメッセージングをしていますが、Spring Cloud Streamでは独自クラスを使ってメッセージングをしています。そのためDaprの方がテスト時に別の処理に置き換えたり、curlコマンドでのテストがやりやすい一方で、HTTPを経由することのオーバーヘッドがあるとも言えます。

取り回しやすさではDaprに軍配が上がり、性能についてはSpring Cloudに軍配があがる、と言えるでしょうか。

3. 分散トレーシング

最後に、分散トレーシングについて比較します。

Daprの分散トレーシング対応

Daprでは設定ファイルを書くだけで分散トレーシングが有効になります。

cero-t.hatenadiary.jp

設定ファイルは次のような内容です。

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: daprConfig
spec:
  tracing:
    samplingRate: "1"
    zipkin:
      endpointAddress: http://localhost:9411/api/v2/spans

分散トレーシングのサンプリングレートやzipkinサーバのアドレスを指定するだけで、分散トレーシングが有効になります。

ただしトレースIDを自分で伝播させる必要があるため、次のようなコードを書く必要があります。

@GetMapping("/invokeHello")
public Map<String, ?> invokeHello(@RequestHeader("traceparent") String traceparent) {
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.set("traceparent", traceparent);
    HttpEntity<?> request = new HttpEntity<>(httpHeaders);

    Map<?, ?> result = restTemplate.exchange(helloUrl + "/hello", HttpMethod.GET, request, Map.class).getBody();
    return Map.of("baseUrl", helloUrl, "remoteMessage", result);
}

HTTPヘッダで受け取った traceparent というヘッダの値を、次のリクエストのHTTPヘッダに渡しています。これをすべてのエンドポイントの処理に書くことは現実的ではないですから、何か共通的な処理にする必要があるでしょう。その辺りは自分で用意しなければなりません。

Spring Cloud Sleuth

Spring Cloudで分散トレーシングを行うには、Spring Cloud Sleuthを利用します。

spring.pleiades.io

Spring Cloud Sleuthをdependenciesに追加して次のように設定ファイルを作成します。

spring.sleuth.sampler.rate=100
spring.zipkin.sender.type=web
spring.zipkin.baseUrl=http://localhost:9411

このように設定すれば、分散トレーシングが有効になりZipkinにトレース情報が送られます。

分散トレーシングは、RestTemplateやWebClientによるHTTP通信や、Spring Cloud Streamによるメッセージングなどすべてが対象になりますし、Daprのほうで問題となったトレースIDの伝播も自動的に行われます。

それだけでなく、トレースIDなどの情報がロガーのMDC(Mapped Diagnostic Context)に保存されるため、ログの設定を書けばログにトレースIDなどを出力することができ、トレースIDを起点としたログ検索がやりやすくなります。

このような事が何の実装もなく実現でき、トレースIDの伝播も自動で行われるのは、Spring Cloudがフルスタックで提供されていることが引き出すメリットだと言えるでしょうね。

どちらが良いか?

分散トレーシングについては、トレースIDの伝播も自動的に行うSpring Cloudの方が便利だと言えます。

また、Spring Cloud Sleuthではキュー経由でトレース情報を送ることでトレーシングの余計な待ち時間を減らしたり、Zipkin以外の様々な分散トレーシングツールと連携したりすることも可能です。

さすがは5年以上の歴史あるプロダクトだと言ったところでしょうか。僕が分散トレーシングという言葉を知ったのもこのSpring Cloud Sleuthがキッカケでしたし、他のプロダクトに先駆けてフルスタックで機能を提供してきた筋の良いプロダクトだと思います。

総合的な比較

ここまでDaprとSpring Cloudの3機能を比べてきましたが、総合的に見て、どちらの方が良いのでしょうか。

分かりやすさやHTTPによる疎結合という点ではDaprの方が上ですが、非同期メッセージングの性能や分散トレーシングの機能はSpring Cloudに強みがあります。歴史の浅いプロダクトと、歴史あるプロダクトの差、と言い換えることができるかも知れません。またこの3機能だけではなく他の機能を含めて、あるいは世の中にある情報量の差なども考えれば、Spring Cloudの方が上だと言えるでしょう。

バージョンアップに伴う苦痛

それではなぜ、僕はSpring CloudをやめてDaprを選んだのでしょうか。一言でいうと「バージョンの互換性やバージョンアップに伴う問題」が要因でした。

たとえば古いバージョンのJavaとSpring Bootで運用しているシステムがあり、新システムではより新しいバージョンのJavaとSpring Bootで開発しようとした場合、それぞれのシステムでSpring Cloudを使おうとすると、それぞれのSpring Bootに対応したSpring Cloudのバージョンが異なってしまうため、そこで互換性が失われていることがありました。たとえばSpring Cloudのバージョンアップに伴って、内部で使っているEurekaのバージョンが上がった際にプロトコルが変わってしまったことがありましたし、また上にも述べたとおりSpring Cloud Streamは2.xと3.xで大きくAPIの形が変わっています。

であれば常にJava、Spring Boot、Spring Cloudを最新バージョンに更新し続ければ良いのかも知れません。ただ、Spring Cloudはバージョンアップに伴う作業が大がかりになりがちでした。これはCloud Nativeという分野が比較的新しいため、対応するプロダクトやトレンドが変わり、それにSpring Cloudが追従しようとした際に互換性を失わざるを得ないという面があったのだと思います。

またもう少しマイナーな問題としては、Spring Bootの提供スピードとSpring Cloudの提供スピードの違いや依存性の複雑さなどにより、Spring Bootのバージョンを上げようとすると、Spring Cloudがまだ対応していないとか、参照しているライブラリのバージョンが異なってエラーになることがありました。

その辺りの痛みを経験したことで、Spring Cloudが悪いというよりは、「Web APIを提供するアプリケーションと、その下支えをするインフラとの境界になるレイヤーは、もっと疎結合にして、それぞれ個別にバージョンアップできるようにしたほうが良い」と考えるようになったのです。

それがSpring CloudではなくDaprに使うに至った経緯です。

まとめ

  • サービス呼び出しについては、DaprもSpring Cloudもあまり変わらない
  • メッセージングについては、Daprの方がシンプルだがSpring Cloudのほうがおそらく性能が良い
  • 分散トレーシングについては、DaprよりもSpring Cloudの方が高機能だし使いやすい
  • Spring Cloudはバージョンアップに伴う苦痛が大きかった

ところで、最近しばらくSpring Cloudをあまり追っていなかったためドキュメントなどを見直しながら今回のブログを書いたのですが、Spring Cloudのドキュメントはどこに何があるのか少し分かりづらくなっていて、どうしちゃったのかなという気持ちになりました。歴史あるプロダクトに様々な機能がついて複雑になっていく中で、ドキュメントも複雑になっていくということがあるのかも知れません。もちろん、その辺りを補うようなガイドや、ブログ、発表資料なども多いのが、Springの良いところなのですけどね。

いずれにせよ、SpringはJavaのCloud Nativeアプリケーションの開発を牽引する立場として、今後も応援したいと思います。

それでは、また明日!

*1:もしあればこっそり教えてください

Dapr Advent Calendar 21日目 - Daprの開発環境

こんにちは Dapr Advent Calendar 21日目です。今回からは実践編として、実際に開発する上で役立つノウハウやTipsを紹介したいと思います。

Daprの開発環境

ここまでDaprの機能を説明してきて、なんだかよさげには見えるけど、実際に効率良く開発できるのかについてはまだ疑問があるかも知れません。今回は開発の効率に直結する「開発環境」や「デバッグ」について話したいと思います。

DaprのIDE Support

DaprのIDE対応について、公式ドキュメントにはVisual Studio CodeIntelliJについて記載されています。

docs.dapr.io

Visual Studio CodeIntelliJのどっちを使うと良いですかって聞かれれば、そりゃJava開発なんだから今はIntelliJでしょう、としか答えられないのですが、Visual Studio Code対応がどのようになっているのかを見ておきます。

Visual Studio Code + Dapr extensionの所感

Visual Studio Codeの拡張としてDapr extensionが提供されている。このextensionでできることは次の通りです。

  • componentファイルのscaffold作成(pubsub、statestore、zipkinの設定ファイルの作成)
  • dapr起動コマンドのscaffold作成
  • 起動中のDaprアプリケーション一覧の表示
  • 起動中のDaprアプリケーションに対するinvokeとpublish

設定ファイルのscaffoldは ~/.dapr/components と同じファイルを生成するだけなので大したことはありません。デモ用ですかね。

dapr起動コマンドのscaffold作成は、Visual Studio Codeのlaunch.jsondapr run でアプリケーションを起動するconfigurationを追加するというものです。dapr run コマンドを手打ちしなくて済むようになります。

Daprアプリケーション一覧は、次のように表示されます。

f:id:cero-t:20211221005228p:plain:w300
Dapr extensionで見るアプリケーション一覧

起動中のアプリケーション一覧が表示されます。dapr list コマンドで取得できるものと同様ですね。ちなみにk8s上にデプロイしたDaprアプリケーションは見えませんでした。

これらのアプリケーションを選択し、invokeやpublishを実行することができます。invokeする時にわざわざcurlコマンドを叩いたり、ちょっと長めのURLを打ったりしなくて済みます。

Dapr extensionが提供する機能はこのくらいなので、そんなに高機能というわけでも生産性がすごく上がるというわけでもないですが、Daprアプリケーションの簡易UIとして使うのも悪くないという印象でした。

IntelliJによるデバッグ

続いて、IntelliJによるDaprアプリケーションのデバッグ方法についても説明しておきます。

対象のソースコードGitHubにあるHello Worldアプリケーションを利用します。

https://github.com/cero-t/dapr-advent-2021/

ここにある「hello」モジュールを利用します。

Daprの単独起動

まずはDaprを単独で起動します。公式ドキュメントではIntelliJのExternal Toolsを利用していましたが、わざわざ使う必要もないので次のコマンドでDaprを起動します。

dapr run --app-id hello-app --app-port 8080 --dapr-http-port 18080

これでDaprが単独で起動します。まだアプリケーションは起動していません。

IntelliJ側でアプリケーションを起動

続いて、IntelliJhello モジュールの HelloApplicationデバッグ起動します。HelloApplication を右クリックし、Debug 'HelloApplition' を選択するなどで起動できます。

f:id:cero-t:20211221010340p:plain
IntelliJデバッグ起動

アプリケーションを起動したら HelloControllerreturn Map.of("message", "Hello, world!");ブレークポイントを張っておいてください。

アプリケーションへのアクセス

続いて、次のコマンドでアプリケーションにアクセスします。

curl localhost:18080/v1.0/invoke/hello-app/method/hello

このコマンドを実行すると、IntelliJ側で指定したブレークポイントで止まっているはずです。

つまり、Daprを単体起動し、IntelliJからDaprで指定した app-port のアプリケーションを起動すればDapr経由でアプリケーションにアクセスできる状態になるのです。この方法で起動すれば、IntelliJ側でデバッグができるということです。仕組みが分かってしまえば簡単な話ですね!

いつどのツールを使って開発するのか

これまで半年くらい業務でDaprを使って開発し、実運用も始めているのですが、正味の話こんな風にデバッグをできることは、いま初めて知りました。

なぜこれまで知らなかったというと、ローカル環境で開発する時には(担当範囲にもよるのですが)Daprを使うことはあまりないためです。その辺りについて説明したいと思います。

Daprアプリケーションの開発の実際

例として、運用するシステムが Amazon EKS + Dapr + RabbitMQ + PostgreSQL + Spring Boot という構成だったとします。

その際、開発フェーズごとに使うツールは次のようになります。

  • 開発時
  • Dapr経由での動作検証時
    • Java + Docker + Dapr
  • k8sでの動作検証時
    • Java + Docker + Dapr + Minikube + kubectl
  • Amazon EKSでの動作検証時
    • Java + Docker + kubectl

この辺りを順番に説明していきます。

開発時 (Java + Docker)

アプリケーションを開発する際には、Spring Bootで開発するためにJavaJDK)を利用し、PostgreSQLを利用するためにDockerを利用します。ここでDaprは使いません。

これまでのAdvent Calendarでも少し説明していましたが、ローカル環境で開発する際にはアプリケーション同士を直接呼び出しています。

Invoke APIを使わず直接呼び出し

たとえばInvoke APIを使ったアプリケーションのコードは次のようになります。

@Value("${baseUrl}")
private String baseUrl;

@GetMapping("/invokeHello")
public Map<String, ?> invokeHello() {
    Map<?, ?> result = restTemplate.getForObject(baseUrl + "/hello", Map.class);
    return Map.of("baseUrl", baseUrl, "remoteMessage", result);
}

ここで baseUrl としてローカル環境で開発する際には http://localhost:8080 を指定します。そうすれば、ポート8080番で起動している別のアプリケーションにアクセスできますし、あくまでもSpring Bootだけの世界に閉じて開発ができます。

そしてDaprを利用する際には http://localhost:${DAPR_HTTP_PORT}/v1.0/invoke/hello-app/method というInvoke APIのURLに差し替えるのです。

このようにすればローカルではDaprなし、検証や運用はDaprあり、と切り替えることができます。

Pub/sub APIを使わず直接呼び出し

HTTPで呼び出す同期処理はそれで良いとして、非同期処理はどうなるでしょうか。

たとえばPub/sub APIを使ったアプリケーションのコードは次のようになります。

@Value("${pubsubUrl}")
private String pubsubUrl;

@PostMapping("/publish")
public void publish(@RequestBody MyMessage message) {
    restTemplate.postForObject(pubsubUrl, message, Void.class);
}

そしてメッセージを受け取るsubscribe側は次のように実装します。

@PostMapping("/subscribe")
public void subscribe(@RequestBody CloudEvent<MyMessage> cloudEventMessage) {
    doSubscribe(cloudEventMessage.data);
}

@PostMapping("/doSubscribe")
public void doSubscribe(@RequestBody MyMessage message) {
    System.out.println(message);
}

上がDaprから呼ばれる際にCloudEventのエンベロープがついたクラスを引数にしたメソッドです、下がその中のメッセージを使って実処理を行うメソッドです。

このような構成にして、ローカルの開発時には pubsubUrllocalhost:8084/doSubscribe などを指定して直接同期呼び出しをするようにします。そうすればDaprのことを気にせず、Spring Bootの世界に閉じてビジネスロジックに注力して開発ができます。しかも呼び出しが同期となるため、JUnitなどの自動テストの際に非同期処理が終わることを1秒待つ、みたいなことをしなくても、処理が終わればすぐにassertに進むことができます。

そしてDapr利用時には pubsubUrlhttp://localhost:${DAPR_HTTP_PORT}/v1.0/publish/pubsub/my-message などのPub/sub APIのURLにするのです。このようにすればローカルではDaprなし、検証や運用はDaprあり、と切り替えることができます。

そんな切り替えをして、Daprを使ったときに問題が起きることはないか? と疑問に思うかも知れませんが、もちろん何か問題が起きることはあるかも知れません。ただ基本的にはごく少しのコード修正だけで済むものです。

このような方針にするためにも、Dapr Java SDKは使わず、HTTP通信をするためのクライアントだけを利用しています。

Dapr経由での動作検証時 (Java + Docker + Dapr)

Daprのpub/sub機能が正しく動くかどうか試したい場合や、Daprの分散トレーシングで渡される traceparent ヘッダを利用した処理を行いたい場合には、上の構成に加えてDaprをインストールして動作検証を行います。

やや極端ですが、この時点ではDaprのAPIさえ利用すれば利用するミドルウェアは何でも構いません。たとえば、実際にRabbitMQを用いたpub/sub機能を使って開発をしていたメンバにヒアリングした所、開発時にはDaprが勝手にインストールするRedisを使っていたと話していました。開発時にRedisを使い、検証時からRabbitMQを使っていても、設定ファイル以外は変える必要がなく、何の問題もなくアプリケーションは動作していました。

もちろんRabbitMQ固有の機能、たとえばDead Letter Exchange (DLX)などを用いた処理の開発や検証を行うのであればDocker上にRabbitMQをデプロイして検証する必要がありますが、あくまでもpub/subの主機能の検証を行いたいだけであればRedisを使っても構わないのです。

別にそのような方針を推奨するわけでも何でもないですが、Daprがミドルウェアを抽象化するメリットがこういう所で活きてきます。

k8sでの動作検証時 (Java + Docker + Dapr + Minikube + kubectl)

アプリケーションの開発が終わり、Amazon EKSなどにデプロイする前には、k8sでの検証が必要となります。その場合にはMinikubeが必要となります。インフラ構築を担当する2〜3名ほどだけがローカルにMinikubeをインストールして検証をしていました。もちろんkubectlなどの関連ツールも使います。

逆に言えば、インフラを触らないアプリケーション開発者はMinikubeのインストールすらしていません。k8sに難しいイメージを持っていて、「k8sを使わなければならない」というだけで抵抗があるエンジニアにとっては、k8sを使わずに開発できるという方針にしたほうがハッピーでしょう。

Amazon EKSでの動作検証時 (Java + Docker + kubectl)

Minikube上での検証が終われば、また必要なツールは減ります。イメージを作成してEKSにデプロイするだけですから、イメージのビルドのためのJavaとDocker、またデプロイするためのkubectlが必要となるだけです。DaprやMinikubeは必要ありません。

ただしEKS上で問題が起きた際にログなどを確認できるよう、開発者全員がkubectlを利用できるようにすべきです。ログやメトリクスをCloudWatchやDataDogに集約していれば、開発者がkubectlを利用する機会は減るでしょう。

別にDevとOpsを分離することが正しいのだと主張するつもりはないですが、僕は「それぞれのフェーズにおいて注力すべき部分に注力する」というプロセスを大事にしており、それを実現できるという点で、Daprをとても気に入っています。

まとめ

  • Visual Studio CodeのDapr extensionを使えば、起動中のDaprアプリケーション一覧を表示したりinvoke/publishなどを容易に行えます
  • Daprを単体起動すれば、自分の好きなIDEを使ってアプリケーションをデバッグ起動し、Daprを使った処理のデバッグができます
  • 開発のフェーズごとに、注力すべきことに注力するという構成を取りやすいです
  • 開発時にはDaprもMinikubeもなしでビジネスロジックの開発に注力することができます
  • 検証時以降はそれぞれに必要なツールを少しずつ増やして環境を構築するという方針が良いでしょう

こんな風にしてDaprのアプリケーション開発を進めていました。もちろんこれから変わる所もあるでしょうし、今後も方法論を磨いていきたいと思います。

それでは、また明日!