UZABASE Tech Blog

〜迷ったら挑戦する道を選ぶ〜 株式会社ユーザベースの技術チームブログです。

SPEEDA開発チームをブーストするふりかえりのカルチャー

こんにちは!SPEEDA開発チームの岩見です。
この記事では私たちSPEEDA開発チームの中でも特徴的な文化のひとつとなっている、ふりかえりについてご紹介します。

以下のような方々のお役に立つことを願っています。

  • 自分たちのチームでもふりかえりをやってみたい方
  • ふりかえりはやってるけどなんだかマンネリ化している方
  • 色々なふりかえりの運用、実例が知りたい方

SPEEDA開発チームにおけるふりかえり

私たちSPEEDA開発チームでは、プロジェクト単位の少人数のチームに分かれ、それぞれがエクストリームプログラミング(通称XP)をベースとしたアジャイル開発を行なっています。
私たちは短い開発サイクル(イテレーション)を継続的に繰り返す中で、プロダクトとチームでの開発をインクリメンタルに改善、洗練させていくことを目指しています。
こうした中で、私たちはイテレーション(多くの場合1週間が1イテレーションに相当します)の終わりに、ふりかえりを必ず実施しています。

ふりかえりの中では、イテレーションの開発を振り返って、次のイテレーションでの自分たちの開発を改善する為のアクションを一つ設定します。
アクションの数を絞っているのは、数が多すぎても一つ一つのアクションに対する精度が落ちてしまうリスクがあるためです。
短いイテレーションの中でより本質的なアクションを実施する為に、ふりかえりの時間の中で問題の洗い出し、深掘りを集中的に行うようにしています。

こうして決まったアクションをチームで実行しながら、私たちはプロダクトとチームでの開発プロセスを日々進化させています。

SPEEDA開発チームのふりかえりの特徴

私たちがふりかえりのためのミーティングを行う際には、一つの決め事が存在します。
それは、自分たちの所属しているチームの外側から、一人のメンバーにファシリテーターとして参加してもらうことです。
例えばイテレーションのふりかえりであれば、自分たちのプロジェクトチーム以外のエンジニアにファシリテーターとして参加してもらいます。
このエンジニアの中には、Webアプリケーションエンジニア、テストエンジニア、データサイエンティストといった多様なロールのメンバーが含まれています。
また、ファシリテーターの選出には、偏りや重複が出ないように、シフトを組んで回す形をとっています。
そのため、チーム内のエンジニアは、おおよそ1ヶ月に一度、どこかのチームのふりかえりをファシリテーションすることになっています。

SPEEDA開発チームにおけるファシリテーターの役割

ではファシリテーターは何をしているのか、というと、以下のような役割を担っています。

議論しやすい場の設定

ユーザベースの六本木オフィスにはラウンジと呼ばれる社内ミーティング用の広めのスペースがあります。
ファシリテーターはそこにホワイトボードと人数分の椅子をセットし、参加者が議論ができるようなスペースを作ります。
その際には、椅子の配置をメンバーの顔が全員見えるように、かつホワイトボードが見やすいように、ホワイトボードを囲むような形で椅子を設営します。

議論しやすい場作りは、ふりかえりの最中にも行われます。
あるメンバーがあまり議論に参加できていなければ、ファシリテーターがそのメンバーに発言を促したり、議論が脇道にそれてきた際には、議論の進行をメンバー全員で確認したり、と言ったことを行います。
こうした場作りによって、参加者がより議論をしやすい場面を作るのが、ファシリテーターの役割の一つです。

アクティビティの選定を含めた全体の進行

ファシリテーターは、ふりかえりたいテーマに合わせてアクティビティの選定や、タイムスケジュールの設定、タイムキープを行います。
SPEEDA開発チームのふりかえりでは、KPTとよばれるメジャーな手法以外にも様々なアクティビティが用いられています。
例えばイテレーションで起こったことを満遍なくふりかえりたい場合にはタイムライン*1、各メンバーが思っていることや感情に着目してみたいときにはチームレーダー*2や喜怒哀*3、と言った具合に選出を変えていきます。

ファシリテーターの好みとセンスに応じてどのような手法を用いるかが決められるので、参加者は毎回新鮮な気持ちでふりかえりに臨むことができています。

アクティビティは、アジャイル・レトロスペクティブズFun Retrospectivesを参考にして選んだりしています。
特にアジャイル・レトロスペクティブズは、私たち開発チームのメンバーの教科書のようなものになっており、私もファシリテーションを行う際に読み返したりしています。

ファシリテーターを置くメリット

チームの外側からファシリテーターを招くことで、以下のようなメリットを実感しています。

ファシリテーターが他のチームへの理解を深めることができる

SPEEDA開発チームでは、定期的にプロジェクトチーム間での入れ替えを行なっています。
これは、プロジェクトに関する知識、技術を特定個人に偏らせないようにするための施策です。

ファシリテーターとして他のチームのふりかえりに参加することで、他のチームの現在の状況を高い解像度で知ることができます。
結果、いつでもチームメンバーの入れ替えを行うことができるようになっている状況を作り出す一因となっています。

ファシリテーターが問題を構造的・俯瞰的にとらえる能力が高まる

ファシリテーターには、チームで議論されている問題を俯瞰してとらえることが求められます。
議論の進行に合わせて、参加者に様々なアクションを促し、より良い解決策に導く必要があるためです。
ファシリテーションは問題を構造的、俯瞰的にとらえる絶好の機会となっています。

チームのメンバーが議論に集中することができる

これはチームメンバーの視点から見たメリットなのですが、ファシリテーターを別途置くことで、参加者が残り時間などに気を取られず、議論に集中することができるようになります。
結果、短い時間の中でも、腰を据えた議論ができるようになり、より効果的なアクションが打てるようになります。

さいごに

私たちはふりかえりを定期的に実施していますが、同時に「ふりかえりに甘えてはいけない」ということもチーム内で話しています。
これは、ふりかえりの場を設定しているからと言って、チームの問題や改善策について議論する場をそこだけに限定してはいけない、という意図からきているものです。
私達のチームでは直接的な対話を重視して仕事をしており、何かチームで議論、改善するべき事項が出た際には、そのタイミングで議論をするようにしています。
この文化とふりかえりの両輪で、チームとしてより良い姿を目指して日々開発を行なっています。

私たちが目指すチームの姿は、CTO林の以下の記事で紹介しています。
journal.uzabase.com
興味を持たれた方は、是非以下までご連絡ください!
Uzabase, Inc. - Jobs: SPEEDA ソフトウェアエンジニア(サーバー/フロント) - Apply online


それでは良いふりかえりライフを!

*1:時系列に沿って、チームで起こったことや、チームメンバーが感じたことを付箋に書き、貼り出すアクティビティです。一定期間内での時間、因果関係に沿ったデータを収集するのに適しています。

*2:チームで大切にしている価値観(XP Valuesを用いたことがあります。)をレーダー上で表し、各チームメンバーがどのように評価するかを5点満点などでプロットします。その後、プロットされた点数を元に、何故そう思ったかをディスカッションしていきます。

*3:その名の通り、「喜ばしいこと」「怒りを覚えたこと」「哀しかったこと」という軸でデータを収集します。チームメンバーの感情面に着目するアクティビティです。

appiumを使ってモバイルアプリのテストを自動化する

こんにちは!SPEEDA プロダクト開発チームの板倉です。
前回は、テスト環境(appium + gauge + kotlin)のセットアップについて書きました。

appiumを使ってモバイルアプリのテストを自動化する ~環境構築まで~ - UZABASE Tech Blog

今回は、実際にアプリのテストをどう書くのかについて書いていこうと思います。

テスト対象のアプリ

今回はAndroid StudioでBasic Activityを選択してプロジェクトを作成したアプリを例にテストを書いていきます。

f:id:diskit:20190829081840p:plain

f:id:diskit:20190826075829p:plain:w300

今回はプロジェクト作成時に作られたコードをそのまま使用しています。

テストを書く

Spec, Scenario, Step

まずは、画面表示までScenarioを書いていこうと思います。
アプリを起動すると何が表示されるのかをStepに記述します。

# ホーム画面

## ホーム画面を表示する
* ヘッダにタイトル"example"が表示される
* ヘッダにアクションボタンが表示される
* コンテンツにメッセージ"Hello World!"が表示される
* フローティングアクションボタンが表示される

GaugeのSpecの書き方として、
# は Spec
## はScenario
* はStep
となります。

Stepの実装を書いていく

Stepに記述した文章はアノテーションに記述します。
ダブルクオートで囲んだ部分はパラメータになりますので、メソッドに引数として定義します。
1つ目のStepを例に実装すると以下のような形になります。

@Step("ヘッダにタイトル<title>が表示される")
fun ヘッダにタイトルが表示される(title: String) {
}

メソッドの中に操作や検証を記述していきます。
操作や検証をするにはセレクタを使用して要素を取得して行います。
Specに記載した内容を実装すると以下のようになります。

@Step("ヘッダにタイトル<title>が表示される")
fun ヘッダにタイトルが表示される(title: String) {
  val header = driver.findElementById("toolbar")
  header.findElement(By.className("android.widget.TextView")).text shouldEqual title
}


@Step("ヘッダにアクションボタンが表示される")
fun ヘッダにアクションボタンが表示される() {
    val header = driver.findElementById("toolbar")
    header.findElement(By.className("android.widget.ImageView")).isDisplayed shouldBe true
}

@Step("コンテンツにメッセージ<message>が表示される")
fun コンテンツにメッセージが表示される(message: String) {
    // xpathは避けたほうがいいです。
    // そのままのコードを使ったので、開発する際はidを設定するなど工夫が必要だと思います。
    val content = driver.findElement(By.xpath("//android.widget.FrameLayout/android.view.ViewGroup/android.view.ViewGroup"))
    content.findElement(By.className("android.widget.TextView")).text shouldEqual message
}

@Step("フローティングアクションボタンが表示される")
fun フローティングアクションボタンが表示される() {
    val button = driver.findElementById("fab")
    button.isDisplayed shouldBe true
}

この実装をする上で、ヘッダのタイトルのセレクタを記述する際に困りました。
そういったときに使えるツールを紹介します。

Appium Desktop

AppiumはGUIツールが公開されています。
このツールはインスペクターだけでなく、Appiumサーバーの起動も行えます。

github.com

アプリのダウンロードはこちら

確認してみる

今回困ったヘッダのタイトルの要素を見てみると、

f:id:diskit:20190829080347p:plain

左側に表示しているアプリ上でヘッダのタイトルをクリックすると、中央のAppSourceに表示されたソースから要素がハイライトされ、右側には要素の情報を見ることができます。
今回はツールバーをid指定で取得し、その要素からclassNameを指定して取得するセレクタを使用してタイトルの要素を取得することにしました。

さいごに

AppiumからSelenium Driver(AndroidDriver, IOSDriverなど)が提供されているので、思ってた以上に簡単にテストを書くことができました。
今回試したのはアプリに表示されている要素の取得と検証のみだったので、ネイティブ特有の機能を使う場合どのような実装が必要なのかは調べてみようと思います。

appiumを使ってモバイルアプリのテストを自動化する ~環境構築まで~

こんにちは!SPEEDA プロダクト開発チームの板倉です。
前回書いた時はNewsPicksの開発をしていましたが、今はSPEEDAの開発をしてます。

SPEEDAのプロダクトチームでは、以下の記事のようにE2EをGaugeとKotlinを使って書くことが多いです。

Gauge Test Automation Toolとアジャイル開発 - UZABASE Tech Blog

GaugeのConceptを用いてテストシナリオをより仕様書のように記述する - UZABASE Tech Blog

GaugeのParameterを使いこなす - UZABASE Tech Blog

今回は、NewsPicksでアプリ開発をやっていたこともあり、モバイルアプリではE2Eをどうやるのかなということで調べてみました。

環境構築

appiumのインストールとテストプロジェクトの準備について説明します。
※ アプリ開発に関する環境構築については割愛します。

appium

npmを使ってインストールします。

npm install -g appium

プロジェクト

MavenやGradleプロジェクトを作成したのち、以下の依存関係を追加します。

Maven Repository: com.thoughtworks.gauge » gauge-java
Maven Repository: io.appium » java-client

作成したプロジェクトでコマンドを実行してgaugeプロジェクトに必要なファイルを展開します。

gauge init java

実行後のプロジェクトは以下のようなディレクトリ構造になります。

<PROJECT_ROOT>
├── build.gradle
├── env
│   └── default
│       ├── default.properties
│       └── java.properties
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── libs
├── manifest.json
├── settings.gradle
├── specs
│   └── example.spec
└── src
    ├── main
    │   ├── java
    │   ├── kotlin
    │   └── resources
    └── test
        ├── java
        │   └── StepImplementation.java
        ├── kotlin
        └── resources

あとはIDEの機能を使って test/java/StepImplementation.java をkotlinに変換し、test/kotlin 以下に移動します。
これでプロジェクトのセットアップは完了です。

appiumのセットアップ

appiumを使ってアプリを操作する際は、SeleniumのRemoteDriverを通して行うことになります。
DriverにCapabilitiesを指定する必要があり、Androidでは最低限以下の2項目を設定することでテストを行うことができます。

キー 設定内容
deviceName テストを実行する端末の名前 Android Emulator
app テストをするアプリ(apk)の絶対パス /Users/user/workspace/project/test-app.apk

こちらにもあるようにAndroidではdeviceNameが設定しても無視されるようです(2019.7.31時点)。

On Android this capability is currently ignored, though it remains required.

Desired Capabilities - Appium

コードで書くとこんな感じです。

fun <T: WebElement> driver(): AppiumDriver<T> {
  val app = File("example.apk")
  val capabilities = DesiredCapabilities().apply {
    setCapability("deviceName", "Android Emulator")
    setCapability("app", app.absolutePath)
  }
  return AndroidDriver(capabilities)
}

このコードだけでも実行すると appium server が実行され、指定した端末にアプリをインストールし起動するまでが行われます!
※ エミュレータは起動しておく必要があります。

次回は

今回は環境構築まで書いてみました。
次回は実際にアプリをテストするコードを書いてみたいと思います!

さいごに

プロダクトチームは一緒にSPEEDAを開発する仲間を募集しています!
プロダクトチームってどんな感じなの?という疑問に対しては、以下の記事が参考になるかと思います!

journal.uzabase.com

少しでも興味のある方はご連絡お待ちしております!!!

使う技術は自分たちで決める。XPを極め技術を追い求めるエンジニア募集! - 株式会社ユーザベースのWeb エンジニア中途の求人 - Wantedly

「ここではすべてが流れている!」SPEEDA の開発チームに入って驚いた 3 つのこと

7 月から SPEEDA 開発チームに参加しました、野口です!

SPEEDA 開発チームでは、XP のプラクティスを大きく取り入れて日々の開発を進めています。
私は入社前から XP やスクラムのようなアジャイル開発手法とその考え方には慣れ親しんでいたのですが、SPEEDA 開発チームに参加してみて、ユニークだなと感じたことがいくつもありました。

この記事では、SPEEDA 開発チームで私が特にユニークだと感じた 3 つのことについて紹介します。

おことわり

この記事の目的は、SPEEDA 開発チームのユニークな文化を紹介することです。
これらの取り組みには、SPEEDA 開発チームの現在のフェーズに固有の部分も少なからずあると思っており、これを読まれた方の組織やチームで、ここに書かれているようなことをただちに実践することを勧めるものではありません。

私自身、チームに入ってからしばらく経ちますが、まだ十分には消化しきれていない部分もあり、これからも実践しながら考え続けていきたいと思っています :)

その 1: チームのメンバーを「安定させない」

SPEEDA 開発チームは、開発を担う機能エリアごとに、数名程度の小さなチームに分かれています。(以後、この「小さなチーム」のことを単に「チーム」と呼ぶことにします)

日々の開発(スタンドアップや見積もり、ふりかえり等を含む)はこのチームごとに行うのですが、このチームのメンバーは、数ヶ月に一度入れ替えを行っています。実際、私が入社してからも一度入れ替えがあり、7 名いたメンバーのうち 2 名が交代しました。 *1

個人的には、チームのメンバーがお互いの個性やチームの文化・慣習を知り、チームとして安定してパフォーマンスを出せるようになるには時間がかかるため、頻繁な入れ替えは避ける方がよいと考えていました。

では、なぜあえてチームメンバーを安定させず、メンバーが「流れる」ようにするのでしょうか?
今のところ、私は以下の理由があると考えています。

  • やる理由: 製品全体についての知識、および文化を共同所有するため。
    • 個々のチームに知識を閉じず、開発チーム全体で製品全体を共同的に理解し、開発・保守・運用できるようになることが目的。また、文化についても同様。
  • できる理由: 「さまざまなものが流れる」文化があるため。
    • チームメンバーの入れ替えに限らず、SPEEDA の開発チームでは多くのものを安定させず、常に流れる状態に置いている。「流れる」ことが定常状態であるため、メンバーの入れ替えもそのうちの一つにすぎず、チーム・メンバーのいずれも入れ替えにすばやく適応できる。

よく見ると、「やる理由」と「できる理由」は表裏一体になっています。メンバーを入れ替えることによって知識や文化が撹拌され、それによってさらに入れ替えがやりやすくなる、という正のフィードバックの関係です。

その 2: 属人化を防ぐためにドキュメントを「残さない」

SPEEDA 開発チームでは、ドキュメントを用いることがかなり稀です。

アジャイルソフトウェア開発宣言では「包括的なドキュメントよりも個人と対話を」価値とする、と言われていますが、これほどまで対話(特に口頭での会話)に重きを置くソフトウェア開発組織はかなり珍しいのではないかと思います。

アジャイルソフトウェア開発宣言でも「ドキュメントは不要」とまでは言っていない、とはよく言われることで、個人的にも、安定して開発を進めるためには必要十分な量のドキュメントを書いた方がよいと考えていました。

では、なぜドキュメントを「最少限」と言い切れるほどのレベルにまで減らすのでしょうか?
現在の私の理解では、以下の理由があると考えています。

  • やる理由: ドキュメントを減らせば、会話せざるを得ないため。
    • やや本末転倒にも見えるが、知識が「流れる」ために必要な仕掛けと言える。
  • できる理由: 知識は撹拌されており、また常に会話する文化があるため。
    • 「その 1」で紹介したように、SPEEDA の製品についての知識や文化は常に流動し、撹拌されている。また、常時ペアプログラミングを行なっていてペア間の会話は絶えないほか、他ペアや他チームからの割り込みも奨励されており、「質問されたらすぐに答える」ことが徹底されている。

おそらくお気付きのように、ここにも正のフィードバックがあります。ドキュメントを減らせば会話が増え、会話が増えれば、さらにドキュメントを減らせるようになります。
よく「属人化を防ぐためにドキュメントを残す」と言われますが、私たちのチームでは、いわば知識が「チーム全体に属人化(属チーム化?)」したような状態といえます。

その 3: 担当者を「明確にしない」

SPEEDA 開発チームでは、「担当者を明確にしない」ことがよくあります。

たとえば、チームでのふりかえりを行なった際は次週に取り組むアクションを決めますが、私が初めてチームでのふりかえりに参加したとき、アクションの担当者を決めずにふりかえりが終わったことに驚きました。「ちゃんと決めておかないと、みんな忘れてしまったらどうするの?」と思ったのですね。

しかしこれは杞憂でした。当たり前といえば当たり前ですが、誰かが覚えているのですね。
アクションは次週にきちんと行われました。

とはいえ、一般論としてはアクションの担当者を決める方が確実です。私が初めて会ったチームのふりかえりのファシリテーターだったら、「誰か担当者になってくれますか?」と聞くと思います。

他にも、SPEEDA 開発チームでは、普通なら担当者を決めるであろう場面で、あえて決めない、とすることが多くあります。

これはなぜでしょうか?
私の考えでは、以下が理由です。

  • やる理由: 「活動の最小単位」をチームとし、何らかのタスクが個人に帰属することを防ぐため。
    • 何かを個人のタスクとしてしまうと、そのプロセスについての知識と、責任が個人に紐づいてしまう。そうならないよう、万事を個人ではなくチームの関心事にとどめるために、担当者を決めない。
  • できる理由: いつでも活動の最小単位をチームとしており、そうすることに慣れているから。
    • つまり、普段からそうする訓練をしているから。

「やる理由」についてはともかく、「できる理由」については「やっていたらできるようになった」という理由しか思いつきませんでした :)
チーム全体でこれを意識していると、不思議と補い合うようになります。失敗することも時にはありますが、その都度会話して対応します(ただし、できる限り「担当者を決める」以外の方法で)。

「その 1」と「その 2」では「知識」や「文化」が流れていましたが、ここでは「担当」が流れています。*2

そうやってどこへ行きたいのか、そしてこれから

ここまで紹介してきたように、SPEEDA の開発チームでは多くのものが「流れて」います。

私の個人的な感覚では、「ほとんど全てが流れている」と言ってもいいように思います。他にも、今回は紹介しなかったペアプログラミングのやり方や、ミーティングの進め方、製品のデザイン等々、多くの場面が「流れ」の中にあるように感じます。

では、「流れ」それ自体が目的なのでしょうか?
おそらく、Yes でもあり No でもある、と言えます。

ソフトウェア開発という営み自体が絶え間ない流れの中にあり、SPEEDA はソフトウェアサービスなので、その開発チームにあって流れは重要です。そのため、「流れ」はそれ自体として目的の一つになりえます。

一方で、「流れ」は究極の目的ではないはずです。
ユーザベースには「経済情報で、世界を変える」というミッションがあり、それを支える SPEEDA プロダクトチーム(開発チームもここに属する)のミッションは「技術力で、ビジネスをリードする」です。だとすれば、この「流れ」の文化はそれを支えるものであるはずだし、そうあるべきです。

たとえば、以下のインタビュー記事で CTO の林が掲げている「最高の開発チームをつくる」は、「流れ」によって支えられる大きな目標の一つなのでしょう。

journal.uzabase.com

私はまだチームに参加して 2 ヶ月足らずですが、これからもチームで仕事をしていく中でこの流れの文化の究極の意義を見つけ出し、発展させていくことを楽しみにしています!

*1:ちなみに入れ替えを行うこと自体は半ばルール化されていますが、誰が入れ替わるかはメンバー自身の意思によって決まります

*2:もしかしたら、チームの中を「漂っている」と言った方がより的確かもしれません。もっとも「チーム」という単位で見れば、担当は「定まっている」とも言えますが、そのチームのメンバーでさえ入れ替わるので、やはり流れている、漂っているという表現の方が的確に思えます

Istioでマイクロサービスのテスタビリティを向上させる

SPEEDAの開発チームの石橋です。

最近ではマイクロサービスでプロダクトを開発することが多くなってきていると思います。 そういった状況の中でマイクロサービスのテスト、特に異常系のテストをするコストがやや高いという話を何度か耳にしました。 本記事ではIstioのFault Injectionで「エラーが発生する」、「処理に時間がかかる」などの異常系のテストを容易に実現する方法を紹介します。

異常系のテストをする際の課題

サービスAとサービスBがあるとします。サービスAからサービスBにリクエストした際、サービスBがエラーになる可能性があります。 そうした場合、そのエラーが他のマイクロサービスに伝播して、障害がシステム全体へと波及しないよう適切に処理する必要があります。

適切な処理ができていることをテストするためは、「サービスAが正しいリクエストをしてもサービスBが必ずエラーを返す」状態を作らなければなりません。 このような状況を作るためにはいくつか方法が考えられます。

  • 必ずエラーを返すようにサービスBのコードを修正してデプロイする。
  • 必ずエラーを返すモックサーバーを用意し、サービスAの向き先をサービスBからモックサーバーに変更する。

これらの方法はやや面倒だと思います。特に前者はローカルで一時的に修正して確認する分にはよいかもしれませんが、正常系と混在している自動テストに組み込むことは困難です。 IstioのFault Injectionはこの課題を解決します。設定ファイルを適用するだけで「サービスAが正しいリクエストをしてもサービスBが必ずエラーを返す」状態を作ることができます。

環境構築

本記事ではバージョンが1.2.2のIstioを使用して動作確認しています。

まずはKubernetesに必要なリソースを作成していきます。Gatewayはdefault、ServiceとDeploymentとDestinationRuleとVirtualServiceはexample-nsというnamespaceに作成していきます。VirtualServiceはDestinationRuleの後に作成してください。*1 kubectl apply -f ファイル名を実行すれば、それぞれのリソースが作られます。

※それぞれのリソースの概要や設定ファイルの詳細な書き方については割愛します。

Gateway
Service
Deployment
DestinationRule
VirtualService

また、外部からアクセスするためのエンドポイントを環境変数で定義しておきます。後で何度もアクセスするためです。

export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].port}')
export GATEWAY=$INGRESS_HOST:$INGRESS_PORT

Fault Injection

まずはFault Injectionする前に上の環境構築で作ったPodに対してアクセスし、正常に処理されることを確認します。

$ for i in {1..10}; do sleep 1; curl -s -H 'Host: example-svc.example-ns.svc.cluster.local' $GATEWAY -o /dev/null -w '%{http_code}\n' ; done
200
200
200
200
200
200
200
200
200
200

10回リクエストし、HTTPのステータスコード200が10回返ってきているので正常に処理されたことがわかります。

Injecting HTTP Aborts

ここからが本題です。exampleというアプリケーションがHTTPのステータスコード500を返すようにします。virtual-service.yamlの内容を以下のように変更します。

もとのvirtual-service.yamlの最後にHTTPのステータスコード500を必ず返す設定を追加しました。

$ diff virtual-service.yaml 500-virtual-service.yaml -u
--- virtual-service.yaml    2019-07-30 17:21:55.000000000 +0900
+++ 500-virtual-service.yaml    2019-07-30 18:14:22.000000000 +0900
@@ -17,3 +17,7 @@
         port:
           number: 80
         subset: v1
+    fault:
+      abort:
+        httpStatus: 500     
+        percent: 100

500-virtual-service.yamlを適用し、上のcURLを再度実行します。

$ for i in {1..10}; do sleep 1; curl -s -H 'Host: example-svc.example-ns.svc.cluster.local' $GATEWAY -o /dev/null -w '%{http_code}\n' ; done
500
500
500
500
500
500
500
500
500
500

意図通り、すべてHTTPのステータスコード500が返ってきています。もちろんpercentを変更すれば、任意の割合でHTTPのステータスコード500を返すことができます。

Injecting HTTP Delays

次はレスポンスが返ってくるのに時間がかかるパターンです。500-virtual-service.yamlの内容のhttpStatus:fixedDelay:に変更しています。ここでは10sにしているので、レスポンスが10秒遅延します。

システム全体が遅くならないために、呼び出し側のマイクロサービスがタイムアウトなどを適切に行えているかをテストする際に有用です。

最後は少し応用的な例です。ヘッダーにx-e2e-case: errorが付いていれば、HTTPのステータスコード500が返ってきます。付いていなければ、exampleというアプリケーション本来のレスポンスが返ってきます。

1つ目のcURLはヘッダーにx-e2e-case: errorを付けています。2つ目のcURLはヘッダーにx-e2e-case: errorを付けていません。 それぞれのcURLを実行すると、期待通りのステータスコードが返ってきます。

$ curl -s -H 'x-e2e-case: error' -H 'Host: example-svc.example-ns.svc.cluster.local' $GATEWAY -o /dev/null -w '%{http_code}\n'
500
$ curl -s -H 'Host: example-svc.example-ns.svc.cluster.local' $GATEWAY -o /dev/null -w '%{http_code}\n'
200

また、VirtualServiceのmatchは強力で、ヘッダー以外にもURI、ポート番号、クエリパラメータなどでマッチさせることができます。*2

E2Eテストを実行する環境用のVirtualServiceを作り、そこに特定の条件でエラーや遅延が発生する設定を書いておけば、異常系のケースを正しくハンドリングできていることを確認するテストも容易に行えると思います。

まとめ

マイクロサービス化によって様々な恩恵を受けることができる一方で複雑度が上がるとことがあります。複雑度が上がったものの1つがネットワークです。 マイクロサービスではネットワーク越しの通信が当たり前になるので、通信が失敗することを前提として実装するべきです。そして、その実装をテストしやすくするのがFault Injectionです。 Istioの場合だと、VirtualServiceでFault Injectionの設定ができます。

参考

Clojure 1.10.x時代の新しいデバッグツール

こんにちは!こんにちは!SPEEDA開発チーム(通称PDT)に所属しているあやぴーid:ayato0211です。

弊チームでは新しいモノを開発するときに、よくClojureという言語が採用されています。だいたい言語別でシェア2位といったところでしょうか。1位はみんな大好き(?)Kotlinで、こちらはサーバーサイドでの利用が主になっていて、次にE2Eのテストコードを書くときに良く利用されています。

今日はそんな弊チームでよく使われているClojureについて、Clojure 1.10.x時代の新しいデバッグツールをお伝えできれば良いなと思い、この記事を書こうと思った次第です。

大きなデータ構造を簡単に把握したい

Clojureは素晴らしい言語です。Clojureはとても素晴らしい言語です。大事なことなので、2回言いました。それはさておき、Clojureでアプリケーションを書いていると、どうしても大きなデータ構造が出てくる瞬間があります。例えば、Ringのリクエストマップや、Ductのコンフィグマップなどが該当します。それ以外にもマップを引数に取る関数を書くことは往々にしてあるかと思います。

これらの大きなデータ構造というのは、実際に中身を確認するのが非常に面倒です。まず思いつくのは、伝統的なプリントデバッグだと思います。このとき、よくあるのはそのマップがどんなキーを持っているのか分からないので、①とりあえずマップ全体を出力してみて、②注目したい目的のキーをなんとか探し出し、③目的のキーと対応する値だけを出力するように修正して④再度確認を行うという①~④の繰り返しをすることです。

この伝統的な方法はデータ構造が大きければ大きいほど目当ての値を見つけるのが難しいです。僕はよく出力した後に迷子になっています。できればもっとインタラクティブに出力したデータ構造を把握しやすい方法が欲しいところです。そういうツールを今回は紹介したいと思います。

Cognitect's REBLを使う

インタラクティブにデータ構造を把握するためのツールとして、オススメしたいのがCognitectが開発しているREBLというツールです。

REBL、つまりRead Eval Browse Loopということで、その名前の通りただ標準出力に吐く代わりに値をブラウジング(走査)できるというわけです。REBLを使うために必要なのはClojureのバージョンが1.10.0以上であることです。Clojure 1.10.0で新しく追加されたtap>を使うと、REBL上に任意のデータを送ることができ、簡単にブラウジングすることができます(より厳密に言えばDatafyなどの話も出てくるんですが一旦脇に置いておく)。

このREBLをどうやって既存プロジェクトで利用できるようにするのか、Ductの簡単なアプリに組み込む例を見せながら解説したいと思います。

REBLのダウンロードとインストール

REBLは以下のページからダウンロードすることができます。

ライセンス条項をよく読んでほしいのですが、個人の趣味として利用する場合にはあまり問題ありませんが、仕事で利用する場合はDatomicのアクティブな有償ユーザーもしくは、REBLのパトロンでなければなりません。このことに注意してダウンロードしてください。

zipファイルをダウンロードしたら適当な場所に展開しておきます。今回は/home/{{username}}/opt/REBL-0.9.172/REBL-0.9.172.jarとなるようにしました。これでREBLのインストールは完了です。

プロジェクトの準備

次にREBLを使う対象のプロジェクトを準備します。今回はDuctのAPIを簡単に作成して、ついでにexampleハンドラーも生やしてしまいます。以下のコマンドでプロジェクトを作成します。

$ lein new duct demo --template-version 0.12.1 -- +api +ataraxy +example
$ cd demo
$ lein duct setup

普段、意識して--template-versionを指定することはありませんが、この例と全く同じ状況を作りたければ、このように指定することで同じ雛形のバージョンを利用できます。

REBLを利用できるようにする

まずは/home/{{username}}/.lein/profiles.cljに以下の情報を追記します。REBLは開発時に使うもので、開発環境(Javaのバージョン)によって必要となる依存関係が微妙に異なるので、開発者のマシン毎に以下の設定はあると良いでしょう(REBLのREADMEに書いてあるUsageを参照)。僕の手元の環境が今はJava 11になっているのでJava FX関連の依存関係が入っています。また先程インストールしたREBLのJarのパスを:resource-pathsに記述します。

{
 ;; 以下の部分を書き加える
 :tools/rebl
 {:dependencies [[org.clojure/core.async "0.4.500"]
                 [org.openjfx/javafx-fxml "11.0.1"]
                 [org.openjfx/javafx-controls "11.0.1"]
                 [org.openjfx/javafx-swing "11.0.1"]
                 [org.openjfx/javafx-base "11.0.1"]
                 [org.openjfx/javafx-web "11.0.1"]]
  :resource-paths ["/home/{{username}}/opt/REBL-0.9.172/REBL-0.9.172.jar"]}}

次に先程用意したプロジェクトのプロジェクトルート以下にあるprofiles.cljを以下のように書き換えます。profiles.clj.gitignoreされるので、個人用の設定を書く場所として適しています。

;; Local profile overrides
{:profiles/dev
 [:tools/rebl]}

最後にdev/src/local.cljを以下のように記述します。このように書くと、Ductのプロジェクトで最初に(dev)とするとこのファイルが自動的にロードされ、REBLの画面を立ち上げる関数(start-rebl)が使えるようになります。開発しているプロジェクトでチーム全員の合意がとれる場合は、dev/src/dev.cljなどに直接REBLを起動させるための関数を書いても良いかもしれません。

;; Local REPL configuration
(when-not (try
            (require 'cognitect.rebl)
            (catch Exception _))
  (defn start-rebl []
    ((resolve 'cognitect.rebl/ui))
    (add-tap clojure.pprint/pprint)
    (println "REBL started")))

ここまででREBLを利用する下準備が整いました。

REBLにデータを送信する

まずは普段どおりにREPLからシステムを起動をして、REBLもあわせて起動させておきます。

user> (dev)
;;=> :loaded
dev> (go)
:duct.server.http.jetty/starting-server {:port 3000}
;;=> :initiated
dev> (start-rebl)
REBL started
;;=> nil

このまま例えばターミナルなどからcurl localhost:3000/exampleとしても、REBLの方には何も出力されません。なので、src/demo/handler/example.clj:demo.handler/exampleコンポーネントを次のように少し書き換えます。

(defmethod ig/init-key :demo.handler/example [_ options]
  (fn [{[_] :ataraxy/result :as req}]
    (tap> req) ;; <- 追記
    [::response/ok {:example "data"}]))

リクエストマップをtap>に渡すようにしました。こうして、REPLなどからシステムを(reset)などを実行して再起動します。そして、先程と同様にcurl localhost:3000/exampleとターミナルから実行してみます。するとREBLの「tap」タブに次のようにリクエストマップが出力されていると思います。

f:id:ayato0211:20190702182751p:plain

この「tap」タブの画面下部にある「Browse」ボタンを押すと「browse」タブに表示が切り替わります。「nav->」入力欄に例えば0 :headersと入力してEnterを押してみましょう。すると次のような画面になっているはずです。nav->のところはget-inと同じ要領で書けると思えば問題ないと思います。

f:id:ayato0211:20190702182849p:plain

今までであれば出力したデータを見ることしかできませんでしたが、REBLを使えばこのように一度出力した値をREBL上で走査することができます。これにより今までより大きなデータ構造などを把握しやすくなり、デバッグなどもやりやすくなるはずです。

REBLのより具体的な使い方は実際に触るか、次の動画を見てもらえると良いかなと思います。

まとめ

今までプリントデバッグでprintlnpprintなどと書いていたところを、tap>と書くだけでREBLにデータの情報を送ることができ、REBL上でインタラクティブにデータを見ることができるようになりました。これにより、Clojureで開発されたアプリケーションをよりデバッグしやすくなったりするのではないでしょうか。

余談

今回は説明していませんが、Clojure 1.10.0で追加されたdatafyなどをうまく活用すると、REBL上で走査できる対象を広げることができます。実際、ネームスペース(clojure.lang.Namespace)などはDatafiableを実装しているため、REBL上でブラウジングすることができるようになっています。REBL左上のパネルに(the-ns 'dev)など入力して評価してみると分かると思います。このあたりの話もいずれ書けたら良いなーと思ったり思わなかったり。

Kubernetes + Istioでblue-green deploymentを実現する

こんにちは、SPEEDAのSREチームの阿南です。最近Kubernetes界隈が盛り上がっていますね。ここ一年で、各企業での利用事例やKubernetesを解説している書籍等もかなり増え、活用の仕方も徐々に確立されて来ているのではないでしょうか。一方Istioについては、色々できそうということはわかったんだけど、利用事例も少ないためどう使えばいいかわからない、本番運用しているという企業もまだあまり聞かないし導入に踏み切れない、という方も多いかと思います。弊社ではまだ2つのnamespaceのみですが、Istioで運用を開始しております。今回は、Istioで実現できることはなんとなく知っているが、具体的な設定方法がよくわからない方向けにblue-green deployの設定を参考に説明してみたいと思います。

Istioの通信について知りたい方はこちら

環境

GKE 1.9.7-gke.11 ,Istio1.0.2 を利用しています。

IstioのRouting Rule

IstioのRouting Ruleには大きく4つのリソースがあります。

resource 説明
Gateway HTTP/TCPのリクエストを受付. どのHost or Portのアクセスを許可するかのルールを記載。 Ingress / Egress のルールを適用できる
VirtualService ルールにマッチしたリクエストをKubernetesのサービスにルーティングする. weightやheaderの情報を利用した振り分けを定義でき、Istioの設定の要とも言える
DestinationRule trafficの振り分けルールを設定. (例: round robin, least connection 等)
ServiceEntry 外部サービスを登録. 例えば、Kubernetes(Istio)の外側にあるDBの接続先等

この4つが適用された通信のイメージを手書きして見ました。まず、クラスター外部から内部への通信です。

f:id:tanan55:20181118130152p:plain

Gatewayを通ったリクエストがVirtualServiceのルールに基づいて各サービスにルーティングされます。その際に、DestinationRuleに基づき最終的にリクエストが送られるpodが決まります。(DestinationRuleでsubsetが設定されていない場合は、RoundRobinで均等に分散される)

続いて、クラスター内部から外部への通信です。

f:id:tanan55:20181118132321p:plain

コンテナから外向きに通信が発生した後、ServiceEntryのルールに基づきRequestを許可するかどうか判断します。ルール次第で、Egressgatewayを通る場合とそうでない場合の2種類があります。個人的には、まだEgressgatewayを使ってないので、外部のエンドポイントを登録する際はServiceEntryに毎回登録しています。

blue-green deploymentの設定

まずは、Kubernetes のPodをサービスとして稼働させるためのリソースを作成します。

  • namespace

gist.github.com

Namespaceを作成する際に istio-injection: enabled を設定しておきます。この設定で自動的にistio-proxyがサイドカーコンテナとして起動するようになります。

  • deployment

gist.github.com

Deploymentでは、 version: blueversion: green の2つnginxを稼働させます。Istioの設定を行う際に、このlabelを元にBlue-Greenができるように設定します。

  • service

gist.github.com

serviceでは、port: 40001で待ち受け、app: nginxtargetPort: 80番へフォワードします。

続いて、Istioのリソースです。Istioのリソースについては、istioをインストールした際にCRDに登録されていますので、kubectl コマンドを利用して設定の反映が可能です。

  • gateway

gist.github.com

gatewayには許容するHostを指定します。正規表現も可能です。 今回は sample.hoge.com を登録します。

  • virtualservice

gist.github.com

gatewayのルールにマッチした通信が、virtualserviceのルールに基づいてkubernetesのServiceへルーティングされます。ポイントとして、headerに x-version: blue とあれば、subset: blue のdestinationruleが適用されるようにしています。このほかの通信については全てgreenにアクセスされます。この設定を利用してblue-greenの切り替え前にblue環境のテストに利用が可能です。

  • destinationrule

gist.github.com

subsetがblueであれば、version: blue のラベルを付与し、subsetがgreenであれば、 version: green のラベルを付与します。これによって、どのPodにアクセスが振り分けられるかが決まります。以上で、設定が完了です。設定を確認するには下記のコマンドを実行します。

$ kubectl get gateway
$ kubectl get virtualservice
$ kubectl get destinationrule

今回構築した環境のイメージは下記の通りです。

f:id:tanan55:20181118230108p:plain

では、実際にアクセスしてみます。今回は、nginxコンテナに出力されたログを見ることにより、どちらのPodにアクセスされたかを確認します。 下記のコマンドでコンテナのログをtailしておきます。

$ kubectl logs -f -n sample-ns $(kubectl get pod -n sample-ns | grep blue | cut -d' ' -f1) -c nginx
$ kubectl logs -f -n sample-ns $(kubectl get pod -n sample-ns | grep green | cut -d' ' -f1) -c nginx

まずは、headerにx-versionを記載せずアクセスします。

$ kubectl get svc istio-ingressgateway -n istio-system
// EXTERNAL-IP を確認
$ curl -XGET -H 'Host: sample.hoge.com' http://<EXTERNAL-IP>/

f:id:tanan55:20181119223402p:plain

最初にアクセスした際は、全てのリクエストがblueのPodにしかリクエストが送られません。

ここで、headerにx-version: green を付けてアクセスします。

$ curl -XGET -H 'Host: sample.hoge.com' -H 'x-version: green' http://<EXTERNAL-IP>/

f:id:tanan55:20181119223549p:plain

特別なheaderをつけることで、特定のリクエストのみを新規にリリースしたgreenに送ることができます。greenが正常なことを確認できたら、blueとgreenを切り替えます。切り替えの方法は、先ほど設定したvirtualserviceのsubsetのblue-greenを逆転させるだけです。

gist.github.com

上記のyamlを適用後、アクセスすると、blueとgreenが入れ替わり、Blue-Green Deploymentが出来ます。

Istioを使い始めてこれはいい!と思ったポイントとしては、本番環境と全く同じ環境でテストができるということです。検証環境と本番環境で差分があってリリースがうまく行かないというのはよくあることだと思いますが、Istioを使うことで、テストができている状態のPodにアクセスを切り替えることが簡単に実現できます。さらにこれを応用して、特定の割合を振り分けたり、社内ユーザのみに新しい環境にアクセスしたりといったことをPodに変更を加えることなく実現できるようになります。 今回は紹介していませんが、gateway や istio-proxy で telemetryを収集でき、grafana / jaeger 等でリクエストのトレースやレスポンス速度を簡単に可視化できるのでこの辺りをフルスタックで揃えているIstioには最初感動しました。ただし、ハマりどころの多さ(本当に多い)や安定性といった面ではまだまだ成熟していない(バグ結構ある)ので、まずは影響の少ないnamespaceから、かつ、メイン機能となるルーティング周りからIstioを使い始めて、運用ノウハウを貯めていくのがいいかなと個人的には思っています。

仲間募集!!

ユーザベースのSPEEDA SREチームは、 「No Challenge, No SRE, No SPEEDA」 を掲げて業務に取り組んでいます。

「挑戦しなければ、SREではないし、SREがなければ、SPEEDAもない」という意識の元、ユーザベースのミッションである 「経済情報で、世界をかえる」 の実現に向けて、日々邁進しています。

IstioやKubernetes以外にも様々なことにチャレンジしてますので少しでも興味を持ってくださった方はこちらまで!

Istioを使いこなすために知っておくこと

こんにちは、SPEEDAのSREチームの阿南です。最近Kubernetes界隈が盛り上がっていますね。ここ一年で、各企業での利用事例やKubernetesを解説している書籍等もかなり増え、活用の仕方も徐々に確立されて来ているのではないでしょうか。一方Istioについては、色々できそうということはわかったんだけど、利用事例も少ないためどう使えばいいかわからない、本番運用しているという企業もまだあまり聞かないし導入に踏み切れない、という方も多いかと思います。弊社ではまだ2つのnamespaceのみですが、Istioで運用を開始しております。今回は、Istioで実現できることはなんとなく知っているが、内部の通信の仕組みやどのようなコンポーネントがあるのかを追っていきたいと思います。その上でBlue-Green Deploymentを実現するための設定方法についても次回記事で触れていきます。

この記事で扱うこと

  • Istioを構成するコンポーネントについて
  • Istioの通信の流れ

Istioを構成するコンポーネント

Istioのコンポーネントについては、IstioのDocumentationに下記の図が紹介されています。

f:id:tanan55:20181125110511p:plain

図の上側はdata planeで、サービスのPodにサイドカーコンテナ(Envoy) が起動し、Podへのリクエストを中継しています。 図の下側はcontrol planeで、Pilot, Mixer, Citadel が稼働します。Pilotは各サイドカーコンテナにConfigを反映し、Mixerはtelemetry収集やPolicy check(アクセスコントロールや流量の制御等)を担います。Citadelは証明書の管理を担当します。

では、実際にIstioをデプロイしてみて確認してみます。

f:id:tanan55:20181008191459p:plain

デプロイの方法については省略しますが、コマンド一つで大量のPodが起動してきますので、一見面食らいますが、順に整理していきます。(grafana, prometheus, servicegraph, tracingについては実際のサービス通信とは関係が薄いため、省略します)

pod 役割
istio-citadel 証明書の発行、管理を実施
istio-egressgateway Istioの内部から外部へ通信するためのgateway
istio-galley ユーザが定義したIstio設定のvalidationを実施
istio-ingressgateway Istioの外部から内部へ通信するためのgateway
istio-pilot ユーザが定義したIstio設定を反映
istio-policy Mixerの一部. istio-proxyに来たrequestをチェック
istio-telemetry Mixerの一部. 各podからテレメトリを収集
istio-sidecar-injector Podが起動する際に、リクエストをhookしてサイドカーを auto Injectする

上記のコンポーネントでIstioの外からどのようにサービスのPodまでリクエストが到達しているのかを図で表現してみました。

f:id:tanan55:20181125114804p:plain

上図の番号順に処理内容を解説します。

  • リクエストが来る前にEnvoyをサイドカーコンテナとしてInjectしておく。(⓪)
  • ingress-gatewayへリクエストが到達。ここで、hostやpathベースで、どのサービスPodにリクエストをforwardするか決める。(①)
  • istio-proxyへリクエストが到達。実際には、サービスPodへのリクエストをPreroutingしている。例えば、サービスコンテナが80番で稼働していた場合、80番に来たリクエストを15001(Envoyポート)に流す。(②)
  • istio-proxyからistio-policyにアクセスし、Policyのチェックを実施。例えば、流量の制御(最大同時アクセス数100等)で条件にひっかかった場合は、サービスのコンテナに到達する前にその時点でリクエストが返される。(③)
  • Policyでチェックを通ったリクエストがサービスコンテナに到達。(④)

この辺りは、実際に通信の流れを理解するまでに時間がかかりました(今も勉強中です)。ご自身で確認したい方は、実際にistio-proxyのコンテナでshellを起動して、tcpdump, netstat等を取ってみると理解が進むと思います。istio-proxyにはデフォルトでこの辺りのコマンドがインストールされているので、スムーズに調査ができます。
今回、実際に通信をみていった結果、想像以上に色々なことをしてるなという印象でした。特に、PolicyチェックのためにMixerにアクセスしている点は結構重要で、istio-policyのPodがダウンするとサービスの通信もダウンしてしまいます。本番運用する際は、この辺りのConfigurationも注意して見ていきましょう。ちなみに、Istioは必要なコンポーネントに絞ってインストールが可能なので、最初はistio-policyなしで運用するのも十分ありだと思います。

今後Istioがどの程度、使われ広まっていくかはまだわかりませんが、Service Meshに必要な概念や機能を勉強するには良いソフトウェアだと思いますので、皆さんもぜひ一度触れてみてください。次回の記事では、通信の流れやコンポーネントを理解した上で、Istioを使ったBlue-Green Deploymentの方法をみていきます。

Kubernetes + Istioでblue-green deploymentを実現する

仲間募集!!

ユーザベースのSPEEDA SREチームは、 「No Challenge, No SRE, No SPEEDA」 を掲げて業務に取り組んでいます。

「挑戦しなければ、SREではないし、SREがなければ、SPEEDAもない」という意識の元、ユーザベースのミッションである 「経済情報で、世界をかえる」 の実現に向けて、日々邁進しています。

少しでも興味を持ってくださった方はこちらまで!

Vue.jsとFirebaseとGASとiPadで会議室表示板を作った

こんにちは、コーポレートエンジニアリングチームという新チーム所属のたけうち(@chimerast)です。ユーザベース社内ではレアポケモンと呼ばれています。

数ヶ月前、メンバーも数百人を超え、アメリカのQuartz社がユーザベースグループに加わったりして、業務改善を主として行う管理部門付けのエンジニアチームが欲しいという話になり、新しいチームが作られました。名前はなんでも良かったのですが、世の中的に流行っているのとエンジニア採用に効きそうなので、コーポレートエンジニアリングチームという名前になっています。

コーポレートエンジニアリングチームという名前がついてはいるものの、ユーザベース社の場合はその業務範囲は幅広く、業務改善、情報セキュリティ、内部統制、情報システム、全社リスク管理等々、管理部門でITが絡めば大体担当になります。

あと、普通の情シスチームでも面白くないので、

いいのがない、作ればいいじゃん、ホトトギス
使いにくい、作ればいいじゃん、ホトトギス
お金が無い、作ればいいじゃん、ホトトギス

の精神で、バックオフィス業務全般(経理・財務・法務・人事・労務・総務)や監査もわかり、システム開発できる情シスを目指しています。

業務改善軸で数ヶ月前に作ったもの: 会議室表示板

会議室が誰が使っているのか、いつからいつまで使うのかが一目見てわかるものを作りました。iPad用のシステムですが、iOSアプリではなくウェブアプリです。Google Calendarの会議室予約と連動します。

f:id:chimerast:20180724113649j:plain:w280f:id:chimerast:20180724113659j:plain:w280

作った経緯

ユーザベース社は今年2018年7月に六本木にオフィスが引っ越しました。

ピッカピカの新オフィスで、会議室も恵比寿オフィスの時の4部屋から9部屋に倍増、会議室からの眺めもよいという非常に良い環境でテンションも上がります。そして、そこには壁への埋め込み式のiPadが。創業期の狭い8畳ぐらいのオフィスから、数回のオフィス引越しを経て、ついに弊社オフィスにも近代化の波が押し寄せてきたのかとわくわくしたものです。

が、そこまでは良かったものの、オフィス引越チームが選定していた会議室表示システムを入れてみたところ、

  • iPadの画面をタッチしても反応が悪い・遅い、謎のスワイプさせるUI
  • 会議室名についている色に合わせて画面背景の色だけでも変更したいのにできない
  • 現在行っているミーティングの予定しか見れず、次に自分が使う会議室なのか分からない

という状況で使いにくい。社員は会議室を探してうろうろしているし、なにより自分が会議室を使おうとしたときにイライラするという状態でした。

サービスを提供している会社さんには悪いとは思いつつも、普通のiPadだしウェブアプリとして作ればそんなに時間がかからず作れるだろうと思い、強権発動して作って入れ替える事にしました。

これくらいの用途であれば、ウェブアプリとして作ってもサクサク動きます。また、作るのもデプロイするのもiOSアプリと比べると圧倒的に楽です。 一晩夜なべしたらできました。

f:id:chimerast:20181120020512p:plain:w280f:id:chimerast:20181120020518p:plain:w280

作り方および使った技術

Vue.js+TypeScript、Firebase、Google App Scriptを使って作りました。

細かい所は難しいものではないので、はしょります。流れとしては下記になります。

  1. 会議室カレンダーの追加や変更イベントをGoogle App Scriptのトリガーで捕捉できるように設定する
  2. Google App ScriptからCloud Firestoreにカレンダーの追加・変更を登録する
  3. iPad上のウェブアプリからFirestoreのonSnapshot()を呼び出して、リアルタイムにFirestore上の変更を監視する

f:id:chimerast:20181120022515p:plain:w400

GASからFirestoreへの登録にはこちらのGAS用ライブラリを使わせてもらってます。 https://github.com/grahamearley/FirestoreGoogleAppsScript

全体のソースコードは残念ながら現時点では公開していないです。

ウェブアプリをiPadホーム画面に登録する方法

Safariの共有メニューから「ホーム画面に追加」というボタンを押せば、普通のWebサイトをアプリっぽく登録できます。後述しますが、いろいろHTMLに細工をするとほぼネイティブアプリっぽく見えるようになります。

f:id:chimerast:20181120020448p:plain:w280f:id:chimerast:20181120020458p:plain:w280

ネイティブアプリっぽく見せる細かいテクニック

iPadでそのままウェブアプリを表示すると、ブラウザだなーと一目見て分かってしまうので、いくつかHTMLに細工をしています。

上部の時計やバッテリー表示部分の帯を透過させる & 全画面つかえるようにする

iOS標準の状態だと、ウェブアプリを起動すると黒い帯が表示されてしまいます。また全画面にもならないです。

f:id:chimerast:20181120020523p:plain:w280

そのままだとかっこ悪いので、下記のHTMLをHEADタグ内におまじないとして追加するといい感じになります。

<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no,viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

f:id:chimerast:20181120020512p:plain:w280

ドラッグ操作をキャンセルする

iOSのブラウザの標準動作では、指でドラッグするとテキスト選択ができてしまいます。これができてしまうと一瞬でブラウザだとばれてしまい、見た目が悪いので、ontouchmoveのデフォルト操作を無効化します。

<body ontouchmove="event.preventDefault()">

タッチしたときに素早く反応するようにする

.rt-left(@touchstart="inuse = !inuse; return false" :style="{ backgroundColor: room.color }")

普通のPC用ブラウザでマウスクリックしたときのイベントとして利用する onclickonmousedownでも一応動くのですが、タップしても反応が悪かったり、微妙に遅れたりします。

元々入っていたシステムでもタッチ操作の反応が悪かったので、周りのアクリル板の静電気が悪さしてるのかなと思って、帯電防止スプレーを吹きかけてみたりしたのですが直らず、結局素直にスマホ用のontouchstartを使うことで今までの微妙な動作は何だったのかと思うぐらいキビキビ動くようになりました。

最近Safariのアップデートが進んで、onclickonmousedownもタップ操作で十分違和感なく動くようになったと思い込んでいたのですが、勘違いだったようです。素直にスマホ用のイベントを使いましょう。

その他iPadを使った社内システム向けテクニック

ホームボタンを押しても操作できないようにする

iOS標準のアクセスガイド機能を使います。暗証番号を入れないとホーム画面に戻れなくなります。

https://support.apple.com/ja-jp/HT202612

常につけっぱなしにする & 画面が暗くならないようにする

常につけっぱなしにするには、「設定」→「画面表示と明るさ」から、自動ロックを「なし」に設定すればOKです。

これだけだと明るさMAXにしておいても、周りの明るさによって微妙に暗くなったりします。これを避けるためには、「設定」→「一般」→「アクセシビリティ」→「ディスプレイ調整」→「明るさの自動調整」をオフにします。その上で明るさをMAXにします。

最後に

コーポレートエンジニアリングチームでは、業務改善と内部統制・情報セキュリティが表裏一体だということを熱く語れる人を募集中です!

また、12/4(火)に、この辺について熱く語れる人を集めて、情報交換するためのLT大会を開催します。もしご興味があればご参加ください!

お試し就職制度を導入した話と、導入するに至るまでの話

入社して約2ヶ月くらいしか経ってませんが、この技術ブログに初投稿です。皆さんご存知(?)あやぴーさんid:ayato0211です。 何を間違ったのか、社内では筋トレの人として名前が売れてしまいましたが、本業はClojureエンジニアです。所属しているのはSPEEDAの開発チームです。

さて、本業はClojureエンジニアです、と書きながら今日はタイトルにある通り、お試し就職制度を導入したよって話と、そこに至るまでのお話です。最近は有名なIT企業があちらこちらで行っているので、あまり珍しくはなくなったと思うのですが、弊社ユーザベースでもお試し就職制度を試験的に導入することになりました。いわばお試しのお試しですね。

お試し就職制度超概要

まずは端的にお試し就職の概要を簡単に書いておきます。

対象者

  • SPEEDAの開発エンジニアとして選考を受けている方
    • かつ、二次面接まで通過している方、で希望される方のみ
    • 残念ながらSREチームでは行なっておりません

もう少し条件を緩くしたい気持ちもありましたが、システムで扱っている情報が情報だけに選考をある程度進めた方のみを対象とさせてもらいました。

期間

  • 2,3日程度(これ以上の期間は応相談)

報酬

  • 要相談
  • 基本的に報酬は出ると考えてもらって大丈夫です

勤務形態

  • 8時間労働(短縮は応相談)
    • 実際の勤務時間は受け入れチームに依存します

勤務地

経験できること

  • SPEEDAの開発
  • アジャイル開発の現場
    • ペアプロ
  • 開発メンバーとのランチ(and/or ディナー?)
  • and more?

導入に至るまで

さて、本来であればこういった記事を書くのは、エンジニア職ではなくどちらかというと非エンジニア職(例えば広報や人事みたいな)なイメージがあると思います。ですが、これを書いているのがエンジニア職の僕である、というところにユーザベースの面白さというのがあると思うので、この記事の残りではこの制度を導入するに至るまでの経緯を書いていこうと思います。

きっかけ

話は少し遡るんですが、僕がユーザベースに入社するときに、最もネックに感じていたのは仕事の取り組み方のところでした。具体的には、僕が所属しているSPEEDAの開発チームでは、常にペアプロで開発を行なっています。僕にとってはこれが入社して、本当にうまくやっていけるのかと最も不安に感じたところだった、といっても過言ではありません。他にも本当にClojureのプロダクトがあるのか、都市伝説ではないのか、という不安などもありました(ちなみにClojureプロダクトはちゃんとありました!!)。といった感じで、一般的に同じエンジニア職だとしても、転職というのはそれなりにハードルや不安を感じるものだと思います(新しい会社に馴染めるのか、実はこわい会社じゃないのか、嘘ついてるんじゃないか、などなど)。

実際に入社して、数日ほど仕事をしていると徐々にこの開発チームの良さというか、素晴しさみたいなものを知ることができました。その中でもったいないなと思ったのは、この環境をあまり開発チームの面々が表立って 発信していない ということでした。また、こんなに素晴しいチームだともっと早く知ることができていたら、もう少し早く入社したいって思ったかもしれないのに、という気持ちもありました。

あ、ちなみに弊社CTO林のインタビュー記事で若干今のチームにも通じる話が書かれているので興味がある方は是非読んでみてください。

newspicks.com

実際に起こったこと

こういった想いがあり、自由を文化とする弊社であればお試し就職制度を作ることはできるだろうと考えました。そして、採用周りを担当してくれているカルチャーチームのメンバー(Kさん)とCTO林に、「どうですか、やってみませんか」と打診をしたのが導入開始に向けた動きのはじまりでした。このとき僕はまだ入社して3週間も経っていないくらいのときでした。

KさんもCTOの林も「いいですね、是非やりましょう」といった良い反応を示してくれたので幸先良いなと感じました。早速、Kさんに実現させたい場合どうしたら良いかと相談すると、簡単に総務と労務にコンタクトを取ってくれて、労務の方と話してみてくださいと道を作ってくれました。それから労務の方を捕まえて話をして、いくつかのアドバイスを貰うことができました。ここまでである程度の実現方法と課題は見えてきていて、とりあえずやるだけならやれそうというところまで形になりました。念の為、法務にも見てもらった方が良いだろうということで、法務の方に話をしてみると法務的な懸念点がいくつかゴロゴロでてきたので、ミーティングをして課題がクリアできることを確認してもらってokが出た、というのが先週、入社から1ヶ月半時点での話です。

伝えたかったこと

上の話を読んで「ふーん、それで?」という感想を持つ人はきっと多いだろうなと思います。ただ、僕はこの一連の話が実にユーザベースの文化を表わしていると感じています。具体的には以下のようなところです。

  • 入社して間もない人間の突飛な発言でも認めてくれる
  • 発言した人間が自分で動いていける(むしろ動かないといけない)
  • 様々な立場の人が行動をしている人間に手を貸してくれる
  • 現場レベルで相談して新しい制度/仕組みを作っていける

普段の仕事でも良いなあと感じることは多いですが、こういった経験は今までになかったのでとても新鮮でした。

最後に

今回は、お試し就職制度を導入したよって話と、そこに至るまでのお話を書きました。ということで、ユーザベースでは僕と一緒にClojureを書きたいエンジニアを募集しています。話だけでも聞いてみたいっていう方は僕に直接コンタクト取ってもらってもいいですし、下の募集から直接応募してくれても大丈夫です。

www.uzabase.com