Studyplus Engineering Blog

スタディプラスの開発者が発信するブログ

Firebase App DistributionへFabric Betaから社内向けアプリ配信を移行しました

こんにちは、新生モバイルクライアントグループの若宮(id:D_R_1009)です。 今回は社内向けのテストアプリ配信の更新について書きたいと思います。

移行の経緯

スタディプラスAndroidチームでは歴史的な経緯により、Fabric Betaを長らく利用していました。 しかしながら、2020年3月をもってFabric Betaがクローズすることになったため別のサービスへ移行する必要が生じました。

get.fabric.io

いくつかのサービスを検討し、結局Firebase App Distributionを利用することとしました。 いつの間にかベータ版になっていたのが決め手です。

firebase.google.com

試行錯誤したところ、今までと変わらない(一部より利便性を増して)環境構築をできたので共有したいと思います。 「もっといい方法があるよ!」などあれば、ブックマークにコメントを付けて頂けると嬉しいです!

テストアプリ配信環境

テストアプリを配信するにあたり、次の2つの方法を用意しています

  1. CI上で次期リリース用PR作成時に検証環境( Stage )/開発環境( Debug )向きのapkを作成し配信する
  2. ローカル環境(開発者のマシン)から検証環境( Stage )/開発環境( Debug )向きのapkを作成し配信する

1つ目はよくあるCD環境ではないかなと思います。 リリース直前に一通りアプリを触ってテストするため、リリース用PRをベースにビルドを走らせています。 また、サーバーチームのエンジニアが実際にアプリからサーバーAPIを叩く処理を触ることがあるため、社内向けにストアから配信したコードとほぼ同じものを配信する目的もあります。

2つ目のローカルマシンによる実行を用意しているのは、開発者が少なく機能ごとの開発が多いため、検証環境向きのデイリービルドを行っていないためです。 動いているプロジェクトの状況に応じて、またサーバーチームやデザイナーの求めに応じてビルドを走らせてるため、ローカル環境からアップロードできる仕組みを用意しています。

今回Firebase App Distributionを利用するにあたり、このCIとローカルの2つの環境でビルドを走らせるのが少々難しくなっていました。

fastlaneによるlaneの用意

CIとローカル環境用のlaneを用意しておきます。 大きな違いとしてはCI用のlaneでは firebase cli tokenfirebase cli path を設定しておきますが、ローカル環境用では用意していません。

default_platform(:android)

  desc "Debug build and upload Firebase Distribution(for CI)"
  lane :build_and_deploy_debug do
    gradle(
      task: "assemble",
      build_type: "Debug"
    )
    firebase_app_distribution(
      app: ENV["APP_ID_DEBUG"],
      testers_file: "./tester/testers.txt",
      groups_file: "./tester/groups.txt",
      release_notes_file: "./app/BetaDistributionReleaseNotes.txt",
      firebase_cli_token: ENV["FIREBASE_CLI_TOKEN"],
      firebase_cli_path: "./node_modules/.bin/firebase"
    )
  end

  desc "Debug build and upload Firebase Distribution(for local machine)"
  lane :build_and_deploy_debug_local do
    gradle(task: 'clean')
    gradle(
      task: "assemble",
      build_type: "Debug"
    )
    firebase_app_distribution(
      app: ENV["APP_ID_DEBUG"],
      testers_file: "./tester/testers.txt",
      groups_file: "./tester/groups.txt",
      release_notes_file: "./app/BetaDistributionReleaseNotes.txt"
    )
  end
end

CI用の FIREBASE_CLI_TOKEN は公式ドキュメントの通り firebase login:ci を利用して取得しています。

Firebase CLI reference  |  Firebase

Firebase App Distributionの利用はいくつか選択肢がありますが、個人的には fastlane の利用が一番やりやすいのではないかと思います。

firebase.google.com

CircleCIではAndroid用のDocker ImageにRubyの環境が用意されているため、すぐに利用することができます。 もしも fastlane の導入が気になるようでしたら、下記記事を是非ご覧ください。

tech.studyplus.co.jp

CI(CircleCI)の設定

CircleCIではグローバルな環境にFirebase CLIをインストールできないため、作業ディレクトリにnpmを利用してFirebase CLIを用意します。 標準のAndroid用Docker Imageではnodeが用意されていないため -node 付きのnodeが利用できるImageを利用するのが良いと思われます。

version: 2.1
executors:
  android_defaults:
    docker:
      - image: circleci/android:api-29-node

commands:
  setup_bundle:
    steps:
      - restore_cache:
          keys:
            - v1-bundle-cache-{{ checksum "Gemfile.lock" }}
            - v1-bundle-cache-
      - run:
          name: Set path and clean option
          command: |
            bundle config set path './vendor/bundle'
            bundle config set clean 'true'
      - run:
          name: bundle install
          command: bundle check || bundle install --jobs=4 --retry=3
      - save_cache:
          paths:
            - ./vendor/bundle
          key: v1-bundle-cache-{{ checksum "Gemfile.lock" }}

  setup_node:
    steps:
      - restore_cache:
          keys:
            - v1-npm-cache-{{ checksum "package-lock.json" }}
            - v1-npm-cache-
      - run:
          name: npm install
          command: npm install
      - save_cache:
          paths:
            - ./node_modules
          key: v1-npm-cache-{{ checksum "package-lock.json" }}

  setup_android:
    steps:
      - restore_cache:
          keys:
            - v1-gradle-cache-{{ checksum "build.gradle" }}-{{ checksum  "app/build.gradle" }}
            - v1-gradle-cache-
      - run:
          name: Download Dependencies
          command: ./gradlew androidDependencies
      - save_cache:
          paths:
            - /home/circleci/.gradle
          key: v1-gradle-cache-{{ checksum "build.gradle" }}-{{ checksum  "app/build.gradle" }}
  run_build_debug:
    steps:
      - run:
          name: Create new debug build(.apk) and upload Firebase Distribution
          command: bundle exec fastlane android build_and_deploy_debug

jobs:
  build_debug:
    executor:
      name: android_defaults
    steps:
      - checkout
      - setup_bundle
      - setup_node
      - setup_android
      - run_build_debug

workflows:
  deploy:
    jobs:
      - build_debug:
          filters:
            branches:
              only:
                - /^release.*/

Firebase App Distributionへapkを送信する処理がfastlaneに閉じているため、CircleCIではキャッシュの管理程度のステップになります。 なお実運用しているステップでは、途中でリリースノートにブランチ名やコミットログの書き込みなどを行っています。

開発者マシン(shell script)の設定

Mac OSとLinux向けに、Firebase CLIがインストールコマンドを叩いている環境に応じて環境構築をしてくれるスクリプトを公開しています。

Firebase CLI reference  |  Firebase

スタディプラスのモバイルクライアントチームは全員がMacBookProを利用しているため、こちらのスクリプトを採用しました。 このことにより firebase login コマンドの対話的な処理で firebase cli token に該当するリフレッシュトークンを取得することができるようになります。

#!/bin/zsh
echo "Build debug apk, and upload Firebase Distribution"

echo "Login firebase"

curl -sL https://firebase.tools | bash
firebase login

echo "Update local module"

# bundle
bundle config set path './vendor/bundle'
bundle config set clean 'true'
bundle install

echo "Start build"

# Build and upload
bundle exec fastlane android build_and_deploy_debug_local

echo "Done!"

事前にRubyやrbenvによる環境構築が必要となりますが(Bundlerのバージョンアップのため)、一度設定するだけだったので共有コストもほとんどかかりませんでした。 実行時間も短い(6分程度)のため、気軽に実行できる環境が維持されています。

終わりに

今回はFabric BetaからFirebase App Distributionへの移行事例を紹介しました。 実作業時間としては3〜4時間程度だっため、作業負荷もそこまで高くなく、改めてFirebaseの使いやすさを確認することとなりました。

現状ではアプリサイズが原因なのか、Debugのビルド時間にばらつきがあるため、引き続きCI設定を改善していきたいと思います!

開発プロセスを振り返ってみた話

こんにちは、サーバーサイドチームの山下です。 今回は昨年末にチームで実施した開発プロセスの振り返り会についてお話します。

背景

これまでの開発

私が所属しているサーバーサイドチームは、学習記録システムや特集記事配信システムなど、複数のシステムを扱っています。新規機能開発や改修があるとプロジェクトが発足し、各メンバーがアサインされ他チームと連携しながら開発を進める形式がメインです。

そのため、私が入社した時のチームは各々作業を進める雰囲気が強く、どうしても特定のシステムや機能開発が属人化してしまったり、他のメンバーが困っていることに気付けないということがありました。

そんな中、CSM(認定スクラムマスター)の資格を取得したエンジニアが中心となり2019年の夏頃からスクラムでの開発にチャレンジしていました。

なぜ振り返りをしたのか

2019年末のタイミングでこれまでスクラムイベントなど主導してくれていたスクラムマスターがチームを離れることになり、またその他のメンバーも数名入れ替ることになりました。

もともとスクラム自体も「試しにやってみよう!」という形で始まったので、やってみてどうだったか・今後どうしていくかをチームとして認識を合わせる良い機会だと思い、振り返りの場を設けることにしました。

準備

せっかくやるなら今後の開発に活かせるよう、有意義な会にしたく以下の準備や環境作りをしました。

  1. 事前にアジェンダ・タイムボックスを決めシミュレーションしておく
  2. 振り返り中はPCを開かせないようにする

1については当たり前のことかもしれませんが、どうすれば時間内で目的を達成できるか事前にスクラムマスターと一緒に考え、振り返り方法やタイムボックスを決めました。 普段のレトロスペクティブでやっていたKPTではおそらく時間オーバーしたり、まとまらなかったりしていたと思うので、シミュレーションはやっておいて正解でした。

また、2についてはPCを開くとどうしてもslackや他のタスクが気になってしまうので、テーブルは除けて椅子とホワイトボードのみの場にしました。これも議論に集中できて良かったと思います。

振り返りの流れ

全員でまとまった時間をとるのが難しかったため、二部制にしました。

  • [第一部] スクラムやってみてどうだった会
  • [第二部] 今後どうしましょうか会

[第一部] スクラムやってみてどうだった会

この会の目的は「今後の開発プロセスをどうするか決めるために、これまでのプロセスを振り返る」としました。 スクラムイベント毎にGood(良かったこと・続けたいこと)とMotto(改善したいこと・不満)を付箋に書き、各々その理由を共有していきました。 f:id:syamashi:20200109113438j:plain

[第二部] 今後どうしましょうか会

第二部では、第一部の内容を踏まえ今後のプロセスをどうしていくか話し合います。 まずは第一部でGoodになっていることは今後も続けていくことをチームとして合意し、Mottoになっていることをどう改善していくかを決めました。 全てのMottoについて検討すると時間がかかりすぎるのでドット投票で絞って改善案をだしていきました。 以下のような流れです。

  1. Good/Mottoを思い出す
  2. ドット投票で解決したいMottoを決める
  3. 解決案を出す
  4. 解決案の中から試して見るものを決める

f:id:syamashi:20200109113705j:plain

やってみての感想

通常のレトロスペクティブではどうしても普段の作業についての振り返りがメインとなり、スクラムイベント自体を振り返る機会がなかなかありませんでした。

各メンバーが普段の開発プロセスに対してどんな期待や不満があるのか、どうしたら改善できるかを話し合えたのはとても良かったと思います。 また、事前にシミュレーションなどしていたため当日はスムーズに進めることができ、改めて準備や場作りは大切だと感じました。

まとめ

今回は私達のチームで行った開発プロセスの振り返りについてお話しました。

皆さんのチームでもメンバーの入れ替わりなどチーム状況が変わったり、ちょっとマンネリ化してきたなと感じたら、普段の作業プロセス自体を振り返ってみるのものよいかもしれません。

WorkManager + Dagger2によるバックグラウンド処理

こんにちは、Androidチームの若宮(id:D_R_1009)です。 昨年末にAndroidチームが導入した、WorkManagerをDagger2と組み合わせる方法を紹介します。

WorkManagerとは

developer.android.com

WorkManagerは、確実に実行したい非同期処理に対して利用するAndroid Architecture Componentsとなります。 概要についてはDroidKaigi 2019の「実践 WorkManager」をご一読ください。

speakerdeck.com

また、CodeLabも用意されています。

codelabs.developers.google.com

WorkManager + Dagger2(DI)

StudyplusのAndroidアプリではDagger2を活用しているため、WorkManagerとDagger2を組み合わせる必要があります。 検索してみると、次の記事で組み合わせ方が紹介されていました。

proandroiddev.com

もともとAssistedInjectを利用していたこともあり、おおよそこの方法で導入することはできそうでした。 ただ、できれはWorkManagerも provide メソッドによる管理を行いたくなります。

と言うことで、対応していきます。

provide 対応

WorkManagerはインスタンス化する処理が(特に指定しなければ)デフォルトのものが利用されるため、この処理を切り替える必要があります。 対応したのがWorkManager 2.1のため、ドキュメントに従って AndroidManifest.xml に記述を加えます。

developer.android.com

この対応により、 WorkManager.getInscance(context) する前に Configuration.Provider をセットすることができます。

@Provides
@Singleton
fun provideWorkManager(
    context: Context,
    factory: WorkManagerFactory
): WorkManager {
    WorkManager.initialize(context, Configuration.Builder().setWorkerFactory(factory).build())

    return WorkManager.getInstance(context)
}

あとは記事の通り、諸々のモジュールをセットすれば完了です。

@Module(includes = [PresenterModule::class, WorkerBindingModule::class])
object WorkManagerModule {

    @Provides
    @Singleton
    fun provideWorkManager(
        context: Context,
        factory: WorkManagerFactory
    ): WorkManager
}

@Module(includes = [AssistedInject_PresenterModule::class])
@AssistedModule
internal interface PresenterModule

@MapKey
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class WorkerKey(val value: KClass<out ListenableWorker>)

@Module
interface WorkerBindingModule {
    @Binds
    @IntoMap
    @WorkerKey(HogeWorker::class)
    fun bindHogeWorker(factory: HogeWorker.Factory): ChildWorkerFactory
}

利用状況、所感

StudyplusのAndroidアプリでは、WorkManagerをRepository層とViewModel層の間にある概念として利用しています。 このため、DIによりViewModel層からWorkManagerを呼び出し、WorkManagerのWokerにRepositoryをInjectして非同期処理を実行する構成となります。

アプリケーションの各種ライフサイクルに影響を受けずに非同期処理を実行することができ、開発のしやすさが高まったと感じています。 アーキテクチャとしてMVVMを採用しているチームにおいては、有用な選択肢となるのではないでしょうか。

終わりに

簡単ではありますがWorkManagerとDagger2を組み合わせる方法の紹介と、その効果をまとめてみました。 WorkManagerはKotlin Coroutinesを利用することができるため、Repositoryの各メソッドをsuspend関数にしておくだけで、簡単に呼び出すことができます。

2020年も引き続き、よろしくお願いいたします。

EOF 2019に参加しました(イベントレポート)

こんにちは、スタディプラス iOSチームの大石(id:k_oishi)です。 2019/10/31に開催されたEOF 2019に参加しました。

f:id:k_oishi:20191226142847j:plain:w300

EOFとはEngineering Organization Festivalの略で最近役職として注目されているEngineering Manager(略してEM)のためのカンファレンスです。 以前、EM的な役職だったこともあり、最近のEM界隈にも興味がありましたので参加してきました。

オープニングトークではEngineering Managerに興味を持つ参加者同士で自己紹介や現在持っている課題などを共有しましょうという時間がありました。 偶然、隣にいらっしゃった方がプログラミング学習の際に弊社Studyplusを使用しているとのことでうれしい出会いとなりました。

それでは、個人的に気になったセッションの紹介と感想です。

「質とスピード」

和田卓人さん

TDDでおなじみ和田卓人さんによるセッションです。
※詳しい内容はセッションのスライドをご覧ください

このセッションはタイトルのとおり「質とスピード」がテーマになっています。 今回の講演が初演ということで貴重な機会だったかと思います。

初めに、与えられた時間に対しやるべきことが多い場合に品質を犠牲してしまうケースが多いが、品質を犠牲にすればスピードが得られるのか?という問いから始まりました。 結果としては、短期的にスピードは得られますが、長期的には逆効果になるというということでした。

スピードを優先して品質を犠牲にした結果、内部品質といわれる部分が影響を受けます。 内部品質はテスト容易性、理解容易性、変更容易性で構成されています。(これらをまとめて保守性ともいう) これらを犠牲にするとプロジェクトにどのような影響を及ぼすかは、ある程度の経験者であれば容易に想像できると思います。

次にスピードを落とせば保守性は上がるのか?という問いがありました。 作業時間が少なくても品質の高いコードを書く人がいれば、作業時間が十分にあっても品質の低いコードを書く人もいるからです。

つまり、質とスピードはトレードオフではなく、品質をアップするためのコストをかける必要があるということです。 ここでは品質アップの2つの考え方としてコストアップ説とコストダウン説が図解で紹介され興味深いものでした。

結論として長期的に見れば質がスピードを生むのであって、そのスピードがさらなる質を生み、そのループのなかで外部品質を生み、サービスの競争力を生み、売り上げを生むという関係があったのです。 プロジェクトによっては品質を犠牲にしてリリースを優先する場合もあると思います。また、すでにそのような状態のプロジェクトに途中から参加する場合もあると思います。そのような状況をどう改善していくかが1つのポイントでは無いかと思いました。

感想

以前の会社では、通常の開発スプリントを4週実施したら、次の1週はテクニカルスプリントでエンジニア主導での既存コードのリファクタリングや新技術の調査などを行うことができました。 また、現在のStudyplusのiOSチームでは毎週金曜日をリファクタリングと緊急性の無いクラッシュ対応や不具合対応を行う日としてプロダクトの改善を行い、毎週リリースするサイクルを回しています。 このようなことを定期的に行ってはいますが、今後も品質について考えていきたいと思いました。

おまけ

講演内容に引用された書籍は以下のとおりです。

  • アジャイルサムライ
  • ワインバーグのシステム思考法
  • レガシーコードからの脱却
  • エンジニアリング組織論への招待
  • エクストリームプログラミング
  • LeanとDevOpsの科学
  • Experiences of Test Automation
  • A Philosophy of Software Design

すでにご存知のタイトルも多いと思いますが、チェックしてみてはいかがでしょうか。

「レガシーコードからの脱却」

吉羽 龍太郎さん

スライド

書籍「レガシーコードからの脱却」を執筆された吉羽さんによるセッションです。
※詳しい内容はセッションのスライドをご覧ください

この本はタイトルからするとレガシーコードを改善するような内容に受け取れますが、実際はレガシーコードを生み出さないようにする方法論がまとめられているとのことでした。ちなみにタイトルにレガシーとつくと本が売れるそうです。 レガシーコードの定義は様々ですが、このセッションでは修正、拡張、作業が難しいコードと定義され、保守に多額のお金がかかるコードという定義です。

ユーザーに使われるソフトウェアは変更が必要になります。 機能の追加や既存の機能の更新などが想像できると思います。 しかし、これらの更新を事前に予測することは不可能です。 そのため、変更しやすいしておくことが大事であり、その変更に対応できないのはレガシーコードであるということでした。

では、最初からレガシーコードを作らないようにするにはどうすれば良いのでしょうか?

まずは開発プロセスです。 ウォーターフォールはリスクが後半になればなるほど顕在化して取り返しがつかなくなるので、登場してきたのがアジャイルという手法、さらにXPやScrumといった手法が登場しました。 当然、アジャイルでも失敗するときは失敗します。ソフトウェアが生み出す成果に必要な要素は問題設定力、開発力、チーム力です。

次にレガシーコードを作らないための9つのプラクティスの一部が紹介されましたので簡単にまとめます。

  • 1 やり方より先に目的、理由、誰のためかを伝える
    プロダクトオーナーの領域である何をしたいか、なぜしたいか(What)と開発者の領域(How)であるやり方を分離して、双方が創造的に協調してコンテキストを共有、理解することが大事。

  • 2 小さなバッチで作る
    タイムボックスとスコープボックスという概念やケイデンス、リソース効率、プロセス効率などが登場します。 まとめると品質を一定に保ち、間に合わなければスコープを減らす。そして、ソフトウェアの評価として顧客にとっての価値が提供できているのかを小さいバッチでリリースしてフィードバックの回数を増やしてより価値を高めるということでした。

  • 5 Cleanコードを作る
    いわゆる一般的なCleanコードの定義ではありますが、開発の速後向上のために日々の積み重ねが必要で、それによりすばやく働く(= きれいに働く)が実現できるとのこと

  • 8 設計は最後に行う
    ソフトウェア開発は開発中に仕様が追加されたり、あとから分かることがあると思います。それらを随時反映するために、まずコードが動作し、テストがある状態から設計を良くするという考え方です。

感想

全体的に理解は出来るのですが、現実ではそこまでうまくできていない部分が多々あると感じました。1つ1つの考え方や振る舞い方を取り入れるだけでも、少しずつ改善できるのではないでしょうか。 より理解を深めるために「レガシーコードからの脱却」をしっかり読もうと思いました。

最後に

以上、印象に残った発表を紹介させていただきましたが、他にも素晴らしいセッションが多数ありました。 最後に和田卓人さんのツイートを紹介します。

会場の廊下にはスポンサーのブースが設置されていましたが、ある会社さんのブースで自作キーボードのスイッチを交換されている方がいました。弊社の自作キーボード部の部員としてついついキーボード話をしてしまいました。 これもそのような機会だったと思っております。

f:id:k_oishi:20191226142857j:plain:w300

この度このようなイベントに参加することで普段得られない知見を得られ、新しい出会いがありました。 運営スタッフの方々、登壇者の方々に感謝いたします。

スタディプラス SREチームの2019年の取り組みまとめ

SREチームの栗山(id:shepherdMaster)と菅原(id:ksugahara08)です。

年末ということもあり、弊社SREチームが2019年に行ってきた取り組みの中で大きめのトピックを紹介したいと思います。
本来ならもっともっと書きたいことがあるのですが、今回はスタディプラスのSREチームが何をやってきたのか概要がわかるように書いていきたいと思いますのでぜひ最後まで読んで頂けるとありがたいです。

SREチーム発足

2019年はSREチームの発足をしたというのが大きなトピックでした。発足にはインフラを担当していた1名とサーバーサイドから1名が参画し、2名で発足しました。
元々SRE経験者を社外から採用してから発足を考えていたのですが、SREは転職市場でも希少で採用が難航していため、それなら自分たちでSREを始めてしまおう!とSREチームを作ることを決めました。

SREという職種の共有会

www.oreilly.co.jp

発足当初、SREという職種について経験者もいなければ、SREという職責について理解が足りていなかったためとにかく他社事例を見ることからはじめました。
その際に活用したのがYouTubeに上がっているSRE関連の動画でした。動画視聴会は開催しやすく学びも多いということで弊社では割と頻繁に行っております。

次に、オライリー・ジャパンから出版されているSRE サイトリライアビリティエンジニアリング――Googleの信頼性を支えるエンジニアリングチームをSREメンバーそれぞれが読み、30分程度のプレゼン形式で社内のエンジニアにSREという職種はどんなものかを共有する『SRE共有会』を開催しました。

輪読会という形式も選択肢にはあったのですが、

  • SREメンバーが2人で経験者もいないということ
  • スピード重視
  • SREではない周りのエンジニアにも理解してもらいたいという想いがあった

という理由でサクッと概要を掴める共有会形式を取りました。
結果としてSREという職種について理解と協力をしてもらえるようになったと思います。今後も何らかの形で社内への理解を深める活動をしていきたいと考えています。

ポストモーテム導入

前述のSRE本に載っているポストモーテムを弊社でも導入しました。
弊社では以前から障害報告書を書く習慣はあったのですが、障害対応した人がその障害を見るような形でチーム全員で振り返るという事はしていませんでした。
障害報告書をポストモーテムに変えるにあたって内容の変更や振り返りを全員で議論する場を設けるようにしました。
ポストモーテムを導入した結果、弊社では以下のようなメリットがあったと思われます。

  • 時系列で何が起きていたかわかりやすくなった
  • チーム全員で障害を振り返るようになった
  • 障害後、取るべきアクションをきちんとみんなで話し合うようになった
  • 「障害から学ぶ」という意識が広がった

ポストモーテムを導入後の2019年8月23日にはAWSの大きな障害が起きましたが、ポストモーテムで記録に残し、コミュニケーションを取りながら障害対応ができました。導入してすぐにメリットを痛感できたのも機運が高まった要因かもしれません。
今後もポストモーテムを続けていくということだけではなく、振り返ることで障害に強いサービスに変更していきたいと考えています。

脱AWS Elastic BeanstalkとKubernetes移行

弊社ではメインのマイクロサービスはAWS Elastic Beanstalk上で、その他のマイクロサービスはEC2上で動いています。
Elastic Beanstalkはメリットもたくさんあります。しかし、デプロイが遅かったりElastic Beanstalk自体の仕組みが独特で学習コストがかかったりで、他のなにかに移行したいという話が上がっていました。
またEC2のほうは歴史的経緯からEC2インスタンスを増やすためには手動作業(EC2インスタンスを立ち上げてからAnsibleを実行するという作業)が必要という課題がありました。
その他の要望として

  • カナリーリリースを楽にしたい
  • 簡単に言語やライブラリのバージョンアップをするためにコンテナを導入したい
  • インフラ作業コストを下げたい
  • 耐障害性を高めたい

というものがありました。
それらの課題、要望を解消するためにKubernetesに移行することを決めました。現在はまず一部のマイクロサービスのKubernetesへの移行を取り組んでいます。

Terraform移行

www.terraform.io

弊社では前任者がAWSサービスをAnsibleで管理している状況でした。
AWSリソースからEC2の設定まで全てAnsibleで管理するのは、いくつもIaCツールを使うより学習コストを抑えられるという点でメリットがあります。しかし一方でAWSの新規サービスや最新バージョンに追従できないことが多々ありました。
その際は自分たちでPythonのAnsibleモジュールを自作していたのですが、サービス数が増えるに連れ運用コストが比例して増えるようになってきました。
そこで弊社ではAWSリソースの管理をTerraformへ移行することを決めました。
Terraformを選択した理由としては以下があげられます。

  • AWSの新規サービスや最新バージョンに比較的早く追従される
  • 宣言的に記述することができるため直感的にわかりやすい
  • 利用できるProvidersが豊富なこと

もちろんデメリットとしてAWS CloudFormationに比べて新規サービスや最新バージョンに対応するラグありますが、今のところ対応は早く不便に感じていません。
またTerraform学習コストもありますが、新規ツールに対してキャッチアップする姿勢が強いメンバーが揃っていたため問題になっていません。
社内でTerraform共有会を定期開催するなどお互いの知識を教え合う場ができている状態です。
現在はAmazon EKS周りのリソースをTerraformでコード化していますが、これからは現状のリソースもコード化したり、CI/CDの自動化をしていく予定です。

ログ収集基盤改善

ログ収集基盤をAmazon CloudWatch LogsからAmazon S3 + Amazon Athenaに変更しました。
CloudWatch Logsには以下の課題がありました。

  • 料金が高い
  • ログ保存期間が短い
  • UIが使いづらい

それを解決するためにログをS3に保存しAthenaで検索できるように基盤作りをしました。
最初は、Amazon Kinesis Data Firehoseを使ってFirehose自身の変換機能でParquet形式でS3に保存していたのですがFirehoseは料金が高くて諦めました。
JSON形式で保存してAthenaで検索してもそれほど検索速度に影響がなかったので、現在はFluentdから直接ログをS3にJSON形式で保存しています。

Rubyバージョンアップ

弊社では早くからマイクロサービスを採用しており、Rubyを使っているサービスは全部で9サービスあります。その中で2020年3月31日にEOLを迎えるRuby2.4を使っている5つのサービスを2.6にバージョンアップしました。
またついでに使っているGem(Rails含む)のバージョンアップも行いました。

作業自体はどのサービスも概ねスムーズにいったのですが、一部のサービスでPumaのGemのバージョンアップを行ったらゾンビプロセスが発生するバグを踏み抜いてしまい焦りました。

speakerdeck.com

最初は原因が分からなかったのでひとまずGemのバージョンアップをrevertし調査しました。
Pumaの直近のリリースノートに「ゾンビプロセスが発生するバグを修正した」というのがあったのでPumaのGemを最新にしてリリースしたところゾンビプロセスが発生することはなくなりました。

バージョンアップ作業はなかなか大変ですが、非常に重要なので定期的かつ計画的に行っていきます 。
今後コンテナ化を進めていったら新しいバージョンのRubyを入れた新しいサーバーを用意する必要もなくなるのでバージョンアップ作業が楽になるのではないかと期待しています。

jemallocの導入

詳しくは Rubyアプリケーションのメモリ使用量上昇問題をjemallocを使うことで解決しました をご参照下さい。

tech.studyplus.co.jp

勉強会開催

SREチームでは知識、知見をインプット/アウトプットすることを重視しており、定期的に勉強会を開催しています。2019年に行った勉強会を紹介します。

Kubernetesハンズオン

GCPを使ってKubernetes上にアプリケーションを動かしながら、Kubernetesの各機能の説明をするハンズオンを開催しています。Kubernetesを浸透させたいのでエンジニアが新しく入ってくるタイミングでハンズオンを実施しています。

Kubernetetsの各機能の勉強会

Kubernetes完全ガイド本をもとにKubernetesの各機能について紹介する勉強会を7回に分けて開催しました。

コンテナ監視ツール勉強会

Kubernetesを本番サービスで運用していくにはObservabilityを上げていく必要があり、CNCFのTrail Mapにも4番目に載っています。弊社でもKubernetesでの監視をどのようにしていくべきか検討する必要があり、主要な監視ツール9つを比較し、勉強会を行いました。

検討の結果、今後はDatadog等のService Discoveryに対応したツールを利用してKubernetesの監視を設定していきたいと考えています。

サービスメッシュ勉強会

サービスメッシュのIstioの勉強会も開催しました。 その時の資料はこちらです。

qiita.com

CI/CD勉強会

Kubernetesへのデプロイツールとしてどのようなものが最適なのかを知るためにいくつかCI/CDを各自調べ、勉強会を開きました。
以下が調べたツール、サービスになります。

AWS Black Belt動画視聴会

AWSには多くのサービス多くの機能があるため、「自分たちが使っているサービスをより知るため」「使ってないサービスの概要を知るため」という理由でYouTubeに上がっているAWS Black Beltの動画をみんなで視聴する会を定期的に開催しています。(スタディストさんでやっている活動を参考にさせてもらいました)
動画を見ながら「この機能は使えそう」「機会があれば使ってみたい」とみんなでわいわい話しながら見ています。
今年観た動画は Aurora、Athena、Amazon Personalize、Key Management Service、CloudFront、Lake Formation、AI Services、Config です。

監視周りの設定見直し

弊社では主にMackerelを使ってサーバーメトリクスを取得し、Slackにアラートを出したり、Twilioで電話通知を行ったりしていました。
しかしDBが高負荷による障害時に監視やログ取得が足りていないと痛感することがありました。そのため以下のような設定を行い、監視周りの設定見直しを行いました。

  • Amazon Auroraのパフォーマンスインサイト導入
  • Amazon RDS、Auroraのスロークエリを出すようにする
  • 各サービスの死活監視強化

改善はしたものの、まだ現状でも課題があります。CPUやメモリー等のResource Metricsは監視できているものの、スループット等のパフォーマンス周りのWork Metrics、そして設定変更の監視周りであるEventsが弱いと感じています。
今後は Datadog社のMonitoring Modern Infrastructureを参考に監視の改善を行っていきたいと考えています。

深夜メンテナンス手順書の整備

今年何回か深夜メンテナンスを行ったのですが、私たちSREチームが初めて深夜メンテナンスの準備をするときにメンテナンス手順書が存在しないことに気が付きました(あったのは前任者が残した簡単なメモ書き)。
これはメンテナンス手順書を作る機会だと思い、メンテナンスモードにするための必要な手順を1つ1つ社内Wikiに書き起こしました。それ以降の深夜メンテナンスでは毎回メンテナンス手順書を作っています。
事故なく深夜メンテナンスを行うには手順書は非常に重要で、その手順書のレビューやリハーサルもしっかり行っています。

最後に

インフラチームからSREチームに変わりメンバーも増え、様々な改善ができた一年でした。

2020年も弊社サービスのユーザーの皆様により良い価値を提供するためサービス基盤、開発環境、パフォーマンス等の改善など行っていきたいと考えてます。SREチームとして弊社サービスをより愛して頂けるように全力を尽くしていきたいと思います。

Studyplus for SchoolのCSS事情

こんにちは。ForSchool事業部の石上です。最近、弊チームのスクラムマスター id:atomiyama さんから、「伝えにくい事とか相手の注意引きたい時に幼児言葉を使うと有効です。」というテクニックを教えてもらいました。今後はなるべく実践していきたいでちゅ 👶

今日は、Studyplus for SchoolのCSS事情について書かせてもらいます。

以前に書いたやりたいことベースでWebpackにCSS周りの設定をする - Studyplus Engineering Blogとだいぶ重複する部分もありますが、今回はWebpackのloaderの話ではなく、なにが欲しくてなにを使っているのかという形式で整理していきたいと思います。

ちょうど最近 id:tagucch さんがうちのチームに異動してきてくれたので、 id:tagucch さんへの説明も兼ねています。

CSSのためにいろいろ道具が必要になる背景

本題へ入る前に、そもそもなぜCSSを書くために余計な道具が必要となるのでしょう。CSSはブラウザが解釈できる言語なので、そのまま書いて配れば動くはずです。

しかし欲を出して、ちょっとでもCSSを書きやすくするためにはいろんなツールを入れる必要があります。

それらを今回改めて紹介していければと思います。

変数がほしい

CSSでは、セレクタ(画面内の要素)を指定して見た目を当てることができるわけですが、何度も同じスタイルを当てるのは大変です。なので、HTMLのclass属性に名前を指定してうまいこと管理することで繰り返しを避けられるわけですが、その指定だけでは管理しづらいこともあります。

たとえば、色コードです。そのサービスのブランドカラーのような色コードは、いろんなところで使うことがあります。テキストの色だったり、ボタンの背景色だったりします。

.primary-color-text {
  color: #008080;
}
.primary-color-button {
  background: #008080;
  color: #fff;
}

color, backgroundとそれぞれプロパティの種類は違いますが、当てたい色コードは同じです。CSSで変数が使えると以下のように書けます。

:root {
  --primary-color: #008080;
}
.primary-color-text {
  color: var(--primary-color);
}
.primary-color-button {
  background: var(--primary-color);
  color: #fff;
}

これはいいですね。名前がつくことで、色コードの意味を開発者が覚える必要もなくなりますし、変更を入れたいときも一箇所で済みます。

ただ、この記法はまだサポートされていないブラウザもあります。

このコードが使えないブラウザのために代替の手段が必要で、それが今のところはSassというツールです。Sassの変数を使えば、上記とほぼ同じように書けます。

$primary-color: #008080;
.primary-color-text {
  color: $primary-color;
}
.primary-color-button {
  background: $primary-color;
  color: #fff;
}

関数がほしい

変数と同じく、関数もあると嬉しいです。似たようなプロパティをまとめられて、使うところで少しだけ変えられると、とても便利です。ただ、これはCSSでは書けません。

Sassを使うと、以下のようにmixinというものを定義できます。たとえば、スクリーン幅をみて当てるスタイルを変えたいとき、以下のようなものを定義しておくと、各所で長ったらしいメディアクエリを書かずに済んで便利です。

/* mixin */
@mixin max-screen($break-point) {
  @media screen and (max-width: $break-point) {
    @content;
  }
}
/* 使うとき */
@include max-screen(768px) {
  width: 50%;
  /*...*/
}

スコープがほしい

Reactを使ってアプリケーションを作っているとUIの部品をコンポーネントと呼び、汎用的にするべしという風潮があります。実際そうしたほうが、繰り返し同じようなものを実装しなくて済むので幸せになれます。

そうすると、できれば見た目の定義もそのスコープに閉じ込めたくなります。そこで出てくるのがCSS Modulesです。

import * as styles from './styles.scss';

const Button = (props: Props) => {
  return (
    <button className={styles.root}>{props.children}</button>
  )
}
.root {
  background: $primary-color;
  /* ... */
}

Webpackのloader、css-loaderでCSS Modulesの設定をするとこのように書けるようになります。.rootは、ビルド時にアプリケーション内で衝突しない文字列に置き換えられるので、クラス名の衝突を気にせず短いクラス名でCSSを書けます。Button/styles.scssの.rootは、Buttonコンポーネントのスコープに閉じられています。

型がほしい

Studyplus for Schoolのフロントエンド側のコードはTypeScriptで書いているため、エディタ補完のありがたみを感じながら開発をしています。CSS Modulesを利用して、TypeScript側からクラス名を参照できるようになっているので、型もついているとなお嬉しいですね。className={styles.とタイピングしたら、指定できるclass名が補完されてほしい。

これには、Quramy/typed-css-modulesを利用して、CSSの型を生成して使っています。

まとめ

こんな感じで現状だといろいろ使っているのですが、個人的にはなるべく依存するツールは減らしていきたい気持ちがあります。いつかCSSだけで快適に書けるようになったらいいなと思いつつ、そうなるまでは必要なものを選んで使って、いらなくなったら捨ててというのをちゃんと繰り返していければいいかなと考えています。

これもう不要じゃん! とかこっちのほうがいいぞ! みたいものを見つけたときはどんどん変えていきたいのでご指摘もらえると嬉しいです! 👶

CloudNative Days Kansai 2019参加レポート

どーも、SREやってます。菅原です。

先日は11月27,28日にグランフロント大阪のコングレコンベンションセンターで行われたCloudNative Days Kansai 2019に参加して来ました。

f:id:ksugahara08:20191201140016j:plain

せっかくなのでイベントの参加レポートを載せたいと思います。

CloudNative Daysとは

公式サイトから引用させて頂くと、

クラウドネイティブの現状をひとまとめにした開発者のためのイベントです。2018年はJapanContainerDaysとして2度開催し、今年からCloudNativeDaysに名称を変え4月に福岡、7月に東京、11月に大阪で開催します。

ということらしい。

クラウドネイティブ関連の技術を中心としたセッションが行われ、国内インフラ系のイベントでは一番大きいものではないかと思ってます。(クラウドベンダーが主催するものを除き)

CloudNative Days Kansai注目セッション

ここからは私が注目したセッションを紹介していこうと思います。

スライドが上がると思うので私の感想を中心に載せていきます。

コンテナの作り方~Dockerは裏方で何をしているのか~(前佛雅人氏)

コンテナやDockerって何?という疑問に図解で解説するという内容でした。

初心者向けと思いきや、図解でナレッジがまとめられていて、Dockerを使ったことがある人でも頭の中が整理されて本当に良いセッションでした。

また、前佛氏のトークはものすごくわかりやすく「コンテナはデフォルトでisolate」というキャッチフレーズが頭に残ったのは私だけではないでしょう。

メルペイのマイクロサービスとCloud Native(Junichiro Takagi氏)

speakerdeck.com

メルペイのマイクロサービスがどのようにCloud Nativeを取り入れているのか。また、運用してみてどのような課題があるのかを聞くことができた素晴らしいセッションでした。

最後にTakagi氏は「Cloud Nativeで重要なのは技術セットではない、組織体制や開発・運用のスタイルも含めたCultureが重要だ」とまとめていました。

弊社も半年間SREの文化や考え方を社内エンジニアに理解してもらうため共有会や勉強会をしてきたので、組織が大きくなってもこれを継続していくことが必要だなと思いました。

分散システム内のプロセス間の関係性に着目したObservabilityツールの設計と実装(Yuuki Tsubouchi氏)

speakerdeck.com

ゆうきさんのセッションは、Transtracerについての内容でした。

分散アプリケーションにおけるObservabilityの問題を、分散トレーシングで主流のリクエストベースアプローチとは違ったアプローチで問題解決しようというものでした。

正直この分野について詳しくなかったので目から鱗でした。 今後のTranstracerの発展が気になる素晴らしいセッションでした。

Kubernetesの運用を支えるGitOps(藤原峻輝氏)

www.slideshare.net

freeeではWeaveworksのGitOpsを参考にOpsのGit化をしたというセッション内容でした。

バージョン管理やロールバックが容易になるという恩恵を狙ってGitOps導入したところ、集中した権限管理、kubectlでdeployする必要がない安心感、実際に今動いているmanifestがコード上でバージョン管理されるといったメリットが得られたとのことでした。

freeeのGitOpsの導入手法はCloud Nativeを実践していて理想的ないい例だと思いました。弊社でもWeaveworksのGitOpsの考え方の布教活動から始めていきたいと思いました。

Production Ready Kubernetesに必要な15のこと(磯賢大氏)

speakerdeck.com

Kubernetesプラットフォームを構築するにあたって考慮点を15つにまとめており、コロプラでの設定例も紹介している神スライドでした。

Kubernetesを本番環境で運用していくには何を考えておく必要があるのかベストプラクティスがまとまっていたと言っても過言ではないでしょう。 Kubernetes初心者にも、実際に運用している人にも参考になったのではないでしょうか?

弊社もこの15項目についてSREチームで議論して決めていきたいと思います。

最後に

東京から大阪のカンファレンスに参加したのですが、大阪の街並みや関西のエンジニアの熱量を感じることができました。仕事に活かせる話を聞けただけでなく、自分のモチベーション向上につながったと思います。

少ししか大阪に滞在できませんでしたが、最高でした。また機会があれば遠征したいです。