#てらだよしおまつり、沖縄開催で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万件以上のジョブを処理しているので、ちゃんと使えると思います。

AzureのCosmosDBをREST API経由で操作するPHPライブラリを作った

AzureのCosmosDBを使ったあれこれを作る過程で、CosmosDBのRESTAPIを叩くPHPライブラリが必要になったので作りました。

あんまり特別なことはしていないくて、ほんとにただRest APIをラップしたものです。

github.com

インストール

composer.jsonにarakaki-yuji/cosmosdb-clientを追加してください。

{
    "require": {
        "arakaki-yuji/cosmosdb-client": "^0.0.5"
    }
}

使い方

初期化

$client = new \CosmosdbClient\CosmosdbClient($cosmosdbSecretKey, $cosmosdbAccountName);

Databaseの操作

// create database
$client->database->create('database_id');

// list database
$client->database->list();

// get database
$client->database->get('database_id');

// delete database
$client->database->delete('database_id');

Collectionの操作

// create collection
$indexingPolicy = ['indexingMode' => 'lazy'];
$partitionKey = ['paths' => ['/Name']];
$client->collection->create('database_id', 'collection_id', $indexingPolicy, $partitionKey);

// list collection
$client->collection->list('database_id');

// get collection
$client->collection->get('database_id', 'collection_id');

// replace/update collection
$client->collection->replace('database_id', 'collection_id', $indexingPolicy, $partitionKey);

// delete collection
$client->collection->delete('database_id', 'collection_id');

Documentの操作

// create document
$doc = [
    'id' => 1,
    'name' => 'Yuji Arakaki',
    'email' => 'example@test.com'
];
$partitionKeyValue = $doc['name'];
$client->document->create('database_id', 'collection_id', $doc, $partitionKeyValue);

// list document
$client->document->list('database_id', 'collection_id');

// get document
$client->document->get('database_id', 'collection_id', $doc['id'], $partitionKeyValue);

// replace/update document
$client->document->replace('database_id', 'collection_id', $doc, $partitionKeyValue);

// query document
$query = "SELECT * FROM c WHERE c.name = @name";
$parameters = [['name' => '@name', 'value' => 'Yuji Arakaki']];
$client->document->query('database_id', 'collection_id', $query, $parameters);

// delete document
$client->document->delete('database_id', 'collection_id', $doc['id'], $partitionKeyValue);

良いデザインかどうかはさておき、APIのラッパーは筋力さえあれば誰でも作れる。

ありがとう、ハッカーズチャンプルー! ハッカーズチャンプルー2019のコアメンバーとして運営 & Paykeとしてスポンサーセッションしてきました!

今年もハッカーズチャンプルーのコアメンバーとして運営と、あとスポンサーセッションでの登壇もしてきました!

hackers-champloo.org

ツイートのまとめはこちらから。 今年もすごいツイート数で、すべてを見るのに数時間かかりそう。。。

togetter.com

Paykeとして、スポンサーセッションでの登壇

今年は僕の所属するPaykeとしてハッカーズチャンプルーのスポンサーにも参加させて頂き、今回スポンサーセッションを行いました。

登壇内容はこちらになります。

スポンサーセッションは初めて行ったのですが意外と登壇内容に気を使うということに今回始めて気が付きました。

ただ技術の話をしても良かったのですが、せっかく沖縄のエンジニアが数多く聞いてもらえる機会なので、Paykeという会社についも興味を持ってもらいたい。

しかし、ただ会社紹介をしても興味を持ってもらえないだろうし、自分自身も話をしておもろしくない。

そこで自分が日頃考えていて熱量持って話せるハッカーズチャンプルーとエンジニアコミュニティへの感謝の気持ちと、会社紹介を交えるエモい登壇となりました。

最後は時間切れの指笛がなってしまいましたが、なんとか一番伝えたいメッセージまでは届いたので良かったですw

アメンバーとしての準備「スタッフ・登壇者用イベントTシャツ制作」

僕の今イベント準備における最大のミッションは「スタッフ・登壇者用のイベントTシャツの制作」だったのですが、沖縄に新たに生まれたデザイナーコミュニティ「D#(ディーハッシュ)」の緑間さんと外山さんのご協力により最高に可愛いスタッフTシャッツが出来上がりました!

f:id:arakaji-yuu:20190702102230j:plain
スタッフTシャツ

ハッカーズチャンプルーの「ゆるさ」と、沖縄感を出すための紅型色のシーサーがかわゆいやつです^^

こんな素晴らしいデザインを作れる人たちが運営するデザイナーコミュニティ「D#(ディーハッシュ)」ではデザイナー向けのイベントをよく行っていますので、デザインに興味のあるエンジニアの人たちはぜひ参加してみると楽しいと思います!

twitter.com

参加者としての感想

今回もハッカーズチャンプルーらしく、様々なテーマ、様々な技術の話が聞ける、非常にチャンプルーなイベントなって面白かったです。

最初のメインスピーカーであるdeeeetさんが「開発者向け基盤をつくる」という話でマイクロサービスアーキテクチャでプロダクトを作るために基盤がなにを提供するかという高レイヤーの話をしたかと思えば

speakerdeck.com

次のメインスピーカーhikaliumさんは「現代のコンピュータにおける自作OS事情」と一気に低レイヤーに話が移り、

docs.google.com

「高低差ありすぎて耳キーンなるわ!」というツッコミが会場中を響いたとかいなかったとか。

続いてメインスピーカーちょまどさんの「すきなことをやるということ」で、ちょまどさんのキャリアを通じて「好きなことを夢中になってやることが、今はわからなくても、きっと未来につながる」というメッセージがあり、

次のメインスピーカー、Doorkeeperの開発者でもあるポールさんの「サイドプロジェクトが利益を生むビジネスになるまでの苦労と長い道のり」のセッションでは、素晴らしいプロダクトを作り、たくさんの人に利用されていたとしても続く苦労と成功までの長い道のりが話されていて、本当に涙なしには聞けないセッションとなりました!

このセッションの終了後の@k_nishijimaさんのツイート、みんなお金を払おうな!

最後のメインスピーカーであるgongozさんの「What can Emacs be ?」というセッションでは、まさに「好きなことを夢中になってやる」を地でいくEmacs でメディアプレイヤーを作ったり、ファミコンエミュレータを作ったりした話が展開され、会場中が爆笑と感嘆の渦に巻き込まれました。

speakerdeck.com

LT勢も素晴らしく、最初にりゅうさんの「JavaScriptで黒魔術」という記号だけで好きな文字を出力するという謎技術が披露されたと思えば

speakerdeck.com

ちょまどさんのメインセッションのあとにLTを行ったいわむーさんは、「では皆さん、ちょまどさんのセッションでの興奮を落ちつけるためにヨガをやりましょう」といってLTの貴重な1分半をヨガ費やしたり(ちゃんとLTのフリになっていた)

「ラズパイでギターをつくる」というLTでギター経験者が泣いて喜ぶ楽器?をラズパイで作ったことを話したかと思えば

www.slideshare.net

沖縄に住む、本物の黒魔術士ことtompngさんの「意味不明プログラミングの世界」で発表された「実行可能な画像ファイル」という、最早一つのアート作品と言える作品に会場中が拍手喝采に包まれました。

メインセッション、LT、スポンサーセッションまで含めてすべての登壇者が素晴らしい発表をしてくださり、その内容を本当に楽しく聞いてくれる素晴らしい参加者の皆さんが来てくれ、それを運営のみんなで支える事ができた本当に素晴らしいイベントになったと思います!

ありがとう、ハッカーズチャンプルー!

SQLマイグレーションツール「mig」に、同名のテーブル、カラム、インデックスを作成しようとしたというエラーの場合はマイグレーションをスキップするというオプションを追加しました。

以前ブログでも紹介したこともある、PHP製のシンプルなSQLマイグレーションツール「mig」ですが、こちら社内のDBマイグレーションツールとして絶賛使用中で、実は地道にバグフィックスや機能追加を行っています。

arakaji.hatenablog.com

その中で、メンバーからの要望があり追加した機能について紹介します。

同名のテーブル、カラム、インデックスを作成しようとしたというエラーが出た場合マイグレーションをスキップするオプション「skip-duplicate-and-exists-errors」

migでマイグレーションSQLファイルを作成するとファイル名のプレフィックスとしてタイムスタンプが付与され、マイグレーションを実行するとそのSQLファイルのタイムスタンプがmigrationsテーブルに追加されます。 マイグレーションを実行するときには、このテーブルのレコードを見て実行していないSQLを判断し実行しています。

しかし、例えばどこかの環境のDBのSQLダンプを取得してそれを実行して作ったDBの場合、そのSQLダンプ内にmigrationsテーブルが含まれていなければすでにテーブルやカラム、インデックスが作成されているのにそれらを作成をするSQLを実行しようとしてエラーが発生します。

マイグレーション実行時に「skip-duplicate-and-exists-errors」というオプションをつけると、同名のテーブル、カラム、インデックスを作成しようとしたというエラーの場合は、このマイグレーションSQLは実行したものとみなしてmigrationsテーブルにレコードを追加してスキップするという機能を追加しました。

$ mig-cli migrate skip-duplicate-and-exists-errors

なぜ追加したのか?

うちの開発チームではmigの利用は開発環境に限定していて、本番環境へは手動でSQLを実行しています。

理由はソースコードの変更と違い、DBへの変更はパフォーマンスへの影響も大きく実行順番やタイミングの調整も行いたいため、あえて手動で実行しています。

つまり、本番DBにはmigで使うmigrationsテーブルがありません。

以前「Paykeの技術基盤チームの取り組み」という内容で発表をしたこともあるのですが、Paykeでは開発環境にもほぼ本番同等のデータを使うようにしているため定期的に本番に類似したデータのdumpファイルを取り込んでいます。

speakerdeck.com

そのたびにmigrationsテーブルと実際の各テーブルの構成が変わってしまい、migで不要なエラーが発生してマイグレーションが進まないという問題が発生しておりました。

この問題を解決するため、「skip-duplicate-and-exists-errors」というオプションを追加することにしました。

ソフトウェアは使うと磨かれる

migは小さなソフトウェアですが、ホントに自分たちのユースケースとして必要だから開発して、それを実際に利用し、そのフィードバックをもとに少しづつ改善しています。

実際に自分で使ってみるだけだと今回のような機能は思いつかなかったですが、実際チームで使うなかでフィードバックがあって開発することができました。

この機能以外にも、一つのSQLファイルに複数のSQL文が入っていても実行できるようにしたり、SQLファイルの最後に複数の改行が入っている実行に失敗してしまうというバグを修正したりと、地味だけど着実に改善しています。

やはりソフトウェアは開発してリリースするだけだとだめで、実際使われてフィードバックを得ないと磨かれていかないのだと実感しました。

migはおそらくうちの社内だけで使っているソフトウェアですが、いつか世界中の人に使われて自分の名刺代わりになるようなソフトウェアを作っていけるように今後もコツコツやっていきます。

個人プロジェクトとして開発したWebサービス「Collabo」をOSSとして公開しました。

タイトルで言いたいことはすべてなのですが、去年個人プロジェクトとして開発していた「Collabo」というWebサービスGithub上でOSSとして公開しました。

github.com

公開時のブログはこちらです。

arakaji.hatenablog.com

なぜ公開するのか?

公開する理由ですが、簡潔いうと「けじめ」をつけるためです。

Collaboは自分が仕事以外で初めて自分一人で開発して公開まで行ったプロダクトで思い入れもあるのですが、結局自分自身もほとんど使っていないプロダクトになってしまいました。 自分が使わないので、そもそも人に使ってもらえないですし、開発も滞っています。 そのままただプロダクトとして死んでいくなら、せめてソースコードぐらいは公開することで自分のポートフォリオにしようと考えて公開することにしました。

自分のClojureで書いた初めてプロダクトとしてソースコードを公開し、一区切りをつけたいと思います。

Collaboの開発で学んだこと

モチベーション維持の難しさ

リリースまではなんとかがんばれたのですが、リリース後はやりきった感が出てしまってなかなか次の開発に進むことができませんでした。

以下にも書くのですが、自分自身が実際に使うプロダクトにしないとやはり改善をし続けるモチベーションを維持するのは難しかったです。

すでに代替サービスが有る場合、自分のプロダクトでも品質悪ければ使わない

Collaboはだれでもプロジェクトを作成して公開し、そのプロジェクトのイシューを管理することができるサービスです。 しかしプロジェクト管理サービスは他にも代替サービスが無数にあり、そのどれもが数年前から存在していて品質が磨かれているものばかりです。

最初は自分のプロダクトをリリースしたらドックフーディングしながら改善していこうと思っていたのですが、自分のプロダクトでもリリース時点で既存プロダクトよりも使い勝手がよくないと自分ですら使いたくなりませんでした。

やはりせっかく作るのであれば、せめて自分だけでも「このプロダクトは他のプロダクトよりも優れている!」と信じられるものにしないといけないなと学びました。

次の個人プロジェクトについて

次は「単機能でシンプルだが代替サービスのない、もしくは代替サービスをよりも自分は優れていると信じられるプロダクト」を目指して開発したいと思います。

やっていくぞ!!!