GameWith Engineering Blog

GameWith のエンジニア、デザイナーが技術について日々発信していきます。

PHPerKaigi 2018 にスポンサーとして協賛します

GameWith は PHPerKaigi 2018 にプラチナスポンサーとして協賛します。

PHP の大きなカンファレンスといえば、もちろん全国各所で開催される PHP カンファレンスが思い浮かびます。

しかし、今回の PHPerKaigi はチケット販売制いうことで、より質の高いイベントになるのではないかという期待と、PHP のコミュニティ発展に貢献したいという思いから協賛を決定いたしました。

弊社のエンジニアももちろん参加いたしますので、ぜひ会場でお会いしましょう!お気軽にお声がけください!

イベント概要については、以下をご覧ください。

イベント概要

f:id:serimaryo:20180115215750p:plain

イベント名 : PHPerKaigi 2018

概要 : PHPerKaigi(ペチパーカイギ)は、現在PHPを使用している、過去にPHPを使用していた、これからPHPを使いたいと思っているエンジニアが、技術的なノウハウを共有するためのカンファレンス(イベント)です。

日時 : 2018 年 3 月 9 日(木), 10 日(金)

会場 : 練馬区立区民・産業プラザ Coconeriホール

公式 HP : https://phperkaigi.jp/2018/

phperkaigi.jp

InVision謹製、DSMこと「DesignSystemManager」を使ってみて

 GameWith デザイナーの @guchitaka_ です。Web からアプリまで諸々デザインに携わらせていただいております。 inVision が2018年1月提供予定の DSMこと「DesignSystemManager」の EarlyAccess が出来るようになったので早速使ってみました。

www.invisionapp.com

InVision「DesignSystemManager」について

はじめに、そもそもデザインシステムとは

組織におけるデザインの共通言語となるものです。

従来はスタイルガイドやUIキットを作り、それを関係者が参照するような形で組織のデザインを制御していました。しかしながら少しでも更新が滞ったりすると参照する意味がなくなり、死んだガイドラインとなった経験をされた方も多いのではないでしょうか。メンテナンスにかけるコストも馬鹿にならなく、ガイドラインの整備に追われて作る時間がとれず本末転倒になる場合もありました。

デザインシステムはこれを解決するもので、それ自体が各プロダクトにエコシステムを提供するプロダクトとして運用されるというものです。

今回は InVisionDSM について言及するので詳しい説明は割愛します。

このあたりに詳しく書いてあります

InVision DSM で出来ること

  • スタイルやシンボルのバージョン管理
    • Color
    • Text Styles
    • Layer Styles(現在は項目だけ存在)
    • Icons
    • Components(Artboardも出来ます)
    • Fonts
  • Team 管理
  • DesignToken の出力

目次

  • 導入
  • 実践
  • バージョン管理
  • 出力
  • まとめ

導入

Early Access を申請されていた方はメールが届いているかと思いますので指示に従い導入します。 大まかな手順は以下の通りです。

  • 「CraftManger」をインストール(既にインストール済みの場合は一度アンインストール)
  • Organizations 作成
  • Libraries 作成

InVision DSMにアクセスすると以下のような画面が表示されます。

f:id:guchitakas:20171225183053p:plain

今回は適当な Library を作成します。

f:id:guchitakas:20171225183036p:plain

詳しくは以下を参照してください。

support.invisionapp.com

実践

Library の作成と CraftManager の準備が整ったところで実際にDSMを使ってみましょう。

まずは適当なアセットを用意します

Icons と Components は実際の運用を想定して Symbol 化しています。

f:id:guchitakas:20171225185804p:plain

次にDSMのパレットを開いて追加します。

CraftManager のツールバーに六角形のアイコンがあるのでクリックします。 まだ何も追加していないので No Hoge...と表示されるはずです。

f:id:guchitakas:20171225190042p:plain

次に、追加したいアセットを選択して「+」を押します

追加したいアセットを選択して、DSMのパレット右下にある「+」ボタンを押します。とても簡単でありがたいです。 パレットを見ると追加されているのが確認できます。

※ Layer Styles も表示されていますが、EarlyAccess版では未実装のようです。

追加したアセットのSketch 上での見え方

各アセット毎に分けられて表示されています。 階層を切れるので、役割ごとにグルーピングできるのは嬉しいですね。

Colors

f:id:guchitakas:20171225190505p:plain

Text styles

f:id:guchitakas:20171225190518p:plain

Icons

f:id:guchitakas:20171225190531p:plain

Components

f:id:guchitakas:20171225190543p:plain

Web上 での見え方

Colors

RBG/HEX/HSL/HSV が取得できるようです。

f:id:guchitakas:20171225190810p:plain

Text styles

Paragraph の設定は取得できないようです。 可能であれば TextColor も HEX などで見られると嬉しいですね。要望出しておきました。

f:id:guchitakas:20171225190857p:plain

Icons

Icon のダウンロードはここからは出来ません。後述する DesignToken から得られます。

f:id:guchitakas:20171225190907p:plain

Components

f:id:guchitakas:20171225190934p:plain

Fonts

f:id:guchitakas:20171225190945p:plain

アセットを利用する

使い方は簡単、Icon や Component の場合はパレットからドラッグ&ドロップで配置するだけ。 Color や TextStyle の場合は反映したいオブジェクトを選択してパレット内のスタイルをダブルクリックします。

f:id:guchitakas:20171225192306p:plain

アセットを更新する

オブジェクトを選択してパレット内の「+」ボタンを押すとアセットが更新できます。 変更点がダイアログで表示されますので、新規作成か更新を選んでください。 尚、Symbol Override も引き継ぐようですので、更新の際はご注意を。

f:id:guchitakas:20171225192604p:plain

アセットの更新を受け取る

異なるファイルで同じ Icon を DSM からドロップして配置しています。

f:id:guchitakas:20171225194041p:plain

DSM パレットの「↓」アイコンを押すと、ファイル内のアセットに変更内容が反映されます。とても楽ですね。

f:id:guchitakas:20171225194551p:plain f:id:guchitakas:20171225194107p:plain

バージョン管理

まずはバージョンをリリースします

Library 名 をクリックして「Release new version」を選びます。

f:id:guchitakas:20171225194632p:plain

Version 名と Description を求められるので入力して「Create Version」を選びます。 これで作成完了です。

f:id:guchitakas:20171225194644p:plain

バージョンを変更するには

先と同じように Library 名 をクリックして「Switch Version」を選びます。

f:id:guchitakas:20171225194655p:plain

バージョンの更新を共有するには

バージョンの更新だけでは不十分ですよね、それを関係者に共有する必要があります。 Web 版の DSM を開き画面右上の「Share」を押すとURLが出力されるので活用しましょう。

なお「Share」しなくてもDSMを開いた際には、更新された旨が通知される安心設計です。

f:id:guchitakas:20171225195202p:plain

出力

この機能が InVision DSM の真の価値ではないでしょうか、なんと DSM は Design Token の出力に対応しております。 DesignToken が唯一の正しいデザイン(Single source of truth)として機能し、各プロダクトで一貫性のあるデザインを提供できるようになります。

実際に出力する

Web 版の DSM 右上にあるアイコンをクリックします。

f:id:guchitakas:20171225195255p:plain

DesignToken が出力され、取得できるようになります。 対応形式は以下の通りです。

CSS/Sass/Less/Stylus/XML/JSON/YAML/Android/iOS

アイコンなども Resouces として取得できるようですが、余白として設定した透明ピクセルをカットされてしまうので現状はまだ使えません。

f:id:guchitakas:20171225195308p:plain

まとめ

スタイルガイドの作成やデザインのチーム内での共有、Artboardもシンボル化せずに登録できるため、画面単位での共有もできると、非常にありがたみを感じます。 そしてとどめの DesignToken ... SalesForce も採用していますが、これは可能性しか感じません。 ちょうど DesignSystem の構築を考えているところなので、GameWith でも導入を検討したいところです。

参考文献

www.invisionapp.com support.invisionapp.com Designing a Design System // Speaker Deck

CircleCI 2.0 の resource_class で CPU と RAM のリソースを変更してみる

メリークリスマス! @serima です。 いかがお過ごしでしょうか。皆さまのサンタクロースは価値をデリバリしてくれましたでしょうか?

さて、GameWith では CI のツールとして CircleCI を使用しています。 2017 年 7 月頃に CircleCI 2.0 が正式にリリースされ、GameWith でも 2.0 への移行を既に済ませています。(移行作業についてはまた別途記事を書くつもりです。)

移行作業時にドキュメントを読んでいると、resource_class という設定が新たに増えていることに気付きました。

今回は、 resource_class を利用するまでの流れと、参考までに行なったベンチマーク結果をお届けします。

resource_class とは?

下記、抜粋しましたが、詳細はこちらのドキュメントをご覧ください。

It is possible to configure CPU and RAM resources for each job as described in the following table. Note: Paid accounts must request to use this feature by opening a support ticket (or by contacting their Customer Success Manager when applicable) and non-paid users must request to use this feature by opening a ticket at https://support.circleci.com/hc/en-us/requests/new. If resource_class is not specified or an invalid class is specified, the default resource_class: medium will be used. The resource_class key is currently only available for use with the docker executor.

f:id:serimaryo:20171221132434p:plain

(現時点では)docker executor のみで使用できるリソースクラスを変更するための key で、カスタマーサクセスマネージャーにサポートチケットを切る形でリクエストしないと使用できないとのこと。 おそらくユーザ、オーガニゼーション単位でホワイトリストが存在し、そこに追加してもらうことで機能が利用できるようになるのだと思います。 また、デフォルトでは medium class として動作しているようです。

利用するための手続き

GameWith は Paid account なので、使ってみたい旨を簡単に記載して support@circleci.com にメールを送ってみました。

Hi.

Our Organization GameWith uses a CircleCI paid plan.
I found about resource_class in the document, can you add our team to the whitelist?
I hope to improve the build execution further.

Thanks.

しばらくすると、返信が来ました。

Hi Ryo,

I've enabled configurable resources for your org `GameWith'. Keep in mind this is a premium feature of CircleCI 2.0, while we aren't charging for this feature now, we do plan on doing so in the near term.

Best,

-Jia

これで、機能が利用できるようになりました。

しかし、

Keep in mind this is a premium feature of CircleCI 2.0, while we aren't charging for this feature now, we do plan on doing so in the near term.

利用される方は注意が必要です。 この機能は、CircleCI 2.0 のプレミアム機能で、近い将来課金する予定があるとのこと。

xlarge class を利用し続けていたら、気付いたら課金されていた。など、そういったことが発生するかもしれません。(無論、事前アナウンスはあると思いますが…)

また、ハイスペックなリソースを前提とした CI 周りを構築してしまうのもリスクではあると思いますので、各自で塩梅を確かめながらご利用いただけると良いのかなと思います。

実際に試してみる

.circleci/config.yml に下記一行を追加するだけです。

resource_class: large

今回は UnixBench を使用してお手軽にベンチマークをとってみたいと思います。 UnixBench のベンチマーク項目については、こちらが詳しいです。

blog.idcf.jp

今回はあくまでも各 resource_class での比較を行いたかったため、実際のスコアがいくつになったというよりもデフォルトの resource_class である medium よりもどの程度良いかの比較に意味があると考えています。

UnixBench のインストールと実行

普段 CI に使用している Docker image は Ubuntu 14.04 をベースにしたものをカスタムしており、その過程で apt-get install build-essential を行っているので、特に事前にインストールしておく必要があるパッケージやライブラリはありませんでした。(本来であれば、その手順が必要のはずです。)

$ git clone https://github.com/kdlucas/byte-unixbench.git
$ cd byte-unixbench/UnixBench
$ ./Run

ベンチマークの結果

CPU と RAM の増加によって、パフォーマンスの向上は見られますが、想定していたよりも比較的緩やかな傾きとなった印象です。 ベンチマーク自体は一度しか実行していないため、多少の誤差はあるかもしれませんが、グラフとしては綺麗に右肩上がりにはなりました。

f:id:serimaryo:20171225171950p:plain

なお、結果のログは以下 gist においておきましたのでご参考までにどうぞ。 (ホストマシンのコアの情報を後半コピーし忘れてしまいましたが、すべて同じ Intel(R) Xeon(R) CPU E5-2680 v2 @ 2.80GHz の 32 core でした)

[CircleCI 2.0] resource_class ごとの UnixBench の結果 · GitHub

まとめ

上記の比較結果は、どの resource_class を利用するかで料金が変わるなどの場合に、コストパフォーマンス比較の参考としてご利用頂ければと思います。

GameWith では CI に限らず人の手が不要な所は、積極的に自動化・省力化を行なっています。

あらゆるプロセスにおいて、改善できる余地はないか考えつづけ、引き続き発信を行っていきます!

try! Swift Tokyo 2018 にスポンサーとして協賛します

GameWith は try! Swift Tokyo 2018 にゴールドスポンサーとして協賛します。

弊社の iOS アプリ開発は 2015 年のモンスト攻略アプリから始まりました。

直近では、ゲーム攻略情報や、同じゲームをプレイしているユーザー様同士でコミュニケーションを取るための SNS を提供する iOS アプリを 2017 年 11 月 にリリースしています。

またこの 2 年間で Swift はというと、最初のアプリ開発で使用しておりました Swift 2 から、現在は Swift 4 までバージョンアップされており、言語自体も年々洗練されてきていることを感じております。

今回は、このような素晴らしいプログラミング言語 Swift のコミュニティ「try! Swift」をさらに発展させていくお手伝いをしたいという思いから、イベントへのスポンサードを決定しました。

ゴールドスポンサーということで、当日は弊社もブース出展を行います。

今までは、あまりこういった機会がなかったため、積極的に皆様と関われると良いなと思っています。(そして、これからはこういった機会を増やしていきます!)

try! Swift に参加される方はぜひお気軽にお立ち寄りください。

イベント概要については、以下をご覧ください。

イベント概要

f:id:serimaryo:20171215145834p:plain

イベント名 : try! Swift Tokyo 2018

概要 : try! Swift は、 Swiftにおけるベストプラクティス、アプリ開発、サーバーサイドSwift、オープンソースSwift、そしてSwiftのコミュニティなど、プログラミング言語Swiftに関するコミュニティ主催のカンファレンスです。

日時 : 2018 年 3 月 1 日(木), 2 日(金)

会場 : ベルサール新宿グランド

公式 HP : https://www.tryswift.co/events/2018/tokyo/jp/

www.tryswift.co

通知ポップアップの中身をいい感じにスクロールできるようにする

GameWith のエンジニアの @terunuma です。gamewith.jp のフロントエンドに関わる部分を主に触っています。

gamewith.jp ではゲームアプリの攻略/紹介記事を掲載している他に、会員向けのコンテンツとしてアプリのリリース通知や Q&A や他のゲームユーザーと交流できる SNS/コミュニティ機能なども提供しています。

これらの会員向けのコンテンツではアプリリリースの通知や Q&A に質問/回答がついた、SNS でいいねされた時にユーザーに対して通知が飛ぶようになっており、ログイン中であれば通知ボタンにバッジが表示されます。通知ボタンをタップで内容も表示できるようになっています。

f:id:syque:20171213194748p:plain:w300

この通知ポップアップの中にユーザーに届いた通知一覧を表示しているのですが、ポップアップの表示範囲が狭いためスクロールができるようにしています。ですがモバイルだとスクロールの動きがカクカクしており触り心地がよくないので、CSS で -webkit-overflow-scrolling: touch; のプロパティを設定してスクロールがスムーズになるようにしました。

f:id:syque:20171213194531p:plain:w300

これで完成、となればよかったのですが実際に触ってみると通知をスクロールした後に画面が固まってしまうことがありました。原因を探ってみると、どうも通知一覧の上端か下端でスクロールしようとした時に body の方をスクロールしようとしていて、目には見えないけどバウンススクロール状態になっていてそれが終わるまでタッチ処理を受け付けない状態になっていたことが分かりました。

これを解決するために div の端でスクロールしても body をスクロールしないようにできないか、と思い調べてみたところ stackoverflow に Javascript を用いて div のスクロール位置を調整することで解決する方法が紹介されていました。

stackoverflow.com

仕組みとしてはこうです:

  • 対象の、スクロール可能な要素に touchstart のイベントをバインドする
  • そのイベント処理で、現在の div のスクロール位置が上端か下端かをチェックする
  • 上端であれば +1、下端であれば -1、スクロール位置を動かす
  • この状態で端まで引っ張ると、div 内部の要素で必ずバウンススクロールするので body がスクロールされない

実装したスクリプトは以下になります:

scrollableElement.on('touchstart', function() {
    // 上端なら +1 する
    if (scrollableElement.scrollTop() === 0) {
        scrollableElement.scrollTop(1);
    }

    // 下端なら +1 する
    // 要素の高さとスクロール位置の差から、現在位置が下端かどうかを計算する
    var currentScrollTopPosition = scrollableElement.scrollTop();
    var maxScrollTopPosition = scrollableElement[0].scrollHeight - scrollableElement[0].offsetHeight;

    if (currentScrollTopPosition >= maxScrollTopPosition) {
        scrollableElement.scrollTop(currentScrollTopPosition - 1);
    }
});

f:id:syque:20171213203628p:plain:w300

要素が +1/-1 スクロールされる動きは、通知ポップアップを注意深く見ながらタッチすれば見ることができますので興味のある方はぜひ観察してみてください。 (そしてニヤッとしてください 😎 )

これにより、通知ポップアップをスクロールしても画面が固まるという問題を解決することができました。 実際のところ、こういうハックをしなくても自然な実装で作れる(ex. CSS のみで完結できる)のが一番良いのですが、ユーザー体験が良くなる手段になるのであれば今後もコード的に無理のない範囲で取り入れていければと思います。

DroidKaigi 2018 にスポンサーとして協賛します

f:id:serimaryo:20171206151812p:plain

GameWith は DroidKaigi 2018 への「サポーターズ」スポンサーとして協賛を行います。

GameWith はウェブサイトだけでなく、より良いユーザ体験を実現するためにネイティブアプリの開発も行っています。

今回は、Android の技術コミュニティの発展のお手伝いを始めていきたいという思いから、スポンサーとして協賛させていただきました。

当日は弊社のエンジニアもイベントに参加する予定ですので、もし見かけましたらお気軽にお声がけください!

イベント概要については、以下をご覧ください。

イベント概要

イベント名 : DroidKaigi 2018

概要 : DroidKaigiはエンジニアが主役のAndroidカンファレンスです。 Android技術情報の共有とコミュニケーションを目的に、2018年2月8日(木)、9日(金)の2日間開催します。 今回は「ニッチな技術とコミュニケーション」を重視する予定です。

日時 : 2018 年 2 月 8 日(木), 9 日(金)

会場 : ベルサール新宿グランド コンファレンスセンター

主催 : DroidKaigi実行委員会

公式 HP : https://droidkaigi.jp/2018/

droidkaigi.jp

Spot Instance の価格を CloudWatch で記録する

はじめまして。GameWith のエンジニアの @serima です。 普段は GameWith というゲームメディアの機能開発やインフラに目を向けるお仕事をしています。

GameWith では EC2 インスタンスのスケールアウトを Auto Scaling を使用せずに独自のスクリプトでインスタンス数をコントロールしています。*1

それなりの数のインスタンス数を起動しようとしたときに、その Availability Zone におけるそのインスタンスタイプが枯渇しているという状態が一時的に存在し、目的のインスタンスが起動できなかった事がありました。*2

具体的には、以下のようなエラーメッセージが返されました。

InsufficientInstanceCapacity: We currently do not have sufficient c4.xlarge capacity in the Availability Zone you requested (ap-northeast-1a). Our system will be working on provisioning additional capacity. You can currently get c4.xlarge capacity by not specifying an Availability Zone in your request or choosing ap-northeast-1c. status code: 500, request id:

高負荷が予測されるタイミングでインスタンスを想定通り起動できていない状態になってしまうと、サービス運営に影響があるため、回避したい問題となります。

その Availability Zone における残りのインスタンス数などを取得する API などは用意されていないため、いかにその状態を予見するかという解決策を探りました。 何か手がかりになるものはないかとメトリクスを見ていると、枯渇していたタイミングの前には Spot Instance の価格が高騰しているという状態が観測できました。

EC2 のコンソールから、スポットリクエスト -> 価格設定履歴から以下のようなグラフを参照することができます。 f:id:serimaryo:20171124183654p:plain

これを監視し、高騰したタイミングでアラート通知できれば、インスタンス起動のタイミングを早くするなど別のオプションを選ぶことができるようになります。 GameWith では CloudWatch のアラームを PagerDuty に飛ばすような運用をしていますので、そのフローにのせられるとスムースです。

しかし、SpotInstance の価格はそのままでは CloudWatch のメトリクスとして設定できません。 そこで、AWS Lambda を利用して、CloudWatch にカスタムメトリクスとして 1 分おきに値を送信するようにしました。

具体的なスクリプトは以下です。(なお、ランタイムは Python 2.7 を使用しています。)

import boto3
import time
import os
from datetime import datetime, timedelta

region = os.getenv('AWS_REGION', 'ap-northeast-1')

ec2 = boto3.client('ec2', region_name=region)
cloudwatch = boto3.client('cloudwatch', region_name=region)

aws_availability_zone = 'ap-northeast-1a'

def put_cloudwatch(namespace, metric_name, instance_type, timestamp, value):
    metric_data = {
        'MetricName': metric_name,
        'Timestamp': timestamp,
        'Value': value,
        'Unit': 'None',
        'Dimensions': [
            {
                'Name': 'InstanceType',
                'Value': instance_type
            }
        ]
    }
    r = cloudwatch.put_metric_data(
        Namespace = namespace,
        MetricData = [metric_data]
    )
    return r

def lambda_handler(event, context):
    ec2_instance_types = [
        'c4.large',
        'c4.xlarge'
    ]

    metric_name = 'SpotInstancePrice'

    filters = [
        {'Name': 'availability-zone', 'Values': [aws_availability_zone]},
        {'Name': 'instance-type', 'Values': ec2_instance_types},
        {'Name': 'product-description', 'Values': ['Linux/UNIX']},
    ]

    now = datetime.now()
    one_min_ago = now - timedelta(minutes=1)

    spot_price_history = ec2.describe_spot_price_history(Filters=filters, StartTime=one_min_ago).get('SpotPriceHistory')

    for history in spot_price_history:
        instance_type = history.get('InstanceType')
        spot_price = float(history.get('SpotPrice'))
        put_cloudwatch('EC2', metric_name, instance_type, now, spot_price)

    return str(spot_price_history)

Lambda は、現在では定期実行できるようになっており、指定した時間間隔ごとにイベントを発火させることができます。 少し分かりづらいのですが、定期実行を設定する場合は、Lambda のコンソールではなく、CloudWatch のコンソールに移動し、サイドバーの「ルール」から設定することになります。

ちなみに、このスクリプトを処理するロールを新規で作成し、以下のようなポリシーをアタッチしました。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:PutMetricData"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeSpotPriceHistory"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

カスタムメトリクスとして送信された値を元に、無事に以下のようなグラフを作成することができました。 今回は、On-Demand Price を一定時間超えていたらアラーム通知を行うように設定しました。

f:id:serimaryo:20171124180842p:plain

このような小ネタも引き続き、こちらのブログで公開していきたいと思います。

参考

*1:語ると長くなってしまうので、こちらについては後日別記事で書きたいと思います

*2:レイテンシの関係上、Single AZ で運用しています