2019/11/17(日) JAZUG沖縄のミートアップを開催いたしました!

2019/11/17(日) JAZUG沖縄のミートアップを開催いたしました!

jazug-okinawa.connpass.com

自分はPaykeに転職してからずっとAzureを使っていて来ていて、大体1年1回ほどはAzureに関連する勉強回を開催していたのですが、継続的に沖縄でAzureのユーザーが集まれる回を作っていきたいと思い、この度JAZUG( Japan AZure User Group) 沖縄としてミートアップを開催することとなりました。

今回JAUG沖縄のミートアップを開催するということで、非常に多くの方に応援のコメントもいただいたり、一緒にコミュニティを運営していきたいという方が出てきてくれたり、実際にMicrosoftの横井さんにリモートでセッションをしていただいたりと、様々な方にご協力頂きました。

圧倒的感謝!!でございます。

今回イベント内容について

最初のセッションとして、横井さんに「AI 使って、何しよう? ~ あなたの挑戦が、未来を実現する: Microsoft の AI の世界 ~ 」というテーマでMicrosoftが提供しているAI系サービスの特徴やそれを利用していくための勘所などを紹介して頂きました。

1,2年前ほどに勉強回でAzureのAI系サービスについて話を聞いたことがあったのですが、そこから精度が向上していたり、サービスのラインナップ自体も増えていたりと、この分野は継続的にキャッチアップしてプロダクトにどのように活かしせるか常に考えておくと非常に面白いなと思いました。(個人的にはTranslate系をもっとキャッチアップしておきたい。。。)

次に僕が「Azure Web Appはいいぞ!」というタイトルでAzure Web AppというAzureのPaaSの良さを発表させて頂きました。

EBILABという飲食店をデータ駆動で経営改善を支援するBIツールを提供する会社の開発チームから、3人が登壇して頂きました。

@r_nakamineさんが「様々なデータの統合を支えるETL基盤をAzure Serverless コンポーネントで構築」、@puremoru0315さんが「Azure ServerlessでWeb Application開発」、@saboyutakaさんが「10x Serverless Product Development for a Startup with Microsoft Azure」というタイトルでそれぞれ発表していただき、EBILABさんの開発の中の構成がすべて見えてくるとても充実した内容でした。

EBILABさんの技術選定やアーキテクチャ構成などは一貫して「分業可能にすることで生産性を上げる」というコンセプトが通っていて、その分業可能なアーキテクチャを作る上でAzureの様々なマネージドサービスやServerlessテクノロジーを駆使することで本来大規模開発になるところを少人数でも開発可能にしているようでした。

アーキテクチャ選定の思想や紹介してもらった様々なサービス群についてすぐにでも使える情報が多かったので、非常に勉強になりました。

皆さんの登壇が終わったあとは、Microsoft Learnを使って実際にAzure Functionsを触ってみようということで@saboyutakaさんにリードしてもらってMicrosoft Learnを体験してみました。

docs.microsoft.com

今後について

JAZUG沖縄はできるだけ継続的にミートアップを開催して、みんなでMicrosoft Learnを使ったもくもく会を開催したり、県外からのゲストを招いて様々な事例やAzureの最新動向をキャッチアップできる会を作ったりしていきたいと思いますので、今後ともよろしくお願いします。

やっていくぞ!!!

PHPカンファレンス沖縄2019に、個人としては運営スタッフ&登壇者、Paykeはスポンサーとして参加しました!

PHPカンファレンス沖縄2019に運営スタッフ & 登壇者、僕が所属する会社であるPaykeはスポンサーとして参加しました!

phpcon.okinawa.jp

総勢140名近くの方が参加していただく規模のカンファレンスになり、沖縄だとハッカーズチャンプルーに次ぐ規模のカンファレンスになりました。

ハッカーズチャンプルーは沖縄の各エンジニアコミュニティが協力して開催しているコミュニティですが、一つのPHPというコミュニティだけでこの規模のカンファレンスができ、しかも多くの県外から参加者も来ていただけたのはすごく良い事例になったと思いますし、参加者としても非常に楽しめたカンファレンスになったと思います!

スポンサーとして

今回始めての沖縄で開催させるPHPカンファレンスということで、少しばかりの応援としてPaykeもスポンサーさせていただきました。

f:id:arakaji-yuu:20191014145119j:plain

f:id:arakaji-yuu:20191014145146j:plain

このようなカンファレンスで僕らのロゴが掲載されて、全国から来ていただいたPHPerの方々に少しでも認知していただけたなら嬉しい限りです。

弊社CTOの花城はゲストスピーカーとして、「スタートアップにおけるプロダクト開発とエンジニア組織の変遷」と言う内容で登壇しました。 沖縄でPaykeぐらいのスピード感で成長するスタートアップ企業はこれまでなかったので、その成長を遂げたエンジニア組織がどのように変化してきたのかという話は多くの方に興味を持っていただけたようで身内として非常に嬉しい限りです。

あと弊社CTOの登壇に関する反響として一番多かったのは「CTOイケメンですね!」でした笑(うらやましい。。。

登壇者として

今回ボクは「技術基盤/SREの視点で取り組む、サービスの成長を継続し、加速させるためのPHPアプリケーション改善」というタイトルで登壇させていただきました。

Paykeの開発組織の中で技術基盤という組織を作り、解決してきた課題の事例を通じてサービスの成長を継続させ、そして成長させていくためにどのようなことを考えながら意思決定しているかを伝えたいと思って今回の登壇内容を作りました。

30分枠頂いてたのですが時間オーバーしてしまい、最後のほうは一部省いてしまったのですが、一番伝えたかったのは以下のページに書いた「事業目標を理解し、ボトルネックを解消する」ということです。

技術基盤だからといってビジネスのことを考えなくて良いわけでは決してなく、むしろビジネスと事業の成長戦略、そして目標を理解し、その道の先にある技術的ボトルネックを見抜き予めかじめ取り除く、またはその戦略執行をより効率的・効果的に行えるような技術的な武器を整えておく、のようなことがPaykeの技術基盤には求められていて、これまでもそれを意識して意思決定を行ってきました。

この登壇を通して「ビジネスを理解して、それを支えかつ強化する技術的な意思決定をすることが重要」ということをうまく伝えたかったのですが、うまくいったのかはあんまり自信がない。。。

いろんな方に「新垣さんの発表が一番楽しみです!」と言って下さったので、期待に答えられただろうか・・・

人前で話す事自体には最近だいぶなれてきたんですが、うまく伝えられるようになったかというそうでもない気がするので、次からは「伝えたいことを正しく伝える」ということをもっと意識して登壇に望みたいと思います。

参加者として

今回は運営スタッフとしての役割上、ずっとtrack_aの会場にいました。 すべての登壇内容が面白く、かつ勉強になるものばかりで非常に楽しかったのですが、個人的なベストセッションを挙げさせていただくとランチセッションの「小さな機能、大きな仕事」が最高でした。

自分もリファクタリングするときやもしくは自分が新規開発するときの基本的な考え方として「小さくシンプルな役割をもったプログラムをまず作り、それを組み合わせることで大きな役割をするプログラムを作る」というのを意識して行っています。

それは「UNIXという考え方」という本で学んだことや、Clojureのような関数型言語でプログラムを作るときの作法から影響をうけてそうなっていたのですが、頭の中にぼんやりとあるだけでうまく言語化はできていませんでした。

今回のこのセッションでは実際にもう作られて負債化しているシステムの要因分析やそれに対する改善方法のアプローチとして僕が暗黙知的に意識していた「小さなプログラムを組み合わせて大きなプログラムを作る」ということがうまく言語化されていて、登壇中も「あー、そうそう!そういうことなんですよね!!!」とこころの中で首がもげるかというほど頷いてました。

フィーチャーとケイパビリティという言葉で「フィーチャー=ユーザーからみた機能(大きな仕事)」と「ケイパビリティ=フィーチャーを実現するための構成要素として必要な能力(小さな機能)」を明確に分けて話されていたのが印象的で、僕の辞書にはその言葉がこれまでなかったので非常に勉強になりました。

「登壇内容めっちゃ良かった!」と伝えるのと「ajito.fmのファンでめっちゃ聞いてます!」ということを直接伝えられなかったのが個人的には心残り。。。

スタッフとして

今回PHPカンファレンス沖縄は最初はスタッフとして関わる予定ではなかったのですが、実行委員長のカンボさんに直接誘っていただいたのがきっかけでコアスタッフとして途中参加させていただきました。

僕がやったことは以下のような感じです。

  • Paykeにスポンサーとして参加してもらう。
  • キーノートスピーカーとの調整
  • 当日のビデオ撮影(スタッフ用)
  • 後片付け

今回のイベントは他のコアメンバーがすごく頑張っていて「途中から参加したのにあんまり貢献できていないな~」とちょっと罪悪感的なことも頭の中には浮かんでいたのですが、こういうカンファレンスは小さなことでも自分ができることをやってくれる人達の集合で成り立っているので、ここは罪悪感を忘れて「自分も運営に貢献したぞ!」と自信を持って言いたいと思います。

おれも自分のできる範囲で運営がんばったぞー!!!

スペシャルサンクス

今回のイベントが無事開催できたのは、会場提供してくださった株式会社プロトソリューションさんの全面協力のおかげでした。

会場提供だけではなく、会場準備や後片付けにもスタッフを参加させてもらって協力してくれるだけでなく、当日の備品で足りないものが見つかったときに会社にある備品を貸し出してくださったりと非常に多くのサポートをしてくださったことで今回のカンファレンスを無事やり遂げる事ができたと思います。

株式会社プロトソリューションさん、本当にありがとうございました!!

会議終了後に必ず作成してもらうアクションサマリー(または議事録)

会議は意思決定のためにするもので、かつその意思決定を正しく素早く伝達するために、自分は会議終了後には基本的には必ずアクションサマリーを書いて共有するようにしている。

アクションサマリーとは、会議で話して決まった以下の内容を簡単にまとめたものです。

  • 何を目的に議論したのか
  • 結果どうなったのか
  • アクションリスト(誰が、いつまでに、なにをやるのか)

自分は必ずやるようにしているが、それを組織全体に適応するためにはどうすればいいかな〜と考えているときに 「マーケティングとは「組織革命」である。」という本を呼んでいたら、会議後に提供するアクションサマリーについて書いていた。

この本によると、アクションサマリーは以下の内容を描く必要があるそうです。

  1. その会議の目的がなんだったのか?
  2. そして結論はどうだったのか?
  3. 結論に至る議論された主な内容はなんだったのか?
  4. 結論に基づき、関係者が次に取るべきアクションの明示(誰が、なにを、いつまでにするのか? )

そしてこの内容を会議から24時間以内に全体に公開することが義務付けられれているそうです。

これにより、まず会議の関係者に正しい素早くアクションしてもらうためのシステムとして機能するとともに、情報や意思決定の透明性を上げることを組織に浸透させていくという2つの効果があるそうです。

これをもとに、僕の組織でも使えるように以下のようなアクションサマリーのテンプレートを作成しました。 情報共有ツールとしてesaというサービスを利用しているので、テンプレートもMarkdownで書いています

- 日時: 
- 参加者: 

# 会議の目的

# 結論

# 結論に至る議論された主な内容

# アクション(誰が、なにを、いつまにするのか?)

これでうまく回るか試してみます。

thisweekと打つと、月曜日の日付〜日曜日の日付(例: 2019/09/16(月)〜2019/09/22(日))を出力するコマンドを書いた

毎週月曜日に今週やりたいことなどをメモに書き出すっていうことをやっているのですが、そのときのメモのタイトルを毎回 月曜日の日付〜日曜日の日付(例: 2019/09/16(月)〜2019/09/22(日)) にしている。

ただ、それを毎回月曜日と日曜日の日付をカレンダーで調べて書くのが面倒過ぎたのでrubyで簡単なスクリプトを書いた。

Image from Gyazo

コードはgistに公開しています。

gist.github.com

これでほんのちょっとだけ楽になる。

#てらだよしおまつり、沖縄開催でDocker/k8sのハッカソンを2日間行いました!

#てらだよしおまつり、沖縄開催でDocker/k8sハッカソンを2日間行いました!

2019年8月24日(土)〜2019年8月25日(日)の2日間、CODEBASEを会場にお借りして、Microsoftの寺田佳央さんを講師にDocker/k8sハッカソンを開催しました!

寺田さんは今回JavaKubernetesの関連の勉強会で全国ツアーを行うという挑戦を行っており、今回のその全国ツアーのファイナル、沖縄開催を僕がJavaKuecheとして運営させていただくことになりました。

java-kuche.doorkeeper.jp

www.protosolution.co.jp

ハッカソンで、僕が中級者チームのリードをすることに

別記事として書いているのですが、僕はこのコミュニティハッカソンの数日前に、会社で寺田さんとAzure Kubernetes Serviceのハックフェストを行っておりました。

arakaji.hatenablog.com

そこでKubernetes自体やAKSについて学びながら、このコミュニティイベントの打ち合わせもしており、そこで寺田さんから「初級者、中級者で2チームに分けてモブプロ形式で進めたいので、プロジェクターか大きなディスプレイがふたつあるとありがたい」と言われていたので、コミュニティイベントの当日は会場のスタッフと一緒にプロジェクターを2つ用意しておきました。

その時に単純な疑問として、「寺田さん一人で2つのチームのモブプロってどうやるんだろう?」と思っていたのですが...

当日になってイベントが始まり、中級者と初級者のチームに分かれて僕は中級者に入っていると、寺田さんから「ハッカソン用の資料はあるので、それに沿って中級者チームは新垣さんがリードして進めていってもらえますか? 僕は初級者チームと一緒に進めて行きますね!」とおっしゃられて急遽中級者チームのハッカソンをリードすることに!

最初は心の準備が出来ておらずうまくできるかどうか不安だったのですが、実際に資料を見ながら進めていくと数日前にやった寺田さんとのハックフェストで学んだことがしっかりと頭と体に染み付いて、思いの外うまくすすめることができました。

もちろん忘れてしまっていたことなどもいくつか有りましたが、そこは一緒にモブプロしている参加者の人と調べたり、寺田さんに助けて頂いたりすることでアウトプットと復習のサイクルがバンバン回って自分の中で理解がより深まっていきました。

ハッカソンも無事終わり、イベント参加者からも「わかりやすかった」、「面白かった」という良い感想を頂いたり、寺田さんや一緒に来ていただいたクラウドアドボケイトチームのマネージャーであるクリスティーナさんにも「ファシリテート素晴らしかった」とお褒めの言葉をたくさん頂いたので、僕自身もたくさん学べて楽しかったし、みなさんも多くを学んで楽しめた良い回に出来たのだろうと満足しています。

懇親会 〜アジャイルブロックチェーンと日本の政治とマイクロサービス〜

ハッカソン最終日の打ち合げで近くの居酒屋をお借りして懇親会を行ったのですが、個人的に面白かった話の流れがあったのでご紹介します。

会場では席が二席に分かれていて、まず僕がいる席に寺田さんが座っていて、そこでの話題はアジャイルやマイクロサービスについてでした。

いまの世界はビジネス=ソフトウェアの時代であり、そこで勝っていくにはアジャイル、DevOps、そしてその後のマイクロサービスみたいなことを理解して実践していかないといけない。 ただ、ビジネスを作る人や経営者にとってアジャイルやDevOpsみたいなワードを直接出す必要なくて、新しいバージョンのソフトウェアを速く出せるだとか変化に対応できるだとかの、今抱えている直接的な課題を解決するということだけ理解してもらえば良くて、彼らにとってはそのための手段は別に何でも良い。 たとえばバリューストリームマッピングをすべて洗い出し、そこで新しいバージョンをリリースするために工数がかかっているボトルネックを洗い出し、それをなにか手法やツールやアーキテクチャによって解決し、それによって工数がどれだけ短縮されるなどを提示するとアジャイルやDevOps、マイクロサービス、A/Bテストなど僕らがやりたいことは直ぐに実行することができるし、逆にいうとそういうメリットがないのであればやる必要もない。

のような話をしていました。

そのあと、寺田さんと一緒にイベントに来てくれたMicrosoftのクリスティーナさんが席を変わりました。

クリスティーナさんはMicrosoftでのクラウドアドボケイトチームのマネージャーという仕事のほかに、ブロックチェーンを用いたデジタルアイデンティティの証明というプロジェクトを行っており、その活動が評価されてForbesの世界に影響を与えるUnder30の30人のうちの一人として選ばれている方です。

彼女が取り組んでいるデジタルアイデンティティ証明のプロジェクトの話や、今のブロックチェーンのトレンドなどの話をしているとなぜか日本の政治についての話題に変わり、その場の誰がどの発言をしたかはもう忘れましたが

「日本の場合は、法律が作られるけど作られた法律を削除したり時代に合わせて解釈し直すみたい事が少なくて時代遅れの法律が残ってそれを守らないといけずに改善遅れるケースが多い」

「新機能を作るけど、リファクタリングやいらない機能の削除とかしてなくて肥大化しているんですね〜。これ、法律の矛盾をつくらないように新しい法律を作るのも、どんどん難しくなって法律作成のコストが上がってそう」

「法律もうまくいくかどうかわからないんだから、まず小さくリリースしてA/Bテストしたいですよね〜。」

「日本自体が大きなモノリスなアプリケーションになっているからね〜。まず都市とか県とかの単位でサービス分割して権限移譲し、そこの範囲でアジャイルPDCA回せるようにしないといけない」

「そうしたらService単位で失敗したり障害発生しても他のサービスに影響起きないようにできるから大胆に政治でもチャレンジできるね」

「でも、それぞれの都市で共通に必要な機能やコミュケーションはどうする?」

「全Serviceに必要な機能はアスペクト指向的に横断的関心をマイクロサービス基盤側が用意して各Serviceに注入できるようにする必要があるね。Istio的なものが求められる」

など、寺田さんと話していたアジャイルやDevOpsの話を日本の政治の話とつなげて、ソフトウェアエンジニアリングの発想で日本の政治をリアーキテクチャするアイディアが話されていて、面白おかしくありつつも、以外と的を得ているのではないかとおもって非常に印象的でした。

最後に

今回東京から来ていただいた寺田さんにクリスティーナさん、参加してくれた皆様、会場を提供してくれたCODEBASEさん、本当にありがとうございました!

いま僕は仕事でAzureを使っていますが、いま使っていること以上にAzureのテクノロジーを楽しみながらうまく使いこなし、世の中に大きな価値を出すプロダクト作りたいと考えていて、その一環で今後もAzureに関する勉強会などを開催していくと思います。

そのときはまた、皆様のご参加・ご協力をお願いいたします!

Microsoft Hackfest(マイクロソフト ハックフェスト) | ペアプロした2日間、自社サービスをAKSに乗せる過程で学んだこと

2019年8月21日(水)〜22日(木)の2日間、Microsoftクラウドアドボケイトである寺田佳央さんに弊社に来ていただいてHackfestを開催して頂きました。

Hackfestとは、Microsoftのエンジニアの方と自社が抱える課題をテーマとして決め、それを一緒に解決していくというイベントです。

今回はAzureを使った基盤をテーマにしていて、Azureを使ったインフラを管理しているのは弊社で基本僕のみのため、寺田さんと2日間がっつりペアプロできるめちゃくちゃ貴重な機会でした。

非常に多くの学びが得られる濃密な体験だったため、その学びをブログで共有したいと思います。

テーマ決め 〜 イシュー(課題)ドリブンでまず始める 〜

今回Hackfestを開催していただけた経緯が2つあります。 1つは僕が自社のサービス基盤としてKubernetesを使うことを考えていて、Kubernetesを学んでいることをTwitterに投稿していたのを寺田さんに見ていただいたこと。 もう1つは寺田さんがちょうど Java や Azure Kubernetes Serviceに関するコミュニティイベントを日本全国で行うという ジャパンツアーを行う計画をしており、その沖縄開催を行うので、その数日前にハックフェストをやりますか?という提案を頂いたことでした。

その経緯から、Azure Kubernetes Serviceについてのハッカソンをとりあえず始めるという流れになるかな〜と考えていたのですが、寺田さんがまず最初に行ったのは「御社の現状の開発体制やシステム/サービスの構成、そしてなぜKubernetesを使いたいと考えていたのですか?」と僕らの現状と課題をヒアリングするところからでした。

寺田さんが今回のハックフェストやその後行ったコミュニティイベントでも終始おっしゃっていたのが、「Kubernetesも課題を解決するための道具でしかない。すべてをKubernetesでやるべきではなくて、Kubernetesが得意でかつ自分達の課題に合っているところだけを使いましょう」ということでした。この姿勢は本当にプラクティカルで、僕も同じような姿勢で仕事しようとはしていますがたまに「この技術が流行っているから導入したい」になってしまうことがあります。しかしエンジニアとして「技術を用いて課題を解決すること」を職務とする以上、この姿勢は見習わないといけないと思いました。

寺田さんにヒアリングしてもらった中で整理された現状と、僕がKubernetesで解決したいと考えている課題は以下の通りでした。

  • 現時点では数個のサービスが稼働しているだけなのでKubernetesなしでも問題ない
  • ただし、今後1年以内にどんどん新しいサービスがリリース、既存サービスのアップデートがされていく可能性が非常に高い
  • 製品開発のエンジニアはどんどん採用を強化していくが、SREの方は増えてもあと一人くらい。
  • 最小人数のSREで今後加速度的に増えるサービス群のサーバー構築、CI/CDの構築、スケーラビリティの確保、ログ収集やモニタリングなどを扱っていくとSREがサービス提供のボトルネックになってしまう可能性があるので、それらをKubernetesとそのエコシステムを使うことで解決したい

この課題を元に、「今動いている自社サービスのアプリを実際にAzure Kubernetes Serviceに乗せてみて、CI/CD、スケーラビリティの確保、ログ収集・モニタリングの設定などを構築してみる」をテーマにHackfestを行うこととなりました。

CloudShell 〜 ブラウザとスマホアプリで使えるシェル環境 〜

Azure Portal画面に入ったあとにAKSなどの各種リソースを作成する際、寺田さんからCloudShellというAzureの機能について紹介して頂きました。

CloudShellというはAzureのポータルから起動できるShell環境で、ブラウザからそのCloudShellを使うとBashPowerShellを使った各種コマンドが使えるようになります。 CloudShell上で作られたファイルなどはCloudShell用のFile Storageに保存されるため、そこにシェルスクリプトを配置して実行するなんてことも可能ですし、CloudShell上から秘密鍵を用いてSSHVMにログインすることもできます。 さらに、僕はこのとき初めて知ったのですがAzureのスマホアプリが実はあり、そのスマホアプリ上でCloudShellを起動することが出来ます。

PC上にクラウド操作するコマンドを入れるのは、間違った操作でクラウドを破壊できてしまうことと複数のアカウントを使っている場合、いまaz loginしているのはどれ?ってなる恐怖で抵抗がありましたが、CloudShellを使えばそんなに心配することなく az コマンドが使える上にもしものときはモバイルからも操作できるので、これは今後もよく使っていこうと思いました。

azure.microsoft.com

アプリケーション用のイメージ作り 〜イメージのサイズを小さくすること、開発効率向上のためのマルチステージビルド〜

実際のアプリケーションをkubernetesに乗せるというテーマだったので、まずアプリケーションのコンテナ化から開始しました。

開発環境自体はDocker + docker-compose で構築しているのでDockerfile自体はすでにあったのですが、それを単純にイメージにすると4GB超えの巨大なイメージになってしまったため、そのスリム化に着手することに。

開発でずっと使っていた環境をADDするだけだったものを、log系、tmp、cache、画像などアプリケーション自体とは関係ないファイル群を取り除いてイメージを作り直しても 1.4GBほどでまだまだでかい。。

最終的にこれ以上スリム化するのはアプリケーションの構造やFROMで設定しているイメージの変更など、大幅な変更が必要だと判断して一旦今回はそのまま行くことになりましたが、このイメージ作りの過程で寺田さんか以下の点を教わりました。

  • なぜイメージサイズを小さくしなければいけないのか
  • 開発効率を上げるために行うコンテナのマルチステージビルド

なぜイメージサイズを小さくしなければいけないのか

デプロイやスケールアウトにかかる時間が遅くなる

コンテナで作ったアプリケーションをデプロイする、またスケールアウトするためには該当するバージョンのイメージをコンテナレジストリからダウンロードしてからそれを起動する必要があります。

イメージのサイズが大きいとその分ダウンロードにかかる時間が増えてしまい、それによってデプロイ・スケールアウトの速度が低下してしまいます。 同じサーバー上で同じイメージを使う場合にはキャッシュが効きますが、新しいバージョンのデプロイ、またホストマシンがマネージドサービスの場合などはキャッシュが効かずダウンロードが発生するケースが多いのでイメージサイズの大きさはかなり速度に悪影響を及ぼします。

本番環境にデプロイ・スケールアウトするとき以外にもCI/CDを組むと、それらを回すたびにイメージのダウンロードが発生し、その速度がCI/CDの速度の低下、引いては開発速度の低下につながってしまいます。

ホストマシンのディスクサイズの圧迫

Azure Kubernetes Servicesを使う場合にNodeとして扱うサーバーは仮想マシンになります。

仮想マシンなので当たり前ですがディスクサイズの制限があります。

ここに大きいサイズのイメージがずっと貯まり続けていくとディスクサイズの圧迫に繋がります。 ホストマシンであるNodeに入って不要なイメージを削除することで対応することは出来ますが、それを頻繁に気にしないといけない状態は良くないのでイメージサイズは小さい方がいいです。

開発効率向上のためのマルチステージビルド

イメージ作成の際、最初はひとつのDockerfileにすべての設定を書いていたのですが、あとからbase-DockerfileとDockerfile2つにファイルを分けてイメージ作成をするようにしました。

base-Dockerfileは実行環境に必要なものを作るDockerfileで、ApachePHPのインストールや設定などを入れていきます。 Dockerfileの方はアプリケーションコードを入れるためのDockerfileで、アプリケーションコードをADDしたりcomposer installなどをしています。

このように環境とアプリのDockerfileを分離することで、責任範囲を明確にし、依存の関係もはっきりします。 Dockerfileの書き方を変えてキャッシュの効き方を最適化する上でも効率的に作業できるようになりました。

Container Registry 〜 コンテナイメージの保管場所 〜

アプリケーションのDockerイメージを作ったあとは、Azure Kubernetes Services上からイメージをダウンロードできるようにリモートのコンテナレジストリにプッシュしなければなりません。

Azure上にはContainer Registryのサービスがあるので、そちらを利用しました。

azure.microsoft.com

Container Registryを作るときに 「管理者ユーザー」を有効にするとユーザー名とパスワードによる認証でContainer Registryとプッシュ/プルすることができます。

管理者ユーザーを使わなくても azコマンドが使えれば azコマンド経由でレジストリにログインしてプッシュ/プルすることができるのでセキュリティ的にはこっちの方がよさそうです。

docs.microsoft.com

Azure Kubernetes Serviceを使いながらの基本概念を学ぶ

実際にデプロイするイメージを作ったあとは、実際にAKSを使いながらKubernetesの理解を深めていきました。

Node

Nodeとは、Kuberntesのクラスタが乗るホストマシンの事です。

AKSの場合、Azureの仮想マシンをNodeとして使用しています。そしてNodeとして扱う仮想マシン群は、ひとつの可用性セットという含めることでパッチ適応によるアップデートや物理障害時にすべてのNodeがダウンしないように管理されています。

しかし、あくまで単一リージョン内の可用性セットにNodeが含まれている状態のため、リージョン全体に対する障害などは保護されていません。

リージョン障害などからも保護するには、AKSクラスタを別リージョンにも作成し、上流にAzure Traffic Managerを配置してルーティングを管理する必要があります。

AKSでの事業継続性の担保やディザスタリカバリーについてはAzureドキュメントが用意されているのでそちらを参考にすると良さそうです。

docs.microsoft.com

Pod

Podとは、「複数のコンテナを束ねたもの」です。 Kuberntesが扱う最小単位がこのPodになります。

Podの特徴として、「Podとして束ねられたコンテナは必ず同一ノードにデプロイされる」という特徴があります。

例えばPHPアプリケーションのよくある構成として、前段にnginxをおいてリクエストを受け取けから後ろにいるphp-fpmに流してで処理を行うという2段構成になることがあります。 その構成においてnginxとphp-fpmが動くNodeが別のNodeになるとマシンを超えたネットワーク通信が発生しパフォーマンスがその分悪化してしまいます。それをPodという単位に束ねることで同一ノードにデプロイされ、通信のオーバヘッドが抑えられる効果があるので、依存関係の強いコンテナ同士はPodにまとめる必要があります。

Pod自体は単体で動作しますが、それ自体にKubernetesの特徴として取り上げられる以下のような機能は持っていません。

  • ローリングアップデート
  • ヘルスチェック
  • 自動修復機能
  • スケール・オートスケール

Deployment

各Podに対して、ローリングアップデート、ヘルスチェック、自動修復機能、スケール・オートスケールなどを設定しているのがDeploymentです。 Deploymentを定義するyamlの中で、アップデート方法の定義やヘルスチェックの定義(liveness, readiness)、スケール(replicas)などを定義するとKubernetesに反映させることができます。

今回のハッカソンでは、yamlレベルではDeploymentを最小単位として操作しました。

Service

Serviceとは、「論理的にまとめられたPods群に対する通信ポリシーを定義するもの」です。

Kubernetes上にデプロイされたPodは各ノードに配置されそれぞれプライベートのIPアドレスを持っています。 しかし新しいバージョンのアプリをリリースするたびに新しいPodが作られ古いPodは削除されていくので、そのIPアドレスに対して通信することは適切ではありません。

Serviceは特定のPods群に対する通信を管理し、そのサービスに対してのドメインが自動で割り振られます。そのドメインに対してリクエストを送るとServiceが管理しているPods群に対してリクエストが行くという仕組みになっています。

ServiceはあくまでKubernetesクラスタ内部での通信を管理するもののため、Service自体を外部からのリクエストを受けれるように公開することは推奨されていません(Serviceの指定をLoadBalancerにすることで外部公開することができるが、やるべきではない)

クラスタ外部からの通信を受けれるようにするには、Ingressというものをつかいます。

Ingress

Ingressは外部サービスのリクエストを受けつけ、それをKubernetesクラスタ内部にあるServiceにルーティングする役割をもっています。

基本的にnginxをingressとしてAKSクラスタ内にデプロイされ使用します。まだプレビューの段階ですが、Azure Load Balancerというマネージドサービスを使うこともできるようです。

docs.microsoft.com

Kubernetes、そしてAzure Kubernetes Serviceのメリットはなにか?

今回のハックフェストを通して、いままで朧げだったKubernetesそしてAzure Kubernetes Serviceによって何を得られるかがわかってきました。

ブルーグリーンデプロイメントにより安全なリリース

ブルーグリーンデプロイメントとは、新しいバージョンのアプリをリリースする際に既存の環境のソースコードをアップデートするのではなく、新しいバージョンの環境(サーバー)自体を作り、リクエスト向き先を変えることでリリースデプロイを行うことです。 これにより、もし新しいバージョンのアプリに不具合があったとしても前の環境をしばらく残しておけば向き先を変えることで即座に復旧することができます。

Kubernetesにブルーグリーンデプロイメントを行うには、ブルーとグリーン2つのDeploymentを作り、そこへのリクエストをServiceのフィルタリングで切り替える方法があります。

例えばblue-deployment.yamlとgreen-deployment.yamlというファイルを作り、そのファイルのラベル付けでversionというラベルにblueとgreenをそれぞれ設定しておきます。Service上の通信ポリシーではblueのみ通信が行くように設定しておきます。

新しいバージョンのアプリをデプロイする際にはgreen-deployment.yamlの方に新しいDockerイメージを名前とラベル(webapp:2.0など)を指定してKubernetesクラスタ上にデプロイします。デプロイが完了したらServiceの通信ポリシーをblueからgreenに変更すると、即座にgreenがリリースされることになります。もし不具合が発生したらService通信ポリシーをgreenからblueに変更すると、前のバージョンに即戻すことができます。

これをさらに応用すると、greenをデプロイしたあと、一般ユーザーに公開しているドメインからのリクエストはblue、開発者用のドメインから来たリクエストはgreenにリクエストを流すという風に設定することで、本番とまったく同じ環境で安全に動作検証をして問題なければリリースと言うフローを作ることも可能になります。

このブルーグリーンデプロイメントによる安全なリリースにより、以下の効果があると考えています。 - 安全を確保するための、夜間作業によるリリース作業を減らせる - 本番でしか起きない不具合やパフォーマンス問題の発見を早められる - 簡単に元に戻せることにより、事前のクオリティ担保のためテストを減らしてリリース頻度と速度を高めることができる

Kubernetesを使わなくても、自分で仕組みを構築するなどすれば出来なくはないですが、それを簡単に作れる仕組みがあるのは非常に魅力的に感じました。

Container Instanceを使ったNodeの制限を超えたスケーラビリティ

これはAzure Kubernetes Servicesを使った場合のメリットになります。

Kubernetesは複数のNodeの上にクラスタを作ってPodを稼働させるため、クラスタに参加しているNodeの合計のリリース量(CPU,メモリ、ディスク)によってPodがスケールアウトできる数に物理的な制限が発生します。 もちろんあとからNodeを追加することも可能ですが、クラスタへのNodeの追加には非常に時間がかかるため突発的または計画的に急激なアクセスが発生する場合にNodeの数を毎度増減するのには向いていません。

Azure Kuberentes Servicesの場合、Podを稼働させる環境をNode上からContainer InstancesというAzureのマネージドサービスに数分レベルで切り替えることができ、Container Instancesに切り替えるとNodeの制限を超えた数のPodを走らせる事が可能になります。

これにより、通常稼働時に必要十分なNodeだけを動かしておき、プッシュ通知やイベントなどの急激なアクセス増があるときのみContainer Instancesに切り替えてスケールアウトし、通常に稼働戻ったらまたNodeに戻すことでコストの最適化をしながららスケーラビリティを確保することができます。

azure.microsoft.com

infrastructure as codeの実現

現在の僕らのインフラ環境は、Azure WebAppを使っている場合と単純なVMを使っているもの、単純なVMを使っているものもAnsibleによって設定がコード化されているものとされていないものなどが乱立している状態です。また、それらへデプロイフローもそれぞれ違う状態になっています。

これをKubernetes化することにより、インフラ環境やデプロイフローなどはKubernetesyamlファイルでコード化・共通化され、アプリの実行基盤はDockerfileによるコード化されることになります。

これによって、サービス毎に作らなければならないインフラやDevOps周りの共通部分を再利用しやすくなり、製品開発チームが増えサービスが増えていくときにSREがボトルネックとなることが防げると考えられました。

Kubernetesのデメリットはなにか?

今回のハックフェスト通して寺田さんは常に「すべてをKubernetesでやるべきではない。Kubernetes銀の弾丸ではない」ということをおっしゃっていました。寺田さんが明示的におっしゃっていたことと、僕が考えたことを交えながらKubernetesのデメリットも書いておこうと思います。

Kubernetes自体のバージョンアップへの追従

Kubernetes自体のバージョンアップが3ヶ月に一度リリースされます。その度に大きな機能追加が発生したり、いま使っているyamlファイルの記法が変わり新しく作り直す必要が発生する可能性があります。

Azure Kubernetes Serviceのバージョンポリシーが最新から4バージョンまでをサポートするという方針です。

それはつまり、「いま最新バージョンのKubernetesを使っても来年には必ずバージョンアップをしないといけない」ということです。

4バージョンのアップデートはかなり大変のため、現実的には3ヶ月または半年に一度はバージョンアップしていくという方針になるでしょう。

このバージョンの作業も複雑な構成のクラスタを組めば組むほど、サードパーティのライブラリを入れれば入れるほど困難になっていきます。

コンポーネントが増えることによる障害発生のデバック・パフォーマンスチューニングなどの難易度が上がる

普通にVM上でWebアプリをリリース場合、よくある構成はロードバランサー -> VM(WebApp) -> DB という3段構成です。 これをKubernetes上のクラスタに載せた場合、以下のようになります。

Ingress(ロードバランサー) -> Service -> Pods(WebAppのコンテナ群)/Node -> DB

通信経路上にKubernetesが扱っているコンポーネントが登場し、障害発生した場合やパフォーマンスチューニングを行う場合はそれらのコンポーネントをすべて考慮にいれて対応しなければなりません。

Kubernetesがもたらした大きな柔軟性とそれによって得られるアジリティはこれらコンポーネント、抽象化の層によってもたらされています。逆に障害時やパフォーマンスチューニング時はその抽象化の層を降りていく必要があり、抽象化の数だけ降りなければいけないのはいわば当然のトレードオフになります。

僕が考える、Kubernetesとはなんのための、そして誰のためのソフトウェアか

これも寺田さんがおっしゃったことではなく、あくまで今回のハックフェストを通して僕が考えたことです。

僕は今回のハックフェストを通して、Kubernetesは「少人数のSREチームで、たくさんのマシンリソースを効率的に使いながら、たくさんのサービス、そしてそのサービス開発自体を支えるためのソフトウェア」だと理解しました。

Kubernetesは、素晴らしい機能群によってインフラに大きな柔軟性をもたらし、それによってデプロイ、復旧、スケールアウト、マルチバージョンのリリースによるABテストなど様々なことを簡単に実現できるようにしてくれます。しかもクラスタ内のNodeに集積してコンテナを稼働させることによって効率的にマシンリソースを使うことも可能にしてくれます。

しかしそのトレードオフとして、Kubernetes自体のバージョンアップへの追随やトラブル時にKubernetesの抽象化を理解して降りていく必要性が発生します。Kubernetesを導入する場合、イニシャルコストよりもこのランニングコストの方をよく理解しておいた方が良いと思いました。

大きなメリットは得られるが、インフラを管理するSREのようなエンジニアが不要になるものではなく、むしろそこを管理するSREはKubernetesを理解して使いこなす必要があるのでよりスキルが必要かもしれません

少人数の優秀なSREでKubernetesを扱い多くの製品や開発チームを支えることができれば、レバレッジが効いて非常に良い投資となりますが、逆に製品が少ない、また開発チーム自体が少ないのであれば投資対効果は低くなりがちで、Azure Web Appなどのマネージドサービスを使う方がメリットが大きいと思います

Kubernetesというソフトウェア自体は面白いですし、今後もキャッチアップを続けていきますが、自分達の事業や開発チームにとってホントに必要なタイミングは見極めた上で導入は検討していきたいと思います。

Azure DevOps 〜 CI/CDに必要な機能全部入りの最強SaaS

AKSでアプリを動かすというのも一通り終わったあと、寺田さんのおすすめのAzure DevOpsというツールを使ってCI/CDのパイプラインを作ることになりました。

azure.microsoft.com

Azure DevOpsは、まさにDevOpsのために必要な機能がすべて入っているSaaSで、タスク管理のためカンバンなどが使えるBoard、ソースコードを保管するためのプライベートのGitリポジトリのRepos、CI/CDを作るための Pipelineなどが使えます。

今回はCI/CDを作るということで、 ReposというGitリポジトリソースコードをpushすると、 Pipelineのビルドパイプラインが動いて新しいバージョンのDockerイメージをビルドしContainer Registryにプッシュし、 Pipelineのリリースパイプラインによって、新しいバージョンイメージがkubernetes上にデプロイされる というパイプラインを作っていきました。

これらのパイプラインを作るのがすべてGUIでタスクを選択して設定を入れていくだけ構築することができ、大体2~3時間程度ですべて構築することができました。 また、このパイプラインはGUIだけでなくyamlで定義することもできます。

Azureとのインテグレーションがかなりよく出来ているので、本番環境をAzureで構築している身としてはKubernetesは使わなくてもこのAzure DevOpsはぜひ導入していきたいと思えました。

ペアプロ/モブプロの効能

今回のハックフェストはずっと寺田さんとペアプロする形で進めていたのですが、僕にとって今年一番の集中力が発揮され、2日間のハックフェスト中まったく集中が切れることなくすすめる事ができました。 集中力が深く長時間維持できたことでかなり効率よくハックフェストをすすめることができ、当初3日間やる予定だったものが2日間で終わらせることができ、僕も寺田さんもヘロヘロになって「明日は休みましょうw」となる程でした。

寺田さんが一緒だったということが非常に大きかったとは思いますが、ペアプロ/モブプロすることにより

  • 一人でやるよりも「いまやるべきこと」が明確化され、集中しやすい。
  • 予期しないことがあってハマった場合、複数の脳で解決策を探索して議論して素早くトライすることで解決が早い
  • 一緒に同じ画面を見て作業することで、予期しない学びが得られる(bashのショートカットやデバックの仕方など)

のような効果を身を持って体感することができました。

今回はハックフェストでしたが、実際の業務でもペアプロ/モブプロを取り入れてみて生産性が上がるかどうかぜひ試して見たいと思いましたし、僕がこれからコミュニティで勉強会などやるときもペアプロ/モブプロの使ってやってやりたいとおもいます。

最後に

今回のハックフェストを寺田さんに開催していただいたことで、僕個人としてはAzure/Kubernetesに限らないほどエンジニアとして多くの学びを得る事ができました。

会社としても、実際にAzure Kubernetes Serviceを使うべきかどうかの技術検証を、わずか2日間のハックフェストで実際に動かしながら把握することができたのは非常に投資対効果が大きかったと考えています。

今回学んだことを実際の現場に活かして、より良いサービスを作っていきたいと思います。

寺田さん、今回は本当にありがとうございました!!!

Azure Queue StorageをバックエンドにしたPHP製のジョブキューライブラリ「Backjob」を作った

AzureにはQueue Storageというマネージドサービスがあるのですが、そのQueueStorageをバックエンドとして利用したPHP製のジョブキューライブラリを作りました。

github.com

なぜ作ったのか?

今自分の会社で作っているプロダクトの特性がWrite Heavyなアプリケーションになっており、ユーザーがプッシュ通知などで大量アクセスするとWebApp側はスケールできてもRDB側がスケールできないという課題がありました。

それに加えて、2年程まえに自分達で自作していたジョブキューシステムがあったのですがそれがアプリケーションで使っているRDBをそのままバックエンドとして使用しており、その性能限界により「ジョブ登録が大量に来るとロック待ちが発生し障害の元となる」、「ジョブが詰まったときにワーカーを増やして処理性能をあげたいがRDBのパフォーマンスネックによリこれ以上ワーカーを増やせない」という問題もありました。

その課題を解決するために、RDBではなくAzure Queue Storageを用いたジョブキューライブラリを作り、ユーザーからのリクエストで直接RDBに書き込みをするのではなく新しいジョブキュー経由で書き込みをするように変更することでDB負荷の軽減と既存ジョブキューライブラリの課題を解決させたいと考えました。

ここでLaravelなどのモダンなフレームワークを使っていれば標準なジョブキューライブラリがあると思いますが、私達が利用するFuelPHPにはそのようなものがなく、また他のフレームワークで利用されるジョブキューライブラリをみてもRedisやAWSのSQSに対応していてもAzureのQueue Storageには対応していない状況だったため自分で作りました。

インストール

composer でインストールできるように公開しているので、以下の内容をプロジェクトのcomposer.jsonに追記してください。

{
    "require": {
        "arakaki-yuji/backjob": "^0.0.5"
    }
}

使い方

自分のJobを定義する

以下のように\Backjob\Jobクラスを継承して自分の独自Jobを定義します。

class CustomJob extends \Backjob\Job
{
    /**
     * You must define a run method.
     * this method is called when dequeued and run
     */
    public function run()
    {
        $msg = $this->params['message'];
        return $msg;
    }
    
    /**
     * this method is optional.
     * if you define success method, it is called after run method successed.
     */
    public function success()
    {
        return 'success job';
    }
    
     /**
     * this method is optional.
     * if you define fail method, it is called after run method failed.
     */
    public function fail()
    {
        return 'success job';
    }
}

runメソッドはJobが実行されるときに呼ばれるメソッドです。このrunメソッドの中でこのJobが実行したいタスクの実装を書いていきます。 Jobを登録するときにパラメータが渡せるようになっているのですが、そのパラメータは$this->paramsの中に連想配列で入っています。 このrunメソッドは必ず定義してください。

failメソッドとsuccessメソッドは実装してもしなくても構いません。

successメソッドはrunメソッドが正常に実行されたあとに呼ばれるメソッドです。

failメソッドはrunメソッド内で例外やエラーが発生したときに呼ばれるメソッドです。

それぞれ自分たちのアプリケーションの要件に合わせて利用してください。

Jobの登録、そして登録されたジョブの実行の仕方

キューへのジョブの登録、そしてキューからジョブを取り出して実行するために利用するBackjobクラスのインスタンスを作ります。

引数にはQueue Storageのストレージアカウント名、Queue Storageの名前、ストレージアカウントのアクセスキーを与えます。

$backjob = new \Backjob\Backjob::factory($storageAccountName, $queueName, $accessKey);

ジョブをキューストレージに登録するには、登録したいジョブのクラスメソッド makeJobメソッドに引数としてジョブに渡したいパラメータを連想配列で渡し、インスタンス化します。

その後、Backjobインスタンスインスタンスメソッド、queueの引数としてジョブを渡して上げればジョブキューへの登録が完了します。

$params = ['message' => 'Hello Backjob'];
$job = CustomeJob::makeJob($params);
$backjob->queue($job);

ジョブキューに登録されたジョブを実行するには、Backjobインスタンスのrunメソッドを実行します。 runメソッドを実行すると、ジョブキューからひとつのジョブを取り出し、そのジョブをrunメソッドを実行して処理を行います。

$backjob = new \Backjob\Backjob::factory($storageAccountName, $queueName, $accessKey);
$backjob->run(); // => 'Hello Backjob'

このrunメソッドをcronで叩く、もしくはdaemon化してループで呼び続けるコードをそれぞれ使っているフレームワークの流儀に沿って作っていただければOKです。

今後について

自分達のプロダクトに必要だったから作ったライブラリなので、今後も必要に応じてアップデートはしていく予定です。

いま考えている範囲だと、Azure Queue Storageの性能限界を超えた量のジョブを処理する必要になった場合にバックエンドに用意するQueue Storageを複数設定できるようにして負荷分散に対応する、またqueueへの登録が失敗したときのretry回数やtimeoutの設定できるようにするなどはやりたいと考えています。

とりあえず現時点で一週間で40万件以上のジョブを処理しているので、ちゃんと使えると思います。