Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)

Akatsuki Hackers Labは株式会社アカツキが運営しています。

開発環境インフラを ECS 移行している話

 この記事は Akatsuki Advent Calendar 2018 の24日目の記事です。 前回は id:NeoCat さんのElixirのサーバアプリケーションをDatadog APMでトレースするでした。

はじめに

はじめまして、運用中ゲームタイトルでサーバーエンジニアをしている守屋と申します。 私の所属するチームでは AWS EC2 上でサーバーサイドの運用を行ってきました。サービスリリース当初からの様々な改善の積み重ねの結果、近頃はインフラ障害も少なくなり、安定した運用が実現しています。しかし安定した運用が実現すればサーバーエンジニアの仕事は終わりなのでしょうか?安定しているとはいえ既存インフラには様々な技術的な負債が蓄積されています。我々サーバーチームは既存インフラを設計から見直し、新環境へ全てを移行する一大決心を下したのです。

続きを読む

ElixirのサーバアプリケーションをDatadog APMでトレースする

この記事は Akatsuki Advent Calendar 2018 の23日目の記事です。 前回は id:yunon_phys さんの、エンジニア組織の責任範囲の透明性をRACI図で高めてみた でした。

はじめに

アカツキではElixirを使ってゲームのAPIサーバを開発・運用しています。 ゲームのAPIサーバは、大量のリクエストを低いレイテンシで捌くことが要求されるため、Erlang VMの高いスケーラビリティが利用できるのは効果的です。加えてRubyなどを書き慣れている人にとっつきやすいElixirの文法も魅力です。

とはいえ、その性能を引き出すためには、やはりアプリケーションのパフォーマンスチューニングは不可欠です。 その際、"Don't guess, measure" という言葉の通り、どこを改善すれば良いかを知るための良い計測ツールが必要になります。

これまでRuby on Railsのアプリケーションでは、計測・監視のためにNew RelicをAPM (Application Performance Monitoring) ツールとして使っていたのですが、現在New Relicでは公式にはElixirはサポートされていません。*1

そこで今回は、インフラの監視に利用していたDatadogにAPMも統合できることを期待して、Datadog APMを利用してみることにしました。

Datadog APMでは単なる集計値のみならず、タイミングの記録にも対応しており、下記のように個々のAPI内の処理内容をフレームグラフとして見ることも可能なため、性能改善には有効そうです。

f:id:NeoCat:20181215201740p:plain

*1:コミュニティによるライブラリは開発されていますが、Phoenixを前提としているものが多いようです。後述の通り、今回の対象アプリケーションはPhoenixを使用していないため、採用は見送りました。

続きを読む

エンジニア組織の責任範囲の透明性をRACI図で高めてみた

こんにちは、ゆのん(id:yunon_phys)です。この記事は Akatsuki Advent Calendar 2018 の22日目の記事です。 前日は@kackytwさんのDQNの学習速度を改善するでした。

Engineering Managerのjob descriptionを共有する流れ

近年、Engineering Manager(EM)業界が賑わってきています。特に2018年は非常に活発化した年でした。書籍としては、エンジニアリング組織論への招待エンジニアのためのマネジメントキャリアパスという名著が生まれました。また、Engineering Manager Meetupが3回開催され、Slack workspaceでは12/22時点で268人となっています。EMのためのPodcast EM.FMも誕生し、総再生回数が10,000回を突破するなど、多くの方から注目を集めています。*1

*1:このPodcastはエンジニアリング組織論への招待の著者である広木さんと、私がやっています

続きを読む

UnityでAPK拡張ファイルを利用するときのあれこれ

こんにちは。このブログでははじめまして。クライアントエンジニアの下村です。

この記事は Akatsuki Advent Calendar 2018 の19日目の記事です。 
前回は kidach1 さんの ニューラルネットワークでStyle Transferを行う論文の紹介 でした。

背景

この記事を開いた方にはご存知の事かと思いますが、Google Play Storeではアプリケーションパッケージのサイズに制限があり、100MBを超えるapkファイルをアップロードすることができません。 100MBを超える大きなアプリを提供したい場合は、APK拡張ファイルと呼ばれる別のパッケージにデータを分割する必要があります。

インターネット接続を前提とするソーシャルゲームでこの制限に引っかかることはそう多くありませんが、最近たまたまこれを利用する機会があったので備忘録を兼ねて知見を共有したいと思います。

UnityでAPK拡張ファイルを出力する

UnityでAPK拡張ファイルを出力する方法は非常に簡単です。 PlayerSettingsの Split Application Binary オプションにチェックを入れてビルドすると、apkファイルと一緒にobbファイルが生成されます。 Unityは分割されたファイルから自動的にアセットを読み分けるため、Unityの実装上で分割を意識する必要はほとんどありません。

分割したアプリを手動でインストールする

Build And Run を実行するとUnityは自動的にobbファイルをインストールしてくれます。 一方手動でアプリをインストールする場合は、 apkファイルに加えてobbファイルを特定の場所に配置する必要があります。

obbファイルの名前や置き場所は決まっていて、以下のようなパスに配置する必要があります。
外部ストレージのAndroidディレクトリは端末によってパスが異なる事がある点に注意してください。

/storage/emulated/0/Android/obb/<パッケージ名>/main.<ビルド番号>.<パッケージ名>.obb

ファイルの転送には adb push コマンドを使うのが便利です。

$ adb install App.apk
$ adb push App.main.obb /storage/emulated/0/Android/obb/jp.aktsk.testapp/main.1.jp.aktsk.testapp.obb

これで分割されたアプリをインストールすることができました。

APK拡張ファイルのバリデーション

obbファイルは通常apkファイルと一緒にインストールされますが、実態は外部ストレージに配置されるいちファイルに過ぎません。 アプリ起動時にファイルが存在しないという状況が起こりえることから、Googleはアプリ起動時にAPK拡張ファイルのチェックや再ダウンロードを行うよう喚起しています。

Googleからはobbファイルをバックグラウンドでダウンロードするライブラリが公開されていますが、今回は急ぎで対応が必要だったことからこの高機能なダウンローダーを実装して詳しく検証している時間がありませんでした。 幸い分割されたデータはそう大きくなく、最近ではダウンロード漏れもかなり稀な発生率であるらしいことから、起動時に簡単なファイルチェックのみを行うことにしました。

APK拡張ファイルをチェックするアクティビティを挟む

今回のアプリではUnityの起動直後にいくつものアセットを読み込んでいたため、UnityPlayerActivityが起動する前にobbファイルの存在チェックをする必要がありました。 そこでonStartでobbファイルの存在をチェックをするActivityを新しく作成します。

public class ExpansionFileCheckActivity extends Activity {
    @Override
    protected void onStart() {
        super.onStart();

        if (getMainObbFile().canRead()) {
            // UnityPlayerActivityを起動する
            return;
        }

        // エラーを表示してアプリを終了する
    }

    private File getMainObbFile() {
        String fileName = "main." + getVersionCode() + "." + getPackageName() + ".obb";
        return new File(getObbDir(), fileName);
    }

    private long getVersionCode() {
        try {
            PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
            return PackageInfoCompat.getLongVersionCode(packageInfo);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return 0;
        }
    }

    // (省略)
}

getObbDir() でobbファイルの配置されるディレクトリを取得できますが、ファイル名は自分で生成する必要があります。 Activityを追加したら、AndroidManifestを修正して最初に起動するActivityを置き換えます。

<activity android:name="jp.aktsk.testapp.ExpansionFileCheckActivity" android:label="@string/app_name" android:launchMode="singleTask" android:theme="@style/Theme.AppCompat">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>
<activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name" android:launchMode="singleTask">
  <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>

これで起動時に問題があれば適切なエラーを表示することができるようになりました。

必要なときだけアクセス権限を要求する

さらに一部の端末ではインストールされたobbファイルがユーザ所有にならず、アプリからアクセスできなくなることがあるようです。 Unityはこの問題を回避するため、 Split Application Binary オプションを有効にしてビルドすると READ_EXTERNAL_STORAGE 権限要求をAndroidManifestに自動で付与します。

しかしながら、ごく一部の端末のためにすべてのユーザーにこの強力な権限を要求するのはナンセンスです。ここはobbファイルが読み込めなかった場合にのみ権限を要求するようActivityを拡張しましょう。

public class ExpansionFileCheckActivity extends Activity {
    private static final int RC_PERMISSION_REQUEST = 1;

    @Override
    protected void onStart() {
        super.onStart();

        checkApkExtension();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode != RC_PERMISSION_REQUEST) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            return;
        }

        if (grantResults.length == 0 && !ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
            // アプリの設定画面を開く
            return;
        }

        checkApkExtension();
    }

    private void checkApkExtension()
    {
        if (getMainObbFile().canRead()) {
            // UnityPlayerActivityを起動する
            return;
        }

        if (PermissionChecker.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE
            }, RC_PERMISSION_REQUEST);
            return;
        }

        // エラーを表示してアプリを終了する
    }

    // (省略)
}

ファイルチェックに失敗した場合は READ_EXTERNAL_STORAGE 権限をチェックし、なければリクエストしてから再度チェックを行います。 このときユーザーが「今後表示しない」をチェックしていると shouldShowRequestPermissionRationalefalse を返すので、一言添えてアプリ設定画面に誘導するとよいでしょう。

最後にUnity側の権限リクエストを無効化する設定をAndroidManifestに追加すれば完了です。

<meta-data android:name="unityplayer.SkipPermissionsDialog" android:value="true" />

これでなんとかAPK拡張ファイルを利用したアプリをリリースする最低限の準備ができました。 あとはお好みでユーザーへの説明や操作指示などを肉付けしましょう。

その他の注意点

バイナリの配布方法

利用しているアプリケーション配信サービスによってはobbファイルを取り扱えないことがあります。ストアの内部テストだけでは大抵不足なので、ビルドの用途に応じてAPK拡張ファイルを利用するかを切り替えられるような仕組みを準備しておくべきでしょう。

StreamingAssetsへのアクセス

UnityでAPK拡張ファイルを利用するとStreamingAssetsはobbファイルの側に格納されます。AndroidにおいてStreamingAssets以下のファイルへのアクセスはどちらにあってもそう変わりません。
しかし、一部のサードパーティライブラリではobbファイル内のStreamingAssetsに正常にアクセスできないものがありました。

StreamingAssets以下にあるアセットが正しく読み込まれているか検証を忘れないようにしましょう。

ビルド番号

Google Play StoreではAPK拡張ファイルのビルド番号はアプリ本体のものと別に扱われるようですが、Unityは必ずアプリのビルド番号を用いてobbファイルを開こうとするようです。 Google Play Storeで新しいリリースを生成するとき、新しいobbファイルをアップロードせずに過去のファイルを選択すると、Unityからアクセスできなくなってしまいます。 

まとめ

UnityでAPK拡張ファイルを扱うのは簡単ですが、その仕組みには落とし穴がいくつもありました。 アプリサイズが逼迫したときはまずより簡単な手順(例えば、アーキテクチャ毎にapkをビルドするなど)を講じて、なお足りない場合に採用を検討すべきでしょう。

その上でどうしても必要になったときは、Unityによるサポートがあるからと軽視せず早めに実装と検証を行うことが大事ですね。

React Native製アプリのフォントサイズをいい感じに設定したい

そとあそび所属の天野(@mutachii) です。

この記事は Akatsuki Advent Calendar 2018 の17日目の記事です。
前回は s-capybaraさんの、Elixir でソースコードジェネレーションする - Qiitaでした。

はじめに

React Nativeでの開発で避けて通れないのは、多種多様なスクリーンで表示崩れを発生させないように実装することです。

幸い、Flexboxのサポートのおかげでレイアウトの実装で困ることは少ないですが、対応が面倒なのが fontSize の指定だと思います。
これらは当然 Flexbox では対応することができないし、widthやheight, paddingなどのようにパーセント指定することもできません。

React Native 開発始めたてのときにあるある(だと思っている)のは、普段は iPhoneX で動作確認している開発者が、
いざリリースするぞとなったタイミングで、iPhone8やiPhoneSEでは文字が大ぎることや、 逆に、iPadで確認すると文字が異様に小さいということに気づき対応に追われるということじゃないでしょうか...

今回は、そのfontSize 問題の解決に有用な

という2つのライブラリを紹介します。

react-native-size-matters

以下のように使うライブラリです。

import { scale, verticalScale, moderateScale } from 'react-native-size-matters';

const Component = props =>
  <View style={{
    width: scale(30), 
    height: verticalScale(50),
    padding: moderateScale(5)
  }}>
    <Text style={{
      fontSize: moderateScale(14)
    }}>すごい</Text>
  </View>

このscaleやverticalScaleがやっていることはすごく単純で、 基準となるスクリーンサイズ(350 x 650)に対する、現在のスクリーンサイズの倍率を算出して、その倍率をかけた数値を返してくれます。 それだけなので、fontSizeはもちろん、widthやheightにも利用できます。

moderateScaleだけはすこし特殊で、

Sometimes you don't want to scale everything in a linear manner, that's where moderate scale comes in.
The cool thing about it is that you can control the resize factor (default is 0.5).
If normal scale will increase your size by +2X, moderateScale will only increase it by +X, for example:
➡️ scale(10) = 20
➡️ moderateScale(10) = 15
➡️ moderateScale(10, 0.1) = 11

というように、増加 / 減少 させるときの係数を指定できます。 スクリーンサイズが2倍になったからといって、すべての要素のサイズを2倍にしたいわけではない...というようなケースで使います。

微妙に問題になるのは、基準となるスクリーンのサイズが 350 x 680 という値なので、例えば デザインデータが iPhoneXで作成されている場合は、デザインどおりに実装できないことです。
とはいえ、上に書いたとおりとてもシンプルな実装なので、自分たちの基準となるスクリーンサイズを使って実装し直すという方法もありそうです。

react-native-responsive-fontsize

名前通りに responsive な fontSize の指定を可能にする RF という関数を提供してくれます。

import RF from "react-native-responsive-fontsize"

const styles = StyleSheet.create({
  welcome: {
    fontSize: RF(3.5),
    textAlign: "center",
    margin: 10
  },
  instructions: {
    textAlign: "center",
    color: "#333333",
    marginBottom: 5,
    fontSize: RF(2.5)
  }
})

こちらの実装もシンプルでデバイスのHeight を基準にフォントサイズを決めます。なので、RFの引数は fontSize ではなくて パーセンテージ であることに注意してください。

さいごに

fontSize問題にすぐ効く2つのライブラリを紹介しました。

基本的には薄い実装のライブラリ達なので、実装の方向性を確認して、自分たちのプロダクトに合わせて改変していくことや、
PRを出してライブラリ自体を改善しやすいのかなと思っています。